mirror of
https://github.com/kaspanet/kaspad.git
synced 2025-05-25 08:16:46 +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>
149 lines
4.6 KiB
Go
149 lines
4.6 KiB
Go
package bip32
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"github.com/kaspanet/go-secp256k1"
|
|
"github.com/kaspanet/kaspad/cmd/kaspawallet/libkaspawallet/bip32/base58"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
const (
|
|
versionSerializationLen = 4
|
|
depthSerializationLen = 1
|
|
fingerprintSerializationLen = 4
|
|
childNumberSerializationLen = 4
|
|
chainCodeSerializationLen = 32
|
|
keySerializationLen = 33
|
|
checkSumLen = 4
|
|
)
|
|
|
|
const extendedKeySerializationLen = versionSerializationLen +
|
|
depthSerializationLen +
|
|
fingerprintSerializationLen +
|
|
childNumberSerializationLen +
|
|
chainCodeSerializationLen +
|
|
keySerializationLen +
|
|
checkSumLen
|
|
|
|
// DeserializeExtendedPrivateKey deserialized the given base58 string and returns an extended key
|
|
func DeserializeExtendedPrivateKey(extKeyString string) (*ExtendedKey, error) {
|
|
serializedBytes := base58.Decode(extKeyString)
|
|
return deserializeExtendedPrivateKey(serializedBytes)
|
|
}
|
|
|
|
func deserializeExtendedPrivateKey(serialized []byte) (*ExtendedKey, error) {
|
|
if len(serialized) != extendedKeySerializationLen {
|
|
return nil, errors.Errorf("key length must be %d bytes but got %d", extendedKeySerializationLen, len(serialized))
|
|
}
|
|
|
|
err := validateChecksum(serialized)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
extKey := &ExtendedKey{}
|
|
|
|
copy(extKey.Version[:], serialized[:versionSerializationLen])
|
|
extKey.Depth = serialized[versionSerializationLen]
|
|
copy(extKey.ParentFingerprint[:], serialized[versionSerializationLen+depthSerializationLen:])
|
|
extKey.ChildNumber = binary.BigEndian.Uint32(
|
|
serialized[versionSerializationLen+depthSerializationLen+fingerprintSerializationLen:],
|
|
)
|
|
copy(
|
|
extKey.ChainCode[:],
|
|
serialized[versionSerializationLen+depthSerializationLen+fingerprintSerializationLen+childNumberSerializationLen:],
|
|
)
|
|
|
|
isPrivate := isPrivateVersion(extKey.Version)
|
|
if isPrivate {
|
|
privateKeyPadding := serialized[versionSerializationLen+
|
|
depthSerializationLen+
|
|
fingerprintSerializationLen+
|
|
childNumberSerializationLen+
|
|
chainCodeSerializationLen]
|
|
if privateKeyPadding != 0 {
|
|
return nil, errors.Errorf("expected 0 padding for private key but got %d", privateKeyPadding)
|
|
}
|
|
|
|
extKey.privateKey, err = secp256k1.DeserializeECDSAPrivateKeyFromSlice(serialized[versionSerializationLen+
|
|
depthSerializationLen+
|
|
fingerprintSerializationLen+
|
|
childNumberSerializationLen+
|
|
chainCodeSerializationLen+1 : versionSerializationLen+
|
|
depthSerializationLen+
|
|
fingerprintSerializationLen+
|
|
childNumberSerializationLen+
|
|
chainCodeSerializationLen+
|
|
keySerializationLen])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else {
|
|
extKey.publicKey, err = secp256k1.DeserializeECDSAPubKey(serialized[versionSerializationLen+
|
|
depthSerializationLen+
|
|
fingerprintSerializationLen+
|
|
childNumberSerializationLen+
|
|
chainCodeSerializationLen : versionSerializationLen+
|
|
depthSerializationLen+
|
|
fingerprintSerializationLen+
|
|
childNumberSerializationLen+
|
|
chainCodeSerializationLen+
|
|
keySerializationLen])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return extKey, nil
|
|
}
|
|
|
|
func (extKey *ExtendedKey) serialize() ([]byte, error) {
|
|
var serialized [extendedKeySerializationLen]byte
|
|
copy(serialized[:], extKey.Version[:])
|
|
serialized[versionSerializationLen] = extKey.Depth
|
|
copy(serialized[versionSerializationLen+depthSerializationLen:], extKey.ParentFingerprint[:])
|
|
binary.BigEndian.PutUint32(
|
|
serialized[versionSerializationLen+depthSerializationLen+fingerprintSerializationLen:],
|
|
extKey.ChildNumber,
|
|
)
|
|
copy(
|
|
serialized[versionSerializationLen+depthSerializationLen+fingerprintSerializationLen+childNumberSerializationLen:],
|
|
extKey.ChainCode[:],
|
|
)
|
|
|
|
if extKey.IsPrivate() {
|
|
serialized[versionSerializationLen+depthSerializationLen+fingerprintSerializationLen+childNumberSerializationLen+chainCodeSerializationLen] = 0
|
|
copy(
|
|
serialized[versionSerializationLen+
|
|
depthSerializationLen+
|
|
fingerprintSerializationLen+
|
|
childNumberSerializationLen+
|
|
chainCodeSerializationLen+
|
|
1:],
|
|
extKey.privateKey.Serialize()[:],
|
|
)
|
|
} else {
|
|
publicKey, err := extKey.PublicKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
serializedPublicKey, err := publicKey.Serialize()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
copy(
|
|
serialized[versionSerializationLen+depthSerializationLen+fingerprintSerializationLen+childNumberSerializationLen+chainCodeSerializationLen:],
|
|
serializedPublicKey[:],
|
|
)
|
|
}
|
|
|
|
checkSum := doubleSha256(serialized[:len(serialized)-checkSumLen])
|
|
copy(
|
|
serialized[versionSerializationLen+depthSerializationLen+fingerprintSerializationLen+childNumberSerializationLen+chainCodeSerializationLen+keySerializationLen:],
|
|
checkSum,
|
|
)
|
|
return serialized[:], nil
|
|
}
|