Code and Life

Programming, electronics and other cool tech stuff

Supported by

Supported by Picotech

Create and Validate BIP-39 mnemonic with Python

I wanted to gift some bitcoin to a friend, and came up with a fun idea of writing them a poem with words making up an BIP-39 mnemonic word list. Then they can easily type in the 12 words in Electrum and take control of their wallet (and maybe transfer the amount to a wallet I don't have access to :).

BIP-39 Fundamentals

Basic idea of BIP 39 is that there is a wordlist of 2048 words, so each word choice encodes 11 bits (2^11 = 2048) of entropy. With 12 words, you have 12*11=132 bits, enough for 128 bits of of true entropy and a 4 bit checksum. You can read all about it in the BIP-39 itself.

Now only problem is, that the last word is not random, but must match the top 4 bits of SHA256 checksum of the preceding 128 bits. So essentially you can choose the 11 first words, and then try to see which choices of 12th word end up with a valid word list mnemonic.

One could manually type stuff into Electrum word list box, but trying 2048 options sounds pretty frustrating (on average, every 16th try will work). So let's do it in Python!

Validating a BIP-39 word list in Python

First, grab the English wordlist — and yes BIP-39 is not the best way as it depends on the word list, but it is standard enough. Then we read it in with Python:

import hashlib, binascii, sys

nums = {}
wordlist = []
with open('english.txt') as fin:
    i = 0
    for word in fin:
        nums[word.strip()] = i
        wordlist.append(word.strip())
        i += 1

Now we can get the "number" a word means with nums['sun'] or a word matching numbers with wordlist[123]. Validating a 12 word list essentially requires:

  1. Go through the words, transforming a word to 11 bit number.
  2. First words are "high bytes", so bit shift the previous big number left before each word.
  3. Convert 128 bits of the 132-bit number to byte array and calculate its SHA256 checksum
  4. See if the last 4 bits match the top 4 bits of the checksum.

In Python, this becomes:

def isOk(ws):
    N = 0
    for w in ws:
        N = (N<<11) + nums[w]

    nhex = format(N, '033x') # include leading zero if needed

    h = hashlib.sha256(binascii.unhexlify(nhex[:-1])).hexdigest()

    return h[0] == nhex[-1]

Note that nhex stores the 132 bits in hexadecimal string, i.e. 33 letters, we take all but the last (nhex[:-1]) and make it a byte array with binascii.unhexlify and use hashlib.sha256(bs).hexdigest() to retrieve the SHA256. The top 4 bits is essentially the first hex of this checksum, i.e. h[0]. Easy!

Brute forcing the last word of a BIP-39 word list

Once we have this nice function, we can make a simple program to create a word list from command line arguments (sys.argv[1:]) and if we have 11 valid words (ones not found from word list are discarded), loop and print out possible continuations:

words = [w.lower() for w in sys.argv[1:] if w in nums]

print(len(words), 'valid words:', ' '.join(words))

if len(words) == 11:
    for i in range(2048):
        cand = words + [wordlist[i]]
        if isOk(cand): print(' '.join(cand))

Additionally, we can continue with another else if to check a 12 word list for validity:

elif len(words) == 12:
    print('OK' if isOk(words) else 'Invalid', 'wordlist checksum')

Creating BIP-39 poetry

You can now enter your "poem" or another source of creative writing (with 11 words from the wordlist), and the program will print out possible valid endings:

$ python3 bip39.py an absurd accident and access abuse across airport \
    made the actor abondon an alien agent again in an awesome
11 valid words: absurd accident access abuse across airport actor alien agent again awesome
absurd accident access abuse across airport actor alien agent again awesome access
absurd accident access abuse across airport actor alien agent again awesome across
absurd accident access abuse across airport actor alien agent again awesome again
absurd accident access abuse across airport actor alien agent again awesome alley
...

I think I like the "alley" ending best:

An absurd accident and access abuse across airport made the actor abandon an alien agent again in an awesome alley

Now just write it on a card and underline the "actual" keywords. You can then type them in in Electron (you need to select "BIP 39" as the format) to check out the bitcoin addresses derived from the word list and transfer a bitcoin or two (yeah right) into the account. Depending on technical sophistication of the receiver, you may want to hold on to the correct 12 words for a while...