Skip to content

Commit 880c0a7

Browse files
authored
Merge pull request #769 from aidangarske/h5-wolfhsm-port
Add WOLFCRYPT_TZ_WOLFHSM TrustZone engine for STM32H5
2 parents b044c89 + b9bd6da commit 880c0a7

28 files changed

Lines changed: 2420 additions & 41 deletions

.github/workflows/test-external-library-paths.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,4 +88,5 @@ jobs:
8888
WOLFBOOT_LIB_WOLFSSL="$(realpath ../external-libs/wolfssl)" \
8989
WOLFBOOT_LIB_WOLFPKCS11="$(realpath ../external-libs/wolfPKCS11)" \
9090
WOLFBOOT_LIB_WOLFPSA="$(realpath ../external-libs/wolfPSA)" \
91-
WOLFBOOT_LIB_WOLFTPM="$(realpath ../external-libs/wolfTPM)"
91+
WOLFBOOT_LIB_WOLFTPM="$(realpath ../external-libs/wolfTPM)" \
92+
WOLFBOOT_LIB_WOLFHSM="$(realpath ../external-libs/wolfHSM)"

.github/workflows/trustzone-emulator-tests.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ jobs:
119119
grep -q "\\[BKPT\\] imm=0x7f" /tmp/m33mu-fwtpm.log
120120
grep -q "\\[EXPECT BKPT\\] Success" /tmp/m33mu-fwtpm.log
121121
122+
# The wolfHSM STM32H5 TrustZone integration is exercised by the
123+
# nightly cross-repo job in wolfHSM
124+
# (.github/workflows/wolfboot-tz-integration.yml), which builds
125+
# this demo against the latest wolfHSM main and runs it under
126+
# m33mu. It is intentionally not gated on every wolfBoot PR.
127+
122128
- name: Clean and build test with DICE attestation + OTP (stm32h5)
123129
run: |
124130
make clean distclean

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -403,3 +403,6 @@ image.ub
403403
system-default.dtb
404404
test_output/
405405
sdcard.img
406+
407+
# wolfHSM STM32H5 TZ demo build output
408+
port/stmicro/stm32h5-tz-wolfhsm/out/

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,9 @@ OBJS+=$(WOLFCRYPT_OBJS)
218218
OBJS+=$(PUBLIC_KEY_OBJS)
219219
OBJS+=$(WOLFHSM_OBJS)
220220

221+
# Vendored wolfHSM sources: keep cosmetic unused-parameter warnings non-fatal
222+
$(WOLFHSM_OBJS): CFLAGS += -Wno-error=unused-parameter
223+
221224
CFLAGS+= \
222225
-I"." -I"include/" -I"$(WOLFBOOT_LIB_WOLFSSL)" \
223226
-Wno-array-bounds \
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
ARCH?=ARM
2+
TZEN?=1
3+
TARGET?=stm32h5
4+
SIGN?=ECC256
5+
HASH?=SHA256
6+
DEBUG?=0
7+
VTOR?=1
8+
CORTEX_M0?=0
9+
CORTEX_M33?=1
10+
NO_ASM?=0
11+
# Required: wolfBoot's mpu_init() (src/boot_arm.c) programs an MPU region for
12+
# the wolfBoot flash whose layout does not cover this port's secure SRAM, which
13+
# HardFaults secure boot. Keep the MPU off here until that is handled upstream.
14+
NO_MPU=1
15+
EXT_FLASH?=0
16+
SPI_FLASH?=0
17+
ALLOW_DOWNGRADE?=0
18+
NVM_FLASH_WRITEONCE?=1
19+
WOLFBOOT_VERSION?=1
20+
V?=0
21+
SPMATH?=1
22+
RAM_CODE?=1
23+
DUALBANK_SWAP?=0
24+
WOLFBOOT_PARTITION_SIZE?=0xA0000
25+
WOLFBOOT_SECTOR_SIZE?=0x2000
26+
WOLFBOOT_KEYVAULT_ADDRESS?=0x0C040000
27+
WOLFBOOT_KEYVAULT_SIZE?=0x1C000
28+
WOLFBOOT_NSC_ADDRESS?=0x0C05C000
29+
WOLFBOOT_NSC_SIZE?=0x4000
30+
WOLFBOOT_PARTITION_BOOT_ADDRESS?=0x08060000
31+
WOLFBOOT_PARTITION_UPDATE_ADDRESS?=0x0C100000
32+
WOLFBOOT_PARTITION_SWAP_ADDRESS?=0x0C1A0000
33+
FLAGS_HOME=0
34+
DISABLE_BACKUP=0
35+
WOLFCRYPT_TZ=1
36+
WOLFCRYPT_TZ_WOLFHSM=1
37+
IMAGE_HEADER_SIZE?=1024
38+
ARMORED=1

