Add some tests for time_t to ASN1_TIME conversions.

Change-Id: I7712f66e16b761ee23292980cff039e62d29b22f
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/48666
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc
index 725542c..e7dfaa9 100644
--- a/crypto/asn1/asn1_test.cc
+++ b/crypto/asn1/asn1_test.cc
@@ -15,6 +15,7 @@
 #include <limits.h>
 #include <stdio.h>
 
+#include <string>
 #include <vector>
 
 #include <gtest/gtest.h>
@@ -456,6 +457,76 @@
   }
 }
 
+static std::string ASN1StringToStdString(const ASN1_STRING *str) {
+  return std::string(ASN1_STRING_get0_data(str),
+                     ASN1_STRING_get0_data(str) + ASN1_STRING_length(str));
+}
+
+TEST(ASN1Test, SetTime) {
+  static const struct {
+    time_t time;
+    const char *generalized;
+    const char *utc;
+  } kTests[] = {
+    {-631152001, "19491231235959Z", nullptr},
+    {-631152000, "19500101000000Z", "500101000000Z"},
+    {0, "19700101000000Z", "700101000000Z"},
+    {981173106, "20010203040506Z", "010203040506Z"},
+#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,
+    // disable the tests on 32-bit. Re-enable them once the bug is fixed.
+    {2524607999, "20491231235959Z", "491231235959Z"},
+    {2524608000, "20500101000000Z", nullptr},
+    // TODO(davidben): Fix and then test boundary conditions for GeneralizedTime
+    // years.
+#endif
+  };
+  for (const auto &t : kTests) {
+    SCOPED_TRACE(t.time);
+#if defined(OPENSSL_WINDOWS)
+    // Windows |time_t| functions can only handle 1970 through 3000. See
+    // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/gmtime-s-gmtime32-s-gmtime64-s?view=msvc-160
+    if (t.time < 0 || int64_t{t.time} > 32535215999) {
+      continue;
+    }
+#endif
+
+    bssl::UniquePtr<ASN1_UTCTIME> utc(ASN1_UTCTIME_set(nullptr, t.time));
+    if (t.utc) {
+      ASSERT_TRUE(utc);
+      EXPECT_EQ(V_ASN1_UTCTIME, ASN1_STRING_type(utc.get()));
+      EXPECT_EQ(t.utc, ASN1StringToStdString(utc.get()));
+    } else {
+      EXPECT_FALSE(utc);
+    }
+
+    bssl::UniquePtr<ASN1_GENERALIZEDTIME> generalized(
+        ASN1_GENERALIZEDTIME_set(nullptr, t.time));
+    if (t.generalized) {
+      ASSERT_TRUE(generalized);
+      EXPECT_EQ(V_ASN1_GENERALIZEDTIME, ASN1_STRING_type(generalized.get()));
+      EXPECT_EQ(t.generalized, ASN1StringToStdString(generalized.get()));
+    } else {
+      EXPECT_FALSE(generalized);
+    }
+
+    bssl::UniquePtr<ASN1_TIME> choice(ASN1_TIME_set(nullptr, t.time));
+    if (t.generalized) {
+      ASSERT_TRUE(choice);
+      if (t.utc) {
+        EXPECT_EQ(V_ASN1_UTCTIME, ASN1_STRING_type(choice.get()));
+        EXPECT_EQ(t.utc, ASN1StringToStdString(choice.get()));
+      } else {
+        EXPECT_EQ(V_ASN1_GENERALIZEDTIME, ASN1_STRING_type(choice.get()));
+        EXPECT_EQ(t.generalized, ASN1StringToStdString(choice.get()));
+      }
+    } else {
+      EXPECT_FALSE(choice);
+    }
+  }
+}
+
 // The ASN.1 macros do not work on Windows shared library builds, where usage of
 // |OPENSSL_EXPORT| is a bit stricter.
 #if !defined(OPENSSL_WINDOWS) || !defined(BORINGSSL_SHARED_LIBRARY)