Skip to content
Open
Show file tree
Hide file tree
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
4 changes: 4 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,10 @@ set(WOLFBOOT_SOURCES "include/loader.h"
"src/image.c"
"src/loader.c")

if(DEFINED WOLFBOOT_TZ_PSA AND NOT WOLFBOOT_TZ_PSA STREQUAL "0")
list(APPEND WOLFBOOT_SOURCES "src/dice/dice.c")
endif()

# build bin-assemble tool Windows
set(BINASSEMBLE ${CMAKE_CURRENT_BINARY_DIR}/bin-assemble${HOST_EXE})
set(BINASSEMBLE_OBJDIR "${CMAKE_CURRENT_BINARY_DIR}/obj_bin_assemble")
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@ OBJS:= \
./src/libwolfboot.o \
./hal/hal.o

ifeq ($(WOLFBOOT_TZ_PSA),1)
OBJS+=./src/dice/dice.o
endif

ifneq ($(TARGET),library)
OBJS+=./hal/$(TARGET).o
endif
Expand Down
96 changes: 96 additions & 0 deletions docs/DICE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# DICE Attestation

This document describes the DICE-based PSA attestation service in wolfBoot,
including the token format, keying options, and how to access the service from
non-secure code.

## Protocol overview

wolfBoot implements the PSA Certified Attestation API and emits a
COSE_Sign1-wrapped EAT token following the PSA Attestation Token profile.
The minimum claim set includes:

- Nonce binding (challenge).
- Device identity (UEID).
- Implementation ID and lifecycle when available.
- Measured boot components for wolfBoot and the boot image.

The attestation token is produced by the secure world service and signed with
an attestation key derived by DICE or supplied as a provisioned IAK.

## Implementation summary

The implementation lives under `src/dice/` and is shared across targets. The
service is invoked through the PSA Initial Attestation API and builds the
COSE_Sign1 token using a minimal CBOR encoder.

- Claim construction and COSE_Sign1 encoding: `src/dice/dice.c`.
- PSA Initial Attestation service dispatch: `src/arm_tee_psa_ipc.c`.
- NSC wrappers for the PSA Initial Attestation API: `zephyr/src/arm_tee_attest_api.c`.

Measured boot claims reuse the image hashing pipeline already used by
wolfBoot to validate images. Component claims include a measurement type,
measurement value, and a description string.

## Keying model

wolfBoot supports two keying modes selected at build time.

### DICE derived key (default for no provisioning)

- UDS/HUK-derived secret is fetched via `hal_uds_derive_key()`.
- CDI and signing key material are derived deterministically using HKDF.
- The attestation keypair is derived deterministically and used to sign the
COSE_Sign1 payload.

This path requires no external provisioning and binds the attestation key to
UDS plus measured boot material.

### Provisioned IAK

If a platform already provisions an Initial Attestation Key (IAK), wolfBoot
can use it directly to sign the token.

The attestation service calls `hal_attestation_get_iak_private_key()` to
retrieve the private key material from secure storage (or a manufacturer
injection flow). The IAK is used instead of the DICE derived key.

## HAL integration (per-target)

These HAL hooks are optional and have weak stubs for non-TZ boards. Target
families must implement the appropriate subset based on hardware support.

- `hal_uds_derive_key(uint8_t *out, size_t out_len)`
- Returns a device-unique secret (UDS/HUK-derived) for DICE key derivation.
- `hal_attestation_get_ueid(uint8_t *buf, size_t *len)`
- Returns a stable UEID. If unavailable, the UEID is derived from UDS.
- `hal_attestation_get_implementation_id(uint8_t *buf, size_t *len)`
- Optional implementation ID for the token.
- `hal_attestation_get_lifecycle(uint32_t *lifecycle)`
- Optional lifecycle state for the token.
- `hal_attestation_get_iak_private_key(uint8_t *buf, size_t *len)`
- Optional provisioned IAK private key (used in IAK mode only).

## NSC access (non-secure API)

