Skip to content

Commit 9c2fc3f

Browse files
authored
Merge pull request #422 from padelsbach/test-refactor-add-keywrap
Add refactored tests for keywrap
2 parents d6e8066 + 76277e3 commit 9c2fc3f

3 files changed

Lines changed: 99 additions & 78 deletions

File tree

test-refactor/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ Translated tests:
105105
| `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) |
106106
| `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) |
107107
| `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) |
108-
| `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 |
108+
| `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 |
109109
| `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 |
110110
| `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 |
111111

@@ -116,7 +116,7 @@ Not yet migrated (still live in `wolfHSM/test/`):
116116
| `wh_test_comm.c::whTest_Comm` | Pthread mem/tcp/shmem variants only; sequential mem variant has been ported |
117117
| `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. |
118118
| `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. |
119-
| `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 |
119+
| `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 |
120120
| `wh_test_timeout.c::whTest_TimeoutPosix` | |
121121
| `wh_test_nvmflags.c::whTest_NvmFlags` | |
122122
| `wh_test_flash_fault_inject.c` | |

test-refactor/client-server/wh_test_crypto_keywrap.c

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,11 @@ static int _whTest_CryptoKeyWrapSameOwner(whClientContext* client)
7575
sizeof("WrapKeySameOwner"), wrapKey, sizeof(wrapKey), &wrapKeyId));
7676

7777
/* Wrap a local key owned by this client */
78-
meta.id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(client->comm->client_id,
79-
KEYWRAP_TEST_PLAINKEY_ID);
80-
meta.len = sizeof(plainKey);
78+
meta.id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(client->comm->client_id,
79+
KEYWRAP_TEST_PLAINKEY_ID);
80+
meta.len = sizeof(plainKey);
81+
meta.flags = WH_NVM_FLAGS_USAGE_ANY;
82+
memcpy(meta.label, "SameOwner Label", sizeof("SameOwner Label"));
8183
WH_TEST_RETURN_ON_FAIL(wh_Client_KeyWrap(client, WC_CIPHER_AES_GCM,
8284
wrapKeyId, plainKey,
8385
sizeof(plainKey), &meta,
@@ -88,9 +90,10 @@ static int _whTest_CryptoKeyWrapSameOwner(whClientContext* client)
8890
client, WC_CIPHER_AES_GCM, wrapKeyId, wrappedKey, sizeof(wrappedKey),
8991
&exportMeta, unwrappedKey, &unwrappedKeySz));
9092

91-
/* Recovered key matches the original */
93+
/* Recovered key and metadata match the originals */
9294
WH_TEST_ASSERT_RETURN(0 ==
9395
memcmp(unwrappedKey, plainKey, sizeof(plainKey)));
96+
WH_TEST_ASSERT_RETURN(0 == memcmp(&meta, &exportMeta, sizeof(meta)));
9497

9598
/* Evict the wrapping key */
9699
WH_TEST_RETURN_ON_FAIL(wh_Client_KeyEvict(client, wrapKeyId));

test-refactor/client-server/wh_test_keywrap.c

Lines changed: 90 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,23 @@
1919
/*
2020
* test-refactor/client-server/wh_test_keywrap.c
2121
*
22-
* Keywrap policy coverage that runs against any server (no trusted KEK
23-
* needed on the positive paths, and the negatives prove a client cannot
24-
* mint one):
25-
* _whTest_KeywrapSwKekRoundTrip - wrap a plaintext key under a plain
26-
* client-cached KEK and unwrap-and-export
27-
* it back (only unwrap-and-cache and
28-
* wrap-export require a trusted KEK)
22+
* Keywrap policy and input-validation coverage that runs against any server:
23+
* the negatives prove a client cannot mint a trusted KEK or slip an
24+
* undersized blob past the size checks.
2925
* _whTest_KeywrapTrustedKekPolicy - wrap-export and unwrap-and-cache must
3026
* refuse a plain client KEK, and a client
3127
* cannot forge WH_NVM_FLAGS_TRUSTED via the
3228
* NVM add, HKDF cache, or key cache paths
3329
* _whTest_KeywrapDataWrapUsage - data wrap requires USAGE_WRAP on the KEK
30+
* _whTest_KeywrapKeyUnwrapUnderflow - undersized wrapped-key blobs must
31+
* return WH_ERROR_BADARGS, not underflow
32+
* _whTest_KeywrapDataUnwrapUnderflow - undersized wrapped-data blobs must
33+
* return WH_ERROR_BADARGS, not underflow
3434
*
35-
* The trusted-KEK positive paths (wrap-export round-trip, unwrap-and-cache)
36-
* live in misc/wh_test_hwkeystore.c against the hardware KEK, and in
35+
* The positive wrap/unwrap-and-export round trip under a plain client KEK
36+
* lives in wh_test_crypto_keywrap.c. The trusted-KEK positive paths
37+
* (wrap-export round-trip, unwrap-and-cache) live in
38+
* misc/wh_test_hwkeystore.c against the hardware KEK, and in
3739
* misc/wh_test_multiclient.c against an NVM-provisioned KEK.
3840
*/
3941

@@ -92,68 +94,6 @@ static int _CacheSwKek(whClientContext* client, whKeyId* outKekId)
9294
return WH_ERROR_OK;
9395
}
9496

95-
/* Positive software-KEK round trip; needs no trusted KEK. Wrap a plaintext key
96-
* under the plain KEK, unwrap-and-export the blob, and confirm the key
97-
* material and metadata come back unchanged */
98-
static int _whTest_KeywrapSwKekRoundTrip(whClientContext* client)
99-
{
100-
int ret;
101-
whKeyId kekId = WH_KEYID_ERASED;
102-
uint8_t plainKey[WH_TEST_KW_KEYSIZE];
103-
uint8_t tmpPlainKey[WH_TEST_KW_KEYSIZE];
104-
uint16_t tmpPlainKeySz = sizeof(tmpPlainKey);
105-
uint8_t wrappedKey[WH_TEST_KW_WRAPPED_KEYSIZE];
106-
uint16_t wrappedKeySz = sizeof(wrappedKey);
107-
whNvmMetadata metadata = {0};
108-
whNvmMetadata tmpMetadata = {0};
109-
size_t i;
110-
111-
metadata.id = WH_CLIENT_KEYID_MAKE_WRAPPED_META(client->comm->client_id,
112-
WH_TEST_KW_META_ID);
113-
metadata.len = WH_TEST_KW_KEYSIZE;
114-
metadata.flags = WH_NVM_FLAGS_USAGE_ANY;
115-
memcpy(metadata.label, "SwKek Key Label", sizeof("SwKek Key Label"));
116-
117-
for (i = 0; i < sizeof(plainKey); i++) {
118-
plainKey[i] = (uint8_t)(0x7B ^ i);
119-
}
120-
121-
WH_TEST_RETURN_ON_FAIL(_CacheSwKek(client, &kekId));
122-
123-
/* Wrap the plaintext key under the plain software KEK */
124-
ret = wh_Client_KeyWrap(client, WC_CIPHER_AES_GCM, kekId, plainKey,
125-
sizeof(plainKey), &metadata, wrappedKey,
126-
&wrappedKeySz);
127-
if (ret != WH_ERROR_OK) {
128-
WH_ERROR_PRINT("sw-kek: KeyWrap failed %d\n", ret);
129-
(void)wh_Client_KeyEvict(client, kekId);
130-
return ret;
131-
}
132-
133-
/* Unwrap-and-export the blob and confirm the material round-trips */
134-
ret = wh_Client_KeyUnwrapAndExport(client, WC_CIPHER_AES_GCM, kekId,
135-
wrappedKey, wrappedKeySz, &tmpMetadata,
136-
tmpPlainKey, &tmpPlainKeySz);
137-
(void)wh_Client_KeyEvict(client, kekId);
138-
if (ret != WH_ERROR_OK) {
139-
WH_ERROR_PRINT("sw-kek: KeyUnwrapAndExport failed %d\n", ret);
140-
return ret;
141-
}
142-
143-
if (tmpPlainKeySz != sizeof(plainKey) ||
144-
memcmp(plainKey, tmpPlainKey, sizeof(plainKey)) != 0) {
145-
WH_ERROR_PRINT("sw-kek: unwrapped key material mismatch\n");
146-
return WH_ERROR_ABORTED;
147-
}
148-
149-
if (memcmp(&metadata, &tmpMetadata, sizeof(metadata)) != 0) {
150-
WH_ERROR_PRINT("sw-kek: unwrapped metadata mismatch\n");
151-
return WH_ERROR_ABORTED;
152-
}
153-
154-
return WH_ERROR_OK;
155-
}
156-
15797
/* The wrap-export and unwrap-and-cache operations require a trusted KEK (HW or
15898
* WH_NVM_FLAGS_TRUSTED). Prove that (a) a plain client-cached USAGE_WRAP key is
15999
* refused as their KEK, and that a client cannot forge a trusted KEK by
@@ -397,11 +337,89 @@ static int _whTest_KeywrapDataWrapUsage(whClientContext* client)
397337
return WH_ERROR_OK;
398338
}
399339

340+
/* A wrapped blob smaller than its own header must be refused with
341+
* WH_ERROR_BADARGS rather than underflowing the payload length. A usable KEK
342+
* is cached first so the rejection can only come from the size check, not
343+
* from KEK resolution */
344+
static int _whTest_KeywrapKeyUnwrapUnderflow(whClientContext* client)
345+
{
346+
const uint16_t shortSizes[] = {0, 1};
347+
int ret = WH_ERROR_OK;
348+
whKeyId kekId = WH_KEYID_ERASED;
349+
uint8_t blob[1] = {0};
350+
size_t i;
351+
352+
WH_TEST_RETURN_ON_FAIL(_CacheSwKek(client, &kekId));
353+
354+
for (i = 0; i < sizeof(shortSizes) / sizeof(shortSizes[0]); i++) {
355+
whNvmMetadata meta = {0};
356+
uint8_t keyOut[WH_TEST_KW_KEYSIZE] = {0};
357+
uint16_t keyOutSz = sizeof(keyOut);
358+
uint16_t cachedId = WH_KEYID_ERASED;
359+
360+
ret = wh_Client_KeyUnwrapAndExport(client, WC_CIPHER_AES_GCM, kekId,
361+
blob, shortSizes[i], &meta, keyOut,
362+
&keyOutSz);
363+
if (ret != WH_ERROR_BADARGS) {
364+
WH_ERROR_PRINT("KeyUnwrapAndExport(sz=%u) expected BADARGS, "
365+
"got %d\n",
366+
(unsigned)shortSizes[i], ret);
367+
ret = WH_ERROR_ABORTED;
368+
break;
369+
}
370+
371+
ret = wh_Client_KeyUnwrapAndCache(client, WC_CIPHER_AES_GCM, kekId,
372+
blob, shortSizes[i], &cachedId);
373+
if (ret != WH_ERROR_BADARGS) {
374+
WH_ERROR_PRINT("KeyUnwrapAndCache(sz=%u) expected BADARGS, "
375+
"got %d\n",
376+
(unsigned)shortSizes[i], ret);
377+
ret = WH_ERROR_ABORTED;
378+
break;
379+
}
380+
ret = WH_ERROR_OK;
381+
}
382+
383+
(void)wh_Client_KeyEvict(client, kekId);
384+
return ret;
385+
}
386+
387+
/* Same underflow guard on the opaque data path */
388+
static int _whTest_KeywrapDataUnwrapUnderflow(whClientContext* client)
389+
{
390+
const uint32_t shortSizes[] = {0, 1};
391+
int ret = WH_ERROR_OK;
392+
whKeyId kekId = WH_KEYID_ERASED;
393+
uint8_t blob[1] = {0};
394+
size_t i;
395+
396+
WH_TEST_RETURN_ON_FAIL(_CacheSwKek(client, &kekId));
397+
398+
for (i = 0; i < sizeof(shortSizes) / sizeof(shortSizes[0]); i++) {
399+
uint8_t dataOut[WH_TEST_KW_KEYSIZE] = {0};
400+
uint32_t dataOutSz = sizeof(dataOut);
401+
402+
ret = wh_Client_DataUnwrap(client, WC_CIPHER_AES_GCM, kekId, blob,
403+
shortSizes[i], dataOut, &dataOutSz);
404+
if (ret != WH_ERROR_BADARGS) {
405+
WH_ERROR_PRINT("DataUnwrap(sz=%u) expected BADARGS, got %d\n",
406+
(unsigned)shortSizes[i], ret);
407+
ret = WH_ERROR_ABORTED;
408+
break;
409+
}
410+
ret = WH_ERROR_OK;
411+
}
412+
413+
(void)wh_Client_KeyEvict(client, kekId);
414+
return ret;
415+
}
416+
400417
int whTest_KeyWrap(whClientContext* ctx)
401418
{
402-
WH_TEST_RETURN_ON_FAIL(_whTest_KeywrapSwKekRoundTrip(ctx));
403419
WH_TEST_RETURN_ON_FAIL(_whTest_KeywrapTrustedKekPolicy(ctx));
404420
WH_TEST_RETURN_ON_FAIL(_whTest_KeywrapDataWrapUsage(ctx));
421+
WH_TEST_RETURN_ON_FAIL(_whTest_KeywrapKeyUnwrapUnderflow(ctx));
422+
WH_TEST_RETURN_ON_FAIL(_whTest_KeywrapDataUnwrapUnderflow(ctx));
405423

406424
WH_TEST_PRINT("KEYWRAP POLICY SUCCESS\n");
407425
return 0;

0 commit comments

Comments
 (0)