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 }