Skip to content

Commit 3d3c8d2

Browse files
committed
WIP: first stab at secondary root cert
1 parent e44e6b2 commit 3d3c8d2

10 files changed

Lines changed: 127 additions & 45 deletions

config/examples/sim-wolfHSM-client-certchain-ecc.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ IMAGE_HEADER_SIZE=2048
1616

1717
# wolfHSM options
1818
WOLFHSM_CLIENT=1
19+
#WOLFHSM_SECONDARY_ROOT_CA_NVM_ID=2
1920

2021
# sizes should be multiple of system page size
2122
#WOLFBOOT_PARTITION_SIZE=0x40000

config/examples/sim-wolfHSM-client-certchain-rsa4096.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ IMAGE_HEADER_SIZE=4096
1616

1717
# wolfHSM options
1818
WOLFHSM_CLIENT=1
19+
#WOLFHSM_SECONDARY_ROOT_CA_NVM_ID=2
1920

2021
# sizes should be multiple of system page size
2122
#WOLFBOOT_PARTITION_SIZE=0x40000

config/examples/sim-wolfHSM-server-certchain-ecc.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ IMAGE_HEADER_SIZE=2048
1616

1717
# wolfHSM options
1818
WOLFHSM_SERVER=1
19+
#WOLFHSM_SECONDARY_ROOT_CA_NVM_ID=2
1920

2021
# sizes should be multiple of system page size
2122
WOLFBOOT_PARTITION_SIZE=0x200000

config/examples/sim-wolfHSM-server-certchain-rsa4096.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ IMAGE_HEADER_SIZE=4096
1616

1717
# wolfHSM options
1818
WOLFHSM_SERVER=1
19+
#WOLFHSM_SECONDARY_ROOT_CA_NVM_ID=2
1920

2021
# sizes should be multiple of system page size
2122
WOLFBOOT_PARTITION_SIZE=0x200000

docs/Signing.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ When building wolfBoot and the test app with the Makefile, the `USER_*` variable
134134
- `USER_PRIVATE_KEY`: Path to your leaf signing key (DER format)
135135
- `USER_PUBLIC_KEY`: Path to your leaf public key (DER format)
136136
- `USER_CERT_CHAIN`: Path to your certificate chain (DER format)
137+
- `WOLFHSM_SECONDARY_ROOT_CA_NVM_ID`: Optional wolfHSM NVM object ID for a secondary root CA fallback
137138

138139
Example:
139140

@@ -150,7 +151,7 @@ If `USER_CERT_CHAIN` is not provided when `CERT_CHAIN_VERIFY=1`, a dummy certifi
150151

