From ea449e652a9ae73bfe8696d77b26ab1d7d73f926 Mon Sep 17 00:00:00 2001 From: philemon Date: Wed, 6 Aug 2014 20:37:29 +0400 Subject: [PATCH] Regression fix in addr.py, improvements in mmgen-txsign. --- mmgen/addr.py | 3 ++- mmgen/crypto.py | 10 ++++++---- mmgen/main.py | 29 ++++++++++++++-------------- mmgen/main_addrgen.py | 3 +-- mmgen/main_txsign.py | 22 +++++++++++----------- mmgen/tool.py | 12 ++++++------ mmgen/tx.py | 44 +++++++++++++++++++++---------------------- mmgen/util.py | 19 ++++++++++--------- 8 files changed, 72 insertions(+), 70 deletions(-) diff --git a/mmgen/addr.py b/mmgen/addr.py index 0ad95040..8dab142a 100755 --- a/mmgen/addr.py +++ b/mmgen/addr.py @@ -80,7 +80,8 @@ def generate_addrs(seed, addrnums, opts): addrnums.sort() # needed only if caller didn't sort ws = 'key' if 'keys' in opts['gen_what'] else 'address' - if t_addrs != 1: wp = ws+"s" if ws == 'key' else ws+"es" + if t_addrs == 1: wp = ws + else: wp = ws+"s" if ws == 'key' else ws+"es" while pos != t_addrs: seed = sha512(seed).digest() diff --git a/mmgen/crypto.py b/mmgen/crypto.py index 509530e9..52144523 100755 --- a/mmgen/crypto.py +++ b/mmgen/crypto.py @@ -369,10 +369,11 @@ def _get_seed_from_brain_passphrase(words,opts): # Vars for mmgen_*crypt functions only salt_len,sha256_len,nonce_len = 32,32,32 -def mmgen_encrypt(data,what="data",hash_preset='3',opts={}): +def mmgen_encrypt(data,what="data",hash_preset='',opts={}): salt,iv,nonce = get_random(salt_len,opts),\ get_random(g.aesctr_iv_len,opts), get_random(nonce_len,opts) - hp,m = (hash_preset,"user-requested") if hash_preset else ('3',"default") + hp = hash_preset or get_hash_preset_from_user('3',what) + m = "default" if hp == '3' else "user-requested" vmsg("Encrypting %s" % what) qmsg("Using %s hash preset of '%s'" % (m,hp)) passwd = get_new_passphrase("passphrase",{}) @@ -382,11 +383,12 @@ def mmgen_encrypt(data,what="data",hash_preset='3',opts={}): return salt+iv+enc_d -def mmgen_decrypt(data,what="data",hash_preset='3',opts={}): +def mmgen_decrypt(data,what="data",hash_preset='',opts={}): dstart = salt_len + g.aesctr_iv_len salt,iv,enc_d = data[:salt_len],data[salt_len:dstart],data[dstart:] - hp,m = (hash_preset,"user-requested") if hash_preset else ('3',"default") vmsg("Preparing to decrypt %s" % what) + hp = hash_preset or get_hash_preset_from_user('3',what) + m = "default" if hp == '3' else "user-requested" qmsg("Using %s hash preset of '%s'" % (m,hp)) passwd = get_mmgen_passphrase("Enter passphrase: ",{}) key = make_key(passwd, salt, hp) diff --git a/mmgen/main.py b/mmgen/main.py index 0713ec44..88a19b8b 100755 --- a/mmgen/main.py +++ b/mmgen/main.py @@ -20,25 +20,24 @@ main.py - Script launcher for the MMGen suite """ -import sys, termios -from mmgen.util import msg +def launch_addrgen(): import mmgen.main_addrgen +def launch_addrimport(): import mmgen.main_addrimport +def launch_keygen(): import mmgen.main_addrgen +def launch_passchg(): import mmgen.main_passchg +def launch_pywallet(): import mmgen.main_pywallet +def launch_tool(): import mmgen.main_tool +def launch_txcreate(): import mmgen.main_txcreate +def launch_txsend(): import mmgen.main_txsend +def launch_txsign(): import mmgen.main_txsign +def launch_walletchk(): import mmgen.main_walletchk +def launch_walletgen(): import mmgen.main_walletgen def main(progname): + import sys, termios fd = sys.stdin.fileno() old = termios.tcgetattr(fd) - try: - if progname == "addrgen": import mmgen.main_addrgen - elif progname == "addrimport": import mmgen.main_addrimport - elif progname == "keygen": import mmgen.main_addrgen - elif progname == "passchg": import mmgen.main_passchg - elif progname == "pywallet": import mmgen.main_pywallet - elif progname == "tool": import mmgen.main_tool - elif progname == "txcreate": import mmgen.main_txcreate - elif progname == "txsend": import mmgen.main_txsend - elif progname == "txsign": import mmgen.main_txsign - elif progname == "walletchk": import mmgen.main_walletchk - elif progname == "walletgen": import mmgen.main_walletgen + try: eval("launch_"+progname+"()") except KeyboardInterrupt: - msg("\nUser interrupt") + sys.stderr.write("\nUser interrupt\n") termios.tcsetattr(fd, termios.TCSADRAIN, old) sys.exit(1) diff --git a/mmgen/main_addrgen.py b/mmgen/main_addrgen.py index a6e82f9f..8b45a420 100755 --- a/mmgen/main_addrgen.py +++ b/mmgen/main_addrgen.py @@ -161,8 +161,7 @@ addr_data_str = format_addr_data( outfile_base = "{}[{}]".format(seed_id, fmt_addr_idxs(addr_idxs)) if 'flat_list' in opts and user_confirm("Encrypt key list?"): - hp = get_hash_preset_from_user('3') - addr_data_str = mmgen_encrypt(addr_data_str,"key list",hp,opts) + addr_data_str = mmgen_encrypt(addr_data_str,"key list","",opts) enc_ext = "." + g.mmenc_ext else: enc_ext = "" diff --git a/mmgen/main_txsign.py b/mmgen/main_txsign.py index 940c08e8..50fad675 100755 --- a/mmgen/main_txsign.py +++ b/mmgen/main_txsign.py @@ -100,7 +100,6 @@ if 'from_incog_hex' in opts or 'from_incog_hidden' in opts: opts['from_incog'] = True if 'all_keys_from_file' in opts: opts['keys_from_file'] = opts['all_keys_from_file'] -# opts['skip_key_preverify'] = True if not infiles: usage(help_data) for i in infiles: check_infile(i) @@ -110,20 +109,19 @@ c = connect_to_bitcoind() saved_seeds = {} tx_files = [i for i in set(infiles) if get_extension(i) == g.rawtx_ext] addrfiles = [a for a in set(infiles) if get_extension(a) == g.addrfile_ext] -infiles = list(set(infiles) - set(tx_files) - set(addrfiles)) +seed_files = list(set(infiles) - set(tx_files) - set(addrfiles)) if not "info" in opts: do_license_msg(immed=True) if 'keys_from_file' in opts: - from mmgen.crypto import mmgen_decrypt fn = opts['keys_from_file'] d = get_data_from_file(fn,"keylist") if get_extension(fn) == g.mmenc_ext or not \ - is_b58_str(remove_comments(d.split("\n"))[0][:55]): + is_btc_key(remove_comments(d.split("\n"))[0][:55]): qmsg("Keylist appears to be encrypted") + from mmgen.crypto import mmgen_decrypt while True: - hp = get_hash_preset_from_user('3') - d_dec = mmgen_decrypt(d,"encrypted keylist",hp,opts) + d_dec = mmgen_decrypt(d,"encrypted keylist","",opts) if d_dec: d = d_dec; break else: msg("Trying again...") keys_from_file = remove_comments(d.split("\n")) @@ -157,8 +155,9 @@ for tx_file in tx_files: sys.exit(2) if other_inputs and keys and not 'skip_key_preverify' in opts: - a = [i['address'] for i in other_inputs] - preverify_keys(a, keys) + addrs = [i['address'] for i in other_inputs] + mm_inputs = mmgen_inputs if 'all_keys_from_file' in opts else [] + preverify_keys(addrs, keys, mm_inputs) opts['skip_key_preverify'] = True if 'all_keys_from_file' in opts: @@ -168,7 +167,7 @@ for tx_file in tx_files: confirm_or_exit(txmsg['skip_mapping_checks_warning'],"continue") else: check_mmgen_to_btc_addr_mappings( - mmgen_inputs,b2m_map,infiles,saved_seeds,opts) + mmgen_inputs,b2m_map,seed_files,saved_seeds,opts) if len(tx_files) > 1: msg("\nTransaction %s/%s:" % (tx_files.index(tx_file)+1,len(tx_files))) @@ -185,7 +184,7 @@ for tx_file in tx_files: if mmgen_inputs and not 'all_keys_from_file' in opts: ml = [i['account'].split()[0] for i in mmgen_inputs] - keys += get_keys_for_mmgen_addrs(ml,infiles,saved_seeds,opts) + keys += get_keys_for_mmgen_addrs(ml,seed_files,saved_seeds,opts) if 'use_wallet_dat' in opts: sig_tx = sign_tx_with_bitcoind_wallet(c,tx_hex,sig_data,keys,opts) @@ -207,7 +206,8 @@ for tx_file in tx_files: repr(inputs_data), repr(b2m_map) ) - write_to_file(outfile,data,opts,"signed transaction",True,True) + confirm = False if g.quiet else True + write_to_file(outfile,data,opts,"signed transaction",confirm,True) else: msg("failed\nSome keys were missing. Transaction could not be signed.") sys.exit(3) diff --git a/mmgen/tool.py b/mmgen/tool.py index 78c0f371..deb1e4cc 100755 --- a/mmgen/tool.py +++ b/mmgen/tool.py @@ -72,8 +72,8 @@ commands = { "pubkey2addr": [' [str]'], "pubkey2hexaddr": [' [str]'], "privhex2addr": [' [str]','compressed [bool=False]'], - "encrypt": [' [str]','outfile [str=""]','hash_preset [str="3"]'], - "decrypt": [' [str]','outfile [str=""]','hash_preset [str="3"]'], + "encrypt": [' [str]','outfile [str=""]','hash_preset [str=""]'], + "decrypt": [' [str]','outfile [str=""]','hash_preset [str=""]'], "rand2file": [' [str]',' [str]','threads [int=4]'], "bytespec": [' [str]'], } @@ -416,9 +416,9 @@ def hex2wif(hexpriv,compressed=False): print bitcoin.hextowif(hexpriv,compressed) -def encrypt(infile,outfile="",hash_preset='3'): +def encrypt(infile,outfile="",hash_preset=''): data = get_data_from_file(infile,"data for encryption") - enc_d = mmgen_encrypt(data,"",hash_preset,opts) + enc_d = mmgen_encrypt(data,"user data","",opts) if outfile == '-': write_to_stdout(enc_d,"encrypted data",confirm=True) else: @@ -427,9 +427,9 @@ def encrypt(infile,outfile="",hash_preset='3'): write_to_file(outfile, enc_d, opts,"encrypted data",True,True) -def decrypt(infile,outfile="",hash_preset='3'): +def decrypt(infile,outfile="",hash_preset=''): enc_d = get_data_from_file(infile,"encrypted data") - dec_d = mmgen_decrypt(enc_d,"",hash_preset,opts) + dec_d = mmgen_decrypt(enc_d,"user data","",opts) if outfile == '-': write_to_stdout(dec_d,"decrypted data",confirm=True) else: diff --git a/mmgen/tx.py b/mmgen/tx.py index c0f45df1..0f704d22 100755 --- a/mmgen/tx.py +++ b/mmgen/tx.py @@ -137,6 +137,7 @@ def is_btc_amt(amt): def normalize_btc_amt(amt): + # amt must be a string! ret = is_btc_amt(amt) if ret: return ret else: sys.exit(3) @@ -460,10 +461,15 @@ def is_b58_str(s): if ch not in b58a: return False return True +def is_btc_key(s): + if s == "": return False + compressed = False if s[0] == '5' else True + from mmgen.bitcoin import wiftohex + return True if wiftohex(s,compressed) else False def mmaddr2btcaddr_bitcoind(c,mmaddr,acct_data): - # We don't want to create a new object, so we'll use append() + # Don't want to create a new object, so use append() if not acct_data: for i in c.listaccounts(): acct_data.append(i) @@ -677,31 +683,21 @@ def sign_tx_with_bitcoind_wallet(c,tx_hex,sig_data,keys,opts): return sig_tx -def preverify_keys(addrs_orig, keys_orig): +def preverify_keys(addrs_in, keys_in, mm_inputs): - addrs,keys,wrong_keys = set(addrs_orig[0:]),set(keys_orig[0:]),[] - - if len(keys) < len(addrs): - msg("ERROR: not enough keys (%s) for number of non-%s addresses (%s)" % - (len(keys),g.proj_name,len(addrs))) - sys.exit(2) + addrs,keys,extra_keys = set(addrs_in),set(keys_in),[] import mmgen.bitcoin as b qmsg_r('Checking that user-supplied key list contains valid keys...') - invalid_keys = [] - - for n,k in enumerate(keys,1): - c = False if k[0] == '5' else True - if b.wiftohex(k,compressed=c) == False: - invalid_keys.append(k) - + invalid_keys = [k for k in keys if not is_btc_key(k)] if invalid_keys: s = "" if len(invalid_keys) == 1 else "s" msg("\n%s/%s invalid key%s in keylist!\n" % (len(invalid_keys),len(keys),s)) sys.exit(2) - else: qmsg("OK") + + qmsg("OK") # Check that keys match addresses: msg('Pre-verifying keys in user-supplied key list (Ctrl-C to skip)') @@ -716,19 +712,23 @@ def preverify_keys(addrs_orig, keys_orig): addrs.remove(addr) if not addrs: break else: - wrong_keys.append(k) + extra_keys.append(k) except KeyboardInterrupt: msg("\nSkipping") else: msg("") - if wrong_keys: - s = "" if len(wrong_keys) == 1 else "s" - msg("%s extra key%s found" % (len(wrong_keys),s)) + if extra_keys: + s = "" if len(extra_keys) == 1 else "s" + msg("%s extra key%s found" % (len(extra_keys),s)) if addrs: + mms = dict([(i['address'],i['account'].split()[0]) + for i in mm_inputs if i['address'] in addrs]) s = "" if len(addrs) == 1 else "es" - msg("No keys found for the following address%s:" % s) - print " %s" % "\n ".join(addrs) + msg( +"Cannot sign transaction. No keys found for the following address%s:"%s) + for a in sorted(addrs): + print " %s%s" % (a, " ({})".format(mms[a]) if a in mms else "") sys.exit(2) diff --git a/mmgen/util.py b/mmgen/util.py index 922ee9d6..4a489fc9 100755 --- a/mmgen/util.py +++ b/mmgen/util.py @@ -347,14 +347,14 @@ def write_to_file(outfile,data,opts,what="data",confirm_overwrite=False,verbose= if 'outdir' in opts: outfile = make_full_path(opts['outdir'],outfile) - if confirm_overwrite: - from os import stat - try: - stat(outfile) - except: - pass - else: + from os import stat + try: stat(outfile) + except: pass + else: + if confirm_overwrite: confirm_or_exit("","File '%s' already exists\nOverwrite?" % outfile) + else: + msg("Overwriting file '%s'" % outfile) f = open_file_or_exit(outfile,'w') try: @@ -685,8 +685,9 @@ def export_to_hidden_incog(incog_enc,opts): from mmgen.term import kb_hold_protect,get_char -def get_hash_preset_from_user(hp='3'): - p = "Enter hash preset, or hit ENTER to accept the default ('%s'): " % hp +def get_hash_preset_from_user(hp='3',what="data"): + p = "Enter hash preset for %s, or ENTER to accept the default ('%s'): " \ + % (what,hp) while True: ret = my_raw_input(p) if ret: