@@ -13459,6 +13459,74 @@ static int TLSX_ECH_GetSize(WOLFSSL_ECH* ech, byte msgType)
1345913459 return (int)size;
1346013460}
1346113461
13462+ /* rough check that inner hello fields do not exceed length of decrypted
13463+ * information. Additionally, this function will check that all padding bytes
13464+ * are zero and decrease the innerHelloLen accordingly if so.
13465+ * returns 0 on success and otherwise failure */
13466+ static int TLSX_ECH_CheckInnerPadding(WOLFSSL* ssl, WOLFSSL_ECH* ech)
13467+ {
13468+ int headerSz;
13469+ const byte* innerCh;
13470+ word32 innerChLen;
13471+ word32 idx;
13472+ byte sessionIdLen;
13473+ word16 cipherSuitesLen;
13474+ byte compressionLen;
13475+ word16 extLen;
13476+ byte acc = 0;
13477+ word32 i;
13478+
13479+ #ifdef WOLFSSL_DTLS13
13480+ headerSz = ssl->options.dtls ? DTLS13_HANDSHAKE_HEADER_SZ :
13481+ HANDSHAKE_HEADER_SZ;
13482+ #else
13483+ headerSz = HANDSHAKE_HEADER_SZ;
13484+ #endif
13485+
13486+ innerCh = ech->innerClientHello + headerSz;
13487+ innerChLen = ech->innerClientHelloLen;
13488+
13489+ idx = OPAQUE16_LEN + RAN_LEN;
13490+ if (idx >= innerChLen)
13491+ return BUFFER_ERROR;
13492+
13493+ sessionIdLen = innerCh[idx++];
13494+ /* innerHello sessionID must initially be empty */
13495+ if (sessionIdLen != 0)
13496+ return INVALID_PARAMETER;
13497+ idx += sessionIdLen;
13498+ if (idx + OPAQUE16_LEN > innerChLen)
13499+ return BUFFER_ERROR;
13500+
13501+ ato16(innerCh + idx, &cipherSuitesLen);
13502+ idx += OPAQUE16_LEN + cipherSuitesLen;
13503+ if (idx >= innerChLen)
13504+ return BUFFER_ERROR;
13505+
13506+ compressionLen = innerCh[idx++];
13507+ idx += compressionLen;
13508+ if (idx + OPAQUE16_LEN > innerChLen)
13509+ return BUFFER_ERROR;
13510+
13511+ ato16(innerCh + idx, &extLen);
13512+ idx += OPAQUE16_LEN + extLen;
13513+ if (idx > innerChLen)
13514+ return BUFFER_ERROR;
13515+
13516+ /* should now be at the end of the innerHello
13517+ * Per ECH spec all padding bytes MUST be 0 */
13518+ for (i = idx; i < innerChLen; i++) {
13519+ acc |= innerCh[i];
13520+ }
13521+ if (acc != 0) {
13522+ SendAlert(ssl, alert_fatal, illegal_parameter);
13523+ return INVALID_PARAMETER;
13524+ }
13525+
13526+ ech->innerClientHelloLen -= i - idx;
13527+ return 0;
13528+ }
13529+
1346213530/* Locate the given extension type, use the extOffset to start off after where a
1346313531 * previous call to this function ended
1346413532 *
@@ -13510,7 +13578,7 @@ static const byte* TLSX_ECH_FindOuterExtension(const byte* outerCh,
1351013578 return NULL;
1351113579 }
1351213580
13513- while (idx < chLen && (idx - *extensionsStart) < *extensionsLen) {
13581+ while (idx - *extensionsStart < *extensionsLen) {
1351413582 if (idx + OPAQUE16_LEN + OPAQUE16_LEN > chLen)
1351513583 return NULL;
1351613584
@@ -13519,7 +13587,7 @@ static const byte* TLSX_ECH_FindOuterExtension(const byte* outerCh,
1351913587 ato16(outerCh + idx, &len);
1352013588 idx += OPAQUE16_LEN;
1352113589
13522- if (idx + len > chLen )
13590+ if (idx + len - *extensionsStart > *extensionsLen )
1352313591 return NULL;
1352413592
1352513593 if (type == extType) {
@@ -13662,33 +13730,22 @@ static int TLSX_ECH_ExpandOuterExtensions(WOLFSSL* ssl, WOLFSSL_ECH* ech,
1366213730 outerCh = ech->aad;
1366313731 outerChLen = ech->aadLen;
1366413732
13733+ /* don't need to check for buffer overflows here since they are caught by
13734+ * TLSX_ECH_CheckInnerPadding */
1366513735 idx = OPAQUE16_LEN + RAN_LEN;
13666- if (idx >= innerChLen)
13667- return BUFFER_ERROR;
1366813736
1366913737 sessionIdLen = innerCh[idx++];
1367013738 idx += sessionIdLen;
13671- /* the ECH spec details that innerhello sessionID must initially be empty */
13672- if (sessionIdLen != 0)
13673- return INVALID_PARAMETER;
13674- if (idx + OPAQUE16_LEN > innerChLen)
13675- return BUFFER_ERROR;
1367613739
1367713740 ato16(innerCh + idx, &cipherSuitesLen);
1367813741 idx += OPAQUE16_LEN + cipherSuitesLen;
13679- if (idx >= innerChLen)
13680- return BUFFER_ERROR;
1368113742
1368213743 compressionLen = innerCh[idx++];
1368313744 idx += compressionLen;
13684- if (idx + OPAQUE16_LEN > innerChLen)
13685- return BUFFER_ERROR;
1368613745
1368713746 ato16(innerCh + idx, &innerExtLen);
1368813747 idx += OPAQUE16_LEN;
1368913748 innerExtIdx = idx;
13690- if (idx + innerExtLen > innerChLen)
13691- return BUFFER_ERROR;
1369213749
1369313750 /* validate ech_outer_extensions and calculate extra size */
1369413751 while (idx < innerChLen && (idx - innerExtIdx) < innerExtLen) {
@@ -13931,12 +13988,13 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size,
1393113988 byte msgType)
1393213989{
1393313990 int ret = 0;
13934- int i;
1393513991 TLSX* echX;
1393613992 WOLFSSL_ECH* ech;
1393713993 WOLFSSL_EchConfig* echConfig;
1393813994 byte* aadCopy;
1393913995 byte* readBuf_p = (byte*)readBuf;
13996+ word16 tmpVal16;
13997+
1394013998 WOLFSSL_MSG("TLSX_ECH_Parse");
1394113999 if (ssl->options.disableECH) {
1394214000 WOLFSSL_MSG("TLSX_ECH_Parse: ECH disabled. Ignoring.");
@@ -13977,21 +14035,26 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size,
1397714035 ech->state = ECH_PARSED_INTERNAL;
1397814036 return 0;
1397914037 }
14038+ else if (ech->type != ECH_TYPE_OUTER) {
14039+ /* type MUST be INNER or OUTER */
14040+ SendAlert(ssl, alert_fatal, illegal_parameter);
14041+ return INVALID_PARAMETER;
14042+ }
1398014043 /* technically the payload would only be 1 byte at this length */
1398114044 if (size < 11 + ech->encLen)
1398214045 return BAD_FUNC_ARG;
13983- /* read kdfId */
13984- ato16(readBuf_p, &ech->cipherSuite.kdfId);
13985- readBuf_p += 2;
13986- /* read aeadId */
13987- ato16(readBuf_p, &ech->cipherSuite.aeadId);
13988- readBuf_p += 2;
13989- /* read configId */
13990- ech->configId = *readBuf_p;
13991- readBuf_p++;
1399214046 /* only get enc if we don't already have the hpke context */
1399314047 if (ech->hpkeContext == NULL) {
13994- /* read encLen */
14048+ /* kdfId */
14049+ ato16(readBuf_p, &ech->cipherSuite.kdfId);
14050+ readBuf_p += 2;
14051+ /* aeadId */
14052+ ato16(readBuf_p, &ech->cipherSuite.aeadId);
14053+ readBuf_p += 2;
14054+ /* configId */
14055+ ech->configId = *readBuf_p;
14056+ readBuf_p++;
14057+ /* encLen */
1399514058 ato16(readBuf_p, &ech->encLen);
1399614059 readBuf_p += 2;
1399714060 if (ech->encLen > HPKE_Npk_MAX)
@@ -14001,6 +14064,33 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size,
1400114064 readBuf_p += ech->encLen;
1400214065 }
1400314066 else {
14067+ /* kdfId, aeadId, and configId must be the same as last time */
14068+ /* kdf */
14069+ ato16(readBuf_p, &tmpVal16);
14070+ if (tmpVal16 != ech->cipherSuite.kdfId) {
14071+ SendAlert(ssl, alert_fatal, illegal_parameter);
14072+ return INVALID_PARAMETER;
14073+ }
14074+ readBuf_p += 2;
14075+ /* aead */
14076+ ato16(readBuf_p, &tmpVal16);
14077+ if (tmpVal16 != ech->cipherSuite.aeadId) {
14078+ SendAlert(ssl, alert_fatal, illegal_parameter);
14079+ return INVALID_PARAMETER;
14080+ }
14081+ readBuf_p += 2;
14082+ /* configId */
14083+ if (*readBuf_p != ech->configId) {
14084+ SendAlert(ssl, alert_fatal, illegal_parameter);
14085+ return INVALID_PARAMETER;
14086+ }
14087+ readBuf_p++;
14088+ /* on an HRR the enc value MUST be empty */
14089+ ato16(readBuf_p, &tmpVal16);
14090+ if (tmpVal16 != 0) {
14091+ SendAlert(ssl, alert_fatal, illegal_parameter);
14092+ return INVALID_PARAMETER;
14093+ }
1400414094 readBuf_p += 2;
1400514095 }
1400614096 /* read hello inner len */
@@ -14051,19 +14141,12 @@ static int TLSX_ECH_Parse(WOLFSSL* ssl, const byte* readBuf, word16 size,
1405114141 }
1405214142 }
1405314143 if (ret == 0) {
14054- i = 0;
14055- /* decrement until before the padding */
14056- /* TODO: verify padding is 0, abort with illegal_parameter */
14057- while (ech->innerClientHello[ech->innerClientHelloLen +
14058- HANDSHAKE_HEADER_SZ - i - 1] != ECH_TYPE_INNER) {
14059- i++;
14144+ ret = TLSX_ECH_CheckInnerPadding(ssl, ech);
14145+ if (ret == 0) {
14146+ /* expand EchOuterExtensions if present.
14147+ * Also, if it exists, copy sessionID from outer hello */
14148+ ret = TLSX_ECH_ExpandOuterExtensions(ssl, ech, ssl->heap);
1406014149 }
14061- /* subtract the length of the padding from the length */
14062- ech->innerClientHelloLen -= i;
14063-
14064- /* expand EchOuterExtensions if present
14065- * and, if it exists, copy sessionID from outer hello */
14066- ret = TLSX_ECH_ExpandOuterExtensions(ssl, ech, ssl->heap);
1406714150 }
1406814151 /* if we failed to extract/expand, set state to retry configs */
1406914152 if (ret != 0) {
0 commit comments