renamed and modified: XOR-Seed-Splitting:-Theory-and-Practice.md

The MMGen Project 2019-11-22 13:20:27 +00:00
commit ec87b538a9
Signed by: mmgen
GPG key ID: 3F8B1861E32B7DA2
2 changed files with 276 additions and 239 deletions

@ -1,239 +0,0 @@
## Contents
+ [N-of-N Seed Splitting: A Theoretical Introduction](#a_nn)
- [Deterministic Shares](#a_ds)
- [Named Splits](#a_ns)
- [Master Shares](#a_ms)
+ [Seed Splitting with MMGen](#a_ss)
### <a name="a_nn">N-of-N 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 XOR
operation:
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 N-of-N 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?
An easy solution would be to just use our OS’s random number generator. A
better one, however, is to generate the values deterministically. This provides
two key advantages: 1) we avoid reliance on OS random numbers, the quality of
which can depend on the hardware, operating system and such factors as the lack
of a good entropy pool on machines without a network connection; and 2) we gain
reproducibility—the ability to identical 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
generate them? A naïve solution is to just create a 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 (and if either assumption is false, we’re in big trouble
anyway), then *share*<sub>1</sub> is strongly random 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 two, however, the problem arises
of how 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 problem 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 point of having a greater than
2-way split has been nullified.
This example illustrates what happens when we violate the golden rule of the
wallet developer: *never derive one secret from another, except from the wallet
seed itself.* The seed belongs solely to the wallet’s owner and is never made
public, but other secrets might belong to others (including by being
accidentally revealed), giving those others access to any secrets that might
be derived from the secrets they possess.
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] algorithm was
created just for this purpose:
![]["share1 = HMAC(seed,'share1'), share2 = HMAC(seed,'share2'), ... shareN-1 = HMAC(seed,'share<N-1>')"]
Using these deterministic 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>
By now it might seem that we’ve laid the necessary theoretical groundwork and
can now proceed to the implementation stage. But actually, we’re just getting
started.
Consider the following situation: 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 share 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 for generating multiple splits: using the
deterministic approach 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 each share’s
unique identifier, for our split with Alice, we’ll add “alice”, and so forth.
To keep things simple for demonstration purposes, we’ll just separate the parts
of the identifier with colons:
![]["share1_bob = HMAC(seed,'bob:share1')"]
![]["2-way split with Bob: seed ⊕ share1_bob = share2_bob"]
In addition, we should handle the case where we have multiple identically named splits
of different length. To avoid reusing shares between the splits, we’ll add an
additional identifier element specifying the total number of shares:
![]["share1of3_friends = HMAC(seed,'friends:share1:of3') ..."]
![]["share1of4_friends = HMAC(seed,'friends:share1:of4') ..."]
![]["Split with 3 friends: seed ⊕ share1of3_friends ⊕ share2of3_friends = share3of3_friends"]
![]["Split with 4 friends: seed ⊕ share1of4_friends ⊕ share2of4_friends ⊕ share3of4_friends = share4of4_friends"]
So now we can create multiple named splits with various people and groups, and
we’ve ensured that all shares of all possible splits are unique.
#### <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 means another new share that we
have to store somewhere. If there were some mechanism to generate our share of
each split from a single master share, this would simplify things enormously for
us. The ability to create several such master shares might come in handy too.
### <a name="a_ss">Seed Splitting with MMGen</a>
The MMGen wallet implements seed-splitting functionality via the commands
`mmgen-seedsplit` and `mmgen-seedjoin`.
TBD
[⊕]: 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>')"
["share1_bob = HMAC(seed,'bob:share1')"]: https://mmgen.github.io/images/ss/bob.svg "share1_bob = HMAC(seed,'bob:share1')"
["2-way split with Bob: seed ⊕ share1_bob = share2_bob"]: https://mmgen.github.io/images/ss/bob2.svg "2-way split with Bob: seed ⊕ share1_bob = share2_bob"
["share1of3_friends = HMAC(seed,'friends:share1:of3') ..."]: https://mmgen.github.io/images/ss/friends1.svg "share1of3_friends = HMAC(seed,'friends:share1:of3') ..."
["share1of4_friends = HMAC(seed,'friends:share1:of4') ..."]: https://mmgen.github.io/images/ss/friends2.svg "share1of4_friends = HMAC(seed,'friends:share1:of4') ..."
["Split with 3 friends: seed ⊕ share1of3_friends ⊕ share2of3_friends = share3of3_friends"]: https://mmgen.github.io/images/ss/friends1a.svg "Split with 3 friends: seed ⊕ share1of3_friends ⊕ share2of3_friends = share3of3_friends"
["Split with 4 friends: seed ⊕ share1of4_friends ⊕ share2of4_friends ⊕ share3of4_friends = share4of4_friends"]: https://mmgen.github.io/images/ss/friends2a.svg "Split with 4 friends: seed ⊕ share1of4_friends ⊕ share2of4_friends ⊕ share3of4_friends = share4of4_friends"
<!-- 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

@ -0,0 +1,276 @@
## 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 XOR
operation:
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 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 your memory, 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 seed-splitting functionality via the commands
`mmgen-seedsplit` and `mmgen-seedjoin`.
TBD
[⊕]: 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