muun-recovery/vendor/github.com/muun/libwallet/hdprivatekey.go

164 lines
4.2 KiB
Go

package libwallet
import (
"crypto/sha256"
"errors"
"fmt"
"strings"
"github.com/muun/libwallet/hdpath"
"github.com/btcsuite/btcutil/hdkeychain"
)
// HDPrivateKey is an HD capable priv key
type HDPrivateKey struct {
key hdkeychain.ExtendedKey
Network *Network
Path string
}
// NewHDPrivateKey builds an HD priv key from a seed for a given network
func NewHDPrivateKey(seed []byte, network *Network) (*HDPrivateKey, error) {
key, err := hdkeychain.NewMaster(seed, network.network)
if err != nil {
return nil, err
}
return &HDPrivateKey{key: *key, Network: network, Path: "m"}, nil
}
// NewHDPrivateKeyFromBytes builds an HD priv key from the compress priv and chain code for a given network
func NewHDPrivateKeyFromBytes(rawKey, chainCode []byte, network *Network) (*HDPrivateKey, error) {
parentFP := []byte{0, 0, 0, 0}
key := hdkeychain.NewExtendedKey(network.network.HDPrivateKeyID[:],
rawKey, chainCode, parentFP, 0, 0, true)
return &HDPrivateKey{key: *key, Network: network, Path: "m"}, nil
}
// NewHDPrivateKeyFromString creates an HD priv key from a base58-encoded string
// If the parsed key is public, it returns an error
func NewHDPrivateKeyFromString(str, path string, network *Network) (*HDPrivateKey, error) {
key, err := hdkeychain.NewKeyFromString(str)
if err != nil {
return nil, err
}
if !key.IsPrivate() {
return nil, errors.New("encoded key was not a private key")
}
return &HDPrivateKey{key: *key, Network: network, Path: path}, nil
}
// PublicKey returns the matching pub key
func (p *HDPrivateKey) PublicKey() *HDPublicKey {
key, err := p.key.Neuter()
if err != nil {
panic("original key was invalid")
}
return &HDPublicKey{key: *key, Network: p.Network, Path: p.Path}
}
// String return the key base58-encoded
func (p *HDPrivateKey) String() string {
return p.key.String()
}
// DerivedAt derives a new child priv key, which may be hardened
// index should be uint32 but for java compat we use int64
func (p *HDPrivateKey) DerivedAt(index int64, hardened bool) (*HDPrivateKey, error) {
var modifier uint32
if hardened {
modifier = hdkeychain.HardenedKeyStart
}
child, err := p.key.Child(uint32(index) | modifier)
if err != nil {
return nil, err
}
parentPath, err := hdpath.Parse(p.Path)
if err != nil {
return nil, err
}
path := parentPath.Child(uint32(index) | modifier)
return &HDPrivateKey{key: *child, Network: p.Network, Path: path.String()}, nil
}
func (p *HDPrivateKey) DeriveTo(path string) (*HDPrivateKey, error) {
if !strings.HasPrefix(path, p.Path) {
return nil, fmt.Errorf("derivation path %v is not prefix of the keys path %v", path, p.Path)
}
firstPath, err := hdpath.Parse(p.Path)
if err != nil {
return nil, fmt.Errorf("couldn't parse derivation path %v: %w", p.Path, err)
}
secondPath, err := hdpath.Parse(path)
if err != nil {
return nil, fmt.Errorf("couldn't parse derivation path %v: %w", path, err)
}
indexes := secondPath.IndexesFrom(firstPath)
derivedKey := p
for depth, index := range indexes {
derivedKey, err = derivedKey.DerivedAt(int64(index.Index), index.Hardened)
if err != nil {
return nil, fmt.Errorf("failed to derive key at path %v on depth %v: %w", path, depth, err)
}
}
// The generated path has no names in it, so replace it
derivedKey.Path = path
return derivedKey, nil
}
// Sign a payload using the backing EC key
func (p *HDPrivateKey) Sign(data []byte) ([]byte, error) {
signingKey, err := p.key.ECPrivKey()
if err != nil {
return nil, err
}
hash := sha256.Sum256(data)
sig, err := signingKey.Sign(hash[:])
if err != nil {
return nil, err
}
return sig.Serialize(), nil
}
func (p *HDPrivateKey) Decrypter() Decrypter {
return &hdPrivKeyDecrypter{p, nil, true}
}
func (p *HDPrivateKey) DecrypterFrom(senderKey *PublicKey) Decrypter {
return &hdPrivKeyDecrypter{p, senderKey, false}
}
func (p *HDPrivateKey) Encrypter() Encrypter {
return &hdPubKeyEncrypter{p.PublicKey(), p}
}
func (p *HDPrivateKey) EncrypterTo(receiver *HDPublicKey) Encrypter {
return &hdPubKeyEncrypter{receiver, p}
}
// What follows is a workaround for https://github.com/golang/go/issues/46893
func SignWithPrivateKey(key *HDPrivateKey, data []byte) ([]byte, error) {
return key.Sign(data)
}