Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 178 additions & 28 deletions rpifwcrypto/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,37 +16,187 @@ Although this service can be used via raw vcmailbox commands the
recommended API is either the command line rpi-fw-crypto application
or the librpifwcrypto.so shared library.

**Build Instructions**
Install prerequisites with "sudo apt install cmake libgnutls28-dev"" - you need at least version 3.10.

- *mkdir build*
- *cd build*
- *cmake ..*
- *make*
- *sudo make install*

**Usage**

* rpi-fw-crypto -h (Displays usage instructions for all operations)
* rpi-fw-crypto get-num-otp-keys (Returns the number of OTP key slots)
* rpi-fw-crypto sign --in message.bin --key-id 1 --alg ec --out sig.bin (Signs message.bin with the device unique OTP key (id 1))
* rpi-fw-crypto get-key-status 1 (Gets the status of key-id 1)
* rpi-fw-crypto set-key-status 1 LOCKED (Blocks the raw OTP read API on this key until the device is rebooted)
* rpi-fw-crypto get-key-usage 1 (Gets the usage of key-id 1)
* rpi-fw-crypto set-key-usage 1 0x1 (Sets the usage of key-id 1 to RPI_CONNECT in OTP)
* rpi-fw-crypto hmac --in message.bin --key-id 1 --out hmac.bin (Generates the SHA256 HMAC of message.bin and OTP key id 1)
* rpi-fw-crypto pubkey --key-id 1 --out device-pub.der (Derives and retrieves the corresponding public key for the specified device private ECDSA P256 key)
* rpi-fw-crypto privkey --key-id 1 --out device-priv.der (Retrieves the device private key - in DER form - will error if key status is locked)
* rpi-fw-crypto genkey --key-id 1 --alg ec (Generates an ECDSA P256 key-pair and writes the private key to the OTP)

** Notes **
## Build Instructions

Install prerequisites with `sudo apt install cmake libgnutls28-dev` - you need at least version 3.10.

```
mkdir build
cd build
cmake ..
make
sudo make install
```

If overwriting the system (APT) installed `rpi-fw-crypto` set the CMake
install prefix to `/usr`. Otherwise, there will be a library mismatch because
the default install prefix is `/usr/local`:

```
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
```

## Usage

Display usage instructions for all operations:

```
rpi-fw-crypto -h
```

Return the number of OTP key slots:

```
rpi-fw-crypto get-num-otp-keys
```

Sign message.bin with the device unique OTP key (id 1):

```
rpi-fw-crypto sign --in message.bin --key-id 1 --alg ec --out sig.bin
```

Get the status of key-id 1:

```
rpi-fw-crypto get-key-status 1
```

Block the raw OTP read API on key-id 1 until the device is rebooted:

```
rpi-fw-crypto set-key-status 1 LOCKED
```

Get the usage of key-id 1:

```
rpi-fw-crypto get-key-usage 1
```

Set the usage of key-id 1 to RPI_CONNECT in OTP:

```
rpi-fw-crypto set-key-usage 1 0x1
```

The key usage is stored in OTP and records what the key is used for
(`RPI_FW_CRYPTO_KEY_USAGE` in `rpifwcrypto.h`):

| Value | Usage | Description |
|-------|-------|-------------|
| 0x0 | `UNDEFINED` | Key usage has not been set |
| 0x1 | `RPI_CONNECT` | Raspberry Pi Connect device key |
| 0x2 - 0x7 | `RPI_RESERVED_0` - `RPI_RESERVED_5` | Reserved for use by Raspberry Pi |
| 0x8 - 0xE | `USER_DEFINED_0` - `USER_DEFINED_6` | Available for user-defined purposes |
| 0xF | `INVALID` | Invalid / not usable |

Generate the SHA256 HMAC of message.bin and OTP key id 1:

```
rpi-fw-crypto hmac --in message.bin --key-id 1 --out hmac.bin
```

Derive and retrieve the corresponding public key for the specified device private ECDSA P256 key:

```
rpi-fw-crypto pubkey --key-id 1 --out device-pub.der
```

Retrieve the device private key in DER form - will error if key status is locked:

```
rpi-fw-crypto privkey --key-id 1 --out device-priv.der
```

Generate an ECDSA P256 key-pair and write the private key to the OTP:

```
rpi-fw-crypto genkey --key-id 1 --alg ec
```

## Locking the device private key

Access to the device private key can be locked by default at boot by setting
`lock_device_private_key=1` in config.txt. This blocks the raw OTP read API
(`rpi-fw-crypto privkey`) for the key until the device is rebooted, whilst
still allowing the sign, hmac and pubkey operations.

The contents of config.txt (within boot.img) is authenticated by the firmware
if secure-boot is enabled and `lock_device_private_key=1` should always be
specified if secure-boot is enabled.

## OpenSSL equivalents

The firmware uses MbedTLS to implement the cryptographic operations. For
reference / test, here are the OpenSSL equivalents. In these examples
`private_key.pem` is a local copy of the device private key. If the key
status is not LOCKED it can be extracted and converted to PEM with:

```
rpi-fw-crypto privkey --key-id 1 --out device-priv.der
openssl ec -inform DER -in device-priv.der -outform PEM -out private_key.pem
```

**sign** - SHA256 hash of the input, ECDSA P-256 signature in DER form:

```
openssl pkeyutl -sign -inkey private_key.pem -rawin -in message.bin -out sig.bin
```

**verify** - check a signature with the device public key:

```
openssl pkeyutl -verify -pubin -inkey device-pub.der -sigfile sig.bin -rawin -in message.bin
```

**hmac** - HMAC-SHA256 keyed with the raw 32-byte OTP key value:

```
openssl dgst -sha256 -mac HMAC -macopt hexkey:"$(rpi-otp-private-key)" message.bin
```

**pubkey** - DER (SubjectPublicKeyInfo) public key derived from the private key:

```
openssl ec -in private_key.pem -pubout -outform DER -out device-pub.der
```

**genkey** - ECDSA P-256 (prime256v1) key-pair generation:

```
openssl ecparam -name prime256v1 -genkey -noout -out private_key.pem
```

## Error handling and debug

If the firmware reports an error then `rpi-fw-crypto` prints the error
e.g. `Last crypto error: 4 (Key locked)` and sets the exit code to the
negated firmware error code (`RPI_FW_CRYPTO_STATUS` in `rpifwcrypto.h`).
Since shells report exit codes as an unsigned byte this appears as
`256 - N` e.g. `KEY_LOCKED` (4) gives an exit code of 252.

The firmware logs can be viewed with `sudo vclog -m` for additional debug.

| Code | Status | Description |
|------|--------|-------------|
| 0 | `RPI_FW_CRYPTO_SUCCESS` | Success |
| 1 | `RPI_FW_CRYPTO_ERROR_UNKNOWN` | Unknown error |
| 2 | `RPI_FW_CRYPTO_EINVAL` | Invalid argument errors e.g. zero length etc |
| 3 | `RPI_FW_CRYPTO_KEY_NOT_FOUND` | No key for the given key-id |
| 4 | `RPI_FW_CRYPTO_KEY_LOCKED` | Requested operation for that key is locked |
| 5 | `RPI_FW_CRYPTO_KEY_OTP_ERROR` | OTP read error |
| 6 | `RPI_FW_CRYPTO_KEY_NOT_SET` | Key is all zeros |
| 7 | `RPI_FW_CRYPTO_KEY_INVALID` | Invalid key type/format |
| 8 | `RPI_FW_CRYPTO_NOT_SUPPORTED` | Requested operation is not supported |
| 9 | `RPI_FW_CRYPTO_OPERATION_FAILED` | Crypto algorithm error |
| 10 | `RPI_FW_CRYPTO_KEY_NOT_BLANK` | Key slot is not blank |

## Notes

The device unique private key can also be provisioned with the `rpi-otp-private-key` utility.
This MUST be a raw ECDSA P-256 key and not just a random number.

Access to the device private key can be locked by default at boot by setting lock_device_private_key=1 in config.txt.
The contents of config.txt (within boot.img) is authenticated by the firmware if secure-boot is enabled and
lock_device_private_key=1 should always be specified if secure-boot is enabled.

This service is not a hardware security module and the current implementation
does not protect the key and/or OTP from being accessed directly with root level privileges.
It just removes the need to expose the key to userspace (e.g. initramfs) scripts.
Loading