From c6b2104f3f5396ed509a504644685b58dd9e6758 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 11:28:56 -0700 Subject: [PATCH 01/22] F-6318 - Validate priv size marker before read in readKeyBlob --- examples/tpm_test_keys.c | 20 ++++++++--- tests/unit_tests.c | 75 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 4 deletions(-) diff --git a/examples/tpm_test_keys.c b/examples/tpm_test_keys.c index 3988e1b2..43142b71 100644 --- a/examples/tpm_test_keys.c +++ b/examples/tpm_test_keys.c @@ -224,7 +224,7 @@ int readKeyBlob(const char* filename, WOLFTPM2_KEYBLOB* key) goto exit; } fileSz -= bytes_read; - if (key->pub.size > sizeof(UINT16) + sizeof(pubAreaBuffer)) { + if (sizeof(UINT16) + key->pub.size > sizeof(pubAreaBuffer)) { printf("Public key size is too large\n"); rc = BUFFER_E; goto exit; } @@ -249,10 +249,22 @@ int readKeyBlob(const char* filename, WOLFTPM2_KEYBLOB* key) if (fileSz > 0) { printf("Reading the private part of the key\n"); - bytes_read = XFREAD(&key->priv, 1, fileSz, fp); - if (bytes_read != fileSz) { + /* Read and validate the size marker before the buffer so a + * crafted file cannot overflow priv */ + bytes_read = XFREAD(&key->priv.size, 1, sizeof(key->priv.size), fp); + if (bytes_read != sizeof(key->priv.size)) { + printf("Read %zu, expected private size marker of %zu bytes\n", + bytes_read, sizeof(key->priv.size)); + goto exit; + } + if (key->priv.size > sizeof(key->priv.buffer)) { + printf("Private key size is too large\n"); + rc = BUFFER_E; goto exit; + } + bytes_read = XFREAD(key->priv.buffer, 1, key->priv.size, fp); + if (bytes_read != key->priv.size) { printf("Read %zu, expected private blob %zu bytes\n", - bytes_read, fileSz); + bytes_read, (size_t)key->priv.size); goto exit; } rc = 0; /* success */ diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 8afe674f..1e6a89fe 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -3826,6 +3826,77 @@ static void test_wolfTPM2_SPDM_Functions(void) } #endif /* WOLFTPM_SPDM */ +#if !defined(NO_FILESYSTEM) && !defined(NO_WRITE_TEMP_FILES) && \ + !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) +/* Craft a key-blob file with a small valid public area and an oversized + * private tail, then load it with readKeyBlob(). A canary placed directly + * after the keyblob catches any write past it. No TPM is required. */ +static void test_readKeyBlob_PrivOverflow(void) +{ + int rc; + int pubAreaSize = 0; + word32 i; + size_t privTailSz, remaining, chunk; + UINT16 privSizeMarker; + XFILE fp; + byte pubAreaBuffer[sizeof(TPM2B_PUBLIC)]; + byte filler[64]; + WOLFTPM2_KEYBLOB tmpl; + const char* filename = "keyblob_overflow_test.raw"; + struct { + WOLFTPM2_KEYBLOB key; + byte canary[128]; + } guarded; + + XMEMSET(&tmpl, 0, sizeof(tmpl)); + + /* Build a real minimal public area so TPM2_ParsePublic() accepts it and + * readKeyBlob proceeds to the private read. */ + rc = wolfTPM2_GetKeyTemplate_ECC(&tmpl.pub.publicArea, + TPMA_OBJECT_sign | TPMA_OBJECT_userWithAuth | TPMA_OBJECT_noDA, + TPM_ECC_NIST_P256, TPM_ALG_ECDSA); + AssertIntEQ(rc, 0); + rc = TPM2_AppendPublic(pubAreaBuffer, (word32)sizeof(pubAreaBuffer), + &pubAreaSize, &tmpl.pub); + AssertIntEQ(rc, 0); + + /* Private tail longer than the destination so the pre-fix path, which + * reads the whole remaining file into &key->priv, overflows past the + * keyblob. Keep the size marker small and valid so the post-read sanity + * check passes. */ + privTailSz = sizeof(guarded.key.priv) + sizeof(filler); + privSizeMarker = 32; + + fp = XFOPEN(filename, "wb"); + AssertNotNull(fp); + XFWRITE(&tmpl.pub.size, 1, sizeof(tmpl.pub.size), fp); + XFWRITE(pubAreaBuffer, 1, sizeof(UINT16) + tmpl.pub.size, fp); + XFWRITE(&privSizeMarker, 1, sizeof(privSizeMarker), fp); + XMEMSET(filler, 0xAB, sizeof(filler)); + remaining = privTailSz - sizeof(privSizeMarker); + while (remaining > 0) { + chunk = (remaining < sizeof(filler)) ? remaining : sizeof(filler); + XFWRITE(filler, 1, chunk, fp); + remaining -= chunk; + } + XFCLOSE(fp); + + XMEMSET(&guarded, 0, sizeof(guarded)); + XMEMSET(guarded.canary, 0x5A, sizeof(guarded.canary)); + + rc = readKeyBlob(filename, &guarded.key); + (void)rc; + + for (i = 0; i < sizeof(guarded.canary); i++) { + AssertIntEQ(guarded.canary[i], 0x5A); + } + + remove(filename); + + printf("Test TPM Wrapper: %-40s Passed\n", "readKeyBlob priv overflow:"); +} +#endif + /* Test creating key and exporting keyblob as buffer, * importing and loading key. */ static void test_wolfTPM2_KeyBlob(TPM_ALG_ID alg) @@ -5251,6 +5322,10 @@ int unit_tests(int argc, char *argv[]) test_wolfTPM2_NVDeleteKey_BoundaryChecks(); test_wolfTPM2_UnloadHandle_PersistentGuard(); test_TPM2_GetHashDigestSize_AllAlgs(); + #if !defined(NO_FILESYSTEM) && !defined(NO_WRITE_TEMP_FILES) && \ + !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) + test_readKeyBlob_PrivOverflow(); + #endif test_wolfTPM2_KeyBlob(TPM_ALG_RSA); test_wolfTPM2_KeyBlob(TPM_ALG_ECC); #if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) && \ From 52ae1f9a0ef70b9471801f10b8528a6d7ad3fd68 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 11:30:41 -0700 Subject: [PATCH 02/22] F-6310 - Enable Nuvoton GPIO pull-up by setting the bit --- examples/gpio/gpio_config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/gpio/gpio_config.c b/examples/gpio/gpio_config.c index 92ed6c61..20d79166 100644 --- a/examples/gpio/gpio_config.c +++ b/examples/gpio/gpio_config.c @@ -377,7 +377,7 @@ int TPM2_GPIO_Config_Example(void* userCtx, int argc, char *argv[]) /* Extra step for open-drain with pull-up mode */ if (gpioMode == TPM_GPIO_MODE_PULLUP) { - newConfig.GpioPullUp &= ~(1 << gpioNum); + newConfig.GpioPullUp |= (1 << gpioNum); } #ifdef WOLFTPM_DEBUG_VERBOSE From 7e060e1915b5459b9862c47a02064b15f41d249f Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 11:37:35 -0700 Subject: [PATCH 03/22] F-6315 - Add testable swtpm response-size validation helper --- src/tpm2_swtpm.c | 17 ++++++++++++++--- tests/unit_tests.c | 27 +++++++++++++++++++++++++++ wolftpm/tpm2_swtpm.h | 3 +++ 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/src/tpm2_swtpm.c b/src/tpm2_swtpm.c index 89a542f3..b37d9c84 100644 --- a/src/tpm2_swtpm.c +++ b/src/tpm2_swtpm.c @@ -478,6 +478,18 @@ static TPM_RC SwTpmDisconnect(TPM2_CTX* ctx) return rc; } +/* Validate a swtpm-reported response size against the receive buffer before + * it is used to read into packet->buf. Rejects undersized headers and any + * length that would overflow the buffer. */ +int TPM2_SwtpmValidateRspSz(int packetSize, uint32_t rspSz) +{ + int rc = TPM_RC_SUCCESS; + if (rspSz < TPM2_HEADER_SIZE || rspSz > (uint32_t)packetSize) { + rc = TPM_RC_FAILURE; + } + return rc; +} + /* Talk to a TPM through socket * return TPM_RC_SUCCESS on success, * TPM_RC_FAILURE on other errors @@ -543,13 +555,12 @@ int TPM2_SWTPM_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet) if (rc == TPM_RC_SUCCESS) { rc = SwTpmReceive(ctx, &tss_word, sizeof(uint32_t)); rspSz = TPM2_Packet_SwapU32(tss_word); - if (rspSz < TPM2_HEADER_SIZE || - rspSz > (uint32_t)packet->size) { + rc = TPM2_SwtpmValidateRspSz(packet->size, rspSz); + if (rc != TPM_RC_SUCCESS) { #ifdef WOLFTPM_DEBUG_VERBOSE printf("Response size(%u) out of range (header=%d, buf=%d)\n", rspSz, TPM2_HEADER_SIZE, packet->size); #endif - rc = TPM_RC_FAILURE; } } diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 1e6a89fe..c47a263e 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -3826,6 +3827,29 @@ static void test_wolfTPM2_SPDM_Functions(void) } #endif /* WOLFTPM_SPDM */ +#ifdef WOLFTPM_SWTPM +/* Pin the swtpm response-size bounds so a mutation dropping either the + * lower (header) or upper (buffer) bound is caught. */ +static void test_TPM2_SwtpmValidateRspSz(void) +{ + int packetSize = 4096; + + AssertIntEQ(TPM2_SwtpmValidateRspSz(packetSize, TPM2_HEADER_SIZE), + TPM_RC_SUCCESS); + AssertIntEQ(TPM2_SwtpmValidateRspSz(packetSize, (uint32_t)packetSize), + TPM_RC_SUCCESS); + + AssertIntEQ(TPM2_SwtpmValidateRspSz(packetSize, TPM2_HEADER_SIZE - 1), + TPM_RC_FAILURE); + AssertIntEQ(TPM2_SwtpmValidateRspSz(packetSize, (uint32_t)packetSize + 1), + TPM_RC_FAILURE); + AssertIntEQ(TPM2_SwtpmValidateRspSz(packetSize, 0xFFFFFFFFUL), + TPM_RC_FAILURE); + + printf("Test TPM2: %-40s Passed\n", "Swtpm ValidateRspSz:"); +} +#endif /* WOLFTPM_SWTPM */ + #if !defined(NO_FILESYSTEM) && !defined(NO_WRITE_TEMP_FILES) && \ !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) /* Craft a key-blob file with a small valid public area and an oversized @@ -5322,6 +5346,9 @@ int unit_tests(int argc, char *argv[]) test_wolfTPM2_NVDeleteKey_BoundaryChecks(); test_wolfTPM2_UnloadHandle_PersistentGuard(); test_TPM2_GetHashDigestSize_AllAlgs(); + #ifdef WOLFTPM_SWTPM + test_TPM2_SwtpmValidateRspSz(); + #endif #if !defined(NO_FILESYSTEM) && !defined(NO_WRITE_TEMP_FILES) && \ !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) test_readKeyBlob_PrivOverflow(); diff --git a/wolftpm/tpm2_swtpm.h b/wolftpm/tpm2_swtpm.h index 48db51eb..982c5fbd 100644 --- a/wolftpm/tpm2_swtpm.h +++ b/wolftpm/tpm2_swtpm.h @@ -47,6 +47,9 @@ /* TPM2 IO for using TPM through a Socket connection */ WOLFTPM_LOCAL int TPM2_SWTPM_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet); +/* Validate a swtpm-reported response size against the receive buffer size */ +WOLFTPM_API int TPM2_SwtpmValidateRspSz(int packetSize, uint32_t rspSz); + #ifdef WOLFTPM_SWTPM_UART /* Close the persistent UART FD during final TPM context cleanup */ WOLFTPM_LOCAL void TPM2_SwtpmCloseUART(TPM2_CTX* ctx); From 3696b0a7b02b2a11217a3297e6c1292046c2d8db Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 11:38:55 -0700 Subject: [PATCH 04/22] F-6319 - Clamp NV dataSize to digest buffer in secure_rot --- examples/boot/secure_rot.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/boot/secure_rot.c b/examples/boot/secure_rot.c index c009e139..69cc3a0d 100644 --- a/examples/boot/secure_rot.c +++ b/examples/boot/secure_rot.c @@ -252,6 +252,9 @@ int TPM2_Boot_SecureROT_Example(void* userCtx, int argc, char *argv[]) rc = wolfTPM2_NVReadPublic(&dev, nvIndex, &nvPublic); if (rc == 0) { digestSz = nvPublic.dataSize; + if (digestSz > (int)sizeof(digest)) { + digestSz = (int)sizeof(digest); + } } /* Read access */ From 4d3b2d61e4e59118a3b46f71070fbbb49c6c4cf6 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 11:41:28 -0700 Subject: [PATCH 05/22] F-6320 - Zeroize GetRandom response buffer in TPM2_GetNonceNoLock --- src/tpm2.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tpm2.c b/src/tpm2.c index c594df12..23343461 100644 --- a/src/tpm2.c +++ b/src/tpm2.c @@ -6529,6 +6529,8 @@ int TPM2_GetNonceNoLock(byte* nonceBuf, int nonceSz) TPM2_Packet_ParseBytes(&packet, &nonceBuf[randSz], outSz); randSz += outSz; } + /* response buffer held freshly generated random; wipe before return */ + TPM2_ForceZero(buffer, sizeof(buffer)); #endif return rc; From 211d4391b97c3adac7c764473b5c54d94a7f9c90 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 11:42:59 -0700 Subject: [PATCH 06/22] F-4165 - Zeroize partial SPDM key material on derive failure --- src/spdm/spdm_kdf.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/spdm/spdm_kdf.c b/src/spdm/spdm_kdf.c index e17158ce..e823ed64 100644 --- a/src/spdm/spdm_kdf.c +++ b/src/spdm/spdm_kdf.c @@ -170,6 +170,19 @@ int wolfSPDM_DeriveFromHandshakeSecret(WOLFSPDM_CTX* ctx, ctx->rspDataKey, ctx->rspDataIv); } + if (rc != WOLFSPDM_SUCCESS) { + /* wipe any partially derived material so it cannot carry into a + * reused ctx */ + wc_ForceZero(ctx->reqHsSecret, sizeof(ctx->reqHsSecret)); + wc_ForceZero(ctx->rspHsSecret, sizeof(ctx->rspHsSecret)); + wc_ForceZero(ctx->reqFinishedKey, sizeof(ctx->reqFinishedKey)); + wc_ForceZero(ctx->rspFinishedKey, sizeof(ctx->rspFinishedKey)); + wc_ForceZero(ctx->reqDataKey, sizeof(ctx->reqDataKey)); + wc_ForceZero(ctx->rspDataKey, sizeof(ctx->rspDataKey)); + wc_ForceZero(ctx->reqDataIv, sizeof(ctx->reqDataIv)); + wc_ForceZero(ctx->rspDataIv, sizeof(ctx->rspDataIv)); + } + return rc; } From 2246a42e3d3b2dab05ba8117cb7567921af9a9c8 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 11:48:56 -0700 Subject: [PATCH 07/22] F-5656 - Scrub wrapped private blob from CreateKey stack output --- src/tpm2_wrap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index bc1d8de1..ab2f8fc5 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -2943,6 +2943,7 @@ int wolfTPM2_CreateKey(WOLFTPM2_DEV* dev, WOLFTPM2_KEYBLOB* keyBlob, wolfTPM2_CopyPriv(&keyBlob->priv, &createOut.outPrivate); TPM2_ForceZero(&createIn.inSensitive, sizeof(createIn.inSensitive)); + TPM2_ForceZero(&createOut.outPrivate, sizeof(createOut.outPrivate)); return rc; } From 95eb1f7e5b05c12ff7011fe5c71ebc65f6e93b36 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 11:49:06 -0700 Subject: [PATCH 08/22] F-5657 - Scrub wrapped private blob from CreateLoadedKey stack output --- src/tpm2_wrap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index ab2f8fc5..776e3e9c 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -3089,6 +3089,8 @@ int wolfTPM2_CreateLoadedKey(WOLFTPM2_DEV* dev, WOLFTPM2_KEYBLOB* keyBlob, TPM2_ForceZero(&createLoadedIn.inSensitive, sizeof(createLoadedIn.inSensitive)); + TPM2_ForceZero(&createLoadedOut.outPrivate, + sizeof(createLoadedOut.outPrivate)); return rc; } From 615b7e43c3ce40974a86f4c04a413f73f3d3c104 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 11:49:32 -0700 Subject: [PATCH 09/22] F-5661 - Scrub sealed key blob from CreateKeySeal stack output --- src/tpm2_wrap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 776e3e9c..5051fe37 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -9199,6 +9199,7 @@ int wolfTPM2_CreateKeySeal_ex(WOLFTPM2_DEV* dev, WOLFTPM2_KEYBLOB* keyBlob, wolfTPM2_CopyPriv(&keyBlob->priv, &createOut.outPrivate); TPM2_ForceZero(&createIn.inSensitive, sizeof(createIn.inSensitive)); + TPM2_ForceZero(&createOut.outPrivate, sizeof(createOut.outPrivate)); return rc; } From 577f3ab05c59693b3a81a71ac0c3f0fb34552b1b Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 11:52:22 -0700 Subject: [PATCH 10/22] F-6321 - Zeroize decrypted TPM response buffer in TPM2_SPDM_SendCommand --- src/tpm2.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/tpm2.c b/src/tpm2.c index 23343461..aa95dbf0 100644 --- a/src/tpm2.c +++ b/src/tpm2.c @@ -450,14 +450,19 @@ static TPM_RC TPM2_SPDM_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet) rc = wolfTPM2_SPDM_SecuredExchange(spdmCtx, packet->buf, packet->pos, tpmResp, &tpmRespSz); - if (rc != 0) + if (rc != 0) { + TPM2_ForceZero(tpmResp, sizeof(tpmResp)); return rc; + } - if (tpmRespSz > MAX_RESPONSE_SIZE) + if (tpmRespSz > MAX_RESPONSE_SIZE) { + TPM2_ForceZero(tpmResp, sizeof(tpmResp)); return TPM_RC_SIZE; + } XMEMCPY(packet->buf, tpmResp, tpmRespSz); packet->pos = 0; packet->size = tpmRespSz; + TPM2_ForceZero(tpmResp, sizeof(tpmResp)); return TPM_RC_SUCCESS; } #endif /* WOLFTPM_SPDM */ From 6b3b50f35e8e000b995e75f350d9e0804f2c0677 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 11:52:22 -0700 Subject: [PATCH 11/22] F-5662 - Zeroize TIS transport tx/rx buffers after TPM traffic --- src/tpm2_tis.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tpm2_tis.c b/src/tpm2_tis.c index 717345bb..72ce7c3c 100644 --- a/src/tpm2_tis.c +++ b/src/tpm2_tis.c @@ -203,6 +203,8 @@ int TPM2_TIS_Read(TPM2_CTX* ctx, word32 addr, byte* result, rc = ctx->ioCb(ctx, txBuf, rxBuf, len + TPM_TIS_HEADER_SZ, ctx->userCtx); XMEMCPY(result, &rxBuf[TPM_TIS_HEADER_SZ], len); + TPM2_ForceZero(txBuf, sizeof(txBuf)); + TPM2_ForceZero(rxBuf, sizeof(rxBuf)); #endif TPM2_TIS_UNLOCK(); #ifdef WOLFTPM_DEBUG_IO @@ -243,6 +245,8 @@ int TPM2_TIS_Write(TPM2_CTX* ctx, word32 addr, const byte* value, XMEMSET(rxBuf, 0, sizeof(rxBuf)); rc = ctx->ioCb(ctx, txBuf, rxBuf, len + TPM_TIS_HEADER_SZ, ctx->userCtx); + TPM2_ForceZero(txBuf, sizeof(txBuf)); + TPM2_ForceZero(rxBuf, sizeof(rxBuf)); #endif TPM2_TIS_UNLOCK(); #ifdef WOLFTPM_DEBUG_IO From 98a5f95fffcbf3c20509daf6de5949dac9605039 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 11:55:03 -0700 Subject: [PATCH 12/22] F-6312 - Clamp classic-arm inner size in TPM2_Packet_AppendSensitive --- src/tpm2_packet.c | 8 ++++++++ tests/unit_tests.c | 27 +++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/tpm2_packet.c b/src/tpm2_packet.c index 338837b6..66c5d4df 100644 --- a/src/tpm2_packet.c +++ b/src/tpm2_packet.c @@ -830,18 +830,26 @@ void TPM2_Packet_AppendSensitive(TPM2_Packet* packet, TPM2B_SENSITIVE* sensitive switch (sensitive->sensitiveArea.sensitiveType) { case TPM_ALG_RSA: + if (sens->rsa.size > sizeof(sens->rsa.buffer)) + sens->rsa.size = sizeof(sens->rsa.buffer); TPM2_Packet_AppendU16(packet, sens->rsa.size); TPM2_Packet_AppendBytes(packet, sens->rsa.buffer, sens->rsa.size); break; case TPM_ALG_ECC: + if (sens->ecc.size > sizeof(sens->ecc.buffer)) + sens->ecc.size = sizeof(sens->ecc.buffer); TPM2_Packet_AppendU16(packet, sens->ecc.size); TPM2_Packet_AppendBytes(packet, sens->ecc.buffer, sens->ecc.size); break; case TPM_ALG_KEYEDHASH: + if (sens->bits.size > sizeof(sens->bits.buffer)) + sens->bits.size = sizeof(sens->bits.buffer); TPM2_Packet_AppendU16(packet, sens->bits.size); TPM2_Packet_AppendBytes(packet, sens->bits.buffer, sens->bits.size); break; case TPM_ALG_SYMCIPHER: + if (sens->sym.size > sizeof(sens->sym.buffer)) + sens->sym.size = sizeof(sens->sym.buffer); TPM2_Packet_AppendU16(packet, sens->sym.size); TPM2_Packet_AppendBytes(packet, sens->sym.buffer, sens->sym.size); break; diff --git a/tests/unit_tests.c b/tests/unit_tests.c index c47a263e..0f9becae 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -3135,6 +3135,32 @@ static void test_TPM2_Sensitive_Roundtrip(void) printf("Test TPM Wrapper: %-40s Passed\n", "Sensitive roundtrip:"); } +/* An oversized inner size on a classic arm must be clamped to the arm buffer + * size, matching the PQC arms, so AppendBytes does not over-read the source. */ +static void test_TPM2_AppendSensitive_Clamp(void) +{ + TPM2_Packet packet; + byte buf[1024]; + TPM2B_SENSITIVE sens; + word16 bufCap; + + XMEMSET(&sens, 0, sizeof(sens)); + sens.sensitiveArea.sensitiveType = TPM_ALG_RSA; + bufCap = (word16)sizeof(sens.sensitiveArea.sensitive.rsa.buffer); + sens.sensitiveArea.sensitive.rsa.size = bufCap + 100; + + XMEMSET(buf, 0, sizeof(buf)); + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = buf; + packet.size = sizeof(buf); + + TPM2_Packet_AppendSensitive(&packet, &sens); + + AssertIntEQ(sens.sensitiveArea.sensitive.rsa.size, bufCap); + + printf("Test TPM2: %-40s Passed\n", "AppendSensitive clamp:"); +} + static void test_KeySealTemplate(void) { int rc; @@ -5322,6 +5348,7 @@ int unit_tests(int argc, char *argv[]) test_TPM2_Public_PQC_Roundtrip(); #endif test_TPM2_Sensitive_Roundtrip(); + test_TPM2_AppendSensitive_Clamp(); test_KeySealTemplate(); test_SealAndKeyedHash_Boundaries(); test_GetAlgId(); From af8fe5b72bf90659ec9f1c381f1ea1989a21ec93 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 11:56:46 -0700 Subject: [PATCH 13/22] F-6313 - Clear publicArea when ParsePublic reads zero size --- src/tpm2_packet.c | 3 +++ tests/unit_tests.c | 29 +++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/src/tpm2_packet.c b/src/tpm2_packet.c index 66c5d4df..1c0125d1 100644 --- a/src/tpm2_packet.c +++ b/src/tpm2_packet.c @@ -1214,6 +1214,9 @@ void TPM2_Packet_ParsePublic(TPM2_Packet* packet, TPM2B_PUBLIC* pub) TPM2_Packet_ParseU16(packet, &pub->size); pubStartPos = (packet != NULL) ? packet->pos : 0; + if (pub->size == 0) { + XMEMSET(&pub->publicArea, 0, sizeof(pub->publicArea)); + } if (pub->size > 0) { TPM2_Packet_ParseU16(packet, &pub->publicArea.type); TPM2_Packet_ParseU16(packet, &pub->publicArea.nameAlg); diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 0f9becae..5f6c1393 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -3135,6 +3135,34 @@ static void test_TPM2_Sensitive_Roundtrip(void) printf("Test TPM Wrapper: %-40s Passed\n", "Sensitive roundtrip:"); } +/* A zero-size TPM2B_PUBLIC must clear publicArea so stale fields from a reused + * struct cannot survive a parse. */ +static void test_TPM2_ParsePublic_EmptyClears(void) +{ + TPM2_Packet packet; + byte buf[8]; + TPM2B_PUBLIC pub; + + XMEMSET(&pub, 0, sizeof(pub)); + pub.publicArea.type = TPM_ALG_RSA; + pub.publicArea.nameAlg = TPM_ALG_SHA256; + pub.publicArea.objectAttributes = 0xFFFFFFFFUL; + + XMEMSET(buf, 0, sizeof(buf)); + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = buf; + packet.size = sizeof(buf); + + TPM2_Packet_ParsePublic(&packet, &pub); + + AssertIntEQ(pub.size, 0); + AssertIntEQ(pub.publicArea.type, 0); + AssertIntEQ(pub.publicArea.nameAlg, 0); + AssertIntEQ(pub.publicArea.objectAttributes, 0); + + printf("Test TPM2: %-40s Passed\n", "ParsePublic empty clears:"); +} + /* An oversized inner size on a classic arm must be clamped to the arm buffer * size, matching the PQC arms, so AppendBytes does not over-read the source. */ static void test_TPM2_AppendSensitive_Clamp(void) @@ -5348,6 +5376,7 @@ int unit_tests(int argc, char *argv[]) test_TPM2_Public_PQC_Roundtrip(); #endif test_TPM2_Sensitive_Roundtrip(); + test_TPM2_ParsePublic_EmptyClears(); test_TPM2_AppendSensitive_Clamp(); test_KeySealTemplate(); test_SealAndKeyedHash_Boundaries(); From 02bc64173a12e8b6e6d930717ac0d783d12acd67 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 12:00:04 -0700 Subject: [PATCH 14/22] F-6316 - Add testable TIS response-size validation helper --- src/tpm2_tis.c | 15 +++++++++++++-- tests/unit_tests.c | 23 +++++++++++++++++++++++ wolftpm/tpm2_tis.h | 1 + 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/tpm2_tis.c b/src/tpm2_tis.c index 72ce7c3c..3b67c5b8 100644 --- a/src/tpm2_tis.c +++ b/src/tpm2_tis.c @@ -439,6 +439,17 @@ int TPM2_TIS_GetBurstCount(TPM2_CTX* ctx, word16* burstCount) return rc; } +/* Validate a TPM-reported response size from the wire header against the + * caller buffer before the receive loop writes into it. */ +int TPM2_TIS_ValidateRspSz(int rspSz, int packetSize) +{ + int rc = TPM_RC_SUCCESS; + if (rspSz < 0 || rspSz >= MAX_RESPONSE_SIZE || rspSz > packetSize) { + rc = TPM_RC_FAILURE; + } + return rc; +} + int TPM2_TIS_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet) { int rc; @@ -561,8 +572,8 @@ int TPM2_TIS_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet) rspSz = TPM2_Packet_SwapU32(tmpSz); /* safety check for stuck FFFF case */ - if (rspSz < 0 || rspSz >= MAX_RESPONSE_SIZE || rspSz > packet->size) { - rc = TPM_RC_FAILURE; + rc = TPM2_TIS_ValidateRspSz(rspSz, packet->size); + if (rc != TPM_RC_SUCCESS) { goto exit; } } diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 5f6c1393..58f59b9e 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -3135,6 +3136,27 @@ static void test_TPM2_Sensitive_Roundtrip(void) printf("Test TPM Wrapper: %-40s Passed\n", "Sensitive roundtrip:"); } +/* Pin the TIS response-size bounds so a mutation dropping the buffer-bound or + * the MAX_RESPONSE_SIZE term is caught. */ +static void test_TPM2_TIS_ValidateRspSz(void) +{ + int packetSize = 1024; + + AssertIntEQ(TPM2_TIS_ValidateRspSz(TPM2_HEADER_SIZE, packetSize), + TPM_RC_SUCCESS); + AssertIntEQ(TPM2_TIS_ValidateRspSz(packetSize, packetSize), + TPM_RC_SUCCESS); + + AssertIntEQ(TPM2_TIS_ValidateRspSz(packetSize + 1, packetSize), + TPM_RC_FAILURE); + AssertIntEQ(TPM2_TIS_ValidateRspSz(MAX_RESPONSE_SIZE, MAX_RESPONSE_SIZE), + TPM_RC_FAILURE); + AssertIntEQ(TPM2_TIS_ValidateRspSz(-1, packetSize), + TPM_RC_FAILURE); + + printf("Test TPM2: %-40s Passed\n", "TIS ValidateRspSz:"); +} + /* A zero-size TPM2B_PUBLIC must clear publicArea so stale fields from a reused * struct cannot survive a parse. */ static void test_TPM2_ParsePublic_EmptyClears(void) @@ -5376,6 +5398,7 @@ int unit_tests(int argc, char *argv[]) test_TPM2_Public_PQC_Roundtrip(); #endif test_TPM2_Sensitive_Roundtrip(); + test_TPM2_TIS_ValidateRspSz(); test_TPM2_ParsePublic_EmptyClears(); test_TPM2_AppendSensitive_Clamp(); test_KeySealTemplate(); diff --git a/wolftpm/tpm2_tis.h b/wolftpm/tpm2_tis.h index 42bcdbec..28c10b41 100644 --- a/wolftpm/tpm2_tis.h +++ b/wolftpm/tpm2_tis.h @@ -52,6 +52,7 @@ WOLFTPM_LOCAL int TPM2_TIS_GetBurstCount(TPM2_CTX* ctx, word16* burstCount); WOLFTPM_LOCAL int TPM2_TIS_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet); +WOLFTPM_API int TPM2_TIS_ValidateRspSz(int rspSz, int packetSize); WOLFTPM_LOCAL int TPM2_TIS_Ready(TPM2_CTX* ctx); WOLFTPM_LOCAL int TPM2_TIS_WaitForStatus(TPM2_CTX* ctx, byte status, byte status_mask); WOLFTPM_LOCAL int TPM2_TIS_Status(TPM2_CTX* ctx, byte* status); From 05f6cb7fd022eb22896a1f3f338b1ce5cb63595a Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 12:02:29 -0700 Subject: [PATCH 15/22] F-6317 - Add testable SPDM I/O response-size validation helper --- src/tpm2_spdm.c | 17 +++++++++++++++-- tests/unit_tests.c | 18 ++++++++++++++++++ wolftpm/tpm2_spdm.h | 4 ++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/tpm2_spdm.c b/src/tpm2_spdm.c index a0859a72..63f912df 100644 --- a/src/tpm2_spdm.c +++ b/src/tpm2_spdm.c @@ -66,6 +66,16 @@ /* TIS I/O Callback (SPI/I2C TPM transport for SPDM) */ /* -------------------------------------------------------------------------- */ +/* Bound a framed response size against both the caller buffer and the local + * I/O buffer before copying into the caller's rxBuf. */ +int wolfTPM2_SPDM_ValidateRspSz(word32 rspSz, word32 rxSz, word32 ioBufSz) +{ + if (rspSz > rxSz || rspSz > ioBufSz) { + return -1; + } + return 0; +} + #ifdef WOLFTPM_SPDM_TIS_IO /* TIS I/O callback for routing wolfSPDM through TPM SPI/I2C FIFO. * This matches the WOLFSPDM_IO_CB signature. TCG framing (headers) is @@ -117,7 +127,7 @@ static int wolfTPM2_SPDM_TisIoCb( XMEMCPY(&rspSz, &ioBuf[2], sizeof(UINT32)); rspSz = TPM2_Packet_SwapU32(rspSz); - if (rspSz > *rxSz || rspSz > sizeof(ioBuf)) { + if (wolfTPM2_SPDM_ValidateRspSz(rspSz, *rxSz, sizeof(ioBuf)) != 0) { return -1; } @@ -171,7 +181,10 @@ static int wolfTPM2_SPDM_SwtpmIoCb( XMEMCPY(&rspSz, &ioBuf[2], sizeof(word32)); rspSz = TPM2_Packet_SwapU32(rspSz); - if (rspSz < TPM2_HEADER_SIZE || rspSz > *rxSz || rspSz > sizeof(ioBuf)) { + if (rspSz < TPM2_HEADER_SIZE) { + return -1; + } + if (wolfTPM2_SPDM_ValidateRspSz(rspSz, *rxSz, sizeof(ioBuf)) != 0) { return -1; } diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 58f59b9e..ae860df5 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -3136,6 +3137,22 @@ static void test_TPM2_Sensitive_Roundtrip(void) printf("Test TPM Wrapper: %-40s Passed\n", "Sensitive roundtrip:"); } +#ifdef WOLFTPM_SPDM +/* Pin the rxBuf bound in the SPDM I/O callbacks: a response larger than the + * caller buffer must be rejected even when it fits the local I/O buffer. */ +static void test_wolfTPM2_SPDM_ValidateRspSz(void) +{ + word32 ioBufSz = MAX_RESPONSE_SIZE; + word32 rxSz = 256; + + AssertIntEQ(wolfTPM2_SPDM_ValidateRspSz(rxSz, rxSz, ioBufSz), 0); + AssertIntEQ(wolfTPM2_SPDM_ValidateRspSz(rxSz + 1, rxSz, ioBufSz), -1); + AssertIntEQ(wolfTPM2_SPDM_ValidateRspSz(ioBufSz + 1, ioBufSz, ioBufSz), -1); + + printf("Test TPM2: %-40s Passed\n", "SPDM ValidateRspSz:"); +} +#endif /* WOLFTPM_SPDM */ + /* Pin the TIS response-size bounds so a mutation dropping the buffer-bound or * the MAX_RESPONSE_SIZE term is caught. */ static void test_TPM2_TIS_ValidateRspSz(void) @@ -5454,6 +5471,7 @@ int unit_tests(int argc, char *argv[]) test_wolfTPM2_Cleanup(); test_wolfTPM2_thread_local_storage(); #ifdef WOLFTPM_SPDM + test_wolfTPM2_SPDM_ValidateRspSz(); test_wolfTPM2_SPDM_Functions(); #endif #endif /* !WOLFTPM2_NO_WRAPPER */ diff --git a/wolftpm/tpm2_spdm.h b/wolftpm/tpm2_spdm.h index 5dfdde87..31c7fcab 100644 --- a/wolftpm/tpm2_spdm.h +++ b/wolftpm/tpm2_spdm.h @@ -181,6 +181,10 @@ WOLFTPM_API int wolfTPM2_SPDM_SetTisIO( WOLFTPM2_SPDM_CTX* ctx ); +/* Validate a framed response size against caller and I/O buffer sizes */ +WOLFTPM_API int wolfTPM2_SPDM_ValidateRspSz(word32 rspSz, word32 rxSz, + word32 ioBufSz); + #ifdef __cplusplus } /* extern "C" */ #endif From a9ea3dce7f0ffb4aa48e50bd9e4b6bb7a6a63075 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 12:03:50 -0700 Subject: [PATCH 16/22] F-6322 - Correct ML-DSA sign sequence comments to streamable --- examples/pqc/mldsa_sign.c | 13 ++++++------- examples/pqc/mldsa_verify_neg.c | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/examples/pqc/mldsa_sign.c b/examples/pqc/mldsa_sign.c index 5423402b..43967d08 100644 --- a/examples/pqc/mldsa_sign.c +++ b/examples/pqc/mldsa_sign.c @@ -24,10 +24,10 @@ * (SignSequenceComplete), Sec.17.6 (VerifySequenceStart), Sec.20.3 * (VerifySequenceComplete). * - * Pure ML-DSA is one-shot on the sign path: SequenceUpdate is rejected - * with TPM_RC_ONE_SHOT_SIGNATURE, the full message must arrive via the - * SignSequenceComplete buffer. Verify sequences do accept Update per - * Sec.20.3 and this example uses that path to exercise both idioms. */ + * Pure ML-DSA is streamable on both sign and verify: SequenceUpdate is + * accepted (only multi-pass schemes such as EdDSA are one-shot). This + * example passes the whole message via SignSequenceComplete on sign and + * streams it through SequenceUpdate on verify to exercise both idioms. */ #ifdef HAVE_CONFIG_H #include @@ -144,9 +144,8 @@ static int mldsa_sign_run(int argc, char *argv[]) (unsigned)mldsaKey.handle.hndl, (unsigned)mldsaKey.pub.publicArea.unique.mldsa.size); - /* Sign: Pure ML-DSA is one-shot per Sec.17.5. Message goes via - * SignSequenceComplete's buffer parameter, not via SequenceUpdate - * (which returns TPM_RC_ONE_SHOT_SIGNATURE for Pure MLDSA keys). */ + /* Sign: pass the whole message via SignSequenceComplete's buffer. + * Pure ML-DSA also accepts SequenceUpdate since it is streamable. */ rc = wolfTPM2_SignSequenceStart(&dev, &mldsaKey, NULL, 0, &seqHandle); if (rc != TPM_RC_SUCCESS) { printf("SignSequenceStart failed 0x%x: %s\n", diff --git a/examples/pqc/mldsa_verify_neg.c b/examples/pqc/mldsa_verify_neg.c index a9797c54..aecc2079 100644 --- a/examples/pqc/mldsa_verify_neg.c +++ b/examples/pqc/mldsa_verify_neg.c @@ -163,7 +163,7 @@ static int mldsa_verify_neg_run(int argc, char *argv[]) goto exit; } - /* Sign the message (Pure ML-DSA is one-shot via Complete's buffer). */ + /* Sign the message via Complete's buffer (Pure ML-DSA is streamable). */ rc = wolfTPM2_SignSequenceStart(&dev, &mldsaKey, NULL, 0, &seqHandle); if (rc == TPM_RC_SUCCESS) { rc = wolfTPM2_SignSequenceComplete(&dev, seqHandle, &mldsaKey, From b5be21d2750730d71a5ed3ce6cf416576a6aab9b Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 12:04:42 -0700 Subject: [PATCH 17/22] F-6314 - Add maximum-size TPM2B sensitive roundtrip test --- tests/unit_tests.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/unit_tests.c b/tests/unit_tests.c index ae860df5..a8c96faa 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -3228,6 +3228,42 @@ static void test_TPM2_AppendSensitive_Clamp(void) printf("Test TPM2: %-40s Passed\n", "AppendSensitive clamp:"); } +/* Roundtrip a maximum-size inner payload (size == buffer capacity) so the + * parse-side ParseU16Buf clamp branch is exercised with valid data. */ +static void test_TPM2_Sensitive_MaxRoundtrip(void) +{ + TPM2_Packet packet; + byte buf[1024]; + TPM2B_SENSITIVE sensIn, sensOut; + word16 cap, i; + + XMEMSET(&sensIn, 0, sizeof(sensIn)); + sensIn.sensitiveArea.sensitiveType = TPM_ALG_RSA; + cap = (word16)sizeof(sensIn.sensitiveArea.sensitive.rsa.buffer); + sensIn.sensitiveArea.sensitive.rsa.size = cap; + for (i = 0; i < cap; i++) { + sensIn.sensitiveArea.sensitive.rsa.buffer[i] = (byte)(i & 0xFF); + } + + XMEMSET(buf, 0, sizeof(buf)); + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = buf; + packet.size = sizeof(buf); + + TPM2_Packet_AppendSensitive(&packet, &sensIn); + + packet.pos = 0; + XMEMSET(&sensOut, 0, sizeof(sensOut)); + TPM2_Packet_ParseSensitive(&packet, &sensOut); + + AssertIntEQ(sensOut.sensitiveArea.sensitiveType, TPM_ALG_RSA); + AssertIntEQ(sensOut.sensitiveArea.sensitive.rsa.size, cap); + AssertIntEQ(XMEMCMP(sensOut.sensitiveArea.sensitive.rsa.buffer, + sensIn.sensitiveArea.sensitive.rsa.buffer, cap), 0); + + printf("Test TPM2: %-40s Passed\n", "Sensitive max roundtrip:"); +} + static void test_KeySealTemplate(void) { int rc; @@ -5418,6 +5454,7 @@ int unit_tests(int argc, char *argv[]) test_TPM2_TIS_ValidateRspSz(); test_TPM2_ParsePublic_EmptyClears(); test_TPM2_AppendSensitive_Clamp(); + test_TPM2_Sensitive_MaxRoundtrip(); test_KeySealTemplate(); test_SealAndKeyedHash_Boundaries(); test_GetAlgId(); From 8c1f24616fde9ee255c196ab3456c7649245cbd9 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 12:06:44 -0700 Subject: [PATCH 18/22] F-6311 - Prefer tpmrm0 with tpm0 fallback on Linux default --- src/tpm2_linux.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/tpm2_linux.c b/src/tpm2_linux.c index 10054607..5c86e65a 100644 --- a/src/tpm2_linux.c +++ b/src/tpm2_linux.c @@ -92,15 +92,19 @@ int TPM2_LINUX_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet) #include /* TPM Device Path Configuration: - * - /dev/tpm0: TPM raw device (default) - * - /dev/tpmrm0: TPM resource manager (requires kernel 5.12+) - * Enabled with WOLFTPM_USE_TPMRM + * - /dev/tpmrm0: TPM resource manager (requires kernel 5.12+), preferred as it + * isolates and flushes per-connection transient objects/sessions + * - /dev/tpm0: TPM raw device, no in-kernel resource management + * The default prefers the resource manager and falls back to the raw device + * when it is unavailable. Define WOLFTPM_USE_TPMRM for resource manager only, + * or set TPM2_LINUX_DEV to pin a specific device. */ #ifndef TPM2_LINUX_DEV #ifdef WOLFTPM_USE_TPMRM #define TPM2_LINUX_DEV "/dev/tpmrm0" #else - #define TPM2_LINUX_DEV "/dev/tpm0" + #define TPM2_LINUX_DEV "/dev/tpmrm0" + #define TPM2_LINUX_DEV_FALLBACK "/dev/tpm0" #endif #endif @@ -121,14 +125,23 @@ int TPM2_LINUX_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet) int rc_poll, nfds = 1; /* Polling single TPM dev file */ struct pollfd fds; int rspSz = 0; + const char* devName = TPM2_LINUX_DEV; #ifdef WOLFTPM_DEBUG_VERBOSE printf("Command size: %d\n", packet->pos); TPM2_PrintBin(packet->buf, packet->pos); #endif - if (ctx->fd < 0) + if (ctx->fd < 0) { ctx->fd = open(TPM2_LINUX_DEV, O_RDWR | O_NONBLOCK); +#ifdef TPM2_LINUX_DEV_FALLBACK + if (ctx->fd < 0) { + ctx->fd = open(TPM2_LINUX_DEV_FALLBACK, O_RDWR | O_NONBLOCK); + if (ctx->fd >= 0) + devName = TPM2_LINUX_DEV_FALLBACK; + } +#endif + } if (ctx->fd >= 0) { /* Send the TPM command */ if (write(ctx->fd, packet->buf, packet->pos) == packet->pos) { @@ -147,11 +160,11 @@ int TPM2_LINUX_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet) else { #ifdef DEBUG_WOLFTPM if (ret == 0) { - printf("Received EOF from %s\n", TPM2_LINUX_DEV); + printf("Received EOF from %s\n", devName); } else { printf("Failed to read from %s (ret %zd):" - " errno %d = %s\n", TPM2_LINUX_DEV, ret, + " errno %d = %s\n", devName, ret, errno, strerror(errno)); } #endif @@ -161,7 +174,7 @@ int TPM2_LINUX_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet) else { #ifdef DEBUG_WOLFTPM printf("Failed poll on %s: errno %d = %s\n", - TPM2_LINUX_DEV, errno, strerror(errno)); + devName, errno, strerror(errno)); #endif rc = TPM_RC_FAILURE; } @@ -169,19 +182,19 @@ int TPM2_LINUX_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet) else { #ifdef DEBUG_WOLFTPM printf("Failed write to %s: errno %d = %s\n", - TPM2_LINUX_DEV, errno, strerror(errno)); + devName, errno, strerror(errno)); #endif rc = TPM_RC_FAILURE; } } else if (ctx->fd == -1 && errno == EACCES) { printf("Permission denied on %s\n" - "Use sudo or add tss group to user.\n", TPM2_LINUX_DEV); + "Use sudo or add tss group to user.\n", devName); } else { #ifdef DEBUG_WOLFTPM printf("Failed to open %s: errno %d = %s\n", - TPM2_LINUX_DEV, errno, strerror(errno)); + devName, errno, strerror(errno)); #endif } From f9ebeb38ed7750fbe9897e747f9767030ced5913 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 12:37:30 -0700 Subject: [PATCH 19/22] F-6315 - Preserve SwTpmReceive error before response-size validation --- src/tpm2_swtpm.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/tpm2_swtpm.c b/src/tpm2_swtpm.c index b37d9c84..723005ee 100644 --- a/src/tpm2_swtpm.c +++ b/src/tpm2_swtpm.c @@ -555,12 +555,14 @@ int TPM2_SWTPM_SendCommand(TPM2_CTX* ctx, TPM2_Packet* packet) if (rc == TPM_RC_SUCCESS) { rc = SwTpmReceive(ctx, &tss_word, sizeof(uint32_t)); rspSz = TPM2_Packet_SwapU32(tss_word); - rc = TPM2_SwtpmValidateRspSz(packet->size, rspSz); - if (rc != TPM_RC_SUCCESS) { - #ifdef WOLFTPM_DEBUG_VERBOSE - printf("Response size(%u) out of range (header=%d, buf=%d)\n", - rspSz, TPM2_HEADER_SIZE, packet->size); - #endif + if (rc == TPM_RC_SUCCESS) { + rc = TPM2_SwtpmValidateRspSz(packet->size, rspSz); + if (rc != TPM_RC_SUCCESS) { + #ifdef WOLFTPM_DEBUG_VERBOSE + printf("Response size(%u) out of range (header=%d, buf=%d)\n", + rspSz, TPM2_HEADER_SIZE, packet->size); + #endif + } } } From 9e7769a08a6508f90657685675f67e4894c6ed68 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 12:37:54 -0700 Subject: [PATCH 20/22] F-6318 - Return BUFFER_E on short private read in readKeyBlob --- examples/tpm_test_keys.c | 4 ++-- tests/unit_tests.c | 29 ++++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/examples/tpm_test_keys.c b/examples/tpm_test_keys.c index 43142b71..171252d8 100644 --- a/examples/tpm_test_keys.c +++ b/examples/tpm_test_keys.c @@ -255,7 +255,7 @@ int readKeyBlob(const char* filename, WOLFTPM2_KEYBLOB* key) if (bytes_read != sizeof(key->priv.size)) { printf("Read %zu, expected private size marker of %zu bytes\n", bytes_read, sizeof(key->priv.size)); - goto exit; + rc = BUFFER_E; goto exit; } if (key->priv.size > sizeof(key->priv.buffer)) { printf("Private key size is too large\n"); @@ -265,7 +265,7 @@ int readKeyBlob(const char* filename, WOLFTPM2_KEYBLOB* key) if (bytes_read != key->priv.size) { printf("Read %zu, expected private blob %zu bytes\n", bytes_read, (size_t)key->priv.size); - goto exit; + rc = BUFFER_E; goto exit; } rc = 0; /* success */ } diff --git a/tests/unit_tests.c b/tests/unit_tests.c index a8c96faa..6568ebab 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -3203,7 +3203,8 @@ static void test_TPM2_ParsePublic_EmptyClears(void) } /* An oversized inner size on a classic arm must be clamped to the arm buffer - * size, matching the PQC arms, so AppendBytes does not over-read the source. */ + * size, matching the PQC arms, so AppendBytes does not over-read the source. + * Each arm has its own buffer member so all four are exercised. */ static void test_TPM2_AppendSensitive_Clamp(void) { TPM2_Packet packet; @@ -3991,6 +3992,7 @@ static void test_readKeyBlob_PrivOverflow(void) word32 i; size_t privTailSz, remaining, chunk; UINT16 privSizeMarker; + UINT16 bigMarker; XFILE fp; byte pubAreaBuffer[sizeof(TPM2B_PUBLIC)]; byte filler[64]; @@ -4046,6 +4048,31 @@ static void test_readKeyBlob_PrivOverflow(void) remove(filename); + /* Rejection branch: priv.size marker larger than the destination buffer + * must be refused with BUFFER_E before any bytes are read. */ + bigMarker = (UINT16)(sizeof(guarded.key.priv.buffer) + 1); + fp = XFOPEN(filename, "wb"); + AssertNotNull(fp); + XFWRITE(&tmpl.pub.size, 1, sizeof(tmpl.pub.size), fp); + XFWRITE(pubAreaBuffer, 1, sizeof(UINT16) + tmpl.pub.size, fp); + XFWRITE(&bigMarker, 1, sizeof(bigMarker), fp); + XFCLOSE(fp); + XMEMSET(&guarded, 0, sizeof(guarded)); + rc = readKeyBlob(filename, &guarded.key); + AssertIntEQ(rc, BUFFER_E); + remove(filename); + + /* Rejection branch: pub.size marker larger than the public area buffer. */ + bigMarker = (UINT16)sizeof(pubAreaBuffer); + fp = XFOPEN(filename, "wb"); + AssertNotNull(fp); + XFWRITE(&bigMarker, 1, sizeof(bigMarker), fp); + XFCLOSE(fp); + XMEMSET(&guarded, 0, sizeof(guarded)); + rc = readKeyBlob(filename, &guarded.key); + AssertIntEQ(rc, BUFFER_E); + remove(filename); + printf("Test TPM Wrapper: %-40s Passed\n", "readKeyBlob priv overflow:"); } #endif From 68f40848300dfe751a8cba0c7d58e7984c91e9f4 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 12:38:24 -0700 Subject: [PATCH 21/22] F-6312 - Cover ECC, KEYEDHASH, SYMCIPHER arms in AppendSensitive clamp test --- tests/unit_tests.c | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 6568ebab..80361234 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -3210,21 +3210,48 @@ static void test_TPM2_AppendSensitive_Clamp(void) TPM2_Packet packet; byte buf[1024]; TPM2B_SENSITIVE sens; - word16 bufCap; + word16 rsaCap, eccCap, bitsCap, symCap; + + rsaCap = (word16)sizeof(sens.sensitiveArea.sensitive.rsa.buffer); + eccCap = (word16)sizeof(sens.sensitiveArea.sensitive.ecc.buffer); + bitsCap = (word16)sizeof(sens.sensitiveArea.sensitive.bits.buffer); + symCap = (word16)sizeof(sens.sensitiveArea.sensitive.sym.buffer); XMEMSET(&sens, 0, sizeof(sens)); sens.sensitiveArea.sensitiveType = TPM_ALG_RSA; - bufCap = (word16)sizeof(sens.sensitiveArea.sensitive.rsa.buffer); - sens.sensitiveArea.sensitive.rsa.size = bufCap + 100; + sens.sensitiveArea.sensitive.rsa.size = rsaCap + 100; + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = buf; + packet.size = sizeof(buf); + TPM2_Packet_AppendSensitive(&packet, &sens); + AssertIntEQ(sens.sensitiveArea.sensitive.rsa.size, rsaCap); - XMEMSET(buf, 0, sizeof(buf)); + XMEMSET(&sens, 0, sizeof(sens)); + sens.sensitiveArea.sensitiveType = TPM_ALG_ECC; + sens.sensitiveArea.sensitive.ecc.size = eccCap + 100; XMEMSET(&packet, 0, sizeof(packet)); packet.buf = buf; packet.size = sizeof(buf); + TPM2_Packet_AppendSensitive(&packet, &sens); + AssertIntEQ(sens.sensitiveArea.sensitive.ecc.size, eccCap); + XMEMSET(&sens, 0, sizeof(sens)); + sens.sensitiveArea.sensitiveType = TPM_ALG_KEYEDHASH; + sens.sensitiveArea.sensitive.bits.size = bitsCap + 100; + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = buf; + packet.size = sizeof(buf); TPM2_Packet_AppendSensitive(&packet, &sens); + AssertIntEQ(sens.sensitiveArea.sensitive.bits.size, bitsCap); - AssertIntEQ(sens.sensitiveArea.sensitive.rsa.size, bufCap); + XMEMSET(&sens, 0, sizeof(sens)); + sens.sensitiveArea.sensitiveType = TPM_ALG_SYMCIPHER; + sens.sensitiveArea.sensitive.sym.size = symCap + 100; + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = buf; + packet.size = sizeof(buf); + TPM2_Packet_AppendSensitive(&packet, &sens); + AssertIntEQ(sens.sensitiveArea.sensitive.sym.size, symCap); printf("Test TPM2: %-40s Passed\n", "AppendSensitive clamp:"); } From ac87e6bb7ab8d42cccef4908d70c756878a0bc87 Mon Sep 17 00:00:00 2001 From: aidan garske Date: Mon, 22 Jun 2026 13:03:13 -0700 Subject: [PATCH 22/22] F-6318 - Create readKeyBlob test files with owner-only permissions --- tests/unit_tests.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 80361234..f40bfea1 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -39,6 +39,9 @@ #include #include +#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) +#include +#endif /* Test Fail Helpers */ #ifndef NO_ABORT @@ -4009,6 +4012,21 @@ static void test_TPM2_SwtpmValidateRspSz(void) #if !defined(NO_FILESYSTEM) && !defined(NO_WRITE_TEMP_FILES) && \ !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) +/* Create a temp file for writing with owner-only permissions, so the test + * does not leave a world-writable file as plain fopen("wb") would. */ +static XFILE openTestFileWrite(const char* fn) +{ +#if defined(__linux__) || defined(__APPLE__) || defined(__unix__) + int fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (fd < 0) { + return XBADFILE; + } + return fdopen(fd, "wb"); +#else + return XFOPEN(fn, "wb"); +#endif +} + /* Craft a key-blob file with a small valid public area and an oversized * private tail, then load it with readKeyBlob(). A canary placed directly * after the keyblob catches any write past it. No TPM is required. */ @@ -4049,7 +4067,7 @@ static void test_readKeyBlob_PrivOverflow(void) privTailSz = sizeof(guarded.key.priv) + sizeof(filler); privSizeMarker = 32; - fp = XFOPEN(filename, "wb"); + fp = openTestFileWrite(filename); AssertNotNull(fp); XFWRITE(&tmpl.pub.size, 1, sizeof(tmpl.pub.size), fp); XFWRITE(pubAreaBuffer, 1, sizeof(UINT16) + tmpl.pub.size, fp); @@ -4078,7 +4096,7 @@ static void test_readKeyBlob_PrivOverflow(void) /* Rejection branch: priv.size marker larger than the destination buffer * must be refused with BUFFER_E before any bytes are read. */ bigMarker = (UINT16)(sizeof(guarded.key.priv.buffer) + 1); - fp = XFOPEN(filename, "wb"); + fp = openTestFileWrite(filename); AssertNotNull(fp); XFWRITE(&tmpl.pub.size, 1, sizeof(tmpl.pub.size), fp); XFWRITE(pubAreaBuffer, 1, sizeof(UINT16) + tmpl.pub.size, fp); @@ -4091,7 +4109,7 @@ static void test_readKeyBlob_PrivOverflow(void) /* Rejection branch: pub.size marker larger than the public area buffer. */ bigMarker = (UINT16)sizeof(pubAreaBuffer); - fp = XFOPEN(filename, "wb"); + fp = openTestFileWrite(filename); AssertNotNull(fp); XFWRITE(&bigMarker, 1, sizeof(bigMarker), fp); XFCLOSE(fp);