151152
**Note:** If your private key is managed by a third party and you only have access to the public key, use `keygen -i` to import it instead. See the [Keygen tool](#keygen-tool) section above.
152153

153-
Certificate chain verification of images is currently limited to use in conjunction with wolfHSM. See [wolfHSM.md](wolfHSM.md) for more details.
154+
Certificate chain verification of images is currently limited to use in conjunction with wolfHSM. For wolfHSM builds, `WOLFHSM_SECONDARY_ROOT_CA_NVM_ID=<id>` enables fallback to a second root CA only when primary root verification fails. The secondary root certificate must be provisioned in wolfHSM NVM at that ID. See [wolfHSM.md](wolfHSM.md) for more details.
154155

155156
#### Target partition id (Multiple partition images, "self-update" feature)
156157

docs/compile.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,10 @@ make CERT_CHAIN_VERIFY=1 \
441441

442442
When `CERT_CHAIN_VERIFY=1` is set without `USER_CERT_CHAIN`, the build auto-generates a dummy 3-tier certificate hierarchy in `test-dummy-ca/` for testing. This also applies to wolfHSM NVM image generation when applicable.
443443

444+
For wolfHSM certificate-chain builds, `WOLFHSM_SECONDARY_ROOT_CA_NVM_ID=<id>` can be set to compile in a secondary root CA fallback. wolfBoot verifies against the primary root CA NVM ID provided by the HAL first, then retries with the configured secondary NVM ID only if the primary certificate-chain verification fails. Leave it unset for the existing single-root behavior.
445+
446+
The secondary root certificate must be provisioned in wolfHSM NVM at the configured object ID. The option applies only to wolfHSM certificate-chain trust validation; signature verification continues to use the cached leaf public key from whichever root verified the chain.
447+
444448
### Manual Key Management
445449

446450
For advanced scenarios (multiple keys, mixed algorithms, partition-restricted keys, or third-party managed private keys), use the `keygen` tool directly instead of the `USER_*` variables.

docs/wolfHSM.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ The certificate verification process with wolfHSM works as follows:
5151
4. A certificate chain is created consisting of the signing identity certificate and an optional number of intermediate certificates, where trust is chained back to the root CA.
5252
5. During the signing process, the image is signed with the signer private key and the certificate chain is embedded in the firmware image header.
5353
6. During boot, wolfBoot extracts the certificate chain from the firmware header
54-
7. wolfBoot uses the wolfHSM server (remotely or directly, depending on configuration) to verify the certificate chain against a pre-provisioned root CA certificate stored on the HSM and caches the public key of the leaf certificate if the chain verifies as trusted
54+
7. wolfBoot uses the wolfHSM server (remotely or directly, depending on configuration) to verify the certificate chain against a pre-provisioned root CA certificate stored on the HSM and caches the public key of the leaf certificate if the chain verifies as trusted. If `WOLFHSM_SECONDARY_ROOT_CA_NVM_ID` is configured and the primary root check fails, wolfBoot retries the certificate chain against that secondary root CA NVM object.
5555
8. If the chain is trusted, wolfBoot uses the cached public key from the leaf certificate to verify the firmware signature on the wolfHSM server
5656

5757
To use certificate verification with wolfHSM:
@@ -61,6 +61,8 @@ To use certificate verification with wolfHSM:
6161
3. Pre-provision the root CA certificate on the wolfHSM server at the NVM ID specified by the HAL `hsmNvmIdCertRootCA`
6262
4. Sign firmware images with the `--cert-chain` option, providing a DER-encoded certificate chain
6363

64+
Optionally set `WOLFHSM_SECONDARY_ROOT_CA_NVM_ID=<id>` to allow certificate-chain fallback to a second wolfHSM root CA object. The secondary root certificate must be provisioned in wolfHSM NVM at the configured object ID. This fallback only affects wolfHSM certificate-chain trust validation; the image signature is still verified with the cached leaf public key from the chain that verified successfully.
65+
6466
To build the simulator using wolfHSM for certificate verification:
6567

6668
- **Client Mode**: Use [config/examples/sim-wolfHSM-client-certchain.config](config/examples/sim-wolfHSM-client-certchain.config)
@@ -84,6 +86,12 @@ This option enables use of the reserved wolfHSM public key ID for firmware authe
8486

8587
If this option is not defined, cryptographic operations are still performed on the wolfHSM server, but wolfBoot assumes the key material is present in the keystore and NOT stored on the HSM. This means that wolfBoot will first load keys from the keystore, send the key material to the wolfHSM server at the time of use (cached as ephemeral keys), and finally evict the key from the HSM after usage. This behavior is typically only useful for debugging or testing scenarios, where the keys may not be pre-loaded onto the HSM. The keystore for use in this mode should not be generated with the `--nolocalkeys` option.
8688

89+
### `WOLFHSM_SECONDARY_ROOT_CA_NVM_ID`
90+
91+
This Make/config option defines `WOLFBOOT_WOLFHSM_SECONDARY_ROOT_CA_NVM_ID` and enables a secondary root CA fallback for wolfHSM certificate-chain verification. wolfBoot first verifies the image certificate chain against `hsmNvmIdCertRootCA`. If that attempt fails due to a wolfHSM API error or a certificate validation failure, wolfBoot retries with the configured secondary NVM ID.
92+
93+
Leave this option unset to preserve the normal single-root behavior. When set, the secondary root CA certificate must already be provisioned in wolfHSM NVM at that object ID, for example `WOLFHSM_SECONDARY_ROOT_CA_NVM_ID=2`.
94+
8795
## HAL Implementations
8896

8997
In addition to the standard wolfBoot HAL functions, wolfHSM-enabled platforms must also implement or instantiate the following wolfHSM-specific items in the platform HAL:
@@ -96,6 +104,7 @@ In addition to the standard wolfBoot HAL functions, wolfHSM-enabled platforms mu
96104
- `hsmDevIdHash`: The HSM device ID for hash operations. This is used to identify the HSM device to wolfBoot.
97105
- `hsmDevIdPubKey`: The HSM device ID for public key operations. This is used to identify the HSM device to wolfBoot.
98106
- `hsmKeyIdPubKey`: The HSM key ID for public key operations. This is used to identify the key to use for public key operations.
107+
- `hsmNvmIdCertRootCA`: The wolfHSM NVM ID for the primary trusted root CA certificate when certificate-chain verification is enabled.
99108

100109
### Client HAL Functions
101110

options.mk

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1384,6 +1384,11 @@ ifeq ($(WOLFHSM_SERVER),1)
13841384

13851385
endif
13861386

1387+
# Optional secondary wolfHSM root CA NVM ID for certificate-chain fallback.
1388+
ifneq ($(WOLFHSM_SECONDARY_ROOT_CA_NVM_ID),)
1389+
CFLAGS += -DWOLFBOOT_WOLFHSM_SECONDARY_ROOT_CA_NVM_ID=$(WOLFHSM_SECONDARY_ROOT_CA_NVM_ID)
1390+
endif
1391+
13871392
# wolfBoot hooks framework
13881393
# WOLFBOOT_HOOKS_FILE: path to a single .c file containing hook definitions
13891394
WOLFBOOT_HOOKS_ENABLED :=

src/image.c

Lines changed: 100 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,59 @@ int NOINLINEFUNCTION image_CT_compare(
7676
defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER))
7777
static whKeyId g_certLeafKeyId = WH_KEYID_ERASED;
7878
static int g_leafKeyIdValid = 0;
79+
80+
static void wolfBoot_cert_chain_evict_leaf_key(void)
81+
{
82+
if (g_certLeafKeyId != WH_KEYID_ERASED) {
83+
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
84+
(void)wh_Client_KeyEvict(&hsmClientCtx, g_certLeafKeyId);
85+
#elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
86+
(void)wh_Server_KeystoreEvictKey(&hsmServerCtx, g_certLeafKeyId);
87+
#endif
88+
}
89+
90+
g_certLeafKeyId = WH_KEYID_ERASED;
91+
g_leafKeyIdValid = 0;
92+
}
93+
94+
static int wolfBoot_cert_chain_verify_with_root(uint8_t *cert_chain,
95+
uint16_t cert_chain_size, whNvmId root_id, int32_t *verify_result)
96+
{
97+
int hsm_ret = WH_ERROR_OK;
98+
99+
*verify_result = -1;
100+
101+
/* Verify certificate chain using wolfHSM's verification API. Use DMA if
102+
* available in the wolfHSM configuration. */
103+
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
104+
#if defined(WOLFHSM_CFG_DMA)
105+
wolfBoot_printf("verifying cert chain and caching leaf pubkey "
106+
"(root=%08x, using DMA)\n", (unsigned int)root_id);
107+
hsm_ret = wh_Client_CertVerifyDmaAndCacheLeafPubKey(
108+
&hsmClientCtx, cert_chain, cert_chain_size, root_id,
109+
WH_NVM_FLAGS_USAGE_VERIFY, &g_certLeafKeyId, verify_result);
110+
#else
111+
wolfBoot_printf("verifying cert chain and caching leaf pubkey "
112+
"(root=%08x)\n", (unsigned int)root_id);
113+
hsm_ret = wh_Client_CertVerifyAndCacheLeafPubKey(
114+
&hsmClientCtx, cert_chain, cert_chain_size, root_id,
115+
WH_NVM_FLAGS_USAGE_VERIFY, &g_certLeafKeyId, verify_result);
116+
#endif
117+
#elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
118+
wolfBoot_printf("verifying cert chain and caching leaf pubkey "
119+
"(root=%08x)\n", (unsigned int)root_id);
120+
hsm_ret = wh_Server_CertVerify(
121+
&hsmServerCtx, cert_chain, cert_chain_size, root_id,
122+
WH_CERT_FLAGS_CACHE_LEAF_PUBKEY, WH_NVM_FLAGS_USAGE_VERIFY,
123+
&g_certLeafKeyId);
124+
if (hsm_ret == WH_ERROR_OK) {
125+
*verify_result = 0;
126+
}
127+
wolfBoot_printf("wh_Server_CertVerify returned %d\n", hsm_ret);
128+
#endif
129+
130+
return hsm_ret;
131+
}
79132
#endif
80133

