Add BN_count_low_zero_bits.

This allows a BIGNUM consumer to avoid messing around with bn->d and
bn->top/width.

Bug: 232
Change-Id: I134cf412fef24eb404ff66c84831b4591d921a17
Reviewed-on: https://boringssl-review.googlesource.com/25484
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/fipsmodule/bn/bn_test.cc b/crypto/fipsmodule/bn/bn_test.cc
index 3d4867d..8b4c488 100644
--- a/crypto/fipsmodule/bn/bn_test.cc
+++ b/crypto/fipsmodule/bn/bn_test.cc
@@ -2064,3 +2064,44 @@
   EXPECT_EQ(mont->N.width, mont2->N.width);
   EXPECT_EQ(0, BN_cmp(&mont->RR, &mont2->RR));
 }
+
+TEST_F(BNTest, CountLowZeroBits) {
+  bssl::UniquePtr<BIGNUM> ten(BN_new());
+  ASSERT_TRUE(ten);
+  ASSERT_TRUE(BN_set_word(ten.get(), 10));
+
+  bssl::UniquePtr<BIGNUM> eight(BN_new());
+  ASSERT_TRUE(eight);
+  ASSERT_TRUE(BN_set_word(eight.get(), 8));
+
+  bssl::UniquePtr<BIGNUM> two_exp_256(BN_new());
+  ASSERT_TRUE(two_exp_256);
+  ASSERT_TRUE(BN_lshift(two_exp_256.get(), BN_value_one(), 256));
+
+  bssl::UniquePtr<BIGNUM> two_exp_256_plus_4(BN_new());
+  ASSERT_TRUE(two_exp_256_plus_4);
+  ASSERT_TRUE(BN_lshift(two_exp_256_plus_4.get(), BN_value_one(), 256));
+  ASSERT_TRUE(BN_add_word(two_exp_256_plus_4.get(), 4));
+
+  bssl::UniquePtr<BIGNUM> zero(BN_new());
+  ASSERT_TRUE(zero);
+  BN_zero(zero.get());
+
+  EXPECT_EQ(1, BN_count_low_zero_bits(ten.get()));
+  EXPECT_EQ(3, BN_count_low_zero_bits(eight.get()));
+  EXPECT_EQ(256, BN_count_low_zero_bits(two_exp_256.get()));
+  EXPECT_EQ(2, BN_count_low_zero_bits(two_exp_256_plus_4.get()));
+  EXPECT_EQ(0, BN_count_low_zero_bits(zero.get()));
+
+  ASSERT_TRUE(bn_resize_words(ten.get(), 16));
+  ASSERT_TRUE(bn_resize_words(eight.get(), 16));
+  ASSERT_TRUE(bn_resize_words(two_exp_256.get(), 16));
+  ASSERT_TRUE(bn_resize_words(two_exp_256_plus_4.get(), 16));
+  ASSERT_TRUE(bn_resize_words(zero.get(), 16));
+
+  EXPECT_EQ(1, BN_count_low_zero_bits(ten.get()));
+  EXPECT_EQ(3, BN_count_low_zero_bits(eight.get()));
+  EXPECT_EQ(256, BN_count_low_zero_bits(two_exp_256.get()));
+  EXPECT_EQ(2, BN_count_low_zero_bits(two_exp_256_plus_4.get()));
+  EXPECT_EQ(0, BN_count_low_zero_bits(zero.get()));
+}
diff --git a/crypto/fipsmodule/bn/shift.c b/crypto/fipsmodule/bn/shift.c
index 0fda855..7a49028 100644
--- a/crypto/fipsmodule/bn/shift.c
+++ b/crypto/fipsmodule/bn/shift.c
@@ -304,3 +304,19 @@
   bn_set_minimal_width(a);
   return 1;
 }
+
+int BN_count_low_zero_bits(const BIGNUM *bn) {
+  for (int i = 0; i < bn->width; i++) {
+    if (bn->d[i] != 0) {
+      int bits = 0;
+      for (BN_ULONG w = bn->d[i]; (w & 1) == 0; w >>= 1) {
+        bits++;
+      }
+      return i * BN_BITS2 + bits;
+    }
+  }
+
+  // We got to the end of |bn| and saw no non-zero words. |bn| is zero, so
+  // return zero.
+  return 0;
+}
diff --git a/include/openssl/bn.h b/include/openssl/bn.h
index 495e338..871ba33 100644
--- a/include/openssl/bn.h
+++ b/include/openssl/bn.h
@@ -459,6 +459,7 @@
 // BN_is_pow2 returns 1 if |a| is a power of two, and 0 otherwise.
 OPENSSL_EXPORT int BN_is_pow2(const BIGNUM *a);
 
+
 // Bitwise operations.
 
 // BN_lshift sets |r| equal to |a| << n. The |a| and |r| arguments may be the
@@ -495,6 +496,11 @@
 // on success or zero if |n| is greater than the length of |a| already.
 OPENSSL_EXPORT int BN_mask_bits(BIGNUM *a, int n);
 
+// BN_count_low_zero_bits returns the number of low-order zero bits in |bn|, or
+// the number of factors of two which divide it. It returns zero if |bn| is
+// zero.
+OPENSSL_EXPORT int BN_count_low_zero_bits(const BIGNUM *bn);
+
 
 // Modulo arithmetic.