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 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 aDeprecated_
prefix. New Devices will not send or accept these messages.Initialize.state
was renamed toInitialize.session_id
.- Corresponding field
Features.session_id
was added. New Devices will always send this field in response toInitialize
call. - A new value
Capability_PassphraseEntry
was added to theFeatures.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.
- If you have a session ID stored, call
Initialize(session_id=stored_session_id)
- Check the value of
Features.session_id
. If it is identical tostored_session_id
, the session was resumed and user will not need to be prompted for a passphrase.- If
Features.session_id
is not set, you are communicating with an older Device. Do not store the null value as session ID. - Otherwise store the value as
stored_session_id
.
- If
- When you receive a
PassphraseRequest(_on_device=true)
, respond withPassphraseAck()
with no fields set. - When you receive a
PassphraseRequest
, prompt the user for passphrase.- If
Features.capabilities
contains valueCapability_PassphraseEntry
, display a UI element that allows the user to enter passphrase on-device. - If the user chooses this option, send
PassphraseAck(passphrase=null, on_device=true)
- If the user enters the passphrase in your application, send
PassphraseAck(passphrase="user entered passphrase", on_device=false)
- If
- When you receive a
Deprecated_PassphraseStateRequest(state=...)
, store the value ofstate
asstored_session_id
, and respond withDeprecated_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.