From ac540f9286bbc8745a3fb3562365b52404a8758c Mon Sep 17 00:00:00 2001 From: Sameeh Date: Fri, 30 Oct 2026 18:14:27 +0200 Subject: [PATCH] Check the dates of cached chain CAs when they are used After a handshake, ProcessPeerCerts() adds each verified CA from the peer's chain to the certificate manager as a WOLFSSL_CHAIN_CA. The Signer kept no validity dates, so a later peer that sent only its leaf was verified with the cached CA even after that CA had expired. Sending the expired CA, or using a new context, failed with ASN_AFTER_DATE_E. Keep the DER encoded notBefore and notAfter of a chain CA in its Signer, and after parsing each peer certificate walk from its issuer up to the self-signed trust anchor, checking the dates of every chain CA on the way. The error is reported as ASN_BEFORE_DATE_E or ASN_AFTER_DATE_E on that certificate, so a verify callback can accept it as before. CAs the application loaded are trust anchors and are not checked again. The walk uses the name constraints issuer lookup, exported as wolfssl_local_FindSignerByAkidOrName(). With IGNORE_NAME_CONSTRAINTS only the direct issuer is checked. A CA renewed with the same key is not added again, so the peer sending it now updates the dates of the cached CA. Otherwise the cached copy would fail a chain that includes a valid renewed CA. wolfSSL_CertManagerVerifyBuffer() checks cached chain CAs the same way, and wolfSSL_CertManagerLoadCABufferType() stores the dates of a CA loaded as WOLFSSL_CHAIN_CA. Add test_wolfSSL_chain_ca_cached_dates, which reuses one client context for a full chain, a leaf alone before and after the CA expires, a verify callback accepting the error and a renewed CA, and checks certificate manager verification. --- src/internal.c | 24 ++++ src/ssl_certman.c | 167 +++++++++++++++++++++++++ tests/api/test_ssl_cert.c | 248 ++++++++++++++++++++++++++++++++++++++ tests/api/test_ssl_cert.h | 2 + wolfcrypt/src/asn.c | 50 +++++++- wolfssl/internal.h | 6 + wolfssl/wolfcrypt/asn.h | 21 ++++ 7 files changed, 515 insertions(+), 3 deletions(-) diff --git a/src/internal.c b/src/internal.c index cd897621c54..6e8ef206a7b 100644 --- a/src/internal.c +++ b/src/internal.c @@ -17552,6 +17552,15 @@ PRAGMA_GCC_DIAG_POP } #endif /* HAVE_RPK */ +#ifndef NO_ASN_TIME_CHECK + /* The issuer may be a CA cached from an earlier peer chain that has + * expired since. Report it as a date error on this certificate. */ + if (ret == 0 && (verify == VERIFY || verify == VERIFY_NAME)) { + ret = wolfssl_local_CheckChainCADates(SSL_CM(ssl), args->dCert->ca, + extraSigners); + } +#endif + /* perform below checks for date failure cases */ if (ret == 0 || ret == WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E) || @@ -19271,6 +19280,9 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, #endif /* WOLFSSL_TRUST_PEER_CERT */ ) { int skipAddCA = 0; + #ifndef NO_ASN_TIME_CHECK + int caVerifyRet = 0; + #endif #if defined(HAVE_OCSP) && defined(HAVE_CRL) int ocspNoUrl = 0; int ocspStapleDeferred = 0; @@ -19581,6 +19593,9 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, } #endif /* defined(__APPLE__) && defined(WOLFSSL_SYS_CA_CERTS) */ + #ifndef NO_ASN_TIME_CHECK + caVerifyRet = ret; + #endif /* Do verify callback */ ret = DoVerifyCallback(SSL_CM(ssl), ssl, ret, args); if (ssl->options.verifyNone && @@ -19651,6 +19666,15 @@ int ProcessPeerCerts(WOLFSSL* ssl, byte* input, word32* inOutIdx, ret = 0; } } + #ifndef NO_ASN_TIME_CHECK + else if (caVerifyRet == 0 && subjectHash != NULL) { + /* A CA renewed with the same key is not added + * again. Take its dates so the cached CA does + * not expire with the old certificate. */ + ret = wolfssl_local_SetChainCADates(SSL_CM(ssl), + subjectHash, args->dCert); + } + #endif } #if defined(OPENSSL_EXTRA) || defined(OPENSSL_EXTRA_X509_SMALL) /* A chain CA refused for its key usage fails the verify diff --git a/src/ssl_certman.c b/src/ssl_certman.c index fb9490525bc..571e44a27f3 100644 --- a/src/ssl_certman.c +++ b/src/ssl_certman.c @@ -664,6 +664,15 @@ int wolfSSL_CertManagerLoadCABufferType(WOLFSSL_CERT_MANAGER* cm, } else { ret = SetCAType(cm, dCert->extSubjKeyId, type); } + #ifndef NO_ASN_TIME_CHECK + /* A chain CA is not a trust anchor and its dates are checked + * each time it is used. */ + if (ret == WOLFSSL_SUCCESS && type == WOLFSSL_CHAIN_CA && + wolfssl_local_SetChainCADates(cm, dCert->extSubjKeyId, + dCert) != 0) { + ret = WOLFSSL_FATAL_ERROR; + } + #endif } if (dCert) { @@ -925,6 +934,12 @@ int CM_VerifyBuffer_ex(WOLFSSL_CERT_MANAGER* cm, const unsigned char* buff, * against a known CA. */ ret = ParseCertRelative(cert, CERT_TYPE, VERIFY, cm, NULL); } +#ifndef NO_ASN_TIME_CHECK + if (ret == 0) { + /* A CA cached from a peer's chain may have expired since. */ + ret = wolfssl_local_CheckChainCADates(cm, cert->ca, NULL); + } +#endif #ifdef HAVE_CRL if ((ret == 0) && cm->crlEnabled) { @@ -2812,6 +2827,158 @@ int AlreadySigner(WOLFSSL_CERT_MANAGER* cm, byte* hash) return ret; } +#ifndef NO_ASN_TIME_CHECK +/* Check the dates of each CA cached from a peer's chain on the path from ca up + * to the self-signed trust anchor. Such a CA is not a trust anchor, so it is + * checked each time it is used and not only when the peer that sent it was + * verified. Other CAs keep no dates and are skipped. With + * IGNORE_NAME_CONSTRAINTS there is no issuer lookup and only ca is checked. + * + * @param [in] cm Certificate manager. + * @param [in] ca CA that verified the certificate. May be NULL. + * @param [in] pending Certificate Status Request v2 signers that are not in + * the certificate manager yet. May be NULL. + * @return 0 when no cached chain CA on the path is out of date. + * @return ASN_BEFORE_DATE_E when a cached chain CA is not valid yet. + * @return ASN_AFTER_DATE_E when a cached chain CA has expired. + * @return BAD_MUTEX_E when the CA lock cannot be taken. + * @return MAX_CHAIN_ERROR when the path is longer than + * WOLFSSL_MAX_CHAIN_DEPTH. + */ +int wolfssl_local_CheckChainCADates(WOLFSSL_CERT_MANAGER* cm, Signer* ca, + Signer* pending) +{ + int ret = 0; + int depth = 0; + int isChainCA; + Signer* prev = NULL; + Signer* parent; + SignerValidity validity; + + if (cm == NULL || wc_AsnGetSkipDateCheck()) + return 0; + + while (ret == 0 && ca != NULL) { + isChainCA = 0; + if (ca->validity != NULL) { + /* Another handshake can change the dates, and the application + * the type. */ + if (wc_LockMutex(&cm->caLock) != 0) { + ret = BAD_MUTEX_E; + break; + } + isChainCA = (ca->type == WOLFSSL_CHAIN_CA); + XMEMCPY(&validity, ca->validity, sizeof(validity)); + wc_UnLockMutex(&cm->caLock); + } + if (isChainCA) { + if (!XVALIDATE_DATE(&validity.notBefore[2], + validity.notBefore[0], ASN_BEFORE, + validity.notBefore[1])) { + WOLFSSL_MSG("Cached chain CA is not yet valid"); + ret = ASN_BEFORE_DATE_E; + break; + } + if (!XVALIDATE_DATE(&validity.notAfter[2], + validity.notAfter[0], ASN_AFTER, + validity.notAfter[1])) { + WOLFSSL_MSG("Cached chain CA has expired"); + ret = ASN_AFTER_DATE_E; + break; + } + } + if (ca->selfSigned) + break; + #ifndef IGNORE_NAME_CONSTRAINTS + parent = wolfssl_local_FindSignerByAkidOrName(cm, pending, ca); + #else + (void)pending; + parent = NULL; + #endif + /* Stop on missing parent, self-loop, or A->B->A cycle. */ + if (parent == NULL || parent == ca || parent == prev) + break; + if (++depth >= WOLFSSL_MAX_CHAIN_DEPTH) { + ret = MAX_CHAIN_ERROR; + break; + } + prev = ca; + ca = parent; + } + + if (ret != 0) + WOLFSSL_ERROR_VERBOSE(ret); + + return ret; +} + +/* Store the dates of cert on the CA cached from a peer's chain that it is the + * certificate for. This is used when the peer sends a CA that is already + * cached, for example one renewed with the same key, and when an application + * loads a CA as WOLFSSL_CHAIN_CA. A CA of any other type, or with a different + * public key, is left unchanged. + * + * @param [in] cm Certificate manager. + * @param [in] hash Hash identifying the CA, as passed to AlreadySigner(). + * @param [in] cert Certificate for the CA. + * @return 0 on success, including when there is nothing to change. + * @return BAD_FUNC_ARG when cm, hash or cert is NULL. + * @return ASN_DATE_SZ_E when a date in cert is too long. + * @return BAD_MUTEX_E when the CA lock cannot be taken. + * @return MEMORY_E when dynamic memory allocation fails. + */ +int wolfssl_local_SetChainCADates(WOLFSSL_CERT_MANAGER* cm, const byte* hash, + const DecodedCert* cert) +{ + int ret; + Signer* signer; + byte* subjectHash; + SignerValidity validity; + + if (cm == NULL || hash == NULL || cert == NULL) + return BAD_FUNC_ARG; + + ret = wolfssl_local_SetSignerValidity(&validity, cert); + if (ret == 0 && wc_LockMutex(&cm->caLock) != 0) + ret = BAD_MUTEX_E; + if (ret == 0) { + signer = cm->caTable[HashSigner(hash)]; + while (signer != NULL) { + #ifndef NO_SKID + subjectHash = signer->subjectKeyIdHash; + #else + subjectHash = signer->subjectNameHash; + #endif + if (XMEMCMP(hash, subjectHash, SIGNER_DIGEST_SIZE) == 0) + break; + signer = signer->next; + } + if (signer != NULL && signer->type == WOLFSSL_CHAIN_CA && + cert->publicKey != NULL && signer->publicKey != NULL && + signer->pubKeySize == cert->pubKeySize && + XMEMCMP(signer->publicKey, cert->publicKey, + cert->pubKeySize) == 0) { + if (signer->validity == NULL) { + signer->validity = (SignerValidity*)XMALLOC( + sizeof(SignerValidity), cm->heap, DYNAMIC_TYPE_SIGNER); + if (signer->validity == NULL) + ret = MEMORY_E; + else + XMEMCPY(signer->validity, &validity, sizeof(validity)); + } + else if (XMEMCMP(signer->validity, &validity, + sizeof(validity)) != 0) { + WOLFSSL_MSG("Updating dates of cached chain CA"); + XMEMCPY(signer->validity, &validity, sizeof(validity)); + } + } + wc_UnLockMutex(&cm->caLock); + } + + return ret; +} +#endif /* !NO_ASN_TIME_CHECK */ + #ifdef WOLFSSL_TRUST_PEER_CERT /* use the first 32 bits of the digest as the table row */ static WC_INLINE word32 TrustedPeerHashSigner(const byte* hash) diff --git a/tests/api/test_ssl_cert.c b/tests/api/test_ssl_cert.c index 51d37f9001c..ed3c9c687ed 100644 --- a/tests/api/test_ssl_cert.c +++ b/tests/api/test_ssl_cert.c @@ -3081,6 +3081,254 @@ int test_wolfSSL_chain_ca_ext_key_usage(void) return EXPECT_RESULT(); } +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \ + !defined(NO_TLS) && !defined(NO_SHA256) && !defined(NO_ASN_TIME) && \ + defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \ + defined(USE_CERT_BUFFERS_2048) && !defined(IGNORE_KEY_EXTENSIONS) && \ + !defined(NO_ASN_TIME_CHECK) + +#define TEST_CA_DATE_DAY ((time_t)86400) + +static time_t test_ca_date_now = 0; + +/* Report test_ca_date_now as the current time. */ +static time_t test_ca_date_time_cb(time_t* t) +{ + if (t != NULL) + *t = test_ca_date_now; + + return test_ca_date_now; +} + +/* Stand in for an application that accepts an expired certificate. */ +static int test_ca_date_accept_cb(int preverify, + WOLFSSL_X509_STORE_CTX* store) +{ +#ifdef OPENSSL_COMPATIBLE_DEFAULTS + if (store->error == WOLFSSL_X509_V_ERR_CERT_HAS_EXPIRED) +#else + if (store->error == WC_NO_ERR_TRACE(ASN_AFTER_DATE_E)) +#endif + return 1; + + return preverify; +} + +/* Connect a new client from *ctx_c to a new server that presents chainDer, + * with the clock at now. *ctx_c is created on the first call and trusts only + * rootDer, so later calls share its certificate manager. The client's error, + * or 0 when the handshake completes, is returned through hsRet. */ +static int test_ca_date_connect(WOLFSSL_CTX** ctx_c, const byte* rootDer, + int rootSz, const byte* chainDer, int chainSz, time_t now, + VerifyCallback verifyCb, int* hsRet) +{ + EXPECT_DECLS; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + struct test_memio_ctx test_ctx; + + *hsRet = -1; + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup_ex(&test_ctx, ctx_c, &ctx_s, &ssl_c, &ssl_s, + wolfSSLv23_client_method, wolfSSLv23_server_method, + (byte*)rootDer, rootSz, (byte*)chainDer, chainSz, + (byte*)client_key_der_2048, (int)sizeof_client_key_der_2048), 0); + + if (EXPECT_SUCCESS()) { + wolfSSL_set_verify(ssl_c, WOLFSSL_VERIFY_PEER, verifyCb); + test_ca_date_now = now; + ExpectIntEQ(wc_SetTimeCb(test_ca_date_time_cb), 0); + if (test_memio_do_handshake(ssl_c, ssl_s, 10, NULL) == 0) + *hsRet = 0; + else + *hsRet = wolfSSL_get_error(ssl_c, WOLFSSL_FATAL_ERROR); + (void)wc_SetTimeCb(NULL); + } + + wolfSSL_free(ssl_s); + wolfSSL_free(ssl_c); + wolfSSL_CTX_free(ctx_s); + + return EXPECT_RESULT(); +} + +/* Verify leafDer with a certificate manager that trusts rootDer and holds + * caDer as a CA of the given type, with the clock at now. Returns the result + * of wolfSSL_CertManagerVerifyBuffer(), or -1 when the setup fails. */ +static int test_ca_date_cm_verify(const byte* rootDer, int rootSz, + const byte* caDer, int caSz, int caType, const byte* leafDer, int leafSz, + time_t now) +{ + WOLFSSL_CERT_MANAGER* cm; + int ret = -1; + + cm = wolfSSL_CertManagerNew(); + if (cm == NULL) + return ret; + + if (wolfSSL_CertManagerLoadCABuffer(cm, rootDer, rootSz, + WOLFSSL_FILETYPE_ASN1) == WOLFSSL_SUCCESS && + wolfSSL_CertManagerLoadCABufferType(cm, caDer, caSz, + WOLFSSL_FILETYPE_ASN1, 0, WOLFSSL_LOAD_VERIFY_DEFAULT_FLAGS, + caType) == WOLFSSL_SUCCESS) { + test_ca_date_now = now; + if (wc_SetTimeCb(test_ca_date_time_cb) == 0) { + ret = wolfSSL_CertManagerVerifyBuffer(cm, leafDer, leafSz, + WOLFSSL_FILETYPE_ASN1); + (void)wc_SetTimeCb(NULL); + } + } + wolfSSL_CertManagerFree(cm); + + return ret; +} +#endif /* cached chain CA date test dependencies */ + +/* Test that a CA cached from a peer's chain is not used once it has expired. + * + * After a handshake the client keeps the intermediate CA that the server sent. + * A later server that sends only its leaf is verified with that cached CA, so + * the CA's dates must be checked then too: the client must fail the same way + * it does when the expired CA is sent. A CA renewed with the same key and sent + * again replaces the cached dates. A CA that the application loaded is a trust + * anchor and its dates are not checked again. + * + * @return TEST_SUCCESS on success. + */ +int test_wolfSSL_chain_ca_cached_dates(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \ + !defined(NO_TLS) && !defined(NO_SHA256) && !defined(NO_ASN_TIME) && \ + defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_CERT_EXT) && \ + defined(USE_CERT_BUFFERS_2048) && !defined(IGNORE_KEY_EXTENSIONS) && \ + !defined(NO_ASN_TIME_CHECK) + const byte* rootDer = ca_cert_der_2048; + int rootSz = (int)sizeof_ca_cert_der_2048; + WOLFSSL_CTX* ctx_c = NULL; + WC_RNG rng; + RsaKey caKey; + RsaKey intKey; + RsaKey leafKey; + int rngInit = 0; + int caInit = 0; + int intInit = 0; + int leafInit = 0; + byte* oldCaDer = NULL; + byte* newCaDer = NULL; + byte* chainDer = NULL; + int oldCaSz = 0; + int newCaSz = 0; + int leafSz = 0; + int hsRet = -1; + time_t now = wc_Time(NULL); + /* The old CA expires in 100 days. The leaf and the renewed CA are valid + * for 500 days. */ + time_t later = now + 200 * TEST_CA_DATE_DAY; + word32 idx; + + ExpectNotNull(oldCaDer = (byte*)XMALLOC(TEST_EKU_CERT_BUF_SZ, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectNotNull(newCaDer = (byte*)XMALLOC(TEST_EKU_CERT_BUF_SZ, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectNotNull(chainDer = (byte*)XMALLOC(3 * TEST_EKU_CERT_BUF_SZ, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + + ExpectIntEQ(wc_InitRng(&rng), 0); + if (EXPECT_SUCCESS()) rngInit = 1; + ExpectIntEQ(wc_InitRsaKey(&caKey, NULL), 0); + if (EXPECT_SUCCESS()) caInit = 1; + idx = 0; + ExpectIntEQ(wc_RsaPrivateKeyDecode(ca_key_der_2048, &idx, &caKey, + (word32)sizeof_ca_key_der_2048), 0); + ExpectIntEQ(wc_InitRsaKey(&intKey, NULL), 0); + if (EXPECT_SUCCESS()) intInit = 1; + idx = 0; + ExpectIntEQ(wc_RsaPrivateKeyDecode(server_key_der_2048, &idx, &intKey, + (word32)sizeof_server_key_der_2048), 0); + ExpectIntEQ(wc_InitRsaKey(&leafKey, NULL), 0); + if (EXPECT_SUCCESS()) leafInit = 1; + idx = 0; + ExpectIntEQ(wc_RsaPrivateKeyDecode(client_key_der_2048, &idx, &leafKey, + (word32)sizeof_client_key_der_2048), 0); + + /* The old CA was issued 400 days ago and is valid for 500 days. The + * renewed CA has the same name and key and is issued now. */ + if (EXPECT_SUCCESS()) { + test_ca_date_now = now - 400 * TEST_CA_DATE_DAY; + ExpectIntEQ(wc_SetTimeCb(test_ca_date_time_cb), 0); + ExpectIntGT((oldCaSz = test_eku_gen_ca(oldCaDer, TEST_EKU_CERT_BUF_SZ, + &intKey, rootDer, rootSz, &caKey, &rng, NULL, "CA Date Inter", 0, + NULL)), 0); + (void)wc_SetTimeCb(NULL); + } + ExpectIntGT((newCaSz = test_eku_gen_ca(newCaDer, TEST_EKU_CERT_BUF_SZ, + &intKey, rootDer, rootSz, &caKey, &rng, NULL, "CA Date Inter", 0, + NULL)), 0); + /* chainDer holds the leaf followed by the old CA. */ + ExpectIntGT((leafSz = test_eku_gen_leaf(chainDer, TEST_EKU_CERT_BUF_SZ, + &leafKey, oldCaDer, oldCaSz, &intKey, &rng)), 0); + if (EXPECT_SUCCESS()) + XMEMCPY(chainDer + leafSz, oldCaDer, (size_t)oldCaSz); + + /* The first handshake sends the old CA and the client caches it. */ + ExpectIntEQ(test_ca_date_connect(&ctx_c, rootDer, rootSz, chainDer, + leafSz + oldCaSz, now, NULL, &hsRet), TEST_SUCCESS); + ExpectIntEQ(hsRet, 0); + /* While it is valid, the cached CA verifies a leaf sent alone. */ + ExpectIntEQ(test_ca_date_connect(&ctx_c, rootDer, rootSz, chainDer, + leafSz, now, NULL, &hsRet), TEST_SUCCESS); + ExpectIntEQ(hsRet, 0); + /* Once it has expired, sending it fails and so must using the cached + * copy. */ + ExpectIntEQ(test_ca_date_connect(&ctx_c, rootDer, rootSz, chainDer, + leafSz + oldCaSz, later, NULL, &hsRet), TEST_SUCCESS); + ExpectIntEQ(hsRet, WC_NO_ERR_TRACE(ASN_AFTER_DATE_E)); + ExpectIntEQ(test_ca_date_connect(&ctx_c, rootDer, rootSz, chainDer, + leafSz, later, NULL, &hsRet), TEST_SUCCESS); + ExpectIntEQ(hsRet, WC_NO_ERR_TRACE(ASN_AFTER_DATE_E)); + /* A verify callback can accept the date error, as for any other. */ + ExpectIntEQ(test_ca_date_connect(&ctx_c, rootDer, rootSz, chainDer, + leafSz, later, test_ca_date_accept_cb, &hsRet), TEST_SUCCESS); + ExpectIntEQ(hsRet, 0); + /* The renewed CA is accepted and its dates replace the cached ones, so a + * leaf sent alone verifies again. */ + if (EXPECT_SUCCESS()) + XMEMCPY(chainDer + leafSz, newCaDer, (size_t)newCaSz); + ExpectIntEQ(test_ca_date_connect(&ctx_c, rootDer, rootSz, chainDer, + leafSz + newCaSz, later, NULL, &hsRet), TEST_SUCCESS); + ExpectIntEQ(hsRet, 0); + ExpectIntEQ(test_ca_date_connect(&ctx_c, rootDer, rootSz, chainDer, + leafSz, later, NULL, &hsRet), TEST_SUCCESS); + ExpectIntEQ(hsRet, 0); + wolfSSL_CTX_free(ctx_c); + + /* Certificate manager verification checks a cached chain CA the same way. + * A CA that the application loaded is a trust anchor and is not checked + * again. wolfSSL_CertManagerLoadCABufferType() finds the CA to set the + * type of by its subject key identifier. */ +#ifndef NO_SKID + ExpectIntEQ(test_ca_date_cm_verify(rootDer, rootSz, oldCaDer, oldCaSz, + WOLFSSL_CHAIN_CA, chainDer, leafSz, now), WOLFSSL_SUCCESS); + ExpectIntEQ(test_ca_date_cm_verify(rootDer, rootSz, oldCaDer, oldCaSz, + WOLFSSL_CHAIN_CA, chainDer, leafSz, later), + WC_NO_ERR_TRACE(ASN_AFTER_DATE_E)); +#endif + ExpectIntEQ(test_ca_date_cm_verify(rootDer, rootSz, oldCaDer, oldCaSz, + WOLFSSL_USER_CA, chainDer, leafSz, later), WOLFSSL_SUCCESS); + + if (rngInit) wc_FreeRng(&rng); + if (caInit) wc_FreeRsaKey(&caKey); + if (intInit) wc_FreeRsaKey(&intKey); + if (leafInit) wc_FreeRsaKey(&leafKey); + XFREE(oldCaDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(newCaDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(chainDer, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return EXPECT_RESULT(); +} + #if defined(WOLFSSL_SMALL_CERT_VERIFY) && \ defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && !defined(NO_RSA) && \ !defined(NO_TLS) && !defined(NO_SHA256) && !defined(NO_ASN_TIME) && \ diff --git a/tests/api/test_ssl_cert.h b/tests/api/test_ssl_cert.h index bb6978ea123..caa5c60954c 100644 --- a/tests/api/test_ssl_cert.h +++ b/tests/api/test_ssl_cert.h @@ -69,6 +69,7 @@ int test_wolfSSL_verify_tls13_failnocert_only(void); int test_wolfSSL_verify_empty_server_cert(void); int test_wolfSSL_verify_post_handshake_defers(void); int test_wolfSSL_chain_ca_ext_key_usage(void); +int test_wolfSSL_chain_ca_cached_dates(void); int test_wolfSSL_small_cert_verify_sig_error(void); int test_wolfSSL_session_chain_oversized_cert(void); int test_wolfSSL_session_ticket_oversized_peer_cert(void); @@ -121,6 +122,7 @@ int test_wolfSSL_SESSION_get0_peer_full_chain(void); TEST_DECL_GROUP("ssl_cert", \ test_wolfSSL_verify_post_handshake_defers), \ TEST_DECL_GROUP("ssl_cert", test_wolfSSL_chain_ca_ext_key_usage), \ + TEST_DECL_GROUP("ssl_cert", test_wolfSSL_chain_ca_cached_dates), \ TEST_DECL_GROUP("ssl_cert", \ test_wolfSSL_small_cert_verify_sig_error), \ TEST_DECL_GROUP("ssl_cert", \ diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index a1d8bae76dc..edf7543b922 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -25676,8 +25676,8 @@ Signer* findSignerByName(Signer *list, byte *hash) /* Find a signer for cert in cm and extraCAList. Prefers AKID->SKID * with name-hash validation. Fall back to name-only when AKID is * absent. */ -static Signer* FindSignerByAkidOrName(WOLFSSL_CERT_MANAGER* cm, - Signer* extraCAList, Signer* cert) +Signer* wolfssl_local_FindSignerByAkidOrName(WOLFSSL_CERT_MANAGER* cm, + Signer* extraCAList, Signer* cert) { Signer* signer = NULL; #ifdef HAVE_CERTIFICATE_STATUS_REQUEST_V2 @@ -26704,7 +26704,8 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, /* Stop at trust anchor (self-signed). */ if (ncSigner->selfSigned) break; - ncParent = FindSignerByAkidOrName(cm, extraCAList, ncSigner); + ncParent = wolfssl_local_FindSignerByAkidOrName(cm, extraCAList, + ncSigner); /* Stop on missing parent, self-loop, or A->B->A cycle. */ if (ncParent == NULL || ncParent == ncSigner || ncParent == ncPrev) @@ -26736,6 +26737,31 @@ int ParseCertRelative(DecodedCert* cert, int type, int verify, return ret; } +#if !defined(NO_ASN_TIME_CHECK) && !defined(WOLFCRYPT_ONLY) +/* Copy the DER encoded validity dates of cert into validity. + * Returns 0, BAD_FUNC_ARG or ASN_DATE_SZ_E. */ +int wolfssl_local_SetSignerValidity(SignerValidity* validity, + const DecodedCert* cert) +{ + if (validity == NULL || cert == NULL || cert->beforeDate == NULL || + cert->afterDate == NULL) + return BAD_FUNC_ARG; + + if (cert->beforeDateLen <= 2 || + cert->beforeDateLen > (int)sizeof(validity->notBefore) || + cert->afterDateLen <= 2 || + cert->afterDateLen > (int)sizeof(validity->notAfter)) + return ASN_DATE_SZ_E; + + XMEMSET(validity, 0, sizeof(SignerValidity)); + XMEMCPY(validity->notBefore, cert->beforeDate, + (size_t)cert->beforeDateLen); + XMEMCPY(validity->notAfter, cert->afterDate, (size_t)cert->afterDateLen); + + return 0; +} +#endif /* !NO_ASN_TIME_CHECK && !WOLFCRYPT_ONLY */ + int FillSigner(Signer* signer, DecodedCert* cert, int type, DerBuffer *der) { int ret = 0; @@ -26743,6 +26769,21 @@ int FillSigner(Signer* signer, DecodedCert* cert, int type, DerBuffer *der) if (signer == NULL || cert == NULL) return BAD_FUNC_ARG; +#if !defined(NO_ASN_TIME_CHECK) && !defined(WOLFCRYPT_ONLY) + /* A CA from a peer's chain is not a trust anchor. Keep its dates so they + * can be checked again each time it is used. */ + if (type == WOLFSSL_CHAIN_CA) { + signer->validity = (SignerValidity*)XMALLOC(sizeof(SignerValidity), + cert->heap, DYNAMIC_TYPE_SIGNER); + if (signer->validity == NULL) { + ret = MEMORY_E; + } + else { + ret = wolfssl_local_SetSignerValidity(signer->validity, cert); + } + } +#endif + #ifdef WOLFSSL_DUAL_ALG_CERTS if (ret == 0 && signer != NULL) { if (cert->extSapkiSet && cert->sapkiLen > 0) { @@ -26884,6 +26925,9 @@ void FreeSigner(Signer* signer, void* heap) #endif #ifdef WOLFSSL_SIGNER_DER_CERT FreeDer(&signer->derCert); +#endif +#if !defined(NO_ASN_TIME_CHECK) && !defined(WOLFCRYPT_ONLY) + XFREE(signer->validity, heap, DYNAMIC_TYPE_SIGNER); #endif XFREE(signer, heap, DYNAMIC_TYPE_SIGNER); } diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 3ea829c0b4e..df7ff724b39 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -4975,6 +4975,12 @@ int ProcessOldClientHello(WOLFSSL* ssl, const byte* input, word32* inOutIdx, WOLFSSL_LOCAL int SetCAType(WOLFSSL_CERT_MANAGER* cm, byte* hash, int type); WOLFSSL_LOCAL int AlreadySigner(WOLFSSL_CERT_MANAGER* cm, byte* hash); +#ifndef NO_ASN_TIME_CHECK + WOLFSSL_LOCAL int wolfssl_local_CheckChainCADates(WOLFSSL_CERT_MANAGER* cm, + Signer* ca, Signer* pending); + WOLFSSL_LOCAL int wolfssl_local_SetChainCADates(WOLFSSL_CERT_MANAGER* cm, + const byte* hash, const DecodedCert* cert); +#endif #ifdef WOLFSSL_TRUST_PEER_CERT WOLFSSL_LOCAL int AddTrustedPeer(WOLFSSL_CERT_MANAGER* cm, DerBuffer** pDer, int verify); diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 6bb3427a208..f05bf8ca913 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -2355,6 +2355,16 @@ struct DecodedCert { /* Signer and CRL hashes come from CalcHashId*(). */ #define SIGNER_DIGEST_SIZE KEYID_SIZE +#if !defined(NO_ASN_TIME_CHECK) && !defined(WOLFCRYPT_ONLY) +/* Validity of a CA cached from a peer's chain, DER encoded: tag, length and + * date. Such a CA is not a trust anchor, so it is checked whenever it is used + * to verify a certificate. */ +typedef struct SignerValidity { + byte notBefore[MAX_DATE_SIZE + 2]; + byte notAfter[MAX_DATE_SIZE + 2]; +} SignerValidity; +#endif + /* CA Signers */ /* if change layout change PERSIST_CERT_CACHE functions too */ struct Signer { @@ -2426,6 +2436,9 @@ struct Signer { #ifdef WOLFSSL_SIGNER_DER_CERT DerBuffer* derCert; #endif +#if !defined(NO_ASN_TIME_CHECK) && !defined(WOLFCRYPT_ONLY) + SignerValidity* validity; /* dates of a WOLFSSL_CHAIN_CA */ +#endif #if defined(WOLFSSL_RENESAS_TSIP_TLS) || defined(WOLFSSL_RENESAS_FSPSM_TLS) word32 cm_idx; #endif @@ -2666,6 +2679,14 @@ WOLFSSL_LOCAL int wc_GetPubX509(DecodedCert* cert, int verify, int* badDate); WOLFSSL_LOCAL const byte* OidFromId(word32 id, word32 type, word32* oidSz); WOLFSSL_LOCAL Signer* findSignerByKeyHash(Signer *list, byte *hash); WOLFSSL_LOCAL Signer* findSignerByName(Signer *list, byte *hash); +#ifndef IGNORE_NAME_CONSTRAINTS +WOLFSSL_LOCAL Signer* wolfssl_local_FindSignerByAkidOrName( + WOLFSSL_CERT_MANAGER* cm, Signer* extraCAList, Signer* cert); +#endif +#if !defined(NO_ASN_TIME_CHECK) && !defined(WOLFCRYPT_ONLY) +WOLFSSL_LOCAL int wolfssl_local_SetSignerValidity(SignerValidity* validity, + const DecodedCert* cert); +#endif WOLFSSL_TEST_VIS int FillSigner(Signer* signer, DecodedCert* cert, int type, DerBuffer *der); WOLFSSL_TEST_VIS Signer* MakeSigner(void* heap); WOLFSSL_TEST_VIS void FreeSigner(Signer* signer, void* heap);