You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Until now, after keystore_unlock(), the seed was present in RAM in
plaintext.
We instead keep the seed in RAM in encrypted form. The enryption key
is stretched using the secure chip, much like the device unlock
password itself is stretched to encrypt the seed for storage in flash
memory. The idea is to keep the decrypted seed in memory for the least
amount of time possible, so: decrypt, use, discard. This reduces the
attack surface and mitigates potential attacks where the RAM could be
accessed by an attacker.
The unstretched key in this case is not the device password, but a
randomly generated key stored in RAM. We cannot use the device
password as we want to be able to decrypt the seed on demand without
prompting the user for the password.
We use the KDF secure chip slot. It is not attached to the monotonic
counter, so it can be used without limitations. One KDF key
stretch (used in encryption and decryption) takes around 100ms.
Alternatives discarded:
- Use a data slot on the securechip to store the encryption
key. Surprisingly, an encrypted read of this slot is slower than
KDF, likely because an encrypted read, in addition to an auth
call (consisting of NONCE an CHECKMAC commands), do a READ, NONCE,
GENDIG and again READ, while KDF after auth only is one KDF
call. Possibly the many roundtrips to the chip make it slower
overall.
- Use SHA instead of KDF: our secure chip configuration does not
permit SHA operations.
This commit performs encryption and on-demand decryption for the
seed. The next commit will do the same for the bip39 seed.
The changed backup.rs unit test is to move mock_unlocked() before
mock_memory(), as mock_memory() overwrites the salt root, which is
needed in the key stretching.
The changed signtx.rs unit test is to move random::mock_reset() after
mock_unlocked(), as the P2TR signature uses random_32_bytes(), which
in unit tests is a deterministic PRNG, which was influenced by calling
random_32_bytes() when creating the seed encryption key in mock_unlocked().
0 commit comments