utils.py 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739
  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. utils.py: Shared routines for the mmgen suite
  20. """
  21. import sys
  22. from mmgen.config import *
  23. from binascii import hexlify,unhexlify
  24. def msg(s): sys.stderr.write(s + "\n")
  25. def msg_r(s): sys.stderr.write(s)
  26. def bail(): sys.exit(9)
  27. def _my_getpass(prompt):
  28. from getpass import getpass
  29. # getpass prompts to stderr, so no trickery required as with raw_input()
  30. try: pw = getpass(prompt)
  31. except:
  32. msg("\nInterrupted by user")
  33. sys.exit(1)
  34. return pw
  35. def _my_raw_input(prompt):
  36. msg_r(prompt)
  37. try: pw = raw_input()
  38. except:
  39. msg("\nInterrupted by user")
  40. sys.exit(1)
  41. return pw
  42. def _get_hash_params(hash_preset):
  43. if hash_preset in hash_presets:
  44. return hash_presets[hash_preset] # N,p,r,buflen
  45. else:
  46. # Shouldn't be here
  47. msg("%s: invalid 'hash_preset' value" % hash_preset)
  48. sys.exit(3)
  49. def show_hash_presets():
  50. fs = " {:<7} {:<6} {:<3} {}"
  51. msg("Available parameters for scrypt.hash():")
  52. msg(fs.format("Preset","N","r","p"))
  53. for i in sorted(hash_presets.keys()):
  54. msg(fs.format("'%s'" % i, *hash_presets[i]))
  55. msg("N = memory usage (power of two), p = iterations (rounds)")
  56. sys.exit(0)
  57. def check_opts(opts,keys):
  58. for key in keys:
  59. if key not in opts: continue
  60. val = opts[key]
  61. what = "parameter for '--%s' option" % key.replace("_","-")
  62. if key == 'outdir':
  63. what = "output directory"
  64. import re, os, stat
  65. d = re.sub(r'/*$','', val)
  66. opts[key] = d
  67. try: mode = os.stat(d).st_mode
  68. except:
  69. msg("Unable to stat requested %s '%s'. Aborting" % (what,d))
  70. sys.exit(1)
  71. if not stat.S_ISDIR(mode):
  72. msg("Requested %s '%s' is not a directory. Aborting" %(what,d))
  73. sys.exit(1)
  74. if not os.access(d, os.W_OK|os.X_OK):
  75. msg("Requested %s '%s' is unwritable by you. Aborting"%(what,d))
  76. sys.exit(1)
  77. elif key == 'label':
  78. label = val.strip()
  79. opts[key] = label
  80. if len(label) > 32:
  81. msg("Label must be 32 characters or less")
  82. sys.exit(1)
  83. from string import ascii_letters, digits
  84. label_chrs = list(ascii_letters + digits) + [".", "_", " "]
  85. for ch in list(label):
  86. if ch not in label_chrs:
  87. msg("'%s': illegal character in label" % ch)
  88. sys.exit(1)
  89. elif key == 'from_brain':
  90. try:
  91. l,p = val.split(",")
  92. except:
  93. msg("'%s': invalid %s" % (val,what))
  94. sys.exit(1)
  95. try:
  96. int(l)
  97. except:
  98. msg("'%s': invalid 'l' %s (not an integer)" % (l,what))
  99. sys.exit(1)
  100. if int(l) not in seed_lens:
  101. msg("'%s': invalid 'l' %s. Options: %s" %
  102. (l, what, ", ".join([str(i) for i in seed_lens])))
  103. sys.exit(1)
  104. if p not in hash_presets:
  105. hps = ", ".join([i for i in sorted(hash_presets.keys())])
  106. msg("'%s': invalid 'p' %s. Options: %s" % (p, what, hps))
  107. sys.exit(1)
  108. elif key == 'seed_len':
  109. if val not in seed_lens:
  110. msg("'%s': invalid %s. Options: %s"
  111. % (val,what,", ".join([str(i) for i in seed_lens])))
  112. sys.exit(2)
  113. elif key == 'hash_preset':
  114. if val not in hash_presets:
  115. msg("'%s': invalid %s. Options: %s"
  116. % (val,what,", ".join(sorted(hash_presets.keys()))))
  117. sys.exit(2)
  118. elif key == 'usr_randlen':
  119. if val > max_randlen or val < min_randlen:
  120. msg("'%s': invalid %s (must be >= %s and <= %s)"
  121. % (val,what,min_randlen,max_randlen))
  122. sys.exit(2)
  123. cmessages = {
  124. 'null': "",
  125. 'unencrypted_secret_keys': """
  126. This program generates secret keys from your {} seed, outputting them in
  127. UNENCRYPTED form. Generate only the key(s) you need and guard them carefully.
  128. """.format(proj_name),
  129. 'brain_warning': """
  130. ############################## EXPERTS ONLY! ##############################
  131. A brainwallet will be secure only if you really know what you're doing and
  132. have put much care into its creation. {} assumes no responsibility for
  133. coins stolen as a result of a poorly crafted brainwallet passphrase.
  134. A key will be generated from your passphrase using the parameters requested
  135. by you: seed length {}, hash preset '{}'. For brainwallets it's highly
  136. recommended to use one of the higher-numbered presets
  137. Remember the seed length and hash preset parameters you've specified. To
  138. generate the correct keys/addresses associated with this passphrase in the
  139. future, you must continue using these same parameters
  140. """
  141. }
  142. def confirm_or_exit(message, question):
  143. if message.strip(): msg(message.strip())
  144. msg("")
  145. conf_msg = "Type uppercase 'YES' to confirm: "
  146. if question[0].isupper():
  147. prompt = question + " " + conf_msg
  148. else:
  149. prompt = "Are you sure you want to %s?\n%s" % (question,conf_msg)
  150. if _my_raw_input(prompt).strip() != "YES":
  151. msg("Program aborted by user")
  152. sys.exit(2)
  153. msg("")
  154. def set_if_unset_and_typeconvert(opts,item):
  155. # ('usr_randlen',usr_randlen,'int'),
  156. for opt,var,dtype in item:
  157. if dtype == 'int': f,s = int,"an integer"
  158. elif dtype == 'str': f,s = str,"a string"
  159. if opt in opts:
  160. val = opts[opt]
  161. what = "invalid parameter for '--%s' option" % opt.replace("_","-")
  162. try:
  163. f(val)
  164. except:
  165. msg("'%s': %s (not %s)" % (val,what,s))
  166. sys.exit(1)
  167. opts[opt] = f(val)
  168. else:
  169. opts[opt] = var
  170. def make_chksum_8(s):
  171. from hashlib import sha256
  172. return sha256(sha256(s).digest()).hexdigest()[:8].upper()
  173. def _make_chksum_6(s):
  174. from hashlib import sha256
  175. return sha256(s).hexdigest()[:6]
  176. def _get_from_brain_opt_params(opts):
  177. l,p = opts['from_brain'].split(",")
  178. return(int(l),p)
  179. def check_infile(f):
  180. import os, stat
  181. try: mode = os.stat(f).st_mode
  182. except:
  183. msg("Unable to stat requested input file '%s'. Aborting" % f)
  184. sys.exit(1)
  185. if not stat.S_ISREG(mode) or stat.S_ISLNK(mode):
  186. msg("Requested input file '%s' is not a file. Aborting" % f)
  187. sys.exit(1)
  188. if not os.access(f, os.R_OK):
  189. msg("Requested input file '%s' is unreadable by you. Aborting" % f)
  190. sys.exit(1)
  191. def parse_address_range(arg):
  192. import re
  193. m = re.match(r'^(\d+)(-(\d+))*$', arg)
  194. if m == None:
  195. msg(arg + ": invalid argument for address range")
  196. sys.exit(2)
  197. start,end = int(m.group(1)), int(m.group(3) or m.group(1))
  198. if start < 1:
  199. msg(args + ": First address must be >= 1")
  200. sys.exit(2)
  201. if end < start:
  202. msg(arg + ": Last address must be >= first address")
  203. sys.exit(2)
  204. return start,end
  205. def get_first_passphrase_from_user(what, opts):
  206. """
  207. Prompt the user for a passphrase and return it
  208. Supported options: echo_passphrase
  209. """
  210. if not 'quiet' in opts:
  211. msg("""
  212. Now you must choose a passphrase to encrypt the seed with. A key will be
  213. generated from your passphrase using a hash preset of '%s'. Please note that
  214. no strength checking of passphrases is performed. For an empty passphrase,
  215. just hit ENTER twice.
  216. """ % opts['hash_preset'])
  217. for i in range(passwd_max_tries):
  218. if 'echo_passphrase' in opts:
  219. return _my_raw_input("Enter %s: " % what)
  220. else:
  221. ret = _my_getpass("Enter %s: " % what)
  222. if ret == _my_getpass("Repeat %s: " % what):
  223. s = " (empty)" if not len(ret) else ""
  224. msg("%ss match%s" % (what.capitalize(),s))
  225. return ret
  226. else:
  227. msg("%ss do not match" % what.capitalize())
  228. msg("User failed to duplicate passphrase in " + str(passwd_max_tries) + " attempts")
  229. sys.exit(2)
  230. def _scrypt_hash_passphrase(passwd, salt, hash_preset, buflen=32):
  231. N,r,p = _get_hash_params(hash_preset)
  232. import scrypt
  233. return scrypt.hash(passwd, salt, 2**N, r, p, buflen=buflen)
  234. def _get_seed_from_brain_passphrase(words,opts):
  235. bp = " ".join(words)
  236. if debug: print "Sanitized brain passphrase: %s" % bp
  237. seed_len,hash_preset = _get_from_brain_opt_params(opts)
  238. if debug: print "Brainwallet l = %s, p = %s" % (seed_len,hash_preset)
  239. msg_r("Hashing brainwallet data. Please wait...")
  240. # Use buflen arg to scrypt.hash() to get seed of desired length
  241. seed = _scrypt_hash_passphrase(bp, "", hash_preset, buflen=seed_len/8)
  242. msg("Done")
  243. return seed
  244. def encrypt_seed(seed, key, opts):
  245. """
  246. Encrypt a seed for a {} deterministic wallet
  247. """.format(proj_name)
  248. # 192-bit seed is 24 bytes -> not multiple of 16. Must use MODE_CTR
  249. from Crypto.Cipher import AES
  250. from Crypto.Util import Counter
  251. c = AES.new(key, AES.MODE_CTR,counter=Counter.new(128))
  252. enc_seed = c.encrypt(seed)
  253. msg_r("Performing a test decryption of the seed...")
  254. c = AES.new(key, AES.MODE_CTR,counter=Counter.new(128))
  255. dec_seed = c.decrypt(enc_seed)
  256. if dec_seed == seed: msg("done")
  257. else:
  258. msg("FAILED.\nDecrypted seed doesn't match original seed. Aborting.")
  259. sys.exit(2)
  260. return enc_seed
  261. def write_to_stdout(data, what, confirm=True):
  262. if sys.stdout.isatty() and confirm:
  263. confirm_or_exit("",'output {} to screen'.format(what))
  264. elif not sys.stdout.isatty():
  265. import os
  266. of = os.readlink("/proc/%d/fd/1" % os.getpid())
  267. msg("Writing data to file '%s'" % of)
  268. sys.stdout.write(data)
  269. def get_default_wordlist():
  270. wl_id = default_wl
  271. if wl_id == "electrum": from mmgen.mn_electrum import electrum_words as wl
  272. elif wl_id == "tirosh": from mmgen.mn_tirosh import tirosh_words as wl
  273. return wl.strip().split("\n")
  274. def write_to_file(outfile,data,confirm=False):
  275. if confirm:
  276. from os import stat
  277. try:
  278. stat(outfile)
  279. except:
  280. pass
  281. else:
  282. confirm_or_exit("","File '%s' already exists\nOverwrite?" % outfile)
  283. try:
  284. f = open(outfile,'w')
  285. except:
  286. msg("Failed to open file '%s' for writing" % outfile)
  287. sys.exit(2)
  288. try:
  289. f.write(data)
  290. except:
  291. msg("Failed to write to file '%s'" % outfile)
  292. sys.exit(2)
  293. f.close
  294. def write_seed(seed, opts):
  295. outfile = "%s.%s" % (make_chksum_8(seed).upper(),seed_ext)
  296. if 'outdir' in opts:
  297. outfile = "%s/%s" % (opts['outdir'], outfile)
  298. from mmgen.bitcoin import b58encode_pad
  299. data = col4(b58encode_pad(seed))
  300. chk = _make_chksum_6(b58encode_pad(seed))
  301. o = "%s %s\n" % (chk,data)
  302. if 'stdout' in opts:
  303. write_to_stdout(o,"seed data",confirm=True)
  304. elif not sys.stdout.isatty():
  305. write_to_stdout(o,"seed data",confirm=False)
  306. else:
  307. write_to_file(outfile,o)
  308. msg("%s data saved to file '%s'" % ("Seed",outfile))
  309. def write_mnemonic(mn, seed, opts):
  310. outfile = "%s.words" % make_chksum_8(seed).upper()
  311. if 'outdir' in opts:
  312. outfile = "%s/%s" % (opts['outdir'], outfile)
  313. o = " ".join(mn) + "\n"
  314. if 'stdout' in opts:
  315. write_to_stdout(o,"mnemonic data",confirm=True)
  316. elif not sys.stdout.isatty():
  317. write_to_stdout(o,"mnemonic data",confirm=False)
  318. else:
  319. write_to_file(outfile,o)
  320. msg("%s data saved to file '%s'" % ("Mnemonic",outfile))
  321. def _display_control_data(label,metadata,hash_preset,salt,enc_seed):
  322. msg("WALLET DATA")
  323. fs = " {:25} {}"
  324. pw_empty = "yes" if metadata[3] == "E" else "no"
  325. from mmgen.bitcoin import b58encode_pad
  326. for i in (
  327. ("Label:", label),
  328. ("Seed ID:", metadata[0]),
  329. ("Key ID:", metadata[1]),
  330. ("Seed length:", metadata[2]),
  331. ("Scrypt hash params:", "Preset '%s' (%s)" % (hash_preset,
  332. " ".join([str(i) for i in _get_hash_params(hash_preset)]))),
  333. ("Passphrase is empty:", pw_empty),
  334. ("Timestamp:", "%s UTC" % metadata[4]),
  335. ("Salt:", b58encode_pad(salt)),
  336. ("Encrypted seed:", b58encode_pad(enc_seed))
  337. ): msg(fs.format(*i))
  338. def col4(s):
  339. nondiv = 1 if len(s) % 4 else 0
  340. return " ".join([s[4*i:4*i+4] for i in range(len(s)/4 + nondiv)])
  341. def write_wallet_to_file(seed, passwd, key_id, salt, enc_seed, opts):
  342. import time
  343. tv = time.gmtime(time.time())[:6]
  344. ts_hdr = "{:04d}{:02d}{:02d}_{:02d}{:02d}{:02d}".format(*tv)
  345. seed_id = make_chksum_8(seed)
  346. seed_len = str(len(seed)*8)
  347. pw_status = "NE" if len(passwd) else "E"
  348. hash_preset = opts['hash_preset']
  349. outfile = "{}-{}[{},{}].dat".format(seed_id,key_id,seed_len,hash_preset)
  350. if 'outdir' in opts:
  351. outfile = "%s/%s" % (opts['outdir'], outfile)
  352. label = opts['label'] if 'label' in opts else "None"
  353. from mmgen.bitcoin import b58encode_pad
  354. sf = b58encode_pad(salt)
  355. esf = b58encode_pad(enc_seed)
  356. metadata = seed_id.lower(),key_id.lower(),seed_len,pw_status,ts_hdr
  357. lines = (
  358. label,
  359. "{} {} {} {} {}".format(*metadata),
  360. "{}: {} {} {}".format(hash_preset,*_get_hash_params(hash_preset)),
  361. "{} {}".format(_make_chksum_6(sf), col4(sf)),
  362. "{} {}".format(_make_chksum_6(esf), col4(esf))
  363. )
  364. chk = _make_chksum_6(" ".join(lines))
  365. confirm = False if 'quiet' in opts else True
  366. write_to_file(outfile, "\n".join((chk,)+lines)+"\n", confirm)
  367. msg("Wallet saved to file '%s'" % outfile)
  368. if 'verbose' in opts:
  369. _display_control_data(label,metadata,hash_preset,salt,enc_seed)
  370. def compare_checksums(chksum1, desc1, chksum2, desc2):
  371. if chksum1.lower() == chksum2.lower():
  372. msg("OK (%s)" % chksum1.upper())
  373. return True
  374. else:
  375. msg("ERROR!\nComputed checksum %s (%s) doesn't match checksum %s (%s)" \
  376. % (desc1,chksum1,desc2,chksum2))
  377. return False
  378. def _is_hex(s):
  379. try: int(s,16)
  380. except: return False
  381. else: return True
  382. def check_mmseed_format(words):
  383. valid = False
  384. what = "%s data" % seed_ext
  385. chklen = len(words[0])
  386. if len(words) < 3 or len(words) > 12:
  387. msg("Invalid data length (%s) in %s" % (len(words),what))
  388. elif not _is_hex(words[0]):
  389. msg("Invalid format of checksum '%s' in %s"%(words[0], what))
  390. elif chklen != 6:
  391. msg("Incorrect length of checksum (%s) in %s" % (chklen,what))
  392. else: valid = True
  393. if valid == False:
  394. msg("Invalid %s data" % seed_ext)
  395. sys.exit(3)
  396. def check_wallet_format(infile, lines, opts):
  397. def vmsg(s):
  398. if 'verbose' in opts: msg(s)
  399. what = "wallet file '%s'" % infile
  400. valid = False
  401. chklen = len(lines[0])
  402. if len(lines) != 6:
  403. vmsg("Invalid number of lines (%s) in %s" % (len(lines),what))
  404. elif chklen != 6:
  405. vmsg("Incorrect length of Master checksum (%s) in %s" % (chklen,what))
  406. elif not _is_hex(lines[0]):
  407. vmsg("Invalid format of Master checksum '%s' in %s"%(lines[0], what))
  408. else: valid = True
  409. if valid == False:
  410. msg("Invalid %s" % what)
  411. sys.exit(2)
  412. def _check_chksum_6(chk,val,desc,infile):
  413. comp_chk = _make_chksum_6(val)
  414. if chk != comp_chk:
  415. msg("%s checksum incorrect in file '%s'!" % (desc,infile))
  416. msg("Checksum: %s. Computed value: %s" % (chk,comp_chk))
  417. sys.exit(2)
  418. elif debug:
  419. msg("%s checksum passed: %s" % (desc.capitalize(),chk))
  420. #def bin_pad(bindata,length):
  421. # return unhexlify(hexlify(bindata).zfill(length*2))
  422. def get_data_from_wallet(infile,opts):
  423. msg("Getting {} wallet data from file: {}".format(proj_name,infile))
  424. try:
  425. f = open(infile, 'r')
  426. except:
  427. msg("Unable to open file '" + infile + "' for reading")
  428. sys.exit(2)
  429. lines = [i.strip() for i in f.readlines()]
  430. f.close()
  431. check_wallet_format(infile, lines, opts)
  432. label = lines[1]
  433. metadata = lines[2].split()
  434. for i in 0,1: metadata[i] = metadata[i].upper()
  435. hd = lines[3].split()
  436. hash_preset = hd[0][:-1]
  437. hash_params = [int(i) for i in hd[1:]]
  438. if hash_params != _get_hash_params(hash_preset):
  439. msg("Hash parameters '%s' don't match hash preset '%s'" %
  440. (" ".join(hash_params), hash_preset))
  441. sys.exit(9)
  442. res = {}
  443. from mmgen.bitcoin import b58decode_pad
  444. for i,key in (4,"salt"),(5,"enc_seed"):
  445. l = lines[i].split()
  446. val = "".join(l[1:])
  447. _check_chksum_6(l[0], val, key, infile)
  448. res[key] = b58decode_pad(val)
  449. _check_chksum_6(lines[0], " ".join(lines[1:]), "Master", infile)
  450. return label,metadata,hash_preset,res['salt'],res['enc_seed']
  451. def _get_words_from_user(opts, prompt):
  452. # split() also strips
  453. if 'echo_passphrase' in opts:
  454. return _my_raw_input(prompt).split()
  455. else:
  456. return _my_getpass(prompt).split()
  457. def _get_words_from_file(infile,what):
  458. msg("Getting %s data from file '%s'" % (what,infile))
  459. try:
  460. f = open(infile, 'r')
  461. except:
  462. msg("Unable to open file '%s' for reading" % infile)
  463. sys.exit(2)
  464. lines = f.readlines()
  465. f.close()
  466. # split() also strips
  467. return [w for l in lines for w in l.split()]
  468. def get_words(infile,what,prompt,opts):
  469. if infile:
  470. words = _get_words_from_file(infile,what)
  471. else:
  472. words = _get_words_from_user(opts,prompt)
  473. if debug: print "Sanitized input: [%s]" % " ".join(words)
  474. return words
  475. def get_seed_from_seed_data(words):
  476. check_mmseed_format(words)
  477. stored_chk = words[0]
  478. seed_b58 = "".join(words[1:])
  479. chk = _make_chksum_6(seed_b58)
  480. msg_r("Validating %s checksum..." % seed_ext)
  481. if compare_checksums(chk, "from seed", stored_chk, "from input"):
  482. from mmgen.bitcoin import b58decode_pad
  483. seed = b58decode_pad(seed_b58)
  484. msg("%s data produces seed ID: %s" % (seed_ext,make_chksum_8(seed)))
  485. return seed
  486. else:
  487. msg("Invalid checksum for {} seed".format(proj_name))
  488. sys.exit(9)
  489. def get_seed_from_wallet(infile,opts,
  490. prompt="Enter {} wallet passphrase: ".format(proj_name)):
  491. wdata = get_data_from_wallet(infile,opts)
  492. label,metadata,hash_preset,salt,enc_seed = wdata
  493. if 'verbose' in opts: _display_control_data(*wdata)
  494. passwd = " ".join(get_words("","",prompt,opts))
  495. key = make_key(passwd, salt, hash_preset)
  496. return decrypt_seed(enc_seed, key, metadata[0], metadata[1])
  497. def make_key(passwd, salt, hash_preset):
  498. msg_r("Hashing passphrase. Please wait...")
  499. key = _scrypt_hash_passphrase(passwd, salt, hash_preset)
  500. msg("done")
  501. return key
  502. def decrypt_seed(enc_seed, key, seed_id, key_id):
  503. msg_r("Checking key...")
  504. chk = make_chksum_8(key)
  505. if not compare_checksums(chk, "of key", key_id, "in header"):
  506. msg("Passphrase incorrect?")
  507. sys.exit(3)
  508. msg_r("Decrypting seed with key...")
  509. from Crypto.Cipher import AES
  510. from Crypto.Util import Counter
  511. c = AES.new(key, AES.MODE_CTR,counter=Counter.new(128))
  512. dec_seed = c.decrypt(enc_seed)
  513. chk = make_chksum_8(dec_seed)
  514. if compare_checksums(chk,"of decrypted seed",seed_id,"in header"):
  515. msg("Passphrase is OK")
  516. else:
  517. if not debug:
  518. msg_r("Checking key ID...")
  519. chk = make_chksum_8(key)
  520. if compare_checksums(chk, "of key", key_id, "in header"):
  521. msg("Key ID is correct but decryption of seed failed")
  522. else:
  523. msg("Incorrect passphrase")
  524. sys.exit(3)
  525. if debug: msg("key: %s" % hexlify(key))
  526. return dec_seed
  527. def get_seed(infile,opts,no_wallet=False):
  528. if 'from_mnemonic' in opts:
  529. prompt = "Enter mnemonic: "
  530. words = get_words(infile,"mnemonic",prompt,opts)
  531. wl = get_default_wordlist()
  532. from mmgen.mnemonic import get_seed_from_mnemonic
  533. return get_seed_from_mnemonic(words,wl)
  534. elif 'from_brain' in opts:
  535. msg("")
  536. if 'quiet' not in opts:
  537. confirm_or_exit(
  538. cmessages['brain_warning'].format(
  539. proj_name.capitalize(),
  540. *_get_from_brain_opt_params(opts)),
  541. "continue")
  542. prompt = "Enter brainwallet passphrase: "
  543. words = get_words(infile,"brainwallet",prompt,opts)
  544. return _get_seed_from_brain_passphrase(words,opts)
  545. elif 'from_seed' in opts:
  546. prompt = "Enter seed in %s format: " % seed_ext
  547. words = get_words(infile,"seed",prompt,opts)
  548. return get_seed_from_seed_data(words)
  549. elif no_wallet:
  550. return False
  551. else:
  552. return get_seed_from_wallet(infile, opts)