BIP 32 was the first protocol adopted for seed management in HD (hierarchical deterministic) wallets. HD wallets are available as hardware, mobile and desktop wallets; some of them are free and some are paid for. The most popular are Trezor, KeepKey, Ledger Nano S, Mycelium, Jaxx, Eidoo and Electrum.
These wallets require a single backup that allows the user to fully restore data at any time.
This thanks to the wallet’s ability to route all private tree keys using BIP 32. This is a transfer protocol that allows parent keys to create child keys in a hierarchy.
These wallets can be shared partially or entirely with different systems, choosing whether or not to keep the ability to spend coins.
BIP32, in fact, specifies how to derive key pairs from the main seed in a so-called tree structure and how to build a wallet using this structure.
The second advantage of HD wallets is the possibility to create a sequence of public keys without having access to the corresponding private keys. This allows HD wallets to be used on an unsecured server or with only receiving capacity, with the issuance of a different public key for each transaction.
Child key derivation (CKD) is the function that derives a number of “Child” keys from a “Parent” key. To avoid that these depend exclusively on the “Parent” key, an additional 256 bits of entropy represented by the “Chain Code” are added.
This is used to introduce apparently random data into the process, so that the index is not enough to derive other “child” keys.
The key derivation function is therefore based on a mono-directional hash that combines:
- A private or public “Parent” key (uncompressed ECDSA key)
- A chain code (256 bit)
- An index (32 bits)
This generates the so-called “Extended Keys”. In essence, an extended key is only a basic coded serialization of certain data.
An HD wallet is organized in numbered “Accounts” (default is “0”).
Each one is composed of two key chains: an external one that is used to generate the addresses on which to receive funds, an internal one for other operations.
How are these chains represented?
The keys are then “chained” in a derivation path. A Private – Public key pair will share the same [ depth ] [ parent fingerprint ] and [ key index ] because they will be in the same position within this path.
A derivation method known as hardened derivation has also been introduced in HD wallets that breaks the link between the “Parent” public key and the chain code of the “Child”.
The hardened derivation uses the “Parent” private key and not the public key to generate the child chain code. To distinguish between keys obtained from the classical derivation and those obtained from the hardened derivation, the set of possible values for the [ key index ] is divided into two ranges:
- the values between 0 to 2^31-1 are used for the classical derivation,
- those from 2^31 to 2^32-1 are used for the hardened derivation.
How does CKD work
Let’s imagine that we want to replicate this derivation path.
Let’s decode the main extended private key that we find in “m”.
0488ade4 [ magic ] 00 [ depth ] 00000000 [ parent fingerprint ] 00000000 [ key index ] 47fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae6236141 [ chain code ] 00edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea [ key ] 0a794dec [ checksum ]
The range of the next key pair is identified with 0H indicating a Hardened index. The CKD for hardened index keys expects to derive the “Child” private key using Chain code and “Parent” private key as input to an HMAC-SHA512.
This algorithm requires a Key value (the Chain code of the “Parent”) and a Text value (Key of the “Parent” + index of the child). The first [ key index ] of the Hardened indexes is 80000000. (PS: do not confuse the key of the algorithm with the Key).
We have now obtained Chain Code and Child key. The [ Magic ] will be the same, the [ depth ] is progressive, we have explained the [ key index ]. We now have to derive the [ parent fingerprint ] before we can calculate the final [ checksum ]. The [ parent fingerprint ] corresponds to the HASH 160 of the parent’s public key.
To get the public key “Parent” in “m” we have two ways:
- We decode from base58 the xpub extended key.
- Multiply the private key by the Generator Point (G = 0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798)
We proceed by decoding xpub showing how a pair of keys share the same [ depth ] [ parent fingerprint ] [ key index ] and [ chain code ].
0488b21e [ magic ] 00 [ depth ] 00000000 [ parent fingerprint ] 00000000 [ key index ]: 873dff81c02f525623fd1fe5167eac3a55a049de3d314bb42ee227ffed37d508 [ chain code ] 0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2 [ key ] ab473b21 [ checksum ]
We now calculate the HASH160 of the public key to get the [ parent fingerprint ] of the Child:
HASH160 (0339a36013301597daef41fbe593a02cc513d0b55527ec2df1050e2e8ff49c85c2) = 3442193e1bb70916e914552172cd4e2dbc9df811
We have our “Child” keys. Let’s calculate the Checksum and encode Base58:
0488ade4 (Indicates extended private keys) 01 3442193e 80000000 47fdacbd0f1097043b78c63c20c34ef4ed9a111d98 0047ad16282c7ae6236141 00edb2e14f9ee77d26dd93b4ecede8d16ed408ce14 9b6cd80b0715a2d911a0afea
0488b21e (Indicates extended public keys) 01 3442193e 80000000 47fdacbd0f1097043b78c63c20c34ef4ed9a111d98 0047ad16282c7ae6236141 035a784662a4a20a65bf6aab9ae98a6c068a81c52e 4b032c0fb5400c706cfccc56
Checksum calculation for the “Child” private key:
SHA256(SHA256 0488ade4013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae623614 100edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea)) =0a794dec e084ccdfcd78d42c3c163dc8b0620eaa265ad0761b3216907195e5e7
Checksum calculation for the public key “Child”:
SHA256(SHA256(0488b21e013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad1 6282c7ae6236141035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56)) =b8b9c580 d5fa9a36a29ad7ce03c33b1eb7fb3fcbc5e8b529d2bdb66832da9584
Let’s encode everything using base58:
base58-ecncode (0488ade4013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae62361 4100edb2e14f9ee77d26dd93b4ecede8d16ed408ce149b6cd80b0715a2d911a0afea0a794dec)
base58-encode (0488b21e013442193e8000000047fdacbd0f1097043b78c63c20c34ef4ed9a111d980047ad16282c7ae62361 41035a784662a4a20a65bf6aab9ae98a6c068a81c52e4b032c0fb5400c706cfccc56 b8b9c580)
Basically this is the Hardened derivation: the parent’s private key and chain code are used to derive the next key in a hardened index.
The next index in the derivation path is non-hardened: Chain m/0H/1 . Unlike hardened derivation, it is possible to derive the Child’s private key (and consequently the corresponding public key) using the Parent’s public key instead of the private key as input to the CKD function. The steps are the same. Here are the results obtained:
0488ade4 02 5c1bd648 00000001 2a7857631386ba23dacac34180dd1983734e444fdb f774041578e9b6adb37c19 003c6cb8d0f6a264c91ea8b5030fadaa8e538b020f 0a387421a12de9319dc93368 b34bc442
0488b21e 02 5c1bd648 00000001 2a7857631386ba23dacac34180dd1983734e444fdb f774041578e9b6adb37c19 03501e454bf00751f24b1b489aa925215d66af2234 e3891c3b21a52bedb3cd711c 6f6e2af7
base58-encode (0488ade4025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c 19003c6cb8d0f6a264c91ea8b5030fadaa8e538b020f0a387421a12de9319dc93368b34bc442)
base58-encode (0488b21e025c1bd648000000012a7857631386ba23dacac34180dd1983734e444fdbf774041578e9b6adb37c 1903501e454bf00751f24b1b489aa925215d66af2234e3891c3b21a52bedb3cd711c6f6e2af7)