hdkeychain

package module
v3.0.0-...-cf10ea2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 14, 2023 License: ISC Imports: 11 Imported by: 3

README

hdkeychain

Build Status ISC License Doc

Package hdkeychain provides an API for Exchangecoin hierarchical deterministic extended keys (based on BIP0032).

A comprehensive suite of tests is provided to ensure proper functionality.

Feature Overview

  • Full BIP0032 implementation
  • Single type for private and public extended keys
  • Convenient cryptographically secure seed generation
  • Simple creation of master nodes
  • Support for multi-layer derivation
  • Easy serialization and deserialization for both private and public extended keys
  • Support for custom networks by accepting a network parameters interface
  • Allows obtaining the underlying serialized secp256k1 pubkeys and privkeys directly so they can either be used directly or optionally converted to the secp256k1 types which provide powerful tools for working with them to do things like sign transactions and generate payment scripts
  • Uses the highly-optimized secp256k1 package
  • Code examples including:
    • Generating a cryptographically secure random seed and deriving a master node from it
    • Default HD wallet layout as described by BIP0032
    • Audits use case as described by BIP0032
  • Comprehensive test coverage including the BIP0032 test vectors
  • Benchmarks

BIP0032 Conformity

Two different child key derivation functions are provided: the Child function derives extended keys using a modified scheme based on BIP0032, whereas ChildBIP32Std produces keys that strictly conform to the standard. The Child function should be used for Exchangecoin wallet key derivation for legacy reasons.

Installation and Updating

This package is part of the github.com/EXCCoin/exccd/hdkeychain/v3 module. Use the standard go tooling for working with modules to incorporate it.

Examples

License

Package hdkeychain is licensed under the copyfree ISC License.

Documentation

Overview

Package hdkeychain provides an API for Decred hierarchical deterministic extended keys (based on BIP0032).

The ability to implement hierarchical deterministic wallets depends on the ability to create and derive hierarchical deterministic extended keys.

At a high level, this package provides support for those hierarchical deterministic extended keys by providing an ExtendedKey type and supporting functions. Each extended key can either be a private or public extended key which itself is capable of deriving a child extended key.

Determining the Extended Key Type

Whether an extended key is a private or public extended key can be determined with the IsPrivate function.

Transaction Signing Keys and Payment Addresses

In order to create and sign transactions, or provide others with addresses to send funds to, the underlying key and address material must be accessible. This package provides the SerializedPubKey and SerializedPrivKey functions for this purpose. The caller may then create the desired address types.

The Master Node

As previously mentioned, the extended keys are hierarchical meaning they are used to form a tree. The root of that tree is called the master node and this package provides the NewMaster function to create it from a cryptographically random seed. The GenerateSeed function is provided as a convenient way to create a random seed for use with the NewMaster function.

Deriving Children

Once you have created a tree root (or have deserialized an extended key as discussed later), the child extended keys can be derived by using either the Child or ChildBIP32Std function. The difference is described in the following section. These functions support deriving both normal (non-hardened) and hardened child extended keys. In order to derive a hardened extended key, use the HardenedKeyStart constant + the hardened key number as the index to the Child function. This provides the ability to cascade the keys into a tree and hence generate the hierarchical deterministic key chains.

BIP0032 Conformity

The Child function derives extended keys with a modified scheme based on BIP0032, whereas ChildBIP32Std produces keys that strictly conform to the standard. Specifically, the Decred variation strips leading zeros of a private key, causing subsequent child keys to differ from the keys expected by standard BIP0032. The ChildBIP32Std method retains leading zeros, ensuring the child keys expected by BIP0032 are derived. The Child function must be used for Decred wallet key derivation for legacy reasons.

Normal vs Hardened Child Extended Keys

