Skip to content

Instantly share code, notes, and snippets.

@kpe
Created May 19, 2021 16:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kpe/c677167da488a93035dc3e9bc3ef7364 to your computer and use it in GitHub Desktop.
Save kpe/c677167da488a93035dc3e9bc3ef7364 to your computer and use it in GitHub Desktop.
bip39-diceware-helper
import os
import tempfile
import random
from typing import Tuple, List
WORDLIST_URL = "https://raw.githubusercontent.com/bitcoin/bips/master/bip-0039/english.txt"
WORDLIST_LOCAL = "/tmp/"
USE_RANDOM = False
#
# https://www.reddit.com/r/Bitcoin/comments/c82urq/how_to_manual_bip39_last_word_calculation_when/
#
# s=s/256
# h=hashlib.sha256(binascii.unhexlify('%064x' % s)).digest().encode('hex')
# int(('%064x' % s)[-1] + h[:2], 16) % 2048
#
def fetch_wordlist():
wordlist_path = os.path.join(tempfile.gettempdir(), WORDLIST_URL.split('/')[-1])
if not os.path.isfile(wordlist_path):
print(f"Wordlist not found locally at: {wordlist_path}")
print(f"fetching wordlist from: {WORDLIST_URL}")
import requests
response = requests.get(WORDLIST_URL)
with open(wordlist_path, "w") as f:
f.write(response.content.decode("utf8"))
else:
print(f"Using local wordlist from: {wordlist_path}")
with open(wordlist_path, "r") as f:
result = f.readlines()
return list(map(str.strip, result))
def input_coin_flip(ndx):
while True:
inp = input(f"{ndx:2d}: (H)ead or (T)ail? ").upper().strip()
if inp in ["H", "T"]:
return inp == "T"
def input_dice(ndx, dndx):
while True:
try:
inp = int(input(f"{ndx:2d}: Dice {dndx} (1-6)? ").strip())
if 0 < inp < 7:
return inp
except ValueError:
pass
def input_coin_flip_random(ndx):
return random.randint(0, 1)
def input_dice_random(ndx) -> int:
return random.randint(1, 6)
if USE_RANDOM:
input_dice = input_dice_random
input_coin_flip = input_coin_flip_random
def diceware_to_word_ndx(coin: int, dices: List[int]):
return coin * 6**4 + sum((dice - 1) * (6**pos) for pos, dice in enumerate(reversed(dices)))
def input_word_entropy(ndx) -> Tuple[int, List[int]]:
while True:
result = input_coin_flip(ndx+1), [input_dice(ndx+1, dndx+1) for dndx in range(4)]
if diceware_to_word_ndx(*result) < 2048:
return result
def input_entropy(word_count=24):
return [input_word_entropy(ndx) for ndx in range(word_count)]
def bip39_from_diceware(wordlist: List[str], entropy: List[Tuple[int, List[int]]]):
word_ndxs = []
for ndx, (coin, dices) in enumerate(entropy):
word_ndx = diceware_to_word_ndx(coin, dices)
word = wordlist[word_ndx]
print("{:2d}: {}{}{}{}{} : {:9s} : {}".format(ndx + 1, 'T' if coin else 'H', *dices, word, word_ndx))
word_ndxs.append(word_ndx)
bip39_ndxs_to_mnemonic(word_ndxs)
def bip39_ndxs_to_mnemonic(wordlist: List[str], word_ndxs: List[int]):
import hashlib
import binascii
all_bits = 0
for ndx, word_ndx in enumerate(word_ndxs):
word = wordlist[word_ndx]
print("{:2d}: {:9s} : {}".format(ndx+1, word, word_ndx))
all_bits = (all_bits << 11) + word_ndx
s = all_bits
s = s//256
h = hashlib.sha256(binascii.unhexlify('%064x' % s)).digest().hex()
word_ndx = int(('%064x' % s)[-1] + h[:2], 16) % 2048
word_ndxs[-1] = word_ndx
word = wordlist[word_ndx]
print("{:2d}: {}{}{}{}{} : {:9s} : {}".format(24, 'x', *['x']*4, word, word_ndx))
print("mnemonic: ", " ".join([wordlist[wndx] for wndx in word_ndxs]))
def stdin_bytes_to_mnemonic(wordlist: List[str], word_count: int):
"""
Reads the entropy from stdin as base 10 byte values (0-255).
cat /dev/urandom | hexdump -v -n 33 -e '/1 "%03u\n"'
"""
import sys
buff = 0
for ndx in range(word_count*11//8):
line = int(sys.stdin.readline().strip())
print(f"{ndx:3d}:byte: [{line}]")
buff = (buff << 8) + line
print(buff)
word_ndxs = []
for ndx in range(word_count):
word_ndx = buff & (2**11 - 1)
buff = buff >> 11
print(f"{ndx:2d}:word ndx: {word_ndx}: {wordlist[word_ndx]}")
word_ndxs.append(word_ndx)
bip39_ndxs_to_mnemonic(wordlist, word_ndxs)
if __name__ == "__main__":
bip39_wordlist = fetch_wordlist()
word_count = 24
import sys
if sys.stdin.isatty():
diceware_entropy = input_entropy(word_count)
bip39_from_diceware(bip39_wordlist, diceware_entropy)
else:
print("reading entropy as a single base-10 byte (0-255) per line from stdin...")
print(r' try:\n\t cat /dev/urandom | hexdump -v -n 33 -e \'/1 "%03u\n"\'')
stdin_bytes_to_mnemonic(bip39_wordlist, word_count)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment