From 3f3554e13556a297dcf6a0730fce10b870822b45 Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Mon, 11 May 2026 14:03:14 -0600 Subject: [PATCH 1/2] Add support for wolfHSM multi-root certificate verification --- docs/wolfHSM.md | 3 ++- hal/aurix_tc3xx.c | 20 ++++++++++++++++---- hal/sim.c | 20 ++++++++++++++++---- include/hal.h | 7 +++++-- lib/wolfHSM | 2 +- src/image.c | 17 ++++++++++------- 6 files changed, 50 insertions(+), 19 deletions(-) diff --git a/docs/wolfHSM.md b/docs/wolfHSM.md index b6fafbcb94..c6fc294e3f 100644 --- a/docs/wolfHSM.md +++ b/docs/wolfHSM.md @@ -58,7 +58,7 @@ To use certificate verification with wolfHSM: 1. Enable `WOLFBOOT_CERT_CHAIN_VERIFY` in your wolfBoot configuration 2. Ensure the wolfHSM server is configured with certificate manager support (`WOLFHSM_CFG_CERTIFICATE_MANAGER`) -3. Pre-provision the root CA certificate on the wolfHSM server at the NVM ID specified by the HAL `hsmNvmIdCertRootCA` +3. Pre-provision one or more root CA certificates on the wolfHSM server at the NVM IDs listed in the HAL `hsmNvmIdCertRootCAList`. Verification succeeds if the embedded chain anchors to *any* root in the list (absent NVM IDs are silently skipped). The list length must not exceed `WOLFHSM_CFG_CERT_MAX_VERIFY_ROOTS` (default 8). 4. Sign firmware images with the `--cert-chain` option, providing a DER-encoded certificate chain To build the simulator using wolfHSM for certificate verification: @@ -96,6 +96,7 @@ In addition to the standard wolfBoot HAL functions, wolfHSM-enabled platforms mu - `hsmDevIdHash`: The HSM device ID for hash operations. This is used to identify the HSM device to wolfBoot. - `hsmDevIdPubKey`: The HSM device ID for public key operations. This is used to identify the HSM device to wolfBoot. - `hsmKeyIdPubKey`: The HSM key ID for public key operations. This is used to identify the key to use for public key operations. +- `hsmNvmIdCertRootCAList` / `hsmNvmIdCertRootCACount`: Array of NVM IDs identifying the trusted root CA certificate(s) and its element count. Only used when building with `WOLFBOOT_CERT_CHAIN_VERIFY`. The chain in the firmware header may anchor to any of the listed roots; the count is bounded by `WOLFHSM_CFG_CERT_MAX_VERIFY_ROOTS` (default 8). Each in-tree HAL provides a default of `{ 1 }`; override at build time by passing a comma-separated initializer in `WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST`, e.g. `make CFLAGS_EXTRA='-DWOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST="1, 2, 3"'`. ### Client HAL Functions diff --git a/hal/aurix_tc3xx.c b/hal/aurix_tc3xx.c index 5b5c9f2997..f38745370b 100644 --- a/hal/aurix_tc3xx.c +++ b/hal/aurix_tc3xx.c @@ -111,15 +111,27 @@ const int hsmDevIdCrypt = WH_DEV_ID; const int hsmKeyIdCrypt = 0xFF; #endif #ifdef WOLFBOOT_CERT_CHAIN_VERIFY -const whNvmId hsmNvmIdCertRootCA = 1; +/* Override at build time, e.g. -DWOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST="1, 2, 3" */ +#ifndef WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST +#define WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST 1 +#endif +const whNvmId hsmNvmIdCertRootCAList[] = { WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST }; +const uint16_t hsmNvmIdCertRootCACount = + sizeof(hsmNvmIdCertRootCAList) / sizeof(hsmNvmIdCertRootCAList[0]); #endif #elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) /*WOLFBOOT_ENABLE_WOLFHSM_CLIENT*/ /* map wolfBoot HAL layer wofHSM exports to their tchsm config vals */ -const int hsmDevIdHash = HSM_DEVID; -const int hsmDevIdPubKey = HSM_DEVID; -const whNvmId hsmNvmIdCertRootCA = 1; +const int hsmDevIdHash = HSM_DEVID; +const int hsmDevIdPubKey = HSM_DEVID; +/* Override at build time, e.g. -DWOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST="1, 2, 3" */ +#ifndef WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST +#define WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST 1 +#endif +const whNvmId hsmNvmIdCertRootCAList[] = { WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST }; +const uint16_t hsmNvmIdCertRootCACount = + sizeof(hsmNvmIdCertRootCAList) / sizeof(hsmNvmIdCertRootCAList[0]); #ifdef EXT_ENCRYPT #error "AURIX does not support firmware encryption with wolfHSM(yet)" const int hsmDevIdCrypt = INVALID_DEVID; /*HSM_DEVID once CCB enabled*/ diff --git a/hal/sim.c b/hal/sim.c index 702f381070..40867e906c 100644 --- a/hal/sim.c +++ b/hal/sim.c @@ -191,7 +191,13 @@ const int hsmDevIdCrypt = WH_DEV_ID; const int hsmKeyIdCrypt = 0xFF; #endif #ifdef WOLFBOOT_CERT_CHAIN_VERIFY -const whNvmId hsmNvmIdCertRootCA = 1; +/* Override at build time, e.g. -DWOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST="1, 2, 3" */ +#ifndef WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST +#define WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST 1 +#endif +const whNvmId hsmNvmIdCertRootCAList[] = { WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST }; +const uint16_t hsmNvmIdCertRootCACount = + sizeof(hsmNvmIdCertRootCAList) / sizeof(hsmNvmIdCertRootCAList[0]); #endif int hal_hsm_init_connect(void); @@ -257,9 +263,15 @@ whServerConfig s_conf[1] = {{ whServerContext hsmServerCtx = {0}; -const int hsmDevIdHash = INVALID_DEVID; -const int hsmDevIdPubKey = INVALID_DEVID; -const whNvmId hsmNvmIdCertRootCA = 1; +const int hsmDevIdHash = INVALID_DEVID; +const int hsmDevIdPubKey = INVALID_DEVID; +/* Override at build time, e.g. -DWOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST="1, 2, 3" */ +#ifndef WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST +#define WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST 1 +#endif +const whNvmId hsmNvmIdCertRootCAList[] = { WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST }; +const uint16_t hsmNvmIdCertRootCACount = + sizeof(hsmNvmIdCertRootCAList) / sizeof(hsmNvmIdCertRootCAList[0]); #ifdef EXT_ENCRYPT #error "Simulator does not support firmware encryption with wolfHSM(yet)" const int hsmDevIdCrypt = WH_DEV_ID; diff --git a/include/hal.h b/include/hal.h index 1082976591..a3a81bbdf9 100644 --- a/include/hal.h +++ b/include/hal.h @@ -221,8 +221,11 @@ extern const int hsmDevIdCrypt; /* devId for image (enc)decryption */ extern const int hsmKeyIdCrypt; /* KeyId for image (enc/dec)ryption */ #endif #ifdef WOLFBOOT_CERT_CHAIN_VERIFY -/* NvmId for trusted root CA certificate */ -extern const whNvmId hsmNvmIdCertRootCA; +/* List of NvmIds for trusted root CA certificates. Verification succeeds if + * the cert chain anchors to any root in the list. The list length must not + * exceed WOLFHSM_CFG_CERT_MAX_VERIFY_ROOTS. */ +extern const whNvmId hsmNvmIdCertRootCAList[]; +extern const uint16_t hsmNvmIdCertRootCACount; #endif #endif /* WOLFBOOT_ENABLE_WOLFHSM_CLIENT || WOLFBOOT_ENABLE_WOLFHSM_SERVER */ diff --git a/lib/wolfHSM b/lib/wolfHSM index 977bf187e7..80ee2d59ec 160000 --- a/lib/wolfHSM +++ b/lib/wolfHSM @@ -1 +1 @@ -Subproject commit 977bf187e7a57a184493dcd216eb9a328f381865 +Subproject commit 80ee2d59ecbfd46bd4b7b6ab8ed742ca5dac3e0f diff --git a/src/image.c b/src/image.c index f7a0384a3f..92ef90391e 100644 --- a/src/image.c +++ b/src/image.c @@ -2319,25 +2319,28 @@ int wolfBoot_verify_authenticity(struct wolfBoot_image *img) #if defined(WOLFHSM_CFG_DMA) wolfBoot_printf( "verifying cert chain and caching leaf pubkey (using DMA)\n"); - hsm_ret = wh_Client_CertVerifyDmaAndCacheLeafPubKey( - &hsmClientCtx, cert_chain, cert_chain_size, hsmNvmIdCertRootCA, + hsm_ret = wh_Client_CertVerifyMultiRootDmaAndCacheLeafPubKey( + &hsmClientCtx, cert_chain, cert_chain_size, + hsmNvmIdCertRootCAList, hsmNvmIdCertRootCACount, WH_NVM_FLAGS_USAGE_VERIFY, &g_certLeafKeyId, &cert_verify_result); #else wolfBoot_printf("verifying cert chain and caching leaf pubkey\n"); - hsm_ret = wh_Client_CertVerifyAndCacheLeafPubKey( - &hsmClientCtx, cert_chain, cert_chain_size, hsmNvmIdCertRootCA, + hsm_ret = wh_Client_CertVerifyMultiRootAndCacheLeafPubKey( + &hsmClientCtx, cert_chain, cert_chain_size, + hsmNvmIdCertRootCAList, hsmNvmIdCertRootCACount, WH_NVM_FLAGS_USAGE_VERIFY, &g_certLeafKeyId, &cert_verify_result); #endif #elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) wolfBoot_printf("verifying cert chain and caching leaf pubkey\n"); - hsm_ret = wh_Server_CertVerify( - &hsmServerCtx, cert_chain, cert_chain_size, hsmNvmIdCertRootCA, + hsm_ret = wh_Server_CertVerifyMultiRoot( + &hsmServerCtx, cert_chain, cert_chain_size, + hsmNvmIdCertRootCAList, hsmNvmIdCertRootCACount, WH_CERT_FLAGS_CACHE_LEAF_PUBKEY, WH_NVM_FLAGS_USAGE_VERIFY, &g_certLeafKeyId); if (hsm_ret == WH_ERROR_OK) { cert_verify_result = 0; } - wolfBoot_printf("wh_Server_CertVerify returned %d\n", hsm_ret); + wolfBoot_printf("wh_Server_CertVerifyMultiRoot returned %d\n", hsm_ret); #endif /* Error or verification failure results in standard auth check failure From 4ab0400cc63fb9981861238f8ffd9791d9a37bf7 Mon Sep 17 00:00:00 2001 From: Brett Nicholas <7547222+bigbrett@users.noreply.github.com> Date: Mon, 11 May 2026 17:09:38 -0600 Subject: [PATCH 2/2] expose root CA list as makefile var --- docs/wolfHSM.md | 2 +- hal/aurix_tc3xx.c | 6 ++++-- hal/sim.c | 6 ++++-- options.mk | 9 +++++++++ tools/config.mk | 1 + 5 files changed, 19 insertions(+), 5 deletions(-) diff --git a/docs/wolfHSM.md b/docs/wolfHSM.md index c6fc294e3f..6bdc040cac 100644 --- a/docs/wolfHSM.md +++ b/docs/wolfHSM.md @@ -96,7 +96,7 @@ In addition to the standard wolfBoot HAL functions, wolfHSM-enabled platforms mu - `hsmDevIdHash`: The HSM device ID for hash operations. This is used to identify the HSM device to wolfBoot. - `hsmDevIdPubKey`: The HSM device ID for public key operations. This is used to identify the HSM device to wolfBoot. - `hsmKeyIdPubKey`: The HSM key ID for public key operations. This is used to identify the key to use for public key operations. -- `hsmNvmIdCertRootCAList` / `hsmNvmIdCertRootCACount`: Array of NVM IDs identifying the trusted root CA certificate(s) and its element count. Only used when building with `WOLFBOOT_CERT_CHAIN_VERIFY`. The chain in the firmware header may anchor to any of the listed roots; the count is bounded by `WOLFHSM_CFG_CERT_MAX_VERIFY_ROOTS` (default 8). Each in-tree HAL provides a default of `{ 1 }`; override at build time by passing a comma-separated initializer in `WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST`, e.g. `make CFLAGS_EXTRA='-DWOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST="1, 2, 3"'`. +- `hsmNvmIdCertRootCAList` / `hsmNvmIdCertRootCACount`: Array of NVM IDs identifying the trusted root CA certificate(s) and its element count. Only used when building with `WOLFBOOT_CERT_CHAIN_VERIFY`. The chain in the firmware header may anchor to any of the listed roots; the count is bounded by `WOLFHSM_CFG_CERT_MAX_VERIFY_ROOTS` (default 8). Each in-tree HAL provides a default of `{ 1 }`; override the list via the `WOLFHSM_NVM_ROOT_CA_LIST` build option, which takes a comma-separated initializer (no quotes, no spaces) and is propagated to the HAL as `-DWOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST=...`. Set it in `.config` (e.g. `WOLFHSM_NVM_ROOT_CA_LIST=1,2,3`) or on the make command line (`make WOLFHSM_NVM_ROOT_CA_LIST=1,2,3 ...`). ### Client HAL Functions diff --git a/hal/aurix_tc3xx.c b/hal/aurix_tc3xx.c index f38745370b..71cdc89f21 100644 --- a/hal/aurix_tc3xx.c +++ b/hal/aurix_tc3xx.c @@ -111,7 +111,8 @@ const int hsmDevIdCrypt = WH_DEV_ID; const int hsmKeyIdCrypt = 0xFF; #endif #ifdef WOLFBOOT_CERT_CHAIN_VERIFY -/* Override at build time, e.g. -DWOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST="1, 2, 3" */ +/* Set WOLFHSM_NVM_ROOT_CA_LIST=1,2,3 in .config (or pass on the make command + * line) to override the default single-root list. */ #ifndef WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST #define WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST 1 #endif @@ -125,7 +126,8 @@ const uint16_t hsmNvmIdCertRootCACount = /* map wolfBoot HAL layer wofHSM exports to their tchsm config vals */ const int hsmDevIdHash = HSM_DEVID; const int hsmDevIdPubKey = HSM_DEVID; -/* Override at build time, e.g. -DWOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST="1, 2, 3" */ +/* Set WOLFHSM_NVM_ROOT_CA_LIST=1,2,3 in .config (or pass on the make command + * line) to override the default single-root list. */ #ifndef WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST #define WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST 1 #endif diff --git a/hal/sim.c b/hal/sim.c index 40867e906c..b799cdfaf0 100644 --- a/hal/sim.c +++ b/hal/sim.c @@ -191,7 +191,8 @@ const int hsmDevIdCrypt = WH_DEV_ID; const int hsmKeyIdCrypt = 0xFF; #endif #ifdef WOLFBOOT_CERT_CHAIN_VERIFY -/* Override at build time, e.g. -DWOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST="1, 2, 3" */ +/* Set WOLFHSM_NVM_ROOT_CA_LIST=1,2,3 in .config (or pass on the make command + * line) to override the default single-root list. */ #ifndef WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST #define WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST 1 #endif @@ -265,7 +266,8 @@ whServerContext hsmServerCtx = {0}; const int hsmDevIdHash = INVALID_DEVID; const int hsmDevIdPubKey = INVALID_DEVID; -/* Override at build time, e.g. -DWOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST="1, 2, 3" */ +/* Set WOLFHSM_NVM_ROOT_CA_LIST=1,2,3 in .config (or pass on the make command + * line) to override the default single-root list. */ #ifndef WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST #define WOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST 1 #endif diff --git a/options.mk b/options.mk index 442e20143a..dff203822b 100644 --- a/options.mk +++ b/options.mk @@ -1456,6 +1456,15 @@ ifneq ($(CERT_CHAIN_VERIFY),) # export the private key in DER format so it can be used with certificates KEYGEN_OPTIONS += --der + # Optional override for the wolfHSM trusted-root NVM ID list used during + # cert-chain verification. Expects a comma-separated initializer (no quotes, + # no spaces), e.g. WOLFHSM_NVM_ROOT_CA_LIST=1,2,3. Bounded by + # WOLFHSM_CFG_CERT_MAX_VERIFY_ROOTS. When unset, falls back to a HAL-specified + # default + ifneq ($(strip $(WOLFHSM_NVM_ROOT_CA_LIST)),) + CFLAGS += '-DWOLFBOOT_WOLFHSM_NVM_ROOT_CA_LIST=$(WOLFHSM_NVM_ROOT_CA_LIST)' + endif + # User-provided cert chain takes precedence ifneq ($(USER_CERT_CHAIN),) CERT_CHAIN_FILE = $(USER_CERT_CHAIN) diff --git a/tools/config.mk b/tools/config.mk index 15eefc3e01..42461aa781 100644 --- a/tools/config.mk +++ b/tools/config.mk @@ -132,4 +132,5 @@ CONFIG_VARS:= ARCH TARGET SIGN HASH MCUXSDK MCUXPRESSO MCUXPRESSO_CPU MCUXPRESSO SIGN_SECONDARY \ WOLFHSM_CLIENT \ WOLFHSM_CLIENT_LOCAL_KEYS \ + WOLFHSM_NVM_ROOT_CA_LIST \ ENCRYPT_CACHE