diff --git a/docs/common/communication/passphrase-redesign-migration.md b/docs/common/communication/passphrase-redesign-migration.md new file mode 100644 index 0000000000..68631a21b0 --- /dev/null +++ b/docs/common/communication/passphrase-redesign-migration.md @@ -0,0 +1,146 @@ + +# Passphrase Redesign In 1.9.0 / 2.3.0 + +On the T1, passphrase must be entered on the host PC and sent to Trezor. On the TT, the +user can choose whether to enter the passphrase on host or on Trezor's touch screen. + +In versions 1.9.0 and 2.3.0 we have redesigned how the passphrase is +communicated between the Host and the Device. The new design is documented +thoroughly in [passphrase.md](passphrase.md) and this document should help +with the transition from the old design. + +## New features + +* Passphrase flow is now identical for T1 and TT. +* By keeping track of _sessions_, it is possible to avoid having to send passphrase repeatedly. +* The choice between entering on Host or Device for TT has been moved from Device to Host. +* Multiple passphrases are cached simultaneously. + +## Backwards compatibility + +T1 1.9.0 is fully backwards-compatible and works with existing Host code. + +TT 2.3.0 communicating with old Host software degrades to T1-level features: entering +passphrase on device will not be available, and on-device passphrase caching via the +`state` field will not be available. + +As a workaround, it is possible to use the "passphrase always on device" feature on the +new TT firmware. When enabled, the passphrase flow is completely hidden from the Host +software, and the Device itself prompts the user to enter the passphrase. + +## Implementation guide + +### Protobuf changes + +Protobuf has built-in backwards compatibility mechanisms, so a conforming implementation +should continue to work with old protobuf definitions. + +To restore support for TT on-device passphrase entry, and to make use of the new +features, you will need to update to newer protobuf definitions from `trezor-common` +(TODO: link to commit in trezor-common). + +The gist of the changes is: + +- `PassphraseRequest.on_device` was deprecated, and renamed to `_on_device`. New Devices + will never send this field. +- Corresponding field `PassphraseAck.on_device` was added. +- `PassphraseStateRequest`/`PassphraseStateAck` messages were deprecated, and renamed + with a `Deprecated_` prefix. New Devices will not send or accept these messages. +- `Initialize.state` was renamed to `Initialize.session_id`. +- Corresponding field `Features.session_id` was added. New Devices will always send this + field in response to `Initialize` call. +- A new value `Capability_PassphraseEntry` was added to the `Features.Capability` + enum. This capability will be sent from a Device that supports on-device passphrase + entry (currently only TT). + +### Restoring on-device entry for TT + +The Host software reacts to a `PassphraseRequest` message by prompting the user for a +passphrase and sending it in the `PassphraseAck.passphrase` field. + +A new UI element should be added: when the passphrase prompt is displayed on Host, there +should be an option to "enter passphrase on device". When the user selects this option, +the Host must send a `PassphraseAck(passphrase=null, on_device=true)`. + +The "enter passphrase on device" option should be displayed when `Features.capabilities` +contain the `Capability_PassphraseEntry` value, regardless of reported Trezor version or +model. Firmwares older than 2.3.0 or 1.9.0 never set this value, so this ensures +forwards and backwards compatibility. + +### Cross-version compatibility for TT + +TT version \< 2.3.0 will send `PassphraseRequest(_on_device=true)` if the user selected +on-device entry. Neither T1 nor TT >= 2.3.0 will ever set this field to true. + +If the Host receives `PassphraseRequest(_on_device=true)`, it should immediately respond +with `PassphraseAck()` with no fields set. + +TT version \< 2.3.0 will send `Deprecated_PassphraseStateRequest(state=[bytes])` after +receiving `PassphraseAck`. The Host should immediately respond with +`Deprecated_PassphraseStateAck()` with no fields set. If the Host does session +management, it should store the value of `state` as the session ID. Note that `state` +used to be 64 bytes long, but `session_id` is only 32 bytes. + +### Triggering passphrase prompt + +Use `GetAddress(coin_name="Testnet", address_n=[44'/1'/0'/0/0])` (the first address of +the first account of Testnet) to ensure that the Device asks for a passphrase if +needed, and caches it for future use. + +### Validating passphrases + +You can store the result of the above call, and in the future, compare it to a newly +received address. This is a good way to check if the user is using the same passphrase +as last time. + +Do not store user-entered passphrases for the purpose of validation, even in hashed, +encrypted, or otherwise obfuscated format. + +### Session support + +A call to `Initialize` can include a `session_id` field. When starting a new user +session, this field should be left empty. + +The response `Features` message will always include a `session_id` field. The value of +this field should be stored. When calling `Initialize` again, the stored value should +be sent as `session_id`. If the received `Features.session_id` is the same, it means +that session was resumed successfully and the user will not be prompted for passphrase. + +``` +--> Initialize() +<-- Features(session_id=0xABCDEF, ...) + +--> Initialize(session_id=0xABCDEF) +<-- Features(session_id=0xABCDEF) +# (session resumed successfully) + +--> Initialize(session_id=0xABCDEF) +<-- Features(session_id=0x123456) +# (session was not resumed, user will be prompted for passphrase again) +``` + +Session support is identical on T1 and TT, and both models support multiple sessions, +i.e., it is possible to seamlessly switch between using multiple passphrases. + +### Cross-version compatible algorithm summary + +The following algorithm will ensure that your Host application works properly with +both T1 and TT with both older and newer firmwares. + +1. If you have a session ID stored, call `Initialize(session_id=stored_session_id)` +2. Check the value of `Features.session_id`. If it is identical to `stored_session_id`, + the session was resumed and user will not need to be prompted for a passphrase. + 1. If `Features.session_id` is not set, you are communicating with an older Device. + Do not store the null value as session ID. + 2. Otherwise store the value as `stored_session_id`. +3. When you receive a `PassphraseRequest(_on_device=true)`, respond with + `PassphraseAck()` with no fields set. +4. When you receive a `PassphraseRequest`, prompt the user for passphrase. + 1. If `Features.capabilities` contains value `Capability_PassphraseEntry`, display a + UI element that allows the user to enter passphrase on-device. + 2. If the user chooses this option, send `PassphraseAck(passphrase=null, on_device=true)` + 3. If the user enters the passphrase in your application, send + `PassphraseAck(passphrase="user entered passphrase", on_device=false)` +5. When you receive a `Deprecated_PassphraseStateRequest(state=...)`, store the value + of `state` as `stored_session_id`, and respond with `Deprecated_PassphraseStateAck` + with no fields set.