Add ASN1_INTEGER_get_int64 and ASN1_ENUMERATED_get_int64.

Node uses this.

Change-Id: I13e1734a8f60d4ad0c6a7bcab830c3a0406542b1
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/54307
Commit-Queue: Bob Beck <bbe@google.com>
Reviewed-by: Bob Beck <bbe@google.com>
diff --git a/crypto/asn1/a_int.c b/crypto/asn1/a_int.c
index afc88d2..89d6a54 100644
--- a/crypto/asn1/a_int.c
+++ b/crypto/asn1/a_int.c
@@ -333,16 +333,11 @@
   return asn1_string_get_uint64(out, a, V_ASN1_ENUMERATED);
 }
 
-static long asn1_string_get_long(const ASN1_STRING *a, int type) {
-  if (a == NULL) {
-    return 0;
-  }
-
+static int asn1_string_get_int64(int64_t *out, const ASN1_STRING *a, int type) {
   uint64_t v;
   if (!asn1_string_get_abs_uint64(&v, a, type)) {
-    goto err;
+    return 0;
   }
-
   int64_t i64;
   int fits_in_i64;
   // Check |v != 0| to handle manually-constructed negative zeros.
@@ -353,16 +348,36 @@
     i64 = (int64_t)v;
     fits_in_i64 = i64 >= 0;
   }
-  static_assert(sizeof(long) <= sizeof(int64_t), "long is too big");
+  if (!fits_in_i64) {
+    OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_INTEGER);
+    return 0;
+  }
+  *out = i64;
+  return 1;
+}
 
-  if (fits_in_i64 && LONG_MIN <= i64 && i64 <= LONG_MAX) {
-    return (long)i64;
+int ASN1_INTEGER_get_int64(int64_t *out, const ASN1_INTEGER *a) {
+  return asn1_string_get_int64(out, a, V_ASN1_INTEGER);
+}
+
+int ASN1_ENUMERATED_get_int64(int64_t *out, const ASN1_ENUMERATED *a) {
+  return asn1_string_get_int64(out, a, V_ASN1_ENUMERATED);
+}
+
+static long asn1_string_get_long(const ASN1_STRING *a, int type) {
+  if (a == NULL) {
+    return 0;
   }
 
-err:
-  // This function's return value does not distinguish overflow from -1.
-  ERR_clear_error();
-  return -1;
+  int64_t v;
+  if (!asn1_string_get_int64(&v, a, type) ||  //
+      v < LONG_MIN || v > LONG_MAX) {
+    // This function's return value does not distinguish overflow from -1.
+    ERR_clear_error();
+    return -1;
+  }
+
+  return (long)v;
 }
 
 long ASN1_INTEGER_get(const ASN1_INTEGER *a) {
diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc
index ffb3e4a..4d07a8b 100644
--- a/crypto/asn1/asn1_test.cc
+++ b/crypto/asn1/asn1_test.cc
@@ -328,9 +328,10 @@
     EXPECT_EQ(ptr, t.der.data() + t.der.size());
     objs["der"] = std::move(by_der);
 
-    // Construct |ASN1_INTEGER| from |long| or |uint64_t|, if it fits.
-    bool fits_in_long = false, fits_in_u64 = false;
+    // Construct |ASN1_INTEGER| from various C types, if it fits.
+    bool fits_in_long = false, fits_in_i64 = false, fits_in_u64 = false;
     uint64_t u64 = 0;
+    int64_t i64 = 0;
     long l = 0;
     uint64_t abs_u64;
     if (BN_get_u64(bn.get(), &abs_u64)) {
@@ -343,20 +344,26 @@
         objs["u64"] = std::move(by_u64);
       }
 
+      fits_in_i64 = BN_cmp(int64_min.get(), bn.get()) <= 0 &&
+                    BN_cmp(bn.get(), int64_max.get()) <= 0;
+
+      if (fits_in_i64) {
+        if (BN_is_negative(bn.get())) {
+          i64 = static_cast<int64_t>(0u - abs_u64);
+        } else {
+          i64 = static_cast<int64_t>(abs_u64);
+        }
+      }
+
       if (sizeof(long) == 8) {
-        fits_in_long = BN_cmp(int64_min.get(), bn.get()) <= 0 &&
-                       BN_cmp(bn.get(), int64_max.get()) <= 0;
+        fits_in_long = fits_in_i64;
       } else {
         ASSERT_EQ(4u, sizeof(long));
         fits_in_long = BN_cmp(int32_min.get(), bn.get()) <= 0 &&
                        BN_cmp(bn.get(), int32_max.get()) <= 0;
       }
       if (fits_in_long) {
-        if (BN_is_negative(bn.get())) {
-          l = static_cast<long>(0u - abs_u64);
-        } else {
-          l = static_cast<long>(abs_u64);
-        }
+        l = static_cast<long>(i64);
         bssl::UniquePtr<ASN1_INTEGER> by_long(ASN1_INTEGER_new());
         ASSERT_TRUE(by_long);
         ASSERT_TRUE(ASN1_INTEGER_set(by_long.get(), l));
@@ -396,6 +403,15 @@
         EXPECT_FALSE(ASN1_INTEGER_get_uint64(&v, obj));
       }
 
+      if (fits_in_i64) {
+        int64_t v;
+        ASSERT_TRUE(ASN1_INTEGER_get_int64(&v, obj));
+        EXPECT_EQ(v, i64);
+      } else {
+        int64_t v;
+        EXPECT_FALSE(ASN1_INTEGER_get_int64(&v, obj));
+      }
+
       if (fits_in_long) {
         EXPECT_EQ(l, ASN1_INTEGER_get(obj));
       } else {
diff --git a/include/openssl/asn1.h b/include/openssl/asn1.h
index b7dfed2..8c43ee0 100644
--- a/include/openssl/asn1.h
+++ b/include/openssl/asn1.h
@@ -1090,6 +1090,11 @@
 OPENSSL_EXPORT int ASN1_INTEGER_get_uint64(uint64_t *out,
                                            const ASN1_INTEGER *a);
 
+// ASN1_INTEGER_get_int64 converts |a| to a |int64_t|. On success, it returns
+// one and sets |*out| to the result. If |a| did not fit or has the wrong type,
+// it returns zero.
+OPENSSL_EXPORT int ASN1_INTEGER_get_int64(int64_t *out, const ASN1_INTEGER *a);
+
 // ASN1_INTEGER_get returns the value of |a| as a |long|, or -1 if |a| is out of
 // range or the wrong type.
 //
@@ -1154,6 +1159,12 @@
 OPENSSL_EXPORT int ASN1_ENUMERATED_get_uint64(uint64_t *out,
                                               const ASN1_ENUMERATED *a);
 
+// ASN1_ENUMERATED_get_int64 converts |a| to a |int64_t|. On success, it
+// returns one and sets |*out| to the result. If |a| did not fit or has the
+// wrong type, it returns zero.
+OPENSSL_EXPORT int ASN1_ENUMERATED_get_int64(int64_t *out,
+                                             const ASN1_ENUMERATED *a);
+
 // ASN1_ENUMERATED_get returns the value of |a| as a |long|, or -1 if |a| is out
 // of range or the wrong type.
 //