From c8fcbdfcdcc6e7254f70646fa5e2ba7a95d81bb2 Mon Sep 17 00:00:00 2001 From: philemon Date: Sat, 30 Nov 2013 16:48:21 +0400 Subject: [PATCH] modified: README.md modified: mmgen-keygen modified: mmgen-walletchk modified: mmgen/utils.py --- README.md | 215 +++++++++++++++++++++++++++++++++++++++++++++++- mmgen-keygen | 4 +- mmgen-walletchk | 19 ++--- mmgen/utils.py | 30 +++++-- 4 files changed, 245 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index fc416e6b..aec3715a 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,213 @@ -mmgen -===== +# mmgen = Multi-Mode GENerator +## command-line Bitcoin cold storage solution + +NOTE: for the time being, MMGen should be considered Alpha software. +Downloading and testing it out is easy, risk-free and encouraged. +However, spending significant amounts of BTC into your mmgen-generated +addresses is done at your own risk. + +### Features: + +> As with all deterministic wallets, mmgen can generate an unlimited number +> of address/key pairs from a single seed. You back up your wallet only once. + +> MMGen gives you four ways to access your Bitcoins: + +>> 1) with a wallet encrypted using the crack-resistant scrypt hash function +>> + AES256. The wallet's password and hash strength can be changed. + +>> 2) from a one-line seed file (unencrypted); + +>> 3) from an Electrum-like mnemonic of 12, 18 or 24 words; or + +>> 4) from a brain password (recommended for expert users only). + +> Furthermore, these methods can all be combined. If you forget your +> Electrum-like mnemonic, for example, you can regenerate it and your +> keys from a stored wallet or seed. Correspondingly, a lost wallet or +> seed can be recovered from the mnemonic. + +> The wallet and seed are short, simple text files suitable for printing +> or even writing out by hand. The base-58-encoded seed is short enough +> to memorize, providing another brain storage alternative. + +> Implemented as a suite of python scripts, MMGen is super-lightweight. +> Combined with bitcoind compiled with the watch-only address feature +> (see below), it provides a complete solution for securely storing +> Bitcoins offline and tracking and spending them online. + + +### Instructions for Linux/Unix: + +### Download: +> `git clone https://github.com/mmgen/mmgen.git` + +### Install: +> Install the ecdsa, scrypt and pycrypto modules: +>> `sudo pip install ecdsa scrypt pycrypto` + +> Install mmgen: +>> `cd mmgen; sudo ./setup.py install` + +### Getting Started: +> On your offline computer: + +> Generate a wallet with a random seed: + + $ mmgen-walletgen + ... + Wallet saved to file '89ABCDEF-76543210[256,3].dat' + + +> "89ABCDEF" is the Seed ID; "76543210" is the Key ID. +> The Seed ID never changes and will be used to identify all +> keys/addresses generated by this wallet. +> The Key ID changes when the wallet's password or hash preset are changed. +> "256" is the seed length; "3" is the scrypt hash preset. +> These are configurable. + + +> Generate ten addresses with the wallet: + + $ mmgen-addrgen 89ABCDEF-76543210[256,3].dat 1-10 + ... + Address data saved to file '89ABCDEF[1-10].addrs' + + +> Note that the address range, "1-10", is indicated in the filename. +> To generate addresses 1000 through 2000 (for example), specify +> "1000-2000" on the command line and the filename will be +> '89ABCDEF[1000-2000].addrs' + + $ cat '89ABCDEF[1-10].addrs' + 89ABCDEF { + 1 16bNmyYISiptuvJG3X7MPwiiS4HYvD7ksE + 2 1AmkUxrfy5dMrfmeYwTxLxfIswUCcpeysc + 3 1HgYCsfqYzIg7LVVfDTp7gYJocJEiDAy6N + 4 14Tu3z1tiexXDonNsFIkvzqutE5E3pTK8s + 5 1PeI55vtp2bX2uKDkAAR2c6ekHNYe4Hcq7 + 6 1FEqfEsSILwXPfMvVvVuUovzTaaST62Mnf + 7 1LTTzuhMqPLwQ4IGCwwugny6ZMtUQJSJ1 + 8 1F9495H8EJLb54wirgZkVgI47SP7M2RQWv + 9 1JbrCyt7BdxRE9GX1N7GiEct8UnIjPmpYd + 10 1H7vVTk4ejUbQXw45I6g5qvPBSe9bsjDqh + } + + +> To store your Bitcoins, spend them into these addresses from whatever +> wallets/software you're currently using. If you have lots of BTC, +> generate lots of addresses so that each address will have only a +> relatively small balance. + +### Spending your stored coins: +> Take address 1 out of cold storage by generating a key for it: + + $ mmgen-keygen 89ABCDEF-76543210[256,3].dat 1 + ... + Key data saved to file '89ABCDEF[1].akeys' + + $ cat 89ABCDEF[1].akeys + 89ABCDEF { + 1 sec: 5JCAfK1pjRoJgmpmd2HEMNwHxAzprGIXeQt8dz5qt3iLvU2KCbS + addr: 16bNmyYISiptuvJG3X7MPwiiS4HYvD7ksE + } + +> Save the \*.akeys file to a USB stick and transfer it to your online computer. + +> On your online computer, import the secret key into +> a running bitcoind or bitcoin-qt: + + $ bitcoind importprivkey 5JCAfK1pjRoJgmpmd2HEMNwHxAzprGIXeQt8dz5qt3iLvU2KCbS + +> That's all there is to it! + +> OPTIONAL: To track balances without exposing secret keys on your +> online computer, download and compile sipa's bitcoind patched for +> watch-only addresses: + + $ git clone https://github.com/sipa/bitcoin + $ git branch mywatchonly remotes/origin/watchonly + $ git checkout mywatchonly + + (build, install) + +> Import your addresses from '89ABCDEF[1-10].addrs': + + $ bitcoind importaddress 16bNmyYISiptuvJG3X7MPwiiS4HYvD7ksE + $ bitcoind importaddress 1AmkUxrfy5dMrfmeYwTxLxfIswUCcpeysc + $ ... + +### Using the mnemonic and seed features: + +> Using our example above, + +> Generate a mnemonic from the wallet: + + $ mmgen-walletchk -m '89ABCDEF-76543210[256,3].dat' + ... + Mnemonic data saved to file '89ABCDEF.words' + + $ cat 89ABCDEF.words + pleasure tumble spider laughter many stumble secret bother + after search float relationship path strong curtain savior + worst suspend bright touch away dirty measure thorn + +> Note: a 128-bit or 192-bit seed will generate a shorter mnemonic of 12 +> or 18 words. Generate a wallet with a shorter seed by using +> `mmgen-walletgen -l`. Whether you consider 128 or 192 bytes of +> entropy to be enough is up to you. + +> Generate addresses 1-11 using the mnemonic instead of the wallet: + + $ mmgen-addrgen -m 89ABCDEF.words 1-11 + ... + Address data saved to file '89ABCDEF[1-11].addrs' + +> Compare the first ten addresses with those earlier generated from the +> wallet. You'll see they're the same. + +> Recover a lost wallet using the mnemonic: + + $ mmgen-walletgen -m 89ABCDEF.words + ... + Wallet saved to file '89ABCDEF-01234567[256,3].dat' + +> Note that the regenerated wallet has a different Key ID but +> of course the same Seed ID. + +> Seeds are generated the same way as mnemonics. Just change the +> '-m' option to '-s' in the preceding commands. + +> A seed file for a 256-bit seed looks like this: + + $ cat 8B7392ED.mmseed + f4c84b C5ZT wWpT Jsoi wRVw 2dm9 Aftd WLb8 FggQ eC8h Szjd da9L + +> And for a 128-bit seed: + + $ cat 8E0DFB78.mmseed + 0fe02f XnyC NfPH piuW dQ2d nM47 VU + +> The latter is short enough to be memorized or written down. + +> The first word is a checksum. +> To check that you've written or memorized the seed correctly, take the +> first 3 bytes of a sha256 hash of the remainder of the line (with +> spaces removed). + +#### Mnemonics and seeds — additional information: +> Mnemonic and seed data may be entered at the prompt instead of from a +> file. Just omit the filename on the command line. + +> Mnemonic and seed data may be printed to standard output instead of a +> file with `mmgen-walletchk -S` + +> Mnemonic and seed files may be output to a directory besides the +> current one with `mmgen-walletchk -d` + +> Bear in mind that mnemonic and seed data is unencrypted. If it's +> compromised, your Bitcoins can easily be stolen. Make sure no one's +> looking when you print mnemonic or seed data to screen. Securely +> delete your mnemonic and seed files. In Linux, you can achieve +> additional security by writing the files to volatile memory in +> '/dev/shm' instead of disk. diff --git a/mmgen-keygen b/mmgen-keygen index 59dec039..cf8f1284 100755 --- a/mmgen-keygen +++ b/mmgen-keygen @@ -168,8 +168,8 @@ if 'stdout' in opts: if invoked_as == "keygen" and not 'quiet' in opts: confirm = True else: confirm = False - write_to_stdout(addr_data_str,confirm) + write_to_stdout(addr_data_str,"secret keys",confirm) elif not sys.stdout.isatty(): - write_to_stdout(addr_data_str,confirm=False) + write_to_stdout(addr_data_str,"secret keys",confirm=False) else: write_addr_data_to_file(seed, addr_data_str, start, end, opts) diff --git a/mmgen-walletchk b/mmgen-walletchk index 9986a526..6586d5f0 100755 --- a/mmgen-walletchk +++ b/mmgen-walletchk @@ -32,18 +32,23 @@ help_data = { 'usage': "[opts] [filename]", 'options': """ -h, --help Print this help message +-d, --outdir d Specify an alternate directory 'd' for output -e, --echo-passphrase Print passphrase to screen when typing it -m, --export-mnemonic Export the wallet's mnemonic to file -s, --export-seed Export the wallet's seed to file +-S, --stdout Print seed or mnemonic data to standard output -v, --verbose Produce more verbose output """ } -short_opts = "hemsv" -long_opts = "help","echo_passphrase","export_mnemonic","export_seed","verbose" +short_opts = "hd:emsSv" +long_opts = "help","outdir=","echo_passphrase","export_mnemonic",\ + "export_seed","stdout","verbose" opts,cmd_args = Opts.process_opts(sys.argv,help_data,short_opts,long_opts) +check_opts(opts, ('outdir',)) + if len(cmd_args) != 1: msg("One input file must be specified") sys.exit(2) @@ -63,13 +68,7 @@ if 'export_mnemonic' in opts: p = True if debug else False mn = get_mnemonic_from_seed(seed, wl, default_wl, print_info=p) - write_mnemonic_to_file(mn, seed, opts) - - if debug: print "Mnemonic:\n%s" % " ".join(mn) + write_mnemonic(mn, seed, opts) if 'export_seed' in opts: - write_seed_to_file(seed, opts) - - if debug: - from mmgen.bitcoin import b58encode_pad - print "Seed: %s" % col4(b58encode_pad(seed)) + write_seed(seed, opts) diff --git a/mmgen/utils.py b/mmgen/utils.py index 6e927206..478928d2 100755 --- a/mmgen/utils.py +++ b/mmgen/utils.py @@ -342,14 +342,14 @@ def encrypt_seed(seed, key, opts): return enc_seed -def write_to_stdout(data, confirm=True): +def write_to_stdout(data, what, confirm=True): if sys.stdout.isatty() and confirm: - confirm_or_exit("",'output secret keys to screen') + confirm_or_exit("",'output {} to screen'.format(what)) elif not sys.stdout.isatty(): import os of = os.readlink("/proc/%d/fd/1" % os.getpid()) msg("Writing data to file '%s'" % of) - sys.stdout.write("\n" + data) + sys.stdout.write(data) def get_default_wordlist(): @@ -386,7 +386,7 @@ def write_to_file(outfile,data,confirm=False): f.close -def write_seed_to_file(seed, opts): +def write_seed(seed, opts): outfile = "%s.%s" % (make_chksum_8(seed).upper(),seed_ext) if 'outdir' in opts: @@ -396,20 +396,32 @@ def write_seed_to_file(seed, opts): data = col4(b58encode_pad(seed)) chk = _make_chksum_6(b58encode_pad(seed)) - write_to_file(outfile, "%s %s\n" % (chk,data)) + o = "%s %s\n" % (chk,data) - msg("%s data saved to file '%s'" % ("Seed",outfile)) + if 'stdout' in opts: + write_to_stdout(o,"seed data",confirm=True) + elif not sys.stdout.isatty(): + write_to_stdout(o,"seed data",confirm=False) + else: + write_to_file(outfile,o) + msg("%s data saved to file '%s'" % ("Seed",outfile)) -def write_mnemonic_to_file(mn, seed, opts): +def write_mnemonic(mn, seed, opts): outfile = "%s.words" % make_chksum_8(seed).upper() if 'outdir' in opts: outfile = "%s/%s" % (opts['outdir'], outfile) - write_to_file(outfile," ".join(mn) + "\n") + o = " ".join(mn) + "\n" - msg("%s data saved to file '%s'" % ("Mnemonic",outfile)) + if 'stdout' in opts: + write_to_stdout(o,"mnemonic data",confirm=True) + elif not sys.stdout.isatty(): + write_to_stdout(o,"mnemonic data",confirm=False) + else: + write_to_file(outfile,o) + msg("%s data saved to file '%s'" % ("Mnemonic",outfile)) def _display_control_data(label,metadata,hash_preset,salt,enc_seed):