mmgen-wallet/mmgen-addrgen
philemon a3c4bd8731 New feature: export wallet to incognito format
Incognito wallet is 48, 56 or 64 bytes of apparently random data.

  Allows user to hide wallet data in a pre-existing file or on a disk
  partition (preferably filled in advance with random data).

  Can be used to hide wallet securely in unencrypted cloud storage or
  on paper, without revealing the nature of the data.

  Data may be written at a user-specified offset into the file or
  partition, in which case user must remember the offset.

  Each export operation uses a new random init vector to create different
  data each time.  This allows the user to hide wallets at different
  locations on the Net without detection.

  User must remember hash preset in addition to passphrase (though trial
  and error can be used if it's forgotten).

  Fully integrated with address generation and tx signing operations.
2014-07-14 18:31:00 +04:00

183 lines
6.4 KiB
Python
Executable file

#!/usr/bin/env python
#
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
# Copyright (C) 2013 by 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-addrgen: Generate a list or range of addresses from a mmgen
deterministic wallet.
Call as 'btc-keygen' to allow key generation as well.
"""
import sys
import mmgen.config as g
from mmgen.Opts import *
from mmgen.license import *
from mmgen.util import *
from mmgen.addr import *
invoked_as = sys.argv[0].split("-")[-1]
gen_what = "addresses" if invoked_as == "addrgen" else "keys"
if invoked_as == "keygen":
extra_help_data = (
"\n-A, --no-addresses Print only secret keys, no addresses",
"\n-x, --b16 Print secret keys in hexadecimal too",
"\nBy default, both addresses and secret keys are generated."
)
else: extra_help_data = ("","","")
help_data = {
'prog_name': sys.argv[0].split("/")[-1],
'desc': """Generate a list or range of {} from a {} wallet,
mnemonic, seed or password""".format(gen_what,g.proj_name),
'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: {}
(default: {})
-p, --hash-preset p Use scrypt.hash() parameters from preset 'p'
when hashing password (default: '{}')
-P, --passwd-file f Get passphrase from file 'f'
-q, --quiet Suppress warnings; overwrite files without
prompting
-S, --stdout Print {W} to stdout
-v, --verbose Produce more verbose output{}
-b, --from-brain l,p Generate {W} from a user-created password,
i.e. a "brainwallet", using seed length 'l' and
hash preset 'p' (comma-separated)
-g, --from-incognito Generate {W} from an incognito-format wallet
-G, --hidden-incog-data f,o,l Generate {W} from incognito data in file
'f' at offset 'o', with seed length of 'l'
-m, --from-mnemonic Generate {W} from an electrum-like mnemonic
-s, --from-seed Generate {W} from a seed in .{S} format
Addresses are given in a comma-separated list. Hyphen-separated
ranges are also allowed.{}
If available, the external 'keyconv' program will be used for address
generation.
Data for the --from-<what> options will be taken from <infile> if <infile>
is specified. Otherwise, the user will be prompted to enter the data.
For passphrases all combinations of whitespace are equal, and leading and
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.
BRAINWALLET NOTE:
As brainwallets require especially strong hashing to thwart dictionary
attacks, the brainwallet hash preset must be specified by the user, using
the 'p' parameter of the '--from-brain' option
The '--from-brain' option also requires the user to specify a seed length
(the 'l' parameter)
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(
extra_help_data[0],
", ".join([str(i) for i in g.seed_lens]),
g.seed_len,
g.hash_preset,
extra_help_data[1],
extra_help_data[2],
W=gen_what,
S=g.seed_ext
)
}
short_opts = ["h","A","d:","e",
"H","K","l:","p:",
"P:","q","S","v","x","b:",
"g","G:","m","s"]
long_opts = ["help","no_addresses","outdir=","echo_passphrase",
"show_hash_presets","no_keyconv","seed_len=","hash_preset=",
"passwd_file=","quiet","stdout","verbose","b16","from_brain=",
"from_incognito","hidden_incog_data=","from_mnemonic","from_seed"]
if invoked_as == "addrgen":
for i in "A","x": short_opts.remove(i)
for i in "no_addresses","b16": long_opts.remove(i)
opts,cmd_args = process_opts(sys.argv,help_data,"".join(short_opts),long_opts)
if 'show_hash_presets' in opts: show_hash_presets()
if 'quiet' in opts: g.quiet = True
if 'verbose' in opts: g.verbose = True
if 'hidden_incog_data' in opts: opts['from_incognito'] = True
opts['gen_what'] = gen_what
check_opts(opts,long_opts)
if g.debug: show_opts_and_cmd_args(opts,cmd_args)
if len(cmd_args) == 1 and (
'from_mnemonic' in opts
or 'from_brain' in opts
or 'from_seed' in opts
or 'from_incognito' in opts
):
infile,addr_list_arg = "",cmd_args[0]
elif len(cmd_args) == 2:
infile,addr_list_arg = cmd_args
check_infile(infile)
else: usage(help_data)
addr_list = parse_address_list(addr_list_arg)
if not addr_list: sys.exit(2)
do_license_msg()
# Interact with user:
if invoked_as == "keygen" and not g.quiet:
confirm_or_exit(cmessages['unencrypted_secret_keys'], 'continue')
# Generate data:
if invoked_as == "addrgen":
opts['print_addresses_only'] = True
else:
if not 'no_addresses' in opts: opts['print_secret'] = True
seed = get_seed_retry(infile,opts)
seed_id = make_chksum_8(seed)
addr_data = generate_addrs(seed, addr_list, opts)
addr_data_str = format_addr_data(addr_data, seed_id, opts)
# Output data:
if 'stdout' in opts:
if invoked_as == "keygen" and not g.quiet:
confirm = True
else: confirm = False
write_to_stdout(addr_data_str,"secret keys",confirm)
elif not sys.stdout.isatty():
write_to_stdout(addr_data_str,"secret keys",confirm=False)
else:
write_addr_data_to_file(seed, addr_data_str, addr_list, opts)