main_walletgen.py 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. #!/usr/bin/env python
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. """
  19. mmgen-walletgen: Generate an MMGen deterministic wallet
  20. """
  21. import sys, os
  22. from hashlib import sha256
  23. import mmgen.config as g
  24. from mmgen.Opts import *
  25. from mmgen.license import *
  26. from mmgen.util import *
  27. from mmgen.crypto import *
  28. help_data = {
  29. 'prog_name': g.prog_name,
  30. 'desc': "Generate an {} deterministic wallet".format(g.proj_name),
  31. 'usage': "[opts] [infile]",
  32. 'options': """
  33. -h, --help Print this help message
  34. -d, --outdir= d Specify an alternate directory 'd' for output
  35. -e, --echo-passphrase Print passphrase to screen when typing it
  36. -H, --show-hash-presets Show information on available hash presets
  37. -l, --seed-len= n Create seed of length 'n'. Options: {seed_lens}
  38. (default: {g.seed_len})
  39. -L, --label= l Label to identify this wallet (32 chars max.
  40. Allowed symbols: A-Z, a-z, 0-9, " ", "_", ".")
  41. -p, --hash-preset= p Use scrypt.hash() parameters from preset 'p'
  42. (default: '{g.hash_preset}')
  43. -P, --passwd-file= f Get MMGen wallet passphrase from file 'f'
  44. -q, --quiet Produce quieter output; overwrite files without
  45. prompting
  46. -r, --usr-randchars= n Get 'n' characters of additional randomness from
  47. user (min={g.min_urandchars}, max={g.max_urandchars})
  48. -v, --verbose Produce more verbose output
  49. -b, --from-brain= l,p Generate wallet from a user-created passphrase,
  50. i.e. a "brainwallet", using seed length 'l' and
  51. hash preset 'p' (comma-separated)
  52. -g, --from-incog Generate wallet from an incognito-format wallet
  53. -G, --from-incog-hidden= f,o,l Generate keys from incognito data in file
  54. 'f' at offset 'o', with seed length of 'l'
  55. -o, --old-incog-fmt Use old (pre-0.7.8) incog format
  56. -m, --from-mnemonic Generate wallet from an Electrum-like mnemonic
  57. -s, --from-seed Generate wallet from a seed in .{g.seed_ext} format
  58. """.format(seed_lens=",".join([str(i) for i in g.seed_lens]), g=g),
  59. 'notes': """
  60. By default (i.e. when invoked without any of the '--from-<what>' options),
  61. {g.prog_name} generates a wallet based on a random seed.
  62. Data for the --from-<what> options will be taken from <infile> if <infile>
  63. is specified. Otherwise, the user will be prompted to enter the data.
  64. For passphrases all combinations of whitespace are equal, and leading and
  65. trailing space are ignored. This permits reading passphrase data from a
  66. multi-line file with free spacing and indentation. This is particularly
  67. convenient for long brainwallet passphrases, for example.
  68. Since good randomness is particularly important when generating wallets,
  69. the '--usr-randchars' option is turned on by default to gather additional
  70. entropy from the user. If you fully trust your OS's random number gener-
  71. ator and wish to disable this option, specify '-r0' on the command line.
  72. BRAINWALLET NOTE:
  73. As brainwallets require especially strong hashing to thwart dictionary
  74. attacks, the brainwallet hash preset must be specified by the user, using
  75. the 'p' parameter of the '--from-brain' option. This preset should be
  76. stronger than the one used for hashing the seed (i.e. the default value or
  77. the one specified in the '--hash-preset' option).
  78. The '--from-brain' option also requires the user to specify a seed length
  79. (the 'l' parameter), which overrides both the default and any one given in
  80. the '--seed-len' option.
  81. For a brainwallet passphrase to always generate the same keys and
  82. addresses, the same 'l' and 'p' parameters to '--from-brain' must be used
  83. in all future invocations with that passphrase.
  84. """.format(g=g)
  85. }
  86. wmsg = {
  87. 'choose_wallet_passphrase': """
  88. You must choose a passphrase to encrypt the wallet with. A key will be
  89. generated from your passphrase using a hash preset of '%s'. Please note that
  90. no strength checking of passphrases is performed. For an empty passphrase,
  91. just hit ENTER twice.
  92. """.strip(),
  93. 'brain_warning': """
  94. ############################## EXPERTS ONLY! ##############################
  95. A brainwallet will be secure only if you really know what you're doing and
  96. have put much care into its creation. {} assumes no responsibility for
  97. coins stolen as a result of a poorly crafted brainwallet passphrase.
  98. A key will be generated from your passphrase using the parameters requested
  99. by you: seed length {}, hash preset '{}'. For brainwallets it's highly
  100. recommended to use one of the higher-numbered presets
  101. Remember the seed length and hash preset parameters you've specified. To
  102. generate the correct keys/addresses associated with this passphrase in the
  103. future, you must continue using these same parameters
  104. """,
  105. }
  106. opts,cmd_args = parse_opts(sys.argv,help_data)
  107. if 'show_hash_presets' in opts: show_hash_presets()
  108. if opts['usr_randchars'] == -1: opts['usr_randchars'] = g.usr_randchars_dfl
  109. if g.debug: show_opts_and_cmd_args(opts,cmd_args)
  110. if len(cmd_args) == 1:
  111. infile = cmd_args[0]
  112. check_infile(infile)
  113. ext = infile.split(".")[-1]
  114. ok_exts = g.seedfile_exts
  115. for e in ok_exts:
  116. if e == ext: break
  117. else:
  118. msg(
  119. "Input file must have one of the following extensions: .%s" % ", .".join(ok_exts))
  120. sys.exit(1)
  121. elif len(cmd_args) == 0:
  122. infile = ""
  123. else: usage(help_data)
  124. # Begin execution
  125. do_license_msg()
  126. if 'from_brain' in opts and not g.quiet:
  127. confirm_or_exit(wmsg['brain_warning'].format(
  128. g.proj_name, *get_from_brain_opt_params(opts)),
  129. "continue")
  130. for i in 'from_mnemonic','from_brain','from_seed','from_incog':
  131. if infile or (i in opts):
  132. seed = get_seed_retry(infile,opts)
  133. # if "from_incog" in opts or get_extension(infile) == g.incog_ext:
  134. # qmsg(cmessages['incog'] % make_chksum_8(seed))
  135. # else: qmsg("")
  136. qmsg("")
  137. break
  138. else:
  139. # Truncate random data for smaller seed lengths
  140. seed = sha256(get_random(128,opts)).digest()[:opts['seed_len']/8]
  141. salt = sha256(get_random(128,opts)).digest()[:g.salt_len]
  142. qmsg(wmsg['choose_wallet_passphrase'] % opts['hash_preset'])
  143. passwd = get_new_passphrase("new {} wallet".format(g.proj_name), opts)
  144. key = make_key(passwd, salt, opts['hash_preset'])
  145. enc_seed = encrypt_seed(seed, key)
  146. write_wallet_to_file(seed,passwd,make_chksum_8(key),salt,enc_seed,opts)