Skip to content

Commit 8f73ae4

Browse files
committed
Fix TLSX_Parse to check dup ECH
1 parent 178e10e commit 8f73ae4

10 files changed

Lines changed: 562 additions & 14 deletions

File tree

src/internal.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21862,6 +21862,23 @@ int DoApplicationData(WOLFSSL* ssl, byte* input, word32* inOutIdx, int sniff)
2186221862
}
2186321863
#endif
2186421864

21865+
/* Rate-limit empty application data records to prevent DoS */
21866+
if (dataSz == 0) {
21867+
if (++ssl->options.emptyRecordCount >= WOLFSSL_MAX_EMPTY_RECORDS) {
21868+
WOLFSSL_MSG("Too many empty records");
21869+
#ifdef WOLFSSL_EXTRA_ALERTS
21870+
if (sniff == NO_SNIFF) {
21871+
SendAlert(ssl, alert_fatal, unexpected_message);
21872+
}
21873+
#endif
21874+
WOLFSSL_ERROR_VERBOSE(EMPTY_RECORD_LIMIT_E);
21875+
return EMPTY_RECORD_LIMIT_E;
21876+
}
21877+
}
21878+
else {
21879+
ssl->options.emptyRecordCount = 0;
21880+
}
21881+
2186521882
/* read data */
2186621883
if (dataSz) {
2186721884
int rawSz = dataSz; /* keep raw size for idx adjustment */
@@ -27573,6 +27590,9 @@ const char* wolfSSL_ERR_reason_error_string(unsigned long e)
2757327590
case ALERT_COUNT_E:
2757427591
return "Alert Count exceeded error";
2757527592

27593+
case EMPTY_RECORD_LIMIT_E:
27594+
return "Too many empty records error";
27595+
2757627596
case EXT_MISSING:
2757727597
return "Required TLS extension missing";
2757827598

src/tls.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17043,6 +17043,9 @@ int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType,
1704317043
#ifdef WOLFSSL_QUIC
1704417044
|| (type == TLSX_KEY_QUIC_TP_PARAMS_DRAFT)
1704517045
#endif
17046+
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
17047+
|| (type == TLSX_ECH)
17048+
#endif
1704617049
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_DUAL_ALG_CERTS)
1704717050
|| (type == TLSX_CKS)
1704817051
#endif

src/tls13.c

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2638,9 +2638,10 @@ static int EncryptTls13(WOLFSSL* ssl, byte* output, const byte* input,
26382638
#endif
26392639

26402640
#ifdef WOLFSSL_CIPHER_TEXT_CHECK
2641-
if (ssl->specs.bulk_cipher_algorithm != wolfssl_cipher_null) {
2641+
if (ssl->specs.bulk_cipher_algorithm != wolfssl_cipher_null &&
2642+
dataSz >= WOLFSSL_CIPHER_CHECK_SZ) {
26422643
XMEMCPY(ssl->encrypt.sanityCheck, input,
2643-
min(dataSz, sizeof(ssl->encrypt.sanityCheck)));
2644+
sizeof(ssl->encrypt.sanityCheck));
26442645
}
26452646
#endif
26462647

@@ -2824,8 +2825,9 @@ static int EncryptTls13(WOLFSSL* ssl, byte* output, const byte* input,
28242825

28252826
#ifdef WOLFSSL_CIPHER_TEXT_CHECK
28262827
if (ssl->specs.bulk_cipher_algorithm != wolfssl_cipher_null &&
2828+
dataSz >= WOLFSSL_CIPHER_CHECK_SZ &&
28272829
XMEMCMP(output, ssl->encrypt.sanityCheck,
2828-
min(dataSz, sizeof(ssl->encrypt.sanityCheck))) == 0) {
2830+
sizeof(ssl->encrypt.sanityCheck)) == 0) {
28292831

28302832
WOLFSSL_MSG("EncryptTls13 sanity check failed! Glitch?");
28312833
return ENCRYPT_ERROR;

tests/api.c

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21402,6 +21402,226 @@ static int test_MakeCertWithPathLen(void)
2140221402
return EXPECT_RESULT();
2140321403
}
2140421404

21405+
static int test_PathLenSelfIssued(void)
21406+
{
21407+
EXPECT_DECLS;
21408+
#if defined(WOLFSSL_CERT_REQ) && !defined(NO_ASN_TIME) && \
21409+
defined(WOLFSSL_CERT_GEN) && defined(HAVE_ECC) && \
21410+
defined(WOLFSSL_CERT_EXT) && !defined(NO_CERTS) && \
21411+
(!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH))
21412+
Cert cert;
21413+
DecodedCert decodedCert;
21414+
byte rootDer[FOURK_BUF];
21415+
byte icaDer[FOURK_BUF];
21416+
byte entityDer[FOURK_BUF];
21417+
int rootDerSz = 0;
21418+
int icaDerSz = 0;
21419+
int entityDerSz = 0;
21420+
WC_RNG rng;
21421+
ecc_key rootKey;
21422+
ecc_key icaKey;
21423+
ecc_key entityKey;
21424+
WOLFSSL_CERT_MANAGER* cm = NULL;
21425+
21426+
XMEMSET(&rng, 0, sizeof(WC_RNG));
21427+
XMEMSET(&rootKey, 0, sizeof(ecc_key));
21428+
XMEMSET(&icaKey, 0, sizeof(ecc_key));
21429+
XMEMSET(&entityKey, 0, sizeof(ecc_key));
21430+
21431+
ExpectIntEQ(wc_InitRng(&rng), 0);
21432+
ExpectIntEQ(wc_ecc_init(&rootKey), 0);
21433+
ExpectIntEQ(wc_ecc_init(&icaKey), 0);
21434+
ExpectIntEQ(wc_ecc_init(&entityKey), 0);
21435+
ExpectIntEQ(wc_ecc_make_key(&rng, 32, &rootKey), 0);
21436+
ExpectIntEQ(wc_ecc_make_key(&rng, 32, &icaKey), 0);
21437+
ExpectIntEQ(wc_ecc_make_key(&rng, 32, &entityKey), 0);
21438+
21439+
/* Step 1: Create root CA with pathLen=0 */
21440+
ExpectIntEQ(wc_InitCert(&cert), 0);
21441+
(void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
21442+
(void)XSTRNCPY(cert.subject.state, "MT", CTC_NAME_SIZE);
21443+
(void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
21444+
(void)XSTRNCPY(cert.subject.org, "TestCA", CTC_NAME_SIZE);
21445+
(void)XSTRNCPY(cert.subject.unit, "Test", CTC_NAME_SIZE);
21446+
(void)XSTRNCPY(cert.subject.commonName, "TestRootCA", CTC_NAME_SIZE);
21447+
(void)XSTRNCPY(cert.subject.email, "root@test.com", CTC_NAME_SIZE);
21448+
cert.selfSigned = 1;
21449+
cert.isCA = 1;
21450+
cert.pathLen = 0;
21451+
cert.pathLenSet = 1;
21452+
cert.sigType = CTC_SHA256wECDSA;
21453+
cert.keyUsage = KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN;
21454+
ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey),
21455+
0);
21456+
21457+
ExpectIntGE(wc_MakeCert(&cert, rootDer, FOURK_BUF, NULL, &rootKey, &rng),
21458+
0);
21459+
ExpectIntGE(rootDerSz = wc_SignCert(cert.bodySz, cert.sigType, rootDer,
21460+
FOURK_BUF, NULL, &rootKey, &rng), 0);
21461+
21462+
/* Step 2: Create self-issued intermediate (same subject DN as root,
21463+
* different key, signed by root) - this should be blocked by pathLen=0 */
21464+
ExpectIntEQ(wc_InitCert(&cert), 0);
21465+
cert.selfSigned = 0;
21466+
cert.isCA = 1;
21467+
cert.sigType = CTC_SHA256wECDSA;
21468+
cert.keyUsage = KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN;
21469+
/* Set both subject and issuer from the root cert so they match */
21470+
ExpectIntEQ(wc_SetSubjectBuffer(&cert, rootDer, rootDerSz), 0);
21471+
ExpectIntEQ(wc_SetIssuerBuffer(&cert, rootDer, rootDerSz), 0);
21472+
ExpectIntEQ(wc_SetAuthKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey), 0);
21473+
ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &icaKey),
21474+
0);
21475+
21476+
ExpectIntGE(wc_MakeCert(&cert, icaDer, FOURK_BUF, NULL, &icaKey, &rng), 0);
21477+
ExpectIntGE(icaDerSz = wc_SignCert(cert.bodySz, cert.sigType, icaDer,
21478+
FOURK_BUF, NULL, &rootKey, &rng), 0);
21479+
21480+
/* Step 3: Create entity cert signed by the intermediate */
21481+
ExpectIntEQ(wc_InitCert(&cert), 0);
21482+
cert.selfSigned = 0;
21483+
cert.isCA = 0;
21484+
cert.sigType = CTC_SHA256wECDSA;
21485+
(void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
21486+
(void)XSTRNCPY(cert.subject.state, "MT", CTC_NAME_SIZE);
21487+
(void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
21488+
(void)XSTRNCPY(cert.subject.org, "TestEntity", CTC_NAME_SIZE);
21489+
(void)XSTRNCPY(cert.subject.commonName, "entity.test", CTC_NAME_SIZE);
21490+
ExpectIntEQ(wc_SetIssuerBuffer(&cert, icaDer, icaDerSz), 0);
21491+
ExpectIntEQ(wc_SetAuthKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &icaKey), 0);
21492+
21493+
ExpectIntGE(wc_MakeCert(&cert, entityDer, FOURK_BUF, NULL, &entityKey,
21494+
&rng), 0);
21495+
ExpectIntGE(entityDerSz = wc_SignCert(cert.bodySz, cert.sigType, entityDer,
21496+
FOURK_BUF, NULL, &icaKey, &rng), 0);
21497+
21498+
/* Step 4: Load root CA into cert manager */
21499+
ExpectNotNull(cm = wolfSSL_CertManagerNew());
21500+
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, rootDer, rootDerSz,
21501+
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
21502+
21503+
/* Step 5: Parse the self-issued intermediate as a chain cert.
21504+
* This simulates TLS chain verification where the intermediate is
21505+
* received as part of the certificate chain.
21506+
* Root CA has pathLen=0, so it should NOT be allowed to sign any
21507+
* intermediate CA (including self-issued ones).
21508+
* BUG: wolfSSL sets selfSigned=1 for this cert (issuer==subject DN),
21509+
* which causes the pathLen enforcement to be entirely skipped. */
21510+
wc_InitDecodedCert(&decodedCert, icaDer, (word32)icaDerSz, NULL);
21511+
ExpectIntEQ(wc_ParseCert(&decodedCert, CHAIN_CERT_TYPE, VERIFY,
21512+
cm), WC_NO_ERR_TRACE(ASN_PATHLEN_INV_E));
21513+
wc_FreeDecodedCert(&decodedCert);
21514+
21515+
wolfSSL_CertManagerFree(cm);
21516+
wc_ecc_free(&entityKey);
21517+
wc_ecc_free(&icaKey);
21518+
wc_ecc_free(&rootKey);
21519+
wc_FreeRng(&rng);
21520+
#endif
21521+
return EXPECT_RESULT();
21522+
}
21523+
21524+
static int test_PathLenNoKeyUsage(void)
21525+
{
21526+
EXPECT_DECLS;
21527+
#if defined(WOLFSSL_CERT_REQ) && !defined(NO_ASN_TIME) && \
21528+
defined(WOLFSSL_CERT_GEN) && defined(HAVE_ECC) && \
21529+
defined(WOLFSSL_CERT_EXT) && !defined(NO_CERTS) && \
21530+
(!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH))
21531+
Cert cert;
21532+
DecodedCert decodedCert;
21533+
byte rootDer[FOURK_BUF];
21534+
byte icaDer[FOURK_BUF];
21535+
int rootDerSz = 0;
21536+
int icaDerSz = 0;
21537+
WC_RNG rng;
21538+
ecc_key rootKey;
21539+
ecc_key icaKey;
21540+
WOLFSSL_CERT_MANAGER* cm = NULL;
21541+
21542+
XMEMSET(&rng, 0, sizeof(WC_RNG));
21543+
XMEMSET(&rootKey, 0, sizeof(ecc_key));
21544+
XMEMSET(&icaKey, 0, sizeof(ecc_key));
21545+
21546+
ExpectIntEQ(wc_InitRng(&rng), 0);
21547+
ExpectIntEQ(wc_ecc_init(&rootKey), 0);
21548+
ExpectIntEQ(wc_ecc_init(&icaKey), 0);
21549+
ExpectIntEQ(wc_ecc_make_key(&rng, 32, &rootKey), 0);
21550+
ExpectIntEQ(wc_ecc_make_key(&rng, 32, &icaKey), 0);
21551+
21552+
/* Step 1: Create root CA with pathLen=0 and KeyUsage */
21553+
ExpectIntEQ(wc_InitCert(&cert), 0);
21554+
(void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
21555+
(void)XSTRNCPY(cert.subject.state, "MT", CTC_NAME_SIZE);
21556+
(void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
21557+
(void)XSTRNCPY(cert.subject.org, "TestCA2", CTC_NAME_SIZE);
21558+
(void)XSTRNCPY(cert.subject.unit, "Test", CTC_NAME_SIZE);
21559+
(void)XSTRNCPY(cert.subject.commonName, "TestRootCA2", CTC_NAME_SIZE);
21560+
(void)XSTRNCPY(cert.subject.email, "root@test2.com", CTC_NAME_SIZE);
21561+
cert.selfSigned = 1;
21562+
cert.isCA = 1;
21563+
cert.pathLen = 0;
21564+
cert.pathLenSet = 1;
21565+
cert.sigType = CTC_SHA256wECDSA;
21566+
cert.keyUsage = KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN;
21567+
ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey),
21568+
0);
21569+
21570+
ExpectIntGE(wc_MakeCert(&cert, rootDer, FOURK_BUF, NULL, &rootKey, &rng),
21571+
0);
21572+
ExpectIntGE(rootDerSz = wc_SignCert(cert.bodySz, cert.sigType, rootDer,
21573+
FOURK_BUF, NULL, &rootKey, &rng), 0);
21574+
21575+
/* Step 2: Create intermediate CA WITHOUT KeyUsage extension.
21576+
* Per RFC 5280, when KeyUsage is absent all uses are valid.
21577+
* The root's pathLen=0 should still block this intermediate CA.
21578+
* BUG: pathLen check requires extKeyUsageSet which is false when
21579+
* KeyUsage is absent, so the check is skipped entirely. */
21580+
ExpectIntEQ(wc_InitCert(&cert), 0);
21581+
cert.selfSigned = 0;
21582+
cert.isCA = 1;
21583+
cert.sigType = CTC_SHA256wECDSA;
21584+
/* Intentionally do NOT set keyUsage - test that pathLen is still enforced */
21585+
cert.keyUsage = 0;
21586+
(void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
21587+
(void)XSTRNCPY(cert.subject.state, "MT", CTC_NAME_SIZE);
21588+
(void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
21589+
(void)XSTRNCPY(cert.subject.org, "TestICA", CTC_NAME_SIZE);
21590+
(void)XSTRNCPY(cert.subject.unit, "Test", CTC_NAME_SIZE);
21591+
(void)XSTRNCPY(cert.subject.commonName, "TestICA-NoKU", CTC_NAME_SIZE);
21592+
(void)XSTRNCPY(cert.subject.email, "ica@test2.com", CTC_NAME_SIZE);
21593+
ExpectIntEQ(wc_SetIssuerBuffer(&cert, rootDer, rootDerSz), 0);
21594+
ExpectIntEQ(wc_SetAuthKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey), 0);
21595+
ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &icaKey),
21596+
0);
21597+
21598+
ExpectIntGE(wc_MakeCert(&cert, icaDer, FOURK_BUF, NULL, &icaKey, &rng), 0);
21599+
ExpectIntGE(icaDerSz = wc_SignCert(cert.bodySz, cert.sigType, icaDer,
21600+
FOURK_BUF, NULL, &rootKey, &rng), 0);
21601+
21602+
/* Step 3: Load root CA into cert manager */
21603+
ExpectNotNull(cm = wolfSSL_CertManagerNew());
21604+
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, rootDer, rootDerSz,
21605+
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
21606+
21607+
/* Step 4: Parse the intermediate (no KeyUsage) as a chain cert.
21608+
* Root CA has pathLen=0, this intermediate CA should be rejected.
21609+
* The intermediate does NOT have the KeyUsage extension, but per
21610+
* RFC 5280 4.2.1.3 all key uses are valid when the extension is
21611+
* absent, so pathLen must still be enforced. */
21612+
wc_InitDecodedCert(&decodedCert, icaDer, (word32)icaDerSz, NULL);
21613+
ExpectIntEQ(wc_ParseCert(&decodedCert, CHAIN_CERT_TYPE, VERIFY,
21614+
cm), WC_NO_ERR_TRACE(ASN_PATHLEN_INV_E));
21615+
wc_FreeDecodedCert(&decodedCert);
21616+
21617+
wolfSSL_CertManagerFree(cm);
21618+
wc_ecc_free(&icaKey);
21619+
wc_ecc_free(&rootKey);
21620+
wc_FreeRng(&rng);
21621+
#endif
21622+
return EXPECT_RESULT();
21623+
}
21624+
2140521625
static int test_MakeCertWith0Ser(void)
2140621626
{
2140721627
EXPECT_DECLS;
@@ -35277,6 +35497,8 @@ TEST_CASE testCases[] = {
3527735497
TEST_DECL(test_wc_ParseCert),
3527835498
TEST_DECL(test_wc_ParseCert_Error),
3527935499
TEST_DECL(test_MakeCertWithPathLen),
35500+
TEST_DECL(test_PathLenSelfIssued),
35501+
TEST_DECL(test_PathLenNoKeyUsage),
3528035502
TEST_DECL(test_MakeCertWith0Ser),
3528135503
TEST_DECL(test_MakeCertWithCaFalse),
3528235504
#ifdef WOLFSSL_CERT_SIGN_CB

0 commit comments

Comments
 (0)