Never Use a Brain Wallet

May 22, 2019

Among many reasons why people find it hard to use cryptocurrency there’s a simple one – memorising the private key is too hard. So, people invented brain wallet, which turns a string of words into a private key and thus wallet.

It’s genius in that now a user needs only to memorise whatever he or she used to create the wallet. You can turn your name, phone number, DoB, favourite quote, lover’s home address, …, literally anything into a cryptocurrency wallet. However, this also means that if someone else successfully guessed the passphrase you used, they can sweep all the coins you have!

Python brain wallet for Bitcoin

After a little bit of research, I’ve put together a simple brain wallet Python script that turns any input string to a legal Bitcoin private key and its address.

import codecs
import hashlib
import ecdsa


class BrainWallet:

    @staticmethod
    def generate_address_from_passphrase(passphrase):
        private_key = str(hashlib.sha256(
            passphrase.encode('utf-8')).hexdigest())
        address =  BrainWallet.generate_address_from_private_key(private_key)
        return private_key, address

    @staticmethod
    def generate_address_from_private_key(private_key):
        public_key = BrainWallet.__private_to_public(private_key)
        address = BrainWallet.__public_to_address(public_key)
        return address

    @staticmethod
    def __private_to_public(private_key):
        private_key_bytes = codecs.decode(private_key, 'hex')
        # Get ECDSA public key
        key = ecdsa.SigningKey.from_string(
            private_key_bytes, curve=ecdsa.SECP256k1).verifying_key
        key_bytes = key.to_string()
        key_hex = codecs.encode(key_bytes, 'hex')
        # Add bitcoin byte
        bitcoin_byte = b'04'
        public_key = bitcoin_byte + key_hex
        return public_key

    @staticmethod
    def __public_to_address(public_key):
        public_key_bytes = codecs.decode(public_key, 'hex')
        # Run SHA256 for the public key
        sha256_bpk = hashlib.sha256(public_key_bytes)
        sha256_bpk_digest = sha256_bpk.digest()
        # Run ripemd160 for the SHA256
        ripemd160_bpk = hashlib.new('ripemd160')
        ripemd160_bpk.update(sha256_bpk_digest)
        ripemd160_bpk_digest = ripemd160_bpk.digest()
        ripemd160_bpk_hex = codecs.encode(ripemd160_bpk_digest, 'hex')
        # Add network byte
        network_byte = b'00'
        network_bitcoin_public_key = network_byte + ripemd160_bpk_hex
        network_bitcoin_public_key_bytes = codecs.decode(
            network_bitcoin_public_key, 'hex')
        # Double SHA256 to get checksum
        sha256_nbpk = hashlib.sha256(network_bitcoin_public_key_bytes)
        sha256_nbpk_digest = sha256_nbpk.digest()
        sha256_2_nbpk = hashlib.sha256(sha256_nbpk_digest)
        sha256_2_nbpk_digest = sha256_2_nbpk.digest()
        sha256_2_hex = codecs.encode(sha256_2_nbpk_digest, 'hex')
        checksum = sha256_2_hex[:8]
        # Concatenate public key and checksum to get the address
        address_hex = (network_bitcoin_public_key + checksum).decode('utf-8')
        wallet = BrainWallet.base58(address_hex)
        return wallet

    @staticmethod
    def base58(address_hex):
        alphabet = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
        b58_string = ''
        # Get the number of leading zeros and convert hex to decimal
        leading_zeros = len(address_hex) - len(address_hex.lstrip('0'))
        # Convert hex to decimal
        address_int = int(address_hex, 16)
        # Append digits to the start of string
        while address_int > 0:
            digit = address_int % 58
            digit_char = alphabet[digit]
            b58_string = digit_char + b58_string
            address_int //= 58
        # Add '1' for each 2 leading zeros
        ones = leading_zeros // 2
        for one in range(ones):
            b58_string = '1' + b58_string
        return b58_string

Easily “cracking” a wallet

Let me show you some really easy-to-guess passphrases and their associated private keys and addresses. As an example, the code below uses “password” as the input passphrase and derives the private key and address from it.

passphrase = 'password'
wallet = BrainWallet()
private_key, address = wallet.generate_address_from_passphrase(passphrase)
print(f'passphrase: {passphrase}')
print(f'private key: {private_key}')
print(f'address: {address}')

The output is:

passphrase: password
private key: 5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
address: 16ga2uqnF1NqpAuQeeg7sTCAdtDUwDyJav

As at May 22, 2019, this address has 45,014 transactions with a total of 0.3563 BTC (of course the balance is zero)! You can check its current balance at blockchain.com. Also, congratulations, you are now one of the many owners of this address/wallet. So next time you observe some coins transfered to it, you’ll be able to use it as well (though I don’t suggest you to do so)!

Some other “cracked” wallets

I explored a little bit more and it’s surprising to find out how easy it is to crack a wallet this way. Below is a table of some passphrases and their associated keys and addresses.

Passphrase Private Key Address Used
satoshi da2876b3eb31edb4436fa4650673fc6f01f90de2f1793c4ec332b2387b09726f 1ADJqstUMBB5zFquWg19UqZ7Zc6ePCpzLE True
bitcoin 6b88c087247aa2f07ee1c5956b8e1a9f4c7f892a70e324f1bb3d161e05ca107b 1E984zyYbNmeuumzEdqT8VSL8QGJi3byAD True
hello world b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9 1CS8g7nwaxPPprb4vqcTVdLCuCRirsbsMb True
testing cf80cd8aed482d5d1527d7dc72fceff84e6326592848447d2dc0b0e87dfc9a90 1JdDsbYYRSpsTnBVgenruULVeUjt5z6WnR True
god 5723360ef11043a879520412e9ad897e0ebcb99cc820ec363bfecc9d751a1a99 1KxmSmcMTmPvU1qSLYpJLrqnSzBoQ53NXN True
terminator aa802f654e3ae7aaa1b73f8724056a05e2691accea8dd90057916080f84d7e93 18kvt3D6K1CG8MxGP6ke7q6vLU5NGpLZdR True
abc ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad 1NEwmNSC7w9nZeASngHCd43Bc5eC2FmXpn True

And a lot of swear words are used as well, but I’m just going to skip them.

Apart from the single world and short phrases, some people do use famous quotes. As an example, see this one from A Tale of Two Cities:

it was the best of times it was the worst of times

Its corresponding private key is af8da705bfd95621983e5cf4232ac1ca0c79b47122e3defd8a98fa9a4387d985 and its address is 17WenQJaYvqCNumebQU54TsixWtQ1GQ4ND. It has received 1 BTC in total but again the balance is zero, lol.

Concluding remark

Never use a brain wallet. Because if you can think of it, someone else might also be able to come up with same passphrase. But, if you are comfortable or absolutely sure that your passphrase is secret, feel free to use the script above and make yourself a wallet. 😏

comments powered by Disqus