From 01227dcbe2a3b9055c384325aef89c54c3d73c9f Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Wed, 8 Jul 2026 15:26:51 -0400 Subject: [PATCH 1/2] Implement client api to generate and cache a random key --- docs/src/5-Features.md | 3 +- src/wh_client.c | 99 +++++++++++++++++++++++++++++++++++ src/wh_message_keystore.c | 32 +++++++++++ src/wh_server_keystore.c | 99 +++++++++++++++++++++++++++++++++++ test/wh_test_crypto.c | 89 +++++++++++++++++++++++++++++++ wolfhsm/wh_client.h | 72 +++++++++++++++++++++++++ wolfhsm/wh_message.h | 1 + wolfhsm/wh_message_keystore.h | 28 ++++++++++ 8 files changed, 422 insertions(+), 1 deletion(-) diff --git a/docs/src/5-Features.md b/docs/src/5-Features.md index 0e0f7d3af..743a87c2f 100644 --- a/docs/src/5-Features.md +++ b/docs/src/5-Features.md @@ -313,9 +313,10 @@ The two tiers keep one large key (e.g. an an ML-DSA-87 private key) from dictati Each cached key carries its full `whNvmMetadata` record alongside the key bytes, plus an internal **committed flag** marking whether a copy also exists in NVM. This flag is what makes the cache a true working set: when it needs a slot and none is free, the keystore evicts a key that is already committed (and can be reloaded later), but never an uncommitted one. Uncommitted keys are RAM-only, so the caller must commit them to survive eviction or reset; if the cache fills with uncommitted keys, the next cache operation returns `WH_ERROR_NOSPACE` instead of silently dropping material. -A client drives this tier with five operations: +A client drives this tier with six operations: - **Cache**: write key bytes and metadata into a server cache slot. The key is usable immediately but RAM-only. +- **Cache Random**: have the server generate a random key from its own RNG and cache it into a server cache slot. - **Commit**: copy a cached key into NVM as a durable object. The cache copy is marked committed and becomes a candidate for eviction. - **Evict**: drop the cache copy. If the key was committed, the NVM copy remains and reloads on the next reference; if it was uncommitted, the key is gone. - **Export**: read a cached key's bytes back to the client, subject to `WH_NVM_FLAGS_NONEXPORTABLE`. diff --git a/src/wh_client.c b/src/wh_client.c index dbee41cc5..26d33f99b 100644 --- a/src/wh_client.c +++ b/src/wh_client.c @@ -905,6 +905,105 @@ int wh_Client_KeyCache(whClientContext* c, uint32_t flags, uint8_t* label, return ret; } +int wh_Client_KeyCacheRandomRequest_ex(whClientContext* c, uint32_t flags, + uint8_t* label, uint16_t labelSz, + uint16_t keySz, uint16_t keyId) +{ + whMessageKeystore_CacheRandomRequest* req = NULL; + uint16_t capSz; + + if (c == NULL || keySz == 0) { + return WH_ERROR_BADARGS; + } + + req = (whMessageKeystore_CacheRandomRequest*)wh_CommClient_GetDataPtr(c->comm); + if (req == NULL) { + return WH_ERROR_BADARGS; + } + memset(req, 0, sizeof(*req)); + req->id = keyId; + req->flags = flags; + req->sz = keySz; + + if (label == NULL) { + req->labelSz = 0; + } + else { + /* write label */ + capSz = (labelSz > WH_NVM_LABEL_LEN) ? WH_NVM_LABEL_LEN : labelSz; + req->labelSz = capSz; + memcpy(req->label, label, capSz); + } + + /* write request (no key material is sent) */ + return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_KEY, WH_KEY_CACHE_RANDOM, + sizeof(*req), (uint8_t*)req); +} + +int wh_Client_KeyCacheRandomRequest(whClientContext* c, uint32_t flags, + uint8_t* label, uint16_t labelSz, + uint16_t keySz) +{ + return wh_Client_KeyCacheRandomRequest_ex(c, flags, label, labelSz, + keySz, WH_KEYID_ERASED); +} + +int wh_Client_KeyCacheRandomResponse(whClientContext* c, uint16_t* keyId) +{ + uint16_t group; + uint16_t action; + uint16_t size; + int ret; + whMessageKeystore_CacheRandomResponse *resp = NULL; + + if (c == NULL || keyId == NULL) { + return WH_ERROR_BADARGS; + } + + resp = (whMessageKeystore_CacheRandomResponse*)wh_CommClient_GetDataPtr( + c->comm); + if (resp == NULL) { + return WH_ERROR_BADARGS; + } + + ret = wh_Client_RecvResponse(c, &group, &action, &size, (uint8_t*)resp); + if (ret == WH_ERROR_OK) { + if (resp->rc != 0) { + ret = resp->rc; + } + else { + *keyId = resp->id; + } + } + + return ret; +} + +int wh_Client_KeyCacheRandom(whClientContext* c, uint32_t flags, + uint8_t* label, uint16_t labelSz, + uint16_t keySz, uint16_t* keyId) +{ + int ret = WH_ERROR_OK; + + if (keyId == NULL) { + return WH_ERROR_BADARGS; + } + + ret = wh_Client_KeyCacheRandomRequest_ex(c, flags, label, labelSz, + keySz, *keyId); + + if (ret == 0) { + do { + ret = wh_Client_KeyCacheRandomResponse(c, keyId); + } while (ret == WH_ERROR_NOTREADY); + } + + WH_DEBUG_CLIENT_VERBOSE("label:%.*s key_id:%x ret:%d \n", + (label != NULL) ? (int)labelSz : 0, + (label != NULL) ? (const char*)label : "", *keyId, ret); + return ret; +} + int wh_Client_KeyEvictRequest(whClientContext* c, uint16_t keyId) { whMessageKeystore_EvictRequest* req = NULL; diff --git a/src/wh_message_keystore.c b/src/wh_message_keystore.c index 209ca5483..7f019be1a 100644 --- a/src/wh_message_keystore.c +++ b/src/wh_message_keystore.c @@ -59,6 +59,38 @@ int wh_MessageKeystore_TranslateCacheResponse( return 0; } +/* Key Cache Random Request translation */ +int wh_MessageKeystore_TranslateCacheRandomRequest( + uint16_t magic, const whMessageKeystore_CacheRandomRequest* src, + whMessageKeystore_CacheRandomRequest* dest) +{ + if ((src == NULL) || (dest == NULL)) { + return WH_ERROR_BADARGS; + } + WH_T32(magic, dest, src, flags); + WH_T32(magic, dest, src, labelSz); + WH_T16(magic, dest, src, sz); + WH_T16(magic, dest, src, id); + /* Label is just a byte array, no translation needed */ + if (src != dest) { + memcpy(dest->label, src->label, WH_NVM_LABEL_LEN); + } + return 0; +} + +/* Key Cache Random Response translation */ +int wh_MessageKeystore_TranslateCacheRandomResponse( + uint16_t magic, const whMessageKeystore_CacheRandomResponse* src, + whMessageKeystore_CacheRandomResponse* dest) +{ + if ((src == NULL) || (dest == NULL)) { + return WH_ERROR_BADARGS; + } + WH_T32(magic, dest, src, rc); + WH_T16(magic, dest, src, id); + return 0; +} + /* Key Evict Request translation */ int wh_MessageKeystore_TranslateEvictRequest( uint16_t magic, const whMessageKeystore_EvictRequest* src, diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c index 90cfb56be..2b9c4ef37 100644 --- a/src/wh_server_keystore.c +++ b/src/wh_server_keystore.c @@ -850,6 +850,43 @@ int wh_Server_KeystoreCacheKeyChecked(whServerContext* server, return _KeystoreCacheKey(server, meta, in, 1); } +#ifndef WC_NO_RNG +static int _KeystoreCacheRandomKey(whServerContext* server, whNvmMetadata* meta) +{ + uint8_t* slotBuf; + whNvmMetadata* slotMeta; + int ret; + + /* make sure id and length are valid */ + if ((server == NULL) || (meta == NULL) || (meta->len == 0) || + WH_KEYID_ISERASED(meta->id) || + ((meta->len > WOLFHSM_CFG_SERVER_KEYCACHE_BUFSIZE) && + (meta->len > WOLFHSM_CFG_SERVER_KEYCACHE_BIG_BUFSIZE))) { + return WH_ERROR_BADARGS; + } + + ret = wh_Server_KeystoreGetCacheSlotChecked(server, meta->id, meta->len, + &slotBuf, &slotMeta); + if (ret != WH_ERROR_OK) { + return ret; + } + + /* Fill the slot directly from the server RNG */ + ret = wc_RNG_GenerateBlock(server->crypto->rng, slotBuf, meta->len); + if (ret != 0) { + return ret; + } + + memcpy((uint8_t*)slotMeta, (uint8_t*)meta, sizeof(whNvmMetadata)); + _MarkKeyCommitted(_GetCacheContext(server, meta->id), meta->id, 0); + + WH_DEBUG_SERVER_VERBOSE("hsmGenerateKey: cached keyid=0x%X, len=%u\n", + meta->id, meta->len); + + return WH_ERROR_OK; +} +#endif /* !WC_NO_RNG */ + static int _FindInCache(whServerContext* server, whKeyId keyId, int* out_index, int* out_big, uint8_t** out_buffer, whNvmMetadata** out_meta) @@ -2272,6 +2309,68 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic, *out_resp_size = sizeof(resp); } break; + case WH_KEY_CACHE_RANDOM: { + whMessageKeystore_CacheRandomRequest req = {0}; + whMessageKeystore_CacheRandomResponse resp = {0}; + + /* Validate req_size can hold the fixed request struct */ + if (req_size < sizeof(req)) { + ret = WH_ERROR_BADARGS; + } + + if (ret == WH_ERROR_OK) { + /* translate request */ + (void)wh_MessageKeystore_TranslateCacheRandomRequest( + magic, (whMessageKeystore_CacheRandomRequest*)req_packet, + &req); + + /* set the metadata fields (no key material is sent) */ + meta->id = wh_KeyId_TranslateFromClient( + WH_KEYTYPE_CRYPTO, server->comm->client_id, req.id); + meta->access = WH_NVM_ACCESS_ANY; + meta->flags = req.flags; + meta->len = req.sz; + /* truncate label if it's too large */ + if (req.labelSz > WH_NVM_LABEL_LEN) { + req.labelSz = WH_NVM_LABEL_LEN; + } + memcpy(meta->label, req.label, req.labelSz); + } + +#ifndef WC_NO_RNG + if (ret == WH_ERROR_OK) { + ret = WH_SERVER_NVM_LOCK(server); + } + if (ret == WH_ERROR_OK) { + /* get a new id if one wasn't provided */ + if (WH_KEYID_ISERASED(meta->id)) { + ret = wh_Server_KeystoreGetUniqueId(server, &meta->id); + } + /* generate the key from the server RNG and cache it */ + if (ret == WH_ERROR_OK) { + ret = _KeystoreCacheRandomKey(server, meta); + } + + (void)WH_SERVER_NVM_UNLOCK(server); + } /* WH_SERVER_NVM_LOCK() */ +#else + if (ret == WH_ERROR_OK) { + ret = WH_ERROR_NOTIMPL; + } +#endif /* !WC_NO_RNG */ + + if (ret == WH_ERROR_OK) { + /* Translate server keyId back to client format with flags */ + resp.id = wh_KeyId_TranslateToClient(meta->id); + } + resp.rc = ret; + + (void)wh_MessageKeystore_TranslateCacheRandomResponse( + magic, &resp, (whMessageKeystore_CacheRandomResponse*)resp_packet); + + *out_resp_size = sizeof(resp); + } break; + #ifdef WOLFHSM_CFG_DMA case WH_KEY_CACHE_DMA: { diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c index a5850cde5..9e2a36188 100644 --- a/test/wh_test_crypto.c +++ b/test/wh_test_crypto.c @@ -6753,6 +6753,90 @@ static int whTest_CacheExportKeyDma(whClientContext* ctx, whKeyId* inout_key_id, } #endif /* WOLFHSM_CFG_DMA */ +static int whTest_KeyCacheRandom(whClientContext* ctx, int devId, + WC_RNG* rng) +{ + (void)devId; + (void)rng; /* Unused */ + +#define WH_TEST_KEYCACHERANDOM_KEYSIZE 32 + int ret; + int i; + int isZero; + uint16_t outLen; + uint16_t keyId; + uint16_t keyId2; + uint8_t keyOut[WH_TEST_KEYCACHERANDOM_KEYSIZE] = {0}; + uint8_t labelIn[WH_NVM_LABEL_LEN] = "KeyGenFromRng Test"; + uint8_t labelOut[WH_NVM_LABEL_LEN] = {0}; + + /* Generate a key from the server RNG and cache it */ + keyId = WH_KEYID_ERASED; + ret = wh_Client_KeyCacheRandom(ctx, 0, labelIn, sizeof(labelIn), + WH_TEST_KEYCACHERANDOM_KEYSIZE, &keyId); + if (ret != 0) { + WH_ERROR_PRINT("Failed to wh_Client_KeyCacheRandom %d\n", ret); + } + else if (keyId == WH_KEYID_ERASED) { + WH_ERROR_PRINT("KeyCacheRandom returned erased keyId\n"); + ret = -1; + } + + /* Export it back and verify size, label, and that RNG actually ran */ + if (ret == 0) { + outLen = sizeof(keyOut); + ret = wh_Client_KeyExport(ctx, keyId, labelOut, sizeof(labelOut), + keyOut, &outLen); + if (ret != 0) { + WH_ERROR_PRINT("Failed to wh_Client_KeyExport %d\n", ret); + } + else if (outLen != WH_TEST_KEYCACHERANDOM_KEYSIZE) { + WH_ERROR_PRINT("KeyCacheRandom bad length %u\n", outLen); + ret = -1; + } + else if (memcmp(labelIn, labelOut, sizeof(labelIn)) != 0) { + WH_ERROR_PRINT("KeyCacheRandom label mismatch\n"); + ret = -1; + } + else { + /* sanity: generated key should not be all zeros */ + isZero = 1; + for (i = 0; i < (int)outLen; i++) { + if (keyOut[i] != 0) { + isZero = 0; + break; + } + } + if (isZero) { + WH_ERROR_PRINT("KeyCacheRandom produced all-zero key\n"); + ret = -1; + } + } + } + + /* A second generation should yield a distinct auto-assigned keyId */ + if (ret == 0) { + keyId2 = WH_KEYID_ERASED; + ret = wh_Client_KeyCacheRandom(ctx, 0, labelIn, sizeof(labelIn), + WH_TEST_KEYCACHERANDOM_KEYSIZE, &keyId2); + if (ret != 0) { + WH_ERROR_PRINT("Failed to wh_Client_KeyCacheRandom(2) %d\n", ret); + } + else if (keyId2 == keyId) { + WH_ERROR_PRINT("KeyCacheRandom reused keyId 0x%X\n", keyId); + ret = -1; + } + (void)wh_Client_KeyEvict(ctx, keyId2); + } + + (void)wh_Client_KeyEvict(ctx, keyId); + + if (ret == 0) { + WH_TEST_PRINT("KEY CACHE RANDOM SUCCESS\n"); + } + return ret; +} + static int whTest_KeyCache(whClientContext* ctx, int devId, WC_RNG* rng) { (void)devId; (void)rng; /* Unused */ @@ -15341,6 +15425,11 @@ int whTest_CryptoClientConfig(whClientConfig* config) ret = whTest_KeyCache(client, WH_CLIENT_DEVID(client), rng); } + if (ret == 0) { + /* Test server-side key generation from RNG */ + ret = whTest_KeyCacheRandom(client, WH_DEV_ID, rng); + } + if (ret == 0) { /* Test Non-Exportable Flag enforcement on keystore */ ret = diff --git a/wolfhsm/wh_client.h b/wolfhsm/wh_client.h index 31707e8cc..c1b8e205f 100644 --- a/wolfhsm/wh_client.h +++ b/wolfhsm/wh_client.h @@ -710,6 +710,78 @@ int wh_Client_KeyCache(whClientContext* c, uint32_t flags, uint8_t* label, uint16_t labelSz, const uint8_t* in, uint16_t inSz, uint16_t* keyId); +/** + * @brief Sends a request to generate a key from the server RNG and cache it. + * + * The server generates keySz random bytes into a cache slot and returns only + * the assigned key ID. This function does not block; it returns immediately + * after sending the request. + * + * @param[in] c Pointer to the client context. + * @param[in] flags Flags (whNvmFlags) for the generated key. + * @param[in] label Pointer to the label associated with the key. + * @param[in] labelSz Size of the label. + * @param[in] keySz Number of random key bytes to generate. + * @param[in] keyId Key ID to be used. If set to WH_KEYID_ERASED, a new ID + * will be generated. + * @return int Returns 0 on success, or a negative error code on failure. + */ +int wh_Client_KeyCacheRandomRequest_ex(whClientContext* c, uint32_t flags, + uint8_t* label, uint16_t labelSz, + uint16_t keySz, uint16_t keyId); + +/** + * @brief Sends a request to generate a key from the server RNG and cache it. + * + * Like wh_Client_KeyCacheRandomRequest_ex, but always requests a + * server-assigned key ID (WH_KEYID_ERASED). This function does not block. + * + * @param[in] c Pointer to the client context. + * @param[in] flags Flags (whNvmFlags) for the generated key. + * @param[in] label Pointer to the label associated with the key. + * @param[in] labelSz Size of the label. + * @param[in] keySz Number of random key bytes to generate. + * @return int Returns 0 on success, or a negative error code on failure. + */ +int wh_Client_KeyCacheRandomRequest(whClientContext* c, uint32_t flags, + uint8_t* label, uint16_t labelSz, + uint16_t keySz); + +/** + * @brief Receives the response to a generate-and-cache request. + * + * This function attempts to process the response to a request that had the + * server generate a key from its RNG and cache it. It validates the response + * and extracts the key ID of the cached key. This function does not block; it + * returns WH_ERROR_NOTREADY if a response has not been received. + * + * @param[in] c Pointer to the client context. + * @param[out] keyId Pointer to store the key ID assigned by the server. + * @return int Returns 0 on success, WH_ERROR_NOTREADY if no response is + * available, or a negative error code on failure. + */ +int wh_Client_KeyCacheRandomResponse(whClientContext* c, uint16_t* keyId); + +/** + * @brief Generates a key from the server RNG, caches it, and returns its ID. + * + * This function handles the complete process of requesting the server to + * generate a key from its RNG and receiving the response. It blocks until the + * operation completes or an error occurs. + * + * @param[in] c Pointer to the client context. + * @param[in] flags Flags (whNvmFlags) for the generated key. + * @param[in] label Pointer to the label associated with the key. + * @param[in] labelSz Size of the label. + * @param[in] keySz Number of random key bytes to generate. + * @param[in,out] keyId On input, the requested key ID (or WH_KEYID_ERASED). On + * success, stores the key ID assigned by the server. + * @return int Returns 0 on success, or a negative error code on failure. + */ +int wh_Client_KeyCacheRandom(whClientContext* c, uint32_t flags, + uint8_t* label, uint16_t labelSz, + uint16_t keySz, uint16_t* keyId); + /** * @brief Sends a key eviction request to the server. * diff --git a/wolfhsm/wh_message.h b/wolfhsm/wh_message.h index c562c877f..f66a79ec7 100644 --- a/wolfhsm/wh_message.h +++ b/wolfhsm/wh_message.h @@ -72,6 +72,7 @@ enum WH_KEY_ENUM { WH_KEY_DATAUNWRAP, WH_KEY_EXPORT_PUBLIC, WH_KEY_EXPORT_PUBLIC_DMA, + WH_KEY_CACHE_RANDOM, }; /* SHE actions */ diff --git a/wolfhsm/wh_message_keystore.h b/wolfhsm/wh_message_keystore.h index 95aea456f..c496307c4 100644 --- a/wolfhsm/wh_message_keystore.h +++ b/wolfhsm/wh_message_keystore.h @@ -61,6 +61,34 @@ int wh_MessageKeystore_TranslateCacheResponse( uint16_t magic, const whMessageKeystore_CacheResponse* src, whMessageKeystore_CacheResponse* dest); +/* Key Cache Random Request + * Requests the server to generate a key from its RNG and cache it. No key + * material is sent by the client. */ +typedef struct { + uint32_t flags; + uint32_t labelSz; + uint16_t sz; /* number of random key bytes to generate */ + uint16_t id; /* requested keyId, or WH_KEYID_ERASED to auto-assign */ + uint8_t WH_PAD[4]; + uint8_t label[WH_NVM_LABEL_LEN]; +} whMessageKeystore_CacheRandomRequest; + +/* Key Cache Random Response */ +typedef struct { + uint32_t rc; + uint16_t id; + uint8_t WH_PAD[6]; +} whMessageKeystore_CacheRandomResponse; + +/* Key Cache Random translation functions */ +int wh_MessageKeystore_TranslateCacheRandomRequest( + uint16_t magic, const whMessageKeystore_CacheRandomRequest* src, + whMessageKeystore_CacheRandomRequest* dest); + +int wh_MessageKeystore_TranslateCacheRandomResponse( + uint16_t magic, const whMessageKeystore_CacheRandomResponse* src, + whMessageKeystore_CacheRandomResponse* dest); + /* Key Evict Request */ typedef struct { uint16_t id; From ad3ecd63ff8e139029c0ac4655d06035a6551d7e Mon Sep 17 00:00:00 2001 From: Alex Lanzano Date: Fri, 10 Jul 2026 12:48:42 -0400 Subject: [PATCH 2/2] Address review comments --- src/wh_client.c | 23 ++++++++--------------- src/wh_server_keystore.c | 22 +++++++++++----------- test/wh_test_crypto.c | 26 +++++++++++++++++++++++--- wolfhsm/wh_client.h | 21 ++------------------- 4 files changed, 44 insertions(+), 48 deletions(-) diff --git a/src/wh_client.c b/src/wh_client.c index 26d33f99b..dcd2c3f33 100644 --- a/src/wh_client.c +++ b/src/wh_client.c @@ -905,18 +905,19 @@ int wh_Client_KeyCache(whClientContext* c, uint32_t flags, uint8_t* label, return ret; } -int wh_Client_KeyCacheRandomRequest_ex(whClientContext* c, uint32_t flags, - uint8_t* label, uint16_t labelSz, - uint16_t keySz, uint16_t keyId) +int wh_Client_KeyCacheRandomRequest(whClientContext* c, uint32_t flags, + uint8_t* label, uint16_t labelSz, + uint16_t keySz, uint16_t keyId) { whMessageKeystore_CacheRandomRequest* req = NULL; - uint16_t capSz; + uint16_t capSz; if (c == NULL || keySz == 0) { return WH_ERROR_BADARGS; } - req = (whMessageKeystore_CacheRandomRequest*)wh_CommClient_GetDataPtr(c->comm); + req = (whMessageKeystore_CacheRandomRequest*)wh_CommClient_GetDataPtr( + c->comm); if (req == NULL) { return WH_ERROR_BADARGS; } @@ -940,14 +941,6 @@ int wh_Client_KeyCacheRandomRequest_ex(whClientContext* c, uint32_t flags, sizeof(*req), (uint8_t*)req); } -int wh_Client_KeyCacheRandomRequest(whClientContext* c, uint32_t flags, - uint8_t* label, uint16_t labelSz, - uint16_t keySz) -{ - return wh_Client_KeyCacheRandomRequest_ex(c, flags, label, labelSz, - keySz, WH_KEYID_ERASED); -} - int wh_Client_KeyCacheRandomResponse(whClientContext* c, uint16_t* keyId) { uint16_t group; @@ -989,8 +982,8 @@ int wh_Client_KeyCacheRandom(whClientContext* c, uint32_t flags, return WH_ERROR_BADARGS; } - ret = wh_Client_KeyCacheRandomRequest_ex(c, flags, label, labelSz, - keySz, *keyId); + ret = wh_Client_KeyCacheRandomRequest(c, flags, label, labelSz, keySz, + *keyId); if (ret == 0) { do { diff --git a/src/wh_server_keystore.c b/src/wh_server_keystore.c index 2b9c4ef37..7c84e0a12 100644 --- a/src/wh_server_keystore.c +++ b/src/wh_server_keystore.c @@ -2340,19 +2340,19 @@ int wh_Server_HandleKeyRequest(whServerContext* server, uint16_t magic, #ifndef WC_NO_RNG if (ret == WH_ERROR_OK) { ret = WH_SERVER_NVM_LOCK(server); - } - if (ret == WH_ERROR_OK) { - /* get a new id if one wasn't provided */ - if (WH_KEYID_ISERASED(meta->id)) { - ret = wh_Server_KeystoreGetUniqueId(server, &meta->id); - } - /* generate the key from the server RNG and cache it */ if (ret == WH_ERROR_OK) { - ret = _KeystoreCacheRandomKey(server, meta); - } + /* get a new id if one wasn't provided */ + if (WH_KEYID_ISERASED(meta->id)) { + ret = wh_Server_KeystoreGetUniqueId(server, &meta->id); + } + /* generate the key from the server RNG and cache it */ + if (ret == WH_ERROR_OK) { + ret = _KeystoreCacheRandomKey(server, meta); + } - (void)WH_SERVER_NVM_UNLOCK(server); - } /* WH_SERVER_NVM_LOCK() */ + (void)WH_SERVER_NVM_UNLOCK(server); + } /* WH_SERVER_NVM_LOCK() */ + } #else if (ret == WH_ERROR_OK) { ret = WH_ERROR_NOTIMPL; diff --git a/test/wh_test_crypto.c b/test/wh_test_crypto.c index 9e2a36188..250aad6f7 100644 --- a/test/wh_test_crypto.c +++ b/test/wh_test_crypto.c @@ -6766,8 +6766,9 @@ static int whTest_KeyCacheRandom(whClientContext* ctx, int devId, uint16_t outLen; uint16_t keyId; uint16_t keyId2; - uint8_t keyOut[WH_TEST_KEYCACHERANDOM_KEYSIZE] = {0}; - uint8_t labelIn[WH_NVM_LABEL_LEN] = "KeyGenFromRng Test"; + uint8_t keyOut[WH_TEST_KEYCACHERANDOM_KEYSIZE] = {0}; + uint8_t keyOut2[WH_TEST_KEYCACHERANDOM_KEYSIZE] = {0}; + uint8_t labelIn[WH_NVM_LABEL_LEN] = "KeyCacheRandom Test"; uint8_t labelOut[WH_NVM_LABEL_LEN] = {0}; /* Generate a key from the server RNG and cache it */ @@ -6814,7 +6815,8 @@ static int whTest_KeyCacheRandom(whClientContext* ctx, int devId, } } - /* A second generation should yield a distinct auto-assigned keyId */ + /* A second generation should yield a distinct auto-assigned keyId and + * distinct key material */ if (ret == 0) { keyId2 = WH_KEYID_ERASED; ret = wh_Client_KeyCacheRandom(ctx, 0, labelIn, sizeof(labelIn), @@ -6826,6 +6828,24 @@ static int whTest_KeyCacheRandom(whClientContext* ctx, int devId, WH_ERROR_PRINT("KeyCacheRandom reused keyId 0x%X\n", keyId); ret = -1; } + else { + /* Export the second key and confirm its bytes differ from the + * first — two RNG generations must not produce the same key */ + outLen = sizeof(keyOut2); + ret = wh_Client_KeyExport(ctx, keyId2, labelOut, + sizeof(labelOut), keyOut2, &outLen); + if (ret != 0) { + WH_ERROR_PRINT("Failed to wh_Client_KeyExport(2) %d\n", ret); + } + else if (outLen != WH_TEST_KEYCACHERANDOM_KEYSIZE) { + WH_ERROR_PRINT("KeyCacheRandom(2) bad length %u\n", outLen); + ret = -1; + } + else if (memcmp(keyOut, keyOut2, outLen) == 0) { + WH_ERROR_PRINT("KeyCacheRandom produced identical keys\n"); + ret = -1; + } + } (void)wh_Client_KeyEvict(ctx, keyId2); } diff --git a/wolfhsm/wh_client.h b/wolfhsm/wh_client.h index c1b8e205f..df7eff708 100644 --- a/wolfhsm/wh_client.h +++ b/wolfhsm/wh_client.h @@ -726,26 +726,9 @@ int wh_Client_KeyCache(whClientContext* c, uint32_t flags, uint8_t* label, * will be generated. * @return int Returns 0 on success, or a negative error code on failure. */ -int wh_Client_KeyCacheRandomRequest_ex(whClientContext* c, uint32_t flags, - uint8_t* label, uint16_t labelSz, - uint16_t keySz, uint16_t keyId); - -/** - * @brief Sends a request to generate a key from the server RNG and cache it. - * - * Like wh_Client_KeyCacheRandomRequest_ex, but always requests a - * server-assigned key ID (WH_KEYID_ERASED). This function does not block. - * - * @param[in] c Pointer to the client context. - * @param[in] flags Flags (whNvmFlags) for the generated key. - * @param[in] label Pointer to the label associated with the key. - * @param[in] labelSz Size of the label. - * @param[in] keySz Number of random key bytes to generate. - * @return int Returns 0 on success, or a negative error code on failure. - */ int wh_Client_KeyCacheRandomRequest(whClientContext* c, uint32_t flags, - uint8_t* label, uint16_t labelSz, - uint16_t keySz); + uint8_t* label, uint16_t labelSz, + uint16_t keySz, uint16_t keyId); /** * @brief Receives the response to a generate-and-cache request.