Various bugfixes.
This commit is contained in:
parent
03b1b4b00e
commit
d3f07f3c9f
16 changed files with 2103 additions and 368 deletions
|
|
@ -45,6 +45,12 @@ addrmsgs = {
|
|||
# address, and it will be appended to the bitcoind wallet label upon import.
|
||||
# The label may contain any printable ASCII symbol.
|
||||
""".strip().format(n=g.max_addr_label_len,pnm=pnm),
|
||||
'keyfile_header': """
|
||||
# {pnm} key file
|
||||
#
|
||||
# This file is editable.
|
||||
# Everything following a hash symbol '#' is a comment and ignored by {pnm}.
|
||||
""".strip().format(pnm=pnm),
|
||||
'no_keyconv_msg': """
|
||||
Executable '{kconv}' unavailable. Falling back on (slow) internal ECDSA library.
|
||||
Please install '{kconv}' from the {vgen} package on your system for much
|
||||
|
|
@ -312,7 +318,7 @@ class AddrInfo(MMGenObject):
|
|||
(w,self.seed_id,self.idxs_fmt,self.checksum))
|
||||
if self.source == "addrgen":
|
||||
qmsg(
|
||||
"This checksum will be used to verify the address file in the future")
|
||||
"Record this checksum: it will be used to verify the address file in the future")
|
||||
elif self.source == "addrfile":
|
||||
qmsg("Check this value against your records")
|
||||
|
||||
|
|
@ -368,35 +374,33 @@ class AddrInfo(MMGenObject):
|
|||
|
||||
|
||||
def fmt_data(self,enable_comments=False):
|
||||
|
||||
# Check data integrity - either all or none must exist for each attr
|
||||
attrs = ['addr','wif','sec']
|
||||
status = [0,0,0]
|
||||
for i in range(self.num_addrs):
|
||||
for d in self.addrdata:
|
||||
for j,attr in enumerate(attrs):
|
||||
try:
|
||||
getattr(self.addrdata[i],attr)
|
||||
if hasattr(d,attr):
|
||||
status[j] += 1
|
||||
except: pass
|
||||
|
||||
for i,s in enumerate(status):
|
||||
if s != 0 and s != self.num_addrs:
|
||||
msg("%s missing %s in addr data"% (self.num_addrs-s,attrs[i]))
|
||||
sys.exit(3)
|
||||
|
||||
if status[0] == None and status[1] == None:
|
||||
if status[0] == status[1] == 0:
|
||||
msg("Addr data contains neither addresses nor keys")
|
||||
sys.exit(3)
|
||||
|
||||
# Header
|
||||
out = []
|
||||
from mmgen.addr import addrmsgs
|
||||
out.append(addrmsgs['addrfile_header'] + "\n")
|
||||
k = ('addrfile_header','keyfile_header')[int(status[0]==0)]
|
||||
out.append(addrmsgs[k]+"\n")
|
||||
if self.checksum:
|
||||
w = "Key-address" if status[1] else "Address"
|
||||
w = ("Key-address","Address")[int(status[1]==0)]
|
||||
out.append("# {} data checksum for {}[{}]: {}".format(
|
||||
w, self.seed_id, self.idxs_fmt, self.checksum))
|
||||
out.append("# Record this value to a secure location\n")
|
||||
out.append("# Record this value to a secure location.\n")
|
||||
out.append("%s {" % self.seed_id)
|
||||
|
||||
# Body
|
||||
|
|
|
|||
|
|
@ -56,6 +56,12 @@ required_opts = [
|
|||
"usr_randchars","stdout","show_hash_presets","label",
|
||||
"keep_passphrase","keep_hash_preset"
|
||||
]
|
||||
incompatible_opts = (
|
||||
("quiet","verbose"),
|
||||
("label","keep_label"),
|
||||
("tx_id", "info"),
|
||||
("tx_id", "terse_info"),
|
||||
)
|
||||
min_screen_width = 80
|
||||
|
||||
wallet_ext = "mmdat"
|
||||
|
|
|
|||
|
|
@ -21,21 +21,18 @@ main.py - Script launcher for the MMGen suite
|
|||
"""
|
||||
|
||||
def launch(what):
|
||||
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_walletconv(): import mmgen.main_walletconv
|
||||
def launch_walletgen(): import mmgen.main_walletgen
|
||||
|
||||
import os
|
||||
t = "MMGEN_USE_OLD_SCRIPTS"
|
||||
if not (t in os.environ and os.environ[t]):
|
||||
if what in ("walletgen","walletchk","passchg"):
|
||||
what = "wallet"
|
||||
|
||||
if what == "walletconv": what = "wallet"
|
||||
if what == "keygen": what = "addrgen"
|
||||
|
||||
try: import termios
|
||||
except: locals()["launch_"+what]() # Windows
|
||||
except: __import__("mmgen.main_" + what) # Windows
|
||||
else:
|
||||
import sys,atexit
|
||||
fd = sys.stdin.fileno()
|
||||
|
|
@ -43,7 +40,7 @@ def launch(what):
|
|||
def at_exit():
|
||||
termios.tcsetattr(fd, termios.TCSADRAIN, old)
|
||||
atexit.register(at_exit)
|
||||
try: locals()["launch_"+what]()
|
||||
try: __import__("mmgen.main_" + what)
|
||||
except KeyboardInterrupt:
|
||||
sys.stderr.write("\nUser interrupt\n")
|
||||
except EOFError:
|
||||
|
|
|
|||
|
|
@ -29,14 +29,20 @@ from mmgen.util import *
|
|||
from mmgen.crypto import *
|
||||
from mmgen.addr import *
|
||||
|
||||
what = "keys" if sys.argv[0].split("-")[-1] == "keygen" else "addresses"
|
||||
if sys.argv[0].split("-")[-1] == "keygen":
|
||||
gen_what = "keys"
|
||||
opt_filter = None
|
||||
else:
|
||||
gen_what = "addresses"
|
||||
opt_filter = "hdceHKlpPqSvbgXGoms"
|
||||
|
||||
opts_data = {
|
||||
'desc': """Generate a range or list of {w} from an {pnm} wallet,
|
||||
mnemonic, seed or password""".format(w=what,pnm=g.proj_name),
|
||||
'desc': """Generate a range or list of {what} from an {pnm} wallet,
|
||||
mnemonic, seed or password""".format(what=gen_what,pnm=g.proj_name),
|
||||
'usage':"[opts] [infile] <address range or list>",
|
||||
'options': """
|
||||
-h, --help Print this help message{}
|
||||
-h, --help Print this help message
|
||||
-A, --no-addresses Print only secret keys, no addresses
|
||||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-c, --save-checksum Save address list checksum to file
|
||||
-e, --echo-passphrase Echo passphrase or mnemonic to screen upon entry
|
||||
|
|
@ -51,7 +57,8 @@ opts_data = {
|
|||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
-S, --stdout Print {what} to stdout
|
||||
-v, --verbose Produce more verbose output{}
|
||||
-v, --verbose Produce more verbose output
|
||||
-x, --b16 Print secret keys in hexadecimal too
|
||||
|
||||
-b, --from-brain= l,p Generate {what} from a user-created password,
|
||||
i.e. a "brainwallet", using seed length 'l' and
|
||||
|
|
@ -64,19 +71,14 @@ opts_data = {
|
|||
-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 what == "keys" else ("","")),
|
||||
seed_lens=", ".join([str(i) for i in g.seed_lens]),
|
||||
what=what,g=g,pnm=g.proj_name
|
||||
seed_lens=", ".join([str(i) for i in g.seed_lens]),
|
||||
pnm=g.proj_name,
|
||||
what=gen_what,g=g
|
||||
),
|
||||
'notes': """
|
||||
|
||||
Addresses are given in a comma-separated list. Hyphen-separated ranges are
|
||||
also allowed.{}
|
||||
also allowed.{a}
|
||||
|
||||
If available, the external 'keyconv' program will be used for address
|
||||
generation.
|
||||
|
|
@ -101,8 +103,8 @@ The '--from-brain' option also requires the user to specify a seed length
|
|||
For a brainwallet passphrase to always generate the same keys and addresses,
|
||||
the same 'l' and 'p' parameters to '--from-brain' must be used in all future
|
||||
invocations with that passphrase
|
||||
""".format("\n\nBy default, both addresses and secret keys are generated."
|
||||
if what == "keys" else "")
|
||||
""".format(a="\n\nBy default, both addresses and secret keys are generated."
|
||||
if gen_what == "keys" else "")
|
||||
}
|
||||
|
||||
wmsg = {
|
||||
|
|
@ -112,7 +114,7 @@ UNENCRYPTED form. Generate only the key(s) you need and guard them carefully.
|
|||
""".format(pnm=g.proj_name),
|
||||
}
|
||||
|
||||
cmd_args = opt.opts.init(opts_data,add_opts=["b16"])
|
||||
cmd_args = opt.opts.init(opts_data,add_opts=["b16"],opt_filter=opt_filter)
|
||||
|
||||
if opt.from_incog_hex or opt.from_incog_hidden: opt.from_incog = True
|
||||
|
||||
|
|
@ -131,14 +133,14 @@ if not addr_idxs: sys.exit(2)
|
|||
do_license_msg()
|
||||
|
||||
# Interact with user:
|
||||
if what == "keys" and not opt.quiet:
|
||||
if gen_what == "keys" and not opt.quiet:
|
||||
confirm_or_exit(wmsg['unencrypted_secret_keys'], 'continue')
|
||||
|
||||
# Generate data:
|
||||
|
||||
seed = get_seed_retry(infile)
|
||||
|
||||
opt.gen_what = "a" if what == "addresses" else (
|
||||
opt.gen_what = "a" if gen_what == "addresses" else (
|
||||
"k" if opt.no_addresses else "ka")
|
||||
|
||||
ainfo = generate_addrs(seed,addr_idxs)
|
||||
|
|
@ -161,11 +163,11 @@ if opt.stdout or not sys.stdout.isatty():
|
|||
if enc_ext and sys.stdout.isatty():
|
||||
msg("Cannot write encrypted data to screen. Exiting")
|
||||
sys.exit(2)
|
||||
write_to_stdout(addrdata_str,what,ask_terminal=(what=="keys"
|
||||
write_to_stdout(addrdata_str,gen_what,ask_terminal=(gen_what=="keys"
|
||||
and not opt.quiet and sys.stdout.isatty()))
|
||||
else:
|
||||
outfile = "%s.%s%s" % (outfile_base, (
|
||||
g.keyaddrfile_ext if "ka" in opt.gen_what else (
|
||||
g.keyfile_ext if "k" in opt.gen_what else
|
||||
g.addrfile_ext)), enc_ext)
|
||||
write_to_file(outfile,addrdata_str,what,not opt.quiet,True)
|
||||
write_to_file(outfile,addrdata_str,gen_what,not opt.quiet,True)
|
||||
|
|
|
|||
|
|
@ -59,9 +59,9 @@ import hashlib
|
|||
import random
|
||||
import math
|
||||
|
||||
import mmgen.config as g
|
||||
import mmgen.globalvars as g
|
||||
import mmgen.opt as opt
|
||||
from mmgen.util import msg,msgrepr,msgrepr_exit
|
||||
from mmgen.util import msg,mdie,mmsg
|
||||
|
||||
max_version = 60000
|
||||
addrtype = 0
|
||||
|
|
@ -86,7 +86,7 @@ opts_data = {
|
|||
}
|
||||
|
||||
cmd_args = opt.opts.init(opts_data)
|
||||
opt.opts.warn_incompatible_opts(['json','keys','addrs','keysforaddrs'])
|
||||
opt.opts.die_on_incompatible_opts(['json','keys','addrs','keysforaddrs'])
|
||||
|
||||
if len(cmd_args) == 1:
|
||||
from mmgen.util import check_infile
|
||||
|
|
|
|||
|
|
@ -285,11 +285,6 @@ def get_keys_from_keylist(kldata,other_addrs):
|
|||
|
||||
infiles = opt.opts.init(opts_data,add_opts=["b16"])
|
||||
|
||||
for l in (
|
||||
('tx_id', 'info'),
|
||||
('tx_id', 'terse_info'),
|
||||
): opt.opts.die_on_incompatible_opts(l)
|
||||
|
||||
if opt.from_incog_hex or opt.from_incog_hidden: opt.from_incog = True
|
||||
|
||||
if not infiles: opt.opts.usage()
|
||||
|
|
|
|||
124
mmgen/main_wallet.py
Executable file
124
mmgen/main_wallet.py
Executable file
|
|
@ -0,0 +1,124 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen/main_wallet: Entry point for MMGen wallet-related scripts
|
||||
"""
|
||||
|
||||
import sys,os,re
|
||||
import mmgen.globalvars as g
|
||||
import mmgen.opt as opt
|
||||
from mmgen.util import die,msg,green,do_license_msg,check_infile,mdie,mmsg,qmsg,capfirst
|
||||
from mmgen.seed import SeedSource
|
||||
|
||||
bn = os.path.basename(sys.argv[0])
|
||||
invoked_as = re.sub(r'^wallet','',bn.split("-")[-1])
|
||||
|
||||
usage = "[opts] [infile]"
|
||||
nargs = 1
|
||||
iaction = "convert"
|
||||
oaction = "convert"
|
||||
|
||||
if invoked_as == "gen":
|
||||
desc = "Generate an {pnm} wallet from a random seed"
|
||||
opt_filter = "hdoJlLpPqrSv"
|
||||
usage = "[opts]"
|
||||
oaction = "output"
|
||||
nargs = 0
|
||||
elif invoked_as == "conv":
|
||||
desc = "Convert an {pnm} wallet from one format to another"
|
||||
opt_filter = None
|
||||
elif invoked_as == "chk":
|
||||
desc = "Check validity of an {pnm} wallet"
|
||||
opt_filter = "hiHOlpPqrv"
|
||||
iaction = "input"
|
||||
elif invoked_as == "passchg":
|
||||
desc = "Change the password, hash preset or label of an {pnm} wallet"
|
||||
opt_filter = "hdiHkKOlLmpPqrSv"
|
||||
iaction = "input"
|
||||
else:
|
||||
die(1,"'%s': unrecognized invocation" % bn)
|
||||
|
||||
opts_data = {
|
||||
'desc': desc.format(pnm=g.proj_name),
|
||||
'usage': usage,
|
||||
'options': """
|
||||
-h, --help Print this help message.
|
||||
-d, --outdir= d Output files to directory 'd' instead of working dir.
|
||||
-i, --in-fmt= f {iaction} from wallet format 'f' (see FMT CODES below).
|
||||
-o, --out-fmt= f {oaction} to wallet format 'f' (see FMT CODES below).
|
||||
-H, --hidden-incog-input-params=f,o Read hidden incognito data from file
|
||||
'f' at offset 'o' (comma-separated).
|
||||
-J, --hidden-incog-output-params=f,o Write hidden incognito data to file
|
||||
'f' at offset 'o' (comma-separated). If file 'f'
|
||||
doesn't exist, it will be created and filled with
|
||||
random data.
|
||||
-O, --old-incog-fmt Specify old-format incognito input.
|
||||
-k, --keep-passphrase Reuse passphrase of input wallet for output wallet.
|
||||
-K, --keep-hash-preset Reuse hash preset of input wallet for output wallet.
|
||||
-l, --seed-len= l Specify wallet seed length of 'l' bits. This option
|
||||
is required only for brainwallet and incognito inputs
|
||||
with non-standard (< {g.seed_len}-bit) seed lengths.
|
||||
-L, --label= l Specify a label 'l' for output wallet.
|
||||
-m, --keep-label Reuse label of input wallet for output wallet.
|
||||
-p, --hash-preset= p Use the scrypt hash parameters defined by preset 'p'
|
||||
for password hashing (default: '{g.hash_preset}').
|
||||
-P, --passwd-file= f Get wallet passphrase from file 'f'
|
||||
-q, --quiet Produce quieter output; suppress some warnings.
|
||||
-r, --usr-randchars=n Get 'n' characters of additional randomness from user
|
||||
(min={g.min_urandchars}, max={g.max_urandchars}, default={g.usr_randchars}).
|
||||
-S, --stdout Write wallet data to stdout instead of file.
|
||||
-v, --verbose Produce more verbose output.
|
||||
|
||||
FMT CODES:
|
||||
{f}
|
||||
""".format(
|
||||
g=g,
|
||||
iaction=capfirst(iaction),
|
||||
oaction=capfirst(oaction),
|
||||
f="\n ".join(SeedSource.format_fmt_codes().split("\n"))
|
||||
)
|
||||
}
|
||||
|
||||
cmd_args = opt.opts.init(opts_data,opt_filter=opt_filter)
|
||||
|
||||
if len(cmd_args) < nargs \
|
||||
and not opt.hidden_incog_input_params and not opt.in_fmt:
|
||||
die(1,"An input file or input format must be specified")
|
||||
elif len(cmd_args) > nargs \
|
||||
or (len(cmd_args) == nargs and opt.hidden_incog_input_params):
|
||||
msg("No input files may be specified" if invoked_as == "gen"
|
||||
else "Too many input files specified")
|
||||
opt.opts.usage()
|
||||
|
||||
if cmd_args: check_infile(cmd_args[0])
|
||||
|
||||
if not invoked_as == "chk": do_license_msg()
|
||||
|
||||
if invoked_as in ("conv","passchg"): msg(green("Processing input wallet"))
|
||||
|
||||
ss_in = None if invoked_as == "gen" \
|
||||
else SeedSource(*cmd_args,passchg=invoked_as=="passchg")
|
||||
|
||||
if invoked_as == "chk":
|
||||
sys.exit()
|
||||
|
||||
if invoked_as in ("conv","passchg"): msg(green("Processing output wallet"))
|
||||
|
||||
ss_out = SeedSource(ss=ss_in,passchg=invoked_as=="passchg")
|
||||
ss_out.write_to_file()
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# 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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-walletconv: Convert an MMGen deterministic wallet from one format
|
||||
to another
|
||||
"""
|
||||
|
||||
import sys
|
||||
import mmgen.globalvars as g
|
||||
import mmgen.opt as opt
|
||||
from mmgen.util import die,msg,green,do_license_msg,check_infile
|
||||
from mmgen.seed import SeedSource
|
||||
|
||||
opts_data = {
|
||||
'sets_disabled': (
|
||||
('hidden_incog_input_params', bool, 'in_fmt', 'hi'),
|
||||
('hidden_incog_output_params', bool, 'out_fmt', 'hi')
|
||||
),
|
||||
'desc': "Convert an {pnm} wallet from one format to another".format(
|
||||
pnm=g.proj_name),
|
||||
'usage':"[opts] [infile]",
|
||||
'options': """
|
||||
-h, --help Print this help message.
|
||||
-d, --outdir= d Output files to directory 'd' instead of working dir.
|
||||
-i, --in-fmt= f Convert from wallet format 'f' (see FMT CODES below).
|
||||
-o, --out-fmt= f Convert to wallet format 'f' (see FMT CODES below).
|
||||
-H, --hidden-incog-input-params=f,o Use filename 'f' and offset 'o' (comma
|
||||
separated) for hidden incognito input.
|
||||
-J, --hidden-incog-output-params=f,o Same above, but for output. If file
|
||||
'f' doesn't exist, it will be created and filled with
|
||||
random data.
|
||||
-O, --old-incog-fmt Specify old-format incognito input.
|
||||
-k, --keep-passphrase Reuse input wallet passphrase for output wallet.
|
||||
-K, --keep-hash-preset Reuse input wallet hash preset for output wallet.
|
||||
-l, --seed-len= l Specify wallet seed length of 'l' bits. This option
|
||||
is required only for brainwallet and incognito inputs
|
||||
with non-standard (< {g.seed_len}-bit) seed lengths.
|
||||
-p, --hash-preset= p Use the scrypt hash parameters defined by preset 'p'
|
||||
for password hashing (default: '{g.hash_preset}').
|
||||
-q, --quiet Produce quieter output; suppress some warnings.
|
||||
-r, --usr-randchars=n Get 'n' characters of additional randomness from user
|
||||
(min={g.min_urandchars}, max={g.max_urandchars}, default={g.usr_randchars}).
|
||||
-S, --stdout Write wallet data to stdout instead of file.
|
||||
-v, --verbose Produce more verbose output.
|
||||
|
||||
FMT CODES:
|
||||
{f}
|
||||
""".format(g=g,f="\n ".join(SeedSource.format_fmt_codes().split("\n")))
|
||||
}
|
||||
|
||||
cmd_args = opt.opts.init(opts_data)
|
||||
|
||||
if len(cmd_args) == 0 \
|
||||
and not opt.hidden_incog_input_params \
|
||||
and not opt.in_fmt:
|
||||
die(1,"An input file or input format must be specified")
|
||||
|
||||
if len(cmd_args) > 1 or (len(cmd_args) == 1 and opt.hidden_incog_input_params):
|
||||
die(1,"Only one input file may be specified")
|
||||
|
||||
if len(cmd_args) == 1:
|
||||
check_infile(cmd_args[0])
|
||||
|
||||
do_license_msg()
|
||||
|
||||
msg(green("Processing input wallet"))
|
||||
|
||||
ss_in = SeedSource(*cmd_args)
|
||||
|
||||
msg(green("Processing output wallet"))
|
||||
|
||||
ss_out = SeedSource(ss=ss_in)
|
||||
ss_out.write_to_file()
|
||||
|
|
@ -24,7 +24,7 @@ import sys
|
|||
import mmgen.globalvars as g
|
||||
import mmgen.share.Opts
|
||||
import opt
|
||||
from mmgen.util import msg,msg_r,mdie,mmsg,Msg,die
|
||||
from mmgen.util import msg,msg_r,mdie,mmsg,Msg,die,is_mmgen_wallet_label
|
||||
|
||||
def usage():
|
||||
Msg("USAGE: %s %s" % (g.prog_name, usage_txt))
|
||||
|
|
@ -38,9 +38,10 @@ mand line. Copyright (C) {g.Cdates} {g.author} {g.email}
|
|||
""".format(pnm=g.proj_name, g=g, pgnm_uc=g.prog_name.upper()).strip())
|
||||
|
||||
def die_on_incompatible_opts(incompat_list):
|
||||
bad = [k for k in opt.__dict__ if opt.__dict__[k] and k in incompat_list]
|
||||
if len(bad) > 1:
|
||||
die(1,"Conflicting options: %s" % ", ".join([fmt_opt(b) for b in bad]))
|
||||
for group in incompat_list:
|
||||
bad = [k for k in opt.__dict__ if opt.__dict__[k] and k in group]
|
||||
if len(bad) > 1:
|
||||
die(1,"Conflicting options: %s" % ", ".join([fmt_opt(b) for b in bad]))
|
||||
|
||||
def _typeconvert_from_dfl(key):
|
||||
|
||||
|
|
@ -80,18 +81,19 @@ def _show_hash_presets():
|
|||
msg(fs.format("'%s'" % i, *g.hash_presets[i]))
|
||||
msg("N = memory usage (power of two), p = iterations (rounds)")
|
||||
|
||||
def init(opts_data,add_opts=[]):
|
||||
def init(opts_data,add_opts=[],opt_filter=None):
|
||||
|
||||
if len(sys.argv) == 2 and sys.argv[1] == '--version':
|
||||
print_version_info(); sys.exit()
|
||||
|
||||
uopts,args,short_opts,long_opts = \
|
||||
mmgen.share.Opts.parse_opts(sys.argv,opts_data)
|
||||
uopts,args,short_opts,long_opts,skipped_opts = \
|
||||
mmgen.share.Opts.parse_opts(sys.argv,opts_data,opt_filter=opt_filter)
|
||||
|
||||
if g.debug:
|
||||
d = (
|
||||
("Short opts", short_opts),
|
||||
("Long opts", long_opts),
|
||||
("Skipped opts", skipped_opts),
|
||||
("User-selected opts", uopts),
|
||||
("Cmd args", args),
|
||||
)
|
||||
|
|
@ -112,7 +114,8 @@ def init(opts_data,add_opts=[]):
|
|||
if k[:2] == "__": del opt.__dict__[k]
|
||||
|
||||
# Transfer uopts into opt, setting required opts to None if not set by user
|
||||
for o in [s.rstrip("=") for s in long_opts] + g.required_opts + add_opts:
|
||||
for o in [s.rstrip("=") for s in long_opts] + \
|
||||
g.required_opts + add_opts + skipped_opts:
|
||||
opt.__dict__[o] = uopts[o] if o in uopts else None
|
||||
|
||||
# A special case - do this here, before opt gets set from g.dfl_vars
|
||||
|
|
@ -144,12 +147,7 @@ def init(opts_data,add_opts=[]):
|
|||
Msg(" %-18s: %-6s [%s]" % (k,v,type(v).__name__))
|
||||
Msg("### END OPTS.PY ###\n")
|
||||
|
||||
for l in (
|
||||
('from_incog_hidden','from_incog','from_seed','from_mnemonic','from_brain'),
|
||||
('export_incog','export_incog_hex','export_incog_hidden','export_mnemonic',
|
||||
'export_seed'),
|
||||
('quiet','verbose')
|
||||
): die_on_incompatible_opts(l)
|
||||
die_on_incompatible_opts(g.incompatible_opts)
|
||||
|
||||
return args
|
||||
|
||||
|
|
@ -225,15 +223,9 @@ def check_opts(usr_opts): # Returns false if any check fails
|
|||
from mmgen.util import check_outdir
|
||||
check_outdir(val) # exits on error
|
||||
elif key == 'label':
|
||||
if not opt_compares(len(val),"<=",g.max_wallet_label_len,"label length"):
|
||||
if not is_mmgen_wallet_label(val):
|
||||
msg("Illegal value for option '%s': '%s'" % (fmt_opt(key),val))
|
||||
return False
|
||||
try: val.decode("ascii")
|
||||
except:
|
||||
msg("ERROR: label contains a non-ASCII symbol")
|
||||
return False
|
||||
w = "character in label"
|
||||
for ch in list(val):
|
||||
if not opt_is_in_list(ch,g.wallet_label_symbols,w): return False
|
||||
# NEW
|
||||
elif key in ('in_fmt','out_fmt'):
|
||||
from mmgen.seed import SeedSource,IncogWallet,Brainwallet,IncogWalletHidden
|
||||
|
|
|
|||
242
mmgen/seed.py
242
mmgen/seed.py
|
|
@ -59,24 +59,29 @@ class SeedSource(MMGenObject):
|
|||
stdin_ok = False
|
||||
ask_tty = True
|
||||
no_tty = False
|
||||
op = None
|
||||
_msg = {}
|
||||
|
||||
class SeedSourceData(MMGenObject): pass
|
||||
|
||||
def __new__(cls,fn=None,ss=None,ignore_in_fmt_opt=False):
|
||||
def __new__(cls,fn=None,ss=None,ignore_in_fmt_opt=False,passchg=False):
|
||||
|
||||
def die_on_opt_mismatch(opt,sstype):
|
||||
opt_sstype = cls.fmt_code_to_sstype(opt)
|
||||
compare_or_die(
|
||||
opt_sstype.__name__, "input format specified on command line",
|
||||
opt_sstype.__name__, "input format requested on command line",
|
||||
sstype.__name__, "input file format"
|
||||
)
|
||||
|
||||
if ss:
|
||||
sstype = cls.fmt_code_to_sstype(opt.out_fmt)
|
||||
me = super(cls,cls).__new__(sstype or Wallet) # output default: Wallet
|
||||
if passchg:
|
||||
sstype = ss.__class__
|
||||
else:
|
||||
sstype = cls.fmt_code_to_sstype(opt.out_fmt)
|
||||
me = super(cls,cls).__new__(sstype or Wallet) # default: Wallet
|
||||
me.seed = ss.seed
|
||||
me.ss_in = ss
|
||||
me.op = ("conv","pwchg_new")[int(passchg)]
|
||||
elif fn or opt.hidden_incog_input_params:
|
||||
if fn:
|
||||
f = Filename(fn)
|
||||
|
|
@ -91,17 +96,20 @@ class SeedSource(MMGenObject):
|
|||
|
||||
me = super(cls,cls).__new__(sstype)
|
||||
me.infile = f
|
||||
me.op = ("old","pwchg_old")[int(passchg)]
|
||||
elif opt.in_fmt: # Input format
|
||||
sstype = cls.fmt_code_to_sstype(opt.in_fmt)
|
||||
me = super(cls,cls).__new__(sstype)
|
||||
me.op = ("old","pwchg_old")[int(passchg)]
|
||||
else: # Called with no inputs - initialize with random seed
|
||||
sstype = cls.fmt_code_to_sstype(opt.out_fmt)
|
||||
me = super(cls,cls).__new__(sstype or Wallet) # output default: Wallet
|
||||
me = super(cls,cls).__new__(sstype or Wallet) # default: Wallet
|
||||
me.seed = Seed()
|
||||
me.op = "new"
|
||||
|
||||
return me
|
||||
|
||||
def __init__(self,fn=None,ss=None,ignore_in_fmt_opt=False):
|
||||
def __init__(self,fn=None,ss=None,ignore_in_fmt_opt=False,passchg=False):
|
||||
|
||||
self.ssdata = self.SeedSourceData()
|
||||
self.msg = {}
|
||||
|
|
@ -113,6 +121,7 @@ class SeedSource(MMGenObject):
|
|||
if hasattr(self,'seed'):
|
||||
g.use_urandchars = True
|
||||
self._encrypt()
|
||||
return
|
||||
elif hasattr(self,'infile'):
|
||||
self._deformat_once()
|
||||
self._decrypt_retry()
|
||||
|
|
@ -123,6 +132,9 @@ class SeedSource(MMGenObject):
|
|||
self._deformat_retry()
|
||||
self._decrypt_retry()
|
||||
|
||||
m = (""," length %s" % self.seed.length)[int(self.seed.length != 256)]
|
||||
qmsg("Valid %s for seed ID %s%s" % (self.desc,self.seed.sid,m))
|
||||
|
||||
def _get_data(self):
|
||||
if hasattr(self,'infile'):
|
||||
self.fmt_data = get_data_from_file(self.infile.name,self.desc)
|
||||
|
|
@ -217,41 +229,113 @@ an empty passphrase, just hit ENTER twice.
|
|||
""".strip()
|
||||
}
|
||||
|
||||
def _get_pw(self,desc=None):
|
||||
self.ssdata.passwd = get_mmgen_passphrase(desc)
|
||||
|
||||
def _get_hash_preset(self,desc=None):
|
||||
# Converting:
|
||||
desc = desc or self.desc
|
||||
if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'hash_preset'):
|
||||
if opt.keep_hash_preset:
|
||||
a = self.ss_in.ssdata.hash_preset
|
||||
qmsg("Reusing hash preset '%s' as per user request" % a)
|
||||
elif 'hash_preset' in opt.set_by_user:
|
||||
# Prompt, but use user-requested value as default
|
||||
a = get_hash_preset_from_user(hp=opt.hash_preset,desc=desc)
|
||||
def _get_hash_preset_from_user(self,hp,desc_suf=""):
|
||||
# hp=a,
|
||||
n = ("","old ")[int(self.op=="pwchg_old")]
|
||||
m,n = (("to accept the default",n),("to reuse the old","new "))[
|
||||
int(self.op=="pwchg_new")]
|
||||
fs = "Enter {}hash preset for {}{}{},\n or hit ENTER {} value ('{}'): "
|
||||
p = fs.format(
|
||||
n,
|
||||
("","new ")[int(self.op=="new")],
|
||||
self.desc,
|
||||
(""," "+desc_suf)[int(bool(desc_suf))],
|
||||
m,
|
||||
hp
|
||||
)
|
||||
while True:
|
||||
ret = my_raw_input(p)
|
||||
if ret:
|
||||
if ret in g.hash_presets.keys():
|
||||
self.ssdata.hash_preset = ret
|
||||
return ret
|
||||
else:
|
||||
msg("Invalid input. Valid choices are %s" %
|
||||
", ".join(sorted(g.hash_presets.keys())))
|
||||
else:
|
||||
a = get_hash_preset_from_user(desc=desc)
|
||||
self.ssdata.hash_preset = hp
|
||||
return hp
|
||||
|
||||
def _get_hash_preset(self,desc_suf=""):
|
||||
if hasattr(self,"ss_in") and hasattr(self.ss_in.ssdata,"hash_preset"):
|
||||
old_hp = self.ss_in.ssdata.hash_preset
|
||||
if opt.keep_hash_preset:
|
||||
qmsg("Reusing hash preset '%s' at user request" % old_hp)
|
||||
self.ssdata.hash_preset = old_hp
|
||||
elif 'hash_preset' in opt.set_by_user:
|
||||
hp = self.ssdata.hash_preset = opt.hash_preset
|
||||
qmsg("Using hash preset '%s' requested on command line"
|
||||
% opt.hash_preset)
|
||||
else: # Prompt, using old value as default
|
||||
hp = self._get_hash_preset_from_user(old_hp,desc_suf)
|
||||
|
||||
if (not opt.keep_hash_preset) and self.op == "pwchg_new":
|
||||
m = ("changed to '%s'" % hp,"unchanged")[int(hp==old_hp)]
|
||||
qmsg("Hash preset %s" % m)
|
||||
elif 'hash_preset' in opt.set_by_user:
|
||||
a = opt.hash_preset
|
||||
qmsg("Using user-requested hash preset of '%s'" % a)
|
||||
self.ssdata.hash_preset = opt.hash_preset
|
||||
qmsg("Using hash preset '%s' requested on command line"%opt.hash_preset)
|
||||
else:
|
||||
a = get_hash_preset_from_user(desc=self.desc)
|
||||
self.ssdata.hash_preset = a
|
||||
self._get_hash_preset_from_user(opt.hash_preset,desc_suf)
|
||||
|
||||
def _get_new_passphrase(self):
|
||||
desc = "{}passphrase for {}{}".format(
|
||||
("","new ")[int(self.op=="pwchg_new")],
|
||||
("","new ")[int(self.op in ("new","conv"))],
|
||||
self.desc
|
||||
)
|
||||
if opt.passwd_file:
|
||||
w = pwfile_reuse_warning()
|
||||
pw = " ".join(get_words_from_file(opt.passwd_file,desc,silent=w))
|
||||
elif opt.echo_passphrase:
|
||||
pw = " ".join(get_words_from_user("Enter %s: " % desc))
|
||||
else:
|
||||
for i in range(g.passwd_max_tries):
|
||||
pw = " ".join(get_words_from_user("Enter %s: " % desc))
|
||||
pw2 = " ".join(get_words_from_user("Repeat passphrase: "))
|
||||
dmsg("Passphrases: [%s] [%s]" % (pw,pw2))
|
||||
if pw == pw2:
|
||||
vmsg("Passphrases match"); break
|
||||
else: msg("Passphrases do not match. Try again.")
|
||||
else:
|
||||
msg("User failed to duplicate passphrase in %s attempts" %
|
||||
g.passwd_max_tries)
|
||||
sys.exit(2)
|
||||
|
||||
if pw == "": qmsg("WARNING: Empty passphrase")
|
||||
self.ssdata.passwd = pw
|
||||
return pw
|
||||
|
||||
def _get_passphrase(self,desc_suf=""):
|
||||
desc ="{}passphrase for {}{}".format(
|
||||
("","old ")[int(self.op=="pwchg_old")],
|
||||
self.desc,
|
||||
(""," "+desc_suf)[int(bool(desc_suf))]
|
||||
)
|
||||
if opt.passwd_file:
|
||||
w = pwfile_reuse_warning()
|
||||
ret = " ".join(get_words_from_file(opt.passwd_file,desc,silent=w))
|
||||
else:
|
||||
ret = " ".join(get_words_from_user("Enter %s: " % desc))
|
||||
self.ssdata.passwd = ret
|
||||
|
||||
def _get_first_pw_and_hp_and_encrypt_seed(self):
|
||||
d = self.ssdata
|
||||
self._get_hash_preset()
|
||||
|
||||
if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'passwd') \
|
||||
and opt.keep_passphrase:
|
||||
d.passwd = self.ss_in.ssdata.passwd
|
||||
qmsg("Reusing passphrase as per user request")
|
||||
|
||||
self._get_hash_preset(desc="new " + self.desc)
|
||||
|
||||
if not hasattr(d,'passwd'):
|
||||
qmsg(self.msg['choose_passphrase'] % (self.desc,self.ssdata.hash_preset))
|
||||
d.passwd = get_new_passphrase(desc="new " + self.desc)
|
||||
if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'passwd'):
|
||||
old_pw = self.ss_in.ssdata.passwd
|
||||
if opt.keep_passphrase:
|
||||
d.passwd = old_pw
|
||||
qmsg("Reusing passphrase at user request")
|
||||
else:
|
||||
pw = self._get_new_passphrase()
|
||||
if self.op == "pwchg_new":
|
||||
m = ("changed","unchanged")[int(pw==old_pw)]
|
||||
qmsg("Passphrase %s" % m)
|
||||
else:
|
||||
qmsg(self.msg['choose_passphrase'] % (self.desc,d.hash_preset))
|
||||
self._get_new_passphrase()
|
||||
|
||||
d.salt = sha256(get_random(128)).digest()[:g.salt_len]
|
||||
key = make_key(d.passwd, d.salt, d.hash_preset)
|
||||
|
|
@ -279,7 +363,7 @@ class Mnemonic (SeedSourceUnenc):
|
|||
deconv = [wl.index(words[::-1][i])*(base**i)
|
||||
for i in range(len(words))]
|
||||
ret = ("{:0%sx}" % pad).format(sum(deconv))
|
||||
return "%s%s" % (('0' if len(ret) % 2 else ''), ret)
|
||||
return ('','0')[len(ret) % 2] + ret
|
||||
|
||||
def _hextobaseN(self,base,hexnum,wl,pad=0):
|
||||
num,ret = int(hexnum,16),[]
|
||||
|
|
@ -344,7 +428,6 @@ class Mnemonic (SeedSourceUnenc):
|
|||
|
||||
check_usr_seed_len(self.seed.length)
|
||||
|
||||
qmsg("Valid mnemonic for seed ID %s" % make_chksum_8(self.seed.data))
|
||||
return True
|
||||
|
||||
def _filename(self):
|
||||
|
|
@ -403,8 +486,6 @@ class SeedFile (SeedSourceUnenc):
|
|||
|
||||
check_usr_seed_len(self.seed.length)
|
||||
|
||||
qmsg("Valid seed data for seed ID %s" % make_chksum_8(self.seed.data))
|
||||
|
||||
return True
|
||||
|
||||
def _filename(self):
|
||||
|
|
@ -417,11 +498,47 @@ class Wallet (SeedSourceEnc):
|
|||
desc = g.proj_name + " wallet"
|
||||
ext = "mmdat"
|
||||
|
||||
def _get_label_from_user(self,old_lbl=""):
|
||||
d = ("to reuse the label '%s'" % old_lbl) if old_lbl else "for no label"
|
||||
p = "Enter a wallet label, or hit ENTER %s: " % d
|
||||
while True:
|
||||
ret = my_raw_input(p)
|
||||
if ret:
|
||||
if is_mmgen_wallet_label(ret):
|
||||
self.ssdata.label = ret; return ret
|
||||
else:
|
||||
msg("Invalid label. Trying again...")
|
||||
else:
|
||||
ret = old_lbl or "No Label"
|
||||
self.ssdata.label = ret; return ret
|
||||
|
||||
# nearly identical to _get_hash_preset() - factor?
|
||||
def _get_label(self):
|
||||
if hasattr(self,'ss_in') and hasattr(self.ss_in.ssdata,'label'):
|
||||
old_lbl = self.ss_in.ssdata.label
|
||||
if opt.keep_label:
|
||||
qmsg("Reusing label '%s' at user request" % old_lbl)
|
||||
self.ssdata.label = old_lbl
|
||||
elif opt.label:
|
||||
qmsg("Using label '%s' requested on command line" % opt.label)
|
||||
lbl = self.ssdata.label = opt.label
|
||||
else: # Prompt, using old value as default
|
||||
lbl = self._get_label_from_user(old_lbl)
|
||||
|
||||
if (not opt.keep_label) and self.op == "pwchg_new":
|
||||
m = ("changed to '%s'" % lbl,"unchanged")[int(lbl==old_lbl)]
|
||||
qmsg("Label %s" % m)
|
||||
elif opt.label:
|
||||
qmsg("Using label '%s' requested on command line" % opt.label)
|
||||
self.ssdata.label = opt.label
|
||||
else:
|
||||
self._get_label_from_user()
|
||||
|
||||
def _encrypt(self):
|
||||
self._get_first_pw_and_hp_and_encrypt_seed()
|
||||
self._get_label()
|
||||
d = self.ssdata
|
||||
d.label = opt.label or "No Label"
|
||||
d.pw_status = "NE" if len(d.passwd) else "E"
|
||||
d.pw_status = ("NE","E")[int(len(d.passwd)==0)]
|
||||
d.timestamp = make_timestamp()
|
||||
|
||||
def _format(self):
|
||||
|
|
@ -477,9 +594,10 @@ class Wallet (SeedSourceEnc):
|
|||
|
||||
d.hash_preset = hp = hpdata[0][:-1] # a string!
|
||||
qmsg("Hash preset of wallet: '%s'" % hp)
|
||||
uhp = opt.hash_preset
|
||||
if uhp and 'hash_preset' in opt.set_by_user and uhp != hp:
|
||||
msg("Warning: ignoring user-requested hash preset '%s'" % uhp)
|
||||
if 'hash_preset' in opt.set_by_user:
|
||||
uhp = opt.hash_preset
|
||||
if uhp != hp:
|
||||
qmsg("Warning: ignoring user-requested hash preset '%s'" % uhp)
|
||||
|
||||
hash_params = [int(i) for i in hpdata[1:]]
|
||||
|
||||
|
|
@ -514,8 +632,8 @@ class Wallet (SeedSourceEnc):
|
|||
def _decrypt(self):
|
||||
d = self.ssdata
|
||||
# Needed for multiple transactions with {}-txsign
|
||||
add = " "+self.infile.name if opt.quiet else ""
|
||||
self._get_pw(self.desc+add)
|
||||
suf = ("",self.infile.name)[int(bool(opt.quiet))]
|
||||
self._get_passphrase(desc_suf=suf)
|
||||
key = make_key(d.passwd, d.salt, d.hash_preset)
|
||||
ret = decrypt_seed(d.enc_seed, key, d.seed_id, d.key_id)
|
||||
if ret:
|
||||
|
|
@ -572,6 +690,7 @@ class Brainwallet (SeedSourceEnc):
|
|||
fmt_codes = "mmbrain","brainwallet","brain","bw","b"
|
||||
desc = "brainwallet"
|
||||
ext = "mmbrain"
|
||||
# brainwallet warning message? TODO
|
||||
|
||||
def _deformat(self):
|
||||
self.brainpasswd = " ".join(self.fmt_data.split())
|
||||
|
|
@ -620,7 +739,7 @@ to exit and re-run the program with the '--old-incog-fmt' option.
|
|||
def _make_iv_chksum(self,s): return sha256(s).hexdigest()[:8].upper()
|
||||
|
||||
def _get_incog_data_len(self,seed_len):
|
||||
e = 0 if opt.old_incog_fmt else g.hincog_chk_len
|
||||
e = (g.hincog_chk_len,0)[int(bool(opt.old_incog_fmt))]
|
||||
return g.aesctr_iv_len + g.salt_len + e + seed_len/8
|
||||
|
||||
def _incog_data_size_chk(self):
|
||||
|
|
@ -718,9 +837,8 @@ to exit and re-run the program with the '--old-incog-fmt' option.
|
|||
|
||||
def _decrypt(self):
|
||||
d = self.ssdata
|
||||
desc = self.desc+" "+d.incog_id
|
||||
self._get_hash_preset(desc)
|
||||
self._get_pw(desc)
|
||||
self._get_hash_preset(desc_suf=d.incog_id)
|
||||
self._get_passphrase(desc_suf=d.incog_id)
|
||||
|
||||
# IV is used BOTH to initialize counter and to salt password!
|
||||
key = make_key(d.passwd, d.iv, d.hash_preset, "wrapper key")
|
||||
|
|
@ -731,10 +849,10 @@ to exit and re-run the program with the '--old-incog-fmt' option.
|
|||
d.enc_seed = dd[g.salt_len:]
|
||||
|
||||
key = make_key(d.passwd, d.salt, d.hash_preset, "main key")
|
||||
msg("Key ID: %s" % make_chksum_8(key))
|
||||
qmsg("Key ID: %s" % make_chksum_8(key))
|
||||
|
||||
verify_seed = self._verify_seed_oldfmt if opt.old_incog_fmt else \
|
||||
self._verify_seed_newfmt
|
||||
verify_seed = getattr(self,"_verify_seed_"+
|
||||
("newfmt","oldfmt")[int(bool(opt.old_incog_fmt))])
|
||||
|
||||
seed = verify_seed(decrypt_seed(d.enc_seed, key, "", ""))
|
||||
|
||||
|
|
@ -749,7 +867,7 @@ to exit and re-run the program with the '--old-incog-fmt' option.
|
|||
class IncogWalletHex (IncogWallet):
|
||||
|
||||
desc = "hex incognito data"
|
||||
fmt_codes = "mmincox","incog_hex","xincog","ix","xi"
|
||||
fmt_codes = "mmincox","incox","incog_hex","xincog","ix","xi"
|
||||
ext = "mmincox"
|
||||
no_tty = False
|
||||
|
||||
|
|
@ -800,7 +918,7 @@ harder to find, you're advised to choose a much larger file size than this.
|
|||
|
||||
def _check_valid_offset(self,fn,action):
|
||||
d = self.ssdata
|
||||
m = "Destination" if action == "write" else "Input"
|
||||
m = ("Input","Destination")[int(action=="write")]
|
||||
if fn.size < d.hincog_offset + d.target_data_len:
|
||||
die(1,
|
||||
"%s file has length %s, too short to %s %s bytes of data at offset %s"
|
||||
|
|
@ -830,9 +948,11 @@ harder to find, you're advised to choose a much larger file size than this.
|
|||
self._format()
|
||||
compare_or_die(d.target_data_len, "target data length",
|
||||
len(self.fmt_data),"length of formatted " + self.desc)
|
||||
fn,d.hincog_offset = self._get_hincog_params("output")
|
||||
|
||||
self.hincog_data_is_new = False
|
||||
k = ("output","input")[int(self.op=="pwchg_new")]
|
||||
fn,d.hincog_offset = self._get_hincog_params(k)
|
||||
|
||||
check_offset = True
|
||||
try:
|
||||
os.stat(fn)
|
||||
except:
|
||||
|
|
@ -841,14 +961,14 @@ harder to find, you're advised to choose a much larger file size than this.
|
|||
min_fsize = d.target_data_len + d.hincog_offset
|
||||
msg(self.msg['choose_file_size'].format(min_fsize))
|
||||
while True:
|
||||
fsize = my_raw_input("Enter file size: ")
|
||||
if is_int(fsize) and int(fsize) >= min_fsize: break
|
||||
fsize = parse_nbytes(my_raw_input("Enter file size: "))
|
||||
if fsize >= min_fsize: break
|
||||
msg("File size must be an integer no less than %s" %
|
||||
min_fsize)
|
||||
|
||||
from mmgen.tool import rand2file
|
||||
rand2file(fn, str(fsize))
|
||||
self.hincog_data_is_new = True
|
||||
check_offset = False
|
||||
else:
|
||||
die(1,"Exiting at user request")
|
||||
|
||||
|
|
@ -856,7 +976,7 @@ harder to find, you're advised to choose a much larger file size than this.
|
|||
|
||||
dmsg("Incog data len %s, offset %s" % (d.target_data_len,d.hincog_offset))
|
||||
|
||||
if not self.hincog_data_is_new:
|
||||
if check_offset:
|
||||
self._check_valid_offset(f,"write")
|
||||
if not opt.quiet: confirm_or_exit("","alter file '%s'" % f.name)
|
||||
|
||||
|
|
@ -865,5 +985,5 @@ harder to find, you're advised to choose a much larger file size than this.
|
|||
os.write(fh, self.fmt_data)
|
||||
os.close(fh)
|
||||
msg("%s written to file '%s' at offset %s" % (
|
||||
self.desc[0].upper()+self.desc[1:],
|
||||
capfirst(self.desc),
|
||||
f.name,d.hincog_offset))
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ def print_help(opts_data):
|
|||
def process_opts(argv,opts_data,short_opts,long_opts):
|
||||
|
||||
import os
|
||||
opts_data['prog_name'] = os.path.split(sys.argv[0])[1]
|
||||
opts_data['prog_name'] = os.path.basename(sys.argv[0])
|
||||
long_opts = [i.replace("_","-") for i in long_opts]
|
||||
|
||||
try: cl_opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
|
||||
|
|
@ -80,22 +80,28 @@ def process_opts(argv,opts_data,short_opts,long_opts):
|
|||
return opts,args
|
||||
|
||||
|
||||
def parse_opts(argv,opts_data):
|
||||
def parse_opts(argv,opts_data,opt_filter=None):
|
||||
|
||||
lines = opts_data['options'].strip().split("\n")
|
||||
import re
|
||||
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]
|
||||
pat = r"^-([a-zA-Z0-9]), --([a-zA-Z0-9-]{2,64})(=| )(.+)"
|
||||
od,skip = [],True
|
||||
|
||||
for l in opts_data['options'].strip().split("\n"):
|
||||
m = re.match(pat,l)
|
||||
if m:
|
||||
skip = True if (opt_filter and m.group(1) not in opt_filter) else False
|
||||
app = [':','='] if (m.group(3) == '=') else ['','']
|
||||
od.append(list(m.groups()) + app + [skip])
|
||||
else:
|
||||
if not skip: od[-1][3] += "\n" + l
|
||||
|
||||
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]
|
||||
opts_data['options'] = "\n".join(
|
||||
[rep.format(w=" ", *m.groups())
|
||||
if m else k for m,k in [(re.match(pat,l),l) for l in lines]]
|
||||
["-{}, --{} {}".format(d[0],d[1],d[3]) for d in od if d[6] == False]
|
||||
)
|
||||
short_opts = "".join([d[0]+d[4] for d in od if d[6] == False])
|
||||
long_opts = [d[1].replace("-","_")+d[5] for d in od if d[6] == False]
|
||||
skipped_opts = [d[1].replace("-","_") for d in od if d[6] == True]
|
||||
|
||||
opts,args = process_opts(argv,opts_data,short_opts,long_opts)
|
||||
|
||||
return opts,args,short_opts,long_opts
|
||||
return opts,args,short_opts,long_opts,skipped_opts
|
||||
|
|
|
|||
|
|
@ -556,28 +556,6 @@ def find_incog_data(filename,iv_id,keep_searching=False):
|
|||
msg("")
|
||||
os.close(f)
|
||||
|
||||
# From "man dd":
|
||||
# c=1, w=2, b=512, kB=1000, K=1024, MB=1000*1000, M=1024*1024,
|
||||
# GB=1000*1000*1000, G=1024*1024*1024, and so on for T, P, E, Z, Y.
|
||||
|
||||
def parse_nbytes(nbytes):
|
||||
import re
|
||||
m = re.match(r'([0123456789]+)(.*)',nbytes)
|
||||
smap = ("c",1),("w",2),("b",512),("kB",1000),("K",1024),("MB",1000*1000),\
|
||||
("M",1024*1024),("GB",1000*1000*1000),("G",1024*1024*1024)
|
||||
if m:
|
||||
if m.group(2):
|
||||
for k,v in smap:
|
||||
if k == m.group(2):
|
||||
return int(m.group(1)) * v
|
||||
else:
|
||||
msg("Valid byte specifiers: '%s'" % "' '".join([i[0] for i in smap]))
|
||||
else:
|
||||
return int(nbytes)
|
||||
|
||||
msg("'%s': invalid byte specifier" % nbytes)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def rand2file(outfile, nbytes, threads=4, silent=False):
|
||||
nbytes = parse_nbytes(nbytes)
|
||||
|
|
|
|||
|
|
@ -55,6 +55,42 @@ def die(ev,s):
|
|||
def Die(ev,s):
|
||||
sys.stdout.write(s+"\n"); sys.exit(ev)
|
||||
|
||||
def is_mmgen_wallet_label(s):
|
||||
if len(s) > g.max_wallet_label_len:
|
||||
msg("ERROR: wallet label length (%s chars) > maximum allowed (%s chars)" % (len(s),g.max_wallet_label_len))
|
||||
return False
|
||||
|
||||
try: s = s.decode("utf8")
|
||||
except: pass
|
||||
|
||||
for ch in s:
|
||||
if ch not in g.wallet_label_symbols:
|
||||
msg("ERROR: wallet label contains illegal symbol (%s)" % ch)
|
||||
return False
|
||||
return True
|
||||
|
||||
# From "man dd":
|
||||
# c=1, w=2, b=512, kB=1000, K=1024, MB=1000*1000, M=1024*1024,
|
||||
# GB=1000*1000*1000, G=1024*1024*1024, and so on for T, P, E, Z, Y.
|
||||
|
||||
def parse_nbytes(nbytes):
|
||||
import re
|
||||
m = re.match(r'([0123456789]+)(.*)',nbytes)
|
||||
smap = ("c",1),("w",2),("b",512),("kB",1000),("K",1024),("MB",1000*1000),\
|
||||
("M",1024*1024),("GB",1000*1000*1000),("G",1024*1024*1024)
|
||||
if m:
|
||||
if m.group(2):
|
||||
for k,v in smap:
|
||||
if k == m.group(2):
|
||||
return int(m.group(1)) * v
|
||||
else:
|
||||
msg("Valid byte specifiers: '%s'" % "' '".join([i[0] for i in smap]))
|
||||
else:
|
||||
return int(nbytes)
|
||||
|
||||
msg("'%s': invalid byte specifier" % nbytes)
|
||||
sys.exit(1)
|
||||
|
||||
import opt
|
||||
|
||||
def qmsg(s,alt=False):
|
||||
|
|
@ -121,8 +157,8 @@ def split_into_cols(col_wid,s):
|
|||
for i in range(len(s)/col_wid+1)]).rstrip()
|
||||
|
||||
def capfirst(s):
|
||||
if len(s) == 0: return s
|
||||
return s[0].upper() + (s[1:] if len(s) > 1 else "")
|
||||
return s if len(s) == 0 else \
|
||||
(s[0].upper() + (s[1:] if len(s) > 1 else ""))
|
||||
|
||||
def make_timestamp():
|
||||
tv = time.gmtime(time.time())[:6]
|
||||
|
|
@ -168,8 +204,6 @@ def file_exists(f):
|
|||
except:
|
||||
return False
|
||||
|
||||
import opt as opt
|
||||
|
||||
def get_from_brain_opt_params():
|
||||
l,p = opt.from_brain.split(",")
|
||||
return(int(l),p)
|
||||
|
|
@ -209,7 +243,7 @@ def compare_chksums(chk1, desc1, chk2, desc2, hdr="", die_on_fail=False):
|
|||
if die_on_fail:
|
||||
die(3,m)
|
||||
else:
|
||||
msg(m)
|
||||
vmsg(m)
|
||||
return False
|
||||
|
||||
vmsg("%s checksum OK (%s)" % (capfirst(desc1),chk1))
|
||||
|
|
@ -320,13 +354,13 @@ def get_new_passphrase(desc,passchg=False):
|
|||
|
||||
w = "{}passphrase for {}".format("new " if passchg else "", desc)
|
||||
if opt.passwd_file:
|
||||
pw = " ".join(_get_words_from_file(opt.passwd_file,w))
|
||||
pw = " ".join(get_words_from_file(opt.passwd_file,w))
|
||||
elif opt.echo_passphrase:
|
||||
pw = " ".join(_get_words_from_user("Enter {}: ".format(w)))
|
||||
pw = " ".join(get_words_from_user("Enter {}: ".format(w)))
|
||||
else:
|
||||
for i in range(g.passwd_max_tries):
|
||||
pw = " ".join(_get_words_from_user("Enter {}: ".format(w)))
|
||||
pw2 = " ".join(_get_words_from_user("Repeat passphrase: "))
|
||||
pw = " ".join(get_words_from_user("Enter {}: ".format(w)))
|
||||
pw2 = " ".join(get_words_from_user("Repeat passphrase: "))
|
||||
dmsg("Passphrases: [%s] [%s]" % (pw,pw2))
|
||||
if pw == pw2:
|
||||
vmsg("Passphrases match"); break
|
||||
|
|
@ -626,15 +660,16 @@ def get_data_from_wallet(infile,silent=False):
|
|||
return label,metadata,hash_preset,res['salt'],res['enc_seed']
|
||||
|
||||
|
||||
def _get_words_from_user(prompt):
|
||||
def get_words_from_user(prompt):
|
||||
# split() also strips
|
||||
words = my_raw_input(prompt, echo=opt.echo_passphrase).split()
|
||||
dmsg("Sanitized input: [%s]" % " ".join(words))
|
||||
return words
|
||||
|
||||
|
||||
def _get_words_from_file(infile,desc):
|
||||
qmsg("Getting %s from file '%s'" % (desc,infile))
|
||||
def get_words_from_file(infile,desc,silent=False):
|
||||
if not silent:
|
||||
qmsg("Getting %s from file '%s'" % (desc,infile))
|
||||
f = open_file_or_exit(infile, 'r')
|
||||
# split() also strips
|
||||
words = f.read().split()
|
||||
|
|
@ -645,9 +680,9 @@ def _get_words_from_file(infile,desc):
|
|||
|
||||
def get_words(infile,desc,prompt):
|
||||
if infile:
|
||||
return _get_words_from_file(infile,desc)
|
||||
return get_words_from_file(infile,desc)
|
||||
else:
|
||||
return _get_words_from_user(prompt)
|
||||
return get_words_from_user(prompt)
|
||||
|
||||
def remove_comments(lines):
|
||||
# re.sub(pattern, repl, string, count=0, flags=0)
|
||||
|
|
@ -709,26 +744,27 @@ def get_seed_from_seed_data(words):
|
|||
|
||||
passwd_file_used = False
|
||||
|
||||
def mark_passwd_file_as_used():
|
||||
def pwfile_reuse_warning():
|
||||
global passwd_file_used
|
||||
if passwd_file_used:
|
||||
msg_r("WARNING: Reusing passphrase from file '%s'." % opt.passwd_file)
|
||||
msg(" This may not be what you want!")
|
||||
qmsg("Reusing passphrase from file '%s' at user request" % opt.passwd_file)
|
||||
return True
|
||||
passwd_file_used = True
|
||||
return False
|
||||
|
||||
|
||||
def get_mmgen_passphrase(desc,passchg=False):
|
||||
prompt ="Enter {}passphrase for {}: ".format("old " if passchg else "",desc)
|
||||
if opt.passwd_file:
|
||||
mark_passwd_file_as_used()
|
||||
return " ".join(_get_words_from_file(opt.passwd_file,"passphrase"))
|
||||
pwfile_reuse_warning()
|
||||
return " ".join(get_words_from_file(opt.passwd_file,"passphrase"))
|
||||
else:
|
||||
return " ".join(_get_words_from_user(prompt))
|
||||
return " ".join(get_words_from_user(prompt))
|
||||
|
||||
|
||||
def get_bitcoind_passphrase(prompt):
|
||||
if opt.passwd_file:
|
||||
mark_passwd_file_as_used()
|
||||
pwfile_reuse_warning()
|
||||
return get_data_from_file(opt.passwd_file,
|
||||
"passphrase").strip("\r\n")
|
||||
else:
|
||||
|
|
|
|||
2
setup.py
2
setup.py
|
|
@ -58,8 +58,8 @@ setup(
|
|||
'mmgen.main_txcreate',
|
||||
'mmgen.main_txsend',
|
||||
'mmgen.main_txsign',
|
||||
'mmgen.main_wallet',
|
||||
'mmgen.main_walletchk',
|
||||
'mmgen.main_walletconv',
|
||||
'mmgen.main_walletgen',
|
||||
|
||||
'mmgen.share.__init__',
|
||||
|
|
|
|||
1535
test/test-oldscripts.py
Executable file
1535
test/test-oldscripts.py
Executable file
File diff suppressed because it is too large
Load diff
211
test/test.py
211
test/test.py
|
|
@ -10,7 +10,7 @@ sys.path.__setitem__(0,os.path.abspath(os.curdir))
|
|||
|
||||
import mmgen.globalvars as g
|
||||
import mmgen.opt as opt
|
||||
from mmgen.util import mmsg,mdie,Msg,die
|
||||
from mmgen.util import mmsg,mdie,Msg,die,capfirst
|
||||
from mmgen.test import *
|
||||
|
||||
hincog_fn = "rand_data"
|
||||
|
|
@ -376,6 +376,7 @@ opts_data = {
|
|||
debugging only)
|
||||
-e, --exact-output Show the exact output of the MMGen script(s) being run
|
||||
-l, --list-cmds List and describe the tests and commands in the test suite
|
||||
-n, --names Display command names instead of descriptions
|
||||
-p, --pause Pause between tests, resuming on keypress
|
||||
-q, --quiet Produce minimal output. Suppress dependency info
|
||||
-s, --system Test scripts and modules installed on system rather than
|
||||
|
|
@ -392,6 +393,9 @@ cmd_args = opt.opts.init(opts_data)
|
|||
|
||||
if opt.system: sys.path.pop(0)
|
||||
|
||||
# temporary
|
||||
#os.environ["MMGEN_USE_OLD_SCRIPTS"] = "1"
|
||||
|
||||
if opt.debug_scripts: os.environ["MMGEN_DEBUG"] = "1"
|
||||
|
||||
if opt.buf_keypress:
|
||||
|
|
@ -524,15 +528,15 @@ class MMGenExpect(object):
|
|||
def __init__(self,name,mmgen_cmd,cmd_args=[],extra_desc=""):
|
||||
if not opt.system:
|
||||
mmgen_cmd = os.path.join(os.curdir,mmgen_cmd)
|
||||
desc = cmd_data[name][1]
|
||||
desc = (cmd_data[name][1],name)[int(bool(opt.names))]
|
||||
if extra_desc: desc += " " + extra_desc
|
||||
if opt.verbose or opt.exact_output:
|
||||
sys.stderr.write(
|
||||
green("Testing %s\nExecuting " % desc) +
|
||||
green("Testing: %s\nExecuting " % desc) +
|
||||
cyan("'%s %s'\n" % (mmgen_cmd," ".join(cmd_args)))
|
||||
)
|
||||
else:
|
||||
msg_r("Testing %s " % (desc+":"))
|
||||
msg_r("Testing %s: " % desc)
|
||||
|
||||
if opt.direct_exec:
|
||||
os.system(" ".join([mmgen_cmd] + cmd_args))
|
||||
|
|
@ -545,6 +549,14 @@ class MMGenExpect(object):
|
|||
p = "'w' for conditions and warranty info, or 'c' to continue: "
|
||||
my_expect(self.p,p,'c')
|
||||
|
||||
def label(self,label="Test Label"):
|
||||
p = "Enter a wallet label, or hit ENTER for no label: "
|
||||
my_expect(self.p,p,label+"\n")
|
||||
|
||||
def usr_rand_out(self,saved=False):
|
||||
m = "%suser-supplied entropy" % ("saved " if saved else "")
|
||||
my_expect(self.p,"Generating encryption key from OS random data plus " + m)
|
||||
|
||||
def usr_rand(self,num_chars):
|
||||
rand_chars = list(getrandstr(num_chars,no_space=True))
|
||||
my_expect(self.p,'symbols left: ','x')
|
||||
|
|
@ -645,8 +657,9 @@ def create_fake_unspent_data(adata,unspent_data_file,tx_data,non_mmgen_input='')
|
|||
for s in tx_data.keys():
|
||||
sid = tx_data[s]['sid']
|
||||
a = adata.addrinfo(sid)
|
||||
for idx,btcaddr in a.addrpairs():
|
||||
add_fake_unspent_entry(out,btcaddr,"%s:%s Test Wallet" % (sid,idx))
|
||||
for n,(idx,btcaddr) in enumerate(a.addrpairs(),1):
|
||||
lbl = (""," addr %02i" % n)[int(bool(n%3))]
|
||||
add_fake_unspent_entry(out,btcaddr,"%s:%s%s" % (sid,idx,lbl))
|
||||
|
||||
if non_mmgen_input:
|
||||
from mmgen.bitcoin import privnum2addr,hextowif
|
||||
|
|
@ -821,47 +834,32 @@ class MMGenTestSuite(object):
|
|||
def generate_cmd_deps(self,fdeps):
|
||||
return [cfgs[str(n)]['dep_generators'][ext] for n,ext in fdeps]
|
||||
|
||||
def walletgen(self,name,brain=False,seed_len=None):
|
||||
|
||||
def walletgen(self,name,seed_len=None):
|
||||
args = ["-d",cfg['tmpdir'],"-p1","-r10"]
|
||||
if seed_len: args += ["-l",str(seed_len)]
|
||||
# if 'seed_len' in cfg: args += ["-l",cfg['seed_len']]
|
||||
if brain:
|
||||
bwf = os.path.join(cfg['tmpdir'],cfg['bw_filename'])
|
||||
args += ["-b",cfg['bw_params'],bwf]
|
||||
make_brainwallet_file(bwf)
|
||||
|
||||
t = MMGenExpect(name,"mmgen-walletgen", args)
|
||||
t.license()
|
||||
|
||||
if brain:
|
||||
t.expect(
|
||||
"A brainwallet will be secure only if you really know what you're doing")
|
||||
t.expect("Type uppercase 'YES' to confirm: ","YES\n")
|
||||
|
||||
t.usr_rand(10)
|
||||
for s in "user-supplied entropy","saved user-supplied entropy":
|
||||
t.expect("Generating encryption key from OS random data plus %s" % s)
|
||||
if brain: break
|
||||
|
||||
t.passphrase_new("new MMGen wallet",cfg['wpasswd'])
|
||||
t.written_to_file("Wallet")
|
||||
t.label()
|
||||
t.written_to_file("MMGen wallet")
|
||||
ok()
|
||||
|
||||
def refwalletgen(self,name):
|
||||
def brainwalletgen_ref(self,name):
|
||||
sl_arg = "-l%s" % cfg['seed_len']
|
||||
hp_arg = "-p%s" % ref_wallet_hash_preset
|
||||
label = "test.py ref. wallet (pw '%s', seed len %s)" \
|
||||
% (ref_wallet_brainpass,cfg['seed_len'])
|
||||
bw_arg = "-b%s,%s" % (cfg['seed_len'], ref_wallet_hash_preset)
|
||||
args = ["-d",cfg['tmpdir'],"-p1","-r10",bw_arg,"-L",label]
|
||||
d = " (%s-bit seed)" % cfg['seed_len']
|
||||
t = MMGenExpect(name,"mmgen-walletgen", args)
|
||||
% (ref_wallet_brainpass,cfg['seed_len'])
|
||||
args = ["-d",cfg['tmpdir'],hp_arg,"-r10",sl_arg,"-ib","-L",label]
|
||||
t = MMGenExpect(name,"mmgen-walletconv", args)
|
||||
t.license()
|
||||
t.expect("Type uppercase 'YES' to confirm: ","YES\n")
|
||||
t.expect("passphrase: ",ref_wallet_brainpass+"\n")
|
||||
t.usr_rand(10)
|
||||
t.expect("Enter brainwallet: ", ref_wallet_brainpass+"\n")
|
||||
t.passphrase_new("new MMGen wallet",cfg['wpasswd'])
|
||||
seed_id = t.written_to_file("Wallet").split("-")[0].split("/")[-1]
|
||||
refcheck("seed ID",seed_id,cfg['seed_id'])
|
||||
t.usr_rand(10)
|
||||
sid = t.written_to_file("MMGen wallet").split("-")[0].split("/")[-1]
|
||||
refcheck("seed ID",sid,cfg['seed_id'])
|
||||
|
||||
def refwalletgen(self,name): self.brainwalletgen_ref(name)
|
||||
|
||||
refwalletgen1 = refwalletgen2 = refwalletgen3 = refwalletgen
|
||||
|
||||
|
|
@ -869,22 +867,23 @@ class MMGenTestSuite(object):
|
|||
|
||||
t = MMGenExpect(name,"mmgen-passchg",
|
||||
["-d",cfg['tmpdir'],"-p","2","-L","New Label","-r","16",walletfile])
|
||||
t.license()
|
||||
t.passphrase("MMGen wallet",cfgs['1']['wpasswd'],pwtype="old")
|
||||
t.expect_getend("Label changed: ")
|
||||
t.expect_getend("Hash preset changed: ")
|
||||
t.expect_getend("Hash preset changed to ")
|
||||
t.passphrase("MMGen wallet",cfg['wpasswd'],pwtype="new")
|
||||
t.expect("Repeat passphrase: ",cfg['wpasswd']+"\n")
|
||||
t.usr_rand(16)
|
||||
t.expect_getend("Key ID changed: ")
|
||||
t.written_to_file("Wallet")
|
||||
t.expect_getend("Label changed to ")
|
||||
# t.expect_getend("Key ID changed: ")
|
||||
t.written_to_file("MMGen wallet")
|
||||
ok()
|
||||
|
||||
def walletchk_beg(self,name,args):
|
||||
t = MMGenExpect(name,"mmgen-walletchk", args)
|
||||
t.expect("Getting MMGen wallet data from file '%s'" % args[-1])
|
||||
t.expect("Getting MMGen wallet from file '%s'" % args[-1])
|
||||
t.passphrase("MMGen wallet",cfg['wpasswd'])
|
||||
t.expect("Passphrase is OK")
|
||||
t.expect("Wallet is OK")
|
||||
t.expect_getend("Valid MMGen wallet for seed ID ")
|
||||
return t
|
||||
|
||||
def walletchk(self,name,walletfile):
|
||||
|
|
@ -1036,53 +1035,67 @@ class MMGenTestSuite(object):
|
|||
vmsg("This is a simulation; no transaction was sent")
|
||||
ok()
|
||||
|
||||
def export_seed(self,name,walletfile):
|
||||
t = self.walletchk_beg(name,["-s","-d",cfg['tmpdir'],walletfile])
|
||||
f = t.written_to_file("Seed data")
|
||||
silence()
|
||||
msg("Seed data: %s" % cyan(get_data_from_file(f,"seed data")))
|
||||
end_silence()
|
||||
ok()
|
||||
|
||||
def export_mnemonic(self,name,walletfile):
|
||||
t = self.walletchk_beg(name,["-m","-d",cfg['tmpdir'],walletfile])
|
||||
f = t.written_to_file("Mnemonic data")
|
||||
silence()
|
||||
msg_r("Mnemonic data: %s" % cyan(get_data_from_file(f,"mnemonic data")))
|
||||
end_silence()
|
||||
ok()
|
||||
|
||||
def export_incog(self,name,walletfile,args=["-g"]):
|
||||
t = MMGenExpect(name,"mmgen-walletchk",args+["-d",cfg['tmpdir'],"-r","10",walletfile])
|
||||
def walletconv_export(self,name,wf,desc,uargs=[],out_fmt="w",pw=False):
|
||||
opts = ["-d",cfg['tmpdir'],"-o",out_fmt] + uargs + [wf]
|
||||
t = MMGenExpect(name,"mmgen-walletconv",opts)
|
||||
t.license()
|
||||
t.passphrase("MMGen wallet",cfg['wpasswd'])
|
||||
t.usr_rand(10)
|
||||
incog_id = t.expect_getend("Incog ID: ")
|
||||
write_to_tmpfile(cfg,incog_id_fn,incog_id+"\n")
|
||||
if args[0] == "-G": return t
|
||||
t.written_to_file("Incognito wallet data",overwrite_unlikely=True)
|
||||
if pw:
|
||||
t.passphrase_new("new "+desc,cfg['wpasswd'])
|
||||
t.usr_rand(10)
|
||||
if " ".join(desc.split()[-2:]) == "incognito data":
|
||||
t.expect("Generating encryption key from OS random data ")
|
||||
t.expect("Generating encryption key from OS random data ")
|
||||
ic_id = t.expect_getend("New Incog Wallet ID: ")
|
||||
t.expect("Generating encryption key from OS random data ")
|
||||
if desc == "hidden incognito data":
|
||||
write_to_tmpfile(cfg,incog_id_fn,ic_id)
|
||||
ret = t.expect(["Create? (Y/n): ","'YES' to confirm: "])
|
||||
if ret == 0:
|
||||
t.send("\n")
|
||||
t.expect("Enter file size: ",str(hincog_bytes)+"\n")
|
||||
else:
|
||||
t.send("YES\n")
|
||||
if out_fmt == "w": t.label()
|
||||
return t.written_to_file(capfirst(desc),oo=True)
|
||||
|
||||
def export_seed(self,name,wf,desc="seed data",out_fmt="seed"):
|
||||
f = self.walletconv_export(name,wf,desc=desc,out_fmt=out_fmt)
|
||||
silence()
|
||||
msg("%s: %s" % (capfirst(desc),cyan(get_data_from_file(f,desc))))
|
||||
end_silence()
|
||||
ok()
|
||||
|
||||
def export_incog_hex(self,name,walletfile):
|
||||
self.export_incog(name,walletfile,args=["-X"])
|
||||
def export_mnemonic(self,name,wf):
|
||||
self.export_seed(name,wf,desc="mnemonic data",out_fmt="words")
|
||||
|
||||
def export_incog(self,name,wf,desc="incognito data",out_fmt="i",add_args=[]):
|
||||
uargs = ["-p1","-r10"] + add_args
|
||||
self.walletconv_export(name,wf,desc=desc,out_fmt=out_fmt,uargs=uargs,pw=True)
|
||||
ok()
|
||||
|
||||
def export_incog_hex(self,name,wf):
|
||||
self.export_incog(name,wf,desc="hex incognito data",out_fmt="xi")
|
||||
|
||||
# TODO: make outdir and hidden incog compatible (ignore --outdir and warn user?)
|
||||
def export_incog_hidden(self,name,walletfile):
|
||||
rf,rd = os.path.join(cfg['tmpdir'],hincog_fn),os.urandom(hincog_bytes)
|
||||
vmsg(green("Writing %s bytes of data to file '%s'" % (hincog_bytes,rf)))
|
||||
write_to_file(rf,rd,verbose=opt.verbose)
|
||||
t = self.export_incog(name,walletfile,args=["-G","%s,%s"%(rf,hincog_offset)])
|
||||
t.written_to_file("Data",query="")
|
||||
ok()
|
||||
def export_incog_hidden(self,name,wf):
|
||||
rf = os.path.join(cfg['tmpdir'],hincog_fn)
|
||||
add_args = ["-J","%s,%s"%(rf,hincog_offset)]
|
||||
self.export_incog(
|
||||
name,wf,desc="hidden incognito data",out_fmt="hi",add_args=add_args)
|
||||
|
||||
def addrgen_seed(self,name,walletfile,foo,desc="seed data",arg="-s"):
|
||||
t = MMGenExpect(name,"mmgen-addrgen",
|
||||
stdout = (False,True)[int(desc=="seed data")] #capture output to screen once
|
||||
add_arg = ([],["-S"])[int(stdout)]
|
||||
t = MMGenExpect(name,"mmgen-addrgen", add_arg +
|
||||
[arg,"-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
|
||||
t.license()
|
||||
t.expect_getend("Valid %s for seed ID " % desc)
|
||||
vmsg("Comparing generated checksum with checksum from previous address file")
|
||||
chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
|
||||
if stdout: t.read()
|
||||
verify_checksum_or_exit(get_addrfile_checksum(),chk)
|
||||
t.no_overwrite()
|
||||
# t.no_overwrite()
|
||||
ok()
|
||||
|
||||
def addrgen_mnemonic(self,name,walletfile,foo):
|
||||
|
|
@ -1097,8 +1110,9 @@ class MMGenTestSuite(object):
|
|||
t.hash_preset("incog wallet",'1')
|
||||
vmsg("Comparing generated checksum with checksum from address file")
|
||||
chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
|
||||
t.close()
|
||||
verify_checksum_or_exit(get_addrfile_checksum(),chk)
|
||||
t.no_overwrite()
|
||||
# t.no_overwrite()
|
||||
ok()
|
||||
|
||||
def addrgen_incog_hex(self,name,walletfile,foo):
|
||||
|
|
@ -1180,8 +1194,20 @@ class MMGenTestSuite(object):
|
|||
self.txsign_end(t)
|
||||
ok()
|
||||
|
||||
def walletgen4(self,name):
|
||||
self.walletgen(name,brain=True)
|
||||
def brainwalletgen_pwfile(self,name):
|
||||
bwf = os.path.join(cfg['tmpdir'],cfg['bw_filename'])
|
||||
make_brainwallet_file(bwf)
|
||||
seed_len = str(cfg['seed_len'])
|
||||
args = ["-d",cfg['tmpdir'],"-p1","-r10","-l"+seed_len,"-ib"]
|
||||
t = MMGenExpect(name,"mmgen-walletconv", args + [bwf])
|
||||
t.license()
|
||||
t.passphrase_new("new MMGen wallet",cfg['wpasswd'])
|
||||
t.usr_rand(10)
|
||||
t.label()
|
||||
t.written_to_file("MMGen wallet")
|
||||
ok()
|
||||
|
||||
def walletgen4(self,name): self.brainwalletgen_pwfile(name)
|
||||
|
||||
def addrgen4(self,name,walletfile):
|
||||
self.addrgen(name,walletfile)
|
||||
|
|
@ -1239,30 +1265,33 @@ class MMGenTestSuite(object):
|
|||
vmsg("Incog ID: %s" % cyan(i_id))
|
||||
t = MMGenExpect(name,"mmgen-tool",
|
||||
["-d",cfg['tmpdir'],"find_incog_data",f1,i_id])
|
||||
o = t.expect_getend("Incog data for ID \w{8} found at offset ",regex=True)
|
||||
o = t.expect_getend("Incog data for ID %s found at offset " % i_id)
|
||||
os.unlink(f1)
|
||||
cmp_or_die(hincog_offset,int(o))
|
||||
|
||||
def walletconv_out(self,name,desc,out_fmt="w",uopts=[],uopts_chk=[],pw=False):
|
||||
opts = ["-d",cfg['tmpdir'],"-r10","-p1","-o",out_fmt] + uopts
|
||||
infile = os.path.join(ref_dir,cfg['seed_id']+".mmwords")
|
||||
d = "(convert)"
|
||||
t = MMGenExpect(name,"mmgen-walletconv",opts+[infile],extra_desc=d)
|
||||
t = MMGenExpect(name,"mmgen-walletconv",opts+[infile],extra_desc="(convert)")
|
||||
t.license()
|
||||
if pw:
|
||||
t.passphrase_new("new "+desc,cfg['wpasswd'])
|
||||
t.usr_rand(10)
|
||||
if " ".join(desc.split()[-2:]) == "incognito data":
|
||||
for i in (1,2,3):
|
||||
t.expect("Generating encryption key from OS random data ")
|
||||
if desc == "hidden incognito data":
|
||||
ret = t.expect(["Create? (Y/n): ","'YES' to confirm: "],"YES\n")
|
||||
ret = t.expect(["Create? (Y/n): ","'YES' to confirm: "])
|
||||
if ret == 0:
|
||||
t.expect("Enter file size: ","1234\n")
|
||||
wf = t.written_to_file(desc[0].upper()+desc[1:],oo=True)
|
||||
t.send("\n")
|
||||
t.expect("Enter file size: ",str(hincog_bytes)+"\n")
|
||||
else:
|
||||
t.send("YES\n")
|
||||
if out_fmt == "w": t.label()
|
||||
wf = t.written_to_file(capfirst(desc),oo=True)
|
||||
ok()
|
||||
|
||||
d = "(check)"
|
||||
if desc == "hidden incognito data":
|
||||
self.keygen_chksum_chk_hincog(name,cfg['seed_id'],uopts_chk)
|
||||
# elif pw:
|
||||
# self.walletchk_chksum_chk(name,wf,cfg['seed_id'],uopts=uopts_chk)
|
||||
else:
|
||||
self.keygen_chksum_chk(name,wf,cfg['seed_id'],pw=pw)
|
||||
|
||||
|
|
@ -1389,7 +1418,7 @@ class MMGenTestSuite(object):
|
|||
t = MMGenExpect(name,"mmgen-keygen", ["-p1","-q","-S","-A"]+hincog_parm+["1"],extra_desc="(check)")
|
||||
t.passphrase("",cfg['wpasswd'])
|
||||
t.expect("Encrypt key list? (y/N): ","\n")
|
||||
t.expect("any printable ASCII symbol.\r\n")
|
||||
t.expect("ignored by MMGen.\r\n")
|
||||
chk = t.readline()[:8]
|
||||
vmsg("Seed ID: %s" % cyan(chk))
|
||||
cmp_or_die(seed_id,chk)
|
||||
|
|
@ -1400,7 +1429,7 @@ class MMGenTestSuite(object):
|
|||
if pw:
|
||||
t.passphrase("",cfg['wpasswd'])
|
||||
t.expect("Encrypt key list? (y/N): ","\n")
|
||||
t.expect("any printable ASCII symbol.\r\n")
|
||||
t.expect("ignored by MMGen.\r\n")
|
||||
chk = t.readline()[:8]
|
||||
vmsg("Seed ID: %s" % cyan(chk))
|
||||
cmp_or_die(seed_id,chk)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue