From 652464ec78fd0b5b2c144f075d1446ab60fd9efb Mon Sep 17 00:00:00 2001 From: Bob Beck Date: Thu, 16 Jun 2022 10:49:00 -0600 Subject: [PATCH] Validate ASN.1 times according to RFC 5280 Refuse to parse times that are invalid according to RFC 5280, with a few exceptions for compatibility. This can affect test code that relies on making and parsing certificates that contain invalid times. Update-Note: Certificates containing invalid ASN.1 times will no longer parse. Bug: 491, 427 Change-Id: I2a3fe3a4d359ac662340a225d05b360718eb8c29 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/52665 Commit-Queue: Bob Beck Reviewed-by: David Benjamin --- crypto/asn1/a_gentm.c | 124 ++------------------ crypto/asn1/a_utctm.c | 100 ++-------------- crypto/asn1/asn1_test.cc | 29 +++++ crypto/asn1/internal.h | 11 +- crypto/asn1/tasn_dec.c | 25 +++- crypto/bytestring/bytestring_test.cc | 126 +++++++++++++++++++- crypto/bytestring/cbs.c | 165 ++++++++++++++++++++++++++- include/openssl/bytestring.h | 20 ++++ 8 files changed, 385 insertions(+), 215 deletions(-) diff --git a/crypto/asn1/a_gentm.c b/crypto/asn1/a_gentm.c index 3e6f14e3f..e0f6d3966 100644 --- a/crypto/asn1/a_gentm.c +++ b/crypto/asn1/a_gentm.c @@ -55,130 +55,26 @@ * [including the GNU Public Licence.] */ #include +#include +#include +#include #include #include -#include -#include - #include "internal.h" int asn1_generalizedtime_to_tm(struct tm *tm, const ASN1_GENERALIZEDTIME *d) { - static const int min[9] = { 0, 0, 1, 1, 0, 0, 0, 0, 0 }; - static const int max[9] = { 99, 99, 12, 31, 23, 59, 59, 12, 59 }; - char *a; - int n, i, l, o; - - if (d->type != V_ASN1_GENERALIZEDTIME) - return (0); - l = d->length; - a = (char *)d->data; - o = 0; - /* - * GENERALIZEDTIME is similar to UTCTIME except the year is represented - * as YYYY. This stuff treats everything as a two digit field so make - * first two fields 00 to 99 - */ - if (l < 13) - goto err; - for (i = 0; i < 7; i++) { - if ((i == 6) && ((a[o] == 'Z') || (a[o] == '+') || (a[o] == '-'))) { - i++; - if (tm) - tm->tm_sec = 0; - break; - } - if ((a[o] < '0') || (a[o] > '9')) - goto err; - n = a[o] - '0'; - if (++o > l) - goto err; - - if ((a[o] < '0') || (a[o] > '9')) - goto err; - n = (n * 10) + a[o] - '0'; - if (++o > l) - goto err; - - if ((n < min[i]) || (n > max[i])) - goto err; - if (tm) { - switch (i) { - case 0: - tm->tm_year = n * 100 - 1900; - break; - case 1: - tm->tm_year += n; - break; - case 2: - tm->tm_mon = n - 1; - break; - case 3: - tm->tm_mday = n; - break; - case 4: - tm->tm_hour = n; - break; - case 5: - tm->tm_min = n; - break; - case 6: - tm->tm_sec = n; - break; - } - } - } - /* - * Optional fractional seconds: decimal point followed by one or more - * digits. - */ - if (a[o] == '.') { - if (++o > l) - goto err; - i = o; - while ((a[o] >= '0') && (a[o] <= '9') && (o <= l)) - o++; - /* Must have at least one digit after decimal point */ - if (i == o) - goto err; + if (d->type != V_ASN1_GENERALIZEDTIME) { + return 0; } - - if (a[o] == 'Z') - o++; - else if ((a[o] == '+') || (a[o] == '-')) { - int offsign = a[o] == '-' ? 1 : -1, offset = 0; - o++; - if (o + 4 > l) - goto err; - for (i = 7; i < 9; i++) { - if ((a[o] < '0') || (a[o] > '9')) - goto err; - n = a[o] - '0'; - o++; - if ((a[o] < '0') || (a[o] > '9')) - goto err; - n = (n * 10) + a[o] - '0'; - if ((n < min[i]) || (n > max[i])) - goto err; - if (tm) { - if (i == 7) - offset = n * 3600; - else if (i == 8) - offset += n * 60; - } - o++; - } - if (offset && !OPENSSL_gmtime_adj(tm, 0, offset * offsign)) - return 0; - } else if (a[o]) { - /* Missing time zone information. */ - goto err; + CBS cbs; + CBS_init(&cbs, d->data, (size_t)d->length); + if (!CBS_parse_generalized_time(&cbs, tm, /*allow_timezone_offset=*/0)) { + return 0; } - return (o == l); - err: - return (0); + return 1; } int ASN1_GENERALIZEDTIME_check(const ASN1_GENERALIZEDTIME *d) diff --git a/crypto/asn1/a_utctm.c b/crypto/asn1/a_utctm.c index 21ea2cc3e..ea984c51e 100644 --- a/crypto/asn1/a_utctm.c +++ b/crypto/asn1/a_utctm.c @@ -55,106 +55,26 @@ * [including the GNU Public Licence.] */ #include +#include +#include +#include #include #include -#include -#include - #include "internal.h" - int asn1_utctime_to_tm(struct tm *tm, const ASN1_UTCTIME *d) { - static const int min[8] = { 0, 1, 1, 0, 0, 0, 0, 0 }; - static const int max[8] = { 99, 12, 31, 23, 59, 59, 12, 59 }; - char *a; - int n, i, l, o; - - if (d->type != V_ASN1_UTCTIME) - return (0); - l = d->length; - a = (char *)d->data; - o = 0; - - if (l < 11) - goto err; - for (i = 0; i < 6; i++) { - if ((i == 5) && ((a[o] == 'Z') || (a[o] == '+') || (a[o] == '-'))) { - i++; - if (tm) - tm->tm_sec = 0; - break; - } - if ((a[o] < '0') || (a[o] > '9')) - goto err; - n = a[o] - '0'; - if (++o > l) - goto err; - - if ((a[o] < '0') || (a[o] > '9')) - goto err; - n = (n * 10) + a[o] - '0'; - if (++o > l) - goto err; - - if ((n < min[i]) || (n > max[i])) - goto err; - if (tm) { - switch (i) { - case 0: - tm->tm_year = n < 50 ? n + 100 : n; - break; - case 1: - tm->tm_mon = n - 1; - break; - case 2: - tm->tm_mday = n; - break; - case 3: - tm->tm_hour = n; - break; - case 4: - tm->tm_min = n; - break; - case 5: - tm->tm_sec = n; - break; - } - } + if (d->type != V_ASN1_UTCTIME) { + return 0; } - if (a[o] == 'Z') - o++; - else if ((a[o] == '+') || (a[o] == '-')) { - int offsign = a[o] == '-' ? 1 : -1, offset = 0; - o++; - if (o + 4 > l) - goto err; - for (i = 6; i < 8; i++) { - if ((a[o] < '0') || (a[o] > '9')) - goto err; - n = a[o] - '0'; - o++; - if ((a[o] < '0') || (a[o] > '9')) - goto err; - n = (n * 10) + a[o] - '0'; - if ((n < min[i]) || (n > max[i])) - goto err; - if (tm) { - if (i == 6) - offset = n * 3600; - else if (i == 7) - offset += n * 60; - } - o++; - } - if (offset && !OPENSSL_gmtime_adj(tm, 0, offset * offsign)) - return 0; + CBS cbs; + CBS_init(&cbs, d->data, (size_t)d->length); + if (!CBS_parse_utc_time(&cbs, tm, /*allow_timezone_offset=*/1)) { + return 0; } - return o == l; - err: - return 0; + return 1; } int ASN1_UTCTIME_check(const ASN1_UTCTIME *d) diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc index 6087ef446..ec6b3719c 100644 --- a/crypto/asn1/asn1_test.cc +++ b/crypto/asn1/asn1_test.cc @@ -901,6 +901,31 @@ static std::string ASN1StringToStdString(const ASN1_STRING *str) { ASN1_STRING_get0_data(str) + ASN1_STRING_length(str)); } +static bool ASN1Time_check_time_t(const ASN1_TIME *s, time_t t) { + struct tm stm, ttm; + int day, sec; + + switch (ASN1_STRING_type(s)) { + case V_ASN1_GENERALIZEDTIME: + if (!asn1_generalizedtime_to_tm(&stm, s)) { + return false; + } + break; + case V_ASN1_UTCTIME: + if (!asn1_utctime_to_tm(&stm, s)) { + return false; + } + break; + default: + return 0; + } + if (!OPENSSL_gmtime(&t, &ttm) || + !OPENSSL_gmtime_diff(&day, &sec, &ttm, &stm)) { + return false; + } + return day == 0 && sec ==0; +} + TEST(ASN1Test, SetTime) { static const struct { time_t time; @@ -911,6 +936,7 @@ TEST(ASN1Test, SetTime) { {-631152000, "19500101000000Z", "500101000000Z"}, {0, "19700101000000Z", "700101000000Z"}, {981173106, "20010203040506Z", "010203040506Z"}, + {951804000, "20000229060000Z", "000229060000Z"}, #if defined(OPENSSL_64_BIT) // TODO(https://crbug.com/boringssl/416): These cases overflow 32-bit // |time_t| and do not consistently work on 32-bit platforms. For now, @@ -939,6 +965,7 @@ TEST(ASN1Test, SetTime) { ASSERT_TRUE(utc); EXPECT_EQ(V_ASN1_UTCTIME, ASN1_STRING_type(utc.get())); EXPECT_EQ(t.utc, ASN1StringToStdString(utc.get())); + EXPECT_TRUE(ASN1Time_check_time_t(utc.get(), t.time)); } else { EXPECT_FALSE(utc); } @@ -949,6 +976,7 @@ TEST(ASN1Test, SetTime) { ASSERT_TRUE(generalized); EXPECT_EQ(V_ASN1_GENERALIZEDTIME, ASN1_STRING_type(generalized.get())); EXPECT_EQ(t.generalized, ASN1StringToStdString(generalized.get())); + EXPECT_TRUE(ASN1Time_check_time_t(generalized.get(), t.time)); } else { EXPECT_FALSE(generalized); } @@ -963,6 +991,7 @@ TEST(ASN1Test, SetTime) { EXPECT_EQ(V_ASN1_GENERALIZEDTIME, ASN1_STRING_type(choice.get())); EXPECT_EQ(t.generalized, ASN1StringToStdString(choice.get())); } + EXPECT_TRUE(ASN1Time_check_time_t(choice.get(), t.time)); } else { EXPECT_FALSE(choice); } diff --git a/crypto/asn1/internal.h b/crypto/asn1/internal.h index 5bdaac8f3..29ae20696 100644 --- a/crypto/asn1/internal.h +++ b/crypto/asn1/internal.h @@ -72,7 +72,7 @@ extern "C" { /* Wrapper functions for time functions. */ /* OPENSSL_gmtime wraps |gmtime_r|. See the manual page for that function. */ -struct tm *OPENSSL_gmtime(const time_t *time, struct tm *result); +OPENSSL_EXPORT struct tm *OPENSSL_gmtime(const time_t *time, struct tm *result); /* OPENSSL_gmtime_adj updates |tm| by adding |offset_day| days and |offset_sec| * seconds. */ @@ -81,9 +81,9 @@ int OPENSSL_gmtime_adj(struct tm *tm, int offset_day, long offset_sec); /* OPENSSL_gmtime_diff calculates the difference between |from| and |to| and * outputs the difference as a number of days and seconds in |*out_days| and * |*out_secs|. */ -int OPENSSL_gmtime_diff(int *out_days, int *out_secs, const struct tm *from, - const struct tm *to); - +OPENSSL_EXPORT int OPENSSL_gmtime_diff(int *out_days, int *out_secs, + const struct tm *from, + const struct tm *to); /* Internal ASN1 structures and functions: not for application use */ @@ -216,6 +216,9 @@ typedef struct { OPENSSL_EXPORT void asn1_get_string_table_for_testing( const ASN1_STRING_TABLE **out_ptr, size_t *out_len); +OPENSSL_EXPORT int asn1_generalizedtime_to_tm(struct tm *tm, + const ASN1_GENERALIZEDTIME *d); +OPENSSL_EXPORT int asn1_utctime_to_tm(struct tm *tm, const ASN1_UTCTIME *d); #if defined(__cplusplus) } /* extern C */ diff --git a/crypto/asn1/tasn_dec.c b/crypto/asn1/tasn_dec.c index bb54811ef..972e89b2e 100644 --- a/crypto/asn1/tasn_dec.c +++ b/crypto/asn1/tasn_dec.c @@ -55,14 +55,14 @@ * [including the GNU Public Licence.] */ #include - -#include -#include - #include +#include #include #include +#include +#include + #include "../internal.h" #include "internal.h" @@ -821,6 +821,23 @@ static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, OPENSSL_PUT_ERROR(ASN1, ASN1_R_UNIVERSALSTRING_IS_WRONG_LENGTH); goto err; } + if (utype == V_ASN1_UTCTIME) { + CBS cbs; + CBS_init(&cbs, cont, (size_t)len); + if (!CBS_parse_utc_time(&cbs, NULL, /*allow_timezone_offset=*/1)) { + OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_TIME_FORMAT); + goto err; + } + } + if (utype == V_ASN1_GENERALIZEDTIME) { + CBS cbs; + CBS_init(&cbs, cont, (size_t)len); + if (!CBS_parse_generalized_time(&cbs, NULL, + /*allow_timezone_offset=*/0)) { + OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_TIME_FORMAT); + goto err; + } + } /* All based on ASN1_STRING and handled the same */ if (!*pval) { stmp = ASN1_STRING_type_new(utype); diff --git a/crypto/bytestring/bytestring_test.cc b/crypto/bytestring/bytestring_test.cc index a8c19132a..b6b716e56 100644 --- a/crypto/bytestring/bytestring_test.cc +++ b/crypto/bytestring/bytestring_test.cc @@ -24,9 +24,9 @@ #include #include -#include "internal.h" #include "../internal.h" #include "../test/test_util.h" +#include "internal.h" TEST(CBSTest, Skip) { @@ -1493,3 +1493,127 @@ TEST(CBBTest, Unicode) { EXPECT_EQ(4u, cbb_get_utf8_len(0x10000)); EXPECT_EQ(4u, cbb_get_utf8_len(0x10ffff)); } + +TEST(CBSTest, BogusTime) { + static const struct { + const char *timestring; + } kBogusTimeTests[] = { + {""}, + {"invalidtimesZ"}, + {"Z"}, + {"0000"}, + {"9999Z"}, + {"00000000000000000000000000000Z"}, + {"19491231235959"}, + {"500101000000.001Z"}, + {"500101000000+6"}, + {"-1970010100000Z"}, + {"7a0101000000Z"}, + {"20500101000000-6"}, + {"20500101000000.001"}, + {"20500229000000Z"}, + {"220229000000Z"}, + {"20500132000000Z"}, + {"220132000000Z"}, + {"20500332000000Z"}, + {"220332000000Z"}, + {"20500532000000Z"}, + {"220532000000Z"}, + {"20500732000000Z"}, + {"220732000000Z"}, + {"20500832000000Z"}, + {"220832000000Z"}, + {"20501032000000Z"}, + {"221032000000Z"}, + {"20501232000000Z"}, + {"221232000000Z"}, + {"20500431000000Z"}, + {"220431000000Z"}, + {"20500631000000Z"}, + {"220631000000Z"}, + {"20500931000000Z"}, + {"220931000000Z"}, + {"20501131000000Z"}, + {"221131000000Z"}, + {"20501100000000Z"}, + {"221100000000Z"}, + {"19500101000000+0600"}, + }; + for (const auto &t : kBogusTimeTests) { + SCOPED_TRACE(t.timestring); + CBS cbs; + CBS_init(&cbs, (const uint8_t *)t.timestring, strlen(t.timestring)); + EXPECT_FALSE(CBS_parse_generalized_time(&cbs, NULL, + /*allow_timezone_offset=*/0)); + EXPECT_FALSE(CBS_parse_utc_time(&cbs, NULL, /*allow_timezone_offset=*/1)); + } + static const struct { + const char *timestring; + } kUTCTZTests[] = { + {"480711220333-0700"}, + {"140704000000-0700"}, + {"480222202332-0500"}, + {"480726113216-0000"}, + {"480726113216-2359"}, + }; + for (const auto &t : kUTCTZTests) { + SCOPED_TRACE(t.timestring); + CBS cbs; + CBS_init(&cbs, (const uint8_t *)t.timestring, strlen(t.timestring)); + EXPECT_FALSE(CBS_parse_generalized_time(&cbs, NULL, + /*allow_timezone_offset=*/0)); + EXPECT_FALSE(CBS_parse_generalized_time(&cbs, NULL, + /*allow_timezone_offset=*/1)); + EXPECT_TRUE(CBS_parse_utc_time(&cbs, NULL, /*allow_timezone_offset=*/1)); + EXPECT_FALSE(CBS_parse_utc_time(&cbs, NULL, /*allow_timezone_offset=*/0)); + } + static const struct { + const char *timestring; + } kBogusUTCTZTests[] = { + {"480711220333-0160"}, + {"140704000000-9999"}, + {"480222202332-2400"}, + }; + for (const auto &t : kBogusUTCTZTests) { + SCOPED_TRACE(t.timestring); + CBS cbs; + CBS_init(&cbs, (const uint8_t *)t.timestring, strlen(t.timestring)); + EXPECT_FALSE(CBS_parse_generalized_time(&cbs, NULL, + /*allow_timezone_offset=*/0)); + EXPECT_FALSE(CBS_parse_utc_time(&cbs, NULL, /*allow_timezone_offset=*/1)); + } + static const struct { + const char *timestring; + } kGenTZTests[] = { + {"20480711220333-0000"}, + {"20140704000000-0100"}, + {"20460311174630-0300"}, + {"20140704000000-2359"}, + }; + for (const auto &t : kGenTZTests) { + SCOPED_TRACE(t.timestring); + CBS cbs; + CBS_init(&cbs, (const uint8_t *)t.timestring, strlen(t.timestring)); + EXPECT_FALSE(CBS_parse_generalized_time(&cbs, NULL, + /*allow_timezone_offset=*/0)); + EXPECT_TRUE(CBS_parse_generalized_time(&cbs, NULL, + /*allow_timezone_offset=*/1)); + EXPECT_FALSE(CBS_parse_utc_time(&cbs, NULL, /*allow_timezone_offset=*/1)); + EXPECT_FALSE(CBS_parse_utc_time(&cbs, NULL, /*allow_timezone_offset=*/0)); + } + static const struct { + const char *timestring; + } kBogusGenTZTests[] = { + {"20480222202332-2400"}, + {"20140704000000-9999"}, + {"20480726113216-0160"}, + }; + for (const auto &t : kBogusGenTZTests) { + SCOPED_TRACE(t.timestring); + CBS cbs; + CBS_init(&cbs, (const uint8_t *)t.timestring, strlen(t.timestring)); + EXPECT_FALSE(CBS_parse_generalized_time(&cbs, NULL, + /*allow_timezone_offset=*/0)); + EXPECT_FALSE(CBS_parse_utc_time(&cbs, NULL, /*allow_timezone_offset=*/1)); + } +} diff --git a/crypto/bytestring/cbs.c b/crypto/bytestring/cbs.c index 741ecfbc5..4e7f37971 100644 --- a/crypto/bytestring/cbs.c +++ b/crypto/bytestring/cbs.c @@ -12,15 +12,18 @@ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#include +#include #include +#include #include +#include #include #include -#include "internal.h" +#include "../asn1/internal.h" #include "../internal.h" +#include "internal.h" void CBS_init(CBS *cbs, const uint8_t *data, size_t len) { @@ -720,3 +723,161 @@ err: CBB_cleanup(&cbb); return NULL; } + +static int cbs_get_two_digits(CBS *cbs, int *out) { + uint8_t first_digit, second_digit; + if (!CBS_get_u8(cbs, &first_digit)) { + return 0; + } + if (!isdigit(first_digit)) { + return 0; + } + if (!CBS_get_u8(cbs, &second_digit)) { + return 0; + } + if (!isdigit(second_digit)) { + return 0; + } + *out = (first_digit - '0') * 10 + (second_digit - '0'); + return 1; +} + +static int is_valid_day(int year, int month, int day) { + if (day < 1) { + return 0; + } + switch (month) { + case 1: + case 3: + case 5: + case 7: + case 8: + case 10: + case 12: + return day <= 31; + case 4: + case 6: + case 9: + case 11: + return day <= 30; + case 2: + if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) { + return day <= 29; + } else { + return day <= 28; + } + default: + return 0; + } +} + +static int CBS_parse_rfc5280_time_internal(const CBS *cbs, int is_gentime, + int allow_timezone_offset, + struct tm *out_tm) { + int year, month, day, hour, min, sec, tmp; + CBS copy = *cbs; + uint8_t tz; + + if (is_gentime) { + if (!cbs_get_two_digits(©, &tmp)) { + return 0; + } + year = tmp * 100; + if (!cbs_get_two_digits(©, &tmp)) { + return 0; + } + year += tmp; + } else { + year = 1900; + if (!cbs_get_two_digits(©, &tmp)) { + return 0; + } + year += tmp; + if (year < 1950) { + year += 100; + } + if (year >= 2050) { + return 0; // A Generalized time must be used. + } + } + if (!cbs_get_two_digits(©, &month) || month < 1 || + month > 12 || // Reject invalid months. + !cbs_get_two_digits(©, &day) || + !is_valid_day(year, month, day) || // Reject invalid days. + !cbs_get_two_digits(©, &hour) || + hour > 23 || // Reject invalid hours. + !cbs_get_two_digits(©, &min) || + min > 59 || // Reject invalid minutes. + !cbs_get_two_digits(©, &sec) || sec > 59 || !CBS_get_u8(©, &tz)) { + return 0; + } + + int offset_sign = 0; + switch (tz) { + case 'Z': + break; // We correctly have 'Z' on the end as per spec. + case '+': + offset_sign = 1; + break; // Should not be allowed per RFC 5280. + case '-': + offset_sign = -1; + break; // Should not be allowed per RFC 5280. + default: + return 0; // Reject anything else after the time. + } + + // If allow_timezone_offset is non-zero, allow for a four digit timezone + // offset to be specified even though this is not allowed by RFC 5280. We are + // permissive of this for UTCTimes due to the unfortunate existence of + // artisinally rolled long lived certificates that were baked into places that + // are now difficult to change. These certificates were generated with the + // 'openssl' command that permissively allowed the creation of certificates + // with notBefore and notAfter times specified as strings for direct + // certificate inclusion on the command line. For context see cl/237068815. + // + // TODO(bbe): This has been expunged from public web-pki as the ecosystem has + // managed to encourage CA compliance with standards. We should find a way to + // get rid of this or make it off by default. + int offset_seconds = 0; + if (offset_sign != 0) { + if (!allow_timezone_offset) { + return 0; + } + int offset_hours, offset_minutes; + if (!cbs_get_two_digits(©, &offset_hours) || + offset_hours > 23 || // Reject invalid hours. + !cbs_get_two_digits(©, &offset_minutes) || + offset_minutes > 59) { // Reject invalid minutes. + return 0; + } + offset_seconds = offset_sign * (offset_hours * 3600 + offset_minutes * 60); + } + + if (CBS_len(©) != 0) { + return 0; // Reject invalid lengths. + } + + if (out_tm != NULL) { + // Fill in the tm fields corresponding to what we validated. + out_tm->tm_year = year - 1900; + out_tm->tm_mon = month - 1; + out_tm->tm_mday = day; + out_tm->tm_hour = hour; + out_tm->tm_min = min; + out_tm->tm_sec = sec; + if (offset_seconds && !OPENSSL_gmtime_adj(out_tm, 0, offset_seconds)) { + return 0; + } + } + return 1; +} + +int CBS_parse_generalized_time(const CBS *cbs, struct tm *out_tm, + int allow_timezone_offset) { + return CBS_parse_rfc5280_time_internal(cbs, 1, allow_timezone_offset, out_tm); +} + +int CBS_parse_utc_time(const CBS *cbs, struct tm *out_tm, + int allow_timezone_offset) { + return CBS_parse_rfc5280_time_internal(cbs, 0, allow_timezone_offset, out_tm); +} diff --git a/include/openssl/bytestring.h b/include/openssl/bytestring.h index 68c1ba4c5..846ab2461 100644 --- a/include/openssl/bytestring.h +++ b/include/openssl/bytestring.h @@ -18,6 +18,7 @@ #include #include +#include #if defined(__cplusplus) extern "C" { @@ -353,6 +354,25 @@ OPENSSL_EXPORT int CBS_is_unsigned_asn1_integer(const CBS *cbs); OPENSSL_EXPORT char *CBS_asn1_oid_to_text(const CBS *cbs); +// CBS_parse_generalized_time returns one if |cbs| is a valid DER-encoded, ASN.1 +// GeneralizedTime body within the limitations imposed by RFC 5280, or zero +// otherwise. If |allow_timezone_offset| is non-zero, four-digit timezone +// offsets, which would not be allowed by DER, are permitted. On success, if +// |out_tm| is non-NULL, |*out_tm| will be zeroed, and then set to the +// corresponding time in UTC. This function does not compute |out_tm->tm_wday| +// or |out_tm->tm_yday|. +OPENSSL_EXPORT int CBS_parse_generalized_time(const CBS *cbs, struct tm *out_tm, + int allow_timezeone_offset); + +// CBS_parse_utc_time returns one if |cbs| is a valid DER-encoded, ASN.1 +// UTCTime body within the limitations imposed by RFC 5280, or zero otherwise. +// If |allow_timezone_offset| is non-zero, four-digit timezone offsets, which +// would not be allowed by DER, are permitted. On success, if |out_tm| is +// non-NULL, |*out_tm| will be zeroed, and then set to the corresponding time +// in UTC. This function does not compute |out_tm->tm_wday| or |out_tm->tm_yday|. +OPENSSL_EXPORT int CBS_parse_utc_time(const CBS *cbs, struct tm *out_tm, + int allow_timezeone_offset); + // CRYPTO ByteBuilder. // // |CBB| objects allow one to build length-prefixed serialisations. A |CBB|