From 845a793066ce40d82c55d445263339dfd63236c3 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Thu, 30 Apr 2026 16:33:29 -0700 Subject: [PATCH 1/6] Add stm32-tz NSC bridge transport New port/stmicro/stm32-tz/wh_transport_nsc.{c,h} implementing both client and server callbacks for a synchronous TrustZone NSC bridge. The non-secure (client) side calls a single cmse_nonsecure_entry veneer (wcs_wolfhsm_transmit, provided by the host) which hands the request to the secure-side server context, runs wh_Server_HandleRequestMessage once inline, and returns the response in the same call -- no polling, notify counter, or async producer/consumer. Send delivers; Recv returns the cached response. Server-side Recv hands the request the host's NSC veneer parked in the static context; Send writes the response back into the NS buffer and stores its size for the veneer to read. Used by wolfBoot's WOLFCRYPT_TZ_WOLFHSM=1 lane on STM32H5 (separate PR against wolfBoot). Gated by WOLFHSM_CFG_PORT_STM32_TZ_NSC so the file is safe to ship in the wolfHSM tree without forcing every consumer to link the unresolved wcs_wolfhsm_transmit extern. --- port/stmicro/stm32-tz/wh_transport_nsc.c | 216 +++++++++++++++++++++++ port/stmicro/stm32-tz/wh_transport_nsc.h | 92 ++++++++++ 2 files changed, 308 insertions(+) create mode 100644 port/stmicro/stm32-tz/wh_transport_nsc.c create mode 100644 port/stmicro/stm32-tz/wh_transport_nsc.h diff --git a/port/stmicro/stm32-tz/wh_transport_nsc.c b/port/stmicro/stm32-tz/wh_transport_nsc.c new file mode 100644 index 000000000..6578e517c --- /dev/null +++ b/port/stmicro/stm32-tz/wh_transport_nsc.c @@ -0,0 +1,216 @@ +/* + * port/stmicro/stm32-tz/wh_transport_nsc.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHSM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +#include "wolfhsm/wh_settings.h" + +#ifdef WOLFHSM_CFG_PORT_STM32_TZ_NSC + +#include +#include + +#include "wolfhsm/wh_comm.h" +#include "wolfhsm/wh_error.h" +#include "wh_transport_nsc.h" + +/* + * Resolved on the non-secure side via the wolfBoot --cmse-implib import + * library; on the secure side the same symbol is provided by the host's + * NSC veneer (wolfBoot's src/wolfhsm_callable.c). The server callbacks + * below never call this; --gc-sections strips client-side code from the + * secure image. + */ +extern int wcs_wolfhsm_transmit(const uint8_t* cmd, uint32_t cmdSz, + uint8_t* rsp, uint32_t* rspSz); + + +/* ============================================================ + * Non-secure (client) callbacks + * ============================================================ */ + +static int _NscClientInit(void* context, const void* config, + whCommSetConnectedCb connectcb, void* connectcb_arg) +{ + whTransportNscClientContext* ctx = (whTransportNscClientContext*)context; + + (void)config; + + if (ctx == NULL) { + return WH_ERROR_BADARGS; + } + + memset(ctx, 0, sizeof(*ctx)); + ctx->initialized = 1; + + /* Synchronous bridge: the secure side is always reachable once linked. */ + if (connectcb != NULL) { + connectcb(connectcb_arg, WH_COMM_CONNECTED); + } + return WH_ERROR_OK; +} + +static int _NscClientSend(void* context, uint16_t size, const void* data) +{ + whTransportNscClientContext* ctx = (whTransportNscClientContext*)context; + uint32_t rspSz; + int rc; + + if (ctx == NULL || data == NULL) { + return WH_ERROR_BADARGS; + } + if (size == 0U || size > WH_TRANSPORT_NSC_BUFFER_SIZE) { + return WH_ERROR_BADARGS; + } + + memcpy(ctx->cmd_buf, data, size); + rspSz = (uint32_t)WH_TRANSPORT_NSC_BUFFER_SIZE; + + rc = wcs_wolfhsm_transmit(ctx->cmd_buf, (uint32_t)size, + ctx->rsp_buf, &rspSz); + if (rc != 0) { + ctx->last_rsp_size = 0; + return WH_ERROR_ABORTED; + } + if (rspSz == 0U || rspSz > (uint32_t)WH_TRANSPORT_NSC_BUFFER_SIZE) { + ctx->last_rsp_size = 0; + return WH_ERROR_ABORTED; + } + + ctx->last_rsp_size = (uint16_t)rspSz; + return WH_ERROR_OK; +} + +static int _NscClientRecv(void* context, uint16_t* out_size, void* data) +{ + whTransportNscClientContext* ctx = (whTransportNscClientContext*)context; + + if (ctx == NULL || out_size == NULL || data == NULL) { + return WH_ERROR_BADARGS; + } + if (ctx->last_rsp_size == 0U) { + return WH_ERROR_NOTREADY; + } + + memcpy(data, ctx->rsp_buf, ctx->last_rsp_size); + *out_size = ctx->last_rsp_size; + ctx->last_rsp_size = 0; + return WH_ERROR_OK; +} + +static int _NscClientCleanup(void* context) +{ + whTransportNscClientContext* ctx = (whTransportNscClientContext*)context; + if (ctx == NULL) { + return WH_ERROR_BADARGS; + } + ctx->initialized = 0; + return WH_ERROR_OK; +} + +const whTransportClientCb whTransportNscClient_Cb = { + .Init = _NscClientInit, + .Send = _NscClientSend, + .Recv = _NscClientRecv, + .Cleanup = _NscClientCleanup, +}; + + +/* ============================================================ + * Secure-side (server) callbacks + * + * The host's NSC veneer populates req_buf/req_size/rsp_buf/rsp_capacity + * and sets request_pending = 1 before calling wh_Server_HandleRequestMessage. + * Recv hands the request to the dispatcher; Send writes the response back + * into rsp_buf and stores its size for the veneer to read. + * ============================================================ */ + +static int _NscServerInit(void* context, const void* config, + whCommSetConnectedCb connectcb, void* connectcb_arg) +{ + whTransportNscServerContext* ctx = (whTransportNscServerContext*)context; + + (void)config; + + if (ctx == NULL) { + return WH_ERROR_BADARGS; + } + + memset(ctx, 0, sizeof(*ctx)); + + if (connectcb != NULL) { + connectcb(connectcb_arg, WH_COMM_CONNECTED); + } + return WH_ERROR_OK; +} + +static int _NscServerRecv(void* context, uint16_t* inout_size, void* data) +{ + whTransportNscServerContext* ctx = (whTransportNscServerContext*)context; + + if (ctx == NULL || inout_size == NULL || data == NULL) { + return WH_ERROR_BADARGS; + } + if (!ctx->request_pending || ctx->req_buf == NULL || ctx->req_size == 0U) { + return WH_ERROR_NOTREADY; + } + if (ctx->req_size > *inout_size) { + return WH_ERROR_ABORTED; + } + + memcpy(data, ctx->req_buf, ctx->req_size); + *inout_size = ctx->req_size; + ctx->request_pending = 0; + return WH_ERROR_OK; +} + +static int _NscServerSend(void* context, uint16_t size, const void* data) +{ + whTransportNscServerContext* ctx = (whTransportNscServerContext*)context; + + if (ctx == NULL || data == NULL) { + return WH_ERROR_BADARGS; + } + if (size == 0U || ctx->rsp_buf == NULL || size > ctx->rsp_capacity) { + return WH_ERROR_ABORTED; + } + + memcpy(ctx->rsp_buf, data, size); + ctx->rsp_size = size; + return WH_ERROR_OK; +} + +static int _NscServerCleanup(void* context) +{ + whTransportNscServerContext* ctx = (whTransportNscServerContext*)context; + if (ctx == NULL) { + return WH_ERROR_BADARGS; + } + return WH_ERROR_OK; +} + +const whTransportServerCb whTransportNscServer_Cb = { + .Init = _NscServerInit, + .Recv = _NscServerRecv, + .Send = _NscServerSend, + .Cleanup = _NscServerCleanup, +}; + +#endif /* WOLFHSM_CFG_PORT_STM32_TZ_NSC */ diff --git a/port/stmicro/stm32-tz/wh_transport_nsc.h b/port/stmicro/stm32-tz/wh_transport_nsc.h new file mode 100644 index 000000000..a7af9f4c1 --- /dev/null +++ b/port/stmicro/stm32-tz/wh_transport_nsc.h @@ -0,0 +1,92 @@ +/* + * port/stmicro/stm32-tz/wh_transport_nsc.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHSM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA + */ + +/* + * Synchronous TrustZone NSC bridge transport for wolfHSM. + * + * The non-secure (client) side calls a single ARMv8-M Cortex-M + * cmse_nonsecure_entry veneer (`wcs_wolfhsm_transmit`) provided by the + * secure-side host. The veneer hands the request to the secure-side + * server context, runs `wh_Server_HandleRequestMessage` once inline, + * and returns the response in the same call. There is no polling, + * notify counter, or async producer/consumer — Send delivers the + * response, Recv just hands it back. + * + * The transport is generic to any ARMv8-M TrustZone target; the + * STM32-specific dependencies live in the host (wolfBoot). + */ + +#ifndef WH_TRANSPORT_NSC_H_ +#define WH_TRANSPORT_NSC_H_ + +#include "wolfhsm/wh_settings.h" + +#ifdef WOLFHSM_CFG_PORT_STM32_TZ_NSC + +#include +#include "wolfhsm/wh_comm.h" + +#define WH_TRANSPORT_NSC_BUFFER_SIZE WH_COMM_MTU + +/* + * Non-secure (client) context. Owns the bridge buffers in NS .bss. + * Not internally thread-safe. + */ +typedef struct { + uint8_t cmd_buf[WH_TRANSPORT_NSC_BUFFER_SIZE]; + uint8_t rsp_buf[WH_TRANSPORT_NSC_BUFFER_SIZE]; + uint16_t last_rsp_size; /* size of the cached response from the last Send */ + uint8_t initialized; + uint8_t WH_PAD[2]; +} whTransportNscClientContext; + +/* Empty config struct - kept for ABI symmetry with other transports */ +typedef struct { + uint8_t WH_PAD[1]; +} whTransportNscClientConfig; + +/* + * Secure-side server context. Populated by the NSC veneer per call: + * before invoking `wh_Server_HandleRequestMessage` the host sets + * req_buf/req_size/rsp_buf/rsp_capacity; after the dispatcher returns, + * the host reads rsp_size to pass back to the non-secure caller. + */ +typedef struct { + const uint8_t* req_buf; + uint16_t req_size; + uint8_t* rsp_buf; + uint16_t rsp_capacity; + uint16_t rsp_size; /* set by Send, read by veneer */ + uint8_t request_pending; /* set by veneer, cleared by Recv */ + uint8_t WH_PAD[1]; +} whTransportNscServerContext; + +typedef struct { + uint8_t WH_PAD[1]; +} whTransportNscServerConfig; + +extern const whTransportClientCb whTransportNscClient_Cb; +extern const whTransportServerCb whTransportNscServer_Cb; + +#endif /* WOLFHSM_CFG_PORT_STM32_TZ_NSC */ + +#endif /* WH_TRANSPORT_NSC_H_ */ From b8d3fe4cd3f7437be3d70a16c24b4b39d63b2dc7 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Fri, 1 May 2026 11:06:35 -0700 Subject: [PATCH 2/6] Cleanup phase 1 code --- port/stmicro/stm32-tz/wh_transport_nsc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/stmicro/stm32-tz/wh_transport_nsc.h b/port/stmicro/stm32-tz/wh_transport_nsc.h index a7af9f4c1..9fe352e56 100644 --- a/port/stmicro/stm32-tz/wh_transport_nsc.h +++ b/port/stmicro/stm32-tz/wh_transport_nsc.h @@ -54,7 +54,7 @@ typedef struct { uint8_t cmd_buf[WH_TRANSPORT_NSC_BUFFER_SIZE]; uint8_t rsp_buf[WH_TRANSPORT_NSC_BUFFER_SIZE]; - uint16_t last_rsp_size; /* size of the cached response from the last Send */ + uint16_t last_rsp_size; uint8_t initialized; uint8_t WH_PAD[2]; } whTransportNscClientContext; From a6435acd54beefe2899ef2977e9e1e23174c6fda Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Fri, 1 May 2026 11:21:42 -0700 Subject: [PATCH 3/6] port/stmicro/stm32-tz: drop redundant comment on last_rsp_size The field name is self-describing; the comment was duplicating it. --- port/stmicro/stm32-tz/wh_transport_nsc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/stmicro/stm32-tz/wh_transport_nsc.h b/port/stmicro/stm32-tz/wh_transport_nsc.h index 9fe352e56..40df86d6f 100644 --- a/port/stmicro/stm32-tz/wh_transport_nsc.h +++ b/port/stmicro/stm32-tz/wh_transport_nsc.h @@ -45,7 +45,7 @@ #include #include "wolfhsm/wh_comm.h" -#define WH_TRANSPORT_NSC_BUFFER_SIZE WH_COMM_MTU +#define WH_TRANSPORT_NSC_BUFFER_SIZE WH_COMM_MTU /* * Non-secure (client) context. Owns the bridge buffers in NS .bss. From 56490aa19075e49f0dcd21cc3c4d9c100048fe24 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Fri, 1 May 2026 13:08:41 -0700 Subject: [PATCH 4/6] docs/chapter08: document STM32 TrustZone (NSC bridge) port Add a port section describing the new port/stmicro/stm32-tz NSC bridge transport: synchronous single-call client Send/Recv, server-side static context, target-agnostic transport with the STM32H5 glue (NSC veneer, whFlashCb adapter, secure-side server init, NS test exerciser) living in the wolfBoot port. --- docs/src/chapter08.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/src/chapter08.md b/docs/src/chapter08.md index 095e7b7be..752439ef0 100644 --- a/docs/src/chapter08.md +++ b/docs/src/chapter08.md @@ -46,6 +46,16 @@ The distribution of this port is restricted by the vendor. Please contact suppo - 1x 100MHz e200z0 PowerPC HSM core with NVM - Crypto offload: TRNG, AES128 +### STM32 TrustZone (STM32H5 / NSC bridge) + +The `port/stmicro/stm32-tz` port provides a synchronous TrustZone non-secure-callable bridge transport for ARMv8-M Cortex-M targets. It is designed for the wolfBoot deployment in which the secure-side wolfBoot image hosts a wolfHSM server and exposes it to the non-secure application through a single `cmse_nonsecure_entry` veneer (`wcs_wolfhsm_transmit`); see `wolfBoot/docs/wolfHSM.md` for the build, flash, and test recipe on STM32H5. + +The port provides: +- Single-call NSC transport (no polling, no shared-memory ring): client `Send` invokes the host-supplied veneer inline and caches the response; client `Recv` returns the cached response on first call. +- Server-side callbacks that consume the request the host's veneer parked in a static context and write the response back to the non-secure caller's buffer. + +The transport itself is target-agnostic; the STM32H5-specific glue (NSC veneer, `whFlashCb` flash adapter, secure-side server init, NS test exerciser) lives in the wolfBoot port. + ### POSIX The POSIX port provides multiple and fully functional implementations of different wolfHSM abstractions that can be used to better understand the exact functionality expected for different hardware abstractions. From ea437f1e3645d8daaae2903f1d3b9146bde5ad10 Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Fri, 1 May 2026 16:01:55 -0700 Subject: [PATCH 5/6] port/stmicro/stm32-tz: Skoll review fixes + host unit test Transport (port/stmicro/stm32-tz/wh_transport_nsc): - _NscServerSend returns WH_ERROR_BADARGS for size validation (only ABORTED when rsp_buf is NULL), matching the contract. - _NscClientSend / _NscClientRecv reject calls on an uninitialized context, giving ctx->initialized a purpose. - _NscServerRecv clears request_pending on the oversize path and resets rsp_size on entry to prevent stale-value leaks. - Drop the redundant cmd_buf staging copy on the client side, saving WH_COMM_MTU bytes of NS BSS plus a per-request memcpy. Test: - New test/wh_test_transport_nsc.c covering BADARGS, NOTREADY, happy path, and the request_pending / rsp_size state machine for both callback tables. Wired into whTest_Unit; new STM32_TZ_NSC=1 build flag compiles the transport source. Docs: - chapter08: client Recv consumes the cached response on the first call (subsequent calls return WH_ERROR_NOTREADY). --- docs/src/chapter08.md | 2 +- port/stmicro/stm32-tz/wh_transport_nsc.c | 21 ++- port/stmicro/stm32-tz/wh_transport_nsc.h | 5 +- test/Makefile | 8 + test/wh_test.c | 7 + test/wh_test_transport_nsc.c | 212 +++++++++++++++++++++++ test/wh_test_transport_nsc.h | 26 +++ 7 files changed, 270 insertions(+), 11 deletions(-) create mode 100644 test/wh_test_transport_nsc.c create mode 100644 test/wh_test_transport_nsc.h diff --git a/docs/src/chapter08.md b/docs/src/chapter08.md index 752439ef0..ae194d2d7 100644 --- a/docs/src/chapter08.md +++ b/docs/src/chapter08.md @@ -51,7 +51,7 @@ The distribution of this port is restricted by the vendor. Please contact suppo The `port/stmicro/stm32-tz` port provides a synchronous TrustZone non-secure-callable bridge transport for ARMv8-M Cortex-M targets. It is designed for the wolfBoot deployment in which the secure-side wolfBoot image hosts a wolfHSM server and exposes it to the non-secure application through a single `cmse_nonsecure_entry` veneer (`wcs_wolfhsm_transmit`); see `wolfBoot/docs/wolfHSM.md` for the build, flash, and test recipe on STM32H5. The port provides: -- Single-call NSC transport (no polling, no shared-memory ring): client `Send` invokes the host-supplied veneer inline and caches the response; client `Recv` returns the cached response on first call. +- Single-call NSC transport (no polling, no shared-memory ring): client `Send` invokes the host-supplied veneer inline and caches the response; client `Recv` consumes the cached response on the first call (subsequent calls return `WH_ERROR_NOTREADY` until the next `Send`). - Server-side callbacks that consume the request the host's veneer parked in a static context and write the response back to the non-secure caller's buffer. The transport itself is target-agnostic; the STM32H5-specific glue (NSC veneer, `whFlashCb` flash adapter, secure-side server init, NS test exerciser) lives in the wolfBoot port. diff --git a/port/stmicro/stm32-tz/wh_transport_nsc.c b/port/stmicro/stm32-tz/wh_transport_nsc.c index 6578e517c..729b79505 100644 --- a/port/stmicro/stm32-tz/wh_transport_nsc.c +++ b/port/stmicro/stm32-tz/wh_transport_nsc.c @@ -73,18 +73,16 @@ static int _NscClientSend(void* context, uint16_t size, const void* data) uint32_t rspSz; int rc; - if (ctx == NULL || data == NULL) { + if (ctx == NULL || data == NULL || ctx->initialized == 0U) { return WH_ERROR_BADARGS; } if (size == 0U || size > WH_TRANSPORT_NSC_BUFFER_SIZE) { return WH_ERROR_BADARGS; } - memcpy(ctx->cmd_buf, data, size); rspSz = (uint32_t)WH_TRANSPORT_NSC_BUFFER_SIZE; - - rc = wcs_wolfhsm_transmit(ctx->cmd_buf, (uint32_t)size, - ctx->rsp_buf, &rspSz); + rc = wcs_wolfhsm_transmit((const uint8_t*)data, (uint32_t)size, + ctx->rsp_buf, &rspSz); if (rc != 0) { ctx->last_rsp_size = 0; return WH_ERROR_ABORTED; @@ -102,7 +100,8 @@ static int _NscClientRecv(void* context, uint16_t* out_size, void* data) { whTransportNscClientContext* ctx = (whTransportNscClientContext*)context; - if (ctx == NULL || out_size == NULL || data == NULL) { + if (ctx == NULL || out_size == NULL || data == NULL || + ctx->initialized == 0U) { return WH_ERROR_BADARGS; } if (ctx->last_rsp_size == 0U) { @@ -172,9 +171,14 @@ static int _NscServerRecv(void* context, uint16_t* inout_size, void* data) return WH_ERROR_NOTREADY; } if (ctx->req_size > *inout_size) { + ctx->request_pending = 0; return WH_ERROR_ABORTED; } + /* Stale value from the previous call must not leak through error paths + * that skip Send. */ + ctx->rsp_size = 0; + memcpy(data, ctx->req_buf, ctx->req_size); *inout_size = ctx->req_size; ctx->request_pending = 0; @@ -188,7 +192,10 @@ static int _NscServerSend(void* context, uint16_t size, const void* data) if (ctx == NULL || data == NULL) { return WH_ERROR_BADARGS; } - if (size == 0U || ctx->rsp_buf == NULL || size > ctx->rsp_capacity) { + if (size == 0U || size > ctx->rsp_capacity) { + return WH_ERROR_BADARGS; + } + if (ctx->rsp_buf == NULL) { return WH_ERROR_ABORTED; } diff --git a/port/stmicro/stm32-tz/wh_transport_nsc.h b/port/stmicro/stm32-tz/wh_transport_nsc.h index 40df86d6f..a55b5025d 100644 --- a/port/stmicro/stm32-tz/wh_transport_nsc.h +++ b/port/stmicro/stm32-tz/wh_transport_nsc.h @@ -48,15 +48,14 @@ #define WH_TRANSPORT_NSC_BUFFER_SIZE WH_COMM_MTU /* - * Non-secure (client) context. Owns the bridge buffers in NS .bss. + * Non-secure (client) context. Owns the response buffer in NS .bss. * Not internally thread-safe. */ typedef struct { - uint8_t cmd_buf[WH_TRANSPORT_NSC_BUFFER_SIZE]; uint8_t rsp_buf[WH_TRANSPORT_NSC_BUFFER_SIZE]; uint16_t last_rsp_size; uint8_t initialized; - uint8_t WH_PAD[2]; + uint8_t WH_PAD[5]; /* Pad to 8-byte alignment */ } whTransportNscClientContext; /* Empty config struct - kept for ABI symmetry with other transports */ diff --git a/test/Makefile b/test/Makefile index a90706c62..0525474d0 100644 --- a/test/Makefile +++ b/test/Makefile @@ -159,6 +159,14 @@ ifeq ($(AUTH),1) DEF += -DWOLFHSM_CFG_ENABLE_AUTHENTICATION endif +# Build the STM32 TZ NSC bridge transport plus its host unit test +ifeq ($(STM32_TZ_NSC),1) + DEF += -DWOLFHSM_CFG_PORT_STM32_TZ_NSC + WOLFHSM_STM32_TZ_DIR := $(WOLFHSM_DIR)/port/stmicro/stm32-tz + INC += -I$(WOLFHSM_STM32_TZ_DIR) + SRC_C += $(wildcard $(WOLFHSM_STM32_TZ_DIR)/*.c) +endif + ## Project defines # Option to build wolfcrypt tests ifeq ($(TESTWOLFCRYPT),1) diff --git a/test/wh_test.c b/test/wh_test.c index 96339fdcc..9aae4dca8 100644 --- a/test/wh_test.c +++ b/test/wh_test.c @@ -45,6 +45,9 @@ #include "wh_test_crypto_affinity.h" #include "wh_test_timeout.h" #include "wh_test_dma.h" +#ifdef WOLFHSM_CFG_PORT_STM32_TZ_NSC +#include "wh_test_transport_nsc.h" +#endif #ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION #include "wh_test_auth.h" #endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ @@ -99,6 +102,10 @@ int whTest_Unit(void) WH_TEST_ASSERT(0 == whTest_Comm()); WH_TEST_ASSERT(0 == whTest_ClientServer()); +#ifdef WOLFHSM_CFG_PORT_STM32_TZ_NSC + WH_TEST_ASSERT(0 == whTest_TransportNsc()); +#endif + #ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION /* Auth tests */ WH_TEST_ASSERT(0 == whTest_AuthMEM()); diff --git a/test/wh_test_transport_nsc.c b/test/wh_test_transport_nsc.c new file mode 100644 index 000000000..7a15df40c --- /dev/null +++ b/test/wh_test_transport_nsc.c @@ -0,0 +1,212 @@ +/* + * test/wh_test_transport_nsc.c + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHSM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfHSM. If not, see . + */ + +#include "wolfhsm/wh_settings.h" + +#ifdef WOLFHSM_CFG_PORT_STM32_TZ_NSC + +#include +#include + +#include "wolfhsm/wh_comm.h" +#include "wolfhsm/wh_error.h" + +#include "wh_transport_nsc.h" +#include "wh_test_common.h" +#include "wh_test_transport_nsc.h" + +/* Stub of the secure-side veneer the client transport normally resolves at + * link time. Echoes the request back with a 1-byte tag. */ +static int g_stub_force_rc; +static uint32_t g_stub_force_rspSz; + +int wcs_wolfhsm_transmit(const uint8_t* cmd, uint32_t cmdSz, uint8_t* rsp, + uint32_t* rspSz) +{ + if (g_stub_force_rc != 0) { + return g_stub_force_rc; + } + if (cmd == NULL || rsp == NULL || rspSz == NULL) { + return -1; + } + if (cmdSz == 0U || cmdSz > WH_TRANSPORT_NSC_BUFFER_SIZE) { + return -1; + } + if (*rspSz < cmdSz + 1U) { + return -1; + } + rsp[0] = 0xAB; + memcpy(rsp + 1, cmd, cmdSz); + *rspSz = (g_stub_force_rspSz != 0U) ? g_stub_force_rspSz : (cmdSz + 1U); + return 0; +} + +static int test_client_callbacks(void) +{ + whTransportNscClientContext ctx; + static const uint8_t data_in[] = {1, 2, 3, 4}; + uint8_t data_out[WH_TRANSPORT_NSC_BUFFER_SIZE]; + uint16_t sz; + int rc; + + g_stub_force_rc = 0; + g_stub_force_rspSz = 0; + + rc = whTransportNscClient_Cb.Init(&ctx, NULL, NULL, NULL); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(ctx.initialized == 1U); + + WH_TEST_ASSERT_RETURN(WH_ERROR_BADARGS == + whTransportNscClient_Cb.Send(NULL, sizeof(data_in), data_in)); + WH_TEST_ASSERT_RETURN(WH_ERROR_BADARGS == + whTransportNscClient_Cb.Send(&ctx, sizeof(data_in), NULL)); + WH_TEST_ASSERT_RETURN(WH_ERROR_BADARGS == + whTransportNscClient_Cb.Send(&ctx, 0, data_in)); + WH_TEST_ASSERT_RETURN(WH_ERROR_BADARGS == + whTransportNscClient_Cb.Send(&ctx, + (uint16_t)WH_TRANSPORT_NSC_BUFFER_SIZE + 1U, data_in)); + + sz = sizeof(data_out); + WH_TEST_ASSERT_RETURN(WH_ERROR_NOTREADY == + whTransportNscClient_Cb.Recv(&ctx, &sz, data_out)); + + rc = whTransportNscClient_Cb.Send(&ctx, sizeof(data_in), data_in); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + sz = sizeof(data_out); + rc = whTransportNscClient_Cb.Recv(&ctx, &sz, data_out); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(sz == sizeof(data_in) + 1U); + WH_TEST_ASSERT_RETURN(data_out[0] == 0xABU); + WH_TEST_ASSERT_RETURN(memcmp(data_out + 1, data_in, sizeof(data_in)) == 0); + + WH_TEST_ASSERT_RETURN(WH_ERROR_NOTREADY == + whTransportNscClient_Cb.Recv(&ctx, &sz, data_out)); + + g_stub_force_rc = -42; + rc = whTransportNscClient_Cb.Send(&ctx, sizeof(data_in), data_in); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_ABORTED); + g_stub_force_rc = 0; + + g_stub_force_rspSz = (uint32_t)WH_TRANSPORT_NSC_BUFFER_SIZE + 1U; + rc = whTransportNscClient_Cb.Send(&ctx, sizeof(data_in), + data_in); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_ABORTED); + g_stub_force_rspSz = 0; + + rc = whTransportNscClient_Cb.Cleanup(&ctx); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(ctx.initialized == 0U); + + /* initialized==0 check */ + WH_TEST_ASSERT_RETURN(WH_ERROR_BADARGS == + whTransportNscClient_Cb.Send(&ctx, sizeof(data_in), data_in)); + sz = sizeof(data_out); + WH_TEST_ASSERT_RETURN(WH_ERROR_BADARGS == + whTransportNscClient_Cb.Recv(&ctx, &sz, data_out)); + + return WH_TEST_SUCCESS; +} + +static int test_server_callbacks(void) +{ + whTransportNscServerContext ctx; + static const uint8_t req[] = {0xAA, 0xBB, 0xCC}; + uint8_t rsp_buf[WH_TRANSPORT_NSC_BUFFER_SIZE]; + uint8_t data_buf[WH_TRANSPORT_NSC_BUFFER_SIZE]; + uint8_t* saved_rsp_buf; + uint16_t sz; + int rc; + + rc = whTransportNscServer_Cb.Init(&ctx, NULL, NULL, NULL); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + sz = sizeof(data_buf); + WH_TEST_ASSERT_RETURN(WH_ERROR_BADARGS == + whTransportNscServer_Cb.Recv(NULL, &sz, data_buf)); + WH_TEST_ASSERT_RETURN(WH_ERROR_BADARGS == + whTransportNscServer_Cb.Recv(&ctx, NULL, data_buf)); + WH_TEST_ASSERT_RETURN(WH_ERROR_BADARGS == + whTransportNscServer_Cb.Recv(&ctx, &sz, NULL)); + + WH_TEST_ASSERT_RETURN(WH_ERROR_NOTREADY == + whTransportNscServer_Cb.Recv(&ctx, &sz, data_buf)); + + /* Stage as the host's NSC veneer would. */ + ctx.req_buf = req; + ctx.req_size = (uint16_t)sizeof(req); + ctx.rsp_buf = rsp_buf; + ctx.rsp_capacity = (uint16_t)sizeof(rsp_buf); + ctx.rsp_size = 0xBEEF; + ctx.request_pending = 1; + + /* Oversize: must clear request_pending. */ + sz = (uint16_t)(sizeof(req) - 1U); + rc = whTransportNscServer_Cb.Recv(&ctx, &sz, data_buf); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_ABORTED); + WH_TEST_ASSERT_RETURN(ctx.request_pending == 0U); + + ctx.req_buf = req; + ctx.req_size = (uint16_t)sizeof(req); + ctx.rsp_buf = rsp_buf; + ctx.rsp_capacity = (uint16_t)sizeof(rsp_buf); + ctx.rsp_size = 0xBEEF; + ctx.request_pending = 1; + + sz = sizeof(data_buf); + rc = whTransportNscServer_Cb.Recv(&ctx, &sz, data_buf); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(sz == sizeof(req)); + WH_TEST_ASSERT_RETURN(memcmp(data_buf, req, sizeof(req)) == 0); + WH_TEST_ASSERT_RETURN(ctx.request_pending == 0U); + WH_TEST_ASSERT_RETURN(ctx.rsp_size == 0U); /* reset by Recv */ + + WH_TEST_ASSERT_RETURN(WH_ERROR_BADARGS == + whTransportNscServer_Cb.Send(&ctx, 0, req)); + WH_TEST_ASSERT_RETURN(WH_ERROR_BADARGS == + whTransportNscServer_Cb.Send(&ctx, ctx.rsp_capacity + 1U, req)); + + saved_rsp_buf = ctx.rsp_buf; + ctx.rsp_buf = NULL; + WH_TEST_ASSERT_RETURN(WH_ERROR_ABORTED == + whTransportNscServer_Cb.Send(&ctx, sizeof(req), req)); + ctx.rsp_buf = saved_rsp_buf; + + rc = whTransportNscServer_Cb.Send(&ctx, sizeof(req), req); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(ctx.rsp_size == sizeof(req)); + WH_TEST_ASSERT_RETURN(memcmp(rsp_buf, req, sizeof(req)) == 0); + + rc = whTransportNscServer_Cb.Cleanup(&ctx); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + return WH_TEST_SUCCESS; +} + +int whTest_TransportNsc(void) +{ + WH_TEST_PRINT("Enter NSC transport tests\n"); + WH_TEST_RETURN_ON_FAIL(test_client_callbacks()); + WH_TEST_RETURN_ON_FAIL(test_server_callbacks()); + WH_TEST_PRINT("NSC transport tests passed\n"); + return WH_TEST_SUCCESS; +} + +#endif /* WOLFHSM_CFG_PORT_STM32_TZ_NSC */ diff --git a/test/wh_test_transport_nsc.h b/test/wh_test_transport_nsc.h new file mode 100644 index 000000000..709338620 --- /dev/null +++ b/test/wh_test_transport_nsc.h @@ -0,0 +1,26 @@ +/* + * test/wh_test_transport_nsc.h + * + * Copyright (C) 2026 wolfSSL Inc. + * + * This file is part of wolfHSM. + * + * wolfHSM is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * wolfHSM is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with wolfHSM. If not, see . + */ +#ifndef WH_TEST_TRANSPORT_NSC_H +#define WH_TEST_TRANSPORT_NSC_H + +int whTest_TransportNsc(void); + +#endif /* WH_TEST_TRANSPORT_NSC_H */ From 4a42e8fb7bce6488a14d5abd9b39f1206add8c8c Mon Sep 17 00:00:00 2001 From: Aidan Garske Date: Fri, 1 May 2026 16:09:21 -0700 Subject: [PATCH 6/6] Add CI testing for tz build path --- .github/workflows/build-and-test.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index fa8d55807..1f14567bd 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -36,6 +36,10 @@ jobs: - name: Build and test run: cd test && make clean && make -j WOLFSSL_DIR=../wolfssl && make run + # Build and test STM32 TrustZone NSC bridge transport (port/stmicro/stm32-tz) + - name: Build and test STM32_TZ_NSC ASAN + run: cd test && make clean && make -j STM32_TZ_NSC=1 ASAN=1 WOLFSSL_DIR=../wolfssl && make run + # Build and test standard build, with DMA and ASAN enabled - name: Build and test DMA ASAN run: cd test && make clean && make -j DMA=1 ASAN=1 WOLFSSL_DIR=../wolfssl && make run