The non-secure application calls the PSA Initial Attestation API wrappers:

- `psa_initial_attest_get_token_size()`
- `psa_initial_attest_get_token()`

These are provided in `zephyr/include/psa/initial_attestation.h` and are
implemented as NSC calls in `zephyr/src/arm_tee_attest_api.c`.

When `WOLFBOOT_TZ_PSA=1`, the NS application can also use PSA Crypto through
`zephyr/include/psa/crypto.h` via the NSC dispatch path
(`zephyr/src/arm_tee_crypto_api.c`). PSA Protected Storage uses
`zephyr/include/psa/protected_storage.h` in the same fashion.

## Test application

The STM32H5 TrustZone test application in `test-app/` exercises PSA crypto,
attestation, and store access. It requests a token at boot and can perform
PSA crypto operations from the non-secure side.

See `docs/Targets.md` for the STM32H5 TrustZone scenarios and how to enable
PSA mode.
9 changes: 9 additions & 0 deletions docs/STM32-TZ.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ The `WOLFCRYPT_TZ_PSA` option provides a standard PSA Crypto interface using
wolfPSA in the secure domain. The key storage uses the same secure flash
keystore backend as PKCS11, exposed through the wolfPSA store API.

### PSA Initial Attestation (DICE)

When `WOLFBOOT_TZ_PSA=1` is enabled, wolfBoot exposes the PSA Initial
Attestation API to non-secure applications. The attestation token is built
using the DICE flow in `src/dice/` and returned as a COSE_Sign1 token.

See [DICE Attestation](DICE.md) for the full protocol description, HAL hooks
(UDS, UEID, lifecycle, implementation ID), and provisioned IAK support.

### Image header size

The `IMAGE_HEADER_SIZE` option has to be carefully tuned to accommodate for the
Expand Down
4 changes: 4 additions & 0 deletions docs/Targets.md
Original file line number Diff line number Diff line change
Expand Up @@ -1379,6 +1379,10 @@ non-secure callables (NSC).

The example configuration for this scenario is available in [/config/examples/stm32h5-tz.config](/config/examples/stm32h5-tz.config).

When `WOLFBOOT_TZ_PSA=1` is enabled, the STM32H5 test application exercises PSA
Crypto, PSA Protected Storage, and PSA Initial Attestation from the non-secure
side. See [DICE Attestation](/docs/DICE.md) for details on the attestation flow
and APIs.
For more information, see [/docs/STM32-TZ.md](/docs/STM32-TZ.md).

### Scenario 3: DUALBANK mode
Expand Down
35 changes: 35 additions & 0 deletions hal/hal.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "hal.h"
#include "string.h"
#include "printf.h"
#include "wolfboot/wolfboot.h"

/* Test for internal flash erase/write */
/* Use TEST_EXT_FLASH to test ext flash (see spi_flash.c or qspi_flash.c) */
Expand Down Expand Up @@ -280,3 +281,37 @@ int hal_flash_test_dualbank(void)
#endif /* DUALBANK_SWAP */

#endif /* TEST_FLASH */

WEAKFUNCTION int hal_uds_derive_key(uint8_t *out, size_t out_len)
{
(void)out;
(void)out_len;
return -1;
}

WEAKFUNCTION int hal_attestation_get_lifecycle(uint32_t *lifecycle)
{
(void)lifecycle;
return -1;
}

WEAKFUNCTION int hal_attestation_get_implementation_id(uint8_t *buf, size_t *len)
{
(void)buf;
(void)len;
return -1;
}

WEAKFUNCTION int hal_attestation_get_ueid(uint8_t *buf, size_t *len)
{
(void)buf;
(void)len;
return -1;
}

WEAKFUNCTION int hal_attestation_get_iak_private_key(uint8_t *buf, size_t *len)
{
(void)buf;
(void)len;
return -1;
}
70 changes: 70 additions & 0 deletions hal/stm32h5.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
*/

#include <stdint.h>
#include <stddef.h>
#include <image.h>
#include <string.h>

Expand All @@ -29,6 +30,12 @@

#include "uart_drv.h"

#if defined(WOLFBOOT_HASH_SHA256)
#include <wolfssl/wolfcrypt/sha256.h>
#elif defined(WOLFBOOT_HASH_SHA384) || defined(WOLFBOOT_HASH_SHA3_384)
#include <wolfssl/wolfcrypt/sha512.h>
#endif

#define PLL_SRC_HSE 1

#if TZ_SECURE()
Expand Down Expand Up @@ -152,6 +159,69 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
return 0;
}

#define STM32H5_BSEC_BASE 0x46009000u
#define STM32H5_BSEC_UID0 (*(volatile uint32_t *)(STM32H5_BSEC_BASE + 0x14))
#define STM32H5_BSEC_UID1 (*(volatile uint32_t *)(STM32H5_BSEC_BASE + 0x18))
#define STM32H5_BSEC_UID2 (*(volatile uint32_t *)(STM32H5_BSEC_BASE + 0x1C))

int hal_uds_derive_key(uint8_t *out, size_t out_len)
{
uint8_t uid[12];
#if defined(WOLFBOOT_HASH_SHA256)
uint8_t digest[SHA256_DIGEST_SIZE];
wc_Sha256 hash;
#else
uint8_t digest[SHA384_DIGEST_SIZE];
wc_Sha384 hash;
#endif
size_t copy_len;

if (out == NULL || out_len == 0) {
return -1;
}

uid[0] = (uint8_t)(STM32H5_BSEC_UID0 >> 0);
uid[1] = (uint8_t)(STM32H5_BSEC_UID0 >> 8);
uid[2] = (uint8_t)(STM32H5_BSEC_UID0 >> 16);
uid[3] = (uint8_t)(STM32H5_BSEC_UID0 >> 24);
uid[4] = (uint8_t)(STM32H5_BSEC_UID1 >> 0);
uid[5] = (uint8_t)(STM32H5_BSEC_UID1 >> 8);
uid[6] = (uint8_t)(STM32H5_BSEC_UID1 >> 16);
uid[7] = (uint8_t)(STM32H5_BSEC_UID1 >> 24);
uid[8] = (uint8_t)(STM32H5_BSEC_UID2 >> 0);
uid[9] = (uint8_t)(STM32H5_BSEC_UID2 >> 8);
uid[10] = (uint8_t)(STM32H5_BSEC_UID2 >> 16);
uid[11] = (uint8_t)(STM32H5_BSEC_UID2 >> 24);

#if defined(WOLFBOOT_HASH_SHA256)
wc_InitSha256(&hash);
wc_Sha256Update(&hash, uid, sizeof(uid));
wc_Sha256Final(&hash, digest);
copy_len = sizeof(digest);
#else
wc_InitSha384(&hash);
wc_Sha384Update(&hash, uid, sizeof(uid));
wc_Sha384Final(&hash, digest);
copy_len = sizeof(digest);
#endif

if (copy_len > out_len) {
copy_len = out_len;
}
memcpy(out, digest, copy_len);
return 0;
}

int hal_attestation_get_lifecycle(uint32_t *lifecycle)
{
if (lifecycle == NULL) {
return -1;
}

*lifecycle = 0x3000u; /* PSA_LIFECYCLE_SECURED (default) */
return 0;
}

void RAMFUNCTION hal_flash_unlock(void)
{
hal_flash_wait_complete(0);
Expand Down
71 changes: 70 additions & 1 deletion hal/stm32l5.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,16 @@
*/

#include <stdint.h>
#include <stddef.h>
#include <image.h>
#include <string.h>

#if defined(WOLFBOOT_HASH_SHA256)
#include <wolfssl/wolfcrypt/sha256.h>
#elif defined(WOLFBOOT_HASH_SHA384) || defined(WOLFBOOT_HASH_SHA3_384)
#include <wolfssl/wolfcrypt/sha512.h>
#endif

#include "hal.h"
#include "hal/stm32l5.h"

Expand Down Expand Up @@ -103,6 +110,69 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
return 0;
}

#define STM32L5_UID_BASE 0x1FFF7590u
#define STM32L5_UID0 (*(volatile uint32_t *)(STM32L5_UID_BASE + 0x0))
#define STM32L5_UID1 (*(volatile uint32_t *)(STM32L5_UID_BASE + 0x4))
#define STM32L5_UID2 (*(volatile uint32_t *)(STM32L5_UID_BASE + 0x8))

int hal_uds_derive_key(uint8_t *out, size_t out_len)
{
uint8_t uid[12];
#if defined(WOLFBOOT_HASH_SHA256)
uint8_t digest[SHA256_DIGEST_SIZE];
wc_Sha256 hash;
#else
uint8_t digest[SHA384_DIGEST_SIZE];
wc_Sha384 hash;
#endif
size_t copy_len;

if (out == NULL || out_len == 0) {
return -1;
}

uid[0] = (uint8_t)(STM32L5_UID0 >> 0);
uid[1] = (uint8_t)(STM32L5_UID0 >> 8);
uid[2] = (uint8_t)(STM32L5_UID0 >> 16);
uid[3] = (uint8_t)(STM32L5_UID0 >> 24);
uid[4] = (uint8_t)(STM32L5_UID1 >> 0);
uid[5] = (uint8_t)(STM32L5_UID1 >> 8);
uid[6] = (uint8_t)(STM32L5_UID1 >> 16);
uid[7] = (uint8_t)(STM32L5_UID1 >> 24);
uid[8] = (uint8_t)(STM32L5_UID2 >> 0);
uid[9] = (uint8_t)(STM32L5_UID2 >> 8);
uid[10] = (uint8_t)(STM32L5_UID2 >> 16);
uid[11] = (uint8_t)(STM32L5_UID2 >> 24);

#if defined(WOLFBOOT_HASH_SHA256)
wc_InitSha256(&hash);
wc_Sha256Update(&hash, uid, sizeof(uid));
wc_Sha256Final(&hash, digest);
copy_len = sizeof(digest);
#else
wc_InitSha384(&hash);
wc_Sha384Update(&hash, uid, sizeof(uid));
wc_Sha384Final(&hash, digest);
copy_len = sizeof(digest);
#endif

if (copy_len > out_len) {
copy_len = out_len;
}
memcpy(out, digest, copy_len);
return 0;
}

int hal_attestation_get_lifecycle(uint32_t *lifecycle)
{
if (lifecycle == NULL) {
return -1;
}

*lifecycle = 0x3000u; /* PSA_LIFECYCLE_SECURED (default) */
return 0;
}

void RAMFUNCTION hal_flash_unlock(void)
{
hal_flash_wait_complete(0);
Expand Down Expand Up @@ -412,4 +482,3 @@ void hal_prepare_boot(void)
periph_unsecure();
#endif
}

8 changes: 8 additions & 0 deletions include/hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ extern "C" {
#endif

#include "target.h"
#include <stddef.h>
#include <stdint.h>

/* Architecture specific calls */
Expand Down Expand Up @@ -137,6 +138,13 @@ int hal_trng_get_entropy(unsigned char *out, unsigned len);

#endif

/* Attestation helpers (optional, weak stubs available). */
int hal_uds_derive_key(uint8_t *out, size_t out_len);
int hal_attestation_get_lifecycle(uint32_t *lifecycle);
int hal_attestation_get_implementation_id(uint8_t *buf, size_t *len);
int hal_attestation_get_ueid(uint8_t *buf, size_t *len);
int hal_attestation_get_iak_private_key(uint8_t *buf, size_t *len);

#ifdef FLASH_OTP_KEYSTORE

int hal_flash_otp_write(uint32_t flashAddress, const void* data, uint16_t length);
Expand Down
Loading