From e3b29f61084948a6c978413163f3639c28347a90 Mon Sep 17 00:00:00 2001 From: Paul Adelsbach Date: Mon, 20 Apr 2026 14:42:35 -0700 Subject: [PATCH] Initial prototype of test refactor w/ runner, helper and test layers --- .github/workflows/build-and-test.yml | 3 + test-refactor/README.md | 136 ++++++++ test-refactor/client-server/wh_test_crypto.c | 178 ++++++++++ test-refactor/client-server/wh_test_crypto.h | 38 +++ test-refactor/client-server/wh_test_echo.c | 80 +++++ test-refactor/client-server/wh_test_echo.h | 32 ++ .../client-server/wh_test_server_info.c | 89 +++++ .../client-server/wh_test_server_info.h | 32 ++ test-refactor/client-server/wh_test_stress.c | 57 ++++ test-refactor/client-server/wh_test_stress.h | 34 ++ test-refactor/misc/wh_test_flash_ramsim.c | 240 +++++++++++++ test-refactor/misc/wh_test_flash_ramsim.h | 32 ++ test-refactor/misc/wh_test_nvm_flash.c | 314 ++++++++++++++++++ test-refactor/misc/wh_test_nvm_flash.h | 33 ++ test-refactor/posix/Makefile | 142 ++++++++ .../posix/wh_test_helpers_client_posix.c | 94 ++++++ .../posix/wh_test_helpers_client_posix.h | 47 +++ .../posix/wh_test_helpers_server_posix.c | 163 +++++++++ .../posix/wh_test_helpers_server_posix.h | 58 ++++ test-refactor/posix/wh_test_main_posix.c | 241 ++++++++++++++ test-refactor/server/wh_test_cert.c | 128 +++++++ test-refactor/server/wh_test_cert.h | 32 ++ test-refactor/wh_test_groups.c | 105 ++++++ test-refactor/wh_test_groups.h | 61 ++++ test-refactor/wh_test_runner.c | 64 ++++ test-refactor/wh_test_runner.h | 75 +++++ 26 files changed, 2508 insertions(+) create mode 100644 test-refactor/README.md create mode 100644 test-refactor/client-server/wh_test_crypto.c create mode 100644 test-refactor/client-server/wh_test_crypto.h create mode 100644 test-refactor/client-server/wh_test_echo.c create mode 100644 test-refactor/client-server/wh_test_echo.h create mode 100644 test-refactor/client-server/wh_test_server_info.c create mode 100644 test-refactor/client-server/wh_test_server_info.h create mode 100644 test-refactor/client-server/wh_test_stress.c create mode 100644 test-refactor/client-server/wh_test_stress.h create mode 100644 test-refactor/misc/wh_test_flash_ramsim.c create mode 100644 test-refactor/misc/wh_test_flash_ramsim.h create mode 100644 test-refactor/misc/wh_test_nvm_flash.c create mode 100644 test-refactor/misc/wh_test_nvm_flash.h create mode 100644 test-refactor/posix/Makefile create mode 100644 test-refactor/posix/wh_test_helpers_client_posix.c create mode 100644 test-refactor/posix/wh_test_helpers_client_posix.h create mode 100644 test-refactor/posix/wh_test_helpers_server_posix.c create mode 100644 test-refactor/posix/wh_test_helpers_server_posix.h create mode 100644 test-refactor/posix/wh_test_main_posix.c create mode 100644 test-refactor/server/wh_test_cert.c create mode 100644 test-refactor/server/wh_test_cert.h create mode 100644 test-refactor/wh_test_groups.c create mode 100644 test-refactor/wh_test_groups.h create mode 100644 test-refactor/wh_test_runner.c create mode 100644 test-refactor/wh_test_runner.h diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index a8a2248b7..dca7b0d5f 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -32,6 +32,9 @@ jobs: repository: wolfssl/wolfssl path: wolfssl + - name: Build and test refactor + run: cd test-refactor/posix && make clean && make -j WOLFSSL_DIR=../../wolfssl && make run + # Build and test standard build - name: Build and test run: cd test && make clean && make -j WOLFSSL_DIR=../wolfssl && make run diff --git a/test-refactor/README.md b/test-refactor/README.md new file mode 100644 index 000000000..a5dc3d868 --- /dev/null +++ b/test-refactor/README.md @@ -0,0 +1,136 @@ +# test-refactor + +Prototype of the refactored wolfHSM test infrastructure. + +## Key differences from test/ + +- **Runner** (`wh_test_runner.h/c`): generic suite executor. + Each suite is a name + NULL-terminated test array, run + either via `whTestRunner_Run` (suite owns its setup/cleanup) + or `whTestRunner_RunWithCtx` (caller provides the live + context). Group functions use the latter. +- **App-owned init**: the port's main() brings up the server + and client once at startup (mirroring real firmware boot) + and hands the live contexts to the group functions. Suites + no longer stand up their own fixtures. +- **Port helpers** (`wh_test_helpers_server_.h/c`, + `wh_test_helpers_client_.h/c`): per-port files that + stand in for what a real target does at boot -- configure + flash, init NVM/crypto, wire up a transport, bring up the + server or client context. +- **Groups** (`wh_test_groups.h/c`): three portable entry + points (Misc/Server/Client) that main invokes. Each runs + its gated suites and calls the caller-implemented reset + hook between them. +- **Threaded driver**: the POSIX port's main runs the server + and client on separate threads. The server thread runs the + server-only group first, then enters a `HandleRequestMessage` + loop; the client thread runs the client-only group against + the live server. Ports that already split server and client + onto different cores/tasks do the same thing natively. +- **Platform split**: platform-specific code is isolated in + `wh_test_helpers_server_.c`, + `wh_test_helpers_client_.c`, and + `wh_test_main_.c`. Test modules and groups are + identical on all platforms. + +## Suites implemented so far + +| Suite | Group | Description | +|-------|-------|-------------| +| Flash RamSim | misc | Write-lock, erase, program, verify, blank-check | +| NVM Flash | misc | Flash unit ops, NVM add/overwrite/destroy/reclaim | +| Cert | server | Server-side cert add/verify/chain/erase | +| ClientServer | client-server | Echo round-trip, server info query | +| ThreadSafe Stress | client-server | Phased multi-thread contention (unchanged internals) | + +## Remaining tests to port + +| Suite | Group | Description | +|-------|-------|-------------| +| Comm | client-server | Transport layer (mem, TCP, SHM) | +| Crypto | client-server | AES, RSA, ECC, CMAC, curve25519, ed25519, etc. | +| Crypto Affinity | client-server | Device ID operation routing | +| SHE | client-server | Secure Hardware Extension key load, crypto, secure boot | +| Keywrap | client-server | Key wrap/unwrap operations | +| Log | misc | Logging frontend, ringbuf, POSIX file backends | +| Lock | misc | Lock primitives with POSIX backend | +| DMA | misc | DMA address translation and allow-list | +| Server Img Mgr | server | Image manager verify/install/erase | +| Timeout | client-server | POSIX timeout enforcement | +| wolfCrypt Test | client-server | wolfCrypt test suite via wolfHSM transport | +| MultiClient | client-server | 2 CS pairs, shared NVM, global/local key isolation | + +## Platforms requiring update + +Each platform with test infrastructure needs its own +`wh_test_helpers_server_.c`, +`wh_test_helpers_client_.c`, and +`wh_test_main_.c` (see "Porting" below). + +| Platform | Vendor | Test files | +|----------|--------|------------| +| POSIX | wolfSSL | `test-refactor/posix/` (done) | +| Bernina | STMicro | `bernina-server/src/bh_test.c` | +| SR6 | STMicro | (no test files found) | +| TC3xx | Infineon | `port/client/wolfhsm_tests.c`, `port/server/ccb_tests.c` | +| RH850 F1KM | Renesas | `rh850_test2_1/`, `rh850_test2_2/` | +| PIC32CZ | Microchip | `czhsm-client/tests/`, `czhsm-server/` | +| TDA4VH | TI | (no test files found) | +| New Eagle | Customer | (no test files found) | + +## File layout + +``` +Portable (ships in wolfHSM): + wh_test_runner.h/c - suite runner + wh_test_groups.h/c - Misc/Server/Client entry points + server/wh_test_*.c/h - server-only test modules + client-server/wh_test_*.c/h - client-server test modules + misc/wh_test_*.c/h - standalone test modules + +Platform-specific (one directory per platform, e.g. posix/): + /wh_test_helpers_misc_.h/c - misc fixtures + /wh_test_helpers_server_.h/c - server bringup + /wh_test_helpers_client_.h/c - client bringup + /wh_test_main_.c - init, group + dispatch, reset + hooks + /Makefile - build rules +``` + +## Porting to other platforms + +1. Implement the init helpers for the side(s) the target + needs. These stand in for what your firmware's normal + boot flow already does -- if it's simpler to call your + existing init code directly from main, that works too: + - `whTestHelperPosix_Server_Init/Cleanup` (reference): + bring up flash/NVM/crypto/transport/server. + - `whTestHelperPosix_Client_Init/Cleanup` (reference): + bring up client comm + handshake. On single-process + targets, the server runs in its own thread and pumps + `HandleRequestMessage` itself. +2. Provide a `main()` that: + - Calls `whTestGroup_Misc()` for standalone tests. + - Brings up the server/client contexts once. + - Calls `whTestGroup_Server(&server)` and/or + `whTestGroup_Client(&client)` with the live handles. + - Tears the contexts down. + - Implements `whTestGroup_ResetServer` and/or + `whTestGroup_ResetClient` -- called between suites to + scrub persistent state. +3. Add the portable `.c` files and your port files to your + build system. + +See `wh_test_main_posix.c` and the two `*_posix.c` helpers as +a reference implementation. + +## Build and run (POSIX) + +``` +cd posix +make run +make run DEBUG=1 +make run THREADSAFE=1 # enables stress test gate +``` diff --git a/test-refactor/client-server/wh_test_crypto.c b/test-refactor/client-server/wh_test_crypto.c new file mode 100644 index 000000000..ddcec2625 --- /dev/null +++ b/test-refactor/client-server/wh_test_crypto.c @@ -0,0 +1,178 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_crypto.c + * + * Basic crypto test suite. Minimal SHA256 and AES-CBC + * round-trips routed through the server via WH_DEV_ID. + */ + +#include "wolfhsm/wh_settings.h" + +#if !defined(WOLFHSM_CFG_NO_CRYPTO) + +#include +#include + +#include "wolfssl/wolfcrypt/settings.h" +#include "wolfssl/wolfcrypt/types.h" +#include "wolfssl/wolfcrypt/aes.h" +#include "wolfssl/wolfcrypt/sha256.h" + +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_client.h" + +#include "wh_test_common.h" +#include "wh_test_runner.h" +#include "wh_test_crypto.h" + +#ifndef NO_SHA256 +static int whTest_CryptoSha256(void* c) +{ + whClientContext* ctx = (whClientContext*)c; + int devId = WH_DEV_ID; + int ret = WH_ERROR_OK; + wc_Sha256 sha256[1]; + uint8_t out[WC_SHA256_DIGEST_SIZE]; + /* Vector exactly one block size in length */ + const char inOne[] = + "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; + const uint8_t expectedOutOne[WC_SHA256_DIGEST_SIZE] = { + 0xff, 0xe0, 0x54, 0xfe, 0x7a, 0xe0, 0xcb, 0x6d, 0xc6, 0x5c, 0x3a, + 0xf9, 0xb6, 0x1d, 0x52, 0x09, 0xf4, 0x39, 0x85, 0x1d, 0xb4, 0x3d, + 0x0b, 0xa5, 0x99, 0x73, 0x37, 0xdf, 0x15, 0x46, 0x68, 0xeb}; + + (void)ctx; + + /* Initialize SHA256 structure */ + ret = wc_InitSha256_ex(sha256, NULL, devId); + if (ret != 0) { + WH_ERROR_PRINT("Failed to wc_InitSha256 on devId 0x%X: %d\n", devId, + ret); + } else { + /* Single-block update should trigger a server transaction */ + ret = wc_Sha256Update(sha256, + (const byte*)inOne, + WC_SHA256_BLOCK_SIZE); + if (ret != 0) { + WH_ERROR_PRINT("Failed to wc_Sha256Update %d\n", ret); + } else { + /* Finalize should trigger a server transaction with empty buffer */ + ret = wc_Sha256Final(sha256, out); + if (ret != 0) { + WH_ERROR_PRINT("Failed to wc_Sha256Final %d\n", ret); + } else { + /* Compare the computed hash with the expected output */ + if (memcmp(out, expectedOutOne, WC_SHA256_DIGEST_SIZE) != 0) { + WH_ERROR_PRINT("SHA256 hash does not match expected.\n"); + ret = -1; + } + } + } + (void)wc_Sha256Free(sha256); + } + if (ret == 0) { + WH_TEST_PRINT("SHA256 DEVID=0x%X SUCCESS\n", devId); + } + return ret; +} +#endif /* !NO_SHA256 */ + + +#if !defined(NO_AES) && defined(HAVE_AES_CBC) +static int whTestCrypto_Aes(void* c) +{ + whClientContext* ctx = (whClientContext*)c; + int devId = WH_DEV_ID; + int ret = 0; + Aes aes[1]; + uint8_t cipher[AES_BLOCK_SIZE] = {0}; + uint8_t plainOut[AES_BLOCK_SIZE] = {0}; + /* NIST SP 800-38B test vectors (same k128 / m used by the CMAC test + * in test/wh_test_crypto.c). Using a fixed vector keeps this suite + * self-contained, no RNG needed. */ + const uint8_t key[] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, + 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c}; + const uint8_t iv[AES_BLOCK_SIZE] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}; + const uint8_t plainIn[AES_BLOCK_SIZE] = { + 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a}; + + (void)ctx; + + /* test aes CBC with client side key */ + ret = wc_AesInit(aes, NULL, devId); + if (ret != 0) { + WH_ERROR_PRINT("Failed to wc_AesInit %d\n", ret); + } else { + ret = wc_AesSetKey(aes, key, sizeof(key), iv, AES_ENCRYPTION); + if (ret != 0) { + WH_ERROR_PRINT("Failed to wc_AesSetKey %d\n", ret); + } else { + ret = wc_AesCbcEncrypt(aes, cipher, plainIn, + sizeof(plainIn)); + if (ret != 0) { + WH_ERROR_PRINT("Failed to wc_AesCbcEncrypt %d\n", ret); + } else { + ret = wc_AesSetKey(aes, key, sizeof(key), iv, + AES_DECRYPTION); + if (ret != 0) { + WH_ERROR_PRINT("Failed to wc_AesSetKey %d\n", ret); + } else { + ret = wc_AesCbcDecrypt(aes, plainOut, cipher, + sizeof(cipher)); + if (ret != 0) { + WH_ERROR_PRINT("Failed to wc_AesCbcDecrypt %d\n", + ret); + } else { + if (memcmp(plainIn, plainOut, sizeof(plainIn)) != + 0) { + WH_ERROR_PRINT("Failed to match AES-CBC\n"); + ret = -1; + } + } + } + } + } + (void)wc_AesFree(aes); + } + if (ret == 0) { + WH_TEST_PRINT("AES CBC DEVID=0x%X SUCCESS\n", devId); + } + return ret; +} +#endif /* !NO_AES && HAVE_AES_CBC */ + + +static whTestFn _tests[] = { +#ifndef NO_SHA256 + whTest_CryptoSha256, +#endif +#if !defined(NO_AES) && defined(HAVE_AES_CBC) + whTestCrypto_Aes, +#endif + NULL +}; + +whTestSuite whTestSuite_Crypto = + WH_TEST_SUITE("Crypto", _tests); + +#endif /* !WOLFHSM_CFG_NO_CRYPTO */ diff --git a/test-refactor/client-server/wh_test_crypto.h b/test-refactor/client-server/wh_test_crypto.h new file mode 100644 index 000000000..9bbc12e69 --- /dev/null +++ b/test-refactor/client-server/wh_test_crypto.h @@ -0,0 +1,38 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_crypto.h + * + * Basic crypto test suite (minimal SHA256 round-trip). + */ + +#ifndef WH_TEST_CRYPTO_REFACTOR_H_ +#define WH_TEST_CRYPTO_REFACTOR_H_ + +#include "wolfhsm/wh_settings.h" + +#if !defined(WOLFHSM_CFG_NO_CRYPTO) + +#include "wh_test_runner.h" + +extern whTestSuite whTestSuite_Crypto; + +#endif /* !WOLFHSM_CFG_NO_CRYPTO */ + +#endif /* WH_TEST_CRYPTO_REFACTOR_H_ */ diff --git a/test-refactor/client-server/wh_test_echo.c b/test-refactor/client-server/wh_test_echo.c new file mode 100644 index 000000000..54ba4c1fc --- /dev/null +++ b/test-refactor/client-server/wh_test_echo.c @@ -0,0 +1,80 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_echo.c + * + * Echo round-trip test. Uses blocking client APIs; the port + * is responsible for pumping the server in parallel. + */ + +#include +#include + +#include "wolfhsm/wh_settings.h" +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_client.h" + +#include "wh_test_common.h" +#include "wh_test_runner.h" +#include "wh_test_echo.h" + +#define REPEAT_COUNT 10 + + +/* + * Echo a message to the server and verify the response + * matches. Repeats several times with different payloads. + */ +static int test_echo(void* ctx) +{ + whClientContext* client = (whClientContext*)ctx; + char send_buf[WOLFHSM_CFG_COMM_DATA_LEN]; + char recv_buf[WOLFHSM_CFG_COMM_DATA_LEN]; + uint16_t send_len = 0; + uint16_t recv_len = 0; + int i; + + for (i = 0; i < REPEAT_COUNT; i++) { + send_len = snprintf(send_buf, sizeof(send_buf), + "Echo test %d", i); + + recv_len = 0; + memset(recv_buf, 0, sizeof(recv_buf)); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_Echo(client, + send_len, send_buf, + &recv_len, recv_buf)); + + WH_TEST_ASSERT_RETURN(recv_len == send_len); + WH_TEST_ASSERT_RETURN( + memcmp(recv_buf, send_buf, recv_len) == 0); + } + + return 0; +} + + +static whTestFn _tests[] = { + test_echo, + NULL +}; + +whTestSuite whTestSuite_Echo = + WH_TEST_SUITE("Echo", _tests); diff --git a/test-refactor/client-server/wh_test_echo.h b/test-refactor/client-server/wh_test_echo.h new file mode 100644 index 000000000..4d486c0f6 --- /dev/null +++ b/test-refactor/client-server/wh_test_echo.h @@ -0,0 +1,32 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_echo.h + * + * Echo round-trip test suite. + */ + +#ifndef WH_TEST_ECHO_REFACTOR_H_ +#define WH_TEST_ECHO_REFACTOR_H_ + +#include "wh_test_runner.h" + +extern whTestSuite whTestSuite_Echo; + +#endif /* WH_TEST_ECHO_REFACTOR_H_ */ diff --git a/test-refactor/client-server/wh_test_server_info.c b/test-refactor/client-server/wh_test_server_info.c new file mode 100644 index 000000000..332dbf955 --- /dev/null +++ b/test-refactor/client-server/wh_test_server_info.c @@ -0,0 +1,89 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_server_info.c + * + * Server info query test. Uses blocking client APIs; the port + * is responsible for pumping the server in parallel. + */ + +#include + +#include "wolfhsm/wh_settings.h" +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_client.h" +#include "wolfhsm/wh_message_comm.h" + +#include "wh_test_common.h" +#include "wh_test_runner.h" +#include "wh_test_server_info.h" + + +/* + * Query server info and verify the response contains + * valid data. + */ +static int test_server_info(void* ctx) +{ + whClientContext* client = (whClientContext*)ctx; + uint8_t version[WH_INFO_VERSION_LEN + 1] = {0}; + uint8_t build[WH_INFO_VERSION_LEN + 1] = {0}; + uint32_t comm_data_len = 0; + uint32_t nvm_object_count = 0; + uint32_t keycache_count = 0; + uint32_t keycache_bufsize = 0; + uint32_t keycache_bigcount = 0; + uint32_t keycache_bigbufsz = 0; + uint32_t customcb_count = 0; + uint32_t dmaaddr_count = 0; + uint32_t debug_state = 0; + uint32_t boot_state = 0; + uint32_t lifecycle_state = 0; + uint32_t nvm_state = 0; + + WH_TEST_RETURN_ON_FAIL( + wh_Client_CommInfo(client, + version, build, + &comm_data_len, + &nvm_object_count, + &keycache_count, + &keycache_bufsize, + &keycache_bigcount, + &keycache_bigbufsz, + &customcb_count, + &dmaaddr_count, + &debug_state, + &boot_state, + &lifecycle_state, + &nvm_state)); + + /* Comm data length must be nonzero */ + WH_TEST_ASSERT_RETURN(comm_data_len > 0); + + return 0; +} + + +static whTestFn _tests[] = { + test_server_info, + NULL +}; + +whTestSuite whTestSuite_ServerInfo = + WH_TEST_SUITE("ServerInfo", _tests); diff --git a/test-refactor/client-server/wh_test_server_info.h b/test-refactor/client-server/wh_test_server_info.h new file mode 100644 index 000000000..1978bac3b --- /dev/null +++ b/test-refactor/client-server/wh_test_server_info.h @@ -0,0 +1,32 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_server_info.h + * + * Server info query test suite. + */ + +#ifndef WH_TEST_SERVER_INFO_REFACTOR_H_ +#define WH_TEST_SERVER_INFO_REFACTOR_H_ + +#include "wh_test_runner.h" + +extern whTestSuite whTestSuite_ServerInfo; + +#endif /* WH_TEST_SERVER_INFO_REFACTOR_H_ */ diff --git a/test-refactor/client-server/wh_test_stress.c b/test-refactor/client-server/wh_test_stress.c new file mode 100644 index 000000000..0f36661aa --- /dev/null +++ b/test-refactor/client-server/wh_test_stress.c @@ -0,0 +1,57 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_stress.c + * + * Thread-safety stress test suite. Thin wrapper around the + * existing whTest_ThreadSafeStress() -- no changes to the + * stress test internals, just runner integration. + */ + +#include "wolfhsm/wh_settings.h" + +#if defined(WOLFHSM_CFG_THREADSAFE) \ + && defined(WOLFHSM_CFG_TEST_POSIX) \ + && defined(WOLFHSM_CFG_GLOBAL_KEYS) \ + && defined(WOLFHSM_CFG_ENABLE_CLIENT) \ + && defined(WOLFHSM_CFG_ENABLE_SERVER) \ + && !defined(WOLFHSM_CFG_NO_CRYPTO) + +#include "wh_test_posix_threadsafe_stress.h" +#include "wh_test_stress.h" + +static int test_stress(void* ctx) +{ + (void)ctx; + return whTest_ThreadSafeStress(); +} + +static whTestFn _tests[] = { + test_stress, + NULL +}; + +whTestSuite whTestSuite_Stress = { + .name = "ThreadSafe Stress", + .tests = _tests, + .setup = NULL, + .cleanup = NULL, +}; + +#endif diff --git a/test-refactor/client-server/wh_test_stress.h b/test-refactor/client-server/wh_test_stress.h new file mode 100644 index 000000000..08661a6c7 --- /dev/null +++ b/test-refactor/client-server/wh_test_stress.h @@ -0,0 +1,34 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_stress.h + * + * Thread-safety stress test suite. Wraps the existing + * whTest_ThreadSafeStress() as a single-test suite for + * runner integration. + */ + +#ifndef WH_TEST_STRESS_REFACTOR_H_ +#define WH_TEST_STRESS_REFACTOR_H_ + +#include "wh_test_runner.h" + +extern whTestSuite whTestSuite_Stress; + +#endif /* WH_TEST_STRESS_REFACTOR_H_ */ diff --git a/test-refactor/misc/wh_test_flash_ramsim.c b/test-refactor/misc/wh_test_flash_ramsim.c new file mode 100644 index 000000000..5af00bb3b --- /dev/null +++ b/test-refactor/misc/wh_test_flash_ramsim.c @@ -0,0 +1,240 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_flash_ramsim.c + * + * Flash RamSim test suite. Exercises the RAM-based flash simulator + * through init, write-lock, erase, program, verify, and blank-check + * operations. No setup/cleanup needed -- the test manages its own + * flash context internally. + */ + +#include +#include +#include + +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_flash_ramsim.h" + +#include "wh_test_common.h" +#include "wh_test_flash_ramsim.h" + +#define TEST_FLASH_SIZE (1024 * 1024) +#define TEST_SECTOR_SIZE (4096) +#define TEST_PAGE_SIZE (256) + + +static void _fillTestData(uint8_t* buf, uint32_t size, + uint32_t base) +{ + uint32_t i; + for (i = 0; i < size; i++) { + buf[i] = (uint8_t)(base + i); + } +} + + +/* + * Verify that write-lock prevents erase and program, and that + * unlock restores access. + */ +static int test_flash_write_lock(void* ctx) +{ + int ret; + whFlashRamsimCtx fctx; + static uint8_t memory[TEST_FLASH_SIZE]; + uint8_t page[TEST_PAGE_SIZE] = {0}; + whFlashRamsimCfg cfg = { + .size = TEST_FLASH_SIZE, + .sectorSize = TEST_SECTOR_SIZE, + .pageSize = TEST_PAGE_SIZE, + .erasedByte = 0xFF, + .memory = memory, + }; + + (void)ctx; + + WH_TEST_RETURN_ON_FAIL(whFlashRamsim_Init(&fctx, &cfg)); + + if (fctx.writeLocked == 1) { + WH_ERROR_PRINT("RamSim locked on init\n"); + whFlashRamsim_Cleanup(&fctx); + return WH_TEST_FAIL; + } + + /* Lock sector 0 */ + ret = whFlashRamsim_WriteLock(&fctx, 0, cfg.sectorSize); + if (ret != WH_ERROR_OK) { + WH_ERROR_PRINT("WriteLock failed: ret=%d\n", ret); + whFlashRamsim_Cleanup(&fctx); + return ret; + } + + /* Erase and program must fail while locked */ + ret = whFlashRamsim_Erase(&fctx, 0, cfg.sectorSize); + if (ret != WH_ERROR_LOCKED) { + WH_ERROR_PRINT("Erase not blocked by lock: ret=%d\n", + ret); + whFlashRamsim_Cleanup(&fctx); + return WH_TEST_FAIL; + } + + ret = whFlashRamsim_Program(&fctx, 0, TEST_PAGE_SIZE, page); + if (ret != WH_ERROR_LOCKED) { + WH_ERROR_PRINT("Program not blocked by lock: ret=%d\n", + ret); + whFlashRamsim_Cleanup(&fctx); + return WH_TEST_FAIL; + } + + /* Unlock and verify access is restored */ + ret = whFlashRamsim_WriteUnlock(&fctx, 0, cfg.sectorSize); + if (ret != WH_ERROR_OK) { + WH_ERROR_PRINT("WriteUnlock failed: ret=%d\n", ret); + whFlashRamsim_Cleanup(&fctx); + return ret; + } + + whFlashRamsim_Cleanup(&fctx); + return 0; +} + + +/* + * Erase every sector, program every page with known data, + * verify, then erase again and blank-check. + */ +static int test_flash_erase_program_verify(void* ctx) +{ + int ret; + whFlashRamsimCtx fctx; + static uint8_t memory[TEST_FLASH_SIZE]; + uint8_t testData[TEST_PAGE_SIZE]; + uint32_t sector; + whFlashRamsimCfg cfg = { + .size = TEST_FLASH_SIZE, + .sectorSize = TEST_SECTOR_SIZE, + .pageSize = TEST_PAGE_SIZE, + .erasedByte = 0xFF, + .memory = memory, + }; + + (void)ctx; + + WH_TEST_RETURN_ON_FAIL(whFlashRamsim_Init(&fctx, &cfg)); + + for (sector = 0; sector < cfg.size / cfg.sectorSize; + sector++) { + uint32_t sOff = sector * cfg.sectorSize; + uint32_t page; + + /* Erase sector */ + ret = whFlashRamsim_Erase(&fctx, sOff, + cfg.sectorSize); + if (ret != 0) { + WH_ERROR_PRINT("Erase sector %u failed: %d\n", + (unsigned)sector, ret); + whFlashRamsim_Cleanup(&fctx); + return ret; + } + + /* Blank check after erase */ + ret = whFlashRamsim_BlankCheck(&fctx, sOff, + cfg.sectorSize); + if (ret != 0) { + WH_ERROR_PRINT("BlankCheck sector %u failed: %d\n", + (unsigned)sector, ret); + whFlashRamsim_Cleanup(&fctx); + return ret; + } + + /* Program and verify each page */ + for (page = 0; + page < cfg.sectorSize / cfg.pageSize; + page++) { + uint32_t pOff = sOff + page * cfg.pageSize; + + _fillTestData(testData, cfg.pageSize, page); + + ret = whFlashRamsim_Program(&fctx, pOff, + cfg.pageSize, testData); + if (ret != 0) { + WH_ERROR_PRINT("Program page %u sector %u" + " failed: %d\n", + (unsigned)page, (unsigned)sector, ret); + whFlashRamsim_Cleanup(&fctx); + return ret; + } + + ret = whFlashRamsim_Verify(&fctx, pOff, + cfg.pageSize, testData); + if (ret != 0) { + WH_ERROR_PRINT("Verify page %u sector %u" + " failed: %d\n", + (unsigned)page, (unsigned)sector, ret); + whFlashRamsim_Cleanup(&fctx); + return ret; + } + } + + /* Sector should no longer be blank */ + ret = whFlashRamsim_BlankCheck(&fctx, sOff, + cfg.sectorSize); + if (ret != WH_ERROR_NOTBLANK) { + WH_ERROR_PRINT("Sector %u blank after program:" + " %d\n", (unsigned)sector, ret); + whFlashRamsim_Cleanup(&fctx); + return WH_TEST_FAIL; + } + + /* Erase and confirm blank */ + ret = whFlashRamsim_Erase(&fctx, sOff, + cfg.sectorSize); + if (ret != 0) { + WH_ERROR_PRINT("Re-erase sector %u failed: %d\n", + (unsigned)sector, ret); + whFlashRamsim_Cleanup(&fctx); + return ret; + } + + ret = whFlashRamsim_BlankCheck(&fctx, sOff, + cfg.sectorSize); + if (ret != 0) { + WH_ERROR_PRINT("Sector %u not blank after" + " re-erase: %d\n", (unsigned)sector, ret); + whFlashRamsim_Cleanup(&fctx); + return ret; + } + } + + whFlashRamsim_Cleanup(&fctx); + return 0; +} + + +static whTestFn _tests[] = { + test_flash_write_lock, + test_flash_erase_program_verify, + NULL +}; + +whTestSuite whTestSuite_FlashRamSim = { + .name = "Flash RamSim", + .tests = _tests, +}; diff --git a/test-refactor/misc/wh_test_flash_ramsim.h b/test-refactor/misc/wh_test_flash_ramsim.h new file mode 100644 index 000000000..c5350ab78 --- /dev/null +++ b/test-refactor/misc/wh_test_flash_ramsim.h @@ -0,0 +1,32 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_flash_ramsim.h + * + * Flash RamSim test suite for the test runner. + */ + +#ifndef WH_TEST_FLASH_RAMSIM_H_ +#define WH_TEST_FLASH_RAMSIM_H_ + +#include "wh_test_runner.h" + +extern whTestSuite whTestSuite_FlashRamSim; + +#endif /* WH_TEST_FLASH_RAMSIM_H_ */ diff --git a/test-refactor/misc/wh_test_nvm_flash.c b/test-refactor/misc/wh_test_nvm_flash.c new file mode 100644 index 000000000..6e08d9ad7 --- /dev/null +++ b/test-refactor/misc/wh_test_nvm_flash.c @@ -0,0 +1,314 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_nvm_flash.c + * + * NVM flash test suite. The fixture (flash + NVM stack) is + * owned by the caller; these tests just consume it. Exercises + * flash unit ops and NVM add/read/overwrite/destroy/reclaim + * through the callback interface. + */ + +#include +#include + +#include "wolfhsm/wh_settings.h" +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_flash.h" +#include "wolfhsm/wh_flash_ramsim.h" +#include "wolfhsm/wh_flash_unit.h" +#include "wolfhsm/wh_nvm.h" +#include "wolfhsm/wh_nvm_flash.h" + +#include "wh_test_common.h" +#include "wh_test_nvm_flash.h" + +#define NVM_FLASH_SIZE (1024 * 1024) +#define NVM_FLASH_SECTOR_SZ (4096) +#define NVM_FLASH_PAGE_SZ (8) + + +/* + * Module-private fixture. A single file-static instance holds + * all ramsim/NVM state the tests poke at; _setup populates it, + * _cleanup is a placeholder for symmetry. + */ +typedef struct { + uint8_t memory[NVM_FLASH_SIZE]; + whFlashRamsimCtx flashCtx; + whFlashRamsimCfg flashCfg; + whFlashCb flashCb; + + whNvmFlashContext nvmFlashCtx; + whNvmFlashConfig nvmFlashCfg; + whNvmCb nvmCb; +} whTestNvmFlashCtx; + +static whTestNvmFlashCtx _ctx; + + +/* + * Populate the module-private fixture. Called from the top + * of each test so every test starts against a fresh state. + */ +static void _setup(void) +{ + whTestNvmFlashCtx* c = &_ctx; + const whFlashCb initFlashCb[1] = {WH_FLASH_RAMSIM_CB}; + whNvmCb initNvmCb[1] = {WH_NVM_FLASH_CB}; + + memset(c, 0, sizeof(*c)); + + c->flashCb = initFlashCb[0]; + c->flashCfg.size = NVM_FLASH_SIZE; + c->flashCfg.sectorSize = NVM_FLASH_SECTOR_SZ; + c->flashCfg.pageSize = NVM_FLASH_PAGE_SZ; + c->flashCfg.erasedByte = 0; + c->flashCfg.memory = c->memory; + + c->nvmFlashCfg.cb = &c->flashCb; + c->nvmFlashCfg.context = &c->flashCtx; + c->nvmFlashCfg.config = &c->flashCfg; + + c->nvmCb = initNvmCb[0]; +} + + +/* ---- Flash unit operations ---- */ + +/* + * Exercises flash unit program/read/erase/blank-check + * and byte-level read/write including unaligned access. + */ +static int test_flash_unit_ops(void* ctx) +{ + whTestNvmFlashCtx* c = &_ctx; + uint8_t write_bytes[8] = { + 0xF0, 0xE1, 0xD2, 0xC3, 0xB4, 0xA5, 0x96, 0x87 + }; + uint8_t read_bytes[8] = {0}; + whFlashUnit write_buf[4] = {0}; + whFlashUnit read_buf[4] = {0}; + uint32_t partition_units = 0; + + (void)ctx; + _setup(); + + WH_TEST_RETURN_ON_FAIL( + c->flashCb.Init(&c->flashCtx, &c->flashCfg)); + + partition_units = wh_FlashUnit_Bytes2Units( + c->flashCb.PartitionSize(&c->flashCtx)); + + /* Unlock + erase + blank check */ + WH_TEST_RETURN_ON_FAIL(wh_FlashUnit_WriteUnlock( + &c->flashCb, &c->flashCtx, 0, partition_units)); + WH_TEST_RETURN_ON_FAIL(wh_FlashUnit_Erase( + &c->flashCb, &c->flashCtx, 0, partition_units)); + WH_TEST_RETURN_ON_FAIL(wh_FlashUnit_BlankCheck( + &c->flashCb, &c->flashCtx, 0, partition_units)); + + /* Program + read back at unit granularity */ + WH_TEST_RETURN_ON_FAIL(wh_FlashUnit_Program( + &c->flashCb, &c->flashCtx, 0, 1, write_buf)); + WH_TEST_RETURN_ON_FAIL(wh_FlashUnit_Program( + &c->flashCb, &c->flashCtx, 1, 2, write_buf)); + + memset(read_buf, 0, sizeof(read_buf)); + WH_TEST_RETURN_ON_FAIL(wh_FlashUnit_Read( + &c->flashCb, &c->flashCtx, 0, 1, read_buf)); + WH_TEST_ASSERT_RETURN(0 == memcmp( + write_buf, read_buf, 1 * WHFU_BYTES_PER_UNIT)); + + /* Program + read back at byte granularity */ + WH_TEST_RETURN_ON_FAIL(wh_FlashUnit_ProgramBytes( + &c->flashCb, &c->flashCtx, + 10 * WHFU_BYTES_PER_UNIT, 8, write_bytes)); + + memset(read_bytes, 0, sizeof(read_bytes)); + WH_TEST_RETURN_ON_FAIL(wh_FlashUnit_ReadBytes( + &c->flashCb, &c->flashCtx, + 10 * WHFU_BYTES_PER_UNIT, 8, read_bytes)); + WH_TEST_ASSERT_RETURN( + 0 == memcmp(write_bytes, read_bytes, 8)); + + /* Unaligned read (exercises offset_rem path) */ + { + uint8_t pattern[WHFU_BYTES_PER_UNIT * 4]; + uint8_t readback[WHFU_BYTES_PER_UNIT * 4]; + uint32_t base = 20; + uint32_t i; + + for (i = 0; i < sizeof(pattern); i++) { + pattern[i] = (uint8_t)(0x10 + i); + } + + WH_TEST_RETURN_ON_FAIL( + wh_FlashUnit_ProgramBytes( + &c->flashCb, &c->flashCtx, + base * WHFU_BYTES_PER_UNIT, + sizeof(pattern), pattern)); + + memset(readback, 0, sizeof(readback)); + WH_TEST_RETURN_ON_FAIL( + wh_FlashUnit_ReadBytes( + &c->flashCb, &c->flashCtx, + base * WHFU_BYTES_PER_UNIT + 3, + 5, readback)); + WH_TEST_ASSERT_RETURN( + 0 == memcmp(readback, &pattern[3], 5)); + + memset(readback, 0, sizeof(readback)); + WH_TEST_RETURN_ON_FAIL( + wh_FlashUnit_ReadBytes( + &c->flashCb, &c->flashCtx, + base * WHFU_BYTES_PER_UNIT + 2, + 21, readback)); + WH_TEST_ASSERT_RETURN( + 0 == memcmp(readback, &pattern[2], 21)); + } + + /* Erase + lock */ + WH_TEST_RETURN_ON_FAIL(wh_FlashUnit_Erase( + &c->flashCb, &c->flashCtx, 0, partition_units)); + WH_TEST_RETURN_ON_FAIL(wh_FlashUnit_BlankCheck( + &c->flashCb, &c->flashCtx, 0, partition_units)); + WH_TEST_RETURN_ON_FAIL(wh_FlashUnit_WriteLock( + &c->flashCb, &c->flashCtx, 0, partition_units)); + + WH_TEST_RETURN_ON_FAIL( + c->flashCb.Cleanup(&c->flashCtx)); + + return 0; +} + + +/* ---- NVM operations ---- */ + +static int _addAndCheck(const whNvmCb* cb, void* context, + whNvmMetadata* meta, whNvmSize len, const uint8_t* data) +{ + whNvmMetadata readMeta = {0}; + uint8_t readBuf[256]; + + WH_TEST_RETURN_ON_FAIL( + cb->AddObject(context, meta, len, data)); + WH_TEST_RETURN_ON_FAIL( + cb->Read(context, meta->id, 0, len, readBuf)); + WH_TEST_RETURN_ON_FAIL( + cb->GetMetadata(context, meta->id, &readMeta)); + WH_TEST_ASSERT_RETURN(meta->id == readMeta.id); + WH_TEST_ASSERT_RETURN(0 == memcmp(data, readBuf, len)); + + return 0; +} + + +/* + * Add objects, overwrite, reclaim, destroy, verify + * data integrity throughout. + */ +static int test_nvm_add_overwrite_destroy(void* ctx) +{ + whTestNvmFlashCtx* c = &_ctx; + const whNvmCb* cb = &c->nvmCb; + + (void)ctx; + _setup(); + uint8_t data1[] = "Data1"; + uint8_t data2[] = "Data2"; + uint8_t data3[] = "Data3"; + uint8_t update1[] = "Update1fdsafdasfdsafdsafdsafdsaf"; + uint8_t update2[] = "Update2fdafdafdafdsafdsafdasfd"; + whNvmId ids[] = {100, 400, 300}; + + whNvmMetadata meta1 = {.id = ids[0], .label = "L1"}; + whNvmMetadata meta2 = {.id = ids[1], .label = "L2"}; + whNvmMetadata meta3 = {.id = ids[2], .label = "L3"}; + + whNvmMetadata readMeta = {0}; + uint8_t readBuf[256]; + size_t i; + + WH_TEST_RETURN_ON_FAIL( + cb->Init(&c->nvmFlashCtx, &c->nvmFlashCfg)); + + /* Add 3 objects */ + WH_TEST_RETURN_ON_FAIL( + _addAndCheck(cb, &c->nvmFlashCtx, + &meta1, sizeof(data1), data1)); + WH_TEST_RETURN_ON_FAIL( + _addAndCheck(cb, &c->nvmFlashCtx, + &meta2, sizeof(data2), data2)); + WH_TEST_RETURN_ON_FAIL( + _addAndCheck(cb, &c->nvmFlashCtx, + &meta3, sizeof(data3), data3)); + + /* Overwrite objects */ + WH_TEST_RETURN_ON_FAIL( + _addAndCheck(cb, &c->nvmFlashCtx, + &meta1, sizeof(update1), update1)); + WH_TEST_RETURN_ON_FAIL( + _addAndCheck(cb, &c->nvmFlashCtx, + &meta2, sizeof(update2), update2)); + + /* Reclaim space */ + WH_TEST_RETURN_ON_FAIL( + cb->DestroyObjects(&c->nvmFlashCtx, 0, NULL)); + + /* Verify all objects survived reclaim */ + for (i = 0; i < sizeof(ids) / sizeof(ids[0]); i++) { + memset(&readMeta, 0, sizeof(readMeta)); + WH_TEST_RETURN_ON_FAIL(cb->GetMetadata( + &c->nvmFlashCtx, ids[i], &readMeta)); + WH_TEST_RETURN_ON_FAIL(cb->Read( + &c->nvmFlashCtx, ids[i], 0, + readMeta.len, readBuf)); + } + + /* Destroy first object, verify it's gone */ + WH_TEST_RETURN_ON_FAIL( + cb->DestroyObjects(&c->nvmFlashCtx, 1, ids)); + WH_TEST_ASSERT_RETURN( + WH_ERROR_NOTFOUND == cb->Read( + &c->nvmFlashCtx, ids[0], 0, + sizeof(readBuf), readBuf)); + + /* Destroy remaining */ + WH_TEST_RETURN_ON_FAIL( + cb->DestroyObjects(&c->nvmFlashCtx, + sizeof(ids) / sizeof(ids[0]), ids)); + + WH_TEST_RETURN_ON_FAIL( + cb->Cleanup(&c->nvmFlashCtx)); + + return 0; +} + + +static whTestFn _tests[] = { + test_flash_unit_ops, + test_nvm_add_overwrite_destroy, + NULL +}; + +whTestSuite whTestSuite_NvmFlash = { + .name = "NVM Flash", + .tests = _tests, +}; diff --git a/test-refactor/misc/wh_test_nvm_flash.h b/test-refactor/misc/wh_test_nvm_flash.h new file mode 100644 index 000000000..c45be825a --- /dev/null +++ b/test-refactor/misc/wh_test_nvm_flash.h @@ -0,0 +1,33 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_nvm_flash.h + * + * NVM flash test suite. Standalone -- owns a ramsim-backed + * flash + NVM stack internally. Callers just invoke Run. + */ + +#ifndef WH_TEST_NVM_FLASH_REFACTOR_H_ +#define WH_TEST_NVM_FLASH_REFACTOR_H_ + +#include "wh_test_runner.h" + +extern whTestSuite whTestSuite_NvmFlash; + +#endif /* WH_TEST_NVM_FLASH_REFACTOR_H_ */ diff --git a/test-refactor/posix/Makefile b/test-refactor/posix/Makefile new file mode 100644 index 000000000..fffc61a2f --- /dev/null +++ b/test-refactor/posix/Makefile @@ -0,0 +1,142 @@ +## Makefile for wolfHSM test-refactor POSIX port +## Mirrors test/Makefile conventions with minimal sources + +## Project name +BIN = wh_test_refactor + +## Important directories +PROJECT_DIR ?= . +REFACTOR_DIR ?= $(PROJECT_DIR)/.. +CONFIG_DIR ?= $(REFACTOR_DIR)/../test/config +WOLFSSL_DIR ?= ../../../wolfssl +WOLFHSM_DIR ?= $(REFACTOR_DIR)/.. +WOLFHSM_PORT_DIR ?= $(WOLFHSM_DIR)/port/posix +TEST_DIR ?= $(WOLFHSM_DIR)/test + +BUILD_DIR ?= $(PROJECT_DIR)/Build + +# Includes +INC = -I$(PROJECT_DIR) \ + -I$(REFACTOR_DIR) \ + -I$(REFACTOR_DIR)/server \ + -I$(REFACTOR_DIR)/client-server \ + -I$(REFACTOR_DIR)/misc \ + -I$(CONFIG_DIR) \ + -I$(TEST_DIR) \ + -I$(WOLFSSL_DIR) \ + -I$(WOLFHSM_DIR) \ + -I$(WOLFHSM_PORT_DIR) + +# POSIX requires C source be defined before any header +DEF += -D_POSIX_C_SOURCE=200809L + +# Library configuration defines for user-supplied settings +DEF += -DWOLFSSL_USER_SETTINGS -DWOLFHSM_CFG + +# Enable POSIX test features and server +DEF += -DWOLFHSM_CFG_TEST_POSIX +DEF += -DWOLFHSM_CFG_ENABLE_CLIENT +DEF += -DWOLFHSM_CFG_ENABLE_SERVER + +# C standard +CSTD ?= -std=c90 + +# Compiler flags +CFLAGS_EXTRA = -Werror -Wall -Wextra +CFLAGS_EXTRA += -ffunction-sections -fdata-sections +CFLAGS_EXTRA += -MMD -MP + +ARCHFLAGS ?= +CFLAGS ?= $(ARCHFLAGS) $(CSTD) $(CFLAGS_EXTRA) +LDFLAGS ?= $(ARCHFLAGS) + +# Dead-strip unused sections +OS_NAME := $(shell uname -s | tr A-Z a-z) +ifeq ($(OS_NAME),darwin) + LDFLAGS += -Wl,-dead_strip +else + LDFLAGS += -Wl,--gc-sections +endif + +## Optional flags (same as test/Makefile) + +ifeq ($(DEBUG),1) + DBGFLAGS = -ggdb -g3 + CFLAGS += $(DBGFLAGS) + LDFLAGS += $(DBGFLAGS) + DEF += -DWOLFHSM_CFG_DEBUG +endif + +ifeq ($(DEBUG_VERBOSE),1) + DBGFLAGS = -ggdb -g3 + CFLAGS += $(DBGFLAGS) + LDFLAGS += $(DBGFLAGS) + DEF += -DWOLFHSM_CFG_DEBUG -DWOLFHSM_CFG_DEBUG_VERBOSE +endif + +ifeq ($(ASAN),1) + CFLAGS += -fsanitize=address + LDFLAGS += -fsanitize=address +endif + +## Source files + +# wolfCrypt +SRC_C += $(wildcard $(WOLFSSL_DIR)/wolfcrypt/src/*.c) + +# wolfSSL TLS (needed by cert manager APIs) +SRC_C += $(wildcard $(WOLFSSL_DIR)/src/*.c) + +# wolfHSM library +SRC_C += $(wildcard $(WOLFHSM_DIR)/src/*.c) + +# POSIX port (timestamps, flash file, etc.) +SRC_C += $(wildcard $(WOLFHSM_PORT_DIR)/*.c) + +# Portable test-refactor sources (runner, groups) +SRC_C += $(wildcard $(REFACTOR_DIR)/*.c) + +# Grouped test sources +SRC_C += $(wildcard $(REFACTOR_DIR)/server/*.c) +SRC_C += $(wildcard $(REFACTOR_DIR)/client-server/*.c) +SRC_C += $(wildcard $(REFACTOR_DIR)/misc/*.c) + +# POSIX-specific sources (main, helpers) +SRC_C += $(wildcard $(PROJECT_DIR)/*.c) + + +## Build rules + +FILENAMES_C = $(notdir $(SRC_C)) +OBJS_C = $(addprefix $(BUILD_DIR)/, $(FILENAMES_C:.c=.o)) +vpath %.c $(dir $(SRC_C)) + +.PHONY: build_app clean run + +build_app: $(BUILD_DIR) $(BUILD_DIR)/$(BIN).elf + @echo Build complete. + +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + +# Workaround: pre-existing warnings in upstream files that +# -Werror promotes to errors. +$(BUILD_DIR)/internal.o: CFLAGS += -Wno-error=implicit-function-declaration +$(BUILD_DIR)/wh_client_crypto.o: CFLAGS += -Wno-error=sign-compare + +$(BUILD_DIR)/%.o: %.c + @echo "Compiling: $(notdir $<)" + $(CC) $(CFLAGS) $(DEF) $(INC) -c -o $@ $< + +-include $(OBJS_C:.o=.d) + +$(BUILD_DIR)/$(BIN).elf: $(OBJS_C) + @echo "Linking: $(notdir $@)" + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + +clean: + @echo "Cleaning build files" + @rm -rf $(BUILD_DIR) + +run: build_app + $(BUILD_DIR)/$(BIN).elf diff --git a/test-refactor/posix/wh_test_helpers_client_posix.c b/test-refactor/posix/wh_test_helpers_client_posix.c new file mode 100644 index 000000000..791791876 --- /dev/null +++ b/test-refactor/posix/wh_test_helpers_client_posix.c @@ -0,0 +1,94 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_helpers_client_posix.c + * + * POSIX client-side init. Wires the client onto the shared + * mem-transport buffers published by the server helper; the + * port's server thread is responsible for pumping requests. + */ + +#include +#include + +#include "wolfhsm/wh_settings.h" +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_transport_mem.h" +#include "wolfhsm/wh_comm.h" +#include "wolfhsm/wh_client.h" + +#include "wh_test_common.h" +#include "wh_test_helpers_server_posix.h" +#include "wh_test_helpers_client_posix.h" + + +/* Client-side transport state (buffers are shared with the + * server via whTestHelperPosix_Server_GetTransportConfig) */ +static whTransportMemClientContext _tmClientCtx; +static whCommClientConfig _commCfg; + +static const whTransportClientCb _tcCb = { + .Init = wh_TransportMem_InitClear, + .Send = wh_TransportMem_SendRequest, + .Recv = wh_TransportMem_RecvResponse, + .Cleanup = wh_TransportMem_Cleanup, +}; + + +int whTestHelperPosix_Client_Init(whClientContext* client) +{ + whClientConfig cCfg; + whTransportMemConfig* tmCfg; + uint32_t clientId = 0; + uint32_t serverId = 0; + + if (client == NULL) { + return WH_ERROR_BADARGS; + } + + tmCfg = whTestHelperPosix_Server_GetTransportConfig(); + if (tmCfg == NULL) { + return WH_ERROR_BADARGS; + } + + memset(&_commCfg, 0, sizeof(_commCfg)); + _commCfg.transport_cb = &_tcCb; + _commCfg.transport_context = (void*)&_tmClientCtx; + _commCfg.transport_config = (void*)tmCfg; + _commCfg.client_id = 1; + + memset(&cCfg, 0, sizeof(cCfg)); + cCfg.comm = &_commCfg; + + WH_TEST_RETURN_ON_FAIL(wh_Client_Init(client, &cCfg)); + WH_TEST_RETURN_ON_FAIL( + wh_Client_CommInit(client, &clientId, &serverId)); + + return 0; +} + + +int whTestHelperPosix_Client_Cleanup(whClientContext* client) +{ + if (client == NULL) { + return 0; + } + wh_Client_Cleanup(client); + return 0; +} diff --git a/test-refactor/posix/wh_test_helpers_client_posix.h b/test-refactor/posix/wh_test_helpers_client_posix.h new file mode 100644 index 000000000..1f437c05d --- /dev/null +++ b/test-refactor/posix/wh_test_helpers_client_posix.h @@ -0,0 +1,47 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_helpers_client_posix.h + * + * POSIX client-side init helper. Stands in for the normal + * boot-time transport+client init a real firmware would do. + * Wires the client onto the mem-transport buffers exposed by + * the server helper and performs the CommInit handshake. The + * port's main is responsible for running a server thread that + * pumps HandleRequestMessage -- this helper does not touch the + * server context. + */ + +#ifndef WH_TEST_HELPERS_CLIENT_POSIX_H_ +#define WH_TEST_HELPERS_CLIENT_POSIX_H_ + +#include "wolfhsm/wh_client.h" + +/* + * Initialize the client context plus transport state and + * perform the CommInit handshake. The server helper must have + * been initialized first (its transport config supplies the + * shared request/response buffers) and a server thread must be + * actively processing requests when this is called. + */ +int whTestHelperPosix_Client_Init(whClientContext* client); + +int whTestHelperPosix_Client_Cleanup(whClientContext* client); + +#endif /* WH_TEST_HELPERS_CLIENT_POSIX_H_ */ diff --git a/test-refactor/posix/wh_test_helpers_server_posix.c b/test-refactor/posix/wh_test_helpers_server_posix.c new file mode 100644 index 000000000..f76d0c80f --- /dev/null +++ b/test-refactor/posix/wh_test_helpers_server_posix.c @@ -0,0 +1,163 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_helpers_server_posix.c + * + * POSIX server-side init. Stand-in for what a real firmware's + * boot flow would do: configure flash/NVM, init crypto, wire + * up a transport, and bring up a server context. + */ + +#include +#include + +#include "wolfhsm/wh_settings.h" +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_flash.h" +#include "wolfhsm/wh_flash_ramsim.h" +#include "wolfhsm/wh_nvm.h" +#include "wolfhsm/wh_nvm_flash.h" +#include "wolfhsm/wh_transport_mem.h" +#include "wolfhsm/wh_comm.h" +#include "wolfhsm/wh_server.h" + +#ifndef WOLFHSM_CFG_NO_CRYPTO +#include "wolfssl/wolfcrypt/settings.h" +#include "wolfssl/wolfcrypt/random.h" +#endif + +#include "wh_test_common.h" +#include "wh_test_helpers_server_posix.h" + +#define POSIX_FLASH_SIZE (1024 * 1024) +#define POSIX_FLASH_SECTOR_SZ (128 * 1024) +#define POSIX_FLASH_PAGE_SZ (8) +#define POSIX_TRANSPORT_BUF_SZ (4096) + + +/* Flash (ramsim backing) */ +static uint8_t _flashMem[POSIX_FLASH_SIZE]; +static whFlashRamsimCtx _flashCtx; +static whFlashRamsimCfg _flashCfg; +static const whFlashCb _flashCb = WH_FLASH_RAMSIM_CB; + +/* NVM wrapping the flash */ +static whNvmFlashContext _nvmFlashCtx; +static whNvmFlashConfig _nvmFlashCfg; +static whNvmCb _nvmCb = WH_NVM_FLASH_CB; +static whNvmContext _nvm; + +#ifndef WOLFHSM_CFG_NO_CRYPTO +static whServerCryptoContext _crypto; +#endif + +/* Mem transport -- buffers and server-side state. + * The client helper re-uses these buffers via + * whTestHelperPosix_Server_GetTransportConfig. */ +static uint8_t _req[POSIX_TRANSPORT_BUF_SZ]; +static uint8_t _resp[POSIX_TRANSPORT_BUF_SZ]; +static whTransportMemConfig _tmCfg; +static whTransportMemServerContext _tmServerCtx; +static const whTransportServerCb _tsCb = WH_TRANSPORT_MEM_SERVER_CB; +static whCommServerConfig _commCfg; + + +int whTestHelperPosix_Server_Init(whServerContext* server) +{ + whNvmConfig nvmCfg; + whServerConfig sCfg; + + if (server == NULL) { + return WH_ERROR_BADARGS; + } + + /* Flash backend */ + memset(&_flashCtx, 0, sizeof(_flashCtx)); + memset(&_flashCfg, 0, sizeof(_flashCfg)); + _flashCfg.size = POSIX_FLASH_SIZE; + _flashCfg.sectorSize = POSIX_FLASH_SECTOR_SZ; + _flashCfg.pageSize = POSIX_FLASH_PAGE_SZ; + _flashCfg.erasedByte = 0xFF; + _flashCfg.memory = _flashMem; + + /* Transport */ + memset(&_tmCfg, 0, sizeof(_tmCfg)); + _tmCfg.req = (whTransportMemCsr*)_req; + _tmCfg.req_size = sizeof(_req); + _tmCfg.resp = (whTransportMemCsr*)_resp; + _tmCfg.resp_size = sizeof(_resp); + + memset(&_commCfg, 0, sizeof(_commCfg)); + _commCfg.transport_cb = &_tsCb; + _commCfg.transport_context = (void*)&_tmServerCtx; + _commCfg.transport_config = (void*)&_tmCfg; + _commCfg.server_id = 1; + + /* NVM -- flash ctx/cfg/cb wired by pointer */ + memset(&_nvmFlashCfg, 0, sizeof(_nvmFlashCfg)); + _nvmFlashCfg.cb = &_flashCb; + _nvmFlashCfg.context = &_flashCtx; + _nvmFlashCfg.config = &_flashCfg; + + memset(&nvmCfg, 0, sizeof(nvmCfg)); + nvmCfg.cb = &_nvmCb; + nvmCfg.context = &_nvmFlashCtx; + nvmCfg.config = &_nvmFlashCfg; + + WH_TEST_RETURN_ON_FAIL(wh_Nvm_Init(&_nvm, &nvmCfg)); + +#ifndef WOLFHSM_CFG_NO_CRYPTO + WH_TEST_RETURN_ON_FAIL(wolfCrypt_Init()); + WH_TEST_RETURN_ON_FAIL( + wc_InitRng_ex(_crypto.rng, NULL, INVALID_DEVID)); +#endif + + memset(&sCfg, 0, sizeof(sCfg)); + sCfg.comm_config = &_commCfg; + sCfg.nvm = &_nvm; +#ifndef WOLFHSM_CFG_NO_CRYPTO + sCfg.crypto = &_crypto; +#endif + + return wh_Server_Init(server, &sCfg); +} + + +int whTestHelperPosix_Server_Cleanup(whServerContext* server) +{ + if (server == NULL) { + return 0; + } + + wh_Server_Cleanup(server); + wh_Nvm_Cleanup(&_nvm); + +#ifndef WOLFHSM_CFG_NO_CRYPTO + wc_FreeRng(_crypto.rng); + wolfCrypt_Cleanup(); +#endif + + return 0; +} + + +whTransportMemConfig* whTestHelperPosix_Server_GetTransportConfig(void) +{ + return &_tmCfg; +} diff --git a/test-refactor/posix/wh_test_helpers_server_posix.h b/test-refactor/posix/wh_test_helpers_server_posix.h new file mode 100644 index 000000000..a494d9648 --- /dev/null +++ b/test-refactor/posix/wh_test_helpers_server_posix.h @@ -0,0 +1,58 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_helpers_server_posix.h + * + * POSIX server-side init helper. Allocates the NVM, crypto, + * transport, and server backing state the test main needs, + * and wires them into a whServerContext. A real target would + * do this in its normal boot flow; this file stands in for + * that flow in the POSIX test harness. + * + * Also exposes the shared mem-transport config so the POSIX + * client helper can wire its side onto the same + * request/response buffers. + */ + +#ifndef WH_TEST_HELPERS_SERVER_POSIX_H_ +#define WH_TEST_HELPERS_SERVER_POSIX_H_ + +#include "wolfhsm/wh_server.h" +#include "wolfhsm/wh_transport_mem.h" + +/* + * Initialize the server context plus all backing state + * (flash, NVM, crypto, transport). Caller owns `server`. + */ +int whTestHelperPosix_Server_Init(whServerContext* server); + +/* + * Tear down the server context plus backing state. Matches + * Server_Init one-for-one. + */ +int whTestHelperPosix_Server_Cleanup(whServerContext* server); + +/* + * Returns the shared mem-transport config (buffers + sizes). + * Used by the POSIX client helper to wire its side onto the + * same buffers the server publishes through. + */ +whTransportMemConfig* whTestHelperPosix_Server_GetTransportConfig(void); + +#endif /* WH_TEST_HELPERS_SERVER_POSIX_H_ */ diff --git a/test-refactor/posix/wh_test_main_posix.c b/test-refactor/posix/wh_test_main_posix.c new file mode 100644 index 000000000..894e64c2f --- /dev/null +++ b/test-refactor/posix/wh_test_main_posix.c @@ -0,0 +1,241 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_main_posix.c + * + * POSIX threaded test driver. Runs the misc group inline, then + * spawns a server thread and a client thread. The server thread + * runs the server-only group first (no client is active yet), + * then marks itself connected, signals the client thread, and + * enters a request-handling loop. The client thread waits for + * that signal, runs the client-only group against the live + * server, and calls CommClose -- the server processes the close + * message, drops its connected state, and the loop exits on the + * next iteration. Suite dispatch is delegated to the portable + * group entry points in wh_test_groups.c; this file also + * implements the reset hooks they invoke between suites. + */ + +#include + +#include "wolfhsm/wh_settings.h" +#include "wolfhsm/wh_client.h" +#include "wolfhsm/wh_comm.h" +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_server.h" + +#include "wh_test_common.h" +#include "wh_test_groups.h" + +#include "wh_test_helpers_client_posix.h" +#include "wh_test_helpers_server_posix.h" + + +/* + * Port-owned contexts. The thread functions fill these in and + * hand them to the group functions, paralleling the firmware + * pattern where these handles come from the normal init flow. + */ +static whServerContext _server; +static whClientContext _client; + +/* + * Server-ready gate. The client thread blocks on this until + * the server finishes its group and enters the request loop, + * so the client never issues a request against a server that + * isn't listening yet. + */ +static pthread_mutex_t _readyMtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t _readyCond = PTHREAD_COND_INITIALIZER; +static int _serverReady = 0; + +/* + * Per-thread return codes. main collects these after join so a + * failure in either thread propagates to the process exit + * status. + */ +static int _serverRc = 0; +static int _clientRc = 0; + + +static void _signalServerReady(void) +{ + pthread_mutex_lock(&_readyMtx); + _serverReady = 1; + pthread_cond_broadcast(&_readyCond); + pthread_mutex_unlock(&_readyMtx); +} + + +static void _waitServerReady(void) +{ + pthread_mutex_lock(&_readyMtx); + while (_serverReady == 0) { + pthread_cond_wait(&_readyCond, &_readyMtx); + } + pthread_mutex_unlock(&_readyMtx); +} + + +static void* _serverThread(void* arg) +{ + whCommConnected connected = WH_COMM_CONNECTED; + int rc; + + (void)arg; + + rc = whTestHelperPosix_Server_Init(&_server); + if (rc != 0) { + _serverRc = rc; + /* Release the client thread so it doesn't stall on the + * ready gate waiting for a server that never came up. */ + _signalServerReady(); + return NULL; + } + + rc = whTestGroup_Server(&_server); + if (rc != 0) { + _serverRc = rc; + (void)whTestHelperPosix_Server_Cleanup(&_server); + _signalServerReady(); + return NULL; + } + + /* Mark connected before signaling -- otherwise the client's + * CommInit request can arrive before the server is willing + * to handle it and HandleRequestMessage short-circuits to + * NOTREADY. */ + rc = wh_Server_SetConnected(&_server, WH_COMM_CONNECTED); + if (rc != 0) { + _serverRc = rc; + (void)whTestHelperPosix_Server_Cleanup(&_server); + _signalServerReady(); + return NULL; + } + + _signalServerReady(); + + /* Handle requests until the server's CommClose handler flips + * us to DISCONNECTED in response to the client's CommClose + * message. */ + while (1) { + rc = wh_Server_HandleRequestMessage(&_server); + if (rc != WH_ERROR_OK && rc != WH_ERROR_NOTREADY) { + _serverRc = rc; + break; + } + (void)wh_Server_GetConnected(&_server, &connected); + if (connected == WH_COMM_DISCONNECTED) { + break; + } + } + + (void)whTestHelperPosix_Server_Cleanup(&_server); + return NULL; +} + + +static void* _clientThread(void* arg) +{ + int rc; + + (void)arg; + + _waitServerReady(); + + /* Server init or the server-only group may have failed. + * Don't try to talk to a server that never entered the + * request loop; propagate the server's error instead. */ + if (_serverRc != 0) { + _clientRc = _serverRc; + return NULL; + } + + rc = whTestHelperPosix_Client_Init(&_client); + if (rc != 0) { + _clientRc = rc; + return NULL; + } + + rc = whTestGroup_Client(&_client); + if (rc != 0) { + _clientRc = rc; + } + + /* CommClose triggers the server-side SetConnected(DISCONNECTED) + * inside HandleRequestMessage, which is what lets the server + * thread exit its loop. Always attempt it so the server can + * shut down even when a test failed. */ + (void)wh_Client_CommClose(&_client); + (void)whTestHelperPosix_Client_Cleanup(&_client); + return NULL; +} + + +int main(void) +{ + pthread_t sthread; + pthread_t cthread; + int rc; + + WH_TEST_RETURN_ON_FAIL(whTestGroup_Misc()); + + rc = pthread_create(&sthread, NULL, _serverThread, NULL); + if (rc != 0) { + return rc; + } + + rc = pthread_create(&cthread, NULL, _clientThread, NULL); + if (rc != 0) { + /* Drop the server out of its loop so the best-effort + * join below doesn't block forever. */ + (void)wh_Server_SetConnected(&_server, + WH_COMM_DISCONNECTED); + (void)pthread_join(sthread, NULL); + return rc; + } + + (void)pthread_join(cthread, NULL); + (void)pthread_join(sthread, NULL); + + if (_serverRc != 0) { + return _serverRc; + } + return _clientRc; +} + + +/* + * Reset hooks invoked by the group functions between suites. + * Placeholder implementations -- once suites drop their own + * setup/cleanup and run against the live contexts, these get + * filled in to scrub persistent state (key cache, NVM, etc.). + */ +int whTestGroup_ResetServer(whServerContext* server) +{ + (void)server; + return 0; +} + + +int whTestGroup_ResetClient(whClientContext* client) +{ + (void)client; + return 0; +} diff --git a/test-refactor/server/wh_test_cert.c b/test-refactor/server/wh_test_cert.c new file mode 100644 index 000000000..0c0ee8a41 --- /dev/null +++ b/test-refactor/server/wh_test_cert.c @@ -0,0 +1,128 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_cert.c + * + * Server-side certificate test suite. Exercises the cert + * manager through direct server API calls. Uses the shared + * server helper for setup/cleanup. + */ + +#include "wolfhsm/wh_settings.h" + +#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) \ + && !defined(WOLFHSM_CFG_NO_CRYPTO) + +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_server.h" +#include "wolfhsm/wh_server_cert.h" + +#include "wh_test_common.h" +#include "wh_test_runner.h" +#include "wh_test_cert.h" +#include "wh_test_cert_data.h" + + +/* + * Add trusted roots, verify valid and invalid certs/chains, + * then remove roots. + */ +static int test_cert_verify(void* ctx) +{ + whServerContext* server = (whServerContext*)ctx; + const whNvmId rootA = 1; + const whNvmId rootB = 2; + + WH_TEST_RETURN_ON_FAIL(wh_Server_CertInit(server)); + + /* Add trusted roots */ + WH_TEST_RETURN_ON_FAIL(wh_Server_CertAddTrusted( + server, rootA, WH_NVM_ACCESS_ANY, + WH_NVM_FLAGS_NONMODIFIABLE, + NULL, 0, ROOT_A_CERT, ROOT_A_CERT_len)); + + WH_TEST_RETURN_ON_FAIL(wh_Server_CertAddTrusted( + server, rootB, WH_NVM_ACCESS_ANY, + WH_NVM_FLAGS_NONMODIFIABLE, + NULL, 0, ROOT_B_CERT, ROOT_B_CERT_len)); + + /* Valid single cert (intermediate against its root) */ + WH_TEST_RETURN_ON_FAIL(wh_Server_CertVerify( + server, INTERMEDIATE_A_CERT, INTERMEDIATE_A_CERT_len, + rootA, WH_CERT_FLAGS_NONE, + WH_NVM_FLAGS_USAGE_ANY, NULL)); + + /* Invalid: leaf without intermediate -- must fail */ + WH_TEST_ASSERT_RETURN( + WH_ERROR_CERT_VERIFY == wh_Server_CertVerify( + server, LEAF_A_CERT, LEAF_A_CERT_len, + rootA, WH_CERT_FLAGS_NONE, + WH_NVM_FLAGS_USAGE_ANY, NULL)); + + /* Invalid: intermediate against wrong root */ + WH_TEST_ASSERT_RETURN( + WH_ERROR_CERT_VERIFY == wh_Server_CertVerify( + server, INTERMEDIATE_B_CERT, + INTERMEDIATE_B_CERT_len, + rootA, WH_CERT_FLAGS_NONE, + WH_NVM_FLAGS_USAGE_ANY, NULL)); + + /* Valid chains */ + WH_TEST_RETURN_ON_FAIL(wh_Server_CertVerify( + server, RAW_CERT_CHAIN_A, RAW_CERT_CHAIN_A_len, + rootA, WH_CERT_FLAGS_NONE, + WH_NVM_FLAGS_USAGE_ANY, NULL)); + + WH_TEST_RETURN_ON_FAIL(wh_Server_CertVerify( + server, RAW_CERT_CHAIN_B, RAW_CERT_CHAIN_B_len, + rootB, WH_CERT_FLAGS_NONE, + WH_NVM_FLAGS_USAGE_ANY, NULL)); + + /* Cross-chain: must fail */ + WH_TEST_ASSERT_RETURN( + WH_ERROR_CERT_VERIFY == wh_Server_CertVerify( + server, RAW_CERT_CHAIN_A, RAW_CERT_CHAIN_A_len, + rootB, WH_CERT_FLAGS_NONE, + WH_NVM_FLAGS_USAGE_ANY, NULL)); + + WH_TEST_ASSERT_RETURN( + WH_ERROR_CERT_VERIFY == wh_Server_CertVerify( + server, RAW_CERT_CHAIN_B, RAW_CERT_CHAIN_B_len, + rootA, WH_CERT_FLAGS_NONE, + WH_NVM_FLAGS_USAGE_ANY, NULL)); + + /* Remove trusted roots */ + WH_TEST_RETURN_ON_FAIL( + wh_Server_CertEraseTrusted(server, rootA)); + WH_TEST_RETURN_ON_FAIL( + wh_Server_CertEraseTrusted(server, rootB)); + + return 0; +} + + +static whTestFn _tests[] = { + test_cert_verify, + NULL +}; + +whTestSuite whTestSuite_Cert = + WH_TEST_SUITE("Cert (Server)", _tests); + +#endif diff --git a/test-refactor/server/wh_test_cert.h b/test-refactor/server/wh_test_cert.h new file mode 100644 index 000000000..c0ca4790f --- /dev/null +++ b/test-refactor/server/wh_test_cert.h @@ -0,0 +1,32 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_cert.h + * + * Server-side certificate test suite for the test runner. + */ + +#ifndef WH_TEST_CERT_REFACTOR_H_ +#define WH_TEST_CERT_REFACTOR_H_ + +#include "wh_test_runner.h" + +extern whTestSuite whTestSuite_Cert; + +#endif /* WH_TEST_CERT_REFACTOR_H_ */ diff --git a/test-refactor/wh_test_groups.c b/test-refactor/wh_test_groups.c new file mode 100644 index 000000000..7722e3104 --- /dev/null +++ b/test-refactor/wh_test_groups.c @@ -0,0 +1,105 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_groups.c + * + * Portable group entry points. Each function runs every + * suite that belongs to its group, gated by the applicable + * compile-time config flags. Ports call these from main. + */ + +#include "wolfhsm/wh_settings.h" + +#include "wh_test_common.h" +#include "wh_test_runner.h" +#include "wh_test_groups.h" + +/* Misc group */ +#include "wh_test_flash_ramsim.h" +#include "wh_test_nvm_flash.h" + +/* Server group */ +#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) \ + && !defined(WOLFHSM_CFG_NO_CRYPTO) +#include "wh_test_cert.h" +#endif + +/* Client group */ +#include "wh_test_echo.h" +#include "wh_test_server_info.h" + +#if !defined(WOLFHSM_CFG_NO_CRYPTO) +#include "wh_test_crypto.h" +#endif + +#if defined(WOLFHSM_CFG_THREADSAFE) \ + && defined(WOLFHSM_CFG_TEST_POSIX) \ + && defined(WOLFHSM_CFG_GLOBAL_KEYS) \ + && defined(WOLFHSM_CFG_ENABLE_CLIENT) \ + && defined(WOLFHSM_CFG_ENABLE_SERVER) \ + && !defined(WOLFHSM_CFG_NO_CRYPTO) +#include "wh_test_stress.h" +#endif + + +int whTestGroup_Misc(void) +{ + WH_TEST_SUITE_RUN(&whTestSuite_FlashRamSim, NULL); + WH_TEST_SUITE_RUN(&whTestSuite_NvmFlash, NULL); + return 0; +} + + +int whTestGroup_Server(whServerContext* server) +{ +#if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) \ + && !defined(WOLFHSM_CFG_NO_CRYPTO) + WH_TEST_SUITE_RUN(&whTestSuite_Cert, server); + WH_TEST_RETURN_ON_FAIL(whTestGroup_ResetServer(server)); +#else + (void)server; +#endif + return 0; +} + + +int whTestGroup_Client(whClientContext* client) +{ + WH_TEST_SUITE_RUN(&whTestSuite_Echo, client); + WH_TEST_RETURN_ON_FAIL(whTestGroup_ResetClient(client)); + + WH_TEST_SUITE_RUN(&whTestSuite_ServerInfo, client); + WH_TEST_RETURN_ON_FAIL(whTestGroup_ResetClient(client)); + +#if !defined(WOLFHSM_CFG_NO_CRYPTO) + WH_TEST_SUITE_RUN(&whTestSuite_Crypto, client); + WH_TEST_RETURN_ON_FAIL(whTestGroup_ResetClient(client)); +#endif + +#if defined(WOLFHSM_CFG_THREADSAFE) \ + && defined(WOLFHSM_CFG_TEST_POSIX) \ + && defined(WOLFHSM_CFG_GLOBAL_KEYS) \ + && defined(WOLFHSM_CFG_ENABLE_CLIENT) \ + && defined(WOLFHSM_CFG_ENABLE_SERVER) \ + && !defined(WOLFHSM_CFG_NO_CRYPTO) + WH_TEST_SUITE_RUN(&whTestSuite_Stress, client); + WH_TEST_RETURN_ON_FAIL(whTestGroup_ResetClient(client)); +#endif + return 0; +} diff --git a/test-refactor/wh_test_groups.h b/test-refactor/wh_test_groups.h new file mode 100644 index 000000000..86d93db89 --- /dev/null +++ b/test-refactor/wh_test_groups.h @@ -0,0 +1,61 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_groups.h + * + * Portable entry points for the three test groups. The port's + * main() owns the client/server contexts and hands them to the + * group functions, which run every suite that belongs to the + * group (gated by the applicable compile-time config flags). + * + * - Misc: standalone suites, no client or server needed + * - Server: server-side suites; takes a whServerContext* + * - Client: client-side suites; takes a whClientContext* + * (the server must already be running -- on + * single-process ports the port sets it up before + * calling into this group) + * + * A client-only port calls Client (and optionally Misc). + * A server-only port calls Server (and optionally Misc). + * A combined port calls all three. + */ + +#ifndef WH_TEST_GROUPS_H_ +#define WH_TEST_GROUPS_H_ + +#include "wolfhsm/wh_client.h" +#include "wolfhsm/wh_server.h" + + +int whTestGroup_Misc(void); +int whTestGroup_Server(whServerContext* server); +int whTestGroup_Client(whClientContext* client); + + +/* + * Caller-implemented reset hooks. The group functions invoke + * these between suites so the caller can scrub any persistent + * state (NVM objects, key cache, connection state, ...) that + * one suite may have mutated before the next one runs. The + * caller owns the context, so only it knows how to reset it. + */ +int whTestGroup_ResetServer(whServerContext* server); +int whTestGroup_ResetClient(whClientContext* client); + +#endif /* WH_TEST_GROUPS_H_ */ diff --git a/test-refactor/wh_test_runner.c b/test-refactor/wh_test_runner.c new file mode 100644 index 000000000..a64a57bcf --- /dev/null +++ b/test-refactor/wh_test_runner.c @@ -0,0 +1,64 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_runner.c + * + * Test runner implementation. See wh_test_runner.h for API docs. + */ + +#include +#include + +#include "wh_test_runner.h" + +/* + * Allow the build to redirect output for embedded targets that + * lack stdout. Define WH_TEST_RUNNER_PRINTF before including + * this file to override. + */ +#ifndef WH_TEST_RUNNER_PRINTF +#define WH_TEST_RUNNER_PRINTF printf +#endif + + +int whTestRunner_Run(const whTestSuite* suite, void* ctx) +{ + int ret = 0; + int i = 0; + + if (suite == NULL || suite->tests == NULL) { + return -1; + } + + WH_TEST_RUNNER_PRINTF("[SUITE] %s\n", + suite->name != NULL ? suite->name : "(unnamed)"); + + for (i = 0; suite->tests[i] != NULL; i++) { + ret = suite->tests[i](ctx); + if (ret != 0) { + WH_TEST_RUNNER_PRINTF("[SUITE] %s: test %d FAILED" + " (%d)\n", suite->name, i, ret); + return ret; + } + } + + WH_TEST_RUNNER_PRINTF("[SUITE] %s: %d test(s) passed\n", + suite->name, i); + return 0; +} diff --git a/test-refactor/wh_test_runner.h b/test-refactor/wh_test_runner.h new file mode 100644 index 000000000..95474a400 --- /dev/null +++ b/test-refactor/wh_test_runner.h @@ -0,0 +1,75 @@ +/* + * 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 . + */ +/* + * test-refactor/wh_test_runner.h + * + * Test runner types and API. A suite is just a name plus a + * NULL-terminated array of test functions; any fixture the + * tests need is owned by the caller (the group function) and + * passed through as the ctx argument. Generic -- knows + * nothing about wolfHSM. + */ + +#ifndef WH_TEST_RUNNER_H_ +#define WH_TEST_RUNNER_H_ + +/* Test function: receives the per-suite context and returns 0 + * on success. */ +typedef int (*whTestFn)(void* ctx); + +typedef struct { + const char* name; /* suite name for output */ + whTestFn* tests; /* NULL-terminated array of tests */ +} whTestSuite; + + +/* + * Run a suite against a caller-provided context. Stops on the + * first test failure. Returns 0 if every test succeeds. + */ +int whTestRunner_Run(const whTestSuite* suite, void* ctx); + + +/* + * Convenience macro: run a suite and return from the calling + * function on failure. Intended for use inside group entry + * points. + */ +#define WH_TEST_SUITE_RUN(suite, ctx) \ + do { \ + int _wh_rc = whTestRunner_Run((suite), (ctx)); \ + if (_wh_rc != 0) { \ + return _wh_rc; \ + } \ + } while (0) + + +/* + * Suite definition macro. Suites with their own fixtures + * expose an init/cleanup pair and let the group function + * drive them. + */ +#define WH_TEST_SUITE(sname, tfns) \ + { \ + .name = (sname), \ + .tests = (tfns), \ + } + + +#endif /* WH_TEST_RUNNER_H_ */