Split BN_prime_checks into two constants for generation and validation.

Although (somewhat) documented in prose, it is not obvious from the name
that BN_prime_checks only works for randomly-selected candidate primes.
Split into BN_prime_checks_for_generation and
BN_prime_checks_for_validation. Fix internal call sites. Notably,
DH_check now uses more iterations.

Consistently call the parameter 'checks' rather than 'iterations', to
match BN_prime_checks.

This is in preparation for importing the Wycheproof primality testing
vectors, some of which include Miller-Rabin worst case values.
(Realistically the blinding mechanism meant, even for those inputs, our
false positive rate was at most ~2^-64 anyway, but best to keep the use
cases clear.)

Update-Note: DH_check may be slower after this change.
Change-Id: Ic13d03d8631e74bf2958979ee5ef45a69e603f46
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/39195
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
David Benjamin 2019-12-15 16:25:44 -05:00 committed by CQ bot account: commit-bot@chromium.org
parent 9511ca4c03
commit 0df6edff4f
6 changed files with 77 additions and 56 deletions

View File

@ -151,7 +151,7 @@ int DH_check(const DH *dh, int *out_flags) {
*out_flags |= DH_CHECK_NOT_SUITABLE_GENERATOR;
}
}
r = BN_is_prime_ex(dh->q, BN_prime_checks, ctx, NULL);
r = BN_is_prime_ex(dh->q, BN_prime_checks_for_validation, ctx, NULL);
if (r < 0) {
goto err;
}
@ -188,7 +188,7 @@ int DH_check(const DH *dh, int *out_flags) {
*out_flags |= DH_CHECK_UNABLE_TO_CHECK_GENERATOR;
}
r = BN_is_prime_ex(dh->p, BN_prime_checks, ctx, NULL);
r = BN_is_prime_ex(dh->p, BN_prime_checks_for_validation, ctx, NULL);
if (r < 0) {
goto err;
}
@ -198,7 +198,7 @@ int DH_check(const DH *dh, int *out_flags) {
if (!BN_rshift1(t1, dh->p)) {
goto err;
}
r = BN_is_prime_ex(t1, BN_prime_checks, ctx, NULL);
r = BN_is_prime_ex(t1, BN_prime_checks_for_validation, ctx, NULL);
if (r < 0) {
goto err;
}

View File

@ -1986,16 +1986,17 @@ TEST_F(BNTest, PrimeChecking) {
ASSERT_TRUE(BN_set_word(p.get(), i));
ASSERT_TRUE(BN_primality_test(
&is_probably_prime_1, p.get(), BN_prime_checks, ctx(),
&is_probably_prime_1, p.get(), BN_prime_checks_for_generation, ctx(),
false /* do_trial_division */, nullptr /* callback */));
EXPECT_EQ(is_prime ? 1 : 0, is_probably_prime_1);
ASSERT_TRUE(BN_primality_test(
&is_probably_prime_2, p.get(), BN_prime_checks, ctx(),
&is_probably_prime_2, p.get(), BN_prime_checks_for_generation, ctx(),
true /* do_trial_division */, nullptr /* callback */));
EXPECT_EQ(is_prime ? 1 : 0, is_probably_prime_2);
if (i > 3 && i % 2 == 1) {
ASSERT_TRUE(BN_enhanced_miller_rabin_primality_test(
&result_3, p.get(), BN_prime_checks, ctx(), nullptr /* callback */));
&result_3, p.get(), BN_prime_checks_for_generation, ctx(),
nullptr /* callback */));
EXPECT_EQ(is_prime, result_3 == bn_probably_prime);
}
}
@ -2003,13 +2004,13 @@ TEST_F(BNTest, PrimeChecking) {
// Negative numbers are not prime.
ASSERT_TRUE(BN_set_word(p.get(), 7));
BN_set_negative(p.get(), 1);
ASSERT_TRUE(BN_primality_test(&is_probably_prime_1, p.get(), BN_prime_checks,
ctx(), false /* do_trial_division */,
nullptr /* callback */));
ASSERT_TRUE(BN_primality_test(
&is_probably_prime_1, p.get(), BN_prime_checks_for_generation, ctx(),
false /* do_trial_division */, nullptr /* callback */));
EXPECT_EQ(0, is_probably_prime_1);
ASSERT_TRUE(BN_primality_test(&is_probably_prime_2, p.get(), BN_prime_checks,
ctx(), true /* do_trial_division */,
nullptr /* callback */));
ASSERT_TRUE(BN_primality_test(
&is_probably_prime_2, p.get(), BN_prime_checks_for_generation, ctx(),
true /* do_trial_division */, nullptr /* callback */));
EXPECT_EQ(0, is_probably_prime_2);
static const char *kComposites[] = {
@ -2068,17 +2069,18 @@ TEST_F(BNTest, PrimeChecking) {
EXPECT_NE(0, DecimalToBIGNUM(&p, str));
ASSERT_TRUE(BN_primality_test(
&is_probably_prime_1, p.get(), BN_prime_checks, ctx(),
&is_probably_prime_1, p.get(), BN_prime_checks_for_generation, ctx(),
false /* do_trial_division */, nullptr /* callback */));
EXPECT_EQ(0, is_probably_prime_1);
ASSERT_TRUE(BN_primality_test(
&is_probably_prime_2, p.get(), BN_prime_checks, ctx(),
&is_probably_prime_2, p.get(), BN_prime_checks_for_generation, ctx(),
true /* do_trial_division */, nullptr /* callback */));
EXPECT_EQ(0, is_probably_prime_2);
ASSERT_TRUE(BN_enhanced_miller_rabin_primality_test(
&result_3, p.get(), BN_prime_checks, ctx(), nullptr /* callback */));
&result_3, p.get(), BN_prime_checks_for_generation, ctx(),
nullptr /* callback */));
EXPECT_EQ(bn_composite, result_3);
}
@ -2257,25 +2259,27 @@ TEST_F(BNTest, PrimeChecking) {
EXPECT_NE(0, HexToBIGNUM(&p, str));
ASSERT_TRUE(BN_primality_test(
&is_probably_prime_1, p.get(), BN_prime_checks, ctx(),
&is_probably_prime_1, p.get(), BN_prime_checks_for_generation, ctx(),
false /* do_trial_division */, nullptr /* callback */));
EXPECT_EQ(1, is_probably_prime_1);
ASSERT_TRUE(BN_primality_test(
&is_probably_prime_2, p.get(), BN_prime_checks, ctx(),
&is_probably_prime_2, p.get(), BN_prime_checks_for_generation, ctx(),
true /* do_trial_division */, nullptr /* callback */));
EXPECT_EQ(1, is_probably_prime_2);
ASSERT_TRUE(BN_enhanced_miller_rabin_primality_test(
&result_3, p.get(), BN_prime_checks, ctx(), nullptr /* callback */));
&result_3, p.get(), BN_prime_checks_for_generation, ctx(),
nullptr /* callback */));
EXPECT_EQ(bn_probably_prime, result_3);
}
// BN_primality_test works with null |BN_CTX|.
ASSERT_TRUE(BN_set_word(p.get(), 5));
ASSERT_TRUE(BN_primality_test(
&is_probably_prime_1, p.get(), BN_prime_checks, nullptr /* ctx */,
false /* do_trial_division */, nullptr /* callback */));
ASSERT_TRUE(
BN_primality_test(&is_probably_prime_1, p.get(),
BN_prime_checks_for_generation, nullptr /* ctx */,
false /* do_trial_division */, nullptr /* callback */));
EXPECT_EQ(1, is_probably_prime_1);
}

