-/*
- * Copyright 2016-2025 The OpenSSL Project Authors. All Rights Reserved.
+/* Copyright 2016-2025 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
#include <openssl/x509.h>
#include <openssl/x509v3.h>
+#include <openssl/x509_vfy.h>
#include "testutil.h"
#include "internal/nelem.h"
+#include "crypto/x509.h"
/**********************************************************************
*
&& TEST_int_eq(X509_PURPOSE_get_id(xp), X509_PURPOSE_DEFAULT_ANY);
}
+/* 0000-01-01 00:00:00 UTC */
+#define MIN_CERT_TIME INT64_C(-62167219200)
+/* 9999-12-31 23:59:59 UTC */
+#define MAX_CERT_TIME INT64_C(253402300799)
+/* 1950-01-01 00:00:00 UTC */
+#define MIN_UTC_TIME INT64_C(-631152000)
+/* 2049-12-31 23:59:59 UTC */
+#define MAX_UTC_TIME INT64_C(2524607999)
+
+typedef struct {
+ int64_t NotBefore;
+ int64_t NotAfter;
+} CERT_TEST_DATA;
+
+/* clang-format off */
+static CERT_TEST_DATA cert_test_data[] = {
+ { 0, 0 },
+ { 0, 1 },
+ { 1, 1 },
+ { -1, 0 },
+ { -1, 1 },
+ { 1442939232, 1443004020 },
+ { 0, INT32_MAX },
+ { INT32_MIN, 0 },
+ { INT32_MIN, INT32_MAX },
+ { 0, UINT32_MAX },
+ { MIN_UTC_TIME, 0 },
+ { MIN_UTC_TIME - 1, 0 },
+ { 0, MAX_UTC_TIME },
+ { 0, MAX_UTC_TIME + 1 },
+ { MIN_UTC_TIME, MAX_UTC_TIME},
+ { MIN_UTC_TIME - 1, MAX_UTC_TIME + 1 },
+ { MIN_CERT_TIME, MAX_CERT_TIME },
+ { MIN_CERT_TIME, MAX_CERT_TIME - 1 },
+ { MIN_CERT_TIME + 1, MAX_CERT_TIME },
+ { MIN_CERT_TIME + 1, MAX_CERT_TIME - 1 },
+ { 0, MAX_CERT_TIME },
+ { 0, MAX_CERT_TIME - 1 }
+};
+/* clang-format on */
+
+/* Returns 0 for success, 1 if failed */
+static int test_a_time(X509_STORE_CTX *ctx, X509 *x509,
+ const int64_t test_time,
+ int64_t notBefore, int64_t notAfter,
+ int64_t lower_limit, int64_t upper_limit)
+{
+ int expected_value, error, expected_error;
+ X509_VERIFY_PARAM *vpm;
+
+ /* Skip tests out of time_t range */
+ if (test_time < lower_limit || test_time > upper_limit)
+ return 0;
+
+ /*
+ * XXX beck This block below is a hack. The current comparison
+ * routines needlessly convert the time_t value to a struct
+ * tm to compare it to the asn1_string converted to a struct tm.
+ * OPENSSL_gmtime() does this, but fails on large time_t values.
+ * Once we remove this conversion we should be able to compare
+ * against the full range of time_t. but for the moment we need
+ * to skip this test if OPENSSL_gmtime() fails.
+ */
+ {
+ const time_t t = (const time_t) test_time;
+ struct tm tm;
+
+ if (OPENSSL_gmtime(&t, &tm) == NULL) {
+ TEST_info("OPENSSL_gmtime can't handle time of %lld, skipping test",
+ (long long) test_time);
+ return 0;
+ }
+ }
+
+ expected_value = notBefore <= test_time;
+ if (expected_value)
+ expected_value = notAfter == MAX_CERT_TIME || notAfter >= test_time;
+
+ if (notBefore > test_time)
+ expected_error = X509_V_ERR_CERT_NOT_YET_VALID;
+ else if (notAfter < test_time && notAfter != MAX_CERT_TIME)
+ expected_error = X509_V_ERR_CERT_HAS_EXPIRED;
+ else
+ expected_error = 0;
+
+ vpm = X509_STORE_CTX_get0_param(ctx);
+ X509_VERIFY_PARAM_set_time(vpm, test_time);
+ if (ossl_x509_check_cert_time(ctx, x509, 0) != expected_value) {
+ TEST_info("ossl_X509_check_cert_time %s unexpectedly when verifying "
+ "notBefore %lld, notAfter %lld at time %lld\n",
+ expected_value ? "failed" : "succeeded",
+ (long long)notBefore, (long long)notAfter,
+ (long long)test_time);
+ return 1;
+ }
+ error = 0;
+ if (ossl_x509_check_certificate_times(vpm, x509, &error) != expected_value) {
+ TEST_info("ossl_X509_check_certificate_times %s unexpectedly when "
+ "verifying notBefore %lld, notAfter %lld at time %lld\n",
+ expected_value ? "failed" : "succeeded",
+ (long long)notBefore, (long long)notAfter,
+ (long long)test_time);
+ return 1;
+ }
+ if (error != expected_error) {
+ TEST_info("ossl_X509_check_certificate_times error return was %d, "
+ "expected %d when verifying notBefore %lld, notAfter %lld "
+ "at time %lld\n",
+ error, expected_error,
+ (long long)notBefore, (long long)notAfter,
+ (long long)test_time);
+ return 1;
+ }
+ return 0;
+}
+
+static int do_x509_time_tests(CERT_TEST_DATA *tests, size_t ntests, int64_t lower_limit, int64_t upper_limit)
+{
+ int ret = 0;
+ int failures = 0;
+ X509 *x509 = NULL;
+ X509_STORE_CTX *ctx = NULL;
+ X509_VERIFY_PARAM *vpm = NULL;
+ ASN1_TIME *nb = NULL, *na = NULL;
+ size_t i;
+
+ if ((x509 = X509_new()) == NULL) {
+ TEST_info("Malloc posral se do postele.");
+ goto err;
+ }
+ if ((ctx = X509_STORE_CTX_new()) == NULL) {
+ TEST_info("Malloc posral se do postele.");
+ goto err;
+ }
+ X509_STORE_CTX_init(ctx, NULL, NULL, NULL);
+ if ((vpm = X509_VERIFY_PARAM_new()) == NULL) {
+ TEST_info("Malloc posral se do postele.");
+ goto err;
+ }
+ X509_STORE_CTX_set0_param(ctx, vpm);
+ if ((nb = ASN1_TIME_new()) == NULL) {
+ TEST_info("Malloc posral se do postele.");
+ goto err;
+ }
+ if ((na = ASN1_TIME_new()) == NULL) {
+ TEST_info("Malloc posral se do postele.");
+ goto err;
+ }
+
+ for (i = 0; i < ntests; i++) {
+ int64_t test_time;
+
+ /* Skip this test if any cert values are out of time_t range */
+ if (tests[i].NotBefore < lower_limit || tests[i].NotBefore > upper_limit)
+ continue;
+ if (tests[i].NotAfter < lower_limit || tests[i].NotAfter > upper_limit)
+ continue;
+
+ if (ASN1_TIME_adj(nb, (time_t)tests[i].NotBefore, 0, 0) == NULL) {
+ TEST_info("Could not create NotBefore");
+ goto err;
+ }
+ if (ASN1_TIME_adj(na, (time_t)tests[i].NotAfter, 0, 0) == NULL) {
+ TEST_info("Could not create NotAfter");
+ goto err;
+ }
+
+ /* Forcibly jam the times into the X509 */
+ if (!X509_set1_notBefore(x509, nb)) {
+ TEST_info("X509_set1_notBefore failed");
+ goto err;
+ }
+ if (!X509_set1_notAfter(x509, na)) {
+ TEST_info("X509_set1_notBefore failed");
+ goto err;
+ }
+
+ /* Test boundaries of NotBefore */
+ test_time = tests[i].NotBefore - 1;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+ test_time = tests[i].NotBefore;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+ test_time = tests[i].NotBefore + 1;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+
+ /* Test boundaries of NotAfter */
+ test_time = tests[i].NotAfter - 1;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+ test_time = tests[i].NotAfter;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+ test_time = tests[i].NotAfter + 1;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+
+ test_time = 1442939232;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+ test_time = 1443004020;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+
+ test_time = MIN_UTC_TIME;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+ test_time = MIN_UTC_TIME - 1;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+
+ test_time = MAX_UTC_TIME;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+
+ test_time = MAX_UTC_TIME + 1;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+
+ /* Test integer value boundaries */
+ test_time = INT64_MIN;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+ test_time = INT32_MIN;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+ test_time = -1;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+ test_time = 0;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+ test_time = 1;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+ test_time = INT32_MAX;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+ test_time = UINT32_MAX;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit, upper_limit);
+ test_time = INT64_MAX;
+ failures += test_a_time(ctx, x509, test_time, tests[i].NotBefore,
+ tests[i].NotAfter, lower_limit,
+ upper_limit);
+ }
+
+ ret = (failures == 0);
+
+err:
+ X509_STORE_CTX_free(ctx);
+ X509_free(x509);
+ ASN1_STRING_free(nb);
+ ASN1_STRING_free(na);
+ return ret;
+}
+
+static int tests_X509_check_time(void)
+{
+ /*
+ * time_t sanity checks. We need these until we can evaluate cert
+ * time without depending on platform time_t. Then all this
+ * unpleasantness to decide to not run unit tests that exceed the
+ * range of a platform time_t can go away.
+ */
+ time_t test_time_t = -1;
+ int time_t_is_unsigned = (test_time_t > 0);
+ int time_t_is_64_bit = (sizeof(time_t) == sizeof(int64_t));
+ int time_t_is_32_bit = (sizeof(time_t) == sizeof(int32_t));
+
+ OPENSSL_assert(time_t_is_32_bit || time_t_is_64_bit);
+ OPENSSL_assert(!time_t_is_unsigned || time_t_is_32_bit);
+
+ if (time_t_is_32_bit) {
+ if (time_t_is_unsigned) {
+ return do_x509_time_tests(cert_test_data, sizeof(cert_test_data)
+ / sizeof(CERT_TEST_DATA), INT32_MIN,
+ INT32_MAX);
+ } else {
+ return do_x509_time_tests(cert_test_data, sizeof(cert_test_data)
+ / sizeof(CERT_TEST_DATA), 0, UINT32_MAX);
+ }
+ }
+ return do_x509_time_tests(cert_test_data, sizeof(cert_test_data)
+ / sizeof(CERT_TEST_DATA), INT64_MIN, INT64_MAX);
+}
+
int setup_tests(void)
{
ADD_TEST(test_standard_exts);
ADD_ALL_TESTS(test_a2i_ipaddress, OSSL_NELEM(a2i_ipaddress_tests));
ADD_TEST(tests_X509_PURPOSE);
+ ADD_TEST(tests_X509_check_time);
return 1;
}