Skip to content

Commit

Permalink
slip-0039: Add extendable backup flag.
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewkozlik committed May 9, 2024
1 parent 9118b20 commit 8d06070
Showing 1 changed file with 29 additions and 18 deletions.
47 changes: 29 additions & 18 deletions slip-0039.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,14 @@ Notation | Meaning
*N<sub>i</sub>* | total number of members in group *i*, a positive integer, 1 &le; *N<sub>i</sub>* &le; 16
*GT* | group threshold, a positive integer, 1 &le; *GT* &le; *G*
*T<sub>i</sub>* | member threshold for group *i*, a positive integer, 1 &le; *T<sub>i</sub>* &le; *N<sub>i</sub>*
*id* | random identifier, a 15-bit positive integer
*MS* | master secret, a string
*id* | random identifier, a 15-bit unsigned integer
*ext* | extendable backup flag
*e* | iteration exponent, a 4-bit unsigned integer
*MS* | master secret, an octet string
*n* | length of the master secret in bytes
*EMS* | encrypted master secret, a string
*EMS* | encrypted master secret, an octet string
&#124;&#124; | concatenation operator
xor | bit-wise exclusive-or of two strings
xor | bit-wise exclusive-or of two octet strings

## Motivation

Expand Down Expand Up @@ -91,19 +93,20 @@ If the member threshold *T<sub>i</sub>* of a group is 1, then the size *N<sub>i<

We propose the following format of the shares:

| Identifier (*id*) | Iteration exponent (*e*) | Group index (*GI*) | Group threshold (*Gt*) | Group count (*g*) | Member index (*I*) | Member threshold (*t*) | Padded share value (*ps*) | Checksum (*C*) |
|---------|--------|--------|--------|--------|--------|--------|---------------------|---------|
| 15 bits | 5 bits | 4 bits | 4 bits | 4 bits | 4 bits | 4 bits | padding + 8*n* bits | 30 bits |
| Identifier (*id*) | Extendable (*ext*) | Iteration exponent (*e*) | Group index (*GI*) | Group threshold (*Gt*) | Group count (*g*) | Member index (*I*) | Member threshold (*t*) | Padded share value (*ps*) | Checksum (*C*) |
|---------|-------|--------|--------|--------|--------|--------|--------|---------------------|---------|
| 15 bits | 1 bit | 4 bits | 4 bits | 4 bits | 4 bits | 4 bits | 4 bits | padding + 8*n* bits | 30 bits |

