diff --git a/src/tpm2.c b/src/tpm2.c index 622780447..de1e38862 100644 --- a/src/tpm2.c +++ b/src/tpm2.c @@ -4086,6 +4086,23 @@ TPM_RC TPM2_VerifyDigestSignature(VerifyDigestSignature_In* in, } #endif /* WOLFTPM_MLDSA_VERIFY */ +#if defined(WOLFTPM_MLKEM_ENCAP) || defined(WOLFTPM_MLKEM_DECAP) +/* Limit parsing to the declared response parameter area, so the + * authorization area cannot satisfy a parameter length. */ +static TPM_RC TPM2_LimitToParamArea(TPM2_Packet* packet) +{ + UINT32 paramSz = 0; + + TPM2_Packet_ParseU32(packet, ¶mSz); + if (packet->overflow || + paramSz > (UINT32)(packet->size - packet->pos)) { + return TPM_RC_SIZE; + } + packet->size = packet->pos + (int)paramSz; + return TPM_RC_SUCCESS; +} +#endif /* WOLFTPM_MLKEM_ENCAP || WOLFTPM_MLKEM_DECAP */ + #ifdef WOLFTPM_MLKEM_ENCAP TPM_RC TPM2_Encapsulate(Encapsulate_In* in, Encapsulate_Out* out) { @@ -4120,42 +4137,25 @@ TPM_RC TPM2_Encapsulate(Encapsulate_In* in, Encapsulate_Out* out) /* send command */ rc = TPM2_SendCommandAuth(ctx, &packet, &info); if (rc == TPM_RC_SUCCESS) { - UINT32 paramSz = 0; - UINT16 wireSize; - UINT16 wireSize2; - if (st == TPM_ST_SESSIONS) { - TPM2_Packet_ParseU32(&packet, ¶mSz); + rc = TPM2_LimitToParamArea(&packet); } - - /* Parse sharedSecret with bounds checking */ - TPM2_Packet_ParseU16(&packet, &wireSize); - out->sharedSecret.size = wireSize; - if (out->sharedSecret.size > - (UINT16)sizeof(out->sharedSecret.buffer)) { - out->sharedSecret.size = - (UINT16)sizeof(out->sharedSecret.buffer); + if (rc == TPM_RC_SUCCESS) { + rc = TPM2_Packet_ParseU16BufStrict(&packet, + &out->sharedSecret.size, out->sharedSecret.buffer, + (UINT16)sizeof(out->sharedSecret.buffer)); } - TPM2_Packet_ParseBytes(&packet, out->sharedSecret.buffer, - out->sharedSecret.size); - if (wireSize > out->sharedSecret.size) { - TPM2_Packet_ParseBytes(&packet, NULL, - wireSize - out->sharedSecret.size); + if (rc == TPM_RC_SUCCESS) { + rc = TPM2_Packet_ParseU16BufStrict(&packet, + &out->ciphertext.size, out->ciphertext.buffer, + (UINT16)sizeof(out->ciphertext.buffer)); } - - /* Parse ciphertext with bounds checking */ - TPM2_Packet_ParseU16(&packet, &wireSize2); - out->ciphertext.size = wireSize2; - if (out->ciphertext.size > - (UINT16)sizeof(out->ciphertext.buffer)) { - out->ciphertext.size = - (UINT16)sizeof(out->ciphertext.buffer); + if (rc == TPM_RC_SUCCESS && packet.pos != packet.size) { + rc = TPM_RC_SIZE; } - TPM2_Packet_ParseBytes(&packet, out->ciphertext.buffer, - out->ciphertext.size); - if (wireSize2 > out->ciphertext.size) { - TPM2_Packet_ParseBytes(&packet, NULL, - wireSize2 - out->ciphertext.size); + if (rc != TPM_RC_SUCCESS) { + TPM2_ForceZero(&out->sharedSecret, sizeof(out->sharedSecret)); + XMEMSET(&out->ciphertext, 0, sizeof(out->ciphertext)); } } @@ -4196,24 +4196,17 @@ TPM_RC TPM2_Decapsulate(Decapsulate_In* in, Decapsulate_Out* out) /* send command */ rc = TPM2_SendCommandAuth(ctx, &packet, &info); if (rc == TPM_RC_SUCCESS) { - UINT32 paramSz = 0; - UINT16 wireSize; - - TPM2_Packet_ParseU32(&packet, ¶mSz); - - /* Parse sharedSecret with bounds checking */ - TPM2_Packet_ParseU16(&packet, &wireSize); - out->sharedSecret.size = wireSize; - if (out->sharedSecret.size > - (UINT16)sizeof(out->sharedSecret.buffer)) { - out->sharedSecret.size = - (UINT16)sizeof(out->sharedSecret.buffer); + rc = TPM2_LimitToParamArea(&packet); + if (rc == TPM_RC_SUCCESS) { + rc = TPM2_Packet_ParseU16BufStrict(&packet, + &out->sharedSecret.size, out->sharedSecret.buffer, + (UINT16)sizeof(out->sharedSecret.buffer)); } - TPM2_Packet_ParseBytes(&packet, out->sharedSecret.buffer, - out->sharedSecret.size); - if (wireSize > out->sharedSecret.size) { - TPM2_Packet_ParseBytes(&packet, NULL, - wireSize - out->sharedSecret.size); + if (rc == TPM_RC_SUCCESS && packet.pos != packet.size) { + rc = TPM_RC_SIZE; + } + if (rc != TPM_RC_SUCCESS) { + TPM2_ForceZero(&out->sharedSecret, sizeof(out->sharedSecret)); } } diff --git a/src/tpm2_packet.c b/src/tpm2_packet.c index 6f08a484a..ae3bdce86 100644 --- a/src/tpm2_packet.c +++ b/src/tpm2_packet.c @@ -348,6 +348,28 @@ void TPM2_Packet_ParseU16Buf(TPM2_Packet* packet, UINT16* size, byte* buf, } } +/* Parse a UINT16-prefixed buffer, failing with TPM_RC_SIZE instead of + * truncating when the size exceeds maxBufSz or the bytes left in the packet. */ +int TPM2_Packet_ParseU16BufStrict(TPM2_Packet* packet, UINT16* size, + byte* buf, UINT16 maxBufSz) +{ + UINT16 wireSize = 0; + + if (packet == NULL || size == NULL || buf == NULL) + return BAD_FUNC_ARG; + + *size = 0; + TPM2_Packet_ParseU16(packet, &wireSize); + if (packet->overflow || wireSize > maxBufSz || + (int)wireSize > packet->size - packet->pos) { + packet->overflow = 1; + return TPM_RC_SIZE; + } + TPM2_Packet_ParseBytes(packet, buf, wireSize); + *size = wireSize; + return TPM_RC_SUCCESS; +} + void TPM2_Packet_MarkU16(TPM2_Packet* packet, int* markSz) { if (packet) { diff --git a/src/tpm2_wrap.c b/src/tpm2_wrap.c index 54714a2dd..4447ae1c3 100644 --- a/src/tpm2_wrap.c +++ b/src/tpm2_wrap.c @@ -6727,6 +6727,23 @@ int wolfTPM2_VerifyDigestSignature(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, } #endif /* WOLFTPM_MLDSA_VERIFY */ +#if defined(WOLFTPM_MLKEM_ENCAP) || defined(WOLFTPM_MLKEM_DECAP) +#define WOLFTPM2_MLKEM_SS_SZ 32 +#endif + +#ifdef WOLFTPM_MLKEM_ENCAP +/* FIPS 203 ciphertext size for the parameter set, or 0 if unknown */ +static int MlKemCiphertextSize(TPMI_MLKEM_PARAMETER_SET parameterSet) +{ + switch (parameterSet) { + case TPM_MLKEM_512: return 768; + case TPM_MLKEM_768: return 1088; + case TPM_MLKEM_1024: return 1568; + default: return 0; + } +} +#endif /* WOLFTPM_MLKEM_ENCAP */ + #ifdef WOLFTPM_MLKEM_ENCAP int wolfTPM2_Encapsulate(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, byte* ciphertext, int* ciphertextSz, byte* sharedSecret, int* sharedSecretSz) @@ -6745,6 +6762,12 @@ int wolfTPM2_Encapsulate(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, XMEMSET(&encapsulateOut, 0, sizeof(encapsulateOut)); rc = TPM2_Encapsulate(&encapsulateIn, &encapsulateOut); + if (rc == TPM_RC_SUCCESS && key->pub.publicArea.type == TPM_ALG_MLKEM && + ((int)encapsulateOut.ciphertext.size != MlKemCiphertextSize( + key->pub.publicArea.parameters.mlkemDetail.parameterSet) || + encapsulateOut.sharedSecret.size != WOLFTPM2_MLKEM_SS_SZ)) { + rc = TPM_RC_SIZE; + } if (rc == TPM_RC_SUCCESS) { if (*ciphertextSz >= (int)encapsulateOut.ciphertext.size) { XMEMCPY(ciphertext, encapsulateOut.ciphertext.buffer, encapsulateOut.ciphertext.size); @@ -6799,6 +6822,10 @@ int wolfTPM2_Decapsulate(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, XMEMSET(&decapsulateOut, 0, sizeof(decapsulateOut)); rc = TPM2_Decapsulate(&decapsulateIn, &decapsulateOut); + if (rc == TPM_RC_SUCCESS && key->pub.publicArea.type == TPM_ALG_MLKEM && + decapsulateOut.sharedSecret.size != WOLFTPM2_MLKEM_SS_SZ) { + rc = TPM_RC_SIZE; + } if (rc == TPM_RC_SUCCESS) { if (*sharedSecretSz >= (int)decapsulateOut.sharedSecret.size) { XMEMCPY(sharedSecret, decapsulateOut.sharedSecret.buffer, decapsulateOut.sharedSecret.size); diff --git a/tests/unit_tests.c b/tests/unit_tests.c index 7054067d2..3eeea4fb1 100644 --- a/tests/unit_tests.c +++ b/tests/unit_tests.c @@ -4517,6 +4517,51 @@ static void test_TPM2_ParseSpdmSessionInfo_Truncated(void) } #endif /* WOLFTPM_SPDM */ +static void test_TPM2_Packet_ParseU16BufStrict(void) +{ + byte field[] = { 0x00, 0x04, 0x11, 0x22, 0x33, 0x44 }; + byte out[8]; + TPM2_Packet packet; + UINT16 size; + int rc; + + XMEMSET(&packet, 0, sizeof(packet)); + XMEMSET(out, 0, sizeof(out)); + packet.buf = field; + packet.size = (int)sizeof(field); + rc = TPM2_Packet_ParseU16BufStrict(&packet, &size, out, 4); + AssertIntEQ(rc, TPM_RC_SUCCESS); + AssertIntEQ(size, 4); + AssertIntEQ(out[3], 0x44); + AssertIntEQ(packet.pos, packet.size); + + /* Larger than the destination: rejected, not truncated */ + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = field; + packet.size = (int)sizeof(field); + rc = TPM2_Packet_ParseU16BufStrict(&packet, &size, out, 3); + AssertIntEQ(rc, TPM_RC_SIZE); + AssertIntEQ(size, 0); + + /* Larger than the bytes left in the response */ + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = field; + packet.size = (int)sizeof(field) - 1; + rc = TPM2_Packet_ParseU16BufStrict(&packet, &size, out, sizeof(out)); + AssertIntEQ(rc, TPM_RC_SIZE); + AssertIntEQ(size, 0); + + /* Size field itself truncated */ + XMEMSET(&packet, 0, sizeof(packet)); + packet.buf = field; + packet.size = 1; + rc = TPM2_Packet_ParseU16BufStrict(&packet, &size, out, sizeof(out)); + AssertIntEQ(rc, TPM_RC_SIZE); + AssertIntEQ(size, 0); + + printf("Test TPM Wrapper:\tParseU16BufStrict:\t\tPassed\n"); +} + /* TPM2_Packet_ParsePoint must resync to outerStart + point->size so a * malformed wire blob with inner x.size / y.size disagreement can't * desynchronize subsequent fields. */ @@ -9940,6 +9985,7 @@ int unit_tests(int argc, char *argv[]) test_wolfTPM2_PolicyTransportSPDMMake(); test_TPM2_ParseSpdmSessionInfo_Truncated(); #endif + test_TPM2_Packet_ParseU16BufStrict(); test_wolfTPM2_Init(); test_wolfTPM2_OpenExisting(); test_wolfTPM2_GetCapabilities(); diff --git a/wolftpm/tpm2_packet.h b/wolftpm/tpm2_packet.h index eb912d68b..bedc632cf 100644 --- a/wolftpm/tpm2_packet.h +++ b/wolftpm/tpm2_packet.h @@ -177,6 +177,8 @@ WOLFTPM_TEST_API int TPM2_ParseSpdmSessionInfo(TPM2_Packet* packet, */ WOLFTPM_LOCAL void TPM2_Packet_ParseU16Buf(TPM2_Packet* packet, UINT16* size, byte* buf, UINT16 maxBufSz); +WOLFTPM_TEST_API int TPM2_Packet_ParseU16BufStrict(TPM2_Packet* packet, + UINT16* size, byte* buf, UINT16 maxBufSz); WOLFTPM_LOCAL void TPM2_Packet_MarkU16(TPM2_Packet* packet, int* markSz); WOLFTPM_LOCAL int TPM2_Packet_PlaceU16(TPM2_Packet* packet, int markSz); WOLFTPM_LOCAL void TPM2_Packet_MarkU32(TPM2_Packet* packet, int* markSz);