SLIP-39 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.
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.
The reference implementation 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.
This process does not use passphrase.
- Generate the required number of random bits (128 or 256), and store as
_MNEMONIC_SECRET
. - Generate a random identifier and store as
_SLIP39_IDENTIFIER
. - Store the default iteration exponent
1
as_SLIP39_ITERATION_EXPONENT
. - The storage now contains all parameters required for seed derivation.
This is the only process that uses passphrase.
- If passphrase is enabled, prompt user for passphrase. Otherwise use empty string.
- Use
slip39.decrypt(_MNEMONIC_SECRET, passphrase, _SLIP39_ITERATION_EXPONENT, _SLIP39_IDENTIFIER)
to "decrypt" the root node that matches the provided passphrase.
This process does not use passphrase.
- Prompt user for group parameters (number of groups, number of shares per group, etc.).
- Use
slip39.split_ems(group parameters, _SLIP39_IDENTIFIER, _SLIP39_ITERATION_EXPONENT, _MNEMONIC_SECRET)
to split the secret into the given number of shares.
This process does not use passphrase.
- Prompt the user to enter enough shares.
- Use
slip39.recover_ems(shares)
to combine the shares and get metadata. - Store the Encrypted Master Secret as
_MNEMONIC_SECRET
. - Store the identifier as
_SLIP39_IDENTIFIER
. - Store the iteration exponent as
_SLIP39_ITERATION_EXPONENT
. - The storage now contains all parameters required for seed derivation.