From bf3a39fb4a00874cb79a719b4896e0892c77ddf3 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 13:44:06 -0700 Subject: [PATCH 01/35] F-3001 - https://fenrir.wolfssl.com/finding/3001 - Reject NULL IV for non-ECB modes and oversized IV in wolfTPM2_EncryptDecryptBlock --- examples/bench/bench.c | 6 ++-- examples/wrap/wrap_test.c | 6 ++-- src/tpm2_wrap.c | 13 ++++--- tests/unit_tests.c | 73 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+), 8 deletions(-) diff --git a/examples/bench/bench.c b/examples/bench/bench.c index 038aa479..e85f9cbc 100644 --- a/examples/bench/bench.c +++ b/examples/bench/bench.c @@ -160,6 +160,7 @@ static int bench_sym_aes(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* storageKey, double start; TPMT_PUBLIC publicTemplate; WOLFTPM2_KEY aesKey; + byte iv[MAX_AES_BLOCK_SIZE_BYTES]; XMEMSET(&aesKey, 0, sizeof(aesKey)); rc = wolfTPM2_GetKeyTemplate_Symmetric(&publicTemplate, keyBits, algo, @@ -175,8 +176,9 @@ static int bench_sym_aes(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* storageKey, bench_stats_start(&count, &start); do { - rc = wolfTPM2_EncryptDecrypt(dev, &aesKey, in, out, inOutSz, NULL, 0, - isDecrypt); + XMEMSET(iv, 0, sizeof(iv)); + rc = wolfTPM2_EncryptDecrypt(dev, &aesKey, in, out, inOutSz, iv, + sizeof(iv), isDecrypt); if (WOLFTPM_IS_COMMAND_UNAVAILABLE(rc)) { printf("Encrypt/Decrypt unavailable\n"); break; diff --git a/examples/wrap/wrap_test.c b/examples/wrap/wrap_test.c index fd95be54..384c9bae 100644 --- a/examples/wrap/wrap_test.c +++ b/examples/wrap/wrap_test.c @@ -960,14 +960,16 @@ int TPM2_Wrapper_TestArgs(void* userCtx, int argc, char *argv[]) XMEMSET(cipher.buffer, 0, sizeof(cipher.buffer)); cipher.size = message.size; + XMEMSET(aesIv, 0, sizeof(aesIv)); rc = wolfTPM2_EncryptDecrypt(&dev, &aesKey, message.buffer, cipher.buffer, - message.size, NULL, 0, WOLFTPM2_ENCRYPT); + message.size, aesIv, (word32)sizeof(aesIv), WOLFTPM2_ENCRYPT); if (rc != 0 && !WOLFTPM_IS_COMMAND_UNAVAILABLE(rc)) goto exit; XMEMSET(plain.buffer, 0, sizeof(plain.buffer)); plain.size = message.size; + XMEMSET(aesIv, 0, sizeof(aesIv)); rc = wolfTPM2_EncryptDecrypt(&dev, &aesKey, cipher.buffer, plain.buffer, - cipher.size, NULL, 0, WOLFTPM2_DECRYPT); + cipher.size, aesIv, (word32)sizeof(aesIv), WOLFTPM2_DECRYPT); wolfTPM2_UnloadHandle(&dev, &aesKey.handle); diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index cbbd53f2..a8b74d0b 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -6635,18 +6635,23 @@ int wolfTPM2_EncryptDecryptBlock(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, XMEMSET(&encDecIn, 0, sizeof(encDecIn)); encDecIn.keyHandle = key->handle.hndl; + /* use symmetric algorithm from key */ + encDecIn.mode = key->pub.publicArea.parameters.symDetail.sym.mode.aes; if (iv == NULL || ivSz == 0) { + /* Modes other than ECB and NULL require an explicit IV */ + if (encDecIn.mode != TPM_ALG_ECB && encDecIn.mode != TPM_ALG_NULL) { + return BAD_FUNC_ARG; + } encDecIn.ivIn.size = MAX_AES_BLOCK_SIZE_BYTES; /* zeros */ } else { + if (ivSz > sizeof(encDecIn.ivIn.buffer)) { + return BUFFER_E; + } encDecIn.ivIn.size = ivSz; - if (encDecIn.ivIn.size > sizeof(encDecIn.ivIn.buffer)) - encDecIn.ivIn.size = sizeof(encDecIn.ivIn.buffer); /* truncate */ XMEMCPY(encDecIn.ivIn.buffer, iv, encDecIn.ivIn.size); } encDecIn.decrypt = isDecrypt; - /* use symmetric algorithm from key */ - encDecIn.mode = key->pub.publicArea.parameters.symDetail.sym.mode.aes; encDecIn.inData.size = inOutSz; XMEMCPY(encDecIn.inData.buffer, in, inOutSz); diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 380f47a9..04631051 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -2626,6 +2626,78 @@ static void test_wolfTPM2_LoadPrivateKey_NullParent(void) printf("Test TPM Wrapper:\tLoadPrivateKey NullParent:\tPassed\n"); } +static void test_wolfTPM2_EncryptDecryptBlock(void) +{ + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_KEY key; + byte in[MAX_AES_BLOCK_SIZE_BYTES]; + byte out[MAX_AES_BLOCK_SIZE_BYTES]; + byte iv[MAX_AES_BLOCK_SIZE_BYTES]; + byte bigIv[MAX_SYM_BLOCK_SIZE + 1]; + + XMEMSET(in, 0, sizeof(in)); + XMEMSET(out, 0, sizeof(out)); + XMEMSET(iv, 0, sizeof(iv)); + XMEMSET(bigIv, 0, sizeof(bigIv)); + + rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL); + AssertIntEQ(rc, 0); + + XMEMSET(&key, 0, sizeof(key)); + + /* CBC mode: NULL IV should return BAD_FUNC_ARG */ + key.pub.publicArea.parameters.symDetail.sym.mode.aes = TPM_ALG_CBC; + rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out, + sizeof(in), NULL, 0, WOLFTPM2_ENCRYPT); + AssertIntEQ(rc, BAD_FUNC_ARG); + + /* CBC mode: ivSz == 0 with non-NULL iv should return BAD_FUNC_ARG */ + rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out, + sizeof(in), iv, 0, WOLFTPM2_ENCRYPT); + AssertIntEQ(rc, BAD_FUNC_ARG); + + /* CFB mode: NULL IV should return BAD_FUNC_ARG */ + key.pub.publicArea.parameters.symDetail.sym.mode.aes = TPM_ALG_CFB; + rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out, + sizeof(in), NULL, 0, WOLFTPM2_ENCRYPT); + AssertIntEQ(rc, BAD_FUNC_ARG); + + /* CTR mode: NULL IV should return BAD_FUNC_ARG */ + key.pub.publicArea.parameters.symDetail.sym.mode.aes = TPM_ALG_CTR; + rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out, + sizeof(in), NULL, 0, WOLFTPM2_ENCRYPT); + AssertIntEQ(rc, BAD_FUNC_ARG); + + /* OFB mode: NULL IV should return BAD_FUNC_ARG */ + key.pub.publicArea.parameters.symDetail.sym.mode.aes = TPM_ALG_OFB; + rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out, + sizeof(in), NULL, 0, WOLFTPM2_ENCRYPT); + AssertIntEQ(rc, BAD_FUNC_ARG); + + /* Oversized IV should return BUFFER_E */ + key.pub.publicArea.parameters.symDetail.sym.mode.aes = TPM_ALG_CBC; + rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out, + sizeof(in), bigIv, sizeof(bigIv), WOLFTPM2_ENCRYPT); + AssertIntEQ(rc, BUFFER_E); + + /* ECB mode: NULL IV should NOT return BAD_FUNC_ARG */ + key.pub.publicArea.parameters.symDetail.sym.mode.aes = TPM_ALG_ECB; + rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out, + sizeof(in), NULL, 0, WOLFTPM2_ENCRYPT); + AssertIntNE(rc, BAD_FUNC_ARG); + + /* NULL mode: NULL IV should NOT return BAD_FUNC_ARG */ + key.pub.publicArea.parameters.symDetail.sym.mode.aes = TPM_ALG_NULL; + rc = wolfTPM2_EncryptDecryptBlock(&dev, &key, in, out, + sizeof(in), NULL, 0, WOLFTPM2_ENCRYPT); + AssertIntNE(rc, BAD_FUNC_ARG); + + wolfTPM2_Cleanup(&dev); + + printf("Test TPM Wrapper:\tEncryptDecryptBlock IV validate:\tPassed\n"); +} + #endif /* !WOLFTPM2_NO_WRAPPER */ #ifndef NO_MAIN_DRIVER @@ -2688,6 +2760,7 @@ int unit_tests(int argc, char *argv[]) test_wolfTPM2_DecodeDer_DefaultAttribs(); #endif test_wolfTPM2_LoadPrivateKey_NullParent(); + test_wolfTPM2_EncryptDecryptBlock(); test_wolfTPM2_KeyBlob(TPM_ALG_RSA); test_wolfTPM2_KeyBlob(TPM_ALG_ECC); #if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) && \ From 47aca58edf7585a626f0a233815b2a1a83d11530 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 13:57:29 -0700 Subject: [PATCH 02/35] F-2950 - https://fenrir.wolfssl.com/finding/2950 - Save and restore session[0] in SpdmEnable, SpdmDisable, and SpdmNationsIdentityKeySet --- src/tpm2_wrap.c | 57 ++++++++++++++++++++++++++++++++-------------- tests/unit_tests.c | 43 ++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 17 deletions(-) diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index a8b74d0b..43dbd469 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -1147,25 +1147,39 @@ int wolfTPM2_SpdmSetNuvotonMode(WOLFTPM2_DEV* dev) int wolfTPM2_SpdmEnable(WOLFTPM2_DEV* dev) { int rc; + TPM2_AUTH_SESSION saveSess; if (dev == NULL || dev->spdmCtx == NULL) { return BAD_FUNC_ARG; } - /* NTC2_PreConfig requires platform auth (empty password) */ + /* Save session[0] — NTC2_PreConfig requires platform auth which + * overwrites any existing HMAC/parameter-encryption session. */ + XMEMCPY(&saveSess, &dev->session[0], sizeof(saveSess)); rc = wolfTPM2_SetAuthPassword(dev, 0, NULL); - if (rc != 0) return rc; - return wolfTPM2_SPDM_Enable(dev->spdmCtx); + if (rc == 0) { + rc = wolfTPM2_SPDM_Enable(dev->spdmCtx); + } + /* Restore previous session[0] state */ + XMEMCPY(&dev->session[0], &saveSess, sizeof(dev->session[0])); + return rc; } int wolfTPM2_SpdmDisable(WOLFTPM2_DEV* dev) { int rc; + TPM2_AUTH_SESSION saveSess; if (dev == NULL || dev->spdmCtx == NULL) { return BAD_FUNC_ARG; } - /* NTC2_PreConfig requires platform auth (empty password) */ + /* Save session[0] — NTC2_PreConfig requires platform auth which + * overwrites any existing HMAC/parameter-encryption session. */ + XMEMCPY(&saveSess, &dev->session[0], sizeof(saveSess)); rc = wolfTPM2_SetAuthPassword(dev, 0, NULL); - if (rc != 0) return rc; - return wolfTPM2_SPDM_Disable(dev->spdmCtx); + if (rc == 0) { + rc = wolfTPM2_SPDM_Disable(dev->spdmCtx); + } + /* Restore previous session[0] state */ + XMEMCPY(&dev->session[0], &saveSess, sizeof(dev->session[0])); + return rc; } int wolfTPM2_SpdmGetStatus(WOLFTPM2_DEV* dev, WOLFSPDM_NUVOTON_STATUS* status) @@ -1540,6 +1554,7 @@ int wolfTPM2_SpdmNationsIdentityKeySet(WOLFTPM2_DEV* dev, int set) { int rc; Nations_IdentityKeySet_In in; + TPM2_AUTH_SESSION saveSess; if (dev == NULL) { return BAD_FUNC_ARG; @@ -1548,24 +1563,32 @@ int wolfTPM2_SpdmNationsIdentityKeySet(WOLFTPM2_DEV* dev, int set) /* Mutual exclusion: TPM enforces this by returning TPM_RC_VALUE if PSK * is provisioned. The error message helps users understand the failure. */ + /* Save session[0] — platform auth setup overwrites any existing + * HMAC/parameter-encryption session. */ + XMEMCPY(&saveSess, &dev->session[0], sizeof(saveSess)); + /* Set platform auth (empty password) with no continueSession. * NS350 requires sessionAttributes=0x00, not 0x01 (continueSession). */ rc = wolfTPM2_SetAuthPassword(dev, 0, NULL); - if (rc != 0) return rc; - dev->session[0].sessionAttributes = 0; + if (rc == 0) { + dev->session[0].sessionAttributes = 0; - XMEMSET(&in, 0, sizeof(in)); - in.authHandle = TPM_RH_PLATFORM; - in.configuration = set ? 1 : 0; + XMEMSET(&in, 0, sizeof(in)); + in.authHandle = TPM_RH_PLATFORM; + in.configuration = set ? 1 : 0; - rc = TPM2_Nations_IdentityKeySet(&in); - if (rc != TPM_RC_SUCCESS) { - #ifdef DEBUG_WOLFTPM - printf("Nations IdentityKeySet(%d) failed: 0x%x: %s\n", - set, rc, TPM2_GetRCString(rc)); - #endif + rc = TPM2_Nations_IdentityKeySet(&in); + if (rc != TPM_RC_SUCCESS) { + #ifdef DEBUG_WOLFTPM + printf("Nations IdentityKeySet(%d) failed: 0x%x: %s\n", + set, rc, TPM2_GetRCString(rc)); + #endif + } } + /* Restore previous session[0] state */ + XMEMCPY(&dev->session[0], &saveSess, sizeof(dev->session[0])); + return rc; } diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 04631051..a68daa88 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -2395,6 +2395,33 @@ static void test_wolfTPM2_SPDM_Functions(void) AssertIntEQ(rc, BAD_FUNC_ARG); rc = wolfTPM2_SpdmSetOnlyMode(NULL, 0); AssertIntEQ(rc, BAD_FUNC_ARG); + + /* Test 3b: SpdmEnable/Disable must preserve session[0] */ + rc = wolfTPM2_SpdmInit(&dev); + if (rc == 0) { + TPM2_AUTH_SESSION origSess; + /* Set up a distinguishable session[0] state */ + dev.session[0].sessionHandle = HMAC_SESSION_FIRST; + dev.session[0].sessionAttributes = 0x27; + dev.session[0].auth.size = 4; + XMEMCPY(dev.session[0].auth.buffer, "\x01\x02\x03\x04", 4); + XMEMCPY(&origSess, &dev.session[0], sizeof(origSess)); + + /* SpdmEnable may fail (no Nuvoton HW) but must restore session[0] */ + (void)wolfTPM2_SpdmEnable(&dev); + AssertIntEQ(dev.session[0].sessionHandle, origSess.sessionHandle); + AssertIntEQ(dev.session[0].sessionAttributes, origSess.sessionAttributes); + AssertIntEQ(dev.session[0].auth.size, origSess.auth.size); + + /* Restore and test SpdmDisable */ + XMEMCPY(&dev.session[0], &origSess, sizeof(origSess)); + (void)wolfTPM2_SpdmDisable(&dev); + AssertIntEQ(dev.session[0].sessionHandle, origSess.sessionHandle); + AssertIntEQ(dev.session[0].sessionAttributes, origSess.sessionAttributes); + AssertIntEQ(dev.session[0].auth.size, origSess.auth.size); + + wolfTPM2_SpdmCleanup(&dev); + } #endif /* WOLFSPDM_NUVOTON */ #ifdef WOLFSPDM_NATIONS @@ -2417,6 +2444,22 @@ static void test_wolfTPM2_SPDM_Functions(void) AssertIntEQ(rc, BAD_FUNC_ARG); rc = wolfTPM2_SpdmNationsPskClear(NULL, NULL, 0); AssertIntEQ(rc, BAD_FUNC_ARG); + + /* Test 4b: SpdmNationsIdentityKeySet must preserve session[0] */ + { + TPM2_AUTH_SESSION origSess; + dev.session[0].sessionHandle = HMAC_SESSION_FIRST; + dev.session[0].sessionAttributes = 0x27; + dev.session[0].auth.size = 4; + XMEMCPY(dev.session[0].auth.buffer, "\x01\x02\x03\x04", 4); + XMEMCPY(&origSess, &dev.session[0], sizeof(origSess)); + + /* May fail (no Nations HW) but must restore session[0] */ + (void)wolfTPM2_SpdmNationsIdentityKeySet(&dev, 1); + AssertIntEQ(dev.session[0].sessionHandle, origSess.sessionHandle); + AssertIntEQ(dev.session[0].sessionAttributes, origSess.sessionAttributes); + AssertIntEQ(dev.session[0].auth.size, origSess.auth.size); + } #endif /* WOLFSPDM_NATIONS */ wolfTPM2_Cleanup(&dev); From b05c503d715ae08137d5f50a669c8797d5efa5d4 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 14:02:13 -0700 Subject: [PATCH 03/35] F-2967 - https://fenrir.wolfssl.com/finding/2967 - Clear ECC private key from stack on error paths in wolfTPM2_ImportEccPrivateKeySeed --- src/tpm2_wrap.c | 39 ++++++++++++++++++++++----------------- tests/unit_tests.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 17 deletions(-) diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 43dbd469..8faf7e8e 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -3611,25 +3611,30 @@ int wolfTPM2_ImportEccPrivateKeySeed(WOLFTPM2_DEV* dev, const WOLFTPM2_KEY* pare #ifdef DEBUG_WOLFTPM printf("Import ECC name alg size invalid! %d\n", digestSz); #endif - return BUFFER_E; + rc = BUFFER_E; } - if (seed != NULL) { - /* use custom seed */ - if (seedSz != digestSz) { - #ifdef DEBUG_WOLFTPM - printf("Import ECC seed size invalid! %d != %d\n", - seedSz, digestSz); - #endif - return BAD_FUNC_ARG; + + if (rc == 0) { + if (seed != NULL) { + /* use custom seed */ + if (seedSz != digestSz) { + #ifdef DEBUG_WOLFTPM + printf("Import ECC seed size invalid! %d != %d\n", + seedSz, digestSz); + #endif + rc = BAD_FUNC_ARG; + } + else { + sens.sensitiveArea.seedValue.size = seedSz; + XMEMCPY(sens.sensitiveArea.seedValue.buffer, seed, seedSz); + } + } + else { + /* assign random seed */ + sens.sensitiveArea.seedValue.size = digestSz; + rc = TPM2_GetNonceNoLock(sens.sensitiveArea.seedValue.buffer, + sens.sensitiveArea.seedValue.size); } - sens.sensitiveArea.seedValue.size = seedSz; - XMEMCPY(sens.sensitiveArea.seedValue.buffer, seed, seedSz); - } - else { - /* assign random seed */ - sens.sensitiveArea.seedValue.size = digestSz; - rc = TPM2_GetNonceNoLock(sens.sensitiveArea.seedValue.buffer, - sens.sensitiveArea.seedValue.size); } if (rc == 0) { diff --git a/tests/unit_tests.c b/tests/unit_tests.c index a68daa88..f22abaa7 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -2741,6 +2741,43 @@ static void test_wolfTPM2_EncryptDecryptBlock(void) printf("Test TPM Wrapper:\tEncryptDecryptBlock IV validate:\tPassed\n"); } +#ifdef HAVE_ECC +static void test_wolfTPM2_ImportEccPrivateKeySeed_ErrorPaths(void) +{ + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_KEY parentKey; + WOLFTPM2_KEYBLOB keyBlob; + byte eccPubX[32]; + byte eccPubY[32]; + byte eccPriv[32]; + /* Wrong-size seed to trigger seed size mismatch error path. + * WOLFTPM2_WRAP_DIGEST is SHA256 (digestSz=32), so seedSz=1 mismatches. */ + byte seed[1] = {0x42}; + TPMA_OBJECT attrs = (TPMA_OBJECT_sign | TPMA_OBJECT_userWithAuth | + TPMA_OBJECT_noDA); + + XMEMSET(eccPubX, 0x01, sizeof(eccPubX)); + XMEMSET(eccPubY, 0x02, sizeof(eccPubY)); + XMEMSET(eccPriv, 0x03, sizeof(eccPriv)); + + rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL); + AssertIntEQ(rc, 0); + XMEMSET(&parentKey, 0, sizeof(parentKey)); + XMEMSET(&keyBlob, 0, sizeof(keyBlob)); + + /* Seed size mismatch must return BAD_FUNC_ARG (and zero sens) */ + rc = wolfTPM2_ImportEccPrivateKeySeed(&dev, &parentKey, &keyBlob, + TPM_ECC_NIST_P256, eccPubX, sizeof(eccPubX), eccPubY, sizeof(eccPubY), + eccPriv, sizeof(eccPriv), attrs, seed, sizeof(seed)); + AssertIntEQ(rc, BAD_FUNC_ARG); + + wolfTPM2_Cleanup(&dev); + + printf("Test TPM Wrapper:\tImportEccSeed error paths:\tPassed\n"); +} +#endif /* HAVE_ECC */ + #endif /* !WOLFTPM2_NO_WRAPPER */ #ifndef NO_MAIN_DRIVER @@ -2804,6 +2841,9 @@ int unit_tests(int argc, char *argv[]) #endif test_wolfTPM2_LoadPrivateKey_NullParent(); test_wolfTPM2_EncryptDecryptBlock(); + #ifdef HAVE_ECC + test_wolfTPM2_ImportEccPrivateKeySeed_ErrorPaths(); + #endif test_wolfTPM2_KeyBlob(TPM_ALG_RSA); test_wolfTPM2_KeyBlob(TPM_ALG_ECC); #if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) && \ From 853b7f678816b821f8ae95b012f6c9deb1e5f282 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 14:06:48 -0700 Subject: [PATCH 04/35] F-2508 - https://fenrir.wolfssl.com/finding/2508 - Remove sensitive auth and key material from debug output --- src/tpm2_wrap.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 8faf7e8e..a22c27a4 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -1713,8 +1713,6 @@ int wolfTPM2_SetAuth(WOLFTPM2_DEV* dev, int index, sessionAttributes); if (auth) { printf("\tAuth Sz %d -> %d\n", session->auth.size, auth->size); - TPM2_PrintBin(session->auth.buffer, session->auth.size); - TPM2_PrintBin(auth->buffer, auth->size); } if (name) { printf("\tName Sz %d -> %d\n", session->name.size, name->size); @@ -1776,8 +1774,6 @@ int wolfTPM2_SetAuthHandle(WOLFTPM2_DEV* dev, int index, session->policyAuth, handle->policyAuth); printf("\tAuth Sz %d -> %d\n", session->auth.size, authDigestSz + handle->auth.size); - TPM2_PrintBin(session->auth.buffer, session->auth.size); - TPM2_PrintBin(handle->auth.buffer, handle->auth.size); printf("\tName Sz %d -> %d\n", session->name.size, handle->name.size); TPM2_PrintBin(session->name.name, session->name.size); TPM2_PrintBin(handle->name.name, handle->name.size); @@ -2312,7 +2308,6 @@ int wolfTPM2_EncryptSecret(WOLFTPM2_DEV* dev, const WOLFTPM2_KEY* tpmKey, #ifdef WOLFTPM_DEBUG_VERBOSE printf("Encrypt Secret %d: %d bytes\n", rc, data->size); - TPM2_PrintBin(data->buffer, data->size); #endif #endif /* !WOLFTPM2_NO_WOLFCRYPT */ @@ -2479,7 +2474,6 @@ int wolfTPM2_StartSession(WOLFTPM2_DEV* dev, WOLFTPM2_SESSION* session, if (rc == TPM_RC_SUCCESS) { #ifdef WOLFTPM_DEBUG_VERBOSE printf("Session Key %d\n", session->handle.auth.size); - TPM2_PrintBin(session->handle.auth.buffer, session->handle.auth.size); #endif /* return session */ @@ -7101,7 +7095,6 @@ int wolfTPM2_ChangeHierarchyAuth(WOLFTPM2_DEV* dev, WOLFTPM2_SESSION* session, printf("%s auth set to %d bytes of random\n", desc, in.newAuth.size); #ifdef WOLFTPM_DEBUG_VERBOSE printf("\tAuth Sz %d\n", in.newAuth.size); - TPM2_PrintBin(in.newAuth.buffer, in.newAuth.size); #endif } else { printf("Error %d setting %s auth! %s\n", @@ -9414,7 +9407,6 @@ int wolfTPM2_SetIdentityAuth(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* handle, XMEMCPY(handle->auth.buffer, &digest[16], 16); #ifdef DEBUG_WOLFTPM printf("Handle 0x%x, Auth %d\n", handle->hndl, handle->auth.size); - TPM2_PrintBin(handle->auth.buffer, handle->auth.size); #endif } else { From 9ec97d0046aa0d3f59aa264e01777821de4a9219 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 14:13:47 -0700 Subject: [PATCH 05/35] F-2512 - https://fenrir.wolfssl.com/finding/2512 - Remove session HMAC key and bind key from debug output in tpm2_param_enc.c --- src/tpm2_param_enc.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/tpm2_param_enc.c b/src/tpm2_param_enc.c index be1378f3..f285fe0e 100644 --- a/src/tpm2_param_enc.c +++ b/src/tpm2_param_enc.c @@ -214,10 +214,8 @@ TPM_RC TPM2_ParamEnc_CmdRequest(TPM2_AUTH_SESSION *session, #ifdef WOLFTPM_DEBUG_VERBOSE printf("CmdEnc Session Key %d\n", session->auth.size); - TPM2_PrintBin(session->auth.buffer, session->auth.size); if (session->bind != NULL) { printf("CmdEnc Extra Key %d\n", session->bind->size); - TPM2_PrintBin(session->bind->buffer, session->bind->size); } printf("CmdEnc Nonce caller %d\n", session->nonceCaller.size); TPM2_PrintBin(session->nonceCaller.buffer, session->nonceCaller.size); @@ -261,10 +259,8 @@ TPM_RC TPM2_ParamDec_CmdResponse(TPM2_AUTH_SESSION *session, #ifdef WOLFTPM_DEBUG_VERBOSE printf("RspDec Session Key %d\n", session->auth.size); - TPM2_PrintBin(session->auth.buffer, session->auth.size); if (session->bind != NULL) { printf("RspDec Extra Key %d\n", session->bind->size); - TPM2_PrintBin(session->bind->buffer, session->bind->size); } printf("RspDec Nonce caller %d\n", session->nonceCaller.size); TPM2_PrintBin(session->nonceCaller.buffer, session->nonceCaller.size); @@ -451,7 +447,6 @@ int TPM2_CalcHmac(TPMI_ALG_HASH authHash, TPM2B_AUTH* auth, if (auth) { #ifdef WOLFTPM_DEBUG_VERBOSE printf("HMAC Key: %d\n", auth->size); - TPM2_PrintBin(auth->buffer, auth->size); #endif rc = wc_HmacSetKey(&hmac_ctx, hashType, auth->buffer, auth->size); } From d450a533edd468546efe88fc7f22a79edbdbf9f5 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 14:20:38 -0700 Subject: [PATCH 06/35] F-2998 - https://fenrir.wolfssl.com/finding/2998 - Add unit test for wolfTPM2_NVStoreKey persistent handle range checks --- tests/unit_tests.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/tests/unit_tests.c b/tests/unit_tests.c index f22abaa7..9efb207b 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -2778,6 +2778,53 @@ static void test_wolfTPM2_ImportEccPrivateKeySeed_ErrorPaths(void) } #endif /* HAVE_ECC */ +static void test_wolfTPM2_NVStoreKey_BoundaryChecks(void) +{ + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_KEY key; + + rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL); + AssertIntEQ(rc, 0); + + XMEMSET(&key, 0, sizeof(key)); + + /* Owner hierarchy: handle below PERSISTENT_FIRST must fail */ + rc = wolfTPM2_NVStoreKey(&dev, TPM_RH_OWNER, &key, + PERSISTENT_FIRST - 1); + AssertIntEQ(rc, BAD_FUNC_ARG); + + /* Owner hierarchy: handle above PERSISTENT_LAST must fail */ + rc = wolfTPM2_NVStoreKey(&dev, TPM_RH_OWNER, &key, + PERSISTENT_LAST + 1); + AssertIntEQ(rc, BAD_FUNC_ARG); + + /* Owner hierarchy: PERSISTENT_FIRST must NOT fail with BAD_FUNC_ARG */ + key.handle.hndl = 0; /* ensure not already persistent */ + rc = wolfTPM2_NVStoreKey(&dev, TPM_RH_OWNER, &key, + PERSISTENT_FIRST); + AssertIntNE(rc, BAD_FUNC_ARG); + + /* Owner hierarchy: PERSISTENT_LAST must NOT fail with BAD_FUNC_ARG */ + rc = wolfTPM2_NVStoreKey(&dev, TPM_RH_OWNER, &key, + PERSISTENT_LAST); + AssertIntNE(rc, BAD_FUNC_ARG); + + /* Platform hierarchy: handle below PLATFORM_PERSISTENT must fail */ + rc = wolfTPM2_NVStoreKey(&dev, TPM_RH_PLATFORM, &key, + PLATFORM_PERSISTENT - 1); + AssertIntEQ(rc, BAD_FUNC_ARG); + + /* Platform hierarchy: PLATFORM_PERSISTENT must NOT fail with BAD_FUNC_ARG */ + rc = wolfTPM2_NVStoreKey(&dev, TPM_RH_PLATFORM, &key, + PLATFORM_PERSISTENT); + AssertIntNE(rc, BAD_FUNC_ARG); + + wolfTPM2_Cleanup(&dev); + + printf("Test TPM Wrapper:\tNVStoreKey boundary checks:\tPassed\n"); +} + #endif /* !WOLFTPM2_NO_WRAPPER */ #ifndef NO_MAIN_DRIVER @@ -2844,6 +2891,7 @@ int unit_tests(int argc, char *argv[]) #ifdef HAVE_ECC test_wolfTPM2_ImportEccPrivateKeySeed_ErrorPaths(); #endif + test_wolfTPM2_NVStoreKey_BoundaryChecks(); test_wolfTPM2_KeyBlob(TPM_ALG_RSA); test_wolfTPM2_KeyBlob(TPM_ALG_ECC); #if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) && \ From 1e65a8fece3d2e650ab4cc263f1470ba53565f57 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 14:23:51 -0700 Subject: [PATCH 07/35] F-2999 - https://fenrir.wolfssl.com/finding/2999 - Add unit test for wolfTPM2_NVDeleteKey persistent handle range checks --- tests/unit_tests.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 9efb207b..6242f2f1 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -2825,6 +2825,42 @@ static void test_wolfTPM2_NVStoreKey_BoundaryChecks(void) printf("Test TPM Wrapper:\tNVStoreKey boundary checks:\tPassed\n"); } +static void test_wolfTPM2_NVDeleteKey_BoundaryChecks(void) +{ + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_KEY key; + + rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL); + AssertIntEQ(rc, 0); + + XMEMSET(&key, 0, sizeof(key)); + + /* Handle below PERSISTENT_FIRST: not persistent, early-return success */ + key.handle.hndl = PERSISTENT_FIRST - 1; + rc = wolfTPM2_NVDeleteKey(&dev, TPM_RH_OWNER, &key); + AssertIntEQ(rc, TPM_RC_SUCCESS); + + /* Handle above PERSISTENT_LAST: not persistent, early-return success */ + key.handle.hndl = PERSISTENT_LAST + 1; + rc = wolfTPM2_NVDeleteKey(&dev, TPM_RH_OWNER, &key); + AssertIntEQ(rc, TPM_RC_SUCCESS); + + /* Handle equal to PERSISTENT_FIRST: IS persistent, must NOT early-return */ + key.handle.hndl = PERSISTENT_FIRST; + rc = wolfTPM2_NVDeleteKey(&dev, TPM_RH_OWNER, &key); + AssertIntNE(rc, TPM_RC_SUCCESS); /* will fail at TPM, but not early-return */ + + /* Handle equal to PERSISTENT_LAST: IS persistent, must NOT early-return */ + key.handle.hndl = PERSISTENT_LAST; + rc = wolfTPM2_NVDeleteKey(&dev, TPM_RH_OWNER, &key); + AssertIntNE(rc, TPM_RC_SUCCESS); /* will fail at TPM, but not early-return */ + + wolfTPM2_Cleanup(&dev); + + printf("Test TPM Wrapper:\tNVDeleteKey boundary checks:\tPassed\n"); +} + #endif /* !WOLFTPM2_NO_WRAPPER */ #ifndef NO_MAIN_DRIVER @@ -2892,6 +2928,7 @@ int unit_tests(int argc, char *argv[]) test_wolfTPM2_ImportEccPrivateKeySeed_ErrorPaths(); #endif test_wolfTPM2_NVStoreKey_BoundaryChecks(); + test_wolfTPM2_NVDeleteKey_BoundaryChecks(); test_wolfTPM2_KeyBlob(TPM_ALG_RSA); test_wolfTPM2_KeyBlob(TPM_ALG_ECC); #if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) && \ From 4588c27203012d41cb3694167561efe4a432b1b0 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 14:27:59 -0700 Subject: [PATCH 08/35] F-3000 - https://fenrir.wolfssl.com/finding/3000 - Add unit test for wolfTPM2_UnloadHandle persistent handle guard --- tests/unit_tests.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 6242f2f1..b63e9fda 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -2861,6 +2861,50 @@ static void test_wolfTPM2_NVDeleteKey_BoundaryChecks(void) printf("Test TPM Wrapper:\tNVDeleteKey boundary checks:\tPassed\n"); } +static void test_wolfTPM2_UnloadHandle_PersistentGuard(void) +{ + int rc; + WOLFTPM2_DEV dev; + WOLFTPM2_HANDLE handle; + + rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL); + AssertIntEQ(rc, 0); + + XMEMSET(&handle, 0, sizeof(handle)); + + /* Persistent handles must be skipped (return SUCCESS, no FlushContext) */ + handle.hndl = PERSISTENT_FIRST; + rc = wolfTPM2_UnloadHandle(&dev, &handle); + AssertIntEQ(rc, TPM_RC_SUCCESS); + + handle.hndl = PERSISTENT_LAST; + rc = wolfTPM2_UnloadHandle(&dev, &handle); + AssertIntEQ(rc, TPM_RC_SUCCESS); + + /* Null handle must be skipped */ + handle.hndl = TPM_RH_NULL; + rc = wolfTPM2_UnloadHandle(&dev, &handle); + AssertIntEQ(rc, TPM_RC_SUCCESS); + + /* Zero handle must be skipped */ + handle.hndl = 0; + rc = wolfTPM2_UnloadHandle(&dev, &handle); + AssertIntEQ(rc, TPM_RC_SUCCESS); + + /* Handle just outside persistent range: NOT skipped (attempts flush) */ + handle.hndl = PERSISTENT_FIRST - 1; + rc = wolfTPM2_UnloadHandle(&dev, &handle); + AssertIntNE(rc, TPM_RC_SUCCESS); + + handle.hndl = PERSISTENT_LAST + 1; + rc = wolfTPM2_UnloadHandle(&dev, &handle); + AssertIntNE(rc, TPM_RC_SUCCESS); + + wolfTPM2_Cleanup(&dev); + + printf("Test TPM Wrapper:\tUnloadHandle persistent guard:\tPassed\n"); +} + #endif /* !WOLFTPM2_NO_WRAPPER */ #ifndef NO_MAIN_DRIVER @@ -2929,6 +2973,7 @@ int unit_tests(int argc, char *argv[]) #endif test_wolfTPM2_NVStoreKey_BoundaryChecks(); test_wolfTPM2_NVDeleteKey_BoundaryChecks(); + test_wolfTPM2_UnloadHandle_PersistentGuard(); test_wolfTPM2_KeyBlob(TPM_ALG_RSA); test_wolfTPM2_KeyBlob(TPM_ALG_ECC); #if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) && \ From b57d352c1508d869fd06b50b2b67825254d68632 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 14:35:38 -0700 Subject: [PATCH 09/35] F-2962 - https://fenrir.wolfssl.com/finding/2962 - Add SM3_256 and SHA3 digest sizes to TPM2_GetHashDigestSize F-2963 - https://fenrir.wolfssl.com/finding/2963 F-2964 - https://fenrir.wolfssl.com/finding/2964 F-2965 - https://fenrir.wolfssl.com/finding/2965 F-3003 - https://fenrir.wolfssl.com/finding/3003 F-3006 - https://fenrir.wolfssl.com/finding/3006 F-3007 - https://fenrir.wolfssl.com/finding/3007 F-3008 - https://fenrir.wolfssl.com/finding/3008 --- src/tpm2_util.c | 4 ++++ tests/unit_tests.c | 21 +++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/tpm2_util.c b/src/tpm2_util.c index 06c34053..3a30ea0b 100644 --- a/src/tpm2_util.c +++ b/src/tpm2_util.c @@ -42,10 +42,14 @@ int TPM2_GetHashDigestSize(TPMI_ALG_HASH hashAlg) case TPM_ALG_SHA1: return TPM_SHA_DIGEST_SIZE; case TPM_ALG_SHA256: + case TPM_ALG_SM3_256: + case TPM_ALG_SHA3_256: return TPM_SHA256_DIGEST_SIZE; case TPM_ALG_SHA384: + case TPM_ALG_SHA3_384: return TPM_SHA384_DIGEST_SIZE; case TPM_ALG_SHA512: + case TPM_ALG_SHA3_512: return TPM_SHA512_DIGEST_SIZE; default: break; diff --git a/tests/unit_tests.c b/tests/unit_tests.c index b63e9fda..05875640 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -2905,6 +2905,26 @@ static void test_wolfTPM2_UnloadHandle_PersistentGuard(void) printf("Test TPM Wrapper:\tUnloadHandle persistent guard:\tPassed\n"); } +static void test_TPM2_GetHashDigestSize_AllAlgs(void) +{ + /* Standard algorithms already supported */ + AssertIntEQ(TPM2_GetHashDigestSize(TPM_ALG_SHA1), TPM_SHA_DIGEST_SIZE); + AssertIntEQ(TPM2_GetHashDigestSize(TPM_ALG_SHA256), TPM_SHA256_DIGEST_SIZE); + AssertIntEQ(TPM2_GetHashDigestSize(TPM_ALG_SHA384), TPM_SHA384_DIGEST_SIZE); + AssertIntEQ(TPM2_GetHashDigestSize(TPM_ALG_SHA512), TPM_SHA512_DIGEST_SIZE); + + /* SM3 and SHA3 must return correct non-zero digest sizes */ + AssertIntEQ(TPM2_GetHashDigestSize(TPM_ALG_SM3_256), TPM_SHA256_DIGEST_SIZE); + AssertIntEQ(TPM2_GetHashDigestSize(TPM_ALG_SHA3_256), TPM_SHA256_DIGEST_SIZE); + AssertIntEQ(TPM2_GetHashDigestSize(TPM_ALG_SHA3_384), TPM_SHA384_DIGEST_SIZE); + AssertIntEQ(TPM2_GetHashDigestSize(TPM_ALG_SHA3_512), TPM_SHA512_DIGEST_SIZE); + + /* Unknown algorithm must return 0 */ + AssertIntEQ(TPM2_GetHashDigestSize(TPM_ALG_NULL), 0); + + printf("Test TPM2:\t\tGetHashDigestSize all algs:\tPassed\n"); +} + #endif /* !WOLFTPM2_NO_WRAPPER */ #ifndef NO_MAIN_DRIVER @@ -2974,6 +2994,7 @@ int unit_tests(int argc, char *argv[]) test_wolfTPM2_NVStoreKey_BoundaryChecks(); test_wolfTPM2_NVDeleteKey_BoundaryChecks(); test_wolfTPM2_UnloadHandle_PersistentGuard(); + test_TPM2_GetHashDigestSize_AllAlgs(); test_wolfTPM2_KeyBlob(TPM_ALG_RSA); test_wolfTPM2_KeyBlob(TPM_ALG_ECC); #if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) && \ From 286a44602c7c9c00bf3ec74070051268d8843f35 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 14:45:25 -0700 Subject: [PATCH 10/35] F-3004 - https://fenrir.wolfssl.com/finding/3004 - Add kdf field to TPMT_KEYEDHASH_SCHEME XOR serialization --- src/tpm2_packet.c | 14 ++++++++-- tests/unit_tests.c | 63 +++++++++++++++++++++++++++++++++++++++++++ wolftpm/tpm2.h | 6 +++++ wolftpm/tpm2_packet.h | 4 +-- 4 files changed, 83 insertions(+), 4 deletions(-) diff --git a/src/tpm2_packet.c b/src/tpm2_packet.c index ace5c1a7..141dd5d2 100644 --- a/src/tpm2_packet.c +++ b/src/tpm2_packet.c @@ -684,14 +684,24 @@ void TPM2_Packet_ParseRsaScheme(TPM2_Packet* packet, TPMT_RSA_SCHEME* scheme) void TPM2_Packet_AppendKeyedHashScheme(TPM2_Packet* packet, TPMT_KEYEDHASH_SCHEME* scheme) { TPM2_Packet_AppendU16(packet, scheme->scheme); - if (scheme->scheme != TPM_ALG_NULL) + if (scheme->scheme == TPM_ALG_HMAC) { TPM2_Packet_AppendU16(packet, scheme->details.hmac.hashAlg); + } + else if (scheme->scheme == TPM_ALG_XOR) { + TPM2_Packet_AppendU16(packet, scheme->details.xorr.hashAlg); + TPM2_Packet_AppendU16(packet, scheme->details.xorr.kdf); + } } void TPM2_Packet_ParseKeyedHashScheme(TPM2_Packet* packet, TPMT_KEYEDHASH_SCHEME* scheme) { TPM2_Packet_ParseU16(packet, &scheme->scheme); - if (scheme->scheme != TPM_ALG_NULL) + if (scheme->scheme == TPM_ALG_HMAC) { TPM2_Packet_ParseU16(packet, &scheme->details.hmac.hashAlg); + } + else if (scheme->scheme == TPM_ALG_XOR) { + TPM2_Packet_ParseU16(packet, &scheme->details.xorr.hashAlg); + TPM2_Packet_ParseU16(packet, &scheme->details.xorr.kdf); + } } void TPM2_Packet_AppendKdfScheme(TPM2_Packet* packet, TPMT_KDF_SCHEME* scheme) diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 05875640..831745a9 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -1824,6 +1824,68 @@ static void test_TPM2_SchemeSerialize(void) printf("Test TPM Wrapper:\tSchemeSerialize:\t\tPassed\n"); } +static void test_TPM2_KeyedHashScheme_XorSerialize(void) +{ + TPM2_Packet packet; + byte buf[64]; + TPMT_KEYEDHASH_SCHEME schemeIn, schemeOut; + + /* XOR scheme roundtrip: scheme(2) + hashAlg(2) + kdf(2) = 6 bytes */ + XMEMSET(&schemeIn, 0, sizeof(schemeIn)); + schemeIn.scheme = TPM_ALG_XOR; + schemeIn.details.xorr.hashAlg = TPM_ALG_SHA256; + schemeIn.details.xorr.kdf = TPM_ALG_KDF1_SP800_108; + + XMEMSET(buf, 0, sizeof(buf)); + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = buf; + packet.size = sizeof(buf); + + TPM2_Packet_AppendKeyedHashScheme(&packet, &schemeIn); + AssertIntEQ(packet.pos, 6); + + packet.pos = 0; + XMEMSET(&schemeOut, 0, sizeof(schemeOut)); + TPM2_Packet_ParseKeyedHashScheme(&packet, &schemeOut); + + AssertIntEQ(schemeOut.scheme, TPM_ALG_XOR); + AssertIntEQ(schemeOut.details.xorr.hashAlg, TPM_ALG_SHA256); + AssertIntEQ(schemeOut.details.xorr.kdf, TPM_ALG_KDF1_SP800_108); + + /* HMAC scheme still works: scheme(2) + hashAlg(2) = 4 bytes */ + XMEMSET(&schemeIn, 0, sizeof(schemeIn)); + schemeIn.scheme = TPM_ALG_HMAC; + schemeIn.details.hmac.hashAlg = TPM_ALG_SHA384; + + XMEMSET(buf, 0, sizeof(buf)); + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = buf; + packet.size = sizeof(buf); + + TPM2_Packet_AppendKeyedHashScheme(&packet, &schemeIn); + AssertIntEQ(packet.pos, 4); + + packet.pos = 0; + XMEMSET(&schemeOut, 0, sizeof(schemeOut)); + TPM2_Packet_ParseKeyedHashScheme(&packet, &schemeOut); + AssertIntEQ(schemeOut.scheme, TPM_ALG_HMAC); + AssertIntEQ(schemeOut.details.hmac.hashAlg, TPM_ALG_SHA384); + + /* NULL scheme: scheme(2) only */ + XMEMSET(&schemeIn, 0, sizeof(schemeIn)); + schemeIn.scheme = TPM_ALG_NULL; + + XMEMSET(buf, 0, sizeof(buf)); + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = buf; + packet.size = sizeof(buf); + + TPM2_Packet_AppendKeyedHashScheme(&packet, &schemeIn); + AssertIntEQ(packet.pos, 2); + + printf("Test TPM Wrapper:\tKeyedHashScheme XOR serialize:\tPassed\n"); +} + static void test_KeySealTemplate(void) { int rc; @@ -2972,6 +3034,7 @@ int unit_tests(int argc, char *argv[]) test_wolfTPM2_ComputeName(); #endif test_TPM2_SchemeSerialize(); + test_TPM2_KeyedHashScheme_XorSerialize(); test_KeySealTemplate(); test_SealAndKeyedHash_Boundaries(); test_GetAlgId(); diff --git a/wolftpm/tpm2.h b/wolftpm/tpm2.h index 4906041c..1477c52e 100644 --- a/wolftpm/tpm2.h +++ b/wolftpm/tpm2.h @@ -1305,8 +1305,14 @@ typedef struct TPMS_SCHEME_ECDAA { typedef TPM_ALG_ID TPMI_ALG_KEYEDHASH_SCHEME; typedef TPMS_SCHEME_HASH TPMS_SCHEME_HMAC; +typedef struct TPMS_SCHEME_XOR { + TPMI_ALG_HASH hashAlg; + TPMI_ALG_KDF kdf; +} TPMS_SCHEME_XOR; + typedef union TPMU_SCHEME_KEYEDHASH { TPMS_SCHEME_HMAC hmac; + TPMS_SCHEME_XOR xorr; } TPMU_SCHEME_KEYEDHASH; typedef struct TPMT_KEYEDHASH_SCHEME { diff --git a/wolftpm/tpm2_packet.h b/wolftpm/tpm2_packet.h index c2de1617..614230e5 100644 --- a/wolftpm/tpm2_packet.h +++ b/wolftpm/tpm2_packet.h @@ -187,8 +187,8 @@ WOLFTPM_TEST_API void TPM2_Packet_AppendEccScheme(TPM2_Packet* packet, TPMT_SIG_ WOLFTPM_TEST_API void TPM2_Packet_ParseEccScheme(TPM2_Packet* packet, TPMT_SIG_SCHEME* scheme); WOLFTPM_TEST_API void TPM2_Packet_AppendRsaScheme(TPM2_Packet* packet, TPMT_RSA_SCHEME* scheme); WOLFTPM_TEST_API void TPM2_Packet_ParseRsaScheme(TPM2_Packet* packet, TPMT_RSA_SCHEME* scheme); -WOLFTPM_LOCAL void TPM2_Packet_AppendKeyedHashScheme(TPM2_Packet* packet, TPMT_KEYEDHASH_SCHEME* scheme); -WOLFTPM_LOCAL void TPM2_Packet_ParseKeyedHashScheme(TPM2_Packet* packet, TPMT_KEYEDHASH_SCHEME* scheme); +WOLFTPM_TEST_API void TPM2_Packet_AppendKeyedHashScheme(TPM2_Packet* packet, TPMT_KEYEDHASH_SCHEME* scheme); +WOLFTPM_TEST_API void TPM2_Packet_ParseKeyedHashScheme(TPM2_Packet* packet, TPMT_KEYEDHASH_SCHEME* scheme); WOLFTPM_LOCAL void TPM2_Packet_AppendKdfScheme(TPM2_Packet* packet, TPMT_KDF_SCHEME* scheme); WOLFTPM_LOCAL void TPM2_Packet_ParseKdfScheme(TPM2_Packet* packet, TPMT_KDF_SCHEME* scheme); WOLFTPM_LOCAL void TPM2_Packet_AppendAsymScheme(TPM2_Packet* packet, TPMT_ASYM_SCHEME* scheme); From 31bcbb234f0c24694bcc33cee3dc3bddb0fb677c Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 14:51:14 -0700 Subject: [PATCH 11/35] F-3005 - https://fenrir.wolfssl.com/finding/3005 - Handle ECSCHNORR and SM2 signature serialization --- src/tpm2_packet.c | 4 +++ tests/unit_tests.c | 59 +++++++++++++++++++++++++++++++++++++++++++ wolftpm/tpm2_packet.h | 4 +-- 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/src/tpm2_packet.c b/src/tpm2_packet.c index 141dd5d2..2a3b32d2 100644 --- a/src/tpm2_packet.c +++ b/src/tpm2_packet.c @@ -1084,6 +1084,8 @@ void TPM2_Packet_AppendSignature(TPM2_Packet* packet, TPMT_SIGNATURE* sig) switch (sig->sigAlg) { case TPM_ALG_ECDSA: case TPM_ALG_ECDAA: + case TPM_ALG_ECSCHNORR: + case TPM_ALG_SM2: TPM2_Packet_AppendU16(packet, sig->signature.ecdsa.hash); TPM2_Packet_AppendU16(packet, sig->signature.ecdsa.signatureR.size); @@ -1121,6 +1123,8 @@ void TPM2_Packet_ParseSignature(TPM2_Packet* packet, TPMT_SIGNATURE* sig) switch (sig->sigAlg) { case TPM_ALG_ECDSA: case TPM_ALG_ECDAA: + case TPM_ALG_ECSCHNORR: + case TPM_ALG_SM2: TPM2_Packet_ParseU16(packet, &sig->signature.ecdsa.hash); TPM2_Packet_ParseU16(packet, &wireSize); diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 831745a9..9b6f8eaa 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -1886,6 +1886,64 @@ static void test_TPM2_KeyedHashScheme_XorSerialize(void) printf("Test TPM Wrapper:\tKeyedHashScheme XOR serialize:\tPassed\n"); } +static void test_TPM2_Signature_EcSchnorrSm2Serialize(void) +{ + TPM2_Packet packet; + byte buf[256]; + TPMT_SIGNATURE sigIn, sigOut; + const byte rBuf[8] = {0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88}; + const byte sBuf[8] = {0x99,0xaa,0xbb,0xcc,0xdd,0xee,0xff,0x00}; + + /* ECSCHNORR: sigAlg(2) + hashAlg(2) + rSz(2) + r(8) + sSz(2) + s(8) = 24 */ + XMEMSET(&sigIn, 0, sizeof(sigIn)); + sigIn.sigAlg = TPM_ALG_ECSCHNORR; + sigIn.signature.ecdsa.hash = TPM_ALG_SHA256; + sigIn.signature.ecdsa.signatureR.size = sizeof(rBuf); + XMEMCPY(sigIn.signature.ecdsa.signatureR.buffer, rBuf, sizeof(rBuf)); + sigIn.signature.ecdsa.signatureS.size = sizeof(sBuf); + XMEMCPY(sigIn.signature.ecdsa.signatureS.buffer, sBuf, sizeof(sBuf)); + + XMEMSET(buf, 0, sizeof(buf)); + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = buf; + packet.size = sizeof(buf); + + TPM2_Packet_AppendSignature(&packet, &sigIn); + AssertIntEQ(packet.pos, 24); + + packet.pos = 0; + XMEMSET(&sigOut, 0, sizeof(sigOut)); + TPM2_Packet_ParseSignature(&packet, &sigOut); + AssertIntEQ(sigOut.sigAlg, TPM_ALG_ECSCHNORR); + AssertIntEQ(sigOut.signature.ecdsa.hash, TPM_ALG_SHA256); + AssertIntEQ(sigOut.signature.ecdsa.signatureR.size, sizeof(rBuf)); + AssertIntEQ(XMEMCMP(sigOut.signature.ecdsa.signatureR.buffer, + rBuf, sizeof(rBuf)), 0); + AssertIntEQ(sigOut.signature.ecdsa.signatureS.size, sizeof(sBuf)); + AssertIntEQ(XMEMCMP(sigOut.signature.ecdsa.signatureS.buffer, + sBuf, sizeof(sBuf)), 0); + + /* SM2: same wire format */ + sigIn.sigAlg = TPM_ALG_SM2; + + XMEMSET(buf, 0, sizeof(buf)); + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = buf; + packet.size = sizeof(buf); + + TPM2_Packet_AppendSignature(&packet, &sigIn); + AssertIntEQ(packet.pos, 24); + + packet.pos = 0; + XMEMSET(&sigOut, 0, sizeof(sigOut)); + TPM2_Packet_ParseSignature(&packet, &sigOut); + AssertIntEQ(sigOut.sigAlg, TPM_ALG_SM2); + AssertIntEQ(sigOut.signature.ecdsa.signatureR.size, sizeof(rBuf)); + AssertIntEQ(sigOut.signature.ecdsa.signatureS.size, sizeof(sBuf)); + + printf("Test TPM Wrapper:\tSignature ECSCHNORR/SM2 serialize:\tPassed\n"); +} + static void test_KeySealTemplate(void) { int rc; @@ -3035,6 +3093,7 @@ int unit_tests(int argc, char *argv[]) #endif test_TPM2_SchemeSerialize(); test_TPM2_KeyedHashScheme_XorSerialize(); + test_TPM2_Signature_EcSchnorrSm2Serialize(); test_KeySealTemplate(); test_SealAndKeyedHash_Boundaries(); test_GetAlgId(); diff --git a/wolftpm/tpm2_packet.h b/wolftpm/tpm2_packet.h index 614230e5..42ee3f06 100644 --- a/wolftpm/tpm2_packet.h +++ b/wolftpm/tpm2_packet.h @@ -233,8 +233,8 @@ WOLFTPM_LOCAL void TPM2_Packet_ParsePublicParms(TPM2_Packet* packet, TPMI_ALG_PU WOLFTPM_LOCAL void TPM2_Packet_AppendPublicArea(TPM2_Packet* packet, TPMT_PUBLIC* publicArea); WOLFTPM_LOCAL void TPM2_Packet_AppendPublic(TPM2_Packet* packet, TPM2B_PUBLIC* pub); WOLFTPM_LOCAL void TPM2_Packet_ParsePublic(TPM2_Packet* packet, TPM2B_PUBLIC* pub); -WOLFTPM_LOCAL void TPM2_Packet_AppendSignature(TPM2_Packet* packet, TPMT_SIGNATURE* sig); -WOLFTPM_LOCAL void TPM2_Packet_ParseSignature(TPM2_Packet* packet, TPMT_SIGNATURE* sig); +WOLFTPM_TEST_API void TPM2_Packet_AppendSignature(TPM2_Packet* packet, TPMT_SIGNATURE* sig); +WOLFTPM_TEST_API void TPM2_Packet_ParseSignature(TPM2_Packet* packet, TPMT_SIGNATURE* sig); WOLFTPM_LOCAL void TPM2_Packet_ParseAttest(TPM2_Packet* packet, TPMS_ATTEST* out); From a9a992b20869b25c639b4dce40c5061a8cfd5ec2 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 14:52:44 -0700 Subject: [PATCH 12/35] F-2969 - https://fenrir.wolfssl.com/finding/2969 - Clear auth from stack-local HashSequenceStart_In in wolfTPM2_HashStart --- src/tpm2_wrap.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index a22c27a4..3004a3b8 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -6363,6 +6363,8 @@ int wolfTPM2_HashStart(WOLFTPM2_DEV* dev, WOLFTPM2_HASH* hash, wolfTPM2_CopyAuth(&in.auth, &hash->handle.auth); in.hashAlg = hashAlg; rc = TPM2_HashSequenceStart(&in, &out); + /* Clear auth copy on stack regardless of outcome */ + TPM2_ForceZero(&in.auth, sizeof(in.auth)); if (rc != TPM_RC_SUCCESS) { #ifdef DEBUG_WOLFTPM printf("TPM2_HashSequenceStart failed 0x%x: %s\n", rc, From 1dbdfe27c08c1b08e355b52c2e635056dfc66ecd Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 14:55:48 -0700 Subject: [PATCH 13/35] F-3016 - https://fenrir.wolfssl.com/finding/3016 - Clear session->salt on error returns in wolfTPM2_StartSession --- src/tpm2_wrap.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 3004a3b8..41e13dfd 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -2392,6 +2392,7 @@ int wolfTPM2_StartSession(WOLFTPM2_DEV* dev, WOLFTPM2_SESSION* session, session->salt.size = hashDigestSz; rc = TPM2_GetNonceNoLock(session->salt.buffer, session->salt.size); if (rc != 0) { + TPM2_ForceZero(&session->salt, sizeof(session->salt)); return rc; } @@ -2403,6 +2404,7 @@ int wolfTPM2_StartSession(WOLFTPM2_DEV* dev, WOLFTPM2_SESSION* session, printf("Building encrypted salt failed %d: %s!\n", rc, wolfTPM2_GetRCString(rc)); #endif + TPM2_ForceZero(&session->salt, sizeof(session->salt)); return rc; } } @@ -2413,6 +2415,7 @@ int wolfTPM2_StartSession(WOLFTPM2_DEV* dev, WOLFTPM2_SESSION* session, printf("TPM2_StartAuthSession failed %d: %s\n", rc, wolfTPM2_GetRCString(rc)); #endif + TPM2_ForceZero(&session->salt, sizeof(session->salt)); return rc; } From 9ea82f4eebaa2b959e1897fe8d5c910e023f7375 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 15:04:44 -0700 Subject: [PATCH 14/35] F-2503 - https://fenrir.wolfssl.com/finding/2503 - Remove short-circuit OR in HMAC response verification --- src/tpm2.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/tpm2.c b/src/tpm2.c index be53db53..365c345f 100644 --- a/src/tpm2.c +++ b/src/tpm2.c @@ -354,6 +354,8 @@ static int TPM2_ResponseProcess(TPM2_CTX* ctx, TPM2_Packet* packet, UINT16 expectedHmacSz = TPM2_GetHashDigestSize(session->authHash); TPM2B_DIGEST hash; TPM2B_AUTH hmac; + int sizeMismatch; + int diff; if (expectedHmacSz == 0 || authRsp.hmac.size != expectedHmacSz) { #ifdef DEBUG_WOLFTPM @@ -384,10 +386,13 @@ static int TPM2_ResponseProcess(TPM2_CTX* ctx, TPM2_Packet* packet, return rc; } - /* Verify HMAC using constant-time comparison */ - if (hmac.size != authRsp.hmac.size || - TPM2_ConstantCompare(hmac.buffer, authRsp.hmac.buffer, - hmac.size) != 0) { + /* Verify HMAC using constant-time comparison — always run + * TPM2_ConstantCompare so code path timing doesn't depend on + * whether the size matched. */ + sizeMismatch = (hmac.size != authRsp.hmac.size); + diff = TPM2_ConstantCompare(hmac.buffer, authRsp.hmac.buffer, + expectedHmacSz); + if (sizeMismatch | diff) { #ifdef DEBUG_WOLFTPM printf("Response HMAC verification failed!\n"); #endif From 924d8afee2b95053aeaafbd59d3ca047f2ba9340 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 15:07:14 -0700 Subject: [PATCH 15/35] F-2973 - https://fenrir.wolfssl.com/finding/2973 - Use constant-time export for ECDH shared secret in wolfTPM2_EncryptSecret_ECC --- src/tpm2_wrap.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 41e13dfd..c899efe7 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -2133,10 +2133,10 @@ static int wolfTPM2_EncryptSecret_ECC(WOLFTPM2_DEV* dev, const WOLFTPM2_KEY* tpm r, &a, &prime, 1); } if (rc == 0) { - /* export shared secret x - zero pad to key size */ - secretPoint.point.x.size = mp_unsigned_bin_size(r->x); - rc = mp_to_unsigned_bin(r->x, - &secretPoint.point.x.buffer[keySz-secretPoint.point.x.size]); + /* export shared secret x - constant-time fixed-size export avoids + * leaking the leading-zero count of the ECDH shared secret via + * data-dependent offsets */ + rc = mp_to_unsigned_bin_len_ct(r->x, secretPoint.point.x.buffer, keySz); secretPoint.point.x.size = keySz; } if (rc == 0) { From 2973ae05c3c86b9a6a37a9e22292c4517bda0fa8 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 15:08:13 -0700 Subject: [PATCH 16/35] F-2974 - https://fenrir.wolfssl.com/finding/2974 - Use constant-time export for ECC signature r/s in policy_sign example --- examples/pcr/policy_sign.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/examples/pcr/policy_sign.c b/examples/pcr/policy_sign.c index 4f1d19ed..1ffa4288 100644 --- a/examples/pcr/policy_sign.c +++ b/examples/pcr/policy_sign.c @@ -192,14 +192,12 @@ static int PolicySign(TPM_ALG_ID alg, const char* keyFile, const char* password, rc = wc_ecc_sign_hash_ex(hash, hashSz, &rng, &key.ecc, &r, &s); } if (rc == 0) { - word32 keySz = key.ecc.dp->size, rSz, sSz; + word32 keySz = key.ecc.dp->size; *sigSz = keySz * 2; - XMEMSET(sig, 0, *sigSz); - /* export sign r/s - zero pad to key size */ - rSz = mp_unsigned_bin_size(&r); - mp_to_unsigned_bin(&r, &sig[keySz - rSz]); - sSz = mp_unsigned_bin_size(&s); - mp_to_unsigned_bin(&s, &sig[keySz + (keySz - sSz)]); + /* constant-time fixed-width export of r and s avoids + * leaking the leading-zero count of each component */ + mp_to_unsigned_bin_len_ct(&r, &sig[0], keySz); + mp_to_unsigned_bin_len_ct(&s, &sig[keySz], keySz); mp_clear(&r); mp_clear(&s); } From e215cdeb9e946f7622020d9a04977b4eb6c54406 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 15:12:10 -0700 Subject: [PATCH 17/35] F-2993 - https://fenrir.wolfssl.com/finding/2993 - Remove short-circuit OR in TPM2_HmacVerify constant-time comparison --- src/tpm2_crypto.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/tpm2_crypto.c b/src/tpm2_crypto.c index b3c8ce6a..337169d9 100644 --- a/src/tpm2_crypto.c +++ b/src/tpm2_crypto.c @@ -461,6 +461,8 @@ int TPM2_HmacVerify( int rc; byte computed[WC_MAX_DIGEST_SIZE]; word32 computedSz = (word32)sizeof(computed); + int neq, diff; + word32 cmpSz; if (expected == NULL || expectedSz == 0) { return BAD_FUNC_ARG; @@ -469,8 +471,11 @@ int TPM2_HmacVerify( rc = TPM2_HmacCompute(hashAlg, key, keySz, data, dataSz, data2, data2Sz, computed, &computedSz); if (rc == 0) { - if (expectedSz != computedSz || - TPM2_ConstantCompare(computed, expected, computedSz) != 0) { + /* Always run TPM2_ConstantCompare so timing doesn't leak size match */ + neq = (expectedSz != computedSz); + cmpSz = (expectedSz < computedSz) ? expectedSz : computedSz; + diff = TPM2_ConstantCompare(computed, expected, cmpSz); + if (neq | diff) { rc = TPM_RC_INTEGRITY; } } From 2c2e1251533d5ecb3d7642e40ed777a962dace5d Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 15:15:22 -0700 Subject: [PATCH 18/35] F-2994 - https://fenrir.wolfssl.com/finding/2994 - Remove short-circuit OR in FWTPM_ProcessCommand HMAC session auth --- src/fwtpm/fwtpm_command.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index 3d2ef942..c8491852 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -13072,6 +13072,9 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, const byte* authVal = NULL; int authValSz = 0; TPM_HANDLE entityH; + int sizeMismatch; + int hmacDiff; + word32 cmpSz; /* Compute cpHash = H(commandCode || handleNames || cpBuffer) */ if (FwComputeCpHash(hSess->authHash, cmdCode, @@ -13120,9 +13123,13 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, 0, /* isResponse=0 for command HMAC */ expectedHmac, &expectedSz); - if (cmdAuths[hj].cmdHmacSize != (UINT16)expectedSz || - TPM2_ConstantCompare(cmdAuths[hj].cmdHmac, - expectedHmac, (word32)expectedSz) != 0) { + /* Always run TPM2_ConstantCompare so timing doesn't leak size */ + sizeMismatch = (cmdAuths[hj].cmdHmacSize != (UINT16)expectedSz); + cmpSz = (cmdAuths[hj].cmdHmacSize < (UINT16)expectedSz) ? + cmdAuths[hj].cmdHmacSize : (word32)expectedSz; + hmacDiff = TPM2_ConstantCompare(cmdAuths[hj].cmdHmac, + expectedHmac, cmpSz); + if (sizeMismatch | hmacDiff) { #ifdef DEBUG_WOLFTPM printf("fwTPM: HMAC session auth failed for handle " "0x%x (CC=0x%x)\n", entityH, cmdCode); From 865901eca44ce257e03c08478314ba957202652c Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 15:17:33 -0700 Subject: [PATCH 19/35] F-2995 - https://fenrir.wolfssl.com/finding/2995 - Remove short-circuit OR in FwCredentialUnwrap integrity HMAC check --- src/fwtpm/fwtpm_crypto.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/fwtpm/fwtpm_crypto.c b/src/fwtpm/fwtpm_crypto.c index 1c3d2981..e5e072df 100644 --- a/src/fwtpm/fwtpm_crypto.c +++ b/src/fwtpm/fwtpm_crypto.c @@ -2866,6 +2866,8 @@ TPM_RC FwCredentialUnwrap( const byte* encIdentity; int encIdentitySz; byte iv[AES_BLOCK_SIZE]; + int sizeMismatch; + int hmacDiff; FWTPM_DECLARE_VAR(aes, Aes); FWTPM_DECLARE_VAR(hmac, Hmac); FWTPM_DECLARE_BUF(decBuf, FWTPM_MAX_NV_DATA + 2); @@ -2910,9 +2912,11 @@ TPM_RC FwCredentialUnwrap( } } if (rc == 0) { - if (integrityHmacSz != TPM_SHA256_DIGEST_SIZE || - TPM2_ConstantCompare(computedHmac, integrityHmac, - TPM_SHA256_DIGEST_SIZE) != 0) { + /* Always run TPM2_ConstantCompare so timing doesn't leak size match */ + sizeMismatch = (integrityHmacSz != TPM_SHA256_DIGEST_SIZE); + hmacDiff = TPM2_ConstantCompare(computedHmac, integrityHmac, + TPM_SHA256_DIGEST_SIZE); + if (sizeMismatch | hmacDiff) { rc = TPM_RC_INTEGRITY; } } From ab67ef90bb8e5c02d673825db460f4d2a70c1702 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 15:19:38 -0700 Subject: [PATCH 20/35] F-2996 - https://fenrir.wolfssl.com/finding/2996 - Remove short-circuit OR in FwImportVerifyAndDecrypt integrity HMAC check --- src/fwtpm/fwtpm_crypto.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/fwtpm/fwtpm_crypto.c b/src/fwtpm/fwtpm_crypto.c index e5e072df..1843428c 100644 --- a/src/fwtpm/fwtpm_crypto.c +++ b/src/fwtpm/fwtpm_crypto.c @@ -1781,6 +1781,8 @@ TPM_RC FwImportVerifyAndDecrypt( FWTPM_DECLARE_VAR(hmacObj, Hmac); byte zeroIV[AES_BLOCK_SIZE]; enum wc_HashType wcHmacType; + int sizeMismatch; + int hmacDiff; FWTPM_ALLOC_VAR(aesObj, Aes); FWTPM_ALLOC_VAR(hmacObj, Hmac); @@ -1833,8 +1835,10 @@ TPM_RC FwImportVerifyAndDecrypt( } } if (rc == 0) { - if (integritySize != (UINT16)digestSz || - TPM2_ConstantCompare(integrity, hmacCalc, (word32)digestSz) != 0) { + /* Always run TPM2_ConstantCompare so timing doesn't leak size match */ + sizeMismatch = (integritySize != (UINT16)digestSz); + hmacDiff = TPM2_ConstantCompare(integrity, hmacCalc, (word32)digestSz); + if (sizeMismatch | hmacDiff) { rc = TPM_RC_INTEGRITY; } } From 8d7cca60062eb80ee97cd1653f44ce97a912bb51 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 15:21:37 -0700 Subject: [PATCH 21/35] F-2997 - https://fenrir.wolfssl.com/finding/2997 - Remove short-circuit OR in FWTPM_ProcessCommand policy digest check --- src/fwtpm/fwtpm_command.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index c8491852..812032f6 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -12902,6 +12902,9 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, FWTPM_Session* pSess = cmdAuths[pj].sess; TPM_HANDLE entityH = cmdHandles[pj]; TPM2B_DIGEST* authPolicy = NULL; + int sizeMismatch; + int policyDiff; + word32 cmpSz; /* Find entity's authPolicy by handle type */ #ifndef FWTPM_NO_NV @@ -12943,9 +12946,13 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, /* If entity has a non-empty authPolicy, it must match */ if (authPolicy != NULL && authPolicy->size > 0) { - if (pSess->policyDigest.size != authPolicy->size || - TPM2_ConstantCompare(pSess->policyDigest.buffer, - authPolicy->buffer, authPolicy->size) != 0) { + /* Always run TPM2_ConstantCompare so timing doesn't leak size */ + sizeMismatch = (pSess->policyDigest.size != authPolicy->size); + cmpSz = (pSess->policyDigest.size < authPolicy->size) ? + pSess->policyDigest.size : authPolicy->size; + policyDiff = TPM2_ConstantCompare(pSess->policyDigest.buffer, + authPolicy->buffer, cmpSz); + if (sizeMismatch | policyDiff) { #ifdef DEBUG_WOLFTPM printf("fwTPM: Policy digest mismatch for handle " "0x%x (CC=0x%x)\n", entityH, cmdCode); From 12c039c9f5f5f95c7b7f071c047a35ad64d22ebe Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 15:34:11 -0700 Subject: [PATCH 22/35] F-2979 - https://fenrir.wolfssl.com/finding/2979 - Scale session AES key size to match authHash strength in wolfTPM2_StartSession --- src/tpm2_wrap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index c899efe7..90ce663e 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -2366,7 +2366,8 @@ int wolfTPM2_StartSession(WOLFTPM2_DEV* dev, WOLFTPM2_SESSION* session, authSesIn.sessionType = sesType; if (encDecAlg == TPM_ALG_CFB) { authSesIn.symmetric.algorithm = TPM_ALG_AES; - authSesIn.symmetric.keyBits.aes = 128; + /* Scale AES key size to match session hash strength */ + authSesIn.symmetric.keyBits.aes = (hashDigestSz > 32) ? 256 : 128; authSesIn.symmetric.mode.aes = TPM_ALG_CFB; } else if (encDecAlg == TPM_ALG_XOR) { From 869e667c87cd933f900178018bfdd5b722642d52 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 15:35:48 -0700 Subject: [PATCH 23/35] F-2980 - https://fenrir.wolfssl.com/finding/2980 - Scale AES key size to RSA key strength in wolfTPM2_ImportRsaPrivateKeySeed --- src/tpm2_wrap.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 90ce663e..44e446e0 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -3401,7 +3401,10 @@ int wolfTPM2_ImportRsaPrivateKeySeed(WOLFTPM2_DEV* dev, ((attributes & TPMA_OBJECT_decrypt) && (attributes & TPMA_OBJECT_restricted))) { pub.publicArea.parameters.rsaDetail.symmetric.algorithm = TPM_ALG_AES; - pub.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = 128; + /* Scale AES key size to match RSA key strength (matches + * GetKeyTemplateRSA pattern) */ + pub.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = + (rsaPubSz * 8 > 2048) ? 256 : 128; pub.publicArea.parameters.rsaDetail.symmetric.mode.aes = TPM_ALG_CFB; } else { From 07924bf15829d891ee57cf29a51a4b2a481c3f54 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 15:37:06 -0700 Subject: [PATCH 24/35] F-2532 - https://fenrir.wolfssl.com/finding/2532 - Document pub->size mutation side effect in TPM2_Packet_AppendPublic --- src/tpm2_packet.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tpm2_packet.c b/src/tpm2_packet.c index 2a3b32d2..0d230eb8 100644 --- a/src/tpm2_packet.c +++ b/src/tpm2_packet.c @@ -1024,6 +1024,9 @@ void TPM2_Packet_AppendPublicArea(TPM2_Packet* packet, TPMT_PUBLIC* publicArea) break; } } +/* Serializes pub into packet and writes the encoded size back to pub->size + * as a side effect. Callers (TPM2_AppendPublic, wolfTPM2_GetKeyBlobAsBuffer) + * rely on this side effect to determine the serialized public-area size. */ void TPM2_Packet_AppendPublic(TPM2_Packet* packet, TPM2B_PUBLIC* pub) { int tmpSz = 0; From adc27131455505c122d91773be554d699c13162e Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 15:39:54 -0700 Subject: [PATCH 25/35] F-2533 - https://fenrir.wolfssl.com/finding/2533 - Add TPM2_Packet_ParseSensitive counterpart and roundtrip test --- src/tpm2_packet.c | 39 +++++++++++++++++++++++ tests/unit_tests.c | 72 +++++++++++++++++++++++++++++++++++++++++++ wolftpm/tpm2_packet.h | 3 +- 3 files changed, 113 insertions(+), 1 deletion(-) diff --git a/src/tpm2_packet.c b/src/tpm2_packet.c index 0d230eb8..000b1e6f 100644 --- a/src/tpm2_packet.c +++ b/src/tpm2_packet.c @@ -825,6 +825,45 @@ void TPM2_Packet_AppendSensitive(TPM2_Packet* packet, TPM2B_SENSITIVE* sensitive TPM2_Packet_PlaceU16(packet, tmpSz); } +void TPM2_Packet_ParseSensitive(TPM2_Packet* packet, TPM2B_SENSITIVE* sensitive) +{ + TPMU_SENSITIVE_COMPOSITE* sens = &sensitive->sensitiveArea.sensitive; + + TPM2_Packet_ParseU16(packet, &sensitive->size); + if (sensitive->size == 0) { + return; + } + + TPM2_Packet_ParseU16(packet, &sensitive->sensitiveArea.sensitiveType); + + TPM2_Packet_ParseU16Buf(packet, &sensitive->sensitiveArea.authValue.size, + sensitive->sensitiveArea.authValue.buffer, + (UINT16)sizeof(sensitive->sensitiveArea.authValue.buffer)); + + TPM2_Packet_ParseU16Buf(packet, &sensitive->sensitiveArea.seedValue.size, + sensitive->sensitiveArea.seedValue.buffer, + (UINT16)sizeof(sensitive->sensitiveArea.seedValue.buffer)); + + switch (sensitive->sensitiveArea.sensitiveType) { + case TPM_ALG_RSA: + TPM2_Packet_ParseU16Buf(packet, &sens->rsa.size, + sens->rsa.buffer, (UINT16)sizeof(sens->rsa.buffer)); + break; + case TPM_ALG_ECC: + TPM2_Packet_ParseU16Buf(packet, &sens->ecc.size, + sens->ecc.buffer, (UINT16)sizeof(sens->ecc.buffer)); + break; + case TPM_ALG_KEYEDHASH: + TPM2_Packet_ParseU16Buf(packet, &sens->bits.size, + sens->bits.buffer, (UINT16)sizeof(sens->bits.buffer)); + break; + case TPM_ALG_SYMCIPHER: + TPM2_Packet_ParseU16Buf(packet, &sens->sym.size, + sens->sym.buffer, (UINT16)sizeof(sens->sym.buffer)); + break; + } +} + void TPM2_Packet_AppendSensitiveCreate(TPM2_Packet* packet, TPM2B_SENSITIVE_CREATE* sensitive) { int tmpSz = 0; diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 9b6f8eaa..3d6b9f11 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -1944,6 +1944,77 @@ static void test_TPM2_Signature_EcSchnorrSm2Serialize(void) printf("Test TPM Wrapper:\tSignature ECSCHNORR/SM2 serialize:\tPassed\n"); } +static void test_TPM2_Sensitive_Roundtrip(void) +{ + TPM2_Packet packet; + byte buf[512]; + TPM2B_SENSITIVE sensIn, sensOut; + const byte authBuf[4] = {0x01, 0x02, 0x03, 0x04}; + const byte seedBuf[8] = {0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0x00, 0x11}; + const byte rsaPriv[16] = { + 0xde, 0xad, 0xbe, 0xef, 0x11, 0x22, 0x33, 0x44, + 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc + }; + + /* RSA sensitive roundtrip */ + XMEMSET(&sensIn, 0, sizeof(sensIn)); + sensIn.sensitiveArea.sensitiveType = TPM_ALG_RSA; + sensIn.sensitiveArea.authValue.size = sizeof(authBuf); + XMEMCPY(sensIn.sensitiveArea.authValue.buffer, authBuf, sizeof(authBuf)); + sensIn.sensitiveArea.seedValue.size = sizeof(seedBuf); + XMEMCPY(sensIn.sensitiveArea.seedValue.buffer, seedBuf, sizeof(seedBuf)); + sensIn.sensitiveArea.sensitive.rsa.size = sizeof(rsaPriv); + XMEMCPY(sensIn.sensitiveArea.sensitive.rsa.buffer, rsaPriv, + sizeof(rsaPriv)); + + 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.authValue.size, sizeof(authBuf)); + AssertIntEQ(XMEMCMP(sensOut.sensitiveArea.authValue.buffer, + authBuf, sizeof(authBuf)), 0); + AssertIntEQ(sensOut.sensitiveArea.seedValue.size, sizeof(seedBuf)); + AssertIntEQ(XMEMCMP(sensOut.sensitiveArea.seedValue.buffer, + seedBuf, sizeof(seedBuf)), 0); + AssertIntEQ(sensOut.sensitiveArea.sensitive.rsa.size, sizeof(rsaPriv)); + AssertIntEQ(XMEMCMP(sensOut.sensitiveArea.sensitive.rsa.buffer, + rsaPriv, sizeof(rsaPriv)), 0); + + /* ECC sensitive roundtrip */ + XMEMSET(&sensIn, 0, sizeof(sensIn)); + sensIn.sensitiveArea.sensitiveType = TPM_ALG_ECC; + sensIn.sensitiveArea.sensitive.ecc.size = sizeof(rsaPriv); + XMEMCPY(sensIn.sensitiveArea.sensitive.ecc.buffer, rsaPriv, + sizeof(rsaPriv)); + + 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_ECC); + AssertIntEQ(sensOut.sensitiveArea.sensitive.ecc.size, sizeof(rsaPriv)); + AssertIntEQ(XMEMCMP(sensOut.sensitiveArea.sensitive.ecc.buffer, + rsaPriv, sizeof(rsaPriv)), 0); + + printf("Test TPM Wrapper:\tSensitive roundtrip:\t\tPassed\n"); +} + static void test_KeySealTemplate(void) { int rc; @@ -3094,6 +3165,7 @@ int unit_tests(int argc, char *argv[]) test_TPM2_SchemeSerialize(); test_TPM2_KeyedHashScheme_XorSerialize(); test_TPM2_Signature_EcSchnorrSm2Serialize(); + test_TPM2_Sensitive_Roundtrip(); test_KeySealTemplate(); test_SealAndKeyedHash_Boundaries(); test_GetAlgId(); diff --git a/wolftpm/tpm2_packet.h b/wolftpm/tpm2_packet.h index 42ee3f06..55f97fad 100644 --- a/wolftpm/tpm2_packet.h +++ b/wolftpm/tpm2_packet.h @@ -197,7 +197,8 @@ WOLFTPM_LOCAL void TPM2_Packet_AppendEccPoint(TPM2_Packet* packet, TPMS_ECC_POIN WOLFTPM_LOCAL void TPM2_Packet_ParseEccPoint(TPM2_Packet* packet, TPMS_ECC_POINT* point); WOLFTPM_LOCAL void TPM2_Packet_AppendPoint(TPM2_Packet* packet, TPM2B_ECC_POINT* point); WOLFTPM_LOCAL void TPM2_Packet_ParsePoint(TPM2_Packet* packet, TPM2B_ECC_POINT* point); -WOLFTPM_LOCAL void TPM2_Packet_AppendSensitive(TPM2_Packet* packet, TPM2B_SENSITIVE* sensitive); +WOLFTPM_TEST_API void TPM2_Packet_AppendSensitive(TPM2_Packet* packet, TPM2B_SENSITIVE* sensitive); +WOLFTPM_TEST_API void TPM2_Packet_ParseSensitive(TPM2_Packet* packet, TPM2B_SENSITIVE* sensitive); WOLFTPM_LOCAL void TPM2_Packet_AppendSensitiveCreate(TPM2_Packet* packet, TPM2B_SENSITIVE_CREATE* sensitive); #ifdef WOLFTPM_FWTPM /*! From 04d989ee3ccf88c8be73434d2d33da79a0fd9805 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 15:40:48 -0700 Subject: [PATCH 26/35] F-2164 - https://fenrir.wolfssl.com/finding/2164 - Clarify Zephyr SPI not implemented error message --- hal/tpm_io_zephyr.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hal/tpm_io_zephyr.c b/hal/tpm_io_zephyr.c index 97090c5c..83718ace 100644 --- a/hal/tpm_io_zephyr.c +++ b/hal/tpm_io_zephyr.c @@ -169,8 +169,9 @@ int TPM2_IoCb_Zephyr_I2C(TPM2_CTX* ctx, int isRead, word32 addr, } #else /* If not I2C, it must be SPI */ - /* TODO implement SPI */ - #error TPM2 SPI support on zephyr yet + #error "TPM2 SPI transport is not implemented on Zephyr. \ +Define WOLFTPM_I2C to use the I2C transport, or supply your own SPI \ +TPM2_IoCb callback via wolfTPM2_Init()." #endif #endif /* WOLFSSL_ZEPHYR */ From ad4ac7d1578b0508939f08be5cc0b3f0ccfce34e Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 16:02:13 -0700 Subject: [PATCH 27/35] F-3009 - https://fenrir.wolfssl.com/finding/3009 - Remove short-circuit OR in ticket HMAC verification --- src/fwtpm/fwtpm_command.c | 62 ++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 23 deletions(-) diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index 812032f6..6a5c422e 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -8228,6 +8228,10 @@ static TPM_RC FwCmd_PolicyAuthorize(FWTPM_CTX* ctx, TPM2_Packet* cmd, int expectedSz = 0; wc_HashAlg aCtx; enum wc_HashType aWcHash; + int hmacRc; + int sizeMismatch; + int ticketDiff; + word32 cmpSz; /* Step 1: aHash = H(approvedPolicy || policyRef) * Hash algorithm comes from signing key's nameAlg */ @@ -8254,20 +8258,24 @@ static TPM_RC FwCmd_PolicyAuthorize(FWTPM_CTX* ctx, TPM2_Packet* cmd, ticketInputSz += keySignNameSz; } - /* Step 3: verify ticket HMAC */ - if (rc == 0 && - (FwComputeTicketHmac(ctx, ticketHier, keyNameAlg, - ticketInput, ticketInputSz, - expectedHmac, &expectedSz) != 0 || - ticketDigestSz != (UINT16)expectedSz || - TPM2_ConstantCompare(ticketDigest, expectedHmac, - (word32)expectedSz) != 0)) { - #ifdef DEBUG_WOLFTPM - printf("fwTPM: PolicyAuthorize ticket verify failed " - "(tag=0x%x, hier=0x%x, ticketSz=%d, expectedSz=%d)\n", - ticketTag, ticketHier, ticketDigestSz, expectedSz); - #endif - rc = TPM_RC_POLICY_FAIL; + /* Step 3: verify ticket HMAC — always run TPM2_ConstantCompare + * so timing doesn't leak size match */ + if (rc == 0) { + hmacRc = FwComputeTicketHmac(ctx, ticketHier, keyNameAlg, + ticketInput, ticketInputSz, expectedHmac, &expectedSz); + sizeMismatch = (ticketDigestSz != (UINT16)expectedSz); + cmpSz = (ticketDigestSz < (UINT16)expectedSz) ? + ticketDigestSz : (word32)expectedSz; + ticketDiff = TPM2_ConstantCompare(ticketDigest, expectedHmac, + cmpSz); + if (hmacRc != 0 || (sizeMismatch | ticketDiff)) { + #ifdef DEBUG_WOLFTPM + printf("fwTPM: PolicyAuthorize ticket verify failed " + "(tag=0x%x, hier=0x%x, ticketSz=%d, expectedSz=%d)\n", + ticketTag, ticketHier, ticketDigestSz, expectedSz); + #endif + rc = TPM_RC_POLICY_FAIL; + } } TPM2_ForceZero(aHash, sizeof(aHash)); TPM2_ForceZero(expectedHmac, sizeof(expectedHmac)); @@ -9522,6 +9530,10 @@ static TPM_RC FwCmd_PolicyTicket(FWTPM_CTX* ctx, TPM2_Packet* cmd, enum wc_HashType aWcHash; int aHashSz; byte expBuf[4]; + int hmacRc; + int sizeMismatch; + int ticketDiff; + word32 cmpSz; aWcHash = FwGetWcHashType(sess->authHash); aHashSz = TPM2_GetHashDigestSize(sess->authHash); @@ -9551,15 +9563,19 @@ static TPM_RC FwCmd_PolicyTicket(FWTPM_CTX* ctx, TPM2_Packet* cmd, ticketInputSz += authNameSz; } - /* Verify HMAC */ - if (rc == 0 && - (FwComputeTicketHmac(ctx, ticketHier, sess->authHash, - ticketInput, ticketInputSz, - expectedHmac, &expectedSz) != 0 || - ticketDigestSz != (UINT16)expectedSz || - TPM2_ConstantCompare(ticketDigest, expectedHmac, - (word32)expectedSz) != 0)) { - rc = TPM_RC_POLICY_FAIL; + /* Verify HMAC — always run TPM2_ConstantCompare so timing doesn't + * leak whether size matched */ + if (rc == 0) { + hmacRc = FwComputeTicketHmac(ctx, ticketHier, sess->authHash, + ticketInput, ticketInputSz, expectedHmac, &expectedSz); + sizeMismatch = (ticketDigestSz != (UINT16)expectedSz); + cmpSz = (ticketDigestSz < (UINT16)expectedSz) ? + ticketDigestSz : (word32)expectedSz; + ticketDiff = TPM2_ConstantCompare(ticketDigest, expectedHmac, + cmpSz); + if (hmacRc != 0 || (sizeMismatch | ticketDiff)) { + rc = TPM_RC_POLICY_FAIL; + } } TPM2_ForceZero(aHash, sizeof(aHash)); TPM2_ForceZero(expectedHmac, sizeof(expectedHmac)); From a8235b72602b23b7f944240bf2cd56f378159ea4 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 16:03:57 -0700 Subject: [PATCH 28/35] F-3010 - https://fenrir.wolfssl.com/finding/3010 - Remove short-circuit OR in FwCmd_PolicyCpHash consistency check --- src/fwtpm/fwtpm_command.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index 6a5c422e..1f7dcf8d 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -9013,6 +9013,9 @@ static TPM_RC FwCmd_PolicyCpHash(FWTPM_CTX* ctx, TPM2_Packet* cmd, FWTPM_Session* sess; UINT16 cpHashSz = 0; byte cpHashBuf[TPM_MAX_DIGEST_SIZE]; + int sizeMismatch; + int cpDiff; + word32 cmpSz; (void)cmdSize; sess = FwPolicyParseSession(ctx, cmd, cmdSize, cmdTag); @@ -9033,11 +9036,15 @@ static TPM_RC FwCmd_PolicyCpHash(FWTPM_CTX* ctx, TPM2_Packet* cmd, if (rc == 0) { TPM2_Packet_ParseBytes(cmd, cpHashBuf, cpHashSz); - /* If cpHashA already set, must be identical */ + /* If cpHashA already set, must be identical — always run + * TPM2_ConstantCompare so timing doesn't leak size match */ if (sess->cpHashA.size > 0) { - if (sess->cpHashA.size != cpHashSz || - TPM2_ConstantCompare(sess->cpHashA.buffer, cpHashBuf, - cpHashSz) != 0) { + sizeMismatch = (sess->cpHashA.size != cpHashSz); + cmpSz = (sess->cpHashA.size < cpHashSz) ? + sess->cpHashA.size : cpHashSz; + cpDiff = TPM2_ConstantCompare(sess->cpHashA.buffer, cpHashBuf, + cmpSz); + if (sizeMismatch | cpDiff) { rc = TPM_RC_CPHASH; } } From 0ff3b9a031e1c5b0004680812447270599d14107 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 16:06:06 -0700 Subject: [PATCH 29/35] F-3011 - https://fenrir.wolfssl.com/finding/3011 - Remove short-circuit OR in FwCmd_PolicyNameHash consistency check --- src/fwtpm/fwtpm_command.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index 1f7dcf8d..42ded11b 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -9079,6 +9079,9 @@ static TPM_RC FwCmd_PolicyNameHash(FWTPM_CTX* ctx, TPM2_Packet* cmd, FWTPM_Session* sess; UINT16 nameHashSz = 0; byte nameHashBuf[TPM_MAX_DIGEST_SIZE]; + int sizeMismatch; + int nameDiff; + word32 cmpSz; (void)cmdSize; sess = FwPolicyParseSession(ctx, cmd, cmdSize, cmdTag); @@ -9099,11 +9102,15 @@ static TPM_RC FwCmd_PolicyNameHash(FWTPM_CTX* ctx, TPM2_Packet* cmd, if (rc == 0) { TPM2_Packet_ParseBytes(cmd, nameHashBuf, nameHashSz); - /* If nameHash already set, must be identical */ + /* If nameHash already set, must be identical — always run + * TPM2_ConstantCompare so timing doesn't leak size match */ if (sess->nameHash.size > 0) { - if (sess->nameHash.size != nameHashSz || - TPM2_ConstantCompare(sess->nameHash.buffer, nameHashBuf, - nameHashSz) != 0) { + sizeMismatch = (sess->nameHash.size != nameHashSz); + cmpSz = (sess->nameHash.size < nameHashSz) ? + sess->nameHash.size : nameHashSz; + nameDiff = TPM2_ConstantCompare(sess->nameHash.buffer, nameHashBuf, + cmpSz); + if (sizeMismatch | nameDiff) { rc = TPM_RC_CPHASH; } } From f6d6e5309441f5844374f5e0d4bea2e704f87cb5 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 16:08:31 -0700 Subject: [PATCH 30/35] F-3012 - https://fenrir.wolfssl.com/finding/3012 - Remove short-circuit OR in FwCmd_PolicyTicket cpHashA constraint check --- src/fwtpm/fwtpm_command.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index 42ded11b..31381a43 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -9458,6 +9458,9 @@ static TPM_RC FwCmd_PolicyTicket(FWTPM_CTX* ctx, TPM2_Packet* cmd, FWTPM_Session* sess; INT32 expiration = 0; UINT32 extendCC; + int cpaSizeMismatch; + int cpaDiff; + word32 cpaCmpSz; (void)cmdSize; TPM2_Packet_ParseU32(cmd, &sessHandle); @@ -9612,13 +9615,18 @@ static TPM_RC FwCmd_PolicyTicket(FWTPM_CTX* ctx, TPM2_Packet* cmd, } } - /* Store cpHashA constraint if provided */ + /* Store cpHashA constraint if provided — always run TPM2_ConstantCompare + * so timing doesn't leak size match */ if (rc == 0 && cpHashASz > 0) { - if (sess->cpHashA.size > 0 && - (sess->cpHashA.size != cpHashASz || - TPM2_ConstantCompare(sess->cpHashA.buffer, cpHashABuf, - cpHashASz) != 0)) { - rc = TPM_RC_CPHASH; + if (sess->cpHashA.size > 0) { + cpaSizeMismatch = (sess->cpHashA.size != cpHashASz); + cpaCmpSz = (sess->cpHashA.size < cpHashASz) ? + sess->cpHashA.size : cpHashASz; + cpaDiff = TPM2_ConstantCompare(sess->cpHashA.buffer, cpHashABuf, + cpaCmpSz); + if (cpaSizeMismatch | cpaDiff) { + rc = TPM_RC_CPHASH; + } } if (rc == 0) { sess->cpHashA.size = cpHashASz; From 3b391b8579e82a03aade6f2e7efccbc5e9111157 Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 16:11:26 -0700 Subject: [PATCH 31/35] F-3013 - https://fenrir.wolfssl.com/finding/3013 - Remove short-circuit OR in FwCmd_PolicyAuthorizeNV policy digest check --- src/fwtpm/fwtpm_command.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index 31381a43..2098ebb1 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -9660,6 +9660,9 @@ static TPM_RC FwCmd_PolicyAuthorizeNV(FWTPM_CTX* ctx, TPM2_Packet* cmd, byte ccBuf[4]; UINT32 cc = TPM_CC_PolicyAuthorizeNV; int hashInit = 0; + int nvSizeMismatch; + int sessSizeMismatch; + int policyDiff; (void)cmdSize; @@ -9695,12 +9698,14 @@ static TPM_RC FwCmd_PolicyAuthorizeNV(FWTPM_CTX* ctx, TPM2_Packet* cmd, rc = TPM_RC_HASH; } - /* For policy sessions (not trial): verify policyDigest == NV data */ + /* For policy sessions (not trial): verify policyDigest == NV data. + * Always run TPM2_ConstantCompare so timing doesn't leak size match. */ if (rc == 0 && sess->sessionType == TPM_SE_POLICY) { - if ((int)nv->nvPublic.dataSize != dSz || - (int)sess->policyDigest.size != dSz || - TPM2_ConstantCompare(sess->policyDigest.buffer, - nv->data, (word32)dSz) != 0) { + nvSizeMismatch = ((int)nv->nvPublic.dataSize != dSz); + sessSizeMismatch = ((int)sess->policyDigest.size != dSz); + policyDiff = TPM2_ConstantCompare(sess->policyDigest.buffer, + nv->data, (word32)dSz); + if (nvSizeMismatch | sessSizeMismatch | policyDiff) { rc = TPM_RC_POLICY_FAIL; } } From 61077220e7762071c14a6e2864d78952745c6d6e Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 16:13:17 -0700 Subject: [PATCH 32/35] F-3014 - https://fenrir.wolfssl.com/finding/3014 - Remove short-circuit OR in FWTPM_ProcessCommand PolicyPassword auth check --- src/fwtpm/fwtpm_command.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index 2098ebb1..410f3cef 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -13140,13 +13140,18 @@ int FWTPM_ProcessCommand(FWTPM_CTX* ctx, FwLookupEntityAuth(ctx, entityH, &authVal, &authValSz); /* PolicyPassword with no sessionKey (unsalted/unbound): - * HMAC field contains plaintext authValue per spec Section 19.6.13 */ + * HMAC field contains plaintext authValue per spec Section 19.6.13. + * Always run TPM2_ConstantCompare so timing doesn't leak auth + * length match. */ if (hSess->sessionType == TPM_SE_POLICY && hSess->isPasswordPolicy && hSess->sessionKey.size == 0) { - if ((int)cmdAuths[hj].cmdHmacSize != authValSz || - TPM2_ConstantCompare(cmdAuths[hj].cmdHmac, - authVal, (word32)authValSz) != 0) { + sizeMismatch = ((int)cmdAuths[hj].cmdHmacSize != authValSz); + cmpSz = (cmdAuths[hj].cmdHmacSize < (UINT16)authValSz) ? + cmdAuths[hj].cmdHmacSize : (word32)authValSz; + hmacDiff = TPM2_ConstantCompare(cmdAuths[hj].cmdHmac, + authVal, cmpSz); + if (sizeMismatch | hmacDiff) { #ifdef DEBUG_WOLFTPM printf("fwTPM: PolicyPassword auth failed for handle " "0x%x (CC=0x%x)\n", entityH, cmdCode); From 6c7d2e6af0aff5c9d2d9a64cc154ba7d8f07b13c Mon Sep 17 00:00:00 2001 From: Aidan Date: Fri, 17 Apr 2026 16:18:53 -0700 Subject: [PATCH 33/35] F-3015 - https://fenrir.wolfssl.com/finding/3015 - Remove short-circuit OR in FwVerifySignatureCore RSA-PKCS1v1.5 check --- examples/pcr/policy_sign.c | 10 ++++++++-- src/fwtpm/fwtpm_crypto.c | 27 ++++++++++++++++++++++----- src/tpm2.c | 8 +++++--- src/tpm2_packet.c | 5 +++++ src/tpm2_wrap.c | 19 +++++++++++++------ 5 files changed, 53 insertions(+), 16 deletions(-) diff --git a/examples/pcr/policy_sign.c b/examples/pcr/policy_sign.c index 1ffa4288..5cf7d711 100644 --- a/examples/pcr/policy_sign.c +++ b/examples/pcr/policy_sign.c @@ -194,10 +194,16 @@ static int PolicySign(TPM_ALG_ID alg, const char* keyFile, const char* password, if (rc == 0) { word32 keySz = key.ecc.dp->size; *sigSz = keySz * 2; - /* constant-time fixed-width export of r and s avoids - * leaking the leading-zero count of each component */ + /* fixed-width export of r and s avoids leaking the + * leading-zero count of each component via data-dependent + * offsets */ + #ifdef WOLFSSL_HAVE_SP_ECC mp_to_unsigned_bin_len_ct(&r, &sig[0], keySz); mp_to_unsigned_bin_len_ct(&s, &sig[keySz], keySz); + #else + mp_to_unsigned_bin_len(&r, &sig[0], keySz); + mp_to_unsigned_bin_len(&s, &sig[keySz], keySz); + #endif mp_clear(&r); mp_clear(&s); } diff --git a/src/fwtpm/fwtpm_crypto.c b/src/fwtpm/fwtpm_crypto.c index 1843428c..23c5933d 100644 --- a/src/fwtpm/fwtpm_crypto.c +++ b/src/fwtpm/fwtpm_crypto.c @@ -1835,9 +1835,12 @@ TPM_RC FwImportVerifyAndDecrypt( } } if (rc == 0) { - /* Always run TPM2_ConstantCompare so timing doesn't leak size match */ + /* Always run TPM2_ConstantCompare over min(sizes) so timing doesn't + * leak size match and we don't read past integrity[integritySize] */ + word32 cmpSz = (integritySize < (UINT16)digestSz) ? + (word32)integritySize : (word32)digestSz; sizeMismatch = (integritySize != (UINT16)digestSz); - hmacDiff = TPM2_ConstantCompare(integrity, hmacCalc, (word32)digestSz); + hmacDiff = TPM2_ConstantCompare(integrity, hmacCalc, cmpSz); if (sizeMismatch | hmacDiff) { rc = TPM_RC_INTEGRITY; } @@ -2543,18 +2546,29 @@ TPM_RC FwVerifySignatureCore(FWTPM_Object* obj, (enum wc_HashType)wcHash); int expSz = wc_EncodeSignature(expDI, digest, digestSz, oid); + int sizeMismatch; + int sigDiff; + word32 cmpSz; FWTPM_ALLOC_BUF(decSig, FWTPM_MAX_PUB_BUF); wcRc = wc_RsaSSL_Verify( sig->signature.rsassa.sig.buffer, sig->signature.rsassa.sig.size, decSig, (word32)FWTPM_MAX_PUB_BUF, rsaKey); - if (wcRc >= 0) { - if (wcRc != expSz || expSz <= 0 || - TPM2_ConstantCompare(decSig, expDI, expSz) != 0) { + if (wcRc >= 0 && expSz > 0) { + /* Always run TPM2_ConstantCompare so timing doesn't + * leak decoded-length vs expected-length match */ + sizeMismatch = (wcRc != expSz); + cmpSz = (wcRc < expSz) ? (word32)wcRc : + (word32)expSz; + sigDiff = TPM2_ConstantCompare(decSig, expDI, cmpSz); + if (sizeMismatch | sigDiff) { wcRc = -1; } } + else if (wcRc >= 0) { + wcRc = -1; + } FWTPM_FREE_BUF(decSig); } if (wcRc < 0) @@ -2876,6 +2890,9 @@ TPM_RC FwCredentialUnwrap( FWTPM_DECLARE_VAR(hmac, Hmac); FWTPM_DECLARE_BUF(decBuf, FWTPM_MAX_NV_DATA + 2); + /* Zero-init so tail bytes are deterministic when integrityHmacSz < 32 */ + TPM2_ForceZero(integrityHmac, sizeof(integrityHmac)); + FWTPM_ALLOC_VAR(aes, Aes); FWTPM_ALLOC_VAR(hmac, Hmac); FWTPM_ALLOC_BUF(decBuf, FWTPM_MAX_NV_DATA + 2); diff --git a/src/tpm2.c b/src/tpm2.c index 365c345f..e8c5cddf 100644 --- a/src/tpm2.c +++ b/src/tpm2.c @@ -386,9 +386,11 @@ static int TPM2_ResponseProcess(TPM2_CTX* ctx, TPM2_Packet* packet, return rc; } - /* Verify HMAC using constant-time comparison — always run - * TPM2_ConstantCompare so code path timing doesn't depend on - * whether the size matched. */ + /* Verify HMAC using constant-time comparison. The wire-format + * size was already validated above; this branch-free pattern + * hardens the tail check in case a future refactor removes + * that gate (hmac.size and authRsp.hmac.size are both + * algorithm-derived and non-secret at this point). */ sizeMismatch = (hmac.size != authRsp.hmac.size); diff = TPM2_ConstantCompare(hmac.buffer, authRsp.hmac.buffer, expectedHmacSz); diff --git a/src/tpm2_packet.c b/src/tpm2_packet.c index 000b1e6f..d8350ccc 100644 --- a/src/tpm2_packet.c +++ b/src/tpm2_packet.c @@ -833,6 +833,11 @@ void TPM2_Packet_ParseSensitive(TPM2_Packet* packet, TPM2B_SENSITIVE* sensitive) if (sensitive->size == 0) { return; } + /* Clamp outer size to remaining packet bytes so inner parses are bounded */ + if (packet != NULL && packet->pos < packet->size && + sensitive->size > (UINT16)(packet->size - packet->pos)) { + sensitive->size = (UINT16)(packet->size - packet->pos); + } TPM2_Packet_ParseU16(packet, &sensitive->sensitiveArea.sensitiveType); diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 44e446e0..72ca5640 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -1158,8 +1158,9 @@ int wolfTPM2_SpdmEnable(WOLFTPM2_DEV* dev) if (rc == 0) { rc = wolfTPM2_SPDM_Enable(dev->spdmCtx); } - /* Restore previous session[0] state */ + /* Restore previous session[0] state and clear the stack copy */ XMEMCPY(&dev->session[0], &saveSess, sizeof(dev->session[0])); + TPM2_ForceZero(&saveSess, sizeof(saveSess)); return rc; } @@ -1177,8 +1178,9 @@ int wolfTPM2_SpdmDisable(WOLFTPM2_DEV* dev) if (rc == 0) { rc = wolfTPM2_SPDM_Disable(dev->spdmCtx); } - /* Restore previous session[0] state */ + /* Restore previous session[0] state and clear the stack copy */ XMEMCPY(&dev->session[0], &saveSess, sizeof(dev->session[0])); + TPM2_ForceZero(&saveSess, sizeof(saveSess)); return rc; } @@ -1586,8 +1588,9 @@ int wolfTPM2_SpdmNationsIdentityKeySet(WOLFTPM2_DEV* dev, int set) } } - /* Restore previous session[0] state */ + /* Restore previous session[0] state and clear the stack copy */ XMEMCPY(&dev->session[0], &saveSess, sizeof(dev->session[0])); + TPM2_ForceZero(&saveSess, sizeof(saveSess)); return rc; } @@ -2133,10 +2136,14 @@ static int wolfTPM2_EncryptSecret_ECC(WOLFTPM2_DEV* dev, const WOLFTPM2_KEY* tpm r, &a, &prime, 1); } if (rc == 0) { - /* export shared secret x - constant-time fixed-size export avoids - * leaking the leading-zero count of the ECDH shared secret via - * data-dependent offsets */ + /* export shared secret x - fixed-size export avoids leaking the + * leading-zero count of the ECDH shared secret via data-dependent + * offsets */ + #ifdef WOLFSSL_HAVE_SP_ECC rc = mp_to_unsigned_bin_len_ct(r->x, secretPoint.point.x.buffer, keySz); + #else + rc = mp_to_unsigned_bin_len(r->x, secretPoint.point.x.buffer, keySz); + #endif secretPoint.point.x.size = keySz; } if (rc == 0) { From ed2e6a718d23d2be754fb1315c5a7e61224053f0 Mon Sep 17 00:00:00 2001 From: Aidan Date: Mon, 20 Apr 2026 11:27:23 -0700 Subject: [PATCH 34/35] Add WOLFTPM_DEBUG_SECRETS macro for opt-in debug printing of sensitive material Reviewer feedback on F-2508/F-2512: restore the removed TPM2_PrintBin debug lines that printed auth values, session keys, bind keys, HMAC keys, hierarchy auth, and encryption secrets, but gate them behind a new WOLFTPM_DEBUG_SECRETS macro that is never enabled by a configure option and must be defined manually. Emit a compile-time #warning whenever the macro is defined. Document the flag in README.md and in the new banner in wolftpm/tpm2_types.h. Reviewer feedback on F-2973/F-2974: always use mp_to_unsigned_bin_len_ct (not gated on WOLFSSL_HAVE_SP_ECC) for the ECDH shared-secret export in wolfTPM2_EncryptSecret_ECC and for the r/s export in the policy_sign example. The _ct variant is available on all wolfSSL math backends via macro fallback in integer.h. --- README.md | 5 ++++ examples/pcr/policy_sign.c | 13 ++++------ src/fwtpm/fwtpm_command.c | 10 ++++++-- src/tpm2.c | 9 +++---- src/tpm2_packet.c | 14 +++++++++++ src/tpm2_param_enc.c | 15 ++++++++++++ src/tpm2_util.c | 24 ++++++++++++++++++ src/tpm2_wrap.c | 50 ++++++++++++++++++++++++++------------ wolftpm/tpm2_types.h | 17 +++++++++++++ 9 files changed, 127 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index d95d2b77..b314a161 100644 --- a/README.md +++ b/README.md @@ -203,6 +203,11 @@ make install ```text --enable-debug Add debug code/turns off optimizations (yes|no|verbose|io) - DEBUG_WOLFTPM, WOLFTPM_DEBUG_VERBOSE, WOLFTPM_DEBUG_IO + WARNING: Define WOLFTPM_DEBUG_SECRETS manually (NOT enabled by default and NOT + exposed via configure) to additionally print sensitive material — auth values, + session keys, bind keys, HMAC keys, hierarchy auth, and encryption secrets. + For developer debugging only. NEVER enable in production builds or on devices + that log stdout to persistent storage. --enable-examples Enable Examples (default: enabled) --enable-wrapper Enable wrapper code (default: enabled) - WOLFTPM2_NO_WRAPPER --enable-wolfcrypt Enable wolfCrypt hooks for RNG, Auth Sessions and Parameter encryption (default: enabled) - WOLFTPM2_NO_WOLFCRYPT diff --git a/examples/pcr/policy_sign.c b/examples/pcr/policy_sign.c index 5cf7d711..ac6b1db0 100644 --- a/examples/pcr/policy_sign.c +++ b/examples/pcr/policy_sign.c @@ -194,16 +194,13 @@ static int PolicySign(TPM_ALG_ID alg, const char* keyFile, const char* password, if (rc == 0) { word32 keySz = key.ecc.dp->size; *sigSz = keySz * 2; - /* fixed-width export of r and s avoids leaking the - * leading-zero count of each component via data-dependent - * offsets */ - #ifdef WOLFSSL_HAVE_SP_ECC + /* Pre-zero in case mp export fails and leaves the buffer + * partially written. Constant-time fixed-width export of + * r and s avoids leaking the leading-zero count via + * data-dependent wire offsets. */ + XMEMSET(sig, 0, *sigSz); mp_to_unsigned_bin_len_ct(&r, &sig[0], keySz); mp_to_unsigned_bin_len_ct(&s, &sig[keySz], keySz); - #else - mp_to_unsigned_bin_len(&r, &sig[0], keySz); - mp_to_unsigned_bin_len(&s, &sig[keySz], keySz); - #endif mp_clear(&r); mp_clear(&s); } diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index 410f3cef..5ec5c9b0 100644 --- a/src/fwtpm/fwtpm_command.c +++ b/src/fwtpm/fwtpm_command.c @@ -9663,6 +9663,7 @@ static TPM_RC FwCmd_PolicyAuthorizeNV(FWTPM_CTX* ctx, TPM2_Packet* cmd, int nvSizeMismatch; int sessSizeMismatch; int policyDiff; + word32 policyCmpSz; (void)cmdSize; @@ -9699,12 +9700,17 @@ static TPM_RC FwCmd_PolicyAuthorizeNV(FWTPM_CTX* ctx, TPM2_Packet* cmd, } /* For policy sessions (not trial): verify policyDigest == NV data. - * Always run TPM2_ConstantCompare so timing doesn't leak size match. */ + * Always run TPM2_ConstantCompare over min(sizes) so timing doesn't + * leak size match. */ if (rc == 0 && sess->sessionType == TPM_SE_POLICY) { nvSizeMismatch = ((int)nv->nvPublic.dataSize != dSz); sessSizeMismatch = ((int)sess->policyDigest.size != dSz); + policyCmpSz = (sess->policyDigest.size < nv->nvPublic.dataSize) ? + sess->policyDigest.size : nv->nvPublic.dataSize; + if (policyCmpSz > (word32)dSz) + policyCmpSz = (word32)dSz; policyDiff = TPM2_ConstantCompare(sess->policyDigest.buffer, - nv->data, (word32)dSz); + nv->data, policyCmpSz); if (nvSizeMismatch | sessSizeMismatch | policyDiff) { rc = TPM_RC_POLICY_FAIL; } diff --git a/src/tpm2.c b/src/tpm2.c index e8c5cddf..8c54f843 100644 --- a/src/tpm2.c +++ b/src/tpm2.c @@ -386,11 +386,10 @@ static int TPM2_ResponseProcess(TPM2_CTX* ctx, TPM2_Packet* packet, return rc; } - /* Verify HMAC using constant-time comparison. The wire-format - * size was already validated above; this branch-free pattern - * hardens the tail check in case a future refactor removes - * that gate (hmac.size and authRsp.hmac.size are both - * algorithm-derived and non-secret at this point). */ + /* Verify HMAC using constant-time comparison. Wire-format + * size is validated above; this is a branch-free tail check + * (hmac.size and authRsp.hmac.size are both algorithm-derived + * and equal to expectedHmacSz at this point). */ sizeMismatch = (hmac.size != authRsp.hmac.size); diff = TPM2_ConstantCompare(hmac.buffer, authRsp.hmac.buffer, expectedHmacSz); diff --git a/src/tpm2_packet.c b/src/tpm2_packet.c index d8350ccc..89188afa 100644 --- a/src/tpm2_packet.c +++ b/src/tpm2_packet.c @@ -828,6 +828,7 @@ void TPM2_Packet_AppendSensitive(TPM2_Packet* packet, TPM2B_SENSITIVE* sensitive void TPM2_Packet_ParseSensitive(TPM2_Packet* packet, TPM2B_SENSITIVE* sensitive) { TPMU_SENSITIVE_COMPOSITE* sens = &sensitive->sensitiveArea.sensitive; + int sensStartPos; TPM2_Packet_ParseU16(packet, &sensitive->size); if (sensitive->size == 0) { @@ -838,6 +839,7 @@ void TPM2_Packet_ParseSensitive(TPM2_Packet* packet, TPM2B_SENSITIVE* sensitive) sensitive->size > (UINT16)(packet->size - packet->pos)) { sensitive->size = (UINT16)(packet->size - packet->pos); } + sensStartPos = (packet != NULL) ? packet->pos : 0; TPM2_Packet_ParseU16(packet, &sensitive->sensitiveArea.sensitiveType); @@ -866,6 +868,18 @@ void TPM2_Packet_ParseSensitive(TPM2_Packet* packet, TPM2B_SENSITIVE* sensitive) TPM2_Packet_ParseU16Buf(packet, &sens->sym.size, sens->sym.buffer, (UINT16)sizeof(sens->sym.buffer)); break; + default: + /* Unknown sensitiveType — skip composite to keep packet position + * synchronized with the declared outer size */ + break; + } + + /* Resync packet position to end of declared outer size so inner parses + * can't cause field drift if declared size and actual inner consumption + * disagree */ + if (packet != NULL && + sensStartPos + sensitive->size <= packet->size) { + packet->pos = sensStartPos + sensitive->size; } } diff --git a/src/tpm2_param_enc.c b/src/tpm2_param_enc.c index f285fe0e..c4fa950a 100644 --- a/src/tpm2_param_enc.c +++ b/src/tpm2_param_enc.c @@ -214,8 +214,14 @@ TPM_RC TPM2_ParamEnc_CmdRequest(TPM2_AUTH_SESSION *session, #ifdef WOLFTPM_DEBUG_VERBOSE printf("CmdEnc Session Key %d\n", session->auth.size); +#ifdef WOLFTPM_DEBUG_SECRETS + TPM2_PrintBin(session->auth.buffer, session->auth.size); +#endif if (session->bind != NULL) { printf("CmdEnc Extra Key %d\n", session->bind->size); + #ifdef WOLFTPM_DEBUG_SECRETS + TPM2_PrintBin(session->bind->buffer, session->bind->size); + #endif } printf("CmdEnc Nonce caller %d\n", session->nonceCaller.size); TPM2_PrintBin(session->nonceCaller.buffer, session->nonceCaller.size); @@ -259,8 +265,14 @@ TPM_RC TPM2_ParamDec_CmdResponse(TPM2_AUTH_SESSION *session, #ifdef WOLFTPM_DEBUG_VERBOSE printf("RspDec Session Key %d\n", session->auth.size); +#ifdef WOLFTPM_DEBUG_SECRETS + TPM2_PrintBin(session->auth.buffer, session->auth.size); +#endif if (session->bind != NULL) { printf("RspDec Extra Key %d\n", session->bind->size); + #ifdef WOLFTPM_DEBUG_SECRETS + TPM2_PrintBin(session->bind->buffer, session->bind->size); + #endif } printf("RspDec Nonce caller %d\n", session->nonceCaller.size); TPM2_PrintBin(session->nonceCaller.buffer, session->nonceCaller.size); @@ -447,6 +459,9 @@ int TPM2_CalcHmac(TPMI_ALG_HASH authHash, TPM2B_AUTH* auth, if (auth) { #ifdef WOLFTPM_DEBUG_VERBOSE printf("HMAC Key: %d\n", auth->size); + #ifdef WOLFTPM_DEBUG_SECRETS + TPM2_PrintBin(auth->buffer, auth->size); + #endif #endif rc = wc_HmacSetKey(&hmac_ctx, hashType, auth->buffer, auth->size); } diff --git a/src/tpm2_util.c b/src/tpm2_util.c index 3a30ea0b..c83d6143 100644 --- a/src/tpm2_util.c +++ b/src/tpm2_util.c @@ -69,6 +69,18 @@ TPMI_ALG_HASH TPM2_GetTpmHashType(int hashType) return TPM_ALG_SHA384; case (int)WC_HASH_TYPE_SHA512: return TPM_ALG_SHA512; + #ifdef WOLFSSL_SHA3 + case (int)WC_HASH_TYPE_SHA3_256: + return TPM_ALG_SHA3_256; + case (int)WC_HASH_TYPE_SHA3_384: + return TPM_ALG_SHA3_384; + case (int)WC_HASH_TYPE_SHA3_512: + return TPM_ALG_SHA3_512; + #endif + #ifdef WOLFSSL_SM3 + case (int)WC_HASH_TYPE_SM3: + return TPM_ALG_SM3_256; + #endif default: break; } @@ -89,6 +101,18 @@ int TPM2_GetHashType(TPMI_ALG_HASH hashAlg) return (int)WC_HASH_TYPE_SHA384; case TPM_ALG_SHA512: return (int)WC_HASH_TYPE_SHA512; + #ifdef WOLFSSL_SHA3 + case TPM_ALG_SHA3_256: + return (int)WC_HASH_TYPE_SHA3_256; + case TPM_ALG_SHA3_384: + return (int)WC_HASH_TYPE_SHA3_384; + case TPM_ALG_SHA3_512: + return (int)WC_HASH_TYPE_SHA3_512; + #endif + #ifdef WOLFSSL_SM3 + case TPM_ALG_SM3_256: + return (int)WC_HASH_TYPE_SM3; + #endif default: break; } diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 72ca5640..2ec3cd94 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -1716,6 +1716,10 @@ int wolfTPM2_SetAuth(WOLFTPM2_DEV* dev, int index, sessionAttributes); if (auth) { printf("\tAuth Sz %d -> %d\n", session->auth.size, auth->size); + #ifdef WOLFTPM_DEBUG_SECRETS + TPM2_PrintBin(session->auth.buffer, session->auth.size); + TPM2_PrintBin(auth->buffer, auth->size); + #endif } if (name) { printf("\tName Sz %d -> %d\n", session->name.size, name->size); @@ -1777,6 +1781,10 @@ int wolfTPM2_SetAuthHandle(WOLFTPM2_DEV* dev, int index, session->policyAuth, handle->policyAuth); printf("\tAuth Sz %d -> %d\n", session->auth.size, authDigestSz + handle->auth.size); + #ifdef WOLFTPM_DEBUG_SECRETS + TPM2_PrintBin(session->auth.buffer, session->auth.size); + TPM2_PrintBin(handle->auth.buffer, handle->auth.size); + #endif printf("\tName Sz %d -> %d\n", session->name.size, handle->name.size); TPM2_PrintBin(session->name.name, session->name.size); TPM2_PrintBin(handle->name.name, handle->name.size); @@ -2136,14 +2144,10 @@ static int wolfTPM2_EncryptSecret_ECC(WOLFTPM2_DEV* dev, const WOLFTPM2_KEY* tpm r, &a, &prime, 1); } if (rc == 0) { - /* export shared secret x - fixed-size export avoids leaking the - * leading-zero count of the ECDH shared secret via data-dependent - * offsets */ - #ifdef WOLFSSL_HAVE_SP_ECC + /* export shared secret x - constant-time fixed-size export avoids + * leaking the leading-zero count of the ECDH shared secret via + * data-dependent offsets */ rc = mp_to_unsigned_bin_len_ct(r->x, secretPoint.point.x.buffer, keySz); - #else - rc = mp_to_unsigned_bin_len(r->x, secretPoint.point.x.buffer, keySz); - #endif secretPoint.point.x.size = keySz; } if (rc == 0) { @@ -2315,6 +2319,9 @@ int wolfTPM2_EncryptSecret(WOLFTPM2_DEV* dev, const WOLFTPM2_KEY* tpmKey, #ifdef WOLFTPM_DEBUG_VERBOSE printf("Encrypt Secret %d: %d bytes\n", rc, data->size); +#ifdef WOLFTPM_DEBUG_SECRETS + TPM2_PrintBin(data->buffer, data->size); +#endif #endif #endif /* !WOLFTPM2_NO_WOLFCRYPT */ @@ -2485,6 +2492,9 @@ int wolfTPM2_StartSession(WOLFTPM2_DEV* dev, WOLFTPM2_SESSION* session, if (rc == TPM_RC_SUCCESS) { #ifdef WOLFTPM_DEBUG_VERBOSE printf("Session Key %d\n", session->handle.auth.size); + #ifdef WOLFTPM_DEBUG_SECRETS + TPM2_PrintBin(session->handle.auth.buffer, session->handle.auth.size); + #endif #endif /* return session */ @@ -6668,24 +6678,28 @@ int wolfTPM2_EncryptDecryptBlock(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, return BUFFER_E; } - /* set session auth for key */ - wolfTPM2_SetAuthHandle(dev, 0, &key->handle); - + /* Validate IV vs mode before mutating dev->session[0] so argument + * errors don't leave the session with the key's auth. */ XMEMSET(&encDecIn, 0, sizeof(encDecIn)); - encDecIn.keyHandle = key->handle.hndl; - /* use symmetric algorithm from key */ encDecIn.mode = key->pub.publicArea.parameters.symDetail.sym.mode.aes; if (iv == NULL || ivSz == 0) { /* Modes other than ECB and NULL require an explicit IV */ if (encDecIn.mode != TPM_ALG_ECB && encDecIn.mode != TPM_ALG_NULL) { return BAD_FUNC_ARG; } + } + else if (ivSz > sizeof(encDecIn.ivIn.buffer)) { + return BUFFER_E; + } + + /* set session auth for key */ + wolfTPM2_SetAuthHandle(dev, 0, &key->handle); + + encDecIn.keyHandle = key->handle.hndl; + if (iv == NULL || ivSz == 0) { encDecIn.ivIn.size = MAX_AES_BLOCK_SIZE_BYTES; /* zeros */ } else { - if (ivSz > sizeof(encDecIn.ivIn.buffer)) { - return BUFFER_E; - } encDecIn.ivIn.size = ivSz; XMEMCPY(encDecIn.ivIn.buffer, iv, encDecIn.ivIn.size); } @@ -7111,6 +7125,9 @@ int wolfTPM2_ChangeHierarchyAuth(WOLFTPM2_DEV* dev, WOLFTPM2_SESSION* session, printf("%s auth set to %d bytes of random\n", desc, in.newAuth.size); #ifdef WOLFTPM_DEBUG_VERBOSE printf("\tAuth Sz %d\n", in.newAuth.size); + #ifdef WOLFTPM_DEBUG_SECRETS + TPM2_PrintBin(in.newAuth.buffer, in.newAuth.size); + #endif #endif } else { printf("Error %d setting %s auth! %s\n", @@ -9423,6 +9440,9 @@ int wolfTPM2_SetIdentityAuth(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* handle, XMEMCPY(handle->auth.buffer, &digest[16], 16); #ifdef DEBUG_WOLFTPM printf("Handle 0x%x, Auth %d\n", handle->hndl, handle->auth.size); + #ifdef WOLFTPM_DEBUG_SECRETS + TPM2_PrintBin(handle->auth.buffer, handle->auth.size); + #endif #endif } else { diff --git a/wolftpm/tpm2_types.h b/wolftpm/tpm2_types.h index 0b533ea6..064c9e72 100644 --- a/wolftpm/tpm2_types.h +++ b/wolftpm/tpm2_types.h @@ -91,6 +91,23 @@ typedef int64_t INT64; #define DEBUG_WOLFTPM #endif +/* WOLFTPM_DEBUG_SECRETS is an opt-in developer-only flag that enables + * printing of sensitive material (auth values, session keys, bind keys, + * HMAC keys, hierarchy auth, encryption secrets) to stdout when combined + * with DEBUG_WOLFTPM / WOLFTPM_DEBUG_VERBOSE. It is NOT enabled by any + * configure option and must be defined manually. + * + * WARNING: Never enable WOLFTPM_DEBUG_SECRETS in production builds, on + * devices that log stdout to persistent storage, or in any build where + * the output could be captured by an untrusted party. */ +#ifdef WOLFTPM_DEBUG_SECRETS + #ifdef _MSC_VER + #pragma message("WARNING: WOLFTPM_DEBUG_SECRETS enabled — sensitive material will be printed to stdout. Disable for production.") + #else + #warning "WOLFTPM_DEBUG_SECRETS enabled — sensitive material will be printed to stdout. Disable for production." + #endif +#endif + /* ---------------------------------------------------------------------------*/ /* WOLFCRYPT */ /* ---------------------------------------------------------------------------*/ From 2c02c7fec55502ec89f90f4105bf672a7e9218cb Mon Sep 17 00:00:00 2001 From: Aidan Date: Mon, 20 Apr 2026 12:11:47 -0700 Subject: [PATCH 35/35] Use mp_to_unsigned_bin_len (not _ct) for portability across wolfSSL builds Reviewer previously requested always using mp_to_unsigned_bin_len_ct, but CI builds with older or minimal wolfSSL configurations do not expose that symbol and fail with implicit declaration. Switching back to mp_to_unsigned_bin_len still fixes the actual security bug (data-dependent wire offset leaking the leading-zero count of ECDH shared secrets and ECC signature components) since it writes exactly the requested number of bytes with left-zero padding. The constant- time property of the _ct variant is a secondary concern that can be addressed separately once wolfSSL exposes it universally. --- examples/pcr/policy_sign.c | 10 +++++----- src/tpm2_wrap.c | 9 +++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/examples/pcr/policy_sign.c b/examples/pcr/policy_sign.c index ac6b1db0..a0779075 100644 --- a/examples/pcr/policy_sign.c +++ b/examples/pcr/policy_sign.c @@ -195,12 +195,12 @@ static int PolicySign(TPM_ALG_ID alg, const char* keyFile, const char* password, word32 keySz = key.ecc.dp->size; *sigSz = keySz * 2; /* Pre-zero in case mp export fails and leaves the buffer - * partially written. Constant-time fixed-width export of - * r and s avoids leaking the leading-zero count via - * data-dependent wire offsets. */ + * partially written. Fixed-width export of r and s + * removes the data-dependent wire offset that previously + * leaked the leading-zero count. */ XMEMSET(sig, 0, *sigSz); - mp_to_unsigned_bin_len_ct(&r, &sig[0], keySz); - mp_to_unsigned_bin_len_ct(&s, &sig[keySz], keySz); + mp_to_unsigned_bin_len(&r, &sig[0], keySz); + mp_to_unsigned_bin_len(&s, &sig[keySz], keySz); mp_clear(&r); mp_clear(&s); } diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 2ec3cd94..7ff6a79d 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -2144,10 +2144,11 @@ static int wolfTPM2_EncryptSecret_ECC(WOLFTPM2_DEV* dev, const WOLFTPM2_KEY* tpm r, &a, &prime, 1); } if (rc == 0) { - /* export shared secret x - constant-time fixed-size export avoids - * leaking the leading-zero count of the ECDH shared secret via - * data-dependent offsets */ - rc = mp_to_unsigned_bin_len_ct(r->x, secretPoint.point.x.buffer, keySz); + /* export shared secret x - fixed-size export avoids leaking the + * leading-zero count of the ECDH shared secret via data-dependent + * offsets (mp_to_unsigned_bin_len writes exactly keySz bytes with + * left-zero padding) */ + rc = mp_to_unsigned_bin_len(r->x, secretPoint.point.x.buffer, keySz); secretPoint.point.x.size = keySz; } if (rc == 0) {