1import hashlib
2import os.path
3import binascii
4import random
5from bisect import bisect_left
6
7wordlist_english=list(open(os.path.join(os.path.dirname(os.path.realpath(__file__)),'english.txt'),'r'))
8
9def eint_to_bytes(entint,entbits):
10	a=hex(entint)[2:].rstrip('L').zfill(32)
11	print(a)
12	return binascii.unhexlify(a)
13
14def mnemonic_int_to_words(mint,mint_num_words,wordlist=wordlist_english):
15	backwords=[wordlist[(mint >> (11*x)) & 0x7FF].strip() for x in range(mint_num_words)]
16	return backwords[::-1]
17
18def entropy_cs(entbytes):
19	entropy_size=8*len(entbytes)
20	checksum_size=entropy_size//32
21	hd=hashlib.sha256(entbytes).hexdigest()
22	csint=int(hd,16) >> (256-checksum_size)
23	return csint,checksum_size
24
25def entropy_to_words(entbytes,wordlist=wordlist_english):
26	if(len(entbytes) < 4 or len(entbytes) % 4 != 0):
27		raise ValueError("The size of the entropy must be a multiple of 4 bytes (multiple of 32 bits)")
28	entropy_size=8*len(entbytes)
29	csint,checksum_size = entropy_cs(entbytes)
30	entint=int(binascii.hexlify(entbytes),16)
31	mint=(entint << checksum_size) | csint
32	mint_num_words=(entropy_size+checksum_size)//11
33
34	return mnemonic_int_to_words(mint,mint_num_words,wordlist)
35
36def words_bisect(word,wordlist=wordlist_english):
37	lo=bisect_left(wordlist,word)
38	hi=len(wordlist)-bisect_left(wordlist[:lo:-1],word)
39
40	return lo,hi
41
42def words_split(wordstr,wordlist=wordlist_english):
43	def popword(wordstr,wordlist):
44		for fwl in range(1,9):
45			w=wordstr[:fwl].strip()
46			lo,hi=words_bisect(w,wordlist)
47			if(hi-lo == 1):
48				return w,wordstr[fwl:].lstrip()
49			wordlist=wordlist[lo:hi]
50		raise Exception("Wordstr %s not found in list" %(w))
51
52	words=[]
53	tail=wordstr
54	while(len(tail)):
55		head,tail=popword(tail,wordlist)
56		words.append(head)
57	return words
58
59def words_to_mnemonic_int(words,wordlist=wordlist_english):
60	if(instance(words,str)):
61		words=words_split(words,wordlist)
62	return sum([wordlist.index(w) << (11*x) for x,w in enumerate(words[::-1])])
63
64def words_verify(words,wordlist=wordlist_english):
65	if(isinstance(words,str)):
66		words=words_split(words,wordlist)
67
68	mint = words_to_mnemonic_int(words,wordlist)
69	mint_bits=len(words)*11
70	cs_bits=mint_bits//32
71	entropy_bits=mint_bits-cs_bits
72	eint=mint >> cs_bits
73	csint=mint & ((1 << cs_bits)-1)
74	ebytes=_eint_to_bytes(eint,entropy_bits)
75	return csint == entropy_cs(ebytes)
76
77def mnemonic_to_seed(mnemonic_phrase,passphrase=u''):
78	try:
79		from hashlib import pbkdf2_hmac
80		def pbkdf2_hmac_sha256(password,salt,iters=2048):
81			return pbkdf2_hmac(hash_name='sha512',password=password,salt=salt,iterations=iters)
82	except:
83		try:
84			from Crypto.Protocol.KDF import PBKDF2
85			from Crypto.Hash import SHA512,HMAC
86
87			def pbkdf2_hmac_sha256(password,salt,iters=2048):
88				return PBKDF2(password=password,salt=salt,dkLen=64,count=iters,prf=lambda p,s: HMAC.new(p,s,SHA512).digest())
89		except:
90			try:
91
92				from pbkdf2 import PBKDF2
93				import hmac
94				def pbkdf2_hmac_sha256(password,salt,iters=2048):
95					return PBKDF2(password,salt, iterations=iters, macmodule=hmac, digestmodule=hashlib.sha512).read(64)
96			except:
97				raise RuntimeError("No implementation of pbkdf2 was found!")
98
99	return pbkdf2_hmac_sha256(password=mnemonic_phrase,salt='mnemonic'+passphrase)
100
101def words_mine(prefix,entbits,satisfunction,wordlist=wordlist_english,randombits=random.getrandbits):
102	prefix_bits=len(prefix)*11
103	mine_bits=entbits-prefix_bits
104	pint=words_to_mnemonic_int(prefix,wordlist)
105	pint<<=mine_bits
106	dint=randombits(mine_bits)
107	count=0
108	while(not satisfunction(entropy_to_words(eint_to_bytes(pint+dint,entbits)))):
109		dint=randombits(mine_bits)
110		if((count & 0xFFFF) == 0):
111			print("Searched %f percent of the space" % (float(count)/float(1 << mine_bits)))
112
113	return entropy_to_words(eint_to_bytes(pint+dint,entbits))
114
115if __name__=="__main__":
116	import json
117	testvectors=json.load(open('vectors.json','r'))
118	passed=True
119	for v in testvectors['english']:
120		ebytes=binascii.unhexlify(v[0])
121		w=' '.join(entropy_to_words(ebytes))
122		seed=mnemonic_to_seed(w,passphrase='TREZOR')
123		passed = passed and w==v[1]
124		passed = passed and binascii.hexlify(seed)==v[2]
125	print("Tests %s." % ("Passed" if passed else "Failed"))
126
127
128