diff --git a/mmgen-addrgen b/mmgen-addrgen index 78694460..010c9c15 100755 --- a/mmgen-addrgen +++ b/mmgen-addrgen @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mmgen-addrimport b/mmgen-addrimport index 8d558f77..13835fbb 100755 --- a/mmgen-addrimport +++ b/mmgen-addrimport @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mmgen-passchg b/mmgen-passchg index 36c58070..a6117dc3 100755 --- a/mmgen-passchg +++ b/mmgen-passchg @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mmgen-tool b/mmgen-tool new file mode 100755 index 00000000..96274092 --- /dev/null +++ b/mmgen-tool @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# +# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution +# Copyright (C) 2013 by philemon +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +mmgen-tool: Perform various Bitcoin-related operations - part of the MMGen suite +""" + +import sys, os +from hashlib import sha256 + +from mmgen.Opts import * +import mmgen.config as g +from mmgen.util import pretty_hexdump +from mmgen.tool import * +prog_name = sys.argv[0].split("/")[-1] + +help_data = { + 'prog_name': prog_name, + 'desc': "Perform various BTC-related operations", + 'usage': "[opts] ", + 'options': """ +-h, --help Print this help message +-q, --quiet Produce quieter output +-v, --verbose Produce more verbose output + +COMMANDS:{} +Type '{} --help for usage information on a particular command +""".format(command_help,prog_name) +} + +short_opts = "hqv" +long_opts = "help","quiet","verbose" + +opts,cmd_args = process_opts(sys.argv,help_data,short_opts,long_opts) +if 'quiet' in opts: g.quiet = True +if 'verbose' in opts: g.verbose = True + +if len(cmd_args) < 1: + usage(help_data) + sys.exit(1) +else: command = cmd_args.pop(0) + +if command not in commands.keys(): + msg("'%s': No such command" % command) + sys.exit(1) + +if cmd_args and cmd_args[0] == '--help': + tool_usage(prog_name, command) + sys.exit(0) + +args = process_args(prog_name, command, cmd_args) if cmd_args else [] + +#print command + "(" + ", ".join(args) + ")" +eval(command + "(" + ", ".join(args) + ")") diff --git a/mmgen-txcreate b/mmgen-txcreate index df9b5dfd..5d0fce2b 100755 --- a/mmgen-txcreate +++ b/mmgen-txcreate @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -79,36 +79,36 @@ if not 'info' in opts: check_infile(a) addr_data.append(parse_addrs_file(a)) - def mm2btc_addr_proc(c,mmadr,acct_data,addr_data,b2m_map): - btaddr,label = mmgen_addr_to_walletd(c,mmadr,acct_data) - if not btaddr: - btaddr,label = mmgen_addr_to_addr_data(mmadr,addr_data) - b2m_map[btaddr] = mmadr,label - return btaddr + def mm_addr2btc_addr(c,mmadr,acct_data,addr_data,b2m_map): + btcaddr,label = mmgen_addr_to_walletd(c,mmadr,acct_data) + if not btcaddr: + btcaddr,label = mmgen_addr_to_addr_data(mmadr,addr_data) + b2m_map[btcaddr] = mmadr,label + return btcaddr for a in [i for i in cmd_args if not match_ext(i,g.addrfile_ext)]: if "," in a: a1,a2 = a.split(",") - if is_mmgen_addr(a1) or is_btc_addr(a1): - btaddr = mm2btc_addr_proc(c,a1,acct_data,addr_data,b2m_map) \ - if is_mmgen_addr(a1) else a1 - if is_btc_amt(a2): - tx_out[btaddr] = check_btc_amt(a2) - else: - msg("%s: invalid amount in argument '%s'" % (a2,a)) - sys.exit(2) + if is_btc_addr(a1): + btcaddr = a1 + elif is_mmgen_addr(a1): + btcaddr = mm_addr2btc_addr(c,a1,acct_data,addr_data,b2m_map) else: msg("%s: unrecognized subargument in argument '%s'" % (a1,a)) sys.exit(2) + + if is_btc_amt(a2): + tx_out[btcaddr] = check_btc_amt(a2) + else: + msg("%s: invalid amount in argument '%s'" % (a2,a)) + sys.exit(2) elif is_mmgen_addr(a) or is_btc_addr(a): if change_addr: - msg("More than one change address specified: %s, %s" % + msg("ERROR: More than one change address specified: %s, %s" % (change_addr, a)) sys.exit(2) - if is_mmgen_addr(a): - change_addr = mm2btc_addr_proc(c,a,acct_data,addr_data,b2m_map) - else: - change_addr = a + change_addr = a if is_btc_addr(a) else \ + mm_addr2btc_addr(c,a,acct_data,addr_data,b2m_map) tx_out[change_addr] = 0 else: msg("%s: unrecognized argument" % a) @@ -157,7 +157,9 @@ msg("Total amount to spend: %s%s" % ( while True: sel_nums = select_outputs(unspent, "Enter a range or space-separated list of outputs to spend: ") - msg("Selected outputs: %s" % " ".join(str(i) for i in sel_nums)) + msg("Selected output%s: %s" % + (("" if len(sel_nums) == 1 else "s"), " ".join(str(i) for i in sel_nums)) + ) sel_unspent = [unspent[i-1] for i in sel_nums] mmaddrs = set([parse_mmgen_label(i.account)[0] for i in sel_unspent]) diff --git a/mmgen-txsend b/mmgen-txsend index 9a3ce94d..c54d1b40 100755 --- a/mmgen-txsend +++ b/mmgen-txsend @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mmgen-txsign b/mmgen-txsign index 5d4ad520..80876f68 100755 --- a/mmgen-txsign +++ b/mmgen-txsign @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mmgen-walletchk b/mmgen-walletchk index 819fd205..f0ce9770 100755 --- a/mmgen-walletchk +++ b/mmgen-walletchk @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -73,50 +73,19 @@ elif 'export_seed' in opts: qmsg("Exporting seed data to file by user request") elif 'export_incog' in opts: qmsg("Exporting wallet to incognito format by user request") - - d = get_data_from_wallet(cmd_args[0],silent=True) - seed_id,key_id,preset,salt,enc_seed = \ - d[1][0], d[1][1], d[2].split(":")[0], d[3], d[4] - - passwd = get_mmgen_passphrase("Enter mmgen passphrase: ",opts) - key = make_key(passwd, salt, preset, "main key") - # We don't need the seed; just do this to verify password. - if decrypt_seed(enc_seed, key, seed_id, key_id) == False: - sys.exit(2) - - from Crypto import Random - iv = Random.new().read(g.aesctr_iv_len) - iv_id = make_chksum_8(iv) - qmsg("IV ID: %s" % iv_id) - - from binascii import hexlify - from hashlib import sha256 - # IV is used BOTH to initialize counter and to salt password! - key = make_key(passwd, iv, preset, "wrapper key") - incog_enc = encrypt_seed(salt + enc_seed, key, iv=int(hexlify(iv),16)) + incog_enc,seed_id,key_id,iv_id,preset = \ + wallet_to_incog_data(cmd_args[0],opts) if "export_incog_hidden" in opts: - fname,offset = opts['export_incog_hidden'].split(",") #Already sanity-checked - offset = int(offset) - - check_data_fits_file_at_offset(fname,offset,len(iv + incog_enc),"write") - - if not g.quiet: confirm_or_exit("","alter file '%s'" % fname) - f = os.open(fname,os.O_RDWR) - os.lseek(f, offset, os.SEEK_SET) - os.write(f, iv + incog_enc) - os.close(f) - qmsg("Data written to file '%s' at offset %s" % (fname,offset), - "Data written to file") + export_to_hidden_incog(incog_enc,opts) else: + seed_len = (len(incog_enc)-g.salt_len-g.aesctr_iv_len)*8 fn = "%s-%s-%s[%s,%s].%s" % ( - seed_id, key_id, iv_id, len(enc_seed)*8, preset, + seed_id, key_id, iv_id, seed_len, preset, g.incog_hex_ext if "export_incog_hex" in opts else g.incog_ext ) - data = iv + incog_enc - if "export_incog_hex" in opts: - data = "".join([hexlify(data[i*2:i*2+2]) + (" " if (i+1)%8 else "\n") - for i in range(len(data)/2)]) + data = pretty_hexdump(incog_enc,2,8,line_nums=False) \ + if "export_incog_hex" in opts else incog_enc export_to_file(fn, data, "incognito wallet data", opts) sys.exit() diff --git a/mmgen-walletgen b/mmgen-walletgen index d0c93088..75774a41 100755 --- a/mmgen-walletgen +++ b/mmgen-walletgen @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mmgen/Opts.py b/mmgen/Opts.py index 4722f1a6..9b457348 100755 --- a/mmgen/Opts.py +++ b/mmgen/Opts.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mmgen/__init__.py b/mmgen/__init__.py index c3b62a79..8fc4d095 100755 --- a/mmgen/__init__.py +++ b/mmgen/__init__.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mmgen/addr.py b/mmgen/addr.py index fd9a25c2..02931553 100755 --- a/mmgen/addr.py +++ b/mmgen/addr.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mmgen/bitcoin.py b/mmgen/bitcoin.py index d5f094ef..431f7ab2 100755 --- a/mmgen/bitcoin.py +++ b/mmgen/bitcoin.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # # MMGen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mmgen/config.py b/mmgen/config.py index 3e0e3365..20c08811 100755 --- a/mmgen/config.py +++ b/mmgen/config.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mmgen/license.py b/mmgen/license.py index 0bc72a60..7083b06b 100755 --- a/mmgen/license.py +++ b/mmgen/license.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -24,9 +24,9 @@ from mmgen.util import msg, msg_r, get_char gpl = { 'warning': """ - MMGen Copyright (C) 2013 by Philemon . This - program comes with ABSOLUTELY NO WARRANTY. This is free software, - and you are welcome to redistribute it under certain conditions. + MMGen Copyright (C) 2013-2014 by Philemon . This + program comes with ABSOLUTELY NO WARRANTY. This is free software, and + you are welcome to redistribute it under certain conditions. """, 'prompt': """ Press 'w' for conditions and warranty info, or 'c' to continue: diff --git a/mmgen/mn_electrum.py b/mmgen/mn_electrum.py index 272e9fc2..2e99709d 100755 --- a/mmgen/mn_electrum.py +++ b/mmgen/mn_electrum.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mmgen/mn_tirosh.py b/mmgen/mn_tirosh.py index 239cfc52..814ea12a 100755 --- a/mmgen/mn_tirosh.py +++ b/mmgen/mn_tirosh.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mmgen/mnemonic.py b/mmgen/mnemonic.py index bd32ae73..e4ea611e 100755 --- a/mmgen/mnemonic.py +++ b/mmgen/mnemonic.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -95,7 +95,7 @@ def check_wordlist(wl_str,label): from hashlib import sha256 - print "Length: %i" % len(wl) + print "Length: %i words" % len(wl) new_chksum = sha256(" ".join(wl)).hexdigest()[:8] if new_chksum != wl_checksums[label]: diff --git a/mmgen/term.py b/mmgen/term.py index eec7292f..c9cf24e8 100755 --- a/mmgen/term.py +++ b/mmgen/term.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mmgen/tests/bitcoin.py b/mmgen/tests/bitcoin.py index 6abb908c..1c7288c7 100755 --- a/mmgen/tests/bitcoin.py +++ b/mmgen/tests/bitcoin.py @@ -218,6 +218,5 @@ tests = { "wiftoaddr_comp": ['wif [str]'], } - args = process_test_args(sys.argv, tests) eval(sys.argv[1])(*args) diff --git a/mmgen/tests/mnemonic.py b/mmgen/tests/mnemonic.py index 9727bf5d..d2d2afe9 100755 --- a/mmgen/tests/mnemonic.py +++ b/mmgen/tests/mnemonic.py @@ -19,11 +19,12 @@ mnemonic.py: Test suite for mmgen.mnemonic module """ -from mmgen.mnemonic import * from test import * import sys from binascii import hexlify + +from mmgen.mnemonic import * from mmgen.mn_electrum import electrum_words as el from mmgen.mn_tirosh import tirosh_words as tl diff --git a/mmgen/tool.py b/mmgen/tool.py new file mode 100755 index 00000000..bfc4327a --- /dev/null +++ b/mmgen/tool.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python +# +# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution +# Copyright (C) 2013-2014 by philemon +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""" +tool.py: Routines and data for the mmgen-tool utility +""" + +import sys +import mmgen.bitcoin as bitcoin + +from mmgen.util import * + +commands = { +# "keyconv_compare": ['wif [str]'], +# "keyconv_compare_randloop": ['iterations [int]'], +# "hextob58_pad": ['hexnum [str]], +# "b58tohex_pad": ['b58num [str]'], +# "hextob58_pad_randloop": ['iterations [int]'], +# "test_wiftohex": ['wif [str]'], +# "hextosha256": ['hexnum [str]'], +# "hextowiftopubkey": ['hexnum [str]'], +# "pubhextoaddr": ['hexnum [str]'], +# "hextowif_comp": ['hexnum [str]'], +# "wiftohex_comp": ['wif [str]'], +# "privhextoaddr_comp": ['hexnum [str]'], +# "wiftoaddr_comp": ['wif [str]'], + "strtob58": [' [str]'], + "hextob58": [' [str]'], + "b58tohex": [' [str]'], + "b58randenc": [], + "randwif": ['compressed [bool=False]'], + "randpair": ['compressed [bool=False]'], + "wif2addr": [' [str]', 'compressed [bool=False]'], + "hexdump": [' [str]', 'cols [int=8]', 'line_nums [bool=True]'], + "unhexdump": [' [str]'], + "mn_rand128": ['wordlist [str="electrum"]'], + "mn_rand192": ['wordlist [str="electrum"]'], + "mn_rand256": ['wordlist [str="electrum"]'], + "mn_stats": ['wordlist [str="electrum"]'], + "mn_printlist": ['wordlist [str="electrum"]'] +} + +command_help = """ +General operations: +hexdump - encode binary data in formatted hexadecimal form +unhexdump - decode formatted hexadecimal data + +Bitcoin operations: +strtob58 - convert a string to base 58 +hextob58 - convert a hexadecimal number to base 58 +b58tohex - convert a base 58 number to hexadecimal +b58randenc - generate a random 32-byte number and convert it to base 58 +randwif - generate a random private key in WIF format +randpair - generate a random private key/address pair +wif2addr - generate a Bitcoin address from a key in WIF format + +Mnemonic operations (choose "electrum" (default), "tirosh" or "all" wordlists): +mn_rand128 - generate random 128-bit mnemonic +mn_rand192 - generate random 192-bit mnemonic +mn_rand256 - generate random 256-bit mnemonic +mn_stats - show stats for mnemonic wordlist +mn_printlist - print mnemonic wordlist + +IMPORTANT NOTE: Though MMGen mnemonics use the Electrum wordlist, they're +computed using a different algorithm and are NOT Electrum-compatible! +""" + +def tool_usage(prog_name, command): + print "USAGE: '%s %s%s'" % (prog_name, command, + (" "+" ".join(commands[command]) if commands[command] else "")) + +def process_args(prog_name, command, uargs): + cargs = commands[command] + cargs_req = [[i.split(" [")[0],i.split(" [")[1][:-1]] + for i in cargs if "=" not in i] + cargs_nam = dict([[ + i.split(" [")[0], + [i.split(" [")[1].split("=")[0], i.split(" [")[1].split("=")[1][:-1]] + ] for i in cargs if "=" in i]) + uargs_req = [i for i in uargs if "=" not in i] + uargs_nam = dict([i.split("=") for i in uargs if "=" in i]) + +# print cargs_req; print cargs_nam; print uargs_req; print uargs_nam; sys.exit() + + n = len(cargs_req) + if len(uargs_req) != n: + print "ERROR: %s argument%s required" % (n, " is" if n==1 else "s are") + tool_usage(prog_name, command) + sys.exit(1) + + for a in uargs_nam.keys(): + if a not in cargs_nam.keys(): + print "'%s' invalid named argument" % a + sys.exit(1) + + def test_type(arg_type,arg,name=""): + try: + t = type(eval(arg)) + assert(t == eval(arg_type)) + except: + print "'%s': Invalid argument for argument %s ('%s' required)" % \ + (arg, name, arg_type) + sys.exit(1) + return True + + ret = [] + + for i in range(len(cargs_req)): + arg,arg_type = uargs_req[i], cargs_req[i][1] + if arg == "true" or arg == "false": arg = arg.capitalize() + if arg_type == "str": + ret.append('"%s"' % (arg)) + elif test_type(arg_type, arg, "#"+str(i+1)): + ret.append('%s' % (arg)) + + for k in uargs_nam.keys(): + arg,arg_type = uargs_nam[k], cargs_nam[k][0] + if arg == "true" or arg == "false": arg = arg.capitalize() + if arg_type == "str": + ret.append('%s="%s"' % (k, arg)) + elif test_type(arg_type, arg, "'"+k+"'"): + ret.append('%s=%s' % (k, arg)) + + return ret + + +# Individual commands + +def print_convert_results(indata,enc,dec,no_recode=False): + vmsg("Input: [%s]" % indata) + vmsg_r("Encoded data: ["); msg_r(enc); vmsg_r("]"); msg("") + if not no_recode: + vmsg("Recoded data: [%s]" % dec) + if indata != dec: + msg("WARNING! Recoded number doesn't match input stringwise!") + +def hexdump(infile, cols=8, line_nums=True): + data = get_data_from_file(infile) + o = pretty_hexdump(data, 2, cols, line_nums) + print o + +def unhexdump(infile): + data = get_data_from_file(infile) + o = decode_pretty_hexdump(data) + sys.stdout.write(o) + +def strtob58(s): + enc = bitcoin.b58encode(s) + dec = bitcoin.b58decode(enc) + print_convert_results(s,enc,dec) + +def hextob58(s,f_enc=bitcoin.b58encode, f_dec=bitcoin.b58decode): + enc = f_enc(unhexlify(s)) + dec = hexlify(f_dec(enc)) + print_convert_results(s,enc,dec) + +def b58tohex(s,f_enc=bitcoin.b58decode, f_dec=bitcoin.b58encode): + tmp = f_enc(s) + if tmp == False: sys.exit(1) + enc = hexlify(tmp) + dec = f_dec(unhexlify(enc)) + print_convert_results(s,enc,dec) + +def get_random(length): + from Crypto import Random + return Random.new().read(length) + +def b58randenc(): + r = get_random(32) + enc = bitcoin.b58encode(r) + dec = bitcoin.b58decode(enc) + print_convert_results(hexlify(r),enc,hexlify(dec)) + +def randwif(compressed=False): + r_hex = hexlify(get_random(32)) + enc = bitcoin.hextowif(r_hex,compressed) + print_convert_results(r_hex,enc,"",no_recode=True) + +def randpair(compressed=False): + r_hex = hexlify(get_random(32)) + wif = bitcoin.hextowif(r_hex,compressed) + addr = bitcoin.privnum2addr(int(r_hex,16),compressed) + vmsg("Key (hex): %s" % r_hex) + vmsg_r("Key (WIF): "); msg(wif) + vmsg_r("Addr: "); msg(addr) + +def wif2addr(s_in,compressed=False): + s_enc = bitcoin.wiftohex(s_in,compressed) + if s_enc == False: + msg("Invalid address") + sys.exit(1) + addr = bitcoin.privnum2addr(int(s_enc,16),compressed) + vmsg_r("Addr: "); msg(addr) + +from mmgen.mnemonic import * +from mmgen.mn_electrum import electrum_words as el +from mmgen.mn_tirosh import tirosh_words as tl + +wordlists = sorted(wl_checksums.keys()) + +def get_wordlist(wordlist): + wordlist = wordlist.lower() + if wordlist not in wordlists: + msg('"%s": invalid wordlist. Valid choices: %s' % + (wordlist,'"'+'" "'.join(wordlists)+'"')) + sys.exit(1) + return el if wordlist == "electrum" else tl + +def do_random_mn(nbytes,wordlist): + r = get_random(nbytes) + wlists = wordlists if wordlist == "all" else [wordlist] + for wl in wlists: + l = get_wordlist(wl) + if wl == wlists[0]: vmsg("Seed: %s" % hexlify(r)) + mn = get_mnemonic_from_seed(r,l.strip().split("\n"), + wordlist,print_info=False) + vmsg("%s wordlist mnemonic:" % (wl.capitalize())) + print " ".join(mn) + +def mn_rand128(wordlist="electrum"): do_random_mn(16,wordlist) +def mn_rand192(wordlist="electrum"): do_random_mn(24,wordlist) +def mn_rand256(wordlist="electrum"): do_random_mn(32,wordlist) + +def mn_stats(wordlist="electrum"): + l = get_wordlist(wordlist) + check_wordlist(l,wordlist) + +def mn_printlist(wordlist="electrum"): + l = get_wordlist(wordlist) + print "%s" % l.strip() diff --git a/mmgen/tx.py b/mmgen/tx.py index 4c2ad228..ee1e4415 100755 --- a/mmgen/tx.py +++ b/mmgen/tx.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/mmgen/util.py b/mmgen/util.py index 10f36650..fee99430 100755 --- a/mmgen/util.py +++ b/mmgen/util.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -994,5 +994,61 @@ def do_pager(text): else: print text+end +def export_to_hidden_incog(incog_enc,opts): + fname,offset = opts['export_incog_hidden'].split(",") #Already sanity-checked + + check_data_fits_file_at_offset(fname,int(offset),len(incog_enc),"write") + + if not g.quiet: confirm_or_exit("","alter file '%s'" % fname) + f = os.open(fname,os.O_RDWR) + os.lseek(f, int(offset), os.SEEK_SET) + os.write(f, incog_enc) + os.close(f) + qmsg("Data written to file '%s' at offset %s" % (fname,offset), + "Data written to file") + + +def pretty_hexdump(data,gw,cols,line_nums=False): + r = 1 if len(data) % gw else 0 + return "".join( + [ + ("" if (line_nums == False or i % cols) else "%03i: " % (i/cols)) + + hexlify(data[i*gw:i*gw+gw]) + + (" " if (i+1) % cols else "\n") + for i in range(len(data)/gw + r) + ] + ).rstrip() + +def decode_pretty_hexdump(data): + import re + lines = [re.sub('^\d+:\s+','',l) for l in data.split("\n")] + return unhexlify("".join(("".join(lines).split()))) + +def wallet_to_incog_data(infile,opts): + + d = get_data_from_wallet(infile,silent=True) + seed_id,key_id,preset,salt,enc_seed = \ + d[1][0], d[1][1], d[2].split(":")[0], d[3], d[4] + + passwd = get_mmgen_passphrase("Enter mmgen passphrase: ",opts) + key = make_key(passwd, salt, preset, "main key") + # We don't need the seed; just do this to verify password. + if decrypt_seed(enc_seed, key, seed_id, key_id) == False: + sys.exit(2) + + from Crypto import Random + iv = Random.new().read(g.aesctr_iv_len) + iv_id = make_chksum_8(iv) + qmsg("IV ID: %s" % iv_id) + + from binascii import hexlify + from hashlib import sha256 + # IV is used BOTH to initialize counter and to salt password! + key = make_key(passwd, iv, preset, "wrapper key") + wrap_enc = encrypt_seed(salt + enc_seed, key, iv=int(hexlify(iv),16)) + + return iv+wrap_enc,seed_id,key_id,iv_id,preset + + if __name__ == "__main__": print "util.py" diff --git a/mmgen/walletgen.py b/mmgen/walletgen.py index adb1153b..f3b3a02e 100755 --- a/mmgen/walletgen.py +++ b/mmgen/walletgen.py @@ -1,7 +1,7 @@ #!/usr/bin/env python # # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution -# Copyright (C) 2013 by philemon +# Copyright (C) 2013-2014 by philemon # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/setup.py b/setup.py index 278d1475..860153ff 100755 --- a/setup.py +++ b/setup.py @@ -20,6 +20,7 @@ setup( 'mmgen.mn_tirosh', 'mmgen.Opts', 'mmgen.term', + 'mmgen.tool', 'mmgen.tx', 'mmgen.util', 'mmgen.walletgen', @@ -52,5 +53,6 @@ setup( 'mmgen-txsign', 'mmgen-txsend', 'mmgen-pywallet' + 'mmgen-tool', ] )