From 3302ac774ffbd45b887cd4c9ce773effaccb8b77 Mon Sep 17 00:00:00 2001 From: philemon Date: Sat, 15 Mar 2014 00:57:33 +0400 Subject: [PATCH] Various bugfixes, better password handling --- MANIFEST | 2 +- mmgen-addrgen | 8 +++++- mmgen-passchg | 10 ++++--- mmgen-txsign | 26 +++++++++++++++--- mmgen-walletgen | 8 ++++-- mmgen/addr.py | 5 +++- mmgen/mnemonic.py | 4 +-- mmgen/utils.py | 69 ++++++++++++++++++++++++++++------------------- setup.py | 2 +- 9 files changed, 91 insertions(+), 43 deletions(-) diff --git a/MANIFEST b/MANIFEST index c231ce0f..f5bba605 100644 --- a/MANIFEST +++ b/MANIFEST @@ -3,6 +3,7 @@ mmgen-addrgen mmgen-addrimport mmgen-keygen mmgen-passchg +mmgen-pywallet mmgen-txcreate mmgen-txsend mmgen-txsign @@ -30,7 +31,6 @@ mmgen/rpc/proxy.py mmgen/rpc/util.py scripts/bitcoind-walletunlock.py scripts/deinstall.sh -scripts/pywallet.py tests/addr.py tests/bitcoin.py tests/mn_electrum.py diff --git a/mmgen-addrgen b/mmgen-addrgen index c71cec0b..15a6cd54 100755 --- a/mmgen-addrgen +++ b/mmgen-addrgen @@ -163,7 +163,13 @@ if invoked_as == "addrgen": else: if not 'no_addresses' in opts: opts['print_secret'] = True -seed = get_seed(infile,opts) +# Repeat on incorrect pw entry +silent = False +while True: + seed = get_seed(infile,opts,silent=silent) + silent = True + if seed: break + seed_id = make_chksum_8(seed) addr_data = generate_addrs(seed, addr_list, opts) addr_data_str = format_addr_data(addr_data, seed_id, opts) diff --git a/mmgen-passchg b/mmgen-passchg index 6134b082..9bc37adb 100755 --- a/mmgen-passchg +++ b/mmgen-passchg @@ -66,9 +66,13 @@ infile = cmd_args[0] label,metadata,hash_preset,salt,enc_seed = get_data_from_wallet(infile,opts) seed_id,key_id = metadata[:2] oldp = "" if 'keep_old_passphrase' in opts else "old " -passwd = " ".join(get_words("","",("Enter %spassphrase: " % oldp),opts)) -key = make_key(passwd, salt, hash_preset) -seed = decrypt_seed(enc_seed, key, seed_id, key_id) + +# Repeat on incorrect pw entry +while True: + passwd = " ".join(get_words("","",("Enter %spassphrase: " % oldp),opts)) + key = make_key(passwd, salt, hash_preset) + seed = decrypt_seed(enc_seed, key, seed_id, key_id) + if seed: break changed = {} diff --git a/mmgen-txsign b/mmgen-txsign index 174757dc..49bf1485 100755 --- a/mmgen-txsign +++ b/mmgen-txsign @@ -109,7 +109,7 @@ if user_confirm("View transaction data? ",default_yes=False): view_tx_data(c,inputs_data,tx_hex,metadata) # Are inputs mmgen addresses? -infile,mmgen_addrs,other_addrs,keys = "",[],[],[] +mmgen_addrs,other_addrs,keys = [],[],[] for i in inputs_data: if verify_mmgen_label(i['account']): @@ -121,9 +121,11 @@ if mmgen_addrs and not 'force_wallet_dat' in opts: # Check that all the seed IDs are the same: seed_ids = list(set([i['account'][:8] for i in mmgen_addrs])) while seed_ids: + infile = False if cmd_args: infile = cmd_args.pop() ext = infile.split(".")[-1] + found = False for e,o in ( (wallet_ext, {}), (mn_ext, {"from_mnemonic":True}), @@ -131,15 +133,29 @@ if mmgen_addrs and not 'force_wallet_dat' in opts: (brain_ext, {}) ): if e == ext: + found = True if e == brain_ext: if "from_brain" in opts: o = opts else: msg("'--from-brain' option must be specified for brainwallet file") sys.exit(2) - seed = get_seed(infile,o) + silent = False + while True: + seed = get_seed(infile,o,silent=silent) + silent = True + if seed: break + + if not found: + msg("%s: invalid file extension" % ext) + sys.exit(2) + elif "from_brain" in opts or "from_mnemonic" in opts or "from_seed" in opts: msg("Need data for seed ID %s" % seed_ids[0]) - seed = get_seed("",opts) + silent = False + while True: + seed = get_seed("",opts,silent=silent) + silent = True + if seed: break else: msg("One of '-b', '-m' or '-s' must be specified for seed IDs: %s" % " ".join(seed_ids)) @@ -158,7 +174,9 @@ if mmgen_addrs and not 'force_wallet_dat' in opts: keys += [i['wif'] for i in generate_addrs(seed, seed_id_addrs, o)] else: msg("Supplied seed ID (%s) is incorrect" % seed_id) - sys.exit(2) + if infile: + msg("Invalid input file: %s" % infile) + sys.exit(2) if other_addrs: if 'keys_from_file' in opts: diff --git a/mmgen-walletgen b/mmgen-walletgen index 4967312f..7e491cad 100755 --- a/mmgen-walletgen +++ b/mmgen-walletgen @@ -151,8 +151,12 @@ if debug: display_user_random_data(usr_keys,key_timings) usr_rand_data = sha256(usr_keys).digest() + \ sha256("".join(key_timings)).digest() -s = get_seed(infile,opts,no_wallet=True) -if s: seed = s +if 'from_mnemonic' in opts or 'from_brain' in opts or 'from_seed' in opts: + silent = False + while True: + seed = get_seed(infile,opts,silent=silent) + silent = True + if seed: break else: # Truncate random data for smaller seed lengths seed = os_rand_data[0] + usr_rand_data diff --git a/mmgen/addr.py b/mmgen/addr.py index 5f025408..508ad126 100755 --- a/mmgen/addr.py +++ b/mmgen/addr.py @@ -108,7 +108,10 @@ def generate_addrs(seed, addrnums, opts): out.append(el) w = opts['gen_what'] - if t_addrs == 1: w = w[:-1] + if t_addrs == 1: + import re + w = re.sub('e*s$','',w) + sys.stderr.write("\rGenerated %s %s%s\n"%(t_addrs, w, " "*15)) return out diff --git a/mmgen/mnemonic.py b/mmgen/mnemonic.py index 1f1fe249..8144b1ff 100755 --- a/mmgen/mnemonic.py +++ b/mmgen/mnemonic.py @@ -50,12 +50,12 @@ def get_seed_from_mnemonic(mn,wl): if len(mn) not in mnemonic_lens: msg("Bad mnemonic (%i words). Allowed numbers of words: %s" % (len(mn)," ".join([str(i) for i in mnemonic_lens]))) - sys.exit(2) + return False for w in mn: if w not in wl: msg("Bad mnemonic: '%s' is not in the wordlist" % w) - sys.exit(2) + return False from binascii import unhexlify seed = unhexlify(baseNtohex(mn_base,mn,wl,mn_fill(mn))) diff --git a/mmgen/utils.py b/mmgen/utils.py index 250345b6..153bc421 100755 --- a/mmgen/utils.py +++ b/mmgen/utils.py @@ -536,7 +536,7 @@ def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed, opts): if 'outdir' in opts: outfile = "%s/%s" % (opts['outdir'], outfile) - label = opts['label'] if 'label' in opts else "None" + label = opts['label'] if 'label' in opts else "No Label" from mmgen.bitcoin import b58encode_pad @@ -572,14 +572,15 @@ def write_walletdat_dump_to_file(wallet_id,data,num_keys,ext,what,opts): msg("wallet.dat %s saved to file '%s'" % (what,outfile)) -def compare_checksums(chksum1, desc1, chksum2, desc2): +def compare_checksums(chksum1, desc1, chksum2, desc2): if chksum1.lower() == chksum2.lower(): msg("OK (%s)" % chksum1.upper()) return True else: - msg("ERROR!\nComputed checksum %s (%s) doesn't match checksum %s (%s)" \ - % (desc1,chksum1,desc2,chksum2)) + if debug: + msg("ERROR!\nComputed checksum %s (%s) doesn't match checksum %s (%s)" \ + % (desc1,chksum1,desc2,chksum2)) return False def _is_hex(s): @@ -588,7 +589,7 @@ def _is_hex(s): else: return True -def check_mmseed_format(words): +def _check_mmseed_format(words): valid = False what = "%s data" % seed_ext @@ -602,12 +603,10 @@ def check_mmseed_format(words): msg("Incorrect length of checksum (%s) in %s" % (chklen,what)) else: valid = True - if valid == False: - msg("Invalid %s data" % seed_ext) - sys.exit(3) + return valid -def check_wallet_format(infile, lines, opts): +def check_wallet_format(infile, lines, opts): def vmsg(s): if 'verbose' in opts: msg(s) @@ -638,9 +637,10 @@ def _check_chksum_6(chk,val,desc,infile): msg("%s checksum passed: %s" % (desc.capitalize(),chk)) -def get_data_from_wallet(infile,opts): +def get_data_from_wallet(infile,opts,silent=False): - msg("Getting {} wallet data from file '{}'".format(proj_name,infile)) + if not silent: + msg("Getting {} wallet data from file '{}'".format(proj_name,infile)) f = open_file_or_exit(infile, 'r') @@ -719,9 +719,11 @@ def get_words(infile,what,prompt,opts): return words -def get_seed_from_seed_data(words): +def _get_seed_from_seed_data(words): - check_mmseed_format(words) + if not _check_mmseed_format(words): + msg("Invalid %s data" % seed_ext) + return False stored_chk = words[0] seed_b58 = "".join(words[1:]) @@ -733,19 +735,23 @@ def get_seed_from_seed_data(words): seed = b58decode_pad(seed_b58) if seed == False: msg("Invalid b58 number: %s" % val) - sys.exit(9) + return False msg("%s data produces seed ID: %s" % (seed_ext,make_chksum_8(seed))) return seed else: msg("Invalid checksum for {} seed".format(proj_name)) - sys.exit(9) + return False -def get_seed_from_wallet(infile,opts, - prompt="Enter {} wallet passphrase: ".format(proj_name)): +def get_seed_from_wallet( + infile, + opts, + prompt="Enter {} wallet passphrase: ".format(proj_name), + silent=False + ): - wdata = get_data_from_wallet(infile,opts) + wdata = get_data_from_wallet(infile,opts,silent=silent) label,metadata,hash_preset,salt,enc_seed = wdata if 'verbose' in opts: _display_control_data(*wdata) @@ -770,8 +776,8 @@ def decrypt_seed(enc_seed, key, seed_id, key_id): msg_r("Checking key...") chk = make_chksum_8(key) if not compare_checksums(chk, "of key", key_id, "in header"): - msg("Passphrase incorrect?") - sys.exit(3) + msg("Incorrect passphrase") + return False msg_r("Decrypting seed with key...") @@ -793,21 +799,22 @@ def decrypt_seed(enc_seed, key, seed_id, key_id): else: msg("Incorrect passphrase") - sys.exit(3) + return False if debug: msg("key: %s" % hexlify(key)) return dec_seed -def get_seed(infile,opts,no_wallet=False): +def get_seed(infile,opts,silent=False): if 'from_mnemonic' in opts: prompt = "Enter mnemonic: " + what = "mnemonic" words = get_words(infile,"mnemonic data",prompt,opts) wl = get_default_wordlist() from mmgen.mnemonic import get_seed_from_mnemonic - return get_seed_from_mnemonic(words,wl) + seed = get_seed_from_mnemonic(words,wl) elif 'from_brain' in opts: if 'quiet' not in opts: confirm_or_exit( @@ -816,16 +823,22 @@ def get_seed(infile,opts,no_wallet=False): *_get_from_brain_opt_params(opts)), "continue") prompt = "Enter brainwallet passphrase: " + what = "brainwallet" words = get_words(infile,"brainwallet data",prompt,opts) - return _get_seed_from_brain_passphrase(words,opts) + seed = _get_seed_from_brain_passphrase(words,opts) elif 'from_seed' in opts: prompt = "Enter seed in %s format: " % seed_ext + what = "seed" words = get_words(infile,"seed data",prompt,opts) - return get_seed_from_seed_data(words) - elif no_wallet: - return False + seed = _get_seed_from_seed_data(words) else: - return get_seed_from_wallet(infile, opts) + return get_seed_from_wallet(infile, opts, silent=silent) + + if infile and not seed: + msg("Invalid %s file: %s" % (what,infile)) + sys.exit(2) + + return seed def remove_blanks_comments(lines): diff --git a/setup.py b/setup.py index fb86aaeb..7b8ee368 100755 --- a/setup.py +++ b/setup.py @@ -3,7 +3,7 @@ from distutils.core import setup setup( name = 'mmgen', - version = '0.6.4', + version = '0.6.5', author = 'Philemon', author_email = 'mmgen-py@yandex.com', url = 'https://github.com/mmgen/mmgen',