add 3 wiki pages
Added: - doc/wiki/using-mmgen/Test-Suite.md - doc/wiki/using-mmgen/Tool-API.md - doc/wiki/using-mmgen/XOR-Seed-Splitting:-Theory-and-Practice.md
This commit is contained in:
parent
34f30fbf35
commit
59368fd7c1
3 changed files with 608 additions and 0 deletions
193
doc/wiki/using-mmgen/Test-Suite.md
Normal file
193
doc/wiki/using-mmgen/Test-Suite.md
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
## Introduction
|
||||
|
||||
In addition to low-level subsystems, the suite tests the overall operation of
|
||||
MMGen’s commands by running them interactively as a user would. Thus the test
|
||||
suite is useful not only for ensuring the MMGen system is correctly installed
|
||||
and working on your platform but also for learning how it works.
|
||||
|
||||
BTC-only testing requires only Bitcoin Core to be installed. Altcoin testing
|
||||
requires various helper programs and libraries. Installation instructions for
|
||||
these are provided below. Non-standard RPC ports and data directories are
|
||||
always used, so there’s no need to stop your running node or nodes.
|
||||
|
||||
On Linux/x86\_64 with a reasonably fast processor, the full suite should run in
|
||||
under 15 minutes when invoked with the `-F` option. Execution times on other
|
||||
platforms may be much slower.
|
||||
|
||||
## Quick start
|
||||
|
||||
### Container setup (if applicable)
|
||||
|
||||
The test suite requires the `/dev/loopX` devices to exist and be enabled. If
|
||||
you’re running in an LXC container, note that only privileged containers allow
|
||||
loop devices. You may enable them in the config file as follows:
|
||||
|
||||
lxc.cgroup2.devices.allow = b 7:0 rwm # /dev/loop0
|
||||
lxc.cgroup2.devices.allow = b 7:1 rwm # /dev/loop1
|
||||
lxc.cgroup2.devices.allow = b 7:2 rwm # /dev/loop2
|
||||
|
||||
Every time the container is started, you may need to create the files afresh:
|
||||
|
||||
# mknod /dev/loop0 b 7 0
|
||||
# mknod /dev/loop1 b 7 1
|
||||
# mknod /dev/loop2 b 7 2
|
||||
|
||||
### BTC-only testing
|
||||
|
||||
Clone the Bitcoin Core repo somewhere on your system:
|
||||
|
||||
$ git clone https://github.com/bitcoin/bitcoin
|
||||
|
||||
Install the Bitcoin Core daemon [(source)][sd] [(binaries)][bd].
|
||||
|
||||
Point the test suite to your copy of the Bitcoin Core repo:
|
||||
|
||||
$ export CORE_REPO_ROOT=/path/to/bitcoin/core/repo
|
||||
|
||||
Install Pycoin:
|
||||
|
||||
$ python3 -m pip install --user pycoin
|
||||
|
||||
CD to the MMGen repository root and build without installing:
|
||||
|
||||
$ cd path/to/mmgen/repo
|
||||
$ python3 setup.py build_ext --inplace
|
||||
|
||||
Run the following if upgrading from a previous version of MMGen:
|
||||
|
||||
$ test/test.py clean
|
||||
|
||||
Run the test suite in fast mode, skipping altcoin tests:
|
||||
|
||||
$ test/test-release.sh -F noalt
|
||||
|
||||
### Complete testing (BTC plus all supported altcoins)
|
||||
|
||||
Complete the BTC-only installation steps above, without running the test.
|
||||
|
||||
Make sure the [Bitcoin Cash Node][cnd], [Litecoin][ld] and [Monero][md]
|
||||
daemons are installed on your system.
|
||||
|
||||
Install [OpenEthereum, Parity, Geth, the Ethereum dependencies][oe] and
|
||||
optionally the [Solidity compiler][sc] as described on the
|
||||
Altcoin-and-Forkcoin-Support page.
|
||||
|
||||
In addition, you must install the following helper programs and libraries (MSYS2
|
||||
users can omit Zcash-Mini and leave out `sudo` in commands):
|
||||
|
||||
#### SSH daemon setup (MSYS2 only)
|
||||
|
||||
The XMR test sets up a local SOCKS proxy to test transaction relaying. This
|
||||
requires the SSH daemon to be set up and running. On MSYS2 systems, SSHD
|
||||
is not configured by default, but it may be easily set up with the following
|
||||
steps:
|
||||
|
||||
Open PowerShell as administrator, and at the DOS prompt, execute:
|
||||
|
||||
system32> net user administrator /active:yes
|
||||
system32> C:\\msys64\usr\bin\bash.exe --login
|
||||
|
||||
Now, at the MSYS2 prompt, cd to the MMGen repository root and run the setup
|
||||
script:
|
||||
|
||||
$ scripts/msys2-sshd-setup.sh
|
||||
|
||||
The daemon should now start automatically every time the system is booted. It
|
||||
may also be started and stopped manually at the DOS or MSYS2 prompt as follows
|
||||
(PowerShell must be running with admin privileges):
|
||||
|
||||
# net start msys2_sshd
|
||||
# net stop msys2_sshd
|
||||
|
||||
#### MoneroPy
|
||||
|
||||
$ git clone https://github.com/bigreddmachine/MoneroPy
|
||||
$ cd MoneroPy
|
||||
$ python3 setup.py install --user
|
||||
$ cd ..
|
||||
|
||||
#### Vanitygen PlusPlus (forked from Vanitygen Plus)
|
||||
|
||||
$ git clone https://github.com/10gic/vanitygen-plusplus
|
||||
$ cd vanitygen-plusplus
|
||||
$ git checkout -b vanitygen-plus e7858035d092 # rewind to fork commit
|
||||
$ make keyconv # ‘mingw32-make.exe keyconv’ for MSYS2
|
||||
$ sudo install --strip keyconv /usr/local/bin # ‘keyconv.exe’ for MSYS2
|
||||
$ cd ..
|
||||
|
||||
#### Zcash-Mini
|
||||
|
||||
$ sudo apt-get install golang # skip this if Go is already installed
|
||||
$ git clone https://github.com/FiloSottile/zcash-mini
|
||||
$ cd zcash-mini
|
||||
$ go mod init zcash-mini
|
||||
$ go mod tidy
|
||||
$ go build -mod=mod # or just ’go build’
|
||||
$ sudo install --strip ./zcash-mini /usr/local/bin
|
||||
$ cd ..
|
||||
|
||||
#### Ethkey
|
||||
|
||||
On Arch Linux systems, ethkey is included in the OpenEthereum package:
|
||||
|
||||
$ pacman -S openethereum
|
||||
|
||||
For other systems, you may have to build ethkey from source:
|
||||
|
||||
$ sudo apt-get install rustc # skip this if Rust is already installed
|
||||
$ git clone https://github.com/openethereum/openethereum
|
||||
$ cd openethereum
|
||||
$ git checkout v2.6.6 # this version builds on ARM boards - your mileage may vary
|
||||
$ cargo build -p ethkey-cli --release
|
||||
$ sudo install --strip ./target/release/ethkey /usr/local/bin
|
||||
$ cd ..
|
||||
|
||||
#### Monero note
|
||||
|
||||
The Monero test (`test/test-release.sh xmr`) creates a private network and
|
||||
mines coins, so is therefore non-deterministic and prone to random failures.
|
||||
If you experience such a failure, just restart the test.
|
||||
|
||||
### Run the tests
|
||||
|
||||
Now you can run the test suite for all coins:
|
||||
|
||||
$ test/test-release.sh -F
|
||||
|
||||
## Overview of the individual tests
|
||||
|
||||
`test/test-release.sh` is just a simple shell script that invokes the test
|
||||
scripts with various options and arguments to ensure complete coverage of
|
||||
MMGen’s functionality. Launch the script with the `-t` option to view the
|
||||
invocations without running them.
|
||||
|
||||
The test scripts themselves are all located in the `test/` directory and bear
|
||||
the `.py` extension. They may be run individually if desired. Options and
|
||||
arguments required by the tests are described in detail on their help screens.
|
||||
|
||||
High-level testing of the MMGen system is performed by `test/test.py`, which
|
||||
uses the `pexpect` library to simulate interactive operation of MMGen user
|
||||
commands. Running `test/test.py` with the `-e` option will display the
|
||||
commands’ output on the screen as they’re being run.
|
||||
|
||||
| Test | What it tests |
|
||||
|:----------------------|:-----------------------------------------------------|
|
||||
| `test/colortest.py` | terminfo parsing; terminal colors |
|
||||
| `test/gentest.py` | key/address generation - profiling and data validity |
|
||||
| `test/hashfunc.py` | native SHA2 and Keccak implementations |
|
||||
| `test/objtest.py` | MMGen data objects - creation and error handling |
|
||||
| `test/objattrtest.py` | MMGen data objects - immutable attributes |
|
||||
| `test/scrambletest.py`| HMAC scramble strings used in key/password derivation|
|
||||
| `test/test.py` | overall operation of MMGen commands |
|
||||
| `test/tooltest.py` | the `mmgen-tool` utility - overall operation |
|
||||
| `test/tooltest2.py` | the `mmgen-tool` utility - data validity |
|
||||
| `test/unit_tests.py` | low-level subsystems |
|
||||
|
||||
[sd]: Install-Bitcoind-from-Source-on-Debian-or-Ubuntu-Linux
|
||||
[bd]: Install-Bitcoind
|
||||
[md]: https://getmonero.org/downloads/#linux
|
||||
[ad]: https://download.bitcoinabc.org/
|
||||
[cnd]: https://bitcoincashnode.org/
|
||||
[ld]: https://download.litecoin.org/litecoin-0.17.1/
|
||||
[oe]: Altcoin-and-Forkcoin-Support#a_oe
|
||||
[sc]: Altcoin-and-Forkcoin-Support#a_dt
|
||||
73
doc/wiki/using-mmgen/Tool-API.md
Normal file
73
doc/wiki/using-mmgen/Tool-API.md
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
The tool API provides a convenient interface to selected methods in the
|
||||
mmgen.tool module. Type `pydoc3 mmgen.tool.api` for available methods and
|
||||
call signatures.
|
||||
|
||||
## Examples
|
||||
|
||||
### Initialize:
|
||||
|
||||
from mmgen.tool.api import tool_api
|
||||
tool = tool_api()
|
||||
|
||||
### Key/address generation:
|
||||
|
||||
# List available coins:
|
||||
print(' '.join(tool.coins))
|
||||
|
||||
# Initialize a coin/network pair:
|
||||
proto = tool.init_coin('btc','mainnet')
|
||||
|
||||
# Print the available address types for current coin/network, along with a
|
||||
# description. If tool.addrtype is unset, the first-listed will be used:
|
||||
tool.print_addrtypes()
|
||||
|
||||
# Set the address type to P2PKH with compressed public key:
|
||||
tool.addrtype = 'compressed'
|
||||
|
||||
# Skip user entropy gathering (not recommended)
|
||||
tool.usr_randchars = 0
|
||||
|
||||
# Generate a random hex secret:
|
||||
hexsec = tool.randhex()
|
||||
|
||||
# Generate the key and address:
|
||||
wif = tool.hex2wif(hexsec)
|
||||
addr = tool.wif2addr(wif)
|
||||
|
||||
# Generate an LTC regtest Segwit key and address:
|
||||
proto = tool.init_coin('ltc','regtest')
|
||||
tool.addrtype = 'segwit'
|
||||
wif = tool.hex2wif(hexsec)
|
||||
addr = tool.wif2addr(wif)
|
||||
|
||||
# Generate a random LTC regtest Bech32 key/address pair:
|
||||
tool.addrtype = 'bech32'
|
||||
wif,addr = tool.randpair()
|
||||
|
||||
### Mnemonic seed phrase generation:
|
||||
|
||||
# Generate an MMGen native mnemonic seed phrase:
|
||||
mmgen_seed = tool.hex2mn(hexsec)
|
||||
|
||||
# Generate a BIP39 mnemonic seed phrase:
|
||||
bip39_seed = tool.hex2mn(hexsec,fmt='bip39')
|
||||
|
||||
### Utility methods:
|
||||
|
||||
# Reverse the hex string:
|
||||
hexsec_rev = tool.hexreverse(hexsec)
|
||||
|
||||
# Get the HASH160 of the value:
|
||||
sec_hash160 = tool.hash160(hexsec)
|
||||
|
||||
# Convert the value to base58 format:
|
||||
sec_b58 = tool.hextob58(hexsec)
|
||||
|
||||
# Convert the value to base58 check format:
|
||||
sec_b58chk = tool.hextob58chk(hexsec)
|
||||
|
||||
# Convert the byte specification '4G' to an integer:
|
||||
four_g = tool.bytespec('4G')
|
||||
|
||||
# Convert the byte specification '4GB' to an integer:
|
||||
four_gb = tool.bytespec('4GB')
|
||||
342
doc/wiki/using-mmgen/XOR-Seed-Splitting:-Theory-and-Practice.md
Normal file
342
doc/wiki/using-mmgen/XOR-Seed-Splitting:-Theory-and-Practice.md
Normal file
|
|
@ -0,0 +1,342 @@
|
|||
## Contents
|
||||
|
||||
+ [XOR Seed Splitting: A Theoretical Introduction](#a_xor)
|
||||
- [Deterministic Shares](#a_ds)
|
||||
- [Named Splits](#a_ns)
|
||||
- [Master Shares](#a_ms)
|
||||
+ [Seed Splitting with MMGen](#a_ss)
|
||||
|
||||
### <a name="a_xor">XOR Seed Splitting: A Theoretical Introduction</a>
|
||||
|
||||
The bitwise exclusive-or operation (usually denoted as `XOR`, or “![⊕]”)
|
||||
has interesting properties that make it very useful in cryptography.
|
||||
|
||||
Suppose we have two bytes, *a* and *b*:
|
||||
|
||||
![]["a: 1 0 0 1 0 1 0 0"]
|
||||
|
||||
![]["b: 0 1 0 1 1 1 1 0"]
|
||||
|
||||
To XOR the two bytes, we compare each of their bits. If the bit is the same for
|
||||
both *a* and *b,* the corresponding bit of the result is 0. If it differs, the
|
||||
result is 1:
|
||||
|
||||
![]["a ⊖ b: 1 1 0 0 1 0 1 0"]
|
||||
|
||||
Thus XOR can be thought of logically as “one or the other but not both”, or
|
||||
arithmetically as addition [modulo][wm] 2 without carry, since 1 plus 1 equals 0
|
||||
in base-2 arithmetic.
|
||||
|
||||
As is clear from our above example, switching the order of *a* and *b* has no
|
||||
effect on the result. So XOR, like addition, is commutative:
|
||||
|
||||
![]["a ⊕ b = b ⊕ a"]
|
||||
|
||||
And like addition, grouping has no effect on the result. XOR is associative:
|
||||
|
||||
![]["a ⊕ (b ⊕ c) = (a ⊕ b) ⊕ c"]
|
||||
|
||||
But unlike addition, XOR has an extra property: *invertibility.* The result can
|
||||
be switched with any of the operands, making XOR sort of like addition and
|
||||
subtraction rolled into one. Thus, if
|
||||
|
||||
![]["a ⊕ b = c"]
|
||||
|
||||
then
|
||||
|
||||
![]["c ⊕ a = b"]
|
||||
|
||||
![]["b ⊕ c = a"]
|
||||
|
||||
and so forth.
|
||||
|
||||
This last property makes XOR very handy for encryption and decryption. Given
|
||||
a plaintext *P* and a random value *r* with the same bit length as *P*, we
|
||||
encrypt *P* by XOR’ing it with *r* to obtain the ciphertext *C:*
|
||||
|
||||
![]["P ⊕ r = C"]
|
||||
|
||||
To decrypt, we just XOR the ciphertext with *r* to recover the plaintext:
|
||||
|
||||
![]["C ⊕ r = P"]
|
||||
|
||||
The randomness of the ciphertext is guaranteed to be no less than that of the
|
||||
random value. Thus if *r* is perfectly random, then *C* is perfectly
|
||||
undecipherable, given no knowledge of *r.* This is the principle underlying the
|
||||
[one-time pads][otp] used by spies and diplomats before the computer age, as
|
||||
well as modern [stream ciphers][sc].
|
||||
|
||||
To demonstrate how this can be used for seed splitting, all we do is change
|
||||
the names of the variables:
|
||||
|
||||
![]["seed ⊕ share1 = share2"]
|
||||
|
||||
Here *seed* is analogous to the plaintext *P,* *share*<sub>1</sub> is a random
|
||||
value with the same bit length as *seed,* and *share*<sub>2</sub> is the
|
||||
resulting ciphertext. Just as the *C* reveals nothing about *P* in the previous
|
||||
example, *share*<sub>2</sub> reveals nothing about *seed* without knowledge of
|
||||
*share*<sub>1</sub>. To recover the seed, we just XOR the two shares. Since XOR
|
||||
is commutative, the order in which we combine them isn’t important:
|
||||
|
||||
![]["share2 ⊕ share1 = seed"]
|
||||
|
||||
Thanks to XOR’s associativity, splits of any arbitrary length *n* can be created
|
||||
by using *n*-1 random shares, with the *n*-th share being the result of the
|
||||
chained XOR operations:
|
||||
|
||||
Perform an *n*-way split:
|
||||
|
||||
![]["seed ⊕ share1 ⊕ share2 ... ⊕ shareN-1 = shareN"]
|
||||
|
||||
Join shares 1 through *n* to recover the seed:
|
||||
|
||||
![]["share1 ⊕ share2 ... ⊕ shareN = seed"]
|
||||
|
||||
Knowledge of any combination of *n*-1 shares reveals nothing about the seed.
|
||||
|
||||
#### <a name="a_ds">Deterministic Shares</a>
|
||||
|
||||
So we’ve seen that the mathematics behind XOR seed splitting is basically
|
||||
trivial. In practice, though, there are several issues that need to be
|
||||
resolved. For example, how do we obtain the random values for the shares?
|
||||
The easy answer is to just use the random number generator provided by our
|
||||
operating system. Generating the values deterministically is a better solution,
|
||||
however, providing us with two key advantages: 1) we avoid reliance on such
|
||||
factors as the quality of our OS RNG, the underlying hardware, and the entropy
|
||||
pool; and 2) we gain reproducibility—the ability to generate identical shares
|
||||
repeatedly, and as a consequence, to generate shares independently of each
|
||||
other.
|
||||
|
||||
This latter feature is especially useful. Suppose I want to do a 2-way split
|
||||
of my seed for backup purposes, giving one share to my friend Bob and storing
|
||||
the other share at some location accessible to me but unknown to Bob. With
|
||||
deterministic shares, I can generate Bob’s share now, giving it to Bob, and my
|
||||
own share later, once I’ve determined a good location for its safekeeping. And
|
||||
if either of us loses our share, it can just be regenerated.
|
||||
|
||||
OK, so now that we’re sold on the idea of deterministic shares, how do we go
|
||||
about creating them? A naive approach would be to just generate a secure
|
||||
cryptographic hash of our seed using the SHA256 algorithm and use that directly
|
||||
as the first share:
|
||||
|
||||
![]["share1 = SHA256(seed)"]
|
||||
|
||||
This would work fine for a 2-way split: assuming SHA256 is secure and our seed
|
||||
is strongly random (if either assumption is false, we’re in big trouble anyway),
|
||||
then *share*<sub>1</sub> is strongly random too and reveals nothing about our
|
||||
seed. And being derived from the seed alone, it’s regenerable on demand by the
|
||||
seed’s owner.
|
||||
|
||||
For *n*-way splits where *n* is greater than 2, however, we run into a problem
|
||||
when attempting to generate the additional random values. We might be tempted
|
||||
to just keep hashing:
|
||||
|
||||
![]["share2 = SHA256(share1), share3 = SHA256(share2), ..."]
|
||||
|
||||
But you may have already spotted the mistake here: the owner of the first share
|
||||
can generate all the successive shares up to *n*-1. Without the final *n*’th
|
||||
share he can’t recover the seed, but the whole benefit of having the additional
|
||||
shares has been nullified.
|
||||
|
||||
***Important disclaimer:*** *there are other reasons, beyond the scope of this
|
||||
discussion, why using a bare hash of the seed as our random number source might
|
||||
not be a good idea. Bear in mind that this is a simplified **theoretical**
|
||||
introduction, and the examples presented herein are not suitable for
|
||||
implementation in real production code.*
|
||||
|
||||
The above example illustrates what happens when we violate the golden rule of
|
||||
the wallet developer: *never derive a secret from another secret that someone
|
||||
besides the wallet’s owner could potentially gain access to.* This goes for
|
||||
the private keys of the addresses in a wallet, which could be compromised in a
|
||||
security breach. And it certainly goes for seed shares, which are intended for
|
||||
distribution to others from the outset.
|
||||
|
||||
The solution to this problem is to derive the shares directly from the seed, but
|
||||
with an added identifier that’s unique to each share. The [HMAC] message-digest
|
||||
algorithm is ideally suited for this:
|
||||
|
||||
![]["share1 = HMAC(seed,'share1'), share2 = HMAC(seed,'share2'), ... shareN-1 = HMAC(seed,'share<N-1>')"]
|
||||
|
||||
Using these unique pseudorandom values, we can now split and rejoin our seed in
|
||||
the manner described at the end of the previous section.
|
||||
|
||||
#### <a name="a_ns">Named Splits</a>
|
||||
|
||||
Now, we’d like to use seed splitting as part of our backup strategy, entrusting
|
||||
shares of our seed with various people we know. Multiple 2-way splits seems
|
||||
like the best approach—if one of our trustees loses their share, moves to
|
||||
another city, or gets run over by a bus, then we have the others left as a
|
||||
fallback.
|
||||
|
||||
However, we have no mechanism as yet for generating multiple splits: using the
|
||||
deterministic method outlined above, we can only generate the same set of
|
||||
pseudorandom shares over and over again. The obvious solution here is to give
|
||||
each split a name: for our split with Bob, we’ll add “bob” to the share
|
||||
identifier, for our split with Alice, we’ll add “alice”, and so forth. Using
|
||||
this approach, we can create an arbitrary number of uniquely named splits.
|
||||
|
||||
Create a 2-way split with Bob:
|
||||
|
||||
![]["share_me = HMAC(seed,'bob:share1'), share_bob = seed ⊕ share_me"]
|
||||
|
||||
Create a 2-way split with Alice:
|
||||
|
||||
![]["share_me = HMAC(seed,'alice:share1'), share_alice = seed ⊕ share_me"]
|
||||
|
||||
In addition, we should handle the case of multiple splits with different length
|
||||
but the same name. To exclude the reuse of shares in this case, we’ll add an
|
||||
additional identifier field specifying the total number of shares in the split.
|
||||
|
||||
Create a 3-way split “friends” with Bob and Alice:
|
||||
|
||||
![]["share_me = HMAC(seed,'friends:share1:of3'), share_bob = HMAC(seed,'friends:share2:of3'), share_alice = seed ⊕ share_me ⊕ share_bob"]
|
||||
|
||||
Create a 4-way split “friends” with Bob, Alice and Carol:
|
||||
|
||||
![]["share_me = HMAC(seed,'friends:share1:of4'), share_bob = HMAC(seed,'friends:share2:of4'), share_alice = HMAC(seed,'friends:share3:of4'), share_carol = seed ⊕ share_me ⊕ share_bob ⊕ share_alice"]
|
||||
|
||||
Thus we’ve ensured the uniqueness of all shares across all possible splits.
|
||||
|
||||
#### <a name="a_ms">Master Shares</a>
|
||||
|
||||
As the number of splits we create grows, the question of how to store our shares
|
||||
becomes especially problematic. Each new split creates another new share that
|
||||
must be securely stored somewhere. What we need is some mechanism to generate
|
||||
our share of each split from a single master share. This master share would
|
||||
need to be generated and stored just once, saving us a great deal of trouble.
|
||||
|
||||
Having multiple master shares could be useful too. For example, if we ever
|
||||
wanted to revoke our splits, all we’d have to do is destroy our copy of the
|
||||
current master share, ensuring that none of the splits made with its
|
||||
participation could be joined. To create new splits, we’d use the next master
|
||||
share.
|
||||
|
||||
This is all easy to implement using the tools we’re already familiar with from
|
||||
the preceding sections. Using HMAC-SHA256, we’ll generate a range of indexed
|
||||
master shares from our seed as follows:
|
||||
|
||||
![]["master1 = HMAC(seed,'master1'), master2 = HMAC(seed,'master2'), ..."]
|
||||
|
||||
Using master share #1 as our share, our 4-way split “friends” with Bob, Alice
|
||||
and Carol now looks like this:
|
||||
|
||||
![]["share_me = master1, share_bob = HMAC(seed,'friends:share2:of4:master1'), share_alice = HMAC(seed,'friends:share2:of4:master1'), share_carol = seed ⊕ HMAC(master1,'friends:share1:of4') ⊕ share_bob ⊕ share_alice"]
|
||||
|
||||
And rejoining the seed looks like this:
|
||||
|
||||
![]["seed = HMAC(master1,'friends:share1:of4') ⊕ share_bob ⊕ share_alice ⊕ share_carol"]
|
||||
|
||||
The rejoining process now involves more than a simple XOR’ing of shares: The
|
||||
name of the split must be input to the join function, so it should something
|
||||
that’s easy to memorize. If you *really* don’t trust yourself to remember
|
||||
the word “friends”, you could write it down somewhere.
|
||||
|
||||
Also note that an additional field, `master<n>`, has been appended to the share
|
||||
identifiers. This is to ensure that the shares of each master share split are
|
||||
unique, and differ from their non-master-share counterparts.
|
||||
|
||||
### <a name="a_ss">Seed Splitting with MMGen</a>
|
||||
|
||||
The MMGen wallet implements the seed splitting and joining functionality
|
||||
described above via the commands [`mmgen-seedsplit`][SS] and
|
||||
[`mmgen-seedjoin`][SJ]. Usage examples can be found on the `mmgen-seedsplit`
|
||||
help screen.
|
||||
|
||||
Shares can be made from and exported to all supported MMGen wallet formats.
|
||||
This means you can split a BIP39 seed phrase, for example, and output the share
|
||||
back to BIP39 in one easy command:
|
||||
|
||||
# Create share 1 of a 2-way split of the provided BIP39 seed phrase:
|
||||
$ mmgen-seedsplit -o bip39 sample.bip39 1:2
|
||||
|
||||
Each share of a split has a unique share ID. The share IDs are displayed by
|
||||
`mmgen-seedsplit` so that the user may record them for later reference. They
|
||||
may also be viewed with the `mmgen-tool list_shares` command:
|
||||
|
||||
# List the share IDs of a 2-way named split 'alice' of your default wallet:
|
||||
$ mmgen-tool list_shares 2 id_str=alice
|
||||
|
||||
Seed: 71CA5049 (256 bits)
|
||||
Split Type: 2-of-2 (XOR)
|
||||
ID String: alice
|
||||
|
||||
Shares
|
||||
------
|
||||
1: D0BBD210
|
||||
2: 25F0BD65
|
||||
|
||||
# List the share IDs of a 3-way default split of provided BIP39 seed phrase:
|
||||
$ mmgen-tool list_shares 3 wallet=sample.bip39
|
||||
|
||||
Seed: 03BAE887 (128 bits)
|
||||
Split Type: 3-of-3 (XOR)
|
||||
ID String: default
|
||||
|
||||
Shares
|
||||
------
|
||||
1: 83B9AF74
|
||||
2: 109485F4
|
||||
3: 424522DC
|
||||
|
||||
Share IDs are handy for checking the correctness of shares when rejoining a
|
||||
split. Let’s say you’ve decided to rejoin your 2-way split with Alice, whose
|
||||
share you exported to BIP39 format. This can be done by contacting Alice by
|
||||
phone, for example, and having her read the mnemonic phrase to you. If the ID
|
||||
of Alice’s share as displayed by `mmgen-seedjoin` matches the value you recorded
|
||||
when making the split, then you know Alice has given you the correct phrase.
|
||||
|
||||
***Note:*** *when recovering shares over an insecure channel like the telephone,
|
||||
it’s advisable to destroy all copies of your share once you’ve rejoined the
|
||||
seed to safeguard against a possible eavesdropping attacker.*
|
||||
|
||||
Ordinary named splits can easily be rejoined even without the MMGen software.
|
||||
First, each share must be converted to hexadecimal data. If your shares are in
|
||||
BIP39 format, for example, there are command-line tools available to do this.
|
||||
Then a single line of Python code is all that’s required to finish the job:
|
||||
|
||||
$ python3
|
||||
>>> seed_hex = hex(int(share1_hex,16) ^ int(share2_hex,16)) # rejoin a 2-way split
|
||||
|
||||
(Note that the XOR operator in Python is `^`.)
|
||||
|
||||
Unfortunately, rejoining master-share splits is considerably harder to do at
|
||||
the Python command prompt. This is because converting the master share into the
|
||||
temporary share used to make the split involves an additional step, as you’ll
|
||||
recall from the above discussion. In addition, this step is implemented by
|
||||
MMGen somewhat differently than as described above. For advanced users, an
|
||||
example will be provided in a future version of this document.
|
||||
|
||||
[⊕]: https://mmgen.github.io/images/ss/o_xor.svg "⊕"
|
||||
["a: 1 0 0 1 0 1 0 0"]: https://mmgen.github.io/images/ss/byte_a.svg "a: 1 0 0 1 0 1 0 0"
|
||||
["b: 0 1 0 1 1 1 1 0"]: https://mmgen.github.io/images/ss/byte_b.svg "b: 0 1 0 1 1 1 1 0"
|
||||
["a ⊖ b: 1 1 0 0 1 0 1 0"]: https://mmgen.github.io/images/ss/byte_ab.svg "a ⊖ b: 1 1 0 0 1 0 1 0"
|
||||
["a ⊕ b = b ⊕ a"]: https://mmgen.github.io/images/ss/ab-ba.svg "a ⊕ b = b ⊕ a"
|
||||
["a ⊕ (b ⊕ c) = (a ⊕ b) ⊕ c"]: https://mmgen.github.io/images/ss/abc.svg "a ⊕ (b ⊕ c) = (a ⊕ b) ⊕ c"
|
||||
["a ⊕ b = c"]: https://mmgen.github.io/images/ss/ab-c.svg "a ⊕ b = c"
|
||||
["c ⊕ a = b"]: https://mmgen.github.io/images/ss/ca-b.svg "c ⊕ a = b"
|
||||
["b ⊕ c = a"]: https://mmgen.github.io/images/ss/bc-a.svg "b ⊕ c = a"
|
||||
["P ⊕ r = C"]: https://mmgen.github.io/images/ss/Pr-C.svg "P ⊕ r = C"
|
||||
["C ⊕ r = P"]: https://mmgen.github.io/images/ss/Cr-P.svg "C ⊕ r = P"
|
||||
["seed ⊕ share1 = share2"]: https://mmgen.github.io/images/ss/ss-enc.svg "seed ⊕ share1 = share2"
|
||||
["share2 ⊕ share1 = seed"]: https://mmgen.github.io/images/ss/ss-dec.svg "share2 ⊕ share1 = seed"
|
||||
["seed ⊕ share1 ⊕ share2 ... ⊕ shareN-1 = shareN"]: https://mmgen.github.io/images/ss/ssN-enc.svg "seed ⊕ share1 ⊕ share2 ... ⊕ shareN-1 = shareN"
|
||||
["share1 ⊕ share2 ... ⊕ shareN = seed"]: https://mmgen.github.io/images/ss/ssN-dec.svg "share1 ⊕ share2 ... ⊕ shareN = seed"
|
||||
["share1 = SHA256(seed)"]: https://mmgen.github.io/images/ss/sha256.svg "share1 = SHA256(seed)"
|
||||
["share2 = SHA256(share1), share3 = SHA256(share2), ..."]: https://mmgen.github.io/images/ss/sha256b.svg "share2 = SHA256(share1), share3 = SHA256(share2), ..."
|
||||
["share1 = HMAC(seed,'share1'), share2 = HMAC(seed,'share2'), ... shareN-1 = HMAC(seed,'share<N-1>')"]: https://mmgen.github.io/images/ss/hmac.svg "share1 = HMAC(seed,'share1'), share2 = HMAC(seed,'share2'), ... shareN-1 = HMAC(seed,'share<N-1>')"
|
||||
["share_me = HMAC(seed,'bob:share1'), share_bob = seed ⊕ share_me"]: https://mmgen.github.io/images/ss/bob.svg "share_me = HMAC(seed,'bob:share1'), share_bob = seed ⊕ share_me"
|
||||
["share_me = HMAC(seed,'alice:share1'), share_alice = seed ⊕ share_me"]: https://mmgen.github.io/images/ss/alice.svg "share_me = HMAC(seed,'alice:share1'), share_alice = seed ⊕ share_me"
|
||||
["share_me = HMAC(seed,'friends:share1:of3'), share_bob = HMAC(seed,'friends:share2:of3'), share_alice = seed ⊕ share_me ⊕ share_bob"]: https://mmgen.github.io/images/ss/friends1.svg "share_me = HMAC(seed,'friends:share1:of3'), share_bob = HMAC(seed,'friends:share2:of3'), share_alice = seed ⊕ share_me ⊕ share_bob"
|
||||
["share_me = HMAC(seed,'friends:share1:of4'), share_bob = HMAC(seed,'friends:share2:of4'), share_alice = HMAC(seed,'friends:share3:of4'), share_carol = seed ⊕ share_me ⊕ share_bob ⊕ share_alice"]: https://mmgen.github.io/images/ss/friends2.svg "share_me = HMAC(seed,'friends:share1:of4'), share_bob = HMAC(seed,'friends:share2:of4'), share_alice = HMAC(seed,'friends:share3:of4'), share_carol = seed ⊕ share_me ⊕ share_bob ⊕ share_alice"
|
||||
["master1 = HMAC(seed,'master1'), master2 = HMAC(seed,'master2'), ..."]: https://mmgen.github.io/images/ss/master.svg "master1 = HMAC(seed,'master1'), master2 = HMAC(seed,'master2'), ..."
|
||||
["share_me = master1, share_bob = HMAC(seed,'friends:share2:of4:master1'), share_alice = HMAC(seed,'friends:share2:of4:master1'), share_carol = seed ⊕ HMAC(master1,'friends:share1:of4') ⊕ share_bob ⊕ share_alice"]: https://mmgen.github.io/images/ss/friends3.svg "share_me = master1, share_bob = HMAC(seed,'friends:share2:of4:master1'), share_alice = HMAC(seed,'friends:share2:of4:master1'), share_carol = seed ⊕ HMAC(master1,'friends:share1:of4') ⊕ share_bob ⊕ share_alice"
|
||||
["seed = HMAC(master1,'friends:share1:of4') ⊕ share_bob ⊕ share_alice ⊕ share_carol"]: https://mmgen.github.io/images/ss/friends4.svg "seed = HMAC(master1,'friends:share1:of4') ⊕ share_bob ⊕ share_alice ⊕ share_carol"
|
||||
|
||||
<!-- https://mmgen.github.io/images/ss/ -->
|
||||
|
||||
[HMAC]: https://en.wikipedia.org/wiki/HMAC
|
||||
[wm]: https://en.wikipedia.org/wiki/Modular_arithmetic
|
||||
[otp]: https://en.wikipedia.org/wiki/One-time_pad
|
||||
[sc]: https://en.wikipedia.org/wiki/Stream_cipher
|
||||
[SS]: seedsplit-[MMGen-command-help]
|
||||
[SJ]: seedjoin-[MMGen-command-help]
|
||||
Loading…
Add table
Add a link
Reference in a new issue