mirror of
https://github.com/trezor/trezor-firmware.git
synced 2024-12-18 12:28:09 +00:00
docs: improve documentation for sessions
This commit is contained in:
parent
ffe3b5487d
commit
5e27c8a274
@ -1,36 +1,129 @@
|
|||||||
# Sessions (the lack of them)
|
# Sessions
|
||||||
|
|
||||||
Currently the communication protocol lacks sessions, which are planned to be introduced in the near future (see [#79](https://github.com/trezor/trezor-firmware/issues/79)).
|
Trezor has limited support for logical sessions. The main purpose is to enable seamless
|
||||||
|
operation with multiple passphrases.
|
||||||
|
|
||||||
To ensure the device is in the expected state we use something called _session_id_. Session id is a 32 bytes long random blob which identifies the internal device state (mainly its caches). This is primarily useful for passphrase to make sure the same passphrase is cached in the device as the one the user entered a few minutes ago. See [passphrase.md](passphrase.md) for more on that.
|
**Warning: Session isolation does not exist.** Host software is responsible for
|
||||||
|
maintaining isolation. Running multiple host-side applications at the same time is not
|
||||||
|
recommended.
|
||||||
|
|
||||||
On first initialization the Host does not have a session id and starts the communication with an empty Initialize:
|
See "Caveats" section below for details.
|
||||||
|
|
||||||
|
Support for isolated sessions is in the works, see
|
||||||
|
[#79](https://github.com/trezor/trezor-firmware/issues/79).
|
||||||
|
|
||||||
|
## Session lifecycle
|
||||||
|
|
||||||
|
After Trezor starts up, no session exists. Any attempt to use session data (i.e., the
|
||||||
|
seed) will be rejected with `InvalidSession` error code.
|
||||||
|
|
||||||
|
New session is started by calling `Initialize` with no arguments. The response is a
|
||||||
|
`Features` message, which contains a 32-byte `session_id`. All subsequent commands
|
||||||
|
happen within the given session.
|
||||||
|
|
||||||
|
To resume a previous session (after creating a new one), call `Initialize` with a stored
|
||||||
|
`session_id` as an argument.
|
||||||
|
|
||||||
|
Attempt to resume an unknown session ID will transparently allocate a new session ID.
|
||||||
|
|
||||||
|
Since firmwares 1.9.4 / 2.3.4, it is possible to destroy the current session via the
|
||||||
|
`EndSession` call. The session and all its associated data is wiped from Trezor memory,
|
||||||
|
and it is impossible to resume the session. Trezor returns to the initial state and
|
||||||
|
all requests will return `InvalidSession`.
|
||||||
|
|
||||||
|
There is no explicit way to leave a session and keep its data for later resumption.
|
||||||
|
Instead, you can switch to a new session via `Initialize` with no arguments.
|
||||||
|
|
||||||
|
At the moment, both T1 and TT allow for 10 sessions to exist at the same time. When a
|
||||||
|
new session needs to be allocated and there is no space in the cache, the least recently
|
||||||
|
used session is evicted.
|
||||||
|
|
||||||
|
Sessions only exist in RAM and are lost when Trezor is disconnected.
|
||||||
|
|
||||||
|
All commands are performed in the context of the current session, until one of the
|
||||||
|
following happens:
|
||||||
|
|
||||||
|
* Host calls `EndSession`. The current session is destroyed and Trezor returns to the
|
||||||
|
initial state.
|
||||||
|
* Host calls `Initialize` with no arguments, or with an unknown `session_id`. A new
|
||||||
|
session is allocated and its id returned in the `Features` message.
|
||||||
|
* Host calls `Initialize` with a known `session_id`. The specified session is resumed
|
||||||
|
and its `session_id` is returned in the `Features` message.
|
||||||
|
* Trezor is disconnected.
|
||||||
|
|
||||||
|
## Caveats
|
||||||
|
|
||||||
|
* Sessions only exist on the protobuf message level. **There is no proper isolation.**
|
||||||
|
Multiple host applications can insert commands into each other's sessions.
|
||||||
|
|
||||||
|
It is recommended to send `Initialize` to resume a session immediately before each
|
||||||
|
flow. However, even this does not guarantee that another application doesn't insert
|
||||||
|
its own `Initialize` in the time it takes you to send the next command.
|
||||||
|
|
||||||
|
The reverse is also true: session management does not prevent other applications from
|
||||||
|
inserting commands under the currently active session (and therefore passphrase),
|
||||||
|
without knowledge of the session ID or the passphrase.
|
||||||
|
|
||||||
|
* It is impossible to run complex flows concurrently. If an application is in the middle
|
||||||
|
of Bitcoin signing, sending `Initialize` will cancel the signing flow. Resuming the
|
||||||
|
appropriate session later will _not_ continue where it left off.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Allocate a new session, perform a command, and end the session:
|
||||||
```
|
```
|
||||||
Initialize()
|
Initialize()
|
||||||
---------> Features(..., session_id)
|
---------> Features(..., session_id=AAAA)
|
||||||
<---------
|
<---------
|
||||||
|
---<now in session AAAA>---
|
||||||
|
Request
|
||||||
|
---------> Response
|
||||||
|
<---------
|
||||||
|
EndSession()
|
||||||
|
---------> Success()
|
||||||
|
<---------
|
||||||
|
---<now in no session>---
|
||||||
```
|
```
|
||||||
|
|
||||||
After the first Features message is received the Host might store the session_id. To ensure the device state Host must send the Initialize message again including that particular session_id:
|
Allocate two new sessions, resume the first one later:
|
||||||
|
|
||||||
```
|
```
|
||||||
Initialize(session_id)
|
Initialize()
|
||||||
---------> Features(..., session_id)
|
---------> Features(..., session_id=AAAA)
|
||||||
<---------
|
<---------
|
||||||
|
---<now in session AAAA>---
|
||||||
|
Request
|
||||||
|
---------> Response
|
||||||
|
<---------
|
||||||
|
|
||||||
|
Initialize()
|
||||||
|
---------> Features(..., session_id=BBBB)
|
||||||
|
<---------
|
||||||
|
---<now in session BBBB>---
|
||||||
|
Request
|
||||||
|
---------> Response
|
||||||
|
<---------
|
||||||
|
|
||||||
|
Initialize(session_id=AAAA)
|
||||||
|
---------> Features(..., session_id=AAAA)
|
||||||
|
<---------
|
||||||
|
---<now in session AAAA>---
|
||||||
Request
|
Request
|
||||||
---------> Response
|
---------> Response
|
||||||
<---------
|
<---------
|
||||||
```
|
```
|
||||||
|
|
||||||
So to make sure the device state has not changed, the Host must send the Initialize message with the correctly stored session_id before each request. Yes, this is stupid.
|
Attempt to resume session that is not in the cache:
|
||||||
|
```
|
||||||
As mentioned, sessions will be introduced soon™ and fix that. We will probably take the first few bytes of the session_id, declare it a session id, and the rest will remain, without the annoying requirement of sending Initialize before every message.
|
Initialize()
|
||||||
|
---------> Features(..., session_id=AAAA)
|
||||||
----
|
<---------
|
||||||
|
---<now in session AAAA>---
|
||||||
The session is terminated and therefore the caches are cleared if:
|
EndSession()
|
||||||
- Initialize.session_id is empty.
|
---------> Success()
|
||||||
- Initialize.session_id is different then the one cached in Trezor.
|
<---------
|
||||||
- Trezor is replugged (session is not persistent).
|
---<now in no session>---
|
||||||
- ClearSession is received.
|
Initialize(session_id=AAAA)
|
||||||
|
---------> Features(..., session_id=BBBB)
|
||||||
|
<---------
|
||||||
|
---<now in session BBBB>---
|
||||||
|
```
|
||||||
|
Loading…
Reference in New Issue
Block a user