diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index a8a2248b7..380c1d88b 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..218e4d8f5 --- /dev/null +++ b/test-refactor/README.md @@ -0,0 +1,134 @@ +# 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. +- **Transport shim**: in single-process mode, a Recv wrapper + pumps the server on `NOTREADY` so blocking client APIs work + without manual `HandleRequestMessage` calls or separate + threads. Lives in the client-side port helper. +- **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, wrap the client Recv to pump the in-process + server on `NOTREADY`. +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_clientserver.c b/test-refactor/client-server/wh_test_clientserver.c new file mode 100644 index 000000000..162c5c22e --- /dev/null +++ b/test-refactor/client-server/wh_test_clientserver.c @@ -0,0 +1,129 @@ +/* + * 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_clientserver.c + * + * Basic client-server test suite. Exercises echo and + * server info queries using the CS helper for setup. + * All tests use blocking client APIs -- the transport + * shim handles server dispatch automatically. + */ + +#include +#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_clientserver.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; +} + + +/* + * 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_echo, + test_server_info, + NULL +}; + +whTestSuite whTestSuite_ClientServer = + WH_TEST_SUITE("ClientServer", _tests); diff --git a/test-refactor/client-server/wh_test_clientserver.h b/test-refactor/client-server/wh_test_clientserver.h new file mode 100644 index 000000000..f32e1075e --- /dev/null +++ b/test-refactor/client-server/wh_test_clientserver.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_clientserver.h + * + * Basic client-server test suite (echo, info). + */ + +#ifndef WH_TEST_CLIENTSERVER_REFACTOR_H_ +#define WH_TEST_CLIENTSERVER_REFACTOR_H_ + +#include "wh_test_runner.h" + +extern whTestSuite whTestSuite_ClientServer; + +#endif /* WH_TEST_CLIENTSERVER_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..822afb3e1 --- /dev/null +++ b/test-refactor/posix/wh_test_helpers_client_posix.c @@ -0,0 +1,129 @@ +/* + * 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. The shim Recv pumps the in-process + * server on NOTREADY so blocking client APIs work without a + * separate thread. + */ + +#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 "wolfhsm/wh_server.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; + + +/* + * Server pointer for the shim and connect callback. Set at + * Client_Init time so the shim knows what to pump. + */ +static whServerContext* _server = NULL; + + +static int _shimRecv(void* context, uint16_t* out_size, + void* data) +{ + int rc = wh_TransportMem_RecvResponse( + context, out_size, data); + if (rc == WH_ERROR_NOTREADY && _server != NULL) { + wh_Server_HandleRequestMessage(_server); + } + return rc; +} + +static const whTransportClientCb _shimCb = { + .Init = wh_TransportMem_InitClear, + .Send = wh_TransportMem_SendRequest, + .Recv = _shimRecv, + .Cleanup = wh_TransportMem_Cleanup, +}; + + +static int _connectCb(void* context, whCommConnected connected) +{ + (void)context; + if (_server == NULL) { + return WH_ERROR_BADARGS; + } + return wh_Server_SetConnected(_server, connected); +} + + +int whTestHelperPosix_Client_Init(whClientContext* client, + whServerContext* server) +{ + whClientConfig cCfg; + whTransportMemConfig* tmCfg; + uint32_t clientId = 0; + uint32_t serverId = 0; + + if (client == NULL || server == NULL) { + return WH_ERROR_BADARGS; + } + + _server = server; + + tmCfg = whTestHelperPosix_Server_GetTransportConfig(); + if (tmCfg == NULL) { + return WH_ERROR_BADARGS; + } + + memset(&_commCfg, 0, sizeof(_commCfg)); + _commCfg.transport_cb = &_shimCb; + _commCfg.transport_context = (void*)&_tmClientCtx; + _commCfg.transport_config = (void*)tmCfg; + _commCfg.client_id = 1; + _commCfg.connect_cb = _connectCb; + + 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); + _server = NULL; + 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..da046302b --- /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. + * In single-process mode the server lives in the same process, + * so this helper wires a shim Recv that pumps the server on + * NOTREADY -- hence the whServerContext* parameter. + */ + +#ifndef WH_TEST_HELPERS_CLIENT_POSIX_H_ +#define WH_TEST_HELPERS_CLIENT_POSIX_H_ + +#include "wolfhsm/wh_client.h" +#include "wolfhsm/wh_server.h" + +/* + * Initialize the client context plus transport state and + * perform the CommInit handshake with the already-running + * server. `server` must be the same server Init'd via + * whTestHelperPosix_Server_Init; the shim Recv wrapper pumps + * it whenever the client's receive blocks. + */ +int whTestHelperPosix_Client_Init(whClientContext* client, + whServerContext* server); + +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..8521d1ead --- /dev/null +++ b/test-refactor/posix/wh_test_helpers_server_posix.c @@ -0,0 +1,165 @@ +/* + * 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 so the shim + * can pump messages in single-process mode. */ +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..05f8d7842 --- /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 connect the single-process shim to 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..c7361feac --- /dev/null +++ b/test-refactor/posix/wh_test_main_posix.c @@ -0,0 +1,107 @@ +/* + * 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 test driver. Mirrors a real firmware's boot flow: + * brings up the server and (if applicable) the client once, + * runs the test groups against the live contexts, then tears + * them down. 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 "wolfhsm/wh_settings.h" +#include "wolfhsm/wh_client.h" +#include "wolfhsm/wh_server.h" + +#include "wh_test_common.h" +#include "wh_test_groups.h" + +#if defined(WOLFHSM_CFG_ENABLE_SERVER) +#include "wh_test_helpers_server_posix.h" +#endif + +#if defined(WOLFHSM_CFG_ENABLE_CLIENT) \ + && defined(WOLFHSM_CFG_ENABLE_SERVER) +#include "wh_test_helpers_client_posix.h" +#endif + + +/* + * Port-owned contexts. main provides these to the group + * functions, paralleling the firmware pattern where these + * handles come from the normal init flow. + */ +#if defined(WOLFHSM_CFG_ENABLE_SERVER) +static whServerContext _server; +#endif +#if defined(WOLFHSM_CFG_ENABLE_CLIENT) \ + && defined(WOLFHSM_CFG_ENABLE_SERVER) +static whClientContext _client; +#endif + + +int main(void) +{ + whTestGroup_Misc(); + +#if defined(WOLFHSM_CFG_ENABLE_SERVER) + WH_TEST_RETURN_ON_FAIL( + whTestHelperPosix_Server_Init(&_server)); + + whTestGroup_Server(&_server); +#endif + +#if defined(WOLFHSM_CFG_ENABLE_CLIENT) \ + && defined(WOLFHSM_CFG_ENABLE_SERVER) + WH_TEST_RETURN_ON_FAIL( + whTestHelperPosix_Client_Init(&_client, &_server)); + + whTestGroup_Client(&_client); + + whTestHelperPosix_Client_Cleanup(&_client); +#endif + +#if defined(WOLFHSM_CFG_ENABLE_SERVER) + whTestHelperPosix_Server_Cleanup(&_server); +#endif + + return 0; +} + + +/* + * 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..c972a73eb --- /dev/null +++ b/test-refactor/wh_test_groups.c @@ -0,0 +1,92 @@ +/* + * 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_clientserver.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_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_ClientServer, client); + WH_TEST_RETURN_ON_FAIL(whTestGroup_ResetClient(client)); + +#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_ */