Do RSA sqrt(2) business in BIGNUM.

This is actually a bit more complicated (the mismatching widths cases
will never actually happen in RSA), but it's easier to think about and
removes more width-sensitive logic.

Bug: 232
Change-Id: I85fe6e706be1f7d14ffaf587958e930f47f85b3c
Reviewed-on: https://boringssl-review.googlesource.com/25246
Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
David Benjamin
2018-02-02 18:32:32 +00:00
committed by Adam Langley
parent 43cf27e7d7
commit dc8b1abb75
4 changed files with 128 additions and 57 deletions
+47 -30
View File
@@ -1890,44 +1890,61 @@ TEST_F(BNTest, NonMinimal) {
bssl::UniquePtr<BIGNUM> ten(BN_new());
ASSERT_TRUE(ten);
ASSERT_TRUE(BN_set_word(ten.get(), 10));
bssl::UniquePtr<BIGNUM> ten_copy(BN_dup(ten.get()));
ASSERT_TRUE(ten_copy);
bssl::UniquePtr<BIGNUM> eight(BN_new());
ASSERT_TRUE(eight);
ASSERT_TRUE(BN_set_word(eight.get(), 8));
// Check some comparison functions on |ten|.
EXPECT_TRUE(BN_abs_is_word(ten.get(), 10));
EXPECT_TRUE(BN_is_word(ten.get(), 10));
EXPECT_EQ(10u, BN_get_word(ten.get()));
uint64_t v;
ASSERT_TRUE(BN_get_u64(ten.get(), &v));
EXPECT_EQ(10u, v);
EXPECT_TRUE(BN_equal_consttime(ten.get(), ten_copy.get()));
EXPECT_EQ(BN_cmp(ten.get(), ten_copy.get()), 0);
EXPECT_FALSE(BN_equal_consttime(ten.get(), eight.get()));
EXPECT_LT(BN_cmp(eight.get(), ten.get()), 0);
EXPECT_EQ(4u, BN_num_bits(ten.get()));
EXPECT_EQ(1u, BN_num_bytes(ten.get()));
EXPECT_FALSE(BN_is_pow2(ten.get()));
bssl::UniquePtr<BIGNUM> forty_two(BN_new());
ASSERT_TRUE(forty_two);
ASSERT_TRUE(BN_set_word(forty_two.get(), 42));
// Make a wider version of |ten|.
EXPECT_TRUE(bn_resize_words(ten.get(), 4));
EXPECT_EQ(4, ten->top);
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));
// The same properties hold.
EXPECT_TRUE(BN_abs_is_word(ten.get(), 10));
EXPECT_TRUE(BN_is_word(ten.get(), 10));
EXPECT_EQ(10u, BN_get_word(ten.get()));
ASSERT_TRUE(BN_get_u64(ten.get(), &v));
EXPECT_EQ(10u, v);
EXPECT_TRUE(BN_equal_consttime(ten.get(), ten_copy.get()));
EXPECT_EQ(BN_cmp(ten.get(), ten_copy.get()), 0);
EXPECT_FALSE(BN_equal_consttime(ten.get(), eight.get()));
EXPECT_LT(BN_cmp(eight.get(), ten.get()), 0);
EXPECT_EQ(4u, BN_num_bits(ten.get()));
EXPECT_EQ(1u, BN_num_bytes(ten.get()));
EXPECT_FALSE(BN_is_pow2(ten.get()));
// Check some comparison functions on |ten| before and after expanding.
for (size_t width = 1; width < 10; width++) {
SCOPED_TRACE(width);
// Make a wider version of |ten|.
EXPECT_TRUE(bn_resize_words(ten.get(), width));
EXPECT_EQ(static_cast<int>(width), ten->top);
EXPECT_TRUE(BN_abs_is_word(ten.get(), 10));
EXPECT_TRUE(BN_is_word(ten.get(), 10));
EXPECT_EQ(10u, BN_get_word(ten.get()));
uint64_t v;
ASSERT_TRUE(BN_get_u64(ten.get(), &v));
EXPECT_EQ(10u, v);
EXPECT_TRUE(BN_equal_consttime(ten.get(), ten_copy.get()));
EXPECT_TRUE(BN_equal_consttime(ten_copy.get(), ten.get()));
EXPECT_FALSE(BN_less_than_consttime(ten.get(), ten_copy.get()));
EXPECT_FALSE(BN_less_than_consttime(ten_copy.get(), ten.get()));
EXPECT_EQ(BN_cmp(ten.get(), ten_copy.get()), 0);
EXPECT_FALSE(BN_equal_consttime(ten.get(), eight.get()));
EXPECT_FALSE(BN_less_than_consttime(ten.get(), eight.get()));
EXPECT_TRUE(BN_less_than_consttime(eight.get(), ten.get()));
EXPECT_LT(BN_cmp(eight.get(), ten.get()), 0);
EXPECT_FALSE(BN_equal_consttime(ten.get(), forty_two.get()));
EXPECT_TRUE(BN_less_than_consttime(ten.get(), forty_two.get()));
EXPECT_FALSE(BN_less_than_consttime(forty_two.get(), ten.get()));
EXPECT_GT(BN_cmp(forty_two.get(), ten.get()), 0);
EXPECT_FALSE(BN_equal_consttime(ten.get(), two_exp_256.get()));
EXPECT_TRUE(BN_less_than_consttime(ten.get(), two_exp_256.get()));
EXPECT_FALSE(BN_less_than_consttime(two_exp_256.get(), ten.get()));
EXPECT_GT(BN_cmp(two_exp_256.get(), ten.get()), 0);
EXPECT_EQ(4u, BN_num_bits(ten.get()));
EXPECT_EQ(1u, BN_num_bytes(ten.get()));
EXPECT_FALSE(BN_is_pow2(ten.get()));
}
// |ten| may be resized back down to one word.
EXPECT_TRUE(bn_resize_words(ten.get(), 1));
+42 -3
View File
@@ -177,19 +177,41 @@ int bn_cmp_part_words(const BN_ULONG *a, const BN_ULONG *b, int cl, int dl) {
return bn_cmp_words(a, b, cl);
}
int bn_less_than_words(const BN_ULONG *a, const BN_ULONG *b, size_t len) {
static int bn_less_than_words_impl(const BN_ULONG *a, size_t a_len,
const BN_ULONG *b, size_t b_len) {
OPENSSL_COMPILE_ASSERT(sizeof(BN_ULONG) <= sizeof(crypto_word_t),
crypto_word_t_too_small);
int ret = 0;
// Process the words in little-endian order.
for (size_t i = 0; i < len; i++) {
// Process the common words in little-endian order.
size_t min = a_len < b_len ? a_len : b_len;
for (size_t i = 0; i < min; i++) {
crypto_word_t eq = constant_time_eq_w(a[i], b[i]);
crypto_word_t lt = constant_time_lt_w(a[i], b[i]);
ret = constant_time_select_int(eq, ret, constant_time_select_int(lt, 1, 0));
}
// If |a| or |b| has non-zero words beyond |min|, they take precedence.
if (a_len < b_len) {
crypto_word_t mask = 0;
for (size_t i = a_len; i < b_len; i++) {
mask |= b[i];
}
ret = constant_time_select_int(constant_time_is_zero_w(mask), ret, 1);
} else if (b_len < a_len) {
crypto_word_t mask = 0;
for (size_t i = b_len; i < a_len; i++) {
mask |= a[i];
}
ret = constant_time_select_int(constant_time_is_zero_w(mask), ret, 0);
}
return ret;
}
int bn_less_than_words(const BN_ULONG *a, const BN_ULONG *b, size_t len) {
return bn_less_than_words_impl(a, len, b, len);
}
int BN_abs_is_word(const BIGNUM *bn, BN_ULONG w) {
switch (bn_minimal_width(bn)) {
case 1:
@@ -261,3 +283,20 @@ int BN_equal_consttime(const BIGNUM *a, const BIGNUM *b) {
mask |= (a->neg ^ b->neg);
return mask == 0;
}
int BN_less_than_consttime(const BIGNUM *a, const BIGNUM *b) {
// We do not attempt to process the sign bit in constant time. Negative
// |BIGNUM|s should never occur in crypto, only calculators.
if (a->neg && !b->neg) {
return 1;
}
if (b->neg && !a->neg) {
return 0;
}
if (a->neg && b->neg) {
const BIGNUM *tmp = a;
a = b;
b = tmp;
}
return bn_less_than_words_impl(a->d, a->top, b->d, b->top);
}
+34 -24
View File
@@ -838,7 +838,8 @@ int rsa_greater_than_pow2(const BIGNUM *b, int n) {
// relatively prime to |e|. If |p| is non-NULL, |out| will also not be close to
// |p|.
static int generate_prime(BIGNUM *out, int bits, const BIGNUM *e,
const BIGNUM *p, BN_CTX *ctx, BN_GENCB *cb) {
const BIGNUM *p, const BIGNUM *sqrt2, BN_CTX *ctx,
BN_GENCB *cb) {
if (bits < 128 || (bits % BN_BITS2) != 0) {
OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
return 0;
@@ -882,30 +883,14 @@ static int generate_prime(BIGNUM *out, int bits, const BIGNUM *e,
}
}
// If out < 2^(bits-1)×√2, try again (steps 4.4 and 5.5).
//
// We check the most significant words, so we retry if ⌊out/2^k⌋ <= ⌊b/2^k⌋,
// where b = 2^(bits-1)×√2 and k = max(0, bits - 1536). For key sizes up to
// 3072 (bits = 1536), k = 0, so we are testing that ⌊out⌋ <= ⌊b⌋. out is an
// integer and b is not, so this is equivalent to out < b. That is, the
// comparison is exact for FIPS key sizes.
// If out < 2^(bits-1)×√2, try again (steps 4.4 and 5.5). This is equivalent
// to out <= ⌊2^(bits-1)×√2⌋, or out <= sqrt2 for FIPS key sizes.
//
// For larger keys, the comparison is approximate, leaning towards
// retrying. That is, we reject a negligible fraction of primes that are
// within the FIPS bound, but we will never accept a prime outside the
// bound, ensuring the resulting RSA key is the right size. Specifically, if
// the FIPS bound holds, we have ⌊out/2^k⌋ < out/2^k < b/2^k. This implies
// ⌊out/2^k⌋ <= ⌊b/2^k⌋. That is, the FIPS bound implies our bound and so we
// are slightly tighter.
size_t out_len = (size_t)out->top;
assert(out_len == (size_t)bits / BN_BITS2);
size_t to_check = kBoringSSLRSASqrtTwoLen;
if (to_check > out_len) {
to_check = out_len;
}
if (!bn_less_than_words(
kBoringSSLRSASqrtTwo + kBoringSSLRSASqrtTwoLen - to_check,
out->d + out_len - to_check, to_check)) {
// bound, ensuring the resulting RSA key is the right size.
if (!BN_less_than_consttime(sqrt2, out)) {
continue;
}
@@ -969,7 +954,9 @@ int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e_value, BN_GENCB *cb) {
BIGNUM *pm1 = BN_CTX_get(ctx);
BIGNUM *qm1 = BN_CTX_get(ctx);
BIGNUM *gcd = BN_CTX_get(ctx);
if (totient == NULL || pm1 == NULL || qm1 == NULL || gcd == NULL) {
BIGNUM *sqrt2 = BN_CTX_get(ctx);
if (totient == NULL || pm1 == NULL || qm1 == NULL || gcd == NULL ||
sqrt2 == NULL) {
goto bn_err;
}
@@ -990,12 +977,35 @@ int RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *e_value, BN_GENCB *cb) {
}
int prime_bits = bits / 2;
// Compute sqrt2 >= ⌊2^(prime_bits-1)×√2⌋.
if (!bn_set_words(sqrt2, kBoringSSLRSASqrtTwo, kBoringSSLRSASqrtTwoLen)) {
goto bn_err;
}
int sqrt2_bits = kBoringSSLRSASqrtTwoLen * BN_BITS2;
assert(sqrt2_bits == (int)BN_num_bits(sqrt2));
if (sqrt2_bits > prime_bits) {
// For key sizes up to 3072 (prime_bits = 1536), this is exactly
// ⌊2^(prime_bits-1)×√2⌋.
if (!BN_rshift(sqrt2, sqrt2, sqrt2_bits - prime_bits)) {
goto bn_err;
}
} else if (prime_bits > sqrt2_bits) {
// For key sizes beyond 3072, this is approximate. We err towards retrying
// to ensure our key is the right size and round up.
if (!BN_add_word(sqrt2, 1) ||
!BN_lshift(sqrt2, sqrt2, prime_bits - sqrt2_bits)) {
goto bn_err;
}
}
assert(prime_bits == (int)BN_num_bits(sqrt2));
do {
// Generate p and q, each of size |prime_bits|, using the steps outlined in
// appendix FIPS 186-4 appendix B.3.3.
if (!generate_prime(rsa->p, prime_bits, rsa->e, NULL, ctx, cb) ||
if (!generate_prime(rsa->p, prime_bits, rsa->e, NULL, sqrt2, ctx, cb) ||
!BN_GENCB_call(cb, 3, 0) ||
!generate_prime(rsa->q, prime_bits, rsa->e, rsa->p, ctx, cb) ||
!generate_prime(rsa->q, prime_bits, rsa->e, rsa->p, sqrt2, ctx, cb) ||
!BN_GENCB_call(cb, 3, 1)) {
goto bn_err;
}
+5
View File
@@ -440,6 +440,11 @@ OPENSSL_EXPORT int BN_ucmp(const BIGNUM *a, const BIGNUM *b);
// independent of the contents (including the signs) of |a| and |b|.
OPENSSL_EXPORT int BN_equal_consttime(const BIGNUM *a, const BIGNUM *b);
// BN_less_than_consttime returns one if |a| is less than |b|, and zero
// otherwise. It takes an amount of time dependent on the sizes and signs of |a|
// and |b|, but independent of the contents of |a| and |b|.
OPENSSL_EXPORT int BN_less_than_consttime(const BIGNUM *a, const BIGNUM *b);
// BN_abs_is_word returns one if the absolute value of |bn| equals |w| and zero
// otherwise.
OPENSSL_EXPORT int BN_abs_is_word(const BIGNUM *bn, BN_ULONG w);