Browse Source

bip39.py: cleanups

The MMGen Project 3 years ago
parent
commit
4496f795d7
3 changed files with 39 additions and 32 deletions
  1. 27 28
      mmgen/bip39.py
  2. 10 2
      mmgen/wallet.py
  3. 2 2
      test/unit_tests_d/ut_bip39.py

+ 27 - 28
mmgen/bip39.py

@@ -23,23 +23,29 @@ bip39.py - Data and routines for BIP39 mnemonic seed phrases
 from hashlib import sha256
 
 from .exception import *
-from .baseconv import *
+from .baseconv import baseconv
 from .util import is_hex_str
 
+def is_bip39_str(s):
+	return bool( bip39.tohex(s.split(), wl_id='bip39') )
+
 # implements a subset of the baseconv API
 class bip39(baseconv):
 
-	mn_base = 2048
-	wl_chksums = { 'bip39': 'f18b9a84' }
+	desc            = { 'bip39': ('BIP39 mnemonic', 'BIP39 mnemonic seed phrase') }
+	wl_chksums      = { 'bip39': 'f18b9a84' }
 	seedlen_map     = { 'bip39': { 16:12, 24:18, 32:24 } }
 	seedlen_map_rev = { 'bip39': { 12:16, 18:24, 24:32 } }
+
+	from collections import namedtuple
+	bc = namedtuple('bip39_constants',['chk_len','mn_len'])
 	#    ENT   CS  MS
 	constants = {
-		'128': (4, 12),
-		'160': (5, 15),
-		'192': (6, 18),
-		'224': (7, 21),
-		'256': (8, 24),
+		128: bc(4, 12),
+		160: bc(5, 15),
+		192: bc(6, 18),
+		224: bc(7, 21),
+		256: bc(8, 24),
 	}
 	from .mn_bip39 import words
 	digits = { 'bip39': words }
@@ -47,15 +53,15 @@ class bip39(baseconv):
 	@classmethod
 	def nwords2seedlen(cls,nwords,in_bytes=False,in_hex=False):
 		for k,v in cls.constants.items():
-			if v[1] == nwords:
-				return int(k)//8 if in_bytes else int(k)//4 if in_hex else int(k)
+			if v.mn_len == nwords:
+				return k//8 if in_bytes else k//4 if in_hex else k
 		raise MnemonicError(f'{nwords!r}: invalid word length for BIP39 mnemonic')
 
 	@classmethod
 	def seedlen2nwords(cls,seed_len,in_bytes=False,in_hex=False):
 		seed_bits = seed_len * 8 if in_bytes else seed_len * 4 if in_hex else seed_len
 		try:
-			return cls.constants[str(seed_bits)][1]
+			return cls.constants[seed_bits].mn_len
 		except:
 			raise ValueError(f'{seed_bits!r}: invalid seed length for BIP39 mnemonic')
 
@@ -71,6 +77,7 @@ class bip39(baseconv):
 	def tohex(cls,words,wl_id,pad=None):
 		assert isinstance(words,(list,tuple)),'words must be list or tuple'
 		assert wl_id == 'bip39',"'wl_id' must be 'bip39'"
+		assert pad == None, f"{pad}: invalid 'pad' argument (must be None)"
 
 		wl = cls.digits[wl_id]
 
@@ -80,23 +87,20 @@ class bip39(baseconv):
 
 		res = ''.join(['{:011b}'.format(wl.index(w)) for w in words])
 
-		for k in cls.constants:
-			if len(words) == cls.constants[k][1]:
-				bitlen = int(k)
+		for k,v in cls.constants.items():
+			if len(words) == v.mn_len:
+				bitlen = k
 				break
 		else:
 			raise MnemonicError(f'{len(words)}: invalid BIP39 seed phrase length')
 
