main_addrgen.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. #!/usr/bin/env python
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2014 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-addrgen: Generate a series or range of addresses from an MMGen
  20. deterministic wallet
  21. """
  22. import sys
  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. from mmgen.addr import *
  29. from mmgen.tx import make_addr_data_chksum
  30. what = "keys" if sys.argv[0].split("-")[-1] == "keygen" else "addresses"
  31. help_data = {
  32. 'prog_name': g.prog_name,
  33. 'desc': """Generate a range or list of {} from an {g.proj_name} wallet,
  34. mnemonic, seed or password""".format(what,g=g),
  35. 'usage':"[opts] [infile] <address range or list>",
  36. 'options': """
  37. -h, --help Print this help message{}
  38. -d, --outdir= d Specify an alternate directory 'd' for output
  39. -c, --save-checksum Save address list checksum to file
  40. -e, --echo-passphrase Echo passphrase or mnemonic to screen upon entry
  41. -H, --show-hash-presets Show information on available hash presets
  42. -K, --no-keyconv Force use of internal libraries for address gener-
  43. ation, even if 'keyconv' is available
  44. -l, --seed-len= N Length of seed. Options: {seed_lens}
  45. (default: {g.seed_len})
  46. -p, --hash-preset= p Use scrypt.hash() parameters from preset 'p' when
  47. hashing password (default: '{g.hash_preset}')
  48. -P, --passwd-file= f Get MMGen wallet passphrase from file 'f'
  49. -q, --quiet Suppress warnings; overwrite files without
  50. prompting
  51. -S, --stdout Print {what} to stdout
  52. -v, --verbose Produce more verbose output{}
  53. -b, --from-brain= l,p Generate {what} from a user-created password,
  54. i.e. a "brainwallet", using seed length 'l' and
  55. hash preset 'p' (comma-separated)
  56. -g, --from-incog Generate {what} from an incognito wallet
  57. -X, --from-incog-hex Generate {what} from incognito hexadecimal wallet
  58. -G, --from-incog-hidden=f,o,l Generate {what} from incognito data in file
  59. 'f' at offset 'o', with seed length of 'l'
  60. -o, --old-incog-fmt Use old (pre-0.7.8) incog format
  61. -m, --from-mnemonic Generate {what} from an electrum-like mnemonic
  62. -s, --from-seed Generate {what} from a seed in .{g.seed_ext} format
  63. """.format(
  64. *(
  65. (
  66. "\n-A, --no-addresses Print only secret keys, no addresses",
  67. "\n-x, --b16 Print secret keys in hexadecimal too"
  68. )
  69. if what == "keys" else ("","")),
  70. seed_lens=", ".join([str(i) for i in g.seed_lens]),
  71. what=what, g=g
  72. ),
  73. 'notes': """
  74. Addresses are given in a comma-separated list. Hyphen-separated ranges are
  75. also allowed.{}
  76. If available, the external 'keyconv' program will be used for address
  77. generation.
  78. Data for the --from-<what> options will be taken from <infile> if <infile>
  79. is specified. Otherwise, the user will be prompted to enter the data.
  80. For passphrases all combinations of whitespace are equal, and leading and
  81. trailing space are ignored. This permits reading passphrase data from a
  82. multi-line file with free spacing and indentation. This is particularly
  83. convenient for long brainwallet passphrases, for example.
  84. BRAINWALLET NOTE:
  85. As brainwallets require especially strong hashing to thwart dictionary
  86. attacks, the brainwallet hash preset must be specified by the user, using
  87. the 'p' parameter of the '--from-brain' option
  88. The '--from-brain' option also requires the user to specify a seed length
  89. (the 'l' parameter)
  90. For a brainwallet passphrase to always generate the same keys and addresses,
  91. the same 'l' and 'p' parameters to '--from-brain' must be used in all future
  92. invocations with that passphrase
  93. """.format("\n\nBy default, both addresses and secret keys are generated."
  94. if what == "keys" else "")
  95. }
  96. wmsg = {
  97. 'unencrypted_secret_keys': """
  98. This program generates secret keys from your {} seed, outputting them in
  99. UNENCRYPTED form. Generate only the key(s) you need and guard them carefully.
  100. """.format(g.proj_name),
  101. }
  102. opts,cmd_args = parse_opts(sys.argv,help_data)
  103. if 'show_hash_presets' in opts: show_hash_presets()
  104. if 'from_incog_hex' in opts or 'from_incog_hidden' in opts:
  105. opts['from_incog'] = True
  106. if g.debug: show_opts_and_cmd_args(opts,cmd_args)
  107. if len(cmd_args) == 1 and (
  108. 'from_mnemonic' in opts
  109. or 'from_brain' in opts
  110. or 'from_seed' in opts
  111. or 'from_incog_hidden' in opts
  112. ):
  113. infile,addr_idx_arg = "",cmd_args[0]
  114. elif len(cmd_args) == 2:
  115. infile,addr_idx_arg = cmd_args
  116. check_infile(infile)
  117. else: usage(help_data)
  118. addr_idxs = parse_addr_idxs(addr_idx_arg)
  119. if not addr_idxs: sys.exit(2)
  120. do_license_msg()
  121. # Interact with user:
  122. if what == "keys" and not g.quiet:
  123. confirm_or_exit(wmsg['unencrypted_secret_keys'], 'continue')
  124. # Generate data:
  125. seed = get_seed_retry(infile,opts)
  126. seed_id = make_chksum_8(seed)
  127. opts['gen_what'] = "a" if what == "addresses" else (
  128. "k" if 'no_addresses' in opts else "ka")
  129. addr_data = generate_addrs(seed, addr_idxs, opts)
  130. if 'a' in opts['gen_what']:
  131. if 'k' in opts['gen_what']:
  132. def l(a): return ( a.num, (a.addr,"",a.wif) )
  133. keys = True
  134. else:
  135. def l(a): return ( a.num, (a.addr,) )
  136. keys = False
  137. addr_data_chksum = make_addr_data_chksum([l(a) for a in addr_data],keys)
  138. else:
  139. addr_data_chksum = ""
  140. addr_data_str = format_addr_data(
  141. addr_data, addr_data_chksum, seed_id, addr_idxs, opts)
  142. outfile_base = "{}[{}]".format(seed_id, fmt_addr_idxs(addr_idxs))
  143. if 'a' in opts['gen_what']:
  144. w = "key-address" if 'k' in opts['gen_what'] else "address"
  145. qmsg("Checksum for %s data %s: %s" % (w,outfile_base,addr_data_chksum))
  146. if 'save_checksum' in opts:
  147. write_to_file(outfile_base+"."+g.addrfile_chksum_ext,
  148. addr_data_chksum+"\n",opts,"%s data checksum" % w,True,True,False)
  149. else:
  150. qmsg("This checksum will be used to verify the %s file in the future."%w)
  151. qmsg("Record it to a safe location.")
  152. if 'k' in opts['gen_what'] and keypress_confirm("Encrypt key list?"):
  153. addr_data_str = mmgen_encrypt(addr_data_str,"key list","",opts)
  154. enc_ext = "." + g.mmenc_ext
  155. else: enc_ext = ""
  156. # Output data:
  157. if 'stdout' in opts or not sys.stdout.isatty():
  158. if enc_ext and sys.stdout.isatty():
  159. msg("Cannot write encrypted data to screen. Exiting")
  160. sys.exit(2)
  161. write_to_stdout(addr_data_str,what,
  162. (what=="keys"and not g.quiet and sys.stdout.isatty()))
  163. else:
  164. outfile = "%s.%s%s" % (outfile_base, (
  165. g.keyaddrfile_ext if "ka" in opts['gen_what'] else (
  166. g.keyfile_ext if "k" in opts['gen_what'] else
  167. g.addrfile_ext)), enc_ext)
  168. write_to_file(outfile,addr_data_str,opts,what,not g.quiet,True)