From f491239c260e5c1973a163db17afd88e59042416 Mon Sep 17 00:00:00 2001 From: matejcik Date: Thu, 12 Dec 2019 13:29:14 +0100 Subject: [PATCH] core: add documentation for SLIP-39 handling in Trezor --- docs/core/misc/slip0039.md | 105 +++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 docs/core/misc/slip0039.md diff --git a/docs/core/misc/slip0039.md b/docs/core/misc/slip0039.md new file mode 100644 index 0000000000..f3deebd2d7 --- /dev/null +++ b/docs/core/misc/slip0039.md @@ -0,0 +1,105 @@ +# 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.