81134
/* TPM based verify */
@@ -328,12 +381,7 @@ static void wolfBoot_verify_signature_ecc(uint8_t key_slot,
328381
}
329382
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY)
330383
if (g_leafKeyIdValid) {
331-
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
332-
(void)wh_Client_KeyEvict(&hsmClientCtx, g_certLeafKeyId);
333-
#elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
334-
(void)wh_Server_KeystoreEvictKey(&hsmServerCtx, g_certLeafKeyId);
335-
#endif
336-
g_leafKeyIdValid = 0;
384+
wolfBoot_cert_chain_evict_leaf_key();
337385
}
338386
#endif
339387
#else
@@ -546,12 +594,7 @@ static void wolfBoot_verify_signature_rsa_common(uint8_t key_slot,
546594
}
547595
#elif defined(WOLFBOOT_CERT_CHAIN_VERIFY)
548596
if (g_leafKeyIdValid) {
549-
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
550-
(void)wh_Client_KeyEvict(&hsmClientCtx, g_certLeafKeyId);
551-
#elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
552-
(void)wh_Server_KeystoreEvictKey(&hsmServerCtx, g_certLeafKeyId);
553-
#endif
554-
g_leafKeyIdValid = 0;
597+
wolfBoot_cert_chain_evict_leaf_key();
555598
}
556599
#endif /* !WOLFBOOT_USE_WOLFHSM_PUBKEY_ID */
557600
#else
@@ -2260,11 +2303,17 @@ int wolfBoot_verify_authenticity(struct wolfBoot_image *img)
22602303
defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER))
22612304
uint8_t* cert_chain;
22622305
uint16_t cert_chain_size;
2263-
int32_t cert_verify_result;
2306+
int32_t cert_verify_result;
2307+
int32_t primary_verify_result;
22642308
int hsm_ret;
2309+
int primary_hsm_ret;
2310+
#ifdef WOLFBOOT_WOLFHSM_SECONDARY_ROOT_CA_NVM_ID
2311+
int32_t secondary_verify_result;
2312+
int secondary_hsm_ret;
2313+
#endif
22652314