A private extended key can be used to derive both hardened and non-hardened (normal) child private and public extended keys. A public extended key can only be used to derive non-hardened child public extended keys. As enumerated in BIP0032 "knowledge of the extended public key plus any non-hardened private key descending from it is equivalent to knowing the extended private key (and thus every private and public key descending from it). This means that extended public keys must be treated more carefully than regular public keys. It is also the reason for the existence of hardened keys, and why they are used for the account level in the tree. This way, a leak of an account-specific (or below) private key never risks compromising the master or other accounts."

Neutering a Private Extended Key

A private extended key can be converted to a new instance of the corresponding public extended key with the Neuter function. The original extended key is not modified. A public extended key is still capable of deriving non-hardened child public extended keys.

Serializing and Deserializing Extended Keys

Extended keys are serialized and deserialized with the String and NewKeyFromString functions. The serialized key is a Base58-encoded string which looks like the following:

public key:   dpubZCGVaKZBiMo7pMgLaZm1qmchjWenTeVcUdFQkTNsFGFEA6xs4EW8PKiqYqP7HBAitt9Hw16VQkQ1tjsZQSHNWFc6bEK6bLqrbco24FzBTY4
private key:  dprv3kUQDBztdyjKuwnaL3hfKYpT7W6X2huYH5d61YSWFBebSYwEBHAXJkCpQ7rvMAxPzKqxVCGLvBqWvGxXjAyMJsV1XwKkfnQCM9KctC8k8bk

Network

Extended keys are much like normal Decred addresses in that they have version bytes which tie them to a specific network. The network that an extended key is associated with is specified when creating and decoding the key. In the case of decoding, an error will be returned if a given encoded extended key is not for the specified network.

Example (Audits)

This example demonstrates the audits use case in BIP0032.

// The audits use case described in BIP0032 is:
//
// In case an auditor needs full access to the list of incoming and
// outgoing payments, one can share all account public extended keys.
// This will allow the auditor to see all transactions from and to the
// wallet, in all accounts, but not a single secret key.
//
//   * N(m/*)
//   corresponds to the neutered master extended key (also called
//   the master public extended key)

// Ordinarily this would either be read from some encrypted source
// and be decrypted or generated as the NewMaster example shows, but
// for the purposes of this example, the private extended key for the
// master node is being hard coded here.
master := "xprv9xrCN3r9gRJF4pX6no8T6oLEEigiEM1qqzioEp9eN8L9HdXKNLjtsgsPAh" +
	"fVcjZJuAKWNJ2vE8tAZPt2MMnr32B4iht4pnzssqjQbVzsxLx"

// Start by getting an extended key instance for the master node.
// This gives the path:
//   m
net := chaincfg.MainNetParams()
masterKey, err := hdkeychain.NewKeyFromString(master, net)
if err != nil {
	fmt.Println(err)
	return
}

// Neuter the master key to generate a master public extended key.  This
// gives the path:
//   N(m/*)
masterPubKey := masterKey.Neuter()

// Share the master public extended key with the auditor.
fmt.Println("Audit key N(m/*):", masterPubKey)
Output:

