Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion test-refactor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ Translated tests:
| `wh_test_crypto.c::whTest_Crypto` | `client-server/wh_test_crypto_{aes,cmac,curve25519,ecc,ed25519,kdf,keypolicy,mldsa,rng,rsa,sha}.c::whTest_Crypto_*` | Client | Split into per-algorithm suites; key revocation is gated by `WOLFHSM_CFG_TEST_ALLOW_PERSISTENT_NVM_ARTIFACTS`. Legacy ran FLASH and FLASH_LOG backends; the port runs the plain flash backend only -- FLASH_LOG re-run pending (see Known coverage gaps) |
| `wh_test_crypto.c::whTest_CryptoKeyUsagePolicies` (AES CTR/ECB/GCM subset) | `client-server/wh_test_crypto_aes.c::whTest_CryptoAesKeyUsagePolicies` | Client | AES-CTR/ECB/GCM key usage enforcement (non-DMA and DMA variants) |
| `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::_testClientCounter` | `client-server/wh_test_counter.c::whTest_Counter` | Client | NVM counter API (reset/init/increment/read/destroy, saturation, slot reuse). Config-agnostic: the counter messages do not use the cryptocb, so it runs unchanged under the DMA and non-DMA builds. The legacy global-NVM-empty check is replaced by an object census bracketing the test, so it is robust to the shared server holding other tests' objects |
| `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, whTest_NvmFlash_Recovery}` | `posix/wh_test_nvm_flash.c::{whTest_NvmAddOverwriteDestroy, whTest_NvmFlashLog, whTest_NvmRecovery}` | POSIX port-specific (`whTestGroup_RunOne`) | remove ramsim coupling and migrate to server group; flash-log backend exercised by `whTest_NvmFlashLog` (skipped unless `WOLFHSM_CFG_SERVER_NVM_FLASH_LOG`) |
Expand All @@ -99,7 +100,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 client-side coverage (NVM object add/read/list/destroy, including the DMA slice) still needs to be split out as new tests. The counter API is ported (see `whTest_Counter`); 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` | |
Expand Down
162 changes: 162 additions & 0 deletions test-refactor/client-server/wh_test_counter.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/
/*
* test-refactor/client-server/wh_test_counter.c
*
* NVM monotonic counter round-trips routed through the server. Covers
* reset/init/increment/read/destroy, increment saturation at the uint32_t
* max, and slot reuse across more counters than the NVM directory holds.
*
* The counter API is carried by dedicated counter messages, not the
* cryptocb, so it is identical across the DMA and non-DMA builds. The
* WOLFHSM_CFG_DMA build option is covered by compiling and running this
* test under that configuration, not by toggling the client DMA mode.
*/

#include <stdint.h>

#include "wolfhsm/wh_settings.h"
#include "wolfhsm/wh_error.h"
#include "wolfhsm/wh_common.h"
#include "wolfhsm/wh_client.h"

#include "wh_test_common.h"
#include "wh_test_list.h"

/* Increment well past the NVM directory size to confirm a single counter
* reuses one slot rather than leaking an object per increment. */
#define WH_TEST_COUNTER_INCREMENTS (2 * WOLFHSM_CFG_NVM_OBJECT_COUNT)

/* Reads the current count of free NVM objects, failing on a server error. */
static int _whTest_CounterAvailObjects(whClientContext* ctx,
whNvmId* outAvailObjects)
{
int32_t serverRc = 0;
uint32_t availSize = 0;
uint32_t reclaimSize = 0;
whNvmId availObjects = 0;
whNvmId reclaimObjects = 0;

WH_TEST_RETURN_ON_FAIL(wh_Client_NvmGetAvailable(
ctx, &serverRc, &availSize, &availObjects, &reclaimSize,
&reclaimObjects));
WH_TEST_ASSERT_RETURN(serverRc == WH_ERROR_OK);

*outAvailObjects = availObjects;
return WH_ERROR_OK;
}

/* Reset, increment past the directory size, and saturate a single counter. */
static int _whTest_CounterIncrement(whClientContext* ctx)
{
const whNvmId counterId = 1;
const uint32_t maxCounterVal = 0xFFFFFFFF;
size_t i = 0;
uint32_t counter = 0;

/* A fresh counter starts at zero. */
WH_TEST_RETURN_ON_FAIL(wh_Client_CounterReset(ctx, counterId, &counter));
WH_TEST_ASSERT_RETURN(counter == 0);

/* Each increment advances by one and read-back matches, with no object
* leak across more increments than the directory could hold. */
for (i = 0; i < WH_TEST_COUNTER_INCREMENTS; i++) {
WH_TEST_RETURN_ON_FAIL(
wh_Client_CounterIncrement(ctx, counterId, &counter));
WH_TEST_ASSERT_RETURN(counter == i + 1);

WH_TEST_RETURN_ON_FAIL(
wh_Client_CounterRead(ctx, counterId, &counter));
WH_TEST_ASSERT_RETURN(counter == i + 1);
}

WH_TEST_RETURN_ON_FAIL(wh_Client_CounterReset(ctx, counterId, &counter));
WH_TEST_ASSERT_RETURN(counter == 0);

/* Init near the max and confirm increments saturate instead of rolling. */
counter = maxCounterVal;
WH_TEST_RETURN_ON_FAIL(wh_Client_CounterInit(ctx, counterId, &counter));
WH_TEST_ASSERT_RETURN(counter == maxCounterVal);

WH_TEST_RETURN_ON_FAIL(
wh_Client_CounterIncrement(ctx, counterId, &counter));
WH_TEST_ASSERT_RETURN(counter == maxCounterVal);

WH_TEST_RETURN_ON_FAIL(
wh_Client_CounterIncrement(ctx, counterId, &counter));
WH_TEST_ASSERT_RETURN(counter == maxCounterVal);

WH_TEST_RETURN_ON_FAIL(wh_Client_CounterRead(ctx, counterId, &counter));
WH_TEST_ASSERT_RETURN(counter == maxCounterVal);

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;
}

/* Create and destroy counters across many ids, confirming destroy frees the
* slot and a destroyed counter can no longer be read. */
static int _whTest_CounterDestroy(whClientContext* ctx)
{
size_t i = 0;
uint32_t counter = 0;

for (i = 1; i < WH_TEST_COUNTER_INCREMENTS; 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));

/* A destroyed counter must not be readable. */
WH_TEST_ASSERT_RETURN(
WH_ERROR_NOTFOUND ==
wh_Client_CounterRead(ctx, (whNvmId)i, &counter));
}

return WH_ERROR_OK;
}

/*
* NVM counter API test. Brackets the sub-tests with an NVM object census so
* the no-leak guarantee holds regardless of any objects other tests left
* behind in the shared server.
*/
int whTest_Counter(whClientContext* ctx)
{
whNvmId baselineObjects = 0;
whNvmId finalObjects = 0;

WH_TEST_PRINT("Testing NVM counters...\n");

WH_TEST_RETURN_ON_FAIL(
_whTest_CounterAvailObjects(ctx, &baselineObjects));

WH_TEST_RETURN_ON_FAIL(_whTest_CounterIncrement(ctx));
WH_TEST_RETURN_ON_FAIL(_whTest_CounterDestroy(ctx));

/* Reset and destroy must not leak NVM objects. */
WH_TEST_RETURN_ON_FAIL(_whTest_CounterAvailObjects(ctx, &finalObjects));
WH_TEST_ASSERT_RETURN(finalObjects == baselineObjects);

return WH_ERROR_OK;
}
2 changes: 2 additions & 0 deletions test-refactor/wh_test_list.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ WH_TEST_DECL(whTest_CryptoSha256);
WH_TEST_DECL(whTest_She);
WH_TEST_DECL(whTest_SheMasterEcuKeyFallback);
WH_TEST_DECL(whTest_SheReqSizeChecking);
WH_TEST_DECL(whTest_Counter);
WH_TEST_DECL(whTest_Echo);
WH_TEST_DECL(whTest_ServerInfo);
WH_TEST_DECL(whTest_WolfCryptTest);
Expand Down Expand Up @@ -118,6 +119,7 @@ const whTestCase whTestsClient[] = {
{ "whTest_CryptoRsaBufferTooSmall", whTest_CryptoRsaBufferTooSmall },
{ "whTest_CryptoSha256", whTest_CryptoSha256 },
{ "whTest_She", whTest_She },
{ "whTest_Counter", whTest_Counter },
{ "whTest_Echo", whTest_Echo },
{ "whTest_ServerInfo", whTest_ServerInfo },
{ "whTest_WolfCryptTest", whTest_WolfCryptTest },
Expand Down
Loading