From 32013c190657eb2e13686a07308f66d9c9fe0387 Mon Sep 17 00:00:00 2001 From: Tim Gover Date: Thu, 4 Jun 2026 12:42:49 +0100 Subject: [PATCH 1/4] rpifwcrypto: Add OpenSSL equivalent commands --- rpifwcrypto/README.md | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/rpifwcrypto/README.md b/rpifwcrypto/README.md index 3b31a8c..acaef61 100644 --- a/rpifwcrypto/README.md +++ b/rpifwcrypto/README.md @@ -39,14 +39,48 @@ Install prerequisites with "sudo apt install cmake libgnutls28-dev"" - you need * 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) +**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 +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 +``` + ** 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. From 3445ad4f0d60bf7842409ae533f8e1b74239c0e3 Mon Sep 17 00:00:00 2001 From: Tim Gover Date: Thu, 4 Jun 2026 12:46:12 +0100 Subject: [PATCH 2/4] rpifwcrypto: Describe error handling and debug --- rpifwcrypto/README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/rpifwcrypto/README.md b/rpifwcrypto/README.md index acaef61..b7842f5 100644 --- a/rpifwcrypto/README.md +++ b/rpifwcrypto/README.md @@ -77,6 +77,16 @@ openssl ec -in private_key.pem -pubout -outform DER -out device-pub.der 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. + ** 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. From 7846c86f579e794509d3bf1c7263fe10cc16c81f Mon Sep 17 00:00:00 2001 From: Tim Gover Date: Thu, 4 Jun 2026 12:50:49 +0100 Subject: [PATCH 3/4] rpifwcrpto: Describe exit/usage codes and make it easier to copy and paste CLI examples --- rpifwcrypto/README.md | 160 ++++++++++++++++++++++++++++++++++-------- 1 file changed, 129 insertions(+), 31 deletions(-) diff --git a/rpifwcrypto/README.md b/rpifwcrypto/README.md index b7842f5..ad8c6fc 100644 --- a/rpifwcrypto/README.md +++ b/rpifwcrypto/README.md @@ -16,30 +16,98 @@ 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) - -**Locking the device private key** +## 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 + +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 @@ -50,7 +118,7 @@ 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** +## OpenSSL equivalents The firmware uses MbedTLS to implement the cryptographic operations. For reference / test, here are the OpenSSL equivalents. In these examples @@ -62,22 +130,37 @@ 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: + ``` -# 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: -# 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 +**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: -# genkey - ECDSA P-256 (prime256v1) key-pair generation +``` openssl ecparam -name prime256v1 -genkey -noout -out private_key.pem ``` -**Error handling and debug** +## 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 @@ -87,7 +170,22 @@ Since shells report exit codes as an unsigned byte this appears as The firmware logs can be viewed with `sudo vclog -m` for additional debug. -** Notes ** +| 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. From 7f88ee4f95149f3de8a5619f8f50ed0f4b99516e Mon Sep 17 00:00:00 2001 From: Tim Gover Date: Thu, 4 Jun 2026 12:56:18 +0100 Subject: [PATCH 4/4] rpifwcrypto: docs: Describe cmake install prefix --- rpifwcrypto/README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/rpifwcrypto/README.md b/rpifwcrypto/README.md index ad8c6fc..4361d82 100644 --- a/rpifwcrypto/README.md +++ b/rpifwcrypto/README.md @@ -28,6 +28,14 @@ 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: