Mnemonics are the private key separated in 12 words which joined together in the very same order produces the private key
That is incorrect. A mnemonic represents an entropy that is passed to a PBKDF2 key-stretching function with 2048 rounds of hashing to generate a 512 bits seed. This seed then acts like a keychain that is used to generate different keys. Check the last section of the answer to see how private keys are generated from seed.
How are mnemonics generated?
As said above, mnemonics are representation of entropy along with a checksum. First step involves making a decision as to how much entropy you consider safe for your operations. Assume, for now that you have decided on 128 bits of entropy. Below are the steps that you will follow to convert this entropy to mnemonic.
- Use some cryptographically secure entropy generator to generate 128 bits of entropy.
- Calculate the
SHA256 of the entropy.
- Append the first
entropy_length/32 bits of the
SHA256 of the entropy at the end of the entropy. For example, in our case we will append the first 4 bits of the
SHA256(entropy) to the entropy since our entropy is 128 bits.
- Each word of the mnemonic represents 11 bits. Hence, if you check the wordlist you will find 2048 unique words. Now, divide the
entropy + checksum into parts of 11 bits each.
- Match this 11 bit fragments to the words in the lookup table in the wordlist. Since we used 128 bits of entropy our checksum was 4 bits. So our entropy along with checksum represented a total of 132 bits. Thus our mnemonic will be 12 words.
If you had used 256 bits of entropy, your checksum would have been (256/32 =) 8 bits. That would represent (264/11) = 24 words.
One thing to note is that any 12/24 words cannot be used as a mnemonic. Some 'portion' of the last word generally contains the checksum of the words chosen and hence has to be calculated. It is also discouraged to generate words directly from thought and use a secure cryptographic function to do so.
Why Ledger Mnemonics have 24 words?
That is a design choice of security. More the number of words higher the entropy. 24 words will provide 256 bits of entropy. It is also important to note that a mnemonic phrase cannot be used back and forth between different number of words. For example you cannot convert a 24 word representation to 12 words and vice versa.
How those words are converted to a private key?
The mnemonic is passed to key-stretching function PBKDF2 with 2048 rounds of hashing. The PBKDF2 function also has the ability to take a 'salt' that can be an optional passphrase. This passphrase provides an additional layer of security and prevents brute-force attack with look-up tables. The output of this function is a 512 bit seed.
This seed is then passed to
HMAC-SHA512 with key "Bitcoin seed". The resulting hash is used to create the master private key (m) and master chain code (c). The left 256 bits of that resulting hash represents
m while the right 256 bits represents
c. The master private key
m is then used to generate master public key
M = m*G).
From here a number of derivation paths existing for different wallets. The most common one is a hardened derivation method as specified in BIP 44. Essentially, hardened keys use the parent private key in the hash function to generate child private key, while non-hardened uses parent public key in the hash function. This improves the security in the generation of child keys. In the below derivation, k and K represents private key and the associated public key respectively.
We would first need to show that we have used BIP 44 derivation path. That can be done with an index number and generate a private key one level deeper from the master private key. The child private key one level deeper is generated by:
kchild = kpar + hash(kpar, cpar, i) where
i is the index number. For hardened derivation of BIP 44,
i will be
0x80000044 (we use the latter 231 half of the index number for hardened derivation). This result will give us a 512 bit number. The left 256 bits will represent the child private key and the right 256 bits will represent the child chain code.
The next level represents the coin. For Bitcoin, that is
0x80000000 in hardened derivation. You then calculate the child private key and child chain code one level deeper using the formula above.
The next level represents account. You can use multiple accounts to represent different functions and help manage your funds better. You can use the above logic to generate the account private key and chain code. Again, this is hardened derivation so the first account will have index number as
From here onward we do not use the hardened derivation. The next level represents receiving address vs change. This allows you to have different bunch for receiving private keys and different key bunch for change private keys. The function we will use to generate the child private from parent will be:
kchild = kpar + hash(Kpar, cpar, i). Now
i will be
0x00000000 for receiving and
0x00000001 for change. Also note, now we have public key in the hash function rather than private key which shows this is not hardened derivation.
Now, at the next level we use these receiving and change key bunch to generate individual private keys. Use the above generate private keys and chain code and pass them to the above mentioned function
kchild = kpar + hash(Kpar, cpar, i) to generate individual keys. Every increment of
i will give you a different private key.
Now use these private keys to generate bitcoin addresses.