From ca22d72feacc8d76670c432f463fca1a2e1454f7 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 14 Jan 2026 09:36:15 +0100 Subject: [PATCH 1/6] Add support for DICE attestation + PSA attestation --- CMakeLists.txt | 1 + Makefile | 1 + docs/STM32-TZ.md | 9 + docs/Targets.md | 4 + hal/hal.c | 35 + hal/stm32h5.c | 70 ++ hal/stm32l5.c | 71 +- include/hal.h | 8 + include/wolfboot/dice.h | 32 + options.mk | 6 + src/arm_tee_psa_ipc.c | 239 ++++++- src/dice/dice.c | 972 +++++++++++++++++++++++++++ test-app/ARM-stm32h5-ns.ld | 1 + test-app/ARM-stm32h5.ld | 1 + test-app/Makefile | 71 +- test-app/app_stm32h5.c | 234 ++++++- test-app/arm_tee_ns_interface_stub.c | 24 + test-app/hal_trng_psa.c | 19 + test-app/wcs/user_settings.h | 20 +- tools/config.mk | 6 +- zephyr/include/arm_tee_crypto_defs.h | 7 + zephyr/src/arm_tee_crypto_api.c | 215 +++++- 22 files changed, 2016 insertions(+), 30 deletions(-) create mode 100644 include/wolfboot/dice.h create mode 100644 src/dice/dice.c create mode 100644 test-app/arm_tee_ns_interface_stub.c create mode 100644 test-app/hal_trng_psa.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 210bedf49c..e3d298de3f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -554,6 +554,7 @@ set(WOLFBOOT_SOURCES "include/loader.h" "include/image.h" "src/string.c" "src/image.c" + "src/dice/dice.c" "src/loader.c") # build bin-assemble tool Windows diff --git a/Makefile b/Makefile index bb62d3d320..6ffda2c29c 100644 --- a/Makefile +++ b/Makefile @@ -34,6 +34,7 @@ OBJS:= \ ./src/string.o \ ./src/image.o \ ./src/libwolfboot.o \ + ./src/dice/dice.o \ ./hal/hal.o ifneq ($(TARGET),library) diff --git a/docs/STM32-TZ.md b/docs/STM32-TZ.md index ad8dd388de..aa885405a4 100644 --- a/docs/STM32-TZ.md +++ b/docs/STM32-TZ.md @@ -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 diff --git a/docs/Targets.md b/docs/Targets.md index 49d4c1e453..aa5b18a717 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -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 diff --git a/hal/hal.c b/hal/hal.c index c89e42f3d7..34cfb394fe 100644 --- a/hal/hal.c +++ b/hal/hal.c @@ -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) */ @@ -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; +} diff --git a/hal/stm32h5.c b/hal/stm32h5.c index 264f8351e4..fb2a9ef590 100644 --- a/hal/stm32h5.c +++ b/hal/stm32h5.c @@ -20,6 +20,7 @@ */ #include +#include #include #include @@ -29,6 +30,12 @@ #include "uart_drv.h" +#if defined(WOLFBOOT_HASH_SHA256) +#include +#elif defined(WOLFBOOT_HASH_SHA384) || defined(WOLFBOOT_HASH_SHA3_384) +#include +#endif + #define PLL_SRC_HSE 1 #if TZ_SECURE() @@ -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); diff --git a/hal/stm32l5.c b/hal/stm32l5.c index 36843412de..54894db965 100644 --- a/hal/stm32l5.c +++ b/hal/stm32l5.c @@ -20,9 +20,16 @@ */ #include +#include #include #include +#if defined(WOLFBOOT_HASH_SHA256) +#include +#elif defined(WOLFBOOT_HASH_SHA384) || defined(WOLFBOOT_HASH_SHA3_384) +#include +#endif + #include "hal.h" #include "hal/stm32l5.h" @@ -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); @@ -412,4 +482,3 @@ void hal_prepare_boot(void) periph_unsecure(); #endif } - diff --git a/include/hal.h b/include/hal.h index 1804e05465..90c20f165f 100644 --- a/include/hal.h +++ b/include/hal.h @@ -29,6 +29,7 @@ extern "C" { #endif #include "target.h" +#include #include /* Architecture specific calls */ @@ -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); diff --git a/include/wolfboot/dice.h b/include/wolfboot/dice.h new file mode 100644 index 0000000000..6799773dc3 --- /dev/null +++ b/include/wolfboot/dice.h @@ -0,0 +1,32 @@ +/* dice.h + * + * DICE helpers and PSA attestation token builder. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + */ + +#ifndef WOLFBOOT_DICE_H +#define WOLFBOOT_DICE_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int wolfBoot_dice_get_token(const uint8_t *challenge, + size_t challenge_size, + uint8_t *token_buf, + size_t token_buf_size, + size_t *token_size); + +int wolfBoot_dice_get_token_size(size_t challenge_size, size_t *token_size); + +#ifdef __cplusplus +} +#endif + +#endif /* WOLFBOOT_DICE_H */ diff --git a/options.mk b/options.mk index d55dd160c4..4be8b6e0f6 100644 --- a/options.mk +++ b/options.mk @@ -31,6 +31,10 @@ ifeq ($(WOLFBOOT_TPM_KEYSTORE),1) endif endif +ifeq ($(WOLFBOOT_ATTESTATION_IAK),1) + CFLAGS+=-D"WOLFBOOT_ATTESTATION_IAK" +endif + ## Sealing a secret into the TPM ifeq ($(WOLFBOOT_TPM_SEAL),1) WOLFTPM:=1 @@ -744,6 +748,8 @@ ifeq ($(WOLFCRYPT_TZ_PSA),1) WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/pwdbased.o WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/hmac.o WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/dh.o + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/chacha.o + WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/des3.o ifeq ($(findstring random.o,$(WOLFCRYPT_OBJS)),) WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/random.o endif diff --git a/src/arm_tee_psa_ipc.c b/src/arm_tee_psa_ipc.c index 5c81f53dd8..dbd660f374 100644 --- a/src/arm_tee_psa_ipc.c +++ b/src/arm_tee_psa_ipc.c @@ -21,6 +21,7 @@ #include #include +#include /* Service IDs/handles aligned with ARM TEE defaults. */ #define ARM_TEE_CRYPTO_SID (0x00000080U) @@ -43,8 +44,15 @@ #define ARM_TEE_CRYPTO_HASH_COMPUTE_SID (0x0300U) #define ARM_TEE_CRYPTO_HASH_SETUP_SID (0x0302U) #define ARM_TEE_CRYPTO_HASH_UPDATE_SID (0x0303U) +#define ARM_TEE_CRYPTO_HASH_CLONE_SID (0x0304U) #define ARM_TEE_CRYPTO_HASH_FINISH_SID (0x0305U) #define ARM_TEE_CRYPTO_HASH_ABORT_SID (0x0307U) +#define ARM_TEE_CRYPTO_CIPHER_ENCRYPT_SETUP_SID (0x0400U) +#define ARM_TEE_CRYPTO_CIPHER_DECRYPT_SETUP_SID (0x0401U) +#define ARM_TEE_CRYPTO_CIPHER_SET_IV_SID (0x0402U) +#define ARM_TEE_CRYPTO_CIPHER_UPDATE_SID (0x0403U) +#define ARM_TEE_CRYPTO_CIPHER_FINISH_SID (0x0404U) +#define ARM_TEE_CRYPTO_CIPHER_ABORT_SID (0x0405U) #define ARM_TEE_CRYPTO_ASYMMETRIC_SIGN_HASH_SID (0x0702U) #define ARM_TEE_CRYPTO_ASYMMETRIC_VERIFY_HASH_SID (0x0703U) @@ -106,6 +114,19 @@ struct wolfboot_hash_slot { static struct wolfboot_hash_slot g_hash_slots[WOLFBOOT_ARM_TEE_HASH_SLOTS]; static uint32_t g_hash_next_handle = 1; +struct wolfboot_cipher_slot { + uint32_t handle; + psa_cipher_operation_t op; + int in_use; +}; + +#ifndef WOLFBOOT_ARM_TEE_CIPHER_SLOTS +#define WOLFBOOT_ARM_TEE_CIPHER_SLOTS 4 +#endif + +static struct wolfboot_cipher_slot g_cipher_slots[WOLFBOOT_ARM_TEE_CIPHER_SLOTS]; +static uint32_t g_cipher_next_handle = 1; + #ifndef WOLFBOOT_PS_MAX_DATA #define WOLFBOOT_PS_MAX_DATA 512 #endif @@ -123,6 +144,24 @@ struct wolfboot_ps_entry { static struct wolfboot_ps_entry g_ps_entries[WOLFBOOT_PS_MAX_ENTRIES]; +static psa_status_t wolfboot_attest_status(int dice_rc) +{ + switch (dice_rc) { + case 0: + return PSA_SUCCESS; + case -1: + return PSA_ERROR_INVALID_ARGUMENT; + case -2: + return PSA_ERROR_BUFFER_TOO_SMALL; + case -3: + return PSA_ERROR_HARDWARE_FAILURE; + case -4: + return PSA_ERROR_GENERIC_ERROR; + default: + return PSA_ERROR_GENERIC_ERROR; + } +} + /* Minimal newlib syscall stubs to avoid link errors in bare-metal builds. */ #ifndef WOLFBOOT_NO_SYSCALL_STUBS #if defined(__GNUC__) @@ -255,6 +294,43 @@ static void wolfboot_hash_free(uint32_t handle) } } +static struct wolfboot_cipher_slot *wolfboot_cipher_find(uint32_t handle) +{ + if (handle == 0) { + return NULL; + } + for (size_t i = 0; i < WOLFBOOT_ARM_TEE_CIPHER_SLOTS; i++) { + if (g_cipher_slots[i].in_use && g_cipher_slots[i].handle == handle) { + return &g_cipher_slots[i]; + } + } + return NULL; +} + +static struct wolfboot_cipher_slot *wolfboot_cipher_alloc(uint32_t *handle) +{ + for (size_t i = 0; i < WOLFBOOT_ARM_TEE_CIPHER_SLOTS; i++) { + if (!g_cipher_slots[i].in_use) { + g_cipher_slots[i].in_use = 1; + g_cipher_slots[i].handle = g_cipher_next_handle++; + g_cipher_slots[i].op = psa_cipher_operation_init(); + *handle = g_cipher_slots[i].handle; + return &g_cipher_slots[i]; + } + } + return NULL; +} + +static void wolfboot_cipher_free(uint32_t handle) +{ + struct wolfboot_cipher_slot *slot = wolfboot_cipher_find(handle); + if (slot != NULL) { + (void)psa_cipher_abort(&slot->op); + slot->in_use = 0; + slot->handle = 0; + } +} + static psa_status_t wolfboot_crypto_dispatch(const psa_invec *in_vec, size_t in_len, psa_outvec *out_vec, @@ -422,6 +498,32 @@ static psa_status_t wolfboot_crypto_dispatch(const psa_invec *in_vec, in_vec[1].len); } + case ARM_TEE_CRYPTO_HASH_CLONE_SID: { + struct wolfboot_hash_slot *src_slot; + struct wolfboot_hash_slot *dst_slot; + uint32_t handle = 0; + psa_status_t status; + if (out_vec == NULL || out_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + src_slot = wolfboot_hash_find(iov->op_handle); + if (src_slot == NULL) { + return PSA_ERROR_BAD_STATE; + } + dst_slot = wolfboot_hash_alloc(&handle); + if (dst_slot == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + status = psa_hash_clone(&src_slot->op, &dst_slot->op); + if (status != PSA_SUCCESS) { + wolfboot_hash_free(handle); + return status; + } + *(uint32_t *)out_vec[0].base = handle; + out_vec[0].len = sizeof(uint32_t); + return PSA_SUCCESS; + } + case ARM_TEE_CRYPTO_HASH_FINISH_SID: { struct wolfboot_hash_slot *slot; size_t hash_len = 0; @@ -464,6 +566,101 @@ static psa_status_t wolfboot_crypto_dispatch(const psa_invec *in_vec, return PSA_SUCCESS; } + case ARM_TEE_CRYPTO_CIPHER_ENCRYPT_SETUP_SID: + case ARM_TEE_CRYPTO_CIPHER_DECRYPT_SETUP_SID: { + struct wolfboot_cipher_slot *slot; + uint32_t handle = 0; + psa_status_t status; + if (out_vec == NULL || out_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + slot = wolfboot_cipher_alloc(&handle); + if (slot == NULL) { + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + if (iov->function_id == ARM_TEE_CRYPTO_CIPHER_ENCRYPT_SETUP_SID) { + status = psa_cipher_encrypt_setup(&slot->op, iov->key_id, iov->alg); + } else { + status = psa_cipher_decrypt_setup(&slot->op, iov->key_id, iov->alg); + } + if (status != PSA_SUCCESS) { + wolfboot_cipher_free(handle); + return status; + } + *(uint32_t *)out_vec[0].base = handle; + out_vec[0].len = sizeof(uint32_t); + return PSA_SUCCESS; + } + + case ARM_TEE_CRYPTO_CIPHER_SET_IV_SID: { + struct wolfboot_cipher_slot *slot; + if (in_len < 2) { + return PSA_ERROR_INVALID_ARGUMENT; + } + slot = wolfboot_cipher_find(iov->op_handle); + if (slot == NULL) { + return PSA_ERROR_BAD_STATE; + } + return psa_cipher_set_iv(&slot->op, + (const uint8_t *)in_vec[1].base, + in_vec[1].len); + } + + case ARM_TEE_CRYPTO_CIPHER_UPDATE_SID: { + struct wolfboot_cipher_slot *slot; + size_t out_len_local = 0; + psa_status_t status; + if (in_len < 2 || out_vec == NULL || out_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + slot = wolfboot_cipher_find(iov->op_handle); + if (slot == NULL) { + return PSA_ERROR_BAD_STATE; + } + status = psa_cipher_update(&slot->op, + (const uint8_t *)in_vec[1].base, + in_vec[1].len, + (uint8_t *)out_vec[0].base, + out_vec[0].len, + &out_len_local); + if (status == PSA_SUCCESS) { + out_vec[0].len = out_len_local; + } + return status; + } + + case ARM_TEE_CRYPTO_CIPHER_FINISH_SID: { + struct wolfboot_cipher_slot *slot; + size_t out_len_local = 0; + psa_status_t status; + if (out_vec == NULL || out_len < 1) { + return PSA_ERROR_INVALID_ARGUMENT; + } + slot = wolfboot_cipher_find(iov->op_handle); + if (slot == NULL) { + return PSA_ERROR_BAD_STATE; + } + status = psa_cipher_finish(&slot->op, + (uint8_t *)out_vec[0].base, + out_vec[0].len, + &out_len_local); + if (status == PSA_SUCCESS) { + out_vec[0].len = out_len_local; + wolfboot_cipher_free(iov->op_handle); + } + return status; + } + + case ARM_TEE_CRYPTO_CIPHER_ABORT_SID: { + struct wolfboot_cipher_slot *slot; + slot = wolfboot_cipher_find(iov->op_handle); + if (slot != NULL) { + (void)psa_cipher_abort(&slot->op); + wolfboot_cipher_free(iov->op_handle); + } + return PSA_SUCCESS; + } + case ARM_TEE_CRYPTO_ASYMMETRIC_SIGN_HASH_SID: { size_t sig_len = 0; psa_status_t status; @@ -658,11 +855,29 @@ int32_t arm_tee_psa_call(psa_handle_t handle, int32_t type, if (handle == (psa_handle_t)ARM_TEE_ATTESTATION_HANDLE) { if (type == ARM_TEE_ATTEST_GET_TOKEN) { - if (out_vec == NULL || out_len < 1) { + const psa_invec *challenge_vec; + size_t token_len = 0; + int dice_rc; + psa_status_t status; + + if (in_vec == NULL || in_len < 1 || out_vec == NULL || out_len < 1) { return PSA_ERROR_INVALID_ARGUMENT; } - out_vec[0].len = 0; - return PSA_SUCCESS; + challenge_vec = &in_vec[0]; + if (challenge_vec->base == NULL || out_vec[0].base == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + dice_rc = wolfBoot_dice_get_token((const uint8_t *)challenge_vec->base, + challenge_vec->len, + (uint8_t *)out_vec[0].base, + out_vec[0].len, + &token_len); + status = wolfboot_attest_status(dice_rc); + if (status == PSA_SUCCESS || status == PSA_ERROR_BUFFER_TOO_SMALL) { + out_vec[0].len = token_len; + } + return status; } if (type == ARM_TEE_ATTEST_GET_TOKEN_SIZE) { if (out_vec == NULL || out_len < 1 || out_vec[0].base == NULL || @@ -670,7 +885,25 @@ int32_t arm_tee_psa_call(psa_handle_t handle, int32_t type, return PSA_ERROR_INVALID_ARGUMENT; } { + const rot_size_t *challenge_size = NULL; rot_size_t token_size = 0; + size_t token_size_native = 0; + psa_status_t status; + int dice_rc; + + if (in_vec == NULL || in_len < 1 || in_vec[0].base == NULL || + in_vec[0].len < sizeof(rot_size_t)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + challenge_size = (const rot_size_t *)in_vec[0].base; + dice_rc = wolfBoot_dice_get_token_size(*challenge_size, + &token_size_native); + status = wolfboot_attest_status(dice_rc); + if (status != PSA_SUCCESS) { + return status; + } + token_size = (rot_size_t)token_size_native; XMEMCPY(out_vec[0].base, &token_size, sizeof(token_size)); out_vec[0].len = sizeof(token_size); } diff --git a/src/dice/dice.c b/src/dice/dice.c new file mode 100644 index 0000000000..e3338e971e --- /dev/null +++ b/src/dice/dice.c @@ -0,0 +1,972 @@ +/* dice.c + * + * DICE helpers and PSA attestation token builder. + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfBoot. + */ + +#include +#include + +#include "hal.h" +#include "image.h" +#include "wolfboot/wolfboot.h" +#include "wolfboot/dice.h" + +#include +#include +#include +#include +#include +#include + +#if defined(WOLFBOOT_HASH_SHA384) +#include +#elif defined(WOLFBOOT_HASH_SHA3_384) +#include +#endif + +#ifndef PSA_INITIAL_ATTEST_CHALLENGE_SIZE_32 +#define PSA_INITIAL_ATTEST_CHALLENGE_SIZE_32 (32u) +#define PSA_INITIAL_ATTEST_CHALLENGE_SIZE_48 (48u) +#define PSA_INITIAL_ATTEST_CHALLENGE_SIZE_64 (64u) +#endif + +#ifndef WOLFBOOT_DICE_MAX_PAYLOAD +#define WOLFBOOT_DICE_MAX_PAYLOAD 768 +#endif + +#ifndef WOLFBOOT_DICE_MAX_TBS +#define WOLFBOOT_DICE_MAX_TBS 1024 +#endif + +#define WOLFBOOT_DICE_CDI_LEN 32 +#define WOLFBOOT_DICE_KEY_LEN 32 +#define WOLFBOOT_DICE_UEID_LEN 33 +#define WOLFBOOT_DICE_SIG_LEN 64 + +#define WOLFBOOT_DICE_SUCCESS 0 +#define WOLFBOOT_DICE_ERR_INVALID_ARGUMENT -1 +#define WOLFBOOT_DICE_ERR_BUFFER_TOO_SMALL -2 +#define WOLFBOOT_DICE_ERR_HW -3 +#define WOLFBOOT_DICE_ERR_CRYPTO -4 + +#define COSE_LABEL_ALG 1 +#define COSE_ALG_ES256 (-7) + +#define EAT_CLAIM_NONCE 10 +#define EAT_CLAIM_UEID 256 + +#define PSA_IAT_CLAIM_IMPLEMENTATION_ID 2396 +#define PSA_IAT_CLAIM_LIFECYCLE 2398 +#define PSA_IAT_CLAIM_SW_COMPONENTS 2399 + +#define PSA_SW_COMPONENT_MEASUREMENT_TYPE 1 +#define PSA_SW_COMPONENT_MEASUREMENT_VALUE 2 +#define PSA_SW_COMPONENT_MEASUREMENT_DESCRIPTION 5 + +#define WOLFBOOT_UEID_TYPE_RANDOM 0x01 + +#if defined(WOLFBOOT_HASH_SHA256) +#define WOLFBOOT_DICE_KDF_HASH_TYPE WC_HASH_TYPE_SHA256 +#define WOLFBOOT_DICE_KDF_HASH_SIZE SHA256_DIGEST_SIZE +#define WOLFBOOT_MEASUREMENT_HASH_NAME "sha-256" +#elif defined(WOLFBOOT_HASH_SHA384) +#define WOLFBOOT_DICE_KDF_HASH_TYPE WC_HASH_TYPE_SHA384 +#define WOLFBOOT_DICE_KDF_HASH_SIZE SHA384_DIGEST_SIZE +#define WOLFBOOT_MEASUREMENT_HASH_NAME "sha-384" +#elif defined(WOLFBOOT_HASH_SHA3_384) +#define WOLFBOOT_DICE_KDF_HASH_TYPE WC_HASH_TYPE_SHA384 +#define WOLFBOOT_DICE_KDF_HASH_SIZE 48 +#define WOLFBOOT_MEASUREMENT_HASH_NAME "sha3-384" +#else +#error "No supported hash for DICE attestation" +#endif + +struct wolfboot_dice_component { + const char *measurement_type; + size_t measurement_type_len; + const char *measurement_desc; + size_t measurement_desc_len; + uint8_t measurement[WOLFBOOT_SHA_DIGEST_SIZE]; + size_t measurement_len; +}; + +struct wolfboot_dice_claims { + const uint8_t *challenge; + size_t challenge_len; + uint8_t ueid[WOLFBOOT_DICE_UEID_LEN]; + size_t ueid_len; + uint8_t implementation_id[WOLFBOOT_SHA_DIGEST_SIZE]; + size_t implementation_id_len; + uint32_t lifecycle; + int has_lifecycle; + struct wolfboot_dice_component components[2]; + size_t component_count; +}; + +struct wolfboot_cbor_writer { + uint8_t *buf; + size_t size; + size_t offset; + int error; +}; + +static void wolfboot_cbor_init(struct wolfboot_cbor_writer *w, + uint8_t *buf, + size_t size) +{ + w->buf = buf; + w->size = size; + w->offset = 0; + w->error = 0; +} + +static void wolfboot_cbor_reserve(struct wolfboot_cbor_writer *w, size_t len) +{ + if (w->error != 0) { + return; + } + if (w->buf == NULL || w->size == 0) { + w->offset += len; + return; + } + if (w->offset + len > w->size) { + w->error = WOLFBOOT_DICE_ERR_BUFFER_TOO_SMALL; + return; + } + w->offset += len; +} + +static void wolfboot_cbor_put_type_val(struct wolfboot_cbor_writer *w, + uint8_t major, + uint64_t val) +{ + uint8_t tmp[9]; + size_t len = 0; + + if (val <= 23) { + tmp[len++] = (uint8_t)((major << 5) | (uint8_t)val); + } + else if (val <= 0xFF) { + tmp[len++] = (uint8_t)((major << 5) | 24); + tmp[len++] = (uint8_t)val; + } + else if (val <= 0xFFFF) { + tmp[len++] = (uint8_t)((major << 5) | 25); + tmp[len++] = (uint8_t)(val >> 8); + tmp[len++] = (uint8_t)(val & 0xFF); + } + else if (val <= 0xFFFFFFFFu) { + tmp[len++] = (uint8_t)((major << 5) | 26); + tmp[len++] = (uint8_t)(val >> 24); + tmp[len++] = (uint8_t)(val >> 16); + tmp[len++] = (uint8_t)(val >> 8); + tmp[len++] = (uint8_t)(val & 0xFF); + } + else { + tmp[len++] = (uint8_t)((major << 5) | 27); + tmp[len++] = (uint8_t)(val >> 56); + tmp[len++] = (uint8_t)(val >> 48); + tmp[len++] = (uint8_t)(val >> 40); + tmp[len++] = (uint8_t)(val >> 32); + tmp[len++] = (uint8_t)(val >> 24); + tmp[len++] = (uint8_t)(val >> 16); + tmp[len++] = (uint8_t)(val >> 8); + tmp[len++] = (uint8_t)(val & 0xFF); + } + + wolfboot_cbor_reserve(w, len); + if (w->error != 0) { + return; + } + if (w->buf == NULL || w->size == 0) { + return; + } + XMEMCPY(w->buf + (w->offset - len), tmp, len); +} + +static void wolfboot_cbor_put_uint(struct wolfboot_cbor_writer *w, uint64_t val) +{ + wolfboot_cbor_put_type_val(w, 0, val); +} + +static void wolfboot_cbor_put_int(struct wolfboot_cbor_writer *w, int64_t val) +{ + if (val >= 0) { + wolfboot_cbor_put_uint(w, (uint64_t)val); + } + else { + uint64_t n = (uint64_t)(-1 - val); + wolfboot_cbor_put_type_val(w, 1, n); + } +} + +static void wolfboot_cbor_put_bstr(struct wolfboot_cbor_writer *w, + const uint8_t *data, + size_t len) +{ + wolfboot_cbor_put_type_val(w, 2, len); + wolfboot_cbor_reserve(w, len); + if (w->error != 0) { + return; + } + if (w->buf == NULL || w->size == 0) { + return; + } + XMEMCPY(w->buf + (w->offset - len), data, len); +} + +static void wolfboot_cbor_put_tstr(struct wolfboot_cbor_writer *w, + const char *data, + size_t len) +{ + wolfboot_cbor_put_type_val(w, 3, len); + wolfboot_cbor_reserve(w, len); + if (w->error != 0) { + return; + } + if (w->buf == NULL || w->size == 0) { + return; + } + XMEMCPY(w->buf + (w->offset - len), data, len); +} + +static void wolfboot_cbor_put_array_start(struct wolfboot_cbor_writer *w, + size_t count) +{ + wolfboot_cbor_put_type_val(w, 4, count); +} + +static void wolfboot_cbor_put_map_start(struct wolfboot_cbor_writer *w, + size_t count) +{ + wolfboot_cbor_put_type_val(w, 5, count); +} + +static int wolfboot_hash_region(uintptr_t address, uint32_t size, uint8_t *out) +{ +#if defined(WOLFBOOT_HASH_SHA256) + wc_Sha256 hash; +#elif defined(WOLFBOOT_HASH_SHA384) + wc_Sha384 hash; +#elif defined(WOLFBOOT_HASH_SHA3_384) + wc_Sha3 hash; +#endif + uint32_t pos = 0; + uint32_t chunk; + int ret = 0; + +#if defined(WOLFBOOT_HASH_SHA256) + wc_InitSha256(&hash); +#elif defined(WOLFBOOT_HASH_SHA384) + wc_InitSha384(&hash); +#elif defined(WOLFBOOT_HASH_SHA3_384) + wc_InitSha3_384(&hash, NULL, INVALID_DEVID); +#endif + + while (pos < size) { + chunk = WOLFBOOT_SHA_BLOCK_SIZE; + if (pos + chunk > size) { + chunk = size - pos; + } +#if defined(EXT_FLASH) && defined(NO_XIP) + { + uint8_t tmp[WOLFBOOT_SHA_BLOCK_SIZE]; + int read_sz = ext_flash_read(address + pos, tmp, chunk); + if (read_sz != (int)chunk) { + ret = -1; + break; + } +#if defined(WOLFBOOT_HASH_SHA256) + wc_Sha256Update(&hash, tmp, chunk); +#elif defined(WOLFBOOT_HASH_SHA384) + wc_Sha384Update(&hash, tmp, chunk); +#elif defined(WOLFBOOT_HASH_SHA3_384) + wc_Sha3_384_Update(&hash, tmp, chunk); +#endif + } +#else +#if defined(WOLFBOOT_HASH_SHA256) + wc_Sha256Update(&hash, (const uint8_t *)(address + pos), chunk); +#elif defined(WOLFBOOT_HASH_SHA384) + wc_Sha384Update(&hash, (const uint8_t *)(address + pos), chunk); +#elif defined(WOLFBOOT_HASH_SHA3_384) + wc_Sha3_384_Update(&hash, (const uint8_t *)(address + pos), chunk); +#endif +#endif + pos += chunk; + } + + if (ret == 0) { +#if defined(WOLFBOOT_HASH_SHA256) + wc_Sha256Final(&hash, out); +#elif defined(WOLFBOOT_HASH_SHA384) + wc_Sha384Final(&hash, out); +#elif defined(WOLFBOOT_HASH_SHA3_384) + wc_Sha3_384_Final(&hash, out); +#endif + } + + return ret; +} + +static int wolfboot_get_boot_image_hash(uint8_t *out, size_t *out_len) +{ + struct wolfBoot_image img; + uint8_t *hash_ptr = NULL; + uint16_t hash_len = 0; + + if (out == NULL || out_len == NULL || *out_len < WOLFBOOT_SHA_DIGEST_SIZE) { + return -1; + } + + if (wolfBoot_open_image(&img, PART_BOOT) != 0) { + return -1; + } + + hash_len = wolfBoot_get_header(&img, HDR_HASH, &hash_ptr); + if (hash_len != WOLFBOOT_SHA_DIGEST_SIZE || hash_ptr == NULL) { + return -1; + } + + XMEMCPY(out, hash_ptr, hash_len); + *out_len = hash_len; + return 0; +} + +static int wolfboot_get_wolfboot_hash(uint8_t *out, size_t *out_len) +{ +#if !defined(WOLFBOOT_PARTITION_BOOT_ADDRESS) || !defined(ARCH_FLASH_OFFSET) + (void)out; + (void)out_len; + return -1; +#else + uintptr_t start = (uintptr_t)ARCH_FLASH_OFFSET; + uintptr_t end = (uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS; + uint32_t size; + + if (out == NULL || out_len == NULL || *out_len < WOLFBOOT_SHA_DIGEST_SIZE) { + return -1; + } + + if (end <= start) { + return -1; + } + + size = (uint32_t)(end - start); + if (wolfboot_hash_region(start, size, out) != 0) { + return -1; + } + + *out_len = WOLFBOOT_SHA_DIGEST_SIZE; + return 0; +#endif +} + +static int wolfboot_dice_hkdf(const uint8_t *ikm, + size_t ikm_len, + const uint8_t *salt, + size_t salt_len, + const uint8_t *info, + size_t info_len, + uint8_t *out, + size_t out_len) +{ + int ret = wc_HKDF_ex(WOLFBOOT_DICE_KDF_HASH_TYPE, + ikm, (word32)ikm_len, + salt, (word32)salt_len, + info, (word32)info_len, + out, (word32)out_len, + NULL, INVALID_DEVID); + return ret == 0 ? 0 : -1; +} + +static int wolfboot_dice_get_ueid(uint8_t *ueid, size_t *ueid_len, + const uint8_t *uds, size_t uds_len) +{ + size_t len = WOLFBOOT_DICE_UEID_LEN; + uint8_t digest[WOLFBOOT_DICE_KDF_HASH_SIZE]; + + if (ueid == NULL || ueid_len == NULL) { + return -1; + } + + if (hal_attestation_get_ueid(ueid, &len) == 0) { + *ueid_len = len; + return 0; + } + + if (uds == NULL || uds_len == 0) { + return -1; + } + +#if defined(WOLFBOOT_HASH_SHA256) + { + wc_Sha256 hash; + wc_InitSha256(&hash); + wc_Sha256Update(&hash, uds, (word32)uds_len); + wc_Sha256Final(&hash, digest); + } +#elif defined(WOLFBOOT_HASH_SHA384) + { + wc_Sha384 hash; + wc_InitSha384(&hash); + wc_Sha384Update(&hash, uds, (word32)uds_len); + wc_Sha384Final(&hash, digest); + } +#elif defined(WOLFBOOT_HASH_SHA3_384) + { + wc_Sha3 hash; + wc_InitSha3_384(&hash, NULL, INVALID_DEVID); + wc_Sha3_384_Update(&hash, uds, (word32)uds_len); + wc_Sha3_384_Final(&hash, digest); + } +#endif + + ueid[0] = WOLFBOOT_UEID_TYPE_RANDOM; + XMEMCPY(&ueid[1], digest, + (WOLFBOOT_DICE_UEID_LEN - 1)); + *ueid_len = WOLFBOOT_DICE_UEID_LEN; + return 0; +} + +static int wolfboot_dice_fixup_priv(uint8_t *priv, size_t priv_len) +{ + mp_int k; + mp_int order; + mp_int mod; + const ecc_set_type *curve; + int curve_idx; + int ret; + + if (priv == NULL || priv_len == 0) { + return -1; + } + + curve_idx = wc_ecc_get_curve_idx(ECC_SECP256R1); + curve = wc_ecc_get_curve_params(curve_idx); + if (curve == NULL) { + return -1; + } + + ret = mp_init(&k); + if (ret != MP_OKAY) { + return -1; + } + ret = mp_init(&order); + if (ret != MP_OKAY) { + mp_clear(&k); + return -1; + } + ret = mp_init(&mod); + if (ret != MP_OKAY) { + mp_clear(&k); + mp_clear(&order); + return -1; + } + + ret = mp_read_unsigned_bin(&k, priv, (int)priv_len); + if (ret == MP_OKAY) { + ret = mp_read_radix(&order, curve->order, 16); + } + if (ret == MP_OKAY) { + ret = mp_mod(&k, &order, &mod); + } + if (ret == MP_OKAY && mp_iszero(&mod) == MP_YES) { + ret = mp_set_int(&mod, 1); + } + if (ret == MP_OKAY) { + XMEMSET(priv, 0, priv_len); + ret = mp_to_unsigned_bin_len(&mod, priv, (int)priv_len); + } + + mp_clear(&mod); + mp_clear(&order); + mp_clear(&k); + + return ret == MP_OKAY ? 0 : -1; +} + +static int wolfboot_dice_collect_claims(struct wolfboot_dice_claims *claims) +{ + uint8_t uds[WOLFBOOT_DICE_CDI_LEN]; + size_t uds_len = sizeof(uds); + uint8_t wb_hash[WOLFBOOT_SHA_DIGEST_SIZE]; + size_t wb_hash_len = sizeof(wb_hash); + uint8_t boot_hash[WOLFBOOT_SHA_DIGEST_SIZE]; + size_t boot_hash_len = sizeof(boot_hash); + + XMEMSET(claims, 0, sizeof(*claims)); + + if (hal_uds_derive_key(uds, uds_len) != 0) { + return WOLFBOOT_DICE_ERR_HW; + } + + if (wolfboot_dice_get_ueid(claims->ueid, &claims->ueid_len, + uds, uds_len) != 0) { + return WOLFBOOT_DICE_ERR_HW; + } + + { + size_t impl_len = sizeof(claims->implementation_id); + if (hal_attestation_get_implementation_id(claims->implementation_id, + &impl_len) == 0) { + claims->implementation_id_len = impl_len; + } + } + + if (claims->implementation_id_len == 0) { + if (wolfboot_get_wolfboot_hash(wb_hash, &wb_hash_len) == 0) { + XMEMCPY(claims->implementation_id, wb_hash, wb_hash_len); + claims->implementation_id_len = wb_hash_len; + } + } + + if (hal_attestation_get_lifecycle(&claims->lifecycle) == 0) { + claims->has_lifecycle = 1; + } + + if (wolfboot_get_wolfboot_hash(wb_hash, &wb_hash_len) == 0) { + claims->components[claims->component_count].measurement_type = + WOLFBOOT_MEASUREMENT_HASH_NAME; + claims->components[claims->component_count].measurement_type_len = + XSTRLEN(WOLFBOOT_MEASUREMENT_HASH_NAME); + claims->components[claims->component_count].measurement_desc = + "wolfboot"; + claims->components[claims->component_count].measurement_desc_len = + XSTRLEN("wolfboot"); + XMEMCPY(claims->components[claims->component_count].measurement, + wb_hash, wb_hash_len); + claims->components[claims->component_count].measurement_len = wb_hash_len; + claims->component_count++; + } + + if (wolfboot_get_boot_image_hash(boot_hash, &boot_hash_len) == 0) { + claims->components[claims->component_count].measurement_type = + WOLFBOOT_MEASUREMENT_HASH_NAME; + claims->components[claims->component_count].measurement_type_len = + XSTRLEN(WOLFBOOT_MEASUREMENT_HASH_NAME); + claims->components[claims->component_count].measurement_desc = + "boot-image"; + claims->components[claims->component_count].measurement_desc_len = + XSTRLEN("boot-image"); + XMEMCPY(claims->components[claims->component_count].measurement, + boot_hash, boot_hash_len); + claims->components[claims->component_count].measurement_len = boot_hash_len; + claims->component_count++; + } + + return WOLFBOOT_DICE_SUCCESS; +} + +static int wolfboot_dice_derive_attestation_key(ecc_key *key, + const uint8_t *uds, + size_t uds_len, + const struct wolfboot_dice_claims *claims) +{ + uint8_t cdi[WOLFBOOT_DICE_CDI_LEN]; + uint8_t seed[WOLFBOOT_DICE_CDI_LEN]; + uint8_t priv[WOLFBOOT_DICE_KEY_LEN]; + size_t i; + + XMEMSET(cdi, 0, sizeof(cdi)); + XMEMSET(seed, 0, sizeof(seed)); + XMEMSET(priv, 0, sizeof(priv)); + + if (claims->component_count == 0) { + return -1; + } + + if (wolfboot_dice_hkdf(uds, uds_len, + claims->components[0].measurement, + claims->components[0].measurement_len, + (const uint8_t *)"WOLFBOOT-CDI-0", 14, + cdi, sizeof(cdi)) != 0) { + return -1; + } + + for (i = 1; i < claims->component_count; i++) { + if (wolfboot_dice_hkdf(cdi, sizeof(cdi), + claims->components[i].measurement, + claims->components[i].measurement_len, + (const uint8_t *)"WOLFBOOT-CDI", 12, + cdi, sizeof(cdi)) != 0) { + return -1; + } + } + + if (wolfboot_dice_hkdf(cdi, sizeof(cdi), + (const uint8_t *)"WOLFBOOT-IAK", 12, + (const uint8_t *)"WOLFBOOT-IAK", 12, + seed, sizeof(seed)) != 0) { + return -1; + } + + if (wolfboot_dice_hkdf(seed, sizeof(seed), + (const uint8_t *)"WOLFBOOT-IAK", 12, + (const uint8_t *)"WOLFBOOT-IAK-KEY", 16, + priv, sizeof(priv)) != 0) { + return -1; + } + + if (wolfboot_dice_fixup_priv(priv, sizeof(priv)) != 0) { + return -1; + } + + if (wc_ecc_import_private_key_ex(priv, sizeof(priv), NULL, 0, + key, ECC_SECP256R1) != 0) { + return -1; + } + + return 0; +} + +static int wolfboot_attest_get_private_key(ecc_key *key, + const struct wolfboot_dice_claims *claims) +{ + uint8_t uds[WOLFBOOT_DICE_CDI_LEN]; + size_t uds_len = sizeof(uds); + +#ifdef WOLFBOOT_ATTESTATION_IAK + { + uint8_t priv[WOLFBOOT_DICE_KEY_LEN]; + size_t priv_len = sizeof(priv); + + if (hal_attestation_get_iak_private_key == NULL) { + return -1; + } + if (hal_attestation_get_iak_private_key(priv, &priv_len) != 0) { + return -1; + } + if (priv_len != WOLFBOOT_DICE_KEY_LEN) { + return -1; + } + if (wc_ecc_import_private_key_ex(priv, (word32)priv_len, NULL, 0, + key, ECC_SECP256R1) != 0) { + return -1; + } + return 0; + } +#else + if (hal_uds_derive_key(uds, uds_len) != 0) { + return -1; + } + return wolfboot_dice_derive_attestation_key(key, uds, uds_len, claims); +#endif +} + +static int wolfboot_dice_encode_payload(uint8_t *buf, + size_t buf_len, + const struct wolfboot_dice_claims *claims, + size_t *payload_len) +{ + struct wolfboot_cbor_writer w; + size_t map_count = 2; + size_t i; + + if (claims->implementation_id_len > 0) { + map_count++; + } + if (claims->has_lifecycle) { + map_count++; + } + if (claims->component_count > 0) { + map_count++; + } + + wolfboot_cbor_init(&w, buf, buf_len); + wolfboot_cbor_put_map_start(&w, map_count); + + wolfboot_cbor_put_int(&w, EAT_CLAIM_NONCE); + wolfboot_cbor_put_bstr(&w, claims->challenge, claims->challenge_len); + + wolfboot_cbor_put_int(&w, EAT_CLAIM_UEID); + wolfboot_cbor_put_bstr(&w, claims->ueid, claims->ueid_len); + + if (claims->implementation_id_len > 0) { + wolfboot_cbor_put_int(&w, PSA_IAT_CLAIM_IMPLEMENTATION_ID); + wolfboot_cbor_put_bstr(&w, + claims->implementation_id, + claims->implementation_id_len); + } + + if (claims->has_lifecycle) { + wolfboot_cbor_put_int(&w, PSA_IAT_CLAIM_LIFECYCLE); + wolfboot_cbor_put_uint(&w, claims->lifecycle); + } + + if (claims->component_count > 0) { + wolfboot_cbor_put_int(&w, PSA_IAT_CLAIM_SW_COMPONENTS); + wolfboot_cbor_put_array_start(&w, claims->component_count); + for (i = 0; i < claims->component_count; i++) { + wolfboot_cbor_put_map_start(&w, 3); + wolfboot_cbor_put_uint(&w, PSA_SW_COMPONENT_MEASUREMENT_TYPE); + wolfboot_cbor_put_tstr(&w, + claims->components[i].measurement_type, + claims->components[i].measurement_type_len); + wolfboot_cbor_put_uint(&w, PSA_SW_COMPONENT_MEASUREMENT_VALUE); + wolfboot_cbor_put_bstr(&w, + claims->components[i].measurement, + claims->components[i].measurement_len); + wolfboot_cbor_put_uint(&w, PSA_SW_COMPONENT_MEASUREMENT_DESCRIPTION); + wolfboot_cbor_put_tstr(&w, + claims->components[i].measurement_desc, + claims->components[i].measurement_desc_len); + } + } + + if (w.error != 0) { + return w.error; + } + + *payload_len = w.offset; + return 0; +} + +static int wolfboot_dice_encode_protected(uint8_t *buf, + size_t buf_len, + size_t *prot_len) +{ + struct wolfboot_cbor_writer w; + + wolfboot_cbor_init(&w, buf, buf_len); + wolfboot_cbor_put_map_start(&w, 1); + wolfboot_cbor_put_uint(&w, COSE_LABEL_ALG); + wolfboot_cbor_put_int(&w, COSE_ALG_ES256); + + if (w.error != 0) { + return w.error; + } + + *prot_len = w.offset; + return 0; +} + +static int wolfboot_dice_build_sig_structure(uint8_t *buf, + size_t buf_len, + const uint8_t *prot, + size_t prot_len, + const uint8_t *payload, + size_t payload_len, + size_t *tbs_len) +{ + struct wolfboot_cbor_writer w; + + wolfboot_cbor_init(&w, buf, buf_len); + wolfboot_cbor_put_array_start(&w, 4); + wolfboot_cbor_put_tstr(&w, "Signature1", 10); + wolfboot_cbor_put_bstr(&w, prot, prot_len); + wolfboot_cbor_put_bstr(&w, (const uint8_t *)"", 0); + wolfboot_cbor_put_bstr(&w, payload, payload_len); + + if (w.error != 0) { + return w.error; + } + + *tbs_len = w.offset; + return 0; +} + +static int wolfboot_dice_sign_tbs(const uint8_t *tbs, + size_t tbs_len, + uint8_t *sig, + size_t *sig_len, + const struct wolfboot_dice_claims *claims) +{ + ecc_key key; + WC_RNG rng; + int ret; + uint8_t hash[SHA256_DIGEST_SIZE]; + uint8_t der_sig[128]; + word32 der_sig_len = sizeof(der_sig); + uint8_t r[WOLFBOOT_DICE_SIG_LEN / 2]; + uint8_t s[WOLFBOOT_DICE_SIG_LEN / 2]; + word32 r_len = sizeof(r); + word32 s_len = sizeof(s); + + if (sig == NULL || sig_len == NULL || *sig_len < WOLFBOOT_DICE_SIG_LEN) { + return WOLFBOOT_DICE_ERR_INVALID_ARGUMENT; + } + + wc_ecc_init(&key); + if (wolfboot_attest_get_private_key(&key, claims) != 0) { + wc_ecc_free(&key); + return WOLFBOOT_DICE_ERR_HW; + } + + (void)wc_ecc_set_deterministic(&key, 1); + if (wc_InitRng(&rng) != 0) { + wc_ecc_free(&key); + return WOLFBOOT_DICE_ERR_HW; + } + + { + wc_Sha256 sha; + wc_InitSha256(&sha); + wc_Sha256Update(&sha, tbs, (word32)tbs_len); + wc_Sha256Final(&sha, hash); + } + + ret = wc_ecc_sign_hash(hash, sizeof(hash), der_sig, &der_sig_len, &rng, &key); + wc_FreeRng(&rng); + if (ret != 0) { + wc_ecc_free(&key); + return WOLFBOOT_DICE_ERR_CRYPTO; + } + + ret = wc_ecc_sig_to_rs(der_sig, der_sig_len, r, &r_len, s, &s_len); + if (ret != 0 || r_len > sizeof(r) || s_len > sizeof(s)) { + wc_ecc_free(&key); + return WOLFBOOT_DICE_ERR_CRYPTO; + } + + XMEMSET(sig, 0, WOLFBOOT_DICE_SIG_LEN); + XMEMCPY(sig + (sizeof(r) - r_len), r, r_len); + XMEMCPY(sig + sizeof(r) + (sizeof(s) - s_len), s, s_len); + *sig_len = WOLFBOOT_DICE_SIG_LEN; + + wc_ecc_free(&key); + return WOLFBOOT_DICE_SUCCESS; +} + +static int wolfboot_dice_build_token(uint8_t *token_buf, + size_t token_buf_size, + size_t *token_len, + const uint8_t *challenge, + size_t challenge_len) +{ + struct wolfboot_dice_claims claims; + uint8_t payload[WOLFBOOT_DICE_MAX_PAYLOAD]; + size_t payload_len = 0; + uint8_t protected_hdr[32]; + size_t protected_len = 0; + uint8_t tbs[WOLFBOOT_DICE_MAX_TBS]; + size_t tbs_len = 0; + uint8_t sig[WOLFBOOT_DICE_SIG_LEN]; + size_t sig_len = sizeof(sig); + struct wolfboot_cbor_writer w; + int ret; + + ret = wolfboot_dice_collect_claims(&claims); + if (ret != 0) { + return ret; + } + + claims.challenge = challenge; + claims.challenge_len = challenge_len; + + ret = wolfboot_dice_encode_payload(payload, sizeof(payload), &claims, + &payload_len); + if (ret != 0) { + return ret; + } + + ret = wolfboot_dice_encode_protected(protected_hdr, sizeof(protected_hdr), + &protected_len); + if (ret != 0) { + return ret; + } + + ret = wolfboot_dice_build_sig_structure(tbs, sizeof(tbs), + protected_hdr, protected_len, + payload, payload_len, &tbs_len); + if (ret != 0) { + return ret; + } + + if (token_buf != NULL) { + ret = wolfboot_dice_sign_tbs(tbs, tbs_len, sig, &sig_len, &claims); + if (ret != 0) { + return ret; + } + } + + wolfboot_cbor_init(&w, token_buf, token_buf_size); + wolfboot_cbor_put_array_start(&w, 4); + wolfboot_cbor_put_bstr(&w, protected_hdr, protected_len); + wolfboot_cbor_put_map_start(&w, 0); + wolfboot_cbor_put_bstr(&w, payload, payload_len); + if (token_buf != NULL) { + wolfboot_cbor_put_bstr(&w, sig, sig_len); + } + else { + wolfboot_cbor_put_type_val(&w, 2, WOLFBOOT_DICE_SIG_LEN); + wolfboot_cbor_reserve(&w, WOLFBOOT_DICE_SIG_LEN); + } + + if (w.error != 0) { + return w.error; + } + + *token_len = w.offset; + return WOLFBOOT_DICE_SUCCESS; +} + +int wolfBoot_dice_get_token(const uint8_t *challenge, + size_t challenge_size, + uint8_t *token_buf, + size_t token_buf_size, + size_t *token_size) +{ + size_t needed = 0; + int ret; + + if (challenge == NULL || token_size == NULL) { + return WOLFBOOT_DICE_ERR_INVALID_ARGUMENT; + } + + if (challenge_size != PSA_INITIAL_ATTEST_CHALLENGE_SIZE_32 && + challenge_size != PSA_INITIAL_ATTEST_CHALLENGE_SIZE_48 && + challenge_size != PSA_INITIAL_ATTEST_CHALLENGE_SIZE_64) { + return WOLFBOOT_DICE_ERR_INVALID_ARGUMENT; + } + + ret = wolfboot_dice_build_token(NULL, 0, &needed, challenge, challenge_size); + if (ret != 0) { + return ret; + } + + if (token_buf == NULL || token_buf_size < needed) { + *token_size = needed; + return WOLFBOOT_DICE_ERR_BUFFER_TOO_SMALL; + } + + ret = wolfboot_dice_build_token(token_buf, token_buf_size, &needed, + challenge, challenge_size); + if (ret != 0) { + return ret; + } + + *token_size = needed; + return WOLFBOOT_DICE_SUCCESS; +} + +int wolfBoot_dice_get_token_size(size_t challenge_size, size_t *token_size) +{ + size_t needed = 0; + int ret; + uint8_t dummy_challenge[PSA_INITIAL_ATTEST_CHALLENGE_SIZE_64]; + + if (token_size == NULL) { + return WOLFBOOT_DICE_ERR_INVALID_ARGUMENT; + } + + if (challenge_size != PSA_INITIAL_ATTEST_CHALLENGE_SIZE_32 && + challenge_size != PSA_INITIAL_ATTEST_CHALLENGE_SIZE_48 && + challenge_size != PSA_INITIAL_ATTEST_CHALLENGE_SIZE_64) { + return WOLFBOOT_DICE_ERR_INVALID_ARGUMENT; + } + + XMEMSET(dummy_challenge, 0, sizeof(dummy_challenge)); + ret = wolfboot_dice_build_token(NULL, 0, &needed, dummy_challenge, + challenge_size); + if (ret != 0) { + return ret; + } + + *token_size = needed; + return WOLFBOOT_DICE_SUCCESS; +} diff --git a/test-app/ARM-stm32h5-ns.ld b/test-app/ARM-stm32h5-ns.ld index b8d7133752..82000cfd0e 100644 --- a/test-app/ARM-stm32h5-ns.ld +++ b/test-app/ARM-stm32h5-ns.ld @@ -52,3 +52,4 @@ SECTIONS PROVIDE(_start_heap = _end); PROVIDE(_heap_size = 4K); PROVIDE(_end_stack = ORIGIN(RAM) + LENGTH(RAM)); +PROVIDE(end = _end); diff --git a/test-app/ARM-stm32h5.ld b/test-app/ARM-stm32h5.ld index a225a39ab1..88e7749383 100644 --- a/test-app/ARM-stm32h5.ld +++ b/test-app/ARM-stm32h5.ld @@ -52,3 +52,4 @@ SECTIONS PROVIDE(_start_heap = _end); PROVIDE(_heap_size = 4K); PROVIDE(_end_stack = ORIGIN(RAM) + LENGTH(RAM)); +PROVIDE(end = _end); diff --git a/test-app/Makefile b/test-app/Makefile index 135dbfd5ac..e02a9663a3 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -3,13 +3,18 @@ -include ../options.mk -include ./wcs/pkcs11.mk +WOLFBOOT_LIB_WOLFSSL?=../lib/wolfssl +WOLFSSL_LOCAL_OBJDIR?=wolfssl_obj +vpath %.c $(WOLFBOOT_LIB_WOLFSSL) +vpath %.S $(WOLFBOOT_LIB_WOLFSSL) + TARGET?=none ARCH?=ARM MCUXPRESSO_CMSIS?=$(MCUXPRESSO)/CMSIS ifeq ($(TZEN),1) # wcs directory contains a user_settings.h, which will conflict with # the one in the include directory if the test app needs it (e.g. wolfHSM) - CFLAGS+=-I./wcs + CFLAGS:=-I./wcs $(CFLAGS) endif CFLAGS+=-I. -I.. DEBUG?=1 @@ -21,6 +26,10 @@ BOOTLOADER_PARTITION_SIZE?=$$(( $(WOLFBOOT_PARTITION_BOOT_ADDRESS) - $(ARCH_FLAS # For test-applications we should only use user_settings.h CFLAGS+=-DWOLFSSL_USER_SETTINGS -DWOLFTPM_USER_SETTINGS +ifeq ($(WOLFBOOT_ATTESTATION_TEST),1) + CFLAGS+=-DWOLFBOOT_ATTESTATION_TEST +endif + ifeq ($(HASH),SHA256) WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.o CFLAGS+=-D"WOLFBOOT_HASH_SHA256" @@ -66,6 +75,12 @@ endif include ../arch.mk +# Optional alias for clearer TZ PSA selection in app builds. +ifeq ($(WOLFBOOT_TZ_PSA),1) + WOLFCRYPT_TZ=1 + WOLFCRYPT_TZ_PSA=1 +endif + # Setup default linker flags LDFLAGS+=-T $(LSCRIPT) -Wl,-gc-sections -Wl,-Map=image.map @@ -81,10 +96,35 @@ ifeq ($(TZEN),1) CFLAGS+=-I./ APP_OBJS+=../hal/$(TARGET)_ns.o ifeq ($(WOLFCRYPT_TZ),1) + CFLAGS+=-I"$(WOLFBOOT_LIB_WOLFSSL)" -I../lib/wolfssl APP_OBJS+=../src/wc_secure_calls.o + WOLFCRYPT_APP_OBJS+=\ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/memory.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/hash.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha512.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha3.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/hmac.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/pwdbased.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/pkcs12.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/random.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/coding.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/asn.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/ecc.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/rsa.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/aes.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/wolfmath.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sp_int.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sp_cortexm.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sp_c32.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/wc_port.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/wolfentropy.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/dh.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/wc_encrypt.o ifeq ($(WOLFCRYPT_TZ_PKCS11),1) CFLAGS+=-DWOLFSSL_USER_SETTINGS -DWOLFTPM_USER_SETTINGS - CFLAGS+=-DWOLFBOOT_PKCS11_APP -DSECURE_PKCS11 + CFLAGS+=-DWOLFBOOT_PKCS11_APP -DSECURE_PKCS11 -DWOLFBOOT_TZ_PKCS11 CFLAGS+=-I"$(WOLFBOOT_LIB_WOLFPKCS11)" APP_OBJS+=./wcs/pkcs11_test_ecc.o APP_OBJS+=./wcs/pkcs11_stub.o @@ -124,6 +164,24 @@ ifeq ($(TZEN),1) CFLAGS+=-DWOLFBOOT_SECURE_CALLS -Wstack-usage=19184 LDFLAGS+=--specs=nosys.specs -u _printf_float endif + ifeq ($(WOLFCRYPT_TZ_PSA),1) + CFLAGS+=-DWOLFBOOT_TZ_PSA -DWOLFSSL_HAVE_PSA + CFLAGS+=-I../zephyr/include -I../lib/wolfPSA/wolfpsa + CFLAGS+=-I"$(WOLFBOOT_LIB_WOLFSSL)" -I../lib/wolfssl/wolfssl + APP_OBJS+=hal_trng_psa.o + APP_OBJS+=../zephyr/src/wolfboot_psa_ns_api.o + APP_OBJS+=arm_tee_ns_interface_stub.o + APP_OBJS+=../zephyr/src/arm_tee_attest_api.o + APP_OBJS+=../zephyr/src/arm_tee_crypto_api.o + WOLFCRYPT_APP_OBJS+=\ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/psa/psa.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/psa/psa_aes.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/psa/psa_hash.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/psa/psa_pkcbs.o + endif + WOLFCRYPT_APP_OBJS := $(patsubst $(WOLFBOOT_LIB_WOLFSSL)/%, \ + $(WOLFSSL_LOCAL_OBJDIR)/%, $(WOLFCRYPT_APP_OBJS)) + APP_OBJS+=$(sort $(WOLFCRYPT_APP_OBJS)) else APP_OBJS+=../hal/$(TARGET).o endif @@ -569,6 +627,9 @@ image.srec: image.elf @echo "\t[SREC] $@" $(Q)$(OBJCOPY) $(OBJCOPY_FLAGS) -O srec $^ $@ +APP_OBJS := $(patsubst $(WOLFBOOT_LIB_WOLFSSL)/%, \ + $(WOLFSSL_LOCAL_OBJDIR)/%, $(APP_OBJS)) + ifeq ($(ELF_FLASH_SCATTER),1) # When ELF_FLASH_SCATTER=1, preprocess the ELF file with the squashelf tool SQUASHELF_TOOL = ../tools/squashelf/squashelf @@ -611,8 +672,14 @@ delta-extra-data: image.bin @echo "\t[AS-$(ARCH)] $@" $(Q)$(CC) $(CFLAGS) -c $(OUTPUT_FLAG) $@ $^ +$(WOLFSSL_LOCAL_OBJDIR)/%.o: %.c + @echo "\t[CC-$(ARCH)] $@" + $(Q)mkdir -p $(dir $@) + $(Q)$(CC) $(CFLAGS) -c $(OUTPUT_FLAG) $@ $< + clean: $(Q)rm -f *.bin *.elf tags *.o $(LSCRIPT) $(APP_OBJS) wcs/*.o + $(Q)rm -rf $(WOLFSSL_LOCAL_OBJDIR) $(LSCRIPT): $(LSCRIPT_TEMPLATE) FORCE $(Q)printf "%d" $(WOLFBOOT_PARTITION_BOOT_ADDRESS) > .wolfboot-offset diff --git a/test-app/app_stm32h5.c b/test-app/app_stm32h5.c index 787ea59ace..15975fc691 100644 --- a/test-app/app_stm32h5.c +++ b/test-app/app_stm32h5.c @@ -33,23 +33,35 @@ #include "wolfboot/wolfboot.h" #include "keystore.h" #include "target.h" -#include "image.h" #ifdef WOLFBOOT_TPM #include "tpm.h" #endif -#ifdef SECURE_PKCS11 +#ifdef WOLFBOOT_TZ_PKCS11 #include "wcs/user_settings.h" #include "wolfssl/wolfcrypt/settings.h" #include "wolfssl/wolfcrypt/wc_pkcs11.h" #include "wolfssl/wolfcrypt/random.h" -#include "wolfcrypt/benchmark/benchmark.h" -#include "wolfcrypt/test/test.h" extern const char pkcs11_library_name[]; extern const CK_FUNCTION_LIST wolfpkcs11nsFunctionList; #endif +#ifdef WOLFCRYPT_SECURE_MODE +int benchmark_test(void *args); +int wolfcrypt_test(void *args); +#endif + +#ifdef WOLFBOOT_TZ_PSA +#include "psa/crypto.h" +#include "psa/error.h" +#include "psa/initial_attestation.h" +#include "wolfssl/wolfcrypt/types.h" +#include "wolfssl/wolfcrypt/sha256.h" +#include "wolfssl/wolfcrypt/sha512.h" +#include "wolfssl/wolfcrypt/sha3.h" +#endif + volatile unsigned int jiffies = 0; /* Usart irq-based read function */ @@ -156,7 +168,9 @@ extern int ecdsa_sign_verify(int devId); static int cmd_help(const char *args); static int cmd_info(const char *args); static int cmd_success(const char *args); +#ifdef WOLFBOOT_TZ_PKCS11 static int cmd_login_pkcs11(const char *args); +#endif static int cmd_random(const char *args); static int cmd_benchmark(const char *args); static int cmd_test(const char *args); @@ -192,7 +206,9 @@ struct console_command COMMANDS[] = {cmd_help, "help", "shows this help message"}, {cmd_info, "info", "display information about the system and partitions"}, {cmd_success, "success", "confirm a successful update"}, +#ifdef WOLFBOOT_TZ_PKCS11 {cmd_login_pkcs11, "pkcs11", "enable and test crypto calls with PKCS11 in secure mode" }, +#endif {cmd_random, "random", "generate a random number"}, {cmd_timestamp, "timestamp", "print the current systick/timestamp"}, {cmd_benchmark, "benchmark", "run the wolfCrypt benchmark"}, @@ -557,7 +573,17 @@ static int cmd_success(const char *args) static int cmd_random(const char *args) { -#ifdef WOLFCRYPT_SECURE_MODE +#ifdef WOLFBOOT_TZ_PSA + uint32_t rand = 0; + psa_status_t status = psa_generate_random((uint8_t *)&rand, sizeof(rand)); + if (status != PSA_SUCCESS) { + printf("Failed to generate PSA random number (%ld)\r\n", + (long)status); + return -1; + } + printf("Today's lucky number: 0x%08lX\r\n", rand); + printf("Brought to you by PSA crypto + HW TRNG in Secure world\r\n"); +#elif defined(WOLFCRYPT_SECURE_MODE) WC_RNG rng; int ret; uint32_t rand; @@ -593,10 +619,185 @@ static int cmd_timestamp(const char *args) return 0; } +#if defined(WOLFBOOT_ATTESTATION_TEST) && defined(WOLFBOOT_TZ_PSA) +static int run_attestation_test(void) +{ + uint8_t challenge[PSA_INITIAL_ATTEST_CHALLENGE_SIZE_64]; + uint8_t token[1024]; + size_t token_size = 0; + psa_status_t status; + size_t i; + + for (i = 0; i < sizeof(challenge); i++) { + challenge[i] = (uint8_t)i; + } + + status = psa_initial_attest_get_token_size(sizeof(challenge), &token_size); + if (status != PSA_SUCCESS) { + printf("attest: token size query failed (%d)\r\n", status); + return -1; + } + printf("attest: token size is %lu bytes\r\n", + (unsigned long)token_size); + + status = psa_initial_attest_get_token(challenge, sizeof(challenge), + token, sizeof(token), &token_size); + if (status != PSA_SUCCESS) { + printf("attest: get token failed (%d)\r\n", status); + return -1; + } + printf("attest: token size %lu bytes\r\n", (unsigned long)token_size); + print_hex(token, (uint32_t)token_size, 1); + return 0; +} +#endif + +#ifdef WOLFBOOT_TZ_PSA +/* Hash helpers for app-side measurement printing. */ +#if defined(WOLFBOOT_HASH_SHA256) +#define APP_HASH_HDR HDR_SHA256 +#define APP_HASH_SIZE (32u) +typedef wc_Sha256 app_hash_t; +#define app_hash_init(h) wc_InitSha256((h)) +#define app_hash_update(h, data, len) \ + wc_Sha256Update((h), (const byte *)(data), (word32)(len)) +#define app_hash_final(h, out) wc_Sha256Final((h), (byte *)(out)) +#elif defined(WOLFBOOT_HASH_SHA384) +#define APP_HASH_HDR HDR_SHA384 +#define APP_HASH_SIZE (48u) +typedef wc_Sha384 app_hash_t; +#define app_hash_init(h) wc_InitSha384((h)) +#define app_hash_update(h, data, len) \ + wc_Sha384Update((h), (const byte *)(data), (word32)(len)) +#define app_hash_final(h, out) wc_Sha384Final((h), (byte *)(out)) +#elif defined(WOLFBOOT_HASH_SHA3_384) +#define APP_HASH_HDR HDR_SHA3_384 +#define APP_HASH_SIZE (48u) +typedef wc_Sha3 app_hash_t; +#define app_hash_init(h) wc_InitSha3_384((h), NULL, INVALID_DEVID) +#define app_hash_update(h, data, len) \ + wc_Sha3_384_Update((h), (const byte *)(data), (word32)(len)) +#define app_hash_final(h, out) wc_Sha3_384_Final((h), (byte *)(out)) +#else +#define APP_HASH_HDR 0 +#define APP_HASH_SIZE (0u) +typedef int app_hash_t; +#define app_hash_init(h) (void)(h) +#define app_hash_update(h, data, len) (void)(h), (void)(data), (void)(len) +#define app_hash_final(h, out) (void)(h), (void)(out) +#endif + +static int hash_region(uintptr_t address, uint32_t size, uint8_t *out) +{ + app_hash_t hash; + const uint8_t *ptr = (const uint8_t *)address; + uint32_t pos = 0; + + if (out == NULL || size == 0 || APP_HASH_SIZE == 0u) { + return -1; + } + + app_hash_init(&hash); + + while (pos < size) { + uint32_t chunk = size - pos; + if (chunk > 256) { + chunk = 256; + } + app_hash_update(&hash, ptr + pos, chunk); + pos += chunk; + } + + app_hash_final(&hash, out); + return 0; +} + +static int run_psa_boot_attestation(void) +{ + psa_status_t status; + uint8_t challenge[PSA_INITIAL_ATTEST_CHALLENGE_SIZE_64]; + uint8_t token[1024]; + uint8_t hash_buf[APP_HASH_SIZE]; + size_t token_size = 0; + int ret = 0; + size_t i; + + printf("PSA boot attestation: start\r\n"); + + printf(" step 1: TODO verify boot image post-boot\r\n"); + printf(" step 2: TODO read boot image measurement (HDR_HASH)\r\n"); + + printf(" step 3: compute wolfBoot measurement\r\n"); +#if defined(WOLFBOOT_PARTITION_BOOT_ADDRESS) && defined(ARCH_FLASH_OFFSET) + if (ret == 0) { + uintptr_t start = (uintptr_t)ARCH_FLASH_OFFSET; + uintptr_t end = (uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS; + if (end <= start) { + printf(" step 3: invalid wolfBoot region\r\n"); + ret = -1; + } else if (hash_region(start, (uint32_t)(end - start), hash_buf) != 0) { + printf(" step 3: wolfBoot hash failed\r\n"); + ret = -1; + } else { + printf(" step 3: wolfBoot hash (%u bytes)\r\n", + (unsigned int)APP_HASH_SIZE); + print_hex(hash_buf, APP_HASH_SIZE, 0); + } + } +#else + printf(" step 3: wolfBoot region unavailable for hashing\r\n"); +#endif + + printf(" step 4: generate attestation challenge\r\n"); + status = psa_generate_random(challenge, sizeof(challenge)); + if (status != PSA_SUCCESS) { + printf(" step 4: PSA RNG failed (%ld), using deterministic nonce\r\n", + (long)status); + for (i = 0; i < sizeof(challenge); i++) { + challenge[i] = (uint8_t)i; + } + } else { + printf(" step 4: challenge ready (%u bytes)\r\n", + (unsigned int)sizeof(challenge)); + } + + printf(" step 5: request IAT token size\r\n"); + status = psa_initial_attest_get_token_size(sizeof(challenge), &token_size); + if (status != PSA_SUCCESS) { + printf(" step 5: token size failed (%ld)\r\n", (long)status); + ret = -1; + } else { + printf(" step 5: token size %lu bytes\r\n", + (unsigned long)token_size); + } + + printf(" step 6: request IAT token\r\n"); + if (ret == 0 && token_size <= sizeof(token)) { + status = psa_initial_attest_get_token(challenge, sizeof(challenge), + token, sizeof(token), &token_size); + if (status != PSA_SUCCESS) { + printf(" step 6: token failed (%ld)\r\n", (long)status); + ret = -1; + } else { + printf(" step 6: token received (%lu bytes)\r\n", + (unsigned long)token_size); + print_hex(token, (uint32_t)token_size, 1); + } + } else if (ret == 0) { + printf(" step 6: token buffer too small (%lu > %lu)\r\n", + (unsigned long)token_size, (unsigned long)sizeof(token)); + ret = -1; + } + + printf("PSA boot attestation: %s\r\n", ret == 0 ? "success" : "failed"); + return ret; +} +#endif + +#ifdef WOLFBOOT_TZ_PKCS11 static int cmd_login_pkcs11(const char *args) { int ret = -1; -#ifdef SECURE_PKCS11 unsigned int devId = 0; Pkcs11Token token; Pkcs11Dev PKCS11_d; @@ -687,11 +888,9 @@ static int cmd_login_pkcs11(const char *args) printf("PKCS11 initialization completed successfully.\r\n"); pkcs11_initialized = 1; } -#else - printf("Feature only supported with WOLFCRYPT_TZ=1\n"); -#endif /* SECURE_PKCS11 */ return ret; } +#endif /* WOLFBOOT_TZ_PKCS11 */ static int cmd_benchmark(const char *args) { @@ -1106,11 +1305,28 @@ void main(void) printf("Version : 0x%lx\r\n", app_version); printf("========================\r\n"); +#ifdef WOLFBOOT_TZ_PSA + ret = psa_crypto_init(); + if (ret == PSA_SUCCESS) { + printf("PSA crypto init ok\r\n"); + } else { + printf("PSA crypto init failed (%d)\r\n", ret); + } +#endif + cmd_info(NULL); #ifdef WOLFBOOT_TPM cmd_tpm_info(NULL); #endif +#if defined(WOLFBOOT_ATTESTATION_TEST) && defined(WOLFBOOT_TZ_PSA) + (void)run_attestation_test(); +#endif + +#ifdef WOLFBOOT_TZ_PSA + (void)run_psa_boot_attestation(); +#endif + console_loop(); while(1) diff --git a/test-app/arm_tee_ns_interface_stub.c b/test-app/arm_tee_ns_interface_stub.c new file mode 100644 index 0000000000..51ee1b637b --- /dev/null +++ b/test-app/arm_tee_ns_interface_stub.c @@ -0,0 +1,24 @@ +/* arm_tee_ns_interface_stub.c + * + * Minimal non-Zephyr dispatcher for bare-metal test-app. + */ + +#include "arm_tee_ns_interface.h" +#include "psa/error.h" +#include + +int32_t arm_tee_ns_interface_dispatch(arm_tee_veneer_fn fn, + uint32_t arg0, uint32_t arg1, + uint32_t arg2, uint32_t arg3) +{ + if (fn == NULL) { + return (int32_t)PSA_ERROR_INVALID_ARGUMENT; + } + + return fn(arg0, arg1, arg2, arg3); +} + +uint32_t arm_tee_ns_interface_init(void) +{ + return PSA_SUCCESS; +} diff --git a/test-app/hal_trng_psa.c b/test-app/hal_trng_psa.c new file mode 100644 index 0000000000..b388212e24 --- /dev/null +++ b/test-app/hal_trng_psa.c @@ -0,0 +1,19 @@ +/* hal_trng_psa.c + * + * PSA-backed entropy for bare-metal test-app. + */ + +#include +#include "psa/crypto.h" + +int hal_trng_get_entropy(unsigned char *out, unsigned len) +{ + psa_status_t status; + + if (out == NULL || len == 0) { + return -1; + } + + status = psa_generate_random(out, len); + return (status == PSA_SUCCESS) ? 0 : -1; +} diff --git a/test-app/wcs/user_settings.h b/test-app/wcs/user_settings.h index 4d6bbce320..87323cb30f 100644 --- a/test-app/wcs/user_settings.h +++ b/test-app/wcs/user_settings.h @@ -46,11 +46,14 @@ extern int tolower(int c); #define XTOLOWER(c) tolower((c)) /* PKCS11 */ -#define HAVE_PKCS11 -#define HAVE_WOLF_BIGINT -#define HAVE_PKCS11_STATIC -#define WOLF_CRYPTO_CB -#define MAX_CRYPTO_DEVID_CALLBACKS 2 +#ifdef WOLFBOOT_TZ_PKCS11 + #define HAVE_PKCS11 + #define HAVE_WOLF_BIGINT + #define HAVE_PKCS11_STATIC + #define WOLF_CRYPTO_CB + #define MAX_CRYPTO_DEVID_CALLBACKS 2 +#endif + /* ECC */ @@ -100,6 +103,9 @@ extern int tolower(int c); #ifndef WOLFSSL_AES_COUNTER #define WOLFSSL_AES_COUNTER #endif +#ifndef WOLFSSL_AES_CBC +#define WOLFSSL_AES_CBC +#endif #ifndef WOLFSSL_AES_DIRECT #define WOLFSSL_AES_DIRECT #endif @@ -142,6 +148,10 @@ extern int tolower(int c); #define WC_TEST_NO_CRYPTOCB_SW_TEST #define BENCH_EMBEDDED +#define HAVE_ECC_KEY_EXPORT +#define HAVE_PKCS8 +#define HAVE_PKCS12 + #ifdef SECURE_PKCS11 static inline int wcs_cmse_get_random(unsigned char* output, int sz) diff --git a/tools/config.mk b/tools/config.mk index a4fd9dc1a8..b27a8ed853 100644 --- a/tools/config.mk +++ b/tools/config.mk @@ -57,6 +57,8 @@ ifeq ($(ARCH),) MEASURED_BOOT?=0 WOLFBOOT_TPM_SEAL?=0 WOLFBOOT_TPM_KEYSTORE?=0 + WOLFBOOT_ATTESTATION_IAK?=0 + WOLFBOOT_ATTESTATION_TEST?=0 WOLFBOOT_UNIVERSAL_KEYSTORE?=0 TZEN?=0 WOLFCRYPT_TZ?=0 @@ -92,7 +94,9 @@ CONFIG_VARS:= ARCH TARGET SIGN HASH MCUXSDK MCUXPRESSO MCUXPRESSO_CPU MCUXPRESSO CORTEX_M0 CORTEX_M7 CORTEX_M33 NO_ASM EXT_FLASH SPI_FLASH NO_XIP UART_FLASH ALLOW_DOWNGRADE NVM_FLASH_WRITEONCE \ DISABLE_BACKUP WOLFBOOT_VERSION V NO_MPU ENCRYPT FLAGS_HOME FLAGS_INVERT \ SPMATH SPMATHALL RAM_CODE DUALBANK_SWAP IMAGE_HEADER_SIZE PKA TZEN PSOC6_CRYPTO \ - WOLFTPM WOLFBOOT_TPM_VERIFY MEASURED_BOOT WOLFBOOT_TPM_SEAL WOLFBOOT_TPM_KEYSTORE \ + WOLFTPM WOLFBOOT_TPM_VERIFY MEASURED_BOOT WOLFBOOT_TPM_SEAL WOLFBOOT_TPM_KEYSTORE \ + WOLFBOOT_ATTESTATION_IAK \ + WOLFBOOT_ATTESTATION_TEST \ WOLFCRYPT_TZ WOLFCRYPT_TZ_PKCS11 \ WOLFCRYPT_TZ_PSA \ WOLFBOOT_PARTITION_SIZE WOLFBOOT_SECTOR_SIZE \ diff --git a/zephyr/include/arm_tee_crypto_defs.h b/zephyr/include/arm_tee_crypto_defs.h index 7c7e049c15..fea90486b5 100644 --- a/zephyr/include/arm_tee_crypto_defs.h +++ b/zephyr/include/arm_tee_crypto_defs.h @@ -64,8 +64,15 @@ struct arm_tee_crypto_pack_iovec { #define ARM_TEE_CRYPTO_HASH_COMPUTE_SID (0x0300U) #define ARM_TEE_CRYPTO_HASH_SETUP_SID (0x0302U) #define ARM_TEE_CRYPTO_HASH_UPDATE_SID (0x0303U) +#define ARM_TEE_CRYPTO_HASH_CLONE_SID (0x0304U) #define ARM_TEE_CRYPTO_HASH_FINISH_SID (0x0305U) #define ARM_TEE_CRYPTO_HASH_ABORT_SID (0x0307U) +#define ARM_TEE_CRYPTO_CIPHER_ENCRYPT_SETUP_SID (0x0400U) +#define ARM_TEE_CRYPTO_CIPHER_DECRYPT_SETUP_SID (0x0401U) +#define ARM_TEE_CRYPTO_CIPHER_SET_IV_SID (0x0402U) +#define ARM_TEE_CRYPTO_CIPHER_UPDATE_SID (0x0403U) +#define ARM_TEE_CRYPTO_CIPHER_FINISH_SID (0x0404U) +#define ARM_TEE_CRYPTO_CIPHER_ABORT_SID (0x0405U) #define ARM_TEE_CRYPTO_ASYMMETRIC_SIGN_HASH_SID (0x0702U) #define ARM_TEE_CRYPTO_ASYMMETRIC_VERIFY_HASH_SID (0x0703U) diff --git a/zephyr/src/arm_tee_crypto_api.c b/zephyr/src/arm_tee_crypto_api.c index 37a7cb6860..5a9dde26cb 100644 --- a/zephyr/src/arm_tee_crypto_api.c +++ b/zephyr/src/arm_tee_crypto_api.c @@ -226,19 +226,22 @@ psa_status_t psa_hash_compute(psa_algorithm_t alg, psa_status_t psa_hash_setup(psa_hash_operation_t *operation, psa_algorithm_t alg) { + uint32_t op_handle = (uint32_t)operation->opaque; struct arm_tee_crypto_pack_iovec iov = { .function_id = ARM_TEE_CRYPTO_HASH_SETUP_SID, .alg = alg, - .op_handle = operation->handle, + .op_handle = op_handle, }; psa_invec in_vec[] = { {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, }; psa_outvec out_vec[] = { - {.base = &(operation->handle), .len = sizeof(uint32_t)}, + {.base = &op_handle, .len = sizeof(op_handle)}, }; - return API_DISPATCH(in_vec, out_vec); + psa_status_t status = API_DISPATCH(in_vec, out_vec); + operation->opaque = op_handle; + return status; } psa_status_t psa_hash_update(psa_hash_operation_t *operation, @@ -247,7 +250,7 @@ psa_status_t psa_hash_update(psa_hash_operation_t *operation, { struct arm_tee_crypto_pack_iovec iov = { .function_id = ARM_TEE_CRYPTO_HASH_UPDATE_SID, - .op_handle = operation->handle, + .op_handle = (uint32_t)operation->opaque, }; psa_invec in_vec[] = { {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, @@ -263,38 +266,232 @@ psa_status_t psa_hash_finish(psa_hash_operation_t *operation, size_t *hash_length) { psa_status_t status; + uint32_t op_handle = (uint32_t)operation->opaque; struct arm_tee_crypto_pack_iovec iov = { .function_id = ARM_TEE_CRYPTO_HASH_FINISH_SID, - .op_handle = operation->handle, + .op_handle = op_handle, }; psa_invec in_vec[] = { {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, }; psa_outvec out_vec[] = { - {.base = &(operation->handle), .len = sizeof(uint32_t)}, + {.base = &op_handle, .len = sizeof(op_handle)}, {.base = hash, .len = hash_size}, }; status = API_DISPATCH(in_vec, out_vec); *hash_length = out_vec[1].len; + operation->opaque = op_handle; return status; } psa_status_t psa_hash_abort(psa_hash_operation_t *operation) { + uint32_t op_handle = (uint32_t)operation->opaque; struct arm_tee_crypto_pack_iovec iov = { .function_id = ARM_TEE_CRYPTO_HASH_ABORT_SID, - .op_handle = operation->handle, + .op_handle = op_handle, }; psa_invec in_vec[] = { {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, }; psa_outvec out_vec[] = { - {.base = &(operation->handle), .len = sizeof(uint32_t)}, + {.base = &op_handle, .len = sizeof(op_handle)}, }; - return API_DISPATCH(in_vec, out_vec); + psa_status_t status = API_DISPATCH(in_vec, out_vec); + operation->opaque = op_handle; + return status; +} + +psa_status_t psa_hash_clone(const psa_hash_operation_t *source_operation, + psa_hash_operation_t *target_operation) +{ + uint32_t src_handle; + uint32_t dst_handle = 0; + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_HASH_CLONE_SID, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + }; + psa_outvec out_vec[] = { + {.base = &dst_handle, .len = sizeof(dst_handle)}, + }; + psa_status_t status; + + if (source_operation == NULL || target_operation == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + src_handle = (uint32_t)source_operation->opaque; + iov.op_handle = src_handle; + + status = API_DISPATCH(in_vec, out_vec); + if (status == PSA_SUCCESS) { + target_operation->opaque = dst_handle; + } + return status; +} + +psa_status_t psa_cipher_encrypt_setup(psa_cipher_operation_t *operation, + psa_key_id_t key, + psa_algorithm_t alg) +{ + uint32_t op_handle = 0; + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_CIPHER_ENCRYPT_SETUP_SID, + .key_id = key, + .alg = alg, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + }; + psa_outvec out_vec[] = { + {.base = &op_handle, .len = sizeof(op_handle)}, + }; + psa_status_t status; + + if (operation == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + status = API_DISPATCH(in_vec, out_vec); + if (status == PSA_SUCCESS) { + operation->opaque = op_handle; + } + return status; +} + +psa_status_t psa_cipher_decrypt_setup(psa_cipher_operation_t *operation, + psa_key_id_t key, + psa_algorithm_t alg) +{ + uint32_t op_handle = 0; + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_CIPHER_DECRYPT_SETUP_SID, + .key_id = key, + .alg = alg, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + }; + psa_outvec out_vec[] = { + {.base = &op_handle, .len = sizeof(op_handle)}, + }; + psa_status_t status; + + if (operation == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + status = API_DISPATCH(in_vec, out_vec); + if (status == PSA_SUCCESS) { + operation->opaque = op_handle; + } + return status; +} + +psa_status_t psa_cipher_set_iv(psa_cipher_operation_t *operation, + const uint8_t *iv, + size_t iv_length) +{ + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_CIPHER_SET_IV_SID, + .op_handle = (uint32_t)operation->opaque, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + {.base = iv, .len = iv_length}, + }; + + if (operation == NULL || (iv == NULL && iv_length > 0)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + return API_DISPATCH_NO_OUTVEC(in_vec); +} + +psa_status_t psa_cipher_update(psa_cipher_operation_t *operation, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_CIPHER_UPDATE_SID, + .op_handle = (uint32_t)operation->opaque, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + {.base = input, .len = input_length}, + }; + psa_outvec out_vec[] = { + {.base = output, .len = output_size}, + }; + psa_status_t status; + + if (operation == NULL || output_length == NULL || + (input == NULL && input_length > 0) || + (output == NULL && output_size > 0)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + status = API_DISPATCH(in_vec, out_vec); + if (status == PSA_SUCCESS) { + *output_length = out_vec[0].len; + } + return status; +} + +psa_status_t psa_cipher_finish(psa_cipher_operation_t *operation, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_CIPHER_FINISH_SID, + .op_handle = (uint32_t)operation->opaque, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + }; + psa_outvec out_vec[] = { + {.base = output, .len = output_size}, + }; + psa_status_t status; + + if (operation == NULL || output_length == NULL || + (output == NULL && output_size > 0)) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + status = API_DISPATCH(in_vec, out_vec); + if (status == PSA_SUCCESS) { + *output_length = out_vec[0].len; + operation->opaque = 0; + } + return status; +} + +psa_status_t psa_cipher_abort(psa_cipher_operation_t *operation) +{ + struct arm_tee_crypto_pack_iovec iov = { + .function_id = ARM_TEE_CRYPTO_CIPHER_ABORT_SID, + .op_handle = (uint32_t)operation->opaque, + }; + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct arm_tee_crypto_pack_iovec)}, + }; + + if (operation == NULL) { + return PSA_ERROR_INVALID_ARGUMENT; + } + + operation->opaque = 0; + return API_DISPATCH_NO_OUTVEC(in_vec); } psa_status_t psa_sign_hash(psa_key_id_t key, From 405e9657ce406789e33c5a1c2afc1fb30bfb7ec4 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 14 Jan 2026 16:25:40 +0100 Subject: [PATCH 2/6] Remove dice.o from non-tz targets --- Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 6ffda2c29c..352987798c 100644 --- a/Makefile +++ b/Makefile @@ -34,9 +34,12 @@ OBJS:= \ ./src/string.o \ ./src/image.o \ ./src/libwolfboot.o \ - ./src/dice/dice.o \ ./hal/hal.o +ifeq ($(WOLFCRYPT_TZ),1) +OBJS+=./src/dice/dice.o +endif + ifneq ($(TARGET),library) OBJS+=./hal/$(TARGET).o endif From 2d5c208fc16dcd8ff7af6ba7f3183e5168ca7fb8 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 14 Jan 2026 16:38:31 +0100 Subject: [PATCH 3/6] DICE requires WOLFBOOT_TZ_PSA=1. Add doc. --- CMakeLists.txt | 5 ++- Makefile | 2 +- docs/DICE.md | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 docs/DICE.md diff --git a/CMakeLists.txt b/CMakeLists.txt index e3d298de3f..19f657e9a1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -554,9 +554,12 @@ set(WOLFBOOT_SOURCES "include/loader.h" "include/image.h" "src/string.c" "src/image.c" - "src/dice/dice.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") diff --git a/Makefile b/Makefile index 352987798c..565dbd1882 100644 --- a/Makefile +++ b/Makefile @@ -36,7 +36,7 @@ OBJS:= \ ./src/libwolfboot.o \ ./hal/hal.o -ifeq ($(WOLFCRYPT_TZ),1) +ifeq ($(WOLFBOOT_TZ_PSA),1) OBJS+=./src/dice/dice.o endif diff --git a/docs/DICE.md b/docs/DICE.md new file mode 100644 index 0000000000..2eba4863b8 --- /dev/null +++ b/docs/DICE.md @@ -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. From f1a708c72378fb9fb9144f2d73879b3d2993c40e Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 14 Jan 2026 17:51:40 +0100 Subject: [PATCH 4/6] Fix test-app build with WOLFBOOT_TZ_PKCS11 --- test-app/Makefile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test-app/Makefile b/test-app/Makefile index e02a9663a3..20fe5e1d82 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -181,7 +181,9 @@ ifeq ($(TZEN),1) endif WOLFCRYPT_APP_OBJS := $(patsubst $(WOLFBOOT_LIB_WOLFSSL)/%, \ $(WOLFSSL_LOCAL_OBJDIR)/%, $(WOLFCRYPT_APP_OBJS)) - APP_OBJS+=$(sort $(WOLFCRYPT_APP_OBJS)) + ifneq ($(WOLFCRYPT_TZ_PKCS11),1) + APP_OBJS+=$(sort $(WOLFCRYPT_APP_OBJS)) + endif else APP_OBJS+=../hal/$(TARGET).o endif From ce44ced0f0b32eacef5d8eb6f65b28db89d980f5 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 14 Jan 2026 18:01:49 +0100 Subject: [PATCH 5/6] Fix regression: unreachable objects in app build --- test-app/Makefile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/test-app/Makefile b/test-app/Makefile index 20fe5e1d82..81bc24cdca 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -598,6 +598,9 @@ ifeq ($(TARGET),aurix_tc3xx) endif endif +# Capture final flags for locally built wolfSSL objects. +WOLFSSL_CFLAGS:=$(CFLAGS) + ifeq ($(WOLFHSM_CLIENT),1) CFLAGS += -DSTRING_USER -I"$(WOLFBOOT_LIB_WOLFSSL)" APP_OBJS += $(WOLFHSM_OBJS) @@ -677,7 +680,7 @@ delta-extra-data: image.bin $(WOLFSSL_LOCAL_OBJDIR)/%.o: %.c @echo "\t[CC-$(ARCH)] $@" $(Q)mkdir -p $(dir $@) - $(Q)$(CC) $(CFLAGS) -c $(OUTPUT_FLAG) $@ $< + $(Q)$(CC) $(WOLFSSL_CFLAGS) -c $(OUTPUT_FLAG) $@ $< clean: $(Q)rm -f *.bin *.elf tags *.o $(LSCRIPT) $(APP_OBJS) wcs/*.o From d1ff677e330bc98bc1801d9e184a769f87ae24fc Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 14 Jan 2026 18:38:26 +0100 Subject: [PATCH 6/6] Fixed otp tool build regression --- tools/keytools/otp/Makefile | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/tools/keytools/otp/Makefile b/tools/keytools/otp/Makefile index 7b44f3b145..e02c6b117b 100644 --- a/tools/keytools/otp/Makefile +++ b/tools/keytools/otp/Makefile @@ -7,14 +7,26 @@ ifeq ($(V),0) Q=@ endif +WOLFBOOT_LIB_WOLFSSL?=../../../lib/wolfssl + TARGET?=none ARCH?=ARM CROSS_COMPILE?=arm-none-eabi- CFLAGS+=-O0 -ggdb -CFLAGS+=-I. -I../../../ -I../../../include +CFLAGS+=-I. -I../../../ -I../../../include -I../../../lib/wolfssl CFLAGS+=-I./wcs -CFLAGS+=-DFLASH_OTP_KEYSTORE -D__FLASH_OTP_PRIMER +CFLAGS+=-DFLASH_OTP_KEYSTORE -D__FLASH_OTP_PRIMER -DWOLFSSL_USER_SETTINGS PRI_KS_OBJS+=startup.o otp-keystore-primer.o ../../../src/keystore.o + +ifeq ($(HASH),SHA256) + PRI_KS_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.o +endif +ifeq ($(HASH),SHA384) + PRI_KS_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha512.o +endif +ifeq ($(HASH),SHA3) + PRI_KS_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha3.o +endif LSCRIPT=target.ld LDFLAGS+=$(CFLAGS) -T$(LSCRIPT) -lc -Wl,-Map=otp-keystore-primer.map