mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-30 10:08:18 +00:00
150 lines
7.0 KiB
Markdown
150 lines
7.0 KiB
Markdown
# 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.
|
|
- `PassphraseAck.state` was deprecated, and renamed to `_state`. It is retained for
|
|
code compatibility, but the field should never be set.
|
|
- `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.
|
|
|
|
### 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.
|
|
|
|
Note: up to 64 bytes may be required to store the session ID. Firmwares < 2.3.0 use a
|
|
64-byte value, newer firmwares use a 32-byte value.
|