diff --git a/src/x509_str.c b/src/x509_str.c index cbf82cc396a..432a48897da 100644 --- a/src/x509_str.c +++ b/src/x509_str.c @@ -230,6 +230,18 @@ int wolfSSL_X509_STORE_CTX_init(WOLFSSL_X509_STORE_CTX* ctx, XMEMSET(ctx->param, 0, sizeof(*ctx->param)); } + /* Copy check_time from store parameters if available */ + if (store != NULL && store->param != NULL) { + if ((store->param->flags & WOLFSSL_USE_CHECK_TIME) != 0 && + store->param->check_time != 0) { + ctx->param->check_time = store->param->check_time; + ctx->param->flags |= WOLFSSL_USE_CHECK_TIME; + } + if ((store->param->flags & WOLFSSL_NO_CHECK_TIME) != 0) { + ctx->param->flags |= WOLFSSL_NO_CHECK_TIME; + } + } + return WOLFSSL_SUCCESS; } return WOLFSSL_FAILURE; @@ -321,6 +333,74 @@ static void SetupStoreCtxError(WOLFSSL_X509_STORE_CTX* ctx, int ret) SetupStoreCtxError_ex(ctx, ret, depth); } +#ifndef NO_ASN_TIME +/* Post certificate validation date handling. This function is called after the + * certificate has been verified by the certificate manager. It then checks if + * X509 store parameters are set for date validation override. + * @param ctx The certificate store context + * @param ret The return value from the certificate manager verify + * @return The return value for the certificate date validation after override + */ +static int X509StoreVerifyCertDate(WOLFSSL_X509_STORE_CTX* ctx, int ret) +{ + byte *afterDate = ctx->current_cert->notAfter.data; + byte *beforeDate = ctx->current_cert->notBefore.data; + + /* Only override existing date errors or WOLFSSL_SUCCESS. */ + if (ret == WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E) || + ret == WC_NO_ERR_TRACE(ASN_AFTER_DATE_E) || + ret == WC_NO_ERR_TRACE(WOLFSSL_SUCCESS)) { +#ifdef USE_WOLF_VALIDDATE + WOLFSSL_X509_VERIFY_PARAM* param = NULL; + + /* If no external XVALIDATE_DATE was defined then use param for date + validation overrides. */ + if (ctx->param != NULL) { + param = ctx->param; + } + else if (ctx->store != NULL && ctx->store->param != NULL) { + param = ctx->store->param; + } + + if (param != NULL) { + if ((param->flags & WOLFSSL_NO_CHECK_TIME) != 0) { + WOLFSSL_MSG("Overriding date validation WOLFSSL_NO_CHECK_TIME"); + ret = WOLFSSL_SUCCESS; + } + else if ((param->flags & WOLFSSL_USE_CHECK_TIME) != 0 && + (param->check_time != 0)) { + time_t checkTime = param->check_time; + ret = WOLFSSL_SUCCESS; /* override date error and use custom set + time for validating certificate dates */ + WOLFSSL_MSG("Override date validation, WOLFSSL_USE_CHECK_TIME"); + if (wc_ValidateDateWithTime(afterDate, + (byte)ctx->current_cert->notAfter.type, ASN_AFTER, + checkTime) < 1) { + ret = ASN_AFTER_DATE_E; + } + else if (wc_ValidateDateWithTime(beforeDate, + (byte)ctx->current_cert->notBefore.type, ASN_BEFORE, + checkTime) < 1) { + ret = ASN_BEFORE_DATE_E; + } + } + } +#else + if (XVALIDATE_DATE(afterDate, + (byte)ctx->current_cert->notAfter.type, ASN_AFTER) < 1) { + ret = ASN_AFTER_DATE_E; + } + else if (XVALIDATE_DATE(beforeDate, + (byte)ctx->current_cert->notBefore.type, ASN_BEFORE) < 1) { + ret = ASN_BEFORE_DATE_E; + } +#endif /* USE_WOLF_VALIDDATE */ + } + + return ret; +} +#endif /* NO_ASN_TIME */ + static int X509StoreVerifyCert(WOLFSSL_X509_STORE_CTX* ctx) { int ret = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); @@ -331,39 +411,16 @@ static int X509StoreVerifyCert(WOLFSSL_X509_STORE_CTX* ctx) ctx->current_cert->derCert->buffer, ctx->current_cert->derCert->length, WOLFSSL_FILETYPE_ASN1); + #ifndef NO_ASN_TIME + /* update return value with any date validation overrides */ + ret = X509StoreVerifyCertDate(ctx, ret); + #endif SetupStoreCtxError(ctx, ret); #if defined(OPENSSL_ALL) || defined(WOLFSSL_QT) if (ctx->store->verify_cb) ret = ctx->store->verify_cb(ret >= 0 ? 1 : 0, ctx) == 1 ? WOLFSSL_SUCCESS : ret; #endif - - #ifndef NO_ASN_TIME - if (ret != WC_NO_ERR_TRACE(ASN_BEFORE_DATE_E) && - ret != WC_NO_ERR_TRACE(ASN_AFTER_DATE_E)) { - /* wolfSSL_CertManagerVerifyBuffer only returns ASN_AFTER_DATE_E or - * ASN_BEFORE_DATE_E if there are no additional errors found in the - * cert. Therefore, check if the cert is expired or not yet valid - * in order to return the correct expected error. */ - byte *afterDate = ctx->current_cert->notAfter.data; - byte *beforeDate = ctx->current_cert->notBefore.data; - - if (XVALIDATE_DATE(afterDate, - (byte)ctx->current_cert->notAfter.type, ASN_AFTER) < 1) { - ret = ASN_AFTER_DATE_E; - } - else if (XVALIDATE_DATE(beforeDate, - (byte)ctx->current_cert->notBefore.type, ASN_BEFORE) < 1) { - ret = ASN_BEFORE_DATE_E; - } - SetupStoreCtxError(ctx, ret); - #if defined(OPENSSL_ALL) || defined(WOLFSSL_QT) - if (ctx->store->verify_cb) - ret = ctx->store->verify_cb(ret >= 0 ? 1 : 0, - ctx) == 1 ? WOLFSSL_SUCCESS : -1; - #endif - } - #endif } return ret; @@ -1445,16 +1502,22 @@ static int X509StoreAddCa(WOLFSSL_X509_STORE* store, { int result = WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR); DerBuffer* derCert = NULL; + int verify = VERIFY; WOLFSSL_ENTER("X509StoreAddCa"); if (store != NULL && x509 != NULL && x509->derCert != NULL) { + /* Check if NO_CHECK_TIME flag is set - if so, skip date validation */ + if (store->param != NULL && + (store->param->flags & WOLFSSL_NO_CHECK_TIME) != 0) { + verify = VERIFY_SKIP_DATE; + } result = AllocDer(&derCert, x509->derCert->length, x509->derCert->type, NULL); if (result == 0) { /* AddCA() frees the buffer. */ XMEMCPY(derCert->buffer, x509->derCert->buffer, x509->derCert->length); - result = AddCA(store->cm, &derCert, type, VERIFY); + result = AddCA(store->cm, &derCert, type, verify); } } diff --git a/tests/api/test_ossl_x509_str.c b/tests/api/test_ossl_x509_str.c index e0c481b5012..53206b2c30e 100644 --- a/tests/api/test_ossl_x509_str.c +++ b/tests/api/test_ossl_x509_str.c @@ -54,6 +54,117 @@ int test_wolfSSL_X509_STORE_CTX_set_time(void) return EXPECT_RESULT(); } +int test_wolfSSL_X509_STORE_check_time(void) +{ + EXPECT_DECLS; +#if defined(OPENSSL_EXTRA) && !defined(NO_FILESYSTEM) && \ + !defined(NO_ASN_TIME) && !defined(NO_RSA) + WOLFSSL_X509_STORE* store = NULL; + WOLFSSL_X509_STORE_CTX* ctx = NULL; + WOLFSSL_X509* ca = NULL; + WOLFSSL_X509* cert = NULL; + int ret; + time_t check_time; + const char* srvCertFile = "./certs/server-cert.pem"; + const char* expiredCertFile = "./certs/test/expired/expired-cert.pem"; + + /* Set check_time to May 26, 2000 - should fail "not yet valid" check */ + ExpectNotNull(store = wolfSSL_X509_STORE_new()); + if (store != NULL) { + /* Load CA certificate - should validate with current time by default */ + ExpectNotNull(ca = wolfSSL_X509_load_certificate_file(caCertFile, + SSL_FILETYPE_PEM)); + ExpectIntEQ(wolfSSL_X509_STORE_add_cert(store, ca), WOLFSSL_SUCCESS); + + /* Set check_time to May 26, 2000 (timestamp: 959320800) */ + check_time = (time_t)959320800; /* May 26, 2000 00:00:00 UTC */ + store->param->check_time = check_time; + wolfSSL_X509_VERIFY_PARAM_set_flags(store->param, + WOLFSSL_USE_CHECK_TIME); + ExpectTrue(store->param->check_time == check_time); + ExpectNotNull(cert = wolfSSL_X509_load_certificate_file(srvCertFile, + SSL_FILETYPE_PEM)); + ExpectNotNull(ctx = wolfSSL_X509_STORE_CTX_new()); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_init(ctx, store, cert, NULL), + WOLFSSL_SUCCESS); + + /* Verify that check_time was copied to context */ + ExpectTrue((ctx->param->flags & WOLFSSL_USE_CHECK_TIME) == + WOLFSSL_USE_CHECK_TIME); + ExpectTrue(ctx->param->check_time == check_time); + + /* Verify certificate using the custom check_time - should fail because + * certificate is not yet valid (use before check fails) */ + ret = wolfSSL_X509_verify_cert(ctx); + ExpectIntNE(ret, WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_get_error(ctx), + WOLFSSL_X509_V_ERR_CERT_NOT_YET_VALID); + wolfSSL_X509_STORE_CTX_free(ctx); + ctx = NULL; + } + wolfSSL_X509_STORE_free(store); + store = NULL; + wolfSSL_X509_free(cert); + cert = NULL; + wolfSSL_X509_free(ca); + ca = NULL; + + /* Verify without setting check_time - should work with current time */ + ExpectNotNull(store = wolfSSL_X509_STORE_new()); + if (store != NULL) { + ExpectNotNull(ca = wolfSSL_X509_load_certificate_file(caCertFile, + SSL_FILETYPE_PEM)); + ExpectIntEQ(wolfSSL_X509_STORE_add_cert(store, ca), WOLFSSL_SUCCESS); + ExpectNotNull(cert = wolfSSL_X509_load_certificate_file(srvCertFile, + SSL_FILETYPE_PEM)); + ExpectNotNull(ctx = wolfSSL_X509_STORE_CTX_new()); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_init(ctx, store, cert, NULL), + WOLFSSL_SUCCESS); + ret = wolfSSL_X509_verify_cert(ctx); + ExpectIntEQ(ret, WOLFSSL_SUCCESS); + wolfSSL_X509_STORE_CTX_free(ctx); + ctx = NULL; + } + wolfSSL_X509_STORE_free(store); + store = NULL; + wolfSSL_X509_free(cert); + cert = NULL; + wolfSSL_X509_free(ca); + ca = NULL; + + /* Test WOLFSSL_NO_CHECK_TIME flag with expired certificate */ + ExpectNotNull(store = wolfSSL_X509_STORE_new()); + if (store != NULL) { + /* Set NO_CHECK_TIME flag to skip time validation */ + wolfSSL_X509_VERIFY_PARAM_set_flags(store->param, + WOLFSSL_NO_CHECK_TIME); + ExpectTrue((store->param->flags & WOLFSSL_NO_CHECK_TIME) == + WOLFSSL_NO_CHECK_TIME); + + /* Load expired certificate (self-signed) */ + ExpectNotNull(cert = wolfSSL_X509_load_certificate_file(expiredCertFile, + SSL_FILETYPE_PEM)); + /* Add expired certificate as trusted CA (self-signed) */ + ExpectIntEQ(wolfSSL_X509_STORE_add_cert(store, cert), WOLFSSL_SUCCESS); + + ExpectNotNull(ctx = wolfSSL_X509_STORE_CTX_new()); + ExpectIntEQ(wolfSSL_X509_STORE_CTX_init(ctx, store, cert, NULL), + WOLFSSL_SUCCESS); + /* Verify expired certificate with NO_CHECK_TIME - should succeed + * because time validation is skipped */ + ret = wolfSSL_X509_verify_cert(ctx); + ExpectIntEQ(ret, WOLFSSL_SUCCESS); + } + wolfSSL_X509_STORE_CTX_free(ctx); + ctx = NULL; + wolfSSL_X509_STORE_free(store); + store = NULL; + wolfSSL_X509_free(cert); + cert = NULL; +#endif /* OPENSSL_EXTRA && !NO_FILESYSTEM && !NO_ASN_TIME && !NO_RSA */ + return EXPECT_RESULT(); +} + int test_wolfSSL_X509_STORE_CTX_get0_store(void) { EXPECT_DECLS; diff --git a/tests/api/test_ossl_x509_str.h b/tests/api/test_ossl_x509_str.h index 3337141a6c5..1ee0f71c434 100644 --- a/tests/api/test_ossl_x509_str.h +++ b/tests/api/test_ossl_x509_str.h @@ -25,6 +25,7 @@ #include int test_wolfSSL_X509_STORE_CTX_set_time(void); +int test_wolfSSL_X509_STORE_check_time(void); int test_wolfSSL_X509_STORE_CTX_get0_store(void); int test_wolfSSL_X509_STORE_CTX(void); int test_wolfSSL_X509_STORE_CTX_ex(void); @@ -43,6 +44,7 @@ int test_X509_STORE_No_SSL_CTX(void); #define TEST_OSSL_X509_STORE_DECLS \ TEST_DECL_GROUP("ossl_x509_store", test_wolfSSL_X509_STORE_CTX_set_time), \ + TEST_DECL_GROUP("ossl_x509_store", test_wolfSSL_X509_STORE_check_time), \ TEST_DECL_GROUP("ossl_x509_store", \ test_wolfSSL_X509_STORE_CTX_get0_store), \ TEST_DECL_GROUP("ossl_x509_store", test_wolfSSL_X509_STORE_CTX), \ diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 8d5f765b5bf..8452f99c28c 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -16574,6 +16574,12 @@ static WC_INLINE int DateLessThan(const struct tm* a, const struct tm* b) /* format = ASN_UTC_TIME or ASN_GENERALIZED_TIME */ /* dateType = ASN_AFTER or ASN_BEFORE */ int wc_ValidateDate(const byte* date, byte format, int dateType) +{ + return wc_ValidateDateWithTime(date, format, dateType, 0); +} + +int wc_ValidateDateWithTime(const byte* date, byte format, int dateType, + time_t checkTime) { time_t ltime; struct tm certTime; @@ -16591,7 +16597,14 @@ int wc_ValidateDate(const byte* date, byte format, int dateType) #endif (void)tmpTime; - ltime = wc_Time(0); + /* Use checkTime if provided (non-zero), otherwise use current time */ + if (checkTime != 0) { + ltime = checkTime; + } + else { + ltime = wc_Time(0); + } + #ifndef NO_TIME_SIGNEDNESS_CHECK if (sizeof(ltime) == sizeof(word32) && (sword32)ltime < 0){ /* A negative response here could be due to a 32-bit time_t diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 0d6ef5b0601..6e08f3596ca 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -2255,6 +2255,10 @@ WOLFSSL_LOCAL int ExtractDate(const unsigned char* date, unsigned char format, wolfssl_tm* certTime, int* idx); WOLFSSL_LOCAL int DateGreaterThan(const struct tm* a, const struct tm* b); WOLFSSL_LOCAL int wc_ValidateDate(const byte* date, byte format, int dateType); +#ifndef NO_ASN_TIME +WOLFSSL_LOCAL int wc_ValidateDateWithTime(const byte* date, byte format, + int dateType, time_t checkTime); +#endif WOLFSSL_TEST_VIS int wc_AsnSetSkipDateCheck(int skip_p); WOLFSSL_LOCAL int wc_AsnGetSkipDateCheck(void);