Skip to content
Merged
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
4 changes: 2 additions & 2 deletions test-refactor/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ Translated tests:
| `wh_test_server_img_mgr.c::whTest_ServerImgMgr` | `server/wh_test_server_img_mgr.c::whTest_ServerImgMgr` | Server | Per-method subtests (ECC P256, AES-CMAC, RSA2048, wolfBoot RSA4096, and -- under `WOLFHSM_CFG_CERTIFICATE_MANAGER` -- wolfBoot cert chain) run against the shared server context instead of each building its own server/NVM/transport. Each subtest scrubs the NVM objects and key-cache entries it creates so the group's shared NVM stays clean. 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_she.c` (SHE<->keywrap interop + `wh_She_TestWrappedInterop`) | `misc/wh_test_she_keywrap.c::whTest_SheKeywrapInterop` | Misc | needs a server whose NVM carries a `WH_NVM_FLAGS_TRUSTED` KEK, so it runs its own client/server pair with a sequential split-API pump; covers wrap-export by id (incl. the SHE slot-0 gate regression), key-wrap vs data-wrap domain separation, the unwrap-and-export type gate (a SHE-typed blob must not export as plaintext), unwrap-and-cache priming, the counter rollback guard, the LoadKey stale-cache regression, and the two-session reboot interop. Secure boot is driven with raw INIT/UPDATE/FINISH messages (blocking-only client API) |
| `wh_test_she_no_nvm.c::whTest_SheNoNvm` | `misc/wh_test_she_no_nvm.c::whTest_SheNoNvm` | Misc | NULL-NVM server (`whServerConfig.nvm == NULL`), so it runs its own client/server pair with a sequential split-API pump instead of the legacy pthread harness; same end-to-end flow (unwrap-and-cache provisioning incl. SHE slot 0, secure boot, LoadKey cache path, ECB/CBC/CMAC, ExportRamKey, PRNG, KEK immutability) |
| `wh_test_keywrap.c` (sw-KEK round trip, trusted-KEK policy negatives, data-wrap usage) | `client-server/wh_test_keywrap.c::whTest_KeyWrap` | Client | runs against any server: positive KeyWrap/KeyUnwrapAndExport under a plain client KEK, wrap-export/unwrap-and-cache refusal of a non-trusted KEK, the NVM-add / HKDF-cache / key-cache `WH_NVM_FLAGS_TRUSTED` forge negatives, and the data-wrap `USAGE_WRAP` negative. The trusted-KEK positive paths (wrap-export round-trip incl. NONEXPORTABLE enforcement, unwrap-and-cache, and the cache-only check that commit/erase reject an unwrapped key id) run in `misc/wh_test_hwkeystore.c` against the hardware KEK |
| `wh_test_keywrap.c` (sw-KEK round trip, trusted-KEK policy negatives, data-wrap usage) | `client-server/wh_test_keywrap.c::whTest_KeyWrap` | Client | runs against any server: positive KeyWrap/KeyUnwrapAndExport under a plain client KEK, wrap-export/unwrap-and-cache refusal of a non-trusted KEK, the NVM-add / HKDF-cache / key-cache `WH_NVM_FLAGS_TRUSTED` forge negatives, the data-wrap `USAGE_WRAP` negative, and the key/data unwrap underflow bad-args checks. The trusted-KEK positive paths (wrap-export round-trip incl. NONEXPORTABLE enforcement, unwrap-and-cache, and the cache-only check that commit/erase reject an unwrapped key id) run in `misc/wh_test_hwkeystore.c` against the hardware KEK |
| `wh_test_log.c::whTest_Log` (frontend, macros, ring buffer, mock/ringbuf generic harness) | `misc/wh_test_log.c::whTest_Log` | Misc | Portable subset: frontend API, log macros, formatted macros, ring buffer backend, and the backend-agnostic harness against the mock and ring buffer backends. The harness itself lives in `misc/wh_test_log_backend.h` so the POSIX port reuses it |
| `wh_test_log.c` (POSIX file backend, concurrent, client/server) | `posix/wh_test_log_posix.c::{whTest_LogPosixFile_Generic, whTest_LogPosixFile, whTest_LogPosixFileConcurrent, whTest_LogClientServerMemTransport}` | POSIX port-specific (`whTestGroup_RunOne`) | POSIX file backend (via the shared harness and directly), multi-threaded concurrent writers, and a client/server log smoke test over the mem transport. Need pthreads and the host file system, so they run from the port main, not the portable registry |

Expand All @@ -116,7 +116,7 @@ Not yet migrated (still live in `wolfHSM/test/`):
| `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 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` | 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_keywrap.c::whTest_KeyWrapClientConfig` | Policy coverage (sw-KEK round trip, trusted-KEK negatives, data-wrap usage) is ported to `client-server/wh_test_keywrap.c`; the remaining legacy coverage (hardware-KEK cryptocb use of a cached wrapped key, unwrap underflow/oversize negatives) is not |
| `wh_test_keywrap.c::whTest_KeyWrapClientConfig` | Policy coverage (sw-KEK round trip, trusted-KEK negatives, data-wrap usage) and the key/data unwrap underflow negatives are ported to `client-server/wh_test_keywrap.c`; the remaining legacy coverage (hardware-KEK cryptocb use of a cached wrapped key, unwrap oversize negatives) is not |
| `wh_test_timeout.c::whTest_TimeoutPosix` | |
| `wh_test_nvmflags.c::whTest_NvmFlags` | |
| `wh_test_flash_fault_inject.c` | |
Expand Down
11 changes: 7 additions & 4 deletions test-refactor/client-server/wh_test_crypto_keywrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,11 @@ static int _whTest_CryptoKeyWrapSameOwner(whClientContext* client)
sizeof("WrapKeySameOwner"), wrapKey, sizeof(wrapKey), &wrapKeyId));

/* Wrap a local key owned by this client */
meta.id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(client->comm->client_id,
KEYWRAP_TEST_PLAINKEY_ID);
meta.len = sizeof(plainKey);
meta.id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(client->comm->client_id,
KEYWRAP_TEST_PLAINKEY_ID);
meta.len = sizeof(plainKey);
meta.flags = WH_NVM_FLAGS_USAGE_ANY;
memcpy(meta.label, "SameOwner Label", sizeof("SameOwner Label"));
WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrap(client, WC_CIPHER_AES_GCM,
wrapKeyId, plainKey,
sizeof(plainKey), &meta,
Expand All @@ -88,9 +90,10 @@ static int _whTest_CryptoKeyWrapSameOwner(whClientContext* client)
client, WC_CIPHER_AES_GCM, wrapKeyId, wrappedKey, sizeof(wrappedKey),
&exportMeta, unwrappedKey, &unwrappedKeySz));

/* Recovered key matches the original */
/* Recovered key and metadata match the originals */
WH_TEST_ASSERT_RETURN(0 ==
memcmp(unwrappedKey, plainKey, sizeof(plainKey)));
WH_TEST_ASSERT_RETURN(0 == memcmp(&meta, &exportMeta, sizeof(meta)));

/* Evict the wrapping key */
WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, wrapKeyId));
Expand Down
162 changes: 90 additions & 72 deletions test-refactor/client-server/wh_test_keywrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,23 @@
/*
* test-refactor/client-server/wh_test_keywrap.c
*
* Keywrap policy coverage that runs against any server (no trusted KEK
* needed on the positive paths, and the negatives prove a client cannot
* mint one):
* _whTest_KeywrapSwKekRoundTrip - wrap a plaintext key under a plain
* client-cached KEK and unwrap-and-export
* it back (only unwrap-and-cache and
* wrap-export require a trusted KEK)
* Keywrap policy and input-validation coverage that runs against any server:
* the negatives prove a client cannot mint a trusted KEK or slip an
* undersized blob past the size checks.
* _whTest_KeywrapTrustedKekPolicy - wrap-export and unwrap-and-cache must
* refuse a plain client KEK, and a client
* cannot forge WH_NVM_FLAGS_TRUSTED via the
* NVM add, HKDF cache, or key cache paths
* _whTest_KeywrapDataWrapUsage - data wrap requires USAGE_WRAP on the KEK
* _whTest_KeywrapKeyUnwrapUnderflow - undersized wrapped-key blobs must
* return WH_ERROR_BADARGS, not underflow
* _whTest_KeywrapDataUnwrapUnderflow - undersized wrapped-data blobs must
* return WH_ERROR_BADARGS, not underflow
*
* The trusted-KEK positive paths (wrap-export round-trip, unwrap-and-cache)
* live in misc/wh_test_hwkeystore.c against the hardware KEK, and in
* The positive wrap/unwrap-and-export round trip under a plain client KEK
* lives in wh_test_crypto_keywrap.c. The trusted-KEK positive paths
* (wrap-export round-trip, unwrap-and-cache) live in
* misc/wh_test_hwkeystore.c against the hardware KEK, and in
* misc/wh_test_multiclient.c against an NVM-provisioned KEK.
*/

Expand Down Expand Up @@ -92,68 +94,6 @@ static int _CacheSwKek(whClientContext* client, whKeyId* outKekId)
return WH_ERROR_OK;
}

/* Positive software-KEK round trip; needs no trusted KEK. Wrap a plaintext key
* under the plain KEK, unwrap-and-export the blob, and confirm the key
* material and metadata come back unchanged */
static int _whTest_KeywrapSwKekRoundTrip(whClientContext* client)
{
int ret;
whKeyId kekId = WH_KEYID_ERASED;
uint8_t plainKey[WH_TEST_KW_KEYSIZE];
uint8_t tmpPlainKey[WH_TEST_KW_KEYSIZE];
uint16_t tmpPlainKeySz = sizeof(tmpPlainKey);
uint8_t wrappedKey[WH_TEST_KW_WRAPPED_KEYSIZE];
uint16_t wrappedKeySz = sizeof(wrappedKey);
whNvmMetadata metadata = {0};
whNvmMetadata tmpMetadata = {0};
size_t i;

metadata.id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(client->comm->client_id,
WH_TEST_KW_META_ID);
metadata.len = WH_TEST_KW_KEYSIZE;
metadata.flags = WH_NVM_FLAGS_USAGE_ANY;
memcpy(metadata.label, "SwKek Key Label", sizeof("SwKek Key Label"));

for (i = 0; i < sizeof(plainKey); i++) {
plainKey[i] = (uint8_t)(0x7B ^ i);
}

WH_TEST_RETURN_ON_FAIL(_CacheSwKek(client, &kekId));

/* Wrap the plaintext key under the plain software KEK */
ret = wh_Client_KeyWrap(client, WC_CIPHER_AES_GCM, kekId, plainKey,
sizeof(plainKey), &metadata, wrappedKey,
&wrappedKeySz);
if (ret != WH_ERROR_OK) {
WH_ERROR_PRINT("sw-kek: KeyWrap failed %d\n", ret);
(void)wh_Client_KeyEvict(client, kekId);
return ret;
}

/* Unwrap-and-export the blob and confirm the material round-trips */
ret = wh_Client_KeyUnwrapAndExport(client, WC_CIPHER_AES_GCM, kekId,
wrappedKey, wrappedKeySz, &tmpMetadata,
tmpPlainKey, &tmpPlainKeySz);
(void)wh_Client_KeyEvict(client, kekId);
if (ret != WH_ERROR_OK) {
WH_ERROR_PRINT("sw-kek: KeyUnwrapAndExport failed %d\n", ret);
return ret;
}

if (tmpPlainKeySz != sizeof(plainKey) ||
memcmp(plainKey, tmpPlainKey, sizeof(plainKey)) != 0) {
WH_ERROR_PRINT("sw-kek: unwrapped key material mismatch\n");
return WH_ERROR_ABORTED;
}

if (memcmp(&metadata, &tmpMetadata, sizeof(metadata)) != 0) {
WH_ERROR_PRINT("sw-kek: unwrapped metadata mismatch\n");
return WH_ERROR_ABORTED;
}

return WH_ERROR_OK;
}

/* The wrap-export and unwrap-and-cache operations require a trusted KEK (HW or
* WH_NVM_FLAGS_TRUSTED). Prove that (a) a plain client-cached USAGE_WRAP key is
* refused as their KEK, and that a client cannot forge a trusted KEK by
Expand Down Expand Up @@ -397,11 +337,89 @@ static int _whTest_KeywrapDataWrapUsage(whClientContext* client)
return WH_ERROR_OK;
}

/* A wrapped blob smaller than its own header must be refused with
* WH_ERROR_BADARGS rather than underflowing the payload length. A usable KEK
* is cached first so the rejection can only come from the size check, not
* from KEK resolution */
static int _whTest_KeywrapKeyUnwrapUnderflow(whClientContext* client)
{
const uint16_t shortSizes[] = {0, 1};
int ret = WH_ERROR_OK;
whKeyId kekId = WH_KEYID_ERASED;
uint8_t blob[1] = {0};
size_t i;

WH_TEST_RETURN_ON_FAIL(_CacheSwKek(client, &kekId));

for (i = 0; i < sizeof(shortSizes) / sizeof(shortSizes[0]); i++) {
whNvmMetadata meta = {0};
uint8_t keyOut[WH_TEST_KW_KEYSIZE] = {0};
uint16_t keyOutSz = sizeof(keyOut);
uint16_t cachedId = WH_KEYID_ERASED;

ret = wh_Client_KeyUnwrapAndExport(client, WC_CIPHER_AES_GCM, kekId,
blob, shortSizes[i], &meta, keyOut,
&keyOutSz);
if (ret != WH_ERROR_BADARGS) {
WH_ERROR_PRINT("KeyUnwrapAndExport(sz=%u) expected BADARGS, "
"got %d\n",
(unsigned)shortSizes[i], ret);
ret = WH_ERROR_ABORTED;
break;
}

ret = wh_Client_KeyUnwrapAndCache(client, WC_CIPHER_AES_GCM, kekId,
blob, shortSizes[i], &cachedId);
if (ret != WH_ERROR_BADARGS) {
WH_ERROR_PRINT("KeyUnwrapAndCache(sz=%u) expected BADARGS, "
"got %d\n",
(unsigned)shortSizes[i], ret);
ret = WH_ERROR_ABORTED;
break;
}
ret = WH_ERROR_OK;
}

(void)wh_Client_KeyEvict(client, kekId);
return ret;
}

/* Same underflow guard on the opaque data path */
static int _whTest_KeywrapDataUnwrapUnderflow(whClientContext* client)
{
const uint32_t shortSizes[] = {0, 1};
int ret = WH_ERROR_OK;
whKeyId kekId = WH_KEYID_ERASED;
uint8_t blob[1] = {0};
size_t i;

WH_TEST_RETURN_ON_FAIL(_CacheSwKek(client, &kekId));

for (i = 0; i < sizeof(shortSizes) / sizeof(shortSizes[0]); i++) {
uint8_t dataOut[WH_TEST_KW_KEYSIZE] = {0};
uint32_t dataOutSz = sizeof(dataOut);

ret = wh_Client_DataUnwrap(client, WC_CIPHER_AES_GCM, kekId, blob,
shortSizes[i], dataOut, &dataOutSz);
if (ret != WH_ERROR_BADARGS) {
WH_ERROR_PRINT("DataUnwrap(sz=%u) expected BADARGS, got %d\n",
(unsigned)shortSizes[i], ret);
ret = WH_ERROR_ABORTED;
break;
}
ret = WH_ERROR_OK;
}

(void)wh_Client_KeyEvict(client, kekId);
return ret;
}

int whTest_KeyWrap(whClientContext* ctx)
{
WH_TEST_RETURN_ON_FAIL(_whTest_KeywrapSwKekRoundTrip(ctx));
WH_TEST_RETURN_ON_FAIL(_whTest_KeywrapTrustedKekPolicy(ctx));
WH_TEST_RETURN_ON_FAIL(_whTest_KeywrapDataWrapUsage(ctx));
WH_TEST_RETURN_ON_FAIL(_whTest_KeywrapKeyUnwrapUnderflow(ctx));
WH_TEST_RETURN_ON_FAIL(_whTest_KeywrapDataUnwrapUnderflow(ctx));

WH_TEST_PRINT("KEYWRAP POLICY SUCCESS\n");
return 0;
Expand Down
Loading