From 7d45f939f79367f44937dc451ac3cdcce0534095 Mon Sep 17 00:00:00 2001 From: Miguel Mota Date: Thu, 5 Jul 2018 01:17:21 -0700 Subject: [PATCH] add basic test --- Gopkg.lock | 20 +- hdwallet.go | 16 + hdwallet_test.go | 69 +++-- .../FactomProject/basen/.travis.yml | 1 + vendor/github.com/FactomProject/basen/LICENSE | 19 ++ vendor/github.com/FactomProject/basen/README | 3 + .../github.com/FactomProject/basen/basen.go | 119 ++++++++ .../FactomProject/btcutilecc/.gitignore | 25 ++ .../FactomProject/btcutilecc/README.md | 8 + .../FactomProject/btcutilecc/arith.go | 32 ++ .../FactomProject/btcutilecc/blind.go | 30 ++ .../btcutilecc/blind_requester.go | 72 +++++ .../FactomProject/btcutilecc/blind_signer.go | 54 ++++ .../FactomProject/btcutilecc/ecdh.go | 10 + .../FactomProject/btcutilecc/keys.go | 13 + .../FactomProject/btcutilecc/random.go | 26 ++ .../FactomProject/btcutilecc/secp256k1.go | 238 +++++++++++++++ .../tyler-smith/go-bip32/.travis.yml | 11 + .../tyler-smith/go-bip32/CONTRIBUTING.md | 1 + .../tyler-smith/go-bip32/Gopkg.lock | 37 +++ .../tyler-smith/go-bip32/Gopkg.toml | 68 +++++ .../github.com/tyler-smith/go-bip32/LICENSE | 21 ++ .../github.com/tyler-smith/go-bip32/README.md | 66 ++++ .../github.com/tyler-smith/go-bip32/bip32.go | 286 ++++++++++++++++++ .../github.com/tyler-smith/go-bip32/utils.go | 199 ++++++++++++ 25 files changed, 1412 insertions(+), 32 deletions(-) create mode 100644 vendor/github.com/FactomProject/basen/.travis.yml create mode 100644 vendor/github.com/FactomProject/basen/LICENSE create mode 100644 vendor/github.com/FactomProject/basen/README create mode 100644 vendor/github.com/FactomProject/basen/basen.go create mode 100644 vendor/github.com/FactomProject/btcutilecc/.gitignore create mode 100644 vendor/github.com/FactomProject/btcutilecc/README.md create mode 100644 vendor/github.com/FactomProject/btcutilecc/arith.go create mode 100644 vendor/github.com/FactomProject/btcutilecc/blind.go create mode 100644 vendor/github.com/FactomProject/btcutilecc/blind_requester.go create mode 100644 vendor/github.com/FactomProject/btcutilecc/blind_signer.go create mode 100644 vendor/github.com/FactomProject/btcutilecc/ecdh.go create mode 100644 vendor/github.com/FactomProject/btcutilecc/keys.go create mode 100644 vendor/github.com/FactomProject/btcutilecc/random.go create mode 100644 vendor/github.com/FactomProject/btcutilecc/secp256k1.go create mode 100644 vendor/github.com/tyler-smith/go-bip32/.travis.yml create mode 100644 vendor/github.com/tyler-smith/go-bip32/CONTRIBUTING.md create mode 100644 vendor/github.com/tyler-smith/go-bip32/Gopkg.lock create mode 100644 vendor/github.com/tyler-smith/go-bip32/Gopkg.toml create mode 100644 vendor/github.com/tyler-smith/go-bip32/LICENSE create mode 100644 vendor/github.com/tyler-smith/go-bip32/README.md create mode 100644 vendor/github.com/tyler-smith/go-bip32/bip32.go create mode 100644 vendor/github.com/tyler-smith/go-bip32/utils.go diff --git a/Gopkg.lock b/Gopkg.lock index 0b55b65..f6c52ed 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1,6 +1,18 @@ # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. +[[projects]] + branch = "master" + name = "github.com/FactomProject/basen" + packages = ["."] + revision = "fe3947df716ebfda9847eb1b9a48f9592e06478c" + +[[projects]] + branch = "master" + name = "github.com/FactomProject/btcutilecc" + packages = ["."] + revision = "d3a63a5752ecf3fbc06bd97365da752111c263df" + [[projects]] branch = "master" name = "github.com/btcsuite/btcd" @@ -37,6 +49,12 @@ revision = "dea1ce052a10cd7d401a5c04f83f371a06fe293c" version = "v1.8.11" +[[projects]] + branch = "master" + name = "github.com/tyler-smith/go-bip32" + packages = ["."] + revision = "2c9cfd17756470a0b7c3e4b7954bae7d11035504" + [[projects]] branch = "master" name = "github.com/tyler-smith/go-bip39" @@ -55,6 +73,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "1835004d90a11df297dcdec95954701dd5ac5a5fcb0a171f392b8ff4bc533eb1" + inputs-digest = "e4d09c833c4e21257cbf69438114f2c6b1a7d3f61f1f38bd2a12b580a35379f6" solver-name = "gps-cdcl" solver-version = 1 diff --git a/hdwallet.go b/hdwallet.go index 807465d..0f8be04 100644 --- a/hdwallet.go +++ b/hdwallet.go @@ -12,6 +12,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/crypto" + "github.com/tyler-smith/go-bip32" "github.com/tyler-smith/go-bip39" ) @@ -101,12 +102,22 @@ func (s Wallet) Derive(index interface{}) (*Wallet, error) { idx = uint32(v) case int16: idx = uint32(v) + case int32: + idx = uint32(v) + case int64: + idx = uint32(v) case uint: idx = uint32(v) case uint8: idx = uint32(v) case uint16: idx = uint32(v) + case uint32: + idx = v + case uint64: + idx = uint32(v) + default: + return nil, errors.New("unsupported index type") } address, err := s.extendedKey.Child(idx) @@ -209,3 +220,8 @@ func NewMnemonic() (string, error) { } return bip39.NewMnemonic(entropy) } + +// NewSeed ... +func NewSeed() ([]byte, error) { + return bip32.NewSeed() +} diff --git a/hdwallet_test.go b/hdwallet_test.go index 0b0f540..ee0dff7 100644 --- a/hdwallet_test.go +++ b/hdwallet_test.go @@ -1,48 +1,55 @@ package hdwallet import ( - "fmt" - "log" "testing" ) -// TODO: tests +// TODO: table test func TestNew(t *testing.T) { mnemonic := "tag volcano eight thank tide danger coast health above argue embrace heavy" - /* - seed, err := bip32.NewSeed() - if err != nil { - log.Fatalln("Error generating seed:", err) - } - */ - //seed := bip39.NewSeed(mnemonic, "") - - wallet, err := New(Config{ + root, err := New(Config{ Mnemonic: mnemonic, - Path: "", + Path: "m/44'/60'/0'/0", }) if err != nil { - log.Fatal(err) + t.Error(err) } - fmt.Println(wallet.PrivateKeyHex()) - fmt.Println("") - fmt.Println(wallet.PublicKeyHex()) - fmt.Println("") - fmt.Println(wallet.AddressHex()) - fmt.Println("") - fmt.Println(wallet.Path()) + if root.PrivateKeyHex() != "7657783b9ba4d4b16062337235432bbc5c80e3dce39fdc91e62d744fdb665cad" { + t.Error("wrong private key") + } + + if root.PublicKeyHex() != "177c0776ca4c9e160822a1006eb6d236039eb882da8d7687ba20049d73e6230cae699eb8037aeeee2098d433d4210401a0cc1bf635c3fee2a40933d22c1206e7" { + t.Error("wrong public key") + } + + if root.AddressHex() != "0xAF1c991f6068Ac832eC60A8557eF1C7D8B9BcCD6" { + t.Error("wrong address") + } + + if root.Path() != `m/44'/60'/0'/0` { + t.Error("wrong hdpath") + } - wal, err := wallet.Derive(0) + wallet, err := root.Derive(0) if err != nil { - log.Fatal(err) - } - fmt.Println(wal.PrivateKeyHex()) - fmt.Println("") - fmt.Println(wal.PublicKeyHex()) - fmt.Println("") - fmt.Println(wal.AddressHex()) - fmt.Println("") - fmt.Println(wal.Path()) + t.Error(err) + } + + if wallet.PrivateKeyHex() != "63e21d10fd50155dbba0e7d3f7431a400b84b4c2ac1ee38872f82448fe3ecfb9" { + t.Error("wrong private key") + } + + if wallet.PublicKeyHex() != "6005c86a6718f66221713a77073c41291cc3abbfcd03aa4955e9b2b50dbf7f9b6672dad0d46ade61e382f79888a73ea7899d9419becf1d6c9ec2087c1188fa18" { + t.Error("wrong public key") + } + + if wallet.AddressHex() != "0xC49926C4124cEe1cbA0Ea94Ea31a6c12318df947" { + t.Error("wrong address") + } + + if wallet.Path() != `m/44'/60'/0'/0/0` { + t.Error("wrong hdpath") + } } diff --git a/vendor/github.com/FactomProject/basen/.travis.yml b/vendor/github.com/FactomProject/basen/.travis.yml new file mode 100644 index 0000000..4f2ee4d --- /dev/null +++ b/vendor/github.com/FactomProject/basen/.travis.yml @@ -0,0 +1 @@ +language: go diff --git a/vendor/github.com/FactomProject/basen/LICENSE b/vendor/github.com/FactomProject/basen/LICENSE new file mode 100644 index 0000000..edcf518 --- /dev/null +++ b/vendor/github.com/FactomProject/basen/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2014 Casey Marshall + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/vendor/github.com/FactomProject/basen/README b/vendor/github.com/FactomProject/basen/README new file mode 100644 index 0000000..46bc70f --- /dev/null +++ b/vendor/github.com/FactomProject/basen/README @@ -0,0 +1,3 @@ +basen (base-N) is a simple Go encoding package for representing bytes as big integers in arbitrary base-N encoding. + +See https://godoc.org/github.com/cmars/basen for package documentation. diff --git a/vendor/github.com/FactomProject/basen/basen.go b/vendor/github.com/FactomProject/basen/basen.go new file mode 100644 index 0000000..9fa2212 --- /dev/null +++ b/vendor/github.com/FactomProject/basen/basen.go @@ -0,0 +1,119 @@ +// Copyright (c) 2014 Casey Marshall. See LICENSE file for details. + +package basen + +import ( + "crypto/rand" + "fmt" + "math/big" + "unicode/utf8" +) + +var zero = big.NewInt(int64(0)) + +// Encoding represents a given base-N encoding. +type Encoding struct { + alphabet string + index map[byte]*big.Int + base *big.Int +} + +const base62Alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + +// Base62 represents bytes as a base-62 number [0-9A-Za-z]. +var Base62 = NewEncoding(base62Alphabet) + +const base58Alphabet = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ" + +// Base58 represents bytes as a base-58 number [1-9A-GHJ-LM-Za-z]. +var Base58 = NewEncoding(base58Alphabet) + +// NewEncoding creates a new base-N representation from the given alphabet. +// Panics if the alphabet is not unique. Only ASCII characters are supported. +func NewEncoding(alphabet string) *Encoding { + return &Encoding{ + alphabet: alphabet, + index: newAlphabetMap(alphabet), + base: big.NewInt(int64(len(alphabet))), + } +} + +func newAlphabetMap(s string) map[byte]*big.Int { + if utf8.RuneCountInString(s) != len(s) { + panic("multi-byte characters not supported") + } + result := make(map[byte]*big.Int) + for i := range s { + result[s[i]] = big.NewInt(int64(i)) + } + if len(result) != len(s) { + panic("alphabet contains non-unique characters") + } + return result +} + +// Random returns the base-encoded representation of n random bytes. +func (enc *Encoding) Random(n int) (string, error) { + buf := make([]byte, n) + _, err := rand.Reader.Read(buf) + if err != nil { + return "", err + } + return enc.EncodeToString(buf), nil +} + +// MustRandom returns the base-encoded representation of n random bytes, +// panicking in the unlikely event of a read error from the random source. +func (enc *Encoding) MustRandom(n int) string { + s, err := enc.Random(n) + if err != nil { + panic(err) + } + return s +} + +// Base returns the number base of the encoding. +func (enc *Encoding) Base() int { + return len(enc.alphabet) +} + +// EncodeToString returns the base-encoded string representation +// of the given bytes. +func (enc *Encoding) EncodeToString(b []byte) string { + n := new(big.Int) + r := new(big.Int) + n.SetBytes(b) + var result []byte + for n.Cmp(zero) > 0 { + n, r = n.DivMod(n, enc.base, r) + result = append([]byte{enc.alphabet[r.Int64()]}, result...) + } + return string(result) +} + +// DecodeString returns the bytes for the given base-encoded string. +func (enc *Encoding) DecodeString(s string) ([]byte, error) { + result := new(big.Int) + for i := range s { + n, ok := enc.index[s[i]] + if !ok { + return nil, fmt.Errorf("invalid character %q at index %d", s[i], i) + } + result = result.Add(result.Mul(result, enc.base), n) + } + return result.Bytes(), nil +} + +// DecodeStringN returns N bytes for the given base-encoded string. +// Use this method to ensure the value is left-padded with zeroes. +func (enc *Encoding) DecodeStringN(s string, n int) ([]byte, error) { + value, err := enc.DecodeString(s) + if err != nil { + return nil, err + } + if len(value) > n { + return nil, fmt.Errorf("value is too large") + } + pad := make([]byte, n-len(value)) + return append(pad, value...), nil +} diff --git a/vendor/github.com/FactomProject/btcutilecc/.gitignore b/vendor/github.com/FactomProject/btcutilecc/.gitignore new file mode 100644 index 0000000..44f731b --- /dev/null +++ b/vendor/github.com/FactomProject/btcutilecc/.gitignore @@ -0,0 +1,25 @@ +# Compiled Object files, Static and Dynamic libs (Shared Objects) +*.o +*.a +*.so + +# Folders +_obj +_test + +# Architecture specific extensions/prefixes +*.[568vq] +[568vq].out + +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* + +_testmain.go + +*.exe + +# Editor temporary files +*.swp diff --git a/vendor/github.com/FactomProject/btcutilecc/README.md b/vendor/github.com/FactomProject/btcutilecc/README.md new file mode 100644 index 0000000..07922e2 --- /dev/null +++ b/vendor/github.com/FactomProject/btcutilecc/README.md @@ -0,0 +1,8 @@ +btcutil +======= + +Utility functions for Bitcoin elliptic curve cryptography. + +Install with + + go get github.com/mndrix/btcutil diff --git a/vendor/github.com/FactomProject/btcutilecc/arith.go b/vendor/github.com/FactomProject/btcutilecc/arith.go new file mode 100644 index 0000000..9919a71 --- /dev/null +++ b/vendor/github.com/FactomProject/btcutilecc/arith.go @@ -0,0 +1,32 @@ +package btcutil + +import "crypto/ecdsa" +import "math/big" + +// Multiplies the base G by a large integer. The resulting +// point is represented as an ECDSA public key since that's +// typically how they're used. +func ScalarBaseMult(k *big.Int) *ecdsa.PublicKey { + key := new(ecdsa.PublicKey) + key.Curve = Secp256k1() + key.X, key.Y = Secp256k1().ScalarBaseMult(k.Bytes()) + return key +} + +// Multiply a large integer and a point. The resulting point +// is represented as an ECDSA public key. +func ScalarMult(k *big.Int, B *ecdsa.PublicKey) *ecdsa.PublicKey { + key := new(ecdsa.PublicKey) + key.Curve = Secp256k1() + key.X, key.Y = Secp256k1().ScalarMult(B.X, B.Y, k.Bytes()) + return key +} + +// Adds two points to create a third. Points are represented as +// ECDSA public keys. +func Add(a, b *ecdsa.PublicKey) *ecdsa.PublicKey { + key := new(ecdsa.PublicKey) + key.Curve = Secp256k1() + key.X, key.Y = Secp256k1().Add(a.X, a.Y, b.X, b.Y) + return key +} diff --git a/vendor/github.com/FactomProject/btcutilecc/blind.go b/vendor/github.com/FactomProject/btcutilecc/blind.go new file mode 100644 index 0000000..f83fa3e --- /dev/null +++ b/vendor/github.com/FactomProject/btcutilecc/blind.go @@ -0,0 +1,30 @@ +package btcutil + +import "crypto/ecdsa" +import "fmt" +import "math/big" + +// Based on algorithm described in An Efficient Blind Signature Scheme +// Based on the Elliptic Curve Discrete Logarithm Problem by +// Nikooghadam and Zakerolhosseini + +type BlindSignature struct { + M, S *big.Int // called m and s in the paper + F *ecdsa.PublicKey +} + +func BlindVerify(Q *ecdsa.PublicKey, sig *BlindSignature) bool { + crv := Secp256k1().Params() + + // onlooker verifies signature (§4.5) + sG := ScalarBaseMult(sig.S) + rm := new(big.Int).Mul(new(big.Int).Mod(sig.F.X, crv.N), sig.M) + rm.Mod(rm, crv.N) + rmQ := ScalarMult(rm, Q) + rmQplusF := Add(rmQ, sig.F) + + fmt.Println("") + fmt.Printf("sG = %x\n", sG.X) + fmt.Printf("rmQ + F = %x\n", rmQplusF.X) + return KeysEqual(sG, rmQplusF) +} diff --git a/vendor/github.com/FactomProject/btcutilecc/blind_requester.go b/vendor/github.com/FactomProject/btcutilecc/blind_requester.go new file mode 100644 index 0000000..bb244bf --- /dev/null +++ b/vendor/github.com/FactomProject/btcutilecc/blind_requester.go @@ -0,0 +1,72 @@ +package btcutil + +import "crypto/ecdsa" +import "crypto/rand" +import "math/big" + +type BlindRequesterState struct { + // secret stuff + a, b, bInv, c *big.Int + + // shareable stuff + F *ecdsa.PublicKey + X0 *big.Int // + Mhat *big.Int // called m̂ in the paper +} + +// Calculates a blinded version of message m +func BlindMessage(rState *BlindRequesterState, Q, R *ecdsa.PublicKey, m *big.Int) *big.Int { + crv := Secp256k1().Params() + + // generate F which is not equal to O (§4.2) + var err error + F := new(ecdsa.PublicKey) + for F.X == nil && F.Y == nil { + // requester's three blinding factors (§4.2) + rState.a, err = RandFieldElement(rand.Reader) + maybePanic(err) + rState.b, err = RandFieldElement(rand.Reader) + maybePanic(err) + rState.c, err = RandFieldElement(rand.Reader) + maybePanic(err) + rState.bInv = new(big.Int).ModInverse(rState.b, crv.N) + + // requester calculates point F (§4.2) + abInv := new(big.Int).Mul(rState.a, rState.bInv) + abInv.Mod(abInv, crv.N) + bInvR := ScalarMult(rState.bInv, R) + abInvQ := ScalarMult(abInv, Q) + cG := ScalarBaseMult(rState.c) + F = Add(bInvR, abInvQ) + F = Add(F, cG) + } + rState.F = F + + // calculate r and m̂ + r := new(big.Int).Mod(F.X, crv.N) + mHat := new(big.Int).Mul(rState.b, r) + mHat.Mul(mHat, m) + mHat.Add(mHat, rState.a) + mHat.Mod(mHat, crv.N) + rState.Mhat = mHat + + return rState.Mhat +} + +// Extract true signature from the blind signature +func BlindExtract(rState *BlindRequesterState, sHat *big.Int) *BlindSignature { + crv := Secp256k1().Params() + + // requester extracts the real signature (§4.4) + s := new(big.Int).Mul(rState.bInv, sHat) + s.Add(s, rState.c) + s.Mod(s, crv.N) + sig := &BlindSignature{S: s, F: rState.F} + return sig +} + +func maybePanic(err error) { + if err != nil { + panic(err) + } +} diff --git a/vendor/github.com/FactomProject/btcutilecc/blind_signer.go b/vendor/github.com/FactomProject/btcutilecc/blind_signer.go new file mode 100644 index 0000000..642a179 --- /dev/null +++ b/vendor/github.com/FactomProject/btcutilecc/blind_signer.go @@ -0,0 +1,54 @@ +package btcutil + +import "crypto/ecdsa" +import "crypto/rand" +import "fmt" +import "math/big" + +type BlindSignerState struct { + // secret stuff + d, k *big.Int + + // shareable stuff + Q *ecdsa.PublicKey +} + +// Request that the signer start a blind signature protocol. Returns +// the signer's public key and an EC point named R. +func BlindSession(sState *BlindSignerState) (*ecdsa.PublicKey, *ecdsa.PublicKey) { + + // generate signer's private & public key pair + if sState.Q == nil { + keys, err := GenerateKey(rand.Reader) + maybePanic(err) + sState.d = keys.D + sState.Q = &keys.PublicKey + fmt.Printf("Signer:\t%x\n\t%x\n", sState.d, sState.Q.X) + } + + // generate k and R for each user request (§4.2) + request, err := GenerateKey(rand.Reader) + maybePanic(err) + sState.k = request.D + R := &request.PublicKey + + return sState.Q, R +} + +// Signs a blinded message +func BlindSign(sState *BlindSignerState, R *ecdsa.PublicKey, mHat *big.Int) *big.Int { + crv := Secp256k1().Params() + + // verify that R matches our secret k + R_ := ScalarBaseMult(sState.k) + if !KeysEqual(R, R_) { + panic("unknown R") + } + + // signer generates signature (§4.3) + sHat := new(big.Int).Mul(sState.d, mHat) + sHat.Add(sHat, sState.k) + sHat.Mod(sHat, crv.N) + + return sHat +} diff --git a/vendor/github.com/FactomProject/btcutilecc/ecdh.go b/vendor/github.com/FactomProject/btcutilecc/ecdh.go new file mode 100644 index 0000000..8c38a86 --- /dev/null +++ b/vendor/github.com/FactomProject/btcutilecc/ecdh.go @@ -0,0 +1,10 @@ +package btcutil + +import "crypto/ecdsa" +import "math/big" + +// Calculate a shared secret using elliptic curve Diffie-Hellman +func ECDH(priv *ecdsa.PrivateKey, pub *ecdsa.PublicKey) *big.Int { + x, _ := Secp256k1().ScalarMult(pub.X, pub.Y, priv.D.Bytes()) + return x +} diff --git a/vendor/github.com/FactomProject/btcutilecc/keys.go b/vendor/github.com/FactomProject/btcutilecc/keys.go new file mode 100644 index 0000000..0b9f7e7 --- /dev/null +++ b/vendor/github.com/FactomProject/btcutilecc/keys.go @@ -0,0 +1,13 @@ +package btcutil + +import "crypto/ecdsa" +import "io" + +// GenerateKey generates a public and private key pair +func GenerateKey(rand io.Reader) (*ecdsa.PrivateKey, error) { + return ecdsa.GenerateKey(Secp256k1(), rand) +} + +func KeysEqual(a, b *ecdsa.PublicKey) bool { + return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0 +} diff --git a/vendor/github.com/FactomProject/btcutilecc/random.go b/vendor/github.com/FactomProject/btcutilecc/random.go new file mode 100644 index 0000000..84b66cb --- /dev/null +++ b/vendor/github.com/FactomProject/btcutilecc/random.go @@ -0,0 +1,26 @@ +package btcutil + +import "io" +import "math/big" + +var one = new(big.Int).SetInt64(1) + +// RandFieldElement returns a random element of the field underlying the given +// curve using the procedure given in [NSA] A.2.1. +// +// Implementation copied from Go's crypto/ecdsa package since +// the function wasn't public. Modified to always use secp256k1 curve. +func RandFieldElement(rand io.Reader) (k *big.Int, err error) { + params := Secp256k1().Params() + b := make([]byte, params.BitSize/8+8) + _, err = io.ReadFull(rand, b) + if err != nil { + return + } + + k = new(big.Int).SetBytes(b) + n := new(big.Int).Sub(params.N, one) + k.Mod(k, n) + k.Add(k, one) + return +} diff --git a/vendor/github.com/FactomProject/btcutilecc/secp256k1.go b/vendor/github.com/FactomProject/btcutilecc/secp256k1.go new file mode 100644 index 0000000..7cab8d2 --- /dev/null +++ b/vendor/github.com/FactomProject/btcutilecc/secp256k1.go @@ -0,0 +1,238 @@ +// Copyright 2010 The Go Authors. All rights reserved. +// Copyright 2011 ThePiachu. All rights reserved. +// Copyright 2013 Michael Hendricks. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package btcutil + +// See http://www.hyperelliptic.org/EFD/g1p/auto-shortw.html +// and http://stackoverflow.com/a/8392111/174463 +// for details on how this Koblitz curve math works. + +import "crypto/elliptic" +import "fmt" +import "math/big" + +// A Koblitz Curve with a=0. +type KoblitzCurve struct { + P *big.Int // the order of the underlying field + N *big.Int // the order of the base point + B *big.Int // the constant of the KoblitzCurve equation + Gx, Gy *big.Int // (x,y) of the base point + BitSize int // the size of the underlying field +} + +// Returns the secp256k1 curve. +var secp256k1 *KoblitzCurve + +func Secp256k1() elliptic.Curve { + return secp256k1 +} + +func init() { + var p, n, gx, gy big.Int + fmt.Sscan("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", &p) + fmt.Sscan("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", &n) + fmt.Sscan("0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", &gx) + fmt.Sscan("0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", &gy) + b := big.NewInt(7) + secp256k1 = &KoblitzCurve{ + P: &p, + N: &n, + B: b, + Gx: &gx, + Gy: &gy, + BitSize: 256, + } +} + +func (curve *KoblitzCurve) IsOnCurve(x, y *big.Int) bool { + // y² = x³ + b + y2 := new(big.Int).Mul(y, y) + y2.Mod(y2, curve.P) + + x3 := new(big.Int).Mul(x, x) + x3.Mul(x3, x) + + x3.Add(x3, curve.B) + x3.Mod(x3, curve.P) + + return x3.Cmp(y2) == 0 +} + +// affineFromJacobian reverses the Jacobian transform. +func (curve *KoblitzCurve) affineFromJacobian(x, y, z *big.Int) (xOut, yOut *big.Int) { + zinv := new(big.Int).ModInverse(z, curve.P) + zinvsq := new(big.Int).Mul(zinv, zinv) + + xOut = new(big.Int).Mul(x, zinvsq) + xOut.Mod(xOut, curve.P) + + zinvsq.Mul(zinvsq, zinv) + yOut = new(big.Int).Mul(y, zinvsq) + yOut.Mod(yOut, curve.P) + return +} + +func (curve *KoblitzCurve) Add(x1, y1, x2, y2 *big.Int) (*big.Int, *big.Int) { + z := new(big.Int).SetInt64(1) + return curve.affineFromJacobian(curve.addJacobian(x1, y1, z, x2, y2, z)) +} + +// addJacobian takes two points in Jacobian coordinates, (x1, y1, z1) and +// (x2, y2, z2) and returns their sum, also in Jacobian form. +func (curve *KoblitzCurve) addJacobian(x1, y1, z1, x2, y2, z2 *big.Int) (*big.Int, *big.Int, *big.Int) { + // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#addition-add-2007-bl + z1z1 := new(big.Int).Mul(z1, z1) + z1z1.Mod(z1z1, curve.P) + z2z2 := new(big.Int).Mul(z2, z2) + z2z2.Mod(z2z2, curve.P) + + u1 := new(big.Int).Mul(x1, z2z2) + u1.Mod(u1, curve.P) + u2 := new(big.Int).Mul(x2, z1z1) + u2.Mod(u2, curve.P) + h := new(big.Int).Sub(u2, u1) + if h.Sign() == -1 { + h.Add(h, curve.P) + } + i := new(big.Int).Lsh(h, 1) + i.Mul(i, i) + j := new(big.Int).Mul(h, i) + + s1 := new(big.Int).Mul(y1, z2) + s1.Mul(s1, z2z2) + s1.Mod(s1, curve.P) + s2 := new(big.Int).Mul(y2, z1) + s2.Mul(s2, z1z1) + s2.Mod(s2, curve.P) + r := new(big.Int).Sub(s2, s1) + if r.Sign() == -1 { + r.Add(r, curve.P) + } + r.Lsh(r, 1) + v := new(big.Int).Mul(u1, i) + + x3 := new(big.Int).Set(r) + x3.Mul(x3, x3) + x3.Sub(x3, j) + x3.Sub(x3, v) + x3.Sub(x3, v) + x3.Mod(x3, curve.P) + + y3 := new(big.Int).Set(r) + v.Sub(v, x3) + y3.Mul(y3, v) + s1.Mul(s1, j) + s1.Lsh(s1, 1) + y3.Sub(y3, s1) + y3.Mod(y3, curve.P) + + z3 := new(big.Int).Add(z1, z2) + z3.Mul(z3, z3) + z3.Sub(z3, z1z1) + if z3.Sign() == -1 { + z3.Add(z3, curve.P) + } + z3.Sub(z3, z2z2) + if z3.Sign() == -1 { + z3.Add(z3, curve.P) + } + z3.Mul(z3, h) + z3.Mod(z3, curve.P) + + return x3, y3, z3 +} + +func (curve *KoblitzCurve) Double(x1, y1 *big.Int) (*big.Int, *big.Int) { + z1 := new(big.Int).SetInt64(1) + return curve.affineFromJacobian(curve.doubleJacobian(x1, y1, z1)) +} + +// doubleJacobian takes a point in Jacobian coordinates, (x, y, z), and +// returns its double, also in Jacobian form. +func (curve *KoblitzCurve) doubleJacobian(x, y, z *big.Int) (*big.Int, *big.Int, *big.Int) { + // See http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian-0.html#doubling-dbl-2009-l + + a := new(big.Int).Mul(x, x) //X1² + b := new(big.Int).Mul(y, y) //Y1² + c := new(big.Int).Mul(b, b) //B² + + d := new(big.Int).Add(x, b) //X1+B + d.Mul(d, d) //(X1+B)² + d.Sub(d, a) //(X1+B)²-A + d.Sub(d, c) //(X1+B)²-A-C + d.Mul(d, big.NewInt(2)) //2*((X1+B)²-A-C) + + e := new(big.Int).Mul(big.NewInt(3), a) //3*A + f := new(big.Int).Mul(e, e) //E² + + x3 := new(big.Int).Mul(big.NewInt(2), d) //2*D + x3.Sub(f, x3) //F-2*D + x3.Mod(x3, curve.P) + + y3 := new(big.Int).Sub(d, x3) //D-X3 + y3.Mul(e, y3) //E*(D-X3) + y3.Sub(y3, new(big.Int).Mul(big.NewInt(8), c)) //E*(D-X3)-8*C + y3.Mod(y3, curve.P) + + z3 := new(big.Int).Mul(y, z) //Y1*Z1 + z3.Mul(big.NewInt(2), z3) //3*Y1*Z1 + z3.Mod(z3, curve.P) + + return x3, y3, z3 +} + +func (curve *KoblitzCurve) ScalarMult(Bx, By *big.Int, k []byte) (*big.Int, *big.Int) { + // We have a slight problem in that the identity of the group (the + // point at infinity) cannot be represented in (x, y) form on a finite + // machine. Thus the standard add/double algorithm has to be tweaked + // slightly: our initial state is not the identity, but x, and we + // ignore the first true bit in |k|. If we don't find any true bits in + // |k|, then we return nil, nil, because we cannot return the identity + // element. + + Bz := new(big.Int).SetInt64(1) + x := Bx + y := By + z := Bz + + seenFirstTrue := false + for _, byte := range k { + for bitNum := 0; bitNum < 8; bitNum++ { + if seenFirstTrue { + x, y, z = curve.doubleJacobian(x, y, z) + } + if byte&0x80 == 0x80 { + if !seenFirstTrue { + seenFirstTrue = true + } else { + x, y, z = curve.addJacobian(Bx, By, Bz, x, y, z) + } + } + byte <<= 1 + } + } + + if !seenFirstTrue { + return nil, nil + } + + return curve.affineFromJacobian(x, y, z) +} + +func (curve *KoblitzCurve) ScalarBaseMult(k []byte) (*big.Int, *big.Int) { + return curve.ScalarMult(curve.Gx, curve.Gy, k) +} + +func (curve *KoblitzCurve) Params() *elliptic.CurveParams { + return &elliptic.CurveParams{ + P: curve.P, + N: curve.N, + B: curve.B, + Gx: curve.Gx, + Gy: curve.Gy, + BitSize: curve.BitSize, + } +} diff --git a/vendor/github.com/tyler-smith/go-bip32/.travis.yml b/vendor/github.com/tyler-smith/go-bip32/.travis.yml new file mode 100644 index 0000000..8a53d43 --- /dev/null +++ b/vendor/github.com/tyler-smith/go-bip32/.travis.yml @@ -0,0 +1,11 @@ +language: go +go: + - 1.8.1 + - tip + +script: + - go get github.com/stretchr/testify/assert + - go test -v github.com/tyler-smith/go-bip32 + - go get -u github.com/alecthomas/gometalinter + - $GOPATH/bin/gometalinter --install + - $GOPATH/bin/gometalinter $GOPATH/src/github.com/tyler-smith/go-bip32 \ No newline at end of file diff --git a/vendor/github.com/tyler-smith/go-bip32/CONTRIBUTING.md b/vendor/github.com/tyler-smith/go-bip32/CONTRIBUTING.md new file mode 100644 index 0000000..232572a --- /dev/null +++ b/vendor/github.com/tyler-smith/go-bip32/CONTRIBUTING.md @@ -0,0 +1 @@ +Contributions are very welcome. This is a side-project and I can't do it all on my own. Working together is the best way to get to the best sofware. Changes must comply with [gometalinter](https://github.com/alecthomas/gometalinter). It's tested in Travis and PRs will be rejected if they fail. diff --git a/vendor/github.com/tyler-smith/go-bip32/Gopkg.lock b/vendor/github.com/tyler-smith/go-bip32/Gopkg.lock new file mode 100644 index 0000000..237c3d1 --- /dev/null +++ b/vendor/github.com/tyler-smith/go-bip32/Gopkg.lock @@ -0,0 +1,37 @@ +memo = "c334fc04bb8346ff7e90f5df9b09eee70e1db7b18f36b1e90e092477e2e64b35" + +[[projects]] + branch = "master" + name = "github.com/FactomProject/basen" + packages = ["."] + revision = "fe3947df716ebfda9847eb1b9a48f9592e06478c" + +[[projects]] + branch = "master" + name = "github.com/FactomProject/btcutilecc" + packages = ["."] + revision = "d3a63a5752ecf3fbc06bd97365da752111c263df" + +[[projects]] + name = "github.com/davecgh/go-spew" + packages = ["spew"] + revision = "346938d642f2ec3594ed81d874461961cd0faa76" + version = "v1.1.0" + +[[projects]] + name = "github.com/pmezard/go-difflib" + packages = ["difflib"] + revision = "792786c7400a136282c1664665ae0a8db921c6c2" + version = "v1.0.0" + +[[projects]] + branch = "master" + name = "github.com/stretchr/testify" + packages = ["assert"] + revision = "f6abca593680b2315d2075e0f5e2a9751e3f431a" + +[[projects]] + branch = "master" + name = "golang.org/x/crypto" + packages = ["ripemd160"] + revision = "850760c427c516be930bc91280636328f1a62286" diff --git a/vendor/github.com/tyler-smith/go-bip32/Gopkg.toml b/vendor/github.com/tyler-smith/go-bip32/Gopkg.toml new file mode 100644 index 0000000..6ccded6 --- /dev/null +++ b/vendor/github.com/tyler-smith/go-bip32/Gopkg.toml @@ -0,0 +1,68 @@ + +## Gopkg.toml example (these lines may be deleted) + +## "required" lists a set of packages (not projects) that must be included in +## Gopkg.lock. This list is merged with the set of packages imported by the current +## project. Use it when your project needs a package it doesn't explicitly import - +## including "main" packages. +# required = ["github.com/user/thing/cmd/thing"] + +## "ignored" lists a set of packages (not projects) that are ignored when +## dep statically analyzes source code. Ignored packages can be in this project, +## or in a dependency. +# ignored = ["github.com/user/project/badpkg"] + +## Dependencies define constraints on dependent projects. They are respected by +## dep whether coming from the Gopkg.toml of the current project or a dependency. +# [[dependencies]] +## Required: the root import path of the project being constrained. +# name = "github.com/user/project" +# +## Recommended: the version constraint to enforce for the project. +## Only one of "branch", "version" or "revision" can be specified. +# version = "1.0.0" +# branch = "master" +# revision = "abc123" +# +## Optional: an alternate location (URL or import path) for the project's source. +# source = "https://github.com/myfork/package.git" + +## Overrides have the same structure as [[dependencies]], but supercede all +## [[dependencies]] declarations from all projects. Only the current project's +## [[overrides]] are applied. +## +## Overrides are a sledgehammer. Use them only as a last resort. +# [[overrides]] +## Required: the root import path of the project being constrained. +# name = "github.com/user/project" +# +## Optional: specifying a version constraint override will cause all other +## constraints on this project to be ignored; only the overriden constraint +## need be satisfied. +## Again, only one of "branch", "version" or "revision" can be specified. +# version = "1.0.0" +# branch = "master" +# revision = "abc123" +# +## Optional: specifying an alternate source location as an override will +## enforce that the alternate location is used for that project, regardless of +## what source location any dependent projects specify. +# source = "https://github.com/myfork/package.git" + + + +[[dependencies]] + branch = "master" + name = "github.com/FactomProject/basen" + +[[dependencies]] + branch = "master" + name = "github.com/FactomProject/btcutilecc" + +[[dependencies]] + branch = "master" + name = "github.com/stretchr/testify" + +[[dependencies]] + branch = "master" + name = "golang.org/x/crypto" diff --git a/vendor/github.com/tyler-smith/go-bip32/LICENSE b/vendor/github.com/tyler-smith/go-bip32/LICENSE new file mode 100644 index 0000000..31dd8ba --- /dev/null +++ b/vendor/github.com/tyler-smith/go-bip32/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017 Tyler Smith + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/tyler-smith/go-bip32/README.md b/vendor/github.com/tyler-smith/go-bip32/README.md new file mode 100644 index 0000000..005889a --- /dev/null +++ b/vendor/github.com/tyler-smith/go-bip32/README.md @@ -0,0 +1,66 @@ +# go-bip32 + +[![Build Status](https://api.travis-ci.org/tyler-smith/go-bip32.png)](https://travis-ci.org/tyler-smith/go-bip32) + +An implementation of the BIP32 spec for Hierarchical Deterministic Bitcoin addresses as a simple Go library. The semantics of derived keys are up to the user. [BIP43](https://github.com/bitcoin/bips/blob/master/bip-0043.mediawiki) and [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) are good schemes to implement with this library. An additional library for either or both of those on top of this library should be developed. + +## Example + +It's very unlikely, but possible, that a given index does not produce a valid +private key. Error checking is skipped in this example for brevity but should be handled in real code. In such a case, a ErrInvalidPrivateKey is returned. + +ErrInvalidPrivateKey should be handled by trying the next index for a child key. + +Any valid private key will have a valid public key so that `Key.PublicKey()` +method never returns an error. + +```go +package main + +import ( + "github.com/tyler-smith/go-bip32" + "fmt" + "log" +) + +// Example address creation for a fictitious company ComputerVoice Inc. where +// each department has their own wallet to manage +func main(){ + // Generate a seed to determine all keys from. + // This should be persisted, backed up, and secured + seed, err := bip32.NewSeed() + if err != nil { + log.Fatalln("Error generating seed:", err) + } + + // Create master private key from seed + computerVoiceMasterKey, _ := bip32.NewMasterKey(seed) + + // Map departments to keys + // There is a very small chance a given child index is invalid + // If so your real program should handle this by skipping the index + departmentKeys := map[string]*bip32.Key{} + departmentKeys["Sales"], _ = computerVoiceMasterKey.NewChildKey(0) + departmentKeys["Marketing"], _ = computerVoiceMasterKey.NewChildKey(1) + departmentKeys["Engineering"], _ = computerVoiceMasterKey.NewChildKey(2) + departmentKeys["Customer Support"], _ = computerVoiceMasterKey.NewChildKey(3) + + // Create public keys for record keeping, auditors, payroll, etc + departmentAuditKeys := map[string]*bip32.Key{} + departmentAuditKeys["Sales"] = departmentKeys["Sales"].PublicKey() + departmentAuditKeys["Marketing"] = departmentKeys["Marketing"].PublicKey() + departmentAuditKeys["Engineering"] = departmentKeys["Engineering"].PublicKey() + departmentAuditKeys["Customer Support"] = departmentKeys["Customer Support"].PublicKey() + + // Print public keys + for department, pubKey := range departmentAuditKeys { + fmt.Println(department, pubKey) + } +} +``` + +## Thanks + +The developers at [Factom](https://www.factom.com/) have contributed a lot to this library and have made many great improvements to it. Please check out their project(s) and give them a thanks if you use this library. + +Thanks to [bartekn](https://github.com/bartekn) from Stellar for some important bug catches. diff --git a/vendor/github.com/tyler-smith/go-bip32/bip32.go b/vendor/github.com/tyler-smith/go-bip32/bip32.go new file mode 100644 index 0000000..8ca4121 --- /dev/null +++ b/vendor/github.com/tyler-smith/go-bip32/bip32.go @@ -0,0 +1,286 @@ +package bip32 + +import ( + "bytes" + "crypto/hmac" + "crypto/rand" + "crypto/sha512" + "encoding/hex" + "errors" +) + +const ( + // FirstHardenedChild is the index of the firxt "harded" child key as per the + // bip32 spec + FirstHardenedChild = uint32(0x80000000) + + // PublicKeyCompressedLength is the byte count of a compressed public key + PublicKeyCompressedLength = 33 +) + +var ( + // PrivateWalletVersion is the version flag for serialized private keys + PrivateWalletVersion, _ = hex.DecodeString("0488ADE4") + + // PublicWalletVersion is the version flag for serialized private keys + PublicWalletVersion, _ = hex.DecodeString("0488B21E") + + // ErrSerializedKeyWrongSize is returned when trying to deserialize a key that + // has an incorrect length + ErrSerializedKeyWrongSize = errors.New("Serialized keys should by exactly 82 bytes") + + // ErrHardnedChildPublicKey is returned when trying to create a harded child + // of the public key + ErrHardnedChildPublicKey = errors.New("Can't create hardened child for public key") + + // ErrInvalidChecksum is returned when deserializing a key with an incorrect + // checksum + ErrInvalidChecksum = errors.New("Checksum doesn't match") + + // ErrInvalidPrivateKey is returned when a derived private key is invalid + ErrInvalidPrivateKey = errors.New("Invalid private key") + + // ErrInvalidPublicKey is returned when a derived public key is invalid + ErrInvalidPublicKey = errors.New("Invalid public key") +) + +// Key represents a bip32 extended key +type Key struct { + Key []byte // 33 bytes + Version []byte // 4 bytes + ChildNumber []byte // 4 bytes + FingerPrint []byte // 4 bytes + ChainCode []byte // 32 bytes + Depth byte // 1 bytes + IsPrivate bool // unserialized +} + +// NewMasterKey creates a new master extended key from a seed +func NewMasterKey(seed []byte) (*Key, error) { + // Generate key and chaincode + hmac := hmac.New(sha512.New, []byte("Bitcoin seed")) + _, err := hmac.Write(seed) + if err != nil { + return nil, err + } + intermediary := hmac.Sum(nil) + + // Split it into our key and chain code + keyBytes := intermediary[:32] + chainCode := intermediary[32:] + + // Validate key + err = validatePrivateKey(keyBytes) + if err != nil { + return nil, err + } + + // Create the key struct + key := &Key{ + Version: PrivateWalletVersion, + ChainCode: chainCode, + Key: keyBytes, + Depth: 0x0, + ChildNumber: []byte{0x00, 0x00, 0x00, 0x00}, + FingerPrint: []byte{0x00, 0x00, 0x00, 0x00}, + IsPrivate: true, + } + + return key, nil +} + +// NewChildKey derives a child key from a given parent as outlined by bip32 +func (key *Key) NewChildKey(childIdx uint32) (*Key, error) { + // Fail early if trying to create hardned child from public key + if !key.IsPrivate && childIdx >= FirstHardenedChild { + return nil, ErrHardnedChildPublicKey + } + + intermediary, err := key.getIntermediary(childIdx) + if err != nil { + return nil, err + } + + // Create child Key with data common to all both scenarios + childKey := &Key{ + ChildNumber: uint32Bytes(childIdx), + ChainCode: intermediary[32:], + Depth: key.Depth + 1, + IsPrivate: key.IsPrivate, + } + + // Bip32 CKDpriv + if key.IsPrivate { + childKey.Version = PrivateWalletVersion + fingerprint, err := hash160(publicKeyForPrivateKey(key.Key)) + if err != nil { + return nil, err + } + childKey.FingerPrint = fingerprint[:4] + childKey.Key = addPrivateKeys(intermediary[:32], key.Key) + + // Validate key + err = validatePrivateKey(childKey.Key) + if err != nil { + return nil, err + } + // Bip32 CKDpub + } else { + keyBytes := publicKeyForPrivateKey(intermediary[:32]) + + // Validate key + err := validateChildPublicKey(keyBytes) + if err != nil { + return nil, err + } + + childKey.Version = PublicWalletVersion + fingerprint, err := hash160(key.Key) + if err != nil { + return nil, err + } + childKey.FingerPrint = fingerprint[:4] + childKey.Key = addPublicKeys(keyBytes, key.Key) + } + + return childKey, nil +} + +func (key *Key) getIntermediary(childIdx uint32) ([]byte, error) { + // Get intermediary to create key and chaincode from + // Hardened children are based on the private key + // NonHardened children are based on the public key + childIndexBytes := uint32Bytes(childIdx) + + var data []byte + if childIdx >= FirstHardenedChild { + data = append([]byte{0x0}, key.Key...) + } else { + if key.IsPrivate { + data = publicKeyForPrivateKey(key.Key) + } else { + data = key.Key + } + } + data = append(data, childIndexBytes...) + + hmac := hmac.New(sha512.New, key.ChainCode) + _, err := hmac.Write(data) + if err != nil { + return nil, err + } + return hmac.Sum(nil), nil +} + +// PublicKey returns the public version of key or return a copy +// The 'Neuter' function from the bip32 spec +func (key *Key) PublicKey() *Key { + keyBytes := key.Key + + if key.IsPrivate { + keyBytes = publicKeyForPrivateKey(keyBytes) + } + + return &Key{ + Version: PublicWalletVersion, + Key: keyBytes, + Depth: key.Depth, + ChildNumber: key.ChildNumber, + FingerPrint: key.FingerPrint, + ChainCode: key.ChainCode, + IsPrivate: false, + } +} + +// Serialize a Key to a 78 byte byte slice +func (key *Key) Serialize() ([]byte, error) { + // Private keys should be prepended with a single null byte + keyBytes := key.Key + if key.IsPrivate { + keyBytes = append([]byte{0x0}, keyBytes...) + } + + // Write fields to buffer in order + buffer := new(bytes.Buffer) + buffer.Write(key.Version) + buffer.WriteByte(key.Depth) + buffer.Write(key.FingerPrint) + buffer.Write(key.ChildNumber) + buffer.Write(key.ChainCode) + buffer.Write(keyBytes) + + // Append the standard doublesha256 checksum + serializedKey, err := addChecksumToBytes(buffer.Bytes()) + if err != nil { + return nil, err + } + + return serializedKey, nil +} + +// B58Serialize encodes the Key in the standard Bitcoin base58 encoding +func (key *Key) B58Serialize() string { + serializedKey, err := key.Serialize() + if err != nil { + return "" + } + + return base58Encode(serializedKey) +} + +// String encodes the Key in the standard Bitcoin base58 encoding +func (key *Key) String() string { + return key.B58Serialize() +} + +// Deserialize a byte slice into a Key +func Deserialize(data []byte) (*Key, error) { + if len(data) != 82 { + return nil, ErrSerializedKeyWrongSize + } + var key = &Key{} + key.Version = data[0:4] + key.Depth = data[4] + key.FingerPrint = data[5:9] + key.ChildNumber = data[9:13] + key.ChainCode = data[13:45] + + if data[45] == byte(0) { + key.IsPrivate = true + key.Key = data[46:78] + } else { + key.IsPrivate = false + key.Key = data[45:78] + } + + // validate checksum + cs1, err := checksum(data[0 : len(data)-4]) + if err != nil { + return nil, err + } + + cs2 := data[len(data)-4:] + for i := range cs1 { + if cs1[i] != cs2[i] { + return nil, ErrInvalidChecksum + } + } + return key, nil +} + +// B58Deserialize deserializes a Key encoded in base58 encoding +func B58Deserialize(data string) (*Key, error) { + b, err := base58Decode(data) + if err != nil { + return nil, err + } + return Deserialize(b) +} + +// NewSeed returns a cryptographically secure seed +func NewSeed() ([]byte, error) { + // Well that easy, just make go read 256 random bytes into a slice + s := make([]byte, 256) + _, err := rand.Read(s) + return s, err +} diff --git a/vendor/github.com/tyler-smith/go-bip32/utils.go b/vendor/github.com/tyler-smith/go-bip32/utils.go new file mode 100644 index 0000000..ec8ca91 --- /dev/null +++ b/vendor/github.com/tyler-smith/go-bip32/utils.go @@ -0,0 +1,199 @@ +package bip32 + +import ( + "bytes" + "crypto/sha256" + "encoding/binary" + "fmt" + "io" + "math/big" + + "github.com/FactomProject/basen" + "github.com/FactomProject/btcutilecc" + "golang.org/x/crypto/ripemd160" +) + +var ( + curve = btcutil.Secp256k1() + curveParams = curve.Params() + + // BitcoinBase58Encoding is the encoding used for bitcoin addresses + BitcoinBase58Encoding = basen.NewEncoding("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") +) + +// +// Hashes +// + +func hashSha256(data []byte) ([]byte, error) { + hasher := sha256.New() + _, err := hasher.Write(data) + if err != nil { + return nil, err + } + return hasher.Sum(nil), nil +} + +func hashDoubleSha256(data []byte) ([]byte, error) { + hash1, err := hashSha256(data) + if err != nil { + return nil, err + } + + hash2, err := hashSha256(hash1) + if err != nil { + return nil, err + } + return hash2, nil +} + +func hashRipeMD160(data []byte) ([]byte, error) { + hasher := ripemd160.New() + _, err := io.WriteString(hasher, string(data)) + if err != nil { + return nil, err + } + return hasher.Sum(nil), nil +} + +func hash160(data []byte) ([]byte, error) { + hash1, err := hashSha256(data) + if err != nil { + return nil, err + } + + hash2, err := hashRipeMD160(hash1) + if err != nil { + return nil, err + } + + return hash2, nil +} + +// +// Encoding +// + +func checksum(data []byte) ([]byte, error) { + hash, err := hashDoubleSha256(data) + if err != nil { + return nil, err + } + + return hash[:4], nil +} + +func addChecksumToBytes(data []byte) ([]byte, error) { + checksum, err := checksum(data) + if err != nil { + return nil, err + } + return append(data, checksum...), nil +} + +func base58Encode(data []byte) string { + return BitcoinBase58Encoding.EncodeToString(data) +} + +func base58Decode(data string) ([]byte, error) { + return BitcoinBase58Encoding.DecodeString(data) +} + +// Keys +func publicKeyForPrivateKey(key []byte) []byte { + return compressPublicKey(curve.ScalarBaseMult(key)) +} + +func addPublicKeys(key1 []byte, key2 []byte) []byte { + x1, y1 := expandPublicKey(key1) + x2, y2 := expandPublicKey(key2) + return compressPublicKey(curve.Add(x1, y1, x2, y2)) +} + +func addPrivateKeys(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) + + b := key1Int.Bytes() + if len(b) < 32 { + extra := make([]byte, 32-len(b)) + b = append(extra, b...) + } + return b +} + +func compressPublicKey(x *big.Int, y *big.Int) []byte { + var key bytes.Buffer + + // Write header; 0x2 for even y value; 0x3 for odd + key.WriteByte(byte(0x2) + byte(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() + for i := 0; i < (PublicKeyCompressedLength - 1 - len(xBytes)); i++ { + key.WriteByte(0x0) + } + key.Write(xBytes) + + return key.Bytes() +} + +// As described at https://crypto.stackexchange.com/a/8916 +func expandPublicKey(key []byte) (*big.Int, *big.Int) { + Y := big.NewInt(0) + X := big.NewInt(0) + X.SetBytes(key[1:]) + + // y^2 = x^3 + ax^2 + b + // a = 0 + // => y^2 = x^3 + b + ySquared := big.NewInt(0) + ySquared.Exp(X, big.NewInt(3), nil) + ySquared.Add(ySquared, curveParams.B) + + Y.ModSqrt(ySquared, curveParams.P) + + Ymod2 := big.NewInt(0) + Ymod2.Mod(Y, big.NewInt(2)) + + signY := uint64(key[0]) - 2 + if signY != Ymod2.Uint64() { + Y.Sub(curveParams.P, Y) + } + + return X, Y +} + +func validatePrivateKey(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 + len(key) != 32 { //or is too short + return ErrInvalidPrivateKey + } + + return nil +} + +func validateChildPublicKey(key []byte) error { + x, y := expandPublicKey(key) + + if x.Sign() == 0 || y.Sign() == 0 { + return ErrInvalidPublicKey + } + + return nil +} + +// +// Numerical +// +func uint32Bytes(i uint32) []byte { + bytes := make([]byte, 4) + binary.BigEndian.PutUint32(bytes, i) + return bytes +}