Old non-oo wallet code and files removed
test/test.py updated
This commit is contained in:
parent
d3f07f3c9f
commit
acd8eb26c5
21 changed files with 516 additions and 2680 deletions
|
|
@ -12,6 +12,7 @@
|
|||
* <a href=#11>Using the mnemonic and seed features</a>
|
||||
* <a href=#12>Mnemonics and seeds — additional information</a>
|
||||
* <a href=#13>Incognito wallets</a>
|
||||
* <a href=#13a>Hidden incognito wallets</a>
|
||||
|
||||
|
||||
### <a name=01>Basic Operations</a>
|
||||
|
|
@ -22,7 +23,7 @@ On your offline computer, generate a wallet with a random seed:
|
|||
|
||||
$ mmgen-walletgen
|
||||
...
|
||||
Wallet saved to file '89ABCDEF-76543210[256,3].mmdat'
|
||||
MMGen wallet written to file '89ABCDEF-76543210[256,3].mmdat'
|
||||
|
||||
"89ABCDEF" is the Seed ID; "76543210" is the Key ID. These are randomly
|
||||
generated, so your IDs will of course be different than the fictitious ones used
|
||||
|
|
@ -41,7 +42,7 @@ Now generate ten addresses with your just-created wallet:
|
|||
|
||||
$ mmgen-addrgen 89ABCDEF-76543210[256,3].mmdat 1-10
|
||||
...
|
||||
Address data saved to file '89ABCDEF[1-10].addrs'
|
||||
Addresses written to file '89ABCDEF[1-10].addrs'
|
||||
|
||||
$ cat '89ABCDEF[1-10].addrs'
|
||||
89ABCDEF {
|
||||
|
|
@ -58,7 +59,7 @@ Now generate ten addresses with your just-created wallet:
|
|||
}
|
||||
|
||||
Note that the address range, "1-10", is reflected in the resulting filename.
|
||||
MMGen addresses are identified by their seed ID and index number, separated by a
|
||||
MMGen addresses are identified by their Seed ID and index number, separated by a
|
||||
colon. In this example, "89ABCDEF:1" is the MMGen equivalent of Bitcoin address
|
||||
16bNmyYISiptuvJG3X7MPwiiS4HYvD7ksE, "89ABCDEF:2" the equivalent of
|
||||
1AmkUxrfy5dMrfmeYwTxLxfIswUCcpeysc, and so forth.
|
||||
|
|
@ -93,9 +94,9 @@ Copy this file onto a USB stick and transfer it to your online computer.
|
|||
#### <a name=04>Import addresses (online computer):</a>
|
||||
|
||||
On your online computer, go to your bitcoind data directory and move any
|
||||
existing 'wallet.dat' file out of the way. Start bitcoind and let it generate
|
||||
a new 'wallet.dat', which you'll use as your **tracking wallet**. Import your
|
||||
four addresses into the new tracking wallet with the command:
|
||||
existing 'wallet.dat' file out of harm's way. Start bitcoind and let it
|
||||
generate a new 'wallet.dat', which you'll use as your **tracking wallet**.
|
||||
Import your four addresses into the new tracking wallet with the command:
|
||||
|
||||
$ mmgen-addrimport my.addrs
|
||||
|
||||
|
|
@ -138,7 +139,7 @@ harmless until they're signed and broadcast to the network, so feel free to
|
|||
experiment with different transactions using different combinations of inputs
|
||||
and outputs.
|
||||
|
||||
First of all you'll want to examine your balances. Note that 'mmgen-tool
|
||||
First of all, you'll want to examine your balances. Note that 'mmgen-tool
|
||||
listaddresses' shows only MMGen address balances; to view **all** balances,
|
||||
including your non-MMGen ones, use the 'mmgen-txcreate' command:
|
||||
|
||||
|
|
@ -147,8 +148,8 @@ including your non-MMGen ones, use the 'mmgen-txcreate' command:
|
|||
A list of all unspent outputs will appear, along with a menu allowing you to
|
||||
sort the outputs by four criteria: transaction ID, address, amount and
|
||||
transaction age. Your overall balance in BTC appears at the top of the screen.
|
||||
The list may be viewed in a pager or printed to file. For a wallet with ten
|
||||
unspent outputs, the display might look something like this:
|
||||
The list may optionally be viewed in a pager or printed to file. For a wallet
|
||||
with ten unspent outputs, the display might look something like this:
|
||||
|
||||
UNSPENT OUTPUTS (sort order: reverse amount) Total BTC: 39.72
|
||||
Num TX id Vout Address Amount (BTC) Age(days)
|
||||
|
|
@ -168,126 +169,143 @@ unspent outputs, the display might look something like this:
|
|||
(Type 'q' to quit sorting, 'p' to print to file, 'v' to view in pager):
|
||||
|
||||
Now let's actually create a transaction. Let's say you've decided to gradually
|
||||
begin moving your 39.72 BTC balance into your shiny new MMGen wallet with seed
|
||||
begin moving your 39.72 BTC balance into your shiny new MMGen wallet with Seed
|
||||
ID 89ABCDEF.
|
||||
|
||||
Before moving any funds into your MMGen wallet, you should back it up in several
|
||||
places and possibly on several media too: paper, flash memory or CD-ROM, for
|
||||
example. Of course the wallet should have a passphrase. Otherwise, anyone who
|
||||
gains physical access to one of your backups can easily steal your coins.
|
||||
example. You're advised to use a passphrase on your wallet. Otherwise, anyone
|
||||
who gains physical access to one of your backups can easily steal your coins.
|
||||
|
||||
Recall that there's no limit to the number of addresses you can generate with
|
||||
your seed. You've wisely determined that having many addresses with relatively
|
||||
small balances is a Good Idea. So you've decided to begin by breaking up the
|
||||
address with the largest balance, 10 BTC, into three roughly equal parts,
|
||||
sending it to the addresses labeled "Storage 1", "Storage 2" and "Storage 3"
|
||||
(89ABCDEF:2, 89ABCDEF:3 and 89ABCDEF:4).
|
||||
small balances is a Good Idea. So you've decided to begin by breaking up your
|
||||
address with the largest balance, 10 BTC, address 1F93Znz..., into three roughly
|
||||
equal parts and send them to the addresses labeled "Storage 1", "Storage 2" and
|
||||
"Storage 3" (89ABCDEF:2, 89ABCDEF:3 and 89ABCDEF:4).
|
||||
|
||||
To refresh your memory, here are the three addresses in question:
|
||||
To refresh your memory, here are the three destination addresses in question:
|
||||
|
||||
$ cat my.addrs
|
||||
$ cat my.addrs | grep -v Donations
|
||||
# My first MMGen addresses
|
||||
89ABCDEF {
|
||||
1 16bNmyYISiptuvJG3X7MPwiiS4HYvD7ksE Donations
|
||||
2 1AmkUxrfy5dMrfmeYwTxLxfIswUCcpeysc Storage 1
|
||||
3 1HgYCsfqYzIg7LVVfDTp7gYJocJEiDAy6N Storage 2
|
||||
4 14Tu3z1tiexXDonNsFIkvzqutE5E3pTK8s Storage 3
|
||||
}
|
||||
|
||||
The following command will send 3.3 BTC to the first two addresses and the
|
||||
remainder of the transaction's 10 BTC input to the third, subtracting a default
|
||||
transaction fee of 0.001 BTC:
|
||||
The following command does just this, sending 6.6 BTC of the transaction's 10
|
||||
BTC input to the first two addresses and the remainder to the third address,
|
||||
for which no amount has been specified:
|
||||
|
||||
$ mmgen-txcreate 1AmkUxrfy5dMrfmeYwTxLxfIswUCcpeysc,3.3 1HgYCsfqYzIg7LVVfDTp7gYJocJEiDAy6N,3.3 14Tu3z1tiexXDonNsFIkvzqutE5E3pTK8s
|
||||
|
||||
The bare address with no amount is the **change address**. MMGen will compute
|
||||
the change amount (3.399 BTC in this case) automatically.
|
||||
The address with no amount is the **change address**; MMGen will calculate
|
||||
the amount sent to it automatically by subtracting the sum of the outputs
|
||||
plus transaction fee, if any, from the inputs. In our example, 3.39995 BTC (10
|
||||
BTC - (3.3 + 3.3 BTC) - .00005 BTC default transaction fee) will go to this
|
||||
address.
|
||||
|
||||
Note that the above transaction can be written much more elegantly and concisely
|
||||
using MMGen addresses in place of their Bitcoin equivalents:
|
||||
Note that the above transaction can be expressed much more concisely by
|
||||
replacing the Bitcoin addresses with their MMGen equivalents:
|
||||
|
||||
$ mmgen-txcreate 89ABCDEF:2,3.3 89ABCDEF:3,3.3 89ABCDEF:4
|
||||
|
||||
After hitting ENTER you'll be presented with the same display produced by the
|
||||
'-i' option above, plus an interactive menu. After quitting with 'q', you'll
|
||||
be prompted to choose the transaction's inputs.
|
||||
For this to work, the addresses must be imported into your tracking wallet,
|
||||
which they should be in any case.
|
||||
|
||||
After hitting ENTER you'll be presented with the same UNSPENT OUTPUTS display as
|
||||
with the '-i' option above. In our example, note that the output with 10 BTC
|
||||
which you wish to spend, 1F93Znz..., is listed as number '1'. Remember this.
|
||||
After quitting the menu with 'q' you'll see the following prompt:
|
||||
|
||||
Enter a range or space-separated list of outputs to spend:
|
||||
|
||||
Find the input with the 10 BTC balance in the list. This is input 1), so type
|
||||
'1' and ENTER. After several more prompts and confirmations 'mmgen-txcreate'
|
||||
will exit with the message:
|
||||
Type your remembered '1' here and hit ENTER. After several more prompts and
|
||||
confirmations, your transaction will be saved:
|
||||
|
||||
Transaction data saved to file 'tx_1EDCBA[6.6].raw'
|
||||
Transaction written to file 'tx_FEDCBA[6.6].raw'
|
||||
|
||||
Note that the transaction has a unique ID, and the non-change spend amount, 6.6
|
||||
BTC, is conveniently included in the filename.
|
||||
Note that the transaction has a unique ID, and the non-change spend amount of
|
||||
6.6 BTC is included in the filename.
|
||||
|
||||
#### <a name=06>Sign a transaction (offline computer):</a>
|
||||
|
||||
Now copy the raw transaction you've just created to a USB stick and transfer it
|
||||
to your offline computer for signing. You need to find the key for your
|
||||
transaction's one input address, 1F9495H8EJL.... If the key in question is in a
|
||||
bitcoin 'wallet.dat', there's an included command (a modified version of the
|
||||
well-known pywallet utility) that will conveniently extract it for you:
|
||||
to your offline computer for signing. For this you'll need the key corresponding
|
||||
to the transaction's input address, 1F93Znz.... If the key in question is in a
|
||||
bitcoind 'wallet.dat', copy 'wallet.dat' to your offline machine as well and use
|
||||
the included command 'mmgen-pywallet' (a modified version of the well-known
|
||||
pywallet utility) to extract the required key from it.
|
||||
|
||||
$ mmgen-pywallet -k wallet.dat
|
||||
...
|
||||
wallet.dat secret keys saved to file wd_EDBC983A[102].keys
|
||||
Private keys written to file 'wd_EDBC983A[102].keys'
|
||||
|
||||
You've in fact extracted a list of all of the wallet's 102 keys here, but that's
|
||||
not a problem, since the unused keys will be ignored (you can extract only the
|
||||
keys you need using the '--keys-for-addrs' option). Now go ahead and sign the
|
||||
transaction using this list of keys.
|
||||
not a problem, since the unused keys will be ignored (if you wish, you can
|
||||
extract only the one key you need using the '--keys-for-addrs' option). Now go
|
||||
ahead and sign the transaction using this key list:
|
||||
|
||||
$ mmgen-txsign -k wd_EDBC983A[102].keys tx_1EDCBA[6.6].raw
|
||||
$ mmgen-txsign -k wd_EDBC983A[102].keys tx_FEDCBA[6.6].raw
|
||||
...
|
||||
Signed transaction saved to file tx_ABCDEF[0.1].sig
|
||||
Signed transaction written to file 'tx_FEDCBA[6.6].sig'
|
||||
|
||||
Note that 'mmgen-pywallet's output is just a flat list of keys. So if you have
|
||||
several Bitcoin wallets with balances, you can just dump all their keys and
|
||||
merge them into a single file which you can use to sign all future transactions
|
||||
with wallet.dat inputs:
|
||||
several Bitcoin wallets with balances, you can just concatenate these lists into
|
||||
a single file which you can use to sign all future transactions with
|
||||
'wallet.dat' inputs:
|
||||
|
||||
$ mmgen-pywallet -k wallet1.dat
|
||||
$ mmgen-pywallet -k wallet2.dat
|
||||
$ mmgen-pywallet -k wallet3.dat
|
||||
$ cat wd_*.keys > all_keys
|
||||
|
||||
For your future transactions with MMGen address inputs, you'll list the MMGen
|
||||
seed source (wallet, mnemonic or seed file) on the command line after the
|
||||
transaction file, and the required keys will be generated automatically, as in
|
||||
this example:
|
||||
Once you've migrated your funds to MMGen, such key files will no longer be
|
||||
needed. Instead, you'll sign transactions by listing an MMGen seed source
|
||||
(wallet, mnemonic or seed file) on the command line after the transaction,
|
||||
and the required keys will be generated on the fly, as in this example:
|
||||
|
||||
$ mmgen-txsign tx_9D2C3A[1.23].raw B73B58EA-125FB230[256,3].mmdat
|
||||
...
|
||||
Signed transaction saved to file tx_9D2C3A[1.23].sig
|
||||
$ mmgen-txsign tx_ABCDE[1.2345].raw 1234567A-BCDEF123[256,3].mmdat
|
||||
|
||||
Transactions may contain a mixture of MMGen and non-MMGen inputs as well as
|
||||
inputs with more than one MMGen seed ID. Just provide a seed source for each
|
||||
seed ID on the command line.
|
||||
Transactions may contain a mixture of MMGen and non-MMGen inputs, as well as
|
||||
inputs with more than one MMGen Seed ID. Just list a seed source for each
|
||||
MMGen input on the command line after the transaction, as in this example:
|
||||
|
||||
Eventually, when you've placed all your BTC under MMGen control, you'll never
|
||||
have deal with keys directly again, because MMGen generates all keys on the fly
|
||||
using the seed.
|
||||
$ mmgen-txsign -k key_list my_tx.raw a.mmdat b.mmwords c.mmseed
|
||||
|
||||
#### <a name=07>Send a transaction (online computer):</a>
|
||||
|
||||
Now you're ready for the final step: broadcasting the transaction to the network.
|
||||
Copy the 'tx_*.sig' file to your online computer, start bitcoind, if it's not
|
||||
running, and execute the command:
|
||||
Now you're ready for the final step: broadcasting the transaction to the
|
||||
network. Copy just-created signed transaction file to your online computer,
|
||||
start bitcoind and issue the command:
|
||||
|
||||
$ mmgen-txsend tx_1EDCBA[6.6].sig
|
||||
$ mmgen-txsend tx_FEDCBA[6.6].sig
|
||||
|
||||
Like all MMGen commands, 'mmgen-txsend' is interactive, so you'll be asked for
|
||||
confirmation before the transaction is actually sent.
|
||||
Like all MMGen commands, 'mmgen-txsend' is interactive, so you'll be prompted
|
||||
before the transaction is actually sent.
|
||||
|
||||
Once the transaction's confirmed by the network, your three new MMGen addresses
|
||||
will appear on the listing of 'mmgen-txcreate -i'. Type 'm' at the menu to
|
||||
see them displayed in MMGen format.
|
||||
Once the transaction is broadcast to the network, you can view your three new
|
||||
MMGen addresses and their balances:
|
||||
|
||||
$ mmgen-tool listaddresses minconf=0
|
||||
ADDRESS COMMENT BALANCE
|
||||
89ABCDEF:2 Storage 1 3.3
|
||||
89ABCDEF:3 Storage 2 3.3
|
||||
89ABCDEF:4 Storage 3 3.39995
|
||||
|
||||
Your total MMGen balance will also now be visible:
|
||||
|
||||
$ mmgen-tool getbalance minconf=0
|
||||
Wallet Unconfirmed <0 confirms >=0 confirms
|
||||
89ABCDEF: 0 BTC 0 BTC 9.99995 BTC
|
||||
TOTAL: 0 BTC 0 BTC 9.99995 BTC
|
||||
|
||||
To verify that your transaction's received its first, second, third and so forth
|
||||
confirmation, increase the 'minconf' value to 1, 2, 3 and so forth.
|
||||
|
||||
Congratulations! You've performed your first MMGen transaction and placed your
|
||||
first funds under MMGen's control.
|
||||
first funds under MMGen control.
|
||||
|
||||
### <a name=10>Additional Features</a>
|
||||
|
||||
|
|
@ -295,9 +313,9 @@ first funds under MMGen's control.
|
|||
|
||||
Continuing our example above, generate a mnemonic from the wallet:
|
||||
|
||||
$ mmgen-walletchk -m '89ABCDEF-76543210[256,3].mmdat'
|
||||
$ mmgen-walletconv -o words '89ABCDEF-76543210[256,3].mmdat'
|
||||
...
|
||||
Mnemonic data saved to file '89ABCDEF.mmwords'
|
||||
Mnemonic data written to file '89ABCDEF.mmwords'
|
||||
|
||||
$ cat 89ABCDEF.mmwords
|
||||
pleasure tumble spider laughter many stumble secret bother after search
|
||||
|
|
@ -309,114 +327,139 @@ words. You may generate a wallet with these seed lengths using the '-l'
|
|||
option to 'mmgen-walletgen'.
|
||||
|
||||
Though some consider 128 bits of entropy to provide adequate security for the
|
||||
foreseeable future, you should stick to the default 256-bit seed length if
|
||||
you're not planning to use the mnemonic feature.
|
||||
foreseeable future, it's advisable to stick to the default 256-bit seed length
|
||||
if you're not planning to use the mnemonic feature.
|
||||
|
||||
NOTE: MMGen mnemonics are generated from the Electrum wordlist, but using
|
||||
ordinary base conversion instead of Electrum's more complicated algorithm.
|
||||
|
||||
Generate addresses 1-11 of seed 89ABCDEF using the mnemonic instead of the
|
||||
wallet:
|
||||
The mnemonic file may be used any place you'd use a MMGen wallet with the same
|
||||
Seed ID. You can generate ten addresses with it just as you did with the
|
||||
wallet, for example:
|
||||
|
||||
$ mmgen-addrgen 89ABCDEF.mmwords 1-11
|
||||
$ mmgen-addrgen 89ABCDEF.mmwords 1-10
|
||||
...
|
||||
Address data saved to file '89ABCDEF[1-11].addrs'
|
||||
Address data written to file '89ABCDEF[1-10].addrs'
|
||||
|
||||
Compare the first ten addresses with those earlier generated by the wallet.
|
||||
You'll see they're the same.
|
||||
The resulting address file will be identical to one generated by any wallet with
|
||||
Seed ID '89ABCDEF'.
|
||||
|
||||
Regenerate a lost wallet using the mnemonic:
|
||||
The mnemonic can be used to regenerate a lost wallet:
|
||||
|
||||
$ mmgen-walletgen 89ABCDEF.mmwords
|
||||
$ mmgen-walletconv 89ABCDEF.mmwords
|
||||
...
|
||||
Wallet saved to file '89ABCDEF-01234567[256,3].mmdat'
|
||||
MMGen wallet written to file '89ABCDEF-01234567[256,3].mmdat'
|
||||
|
||||
Note that the regenerated wallet has a different Key ID but of course the same
|
||||
Seed ID.
|
||||
|
||||
Seed files bear the extension '.mmseed' and are listed on the command line the
|
||||
same way mnemonic files are.
|
||||
Seed files bear the extension '.mmseed' and are generated and used exactly
|
||||
the same way as mnemonic files:
|
||||
|
||||
A seed file for a 256-bit seed looks like this:
|
||||
$ mmgen-walletconv -o seed '89ABCDEF-76543210[256,3].mmdat'
|
||||
...
|
||||
Seed data written to file '89ABCDEF.mmseed'
|
||||
|
||||
And they can also be used to regenerate a wallet:
|
||||
|
||||
$ mmgen-walletconv 89ABCDEF.mmseed
|
||||
...
|
||||
MMGen wallet written to file '89ABCDEF-23456701[256,3].mmdat'
|
||||
|
||||
Here's a sample seed file for a 256-bit wallet:
|
||||
|
||||
$ cat 8B7392ED.mmseed
|
||||
f4c84b C5ZT wWpT Jsoi wRVw 2dm9 Aftd WLb8 FggQ eC8h Szjd da9L
|
||||
|
||||
And for a 128-bit seed:
|
||||
And for a 128-bit wallet:
|
||||
|
||||
$ cat 8E0DFB78.mmseed
|
||||
0fe02f XnyC NfPH piuW dQ2d nM47 VU
|
||||
|
||||
As you can see, the latter file is short enough to be memorized or written down
|
||||
on a scrap of paper. From the unix command line, you can test your memory using
|
||||
the seed's checksum ("0fe02f" in this example) as follows:
|
||||
As you can see, seed files are short enough to be easily written out by hand or
|
||||
even memorized. And their built-in checksum makes it easy to test your memory
|
||||
using a simple Unix shell command:
|
||||
|
||||
$ echo -n XnyC NfPH piuW dQ2d nM47 VU | tr -d ' ' |sha256sum |cut -c 1-6
|
||||
$ echo -n XnyC NfPH piuW dQ2d nM47 VU | tr -d ' '| sha256sum | cut -c 1-6
|
||||
0fe02f
|
||||
|
||||
Or better yet, use 'mmgen-tool' to do the same thing:
|
||||
Or you can do the same thing with 'mmgen-tool':
|
||||
|
||||
$ mmgen-tool str2id6 'XnyC NfPH piuW dQ2d nM47 VU'
|
||||
0fe02f
|
||||
|
||||
#### <a name=12>Mnemonics and seeds — additional information:</a>
|
||||
|
||||
With the '-m' or '-s' option, MMGen commands that take mnemonic and seed
|
||||
data may receive the data from a prompt instead of a file.
|
||||
MMGen commands that take mnemonic and seed data may receive the data from a
|
||||
prompt instead of a file. Just omit the file name and specify the input format:
|
||||
|
||||
MMGen commands that produce mnemonic and seed data may be forced to print it to
|
||||
standard output instead of file with the '-S' option. This feature has
|
||||
intentionally been made optional to safeguard against looking-over-the-shoulder,
|
||||
Van Eyck phreaking and other side-channel attacks. MMGen commands never print
|
||||
private data to the screen unless explicitly asked to.
|
||||
$ mmgen-walletconv -i words
|
||||
...
|
||||
Enter mnemonic data: <type or paste your mnemonic here>
|
||||
|
||||
With the '-S' option, MMGen commands may be requested to print wallet data to
|
||||
screen instead of a file. To safeguard against over-the-shoulder, Van Eck
|
||||
phreaking and other side-channel attacks, you'll be prompted before this
|
||||
sensitive data is actually displayed. MMGen never prints unencrypted private
|
||||
data to screen by default.
|
||||
|
||||
The output of any MMGen command may be written to a directory of your choice
|
||||
using the '-d' option. For example, on a Linux system you can use
|
||||
'-d /dev/shm' to write key and seed data to volatile memory instead of disk.
|
||||
This also has obvious security benefits, ensuring that no sensitive data
|
||||
remains on disk after your computer's been powered down.
|
||||
'-d /dev/shm' to write keys and seeds to volatile memory instead of disk,
|
||||
ensuring that no trace of this sensitive data remains once your computer's been
|
||||
powered down.
|
||||
|
||||
#### <a name=13><a name=incog>Incognito wallets</a>
|
||||
|
||||
A wallet exported to incognito format is indistinguishable from random data,
|
||||
allowing you to hide your wallet at an offset within a random-filled file or
|
||||
partition. Thus both the location and nature of the data are unknown to a
|
||||
potential attacker, who in addition cannot be sure that the file or partition
|
||||
contains anything useful at all, barring any inside knowledge.
|
||||
An incognito format wallet is indistinguishable from random data, allowing you
|
||||
to hide your wallet at an offset within a random-data-filled file or partition.
|
||||
Barring any inside knowledge, a potential attacker has no way of knowing where
|
||||
the wallet is hidden, or whether the file or partition contains anything of
|
||||
interest at all for that matter.
|
||||
|
||||
An incognito wallet with a reasonably secure password could even be hidden on
|
||||
unencrypted cloud storage. Hiding your wallet at some offset in a 1 GB file
|
||||
increases the difficulty of any attack by a factor of one billion, assuming
|
||||
a potential attacker even knows or suspects you have an MMGen wallet hidden
|
||||
there.
|
||||
again that any potential attacker even knows or suspects you have an MMGen
|
||||
wallet hidden there.
|
||||
|
||||
If you plan to store your incognito wallet in an insecure location such as cloud
|
||||
storage, you're advised to use a strong scrypt preset and a strong password.
|
||||
These can be changed using the 'mmgen-passchg' utility:
|
||||
storage, you're advised to use a strong scrypt (hash) preset and a strong
|
||||
password. These can be changed using the 'mmgen-passchg' utility:
|
||||
|
||||
$ mmgen-passchg -p 5 89ABCDEF-01234567[256,3].mmdat
|
||||
...
|
||||
Hash preset has changed (3 -> 5)
|
||||
Enter new passphrase: <my new strong passphrase>
|
||||
Hash preset of wallet: '3'
|
||||
Enter old passphrase for MMGen wallet: <old weak passphrase>
|
||||
...
|
||||
Wallet saved to file '89ABCDEF-87654321[256,5].mmdat'
|
||||
|
||||
The new scrypt preset is indicated by the numeral '5' after the comma in the new
|
||||
wallet filename. Now export your new strengthened wallet to incognito format:
|
||||
|
||||
$ mmgen-walletchk -g 89ABCDEF-87654321[256,5].mmdat
|
||||
Hash preset changed to '5'
|
||||
Enter new passphrase for MMGen wallet: <new strong passphrase>
|
||||
...
|
||||
Incognito wallet data saved to file '89ABCDEF-87654321-ECA86420[256,5].mmincog'
|
||||
MMGen wallet written to file '89ABCDEF-87654321[256,5].mmdat'
|
||||
|
||||
'ECA86420' is the Incog ID. This can be used by the 'mmgen-tool' utility to
|
||||
search through a file or partition and locate your wallet if you've forgotten
|
||||
where you hid it (see below).
|
||||
The scrypt preset is the numeral in the wallet filename following the seed
|
||||
length. As you can see, it's now changed to '5'. Now export your new toughened
|
||||
wallet to incognito format, using the '-k' option to leave the passphrase
|
||||
unchanged:
|
||||
|
||||
Repeat the same export operation, but output to hexadecimal:
|
||||
|
||||
$ mmgen-walletchk -X 89ABCDEF-87654321[256,5].mmdat
|
||||
$ mmgen-walletconv -k -o incog 89ABCDEF-87654321[256,5].mmdat
|
||||
...
|
||||
Incognito wallet data saved to file '89ABCDEF-87654321-CA86420E[256,5].mmincox'
|
||||
Reusing passphrase at user request
|
||||
...
|
||||
New Incog Wallet ID: ECA86420
|
||||
...
|
||||
Incognito data written to file '89ABCDEF-87654321-ECA86420[256,5].mmincog'
|
||||
|
||||
Incog wallets have a special identifier, the Incog ID, which can be used to
|
||||
locate the wallet data if you've forgotten where you hid it (see the example
|
||||
below). Naturally, an attacker could use this ID to find the data too, so it
|
||||
should be kept secret.
|
||||
|
||||
Incog wallets can also be output to hexadecimal format:
|
||||
|
||||
$ mmgen-walletconv -k -o incox 89ABCDEF-87654321[256,5].mmdat
|
||||
...
|
||||
Hex incognito data written to file '89ABCDEF-87654321-CA86420E[256,5].mmincox'
|
||||
|
||||
$ cat 89ABCDEF-87654321-1EE402F4[256,5].mmincox
|
||||
6772 edb2 10cf ad0d c7dd 484b cc7e 42e9
|
||||
|
|
@ -424,72 +467,67 @@ Repeat the same export operation, but output to hexadecimal:
|
|||
3706 c5ce 56e0 7590 e677 6c6e 750a d057
|
||||
b43a 21f9 82c7 6bd1 fe96 bad9 2d54 c4c0
|
||||
|
||||
Note that the Incog ID is different here: it's generated from the init vector,
|
||||
Note that the Incog ID is different here: it's generated from an init vector,
|
||||
which is a different random number each time, making the incog data as a whole
|
||||
different as well. This allows you to store your incog data in multiple
|
||||
insecure locations without having repeated "random" wallet data give you away.
|
||||
public locations without having repeated "random" wallet data give you away.
|
||||
|
||||
As you can see, this data is ideally suited for a paper wallet. Just print it
|
||||
out on a printer and you're ready to go.
|
||||
Indistinguishable from any random hex dump, this data is ideally suited for a
|
||||
paper wallet that could potentially fall into the wrong hands.
|
||||
|
||||
Your incognito wallet (whether hex or binary) can be used just like any other
|
||||
MMGen wallet, mnemonic or seed file. Generate addresses with it like this:
|
||||
MMGen wallet, mnemonic or seed file to generate addresses and sign transactions:
|
||||
|
||||
$ mmgen-addrgen 89ABCDEF-87654321-CA86420E[256,5].mmincox 100-110
|
||||
$ mmgen-addrgen 89ABCDEF-87654321-CA86420E[256,5].mmincox 101-110
|
||||
...
|
||||
Generated 10 addresses
|
||||
Addresses written to file '89ABCDEF[100-110].addrs'
|
||||
|
||||
|
||||
Or sign a transaction like this:
|
||||
Addresses written to file '89ABCDEF[101-110].addrs'
|
||||
|
||||
$ mmgen-txsign tx_FABCDE[0.3].raw 89ABCDEF-87654321-CA86420E[256,5].mmincox
|
||||
...
|
||||
Signed transaction saved to file tx_FABCDE[0.3].sig
|
||||
Signed transaction written to file tx_FABCDE[0.3].sig
|
||||
|
||||
You can create an incognito wallet and hide it at a specified offset in a file or
|
||||
partition in one convenient operation using the '-G' ('--export-incog-hidden')
|
||||
option. Here's how you'd hide a wallet in a 1GB file filled with random data.
|
||||
First create the random file:
|
||||
##### <a name=13a><a name=incog>Hidden incognito wallets</a>
|
||||
|
||||
$ dd if=/dev/urandom of=random.dat bs=1K count=1M
|
||||
With the '-o hincog' option, incognito wallet data can be created and hidden at
|
||||
a specified offset in a file or partition in a single convenient operation, with
|
||||
the random file being created automatically if required. Here's how you'd
|
||||
create a 1GB file 'random.dat' and hide a wallet in it at offset 123456789:
|
||||
|
||||
Or better yet, use 'mmgen-tool' to do the same job but with some additional user
|
||||
entropy and a progress meter:
|
||||
|
||||
$ mmgen-tool -r40 rand2file random.dat 1G
|
||||
|
||||
Now export your wallet to hidden incognito format, hiding it in this 1GB random
|
||||
file at offset 123456789:
|
||||
|
||||
$ mmgen-walletchk -G random.dat,123456789 89ABCDEF-87654321[256,5].mmdat
|
||||
$ mmgen-walletconv -k -o hincog -J random.dat,123456789 89ABCDEF-87654321[256,5].mmdat
|
||||
...
|
||||
New Incog Wallet ID: ED1F2ACB
|
||||
...
|
||||
Requested file 'random.dat' does not exist. Create? (Y/n): Y
|
||||
Enter file size: 1G
|
||||
...
|
||||
Incog ID: ED1F2ACB
|
||||
Data written to file 'random.dat' at offset 123456789
|
||||
|
||||
The altered random file can now be uploaded to a cloud storage service, for
|
||||
example, or some other, preferably non-public, location on the Net (in a
|
||||
real-life situation you'll choose a less obvious offset than '123456789'
|
||||
though, won't you?).
|
||||
Your "random" file can now be uploaded to a cloud storage service, for example,
|
||||
or some other, preferably non-public, location on the Net (in a real-life
|
||||
situation you will choose a less obvious offset than '123456789' though, won't
|
||||
you?).
|
||||
|
||||
If at some point in the future you download this file to recover your wallet
|
||||
data but find you've forgotten the offset, you can recover it using the saved
|
||||
Incog ID as follows:
|
||||
Now let's say at some point in the future you download this file to recover
|
||||
your wallet and realize you've forgotten the offset where the data is hidden.
|
||||
If you've saved your Incog ID, you're in luck:
|
||||
|
||||
$ mmgen-tool find_incog_data random.dat ED1F2ACB
|
||||
...
|
||||
Incog data for ID ED1F2ACB found at offset 123456789
|
||||
|
||||
Hidden incog wallets are almost as convenient to use as ordinary ones.
|
||||
The search process can be slow, so patience is required. In addition, on
|
||||
large files 'false positives' are a distinct possibility, in which case you'll
|
||||
need to use the 'keep_searching=1' parameter to keep going until you find the
|
||||
real offset.
|
||||
|
||||
Hidden incog wallets are nearly as convenient to use as ordinary ones.
|
||||
Generating ten addresses with your hidden incog data is as easy as this:
|
||||
|
||||
$ mmgen-addrgen -G random.dat,123456789,256 32-42
|
||||
$ mmgen-addrgen -H random.dat,123456789 101-110
|
||||
|
||||
Use the same syntax to sign a transaction:
|
||||
Transaction signing uses the same syntax:
|
||||
|
||||
$ mmgen-txsign -G random.dat,123456789,256 tx_ABCDEE[0.1].raw
|
||||
$ mmgen-txsign -H random.dat,123456789 tx_ABCDEF[0.1].raw
|
||||
...
|
||||
Signed transaction saved to file tx_ABCDEE[0.1].sig
|
||||
|
||||
Note that the seed length parameter here will always be '256' unless you're
|
||||
using a non-default seed length.
|
||||
Signed transaction written to file 'tx_ABCDEF[0.1].sig'
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ from hashlib import new as hashlib_new
|
|||
from binascii import hexlify, unhexlify
|
||||
|
||||
from mmgen.bitcoin import numtowif
|
||||
# from mmgen.util import msg,qmsg,qmsg_r,make_chksum_N,get_lines_from_file,get_data_from_file,get_extension
|
||||
from mmgen.util import *
|
||||
from mmgen.tx import *
|
||||
from mmgen.obj import *
|
||||
|
|
@ -367,10 +366,9 @@ class AddrInfo(MMGenObject):
|
|||
|
||||
|
||||
def make_addrdata_chksum(self):
|
||||
nchars = 24
|
||||
lines=[" ".join([str(e.idx),e.addr]+([e.wif] if self.has_keys else []))
|
||||
for e in self.addrdata]
|
||||
self.checksum = make_chksum_N(" ".join(lines), nchars, sep=True)
|
||||
self.checksum = make_chksum_N(" ".join(lines), nchars=24, sep=True)
|
||||
|
||||
|
||||
def fmt_data(self,enable_comments=False):
|
||||
|
|
|
|||
250
mmgen/crypto.py
250
mmgen/crypto.py
|
|
@ -30,15 +30,6 @@ from mmgen.util import *
|
|||
from mmgen.term import get_char
|
||||
|
||||
crmsg = {
|
||||
'incog_iv_id': """
|
||||
Check that the generated Incog ID above is correct.
|
||||
If it's not, then your incognito data is incorrect or corrupted.
|
||||
""",
|
||||
'incog_iv_id_hidden': """
|
||||
Check that the generated Incog ID above is correct.
|
||||
If it's not, then your incognito data is incorrect or corrupted,
|
||||
or you've supplied an incorrect offset.
|
||||
""",
|
||||
'usr_rand_notice': """
|
||||
You've chosen to not fully trust your OS's random number generator and provide
|
||||
some additional entropy of your own. Please type %s symbols on your keyboard.
|
||||
|
|
@ -47,14 +38,23 @@ use both upper and lowercase as well as punctuation and numerals. What you
|
|||
type will not be displayed on the screen. Note that the timings between your
|
||||
keystrokes will also be used as a source of randomness.
|
||||
""",
|
||||
'incorrect_incog_passphrase_try_again': """
|
||||
Incorrect passphrase, hash preset, or maybe old-format incog wallet.
|
||||
Try again? (Y)es, (n)o, (m)ore information:
|
||||
""".strip(),
|
||||
'confirm_seed_id': """
|
||||
If the seed ID above is correct but you're seeing this message, then you need
|
||||
to exit and re-run the program with the '--old-incog-fmt' option.
|
||||
""".strip(),
|
||||
# 'incog_iv_id': """
|
||||
# Check that the generated Incog ID above is correct.
|
||||
# If it's not, then your incognito data is incorrect or corrupted.
|
||||
# """,
|
||||
# 'incog_iv_id_hidden': """
|
||||
# Check that the generated Incog ID above is correct.
|
||||
# If it's not, then your incognito data is incorrect or corrupted,
|
||||
# or you've supplied an incorrect offset.
|
||||
# """,
|
||||
# 'incorrect_incog_passphrase_try_again': """
|
||||
# Incorrect passphrase, hash preset, or maybe old-format incog wallet.
|
||||
# Try again? (Y)es, (n)o, (m)ore information:
|
||||
# """.strip(),
|
||||
# 'confirm_seed_id': """
|
||||
# If the seed ID above is correct but you're seeing this message, then you need
|
||||
# to exit and re-run the program with the '--old-incog-fmt' option.
|
||||
# """.strip(),
|
||||
}
|
||||
|
||||
def encrypt_seed(seed, key):
|
||||
|
|
@ -211,222 +211,6 @@ def get_random(length):
|
|||
return os_rand
|
||||
|
||||
|
||||
def get_seed_from_wallet(
|
||||
infile,
|
||||
desc="{pnm} wallet".format(pnm=g.proj_name),
|
||||
silent=False
|
||||
):
|
||||
|
||||
wdata = get_data_from_wallet(infile,silent=silent)
|
||||
label,metadata,hash_preset,salt,enc_seed = wdata
|
||||
|
||||
if opt.debug: display_control_data(*wdata)
|
||||
|
||||
padd = " "+infile if opt.quiet else ""
|
||||
passwd = get_mmgen_passphrase(desc+padd)
|
||||
|
||||
key = make_key(passwd, salt, hash_preset)
|
||||
|
||||
return decrypt_seed(enc_seed, key, metadata[0], metadata[1])
|
||||
|
||||
|
||||
def get_hidden_incog_data():
|
||||
# Already sanity-checked:
|
||||
fname,offset,seed_len = opt.from_incog_hidden.split(",")
|
||||
qmsg("Getting hidden incog data from file '%s'" % fname)
|
||||
|
||||
z = 0 if opt.old_incog_fmt else 8
|
||||
dlen = g.aesctr_iv_len + g.salt_len + (int(seed_len)/8) + z
|
||||
|
||||
fsize = check_data_fits_file_at_offset(fname,int(offset),dlen,"read")
|
||||
|
||||
import os
|
||||
f = os.open(fname,os.O_RDONLY)
|
||||
os.lseek(f, int(offset), os.SEEK_SET)
|
||||
data = os.read(f, dlen)
|
||||
os.close(f)
|
||||
qmsg("Data read from file '%s' at offset %s" % (fname,offset),
|
||||
"Data read from file")
|
||||
return data
|
||||
|
||||
def confirm_old_format():
|
||||
|
||||
while True:
|
||||
reply = get_char(
|
||||
crmsg['incorrect_incog_passphrase_try_again']+" ").strip("\n\r")
|
||||
if not reply: msg(""); return False
|
||||
elif reply in 'yY': msg(""); return False
|
||||
elif reply in 'nN': msg("\nExiting at user request"); sys.exit(1)
|
||||
elif reply in 'mM': msg(""); return True
|
||||
else:
|
||||
if opt.verbose: msg("\nInvalid reply")
|
||||
else: msg_r("\r")
|
||||
|
||||
|
||||
def get_seed_from_incog_wallet(
|
||||
infile,
|
||||
desc="{pnm} incognito wallet".format(pnm=g.proj_name),
|
||||
silent=False,
|
||||
hex_input=False
|
||||
):
|
||||
|
||||
desc = "incognito wallet data"
|
||||
|
||||
if opt.from_incog_hidden:
|
||||
d = get_hidden_incog_data()
|
||||
else:
|
||||
d = get_data_from_file(infile,desc)
|
||||
if hex_input:
|
||||
try:
|
||||
d = unhexlify("".join(d.split()).strip())
|
||||
except:
|
||||
msg("Data in file '%s' is not in hexadecimal format" % infile)
|
||||
sys.exit(2)
|
||||
# File could be of invalid length, so check:
|
||||
z = 0 if opt.old_incog_fmt else 8
|
||||
valid_dlens = [i/8 + g.aesctr_iv_len + g.salt_len + z for i in g.seed_lens]
|
||||
# New fmt: [56, 64, 72]. Old fmt: [48, 56, 64].
|
||||
if len(d) not in valid_dlens:
|
||||
vn = [i/8 + g.aesctr_iv_len + g.salt_len + 8 for i in g.seed_lens]
|
||||
if len(d) in vn:
|
||||
msg("Re-run the program without the '--old-incog-fmt' option")
|
||||
sys.exit()
|
||||
else: qmsg(
|
||||
"Invalid incognito file size: %s. Valid sizes (in bytes): %s" %
|
||||
(len(d), " ".join([str(i) for i in valid_dlens])))
|
||||
return False
|
||||
|
||||
iv, enc_incog_data = d[0:g.aesctr_iv_len], d[g.aesctr_iv_len:]
|
||||
|
||||
incog_id = make_iv_chksum(iv)
|
||||
msg("Incog ID: %s (IV ID: %s)" % (incog_id,make_chksum_8(iv)))
|
||||
qmsg("Check the applicable value against your records")
|
||||
vmsg(crmsg['incog_iv_id_hidden' if opt.from_incog_hidden
|
||||
else 'incog_iv_id'])
|
||||
|
||||
while True:
|
||||
passwd = get_mmgen_passphrase(desc+" "+incog_id)
|
||||
|
||||
qmsg("Configured hash presets: %s" % " ".join(sorted(g.hash_presets)))
|
||||
if 'hash_preset' in opt.set_by_user:
|
||||
hp = opt.hash_preset
|
||||
else:
|
||||
hp = get_hash_preset_from_user(desc="incog wallet")
|
||||
|
||||
# IV is used BOTH to initialize counter and to salt password!
|
||||
key = make_key(passwd, iv, hp, "wrapper key")
|
||||
d = decrypt_data(enc_incog_data, key, int(hexlify(iv),16), "incog data")
|
||||
|
||||
salt,enc_seed = d[0:g.salt_len], d[g.salt_len:]
|
||||
|
||||
key = make_key(passwd, salt, hp, "main key")
|
||||
vmsg("Key ID: %s" % make_chksum_8(key))
|
||||
|
||||
seed = decrypt_seed(enc_seed, key, "", "")
|
||||
old_fmt_sid = make_chksum_8(seed)
|
||||
|
||||
def confirm_correct_seed_id(sid):
|
||||
m = "Seed ID: %s. Is the seed ID correct?" % sid
|
||||
return keypress_confirm(m, True)
|
||||
|
||||
if opt.old_incog_fmt:
|
||||
if confirm_correct_seed_id(old_fmt_sid):
|
||||
break
|
||||
else:
|
||||
chk,seed_maybe = seed[:8],seed[8:]
|
||||
if sha256(seed_maybe).digest()[:8] == chk:
|
||||
msg("Passphrase and hash preset are correct")
|
||||
seed = seed_maybe
|
||||
break
|
||||
elif confirm_old_format():
|
||||
if confirm_correct_seed_id(old_fmt_sid):
|
||||
break
|
||||
|
||||
msg("Valid incog data for seed ID %s" % make_chksum_8(seed))
|
||||
return seed
|
||||
|
||||
|
||||
def _get_seed(infile,silent=False,seed_id=""):
|
||||
|
||||
ext = get_extension(infile)
|
||||
|
||||
if ext == g.mn_ext: source = "mnemonic"
|
||||
elif ext == g.brain_ext: source = "brainwallet"
|
||||
elif ext == g.seed_ext: source = "seed"
|
||||
elif ext == g.wallet_ext: source = "wallet"
|
||||
elif ext == g.incog_ext: source = "incognito wallet"
|
||||
elif ext == g.incog_hex_ext: source = "incognito wallet"
|
||||
elif opt.from_mnemonic : source = "mnemonic"
|
||||
elif opt.from_brain : source = "brainwallet"
|
||||
elif opt.from_seed : source = "seed"
|
||||
elif opt.from_incog : source = "incognito wallet"
|
||||
else:
|
||||
if infile: msg(
|
||||
"Invalid file extension for file: %s\nValid extensions: '.%s'" %
|
||||
(infile, "', '.".join(g.seedfile_exts)))
|
||||
else: msg("No seed source type specified and no file supplied")
|
||||
sys.exit(2)
|
||||
|
||||
seed_id_str = " for seed ID "+seed_id if seed_id else ""
|
||||
if source == "mnemonic":
|
||||
prompt = "Enter mnemonic%s: " % seed_id_str
|
||||
words = get_words(infile,"mnemonic data",prompt)
|
||||
wl = get_default_wordlist()
|
||||
from mmgen.mnemonic import get_seed_from_mnemonic
|
||||
seed = get_seed_from_mnemonic(words,wl)
|
||||
elif source == "brainwallet":
|
||||
if not opt.from_brain:
|
||||
msg("'--from-brain' parameters must be specified for brainwallet file")
|
||||
sys.exit(2)
|
||||
prompt = "Enter brainwallet passphrase%s: " % seed_id_str
|
||||
words = get_words(infile,"brainwallet data",prompt)
|
||||
seed = _get_seed_from_brain_passphrase(words)
|
||||
elif source == "seed":
|
||||
prompt = "Enter seed%s in %s format: " % (seed_id_str,g.seed_ext)
|
||||
words = get_words(infile,"seed data",prompt)
|
||||
seed = get_seed_from_seed_data(words)
|
||||
elif source == "wallet":
|
||||
seed = get_seed_from_wallet(infile, silent=silent)
|
||||
elif source == "incognito wallet":
|
||||
h = (ext == g.incog_hex_ext) or opt.from_incog_hex
|
||||
seed = get_seed_from_incog_wallet(infile, silent=silent, hex_input=h)
|
||||
|
||||
|
||||
if infile and not seed and (
|
||||
source == "seed" or source == "mnemonic" or source == "incognito wallet"):
|
||||
msg("Invalid %s file '%s'" % (source,infile))
|
||||
sys.exit(2)
|
||||
|
||||
dmsg("Seed: %s" % hexlify(seed))
|
||||
|
||||
return seed
|
||||
|
||||
|
||||
# Repeat if entered data is invalid
|
||||
def get_seed_retry(infile,seed_id=""):
|
||||
silent = False
|
||||
while True:
|
||||
seed = _get_seed(infile,silent=silent,seed_id=seed_id)
|
||||
silent = True
|
||||
if seed: return seed
|
||||
|
||||
|
||||
def _get_seed_from_brain_passphrase(words,silent=False):
|
||||
bp = " ".join(words)
|
||||
dmsg("Sanitized brain passphrase: %s" % bp)
|
||||
seed_len,hash_preset = get_from_brain_opt_params()
|
||||
dmsg("Brainwallet l = %s, p = %s" % (seed_len,hash_preset))
|
||||
vmsg_r("Hashing brainwallet data. Please wait...")
|
||||
# Use buflen arg of scrypt.hash() to get seed of desired length
|
||||
seed = scrypt_hash_passphrase(bp, "", hash_preset, buflen=seed_len/8)
|
||||
vmsg("Done")
|
||||
|
||||
if not silent:
|
||||
msg("Valid brainwallet for seed ID %s" % make_chksum_8(seed))
|
||||
|
||||
return seed
|
||||
|
||||
|
||||
# Vars for mmgen_*crypt functions only
|
||||
salt_len,sha256_len,nonce_len = 32,32,32
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ from mmgen.util import die,get_extension,check_infile
|
|||
|
||||
class Filename(MMGenObject):
|
||||
|
||||
def __init__(self,fn,ftype=None):
|
||||
def __init__(self,fn,ftype=None,write=False):
|
||||
self.name = fn
|
||||
self.dirname = os.path.dirname(fn)
|
||||
self.basename = os.path.basename(fn)
|
||||
|
|
@ -41,10 +41,17 @@ class Filename(MMGenObject):
|
|||
die(2,"Unrecognized extension '.%s' for file '%s'" % (self.ext,fn))
|
||||
|
||||
# TODO: Check for Windows
|
||||
mode = (os.O_RDONLY,os.O_RDWR)[int(write)]
|
||||
import stat
|
||||
if stat.S_ISBLK(os.stat(fn).st_mode):
|
||||
fd = os.open(fn, os.O_RDONLY)
|
||||
self.size = os.lseek(fd, 0, os.SEEK_END)
|
||||
os.close(fd)
|
||||
try:
|
||||
fd = os.open(fn, mode)
|
||||
except OSError as e:
|
||||
if e.errno == 13:
|
||||
die(2,"'%s': permission denied" % fn)
|
||||
# if e.errno != 17: raise
|
||||
else:
|
||||
self.size = os.lseek(fd, 0, os.SEEK_END)
|
||||
os.close(fd)
|
||||
else:
|
||||
self.size = os.stat(fn).st_size
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ prog_name = os.path.basename(sys.argv[0])
|
|||
author = "Philemon"
|
||||
email = "<mmgen-py@yandex.com>"
|
||||
Cdates = '2013-2015'
|
||||
version = '0.8.0'
|
||||
version = '0.8.1rc1'
|
||||
|
||||
required_opts = [
|
||||
"quiet","verbose","debug","outdir","echo_passphrase","passwd_file",
|
||||
|
|
|
|||
|
|
@ -22,14 +22,9 @@ main.py - Script launcher for the MMGen suite
|
|||
|
||||
def launch(what):
|
||||
|
||||
import os
|
||||
t = "MMGEN_USE_OLD_SCRIPTS"
|
||||
if not (t in os.environ and os.environ[t]):
|
||||
if what in ("walletgen","walletchk","passchg"):
|
||||
what = "wallet"
|
||||
|
||||
if what == "walletconv": what = "wallet"
|
||||
if what == "keygen": what = "addrgen"
|
||||
if what in ("walletgen","walletchk","walletconv","passchg"):
|
||||
what = "wallet"
|
||||
if what == "keygen": what = "addrgen"
|
||||
|
||||
try: import termios
|
||||
except: __import__("mmgen.main_" + what) # Windows
|
||||
|
|
|
|||
|
|
@ -28,48 +28,50 @@ import mmgen.opt as opt
|
|||
from mmgen.util import *
|
||||
from mmgen.crypto import *
|
||||
from mmgen.addr import *
|
||||
from mmgen.seed import SeedSource
|
||||
|
||||
if sys.argv[0].split("-")[-1] == "keygen":
|
||||
gen_what = "keys"
|
||||
opt_filter = None
|
||||
note1 = """
|
||||
By default, both addresses and secret keys are generated.
|
||||
""".strip()
|
||||
else:
|
||||
gen_what = "addresses"
|
||||
opt_filter = "hdceHKlpPqSvbgXGoms"
|
||||
opt_filter = "hcdeiHOKlpzPqSv"
|
||||
note1 = """
|
||||
If available, the external 'keyconv' program will be used for address
|
||||
generation.
|
||||
""".strip()
|
||||
|
||||
opts_data = {
|
||||
'sets': [('print_checksum',True,'quiet',True)],
|
||||
'desc': """Generate a range or list of {what} from an {pnm} wallet,
|
||||
mnemonic, seed or password""".format(what=gen_what,pnm=g.proj_name),
|
||||
'usage':"[opts] [infile] <address range or list>",
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
-A, --no-addresses Print only secret keys, no addresses
|
||||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-c, --save-checksum Save address list checksum to file
|
||||
-e, --echo-passphrase Echo passphrase or mnemonic to screen upon entry
|
||||
-H, --show-hash-presets Show information on available hash presets
|
||||
-K, --no-keyconv Force use of internal libraries for address gener-
|
||||
ation, even if 'keyconv' is available
|
||||
-l, --seed-len= N Length of seed. Options: {seed_lens}
|
||||
(default: {g.seed_len})
|
||||
-p, --hash-preset= p Use scrypt.hash() parameters from preset 'p' when
|
||||
hashing password (default: '{g.hash_preset}')
|
||||
-P, --passwd-file= f Get {pnm} wallet passphrase from file 'f'
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
-S, --stdout Print {what} to stdout
|
||||
-v, --verbose Produce more verbose output
|
||||
-x, --b16 Print secret keys in hexadecimal too
|
||||
|
||||
-b, --from-brain= l,p Generate {what} from a user-created password,
|
||||
i.e. a "brainwallet", using seed length 'l' and
|
||||
hash preset 'p' (comma-separated)
|
||||
-g, --from-incog Generate {what} from an incognito wallet
|
||||
-X, --from-incog-hex Generate {what} from incognito hexadecimal wallet
|
||||
-G, --from-incog-hidden=f,o,l Generate {what} from incognito data in file
|
||||
'f' at offset 'o', with seed length of 'l'
|
||||
-o, --old-incog-fmt Use old (pre-0.7.8) incog format
|
||||
-m, --from-mnemonic Generate {what} from an electrum-like mnemonic
|
||||
-s, --from-seed Generate {what} from a seed in .{g.seed_ext} format
|
||||
-h, --help Print this help message.
|
||||
-A, --no-addresses Print only secret keys, no addresses.
|
||||
-c, --print-checksum Print address list checksum and exit.
|
||||
-d, --outdir= d Output files to directory 'd' instead of working dir.
|
||||
-e, --echo-passphrase Echo passphrase or mnemonic to screen upon entry.
|
||||
-i, --in-fmt= f Input is from wallet format 'f' (see FMT CODES below).
|
||||
-H, --hidden-incog-input-params=f,o Read hidden incognito data from file
|
||||
'f' at offset 'o' (comma-separated).
|
||||
-O, --old-incog-fmt Specify old-format incognito input.
|
||||
-K, --no-keyconv Force use of internal libraries for address genera-
|
||||
tion, even if 'keyconv' is available.
|
||||
-l, --seed-len= l Specify wallet seed length of 'l' bits. This option
|
||||
is required only for brainwallet and incognito inputs
|
||||
with non-standard (< {g.seed_len}-bit) seed lengths.
|
||||
-p, --hash-preset= p Use the scrypt hash parameters defined by preset 'p'
|
||||
for password hashing (default: '{g.hash_preset}').
|
||||
-z, --show-hash-presets Show information on available hash presets.
|
||||
-P, --passwd-file= f Get wallet passphrase from file 'f'.
|
||||
-q, --quiet Produce quieter output; suppress some warnings.
|
||||
-S, --stdout Print {what} to stdout.
|
||||
-v, --verbose Produce more verbose output.
|
||||
-x, --b16 Print secret keys in hexadecimal too.
|
||||
""".format(
|
||||
seed_lens=", ".join([str(i) for i in g.seed_lens]),
|
||||
pnm=g.proj_name,
|
||||
|
|
@ -78,96 +80,63 @@ opts_data = {
|
|||
'notes': """
|
||||
|
||||
Addresses are given in a comma-separated list. Hyphen-separated ranges are
|
||||
also allowed.{a}
|
||||
also allowed.
|
||||
|
||||
If available, the external 'keyconv' program will be used for address
|
||||
generation.
|
||||
{n}
|
||||
|
||||
Data for the --from-<what> options will be taken from <infile> if <infile>
|
||||
is specified. Otherwise, the user will be prompted to enter the data.
|
||||
{o.pw_note}
|
||||
|
||||
For passphrases all combinations of whitespace are equal, and leading and
|
||||
trailing space are ignored. This permits reading passphrase data from a
|
||||
multi-line file with free spacing and indentation. This is particularly
|
||||
convenient for long brainwallet passphrases, for example.
|
||||
{o.bw_note}
|
||||
|
||||
BRAINWALLET NOTE:
|
||||
|
||||
As brainwallets require especially strong hashing to thwart dictionary
|
||||
attacks, the brainwallet hash preset must be specified by the user, using
|
||||
the 'p' parameter of the '--from-brain' option
|
||||
|
||||
The '--from-brain' option also requires the user to specify a seed length
|
||||
(the 'l' parameter)
|
||||
|
||||
For a brainwallet passphrase to always generate the same keys and addresses,
|
||||
the same 'l' and 'p' parameters to '--from-brain' must be used in all future
|
||||
invocations with that passphrase
|
||||
""".format(a="\n\nBy default, both addresses and secret keys are generated."
|
||||
if gen_what == "keys" else "")
|
||||
FMT CODES:
|
||||
{f}
|
||||
""".format(
|
||||
n=note1,
|
||||
f="\n ".join(SeedSource.format_fmt_codes().split("\n")),
|
||||
o=opt.opts
|
||||
)
|
||||
}
|
||||
|
||||
wmsg = {
|
||||
'unencrypted_secret_keys': """
|
||||
This program generates secret keys from your {pnm} seed, outputting them in
|
||||
UNENCRYPTED form. Generate only the key(s) you need and guard them carefully.
|
||||
""".format(pnm=g.proj_name),
|
||||
}
|
||||
|
||||
cmd_args = opt.opts.init(opts_data,add_opts=["b16"],opt_filter=opt_filter)
|
||||
|
||||
if opt.from_incog_hex or opt.from_incog_hidden: opt.from_incog = True
|
||||
nargs = 2
|
||||
if len(cmd_args) < nargs \
|
||||
and not opt.hidden_incog_input_params and not opt.in_fmt:
|
||||
opt.opts.usage()
|
||||
elif len(cmd_args) > nargs \
|
||||
or (len(cmd_args) == nargs and opt.hidden_incog_input_params):
|
||||
opt.opts.usage()
|
||||
|
||||
if len(cmd_args) == 1 and any([
|
||||
opt.from_mnemonic,opt.from_brain,opt.from_seed,opt.from_incog_hidden]):
|
||||
infile,addr_idx_arg = "",cmd_args[0]
|
||||
elif len(cmd_args) == 2:
|
||||
infile,addr_idx_arg = cmd_args
|
||||
check_infile(infile)
|
||||
else: opt.opts.usage()
|
||||
|
||||
addr_idxs = parse_addr_idxs(addr_idx_arg)
|
||||
|
||||
if not addr_idxs: sys.exit(2)
|
||||
addrlist_arg = cmd_args.pop()
|
||||
addr_idxs = parse_addr_idxs(addrlist_arg)
|
||||
if not addr_idxs:
|
||||
die(1,"'%s': invalid address list argument" % addrlist_arg)
|
||||
|
||||
do_license_msg()
|
||||
|
||||
# Interact with user:
|
||||
if gen_what == "keys" and not opt.quiet:
|
||||
confirm_or_exit(wmsg['unencrypted_secret_keys'], 'continue')
|
||||
opt.gen_what = "a" if gen_what == "addresses" \
|
||||
else "k" if opt.no_addresses else "ka"
|
||||
|
||||
# Generate data:
|
||||
ss = SeedSource(*cmd_args)
|
||||
|
||||
seed = get_seed_retry(infile)
|
||||
|
||||
opt.gen_what = "a" if gen_what == "addresses" else (
|
||||
"k" if opt.no_addresses else "ka")
|
||||
|
||||
ainfo = generate_addrs(seed,addr_idxs)
|
||||
ainfo = generate_addrs(ss.seed.data,addr_idxs)
|
||||
|
||||
addrdata_str = ainfo.fmt_data()
|
||||
outfile_base = "{}[{}]".format(make_chksum_8(seed), ainfo.idxs_fmt)
|
||||
outfile_base = "{}[{}]".format(ss.seed.sid, ainfo.idxs_fmt)
|
||||
|
||||
if 'a' in opt.gen_what and opt.save_checksum:
|
||||
w = "key-address" if 'k' in opt.gen_what else "address"
|
||||
write_to_file(outfile_base+"."+g.addrfile_chksum_ext,
|
||||
ainfo.checksum+"\n","%s data checksum" % w,True,True,False)
|
||||
if 'a' in opt.gen_what and opt.print_checksum:
|
||||
Die(0,ainfo.checksum)
|
||||
|
||||
if 'k' in opt.gen_what and keypress_confirm("Encrypt key list?"):
|
||||
addrdata_str = mmgen_encrypt(addrdata_str,"new key list","")
|
||||
enc_ext = "." + g.mmenc_ext
|
||||
else: enc_ext = ""
|
||||
|
||||
# Output data:
|
||||
if opt.stdout or not sys.stdout.isatty():
|
||||
if enc_ext and sys.stdout.isatty():
|
||||
msg("Cannot write encrypted data to screen. Exiting")
|
||||
sys.exit(2)
|
||||
write_to_stdout(addrdata_str,gen_what,ask_terminal=(gen_what=="keys"
|
||||
and not opt.quiet and sys.stdout.isatty()))
|
||||
else:
|
||||
outfile = "%s.%s%s" % (outfile_base, (
|
||||
g.keyaddrfile_ext if "ka" in opt.gen_what else (
|
||||
g.keyfile_ext if "k" in opt.gen_what else
|
||||
g.addrfile_ext)), enc_ext)
|
||||
write_to_file(outfile,addrdata_str,gen_what,not opt.quiet,True)
|
||||
ext = (g.keyfile_ext,g.keyaddrfile_ext)[int("ka" in opt.gen_what)]
|
||||
ext = (g.addrfile_ext,ext)[int("k" in opt.gen_what)]
|
||||
outfile = "%s.%s%s" % (outfile_base, ext, enc_ext)
|
||||
ask_tty = "k" in opt.gen_what and not opt.quiet
|
||||
if gen_what == "keys": gen_what = "secret keys"
|
||||
write_data_to_file(outfile,addrdata_str,gen_what,ask_tty=ask_tty)
|
||||
|
|
|
|||
|
|
@ -1,124 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-passchg: Change an MMGen deterministic wallet's passphrase, label or
|
||||
hash preset
|
||||
"""
|
||||
|
||||
import sys
|
||||
from mmgen.util import *
|
||||
from mmgen.crypto import *
|
||||
import mmgen.globalvars as g
|
||||
import mmgen.opt as opt
|
||||
|
||||
opts_data = {
|
||||
'desc': """Change the passphrase, hash preset or label of an {pnm}
|
||||
deterministic wallet""".format(pnm=g.proj_name),
|
||||
'usage': "[opts] [filename]",
|
||||
'options': """
|
||||
-h, --help Print this help message
|
||||
-d, --outdir= d Specify an alternate directory 'd' for output
|
||||
-H, --show-hash-presets Show information on available hash presets
|
||||
-k, --keep-old-passphrase Keep old passphrase (use when changing hash
|
||||
strength or label only)
|
||||
-L, --label= l Change the wallet's label to 'l'
|
||||
-p, --hash-preset= p Change scrypt.hash() parameters to preset 'p'
|
||||
(default: '{g.hash_preset}')
|
||||
-P, --passwd-file= f Get new {pnm} wallet passphrase from file 'f'
|
||||
-r, --usr-randchars= n Get 'n' characters of additional randomness from
|
||||
user (min={g.min_urandchars}, max={g.max_urandchars})
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
-v, --verbose Produce more verbose output
|
||||
""".format(g=g,pnm=g.proj_name),
|
||||
'notes': """
|
||||
|
||||
NOTE: The key ID will change if either the passphrase or hash preset are
|
||||
changed
|
||||
"""
|
||||
}
|
||||
|
||||
cmd_args = opt.opts.init(opts_data)
|
||||
|
||||
if len(cmd_args) != 1:
|
||||
msg("One input file must be specified")
|
||||
sys.exit(2)
|
||||
infile = cmd_args[0]
|
||||
|
||||
# Old key:
|
||||
label,metadata,hash_preset,salt,enc_seed = get_data_from_wallet(infile)
|
||||
seed_id,key_id = metadata[:2]
|
||||
|
||||
# Repeat on incorrect pw entry
|
||||
while True:
|
||||
desc = "{pnm} wallet".format(pnm=g.proj_name)
|
||||
passwd = get_mmgen_passphrase(desc,not opt.keep_old_passphrase)
|
||||
key = make_key(passwd, salt, hash_preset)
|
||||
seed = decrypt_seed(enc_seed, key, seed_id, key_id)
|
||||
if seed: break
|
||||
|
||||
changed = {}
|
||||
|
||||
if opt.label:
|
||||
if opt.label != label:
|
||||
msg("Label changed: '%s' -> '%s'" % (label, opt.label))
|
||||
changed['label'] = True
|
||||
else:
|
||||
msg("Label is unchanged: '%s'" % (label))
|
||||
else: opt.label = label # Copy the old label
|
||||
|
||||
if opt.hash_preset:
|
||||
if hash_preset != opt.hash_preset:
|
||||
qmsg("Hash preset changed: '%s' -> '%s'" %
|
||||
(hash_preset, opt.hash_preset))
|
||||
changed['preset'] = True
|
||||
else:
|
||||
msg("Hash preset is unchanged")
|
||||
else:
|
||||
opt.hash_preset = hash_preset
|
||||
|
||||
if opt.keep_old_passphrase:
|
||||
msg("Keeping old passphrase by user request")
|
||||
else:
|
||||
new_passwd = get_new_passphrase(
|
||||
"{pnm} wallet".format(pnm=g.proj_name), True)
|
||||
|
||||
if new_passwd == passwd:
|
||||
qmsg("Passphrase is unchanged")
|
||||
else:
|
||||
qmsg("Passphrase has changed")
|
||||
passwd = new_passwd
|
||||
changed['passwd'] = True
|
||||
|
||||
if 'preset' in changed or 'passwd' in changed: # Update key ID, salt
|
||||
qmsg("Will update salt and key ID")
|
||||
|
||||
from hashlib import sha256
|
||||
|
||||
salt = sha256(salt + get_random(128)).digest()[:g.salt_len]
|
||||
key = make_key(passwd, salt, opt.hash_preset)
|
||||
new_key_id = make_chksum_8(key)
|
||||
qmsg("Key ID changed: %s -> %s" % (key_id,new_key_id))
|
||||
key_id = new_key_id
|
||||
enc_seed = encrypt_seed(seed, key)
|
||||
elif not 'label' in changed:
|
||||
msg("Data unchanged. No file will be written")
|
||||
sys.exit(2)
|
||||
|
||||
write_wallet_to_file(seed, passwd, key_id, salt, enc_seed)
|
||||
|
|
@ -26,44 +26,50 @@ import mmgen.globalvars as g
|
|||
import mmgen.opt as opt
|
||||
from mmgen.tx import *
|
||||
from mmgen.util import do_license_msg,dmsg
|
||||
from mmgen.seed import SeedSource
|
||||
|
||||
|
||||
pnm = g.proj_name
|
||||
pnl = pnm.lower()
|
||||
|
||||
opts_data = {
|
||||
'desc': "Sign Bitcoin transactions generated by {pnl}-txcreate".format(pnl=pnl),
|
||||
'usage': "[opts] <transaction file> .. [mmgen wallet/seed/words/brainwallet file] ..",
|
||||
'usage': "[opts] <transaction file>... [seed source]...",
|
||||
'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
|
||||
-i, --info Display information about the transaction and exit
|
||||
-t, --terse-info Like '--info', but produce more concise output
|
||||
-I, --tx-id Display transaction ID and exit
|
||||
-k, --keys-from-file= f Provide additional keys for non-{pnm} addresses
|
||||
-K, --no-keyconv Force use of internal libraries for address gener-
|
||||
ation, even if 'keyconv' is available
|
||||
-M, --mmgen-keys-from-file=f Provide keys for {pnm} addresses in a key-
|
||||
address file (output of '{pnl}-keygen'). Permits
|
||||
online signing without an {pnm} seed source.
|
||||
The key-address file is also used to verify
|
||||
{pnm}-to-BTC mappings, so its checksum should
|
||||
be recorded by the user.
|
||||
-P, --passwd-file= f Get {pnm} wallet or bitcoind passphrase from file 'f'
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
-v, --verbose Produce more verbose output
|
||||
-b, --from-brain= l,p Generate keys from a user-created password,
|
||||
i.e. a "brainwallet", using seed length 'l' and
|
||||
hash preset 'p'
|
||||
-w, --use-wallet-dat Get keys from a running bitcoind
|
||||
-g, --from-incog Generate keys from an incognito wallet
|
||||
-X, --from-incog-hex Generate keys from an incognito hexadecimal wallet
|
||||
-G, --from-incog-hidden= f,o,l Generate keys from incognito data in file
|
||||
'f' at offset 'o', with seed length of 'l'
|
||||
-o, --old-incog-fmt Use old (pre-0.7.8) incog format
|
||||
-m, --from-mnemonic Generate keys from an electrum-like mnemonic
|
||||
-s, --from-seed Generate keys from a seed in .{g.seed_ext} format
|
||||
-h, --help Print this help message.
|
||||
-b, --brain-params=l,p Use seed length 'l' and hash preset 'p' for brain-
|
||||
wallet input. Required only if these parameters dif-
|
||||
fer from those of an incognito wallet also being used
|
||||
as a seed source.
|
||||
-d, --outdir= d Specify an alternate directory 'd' for output.
|
||||
-D, --tx-id Display transaction ID and exit.
|
||||
-e, --echo-passphrase Print passphrase to screen when typing it.
|
||||
-i, --in-fmt= f Input is from wallet format 'f' (see FMT CODES below).
|
||||
-H, --hidden-incog-input-params=f,o Read hidden incognito data from file
|
||||
'f' at offset 'o' (comma-separated).
|
||||
-O, --old-incog-fmt Specify old-format incognito input.
|
||||
-l, --seed-len= l Specify wallet seed length of 'l' bits. This option
|
||||
is required only for brainwallet and incognito inputs
|
||||
with non-standard (< {g.seed_len}-bit) seed lengths.
|
||||
-p, --hash-preset=p Use the scrypt hash parameters defined by preset 'p'
|
||||
for password hashing (default: '{g.hash_preset}').
|
||||
-z, --show-hash-presets Show information on available hash presets.
|
||||
-k, --keys-from-file=f Provide additional keys for non-{pnm} addresses
|
||||
-K, --no-keyconv Force use of internal libraries for address gener-
|
||||
ation, even if 'keyconv' is available.
|
||||
-M, --mmgen-keys-from-file=f Provide keys for {pnm} addresses in a key-
|
||||
address file (output of '{pnl}-keygen'). Permits
|
||||
online signing without an {pnm} seed source.
|
||||
The key-address file is also used to verify
|
||||
{pnm}-to-BTC mappings, so its checksum should
|
||||
be recorded by the user.
|
||||
-P, --passwd-file= f Get {pnm} wallet or bitcoind passphrase from file 'f'
|
||||
-q, --quiet Suppress warnings; overwrite files without
|
||||
prompting
|
||||
-I, --info Display information about the transaction and exit.
|
||||
-t, --terse-info Like '--info', but produce more concise output.
|
||||
-v, --verbose Produce more verbose output
|
||||
-w, --use-wallet-dat Get keys from a running bitcoind
|
||||
""".format(g=g,pnm=pnm,pnl=pnl),
|
||||
'notes': """
|
||||
|
||||
|
|
@ -91,7 +97,13 @@ Seed data supplied in files must have the following extensions:
|
|||
seed: '.{g.seed_ext}'
|
||||
mnemonic: '.{g.mn_ext}'
|
||||
brainwallet: '.{g.brain_ext}'
|
||||
""".format(g=g,pnm=pnm,pnl=pnl)
|
||||
|
||||
FMT CODES:
|
||||
{f}
|
||||
""".format(
|
||||
f="\n ".join(SeedSource.format_fmt_codes().split("\n")),
|
||||
g=g,pnm=pnm,pnl=pnl
|
||||
)
|
||||
}
|
||||
|
||||
wmsg = {
|
||||
|
|
@ -110,23 +122,20 @@ def get_seed_for_seed_id(seed_id,infiles,saved_seeds):
|
|||
if seed_id in saved_seeds.keys():
|
||||
return saved_seeds[seed_id]
|
||||
|
||||
from mmgen.crypto import get_seed_retry
|
||||
|
||||
while True:
|
||||
if infiles:
|
||||
seed = get_seed_retry(infiles.pop(0))
|
||||
elif any([opt.from_brain,opt.from_mnemonic,opt.from_seed,opt.from_incog]):
|
||||
ss = SeedSource(infiles.pop(0),ignore_in_fmt=True)
|
||||
elif opt.in_fmt:
|
||||
qmsg("Need seed data for seed ID %s" % seed_id)
|
||||
seed = get_seed_retry("",seed_id)
|
||||
ss = SeedSource()
|
||||
msg("User input produced seed ID %s" % make_chksum_8(seed))
|
||||
else:
|
||||
msg("ERROR: No seed source found for seed ID: %s" % seed_id)
|
||||
sys.exit(2)
|
||||
|
||||
sid = make_chksum_8(seed)
|
||||
saved_seeds[sid] = seed
|
||||
saved_seeds[ss.seed.sid] = ss.seed.data
|
||||
|
||||
if sid == seed_id: return seed
|
||||
if ss.seed.sid == seed_id: return ss.seed.data
|
||||
|
||||
|
||||
def get_keys_for_mmgen_addrs(mmgen_addrs,infiles,saved_seeds):
|
||||
|
|
@ -285,7 +294,7 @@ def get_keys_from_keylist(kldata,other_addrs):
|
|||
|
||||
infiles = opt.opts.init(opts_data,add_opts=["b16"])
|
||||
|
||||
if opt.from_incog_hex or opt.from_incog_hidden: opt.from_incog = True
|
||||
#if opt.from_incog_hex or opt.from_incog_hidden: opt.from_incog = True
|
||||
|
||||
if not infiles: opt.opts.usage()
|
||||
for i in infiles: check_infile(i)
|
||||
|
|
|
|||
|
|
@ -33,10 +33,12 @@ usage = "[opts] [infile]"
|
|||
nargs = 1
|
||||
iaction = "convert"
|
||||
oaction = "convert"
|
||||
bw_note = opt.opts.bw_note
|
||||
pw_note = opt.opts.pw_note
|
||||
|
||||
if invoked_as == "gen":
|
||||
desc = "Generate an {pnm} wallet from a random seed"
|
||||
opt_filter = "hdoJlLpPqrSv"
|
||||
opt_filter = "ehdoJlLpPqrSvz"
|
||||
usage = "[opts]"
|
||||
oaction = "output"
|
||||
nargs = 0
|
||||
|
|
@ -45,21 +47,25 @@ elif invoked_as == "conv":
|
|||
opt_filter = None
|
||||
elif invoked_as == "chk":
|
||||
desc = "Check validity of an {pnm} wallet"
|
||||
opt_filter = "hiHOlpPqrv"
|
||||
opt_filter = "ehiHOlpPqrvz"
|
||||
iaction = "input"
|
||||
elif invoked_as == "passchg":
|
||||
desc = "Change the password, hash preset or label of an {pnm} wallet"
|
||||
opt_filter = "hdiHkKOlLmpPqrSv"
|
||||
opt_filter = "ehdiHkKOlLmpPqrSvz"
|
||||
iaction = "input"
|
||||
bw_note = ""
|
||||
else:
|
||||
die(1,"'%s': unrecognized invocation" % bn)
|
||||
|
||||
opts_data = {
|
||||
# Can't use: share/Opts doesn't know anything about fmt codes
|
||||
# 'sets': [('hidden_incog_output_params',bool,'out_fmt','hi')],
|
||||
'desc': desc.format(pnm=g.proj_name),
|
||||
'usage': usage,
|
||||
'options': """
|
||||
-h, --help Print this help message.
|
||||
-d, --outdir= d Output files to directory 'd' instead of working dir.
|
||||
-e, --echo-passphrase Echo passphrases and other user input to screen.
|
||||
-i, --in-fmt= f {iaction} from wallet format 'f' (see FMT CODES below).
|
||||
-o, --out-fmt= f {oaction} to wallet format 'f' (see FMT CODES below).
|
||||
-H, --hidden-incog-input-params=f,o Read hidden incognito data from file
|
||||
|
|
@ -78,20 +84,28 @@ opts_data = {
|
|||
-m, --keep-label Reuse label of input wallet for output wallet.
|
||||
-p, --hash-preset= p Use the scrypt hash parameters defined by preset 'p'
|
||||
for password hashing (default: '{g.hash_preset}').
|
||||
-P, --passwd-file= f Get wallet passphrase from file 'f'
|
||||
-z, --show-hash-presets Show information on available hash presets.
|
||||
-P, --passwd-file= f Get wallet passphrase from file 'f'.
|
||||
-q, --quiet Produce quieter output; suppress some warnings.
|
||||
-r, --usr-randchars=n Get 'n' characters of additional randomness from user
|
||||
(min={g.min_urandchars}, max={g.max_urandchars}, default={g.usr_randchars}).
|
||||
-S, --stdout Write wallet data to stdout instead of file.
|
||||
-v, --verbose Produce more verbose output.
|
||||
|
||||
FMT CODES:
|
||||
{f}
|
||||
""".format(
|
||||
g=g,
|
||||
iaction=capfirst(iaction),
|
||||
oaction=capfirst(oaction),
|
||||
f="\n ".join(SeedSource.format_fmt_codes().split("\n"))
|
||||
),
|
||||
'notes': """
|
||||
|
||||
{pw_note}{bw_note}
|
||||
|
||||
FMT CODES:
|
||||
{f}
|
||||
""".format(
|
||||
f="\n ".join(SeedSource.format_fmt_codes().split("\n")),
|
||||
pw_note=pw_note,
|
||||
bw_note=("","\n\n" + bw_note)[int(bool(bw_note))]
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,167 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-walletchk: Check integrity of an MMGen deterministic wallet, display
|
||||
information about it and export it to various formats
|
||||
"""
|
||||
|
||||
import sys
|
||||
import mmgen.globalvars as g
|
||||
import mmgen.opt as opt
|
||||
from mmgen.util import *
|
||||
from mmgen.crypto import *
|
||||
|
||||
opts_data = {
|
||||
'desc': """Check integrity of an {pnm} deterministic wallet, display
|
||||
its information, and export seed and mnemonic data.
|
||||
""".format(pnm=g.proj_name),
|
||||
'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
|
||||
-P, --passwd-file= f Get {pnm} wallet passphrase from file 'f'
|
||||
-q, --quiet Suppress warnings; overwrite files without prompting
|
||||
-r, --usr-randchars= n Get 'n' characters of additional randomness from
|
||||
user (min={g.min_urandchars}, max={g.max_urandchars})
|
||||
-S, --stdout Print seed or mnemonic data to standard output
|
||||
-v, --verbose Produce more verbose output
|
||||
-g, --export-incog Export wallet to incognito format
|
||||
-X, --export-incog-hex Export wallet to incognito hexadecimal format
|
||||
-G, --export-incog-hidden=f,o Hide incognito data in existing file 'f'
|
||||
at offset 'o' (comma-separated)
|
||||
-o, --old-incog-fmt Use old (pre-0.7.8) incog format
|
||||
-m, --export-mnemonic Export the wallet's mnemonic to file
|
||||
-s, --export-seed Export the wallet's seed to file
|
||||
""".format(g=g,pnm=g.proj_name),
|
||||
'notes': """
|
||||
|
||||
Since good randomness is particularly important for incognito wallets,
|
||||
the '--usr-randchars' option is turned on by default to gather additional
|
||||
entropy from the user when one of the '--export-incog*' options is
|
||||
selected. If you fully trust your OS's random number generator and wish
|
||||
to disable this option, then specify '-r0' on the command line.
|
||||
"""
|
||||
}
|
||||
|
||||
def wallet_to_incog_data(infile):
|
||||
|
||||
d = get_data_from_wallet(infile,silent=True)
|
||||
seed_id,key_id,preset,salt,enc_seed = \
|
||||
d[1][0], d[1][1], d[2].split(":")[0], d[3], d[4]
|
||||
|
||||
while True:
|
||||
passwd = get_mmgen_passphrase("{pnm} wallet".format(pnm=g.proj_name))
|
||||
key = make_key(passwd, salt, preset, "main key")
|
||||
seed = decrypt_seed(enc_seed, key, seed_id, key_id)
|
||||
if seed: break
|
||||
|
||||
iv = get_random(g.aesctr_iv_len)
|
||||
iv_id = make_iv_chksum(iv)
|
||||
msg("Incog ID: %s" % iv_id)
|
||||
|
||||
if not opt.old_incog_fmt:
|
||||
salt = get_random(g.salt_len)
|
||||
key = make_key(passwd, salt, preset, "incog wallet key")
|
||||
key_id = make_chksum_8(key)
|
||||
from hashlib import sha256
|
||||
chk = sha256(seed).digest()[:8]
|
||||
enc_seed = encrypt_data(chk+seed, key, 1, "seed")
|
||||
|
||||
# IV is used BOTH to initialize counter and to salt password!
|
||||
key = make_key(passwd, iv, preset, "incog wrapper key")
|
||||
wrap_enc = encrypt_data(salt+enc_seed,key,int(hexlify(iv),16),"incog data")
|
||||
|
||||
return iv+wrap_enc,seed_id,key_id,iv_id,preset
|
||||
|
||||
|
||||
def export_to_hidden_incog(incog_enc):
|
||||
outfile,offset = opt.export_incog_hidden.split(",") #Already sanity-checked
|
||||
if opt.outdir: outfile = make_full_path(opt.outdir,outfile)
|
||||
|
||||
dmsg("Incog data len %s, offset %s" % (len(incog_enc),offset))
|
||||
check_data_fits_file_at_offset(outfile,int(offset),len(incog_enc),"write")
|
||||
|
||||
if not opt.quiet: confirm_or_exit("","alter file '%s'" % outfile)
|
||||
import os
|
||||
f = os.open(outfile,os.O_RDWR)
|
||||
os.lseek(f, int(offset), os.SEEK_SET)
|
||||
os.write(f, incog_enc)
|
||||
os.close(f)
|
||||
msg("Data written to file '%s' at offset %s" %
|
||||
(os.path.relpath(outfile),offset))
|
||||
|
||||
|
||||
cmd_args = opt.opts.init(opts_data)
|
||||
|
||||
if opt.export_incog_hidden or opt.export_incog_hex:
|
||||
opt.export_incog = True
|
||||
|
||||
if len(cmd_args) != 1: opt.opts.usage()
|
||||
|
||||
check_infile(cmd_args[0])
|
||||
|
||||
if opt.outdir and opt.export_incog_hidden:
|
||||
msg("Warning: '--outdir' option is ignored when exporting hidden incog data")
|
||||
|
||||
g.use_urandchars = True
|
||||
|
||||
if opt.export_mnemonic:
|
||||
qmsg("Exporting mnemonic data to file by user request")
|
||||
elif opt.export_seed:
|
||||
qmsg("Exporting seed data to file by user request")
|
||||
elif opt.export_incog:
|
||||
qmsg("Exporting wallet to incognito format by user request")
|
||||
incog_enc,seed_id,key_id,iv_id,preset = \
|
||||
wallet_to_incog_data(cmd_args[0])
|
||||
|
||||
if opt.export_incog_hidden:
|
||||
export_to_hidden_incog(incog_enc)
|
||||
else:
|
||||
z = 0 if opt.old_incog_fmt else 8
|
||||
seed_len = (len(incog_enc)-g.salt_len-g.aesctr_iv_len-z)*8
|
||||
fn = "%s-%s-%s[%s,%s].%s" % (
|
||||
seed_id, key_id, iv_id, seed_len, preset,
|
||||
g.incog_hex_ext if opt.export_incog_hex else g.incog_ext
|
||||
)
|
||||
data = pretty_hexdump(incog_enc,2,8,line_nums=False) \
|
||||
if opt.export_incog_hex else incog_enc
|
||||
write_to_file_or_stdout(fn, data, "incognito wallet data")
|
||||
|
||||
sys.exit()
|
||||
|
||||
seed = get_seed_retry(cmd_args[0])
|
||||
if seed: msg("Wallet is OK")
|
||||
else:
|
||||
msg("Error opening wallet")
|
||||
sys.exit(2)
|
||||
|
||||
if opt.export_mnemonic:
|
||||
wl = get_default_wordlist()
|
||||
from mmgen.mnemonic import get_mnemonic_from_seed
|
||||
mn = get_mnemonic_from_seed(seed, wl, g.default_wordlist, opt.debug)
|
||||
fn = "%s.%s" % (make_chksum_8(seed).upper(), g.mn_ext)
|
||||
write_to_file_or_stdout(fn, " ".join(mn)+"\n", "mnemonic data")
|
||||
|
||||
elif opt.export_seed:
|
||||
from mmgen.bitcoin import b58encode_pad
|
||||
data = split_into_cols(4,b58encode_pad(seed))
|
||||
chk = make_chksum_6(b58encode_pad(seed))
|
||||
fn = "%s.%s" % (make_chksum_8(seed).upper(), g.seed_ext)
|
||||
write_to_file_or_stdout(fn, "%s %s\n" % (chk,data), "seed data")
|
||||
|
|
@ -1,172 +0,0 @@
|
|||
#!/usr/bin/env python
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
|
||||
# Copyright (C)2013-2015 Philemon <mmgen-py@yandex.com>
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
mmgen-walletgen: Generate an MMGen deterministic wallet
|
||||
"""
|
||||
|
||||
import sys, os
|
||||
from hashlib import sha256
|
||||
|
||||
import mmgen.globalvars as g
|
||||
import mmgen.opt as opt
|
||||
from mmgen.util import *
|
||||
from mmgen.crypto import *
|
||||
|
||||
pnm = g.proj_name
|
||||
|
||||
opts_data = {
|
||||
'desc': "Generate an {pnm} deterministic wallet".format(pnm=pnm),
|
||||
'usage': "[opts] [infile]",
|
||||
'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
|
||||
-H, --show-hash-presets Show information on available hash presets
|
||||
-l, --seed-len= n Create seed of length 'n'. Options: {seed_lens}
|
||||
(default: {g.seed_len})
|
||||
-L, --label= l Label to identify this wallet (32 chars max.
|
||||
Allowed symbols: A-Z, a-z, 0-9, " ", "_", ".")
|
||||
-p, --hash-preset= p Use scrypt.hash() parameters from preset 'p'
|
||||
(default: '{g.hash_preset}')
|
||||
-P, --passwd-file= f Get {pnm} wallet passphrase from file 'f'
|
||||
-q, --quiet Produce quieter output; overwrite files without
|
||||
prompting
|
||||
-r, --usr-randchars= n Get 'n' characters of additional randomness from
|
||||
user (min={g.min_urandchars}, max={g.max_urandchars})
|
||||
-v, --verbose Produce more verbose output
|
||||
|
||||
-b, --from-brain= l,p Generate wallet from a user-created passphrase,
|
||||
i.e. a "brainwallet", using seed length 'l' and
|
||||
hash preset 'p' (comma-separated)
|
||||
-g, --from-incog Generate wallet from an incognito-format wallet
|
||||
-G, --from-incog-hidden= f,o,l Generate keys from incognito data in file
|
||||
'f' at offset 'o', with seed length of 'l'
|
||||
-o, --old-incog-fmt Use old (pre-0.7.8) incog format
|
||||
-m, --from-mnemonic Generate wallet from an Electrum-like mnemonic
|
||||
-s, --from-seed Generate wallet from a seed in .{g.seed_ext} format
|
||||
""".format(seed_lens=",".join([str(i) for i in g.seed_lens]),g=g,pnm=pnm),
|
||||
'notes': """
|
||||
|
||||
By default (i.e. when invoked without any of the '--from-<what>' options),
|
||||
{g.prog_name} generates a wallet based on a random seed.
|
||||
|
||||
Data for the --from-<what> options will be taken from <infile> if <infile>
|
||||
is specified. Otherwise, the user will be prompted to enter the data.
|
||||
|
||||
For passphrases all combinations of whitespace are equal, and leading and
|
||||
trailing space are ignored. This permits reading passphrase data from a
|
||||
multi-line file with free spacing and indentation. This is particularly
|
||||
convenient for long brainwallet passphrases, for example.
|
||||
|
||||
Since good randomness is particularly important when generating wallets,
|
||||
the '--usr-randchars' option is turned on by default to gather additional
|
||||
entropy from the user. If you fully trust your OS's random number gener-
|
||||
ator and wish to disable this option, specify '-r0' on the command line.
|
||||
|
||||
BRAINWALLET NOTE:
|
||||
|
||||
As brainwallets require especially strong hashing to thwart dictionary
|
||||
attacks, the brainwallet hash preset must be specified by the user, using
|
||||
the 'p' parameter of the '--from-brain' option. This preset should be
|
||||
stronger than the one used for hashing the seed (i.e. the default value or
|
||||
the one specified in the '--hash-preset' option).
|
||||
|
||||
The '--from-brain' option also requires the user to specify a seed length
|
||||
(the 'l' parameter), which overrides both the default and any one given in
|
||||
the '--seed-len' option.
|
||||
|
||||
For a brainwallet passphrase to always generate the same keys and
|
||||
addresses, the same 'l' and 'p' parameters to '--from-brain' must be used
|
||||
in all future invocations with that passphrase.
|
||||
""".format(g=g)
|
||||
}
|
||||
|
||||
wmsg = {
|
||||
'choose_wallet_passphrase': """
|
||||
You must choose a passphrase to encrypt the wallet with. A key will be
|
||||
generated from your passphrase using a hash preset of '%s'. Please note that
|
||||
no strength checking of passphrases is performed. For an empty passphrase,
|
||||
just hit ENTER twice.
|
||||
""".strip(),
|
||||
'brain_warning': """
|
||||
############################## EXPERTS ONLY! ##############################
|
||||
|
||||
A brainwallet will be secure only if you really know what you're doing and
|
||||
have put much care into its creation. The creators of {pnm} assume no
|
||||
responsibility for coins stolen as a result of a poorly crafted brainwallet
|
||||
passphrase.
|
||||
|
||||
A key will be generated from your passphrase using the parameters requested
|
||||
by you: seed length {}, hash preset '{}'. For brainwallets it's highly
|
||||
recommended to use one of the higher-numbered presets.
|
||||
|
||||
Remember the seed length and hash preset parameters you've specified. To
|
||||
generate the correct keys/addresses associated with this passphrase in the
|
||||
future, you must continue using these same parameters.
|
||||
""",
|
||||
}
|
||||
|
||||
import mmgen.opt as opt
|
||||
cmd_args = opt.opts.init(opts_data)
|
||||
|
||||
if len(cmd_args) == 1:
|
||||
infile = cmd_args[0]
|
||||
check_infile(infile)
|
||||
ext = infile.split(".")[-1]
|
||||
ok_exts = g.seedfile_exts
|
||||
for e in ok_exts:
|
||||
if e == ext: break
|
||||
else:
|
||||
msg(
|
||||
"Input file must have one of the following extensions: .%s" % ", .".join(ok_exts))
|
||||
sys.exit(1)
|
||||
elif len(cmd_args) == 0:
|
||||
infile = ""
|
||||
else: opt.opts.usage()
|
||||
|
||||
g.use_urandchars = True
|
||||
|
||||
# Begin execution
|
||||
|
||||
do_license_msg()
|
||||
|
||||
if opt.from_brain and not opt.quiet:
|
||||
confirm_or_exit(wmsg['brain_warning'].format(
|
||||
pnm=pnm, *get_from_brain_opt_params()),
|
||||
"continue")
|
||||
|
||||
if infile or any([
|
||||
opt.from_mnemonic,opt.from_brain,opt.from_seed,opt.from_incog]):
|
||||
seed = get_seed_retry(infile)
|
||||
qmsg("")
|
||||
else:
|
||||
# Truncate random data for smaller seed lengths
|
||||
seed = sha256(get_random(128)).digest()[:opt.seed_len/8]
|
||||
|
||||
salt = sha256(get_random(128)).digest()[:g.salt_len]
|
||||
|
||||
qmsg(wmsg['choose_wallet_passphrase'] % opt.hash_preset)
|
||||
|
||||
passwd = get_new_passphrase("new {pnm} wallet".format(pnm=pnm))
|
||||
|
||||
key = make_key(passwd, salt, opt.hash_preset)
|
||||
|
||||
enc_seed = encrypt_seed(seed, key)
|
||||
|
||||
write_wallet_to_file(seed,passwd,make_chksum_8(key),salt,enc_seed)
|
||||
|
|
@ -26,6 +26,20 @@ import mmgen.share.Opts
|
|||
import opt
|
||||
from mmgen.util import msg,msg_r,mdie,mmsg,Msg,die,is_mmgen_wallet_label
|
||||
|
||||
pw_note = """
|
||||
For passphrases all combinations of whitespace are equal and leading and
|
||||
trailing space is ignored. This permits reading passphrase or brainwallet
|
||||
data from a multi-line file with free spacing and indentation.
|
||||
""".strip()
|
||||
|
||||
bw_note = """
|
||||
BRAINWALLET NOTE:
|
||||
|
||||
To thwart dictionary attacks, it's recommended to use a strong hash preset
|
||||
with brainwallets. For a brainwallet passphrase to generate the correct
|
||||
seed, the same seed length and hash preset parameters must always be used.
|
||||
""".strip()
|
||||
|
||||
def usage():
|
||||
Msg("USAGE: %s %s" % (g.prog_name, usage_txt))
|
||||
sys.exit(2)
|
||||
|
|
@ -175,9 +189,10 @@ def check_opts(usr_opts): # Returns false if any check fails
|
|||
(val,desc,n,sepword))
|
||||
return False
|
||||
|
||||
def opt_compares(val,op,target,desc):
|
||||
def opt_compares(val,op,target,desc,what=""):
|
||||
if what: what += " "
|
||||
if not eval("%s %s %s" % (val, op, target)):
|
||||
msg("%s: invalid %s (not %s %s)" % (val,desc,op,target))
|
||||
msg("%s: invalid %s (%snot %s %s)" % (val,desc,what,op,target))
|
||||
return False
|
||||
return True
|
||||
|
||||
|
|
@ -212,15 +227,14 @@ def check_opts(usr_opts): # Returns false if any check fails
|
|||
|
||||
desc = "parameter for '%s' option" % fmt_opt(key)
|
||||
|
||||
from mmgen.util import check_infile,check_outfile,check_outdir
|
||||
# Check for file existence and readability
|
||||
from mmgen.util import check_infile
|
||||
if key in ('keys_from_file','mmgen_keys_from_file',
|
||||
'passwd_file','keysforaddrs','comment_file'):
|
||||
check_infile(val) # exits on error
|
||||
continue
|
||||
|
||||
if key == 'outdir':
|
||||
from mmgen.util import check_outdir
|
||||
check_outdir(val) # exits on error
|
||||
elif key == 'label':
|
||||
if not is_mmgen_wallet_label(val):
|
||||
|
|
@ -244,10 +258,22 @@ def check_opts(usr_opts): # Returns false if any check fails
|
|||
elif issubclass(sstype,Brainwallet):
|
||||
die(1,"Output to brainwallet format unsupported")
|
||||
elif key in ('hidden_incog_input_params','hidden_incog_output_params'):
|
||||
a = val.split(",")
|
||||
if len(a) != 2:
|
||||
opt_display(key,val)
|
||||
msg("Option requires two comma-separated arguments")
|
||||
return False
|
||||
if not opt_is_int(a[1],desc): return False
|
||||
if key == 'hidden_incog_input_params':
|
||||
check_infile(val.split(",")[0])
|
||||
check_infile(a[0],blkdev_ok=True)
|
||||
key2 = 'in_fmt'
|
||||
else:
|
||||
import os
|
||||
try: os.stat(a[0])
|
||||
except:
|
||||
b = os.path.dirname(a[0])
|
||||
if b: check_outdir(b)
|
||||
else: check_outfile(a[0],blkdev_ok=True)
|
||||
key2 = 'out_fmt'
|
||||
if hasattr(opt,key2):
|
||||
val2 = getattr(opt,key2)
|
||||
|
|
@ -257,47 +283,22 @@ def check_opts(usr_opts): # Returns false if any check fails
|
|||
"Option conflict:\n %s, with\n %s=%s" % (
|
||||
fmt_opt(key),fmt_opt(key2),val2
|
||||
))
|
||||
|
||||
# begin OLD, deprecated
|
||||
elif key == 'hidden_incog_params':
|
||||
from mmgen.util import check_outfile
|
||||
if not opt_splits(val,",",2,desc): return False
|
||||
outfile,offset = val.split(",")
|
||||
check_outfile(outfile)
|
||||
w = "offset " + desc
|
||||
if not opt_is_int(offset,w): return False
|
||||
if not opt_compares(offset,">=",0,desc): return False
|
||||
elif key == 'export_incog_hidden' or key == 'from_incog_hidden':
|
||||
if key == 'from_incog_hidden':
|
||||
if not opt_splits(val,",",3,desc): return False
|
||||
infile,offset,seed_len = val.split(",")
|
||||
from mmgen.util import check_infile
|
||||
check_infile(infile)
|
||||
w = "seed length " + desc
|
||||
if not opt_is_int(seed_len,w): return False
|
||||
if not opt_is_in_list(int(seed_len),g.seed_lens,w): return False
|
||||
else:
|
||||
from mmgen.util import check_outfile
|
||||
if not opt_splits(val,",",2,desc): return False
|
||||
outfile,offset = val.split(",")
|
||||
check_outfile(outfile)
|
||||
w = "offset " + desc
|
||||
if not opt_is_int(offset,w): return False
|
||||
if not opt_compares(offset,">=",0,desc): return False
|
||||
elif key == 'from_brain':
|
||||
if not opt_splits(val,",",2,desc): return False
|
||||
l,p = val.split(",")
|
||||
w = "seed length " + desc
|
||||
if not opt_is_int(l,w): return False
|
||||
if not opt_is_in_list(int(l),g.seed_lens,w): return False
|
||||
w = "hash preset " + desc
|
||||
if not opt_is_in_list(p,g.hash_presets.keys(),w): return False
|
||||
# end OLD
|
||||
elif key == 'seed_len':
|
||||
if not opt_is_int(val,desc): return False
|
||||
if not opt_is_in_list(int(val),g.seed_lens,desc): return False
|
||||
elif key == 'hash_preset':
|
||||
if not opt_is_in_list(val,g.hash_presets.keys(),desc): return False
|
||||
elif key == 'brain_params':
|
||||
a = val.split(",")
|
||||
if len(a) != 2:
|
||||
opt_display(key,val)
|
||||
msg("Option requires two comma-separated arguments")
|
||||
return False
|
||||
d = "seed length " + desc
|
||||
if not opt_is_int(a[0],d): return False
|
||||
if not opt_is_in_list(int(a[0]),g.seed_lens,d): return False
|
||||
d = "hash preset " + desc
|
||||
if not opt_is_in_list(a[1],g.hash_presets.keys(),d): return False
|
||||
elif key == 'usr_randchars':
|
||||
if val == 0: continue
|
||||
if not opt_is_int(val,desc): return False
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ class SeedSource(MMGenObject):
|
|||
|
||||
class SeedSourceData(MMGenObject): pass
|
||||
|
||||
def __new__(cls,fn=None,ss=None,ignore_in_fmt_opt=False,passchg=False):
|
||||
def __new__(cls,fn=None,ss=None,ignore_in_fmt=False,passchg=False):
|
||||
|
||||
def die_on_opt_mismatch(opt,sstype):
|
||||
opt_sstype = cls.fmt_code_to_sstype(opt)
|
||||
|
|
@ -91,7 +91,7 @@ class SeedSource(MMGenObject):
|
|||
f = Filename(fn,ftype="hincog")
|
||||
sstype = cls.fmt_code_to_sstype("hincog")
|
||||
|
||||
if opt.in_fmt and not ignore_in_fmt_opt:
|
||||
if opt.in_fmt and not ignore_in_fmt:
|
||||
die_on_opt_mismatch(opt.in_fmt,sstype)
|
||||
|
||||
me = super(cls,cls).__new__(sstype)
|
||||
|
|
@ -109,7 +109,7 @@ class SeedSource(MMGenObject):
|
|||
|
||||
return me
|
||||
|
||||
def __init__(self,fn=None,ss=None,ignore_in_fmt_opt=False,passchg=False):
|
||||
def __init__(self,fn=None,ss=None,ignore_in_fmt=False,passchg=False):
|
||||
|
||||
self.ssdata = self.SeedSourceData()
|
||||
self.msg = {}
|
||||
|
|
@ -692,16 +692,26 @@ class Brainwallet (SeedSourceEnc):
|
|||
ext = "mmbrain"
|
||||
# brainwallet warning message? TODO
|
||||
|
||||
def get_bw_params(self):
|
||||
# already checked
|
||||
a = opt.brain_params.split(",")
|
||||
return int(a[0]),a[1]
|
||||
|
||||
def _deformat(self):
|
||||
self.brainpasswd = " ".join(self.fmt_data.split())
|
||||
return True
|
||||
|
||||
def _decrypt(self):
|
||||
self._get_hash_preset()
|
||||
d = self.ssdata
|
||||
if hasattr(opt,"brain_params"):
|
||||
seed_len,d.hash_preset = self.get_bw_params()
|
||||
else:
|
||||
self._get_hash_preset()
|
||||
seed_len = opt.seed_len
|
||||
vmsg_r("Hashing brainwallet data. Please wait...")
|
||||
# Use buflen arg of scrypt.hash() to get seed of desired length
|
||||
seed = scrypt_hash_passphrase(self.brainpasswd, "",
|
||||
self.ssdata.hash_preset, buflen=opt.seed_len/8)
|
||||
d.hash_preset, buflen=seed_len/8)
|
||||
vmsg("Done")
|
||||
self.seed = Seed(seed)
|
||||
msg("Seed ID: %s" % self.seed.sid)
|
||||
|
|
@ -921,8 +931,8 @@ harder to find, you're advised to choose a much larger file size than this.
|
|||
m = ("Input","Destination")[int(action=="write")]
|
||||
if fn.size < d.hincog_offset + d.target_data_len:
|
||||
die(1,
|
||||
"%s file has length %s, too short to %s %s bytes of data at offset %s"
|
||||
% (m,fn.size,action,d.target_data_len,d.hincog_offset))
|
||||
"%s file '%s' has length %s, too short to %s %s bytes of data at offset %s"
|
||||
% (m,fn.name,fn.size,action,d.target_data_len,d.hincog_offset))
|
||||
|
||||
def _get_data(self):
|
||||
d = self.ssdata
|
||||
|
|
@ -941,7 +951,6 @@ harder to find, you're advised to choose a much larger file size than this.
|
|||
qmsg("Data read from file '%s' at offset %s" %
|
||||
(self.infile.name,d.hincog_offset), "Data read from file")
|
||||
|
||||
|
||||
# overrides method in SeedSource
|
||||
def write_to_file(self):
|
||||
d = self.ssdata
|
||||
|
|
@ -972,9 +981,10 @@ harder to find, you're advised to choose a much larger file size than this.
|
|||
else:
|
||||
die(1,"Exiting at user request")
|
||||
|
||||
self.outfile = f = Filename(fn,ftype="hincog")
|
||||
self.outfile = f = Filename(fn,ftype=self.fmt_codes[0],write=True)
|
||||
|
||||
dmsg("Incog data len %s, offset %s" % (d.target_data_len,d.hincog_offset))
|
||||
dmsg("%s data len %s, offset %s" % (
|
||||
capfirst(self.desc),d.target_data_len,d.hincog_offset))
|
||||
|
||||
if check_offset:
|
||||
self._check_valid_offset(f,"write")
|
||||
|
|
@ -985,5 +995,4 @@ harder to find, you're advised to choose a much larger file size than this.
|
|||
os.write(fh, self.fmt_data)
|
||||
os.close(fh)
|
||||
msg("%s written to file '%s' at offset %s" % (
|
||||
capfirst(self.desc),
|
||||
f.name,d.hincog_offset))
|
||||
capfirst(self.desc),f.name,d.hincog_offset))
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ def process_opts(argv,opts_data,short_opts,long_opts):
|
|||
opts_data['prog_name'] = os.path.basename(sys.argv[0])
|
||||
long_opts = [i.replace("_","-") for i in long_opts]
|
||||
|
||||
try: cl_opts, args = getopt.getopt(argv[1:], short_opts, long_opts)
|
||||
try: cl_opts,args = getopt.getopt(argv[1:], short_opts, long_opts)
|
||||
except getopt.GetoptError as err:
|
||||
print str(err); sys.exit(2)
|
||||
|
||||
|
|
|
|||
|
|
@ -132,11 +132,11 @@ def suf(arg,suf_type):
|
|||
def get_extension(f):
|
||||
return os.path.splitext(f)[1][1:]
|
||||
|
||||
def make_chksum_N(s,n,sep=False):
|
||||
if n%4 or not (4 <= n <= 64): return False
|
||||
def make_chksum_N(s,nchars,sep=False):
|
||||
if nchars%4 or not (4 <= nchars <= 64): return False
|
||||
s = sha256(sha256(s).digest()).hexdigest().upper()
|
||||
sep = " " if sep else ""
|
||||
return sep.join([s[i*4:i*4+4] for i in range(n/4)])
|
||||
return sep.join([s[i*4:i*4+4] for i in range(nchars/4)])
|
||||
|
||||
def make_chksum_8(s,sep=False):
|
||||
s = sha256(sha256(s).digest()).hexdigest()[:8].upper()
|
||||
|
|
@ -273,36 +273,43 @@ def open_file_or_exit(filename,mode):
|
|||
return f
|
||||
|
||||
|
||||
def check_file_type_and_access(fname,ftype):
|
||||
def check_file_type_and_access(fname,ftype,blkdev_ok=False):
|
||||
|
||||
import os, stat
|
||||
|
||||
typ2,tdesc2,access,action = (stat.S_ISLNK,"symbolic link",os.R_OK,"read")\
|
||||
if ftype == "input file" else (stat.S_ISBLK,"block device",os.W_OK,"writ")
|
||||
a = ((os.R_OK,"read"),(os.W_OK,"writ"))
|
||||
access,m = a[int(ftype in ("output file","output directory"))]
|
||||
|
||||
if ftype == "directory":
|
||||
typ1,typ2,tdesc = stat.S_ISDIR,stat.S_ISDIR,"directory"
|
||||
else:
|
||||
typ1,tdesc = stat.S_ISREG,"regular file or "+tdesc2
|
||||
ok_types = [
|
||||
(stat.S_ISREG,"regular file"),
|
||||
(stat.S_ISLNK,"symbolic link")
|
||||
]
|
||||
if blkdev_ok: ok_types.append((stat.S_ISBLK,"block device"))
|
||||
if ftype == "output directory": ok_types = [(stat.S_ISDIR, "output directory")]
|
||||
|
||||
try: mode = os.stat(fname).st_mode
|
||||
except:
|
||||
msg("Unable to stat requested %s '%s'" % (ftype,fname))
|
||||
sys.exit(1)
|
||||
|
||||
if not (typ1(mode) or typ2(mode)):
|
||||
msg("Requested %s '%s' is not a %s" % (ftype,fname,tdesc))
|
||||
for t in ok_types:
|
||||
if t[0](mode): break
|
||||
else:
|
||||
msg("Requested %s '%s' is not a %s" % (ftype,fname,
|
||||
" or ".join([t[1] for t in ok_types])))
|
||||
sys.exit(1)
|
||||
|
||||
if not os.access(fname, access):
|
||||
msg("Requested %s '%s' is un%sable by you" % (ftype,fname,action))
|
||||
msg("Requested %s '%s' is not %sable by you" % (ftype,fname,m))
|
||||
sys.exit(1)
|
||||
|
||||
return True
|
||||
|
||||
def check_infile(f): return check_file_type_and_access(f,"input file")
|
||||
def check_outfile(f): return check_file_type_and_access(f,"output file")
|
||||
def check_outdir(f): return check_file_type_and_access(f,"directory")
|
||||
def check_infile(f,blkdev_ok=False):
|
||||
return check_file_type_and_access(f,"input file",blkdev_ok=blkdev_ok)
|
||||
def check_outfile(f,blkdev_ok=False):
|
||||
return check_file_type_and_access(f,"output file",blkdev_ok=blkdev_ok)
|
||||
def check_outdir(f): return check_file_type_and_access(f,"output directory")
|
||||
|
||||
def _validate_addr_num(n):
|
||||
|
||||
|
|
|
|||
5
setup.py
5
setup.py
|
|
@ -21,7 +21,7 @@ from distutils.core import setup
|
|||
setup(
|
||||
name = 'mmgen',
|
||||
description = 'A complete Bitcoin cold-storage solution for the command line',
|
||||
version = '0.8.0',
|
||||
version = '0.8.1rc1',
|
||||
author = 'Philemon',
|
||||
author_email = 'mmgen-py@yandex.com',
|
||||
url = 'https://github.com/mmgen/mmgen',
|
||||
|
|
@ -52,15 +52,12 @@ setup(
|
|||
'mmgen.main',
|
||||
'mmgen.main_addrgen',
|
||||
'mmgen.main_addrimport',
|
||||
'mmgen.main_passchg',
|
||||
'mmgen.main_pywallet',
|
||||
'mmgen.main_tool',
|
||||
'mmgen.main_txcreate',
|
||||
'mmgen.main_txsend',
|
||||
'mmgen.main_txsign',
|
||||
'mmgen.main_wallet',
|
||||
'mmgen.main_walletchk',
|
||||
'mmgen.main_walletgen',
|
||||
|
||||
'mmgen.share.__init__',
|
||||
'mmgen.share.Opts',
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
98
test/test.py
98
test/test.py
|
|
@ -13,6 +13,7 @@ import mmgen.opt as opt
|
|||
from mmgen.util import mmsg,mdie,Msg,die,capfirst
|
||||
from mmgen.test import *
|
||||
|
||||
tb_cmd = "scripts/traceback.py"
|
||||
hincog_fn = "rand_data"
|
||||
hincog_bytes = 1024*1024
|
||||
hincog_offset = 98765
|
||||
|
|
@ -28,8 +29,8 @@ ref_wallet_hash_preset = "1"
|
|||
ref_wallet_incog_offset = 123
|
||||
|
||||
ref_bw_hash_preset = "1"
|
||||
ref_bw_file = "brainwallet"
|
||||
ref_bw_file_spc = "brainwallet-spaced"
|
||||
ref_bw_file = "wallet.mmbrain"
|
||||
ref_bw_file_spc = "wallet-spaced.mmbrain"
|
||||
|
||||
ref_kafile_pass = "kafile password"
|
||||
ref_kafile_hash_preset = "1"
|
||||
|
|
@ -381,8 +382,9 @@ opts_data = {
|
|||
-q, --quiet Produce minimal output. Suppress dependency info
|
||||
-s, --system Test scripts and modules installed on system rather than
|
||||
those in the repo root
|
||||
-t, --traceback Run the command inside the '{tb_cmd}' script
|
||||
-v, --verbose Produce more verbose output
|
||||
""",
|
||||
""".format(tb_cmd=tb_cmd),
|
||||
'notes': """
|
||||
|
||||
If no command is given, the whole suite of tests is run.
|
||||
|
|
@ -542,6 +544,9 @@ class MMGenExpect(object):
|
|||
os.system(" ".join([mmgen_cmd] + cmd_args))
|
||||
sys.exit()
|
||||
else:
|
||||
if opt.traceback:
|
||||
cmd_args = [mmgen_cmd] + cmd_args
|
||||
mmgen_cmd = tb_cmd
|
||||
self.p = pexpect.spawn(mmgen_cmd,cmd_args)
|
||||
if opt.exact_output: self.p.logfile = sys.stdout
|
||||
|
||||
|
|
@ -580,7 +585,7 @@ class MMGenExpect(object):
|
|||
passphrase+"\n",regex=True)
|
||||
|
||||
def hash_preset(self,desc,preset=''):
|
||||
my_expect(self.p,("Enter hash preset for %s," % desc))
|
||||
my_expect(self.p,("Enter hash preset for %s" % desc))
|
||||
my_expect(self.p,("or hit ENTER .*?:"), str(preset)+"\n",regex=True)
|
||||
|
||||
def written_to_file(self,desc,overwrite_unlikely=False,query="Overwrite? ",oo=False):
|
||||
|
|
@ -897,12 +902,11 @@ class MMGenTestSuite(object):
|
|||
t.license()
|
||||
t.passphrase("MMGen wallet",cfg['wpasswd'])
|
||||
t.expect("Passphrase is OK")
|
||||
t.expect("[0-9]+ addresses generated",regex=True)
|
||||
chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
|
||||
if check_ref:
|
||||
refcheck("address data checksum",chk,cfg['addrfile_chk'])
|
||||
return
|
||||
t.written_to_file("Addresses")
|
||||
t.written_to_file("Addresses",oo=True)
|
||||
ok()
|
||||
|
||||
def refaddrgen(self,name,walletfile):
|
||||
|
|
@ -1084,11 +1088,11 @@ class MMGenTestSuite(object):
|
|||
self.export_incog(
|
||||
name,wf,desc="hidden incognito data",out_fmt="hi",add_args=add_args)
|
||||
|
||||
def addrgen_seed(self,name,walletfile,foo,desc="seed data",arg="-s"):
|
||||
def addrgen_seed(self,name,walletfile,foo,desc="seed data",in_fmt="seed"):
|
||||
stdout = (False,True)[int(desc=="seed data")] #capture output to screen once
|
||||
add_arg = ([],["-S"])[int(stdout)]
|
||||
t = MMGenExpect(name,"mmgen-addrgen", add_arg +
|
||||
[arg,"-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
|
||||
["-i"+in_fmt,"-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
|
||||
t.license()
|
||||
t.expect_getend("Valid %s for seed ID " % desc)
|
||||
vmsg("Comparing generated checksum with checksum from previous address file")
|
||||
|
|
@ -1098,16 +1102,19 @@ class MMGenTestSuite(object):
|
|||
# t.no_overwrite()
|
||||
ok()
|
||||
|
||||
def addrgen_mnemonic(self,name,walletfile,foo):
|
||||
self.addrgen_seed(name,walletfile,foo,desc="mnemonic",arg="-m")
|
||||
def addrgen_mnemonic(self,name,wf,foo):
|
||||
self.addrgen_seed(name,wf,foo,desc="mnemonic data",in_fmt="words")
|
||||
|
||||
def addrgen_incog(self,name,walletfile,foo,args=["-g"]):
|
||||
t = MMGenExpect(name,"mmgen-addrgen",args+["-d",
|
||||
cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
|
||||
def addrgen_incog(self,name,wf=[],foo="",in_fmt="i",
|
||||
desc="incognito data",args=[]):
|
||||
t = MMGenExpect(name,"mmgen-addrgen",
|
||||
args+["-i"+in_fmt,"-d",cfg['tmpdir']]+
|
||||
([wf] if wf else [])+
|
||||
[cfg['addr_idx_list']])
|
||||
t.license()
|
||||
t.expect_getend("Incog ID: ")
|
||||
t.passphrase("incognito wallet data \w{8}", cfg['wpasswd'])
|
||||
t.hash_preset("incog wallet",'1')
|
||||
t.expect_getend("Incog Wallet ID: ")
|
||||
t.hash_preset(desc,'1')
|
||||
t.passphrase("%s \w{8}" % desc, cfg['wpasswd'])
|
||||
vmsg("Comparing generated checksum with checksum from address file")
|
||||
chk = t.expect_getend(r"Checksum for address data .*?: ",regex=True)
|
||||
t.close()
|
||||
|
|
@ -1115,19 +1122,18 @@ class MMGenTestSuite(object):
|
|||
# t.no_overwrite()
|
||||
ok()
|
||||
|
||||
def addrgen_incog_hex(self,name,walletfile,foo):
|
||||
self.addrgen_incog(name,walletfile,foo,args=["-X"])
|
||||
def addrgen_incog_hex(self,name,wf,foo):
|
||||
self.addrgen_incog(name,wf,"",in_fmt="xi",desc="hex incognito data")
|
||||
|
||||
def addrgen_incog_hidden(self,name,walletfile,foo):
|
||||
def addrgen_incog_hidden(self,name,wf,foo):
|
||||
rf = os.path.join(cfg['tmpdir'],hincog_fn)
|
||||
self.addrgen_incog(name,walletfile,foo,
|
||||
args=["-G","%s,%s,%s"%(rf,hincog_offset,hincog_seedlen)])
|
||||
self.addrgen_incog(name,[],"",in_fmt="hi",desc="hidden incognito data",
|
||||
args=["-H","%s,%s"%(rf,hincog_offset),"-l",str(hincog_seedlen)])
|
||||
|
||||
def keyaddrgen(self,name,walletfile,check_ref=False):
|
||||
t = MMGenExpect(name,"mmgen-keygen",
|
||||
["-d",cfg['tmpdir'],walletfile,cfg['addr_idx_list']])
|
||||
t.license()
|
||||
t.expect("Type uppercase 'YES' to confirm: ","YES\n")
|
||||
t.passphrase("MMGen wallet",cfg['wpasswd'])
|
||||
chk = t.expect_getend(r"Checksum for key-address data .*?: ",regex=True)
|
||||
if check_ref:
|
||||
|
|
@ -1136,7 +1142,7 @@ class MMGenTestSuite(object):
|
|||
t.expect("Encrypt key list? (y/N): ","y")
|
||||
t.hash_preset("new key list",'1')
|
||||
t.passphrase_new("new key list",cfg['kapasswd'])
|
||||
t.written_to_file("Keys")
|
||||
t.written_to_file("Secret keys",oo=True)
|
||||
ok()
|
||||
|
||||
def refkeyaddrgen(self,name,walletfile):
|
||||
|
|
@ -1189,7 +1195,7 @@ class MMGenTestSuite(object):
|
|||
t.license()
|
||||
t.tx_view()
|
||||
for cnum in ('1','3'):
|
||||
t.expect_getend("Getting MMGen wallet data from file ")
|
||||
# t.expect_getend("Getting MMGen wallet data from file ")
|
||||
t.passphrase("MMGen wallet",cfgs[cnum]['wpasswd'])
|
||||
self.txsign_end(t)
|
||||
ok()
|
||||
|
|
@ -1218,15 +1224,12 @@ class MMGenTestSuite(object):
|
|||
def txsign4(self,name,f1,f2,f3,f4,f5):
|
||||
non_mm_fn = os.path.join(cfg['tmpdir'],non_mmgen_fn)
|
||||
t = MMGenExpect(name,"mmgen-txsign",
|
||||
["-d",cfg['tmpdir'],"-b",cfg['bw_params'],"-k",non_mm_fn,f1,f2,f3,f4,f5])
|
||||
["-d",cfg['tmpdir'],"-i","brain","-b"+cfg['bw_params'],"-p1","-k",non_mm_fn,f1,f2,f3,f4,f5])
|
||||
t.license()
|
||||
t.tx_view()
|
||||
|
||||
for cnum,desc,app in ('1',"incognito","incognito"),('3',"MMGen","MMGen"):
|
||||
t.expect_getend("Getting %s wallet data from file " % desc)
|
||||
t.passphrase("%s wallet"%app,cfgs[cnum]['wpasswd'])
|
||||
if cnum == '1':
|
||||
t.hash_preset("incog wallet",'1')
|
||||
for cnum,desc in ('1',"incognito data"),('3',"MMGen wallet"):
|
||||
t.passphrase(("%s" % desc),cfgs[cnum]['wpasswd'])
|
||||
|
||||
self.txsign_end(t)
|
||||
ok()
|
||||
|
|
@ -1368,11 +1371,11 @@ class MMGenTestSuite(object):
|
|||
def ref_hincog_conv_out(self,name,extra_uopts=[]):
|
||||
ic_f = os.path.join(cfg['tmpdir'],"rand.data")
|
||||
hi_parms = "%s,%s" % (ic_f,ref_wallet_incog_offset)
|
||||
hi_parms_legacy = "%s,%s,%s"%(ic_f,ref_wallet_incog_offset,cfg['seed_len'])
|
||||
sl_parm = "-l" + str(cfg['seed_len'])
|
||||
self.walletconv_out(name,
|
||||
"hidden incognito data", "hi",
|
||||
uopts=["-J",hi_parms] + extra_uopts,
|
||||
uopts_chk=["-G",hi_parms_legacy],
|
||||
uopts=["-J",hi_parms,sl_parm] + extra_uopts,
|
||||
uopts_chk=["-H",hi_parms,sl_parm],
|
||||
pw=True
|
||||
)
|
||||
|
||||
|
|
@ -1411,8 +1414,8 @@ class MMGenTestSuite(object):
|
|||
|
||||
def ref_brain_chk(self,name,bw_file=ref_bw_file):
|
||||
wf = os.path.join(ref_dir,bw_file)
|
||||
arg = "-b%s,%s" % (cfg['seed_len'],ref_bw_hash_preset)
|
||||
self.keygen_chksum_chk(name,wf,cfg['ref_bw_seed_id'],[arg])
|
||||
args = ["-l%s" % cfg['seed_len'], "-p"+ref_bw_hash_preset]
|
||||
self.keygen_chksum_chk(name,wf,cfg['ref_bw_seed_id'],args)
|
||||
|
||||
def keygen_chksum_chk_hincog(self,name,seed_id,hincog_parm):
|
||||
t = MMGenExpect(name,"mmgen-keygen", ["-p1","-q","-S","-A"]+hincog_parm+["1"],extra_desc="(check)")
|
||||
|
|
@ -1425,7 +1428,9 @@ class MMGenTestSuite(object):
|
|||
|
||||
def keygen_chksum_chk(self,name,wf,seed_id,args=[],pw=False):
|
||||
hp_arg = ["-p1"] if pw else []
|
||||
t = MMGenExpect(name,"mmgen-keygen", ["-q","-S","-A"]+args+hp_arg+[wf,"1"],extra_desc="(check)")
|
||||
t = MMGenExpect(name,"mmgen-keygen",
|
||||
["-l",str(cfg['seed_len']),"-q","-S","-A"]+args+hp_arg+[wf,"1"],
|
||||
extra_desc="(check)")
|
||||
if pw:
|
||||
t.passphrase("",cfg['wpasswd'])
|
||||
t.expect("Encrypt key list? (y/N): ","\n")
|
||||
|
|
@ -1448,20 +1453,21 @@ class MMGenTestSuite(object):
|
|||
def ref_brain_chk_spc3(self,name):
|
||||
self.ref_brain_chk(name,bw_file=ref_bw_file_spc)
|
||||
|
||||
def ref_hincog_chk(self,name):
|
||||
for wtype,desc,earg in ('hic_wallet','',[]), \
|
||||
('hic_wallet_old','(old format)',["-o"]):
|
||||
ic_arg = "%s,%s,%s" % (
|
||||
def ref_hincog_chk(self,name,desc="hidden incognito data"):
|
||||
for wtype,edesc,earg in ('hic_wallet','',[]), \
|
||||
('hic_wallet_old','(old format)',["-O"]):
|
||||
ic_arg = "%s,%s" % (
|
||||
os.path.join(ref_dir,cfg[wtype]),
|
||||
ref_wallet_incog_offset,cfg['seed_len']
|
||||
ref_wallet_incog_offset
|
||||
)
|
||||
t = MMGenExpect(name,"mmgen-keygen",
|
||||
["-q","-A"]+earg+["-G"]+[ic_arg]+['1'],extra_desc=desc)
|
||||
t.passphrase("incognito wallet",cfg['wpasswd'])
|
||||
t.hash_preset("incog wallet","1")
|
||||
t = MMGenExpect(name,"mmgen-keygen",["-l",str(cfg['seed_len']),
|
||||
"-q","-A"]+earg+["-H"]+[ic_arg]+['1'],extra_desc=edesc)
|
||||
t.hash_preset(desc,"1")
|
||||
t.passphrase(desc,cfg['wpasswd'])
|
||||
if wtype == 'hic_wallet_old':
|
||||
t.expect("Is the seed ID correct? (Y/n): ","\n")
|
||||
chk = t.expect_getend("Valid incog data for seed ID ")
|
||||
chk = t.expect_getend("Seed ID: ")
|
||||
t.expect("Encrypt key list? (y/N): ","\n")
|
||||
t.close()
|
||||
cmp_or_die(cfg['seed_id'],chk)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue