diff --git a/test-refactor/README.md b/test-refactor/README.md
index 6de6046f9..e5adc4786 100644
--- a/test-refactor/README.md
+++ b/test-refactor/README.md
@@ -90,6 +90,7 @@ Translated tests:
| `wh_test_flash_fault_inject.c` | `posix/wh_test_flash_fault_inject.c` | helper (no test) | fault-injection flash wrapper used by the recovery test |
| `wh_test_posix_threadsafe_stress.c::whTest_ThreadSafeStress` | called directly from `posix/wh_test_posix_main.c` | POSIX port-specific (direct call) | |
| `wh_test_check_struct_padding.c` | `misc/wh_test_check_struct_padding.c` | Build-time (compile-only) | Wire-format `-Wpadded` audit; the POSIX Makefile compiles it with `-Wpadded -DWH_PADDING_CHECK`. Not a runtime test, so not registered in `wh_test_list.c` |
+| `wh_test_crypto_affinity.c::whTest_CryptoAffinity` | `misc/wh_test_crypto_affinity.c::whTest_CryptoAffinity` | Misc | Self-contained sequential client+server pair so the subtests can set the server devId. The WithCb subtest configures a HW devId plus a registered cryptoCb to cover the server's HW affinity-to-devId routing; the NoCb subtest uses INVALID_DEVID for the SW fallback. Under WOLFHSM_CFG_DMA both subtests also issue an AES CBC DMA request so the DMA crypto dispatch's affinity routing is covered with the same HW/SW branch matrix as the non-DMA path. Kept out of the client group because the shared port server is fixed at INVALID_DEVID and cannot exercise the HW routing branch |
| `wh_test_auth.c` (`whTest_AuthMEM` / `whTest_AuthTest` sub-tests) | `client-server/wh_test_auth.c::{whTest_AuthBadArgs, whTest_AuthLogin, whTest_AuthLogout, whTest_AuthAddUser, whTest_AuthDeleteUser, whTest_AuthSetPermissions, whTest_AuthSetCredentials, whTest_AuthRequestAuthorization}` | Client | Under `WOLFHSM_CFG_ENABLE_AUTHENTICATION` the POSIX server installs an auth context + admin user and the client logs in as admin at connect, so the ordinary client tests run authorized; each auth test brackets its own session (logout to start clean, restore admin on exit). Uses the blocking client API; the legacy own-server setup and single-thread manual-pump are dropped. Build with `make AUTH=1`. The TCP/client-only variant (`whTest_AuthTCP`) is not ported |
Not yet migrated (still live in `wolfHSM/test/`):
@@ -100,7 +101,6 @@ Not yet migrated (still live in `wolfHSM/test/`):
| `wh_test_clientserver.c::whTest_ClientServer` | Pthread variant: remaining client-side coverage (NVM ops, etc.) still needs to be split out as new tests. The sequential test is dropped |
| `wh_test_crypto.c::whTest_Crypto` | Remaining crypto coverage not yet split out: the AES async family (comm-buffer `whTest_CryptoAesAsync`/`AesAsyncKat` + DMA `whTest_CryptoAesDmaAsync`/`AesDmaAsyncKat`, round-trip & KAT). ECC DMA export-public and the ML-DSA wolfCrypt-API path are now migrated. |
| `wh_test_crypto.c::whTest_KeyCache`, `whTest_NonExportableKeystore` | Keystore tests (key-cache lifecycle and non-exportable-flag enforcement) dispatched from the legacy `whTest_Crypto`. The per-algorithm suites use `wh_Client_KeyCache`, but these dedicated keystore tests are not yet split out. |
-| `wh_test_crypto_affinity.c::whTest_CryptoAffinity` | |
| `wh_test_keywrap.c::whTest_KeyWrapClientConfig` | |
| `wh_test_multiclient.c::whTest_MultiClient` | |
| `wh_test_lock.c::whTest_LockConfig`, `whTest_LockPosix` | `whTest_LockConfig` to be reworked to fit the Misc group, likely with a context param. |
diff --git a/test-refactor/misc/wh_test_crypto_affinity.c b/test-refactor/misc/wh_test_crypto_affinity.c
new file mode 100644
index 000000000..1a1a590f0
--- /dev/null
+++ b/test-refactor/misc/wh_test_crypto_affinity.c
@@ -0,0 +1,612 @@
+/*
+ * 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/misc/wh_test_crypto_affinity.c
+ *
+ * Tests for the crypto affinity API. Affinity is client-local state
+ * transmitted per-message in the generic crypto header; no round-trip
+ * to the server is required to set or get it. The server uses the
+ * per-message affinity to pick its devId: HW affinity routes to the
+ * configured HW devId, anything else falls back to INVALID_DEVID (SW).
+ *
+ * Self-contained in the Misc group: each subtest stands up its own
+ * client/server pair and pumps requests sequentially so it can control
+ * the server's devId, which the shared port server (fixed at
+ * INVALID_DEVID) cannot provide. The WithCb subtest exercises the HW
+ * routing branch; the NoCb subtest exercises the SW fallback branch.
+ * Under WOLFHSM_CFG_DMA the WithCb subtest also drives an AES CBC DMA
+ * request so the DMA crypto dispatch's affinity routing is covered too.
+ */
+
+#include "wolfhsm/wh_settings.h"
+
+/* Only compile if we have crypto, client, server, and crypto callbacks */
+#if defined(WOLFHSM_CFG_ENABLE_CLIENT) && \
+ defined(WOLFHSM_CFG_ENABLE_SERVER) && \
+ !defined(WOLFHSM_CFG_NO_CRYPTO) && defined(WOLF_CRYPTO_CB)
+
+#include
+#include
+#include
+
+#include "wolfssl/wolfcrypt/settings.h"
+#include "wolfssl/wolfcrypt/types.h"
+#include "wolfssl/wolfcrypt/cryptocb.h"
+#include "wolfssl/wolfcrypt/random.h"
+
+#include "wolfhsm/wh_error.h"
+#include "wolfhsm/wh_common.h"
+#include "wolfhsm/wh_comm.h"
+#include "wolfhsm/wh_transport_mem.h"
+#include "wolfhsm/wh_client.h"
+#include "wolfhsm/wh_client_crypto.h"
+#include "wolfhsm/wh_server.h"
+#include "wolfhsm/wh_nvm.h"
+#include "wolfhsm/wh_nvm_flash.h"
+#include "wolfhsm/wh_flash_ramsim.h"
+
+#include "wh_test_common.h"
+#include "wh_test_list.h"
+
+#define BUFFER_SIZE 4096
+#define FLASH_RAM_SIZE (1024 * 1024)
+#define FLASH_SECTOR_SIZE (128 * 1024)
+#define FLASH_PAGE_SIZE (8)
+#define TEST_DEV_ID 0xCA
+
+/* Counter to track how many times the crypto callback is invoked */
+static int cryptoCbInvokeCount = 0;
+
+static whServerContext* cryptoAffinityTestServerCtx = NULL;
+
+/* Test crypto callback that just increments a counter and returns
+ * CRYPTOCB_UNAVAILABLE to fall back to software */
+static int _testCryptoCb(int devId, wc_CryptoInfo* info, void* ctx)
+{
+ (void)devId;
+ (void)info;
+ (void)ctx;
+
+ cryptoCbInvokeCount++;
+
+ /* Return CRYPTOCB_UNAVAILABLE to indicate we don't handle this operation
+ * and wolfCrypt should fall back to software implementation */
+ return CRYPTOCB_UNAVAILABLE;
+}
+
+static int _cryptoAffinityTestConnectCb(void* context,
+ whCommConnected connected)
+{
+ (void)context;
+
+ if (cryptoAffinityTestServerCtx == NULL) {
+ WH_ERROR_PRINT("Client connect callback server context is NULL\n");
+ WH_TEST_ASSERT_RETURN(0);
+ }
+
+ /* Set server connect flag. In a "real" system, this should signal the
+ * server via out-of-band mechanism. The server app is responsible for
+ * receiving this signal and calling wh_Server_SetConnected() */
+ return wh_Server_SetConnected(cryptoAffinityTestServerCtx, connected);
+}
+
+
+static int _whTest_CryptoAffinityWithCb(void)
+{
+ int rc = 0;
+ uint32_t affinity = 0;
+
+ /* Transport memory configuration */
+ uint8_t req[BUFFER_SIZE] = {0};
+ uint8_t resp[BUFFER_SIZE] = {0};
+ whTransportMemConfig tmcf[1] = {{
+ .req = (whTransportMemCsr*)req,
+ .req_size = sizeof(req),
+ .resp = (whTransportMemCsr*)resp,
+ .resp_size = sizeof(resp),
+ }};
+
+ /* Client configuration/contexts */
+ whTransportClientCb tccb[1] = {WH_TRANSPORT_MEM_CLIENT_CB};
+ whTransportMemClientContext tmcc[1] = {0};
+ whCommClientConfig cc_conf[1] = {{
+ .transport_cb = tccb,
+ .transport_context = (void*)tmcc,
+ .transport_config = (void*)tmcf,
+ .client_id = WH_TEST_DEFAULT_CLIENT_ID,
+ .connect_cb = _cryptoAffinityTestConnectCb,
+ }};
+ whClientConfig c_conf[1] = {{
+ .comm = cc_conf,
+ }};
+ whClientContext client[1] = {0};
+
+ /* Server configuration/contexts */
+ whTransportServerCb tscb[1] = {WH_TRANSPORT_MEM_SERVER_CB};
+ whTransportMemServerContext tmsc[1] = {0};
+ whCommServerConfig cs_conf[1] = {{
+ .transport_cb = tscb,
+ .transport_context = (void*)tmsc,
+ .transport_config = (void*)tmcf,
+ .server_id = 123,
+ }};
+
+ /* Flash/NVM configuration */
+ uint8_t flash_memory[FLASH_RAM_SIZE] = {0};
+ whFlashRamsimCtx fc[1] = {0};
+ whFlashRamsimCfg fc_conf[1] = {{
+ .size = FLASH_RAM_SIZE,
+ .sectorSize = FLASH_SECTOR_SIZE,
+ .pageSize = FLASH_PAGE_SIZE,
+ .erasedByte = ~(uint8_t)0,
+ .memory = flash_memory,
+ }};
+ const whFlashCb fcb[1] = {WH_FLASH_RAMSIM_CB};
+
+ whNvmFlashContext nfc[1] = {0};
+ whNvmFlashConfig nf_conf[1] = {{
+ .cb = fcb,
+ .context = fc,
+ .config = fc_conf,
+ }};
+
+
+ whNvmCb nfcb[1] = {WH_NVM_FLASH_CB};
+ whNvmConfig n_conf[1] = {{
+ .cb = nfcb,
+ .context = nfc,
+ .config = nf_conf,
+ }};
+ whNvmContext nvm[1] = {0};
+
+ /* Crypto context */
+ whServerCryptoContext crypto[1] = {0};
+
+ whServerConfig s_conf[1] = {{
+ .comm_config = cs_conf,
+ .nvm = nvm,
+ .crypto = crypto,
+ .devId = TEST_DEV_ID,
+ }};
+ whServerContext server[1] = {0};
+
+ cryptoAffinityTestServerCtx = server;
+
+ WH_TEST_PRINT(" whTest_CryptoAffinityWithCb...");
+
+ /* Initialize wolfCrypt and register our test crypto callback */
+ WH_TEST_RETURN_ON_FAIL(wolfCrypt_Init());
+ WH_TEST_RETURN_ON_FAIL(
+ wc_CryptoCb_RegisterDevice(TEST_DEV_ID, _testCryptoCb, NULL));
+
+ /* Initialize NVM */
+ WH_TEST_RETURN_ON_FAIL(wh_Nvm_Init(nvm, n_conf));
+
+ /* Initialize RNG */
+ WH_TEST_RETURN_ON_FAIL(wc_InitRng_ex(crypto->rng, NULL, INVALID_DEVID));
+
+ /* Initialize server and client */
+ WH_TEST_RETURN_ON_FAIL(wh_Server_Init(server, s_conf));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_Init(client, c_conf));
+
+ /* Check that the server side is ready to recv */
+ WH_TEST_ASSERT_RETURN(WH_ERROR_NOTREADY ==
+ wh_Server_HandleRequestMessage(server));
+
+ /* Send comm init */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_CommInitRequest(client));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_CommInitResponse(client, NULL, NULL));
+
+ /* Verify server initial state - devId should be set */
+ WH_TEST_ASSERT_RETURN(server->devId == TEST_DEV_ID);
+
+ /* Test 1: Default affinity after init should be HW (0) */
+ rc = wh_Client_GetCryptoAffinity(client, &affinity);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(affinity == WH_CRYPTO_AFFINITY_HW);
+
+ /* Test 2: Set SW affinity - local only, no round-trip */
+ rc = wh_Client_SetCryptoAffinity(client, WH_CRYPTO_AFFINITY_SW);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ rc = wh_Client_GetCryptoAffinity(client, &affinity);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(affinity == WH_CRYPTO_AFFINITY_SW);
+
+ /* Test 3: Restore HW affinity */
+ rc = wh_Client_SetCryptoAffinity(client, WH_CRYPTO_AFFINITY_HW);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ rc = wh_Client_GetCryptoAffinity(client, &affinity);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(affinity == WH_CRYPTO_AFFINITY_HW);
+
+ /* Test 4: Invalid affinity returns WH_ERROR_BADARGS */
+ rc = wh_Client_SetCryptoAffinity(client, 0xFF);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ /* Affinity should remain unchanged */
+ rc = wh_Client_GetCryptoAffinity(client, &affinity);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(affinity == WH_CRYPTO_AFFINITY_HW);
+
+ /* Test 5: With HW affinity, crypto op triggers HW callback.
+ * Set HW affinity, send an AES CBC request through the client-server path.
+ * The server should see affinity=HW in the message and use the HW devId,
+ * which triggers the registered crypto callback. */
+ cryptoCbInvokeCount = 0;
+ rc = wh_Client_SetCryptoAffinity(client, WH_CRYPTO_AFFINITY_HW);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ {
+ Aes aes[1];
+ uint8_t key[AES_BLOCK_SIZE] = {0x01};
+ uint8_t iv[AES_BLOCK_SIZE] = {0x02};
+ uint8_t plainIn[AES_BLOCK_SIZE] = {0x03};
+ uint8_t cipherOut[AES_BLOCK_SIZE] = {0};
+ uint32_t outSize = 0;
+
+ WH_TEST_RETURN_ON_FAIL(wc_AesInit(aes, NULL, INVALID_DEVID));
+ WH_TEST_RETURN_ON_FAIL(
+ wc_AesSetKey(aes, key, sizeof(key), iv, AES_ENCRYPTION));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_AesCbcRequest(client, aes, 1, plainIn, sizeof(plainIn)));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_AesCbcResponse(client, aes, cipherOut, &outSize));
+ wc_AesFree(aes);
+ }
+ /* Crypto callback should have been invoked at least once */
+ WH_TEST_ASSERT_RETURN(cryptoCbInvokeCount > 0);
+
+ /* Test 6: With SW affinity, same request does NOT trigger HW callback */
+ cryptoCbInvokeCount = 0;
+ rc = wh_Client_SetCryptoAffinity(client, WH_CRYPTO_AFFINITY_SW);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ {
+ Aes aes[1];
+ uint8_t key[AES_BLOCK_SIZE] = {0x01};
+ uint8_t iv[AES_BLOCK_SIZE] = {0x02};
+ uint8_t plainIn[AES_BLOCK_SIZE] = {0x03};
+ uint8_t cipherOut[AES_BLOCK_SIZE] = {0};
+ uint32_t outSize = 0;
+
+ WH_TEST_RETURN_ON_FAIL(wc_AesInit(aes, NULL, INVALID_DEVID));
+ WH_TEST_RETURN_ON_FAIL(
+ wc_AesSetKey(aes, key, sizeof(key), iv, AES_ENCRYPTION));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_AesCbcRequest(client, aes, 1, plainIn, sizeof(plainIn)));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_AesCbcResponse(client, aes, cipherOut, &outSize));
+ wc_AesFree(aes);
+ }
+ /* Crypto callback should NOT have been invoked */
+ WH_TEST_ASSERT_RETURN(cryptoCbInvokeCount == 0);
+
+#ifdef WOLFHSM_CFG_DMA
+ /* Test 7: DMA path mirrors tests 5-6 but routes through the server's DMA
+ * crypto dispatch. HW affinity selects the HW devId (callback fires); SW
+ * affinity falls back to the SW devId (callback silent). */
+ cryptoCbInvokeCount = 0;
+ rc = wh_Client_SetCryptoAffinity(client, WH_CRYPTO_AFFINITY_HW);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ {
+ Aes aes[1];
+ uint8_t key[AES_BLOCK_SIZE] = {0x01};
+ uint8_t iv[AES_BLOCK_SIZE] = {0x02};
+ uint8_t plainIn[AES_BLOCK_SIZE] = {0x03};
+ uint8_t cipherOut[AES_BLOCK_SIZE] = {0};
+
+ WH_TEST_RETURN_ON_FAIL(wc_AesInit(aes, NULL, INVALID_DEVID));
+ WH_TEST_RETURN_ON_FAIL(
+ wc_AesSetKey(aes, key, sizeof(key), iv, AES_ENCRYPTION));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_AesCbcDmaRequest(
+ client, aes, 1, plainIn, sizeof(plainIn), cipherOut));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_AesCbcDmaResponse(client, aes));
+ wc_AesFree(aes);
+ }
+ /* HW affinity DMA op triggers the HW callback */
+ WH_TEST_ASSERT_RETURN(cryptoCbInvokeCount > 0);
+
+ cryptoCbInvokeCount = 0;
+ rc = wh_Client_SetCryptoAffinity(client, WH_CRYPTO_AFFINITY_SW);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ {
+ Aes aes[1];
+ uint8_t key[AES_BLOCK_SIZE] = {0x01};
+ uint8_t iv[AES_BLOCK_SIZE] = {0x02};
+ uint8_t plainIn[AES_BLOCK_SIZE] = {0x03};
+ uint8_t cipherOut[AES_BLOCK_SIZE] = {0};
+
+ WH_TEST_RETURN_ON_FAIL(wc_AesInit(aes, NULL, INVALID_DEVID));
+ WH_TEST_RETURN_ON_FAIL(
+ wc_AesSetKey(aes, key, sizeof(key), iv, AES_ENCRYPTION));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_AesCbcDmaRequest(
+ client, aes, 1, plainIn, sizeof(plainIn), cipherOut));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_AesCbcDmaResponse(client, aes));
+ wc_AesFree(aes);
+ }
+ /* SW affinity DMA op does NOT trigger the HW callback */
+ WH_TEST_ASSERT_RETURN(cryptoCbInvokeCount == 0);
+#endif /* WOLFHSM_CFG_DMA */
+
+ /* Test 8: NULL args validation */
+ rc = wh_Client_SetCryptoAffinity(NULL, WH_CRYPTO_AFFINITY_SW);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_Client_GetCryptoAffinity(NULL, &affinity);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+ rc = wh_Client_GetCryptoAffinity(client, NULL);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS);
+
+ /* Cleanup */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_CommCloseRequest(client));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_CommCloseResponse(client));
+
+ WH_TEST_RETURN_ON_FAIL(wh_Server_Cleanup(server));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_Cleanup(client));
+
+ wc_FreeRng(crypto->rng);
+ wh_Nvm_Cleanup(nvm);
+ wc_CryptoCb_UnRegisterDevice(TEST_DEV_ID);
+ wolfCrypt_Cleanup();
+
+ WH_TEST_PRINT("PASS\n");
+ return WH_ERROR_OK;
+}
+
+
+static int _whTest_CryptoAffinityNoCb(void)
+{
+ int rc = 0;
+ uint32_t affinity = 0;
+
+ /* Transport memory configuration */
+ uint8_t req[BUFFER_SIZE] = {0};
+ uint8_t resp[BUFFER_SIZE] = {0};
+ whTransportMemConfig tmcf[1] = {{
+ .req = (whTransportMemCsr*)req,
+ .req_size = sizeof(req),
+ .resp = (whTransportMemCsr*)resp,
+ .resp_size = sizeof(resp),
+ }};
+
+ /* Client configuration/contexts */
+ whTransportClientCb tccb[1] = {WH_TRANSPORT_MEM_CLIENT_CB};
+ whTransportMemClientContext tmcc[1] = {0};
+ whCommClientConfig cc_conf[1] = {{
+ .transport_cb = tccb,
+ .transport_context = (void*)tmcc,
+ .transport_config = (void*)tmcf,
+ .client_id = WH_TEST_DEFAULT_CLIENT_ID,
+ .connect_cb = _cryptoAffinityTestConnectCb,
+ }};
+ whClientConfig c_conf[1] = {{
+ .comm = cc_conf,
+ }};
+ whClientContext client[1] = {0};
+
+ /* Server configuration/contexts */
+ whTransportServerCb tscb[1] = {WH_TRANSPORT_MEM_SERVER_CB};
+ whTransportMemServerContext tmsc[1] = {0};
+ whCommServerConfig cs_conf[1] = {{
+ .transport_cb = tscb,
+ .transport_context = (void*)tmsc,
+ .transport_config = (void*)tmcf,
+ .server_id = 123,
+ }};
+
+ /* Flash/NVM configuration */
+ uint8_t flash_memory[FLASH_RAM_SIZE] = {0};
+ whFlashRamsimCtx fc[1] = {0};
+ whFlashRamsimCfg fc_conf[1] = {{
+ .size = FLASH_RAM_SIZE,
+ .sectorSize = FLASH_SECTOR_SIZE,
+ .pageSize = FLASH_PAGE_SIZE,
+ .erasedByte = ~(uint8_t)0,
+ .memory = flash_memory,
+ }};
+ const whFlashCb fcb[1] = {WH_FLASH_RAMSIM_CB};
+
+ whNvmFlashContext nfc[1] = {0};
+ whNvmFlashConfig nf_conf[1] = {{
+ .cb = fcb,
+ .context = fc,
+ .config = fc_conf,
+ }};
+
+
+ whNvmCb nfcb[1] = {WH_NVM_FLASH_CB};
+ whNvmConfig n_conf[1] = {{
+ .cb = nfcb,
+ .context = nfc,
+ .config = nf_conf,
+ }};
+ whNvmContext nvm[1] = {0};
+
+ /* Crypto context */
+ whServerCryptoContext crypto[1] = {0};
+
+ whServerConfig s_conf[1] = {{
+ .comm_config = cs_conf,
+ .nvm = nvm,
+ .crypto = crypto,
+ .devId = INVALID_DEVID,
+ }};
+ whServerContext server[1] = {0};
+
+ cryptoAffinityTestServerCtx = server;
+
+ WH_TEST_PRINT(" whTest_CryptoAffinityNoCb...");
+
+ /* Initialize wolfCrypt */
+ WH_TEST_RETURN_ON_FAIL(wolfCrypt_Init());
+
+ /* Initialize NVM */
+ WH_TEST_RETURN_ON_FAIL(wh_Nvm_Init(nvm, n_conf));
+
+ /* Initialize RNG */
+ WH_TEST_RETURN_ON_FAIL(wc_InitRng_ex(crypto->rng, NULL, INVALID_DEVID));
+
+ /* Initialize server and client */
+ WH_TEST_RETURN_ON_FAIL(wh_Server_Init(server, s_conf));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_Init(client, c_conf));
+
+ /* Check that the server side is ready to recv */
+ WH_TEST_ASSERT_RETURN(WH_ERROR_NOTREADY ==
+ wh_Server_HandleRequestMessage(server));
+
+ /* Send comm init */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_CommInitRequest(client));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_CommInitResponse(client, NULL, NULL));
+
+ /* Verify server configured with INVALID_DEVID */
+ WH_TEST_ASSERT_RETURN(server->devId == INVALID_DEVID);
+
+ /* Test 1: Default affinity should be HW */
+ rc = wh_Client_GetCryptoAffinity(client, &affinity);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(affinity == WH_CRYPTO_AFFINITY_HW);
+
+ /* Test 2: Set SW affinity - should succeed */
+ rc = wh_Client_SetCryptoAffinity(client, WH_CRYPTO_AFFINITY_SW);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ rc = wh_Client_GetCryptoAffinity(client, &affinity);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(affinity == WH_CRYPTO_AFFINITY_SW);
+
+ /* Test 3: Set HW affinity on client side succeeds (it's just local state).
+ * But when the server processes a request with HW affinity and no valid
+ * devId, it will use INVALID_DEVID (SW) anyway. */
+ rc = wh_Client_SetCryptoAffinity(client, WH_CRYPTO_AFFINITY_HW);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ rc = wh_Client_GetCryptoAffinity(client, &affinity);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+ WH_TEST_ASSERT_RETURN(affinity == WH_CRYPTO_AFFINITY_HW);
+
+ /* Test 4: AES CBC request with HW affinity succeeds even without a HW
+ * crypto callback because the server falls back to INVALID_DEVID (SW). */
+ {
+ Aes aes[1];
+ uint8_t key[AES_BLOCK_SIZE] = {0x01};
+ uint8_t iv[AES_BLOCK_SIZE] = {0x02};
+ uint8_t plainIn[AES_BLOCK_SIZE] = {0x03};
+ uint8_t cipherOut[AES_BLOCK_SIZE] = {0};
+ uint32_t outSize = 0;
+
+ WH_TEST_RETURN_ON_FAIL(wc_AesInit(aes, NULL, INVALID_DEVID));
+ WH_TEST_RETURN_ON_FAIL(
+ wc_AesSetKey(aes, key, sizeof(key), iv, AES_ENCRYPTION));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_AesCbcRequest(client, aes, 1, plainIn, sizeof(plainIn)));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_AesCbcResponse(client, aes, cipherOut, &outSize));
+ WH_TEST_ASSERT_RETURN(outSize == sizeof(plainIn));
+ wc_AesFree(aes);
+ }
+
+ /* Test 5: AES CBC request with SW affinity also succeeds */
+ rc = wh_Client_SetCryptoAffinity(client, WH_CRYPTO_AFFINITY_SW);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ {
+ Aes aes[1];
+ uint8_t key[AES_BLOCK_SIZE] = {0x01};
+ uint8_t iv[AES_BLOCK_SIZE] = {0x02};
+ uint8_t plainIn[AES_BLOCK_SIZE] = {0x03};
+ uint8_t cipherOut[AES_BLOCK_SIZE] = {0};
+ uint32_t outSize = 0;
+
+ WH_TEST_RETURN_ON_FAIL(wc_AesInit(aes, NULL, INVALID_DEVID));
+ WH_TEST_RETURN_ON_FAIL(
+ wc_AesSetKey(aes, key, sizeof(key), iv, AES_ENCRYPTION));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_AesCbcRequest(client, aes, 1, plainIn, sizeof(plainIn)));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+ WH_TEST_RETURN_ON_FAIL(
+ wh_Client_AesCbcResponse(client, aes, cipherOut, &outSize));
+ WH_TEST_ASSERT_RETURN(outSize == sizeof(plainIn));
+ wc_AesFree(aes);
+ }
+
+#ifdef WOLFHSM_CFG_DMA
+ /* Test 6: AES CBC DMA request with HW affinity still succeeds without a HW
+ * crypto callback: with no valid server devId the DMA dispatch falls back
+ * to INVALID_DEVID (SW). Exercises the DMA routing's SW-fallback branch. */
+ rc = wh_Client_SetCryptoAffinity(client, WH_CRYPTO_AFFINITY_HW);
+ WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK);
+
+ {
+ Aes aes[1];
+ uint8_t key[AES_BLOCK_SIZE] = {0x01};
+ uint8_t iv[AES_BLOCK_SIZE] = {0x02};
+ uint8_t plainIn[AES_BLOCK_SIZE] = {0x03};
+ uint8_t cipherOut[AES_BLOCK_SIZE] = {0};
+
+ WH_TEST_RETURN_ON_FAIL(wc_AesInit(aes, NULL, INVALID_DEVID));
+ WH_TEST_RETURN_ON_FAIL(
+ wc_AesSetKey(aes, key, sizeof(key), iv, AES_ENCRYPTION));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_AesCbcDmaRequest(
+ client, aes, 1, plainIn, sizeof(plainIn), cipherOut));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_AesCbcDmaResponse(client, aes));
+ wc_AesFree(aes);
+ }
+#endif /* WOLFHSM_CFG_DMA */
+
+ /* Cleanup */
+ WH_TEST_RETURN_ON_FAIL(wh_Client_CommCloseRequest(client));
+ WH_TEST_RETURN_ON_FAIL(wh_Server_HandleRequestMessage(server));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_CommCloseResponse(client));
+
+ WH_TEST_RETURN_ON_FAIL(wh_Server_Cleanup(server));
+ WH_TEST_RETURN_ON_FAIL(wh_Client_Cleanup(client));
+
+ wc_FreeRng(crypto->rng);
+ wh_Nvm_Cleanup(nvm);
+ wolfCrypt_Cleanup();
+
+ WH_TEST_PRINT("PASS\n");
+ return WH_ERROR_OK;
+}
+
+
+int whTest_CryptoAffinity(void* ctx)
+{
+ (void)ctx;
+
+ WH_TEST_PRINT("Testing Crypto Affinity...\n");
+ WH_TEST_RETURN_ON_FAIL(_whTest_CryptoAffinityWithCb());
+ WH_TEST_RETURN_ON_FAIL(_whTest_CryptoAffinityNoCb());
+
+ return WH_ERROR_OK;
+}
+
+#endif /* WOLFHSM_CFG_ENABLE_CLIENT && WOLFHSM_CFG_ENABLE_SERVER && \
+ * !WOLFHSM_CFG_NO_CRYPTO && WOLF_CRYPTO_CB */
diff --git a/test-refactor/wh_test_list.c b/test-refactor/wh_test_list.c
index a7ff07128..d955c7eb0 100644
--- a/test-refactor/wh_test_list.c
+++ b/test-refactor/wh_test_list.c
@@ -38,6 +38,7 @@
/* Test declarations and weak skip implementations. */
WH_TEST_DECL(whTest_ClientDevId);
WH_TEST_DECL(whTest_Comm);
+WH_TEST_DECL(whTest_CryptoAffinity);
WH_TEST_DECL(whTest_Dma);
WH_TEST_DECL(whTest_KeystoreReqSize);
WH_TEST_DECL(whTest_CertVerify);
@@ -70,6 +71,7 @@ WH_TEST_DECL(whTest_AuthRequestAuthorization);
const whTestCase whTestsMisc[] = {
{"whTest_ClientDevId", whTest_ClientDevId},
{"whTest_Comm", whTest_Comm},
+ {"whTest_CryptoAffinity", whTest_CryptoAffinity},
{"whTest_Dma", whTest_Dma},
{"whTest_KeystoreReqSize", whTest_KeystoreReqSize},
};