22662315
/* Reset certificate chain usage for this verification */
2267-
g_leafKeyIdValid = 0;
2316+
wolfBoot_cert_chain_evict_leaf_key();
22682317
#endif
22692318

22702319
stored_signature_size = get_header(img, HDR_SIGNATURE, &stored_signature);
@@ -2336,41 +2385,49 @@ int wolfBoot_verify_authenticity(struct wolfBoot_image *img)
23362385
wolfBoot_printf("Found certificate chain (%d bytes)\n",
23372386
cert_chain_size);
23382387

2339-
/* Verify certificate chain using wolfHSM's verification API. Use DMA if
2340-
* available in the wolfHSM configuration */
2341-
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT)
2342-
#if defined(WOLFHSM_CFG_DMA)
2343-
wolfBoot_printf(
2344-
"verifying cert chain and caching leaf pubkey (using DMA)\n");
2345-
hsm_ret = wh_Client_CertVerifyDmaAndCacheLeafPubKey(
2346-
&hsmClientCtx, cert_chain, cert_chain_size, hsmNvmIdCertRootCA,
2347-
WH_NVM_FLAGS_USAGE_VERIFY, &g_certLeafKeyId, &cert_verify_result);
2348-
#else
2349-
wolfBoot_printf("verifying cert chain and caching leaf pubkey\n");
2350-
hsm_ret = wh_Client_CertVerifyAndCacheLeafPubKey(
2351-
&hsmClientCtx, cert_chain, cert_chain_size, hsmNvmIdCertRootCA,
2352-
WH_NVM_FLAGS_USAGE_VERIFY, &g_certLeafKeyId, &cert_verify_result);
2353-
#endif
2354-
#elif defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
2355-
wolfBoot_printf("verifying cert chain and caching leaf pubkey\n");
2356-
hsm_ret = wh_Server_CertVerify(
2357-
&hsmServerCtx, cert_chain, cert_chain_size, hsmNvmIdCertRootCA,
2358-
WH_CERT_FLAGS_CACHE_LEAF_PUBKEY, WH_NVM_FLAGS_USAGE_VERIFY,
2359-
&g_certLeafKeyId);
2360-
if (hsm_ret == WH_ERROR_OK) {
2361-
cert_verify_result = 0;
2388+
primary_hsm_ret = wolfBoot_cert_chain_verify_with_root(cert_chain,
2389+
cert_chain_size, hsmNvmIdCertRootCA, &primary_verify_result);
2390+
hsm_ret = primary_hsm_ret;
2391+
cert_verify_result = primary_verify_result;
2392+
2393+
#ifdef WOLFBOOT_WOLFHSM_SECONDARY_ROOT_CA_NVM_ID
2394+
if (hsm_ret != WH_ERROR_OK || cert_verify_result != 0) {
2395+
wolfBoot_printf("Primary certificate chain verification failed: "
2396+
"hsm_ret=%d, verify_result=%d; retrying with "
2397+
"secondary root %08x\n",
2398+
hsm_ret, cert_verify_result,
2399+
(unsigned int)
2400+
WOLFBOOT_WOLFHSM_SECONDARY_ROOT_CA_NVM_ID);
2401+
wolfBoot_cert_chain_evict_leaf_key();
2402+
2403+
secondary_hsm_ret = wolfBoot_cert_chain_verify_with_root(cert_chain,
2404+
cert_chain_size,
2405+
(whNvmId)WOLFBOOT_WOLFHSM_SECONDARY_ROOT_CA_NVM_ID,
2406+
&secondary_verify_result);
2407+
hsm_ret = secondary_hsm_ret;
2408+
cert_verify_result = secondary_verify_result;
2409+
2410+
if (hsm_ret != WH_ERROR_OK || cert_verify_result != 0) {
2411+
wolfBoot_printf("Certificate chain verification failed: "
2412+
"primary_hsm_ret=%d, "
2413+
"primary_verify_result=%d, "
2414+
"secondary_hsm_ret=%d, "
2415+
"secondary_verify_result=%d\n",
2416+
primary_hsm_ret, primary_verify_result,
2417+
secondary_hsm_ret, secondary_verify_result);
2418+
wolfBoot_cert_chain_evict_leaf_key();
2419+
return -1;
2420+
}
23622421
}
2363-
wolfBoot_printf("wh_Server_CertVerify returned %d\n", hsm_ret);
2364-
#endif
2365-
2366-
/* Error or verification failure results in standard auth check failure
2367-
* path */
2368-
if (hsm_ret != 0 || cert_verify_result != 0) {
2422+
#else
2423+
if (hsm_ret != WH_ERROR_OK || cert_verify_result != 0) {
23692424
wolfBoot_printf("Certificate chain verification failed: "
23702425
"hsm_ret=%d, verify_result=%d\n",
23712426
hsm_ret, cert_verify_result);
2427+
wolfBoot_cert_chain_evict_leaf_key();
23722428
return -1;
23732429
}
2430+
#endif
23742431

23752432
wolfBoot_printf("Certificate chain verified, using leaf key ID: %08x\n",
23762433
(unsigned int)g_certLeafKeyId);

tools/config.mk

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ ifeq ($(ARCH),)
9191
FLASH_MULTI_SECTOR_ERASE=0
9292
WOLFHSM_CLIENT=0
9393
WOLFHSM_CLIENT_LOCAL_KEYS=0
94+
WOLFHSM_SECONDARY_ROOT_CA_NVM_ID?=
9495
endif
9596

9697
CONFIG_VARS:= ARCH TARGET SIGN HASH MCUXSDK MCUXPRESSO MCUXPRESSO_CPU MCUXPRESSO_DRIVERS \
@@ -125,4 +126,5 @@ CONFIG_VARS:= ARCH TARGET SIGN HASH MCUXSDK MCUXPRESSO MCUXPRESSO_CPU MCUXPRESSO
125126
SIGN_SECONDARY \
126127
WOLFHSM_CLIENT \
127128
WOLFHSM_CLIENT_LOCAL_KEYS \
129+
WOLFHSM_SECONDARY_ROOT_CA_NVM_ID \
128130
ENCRYPT_CACHE

0 commit comments

Comments
 (0)