docs/STM32-TZ.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,19 @@ The `WOLFCRYPT_TZ_PSA` option provides a standard PSA Crypto interface using
3333
wolfPSA in the secure domain. The key storage uses the same secure flash
3434
keystore backend as PKCS11, exposed through the wolfPSA store API.
3535

36+
### wolfHSM API in non-secure world
37+
38+
The `WOLFCRYPT_TZ_WOLFHSM` option hosts a wolfHSM server inside the secure
39+
domain and exposes it to non-secure applications through a single non-secure
40+
callable veneer. Non-secure code uses the standard wolfCrypt API with the
41+
wolfHSM client cryptocb registered under `WH_DEV_ID`; key material, the
42+
keystore, and crypto operations stay in the secure domain. Persistent keys
43+
live in the same secure flash keystore region used by PKCS11 and PSA, with
44+
two-partition journaling for power-fail safety.
45+
46+
See [wolfHSM](wolfHSM.md) for the full configuration, build, flash, and
47+
test recipe on STM32H5.
48+
3649
### PSA Initial Attestation (DICE)
3750

3851
When `WOLFCRYPT_TZ_PSA=1` is enabled, wolfBoot exposes the PSA Initial

docs/wolfHSM.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ wolfBoot supports using wolfHSM on the following platforms:
2121

2222
- wolfBoot simulator (using wolfHSM POSIX TCP transport)
2323
- AURIX TC3xx (shared memory transport)
24+
- STM32H5 TrustZone (the secure-side wolfBoot hosts a wolfHSM server and exposes it to the non-secure application through a single NSC veneer; see [STM32H5 TrustZone Engine](#stm32h5-trustzone-engine) below)
2425

2526
Details on configuring wolfBoot to use wolfHSM on each of these platforms can be found in the wolfBoot (and wolfHSM) documentation specific to that target, with the exception of the simulator, which is documented here. The remainder of this document focuses on the generic wolfHSM-related configuration options.
2627

@@ -237,3 +238,51 @@ When using wolfHSM server mode, no external server is required. wolfBoot include
237238
```
238239

239240
The embedded wolfHSM server will automatically handle all cryptographic operations and key management using the file-based NVM storage(`wolfBoot_wolfHSM_NVM.bin`) that was generated above.
241+
242+
## STM32H5 TrustZone Engine
243+
244+
On STM32H5, wolfBoot can host a wolfHSM server in the secure TrustZone image and expose it to the non-secure application through a single non-secure-callable veneer (`wcs_wolfhsm_transmit`). The non-secure side runs the standard wolfHSM client API, which auto-registers a wolfCrypt cryptocb under `WH_DEV_ID`, so application-level wolfCrypt calls that pass that device ID transparently round-trip to the secure server.
245+
246+
This is a separate deployment shape from the wolfHSM client/server modes documented above; it does not use `WOLFBOOT_ENABLE_WOLFHSM_CLIENT/SERVER` or the `hsmClientCtx`/`hsmServerCtx` HAL hooks, and is mutually exclusive with the other STM32H5 TrustZone engines (`WOLFCRYPT_TZ_PKCS11`, `WOLFCRYPT_TZ_PSA`, `WOLFCRYPT_TZ_FWTPM`).
247+
248+
### Build
249+
250+
```sh
251+
cp config/examples/stm32h5-tz-wolfhsm.config .config
252+
make
253+
```
254+
255+
By default the auto-test prints a UART pass/fail line and idles in `while (1)`, which is safe on real silicon. For emulator or under-debugger runs that signal pass/fail via a breakpoint, add `WOLFBOOT_TZ_TEST_BKPT=1` so the auto-test issues `bkpt` instead (this HardFaults on real silicon without a debugger attached):
256+
257+
```sh
258+
make WOLFBOOT_TZ_TEST_BKPT=1
259+
```
260+
261+
### Flash
262+
263+
The wolfBoot helper programs the option bytes the secure boot path requires (`TZEN`, `SECBOOTADD`, `SECWM1`/`SECWM2`); see [STM32-TZ.md](STM32-TZ.md) for the option-byte details:
264+
265+
```sh
266+
./tools/scripts/set-stm32-tz-option-bytes.sh
267+
STM32_Programmer_CLI -c port=swd -d wolfboot.bin 0x0C000000
268+
STM32_Programmer_CLI -c port=swd -d test-app/image_v1_signed.bin 0x08060000
269+
```
270+
271+
### Test
272+
273+
The non-secure test application runs the wolfHSM auto-test at startup. A successful first boot ends with:
274+
275+
```text
276+
wolfHSM CommInit ok (client=1 server=...)
277+
wolfHSM RNG ok: <16 random bytes>
278+
wolfHSM SHA256 ok
279+
wolfHSM AES ok
280+
wolfHSM first boot path, committing key to NVM
281+
wolfHSM NSC tests passed
282+
```
283+
284+
The default build prints a final `WOLFHSM_TZ_TEST_PASS` UART line on success. The `WOLFBOOT_TZ_TEST_BKPT=1` build raises `bkpt #0x7d` on first-boot success and `bkpt #0x7f` on second-boot success (after the persisted key is reloaded from flash on reset) instead, for emulator and debugger runs. Reset the board (no re-flash) to verify persistence; the second boot prints `wolfHSM second boot path, restored persisted key`.
285+
286+
### Notes
287+
288+
The wolfHSM NVM lives in the existing `FLASH_KEYVAULT` region (112 KiB at `0x0C040000`) shared with the other STM32H5 TrustZone engines. The flash adapter (`src/wolfhsm_flash_hal.c`) caches the affected sector, modifies it, and rewrites the whole 8 KiB sector in one erase + program cycle, mirroring `psa_store.c` / `pkcs11_store.c`. This satisfies the H5 quad-word ECC rule that each 16-byte unit may be programmed exactly once between erases, which wolfHSM's 8-byte-unit writes would otherwise violate.

include/user_settings.h

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,10 @@ extern int tolower(int c);
518518
defined(WOLFBOOT_SIGN_ML_DSA)) && \
519519
!defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
520520
#define WC_NO_RNG
521+
/* wolfssl rejects WC_NO_RNG with the default blinding macros.
522+
* wolfBoot is verify-only, no blinding is meaningful with a
523+
* disabled RNG. */
524+
#define WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS
521525
#endif
522526
#define WC_NO_HASHDRBG
523527
#define NO_AES_CBC
@@ -571,6 +575,9 @@ extern int tolower(int c);
571575
defined(WOLFBOOT_SIGN_ML_DSA)) && \
572576
!defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
573577
# define WC_NO_RNG
578+
/* wolfssl rejects WC_NO_RNG with the default blinding macros. wolfBoot
579+
* is verify-only, no blinding is meaningful with a disabled RNG. */
580+
# define WC_BLINDING_NO_RNG_ACKNOWLEDGE_WEAKNESS
574581
# endif
575582
# define WC_NO_HASHDRBG
576583
# define NO_DEV_RANDOM
@@ -770,12 +777,71 @@ extern int tolower(int c);
770777
#endif
771778

772779
#if defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) || \
773-
defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER)
780+
defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) || \
781+
defined(WOLFCRYPT_TZ_WOLFHSM)
774782
# define WOLF_CRYPTO_CB
775783
# undef HAVE_ANONYMOUS_INLINE_AGGREGATES
776784
# define HAVE_ANONYMOUS_INLINE_AGGREGATES 1
777785
# define WOLFSSL_KEY_GEN
778-
#endif /* WOLFBOOT_ENABLE_WOLFHSM_CLIENT || WOLFBOOT_ENABLE_WOLFHSM_SERVER */
786+
#endif
787+
788+
/* Secure-side sizing common to any wolfHSM server build (TZ engine and
789+
* the verify-only cert-chain server). Gated out of host unit tests. */
790+
#if (defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) || \
791+
defined(WOLFCRYPT_TZ_WOLFHSM)) && !defined(UNIT_TEST)
792+
/* Match NS-side WC_MAX_DIGEST_SIZE. NS test-app/wcs/user_settings.h
793+
* enables WOLFSSL_SHA3 which sets WC_MAX_DIGEST_SIZE = 64. Without
794+
* SHA384/SHA512 on the secure side, WC_MAX_DIGEST_SIZE caps at
795+
* SHA256's 32 and wc_ecc_sign_hash (ecc.c:7281) rejects legitimately
796+
* oversized hashes (e.g. ECDSA truncation tests) with BAD_LENGTH_E. */
797+
# ifndef WOLFSSL_SHA384
798+
# define WOLFSSL_SHA384
799+
# endif
800+
# ifndef WOLFSSL_SHA512
801+
# define WOLFSSL_SHA512
802+
# endif
803+
/* Match the keycache sizing the wolfHSM test suite is validated
804+
* against (test/config/wolfhsm_cfg.h: 9 regular + 3 big). The
805+
* library defaults (8 + 1) are one regular slot short of the
806+
* suite's peak working set and leave too few big slots for the
807+
* RSA/ML-DSA cases. */
808+
# ifndef WOLFHSM_CFG_SERVER_KEYCACHE_COUNT
809+
# define WOLFHSM_CFG_SERVER_KEYCACHE_COUNT 9
810+
# endif
811+
# ifndef WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT
812+
# define WOLFHSM_CFG_SERVER_KEYCACHE_BIG_COUNT 3
813+
# endif
814+
#endif /* WOLFBOOT_ENABLE_WOLFHSM_SERVER || WOLFCRYPT_TZ_WOLFHSM, !UNIT_TEST */
815+
816+
/* AES/HMAC/HKDF are only needed by the STM32H5 TrustZone engine, whose
817+
* non-secure client routes general crypto (AES/HMAC/HKDF) through the
818+
* secure server. The verify-only cert-chain server does not service these
819+
* requests, so it keeps the NO_AES/NO_HMAC set earlier in this file. */
820+
#if defined(WOLFCRYPT_TZ_WOLFHSM) && !defined(UNIT_TEST)
821+
# undef NO_AES
822+
# undef NO_HMAC
823+
# ifndef WOLFSSL_AES_DIRECT
824+
# define WOLFSSL_AES_DIRECT
825+
# endif
826+
# ifndef HAVE_HKDF
827+
# define HAVE_HKDF
828+
# endif
829+
# ifndef WOLFSSL_AES_COUNTER
830+
# define WOLFSSL_AES_COUNTER
831+
# endif
832+
# ifndef HAVE_AESCTR
833+
# define HAVE_AESCTR
834+
# endif
835+
# ifndef WOLFSSL_AES_GCM
836+
# define WOLFSSL_AES_GCM
837+
# endif
838+
# ifndef HAVE_AESGCM
839+
# define HAVE_AESGCM
840+
# endif
841+
# ifndef GCM_TABLE_4BIT
842+
# define GCM_TABLE_4BIT
843+
# endif
844+
#endif /* WOLFCRYPT_TZ_WOLFHSM && !UNIT_TEST */
779845

