Add WOLFCRYPT_TZ_WOLFHSM TrustZone engine for STM32H5#769
Conversation
e312cbb to
0c7b556
Compare
c86b935 to
2c6827d
Compare
45181ab to
24de72f
Compare
- 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
- options.mk: drop duplicate WOLFHSM_CLIENT_OBJS / WOLFHSM_SERVER_OBJS
block; top-of-file definitions are reached before all consumers.
- wolfhsm_callable.c: idempotency guard in wcs_wolfhsm_init; defensive
memset of g_srv_tx_ctx; move g_flash_cfg to a stack local; clamp
rsp_size to rsp_capacity before publishing *rspSz; set *rspSz = 0
on every early-validation error path; use { 0 } initializer; switch
to #ifdef WOLF_CRYPTO_CB.
- wolfhsm_flash_hal.c: rename _Foo callbacks to whFlashH5_Foo to avoid
C-reserved leading-underscore-uppercase identifiers; constant-time
compare in Verify since data may be key material; defensive
wc_ForceZero(cached_sector) on entry to Program; Erase short-circuits
size == 0 before the alignment check for consistency.
- test-app/wcs/wolfhsm_test.c: split AesSetIV vs AesCbcEncrypt error
diagnostics; KeyEvict the cached key when KeyCommit fails; use { 0 }
initializer for nsc_cfg.
- tools/unit-tests/Makefile: add WOLFBOOT_LIB_WOLFHSM default and
external-libs fallback so unit-wolfhsm_flash_hal finds wolfhsm
headers in CI.
- .github/workflows/test-external-library-paths.yml: pass
WOLFBOOT_LIB_WOLFHSM to the unit-test matrix entry.
- unit-wolfhsm_flash_hal.c: cover Cleanup, erase-failure propagation,
Read happy path, multi-sector Program, NULL context for
Read/PartitionSize/Program/Erase/Verify/BlankCheck, NULL data, and
WriteLock/WriteUnlock; use MAP_FIXED_NOREPLACE when available.
24de72f to
1f1a3ac
Compare
a03e6e5 to
d735665
Compare
| flash_cfg.base = (uint32_t)&_flash_keyvault; | ||
| flash_cfg.size = (uint32_t)&_flash_keyvault_size; | ||
| flash_cfg.partition_size = WCS_WOLFHSM_PARTITION_SIZE; | ||
| g_nvm_flash_cfg.config = &flash_cfg; |
There was a problem hiding this comment.
This is storing a temporary stack value (flash_cfg, declared above) into the global structure.
When wcs_wolfhsm_init() returns, this value becomes a dangling pointer to future stack values.
| return WH_ERROR_NOTREADY; | ||
| } | ||
|
|
||
| g_srv_tx_ctx.req_buf = cmd; |
There was a problem hiding this comment.
cmd is provided by the caller. It should be validated using cmse_check_address_range. If not checked, the non-secure app is compromised, it could send forged to pass a secure-side pointer here.
| endif | ||
|
|
||
| ifeq ($(WOLFCRYPT_TZ_FWTPM),1) | ||
| ifeq ($(WOLFCRYPT_TZ_WOLFHSM),1) |
There was a problem hiding this comment.
Should also add guard against invalid combo like WOLFCRYPT_TZ_PKCS11 or WOLFCRYPT_TZ_PSA
| # (.github/workflows/wolfboot-tz-integration.yml), which builds | ||
| # this demo against the latest wolfHSM main and runs it under | ||
| # m33mu. It is intentionally not gated on every wolfBoot PR. | ||
|
|
There was a problem hiding this comment.
All other TZ engines have m33mu tests: I think we should add one for TZ_WOLFHSM here too.
| }; | ||
|
|
||
| static whServerContext g_server; | ||
| static int g_wolfhsm_ready; |
| @@ -0,0 +1,3 @@ | |||
| BOOT_ADDR=0x08060000 | |||
There was a problem hiding this comment.
this file has been probably added by accident. Add the 'out' directory to .gitignore
| CORTEX_M0?=0 | ||
| CORTEX_M33?=1 | ||
| NO_ASM?=0 | ||
| NO_MPU=1 |
There was a problem hiding this comment.
NO_MPU set with a TZ setup means that also apps in NS domain will not be able to use it. Please remove NO_MPU=1 if not strictly needed for some reason
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