mirror of
https://github.com/tillitis/tillitis-key1.git
synced 2024-12-20 13:24:24 -05:00
Update documentation according to new firmware protocol
This commit is contained in:
parent
c80dc53027
commit
632e21886d
@ -13,8 +13,9 @@ Learn more about the concepts in the
|
|||||||
|
|
||||||
## CPU
|
## CPU
|
||||||
|
|
||||||
We use a PicoRV32, a 32-bit RISC-V system, as the CPU for running the
|
We use a PicoRV32, a 32-bit RISC-V system (RV32IMC), as the CPU for
|
||||||
firmware and the loaded app. All types are little-endian.
|
running the firmware and the loaded app. The firmware and device app
|
||||||
|
both run in machine mode. All types are little-endian.
|
||||||
|
|
||||||
## Constraints
|
## Constraints
|
||||||
|
|
||||||
@ -34,7 +35,7 @@ specifications:
|
|||||||
|
|
||||||
The Tillitis Key has two modes of operation; firmware/loader mode and
|
The Tillitis Key has two modes of operation; firmware/loader mode and
|
||||||
application mode. The firmware mode has the responsibility of
|
application mode. The firmware mode has the responsibility of
|
||||||
receiving, measuring, and loading the application.
|
receiving, measuring, loading, and starting the application.
|
||||||
|
|
||||||
The firmware and application uses a memory mapped IO for SoC
|
The firmware and application uses a memory mapped IO for SoC
|
||||||
communication. This MMIO resides at `0xc000_0000`. *Nota bene*: Almost
|
communication. This MMIO resides at `0xc000_0000`. *Nota bene*: Almost
|
||||||
@ -44,21 +45,19 @@ The application has a constrained variant of the firmware memory map,
|
|||||||
which is outlined below. E.g. UDS isn't readable, and the `APP_{ADDR,
|
which is outlined below. E.g. UDS isn't readable, and the `APP_{ADDR,
|
||||||
SIZE}` are not writable for the application.
|
SIZE}` are not writable for the application.
|
||||||
|
|
||||||
The software on the Tillitis Key communicates to the host via the
|
The firmware (and optionally all software) on the Tillitis Key
|
||||||
`UART_{RX,TX}_{STATUS,DATA}` registers, using the framing protocol
|
communicates to the host via the `UART_{RX,TX}_{STATUS,DATA}`
|
||||||
described in [Framing
|
registers, using the framing protocol described in [Framing
|
||||||
Protocol](../framing_protocol/framing_protocol.md).
|
Protocol](../framing_protocol/framing_protocol.md).
|
||||||
|
|
||||||
The firmware defines a protocol (command/response interface) on top of
|
The firmware defines a protocol (command/response interface) on top of
|
||||||
the framing layer, which is used to bootstrap the application onto the
|
this framing layer which is used to bootstrap the application onto the
|
||||||
device.
|
device. All commands are initiated by the host. All commands receive a
|
||||||
|
reply.
|
||||||
|
|
||||||
On the framing layer, it's required that each frame the device
|
Applications define their own per-application protocol used for
|
||||||
receives, a responding frame must be sent back to the host, in a
|
communication with their host part. They may or may not be based on
|
||||||
ping-pong manner.
|
the Framing Protocol.
|
||||||
|
|
||||||
Applications define a per-application protocol, which is the contract
|
|
||||||
between the host and the device.
|
|
||||||
|
|
||||||
## Firmware
|
## Firmware
|
||||||
|
|
||||||
@ -86,40 +85,48 @@ The purpose of the firmware is to bootstrap an application. The host
|
|||||||
will send a raw binary targeted to be loaded at `0x4000_7000` in the
|
will send a raw binary targeted to be loaded at `0x4000_7000` in the
|
||||||
device.
|
device.
|
||||||
|
|
||||||
1. The host sends the User Supplied Secret (USS) by using the
|
1. The host sends the `FW_CMD_LOAD_APP` command with the size of the
|
||||||
`FW_CMD_LOAD_USS` command and gets a `FW_RSP_LOAD_USS` back.
|
device app and the user-supplied secret as arguments and and gets
|
||||||
2. The host sends the size of the app by using the
|
a `FW_RSP_LOAD_APP` back.
|
||||||
`FW_CMD_LOAD_APP_SIZE` command.
|
2. If the the host receive a sucessful response, it will send
|
||||||
3. The firmware executes `FW_CMD_LOAD_APP_SIZE` command, which
|
|
||||||
stores the application size into `APP_SIZE`, and sets `APP_ADDR`
|
|
||||||
to zero. A `FW_RSP_LOAD_APP_SIZE` reponse is sent back to the
|
|
||||||
host, with the status of the action (ok/fail).
|
|
||||||
4. If the the host receive a sucessful response, it will send
|
|
||||||
multiple `FW_CMD_LOAD_APP_DATA` commands, together containing the
|
multiple `FW_CMD_LOAD_APP_DATA` commands, together containing the
|
||||||
full application.
|
full application.
|
||||||
5. For each received `FW_CMD_LOAD_APP_DATA` command the firmware
|
3. On receiving`FW_CMD_LOAD_APP_DATA` commands the firmware places
|
||||||
places the data into `0x4000_7000` and upwards. The firmware
|
the data into `0x4000_7000` and upwards. The firmware replies
|
||||||
replies with a `FW_RSP_LOAD_APP_DATA` response to the host for
|
with a `FW_RSP_LOAD_APP_DATA` response to the host for each
|
||||||
each received block.
|
received block except the last data block.
|
||||||
6. When the final block of the application image is received, we
|
4. When the final block of the application image is received with a
|
||||||
measure the application by computing a BLAKE2s digest over the
|
`FW_CMD_LOAD_APP_DATA`, the firmware measure the application by
|
||||||
entire application,
|
computing a BLAKE2s digest over the entire application. Then
|
||||||
|
firmware send back the `FW_RSP_LOAD_APP_DATA_READY` response
|
||||||
|
containing the measurement.
|
||||||
|
5. The Compound Device Identifier (CDI) is then computed by using
|
||||||
|
the `UDS`, the measurement of the application, and the `USS`, and
|
||||||
|
placed in the `CDI` register. Then the start address of the
|
||||||
|
device app, `0x4000_7000`, is written to `APP_ADDR` and the size
|
||||||
|
to `APP_SIZE` to let the device application know where it is
|
||||||
|
loaded and how large it is, if it wants to relocate in RAM.
|
||||||
|
6. The firmware now starts the application by switching to
|
||||||
|
application mode by writing to the `SWITCH_APP` register. In this
|
||||||
|
mode the MMIO region is restricted; e.g. some registers are
|
||||||
|
removed (`UDS`), and some are switched from read/write to
|
||||||
|
read-only. This is outlined in the memory map below.
|
||||||
|
|
||||||
The Compound Device Identifier is computed by using the `UDS`,
|
The firmware now executes assembler code that writes zeros to
|
||||||
the measurement of the application, and the `USS`, and placed in
|
stack and data of the firmware, then jumps to what's in
|
||||||
the `CDI` register. Then `0x4000_7000` is written to `APP_ADDR`.
|
`APP_ADDR` which starts device app execution.
|
||||||
The final `FW_RSP_LOAD_APP_DATA` response is sent to the host,
|
|
||||||
completing the loading.
|
|
||||||
|
|
||||||
NOTE: The firmware uses SPRAM for data and stack. We need to make sure
|
There is now no other means of getting back from application mode
|
||||||
that the application image does not overwrite the firmware's running
|
to firmware mode than resetting/power cycling the device.
|
||||||
state. The application should probably do a similar relocation for
|
|
||||||
stack/data at reset, as the firmware does. Further; the firmware need
|
|
||||||
to check application image is sane. The shared firmware data area
|
|
||||||
(e.g. `.data` and the stack must be cleared prior launching the
|
|
||||||
application.
|
|
||||||
|
|
||||||
### CDI computation
|
### User-supplied Secret (USS)
|
||||||
|
|
||||||
|
USS is a 32 bytes long secret provided by the user. Typically a host
|
||||||
|
program gets a passphrase from the user and then does KDF of some
|
||||||
|
sort, for instance a BLAKE2s, to get 32 bytes which it sends to the
|
||||||
|
firmware to be part of the CDI computation.
|
||||||
|
|
||||||
|
### Compound Device Identifier (CDI) computation
|
||||||
|
|
||||||
The CDI is computed by:
|
The CDI is computed by:
|
||||||
|
|
||||||
@ -139,62 +146,100 @@ The `blake2s()` function in the firmware is fed with a buffer in
|
|||||||
fed with a context used for computations that is also part of the
|
fed with a context used for computations that is also part of the
|
||||||
`FW_RAM`.
|
`FW_RAM`.
|
||||||
|
|
||||||
### Loading the User Supplied Secret (USS)
|
After doing the computation `FW_RAM` is also cleared with zeroes.
|
||||||
|
|
||||||
The host program may send `FW_CMD_LOAD_USS` and `FW_CMD_LOAD_APP_SIZE`
|
### Firmware protocol definition
|
||||||
in any order. But it *should* always send both `FW_CMD_LOAD_USS` and
|
|
||||||
`FW_CMD_LOAD_APP_SIZE` before sending the multiple
|
|
||||||
`FW_CMD_LOAD_APP_DATA`. If it does not, the USS will not be
|
|
||||||
predictable because somebody could have send `FW_CMD_LOAD_USS` before,
|
|
||||||
and the last `FW_CMD_LOAD_APP_DATA` (on whichever iteration) will
|
|
||||||
cause the currently loaded USS to be used for calculating CDI.
|
|
||||||
|
|
||||||
### Starting an application
|
The firmware commands and responses are built on top of the [Framing
|
||||||
|
Protocol](../framing_protocol/framing_protocol.md).
|
||||||
|
|
||||||
Starting an application includes the "switch to application mode"
|
The commands look like this:
|
||||||
step, which is done by writing to the `SWITCH_APP` register. The
|
|
||||||
switch from firmware mode to application mode is a mode switch, and
|
|
||||||
context switch. When entering application mode the MMIO region is
|
|
||||||
restricted; e.g. some registers are removed (`UDS`), and some are
|
|
||||||
switched from read/write to read-only. This is outlined in the memory
|
|
||||||
map below.
|
|
||||||
|
|
||||||
There is no other means of getting back from application mode to
|
| *name* | *size (bytes)* | *comment* |
|
||||||
firmware mode than resetting/power cycling the device.
|
|------------------|----------------|------------------------------------------|
|
||||||
|
| Header | 1 | Framing protocol header including length |
|
||||||
|
| Command/Response | 1 | Any of the below commands or responses |
|
||||||
|
| Data | n | Any additional data |
|
||||||
|
|
||||||
Prerequisites: `APP_SIZE` and `APP_ADDR` has to be non-zero.
|
The responses might include a one byte status field where 0 is
|
||||||
Procedure:
|
`STATUS_OK` and 1 is `STATUS_BAD`.
|
||||||
|
|
||||||
1. The host sends `FW_CMD_RUN_APP` to the device.
|
#### `FW_CMD_NAME_VERSION` (0x01)
|
||||||
2. The firmware responds with `FW_RSP_RUN_APP`
|
|
||||||
3. The firmware writes to `SWITCH_APP`, and executes assembler code
|
|
||||||
that writes zeros to stack and data of the firmware, then jumps
|
|
||||||
to what's in APP_ADDR.
|
|
||||||
4. The device is now in application mode and is executing the
|
|
||||||
application.
|
|
||||||
|
|
||||||
### Protocol definition
|
Get the name and version of the stick.
|
||||||
|
|
||||||
Available commands/reponses:
|
#### `FW_RSP_NAME_VERSION` (0x02)
|
||||||
|
|
||||||
#### `FW_{CMD,RSP}_LOAD_USS`
|
| *name* | *size (bytes)* | *comment* |
|
||||||
#### `FW_{CMD,RSP}_LOAD_APP_SIZE`
|
|---------|----------------|-----------------|
|
||||||
#### `FW_{CMD,RSP}_LOAD_APP_DATA`
|
| name0 | 4 | ASCII |
|
||||||
#### `FW_{CMD,RSP}_RUN_APP`
|
| name1 | 4 | ASCII |
|
||||||
#### `FW_{CMD,RSP}_NAME_VERSION`
|
| version | 4 | Integer version |
|
||||||
#### `FW_{CMD,RSP}_UDI`
|
|
||||||
|
|
||||||
#### `FW_{CMD,RSP}_VERIFY_DEVICE`
|
In a bad response the fields will be zeroed.
|
||||||
|
|
||||||
Verification that the device is an authentic Tillitis
|
#### `FW_CMD_LOAD_APP` (0x03)
|
||||||
device. Implemented using challenge/response.
|
|
||||||
|
|
||||||
#### `FW_{CMD,RSP}_GET_APP_DIGEST`
|
| *name* | *size (bytes)* | *comment* |
|
||||||
|
|--------------|----------------|---------------------|
|
||||||
|
| size | 4 | |
|
||||||
|
| uss-provided | 1 | 0 = false, 1 = true |
|
||||||
|
| uss | 32 | Ignored if above 0 |
|
||||||
|
|
||||||
This command returns the un-keyed hash digest for the application that
|
Start an application loading session by setting the size of the
|
||||||
was loaded. It allows the host to verify that the application was
|
expected device application and a user-supplied secret, if
|
||||||
correctly loaded. This means that the CDI calculated will be correct
|
`uss-provided` is 1. Otherwise `USS` is ignored.
|
||||||
given that the UDS has not been modified.
|
|
||||||
|
#### `FW_RSP_LOAD_APP` (0x04)
|
||||||
|
|
||||||
|
Response to `FW_CMD_LOAD_APP`
|
||||||
|
|
||||||
|
| *name* | *size (bytes)* | *comment* |
|
||||||
|
|--------|----------------|-----------------------------|
|
||||||
|
| status | 1 | `STATUS_OK` or `STATUS_BAD` |
|
||||||
|
|
||||||
|
#### `FW_CMD_LOAD_APP_DATA` (0x05)
|
||||||
|
|
||||||
|
| *name* | *size (bytes)* | *comment* |
|
||||||
|
|--------|----------------|---------------------|
|
||||||
|
| data | 127 | Raw binary app data |
|
||||||
|
|
||||||
|
Load 127 bytes of raw app binary into device RAM. Should be sent
|
||||||
|
consecutively over the complete raw binary.
|
||||||
|
|
||||||
|
#### `FW_RSP_LOAD_APP_DATA` (0x06)
|
||||||
|
|
||||||
|
Response to all but the ultimate `FW_CMD_LOAD_APP_DATA` commands.
|
||||||
|
|
||||||
|
| *name* | *size (bytes)* | *comment* |
|
||||||
|
|--------|----------------|--------------------------|
|
||||||
|
| status | 1 | `STATUS_OK`/`STATUS_BAD` |
|
||||||
|
|
||||||
|
#### `FW_RSP_LOAD_APP_DATA_READY` (0x07)
|
||||||
|
|
||||||
|
The response to the last `FW_CMD_LOAD_APP_DATA` is an
|
||||||
|
`FW_RSP_LOAD_APP_DATA_READY` with the un-keyed hash digest for the
|
||||||
|
application that was loaded. It allows the host to verify that the
|
||||||
|
application was correctly loaded. This means that the CDI calculated
|
||||||
|
will be correct given that the UDS has not been modified.
|
||||||
|
|
||||||
|
| *name* | *size (bytes)* | *comment* |
|
||||||
|
|--------|----------------|--------------------------|
|
||||||
|
| status | 1 | `STATUS_OK`/`STATUS_BAD` |
|
||||||
|
| digest | 32 | BLAKE2s(app) |
|
||||||
|
|
||||||
|
#### `FW_CMD_GET_UDI` (0x08)
|
||||||
|
|
||||||
|
Ask for the Unique Device Identifier (UDI) of the device.
|
||||||
|
|
||||||
|
#### `FW_RSP_GET_UDI` (0x09)
|
||||||
|
|
||||||
|
Response to `FW_CMD_GET_UDI`.
|
||||||
|
|
||||||
|
| *name* | *size (bytes)* | *comment* |
|
||||||
|
|--------|----------------|-------------------------------------------------|
|
||||||
|
| status | 1 | `STATUS_OK`/`STATUS_BAD` |
|
||||||
|
| udi | 8 | Vendor (2B), Product (1B), rev(4b), serial (4B) |
|
||||||
|
|
||||||
#### Get the name and version of the device
|
#### Get the name and version of the device
|
||||||
|
|
||||||
@ -225,37 +270,19 @@ host ->
|
|||||||
u8 CMD[1 + 128];
|
u8 CMD[1 + 128];
|
||||||
|
|
||||||
CMD[0].len = 128 // command frame format
|
CMD[0].len = 128 // command frame format
|
||||||
CMD[1] = 0x0a // FW_CMD_LOAD_USS
|
CMD[1] = 0x03 // FW_CMD_LOAD_APP
|
||||||
|
|
||||||
CMD[2..6] = User Supplied Secret
|
|
||||||
|
|
||||||
CMD[6..] = 0
|
|
||||||
|
|
||||||
host <-
|
|
||||||
u8 RSP[1 + 4];
|
|
||||||
|
|
||||||
RSP[0].len = 4 // command frame format
|
|
||||||
RSP[1] = 0x0b // FW_RSP_LOAD_USS
|
|
||||||
|
|
||||||
RSP[2] = STATUS
|
|
||||||
|
|
||||||
RSP[3..] = 0
|
|
||||||
|
|
||||||
host ->
|
|
||||||
u8 CMD[1 + 32];
|
|
||||||
|
|
||||||
CMD[0].len = 32 // command frame format
|
|
||||||
CMD[1] = 0x03 // FW_CMD_LOAD_APP_SIZE
|
|
||||||
|
|
||||||
CMD[2..6] = APP_SIZE
|
CMD[2..6] = APP_SIZE
|
||||||
|
|
||||||
CMD[6..] = 0
|
CMD[6] = USS supplied? 0 = false, 1 = true
|
||||||
|
CMD[7..39] = USS
|
||||||
|
CMD[40..] = 0
|
||||||
|
|
||||||
host <-
|
host <-
|
||||||
u8 RSP[1 + 4];
|
u8 RSP[1 + 4];
|
||||||
|
|
||||||
RSP[0].len = 4 // command frame format
|
RSP[0].len = 4 // command frame format
|
||||||
RSP[1] = 0x04 // FW_RSP_LOAD_APP_SIZE
|
RSP[1] = 0x04 // FW_RSP_LOAD_APP
|
||||||
|
|
||||||
RSP[2] = STATUS
|
RSP[2] = STATUS
|
||||||
|
|
||||||
@ -281,6 +308,21 @@ host <-
|
|||||||
RSP[3..] = 0
|
RSP[3..] = 0
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Except response from last chunk of app data which is:
|
||||||
|
|
||||||
|
```
|
||||||
|
host <-
|
||||||
|
u8 RSP[1 + 4]
|
||||||
|
|
||||||
|
RSP[0].len = 128 // command frame format
|
||||||
|
RSP[1] = 0x07 // FW_RSP_LOAD_APP_DATA_READY
|
||||||
|
|
||||||
|
RSP[2] = STATUS
|
||||||
|
|
||||||
|
RSP[3..35] = app digest
|
||||||
|
RSP[36..] = 0
|
||||||
|
```
|
||||||
|
|
||||||
### Memory map
|
### Memory map
|
||||||
|
|
||||||
Assigned top level prefixes:
|
Assigned top level prefixes:
|
||||||
|
Loading…
Reference in New Issue
Block a user