diff --git a/src/fwtpm/fwtpm_crypto.c b/src/fwtpm/fwtpm_crypto.c index 1ba56fde..eef70431 100644 --- a/src/fwtpm/fwtpm_crypto.c +++ b/src/fwtpm/fwtpm_crypto.c @@ -2592,6 +2592,7 @@ TPM_RC FwDecryptSeed(FWTPM_CTX* ctx, encSeedBuf, encSeedSz, &sharedK); } if (rc == 0) { + /* K is 32-byte ML-KEM secret per FIPS 203 (Part 1 47.4 errata) */ int kdfRc = TPM2_KDFa_ex(nameAlg, sharedK.buffer, sharedK.size, kdfLabel, encSeedBuf, (UINT32)encSeedSz, @@ -2845,6 +2846,7 @@ TPM_RC FwEncryptSeed(FWTPM_CTX* ctx, rc = TPM_RC_SIZE; } if (rc == 0) { + /* K is 32-byte ML-KEM secret per FIPS 203 (Part 1 47.4 errata) */ int kdfRc = TPM2_KDFa_ex(nameAlg, sharedK.buffer, sharedK.size, kdfLabel, ciphertext->buffer, (UINT32)ciphertext->size, diff --git a/src/tpm2_cryptocb.c b/src/tpm2_cryptocb.c index 3047e531..27b26212 100644 --- a/src/tpm2_cryptocb.c +++ b/src/tpm2_cryptocb.c @@ -352,6 +352,7 @@ int wolfTPM2_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx) byte sigRS[MAX_ECC_BYTES*2]; byte *r = sigRS, *s = &sigRS[MAX_ECC_BYTES]; word32 rLen = MAX_ECC_BYTES, sLen = MAX_ECC_BYTES; + word32 keySz = 0; XMEMSET(&eccPub, 0, sizeof(eccPub)); XMEMSET(sigRS, 0, sizeof(sigRS)); @@ -359,13 +360,20 @@ int wolfTPM2_CryptoDevCb(int devId, wc_CryptoInfo* info, void* ctx) /* Decode ECDSA Header */ rc = wc_ecc_sig_to_rs(info->pk.eccverify.sig, info->pk.eccverify.siglen, r, &rLen, s, &sLen); + if (rc == 0) { + /* R/S larger than key size underflows the pad offset */ + keySz = wc_ecc_size(info->pk.eccverify.key); + if (keySz == 0 || keySz > MAX_ECC_BYTES || + rLen > keySz || sLen > keySz) { + rc = exit_rc; + } + } if (rc == 0) { /* load public key into TPM */ rc = wolfTPM2_EccKey_WolfToTpm(tlsCtx->dev, info->pk.eccverify.key, &eccPub); if (rc == 0) { /* combine R and S at key size (zero pad leading) */ - word32 keySz = wc_ecc_size(info->pk.eccverify.key); XMEMMOVE(&sigRS[keySz-rLen], r, rLen); XMEMSET(&sigRS[0], 0, keySz-rLen); XMEMMOVE(&sigRS[keySz + (keySz-sLen)], s, sLen); diff --git a/src/tpm2_packet.c b/src/tpm2_packet.c index e4809fb3..737ba81a 100644 --- a/src/tpm2_packet.c +++ b/src/tpm2_packet.c @@ -740,8 +740,12 @@ void TPM2_Packet_ParseAsymScheme(TPM2_Packet* packet, TPMT_ASYM_SCHEME* scheme) void TPM2_Packet_AppendEccPoint(TPM2_Packet* packet, TPMS_ECC_POINT* point) { + if (point->x.size > sizeof(point->x.buffer)) + point->x.size = sizeof(point->x.buffer); TPM2_Packet_AppendU16(packet, point->x.size); TPM2_Packet_AppendBytes(packet, point->x.buffer, point->x.size); + if (point->y.size > sizeof(point->y.buffer)) + point->y.size = sizeof(point->y.buffer); TPM2_Packet_AppendU16(packet, point->y.size); TPM2_Packet_AppendBytes(packet, point->y.buffer, point->y.size); } @@ -1157,6 +1161,8 @@ void TPM2_Packet_AppendPublicArea(TPM2_Packet* packet, TPMT_PUBLIC* publicArea) TPM2_Packet_AppendU16(packet, publicArea->type); TPM2_Packet_AppendU16(packet, publicArea->nameAlg); TPM2_Packet_AppendU32(packet, publicArea->objectAttributes); + if (publicArea->authPolicy.size > sizeof(publicArea->authPolicy.buffer)) + publicArea->authPolicy.size = sizeof(publicArea->authPolicy.buffer); TPM2_Packet_AppendU16(packet, publicArea->authPolicy.size); TPM2_Packet_AppendBytes(packet, publicArea->authPolicy.buffer, publicArea->authPolicy.size); @@ -1166,16 +1172,28 @@ void TPM2_Packet_AppendPublicArea(TPM2_Packet* packet, TPMT_PUBLIC* publicArea) switch (publicArea->type) { case TPM_ALG_KEYEDHASH: + if (publicArea->unique.keyedHash.size > + sizeof(publicArea->unique.keyedHash.buffer)) + publicArea->unique.keyedHash.size = + sizeof(publicArea->unique.keyedHash.buffer); TPM2_Packet_AppendU16(packet, publicArea->unique.keyedHash.size); TPM2_Packet_AppendBytes(packet, publicArea->unique.keyedHash.buffer, publicArea->unique.keyedHash.size); break; case TPM_ALG_SYMCIPHER: + if (publicArea->unique.sym.size > + sizeof(publicArea->unique.sym.buffer)) + publicArea->unique.sym.size = + sizeof(publicArea->unique.sym.buffer); TPM2_Packet_AppendU16(packet, publicArea->unique.sym.size); TPM2_Packet_AppendBytes(packet, publicArea->unique.sym.buffer, publicArea->unique.sym.size); break; case TPM_ALG_RSA: + if (publicArea->unique.rsa.size > + sizeof(publicArea->unique.rsa.buffer)) + publicArea->unique.rsa.size = + sizeof(publicArea->unique.rsa.buffer); TPM2_Packet_AppendU16(packet, publicArea->unique.rsa.size); TPM2_Packet_AppendBytes(packet, publicArea->unique.rsa.buffer, publicArea->unique.rsa.size); @@ -1186,6 +1204,10 @@ void TPM2_Packet_AppendPublicArea(TPM2_Packet* packet, TPMT_PUBLIC* publicArea) #ifdef WOLFTPM_MLDSA case TPM_ALG_MLDSA: case TPM_ALG_HASH_MLDSA: + if (publicArea->unique.mldsa.size > + sizeof(publicArea->unique.mldsa.buffer)) + publicArea->unique.mldsa.size = + sizeof(publicArea->unique.mldsa.buffer); TPM2_Packet_AppendU16(packet, publicArea->unique.mldsa.size); TPM2_Packet_AppendBytes(packet, publicArea->unique.mldsa.buffer, publicArea->unique.mldsa.size); @@ -1193,6 +1215,10 @@ void TPM2_Packet_AppendPublicArea(TPM2_Packet* packet, TPMT_PUBLIC* publicArea) #endif /* WOLFTPM_MLDSA */ #ifdef WOLFTPM_MLKEM case TPM_ALG_MLKEM: + if (publicArea->unique.mlkem.size > + sizeof(publicArea->unique.mlkem.buffer)) + publicArea->unique.mlkem.size = + sizeof(publicArea->unique.mlkem.buffer); TPM2_Packet_AppendU16(packet, publicArea->unique.mlkem.size); TPM2_Packet_AppendBytes(packet, publicArea->unique.mlkem.buffer, publicArea->unique.mlkem.size); diff --git a/src/tpm2_param_enc.c b/src/tpm2_param_enc.c index 8be69d77..be593b9e 100644 --- a/src/tpm2_param_enc.c +++ b/src/tpm2_param_enc.c @@ -57,13 +57,6 @@ /* --- Param Enc/Dec Functions -- */ /******************************************************************************/ -/* Maximum XOR mask size. RSA-2048 inSensitive parameter blobs on Create can - * exceed MAX_DIGEST_BUFFER (1024), so leave headroom to ~1250 bytes. Keep - * stack usage bounded by switching to heap under WOLFTPM_SMALL_STACK. */ -#ifndef TPM2_XOR_MASK_MAX -#define TPM2_XOR_MASK_MAX 1280 -#endif - /* XOR parameter encryption/decryption (shared by client and fwTPM). * XOR is symmetric so encrypt and decrypt are the same operation. * nonceA/nonceB order determines direction (caller/TPM or TPM/caller). */ @@ -389,6 +382,7 @@ int TPM2_CalcCpHash(TPMI_ALG_HASH authHash, TPM_CC cmdCode, rc = wc_HashFinal(&hash_ctx, hashType, hash->buffer); wc_HashFree(&hash_ctx, hashType); + TPM2_ForceZero(&hash_ctx, sizeof(hash_ctx)); } #ifdef WOLFTPM_DEBUG_VERBOSE @@ -435,6 +429,7 @@ int TPM2_CalcRpHash(TPMI_ALG_HASH authHash, rc = wc_HashFinal(&hash_ctx, hashType, hash->buffer); wc_HashFree(&hash_ctx, hashType); + TPM2_ForceZero(&hash_ctx, sizeof(hash_ctx)); } #ifdef WOLFTPM_DEBUG_VERBOSE diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 75483603..e31682a0 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -1923,6 +1923,41 @@ static void test_TPM2_ParamEnc_XOR_Vector(void) #endif } +static void test_TPM2_ParamEnc_XOR_MaskBoundary(void) +{ +#ifndef WOLFTPM2_NO_WOLFCRYPT + int rc; + TPMI_ALG_HASH authHash = TPM_ALG_SHA256; + TPM2B_AUTH sessKey; + TPM2B_NONCE nonceCaller, nonceTPM; + byte data[TPM2_XOR_MASK_MAX + 1]; + + sessKey.size = TPM_SHA256_DIGEST_SIZE; + XMEMSET(sessKey.buffer, 0xCC, sessKey.size); + nonceCaller.size = TPM_SHA256_DIGEST_SIZE; + XMEMSET(nonceCaller.buffer, 0x11, nonceCaller.size); + nonceTPM.size = TPM_SHA256_DIGEST_SIZE; + XMEMSET(nonceTPM.buffer, 0x22, nonceTPM.size); + XMEMSET(data, 0, sizeof(data)); + + /* exactly at capacity must succeed */ + rc = TPM2_ParamEnc_XOR(authHash, sessKey.buffer, sessKey.size, + nonceCaller.buffer, nonceCaller.size, + nonceTPM.buffer, nonceTPM.size, + data, TPM2_XOR_MASK_MAX); + AssertIntEQ(TPM_RC_SUCCESS, rc); + + /* one byte past capacity must be rejected */ + rc = TPM2_ParamEnc_XOR(authHash, sessKey.buffer, sessKey.size, + nonceCaller.buffer, nonceCaller.size, + nonceTPM.buffer, nonceTPM.size, + data, TPM2_XOR_MASK_MAX + 1); + AssertIntEQ(BUFFER_E, rc); + + printf("Test TPM Wrapper: %-40s Passed\n", "ParamEnc_XOR mask boundary:"); +#endif +} + static void test_TPM2_ParamEnc_AESCFB_Vector(void) { #if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFSSL_AES_CFB) @@ -1970,6 +2005,35 @@ static void test_TPM2_ParamEnc_AESCFB_Vector(void) #endif } +static void test_TPM2_ParamEnc_AESCFB_KeyBoundary(void) +{ +#if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFSSL_AES_CFB) + int rc; + TPMI_ALG_HASH authHash = TPM_ALG_SHA256; + TPM2B_AUTH sessKey; + TPM2B_NONCE nonceCaller, nonceTPM; + byte data[32]; + + sessKey.size = TPM_SHA256_DIGEST_SIZE; + XMEMSET(sessKey.buffer, 0xDD, sessKey.size); + nonceCaller.size = TPM_SHA256_DIGEST_SIZE; + XMEMSET(nonceCaller.buffer, 0x33, nonceCaller.size); + nonceTPM.size = TPM_SHA256_DIGEST_SIZE; + XMEMSET(nonceTPM.buffer, 0x44, nonceTPM.size); + XMEMSET(data, 0, sizeof(data)); + + /* keyBits above 256 (symKeySz > 32) must be rejected, not overflow symKey */ + rc = TPM2_ParamEnc_AESCFB(authHash, 512, + sessKey.buffer, sessKey.size, + nonceCaller.buffer, nonceCaller.size, + nonceTPM.buffer, nonceTPM.size, + data, sizeof(data), 1); + AssertIntEQ(BUFFER_E, rc); + + printf("Test TPM Wrapper: %-40s Passed\n", "ParamEnc_AESCFB key boundary:"); +#endif +} + /* Known-answer test cross-checking TPM2_ParamEnc_AESCFB against an * independent KDFa + AES-CFB reference built from wolfCrypt primitives. * The pure round-trip test above cannot detect mutations that affect @@ -2947,6 +3011,82 @@ static void test_TPM2_ResponseProcess_ParamSizeOverflow(void) printf("Test TPM Wrapper:\tResponseProcess paramSize overflow:\tPassed\n"); } +static void test_TPM2_ResponseProcess_HmacVerify(void) +{ +#if !defined(WOLFTPM2_NO_WOLFCRYPT) && !defined(NO_HMAC) + TPM2_CTX ctx; + TPM2_AUTH_SESSION session[1]; + TPM2_Packet packet; + CmdInfo_t info; + TPM2B_DIGEST rpHash; + TPM2B_AUTH expHmac; + TPM2B_NONCE nonceTPM; + byte buf[128]; + int rc, i; + TPM_CC cmdCode = 0x17F; + UINT16 hmacSz = 32, nonceSz = 32; + UINT32 paramSz = 4; + UINT32 pos, hmacOff, respSz; + byte attr = 0x01; + + XMEMSET(&ctx, 0, sizeof(ctx)); + XMEMSET(session, 0, sizeof(session)); + XMEMSET(&info, 0, sizeof(info)); + XMEMSET(buf, 0, sizeof(buf)); + + /* HMAC session with a known auth value and nonces */ + session[0].sessionHandle = HMAC_SESSION_FIRST; + session[0].authHash = TPM_ALG_SHA256; + session[0].auth.size = 4; + XMEMSET(session[0].auth.buffer, 0xA5, 4); + session[0].nonceCaller.size = 32; + XMEMSET(session[0].nonceCaller.buffer, 0x5C, 32); + session[0].nonceTPM.size = 32; + XMEMSET(session[0].nonceTPM.buffer, 0xC5, 32); + ctx.session = session; + info.authCnt = 1; + + /* header + paramSize(U32) + params + auth area (nonce, attr, hmac) */ + pos = TPM2_HEADER_SIZE; + buf[pos++] = 0; buf[pos++] = 0; buf[pos++] = 0; buf[pos++] = (byte)paramSz; + buf[pos++] = 0xDE; buf[pos++] = 0xAD; buf[pos++] = 0xBE; buf[pos++] = 0xEF; + buf[pos++] = (byte)(nonceSz >> 8); buf[pos++] = (byte)(nonceSz & 0xFF); + for (i = 0; i < nonceSz; i++) + buf[pos++] = 0x99; /* response nonceTPM */ + buf[pos++] = attr; /* sessionAttributes */ + buf[pos++] = (byte)(hmacSz >> 8); buf[pos++] = (byte)(hmacSz & 0xFF); + hmacOff = pos; + pos += hmacSz; + respSz = pos; + + /* expected HMAC uses the response nonce as nonceTPM */ + nonceTPM.size = nonceSz; + XMEMSET(nonceTPM.buffer, 0x99, nonceSz); + rc = TPM2_CalcRpHash(TPM_ALG_SHA256, cmdCode, &buf[TPM2_HEADER_SIZE + 4], + paramSz, &rpHash); + AssertIntEQ(rc, TPM_RC_SUCCESS); + XMEMSET(&expHmac, 0, sizeof(expHmac)); + rc = TPM2_CalcHmac(TPM_ALG_SHA256, &session[0].auth, &rpHash, + &nonceTPM, &session[0].nonceCaller, attr, &expHmac); + AssertIntEQ(rc, TPM_RC_SUCCESS); + XMEMCPY(&buf[hmacOff], expHmac.buffer, hmacSz); + + /* untampered response HMAC must verify and update nonceTPM */ + packet.buf = buf; packet.pos = 0; packet.size = (int)respSz; + rc = TPM2_ResponseProcess(&ctx, &packet, &info, cmdCode, respSz); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(session[0].nonceTPM.buffer[0], 0x99); + + /* flipping one HMAC byte must be detected */ + buf[hmacOff] ^= 0xFF; + packet.buf = buf; packet.pos = 0; packet.size = (int)respSz; + rc = TPM2_ResponseProcess(&ctx, &packet, &info, cmdCode, respSz); + AssertIntEQ(rc, TPM_RC_HMAC); + + printf("Test TPM Wrapper:\tResponseProcess HMAC verify:\tPassed\n"); +#endif +} + /* wolfTPM2_NVCreateAuthPolicy must derive nameAlg from authPolicySz so * the policy digest hash matches the index's nameAlg. Bug-mode hardcoded * SHA-256 nameAlg, which made SHA-384/SHA-512 policies unsatisfiable. @@ -3394,6 +3534,111 @@ static void test_TPM2_Signature_EcSchnorrSm2Serialize(void) "Signature ECSCHNORR/SM2 serialize:"); } +static void test_TPM2_Public_RsaEcc_Roundtrip(void) +{ +#if !defined(WOLFTPM2_NO_WOLFCRYPT) + int rc, sz; + byte buf[sizeof(TPM2B_PUBLIC)]; + TPM2B_PUBLIC pubIn, pubOut; + const byte uniqueBytes[8] = { + 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF, 0x11, 0x22 + }; + + /* RSA with AES-128-CFB symmetric wrapper (exercises the AES mode field) + * and an RSASSA-SHA256 scheme */ + XMEMSET(&pubIn, 0, sizeof(pubIn)); + pubIn.publicArea.type = TPM_ALG_RSA; + pubIn.publicArea.nameAlg = TPM_ALG_SHA256; + pubIn.publicArea.objectAttributes = TPMA_OBJECT_sign; + pubIn.publicArea.parameters.rsaDetail.symmetric.algorithm = TPM_ALG_AES; + pubIn.publicArea.parameters.rsaDetail.symmetric.keyBits.aes = 128; + pubIn.publicArea.parameters.rsaDetail.symmetric.mode.aes = TPM_ALG_CFB; + pubIn.publicArea.parameters.rsaDetail.scheme.scheme = TPM_ALG_RSASSA; + pubIn.publicArea.parameters.rsaDetail.scheme.details.rsassa.hashAlg = + TPM_ALG_SHA256; + pubIn.publicArea.parameters.rsaDetail.keyBits = 2048; + pubIn.publicArea.parameters.rsaDetail.exponent = 0x10001; + pubIn.publicArea.unique.rsa.size = sizeof(uniqueBytes); + XMEMCPY(pubIn.publicArea.unique.rsa.buffer, uniqueBytes, + sizeof(uniqueBytes)); + + XMEMSET(buf, 0, sizeof(buf)); + sz = 0; + rc = TPM2_AppendPublic(buf, (word32)sizeof(buf), &sz, &pubIn); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntGT(sz, 0); + + XMEMSET(&pubOut, 0, sizeof(pubOut)); + rc = TPM2_ParsePublic(&pubOut, buf, (word32)sz, &sz); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(pubOut.publicArea.type, TPM_ALG_RSA); + AssertIntEQ(pubOut.publicArea.nameAlg, TPM_ALG_SHA256); + AssertIntEQ(pubOut.publicArea.parameters.rsaDetail.symmetric.algorithm, + TPM_ALG_AES); + AssertIntEQ(pubOut.publicArea.parameters.rsaDetail.symmetric.keyBits.aes, + 128); + AssertIntEQ(pubOut.publicArea.parameters.rsaDetail.symmetric.mode.aes, + TPM_ALG_CFB); + AssertIntEQ(pubOut.publicArea.parameters.rsaDetail.scheme.scheme, + TPM_ALG_RSASSA); + AssertIntEQ( + pubOut.publicArea.parameters.rsaDetail.scheme.details.rsassa.hashAlg, + TPM_ALG_SHA256); + AssertIntEQ(pubOut.publicArea.parameters.rsaDetail.keyBits, 2048); + AssertIntEQ((int)pubOut.publicArea.parameters.rsaDetail.exponent, 0x10001); + AssertIntEQ(pubOut.publicArea.unique.rsa.size, sizeof(uniqueBytes)); + AssertIntEQ(XMEMCMP(pubOut.publicArea.unique.rsa.buffer, uniqueBytes, + sizeof(uniqueBytes)), 0); + + /* ECC P-256 with ECDSA-SHA256 scheme and NULL symmetric/kdf */ + XMEMSET(&pubIn, 0, sizeof(pubIn)); + pubIn.publicArea.type = TPM_ALG_ECC; + pubIn.publicArea.nameAlg = TPM_ALG_SHA256; + pubIn.publicArea.objectAttributes = TPMA_OBJECT_sign; + pubIn.publicArea.parameters.eccDetail.symmetric.algorithm = TPM_ALG_NULL; + pubIn.publicArea.parameters.eccDetail.scheme.scheme = TPM_ALG_ECDSA; + pubIn.publicArea.parameters.eccDetail.scheme.details.ecdsa.hashAlg = + TPM_ALG_SHA256; + pubIn.publicArea.parameters.eccDetail.curveID = TPM_ECC_NIST_P256; + pubIn.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL; + pubIn.publicArea.unique.ecc.x.size = sizeof(uniqueBytes); + XMEMCPY(pubIn.publicArea.unique.ecc.x.buffer, uniqueBytes, + sizeof(uniqueBytes)); + pubIn.publicArea.unique.ecc.y.size = sizeof(uniqueBytes); + XMEMCPY(pubIn.publicArea.unique.ecc.y.buffer, uniqueBytes, + sizeof(uniqueBytes)); + + XMEMSET(buf, 0, sizeof(buf)); + sz = 0; + rc = TPM2_AppendPublic(buf, (word32)sizeof(buf), &sz, &pubIn); + AssertIntEQ(rc, TPM_RC_SUCCESS); + + XMEMSET(&pubOut, 0, sizeof(pubOut)); + rc = TPM2_ParsePublic(&pubOut, buf, (word32)sz, &sz); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(pubOut.publicArea.type, TPM_ALG_ECC); + AssertIntEQ(pubOut.publicArea.parameters.eccDetail.symmetric.algorithm, + TPM_ALG_NULL); + AssertIntEQ(pubOut.publicArea.parameters.eccDetail.scheme.scheme, + TPM_ALG_ECDSA); + AssertIntEQ( + pubOut.publicArea.parameters.eccDetail.scheme.details.ecdsa.hashAlg, + TPM_ALG_SHA256); + AssertIntEQ(pubOut.publicArea.parameters.eccDetail.curveID, + TPM_ECC_NIST_P256); + AssertIntEQ(pubOut.publicArea.parameters.eccDetail.kdf.scheme, + TPM_ALG_NULL); + AssertIntEQ(pubOut.publicArea.unique.ecc.x.size, sizeof(uniqueBytes)); + AssertIntEQ(XMEMCMP(pubOut.publicArea.unique.ecc.x.buffer, uniqueBytes, + sizeof(uniqueBytes)), 0); + AssertIntEQ(pubOut.publicArea.unique.ecc.y.size, sizeof(uniqueBytes)); + AssertIntEQ(XMEMCMP(pubOut.publicArea.unique.ecc.y.buffer, uniqueBytes, + sizeof(uniqueBytes)), 0); + + printf("Test TPM Wrapper: %-40s Passed\n", "Public RSA/ECC roundtrip:"); +#endif +} + #ifdef WOLFTPM_PQC /* Round-trip the v1.85 PQC arms of TPMT_SIGNATURE through the packet * marshaler. Pure ML-DSA (Table 217 mldsa arm) is bare TPM2B + bytes — @@ -3873,6 +4118,100 @@ static void test_TPM2_AppendSensitive_Clamp(void) printf("Test TPM2: %-40s Passed\n", "AppendSensitive clamp:"); } +static void test_TPM2_AppendPublic_Clamp(void) +{ + TPM2_Packet packet; + byte buf[sizeof(TPM2B_PUBLIC)]; + TPM2B_PUBLIC pub; + word16 policyCap, rsaCap, eccCap, khCap, symCap; +#ifdef WOLFTPM_MLDSA + word16 mldsaCap; +#endif +#ifdef WOLFTPM_MLKEM + word16 mlkemCap; +#endif + + policyCap = (word16)sizeof(pub.publicArea.authPolicy.buffer); + rsaCap = (word16)sizeof(pub.publicArea.unique.rsa.buffer); + eccCap = (word16)sizeof(pub.publicArea.unique.ecc.x.buffer); + khCap = (word16)sizeof(pub.publicArea.unique.keyedHash.buffer); + symCap = (word16)sizeof(pub.publicArea.unique.sym.buffer); + + XMEMSET(&pub, 0, sizeof(pub)); + pub.publicArea.type = TPM_ALG_RSA; + pub.publicArea.nameAlg = TPM_ALG_SHA256; + pub.publicArea.authPolicy.size = policyCap + 100; + pub.publicArea.unique.rsa.size = rsaCap + 100; + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = buf; + packet.size = sizeof(buf); + TPM2_Packet_AppendPublic(&packet, &pub); + AssertIntEQ(pub.publicArea.authPolicy.size, policyCap); + AssertIntEQ(pub.publicArea.unique.rsa.size, rsaCap); + + /* ECC point x/y sizes must clamp on append too */ + XMEMSET(&pub, 0, sizeof(pub)); + pub.publicArea.type = TPM_ALG_ECC; + pub.publicArea.nameAlg = TPM_ALG_SHA256; + pub.publicArea.unique.ecc.x.size = eccCap + 100; + pub.publicArea.unique.ecc.y.size = eccCap + 100; + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = buf; + packet.size = sizeof(buf); + TPM2_Packet_AppendPublic(&packet, &pub); + AssertIntEQ(pub.publicArea.unique.ecc.x.size, eccCap); + AssertIntEQ(pub.publicArea.unique.ecc.y.size, eccCap); + + /* keyedHash unique size must clamp */ + XMEMSET(&pub, 0, sizeof(pub)); + pub.publicArea.type = TPM_ALG_KEYEDHASH; + pub.publicArea.nameAlg = TPM_ALG_SHA256; + pub.publicArea.unique.keyedHash.size = khCap + 100; + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = buf; + packet.size = sizeof(buf); + TPM2_Packet_AppendPublic(&packet, &pub); + AssertIntEQ(pub.publicArea.unique.keyedHash.size, khCap); + + /* symcipher unique size must clamp */ + XMEMSET(&pub, 0, sizeof(pub)); + pub.publicArea.type = TPM_ALG_SYMCIPHER; + pub.publicArea.nameAlg = TPM_ALG_SHA256; + pub.publicArea.unique.sym.size = symCap + 100; + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = buf; + packet.size = sizeof(buf); + TPM2_Packet_AppendPublic(&packet, &pub); + AssertIntEQ(pub.publicArea.unique.sym.size, symCap); + +#ifdef WOLFTPM_MLDSA + mldsaCap = (word16)sizeof(pub.publicArea.unique.mldsa.buffer); + XMEMSET(&pub, 0, sizeof(pub)); + pub.publicArea.type = TPM_ALG_MLDSA; + pub.publicArea.nameAlg = TPM_ALG_SHA256; + pub.publicArea.unique.mldsa.size = mldsaCap + 100; + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = buf; + packet.size = sizeof(buf); + TPM2_Packet_AppendPublic(&packet, &pub); + AssertIntEQ(pub.publicArea.unique.mldsa.size, mldsaCap); +#endif +#ifdef WOLFTPM_MLKEM + mlkemCap = (word16)sizeof(pub.publicArea.unique.mlkem.buffer); + XMEMSET(&pub, 0, sizeof(pub)); + pub.publicArea.type = TPM_ALG_MLKEM; + pub.publicArea.nameAlg = TPM_ALG_SHA256; + pub.publicArea.unique.mlkem.size = mlkemCap + 100; + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = buf; + packet.size = sizeof(buf); + TPM2_Packet_AppendPublic(&packet, &pub); + AssertIntEQ(pub.publicArea.unique.mlkem.size, mlkemCap); +#endif + + printf("Test TPM2: %-40s Passed\n", "AppendPublic clamp:"); +} + /* Roundtrip a maximum-size inner payload (size == buffer capacity) so the * parse-side ParseU16Buf clamp branch is exercised with valid data. */ static void test_TPM2_Sensitive_MaxRoundtrip(void) @@ -4028,6 +4367,149 @@ static void test_wolfTPM2_CSR(void) #endif } +static void test_wolfTPM2_CryptoDevCb_EccVerifyOversizedRS(void) +{ +#if !defined(WOLFTPM2_NO_WRAPPER) && defined(WOLFTPM_CRYPTOCB) && \ + !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) && \ + defined(HAVE_ECC_VERIFY) && !defined(WC_NO_RNG) && (MAX_ECC_BYTES > 32) + int rc; + int i; + int c, rLen, sLen; + int verifyRes = 0; + WOLFTPM2_DEV dev; + TpmCryptoDevCtx tpmCtx; + wc_CryptoInfo info; + ecc_key key; + byte digest[32]; + byte sig[128]; + word32 sigSz; + + XMEMSET(digest, 0x33, sizeof(digest)); + XMEMSET(&tpmCtx, 0, sizeof(tpmCtx)); + + rc = wolfTPM2_Init(&dev, TPM2_IoCb, NULL); + AssertIntEQ(rc, 0); + tpmCtx.dev = &dev; + + rc = wc_ecc_init(&key); + AssertIntEQ(rc, 0); + rc = wc_ecc_make_key_ex(wolfTPM2_GetRng(&dev), 32, &key, ECC_SECP256R1); + AssertIntEQ(rc, 0); + + /* c==0 drives the oversized-R guard, c==1 the oversized-S guard; both + * exceed the P-256 key size and must fall back before the TPM key load */ + for (c = 0; c < 2; c++) { + rLen = (c == 0) ? 40 : 32; + sLen = (c == 0) ? 32 : 40; + + sigSz = 0; + sig[sigSz++] = 0x30; + sig[sigSz++] = (byte)(2 + rLen + 2 + sLen); + sig[sigSz++] = 0x02; + sig[sigSz++] = (byte)rLen; + for (i = 0; i < rLen; i++) + sig[sigSz++] = 0x11; + sig[sigSz++] = 0x02; + sig[sigSz++] = (byte)sLen; + for (i = 0; i < sLen; i++) + sig[sigSz++] = 0x22; + + XMEMSET(&info, 0, sizeof(info)); + info.algo_type = WC_ALGO_TYPE_PK; + info.pk.type = WC_PK_TYPE_ECDSA_VERIFY; + info.pk.eccverify.sig = sig; + info.pk.eccverify.siglen = sigSz; + info.pk.eccverify.hash = digest; + info.pk.eccverify.hashlen = (word32)sizeof(digest); + info.pk.eccverify.res = &verifyRes; + info.pk.eccverify.key = &key; + + rc = wolfTPM2_CryptoDevCb(INVALID_DEVID, &info, &tpmCtx); + AssertIntEQ(rc, CRYPTOCB_UNAVAILABLE); + } + + wc_ecc_free(&key); + wolfTPM2_Cleanup(&dev); + + printf("Test TPM Wrapper: %-40s Passed\n", "CryptoDevCb ECC oversized R/S:"); +#endif +} + +static void test_TPM2_ASN_DecodeX509Cert_Errors(void) +{ +#if !defined(WOLFTPM2_NO_WRAPPER) && !defined(WOLFTPM2_NO_ASN) + int rc; + DecodedX509 x509; + byte garbage[16]; + byte trunc[4]; + + XMEMSET(&x509, 0, sizeof(x509)); + XMEMSET(garbage, 0xFF, sizeof(garbage)); + + /* NULL arguments must be rejected, not dereferenced */ + rc = TPM2_ASN_DecodeX509Cert(NULL, 0, &x509); + AssertIntNE(rc, 0); + rc = TPM2_ASN_DecodeX509Cert(garbage, (int)sizeof(garbage), NULL); + AssertIntNE(rc, 0); + + /* malformed input must not report success */ + rc = TPM2_ASN_DecodeX509Cert(garbage, (int)sizeof(garbage), &x509); + AssertIntNE(rc, 0); + + /* outer SEQUENCE whose length runs past the buffer (TPM_RC_INSUFFICIENT) */ + trunc[0] = 0x30; trunc[1] = 0x20; trunc[2] = 0x00; trunc[3] = 0x00; + rc = TPM2_ASN_DecodeX509Cert(trunc, (int)sizeof(trunc), &x509); + AssertIntNE(rc, 0); + + printf("Test TPM Wrapper: %-40s Passed\n", "ASN DecodeX509Cert errors:"); +#endif +} + +#if !defined(WOLFTPM2_NO_WRAPPER) && !defined(WOLFTPM2_NO_ASN) +#include +#endif +static void test_TPM2_ASN_DecodeX509Cert_Valid(void) +{ +#if !defined(WOLFTPM2_NO_WRAPPER) && !defined(WOLFTPM2_NO_ASN) + int rc; + DecodedX509 x509; + + /* a well-formed DER certificate must decode and populate the fields */ + XMEMSET(&x509, 0, sizeof(x509)); + rc = TPM2_ASN_DecodeX509Cert((uint8_t*)kSTSAFEIntCa20, + (int)sizeof(kSTSAFEIntCa20), &x509); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntGT(x509.certSz, 0); + AssertNotNull(x509.publicKey); + AssertIntGT(x509.pubKeySz, 0); + AssertNotNull(x509.signature); + AssertIntGT(x509.sigSz, 0); + + printf("Test TPM Wrapper: %-40s Passed\n", "ASN DecodeX509Cert valid:"); +#endif +} + +static void test_TPM2_ASN_DecodeTag_Errors(void) +{ +#if !defined(WOLFTPM2_NO_WRAPPER) && !defined(WOLFTPM2_NO_ASN) + int rc, idx, tagLen; + byte buf[4]; + + buf[0] = 0x30; buf[1] = 0x02; buf[2] = 0x00; buf[3] = 0x00; + + idx = 0; + rc = TPM2_ASN_DecodeTag(buf, (int)sizeof(buf), &idx, &tagLen, 0x30); + AssertIntEQ(rc, 0); + + /* wrong expected tag must be reported, not accepted as success */ + idx = 0; + rc = TPM2_ASN_DecodeTag(buf, (int)sizeof(buf), &idx, &tagLen, 0x02); + AssertIntNE(rc, 0); + + printf("Test TPM Wrapper: %-40s Passed\n", "ASN DecodeTag tag mismatch:"); +#endif +} + #if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(HAVE_ECC) && \ !defined(WOLFTPM2_NO_ASN) #define FLAGS_USE_WOLFCRYPT (1 << 0) @@ -6116,7 +6598,9 @@ int unit_tests(int argc, char *argv[]) test_TPM2_ResponseHmacVerification(); test_TPM2_CalcHmac(); test_TPM2_ParamEnc_XOR_Vector(); + test_TPM2_ParamEnc_XOR_MaskBoundary(); test_TPM2_ParamEnc_AESCFB_Vector(); + test_TPM2_ParamEnc_AESCFB_KeyBoundary(); test_TPM2_ParamEnc_AESCFB_KAT(); test_TPM2_ParamDec_XOR_Roundtrip(); test_TPM2_ParamDec_AESCFB_Roundtrip(); @@ -6143,11 +6627,13 @@ int unit_tests(int argc, char *argv[]) test_TPM2_Packet_RetryRestore(); #endif test_TPM2_ResponseProcess_ParamSizeOverflow(); + test_TPM2_ResponseProcess_HmacVerify(); test_wolfTPM2_NVCreateAuthPolicy_NameAlg(); test_wolfTPM2_GetKeyTemplate_KeyedHash_Scheme(); test_wolfTPM2_LoadEccPublicKey_Ex(); test_TPM2_KeyedHashScheme_XorSerialize(); test_TPM2_Signature_EcSchnorrSm2Serialize(); + test_TPM2_Public_RsaEcc_Roundtrip(); #ifdef WOLFTPM_PQC test_TPM2_Signature_PQC_Serialize(); test_TPM2_Public_PQC_Roundtrip(); @@ -6156,12 +6642,17 @@ int unit_tests(int argc, char *argv[]) test_TPM2_TIS_ValidateRspSz(); test_TPM2_ParsePublic_EmptyClears(); test_TPM2_AppendSensitive_Clamp(); + test_TPM2_AppendPublic_Clamp(); test_TPM2_Sensitive_MaxRoundtrip(); test_KeySealTemplate(); test_SealAndKeyedHash_Boundaries(); test_GetAlgId(); test_wolfTPM2_ReadPublicKey(); test_wolfTPM2_CSR(); + test_wolfTPM2_CryptoDevCb_EccVerifyOversizedRS(); + test_TPM2_ASN_DecodeX509Cert_Errors(); + test_TPM2_ASN_DecodeX509Cert_Valid(); + test_TPM2_ASN_DecodeTag_Errors(); #if !defined(WOLFTPM2_NO_WOLFCRYPT) && defined(WOLFTPM2_PEM_DECODE) && \ !defined(NO_RSA) test_wolfTPM_ImportPublicKey(); diff --git a/wolftpm/tpm2_param_enc.h b/wolftpm/tpm2_param_enc.h index 6e0e303f..331df6cc 100644 --- a/wolftpm/tpm2_param_enc.h +++ b/wolftpm/tpm2_param_enc.h @@ -30,6 +30,12 @@ extern "C" { #endif +/* Maximum XOR mask size. RSA-2048 inSensitive parameter blobs on Create can + * exceed MAX_DIGEST_BUFFER (1024), so leave headroom to ~1250 bytes. */ +#ifndef TPM2_XOR_MASK_MAX +#define TPM2_XOR_MASK_MAX 1280 +#endif + /* XOR parameter encryption/decryption (raw pointer interface). * XOR is symmetric so encrypt and decrypt are the same operation. */ WOLFTPM_TEST_API int TPM2_ParamEnc_XOR( @@ -54,7 +60,7 @@ WOLFTPM_TEST_API int TPM2_CalcHmac(TPMI_ALG_HASH authHash, TPM2B_AUTH* auth, const TPM2B_DIGEST* hash, const TPM2B_NONCE* nonceNew, const TPM2B_NONCE* nonceOld, TPMA_SESSION sessionAttributes, TPM2B_AUTH* hmac); -WOLFTPM_LOCAL int TPM2_CalcRpHash(TPMI_ALG_HASH authHash, +WOLFTPM_TEST_API int TPM2_CalcRpHash(TPMI_ALG_HASH authHash, TPM_CC cmdCode, BYTE* param, UINT32 paramSz, TPM2B_DIGEST* hash); WOLFTPM_LOCAL int TPM2_CalcCpHash(TPMI_ALG_HASH authHash, TPM_CC cmdCode, TPM2B_NAME* name1, TPM2B_NAME* name2, TPM2B_NAME* name3,