mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-27 00:28:10 +00:00
106 lines
5.0 KiB
Markdown
106 lines
5.0 KiB
Markdown
|
# Use of SLIP-39 in trezor-core
|
||
|
|
||
|
[SLIP-39](https://github.com/satoshilabs/slips/blob/master/slip-0039.md) describes a way
|
||
|
to securely back up a secret value using Shamir's Secret Sharing scheme.
|
||
|
|
||
|
The secret value, called a Master Secret (**MS**) in SLIP-39 terminology, is first
|
||
|
encrypted by a passphrase, producing an Encrypted Master Secret (**EMS**). The EMS is
|
||
|
then split into a number of shares, which are encoded as a set of mnemonic words.
|
||
|
Afterwards, it is possible to recombine some or all of the shares to obtain back the
|
||
|
EMS, and when the correct passphrase is provided, decrypt the original Master Secret.
|
||
|
|
||
|
This does not quite match Trezor's use of the "passphrase protection" feature, namely
|
||
|
that any passphrase is valid, and using any passphrase will yield a working wallet.
|
||
|
|
||
|
SLIP-39 enables this usage by specifying that passphrases are not validated in any way.
|
||
|
Decrypting an EMS with any passphrase will produce data usable as the Master Secret,
|
||
|
regardless of whether it is the original data or not.
|
||
|
|
||
|
|
||
|
## Seed handling in Trezor
|
||
|
|
||
|
Trezor stores a _mnemonic secret_ in a storage field `_MNEMONIC_SECRET`. This is the
|
||
|
input for the root node derivation process: `mnemonic.get_seed(passphrase)` takes the
|
||
|
user-provided passphrase as an argument, and derives the appropriate root node from the
|
||
|
mnemonic secret.
|
||
|
|
||
|
With BIP-39, the recovery phrase itself is the mnemonic secret. During device
|
||
|
initialization, the raw recovery phrase is given to the user, and also directly stored
|
||
|
in the `_MNEMONIC_SECRET` field. Whenever the root node is required, it is derived by
|
||
|
applying PBKDF2 to the mnemonic secret plus passphrase.
|
||
|
|
||
|
For SLIP-39 it is not practical to store the raw data of the recovery shares. During
|
||
|
device initialization, a random Encrypted Master Secret is generated and stored as
|
||
|
`_MNEMONIC_SECRET`. SLIP-39 encryption parameters (a random identifier and an iteration
|
||
|
exponent) are stored alongside the mnemonic secret in their own storage fields. Whenever
|
||
|
the root node is required, it is derived by "decrypting" the stored mnemonic secret with
|
||
|
the provided passphrase.
|
||
|
|
||
|
|
||
|
## SLIP-39 implementation
|
||
|
|
||
|
The [reference implementation](https://github.com/trezor/python-shamir-mnemonic) of
|
||
|
SLIP-39 provides the following high-level API:
|
||
|
|
||
|
* `generate_mnemonics(group parameters, master_secret, passphrase)`: Encrypt Master
|
||
|
Secret with the provided passphrase, and split into a number of shares defined via
|
||
|
the group parameters.
|
||
|
Implemented using the following:
|
||
|
- `encrypt(master_secret, passphrase, iteration_exponent, identifier)`: Encrypt the
|
||
|
Master Secret with the given passphrase and parameters.
|
||
|
- **`split_ems(group parameters, identifier, iteration_exponent, encrypted_master_secret)`**:
|
||
|
Split the encrypted secret and encode the metadata into a set of shares defined via
|
||
|
the group parameters.
|
||
|
* `combine_mnemonics(set of shares, passphrase)`: Combine the given set of shares to
|
||
|
reconstruct the secret, then decrypt it with the provided passphrase.
|
||
|
Implemented using the following:
|
||
|
- **`recover_ems(set of shares)`**: Combine the given set of shares to obtain the
|
||
|
encrypted master secret, identifier and iteration exponent.
|
||
|
- **`decrypt(encrypted_master_secret, passphrase, iteration_exponent, identifier)`**:
|
||
|
Decrypt the secret with the given passphrase and parameters, to obtain the original
|
||
|
Master Secret.
|
||
|
|
||
|
Only the functions denoted in **bold** are implemented in trezor-core. Recovery shares
|
||
|
are generated with `split_ems` and combined with `recover_ems`. Passphrase decryption is
|
||
|
done with `decrypt`. There is never an original "master secret" to be encrypted, so the
|
||
|
`encrypt` function is also omitted.
|
||
|
|
||
|
|
||
|
## Step-by-step
|
||
|
|
||
|
### Device initialization
|
||
|
|
||
|
This process does not use passphrase.
|
||
|
|
||
|
1. Generate the required number of random bits (128 or 256), and store as
|
||
|
`_MNEMONIC_SECRET`.
|
||
|
2. Generate a random identifier and store as `_SLIP39_IDENTIFIER`.
|
||
|
3. Store the default iteration exponent `1` as `_SLIP39_ITERATION_EXPONENT`.
|
||
|
4. The storage now contains all parameters required for seed derivation.
|
||
|
|
||
|
### Seed derivation
|
||
|
|
||
|
This is the only process that uses passphrase.
|
||
|
|
||
|
1. If passphrase is enabled, prompt user for passphrase. Otherwise use empty string.
|
||
|
2. Use `slip39.decrypt(_MNEMONIC_SECRET, passphrase, _SLIP39_ITERATION_EXPONENT, _SLIP39_IDENTIFIER)`
|
||
|
to "decrypt" the root node that matches the provided passphrase.
|
||
|
|
||
|
### Seed backup
|
||
|
|
||
|
This process does not use passphrase.
|
||
|
|
||
|
1. Prompt user for group parameters (number of groups, number of shares per group, etc.).
|
||
|
2. Use `slip39.split_ems(group parameters, _SLIP39_IDENTIFIER, _SLIP39_ITERATION_EXPONENT, _MNEMONIC_SECRET)` to split the secret into the given number of shares.
|
||
|
|
||
|
### Seed recovery
|
||
|
|
||
|
This process does not use passphrase.
|
||
|
|
||
|
1. Prompt the user to enter enough shares.
|
||
|
2. Use `slip39.recover_ems(shares)` to combine the shares and get metadata.
|
||
|
3. Store the Encrypted Master Secret as `_MNEMONIC_SECRET`.
|
||
|
4. Store the identifier as `_SLIP39_IDENTIFIER`.
|
||
|
5. Store the iteration exponent as `_SLIP39_ITERATION_EXPONENT`.
|
||
|
6. The storage now contains all parameters required for seed derivation.
|