Make initial public release

This commit is contained in:
Joachim Strömbergson 2022-09-19 08:51:11 +02:00 committed by Daniel Lublin
commit 715de60f4a
251 changed files with 881225 additions and 0 deletions

View file

@ -0,0 +1,105 @@
# Address map breakdown and scheme and data formats
## Introduction
We need to agree and define how we use the 32-bit address space. In a way that is:
1. Efficient for HW implementation (decoding logic)
2. Easy to implement in QEMU model
3. Natural, easy to understand and use in SW and develop for
4. Allow simple access separation, control based on security state (FW, APP)
This shared document (hopefully) allows us to accomplish these goals.
## Addressing and core access
In general, the cores only operates on 32-bit words. They expect to get 32-bits
when written to, and return 32-bits when read. The APIs are very simplistic and
does not support byte or half words operations (this would require additional
wires from the CPU core to signal which load-store operation it is trying to
perform - byte, half word, word.
The RISC-V architecture in contrast has byte addressable addresses. This means
that 0x00000000 .. 0x00000003 points to the four different bytes in word 0.
In order to reduce confusion we should:
- Skip the two LSBs in the address. Since the cores have 8 bit address space,
their address space as seen by the CPU is 10 bits, but will be 0x000, 0x004,
0x008, 0x00c etc. A core always sees an 8-bit address. It is the way the core
is hooked up in the application_fpga that controls which 8 bits those are (of
the 32-bit address the CPU requests).
- Always use 32-bit read or write instructions when accessing core registers.
That is lw, sw and use uint32_t as source or destination data type. Using lh,
lb will cause confusion.
Looking at the address format above I would suggest something like:
```
31st bit 0th bit
v v
0000 0000 0000 0000 0000 0000 0000 0000
- Bits [31 .. 30] (2 bits): Top level area prefix (0b00:ROM, 0b01:RAM, 0b10:reserved, 0b11:MMIO)
- Bits [29 .. 24] (6 bits): Core select. We want to support at least 16 cores
- Bits [23 .. 0] (24 bits): Memory/in-core address.
```
This leaves 24 bits for memory or in-core address. Actually, for memory it is
possible to use 30 bits, because core select is not needed.
The cores only have 8-bit addresses, but they are 32-bit word-aligned,
so they will occupy 10 LSBs (also see above).
Assigned top level prefixes:
```
ROM 0b00, 30-bit address
RAM 0b01, 30-bit address
reserved 0b10
MMIO 0b11, 6 bits for core select, 24 bits rest
```
Assigned core prefixes:
```
ROM 0x00
RAM 0x40
TRNG 0xc0 // cores from 0b11000000...
TIMER 0xc1
UDS 0xc2
UART 0xc3
TOUCH 0xc4
MTA1 0xff
```
Examples:
```
0xc3000004: NAME1 in UART
0xff000000: NAME0 in MTA1
0xff000008: VERSION in MTA1
```
## Endianness
Currently all cores handles words in Big Endian format. I need to ensure that
the cores are can handle little endian words. This will probably affect, force
update of which bits are used in status, configuration and control registers.
## Mirror effects
Since each core (and ROM, RAM) only use lower subset of the allocated 24 bit
address, contents will be mirrored modulo the size of the real address. For
example, the TRNG will appear every 10 bit address.
The reason for this is that there is no boundary checks on the address for
in-core addresses. This would require 24 bit comparators. This could be added
when we see that we have resources to do so. A later stage cleanup.
---

View file

@ -0,0 +1,246 @@
# NOTE: this document is outdated, an update is pending.
# MTA1-MKDF software
## Definitions
* Firmware -- software that is part of ROM, and is supplied via the
FPGA bit stream
* Secure Application (short: Application) -- software supplied by
the host machine, which is received, measured, and loaded by the
firmware.
## Types
The PicoRV32 is a 32-bit RISC-V system. All types are little-endian.
## Constraints
The application FPGA is a Lattice UP5K, with the following
specifications:
* 32KB x 4 SPRAM => 128KB for Application
* 4Kb x 30 EBR => 120Kb, PicoRV32 uses ~4 EBR internally => 13KB for
Firmware. We should probably aim for less; 8KB should be the
target.
## Introduction
The MTA1_MKDF has two modes of operation; firmware/loader mode and
application mode. The firmware mode has the responsibility of receive,
measure, and load the application.
The firmware and application uses a memory mapped IO for SoC
communication. The memory map resides at `0x9000_0000`. The
application has a constrained variant of the firmware memory map,
which is outlined below. E.g. UID and UDA are not readable, and the
`APP_{ADDR, SIZE}` are not writable for the application.
The MTA1_MKDF software communicates to the host via the `{RX,TX}_FIFO`
registers, using the framing protocol described in [Framing
Protocol](../framing_protocol/framing_protocol.md).
The firmware defines a protocol (command/response interface) on top of
the framing layer, which is used to bootstrap the application onto the
device.
On the framing layer, it's required that each frame the device
receives, a responding frame must be sent back to the host, in a
ping-pong manner.
Applications define a per-application protocol, which is the contract
between the host and the device.
## Firmware
The firware is part of FPGA bitstream (ROM), and is loaded at
`0x0000_1000`.
### Reset
The PicoRV32 executes `_start` from `crt0.S` `.text` at `0x0000_1000`,
which initializes the stack, `.data`, and `.bss` at
`0x8000_0000`. When the initialization is finished, the firmware waits
for incoming commands from the host, by busy-polling the
`RX_FIFO_{AVAILABLE,DATA}`registers. When a complete command is read,
the firmware executes the command.
### Loading an application
The purpose of the firmware is to bootstrapping an application.
1. The host sends a raw binary, targeted to be loaded at
`0x8000_0000` in the device. The host starts off by sending the
binary size using the `FW_CMD_LOAD_APP_SIZE` command.
2. The firmware executes `FW_CMD_LOAD_APP_SIZE` command, which
stores the application size into `APP_SIZE`, and sets `APP_ADDR`
to zero. A `FW_RSP_LOAD_APP_SIZE` reponse is sent back to the
host, with the status of the action (ok/fail).
3. If the the host receive a sucessful command, it will send
multiple `FW_CMD_LOAD_APP_DATA` commands, containing the full
application.
4. For each received `FW_CMD_LOAD_APP_DATA` commands, the firmware
measures (XXX define how blake2s is used) the data, and places it
into `0x8000_0000`. The firmware response with
`FW_RSP_LOAD_APP_DATA` response to the host for each received
block.
5. When the final block of the application image is received,
`0x8000_0000` is written to `APP_ADDR`. The `CDI` is computed by
used the `UDS` and measurement from the application, and placed
in the `CDI` register. The final `FW_RSP_LOAD_APP_DATA` response
is sent to the host, completing the loading.
NOTE: The firmware uses SPRAM for data and stack. We need to make sure
that the application image does not overwrite the firmware's running
state. The application should probably do a similar relocation for
stack/data at reset, as the firmware does. Further; the firmware need
to check application image is sane. The shared firmware data area
(e.g. `.data` and the stack must be cleared prior launching the
application.
### Starting an application
Starting an application includes the "switch to application mode"
step, which is done by writing to the `SWITCH_APP` regiester. The
switch from firmware mode to application mode is a mode switch, and
context switch. Enter application mode, means the the MMIO region is
restricted; E.g. some registers are removed (`UDS`), and some are
switched from read/write to read-only. This is outlined in the memory
map below.
There is no other means of getting back from application mode to
firmware mode, than resetting/power cycling the device.
Prerequisites: `APP_SIZE` and `APP_ADDR` has to be non-zero.
1. The host sends `FW_CMD_RUN_APP` to the device.
2. The firmware respons with `FW_RSP_RUN_APP`
3. The firmware writes a non-zero to `SWITCH_APP`, and executes
```
// a0 = 0x9000_0000 + 0x420 (APP_ADDR address)
lw a0,1056(a0)
jalr x0,0(a0)
```
4. The device is now in application mode, and executes the code from
`0x8000_0000`.
### Protocol definition
Available commands/reponses:
#### `FW_{CMD,RSP}_LOAD_APP_SIZE`
#### `FW_{CMD,RSP}_LOAD_APP_DATA`
#### `FW_{CMD,RSP}_RUN_APP`
#### `FW_{CMD,RSP}_NAME_VERSION`
#### `FW_{CMD,RSP}_UID`
#### `FW_{CMD,RSP}_TRNG_DATA`
#### `FW_{CMD,RSP}_TRNG_STATUS`
#### `FW_{CMD,RSP}_VERIFY_DEVICE`
Verification that the device is an authentic Mullvad
device. Implemented using challenge/response.
#### `FW_{CMD,RSP}_GET_APPLICATION_DIGEST`
This command returns the un-keyed hash digest for the application that
was loaded. It allows the host to verify that the application was
correctly loaded. This means that the CDI calculated will be correct
given that the UDS has not been modified.
XXX Should we think a bit more about versioning/possiblity to extend?
Is 1B enough for a command/response range?
#### Get the name and version of the device
```
host ->
u8 CMD[1 + 1];
CMD[0].len = 1 // command frame format
CMD[1] = 0x01 // FW_CMD_NAME_VERSION
host <-
u8 RSP[1 + 32]
RSP[0].len = 33 // command frame format
RSP[1] = 0x02 // FW_RSP_NAME_VERSION
RSP[2..6] = NAME0
RSP[6..10] = NAME1
RSP[10..14] = VERSION
RSP[14..] = 0
```
#### Load an application
```
host ->
u8 CMD[1 + 32];
CMD[0].len = 5 // command frame format
CMD[1] = 0x03 // FW_CMD_LOAD_APP_SIZE
CMD[2..6] = APP_SIZE
CMD[6..] = 0
host <-
u8 RSP[1 + 4];
RSP[0].len = 5 // command frame format
RSP[1] = 0x04 // FW_RSP_LOAD_APP_SIZE
RSP[2] = STATUS
RSP[3..] = 0
repeat ceil(APP_SIZE / 63) times:
host ->
u8 CMD[1 + 64];
CMD[0].len = 65 // command frame format
CMD[1] = 0x05 // FW_CMD_LOAD_APP_DATA
CMD[2..] = APP_DATA (pad with zeros)
host <-
u8 RSP[1 + 4]
RSP[0].len = 5 // command frame format
RSP[1] = 0x06 // FW_RSP_LOAD_APP_DATA
RSP[2] = STATUS
RSP[3..] = 0
```
### Memory map
The memory map exposes SoC functionality to the software, when in
firmware mode (privileged mode) It is s set of memory mapped
registers, starting at base address `0x9000_0000`.
| *name* | *r/w* | *offset* | *size* | *type* | *content* | *description* |
|--------------------|-------|----------|--------|---------|-----------|---------------------------------------------------------|
| UDS[^1] | r | 0x0 | 32B | u8[32] | | Unique Device Secret key. |
| UDA | r | 0x20 | 16B | u8[16] | | Unique Device Authentication key. |
| SWITCH_APP | w | 0x30 | 1B | u8 | | Switch to application mode. Write non-zero to trigger. |
| XXX 460 bytes hole | | | | | | |
| UDI | r | 0x200 | 8B | u64 | | Unique Device ID (UDI). |
| NAME0 | r | 0x208 | 4B | char[4] | "mta1" | |
| NAME1 | r | 0x20c | 4B | char[4] | "mkdf" | |
| VERSION | r | 0x210 | 4B | u32 | 1 | Current version. |
| RX_FIFO_AVAILABLE | r | 0x214 | 1B | u8 | | Non-zero if a valid byte can be read from RX_FIFO_DATA. |
| RX_FIFO_DATA | r | 0x215 | 1B | u8 | | FIFO Rx data. |
| TX_FIFO_AVAILABLE | r | 0x216 | 1B | u8 | | Non-zero if a valid byte can be written to TX_FIFO_DATA |
| TX_FIFO_DATA | w | 0x217 | 1B | u8 | | FIFO Tx data. |
| LED | r/w | 0x218 | 4B | u32 | | LED |
| COUNTER | r | 0x21c | 4B | u32 | | Counter |
| TRNG_STATUS | r | 0x220 | 4B | u32 | | data_ready/error |
| TRNG_DATA | r | 0x224 | 4B | u32 | | TRNG data |
| XXX 472 bytes hole | | | | | | |
| CDI | r/w | 0x400 | 32B | u8[32] | | Compound Device Identifier (CDI). UDS+measurement... |
| APP_ADDR | r/w | 0x420 | 4B | u32 | | Application address (0x8000_0000) |
| APP_SIZE | r/w | 0x424 | 4B | u32 | | Application size |
[^1]: The UDS can only be read *once* per power-cycle.
## Application
### Memory map
See the [Memory model](./memory_model.md) for information about the
memory map and how access to memory areas work.

View file

@ -0,0 +1,233 @@
# System Description
## Purpose and Revision
The purpose of this document is to provide a description of the
mta1_mkdf. What it is, what is supposed to be used for, by whom, where
and possible use cases. The document also provides a functional level
description of features and components of the mta1_mkdf.
Finally, the document acts as a requirement description. For the
requirements, the document follows
[RFC2119](https://datatracker.ietf.org/doc/html/rfc2119) to indicate
requirement levels.
The described functionality and requirements applies
to version one (v1) of the mta1_mkdf.
The intended users of this document are:
- Implementors of the mta1_mkdf hardware, firmware and SDKs
- Developers of secure applications for the mta1_mkdf
- Technically skilled third parties that wants to understand the
mta1_mkdf
## Introduction
The mta1_mkdf is USB-connected, RISC-V based application platform. The
purpose of the mta1_mkdf is to provide a secure application environment
for applications that provides some security functionality needed by the
user. Some examples of such security functionality are:
- TOTP token generators
- Signing oracles
- SSH login dongles
### Measured Based Security
The key, unique feature of the mta1_mkdf is that it measures the secure
application when the application is being loaded onto the device. The
measurement, combined with a Unique Device Secret (UDS) is used to
derive secrets for the application.
The consequence of this is that if the application is altered, the keys
derived will also change. Conversely, if the keys derived are the same as
last time the application was loaded onto the same device, the
application can be trusted not to have been altered.
Note that since the UDS is per-device unique, the same application
loaded onto another mta1_mkdf device will cause a different set of keys
to be derived. This ties keys to a specific device.
The derivation can also be combined with a User Supplied Secret
(USS). This means that keys derived are both based on something the user
has - the specific device, and something the user knows (the USS). And
the keys are protected and can be trusted because of the measurement
being used in the derivation.
### Assets
The mta1_mkdf store and use the following assets internally:
- UDS - Unique Device Secret. Provisioned and stored during
device manufacturing. Never to be replaced during the life time of
a given device. Used to derive application secrets. Must never leave
the device. Mullvad must NOT store a copy of the UDS.
- UDI - Unique Device ID. Provisioned and stored during
device manufacturing. Never to be replaced or altered during the life
time of a given device. May be copied, extracted, read from the device.
- UDA - Unique Device Authentication Secret. Provisioned and stored during
device manufacturing. Never to be replaced during the life time of
a given device. Used to authenticate a specific device. Must never
leave the device. Mullvad MUST have a copy of the UDA.
Additionally the following asset could be provided from the host:
- USS - User Supplied Secret. Provisioned by the application. May
possibly be replaced many times. Supplied from the host to the
device. Should not be revealed to a third party.
### Subsystems and Components
The mta1_mkdf as a project, system and secure application platform
consists of a number of subsystems and components, modules, support
libraries etc. Roughly these can be divided into:
- mta1_mkdf boards. PCB designs for development and general usage
- interface_fpga. FPGA design with cores
- application_fpga. FPGA design with cores including CPU and memory
- application_fpga FW. The base software running on the CPU to boot, load
applications, derive keys etc
- application_fpga secure application. One or more applications loaded
into the application_fpga to provide some functionality to the user of
the host
- host side application loader. Software that talks to the FW in the
application_fpga to load a secure application
- host side boot, management. Support software to boot, authenticate the
mta1_mkdf board connected to a host
- host side secure application. Software that communicates with the
secure application running in the application_fpga as needed to solve
a security objective
- application_fpga FW SDK. Tools, libraries, documentation and examples
to support development of the application_fpga firmware
- secure application SDK. Tools, libraries, documentation and examples
to support development of the secure applications to be loaded onto
the application_fpga
- host side secure application SDK. Tools, libraries, documentation and
examples to support development of the host applications
## Application FPGA Hardware Functionality
The Application FPGA hardware should provide the following:
1. Fixed information
- Unique Device ID (UID)
- 64 bits
- Readable via API before application start
- Generated and stored by Mullvad
- Unique Device Authentication key (UDA)
- At least 128 bits number
- Readable by FW before application start
- Generated and stored by Mullvad
- Unique Device Secret (UDS)
- 256 bits
- Readable by HW before application start
- Generated but NOT stored by Mullvad
- NAME
- 64 bits. ASCII string. "mta1_mkdf"
- Readable via API before application start
- Set by Mullvad as part of FPGA design
- VERSION: version
- 32 bits. 32 bit data, for example 1
- Readable via API before application start
- Set by Mullvad as part of FPGA design
2. Communication
- Rx-FIFO with status (data_available)
- 8 bit data in RX_FIFO_DATA address
- Byte received status bit in RX_FIFO_AVAILABLE address
- Readable by FW and application
- Tx-FIFO with capacity (fifo_ready)
- 8 bit data in TX_FIFO_DATA address
- Ready to store byte status bit in TX_FIFO_READY address
- Status readable by FW and application
- Data writable by FW and application
3. I/O
- LED (RGB)
- Status and control in LED address
- Readable and writable by FW and application
4. Counter
- One general purpose counter
- Prescaler (for counting cycles and seconds)
- Start value, alternatively reset
- Saturating max, alternatively stop at zero
- Readable and writable by FW and application
5. TRNG
- ROSC based internal entropy source
- Von Neumann decorrelation
- Simple self-testing ability
- 32 bit data
- Status (data_ready, error)
- Readable by FW and application
6. Introspection
- Address och size of loaded application
- Readable by FW and application
## Application FPGA Firmware Functionality
The firmware in the application should provide the following
functionality:
- Read access to fixed values:
- application_fpga name and version strings
- Unique Device ID (UID)
- Read and write to test register used for debugging
- Respond to challenge/response based device authentication commands
- Receive and store a 32 byte User Supplied Secret (USS)
- Receive, store and measure a secure application
- Derive Application Master Secret (AMS) given measurement, UDS and USS
- Provide hashing using Blake2s
- Start a loaded application. This includes locking down access to UDS,
UDA etc
## References
More detailed information about the software running on the device
(referred to firmware, SDK, and secure application), can be found in
the [software document](software.md).
## Work in Progress
TODOs and random notes, questions to be worked into the document. Or be
scratched.
- Possible technical solution - Could we reuse the button as a physical
presence detect when injecting a bitstream from the interface_fpga
to the application_fpga? Alternative have a strap, which would
require opening the stick. The stick is the sold with nail polish to
reseal it.
- Ideas - mitigating mechanisms for host bases threats
- Push button
- User Supplied Secret (USS)
- Open Questions to be investigated, handled
- Terminology - naming things
- How to create trust in the SDKs