How cryptocurrency wallets store keys vary greatly based on the technology used and whether or not the public key is created from a single or multiple private key. Investigators should understand how keys are created because tracking them can prove very complicated and tedious. Some organizations require complex transactions, which results in the need for a complex wallet design. This section will break down the anatomy of a cryptocurrency wallet and explain how wallets store public and private keys.
There are three primary categories of wallet designs:
- Nondeterministic (or Type-0): In this design, keys are compiled in a list of public/private key pairs. This equates to many keys to manage and a lot of data to back up and secure, as each key is randomly generated on its own accord, and they are not seeded from a common key. This means that any backups of the wallet must store every single private key used as an address…as well as a buffer of 100 or so future keys that may have already been given out as addresses but not yet as received payments.
- Deterministic (or Type-1 or “seeded”): In this design, the private keys are derived from a single “seed” that is based on a random number. A “seed” is a collection of random words or a “mnemonic phrase,” which needs to be put in the right order to restore access to the wallet. This method is significantly better because you only need to store and back up the seed to recover all the generated private keys. This makes the wallet much easier to manage.
- Hierarchical Deterministic (or HD): This design is the most current wallet protocol and was implemented in 2016. With this protocol, a single key can be used to generate an entire tree of key pairs. The single key (or seed) serves as the “root” of the tree. An HD wallet does not need to back up much data, as the private keys to every address it has ever produced can be recalculated given the root key. The root key can then be recalculated by feeding in the “seed.”
The seed is created through a process called a BIP39, a mnemonic phrase named after the Bitcoin Improvement Proposal 39. The seed can be made up of anywhere from 12 to 24 words. To create one, follow these steps:
- Take the seed (random 256-bit sequence).
- SHA256 the seed.
- Add a checksum.
- Divide the result into 11-bit sections.
- Use each 11-bit section to reference the index of a dictionary of 2048 words.
To see an example of this, go to Mnemonic Code Converter or browse to https://iancoleman.io/bip39/. Here, you can generate a new seed with its associated mnemonic words (see Figure 11.6). You can select your own words from the BIP39 Word List or have the generator create a random set of mnemonic words for you. A BIP39 Word List can be found at GitHub BIP39 Word List or by browsing to: https://github.com/bitcoin/bips/blob/master/bip-0039/english.txt.
Figure 11-6: Mnemonic Generator
Once you have generated your mnemonic phrase in the generator, scroll down to the derived address box. There you will notice a list of derived keys that have been automatically generated. It will also include the private key, public key, and address of the currency you chose to generate the phrase (Bitcoin generated keys shown in Figure 11-7).
Figure 11-7: List of key pairs generated from seed
So, as you can see, the derived addresses are comprised of a structured set of private and public keys. This structured set of keys is also known as a “tree.” The master key is structured so that it can generate Child keys and Grandchild keys. The hierarchical tree uses this naming method to identify a specific key within the tree (See Figure 11-8). The keys carrying a number identifier as follows:
- Master Key – km
- Child Keys – km/0, km/1, km/2, and so on
- Grandchild Keys – km/0/0, km/1/1, km/2/2, and so on
Figure 11-8: Hierarchical Key Tree
Currently, Bitcoin Core uses a simplified version of the hierarchical tree known as BIP32. A newer method called BIP44, currently being used by Trezor, has provided more flexibility and information. Most likely, BIP44 will replace BIP32 and become the new industry standard.
With BIP44, the master key is generated through the creation of a 512-bit pseudo-random number. The result is then hashed using the hashing algorithm, HMAC-SHA512. In doing so, a 512-bit output is created, with the 256 bits on the left side being used as the master key and the right 256 bits being used as the chain code for key derivation.
So, to derive a child key, the master private key is hashed with the chain code and desired index. This creates another 512-bit hash, which is then used to create the child’s private key and the child chain code. Any new keys of the same generation (child of the master) can be derived by simply changing the index used, while children of the child key (grandchild keys) can be derived by doing the same process but with the child’s private key and child chain code.
- Hardened Derivation – When a private key is used, along with an index, to derive the private keys associated with a specific generation of keys.
- Non-hardened Derivation – The process of deriving child keys using a parent public key and chain code. The non-hardened derivation is only able to generate public child keys. This becomes useful when a payment address needs to be generated without risking exposure of private keys to a possible attacker.
To clarify, the BIP44 structure looks like this:
In breaking it down further, you can see that:
- m is the master key.
- purpose represents the keys purpose as being BIP44 compliant.
- coin_type derives keys for specific altcoin, Bitcoin being 1, the testnet being 2, litecoin being 3, and so on according to the list of registered coins.
- account defines an index for users to separate funds according to different personal uses, similar to bank accounts, which exemplifies the benefit of the hierarchical system.
- change is based on a binary index and is set to either 0 to generate non-change receiving addresses or 1 to generate change addresses for outgoing transactions.
- address is a number that represents the number receiving address for payments. Numbering starts at 0, so the value 3 would then be the receiving address of 4 on that tree branch.