Skip to content

Commit 58625d1

Browse files
corrections for ECH specification
1 parent c3a38dc commit 58625d1

2 files changed

Lines changed: 140 additions & 55 deletions

File tree

src/tls.c

Lines changed: 136 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -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) {

tests/api.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14074,7 +14074,7 @@ static int test_wolfSSL_Tls13_ECH_params(void)
1407414074
const char* b64Configs =
1407514075
"AEX+DQBBFAAgACBuAoQI8+liEVYQbXKBDeVgTmF2rfXuKO2knhwrN7jgTgAEAAEAAQASY2xvdWRmbGFyZS1lY2guY29tAAA=";
1407614076
word32 outputLen = sizeof(testBuf);
14077-
word16 tmpLen;
14077+
word16 tmpLen = 0;
1407814078
WOLFSSL_CTX* ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
1407914079
WOLFSSL* ssl = wolfSSL_new(ctx);
1408014080

@@ -14619,9 +14619,9 @@ static int test_wolfSSL_Tls13_ECH_new_config(void)
1461914619
word32 altConfigLen = sizeof(altConfig);
1462014620
byte combinedConfigs[512];
1462114621
word32 combinedConfigsLen = sizeof(combinedConfigs);
14622-
word16 firstConfigLen;
14623-
word16 secondConfigOffset;
14624-
word16 secondConfigLen;
14622+
word16 firstConfigLen = 0;
14623+
word16 secondConfigOffset = 0;
14624+
word16 secondConfigLen = 0;
1462514625

1462614626
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
1462714627

0 commit comments

Comments
 (0)