mmgen-walletgen 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. #!/usr/bin/env python
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C) 2013 by 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 a mmgen deterministic wallet
  20. """
  21. import sys, os
  22. from hashlib import sha256
  23. from mmgen.Opts import *
  24. from mmgen.license import *
  25. from mmgen.config import *
  26. from mmgen.walletgen import *
  27. from mmgen.utils import *
  28. prog_name = sys.argv[0].split("/")[-1]
  29. help_data = {
  30. 'prog_name': prog_name,
  31. 'desc': "Generate a {} deterministic wallet".format(proj_name),
  32. 'usage': "[opts] [infile]",
  33. 'options': """
  34. -h, --help Print this help message
  35. -d, --outdir d Specify an alternate directory 'd' for output
  36. -e, --echo-passphrase Print passphrase to screen when typing it
  37. -l, --seed-len n Create seed of length 'n'. Options: {}
  38. (default: {})
  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: '{}')
  43. -P, --show-hash-presets Show information on available hash presets
  44. -q, --quiet Suppress warnings; overwrite files without asking
  45. -u, --usr-randlen n Get 'n' characters of randomness from the user
  46. (default: {})
  47. -b, --from-brain l,p Generate wallet from a user-created passphrase,
  48. i.e. a "brainwallet", using seed length 'l' and
  49. hash preset 'p' (comma-separated)
  50. -m, --from-mnemonic Generate wallet from an Electrum-like mnemonic
  51. -s, --from-seed Generate wallet from a seed in .{S} format
  52. By default (i.e. when invoked without any of the '--from-<what>' options),
  53. {} generates a wallet based on a random seed.
  54. Data for the --from-<what> options will be taken from <infile> if <infile>
  55. is specified. Otherwise, the user will be prompted to enter the data.
  56. For passphrases all combinations of whitespace are equal, and leading and
  57. trailing space are ignored. This permits reading passphrase data from a
  58. multi-line file with free spacing and indentation. This is particularly
  59. convenient for long brainwallet passphrases, for example.
  60. BRAINWALLET NOTE:
  61. As brainwallets require especially strong hashing to thwart dictionary
  62. attacks, the brainwallet hash preset must be specified by the user, using
  63. the 'p' parameter of the '--from-brain' option. This preset should be
  64. stronger than the one used for hashing the seed (i.e. the default value or
  65. the one specified in the '--hash-preset' option).
  66. The '--from-brain' option also requires the user to specify a seed length
  67. (the 'l' parameter), which overrides both the default and any one given in
  68. the '--seed-len' option.
  69. For a brainwallet passphrase to always generate the same keys and addresses,
  70. the same 'l' and 'p' parameters to '--from-brain' must be used in all future
  71. invocations with that passphrase.
  72. """.format(
  73. ", ".join([str(i) for i in seed_lens]),
  74. seed_len,
  75. hash_preset,
  76. usr_randlen,
  77. prog_name,
  78. S=seed_ext,
  79. )
  80. }
  81. short_opts = "hd:el:L:p:Pqu:b:ms"
  82. long_opts = "help","outdir=","echo_passphrase","seed_len=","label=",\
  83. "hash_preset=","show_hash_presets","quiet","usr_randlen=",\
  84. "from_brain=","from_mnemonic","from_seed"
  85. opts,cmd_args = process_opts(sys.argv,help_data,short_opts,long_opts)
  86. if 'show_hash_presets' in opts: show_hash_presets()
  87. # Argument sanity checks and processing:
  88. set_if_unset_and_typeconvert(opts,(
  89. ('usr_randlen',usr_randlen,'int'),
  90. ('hash_preset',hash_preset,'str'),
  91. ('seed_len',seed_len,'int')
  92. ))
  93. # Exits on invalid input
  94. check_opts(opts,
  95. ('usr_randlen','hash_preset','seed_len','outdir','label','from_brain')
  96. )
  97. if debug:
  98. print "Processed options: %s" % repr(opts)
  99. print "Cmd args: %s" % repr(cmd_args)
  100. if len(cmd_args) == 1 and (
  101. 'from_brain' in opts or
  102. 'from_mnemonic' in opts or
  103. 'from_seed' in opts):
  104. infile = cmd_args[0]
  105. check_infile(infile)
  106. elif len(cmd_args) == 0: infile = ""
  107. else: usage(help_data)
  108. # Begin execution
  109. if not 'quiet' in opts: do_license_msg()
  110. msg_r("Acquiring random data from your computer...")
  111. try:
  112. from Crypto import Random
  113. r = Random.new()
  114. os_rand_data = (r.read(256),r.read(128))
  115. except:
  116. msg("FAILED\nUnable to generate random numbers. Exiting")
  117. sys.exit(2)
  118. msg("OK")
  119. if debug: display_os_random_data(os_rand_data)
  120. usr_keys,key_timings = get_random_data_from_user(opts)
  121. msg("")
  122. if debug: display_user_random_data(usr_keys,key_timings)
  123. usr_rand_data = sha256(usr_keys).digest() + \
  124. sha256("".join(key_timings)).digest()
  125. if 'from_mnemonic' in opts or 'from_brain' in opts or 'from_seed' in opts:
  126. silent = False
  127. while True:
  128. seed = get_seed(infile,opts,silent=silent)
  129. silent = True
  130. if seed: break
  131. else:
  132. # Truncate random data for smaller seed lengths
  133. seed = os_rand_data[0] + usr_rand_data
  134. seed = sha256(seed).digest()[:opts['seed_len']/8]
  135. salt = os_rand_data[1] + usr_rand_data
  136. salt = sha256(salt).digest()[:salt_len]
  137. passwd = get_first_passphrase_from_user(
  138. "{} wallet passphrase".format(proj_name), opts)
  139. key = make_key(passwd, salt, opts['hash_preset'])
  140. enc_seed = encrypt_seed(seed, key, opts)
  141. write_wallet_to_file(seed,passwd,make_chksum_8(key),salt,enc_seed,opts)