utils.py 21 KB

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