Add WOLFCRYPT_TZ_WOLFHSM TrustZone engine for STM32H5#769
Conversation
- New WOLFCRYPT_TZ_WOLFHSM build flag, mutually exclusive with
WOLFCRYPT_TZ_PKCS11 / _PSA / _FWTPM
- Extract WOLFHSM_CLIENT_OBJS and WOLFHSM_SERVER_OBJS shared
variables; legacy WOLFHSM_CLIENT/SERVER blocks now reference them
to share file lists with the new TZ engine
- New config/examples/stm32h5-tz-wolfhsm.config (one-line delta
from stm32h5-tz-fwtpm.config)
- NS test-app RNG seed routes through wcs_get_random (same pattern
as PKCS11 / fwTPM)
No NSC entries or wolfHSM-specific code yet — Phase 0 only wires up
the build flag and validates existing-engine compatibility. Builds
cleanly with stm32h5-tz-wolfhsm.config; existing TZ configs and the
sim wolfHSM client/server configs continue to build.
The four files to stage:
- options.mk (mutex guards, shared-vars refactor, TZ_WOLFHSM block)
- test-app/Makefile (NS-side TZ_WOLFHSM block + alias)
- test-app/wcs/user_settings.h (RNG seed CFG)
- config/examples/stm32h5-tz-wolfhsm.config (new)
Phase 1 of the WOLFCRYPT_TZ_WOLFHSM=1 lane: hosts a wolfHSM server in the
secure world and exposes it across the NSC boundary via a single packet-
shaped veneer (wcs_wolfhsm_transmit), mirroring fwTPM lane 2's shape.
NS test-app runs a wolfHSM client; wc_RNG_GenerateBlock with WH_DEV_ID
round-trips through the bridge.
- include/wolfboot/wcs_wolfhsm.h: NSC entry declarations
- src/wolfhsm_callable.c: secure-side server init (RNG + ramsim NVM +
comm + transport) and the single-NSC veneer with TOCTOU single-fetch
defense on *rspSz
- src/wc_callable.c: hook wcs_wolfhsm_init() into wcs_Init()
- test-app/wcs/wolfhsm_stub.c: NS .bss buffers + transport context
- test-app/wcs/wolfhsm_test.c: client init + CommInit handshake +
RNG-via-WH_DEV_ID exerciser; auto-runs at boot
- options.mk + test-app/Makefile: wire the WOLFHSM_*_OBJS lists, the
ramsim NVM, the wolfssl crypto primitives, and a separate wolfhsm_obj/
build dir so NS-side compiles wolfHSM with WOLFHSM_CFG_ENABLE_CLIENT
while the secure side gets WOLFHSM_CFG_ENABLE_SERVER
- user_settings.h (both): set WOLF_CRYPTO_CB / HAVE_ANONYMOUS_INLINE_-
AGGREGATES=1 / WOLFSSL_KEY_GEN whenever WOLFCRYPT_TZ_WOLFHSM is set;
NS RNG seed routes through wcs_get_random
- D25 hardware test recipe wired in via WOLFBOOT_TZ_TEST_NO_BKPT
Builds clean against stm32h5-tz-wolfhsm.config; m33mu CI passes:
wolfHSM CommInit ok (client=1 server=56)
wolfHSM RNG ok: ...
wolfHSM NSC tests passed
[BKPT] imm=0x7f / [EXPECT BKPT] Success
The auto-test block in app_stm32h5.c gates its bkpt #0x7f / #0x7e on
WOLFBOOT_TZ_TEST_NO_BKPT, but the make-flag was never propagated to
CFLAGS. Building with make WOLFBOOT_TZ_TEST_NO_BKPT=1 now actually
swaps the BKPTs for printf(WOLFHSM_TZ_TEST_{PASS,FAIL}) + while(1)
loops, which is the canonical hardware-test path (per D25) since
real silicon HardFaults on bkpt without a debugger attached.
Verified on NUCLEO-H563ZI hardware:
wolfHSM CommInit ok (client=1 server=56)
wolfHSM RNG ok: <16 random bytes>
wolfHSM NSC tests passed
WOLFHSM_TZ_TEST_PASS
- include/wolfboot/wcs_wolfhsm.h: remove WCS_WOLFHSM_MAX_REQ_SIZE /
MAX_RSP_SIZE macros that hardcoded 1288U; the value is just WH_COMM_MTU
and silently desyncs if WOLFHSM_CFG_COMM_DATA_LEN changes
- src/wolfhsm_callable.c: use WH_COMM_MTU directly in size guards;
replace bare 56 server_id with WCS_WOLFHSM_SERVER_ID; drop Phase 1b /
Phase 3 comment, keep only the real-HW pageSize=8 invariant
- test-app/wcs/wolfhsm_test.c: drop unused wolfboot/wcs_wolfhsm.h
include; reword the Phase 1c exerciser header to a stable description
m33mu still green (CommInit + RNG round-trip + BKPT 0x7f).
cmd_wolfhsm_test now exercises three crypto round-trips:
- SHA256(abc) → digest matches FIPS 180-2 Appendix B.1 vector.
- AES-128-CBC encrypt with a cached HSM key:
wh_Client_KeyCache(WH_NVM_FLAGS_USAGE_ENCRYPT) imports the key
to the server's keystore; wc_AesInit + wh_Client_AesSetKeyId
links the wolfCrypt Aes struct to the cached keyId; the
cryptocb dispatches wc_AesCbcEncrypt to the server, which
runs the AES op against its in-cache key. Ciphertext compared
against the FIPS 197 Appendix B vector. Key evicted on exit.
- RNG via WH_DEV_ID (already in Phase 1).
Each step prints a labelled UART line (wolfHSM RNG/SHA256/AES ok).
Verified on m33mu; the AES path forces WH_NVM_FLAGS_USAGE_ENCRYPT
since cached keys without usage flags fail with WH_ERROR_USAGE.
Stack budget unchanged (STACK_USAGE=20000 sufficient). PKCS11 / PSA /
Replaces the Phase 1 ramsim NVM with a real flash-backed store living
in the existing wolfBoot keyvault region (FLASH_KEYVAULT, 112 KiB at
0x0C040000), so wolfHSM-cached keys persist across reset.
- include/wolfboot/wolfhsm_flash_hal.h: whFlashH5Ctx (base / size /
partition_size) and whFlashH5_Cb extern.
- src/wolfhsm_flash_hal.c: 10-callback whFlashCb adapter wrapping
hal_flash_unlock/lock/write/erase. PartitionSize is configurable via
the context (default 32 KiB per partition; two partitions = 64 KiB
used, 48 KiB headroom in the 112 KiB keyvault). Direct memory reads
for Read/Verify/BlankCheck. WriteLock/Unlock are no-ops on H5 (lock
is global).
- src/wolfhsm_callable.c: drop ramsim ctx/cfg + wh_flash_ramsim
include; wire wh_NvmFlashConfig to the new adapter; vault address /
size sourced from the linker symbols _flash_keyvault /
_flash_keyvault_size, matching the PSA / PKCS11 store pattern.
- options.mk: drop wh_flash_ramsim.o, add src/wolfhsm_flash_hal.o.
Verified on m33mu with --persist: CommInit handshake + RNG + SHA256 +
AES cached-key round-trips all pass through wolfHSM's two-partition
journaling layer talking to actual flash. Persistence-across-reset
test (P3.3) follows in a separate commit. PKCS11 / PSA / fwTPM
regression builds remain clean.
e2e real HW tests with first and second boot persistant trip
- docs/wolfHSM.md: append a STM32H5 TrustZone Engine section
alongside the simulator section. Covers build (incl.
WOLFBOOT_TZ_TEST_NO_BKPT for hardware), flashing via
set-stm32-tz-option-bytes.sh + STM32_Programmer_CLI, expected UART
output for both boots, and notes the H5 quad-word ECC handling
shared with psa_store / pkcs11_store. Existing client/server
content untouched.
- .github/workflows/trustzone-emulator-tests.yml: add a wolfHSM step
that mirrors the PKCS11 first/second-boot pattern -- one m33mu
--persist run with --expect-bkpt 0x7d after the first boot path,
committing key to NVM message, then a second --persist run with
--expect-bkpt 0x7f after the restored persisted key message.
Add a wolfHSM section to docs/STM32-TZ.md alongside the PKCS11 and PSA sections, with a cross-reference to the dedicated docs/wolfHSM.md for the full STM32H5 build/flash/test recipe.
- callable: runtime flash config init (drop non-portable static cast),
panic on init failures, volatile *rspSz read, clear borrowed NS
pointers post-dispatch.
- flash_hal: propagate hal_flash_* errors, hoist unlock/lock outside
loop, per-iteration cached_sector wipe, validate config before
copying into ctx, rename sector_base to sector_offset.
- test-app: aes_inited guard, KeyEvict after KeyCommit, ForceZero
consistency, drop redundant keyId reassignment.
- test-app/Makefile: WOLFBOOT_LIB_WOLFHSM default for standalone
test-app builds.
- tools/unit-tests/unit-wolfhsm_flash_hal.c: 10-test host unit test
for the flash adapter, modeled on unit-psa_store.
CMSE pointer-range checks intentionally not applied: m33mu lacks
TT/TTAT, and PKCS11/PSA/fwTPM siblings all skip CMSE
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds support for running wolfHSM as a fourth STM32H5 TrustZone crypto “engine”, hosting the wolfHSM server in the secure image and routing non-secure wolfCrypt operations through an NSC veneer, with flash-backed persistent key storage and accompanying tests/docs.
Changes:
- Add secure-side wolfHSM server + NSC veneer (
wcs_wolfhsm_transmit) and non-secure client-side test harness for STM32H5. - Implement STM32H5 flash adapter (
whFlashCb) using a sector-cached read/modify/erase/write strategy, plus host unit tests. - Update build system and CI to include the new
WOLFCRYPT_TZ_WOLFHSMlane/configuration and document usage.
Reviewed changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/unit-tests/unit-wolfhsm_flash_hal.c | New host unit tests for the STM32H5 wolfHSM flash adapter using an mmap-backed mock flash. |
| tools/unit-tests/Makefile | Builds and runs the new unit test target (unit-wolfhsm_flash_hal). |
| test-app/wcs/wolfhsm_test.h | Declares the non-secure wolfHSM TZ auto-test entry point and result codes. |
| test-app/wcs/wolfhsm_test.c | Implements non-secure wolfHSM client tests (CommInit/RNG/SHA256/AES + persistence). |
| test-app/wcs/wolfhsm_stub.c | Provides a non-secure singleton NSC transport context for the wolfHSM client. |
| test-app/wcs/user_settings.h | Enables wolfCrypt callback support and related settings when WOLFCRYPT_TZ_WOLFHSM is enabled. |
| test-app/app_stm32h5.c | Hooks the wolfHSM non-secure auto-test into the STM32H5 test app startup path. |
| test-app/Makefile | Adds wolfHSM library paths/objects and build rules for the non-secure client-side pieces. |
| src/wolfhsm_flash_hal.c | Implements the STM32H5 wolfHSM flash adapter (whFlashCb) with sector caching. |
| src/wolfhsm_callable.c | Adds secure-side initialization and the NSC veneer for wolfHSM request/response handling. |
| src/wc_callable.c | Initializes the secure-side wolfHSM server during wcs_Init() when enabled. |
| options.mk | Adds WOLFCRYPT_TZ_WOLFHSM build integration, mutual exclusion checks, and shared wolfHSM object lists. |
| include/wolfboot/wolfhsm_flash_hal.h | Declares the flash adapter context and callback table for STM32H5. |
| include/wolfboot/wcs_wolfhsm.h | Declares the NSC veneer and secure init function for wolfHSM TZ mode. |
| include/user_settings.h | Enables WOLF_CRYPTO_CB when WOLFCRYPT_TZ_WOLFHSM is enabled globally. |
| docs/wolfHSM.md | Documents STM32H5 TrustZone wolfHSM engine build/flash/test details. |
| docs/STM32-TZ.md | Adds STM32H5 TrustZone documentation for the wolfHSM engine option. |
| config/examples/stm32h5-tz-wolfhsm.config | New example configuration enabling WOLFCRYPT_TZ_WOLFHSM on STM32H5. |
| .github/workflows/trustzone-emulator-tests.yml | Adds an m33mu first/second boot persistence CI lane for wolfHSM TZ mode. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| int CSME_NSE_API wcs_wolfhsm_transmit(const uint8_t *cmd, uint32_t cmdSz, | ||
| uint8_t *rsp, uint32_t *rspSz) | ||
| { | ||
| uint32_t rsp_capacity; | ||
| int rc; | ||
|
|
||
| if (cmd == NULL || rsp == NULL || rspSz == NULL) { | ||
| return WH_ERROR_BADARGS; | ||
| } | ||
| /* volatile read forbids the compiler from re-fetching *rspSz later. */ | ||
| rsp_capacity = *(volatile const uint32_t *)rspSz; |
| rc = wc_InitRng(g_crypto_ctx.rng); | ||
| if (rc != 0) { | ||
| wolfBoot_panic(); | ||
| } |
| #define WHFH5_SECTOR_SIZE (8U * 1024U) | ||
|
|
||
| /* Sector-cached read-modify-erase-write, mirroring psa_store.c. STM32H5 | ||
| * flash programs in 16-byte quad-words with ECC; each quad-word can be | ||
| * programmed exactly once between erases. wolfHSM issues 8-byte unit | ||
| * writes which would otherwise re-program neighbouring qwords, so every | ||
| * Program call here loads the affected sector into RAM, modifies it, and | ||
| * rewrites the whole 8 KiB sector after an erase. */ | ||
| static uint8_t cached_sector[WHFH5_SECTOR_SIZE]; |
| hal_flash_unlock(); | ||
| rc = hal_flash_erase(ctx->base + offset, (int)size); | ||
| hal_flash_lock(); | ||
| return (rc == 0) ? WH_ERROR_OK : WH_ERROR_ABORTED; |
| void *p = mmap((void *)(uintptr_t)MOCK_FLASH_BASE, MOCK_FLASH_SIZE, | ||
| PROT_READ | PROT_WRITE, | ||
| MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | ||
| ck_assert_ptr_eq(p, (void *)(uintptr_t)MOCK_FLASH_BASE); | ||
| memset((void *)(uintptr_t)MOCK_FLASH_BASE, 0xFF, MOCK_FLASH_SIZE); |
| #include "../../src/wolfhsm_flash_hal.c" | ||
|
|
There was a problem hiding this comment.
Please ignore this. Including .c in unit tests is OK
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #769
Scan targets checked: wolfboot-bugs, wolfboot-src
No new issues found in the changed files. ✅
Description
WOLFCRYPT_TZ_WOLFHSM=1a fourth TrustZone engine for STM32H5alongside PKCS11, PSA, and fwTPM. New config:
config/examples/stm32h5-tz-wolfhsm.config.the non-secure application through a single NSC veneer
(
wcs_wolfhsm_transmit). The non-secure app uses the standardwolfCrypt API; operations through
WH_DEV_IDroute to the secureside. Keys never leave the secure world.
whFlashCbadapter(
src/wolfhsm_flash_hal.c) overhal_flash_*, mirroringpsa_store.c's sector-cached read-modify-erase-write pattern.Two-partition journaling in the existing keyvault region.
WOLFHSM_SERVER_OBJS/WOLFHSM_CLIENT_OBJSvariables avoidobject-list duplication with the existing AURIX/sim wolfHSM lanes.
.github/workflows/trustzone-emulator-tests.yml:m33mu first-boot/second-boot persistence test asserting CommInit,
RNG, SHA256, AES, and keystore restore across reset.
tools/unit-tests/unit-wolfhsm_flash_hal.ccovers bounds, alignment, multi-sector, and write-failure paths.
docs/wolfHSM.md+ wolfHSM section indocs/STM32-TZ.md.Notes
lib/wolfHSMsubmodule pin is not bumped in this PR. The wolfHSMPR (
port/stmicro/stm32-tzNSC bridge transport) lands first; thepin bump will follow in a small standalone PR.
Test plan
WOLFHSM_TZ_TEST_PASSover UART