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/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/pcr/policy_sign.c b/examples/pcr/policy_sign.c index 4f1d19ed..a0779075 100644 --- a/examples/pcr/policy_sign.c +++ b/examples/pcr/policy_sign.c @@ -192,14 +192,15 @@ 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; + /* Pre-zero in case mp export fails and leaves the buffer + * 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); - /* 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)]); + 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/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/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 */ diff --git a/src/fwtpm/fwtpm_command.c b/src/fwtpm/fwtpm_command.c index 3d2ef942..5ec5c9b0 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)); @@ -9005,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); @@ -9025,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; } } @@ -9064,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); @@ -9084,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; } } @@ -9436,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); @@ -9522,6 +9547,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 +9580,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)); @@ -9582,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; @@ -9622,6 +9660,10 @@ 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; + word32 policyCmpSz; (void)cmdSize; @@ -9657,12 +9699,19 @@ 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 over min(sizes) 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); + 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, policyCmpSz); + if (nvSizeMismatch | sessSizeMismatch | policyDiff) { rc = TPM_RC_POLICY_FAIL; } } @@ -12902,6 +12951,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 +12995,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); @@ -13072,6 +13128,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, @@ -13087,13 +13146,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); @@ -13120,9 +13184,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); diff --git a/src/fwtpm/fwtpm_crypto.c b/src/fwtpm/fwtpm_crypto.c index 1c3d2981..23c5933d 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,13 @@ TPM_RC FwImportVerifyAndDecrypt( } } if (rc == 0) { - if (integritySize != (UINT16)digestSz || - TPM2_ConstantCompare(integrity, hmacCalc, (word32)digestSz) != 0) { + /* 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, cmpSz); + if (sizeMismatch | hmacDiff) { rc = TPM_RC_INTEGRITY; } } @@ -2539,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) @@ -2866,10 +2884,15 @@ 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); + /* 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); @@ -2910,9 +2933,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; } } diff --git a/src/tpm2.c b/src/tpm2.c index be53db53..8c54f843 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,14 @@ 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. 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); + if (sizeMismatch | diff) { #ifdef DEBUG_WOLFTPM printf("Response HMAC verification failed!\n"); #endif 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; } } diff --git a/src/tpm2_packet.c b/src/tpm2_packet.c index ace5c1a7..89188afa 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) @@ -815,6 +825,64 @@ 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; + int sensStartPos; + + TPM2_Packet_ParseU16(packet, &sensitive->size); + 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); + } + sensStartPos = (packet != NULL) ? packet->pos : 0; + + 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; + 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; + } +} + void TPM2_Packet_AppendSensitiveCreate(TPM2_Packet* packet, TPM2B_SENSITIVE_CREATE* sensitive) { int tmpSz = 0; @@ -1014,6 +1082,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; @@ -1074,6 +1145,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); @@ -1111,6 +1184,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/src/tpm2_param_enc.c b/src/tpm2_param_enc.c index be1378f3..c4fa950a 100644 --- a/src/tpm2_param_enc.c +++ b/src/tpm2_param_enc.c @@ -214,10 +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); @@ -261,10 +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); @@ -451,7 +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); - TPM2_PrintBin(auth->buffer, 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 06c34053..c83d6143 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; @@ -65,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; } @@ -85,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 cbbd53f2..7ff6a79d 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -1147,25 +1147,41 @@ 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 and clear the stack copy */ + XMEMCPY(&dev->session[0], &saveSess, sizeof(dev->session[0])); + TPM2_ForceZero(&saveSess, sizeof(saveSess)); + 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 and clear the stack copy */ + XMEMCPY(&dev->session[0], &saveSess, sizeof(dev->session[0])); + TPM2_ForceZero(&saveSess, sizeof(saveSess)); + return rc; } int wolfTPM2_SpdmGetStatus(WOLFTPM2_DEV* dev, WOLFSPDM_NUVOTON_STATUS* status) @@ -1540,6 +1556,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 +1565,33 @@ 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 and clear the stack copy */ + XMEMCPY(&dev->session[0], &saveSess, sizeof(dev->session[0])); + TPM2_ForceZero(&saveSess, sizeof(saveSess)); + return rc; } @@ -1690,8 +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); @@ -1753,8 +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); @@ -2114,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 - 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 - 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) { @@ -2289,8 +2320,10 @@ 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 */ (void)label; @@ -2348,7 +2381,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) { @@ -2374,6 +2408,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; } @@ -2385,6 +2420,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; } } @@ -2395,6 +2431,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; } @@ -2456,7 +2493,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 */ @@ -3380,7 +3419,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 { @@ -3588,25 +3630,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) { @@ -6341,6 +6388,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, @@ -6630,23 +6679,32 @@ int wolfTPM2_EncryptDecryptBlock(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, return BUFFER_E; } + /* 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.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); - XMEMSET(&encDecIn, 0, sizeof(encDecIn)); encDecIn.keyHandle = key->handle.hndl; if (iv == NULL || ivSz == 0) { encDecIn.ivIn.size = MAX_AES_BLOCK_SIZE_BYTES; /* zeros */ } else { 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); @@ -7068,7 +7126,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); - TPM2_PrintBin(in.newAuth.buffer, 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", @@ -9381,7 +9441,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); - TPM2_PrintBin(handle->auth.buffer, handle->auth.size); + #ifdef WOLFTPM_DEBUG_SECRETS + TPM2_PrintBin(handle->auth.buffer, handle->auth.size); + #endif #endif } else { diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 380f47a9..3d6b9f11 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -1824,6 +1824,197 @@ 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_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_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; @@ -2395,6 +2586,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 +2635,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); @@ -2626,6 +2860,262 @@ 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"); +} + +#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 */ + +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"); +} + +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"); +} + +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"); +} + +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 @@ -2673,6 +3163,9 @@ int unit_tests(int argc, char *argv[]) test_wolfTPM2_ComputeName(); #endif test_TPM2_SchemeSerialize(); + test_TPM2_KeyedHashScheme_XorSerialize(); + test_TPM2_Signature_EcSchnorrSm2Serialize(); + test_TPM2_Sensitive_Roundtrip(); test_KeySealTemplate(); test_SealAndKeyedHash_Boundaries(); test_GetAlgId(); @@ -2688,6 +3181,14 @@ int unit_tests(int argc, char *argv[]) test_wolfTPM2_DecodeDer_DefaultAttribs(); #endif test_wolfTPM2_LoadPrivateKey_NullParent(); + test_wolfTPM2_EncryptDecryptBlock(); + #ifdef HAVE_ECC + test_wolfTPM2_ImportEccPrivateKeySeed_ErrorPaths(); + #endif + 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) && \ 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..55f97fad 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); @@ -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 /*! @@ -233,8 +234,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); 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 */ /* ---------------------------------------------------------------------------*/