diff --git a/test-refactor/README.md b/test-refactor/README.md index 54fac5cb6..76cfcb4e3 100644 --- a/test-refactor/README.md +++ b/test-refactor/README.md @@ -81,6 +81,7 @@ Translated tests: | `wh_test_comm.c::whTest_Comm` | `misc/wh_test_comm.c::whTest_Comm` | Misc | Sequential mem variant only; pthread mem/tcp/shmem variants remain in the legacy harness | | `wh_test_keystore_reqsize.c::whTest_KeystoreReqSize` | `misc/wh_test_keystore_reqsize.c::whTest_KeystoreReqSize` | Misc | | | `wh_test_multiclient.c::whTest_MultiClient` | `misc/wh_test_multiclient.c::whTest_MultiClient` | Misc | Sequential variant only (no legacy pthread variant exists); body is a no-op when `WOLFHSM_CFG_GLOBAL_KEYS` is off | +| `wh_test_lock.c::whTest_LockConfig`, `whTest_LockPosix` | `misc/wh_test_lock.c::whTest_Lock` | Misc | Gated on `WOLFHSM_CFG_THREADSAFE`; only exercised under `make THREADSAFE=1`, otherwise reported SKIPPED. The two public entry points collapse into one registry entry that builds its own POSIX lock config | | `wh_test_cert.c::whTest_CertRamSim` | `server/wh_test_cert.c::whTest_CertVerify` | Server | remove ramsim coupling and migrate to server group. 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_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) | @@ -110,7 +111,11 @@ Not yet migrated (still live in `wolfHSM/test/`): | `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` | | +<<<<<<< HEAD | `wh_test_lock.c::whTest_LockConfig`, `whTest_LockPosix` | `whTest_LockConfig` to be reworked to fit the Misc group, likely with a context param. | +======= +| `wh_test_multiclient.c::whTest_MultiClient` | | +>>>>>>> 42998e6 (Add refactored tests for threadsafe lock, coverage identical) | `wh_test_log.c::whTest_Log`, `whTest_LogBackend_RunAll` | `whTest_LogBackend_RunAll` to be reworked to fit the Misc group, likely with a context param. | | `wh_test_timeout.c::whTest_TimeoutPosix` | | | `wh_test_server_img_mgr.c::whTest_ServerImgMgr` | | diff --git a/test-refactor/misc/wh_test_lock.c b/test-refactor/misc/wh_test_lock.c new file mode 100644 index 000000000..8cd4b60aa --- /dev/null +++ b/test-refactor/misc/wh_test_lock.c @@ -0,0 +1,269 @@ +/* + * 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_lock.c + * + * Super simple smoke tests to ensure a lock implementation has basic + * functionality. Pure unit tests of wh_lock.c plus one NVM-ramsim round + * trip with locking installed. Needs no client, server, or port fixture; + * the test builds its own POSIX lock config. + */ + +#include "wolfhsm/wh_settings.h" + +#ifdef WOLFHSM_CFG_THREADSAFE + +#include +#include + +#include "wolfhsm/wh_error.h" +#include "wolfhsm/wh_lock.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" + +#ifdef WOLFHSM_CFG_TEST_POSIX +#include "port/posix/posix_lock.h" +#define FLASH_RAM_SIZE (1024 * 1024) +#define FLASH_SECTOR_SIZE (4096) +#define FLASH_PAGE_SIZE (8) +#endif + + +/* Test: Lock init/cleanup lifecycle */ +static int _whTest_LockLifecycle(whLockConfig* lockConfig) +{ + whLock lock; + int rc; + + memset(&lock, 0, sizeof(lock)); + + WH_TEST_PRINT("Testing lock lifecycle...\n"); + + if (lockConfig == NULL) { + WH_TEST_PRINT(" Lock lifecycle: SKIPPED (no config provided)\n"); + return WH_ERROR_OK; + } + + /* Init should succeed */ + rc = wh_Lock_Init(&lock, lockConfig); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + /* Acquire and release should work */ + rc = wh_Lock_Acquire(&lock); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + rc = wh_Lock_Release(&lock); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + /* Cleanup should succeed */ + rc = wh_Lock_Cleanup(&lock); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + /* After cleanup, acquire should fail */ + rc = wh_Lock_Acquire(&lock); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + + /* After cleanup, release should fail */ + rc = wh_Lock_Release(&lock); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + + /* Second cleanup should succeed (idempotent) */ + rc = wh_Lock_Cleanup(&lock); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + WH_TEST_PRINT(" Lock lifecycle: PASS\n"); + return WH_ERROR_OK; +} + +/* Test: NULL config results in no-op locking */ +static int _whTest_LockNullConfigNoOp(void) +{ + whLock lock; + int rc; + + memset(&lock, 0, sizeof(lock)); + + WH_TEST_PRINT("Testing NULL config no-op...\n"); + + /* Init with NULL config should succeed (no-op mode) */ + rc = wh_Lock_Init(&lock, NULL); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + /* Acquire/release should be no-ops returning OK */ + rc = wh_Lock_Acquire(&lock); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + rc = wh_Lock_Release(&lock); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + /* Cleanup should succeed */ + rc = wh_Lock_Cleanup(&lock); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + WH_TEST_PRINT(" NULL config no-op: PASS\n"); + return WH_ERROR_OK; +} + +/* Test: Operations on uninitialized lock should fail appropriately */ +static int _whTest_LockUninitialized(void) +{ + whLock lock; + int rc; + + WH_TEST_PRINT("Testing uninitialized lock...\n"); + + /* Create zeroed lock structure (no init call) */ + memset(&lock, 0, sizeof(lock)); + + /* Acquire on uninitialized lock should fail */ + rc = wh_Lock_Acquire(&lock); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + + /* Release on uninitialized lock should fail */ + rc = wh_Lock_Release(&lock); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_BADARGS); + + /* Cleanup on uninitialized lock should succeed (idempotent) */ + rc = wh_Lock_Cleanup(&lock); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + WH_TEST_PRINT(" Uninitialized lock: PASS\n"); + return WH_ERROR_OK; +} + +#ifdef WOLFHSM_CFG_TEST_POSIX +/* Test: NVM simulator with lock config. Restrict to POSIX port test due to + * resource utilization */ +static int _whTest_LockNvmRamSim(whLockConfig* lockConfig) +{ + /* Flash simulator */ + uint8_t flashMemory[FLASH_RAM_SIZE]; + const whFlashCb flashCb[1] = {WH_FLASH_RAMSIM_CB}; + whFlashRamsimCtx flashCtx[1] = {0}; + whFlashRamsimCfg flashCfg[1] = {{ + .size = FLASH_RAM_SIZE, + .sectorSize = FLASH_SECTOR_SIZE, + .pageSize = FLASH_PAGE_SIZE, + .erasedByte = 0xFF, + .memory = flashMemory, + }}; + + /* NVM flash layer */ + whNvmCb nvmFlashCb[1] = {WH_NVM_FLASH_CB}; + whNvmFlashContext nvmFlashCtx[1] = {0}; + whNvmFlashConfig nvmFlashCfg = { + .cb = flashCb, + .context = flashCtx, + .config = flashCfg, + }; + + /* NVM context with lock. Zero-init nvmCfg so any conditionally-compiled + * fields (e.g. certVerifyCacheLockConfig under + * WOLFHSM_CFG_CERTIFICATE_VERIFY_CACHE_GLOBAL) start as NULL = no-op + * locking, rather than as indeterminate stack garbage. */ + whNvmContext nvm = {0}; + whNvmConfig nvmCfg = {0}; + + whNvmMetadata meta; + uint8_t testData[] = "Hello, NVM with lock!"; + uint8_t readBuf[32]; + int rc; + + WH_TEST_PRINT("Testing NVM with lock...\n"); + + memset(flashMemory, 0xFF, sizeof(flashMemory)); + + /* Initialize NVM flash */ + rc = wh_NvmFlash_Init(nvmFlashCtx, &nvmFlashCfg); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + /* Set up NVM config with user-supplied lock */ + nvmCfg.cb = nvmFlashCb; + nvmCfg.context = nvmFlashCtx; + nvmCfg.config = &nvmFlashCfg; + nvmCfg.lockConfig = lockConfig; + + rc = wh_Nvm_Init(&nvm, &nvmCfg); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + /* Test basic NVM operations with locking */ + memset(&meta, 0, sizeof(meta)); + meta.id = 1; + meta.len = sizeof(testData); + + rc = wh_Nvm_AddObject(&nvm, &meta, sizeof(testData), testData); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + memset(readBuf, 0, sizeof(readBuf)); + rc = wh_Nvm_Read(&nvm, 1, 0, sizeof(testData), readBuf); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + WH_TEST_ASSERT_RETURN(memcmp(testData, readBuf, sizeof(testData)) == 0); + + /* Cleanup */ + rc = wh_Nvm_Cleanup(&nvm); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + rc = wh_NvmFlash_Cleanup(nvmFlashCtx); + WH_TEST_ASSERT_RETURN(rc == WH_ERROR_OK); + + WH_TEST_PRINT(" NVM with lock: PASS\n"); + return WH_ERROR_OK; +} +#endif /* WOLFHSM_CFG_TEST_POSIX */ + + +int whTest_Lock(void* ctx) +{ + /* Misc runner dispatches with NULL; no shared context at this stage, so the + * test owns its lock config (mirrors the legacy POSIX wrapper). */ + (void)ctx; + +#if defined(WOLFHSM_CFG_TEST_POSIX) + posixLockContext lockCtx = {0}; + whLockCb lockCb = POSIX_LOCK_CB; + whLockConfig lockConfig; + + lockConfig.cb = &lockCb; + lockConfig.context = &lockCtx; + lockConfig.config = NULL; /* Use default mutex attributes */ + + whLockConfig* lockConfigPtr = &lockConfig; +#else + /* No backend: lifecycle subtest self-skips on NULL config */ + whLockConfig* lockConfigPtr = NULL; +#endif + + WH_TEST_PRINT("Testing lock functionality...\n"); + + WH_TEST_RETURN_ON_FAIL(_whTest_LockLifecycle(lockConfigPtr)); + WH_TEST_RETURN_ON_FAIL(_whTest_LockNullConfigNoOp()); + WH_TEST_RETURN_ON_FAIL(_whTest_LockUninitialized()); +#ifdef WOLFHSM_CFG_TEST_POSIX + WH_TEST_RETURN_ON_FAIL(_whTest_LockNvmRamSim(lockConfigPtr)); +#endif + + WH_TEST_PRINT("Lock tests PASSED\n"); + return WH_ERROR_OK; +} + +#endif /* WOLFHSM_CFG_THREADSAFE */ diff --git a/test-refactor/wh_test_list.c b/test-refactor/wh_test_list.c index 683555463..ceb761843 100644 --- a/test-refactor/wh_test_list.c +++ b/test-refactor/wh_test_list.c @@ -45,6 +45,7 @@ WH_TEST_DECL(whTest_Comm); WH_TEST_DECL(whTest_Dma); WH_TEST_DECL(whTest_KeystoreReqSize); WH_TEST_DECL(whTest_MultiClient); +WH_TEST_DECL(whTest_Lock); WH_TEST_DECL(whTest_CertVerify); WH_TEST_DECL(whTest_NvmOptional); WH_TEST_DECL(whTest_ClientCerts); @@ -86,11 +87,12 @@ WH_TEST_DECL(whTest_AuthSetCredentials); WH_TEST_DECL(whTest_AuthRequestAuthorization); const whTestCase whTestsMisc[] = { - { "whTest_ClientDevId", whTest_ClientDevId }, - { "whTest_Comm", whTest_Comm }, - { "whTest_Dma", whTest_Dma }, - { "whTest_KeystoreReqSize", whTest_KeystoreReqSize }, - { "whTest_MultiClient", whTest_MultiClient }, + { "whTest_ClientDevId", whTest_ClientDevId }, + { "whTest_Comm", whTest_Comm }, + { "whTest_Dma", whTest_Dma }, + { "whTest_KeystoreReqSize", whTest_KeystoreReqSize }, + { "whTest_MultiClient", whTest_MultiClient }, + { "whTest_Lock", whTest_Lock}, }; const size_t whTestsMiscCount = ARRAY_SIZE(whTestsMisc);