View File

@ -210,7 +210,7 @@ static const uint16_t kPrimes[] = {
};
// BN_prime_checks_for_size returns the number of Miller-Rabin iterations
// necessary for a 'bits'-bit prime.
// necessary for generating a 'bits'-bit candidate prime.
//
//
// This table is generated using the algorithm of FIPS PUB 186-4
@ -604,9 +604,8 @@ err:
return ret;
}
int BN_primality_test(int *out_is_probably_prime, const BIGNUM *w,
int iterations, BN_CTX *ctx, int do_trial_division,
BN_GENCB *cb) {
int BN_primality_test(int *out_is_probably_prime, const BIGNUM *w, int checks,
BN_CTX *ctx, int do_trial_division, BN_GENCB *cb) {
// This function's secrecy and performance requirements come from RSA key
// generation. We generate RSA keys by selecting two large, secret primes with
// rejection sampling.
@ -679,8 +678,8 @@ int BN_primality_test(int *out_is_probably_prime, const BIGNUM *w,
}
}
if (iterations == BN_prime_checks) {
iterations = BN_prime_checks_for_size(BN_num_bits(w));
if (checks == BN_prime_checks_for_generation) {
checks = BN_prime_checks_for_size(BN_num_bits(w));
}
BN_CTX *new_ctx = NULL;
@ -736,7 +735,7 @@ int BN_primality_test(int *out_is_probably_prime, const BIGNUM *w,
// Using |constant_time_lt_w| seems to prevent the compiler from optimizing
// this into two jumps.
for (int i = 1; (i <= BN_PRIME_CHECKS_BLINDED) |
constant_time_lt_w(uniform_iterations, iterations);
constant_time_lt_w(uniform_iterations, checks);
i++) {
// Step 4.1-4.2
int is_uniform;
@ -765,7 +764,7 @@ int BN_primality_test(int *out_is_probably_prime, const BIGNUM *w,
}
}
assert(uniform_iterations >= (crypto_word_t)iterations);
assert(uniform_iterations >= (crypto_word_t)checks);
*out_is_probably_prime = 1;
ret = 1;
@ -792,7 +791,7 @@ int BN_is_prime_fasttest_ex(const BIGNUM *a, int checks, BN_CTX *ctx,
}
int BN_enhanced_miller_rabin_primality_test(
enum bn_primality_result_t *out_result, const BIGNUM *w, int iterations,
enum bn_primality_result_t *out_result, const BIGNUM *w, int checks,
BN_CTX *ctx, BN_GENCB *cb) {
// Enhanced Miller-Rabin is only valid on odd integers greater than 3.
if (!BN_is_odd(w) || BN_cmp_word(w, 3) <= 0) {
@ -800,8 +799,8 @@ int BN_enhanced_miller_rabin_primality_test(
return 0;
}
if (iterations == BN_prime_checks) {
iterations = BN_prime_checks_for_size(BN_num_bits(w));
if (checks == BN_prime_checks_for_generation) {
checks = BN_prime_checks_for_size(BN_num_bits(w));
}
int ret = 0;
@ -848,7 +847,7 @@ int BN_enhanced_miller_rabin_primality_test(
// The following loop performs in inner iteration of the Enhanced Miller-Rabin
// Primality test (Step 4).
for (int i = 1; i <= iterations; i++) {
for (int i = 1; i <= checks; i++) {
// Step 4.1-4.2
if (!BN_rand_range_ex(b, 2, w1)) {
goto err;

View File

@ -809,6 +809,11 @@ int RSA_check_fips(RSA *key) {
int ret = 1;
// Perform partial public key validation of RSA keys (SP 800-89 5.3.3).
// Although this is not for primality testing, SP 800-89 cites an RSA
// primality testing algorithm, so we use |BN_prime_checks_for_generation| to
// match. This is only a plausibility test and we expect the value to be
// composite, so too few iterations will cause us to reject the key, not use
// an implausible one.
enum bn_primality_result_t primality_result;
if (BN_num_bits(key->e) <= 16 ||
BN_num_bits(key->e) > 256 ||
@ -817,7 +822,8 @@ int RSA_check_fips(RSA *key) {
!BN_gcd(&small_gcd, key->n, g_small_factors(), ctx) ||
!BN_is_one(&small_gcd) ||
!BN_enhanced_miller_rabin_primality_test(&primality_result, key->n,
BN_prime_checks, ctx, NULL) ||
BN_prime_checks_for_generation,
ctx, NULL) ||
primality_result != bn_non_prime_power_composite) {
OPENSSL_PUT_ERROR(RSA, RSA_R_PUBLIC_KEY_VALIDATION_FAILED);
ret = 0;

View File

@ -1046,8 +1046,8 @@ static int generate_prime(BIGNUM *out, int bits, const BIGNUM *e,
if (relatively_prime) {
// Test |out| for primality (steps 4.5.1 and 5.6.1).
int is_probable_prime;
if (!BN_primality_test(&is_probable_prime, out, BN_prime_checks, ctx, 0,
cb)) {
if (!BN_primality_test(&is_probable_prime, out,
BN_prime_checks_for_generation, ctx, 0, cb)) {
goto err;
}
if (is_probable_prime) {

View File

@ -684,10 +684,22 @@ OPENSSL_EXPORT int BN_generate_prime_ex(BIGNUM *ret, int bits, int safe,
const BIGNUM *add, const BIGNUM *rem,
BN_GENCB *cb);
// BN_prime_checks is magic value that can be used as the |checks| argument to
// the primality testing functions in order to automatically select a number of
// Miller-Rabin checks that gives a false positive rate of ~2^{-80}.
#define BN_prime_checks 0
// BN_prime_checks_for_validation can be used as the |checks| argument to the
// primarily testing functions when validating an externally-supplied candidate
// prime. It gives a false positive rate of at most 2^{-128}. (The worst case
// false positive rate for a single iteration is 1/4, so we perform 32
// iterations.)
#define BN_prime_checks_for_validation 32
// BN_prime_checks_for_generation can be used as the |checks| argument to the
// primality testing functions when generating random primes. It gives a false
// positive rate at most the security level of the corresponding RSA key size.
//
// Note this value only performs enough checks if the candidate prime was
// selected randomly. If validating an externally-supplied candidate, especially
// one that may be selected adversarially, use |BN_prime_checks_for_validation|
// instead.
#define BN_prime_checks_for_generation 0
// bn_primality_result_t enumerates the outcomes of primality-testing.
enum bn_primality_result_t {
@ -698,7 +710,7 @@ enum bn_primality_result_t {
// BN_enhanced_miller_rabin_primality_test tests whether |w| is probably a prime
// number using the Enhanced Miller-Rabin Test (FIPS 186-4 C.3.2) with
// |iterations| iterations and returns the result in |out_result|. Enhanced
// |checks| iterations and returns the result in |out_result|. Enhanced
// Miller-Rabin tests primality for odd integers greater than 3, returning
// |bn_probably_prime| if the number is probably prime,
// |bn_non_prime_power_composite| if the number is a composite that is not the
@ -706,12 +718,10 @@ enum bn_primality_result_t {
// success and zero on failure. If |cb| is not NULL, then it is called during
// each iteration of the primality test.
//
// If |iterations| is |BN_prime_checks|, then a value that results in a false
// positive rate lower than the number-field sieve security level of |w| is
// used, provided |w| was generated randomly. |BN_prime_checks| is not suitable
// for inputs potentially crafted by an adversary.
// See |BN_prime_checks_for_validation| and |BN_prime_checks_for_generation| for
// recommended values of |checks|.
OPENSSL_EXPORT int BN_enhanced_miller_rabin_primality_test(
enum bn_primality_result_t *out_result, const BIGNUM *w, int iterations,
enum bn_primality_result_t *out_result, const BIGNUM *w, int checks,
BN_CTX *ctx, BN_GENCB *cb);
// BN_primality_test sets |*is_probably_prime| to one if |candidate| is
@ -720,11 +730,9 @@ OPENSSL_EXPORT int BN_enhanced_miller_rabin_primality_test(
//
// If |do_trial_division| is non-zero then |candidate| will be tested against a
// list of small primes before Miller-Rabin tests. The probability of this
// function returning a false positive is 2^{2*checks}. If |checks| is
// |BN_prime_checks| then a value that results in a false positive rate lower
// than the number-field sieve security level of |candidate| is used, provided
// |candidate| was generated randomly. |BN_prime_checks| is not suitable for
// inputs potentially crafted by an adversary.
// function returning a false positive is at most 2^{2*checks}. See
// |BN_prime_checks_for_validation| and |BN_prime_checks_for_generation| for
// recommended values of |checks|.
//
// If |cb| is not NULL then it is called during the checking process. See the
// comment above |BN_GENCB|.
@ -740,11 +748,9 @@ OPENSSL_EXPORT int BN_primality_test(int *is_probably_prime,
//
// If |do_trial_division| is non-zero then |candidate| will be tested against a
// list of small primes before Miller-Rabin tests. The probability of this
// function returning one when |candidate| is composite is 2^{2*checks}. If
// |checks| is |BN_prime_checks| then a value that results in a false positive
// rate lower than the number-field sieve security level of |candidate| is used,
// provided |candidate| was generated randomly. |BN_prime_checks| is not
// suitable for inputs potentially crafted by an adversary.
// function returning one when |candidate| is composite is at most 2^{2*checks}.
// See |BN_prime_checks_for_validation| and |BN_prime_checks_for_generation| for
// recommended values of |checks|.
//
// If |cb| is not NULL then it is called during the checking process. See the
// comment above |BN_GENCB|.
@ -939,6 +945,12 @@ OPENSSL_EXPORT int BN_MONT_CTX_set(BN_MONT_CTX *mont, const BIGNUM *mod,
// Use |BN_bn2bin_padded| instead. It is |size_t|-clean.
OPENSSL_EXPORT int BN_bn2binpad(const BIGNUM *in, uint8_t *out, int len);
// BN_prime_checks is a deprecated alias for |BN_prime_checks_for_validation|.
// Use |BN_prime_checks_for_generation| or |BN_prime_checks_for_validation|
// instead. (This defaults to the |_for_validation| value in order to be
// conservative.)
#define BN_prime_checks BN_prime_checks_for_validation
// Private functions