-		if pad != None:
-			assert pad * 4 == bitlen, f'{pad}: invalid pad length'
-
 		seed_bin = res[:bitlen]
 		chk_bin = res[bitlen:]
 
 		seed_hex = '{:0{w}x}'.format(int(seed_bin,2),w=bitlen//4)
 		seed_bytes = bytes.fromhex(seed_hex)
 
-		chk_len = cls.constants[str(bitlen)][0]
+		chk_len = cls.constants[bitlen].chk_len
 		chk_hex_chk = sha256(seed_bytes).hexdigest()
 		chk_bin_chk = '{:0{w}b}'.format(int(chk_hex_chk,16),w=256)[:chk_len]
 
@@ -110,29 +114,24 @@ class bip39(baseconv):
 		assert is_hex_str(seed_hex),'seed data not a hexadecimal string'
 		assert wl_id == 'bip39',"'wl_id' must be 'bip39'"
 		assert tostr == False,"'tostr' must be False for 'bip39'"
+		assert pad == None, f"{pad}: invalid 'pad' argument (must be None)"
 
 		wl = cls.digits[wl_id]
 		seed_bytes = bytes.fromhex(seed_hex)
 		bitlen = len(seed_bytes) * 8
 
-		assert str(bitlen) in cls.constants, f'{bitlen}: invalid seed bit length'
-		chk_len,mn_len = cls.constants[str(bitlen)]
-
-		if pad != None:
-			assert mn_len == pad, f'{pad}: invalid pad length'
+		assert bitlen in cls.constants, f'{bitlen}: invalid seed bit length'
+		c = cls.constants[bitlen]
 
 		chk_hex = sha256(seed_bytes).hexdigest()
 
 		seed_bin = '{:0{w}b}'.format(int(seed_hex,16),w=bitlen)
-		chk_bin = '{:0{w}b}'.format(int(chk_hex,16),w=256)[:chk_len]
+		chk_bin = '{:0{w}b}'.format(int(chk_hex,16),w=256)[:c.chk_len]
 
 		res = seed_bin + chk_bin
 
-		return tuple(wl[int(res[i*11:(i+1)*11],2)] for i in range(mn_len))
+		return tuple(wl[int(res[i*11:(i+1)*11],2)] for i in range(c.mn_len))
 
 	@classmethod
 	def init_mn(cls,mn_id):
 		assert mn_id == 'bip39', "'mn_id' must be 'bip39'"
-
-def is_bip39_str(s):
-	return bool(bip39.tohex(s.split(),wl_id='bip39'))

+ 10 - 2
mmgen/wallet.py

@@ -414,10 +414,12 @@ class Mnemonic(WalletUnenc):
 		return mn_entry(self.wl_id).get_mnemonic_from_user(mn_len)
 
 	@staticmethod
-	def _mn2hex_pad(mn): return len(mn) * 8 // 3
+	def _mn2hex_pad(mn):
+		return len(mn) * 8 // 3
 
 	@staticmethod
-	def _hex2mn_pad(hexnum): return len(hexnum) * 3 // 8
+	def _hex2mn_pad(hexnum):
+		return len(hexnum) * 3 // 8
 
 	def _format(self):
 
@@ -486,6 +488,12 @@ class BIP39Mnemonic(Mnemonic):
 		self.conv_cls = bip39
 		super().__init__(*args,**kwargs)
 
+	@staticmethod
+	def _mn2hex_pad(mn): return None
+
+	@staticmethod
+	def _hex2mn_pad(hexnum): return None
+
 class MMGenSeedFile(WalletUnenc):
 
 	stdin_ok = True

+ 2 - 2
test/unit_tests_d/ut_bip39.py

@@ -128,8 +128,8 @@ class unit_test(object):
 ('mnemonic type',    'AssertionError', 'must be list',     lambda:th('string','bip39')),
 ('id (fromhex)',     'AssertionError', "must be 'bip39'",  lambda:th(good_mn,'foo')),
 ('arg (tostr=True)', 'AssertionError', "'tostr' must be",  lambda:fh(good_seed,'bip39',tostr=True)),
-('pad len (fromhex)','AssertionError', "invalid pad len",  lambda:fh(good_seed,'bip39',pad=23)),
-('pad len (tohex)',  'AssertionError', "invalid pad len",  lambda:th(good_mn,'bip39',pad=23)),
+('pad len (fromhex)','AssertionError', "invalid 'pad' arg",lambda:fh(good_seed,'bip39',pad=23)),
+('pad len (tohex)',  'AssertionError', "invalid 'pad' arg",lambda:th(good_mn,'bip39',pad=23)),
 ('word',             'MnemonicError',  "not in the BIP39", lambda:th(bad_word_mn,'bip39')),
 ('checksum',         'MnemonicError',  "checksum",         lambda:th(bad_chksum_mn,'bip39')),
 ('seed phrase len',  'MnemonicError',  "phrase len",       lambda:th(bad_len_mn,'bip39')),