diff --git a/port/posix/posix_transport_shm.c b/port/posix/posix_transport_shm.c index a5ba81ba9..2b1f25901 100644 --- a/port/posix/posix_transport_shm.c +++ b/port/posix/posix_transport_shm.c @@ -603,6 +603,9 @@ int posixTransportShm_ClientStaticMemDmaCallback( else if (oper == WH_DMA_OPER_CLIENT_READ_POST) { if (isInDma == 0) { uint8_t* ptr = (uint8_t*)dmaPtr + (uintptr_t)*xformedCliAddr; + /* The bounce buffer holds a copy of client input (possibly key + * material); clear it before returning it to the shared DMA heap. */ + wh_Utils_ForceZero(ptr, len); XFREE(ptr, heap, DYNAMIC_TYPE_TMP_BUFFER); } } @@ -611,6 +614,9 @@ int posixTransportShm_ClientStaticMemDmaCallback( uint8_t* ptr = (uint8_t*)dmaPtr + (uintptr_t)*xformedCliAddr; memcpy((void*)clientAddr, ptr, len); /* copy results of what server wrote */ + /* The bounce buffer holds server output (possibly key material); + * clear it before returning it to the shared DMA heap. */ + wh_Utils_ForceZero(ptr, len); XFREE(ptr, heap, DYNAMIC_TYPE_TMP_BUFFER); } } diff --git a/src/wh_client.c b/src/wh_client.c index 688c42143..5e34b7bc0 100644 --- a/src/wh_client.c +++ b/src/wh_client.c @@ -34,6 +34,7 @@ /* Common WolfHSM types and defines shared with the server */ #include "wolfhsm/wh_common.h" #include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_utils.h" /* For wh_Utils_ForceZero */ /* Components */ #include "wolfhsm/wh_comm.h" @@ -811,6 +812,7 @@ int wh_Client_KeyCacheRequest_ex(whClientContext* c, uint32_t flags, whMessageKeystore_CacheRequest* req = NULL; uint8_t* packIn; uint16_t capSz; + int rc; if (c == NULL || in == NULL || inSz == 0 || sizeof(*req) + inSz > WOLFHSM_CFG_COMM_DATA_LEN) { @@ -841,8 +843,16 @@ int wh_Client_KeyCacheRequest_ex(whClientContext* c, uint32_t flags, memcpy(packIn, in, inSz); /* write request */ - return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_KEY, WH_KEY_CACHE, - sizeof(*req) + inSz, (uint8_t*)req); + rc = wh_Client_SendRequest(c, WH_MESSAGE_GROUP_KEY, WH_KEY_CACHE, + sizeof(*req) + inSz, (uint8_t*)req); + + /* The imported key bytes have been serialized into the transport; do not + * leave a copy lingering in the long-lived client comm buffer. The shared + * request transport buffer is cleared server-side once the request has been + * consumed (see wh_TransportMem_RecvRequest). */ + wh_Utils_ForceZero(packIn, inSz); + + return rc; } int wh_Client_KeyCacheRequest(whClientContext* c, uint32_t flags, @@ -1020,6 +1030,10 @@ int wh_Client_KeyExportResponse(whClientContext* c, uint8_t* label, memcpy(label, resp->label, labelSz); } } + /* The response received into the shared comm buffer holds the exported + * key material; zeroize the whole received payload before returning on + * every path so it does not linger in the long-lived session buffer. */ + wh_Utils_ForceZero(resp, size); } return ret; } diff --git a/src/wh_client_keywrap.c b/src/wh_client_keywrap.c index 0bb1e15f0..3c3414c1a 100644 --- a/src/wh_client_keywrap.c +++ b/src/wh_client_keywrap.c @@ -9,6 +9,7 @@ #include #include #include +#include /* For wh_Utils_ForceZero */ #include #include @@ -203,24 +204,30 @@ int wh_Client_KeyUnwrapAndExportResponse(whClientContext* ctx, size < sizeof(*resp) || size < sizeof(*resp) + sizeof(*metadataOut) + resp->keySz || resp->cipherType != cipherType) { - return WH_ERROR_ABORTED; + ret = WH_ERROR_ABORTED; } - - if (resp->rc != WH_ERROR_OK) { - return resp->rc; + else if (resp->rc != WH_ERROR_OK) { + ret = resp->rc; } else if (resp->keySz > *keyInOutSz) { - return WH_ERROR_BUFFER_SIZE; + ret = WH_ERROR_BUFFER_SIZE; + } + else { + /* Copy the metadata and key from the response data into metadataOut and + * keyOut */ + respData = (uint8_t*)(resp + 1); + memcpy(metadataOut, respData, sizeof(*metadataOut)); + memcpy(keyOut, respData + sizeof(*metadataOut), resp->keySz); + *keyInOutSz = resp->keySz; + ret = WH_ERROR_OK; } - /* Copy the metadata and key from the response data into metadataOut and - * keyOut */ - respData = (uint8_t*)(resp + 1); - memcpy(metadataOut, respData, sizeof(*metadataOut)); - memcpy(keyOut, respData + sizeof(*metadataOut), resp->keySz); - *keyInOutSz = resp->keySz; + /* The response received into the shared comm buffer holds the recovered + * plaintext key; zeroize the whole received payload before returning on + * every path so it does not linger in the long-lived session buffer. */ + wh_Utils_ForceZero(resp, size); - return WH_ERROR_OK; + return ret; } int wh_Client_KeyUnwrapAndExport(whClientContext* ctx, diff --git a/src/wh_transport_mem.c b/src/wh_transport_mem.c index 92e74d4c2..b08a84c08 100644 --- a/src/wh_transport_mem.c +++ b/src/wh_transport_mem.c @@ -214,6 +214,17 @@ int wh_TransportMem_SendResponse(void* c, uint16_t len, const void* data) wh_Utils_memcpy_flush(context->resp_data, data, len); } + /* The request contents were already copied out at RecvRequest time and the + * response is now ready, so the shared request data buffer is no longer + * needed. Clear it (with a cache flush, like all shared-buffer writes) so it + * does not retain a (possibly sensitive) request payload for another party + * mapping the shared region to read. This must happen BEFORE publishing the + * response CSR below: that publish releases the client to issue its next + * request into req_data, so clearing afterwards could race that write. */ + if (req.s.len != 0) { + (void)wh_Utils_memset_flush(context->req_data, 0, req.s.len); + } + resp.s.len = len; resp.s.notify = req.s.notify; diff --git a/test/wh_test.c b/test/wh_test.c index 6bfc6c4ce..3546c5798 100644 --- a/test/wh_test.c +++ b/test/wh_test.c @@ -73,6 +73,429 @@ #include "wolfhsm/wh_server.h" #if defined(WOLFHSM_CFG_ENABLE_CLIENT) && defined(WOLFHSM_CFG_ENABLE_SERVER) + +/* =========================================================================== + * Key-material remanence regression tests (#5473/#5474/#6030/#5476). + * + * Each test runs a real client/server operation and then asserts that no copy + * of the secret key bytes is left behind in the long-lived/shared buffers it + * passed through. These guard against a future deletion of the + * wh_Utils_ForceZero / wh_Utils_memset_flush calls (such a deletion does not + * change any functional behaviour, so only a test like this would catch it). + * =========================================================================== */ +#if defined(WOLFHSM_CFG_TEST_POSIX) + +#include +#include + +#include "wolfhsm/wh_common.h" +#include "wolfhsm/wh_comm.h" +#include "wolfhsm/wh_transport_mem.h" +#include "wolfhsm/wh_nvm.h" +#include "wolfhsm/wh_nvm_flash.h" +#include "wolfhsm/wh_flash_ramsim.h" + +#if !defined(WOLFHSM_CFG_NO_CRYPTO) +#include "wolfssl/wolfcrypt/settings.h" +#include "wolfssl/wolfcrypt/types.h" +#endif + +#if defined(WOLFHSM_CFG_KEYWRAP) && !defined(WOLFHSM_CFG_NO_CRYPTO) && \ + defined(HAVE_AESGCM) +#include "wolfhsm/wh_client_crypto.h" +#endif + +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_STATIC_MEMORY) +#include "wolfssl/wolfcrypt/memory.h" +#include "wolfhsm/wh_dma.h" +#include "port/posix/posix_transport_shm.h" +#endif + +#define REM_CLIENT_ID 1 +#define REM_KEKID 10 +#define REM_AES_KEYSZ 32 + +#define REM_FLASH_RAM_SIZE (1024 * 1024) +#define REM_FLASH_SECTOR_SIZE (128 * 1024) +#define REM_FLASH_PAGE_SIZE (8) + +enum { + REM_BUFFER_SIZE = sizeof(whTransportMemCsr) + sizeof(whCommHeader) + + WOLFHSM_CFG_COMM_DATA_LEN, +}; + +/* server thread plumbing */ +static volatile int g_remServerRun = 1; + +static void* remServerTask(void* arg) +{ + whServerContext* server = (whServerContext*)arg; + int ret; + + while (g_remServerRun) { + ret = wh_Server_HandleRequestMessage(server); + if (ret != WH_ERROR_NOTREADY && ret != WH_ERROR_OK) { + WH_ERROR_PRINT("[remanence server] HandleRequestMessage %d\n", ret); + break; + } + } + return NULL; +} + +/* Return non-zero (and print) if needle is found anywhere in hay. */ +static int remContains(const uint8_t* hay, size_t haySz, const uint8_t* needle, + size_t needleSz) +{ + size_t i; + if (haySz < needleSz) { + return 0; + } + for (i = 0; i + needleSz <= haySz; i++) { + if (memcmp(hay + i, needle, needleSz) == 0) { + return 1; + } + } + return 0; +} + +/* #5473: importing (caching) a key must not leave the imported key bytes in the + * client comm buffer or in the shared transport request buffer. */ +static int remTest_KeyCacheImport(whClientContext* client, const uint8_t* reqBuf, + size_t reqBufSz) +{ + whKeyId keyId = WH_KEYID_ERASED; + uint8_t label[WH_NVM_LABEL_LEN] = "rem-import"; + uint8_t key[REM_AES_KEYSZ]; + const uint8_t* commData; + int i; + + for (i = 0; i < REM_AES_KEYSZ; i++) { + key[i] = (uint8_t)(0xC5 ^ i); + } + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCache(client, WH_NVM_FLAGS_NONE, label, + sizeof(label), key, sizeof(key), + &keyId)); + + commData = wh_CommClient_GetDataPtr(client->comm); + if (remContains(commData, WOLFHSM_CFG_COMM_DATA_LEN, key, sizeof(key))) { + WH_ERROR_PRINT("#5473: imported key left in client comm buffer\n"); + return WH_TEST_FAIL; + } + if (remContains(reqBuf, reqBufSz, key, sizeof(key))) { + WH_ERROR_PRINT("#5473: imported key left in shared request buffer\n"); + return WH_TEST_FAIL; + } + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); + return WH_ERROR_OK; +} + +/* #5474: exporting a cached key must not leave the exported key bytes in the + * client comm buffer after copy-out. */ +static int remTest_KeyExport(whClientContext* client) +{ + whKeyId keyId = WH_KEYID_ERASED; + uint8_t label[WH_NVM_LABEL_LEN] = "rem-export"; + uint8_t key[REM_AES_KEYSZ]; + uint8_t outKey[REM_AES_KEYSZ] = {0}; + uint16_t outSz = sizeof(outKey); + uint8_t outLabel[WH_NVM_LABEL_LEN] = {0}; + const uint8_t* commData; + int i; + + for (i = 0; i < REM_AES_KEYSZ; i++) { + key[i] = (uint8_t)(0x74 ^ i); + } + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCache(client, WH_NVM_FLAGS_NONE, label, + sizeof(label), key, sizeof(key), + &keyId)); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyExport(client, keyId, outLabel, + sizeof(outLabel), outKey, + &outSz)); + + /* sanity: we actually got the key back */ + if (outSz != REM_AES_KEYSZ || memcmp(outKey, key, REM_AES_KEYSZ) != 0) { + WH_ERROR_PRINT("#5474: export did not return the expected key\n"); + return WH_TEST_FAIL; + } + + commData = wh_CommClient_GetDataPtr(client->comm); + if (remContains(commData, WOLFHSM_CFG_COMM_DATA_LEN, key, sizeof(key))) { + WH_ERROR_PRINT("#5474: exported key left in client comm buffer\n"); + return WH_TEST_FAIL; + } + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, keyId)); + return WH_ERROR_OK; +} + +#if defined(WOLFHSM_CFG_KEYWRAP) && !defined(WOLFHSM_CFG_NO_CRYPTO) && \ + defined(HAVE_AESGCM) +/* #6030: unwrap-and-export must not leave the recovered plaintext key in the + * client comm buffer after copy-out. */ +static int remTest_KeyUnwrapAndExport(whClientContext* client) +{ + whKeyId kekId = REM_KEKID; + uint8_t keklabel[WH_NVM_LABEL_LEN] = "rem-kek"; + const uint8_t kek[REM_AES_KEYSZ] = { + 0x03, 0x03, 0x0d, 0xd9, 0xeb, 0x18, 0x17, 0x2e, 0x06, 0x6e, 0x19, + 0xce, 0x98, 0x44, 0x54, 0x0d, 0x78, 0xa0, 0xbe, 0xe7, 0x35, 0x43, + 0x40, 0xa4, 0x22, 0x8a, 0xd1, 0x0e, 0xa3, 0x63, 0x1c, 0x0b}; + uint8_t plainKey[REM_AES_KEYSZ]; + uint8_t wrapped[WH_KEYWRAP_AES_GCM_HEADER_SIZE + REM_AES_KEYSZ + + sizeof(whNvmMetadata)]; + uint16_t wrappedSz = sizeof(wrapped); + whNvmMetadata meta = {0}; + whNvmMetadata outMeta = {0}; + uint8_t outKey[REM_AES_KEYSZ] = {0}; + uint16_t outKeySz = sizeof(outKey); + const uint8_t* commData; + int i; + + for (i = 0; i < REM_AES_KEYSZ; i++) { + plainKey[i] = (uint8_t)(0x60 ^ i); + } + meta.id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(REM_CLIENT_ID, 20); + meta.len = REM_AES_KEYSZ; + meta.flags = WH_NVM_FLAGS_USAGE_ANY; + memcpy(meta.label, "rem-wrapme", sizeof("rem-wrapme")); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyCache( + client, WH_NVM_FLAGS_NONEXPORTABLE | WH_NVM_FLAGS_USAGE_WRAP, keklabel, + sizeof(keklabel), (uint8_t*)kek, sizeof(kek), &kekId)); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrap(client, WC_CIPHER_AES_GCM, + REM_KEKID, plainKey, + sizeof(plainKey), &meta, wrapped, + &wrappedSz)); + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyUnwrapAndExport( + client, WC_CIPHER_AES_GCM, REM_KEKID, wrapped, wrappedSz, &outMeta, + outKey, &outKeySz)); + + if (outKeySz != REM_AES_KEYSZ || + memcmp(outKey, plainKey, REM_AES_KEYSZ) != 0) { + WH_ERROR_PRINT("#6030: unwrap+export did not return the expected key\n"); + return WH_TEST_FAIL; + } + + commData = wh_CommClient_GetDataPtr(client->comm); + if (remContains(commData, WOLFHSM_CFG_COMM_DATA_LEN, plainKey, + sizeof(plainKey))) { + WH_ERROR_PRINT("#6030: recovered key left in client comm buffer\n"); + return WH_TEST_FAIL; + } + + WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, REM_KEKID)); + return WH_ERROR_OK; +} +#endif /* WOLFHSM_CFG_KEYWRAP && !NO_CRYPTO && HAVE_AESGCM */ + +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_STATIC_MEMORY) +/* #5476: the POSIX shm DMA bounce buffer must be zeroized before it is freed + * back to the shared static-memory DMA heap. Exercises the callback directly. */ +static int remTest_DmaBounce(void) +{ + static uint8_t dmaRegion[8000] __attribute__((aligned(16))); + const word32 sizeList[1] = {128}; + const word32 distList[1] = {16}; + WOLFSSL_HEAP_HINT* hint = NULL; + int ret; + int i; + uint8_t secret[REM_AES_KEYSZ]; + void* xformed = NULL; + whDmaFlags flags = {0}; + uint8_t* slot; + + posixTransportShmContext shmCtx; + whClientContext client; + + for (i = 0; i < REM_AES_KEYSZ; i++) { + secret[i] = (uint8_t)(0xA0 + i); + } + memset(dmaRegion, 0, sizeof(dmaRegion)); + memset(&shmCtx, 0, sizeof(shmCtx)); + memset(&client, 0, sizeof(client)); + + ret = wc_LoadStaticMemory_ex(&hint, 1, sizeList, distList, dmaRegion, + (word32)sizeof(dmaRegion), 0, 0); + if (ret != 0) { + WH_ERROR_PRINT("#5476: wc_LoadStaticMemory_ex %d\n", ret); + return WH_TEST_FAIL; + } + + shmCtx.dma = dmaRegion; + shmCtx.dma_size = sizeof(dmaRegion); + shmCtx.heap = (void*)hint; + client.comm->transport_context = (void*)&shmCtx; + + /* READ path: client input copied into a bounce buffer, then freed. */ + ret = posixTransportShm_ClientStaticMemDmaCallback( + &client, (uintptr_t)secret, &xformed, REM_AES_KEYSZ, + WH_DMA_OPER_CLIENT_READ_PRE, flags); + if (ret != WH_ERROR_OK) { + WH_ERROR_PRINT("#5476: READ_PRE %d\n", ret); + return WH_TEST_FAIL; + } + slot = dmaRegion + (uintptr_t)xformed; + ret = posixTransportShm_ClientStaticMemDmaCallback( + &client, (uintptr_t)secret, &xformed, REM_AES_KEYSZ, + WH_DMA_OPER_CLIENT_READ_POST, flags); + if (ret != WH_ERROR_OK) { + WH_ERROR_PRINT("#5476: READ_POST %d\n", ret); + return WH_TEST_FAIL; + } + if (remContains(slot, REM_AES_KEYSZ, secret, REM_AES_KEYSZ)) { + WH_ERROR_PRINT("#5476: client input left in freed DMA bounce buffer\n"); + return WH_TEST_FAIL; + } + + return WH_ERROR_OK; +} +#endif /* WOLFHSM_CFG_DMA && WOLFSSL_STATIC_MEMORY */ + +static int whTest_Remanence(void) +{ + int ret = WH_ERROR_OK; + + /* shared transport buffers (we scan req[] for #5473) */ + static uint8_t req[REM_BUFFER_SIZE]; + static uint8_t resp[REM_BUFFER_SIZE]; + whTransportMemConfig tmcf[1] = {{ + .req = (whTransportMemCsr*)req, + .req_size = sizeof(req), + .resp = (whTransportMemCsr*)resp, + .resp_size = sizeof(resp), + }}; + + 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 = REM_CLIENT_ID, + }}; + whClientConfig c_conf[1] = {{.comm = cc_conf}}; + whClientContext client[1] = {0}; + + 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 = 124, + }}; + + static uint8_t memory[REM_FLASH_RAM_SIZE]; + whFlashRamsimCtx fc[1] = {0}; + whFlashRamsimCfg fc_conf[1] = {{ + .size = REM_FLASH_RAM_SIZE, + .sectorSize = REM_FLASH_SECTOR_SIZE, + .pageSize = REM_FLASH_PAGE_SIZE, + .erasedByte = ~(uint8_t)0, + .memory = memory, + }}; + const whFlashCb fcb[1] = {WH_FLASH_RAMSIM_CB}; + whNvmFlashConfig nvmFlashCfg = {.cb = fcb, .context = fc, .config = fc_conf}; + whNvmFlashContext nvmFlashCtx = {0}; + whNvmCb nfcb[1] = {WH_NVM_FLASH_CB}; + whNvmConfig n_conf[1] = {{.cb = nfcb, + .context = &nvmFlashCtx, + .config = &nvmFlashCfg}}; + whNvmContext nvm[1] = {{0}}; + +#ifndef WOLFHSM_CFG_NO_CRYPTO + whServerCryptoContext crypto[1] = {0}; +#endif + whServerConfig s_conf[1] = {{ + .comm_config = cs_conf, + .nvm = nvm, +#ifndef WOLFHSM_CFG_NO_CRYPTO + .crypto = crypto, + .devId = INVALID_DEVID, +#endif + }}; + whServerContext server[1] = {0}; + pthread_t sthread; + + memset(memory, 0, sizeof(memory)); + memset(req, 0, sizeof(req)); + memset(resp, 0, sizeof(resp)); + + WH_TEST_PRINT("Testing key-material remanence (#5473/#5474/#6030/#5476)\n"); + +#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 + WH_TEST_RETURN_ON_FAIL(wh_Nvm_Init(nvm, n_conf)); + WH_TEST_RETURN_ON_FAIL(wh_Server_Init(server, s_conf)); + WH_TEST_RETURN_ON_FAIL(wh_Server_SetConnected(server, WH_COMM_CONNECTED)); + server->comm->client_id = REM_CLIENT_ID; + + g_remServerRun = 1; + if (pthread_create(&sthread, NULL, remServerTask, server) != 0) { + WH_ERROR_PRINT("remanence: pthread_create failed\n"); + ret = WH_ERROR_ABORTED; + goto out_server; + } + + WH_TEST_RETURN_ON_FAIL(wh_Client_Init(client, c_conf)); + ret = wh_Client_CommInit(client, NULL, NULL); + if (ret != WH_ERROR_OK) { + WH_ERROR_PRINT("remanence: CommInit %d\n", ret); + goto out_client; + } + + ret = remTest_KeyCacheImport(client, req, sizeof(req)); + if (ret == WH_ERROR_OK) { + ret = remTest_KeyExport(client); + } +#if defined(WOLFHSM_CFG_KEYWRAP) && !defined(WOLFHSM_CFG_NO_CRYPTO) && \ + defined(HAVE_AESGCM) + if (ret == WH_ERROR_OK) { + ret = remTest_KeyUnwrapAndExport(client); + } +#endif + + (void)wh_Client_CommClose(client); +out_client: + (void)wh_Client_Cleanup(client); +out_server: + g_remServerRun = 0; + pthread_join(sthread, NULL); + (void)wh_Server_Cleanup(server); + (void)wh_Nvm_Cleanup(nvm); +#ifndef WOLFHSM_CFG_NO_CRYPTO + (void)wc_FreeRng(crypto->rng); + (void)wolfCrypt_Cleanup(); +#endif + +#if defined(WOLFHSM_CFG_DMA) && defined(WOLFSSL_STATIC_MEMORY) + if (ret == WH_ERROR_OK) { + ret = remTest_DmaBounce(); + } +#endif + + if (ret == WH_ERROR_OK) { + WH_TEST_PRINT("REMANENCE TESTS SUCCESS\n"); + } + return ret; +} + +#else /* !WOLFHSM_CFG_TEST_POSIX */ +static int whTest_Remanence(void) +{ + /* Requires an in-process client+server POSIX build */ + return WH_ERROR_OK; +} +#endif /* WOLFHSM_CFG_TEST_POSIX */ + int whTest_Unit(void) { WH_TEST_PRINT("Enter unit tests\n"); @@ -105,6 +528,9 @@ int whTest_Unit(void) WH_TEST_ASSERT(0 == whTest_Comm()); WH_TEST_ASSERT(0 == whTest_ClientServer()); + /* Key-material remanence regression tests (#5473/#5474/#6030/#5476) */ + WH_TEST_ASSERT(0 == whTest_Remanence()); + #ifdef WOLFHSM_CFG_ENABLE_AUTHENTICATION /* Auth tests */ WH_TEST_ASSERT(0 == whTest_AuthMEM());