bip39.py: cleanups
This commit is contained in:
parent
16dff263a9
commit
4496f795d7
3 changed files with 39 additions and 32 deletions
|
|
@ -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'))
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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')),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue