whitespace: wallet

This commit is contained in:
The MMGen Project 2024-10-18 10:32:11 +00:00
commit 7fdaba0f78
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
15 changed files with 239 additions and 227 deletions

View file

@ -15,31 +15,42 @@ wallet.__init__: wallet class initializer
import importlib
from collections import namedtuple
from ..util import die,get_extension
from ..util import die, get_extension
from ..objmethods import MMGenObject
from ..seed import Seed
_wd = namedtuple('wallet_data', ['type','name','ext','base_type','enc','fmt_codes'])
_wd = namedtuple('wallet_data', ['type', 'name', 'ext', 'base_type', 'enc', 'fmt_codes'])
wallet_data = {
'bip39': _wd('bip39', 'BIP39Mnemonic', 'bip39', 'mnemonic', False,('bip39',)),
'brain': _wd('brain', 'Brainwallet', 'mmbrain',None, True, ('mmbrain','brainwallet','brain','bw')),
'dieroll': _wd('dieroll', 'DieRollWallet', 'b6d', None, False,('b6d','die','dieroll')),
'incog': _wd('incog', 'IncogWallet', 'mmincog','incog_base',True, ('mmincog','incog','icg','i')),
'incog_hex': _wd('incog_hex', 'IncogWalletHex', 'mmincox','incog_base',True, ('mmincox','incox','incog_hex','ix','xi')),
'incog_hidden':_wd('incog_hidden','IncogWalletHidden',None, 'incog_base',True, ('incog_hidden','hincog','ih','hi')),
'mmgen': _wd('mmgen', 'MMGenWallet', 'mmdat', None, True, ('wallet','w')),
'mmhex': _wd('mmhex', 'MMGenHexSeedFile', 'mmhex', None, False,('seedhex','hexseed','mmhex')),
'plainhex': _wd('plainhex', 'PlainHexSeedFile', 'hex', None, False,('hex','rawhex','plainhex')),
'seed': _wd('seed', 'MMGenSeedFile', 'mmseed', None, False,('mmseed','seed','s')),
'words': _wd('words', 'MMGenMnemonic', 'mmwords','mnemonic', False,('mmwords','words','mnemonic','mn','m')),
'bip39':
_wd('bip39', 'BIP39Mnemonic', 'bip39', 'mnemonic', False, ('bip39',)),
'brain':
_wd('brain', 'Brainwallet', 'mmbrain', None, True, ('mmbrain','brainwallet','brain','bw')),
'dieroll':
_wd('dieroll', 'DieRollWallet', 'b6d', None, False, ('b6d','die','dieroll')),
'incog':
_wd('incog', 'IncogWallet', 'mmincog', 'incog_base', True, ('mmincog','incog','icg','i')),
'incog_hex':
_wd('incog_hex', 'IncogWalletHex', 'mmincox', 'incog_base', True, ('mmincox','incox','incog_hex','ix','xi')),
'incog_hidden':
_wd('incog_hidden', 'IncogWalletHidden', None, 'incog_base', True, ('incog_hidden','hincog','ih','hi')),
'mmgen':
_wd('mmgen', 'MMGenWallet', 'mmdat', None, True, ('wallet','w')),
'mmhex':
_wd('mmhex', 'MMGenHexSeedFile', 'mmhex', None, False, ('seedhex','hexseed','mmhex')),
'plainhex':
_wd('plainhex', 'PlainHexSeedFile', 'hex', None, False, ('hex','rawhex','plainhex')),
'seed':
_wd('seed', 'MMGenSeedFile', 'mmseed', None, False, ('mmseed','seed','s')),
'words':
_wd('words', 'MMGenMnemonic', 'mmwords', 'mnemonic', False, ('mmwords','words','mnemonic','mn','m')),
}
def get_wallet_data(
wtype = None,
fmt_code = None,
ext = None,
die_on_fail = False ):
die_on_fail = False):
if wtype:
return wallet_data[wtype]
@ -52,7 +63,7 @@ def get_wallet_data(
if ext == v.ext:
return v
else:
die(4,'no argument supplied!')
die(4, 'no argument supplied!')
if die_on_fail:
if fmt_code:
@ -64,17 +75,17 @@ def get_wallet_cls(
wtype = None,
fmt_code = None,
ext = None,
die_on_fail = False ):
die_on_fail = False):
return getattr(
importlib.import_module( 'mmgen.wallet.{}'.format(
importlib.import_module('mmgen.wallet.{}'.format(
wtype or
get_wallet_data(
fmt_code = fmt_code,
ext = ext,
die_on_fail = die_on_fail ).type
die_on_fail = die_on_fail).type
)),
'wallet' )
'wallet')
def get_wallet_extensions(key):
return {
@ -89,14 +100,14 @@ def format_fmt_codes():
','.join(v.fmt_codes)
) for v in wallet_data.values()]
w = max(len(i[0]) for i in d)
ret = [f'{a:<{w}} {b:<9} {c}' for a,b,c in [
('Format','FileExt','Valid codes'),
('------','-------','-----------')
] + sorted(d) ]
ret = [f'{a:<{w}} {b:<9} {c}' for a, b, c in [
('Format', 'FileExt', 'Valid codes'),
('------', '-------', '-----------')
] + sorted(d)]
return '\n'.join(ret) + '\n'
def _get_me(modname):
return MMGenObject.__new__( getattr( importlib.import_module(f'mmgen.wallet.{modname}'), 'wallet' ) )
return MMGenObject.__new__(getattr(importlib.import_module(f'mmgen.wallet.{modname}'), 'wallet'))
def Wallet(
cfg,
@ -108,42 +119,42 @@ def Wallet(
in_data = None,
ignore_in_fmt = False,
in_fmt = None,
passwd_file = None ):
passwd_file = None):
in_fmt = in_fmt or cfg.in_fmt
ss_out = (
get_wallet_data(
fmt_code = cfg.out_fmt,
die_on_fail = True ).type
if cfg.out_fmt else None )
die_on_fail = True).type
if cfg.out_fmt else None)
if seed or seed_bin:
me = _get_me( ss_out or 'mmgen' ) # default to native wallet format
me.seed = seed or Seed( cfg, seed_bin=seed_bin )
me = _get_me(ss_out or 'mmgen') # default to native wallet format
me.seed = seed or Seed(cfg, seed_bin=seed_bin)
me.op = 'new'
elif ss:
me = _get_me( ss.type if passchg else (ss_out or 'mmgen') )
me = _get_me(ss.type if passchg else (ss_out or 'mmgen'))
me.seed = ss.seed
me.ss_in = ss
me.op = 'pwchg_new' if passchg else 'conv'
elif fn or cfg.hidden_incog_input_params:
if fn:
wd = get_wallet_data(ext=get_extension(fn),die_on_fail=True)
wd = get_wallet_data(ext=get_extension(fn), die_on_fail=True)
if in_fmt and (not ignore_in_fmt) and in_fmt not in wd.fmt_codes:
die(1,f'{in_fmt}: --in-fmt parameter does not match extension of input file')
me = _get_me( wd.type )
die(1, f'{in_fmt}: --in-fmt parameter does not match extension of input file')
me = _get_me(wd.type)
else:
fn = ','.join(cfg.hidden_incog_input_params.split(',')[:-1]) # permit comma in filename
me = _get_me( 'incog_hidden' )
me = _get_me('incog_hidden')
from ..filename import MMGenFile
me.infile = MMGenFile( fn, subclass=type(me) )
me.infile = MMGenFile(fn, subclass=type(me))
me.op = 'pwchg_old' if passchg else 'old'
elif in_fmt:
me = _get_me( get_wallet_data(fmt_code=in_fmt).type )
me = _get_me(get_wallet_data(fmt_code=in_fmt).type)
me.op = 'pwchg_old' if passchg else 'old'
else: # called with no arguments: initialize with random seed
me = _get_me( ss_out or 'mmgen' ) # default to native wallet format
me = _get_me(ss_out or 'mmgen') # default to native wallet format
me.seed = Seed(cfg)
me.op = 'new'
@ -151,9 +162,9 @@ def Wallet(
me.__init__(
in_data = in_data,
passwd_file = passwd_file )
passwd_file = passwd_file)
return me
def check_wallet_extension(fn):
get_wallet_data( ext=get_extension(fn), die_on_fail=True ) # raises exception on failure
get_wallet_data(ext=get_extension(fn), die_on_fail=True) # raises exception on failure

View file

@ -14,20 +14,20 @@ wallet.base: wallet base class
import os
from ..util import msg,die
from ..util import msg, die
from ..color import orange
from ..objmethods import MMGenObject
from . import wallet_data,get_wallet_cls
from . import wallet_data, get_wallet_cls
class WalletMeta(type):
def __init__(cls,name,bases,namespace):
t = cls.__module__.rsplit('.',maxsplit=1)[-1]
def __init__(cls, name, bases, namespace):
t = cls.__module__.rsplit('.', maxsplit=1)[-1]
if t in wallet_data:
for k,v in wallet_data[t]._asdict().items():
setattr(cls,k,v)
for k, v in wallet_data[t]._asdict().items():
setattr(cls, k, v)
class wallet(MMGenObject,metaclass=WalletMeta):
class wallet(MMGenObject, metaclass=WalletMeta):
desc = 'MMGen seed source'
file_mode = 'text'
@ -42,7 +42,7 @@ class wallet(MMGenObject,metaclass=WalletMeta):
def __init__(self,
in_data = None,
passwd_file = None ):
passwd_file = None):
self.passwd_file = None if passwd_file is False else (passwd_file or self.cfg.passwd_file)
self.ssdata = self.WalletData()
@ -50,18 +50,18 @@ class wallet(MMGenObject,metaclass=WalletMeta):
self.in_data = in_data
for c in reversed(self.__class__.__mro__):
if hasattr(c,'_msg'):
if hasattr(c, '_msg'):
self.msg.update(c._msg)
if hasattr(self,'seed'):
if hasattr(self, 'seed'):
self._encrypt()
return
elif hasattr(self,'infile') or self.in_data or not self.cfg.stdin_tty:
elif hasattr(self, 'infile') or self.in_data or not self.cfg.stdin_tty:
self._deformat_once()
self._decrypt_retry()
else:
if not self.stdin_ok:
die(1,f'Reading from standard input not supported for {self.desc} format')
die(1, f'Reading from standard input not supported for {self.desc} format')
self._deformat_retry()
self._decrypt_retry()
@ -69,31 +69,31 @@ class wallet(MMGenObject,metaclass=WalletMeta):
a = self.desc,
b = self.seed.sid.hl(),
c = f' (seed length {self.seed.bitlen})' if self.seed.bitlen != 256 else '',
d = '' if not hasattr(self,'mnemonic') or self.mnemonic.has_chksum else
d = '' if not hasattr(self, 'mnemonic') or self.mnemonic.has_chksum else
orange(' [mnemonic format has no checksum]')
))
def _get_data(self):
if hasattr(self,'infile'):
if hasattr(self, 'infile'):
from ..fileutil import get_data_from_file
self.fmt_data = get_data_from_file(
self.cfg,
self.infile.name,
self.desc,
binary = self.file_mode=='binary' )
binary = self.file_mode=='binary')
elif self.in_data:
self.fmt_data = self.in_data
else:
self.fmt_data = self._get_data_from_user(self.desc)
def _get_data_from_user(self,desc):
def _get_data_from_user(self, desc):
from ..ui import get_data_from_user
return get_data_from_user( self.cfg, desc )
return get_data_from_user(self.cfg, desc)
def _deformat_once(self):
self._get_data()
if not self._deformat():
die(2,'Invalid format for input data')
die(2, 'Invalid format for input data')
def _deformat_retry(self):
while True:
@ -110,7 +110,7 @@ class wallet(MMGenObject,metaclass=WalletMeta):
self._format()
return self.fmt_data
def write_to_file(self,outdir='',desc=''):
def write_to_file(self, outdir='', desc=''):
self._format()
kwargs = {
'desc': desc or self.desc,
@ -121,16 +121,16 @@ class wallet(MMGenObject,metaclass=WalletMeta):
if outdir:
# write_data_to_file(): outfile with absolute path overrides self.cfg.outdir
of = os.path.abspath(os.path.join(outdir,self._filename()))
of = os.path.abspath(os.path.join(outdir, self._filename()))
from ..fileutil import write_data_to_file
write_data_to_file(
self.cfg,
of if outdir else self._filename(),
self.fmt_data,
**kwargs )
**kwargs)
def check_usr_seed_len(self,bitlen=None):
def check_usr_seed_len(self, bitlen=None):
chk = bitlen or self.seed.bitlen
if self.cfg.seed_len and self.cfg.seed_len != chk:
die(1,f'ERROR: requested seed length ({self.cfg.seed_len}) doesn’t match seed length of source ({chk})')
die(1, f'ERROR: requested seed length ({self.cfg.seed_len}) doesn’t match seed length of source ({chk})')

View file

@ -20,7 +20,7 @@ class wallet(wallet):
mn_type = 'BIP39'
wl_id = 'bip39'
def __init__(self,*args,**kwargs):
def __init__(self, *args, **kwargs):
from ..bip39 import bip39
self.conv_cls = bip39
super().__init__(*args,**kwargs)
super().__init__(*args, **kwargs)

View file

@ -26,7 +26,7 @@ class wallet(wallet):
def get_bw_params(self):
# already checked
a = self.cfg.brain_params.split(',')
return int(a[0]),a[1]
return int(a[0]), a[1]
def _deformat(self):
self.brainpasswd = ' '.join(self.fmt_data.split())
@ -35,11 +35,11 @@ class wallet(wallet):
def _decrypt(self):
d = self.ssdata
if self.cfg.brain_params:
bw_seed_len,d.hash_preset = self.get_bw_params()
bw_seed_len, d.hash_preset = self.get_bw_params()
else:
if not self.cfg.seed_len:
self.cfg._util.qmsg(f'Using default seed length of {yellow(str(Seed.dfl_len))} bits\n'
+ 'If this is not what you want, use the --seed-len option' )
+ 'If this is not what you want, use the --seed-len option')
self._get_hash_preset()
bw_seed_len = self.cfg.seed_len or Seed.dfl_len
self.cfg._util.qmsg_r('Hashing brainwallet data. Please wait...')
@ -48,9 +48,9 @@ class wallet(wallet):
self.brainpasswd.encode(),
b'',
d.hash_preset,
buflen = bw_seed_len // 8 )
buflen = bw_seed_len // 8)
self.cfg._util.qmsg('Done')
self.seed = Seed( self.cfg, seed )
self.seed = Seed(self.cfg, seed)
msg(f'Seed ID: {self.seed.sid}')
self.cfg._util.qmsg('Check this value against your records')
return True

View file

@ -13,7 +13,7 @@ wallet.dieroll: dieroll wallet class
"""
import time
from ..util import msg,msg_r,die,fmt,remove_whitespace
from ..util import msg, msg_r, die, fmt, remove_whitespace
from ..util2 import block_format
from ..seed import Seed
from ..baseconv import baseconv
@ -32,8 +32,8 @@ class wallet(wallet):
interactive_input = False
def _format(self):
d = baseconv('b6d').frombytes(self.seed.data,pad='seed',tostr=True) + '\n'
self.fmt_data = block_format(d,gw=5,cols=5)
d = baseconv('b6d').frombytes(self.seed.data, pad='seed', tostr=True) + '\n'
self.fmt_data = block_format(d, gw=5, cols=5)
def _deformat(self):
@ -42,34 +42,34 @@ class wallet(wallet):
rmap = bc.seedlen_map_rev
if not len(d) in rmap:
die( 'SeedLengthError', '{!r}: invalid length for {} (must be one of {})'.format(
die('SeedLengthError', '{!r}: invalid length for {} (must be one of {})'.format(
len(d),
self.desc,
list(rmap) ))
list(rmap)))
# truncate seed to correct length, discarding high bits
seed_len = rmap[len(d)]
seed_bytes = bc.tobytes( d, pad='seed' )[-seed_len:]
seed_bytes = bc.tobytes(d, pad='seed')[-seed_len:]
if self.interactive_input and self.cfg.usr_randchars:
from ..ui import keypress_confirm
if keypress_confirm( self.cfg, self.user_entropy_prompt ):
if keypress_confirm(self.cfg, self.user_entropy_prompt):
from ..crypto import Crypto
seed_bytes = Crypto(self.cfg).add_user_random(
rand_bytes = seed_bytes,
desc = 'gathered from your die rolls' )
desc = 'gathered from your die rolls')
self.desc += ' plus user-supplied entropy'
self.seed = Seed( self.cfg, seed_bytes )
self.seed = Seed(self.cfg, seed_bytes)
self.check_usr_seed_len()
return True
def _get_data_from_user(self,desc):
def _get_data_from_user(self, desc):
if not self.cfg.stdin_tty:
from ..ui import get_data_from_user
return get_data_from_user( self.cfg, desc )
return get_data_from_user(self.cfg, desc)
bc = baseconv('b6d')
@ -90,7 +90,7 @@ class wallet(wallet):
enter the result on the keyboard as a digit. If you make an invalid entry,
you'll be prompted to re-enter it.
"""
msg('\n'+fmt(message.strip()).format(sb=seed_bitlen,nd=nDierolls)+'\n')
msg('\n'+fmt(message.strip()).format(sb=seed_bitlen, nd=nDierolls)+'\n')
CUR_HIDE = '\033[?25l'
CUR_SHOW = '\033[?25h'
@ -104,7 +104,7 @@ class wallet(wallet):
p = prompt_fs
while True:
time.sleep(self.cfg.short_disp_timeout)
ch = get_char(p.format(n),num_bytes=1)
ch = get_char(p.format(n), num_bytes=1)
if ch in bc.digits:
msg_r(CUR_HIDE + ' OK')
return ch
@ -112,7 +112,7 @@ class wallet(wallet):
msg_r(invalid_msg)
p = clear_line + prompt_fs
dierolls,n = [],1
dierolls, n = [], 1
while len(dierolls) < nDierolls:
dierolls.append(get_digit(n))
n += 1

View file

@ -13,36 +13,36 @@ wallet.enc: encrypted wallet base class
"""
from ..cfg import gc
from ..util import msg,make_chksum_8,die
from ..util import msg, make_chksum_8, die
from .base import wallet
class wallet(wallet):
def __init__(self,*args,**kwargs):
def __init__(self, *args, **kwargs):
from ..crypto import Crypto
self.crypto = Crypto(self.cfg)
super().__init__(*args,**kwargs)
super().__init__(*args, **kwargs)
def _decrypt_retry(self):
while True:
if self._decrypt():
break
if self.passwd_file:
die(2,'Passphrase from password file, so exiting')
die(2, 'Passphrase from password file, so exiting')
msg('Trying again...')
def _get_hash_preset_from_user(self,old_preset,add_desc=''):
def _get_hash_preset_from_user(self, old_preset, add_desc=''):
prompt = 'Enter {}hash preset for {}{}{},\nor hit ENTER to {} value ({!r}): '.format(
('old ' if self.op=='pwchg_old' else 'new ' if self.op=='pwchg_new' else ''),
('','new ')[self.op=='new'],
('', 'new ')[self.op=='new'],
self.desc,
('',' '+add_desc)[bool(add_desc)],
('accept the default','reuse the old')[self.op=='pwchg_new'],
old_preset )
return self.crypto.get_hash_preset_from_user( old_preset=old_preset, prompt=prompt )
('', ' '+add_desc)[bool(add_desc)],
('accept the default', 'reuse the old')[self.op=='pwchg_new'],
old_preset)
return self.crypto.get_hash_preset_from_user(old_preset=old_preset, prompt=prompt)
def _get_hash_preset(self,add_desc=''):
if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'hash_preset'):
def _get_hash_preset(self, add_desc=''):
if hasattr(self, 'ss_in') and hasattr(self.ss_in.ssdata, 'hash_preset'):
old_hp = self.ss_in.ssdata.hash_preset
if self.cfg.keep_hash_preset:
hp = old_hp
@ -51,7 +51,7 @@ class wallet(wallet):
hp = self.cfg.hash_preset
self.cfg._util.qmsg(f'Using user-configured hash preset {hp!r}')
else: # Prompt, using old value as default
hp = self._get_hash_preset_from_user( old_preset=old_hp, add_desc=add_desc )
hp = self._get_hash_preset_from_user(old_preset=old_hp, add_desc=add_desc)
if (not self.cfg.keep_hash_preset) and self.op == 'pwchg_new':
self.cfg._util.qmsg('Hash preset {}'.format(
'unchanged' if hp == old_hp else f'changed to {hp!r}'))
@ -61,27 +61,27 @@ class wallet(wallet):
else:
hp = self._get_hash_preset_from_user(
old_preset = gc.dfl_hash_preset,
add_desc = add_desc )
add_desc = add_desc)
self.ssdata.hash_preset = hp
def _get_new_passphrase(self):
return self.crypto.get_new_passphrase(
data_desc = ('new ' if self.op in ('new','conv') else '') + self.desc,
data_desc = ('new ' if self.op in ('new', 'conv') else '') + self.desc,
hash_preset = self.ssdata.hash_preset,
passwd_file = self.passwd_file,
pw_desc = ('new ' if self.op=='pwchg_new' else '') + 'passphrase' )
pw_desc = ('new ' if self.op=='pwchg_new' else '') + 'passphrase')
def _get_passphrase(self,add_desc=''):
def _get_passphrase(self, add_desc=''):
return self.crypto.get_passphrase(
data_desc = self.desc + (f' {add_desc}' if add_desc else ''),
passwd_file = self.passwd_file,
pw_desc = ('old ' if self.op == 'pwchg_old' else '') + 'passphrase' )
pw_desc = ('old ' if self.op == 'pwchg_old' else '') + 'passphrase')
def _get_first_pw_and_hp_and_encrypt_seed(self):
d = self.ssdata
self._get_hash_preset()
if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'passwd'):
if hasattr(self, 'ss_in') and hasattr(self.ss_in.ssdata, 'passwd'):
old_pw = self.ss_in.ssdata.passwd
if self.cfg.keep_passphrase:
d.passwd = old_pw
@ -89,12 +89,12 @@ class wallet(wallet):
else:
d.passwd = self._get_new_passphrase()
if self.op == 'pwchg_new':
self.cfg._util.qmsg('Passphrase {}'.format( 'unchanged' if d.passwd == old_pw else 'changed' ))
self.cfg._util.qmsg('Passphrase {}'.format('unchanged' if d.passwd == old_pw else 'changed'))
else:
d.passwd = self._get_new_passphrase()
from hashlib import sha256
d.salt = sha256( self.crypto.get_random(128) ).digest()[:self.crypto.salt_len]
key = self.crypto.make_key( d.passwd, d.salt, d.hash_preset )
d.salt = sha256(self.crypto.get_random(128)).digest()[:self.crypto.salt_len]
key = self.crypto.make_key(d.passwd, d.salt, d.hash_preset)
d.key_id = make_chksum_8(key)
d.enc_seed = self.crypto.encrypt_seed( self.seed.data, key )
d.enc_seed = self.crypto.encrypt_seed(self.seed.data, key)

View file

@ -13,7 +13,7 @@ wallet.incog_base: incognito wallet base class
"""
from ..seed import Seed
from ..util import msg,make_chksum_8,die
from ..util import msg, make_chksum_8, die
from .enc import wallet
class wallet(wallet):
@ -30,16 +30,16 @@ class wallet(wallet):
'decrypt_params': " {} hash preset"
}
def _make_iv_chksum(self,s):
def _make_iv_chksum(self, s):
from hashlib import sha256
return sha256(s).hexdigest()[:8].upper()
def _get_incog_data_len(self,seed_len):
def _get_incog_data_len(self, seed_len):
return (
self.crypto.aesctr_iv_len
+ self.crypto.salt_len
+ (0 if self.cfg.old_incog_fmt else self.crypto.hincog_chk_len)
+ seed_len//8 )
+ seed_len//8)
def _incog_data_size_chk(self):
# valid sizes: 56, 64, 72
@ -55,42 +55,42 @@ class wallet(wallet):
msg(f'Valid data size for this seed length: {valid_dlen} bytes')
for sl in Seed.lens:
if dlen == self._get_incog_data_len(sl):
die(1,f'Valid seed length for this data size: {sl} bits')
die(1, f'Valid seed length for this data size: {sl} bits')
msg(f'This data size ({dlen} bytes) is invalid for all available seed lengths')
return False
def _encrypt (self):
self._get_first_pw_and_hp_and_encrypt_seed()
if self.cfg.old_incog_fmt:
die(1,'Writing old-format incognito wallets is unsupported')
die(1, 'Writing old-format incognito wallets is unsupported')
d = self.ssdata
crypto = self.crypto
d.iv = crypto.get_random( crypto.aesctr_iv_len )
d.iv = crypto.get_random(crypto.aesctr_iv_len)
d.iv_id = self._make_iv_chksum(d.iv)
msg(f'New Incog Wallet ID: {d.iv_id}')
self.cfg._util.qmsg('Make a record of this value')
self.cfg._util.vmsg('\n ' + self.msg['record_incog_id'].strip()+'\n')
d.salt = crypto.get_random( crypto.salt_len )
d.salt = crypto.get_random(crypto.salt_len)
seed_key = crypto.make_key(
passwd = d.passwd,
salt = d.salt,
hash_preset = d.hash_preset,
desc = 'incog wallet key' )
desc = 'incog wallet key')
from hashlib import sha256
chk = sha256(self.seed.data).digest()[:8]
d.enc_seed = crypto.encrypt_seed(
data = chk + self.seed.data,
key = seed_key )
key = seed_key)
# IV is used BOTH to initialize counter and to salt password!
d.wrapper_key = crypto.make_key(
passwd = d.passwd,
salt = d.iv,
hash_preset = d.hash_preset,
desc = 'incog wrapper key' )
desc = 'incog wrapper key')
d.key_id = make_chksum_8(d.wrapper_key)
self.cfg._util.vmsg(f'Key ID: {d.key_id}')
@ -103,7 +103,7 @@ class wallet(wallet):
data = d.salt + d.enc_seed,
key = d.wrapper_key,
iv = d.iv,
desc = self.desc )
desc = self.desc)
def _filename(self):
s = self.seed
@ -114,7 +114,7 @@ class wallet(wallet):
d.iv_id,
s.bitlen,
d.hash_preset,
self.ext )
self.ext)
def _deformat(self):
@ -131,20 +131,20 @@ class wallet(wallet):
return True
def _verify_seed_newfmt(self,data):
chk,seed = data[:8],data[8:]
def _verify_seed_newfmt(self, data):
chk, seed = data[:8], data[8:]
from hashlib import sha256
if sha256(seed).digest()[:8] == chk:
self.cfg._util.qmsg('Passphrase{} are correct'.format( self.msg['decrypt_params'].format('and') ))
self.cfg._util.qmsg('Passphrase{} are correct'.format(self.msg['decrypt_params'].format('and')))
return seed
else:
msg('Incorrect passphrase{}'.format( self.msg['decrypt_params'].format('or') ))
msg('Incorrect passphrase{}'.format(self.msg['decrypt_params'].format('or')))
return False
def _verify_seed_oldfmt(self,seed):
def _verify_seed_oldfmt(self, seed):
m = f'Seed ID: {make_chksum_8(seed)}. Is the Seed ID correct?'
from ..ui import keypress_confirm
if keypress_confirm( self.cfg, m, True ):
if keypress_confirm(self.cfg, m, True):
return seed
else:
return False
@ -160,13 +160,13 @@ class wallet(wallet):
passwd = d.passwd,
salt = d.iv,
hash_preset = d.hash_preset,
desc = 'wrapper key' )
desc = 'wrapper key')
dd = crypto.decrypt_data(
enc_data = d.enc_incog_data,
key = wrapper_key,
iv = d.iv,
desc = 'incog data' )
desc = 'incog data')
d.salt = dd[0:crypto.salt_len]
d.enc_seed = dd[crypto.salt_len:]
@ -175,21 +175,21 @@ class wallet(wallet):
passwd = d.passwd,
salt = d.salt,
hash_preset = d.hash_preset,
desc = 'main key' )
desc = 'main key')
self.cfg._util.qmsg(f'Key ID: {make_chksum_8(seed_key)}')
verify_seed_func = getattr( self, '_verify_seed_'+ ('oldfmt' if self.cfg.old_incog_fmt else 'newfmt') )
verify_seed_func = getattr(self, '_verify_seed_'+ ('oldfmt' if self.cfg.old_incog_fmt else 'newfmt'))
seed = verify_seed_func(
crypto.decrypt_seed(
enc_seed = d.enc_seed,
key = seed_key,
seed_id = '',
key_id = '' ))
key_id = ''))
if seed:
self.seed = Seed( self.cfg, seed )
self.seed = Seed(self.cfg, seed)
msg(f'Seed ID: {self.seed.sid}')
return True
else:

View file

@ -12,7 +12,7 @@
wallet.incog_hex: hexadecimal incognito wallet class
"""
from ..util2 import pretty_hexdump,decode_pretty_hexdump
from ..util2 import pretty_hexdump, decode_pretty_hexdump
from .incog_base import wallet
class wallet(wallet):

View file

@ -12,10 +12,10 @@
wallet.incog_hidden: hidden incognito wallet class
"""
import sys,os
import sys, os
from ..seed import Seed
from ..util import msg,die,capfirst
from ..util import msg, die, capfirst
from ..util2 import parse_bytespec
from .incog_base import wallet
@ -46,21 +46,21 @@ class wallet(wallet):
'decrypt_params': ', hash preset, offset {} seed length'
}
def _get_hincog_params(self,wtype):
a = getattr(self.cfg,'hidden_incog_'+ wtype +'_params').split(',')
return ','.join(a[:-1]),int(a[-1]) # permit comma in filename
def _get_hincog_params(self, wtype):
a = getattr(self.cfg, 'hidden_incog_'+ wtype +'_params').split(',')
return ','.join(a[:-1]), int(a[-1]) # permit comma in filename
def _check_valid_offset(self,fn,action):
def _check_valid_offset(self, fn, action):
d = self.ssdata
m = ('Input','Destination')[action=='write']
m = ('Input', 'Destination')[action=='write']
if fn.size < d.hincog_offset + d.target_data_len:
die(1,'{a} file {b!r} has length {c}, too short to {d} {e} bytes of data at offset {f}'.format(
die(1, '{a} file {b!r} has length {c}, too short to {d} {e} bytes of data at offset {f}'.format(
a = m,
b = fn.name,
c = fn.size,
d = action,
e = d.target_data_len,
f = d.hincog_offset ))
f = d.hincog_offset))
def _get_data(self):
d = self.ssdata
@ -70,77 +70,77 @@ class wallet(wallet):
# Already sanity-checked:
d.target_data_len = self._get_incog_data_len(self.cfg.seed_len or Seed.dfl_len)
self._check_valid_offset(self.infile,'read')
self._check_valid_offset(self.infile, 'read')
flgs = os.O_RDONLY|os.O_BINARY if sys.platform == 'win32' else os.O_RDONLY
fh = os.open(self.infile.name,flgs)
os.lseek(fh,int(d.hincog_offset),os.SEEK_SET)
self.fmt_data = os.read(fh,d.target_data_len)
fh = os.open(self.infile.name, flgs)
os.lseek(fh, int(d.hincog_offset), os.SEEK_SET)
self.fmt_data = os.read(fh, d.target_data_len)
os.close(fh)
self.cfg._util.qmsg(f'Data read from file {self.infile.name!r} at offset {d.hincog_offset}')
# overrides method in Wallet
def write_to_file(self,**kwargs):
def write_to_file(self, **kwargs):
d = self.ssdata
self._format()
self.cfg._util.compare_or_die(
val1 = d.target_data_len,
desc1 = 'target data length',
val2 = len(self.fmt_data),
desc2 = 'length of formatted ' + self.desc )
desc2 = 'length of formatted ' + self.desc)
k = ('output','input')[self.op=='pwchg_new']
fn,d.hincog_offset = self._get_hincog_params(k)
k = ('output', 'input')[self.op=='pwchg_new']
fn, d.hincog_offset = self._get_hincog_params(k)
if self.cfg.outdir and not os.path.dirname(fn):
fn = os.path.join(self.cfg.outdir,fn)
fn = os.path.join(self.cfg.outdir, fn)
check_offset = True
try:
os.stat(fn)
except:
from ..ui import keypress_confirm,line_input
from ..ui import keypress_confirm, line_input
if keypress_confirm(
self.cfg,
f'Requested file {fn!r} does not exist. Create?',
default_yes = True ):
default_yes = True):
min_fsize = d.target_data_len + d.hincog_offset
msg('\n ' + self.msg['choose_file_size'].strip().format(min_fsize)+'\n')
while True:
fsize = parse_bytespec(line_input( self.cfg, 'Enter file size: ' ))
fsize = parse_bytespec(line_input(self.cfg, 'Enter file size: '))
if fsize >= min_fsize:
break
msg(f'File size must be an integer no less than {min_fsize}')
from ..tool.fileutil import tool_cmd
tool_cmd(self.cfg).rand2file(fn,str(fsize))
tool_cmd(self.cfg).rand2file(fn, str(fsize))
check_offset = False
else:
die(1,'Exiting at user request')
die(1, 'Exiting at user request')
from ..filename import MMGenFile
f = MMGenFile(fn,subclass=type(self),write=True)
f = MMGenFile(fn, subclass=type(self), write=True)
self.cfg._util.dmsg('{} data len {}, offset {}'.format(
capfirst(self.desc),
d.target_data_len,
d.hincog_offset ))
d.hincog_offset))
if check_offset:
self._check_valid_offset(f,'write')
self._check_valid_offset(f, 'write')
if not self.cfg.quiet:
from ..ui import confirm_or_raise
confirm_or_raise(
self.cfg,
message = '',
action = f'alter file {f.name!r}' )
action = f'alter file {f.name!r}')
flgs = os.O_RDWR|os.O_BINARY if sys.platform == 'win32' else os.O_RDWR
fh = os.open(f.name,flgs)
fh = os.open(f.name, flgs)
os.lseek(fh, int(d.hincog_offset), os.SEEK_SET)
os.write(fh, self.fmt_data)
os.close(fh)
msg('{} written to file {!r} at offset {}'.format(
capfirst(self.desc),
f.name,
d.hincog_offset ))
d.hincog_offset))

View file

@ -15,8 +15,8 @@ wallet.mmgen: MMGen native wallet class
import os
from ..seed import Seed
from ..util import msg,make_timestamp,make_chksum_6,split_into_cols,is_chksum_6
from ..obj import MMGenWalletLabel,get_obj
from ..util import msg, make_timestamp, make_chksum_6, split_into_cols, is_chksum_6
from ..obj import MMGenWalletLabel, get_obj
from ..baseconv import baseconv
from .enc import wallet
@ -25,23 +25,23 @@ class wallet(wallet):
desc = 'MMGen wallet'
def __init__(self,*args,**kwargs):
def __init__(self, *args, **kwargs):
if self.cfg.label:
self.label = MMGenWalletLabel(self.cfg.label)
else:
self.label = None
super().__init__(*args,**kwargs)
super().__init__(*args, **kwargs)
# logic identical to _get_hash_preset_from_user()
def _get_label_from_user(self,old_lbl=''):
def _get_label_from_user(self, old_lbl=''):
prompt = 'Enter a wallet label, or hit ENTER {}: '.format(
'to reuse the label {}'.format(old_lbl.hl2(encl='‘’')) if old_lbl else
'for no label' )
'for no label')
from ..ui import line_input
while True:
ret = line_input( self.cfg, prompt )
ret = line_input(self.cfg, prompt)
if ret:
lbl = get_obj(MMGenWalletLabel,s=ret)
lbl = get_obj(MMGenWalletLabel, s=ret)
if lbl:
return lbl
else:
@ -51,18 +51,18 @@ class wallet(wallet):
# logic identical to _get_hash_preset()
def _get_label(self):
if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'label'):
if hasattr(self, 'ss_in') and hasattr(self.ss_in.ssdata, 'label'):
old_lbl = self.ss_in.ssdata.label
if self.cfg.keep_label:
lbl = old_lbl
self.cfg._util.qmsg('Reusing label {} at user request'.format( lbl.hl2(encl='‘’') ))
self.cfg._util.qmsg('Reusing label {} at user request'.format(lbl.hl2(encl='‘’')))
elif self.label:
lbl = self.label
self.cfg._util.qmsg('Using user-configured label {}'.format(lbl.hl2(encl='‘’')))
else: # Prompt, using old value as default
lbl = self._get_label_from_user(old_lbl)
if (not self.cfg.keep_label) and self.op == 'pwchg_new':
self.cfg._util.qmsg('Label {}'.format( 'unchanged' if lbl == old_lbl else f'changed to {lbl!r}' ))
self.cfg._util.qmsg('Label {}'.format('unchanged' if lbl == old_lbl else f'changed to {lbl!r}'))
elif self.label:
lbl = self.label
self.cfg._util.qmsg('Using user-configured label {}'.format(lbl.hl2(encl='‘’')))
@ -74,28 +74,28 @@ class wallet(wallet):
self._get_first_pw_and_hp_and_encrypt_seed()
self._get_label()
d = self.ssdata
d.pw_status = ('NE','E')[len(d.passwd)==0]
d.pw_status = ('NE', 'E')[len(d.passwd)==0]
d.timestamp = make_timestamp()
def _format(self):
d = self.ssdata
s = self.seed
bc = baseconv('b58')
slt_fmt = bc.frombytes(d.salt,pad='seed',tostr=True)
es_fmt = bc.frombytes(d.enc_seed,pad='seed',tostr=True)
slt_fmt = bc.frombytes(d.salt, pad='seed', tostr=True)
es_fmt = bc.frombytes(d.enc_seed, pad='seed', tostr=True)
lines = (
d.label,
'{} {} {} {} {}'.format( s.sid.lower(), d.key_id.lower(), s.bitlen, d.pw_status, d.timestamp ),
'{}: {} {} {}'.format( d.hash_preset, *self.crypto.get_hash_params(d.hash_preset) ),
'{} {}'.format( make_chksum_6(slt_fmt), split_into_cols(4,slt_fmt) ),
'{} {}'.format( make_chksum_6(es_fmt), split_into_cols(4,es_fmt) )
'{} {} {} {} {}'.format(s.sid.lower(), d.key_id.lower(), s.bitlen, d.pw_status, d.timestamp),
'{}: {} {} {}'.format(d.hash_preset, *self.crypto.get_hash_params(d.hash_preset)),
'{} {}'.format(make_chksum_6(slt_fmt), split_into_cols(4, slt_fmt)),
'{} {}'.format(make_chksum_6(es_fmt), split_into_cols(4, es_fmt))
)
chksum = make_chksum_6(' '.join(lines).encode())
self.fmt_data = '\n'.join((chksum,)+lines) + '\n'
def _deformat(self):
def check_master_chksum(lines,desc):
def check_master_chksum(lines, desc):
if len(lines) != 6:
msg(f'Invalid number of lines ({len(lines)}) in {desc} data')
@ -106,24 +106,24 @@ class wallet(wallet):
return False
chk = make_chksum_6(' '.join(lines[1:]))
if not self.cfg._util.compare_chksums(lines[0],'master',chk,'computed',
hdr='For wallet master checksum',verbose=True):
if not self.cfg._util.compare_chksums(lines[0], 'master', chk, 'computed',
hdr='For wallet master checksum', verbose=True):
return False
return True
lines = self.fmt_data.splitlines()
if not check_master_chksum(lines,self.desc):
if not check_master_chksum(lines, self.desc):
return False
d = self.ssdata
d.label = MMGenWalletLabel(lines[1])
d1,d2,d3,d4,d5 = lines[2].split()
d1, d2, d3, d4, d5 = lines[2].split()
d.seed_id = d1.upper()
d.key_id = d2.upper()
self.check_usr_seed_len(int(d3))
d.pw_status,d.timestamp = d4,d5
d.pw_status, d.timestamp = d4, d5
hpdata = lines[3].split()
@ -132,14 +132,14 @@ class wallet(wallet):
if self.cfg.hash_preset and self.cfg.hash_preset != hp:
self.cfg._util.qmsg(f'Warning: ignoring user-requested hash preset {self.cfg.hash_preset!r}')
hash_params = tuple(map(int,hpdata[1:]))
hash_params = tuple(map(int, hpdata[1:]))
if hash_params != self.crypto.get_hash_params(d.hash_preset):
msg(f'Hash parameters {" ".join(hash_params)!r} don’t match hash preset {d.hash_preset!r}')
return False
lmin,_,lmax = sorted(baseconv('b58').seedlen_map_rev) # 22,33,44
for i,key in (4,'salt'),(5,'enc_seed'):
lmin, _, lmax = sorted(baseconv('b58').seedlen_map_rev) # 22, 33, 44
for i, key in (4, 'salt'), (5, 'enc_seed'):
l = lines[i].split(' ')
chk = l.pop(0)
b58_val = ''.join(l)
@ -148,16 +148,20 @@ class wallet(wallet):
msg(f'Invalid format for {key} in {self.desc}: {l}')
return False
if not self.cfg._util.compare_chksums(chk,key,
make_chksum_6(b58_val),'computed checksum',verbose=True):
if not self.cfg._util.compare_chksums(
chk,
key,
make_chksum_6(b58_val),
'computed checksum',
verbose = True):
return False
val = baseconv('b58').tobytes(b58_val,pad='seed')
val = baseconv('b58').tobytes(b58_val, pad='seed')
if val is False:
msg(f'Invalid base 58 number: {b58_val}')
return False
setattr(d,key,val)
setattr(d, key, val)
return True
@ -165,11 +169,11 @@ class wallet(wallet):
d = self.ssdata
# Needed for multiple transactions with {}-txsign
d.passwd = self._get_passphrase(
add_desc = os.path.basename(self.infile.name) if self.cfg.quiet else '' )
key = self.crypto.make_key( d.passwd, d.salt, d.hash_preset )
ret = self.crypto.decrypt_seed( d.enc_seed, key, d.seed_id, d.key_id )
add_desc = os.path.basename(self.infile.name) if self.cfg.quiet else '')
key = self.crypto.make_key(d.passwd, d.salt, d.hash_preset)
ret = self.crypto.decrypt_seed(d.enc_seed, key, d.seed_id, d.key_id)
if ret:
self.seed = Seed( self.cfg, ret )
self.seed = Seed(self.cfg, ret)
return True
else:
return False
@ -182,4 +186,4 @@ class wallet(wallet):
d.key_id,
s.bitlen,
d.hash_preset,
self.ext )
self.ext)

View file

@ -12,9 +12,9 @@
wallet.mmhex: MMGen hexadecimal file wallet class
"""
from ..util import make_chksum_6,split_into_cols
from ..util import make_chksum_6, split_into_cols
from ..seed import Seed
from ..util import msg,is_chksum_6,is_hex_str
from ..util import msg, is_chksum_6, is_hex_str
from .unenc import wallet
class wallet(wallet):
@ -25,14 +25,14 @@ class wallet(wallet):
def _format(self):
h = self.seed.data.hex()
self.ssdata.chksum = make_chksum_6(h)
self.fmt_data = f'{self.ssdata.chksum} {split_into_cols(4,h)}\n'
self.fmt_data = f'{self.ssdata.chksum} {split_into_cols(4, h)}\n'
def _deformat(self):
desc = self.desc
d = self.fmt_data.split()
try:
d[1]
chk,hstr = d[0],''.join(d[1:])
chk, hstr = d[0], ''.join(d[1:])
except:
msg(f'{self.fmt_data.strip()!r}: invalid {desc}')
return False
@ -51,10 +51,10 @@ class wallet(wallet):
self.cfg._util.vmsg_r(f'Validating {desc} checksum...')
if not self.cfg._util.compare_chksums(chk,'file',make_chksum_6(hstr),'computed',verbose=True):
if not self.cfg._util.compare_chksums(chk, 'file', make_chksum_6(hstr), 'computed', verbose=True):
return False
self.seed = Seed( self.cfg, bytes.fromhex(hstr) )
self.seed = Seed(self.cfg, bytes.fromhex(hstr))
self.ssdata.chksum = chk
self.check_usr_seed_len()

View file

@ -28,11 +28,11 @@ class wallet(wallet):
def mn_lens(self):
return sorted(self.conv_cls(self.wl_id).seedlen_map_rev)
def _get_data_from_user(self,desc):
def _get_data_from_user(self, desc):
if not self.cfg.stdin_tty:
from ..ui import get_data_from_user
return get_data_from_user( self.cfg, desc )
return get_data_from_user(self.cfg, desc)
self._print_seed_type()
@ -70,10 +70,10 @@ class wallet(wallet):
if len(mn) not in self.mn_lens:
msg('Invalid mnemonic ({} words). Valid numbers of words: {}'.format(
len(mn),
', '.join(map(str,self.mn_lens)) ))
', '.join(map(str, self.mn_lens))))
return False
for n,w in enumerate(mn,1):
for n, w in enumerate(mn, 1):
if w not in bc.digits:
msg(f'Invalid mnemonic: word #{n} is not in the {self.wl_id.upper()} wordlist')
return False
@ -91,7 +91,7 @@ class wallet(wallet):
val2 = ' '.join(mn),
desc1 = 'recomputed mnemonic',
desc2 = 'original mnemonic',
e = 'Internal error' )
e = 'Internal error')
self.seed = Seed(self.cfg, seed)
self.ssdata.mnemonic = mn

View file

@ -12,7 +12,7 @@
wallet.plainhex: plain hexadecimal wallet class
"""
from ..util import msg,is_hex_str_lc
from ..util import msg, is_hex_str_lc
from ..seed import Seed
from .unenc import wallet
@ -36,7 +36,7 @@ class wallet(wallet):
msg(f'Invalid data length ({len(d)}) in {desc}')
return False
self.seed = Seed( self.cfg, bytes.fromhex(d) )
self.seed = Seed(self.cfg, bytes.fromhex(d))
self.check_usr_seed_len()

View file

@ -12,8 +12,8 @@
wallet.seed: seed file wallet class
"""
from ..util import msg,make_chksum_6,split_into_cols,is_chksum_6
from ..baseconv import baseconv,is_b58_str
from ..util import msg, make_chksum_6, split_into_cols, is_chksum_6
from ..baseconv import baseconv, is_b58_str
from ..seed import Seed
from .unenc import wallet
@ -23,12 +23,12 @@ class wallet(wallet):
desc = 'seed data'
def _format(self):
b58seed = baseconv('b58').frombytes(self.seed.data,pad='seed',tostr=True)
b58seed = baseconv('b58').frombytes(self.seed.data, pad='seed', tostr=True)
self.ssdata.chksum = make_chksum_6(b58seed)
self.ssdata.b58seed = b58seed
self.fmt_data = '{} {}\n'.format(
self.ssdata.chksum,
split_into_cols(4,b58seed) )
split_into_cols(4, b58seed))
def _deformat(self):
desc = self.desc
@ -38,7 +38,7 @@ class wallet(wallet):
msg(f'Invalid data length ({len(ld)}) in {desc}')
return False
a,b = ld[0],''.join(ld[1:])
a, b = ld[0], ''.join(ld[1:])
if not is_chksum_6(a):
msg(f'{a!r}: invalid checksum format in {desc}')
@ -50,16 +50,16 @@ class wallet(wallet):
self.cfg._util.vmsg_r(f'Validating {desc} checksum...')
if not self.cfg._util.compare_chksums(a,'file',make_chksum_6(b),'computed',verbose=True):
if not self.cfg._util.compare_chksums(a, 'file', make_chksum_6(b), 'computed', verbose=True):
return False
ret = baseconv('b58').tobytes(b,pad='seed')
ret = baseconv('b58').tobytes(b, pad='seed')
if ret is False:
msg(f'Invalid base-58 encoded seed: {b}')
return False
self.seed = Seed( self.cfg, ret )
self.seed = Seed(self.cfg, ret)
self.ssdata.chksum = a
self.ssdata.b58seed = b

View file

@ -12,8 +12,8 @@
wallet.unenc: unencrypted wallet base class
"""
from ..color import blue,yellow
from ..util import msg,msg_r,capfirst,is_int
from ..color import blue, yellow
from ..util import msg, msg_r, capfirst, is_int
from .base import wallet
class wallet(wallet):
@ -26,10 +26,7 @@ class wallet(wallet):
def _filename(self):
s = self.seed
return '{}[{}].{}'.format(
s.fn_stem,
s.bitlen,
self.ext )
return '{}[{}].{}'.format(s.fn_stem, s.bitlen, self.ext)
def _print_seed_type(self):
msg('{} {}'.format(
@ -37,7 +34,7 @@ class wallet(wallet):
yellow(self.mn_type)
))
def _choose_seedlen(self,ok_lens):
def _choose_seedlen(self, ok_lens):
from ..term import get_char
def choose_len():
@ -46,7 +43,7 @@ class wallet(wallet):
r = get_char('\r'+prompt)
if is_int(r) and 1 <= int(r) <= len(ok_lens):
break
msg_r(('\r','\n')[self.cfg.test_suite] + ' '*len(prompt) + '\r')
msg_r(('\r', '\n')[self.cfg.test_suite] + ' '*len(prompt) + '\r')
return ok_lens[int(r)-1]
while True:
@ -57,5 +54,5 @@ class wallet(wallet):
self.cfg,
prompt,
default_yes = True,
no_nl = not self.cfg.test_suite ):
no_nl = not self.cfg.test_suite):
return usr_len