import hashlib import os.path import binascii import random from bisect import bisect_left wordlist_english=list(open(os.path.join(os.path.dirname(os.path.realpath(__file__)),'english.txt'),'r')) def eint_to_bytes(entint,entbits): a=hex(entint)[2:].rstrip('L').zfill(32) print(a) return binascii.unhexlify(a) def mnemonic_int_to_words(mint,mint_num_words,wordlist=wordlist_english): backwords=[wordlist[(mint >> (11*x)) & 0x7FF].strip() for x in range(mint_num_words)] return backwords[::-1] def entropy_cs(entbytes): entropy_size=8*len(entbytes) checksum_size=entropy_size//32 hd=hashlib.sha256(entbytes).hexdigest() csint=int(hd,16) >> (256-checksum_size) return csint,checksum_size def entropy_to_words(entbytes,wordlist=wordlist_english): if(len(entbytes) < 4 or len(entbytes) % 4 != 0): raise ValueError("The size of the entropy must be a multiple of 4 bytes (multiple of 32 bits)") entropy_size=8*len(entbytes) csint,checksum_size = entropy_cs(entbytes) entint=int(binascii.hexlify(entbytes),16) mint=(entint << checksum_size) | csint mint_num_words=(entropy_size+checksum_size)//11 return mnemonic_int_to_words(mint,mint_num_words,wordlist) def words_bisect(word,wordlist=wordlist_english): lo=bisect_left(wordlist,word) hi=len(wordlist)-bisect_left(wordlist[:lo:-1],word) return lo,hi def words_split(wordstr,wordlist=wordlist_english): def popword(wordstr,wordlist): for fwl in range(1,9): w=wordstr[:fwl].strip() lo,hi=words_bisect(w,wordlist) if(hi-lo == 1): return w,wordstr[fwl:].lstrip() wordlist=wordlist[lo:hi] raise Exception("Wordstr %s not found in list" %(w)) words=[] tail=wordstr while(len(tail)): head,tail=popword(tail,wordlist) words.append(head) return words def words_to_mnemonic_int(words,wordlist=wordlist_english): if(isinstance(words,str)): words=words_split(words,wordlist) return sum([wordlist.index(w) << (11*x) for x,w in enumerate(words[::-1])]) def words_verify(words,wordlist=wordlist_english): if(isinstance(words,str)): words=words_split(words,wordlist) mint = words_to_mnemonic_int(words,wordlist) mint_bits=len(words)*11 cs_bits=mint_bits//32 entropy_bits=mint_bits-cs_bits eint=mint >> cs_bits csint=mint & ((1 << cs_bits)-1) ebytes=_eint_to_bytes(eint,entropy_bits) return csint == entropy_cs(ebytes) def mnemonic_to_seed(mnemonic_phrase,passphrase=b''): try: from hashlib import pbkdf2_hmac def pbkdf2_hmac_sha256(password,salt,iters=2048): return pbkdf2_hmac(hash_name='sha512',password=password,salt=salt,iterations=iters) except: try: from Crypto.Protocol.KDF import PBKDF2 from Crypto.Hash import SHA512,HMAC def pbkdf2_hmac_sha256(password,salt,iters=2048): return PBKDF2(password=password,salt=salt,dkLen=64,count=iters,prf=lambda p,s: HMAC.new(p,s,SHA512).digest()) except: try: from pbkdf2 import PBKDF2 import hmac def pbkdf2_hmac_sha256(password,salt,iters=2048): return PBKDF2(password,salt, iterations=iters, macmodule=hmac, digestmodule=hashlib.sha512).read(64) except: raise RuntimeError("No implementation of pbkdf2 was found!") return pbkdf2_hmac_sha256(password=mnemonic_phrase,salt=b'mnemonic'+passphrase) def words_mine(prefix,entbits,satisfunction,wordlist=wordlist_english,randombits=random.getrandbits): prefix_bits=len(prefix)*11 mine_bits=entbits-prefix_bits pint=words_to_mnemonic_int(prefix,wordlist) pint<<=mine_bits dint=randombits(mine_bits) count=0 while(not satisfunction(entropy_to_words(eint_to_bytes(pint+dint,entbits)))): dint=randombits(mine_bits) if((count & 0xFFFF) == 0): print("Searched %f percent of the space" % (float(count)/float(1 << mine_bits))) return entropy_to_words(eint_to_bytes(pint+dint,entbits)) if __name__=="__main__": import json testvectors=json.load(open('vectors.json','r')) passed=True for v in testvectors['english']: ebytes=binascii.unhexlify(v[0]) w=' '.join(entropy_to_words(ebytes)) seed=mnemonic_to_seed(w,passphrase='TREZOR') passed = passed and w==v[1] passed = passed and binascii.hexlify(seed)==v[2] print("Tests %s." % ("Passed" if passed else "Failed"))