Add ASN1_INTEGET_set_uint64.

Change-Id: I3298875a376c98cbb60deb8c99b9548c84b014df
Reviewed-on: https://boringssl-review.googlesource.com/24484
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 8a4edd6..b53c00b 100644
--- a/crypto/asn1/a_int.c
+++ b/crypto/asn1/a_int.c
@@ -383,6 +383,35 @@
     return (1);
 }
 
+int ASN1_INTEGER_set_uint64(ASN1_INTEGER *out, uint64_t v)
+{
+    uint8_t *const newdata = OPENSSL_malloc(sizeof(uint64_t));
+    if (newdata == NULL) {
+        OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
+        return 0;
+    }
+
+    OPENSSL_free(out->data);
+    out->data = newdata;
+    v = CRYPTO_bswap8(v);
+    memcpy(out->data, &v, sizeof(v));
+
+    out->type = V_ASN1_INTEGER;
+
+    size_t leading_zeros;
+    for (leading_zeros = 0; leading_zeros < sizeof(uint64_t) - 1;
+         leading_zeros++) {
+        if (out->data[leading_zeros] != 0) {
+            break;
+        }
+    }
+
+    out->length = sizeof(uint64_t) - leading_zeros;
+    OPENSSL_memmove(out->data, out->data + leading_zeros, out->length);
+
+    return 1;
+}
+
 long ASN1_INTEGER_get(const ASN1_INTEGER *a)
 {
     int neg = 0, i;
diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc
index accf3ba..55c0f17 100644
--- a/crypto/asn1/asn1_test.cc
+++ b/crypto/asn1/asn1_test.cc
@@ -60,3 +60,30 @@
   EXPECT_EQ(Bytes(&kZero, 1), Bytes(obj->value.asn1_string->data,
                                     obj->value.asn1_string->length));
 }
+
+TEST(ASN1Test, IntegerSetting) {
+  bssl::UniquePtr<ASN1_INTEGER> by_bn(M_ASN1_INTEGER_new());
+  bssl::UniquePtr<ASN1_INTEGER> by_long(M_ASN1_INTEGER_new());
+  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,
+  };
+  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));
+    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_uint64.get()));
+  }
+}
diff --git a/crypto/fipsmodule/modes/internal.h b/crypto/fipsmodule/modes/internal.h
index f6ee8f4..68ef4dc 100644
--- a/crypto/fipsmodule/modes/internal.h
+++ b/crypto/fipsmodule/modes/internal.h
@@ -66,38 +66,6 @@
 #define STRICT_ALIGNMENT 0
 #endif
 
-#if defined(__GNUC__) && __GNUC__ >= 2
-static inline uint32_t CRYPTO_bswap4(uint32_t x) {
-  return __builtin_bswap32(x);
-}
-
-static inline uint64_t CRYPTO_bswap8(uint64_t x) {
-  return __builtin_bswap64(x);
-}
-#elif defined(_MSC_VER)
-OPENSSL_MSVC_PRAGMA(warning(push, 3))
-#include <intrin.h>
-OPENSSL_MSVC_PRAGMA(warning(pop))
-#pragma intrinsic(_byteswap_uint64, _byteswap_ulong)
-static inline uint32_t CRYPTO_bswap4(uint32_t x) {
-  return _byteswap_ulong(x);
-}
-
-static inline uint64_t CRYPTO_bswap8(uint64_t x) {
-  return _byteswap_uint64(x);
-}
-#else
-static inline uint32_t CRYPTO_bswap4(uint32_t x) {
-  x = (x >> 16) | (x << 16);
-  x = ((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8);
-  return x;
-}
-
-static inline uint64_t CRYPTO_bswap8(uint64_t x) {
-  return CRYPTO_bswap4(x >> 32) | (((uint64_t)CRYPTO_bswap4(x)) << 32);
-}
-#endif
-
 static inline uint32_t GETU32(const void *in) {
   uint32_t v;
   OPENSSL_memcpy(&v, in, sizeof(v));
diff --git a/crypto/internal.h b/crypto/internal.h
index 5706414..e6bab02 100644
--- a/crypto/internal.h
+++ b/crypto/internal.h
@@ -591,6 +591,41 @@
                                         void *obj, CRYPTO_EX_DATA *ad);
 
 
+// Endianness conversions.
+
+#if defined(__GNUC__) && __GNUC__ >= 2
+static inline uint32_t CRYPTO_bswap4(uint32_t x) {
+  return __builtin_bswap32(x);
+}
+
+static inline uint64_t CRYPTO_bswap8(uint64_t x) {
+  return __builtin_bswap64(x);
+}
+#elif defined(_MSC_VER)
+OPENSSL_MSVC_PRAGMA(warning(push, 3))
+#include <intrin.h>
+OPENSSL_MSVC_PRAGMA(warning(pop))
+#pragma intrinsic(_byteswap_uint64, _byteswap_ulong)
+static inline uint32_t CRYPTO_bswap4(uint32_t x) {
+  return _byteswap_ulong(x);
+}
+
+static inline uint64_t CRYPTO_bswap8(uint64_t x) {
+  return _byteswap_uint64(x);
+}
+#else
+static inline uint32_t CRYPTO_bswap4(uint32_t x) {
+  x = (x >> 16) | (x << 16);
+  x = ((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8);
+  return x;
+}
+
+static inline uint64_t CRYPTO_bswap8(uint64_t x) {
+  return CRYPTO_bswap4(x >> 32) | (((uint64_t)CRYPTO_bswap4(x)) << 32);
+}
+#endif
+
+
 // Language bug workarounds.
 //
 // Most C standard library functions are undefined if passed NULL, even when the
diff --git a/include/openssl/asn1.h b/include/openssl/asn1.h
index 6572995..c7ead03 100644
--- a/include/openssl/asn1.h
+++ b/include/openssl/asn1.h
@@ -737,6 +737,7 @@
 OPENSSL_EXPORT ASN1_OBJECT *ASN1_OBJECT_create(int nid, unsigned char *data,int len, const char *sn, const char *ln);
 
 OPENSSL_EXPORT int ASN1_INTEGER_set(ASN1_INTEGER *a, long v);
+OPENSSL_EXPORT int ASN1_INTEGER_set_uint64(ASN1_INTEGER *out, uint64_t v);
 OPENSSL_EXPORT long ASN1_INTEGER_get(const ASN1_INTEGER *a);
 OPENSSL_EXPORT ASN1_INTEGER *BN_to_ASN1_INTEGER(const BIGNUM *bn, ASN1_INTEGER *ai);
 OPENSSL_EXPORT BIGNUM *ASN1_INTEGER_to_BN(const ASN1_INTEGER *ai,BIGNUM *bn);