From a5a7cc7593785bd7b8d790d8fb6c204cd2c265a7 Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Sat, 4 Oct 2025 11:21:07 -0600 Subject: [PATCH] Add tests for the posix time functions. MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit These test both the posix time conversion funcitons, and the underlying ASN1_TIME_adj and ASN1_TIME_to_tm that they underpin. Reviewed-by: Neil Horman Reviewed-by: Saša Nedvědický (Merged from https://github.com/openssl/openssl/pull/28748) --- crypto/asn1/a_time_posix.c | 8 +- include/openssl/posix_time.h | 44 ++++---- test/asn1_internal_test.c | 194 +++++++++++++++++++++++++++++++++++ 3 files changed, 218 insertions(+), 28 deletions(-) diff --git a/crypto/asn1/a_time_posix.c b/crypto/asn1/a_time_posix.c index 05f2c8596ee..51278e73dbc 100644 --- a/crypto/asn1/a_time_posix.c +++ b/crypto/asn1/a_time_posix.c @@ -144,16 +144,16 @@ static int utc_from_posix_time(int64_t time, int *out_year, int *out_month, day_of_era = days - era * 146097; year_of_era = (day_of_era - day_of_era / 1460 + day_of_era / 36524 - day_of_era / 146096) / 365; - *out_year = (int)(year_of_era + era * 400); /* Year starts on Mar 1 */ + *out_year = (int) (year_of_era + era * 400); /* Year starts on Mar 1 */ day_of_year = day_of_era - (365 * year_of_era + year_of_era / 4 - year_of_era / 100); month_of_year = (5 * day_of_year + 2) / 153; *out_month = (int) (month_of_year < 10 ? month_of_year + 3 : - month_of_year - 9); + month_of_year - 9); if (*out_month <= 2) (*out_year)++; /* Adjust year back to Jan 1 start of year. */ - *out_day = (int)(day_of_year - (153 * month_of_year + 2) / 5 + 1); + *out_day = (int) (day_of_year - (153 * month_of_year + 2) / 5 + 1); *out_hours = (int) leftover_seconds / SECS_PER_HOUR; leftover_seconds %= SECS_PER_HOUR; *out_minutes = (int) leftover_seconds / 60; @@ -275,5 +275,3 @@ int OPENSSL_gmtime_diff(int *out_days, int *out_secs, const struct tm *from, return 1; } - - diff --git a/include/openssl/posix_time.h b/include/openssl/posix_time.h index 43ca29cfc65..46f5d56a221 100644 --- a/include/openssl/posix_time.h +++ b/include/openssl/posix_time.h @@ -15,31 +15,29 @@ # if defined(__cplusplus) extern "C" { # endif +/* + * OPENSSL_posix_to_tm converts a int64_t POSIX time value in |time|, + * which must be in the range of year 0000 to 9999, to a broken out + * time value in |tm|. It returns one on success and zero on error. + */ +int OPENSSL_posix_to_tm(int64_t time, struct tm *out_tm); - /* - * OPENSSL_posix_to_tm converts a int64_t POSIX time value in - * |time|, which must be in the range of year 0000 to 9999, to a - * broken out time value in |tm|. It returns one on success and - * zero on error. - */ - int OPENSSL_posix_to_tm(int64_t time, struct tm *out_tm); - - /* - * OPENSSL_tm_to_posix converts a time value between the years 0 - * and 9999 in |tm| to a POSIX time value in |out|. One is - * returned on success, zero is returned on failure. It is a - * failure if |tm| contains out of range values. - */ - int OPENSSL_tm_to_posix(const struct tm *tm, int64_t *out); +/* + * OPENSSL_tm_to_posix converts a time value between the years 0 and + * 9999 in |tm| to a POSIX time value in |out|. One is returned on + * success, zero is returned on failure. It is a failure if |tm| + * contains out of range values. + */ +int OPENSSL_tm_to_posix(const struct tm *tm, int64_t *out); - /* - * OPENSSL_timegm converts a time value between the years 0 and - * 9999 in |tm| to a time_t value in |out|. One is returned on - * success, zero is returned on failure. It is a failure if the - * converted time can not be represented in a time_t, or if the tm - * contains out of range values. - */ - int OPENSSL_timegm(const struct tm *tm, time_t *out); +/* + * OPENSSL_timegm converts a time value between the years 0 and 9999 + * in |tm| to a time_t value in |out|. One is returned on success, + * zero is returned on failure. It is a failure if the converted time + * can not be represented in a time_t, or if the tm contains out of + * range values. + */ +int OPENSSL_timegm(const struct tm *tm, time_t *out); # if defined(__cplusplus) } /* extern C */ diff --git a/test/asn1_internal_test.c b/test/asn1_internal_test.c index 3c2222d988d..bca6d3f5c8a 100644 --- a/test/asn1_internal_test.c +++ b/test/asn1_internal_test.c @@ -21,6 +21,7 @@ #include #include #include +#include #include "testutil.h" #include "internal/nelem.h" @@ -264,6 +265,198 @@ static int test_obj_nid_undef(void) return 1; } +/* 0000-01-01 00:00:00 UTC */ +#define MIN_POSIX_TIME INT64_C(-62167219200) +/* 9999-12-31 23:59:59 UTC */ +#define MAX_POSIX_TIME INT64_C(253402300799) + +extern ASN1_TIME *ossl_asn1_time_from_tm(ASN1_TIME *s, struct tm *ts, int type); + +/* A time_t immune way to get an ASN1_TIME via the same internal conversion */ +static int ossl_asn1_time_from_posix(int64_t posix_time, ASN1_TIME *out_time) +{ + struct tm tm; + + if (!OPENSSL_posix_to_tm(posix_time, &tm)) + return 0; + if (out_time == NULL) + return 0; + return ossl_asn1_time_from_tm(out_time, &tm, V_ASN1_UNDEF) != NULL; +} + +static int test_a_posix_time(int64_t time, struct tm *tm, ASN1_TIME *asn1_time) +{ + /* + * We test a posix time by converting it to a tm, converting + * that to an ASN1_TIME, converting the ASN1_TIME back to a tm, + * and then converting the tm back to a posix time. + * + * We expect all of those steps to fail correctly for values + * outside of the valid range, and to work correctly for + * values in the range + */ + int expected_to_work = time >= MIN_POSIX_TIME && time <= MAX_POSIX_TIME; + time_t time_as_time_t; + int ret = 1; + + if (!TEST_int_eq(OPENSSL_posix_to_tm(time, tm), expected_to_work)) { + TEST_info("OPENSSL_posix_to_tm %s unexpectedly converting %lld\n", + expected_to_work ? "failed" : "succeeded", (long long) time); + ret = 0; + + } + if (ret && expected_to_work && OPENSSL_timegm(tm, &time_as_time_t)) { + /* + * When we got a tm from the previous step, and if + * OPENSSL_timegm succeeds, whatever value we are testing fits + * in a time_t on this platform, so we can call ASN1_TIME_adj + * with it to try to convert to ASN1_TIME. It remains an enduring + * tragedy that adj takes time_t and not int64_t. + */ + if ((ASN1_TIME_adj(asn1_time, time_as_time_t, 0, 0) != NULL) + == !expected_to_work) { + TEST_info("ASN1_TIME_adj %s unexpectedly converting %lld\n", + expected_to_work ? "failed" : "succeeded", + (long long) time); + ret = 0; + } + } else { + /* + * otherwise we still call the same underlying conversion, but without + * passing through time_t, to test the underlying conversion to ASN1_TIME. + */ + if (!TEST_int_eq(ossl_asn1_time_from_posix(time, asn1_time), + expected_to_work)) { + TEST_info("ossl_asn1_time_from_posix %s unexpectedly converting %lld\n", + expected_to_work ? "failed" : "succeeded", + (long long) time); + ret = 0; + } + } + if (expected_to_work) { + int64_t should_be_same = time - 1; + + /* + * We should have an ASN1_TIME from the previous steps, it should + * convert to a tm (no matter what time_t is) + */ + if (!ASN1_TIME_to_tm(asn1_time, tm)) { + TEST_info("ASN1_TIME_to_tm failed unexpectedly converting %lld\n", + (long long) time); + ret = 0; + } + /* That tm should convert back to a posix time */ + if (!TEST_int_eq(OPENSSL_tm_to_posix(tm, &should_be_same), + expected_to_work)) { + TEST_info("OPENSSL_tm_to_posix failed unexpectedly converting %lld\n", + (long long) time); + ret = 0; + } + /* The resulting posix time should be the same one we started with. */ + if (!TEST_int64_t_eq(time, should_be_same)) { + TEST_info("Got converted time of time of %lld, expected %lld\n", + (long long) should_be_same, (long long) time); + ret = 0; + } + } + + return ret; +} + +static int posix_time_test(void) +{ + int ret = 0; + int64_t test_time; + struct tm tm; + ASN1_TIME *conversion_time = NULL; + + conversion_time = ASN1_TIME_new(); + if (!TEST_ptr(conversion_time)) { + TEST_info("malloc failed\n"); + goto err; + } + + /* + * Frequently platform conversions can not deal with one second before the + * the Unix epoch, due to inheriting terrible API design and knocking this + * time value out as an error return. + * + * We should do better. + */ + if (!test_a_posix_time(-1, &tm, conversion_time)) + goto err; + + /* The epoch should also not be an error */ + if (!test_a_posix_time(0, &tm, conversion_time)) + goto err; + /* + * A time oddly near the epoch, but not quite at the epoch., + * In memory of Vernor Vinge. + */ + if (!test_a_posix_time(-16751025, &tm, conversion_time)) + goto err; + + /* Test the minimum boundary */ + if (!test_a_posix_time(MIN_POSIX_TIME - 1, &tm, conversion_time)) + goto err; + + if (!test_a_posix_time(MIN_POSIX_TIME, &tm, conversion_time)) + goto err; + + /* Test the maximum boundary */ + if (!test_a_posix_time(MAX_POSIX_TIME, &tm, conversion_time)) + goto err; + + if (!test_a_posix_time(MAX_POSIX_TIME + 1, &tm, conversion_time)) + goto err; + + /* + * Only the bad idea bears who visited the platform authors know + * for certain what decisions were made about time_t. So let's + * make sure we test the realistic limiting values of the + * possible outcomes of that visit. + */ + if (!test_a_posix_time(INT_MAX, &tm, conversion_time)) + goto err; + + if (!test_a_posix_time(INT_MIN, &tm, conversion_time)) + goto err; + + if (!test_a_posix_time(UINT_MAX, &tm, conversion_time)) + goto err; + + if (!test_a_posix_time(INT32_MAX, &tm, conversion_time)) + goto err; + + if (!test_a_posix_time(INT32_MIN, &tm, conversion_time)) + goto err; + + if (!test_a_posix_time(UINT32_MAX, &tm, conversion_time)) + goto err; + + if (!test_a_posix_time(INT64_MAX, &tm, conversion_time)) + goto err; + + if (!test_a_posix_time(INT64_MIN, &tm, conversion_time)) + goto err; + + /* Test from outside through and past the full range of validity */ +#define INCREMENT 100000 /* because doing them all is a bit slow */ + + for (test_time = MIN_POSIX_TIME - INCREMENT; + test_time <= MAX_POSIX_TIME + INCREMENT; + test_time += INCREMENT) { + if (!test_a_posix_time(test_time, &tm, conversion_time)) + goto err; + } + + ret = 1; + + err: + ASN1_TIME_free(conversion_time); + return ret; +} + int setup_tests(void) { ADD_TEST(test_tbl_standard); @@ -272,5 +465,6 @@ int setup_tests(void) ADD_TEST(test_unicode_range); ADD_TEST(test_obj_create); ADD_TEST(test_obj_nid_undef); + ADD_TEST(posix_time_test); return 1; } -- 2.47.3