mmgen-walletgen 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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. Note that passphrase data may be input in free-form fashion, using any
  57. combination of spaces or tabs (or newlines, in a file) between words.
  58. BRAINWALLET NOTE:
  59. As brainwallets require especially strong hashing to thwart dictionary
  60. attacks, the brainwallet hash preset must be specified by the user, using
  61. the 'p' parameter of the '--from-brain' option. This preset should be
  62. stronger than the one used for hashing the seed (i.e. the default value or
  63. the one specified in the '--hash-preset' option).
  64. The '--from-brain' option also requires the user to specify a seed length
  65. (the 'l' parameter), which overrides both the default and any one given in
  66. the '--seed-len' option.
  67. For a brainwallet passphrase to always generate the same keys and addresses,
  68. the same 'l' and 'p' parameters to '--from-brain' must be used in all future
  69. invocations with that passphrase.
  70. """.format(
  71. ", ".join([str(i) for i in seed_lens]),
  72. seed_len,
  73. hash_preset,
  74. usr_randlen,
  75. prog_name,
  76. S=seed_ext,
  77. )
  78. }
  79. short_opts = "hd:el:L:p:Pqu:b:ms"
  80. long_opts = "help","outdir=","echo_passphrase","seed_len=","label=",\
  81. "hash_preset=","show_hash_presets","quiet","usr_randlen=",\
  82. "from_brain=","from_mnemonic","from_seed"
  83. opts,cmd_args = process_opts(sys.argv,help_data,short_opts,long_opts)
  84. if 'show_hash_presets' in opts: show_hash_presets()
  85. # Argument sanity checks and processing:
  86. set_if_unset_and_typeconvert(opts,(
  87. ('usr_randlen',usr_randlen,'int'),
  88. ('hash_preset',hash_preset,'str'),
  89. ('seed_len',seed_len,'int')
  90. ))
  91. # Exits on invalid input
  92. check_opts(opts,
  93. ('usr_randlen','hash_preset','seed_len','outdir','label','from_brain')
  94. )
  95. if debug:
  96. print "Processed options: %s" % repr(opts)
  97. print "Cmd args: %s" % repr(cmd_args)
  98. if len(cmd_args) == 1 and (
  99. 'from_brain' in opts or
  100. 'from_mnemonic' in opts or
  101. 'from_seed' in opts):
  102. infile = cmd_args[0]
  103. check_infile(infile)
  104. elif len(cmd_args) == 0: infile = ""
  105. else: usage(help_data)
  106. # Begin execution
  107. if not 'quiet' in opts: do_license_msg()
  108. msg_r("Acquiring random data from your computer...")
  109. try:
  110. from Crypto import Random
  111. r = Random.new()
  112. os_rand_data = (r.read(256),r.read(128))
  113. except:
  114. msg("FAILED\nUnable to generate random numbers. Exiting")
  115. sys.exit(2)
  116. msg("OK")
  117. if debug: display_os_random_data(os_rand_data)
  118. usr_keys,key_timings = get_random_data_from_user(opts)
  119. msg("")
  120. if debug: display_user_random_data(usr_keys,key_timings)
  121. usr_rand_data = sha256(usr_keys).digest() + \
  122. sha256("".join(key_timings)).digest()
  123. s = get_seed(infile,opts,no_wallet=True)
  124. if s: seed = s
  125. else:
  126. # Truncate random data for smaller seed lengths
  127. seed = os_rand_data[0] + usr_rand_data
  128. seed = sha256(seed).digest()[:opts['seed_len']/8]
  129. salt = os_rand_data[1] + usr_rand_data
  130. salt = sha256(salt).digest()[:salt_len]
  131. passwd = get_first_passphrase_from_user(
  132. "{} wallet passphrase".format(proj_name), opts)
  133. key = make_key(passwd, salt, opts['hash_preset'])
  134. enc_seed = encrypt_seed(seed, key, opts)
  135. write_wallet_to_file(seed,passwd,make_chksum_8(key),salt,enc_seed,opts)