Add BN_get_u64 so that Android doesn't have to reach into the BIGNUM structs

BUG=97

Change-Id: I4799cc99511e73af44def1d4daa36a8b4699f62d
Reviewed-on: https://boringssl-review.googlesource.com/12904
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/bn/bn_test.cc b/crypto/bn/bn_test.cc
index dfca2bc..89d9fed 100644
--- a/crypto/bn/bn_test.cc
+++ b/crypto/bn/bn_test.cc
@@ -1495,7 +1495,7 @@
   return true;
 }
 
-static bool TestBNSetU64() {
+static bool TestBNSetGetU64() {
   static const struct {
     const char *hex;
     uint64_t value;
@@ -1517,6 +1517,36 @@
       ERR_print_errors_fp(stderr);
       return false;
     }
+
+    uint64_t tmp;
+    if (!BN_get_u64(bn.get(), &tmp) || tmp != test.value) {
+      fprintf(stderr, "BN_get_u64 test failed for 0x%s.\n", test.hex);
+      return false;
+    }
+
+    BN_set_negative(bn.get(), 1);
+    if (!BN_get_u64(bn.get(), &tmp) || tmp != test.value) {
+      fprintf(stderr, "BN_get_u64 test failed for -0x%s.\n", test.hex);
+      return false;
+    }
+  }
+
+  // Test that BN_get_u64 fails on large numbers.
+  bssl::UniquePtr<BIGNUM> bn(BN_new());
+  if (!BN_lshift(bn.get(), BN_value_one(), 64)) {
+    return false;
+  }
+
+  uint64_t tmp;
+  if (BN_get_u64(bn.get(), &tmp)) {
+    fprintf(stderr, "BN_get_u64 of 2^64 unexpectedly succeeded.\n");
+    return false;
+  }
+
+  BN_set_negative(bn.get(), 1);
+  if (BN_get_u64(bn.get(), &tmp)) {
+    fprintf(stderr, "BN_get_u64 of -2^64 unexpectedly succeeded.\n");
+    return false;
   }
 
   return true;
@@ -1548,7 +1578,7 @@
       !TestSmallPrime(ctx.get()) ||
       !TestCmpWord() ||
       !TestBN2Dec() ||
-      !TestBNSetU64()) {
+      !TestBNSetGetU64()) {
     return 1;
   }
 
diff --git a/crypto/bn/convert.c b/crypto/bn/convert.c
index f03de9e..c44e7a8 100644
--- a/crypto/bn/convert.c
+++ b/crypto/bn/convert.c
@@ -515,6 +515,24 @@
   }
 }
 
+int BN_get_u64(const BIGNUM *bn, uint64_t *out) {
+  switch (bn->top) {
+    case 0:
+      *out = 0;
+      return 1;
+    case 1:
+      *out = bn->d[0];
+      return 1;
+#if defined(OPENSSL_32_BIT)
+    case 2:
+      *out = (uint64_t) bn->d[0] | (((uint64_t) bn->d[1]) << 32);
+      return 1;
+#endif
+    default:
+      return 0;
+  }
+}
+
 size_t BN_bn2mpi(const BIGNUM *in, uint8_t *out) {
   const size_t bits = BN_num_bits(in);
   const size_t bytes = (bits + 7) / 8;
diff --git a/include/openssl/bn.h b/include/openssl/bn.h
index c98e0bd..d3b6644 100644
--- a/include/openssl/bn.h
+++ b/include/openssl/bn.h
@@ -306,6 +306,11 @@
  * will be returned. */
 OPENSSL_EXPORT BN_ULONG BN_get_word(const BIGNUM *bn);
 
+/* BN_get_u64 sets |*out| to the absolute value of |bn| as a |uint64_t| and
+ * returns one. If |bn| is too large to be represented as a |uint64_t|, it
+ * returns zero. */
+OPENSSL_EXPORT int BN_get_u64(const BIGNUM *bn, uint64_t *out);
+
 
 /* ASN.1 functions. */