Fix |ASN1_INTEGER_set| when setting zero.

|ASN1_INTEGER_set| and |BN_to_ASN1_INTEGER| disagree about how to encode
zero. OpenSSL master has aligned around the behaviour of the latter
(i.e. a single zero byte) so fix |ASN1_INTEGER_set| to do that. (This is
also the form that DER requires.)

At the same time, fix undefined behaviour when negative a |long| whose
value is |LONG_MIN|.

Change-Id: I1198de35e61a286ac6472e99152f3d22fda59044
Reviewed-on: https://boringssl-review.googlesource.com/24485
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/asn1/a_int.c b/crypto/asn1/a_int.c
index b53c00b..dd74550 100644
--- a/crypto/asn1/a_int.c
+++ b/crypto/asn1/a_int.c
@@ -347,40 +347,16 @@
 
 int ASN1_INTEGER_set(ASN1_INTEGER *a, long v)
 {
-    int j, k;
-    unsigned int i;
-    unsigned char buf[sizeof(long) + 1];
-    long d;
-
-    a->type = V_ASN1_INTEGER;
-    if (a->length < (int)(sizeof(long) + 1)) {
-        if (a->data != NULL)
-            OPENSSL_free(a->data);
-        if ((a->data =
-             (unsigned char *)OPENSSL_malloc(sizeof(long) + 1)) != NULL)
-            OPENSSL_memset((char *)a->data, 0, sizeof(long) + 1);
-    }
-    if (a->data == NULL) {
-        OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
-        return (0);
-    }
-    d = v;
-    if (d < 0) {
-        d = -d;
-        a->type = V_ASN1_NEG_INTEGER;
+    if (v >= 0) {
+        return ASN1_INTEGER_set_uint64(a, (uint64_t) v);
     }
 
-    for (i = 0; i < sizeof(long); i++) {
-        if (d == 0)
-            break;
-        buf[i] = (int)d & 0xff;
-        d >>= 8;
+    if (!ASN1_INTEGER_set_uint64(a, 0 - (uint64_t) v)) {
+        return 0;
     }
-    j = 0;
-    for (k = i - 1; k >= 0; k--)
-        a->data[j++] = buf[k];
-    a->length = j;
-    return (1);
+
+    a->type = V_ASN1_NEG_INTEGER;
+    return 1;
 }
 
 int ASN1_INTEGER_set_uint64(ASN1_INTEGER *out, uint64_t v)
diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc
index 55c0f17..7c114dd 100644
--- a/crypto/asn1/asn1_test.cc
+++ b/crypto/asn1/asn1_test.cc
@@ -15,6 +15,7 @@
 #include <stdio.h>
 
 #include <gtest/gtest.h>
+#include <limits.h>
 
 #include <openssl/asn1.h>
 #include <openssl/err.h>
@@ -67,23 +68,23 @@
   bssl::UniquePtr<ASN1_INTEGER> by_uint64(M_ASN1_INTEGER_new());
   bssl::UniquePtr<BIGNUM> bn(BN_new());
 
-  const std::vector<unsigned> kValues = {
-      0, 1, 2, 0xff, 0x100, 0xffff, 0x10000,
+  const std::vector<int64_t> kValues = {
+      LONG_MIN, -2, -1, 0, 1, 2, 0xff, 0x100, 0xffff, 0x10000, LONG_MAX,
   };
   for (const auto &i : kValues) {
     SCOPED_TRACE(i);
 
     ASSERT_EQ(1, ASN1_INTEGER_set(by_long.get(), i));
-    ASSERT_EQ(1, ASN1_INTEGER_set_uint64(by_uint64.get(), i));
-    ASSERT_TRUE(BN_set_word(bn.get(), i));
+    const uint64_t abs = i < 0 ? (0 - (uint64_t) i) : i;
+    ASSERT_TRUE(BN_set_u64(bn.get(), abs));
+    BN_set_negative(bn.get(), i < 0);
     ASSERT_TRUE(BN_to_ASN1_INTEGER(bn.get(), by_bn.get()));
 
-    if (i != 0) {
-      // |ASN1_INTEGER_set| and |BN_to_ASN1_INTEGER| disagree about how to
-      // encode zero. The former leaves an empty value while the latter encodes
-      // as a single zero byte.
-      EXPECT_EQ(0, ASN1_INTEGER_cmp(by_bn.get(), by_long.get()));
+    EXPECT_EQ(0, ASN1_INTEGER_cmp(by_bn.get(), by_long.get()));
+
+    if (i >= 0) {
+      ASSERT_EQ(1, ASN1_INTEGER_set_uint64(by_uint64.get(), i));
+      EXPECT_EQ(0, ASN1_INTEGER_cmp(by_bn.get(), by_uint64.get()));
     }
-    EXPECT_EQ(0, ASN1_INTEGER_cmp(by_bn.get(), by_uint64.get()));
   }
 }