123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- #!/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-walletgen: Generate a mmgen deterministic wallet
- """
- import sys, os
- from hashlib import sha256
- from mmgen.Opts import *
- from mmgen.license import *
- import mmgen.config as g
- from mmgen.walletgen import *
- from mmgen.util import *
- prog_name = sys.argv[0].split("/")[-1]
- help_data = {
- 'prog_name': prog_name,
- 'desc': "Generate a {} deterministic wallet".format(g.proj_name),
- 'usage': "[opts] [infile]",
- '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
- -H, --show-hash-presets Show information on available hash presets
- -l, --seed-len n Create seed of length 'n'. Options: {}
- (default: {})
- -L, --label l Label to identify this wallet (32 chars max.
- Allowed symbols: A-Z, a-z, 0-9, " ", "_", ".")
- -p, --hash-preset p Use scrypt.hash() parameters from preset 'p'
- (default: '{}')
- -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: {})
- -v, --verbose Produce more verbose output
- -b, --from-brain l,p Generate wallet from a user-created passphrase,
- i.e. a "brainwallet", using seed length 'l' and
- hash preset 'p' (comma-separated)
- -g, --from-incognito Generate wallet from an incognito-format wallet
- -m, --from-mnemonic Generate wallet from an Electrum-like mnemonic
- -s, --from-seed Generate wallet from a seed in .{S} format
- By default (i.e. when invoked without any of the '--from-<what>' options),
- {} generates a wallet based on a random seed.
- 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. This preset should be
- stronger than the one used for hashing the seed (i.e. the default value or
- the one specified in the '--hash-preset' option).
- The '--from-brain' option also requires the user to specify a seed length
- (the 'l' parameter), which overrides both the default and any one given in
- the '--seed-len' option.
- 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(
- ",".join([str(i) for i in g.seed_lens]),
- g.seed_len,
- g.hash_preset,
- g.usr_randlen,
- prog_name,
- S=g.seed_ext,
- )
- }
- short_opts = "hd:eHl:L:p:P:qu:vb:gms"
- long_opts = "help","outdir=","echo_passphrase","show_hash_presets","seed_len=",\
- "label=","hash_preset=","passwd_file=","quiet","usr_randlen=","verbose",\
- "from_brain=","from_incognito","from_mnemonic","from_seed"
- opts,cmd_args = process_opts(sys.argv,help_data,short_opts,long_opts)
- if 'quiet' in opts: g.quiet = True
- if 'verbose' in opts: g.verbose = True
- if 'show_hash_presets' in opts: show_hash_presets()
- check_opts(opts,long_opts)
- if g.debug: show_opts_and_cmd_args(opts,cmd_args)
- if len(cmd_args) == 1:
- infile = cmd_args[0]
- check_infile(infile)
- ext = infile.split(".")[-1]
- ok_exts = g.seedfile_exts
- for e in ok_exts:
- if e == ext: break
- else:
- msg(
- "Input file must have one of the following extensions: .%s" % ", .".join(ok_exts))
- sys.exit(1)
- elif len(cmd_args) == 0:
- infile = ""
- else: usage(help_data)
- # Begin execution
- 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()
- for i in 'from_mnemonic','from_brain','from_seed','from_incognito':
- if infile or (i in opts):
- seed = get_seed_retry(infile,opts)
- if "from_incognito" in opts or get_extension(infile) == g.incog_ext:
- qmsg(cmessages['incognito'] % make_chksum_8(seed))
- else: qmsg("")
- 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]
- salt = os_rand_data[1] + usr_rand_data
- salt = sha256(salt).digest()[:g.salt_len]
- qmsg("""Now you must choose a passphrase to encrypt the seed 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.
- """ % opts['hash_preset'])
- passwd = get_new_passphrase("{} wallet passphrase".format(g.proj_name), opts)
- key = make_key(passwd, salt, opts['hash_preset'])
- enc_seed = encrypt_seed(seed, key)
- write_wallet_to_file(seed,passwd,make_chksum_8(key),salt,enc_seed,opts)
|