From c9aa5043d964ad84ad0a6f9ae51c8272c3b66456 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Thu, 25 Jun 2026 10:47:52 -0700 Subject: [PATCH 01/13] F-6031 - Zeroize SHE plaintext key in client comm buffer after send --- src/wh_client_she.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/wh_client_she.c b/src/wh_client_she.c index 716934f83..4d772f222 100644 --- a/src/wh_client_she.c +++ b/src/wh_client_she.c @@ -46,6 +46,7 @@ #include "wolfhsm/wh_client.h" #include "wolfhsm/wh_client_she.h" +#include "wolfhsm/wh_utils.h" int wh_Client_ShePreProgramKey(whClientContext* c, whNvmId keyId, whNvmFlags flags, uint8_t* key, whNvmSize keySz) @@ -336,6 +337,7 @@ int wh_Client_SheLoadKey(whClientContext* c, uint8_t* messageOne, int wh_Client_SheLoadPlainKeyRequest(whClientContext* c, uint8_t* key, uint32_t keySz) { + int ret; whMessageShe_LoadPlainKeyRequest* req = NULL; if (c == NULL || key == NULL || keySz < WH_SHE_KEY_SZ) { @@ -346,8 +348,10 @@ int wh_Client_SheLoadPlainKeyRequest(whClientContext* c, uint8_t* key, memcpy(req->key, key, WH_SHE_KEY_SZ); - return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_SHE, WH_SHE_LOAD_PLAIN_KEY, - sizeof(*req), (uint8_t*)req); + ret = wh_Client_SendRequest(c, WH_MESSAGE_GROUP_SHE, WH_SHE_LOAD_PLAIN_KEY, + sizeof(*req), (uint8_t*)req); + wh_Utils_ForceZero(req->key, WH_SHE_KEY_SZ); + return ret; } int wh_Client_SheLoadPlainKeyResponse(whClientContext* c) From b31d966320d2c9fcd93c02664469518d49222cd2 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Thu, 25 Jun 2026 10:47:52 -0700 Subject: [PATCH 02/13] F-5475 - Zeroize prior credential material before storing new value --- src/wh_auth_base.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/wh_auth_base.c b/src/wh_auth_base.c index 9b49f7001..fda3547a7 100644 --- a/src/wh_auth_base.c +++ b/src/wh_auth_base.c @@ -479,6 +479,11 @@ int wh_Auth_BaseUserSetCredentials(void* context, uint16_t current_user_id, return WH_ERROR_ACCESS; } + /* A positive new-credential length requires a valid buffer */ + if (new_credentials == NULL && new_credentials_len > 0) { + return WH_ERROR_BADARGS; + } + /* Validate method is supported */ if (method != WH_AUTH_METHOD_PIN #if defined(WOLFHSM_CFG_CERTIFICATE_MANAGER) && !defined(WOLFHSM_CFG_NO_CRYPTO) @@ -543,7 +548,8 @@ int wh_Auth_BaseUserSetCredentials(void* context, uint16_t current_user_id, } } - /* Set new credentials */ + /* Set new credentials. The prior credential is zeroized only at the commit + * point so a failed update leaves the existing credential intact. */ if (new_credentials_len > 0) { if (method == WH_AUTH_METHOD_PIN) { #ifndef WOLFHSM_CFG_NO_CRYPTO @@ -555,6 +561,7 @@ int wh_Auth_BaseUserSetCredentials(void* context, uint16_t current_user_id, wh_Utils_ForceZero(hash, sizeof(hash)); return rc; } + wh_Utils_ForceZero(user->credentials, sizeof(user->credentials)); memcpy(user->credentials, hash, WC_SHA256_DIGEST_SIZE); user->credentials_len = WC_SHA256_DIGEST_SIZE; wh_Utils_ForceZero(hash, sizeof(hash)); @@ -562,6 +569,7 @@ int wh_Auth_BaseUserSetCredentials(void* context, uint16_t current_user_id, if (new_credentials_len > WH_AUTH_BASE_MAX_CREDENTIALS_LEN) { return WH_ERROR_BUFFER_SIZE; } + wh_Utils_ForceZero(user->credentials, sizeof(user->credentials)); memcpy(user->credentials, new_credentials, new_credentials_len); user->credentials_len = new_credentials_len; #endif /* WOLFHSM_CFG_NO_CRYPTO */ @@ -570,12 +578,14 @@ int wh_Auth_BaseUserSetCredentials(void* context, uint16_t current_user_id, if (new_credentials_len > WH_AUTH_BASE_MAX_CREDENTIALS_LEN) { return WH_ERROR_BUFFER_SIZE; } + wh_Utils_ForceZero(user->credentials, sizeof(user->credentials)); memcpy(user->credentials, new_credentials, new_credentials_len); user->credentials_len = new_credentials_len; } } else { /* Allow clearing credentials by setting length to 0 */ + wh_Utils_ForceZero(user->credentials, sizeof(user->credentials)); user->credentials_len = 0; } user->method = method; From 039725a74ccec45e34c381f98276a9291bdf63aa Mon Sep 17 00:00:00 2001 From: aidan garske Date: Thu, 25 Jun 2026 10:47:52 -0700 Subject: [PATCH 03/13] F-5141 - Bound config file path copy to prevent stack overflow --- examples/posix/wh_posix_server/wh_posix_server_cfg.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/examples/posix/wh_posix_server/wh_posix_server_cfg.c b/examples/posix/wh_posix_server/wh_posix_server_cfg.c index c826438f0..17a671f94 100644 --- a/examples/posix/wh_posix_server/wh_posix_server_cfg.c +++ b/examples/posix/wh_posix_server/wh_posix_server_cfg.c @@ -486,12 +486,19 @@ static void parseNvmInitFile(const char* filePath) /* Parse the file path */ token = strtok(NULL, " "); - if (!token || sscanf(token, "%s", filePath) != 1) { + if (!token) { WOLFHSM_CFG_PRINTF("Error on line %d: Malformed entry - missing file path\n", lineNumber); fclose(file); exit(EXIT_FAILURE); } + if (snprintf(filePath, sizeof(filePath), "%s", token) >= + (int)sizeof(filePath)) { + WOLFHSM_CFG_PRINTF("Error on line %d: File path too long\n", + lineNumber); + fclose(file); + exit(EXIT_FAILURE); + } /* Add the parsed entry to the linked list */ appendEntry(&entryHead, (uint8_t)clientId, (uint16_t)id, From ea858cc559460675a369826bd06096ff3f1855dd Mon Sep 17 00:00:00 2001 From: aidan garske Date: Thu, 25 Jun 2026 10:47:52 -0700 Subject: [PATCH 04/13] F-5142 - Bound whnvmtool config path copy to prevent stack overflow --- tools/whnvmtool/whnvmtool.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/tools/whnvmtool/whnvmtool.c b/tools/whnvmtool/whnvmtool.c index ec8012dbf..ffc46d0b1 100644 --- a/tools/whnvmtool/whnvmtool.c +++ b/tools/whnvmtool/whnvmtool.c @@ -384,13 +384,20 @@ void parseConfigFile(const char* filePath) /* Parse the file path */ token = strtok(NULL, " "); - if (!token || sscanf(token, "%s", filePath) != 1) { + if (!token) { fprintf(stderr, "Error on line %d: Malformed entry - missing file path\n", lineNumber); fclose(file); exit(EXIT_FAILURE); } + if (snprintf(filePath, sizeof(filePath), "%s", token) >= + (int)sizeof(filePath)) { + fprintf(stderr, "Error on line %d: File path too long\n", + lineNumber); + fclose(file); + exit(EXIT_FAILURE); + } /* Add the parsed entry to the linked list */ appendEntry(&entryHead, (uint8_t)clientId, (uint16_t)id, From 28fe33e09c9593f8c8a70458f375f0e3e6e9005f Mon Sep 17 00:00:00 2001 From: aidan garske Date: Thu, 25 Jun 2026 10:47:52 -0700 Subject: [PATCH 05/13] F-5139 - Add O_EXCL to SHM create to close name TOCTOU --- port/posix/posix_transport_shm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/port/posix/posix_transport_shm.c b/port/posix/posix_transport_shm.c index a5ba81ba9..504e6d4eb 100644 --- a/port/posix/posix_transport_shm.c +++ b/port/posix/posix_transport_shm.c @@ -208,8 +208,8 @@ static int posixTransportShm_CreateMap(char* name, uint16_t req_size, /* Attempt to remove any existing object. */ (void)shm_unlink(name); - /* Create shared memory object and set the size */ - fd = shm_open(name, O_CREAT | O_RDWR, PTSHM_CREATEMODE); + /* O_EXCL fails the open if a racing process re-created the name */ + fd = shm_open(name, O_CREAT | O_EXCL | O_RDWR, PTSHM_CREATEMODE); if (fd >= 0) { /* Set the size of the shared memory object. * Note this is the minimum size, as the OS may make it larger. */ From 04027af4f54ea6a8db074e602592134a09797511 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Thu, 25 Jun 2026 10:47:52 -0700 Subject: [PATCH 06/13] F-4235 - Propagate VERIFY_ACERT_DMA error into response before serialize --- src/wh_server_cert.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/wh_server_cert.c b/src/wh_server_cert.c index db0871605..5de6ebeeb 100644 --- a/src/wh_server_cert.c +++ b/src/wh_server_cert.c @@ -1515,15 +1515,15 @@ int wh_Server_HandleCertRequest(whServerContext* server, uint16_t magic, WH_DMA_OPER_CLIENT_READ_POST, (whServerDmaFlags){0}); } + /* Propagate any server-side error before serializing the response */ + if (rc != WH_ERROR_OK) { + resp.rc = rc; + } + /* Convert the response struct */ wh_MessageCert_TranslateSimpleResponse( magic, &resp, (whMessageCert_SimpleResponse*)resp_packet); *out_resp_size = sizeof(resp); - - /* If there was an error, return it in the response */ - if (rc != WH_ERROR_OK) { - resp.rc = rc; - } } break; #endif /* WOLFHSM_CFG_DMA */ #endif /* WOLFHSM_CFG_CERTIFICATE_MANAGER_ACERT */ From d2ec0c033ba73730c97b71b83eb7586ab76e5365 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Thu, 25 Jun 2026 10:48:38 -0700 Subject: [PATCH 07/13] F-6029 - Zeroize plaintext key in KeyWrap request comm buffer --- src/wh_client_keywrap.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/wh_client_keywrap.c b/src/wh_client_keywrap.c index 0bb1e15f0..35750eff3 100644 --- a/src/wh_client_keywrap.c +++ b/src/wh_client_keywrap.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -17,6 +18,7 @@ int wh_Client_KeyWrapRequest(whClientContext* ctx, uint16_t serverKeyId, void* key, uint16_t keySz, whNvmMetadata* metadata) { + int ret; uint16_t group = WH_MESSAGE_GROUP_KEY; uint16_t action = WH_KEY_KEYWRAP; whMessageKeystore_KeyWrapRequest* req = NULL; @@ -30,6 +32,10 @@ int wh_Client_KeyWrapRequest(whClientContext* ctx, return WH_ERROR_BADARGS; } + if (sizeof(*req) + sizeof(*metadata) + keySz > WOLFHSM_CFG_COMM_DATA_LEN) { + return WH_ERROR_BADARGS; + } + /* Set the request pointer to the shared comm data memory region */ req = (whMessageKeystore_KeyWrapRequest*)wh_CommClient_GetDataPtr(ctx->comm); @@ -47,9 +53,11 @@ int wh_Client_KeyWrapRequest(whClientContext* ctx, memcpy(reqData, metadata, sizeof(*metadata)); memcpy(reqData + sizeof(*metadata), key, keySz); - return wh_Client_SendRequest(ctx, group, action, - sizeof(*req) + sizeof(*metadata) + keySz, - (uint8_t*)req); + ret = wh_Client_SendRequest(ctx, group, action, + sizeof(*req) + sizeof(*metadata) + keySz, + (uint8_t*)req); + wh_Utils_ForceZero(reqData + sizeof(*metadata), keySz); + return ret; } int wh_Client_KeyWrapResponse(whClientContext* ctx, From c5a6ee5e44a6ee53cb3d20b73b7a9ea7d60d11c1 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Thu, 25 Jun 2026 10:48:58 -0700 Subject: [PATCH 08/13] F-6030 - Zeroize unwrapped plaintext key in export response buffer --- src/wh_client_keywrap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/wh_client_keywrap.c b/src/wh_client_keywrap.c index 35750eff3..763892c23 100644 --- a/src/wh_client_keywrap.c +++ b/src/wh_client_keywrap.c @@ -228,6 +228,7 @@ int wh_Client_KeyUnwrapAndExportResponse(whClientContext* ctx, memcpy(keyOut, respData + sizeof(*metadataOut), resp->keySz); *keyInOutSz = resp->keySz; + wh_Utils_ForceZero(respData + sizeof(*metadataOut), resp->keySz); return WH_ERROR_OK; } From 56bc2a2652b0ad843e86c43eb39b5ac151302f37 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Thu, 25 Jun 2026 10:49:39 -0700 Subject: [PATCH 09/13] F-5473 - Zeroize imported key bytes in client cache request buffer --- src/wh_client.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/wh_client.c b/src/wh_client.c index 688c42143..c6dd4db5d 100644 --- a/src/wh_client.c +++ b/src/wh_client.c @@ -37,6 +37,7 @@ /* Components */ #include "wolfhsm/wh_comm.h" +#include "wolfhsm/wh_utils.h" #ifndef WOLFHSM_CFG_NO_CRYPTO #include "wolfssl/wolfcrypt/settings.h" @@ -811,6 +812,7 @@ int wh_Client_KeyCacheRequest_ex(whClientContext* c, uint32_t flags, whMessageKeystore_CacheRequest* req = NULL; uint8_t* packIn; uint16_t capSz; + int ret; if (c == NULL || in == NULL || inSz == 0 || sizeof(*req) + inSz > WOLFHSM_CFG_COMM_DATA_LEN) { @@ -841,8 +843,10 @@ int wh_Client_KeyCacheRequest_ex(whClientContext* c, uint32_t flags, memcpy(packIn, in, inSz); /* write request */ - return wh_Client_SendRequest(c, WH_MESSAGE_GROUP_KEY, WH_KEY_CACHE, - sizeof(*req) + inSz, (uint8_t*)req); + ret = wh_Client_SendRequest(c, WH_MESSAGE_GROUP_KEY, WH_KEY_CACHE, + sizeof(*req) + inSz, (uint8_t*)req); + wh_Utils_ForceZero(packIn, inSz); + return ret; } int wh_Client_KeyCacheRequest(whClientContext* c, uint32_t flags, From 7044b67c87940c5812891d74267f554792545cec Mon Sep 17 00:00:00 2001 From: aidan garske Date: Thu, 25 Jun 2026 10:49:53 -0700 Subject: [PATCH 10/13] F-5474 - Zeroize exported key bytes in client response buffer --- src/wh_client.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/wh_client.c b/src/wh_client.c index c6dd4db5d..97562a78c 100644 --- a/src/wh_client.c +++ b/src/wh_client.c @@ -1005,6 +1005,10 @@ int wh_Client_KeyExportResponse(whClientContext* c, uint8_t* label, if (resp->rc != 0) { ret = resp->rc; } + else if (size < sizeof(*resp) || resp->len > (size - sizeof(*resp))) { + /* Response frame does not contain the claimed key length */ + ret = WH_ERROR_ABORTED; + } else { if (out == NULL) { *outSz = resp->len; @@ -1023,6 +1027,7 @@ int wh_Client_KeyExportResponse(whClientContext* c, uint8_t* label, else memcpy(label, resp->label, labelSz); } + wh_Utils_ForceZero(packOut, resp->len); } } return ret; From 74e84090aa18e3a80e50e9c75924f6b894fd0c47 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Thu, 25 Jun 2026 10:52:14 -0700 Subject: [PATCH 11/13] F-6207 - Subtract response header from HKDF and CMAC-KDF output bound --- src/wh_server_crypto.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/wh_server_crypto.c b/src/wh_server_crypto.c index 788f91f53..17bc08b71 100644 --- a/src/wh_server_crypto.c +++ b/src/wh_server_crypto.c @@ -1533,8 +1533,10 @@ static int _HandleHkdf(whServerContext* ctx, uint16_t magic, int devId, */ uint8_t* out = (uint8_t*)cryptoDataOut + sizeof(whMessageCrypto_HkdfResponse); - uint16_t max_size = (uint16_t)(WOLFHSM_CFG_COMM_DATA_LEN - - ((uint8_t*)out - (uint8_t*)cryptoDataOut)); + uint16_t max_size = + (uint16_t)(WOLFHSM_CFG_COMM_DATA_LEN - + sizeof(whMessageCrypto_GenericResponseHeader) - + sizeof(whMessageCrypto_HkdfResponse)); /* Check if output size is valid */ if (outSz > max_size) { @@ -1694,8 +1696,10 @@ static int _HandleCmacKdf(whServerContext* ctx, uint16_t magic, int devId, uint8_t* out = (uint8_t*)cryptoDataOut + sizeof(whMessageCrypto_CmacKdfResponse); - uint16_t max_size = (uint16_t)(WOLFHSM_CFG_COMM_DATA_LEN - - ((uint8_t*)out - (uint8_t*)cryptoDataOut)); + uint16_t max_size = + (uint16_t)(WOLFHSM_CFG_COMM_DATA_LEN - + sizeof(whMessageCrypto_GenericResponseHeader) - + sizeof(whMessageCrypto_CmacKdfResponse)); if (outSz > max_size) { return WH_ERROR_BADARGS; From c70003d8550c7b87c364ab6adf5dc05884dfd4e9 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Thu, 25 Jun 2026 10:53:09 -0700 Subject: [PATCH 12/13] F-4329 - Force 64-bit arithmetic in AES-GCM needed_size check --- src/wh_server_crypto.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/wh_server_crypto.c b/src/wh_server_crypto.c index 17bc08b71..0e4c012e8 100644 --- a/src/wh_server_crypto.c +++ b/src/wh_server_crypto.c @@ -3369,9 +3369,10 @@ static int _HandleAesGcm(whServerContext* ctx, uint16_t magic, int devId, uint32_t tag_len = req.authTagSz; whKeyId key_id = wh_KeyId_TranslateFromClient( WH_KEYTYPE_CRYPTO, ctx->comm->client_id, req.keyId); - uint64_t needed_size = sizeof(whMessageCrypto_AesGcmRequest) + len + - key_len + iv_len + authin_len + - ((enc == 0) ? tag_len : 0); + uint64_t needed_size = (uint64_t)sizeof(whMessageCrypto_AesGcmRequest) + + (uint64_t)len + (uint64_t)key_len + + (uint64_t)iv_len + (uint64_t)authin_len + + ((enc == 0) ? (uint64_t)tag_len : 0); if (needed_size != inSize) { return WH_ERROR_BADARGS; } From efe02ca1a08f748e2251ba41736bbc47df73429e Mon Sep 17 00:00:00 2001 From: aidan garske Date: Thu, 25 Jun 2026 10:53:35 -0700 Subject: [PATCH 13/13] F-4978 - Bound AES-GCM response output and tag to comm buffer --- src/wh_server_crypto.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/wh_server_crypto.c b/src/wh_server_crypto.c index 0e4c012e8..81966e3e4 100644 --- a/src/wh_server_crypto.c +++ b/src/wh_server_crypto.c @@ -3396,6 +3396,12 @@ static int _HandleAesGcm(whServerContext* ctx, uint16_t magic, int devId, uint32_t res_len = sizeof(whMessageCrypto_AesGcmResponse) + len + ((enc == 0) ? 0 : tag_len); + /* Ensure the response output and tag fit within the comm data buffer */ + if (res_len > (WOLFHSM_CFG_COMM_DATA_LEN - + sizeof(whMessageCrypto_GenericResponseHeader))) { + return WH_ERROR_BADARGS; + } + WH_DEBUG_SERVER_VERBOSE("AESGCM: enc:%d keylen:%d ivsz:%d insz:%d authinsz:%d " "authtagsz:%d reqsz:%u ressz:%u\n", enc, key_len, iv_len, len, authin_len, tag_len, (uint32_t)needed_size,