func TestEncodeDecodeWIF(t *testing.T) { priv1, _ := btcec.PrivKeyFromBytes(btcec.S256(), []byte{ 0x0c, 0x28, 0xfc, 0xa3, 0x86, 0xc7, 0xa2, 0x27, 0x60, 0x0b, 0x2f, 0xe5, 0x0b, 0x7c, 0xae, 0x11, 0xec, 0x86, 0xd3, 0xbf, 0x1f, 0xbe, 0x47, 0x1b, 0xe8, 0x98, 0x27, 0xe1, 0x9d, 0x72, 0xaa, 0x1d}) priv2, _ := btcec.PrivKeyFromBytes(btcec.S256(), []byte{ 0xdd, 0xa3, 0x5a, 0x14, 0x88, 0xfb, 0x97, 0xb6, 0xeb, 0x3f, 0xe6, 0xe9, 0xef, 0x2a, 0x25, 0x81, 0x4e, 0x39, 0x6f, 0xb5, 0xdc, 0x29, 0x5f, 0xe9, 0x94, 0xb9, 0x67, 0x89, 0xb2, 0x1a, 0x03, 0x98}) wif1, err := NewWIF(priv1, &btcnet.MainNetParams, false) if err != nil { t.Fatal(err) } wif2, err := NewWIF(priv2, &btcnet.TestNet3Params, true) if err != nil { t.Fatal(err) } tests := []struct { wif *WIF encoded string }{ { wif1, "5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ", }, { wif2, "cV1Y7ARUr9Yx7BR55nTdnR7ZXNJphZtCCMBTEZBJe1hXt2kB684q", }, } for _, test := range tests { // Test that encoding the WIF structure matches the expected string. s := test.wif.String() if s != test.encoded { t.Errorf("TestEncodeDecodePrivateKey failed: want '%s', got '%s'", test.encoded, s) continue } // Test that decoding the expected string results in the original WIF // structure. w, err := DecodeWIF(test.encoded) if err != nil { t.Error(err) continue } if got := w.String(); got != test.encoded { t.Errorf("NewWIF failed: want '%v', got '%v'", test.wif, got) } } }
// NewKeyFromString returns a new extended key instance from a base58-encoded // extended key. func NewKeyFromString(key string) (*ExtendedKey, error) { // The base58-decoded extended key must consist of a serialized payload // plus an additional 4 bytes for the checksum. decoded := btcutil.Base58Decode(key) if len(decoded) != serializedKeyLen+4 { return nil, ErrInvalidKeyLen } // The serialized format is: // version (4) || depth (1) || parent fingerprint (4)) || // child num (4) || chain code (32) || key data (33) || checksum (4) // Split the payload and checksum up and ensure the checksum matches. payload := decoded[:len(decoded)-4] checkSum := decoded[len(decoded)-4:] expectedCheckSum := btcwire.DoubleSha256(payload)[:4] if !bytes.Equal(checkSum, expectedCheckSum) { return nil, ErrBadChecksum } // Deserialize each of the payload fields. version := payload[:4] depth := uint16(payload[4:5][0]) parentFP := payload[5:9] childNum := binary.BigEndian.Uint32(payload[9:13]) chainCode := payload[13:45] keyData := payload[45:78] // The key data is a private key if it starts with 0x00. Serialized // compressed pubkeys either start with 0x02 or 0x03. isPrivate := keyData[0] == 0x00 if isPrivate { // Ensure the private key is valid. It must be within the range // of the order of the secp256k1 curve and not be 0. keyData = keyData[1:] keyNum := new(big.Int).SetBytes(keyData) if keyNum.Cmp(btcec.S256().N) >= 0 || keyNum.Sign() == 0 { return nil, ErrUnusableSeed } } else { // Ensure the public key parses correctly and is actually on the // secp256k1 curve. _, err := btcec.ParsePubKey(keyData, btcec.S256()) if err != nil { return nil, err } } return newExtendedKey(version, keyData, chainCode, parentFP, depth, childNum, isPrivate), nil }
// NewMaster creates a new master node for use in creating a hierarchical // deterministic key chain. The seed must be between 128 and 512 bits and // should be generated by a cryptographically secure random generation source. // // NOTE: There is an extremely small chance (< 1 in 2^127) the provided seed // will derive to an unusable secret key. The ErrUnusable error will be // returned if this should occur, so the caller must check for it and generate a // new seed accordingly. func NewMaster(seed []byte) (*ExtendedKey, error) { // Per [BIP32], the seed must be in range [minSeedBytes, maxSeedBytes]. if len(seed) < minSeedBytes || len(seed) > maxSeedBytes { return nil, ErrInvalidSeedLen } // First take the HMAC-SHA512 of the master key and the seed data: // I = HMAC-SHA512(Key = "Bitcoin seed", Data = S) hmac512 := hmac.New(sha512.New, masterKey) hmac512.Write(seed) lr := hmac512.Sum(nil) // Split "I" into two 32-byte sequences Il and Ir where: // Il = master secret key // Ir = master chain code secretKey := lr[:len(lr)/2] chainCode := lr[len(lr)/2:] // Ensure the key in usable. secretKeyNum := new(big.Int).SetBytes(secretKey) if secretKeyNum.Cmp(btcec.S256().N) >= 0 || secretKeyNum.Sign() == 0 { return nil, ErrUnusableSeed } parentFP := []byte{0x00, 0x00, 0x00, 0x00} return newExtendedKey(btcnet.MainNetParams.HDPrivateKeyID[:], secretKey, chainCode, parentFP, 0, 0, true), nil }
// pubKeyBytes returns bytes for the serialized compressed public key associated // with this extended key in an efficient manner including memoization as // necessary. // // When the extended key is already a public key, the key is simply returned as // is since it's already in the correct form. However, when the extended key is // a private key, the public key will be calculated and memoized so future // accesses can simply return the cached result. func (k *ExtendedKey) pubKeyBytes() []byte { // Just return the key if it's already an extended public key. if !k.isPrivate { return k.key } // This is a private extended key, so calculate and memoize the public // key if needed. if len(k.pubKey) == 0 { pkx, pky := btcec.S256().ScalarBaseMult(k.key) pubKey := btcec.PublicKey{Curve: btcec.S256(), X: pkx, Y: pky} k.pubKey = pubKey.SerializeCompressed() } return k.pubKey }
// ECPrivKey converts the extended key to a btcec private key and returns it. // As you might imagine this is only possible if the extended key is a private // extended key (as determined by the IsPrivate function). The ErrNotPrivExtKey // error will be returned if this function is called on a public extended key. func (k *ExtendedKey) ECPrivKey() (*btcec.PrivateKey, error) { if !k.isPrivate { return nil, ErrNotPrivExtKey } privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), k.key) return privKey, nil }
// DecodeWIF creates a new WIF structure by decoding the string encoding of // the import format. // // The WIF string must be a base58-encoded string of the following byte // sequence: // // * 1 byte to identify the network, must be 0x80 for mainnet or 0xef for // either testnet3 or the regression test network // * 32 bytes of a binary-encoded, big-endian, zero-padded private key // * Optional 1 byte (equal to 0x01) if the address being imported or exported // was created by taking the RIPEMD160 after SHA256 hash of a serialized // compressed (33-byte) public key // * 4 bytes of checksum, must equal the first four bytes of the double SHA256 // of every byte before the checksum in this sequence // // If the base58-decoded byte sequence does not match this, DecodeWIF will // return a non-nil error. ErrMalformedPrivateKey is returned when the WIF // is of an impossible length or the expected compressed pubkey magic number // does not equal the expected value of 0x01. ErrChecksumMismatch is returned // if the expected WIF checksum does not match the calculated checksum. func DecodeWIF(wif string) (*WIF, error) { decoded := Base58Decode(wif) decodedLen := len(decoded) var compress bool // Length of base58 decoded WIF must be 32 bytes + an optional 1 byte // (0x01) if compressed, plus 1 byte for netID + 4 bytes of checksum. switch decodedLen { case 1 + btcec.PrivKeyBytesLen + 1 + 4: if decoded[33] != compressMagic { return nil, ErrMalformedPrivateKey } compress = true case 1 + btcec.PrivKeyBytesLen + 4: compress = false default: return nil, ErrMalformedPrivateKey } // Checksum is first four bytes of double SHA256 of the identifier byte // and privKey. Verify this matches the final 4 bytes of the decoded // private key. var tosum []byte if compress { tosum = decoded[:1+btcec.PrivKeyBytesLen+1] } else { tosum = decoded[:1+btcec.PrivKeyBytesLen] } cksum := btcwire.DoubleSha256(tosum)[:4] if !bytes.Equal(cksum, decoded[decodedLen-4:]) { return nil, ErrChecksumMismatch } netID := decoded[0] privKeyBytes := decoded[1 : 1+btcec.PrivKeyBytesLen] privKey, _ := btcec.PrivKeyFromBytes(btcec.S256(), privKeyBytes) return &WIF{privKey, compress, netID}, nil }
// NewAddressPubKey returns a new AddressPubKey which represents a pay-to-pubkey // address. The serializedPubKey parameter must be a valid pubkey and can be // uncompressed, compressed, or hybrid. func NewAddressPubKey(serializedPubKey []byte, net *btcnet.Params) (*AddressPubKey, error) { pubKey, err := btcec.ParsePubKey(serializedPubKey, btcec.S256()) if err != nil { return nil, err } // Set the format of the pubkey. This probably should be returned // from btcec, but do it here to avoid API churn. We already know the // pubkey is valid since it parsed above, so it's safe to simply examine // the leading byte to get the format. pkFormat := PKFUncompressed switch serializedPubKey[0] { case 0x02, 0x03: pkFormat = PKFCompressed case 0x06, 0x07: pkFormat = PKFHybrid } return &AddressPubKey{ pubKeyFormat: pkFormat, pubKey: pubKey, pubKeyHashID: net.PubKeyHashAddrID, }, nil }
// ECPubKey converts the extended key to a btcec public key and returns it. func (k *ExtendedKey) ECPubKey() (*btcec.PublicKey, error) { return btcec.ParsePubKey(k.pubKeyBytes(), btcec.S256()) }
// Child returns a derived child extended key at the given index. When this // extended key is a private extended key (as determined by the IsPrivate // function), a private extended key will be derived. Otherwise, the derived // extended key will be also be a public extended key. // // When the index is greater to or equal than the HardenedKeyStart constant, the // derived extended key will be a hardened extended key. It is only possible to // derive a hardended extended key from a private extended key. Consequently, // this function will return ErrDeriveHardFromPublic if a hardened child // extended key is requested from a public extended key. // // A hardened extended key is useful since, as previously mentioned, it requires // a parent private extended key to derive. In other words, normal child // extended public keys can be derived from a parent public extended key (no // knowledge of the parent private key) whereas hardened extended keys may not // be. // // NOTE: There is an extremely small chance (< 1 in 2^127) the specific child // index does not derive to a usable child. The ErrInvalidChild error will be // returned if this should occur, and the caller is expected to ignore the // invalid child and simply increment to the next index. func (k *ExtendedKey) Child(i uint32) (*ExtendedKey, error) { // There are four scenarios that could happen here: // 1) Private extended key -> Hardened child private extended key // 2) Private extended key -> Non-hardened child private extended key // 3) Public extended key -> Non-hardened child public extended key // 4) Public extended key -> Hardened child public extended key (INVALID!) // Case #4 is invalid, so error out early. // A hardened child extended key may not be created from a public // extended key. isChildHardened := i >= HardenedKeyStart if !k.isPrivate && isChildHardened { return nil, ErrDeriveHardFromPublic } // The data used to derive the child key depends on whether or not the // child is hardened per [BIP32]. // // For hardened children: // 0x00 || ser256(parentKey) || ser32(i) // // For normal children: // serP(parentPubKey) || ser32(i) keyLen := 33 data := make([]byte, keyLen+4) if isChildHardened { // Case #1. // When the child is a hardened child, the key is known to be a // private key due to the above early return. Pad it with a // leading zero as required by [BIP32] for deriving the child. copy(data[1:], k.key) } else { // Case #2 or #3. // This is either a public or private extended key, but in // either case, the data which is used to derive the child key // starts with the secp256k1 compressed public key bytes. copy(data, k.pubKeyBytes()) } binary.BigEndian.PutUint32(data[keyLen:], i) // Take the HMAC-SHA512 of the current key's chain code and the derived // data: // I = HMAC-SHA512(Key = chainCode, Data = data) hmac512 := hmac.New(sha512.New, k.chainCode) hmac512.Write(data) ilr := hmac512.Sum(nil) // Split "I" into two 32-byte sequences Il and Ir where: // Il = intermediate key used to derive the child // Ir = child chain code il := ilr[:len(ilr)/2] childChainCode := ilr[len(ilr)/2:] // Both derived public or private keys rely on treating the left 32-byte // sequence calculated above (Il) as a 256-bit integer that must be // within the valid range for a secp256k1 private key. There is a small // chance (< 1 in 2^127) this condition will not hold, and in that case, // a child extended key can't be created for this index and the caller // should simply increment to the next index. ilNum := new(big.Int).SetBytes(il) if ilNum.Cmp(btcec.S256().N) >= 0 || ilNum.Sign() == 0 { return nil, ErrInvalidChild } // The algorithm used to derive the child key depends on whether or not // a private or public child is being derived. // // For private children: // childKey = parse256(Il) + parentKey // // For public children: // childKey = serP(point(parse256(Il)) + parentKey) var isPrivate bool var childKey []byte if k.isPrivate { // Case #1 or #2. // Add the parent private key to the intermediate private key to // derive the final child key. // // childKey = parse256(Il) + parenKey keyNum := new(big.Int).SetBytes(k.key) ilNum.Add(ilNum, keyNum) ilNum.Mod(ilNum, btcec.S256().N) childKey = ilNum.Bytes() isPrivate = true } else { // Case #3. // Calculate the corresponding intermediate public key for // intermediate private key. ilx, ily := btcec.S256().ScalarBaseMult(il) if ilx.Sign() == 0 || ily.Sign() == 0 { return nil, ErrInvalidChild } // Convert the serialized compressed parent public key into X // and Y coordinates so it can be added to the intermediate // public key. pubKey, err := btcec.ParsePubKey(k.key, btcec.S256()) if err != nil { return nil, err } // Add the intermediate public key to the parent public key to // derive the final child key. // // childKey = serP(point(parse256(Il)) + parentKey) childX, childY := btcec.S256().Add(ilx, ily, pubKey.X, pubKey.Y) pk := btcec.PublicKey{Curve: btcec.S256(), X: childX, Y: childY} childKey = pk.SerializeCompressed() } // The fingerprint of the parent for the derived child is the first 4 // bytes of the RIPEMD160(SHA256(parentPubKey)). parentFP := btcutil.Hash160(k.pubKeyBytes())[:4] return newExtendedKey(k.version, childKey, childChainCode, parentFP, k.depth+1, i, isPrivate), nil }