mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-05-24 07:46:45 +00:00

* Implement bip32 * Unite private and public extended keys * Change variable names * Change test name and add comment * Rename var name * Rename ckd.go to child_key_derivation.go * Rename ser32 -> serializeUint32 * Add PrivateKey method * Rename Path -> DeriveFromPath * Add comment to validateChecksum * Remove redundant condition from parsePath * Rename Fingerprint->ParentFingerprint * Merge hardened and non-hardened paths in calcI * Change fingerPrintFromPoint to method * Move hash160 to hash.go * Fix a bug in calcI * Simplify doubleSha256 * Remove slice end bound * Split long line * Change KaspaMainnetPrivate/public to represent kprv/kpub * Add comments * Fix comment * Copy base58 library to kaspad * Add versions for all networks * Change versions to hex * Add comments Co-authored-by: Svarog <feanorr@gmail.com> Co-authored-by: Elichai Turkel <elichai.turkel@gmail.com>
153 lines
3.2 KiB
Go
153 lines
3.2 KiB
Go
package bip32
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"github.com/kaspanet/go-secp256k1"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
const hardenedIndexStart = 0x80000000
|
|
|
|
// NewMaster returns a new extended private key based on the given seed and version
|
|
func NewMaster(seed []byte, version [4]byte) (*ExtendedKey, error) {
|
|
mac := newHMACWriter([]byte("Bitcoin seed"))
|
|
mac.InfallibleWrite(seed)
|
|
I := mac.Sum(nil)
|
|
|
|
var iL, iR [32]byte
|
|
copy(iL[:], I[:32])
|
|
copy(iR[:], I[32:])
|
|
|
|
privateKey, err := secp256k1.DeserializeECDSAPrivateKeyFromSlice(iL[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &ExtendedKey{
|
|
privateKey: privateKey,
|
|
Version: version,
|
|
Depth: 0,
|
|
ParentFingerprint: [4]byte{},
|
|
ChildNumber: 0,
|
|
ChainCode: iR,
|
|
}, nil
|
|
}
|
|
|
|
func isHardened(i uint32) bool {
|
|
return i >= hardenedIndexStart
|
|
}
|
|
|
|
// Child return the i'th derived child of extKey.
|
|
func (extKey *ExtendedKey) Child(i uint32) (*ExtendedKey, error) {
|
|
I, err := extKey.calcI(i)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var iL, iR [32]byte
|
|
copy(iL[:], I[:32])
|
|
copy(iR[:], I[32:])
|
|
|
|
fingerPrint, err := extKey.calcFingerprint()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
childExt := &ExtendedKey{
|
|
Version: extKey.Version,
|
|
Depth: extKey.Depth + 1,
|
|
ParentFingerprint: fingerPrint,
|
|
ChildNumber: i,
|
|
ChainCode: iR,
|
|
}
|
|
|
|
if extKey.privateKey != nil {
|
|
childExt.privateKey, err = privateKeyAdd(extKey.privateKey, iL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
publicKey, err := extKey.PublicKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
childExt.publicKey, err = pointAdd(publicKey, iL)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return childExt, nil
|
|
}
|
|
|
|
func (extKey *ExtendedKey) calcFingerprint() ([4]byte, error) {
|
|
publicKey, err := extKey.PublicKey()
|
|
if err != nil {
|
|
return [4]byte{}, err
|
|
}
|
|
|
|
serializedPoint, err := publicKey.Serialize()
|
|
if err != nil {
|
|
return [4]byte{}, err
|
|
}
|
|
|
|
hash := hash160(serializedPoint[:])
|
|
var fingerprint [4]byte
|
|
copy(fingerprint[:], hash[:4])
|
|
return fingerprint, nil
|
|
}
|
|
|
|
func privateKeyAdd(k *secp256k1.ECDSAPrivateKey, tweak [32]byte) (*secp256k1.ECDSAPrivateKey, error) {
|
|
kCopy := *k
|
|
err := kCopy.Add(tweak)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &kCopy, nil
|
|
}
|
|
|
|
func (extKey *ExtendedKey) calcI(i uint32) ([]byte, error) {
|
|
if isHardened(i) && !extKey.IsPrivate() {
|
|
return nil, errors.Errorf("Cannot calculate hardened child for public key")
|
|
}
|
|
|
|
mac := newHMACWriter(extKey.ChainCode[:])
|
|
if isHardened(i) {
|
|
mac.InfallibleWrite([]byte{0x00})
|
|
mac.InfallibleWrite(extKey.privateKey.Serialize()[:])
|
|
} else {
|
|
publicKey, err := extKey.PublicKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
serializedPublicKey, err := publicKey.Serialize()
|
|
if err != nil {
|
|
return nil, errors.Wrap(err, "error serializing public key")
|
|
}
|
|
|
|
mac.InfallibleWrite(serializedPublicKey[:])
|
|
}
|
|
|
|
mac.InfallibleWrite(serializeUint32(i))
|
|
return mac.Sum(nil), nil
|
|
}
|
|
|
|
func serializeUint32(v uint32) []byte {
|
|
serialized := make([]byte, 4)
|
|
binary.BigEndian.PutUint32(serialized, v)
|
|
return serialized
|
|
}
|
|
|
|
func pointAdd(point *secp256k1.ECDSAPublicKey, tweak [32]byte) (*secp256k1.ECDSAPublicKey, error) {
|
|
pointCopy := *point
|
|
err := pointCopy.Add(tweak)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &pointCopy, nil
|
|
}
|