Skip to content

Commit 7116ef2

Browse files
committed
Initial prototype of test refactor w/ runner, helper and test layers
1 parent 5ac5385 commit 7116ef2

26 files changed

Lines changed: 2508 additions & 0 deletions

.github/workflows/build-and-test.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ jobs:
3232
repository: wolfssl/wolfssl
3333
path: wolfssl
3434

35+
- name: Build and test refactor
36+
run: cd test-refactor/posix && make clean && make -j WOLFSSL_DIR=../wolfssl && make run
37+
3538
# Build and test standard build
3639
- name: Build and test
3740
run: cd test && make clean && make -j WOLFSSL_DIR=../wolfssl && make run

test-refactor/README.md

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# test-refactor
2+
3+
Prototype of the refactored wolfHSM test infrastructure.
4+
5+
## Key differences from test/
6+
7+
- **Runner** (`wh_test_runner.h/c`): generic suite executor.
8+
Each suite is a name + NULL-terminated test array, run
9+
either via `whTestRunner_Run` (suite owns its setup/cleanup)
10+
or `whTestRunner_RunWithCtx` (caller provides the live
11+
context). Group functions use the latter.
12+
- **App-owned init**: the port's main() brings up the server
13+
and client once at startup (mirroring real firmware boot)
14+
and hands the live contexts to the group functions. Suites
15+
no longer stand up their own fixtures.
16+
- **Port helpers** (`wh_test_helpers_server_<port>.h/c`,
17+
`wh_test_helpers_client_<port>.h/c`): per-port files that
18+
stand in for what a real target does at boot -- configure
19+
flash, init NVM/crypto, wire up a transport, bring up the
20+
server or client context.
21+
- **Groups** (`wh_test_groups.h/c`): three portable entry
22+
points (Misc/Server/Client) that main invokes. Each runs
23+
its gated suites and calls the caller-implemented reset
24+
hook between them.
25+
- **Threaded driver**: the POSIX port's main runs the server
26+
and client on separate threads. The server thread runs the
27+
server-only group first, then enters a `HandleRequestMessage`
28+
loop; the client thread runs the client-only group against
29+
the live server. Ports that already split server and client
30+
onto different cores/tasks do the same thing natively.
31+
- **Platform split**: platform-specific code is isolated in
32+
`wh_test_helpers_server_<port>.c`,
33+
`wh_test_helpers_client_<port>.c`, and
34+
`wh_test_main_<port>.c`. Test modules and groups are
35+
identical on all platforms.
36+
37+
## Suites implemented so far
38+
39+
| Suite | Group | Description |
40+
|-------|-------|-------------|
41+
| Flash RamSim | misc | Write-lock, erase, program, verify, blank-check |
42+
| NVM Flash | misc | Flash unit ops, NVM add/overwrite/destroy/reclaim |
43+
| Cert | server | Server-side cert add/verify/chain/erase |
44+
| ClientServer | client-server | Echo round-trip, server info query |
45+
| ThreadSafe Stress | client-server | Phased multi-thread contention (unchanged internals) |
46+
47+
## Remaining tests to port
48+
49+
| Suite | Group | Description |
50+
|-------|-------|-------------|
51+
| Comm | client-server | Transport layer (mem, TCP, SHM) |
52+
| Crypto | client-server | AES, RSA, ECC, CMAC, curve25519, ed25519, etc. |
53+
| Crypto Affinity | client-server | Device ID operation routing |
54+
| SHE | client-server | Secure Hardware Extension key load, crypto, secure boot |
55+
| Keywrap | client-server | Key wrap/unwrap operations |
56+
| Log | misc | Logging frontend, ringbuf, POSIX file backends |
57+
| Lock | misc | Lock primitives with POSIX backend |
58+
| DMA | misc | DMA address translation and allow-list |
59+
| Server Img Mgr | server | Image manager verify/install/erase |
60+
| Timeout | client-server | POSIX timeout enforcement |
61+
| wolfCrypt Test | client-server | wolfCrypt test suite via wolfHSM transport |
62+
| MultiClient | client-server | 2 CS pairs, shared NVM, global/local key isolation |
63+
64+
## Platforms requiring update
65+
66+
Each platform with test infrastructure needs its own
67+
`wh_test_helpers_server_<port>.c`,
68+
`wh_test_helpers_client_<port>.c`, and
69+
`wh_test_main_<port>.c` (see "Porting" below).
70+
71+
| Platform | Vendor | Test files |
72+
|----------|--------|------------|
73+
| POSIX | wolfSSL | `test-refactor/posix/` (done) |
74+
| Bernina | STMicro | `bernina-server/src/bh_test.c` |
75+
| SR6 | STMicro | (no test files found) |
76+
| TC3xx | Infineon | `port/client/wolfhsm_tests.c`, `port/server/ccb_tests.c` |
77+
| RH850 F1KM | Renesas | `rh850_test2_1/`, `rh850_test2_2/` |
78+
| PIC32CZ | Microchip | `czhsm-client/tests/`, `czhsm-server/` |
79+
| TDA4VH | TI | (no test files found) |
80+
| New Eagle | Customer | (no test files found) |
81+
82+
## File layout
83+
84+
```
85+
Portable (ships in wolfHSM):
86+
wh_test_runner.h/c - suite runner
87+
wh_test_groups.h/c - Misc/Server/Client entry points
88+
server/wh_test_*.c/h - server-only test modules
89+
client-server/wh_test_*.c/h - client-server test modules
90+
misc/wh_test_*.c/h - standalone test modules
91+
92+
Platform-specific (one directory per platform, e.g. posix/):
93+
<port>/wh_test_helpers_misc_<port>.h/c - misc fixtures
94+
<port>/wh_test_helpers_server_<port>.h/c - server bringup
95+
<port>/wh_test_helpers_client_<port>.h/c - client bringup
96+
<port>/wh_test_main_<port>.c - init, group
97+
dispatch, reset
98+
hooks
99+
<port>/Makefile - build rules
100+
```
101+
102+
## Porting to other platforms
103+
104+
1. Implement the init helpers for the side(s) the target
105+
needs. These stand in for what your firmware's normal
106+
boot flow already does -- if it's simpler to call your
107+
existing init code directly from main, that works too:
108+
- `whTestHelperPosix_Server_Init/Cleanup` (reference):
109+
bring up flash/NVM/crypto/transport/server.
110+
- `whTestHelperPosix_Client_Init/Cleanup` (reference):
111+
bring up client comm + handshake. On single-process
112+
targets, the server runs in its own thread and pumps
113+
`HandleRequestMessage` itself.
114+
2. Provide a `main()` that:
115+
- Calls `whTestGroup_Misc()` for standalone tests.
116+
- Brings up the server/client contexts once.
117+
- Calls `whTestGroup_Server(&server)` and/or
118+
`whTestGroup_Client(&client)` with the live handles.
119+
- Tears the contexts down.
120+
- Implements `whTestGroup_ResetServer` and/or
121+
`whTestGroup_ResetClient` -- called between suites to
122+
scrub persistent state.
123+
3. Add the portable `.c` files and your port files to your
124+
build system.
125+
126+
See `wh_test_main_posix.c` and the two `*_posix.c` helpers as
127+
a reference implementation.
128+
129+
## Build and run (POSIX)
130+
131+
```
132+
cd posix
133+
make run
134+
make run DEBUG=1
135+
make run THREADSAFE=1 # enables stress test gate
136+
```
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
* Copyright (C) 2026 wolfSSL Inc.
3+
*
4+
* This file is part of wolfHSM.
5+
*
6+
* wolfHSM is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* wolfHSM is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with wolfHSM. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
/*
20+
* test-refactor/wh_test_crypto.c
21+
*
22+
* Basic crypto test suite. Minimal SHA256 and AES-CBC
23+
* round-trips routed through the server via WH_DEV_ID.
24+
*/
25+
26+
#include "wolfhsm/wh_settings.h"
27+
28+
#if !defined(WOLFHSM_CFG_NO_CRYPTO)
29+
30+
#include <stdint.h>
31+
#include <string.h>
32+
33+
#include "wolfssl/wolfcrypt/settings.h"
34+
#include "wolfssl/wolfcrypt/types.h"
35+
#include "wolfssl/wolfcrypt/aes.h"
36+
#include "wolfssl/wolfcrypt/sha256.h"
37+
38+
#include "wolfhsm/wh_error.h"
39+
#include "wolfhsm/wh_client.h"
40+
41+
#include "wh_test_common.h"
42+
#include "wh_test_runner.h"
43+
#include "wh_test_crypto.h"
44+
45+
#ifndef NO_SHA256
46+
static int whTest_CryptoSha256(void* c)
47+
{
48+
whClientContext* ctx = (whClientContext*)c;
49+
int devId = WH_DEV_ID;
50+
int ret = WH_ERROR_OK;
51+
wc_Sha256 sha256[1];
52+
uint8_t out[WC_SHA256_DIGEST_SIZE];
53+
/* Vector exactly one block size in length */
54+
const char inOne[] =
55+
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
56+
const uint8_t expectedOutOne[WC_SHA256_DIGEST_SIZE] = {
57+
0xff, 0xe0, 0x54, 0xfe, 0x7a, 0xe0, 0xcb, 0x6d, 0xc6, 0x5c, 0x3a,
58+
0xf9, 0xb6, 0x1d, 0x52, 0x09, 0xf4, 0x39, 0x85, 0x1d, 0xb4, 0x3d,
59+
0x0b, 0xa5, 0x99, 0x73, 0x37, 0xdf, 0x15, 0x46, 0x68, 0xeb};
60+
61+
(void)ctx;
62+
63+
/* Initialize SHA256 structure */
64+
ret = wc_InitSha256_ex(sha256, NULL, devId);
65+
if (ret != 0) {
66+
WH_ERROR_PRINT("Failed to wc_InitSha256 on devId 0x%X: %d\n", devId,
67+
ret);
68+
} else {
69+
/* Single-block update should trigger a server transaction */
70+
ret = wc_Sha256Update(sha256,
71+
(const byte*)inOne,
72+
WC_SHA256_BLOCK_SIZE);
73+
if (ret != 0) {
74+
WH_ERROR_PRINT("Failed to wc_Sha256Update %d\n", ret);
75+
} else {
76+
/* Finalize should trigger a server transaction with empty buffer */
77+
ret = wc_Sha256Final(sha256, out);
78+
if (ret != 0) {
79+
WH_ERROR_PRINT("Failed to wc_Sha256Final %d\n", ret);
80+
} else {
81+
/* Compare the computed hash with the expected output */
82+
if (memcmp(out, expectedOutOne, WC_SHA256_DIGEST_SIZE) != 0) {
83+
WH_ERROR_PRINT("SHA256 hash does not match expected.\n");
84+
ret = -1;
85+
}
86+
}
87+
}
88+
(void)wc_Sha256Free(sha256);
89+
}
90+
if (ret == 0) {
91+
WH_TEST_PRINT("SHA256 DEVID=0x%X SUCCESS\n", devId);
92+
}
93+
return ret;
94+
}
95+
#endif /* !NO_SHA256 */
96+
97+
98+
#if !defined(NO_AES) && defined(HAVE_AES_CBC)
99+
static int whTestCrypto_Aes(void* c)
100+
{
101+
whClientContext* ctx = (whClientContext*)c;
102+
int devId = WH_DEV_ID;
103+
int ret = 0;
104+
Aes aes[1];
105+
uint8_t cipher[AES_BLOCK_SIZE] = {0};
106+
uint8_t plainOut[AES_BLOCK_SIZE] = {0};
107+
/* NIST SP 800-38B test vectors (same k128 / m used by the CMAC test
108+
* in test/wh_test_crypto.c). Using a fixed vector keeps this suite
109+
* self-contained, no RNG needed. */
110+
const uint8_t key[] = {0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
111+
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c};
112+
const uint8_t iv[AES_BLOCK_SIZE] = {
113+
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
114+
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
115+
const uint8_t plainIn[AES_BLOCK_SIZE] = {
116+
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
117+
0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a};
118+
119+
(void)ctx;
120+
121+
/* test aes CBC with client side key */
122+
ret = wc_AesInit(aes, NULL, devId);
123+
if (ret != 0) {
124+
WH_ERROR_PRINT("Failed to wc_AesInit %d\n", ret);
125+
} else {
126+
ret = wc_AesSetKey(aes, key, sizeof(key), iv, AES_ENCRYPTION);
127+
if (ret != 0) {
128+
WH_ERROR_PRINT("Failed to wc_AesSetKey %d\n", ret);
129+
} else {
130+
ret = wc_AesCbcEncrypt(aes, cipher, plainIn,
131+
sizeof(plainIn));
132+
if (ret != 0) {
133+
WH_ERROR_PRINT("Failed to wc_AesCbcEncrypt %d\n", ret);
134+
} else {
135+
ret = wc_AesSetKey(aes, key, sizeof(key), iv,
136+
AES_DECRYPTION);
137+
if (ret != 0) {
138+
WH_ERROR_PRINT("Failed to wc_AesSetKey %d\n", ret);
139+
} else {
140+
ret = wc_AesCbcDecrypt(aes, plainOut, cipher,
141+
sizeof(cipher));
142+
if (ret != 0) {
143+
WH_ERROR_PRINT("Failed to wc_AesCbcDecrypt %d\n",
144+
ret);
145+
} else {
146+
if (memcmp(plainIn, plainOut, sizeof(plainIn)) !=
147+
0) {
148+
WH_ERROR_PRINT("Failed to match AES-CBC\n");
149+
ret = -1;
150+
}
151+
}
152+
}
153+
}
154+
}
155+
(void)wc_AesFree(aes);
156+
}
157+
if (ret == 0) {
158+
WH_TEST_PRINT("AES CBC DEVID=0x%X SUCCESS\n", devId);
159+
}
160+
return ret;
161+
}
162+
#endif /* !NO_AES && HAVE_AES_CBC */
163+
164+
165+
static whTestFn _tests[] = {
166+
#ifndef NO_SHA256
167+
whTest_CryptoSha256,
168+
#endif
169+
#if !defined(NO_AES) && defined(HAVE_AES_CBC)
170+
whTestCrypto_Aes,
171+
#endif
172+
NULL
173+
};
174+
175+
whTestSuite whTestSuite_Crypto =
176+
WH_TEST_SUITE("Crypto", _tests);
177+
178+
#endif /* !WOLFHSM_CFG_NO_CRYPTO */
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*
2+
* Copyright (C) 2026 wolfSSL Inc.
3+
*
4+
* This file is part of wolfHSM.
5+
*
6+
* wolfHSM is free software; you can redistribute it and/or modify
7+
* it under the terms of the GNU General Public License as published by
8+
* the Free Software Foundation; either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* wolfHSM is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License
17+
* along with wolfHSM. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
/*
20+
* test-refactor/wh_test_crypto.h
21+
*
22+
* Basic crypto test suite (minimal SHA256 round-trip).
23+
*/
24+
25+
#ifndef WH_TEST_CRYPTO_REFACTOR_H_
26+
#define WH_TEST_CRYPTO_REFACTOR_H_
27+
28+
#include "wolfhsm/wh_settings.h"
29+
30+
#if !defined(WOLFHSM_CFG_NO_CRYPTO)
31+
32+
#include "wh_test_runner.h"
33+
34+
extern whTestSuite whTestSuite_Crypto;
35+
36+
#endif /* !WOLFHSM_CFG_NO_CRYPTO */
37+
38+
#endif /* WH_TEST_CRYPTO_REFACTOR_H_ */

0 commit comments

Comments
 (0)