Add BN_set_u64.

Android currently implements this manually (see NativeBN_putULongInt) by
reaching into BIGNUM's internals. BN_ULONG is a somewhat unfortunate API
anyway as the size is platform-dependent, so add a platform-independent
way to do this.

The other things Android needs are going to need more work, but this
one's easy.

BUG=97

Change-Id: I4af4dc29f9845bdce0f0663c379b4b5d3e1dc46e
Reviewed-on: https://boringssl-review.googlesource.com/11088
Commit-Queue: David Benjamin <davidben@google.com>
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/bn/bn.c b/crypto/bn/bn.c
index 496266f..87d81d2 100644
--- a/crypto/bn/bn.c
+++ b/crypto/bn/bn.c
@@ -266,6 +266,28 @@
   return 1;
 }
 
+int BN_set_u64(BIGNUM *bn, uint64_t value) {
+#if BN_BITS2 == 64
+  return BN_set_word(bn, value);
+#elif BN_BITS2 == 32
+  if (value <= BN_MASK2) {
+    return BN_set_word(bn, (BN_ULONG)value);
+  }
+
+  if (bn_wexpand(bn, 2) == NULL) {
+    return 0;
+  }
+
+  bn->neg = 0;
+  bn->d[0] = (BN_ULONG)value;
+  bn->d[1] = (BN_ULONG)(value >> 32);
+  bn->top = 2;
+  return 1;
+#else
+#error "BN_BITS2 must be 32 or 64."
+#endif
+}
+
 int bn_set_words(BIGNUM *bn, const BN_ULONG *words, size_t num) {
   if (bn_wexpand(bn, num) == NULL) {
     return 0;
diff --git a/crypto/bn/bn_test.cc b/crypto/bn/bn_test.cc
index 0867dec..67a5a31 100644
--- a/crypto/bn/bn_test.cc
+++ b/crypto/bn/bn_test.cc
@@ -1437,6 +1437,33 @@
   return true;
 }
 
+static bool TestBNSetU64() {
+  static const struct {
+    const char *hex;
+    uint64_t value;
+  } kU64Tests[] = {
+      {"0", UINT64_C(0x0)},
+      {"1", UINT64_C(0x1)},
+      {"ffffffff", UINT64_C(0xffffffff)},
+      {"100000000", UINT64_C(0x100000000)},
+      {"ffffffffffffffff", UINT64_C(0xffffffffffffffff)},
+  };
+
+  for (const auto& test : kU64Tests) {
+    bssl::UniquePtr<BIGNUM> bn(BN_new()), expected;
+    if (!bn ||
+        !BN_set_u64(bn.get(), test.value) ||
+        !HexToBIGNUM(&expected, test.hex) ||
+        BN_cmp(bn.get(), expected.get()) != 0) {
+      fprintf(stderr, "BN_set_u64 test failed for 0x%s.\n", test.hex);
+      ERR_print_errors_fp(stderr);
+      return false;
+    }
+  }
+
+  return true;
+}
+
 int main(int argc, char *argv[]) {
   CRYPTO_library_init();
 
@@ -1462,7 +1489,8 @@
       !TestExpModZero() ||
       !TestSmallPrime(ctx.get()) ||
       !TestCmpWord() ||
-      !TestBN2Dec()) {
+      !TestBN2Dec() ||
+      !TestBNSetU64()) {
     return 1;
   }
 
diff --git a/include/openssl/bn.h b/include/openssl/bn.h
index 554a81b..66945cb 100644
--- a/include/openssl/bn.h
+++ b/include/openssl/bn.h
@@ -221,6 +221,10 @@
  * allocation failure. */
 OPENSSL_EXPORT int BN_set_word(BIGNUM *bn, BN_ULONG value);
 
+/* BN_set_u64 sets |bn| to |value|. It returns one on success or zero on
+ * allocation failure. */
+OPENSSL_EXPORT int BN_set_u64(BIGNUM *bn, uint64_t value);
+
 /* BN_set_negative sets the sign of |bn|. */
 OPENSSL_EXPORT void BN_set_negative(BIGNUM *bn, int sign);