diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index fa8d55807..c2abba9f8 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 ARMv8-M TrustZone NSC bridge transport (port/armv8m-tz) + - name: Build and test ARMV8M_TZ_NSC ASAN + run: cd test && make clean && make -j ARMV8M_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 diff --git a/docs/src/chapter08.md b/docs/src/chapter08.md index 095e7b7be..5b017325a 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 +### ARMv8-M TrustZone (NSC bridge) + +The `port/armv8m-tz` port provides a synchronous TrustZone non-secure-callable bridge transport for any ARMv8-M Cortex-M target (Cortex-M23 / M33 / M35P / M55 / M85). It is designed for deployments in which a secure-side image hosts a wolfHSM server and exposes it to the non-secure application through a single `cmse_nonsecure_entry` veneer (`wcs_wolfhsm_transmit`). The first integration is wolfBoot on STM32H5; see `wolfBoot/docs/wolfHSM.md` for the build, flash, and test recipe. + +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 is target-agnostic. Bringing it up on a new ARMv8-M part is a porting exercise on the host side only: provide the `cmse_nonsecure_entry` veneer that fronts `wcs_wolfhsm_transmit`, plus whatever flash/NVM adapter and server init the deployment needs. + ### 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. diff --git a/port/armv8m-tz/wh_transport_nsc.c b/port/armv8m-tz/wh_transport_nsc.c new file mode 100644 index 000000000..233c0d0fc --- /dev/null +++ b/port/armv8m-tz/wh_transport_nsc.c @@ -0,0 +1,237 @@ +/* + * port/armv8m-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 wolfHSM. If not, see . + */ + +#include "wolfhsm/wh_settings.h" + +#ifdef WOLFHSM_CFG_PORT_ARMV8M_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 || ctx->initialized == 0U) { + return WH_ERROR_BADARGS; + } + if (size == 0U || size > WH_TRANSPORT_NSC_BUFFER_SIZE) { + return WH_ERROR_BADARGS; + } + /* prior response must be consumed before next Send */ + if (ctx->last_rsp_size != 0U) { + return WH_ERROR_NOTREADY; + } + + 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; + /* propagate known wolfHSM error codes, collapse unknowns */ + if (rc == WH_ERROR_BADARGS || rc == WH_ERROR_NOTREADY || + rc == WH_ERROR_ABORTED) { + return rc; + } + 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; + } + /* out_size is in/out capacity; reject truncation, keep cached response */ + if (*out_size < ctx->last_rsp_size) { + return WH_ERROR_BADARGS; + } + + 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; + } + /* clear stale rsp_size up-front so every exit path leaves a clean state */ + ctx->rsp_size = 0; + + if (ctx->req_size > *inout_size) { + ctx->request_pending = 0; + 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) +{ + /* veneer is responsible for Recv/Send pairing; Send does not enforce it */ + 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; + } + /* clear stale NS pointers so they cannot survive reinit */ + memset(ctx, 0, sizeof(*ctx)); + return WH_ERROR_OK; +} + +const whTransportServerCb whTransportNscServer_Cb = { + .Init = _NscServerInit, + .Recv = _NscServerRecv, + .Send = _NscServerSend, + .Cleanup = _NscServerCleanup, +}; + +#endif /* WOLFHSM_CFG_PORT_ARMV8M_TZ_NSC */ diff --git a/port/armv8m-tz/wh_transport_nsc.h b/port/armv8m-tz/wh_transport_nsc.h new file mode 100644 index 000000000..c91199bc1 --- /dev/null +++ b/port/armv8m-tz/wh_transport_nsc.h @@ -0,0 +1,91 @@ +/* + * port/armv8m-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 wolfHSM. If not, see . + */ + +/* + * 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 target-agnostic across ARMv8-M TrustZone parts; + * the target-specific NSC veneer is provided by the host. + */ + +#ifndef WH_TRANSPORT_NSC_H_ +#define WH_TRANSPORT_NSC_H_ + +#include "wolfhsm/wh_settings.h" + +#ifdef WOLFHSM_CFG_PORT_ARMV8M_TZ_NSC + +#include +#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]; /* trailing slack */ +} whTransportNscClientContext; + +/* Empty config; Init accepts NULL since there is nothing to read. */ +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; + uint8_t* rsp_buf; + uint16_t req_size; + 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; + +/* Pre-populated tables; callbacks are file-local in wh_transport_nsc.c */ +extern const whTransportClientCb whTransportNscClient_Cb; +extern const whTransportServerCb whTransportNscServer_Cb; + +#endif /* WOLFHSM_CFG_PORT_ARMV8M_TZ_NSC */ + +#endif /* WH_TRANSPORT_NSC_H_ */ diff --git a/test/Makefile b/test/Makefile index de09e7d04..4a0bf0dfd 100644 --- a/test/Makefile +++ b/test/Makefile @@ -165,6 +165,14 @@ ifeq ($(AUTH),1) DEF += -DWOLFHSM_CFG_ENABLE_AUTHENTICATION endif +# Build the ARMv8-M TrustZone NSC bridge transport plus its host unit test +ifeq ($(ARMV8M_TZ_NSC),1) + DEF += -DWOLFHSM_CFG_PORT_ARMV8M_TZ_NSC + WOLFHSM_ARMV8M_TZ_DIR := $(WOLFHSM_DIR)/port/armv8m-tz + INC += -I$(WOLFHSM_ARMV8M_TZ_DIR) + SRC_C += $(wildcard $(WOLFHSM_ARMV8M_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 6bfc6c4ce..f6d956066 100644 --- a/test/wh_test.c +++ b/test/wh_test.c @@ -46,6 +46,9 @@ #include "wh_test_timeout.h" #include "wh_test_dma.h" #include "wh_test_keystore_reqsize.h" +#ifdef WOLFHSM_CFG_PORT_ARMV8M_TZ_NSC +#include "wh_test_transport_nsc.h" +#endif #ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION #include "wh_test_auth.h" #endif /* WOLFHSM_CFG_ENABLE_AUTHENTICATION */ @@ -105,6 +108,10 @@ int whTest_Unit(void) WH_TEST_ASSERT(0 == whTest_Comm()); WH_TEST_ASSERT(0 == whTest_ClientServer()); +#ifdef WOLFHSM_CFG_PORT_ARMV8M_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_check_struct_padding.c b/test/wh_test_check_struct_padding.c index bb4b550d0..e2dbfdce7 100644 --- a/test/wh_test_check_struct_padding.c +++ b/test/wh_test_check_struct_padding.c @@ -203,4 +203,10 @@ whMessageCert_VerifyAcertRequest whMessageCert_VerifyAcertRequest_test; #endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER_ACERT */ #endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER */ +#ifdef WOLFHSM_CFG_PORT_ARMV8M_TZ_NSC +#include "wh_transport_nsc.h" +whTransportNscClientContext whTransportNscClientContext_test; +whTransportNscServerContext whTransportNscServerContext_test; +#endif /* WOLFHSM_CFG_PORT_ARMV8M_TZ_NSC */ + #endif /* WH_TEST_CHECK_STRUCT_PADDING_C_ */ diff --git a/test/wh_test_transport_nsc.c b/test/wh_test_transport_nsc.c new file mode 100644 index 000000000..a55317861 --- /dev/null +++ b/test/wh_test_transport_nsc.c @@ -0,0 +1,236 @@ +/* + * 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_ARMV8M_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)); + + /* too-small buffer returns BADARGS, cached response preserved */ + rc = whTransportNscClient_Cb.Send(&ctx, sizeof(data_in), data_in); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + sz = (uint16_t)(sizeof(data_in)); + WH_TEST_ASSERT_RETURN(WH_ERROR_BADARGS == + whTransportNscClient_Cb.Recv(&ctx, &sz, data_out)); + /* second Send before Recv consumes prior reply is NOTREADY */ + WH_TEST_ASSERT_RETURN(WH_ERROR_NOTREADY == + whTransportNscClient_Cb.Send(&ctx, sizeof(data_in), data_in)); + 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); + + 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; + + /* WH_ERROR_* return values from the veneer are propagated unchanged. */ + g_stub_force_rc = WH_ERROR_BADARGS; + rc = whTransportNscClient_Cb.Send(&ctx, sizeof(data_in), data_in); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + g_stub_force_rc = WH_ERROR_NOTREADY; + rc = whTransportNscClient_Cb.Send(&ctx, sizeof(data_in), data_in); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_NOTREADY); + 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 and rsp_size */ + 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); + WH_TEST_ASSERT_RETURN(ctx.rsp_size == 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_ARMV8M_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 */ diff --git a/wolfhsm/wh_settings.h b/wolfhsm/wh_settings.h index 18d23bd57..c4f6d3579 100644 --- a/wolfhsm/wh_settings.h +++ b/wolfhsm/wh_settings.h @@ -129,6 +129,14 @@ * WOLFHSM_CFG_PORT_GETTIME - Function-like macro returning the current system * time in microseconds as a uint64_t. Must be defined in wolfhsm_cfg.h for * the active port UNLESS WOLFHSM_CFG_NO_SYS_TIME is defined + * + * WOLFHSM_CFG_PORT_ARMV8M_TZ_NSC - If defined, build the ARMv8-M TrustZone + * non-secure-callable (NSC) bridge transport in + * port/armv8m-tz/wh_transport_nsc.{c,h}. Target-agnostic across + * ARMv8-M Cortex-M parts; the non-secure client calls into the + * secure-side server through a single cmse_nonsecure_entry veneer + * provided by the host (e.g. wolfBoot's WOLFCRYPT_TZ_WOLFHSM engine). + * Default: Not defined * Overridable porting functions: *