diff --git a/crypto/fipsmodule/bn/bn_test.cc b/crypto/fipsmodule/bn/bn_test.cc index 3d4867de..8b4c4888 100644 --- a/crypto/fipsmodule/bn/bn_test.cc +++ b/crypto/fipsmodule/bn/bn_test.cc @@ -2064,3 +2064,44 @@ TEST_F(BNTest, NonMinimal) { EXPECT_EQ(mont->N.width, mont2->N.width); EXPECT_EQ(0, BN_cmp(&mont->RR, &mont2->RR)); } + +TEST_F(BNTest, CountLowZeroBits) { + bssl::UniquePtr ten(BN_new()); + ASSERT_TRUE(ten); + ASSERT_TRUE(BN_set_word(ten.get(), 10)); + + bssl::UniquePtr eight(BN_new()); + ASSERT_TRUE(eight); + ASSERT_TRUE(BN_set_word(eight.get(), 8)); + + bssl::UniquePtr two_exp_256(BN_new()); + ASSERT_TRUE(two_exp_256); + ASSERT_TRUE(BN_lshift(two_exp_256.get(), BN_value_one(), 256)); + + bssl::UniquePtr 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 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 0fda8556..7a49028d 100644 --- a/crypto/fipsmodule/bn/shift.c +++ b/crypto/fipsmodule/bn/shift.c @@ -304,3 +304,19 @@ int BN_mask_bits(BIGNUM *a, int n) { 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 495e3389..871ba335 100644 --- a/include/openssl/bn.h +++ b/include/openssl/bn.h @@ -459,6 +459,7 @@ OPENSSL_EXPORT int BN_is_odd(const BIGNUM *bn); // 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 @@ OPENSSL_EXPORT int BN_is_bit_set(const BIGNUM *a, int n); // 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.