Online signing, new commands for 'mmgen-tool', code cleanups, bugfixes
This commit is contained in:
parent
08e723c867
commit
490879f968
18 changed files with 467 additions and 323 deletions
|
|
@ -67,6 +67,8 @@ mnemonic or seed or a lost seed from the wallet or mnemonic.
|
|||
|
||||
> #### See [Getting Started with MMGen][3]
|
||||
|
||||
> #### [MMGen command help][6]
|
||||
|
||||
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
||||
|
||||
[**Forum**][4] |
|
||||
|
|
@ -78,3 +80,4 @@ Donate: 15TLdmi5NYLdqmtCqczUs5pBPkJDXRs83w
|
|||
[3]: https://github.com/mmgen/mmgen/wiki/Getting-Started-with-MMGen
|
||||
[4]: https://bitcointalk.org/index.php?topic=567069.0
|
||||
[5]: https://github.com/mmgen/mmgen/wiki/MMGen-Signing-Key
|
||||
[6]: https://github.com/mmgen/mmgen/wiki/cmdhelp/Command-Help
|
||||
|
|
|
|||
|
|
@ -38,36 +38,40 @@ help_data = {
|
|||
mnemonic, seed or password""".format(gen_what,g=g),
|
||||
'usage':"[opts] [infile] <address list>",
|
||||
'options': """
|
||||
-h, --help Print this help message{}
|
||||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-e, --echo-passphrase Echo passphrase or mnemonic to screen upon entry
|
||||
-H, --show-hash-presets Show information on available hash presets
|
||||
-K, --no-keyconv Use internal libraries for address generation
|
||||
instead of 'keyconv'
|
||||
-l, --seed-len= N Length of seed. Options: {seed_lens}
|
||||
(default: {g.seed_len})
|
||||
-p, --hash-preset= p Use scrypt.hash() parameters from preset 'p' when
|
||||
hashing password (default: '{g.hash_preset}')
|
||||
-P, --passwd-file= f Get passphrase from file 'f'
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
-S, --stdout Print {what} to stdout
|
||||
-v, --verbose Produce more verbose output{}
|
||||
-h, --help Print this help message{}
|
||||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-e, --echo-passphrase Echo passphrase or mnemonic to screen upon entry{}
|
||||
-H, --show-hash-presets Show information on available hash presets
|
||||
-K, --no-keyconv Use internal libraries for address generation
|
||||
instead of 'keyconv'
|
||||
-l, --seed-len= N Length of seed. Options: {seed_lens}
|
||||
(default: {g.seed_len})
|
||||
-p, --hash-preset= p Use scrypt.hash() parameters from preset 'p' when
|
||||
hashing password (default: '{g.hash_preset}')
|
||||
-P, --passwd-file= f Get passphrase from file 'f'
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
-S, --stdout Print {what} to stdout
|
||||
-v, --verbose Produce more verbose output{}
|
||||
|
||||
-b, --from-brain= l,p Generate {what} from a user-created password,
|
||||
i.e. a "brainwallet", using seed length 'l' and
|
||||
hash preset 'p' (comma-separated)
|
||||
-g, --from-incog Generate {what} from an incognito wallet
|
||||
-X, --from-incog-hex Generate {what} from incognito hexadecimal wallet
|
||||
-G, --from-incog-hidden= f,o,l Generate {what} from incognito data in file
|
||||
'f' at offset 'o', with seed length of 'l'
|
||||
-m, --from-mnemonic Generate {what} from an electrum-like mnemonic
|
||||
-s, --from-seed Generate {what} from a seed in .{g.seed_ext} format
|
||||
-b, --from-brain= l,p Generate {what} from a user-created password,
|
||||
i.e. a "brainwallet", using seed length 'l' and
|
||||
hash preset 'p' (comma-separated)
|
||||
-g, --from-incog Generate {what} from an incognito wallet
|
||||
-X, --from-incog-hex Generate {what} from incognito hexadecimal wallet
|
||||
-G, --from-incog-hidden=f,o,l Generate {what} from incognito data in file
|
||||
'f' at offset 'o', with seed length of 'l'
|
||||
-m, --from-mnemonic Generate {what} from an electrum-like mnemonic
|
||||
-s, --from-seed Generate {what} from a seed in .{g.seed_ext} format
|
||||
""".format(
|
||||
*(
|
||||
("\n-A, --no-addresses Print only secret keys, no addresses",
|
||||
"\n-x, --b16 Print secret keys in hexadecimal too")
|
||||
if gen_what == "keys" else ("","")),
|
||||
(
|
||||
"\n-A, --no-addresses Print only secret keys, no addresses",
|
||||
"\n-f, --flat-list Produce a flat list of keys suitable for use with" +
|
||||
"\n 'mmgen-txsign'",
|
||||
"\n-x, --b16 Print secret keys in hexadecimal too"
|
||||
)
|
||||
if gen_what == "keys" else ("","","")),
|
||||
seed_lens=", ".join([str(i) for i in g.seed_lens]),
|
||||
what=gen_what, g=g
|
||||
),
|
||||
|
|
@ -160,9 +164,9 @@ if 'stdout' in opts:
|
|||
if gen_what == "keys" and not g.quiet:
|
||||
confirm = True
|
||||
else: confirm = False
|
||||
write_to_stdout(addr_data_str,"secret keys",confirm)
|
||||
write_to_stdout(addr_data_str,gen_what,confirm)
|
||||
elif not sys.stdout.isatty():
|
||||
write_to_stdout(addr_data_str,"secret keys",confirm=False)
|
||||
write_to_stdout(addr_data_str,gen_what,confirm=False)
|
||||
else:
|
||||
write_addr_data_to_file(seed, addr_data_str, addr_idxs, opts)
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,8 @@ from mmgen.tx import connect_to_bitcoind,parse_addrs_file
|
|||
|
||||
help_data = {
|
||||
'prog_name': sys.argv[0].split("/")[-1],
|
||||
'desc': """Import addresses (both mmgen and non-mmgen) into a bitcoind
|
||||
watching wallet""",
|
||||
'desc': """Import addresses (both {pnm} and non-{pnm}) into a bitcoind
|
||||
watching wallet""".format(pnm=g.proj_name),
|
||||
'usage':"[opts] [mmgen address file]",
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
|
|
|
|||
|
|
@ -30,18 +30,20 @@ help_data = {
|
|||
deterministic wallet""".format(g.proj_name),
|
||||
'usage': "[opts] [filename]",
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-H, --show-hash-presets Show information on available hash presets
|
||||
-k, --keep-old-passphrase Keep old passphrase (use when changing hash
|
||||
strength or label only)
|
||||
-L, --label= l Change the wallet's label to 'l'
|
||||
-p, --hash-preset= p Change scrypt.hash() parameters to preset 'p'
|
||||
(default: '{g.hash_preset}')
|
||||
-P, --passwd-file= f Get new passphrase from file 'f'
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
-v, --verbose Produce more verbose output
|
||||
-h, --help Print this help message
|
||||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-H, --show-hash-presets Show information on available hash presets
|
||||
-k, --keep-old-passphrase Keep old passphrase (use when changing hash
|
||||
strength or label only)
|
||||
-L, --label= l Change the wallet's label to 'l'
|
||||
-p, --hash-preset= p Change scrypt.hash() parameters to preset 'p'
|
||||
(default: '{g.hash_preset}')
|
||||
-P, --passwd-file= f Get new passphrase from file 'f'
|
||||
-r, --usr-randchars= n Get 'n' characters of additional randomness from
|
||||
user (min={g.min_urandchars}, max={g.max_urandchars})
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
-v, --verbose Produce more verbose output
|
||||
""".format(g=g),
|
||||
'notes': """
|
||||
|
||||
|
|
@ -110,9 +112,8 @@ if 'preset' in changed or 'passwd' in changed: # Update key ID, salt
|
|||
qmsg("Will update salt and key ID")
|
||||
|
||||
from hashlib import sha256
|
||||
from Crypto import Random
|
||||
|
||||
salt = sha256(salt + Random.new().read(128)).digest()[:g.salt_len]
|
||||
salt = sha256(salt + get_random(128,opts)).digest()[:g.salt_len]
|
||||
key = make_key(passwd, salt, opts['hash_preset'])
|
||||
new_key_id = make_chksum_8(key)
|
||||
qmsg("Key ID changed: %s -> %s" % (key_id,new_key_id))
|
||||
|
|
|
|||
27
mmgen-tool
27
mmgen-tool
|
|
@ -25,7 +25,7 @@ from hashlib import sha256
|
|||
from mmgen.Opts import *
|
||||
import mmgen.config as g
|
||||
from mmgen.util import pretty_hexdump
|
||||
from mmgen.tool import *
|
||||
import mmgen.tool as tool
|
||||
prog_name = sys.argv[0].split("/")[-1]
|
||||
|
||||
help_data = {
|
||||
|
|
@ -33,16 +33,18 @@ help_data = {
|
|||
'desc': "Perform various BTC-related operations",
|
||||
'usage': "[opts] <command> <args>",
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
-q, --quiet Produce quieter output
|
||||
-v, --verbose Produce more verbose output
|
||||
""",
|
||||
-h, --help Print this help message
|
||||
-q, --quiet Produce quieter output
|
||||
-r, --usr-randchars=n Get 'n' characters of additional randomness from
|
||||
user (min={g.min_urandchars}, max={g.max_urandchars})
|
||||
-v, --verbose Produce more verbose output
|
||||
""".format(g=g),
|
||||
'notes': """
|
||||
|
||||
COMMANDS:{}
|
||||
Type '{} <command> --help for usage information on a particular
|
||||
command
|
||||
""".format(command_help,prog_name)
|
||||
""".format(tool.command_help,prog_name)
|
||||
}
|
||||
|
||||
opts,cmd_args = parse_opts(sys.argv,help_data)
|
||||
|
|
@ -53,17 +55,20 @@ 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():
|
||||
command = cmd_args.pop(0)
|
||||
|
||||
if command not in tool.commands.keys():
|
||||
msg("'%s': No such command" % command)
|
||||
sys.exit(1)
|
||||
|
||||
if cmd_args and cmd_args[0] == '--help':
|
||||
tool_usage(prog_name, command)
|
||||
tool.tool_usage(prog_name, command)
|
||||
sys.exit(0)
|
||||
|
||||
args = process_args(prog_name, command, cmd_args)
|
||||
args = tool.process_args(prog_name, command, cmd_args)
|
||||
|
||||
tool.opts = opts
|
||||
|
||||
#print command + "(" + ", ".join(args) + ")"
|
||||
eval(command + "(" + ", ".join(args) + ")")
|
||||
eval("tool." + command + "(" + ", ".join(args) + ")")
|
||||
|
|
|
|||
|
|
@ -36,13 +36,13 @@ help_data = {
|
|||
'desc': "Create a BTC transaction with outputs to specified addresses",
|
||||
'usage': "[opts] <addr,amt> ... [change addr] [addr file] ...",
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-e, --echo-passphrase Print passphrase to screen when typing it
|
||||
-f, --tx-fee= f Transaction fee (default: {g.tx_fee} BTC)
|
||||
-i, --info Display unspent outputs and exit
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
-h, --help Print this help message
|
||||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-e, --echo-passphrase Print passphrase to screen when typing it
|
||||
-f, --tx-fee= f Transaction fee (default: {g.tx_fee} BTC)
|
||||
-i, --info Display unspent outputs and exit
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
""".format(g=g),
|
||||
'notes': """
|
||||
|
||||
|
|
@ -52,12 +52,12 @@ via an interactive menu.
|
|||
Ages of transactions are approximate based on an average block creation
|
||||
interval of {g.mins_per_block} minutes.
|
||||
|
||||
Addresses on the command line can be Bitcoin addresses or MMGen addresses
|
||||
Addresses on the command line can be Bitcoin addresses or {pnm} addresses
|
||||
of the form <seed ID>:<number>.
|
||||
|
||||
To send all inputs (minus TX fee) to a single output, specify one address
|
||||
with no amount on the command line.
|
||||
""".format(g=g)
|
||||
""".format(g=g,pnm=g.proj_name)
|
||||
}
|
||||
|
||||
opts,cmd_args = parse_opts(sys.argv,help_data)
|
||||
|
|
|
|||
|
|
@ -34,9 +34,9 @@ help_data = {
|
|||
'desc': "Send a Bitcoin transaction signed by mmgen-txsign",
|
||||
'usage': "[opts] <signed transaction file>",
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-q, --quiet Suppress warnings; overwrite files without prompting
|
||||
-h, --help Print this help message
|
||||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-q, --quiet Suppress warnings; overwrite files without prompting
|
||||
"""
|
||||
}
|
||||
|
||||
|
|
|
|||
72
mmgen-txsign
72
mmgen-txsign
|
|
@ -30,14 +30,19 @@ from mmgen.util import msg,qmsg
|
|||
help_data = {
|
||||
'prog_name': sys.argv[0].split("/")[-1],
|
||||
'desc': "Sign Bitcoin transactions generated by mmgen-txcreate",
|
||||
'usage': "[opts] <transaction file>,.. [mmgen wallet/seed/words/brainwallet file]...",
|
||||
'usage': "[opts] <transaction file> .. [mmgen wallet/seed/words/brainwallet file] .. [addrfile] ..",
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-e, --echo-passphrase Print passphrase to screen when typing it
|
||||
-i, --info Display information about the transaction and exit
|
||||
-I, --tx-id Display transaction ID and exit
|
||||
-k, --keys-from-file= k Provide additional key data from file 'k'
|
||||
-k, --keys-from-file= f Provide additional keys for non-{pnm} addresses
|
||||
-K, --all-keys-from-file=f Like '-k', only use the keyfile as key source
|
||||
for ALL inputs, including {pnm} ones. Can be used
|
||||
for online signing without an {pnm} seed source.
|
||||
{pnm}-to-BTC mappings can optionally be verified
|
||||
using address file(s) listed on the command line
|
||||
-P, --passwd-file= f Get passphrase from file 'f'
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
|
|
@ -49,37 +54,37 @@ help_data = {
|
|||
-g, --from-incog Generate keys from an incognito wallet
|
||||
-X, --from-incog-hex Generate keys from an incognito hexadecimal wallet
|
||||
-G, --from-incog-hidden= f,o,l Generate keys from incognito data in file
|
||||
'f' at offset 'o', with seed length of 'l'
|
||||
'f' at offset 'o', with seed length of 'l'
|
||||
-m, --from-mnemonic Generate keys from an electrum-like mnemonic
|
||||
-s, --from-seed Generate keys from a seed in .{g.seed_ext} format
|
||||
""".format(g=g),
|
||||
""".format(g=g,pnm=g.proj_name),
|
||||
'notes': """
|
||||
|
||||
Transactions with either mmgen or non-mmgen input addresses may be signed.
|
||||
For non-mmgen inputs, the bitcoind wallet.dat is used as the key source.
|
||||
For mmgen inputs, key data is generated from your seed as with the
|
||||
Transactions with either {pnm} or non-{pnm} input addresses may be signed.
|
||||
For non-{pnm} inputs, the bitcoind wallet.dat is used as the key source.
|
||||
For {pnm} inputs, key data is generated from your seed as with the
|
||||
mmgen-addrgen and mmgen-keygen utilities.
|
||||
|
||||
Data for the --from-<what> options will be taken from a file if a second
|
||||
file is specified on the command line. Otherwise, the user will be
|
||||
prompted to enter the data.
|
||||
|
||||
In cases of transactions with mixed mmgen and non-mmgen inputs, non-mmgen
|
||||
In cases of transactions with mixed {pnm} and non-{pnm} inputs, non-{pnm}
|
||||
keys must be supplied in a separate file (WIF format, one key per line)
|
||||
using the '--keys-from-file' option. Alternatively, one may get keys from
|
||||
a running bitcoind using the '--force-wallet-dat' option. First import the
|
||||
required mmgen keys using 'bitcoind importprivkey'.
|
||||
required {pnm} keys using 'bitcoind importprivkey'.
|
||||
|
||||
For transaction outputs that are MMGen addresses, MMGen-to-Bitcoin address
|
||||
For transaction outputs that are {pnm} addresses, {pnm}-to-Bitcoin address
|
||||
mappings are verified. Therefore, seed material for these addresses must
|
||||
be supplied on the command line.
|
||||
be supplied on the command line (but see '--all-keys-from-file').
|
||||
|
||||
Seed data supplied in files must have the following extensions:
|
||||
wallet: '.{g.wallet_ext}'
|
||||
seed: '.{g.seed_ext}'
|
||||
mnemonic: '.{g.mn_ext}'
|
||||
brainwallet: '.{g.brain_ext}'
|
||||
""".format(g=g)
|
||||
""".format(g=g,pnm=g.proj_name)
|
||||
}
|
||||
|
||||
opts,infiles = parse_opts(sys.argv,help_data)
|
||||
|
|
@ -87,6 +92,9 @@ opts,infiles = parse_opts(sys.argv,help_data)
|
|||
if "quiet" in opts: g.quiet = True
|
||||
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)
|
||||
|
|
@ -94,13 +102,16 @@ for i in infiles: check_infile(i)
|
|||
c = connect_to_bitcoind()
|
||||
|
||||
saved_seeds = {}
|
||||
tx_files = [i for i in set(infiles) if get_extension(i) == g.rawtx_ext]
|
||||
infiles = list(set(infiles) - set(tx_files))
|
||||
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))
|
||||
|
||||
if not "info" in opts: do_license_msg(immed=True)
|
||||
|
||||
keys_from_file = get_lines_from_file(opts['keys_from_file'],"key data",
|
||||
remove_comments=True) if 'keys_from_file' in opts else []
|
||||
if 'keys_from_file' in opts:
|
||||
keys_from_file = get_lines_from_file(opts['keys_from_file'],"key data",
|
||||
remove_comments=True)
|
||||
else: keys_from_file = []
|
||||
|
||||
for tx_file in tx_files:
|
||||
m = "" if 'tx_id' in opts else "transaction data"
|
||||
|
|
@ -118,21 +129,30 @@ for tx_file in tx_files:
|
|||
sys.exit(0)
|
||||
|
||||
# Are inputs mmgen addresses?
|
||||
mmgen_addrs = [i for i in inputs_data if parse_mmgen_label(i['account'])[0]]
|
||||
other_addrs = [i for i in inputs_data if not parse_mmgen_label(i['account'])[0]]
|
||||
mmgen_inputs = [i for i in inputs_data if parse_mmgen_label(i['account'])[0]]
|
||||
other_inputs = [i for i in inputs_data if not parse_mmgen_label(i['account'])[0]]
|
||||
|
||||
if 'all_keys_from_file' in opts: other_inputs = inputs_data
|
||||
|
||||
keys = keys_from_file
|
||||
|
||||
if other_addrs and not keys and not 'use_wallet_dat' in opts:
|
||||
missing_keys_errormsg(other_addrs)
|
||||
if other_inputs and not keys and not 'use_wallet_dat' in opts:
|
||||
missing_keys_errormsg(other_inputs)
|
||||
sys.exit(2)
|
||||
|
||||
if other_addrs and keys and not 'skip_key_preverify' in opts:
|
||||
a = [i['address'] for i in other_addrs]
|
||||
if other_inputs and keys and not 'skip_key_preverify' in opts:
|
||||
a = [i['address'] for i in other_inputs]
|
||||
preverify_keys(a, keys)
|
||||
opts['skip_key_preverify'] = True
|
||||
|
||||
check_mmgen_to_btc_addr_mappings(inputs_data,b2m_map,infiles,saved_seeds,opts)
|
||||
if 'all_keys_from_file' in opts:
|
||||
if addrfiles:
|
||||
check_mmgen_to_btc_addr_mappings_addrfile(mmgen_inputs,b2m_map,addrfiles)
|
||||
else:
|
||||
confirm_or_exit(txmsg['skip_mapping_checks_warning'],"continue")
|
||||
else:
|
||||
check_mmgen_to_btc_addr_mappings(
|
||||
mmgen_inputs,b2m_map,infiles,saved_seeds,opts)
|
||||
|
||||
if len(tx_files) > 1:
|
||||
msg("\nTransaction %s/%s:" % (tx_files.index(tx_file)+1,len(tx_files)))
|
||||
|
|
@ -147,15 +167,15 @@ for tx_file in tx_files:
|
|||
{"txid":i['txid'],"vout":i['vout'],"scriptPubKey":i['scriptPubKey']}
|
||||
for i in inputs_data]
|
||||
|
||||
if mmgen_addrs:
|
||||
ml = [i['account'].split()[0] for i in mmgen_addrs]
|
||||
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)
|
||||
|
||||
if 'use_wallet_dat' in opts:
|
||||
sig_tx = sign_tx_with_bitcoind_wallet(c,tx_hex,sig_data,keys,opts)
|
||||
else:
|
||||
sig_tx = sign_transaction(c,tx_hex,sig_data,keys)
|
||||
elif other_addrs:
|
||||
elif other_inputs:
|
||||
if keys:
|
||||
sig_tx = sign_transaction(c,tx_hex,sig_data,keys)
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -37,14 +37,24 @@ help_data = {
|
|||
-e, --echo-passphrase Print passphrase to screen when typing it
|
||||
-P, --passwd-file= f Get passphrase from file 'f'
|
||||
-q, --quiet Suppress warnings; overwrite files without prompting
|
||||
-r, --usr-randchars= n Get 'n' characters of additional randomness from
|
||||
user (min={g.min_urandchars}, max={g.max_urandchars})
|
||||
-S, --stdout Print seed or mnemonic data to standard output
|
||||
-v, --verbose Produce more verbose output
|
||||
-g, --export-incog Export wallet to incognito format
|
||||
-X, --export-incog-hex Export wallet to incognito hexadecimal format
|
||||
-G, --export-incog-hidden= f,o Hide incognito data in existing file 'f'
|
||||
at offset 'o' (comma-separated)
|
||||
-G, --export-incog-hidden=f,o Hide incognito data in existing file 'f'
|
||||
at offset 'o' (comma-separated)
|
||||
-m, --export-mnemonic Export the wallet's mnemonic to file
|
||||
-s, --export-seed Export the wallet's seed to file
|
||||
""".format(g=g),
|
||||
'notes': """
|
||||
|
||||
Since good randomness is particularly important for incognito wallets,
|
||||
the '--usr-randchars' option is turned on by default to gather additional
|
||||
entropy from the user when one of the '--export-incog*' options is
|
||||
selected. If you fully trust your OS's random number generator and wish
|
||||
to disable this option, then specify '-r0' on the command line.
|
||||
"""
|
||||
}
|
||||
|
||||
|
|
@ -64,6 +74,7 @@ if 'export_mnemonic' in opts:
|
|||
elif 'export_seed' in opts:
|
||||
qmsg("Exporting seed data to file by user request")
|
||||
elif 'export_incog' in opts:
|
||||
if opts['usr_randchars'] == -1: opts['usr_randchars'] = g.usr_randchars_dfl
|
||||
qmsg("Exporting wallet to incognito format by user request")
|
||||
incog_enc,seed_id,key_id,iv_id,preset = \
|
||||
wallet_to_incog_data(cmd_args[0],opts)
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ help_data = {
|
|||
-P, --passwd-file= f Get passphrase from file 'f'
|
||||
-q, --quiet Produce quieter output; overwrite files without
|
||||
prompting
|
||||
-u, --usr-randlen= n Get 'n' characters of randomness from the user
|
||||
(default: {g.usr_randlen})
|
||||
-r, --usr-randchars= n Get 'n' characters of additional randomness from
|
||||
user (min={g.min_urandchars}, max={g.max_urandchars})
|
||||
-v, --verbose Produce more verbose output
|
||||
|
||||
-b, --from-brain= l,p Generate wallet from a user-created passphrase,
|
||||
|
|
@ -71,6 +71,11 @@ trailing space are ignored. This permits reading passphrase data from a
|
|||
multi-line file with free spacing and indentation. This is particularly
|
||||
convenient for long brainwallet passphrases, for example.
|
||||
|
||||
Since good randomness is particularly important when generating wallets,
|
||||
the '--usr-randchars' option is turned on by default to gather additional
|
||||
entropy from the user. If you fully trust your OS's random number gener-
|
||||
ator and wish to disable this option, specify '-r0' on the command line.
|
||||
|
||||
BRAINWALLET NOTE:
|
||||
|
||||
As brainwallets require especially strong hashing to thwart dictionary
|
||||
|
|
@ -94,6 +99,7 @@ opts,cmd_args = parse_opts(sys.argv,help_data)
|
|||
if 'quiet' in opts: g.quiet = True
|
||||
if 'verbose' in opts: g.verbose = True
|
||||
if 'show_hash_presets' in opts: show_hash_presets()
|
||||
if opts['usr_randchars'] == -1: opts['usr_randchars'] = g.usr_randchars_dfl
|
||||
|
||||
if g.debug: show_opts_and_cmd_args(opts,cmd_args)
|
||||
|
||||
|
|
@ -116,31 +122,6 @@ else: usage(help_data)
|
|||
|
||||
do_license_msg()
|
||||
|
||||
qmsg_r("Acquiring random data from your computer...")
|
||||
|
||||
from time import sleep
|
||||
sleep(0.25)
|
||||
|
||||
try:
|
||||
from Crypto import Random
|
||||
r = Random.new()
|
||||
os_rand_data = (r.read(256),r.read(128))
|
||||
except:
|
||||
msg("FAILED\nUnable to generate random numbers. Exiting")
|
||||
sys.exit(2)
|
||||
|
||||
qmsg("OK")
|
||||
|
||||
if g.debug: display_os_random_data(os_rand_data)
|
||||
|
||||
usr_keys,key_timings = get_random_data_from_user(opts)
|
||||
qmsg("")
|
||||
|
||||
if g.debug: display_user_random_data(usr_keys,key_timings)
|
||||
|
||||
usr_rand_data = sha256(usr_keys).digest() + \
|
||||
sha256("".join(key_timings)).digest()
|
||||
|
||||
if 'from_brain' in opts and not g.quiet:
|
||||
confirm_or_exit(cmessages['brain_warning'].format(
|
||||
g.proj_name, *get_from_brain_opt_params(opts)),
|
||||
|
|
@ -155,18 +136,11 @@ for i in 'from_mnemonic','from_brain','from_seed','from_incog':
|
|||
break
|
||||
else:
|
||||
# Truncate random data for smaller seed lengths
|
||||
seed = os_rand_data[0] + usr_rand_data
|
||||
seed = sha256(seed).digest()[:opts['seed_len']/8]
|
||||
seed = sha256(get_random(128,opts)).digest()[:opts['seed_len']/8]
|
||||
|
||||
salt = os_rand_data[1] + usr_rand_data
|
||||
salt = sha256(salt).digest()[:g.salt_len]
|
||||
salt = sha256(get_random(128,opts)).digest()[:g.salt_len]
|
||||
|
||||
qmsg("""
|
||||
Now you must choose a passphrase to encrypt the wallet with. A key will be
|
||||
generated from your passphrase using a hash preset of '%s'. Please note that
|
||||
no strength checking of passphrases is performed. For an empty passphrase,
|
||||
just hit ENTER twice.
|
||||
""".strip() % opts['hash_preset'])
|
||||
qmsg(cmessages['choose_wallet_passphrase'] % opts['hash_preset'])
|
||||
|
||||
passwd = get_new_passphrase("{} wallet passphrase".format(g.proj_name), opts)
|
||||
|
||||
|
|
|
|||
|
|
@ -83,27 +83,38 @@ def parse_opts(argv,help_data):
|
|||
|
||||
lines = help_data['options'].strip().split("\n")
|
||||
import re
|
||||
pat = r"^-([a-zA-Z0-9]), --([a-zA-Z0-9-]{1,64})(=*) (.*)"
|
||||
opt_data = [m.groups() for m in [re.match(pat,l) for l in lines] if m]
|
||||
pat = r"^-([a-zA-Z0-9]), --([a-zA-Z0-9-]{1,64})(=| )(.+)"
|
||||
rep = r"-{0}, --{1}{w}{3}"
|
||||
opt_data = [list(m.groups()) for m in [re.match(pat,l) for l in lines] if m]
|
||||
# for o in opt_data: print o
|
||||
# sys.exit()
|
||||
|
||||
short_opts = "".join([d[0]+(":" if d[2] else "") for d in opt_data if d])
|
||||
long_opts = [d[1].replace("-","_")+d[2] for d in opt_data if d]
|
||||
for d in opt_data:
|
||||
if d[2] == " ": d[2] = ""
|
||||
short_opts = "".join([d[0]+d[2].replace("=",":") for d in opt_data])
|
||||
long_opts = [d[1].replace("-","_")+d[2] for d in opt_data]
|
||||
help_data['options'] = "\n".join(
|
||||
["-{0}, --{1}{w} {3}".format(w=" " if m.group(3) else "", *m.groups())
|
||||
[rep.format(w=" ", *m.groups())
|
||||
if m else k for m,k in [(re.match(pat,l),l) for l in lines]]
|
||||
)
|
||||
opts,infiles = process_opts(argv,help_data,short_opts,long_opts)
|
||||
|
||||
if g.debug: print "processed user opts: %s" % opts
|
||||
# check_opts() doesn't touch opts[]
|
||||
if not check_opts(opts,long_opts): sys.exit(1)
|
||||
|
||||
if not check_opts(opts,long_opts): sys.exit(1) # MMGen only!
|
||||
# If unset, set these to the default values in mmgen.config:
|
||||
for v in g.cl_override_vars:
|
||||
if v in opts: typeconvert_override_var(opts,v)
|
||||
else: opts[v] = eval("g."+v)
|
||||
|
||||
if g.debug: print "processed opts: %s" % opts
|
||||
|
||||
return opts,infiles
|
||||
|
||||
|
||||
def show_opts_and_cmd_args(opts,cmd_args):
|
||||
print "Processed options: %s" % repr(opts)
|
||||
print "Cmd args: %s" % repr(cmd_args)
|
||||
print "Processed options: %s" % repr(opts)
|
||||
print "Cmd args: %s" % repr(cmd_args)
|
||||
|
||||
|
||||
# Everything below here is MMGen-specific:
|
||||
|
|
@ -112,17 +123,13 @@ from mmgen.util import msg,check_infile
|
|||
|
||||
def check_opts(opts,long_opts):
|
||||
|
||||
# These must be set to the default values in mmgen.config:
|
||||
for i in g.cl_override_vars:
|
||||
if i+"=" in long_opts:
|
||||
set_if_unset_and_typeconvert(opts,i)
|
||||
|
||||
for opt,val in opts.items():
|
||||
|
||||
what = "parameter for '--%s' option" % opt.replace("_","-")
|
||||
|
||||
# Check for file existence and readability
|
||||
if opt in ('keys_from_file','addrlist','passwd_file','keysforaddrs'):
|
||||
if opt in ('keys_from_file','all_keys_from_file','addrlist',
|
||||
'passwd_file','keysforaddrs'):
|
||||
check_infile(val) # exits on error
|
||||
continue
|
||||
|
||||
|
|
@ -233,10 +240,14 @@ def check_opts(opts,long_opts):
|
|||
msg("'%s': invalid %s. Options: %s"
|
||||
% (val,what,", ".join(sorted(g.hash_presets.keys()))))
|
||||
return False
|
||||
elif opt == 'usr_randlen':
|
||||
if val > g.max_randlen or val < g.min_randlen:
|
||||
msg("'%s': invalid %s (must be >= %s and <= %s)"
|
||||
% (val,what,g.min_randlen,g.max_randlen))
|
||||
elif opt == 'usr_randchars':
|
||||
try: v = int(val)
|
||||
except:
|
||||
msg("'%s': invalid value for %s (not an integer)" % (val,what))
|
||||
return False
|
||||
if v != 0 and not (g.min_urandchars <= v <= g.max_urandchars):
|
||||
msg("'%s': invalid %s (must be >= %s and <= %s (or zero))"
|
||||
% (v,what,g.min_urandchars,g.max_urandchars))
|
||||
return False
|
||||
else:
|
||||
if g.debug: print "check_opts(): No test for opt '%s'" % opt
|
||||
|
|
@ -244,22 +255,18 @@ def check_opts(opts,long_opts):
|
|||
return True
|
||||
|
||||
|
||||
def set_if_unset_and_typeconvert(opts,opt):
|
||||
def typeconvert_override_var(opts,opt):
|
||||
|
||||
if opt in g.cl_override_vars:
|
||||
if opt not in opts:
|
||||
# Set to similarly named default value in mmgen.config
|
||||
opts[opt] = eval("g."+opt)
|
||||
else:
|
||||
vtype = type(eval("g."+opt))
|
||||
if g.debug: print "Opt: %s, Type: %s" % (opt,vtype)
|
||||
if vtype == int: f,t = int,"an integer"
|
||||
elif vtype == str: f,t = str,"a string"
|
||||
elif vtype == float: f,t = float,"a float"
|
||||
vtype = type(eval("g."+opt))
|
||||
if g.debug: print "Override opt: %-15s [%s]" % (opt,vtype)
|
||||
|
||||
try:
|
||||
opts[opt] = f(opts[opt])
|
||||
except:
|
||||
msg("'%s': invalid parameter for '--%s' option (not %s)" %
|
||||
(opts[opt],opt.replace("_","-"),t))
|
||||
sys.exit(1)
|
||||
if vtype == int: f,t = int,"an integer"
|
||||
elif vtype == str: f,t = str,"a string"
|
||||
elif vtype == float: f,t = float,"a float"
|
||||
|
||||
try:
|
||||
opts[opt] = f(opts[opt])
|
||||
except:
|
||||
msg("'%s': invalid parameter for '--%s' option (not %s)" %
|
||||
(opts[opt],opt.replace("_","-"),t))
|
||||
sys.exit(1)
|
||||
|
|
|
|||
|
|
@ -93,11 +93,11 @@ def generate_addrs(seed, addrnums, opts):
|
|||
if g.debug:
|
||||
print "Privkey round %s:\n hex: %s\n wif: %s" % (i, sec, wif)
|
||||
|
||||
el = { 'num': i }
|
||||
d = { 'num': i }
|
||||
|
||||
if not 'print_addresses_only' in opts:
|
||||
el['sec'] = sec
|
||||
el['wif'] = wif
|
||||
d['sec'] = sec
|
||||
d['wif'] = wif
|
||||
|
||||
if not 'no_addresses' in opts:
|
||||
if keyconv:
|
||||
|
|
@ -107,9 +107,9 @@ def generate_addrs(seed, addrnums, opts):
|
|||
else:
|
||||
addr = privnum2addr(int(sec,16))
|
||||
|
||||
el['addr'] = addr
|
||||
d['addr'] = addr
|
||||
|
||||
out.append(el)
|
||||
out.append(d)
|
||||
|
||||
w = opts['gen_what']
|
||||
if t_addrs == 1:
|
||||
|
|
@ -127,6 +127,10 @@ def generate_keys(seed, addrnums):
|
|||
|
||||
def format_addr_data(addr_data, addr_data_chksum, seed_id, addr_idxs, opts):
|
||||
|
||||
if 'flat_list' in opts:
|
||||
return "\n\n".join(["# %s:%s %s\n%s" % (seed_id,d['num'],d['addr'],d['wif'])
|
||||
for d in addr_data])+"\n\n"
|
||||
|
||||
start = addr_data[0]['num']
|
||||
end = addr_data[-1]['num']
|
||||
|
||||
|
|
@ -140,35 +144,35 @@ def format_addr_data(addr_data, addr_data_chksum, seed_id, addr_idxs, opts):
|
|||
(5 if 'print_secret' in opts else 1) + len(wif_msg)
|
||||
)
|
||||
|
||||
data = []
|
||||
if not 'stdout' in opts: data.append(addrmsgs['addrfile_header'] + "\n")
|
||||
data.append("# Address data checksum for {}[{}]: {}".format(
|
||||
out = []
|
||||
if not 'stdout' in opts: out.append(addrmsgs['addrfile_header'] + "\n")
|
||||
out.append("# Address data checksum for {}[{}]: {}".format(
|
||||
seed_id, fmt_addr_idxs(addr_idxs), addr_data_chksum))
|
||||
data.append("# Record this value to a secure location\n")
|
||||
data.append("%s {" % seed_id.upper())
|
||||
out.append("# Record this value to a secure location\n")
|
||||
out.append("%s {" % seed_id.upper())
|
||||
|
||||
for el in addr_data:
|
||||
col1 = el['num']
|
||||
for d in addr_data:
|
||||
col1 = d['num']
|
||||
if 'no_addresses' in opts:
|
||||
if 'b16' in opts:
|
||||
data.append(fa % (col1, " (hex):", el['sec']))
|
||||
out.append(fa % (col1, " (hex):", d['sec']))
|
||||
col1 = ""
|
||||
data.append(fa % (col1, " (wif):", el['wif']))
|
||||
if 'b16' in opts: data.append("")
|
||||
out.append(fa % (col1, " (wif):", d['wif']))
|
||||
if 'b16' in opts: out.append("")
|
||||
elif 'print_secret' in opts:
|
||||
if 'b16' in opts:
|
||||
data.append(fa % (col1, "sec (hex):", el['sec']))
|
||||
out.append(fa % (col1, "sec (hex):", d['sec']))
|
||||
col1 = ""
|
||||
data.append(fa % (col1, "sec"+wif_msg+":", el['wif']))
|
||||
data.append(fa % ("", "addr:", el['addr']))
|
||||
data.append("")
|
||||
out.append(fa % (col1, "sec"+wif_msg+":", d['wif']))
|
||||
out.append(fa % ("", "addr:", d['addr']))
|
||||
out.append("")
|
||||
else:
|
||||
data.append(fa % (col1, "", el['addr']))
|
||||
out.append(fa % (col1, "", d['addr']))
|
||||
|
||||
if not data[-1]: data.pop()
|
||||
data.append("}")
|
||||
if not out[-1]: out.pop()
|
||||
out.append("}")
|
||||
|
||||
return "\n".join(data) + "\n"
|
||||
return "\n".join(out) + "\n"
|
||||
|
||||
|
||||
def fmt_addr_idxs(addr_idxs):
|
||||
|
|
@ -193,6 +197,7 @@ def write_addr_data_to_file(seed, addr_data_str, addr_idxs, opts):
|
|||
|
||||
if 'print_addresses_only' in opts: ext = g.addrfile_ext
|
||||
elif 'no_addresses' in opts: ext = g.keyfile_ext
|
||||
elif 'flat_list' in opts: ext = g.keylist_ext
|
||||
else: ext = "akeys"
|
||||
|
||||
if 'b16' in opts: ext = ext.replace("keys","xkeys")
|
||||
|
|
|
|||
|
|
@ -46,11 +46,13 @@ rawtx_ext = "raw"
|
|||
sigtx_ext = "sig"
|
||||
addrfile_ext = "addrs"
|
||||
keyfile_ext = "keys"
|
||||
keylist_ext = "keylist"
|
||||
mmenc_ext = "mmenc"
|
||||
|
||||
default_wl = "electrum"
|
||||
#default_wl = "tirosh"
|
||||
|
||||
cl_override_vars = 'seed_len','hash_preset','usr_randlen'
|
||||
cl_override_vars = 'seed_len','hash_preset','usr_randchars'
|
||||
|
||||
seed_lens = 128,192,256
|
||||
seed_len = 256
|
||||
|
|
@ -68,8 +70,8 @@ bogus_wallet_data = getenv("MMGEN_BOGUS_WALLET_DATA")
|
|||
|
||||
mins_per_block = 8.5
|
||||
passwd_max_tries = 5
|
||||
max_randlen,min_randlen = 80,5
|
||||
usr_randlen = 20
|
||||
usr_randchars,usr_randchars_dfl = -1,30 # see get_random()
|
||||
max_urandchars,min_urandchars = 80,10
|
||||
salt_len = 16
|
||||
aesctr_iv_len = 16
|
||||
|
||||
|
|
@ -97,6 +99,7 @@ max_addr_label_len = 32
|
|||
wallet_label_symbols = addr_label_symbols
|
||||
max_wallet_label_len = 32
|
||||
|
||||
user_entropy = ""
|
||||
#addr_label_punc = ".","_",",","-"," ","(",")"
|
||||
#addr_label_symbols = tuple(ascii_letters + digits) + addr_label_punc
|
||||
#wallet_label_punc = addr_label_punc
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import sys
|
|||
import mmgen.bitcoin as bitcoin
|
||||
import binascii as ba
|
||||
|
||||
import mmgen.config as g
|
||||
from mmgen.util import *
|
||||
from mmgen.tx import *
|
||||
|
||||
|
|
@ -33,6 +34,7 @@ def Vmsg(s):
|
|||
def Vmsg_r(s):
|
||||
if g.verbose: sys.stdout.write(s)
|
||||
|
||||
opts = {}
|
||||
commands = {
|
||||
"strtob58": ['<string> [str]'],
|
||||
"hextob58": ['<hex number> [str]'],
|
||||
|
|
@ -66,6 +68,8 @@ commands = {
|
|||
"pubkey2addr": ['<public key in hex format> [str]'],
|
||||
"pubkey2hexaddr": ['<public key in hex format> [str]'],
|
||||
"privhex2addr": ['<private key in hex format> [str]','compressed [bool=False]'],
|
||||
"encrypt": ['<infile> [str]','outfile [str=""]','hash_preset [str="3"]'],
|
||||
"decrypt": ['<infile> [str]','outfile [str=""]','hash_preset [str="3"]'],
|
||||
}
|
||||
|
||||
command_help = """
|
||||
|
|
@ -73,10 +77,10 @@ command_help = """
|
|||
hexdump - encode data into formatted hexadecimal form (file or stdin)
|
||||
unhexdump - decode formatted hexadecimal data (file or stdin)
|
||||
|
||||
MMGen-specific operations
|
||||
id8 - generate 8-character MMGen ID checksum for file (or stdin)
|
||||
id6 - generate 6-character MMGen ID checksum for file (or stdin)
|
||||
check_addrfile - compute checksum and address list for MMGen address file
|
||||
{pnm}-specific operations
|
||||
id8 - generate 8-character {pnm} ID checksum for file (or stdin)
|
||||
id6 - generate 6-character {pnm} ID checksum for file (or stdin)
|
||||
check_addrfile - compute checksum and address list for {pnm} address file
|
||||
|
||||
Bitcoin operations:
|
||||
strtob58 - convert a string to base 58
|
||||
|
|
@ -89,7 +93,7 @@ command_help = """
|
|||
hex2wif - convert a private key from hex to WIF format
|
||||
wif2addr - generate a Bitcoin address from a key in WIF format
|
||||
pubkey2addr - convert Bitcoin public key to address
|
||||
pubkey2hexaddr - convert Bitcoin public key to address in hex format
|
||||
pubkey2hexaddr - convert Bitcoin public key to address in hex format
|
||||
hexaddr2addr - convert Bitcoin address from hex to base58 format
|
||||
addr2hexaddr - convert Bitcoin address from base58 to hex format
|
||||
privhex2addr - generate Bitcoin address from private key in hex format
|
||||
|
|
@ -100,6 +104,14 @@ command_help = """
|
|||
sha256x2 - compute a double sha256 hash of data
|
||||
getrand - print 'n' bytes (default 32) of random data in hex format
|
||||
|
||||
Encryption operations:
|
||||
encrypt - encrypt a file using {pnm}'s encryption suite
|
||||
decrypt - decrypt an {pnm}-encrypted file
|
||||
{pnm} encryption suite:
|
||||
* Key: Scrypt (user-configurable hash parameters, 32-byte salt)
|
||||
* Enc: AES256_CTR, 16-byte rand IV, sha256 hash + 32-byte nonce + data
|
||||
* The encrypted file is indistinguishable from random data
|
||||
|
||||
Mnemonic operations (choose "electrum" (default), "tirosh" or "all"
|
||||
wordlists):
|
||||
mn_rand128 - generate random 128-bit mnemonic
|
||||
|
|
@ -109,14 +121,14 @@ command_help = """
|
|||
mn_printlist - print mnemonic wordlist
|
||||
|
||||
Bitcoind operations (bitcoind must be running):
|
||||
listaddresses - show MMGen addresses and their balances
|
||||
listaddresses - show {pnm} addresses and their balances
|
||||
getbalance - like 'bitcoind getbalance' but shows confirmed/unconfirmed,
|
||||
spendable/unspendable
|
||||
viewtx - show raw transaction in human-readable form
|
||||
viewtx - show raw/signed {pnm} transaction in human-readable form
|
||||
|
||||
IMPORTANT NOTE: Though MMGen mnemonics use the Electrum wordlist, they're
|
||||
IMPORTANT NOTE: Though {pnm} mnemonics use the Electrum wordlist, they're
|
||||
computed using a different algorithm and are NOT Electrum-compatible!
|
||||
"""
|
||||
""".format(pnm=g.proj_name)
|
||||
|
||||
def tool_usage(prog_name, command):
|
||||
print "USAGE: '%s %s%s'" % (prog_name, command,
|
||||
|
|
@ -216,26 +228,22 @@ def b58tohex(s,f_enc=bitcoin.b58decode, f_dec=bitcoin.b58encode):
|
|||
dec = f_dec(ba.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)
|
||||
r = get_random(32,opts)
|
||||
enc = bitcoin.b58encode(r)
|
||||
dec = bitcoin.b58decode(enc)
|
||||
print_convert_results(ba.hexlify(r),enc,ba.hexlify(dec))
|
||||
|
||||
def getrand(bytes='32'):
|
||||
print ba.hexlify(get_random(int(bytes)))
|
||||
print ba.hexlify(get_random(int(bytes),opts))
|
||||
|
||||
def randwif(compressed=False):
|
||||
r_hex = ba.hexlify(get_random(32))
|
||||
r_hex = ba.hexlify(get_random(32,opts))
|
||||
enc = bitcoin.hextowif(r_hex,compressed)
|
||||
print_convert_results(r_hex,enc,"",no_recode=True)
|
||||
|
||||
def randpair(compressed=False):
|
||||
r_hex = ba.hexlify(get_random(32))
|
||||
r_hex = ba.hexlify(get_random(32,opts))
|
||||
wif = bitcoin.hextowif(r_hex,compressed)
|
||||
addr = bitcoin.privnum2addr(int(r_hex,16),compressed)
|
||||
Vmsg("Key (hex): %s" % r_hex)
|
||||
|
|
@ -265,7 +273,7 @@ def get_wordlist(wordlist):
|
|||
return el if wordlist == "electrum" else tl
|
||||
|
||||
def do_random_mn(nbytes,wordlist):
|
||||
r = get_random(nbytes)
|
||||
r = get_random(nbytes,opts)
|
||||
wlists = wordlists if wordlist == "all" else [wordlist]
|
||||
for wl in wlists:
|
||||
l = get_wordlist(wl)
|
||||
|
|
@ -397,3 +405,40 @@ def wif2hex(wif,compressed=False):
|
|||
|
||||
def hex2wif(hexpriv,compressed=False):
|
||||
print bitcoin.hextowif(hexpriv,compressed)
|
||||
|
||||
salt_len,sha256_len,nonce_len = 32,32,32
|
||||
|
||||
def encrypt(infile,outfile="",hash_preset=''):
|
||||
d = get_data_from_file(infile,"data for encryption")
|
||||
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")
|
||||
qmsg("Using %s hash preset of '%s'" % (m,hp))
|
||||
passwd = get_new_passphrase("passphrase",{})
|
||||
key = make_key(passwd, salt, hp)
|
||||
from hashlib import sha256
|
||||
enc_d = encrypt_data(sha256(nonce+d).digest() + nonce + d, key,
|
||||
int(ba.hexlify(iv),16))
|
||||
if outfile == '-': sys.stdout.write(salt+iv+enc_d)
|
||||
else: write_to_file((outfile or infile+"."+g.mmenc_ext),salt+iv+enc_d,True,True)
|
||||
|
||||
def decrypt(infile,outfile="",hash_preset=''):
|
||||
d = get_data_from_file(infile,"encrypted data")
|
||||
dstart = salt_len + g.aesctr_iv_len
|
||||
salt,iv,enc_d = d[:salt_len],d[salt_len:dstart],d[dstart:]
|
||||
hp,m = (hash_preset,"user-requested") if hash_preset else ('3',"default")
|
||||
qmsg("Using %s hash preset of '%s'" % (m,hp))
|
||||
passwd = get_mmgen_passphrase("Enter passphrase: ",{})
|
||||
key = make_key(passwd, salt, hp)
|
||||
dec_d = decrypt_data(enc_d, key, int(ba.hexlify(iv),16))
|
||||
from hashlib import sha256
|
||||
if dec_d[:sha256_len] == sha256(dec_d[sha256_len:]).digest():
|
||||
out = dec_d[sha256_len+nonce_len:]
|
||||
if outfile == '-': sys.stdout.write(out)
|
||||
else:
|
||||
import re
|
||||
of = re.sub(r'\.%s$'%g.mmenc_ext,r'',infile)
|
||||
if of == infile: of = infile+".dec"
|
||||
write_to_file((outfile or of), out, True,True)
|
||||
else:
|
||||
msg("Incorrect passphrase or hash preset")
|
||||
|
|
|
|||
76
mmgen/tx.py
76
mmgen/tx.py
|
|
@ -61,7 +61,25 @@ tracking wallet, or supply an address file for it on the command line.
|
|||
'no_spendable_outputs': """
|
||||
No spendable outputs found! Import addresses with balances into your
|
||||
watch-only wallet using 'mmgen-addrimport' and then re-run this program.
|
||||
""".strip()
|
||||
""".strip(),
|
||||
'mapping_error': """
|
||||
MMGen -> BTC address mappings differ!
|
||||
In transaction: %s
|
||||
Generated from seed: %s
|
||||
""".strip(),
|
||||
'skip_mapping_checks_warning': """
|
||||
You've chosen the '--all-keys-from-file' option. Since all signing keys will
|
||||
be taken from this file, no {pnm} seed source will be consulted and {pnm}-to-
|
||||
BTC mapping checks cannot not be performed. Were an attacker to compromise
|
||||
your tracking wallet or raw transaction file, he could thus cause you to spend
|
||||
coin to an unintended address. For greater security, supply a trusted {pnm}
|
||||
address file for your output addresses on the command line.
|
||||
""".strip().format(pnm=g.proj_name),
|
||||
'missing_mappings': """
|
||||
No information was found in the supplied address files for the following {pnm}
|
||||
addresses: %s
|
||||
The {pnm}-to-BTC mappings for these addresses cannot be verified!
|
||||
""".strip().format(pnm=g.proj_name),
|
||||
}
|
||||
|
||||
# Deleted text:
|
||||
|
|
@ -504,7 +522,7 @@ def mmaddr2btcaddr_bitcoind(c,mmaddr,acct_data):
|
|||
return "",""
|
||||
|
||||
|
||||
def mmaddr2btcaddr_addrfile(mmaddr,addr_data):
|
||||
def mmaddr2btcaddr_addrfile(mmaddr,addr_data,silent=False):
|
||||
|
||||
mmid,mmidx = mmaddr.split(":")
|
||||
|
||||
|
|
@ -512,35 +530,28 @@ def mmaddr2btcaddr_addrfile(mmaddr,addr_data):
|
|||
if mmid == ad[0]:
|
||||
for j in ad[1]:
|
||||
if j[0] == mmidx:
|
||||
msg(txmsg['addrfile_warn_msg'].format(mmaddr=mmaddr))
|
||||
if not user_confirm("Continue anyway?"):
|
||||
sys.exit(1)
|
||||
if not silent:
|
||||
msg(txmsg['addrfile_warn_msg'].format(mmaddr=mmaddr))
|
||||
if not user_confirm("Continue anyway?"):
|
||||
sys.exit(1)
|
||||
return j[1:] if len(j) == 3 else (j[1],"")
|
||||
|
||||
msg(txmsg['addrfile_fail_msg'].format(mmaddr=mmaddr))
|
||||
sys.exit(2)
|
||||
if silent: return "",""
|
||||
else: msg(txmsg['addrfile_fail_msg'].format(mmaddr=mmaddr)); sys.exit(2)
|
||||
|
||||
|
||||
def check_mmgen_to_btc_addr_mappings(inputs_data,b2m_map,infiles,saved_seeds,opts):
|
||||
in_maplist = [(i['account'].split()[0],i['address'])
|
||||
for i in inputs_data if i['account']
|
||||
and is_mmgen_addr(i['account'].split()[0])]
|
||||
def check_mmgen_to_btc_addr_mappings(mmgen_inputs,b2m_map,infiles,saved_seeds,opts):
|
||||
in_maplist = [(i['account'].split()[0],i['address']) for i in mmgen_inputs]
|
||||
out_maplist = [(i[1][0],i[0]) for i in b2m_map.items()]
|
||||
|
||||
for maplist,label in (in_maplist,"inputs"), (out_maplist,"outputs"):
|
||||
if not maplist: continue
|
||||
qmsg("Checking MMGen -> BTC address mappings for %s" % label)
|
||||
mmaddrs = [i[0] for i in maplist]
|
||||
from copy import deepcopy
|
||||
pairs = get_keys_for_mmgen_addrs(mmaddrs,
|
||||
pairs = get_keys_for_mmgen_addrs([i[0] for i in maplist],
|
||||
infiles,saved_seeds,opts,gen_pairs=True)
|
||||
for a,b in zip(sorted(pairs),sorted(maplist)):
|
||||
if a != b:
|
||||
msg("""
|
||||
MMGen -> BTC address mappings differ!
|
||||
In transaction: %s
|
||||
Generated from seed: %s
|
||||
""".strip() % (" ".join(a)," ".join(b)))
|
||||
msg(txmsg['mapping_error'] % (" ".join(a)," ".join(b)))
|
||||
sys.exit(3)
|
||||
|
||||
qmsg("Address mappings OK")
|
||||
|
|
@ -659,7 +670,6 @@ def get_seed_for_seed_id(seed_id,infiles,saved_seeds,opts):
|
|||
def get_keys_for_mmgen_addrs(mmgen_addrs,infiles,saved_seeds,opts,gen_pairs=False):
|
||||
|
||||
seed_ids = list(set([i[:8] for i in mmgen_addrs]))
|
||||
seed_ids_save = seed_ids[0:] # deep copy
|
||||
ret = []
|
||||
|
||||
for seed_id in seed_ids:
|
||||
|
|
@ -733,6 +743,7 @@ def preverify_keys(addrs_orig, keys_orig):
|
|||
sys.exit(2)
|
||||
else: qmsg("OK")
|
||||
|
||||
# Check that keys match addresses:
|
||||
msg('Pre-verifying keys in user-supplied key list (Ctrl-C to skip)')
|
||||
|
||||
try:
|
||||
|
|
@ -768,3 +779,28 @@ A key file must be supplied (or use the "-w" option) for the following
|
|||
non-mmgen address%s:
|
||||
""".strip() % ("" if len(other_addrs) == 1 else "es"))
|
||||
print " %s" % "\n ".join([i['address'] for i in other_addrs])
|
||||
|
||||
|
||||
def check_mmgen_to_btc_addr_mappings_addrfile(mmgen_inputs,b2m_map,addrfiles):
|
||||
addr_data = [parse_addrs_file(a) for a in addrfiles]
|
||||
in_maplist = [(i['account'].split()[0],i['address']) for i in mmgen_inputs]
|
||||
out_maplist = [(i[1][0],i[0]) for i in b2m_map.items()]
|
||||
|
||||
missing,wrong = [],[]
|
||||
for maplist,label in (in_maplist,"inputs"), (out_maplist,"outputs"):
|
||||
qmsg("Checking MMGen -> BTC address mappings for %s" % label)
|
||||
for i in maplist:
|
||||
btaddr = mmaddr2btcaddr_addrfile(i[0],addr_data,silent=True)[0]
|
||||
if not btaddr: missing.append(i[0])
|
||||
elif btaddr != i[1]: wrong.append((i[0],i[1],btaddr))
|
||||
|
||||
if wrong:
|
||||
fs = " {:11} {:35} {}"
|
||||
msg("ERROR: The following address mappings did not match!")
|
||||
msg(fs.format("MMGen addr","In TX file:","In address file:"))
|
||||
for w in wrong: msg(fs.format(*w))
|
||||
sys.exit(3)
|
||||
|
||||
if missing:
|
||||
confirm_or_exit(txmsg['missing_mappings'] % " ".join(missing),"continue")
|
||||
else: qmsg("Address mappings OK")
|
||||
|
|
|
|||
154
mmgen/util.py
154
mmgen/util.py
|
|
@ -20,8 +20,10 @@ util.py: Shared routines for the mmgen suite
|
|||
"""
|
||||
|
||||
import sys
|
||||
import mmgen.config as g
|
||||
from hashlib import sha256
|
||||
from binascii import hexlify,unhexlify
|
||||
|
||||
import mmgen.config as g
|
||||
from mmgen.bitcoin import b58decode_pad
|
||||
from mmgen.term import *
|
||||
|
||||
|
|
@ -46,6 +48,60 @@ def get_extension(f):
|
|||
import os
|
||||
return os.path.splitext(f)[1][1:]
|
||||
|
||||
|
||||
def get_random_data_from_user(uchars):
|
||||
|
||||
if g.quiet: msg("Enter %s random symbols" % uchars)
|
||||
else: msg(cmessages['usr_rand_notice'] % uchars)
|
||||
|
||||
prompt = "You may begin typing. %s symbols left: "
|
||||
msg_r(prompt % uchars)
|
||||
|
||||
import time
|
||||
# time.clock() always returns zero, so we'll use time.time()
|
||||
saved_time = time.time()
|
||||
|
||||
key_data,time_data = "",[]
|
||||
|
||||
for i in range(uchars):
|
||||
key_data += get_char(immed_chars="ALL")
|
||||
msg_r("\r" + prompt % (uchars - i - 1))
|
||||
now = time.time()
|
||||
time_data.append(now - saved_time)
|
||||
saved_time = now
|
||||
|
||||
if g.quiet: msg_r("\r")
|
||||
else: msg_r("\rThank you. That's enough.%s\n\n" % (" "*18))
|
||||
|
||||
fmt_time_data = ["{:.22f}".format(i) for i in time_data]
|
||||
|
||||
if g.debug:
|
||||
msg("\nUser input:\n%s\nKeystroke time intervals:\n%s\n" %
|
||||
(key_data,"\n".join(fmt_time_data)))
|
||||
|
||||
prompt = "User random data successfully acquired. Press ENTER to continue"
|
||||
prompt_and_get_char(prompt,"",enter_ok=True)
|
||||
msg("")
|
||||
|
||||
return key_data+"".join(fmt_time_data)
|
||||
|
||||
|
||||
def get_random(length,opts):
|
||||
from Crypto import Random
|
||||
os_rand = Random.new().read(length)
|
||||
if 'usr_randchars' in opts and opts['usr_randchars'] not in (0,-1):
|
||||
kwhat = "a key from random data with "
|
||||
if not g.user_entropy:
|
||||
g.user_entropy = sha256(
|
||||
get_random_data_from_user(opts['usr_randchars'])).digest()
|
||||
kwhat += "user entropy"
|
||||
else:
|
||||
kwhat += "saved user entropy"
|
||||
key = make_key(g.user_entropy, "", '2', what=kwhat)
|
||||
return encrypt_data(os_rand,key,what="random data")
|
||||
else:
|
||||
return os_rand
|
||||
|
||||
def my_raw_input(prompt,echo=True):
|
||||
try:
|
||||
if echo:
|
||||
|
|
@ -118,7 +174,21 @@ recommended to use one of the higher-numbered presets
|
|||
Remember the seed length and hash preset parameters you've specified. To
|
||||
generate the correct keys/addresses associated with this passphrase in the
|
||||
future, you must continue using these same parameters
|
||||
"""
|
||||
""",
|
||||
'usr_rand_notice': """
|
||||
You've chosen to not fully trust your OS's random number generator and provide
|
||||
some additional entropy of your own. Please type %s symbols on your keyboard.
|
||||
Type slowly and choose your symbols carefully for maximum randomness. Try to
|
||||
use both upper and lowercase as well as punctuation and numerals. What you
|
||||
type will not be displayed on the screen. Note that the timings between your
|
||||
keystrokes will also be used as a source of randomness.
|
||||
""",
|
||||
'choose_wallet_passphrase': """
|
||||
Now you must choose a passphrase to encrypt the wallet with. A key will be
|
||||
generated from your passphrase using a hash preset of '%s'. Please note that
|
||||
no strength checking of passphrases is performed. For an empty passphrase,
|
||||
just hit ENTER twice.
|
||||
""".strip()
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -172,12 +242,10 @@ def prompt_and_get_char(prompt,chars,enter_ok=False,verbose=False):
|
|||
|
||||
|
||||
def make_chksum_8(s,sep=False):
|
||||
from hashlib import sha256
|
||||
s = sha256(sha256(s).digest()).hexdigest()[:8].upper()
|
||||
return "{} {}".format(s[:4],s[4:]) if sep else s
|
||||
|
||||
def make_chksum_6(s):
|
||||
from hashlib import sha256
|
||||
return sha256(s).hexdigest()[:6]
|
||||
|
||||
|
||||
|
|
@ -296,31 +364,36 @@ def _get_seed_from_brain_passphrase(words,opts):
|
|||
return seed
|
||||
|
||||
|
||||
def encrypt_seed(seed, key, iv=1):
|
||||
def encrypt_seed(seed, key):
|
||||
return encrypt_data(seed, key, iv=1, what="seed")
|
||||
|
||||
def encrypt_data(data, key, iv=1, what="data"):
|
||||
"""
|
||||
Encrypt arbitrary data using AES256 in counter mode
|
||||
"""
|
||||
Encrypt a seed for an {} deterministic wallet
|
||||
""".format(g.proj_name)
|
||||
|
||||
# 192-bit seed is 24 bytes -> not multiple of 16. Must use MODE_CTR
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Util import Counter
|
||||
|
||||
c = AES.new(key, AES.MODE_CTR,
|
||||
counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
|
||||
enc_seed = c.encrypt(seed)
|
||||
|
||||
vmsg_r("Performing a test decryption of the seed...")
|
||||
vmsg("Encrypting %s" % what)
|
||||
|
||||
c = AES.new(key, AES.MODE_CTR,
|
||||
counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
|
||||
dec_seed = c.decrypt(enc_seed)
|
||||
enc_data = c.encrypt(data)
|
||||
|
||||
if dec_seed == seed: vmsg("done")
|
||||
vmsg_r("Performing a test decryption of the %s..." % what)
|
||||
|
||||
c = AES.new(key, AES.MODE_CTR,
|
||||
counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
|
||||
dec_data = c.decrypt(enc_data)
|
||||
|
||||
if dec_data == data: vmsg("done\n")
|
||||
else:
|
||||
msg("ERROR.\nDecrypted seed doesn't match original seed")
|
||||
msg("ERROR.\nDecrypted %s doesn't match original %s" % (what,what))
|
||||
sys.exit(2)
|
||||
|
||||
return enc_seed
|
||||
return enc_data
|
||||
|
||||
|
||||
def write_to_stdout(data, what, confirm=True):
|
||||
|
|
@ -354,7 +427,9 @@ def open_file_or_exit(filename,mode):
|
|||
return f
|
||||
|
||||
|
||||
def write_to_file(outfile,data,confirm=False):
|
||||
def write_to_file(outfile,data,confirm=False,verbose=False):
|
||||
|
||||
if verbose: qmsg("Writing data to file '%s'" % outfile)
|
||||
|
||||
if confirm:
|
||||
from os import stat
|
||||
|
|
@ -396,8 +471,8 @@ def _display_control_data(label,metadata,hash_preset,salt,enc_seed):
|
|||
from mmgen.bitcoin import b58encode_pad
|
||||
for i in (
|
||||
("Label:", label),
|
||||
("Seed ID:", metadata[0]),
|
||||
("Key ID:", metadata[1]),
|
||||
("Seed ID:", metadata[0].upper()),
|
||||
("Key ID:", metadata[1].upper()),
|
||||
("Seed length:", "%s bits (%s bytes)" %
|
||||
(metadata[2],int(metadata[2])/8)),
|
||||
("Scrypt params:", "Preset '%s' (%s)" % (hash_preset,
|
||||
|
|
@ -799,10 +874,9 @@ def get_seed_from_incog_wallet(
|
|||
break
|
||||
msg("%s: Invalid hash preset" % hp)
|
||||
|
||||
from hashlib import sha256
|
||||
# IV is used BOTH to initialize counter and to salt password!
|
||||
key = make_key(passwd, iv, hp, "wrapper key")
|
||||
d = decrypt_seed(enc_incog_data, key, "", "", iv=int(hexlify(iv),16))
|
||||
d = decrypt_data(enc_incog_data, key, int(hexlify(iv),16), "incog data")
|
||||
if d == False: sys.exit(2)
|
||||
|
||||
salt,enc_seed = d[0:g.salt_len], d[g.salt_len:]
|
||||
|
|
@ -820,14 +894,14 @@ def get_seed_from_incog_wallet(
|
|||
|
||||
def make_key(passwd, salt, hash_preset, what="key"):
|
||||
|
||||
vmsg_r("Generating %s from passphrase. Please wait..." % what)
|
||||
vmsg_r("Generating %s. Please wait..." % what)
|
||||
key = _scrypt_hash_passphrase(passwd, salt, hash_preset)
|
||||
vmsg("done")
|
||||
if g.debug: print "Key: %s" % hexlify(key)
|
||||
return key
|
||||
|
||||
|
||||
def decrypt_seed(enc_seed, key, seed_id, key_id, iv=1):
|
||||
def decrypt_seed(enc_seed, key, seed_id, key_id):
|
||||
|
||||
vmsg("Checking key...")
|
||||
chk1 = make_chksum_8(key)
|
||||
|
|
@ -836,23 +910,16 @@ def decrypt_seed(enc_seed, key, seed_id, key_id, iv=1):
|
|||
msg("Incorrect passphrase")
|
||||
return False
|
||||
|
||||
vmsg("Decrypting seed with key...")
|
||||
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Util import Counter
|
||||
|
||||
c = AES.new(key, AES.MODE_CTR,
|
||||
counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
|
||||
dec_seed = c.decrypt(enc_seed)
|
||||
dec_seed = decrypt_data(enc_seed, key, iv=1, what="seed")
|
||||
|
||||
chk2 = make_chksum_8(dec_seed)
|
||||
|
||||
if seed_id:
|
||||
if _compare_checksums(chk2,"of decrypted seed",seed_id,"in header"):
|
||||
qmsg("Passphrase is OK")
|
||||
else:
|
||||
if not g.debug:
|
||||
msg_r("Checking key ID...")
|
||||
chk1 = make_chksum_8(key)
|
||||
if _compare_checksums(chk1, "of key", key_id, "in header"):
|
||||
msg("Key ID is correct but decryption of seed failed")
|
||||
else:
|
||||
|
|
@ -867,6 +934,20 @@ def decrypt_seed(enc_seed, key, seed_id, key_id, iv=1):
|
|||
return dec_seed
|
||||
|
||||
|
||||
def decrypt_data(enc_data, key, iv=1, what="data"):
|
||||
|
||||
vmsg("Decrypting %s with key..." % what)
|
||||
|
||||
from Crypto.Cipher import AES
|
||||
from Crypto.Util import Counter
|
||||
|
||||
c = AES.new(key, AES.MODE_CTR,
|
||||
counter=Counter.new(g.aesctr_iv_len*8,initial_value=iv))
|
||||
|
||||
return c.decrypt(enc_data)
|
||||
|
||||
|
||||
|
||||
def _get_words(infile,what,prompt,opts):
|
||||
if infile:
|
||||
return _get_words_from_file(infile,what)
|
||||
|
|
@ -1007,6 +1088,7 @@ def decode_pretty_hexdump(data):
|
|||
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)
|
||||
|
|
@ -1019,16 +1101,14 @@ def wallet_to_incog_data(infile,opts):
|
|||
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 = get_random(g.aesctr_iv_len,opts)
|
||||
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))
|
||||
m = "incog data"
|
||||
wrap_enc = encrypt_data(salt + enc_seed, key, int(hexlify(iv),16), m)
|
||||
|
||||
return iv+wrap_enc,seed_id,key_id,iv_id,preset
|
||||
|
||||
|
|
|
|||
|
|
@ -24,55 +24,4 @@ import mmgen.config as g
|
|||
from mmgen.util import msg, msg_r, qmsg, qmsg_r, get_char, prompt_and_get_char
|
||||
from binascii import hexlify
|
||||
|
||||
def get_random_data_from_user(opts):
|
||||
|
||||
ulen = opts['usr_randlen']
|
||||
|
||||
if g.quiet:
|
||||
msg("Enter %s random symbols" % ulen)
|
||||
else:
|
||||
msg("""
|
||||
We're going to be paranoid and not fully trust your OS's random number
|
||||
generator. Please type %s symbols on your keyboard. Type slowly and choose
|
||||
your symbols carefully for maximum randomness. Try to use both upper and
|
||||
lowercase as well as punctuation and numerals. What you type will not be
|
||||
displayed on the screen.
|
||||
""" % ulen)
|
||||
|
||||
prompt = "You may begin typing. %s symbols left: "
|
||||
msg_r(prompt % ulen)
|
||||
|
||||
import time
|
||||
# time.clock() always returns zero, so we'll use time.time()
|
||||
saved_time = time.time()
|
||||
|
||||
user_rand_data,intervals = "",[]
|
||||
|
||||
for i in range(ulen):
|
||||
user_rand_data += get_char(immed_chars="ALL")
|
||||
msg_r("\r" + prompt % (ulen - i - 1))
|
||||
now = time.time()
|
||||
intervals.append(now - saved_time)
|
||||
saved_time = now
|
||||
|
||||
if g.quiet:
|
||||
msg_r("\r")
|
||||
else:
|
||||
msg_r("\rThank you. That's enough." + " "*15 + "\n\n")
|
||||
|
||||
prompt = "User random data successfully acquired. Press ENTER to continue"
|
||||
prompt_and_get_char(prompt,"",enter_ok=True)
|
||||
|
||||
return user_rand_data, ["{:.22f}".format(i) for i in intervals]
|
||||
|
||||
|
||||
def display_os_random_data(os_rand_data):
|
||||
print "Rand1: {}\nRand2: {}".format(
|
||||
*[hexlify(i) for i in os_rand_data])
|
||||
|
||||
|
||||
def display_user_random_data(user_rand_data,intervals_fmt):
|
||||
msg("\nUser random data: " + user_rand_data)
|
||||
msg("Keystroke time intervals:")
|
||||
for i in range(0,len(intervals_fmt),3):
|
||||
msg(" " + " ".join(intervals_fmt[i:i+3]))
|
||||
|
|
|
|||
3
setup.py
3
setup.py
|
|
@ -3,7 +3,7 @@ from distutils.core import setup
|
|||
|
||||
setup(
|
||||
name = 'mmgen',
|
||||
version = '0.7.5',
|
||||
version = '0.7.6',
|
||||
author = 'Philemon',
|
||||
author_email = 'mmgen-py@yandex.com',
|
||||
url = 'https://github.com/mmgen/mmgen',
|
||||
|
|
@ -45,6 +45,7 @@ setup(
|
|||
],
|
||||
scripts=[
|
||||
'mmgen-addrgen',
|
||||
'mmgen-keygen',
|
||||
'mmgen-addrimport',
|
||||
'mmgen-passchg',
|
||||
'mmgen-walletchk',
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue