Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 10 additions & 0 deletions docs/src/chapter08.md
Original file line number Diff line number Diff line change
Expand Up @@ -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` 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.

### 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.
Expand Down
223 changes: 223 additions & 0 deletions port/stmicro/stm32-tz/wh_transport_nsc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
/*
* 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 <stdint.h>
#include <string.h>

#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 || ctx->initialized == 0U) {
return WH_ERROR_BADARGS;
}
if (size == 0U || size > WH_TRANSPORT_NSC_BUFFER_SIZE) {
return WH_ERROR_BADARGS;
}

rspSz = (uint32_t)WH_TRANSPORT_NSC_BUFFER_SIZE;
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;
}
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 ||
ctx->initialized == 0U) {
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;
}
Comment on lines +99 to +115

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) {
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;
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 || size > ctx->rsp_capacity) {
return WH_ERROR_BADARGS;
}
if (ctx->rsp_buf == NULL) {
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 */
91 changes: 91 additions & 0 deletions port/stmicro/stm32-tz/wh_transport_nsc.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*
* 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 <stdint.h>
#include "wolfhsm/wh_comm.h"

#define WH_TRANSPORT_NSC_BUFFER_SIZE WH_COMM_MTU

/*
* Non-secure (client) context. Owns the response buffer in NS .bss.
* Not internally thread-safe.
*/
typedef struct {
uint8_t rsp_buf[WH_TRANSPORT_NSC_BUFFER_SIZE];
uint16_t last_rsp_size;
uint8_t initialized;
uint8_t WH_PAD[5]; /* Pad to 8-byte alignment */
} 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_ */
8 changes: 8 additions & 0 deletions test/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions test/wh_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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());
Expand Down
Loading
Loading