die roll wallet: interactive input support
- Create a wallet of any MMGen-supported format by inputting rolls of a die interactively at the keyboard. Testing: $ test/test.py -e input Examples: Create a default MMGen wallet from interactive die rolls: $ mmgen-walletconv -i dieroll Create a BIP39 mnemonic seed phrase from interactive die rolls, outputting to screen without prompting: $ mmgen-walletconv -Sq -i dieroll -o bip39
This commit is contained in:
parent
3931cc7be1
commit
4714ef84d5
4 changed files with 77 additions and 0 deletions
|
|
@ -1031,24 +1031,78 @@ class DieRollSeedFile(SeedSourceUnenc):
|
|||
desc = 'base6d die roll seed data'
|
||||
ext = 'b6d'
|
||||
conv_cls = baseconv
|
||||
wclass = 'dieroll'
|
||||
wl_id = 'b6d'
|
||||
mn_type = 'base6d'
|
||||
choose_seedlen_prompt = 'Choose a seed length: 1) 128 bits, 2) 192 bits, 3) 256 bits: '
|
||||
choose_seedlen_confirm = 'Seed length of {} bits chosen. OK?'
|
||||
user_entropy_prompt = 'Would you like to provide some additional entropy from the keyboard?'
|
||||
interactive_input = False
|
||||
|
||||
def _format(self):
|
||||
d = baseconv.frombytes(self.seed.data,'b6d',pad='seed',tostr=True) + '\n'
|
||||
self.fmt_data = block_format(d,gw=5,cols=5)
|
||||
|
||||
def _deformat(self):
|
||||
|
||||
d = self.fmt_data.translate(dict((ord(ws),None) for ws in '\t\n '))
|
||||
|
||||
# truncate seed to correct length, discarding high bits
|
||||
seed_len = self.conv_cls.seedlen_map_rev['b6d'][len(d)]
|
||||
seed_bytes = baseconv.tobytes(d,'b6d',pad='seed')[-seed_len:]
|
||||
|
||||
if self.interactive_input and opt.usr_randchars:
|
||||
if keypress_confirm(self.user_entropy_prompt):
|
||||
seed_bytes = add_user_random(seed_bytes,'die roll data')
|
||||
self.desc += ' plus user-supplied entropy'
|
||||
|
||||
self.seed = Seed(seed_bytes)
|
||||
self.ssdata.hexseed = seed_bytes.hex()
|
||||
|
||||
check_usr_seed_len(self.seed.bitlen)
|
||||
return True
|
||||
|
||||
def _get_data_from_user(self,desc):
|
||||
|
||||
if not g.stdin_tty:
|
||||
return get_data_from_user(desc)
|
||||
|
||||
seed_bitlens = [n*8 for n in sorted(self.conv_cls.seedlen_map['b6d'])]
|
||||
seed_bitlen = self._choose_seedlen(self.wclass,seed_bitlens,self.mn_type)
|
||||
nDierolls = self.conv_cls.seedlen_map['b6d'][seed_bitlen // 8]
|
||||
|
||||
m = 'For a {sb}-bit seed you must roll the die {nd} times. After each die roll,\n'
|
||||
m += 'enter the result on the keyboard as a digit. If you make an invalid entry,\n'
|
||||
m += "you'll be prompted to re-enter it."
|
||||
|
||||
msg('\n'+m.format(sb=seed_bitlen,nd=nDierolls)+'\n')
|
||||
|
||||
b6d_digits = self.conv_cls.digits['b6d']
|
||||
|
||||
from mmgen.term import get_char,get_char
|
||||
def get_digit(n):
|
||||
p = '\b\b\b \rEnter die roll #{}: '+ CUR_SHOW
|
||||
sleep = 0.3
|
||||
while True:
|
||||
ch = get_char(p.format(n),num_chars=1,sleep=sleep).decode()
|
||||
if ch in b6d_digits:
|
||||
msg_r(CUR_HIDE + ' OK')
|
||||
return ch
|
||||
else:
|
||||
msg_r(CUR_HIDE + '\rInvalid entry ')
|
||||
sleep = 0.7
|
||||
p = '\r' + ' '*25 + CUR_SHOW + p
|
||||
|
||||
dierolls,n = [],1
|
||||
while len(dierolls) < nDierolls:
|
||||
dierolls.append(get_digit(n))
|
||||
n += 1
|
||||
|
||||
msg('Die rolls successfully entered' + CUR_SHOW)
|
||||
self.interactive_input = True
|
||||
|
||||
return ''.join(dierolls)
|
||||
|
||||
class PlainHexSeedFile(SeedSourceUnenc):
|
||||
|
||||
stdin_ok = True
|
||||
|
|
|
|||
|
|
@ -96,6 +96,9 @@ def pp_fmt(d):
|
|||
def pp_msg(d):
|
||||
msg(pp_fmt(d))
|
||||
|
||||
CUR_HIDE = '\033[?25l'
|
||||
CUR_SHOW = '\033[?25h'
|
||||
|
||||
def set_for_type(val,refval,desc,invert_bool=False,src=None):
|
||||
src_str = (''," in '{}'".format(src))[bool(src)]
|
||||
if type(refval) == bool:
|
||||
|
|
|
|||
|
|
@ -173,3 +173,8 @@ def stealth_mnemonic_entry(t,mn,fmt):
|
|||
for j in range(len(w)):
|
||||
t.send(w[j])
|
||||
time.sleep(0.005)
|
||||
|
||||
def user_dieroll_entry(t,data):
|
||||
for s in data:
|
||||
t.expect(r'Enter die roll #.+: ',s,regex=True)
|
||||
time.sleep(0.005)
|
||||
|
|
|
|||
|
|
@ -120,6 +120,8 @@ class TestSuiteInput(TestSuiteBase):
|
|||
('password_entry_echo', (1,"utf8 password entry (echoed)", [])),
|
||||
('mnemonic_entry_mmgen', (1,"stealth mnemonic entry (MMGen native)", [])),
|
||||
('mnemonic_entry_bip39', (1,"stealth mnemonic entry (BIP39)", [])),
|
||||
('dieroll_entry', (1,"dieroll entry (base6d)", [])),
|
||||
('dieroll_entry_usrrand', (1,"dieroll entry (base6d) with added user entropy", [])),
|
||||
)
|
||||
|
||||
def password_entry(self,prompt,cmd_args):
|
||||
|
|
@ -150,12 +152,23 @@ class TestSuiteInput(TestSuiteBase):
|
|||
if wcls.wclass == 'mnemonic':
|
||||
mn = read_from_file(wf).strip().split()
|
||||
mn = ['foo'] + mn[:5] + ['grac','graceful'] + mn[5:]
|
||||
elif wcls.wclass == 'dieroll':
|
||||
mn = list(read_from_file(wf).strip().translate(dict((ord(ws),None) for ws in '\t\n ')))
|
||||
for idx,val in ((5,'x'),(18,'0'),(30,'7'),(44,'9')):
|
||||
mn.insert(idx,val)
|
||||
t = self.spawn('mmgen-walletconv',['-r10','-S','-i',fmt,'-o',out_fmt or fmt])
|
||||
t.expect('{} type: {}'.format(capfirst(wcls.wclass),wcls.mn_type))
|
||||
t.expect(wcls.choose_seedlen_prompt,'1')
|
||||
t.expect('(Y/n): ','y')
|
||||
if wcls.wclass == 'mnemonic':
|
||||
stealth_mnemonic_entry(t,mn,fmt=fmt)
|
||||
elif wcls.wclass == 'dieroll':
|
||||
user_dieroll_entry(t,mn)
|
||||
if usr_rand:
|
||||
t.expect(wcls.user_entropy_prompt,'y')
|
||||
t.usr_rand(10)
|
||||
else:
|
||||
t.expect(wcls.user_entropy_prompt,'n')
|
||||
if not usr_rand:
|
||||
sid_chk = 'FE3C6545'
|
||||
sid = t.expect_getend('Valid {} for Seed ID '.format(wcls.desc))[:8]
|
||||
|
|
@ -166,6 +179,8 @@ class TestSuiteInput(TestSuiteBase):
|
|||
|
||||
def mnemonic_entry_mmgen(self): return self._user_seed_entry('words')
|
||||
def mnemonic_entry_bip39(self): return self._user_seed_entry('bip39')
|
||||
def dieroll_entry(self): return self._user_seed_entry('dieroll')
|
||||
def dieroll_entry_usrrand(self):return self._user_seed_entry('dieroll',usr_rand=True,out_fmt='bip39')
|
||||
|
||||
class TestSuiteTool(TestSuiteMain,TestSuiteBase):
|
||||
"tests for interactive 'mmgen-tool' commands"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue