Fix TLSX_Parse to check dup ECH

This commit is contained in:
Eric Blankenhorn
2026-04-10 08:50:28 -05:00
parent 178e10e42a
commit 8f73ae460d
10 changed files with 562 additions and 14 deletions
+20
View File
@@ -21862,6 +21862,23 @@ int DoApplicationData(WOLFSSL* ssl, byte* input, word32* inOutIdx, int sniff)
}
#endif
/* Rate-limit empty application data records to prevent DoS */
if (dataSz == 0) {
if (++ssl->options.emptyRecordCount >= WOLFSSL_MAX_EMPTY_RECORDS) {
WOLFSSL_MSG("Too many empty records");
#ifdef WOLFSSL_EXTRA_ALERTS
if (sniff == NO_SNIFF) {
SendAlert(ssl, alert_fatal, unexpected_message);
}
#endif
WOLFSSL_ERROR_VERBOSE(EMPTY_RECORD_LIMIT_E);
return EMPTY_RECORD_LIMIT_E;
}
}
else {
ssl->options.emptyRecordCount = 0;
}
/* read data */
if (dataSz) {
int rawSz = dataSz; /* keep raw size for idx adjustment */
@@ -27573,6 +27590,9 @@ const char* wolfSSL_ERR_reason_error_string(unsigned long e)
case ALERT_COUNT_E:
return "Alert Count exceeded error";
case EMPTY_RECORD_LIMIT_E:
return "Too many empty records error";
case EXT_MISSING:
return "Required TLS extension missing";
+3
View File
@@ -17043,6 +17043,9 @@ int TLSX_Parse(WOLFSSL* ssl, const byte* input, word16 length, byte msgType,
#ifdef WOLFSSL_QUIC
|| (type == TLSX_KEY_QUIC_TP_PARAMS_DRAFT)
#endif
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
|| (type == TLSX_ECH)
#endif
#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_DUAL_ALG_CERTS)
|| (type == TLSX_CKS)
#endif
+5 -3
View File
@@ -2638,9 +2638,10 @@ static int EncryptTls13(WOLFSSL* ssl, byte* output, const byte* input,
#endif
#ifdef WOLFSSL_CIPHER_TEXT_CHECK
if (ssl->specs.bulk_cipher_algorithm != wolfssl_cipher_null) {
if (ssl->specs.bulk_cipher_algorithm != wolfssl_cipher_null &&
dataSz >= WOLFSSL_CIPHER_CHECK_SZ) {
XMEMCPY(ssl->encrypt.sanityCheck, input,
min(dataSz, sizeof(ssl->encrypt.sanityCheck)));
sizeof(ssl->encrypt.sanityCheck));
}
#endif
@@ -2824,8 +2825,9 @@ static int EncryptTls13(WOLFSSL* ssl, byte* output, const byte* input,
#ifdef WOLFSSL_CIPHER_TEXT_CHECK
if (ssl->specs.bulk_cipher_algorithm != wolfssl_cipher_null &&
dataSz >= WOLFSSL_CIPHER_CHECK_SZ &&
XMEMCMP(output, ssl->encrypt.sanityCheck,
min(dataSz, sizeof(ssl->encrypt.sanityCheck))) == 0) {
sizeof(ssl->encrypt.sanityCheck)) == 0) {
WOLFSSL_MSG("EncryptTls13 sanity check failed! Glitch?");
return ENCRYPT_ERROR;
+222
View File
@@ -21402,6 +21402,226 @@ static int test_MakeCertWithPathLen(void)
return EXPECT_RESULT();
}
static int test_PathLenSelfIssued(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_CERT_REQ) && !defined(NO_ASN_TIME) && \
defined(WOLFSSL_CERT_GEN) && defined(HAVE_ECC) && \
defined(WOLFSSL_CERT_EXT) && !defined(NO_CERTS) && \
(!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH))
Cert cert;
DecodedCert decodedCert;
byte rootDer[FOURK_BUF];
byte icaDer[FOURK_BUF];
byte entityDer[FOURK_BUF];
int rootDerSz = 0;
int icaDerSz = 0;
int entityDerSz = 0;
WC_RNG rng;
ecc_key rootKey;
ecc_key icaKey;
ecc_key entityKey;
WOLFSSL_CERT_MANAGER* cm = NULL;
XMEMSET(&rng, 0, sizeof(WC_RNG));
XMEMSET(&rootKey, 0, sizeof(ecc_key));
XMEMSET(&icaKey, 0, sizeof(ecc_key));
XMEMSET(&entityKey, 0, sizeof(ecc_key));
ExpectIntEQ(wc_InitRng(&rng), 0);
ExpectIntEQ(wc_ecc_init(&rootKey), 0);
ExpectIntEQ(wc_ecc_init(&icaKey), 0);
ExpectIntEQ(wc_ecc_init(&entityKey), 0);
ExpectIntEQ(wc_ecc_make_key(&rng, 32, &rootKey), 0);
ExpectIntEQ(wc_ecc_make_key(&rng, 32, &icaKey), 0);
ExpectIntEQ(wc_ecc_make_key(&rng, 32, &entityKey), 0);
/* Step 1: Create root CA with pathLen=0 */
ExpectIntEQ(wc_InitCert(&cert), 0);
(void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.state, "MT", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.org, "TestCA", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.unit, "Test", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.commonName, "TestRootCA", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.email, "root@test.com", CTC_NAME_SIZE);
cert.selfSigned = 1;
cert.isCA = 1;
cert.pathLen = 0;
cert.pathLenSet = 1;
cert.sigType = CTC_SHA256wECDSA;
cert.keyUsage = KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN;
ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey),
0);
ExpectIntGE(wc_MakeCert(&cert, rootDer, FOURK_BUF, NULL, &rootKey, &rng),
0);
ExpectIntGE(rootDerSz = wc_SignCert(cert.bodySz, cert.sigType, rootDer,
FOURK_BUF, NULL, &rootKey, &rng), 0);
/* Step 2: Create self-issued intermediate (same subject DN as root,
* different key, signed by root) - this should be blocked by pathLen=0 */
ExpectIntEQ(wc_InitCert(&cert), 0);
cert.selfSigned = 0;
cert.isCA = 1;
cert.sigType = CTC_SHA256wECDSA;
cert.keyUsage = KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN;
/* Set both subject and issuer from the root cert so they match */
ExpectIntEQ(wc_SetSubjectBuffer(&cert, rootDer, rootDerSz), 0);
ExpectIntEQ(wc_SetIssuerBuffer(&cert, rootDer, rootDerSz), 0);
ExpectIntEQ(wc_SetAuthKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey), 0);
ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &icaKey),
0);
ExpectIntGE(wc_MakeCert(&cert, icaDer, FOURK_BUF, NULL, &icaKey, &rng), 0);
ExpectIntGE(icaDerSz = wc_SignCert(cert.bodySz, cert.sigType, icaDer,
FOURK_BUF, NULL, &rootKey, &rng), 0);
/* Step 3: Create entity cert signed by the intermediate */
ExpectIntEQ(wc_InitCert(&cert), 0);
cert.selfSigned = 0;
cert.isCA = 0;
cert.sigType = CTC_SHA256wECDSA;
(void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.state, "MT", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.org, "TestEntity", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.commonName, "entity.test", CTC_NAME_SIZE);
ExpectIntEQ(wc_SetIssuerBuffer(&cert, icaDer, icaDerSz), 0);
ExpectIntEQ(wc_SetAuthKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &icaKey), 0);
ExpectIntGE(wc_MakeCert(&cert, entityDer, FOURK_BUF, NULL, &entityKey,
&rng), 0);
ExpectIntGE(entityDerSz = wc_SignCert(cert.bodySz, cert.sigType, entityDer,
FOURK_BUF, NULL, &icaKey, &rng), 0);
/* Step 4: Load root CA into cert manager */
ExpectNotNull(cm = wolfSSL_CertManagerNew());
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, rootDer, rootDerSz,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
/* Step 5: Parse the self-issued intermediate as a chain cert.
* This simulates TLS chain verification where the intermediate is
* received as part of the certificate chain.
* Root CA has pathLen=0, so it should NOT be allowed to sign any
* intermediate CA (including self-issued ones).
* BUG: wolfSSL sets selfSigned=1 for this cert (issuer==subject DN),
* which causes the pathLen enforcement to be entirely skipped. */
wc_InitDecodedCert(&decodedCert, icaDer, (word32)icaDerSz, NULL);
ExpectIntEQ(wc_ParseCert(&decodedCert, CHAIN_CERT_TYPE, VERIFY,
cm), WC_NO_ERR_TRACE(ASN_PATHLEN_INV_E));
wc_FreeDecodedCert(&decodedCert);
wolfSSL_CertManagerFree(cm);
wc_ecc_free(&entityKey);
wc_ecc_free(&icaKey);
wc_ecc_free(&rootKey);
wc_FreeRng(&rng);
#endif
return EXPECT_RESULT();
}
static int test_PathLenNoKeyUsage(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_CERT_REQ) && !defined(NO_ASN_TIME) && \
defined(WOLFSSL_CERT_GEN) && defined(HAVE_ECC) && \
defined(WOLFSSL_CERT_EXT) && !defined(NO_CERTS) && \
(!defined(NO_WOLFSSL_CLIENT) || !defined(WOLFSSL_NO_CLIENT_AUTH))
Cert cert;
DecodedCert decodedCert;
byte rootDer[FOURK_BUF];
byte icaDer[FOURK_BUF];
int rootDerSz = 0;
int icaDerSz = 0;
WC_RNG rng;
ecc_key rootKey;
ecc_key icaKey;
WOLFSSL_CERT_MANAGER* cm = NULL;
XMEMSET(&rng, 0, sizeof(WC_RNG));
XMEMSET(&rootKey, 0, sizeof(ecc_key));
XMEMSET(&icaKey, 0, sizeof(ecc_key));
ExpectIntEQ(wc_InitRng(&rng), 0);
ExpectIntEQ(wc_ecc_init(&rootKey), 0);
ExpectIntEQ(wc_ecc_init(&icaKey), 0);
ExpectIntEQ(wc_ecc_make_key(&rng, 32, &rootKey), 0);
ExpectIntEQ(wc_ecc_make_key(&rng, 32, &icaKey), 0);
/* Step 1: Create root CA with pathLen=0 and KeyUsage */
ExpectIntEQ(wc_InitCert(&cert), 0);
(void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.state, "MT", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.org, "TestCA2", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.unit, "Test", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.commonName, "TestRootCA2", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.email, "root@test2.com", CTC_NAME_SIZE);
cert.selfSigned = 1;
cert.isCA = 1;
cert.pathLen = 0;
cert.pathLenSet = 1;
cert.sigType = CTC_SHA256wECDSA;
cert.keyUsage = KEYUSE_KEY_CERT_SIGN | KEYUSE_CRL_SIGN;
ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey),
0);
ExpectIntGE(wc_MakeCert(&cert, rootDer, FOURK_BUF, NULL, &rootKey, &rng),
0);
ExpectIntGE(rootDerSz = wc_SignCert(cert.bodySz, cert.sigType, rootDer,
FOURK_BUF, NULL, &rootKey, &rng), 0);
/* Step 2: Create intermediate CA WITHOUT KeyUsage extension.
* Per RFC 5280, when KeyUsage is absent all uses are valid.
* The root's pathLen=0 should still block this intermediate CA.
* BUG: pathLen check requires extKeyUsageSet which is false when
* KeyUsage is absent, so the check is skipped entirely. */
ExpectIntEQ(wc_InitCert(&cert), 0);
cert.selfSigned = 0;
cert.isCA = 1;
cert.sigType = CTC_SHA256wECDSA;
/* Intentionally do NOT set keyUsage - test that pathLen is still enforced */
cert.keyUsage = 0;
(void)XSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.state, "MT", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.locality, "Bozeman", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.org, "TestICA", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.unit, "Test", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.commonName, "TestICA-NoKU", CTC_NAME_SIZE);
(void)XSTRNCPY(cert.subject.email, "ica@test2.com", CTC_NAME_SIZE);
ExpectIntEQ(wc_SetIssuerBuffer(&cert, rootDer, rootDerSz), 0);
ExpectIntEQ(wc_SetAuthKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &rootKey), 0);
ExpectIntEQ(wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, &icaKey),
0);
ExpectIntGE(wc_MakeCert(&cert, icaDer, FOURK_BUF, NULL, &icaKey, &rng), 0);
ExpectIntGE(icaDerSz = wc_SignCert(cert.bodySz, cert.sigType, icaDer,
FOURK_BUF, NULL, &rootKey, &rng), 0);
/* Step 3: Load root CA into cert manager */
ExpectNotNull(cm = wolfSSL_CertManagerNew());
ExpectIntEQ(wolfSSL_CertManagerLoadCABuffer(cm, rootDer, rootDerSz,
WOLFSSL_FILETYPE_ASN1), WOLFSSL_SUCCESS);
/* Step 4: Parse the intermediate (no KeyUsage) as a chain cert.
* Root CA has pathLen=0, this intermediate CA should be rejected.
* The intermediate does NOT have the KeyUsage extension, but per
* RFC 5280 4.2.1.3 all key uses are valid when the extension is
* absent, so pathLen must still be enforced. */
wc_InitDecodedCert(&decodedCert, icaDer, (word32)icaDerSz, NULL);
ExpectIntEQ(wc_ParseCert(&decodedCert, CHAIN_CERT_TYPE, VERIFY,
cm), WC_NO_ERR_TRACE(ASN_PATHLEN_INV_E));
wc_FreeDecodedCert(&decodedCert);
wolfSSL_CertManagerFree(cm);
wc_ecc_free(&icaKey);
wc_ecc_free(&rootKey);
wc_FreeRng(&rng);
#endif
return EXPECT_RESULT();
}
static int test_MakeCertWith0Ser(void)
{
EXPECT_DECLS;
@@ -35277,6 +35497,8 @@ TEST_CASE testCases[] = {
TEST_DECL(test_wc_ParseCert),
TEST_DECL(test_wc_ParseCert_Error),
TEST_DECL(test_MakeCertWithPathLen),
TEST_DECL(test_PathLenSelfIssued),
TEST_DECL(test_PathLenNoKeyUsage),
TEST_DECL(test_MakeCertWith0Ser),
TEST_DECL(test_MakeCertWithCaFalse),
#ifdef WOLFSSL_CERT_SIGN_CB
+255
View File
@@ -2922,6 +2922,108 @@ int test_tls13_duplicate_extension(void)
}
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) && \
!defined(NO_WOLFSSL_SERVER) && !defined(NO_FILESYSTEM) && \
(!defined(NO_RSA) || defined(HAVE_ECC))
static int DupEchSend(WOLFSSL* ssl, char* buf, int sz, void* ctx)
{
(void)ssl;
(void)buf;
(void)sz;
(void)ctx;
return sz;
}
static int DupEchRecv(WOLFSSL* ssl, char* buf, int sz, void* ctx)
{
WOLFSSL_BUFFER_INFO* msg = (WOLFSSL_BUFFER_INFO*)ctx;
int len = (int)msg->length;
(void)ssl;
(void)sz;
if (len > sz)
len = sz;
XMEMCPY(buf, msg->buffer, len);
msg->buffer += len;
msg->length -= len;
return len;
}
#endif
/* Test detection of duplicate ECH extension (type 0xfe0d) in ClientHello.
* ECH has a semaphore mapping in TLSX_ToSemaphore() and needs to be included
* in the duplicate-detection gate in TLSX_Parse(). RFC 8446 section 4.2
* requires rejecting messages with duplicate extensions.
*/
int test_tls13_duplicate_ech_extension(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH) && \
!defined(NO_WOLFSSL_SERVER) && !defined(NO_FILESYSTEM) && \
(!defined(NO_RSA) || defined(HAVE_ECC))
/* TLS 1.3 ClientHello with two ECH extensions (type 0xfe0d).
* Extensions block contains: supported_versions + ECH + ECH (dup). */
const unsigned char clientHelloDupEch[] = {
0x16, 0x03, 0x03, 0x00, 0x40, 0x01, 0x00, 0x00,
0x3c, 0x03, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
0x01, 0x01, 0x01, 0x00, 0x00, 0x02, 0x13, 0x01,
0x01, 0x00, 0x00, 0x11, 0x00, 0x2b, 0x00, 0x03,
0x02, 0x03, 0x04, 0xfe, 0x0d, 0x00, 0x01, 0x00,
0xfe, 0x0d, 0x00, 0x01, 0x00
};
WOLFSSL_BUFFER_INFO msg;
const char* testCertFile;
const char* testKeyFile;
WOLFSSL_CTX *ctx = NULL;
WOLFSSL *ssl = NULL;
#ifndef NO_RSA
testCertFile = svrCertFile;
testKeyFile = svrKeyFile;
#elif defined(HAVE_ECC)
testCertFile = eccCertFile;
testKeyFile = eccKeyFile;
#endif
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method()));
ExpectTrue(wolfSSL_CTX_use_certificate_file(ctx, testCertFile,
CERT_FILETYPE));
ExpectTrue(wolfSSL_CTX_use_PrivateKey_file(ctx, testKeyFile,
CERT_FILETYPE));
/* Read from 'msg'. */
wolfSSL_SetIORecv(ctx, DupEchRecv);
/* No where to send to - dummy sender. */
wolfSSL_SetIOSend(ctx, DupEchSend);
ssl = wolfSSL_new(ctx);
ExpectNotNull(ssl);
msg.buffer = (unsigned char*)clientHelloDupEch;
msg.length = (unsigned int)sizeof(clientHelloDupEch);
wolfSSL_SetIOReadCtx(ssl, &msg);
ExpectIntNE(wolfSSL_accept(ssl), WOLFSSL_SUCCESS);
/* Can return duplicate ext error or socket error if the peer closed
* down while sending alert. */
if (wolfSSL_get_error(ssl, 0) != WC_NO_ERR_TRACE(SOCKET_ERROR_E)) {
ExpectIntEQ(wolfSSL_get_error(ssl, 0),
WC_NO_ERR_TRACE(DUPLICATE_TLS_EXT_E));
}
wolfSSL_free(ssl);
wolfSSL_CTX_free(ctx);
#endif
return EXPECT_RESULT();
}
int test_key_share_mismatch(void)
{
EXPECT_DECLS;
@@ -3677,6 +3779,159 @@ int test_tls13_pqc_hybrid_truncated_keyshare(void)
* (32 bytes) does not cause an unsigned integer underflow / OOB read in
* SetTicket. Uses a full memio handshake, then injects a crafted
* NewSessionTicket with a 5-byte ticket into the client's read path. */
int test_tls13_empty_record_limit(void)
{
EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_TLS13)
struct test_memio_ctx test_ctx;
WOLFSSL_CTX *ctx_c = NULL, *ctx_s = NULL;
WOLFSSL *ssl_c = NULL, *ssl_s = NULL;
int recSz;
int numRecs = WOLFSSL_MAX_EMPTY_RECORDS + 1;
byte rec[128]; /* buffer for one encrypted record */
byte *allRecs = NULL;
int i;
char buf[64];
/* Test 1: Exceeding the empty record limit returns an error. */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
/* Consume any post-handshake messages (e.g. NewSessionTicket). */
wolfSSL_read(ssl_c, buf, sizeof(buf));
test_memio_clear_buffer(&test_ctx, 0);
test_memio_clear_buffer(&test_ctx, 1);
/* Get the size of an encrypted zero-length app data record. */
recSz = BuildTls13Message(ssl_c, NULL, 0, NULL, 0,
application_data, 0, 1, 0);
ExpectIntGT(recSz, 0);
ExpectIntLE(recSz, (int)sizeof(rec));
/* Build all empty records into one contiguous buffer. */
if (EXPECT_SUCCESS()) {
allRecs = (byte*)XMALLOC((size_t)(recSz * numRecs), NULL,
DYNAMIC_TYPE_TMP_BUFFER);
ExpectNotNull(allRecs);
}
for (i = 0; i < numRecs && EXPECT_SUCCESS(); i++) {
XMEMSET(rec, 0, sizeof(rec));
ExpectIntEQ(BuildTls13Message(ssl_c, rec, (int)sizeof(rec), rec +
RECORD_HEADER_SZ, 0, application_data, 0, 0, 0),
recSz);
XMEMCPY(allRecs + i * recSz, rec, (size_t)recSz);
}
/* Inject all records as a single message. */
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, (const char*)allRecs,
recSz * numRecs), 0);
/* The server's wolfSSL_read should fail with EMPTY_RECORD_LIMIT_E. */
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)),
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
WC_NO_ERR_TRACE(EMPTY_RECORD_LIMIT_E));
XFREE(allRecs, NULL, DYNAMIC_TYPE_TMP_BUFFER);
allRecs = NULL;
wolfSSL_free(ssl_c);
ssl_c = NULL;
wolfSSL_free(ssl_s);
ssl_s = NULL;
wolfSSL_CTX_free(ctx_c);
ctx_c = NULL;
wolfSSL_CTX_free(ctx_s);
ctx_s = NULL;
/* Test 2: Counter resets on non-empty record.
* Send (limit - 1) empty records, then 1 non-empty, then (limit - 1)
* more empty records. Should succeed without hitting the limit. */
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfTLSv1_3_client_method, wolfTLSv1_3_server_method), 0);
ExpectIntEQ(test_memio_do_handshake(ssl_c, ssl_s, 10, NULL), 0);
wolfSSL_read(ssl_c, buf, sizeof(buf));
test_memio_clear_buffer(&test_ctx, 0);
test_memio_clear_buffer(&test_ctx, 1);
recSz = BuildTls13Message(ssl_c, NULL, 0, NULL, 0,
application_data, 0, 1, 0);
ExpectIntGT(recSz, 0);
{
int emptyBefore = WOLFSSL_MAX_EMPTY_RECORDS - 1;
int emptyAfter = WOLFSSL_MAX_EMPTY_RECORDS - 1;
int dataRecSz;
byte dataRec[128];
byte payload[1] = { 'a' };
int totalSz;
dataRecSz = BuildTls13Message(ssl_c, NULL, 0, NULL, 1,
application_data, 0, 1, 0);
ExpectIntGT(dataRecSz, 0);
totalSz = recSz * (emptyBefore + emptyAfter) + dataRecSz;
if (EXPECT_SUCCESS()) {
allRecs = (byte*)XMALLOC((size_t)totalSz, NULL,
DYNAMIC_TYPE_TMP_BUFFER);
ExpectNotNull(allRecs);
}
/* Build (limit - 1) empty records */
for (i = 0; i < emptyBefore && EXPECT_SUCCESS(); i++) {
XMEMSET(rec, 0, sizeof(rec));
ExpectIntEQ(BuildTls13Message(ssl_c, rec, (int)sizeof(rec),
rec + RECORD_HEADER_SZ, 0, application_data,
0, 0, 0), recSz);
XMEMCPY(allRecs + i * recSz, rec, (size_t)recSz);
}
/* Build 1 non-empty record */
if (EXPECT_SUCCESS()) {
XMEMSET(dataRec, 0, sizeof(dataRec));
XMEMCPY(dataRec + RECORD_HEADER_SZ, payload, sizeof(payload));
ExpectIntEQ(BuildTls13Message(ssl_c, dataRec, (int)sizeof(dataRec),
dataRec + RECORD_HEADER_SZ, 1, application_data,
0, 0, 0), dataRecSz);
XMEMCPY(allRecs + emptyBefore * recSz, dataRec,
(size_t)dataRecSz);
}
/* Build (limit - 1) more empty records */
for (i = 0; i < emptyAfter && EXPECT_SUCCESS(); i++) {
XMEMSET(rec, 0, sizeof(rec));
ExpectIntEQ(BuildTls13Message(ssl_c, rec, (int)sizeof(rec),
rec + RECORD_HEADER_SZ, 0, application_data,
0, 0, 0), recSz);
XMEMCPY(allRecs + emptyBefore * recSz + dataRecSz + i * recSz,
rec, (size_t)recSz);
}
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
(const char*)allRecs, totalSz), 0);
}
/* wolfSSL_read should return the 1-byte payload. The counter resets
* on the non-empty record so neither batch of (limit - 1) empties
* triggers the error. */
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)), 1);
ExpectIntEQ(buf[0], 'a');
XFREE(allRecs, NULL, DYNAMIC_TYPE_TMP_BUFFER);
wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
#endif
return EXPECT_RESULT();
}
int test_tls13_short_session_ticket(void)
{
EXPECT_DECLS;
+4
View File
@@ -36,6 +36,7 @@ int test_tls13_ch2_different_cs(void);
int test_tls13_sg_missing(void);
int test_tls13_ks_missing(void);
int test_tls13_duplicate_extension(void);
int test_tls13_duplicate_ech_extension(void);
int test_key_share_mismatch(void);
int test_tls13_middlebox_compat_empty_session_id(void);
int test_tls13_plaintext_alert(void);
@@ -44,6 +45,7 @@ int test_tls13_unknown_ext_rejected(void);
int test_tls13_cert_req_sigalgs(void);
int test_tls13_derive_keys_no_key(void);
int test_tls13_pqc_hybrid_truncated_keyshare(void);
int test_tls13_empty_record_limit(void);
int test_tls13_short_session_ticket(void);
#define TEST_TLS13_DECLS \
@@ -59,6 +61,7 @@ int test_tls13_short_session_ticket(void);
TEST_DECL_GROUP("tls13", test_tls13_sg_missing), \
TEST_DECL_GROUP("tls13", test_tls13_ks_missing), \
TEST_DECL_GROUP("tls13", test_tls13_duplicate_extension), \
TEST_DECL_GROUP("tls13", test_tls13_duplicate_ech_extension), \
TEST_DECL_GROUP("tls13", test_key_share_mismatch), \
TEST_DECL_GROUP("tls13", test_tls13_middlebox_compat_empty_session_id), \
TEST_DECL_GROUP("tls13", test_tls13_plaintext_alert), \
@@ -66,6 +69,7 @@ int test_tls13_short_session_ticket(void);
TEST_DECL_GROUP("tls13", test_tls13_cert_req_sigalgs), \
TEST_DECL_GROUP("tls13", test_tls13_derive_keys_no_key), \
TEST_DECL_GROUP("tls13", test_tls13_pqc_hybrid_truncated_keyshare), \
TEST_DECL_GROUP("tls13", test_tls13_empty_record_limit), \
TEST_DECL_GROUP("tls13", test_tls13_short_session_ticket), \
TEST_DECL_GROUP("tls13", test_tls13_unknown_ext_rejected)
+39 -10
View File
@@ -22425,16 +22425,24 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm,
if (cert->pathLengthSet)
cert->maxPathLen = cert->pathLength;
if (!cert->selfSigned) {
/* Need to perform a pathlen check on anything that will be used
* to sign certificates later on. Otherwise, pathLen doesn't
* mean anything.
* Nothing to check if we don't have the issuer of this cert. */
if (type != CERT_TYPE && cert->isCA && cert->extKeyUsageSet &&
(cert->extKeyUsage & KEYUSE_KEY_CERT_SIGN) != 0 && cert->ca) {
/* RFC 5280 6.1.4: Check issuer's pathLen constraint.
* Need to perform a pathlen check on anything that will be used
* to sign certificates later on. Otherwise, pathLen doesn't
* mean anything.
* Nothing to check if we don't have the issuer of this cert.
*
* Per RFC 5280, when the KeyUsage extension is absent, all key
* uses are implicitly valid (including keyCertSign), so pathLen
* enforcement must not be gated on KeyUsage presence. */
if (type != CERT_TYPE && cert->isCA && cert->ca &&
(!cert->extKeyUsageSet ||
(cert->extKeyUsage & KEYUSE_KEY_CERT_SIGN) != 0)) {
if (!cert->selfSigned) {
/* RFC 5280 6.1.4(l): Non-self-issued cert decrements and
* checks the issuer's max_path_length. */
if (cert->ca->maxPathLen == 0) {
/* This cert CAN NOT be used as an intermediate cert. The
* issuer does not allow it. */
/* This cert CAN NOT be used as an intermediate cert.
* The issuer does not allow it. */
cert->maxPathLen = 0;
if (verify != NO_VERIFY) {
WOLFSSL_MSG("\tNon-entity cert, maxPathLen is 0");
@@ -22444,7 +22452,28 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, void* cm,
}
}
else {
cert->maxPathLen = (byte)min(cert->ca->maxPathLen - 1U,
cert->maxPathLen = (word16)min(cert->ca->maxPathLen - 1U,
cert->maxPathLen);
}
}
else {
/* RFC 5280 6.1.4(l): Self-issued certs do NOT decrement
* max_path_length, but the issuer's constraint still
* applies. A self-issued cert from a CA with maxPathLen=0
* cannot act as an intermediate CA. */
if (cert->ca->maxPathLen == 0) {
cert->maxPathLen = 0;
if (verify != NO_VERIFY) {
WOLFSSL_MSG("\tSelf-issued cert, maxPathLen is 0");
WOLFSSL_MSG("\tmaxPathLen status: ERROR");
WOLFSSL_ERROR_VERBOSE(ASN_PATHLEN_INV_E);
return ASN_PATHLEN_INV_E;
}
}
else {
/* Self-issued: honor issuer's constraint without
* decrementing. */
cert->maxPathLen = (word16)min(cert->ca->maxPathLen,
cert->maxPathLen);
}
}
+3 -1
View File
@@ -240,7 +240,9 @@ enum wolfSSL_ErrorCodes {
SESSION_TICKET_NONCE_OVERFLOW = -517, /* Session ticket nonce overflow */
WOLFSSL_LAST_E = -517
EMPTY_RECORD_LIMIT_E = -518, /* Too many empty records received */
WOLFSSL_LAST_E = -518
/* codes -1000 to -1999 are reserved for wolfCrypt. */
};
+1
View File
@@ -5216,6 +5216,7 @@ struct Options {
byte asyncState; /* sub-state for enum asyncState */
byte buildMsgState; /* sub-state for enum buildMsgState */
byte alertCount; /* detect warning dos attempt */
byte emptyRecordCount; /* detect empty record dos attempt */
#ifdef WOLFSSL_MULTICAST
word16 mcastID; /* Multicast group ID */
#endif
+10
View File
@@ -4143,6 +4143,16 @@ extern void uITRON4_free(void *p) ;
#ifndef WOLFSSL_ALERT_COUNT_MAX
#define WOLFSSL_ALERT_COUNT_MAX 5
#endif
#if WOLFSSL_ALERT_COUNT_MAX > 255
#error "WOLFSSL_ALERT_COUNT_MAX must be <= 255 (stored in a byte)"
#endif
#ifndef WOLFSSL_MAX_EMPTY_RECORDS
#define WOLFSSL_MAX_EMPTY_RECORDS 32
#endif
#if WOLFSSL_MAX_EMPTY_RECORDS > 255
#error "WOLFSSL_MAX_EMPTY_RECORDS must be <= 255 (stored in a byte)"
#endif
/* Enable blinding by default for C-only, non-small curve25519 implementation */
#if defined(HAVE_CURVE25519) && !defined(CURVE25519_SMALL) && \