From 7da5315a6cd5564dd9c7432d901135f98d3d7e49 Mon Sep 17 00:00:00 2001 From: Paul Adelsbach Date: Thu, 28 May 2026 14:55:04 -0700 Subject: [PATCH] Add refactored client NVM tests --- test-refactor/README.md | 5 +- test-refactor/client-server/wh_test_counter.c | 173 +++++++ test-refactor/client-server/wh_test_nvm_ops.c | 460 ++++++++++++++++++ test-refactor/config/wolfhsm_cfg.h | 1 + test-refactor/wh_test_list.c | 6 + 5 files changed, 644 insertions(+), 1 deletion(-) create mode 100644 test-refactor/client-server/wh_test_counter.c create mode 100644 test-refactor/client-server/wh_test_nvm_ops.c diff --git a/test-refactor/README.md b/test-refactor/README.md index cd37861f9..761d9597d 100644 --- a/test-refactor/README.md +++ b/test-refactor/README.md @@ -83,6 +83,9 @@ Translated tests: | `wh_test_cert.c::whTest_CertRamSim` | `server/wh_test_cert.c::whTest_CertVerify` | Server | remove ramsim coupling and migrate to server group | | `wh_test_crypto.c::whTest_Crypto` | `client-server/wh_test_crypto.c::{whTest_CryptoSha256, whTest_CryptoAes, whTest_CryptoEcc256}` | Client | Subset only; remaining cases listed below | | `wh_test_clientserver.c` (echo and server-info paths) | `client-server/wh_test_echo.c::whTest_Echo`, `client-server/wh_test_server_info.c::whTest_ServerInfo` | Client | pthread test ported, sequential test dropped | +| `wh_test_clientserver.c` (NVM CRUD + OOB read clamping paths) | `client-server/wh_test_nvm_ops.c::{whTest_NvmCrud, whTest_NvmReadOob}` | Client | each test cleans up its own slots; OOB test covers UINT16_MAX overflow regression | +| `wh_test_clientserver.c` (NVM DMA CRUD path) | `client-server/wh_test_nvm_dma.c::whTest_NvmCrudDma` | Client | gated on `WOLFHSM_CFG_DMA` | +| `wh_test_clientserver.c::_testClientCounter` | `client-server/wh_test_counter.c::whTest_Counter` | Client | exercises saturate-on-overflow and slot-leak detection | | `wh_test_wolfcrypt_test.c::whTest_WolfCryptTest` | `client-server/wh_test_wolfcrypt.c::whTest_WolfCryptTest` | Client | | | `wh_test_flash_ramsim.c::whTest_Flash_RamSim` | `posix/wh_test_flash_ramsim.c::{whTest_FlashWriteLock, whTest_FlashEraseProgramVerify, whTest_FlashUnitOps}` | POSIX port-specific (`whTestGroup_RunOne`) | remove ramsim coupling and migrate to server group | | `wh_test_nvm_flash.c::whTest_NvmFlash` | `posix/wh_test_nvm_flash.c::whTest_NvmAddOverwriteDestroy` | POSIX port-specific (`whTestGroup_RunOne`) | remove ramsim coupling and migrate to server group | @@ -93,7 +96,7 @@ Not yet migrated (still live in `wolfHSM/test/`): | Legacy (`wolfHSM/test/`) | Notes | |---|---| | `wh_test_comm.c::whTest_Comm` | Pthread mem/tcp/shmem variants only; sequential mem variant has been ported | -| `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_clientserver.c::whTest_ClientServer` | Pthread variant: remaining coverage is the custom-callback round-trip (`_testCallbacks`) and the server-side DMA register/copy/allowlist exercise (`_testDma`). The sequential test is dropped, as is the FLASH_LOG NVM matrix variant. | | `wh_test_crypto.c::whTest_Crypto` | RNG, key cache, key-cache enforcement, RSA, CMAC, Curve25519, ML-DSA, key usage policies, key revocation | | `wh_test_crypto_affinity.c::whTest_CryptoAffinity` | | | `wh_test_keywrap.c::whTest_KeyWrapClientConfig` | | diff --git a/test-refactor/client-server/wh_test_counter.c b/test-refactor/client-server/wh_test_counter.c new file mode 100644 index 000000000..960f051f9 --- /dev/null +++ b/test-refactor/client-server/wh_test_counter.c @@ -0,0 +1,173 @@ +/* + * 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/client-server/wh_test_counter.c + * + * Exercise the persistent NVM counter API: init/reset, + * sequential increment past WOLFHSM_CFG_NVM_OBJECT_COUNT (catches + * slot leaks), saturate-on-overflow at UINT32_MAX, and + * reset+destroy across many slots. + */ + +#include +#include + +#include "wolfhsm/wh_settings.h" +#include "wolfhsm/wh_common.h" +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_client.h" + +#include "wh_test_common.h" +#include "wh_test_list.h" + + +/* + * Verify counter can update more than WOLFHSM_CFG_NVM_OBJECT_COUNT times. + * Each increment should reuse the same slot. + */ +static int _whTest_CounterSequentialIncrement(whClientContext* ctx) +{ + const whNvmId counterId = 1; + const size_t NUM_INCREMENTS = 2u * WOLFHSM_CFG_NVM_OBJECT_COUNT; + size_t i; + uint32_t counter = 0; + + WH_TEST_RETURN_ON_FAIL( + wh_Client_CounterReset(ctx, counterId, &counter)); + WH_TEST_ASSERT_RETURN(counter == 0); + + for (i = 0; i < NUM_INCREMENTS; i++) { + WH_TEST_RETURN_ON_FAIL( + wh_Client_CounterIncrement(ctx, counterId, &counter)); + WH_TEST_ASSERT_RETURN(counter == (uint32_t)(i + 1)); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_CounterRead(ctx, counterId, &counter)); + WH_TEST_ASSERT_RETURN(counter == (uint32_t)(i + 1)); + } + + WH_TEST_RETURN_ON_FAIL( + wh_Client_CounterReset(ctx, counterId, &counter)); + WH_TEST_ASSERT_RETURN(counter == 0); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_CounterDestroy(ctx, counterId)); + + return WH_ERROR_OK; +} + + +/* + * Verify the counter saturates at UINT32_MAX and does not wrap. + */ +static int _whTest_CounterSaturate(whClientContext* ctx) +{ + const whNvmId counterId = 1; + const uint32_t MAX_COUNTER = 0xFFFFFFFFu; + uint32_t counter = MAX_COUNTER; + + WH_TEST_RETURN_ON_FAIL( + wh_Client_CounterInit(ctx, counterId, &counter)); + WH_TEST_ASSERT_RETURN(counter == MAX_COUNTER); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_CounterIncrement(ctx, counterId, &counter)); + WH_TEST_ASSERT_RETURN(counter == MAX_COUNTER); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_CounterIncrement(ctx, counterId, &counter)); + WH_TEST_ASSERT_RETURN(counter == MAX_COUNTER); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_CounterRead(ctx, counterId, &counter)); + WH_TEST_ASSERT_RETURN(counter == MAX_COUNTER); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_CounterReset(ctx, counterId, &counter)); + WH_TEST_ASSERT_RETURN(counter == 0); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_CounterDestroy(ctx, counterId)); + + return WH_ERROR_OK; +} + + +/* + * Reset+destroy across many slots: catches leaks in the destroy + * path and confirms that a destroyed counter reads back as + * NOTFOUND. + */ +static int _whTest_CounterDestroyMany(whClientContext* ctx) +{ + const size_t NUM_SLOTS = 2u * WOLFHSM_CFG_NVM_OBJECT_COUNT; + size_t i; + uint32_t counter; + + for (i = 1; i < NUM_SLOTS; i++) { + WH_TEST_RETURN_ON_FAIL( + wh_Client_CounterReset(ctx, (whNvmId)i, &counter)); + WH_TEST_ASSERT_RETURN(counter == 0); + + WH_TEST_RETURN_ON_FAIL( + wh_Client_CounterDestroy(ctx, (whNvmId)i)); + + WH_TEST_ASSERT_RETURN(WH_ERROR_NOTFOUND == + wh_Client_CounterRead(ctx, (whNvmId)i, &counter)); + } + + return WH_ERROR_OK; +} + + +int whTest_Counter(whClientContext* ctx) +{ + int32_t server_rc = 0; + uint32_t client_id = 0; + uint32_t server_id = 0; + uint32_t avail_size = 0; + uint32_t reclaim_size = 0; + whNvmId avail_objects = 0; + whNvmId reclaim_objects = 0; + whNvmId baseline = 0; + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmInit( + ctx, &server_rc, &client_id, &server_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmGetAvailable( + ctx, &server_rc, &avail_size, &avail_objects, + &reclaim_size, &reclaim_objects)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + baseline = avail_objects; + + WH_TEST_RETURN_ON_FAIL(_whTest_CounterSequentialIncrement(ctx)); + WH_TEST_RETURN_ON_FAIL(_whTest_CounterSaturate(ctx)); + WH_TEST_RETURN_ON_FAIL(_whTest_CounterDestroyMany(ctx)); + + /* No object slots leaked: available count is back where we + * started before the test ran. */ + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmGetAvailable( + ctx, &server_rc, &avail_size, &avail_objects, + &reclaim_size, &reclaim_objects)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(avail_objects == baseline); + + return WH_ERROR_OK; +} diff --git a/test-refactor/client-server/wh_test_nvm_ops.c b/test-refactor/client-server/wh_test_nvm_ops.c new file mode 100644 index 000000000..92ecb3020 --- /dev/null +++ b/test-refactor/client-server/wh_test_nvm_ops.c @@ -0,0 +1,460 @@ +/* + * 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/client-server/wh_test_nvm_ops.c + * + * NVM object lifecycle tests over both the blocking and DMA + * client APIs. The Add/Update/List/Destroy body is shared via + * a small dispatch struct (WhNvmTestObjectOps) so the same + * coverage applies to both transports. The DMA entry point + * only compiles when WOLFHSM_CFG_DMA is defined; otherwise the + * weak stub in wh_test_list reports it as skipped. + */ + +#include +#include +#include + +#include "wolfhsm/wh_settings.h" +#include "wolfhsm/wh_common.h" +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_client.h" + +#include "wh_test_common.h" +#include "wh_test_list.h" + +#define NVM_TEST_OBJECT_COUNT 5 +#define NVM_TEST_OBJECT_ID_BASE 20 +#define NVM_TEST_OOB_ID 30 +#define NVM_TEST_DMA_ID_BASE 40 + + +/* + * Helpers to unify the DMA and non-DMA test functions. + */ +typedef int (*WhNvmTestObjectAddFn)(whClientContext* ctx, whNvmId id, + whNvmAccess access, whNvmFlags flags, + const uint8_t* label, whNvmSize label_len, + const uint8_t* data, whNvmSize data_len, + int32_t* server_rc); + +typedef int (*WhNvmTestObjectReadFn)(whClientContext* ctx, whNvmId id, + whNvmSize offset, whNvmSize len, + uint8_t* buf, whNvmSize* out_len, + int32_t* server_rc); + +typedef struct { + WhNvmTestObjectAddFn add; + WhNvmTestObjectReadFn read; +} WhNvmTestObjectOps; + + +static int _nvmIdInRange(whNvmId id, whNvmId base, whNvmId count) +{ + return (id >= base) && (id < (whNvmId)(base + count)); +} + + +static int _destroyNvmId(whClientContext* ctx, whNvmId id) +{ + int32_t server_rc = 0; + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmDestroyObjects( + ctx, 1, &id, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmGetMetadata( + ctx, id, &server_rc, NULL, NULL, NULL, NULL, 0, NULL)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_NOTFOUND); + + return WH_ERROR_OK; +} + + +/* + * Add (or re-Add) one object via ops->add and verify: + * - avail_objects drops by 1 (a new slot is always consumed, + * even on re-Add, since the log is append-only) + * - reclaim_objects rises by reclaim_grow (0 for a fresh Add, + * 1 for a re-Add that supersedes a prior version) + * - GetMetadata reports the just-written id/access/flags/len/label + * - ops->read returns the just-written payload + */ +static int _addAndVerifyOne(whClientContext* ctx, + const WhNvmTestObjectOps* ops, whNvmId id, + whNvmAccess access, whNvmFlags flags, + const uint8_t* label, whNvmSize label_len, + const uint8_t* data, whNvmSize data_len, + int reclaim_grow) +{ + int32_t server_rc = 0; + uint32_t avail_size = 0; + uint32_t reclaim_size = 0; + whNvmId prev_avail = 0; + whNvmId prev_reclaim = 0; + whNvmId avail_objects = 0; + whNvmId reclaim_objects = 0; + + whNvmId gid = 0; + whNvmAccess gaccess = 0; + whNvmFlags gflags = 0; + whNvmSize glen = 0; + char glabel[WH_NVM_LABEL_LEN] = {0}; + uint8_t buf[WOLFHSM_CFG_COMM_DATA_LEN]; + whNvmSize rlen = 0; + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmGetAvailable( + ctx, &server_rc, &avail_size, &prev_avail, + &reclaim_size, &prev_reclaim)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_RETURN_ON_FAIL(ops->add( + ctx, id, access, flags, + label, label_len, data, data_len, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmGetAvailable( + ctx, &server_rc, &avail_size, &avail_objects, + &reclaim_size, &reclaim_objects)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(prev_avail - 1 == avail_objects); + WH_TEST_ASSERT_RETURN( + (whNvmId)(prev_reclaim + reclaim_grow) == reclaim_objects); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmGetMetadata( + ctx, id, &server_rc, + &gid, &gaccess, &gflags, &glen, + sizeof(glabel), (uint8_t*)glabel)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(gid == id); + WH_TEST_ASSERT_RETURN(gaccess == access); + WH_TEST_ASSERT_RETURN(gflags == flags); + WH_TEST_ASSERT_RETURN(glen == data_len); + WH_TEST_ASSERT_RETURN(memcmp(glabel, label, label_len) == 0); + + memset(buf, 0, sizeof(buf)); + WH_TEST_RETURN_ON_FAIL(ops->read( + ctx, id, 0, glen, + buf, &rlen, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(rlen == data_len); + WH_TEST_ASSERT_RETURN(memcmp(buf, data, data_len) == 0); + + return WH_ERROR_OK; +} + + +/* + * Create, Read, Update and Destroy test. + */ +static int _runNvmObjectTest(whClientContext* ctx, + const WhNvmTestObjectOps* ops, whNvmId id_base) +{ + int32_t server_rc = 0; + uint32_t client_id = 0; + uint32_t server_id = 0; + uint32_t avail_size = 0; + uint32_t reclaim_size = 0; + whNvmId avail_objects = 0; + whNvmId reclaim_objects = 0; + whNvmId baseline = 0; + char label[WH_NVM_LABEL_LEN]; + char data[WOLFHSM_CFG_COMM_DATA_LEN]; + whNvmSize label_len; + whNvmSize data_len; + int i; + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmInit( + ctx, &server_rc, &client_id, &server_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* Capture the starting available count so we don't assume + * the suite is running on a virgin NVM. */ + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmGetAvailable( + ctx, &server_rc, &avail_size, &avail_objects, + &reclaim_size, &reclaim_objects)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + baseline = avail_objects; + + /* Add phase: fresh objects, no reclaim activity expected. */ + for (i = 0; i < NVM_TEST_OBJECT_COUNT; i++) { + whNvmId id = (whNvmId)(id_base + i); + memset(label, 0, sizeof(label)); + label_len = (whNvmSize)snprintf(label, sizeof(label), + "Label:%d", id); + data_len = (whNvmSize)snprintf(data, sizeof(data), + "Data:%d Counter:%d", id, i); + WH_TEST_RETURN_ON_FAIL(_addAndVerifyOne( + ctx, ops, id, + WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_NONE, + (const uint8_t*)label, label_len, + (const uint8_t*)data, data_len, + 0)); + } + + /* Update phase: re-Add each id with new label and payload. */ + for (i = 0; i < NVM_TEST_OBJECT_COUNT; i++) { + whNvmId id = (whNvmId)(id_base + i); + memset(label, 0, sizeof(label)); + label_len = (whNvmSize)snprintf(label, sizeof(label), + "Upd:%d", id); + data_len = (whNvmSize)snprintf(data, sizeof(data), + "Updated:%d Iter:%d", id, i); + WH_TEST_RETURN_ON_FAIL(_addAndVerifyOne( + ctx, ops, id, + WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_NONE, + (const uint8_t*)label, label_len, + (const uint8_t*)data, data_len, + 1)); + } + + /* Verify List enumerates the ids we own without assuming + * the test owns unrelated objects that may already exist. */ + { + whNvmAccess list_access = WH_NVM_ACCESS_ANY; + whNvmFlags list_flags = WH_NVM_FLAGS_NONE; + whNvmId list_id = 0; + whNvmId list_count = 0; + whNvmId found = 0; + + do { + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmList( + ctx, list_access, list_flags, list_id, + &server_rc, &list_count, &list_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + if ((list_count > 0) && _nvmIdInRange( + list_id, id_base, NVM_TEST_OBJECT_COUNT)) { + found++; + } + } while (list_count > 0); + + WH_TEST_ASSERT_RETURN(found == NVM_TEST_OBJECT_COUNT); + } + + for (i = 0; i < NVM_TEST_OBJECT_COUNT; i++) { + WH_TEST_RETURN_ON_FAIL(_destroyNvmId( + ctx, (whNvmId)(id_base + i))); + } + + /* Cleanup -> Init round-trip leaves NVM in a usable state + * with no leftovers. */ + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmCleanup(ctx, &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmInit( + ctx, &server_rc, &client_id, &server_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmGetAvailable( + ctx, &server_rc, &avail_size, &avail_objects, + &reclaim_size, &reclaim_objects)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(avail_objects == baseline); + + return WH_ERROR_OK; +} + + +/* Blocking-API adapters. */ + +static int _nvmTestObjectAddBlocking(whClientContext* ctx, whNvmId id, + whNvmAccess access, whNvmFlags flags, + const uint8_t* label, whNvmSize label_len, + const uint8_t* data, whNvmSize data_len, + int32_t* server_rc) +{ + return wh_Client_NvmAddObject(ctx, id, access, flags, + label_len, (uint8_t*)label, + data_len, data, server_rc); +} + + +static int _nvmTestObjectReadBlocking(whClientContext* ctx, whNvmId id, + whNvmSize offset, whNvmSize len, + uint8_t* buf, whNvmSize* out_len, + int32_t* server_rc) +{ + return wh_Client_NvmRead(ctx, id, offset, len, + server_rc, out_len, buf); +} + + +static const WhNvmTestObjectOps g_blockingTestOps = { + _nvmTestObjectAddBlocking, + _nvmTestObjectReadBlocking, +}; + + +/* + * Exercises NvmRead's offset/length clamping and overflow + * safety. Adds a single object, runs the boundary cases, then + * destroys it. Blocking-only -- the DMA read API has no + * equivalent out_len reporting. + */ +static int _whTest_NvmOpsReadOob(whClientContext* ctx) +{ + const whNvmId id = NVM_TEST_OOB_ID; + int32_t server_rc = 0; + uint32_t client_id = 0; + uint32_t server_id = 0; + uint8_t buf[WOLFHSM_CFG_COMM_DATA_LEN]; + char label[WH_NVM_LABEL_LEN] = {0}; + char data[] = "OOB read clamping payload"; + whNvmSize label_len; + whNvmSize data_len = (whNvmSize)sizeof(data); + whNvmSize out_len; + whNvmId gid; + whNvmAccess gaccess; + whNvmFlags gflags; + whNvmSize meta_len; + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmInit( + ctx, &server_rc, &client_id, &server_id)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + label_len = (whNvmSize)snprintf(label, sizeof(label), "OOB:%u", id); + + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmAddObject( + ctx, id, WH_NVM_ACCESS_ANY, WH_NVM_FLAGS_NONE, + label_len, (uint8_t*)label, + data_len, (const uint8_t*)data, + &server_rc)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + + /* Confirm metadata length so we can phrase the rest of the + * checks against meta_len rather than the raw write size. */ + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmGetMetadata( + ctx, id, &server_rc, + &gid, &gaccess, &gflags, &meta_len, + 0, NULL)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(meta_len == data_len); + + /* len = meta_len + 1 -> clamp to meta_len */ + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead( + ctx, id, 0, (whNvmSize)(meta_len + 1), + &server_rc, &out_len, buf)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(out_len == meta_len); + + /* off=1, len=meta_len -> clamp to meta_len - 1 */ + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead( + ctx, id, 1, meta_len, + &server_rc, &out_len, buf)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(out_len == (whNvmSize)(meta_len - 1)); + + /* off=meta_len-1, len=meta_len -> clamp to 1 */ + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead( + ctx, id, (whNvmSize)(meta_len - 1), meta_len, + &server_rc, &out_len, buf)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(out_len == 1); + + /* off == meta_len, len = 0 -> BADARGS (no readable bytes) */ + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead( + ctx, id, meta_len, 0, + &server_rc, &out_len, buf)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_BADARGS); + + /* off == UINT16_MAX -> BADARGS. Regression for integer + * overflow in the offset+len bound check. */ + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead( + ctx, id, (whNvmSize)UINT16_MAX, 1, + &server_rc, &out_len, buf)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_BADARGS); + + /* off=meta_len/2, len=meta_len -> clamp to meta_len - off. + * Verifies the overflow-safe form of the bounds check. */ + WH_TEST_RETURN_ON_FAIL(wh_Client_NvmRead( + ctx, id, (whNvmSize)(meta_len / 2), meta_len, + &server_rc, &out_len, buf)); + WH_TEST_ASSERT_RETURN(server_rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN( + out_len == (whNvmSize)(meta_len - (meta_len / 2))); + + return _destroyNvmId(ctx, id); +} + + +int whTest_NvmOps(whClientContext* ctx) +{ + WH_TEST_RETURN_ON_FAIL(_runNvmObjectTest( + ctx, &g_blockingTestOps, NVM_TEST_OBJECT_ID_BASE)); + WH_TEST_RETURN_ON_FAIL(_whTest_NvmOpsReadOob(ctx)); + + return WH_ERROR_OK; +} + + +#ifdef WOLFHSM_CFG_DMA + +/* DMA-API adapters. */ + +static int _nvmTestObjectAddDma(whClientContext* ctx, whNvmId id, + whNvmAccess access, whNvmFlags flags, + const uint8_t* label, whNvmSize label_len, + const uint8_t* data, whNvmSize data_len, + int32_t* server_rc) +{ + whNvmMetadata meta = { + .id = id, + .access = access, + .flags = flags, + .len = 0, + .label = {0}, + }; + if (label_len > sizeof(meta.label)) { + label_len = sizeof(meta.label); + } + memcpy(meta.label, label, label_len); + return wh_Client_NvmAddObjectDma( + ctx, &meta, data_len, data, server_rc); +} + + +static int _nvmTestObjectReadDma(whClientContext* ctx, whNvmId id, + whNvmSize offset, whNvmSize len, + uint8_t* buf, whNvmSize* out_len, + int32_t* server_rc) +{ + int ret = wh_Client_NvmReadDma( + ctx, id, offset, len, buf, server_rc); + /* DMA read returns exactly len bytes on success; mirror that + * into out_len so the shared body's rlen check matches. */ + if (ret == 0 && *server_rc == WH_ERROR_OK) { + *out_len = len; + } + return ret; +} + + +static const WhNvmTestObjectOps g_dmaTestOps = { + _nvmTestObjectAddDma, + _nvmTestObjectReadDma, +}; + + +int whTest_NvmDma(whClientContext* ctx) +{ + return _runNvmObjectTest(ctx, &g_dmaTestOps, NVM_TEST_DMA_ID_BASE); +} + +#endif /* WOLFHSM_CFG_DMA */ diff --git a/test-refactor/config/wolfhsm_cfg.h b/test-refactor/config/wolfhsm_cfg.h index 320257637..9ae263cfb 100644 --- a/test-refactor/config/wolfhsm_cfg.h +++ b/test-refactor/config/wolfhsm_cfg.h @@ -53,6 +53,7 @@ #define WOLFHSM_CFG_KEYWRAP #endif +/* Test log-based NVM flash backend */ #define WOLFHSM_CFG_SERVER_NVM_FLASH_LOG /* WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS is intentionally NOT diff --git a/test-refactor/wh_test_list.c b/test-refactor/wh_test_list.c index ac29019a9..4a098518b 100644 --- a/test-refactor/wh_test_list.c +++ b/test-refactor/wh_test_list.c @@ -37,6 +37,7 @@ WH_TEST_DECL(whTest_Dma); WH_TEST_DECL(whTest_KeystoreReqSize); WH_TEST_DECL(whTest_CertVerify); WH_TEST_DECL(whTest_ClientCerts); +WH_TEST_DECL(whTest_Counter); WH_TEST_DECL(whTest_CryptoAes); WH_TEST_DECL(whTest_CryptoEcc256); WH_TEST_DECL(whTest_CryptoEd25519BufferTooSmall); @@ -44,6 +45,8 @@ WH_TEST_DECL(whTest_CryptoMlDsaBufferTooSmall); WH_TEST_DECL(whTest_CryptoRsaBufferTooSmall); WH_TEST_DECL(whTest_CryptoSha256); WH_TEST_DECL(whTest_Echo); +WH_TEST_DECL(whTest_NvmDma); +WH_TEST_DECL(whTest_NvmOps); WH_TEST_DECL(whTest_ServerInfo); WH_TEST_DECL(whTest_WolfCryptTest); @@ -61,6 +64,7 @@ const size_t whTestsServerCount = sizeof(whTestsServer) / sizeof(whTestsServer[0 const whTestCase whTestsClient[] = { { "whTest_ClientCerts", whTest_ClientCerts }, + { "whTest_Counter", whTest_Counter }, { "whTest_CryptoAes", whTest_CryptoAes }, { "whTest_CryptoEcc256", whTest_CryptoEcc256 }, { "whTest_CryptoEd25519BufferTooSmall", @@ -69,6 +73,8 @@ const whTestCase whTestsClient[] = { { "whTest_CryptoRsaBufferTooSmall", whTest_CryptoRsaBufferTooSmall }, { "whTest_CryptoSha256", whTest_CryptoSha256 }, { "whTest_Echo", whTest_Echo }, + { "whTest_NvmDma", whTest_NvmDma }, + { "whTest_NvmOps", whTest_NvmOps }, { "whTest_ServerInfo", whTest_ServerInfo }, { "whTest_WolfCryptTest", whTest_WolfCryptTest }, };