// Child computes the descendant of an HDKey at the specified child number. func (k *HDKey) Child(i uint32) (*HDKey, error) { // Verify that child derivation is possible isChildHardened := i >= HardenedKeyStart if !k.IsPrivate() && isChildHardened { return nil, ErrDeriveHardenedFromPublic } // Assemble seed data for HMAC seed := make([]byte, childKeySize+childNumberSize) if isChildHardened { copy(seed, k[childKeyOffset:]) // Copy 0x00 || 32-byte secret key } else { copy(seed, k.CompressedPublicKey()[:]) // Copy HEADER || 32-byte X-coord } // Copy child number as uint32 binary.BigEndian.PutUint32(seed[childKeySize:], i) // il, ir = HMAC-512(chainCode, seed), clean up intermediary state il, childChainCode := util.HMAC512Split(k.chainCode(), seed) defer func() { util.Zero(il); util.Zero(childChainCode) }() // Left 32 bytes becomes intermediate secret key, defer clean up ilInt := new(big.Int).SetBytes(il) defer ilInt.SetUint64(0) // Check that ilInt creates valid SecretKey, clean up intermediary SecretKey isk, err := eckey.NewSecretKeyInt(ilInt) if err != nil { return nil, ErrUnusableSeed } defer isk.Zero() ver := k.version() parentCPK := k.CompressedPublicKey() fpBytes := util.Hash256d(parentCPK[:])[:fingerprintSize] fp := binary.BigEndian.Uint32(fpBytes) // If key is private, derive a child secret key if k.IsPrivate() { sk := k.computeChildSecret(ilInt) return newHDSecretKey(ver, k.depth()+1, fp, i, childChainCode, sk), nil } // Otherwise, derive child public key cpk, err := computeChildPublic(parentCPK, isk) if err != nil { return nil, err } return newHDPublicKey(ver, k.depth()+1, fp, i, childChainCode, cpk), nil }
// NewMasterHDKey computes the root HD key from the given seed, key and private // version. func NewMaster(seed, key []byte, version uint16) (*HDKey, error) { if len(seed) < MinSeedSize || len(seed) > MaxSeedSize { return nil, ErrInvalidSeedLength } // il, ir = HMAC-512(key, seed) il, chainCode := util.HMAC512Split(key, seed) defer func() { util.Zero(il); util.Zero(chainCode) }() // Left 32 bytes becomes master secret key, clean up temporary SecretKey sk, err := eckey.NewSecretKey(il) if err != nil { return nil, ErrUnusableSeed } defer sk.Zero() return newHDSecretKey(version, 0, 0, 0, chainCode, sk), nil }