@@ -13485,6 +13485,74 @@ static int TLSX_ECH_GetSize(WOLFSSL_ECH* ech, byte msgType)
1348513485 return (int)size;
1348613486}
1348713487
13488+ /* rough check that inner hello fields do not exceed length of decrypted
13489+ * information. Additionally, this function will check that all padding bytes
13490+ * are zero and decrease the innerHelloLen accordingly if so.
13491+ * returns 0 on success and otherwise failure */
13492+ static int TLSX_ECH_CheckInnerPadding(WOLFSSL* ssl, WOLFSSL_ECH* ech)
13493+ {
13494+ int headerSz;
13495+ const byte* innerCh;
13496+ word32 innerChLen;
13497+ word32 idx;
13498+ byte sessionIdLen;
13499+ word16 cipherSuitesLen;
13500+ byte compressionLen;
13501+ word16 extLen;
13502+ byte acc = 0;
13503+ word32 i;
13504+
13505+ #ifdef WOLFSSL_DTLS13
13506+ headerSz = ssl->options.dtls ? DTLS13_HANDSHAKE_HEADER_SZ :
13507+ HANDSHAKE_HEADER_SZ;
13508+ #else
13509+ headerSz = HANDSHAKE_HEADER_SZ;
13510+ #endif
13511+
13512+ innerCh = ech->innerClientHello + headerSz;
13513+ innerChLen = ech->innerClientHelloLen;
13514+
13515+ idx = OPAQUE16_LEN + RAN_LEN;
13516+ if (idx >= innerChLen)
13517+ return BUFFER_ERROR;
13518+
13519+ sessionIdLen = innerCh[idx++];
13520+ /* innerHello sessionID must initially be empty */
13521+ if (sessionIdLen != 0)
13522+ return INVALID_PARAMETER;
13523+ idx += sessionIdLen;
13524+ if (idx + OPAQUE16_LEN > innerChLen)
13525+ return BUFFER_ERROR;
13526+
13527+ ato16(innerCh + idx, &cipherSuitesLen);
13528+ idx += OPAQUE16_LEN + cipherSuitesLen;
13529+ if (idx >= innerChLen)
13530+ return BUFFER_ERROR;
13531+
13532+ compressionLen = innerCh[idx++];
13533+ idx += compressionLen;
13534+ if (idx + OPAQUE16_LEN > innerChLen)
13535+ return BUFFER_ERROR;
13536+
13537+ ato16(innerCh + idx, &extLen);
13538+ idx += OPAQUE16_LEN + extLen;
13539+ if (idx > innerChLen)
13540+ return BUFFER_ERROR;
13541+
13542+ /* should now be at the end of the innerHello
13543+ * Per ECH spec all padding bytes MUST be 0 */
13544+ for (i = idx; i < innerChLen; i++) {
13545+ acc |= innerCh[i];
13546+ }
13547+ if (acc != 0) {
13548+ SendAlert(ssl, alert_fatal, illegal_parameter);
13549+ return INVALID_PARAMETER;
13550+ }
13551+
13552+ ech->innerClientHelloLen -= i - idx;
13553+ return 0;
13554+ }
13555+
1348813556/* Locate the given extension type, use the extOffset to start off after where a
1348913557 * previous call to this function ended
1349013558 *
@@ -13536,7 +13604,7 @@ static const byte* TLSX_ECH_FindOuterExtension(const byte* outerCh,
1353613604 return NULL;
1353713605 }
1353813606
13539- while (idx < chLen && (idx - *extensionsStart) < *extensionsLen) {
13607+ while (idx - *extensionsStart < *extensionsLen) {
1354013608 if (idx + OPAQUE16_LEN + OPAQUE16_LEN > chLen)
1354113609 return NULL;
1354213610
@@ -13545,7 +13613,7 @@ static const byte* TLSX_ECH_FindOuterExtension(const byte* outerCh,
1354513613 ato16(outerCh + idx, &len);
1354613614 idx += OPAQUE16_LEN;
1354713615
13548- if (idx + len > chLen )
13616+ if (idx + len - *extensionsStart > *extensionsLen )
1354913617 return NULL;
1355013618
1355113619 if (type == extType) {
@@ -13688,33 +13756,22 @@ static int TLSX_ECH_ExpandOuterExtensions(WOLFSSL* ssl, WOLFSSL_ECH* ech,
1368813756 outerCh = ech->aad;
1368913757 outerChLen = ech->aadLen;
1369013758
13759+ /* don't need to check for buffer overflows here since they are caught by
13760+ * TLSX_ECH_CheckInnerPadding */
1369113761 idx = OPAQUE16_LEN + RAN_LEN;
13692- if (idx >= innerChLen)
13693- return BUFFER_ERROR;
1369413762
1369513763 sessionIdLen = innerCh[idx++];
1369613764 idx += sessionIdLen;
13697- /* the ECH spec details that innerhello sessionID must initially be empty */
13698- if (sessionIdLen != 0)
13699- return INVALID_PARAMETER;
13700- if (idx + OPAQUE16_LEN > innerChLen)
13701- return BUFFER_ERROR;
1370213765
1370313766 ato16(innerCh + idx, &cipherSuitesLen);
1370413767 idx += OPAQUE16_LEN + cipherSuitesLen;
13705- if (idx >= innerChLen)
13706- return BUFFER_ERROR;
1370713768
1370813769 compressionLen = innerCh[idx++];
1370913770 idx += compressionLen;
13710- if (idx + OPAQUE16_LEN > innerChLen)
13711- return BUFFER_ERROR;
1371213771
1371313772 ato16(innerCh + idx, &innerExtLen);
1371413773 idx += OPAQUE16_LEN;
1371513774 innerExtIdx = idx;
13716- if (idx + innerExtLen > innerChLen)
13717- return BUFFER_ERROR;
1371813775
1371913776 /* validate ech_outer_extensions and calculate extra size */
1372013777 while (idx < innerChLen && (idx - innerExtIdx) < innerExtLen) {
@@ -13957,14 +14014,14 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size,
1395714014 byte msgType)
1395814015{
1395914016 int ret = 0;
13960- int i;
1396114017 TLSX* echX;
1396214018 WOLFSSL_ECH* ech;
1396314019 WOLFSSL_EchConfig* echConfig;
1396414020 byte* aadCopy;
1396514021 byte* readBuf_p = (byte*)readBuf;
1396614022 word32 offset = 0;
1396714023 word16 len;
14024+ word16 tmpVal16;
1396814025
1396914026 WOLFSSL_MSG("TLSX_ECH_Parse");
1397014027 if (ssl->options.disableECH) {
@@ -14007,39 +14064,74 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size,
1400714064 ech->state = ECH_PARSED_INTERNAL;
1400814065 return 0;
1400914066 }
14067+ else if (ech->type != ECH_TYPE_OUTER) {
14068+ /* type MUST be INNER or OUTER */
14069+ return BAD_FUNC_ARG;
14070+ }
1401014071 /* Must have kdfId, aeadId, configId, enc len and payload len. */
1401114072 if (size < offset + 2 + 2 + 1 + 2 + 2) {
1401214073 return BUFFER_ERROR;
1401314074 }
14014- /* read kdfId */
14015- ato16(readBuf_p, &ech->cipherSuite.kdfId);
14016- readBuf_p += 2;
14017- offset += 2;
14018- /* read aeadId */
14019- ato16(readBuf_p, &ech->cipherSuite.aeadId);
14020- readBuf_p += 2;
14021- offset += 2;
14022- /* read configId */
14023- ech->configId = *readBuf_p;
14024- readBuf_p++;
14025- offset++;
14026- /* read encLen */
14027- ato16(readBuf_p, &len);
14028- readBuf_p += 2;
14029- offset += 2;
14030- /* Check encLen isn't more than remaining bytes minus payload length. */
14031- if (len > size - offset - 2) {
14032- return BAD_FUNC_ARG;
14033- }
14034- if (len > HPKE_Npk_MAX) {
14035- return BAD_FUNC_ARG;
14036- }
1403714075 /* only get enc if we don't already have the hpke context */
1403814076 if (ech->hpkeContext == NULL) {
14077+ /* kdfId */
14078+ ato16(readBuf_p, &ech->cipherSuite.kdfId);
14079+ readBuf_p += 2;
14080+ offset += 2;
14081+ /* aeadId */
14082+ ato16(readBuf_p, &ech->cipherSuite.aeadId);
14083+ readBuf_p += 2;
14084+ offset += 2;
14085+ /* configId */
14086+ ech->configId = *readBuf_p;
14087+ readBuf_p++;
14088+ offset++;
14089+ /* encLen */
14090+ ato16(readBuf_p, &len);
14091+ readBuf_p += 2;
14092+ offset += 2;
14093+ /* Check encLen isn't more than remaining bytes minus
14094+ * payload length. */
14095+ if (len > size - offset - 2) {
14096+ return BAD_FUNC_ARG;
14097+ }
14098+ if (len > HPKE_Npk_MAX) {
14099+ return BAD_FUNC_ARG;
14100+ }
1403914101 /* read enc */
1404014102 XMEMCPY(ech->enc, readBuf_p, len);
1404114103 ech->encLen = len;
1404214104 }
14105+ else {
14106+ /* kdfId, aeadId, and configId must be the same as last time */
14107+ /* kdfId */
14108+ ato16(readBuf_p, &tmpVal16);
14109+ if (tmpVal16 != ech->cipherSuite.kdfId) {
14110+ return BAD_FUNC_ARG;
14111+ }
14112+ readBuf_p += 2;
14113+ offset += 2;
14114+ /* aeadId */
14115+ ato16(readBuf_p, &tmpVal16);
14116+ if (tmpVal16 != ech->cipherSuite.aeadId) {
14117+ return BAD_FUNC_ARG;
14118+ }
14119+ readBuf_p += 2;
14120+ offset += 2;
14121+ /* configId */
14122+ if (*readBuf_p != ech->configId) {
14123+ return BAD_FUNC_ARG;
14124+ }
14125+ readBuf_p++;
14126+ offset++;
14127+ /* on an HRR the enc value MUST be empty */
14128+ ato16(readBuf_p, &len);
14129+ if (len != 0) {
14130+ return BAD_FUNC_ARG;
14131+ }
14132+ readBuf_p += 2;
14133+ offset += 2;
14134+ }
1404314135 readBuf_p += len;
1404414136 offset += len;
1404514137 /* read hello inner len */
@@ -14098,19 +14190,12 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size,
1409814190 }
1409914191 }
1410014192 if (ret == 0) {
14101- i = 0;
14102- /* decrement until before the padding */
14103- /* TODO: verify padding is 0, abort with illegal_parameter */
14104- while (ech->innerClientHello[ech->innerClientHelloLen +
14105- HANDSHAKE_HEADER_SZ - i - 1] != ECH_TYPE_INNER) {
14106- i++;
14193+ ret = TLSX_ECH_CheckInnerPadding(ssl, ech);
14194+ if (ret == 0) {
14195+ /* expand EchOuterExtensions if present.
14196+ * Also, if it exists, copy sessionID from outer hello */
14197+ ret = TLSX_ECH_ExpandOuterExtensions(ssl, ech, ssl->heap);
1410714198 }
14108- /* subtract the length of the padding from the length */
14109- ech->innerClientHelloLen -= i;
14110-
14111- /* expand EchOuterExtensions if present
14112- * and, if it exists, copy sessionID from outer hello */
14113- ret = TLSX_ECH_ExpandOuterExtensions(ssl, ech, ssl->heap);
1411414199 }
1411514200 /* if we failed to extract/expand, set state to retry configs */
1411614201 if (ret != 0) {
0 commit comments