* The **identifier** (*id*) field is a random 15-bit value which is the same for all shares and is used to verify that the shares belong together; it is also used as salt in the encryption of the master secret.
* The **identifier** (*id*) field is a random 15-bit value which is the same for all shares and is used to verify that the shares belong together.
* The **extendable backup flag** (*ext*) field<sup>[10](#ExtendableBackup)</sup> indicates that the *id* is used as salt in the encryption of the master secret when *ext* = 0.
* The **iteration exponent** (*e*) field indicates the total number of iterations to be used in PBKDF2. The number of iterations is calculated as 10000&times;2<sup>*e*</sup>.
* The **group index** (*GI*) field<sup>[3](#IndexEncoding)</sup> is the *x* value of the group share.
* The **group threshold** (*Gt*) field<sup>[3](#IndexEncoding)</sup> indicates how many group shares are needed to reconstruct the master secret. The actual value is encoded as *Gt* = *GT* &minus; 1, so a value of 0 indicates that a single group share is needed (*GT* = 1), a value of 1 indicates that two group shares are needed (*GT* = 2) etc.
* The **group count** (*g*) indicates the total number of groups. The actual value is encoded as *g* = *G* &minus; 1.
* The **member index** (*I*) field<sup>[3](#IndexEncoding)</sup> is the *x* value of the member share in the given group.
* The **member threshold** (*t*) field<sup>[3](#IndexEncoding)</sup> indicates how many member shares are needed to reconstruct the group share. The actual value is encoded as *t* = *T* &minus; 1.
* The **padded share value** (*ps*) field corresponds to a list of the SSS part's *f<sub>k</sub>*(*x*) values (see the diagram above), 1 &le; *k* &le; *n*. Each *f<sub>k</sub>*(*x*) value is encoded as a string of eight bits in big-endian order. The concatenation of these bit strings is the share value. This value is left-padded with "0" bits so that the length of the padded share value in bits becomes the nearest multiple of 10.
* The **checksum** (*C*) field is an RS1024 checksum (see [below](#checksum)) of the data part of the share (that is *id* || *e* || *GI* || *Gt* || *g* || *I* || *t* || *ps*). The customization string (*cs*) of RS1024 is "shamir".
* The **checksum** (*C*) field is an RS1024 checksum (see [below](#checksum)) of the data part of the share (that is *id* || *ext* || *e* || *GI* || *Gt* || *g* || *I* || *t* || *ps*). The customization string (*cs*) of RS1024 is "shamir" if *ext* = 0 and "shamir_extendable" if *ext* = 1.

This structure is then converted into a mnemonic code by splitting it up into 10-bit segments with each becoming an index into a word list containing exactly 1024 words (see [below](#wordlist)). Big-endian bit order is used in all conversions. The entropy<sup>[4](#Digest)</sup> of the master secret MUST be at least 128 bits and its length MUST be a multiple of 16 bits. All implementations MUST support master secrets of length 128 bits and 256 bits:

Expand Down Expand Up @@ -171,10 +174,11 @@ The source of randomness used to generate the values in steps 3 and 4 above MUST

1. If *T<sub>i</sub>* = 1 and *N<sub>i</sub>* &gt; 1 for any *i*, then abort.
2. Generate a random 15-bit value *id*.
3. Compute the encrypted master secret *EMS* = Encrypt(*MS*, *P*, *e*, *id*).
4. Compute the group shares *s*<sub>1</sub>, ... , *s<sub>G</sub>* = SplitSecret(*GT*, *G*, *EMS*).
5. For each group share *s<sub>i</sub>*, 1 &le; *i* &le; *G*, compute the member shares *s*<sub>*i*,1</sub>, ... , *s*<sub>*i*,*N<sub>i</sub>*</sub> = SplitSecret(*T<sub>i</sub>*, *N<sub>i</sub>*, *s<sub>i</sub>*).
6. For each *i* and each *j*, 1 &le; *i* &le; *G*, 1 &le; *j* &le; *N<sub>i</sub>*, return (*id*, *e*, *i* &minus; 1, *GT* &minus; 1, *j* &minus; 1, *T<sub>i</sub>* &minus; 1, *s<sub>i,j</sub>*).
3. Let *ext* = 1.
4. Compute the encrypted master secret *EMS* = Encrypt(*MS*, *P*, *e*, *id*, *ext*).
5. Compute the group shares *s*<sub>1</sub>, ... , *s<sub>G</sub>* = SplitSecret(*GT*, *G*, *EMS*).
6. For each group share *s<sub>i</sub>*, 1 &le; *i* &le; *G*, compute the member shares *s*<sub>*i*,1</sub>, ... , *s*<sub>*i*,*N<sub>i</sub>*</sub> = SplitSecret(*T<sub>i</sub>*, *N<sub>i</sub>*, *s<sub>i</sub>*).
7. For each *i* and each *j*, 1 &le; *i* &le; *G*, 1 &le; *j* &le; *N<sub>i</sub>*, return (*id*, *ext*, *e*, *i* &minus; 1, *GT* &minus; 1, *j* &minus; 1, *T<sub>i</sub>* &minus; 1, *s<sub>i,j</sub>*).

### Combining the shares

Expand All @@ -184,7 +188,7 @@ The source of randomness used to generate the values in steps 3 and 4 above MUST

1. Check the following conditions:
* The checksum of each share MUST be valid. Implementations SHOULD NOT implement correction beyond potentially suggesting to the user where in the mnemonic an error might be found, without suggesting the correction to make<sup>[5](#ChecksumDesign)</sup>.
* All shares MUST have the same identifier *id*, iteration exponent *e*, group threshold *GT*, group count *G* and length. The value of *G* MUST be greater than or equal to *GT*.
* All shares MUST have the same identifier *id*, extendable backup flag *ext*, iteration exponent *e*, group threshold *GT*, group count *G* and length. The value of *G* MUST be greater than or equal to *GT*.
* Let *GM* be the number of pairwise distinct group indices among the given shares. Then *GM* MUST be equal to *GT*.
* All shares with a given group index *GI<sub>i</sub>*, 1 &le; *i* &le; *GM*, MUST have the same member threshold *T<sub>i</sub>*, their member indices MUST be pairwise distinct and their count *M<sub>i</sub>* MUST be equal to *T<sub>i</sub>*.
* The length of the padding of the share value in bits, which is equal to the length of the padded share value in bits modulo 16, MUST NOT exceed 8 bits.
Expand All @@ -197,7 +201,7 @@ The source of randomness used to generate the values in steps 3 and 4 above MUST

3. Let *EMS* = RecoverSecret([(*GI*<sub>1</sub>, *s*<sub>1</sub>), ... , (*GI<sub>GM</sub>*, *s<sub>GM</sub>*)])

4. Return *MS* = Decrypt(*EMS*, *P*, *e*, *id*).
4. Return *MS* = Decrypt(*EMS*, *P*, *e*, *id*, *ext*).

## Checksum

Expand Down Expand Up @@ -254,12 +258,13 @@ The encrypted master secret is then `EMS = R || L`.
The *i*-th round function `F(i, R)` is defined as follows:

```
F(i, R) = PBKDF2(PRF = HMAC-SHA256, Password = (i || passphrase), Salt = ("shamir" || id || R), iterations = 2500 << e, dkLen = n/2 bytes)
F(i, R) = PBKDF2(PRF = HMAC-SHA256, Password = (i || passphrase), Salt = (salt_prefix || R), iterations = 2500 << e, dkLen = n/2 bytes)
```

The value of *i* is encoded as one byte.

The random identifier value *id* is encoded as two bytes in big-endian byte order.
If *ext* = 1, then `salt_prefix` is an empty string.
If *ext* = 0, then `salt_prefix = "shamir" || id`, where the random identifier value *id* is encoded as two bytes in big-endian byte order.

## Decryption of the master secret

Expand Down Expand Up @@ -303,7 +308,7 @@ This specification is required to ensure that SLIP-0039 backups created in one w

## Test vectors

The test vectors are given as a list of triples. The first member of the triple is a description of the test vector, the second member is a list of mnemonics and the third member is the master secret which results from combining the mnemonics. The master secret is encoded as a string containing two hexadecimal digits for each byte. If the string is empty, then attempting to combine the given set of mnemonics should result in an error. The passphrase "TREZOR" is used for all valid sets of mnemonics.
The test vectors are given as a list of quadruples. The first element of the quadruple is a description of the test vector, the second is a list of mnemonics, the third is the master secret which results from combining the mnemonics and the fourth is the BIP32 master extended private key derived from the master secret. The master secret is encoded as a string containing two hexadecimal digits for each byte. If the string is empty, then attempting to combine the given set of mnemonics should result in an error. The passphrase "TREZOR" is used for all valid sets of mnemonics.

<http://github.com/trezor/python-shamir-mnemonic/blob/master/vectors.json>

Expand Down Expand Up @@ -403,6 +408,12 @@ Python wallets with SLIP39 support:

Some individuals have expressed a concern that the inability to convert SLIP-0039 shares to BIP-0039 may lead to vendor lock-in due to the slow adoption of SLIP-0039 by hardware wallet vendors. This concern is unwarranted, since even if the conversion to BIP-0039 were possible and a user needed to recover their seed onto a device which does not support SLIP-0039, then they would need to use some conversion tool running on their computer. In that case, they might as well simply recover their SLIP-0039 shares in a software wallet running on their computer and send all of their funds to a new seed on their new device. Thus the ability to convert shares to a BIP-0039 mnemonic makes no difference in this respect.

10. <a name="ExtendableBackup"></a>**Extendable backup flag**

When this flag is set, it indicates that the *id* is not used as salt in the encryption of the master secret, making it possible to create multiple sets of shares, such that each set of shares uses a different *id* and each set of shares leads to the same master secret for every passphrase. This is a desirable property, because it allows users to start working with their wallet by creating a single-share (1-of-1) scheme and later upgrade to a multi-share scheme while maintaining the same *EMS* and passphrases. In case the user upgrades two or more times to a multi-share scheme, then each set of shares will in all likelihood have a distinct *id*. Thus it will be possible to tell the sets apart and avoid their accidental mixing, which could potentially lead to loss of access to funds, since shares from different sets are incompatible even if they use the same scheme.

The *ext* flag was added in a later revision of this specification. Previously *ext* was the highest bit of a 5-bit iteration exponent. In newly created shares *ext* SHOULD be set to 1. However, since some users already have shares that use *ext* = 0, implementations MUST support master secret decryption using both *ext* values. If *ext* = 0, then implementations SHOULD NOT allow the intentional creation of another set of shares with the same *id* and *EMS*. Doing so would run the risk of users trying to recover shares from incompatible groups, which is something that implementations are not required to handle.

## References

* [BIP-0032: Hierarchical Deterministic Wallets](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki)
Expand Down

0 comments on commit 8d06070

Please sign in to comment.