Cryptocurrency 911 — How does 12-word seed phrase work?

Andy Jazz
The Dark Side
Published in
7 min readSep 17, 2021

Foreword

Today most crypto wallets use 24-word recovery phrases. Usage of a 264-bit phrase reliably protects your private key from hacking. But in this story, I’ll show you how to implement a 12-word seed phrase, for simplicity.

Basically, a seed phrase is a set of predefined words (found in Bitcoin Improvement Proposal 0039 wordlist, or BIP39 wordlist, for short) that allows you to recover your private key, hence get a control over spending funds. It’s a quite simple and user-friendly feature. I highly recommend you use a seed phrase — no matter what happens to your computer or phone, you will always have a backup. Let’s see how it looks through the eyes of a developer.

Most contemporary wallets (so-called HD wallets) support BIP32 (a.k.a. BIP32-Ed25519 in Cardano) and BIP39, so they have a pre-installed dictionary of 2048 words used for recovery of private key (master private key), or to be more precise, in a range of 1 to 2048¹²… 2048²⁴ possible combinations. In this story, I’m using a 12-word mnemonic phrase, where the number of possible combinations will be 2048¹² or 2¹³², as you can see in the table.

|--------------------|--------------------|--------------------|
| Seed Phrase | Bits | Checksum |
|--------------------|--------------------|--------------------|
| 9-word | 99 (96+3) | 3-bit |
|--------------------|--------------------|--------------------|
| 12-word | 132 (128+4) | 4-bit |
|--------------------|--------------------|--------------------|
| 15-word | 165 (160+5) | 5-bit |
|--------------------|--------------------|--------------------|
| 18-word | 198 (192+6) | 6-bit |
|--------------------|--------------------|--------------------|
| 21-word | 231 (224+7) | 7-bit |
|--------------------|--------------------|--------------------|
| 24-word | 264 (256+8) | 8-bit |
|--------------------|--------------------|--------------------|

In recent years, 9- and 12-word second factor phrases for recovering have become more and more popular.

Random binary number

At first, we need a natural binary number from 0 to 2¹²⁸– 1. To get it, I should use a Random Number Generator (RNG), but I’m too lazy to download it, so I’ll flip a coin 128 times. Laziness is NOT the engine of progress. :)

Phew! It was really hard. Let’s examine what I got. Here’s my 128-bit binary number with extremely low entropy, and that’s why.

00101110100101011010001011010111100011011011010100110101110010110000000101001110001110011001101011010101001110111101111010001011

By the way, I’ve prepared 12-word table for my binaries:

As you see, each word contains 11 cells (i.e. bits), thus 2¹¹ = 2048 variations (from 0 to 2047 inclusive). I used Xcode Playground to calculate it:

Prefix “0b” means binary representation in Swift language

Put the data into a table

Now, for the sake of convenience, I will separate the 11-bit groups from each other using an underscore `_` sign.

00101110100_10101101000_10110101111_00011011011_01010011010_11100101100_00000101001_11000111001_10011010110_10101001110_11110111101_0001011

And after that I will put them into the table:

As a result, the last word is missing 4 bits. Why don’t I use a 132-bit number — you’re asking? Do not hurry! The last 4 bits in the whole 12-word phrase are used as checksum.

At first, let’s convert all 11-bit numbers to decimals (except the last one). However, we must remember that the words in the BIP39 dictionary start at decimal index 0001, but the min binary value for any of the 12 words is 00000000000. So I have to add 1 to each binary value.

Conversion in Xcode Playground is natural. You do not even need any online converter.

Left hand side (LHS) — binary values, right hand side (RHS) — decimal values.

Now copy-paste decimals into red cells.

After that, we can easily find words in the English wordlist by the corresponding index. Please note, there are outdated versions of the dictionary. Make sure you are working with the actual wordlist’s version.

I just have to take the last step — to calculate the index of the twelfth word. To do this, I will run my entire 128-bit number through the SHA-256 hash function. From the resulting 256-bit value, I will borrow the first 4 bits, which I will insert at the end of the mnemonic phrase. This is a checksum.

Binary-to-binary SHA-256 algorithm

