Skip to content

Commit 40e728e

Browse files
corrections for ECH specification
1 parent 7e7d809 commit 40e728e

1 file changed

Lines changed: 121 additions & 38 deletions

File tree

src/tls.c

Lines changed: 121 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)