Audit key N(m/*): xpub6BqYmZP3WnrYHJbZtpfTTwGxnkXCdojhDDeQ3CZFvTs8ARrTut49RVBs1wMz6SE9D12o6Vh57BqyJmyRPfy7WaD8XQgUnGieUPR4N6yeAts
Example (DefaultWalletLayout)

This example demonstrates the default hierarchical deterministic wallet layout as described in BIP0032.

// The default wallet layout described in BIP0032 is:
//
// Each account is composed of two keypair chains: an internal and an
// external one. The external keychain is used to generate new public
// addresses, while the internal keychain is used for all other
// operations (change addresses, generation addresses, ..., anything
// that doesn't need to be communicated).
//
//   * m/iH/0/k
//     corresponds to the k'th keypair of the external chain of account
//     number i of the HDW derived from master m.
//   * m/iH/1/k
//     corresponds to the k'th keypair of the internal chain of account
//     number i of the HDW derived from master m.

// Ordinarily this would either be read from some encrypted source
// and be decrypted or generated as the NewMaster example shows, but
// for the purposes of this example, the private extended key for the
// master node is being hard coded here.
master := "xprv9xrCN3r9gRJF4pX6no8T6oLEEigiEM1qqzioEp9eN8L9HdXKNLjtsgsPAh" +
	"fVcjZJuAKWNJ2vE8tAZPt2MMnr32B4iht4pnzssqjQbVzsxLx"

// Start by getting an extended key instance for the master node.
// This gives the path:
//   m
net := chaincfg.MainNetParams()
masterKey, err := hdkeychain.NewKeyFromString(master, net)
if err != nil {
	fmt.Println(err)
	return
}

// Derive the extended key for account 0.  This gives the path:
//   m/0H
acct0, err := masterKey.Child(hdkeychain.HardenedKeyStart + 0)
if err != nil {
	fmt.Println(err)
	return
}

// Derive the extended key for the account 0 external chain.  This
// gives the path:
//   m/0H/0
acct0Ext, err := acct0.Child(0)
if err != nil {
	fmt.Println(err)
	return
}

// Derive the extended key for the account 0 internal chain.  This gives
// the path:
//   m/0H/1
acct0Int, err := acct0.Child(1)
if err != nil {
	fmt.Println(err)
	return
}

// At this point, acct0Ext and acct0Int are ready to derive the keys for
// the external and internal wallet chains.

// Derive the 10th extended key for the account 0 external chain.  This
// gives the path:
//   m/0H/0/10
acct0Ext10, err := acct0Ext.Child(10)
if err != nil {
	fmt.Println(err)
	return
}

// Derive the 1st extended key for the account 0 internal chain.  This
// gives the path:
//   m/0H/1/0
acct0Int0, err := acct0Int.Child(0)
if err != nil {
	fmt.Println(err)
	return
}

// Get and show the address associated with the extended keys for the
// main Decred network.
//
// pubKeyHashAddr is a convenience function to convert an extended
// pubkey to a standard pay-to-pubkey-hash address.
pubKeyHashAddr := func(extKey *hdkeychain.ExtendedKey) (string, error) {
	pkHash := stdaddr.Hash160(extKey.SerializedPubKey())
	addr, err := stdaddr.NewAddressPubKeyHashEcdsaSecp256k1V0(pkHash, net)
	if err != nil {
		fmt.Println(err)
		return "", err
	}
	return addr.String(), nil
}
acct0ExtAddr, err := pubKeyHashAddr(acct0Ext10)
if err != nil {
	fmt.Println(err)
	return
}
acct0IntAddr, err := pubKeyHashAddr(acct0Int0)
if err != nil {
	fmt.Println(err)
	return
}
fmt.Println("Account 0 External Address 10:", acct0ExtAddr)
fmt.Println("Account 0 Internal Address 0:", acct0IntAddr)
Output:

Account 0 External Address 10: 22u28fVBoFgJvsc5T6hdsaNoqcewKTF8D1Vh
Account 0 Internal Address 0: 22ts2TztRkW2JLHL9HeZqySGcn2bFhgWWsqm
Example (NewMaster)

This example demonstrates how to generate a cryptographically random seed then use it to create a new master node (extended key).

// Generate a random seed at the recommended length.
seed, err := hdkeychain.GenerateSeed(hdkeychain.RecommendedSeedLen)
if err != nil {
	fmt.Println(err)
	return
}

// Generate a new master node using the seed.
net := chaincfg.MainNetParams()
key, err := hdkeychain.NewMaster(seed, net)
if err != nil {
	fmt.Println(err)
	return
}

// Show that the generated master node extended key is private.
fmt.Println("Private Extended Key?:", key.IsPrivate())
Output:

Private Extended Key?: true

Index

Examples

Constants

View Source
const (
	// RecommendedSeedLen is the recommended length in bytes for a seed
	// to a master node.
	RecommendedSeedLen = 32 // 256 bits

	// HardenedKeyStart is the index at which a hardened key starts.  Each
	// extended key has 2^31 normal child keys and 2^31 hardened child keys.
	// Thus the range for normal child keys is [0, 2^31 - 1] and the range
	// for hardened child keys is [2^31, 2^32 - 1].
	HardenedKeyStart = 0x80000000 // 2^31

	// MinSeedBytes is the minimum number of bytes allowed for a seed to
	// a master node.
	MinSeedBytes = 16 // 128 bits

	// MaxSeedBytes is the maximum number of bytes allowed for a seed to
	// a master node.
	MaxSeedBytes = 64 // 512 bits

)

Variables

View Source
var (
	// ErrDeriveHardFromPublic describes an error in which the caller
	// attempted to derive a hardened extended key from a public key.
	ErrDeriveHardFromPublic = errors.New("cannot derive a hardened key " +
		"from a public key")

	// ErrNotPrivExtKey describes an error in which the caller attempted
	// to extract a private key from a public extended key.
	ErrNotPrivExtKey = errors.New("unable to create private keys from a " +
		"public extended key")

	// ErrInvalidChild describes an error in which the child at a specific
	// index is invalid due to the derived key falling outside of the valid
	// range for secp256k1 private keys.  This error indicates the caller
	// should simply ignore the invalid child extended key at this index and
	// increment to the next index.
	ErrInvalidChild = errors.New("the extended key at this index is invalid")

	// ErrUnusableSeed describes an error in which the provided seed is not
	// usable due to the derived key falling outside of the valid range for
	// secp256k1 private keys.  This error indicates the caller must choose
	// another seed.
	ErrUnusableSeed = errors.New("unusable seed")

	// ErrInvalidSeedLen describes an error in which the provided seed or
	// seed length is not in the allowed range.
	ErrInvalidSeedLen = fmt.Errorf("seed length must be between %d and %d "+
		"bits", MinSeedBytes*8, MaxSeedBytes*8)

	// ErrBadChecksum describes an error in which the checksum encoded with
	// a serialized extended key does not match the calculated value.
	ErrBadChecksum = errors.New("bad extended key checksum")

	// ErrInvalidKeyLen describes an error in which the provided serialized
	// key is not the expected length.
	ErrInvalidKeyLen = errors.New("the provided serialized extended key " +
		"length is invalid")

	// ErrWrongNetwork describes an error in which the provided serialized
	// key is not for the expected network.
	ErrWrongNetwork = errors.New("the provided serialized extended key " +
		"is for the wrong network")
)

Functions

func GenerateSeed

func GenerateSeed(length uint8) ([]byte, error)

GenerateSeed returns a cryptographically secure random seed that can be used as the input for the NewMaster function to generate a new master node.

The length is in bytes and it must be between 16 and 64 (128 to 512 bits). The recommended length is 32 (256 bits) as defined by the RecommendedSeedLen constant.

Types

type ExtendedKey

type ExtendedKey struct {
	// contains filtered or unexported fields
}

ExtendedKey houses all the information needed to support a hierarchical deterministic extended key. See the package overview documentation for more details on how to use extended keys.

func NewKeyFromString

func NewKeyFromString(key string, net NetworkParams) (*ExtendedKey, error)

NewKeyFromString returns a new extended key instance from a base58-encoded extended key which is required to be for the provided network.

func NewMaster

func NewMaster(seed []byte, net NetworkParams) (*ExtendedKey, error)

NewMaster creates a new master node for use in creating a hierarchical deterministic key chain. The seed must be between 128 and 512 bits and should be generated by a cryptographically secure random generation source.

NOTE: There is an extremely small chance (< 1 in 2^127) the provided seed will derive to an unusable secret key. The ErrUnusable error will be returned if this should occur, so the caller must check for it and generate a new seed accordingly.

func (*ExtendedKey) Child

func (k *ExtendedKey) Child(i uint32) (*ExtendedKey, error)

Child returns a derived child extended key at the given index. When this extended key is a private extended key (as determined by the IsPrivate function), a private extended key will be derived. Otherwise, the derived extended key will be also be a public extended key.

When the index is greater to or equal than the HardenedKeyStart constant, the derived extended key will be a hardened extended key. It is only possible to derive a hardened extended key from a private extended key. Consequently, this function will return ErrDeriveHardFromPublic if a hardened child extended key is requested from a public extended key.

A hardened extended key is useful since, as previously mentioned, it requires a parent private extended key to derive. In other words, normal child extended public keys can be derived from a parent public extended key (no knowledge of the parent private key) whereas hardened extended keys may not be.

NOTE: There is an extremely small chance (< 1 in 2^127) the specific child index does not derive to a usable child. The ErrInvalidChild error will be returned if this should occur, and the caller is expected to ignore the invalid child and simply increment to the next index.

NOTE 2: Child keys derived from the returned extended key will follow the modified Decred variation of the BIP32 derivation scheme such that any leading zero bytes of private keys are stripped, resulting in different subsequent child keys. This should be used for legacy compatibility purposes.

func (*ExtendedKey) ChildBIP32Std

func (k *ExtendedKey) ChildBIP32Std(i uint32) (*ExtendedKey, error)

ChildBIP32Std is like Child, except that derived keys will follow BIP32 strictly by retaining leading zeros in the keys, always generating 32-byte keys, and thus different subsequently derived child keys.

func (*ExtendedKey) ChildNum

func (k *ExtendedKey) ChildNum() uint32

ChildNum returns the child number of the extended key.

func (*ExtendedKey) Depth

func (k *ExtendedKey) Depth() uint16

Depth returns the depth of the extended key.

func (*ExtendedKey) IsPrivate

func (k *ExtendedKey) IsPrivate() bool

IsPrivate returns whether or not the extended key is a private extended key.

A private extended key can be used to derive both hardened and non-hardened child private and public extended keys. A public extended key can only be used to derive non-hardened child public extended keys.

func (*ExtendedKey) Neuter

func (k *ExtendedKey) Neuter() *ExtendedKey

Neuter returns a new extended public key from this extended private key. The same extended key will be returned unaltered if it is already an extended public key.

As the name implies, an extended public key does not have access to the private key, so it is not capable of signing transactions or deriving child extended private keys. However, it is capable of deriving further child extended public keys.

func (*ExtendedKey) ParentFingerprint

func (k *ExtendedKey) ParentFingerprint() uint32

ParentFingerprint returns a fingerprint of the parent extended key from which this one was derived.

func (*ExtendedKey) SerializedPrivKey

func (k *ExtendedKey) SerializedPrivKey() ([]byte, error)

SerializedPrivKey converts the extended key to a secp256k1 private key and returns its serialization. The returned bytes must not be modified.

As you might imagine this is only possible if the extended key is a private extended key (as determined by the IsPrivate function). The ErrNotPrivExtKey error will be returned if this function is called on a public extended key.

func (*ExtendedKey) SerializedPubKey

func (k *ExtendedKey) SerializedPubKey() []byte

SerializedPubKey returns the compressed serialization of the secp256k1 public key. The bytes must not be modified.

func (*ExtendedKey) String

func (k *ExtendedKey) String() string

String returns the extended key as a human-readable base58-encoded string.

func (*ExtendedKey) Zero

func (k *ExtendedKey) Zero()

Zero manually clears all fields and bytes in the extended key. This can be used to explicitly clear key material from memory for enhanced security against memory scraping. This function only clears this particular key and not any children that have already been derived.

type NetworkParams

type NetworkParams interface {
	// HDPrivKeyVersion returns the hierarchical deterministic extended private
	// key magic version bytes.
	HDPrivKeyVersion() [4]byte

	// HDPubKeyVersion returns the hierarchical deterministic extended public
	// key magic version bytes.
	HDPubKeyVersion() [4]byte
}

NetworkParams defines an interface that is used throughout the package to access the hierarchical deterministic extended key magic versions that uniquely identify a network.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL