From b65752a538902a500f9f36ad51f10ecf7f7289d6 Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Wed, 3 Jun 2026 10:29:03 -0400 Subject: [PATCH 01/25] Initial implementation --- arch.mk | 7 +++++ config/examples/lpc55s69-hwpuf.config | 44 +++++++++++++++++++++++++++ test-app/Makefile | 6 ++++ 3 files changed, 57 insertions(+) create mode 100644 config/examples/lpc55s69-hwpuf.config diff --git a/arch.mk b/arch.mk index 38f1c682c7..1bb1447537 100644 --- a/arch.mk +++ b/arch.mk @@ -2121,6 +2121,7 @@ ifeq ($(TARGET),lpc55s69) -I$(MCUXPRESSO)/drivers/iap1 \ -I$(MCUXPRESSO)/drivers/lpc_gpio \ -I$(MCUXPRESSO)/drivers/lpc_iocon \ + -I$(MCUXPRESSO)/drivers/puf \ -I$(MCUXPRESSO)/drivers/rng_1 \ -I$(MCUXPRESSO_CMSIS)/Include \ -I$(MCUXPRESSO_CMSIS)/Core/Include @@ -2152,6 +2153,12 @@ ifeq ($(TARGET),lpc55s69) $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/casper_port.o \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/hashcrypt_port.o endif + ifeq ($(HWPUF),1) + CFLAGS+=-DWOLFSSL_HWPUF -DWOLFSSL_NXP_PUF + OBJS+=\ + $(MCUXPRESSO)/drivers/puf/fsl_puf.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/hwpuf_port.o + endif endif ifeq ($(TARGET),psoc6) diff --git a/config/examples/lpc55s69-hwpuf.config b/config/examples/lpc55s69-hwpuf.config new file mode 100644 index 0000000000..7d0d252597 --- /dev/null +++ b/config/examples/lpc55s69-hwpuf.config @@ -0,0 +1,44 @@ +ARCH?=ARM +TZEN?=0 +TARGET?=lpc55s69 +SIGN?=ECC384 +HASH?=SHA256 +MCUXSDK?=1 +MCUXPRESSO?=$(PWD)/../NXP/mcuxpresso-sdk/mcuxsdk +MCUXPRESSO_CMSIS?=$(PWD)/../NXP/CMSIS_5/CMSIS +MCUXPRESSO_CPU?=LPC55S69JBD100_cm33_core0 +MCUXPRESSO_DRIVERS?=$(MCUXPRESSO)/devices/LPC/LPC5500/LPC55S69 +MCUXPRESSO_PROJECT_TEMPLATE?=$(MCUXPRESSO)/examples/_boards/lpcxpresso55s69/project_template +DEBUG?=0 +DEBUG_UART?=1 +VTOR?=1 +CORTEX_M0?=0 +CORTEX_M33?=1 +NO_ASM?=0 +NO_MPU=1 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 +NVM_FLASH_WRITEONCE?=1 +NO_ARM_ASM=1 +WOLFBOOT_VERSION?=0 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +DUALBANK_SWAP?=0 +FLASH_MULTI_SECTOR_ERASE?=1 + +# Turn on or off hw acceleration of crypto algs in the lpc55s69 +PKA?=0 +# Turn on or off hw puf +HWPUF?=1 + +WOLFBOOT_SECTOR_SIZE?=0x200 + +# use these for test/benchmark +WOLFBOOT_PARTITION_SIZE?=0x2b000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x10000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x3b000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x66000 + +WOLFCRYPT_TEST?=1 diff --git a/test-app/Makefile b/test-app/Makefile index 103bef92d0..f8368916fd 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -1197,6 +1197,12 @@ ifeq ($(TARGET),lpc55s69) $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/casper_port.o \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/hashcrypt_port.o endif + ifeq ($(HWPUF),1) + CFLAGS+=-DWOLFSSL_HWPUF -DWOLFSSL_NXP_PUF + APP_OBJS+=\ + $(MCUXPRESSO)/drivers/puf/fsl_puf.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/hwpuf_port.o + endif ifeq ($(WOLFCRYPT_SUPPORT),1) LDFLAGS+=--specs=nano.specs endif From d569f682ea32e180a35b3400e09bf9f2e287bfa6 Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Thu, 11 Jun 2026 14:12:49 -0400 Subject: [PATCH 02/25] fixes --- arch.mk | 3 ++- test-app/Makefile | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/arch.mk b/arch.mk index 1bb1447537..6d3512adc1 100644 --- a/arch.mk +++ b/arch.mk @@ -2154,9 +2154,10 @@ ifeq ($(TARGET),lpc55s69) $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/hashcrypt_port.o endif ifeq ($(HWPUF),1) - CFLAGS+=-DWOLFSSL_HWPUF -DWOLFSSL_NXP_PUF + CFLAGS+=-DWOLFSSL_HWPUF -DWOLFSSL_NXP_HWPUF OBJS+=\ $(MCUXPRESSO)/drivers/puf/fsl_puf.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/cryptocb.o \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/hwpuf_port.o endif endif diff --git a/test-app/Makefile b/test-app/Makefile index f8368916fd..c85cea4fda 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -1198,9 +1198,11 @@ ifeq ($(TARGET),lpc55s69) $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/hashcrypt_port.o endif ifeq ($(HWPUF),1) - CFLAGS+=-DWOLFSSL_HWPUF -DWOLFSSL_NXP_PUF + CFLAGS+=-DWOLFSSL_HWPUF -DWOLFSSL_NXP_HWPUF APP_OBJS+=\ $(MCUXPRESSO)/drivers/puf/fsl_puf.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/cryptocb.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/hwpuf.o \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/hwpuf_port.o endif ifeq ($(WOLFCRYPT_SUPPORT),1) From 057ea0ef78c143c337426a3f03796a809cdff7ff Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Thu, 11 Jun 2026 14:17:40 -0400 Subject: [PATCH 03/25] default HWPUF off --- config/examples/lpc55s69-hwpuf.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/examples/lpc55s69-hwpuf.config b/config/examples/lpc55s69-hwpuf.config index 7d0d252597..30385a1d1b 100644 --- a/config/examples/lpc55s69-hwpuf.config +++ b/config/examples/lpc55s69-hwpuf.config @@ -31,7 +31,7 @@ FLASH_MULTI_SECTOR_ERASE?=1 # Turn on or off hw acceleration of crypto algs in the lpc55s69 PKA?=0 # Turn on or off hw puf -HWPUF?=1 +HWPUF?=0 WOLFBOOT_SECTOR_SIZE?=0x200 From 8fb226dd48c2b7690d22fbad3dc625715380189f Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Thu, 25 Jun 2026 14:16:18 -0400 Subject: [PATCH 04/25] Add psa attestation support for lpc55s69 --- .github/workflows/test-configs.yml | 21 ++ arch.mk | 1 + config/examples/lpc55s69-tz-psa.config | 48 ++++ docs/Targets.md | 1 + hal/lpc55s69.c | 308 ++++++++++++++++++++++++- options.mk | 7 + test-app/ARM-lpc55s69-ns.ld | 22 +- test-app/app_lpc55s69.c | 75 ++++++ 8 files changed, 475 insertions(+), 8 deletions(-) create mode 100644 config/examples/lpc55s69-tz-psa.config diff --git a/.github/workflows/test-configs.yml b/.github/workflows/test-configs.yml index d3752fe8d5..0dd6516ff3 100644 --- a/.github/workflows/test-configs.yml +++ b/.github/workflows/test-configs.yml @@ -178,6 +178,27 @@ jobs: config-file: ./config/examples/lpc55s69-tz.config board-name: lpcxpresso55s69 + lpc55s69_tz_psa_test: + uses: ./.github/workflows/test-build-mcux-sdk-manifests.yml + with: + arch: arm + config-file: ./config/examples/lpc55s69-tz-psa.config + board-name: lpcxpresso55s69 + + lpc55s69_hwpuf_test: + uses: ./.github/workflows/test-build-mcux-sdk-manifests.yml + with: + arch: arm + config-file: ./config/examples/lpc55s69-hwpuf.config + board-name: lpcxpresso55s69 + + lpc55s69_benchmark_test: + uses: ./.github/workflows/test-build-mcux-sdk-manifests.yml + with: + arch: arm + config-file: ./config/examples/lpc55s69-benchmark.config + board-name: lpcxpresso55s69 + m2354_test: uses: ./.github/workflows/test-build.yml with: diff --git a/arch.mk b/arch.mk index 6d3512adc1..dfaa8a12b5 100644 --- a/arch.mk +++ b/arch.mk @@ -2158,6 +2158,7 @@ ifeq ($(TARGET),lpc55s69) OBJS+=\ $(MCUXPRESSO)/drivers/puf/fsl_puf.o \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/cryptocb.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/hwpuf.o \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/hwpuf_port.o endif endif diff --git a/config/examples/lpc55s69-tz-psa.config b/config/examples/lpc55s69-tz-psa.config new file mode 100644 index 0000000000..6dab4ff2bf --- /dev/null +++ b/config/examples/lpc55s69-tz-psa.config @@ -0,0 +1,48 @@ +ARCH?=ARM +TZEN?=1 +TARGET?=lpc55s69 +SIGN?=ECC384 +HASH?=SHA256 +MCUXSDK?=1 +MCUXPRESSO?=$(PWD)/../NXP/mcuxpresso-sdk/mcuxsdk +MCUXPRESSO_CMSIS?=$(PWD)/../NXP/CMSIS_5/CMSIS +MCUXPRESSO_CPU?=LPC55S69JBD100_cm33_core0 +MCUXPRESSO_DRIVERS?=$(MCUXPRESSO)/devices/LPC/LPC5500/LPC55S69 +MCUXPRESSO_PROJECT_TEMPLATE?=$(MCUXPRESSO)/examples/_boards/lpcxpresso55s69/project_template +DEBUG?=0 +DEBUG_UART?=1 +VTOR?=1 +CORTEX_M0?=0 +CORTEX_M33?=1 +NO_ASM?=0 +NO_MPU=1 +EXT_FLASH?=0 +SPI_FLASH?=0 +ALLOW_DOWNGRADE?=0 +NVM_FLASH_WRITEONCE?=1 +NO_ARM_ASM=1 +WOLFBOOT_VERSION?=0 +V?=0 +SPMATH?=1 +RAM_CODE?=1 +DUALBANK_SWAP?=0 +FLASH_MULTI_SECTOR_ERASE?=1 +WOLFCRYPT_TZ?=1 +WOLFCRYPT_TZ_PSA?=1 +WOLFBOOT_ATTESTATION_TEST?=1 +WOLFBOOT_UDS_UID_FALLBACK_FORTEST?=0 +PKA?=0 +HWPUF?=0 +WOLFBOOT_HWPUF_PROVISION?=0 + +WOLFBOOT_SECTOR_SIZE?=0x200 + +# 200KB boot, 80KB keyvault, 8KB NSC, 56KB partitions, 512 swap +WOLFBOOT_KEYVAULT_ADDRESS?=0x10032000 +WOLFBOOT_KEYVAULT_SIZE?=0x14000 +WOLFBOOT_NSC_ADDRESS?=0x10046000 +WOLFBOOT_NSC_SIZE?=0x2000 +WOLFBOOT_PARTITION_SIZE?=0xE000 +WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x00048000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x00056000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x00064000 diff --git a/docs/Targets.md b/docs/Targets.md index c51bedc972..abd37ad6e2 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -3422,6 +3422,7 @@ support is turned off in the config files, and all crypto is software based. To turn on hardware acceleration, set PKA=1 in the config file. Basic hardware acceleration supported: +- hardware PUF (HWPUF) - SHA1 - SHA-256 - AES-ECB (128, 192, 256 key sizes) diff --git a/hal/lpc55s69.c b/hal/lpc55s69.c index 7e461c3195..da0fae0f0c 100644 --- a/hal/lpc55s69.c +++ b/hal/lpc55s69.c @@ -28,20 +28,312 @@ #include "clock_config.h" #include "fsl_clock.h" #include "fsl_iap.h" +#include "fsl_iap_ffr.h" #include "fsl_iocon.h" +#include "fsl_puf.h" #include "fsl_reset.h" #include "fsl_rng.h" #include "fsl_usart.h" +#include "hal.h" #include "loader.h" #ifdef TZEN #include "hal/armv8m_tz.h" #endif +#include +#include + +#ifdef WOLFSSL_HWPUF +#include +#endif + +#if defined(WOLFCRYPT_TZ_PSA) +# if defined(WOLFBOOT_HASH_SHA256) +# include +# define HAL_KDF_HASH_TYPE WC_HASH_TYPE_SHA256 +# elif defined(WOLFBOOT_HASH_SHA384) +# include +# define HAL_KDF_HASH_TYPE WC_HASH_TYPE_SHA384 +# elif defined(WOLFBOOT_HASH_SHA3_384) +# include +# define HAL_KDF_HASH_TYPE WC_HASH_TYPE_SHA3_384 +# else +# error "No supported hash type for HAL_KDF" +# endif +#endif + static flash_config_t pflash; static const uint32_t pflash_page_size = 512U; uint32_t SystemCoreClock; /* set in clock_config.c */ +static void flashInit(void) +{ + static int initialized = 0; + + if (!initialized) { + XMEMSET(&pflash, 0, sizeof(pflash)); + FLASH_Init(&pflash); + FFR_Init(&pflash); + initialized = 1; + } +} + +static NOINLINEFUNCTION void hal_zeroize(void *ptr, size_t len) +{ + volatile uint8_t *p = (volatile uint8_t *)ptr; + while (len-- > 0U) { + *p++ = 0U; + } +} + + +#if defined(__WOLFBOOT) && defined(WOLFSSL_HWPUF) +#define HWPUF_PROV_MAGIC 0x564f5250ul /* "PROV" */ +#define HWPUF_PROV_FLASH_BASEADR 0x80000ul +#define HWPUF_PROV_FLASH_LEN 0x1000ul +#define HWPUF_PROV_UDS_KEY_INDEX 14 /* NOT the one in PFR */ +#define HWPUF_PROV_UDS_KEY_SIZE 32 + +typedef struct hwpuf_prov { + word32 magic; + byte ac[HWPUF_ACTIVATION_CODE_SIZE]; + byte uds_kc[PUF_GET_KEY_CODE_SIZE_FOR_KEY_SIZE(HWPUF_PROV_UDS_KEY_SIZE)]; +} hwpuf_prov; + +static hwpuf_prov prov; + +static int hwpuf_provision_get(void) +{ + if (sizeof(prov) > HWPUF_PROV_FLASH_LEN) + return -1; + + flashInit(); + + /* verify none of sectors in erased state */ + { + uint32_t addr; + uint32_t start = HWPUF_PROV_FLASH_BASEADR; + uint32_t end = HWPUF_PROV_FLASH_BASEADR + HWPUF_PROV_FLASH_LEN; + + for (addr = start; addr < end; addr += pflash_page_size) { + if (FLASH_VerifyErase(&pflash, addr, pflash_page_size) + == kStatus_FLASH_Success) { + return -1; + } + } + } + + XMEMCPY(&prov, (byte *)HWPUF_PROV_FLASH_BASEADR, sizeof(prov)); + + if (prov.magic != HWPUF_PROV_MAGIC) + return -1; + + return 0; +} + +#ifdef WOLFBOOT_HWPUF_PROVISION +static int hwpuf_provision_set(int force) +{ + int ret = -1; + wc_HWPUF hwpuf; + + if (sizeof(prov) > HWPUF_PROV_FLASH_LEN) + return -1; + + if (!force) { + if (hwpuf_provision_get() == 0) + return -1; + } + + XMEMSET(&prov, 0, sizeof(prov)); + + if (wc_HWPUF_Register(&hwpuf, NULL, INVALID_DEVID) != 0) + return -1; + + if (wc_HWPUF_Init(&hwpuf) != 0) + goto error_out; + + if (wc_HWPUF_Enroll(&hwpuf, prov.ac, sizeof(prov.ac)) != 0) + goto error_out; + + (void)wc_HWPUF_Deinit(&hwpuf); + (void)wc_HWPUF_Init(&hwpuf); + + if (wc_HWPUF_Start(&hwpuf, prov.ac, sizeof(prov.ac)) != 0) + goto error_out; + + if (wc_HWPUF_GenerateKey(&hwpuf, + HWPUF_PROV_UDS_KEY_INDEX, HWPUF_PROV_UDS_KEY_SIZE, + prov.uds_kc, sizeof(prov.uds_kc)) != 0) + goto error_out; + + prov.magic = HWPUF_PROV_MAGIC; + + flashInit(); + + hal_flash_unlock(); + ret = hal_flash_erase(HWPUF_PROV_FLASH_BASEADR, HWPUF_PROV_FLASH_LEN); + if (ret != 0) { + hal_flash_lock(); + goto error_out; + } + ret = hal_flash_write(HWPUF_PROV_FLASH_BASEADR, + (const uint8_t *)&prov, sizeof(prov)); + hal_flash_lock(); + if (ret != 0) + goto error_out; + + ret = 0; + +error_out: + hal_zeroize(&prov, sizeof(prov)); + (void)wc_HWPUF_Zeroize(&hwpuf); + (void)wc_HWPUF_Deinit(&hwpuf); + (void)wc_HWPUF_Unregister(&hwpuf); + return ret; +} +#endif /* WOLFBOOT_HWPUF_PROVISION */ + +#if defined(WOLFCRYPT_TZ_PSA) +static int uds_from_hwpuf(uint8_t *out, size_t out_len) +{ + int ret = -1; + wc_HWPUF hwpuf; + byte uds[HWPUF_PROV_UDS_KEY_SIZE]; + + if (hwpuf_provision_get() != 0) + return -1; + + if (wc_HWPUF_Register(&hwpuf, NULL, INVALID_DEVID) != 0) + return -1; + + if (wc_HWPUF_Init(&hwpuf) != 0) + goto error_out; + + if (wc_HWPUF_Start(&hwpuf, prov.ac, sizeof(prov.ac)) != 0) + goto error_out; + + ret = wc_HWPUF_GetKey(&hwpuf, prov.uds_kc, sizeof(prov.uds_kc), + uds, sizeof(uds)); + if (ret != 0) + goto error_out; + + ret = wc_HKDF_ex((int)HAL_KDF_HASH_TYPE, uds, (word32)sizeof(uds), + (const byte *)"WOLFBOOT-UDS", (word32)12, + (const byte *)"WOLFBOOT-UDS", (word32)12, + (byte *)out, (word32)out_len, + NULL, INVALID_DEVID); + if (ret != 0) + goto error_out; + +error_out: + hal_zeroize(uds, sizeof(uds)); + hal_zeroize(&prov, sizeof(prov)); + (void)wc_HWPUF_Zeroize(&hwpuf); + (void)wc_HWPUF_Deinit(&hwpuf); + (void)wc_HWPUF_Unregister(&hwpuf); + return ret == 0 ? 0 : -1; +} +#endif /* WOLFCRYPT_TZ_PSA */ +#endif /* __WOLFBOOT && WOLFSSL_HWPUF */ + +#if defined(__WOLFBOOT) && defined(WOLFCRYPT_TZ_PSA) +static void reverse_array(byte *s, int len) +{ + int up, dn; + + if (s == NULL) + return; + + up = 0; + dn = len - 1; + while (up < dn) { + byte t = s[up]; + s[up] = s[dn]; + s[dn] = t; + ++up; + --dn; + } +} +#endif + +#if defined(__WOLFBOOT) && defined(WOLFCRYPT_TZ_PSA) && !defined(WOLFBOOT_DICE_HW) +#ifdef WOLFBOOT_UDS_UID_FALLBACK_FORTEST +static int uds_from_uid(uint8_t *out, size_t out_len) +{ + uint8_t uid[16]; +#if defined(WOLFBOOT_HASH_SHA256) + uint8_t digest[SHA256_DIGEST_SIZE]; +#elif defined(WOLFBOOT_HASH_SHA384) + uint8_t digest[SHA384_DIGEST_SIZE]; +#elif defined(WOLFBOOT_HASH_SHA3_384) + uint8_t digest[SHA3_384_DIGEST_SIZE]; +#endif + size_t copy_len = sizeof(digest); + + flashInit(); + + if (FFR_GetUUID(&pflash, uid) != kStatus_Success) + return -1; + reverse_array(uid, sizeof(uid)); + +#if defined(WOLFBOOT_HASH_SHA256) + { + wc_Sha256 hash; + wc_InitSha256(&hash); + wc_Sha256Update(&hash, uid, sizeof(uid)); + wc_Sha256Final(&hash, digest); + } +#elif defined(WOLFBOOT_HASH_SHA384) + { + wc_Sha384 hash; + wc_InitSha384(&hash); + wc_Sha384Update(&hash, uid, sizeof(uid)); + 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, uid, sizeof(uid)); + wc_Sha3_384_Final(&hash, digest); + } +#endif + + if (copy_len > out_len) { + copy_len = out_len; + } + memcpy(out, digest, copy_len); +#ifdef DEBUG + { + int idx; + wolfBoot_printf("[ATTEST] UDS FORTEST:"); + for (idx = 0; idx < (int)out_len; ++idx) + wolfBoot_printf(" %02x", out[idx]); + wolfBoot_printf("\n"); + } +#endif + return 0; +} +#endif /* WOLFBOOT_UDS_UID_FALLBACK_FORTEST */ + +/* Derive UDS from hw puf, fall back to derive from uid */ +int hal_uds_derive_key(uint8_t *out, size_t out_len) +{ + if (out == NULL || out_len == 0) + return -1; + +#ifdef WOLFBOOT_UDS_UID_FALLBACK_FORTEST + return uds_from_uid(out, out_len); +#elif defined(WOLFSSL_HWPUF) + return uds_from_hwpuf(out, out_len); +#else + return -1; +#endif +} +#endif /* __WOLFBOOT && WOLFCRYPT_TZ_PSA && !WOLFBOOT_DICE_HW */ + #ifdef TZEN static void hal_sau_init(void) { @@ -55,7 +347,7 @@ static void hal_sau_init(void) 0); /* Non-secure RAM */ - sau_init_region(2, 0x20020000, 0x20027FFF, 0); + sau_init_region(2, 0x20020000, 0x2002FFFF, 0); /* Peripherals */ sau_init_region(3, 0x40000000, 0x4003FFFF, 0); @@ -121,14 +413,24 @@ void hal_init(void) #endif /* __WOLFBOOT */ #if defined(__WOLFBOOT) || !defined(TZEN) - memset(&pflash, 0, sizeof(pflash)); - FLASH_Init(&pflash); + flashInit(); hal_flash_fix_ecc(); #endif #if defined(TZEN) && !defined(NONSECURE_APP) hal_sau_init(); #endif + +#ifdef __WOLFBOOT + wolfCrypt_Init(); +#endif + +#if defined(__WOLFBOOT) && defined(WOLFBOOT_HWPUF_PROVISION) + if (hwpuf_provision_set(0) != 0) + uart_write("hwpuf provision failure (already provisioned?)\n", 47); + else + uart_write("hwpuf provision success\n", 24); +#endif } #ifdef __WOLFBOOT diff --git a/options.mk b/options.mk index ec2edff8b3..5811e17f6e 100644 --- a/options.mk +++ b/options.mk @@ -1190,6 +1190,13 @@ ifeq ($(WOLFBOOT_DICE_HW),1) endif endif +ifeq ($(WOLFBOOT_HWPUF_PROVISION),1) + ifneq ($(HWPUF),1) + $(error WOLFBOOT_HWPUF_PROVISION requires HWPUF=1) + endif + CFLAGS+=-DWOLFBOOT_HWPUF_PROVISION +endif + ifeq ($(PKCS11_STORE_STATS),1) CFLAGS+=-DPKCS11_STORE_STATS endif diff --git a/test-app/ARM-lpc55s69-ns.ld b/test-app/ARM-lpc55s69-ns.ld index aa3200f9eb..ea288a69a2 100644 --- a/test-app/ARM-lpc55s69-ns.ld +++ b/test-app/ARM-lpc55s69-ns.ld @@ -1,7 +1,10 @@ +_Min_Heap_Size = 0x00000100; /* minimal heap (not using malloc) */ +_Min_Stack_Size = 0x00008000; /* required amount of stack */ + MEMORY { FLASH (rx) : ORIGIN = @WOLFBOOT_TEST_APP_ADDRESS@, LENGTH = @WOLFBOOT_TEST_APP_SIZE@ - RAM (rwx) : ORIGIN = 0x20020000, LENGTH = 32K + RAM (rwx) : ORIGIN = 0x20020000, LENGTH = 64K } SECTIONS @@ -46,13 +49,22 @@ SECTIONS _end_bss = .; _end = .; } > RAM + + ._user_heap_stack : + { + . = ALIGN(8); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + PROVIDE ( _start_heap = . ); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(8); + PROVIDE ( END_STACK = . ); + PROVIDE ( _end_stack = . ); + } > RAM } _wolfboot_partition_boot_address = @WOLFBOOT_PARTITION_BOOT_ADDRESS@; _wolfboot_partition_size = @WOLFBOOT_PARTITION_SIZE@; _wolfboot_partition_update_address = @WOLFBOOT_PARTITION_UPDATE_ADDRESS@; _wolfboot_partition_swap_address = @WOLFBOOT_PARTITION_SWAP_ADDRESS@; - -PROVIDE(_start_heap = _end); -PROVIDE(end = _end); -PROVIDE(_end_stack = ORIGIN(RAM) + LENGTH(RAM)); diff --git a/test-app/app_lpc55s69.c b/test-app/app_lpc55s69.c index 5bacdf98fb..72c85df307 100644 --- a/test-app/app_lpc55s69.c +++ b/test-app/app_lpc55s69.c @@ -41,6 +41,12 @@ int wolfcrypt_test(void *args); int benchmark_test(void *args); #endif +#ifdef WOLFCRYPT_TZ_PSA +#include "psa/crypto.h" +#include "psa/error.h" +#include "psa/initial_attestation.h" +#endif + #define RED_LED 6 #define GREEN_LED 7 #define BLUE_LED 4 @@ -128,6 +134,70 @@ static void check_parts( wolfBoot_printf(" update: ver=0x%lx state=0x%02x\n", *pupdate_ver, *pupdate_state); } +#if defined(WOLFBOOT_ATTESTATION_TEST) && defined(WOLFCRYPT_TZ_PSA) + +#define LINE_LEN 16 +static void print_hex(const uint8_t *buffer, uint32_t length, int dumpChars) +{ + uint32_t i, sz; + + if (!buffer) { + wolfBoot_printf("\tNULL\n"); + return; + } + + while (length > 0) { + sz = length; + if (sz > LINE_LEN) + sz = LINE_LEN; + + wolfBoot_printf("\t"); + for (i = 0; i < LINE_LEN; i++) { + if (i < sz) + wolfBoot_printf("%02x ", buffer[i]); + else + wolfBoot_printf(" "); + } + if (dumpChars) { + wolfBoot_printf("| "); + for (i = 0; i < sz; i++) { + if (buffer[i] > 31 && buffer[i] < 127) + wolfBoot_printf("%c", buffer[i]); + else + wolfBoot_printf("."); + } + } + wolfBoot_printf("\n"); + + buffer += sz; + length -= sz; + } +} + +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(challenge, sizeof(challenge), + token, sizeof(token), &token_size); + if (status != PSA_SUCCESS) { + wolfBoot_printf("attest: get token failed (%d)\n", status); + return -1; + } + wolfBoot_printf("attest: token size %lu bytes\n", (unsigned long)token_size); + print_hex(token, (uint32_t)token_size, 1); + return 0; +} +#endif /* WOLFBOOT_ATTESTATION_TEST && WOLFCRYPT_TZ_PSA */ + void main(void) { uint32_t boot_ver, update_ver; @@ -184,6 +254,11 @@ void main(void) GPIO_PinWrite(GPIO, 1, GREEN_LED, 0); } +#if defined(WOLFBOOT_ATTESTATION_TEST) && defined(WOLFCRYPT_TZ_PSA) + if (run_attestation_test() != 0) + wolfBoot_printf("Make sure WOLFBOOT_HWPUF_PROVISION is set\n"); +#endif + #if defined(WOLFCRYPT_TEST) || defined(WOLFCRYPT_BENCHMARK) wolfCrypt_Init(); From 2dc0488d08b29ed7160bac9250ee43b8049c4010 Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Thu, 25 Jun 2026 23:40:54 -0400 Subject: [PATCH 05/25] Update docs, tweak configs --- config/examples/lpc55s69-benchmark.config | 2 - config/examples/lpc55s69-tz-psa.config | 2 +- config/examples/lpc55s69-tz.config | 4 +- config/examples/lpc55s69.config | 4 +- docs/Targets.md | 55 +++++++++++++++++++++-- 5 files changed, 54 insertions(+), 13 deletions(-) diff --git a/config/examples/lpc55s69-benchmark.config b/config/examples/lpc55s69-benchmark.config index 9804dedae5..8d16996c4b 100644 --- a/config/examples/lpc55s69-benchmark.config +++ b/config/examples/lpc55s69-benchmark.config @@ -31,8 +31,6 @@ FLASH_MULTI_SECTOR_ERASE?=1 # Turn on or off hw acceleration of crypto algs in the lpc55s69 PKA?=0 -# use 1024-byte sector to accommodate RSA4096 signature -# WOLFBOOT_SECTOR_SIZE?=0x400 WOLFBOOT_SECTOR_SIZE?=0x200 # use these for test/benchmark diff --git a/config/examples/lpc55s69-tz-psa.config b/config/examples/lpc55s69-tz-psa.config index 6dab4ff2bf..2afc62fef9 100644 --- a/config/examples/lpc55s69-tz-psa.config +++ b/config/examples/lpc55s69-tz-psa.config @@ -30,7 +30,7 @@ FLASH_MULTI_SECTOR_ERASE?=1 WOLFCRYPT_TZ?=1 WOLFCRYPT_TZ_PSA?=1 WOLFBOOT_ATTESTATION_TEST?=1 -WOLFBOOT_UDS_UID_FALLBACK_FORTEST?=0 +WOLFBOOT_UDS_UID_FALLBACK_FORTEST?=1 PKA?=0 HWPUF?=0 WOLFBOOT_HWPUF_PROVISION?=0 diff --git a/config/examples/lpc55s69-tz.config b/config/examples/lpc55s69-tz.config index 58d4fb6f6a..5fce4fde8d 100644 --- a/config/examples/lpc55s69-tz.config +++ b/config/examples/lpc55s69-tz.config @@ -33,11 +33,9 @@ WOLFCRYPT_TZ_PKCS11?=1 # Turn on or off hw acceleration of crypto algs in the lpc55s69 PKA?=0 -# use 1024-byte sector to accommodate RSA4096 signature -# WOLFBOOT_SECTOR_SIZE?=0x400 WOLFBOOT_SECTOR_SIZE?=0x200 -# 200KB boot, 80KB keyvault, 8KB NSC, 56KB partitions, 512/1024 swap +# 200KB boot, 80KB keyvault, 8KB NSC, 56KB partitions, 512 swap WOLFBOOT_KEYVAULT_ADDRESS?=0x10032000 WOLFBOOT_KEYVAULT_SIZE?=0x14000 WOLFBOOT_NSC_ADDRESS?=0x10046000 diff --git a/config/examples/lpc55s69.config b/config/examples/lpc55s69.config index 57c6647c3f..8643b1dcbe 100644 --- a/config/examples/lpc55s69.config +++ b/config/examples/lpc55s69.config @@ -31,12 +31,10 @@ FLASH_MULTI_SECTOR_ERASE?=1 # Turn on or off hw acceleration of crypto algs in the lpc55s69 PKA?=0 -# use 1024-byte sector to accommodate RSA4096 signature -# WOLFBOOT_SECTOR_SIZE?=0x400 WOLFBOOT_SECTOR_SIZE?=0x200 # Default configuration -# 44KB boot, 52KB partitions, 512/1024 swap +# 44KB boot, 52KB partitions, 512 swap WOLFBOOT_PARTITION_SIZE?=0xB000 WOLFBOOT_PARTITION_BOOT_ADDRESS?=0xD000 WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x18000 diff --git a/docs/Targets.md b/docs/Targets.md index abd37ad6e2..d19991dfc0 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -3422,7 +3422,6 @@ support is turned off in the config files, and all crypto is software based. To turn on hardware acceleration, set PKA=1 in the config file. Basic hardware acceleration supported: -- hardware PUF (HWPUF) - SHA1 - SHA-256 - AES-ECB (128, 192, 256 key sizes) @@ -3436,6 +3435,57 @@ Basic hardware acceleration supported: See [Test and Benchmark](#lpc55s69-test-and-benchmark) for a comparison with and without hardware acceleration. +### LPC55S69: Hardware PUF (HWPUF) + +wolfBoot / wolfCrypt provide support for the LPC55S69's SRAM PUF (Physically +Unclonable Function), which provides root of trust, identity, and key +generation & storage, without the need to expose keys during manufacturing. +Keys of size 128, 192, and 256 bits are supported. +To turn on HWPUF, set HWPUF=1 in the config file (see below). + +### LPC55S69: Example Configurations + +#### The following example configurations are provided: + +- [lpc55s69.config](/config/examples/lpc55s69.config): + - Both wolfBoot and the test-app live in the non-secure realm + - Set PKA=1 in the config file to turn on hardware acceleration + (off by default) + +- [lpc55s69-benchmark.config](/config/examples/lpc55s69-benchmark.config): + - Same as `lpc55s69.config`, but turns on test and benchmark code + - Set PKA=1 in the config file to turn on hardware acceleration + (off by default) + +- [lpc55s69-hwpuf.config](/config/examples/lpc55s69-hwpuf.config): + - Same as `lpc55s69.config`, but turns on test code showing how to use the + hwpuf api + - Set HWPUF=1 in the config file to turn on HWPUF support (off by default) + +- [lpc55s69-tz.config](/config/examples/lpc55s69-tz.config): + - wolfBoot lives in the secure realm, test-app lives in the non-secure realm + - Provides a standard PKCS #11 api to interface with crypto algs in the secure + realm + +- [lpc55s69-tz-psa.config](/config/examples/lpc55s69-tz-psa.config): + - wolfBoot lives in the secure realm, test-app lives in the non-secure realm + - Provides a standard PSA api to interface with crypto algs in the secure + realm + - Provides an example of PSA Attestation + - To turn on HWPUF for use with attestation, set the following in the config + file: + - WOLFBOOT_UDS_UID_FALLBACK_FORTEST=0 + - HWPUF=1 + - WOLFBOOT_HWPUF_PROVISION=1 + +#### Summary of configurables applicable to all lpc55s69 example configurations: +- `PKA` : Turn on/off hardware acceleration of crypto algs +- `HWPUF` : Turn on/off support for the hardware PUF +- `WOLFBOOT_HWPUF_PROVISION` : Turn on/off auto-provisioning of the HWPUF on +first boot. Performs PUF enrollment and generates a UDS key for device +attestation. Stores PUF keycodes in a known flash location and uses them when +necessary. + ### LPC55S69: Configuring and compiling Copy the example configuration file and build with make: @@ -3445,9 +3495,6 @@ cp config/examples/lpc55s69.config .config make ``` -We also provide a TrustZone configuration at `config/examples/lpc55s69-tz.config` -and a benchmarking configuration at `config/examples/lpc55s69-benchmark.config`. - ### LPC55S69: Loading the firmware Download and install the LinkServer tool: From 85d056dad750549989fcdaed6cccb1a0b0141658 Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Sat, 19 Sep 2026 18:18:39 -0400 Subject: [PATCH 06/25] update configs --- config/examples/lpc55s69-benchmark.config | 8 +++++--- config/examples/lpc55s69-hwpuf.config | 6 +++--- config/examples/lpc55s69-tz-psa.config | 4 ++++ config/examples/lpc55s69-tz.config | 2 ++ config/examples/lpc55s69.config | 2 ++ 5 files changed, 16 insertions(+), 6 deletions(-) diff --git a/config/examples/lpc55s69-benchmark.config b/config/examples/lpc55s69-benchmark.config index 8d16996c4b..c22fddd540 100644 --- a/config/examples/lpc55s69-benchmark.config +++ b/config/examples/lpc55s69-benchmark.config @@ -30,14 +30,16 @@ FLASH_MULTI_SECTOR_ERASE?=1 # Turn on or off hw acceleration of crypto algs in the lpc55s69 PKA?=0 +# Turn on or off hw puf +HWPUF?=0 WOLFBOOT_SECTOR_SIZE?=0x200 # use these for test/benchmark -WOLFBOOT_PARTITION_SIZE?=0x2b000 +WOLFBOOT_PARTITION_SIZE?=0x30000 WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x10000 -WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x3b000 -WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x66000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x40000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x70000 WOLFCRYPT_TEST?=1 WOLFCRYPT_BENCHMARK?=1 diff --git a/config/examples/lpc55s69-hwpuf.config b/config/examples/lpc55s69-hwpuf.config index 30385a1d1b..1269bf095f 100644 --- a/config/examples/lpc55s69-hwpuf.config +++ b/config/examples/lpc55s69-hwpuf.config @@ -36,9 +36,9 @@ HWPUF?=0 WOLFBOOT_SECTOR_SIZE?=0x200 # use these for test/benchmark -WOLFBOOT_PARTITION_SIZE?=0x2b000 +WOLFBOOT_PARTITION_SIZE?=0x30000 WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x10000 -WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x3b000 -WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x66000 +WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x40000 +WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x70000 WOLFCRYPT_TEST?=1 diff --git a/config/examples/lpc55s69-tz-psa.config b/config/examples/lpc55s69-tz-psa.config index 2afc62fef9..eb3a9e5bbf 100644 --- a/config/examples/lpc55s69-tz-psa.config +++ b/config/examples/lpc55s69-tz-psa.config @@ -29,9 +29,13 @@ DUALBANK_SWAP?=0 FLASH_MULTI_SECTOR_ERASE?=1 WOLFCRYPT_TZ?=1 WOLFCRYPT_TZ_PSA?=1 + WOLFBOOT_ATTESTATION_TEST?=1 WOLFBOOT_UDS_UID_FALLBACK_FORTEST?=1 + +# Turn on or off hw acceleration of crypto algs in the lpc55s69 PKA?=0 +# Turn on or off hw puf HWPUF?=0 WOLFBOOT_HWPUF_PROVISION?=0 diff --git a/config/examples/lpc55s69-tz.config b/config/examples/lpc55s69-tz.config index 5fce4fde8d..6214a6f942 100644 --- a/config/examples/lpc55s69-tz.config +++ b/config/examples/lpc55s69-tz.config @@ -32,6 +32,8 @@ WOLFCRYPT_TZ_PKCS11?=1 # Turn on or off hw acceleration of crypto algs in the lpc55s69 PKA?=0 +# Turn on or off hw puf +HWPUF?=0 WOLFBOOT_SECTOR_SIZE?=0x200 diff --git a/config/examples/lpc55s69.config b/config/examples/lpc55s69.config index 8643b1dcbe..9556b039b7 100644 --- a/config/examples/lpc55s69.config +++ b/config/examples/lpc55s69.config @@ -30,6 +30,8 @@ FLASH_MULTI_SECTOR_ERASE?=1 # Turn on or off hw acceleration of crypto algs in the lpc55s69 PKA?=0 +# Turn on or off hw puf +HWPUF?=0 WOLFBOOT_SECTOR_SIZE?=0x200 From e5a44f0a086af66ff0916c593c163a5b81c5c1cf Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Sat, 19 Sep 2026 19:58:01 -0400 Subject: [PATCH 07/25] fenrir fixes --- hal/lpc55s69.c | 48 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/hal/lpc55s69.c b/hal/lpc55s69.c index da0fae0f0c..fd01fb6251 100644 --- a/hal/lpc55s69.c +++ b/hal/lpc55s69.c @@ -43,6 +43,7 @@ #include #include +#include #ifdef WOLFSSL_HWPUF #include @@ -271,6 +272,7 @@ static int uds_from_uid(uint8_t *out, size_t out_len) uint8_t digest[SHA3_384_DIGEST_SIZE]; #endif size_t copy_len = sizeof(digest); + int ret; flashInit(); @@ -281,26 +283,44 @@ static int uds_from_uid(uint8_t *out, size_t out_len) #if defined(WOLFBOOT_HASH_SHA256) { wc_Sha256 hash; - wc_InitSha256(&hash); - wc_Sha256Update(&hash, uid, sizeof(uid)); - wc_Sha256Final(&hash, digest); + ret = wc_InitSha256(&hash); + if (ret == 0) { + ret = wc_Sha256Update(&hash, uid, sizeof(uid)); + if (ret == 0) + ret = wc_Sha256Final(&hash, digest); + wc_Sha256Free(&hash); + } } #elif defined(WOLFBOOT_HASH_SHA384) { wc_Sha384 hash; - wc_InitSha384(&hash); - wc_Sha384Update(&hash, uid, sizeof(uid)); - wc_Sha384Final(&hash, digest); + ret = wc_InitSha384(&hash); + if (ret == 0) { + ret = wc_Sha384Update(&hash, uid, sizeof(uid)); + if (ret == 0) + ret = wc_Sha384Final(&hash, digest); + wc_Sha384Free(&hash); + } } #elif defined(WOLFBOOT_HASH_SHA3_384) { wc_Sha3 hash; - wc_InitSha3_384(&hash, NULL, INVALID_DEVID); - wc_Sha3_384_Update(&hash, uid, sizeof(uid)); - wc_Sha3_384_Final(&hash, digest); + ret = wc_InitSha3_384(&hash, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_Sha3_384_Update(&hash, uid, sizeof(uid)); + if (ret == 0) + ret = wc_Sha3_384_Final(&hash, digest); + wc_Sha3_384_Free(&hash); + } } #endif + if (ret != 0) { + ForceZero(uid, sizeof(uid)); + ForceZero(digest, sizeof(digest)); + return -1; + } + if (copy_len > out_len) { copy_len = out_len; } @@ -426,10 +446,16 @@ void hal_init(void) #endif #if defined(__WOLFBOOT) && defined(WOLFBOOT_HWPUF_PROVISION) - if (hwpuf_provision_set(0) != 0) + if (hwpuf_provision_set(0) != 0) { +# ifdef DEBUG_UART uart_write("hwpuf provision failure (already provisioned?)\n", 47); - else +# endif + } + else { +# ifdef DEBUG_UART uart_write("hwpuf provision success\n", 24); +# endif + } #endif } From d551ad9ff0b19cc43ec50ce487ea26879a06c7ce Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Sat, 19 Sep 2026 20:24:46 -0400 Subject: [PATCH 08/25] fix the fix --- hal/lpc55s69.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hal/lpc55s69.c b/hal/lpc55s69.c index fd01fb6251..bc060f8d43 100644 --- a/hal/lpc55s69.c +++ b/hal/lpc55s69.c @@ -43,7 +43,7 @@ #include #include -#include +#include #ifdef WOLFSSL_HWPUF #include @@ -316,8 +316,8 @@ static int uds_from_uid(uint8_t *out, size_t out_len) #endif if (ret != 0) { - ForceZero(uid, sizeof(uid)); - ForceZero(digest, sizeof(digest)); + wc_ForceZero(uid, sizeof(uid)); + wc_ForceZero(digest, sizeof(digest)); return -1; } From a9b744838ac59d1a3af5db9301ba687e699ed8ae Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Sat, 19 Sep 2026 22:08:31 -0400 Subject: [PATCH 09/25] fix for DEBUG_UART=0 --- arch.mk | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch.mk b/arch.mk index dfaa8a12b5..a0e7c1269d 100644 --- a/arch.mk +++ b/arch.mk @@ -2138,10 +2138,10 @@ ifeq ($(TARGET),lpc55s69) $(MCUXPRESSO)/drivers/common/fsl_common_arm.o \ $(MCUXPRESSO)/drivers/iap1/fsl_iap.o \ $(MCUXPRESSO)/drivers/lpc_gpio/fsl_gpio.o \ - $(MCUXPRESSO)/drivers/rng_1/fsl_rng.o + $(MCUXPRESSO)/drivers/rng_1/fsl_rng.o \ + $(MCUXPRESSO_DRIVERS)/drivers/fsl_reset.o ifeq ($(DEBUG_UART),1) OBJS+=\ - $(MCUXPRESSO_DRIVERS)/drivers/fsl_reset.o \ $(MCUXPRESSO)/drivers/flexcomm/fsl_flexcomm.o \ $(MCUXPRESSO)/drivers/flexcomm/usart/fsl_usart.o endif From 3dccfd5b172338ec56d1f5e706d30a60576220f4 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 21 Sep 2026 17:20:58 +0200 Subject: [PATCH 10/25] Remove PUF provisioning debug markers and trace messages Clean up the single-letter UART markers (R/A/B/C/g0-3/1-6/D/E/F/G-P/L0/L/ V/W/d/e) and the hwpuf provision success/failure trace messages added during the lpc55s69 PUF cosim debugging. The PUF+RNG models in m33mu now work; the boot flow completes end-to-end. --- hal/lpc55s69.c | 6 ------ src/update_flash.c | 1 - 2 files changed, 7 deletions(-) diff --git a/hal/lpc55s69.c b/hal/lpc55s69.c index bc060f8d43..07fa201fe9 100644 --- a/hal/lpc55s69.c +++ b/hal/lpc55s69.c @@ -447,14 +447,8 @@ void hal_init(void) #if defined(__WOLFBOOT) && defined(WOLFBOOT_HWPUF_PROVISION) if (hwpuf_provision_set(0) != 0) { -# ifdef DEBUG_UART - uart_write("hwpuf provision failure (already provisioned?)\n", 47); -# endif } else { -# ifdef DEBUG_UART - uart_write("hwpuf provision success\n", 24); -# endif } #endif } diff --git a/src/update_flash.c b/src/update_flash.c index 0e68d8c514..7ff3a4c426 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -1781,7 +1781,6 @@ void RAMFUNCTION wolfBoot_start(void) WP11_Library_Init(); #endif #endif /* !WOLFBOOT_SELF_UPDATE_MONOLITHIC */ - bootRet = wolfBoot_open_image(&boot, PART_BOOT); wolfBoot_printf("Booting version: 0x%x\n", wolfBoot_get_blob_version(boot.hdr)); From a8a95ddce331f37c31568a64c65fd2f7e829d398 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Mon, 21 Sep 2026 18:28:14 +0200 Subject: [PATCH 11/25] Add CI job for lpc55s69 DICE + HW PUF under m33mu Uses lpc55s69-tz-psa.config with HWPUF=1 and WOLFBOOT_HWPUF_PROVISION=1. Requires m33mu with lpc55s69 PUF/RNG models and the 0x0C000000 secure flash alias (wolfboot-ci >= 1.22). --- .../workflows/trustzone-emulator-tests.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/.github/workflows/trustzone-emulator-tests.yml b/.github/workflows/trustzone-emulator-tests.yml index 34b255fb67..2cc24d29ec 100644 --- a/.github/workflows/trustzone-emulator-tests.yml +++ b/.github/workflows/trustzone-emulator-tests.yml @@ -168,3 +168,22 @@ jobs: ./tools/keytools/otp/otp-keystore-gen m33mu tools/keytools/otp/otp-keystore-primer.bin --persist --timeout 10 || true m33mu wolfboot.bin test-app/image_v1_signed.bin:0x60000 --uart-stdout --expect-bkpt 0x7f --timeout 600 --persist + + # LPC55S69 DICE attestation + HW PUF. The PUF root key is provisioned + # at first boot (WOLFBOOT_HWPUF_PROVISION=1) and used for DICE key + # derivation on subsequent boots. Requires m33mu with lpc55s69 PUF/RNG + # models (wolfboot-ci >= 1.22). + - name: Clean and build test with DICE attestation + HW PUF (lpc55s69) + run: | + set -o pipefail + make clean distclean + cp config/examples/lpc55s69-tz-psa.config .config + sed -i 's/^HWPUF?=0/HWPUF?=1/' .config + echo "WOLFBOOT_HWPUF_PROVISION?=1" >> .config + make + m33mu wolfboot.bin test-app/image_v1_signed.bin:0x48000 --cpu lpc55s69 --uart-stdout --timeout 300 \ + | tee /tmp/m33mu-lpc55s69-dice-hwpuf.log + grep -q "Booting version:" /tmp/m33mu-lpc55s69-dice-hwpuf.log + grep -q "Checking integrity...done" /tmp/m33mu-lpc55s69-dice-hwpuf.log + grep -q "Verifying signature...done" /tmp/m33mu-lpc55s69-dice-hwpuf.log + grep -q "state=0x00" /tmp/m33mu-lpc55s69-dice-hwpuf.log From 60705a91710802710e6691e4dd83b21a1f55c14d Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Mon, 5 Oct 2026 12:34:29 -0400 Subject: [PATCH 12/25] Remove hwpuf module --- arch.mk | 1 - hal/lpc55s69.c | 43 +++++++++++++++++++++---------------------- test-app/Makefile | 1 - 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/arch.mk b/arch.mk index a0e7c1269d..fbcd35aa22 100644 --- a/arch.mk +++ b/arch.mk @@ -2158,7 +2158,6 @@ ifeq ($(TARGET),lpc55s69) OBJS+=\ $(MCUXPRESSO)/drivers/puf/fsl_puf.o \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/cryptocb.o \ - $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/hwpuf.o \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/hwpuf_port.o endif endif diff --git a/hal/lpc55s69.c b/hal/lpc55s69.c index 07fa201fe9..dd2c40e0c8 100644 --- a/hal/lpc55s69.c +++ b/hal/lpc55s69.c @@ -42,11 +42,12 @@ #endif #include +#include #include #include -#ifdef WOLFSSL_HWPUF -#include +#ifdef WOLFSSL_NXP_HWPUF +#include #endif #if defined(WOLFCRYPT_TZ_PSA) @@ -98,7 +99,7 @@ static NOINLINEFUNCTION void hal_zeroize(void *ptr, size_t len) typedef struct hwpuf_prov { word32 magic; - byte ac[HWPUF_ACTIVATION_CODE_SIZE]; + byte ac[PUF_ACTIVATION_CODE_SIZE]; byte uds_kc[PUF_GET_KEY_CODE_SIZE_FOR_KEY_SIZE(HWPUF_PROV_UDS_KEY_SIZE)]; } hwpuf_prov; @@ -137,7 +138,6 @@ static int hwpuf_provision_get(void) static int hwpuf_provision_set(int force) { int ret = -1; - wc_HWPUF hwpuf; if (sizeof(prov) > HWPUF_PROV_FLASH_LEN) return -1; @@ -149,22 +149,22 @@ static int hwpuf_provision_set(int force) XMEMSET(&prov, 0, sizeof(prov)); - if (wc_HWPUF_Register(&hwpuf, NULL, INVALID_DEVID) != 0) + if (nxp_hwpuf_RegisterDevice() != 0) return -1; - if (wc_HWPUF_Init(&hwpuf) != 0) + if (wc_CryptoCb_HwpufInit(WOLFSSL_NXP_HWPUF_DEVID) != 0) goto error_out; - if (wc_HWPUF_Enroll(&hwpuf, prov.ac, sizeof(prov.ac)) != 0) + if (wc_CryptoCb_HwpufEnroll(WOLFSSL_NXP_HWPUF_DEVID, prov.ac, sizeof(prov.ac)) != 0) goto error_out; - (void)wc_HWPUF_Deinit(&hwpuf); - (void)wc_HWPUF_Init(&hwpuf); + (void)wc_CryptoCb_HwpufDeinit(WOLFSSL_NXP_HWPUF_DEVID); + (void)wc_CryptoCb_HwpufInit(WOLFSSL_NXP_HWPUF_DEVID); - if (wc_HWPUF_Start(&hwpuf, prov.ac, sizeof(prov.ac)) != 0) + if (wc_CryptoCb_HwpufStart(WOLFSSL_NXP_HWPUF_DEVID, prov.ac, sizeof(prov.ac)) != 0) goto error_out; - if (wc_HWPUF_GenerateKey(&hwpuf, + if (wc_CryptoCb_HwpufGenerateKey(WOLFSSL_NXP_HWPUF_DEVID, HWPUF_PROV_UDS_KEY_INDEX, HWPUF_PROV_UDS_KEY_SIZE, prov.uds_kc, sizeof(prov.uds_kc)) != 0) goto error_out; @@ -189,9 +189,9 @@ static int hwpuf_provision_set(int force) error_out: hal_zeroize(&prov, sizeof(prov)); - (void)wc_HWPUF_Zeroize(&hwpuf); - (void)wc_HWPUF_Deinit(&hwpuf); - (void)wc_HWPUF_Unregister(&hwpuf); + (void)wc_CryptoCb_HwpufZeroize(WOLFSSL_NXP_HWPUF_DEVID); + (void)wc_CryptoCb_HwpufDeinit(WOLFSSL_NXP_HWPUF_DEVID); + (void)nxp_hwpuf_UnregisterDevice(); return ret; } #endif /* WOLFBOOT_HWPUF_PROVISION */ @@ -200,22 +200,21 @@ static int hwpuf_provision_set(int force) static int uds_from_hwpuf(uint8_t *out, size_t out_len) { int ret = -1; - wc_HWPUF hwpuf; byte uds[HWPUF_PROV_UDS_KEY_SIZE]; if (hwpuf_provision_get() != 0) return -1; - if (wc_HWPUF_Register(&hwpuf, NULL, INVALID_DEVID) != 0) + if (nxp_hwpuf_RegisterDevice() != 0) return -1; - if (wc_HWPUF_Init(&hwpuf) != 0) + if (wc_CryptoCb_HwpufInit(WOLFSSL_NXP_HWPUF_DEVID) != 0) goto error_out; - if (wc_HWPUF_Start(&hwpuf, prov.ac, sizeof(prov.ac)) != 0) + if (wc_CryptoCb_HwpufStart(WOLFSSL_NXP_HWPUF_DEVID, prov.ac, sizeof(prov.ac)) != 0) goto error_out; - ret = wc_HWPUF_GetKey(&hwpuf, prov.uds_kc, sizeof(prov.uds_kc), + ret = wc_CryptoCb_HwpufGetKey(WOLFSSL_NXP_HWPUF_DEVID, prov.uds_kc, sizeof(prov.uds_kc), uds, sizeof(uds)); if (ret != 0) goto error_out; @@ -231,9 +230,9 @@ static int uds_from_hwpuf(uint8_t *out, size_t out_len) error_out: hal_zeroize(uds, sizeof(uds)); hal_zeroize(&prov, sizeof(prov)); - (void)wc_HWPUF_Zeroize(&hwpuf); - (void)wc_HWPUF_Deinit(&hwpuf); - (void)wc_HWPUF_Unregister(&hwpuf); + (void)wc_CryptoCb_HwpufZeroize(WOLFSSL_NXP_HWPUF_DEVID); + (void)wc_CryptoCb_HwpufDeinit(WOLFSSL_NXP_HWPUF_DEVID); + (void)nxp_hwpuf_UnregisterDevice(); return ret == 0 ? 0 : -1; } #endif /* WOLFCRYPT_TZ_PSA */ diff --git a/test-app/Makefile b/test-app/Makefile index c85cea4fda..29b54c4861 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -1202,7 +1202,6 @@ ifeq ($(TARGET),lpc55s69) APP_OBJS+=\ $(MCUXPRESSO)/drivers/puf/fsl_puf.o \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/cryptocb.o \ - $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/hwpuf.o \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/hwpuf_port.o endif ifeq ($(WOLFCRYPT_SUPPORT),1) From 154bb2f051841caaf72ef01c067143486b188cd8 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 6 Oct 2026 10:03:13 +0200 Subject: [PATCH 13/25] CI: fix lpc55s69 HWPUF jobs (Copilot review) - hwpuf config job: pass HWPUF=1 so the PUF driver/port compile - m33mu job: set up MCUXpresso SDK (west manifest) + CMSIS_5 for lpc55s69 - provisioning: replace ?=0 instead of appending; disable UID fallback; require the success-only 'attest: token size' message - config.mk: register HWPUF + WOLFBOOT_HWPUF_PROVISION so make config preserves them --- .github/workflows/test-configs.yml | 1 + .../workflows/trustzone-emulator-tests.yml | 24 +++++++++++++++++-- tools/config.mk | 3 +++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-configs.yml b/.github/workflows/test-configs.yml index 0dd6516ff3..6b0ab79507 100644 --- a/.github/workflows/test-configs.yml +++ b/.github/workflows/test-configs.yml @@ -191,6 +191,7 @@ jobs: arch: arm config-file: ./config/examples/lpc55s69-hwpuf.config board-name: lpcxpresso55s69 + make-args: HWPUF=1 lpc55s69_benchmark_test: uses: ./.github/workflows/test-build-mcux-sdk-manifests.yml diff --git a/.github/workflows/trustzone-emulator-tests.yml b/.github/workflows/trustzone-emulator-tests.yml index 2cc24d29ec..60b3c3be1e 100644 --- a/.github/workflows/trustzone-emulator-tests.yml +++ b/.github/workflows/trustzone-emulator-tests.yml @@ -173,17 +173,37 @@ jobs: # at first boot (WOLFBOOT_HWPUF_PROVISION=1) and used for DICE key # derivation on subsequent boots. Requires m33mu with lpc55s69 PUF/RNG # models (wolfboot-ci >= 1.22). + # The lpc55s69 HAL needs MCUXpresso SDK headers (fsl_common.h); same + # west-based setup as the lpc55s69 jobs in test-configs.yml. + - name: Checkout CMSIS (lpc55s69) + uses: actions/checkout@main + with: + repository: nxp-mcuxpresso/CMSIS_5 + path: CMSIS_5 + + - name: Setup MCUXpresso SDK (lpc55s69) + run: | + python3 -m venv venv + source venv/bin/activate + pip install west + west init -m https://github.com/nxp-mcuxpresso/mcuxsdk-manifests.git mcuxpresso-sdk + cd mcuxpresso-sdk + sed -i '/se_hostlib\|emwin/d' manifests/boards/lpcxpresso55s69.yml + west update_board --set board lpcxpresso55s69 + - name: Clean and build test with DICE attestation + HW PUF (lpc55s69) run: | set -o pipefail make clean distclean cp config/examples/lpc55s69-tz-psa.config .config sed -i 's/^HWPUF?=0/HWPUF?=1/' .config - echo "WOLFBOOT_HWPUF_PROVISION?=1" >> .config - make + sed -i 's/^WOLFBOOT_HWPUF_PROVISION?=0/WOLFBOOT_HWPUF_PROVISION?=1/' .config + sed -i 's/^WOLFBOOT_UDS_UID_FALLBACK_FORTEST?=1/WOLFBOOT_UDS_UID_FALLBACK_FORTEST?=0/' .config + make MCUXSDK=1 MCUXPRESSO="$GITHUB_WORKSPACE/mcuxpresso-sdk/mcuxsdk" MCUXPRESSO_CMSIS="$GITHUB_WORKSPACE/CMSIS_5/CMSIS" m33mu wolfboot.bin test-app/image_v1_signed.bin:0x48000 --cpu lpc55s69 --uart-stdout --timeout 300 \ | tee /tmp/m33mu-lpc55s69-dice-hwpuf.log grep -q "Booting version:" /tmp/m33mu-lpc55s69-dice-hwpuf.log grep -q "Checking integrity...done" /tmp/m33mu-lpc55s69-dice-hwpuf.log grep -q "Verifying signature...done" /tmp/m33mu-lpc55s69-dice-hwpuf.log + grep -q "attest: token size" /tmp/m33mu-lpc55s69-dice-hwpuf.log grep -q "state=0x00" /tmp/m33mu-lpc55s69-dice-hwpuf.log diff --git a/tools/config.mk b/tools/config.mk index 1fad0cbab0..ae30fc149e 100644 --- a/tools/config.mk +++ b/tools/config.mk @@ -53,6 +53,8 @@ ifeq ($(ARCH),) DUALBANK_SWAP?=0 IMAGE_HEADER_SIZE?=256 PKA?=1 + HWPUF?=0 + WOLFBOOT_HWPUF_PROVISION?=0 PSOC6_CRYPTO?=1 WOLFTPM?=0 WOLFBOOT_TPM_VERIFY?=0 @@ -117,6 +119,7 @@ CONFIG_VARS:= ARCH TARGET SIGN HASH MCUXSDK MCUXPRESSO MCUXPRESSO_CPU MCUXPRESSO WOLFBOOT_ATTESTATION_TEST \ WOLFBOOT_UDS_UID_FALLBACK_FORTEST \ WOLFBOOT_UDS_OBKEYS \ + HWPUF WOLFBOOT_HWPUF_PROVISION \ WOLFCRYPT_TZ WOLFCRYPT_TZ_PKCS11 \ WOLFCRYPT_TZ_PSA \ WOLFBOOT_DICE_HW \ From e076292f99ba88da7e6a6d7fe551637d6c25dff7 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 6 Oct 2026 10:15:54 +0200 Subject: [PATCH 14/25] lpc55s69: drop empty if/else leftover from PUF provisioning The debug-marker removal left an empty if/else around hwpuf_provision_set(). Provisioning failure is non-fatal: the UID fallback still derives the UDS, so the result is discarded. --- hal/lpc55s69.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hal/lpc55s69.c b/hal/lpc55s69.c index dd2c40e0c8..0e247e7690 100644 --- a/hal/lpc55s69.c +++ b/hal/lpc55s69.c @@ -445,10 +445,9 @@ void hal_init(void) #endif #if defined(__WOLFBOOT) && defined(WOLFBOOT_HWPUF_PROVISION) - if (hwpuf_provision_set(0) != 0) { - } - else { - } + /* Provisioning failure is non-fatal: the UID fallback still derives + * the UDS, so the boot continues. */ + (void)hwpuf_provision_set(0); #endif } From f8275f3e98aee3b0005742820d494611265bd0c1 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 16 Oct 2026 13:31:16 +0200 Subject: [PATCH 15/25] Update wolfssl submodule to latest master Includes the simplified lpc55s69 HWPUF port (wolfSSL PR 11700), which the lpc55s69 HWPUF builds depend on. --- lib/wolfssl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wolfssl b/lib/wolfssl index f99453320f..bc9c079025 160000 --- a/lib/wolfssl +++ b/lib/wolfssl @@ -1 +1 @@ -Subproject commit f99453320ff306fe5bfb10788117f18c9bfeaf6b +Subproject commit bc9c0790253382e73198689dbef57b5c5c4792a5 From af488f03b79c515a5d55d303bcea236c9302429e Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 16 Oct 2026 13:34:46 +0200 Subject: [PATCH 16/25] lpc55s69: port HWPUF HAL to the simplified wolfSSL API wolfSSL PR 11700 replaced the devid-based wc_CryptoCb_Hwpuf* API with the handle-less nxp_hwpuf_* API: drop the device id, the Register/UnregisterDevice calls and the CryptoCb wrappers. --- hal/lpc55s69.c | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/hal/lpc55s69.c b/hal/lpc55s69.c index 0e247e7690..315d32ae77 100644 --- a/hal/lpc55s69.c +++ b/hal/lpc55s69.c @@ -149,24 +149,20 @@ static int hwpuf_provision_set(int force) XMEMSET(&prov, 0, sizeof(prov)); - if (nxp_hwpuf_RegisterDevice() != 0) - return -1; - - if (wc_CryptoCb_HwpufInit(WOLFSSL_NXP_HWPUF_DEVID) != 0) + if (nxp_hwpuf_Init() != 0) goto error_out; - if (wc_CryptoCb_HwpufEnroll(WOLFSSL_NXP_HWPUF_DEVID, prov.ac, sizeof(prov.ac)) != 0) + if (nxp_hwpuf_Enroll(prov.ac, sizeof(prov.ac)) != 0) goto error_out; - (void)wc_CryptoCb_HwpufDeinit(WOLFSSL_NXP_HWPUF_DEVID); - (void)wc_CryptoCb_HwpufInit(WOLFSSL_NXP_HWPUF_DEVID); + (void)nxp_hwpuf_Deinit(); + (void)nxp_hwpuf_Init(); - if (wc_CryptoCb_HwpufStart(WOLFSSL_NXP_HWPUF_DEVID, prov.ac, sizeof(prov.ac)) != 0) + if (nxp_hwpuf_Start(prov.ac, sizeof(prov.ac)) != 0) goto error_out; - if (wc_CryptoCb_HwpufGenerateKey(WOLFSSL_NXP_HWPUF_DEVID, - HWPUF_PROV_UDS_KEY_INDEX, HWPUF_PROV_UDS_KEY_SIZE, - prov.uds_kc, sizeof(prov.uds_kc)) != 0) + if (nxp_hwpuf_GenerateKey(HWPUF_PROV_UDS_KEY_INDEX, HWPUF_PROV_UDS_KEY_SIZE, + prov.uds_kc, sizeof(prov.uds_kc)) != 0) goto error_out; prov.magic = HWPUF_PROV_MAGIC; @@ -189,9 +185,8 @@ static int hwpuf_provision_set(int force) error_out: hal_zeroize(&prov, sizeof(prov)); - (void)wc_CryptoCb_HwpufZeroize(WOLFSSL_NXP_HWPUF_DEVID); - (void)wc_CryptoCb_HwpufDeinit(WOLFSSL_NXP_HWPUF_DEVID); - (void)nxp_hwpuf_UnregisterDevice(); + (void)nxp_hwpuf_Zeroize(); + (void)nxp_hwpuf_Deinit(); return ret; } #endif /* WOLFBOOT_HWPUF_PROVISION */ @@ -205,17 +200,13 @@ static int uds_from_hwpuf(uint8_t *out, size_t out_len) if (hwpuf_provision_get() != 0) return -1; - if (nxp_hwpuf_RegisterDevice() != 0) + if (nxp_hwpuf_Init() != 0) return -1; - if (wc_CryptoCb_HwpufInit(WOLFSSL_NXP_HWPUF_DEVID) != 0) - goto error_out; - - if (wc_CryptoCb_HwpufStart(WOLFSSL_NXP_HWPUF_DEVID, prov.ac, sizeof(prov.ac)) != 0) + if (nxp_hwpuf_Start(prov.ac, sizeof(prov.ac)) != 0) goto error_out; - ret = wc_CryptoCb_HwpufGetKey(WOLFSSL_NXP_HWPUF_DEVID, prov.uds_kc, sizeof(prov.uds_kc), - uds, sizeof(uds)); + ret = nxp_hwpuf_GetKey(prov.uds_kc, sizeof(prov.uds_kc), uds, sizeof(uds)); if (ret != 0) goto error_out; @@ -230,9 +221,8 @@ static int uds_from_hwpuf(uint8_t *out, size_t out_len) error_out: hal_zeroize(uds, sizeof(uds)); hal_zeroize(&prov, sizeof(prov)); - (void)wc_CryptoCb_HwpufZeroize(WOLFSSL_NXP_HWPUF_DEVID); - (void)wc_CryptoCb_HwpufDeinit(WOLFSSL_NXP_HWPUF_DEVID); - (void)nxp_hwpuf_UnregisterDevice(); + (void)nxp_hwpuf_Zeroize(); + (void)nxp_hwpuf_Deinit(); return ret == 0 ? 0 : -1; } #endif /* WOLFCRYPT_TZ_PSA */ From 4981713cec4764f54f0d6ed9364139f4bc38e8f0 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 16 Oct 2026 19:22:27 +0200 Subject: [PATCH 17/25] lpc55s69: hand SRAM2 to the NS world via AHBSC before the jump AHB_SECURE_CTRL gates SRAM at the bus level on this part: the SAU alone does not make an SRAM block non-secure. The boot ROM leaves every 4 KB block secure+privileged, so the NS app at 0x20020000 faults on its first RAM access. Program the SRAM2 rules NS+privileged before the NS jump. --- hal/lpc55s69.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/hal/lpc55s69.c b/hal/lpc55s69.c index 315d32ae77..47f210c277 100644 --- a/hal/lpc55s69.c +++ b/hal/lpc55s69.c @@ -344,8 +344,24 @@ int hal_uds_derive_key(uint8_t *out, size_t out_len) #endif /* __WOLFBOOT && WOLFCRYPT_TZ_PSA && !WOLFBOOT_DICE_HW */ #ifdef TZEN +/* AHB_SECURE_CTRL (AHBSC) gates SRAM at the bus level on this part: the SAU + * alone does not make an SRAM block non-secure. The boot ROM leaves every + * 4 KB block secure+privileged; hand SRAM2 (0x20020000..0x2002FFFF, the NS + * app RAM) to the NS world as non-secure+privileged before the NS jump. */ +#define AHBSC_BASE 0x400AC000u +#define AHBSC_SRAM2_RULE0 (*(volatile uint32_t *)(AHBSC_BASE + 0xA0u)) +#define AHBSC_SRAM2_RULE1 (*(volatile uint32_t *)(AHBSC_BASE + 0xA4u)) + +static void hal_ahbsc_init(void) +{ + AHBSC_SRAM2_RULE0 = 0x11111111u; /* blocks 0-7: NS + privileged */ + AHBSC_SRAM2_RULE1 = 0x11111111u; /* blocks 8-15: NS + privileged */ +} + static void hal_sau_init(void) { + hal_ahbsc_init(); + /* Non-secure callable area */ sau_init_region(0, WOLFBOOT_NSC_ADDRESS, WOLFBOOT_NSC_ADDRESS + WOLFBOOT_NSC_SIZE - 1, 1); From e45ded9f2cdb4a102735af93d84c051e29be4a33 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 16 Oct 2026 19:22:27 +0200 Subject: [PATCH 18/25] lpc55s69: limit PUF provision page check to the pages it occupies hwpuf_provision_get() required none of the 8 pages of the 4 KB provision region to be erased, but the 1248 B provision record fills only 3 pages, so the trailing pages stay 0xFF and the check always failed. Loop to the page-aligned size of the record. --- hal/lpc55s69.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hal/lpc55s69.c b/hal/lpc55s69.c index 47f210c277..072ba09b00 100644 --- a/hal/lpc55s69.c +++ b/hal/lpc55s69.c @@ -116,7 +116,8 @@ static int hwpuf_provision_get(void) { uint32_t addr; uint32_t start = HWPUF_PROV_FLASH_BASEADR; - uint32_t end = HWPUF_PROV_FLASH_BASEADR + HWPUF_PROV_FLASH_LEN; + uint32_t end = HWPUF_PROV_FLASH_BASEADR + + ((sizeof(prov) + pflash_page_size - 1) & ~(pflash_page_size - 1)); for (addr = start; addr < end; addr += pflash_page_size) { if (FLASH_VerifyErase(&pflash, addr, pflash_page_size) From 1ea208cc7d7b067a85cb6c1edc0276c4676c4f40 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 16 Oct 2026 19:22:27 +0200 Subject: [PATCH 19/25] lpc55s69: implement hal_attestation_get_ueid for the HWPUF path The weak stub returned -1, so DICE attestation failed with -3 before key derivation. Derive the UEID from the HWPUF UDS inside the HAL (SHA-256, EAT type RANDOM) so the key material never leaves the platform security boundary. --- hal/lpc55s69.c | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/hal/lpc55s69.c b/hal/lpc55s69.c index 072ba09b00..14a470c000 100644 --- a/hal/lpc55s69.c +++ b/hal/lpc55s69.c @@ -226,6 +226,38 @@ static int uds_from_hwpuf(uint8_t *out, size_t out_len) (void)nxp_hwpuf_Deinit(); return ret == 0 ? 0 : -1; } + +/* EAT UEID for the HWPUF path: derived from the UDS inside the HAL so the + * key code never leaves the platform security boundary (dice.c HW flow). */ +int hal_attestation_get_ueid(uint8_t *buf, size_t *len) +{ + uint8_t uds[HWPUF_PROV_UDS_KEY_SIZE]; + wc_Sha256 sha; + int ret; + + if (buf == NULL || len == NULL || *len < 33) + return -1; + + ret = uds_from_hwpuf(uds, sizeof(uds)); + if (ret != 0) + return -1; + + ret = wc_InitSha256(&sha); + if (ret == 0) { + ret = wc_Sha256Update(&sha, uds, (word32)sizeof(uds)); + if (ret == 0) + ret = wc_Sha256Final(&sha, &buf[1]); + wc_Sha256Free(&sha); + } + hal_zeroize(uds, sizeof(uds)); + if (ret != 0) + return -1; + + /* UEID type RANDOM per EAT spec */ + buf[0] = 0x01; + *len = 33; + return 0; +} #endif /* WOLFCRYPT_TZ_PSA */ #endif /* __WOLFBOOT && WOLFSSL_HWPUF */ From b3be55d8e82597ac3aabda7211c7c18e119b8013 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 16 Oct 2026 23:00:14 +0200 Subject: [PATCH 20/25] CI: run the lpc55s69 SDK setup step under bash source is a bash builtin; the default sh (dash) dies with "source: not found" before west can install. --- .github/workflows/trustzone-emulator-tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/trustzone-emulator-tests.yml b/.github/workflows/trustzone-emulator-tests.yml index 60b3c3be1e..68e6381516 100644 --- a/.github/workflows/trustzone-emulator-tests.yml +++ b/.github/workflows/trustzone-emulator-tests.yml @@ -182,6 +182,7 @@ jobs: path: CMSIS_5 - name: Setup MCUXpresso SDK (lpc55s69) + shell: bash run: | python3 -m venv venv source venv/bin/activate From 68a55492d354a58fee96a5c68121caccb476b644 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 16 Oct 2026 23:00:53 +0200 Subject: [PATCH 21/25] raise footprint limits: ML_DSA (+40B, wolfSSL bump to bc9c07902 ML-DSA small-mem fixes) --- tools/test.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/test.mk b/tools/test.mk index 1ca12493be..8f39c6e66c 100644 --- a/tools/test.mk +++ b/tools/test.mk @@ -1297,5 +1297,5 @@ test-size-all: LIMIT=8980 NO_ARM_ASM=1 make keysclean make clean - make test-size SIGN=ML_DSA ML_DSA_LEVEL=2 LIMIT=19862 \ + make test-size SIGN=ML_DSA ML_DSA_LEVEL=2 LIMIT=19902 \ IMAGE_SIGNATURE_SIZE=2420 IMAGE_HEADER_SIZE?=8192 From b8e421390f38fc831a58ced80222ac639b647382 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Tue, 20 Oct 2026 10:45:34 +0200 Subject: [PATCH 22/25] lpc55s69: don't zeroize the PUF after use; end the HWPUF CI run on bkpt --- .github/workflows/trustzone-emulator-tests.yml | 3 ++- hal/lpc55s69.c | 4 ++-- test-app/app_lpc55s69.c | 7 ++++++- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/.github/workflows/trustzone-emulator-tests.yml b/.github/workflows/trustzone-emulator-tests.yml index 68e6381516..03ccde9fab 100644 --- a/.github/workflows/trustzone-emulator-tests.yml +++ b/.github/workflows/trustzone-emulator-tests.yml @@ -201,10 +201,11 @@ jobs: sed -i 's/^WOLFBOOT_HWPUF_PROVISION?=0/WOLFBOOT_HWPUF_PROVISION?=1/' .config sed -i 's/^WOLFBOOT_UDS_UID_FALLBACK_FORTEST?=1/WOLFBOOT_UDS_UID_FALLBACK_FORTEST?=0/' .config make MCUXSDK=1 MCUXPRESSO="$GITHUB_WORKSPACE/mcuxpresso-sdk/mcuxsdk" MCUXPRESSO_CMSIS="$GITHUB_WORKSPACE/CMSIS_5/CMSIS" - m33mu wolfboot.bin test-app/image_v1_signed.bin:0x48000 --cpu lpc55s69 --uart-stdout --timeout 300 \ + m33mu wolfboot.bin test-app/image_v1_signed.bin:0x48000 --cpu lpc55s69 --uart-stdout --expect-bkpt 0x7f --timeout 300 \ | tee /tmp/m33mu-lpc55s69-dice-hwpuf.log grep -q "Booting version:" /tmp/m33mu-lpc55s69-dice-hwpuf.log grep -q "Checking integrity...done" /tmp/m33mu-lpc55s69-dice-hwpuf.log grep -q "Verifying signature...done" /tmp/m33mu-lpc55s69-dice-hwpuf.log grep -q "attest: token size" /tmp/m33mu-lpc55s69-dice-hwpuf.log grep -q "state=0x00" /tmp/m33mu-lpc55s69-dice-hwpuf.log + grep -q "\\[EXPECT BKPT\\] Success" /tmp/m33mu-lpc55s69-dice-hwpuf.log diff --git a/hal/lpc55s69.c b/hal/lpc55s69.c index 14a470c000..c195f76abe 100644 --- a/hal/lpc55s69.c +++ b/hal/lpc55s69.c @@ -186,7 +186,8 @@ static int hwpuf_provision_set(int force) error_out: hal_zeroize(&prov, sizeof(prov)); - (void)nxp_hwpuf_Zeroize(); + /* No nxp_hwpuf_Zeroize(): it locks the PUF until the next POR, and the + * UDS must still be derivable at runtime. Deinit powers the PUF down. */ (void)nxp_hwpuf_Deinit(); return ret; } @@ -222,7 +223,6 @@ static int uds_from_hwpuf(uint8_t *out, size_t out_len) error_out: hal_zeroize(uds, sizeof(uds)); hal_zeroize(&prov, sizeof(prov)); - (void)nxp_hwpuf_Zeroize(); (void)nxp_hwpuf_Deinit(); return ret == 0 ? 0 : -1; } diff --git a/test-app/app_lpc55s69.c b/test-app/app_lpc55s69.c index 72c85df307..3694ca4713 100644 --- a/test-app/app_lpc55s69.c +++ b/test-app/app_lpc55s69.c @@ -255,8 +255,13 @@ void main(void) } #if defined(WOLFBOOT_ATTESTATION_TEST) && defined(WOLFCRYPT_TZ_PSA) - if (run_attestation_test() != 0) + if (run_attestation_test() == 0) { + __asm__ volatile ("bkpt #0x7f"); + } + else { wolfBoot_printf("Make sure WOLFBOOT_HWPUF_PROVISION is set\n"); + __asm__ volatile ("bkpt #0x7e"); + } #endif #if defined(WOLFCRYPT_TEST) || defined(WOLFCRYPT_BENCHMARK) From 38d7655080acb9249f1c090fc955a295ae1f0cf1 Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Thu, 22 Oct 2026 14:10:55 -0400 Subject: [PATCH 23/25] address pr comments --- .github/workflows/test-configs.yml | 8 - .../workflows/trustzone-emulator-tests.yml | 68 +++- arch.mk | 3 +- config/examples/lpc55s69-hwpuf.config | 44 --- docs/Targets.md | 19 +- hal/lpc55s69.c | 324 ++++++++++-------- options.mk | 9 + src/update_flash.c | 1 + test-app/Makefile | 12 +- test-app/app_lpc55s69.c | 3 +- tools/config.mk | 3 +- 11 files changed, 265 insertions(+), 229 deletions(-) delete mode 100644 config/examples/lpc55s69-hwpuf.config diff --git a/.github/workflows/test-configs.yml b/.github/workflows/test-configs.yml index 6b0ab79507..7c3372d5ab 100644 --- a/.github/workflows/test-configs.yml +++ b/.github/workflows/test-configs.yml @@ -185,14 +185,6 @@ jobs: config-file: ./config/examples/lpc55s69-tz-psa.config board-name: lpcxpresso55s69 - lpc55s69_hwpuf_test: - uses: ./.github/workflows/test-build-mcux-sdk-manifests.yml - with: - arch: arm - config-file: ./config/examples/lpc55s69-hwpuf.config - board-name: lpcxpresso55s69 - make-args: HWPUF=1 - lpc55s69_benchmark_test: uses: ./.github/workflows/test-build-mcux-sdk-manifests.yml with: diff --git a/.github/workflows/trustzone-emulator-tests.yml b/.github/workflows/trustzone-emulator-tests.yml index 03ccde9fab..95c9b91eb5 100644 --- a/.github/workflows/trustzone-emulator-tests.yml +++ b/.github/workflows/trustzone-emulator-tests.yml @@ -194,18 +194,68 @@ jobs: - name: Clean and build test with DICE attestation + HW PUF (lpc55s69) run: | - set -o pipefail make clean distclean cp config/examples/lpc55s69-tz-psa.config .config sed -i 's/^HWPUF?=0/HWPUF?=1/' .config sed -i 's/^WOLFBOOT_HWPUF_PROVISION?=0/WOLFBOOT_HWPUF_PROVISION?=1/' .config sed -i 's/^WOLFBOOT_UDS_UID_FALLBACK_FORTEST?=1/WOLFBOOT_UDS_UID_FALLBACK_FORTEST?=0/' .config + grep -q '^HWPUF?=1' .config + grep -q '^WOLFBOOT_HWPUF_PROVISION?=1' .config + grep -q '^WOLFBOOT_UDS_UID_FALLBACK_FORTEST?=0' .config make MCUXSDK=1 MCUXPRESSO="$GITHUB_WORKSPACE/mcuxpresso-sdk/mcuxsdk" MCUXPRESSO_CMSIS="$GITHUB_WORKSPACE/CMSIS_5/CMSIS" - m33mu wolfboot.bin test-app/image_v1_signed.bin:0x48000 --cpu lpc55s69 --uart-stdout --expect-bkpt 0x7f --timeout 300 \ - | tee /tmp/m33mu-lpc55s69-dice-hwpuf.log - grep -q "Booting version:" /tmp/m33mu-lpc55s69-dice-hwpuf.log - grep -q "Checking integrity...done" /tmp/m33mu-lpc55s69-dice-hwpuf.log - grep -q "Verifying signature...done" /tmp/m33mu-lpc55s69-dice-hwpuf.log - grep -q "attest: token size" /tmp/m33mu-lpc55s69-dice-hwpuf.log - grep -q "state=0x00" /tmp/m33mu-lpc55s69-dice-hwpuf.log - grep -q "\\[EXPECT BKPT\\] Success" /tmp/m33mu-lpc55s69-dice-hwpuf.log + rm -rf /tmp/m33mu-lpc55s69-hwpuf-persist + mkdir -p /tmp/m33mu-lpc55s69-hwpuf-persist + + # First boot enrolls the PUF and stores the record; the second boot + # must reuse the stored record and yield the same device identity. + - name: Run DICE attestation + HW PUF first boot (lpc55s69) + run: | + set -o pipefail + cd /tmp/m33mu-lpc55s69-hwpuf-persist + m33mu "$GITHUB_WORKSPACE/wolfboot.bin" \ + "$GITHUB_WORKSPACE/test-app/image_v1_signed.bin:0x48000" \ + --cpu lpc55s69 --persist --uart-stdout --expect-bkpt 0x7f --timeout 300 \ + | tee /tmp/m33mu-lpc55s69-dice-hwpuf-first.log + + - name: Run DICE attestation + HW PUF second boot (lpc55s69) + run: | + set -o pipefail + cd /tmp/m33mu-lpc55s69-hwpuf-persist + m33mu "$GITHUB_WORKSPACE/wolfboot.bin" \ + "$GITHUB_WORKSPACE/test-app/image_v1_signed.bin:0x48000" \ + --cpu lpc55s69 --persist --uart-stdout --expect-bkpt 0x7f --timeout 300 \ + | tee /tmp/m33mu-lpc55s69-dice-hwpuf-second.log + + - name: Verify DICE attestation + HW PUF boots (lpc55s69) + run: | + for log in /tmp/m33mu-lpc55s69-dice-hwpuf-first.log /tmp/m33mu-lpc55s69-dice-hwpuf-second.log; do + grep -q "Booting version:" $log + grep -q "Checking integrity...done" $log + grep -q "Verifying signature...done" $log + grep -q "attest: token size" $log + grep -q "state=0x00" $log + grep -q "\\[EXPECT BKPT\\] Success" $log + done + # UEID claim (key 256: 19 01 00) is a 33-byte bstr (58 21) + cat > /tmp/ueid.py <<'PYEOF' + import re, sys + data = bytearray() + seen = False + for line in open(sys.argv[1], errors="replace"): + if "attest: token size" in line: + seen = True + continue + if seen and line.startswith("\t"): + data += bytes.fromhex(" ".join( + re.findall(r"\b[0-9a-fA-F]{2}\b", line.split("|")[0]))) + i = data.find(bytes.fromhex("1901005821")) + if i < 0: + sys.exit("UEID claim not found in " + sys.argv[1]) + print(data[i + 5:i + 5 + 33].hex()) + PYEOF + first=$(python3 /tmp/ueid.py /tmp/m33mu-lpc55s69-dice-hwpuf-first.log) + second=$(python3 /tmp/ueid.py /tmp/m33mu-lpc55s69-dice-hwpuf-second.log) + echo "UEID first boot: $first" + echo "UEID second boot: $second" + test -n "$first" + test "$first" = "$second" diff --git a/arch.mk b/arch.mk index fbcd35aa22..4d0002fc78 100644 --- a/arch.mk +++ b/arch.mk @@ -2154,10 +2154,9 @@ ifeq ($(TARGET),lpc55s69) $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/hashcrypt_port.o endif ifeq ($(HWPUF),1) - CFLAGS+=-DWOLFSSL_HWPUF -DWOLFSSL_NXP_HWPUF + CFLAGS+=-DWOLFSSL_NXP_HWPUF OBJS+=\ $(MCUXPRESSO)/drivers/puf/fsl_puf.o \ - $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/cryptocb.o \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/hwpuf_port.o endif endif diff --git a/config/examples/lpc55s69-hwpuf.config b/config/examples/lpc55s69-hwpuf.config deleted file mode 100644 index 1269bf095f..0000000000 --- a/config/examples/lpc55s69-hwpuf.config +++ /dev/null @@ -1,44 +0,0 @@ -ARCH?=ARM -TZEN?=0 -TARGET?=lpc55s69 -SIGN?=ECC384 -HASH?=SHA256 -MCUXSDK?=1 -MCUXPRESSO?=$(PWD)/../NXP/mcuxpresso-sdk/mcuxsdk -MCUXPRESSO_CMSIS?=$(PWD)/../NXP/CMSIS_5/CMSIS -MCUXPRESSO_CPU?=LPC55S69JBD100_cm33_core0 -MCUXPRESSO_DRIVERS?=$(MCUXPRESSO)/devices/LPC/LPC5500/LPC55S69 -MCUXPRESSO_PROJECT_TEMPLATE?=$(MCUXPRESSO)/examples/_boards/lpcxpresso55s69/project_template -DEBUG?=0 -DEBUG_UART?=1 -VTOR?=1 -CORTEX_M0?=0 -CORTEX_M33?=1 -NO_ASM?=0 -NO_MPU=1 -EXT_FLASH?=0 -SPI_FLASH?=0 -ALLOW_DOWNGRADE?=0 -NVM_FLASH_WRITEONCE?=1 -NO_ARM_ASM=1 -WOLFBOOT_VERSION?=0 -V?=0 -SPMATH?=1 -RAM_CODE?=1 -DUALBANK_SWAP?=0 -FLASH_MULTI_SECTOR_ERASE?=1 - -# Turn on or off hw acceleration of crypto algs in the lpc55s69 -PKA?=0 -# Turn on or off hw puf -HWPUF?=0 - -WOLFBOOT_SECTOR_SIZE?=0x200 - -# use these for test/benchmark -WOLFBOOT_PARTITION_SIZE?=0x30000 -WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x10000 -WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x40000 -WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x70000 - -WOLFCRYPT_TEST?=1 diff --git a/docs/Targets.md b/docs/Targets.md index d19991dfc0..8be3bfb4ae 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -3427,9 +3427,7 @@ Basic hardware acceleration supported: - AES-ECB (128, 192, 256 key sizes) - AES-CBC (128, 192, 256 key sizes) - AES-OFB (128, 192, 256 key sizes) - - Only supports full 16-byte blocks - AES-CFB (128, 192, 256 key sizes) - - Only supports full 16-byte blocks - AES-CTR (128, 192, 256 key sizes) - User must avoid counter wrap of all ff's to 0, as this fails in the hardware @@ -3457,11 +3455,6 @@ To turn on HWPUF, set HWPUF=1 in the config file (see below). - Set PKA=1 in the config file to turn on hardware acceleration (off by default) -- [lpc55s69-hwpuf.config](/config/examples/lpc55s69-hwpuf.config): - - Same as `lpc55s69.config`, but turns on test code showing how to use the - hwpuf api - - Set HWPUF=1 in the config file to turn on HWPUF support (off by default) - - [lpc55s69-tz.config](/config/examples/lpc55s69-tz.config): - wolfBoot lives in the secure realm, test-app lives in the non-secure realm - Provides a standard PKCS #11 api to interface with crypto algs in the secure @@ -3485,6 +3478,18 @@ To turn on HWPUF, set HWPUF=1 in the config file (see below). first boot. Performs PUF enrollment and generates a UDS key for device attestation. Stores PUF keycodes in a known flash location and uses them when necessary. +- `WOLFBOOT_HWPUF_PROV_ADDRESS` : Flash address of the 4 KB PUF provisioning +record (default `0x80000`, page aligned). The build fails if it overlaps the +boot, update or swap partition; keep it out of the keyvault and NSC regions as +well. The activation code and key code are written first and verified, then a +commit word is written in the last page of the record, so an interrupted +provisioning is redone on the next boot. A record that is committed but +corrupted is not silently replaced: attestation fails until the region is +erased. + +`HWPUF=1` cannot be combined with `WOLFBOOT_UDS_UID_FALLBACK_FORTEST=1`. With +`TZEN=1` the PUF is reachable only from the secure world (it is excluded from +the non-secure SAU regions and is not linked into the non-secure test-app). ### LPC55S69: Configuring and compiling diff --git a/hal/lpc55s69.c b/hal/lpc55s69.c index c195f76abe..4d77fc4c0b 100644 --- a/hal/lpc55s69.c +++ b/hal/lpc55s69.c @@ -36,21 +36,21 @@ #include "fsl_usart.h" #include "hal.h" #include "loader.h" +#include "printf.h" + +#include #ifdef TZEN #include "hal/armv8m_tz.h" #endif -#include -#include -#include -#include - #ifdef WOLFSSL_NXP_HWPUF #include #endif #if defined(WOLFCRYPT_TZ_PSA) +#include + # if defined(WOLFBOOT_HASH_SHA256) # include # define HAL_KDF_HASH_TYPE WC_HASH_TYPE_SHA256 @@ -63,7 +63,7 @@ # else # error "No supported hash type for HAL_KDF" # endif -#endif +#endif /* WOLFCRYPT_TZ_PSA */ static flash_config_t pflash; static const uint32_t pflash_page_size = 512U; @@ -90,65 +90,107 @@ static NOINLINEFUNCTION void hal_zeroize(void *ptr, size_t len) } -#if defined(__WOLFBOOT) && defined(WOLFSSL_HWPUF) +#if defined(__WOLFBOOT) && defined(WOLFSSL_NXP_HWPUF) && \ + (defined(WOLFCRYPT_TZ_PSA) || defined(WOLFBOOT_HWPUF_PROVISION)) +#ifndef WOLFBOOT_HWPUF_PROV_ADDRESS +#define WOLFBOOT_HWPUF_PROV_ADDRESS 0x80000ul +#endif #define HWPUF_PROV_MAGIC 0x564f5250ul /* "PROV" */ -#define HWPUF_PROV_FLASH_BASEADR 0x80000ul #define HWPUF_PROV_FLASH_LEN 0x1000ul +#define HWPUF_PROV_PAGE_SIZE 512ul +/* The commit word lives alone in the last page of the region and is written + * only after the payload has been written and verified, so an interrupted + * provisioning never leaves a valid-looking record behind. */ +#define HWPUF_PROV_COMMIT_ADDRESS (WOLFBOOT_HWPUF_PROV_ADDRESS + \ + HWPUF_PROV_FLASH_LEN - HWPUF_PROV_PAGE_SIZE) #define HWPUF_PROV_UDS_KEY_INDEX 14 /* NOT the one in PFR */ #define HWPUF_PROV_UDS_KEY_SIZE 32 +#if (WOLFBOOT_HWPUF_PROV_ADDRESS % HWPUF_PROV_PAGE_SIZE) != 0 +#error "WOLFBOOT_HWPUF_PROV_ADDRESS must be aligned to the flash page size" +#endif +#define HWPUF_PROV_OVERLAPS(base, len) \ + ((WOLFBOOT_HWPUF_PROV_ADDRESS < ((base) + (len))) && \ + ((base) < (WOLFBOOT_HWPUF_PROV_ADDRESS + HWPUF_PROV_FLASH_LEN))) +#if HWPUF_PROV_OVERLAPS(WOLFBOOT_PARTITION_BOOT_ADDRESS, WOLFBOOT_PARTITION_SIZE) +#error "WOLFBOOT_HWPUF_PROV_ADDRESS overlaps the boot partition" +#endif +#if HWPUF_PROV_OVERLAPS(WOLFBOOT_PARTITION_UPDATE_ADDRESS, WOLFBOOT_PARTITION_SIZE) +#error "WOLFBOOT_HWPUF_PROV_ADDRESS overlaps the update partition" +#endif +#if HWPUF_PROV_OVERLAPS(WOLFBOOT_PARTITION_SWAP_ADDRESS, WOLFBOOT_SECTOR_SIZE) +#error "WOLFBOOT_HWPUF_PROV_ADDRESS overlaps the swap partition" +#endif + typedef struct hwpuf_prov { - word32 magic; byte ac[PUF_ACTIVATION_CODE_SIZE]; byte uds_kc[PUF_GET_KEY_CODE_SIZE_FOR_KEY_SIZE(HWPUF_PROV_UDS_KEY_SIZE)]; } hwpuf_prov; +/* The payload must end before the commit page */ +typedef char hwpuf_prov_size_check[ + (sizeof(hwpuf_prov) <= HWPUF_PROV_FLASH_LEN - HWPUF_PROV_PAGE_SIZE) ? + 1 : -1]; + +/* Static rather than on the (secure) stack; cleared on every exit path */ static hwpuf_prov prov; -static int hwpuf_provision_get(void) +#define HWPUF_PROV_BLANK 1 +#define HWPUF_PROV_EXISTS 2 + +/* Load the stored record into rec. + * Returns 0 if a committed record was loaded, HWPUF_PROV_BLANK if the + * region holds no committed record (never provisioned, or provisioning was + * interrupted before the commit), -1 if the region is corrupted. */ +static int hwpuf_provision_get(hwpuf_prov *rec) { - if (sizeof(prov) > HWPUF_PROV_FLASH_LEN) - return -1; + uint32_t addr; + uint32_t end = WOLFBOOT_HWPUF_PROV_ADDRESS + + ((sizeof(*rec) + HWPUF_PROV_PAGE_SIZE - 1) & + ~(HWPUF_PROV_PAGE_SIZE - 1)); flashInit(); - /* verify none of sectors in erased state */ - { - uint32_t addr; - uint32_t start = HWPUF_PROV_FLASH_BASEADR; - uint32_t end = HWPUF_PROV_FLASH_BASEADR + - ((sizeof(prov) + pflash_page_size - 1) & ~(pflash_page_size - 1)); - - for (addr = start; addr < end; addr += pflash_page_size) { - if (FLASH_VerifyErase(&pflash, addr, pflash_page_size) - == kStatus_FLASH_Success) { - return -1; - } - } - } + /* Reading a never-programmed page faults on ECC, check before reading */ + if (FLASH_VerifyErase(&pflash, HWPUF_PROV_COMMIT_ADDRESS, + HWPUF_PROV_PAGE_SIZE) == kStatus_FLASH_Success) + return HWPUF_PROV_BLANK; - XMEMCPY(&prov, (byte *)HWPUF_PROV_FLASH_BASEADR, sizeof(prov)); + if (*(volatile uint32_t *)HWPUF_PROV_COMMIT_ADDRESS == 0xFFFFFFFFul) + return HWPUF_PROV_BLANK; - if (prov.magic != HWPUF_PROV_MAGIC) + if (*(volatile uint32_t *)HWPUF_PROV_COMMIT_ADDRESS != HWPUF_PROV_MAGIC) return -1; + for (addr = WOLFBOOT_HWPUF_PROV_ADDRESS; addr < end; + addr += HWPUF_PROV_PAGE_SIZE) { + if (FLASH_VerifyErase(&pflash, addr, HWPUF_PROV_PAGE_SIZE) + == kStatus_FLASH_Success) + return -1; + } + + XMEMCPY(rec, (const void *)WOLFBOOT_HWPUF_PROV_ADDRESS, sizeof(*rec)); return 0; } #ifdef WOLFBOOT_HWPUF_PROVISION +/* Enroll the PUF and store the record. + * Returns 0 when newly provisioned, HWPUF_PROV_EXISTS when a committed record + * already exists, -1 on failure. A corrupted record is only replaced when + * force is set, so the device identity is never silently re-created. */ static int hwpuf_provision_set(int force) { - int ret = -1; + uint32_t magic = HWPUF_PROV_MAGIC; + int ret; - if (sizeof(prov) > HWPUF_PROV_FLASH_LEN) + ret = hwpuf_provision_get(&prov); + hal_zeroize(&prov, sizeof(prov)); + if (ret == 0) + return HWPUF_PROV_EXISTS; + if (ret < 0 && !force) return -1; - if (!force) { - if (hwpuf_provision_get() == 0) - return -1; - } - - XMEMSET(&prov, 0, sizeof(prov)); + ret = -1; if (nxp_hwpuf_Init() != 0) goto error_out; @@ -156,8 +198,11 @@ static int hwpuf_provision_set(int force) if (nxp_hwpuf_Enroll(prov.ac, sizeof(prov.ac)) != 0) goto error_out; - (void)nxp_hwpuf_Deinit(); - (void)nxp_hwpuf_Init(); + if (nxp_hwpuf_Deinit() != 0) + goto error_out; + + if (nxp_hwpuf_Init() != 0) + goto error_out; if (nxp_hwpuf_Start(prov.ac, sizeof(prov.ac)) != 0) goto error_out; @@ -166,24 +211,27 @@ static int hwpuf_provision_set(int force) prov.uds_kc, sizeof(prov.uds_kc)) != 0) goto error_out; - prov.magic = HWPUF_PROV_MAGIC; - flashInit(); hal_flash_unlock(); - ret = hal_flash_erase(HWPUF_PROV_FLASH_BASEADR, HWPUF_PROV_FLASH_LEN); - if (ret != 0) { - hal_flash_lock(); - goto error_out; - } - ret = hal_flash_write(HWPUF_PROV_FLASH_BASEADR, - (const uint8_t *)&prov, sizeof(prov)); - hal_flash_lock(); - if (ret != 0) - goto error_out; + if (hal_flash_erase(WOLFBOOT_HWPUF_PROV_ADDRESS, HWPUF_PROV_FLASH_LEN) != 0) + goto unlock_out; + if (hal_flash_write(WOLFBOOT_HWPUF_PROV_ADDRESS, + (const uint8_t *)&prov, sizeof(prov)) != 0) + goto unlock_out; + if (XMEMCMP((const void *)WOLFBOOT_HWPUF_PROV_ADDRESS, &prov, + sizeof(prov)) != 0) + goto unlock_out; + if (hal_flash_write(HWPUF_PROV_COMMIT_ADDRESS, + (const uint8_t *)&magic, sizeof(magic)) != 0) + goto unlock_out; + if (*(volatile uint32_t *)HWPUF_PROV_COMMIT_ADDRESS != HWPUF_PROV_MAGIC) + goto unlock_out; ret = 0; +unlock_out: + hal_flash_lock(); error_out: hal_zeroize(&prov, sizeof(prov)); /* No nxp_hwpuf_Zeroize(): it locks the PUF until the next POR, and the @@ -199,16 +247,19 @@ static int uds_from_hwpuf(uint8_t *out, size_t out_len) int ret = -1; byte uds[HWPUF_PROV_UDS_KEY_SIZE]; - if (hwpuf_provision_get() != 0) + if (hwpuf_provision_get(&prov) != 0) { + hal_zeroize(&prov, sizeof(prov)); return -1; + } if (nxp_hwpuf_Init() != 0) - return -1; + goto error_out; if (nxp_hwpuf_Start(prov.ac, sizeof(prov.ac)) != 0) goto error_out; - ret = nxp_hwpuf_GetKey(prov.uds_kc, sizeof(prov.uds_kc), uds, sizeof(uds)); + ret = nxp_hwpuf_GetKey(prov.uds_kc, sizeof(prov.uds_kc), + uds, sizeof(uds)); if (ret != 0) goto error_out; @@ -217,8 +268,6 @@ static int uds_from_hwpuf(uint8_t *out, size_t out_len) (const byte *)"WOLFBOOT-UDS", (word32)12, (byte *)out, (word32)out_len, NULL, INVALID_DEVID); - if (ret != 0) - goto error_out; error_out: hal_zeroize(uds, sizeof(uds)); @@ -226,40 +275,8 @@ static int uds_from_hwpuf(uint8_t *out, size_t out_len) (void)nxp_hwpuf_Deinit(); return ret == 0 ? 0 : -1; } - -/* EAT UEID for the HWPUF path: derived from the UDS inside the HAL so the - * key code never leaves the platform security boundary (dice.c HW flow). */ -int hal_attestation_get_ueid(uint8_t *buf, size_t *len) -{ - uint8_t uds[HWPUF_PROV_UDS_KEY_SIZE]; - wc_Sha256 sha; - int ret; - - if (buf == NULL || len == NULL || *len < 33) - return -1; - - ret = uds_from_hwpuf(uds, sizeof(uds)); - if (ret != 0) - return -1; - - ret = wc_InitSha256(&sha); - if (ret == 0) { - ret = wc_Sha256Update(&sha, uds, (word32)sizeof(uds)); - if (ret == 0) - ret = wc_Sha256Final(&sha, &buf[1]); - wc_Sha256Free(&sha); - } - hal_zeroize(uds, sizeof(uds)); - if (ret != 0) - return -1; - - /* UEID type RANDOM per EAT spec */ - buf[0] = 0x01; - *len = 33; - return 0; -} #endif /* WOLFCRYPT_TZ_PSA */ -#endif /* __WOLFBOOT && WOLFSSL_HWPUF */ +#endif /* __WOLFBOOT && WOLFSSL_NXP_HWPUF && (TZ_PSA || HWPUF_PROVISION) */ #if defined(__WOLFBOOT) && defined(WOLFCRYPT_TZ_PSA) static void reverse_array(byte *s, int len) @@ -281,16 +298,19 @@ static void reverse_array(byte *s, int len) } #endif -#if defined(__WOLFBOOT) && defined(WOLFCRYPT_TZ_PSA) && !defined(WOLFBOOT_DICE_HW) +#if defined(__WOLFBOOT) && defined(WOLFCRYPT_TZ_PSA) #ifdef WOLFBOOT_UDS_UID_FALLBACK_FORTEST static int uds_from_uid(uint8_t *out, size_t out_len) { uint8_t uid[16]; #if defined(WOLFBOOT_HASH_SHA256) + wc_Sha256 hash; uint8_t digest[SHA256_DIGEST_SIZE]; #elif defined(WOLFBOOT_HASH_SHA384) + wc_Sha384 hash; uint8_t digest[SHA384_DIGEST_SIZE]; #elif defined(WOLFBOOT_HASH_SHA3_384) + wc_Sha3 hash; uint8_t digest[SHA3_384_DIGEST_SIZE]; #endif size_t copy_len = sizeof(digest); @@ -303,64 +323,46 @@ static int uds_from_uid(uint8_t *out, size_t out_len) reverse_array(uid, sizeof(uid)); #if defined(WOLFBOOT_HASH_SHA256) - { - wc_Sha256 hash; - ret = wc_InitSha256(&hash); - if (ret == 0) { - ret = wc_Sha256Update(&hash, uid, sizeof(uid)); - if (ret == 0) - ret = wc_Sha256Final(&hash, digest); - wc_Sha256Free(&hash); - } + ret = wc_InitSha256(&hash); + if (ret == 0) { + ret = wc_Sha256Update(&hash, uid, sizeof(uid)); + if (ret == 0) + ret = wc_Sha256Final(&hash, digest); + wc_Sha256Free(&hash); } #elif defined(WOLFBOOT_HASH_SHA384) - { - wc_Sha384 hash; - ret = wc_InitSha384(&hash); - if (ret == 0) { - ret = wc_Sha384Update(&hash, uid, sizeof(uid)); - if (ret == 0) - ret = wc_Sha384Final(&hash, digest); - wc_Sha384Free(&hash); - } + ret = wc_InitSha384(&hash); + if (ret == 0) { + ret = wc_Sha384Update(&hash, uid, sizeof(uid)); + if (ret == 0) + ret = wc_Sha384Final(&hash, digest); + wc_Sha384Free(&hash); } #elif defined(WOLFBOOT_HASH_SHA3_384) - { - wc_Sha3 hash; - ret = wc_InitSha3_384(&hash, NULL, INVALID_DEVID); - if (ret == 0) { - ret = wc_Sha3_384_Update(&hash, uid, sizeof(uid)); - if (ret == 0) - ret = wc_Sha3_384_Final(&hash, digest); - wc_Sha3_384_Free(&hash); - } + ret = wc_InitSha3_384(&hash, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_Sha3_384_Update(&hash, uid, sizeof(uid)); + if (ret == 0) + ret = wc_Sha3_384_Final(&hash, digest); + wc_Sha3_384_Free(&hash); } #endif - if (ret != 0) { - wc_ForceZero(uid, sizeof(uid)); - wc_ForceZero(digest, sizeof(digest)); - return -1; + if (ret == 0) { + if (copy_len > out_len) + copy_len = out_len; + XMEMCPY(out, digest, copy_len); } - if (copy_len > out_len) { - copy_len = out_len; - } - memcpy(out, digest, copy_len); -#ifdef DEBUG - { - int idx; - wolfBoot_printf("[ATTEST] UDS FORTEST:"); - for (idx = 0; idx < (int)out_len; ++idx) - wolfBoot_printf(" %02x", out[idx]); - wolfBoot_printf("\n"); - } -#endif - return 0; + hal_zeroize(uid, sizeof(uid)); + hal_zeroize(digest, sizeof(digest)); + hal_zeroize(&hash, sizeof(hash)); + return ret == 0 ? 0 : -1; } #endif /* WOLFBOOT_UDS_UID_FALLBACK_FORTEST */ -/* Derive UDS from hw puf, fall back to derive from uid */ +/* Derive UDS: from the device UID when WOLFBOOT_UDS_UID_FALLBACK_FORTEST (test + * only), otherwise from the HW PUF. There is no runtime fallback. */ int hal_uds_derive_key(uint8_t *out, size_t out_len) { if (out == NULL || out_len == 0) @@ -368,22 +370,25 @@ int hal_uds_derive_key(uint8_t *out, size_t out_len) #ifdef WOLFBOOT_UDS_UID_FALLBACK_FORTEST return uds_from_uid(out, out_len); -#elif defined(WOLFSSL_HWPUF) +#elif defined(WOLFSSL_NXP_HWPUF) return uds_from_hwpuf(out, out_len); #else return -1; #endif } -#endif /* __WOLFBOOT && WOLFCRYPT_TZ_PSA && !WOLFBOOT_DICE_HW */ +#endif /* __WOLFBOOT && WOLFCRYPT_TZ_PSA */ #ifdef TZEN /* AHB_SECURE_CTRL (AHBSC) gates SRAM at the bus level on this part: the SAU * alone does not make an SRAM block non-secure. The boot ROM leaves every * 4 KB block secure+privileged; hand SRAM2 (0x20020000..0x2002FFFF, the NS - * app RAM) to the NS world as non-secure+privileged before the NS jump. */ -#define AHBSC_BASE 0x400AC000u -#define AHBSC_SRAM2_RULE0 (*(volatile uint32_t *)(AHBSC_BASE + 0xA0u)) -#define AHBSC_SRAM2_RULE1 (*(volatile uint32_t *)(AHBSC_BASE + 0xA4u)) + * app RAM) to the NS world as non-secure+privileged before the NS jump. + * Unprivileged NS code is intentionally kept out of SRAM2: the NS test-app + * runs privileged. + * SEC_CTRL_RAM2 MEM_RULE0/1 (UM11126): the SDK header only exposes MEM_RULE0, + * so both are addressed by offset from the secure alias. */ +#define AHBSC_SRAM2_RULE0 (*(volatile uint32_t *)(AHB_SECURE_CTRL_BASE + 0xA0u)) +#define AHBSC_SRAM2_RULE1 (*(volatile uint32_t *)(AHB_SECURE_CTRL_BASE + 0xA4u)) static void hal_ahbsc_init(void) { @@ -408,7 +413,15 @@ static void hal_sau_init(void) sau_init_region(2, 0x20020000, 0x2002FFFF, 0); /* Peripherals */ +#ifdef WOLFSSL_NXP_HWPUF + /* Leave the PUF (0x4003B000..0x4003BFFF) out of the NS regions: it is + * the root of the attestation identity and is driven only by the + * secure world. */ + sau_init_region(3, 0x40000000, 0x4003AFFF, 0); + sau_init_region(6, 0x4003C000, 0x4003FFFF, 0); +#else sau_init_region(3, 0x40000000, 0x4003FFFF, 0); +#endif sau_init_region(4, 0x40080000, 0x400AFFFF, 0); sau_init_region(5, 0x40100000, 0x4010FFFF, 0); @@ -424,7 +437,7 @@ static void periph_unsecure(void) CLOCK_EnableClock(kCLOCK_Iocon); CLOCK_EnableClock(kCLOCK_Gpio1); } -#endif +#endif /* TZEN */ static void hal_flash_fix_ecc(void) { @@ -433,7 +446,7 @@ static void hal_flash_fix_ecc(void) uint32_t start = WOLFBOOT_PARTITION_BOOT_ADDRESS; uint32_t end = WOLFBOOT_PARTITION_SWAP_ADDRESS + WOLFBOOT_SECTOR_SIZE; - memset(page_buf, 0xFF, sizeof(page_buf)); + XMEMSET(page_buf, 0xFF, sizeof(page_buf)); for (addr = start; addr < end; addr += pflash_page_size) { if (FLASH_VerifyErase(&pflash, addr, pflash_page_size) @@ -470,6 +483,11 @@ void hal_init(void) RNG_Init(RNG); #endif /* __WOLFBOOT */ +#if defined(WOLFSSL_NXP_HWPUF) && (defined(__WOLFBOOT) || !defined(TZEN)) + /* FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL: PUF_Init() won't do this */ + CLOCK_EnableClock(kCLOCK_Puf); +#endif + #if defined(__WOLFBOOT) || !defined(TZEN) flashInit(); hal_flash_fix_ecc(); @@ -479,8 +497,10 @@ void hal_init(void) hal_sau_init(); #endif -#ifdef __WOLFBOOT - wolfCrypt_Init(); +#if defined(__WOLFBOOT) && \ + (defined(WOLFCRYPT_TZ_PSA) || defined(WOLFSSL_NXP_HWPUF)) + if (wolfCrypt_Init() != 0) + wolfBoot_printf("wolfCrypt_Init failed\n"); #endif #if defined(__WOLFBOOT) && defined(WOLFBOOT_HWPUF_PROVISION) @@ -508,7 +528,7 @@ void hal_prepare_boot(void) periph_unsecure(); #endif } -#endif +#endif /* __WOLFBOOT */ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) { @@ -526,16 +546,16 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) if (FLASH_VerifyErase(&pflash, page_addr, pflash_page_size) == kStatus_FLASH_Success) { - memset(page_buf, 0xFF, pflash_page_size); + XMEMSET(page_buf, 0xFF, pflash_page_size); } else { - memcpy(page_buf, (void *)page_addr, pflash_page_size); + XMEMCPY(page_buf, (void *)page_addr, pflash_page_size); if (FLASH_Erase(&pflash, page_addr, pflash_page_size, kFLASH_ApiEraseKey) != kStatus_FLASH_Success) return -1; } - memcpy(page_buf + offset, data, chunk); + XMEMCPY(page_buf + offset, data, chunk); if (FLASH_Program(&pflash, page_addr, page_buf, pflash_page_size) != kStatus_FLASH_Success) @@ -565,7 +585,7 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len) if (address % pflash_page_size != 0 || len % pflash_page_size != 0) return -1; - memset(page_buf, 0xFF, sizeof(page_buf)); + XMEMSET(page_buf, 0xFF, sizeof(page_buf)); for (pos = address; pos < address + (uint32_t)len; pos += pflash_page_size) { if (FLASH_Erase(&pflash, pos, pflash_page_size, kFLASH_ApiEraseKey) @@ -597,7 +617,7 @@ int hal_trng_get_entropy(unsigned char *out, unsigned int len) return -1; } -#endif +#endif /* WOLFCRYPT_SECURE_MODE */ #define IOCON_PIO_DIGITAL_EN 0x0100u /*!<@brief Enables digital function */ @@ -675,4 +695,4 @@ void uart_write(const char *buf, unsigned int sz) sz -= line_sz + 1U; } } -#endif +#endif /* DEBUG_UART */ diff --git a/options.mk b/options.mk index 5811e17f6e..d723193c11 100644 --- a/options.mk +++ b/options.mk @@ -1197,6 +1197,15 @@ ifeq ($(WOLFBOOT_HWPUF_PROVISION),1) CFLAGS+=-DWOLFBOOT_HWPUF_PROVISION endif +ifeq ($(HWPUF),1) + ifeq ($(WOLFBOOT_UDS_UID_FALLBACK_FORTEST),1) + $(error HWPUF=1 cannot be combined with WOLFBOOT_UDS_UID_FALLBACK_FORTEST=1: the UDS would come from the public UID instead of the PUF) + endif + ifneq ($(WOLFBOOT_HWPUF_PROV_ADDRESS),) + CFLAGS+=-DWOLFBOOT_HWPUF_PROV_ADDRESS=$(WOLFBOOT_HWPUF_PROV_ADDRESS) + endif +endif + ifeq ($(PKCS11_STORE_STATS),1) CFLAGS+=-DPKCS11_STORE_STATS endif diff --git a/src/update_flash.c b/src/update_flash.c index 7ff3a4c426..0e68d8c514 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -1781,6 +1781,7 @@ void RAMFUNCTION wolfBoot_start(void) WP11_Library_Init(); #endif #endif /* !WOLFBOOT_SELF_UPDATE_MONOLITHIC */ + bootRet = wolfBoot_open_image(&boot, PART_BOOT); wolfBoot_printf("Booting version: 0x%x\n", wolfBoot_get_blob_version(boot.hdr)); diff --git a/test-app/Makefile b/test-app/Makefile index 29b54c4861..38fb4fc7f2 100644 --- a/test-app/Makefile +++ b/test-app/Makefile @@ -1197,12 +1197,14 @@ ifeq ($(TARGET),lpc55s69) $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/casper_port.o \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/hashcrypt_port.o endif + # Under TZEN the PUF belongs to the secure world only ifeq ($(HWPUF),1) - CFLAGS+=-DWOLFSSL_HWPUF -DWOLFSSL_NXP_HWPUF - APP_OBJS+=\ - $(MCUXPRESSO)/drivers/puf/fsl_puf.o \ - $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/cryptocb.o \ - $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/hwpuf_port.o + ifneq ($(TZEN),1) + CFLAGS+=-DWOLFSSL_NXP_HWPUF + APP_OBJS+=\ + $(MCUXPRESSO)/drivers/puf/fsl_puf.o \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/nxp/hwpuf_port.o + endif endif ifeq ($(WOLFCRYPT_SUPPORT),1) LDFLAGS+=--specs=nano.specs diff --git a/test-app/app_lpc55s69.c b/test-app/app_lpc55s69.c index 3694ca4713..e35aca6fad 100644 --- a/test-app/app_lpc55s69.c +++ b/test-app/app_lpc55s69.c @@ -259,7 +259,8 @@ void main(void) __asm__ volatile ("bkpt #0x7f"); } else { - wolfBoot_printf("Make sure WOLFBOOT_HWPUF_PROVISION is set\n"); + wolfBoot_printf("attest: failed (with HWPUF=1, the PUF must have been " + "provisioned: WOLFBOOT_HWPUF_PROVISION=1)\n"); __asm__ volatile ("bkpt #0x7e"); } #endif diff --git a/tools/config.mk b/tools/config.mk index ae30fc149e..94f82444ec 100644 --- a/tools/config.mk +++ b/tools/config.mk @@ -55,6 +55,7 @@ ifeq ($(ARCH),) PKA?=1 HWPUF?=0 WOLFBOOT_HWPUF_PROVISION?=0 + WOLFBOOT_HWPUF_PROV_ADDRESS?= PSOC6_CRYPTO?=1 WOLFTPM?=0 WOLFBOOT_TPM_VERIFY?=0 @@ -119,7 +120,7 @@ CONFIG_VARS:= ARCH TARGET SIGN HASH MCUXSDK MCUXPRESSO MCUXPRESSO_CPU MCUXPRESSO WOLFBOOT_ATTESTATION_TEST \ WOLFBOOT_UDS_UID_FALLBACK_FORTEST \ WOLFBOOT_UDS_OBKEYS \ - HWPUF WOLFBOOT_HWPUF_PROVISION \ + HWPUF WOLFBOOT_HWPUF_PROVISION WOLFBOOT_HWPUF_PROV_ADDRESS \ WOLFCRYPT_TZ WOLFCRYPT_TZ_PKCS11 \ WOLFCRYPT_TZ_PSA \ WOLFBOOT_DICE_HW \ From 11a08ccf98fdf2bb1ba0fa6ad3e36bc7251a23b6 Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Thu, 22 Oct 2026 18:18:49 -0400 Subject: [PATCH 24/25] fix tz emulator test --- .../workflows/trustzone-emulator-tests.yml | 66 ++++--------------- 1 file changed, 13 insertions(+), 53 deletions(-) diff --git a/.github/workflows/trustzone-emulator-tests.yml b/.github/workflows/trustzone-emulator-tests.yml index 95c9b91eb5..fc22b2ebb7 100644 --- a/.github/workflows/trustzone-emulator-tests.yml +++ b/.github/workflows/trustzone-emulator-tests.yml @@ -203,59 +203,19 @@ jobs: grep -q '^WOLFBOOT_HWPUF_PROVISION?=1' .config grep -q '^WOLFBOOT_UDS_UID_FALLBACK_FORTEST?=0' .config make MCUXSDK=1 MCUXPRESSO="$GITHUB_WORKSPACE/mcuxpresso-sdk/mcuxsdk" MCUXPRESSO_CMSIS="$GITHUB_WORKSPACE/CMSIS_5/CMSIS" - rm -rf /tmp/m33mu-lpc55s69-hwpuf-persist - mkdir -p /tmp/m33mu-lpc55s69-hwpuf-persist - # First boot enrolls the PUF and stores the record; the second boot - # must reuse the stored record and yield the same device identity. - - name: Run DICE attestation + HW PUF first boot (lpc55s69) + # Single boot: enroll the PUF, store the record, attest. + # TODO: add a second --persist boot checking the stored record is reused + # (same UEID) once the CI image ships an m33mu with lpc55s69 --persist + # support (m33mu c2d903e, after v1.23). + - name: Run DICE attestation + HW PUF (lpc55s69) run: | set -o pipefail - cd /tmp/m33mu-lpc55s69-hwpuf-persist - m33mu "$GITHUB_WORKSPACE/wolfboot.bin" \ - "$GITHUB_WORKSPACE/test-app/image_v1_signed.bin:0x48000" \ - --cpu lpc55s69 --persist --uart-stdout --expect-bkpt 0x7f --timeout 300 \ - | tee /tmp/m33mu-lpc55s69-dice-hwpuf-first.log - - - name: Run DICE attestation + HW PUF second boot (lpc55s69) - run: | - set -o pipefail - cd /tmp/m33mu-lpc55s69-hwpuf-persist - m33mu "$GITHUB_WORKSPACE/wolfboot.bin" \ - "$GITHUB_WORKSPACE/test-app/image_v1_signed.bin:0x48000" \ - --cpu lpc55s69 --persist --uart-stdout --expect-bkpt 0x7f --timeout 300 \ - | tee /tmp/m33mu-lpc55s69-dice-hwpuf-second.log - - - name: Verify DICE attestation + HW PUF boots (lpc55s69) - run: | - for log in /tmp/m33mu-lpc55s69-dice-hwpuf-first.log /tmp/m33mu-lpc55s69-dice-hwpuf-second.log; do - grep -q "Booting version:" $log - grep -q "Checking integrity...done" $log - grep -q "Verifying signature...done" $log - grep -q "attest: token size" $log - grep -q "state=0x00" $log - grep -q "\\[EXPECT BKPT\\] Success" $log - done - # UEID claim (key 256: 19 01 00) is a 33-byte bstr (58 21) - cat > /tmp/ueid.py <<'PYEOF' - import re, sys - data = bytearray() - seen = False - for line in open(sys.argv[1], errors="replace"): - if "attest: token size" in line: - seen = True - continue - if seen and line.startswith("\t"): - data += bytes.fromhex(" ".join( - re.findall(r"\b[0-9a-fA-F]{2}\b", line.split("|")[0]))) - i = data.find(bytes.fromhex("1901005821")) - if i < 0: - sys.exit("UEID claim not found in " + sys.argv[1]) - print(data[i + 5:i + 5 + 33].hex()) - PYEOF - first=$(python3 /tmp/ueid.py /tmp/m33mu-lpc55s69-dice-hwpuf-first.log) - second=$(python3 /tmp/ueid.py /tmp/m33mu-lpc55s69-dice-hwpuf-second.log) - echo "UEID first boot: $first" - echo "UEID second boot: $second" - test -n "$first" - test "$first" = "$second" + m33mu wolfboot.bin test-app/image_v1_signed.bin:0x48000 --cpu lpc55s69 --uart-stdout --expect-bkpt 0x7f --timeout 300 \ + | tee /tmp/m33mu-lpc55s69-dice-hwpuf.log + grep -q "Booting version:" /tmp/m33mu-lpc55s69-dice-hwpuf.log + grep -q "Checking integrity...done" /tmp/m33mu-lpc55s69-dice-hwpuf.log + grep -q "Verifying signature...done" /tmp/m33mu-lpc55s69-dice-hwpuf.log + grep -q "attest: token size" /tmp/m33mu-lpc55s69-dice-hwpuf.log + grep -q "state=0x00" /tmp/m33mu-lpc55s69-dice-hwpuf.log + grep -q "\\[EXPECT BKPT\\] Success" /tmp/m33mu-lpc55s69-dice-hwpuf.log From 28d717103ac4b274fd7a4a5865ba5d64b6a36c8d Mon Sep 17 00:00:00 2001 From: Thomas Cook Date: Fri, 23 Oct 2026 13:35:15 -0400 Subject: [PATCH 25/25] address pr comments --- docs/Targets.md | 16 +++-- hal/lpc55s69.c | 141 +++++++++++++++++++++++++++------------- test-app/app_lpc55s69.c | 30 --------- 3 files changed, 106 insertions(+), 81 deletions(-) diff --git a/docs/Targets.md b/docs/Targets.md index 8be3bfb4ae..5d4e9f2ba4 100644 --- a/docs/Targets.md +++ b/docs/Targets.md @@ -3480,12 +3480,16 @@ attestation. Stores PUF keycodes in a known flash location and uses them when necessary. - `WOLFBOOT_HWPUF_PROV_ADDRESS` : Flash address of the 4 KB PUF provisioning record (default `0x80000`, page aligned). The build fails if it overlaps the -boot, update or swap partition; keep it out of the keyvault and NSC regions as -well. The activation code and key code are written first and verified, then a -commit word is written in the last page of the record, so an interrupted -provisioning is redone on the next boot. A record that is committed but -corrupted is not silently replaced: attestation fails until the region is -erased. +boot, update or swap partition or the protected flash region (`0x9DE00` and +up), or if it lies below the boot partition (wolfBoot, keyvault and NSC). +The activation code and key code are written first and verified, then a +commit word is written in the last page of the record. Provisioning only +runs when that commit page is erased (or all `0xFF`), so a provisioning +interrupted before the commit is redone on the next boot. A record in any +other state is never silently replaced, because that would change the device +identity: attestation fails until the region is erased. This includes the +rare case where power is lost while the commit page itself is being erased or +programmed. `HWPUF=1` cannot be combined with `WOLFBOOT_UDS_UID_FALLBACK_FORTEST=1`. With `TZEN=1` the PUF is reachable only from the secure world (it is excluded from diff --git a/hal/lpc55s69.c b/hal/lpc55s69.c index 4d77fc4c0b..299247c8d9 100644 --- a/hal/lpc55s69.c +++ b/hal/lpc55s69.c @@ -121,6 +121,15 @@ static NOINLINEFUNCTION void hal_zeroize(void *ptr, size_t len) #if HWPUF_PROV_OVERLAPS(WOLFBOOT_PARTITION_SWAP_ADDRESS, WOLFBOOT_SECTOR_SIZE) #error "WOLFBOOT_HWPUF_PROV_ADDRESS overlaps the swap partition" #endif +/* wolfBoot itself, the keyvault and the NSC region sit below the boot + * partition */ +#if WOLFBOOT_HWPUF_PROV_ADDRESS < WOLFBOOT_PARTITION_BOOT_ADDRESS +#error "WOLFBOOT_HWPUF_PROV_ADDRESS must be above the wolfBoot image" +#endif +/* Protected flash region (PFR) starts at 0x9DE00 */ +#if (WOLFBOOT_HWPUF_PROV_ADDRESS + HWPUF_PROV_FLASH_LEN) > 0x9DE00ul +#error "WOLFBOOT_HWPUF_PROV_ADDRESS overlaps the protected flash region" +#endif typedef struct hwpuf_prov { byte ac[PUF_ACTIVATION_CODE_SIZE]; @@ -132,22 +141,25 @@ typedef char hwpuf_prov_size_check[ (sizeof(hwpuf_prov) <= HWPUF_PROV_FLASH_LEN - HWPUF_PROV_PAGE_SIZE) ? 1 : -1]; -/* Static rather than on the (secure) stack; cleared on every exit path */ +/* prov holds no secure information, so does not need aggressive zeroing */ static hwpuf_prov prov; #define HWPUF_PROV_BLANK 1 -#define HWPUF_PROV_EXISTS 2 /* Load the stored record into rec. - * Returns 0 if a committed record was loaded, HWPUF_PROV_BLANK if the - * region holds no committed record (never provisioned, or provisioning was - * interrupted before the commit), -1 if the region is corrupted. */ + * Returns 0 if a committed record was loaded, HWPUF_PROV_BLANK if the commit + * page is erased or all 0xFF (never provisioned, or interrupted before the + * commit), -1 for anything else. A record in any other state may be a + * committed one that got damaged, so it is never treated as blank: that + * would silently re-enroll the PUF and change the device identity. */ static int hwpuf_provision_get(hwpuf_prov *rec) { uint32_t addr; uint32_t end = WOLFBOOT_HWPUF_PROV_ADDRESS + ((sizeof(*rec) + HWPUF_PROV_PAGE_SIZE - 1) & ~(HWPUF_PROV_PAGE_SIZE - 1)); + uint32_t commit_page[HWPUF_PROV_PAGE_SIZE / sizeof(uint32_t)]; + uint32_t failed_addr, failed_data; flashInit(); @@ -156,9 +168,23 @@ static int hwpuf_provision_get(hwpuf_prov *rec) HWPUF_PROV_PAGE_SIZE) == kStatus_FLASH_Success) return HWPUF_PROV_BLANK; - if (*(volatile uint32_t *)HWPUF_PROV_COMMIT_ADDRESS == 0xFFFFFFFFul) + /* A page whose programming was cut short may fail ECC as well. Compare + * through the flash controller, which reports errors instead of + * faulting, and only read the page once it matches. */ + XMEMSET(commit_page, 0xFF, sizeof(commit_page)); + /* Programmed as 0xFF by hal_flash_erase(): erased, not committed */ + if (FLASH_VerifyProgram(&pflash, HWPUF_PROV_COMMIT_ADDRESS, + HWPUF_PROV_PAGE_SIZE, (const uint8_t *)commit_page, + &failed_addr, &failed_data) == kStatus_FLASH_Success && + *(volatile uint32_t *)HWPUF_PROV_COMMIT_ADDRESS == 0xFFFFFFFFul) return HWPUF_PROV_BLANK; + commit_page[0] = HWPUF_PROV_MAGIC; + if (FLASH_VerifyProgram(&pflash, HWPUF_PROV_COMMIT_ADDRESS, + HWPUF_PROV_PAGE_SIZE, (const uint8_t *)commit_page, + &failed_addr, &failed_data) != kStatus_FLASH_Success) + return -1; + if (*(volatile uint32_t *)HWPUF_PROV_COMMIT_ADDRESS != HWPUF_PROV_MAGIC) return -1; @@ -174,24 +200,23 @@ static int hwpuf_provision_get(hwpuf_prov *rec) } #ifdef WOLFBOOT_HWPUF_PROVISION -/* Enroll the PUF and store the record. - * Returns 0 when newly provisioned, HWPUF_PROV_EXISTS when a committed record - * already exists, -1 on failure. A corrupted record is only replaced when - * force is set, so the device identity is never silently re-created. */ -static int hwpuf_provision_set(int force) +/* Enroll the PUF and store the record, unless one is already stored. + * Returns 0 when newly or previously provisioned, -1 otherwise. Only a blank + * region is provisioned: a damaged record is left alone (attestation fails + * until the region is erased), so the device identity never changes + * silently. */ +static int hwpuf_provision_set(void) { uint32_t magic = HWPUF_PROV_MAGIC; int ret; ret = hwpuf_provision_get(&prov); - hal_zeroize(&prov, sizeof(prov)); - if (ret == 0) - return HWPUF_PROV_EXISTS; - if (ret < 0 && !force) - return -1; - + if (ret != HWPUF_PROV_BLANK) + return ret; ret = -1; + hal_zeroize(&prov, sizeof(prov)); + if (nxp_hwpuf_Init() != 0) goto error_out; @@ -233,9 +258,6 @@ static int hwpuf_provision_set(int force) unlock_out: hal_flash_lock(); error_out: - hal_zeroize(&prov, sizeof(prov)); - /* No nxp_hwpuf_Zeroize(): it locks the PUF until the next POR, and the - * UDS must still be derivable at runtime. Deinit powers the PUF down. */ (void)nxp_hwpuf_Deinit(); return ret; } @@ -247,10 +269,8 @@ static int uds_from_hwpuf(uint8_t *out, size_t out_len) int ret = -1; byte uds[HWPUF_PROV_UDS_KEY_SIZE]; - if (hwpuf_provision_get(&prov) != 0) { - hal_zeroize(&prov, sizeof(prov)); + if (hwpuf_provision_get(&prov) != 0) return -1; - } if (nxp_hwpuf_Init() != 0) goto error_out; @@ -271,7 +291,6 @@ static int uds_from_hwpuf(uint8_t *out, size_t out_len) error_out: hal_zeroize(uds, sizeof(uds)); - hal_zeroize(&prov, sizeof(prov)); (void)nxp_hwpuf_Deinit(); return ret == 0 ? 0 : -1; } @@ -384,9 +403,7 @@ int hal_uds_derive_key(uint8_t *out, size_t out_len) * 4 KB block secure+privileged; hand SRAM2 (0x20020000..0x2002FFFF, the NS * app RAM) to the NS world as non-secure+privileged before the NS jump. * Unprivileged NS code is intentionally kept out of SRAM2: the NS test-app - * runs privileged. - * SEC_CTRL_RAM2 MEM_RULE0/1 (UM11126): the SDK header only exposes MEM_RULE0, - * so both are addressed by offset from the secure alias. */ + * runs privileged. */ #define AHBSC_SRAM2_RULE0 (*(volatile uint32_t *)(AHB_SECURE_CTRL_BASE + 0xA0u)) #define AHBSC_SRAM2_RULE1 (*(volatile uint32_t *)(AHB_SECURE_CTRL_BASE + 0xA4u)) @@ -413,17 +430,8 @@ static void hal_sau_init(void) sau_init_region(2, 0x20020000, 0x2002FFFF, 0); /* Peripherals */ -#ifdef WOLFSSL_NXP_HWPUF - /* Leave the PUF (0x4003B000..0x4003BFFF) out of the NS regions: it is - * the root of the attestation identity and is driven only by the - * secure world. */ - sau_init_region(3, 0x40000000, 0x4003AFFF, 0); - sau_init_region(6, 0x4003C000, 0x4003FFFF, 0); -#else - sau_init_region(3, 0x40000000, 0x4003FFFF, 0); -#endif - sau_init_region(4, 0x40080000, 0x400AFFFF, 0); - sau_init_region(5, 0x40100000, 0x4010FFFF, 0); + sau_init_region(3, 0x40086000, 0x40086FFF, 0); /* uart on flexcomm0 */ + sau_init_region(4, 0x4008c000, 0x4008FFFF, 0); /* gpio for leds */ /* Enable SAU */ SAU_CTRL = SAU_INIT_CTRL_ENABLE; @@ -434,8 +442,6 @@ static void hal_sau_init(void) static void periph_unsecure(void) { - CLOCK_EnableClock(kCLOCK_Iocon); - CLOCK_EnableClock(kCLOCK_Gpio1); } #endif /* TZEN */ @@ -443,7 +449,7 @@ static void hal_flash_fix_ecc(void) { uint8_t page_buf[512]; uint32_t addr; - uint32_t start = WOLFBOOT_PARTITION_BOOT_ADDRESS; + uint32_t start = ARCH_FLASH_OFFSET; uint32_t end = WOLFBOOT_PARTITION_SWAP_ADDRESS + WOLFBOOT_SECTOR_SIZE; XMEMSET(page_buf, 0xFF, sizeof(page_buf)); @@ -456,6 +462,47 @@ static void hal_flash_fix_ecc(void) } } +#ifdef __WOLFBOOT +#define IOCON_PIO_FUNC0 0x00u /*!<@brief Selects pin function 0 */ +#define IOCON_PIO_MODE_PULLUP 0x20u /*!<@brief Selects pull-up function */ +#define IOCON_PIO_SLEW_STANDARD 0x00u /*!<@brief Standard mode, output slew rate control is enabled */ +#define IOCON_PIO_INV_DI 0x00u /*!<@brief Input function is not inverted */ +#define IOCON_PIO_DIGITAL_EN 0x0100u /*!<@brief Enables digital function */ +#define IOCON_PIO_OPENDRAIN_DI 0x00u /*!<@brief Open drain is disabled */ + +#define RED_LED 6 +#define GREEN_LED 7 +#define BLUE_LED 4 + +static void leds_pin_init(void) +{ + /* red output off */ + GPIO->SET[1] = 1UL << RED_LED; + GPIO->DIR[1] |= 1UL << RED_LED; + /* green output off */ + GPIO->SET[1] = 1UL << GREEN_LED; + GPIO->DIR[1] |= 1UL << GREEN_LED; + /* blue output off */ + GPIO->SET[1] = 1UL << BLUE_LED; + GPIO->DIR[1] |= 1UL << BLUE_LED; + + const uint32_t LED_PINMUX_CONFIG = (/* Pin is configured as PIO */ + IOCON_PIO_FUNC0 | + /* Selects pull-up function */ + IOCON_PIO_MODE_PULLUP | + /* Standard mode, output slew rate control is enabled */ + IOCON_PIO_SLEW_STANDARD | + /* Input function is not inverted */ + IOCON_PIO_INV_DI | + /* Enables digital function */ + IOCON_PIO_DIGITAL_EN | + /* Open drain is disabled */ + IOCON_PIO_OPENDRAIN_DI); + IOCON_PinMuxSet(IOCON, 1, RED_LED, LED_PINMUX_CONFIG); + IOCON_PinMuxSet(IOCON, 1, GREEN_LED, LED_PINMUX_CONFIG); + IOCON_PinMuxSet(IOCON, 1, BLUE_LED, LED_PINMUX_CONFIG); +} +#endif /* __WOLFBOOT */ extern int wc_hashcrypt_init(void); extern int wc_casper_init(void); @@ -466,6 +513,9 @@ void hal_init(void) /* lpc55s69 must run < 100 MHz for flash write/erase to work */ BOARD_BootClockFROHF96M(); + CLOCK_EnableClock(kCLOCK_Iocon); + CLOCK_EnableClock(kCLOCK_Gpio1); + # ifdef DEBUG_UART uart_init(); uart_write("lpc55s69 init\n", 14); @@ -481,6 +531,8 @@ void hal_init(void) CLOCK_EnableClock(kCLOCK_Rng); RESET_PeripheralReset(kRNG_RST_SHIFT_RSTn); RNG_Init(RNG); + + leds_pin_init(); #endif /* __WOLFBOOT */ #if defined(WOLFSSL_NXP_HWPUF) && (defined(__WOLFBOOT) || !defined(TZEN)) @@ -504,9 +556,9 @@ void hal_init(void) #endif #if defined(__WOLFBOOT) && defined(WOLFBOOT_HWPUF_PROVISION) - /* Provisioning failure is non-fatal: the UID fallback still derives - * the UDS, so the boot continues. */ - (void)hwpuf_provision_set(0); + /* Provisioning failure does not stop the boot, will run again next boot */ + if (hwpuf_provision_set() != 0) + wolfBoot_printf("hwpuf provision failed\n"); #endif } @@ -630,7 +682,6 @@ int hal_trng_get_entropy(unsigned char *out, unsigned int len) #ifdef DEBUG_UART void uart_init(void) { - CLOCK_EnableClock(kCLOCK_Iocon); const uint32_t port0_pin29_config = (/* Pin is configured as FC0_RXD_SDA_MOSI_DATA */ IOCON_PIO_FUNC1 | /* No addition pin function */ diff --git a/test-app/app_lpc55s69.c b/test-app/app_lpc55s69.c index e35aca6fad..1901134b6d 100644 --- a/test-app/app_lpc55s69.c +++ b/test-app/app_lpc55s69.c @@ -20,9 +20,7 @@ */ #include -#include "fsl_clock.h" #include "fsl_gpio.h" -#include "fsl_iocon.h" #include "target.h" #include "wolfboot/wolfboot.h" @@ -68,20 +66,8 @@ static void systick_init(void) } #endif -#define IOCON_PIO_FUNC0 0x00u /*!<@brief Selects pin function 0 */ -#define IOCON_PIO_MODE_PULLUP 0x20u /*!<@brief Selects pull-up function */ -#define IOCON_PIO_SLEW_STANDARD 0x00u /*!<@brief Standard mode, output slew rate control is enabled */ -#define IOCON_PIO_INV_DI 0x00u /*!<@brief Input function is not inverted */ -#define IOCON_PIO_DIGITAL_EN 0x0100u /*!<@brief Enables digital function */ -#define IOCON_PIO_OPENDRAIN_DI 0x00u /*!<@brief Open drain is disabled */ - static void leds_init(void) { -#ifndef TZEN - CLOCK_EnableClock(kCLOCK_Iocon); - CLOCK_EnableClock(kCLOCK_Gpio1); -#endif - /* red output off */ GPIO->SET[1] = 1UL << RED_LED; GPIO->DIR[1] |= 1UL << RED_LED; @@ -91,22 +77,6 @@ static void leds_init(void) /* blue output off */ GPIO->SET[1] = 1UL << BLUE_LED; GPIO->DIR[1] |= 1UL << BLUE_LED; - - const uint32_t LED_PINMUX_CONFIG = (/* Pin is configured as PIO */ - IOCON_PIO_FUNC0 | - /* Selects pull-up function */ - IOCON_PIO_MODE_PULLUP | - /* Standard mode, output slew rate control is enabled */ - IOCON_PIO_SLEW_STANDARD | - /* Input function is not inverted */ - IOCON_PIO_INV_DI | - /* Enables digital function */ - IOCON_PIO_DIGITAL_EN | - /* Open drain is disabled */ - IOCON_PIO_OPENDRAIN_DI); - IOCON_PinMuxSet(IOCON, 1, RED_LED, LED_PINMUX_CONFIG); - IOCON_PinMuxSet(IOCON, 1, GREEN_LED, LED_PINMUX_CONFIG); - IOCON_PinMuxSet(IOCON, 1, BLUE_LED, LED_PINMUX_CONFIG); } static void check_parts(