mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-05 19:00:49 +02:00
Fix TLSX_Parse to check dup ECH
This commit is contained in:
@@ -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";
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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
@@ -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. */
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) && \
|
||||
|
||||
Reference in New Issue
Block a user