Skip to content

Commit

Permalink
support for custom curves
Browse files Browse the repository at this point in the history
  • Loading branch information
Tyler committed Nov 13, 2019
1 parent 3b593af commit 7003f1a
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 42 deletions.
27 changes: 14 additions & 13 deletions bip32.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"crypto/sha512"
"encoding/hex"
"errors"
"github.com/Moonlight-io/asteroid-core/models/primatives"
)

const (
Expand All @@ -31,9 +32,9 @@ type Key struct {
}

// Creates a new master extended key from a seed
func NewMasterKey(seed []byte) (*Key, error) {
func NewMasterKey(curve primatives.EllipticCurve, masterSeed []byte, seed []byte) (*Key, error) {
// Generate key and chaincode
hmac := hmac.New(sha512.New, []byte("Bitcoin seed"))
hmac := hmac.New(sha512.New, masterSeed)
hmac.Write([]byte(seed))
intermediary := hmac.Sum(nil)

Expand All @@ -42,7 +43,7 @@ func NewMasterKey(seed []byte) (*Key, error) {
chainCode := intermediary[32:]

// Validate key
err := validatePrivateKey(keyBytes)
err := validatePrivateKey(curve, keyBytes)
if err != nil {
return nil, err
}
Expand All @@ -62,7 +63,7 @@ func NewMasterKey(seed []byte) (*Key, error) {
}

// Derives a child key from a given parent as outlined by bip32
func (key *Key) NewChildKey(childIdx uint32) (*Key, error) {
func (key *Key) NewChildKey(curve primatives.EllipticCurve, childIdx uint32) (*Key, error) {
hardenedChild := childIdx >= FirstHardenedChild
childIndexBytes := uint32Bytes(childIdx)

Expand All @@ -78,7 +79,7 @@ func (key *Key) NewChildKey(childIdx uint32) (*Key, error) {
if hardenedChild {
data = append([]byte{0x0}, key.Key...)
} else {
data = publicKeyForPrivateKey(key.Key)
data = publicKeyForPrivateKey(curve, key.Key) //here
}
data = append(data, childIndexBytes...)

Expand All @@ -97,38 +98,38 @@ func (key *Key) NewChildKey(childIdx uint32) (*Key, error) {
// Bip32 CKDpriv
if key.IsPrivate {
childKey.Version = PrivateWalletVersion
childKey.FingerPrint = hash160(publicKeyForPrivateKey(key.Key))[:4]
childKey.Key = addPrivateKeys(intermediary[:32], key.Key)
childKey.FingerPrint = hash160(publicKeyForPrivateKey(curve, key.Key))[:4]
childKey.Key = addPrivateKeys(curve, intermediary[:32], key.Key)

// Validate key
err := validatePrivateKey(childKey.Key)
err := validatePrivateKey(curve, childKey.Key)
if err != nil {
return nil, err
}
// Bip32 CKDpub
} else {
keyBytes := publicKeyForPrivateKey(intermediary[:32])
keyBytes := publicKeyForPrivateKey(curve, intermediary[:32])

// Validate key
err := validateChildPublicKey(keyBytes)
err := validateChildPublicKey(curve, keyBytes)
if err != nil {
return nil, err
}

childKey.Version = PublicWalletVersion
childKey.FingerPrint = hash160(key.Key)[:4]
childKey.Key = addPublicKeys(keyBytes, key.Key)
childKey.Key = addPublicKeys(curve, keyBytes, key.Key)
}

return childKey, nil
}

// Create public version of key or return a copy; 'Neuter' function from the bip32 spec
func (key *Key) PublicKey() *Key {
func (key *Key) PublicKey(curve primatives.EllipticCurve) *Key {
keyBytes := key.Key

if key.IsPrivate {
keyBytes = publicKeyForPrivateKey(keyBytes)
keyBytes = publicKeyForPrivateKey(curve, keyBytes)
}

return &Key{
Expand Down
57 changes: 28 additions & 29 deletions utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,13 @@ import (
"errors"
"fmt"
"github.com/FactomProject/basen"
"github.com/FactomProject/btcutilecc"
"golang.org/x/crypto/ripemd160"
"io"
"math/big"
"github.com/Moonlight-io/asteroid-core/models/primatives"
)

var (
curve = btcutil.Secp256k1()
curveParams = curve.Params()
BitcoinBase58Encoding = basen.NewEncoding("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz")
)

Expand Down Expand Up @@ -61,24 +59,26 @@ func base58Encode(data []byte) []byte {
}

// Keys
func publicKeyForPrivateKey(key []byte) []byte {
return compressPublicKey(curve.ScalarBaseMult([]byte(key)))
func publicKeyForPrivateKey(curve primatives.EllipticCurve, key []byte) []byte {
var keyBigInt big.Int
keyBigInt.SetBytes(key)
return compressPublicKey(curve.ScalarBaseMult(&keyBigInt))
}

func addPublicKeys(key1 []byte, key2 []byte) []byte {
x1, y1 := expandPublicKey(key1)
x2, y2 := expandPublicKey(key2)
return compressPublicKey(curve.Add(x1, y1, x2, y2))
func addPublicKeys(curve primatives.EllipticCurve, key1 []byte, key2 []byte) []byte {
p1 := expandPublicKey(curve, key1)
p2 := expandPublicKey(curve, key2)
return compressPublicKey(curve.Add(p1, p2))
}

func addPrivateKeys(key1 []byte, key2 []byte) []byte {
func addPrivateKeys(curve primatives.EllipticCurve, key1 []byte, key2 []byte) []byte {
var key1Int big.Int
var key2Int big.Int
key1Int.SetBytes(key1)
key2Int.SetBytes(key2)

key1Int.Add(&key1Int, &key2Int)
key1Int.Mod(&key1Int, curve.Params().N)
key1Int.Mod(&key1Int, curve.N)

b := key1Int.Bytes()
if len(b) < 32 {
Expand All @@ -88,14 +88,14 @@ func addPrivateKeys(key1 []byte, key2 []byte) []byte {
return b
}

func compressPublicKey(x *big.Int, y *big.Int) []byte {
func compressPublicKey(p primatives.Point) []byte {
var key bytes.Buffer

// Write header; 0x2 for even y value; 0x3 for odd
key.WriteByte(byte(0x2) + byte(y.Bit(0)))
key.WriteByte(byte(0x2) + byte(p.Y.Bit(0)))

// Write X coord; Pad the key so x is aligned with the LSB. Pad size is key length - header size (1) - xBytes size
xBytes := x.Bytes()
xBytes := p.X.Bytes()
for i := 0; i < (PublicKeyCompressedLength - 1 - len(xBytes)); i++ {
key.WriteByte(0x0)
}
Expand All @@ -105,45 +105,44 @@ func compressPublicKey(x *big.Int, y *big.Int) []byte {
}

// As described at https://bitcointa.lk/threads/compressed-keys-y-from-x.95735/
func expandPublicKey(key []byte) (*big.Int, *big.Int) {
Y := big.NewInt(0)
X := big.NewInt(0)
func expandPublicKey(curve primatives.EllipticCurve, key []byte) (primatives.Point) {
var point primatives.Point
qPlus1Div4 := big.NewInt(0)
X.SetBytes(key[1:])
point.X.SetBytes(key[1:])

// y^2 = x^3 + ax^2 + b
// a = 0
// => y^2 = x^3 + b
ySquared := X.Exp(X, big.NewInt(3), nil)
ySquared.Add(ySquared, curveParams.B)
ySquared := point.X.Exp(point.X, big.NewInt(3), nil)
ySquared.Add(ySquared, curve.B)

qPlus1Div4.Add(curveParams.P, big.NewInt(1))
qPlus1Div4.Add(curve.P, big.NewInt(1))
qPlus1Div4.Div(qPlus1Div4, big.NewInt(4))

// sqrt(n) = n^((q+1)/4) if q = 3 mod 4
Y.Exp(ySquared, qPlus1Div4, curveParams.P)
point.Y.Exp(ySquared, qPlus1Div4, curve.P)

if uint32(key[0])%2 == 0 {
Y.Sub(curveParams.P, Y)
point.Y.Sub(curve.P, point.Y)
}

return X, Y
return point
}

func validatePrivateKey(key []byte) error {
func validatePrivateKey(curve primatives.EllipticCurve, key []byte) error {
if fmt.Sprintf("%x", key) == "0000000000000000000000000000000000000000000000000000000000000000" || //if the key is zero
bytes.Compare(key, curveParams.N.Bytes()) >= 0 || //or is outside of the curve
bytes.Compare(key, curve.N.Bytes()) >= 0 || //or is outside of the curve
len(key) != 32 { //or is too short
return errors.New("Invalid seed")
}

return nil
}

func validateChildPublicKey(key []byte) error {
x, y := expandPublicKey(key)
func validateChildPublicKey(curve primatives.EllipticCurve, key []byte) error {
point := expandPublicKey(curve, key)

if x.Sign() == 0 || y.Sign() == 0 {
if point.X.Sign() == 0 || point.Y.Sign() == 0 {
return errors.New("Invalid public key")
}

Expand Down

0 comments on commit 7003f1a

Please sign in to comment.