mirror of
https://github.com/tillitis/tillitis-key1.git
synced 2025-04-23 16:39:19 -04:00
Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
353d7e9f50 | ||
![]() |
f75620720f | ||
![]() |
fb1269b06e | ||
![]() |
770acc9b38 |
5
.gitignore
vendored
5
.gitignore
vendored
@ -28,12 +28,10 @@
|
|||||||
/testbench_verilator*
|
/testbench_verilator*
|
||||||
/check.smt2
|
/check.smt2
|
||||||
/check.vcd
|
/check.vcd
|
||||||
|
/hw/application_fpga/tkey-libs/libblake2s.a
|
||||||
/hw/application_fpga/tkey-libs/libcommon.a
|
/hw/application_fpga/tkey-libs/libcommon.a
|
||||||
/hw/application_fpga/tkey-libs/libcrt0.a
|
/hw/application_fpga/tkey-libs/libcrt0.a
|
||||||
/hw/application_fpga/tkey-libs/libmonocypher.a
|
/hw/application_fpga/tkey-libs/libmonocypher.a
|
||||||
/hw/application_fpga/tkey-libs/libblake2s.a
|
|
||||||
/hw/application_fpga/tools/partition_table/partition_table
|
|
||||||
/hw/application_fpga/tools/b2s/b2s
|
|
||||||
synth.json
|
synth.json
|
||||||
synth.txt
|
synth.txt
|
||||||
synth.v
|
synth.v
|
||||||
@ -46,7 +44,6 @@ verilated/
|
|||||||
*.o
|
*.o
|
||||||
*.asc
|
*.asc
|
||||||
*.bin
|
*.bin
|
||||||
!/hw/application_fpga/tools/default_partition.bin
|
|
||||||
*.elf
|
*.elf
|
||||||
*.map
|
*.map
|
||||||
*.tmp
|
*.tmp
|
||||||
|
12
README.md
12
README.md
@ -122,17 +122,7 @@ official version tag.
|
|||||||
|
|
||||||
Easiest is probably to just remove the tkey-libs directory and then
|
Easiest is probably to just remove the tkey-libs directory and then
|
||||||
git clone the desired tag. Use the entire repo, but remove the .-files
|
git clone the desired tag. Use the entire repo, but remove the .-files
|
||||||
like `.git`, `.github`, et cetera. Something like:
|
like `.git`, `.github`, et cetera.
|
||||||
|
|
||||||
```
|
|
||||||
$ rm -rf tkey-libs
|
|
||||||
$ git clone git@github.com:tillitis/tkey-libs.git
|
|
||||||
$ cd tkey-libs
|
|
||||||
$ git checkout fw-3
|
|
||||||
```
|
|
||||||
|
|
||||||
Note that you need to change the optimization flag in the tkey-libs'
|
|
||||||
Makefile to `-Os`.
|
|
||||||
|
|
||||||
## Measured boot
|
## Measured boot
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@ For full change log [see](https://github.com/tillitis/tillitis-key1/compare/TK1-
|
|||||||
|
|
||||||
- Security Monitor memory access checks are now more complete.
|
- Security Monitor memory access checks are now more complete.
|
||||||
|
|
||||||
- Add SPI main controller mainly to access the flash chip.
|
- Add SPI main controller mainly to access flash.
|
||||||
|
|
||||||
- Add system reset API. Device apps can reset the system and restart
|
- Add system reset API. Device apps can reset the system and restart
|
||||||
the firmware. The FPGA is not reset.
|
the firmware. The FPGA is not reset.
|
||||||
@ -76,16 +76,8 @@ For full change log [see](https://github.com/tillitis/tillitis-key1/compare/TK1-
|
|||||||
- Add support for the new USB Mode Protocol to communicate with
|
- Add support for the new USB Mode Protocol to communicate with
|
||||||
different endpoints.
|
different endpoints.
|
||||||
|
|
||||||
- Support a filesystem on flash.
|
- Introduce a system call mechanism and the first syscalls: RESET,
|
||||||
|
SET\_LED, GET\_VIDPID.
|
||||||
- Add a system call mechanism and system calls: `RESET`, `ALLOC_AREA`,
|
|
||||||
`DEALLOC_AREA`, `WRITE_DATA`, `READ_DATA`, `PRELOAD_DELETE`,
|
|
||||||
`PRELOAD_STORE`, `PRELOAD_STORE_FIN`, `PRELOAD_GET_DIGSIG`,
|
|
||||||
`STATUS`, and `GET_VIDPID`. See [firmware's
|
|
||||||
README](../hw/application_fpga/fw/README.md) for documentation.
|
|
||||||
|
|
||||||
- Harmonize with [tkey-libs](https://github.com/tillitis/tkey-libs).
|
|
||||||
Import tkey-libs to this repo for convenience.
|
|
||||||
|
|
||||||
### CH552
|
### CH552
|
||||||
|
|
||||||
|
@ -60,12 +60,10 @@ CFLAGS = \
|
|||||||
-Wall \
|
-Wall \
|
||||||
-Wpedantic \
|
-Wpedantic \
|
||||||
-Wno-language-extension-token \
|
-Wno-language-extension-token \
|
||||||
-Wextra \
|
|
||||||
-flto \
|
-flto \
|
||||||
-g \
|
-g \
|
||||||
-I $(LIBDIR)/include \
|
-I $(LIBDIR)/include \
|
||||||
-I $(LIBDIR) \
|
-I $(LIBDIR)
|
||||||
-I $(LIBDIR)/blake2s
|
|
||||||
|
|
||||||
AS = clang
|
AS = clang
|
||||||
|
|
||||||
@ -126,19 +124,15 @@ FIRMWARE_OBJS = \
|
|||||||
$(P)/fw/tk1/main.o \
|
$(P)/fw/tk1/main.o \
|
||||||
$(P)/fw/tk1/start.o \
|
$(P)/fw/tk1/start.o \
|
||||||
$(P)/fw/tk1/proto.o \
|
$(P)/fw/tk1/proto.o \
|
||||||
|
$(P)/fw/tk1/blake2s/blake2s.o \
|
||||||
$(P)/fw/tk1/syscall_enable.o \
|
$(P)/fw/tk1/syscall_enable.o \
|
||||||
$(P)/fw/tk1/syscall_handler.o \
|
$(P)/fw/tk1/syscall_handler.o
|
||||||
$(P)/fw/tk1/spi.o \
|
|
||||||
$(P)/fw/tk1/flash.o \
|
|
||||||
$(P)/fw/tk1/storage.o \
|
|
||||||
$(P)/fw/tk1/partition_table.o \
|
|
||||||
$(P)/fw/tk1/auth_app.o \
|
|
||||||
$(P)/fw/tk1/rng.o \
|
|
||||||
$(P)/fw/tk1/preload_app.o \
|
|
||||||
$(P)/fw/tk1/mgmt_app.o
|
|
||||||
|
|
||||||
CHECK_SOURCES = \
|
FIRMWARE_SOURCES = \
|
||||||
$(P)/fw/tk1/*.[ch]
|
$(P)/fw/tk1/main.c \
|
||||||
|
$(P)/fw/tk1/proto.c \
|
||||||
|
$(P)/fw/tk1/blake2s/blake2s.c \
|
||||||
|
$(P)/fw/tk1/syscall_handler.c
|
||||||
|
|
||||||
TESTFW_OBJS = \
|
TESTFW_OBJS = \
|
||||||
$(P)/fw/testfw/main.o \
|
$(P)/fw/testfw/main.o \
|
||||||
@ -182,7 +176,7 @@ secret:
|
|||||||
LDFLAGS = \
|
LDFLAGS = \
|
||||||
-T $(P)/fw/tk1/firmware.lds \
|
-T $(P)/fw/tk1/firmware.lds \
|
||||||
-Wl,--cref,-M \
|
-Wl,--cref,-M \
|
||||||
-L $(LIBDIR) -lcommon -lblake2s
|
-L $(LIBDIR) -lcommon
|
||||||
|
|
||||||
# Common libraries the firmware and testfw depend on. See
|
# Common libraries the firmware and testfw depend on. See
|
||||||
# https://github.com/tillitis/tkey-libs/
|
# https://github.com/tillitis/tkey-libs/
|
||||||
@ -202,9 +196,6 @@ simfirmware.elf: $(FIRMWARE_OBJS) $(P)/fw/tk1/firmware.lds
|
|||||||
$(CC) $(CFLAGS) $(FIRMWARE_OBJS) $(LDFLAGS) -o $@ > $(basename $@).map
|
$(CC) $(CFLAGS) $(FIRMWARE_OBJS) $(LDFLAGS) -o $@ > $(basename $@).map
|
||||||
|
|
||||||
qemu_firmware.elf: CFLAGS += -DQEMU_DEBUG
|
qemu_firmware.elf: CFLAGS += -DQEMU_DEBUG
|
||||||
qemu_firmware.elf: ASFLAGS += -DQEMU_DEBUG
|
|
||||||
qemu_firmware.elf: CFLAGS += -DQEMU_SYSCALL
|
|
||||||
qemu_firmware.elf: ASFLAGS += -DQEMU_SYSCALL
|
|
||||||
qemu_firmware.elf: firmware.elf
|
qemu_firmware.elf: firmware.elf
|
||||||
mv firmware.elf qemu_firmware.elf
|
mv firmware.elf qemu_firmware.elf
|
||||||
|
|
||||||
@ -217,12 +208,12 @@ compile_commands.json:
|
|||||||
|
|
||||||
.PHONY: check
|
.PHONY: check
|
||||||
check:
|
check:
|
||||||
clang-tidy -header-filter=.* -checks=cert-* $(CHECK_SOURCES) -- $(CFLAGS)
|
clang-tidy -header-filter=.* -checks=cert-* $(FIRMWARE_SOURCES) -- $(CFLAGS)
|
||||||
|
|
||||||
.PHONY: splint
|
.PHONY: splint
|
||||||
splint:
|
splint:
|
||||||
splint \
|
splint \
|
||||||
+unixlib \
|
-nolib \
|
||||||
-predboolint \
|
-predboolint \
|
||||||
+boolint \
|
+boolint \
|
||||||
-nullpass \
|
-nullpass \
|
||||||
@ -233,13 +224,7 @@ splint:
|
|||||||
-unreachable \
|
-unreachable \
|
||||||
-unqualifiedtrans \
|
-unqualifiedtrans \
|
||||||
-fullinitblock \
|
-fullinitblock \
|
||||||
+gnuextensions \
|
$(FIRMWARE_SOURCES)
|
||||||
-fixedformalarray \
|
|
||||||
-mustfreeonly \
|
|
||||||
-I $(LIBDIR)/include \
|
|
||||||
-I $(LIBDIR) \
|
|
||||||
-I $(LIBDIR)/blake2s \
|
|
||||||
$(CHECK_SOURCES)
|
|
||||||
|
|
||||||
testfw.elf: tkey-libs $(TESTFW_OBJS) $(P)/fw/tk1/firmware.lds
|
testfw.elf: tkey-libs $(TESTFW_OBJS) $(P)/fw/tk1/firmware.lds
|
||||||
$(CC) $(CFLAGS) $(TESTFW_OBJS) $(LDFLAGS) -o $@ > $(basename $@).map
|
$(CC) $(CFLAGS) $(TESTFW_OBJS) $(LDFLAGS) -o $@ > $(basename $@).map
|
||||||
|
@ -1 +1 @@
|
|||||||
c770fe25034655241d9e0152b03fcf691c548bc50d30b574a5213abc5b36fe25 application_fpga.bin
|
3d88184d4d636878d7d891e0360413b47f288eea2227435f0bfa2aad6e956f24 application_fpga.bin
|
||||||
|
@ -1 +1 @@
|
|||||||
e72557c38bee1e16114f550b16fc04412bfba09274d7b1fe971ab6b2ef4f06421d976276175ea4df3b7d590599eb052b85a812b689d728be5031ae412b2f8d24 firmware.bin
|
77e6c9e519bce27f7be1a38c10839875ac8c75c71bef540f11569e821638af85a3e724089702a1c17af4c9caa06461ef38a3b8ad5bc5cb01ecf1f3107771708f firmware.bin
|
||||||
|
@ -8,14 +8,13 @@ see [the TKey Developer Handbook](https://dev.tillitis.se/).
|
|||||||
|
|
||||||
## Definitions
|
## Definitions
|
||||||
|
|
||||||
- Firmware: Software in ROM responsible for loading, measuring,
|
- Firmware: Software in ROM responsible for loading, measuring, and
|
||||||
starting applications, and providing system calls. The firmware is
|
starting applications. The firmware is included as part of the FPGA
|
||||||
included as part of the FPGA bitstream and not replacable on a usual
|
bitstream and not replacable on a usual consumer TKey.
|
||||||
consumer TKey.
|
|
||||||
- Client: Software running on a computer or a mobile phone the TKey is
|
- Client: Software running on a computer or a mobile phone the TKey is
|
||||||
inserted into.
|
inserted into.
|
||||||
- Device application or app: Software supplied by the client or from
|
- Device application or app: Software supplied by the client that runs
|
||||||
flash that runs on the TKey.
|
on the TKey.
|
||||||
|
|
||||||
## CPU modes and firmware
|
## CPU modes and firmware
|
||||||
|
|
||||||
@ -65,8 +64,9 @@ The different endpoints:
|
|||||||
|
|
||||||
| *Name* | *Value* | *Comment* |
|
| *Name* | *Value* | *Comment* |
|
||||||
|--------|---------|----------------------------------------------------------------------|
|
|--------|---------|----------------------------------------------------------------------|
|
||||||
| DEBUG | 0x20 | A USB HID special debug pipe. Useful for debug prints. |
|
| CCID | 0x08 | USB CCID, a port for emulating a smart card |
|
||||||
| CH552 | 0x10 | USB controller control |
|
| CH552 | 0x10 | USB controller control |
|
||||||
|
| DEBUG | 0x20 | A USB HID special debug pipe. Useful for debug prints. |
|
||||||
| CDC | 0x40 | USB CDC-ACM, a serial port on the client. |
|
| CDC | 0x40 | USB CDC-ACM, a serial port on the client. |
|
||||||
| FIDO | 0x80 | A USB FIDO security token device, useful for FIDO-type applications. |
|
| FIDO | 0x80 | A USB FIDO security token device, useful for FIDO-type applications. |
|
||||||
|
|
||||||
@ -75,10 +75,10 @@ commands to the `CH552` control endpoint. When the TKey starts only
|
|||||||
the `CH552` and the `CDC` endpoints are active. To change this, send a
|
the `CH552` and the `CDC` endpoints are active. To change this, send a
|
||||||
command to `CH552` in this form:
|
command to `CH552` in this form:
|
||||||
|
|
||||||
| *Name* | *Size* | *Comment* |
|
| *Name* | *Size* | *Comment* |
|
||||||
|----------|--------|-------------------------------|
|
|---------|--------|-------------------------------|
|
||||||
| Command | 1B | Command to the CH552 firmware |
|
| Command | 1B | Command to the CH552 firmware |
|
||||||
| Argument | 1B | Data for the command |
|
| Payload | 1B | Data for the command |
|
||||||
|
|
||||||
Commands:
|
Commands:
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ Protocol](https://dev.tillitis.se/protocol/) which is described in the
|
|||||||
Developer Handbook.
|
Developer Handbook.
|
||||||
|
|
||||||
The firmware uses a protocol on top of this framing layer which is
|
The firmware uses a protocol on top of this framing layer which is
|
||||||
used to load a device application. All commands are initiated by the
|
used to bootstrap an application. All commands are initiated by the
|
||||||
client. All commands receive a reply. See [Firmware
|
client. All commands receive a reply. See [Firmware
|
||||||
protocol](http://dev.tillitis.se/protocol/#firmware-protocol) in the
|
protocol](http://dev.tillitis.se/protocol/#firmware-protocol) in the
|
||||||
Dev Handbook for specific details.
|
Dev Handbook for specific details.
|
||||||
@ -106,191 +106,95 @@ Dev Handbook for specific details.
|
|||||||
|
|
||||||
* FW\_RAM is divided into the following areas:
|
* FW\_RAM is divided into the following areas:
|
||||||
|
|
||||||
- fw stack: 3000 bytes.
|
- fw stack: 3824 bytes.
|
||||||
- resetinfo: 256 bytes.
|
- resetinfo: 256 bytes.
|
||||||
- .data and .bss: 840 bytes.
|
- rest is available for .data and .bss.
|
||||||
|
|
||||||
## Firmware behaviour
|
## Firmware behaviour
|
||||||
|
|
||||||
The purpose of the firmware is to:
|
The purpose of the firmware is to load, measure, and start an
|
||||||
|
application received from the client over the USB/UART.
|
||||||
1. Load, measure, and start an application received from the client
|
|
||||||
over the USB/UART or from one of two flash app slots.
|
|
||||||
2. Provide functionality to run only app's with specific BLAKE2s
|
|
||||||
digests.
|
|
||||||
3. Provide system calls to access the filesystem and get other data.
|
|
||||||
|
|
||||||
The firmware binary is part of the FPGA bitstream as the initial
|
The firmware binary is part of the FPGA bitstream as the initial
|
||||||
values of the Block RAMs used to construct the ROM. The ROM is located
|
values of the Block RAMs used to construct the `FW_ROM`. The `FW_ROM`
|
||||||
at `0x0000_0000`. This is also the CPU reset vector.
|
start address is located at `0x0000_0000` in the CPU memory map, which
|
||||||
|
is also the CPU reset vector.
|
||||||
### Reset type
|
|
||||||
|
|
||||||
When the TKey is started or resetted it can load an app from different
|
|
||||||
sources. We call this the reset type. Reset type is located in the
|
|
||||||
resetinfo part of FW\_RAM. The different reset types loads and start
|
|
||||||
an app from:
|
|
||||||
|
|
||||||
1. Flash slot 0 (default): `FLASH0` with a specific app hash defined
|
|
||||||
in a constant in firmware.
|
|
||||||
2. Flash slot 1: `FLASH1`.
|
|
||||||
3. Flash slot 0 with a specific app hash left from previous app:
|
|
||||||
`FLASH0_VER`
|
|
||||||
4. Flash slot 1 with a specific app hash left from previous app:
|
|
||||||
`FLASH1_VER`.
|
|
||||||
5. Client: `CLIENT`.
|
|
||||||
6. Client with a specific app hash left from previous app:
|
|
||||||
`CLIENT_VER`.
|
|
||||||
|
|
||||||
### Firmware state machine
|
### Firmware state machine
|
||||||
|
|
||||||
|
This is the state diagram of the firmware. There are only four states.
|
||||||
Change of state occur when we receive specific I/O or a fatal error
|
Change of state occur when we receive specific I/O or a fatal error
|
||||||
occurs.
|
occurs.
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
stateDiagram-v2
|
stateDiagram-v2
|
||||||
S0: INITIAL
|
S1: initial
|
||||||
S1: WAITCOMMAND
|
S2: loading
|
||||||
S2: LOADING
|
S3: running
|
||||||
S3: LOAD_FLASH
|
SE: failed
|
||||||
S4: LOAD_FLASH_MGMT
|
|
||||||
S5: START
|
|
||||||
SE: FAIL
|
|
||||||
|
|
||||||
[*] --> S0
|
[*] --> S1
|
||||||
|
|
||||||
S0 --> S1
|
S1 --> S1: Commands
|
||||||
S0 --> S4: Default
|
S1 --> S2: LOAD_APP
|
||||||
S0 --> S3
|
S1 --> SE: Error
|
||||||
|
|
||||||
S1 --> S1: Other commands
|
S2 --> S2: LOAD_APP_DATA
|
||||||
S1 --> S2: LOAD_APP
|
S2 --> S3: Last block received
|
||||||
S1 --> SE: Error
|
S2 --> SE: Error
|
||||||
|
|
||||||
S2 --> S2: LOAD_APP_DATA
|
S3 --> [*]
|
||||||
S2 --> S5: Last block received
|
|
||||||
S2 --> SE: Error
|
|
||||||
|
|
||||||
S3 --> S5
|
|
||||||
S3 --> SE
|
|
||||||
|
|
||||||
S4 --> S5
|
|
||||||
S4 --> SE
|
|
||||||
|
|
||||||
SE --> [*]
|
|
||||||
S5 --> [*]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
States:
|
States:
|
||||||
|
|
||||||
- *INITIAL*: Transitions to next state through reset type left in
|
- `initial` - At start. Allows the commands `NAME_VERSION`, `GET_UDI`,
|
||||||
`FW_RAM`.
|
`LOAD_APP`.
|
||||||
- *WAITCOMMAND*: Waiting for initial commands from client. Allows the
|
- `loading` - Expect application data. Allows only the command
|
||||||
commands `NAME_VERSION`, `GET_UDI`, `LOAD_APP`.
|
`LOAD_APP_DATA`.
|
||||||
- *LOADING*: Expecting application data from client. Allows only the
|
- `run` - Computes CDI and starts the application. Allows no commands.
|
||||||
command `LOAD_APP_DATA` to continue loading the device app.
|
- `fail` - Stops waiting for commands, flashes LED forever. Allows no
|
||||||
- *LOAD_FLASH*: Loading an app from flash. Allows no commands.
|
commands.
|
||||||
- *LOAD_FLASH_MGMT*: Loading and verifyiing a device app from flash.
|
|
||||||
Allows no commands.
|
|
||||||
- *START*: Computes CDI. Possibly verifies app. Starts the
|
|
||||||
application. Does not return to firmware. Allows no commands.
|
|
||||||
- *FAIL* - Halts CPU. Allows no commands.
|
|
||||||
|
|
||||||
Allowed data in state *INITIAL*:
|
Commands in state `initial`:
|
||||||
|
|
||||||
| *reset type* | *next state* |
|
|
||||||
|--------------|-------------------|
|
|
||||||
| `FLASH0` | *LOAD_FLASH_MGMT* |
|
|
||||||
| `FLASH1` | *LOAD_FLASH* |
|
|
||||||
| `FLASH0_VER` | *LOAD_FLASH* |
|
|
||||||
| `FLASH1_VER` | *LOAD_FLASH* |
|
|
||||||
| `CLIENT` | *WAITCOMMAND* |
|
|
||||||
| `CLIENT_VER` | *WAITCOMMAND* |
|
|
||||||
|
|
||||||
I/O in state *LOAD_FLASH*:
|
|
||||||
|
|
||||||
| *I/O* | *next state* |
|
|
||||||
|--------------------|--------------|
|
|
||||||
| Last app data read | *START* |
|
|
||||||
|
|
||||||
I/O in state *LOAD_FLASH_MGMT*:
|
|
||||||
|
|
||||||
| *I/O* | *next state* |
|
|
||||||
|--------------------|--------------|
|
|
||||||
| Last app data read | *START* |
|
|
||||||
|
|
||||||
Commands in state `waitcommand`:
|
|
||||||
|
|
||||||
| *command* | *next state* |
|
| *command* | *next state* |
|
||||||
|-----------------------|--------------|
|
|-----------------------|--------------|
|
||||||
| `FW_CMD_NAME_VERSION` | unchanged |
|
| `FW_CMD_NAME_VERSION` | unchanged |
|
||||||
| `FW_CMD_GET_UDI` | unchanged |
|
| `FW_CMD_GET_UDI` | unchanged |
|
||||||
| `FW_CMD_LOAD_APP` | *LOADING* |
|
| `FW_CMD_LOAD_APP` | `loading` |
|
||||||
|
| | |
|
||||||
|
|
||||||
Commands in state `loading`:
|
Commands in state `loading`:
|
||||||
|
|
||||||
| *command* | *next state* |
|
| *command* | *next state* |
|
||||||
|------------------------|------------------------------------|
|
|------------------------|----------------------------------|
|
||||||
| `FW_CMD_LOAD_APP_DATA` | unchanged or *START* on last chunk |
|
| `FW_CMD_LOAD_APP_DATA` | unchanged or `run` on last chunk |
|
||||||
|
|
||||||
No other states allows commands.
|
|
||||||
|
|
||||||
See [Firmware protocol in the Dev
|
See [Firmware protocol in the Dev
|
||||||
Handbook](http://dev.tillitis.se/protocol/#firmware-protocol) for the
|
Handbook](http://dev.tillitis.se/protocol/#firmware-protocol) for the
|
||||||
definition of the specific commands and their responses.
|
definition of the specific commands and their responses.
|
||||||
|
|
||||||
Plain text explanation of the states:
|
State changes from "initial" to "loading" when receiving `LOAD_APP`,
|
||||||
|
which also sets the size of the number of data blocks to expect. After
|
||||||
|
that we expect several `LOAD_APP_DATA` commands until the last block
|
||||||
|
is received, when state is changed to "running".
|
||||||
|
|
||||||
- *INITIAL*: Start here. Check the `FW_RAM` for the `resetinfo` type
|
In "running", the loaded device app is measured, the Compound Device
|
||||||
for what to do next.
|
Identifier (CDI) is computed, we do some cleanup of firmware data
|
||||||
|
structures, enable the system calls, and finally start the app, which
|
||||||
|
ends the firmware state machine. Hardware guarantees that we leave
|
||||||
|
firmware mode automatically when the program counter leaves ROM.
|
||||||
|
|
||||||
For all types which begins with `FLASH_*`, set next state to
|
The device app is now running in application mode. We can, however,
|
||||||
*LOAD_FLASH*, otherwise set next state to *WAITCOMMAND*.
|
return to firmware mode (excepting access to the UDS) by doing system
|
||||||
|
calls. Note that ROM is still readable, but is now hardware protected
|
||||||
|
from execution, except through the system call mechanism.
|
||||||
|
|
||||||
- *LOAD_FLASH*: Load device app from flash into RAM, app slot taken
|
### Golden path
|
||||||
from context. Compute a BLAKE2s digest over the entire app.
|
|
||||||
Transition to *START*.
|
|
||||||
|
|
||||||
- *LOAD_FLASH_MGMT*: Load device app from flash into RAM, app slot
|
Firmware loads the application at the start of RAM (`0x4000_0000`). It
|
||||||
alway 0. Compute a BLAKE2s digest over the entire app. Register the
|
use a part of the special FW\_RAM for its own stack.
|
||||||
app as a prospective management app if it later goes through
|
|
||||||
verification. Transition to *START*.
|
|
||||||
|
|
||||||
- *WAITCOMMAND*: Wait for commands from the client. Transition to
|
|
||||||
*LOADING* on `LOAD_APP` command, which also sets the size of the
|
|
||||||
number of data blocks to expect.
|
|
||||||
|
|
||||||
- *LOADING*: Wait for several `LOAD_APP_DATA` commands until the last
|
|
||||||
block is received, then transition to *START*.
|
|
||||||
|
|
||||||
- *START*: Compute the Compound Device Identifier (CDI). If we have a
|
|
||||||
registered verification digest, verify that the app we are about to
|
|
||||||
start is indeed the correct app.
|
|
||||||
|
|
||||||
Clean up firmware data structures, enable the system calls, and
|
|
||||||
start the app, which ends the firmware state machine. Hardware
|
|
||||||
guarantees that we leave firmware mode automatically when the
|
|
||||||
program counter leaves ROM.
|
|
||||||
|
|
||||||
- *FAIL*: Execute an illegal instruction which traps the CPU. Hardware
|
|
||||||
detects a trapped CPU and blinks the status LED in red until power
|
|
||||||
loss. No further instructions are executed.
|
|
||||||
|
|
||||||
After leaving *START* the device app is now running in application
|
|
||||||
mode. We can, however, return to firmware mode (excepting access to
|
|
||||||
the UDS) by doing system calls. Note that ROM is still readable, but
|
|
||||||
is now hardware protected from execution, except through the system
|
|
||||||
call mechanism.
|
|
||||||
|
|
||||||
If during this whole time any commands are received which are not
|
|
||||||
allowed in the current state, or any errors occur, we enter the *FAIL*
|
|
||||||
state.
|
|
||||||
|
|
||||||
### Golden path from start to default app
|
|
||||||
|
|
||||||
Firmware loads the device application at the start of RAM
|
|
||||||
(`0x4000_0000`) from either flash or from the client through the UART.
|
|
||||||
Firmware uses a part of the FW\_RAM for its own stack.
|
|
||||||
|
|
||||||
When reset is released, the CPU starts executing the firmware. It
|
When reset is released, the CPU starts executing the firmware. It
|
||||||
begins in `start.S` by clearing all CPU registers, clears all FW\_RAM,
|
begins in `start.S` by clearing all CPU registers, clears all FW\_RAM,
|
||||||
@ -300,167 +204,60 @@ the system calls, but the handler is not yet enabled.
|
|||||||
|
|
||||||
Beginning at `main()` it fills the entire RAM with pseudo random data
|
Beginning at `main()` it fills the entire RAM with pseudo random data
|
||||||
and setting up the RAM address and data hardware scrambling with
|
and setting up the RAM address and data hardware scrambling with
|
||||||
values from the True Random Number Generator (TRNG).
|
values from the True Random Number Generator (TRNG). It then waits for
|
||||||
|
data coming in through the UART.
|
||||||
|
|
||||||
1. Check the special resetinfo area in FW\_RAM for reset type. Type
|
Typical expected use scenario:
|
||||||
zero means default behaviour, load from flash app slot 0, expecting
|
|
||||||
the app there to have a specific hardcoded BLAKE2s digest.
|
|
||||||
|
|
||||||
2. Load app data from flash slot 0 into RAM.
|
1. The client sends the `FW_CMD_LOAD_APP` command with the size of
|
||||||
|
the device app and the optional 32 byte hash of the user-supplied
|
||||||
|
secret as arguments and gets a `FW_RSP_LOAD_APP` back. After
|
||||||
|
using this it's not possible to restart the loading of an
|
||||||
|
application.
|
||||||
|
|
||||||
3. Compute a BLAKE2s digest of the loaded app.
|
2. If the the client receive a sucessful response, it will send
|
||||||
|
multiple `FW_CMD_LOAD_APP_DATA` commands, together containing the
|
||||||
|
full application.
|
||||||
|
|
||||||
4. Compare the computed digest against the allowed app digest
|
3. On receiving`FW_CMD_LOAD_APP_DATA` commands the firmware places
|
||||||
hardcoded in the firmware. If it's not equal, halt CPU.
|
the data into `0x4000_0000` and upwards. The firmware replies
|
||||||
|
with a `FW_RSP_LOAD_APP_DATA` response to the client for each
|
||||||
|
received block except the last data block.
|
||||||
|
|
||||||
7. [Start the device app](#start-the-device-app).
|
4. When the final block of the application image is received with a
|
||||||
|
`FW_CMD_LOAD_APP_DATA`, the firmware measure the application by
|
||||||
|
computing a BLAKE2s digest over the entire application. Then
|
||||||
|
firmware send back the `FW_RSP_LOAD_APP_DATA_READY` response
|
||||||
|
containing the digest.
|
||||||
|
|
||||||
### Start the device app
|
5. The Compound Device Identifier
|
||||||
|
([CDI]((#compound-device-identifier-computation))) is then
|
||||||
|
computed by doing a new BLAKE2s using the Unique Device Secret
|
||||||
|
(UDS), the application digest, and any User Supplied Secret
|
||||||
|
(USS) digest already received.
|
||||||
|
|
||||||
1. Check if there is a verification digest left from the previous app
|
6. The start address of the device app, currently `0x4000_0000`, is
|
||||||
in the resetinfo. If it is, compare with the loaded app's already
|
written to `APP_ADDR` and the size of the binary to `APP_SIZE` to
|
||||||
computed digest. Halt CPU if different.
|
let the device application know where it is loaded and how large
|
||||||
|
it is, if it wants to relocate in RAM.
|
||||||
|
|
||||||
2. Compute the Compound Device Identifier
|
7. The firmware now clears the part of the special `FW_RAM` where it
|
||||||
([CDI]((#compound-device-identifier-computation))) by doing a
|
keeps it stack.
|
||||||
BLAKE2s using the Unique Device Secret (UDS), the application
|
|
||||||
digest, and any User Supplied Secret (USS) digest already received.
|
|
||||||
|
|
||||||
3. Write the start address of the device app, currently `0x4000_0000`,
|
8. The interrupt handler for system calls is enabled.
|
||||||
to `APP_ADDR` and the size of the loaded binary 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.
|
|
||||||
|
|
||||||
4. Clear the stack part of `FW_RAM`.
|
9. Firmware starts the application by jumping to the contents of
|
||||||
|
`APP_ADDR`. Hardware automatically switches from firmware mode to
|
||||||
|
application mode. In this mode some memory access is restricted,
|
||||||
|
e.g. some addresses are inaccessible (`UDS`), and some are
|
||||||
|
switched from read/write to read-only (see [the memory
|
||||||
|
map](https://dev.tillitis.se/memory/)).
|
||||||
|
|
||||||
5. Enable system call interrupt handler.
|
If during this whole time any commands are received which are not
|
||||||
|
allowed in the current state, or any errors occur, we enter the
|
||||||
6. Start the application by jumping to the contents of `APP_ADDR`.
|
"failed" state and execute an illegal instruction. An illegal
|
||||||
Hardware automatically switch from firmware mode to application
|
instruction traps the CPU and hardware blinks the status LED red until
|
||||||
mode. In this mode some memory access is restricted, e.g. some
|
a power cycle. No further instructions are executed.
|
||||||
addresses are inaccessible (`UDS`), and some are switched from
|
|
||||||
read/write to read-only (see [the memory
|
|
||||||
map](https://dev.tillitis.se/memory/)).
|
|
||||||
|
|
||||||
### Management app, chaining apps and verified boot
|
|
||||||
|
|
||||||
Normally, the TKey measures a device app and mixes it together with
|
|
||||||
the Unique Device Secret in hardware to produce the [Compound Device
|
|
||||||
Identifier]((#compound-device-identifier-computation)). The CDI can
|
|
||||||
then be used for creating key material. However, since any key
|
|
||||||
material created like this will change if the app is changed even the
|
|
||||||
slightest, this make it hard to upgrade apps and keep the key
|
|
||||||
material.
|
|
||||||
|
|
||||||
This is where a combination of measured boot and verified boot comes
|
|
||||||
in!
|
|
||||||
|
|
||||||
To support verified boot the firmware supports reset types with
|
|
||||||
verification. This means that the firmware will load an app as usual
|
|
||||||
either from flash or from the client, but before starting the app it
|
|
||||||
will verify the new app's computed digest with a verification digest.
|
|
||||||
The verification digest can either be stored in the firmware itself or
|
|
||||||
left to it from a previous app, a verified boot loader app.
|
|
||||||
|
|
||||||
Such a verified boot loader app:
|
|
||||||
|
|
||||||
- Might be loaded from either flash or client.
|
|
||||||
|
|
||||||
- Typically includes a security policy, for instance a public key and
|
|
||||||
code to check a crytographic signature.
|
|
||||||
|
|
||||||
- Can be specifically trusted by firmware to be able to do filesystem
|
|
||||||
management to be able to update an app slot on flash. Add the app's
|
|
||||||
digest to `allowed_app_digest` in `mgmt_app.c` to allow it to allow
|
|
||||||
it to use `PRELOAD_DELETE`, `PRELOAD_STORE`, and
|
|
||||||
`PRELOAD_STORE_FIN`.
|
|
||||||
|
|
||||||
It works like this:
|
|
||||||
|
|
||||||
- The app reads a digest of the next app in the chain and the
|
|
||||||
signature over the digest from either the filesystem (syscall
|
|
||||||
`PRELOAD_GET_DIGSIG`) or sent from the client.
|
|
||||||
|
|
||||||
- If the signature provided over the digest is verified against the
|
|
||||||
public key the app use the system call `RESET` with the reset type
|
|
||||||
set to `START_FLASH0_VER`, `START_FLASH1_VER`, or `START_CLIENT_VER`
|
|
||||||
depending on where it wants the next app to start from. It also
|
|
||||||
sends the now verified app digest to the firmware in the same system
|
|
||||||
call.
|
|
||||||
|
|
||||||
- The key is reset and firmware starts again. It checks:
|
|
||||||
|
|
||||||
1. The reset type. Start from client or a slot in the filesystem?
|
|
||||||
2. The expected digest of the next app.
|
|
||||||
|
|
||||||
- Firmware loads the app from the expected source.
|
|
||||||
|
|
||||||
- Firmware refuses to start if the loaded app has a different digest.
|
|
||||||
|
|
||||||
- If the app was allowed to start it can now use something
|
|
||||||
deterministic left for it in resetinfo by the verified boot loader
|
|
||||||
app as a seed for it's key material and no longer use CDI for the
|
|
||||||
purpose.
|
|
||||||
|
|
||||||
We propose that a loader app can derive the seed for the next app by
|
|
||||||
creating a shared secret, perhaps something as easy as:
|
|
||||||
|
|
||||||
```
|
|
||||||
secret = blake2s(cdi, "name-of-next-app")
|
|
||||||
```
|
|
||||||
|
|
||||||
The loader shares the secret with the next app by putting it in the
|
|
||||||
part of `resetinfo` that is reserved for inter-app communication.
|
|
||||||
|
|
||||||
The next app can now use the secret as a seed for it's own key
|
|
||||||
material. Depending on the app's behaviour and the numer of keys it
|
|
||||||
needs it can derive more keys, for instance by having nonces stored on
|
|
||||||
its flash area and doing:
|
|
||||||
|
|
||||||
```
|
|
||||||
secret1 = blake2s(secret, nonce1)
|
|
||||||
secret2 = blake2s(secret, nonce2)
|
|
||||||
...
|
|
||||||
```
|
|
||||||
|
|
||||||
Now it can create many secrets deterministically, as long as there is
|
|
||||||
some space left on flash for the nonces and all of them can be traced
|
|
||||||
to the measured identity of the loader app, giving all the features of
|
|
||||||
the measured boot system.
|
|
||||||
|
|
||||||
### App loaded from client
|
|
||||||
|
|
||||||
The default is always to start from a verified app in flash slot
|
|
||||||
0. To be able to load an app from the client you have to send
|
|
||||||
something to the app to reset the TKey with a reset type of
|
|
||||||
`START_CLIENT` or `START_CLIENT_VER`.
|
|
||||||
|
|
||||||
After reset, firmware will:
|
|
||||||
|
|
||||||
1. Wait for data coming in through the UART.
|
|
||||||
|
|
||||||
2. The client sends the `FW_CMD_LOAD_APP` command with the size of
|
|
||||||
the device app and the optional 32 byte hash of the user-supplied
|
|
||||||
secret as arguments and gets a `FW_RSP_LOAD_APP` back. After
|
|
||||||
using this it's not possible to restart the loading of an
|
|
||||||
application.
|
|
||||||
|
|
||||||
3. On a sucessful response, the client will send multiple
|
|
||||||
`FW_CMD_LOAD_APP_DATA` commands, together containing the full
|
|
||||||
application.
|
|
||||||
|
|
||||||
4. On receiving`FW_CMD_LOAD_APP_DATA` commands the firmware places
|
|
||||||
the data into `0x4000_0000` and upwards. The firmware replies
|
|
||||||
with a `FW_RSP_LOAD_APP_DATA` response to the client for each
|
|
||||||
received block except the last data block.
|
|
||||||
|
|
||||||
5. When the final block of the application image is received with a
|
|
||||||
`FW_CMD_LOAD_APP_DATA`, the firmware measure the application by
|
|
||||||
computing a BLAKE2s digest over the entire application. Then
|
|
||||||
firmware send back the `FW_RSP_LOAD_APP_DATA_READY` response
|
|
||||||
containing the digest.
|
|
||||||
|
|
||||||
6. [Start the device app](#start-the-device-app).
|
|
||||||
|
|
||||||
### User-supplied Secret (USS)
|
### User-supplied Secret (USS)
|
||||||
|
|
||||||
@ -480,7 +277,7 @@ CDI = blake2s(UDS, blake2s(app), USS)
|
|||||||
In an ideal world, software would never be able to read UDS at all and
|
In an ideal world, software would never be able to read UDS at all and
|
||||||
we would have a BLAKE2s function in hardware that would be the only
|
we would have a BLAKE2s function in hardware that would be the only
|
||||||
thing able to read the UDS. Unfortunately, we couldn't fit a BLAKE2s
|
thing able to read the UDS. Unfortunately, we couldn't fit a BLAKE2s
|
||||||
implementation in the FPGA.
|
implementation in the FPGA at this time.
|
||||||
|
|
||||||
The firmware instead does the CDI computation using the special
|
The firmware instead does the CDI computation using the special
|
||||||
firmware-only `FW_RAM` which is invisible after switching to app mode.
|
firmware-only `FW_RAM` which is invisible after switching to app mode.
|
||||||
@ -498,7 +295,7 @@ Then we continue with the CDI computation by updating with an optional
|
|||||||
USS digest and finalizing the hash, storing the resulting digest in
|
USS digest and finalizing the hash, storing the resulting digest in
|
||||||
`CDI`.
|
`CDI`.
|
||||||
|
|
||||||
### System calls
|
### Firmware system calls
|
||||||
|
|
||||||
The firmware provides a system call mechanism through the use of the
|
The firmware provides a system call mechanism through the use of the
|
||||||
PicoRV32 interrupt handler. They are triggered by writing to the
|
PicoRV32 interrupt handler. They are triggered by writing to the
|
||||||
@ -506,11 +303,10 @@ trigger address: 0xe1000000. It's typically done with a function
|
|||||||
signature like this:
|
signature like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
int syscall(uint32_t number, uint32_t arg1, uint32_t arg2,
|
int syscall(uint32_t number, uint32_t arg1);
|
||||||
uint32_t arg3);
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Arguments are system call number and up to 6 generic arguments passed
|
Arguments are system call number and upto 6 generic arguments passed
|
||||||
to the system call handler. The caller should place the system call
|
to the system call handler. The caller should place the system call
|
||||||
number in the a0 register and the arguments in registers a1 to a7
|
number in the a0 register and the arguments in registers a1 to a7
|
||||||
according to the RISC-V calling convention. The caller is responsible
|
according to the RISC-V calling convention. The caller is responsible
|
||||||
@ -520,160 +316,16 @@ The syscall handler returns execution on the next instruction after
|
|||||||
the store instruction to the trigger address. The return value from
|
the store instruction to the trigger address. The return value from
|
||||||
the syscall is now available in x10 (a0).
|
the syscall is now available in x10 (a0).
|
||||||
|
|
||||||
The syscall numbers are kept in `syscall_num.h`. The syscalls are
|
To add or change syscalls, see the `syscall_handler()` in
|
||||||
handled in `syscall_handler()` in `syscall_handler.c`.
|
`syscall_handler.c`.
|
||||||
|
|
||||||
#### `RESET`
|
Currently supported syscalls:
|
||||||
|
|
||||||
```
|
| *Name* | *Number* | *Argument* | *Description* |
|
||||||
struct reset {
|
|-------------|----------|------------|----------------------------------|
|
||||||
uint32_t type; // Reset type
|
| RESET | 1 | Unused | Reset the TKey |
|
||||||
uint8_t app_digest[32]; // Digest of next app in chain to verify
|
| SET\_LED | 10 | Colour | Set the colour of the status LED |
|
||||||
uint8_t next_app_data[220]; // Data to leave around for next app
|
| GET\_VIDPID | 12 | Unused | Get Vendor and Product ID |
|
||||||
};
|
|
||||||
|
|
||||||
struct reset rst;
|
|
||||||
|
|
||||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
|
||||||
```
|
|
||||||
|
|
||||||
Resets the TKey. Does not return.
|
|
||||||
|
|
||||||
You can pass data to the firmware about the reset type `type` and a
|
|
||||||
digest that the next app must have. You can also leave some data to
|
|
||||||
the next app in the chain in `next_app_data`.
|
|
||||||
|
|
||||||
The types of the reset are defined in `resetinfo.h`:
|
|
||||||
|
|
||||||
| *Name* | *Comment* |
|
|
||||||
|--------------------|------------------------------------------------|
|
|
||||||
| `START_FLASH0` | Load next app from flash slot 0 |
|
|
||||||
| `START_FLASH1` | Load next app from flash slot 1 |
|
|
||||||
| `START_FLASH0_VER` | Load next app from flash slot 0, but verify it |
|
|
||||||
| `START_FLASH1_VER` | Load next app from flash slot 1, but verify it |
|
|
||||||
| `START_CLIENT` | Load next app from client |
|
|
||||||
| `START_CLIENT_VER` | Load next app from client |
|
|
||||||
|
|
||||||
#### `ALLOC_AREA`
|
|
||||||
|
|
||||||
```
|
|
||||||
syscall(TK1_SYSCALL_ALLOC_AREA, 0, 0, 0);
|
|
||||||
```
|
|
||||||
|
|
||||||
Allocate a flash area for the current app. Returns 0 on success.
|
|
||||||
|
|
||||||
#### `DEALLOC_AREA`
|
|
||||||
|
|
||||||
```
|
|
||||||
syscall(TK1_SYSCALL_DEALLOC_AREA, 0, 0, 0);
|
|
||||||
```
|
|
||||||
|
|
||||||
Free an already allocated flash area for the current app. Returns 0 on
|
|
||||||
success.
|
|
||||||
|
|
||||||
#### `WRITE_DATA`
|
|
||||||
|
|
||||||
```
|
|
||||||
uint32_t offset = 0;
|
|
||||||
uint8_t buf[17];
|
|
||||||
|
|
||||||
TK1_SYSCALL_WRITE_DATA, offset, (uint32_t)buf, sizeof(buf))
|
|
||||||
```
|
|
||||||
|
|
||||||
Write data in `buf` to the app's flash area at byte `offset` within
|
|
||||||
the area. Returns 0 on success.
|
|
||||||
|
|
||||||
#### `READ_DATA`
|
|
||||||
|
|
||||||
```
|
|
||||||
uint32_t offset = 0;
|
|
||||||
uint8_t buf[17];
|
|
||||||
|
|
||||||
syscall(TK1_SYSCALL_READ_DATA, offset, (uint32_t)buf, sizeof(buf);
|
|
||||||
```
|
|
||||||
|
|
||||||
Read into `buf` at byte `offset` from the app's flash area.
|
|
||||||
|
|
||||||
#### `PRELOAD_DELETE`
|
|
||||||
|
|
||||||
```
|
|
||||||
syscall(TK1_SYSCALL_PRELOAD_DELETE, 0, 0, 0);
|
|
||||||
```
|
|
||||||
|
|
||||||
Delete the app in flash slot 1. Returns 0 on success. Only available
|
|
||||||
for the verified management app.
|
|
||||||
|
|
||||||
#### `PRELOAD_STORE`
|
|
||||||
|
|
||||||
```
|
|
||||||
uint8_t *appbinary;
|
|
||||||
uint32_t offset;
|
|
||||||
uint32_t size;
|
|
||||||
|
|
||||||
syscall(TK1_SYSCALL_PRELOAD_STORE, offset, (uint32_t)appbinary,
|
|
||||||
size);
|
|
||||||
```
|
|
||||||
|
|
||||||
Store an app, or possible just a block of an app, from the `appbinary`
|
|
||||||
buffer in flash slot 1 at byte `offset` If you can't find your entire
|
|
||||||
app in the buffer, call `PRELOAD_STORE` many times as you receive the
|
|
||||||
binary from the client. Returns 0 on success.
|
|
||||||
|
|
||||||
Only available for the verified management app.
|
|
||||||
|
|
||||||
#### `PRELOAD_STORE_FIN`
|
|
||||||
|
|
||||||
```
|
|
||||||
uint8_t app_digest[32];
|
|
||||||
uint8_t app_signature[64];
|
|
||||||
size_t app_size;
|
|
||||||
|
|
||||||
syscall(TK1_SYSCALL_PRELOAD_STORE_FIN, app_size,
|
|
||||||
(uint32_t)app_digest, (uint32_t)app_signature)
|
|
||||||
```
|
|
||||||
|
|
||||||
Finalize storing of an app where the complete binary size is
|
|
||||||
`app_size` in flash slot 1. Returns 0 on success. Only available for
|
|
||||||
the verified management app.
|
|
||||||
|
|
||||||
Compute the `app_digest` with BLAKE2s over the entire binary.
|
|
||||||
|
|
||||||
Sign `app_digest` with your Ed25519 private key and pass the
|
|
||||||
resulting signature in `app_signature`.
|
|
||||||
|
|
||||||
#### `PRELOAD_GET_DIGSIG`
|
|
||||||
|
|
||||||
```
|
|
||||||
uint8_t app_digest[32];
|
|
||||||
uint8_t app_signature[64];
|
|
||||||
|
|
||||||
syscall(TK1_SYSCALL_PRELOAD_GET_DIGSIG, (uint32_t)app_digest,
|
|
||||||
(uint32_t)app_signature, 0);
|
|
||||||
```
|
|
||||||
|
|
||||||
Copies the digest and signature of app in flash slot 1 to `app_digest`
|
|
||||||
and `app_signature`. Returns 0 on success. Only available for the
|
|
||||||
verified management app.
|
|
||||||
|
|
||||||
#### `STATUS`
|
|
||||||
|
|
||||||
```
|
|
||||||
syscall(TK1_SYSCALL_PRELOAD_STATUS, 0, 0, 0);
|
|
||||||
```
|
|
||||||
|
|
||||||
Returns filesystem status. Non-zero when problems have been detected,
|
|
||||||
so far only that the first copy of the partition table didn't pass
|
|
||||||
checks.
|
|
||||||
|
|
||||||
#### `GET_VIDPID`
|
|
||||||
|
|
||||||
```
|
|
||||||
syscall(TK1_SYSCALL_PRELOAD_STATUS, 0, 0, 0);
|
|
||||||
```
|
|
||||||
|
|
||||||
Returns Vendor and Product ID. Notably the serial number is not
|
|
||||||
returned, so a device app can't identify what particular TKey it is
|
|
||||||
running on.
|
|
||||||
|
|
||||||
## Developing firmware
|
## Developing firmware
|
||||||
|
|
||||||
@ -694,10 +346,7 @@ you might need to `make clean` before building, if you have already
|
|||||||
built before.
|
built before.
|
||||||
|
|
||||||
If you want debug prints to show up on the special TKey HID debug
|
If you want debug prints to show up on the special TKey HID debug
|
||||||
endpoint instead, define `-DTKEY_DEBUG`. This might mean you can't fit
|
endpoint instead, define `-DTKEY_DEBUG`.
|
||||||
the firmware in the ROM space available, however. You will get a
|
|
||||||
warning if it doesn't fit. In that case, just use explicit
|
|
||||||
`puts(IO_DEBUG, ...)` or `puts(IO_CDC, ...)` and so on.
|
|
||||||
|
|
||||||
Note that if you use `TKEY_DEBUG` you *must* have something listening
|
Note that if you use `TKEY_DEBUG` you *must* have something listening
|
||||||
on the corresponding HID device. It's usually the last HID device
|
on the corresponding HID device. It's usually the last HID device
|
||||||
@ -714,45 +363,6 @@ Most of the utility functions that the firmware use lives in
|
|||||||
but we have vendored it in for firmware use in `../tkey-libs`. See top
|
but we have vendored it in for firmware use in `../tkey-libs`. See top
|
||||||
README for how to update.
|
README for how to update.
|
||||||
|
|
||||||
### Preparing the filesystem
|
|
||||||
|
|
||||||
The TKey supports a simple filesystem. This filesystem must be
|
|
||||||
initiated before starting for the first time. You need a [TKey
|
|
||||||
Programmer Board](https://shop.tillitis.se/products/tkey-dev-kit) for
|
|
||||||
this part.
|
|
||||||
|
|
||||||
1. Choose your pre-loaded app. You /must/ have a pre-loaded app, for
|
|
||||||
example `testloadapp`. Build it with the OCI image we use. The
|
|
||||||
binary needs to produce the BLAKE2s digest in `allowed_app_digest`
|
|
||||||
`tk1/mgmt_app.c`.
|
|
||||||
|
|
||||||
2. Write the filesystem to flash:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ cd ../tools
|
|
||||||
$ ./load_preloaded_app.sh 0 ../fw/testloadapp/testloadapp.bin
|
|
||||||
```
|
|
||||||
|
|
||||||
If you want to use a different pre-loaded app you have to
|
|
||||||
|
|
||||||
1. Check the BLAKE2s digest of the app. You can use `tools/b2s` to
|
|
||||||
compute it.
|
|
||||||
|
|
||||||
2. Update the `allowed_app_digest` in `tk1/mgmt_app.c`.
|
|
||||||
|
|
||||||
3. Create a new `default_partition.bin` using the
|
|
||||||
`tools/partition_table`, typically:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ partition_table -app0 path/to/your/app.bin -o default_partition.bin
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Flash the filesystem image:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ ./load_preloaded_app.sh 0 path/to/your/app.bin
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test firmware
|
### Test firmware
|
||||||
|
|
||||||
The test firmware is in `testfw`. It's currently a bit of a hack and
|
The test firmware is in `testfw`. It's currently a bit of a hack and
|
||||||
@ -764,23 +374,5 @@ terminal program to the serial port device, even if it's running in
|
|||||||
qemu. It waits for you to type a character before starting the tests.
|
qemu. It waits for you to type a character before starting the tests.
|
||||||
|
|
||||||
It needs to be compiled with `-Os` instead of `-O2` in `CFLAGS` in the
|
It needs to be compiled with `-Os` instead of `-O2` in `CFLAGS` in the
|
||||||
ordinary `application_fpga/Makefile` to be able to fit in ROM.
|
ordinary `application_fpga/Makefile` to be able to fit in the 6 kByte
|
||||||
|
ROM.
|
||||||
### Test apps
|
|
||||||
|
|
||||||
There are a couple of test apps. All of them are controlled through
|
|
||||||
the USB CDC, typically by running picocom or similar terminal program,
|
|
||||||
like:
|
|
||||||
|
|
||||||
```
|
|
||||||
$ picocom /dev/ttyACM1
|
|
||||||
```
|
|
||||||
|
|
||||||
or similar.
|
|
||||||
|
|
||||||
- `fw/testapp`: Runs through a couple of tests that are now impossible
|
|
||||||
to do in the `testfw`.
|
|
||||||
- `fw/reset_test`: Interactively test different reset scenarios.
|
|
||||||
- `fw/testloadapp`: Interactively test management app things like
|
|
||||||
installing an app (hardcoded for a small happy blinking app, see
|
|
||||||
`blink.h` for the entire binary!) and to test verified boot.
|
|
||||||
|
@ -1,75 +0,0 @@
|
|||||||
P := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
|
||||||
LIBDIR ?= ../../tkey-libs
|
|
||||||
OBJCOPY ?= llvm-objcopy
|
|
||||||
CC = clang
|
|
||||||
CFLAGS = \
|
|
||||||
-target riscv32-unknown-none-elf \
|
|
||||||
-march=rv32iczmmul \
|
|
||||||
-mabi=ilp32 \
|
|
||||||
-mcmodel=medany \
|
|
||||||
-static \
|
|
||||||
-std=gnu99 \
|
|
||||||
-O2 \
|
|
||||||
-ffast-math \
|
|
||||||
-fno-common \
|
|
||||||
-fno-builtin-printf \
|
|
||||||
-fno-builtin-putchar \
|
|
||||||
-fno-builtin-memcpy \
|
|
||||||
-nostdlib \
|
|
||||||
-mno-relax \
|
|
||||||
-Wall \
|
|
||||||
-Wpedantic \
|
|
||||||
-Wno-language-extension-token \
|
|
||||||
-Werror \
|
|
||||||
-flto \
|
|
||||||
-g \
|
|
||||||
-I $(LIBDIR)/include \
|
|
||||||
-I $(LIBDIR) \
|
|
||||||
-DTKEY_DEBUG
|
|
||||||
|
|
||||||
AS = clang
|
|
||||||
|
|
||||||
ASFLAGS = \
|
|
||||||
-target riscv32-unknown-none-elf \
|
|
||||||
-march=rv32iczmmul \
|
|
||||||
-mabi=ilp32 \
|
|
||||||
-mno-relax
|
|
||||||
|
|
||||||
LDFLAGS = \
|
|
||||||
-T $(P)/app.lds \
|
|
||||||
-L $(LIBDIR) -lcommon
|
|
||||||
|
|
||||||
.PHONY: all
|
|
||||||
all: reset_test.bin
|
|
||||||
|
|
||||||
# Turn elf into bin for device
|
|
||||||
%.bin: %.elf
|
|
||||||
$(OBJCOPY) --input-target=elf32-littleriscv --output-target=binary $^ $@
|
|
||||||
chmod a-x $@
|
|
||||||
|
|
||||||
.PHONY: tkey-libs
|
|
||||||
tkey-libs:
|
|
||||||
make -C $(LIBDIR)
|
|
||||||
|
|
||||||
RESET_TEST_FMTFILES = *.[ch]
|
|
||||||
|
|
||||||
RESET_TEST_OBJS = \
|
|
||||||
$(P)/main.o \
|
|
||||||
$(P)/crt0.o \
|
|
||||||
$(P)/syscall.o
|
|
||||||
|
|
||||||
reset_test.elf: tkey-libs $(RESET_TEST_OBJS)
|
|
||||||
$(CC) $(CFLAGS) $(RESET_TEST_OBJS) $(LDFLAGS) -o $@
|
|
||||||
|
|
||||||
.PHONY: fmt
|
|
||||||
fmt:
|
|
||||||
clang-format --dry-run --ferror-limit=0 $(RESET_TEST_FMTFILES)
|
|
||||||
clang-format --verbose -i $(RESET_TEST_FMTFILES)
|
|
||||||
|
|
||||||
.PHONY: checkfmt
|
|
||||||
checkfmt:
|
|
||||||
clang-format --dry-run --ferror-limit=0 --Werror $(RESET_TEST_FMTFILES)
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
clean:
|
|
||||||
rm -f reset_test.bin reset_test.elf $(RESET_TEST_OBJS)
|
|
@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
* SPDX-FileCopyrightText: 2022 Tillitis AB <tillitis.se>
|
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
*/
|
|
||||||
|
|
||||||
OUTPUT_ARCH( "riscv" )
|
|
||||||
ENTRY(_start)
|
|
||||||
|
|
||||||
MEMORY
|
|
||||||
{
|
|
||||||
RAM (rwx) : ORIGIN = 0x40000000, LENGTH = 0x20000 /* 128 KB */
|
|
||||||
}
|
|
||||||
|
|
||||||
SECTIONS
|
|
||||||
{
|
|
||||||
.text.init :
|
|
||||||
{
|
|
||||||
*(.text.init)
|
|
||||||
} >RAM
|
|
||||||
|
|
||||||
.text :
|
|
||||||
{
|
|
||||||
. = ALIGN(4);
|
|
||||||
*(.text) /* .text sections (code) */
|
|
||||||
*(.text*) /* .text* sections (code) */
|
|
||||||
*(.rodata) /* .rodata sections (constants, strings, etc.) */
|
|
||||||
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
|
|
||||||
*(.srodata) /* .rodata sections (constants, strings, etc.) */
|
|
||||||
*(.srodata*) /* .rodata* sections (constants, strings, etc.) */
|
|
||||||
. = ALIGN(4);
|
|
||||||
_etext = .;
|
|
||||||
_sidata = _etext;
|
|
||||||
} >RAM
|
|
||||||
|
|
||||||
.data : AT (_etext)
|
|
||||||
{
|
|
||||||
. = ALIGN(4);
|
|
||||||
_sdata = .;
|
|
||||||
. = ALIGN(4);
|
|
||||||
*(.data) /* .data sections */
|
|
||||||
*(.data*) /* .data* sections */
|
|
||||||
*(.sdata) /* .sdata sections */
|
|
||||||
*(.sdata*) /* .sdata* sections */
|
|
||||||
. = ALIGN(4);
|
|
||||||
_edata = .;
|
|
||||||
} >RAM
|
|
||||||
|
|
||||||
/* Uninitialized data section */
|
|
||||||
.bss :
|
|
||||||
{
|
|
||||||
. = ALIGN(4);
|
|
||||||
_sbss = .;
|
|
||||||
*(.bss)
|
|
||||||
*(.bss*)
|
|
||||||
*(.sbss)
|
|
||||||
*(.sbss*)
|
|
||||||
*(COMMON)
|
|
||||||
|
|
||||||
. = ALIGN(4);
|
|
||||||
_ebss = .;
|
|
||||||
} >RAM
|
|
||||||
|
|
||||||
/* libcrt0/crt0.S inits stack to start just below end of RAM */
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2022 Tillitis AB <tillitis.se>
|
|
||||||
// SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
.section ".text.init"
|
|
||||||
.global _start
|
|
||||||
_start:
|
|
||||||
li x1, 0
|
|
||||||
li x2, 0
|
|
||||||
li x3, 0
|
|
||||||
li x4, 0
|
|
||||||
li x5, 0
|
|
||||||
li x6, 0
|
|
||||||
li x7, 0
|
|
||||||
li x8, 0
|
|
||||||
li x9, 0
|
|
||||||
li x10,0
|
|
||||||
li x11,0
|
|
||||||
li x12,0
|
|
||||||
li x13,0
|
|
||||||
li x14,0
|
|
||||||
li x15,0
|
|
||||||
li x16,0
|
|
||||||
li x17,0
|
|
||||||
li x18,0
|
|
||||||
li x19,0
|
|
||||||
li x20,0
|
|
||||||
li x21,0
|
|
||||||
li x22,0
|
|
||||||
li x23,0
|
|
||||||
li x24,0
|
|
||||||
li x25,0
|
|
||||||
li x26,0
|
|
||||||
li x27,0
|
|
||||||
li x28,0
|
|
||||||
li x29,0
|
|
||||||
li x30,0
|
|
||||||
li x31,0
|
|
||||||
|
|
||||||
/* init stack below 0x40020000 (TK1_RAM_BASE+TK1_RAM_SIZE) */
|
|
||||||
li sp, 0x4001fff0
|
|
||||||
|
|
||||||
/* zero-init bss section */
|
|
||||||
la a0, _sbss
|
|
||||||
la a1, _ebss
|
|
||||||
bge a0, a1, end_init_bss
|
|
||||||
|
|
||||||
loop_init_bss:
|
|
||||||
sw zero, 0(a0)
|
|
||||||
addi a0, a0, 4
|
|
||||||
blt a0, a1, loop_init_bss
|
|
||||||
|
|
||||||
end_init_bss:
|
|
||||||
call main
|
|
@ -1,161 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2022, 2023 - Tillitis AB
|
|
||||||
* SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <tkey/assert.h>
|
|
||||||
#include <tkey/debug.h>
|
|
||||||
#include <tkey/io.h>
|
|
||||||
#include <tkey/led.h>
|
|
||||||
#include <tkey/lib.h>
|
|
||||||
#include <tkey/tk1_mem.h>
|
|
||||||
|
|
||||||
#include "../testapp/syscall.h"
|
|
||||||
#include "../tk1/proto.h"
|
|
||||||
#include "../tk1/resetinfo.h"
|
|
||||||
#include "../tk1/syscall_num.h"
|
|
||||||
|
|
||||||
// Converts a single hex character to its integer value
|
|
||||||
static uint8_t hex_char_to_byte(uint8_t c)
|
|
||||||
{
|
|
||||||
if (c >= '0' && c <= '9')
|
|
||||||
return c - '0';
|
|
||||||
if (c >= 'A' && c <= 'F')
|
|
||||||
return c - 'A' + 10;
|
|
||||||
if (c >= 'a' && c <= 'f')
|
|
||||||
return c - 'a' + 10;
|
|
||||||
return 0; // Invalid character, should not happen if input is valid
|
|
||||||
}
|
|
||||||
|
|
||||||
// Converts a 64-char hex string into a 32-byte array
|
|
||||||
int hex_string_to_bytes(uint8_t *hex_str, uint8_t *out_bytes, size_t out_len)
|
|
||||||
{
|
|
||||||
if (!hex_str || !out_bytes || out_len < 32)
|
|
||||||
return -1; // Error handling
|
|
||||||
|
|
||||||
for (size_t i = 0; i < 32; i++) {
|
|
||||||
out_bytes[i] = (hex_char_to_byte(hex_str[i * 2]) << 4) |
|
|
||||||
hex_char_to_byte(hex_str[i * 2 + 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0; // Success
|
|
||||||
}
|
|
||||||
//---------------------------------------
|
|
||||||
|
|
||||||
#define BUFSIZE 32
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
uint8_t available = 0;
|
|
||||||
uint8_t cmdbuf[BUFSIZE] = {0};
|
|
||||||
enum ioend endpoint = IO_NONE;
|
|
||||||
led_set(LED_BLUE);
|
|
||||||
struct reset rst = {0};
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
|
|
||||||
puts(IO_CDC, "reset_test: Waiting for command\r\n");
|
|
||||||
|
|
||||||
memset(cmdbuf, 0, BUFSIZE);
|
|
||||||
|
|
||||||
// Wait for data
|
|
||||||
if (readselect(IO_CDC, &endpoint, &available) < 0) {
|
|
||||||
assert(1 == 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (read(IO_CDC, cmdbuf, BUFSIZE, available) < 0) {
|
|
||||||
// read failed! I/O broken? Just redblink.
|
|
||||||
assert(1 == 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
led_set(LED_BLUE | LED_RED);
|
|
||||||
|
|
||||||
switch (cmdbuf[0]) {
|
|
||||||
case '1':
|
|
||||||
// Reset into default state
|
|
||||||
|
|
||||||
rst.type = START_DEFAULT;
|
|
||||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '2':
|
|
||||||
// Reset and load app from client
|
|
||||||
|
|
||||||
rst.type = START_CLIENT;
|
|
||||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '3':
|
|
||||||
// Reset and load app from second flash slot
|
|
||||||
|
|
||||||
rst.type = START_FLASH1;
|
|
||||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '4': {
|
|
||||||
// Reset and load app from client with verification
|
|
||||||
// using an invalid digest.
|
|
||||||
//
|
|
||||||
// Should cause firmware to refuse to start app.
|
|
||||||
|
|
||||||
uint8_t string[] = "0123456789abcdef0123456789abcdef012"
|
|
||||||
"3456789abcdef0123456789abcdef";
|
|
||||||
rst.type = START_CLIENT_VER;
|
|
||||||
hex_string_to_bytes(string, (uint8_t *)&rst.app_digest,
|
|
||||||
sizeof(rst.app_digest));
|
|
||||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case '5': {
|
|
||||||
// Reset and load app from client with verification
|
|
||||||
// using a digest matching the example app (blue.bin)
|
|
||||||
// from tkey-libs
|
|
||||||
|
|
||||||
uint8_t tkeylibs_example_app_digest[] =
|
|
||||||
"96bb4c90603dbbbe09b9a1d7259b5e9e61bedd89a897105c30"
|
|
||||||
"c9d4bf66a98d97";
|
|
||||||
rst.type = START_CLIENT_VER;
|
|
||||||
hex_string_to_bytes(tkeylibs_example_app_digest,
|
|
||||||
(uint8_t *)&rst.app_digest,
|
|
||||||
sizeof(rst.app_digest));
|
|
||||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case '6': {
|
|
||||||
// Reset and load app from second flash slot with
|
|
||||||
// verification using an invalid digest.
|
|
||||||
//
|
|
||||||
// Should cause firmware to refuse to start app.
|
|
||||||
|
|
||||||
uint8_t string[] = "0123456789abcdef0123456789abcdef012"
|
|
||||||
"3456789abcdef0123456789abcdef";
|
|
||||||
rst.type = START_FLASH1_VER;
|
|
||||||
hex_string_to_bytes(string, (uint8_t *)&rst.app_digest,
|
|
||||||
sizeof(rst.app_digest));
|
|
||||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
case '7': {
|
|
||||||
// Reset and load app from second flash slot with
|
|
||||||
// verification using a digest matching the example app
|
|
||||||
// (blue.bin) from tkey-libs
|
|
||||||
//
|
|
||||||
// Blue.bin has to be present on flash in the second
|
|
||||||
// preloaded app slot (slot 1).
|
|
||||||
|
|
||||||
uint8_t tkeylibs_example_app_digest[] =
|
|
||||||
"96bb4c90603dbbbe09b9a1d7259b5e9e61bedd89a897105c30"
|
|
||||||
"c9d4bf66a98d97";
|
|
||||||
rst.type = START_FLASH1_VER;
|
|
||||||
hex_string_to_bytes(tkeylibs_example_app_digest,
|
|
||||||
(uint8_t *)&rst.app_digest,
|
|
||||||
sizeof(rst.app_digest));
|
|
||||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
|
||||||
} break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,85 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2024 Tillitis AB <tillitis.se>
|
|
||||||
// SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
#include "../tk1/picorv32/custom_ops.S"
|
|
||||||
|
|
||||||
.section ".text"
|
|
||||||
.globl syscall
|
|
||||||
|
|
||||||
|
|
||||||
syscall:
|
|
||||||
// Save registers to stack
|
|
||||||
addi sp, sp, -32*4
|
|
||||||
sw x0, 0*4(sp)
|
|
||||||
sw x1, 1*4(sp)
|
|
||||||
// x2 (sp) is assumed to be preserved by the interrupt handler.
|
|
||||||
sw x3, 3*4(sp)
|
|
||||||
sw x4, 4*4(sp)
|
|
||||||
sw x5, 5*4(sp)
|
|
||||||
sw x6, 6*4(sp)
|
|
||||||
sw x7, 7*4(sp)
|
|
||||||
sw x8, 8*4(sp)
|
|
||||||
sw x9, 9*4(sp)
|
|
||||||
// x10 (a0) will contain syscall return value. And should not be saved.
|
|
||||||
sw x11, 11*4(sp)
|
|
||||||
sw x12, 12*4(sp)
|
|
||||||
sw x13, 13*4(sp)
|
|
||||||
sw x14, 14*4(sp)
|
|
||||||
sw x15, 15*4(sp)
|
|
||||||
sw x16, 16*4(sp)
|
|
||||||
sw x17, 17*4(sp)
|
|
||||||
sw x18, 18*4(sp)
|
|
||||||
sw x19, 19*4(sp)
|
|
||||||
sw x20, 20*4(sp)
|
|
||||||
sw x21, 21*4(sp)
|
|
||||||
sw x22, 22*4(sp)
|
|
||||||
sw x23, 23*4(sp)
|
|
||||||
sw x24, 24*4(sp)
|
|
||||||
sw x25, 25*4(sp)
|
|
||||||
sw x26, 26*4(sp)
|
|
||||||
sw x27, 27*4(sp)
|
|
||||||
sw x28, 28*4(sp)
|
|
||||||
sw x29, 29*4(sp)
|
|
||||||
sw x30, 30*4(sp)
|
|
||||||
sw x31, 31*4(sp)
|
|
||||||
|
|
||||||
// Trigger syscall interrupt
|
|
||||||
li t1, 0xe1000000 // Syscall interrupt trigger address
|
|
||||||
sw zero, 0(t1) // Trigger interrupt
|
|
||||||
|
|
||||||
// Restore registers from stack
|
|
||||||
lw x0, 0*4(sp)
|
|
||||||
lw x1, 1*4(sp)
|
|
||||||
// x2 (sp) is assumed to be preserved by the interrupt handler.
|
|
||||||
lw x3, 3*4(sp)
|
|
||||||
lw x4, 4*4(sp)
|
|
||||||
lw x5, 5*4(sp)
|
|
||||||
lw x6, 6*4(sp)
|
|
||||||
lw x7, 7*4(sp)
|
|
||||||
lw x8, 8*4(sp)
|
|
||||||
lw x9, 9*4(sp)
|
|
||||||
// x10 (a0) contains syscall return value. And should not be destroyed.
|
|
||||||
lw x11, 11*4(sp)
|
|
||||||
lw x12, 12*4(sp)
|
|
||||||
lw x13, 13*4(sp)
|
|
||||||
lw x14, 14*4(sp)
|
|
||||||
lw x15, 15*4(sp)
|
|
||||||
lw x16, 16*4(sp)
|
|
||||||
lw x17, 17*4(sp)
|
|
||||||
lw x18, 18*4(sp)
|
|
||||||
lw x19, 19*4(sp)
|
|
||||||
lw x20, 20*4(sp)
|
|
||||||
lw x21, 21*4(sp)
|
|
||||||
lw x22, 22*4(sp)
|
|
||||||
lw x23, 23*4(sp)
|
|
||||||
lw x24, 24*4(sp)
|
|
||||||
lw x25, 25*4(sp)
|
|
||||||
lw x26, 26*4(sp)
|
|
||||||
lw x27, 27*4(sp)
|
|
||||||
lw x28, 28*4(sp)
|
|
||||||
lw x29, 29*4(sp)
|
|
||||||
lw x30, 30*4(sp)
|
|
||||||
lw x31, 31*4(sp)
|
|
||||||
addi sp, sp, 32*4
|
|
||||||
|
|
||||||
ret
|
|
@ -50,7 +50,9 @@ all: testapp.bin
|
|||||||
tkey-libs:
|
tkey-libs:
|
||||||
make -C $(LIBDIR)
|
make -C $(LIBDIR)
|
||||||
|
|
||||||
TESTAPP_FMTFILES = *.[ch]
|
TESTAPP_FMTFILES = \
|
||||||
|
$(P)/main.c \
|
||||||
|
$(P)/syscall.h
|
||||||
|
|
||||||
TESTAPP_OBJS = \
|
TESTAPP_OBJS = \
|
||||||
$(P)/main.o \
|
$(P)/main.o \
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
#include <tkey/tk1_mem.h>
|
#include <tkey/tk1_mem.h>
|
||||||
|
|
||||||
#include "../tk1/proto.h"
|
#include "../tk1/proto.h"
|
||||||
#include "../tk1/resetinfo.h"
|
|
||||||
#include "../tk1/syscall_num.h"
|
#include "../tk1/syscall_num.h"
|
||||||
#include "syscall.h"
|
#include "syscall.h"
|
||||||
|
|
||||||
@ -122,49 +121,13 @@ int main(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// But a syscall to get parts of UDI should be able to run
|
// But a syscall to get parts of UDI should be able to run
|
||||||
int vidpid = syscall(TK1_SYSCALL_GET_VIDPID, 0, 0, 0);
|
int vidpid = syscall(TK1_SYSCALL_GET_VIDPID, 0);
|
||||||
|
|
||||||
if (vidpid != 0x00010203) {
|
if (vidpid != 0x00010203) {
|
||||||
failmsg("Expected VID/PID to be 0x00010203");
|
failmsg("Expected VID/PID to be 0x00010203");
|
||||||
anyfailed = 1;
|
anyfailed = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
puts(IO_CDC, "\r\nAllocating storage area...");
|
|
||||||
|
|
||||||
if (syscall(TK1_SYSCALL_ALLOC_AREA, 0, 0, 0) != 0) {
|
|
||||||
failmsg("Failed to allocate storage area");
|
|
||||||
}
|
|
||||||
puts(IO_CDC, "done.\r\n");
|
|
||||||
|
|
||||||
puts(IO_CDC, "\r\nWriting to storage area...");
|
|
||||||
|
|
||||||
uint8_t out_data[14] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13};
|
|
||||||
if (syscall(TK1_SYSCALL_WRITE_DATA, 0, (uint32_t)out_data,
|
|
||||||
sizeof(out_data)) != 0) {
|
|
||||||
failmsg("Failed to write to storage area");
|
|
||||||
}
|
|
||||||
puts(IO_CDC, "done.\r\n");
|
|
||||||
|
|
||||||
puts(IO_CDC, "\r\nReading from storage area...");
|
|
||||||
|
|
||||||
uint8_t in_data[14] = {0};
|
|
||||||
if (syscall(TK1_SYSCALL_READ_DATA, 0, (uint32_t)in_data,
|
|
||||||
sizeof(in_data)) != 0) {
|
|
||||||
failmsg("Failed to write to storage area");
|
|
||||||
}
|
|
||||||
if (!memeq(in_data, out_data, sizeof(in_data))) {
|
|
||||||
failmsg("Failed to read back data from storage area");
|
|
||||||
anyfailed = 1;
|
|
||||||
}
|
|
||||||
puts(IO_CDC, "done.\r\n");
|
|
||||||
|
|
||||||
puts(IO_CDC, "\r\nDeallocating storage area...");
|
|
||||||
|
|
||||||
if (syscall(TK1_SYSCALL_DEALLOC_AREA, 0, 0, 0) != 0) {
|
|
||||||
failmsg("Failed to deallocate storage area");
|
|
||||||
}
|
|
||||||
puts(IO_CDC, "done.\r\n");
|
|
||||||
|
|
||||||
uint32_t cdi_local[CDI_WORDS];
|
uint32_t cdi_local[CDI_WORDS];
|
||||||
uint32_t cdi_local2[CDI_WORDS];
|
uint32_t cdi_local2[CDI_WORDS];
|
||||||
wordcpy_s(cdi_local, CDI_WORDS, (void *)cdi, CDI_WORDS);
|
wordcpy_s(cdi_local, CDI_WORDS, (void *)cdi, CDI_WORDS);
|
||||||
@ -260,10 +223,7 @@ int main(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (in == '+') {
|
if (in == '+') {
|
||||||
struct reset rst;
|
syscall(TK1_SYSCALL_RESET, 0);
|
||||||
memset(&rst, 0, sizeof(rst));
|
|
||||||
rst.type = START_DEFAULT;
|
|
||||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
write(IO_CDC, &in, 1);
|
write(IO_CDC, &in, 1);
|
||||||
|
@ -6,6 +6,6 @@
|
|||||||
#ifndef TKEY_APP_SYSCALL_H
|
#ifndef TKEY_APP_SYSCALL_H
|
||||||
#define TKEY_APP_SYSCALL_H
|
#define TKEY_APP_SYSCALL_H
|
||||||
|
|
||||||
int syscall(uint32_t number, uint32_t arg1, uint32_t arg2, uint32_t arg3);
|
int syscall(uint32_t number, uint32_t arg1);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Uses ../.clang-format
|
# Uses ../.clang-format
|
||||||
FMTFILES=*.[ch]
|
FMTFILES=main.c
|
||||||
.PHONY: fmt
|
.PHONY: fmt
|
||||||
fmt:
|
fmt:
|
||||||
clang-format --dry-run --ferror-limit=0 $(FMTFILES)
|
clang-format --dry-run --ferror-limit=0 $(FMTFILES)
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
P := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
|
|
||||||
LIBDIR ?= ../../tkey-libs
|
|
||||||
OBJCOPY ?= llvm-objcopy
|
|
||||||
CC = clang
|
|
||||||
CFLAGS = \
|
|
||||||
-target riscv32-unknown-none-elf \
|
|
||||||
-march=rv32iczmmul \
|
|
||||||
-mabi=ilp32 \
|
|
||||||
-mcmodel=medany \
|
|
||||||
-static \
|
|
||||||
-std=gnu99 \
|
|
||||||
-Os \
|
|
||||||
-ffast-math \
|
|
||||||
-fno-common \
|
|
||||||
-fno-builtin-printf \
|
|
||||||
-fno-builtin-putchar \
|
|
||||||
-fno-builtin-memcpy \
|
|
||||||
-nostdlib \
|
|
||||||
-mno-relax \
|
|
||||||
-Wall \
|
|
||||||
-Wpedantic \
|
|
||||||
-Wno-language-extension-token \
|
|
||||||
-Werror \
|
|
||||||
-flto \
|
|
||||||
-g \
|
|
||||||
-I $(LIBDIR)/include \
|
|
||||||
-I $(LIBDIR)
|
|
||||||
|
|
||||||
AS = clang
|
|
||||||
|
|
||||||
ASFLAGS = \
|
|
||||||
-target riscv32-unknown-none-elf \
|
|
||||||
-march=rv32iczmmul \
|
|
||||||
-mabi=ilp32 \
|
|
||||||
-mno-relax
|
|
||||||
|
|
||||||
LDFLAGS = \
|
|
||||||
-T $(LIBDIR)/app.lds \
|
|
||||||
-L $(LIBDIR) -lcrt0 -lcommon -lmonocypher -lblake2s
|
|
||||||
|
|
||||||
.PHONY: all
|
|
||||||
all: testloadapp.bin
|
|
||||||
|
|
||||||
# Turn elf into bin for device
|
|
||||||
%.bin: %.elf
|
|
||||||
$(OBJCOPY) --input-target=elf32-littleriscv --output-target=binary $^ $@
|
|
||||||
chmod a-x $@
|
|
||||||
|
|
||||||
.PHONY: tkey-libs
|
|
||||||
tkey-libs:
|
|
||||||
make -C $(LIBDIR)
|
|
||||||
|
|
||||||
TESTLOADAPP_FMTFILES = *.[ch]
|
|
||||||
|
|
||||||
TESTLOADAPP_OBJS = \
|
|
||||||
$(P)/main.o \
|
|
||||||
../testapp/syscall.o \
|
|
||||||
|
|
||||||
testloadapp.elf: tkey-libs $(TESTLOADAPP_OBJS)
|
|
||||||
$(CC) $(CFLAGS) $(TESTLOADAPP_OBJS) $(LDFLAGS) -o $@
|
|
||||||
|
|
||||||
.PHONY: fmt
|
|
||||||
fmt:
|
|
||||||
clang-format --dry-run --ferror-limit=0 $(TESTLOADAPP_FMTFILES)
|
|
||||||
clang-format --verbose -i $(TESTLOADAPP_FMTFILES)
|
|
||||||
|
|
||||||
.PHONY: checkfmt
|
|
||||||
checkfmt:
|
|
||||||
clang-format --dry-run --ferror-limit=0 --Werror $(TESTLOADAPP_FMTFILES)
|
|
||||||
|
|
||||||
.PHONY: clean
|
|
||||||
clean:
|
|
||||||
rm -f testloadapp.bin testloadapp.elf $(TESTLOADAPP_OBJS)
|
|
@ -1,28 +0,0 @@
|
|||||||
#ifndef BLINK_APP_H
|
|
||||||
#define BLINK_APP_H
|
|
||||||
|
|
||||||
uint8_t blink[] = {
|
|
||||||
0x81, 0x40, 0x01, 0x41, 0x81, 0x41, 0x01, 0x42, 0x81, 0x42, 0x01, 0x43,
|
|
||||||
0x81, 0x43, 0x01, 0x44, 0x81, 0x44, 0x01, 0x45, 0x81, 0x45, 0x01, 0x46,
|
|
||||||
0x81, 0x46, 0x01, 0x47, 0x81, 0x47, 0x01, 0x48, 0x81, 0x48, 0x01, 0x49,
|
|
||||||
0x81, 0x49, 0x01, 0x4a, 0x81, 0x4a, 0x01, 0x4b, 0x81, 0x4b, 0x01, 0x4c,
|
|
||||||
0x81, 0x4c, 0x01, 0x4d, 0x81, 0x4d, 0x01, 0x4e, 0x81, 0x4e, 0x01, 0x4f,
|
|
||||||
0x81, 0x4f, 0x37, 0x01, 0x02, 0x40, 0x41, 0x11, 0x17, 0x05, 0x00, 0x00,
|
|
||||||
0x13, 0x05, 0x45, 0x0c, 0x97, 0x05, 0x00, 0x00, 0x93, 0x85, 0xc5, 0x0b,
|
|
||||||
0x63, 0x57, 0xb5, 0x00, 0x23, 0x20, 0x05, 0x00, 0x11, 0x05, 0xe3, 0x4d,
|
|
||||||
0xb5, 0xfe, 0x97, 0x00, 0x00, 0x00, 0xe7, 0x80, 0xa0, 0x00, 0x00, 0x00,
|
|
||||||
0x41, 0x11, 0x37, 0x05, 0x00, 0xff, 0x11, 0x48, 0xe1, 0x66, 0x13, 0x86,
|
|
||||||
0xf6, 0x69, 0x93, 0x86, 0x06, 0x6a, 0x09, 0x47, 0x85, 0x47, 0x23, 0x22,
|
|
||||||
0x05, 0x03, 0x02, 0xc2, 0x92, 0x45, 0x63, 0x68, 0xb6, 0x00, 0x92, 0x45,
|
|
||||||
0x85, 0x05, 0x2e, 0xc2, 0x92, 0x45, 0xe3, 0xec, 0xd5, 0xfe, 0x58, 0xd1,
|
|
||||||
0x02, 0xc4, 0xa2, 0x45, 0x63, 0x68, 0xb6, 0x00, 0xa2, 0x45, 0x85, 0x05,
|
|
||||||
0x2e, 0xc4, 0xa2, 0x45, 0xe3, 0xec, 0xd5, 0xfe, 0x5c, 0xd1, 0x02, 0xc6,
|
|
||||||
0xb2, 0x45, 0xe3, 0x66, 0xb6, 0xfc, 0xb2, 0x45, 0x85, 0x05, 0x2e, 0xc6,
|
|
||||||
0xb2, 0x45, 0xe3, 0xec, 0xd5, 0xfe, 0x75, 0xbf, 0x19, 0xca, 0x2a, 0x96,
|
|
||||||
0xaa, 0x86, 0x03, 0xc7, 0x05, 0x00, 0x23, 0x80, 0xe6, 0x00, 0x85, 0x06,
|
|
||||||
0x85, 0x05, 0xe3, 0x9a, 0xc6, 0xfe, 0x82, 0x80, 0x11, 0xca, 0x0a, 0x06,
|
|
||||||
0x2a, 0x96, 0xaa, 0x86, 0x98, 0x41, 0x98, 0xc2, 0x91, 0x06, 0x91, 0x05,
|
|
||||||
0xe3, 0x9c, 0xc6, 0xfe, 0x82, 0x80, 0x01, 0xca, 0x2a, 0x96, 0xaa, 0x86,
|
|
||||||
0x23, 0x80, 0xb6, 0x00, 0x85, 0x06, 0xe3, 0x9d, 0xc6, 0xfe, 0x82, 0x80};
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,218 +0,0 @@
|
|||||||
#include <blake2s/blake2s.h>
|
|
||||||
#include <monocypher/monocypher-ed25519.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <tkey/debug.h>
|
|
||||||
#include <tkey/lib.h>
|
|
||||||
#include <tkey/tk1_mem.h>
|
|
||||||
|
|
||||||
#include "../testapp/syscall.h"
|
|
||||||
#include "../tk1/resetinfo.h"
|
|
||||||
#include "../tk1/syscall_num.h"
|
|
||||||
#include "blink.h"
|
|
||||||
#include "tkey/assert.h"
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
static volatile uint32_t *cdi = (volatile uint32_t *) TK1_MMIO_TK1_CDI_FIRST;
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
int install_app(uint8_t secret_key[64])
|
|
||||||
{
|
|
||||||
uint8_t app_digest[32];
|
|
||||||
uint8_t app_signature[64];
|
|
||||||
size_t app_size = sizeof(blink);
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
ret = syscall(TK1_SYSCALL_PRELOAD_DELETE, 0, 0, 0);
|
|
||||||
|
|
||||||
if (ret != 0) {
|
|
||||||
puts(IO_CDC, "couldn't delete preloaded app. error: 0x");
|
|
||||||
putinthex(IO_CDC, ret);
|
|
||||||
puts(IO_CDC, "\r\n");
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = syscall(TK1_SYSCALL_PRELOAD_STORE, 0, (uint32_t)blink,
|
|
||||||
sizeof(blink));
|
|
||||||
|
|
||||||
if (ret != 0) {
|
|
||||||
puts(IO_CDC, "couldn't store app, error: 0x");
|
|
||||||
putinthex(IO_CDC, ret);
|
|
||||||
puts(IO_CDC, "\r\n");
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
puts(IO_CDC, "blink: ");
|
|
||||||
putinthex(IO_CDC, (uint32_t)blink);
|
|
||||||
puts(IO_CDC, "\r\n");
|
|
||||||
|
|
||||||
puts(IO_CDC, "blink[0]: ");
|
|
||||||
putinthex(IO_CDC, blink[0]);
|
|
||||||
puts(IO_CDC, "\r\n");
|
|
||||||
|
|
||||||
puts(IO_CDC, "sizeof(blink): ");
|
|
||||||
putinthex(IO_CDC, sizeof(blink));
|
|
||||||
puts(IO_CDC, "\r\n");
|
|
||||||
|
|
||||||
if (blake2s(app_digest, 32, NULL, 0, blink, sizeof(blink)) != 0) {
|
|
||||||
puts(IO_CDC, "couldn't compute digest\r\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
crypto_ed25519_sign(app_signature, secret_key, app_digest,
|
|
||||||
sizeof(app_digest));
|
|
||||||
|
|
||||||
puts(IO_CDC, "app_digest:\r\n");
|
|
||||||
hexdump(IO_CDC, app_digest, sizeof(app_digest));
|
|
||||||
puts(IO_CDC, "\r\n");
|
|
||||||
|
|
||||||
puts(IO_CDC, "app_signature:\r\n");
|
|
||||||
hexdump(IO_CDC, app_signature, sizeof(app_signature));
|
|
||||||
puts(IO_CDC, "\r\n");
|
|
||||||
|
|
||||||
puts(IO_CDC, "secret_key:\r\n");
|
|
||||||
hexdump(IO_CDC, secret_key, 64);
|
|
||||||
puts(IO_CDC, "\r\n");
|
|
||||||
|
|
||||||
ret = syscall(TK1_SYSCALL_PRELOAD_STORE_FIN, app_size,
|
|
||||||
(uint32_t)app_digest, (uint32_t)app_signature);
|
|
||||||
|
|
||||||
if (ret != 0) {
|
|
||||||
puts(IO_CDC, "couldn't finalize storing app, error:");
|
|
||||||
putinthex(IO_CDC, ret);
|
|
||||||
puts(IO_CDC, "\r\n");
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int verify(uint8_t pubkey[32])
|
|
||||||
{
|
|
||||||
uint8_t app_digest[32];
|
|
||||||
uint8_t app_signature[64];
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
// pubkey we already have
|
|
||||||
// read signature
|
|
||||||
// read digest
|
|
||||||
ret = syscall(TK1_SYSCALL_PRELOAD_GET_DIGSIG, (uint32_t)app_digest,
|
|
||||||
(uint32_t)app_signature, 0);
|
|
||||||
|
|
||||||
if (ret != 0) {
|
|
||||||
puts(IO_CDC, "couldn't get digsig, error:");
|
|
||||||
putinthex(IO_CDC, ret);
|
|
||||||
puts(IO_CDC, "\r\n");
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
puts(IO_CDC, "app_digest:\r\n");
|
|
||||||
hexdump(IO_CDC, app_digest, sizeof(app_digest));
|
|
||||||
puts(IO_CDC, "\r\n");
|
|
||||||
|
|
||||||
puts(IO_CDC, "app_signature:\r\n");
|
|
||||||
hexdump(IO_CDC, app_signature, sizeof(app_signature));
|
|
||||||
puts(IO_CDC, "\r\n");
|
|
||||||
|
|
||||||
puts(IO_CDC, "pubkey:\r\n");
|
|
||||||
hexdump(IO_CDC, pubkey, 32);
|
|
||||||
puts(IO_CDC, "\r\n");
|
|
||||||
|
|
||||||
puts(IO_CDC, "Checking signature...\r\n");
|
|
||||||
|
|
||||||
if (crypto_ed25519_check(app_signature, pubkey, app_digest,
|
|
||||||
sizeof(app_digest)) != 0) {
|
|
||||||
puts(IO_CDC, "signature check failed\r\n");
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
puts(IO_CDC, "Resetting into pre loaded app (slot 2)...\r\n");
|
|
||||||
|
|
||||||
// syscall reset flash1_ver with app_digest
|
|
||||||
struct reset rst;
|
|
||||||
rst.type = START_FLASH1_VER;
|
|
||||||
memcpy_s(rst.app_digest, sizeof(rst.app_digest), app_digest,
|
|
||||||
sizeof(app_digest));
|
|
||||||
memset(rst.next_app_data, 0, sizeof(rst.next_app_data));
|
|
||||||
|
|
||||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
|
||||||
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset_from_client(void)
|
|
||||||
{
|
|
||||||
struct reset rst = {0};
|
|
||||||
|
|
||||||
rst.type = START_CLIENT;
|
|
||||||
|
|
||||||
syscall(TK1_SYSCALL_RESET, (uint32_t)&rst, 0, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void)
|
|
||||||
{
|
|
||||||
uint8_t secret_key[64];
|
|
||||||
uint8_t pubkey[32];
|
|
||||||
enum ioend endpoint;
|
|
||||||
uint8_t available;
|
|
||||||
uint8_t in = 0;
|
|
||||||
|
|
||||||
// Generate a key pair from CDI
|
|
||||||
crypto_ed25519_key_pair(secret_key, pubkey, (uint8_t *)cdi);
|
|
||||||
|
|
||||||
if (readselect(IO_CDC, &endpoint, &available) < 0) {
|
|
||||||
// readselect failed! I/O broken? Just redblink.
|
|
||||||
assert(1 == 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (read(IO_CDC, &in, 1, 1) < 0) {
|
|
||||||
// read failed! I/O broken? Just redblink.
|
|
||||||
assert(1 == 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
puts(IO_CDC, "Hello from testloadapp! 0 = install app in slot 1, 1 = "
|
|
||||||
"verify app, 2 == load app from client\r\n");
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
if (readselect(IO_CDC, &endpoint, &available) < 0) {
|
|
||||||
// readselect failed! I/O broken? Just redblink.
|
|
||||||
assert(1 == 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (read(IO_CDC, &in, 1, 1) < 0) {
|
|
||||||
// read failed! I/O broken? Just redblink.
|
|
||||||
assert(1 == 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (in) {
|
|
||||||
case '0':
|
|
||||||
if (install_app(secret_key) < 0) {
|
|
||||||
puts(IO_CDC, "Failed to install app\r\n");
|
|
||||||
} else {
|
|
||||||
puts(IO_CDC, "Installed app!\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '1':
|
|
||||||
if (verify(pubkey) < 0) {
|
|
||||||
puts(IO_CDC, "Failed to verify app\r\n");
|
|
||||||
} else {
|
|
||||||
puts(IO_CDC, "Verified app!\r\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '2':
|
|
||||||
reset_from_client();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
// Copyright (C) 2024 - Tillitis AB
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <tkey/assert.h>
|
|
||||||
#include <tkey/lib.h>
|
|
||||||
#include <tkey/tk1_mem.h>
|
|
||||||
|
|
||||||
#include "auth_app.h"
|
|
||||||
#include "blake2s/blake2s.h"
|
|
||||||
#include "partition_table.h"
|
|
||||||
#include "rng.h"
|
|
||||||
|
|
||||||
static volatile uint32_t *cdi = (volatile uint32_t *)TK1_MMIO_TK1_CDI_FIRST;
|
|
||||||
|
|
||||||
/* Calculates the authentication digest based on a supplied nonce and the CDI.
|
|
||||||
* Requires that the CDI is already calculated and stored */
|
|
||||||
static void calculate_auth_digest(uint8_t *nonce, uint8_t *auth_digest)
|
|
||||||
{
|
|
||||||
assert(nonce != NULL);
|
|
||||||
assert(auth_digest != NULL);
|
|
||||||
|
|
||||||
blake2s_ctx ctx = {0};
|
|
||||||
|
|
||||||
// Generate a 16 byte authentication digest
|
|
||||||
int blake2err = blake2s_init(&ctx, 16, NULL, 0);
|
|
||||||
assert(blake2err == 0);
|
|
||||||
blake2s_update(&ctx, (const void *)cdi, 32);
|
|
||||||
blake2s_update(&ctx, nonce, 16);
|
|
||||||
blake2s_final(&ctx, auth_digest);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Generates a 16 byte nonce */
|
|
||||||
static void generate_nonce(uint32_t *nonce)
|
|
||||||
{
|
|
||||||
assert(nonce != NULL);
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < 4; i++) {
|
|
||||||
nonce[i] = rng_get_word();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
/* Returns the authentication digest and random nonce. Requires that the CDI is
|
|
||||||
* already calculated and stored */
|
|
||||||
void auth_app_create(struct auth_metadata *auth_table)
|
|
||||||
{
|
|
||||||
assert(auth_table != NULL);
|
|
||||||
|
|
||||||
uint8_t nonce[16] = {0};
|
|
||||||
uint8_t auth_digest[16] = {0};
|
|
||||||
|
|
||||||
generate_nonce((uint32_t *)nonce);
|
|
||||||
|
|
||||||
calculate_auth_digest(nonce, auth_digest);
|
|
||||||
|
|
||||||
memcpy_s(auth_table->authentication_digest, 16, auth_digest, 16);
|
|
||||||
memcpy_s(auth_table->nonce, 16, nonce, 16);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool auth_app_authenticate(struct auth_metadata *auth_table)
|
|
||||||
{
|
|
||||||
assert(auth_table != NULL);
|
|
||||||
|
|
||||||
uint8_t auth_digest[16] = {0};
|
|
||||||
|
|
||||||
calculate_auth_digest(auth_table->nonce, auth_digest);
|
|
||||||
|
|
||||||
if (memeq(auth_digest, auth_table->authentication_digest, 16)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
// Copyright (C) 2024 - Tillitis AB
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
|
|
||||||
#ifndef AUTH_APP_H
|
|
||||||
#define AUTH_APP_H
|
|
||||||
|
|
||||||
#include "partition_table.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
|
|
||||||
void auth_app_create(struct auth_metadata *auth_table);
|
|
||||||
bool auth_app_authenticate(struct auth_metadata *auth_table);
|
|
||||||
|
|
||||||
#endif
|
|
116
hw/application_fpga/fw/tk1/blake2s/LICENSE
Normal file
116
hw/application_fpga/fw/tk1/blake2s/LICENSE
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
CC0 1.0 Universal
|
||||||
|
|
||||||
|
Statement of Purpose
|
||||||
|
|
||||||
|
The laws of most jurisdictions throughout the world automatically confer
|
||||||
|
exclusive Copyright and Related Rights (defined below) upon the creator and
|
||||||
|
subsequent owner(s) (each and all, an "owner") of an original work of
|
||||||
|
authorship and/or a database (each, a "Work").
|
||||||
|
|
||||||
|
Certain owners wish to permanently relinquish those rights to a Work for the
|
||||||
|
purpose of contributing to a commons of creative, cultural and scientific
|
||||||
|
works ("Commons") that the public can reliably and without fear of later
|
||||||
|
claims of infringement build upon, modify, incorporate in other works, reuse
|
||||||
|
and redistribute as freely as possible in any form whatsoever and for any
|
||||||
|
purposes, including without limitation commercial purposes. These owners may
|
||||||
|
contribute to the Commons to promote the ideal of a free culture and the
|
||||||
|
further production of creative, cultural and scientific works, or to gain
|
||||||
|
reputation or greater distribution for their Work in part through the use and
|
||||||
|
efforts of others.
|
||||||
|
|
||||||
|
For these and/or other purposes and motivations, and without any expectation
|
||||||
|
of additional consideration or compensation, the person associating CC0 with a
|
||||||
|
Work (the "Affirmer"), to the extent that he or she is an owner of Copyright
|
||||||
|
and Related Rights in the Work, voluntarily elects to apply CC0 to the Work
|
||||||
|
and publicly distribute the Work under its terms, with knowledge of his or her
|
||||||
|
Copyright and Related Rights in the Work and the meaning and intended legal
|
||||||
|
effect of CC0 on those rights.
|
||||||
|
|
||||||
|
1. Copyright and Related Rights. A Work made available under CC0 may be
|
||||||
|
protected by copyright and related or neighboring rights ("Copyright and
|
||||||
|
Related Rights"). Copyright and Related Rights include, but are not limited
|
||||||
|
to, the following:
|
||||||
|
|
||||||
|
i. the right to reproduce, adapt, distribute, perform, display, communicate,
|
||||||
|
and translate a Work;
|
||||||
|
|
||||||
|
ii. moral rights retained by the original author(s) and/or performer(s);
|
||||||
|
|
||||||
|
iii. publicity and privacy rights pertaining to a person's image or likeness
|
||||||
|
depicted in a Work;
|
||||||
|
|
||||||
|
iv. rights protecting against unfair competition in regards to a Work,
|
||||||
|
subject to the limitations in paragraph 4(a), below;
|
||||||
|
|
||||||
|
v. rights protecting the extraction, dissemination, use and reuse of data in
|
||||||
|
a Work;
|
||||||
|
|
||||||
|
vi. database rights (such as those arising under Directive 96/9/EC of the
|
||||||
|
European Parliament and of the Council of 11 March 1996 on the legal
|
||||||
|
protection of databases, and under any national implementation thereof,
|
||||||
|
including any amended or successor version of such directive); and
|
||||||
|
|
||||||
|
vii. other similar, equivalent or corresponding rights throughout the world
|
||||||
|
based on applicable law or treaty, and any national implementations thereof.
|
||||||
|
|
||||||
|
2. Waiver. To the greatest extent permitted by, but not in contravention of,
|
||||||
|
applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and
|
||||||
|
unconditionally waives, abandons, and surrenders all of Affirmer's Copyright
|
||||||
|
and Related Rights and associated claims and causes of action, whether now
|
||||||
|
known or unknown (including existing as well as future claims and causes of
|
||||||
|
action), in the Work (i) in all territories worldwide, (ii) for the maximum
|
||||||
|
duration provided by applicable law or treaty (including future time
|
||||||
|
extensions), (iii) in any current or future medium and for any number of
|
||||||
|
copies, and (iv) for any purpose whatsoever, including without limitation
|
||||||
|
commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes
|
||||||
|
the Waiver for the benefit of each member of the public at large and to the
|
||||||
|
detriment of Affirmer's heirs and successors, fully intending that such Waiver
|
||||||
|
shall not be subject to revocation, rescission, cancellation, termination, or
|
||||||
|
any other legal or equitable action to disrupt the quiet enjoyment of the Work
|
||||||
|
by the public as contemplated by Affirmer's express Statement of Purpose.
|
||||||
|
|
||||||
|
3. Public License Fallback. Should any part of the Waiver for any reason be
|
||||||
|
judged legally invalid or ineffective under applicable law, then the Waiver
|
||||||
|
shall be preserved to the maximum extent permitted taking into account
|
||||||
|
Affirmer's express Statement of Purpose. In addition, to the extent the Waiver
|
||||||
|
is so judged Affirmer hereby grants to each affected person a royalty-free,
|
||||||
|
non transferable, non sublicensable, non exclusive, irrevocable and
|
||||||
|
unconditional license to exercise Affirmer's Copyright and Related Rights in
|
||||||
|
the Work (i) in all territories worldwide, (ii) for the maximum duration
|
||||||
|
provided by applicable law or treaty (including future time extensions), (iii)
|
||||||
|
in any current or future medium and for any number of copies, and (iv) for any
|
||||||
|
purpose whatsoever, including without limitation commercial, advertising or
|
||||||
|
promotional purposes (the "License"). The License shall be deemed effective as
|
||||||
|
of the date CC0 was applied by Affirmer to the Work. Should any part of the
|
||||||
|
License for any reason be judged legally invalid or ineffective under
|
||||||
|
applicable law, such partial invalidity or ineffectiveness shall not
|
||||||
|
invalidate the remainder of the License, and in such case Affirmer hereby
|
||||||
|
affirms that he or she will not (i) exercise any of his or her remaining
|
||||||
|
Copyright and Related Rights in the Work or (ii) assert any associated claims
|
||||||
|
and causes of action with respect to the Work, in either case contrary to
|
||||||
|
Affirmer's express Statement of Purpose.
|
||||||
|
|
||||||
|
4. Limitations and Disclaimers.
|
||||||
|
|
||||||
|
a. No trademark or patent rights held by Affirmer are waived, abandoned,
|
||||||
|
surrendered, licensed or otherwise affected by this document.
|
||||||
|
|
||||||
|
b. Affirmer offers the Work as-is and makes no representations or warranties
|
||||||
|
of any kind concerning the Work, express, implied, statutory or otherwise,
|
||||||
|
including without limitation warranties of title, merchantability, fitness
|
||||||
|
for a particular purpose, non infringement, or the absence of latent or
|
||||||
|
other defects, accuracy, or the present or absence of errors, whether or not
|
||||||
|
discoverable, all to the greatest extent permissible under applicable law.
|
||||||
|
|
||||||
|
c. Affirmer disclaims responsibility for clearing rights of other persons
|
||||||
|
that may apply to the Work or any use thereof, including without limitation
|
||||||
|
any person's Copyright and Related Rights in the Work. Further, Affirmer
|
||||||
|
disclaims responsibility for obtaining any necessary consents, permissions
|
||||||
|
or other rights required for any use of the Work.
|
||||||
|
|
||||||
|
d. Affirmer understands and acknowledges that Creative Commons is not a
|
||||||
|
party to this document and has no duty or obligation with respect to this
|
||||||
|
CC0 or use of the Work.
|
||||||
|
|
||||||
|
For more information, please see
|
||||||
|
<http://creativecommons.org/publicdomain/zero/1.0/>
|
11
hw/application_fpga/fw/tk1/blake2s/README.md
Normal file
11
hw/application_fpga/fw/tk1/blake2s/README.md
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# blake2s
|
||||||
|
|
||||||
|
A Blake2s implementation taken from Joachim Strömbergson's
|
||||||
|
|
||||||
|
https://github.com/secworks/blake2s
|
||||||
|
|
||||||
|
Specifically from
|
||||||
|
|
||||||
|
https://github.com/secworks/blake2s/tree/master/src/model
|
||||||
|
|
||||||
|
Minor local changes for build purposes.
|
351
hw/application_fpga/fw/tk1/blake2s/blake2s.c
Normal file
351
hw/application_fpga/fw/tk1/blake2s/blake2s.c
Normal file
@ -0,0 +1,351 @@
|
|||||||
|
//======================================================================
|
||||||
|
//
|
||||||
|
// blake2s.c
|
||||||
|
// ---------
|
||||||
|
//
|
||||||
|
// A simple blake2s Reference Implementation.
|
||||||
|
//======================================================================
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "blake2s.h"
|
||||||
|
|
||||||
|
// Dummy printf() for verbose mode
|
||||||
|
static void printf(const char *format, ...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
#define VERBOSE 0
|
||||||
|
#define SHOW_V 0
|
||||||
|
#define SHOW_M_WORDS 0
|
||||||
|
|
||||||
|
|
||||||
|
// Cyclic right rotation.
|
||||||
|
#ifndef ROTR32
|
||||||
|
#define ROTR32(x, y) (((x) >> (y)) ^ ((x) << (32 - (y))))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
// Little-endian byte access.
|
||||||
|
#define B2S_GET32(p) \
|
||||||
|
(((uint32_t) ((uint8_t *) (p))[0]) ^ \
|
||||||
|
(((uint32_t) ((uint8_t *) (p))[1]) << 8) ^ \
|
||||||
|
(((uint32_t) ((uint8_t *) (p))[2]) << 16) ^ \
|
||||||
|
(((uint32_t) ((uint8_t *) (p))[3]) << 24))
|
||||||
|
|
||||||
|
|
||||||
|
// Initialization Vector.
|
||||||
|
static const uint32_t blake2s_iv[8] = {
|
||||||
|
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
|
||||||
|
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
void print_v(uint32_t *v) {
|
||||||
|
printf("0x%08x, 0x%08x, 0x%08x, 0x%08x\n", v[0], v[1], v[2], v[3]);
|
||||||
|
printf("0x%08x, 0x%08x, 0x%08x, 0x%08x\n", v[4], v[5], v[6], v[7]);
|
||||||
|
printf("0x%08x, 0x%08x, 0x%08x, 0x%08x\n", v[8], v[9], v[10], v[11]);
|
||||||
|
printf("0x%08x, 0x%08x, 0x%08x, 0x%08x\n", v[12], v[13], v[14], v[15]);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
// print_ctx()
|
||||||
|
// Print the contents of the context data structure.
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
void print_ctx(blake2s_ctx *ctx) {
|
||||||
|
printf("Chained state (h):\n");
|
||||||
|
printf("0x%08x, 0x%08x, 0x%08x, 0x%08x, ",
|
||||||
|
ctx->h[0], ctx->h[1], ctx->h[2], ctx->h[3]);
|
||||||
|
printf("0x%08x, 0x%08x, 0x%08x, 0x%08x",
|
||||||
|
ctx->h[4], ctx->h[5], ctx->h[6], ctx->h[7]);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("Byte counter (t):\n");
|
||||||
|
printf("0x%08x, 0x%08x", ctx->t[0], ctx->t[1]);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
// B2S_G macro redefined as a G function.
|
||||||
|
// Allows us to output intermediate values for debugging.
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
void G(uint32_t *v, uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t y) {
|
||||||
|
if (VERBOSE) {
|
||||||
|
printf("G started.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SHOW_V) {
|
||||||
|
printf("v before processing:\n");
|
||||||
|
print_v(&v[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SHOW_M_WORDS) {
|
||||||
|
printf("x: 0x%08x, y: 0x%08x\n", x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
v[a] = v[a] + v[b] + x;
|
||||||
|
v[d] = ROTR32(v[d] ^ v[a], 16);
|
||||||
|
v[c] = v[c] + v[d];
|
||||||
|
v[b] = ROTR32(v[b] ^ v[c], 12);
|
||||||
|
v[a] = v[a] + v[b] + y;
|
||||||
|
v[d] = ROTR32(v[d] ^ v[a], 8);
|
||||||
|
v[c] = v[c] + v[d];
|
||||||
|
v[b] = ROTR32(v[b] ^ v[c], 7);
|
||||||
|
|
||||||
|
if (SHOW_V) {
|
||||||
|
printf("v after processing:\n");
|
||||||
|
print_v(&v[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VERBOSE) {
|
||||||
|
printf("G completed.\n\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
// Compression function. "last" flag indicates last block.
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
static void blake2s_compress(blake2s_ctx *ctx, int last)
|
||||||
|
{
|
||||||
|
const uint8_t sigma[10][16] = {
|
||||||
|
{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
|
||||||
|
{14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3},
|
||||||
|
{11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4},
|
||||||
|
{7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8},
|
||||||
|
{9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13},
|
||||||
|
{2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9},
|
||||||
|
{12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11},
|
||||||
|
{13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10},
|
||||||
|
{6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5},
|
||||||
|
{10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
int i;
|
||||||
|
uint32_t v[16], m[16];
|
||||||
|
|
||||||
|
if (VERBOSE) {
|
||||||
|
printf("blake2s_compress started.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// init work variables
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
v[i] = ctx->h[i];
|
||||||
|
v[i + 8] = blake2s_iv[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// low 32 bits of offset
|
||||||
|
// high 32 bits
|
||||||
|
if (VERBOSE) {
|
||||||
|
printf("t[0]: 0x%08x, t[1]: 0x%08x\n", ctx->t[0], ctx->t[1]);
|
||||||
|
}
|
||||||
|
v[12] ^= ctx->t[0];
|
||||||
|
v[13] ^= ctx->t[1];
|
||||||
|
|
||||||
|
// last block flag set ?
|
||||||
|
if (last) {
|
||||||
|
v[14] = ~v[14];
|
||||||
|
}
|
||||||
|
|
||||||
|
// get little-endian words
|
||||||
|
for (i = 0; i < 16; i++) {
|
||||||
|
m[i] = B2S_GET32(&ctx->b[4 * i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VERBOSE) {
|
||||||
|
printf("v before G processing:\n");
|
||||||
|
print_v(&v[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ten rounds of the G function applied on rows, diagonal.
|
||||||
|
for (i = 0; i < 10; i++) {
|
||||||
|
if (VERBOSE) {
|
||||||
|
printf("Round %02d:\n", (i + 1));
|
||||||
|
printf("Row processing started.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
G(&v[0], 0, 4, 8, 12, m[sigma[i][ 0]], m[sigma[i][ 1]]);
|
||||||
|
G(&v[0], 1, 5, 9, 13, m[sigma[i][ 2]], m[sigma[i][ 3]]);
|
||||||
|
G(&v[0], 2, 6, 10, 14, m[sigma[i][ 4]], m[sigma[i][ 5]]);
|
||||||
|
G(&v[0], 3, 7, 11, 15, m[sigma[i][ 6]], m[sigma[i][ 7]]);
|
||||||
|
|
||||||
|
if (VERBOSE) {
|
||||||
|
printf("Row processing completed.\n");
|
||||||
|
printf("Diagonal processing started.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
G(&v[0], 0, 5, 10, 15, m[sigma[i][ 8]], m[sigma[i][ 9]]);
|
||||||
|
G(&v[0], 1, 6, 11, 12, m[sigma[i][10]], m[sigma[i][11]]);
|
||||||
|
G(&v[0], 2, 7, 8, 13, m[sigma[i][12]], m[sigma[i][13]]);
|
||||||
|
G(&v[0], 3, 4, 9, 14, m[sigma[i][14]], m[sigma[i][15]]);
|
||||||
|
|
||||||
|
if (VERBOSE) {
|
||||||
|
printf("Diagonal processing completed.\n");
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VERBOSE) {
|
||||||
|
printf("v after G processing:\n");
|
||||||
|
print_v(&v[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the hash state.
|
||||||
|
for (i = 0; i < 8; ++i) {
|
||||||
|
ctx->h[i] ^= v[i] ^ v[i + 8];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VERBOSE) {
|
||||||
|
printf("blake2s_compress completed.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
// Initialize the hashing context "ctx" with optional key "key".
|
||||||
|
// 1 <= outlen <= 32 gives the digest size in bytes.
|
||||||
|
// Secret key (also <= 32 bytes) is optional (keylen = 0).
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
int blake2s_init(blake2s_ctx *ctx, size_t outlen,
|
||||||
|
const void *key, size_t keylen) // (keylen=0: no key)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (VERBOSE) {
|
||||||
|
printf("blake2s_init started.\n");
|
||||||
|
printf("Context before blake2s_init processing:\n");
|
||||||
|
print_ctx(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (outlen == 0 || outlen > 32 || keylen > 32)
|
||||||
|
return -1; // illegal parameters
|
||||||
|
|
||||||
|
for (i = 0; i < 8; i++) // state, "param block"
|
||||||
|
ctx->h[i] = blake2s_iv[i];
|
||||||
|
ctx->h[0] ^= 0x01010000 ^ (keylen << 8) ^ outlen;
|
||||||
|
|
||||||
|
ctx->t[0] = 0; // input count low word
|
||||||
|
ctx->t[1] = 0; // input count high word
|
||||||
|
ctx->c = 0; // pointer within buffer
|
||||||
|
ctx->outlen = outlen;
|
||||||
|
|
||||||
|
for (i = keylen; i < 64; i++) // zero input block
|
||||||
|
ctx->b[i] = 0;
|
||||||
|
if (keylen > 0) {
|
||||||
|
blake2s_update(ctx, key, keylen);
|
||||||
|
ctx->c = 64; // at the end
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VERBOSE) {
|
||||||
|
printf("Context after blake2s_init processing:\n");
|
||||||
|
print_ctx(ctx);
|
||||||
|
printf("blake2s_init completed.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
// Add "inlen" bytes from "in" into the hash.
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
void blake2s_update(blake2s_ctx *ctx,
|
||||||
|
const void *in, size_t inlen) // data bytes
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (VERBOSE) {
|
||||||
|
printf("blake2s_update started.\n");
|
||||||
|
printf("Context before blake2s_update processing:\n");
|
||||||
|
print_ctx(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < inlen; i++) {
|
||||||
|
if (ctx->c == 64) { // buffer full ?
|
||||||
|
ctx->t[0] += ctx->c; // add counters
|
||||||
|
if (ctx->t[0] < ctx->c) // carry overflow ?
|
||||||
|
ctx->t[1]++; // high word
|
||||||
|
blake2s_compress(ctx, 0); // compress (not last)
|
||||||
|
ctx->c = 0; // counter to zero
|
||||||
|
}
|
||||||
|
ctx->b[ctx->c++] = ((const uint8_t *) in)[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VERBOSE) {
|
||||||
|
printf("Context after blake2s_update processing:\n");
|
||||||
|
print_ctx(ctx);
|
||||||
|
printf("blake2s_update completed.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
// Generate the message digest (size given in init).
|
||||||
|
// Result placed in "out".
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
void blake2s_final(blake2s_ctx *ctx, void *out)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (VERBOSE) {
|
||||||
|
printf("blake2s_final started.\n");
|
||||||
|
printf("Context before blake2s_final processing:\n");
|
||||||
|
print_ctx(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->t[0] += ctx->c; // mark last block offset
|
||||||
|
|
||||||
|
// carry overflow
|
||||||
|
// high word
|
||||||
|
if (ctx->t[0] < ctx->c) {
|
||||||
|
ctx->t[1]++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill up with zeros
|
||||||
|
// final block flag = 1
|
||||||
|
while (ctx->c < 64) {
|
||||||
|
ctx->b[ctx->c++] = 0;
|
||||||
|
}
|
||||||
|
blake2s_compress(ctx, 1);
|
||||||
|
|
||||||
|
// little endian convert and store
|
||||||
|
for (i = 0; i < ctx->outlen; i++) {
|
||||||
|
((uint8_t *) out)[i] =
|
||||||
|
(ctx->h[i >> 2] >> (8 * (i & 3))) & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VERBOSE) {
|
||||||
|
printf("Context after blake2s_final processing:\n");
|
||||||
|
print_ctx(ctx);
|
||||||
|
printf("blake2s_final completed.\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
// Convenience function for all-in-one computation.
|
||||||
|
//------------------------------------------------------------------
|
||||||
|
int blake2s(void *out, size_t outlen,
|
||||||
|
const void *key, size_t keylen,
|
||||||
|
const void *in, size_t inlen,
|
||||||
|
blake2s_ctx *ctx)
|
||||||
|
{
|
||||||
|
if (blake2s_init(ctx, outlen, key, keylen))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
blake2s_update(ctx, in, inlen);
|
||||||
|
|
||||||
|
blake2s_final(ctx, out);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
//======================================================================
|
||||||
|
//======================================================================
|
40
hw/application_fpga/fw/tk1/blake2s/blake2s.h
Normal file
40
hw/application_fpga/fw/tk1/blake2s/blake2s.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// blake2s.h
|
||||||
|
// BLAKE2s Hashing Context and API Prototypes
|
||||||
|
|
||||||
|
#ifndef BLAKE2S_H
|
||||||
|
#define BLAKE2S_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
// state context
|
||||||
|
typedef struct {
|
||||||
|
uint8_t b[64]; // input buffer
|
||||||
|
uint32_t h[8]; // chained state
|
||||||
|
uint32_t t[2]; // total number of bytes
|
||||||
|
size_t c; // pointer for b[]
|
||||||
|
size_t outlen; // digest size
|
||||||
|
} blake2s_ctx;
|
||||||
|
|
||||||
|
// Initialize the hashing context "ctx" with optional key "key".
|
||||||
|
// 1 <= outlen <= 32 gives the digest size in bytes.
|
||||||
|
// Secret key (also <= 32 bytes) is optional (keylen = 0).
|
||||||
|
int blake2s_init(blake2s_ctx *ctx, size_t outlen,
|
||||||
|
const void *key, size_t keylen); // secret key
|
||||||
|
|
||||||
|
// Add "inlen" bytes from "in" into the hash.
|
||||||
|
void blake2s_update(blake2s_ctx *ctx, // context
|
||||||
|
const void *in, size_t inlen); // data to be hashed
|
||||||
|
|
||||||
|
// Generate the message digest (size given in init).
|
||||||
|
// Result placed in "out".
|
||||||
|
void blake2s_final(blake2s_ctx *ctx, void *out);
|
||||||
|
|
||||||
|
// All-in-one convenience function.
|
||||||
|
int blake2s(void *out, size_t outlen, // return buffer for digest
|
||||||
|
const void *key, size_t keylen, // optional secret key
|
||||||
|
const void *in, size_t inlen, // data to be hashed
|
||||||
|
blake2s_ctx *ctx);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -7,7 +7,7 @@ OUTPUT_ARCH("riscv")
|
|||||||
ENTRY(_start)
|
ENTRY(_start)
|
||||||
|
|
||||||
/* Define stack size */
|
/* Define stack size */
|
||||||
STACK_SIZE = 3000;
|
STACK_SIZE = 0xEF0; /* 3824 B */
|
||||||
|
|
||||||
MEMORY
|
MEMORY
|
||||||
{
|
{
|
||||||
|
@ -1,244 +0,0 @@
|
|||||||
// Copyright (C) 2024 - Tillitis AB
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <tkey/assert.h>
|
|
||||||
#include <tkey/tk1_mem.h>
|
|
||||||
|
|
||||||
#include "flash.h"
|
|
||||||
#include "spi.h"
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
static volatile uint32_t *timer = (volatile uint32_t *)TK1_MMIO_TIMER_TIMER;
|
|
||||||
static volatile uint32_t *timer_prescaler = (volatile uint32_t *)TK1_MMIO_TIMER_PRESCALER;
|
|
||||||
static volatile uint32_t *timer_status = (volatile uint32_t *)TK1_MMIO_TIMER_STATUS;
|
|
||||||
static volatile uint32_t *timer_ctrl = (volatile uint32_t *)TK1_MMIO_TIMER_CTRL;
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
// CPU clock frequency in Hz
|
|
||||||
#define CPUFREQ 21000000
|
|
||||||
#define PAGE_SIZE 256
|
|
||||||
|
|
||||||
static bool flash_is_busy(void);
|
|
||||||
static void flash_wait_busy(void);
|
|
||||||
static void flash_write_enable(void);
|
|
||||||
|
|
||||||
static void delay(int timeout_ms)
|
|
||||||
{
|
|
||||||
// Tick once every centisecond
|
|
||||||
*timer_prescaler = CPUFREQ / 100;
|
|
||||||
*timer = timeout_ms / 10;
|
|
||||||
|
|
||||||
*timer_ctrl |= (1 << TK1_MMIO_TIMER_CTRL_START_BIT);
|
|
||||||
|
|
||||||
while (*timer_status != 0) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stop timer
|
|
||||||
*timer_ctrl |= (1 << TK1_MMIO_TIMER_CTRL_STOP_BIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool flash_is_busy(void)
|
|
||||||
{
|
|
||||||
uint8_t tx_buf = READ_STATUS_REG_1;
|
|
||||||
uint8_t rx_buf = {0x00};
|
|
||||||
|
|
||||||
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, &rx_buf,
|
|
||||||
sizeof(rx_buf)) == 0);
|
|
||||||
|
|
||||||
if (rx_buf & (1 << STATUS_REG_BUSY_BIT)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blocking until !busy
|
|
||||||
static void flash_wait_busy(void)
|
|
||||||
{
|
|
||||||
while (flash_is_busy()) {
|
|
||||||
delay(10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void flash_write_enable(void)
|
|
||||||
{
|
|
||||||
uint8_t tx_buf = WRITE_ENABLE;
|
|
||||||
|
|
||||||
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flash_write_disable(void)
|
|
||||||
{
|
|
||||||
uint8_t tx_buf = WRITE_DISABLE;
|
|
||||||
|
|
||||||
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flash_sector_erase(uint32_t address)
|
|
||||||
{
|
|
||||||
uint8_t tx_buf[4] = {0x00};
|
|
||||||
tx_buf[0] = SECTOR_ERASE;
|
|
||||||
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
|
|
||||||
tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF;
|
|
||||||
/* tx_buf[3] is within a sector, and hence does not make a difference */
|
|
||||||
|
|
||||||
flash_write_enable();
|
|
||||||
assert(spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
|
|
||||||
flash_wait_busy();
|
|
||||||
}
|
|
||||||
|
|
||||||
void flash_block_32_erase(uint32_t address)
|
|
||||||
{
|
|
||||||
uint8_t tx_buf[4] = {0x00};
|
|
||||||
tx_buf[0] = BLOCK_ERASE_32K;
|
|
||||||
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
|
|
||||||
tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF;
|
|
||||||
tx_buf[3] = (address >> ADDR_BYTE_1_BIT) & 0xFF;
|
|
||||||
|
|
||||||
flash_write_enable();
|
|
||||||
assert(spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
|
|
||||||
flash_wait_busy();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 64 KiB block erase, only cares about address bits 16 and above.
|
|
||||||
void flash_block_64_erase(uint32_t address)
|
|
||||||
{
|
|
||||||
uint8_t tx_buf[4] = {0x00};
|
|
||||||
tx_buf[0] = BLOCK_ERASE_64K;
|
|
||||||
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
|
|
||||||
/* tx_buf[2] and tx_buf[3] is within a block, and hence does not make a
|
|
||||||
* difference */
|
|
||||||
|
|
||||||
flash_write_enable();
|
|
||||||
assert(spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
|
|
||||||
flash_wait_busy();
|
|
||||||
}
|
|
||||||
|
|
||||||
void flash_release_powerdown(void)
|
|
||||||
{
|
|
||||||
uint8_t tx_buf[4] = {0x00};
|
|
||||||
tx_buf[0] = RELEASE_POWER_DOWN;
|
|
||||||
|
|
||||||
assert(spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flash_powerdown(void)
|
|
||||||
{
|
|
||||||
uint8_t tx_buf = POWER_DOWN;
|
|
||||||
|
|
||||||
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, NULL, 0) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flash_read_manufacturer_device_id(uint8_t *device_id)
|
|
||||||
{
|
|
||||||
assert(device_id != NULL);
|
|
||||||
|
|
||||||
uint8_t tx_buf[4] = {0x00};
|
|
||||||
tx_buf[0] = READ_MANUFACTURER_ID;
|
|
||||||
|
|
||||||
assert(spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, device_id, 2) ==
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flash_read_jedec_id(uint8_t *jedec_id)
|
|
||||||
{
|
|
||||||
assert(jedec_id != NULL);
|
|
||||||
|
|
||||||
uint8_t tx_buf = READ_JEDEC_ID;
|
|
||||||
|
|
||||||
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, jedec_id, 3) ==
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flash_read_unique_id(uint8_t *unique_id)
|
|
||||||
{
|
|
||||||
assert(unique_id != NULL);
|
|
||||||
|
|
||||||
uint8_t tx_buf[5] = {0x00};
|
|
||||||
tx_buf[0] = READ_UNIQUE_ID;
|
|
||||||
|
|
||||||
assert(spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, unique_id, 8) ==
|
|
||||||
0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void flash_read_status(uint8_t *status_reg)
|
|
||||||
{
|
|
||||||
assert(status_reg != NULL);
|
|
||||||
|
|
||||||
uint8_t tx_buf = READ_STATUS_REG_1;
|
|
||||||
|
|
||||||
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, status_reg, 1) ==
|
|
||||||
0);
|
|
||||||
|
|
||||||
tx_buf = READ_STATUS_REG_2;
|
|
||||||
assert(spi_transfer(&tx_buf, sizeof(tx_buf), NULL, 0, status_reg + 1,
|
|
||||||
1) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
int flash_read_data(uint32_t address, uint8_t *dest_buf, size_t size)
|
|
||||||
{
|
|
||||||
if (dest_buf == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t tx_buf[4] = {0x00};
|
|
||||||
tx_buf[0] = READ_DATA;
|
|
||||||
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
|
|
||||||
tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF;
|
|
||||||
tx_buf[3] = (address >> ADDR_BYTE_1_BIT) & 0xFF;
|
|
||||||
|
|
||||||
return spi_transfer(tx_buf, sizeof(tx_buf), NULL, 0, dest_buf, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only handles writes where the least significant byte of the start address is
|
|
||||||
// zero.
|
|
||||||
int flash_write_data(uint32_t address, uint8_t *data, size_t size)
|
|
||||||
{
|
|
||||||
if (data == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (size <= 0 || size > 4096) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t left = size;
|
|
||||||
uint8_t *p_data = data;
|
|
||||||
size_t n_bytes = 0;
|
|
||||||
|
|
||||||
uint8_t tx_buf[4] = {
|
|
||||||
PAGE_PROGRAM, /* tx_buf[0] */
|
|
||||||
(address >> ADDR_BYTE_3_BIT) & 0xFF, /* tx_buf[1] */
|
|
||||||
(address >> ADDR_BYTE_2_BIT) & 0xFF, /* tx_buf[2] */
|
|
||||||
0x00, /* tx_buf[3] */
|
|
||||||
};
|
|
||||||
|
|
||||||
while (left > 0) {
|
|
||||||
if (left >= PAGE_SIZE) {
|
|
||||||
n_bytes = PAGE_SIZE;
|
|
||||||
} else {
|
|
||||||
n_bytes = left;
|
|
||||||
}
|
|
||||||
|
|
||||||
flash_write_enable();
|
|
||||||
|
|
||||||
if (spi_transfer(tx_buf, sizeof(tx_buf), p_data, n_bytes, NULL,
|
|
||||||
0) != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
left -= n_bytes;
|
|
||||||
p_data += n_bytes;
|
|
||||||
|
|
||||||
address += n_bytes;
|
|
||||||
tx_buf[1] = (address >> ADDR_BYTE_3_BIT) & 0xFF;
|
|
||||||
tx_buf[2] = (address >> ADDR_BYTE_2_BIT) & 0xFF;
|
|
||||||
|
|
||||||
flash_wait_busy();
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
// Copyright (C) 2024 - Tillitis AB
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
|
|
||||||
#ifndef TKEY_FLASH_H
|
|
||||||
#define TKEY_FLASH_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define WRITE_ENABLE 0x06
|
|
||||||
#define WRITE_DISABLE 0x04
|
|
||||||
|
|
||||||
#define READ_STATUS_REG_1 0x05
|
|
||||||
#define READ_STATUS_REG_2 0x35
|
|
||||||
#define WRITE_STATUS_REG 0x01
|
|
||||||
|
|
||||||
#define PAGE_PROGRAM 0x02
|
|
||||||
#define SECTOR_ERASE 0x20
|
|
||||||
#define BLOCK_ERASE_32K 0x52
|
|
||||||
#define BLOCK_ERASE_64K 0xD8
|
|
||||||
#define CHIP_ERASE 0xC7
|
|
||||||
|
|
||||||
#define POWER_DOWN 0xB9
|
|
||||||
#define READ_DATA 0x03
|
|
||||||
#define RELEASE_POWER_DOWN 0xAB
|
|
||||||
|
|
||||||
#define READ_MANUFACTURER_ID 0x90
|
|
||||||
#define READ_JEDEC_ID 0x9F
|
|
||||||
#define READ_UNIQUE_ID 0x4B
|
|
||||||
|
|
||||||
#define ENABLE_RESET 0x66
|
|
||||||
#define RESET 0x99
|
|
||||||
|
|
||||||
#define ADDR_BYTE_3_BIT 16
|
|
||||||
#define ADDR_BYTE_2_BIT 8
|
|
||||||
#define ADDR_BYTE_1_BIT 0
|
|
||||||
|
|
||||||
#define STATUS_REG_BUSY_BIT 0
|
|
||||||
#define STATUS_REG_WEL_BIT 1
|
|
||||||
|
|
||||||
void flash_write_disable(void);
|
|
||||||
void flash_sector_erase(uint32_t address);
|
|
||||||
void flash_block_32_erase(uint32_t address);
|
|
||||||
void flash_block_64_erase(uint32_t address);
|
|
||||||
void flash_release_powerdown(void);
|
|
||||||
void flash_powerdown(void);
|
|
||||||
void flash_read_manufacturer_device_id(uint8_t *device_id);
|
|
||||||
void flash_read_jedec_id(uint8_t *jedec_id);
|
|
||||||
void flash_read_unique_id(uint8_t *unique_id);
|
|
||||||
void flash_read_status(uint8_t *status_reg);
|
|
||||||
int flash_read_data(uint32_t address, uint8_t *dest_buf, size_t size);
|
|
||||||
int flash_write_data(uint32_t address, uint8_t *data, size_t size);
|
|
||||||
|
|
||||||
#endif
|
|
@ -3,21 +3,16 @@
|
|||||||
* SPDX-License-Identifier: GPL-2.0-only
|
* SPDX-License-Identifier: GPL-2.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <blake2s/blake2s.h>
|
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <tkey/assert.h>
|
#include <tkey/assert.h>
|
||||||
#include <tkey/debug.h>
|
#include <tkey/debug.h>
|
||||||
#include <tkey/led.h>
|
|
||||||
#include <tkey/lib.h>
|
#include <tkey/lib.h>
|
||||||
#include <tkey/tk1_mem.h>
|
#include <tkey/tk1_mem.h>
|
||||||
|
|
||||||
#include "mgmt_app.h"
|
#include "blake2s/blake2s.h"
|
||||||
#include "partition_table.h"
|
|
||||||
#include "preload_app.h"
|
|
||||||
#include "proto.h"
|
#include "proto.h"
|
||||||
#include "resetinfo.h"
|
|
||||||
#include "state.h"
|
#include "state.h"
|
||||||
#include "syscall_enable.h"
|
#include "syscall_enable.h"
|
||||||
|
|
||||||
@ -38,11 +33,8 @@ static volatile uint32_t *timer_status = (volatile uint32_t *)TK1_MMIO_TIMER
|
|||||||
static volatile uint32_t *timer_ctrl = (volatile uint32_t *)TK1_MMIO_TIMER_CTRL;
|
static volatile uint32_t *timer_ctrl = (volatile uint32_t *)TK1_MMIO_TIMER_CTRL;
|
||||||
static volatile uint32_t *ram_addr_rand = (volatile uint32_t *)TK1_MMIO_TK1_RAM_ADDR_RAND;
|
static volatile uint32_t *ram_addr_rand = (volatile uint32_t *)TK1_MMIO_TK1_RAM_ADDR_RAND;
|
||||||
static volatile uint32_t *ram_data_rand = (volatile uint32_t *)TK1_MMIO_TK1_RAM_DATA_RAND;
|
static volatile uint32_t *ram_data_rand = (volatile uint32_t *)TK1_MMIO_TK1_RAM_DATA_RAND;
|
||||||
static volatile struct reset *resetinfo = (volatile struct reset *)TK1_MMIO_RESETINFO_BASE;
|
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
struct partition_table_storage part_table_storage;
|
|
||||||
|
|
||||||
// Context for the loading of a TKey program
|
// Context for the loading of a TKey program
|
||||||
struct context {
|
struct context {
|
||||||
uint32_t left; // Bytes left to receive
|
uint32_t left; // Bytes left to receive
|
||||||
@ -50,9 +42,6 @@ struct context {
|
|||||||
uint8_t *loadaddr; // Where we are currently loading a TKey program
|
uint8_t *loadaddr; // Where we are currently loading a TKey program
|
||||||
bool use_uss; // Use USS?
|
bool use_uss; // Use USS?
|
||||||
uint8_t uss[32]; // User Supplied Secret, if any
|
uint8_t uss[32]; // User Supplied Secret, if any
|
||||||
uint8_t flash_slot; // App is loaded from flash slot number
|
|
||||||
/*@null@*/ volatile uint8_t
|
|
||||||
*ver_digest; // Verify loaded app against this digest
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void print_hw_version(void);
|
static void print_hw_version(void);
|
||||||
@ -67,14 +56,11 @@ static enum state initial_commands(const struct frame_header *hdr,
|
|||||||
static enum state loading_commands(const struct frame_header *hdr,
|
static enum state loading_commands(const struct frame_header *hdr,
|
||||||
const uint8_t *cmd, enum state state,
|
const uint8_t *cmd, enum state state,
|
||||||
struct context *ctx);
|
struct context *ctx);
|
||||||
|
static void run(const struct context *ctx);
|
||||||
#if !defined(SIMULATION)
|
#if !defined(SIMULATION)
|
||||||
static uint32_t xorwow(uint32_t state, uint32_t acc);
|
static uint32_t xorwow(uint32_t state, uint32_t acc);
|
||||||
#endif
|
#endif
|
||||||
static void scramble_ram(void);
|
static void scramble_ram(void);
|
||||||
static int compute_app_digest(uint8_t *digest);
|
|
||||||
static int load_flash_app(struct partition_table *part_table,
|
|
||||||
uint8_t digest[32], uint8_t slot);
|
|
||||||
static enum state start_where(struct context *ctx);
|
|
||||||
|
|
||||||
static void print_hw_version(void)
|
static void print_hw_version(void)
|
||||||
{
|
{
|
||||||
@ -94,7 +80,6 @@ static void print_digest(uint8_t *md)
|
|||||||
for (int j = 0; j < 4; j++) {
|
for (int j = 0; j < 4; j++) {
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
debug_puthex(md[i + 8 * j]);
|
debug_puthex(md[i + 8 * j]);
|
||||||
(void)md;
|
|
||||||
}
|
}
|
||||||
debug_lf();
|
debug_lf();
|
||||||
}
|
}
|
||||||
@ -159,7 +144,6 @@ static void compute_cdi(const uint8_t *digest, const uint8_t use_uss,
|
|||||||
static void copy_name(uint8_t *buf, const size_t bufsiz, const uint32_t word)
|
static void copy_name(uint8_t *buf, const size_t bufsiz, const uint32_t word)
|
||||||
{
|
{
|
||||||
assert(bufsiz >= 4);
|
assert(bufsiz >= 4);
|
||||||
assert(buf != NULL);
|
|
||||||
|
|
||||||
buf[0] = word >> 24;
|
buf[0] = word >> 24;
|
||||||
buf[1] = word >> 16;
|
buf[1] = word >> 16;
|
||||||
@ -295,6 +279,7 @@ static enum state loading_commands(const struct frame_header *hdr,
|
|||||||
ctx->left -= nbytes;
|
ctx->left -= nbytes;
|
||||||
|
|
||||||
if (ctx->left == 0) {
|
if (ctx->left == 0) {
|
||||||
|
blake2s_ctx b2s_ctx = {0};
|
||||||
int blake2err = 0;
|
int blake2err = 0;
|
||||||
|
|
||||||
debug_puts("Fully loaded ");
|
debug_puts("Fully loaded ");
|
||||||
@ -303,7 +288,9 @@ static enum state loading_commands(const struct frame_header *hdr,
|
|||||||
|
|
||||||
// Compute Blake2S digest of the app,
|
// Compute Blake2S digest of the app,
|
||||||
// storing it for FW_STATE_RUN
|
// storing it for FW_STATE_RUN
|
||||||
blake2err = compute_app_digest(ctx->digest);
|
blake2err = blake2s(&ctx->digest, 32, NULL, 0,
|
||||||
|
(const void *)TK1_RAM_BASE,
|
||||||
|
*app_size, &b2s_ctx);
|
||||||
assert(blake2err == 0);
|
assert(blake2err == 0);
|
||||||
print_digest(ctx->digest);
|
print_digest(ctx->digest);
|
||||||
|
|
||||||
@ -313,7 +300,7 @@ static enum state loading_commands(const struct frame_header *hdr,
|
|||||||
memcpy_s(&rsp[1], CMDSIZE - 1, &ctx->digest, 32);
|
memcpy_s(&rsp[1], CMDSIZE - 1, &ctx->digest, 32);
|
||||||
fwreply(*hdr, FW_RSP_LOAD_APP_DATA_READY, rsp);
|
fwreply(*hdr, FW_RSP_LOAD_APP_DATA_READY, rsp);
|
||||||
|
|
||||||
state = FW_STATE_START;
|
state = FW_STATE_RUN;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,11 +320,13 @@ static enum state loading_commands(const struct frame_header *hdr,
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void jump_to_app(void)
|
static void run(const struct context *ctx)
|
||||||
{
|
{
|
||||||
/* Start of app is always at the beginning of RAM */
|
|
||||||
*app_addr = TK1_RAM_BASE;
|
*app_addr = TK1_RAM_BASE;
|
||||||
|
|
||||||
|
// CDI = hash(uds, hash(app), uss)
|
||||||
|
compute_cdi(ctx->digest, ctx->use_uss, ctx->uss);
|
||||||
|
|
||||||
debug_puts("Flipping to app mode!\n");
|
debug_puts("Flipping to app mode!\n");
|
||||||
debug_puts("Jumping to ");
|
debug_puts("Jumping to ");
|
||||||
debug_putinthex(*app_addr);
|
debug_putinthex(*app_addr);
|
||||||
@ -376,29 +365,6 @@ static void jump_to_app(void)
|
|||||||
__builtin_unreachable();
|
__builtin_unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
static int load_flash_app(struct partition_table *part_table,
|
|
||||||
uint8_t digest[32], uint8_t slot)
|
|
||||||
{
|
|
||||||
if (slot >= N_PRELOADED_APP) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preload_load(part_table, slot) == -1) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
*app_size = part_table->pre_app_data[slot].size;
|
|
||||||
if (*app_size > TK1_APP_MAX_SIZE) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int digest_err = compute_app_digest(digest);
|
|
||||||
assert(digest_err == 0);
|
|
||||||
print_digest(digest);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !defined(SIMULATION)
|
#if !defined(SIMULATION)
|
||||||
static uint32_t xorwow(uint32_t state, uint32_t acc)
|
static uint32_t xorwow(uint32_t state, uint32_t acc)
|
||||||
{
|
{
|
||||||
@ -433,62 +399,6 @@ static void scramble_ram(void)
|
|||||||
*ram_data_rand = rnd_word();
|
*ram_data_rand = rnd_word();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Computes the blake2s digest of the app loaded into RAM */
|
|
||||||
static int compute_app_digest(uint8_t *digest)
|
|
||||||
{
|
|
||||||
return blake2s(digest, 32, NULL, 0, (const void *)TK1_RAM_BASE,
|
|
||||||
*app_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static enum state start_where(struct context *ctx)
|
|
||||||
{
|
|
||||||
assert(ctx != NULL);
|
|
||||||
|
|
||||||
// Where do we start? Read resetinfo 'startfrom'
|
|
||||||
switch (resetinfo->type) {
|
|
||||||
case START_DEFAULT:
|
|
||||||
// fallthrough
|
|
||||||
case START_FLASH0:
|
|
||||||
ctx->flash_slot = 0;
|
|
||||||
ctx->ver_digest = mgmt_app_allowed_digest();
|
|
||||||
|
|
||||||
return FW_STATE_LOAD_FLASH_MGMT;
|
|
||||||
|
|
||||||
case START_FLASH1:
|
|
||||||
ctx->flash_slot = 1;
|
|
||||||
ctx->ver_digest = NULL;
|
|
||||||
|
|
||||||
return FW_STATE_LOAD_FLASH;
|
|
||||||
|
|
||||||
case START_FLASH0_VER:
|
|
||||||
ctx->flash_slot = 0;
|
|
||||||
ctx->ver_digest = resetinfo->app_digest;
|
|
||||||
|
|
||||||
return FW_STATE_LOAD_FLASH;
|
|
||||||
|
|
||||||
case START_FLASH1_VER:
|
|
||||||
ctx->flash_slot = 1;
|
|
||||||
ctx->ver_digest = resetinfo->app_digest;
|
|
||||||
|
|
||||||
return FW_STATE_LOAD_FLASH;
|
|
||||||
|
|
||||||
case START_CLIENT:
|
|
||||||
ctx->ver_digest = NULL;
|
|
||||||
|
|
||||||
return FW_STATE_WAITCOMMAND;
|
|
||||||
|
|
||||||
case START_CLIENT_VER:
|
|
||||||
ctx->ver_digest = resetinfo->app_digest;
|
|
||||||
|
|
||||||
return FW_STATE_WAITCOMMAND;
|
|
||||||
|
|
||||||
default:
|
|
||||||
debug_puts("Unknown startfrom\n");
|
|
||||||
|
|
||||||
return FW_STATE_FAIL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
struct context ctx = {0};
|
struct context ctx = {0};
|
||||||
@ -496,8 +406,6 @@ int main(void)
|
|||||||
uint8_t cmd[CMDSIZE] = {0};
|
uint8_t cmd[CMDSIZE] = {0};
|
||||||
enum state state = FW_STATE_INITIAL;
|
enum state state = FW_STATE_INITIAL;
|
||||||
|
|
||||||
led_set(LED_BLUE);
|
|
||||||
|
|
||||||
print_hw_version();
|
print_hw_version();
|
||||||
|
|
||||||
/*@-mustfreeonly@*/
|
/*@-mustfreeonly@*/
|
||||||
@ -510,11 +418,6 @@ int main(void)
|
|||||||
|
|
||||||
scramble_ram();
|
scramble_ram();
|
||||||
|
|
||||||
if (part_table_read(&part_table_storage) != 0) {
|
|
||||||
// Couldn't read or create partition table
|
|
||||||
assert(1 == 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(SIMULATION)
|
#if defined(SIMULATION)
|
||||||
run(&ctx);
|
run(&ctx);
|
||||||
#endif
|
#endif
|
||||||
@ -522,10 +425,6 @@ int main(void)
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case FW_STATE_INITIAL:
|
case FW_STATE_INITIAL:
|
||||||
state = start_where(&ctx);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FW_STATE_WAITCOMMAND:
|
|
||||||
if (readcommand(&hdr, cmd, state) == -1) {
|
if (readcommand(&hdr, cmd, state) == -1) {
|
||||||
state = FW_STATE_FAIL;
|
state = FW_STATE_FAIL;
|
||||||
break;
|
break;
|
||||||
@ -546,51 +445,9 @@ int main(void)
|
|||||||
state = loading_commands(&hdr, cmd, state, &ctx);
|
state = loading_commands(&hdr, cmd, state, &ctx);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FW_STATE_LOAD_FLASH:
|
case FW_STATE_RUN:
|
||||||
if (load_flash_app(&part_table_storage.table,
|
run(&ctx);
|
||||||
ctx.digest, ctx.flash_slot) < 0) {
|
break; // This is never reached!
|
||||||
debug_puts("Couldn't load app from flash\n");
|
|
||||||
state = FW_STATE_FAIL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
state = FW_STATE_START;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FW_STATE_LOAD_FLASH_MGMT:
|
|
||||||
if (load_flash_app(&part_table_storage.table,
|
|
||||||
ctx.digest, ctx.flash_slot) < 0) {
|
|
||||||
debug_puts("Couldn't load app from flash\n");
|
|
||||||
state = FW_STATE_FAIL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mgmt_app_init(ctx.digest) != 0) {
|
|
||||||
state = FW_STATE_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
state = FW_STATE_START;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FW_STATE_START:
|
|
||||||
// CDI = hash(uds, hash(app), uss)
|
|
||||||
compute_cdi(ctx.digest, ctx.use_uss, ctx.uss);
|
|
||||||
|
|
||||||
if (ctx.ver_digest != NULL) {
|
|
||||||
print_digest(ctx.digest);
|
|
||||||
if (!memeq(ctx.digest, (void *)ctx.ver_digest,
|
|
||||||
sizeof(ctx.digest))) {
|
|
||||||
debug_puts("Digests do not match\n");
|
|
||||||
state = FW_STATE_FAIL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(void)memset((void *)resetinfo->app_digest, 0,
|
|
||||||
sizeof(resetinfo->app_digest));
|
|
||||||
|
|
||||||
jump_to_app();
|
|
||||||
break; // Not reached
|
|
||||||
|
|
||||||
case FW_STATE_FAIL:
|
case FW_STATE_FAIL:
|
||||||
// fallthrough
|
// fallthrough
|
||||||
@ -605,6 +462,5 @@ int main(void)
|
|||||||
|
|
||||||
/*@ -compdestroy @*/
|
/*@ -compdestroy @*/
|
||||||
/* We don't care about memory leaks here. */
|
/* We don't care about memory leaks here. */
|
||||||
|
|
||||||
return (int)0xcafebabe;
|
return (int)0xcafebabe;
|
||||||
}
|
}
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
// Copyright (C) 2024 - Tillitis AB
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <tkey/io.h>
|
|
||||||
#include <tkey/lib.h>
|
|
||||||
|
|
||||||
#include "mgmt_app.h"
|
|
||||||
|
|
||||||
// Lock down what app can start from flash slot 0.
|
|
||||||
//
|
|
||||||
// To update this, compute the BLAKE2s digest of the app.bin
|
|
||||||
static const uint8_t allowed_app_digest[32] = {
|
|
||||||
0xb6, 0x86, 0x1b, 0x26, 0xef, 0x69, 0x77, 0x12, 0xed, 0x6c, 0xca,
|
|
||||||
0xe8, 0x35, 0xb4, 0x5c, 0x01, 0x07, 0x71, 0xab, 0xce, 0x3f, 0x30,
|
|
||||||
0x79, 0xda, 0xe6, 0xf9, 0xee, 0x4b, 0xe2, 0x06, 0x95, 0x33,
|
|
||||||
};
|
|
||||||
|
|
||||||
static uint8_t current_app_digest[32];
|
|
||||||
|
|
||||||
int mgmt_app_init(uint8_t app_digest[32])
|
|
||||||
{
|
|
||||||
if (app_digest == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy_s(current_app_digest, sizeof(current_app_digest), app_digest,
|
|
||||||
32);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Authenticate an management app */
|
|
||||||
bool mgmt_app_authenticate(void)
|
|
||||||
{
|
|
||||||
return memeq(current_app_digest, allowed_app_digest, 32) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t *mgmt_app_allowed_digest(void)
|
|
||||||
{
|
|
||||||
return (uint8_t *)allowed_app_digest;
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
// Copyright (C) 2024 - Tillitis AB
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
|
|
||||||
#ifndef MGMT_APP_H
|
|
||||||
#define MGMT_APP_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
int mgmt_app_init(uint8_t app_digest[32]);
|
|
||||||
bool mgmt_app_authenticate(void);
|
|
||||||
uint8_t *mgmt_app_allowed_digest(void);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,106 +0,0 @@
|
|||||||
// Copyright (C) 2024 - Tillitis AB
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <tkey/assert.h>
|
|
||||||
#include <tkey/lib.h>
|
|
||||||
|
|
||||||
#include "blake2s/blake2s.h"
|
|
||||||
#include "flash.h"
|
|
||||||
#include "partition_table.h"
|
|
||||||
#include "proto.h"
|
|
||||||
|
|
||||||
static enum part_status part_status;
|
|
||||||
|
|
||||||
enum part_status part_get_status(void)
|
|
||||||
{
|
|
||||||
return part_status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void part_digest(struct partition_table *part_table, uint8_t *out_digest,
|
|
||||||
size_t out_len);
|
|
||||||
|
|
||||||
static void part_digest(struct partition_table *part_table, uint8_t *out_digest,
|
|
||||||
size_t out_len)
|
|
||||||
{
|
|
||||||
int blake2err = 0;
|
|
||||||
|
|
||||||
uint8_t key[16] = {
|
|
||||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
assert(part_table != NULL);
|
|
||||||
assert(out_digest != NULL);
|
|
||||||
|
|
||||||
blake2err = blake2s(out_digest, out_len, key, sizeof(key), part_table,
|
|
||||||
sizeof(struct partition_table));
|
|
||||||
|
|
||||||
assert(blake2err == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// part_table_read reads and verifies the partition table storage,
|
|
||||||
// first trying slot 0, then slot 1 if slot 0 does not verify.
|
|
||||||
//
|
|
||||||
// It stores the partition table in storage.
|
|
||||||
//
|
|
||||||
// Returns negative values on errors.
|
|
||||||
int part_table_read(struct partition_table_storage *storage)
|
|
||||||
{
|
|
||||||
uint32_t offset[2] = {
|
|
||||||
ADDR_PARTITION_TABLE_0,
|
|
||||||
ADDR_PARTITION_TABLE_1,
|
|
||||||
};
|
|
||||||
uint8_t check_digest[PART_DIGEST_SIZE] = {0};
|
|
||||||
|
|
||||||
if (storage == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
flash_release_powerdown();
|
|
||||||
(void)memset(storage, 0x00, sizeof(*storage));
|
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++) {
|
|
||||||
if (flash_read_data(offset[i], (uint8_t *)storage,
|
|
||||||
sizeof(*storage)) != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
part_digest(&storage->table, check_digest,
|
|
||||||
sizeof(check_digest));
|
|
||||||
|
|
||||||
if (memeq(check_digest, storage->check_digest,
|
|
||||||
sizeof(check_digest))) {
|
|
||||||
if (i == 1) {
|
|
||||||
part_status = PART_SLOT0_INVALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int part_table_write(struct partition_table_storage *storage)
|
|
||||||
{
|
|
||||||
uint32_t offset[2] = {
|
|
||||||
ADDR_PARTITION_TABLE_0,
|
|
||||||
ADDR_PARTITION_TABLE_1,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (storage == NULL) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
part_digest(&storage->table, storage->check_digest,
|
|
||||||
sizeof(storage->check_digest));
|
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++) {
|
|
||||||
flash_sector_erase(offset[i]);
|
|
||||||
if (flash_write_data(offset[i], (uint8_t *)storage,
|
|
||||||
sizeof(*storage)) != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,109 +0,0 @@
|
|||||||
// Copyright (C) 2024 - Tillitis AB
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
|
|
||||||
#ifndef PARTITION_TABLE_H
|
|
||||||
#define PARTITION_TABLE_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
/* ---- Flash ---- ---- */
|
|
||||||
/* name size start addr */
|
|
||||||
/* ---- ---- ---- */
|
|
||||||
/* bitstream 128KiB 0x00 */
|
|
||||||
/* ---- ---- ---- */
|
|
||||||
/* Partition 64KiB 0x20000 */
|
|
||||||
/* ---- ---- ---- */
|
|
||||||
/* Pre load 1 128KiB 0x30000 */
|
|
||||||
/* Pre load 2 128KiB 0x50000 */
|
|
||||||
/* ---- ---- ---- */
|
|
||||||
/* storage 1 128KiB 0x70000 */
|
|
||||||
/* storage 2 128KiB 0x90000 */
|
|
||||||
/* storage 3 128KiB 0xB0000 */
|
|
||||||
/* storage 4 128KiB 0xD0000 */
|
|
||||||
/* ---- ---- ---- */
|
|
||||||
/* Partition2 64KiB 0xf0000 */
|
|
||||||
|
|
||||||
/* To simplify all blocks are aligned with the 64KiB blocks on the W25Q80DL
|
|
||||||
* flash. */
|
|
||||||
|
|
||||||
#define PART_TABLE_VERSION 1
|
|
||||||
|
|
||||||
#define ADDR_BITSTREAM 0UL
|
|
||||||
#define SIZE_BITSTREAM 0x20000UL // 128KiB
|
|
||||||
|
|
||||||
#define ADDR_PARTITION_TABLE_0 (ADDR_BITSTREAM + SIZE_BITSTREAM)
|
|
||||||
#define ADDR_PARTITION_TABLE_1 0xf0000
|
|
||||||
#define SIZE_PARTITION_TABLE \
|
|
||||||
0x10000UL // 64KiB, 60 KiB reserved, 2 flash pages (2 x 4KiB) for the
|
|
||||||
// partition table
|
|
||||||
|
|
||||||
#define N_PRELOADED_APP 2
|
|
||||||
#define ADDR_PRE_LOADED_APP_0 (ADDR_PARTITION_TABLE_0 + SIZE_PARTITION_TABLE)
|
|
||||||
#define SIZE_PRE_LOADED_APP 0x20000UL // 128KiB
|
|
||||||
|
|
||||||
#define ADDR_STORAGE_AREA \
|
|
||||||
(ADDR_PRE_LOADED_APP_0 + (N_PRELOADED_APP * SIZE_PRE_LOADED_APP))
|
|
||||||
#define SIZE_STORAGE_AREA 0x20000UL // 128KiB
|
|
||||||
#define N_STORAGE_AREA 4
|
|
||||||
|
|
||||||
#define PART_DIGEST_SIZE 16
|
|
||||||
|
|
||||||
enum part_status {
|
|
||||||
PART_SLOT0_INVALID = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Partition Table */
|
|
||||||
/*- Table header */
|
|
||||||
/* - 1 bytes Version */
|
|
||||||
/**/
|
|
||||||
/*- Pre-loaded device app 1 */
|
|
||||||
/* - 4 bytes length. */
|
|
||||||
/* - 32 bytes digest. */
|
|
||||||
/* - 64 bytes signature. */
|
|
||||||
/**/
|
|
||||||
/*- Pre-loaded device app 2 */
|
|
||||||
/* - 4 bytes length. */
|
|
||||||
/* - 32 bytes digest. */
|
|
||||||
/* - 64 bytes signature. */
|
|
||||||
/**/
|
|
||||||
/*- Device app storage area */
|
|
||||||
/* - 1 byte status. */
|
|
||||||
/* - 16 bytes random nonce. */
|
|
||||||
/* - 16 bytes authentication tag. */
|
|
||||||
|
|
||||||
struct auth_metadata {
|
|
||||||
uint8_t nonce[16];
|
|
||||||
uint8_t authentication_digest[16];
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
struct pre_loaded_app_metadata {
|
|
||||||
uint32_t size;
|
|
||||||
uint8_t digest[32];
|
|
||||||
uint8_t signature[64];
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
struct app_storage_area {
|
|
||||||
uint8_t status;
|
|
||||||
struct auth_metadata auth;
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
struct table_header {
|
|
||||||
uint8_t version;
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
struct partition_table {
|
|
||||||
struct table_header header;
|
|
||||||
struct pre_loaded_app_metadata pre_app_data[N_PRELOADED_APP];
|
|
||||||
struct app_storage_area app_storage[N_STORAGE_AREA];
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
struct partition_table_storage {
|
|
||||||
struct partition_table table;
|
|
||||||
uint8_t check_digest[PART_DIGEST_SIZE];
|
|
||||||
} __attribute__((packed));
|
|
||||||
|
|
||||||
enum part_status part_get_status(void);
|
|
||||||
int part_table_read(struct partition_table_storage *storage);
|
|
||||||
int part_table_write(struct partition_table_storage *storage);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,198 +0,0 @@
|
|||||||
// Copyright (C) 2024 - Tillitis AB
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <tkey/debug.h>
|
|
||||||
#include <tkey/lib.h>
|
|
||||||
#include <tkey/tk1_mem.h>
|
|
||||||
|
|
||||||
#include "flash.h"
|
|
||||||
#include "mgmt_app.h"
|
|
||||||
#include "partition_table.h"
|
|
||||||
#include "preload_app.h"
|
|
||||||
|
|
||||||
static uint32_t slot_to_start_address(uint8_t slot)
|
|
||||||
{
|
|
||||||
return ADDR_PRE_LOADED_APP_0 + slot * SIZE_PRE_LOADED_APP;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Loads a preloaded app from flash to app RAM */
|
|
||||||
int preload_load(struct partition_table *part_table, uint8_t from_slot)
|
|
||||||
{
|
|
||||||
if (part_table == NULL) {
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (from_slot >= N_PRELOADED_APP) {
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Check for a valid app in flash */
|
|
||||||
if (part_table->pre_app_data[from_slot].size == 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
uint8_t *loadaddr = (uint8_t *)TK1_RAM_BASE;
|
|
||||||
|
|
||||||
/* Read from flash, straight into RAM */
|
|
||||||
int ret = flash_read_data(slot_to_start_address(from_slot), loadaddr,
|
|
||||||
part_table->pre_app_data[from_slot].size);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Expects to receive chunks of data up to 4096 bytes to store into the
|
|
||||||
* preloaded area. The offset needs to be kept and updated between each call.
|
|
||||||
* Once done, call preload_store_finalize() with the last parameters.
|
|
||||||
* */
|
|
||||||
int preload_store(struct partition_table *part_table, uint32_t offset,
|
|
||||||
uint8_t *data, size_t size, uint8_t to_slot)
|
|
||||||
{
|
|
||||||
if (part_table == NULL || data == NULL) {
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (to_slot >= N_PRELOADED_APP) {
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if we are allowed to store */
|
|
||||||
if (!mgmt_app_authenticate()) {
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for a valid app in flash, bale out if it already exists */
|
|
||||||
if (part_table->pre_app_data[to_slot].size != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((offset + size) > SIZE_PRE_LOADED_APP || size > 4096) {
|
|
||||||
/* Writing outside of area */
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t address = slot_to_start_address(to_slot) + offset;
|
|
||||||
|
|
||||||
debug_puts("preload_store: write to addr: ");
|
|
||||||
debug_putinthex(address);
|
|
||||||
debug_lf();
|
|
||||||
|
|
||||||
return flash_write_data(address, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
int preload_store_finalize(struct partition_table_storage *part_table_storage,
|
|
||||||
size_t app_size, uint8_t app_digest[32],
|
|
||||||
uint8_t app_signature[64], uint8_t to_slot)
|
|
||||||
{
|
|
||||||
struct partition_table *part_table = &part_table_storage->table;
|
|
||||||
|
|
||||||
if (part_table == NULL || app_digest == NULL || app_signature == NULL) {
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (to_slot >= N_PRELOADED_APP) {
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if we are allowed to store */
|
|
||||||
if (!mgmt_app_authenticate()) {
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check for a valid app in flash, bale out if it already exists */
|
|
||||||
if (part_table->pre_app_data[to_slot].size != 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (app_size == 0 || app_size > SIZE_PRE_LOADED_APP) {
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
part_table->pre_app_data[to_slot].size = app_size;
|
|
||||||
memcpy_s(part_table->pre_app_data[to_slot].digest,
|
|
||||||
sizeof(part_table->pre_app_data[to_slot].digest), app_digest,
|
|
||||||
32);
|
|
||||||
memcpy_s(part_table->pre_app_data[to_slot].signature,
|
|
||||||
sizeof(part_table->pre_app_data[to_slot].signature),
|
|
||||||
app_signature, 64);
|
|
||||||
debug_puts("preload_*_final: size: ");
|
|
||||||
debug_putinthex(app_size);
|
|
||||||
debug_lf();
|
|
||||||
|
|
||||||
if (part_table_write(part_table_storage) != 0) {
|
|
||||||
return -6;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int preload_delete(struct partition_table_storage *part_table_storage,
|
|
||||||
uint8_t slot)
|
|
||||||
{
|
|
||||||
struct partition_table *part_table = &part_table_storage->table;
|
|
||||||
|
|
||||||
if (part_table_storage == NULL) {
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slot >= N_PRELOADED_APP) {
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if we are allowed to deleted */
|
|
||||||
if (!mgmt_app_authenticate()) {
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*Check for a valid app in flash */
|
|
||||||
if (part_table->pre_app_data[slot].size == 0) {
|
|
||||||
// Nothing to do.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
part_table->pre_app_data[slot].size = 0;
|
|
||||||
|
|
||||||
(void)memset(part_table->pre_app_data[slot].digest, 0,
|
|
||||||
sizeof(part_table->pre_app_data[slot].digest));
|
|
||||||
|
|
||||||
(void)memset(part_table->pre_app_data[slot].signature, 0,
|
|
||||||
sizeof(part_table->pre_app_data[slot].signature));
|
|
||||||
|
|
||||||
if (part_table_write(part_table_storage) != 0) {
|
|
||||||
return -6;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Assumes the area is 64 KiB block aligned */
|
|
||||||
flash_block_64_erase(
|
|
||||||
slot_to_start_address(slot)); // Erase first 64 KB block
|
|
||||||
flash_block_64_erase(slot_to_start_address(slot) +
|
|
||||||
0x10000); // Erase first 64 KB block
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int preload_get_digsig(struct partition_table *part_table,
|
|
||||||
uint8_t app_digest[32], uint8_t app_signature[64],
|
|
||||||
uint8_t slot)
|
|
||||||
{
|
|
||||||
if (part_table == NULL || app_digest == NULL || app_signature == NULL) {
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (slot >= N_PRELOADED_APP) {
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Check if we are allowed to read */
|
|
||||||
if (!mgmt_app_authenticate()) {
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy_s(app_digest, 32, part_table->pre_app_data[slot].digest,
|
|
||||||
sizeof(part_table->pre_app_data[slot].digest));
|
|
||||||
memcpy_s(app_signature, 64, part_table->pre_app_data[slot].signature,
|
|
||||||
sizeof(part_table->pre_app_data[slot].signature));
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
// Copyright (C) 2024 - Tillitis AB
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
|
|
||||||
#ifndef PRELOAD_APP_H
|
|
||||||
#define PRELOAD_APP_H
|
|
||||||
|
|
||||||
#include "partition_table.h"
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
int preload_load(struct partition_table *part_table, uint8_t from_slot);
|
|
||||||
int preload_store(struct partition_table *part_table, uint32_t offset,
|
|
||||||
uint8_t *data, size_t size, uint8_t to_slot);
|
|
||||||
int preload_store_finalize(struct partition_table_storage *part_table_storage,
|
|
||||||
size_t app_size, uint8_t app_digest[32],
|
|
||||||
uint8_t app_signature[64], uint8_t to_slot);
|
|
||||||
int preload_delete(struct partition_table_storage *part_table_storage,
|
|
||||||
uint8_t slot);
|
|
||||||
int preload_get_digsig(struct partition_table *part_table,
|
|
||||||
uint8_t app_digest[32], uint8_t app_signature[64],
|
|
||||||
uint8_t slot);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,28 +0,0 @@
|
|||||||
// Copyright (C) 2025 - Tillitis AB
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
|
|
||||||
#ifndef TKEY_RESETINFO_H
|
|
||||||
#define TKEY_RESETINFO_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
#define TK1_MMIO_RESETINFO_BASE 0xd0000f00
|
|
||||||
#define TK1_MMIO_RESETINFO_SIZE 0x100
|
|
||||||
|
|
||||||
enum reset_start {
|
|
||||||
START_DEFAULT = 0, // Probably cold boot
|
|
||||||
START_FLASH0 = 1,
|
|
||||||
START_FLASH1 = 2,
|
|
||||||
START_FLASH0_VER = 3,
|
|
||||||
START_FLASH1_VER = 4,
|
|
||||||
START_CLIENT = 5,
|
|
||||||
START_CLIENT_VER = 6,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct reset {
|
|
||||||
uint32_t type; // Reset type
|
|
||||||
uint8_t app_digest[32]; // Program digest
|
|
||||||
uint8_t next_app_data[220]; // Data to leave around for next app
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,29 +0,0 @@
|
|||||||
// Copyright (C) 2024 - Tillitis AB
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
|
|
||||||
#include "rng.h"
|
|
||||||
#include <tkey/tk1_mem.h>
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
static volatile uint32_t *trng_status = (volatile uint32_t *)TK1_MMIO_TRNG_STATUS;
|
|
||||||
static volatile uint32_t *trng_entropy = (volatile uint32_t *)TK1_MMIO_TRNG_ENTROPY;
|
|
||||||
// clang-format on
|
|
||||||
//
|
|
||||||
//
|
|
||||||
uint32_t rng_get_word(void)
|
|
||||||
{
|
|
||||||
while ((*trng_status & (1 << TK1_MMIO_TRNG_STATUS_READY_BIT)) == 0) {
|
|
||||||
}
|
|
||||||
return *trng_entropy;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t rng_xorwow(uint32_t state, uint32_t acc)
|
|
||||||
{
|
|
||||||
state ^= state << 13;
|
|
||||||
state ^= state >> 17;
|
|
||||||
state ^= state << 5;
|
|
||||||
state += acc;
|
|
||||||
return state;
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
// Copyright (C) 2024 - Tillitis AB
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
#ifndef RNG_H
|
|
||||||
#define RNG_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
uint32_t rng_get_word(void);
|
|
||||||
uint32_t rng_xorwow(uint32_t state, uint32_t acc);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,100 +0,0 @@
|
|||||||
// Copyright (C) 2024 - Tillitis AB
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
|
|
||||||
#include "spi.h"
|
|
||||||
#include <tkey/assert.h>
|
|
||||||
#include <tkey/tk1_mem.h>
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
// clang-format off
|
|
||||||
static volatile uint32_t *spi_en = (volatile uint32_t *)(TK1_MMIO_TK1_BASE | 0x200);
|
|
||||||
static volatile uint32_t *spi_xfer = (volatile uint32_t *)(TK1_MMIO_TK1_BASE | 0x204);
|
|
||||||
static volatile uint32_t *spi_data = (volatile uint32_t *)(TK1_MMIO_TK1_BASE | 0x208);
|
|
||||||
// clang-format on
|
|
||||||
|
|
||||||
static int spi_ready(void);
|
|
||||||
static void spi_enable(void);
|
|
||||||
static void spi_disable(void);
|
|
||||||
static void spi_write(uint8_t *cmd, size_t size);
|
|
||||||
static void spi_read(uint8_t *buf, size_t size);
|
|
||||||
|
|
||||||
// returns non-zero when the SPI-master is ready, and zero if not
|
|
||||||
// ready. This can be used to check if the SPI-master is available
|
|
||||||
// in the hardware.
|
|
||||||
static int spi_ready(void)
|
|
||||||
{
|
|
||||||
return *spi_xfer;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void spi_enable(void)
|
|
||||||
{
|
|
||||||
*spi_en = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void spi_disable(void)
|
|
||||||
{
|
|
||||||
*spi_en = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void spi_write(uint8_t *cmd, size_t size)
|
|
||||||
{
|
|
||||||
assert(cmd != NULL);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < size; i++) {
|
|
||||||
while (!spi_ready()) {
|
|
||||||
}
|
|
||||||
|
|
||||||
*spi_data = cmd[i];
|
|
||||||
*spi_xfer = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!spi_ready()) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void spi_read(uint8_t *buf, size_t size)
|
|
||||||
{
|
|
||||||
assert(buf != NULL);
|
|
||||||
|
|
||||||
while (!spi_ready()) {
|
|
||||||
}
|
|
||||||
|
|
||||||
for (size_t i = 0; i < size; i++) {
|
|
||||||
|
|
||||||
*spi_data = 0x00;
|
|
||||||
*spi_xfer = 1;
|
|
||||||
|
|
||||||
// wait until spi master is done
|
|
||||||
while (!spi_ready()) {
|
|
||||||
}
|
|
||||||
|
|
||||||
buf[i] = (*spi_data & 0xff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to both read and write data to the connected SPI flash.
|
|
||||||
int spi_transfer(uint8_t *cmd, size_t cmd_size, uint8_t *tx_buf, size_t tx_size,
|
|
||||||
uint8_t *rx_buf, size_t rx_size)
|
|
||||||
{
|
|
||||||
if (cmd == NULL || cmd_size == 0) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
spi_enable();
|
|
||||||
|
|
||||||
spi_write(cmd, cmd_size);
|
|
||||||
|
|
||||||
if (tx_buf != NULL && tx_size != 0) {
|
|
||||||
spi_write(tx_buf, tx_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rx_buf != NULL && rx_size != 0) {
|
|
||||||
spi_read(rx_buf, rx_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
spi_disable();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
// Copyright (C) 2024 - Tillitis AB
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
|
|
||||||
#ifndef TKEY_SPI_H
|
|
||||||
#define TKEY_SPI_H
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
int spi_transfer(uint8_t *cmd, size_t cmd_size, uint8_t *tx_buf, size_t tx_size,
|
|
||||||
uint8_t *rx_buf, size_t rx_size);
|
|
||||||
|
|
||||||
#endif
|
|
@ -4,19 +4,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <tkey/tk1_mem.h>
|
#include <tkey/tk1_mem.h>
|
||||||
|
|
||||||
#ifdef QEMU_SYSCALL
|
|
||||||
|
|
||||||
#define picorv32_retirq_insn(...) \
|
|
||||||
mv ra, x3; \
|
|
||||||
ret
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#include "picorv32/custom_ops.S" // PicoRV32 custom instructions
|
#include "picorv32/custom_ops.S" // PicoRV32 custom instructions
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define illegal_insn() .word 0
|
#define illegal_insn() .word 0
|
||||||
|
|
||||||
// Variables in bss
|
// Variables in bss
|
||||||
|
@ -8,11 +8,8 @@
|
|||||||
|
|
||||||
enum state {
|
enum state {
|
||||||
FW_STATE_INITIAL,
|
FW_STATE_INITIAL,
|
||||||
FW_STATE_WAITCOMMAND,
|
|
||||||
FW_STATE_LOADING,
|
FW_STATE_LOADING,
|
||||||
FW_STATE_LOAD_FLASH,
|
FW_STATE_RUN,
|
||||||
FW_STATE_LOAD_FLASH_MGMT,
|
|
||||||
FW_STATE_START,
|
|
||||||
FW_STATE_FAIL,
|
FW_STATE_FAIL,
|
||||||
FW_STATE_MAX,
|
FW_STATE_MAX,
|
||||||
};
|
};
|
||||||
|
@ -1,278 +0,0 @@
|
|||||||
// Copyright (C) 2024 - Tillitis AB
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <tkey/debug.h>
|
|
||||||
#include <tkey/lib.h>
|
|
||||||
|
|
||||||
#include "auth_app.h"
|
|
||||||
#include "flash.h"
|
|
||||||
#include "partition_table.h"
|
|
||||||
#include "storage.h"
|
|
||||||
|
|
||||||
/* Returns the index of the first empty area. If there is no empty area -1 is
|
|
||||||
* returned. */
|
|
||||||
static int get_first_empty(struct partition_table *part_table)
|
|
||||||
{
|
|
||||||
if (part_table == NULL) {
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < N_STORAGE_AREA; i++) {
|
|
||||||
if (part_table->app_storage[i].status == 0x00) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int index_to_address(int index, uint32_t *address)
|
|
||||||
{
|
|
||||||
if (address == NULL) {
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((index < 0) || (index >= N_STORAGE_AREA)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
*address = ADDR_STORAGE_AREA + index * SIZE_STORAGE_AREA;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Returns the index of the area an app has allocated. If no area is
|
|
||||||
* authenticated -1 is returned. */
|
|
||||||
static int storage_get_area(struct partition_table *part_table)
|
|
||||||
{
|
|
||||||
if (part_table == NULL) {
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (uint8_t i = 0; i < N_STORAGE_AREA; i++) {
|
|
||||||
if (part_table->app_storage[i].status != 0x00) {
|
|
||||||
if (auth_app_authenticate(
|
|
||||||
&part_table->app_storage[i].auth)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate a new area for an app. Returns zero if a new area is allocated, one
|
|
||||||
* if an area already was allocated, and negative values for errors. */
|
|
||||||
int storage_allocate_area(struct partition_table_storage *part_table_storage)
|
|
||||||
{
|
|
||||||
if (part_table_storage == NULL) {
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct partition_table *part_table = &part_table_storage->table;
|
|
||||||
|
|
||||||
if (storage_get_area(part_table) != -1) {
|
|
||||||
/* Already has an area */
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = get_first_empty(part_table);
|
|
||||||
if (index == -1) {
|
|
||||||
/* No empty slot */
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t start_address = 0;
|
|
||||||
int err = index_to_address(index, &start_address);
|
|
||||||
if (err) {
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Allocate the empty index found */
|
|
||||||
/* Erase area first */
|
|
||||||
|
|
||||||
/* Assumes the area is 64 KiB block aligned */
|
|
||||||
flash_block_64_erase(start_address); // Erase first 64 KB block
|
|
||||||
flash_block_64_erase(start_address +
|
|
||||||
0x10000); // Erase second 64 KB block
|
|
||||||
|
|
||||||
/* Write partition table lastly */
|
|
||||||
part_table->app_storage[index].status = 0x01;
|
|
||||||
auth_app_create(&part_table->app_storage[index].auth);
|
|
||||||
|
|
||||||
if (part_table_write(part_table_storage) != 0) {
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dealloacate a previously allocated storage area. Returns zero on success, and
|
|
||||||
* non-zero on errors. */
|
|
||||||
int storage_deallocate_area(struct partition_table_storage *part_table_storage)
|
|
||||||
{
|
|
||||||
if (part_table_storage == NULL) {
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct partition_table *part_table = &part_table_storage->table;
|
|
||||||
|
|
||||||
int index = storage_get_area(part_table);
|
|
||||||
if (index == -1) {
|
|
||||||
/* No area to deallocate */
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t start_address = 0;
|
|
||||||
int err = index_to_address(index, &start_address);
|
|
||||||
if (err) {
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Erase area first */
|
|
||||||
|
|
||||||
/* Assumes the area is 64 KiB block aligned */
|
|
||||||
flash_block_64_erase(start_address); // Erase first 64 KB block
|
|
||||||
flash_block_64_erase(start_address +
|
|
||||||
0x10000); // Erase second 64 KB block
|
|
||||||
|
|
||||||
/* Clear partition table lastly */
|
|
||||||
part_table->app_storage[index].status = 0;
|
|
||||||
|
|
||||||
(void)memset(part_table->app_storage[index].auth.nonce, 0x00,
|
|
||||||
sizeof(part_table->app_storage[index].auth.nonce));
|
|
||||||
|
|
||||||
(void)memset(
|
|
||||||
part_table->app_storage[index].auth.authentication_digest, 0x00,
|
|
||||||
sizeof(part_table->app_storage[index].auth.authentication_digest));
|
|
||||||
|
|
||||||
if (part_table_write(part_table_storage) != 0) {
|
|
||||||
return -5;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Erases sector. Offset of a sector to begin erasing, must be a multiple of
|
|
||||||
* the sector size. Size to erase in bytes, must be a multiple of the sector *
|
|
||||||
* size. Returns zero on success, negative error code on failure */
|
|
||||||
int storage_erase_sector(struct partition_table *part_table, uint32_t offset,
|
|
||||||
size_t size)
|
|
||||||
{
|
|
||||||
if (part_table == NULL) {
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = storage_get_area(part_table);
|
|
||||||
if (index == -1) {
|
|
||||||
/* No allocated area */
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t start_address = 0;
|
|
||||||
int err = index_to_address(index, &start_address);
|
|
||||||
if (err) {
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cannot only erase entire sectors */
|
|
||||||
if (offset % 4096 != 0) {
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Cannot erase less than one sector */
|
|
||||||
if (size < 4096 || size > SIZE_STORAGE_AREA || size % 4096 != 0) {
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((offset + size) >= SIZE_STORAGE_AREA) {
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t address = start_address + offset;
|
|
||||||
|
|
||||||
debug_puts("storage: erase addr: ");
|
|
||||||
debug_putinthex(address);
|
|
||||||
debug_lf();
|
|
||||||
|
|
||||||
for (size_t i = 0; i < size; i += 4096) {
|
|
||||||
flash_sector_erase(address);
|
|
||||||
address += 4096;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Writes the specified data to the offset inside of the
|
|
||||||
* allocated area. Assumes area has been erased before hand.
|
|
||||||
* Currently only handles writes to one sector, hence max size of 4096 bytes.
|
|
||||||
* Returns zero on success. */
|
|
||||||
int storage_write_data(struct partition_table *part_table, uint32_t offset,
|
|
||||||
uint8_t *data, size_t size)
|
|
||||||
{
|
|
||||||
if (part_table == NULL || data == NULL) {
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = storage_get_area(part_table);
|
|
||||||
if (index == -1) {
|
|
||||||
/* No allocated area */
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t start_address = 0;
|
|
||||||
int err = index_to_address(index, &start_address);
|
|
||||||
if (err) {
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((offset + size) > SIZE_STORAGE_AREA || size > 4096) {
|
|
||||||
/* Writing outside of area */
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t address = start_address + offset;
|
|
||||||
|
|
||||||
debug_puts("storage: write to addr: ");
|
|
||||||
debug_putinthex(address);
|
|
||||||
debug_lf();
|
|
||||||
|
|
||||||
return flash_write_data(address, data, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reads size bytes of data at the specified offset inside of
|
|
||||||
* the allocated area. Returns zero on success. Only read limit
|
|
||||||
* is the size of the allocated area */
|
|
||||||
int storage_read_data(struct partition_table *part_table, uint32_t offset,
|
|
||||||
uint8_t *data, size_t size)
|
|
||||||
{
|
|
||||||
if (part_table == NULL || data == NULL) {
|
|
||||||
return -4;
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = storage_get_area(part_table);
|
|
||||||
if (index == -1) {
|
|
||||||
/* No allocated area */
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t start_address = 0;
|
|
||||||
int err = index_to_address(index, &start_address);
|
|
||||||
if (err) {
|
|
||||||
return -3;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((offset + size) > SIZE_STORAGE_AREA) {
|
|
||||||
/* Reading outside of area */
|
|
||||||
return -2;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t address = start_address + offset;
|
|
||||||
|
|
||||||
debug_puts("storage: read from addr: ");
|
|
||||||
debug_putinthex(address);
|
|
||||||
debug_lf();
|
|
||||||
|
|
||||||
return flash_read_data(address, data, size);
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
// Copyright (C) 2024 - Tillitis AB
|
|
||||||
// SPDX-License-Identifier: GPL-2.0-only
|
|
||||||
|
|
||||||
#ifndef STORAGE_H
|
|
||||||
#define STORAGE_H
|
|
||||||
|
|
||||||
#include "partition_table.h"
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stddef.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
int storage_deallocate_area(struct partition_table_storage *part_table_storage);
|
|
||||||
int storage_allocate_area(struct partition_table_storage *part_table_storage);
|
|
||||||
int storage_erase_sector(struct partition_table *part_table, uint32_t offset,
|
|
||||||
size_t size);
|
|
||||||
int storage_write_data(struct partition_table *part_table, uint32_t offset,
|
|
||||||
uint8_t *data, size_t size);
|
|
||||||
int storage_read_data(struct partition_table *part_table, uint32_t offset,
|
|
||||||
uint8_t *data, size_t size);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,13 +1,5 @@
|
|||||||
#ifdef QEMU_SYSCALL
|
|
||||||
|
|
||||||
#define picorv32_maskirq_insn(...)
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
#include "../tk1/picorv32/custom_ops.S"
|
#include "../tk1/picorv32/custom_ops.S"
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
.section ".text"
|
.section ".text"
|
||||||
.globl syscall_enable
|
.globl syscall_enable
|
||||||
|
|
||||||
|
@ -5,112 +5,29 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <tkey/assert.h>
|
#include <tkey/assert.h>
|
||||||
#include <tkey/debug.h>
|
#include <tkey/led.h>
|
||||||
#include <tkey/lib.h>
|
|
||||||
#include <tkey/tk1_mem.h>
|
|
||||||
|
|
||||||
#include "partition_table.h"
|
|
||||||
#include "preload_app.h"
|
|
||||||
#include "storage.h"
|
|
||||||
|
|
||||||
#include "../tk1/resetinfo.h"
|
|
||||||
#include "../tk1/syscall_num.h"
|
#include "../tk1/syscall_num.h"
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static volatile uint32_t *system_reset = (volatile uint32_t *)TK1_MMIO_TK1_SYSTEM_RESET;
|
static volatile uint32_t *system_reset = (volatile uint32_t *)TK1_MMIO_TK1_SYSTEM_RESET;
|
||||||
static volatile uint32_t *udi = (volatile uint32_t *)TK1_MMIO_TK1_UDI_FIRST;
|
static volatile uint32_t *udi = (volatile uint32_t *)TK1_MMIO_TK1_UDI_FIRST;
|
||||||
static volatile struct reset *resetinfo = (volatile struct reset *)TK1_MMIO_RESETINFO_BASE;
|
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
extern struct partition_table_storage part_table_storage;
|
int32_t syscall_handler(uint32_t number, uint32_t arg1)
|
||||||
extern uint8_t part_status;
|
|
||||||
|
|
||||||
int32_t syscall_handler(uint32_t number, uint32_t arg1, uint32_t arg2,
|
|
||||||
uint32_t arg3)
|
|
||||||
{
|
{
|
||||||
struct reset *userreset = (struct reset *)arg1;
|
|
||||||
|
|
||||||
switch (number) {
|
switch (number) {
|
||||||
case TK1_SYSCALL_RESET:
|
case TK1_SYSCALL_RESET:
|
||||||
if (arg2 > sizeof(resetinfo->next_app_data)) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
(void)memset((void *)resetinfo, 0, sizeof(*resetinfo));
|
|
||||||
resetinfo->type = userreset->type;
|
|
||||||
memcpy((void *)resetinfo->app_digest, userreset->app_digest,
|
|
||||||
32);
|
|
||||||
memcpy((void *)resetinfo->next_app_data,
|
|
||||||
userreset->next_app_data, arg2);
|
|
||||||
*system_reset = 1;
|
*system_reset = 1;
|
||||||
|
|
||||||
// Should not be reached.
|
|
||||||
assert(1 == 2);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TK1_SYSCALL_ALLOC_AREA:
|
|
||||||
if (storage_allocate_area(&part_table_storage) < 0) {
|
|
||||||
debug_puts("couldn't allocate storage area\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
case TK1_SYSCALL_DEALLOC_AREA:
|
case TK1_SYSCALL_SET_LED:
|
||||||
if (storage_deallocate_area(&part_table_storage) < 0) {
|
led_set(arg1);
|
||||||
debug_puts("couldn't deallocate storage area\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
case TK1_SYSCALL_WRITE_DATA:
|
|
||||||
if (storage_write_data(&part_table_storage.table, arg1,
|
|
||||||
(uint8_t *)arg2, arg3) < 0) {
|
|
||||||
debug_puts("couldn't write storage area\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
case TK1_SYSCALL_READ_DATA:
|
|
||||||
if (storage_read_data(&part_table_storage.table, arg1,
|
|
||||||
(uint8_t *)arg2, arg3) < 0) {
|
|
||||||
debug_puts("couldn't read storage area\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
case TK1_SYSCALL_GET_VIDPID:
|
case TK1_SYSCALL_GET_VIDPID:
|
||||||
// UDI is 2 words: VID/PID & serial. Return just the
|
// UDI is 2 words: VID/PID & serial. Return just the
|
||||||
// first word. Serial is kept secret to the device
|
// first word. Serial is kept secret to the device
|
||||||
// app.
|
// app.
|
||||||
return udi[0];
|
return udi[0];
|
||||||
|
|
||||||
case TK1_SYSCALL_PRELOAD_DELETE:
|
|
||||||
return preload_delete(&part_table_storage, 1);
|
|
||||||
|
|
||||||
case TK1_SYSCALL_PRELOAD_STORE:
|
|
||||||
// arg1 offset
|
|
||||||
// arg2 data
|
|
||||||
// arg3 size
|
|
||||||
// always using slot 1
|
|
||||||
return preload_store(&part_table_storage.table, arg1,
|
|
||||||
(uint8_t *)arg2, arg3, 1);
|
|
||||||
|
|
||||||
case TK1_SYSCALL_PRELOAD_STORE_FIN:
|
|
||||||
// arg1 app_size
|
|
||||||
// arg2 app_digest
|
|
||||||
// arg3 app_signature
|
|
||||||
// always using slot 1
|
|
||||||
return preload_store_finalize(&part_table_storage, arg1,
|
|
||||||
(uint8_t *)arg2, (uint8_t *)arg3,
|
|
||||||
1);
|
|
||||||
|
|
||||||
case TK1_SYSCALL_PRELOAD_GET_DIGSIG:
|
|
||||||
return preload_get_digsig(&part_table_storage.table,
|
|
||||||
(uint8_t *)arg1, (uint8_t *)arg2, 1);
|
|
||||||
|
|
||||||
case TK1_SYSCALL_STATUS:
|
|
||||||
return part_get_status();
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
assert(1 == 2);
|
assert(1 == 2);
|
||||||
}
|
}
|
||||||
|
@ -6,18 +6,8 @@
|
|||||||
|
|
||||||
enum syscall_num {
|
enum syscall_num {
|
||||||
TK1_SYSCALL_RESET = 1,
|
TK1_SYSCALL_RESET = 1,
|
||||||
TK1_SYSCALL_ALLOC_AREA = 2,
|
TK1_SYSCALL_SET_LED = 10,
|
||||||
TK1_SYSCALL_DEALLOC_AREA = 3,
|
TK1_SYSCALL_GET_VIDPID = 12,
|
||||||
TK1_SYSCALL_WRITE_DATA = 4,
|
|
||||||
TK1_SYSCALL_READ_DATA = 5,
|
|
||||||
TK1_SYSCALL_ERASE_DATA = 6,
|
|
||||||
TK1_SYSCALL_GET_VIDPID = 7,
|
|
||||||
TK1_SYSCALL_PRELOAD_STORE = 8,
|
|
||||||
TK1_SYSCALL_PRELOAD_STORE_FIN = 9,
|
|
||||||
TK1_SYSCALL_PRELOAD_DELETE = 10,
|
|
||||||
TK1_SYSCALL_PRELOAD_GET_DIGSIG = 11,
|
|
||||||
TK1_SYSCALL_REG_MGMT = 12,
|
|
||||||
TK1_SYSCALL_STATUS = 13,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -17,7 +17,7 @@ INCLUDE=include
|
|||||||
# either of them. You don't need to recompile tkey-libs.
|
# either of them. You don't need to recompile tkey-libs.
|
||||||
|
|
||||||
CFLAGS = -target riscv32-unknown-none-elf -march=rv32iczmmul -mabi=ilp32 \
|
CFLAGS = -target riscv32-unknown-none-elf -march=rv32iczmmul -mabi=ilp32 \
|
||||||
-mcmodel=medany -static -std=gnu99 -Os -ffast-math -fno-common \
|
-mcmodel=medany -static -std=gnu99 -O2 -ffast-math -fno-common \
|
||||||
-fno-builtin-printf -fno-builtin-putchar -nostdlib -mno-relax -flto \
|
-fno-builtin-printf -fno-builtin-putchar -nostdlib -mno-relax -flto \
|
||||||
-Wall -Werror=implicit-function-declaration \
|
-Wall -Werror=implicit-function-declaration \
|
||||||
-I $(INCLUDE) -I .
|
-I $(INCLUDE) -I .
|
||||||
|
@ -32,6 +32,7 @@ The Castor TKey hardware supports more USB endpoints:
|
|||||||
|
|
||||||
- CDC - the same thing as older versions.
|
- CDC - the same thing as older versions.
|
||||||
- FIDO security token, for FIDO-like apps.
|
- FIDO security token, for FIDO-like apps.
|
||||||
|
- CCID, smart card interface.
|
||||||
- DEBUG, a HID debug port.
|
- DEBUG, a HID debug port.
|
||||||
|
|
||||||
The communication is still over a single UART. To differ between the
|
The communication is still over a single UART. To differ between the
|
||||||
|
@ -10,20 +10,22 @@
|
|||||||
// I/O endpoints. Keep it as bits possible to use in a bitmask in
|
// I/O endpoints. Keep it as bits possible to use in a bitmask in
|
||||||
// readselect().
|
// readselect().
|
||||||
//
|
//
|
||||||
// Note that the DEBUG, CDC, and FIDO should be kept the same on
|
// Note that the values for IO_CH552, IO_CDC, IO_FIDO, IO_CCID and IO_DEBUG
|
||||||
// the CH552 side.
|
// should be kept the same in the code for the CH552 side.
|
||||||
enum ioend {
|
enum ioend {
|
||||||
IO_NONE = 0x00, // No endpoint
|
IO_NONE = 0x00, // No endpoint
|
||||||
IO_UART = 0x01, // Only destination, raw UART access
|
IO_UART = 0x01, // Only destination, raw UART access
|
||||||
IO_QEMU = 0x02, // Only destination, QEMU debug port
|
IO_QEMU = 0x02, // Only destination, QEMU debug port
|
||||||
IO_CH552 = 0x10, // Internal CH552 control port
|
IO_CH552 = 0x04, // Internal CH552 control port
|
||||||
IO_DEBUG = 0x20, // HID debug port
|
IO_CDC = 0x08, // CDC "serial" port
|
||||||
IO_CDC = 0x40, // CDC "serial port"
|
IO_FIDO = 0x10, // FIDO security token port
|
||||||
IO_FIDO = 0x80, // FIDO security token port
|
IO_CCID = 0x20, // CCID "smart card" port
|
||||||
|
IO_DEBUG = 0x40, // Debug port over USB HID
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ch552cmd {
|
enum ch552cmd {
|
||||||
SET_ENDPOINTS = 0x01, // Config USB endpoints on the CH552
|
SET_ENDPOINTS = 0x01, // Config USB endpoints on the CH552
|
||||||
|
CH552_CMD_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
void write(enum ioend dest, const uint8_t *buf, size_t nbytes);
|
void write(enum ioend dest, const uint8_t *buf, size_t nbytes);
|
||||||
|
@ -74,15 +74,20 @@ static void write_with_header(enum ioend dest, const uint8_t *buf,
|
|||||||
// write blockingly writes nbytes bytes of data from buf to dest which
|
// write blockingly writes nbytes bytes of data from buf to dest which
|
||||||
// is either:
|
// is either:
|
||||||
//
|
//
|
||||||
|
// - IO_UART: Low-level UART access, no USB Mode Header added.
|
||||||
|
//
|
||||||
// - IO_QEMU: QEMU debug port
|
// - IO_QEMU: QEMU debug port
|
||||||
//
|
//
|
||||||
// - IO_UART: Low-level UART access, no USB Mode Header added.
|
// - IO_CH552: Internal communication between the FPGA and the
|
||||||
|
// CH552, with header.
|
||||||
//
|
//
|
||||||
// - IO_CDC: Through the UART for the CDC endpoint, with header.
|
// - IO_CDC: Through the UART for the CDC endpoint, with header.
|
||||||
//
|
//
|
||||||
// - IO_FIDO: Through the UART for the FIDO endpoint, with header.
|
// - IO_FIDO: Through the UART for the FIDO endpoint, with header.
|
||||||
//
|
//
|
||||||
// - IO_DEBUG: Through the UART for the DEBUG HID endpoint, with
|
// - IO_CCID: Through the UART for the CCID endpoint, with header.
|
||||||
|
//
|
||||||
|
// - IO_DEBUG: Through the UART for the DEBUG endpoint (USB HID), with
|
||||||
// header.
|
// header.
|
||||||
void write(enum ioend dest, const uint8_t *buf, size_t nbytes)
|
void write(enum ioend dest, const uint8_t *buf, size_t nbytes)
|
||||||
{
|
{
|
||||||
@ -203,9 +208,11 @@ static int discard(size_t nbytes)
|
|||||||
//
|
//
|
||||||
// Only endpoints available for read are:
|
// Only endpoints available for read are:
|
||||||
//
|
//
|
||||||
// - IO_DEBUG
|
// - IO_CH552
|
||||||
// - IO_CDC
|
// - IO_CDC
|
||||||
// - IO_FIDO
|
// - IO_FIDO
|
||||||
|
// - IO_CCID
|
||||||
|
// - IO_DEBUG
|
||||||
//
|
//
|
||||||
// If you need blocking low-level UART reads, use uart_read() instead.
|
// If you need blocking low-level UART reads, use uart_read() instead.
|
||||||
//
|
//
|
||||||
@ -352,7 +359,8 @@ void hexdump(enum ioend dest, void *buf, int len)
|
|||||||
// Configure USB endpoints that should be enabled/disabled
|
// Configure USB endpoints that should be enabled/disabled
|
||||||
//
|
//
|
||||||
// Allowed options are:
|
// Allowed options are:
|
||||||
// - IO_FIDO
|
// - IO_FIDO (can't be used used together with IO_CCID)
|
||||||
|
// - IO_CCID (can't be used used together with IO_FIDO)
|
||||||
// - IO_DEBUG
|
// - IO_DEBUG
|
||||||
//
|
//
|
||||||
// The following are always enabled:
|
// The following are always enabled:
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
# b2s
|
|
||||||
|
|
||||||
The firmware included a BLAKE2s digest of the expected device app in
|
|
||||||
the first app slot. The firmware refuses to start the app if the
|
|
||||||
computed digest differs from the constant.
|
|
||||||
|
|
||||||
To simplify computing the digest, use this tool with the `-c` flag for
|
|
||||||
including the digest in a C program:
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
`go build`
|
|
||||||
|
|
||||||
## Running
|
|
||||||
|
|
||||||
```
|
|
||||||
./b2s -m b2s -c
|
|
||||||
// BLAKE2s digest of b2s
|
|
||||||
uint8_t digest[32] = {
|
|
||||||
0x17, 0x36, 0xe9, 0x4e, 0xeb, 0x1b, 0xa2, 0x30, 0x89, 0xa9, 0xaa, 0xe, 0xf2, 0x6f, 0x35, 0xb2, 0xa9, 0x89, 0xac, 0x64, 0x63, 0xde, 0x38, 0x60, 0x47, 0x40, 0x91, 0x4e, 0xd7, 0x72, 0xa0, 0x58,
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
To print the digest in a more user friendly way, leave out the `-c`:
|
|
||||||
|
|
||||||
```
|
|
||||||
./b2s -m b2s
|
|
||||||
1736e94eeb1ba23089a9aa0ef26f35b2a989ac6463de38604740914ed772a058 b2s
|
|
||||||
```
|
|
@ -1,59 +0,0 @@
|
|||||||
// SPDX-FileCopyrightText: 2025 Tillitis AB <tillitis.se>
|
|
||||||
// SPDX-License-Identifier: BSD-2-Clause
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/blake2s"
|
|
||||||
)
|
|
||||||
|
|
||||||
func usage() {
|
|
||||||
fmt.Printf("Usage: %s -m filename [-c]\n", os.Args[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
func printCDigest(digest [blake2s.Size]byte, fileName string) {
|
|
||||||
fmt.Printf("// BLAKE2s digest of %v\n", fileName)
|
|
||||||
fmt.Printf("const uint8_t digest[32] = {\n")
|
|
||||||
|
|
||||||
for _, n := range digest {
|
|
||||||
fmt.Printf("0x%x, ", n)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Printf("\n}; \n")
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var messageFile string
|
|
||||||
var forC bool
|
|
||||||
|
|
||||||
flag.StringVar(&messageFile, "m", "", "Specify file containing message.")
|
|
||||||
flag.BoolVar(&forC, "c", false, "Print digest for inclusion in C program.")
|
|
||||||
|
|
||||||
flag.Usage = usage
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if messageFile == "" {
|
|
||||||
usage()
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
message, err := os.ReadFile(messageFile)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("%v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
digest := blake2s.Sum256(message)
|
|
||||||
|
|
||||||
if forC {
|
|
||||||
printCDigest(digest, messageFile)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("%x %s\n", digest, messageFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Exit(0)
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
module b2s
|
|
||||||
|
|
||||||
go 1.23.0
|
|
||||||
|
|
||||||
toolchain go1.23.7
|
|
||||||
|
|
||||||
require golang.org/x/crypto v0.36.0
|
|
||||||
|
|
||||||
require golang.org/x/sys v0.31.0 // indirect
|
|
@ -1,4 +0,0 @@
|
|||||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
|
||||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
|
||||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
|
||||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
|
@ -1,28 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
|
|
||||||
FLASH_SIZE_BYTES = 1 * 1024 * 1024
|
|
||||||
PRELOADED_APP_0_START = 0x30000
|
|
||||||
|
|
||||||
|
|
||||||
def run(output_path, preloaded_app_0_path):
|
|
||||||
with (
|
|
||||||
open(output_path, "wb") as output_file,
|
|
||||||
open(preloaded_app_0_path, "rb") as preloaded_app_0_file,
|
|
||||||
):
|
|
||||||
content = bytearray(b"\xFF") * FLASH_SIZE_BYTES
|
|
||||||
preloaded_app = preloaded_app_0_file.read()
|
|
||||||
content[
|
|
||||||
PRELOADED_APP_0_START : PRELOADED_APP_0_START + len(preloaded_app)
|
|
||||||
] = preloaded_app
|
|
||||||
output_file.write(content)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
arg_parser = argparse.ArgumentParser()
|
|
||||||
arg_parser.add_argument("OUTPUT_PATH")
|
|
||||||
arg_parser.add_argument("PRELOADED_APP_0_PATH")
|
|
||||||
args = arg_parser.parse_args()
|
|
||||||
|
|
||||||
run(args.OUTPUT_PATH, args.PRELOADED_APP_0_PATH)
|
|
Binary file not shown.
@ -1,34 +0,0 @@
|
|||||||
#!/bin/bash -e
|
|
||||||
|
|
||||||
if [ $# != 2 ]
|
|
||||||
then
|
|
||||||
echo "Usage: $0 slot_num app_file"
|
|
||||||
echo ""
|
|
||||||
echo "Where slot_num is 0 or 1."
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
|
|
||||||
SLOT_NUM="$1"
|
|
||||||
APP="$2"
|
|
||||||
|
|
||||||
if [ "$SLOT_NUM" = "0" ]; then
|
|
||||||
START_ADDRESS=0x30000
|
|
||||||
elif [ "$SLOT_NUM" = "1" ]; then
|
|
||||||
START_ADDRESS=0x50000
|
|
||||||
else
|
|
||||||
echo "Invalid slot_num"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "WARNING: Will install default partition table."
|
|
||||||
read -p "Press CTRL-C to abort. Press key to continue." -n1 -s
|
|
||||||
|
|
||||||
# Write both copies of the partition table
|
|
||||||
tillitis-iceprog -o 128k default_partition.bin
|
|
||||||
tillitis-iceprog -o 0xf0000 default_partition.bin
|
|
||||||
|
|
||||||
# Erase existing pre loaded app
|
|
||||||
tillitis-iceprog -o "$START_ADDRESS" -e 128k
|
|
||||||
|
|
||||||
# Write pre loaded app
|
|
||||||
tillitis-iceprog -o "$START_ADDRESS" "$APP"
|
|
@ -1,7 +0,0 @@
|
|||||||
module partition_table
|
|
||||||
|
|
||||||
go 1.23.0
|
|
||||||
|
|
||||||
require golang.org/x/crypto v0.36.0
|
|
||||||
|
|
||||||
require golang.org/x/sys v0.31.0 // indirect
|
|
@ -1,4 +0,0 @@
|
|||||||
golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34=
|
|
||||||
golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc=
|
|
||||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
|
||||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
|
@ -1,172 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/binary"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/blake2s"
|
|
||||||
)
|
|
||||||
|
|
||||||
type PreLoadedAppData struct {
|
|
||||||
Size uint32
|
|
||||||
Digest [32]uint8
|
|
||||||
Signature [64]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type Auth struct {
|
|
||||||
Nonce [16]uint8
|
|
||||||
AuthDigest [16]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type AppStorage struct {
|
|
||||||
Status uint8
|
|
||||||
Auth Auth
|
|
||||||
}
|
|
||||||
|
|
||||||
type PartTable struct {
|
|
||||||
Version uint8
|
|
||||||
PreLoadedAppData [2]PreLoadedAppData
|
|
||||||
AppStorage [4]AppStorage
|
|
||||||
}
|
|
||||||
|
|
||||||
type PartTableStorage struct {
|
|
||||||
PartTable PartTable
|
|
||||||
Digest [16]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
type Flash struct {
|
|
||||||
Bitstream [0x20000]uint8
|
|
||||||
PartitionTableStorage PartTableStorage
|
|
||||||
PartitionTablePadding [64*1024 - 349]uint8
|
|
||||||
PreLoadedApp0 [0x20000]uint8
|
|
||||||
PreLoadedApp1 [0x20000]uint8
|
|
||||||
AppStorage [4][0x20000]uint8
|
|
||||||
EndPadding [0x10000]uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
func readStruct[T PartTableStorage | Flash](filename string) T {
|
|
||||||
var s T
|
|
||||||
|
|
||||||
file, err := os.Open(filename)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := binary.Read(file, binary.LittleEndian, &s); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
sLen, err := file.Seek(0, io.SeekCurrent)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Fprintf(os.Stderr, "INFO: %T struct is %d byte long\n", *new(T), sLen)
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
func printPartTableStorageCondensed(storage PartTableStorage) {
|
|
||||||
fmt.Printf("Partition Table Storage\n")
|
|
||||||
fmt.Printf(" Partition Table\n")
|
|
||||||
fmt.Printf(" Header\n")
|
|
||||||
fmt.Printf(" Version : %d\n", storage.PartTable.Version)
|
|
||||||
|
|
||||||
for i, appData := range storage.PartTable.PreLoadedAppData {
|
|
||||||
fmt.Printf(" Preloaded App %d\n", i)
|
|
||||||
fmt.Printf(" Size : %d\n", appData.Size)
|
|
||||||
fmt.Printf(" Digest : %x\n", appData.Digest[:16])
|
|
||||||
fmt.Printf(" %x\n", appData.Digest[16:])
|
|
||||||
fmt.Printf(" Signature : %x\n", appData.Signature[:16])
|
|
||||||
fmt.Printf(" %x\n", appData.Signature[16:32])
|
|
||||||
fmt.Printf(" %x\n", appData.Signature[32:48])
|
|
||||||
fmt.Printf(" %x\n", appData.Signature[48:])
|
|
||||||
}
|
|
||||||
fmt.Printf(" Digest : %x\n", storage.Digest)
|
|
||||||
}
|
|
||||||
|
|
||||||
func calculateStorageDigest(data []byte) []byte {
|
|
||||||
key := [16]byte{}
|
|
||||||
|
|
||||||
hash, err := blake2s.New128(key[:])
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
hash.Write(data)
|
|
||||||
digest := hash.Sum([]byte{})
|
|
||||||
|
|
||||||
return digest
|
|
||||||
}
|
|
||||||
|
|
||||||
func generatePartTableStorage(outputFilename string, app0Filename string) {
|
|
||||||
storage := PartTableStorage{
|
|
||||||
PartTable: PartTable{
|
|
||||||
Version: 1,
|
|
||||||
PreLoadedAppData: [2]PreLoadedAppData{},
|
|
||||||
AppStorage: [4]AppStorage{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
if app0Filename != "" {
|
|
||||||
stat, err := os.Stat(app0Filename)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
storage.PartTable.PreLoadedAppData[0].Size = uint32(stat.Size())
|
|
||||||
}
|
|
||||||
|
|
||||||
buf := make([]byte, 4096, 4096)
|
|
||||||
len, err := binary.Encode(buf, binary.LittleEndian, storage.PartTable)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
fmt.Printf("buf - len: %d, data: %x\n", len, buf[:len])
|
|
||||||
|
|
||||||
digest := calculateStorageDigest(buf[:len])
|
|
||||||
copy(storage.Digest[:], digest)
|
|
||||||
fmt.Printf("digest: %x\n", digest)
|
|
||||||
|
|
||||||
storageFile, err := os.Create(outputFilename)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := binary.Write(storageFile, binary.LittleEndian, storage); err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
input := ""
|
|
||||||
output := ""
|
|
||||||
app0 := ""
|
|
||||||
flash := false
|
|
||||||
|
|
||||||
flag.StringVar(&input, "i", "", "Input binary dump file. Cannot be used with -o.")
|
|
||||||
flag.StringVar(&output, "o", "", "Output binary dump file. Cannot be used with -i.")
|
|
||||||
flag.StringVar(&app0, "app0", "", "Binary in pre loaded app slot 0. Can be used with -o.")
|
|
||||||
flag.BoolVar(&flash, "f", false, "Treat input file as a dump of the entire flash memory.")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if (input == "" && output == "") || (input != "" && output != "") {
|
|
||||||
flag.Usage()
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if input != "" {
|
|
||||||
var storage PartTableStorage
|
|
||||||
|
|
||||||
if flash {
|
|
||||||
storage = readStruct[Flash](input).PartitionTableStorage
|
|
||||||
} else {
|
|
||||||
storage = readStruct[PartTableStorage](input)
|
|
||||||
}
|
|
||||||
printPartTableStorageCondensed(storage)
|
|
||||||
} else if output != "" {
|
|
||||||
generatePartTableStorage(output, app0)
|
|
||||||
}
|
|
||||||
}
|
|
@ -57,6 +57,7 @@ if __name__ == "__main__":
|
|||||||
"CdcCtrlInterfaceDesc": "CDC-Ctrl",
|
"CdcCtrlInterfaceDesc": "CDC-Ctrl",
|
||||||
"CdcDataInterfaceDesc": "CDC-Data",
|
"CdcDataInterfaceDesc": "CDC-Data",
|
||||||
"FidoInterfaceDesc": "FIDO",
|
"FidoInterfaceDesc": "FIDO",
|
||||||
|
"CcidInterfaceDesc": "CCID",
|
||||||
"DebugInterfaceDesc": "DEBUG"
|
"DebugInterfaceDesc": "DEBUG"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,6 +94,13 @@ Header file for CH554 microcontrollers.
|
|||||||
#define USB_CDC_REQ_TYPE_SET_CONTROL_LINE_STATE 0x22
|
#define USB_CDC_REQ_TYPE_SET_CONTROL_LINE_STATE 0x22
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* USB CCID (Smart Card) class request code */
|
||||||
|
#ifndef USB_CCID_REQ_TYPE
|
||||||
|
#define USB_CCID_REQ_TYPE_ABORT 0x01
|
||||||
|
#define USB_CCID_REQ_TYPE_GET_CLOCK_FREQUENCIES 0x02
|
||||||
|
#define USB_CCID_REQ_TYPE_GET_DATA_RATES 0x03
|
||||||
|
#endif
|
||||||
|
|
||||||
/* USB request type for hub class request */
|
/* USB request type for hub class request */
|
||||||
#ifndef HUB_GET_HUB_DESCRIPTOR
|
#ifndef HUB_GET_HUB_DESCRIPTOR
|
||||||
#define HUB_CLEAR_HUB_FEATURE 0x20
|
#define HUB_CLEAR_HUB_FEATURE 0x20
|
||||||
@ -200,7 +207,8 @@ Header file for CH554 microcontrollers.
|
|||||||
#define USB_IDX_INTERFACE_CDC_CTRL_STR 0x04
|
#define USB_IDX_INTERFACE_CDC_CTRL_STR 0x04
|
||||||
#define USB_IDX_INTERFACE_CDC_DATA_STR 0x05
|
#define USB_IDX_INTERFACE_CDC_DATA_STR 0x05
|
||||||
#define USB_IDX_INTERFACE_FIDO_STR 0x06
|
#define USB_IDX_INTERFACE_FIDO_STR 0x06
|
||||||
#define USB_IDX_INTERFACE_DEBUG_STR 0x07
|
#define USB_IDX_INTERFACE_CCID_STR 0x07
|
||||||
|
#define USB_IDX_INTERFACE_DEBUG_STR 0x08
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef USB_DEVICE_ADDR
|
#ifndef USB_DEVICE_ADDR
|
||||||
|
@ -8,14 +8,16 @@ enum ioend {
|
|||||||
IO_NONE = 0x00, // No endpoint
|
IO_NONE = 0x00, // No endpoint
|
||||||
IO_UART = 0x01, // Only destination, raw UART access
|
IO_UART = 0x01, // Only destination, raw UART access
|
||||||
IO_QEMU = 0x02, // Only destination, QEMU debug port
|
IO_QEMU = 0x02, // Only destination, QEMU debug port
|
||||||
IO_CH552 = 0x10, // Internal CH552 control port
|
IO_CH552 = 0x04, // Internal CH552 control port
|
||||||
IO_DEBUG = 0x20, // HID debug port
|
IO_CDC = 0x08, // CDC "serial" port
|
||||||
IO_CDC = 0x40, // CDC "serial port"
|
IO_FIDO = 0x10, // FIDO security token port
|
||||||
IO_FIDO = 0x80, // FIDO security token port
|
IO_CCID = 0x20, // CCID "smart card" port
|
||||||
|
IO_DEBUG = 0x40, // Debug port over USB HID
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ch552cmd {
|
enum ch552cmd {
|
||||||
SET_ENDPOINTS = 0x01, // Config enabled/disabled USB endpoints on the CH552
|
SET_ENDPOINTS = 0x01, // Config USB endpoints on the CH552
|
||||||
|
CH552_CMD_MAX,
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -55,6 +55,12 @@ unsigned char FLASH FidoInterfaceDesc[] = { // "FIDO"
|
|||||||
'F', 0, 'I', 0, 'D', 0, 'O', 0,
|
'F', 0, 'I', 0, 'D', 0, 'O', 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
unsigned char FLASH CcidInterfaceDesc[] = { // "CCID"
|
||||||
|
10, // Length of this descriptor (in bytes)
|
||||||
|
0x03, // Descriptor type (String)
|
||||||
|
'C', 0, 'C', 0, 'I', 0, 'D', 0,
|
||||||
|
};
|
||||||
|
|
||||||
unsigned char FLASH DebugInterfaceDesc[] = { // "DEBUG"
|
unsigned char FLASH DebugInterfaceDesc[] = { // "DEBUG"
|
||||||
12, // Length of this descriptor (in bytes)
|
12, // Length of this descriptor (in bytes)
|
||||||
0x03, // Descriptor type (String)
|
0x03, // Descriptor type (String)
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user