780846
#if defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER) && \
781847
defined(WOLFBOOT_CERT_CHAIN_VERIFY)

include/wolfboot/wcs_wolfhsm.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/* wcs_wolfhsm.h
2+
*
3+
* Copyright (C) 2026 wolfSSL Inc.
4+
*
5+
* This file is part of wolfBoot.
6+
*/
7+
8+
#ifndef WOLFBOOT_WCS_WOLFHSM_H
9+
#define WOLFBOOT_WCS_WOLFHSM_H
10+
11+
#include <stdint.h>
12+
#include "wolfboot/wc_secure.h"
13+
14+
#ifdef WOLFCRYPT_TZ_WOLFHSM
15+
16+
int CSME_NSE_API wcs_wolfhsm_transmit(const uint8_t *cmd, uint32_t cmdSz,
17+
uint8_t *rsp, uint32_t *rspSz);
18+
19+
void wcs_wolfhsm_init(void);
20+
21+
#endif /* WOLFCRYPT_TZ_WOLFHSM */
22+
23+
#endif /* WOLFBOOT_WCS_WOLFHSM_H */
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/* wolfhsm_flash_hal.h
2+
*
3+
* Copyright (C) 2026 wolfSSL Inc.
4+
*
5+
* This file is part of wolfBoot.
6+
*/
7+
8+
#ifndef WOLFBOOT_WOLFHSM_FLASH_HAL_H
9+
#define WOLFBOOT_WOLFHSM_FLASH_HAL_H
10+
11+
#ifdef WOLFCRYPT_TZ_WOLFHSM
12+
13+
#include <stdint.h>
14+
15+
#include "wolfhsm/wh_flash.h"
16+
17+
/* Per-call config / context for the adapter. base/size/partition_size are
18+
* the only client-visible fields; the cache lives inside the static
19+
* implementation in wolfhsm_flash_hal.c (mirroring psa_store.c). */
20+
typedef struct {
21+
uint32_t base;
22+
uint32_t size;
23+
uint32_t partition_size;
24+
} whFlashH5Ctx;
25+
26+
extern const whFlashCb whFlashH5_Cb;
27+
28+
#endif /* WOLFCRYPT_TZ_WOLFHSM */
29+
30+
#endif /* WOLFBOOT_WOLFHSM_FLASH_HAL_H */

0 commit comments

Comments
 (0)