From 761c3ed03c80e1dd8806bc114c26226fa5aeec62 Mon Sep 17 00:00:00 2001 From: David Benjamin Date: Sun, 4 Jun 2023 12:21:29 -0400 Subject: [PATCH] Add ASN1_TIME_set_string_X509 rust-openssl uses this function when targetting OpenSSL 1.1.x. Change-Id: Ifeb1b65be9976358f9ee636ed23c1a931e03b275 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/60609 Auto-Submit: David Benjamin Reviewed-by: Bob Beck Commit-Queue: Bob Beck --- crypto/asn1/a_time.c | 35 ++++++++++++++++++++++++++++++++++- crypto/asn1/asn1_test.cc | 35 +++++++++++++++++++++++++++++++++++ include/openssl/asn1.h | 5 +++++ 3 files changed, 74 insertions(+), 1 deletion(-) diff --git a/crypto/asn1/a_time.c b/crypto/asn1/a_time.c index 87bf09213..d8ec85f7c 100644 --- a/crypto/asn1/a_time.c +++ b/crypto/asn1/a_time.c @@ -61,6 +61,7 @@ #include #include +#include #include #include @@ -82,6 +83,10 @@ ASN1_TIME *ASN1_TIME_set(ASN1_TIME *s, time_t time) { return ASN1_TIME_adj(s, time, 0, 0); } +static int fits_in_utc_time(const struct tm *tm) { + return 50 <= tm->tm_year && tm->tm_year < 150; +} + ASN1_TIME *ASN1_TIME_adj(ASN1_TIME *s, int64_t posix_time, int offset_day, long offset_sec) { struct tm tm; @@ -95,7 +100,7 @@ ASN1_TIME *ASN1_TIME_adj(ASN1_TIME *s, int64_t posix_time, int offset_day, return NULL; } } - if ((tm.tm_year >= 50) && (tm.tm_year < 150)) { + if (fits_in_utc_time(&tm)) { return ASN1_UTCTIME_adj(s, posix_time, offset_day, offset_sec); } return ASN1_GENERALIZEDTIME_adj(s, posix_time, offset_day, offset_sec); @@ -171,6 +176,34 @@ int ASN1_TIME_set_string(ASN1_TIME *s, const char *str) { ASN1_GENERALIZEDTIME_set_string(s, str); } +int ASN1_TIME_set_string_X509(ASN1_TIME *s, const char *str) { + CBS cbs; + CBS_init(&cbs, (const uint8_t*)str, strlen(str)); + int type; + struct tm tm; + if (CBS_parse_utc_time(&cbs, /*out_tm=*/NULL, + /*allow_timezone_offset=*/0)) { + type = V_ASN1_UTCTIME; + } else if (CBS_parse_generalized_time(&cbs, &tm, + /*allow_timezone_offset=*/0)) { + type = V_ASN1_GENERALIZEDTIME; + if (fits_in_utc_time(&tm)) { + type = V_ASN1_UTCTIME; + CBS_skip(&cbs, 2); + } + } else { + return 0; + } + + if (s != NULL) { + if (!ASN1_STRING_set(s, CBS_data(&cbs), CBS_len(&cbs))) { + return 0; + } + s->type = type; + } + return 1; +} + static int asn1_time_to_tm(struct tm *tm, const ASN1_TIME *t, int allow_timezone_offset) { if (t == NULL) { diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc index c7e0bf059..94f227287 100644 --- a/crypto/asn1/asn1_test.cc +++ b/crypto/asn1/asn1_test.cc @@ -1100,6 +1100,32 @@ TEST(ASN1Test, TimeSetString) { EXPECT_EQ(V_ASN1_GENERALIZEDTIME, ASN1_STRING_type(s.get())); EXPECT_EQ("19700101000000Z", ASN1StringToStdString(s.get())); + // |ASN1_TIME_set_string_X509| behaves similarly except it additionally + // converts GeneralizedTime to UTCTime if it fits. + ASSERT_TRUE(ASN1_TIME_set_string_X509(s.get(), "700101000000Z")); + EXPECT_EQ(V_ASN1_UTCTIME, ASN1_STRING_type(s.get())); + EXPECT_EQ("700101000000Z", ASN1StringToStdString(s.get())); + + ASSERT_TRUE(ASN1_TIME_set_string_X509(s.get(), "19700101000000Z")); + EXPECT_EQ(V_ASN1_UTCTIME, ASN1_STRING_type(s.get())); + EXPECT_EQ("700101000000Z", ASN1StringToStdString(s.get())); + + ASSERT_TRUE(ASN1_TIME_set_string_X509(s.get(), "19500101000000Z")); + EXPECT_EQ(V_ASN1_UTCTIME, ASN1_STRING_type(s.get())); + EXPECT_EQ("500101000000Z", ASN1StringToStdString(s.get())); + + ASSERT_TRUE(ASN1_TIME_set_string_X509(s.get(), "19491231235959Z")); + EXPECT_EQ(V_ASN1_GENERALIZEDTIME, ASN1_STRING_type(s.get())); + EXPECT_EQ("19491231235959Z", ASN1StringToStdString(s.get())); + + ASSERT_TRUE(ASN1_TIME_set_string_X509(s.get(), "20491231235959Z")); + EXPECT_EQ(V_ASN1_UTCTIME, ASN1_STRING_type(s.get())); + EXPECT_EQ("491231235959Z", ASN1StringToStdString(s.get())); + + ASSERT_TRUE(ASN1_TIME_set_string_X509(s.get(), "20500101000000Z")); + EXPECT_EQ(V_ASN1_GENERALIZEDTIME, ASN1_STRING_type(s.get())); + EXPECT_EQ("20500101000000Z", ASN1StringToStdString(s.get())); + // Invalid inputs are rejected. EXPECT_FALSE(ASN1_UTCTIME_set_string(s.get(), "nope")); EXPECT_FALSE(ASN1_UTCTIME_set_string(s.get(), "19700101000000Z")); @@ -1111,17 +1137,26 @@ TEST(ASN1Test, TimeSetString) { // to anything. EXPECT_TRUE(ASN1_UTCTIME_set_string(nullptr, "700101000000Z")); EXPECT_TRUE(ASN1_TIME_set_string(nullptr, "700101000000Z")); + EXPECT_TRUE(ASN1_TIME_set_string_X509(nullptr, "700101000000Z")); EXPECT_TRUE(ASN1_GENERALIZEDTIME_set_string(nullptr, "19700101000000Z")); EXPECT_TRUE(ASN1_TIME_set_string(nullptr, "19700101000000Z")); + EXPECT_TRUE(ASN1_TIME_set_string_X509(nullptr, "19700101000000Z")); + // Test an input |ASN1_TIME_set_string_X509| won't convert to UTCTime. + EXPECT_TRUE(ASN1_GENERALIZEDTIME_set_string(nullptr, "20500101000000Z")); + EXPECT_TRUE(ASN1_TIME_set_string(nullptr, "20500101000000Z")); + EXPECT_TRUE(ASN1_TIME_set_string_X509(nullptr, "20500101000000Z")); EXPECT_FALSE(ASN1_UTCTIME_set_string(nullptr, "nope")); EXPECT_FALSE(ASN1_GENERALIZEDTIME_set_string(nullptr, "nope")); EXPECT_FALSE(ASN1_TIME_set_string(nullptr, "nope")); + EXPECT_FALSE(ASN1_TIME_set_string_X509(nullptr, "nope")); // Timezone offsets are not allowed by DER. EXPECT_FALSE(ASN1_UTCTIME_set_string(nullptr, "700101000000-0400")); EXPECT_FALSE(ASN1_TIME_set_string(nullptr, "700101000000-0400")); + EXPECT_FALSE(ASN1_TIME_set_string_X509(nullptr, "700101000000-0400")); EXPECT_FALSE(ASN1_GENERALIZEDTIME_set_string(nullptr, "19700101000000-0400")); EXPECT_FALSE(ASN1_TIME_set_string(nullptr, "19700101000000-0400")); + EXPECT_FALSE(ASN1_TIME_set_string_X509(nullptr, "19700101000000-0400")); } TEST(ASN1Test, AdjTime) { diff --git a/include/openssl/asn1.h b/include/openssl/asn1.h index d128c8dfe..c9f265a28 100644 --- a/include/openssl/asn1.h +++ b/include/openssl/asn1.h @@ -1355,6 +1355,11 @@ OPENSSL_EXPORT ASN1_GENERALIZEDTIME *ASN1_TIME_to_generalizedtime( // GeneralizedTime. If |str| is neither, it returns zero. OPENSSL_EXPORT int ASN1_TIME_set_string(ASN1_TIME *s, const char *str); +// ASN1_TIME_set_string_X509 behaves like |ASN1_TIME_set_string| except it +// additionally converts GeneralizedTime to UTCTime if it is in the range where +// UTCTime is used. See RFC 5280, section 4.1.2.5. +OPENSSL_EXPORT int ASN1_TIME_set_string_X509(ASN1_TIME *s, const char *str); + // ASN1_TIME_to_time_t converts |t| to a time_t value in |out|. On // success, one is returned. On failure zero is returned. This function // will fail if the time can not be represented in a time_t.