My last word is now known.

It’s the word blade.

The order of words in the resulting seed phrase is crucial !

|-----|--------------------|
| # | Word |
|-----|--------------------|
| 1 | company |
|-----|--------------------|
| 2 | public |
|-----|--------------------|
| 3 | remove |
|-----|--------------------|
| 4 | bread |
|-----|--------------------|
| 5 | fashion |
|-----|--------------------|
| 6 | tortoise |
|-----|--------------------|
| 7 | ahead |
|-----|--------------------|
| 8 | shrimp |
|-----|--------------------|
| 9 | onion |
|-----|--------------------|
| 10 | prefer |
|-----|--------------------|
| 11 | waste |
|-----|--------------------|
| 12 | blade |
|-----|--------------------|

Important

  1. If the word order in the mnemonic phrase has changed or you made at least one spelling mistake, the private key will not be recovered — thus, you will not be able to use your wallet.
  2. Keep your seed phrase in a safe and inaccessible place, and do not show it to anyone. Do not store your seed phrase on your phone, on your computer, or on the Internet. Whoever gets access to your seed phrase will get access to your funds in the wallet.

Reconstruction

The reconstruction of a source binary number is simple. Let’s declare Swift’s dictionary (where LHS are Keys, RHS are Values):

let wordList: [String: String] = [ "company":  "00101110100",    
"public": "10101101000",
"remove": "10110101111",
"bread": "00011011011",
"fashion": "01010011010",
"tortoise": "11100101100",
"ahead": "00000101001",
"shrimp": "11000111001",
"onion": "10011010110",
"prefer": "10101001110",
"waste": "11110111101",
"blade": "00010111000"
]

Time to check if it works. The nature of a dictionary in Swift is such that if you feed a dictionary with a non-existent key, it will return nil as a result. In other words, dictionaries in Swift are optional under the hood.

wordList["tortoise"]        // "11100101100"wordList["tortose"]         // nil, because there's a typo...

The simplest way to reconstruct our binary number is to use a strings’ addition. Please, take into consideration, that if you enter a word incorrectly, instead of the corresponding dictionary key (whether it’s a wrong word or just typo), you will receive the default value 00000000000, which will lead to an incorrect reconstruction of the original binary number.

var reco: String = wordList["company",  default: "00000000000"] +
wordList["public", default: "00000000000"] +
wordList["remove", default: "00000000000"] +
wordList["bread", default: "00000000000"] +
wordList["fashion", default: "00000000000"] +
wordList["tortoise", default: "00000000000"] +
wordList["ahead", default: "00000000000"] +
wordList["shrimp", default: "00000000000"] +
wordList["onion", default: "00000000000"] +
wordList["prefer", default: "00000000000"] +
wordList["waste", default: "00000000000"] +
wordList["blade", default: "00000000000"]
print(reco)reco.count // 132

Here’s a value of 132-bit number:

001011101001010110100010110101111000110110110101001101011100101100000001010011100011100110011010110101010011101111011110100010111000

I need to truncate four last characters to get a 128-bit source.

var truncated = String(reco.dropLast(4))print(truncated)truncated.count      // 128

Voila! That’s our original number, isn’t it?

00101110100101011010001011010111100011011011010100110101110010110000000101001110001110011001101011010101001110111101111010001011

Creating binary number using BIP39 wordlist

Of course, you can create a binary number using wordlist, i.e. vice versa. But it’s a terrible idea due to the lowest possible entropy. People are usually prone to simplification — the seed phrase can, for example, contain a list of verbs taken from the BIP39 dictionary in ascending index order. In this case, your private key becomes an easy game for hackers.

abandon (0001),  absent (0006),   absorb (0007),   abuse (0010),
accuse (0014), achieve (0015), acquire (0018), act (0020),
adapt (0025), add (0026), addict (0027), GUESS LAST WORD ?

Reliable protection

For those who are paranoidal about the safety of seed phrases, there is a metal box that will protect your 24-word mnemonic in a case of fire and flood.

ELLIPAL Mnemonic Metal

That’s all for now.

If this post is useful for you, please press the Clap button and hold it.

Happy staking and mining!

--

--