diff options
author | tb <> | 2023-05-10 12:21:55 +0000 |
---|---|---|
committer | tb <> | 2023-05-10 12:21:55 +0000 |
commit | b06ec6236f52401a06b0546ab08856db818aee02 (patch) | |
tree | 19201b080903229104292ae2352a17e8ddb4af50 /src/lib | |
parent | 11d060ebfebf1118b35368fbf7d74f0777c8086e (diff) | |
download | openbsd-b06ec6236f52401a06b0546ab08856db818aee02.tar.gz openbsd-b06ec6236f52401a06b0546ab08856db818aee02.tar.bz2 openbsd-b06ec6236f52401a06b0546ab08856db818aee02.zip |
Add Miller-Rabin test for random bases to BPSW
The behavior of the BPSW primality test for numbers > 2^64 is not very
well understood. While there is no known composite that passes the test,
there are heuristics that indicate that there are likely infinitely many.
Therefore it seems appropriate to harden the test. Having a settable
number of MR rounds before doing a version of BPSW is also the approach
taken by Go's primality check in math/big.
This adds a new implementation of the old MR test that runs before running
the strong Lucas test. I like to imagine that it's slightly cleaner code.
We're effectively at about twice the cost of what we had a year ago. In
addition, it adds some non-determinism in case there actually are false
positives for the BPSW test.
The implementation is straightforward. It could easily be tweaked to use
the additional gcds in the "enhanced" MR test of FIPS 186-5, but as long
as we are only going to throw away the additional info, that's not worth
much.
This is a first step towards incorporating some of the considerations in
"A performant misuse-resistant API for Primality Testing" by Massimo and
Paterson. Further work will happen in tree. In particular, there are plans
to crank the number of Miller-Rabin tests considerably so as to have a
guaranteed baseline. The manual will be updated shortly.
positive feedback beck
ok jsing
Diffstat (limited to 'src/lib')
-rw-r--r-- | src/lib/libcrypto/bn/bn_bpsw.c | 143 | ||||
-rw-r--r-- | src/lib/libcrypto/bn/bn_local.h | 4 | ||||
-rw-r--r-- | src/lib/libcrypto/bn/bn_prime.c | 16 |
3 files changed, 130 insertions, 33 deletions
diff --git a/src/lib/libcrypto/bn/bn_bpsw.c b/src/lib/libcrypto/bn/bn_bpsw.c index 9220339f19..a1acbbf1e9 100644 --- a/src/lib/libcrypto/bn/bn_bpsw.c +++ b/src/lib/libcrypto/bn/bn_bpsw.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: bn_bpsw.c,v 1.8 2022/11/26 16:08:51 tb Exp $ */ | 1 | /* $OpenBSD: bn_bpsw.c,v 1.9 2023/05/10 12:21:55 tb Exp $ */ |
2 | /* | 2 | /* |
3 | * Copyright (c) 2022 Martin Grenouilloux <martin.grenouilloux@lse.epita.fr> | 3 | * Copyright (c) 2022 Martin Grenouilloux <martin.grenouilloux@lse.epita.fr> |
4 | * Copyright (c) 2022 Theo Buehler <tb@openbsd.org> | 4 | * Copyright (c) 2022 Theo Buehler <tb@openbsd.org> |
@@ -301,23 +301,103 @@ bn_strong_lucas_selfridge(int *is_prime, const BIGNUM *n, BN_CTX *ctx) | |||
301 | } | 301 | } |
302 | 302 | ||
303 | /* | 303 | /* |
304 | * Miller-Rabin primality test for base 2. | 304 | * Fermat criterion in Miller-Rabin test. |
305 | * | ||
306 | * Check whether 1 < base < n - 1 witnesses that n is composite. For prime n: | ||
307 | * | ||
308 | * * Fermat's little theorem: base^(n-1) = 1 (mod n). | ||
309 | * * The only square roots of 1 (mod n) are 1 and -1. | ||
310 | * | ||
311 | * Calculate base^((n-1)/2) by writing n - 1 = k * 2^s with odd k. Iteratively | ||
312 | * compute power = (base^k)^(2^(s-1)) by successive squaring of base^k. | ||
313 | * | ||
314 | * If power ever reaches -1, base^(n-1) is equal to 1 and n is a pseudoprime | ||
315 | * for base. If power reaches 1 before -1 during successive squaring, we have | ||
316 | * an unexpected square root of 1 and n is composite. Otherwise base^(n-1) != 1, | ||
317 | * and n is composite. | ||
305 | */ | 318 | */ |
306 | 319 | ||
307 | static int | 320 | static int |
308 | bn_miller_rabin_base_2(int *is_prime, const BIGNUM *n, BN_CTX *ctx) | 321 | bn_fermat(int *is_prime, const BIGNUM *n, const BIGNUM *n_minus_one, |
322 | const BIGNUM *k, int s, const BIGNUM *base, BN_CTX *ctx, BN_MONT_CTX *mctx) | ||
309 | { | 323 | { |
310 | BIGNUM *n_minus_one, *k, *x; | 324 | BIGNUM *power; |
311 | int i, s; | ||
312 | int ret = 0; | 325 | int ret = 0; |
326 | int i; | ||
313 | 327 | ||
314 | BN_CTX_start(ctx); | 328 | BN_CTX_start(ctx); |
315 | 329 | ||
316 | if ((n_minus_one = BN_CTX_get(ctx)) == NULL) | 330 | if ((power = BN_CTX_get(ctx)) == NULL) |
331 | goto err; | ||
332 | |||
333 | /* Sanity check: ensure that 1 < base < n - 1. */ | ||
334 | if (BN_cmp(base, BN_value_one()) <= 0 || BN_cmp(base, n_minus_one) >= 0) | ||
335 | goto err; | ||
336 | |||
337 | if (!BN_mod_exp_mont_ct(power, base, k, n, ctx, mctx)) | ||
338 | goto err; | ||
339 | |||
340 | if (BN_is_one(power) || BN_cmp(power, n_minus_one) == 0) { | ||
341 | *is_prime = 1; | ||
342 | goto done; | ||
343 | } | ||
344 | |||
345 | /* Loop invariant: power is neither 1 nor -1 (mod n). */ | ||
346 | for (i = 1; i < s; i++) { | ||
347 | if (!BN_mod_sqr(power, power, n, ctx)) | ||
348 | goto err; | ||
349 | |||
350 | /* n is a pseudoprime for base. */ | ||
351 | if (BN_cmp(power, n_minus_one) == 0) { | ||
352 | *is_prime = 1; | ||
353 | goto done; | ||
354 | } | ||
355 | |||
356 | /* n is composite: there's a square root of unity != 1 or -1. */ | ||
357 | if (BN_is_one(power)) { | ||
358 | *is_prime = 0; | ||
359 | goto done; | ||
360 | } | ||
361 | } | ||
362 | |||
363 | /* | ||
364 | * If we get here, n is definitely composite: base^(n-1) != 1. | ||
365 | */ | ||
366 | |||
367 | *is_prime = 0; | ||
368 | |||
369 | done: | ||
370 | ret = 1; | ||
371 | |||
372 | err: | ||
373 | BN_CTX_end(ctx); | ||
374 | |||
375 | return ret; | ||
376 | } | ||
377 | |||
378 | /* | ||
379 | * Miller-Rabin primality test for base 2 and for |rounds| of random bases. | ||
380 | * On success: is_prime == 0 implies n is composite - the converse is false. | ||
381 | */ | ||
382 | |||
383 | static int | ||
384 | bn_miller_rabin(int *is_prime, const BIGNUM *n, BN_CTX *ctx, size_t rounds) | ||
385 | { | ||
386 | BN_MONT_CTX *mctx = NULL; | ||
387 | BIGNUM *base, *k, *n_minus_one, *three; | ||
388 | size_t i; | ||
389 | int s; | ||
390 | int ret = 0; | ||
391 | |||
392 | BN_CTX_start(ctx); | ||
393 | |||
394 | if ((base = BN_CTX_get(ctx)) == NULL) | ||
317 | goto err; | 395 | goto err; |
318 | if ((k = BN_CTX_get(ctx)) == NULL) | 396 | if ((k = BN_CTX_get(ctx)) == NULL) |
319 | goto err; | 397 | goto err; |
320 | if ((x = BN_CTX_get(ctx)) == NULL) | 398 | if ((n_minus_one = BN_CTX_get(ctx)) == NULL) |
399 | goto err; | ||
400 | if ((three = BN_CTX_get(ctx)) == NULL) | ||
321 | goto err; | 401 | goto err; |
322 | 402 | ||
323 | if (BN_is_word(n, 2) || BN_is_word(n, 3)) { | 403 | if (BN_is_word(n, 2) || BN_is_word(n, 3)) { |
@@ -344,43 +424,56 @@ bn_miller_rabin_base_2(int *is_prime, const BIGNUM *n, BN_CTX *ctx) | |||
344 | goto err; | 424 | goto err; |
345 | 425 | ||
346 | /* | 426 | /* |
347 | * If 2^k is 1 or -1 (mod n) then n is a 2-pseudoprime. | 427 | * Montgomery setup for n. |
348 | */ | 428 | */ |
349 | 429 | ||
350 | if (!BN_set_word(x, 2)) | 430 | if ((mctx = BN_MONT_CTX_new()) == NULL) |
351 | goto err; | 431 | goto err; |
352 | if (!BN_mod_exp_ct(x, x, k, n, ctx)) | 432 | |
433 | if (!BN_MONT_CTX_set(mctx, n, ctx)) | ||
353 | goto err; | 434 | goto err; |
354 | 435 | ||
355 | if (BN_is_one(x) || BN_cmp(x, n_minus_one) == 0) { | 436 | /* |
356 | *is_prime = 1; | 437 | * Perform a Miller-Rabin test for base 2 as required by BPSW. |
438 | */ | ||
439 | |||
440 | if (!BN_set_word(base, 2)) | ||
441 | goto err; | ||
442 | |||
443 | if (!bn_fermat(is_prime, n, n_minus_one, k, s, base, ctx, mctx)) | ||
444 | goto err; | ||
445 | if (!*is_prime) | ||
357 | goto done; | 446 | goto done; |
358 | } | ||
359 | 447 | ||
360 | /* | 448 | /* |
361 | * If 2^{2^i k} == -1 (mod n) for some 1 <= i < s, then n is a | 449 | * Perform Miller-Rabin tests with random 3 <= base < n - 1 to reduce |
362 | * 2-pseudoprime. | 450 | * risk of false positives in BPSW. |
363 | */ | 451 | */ |
364 | 452 | ||
365 | for (i = 1; i < s; i++) { | 453 | if (!BN_set_word(three, 3)) |
366 | if (!BN_mod_sqr(x, x, n, ctx)) | 454 | goto err; |
455 | |||
456 | for (i = 0; i < rounds; i++) { | ||
457 | if (!bn_rand_interval(base, three, n_minus_one)) | ||
367 | goto err; | 458 | goto err; |
368 | if (BN_cmp(x, n_minus_one) == 0) { | 459 | |
369 | *is_prime = 1; | 460 | if (!bn_fermat(is_prime, n, n_minus_one, k, s, base, ctx, mctx)) |
461 | goto err; | ||
462 | if (!*is_prime) | ||
370 | goto done; | 463 | goto done; |
371 | } | ||
372 | } | 464 | } |
373 | 465 | ||
374 | /* | 466 | /* |
375 | * If we got here, n is definitely composite. | 467 | * If we got here, we have a Miller-Rabin pseudoprime. |
376 | */ | 468 | */ |
377 | 469 | ||
378 | *is_prime = 0; | 470 | *is_prime = 1; |
379 | 471 | ||
380 | done: | 472 | done: |
381 | ret = 1; | 473 | ret = 1; |
382 | 474 | ||
383 | err: | 475 | err: |
476 | BN_MONT_CTX_free(mctx); | ||
384 | BN_CTX_end(ctx); | 477 | BN_CTX_end(ctx); |
385 | 478 | ||
386 | return ret; | 479 | return ret; |
@@ -392,7 +485,7 @@ bn_miller_rabin_base_2(int *is_prime, const BIGNUM *n, BN_CTX *ctx) | |||
392 | */ | 485 | */ |
393 | 486 | ||
394 | int | 487 | int |
395 | bn_is_prime_bpsw(int *is_prime, const BIGNUM *n, BN_CTX *in_ctx) | 488 | bn_is_prime_bpsw(int *is_prime, const BIGNUM *n, BN_CTX *in_ctx, size_t rounds) |
396 | { | 489 | { |
397 | BN_CTX *ctx = NULL; | 490 | BN_CTX *ctx = NULL; |
398 | BN_ULONG mod; | 491 | BN_ULONG mod; |
@@ -424,13 +517,11 @@ bn_is_prime_bpsw(int *is_prime, const BIGNUM *n, BN_CTX *in_ctx) | |||
424 | if (ctx == NULL) | 517 | if (ctx == NULL) |
425 | goto err; | 518 | goto err; |
426 | 519 | ||
427 | if (!bn_miller_rabin_base_2(is_prime, n, ctx)) | 520 | if (!bn_miller_rabin(is_prime, n, ctx, rounds)) |
428 | goto err; | 521 | goto err; |
429 | if (!*is_prime) | 522 | if (!*is_prime) |
430 | goto done; | 523 | goto done; |
431 | 524 | ||
432 | /* XXX - Miller-Rabin for random bases? See FIPS 186-4, Table C.1. */ | ||
433 | |||
434 | if (!bn_strong_lucas_selfridge(is_prime, n, ctx)) | 525 | if (!bn_strong_lucas_selfridge(is_prime, n, ctx)) |
435 | goto err; | 526 | goto err; |
436 | 527 | ||
diff --git a/src/lib/libcrypto/bn/bn_local.h b/src/lib/libcrypto/bn/bn_local.h index 24d91af462..78b4157d12 100644 --- a/src/lib/libcrypto/bn/bn_local.h +++ b/src/lib/libcrypto/bn/bn_local.h | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: bn_local.h,v 1.21 2023/04/25 17:59:41 tb Exp $ */ | 1 | /* $OpenBSD: bn_local.h,v 1.22 2023/05/10 12:21:55 tb Exp $ */ |
2 | /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) | 2 | /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) |
3 | * All rights reserved. | 3 | * All rights reserved. |
4 | * | 4 | * |
@@ -324,7 +324,7 @@ int bn_copy(BIGNUM *dst, const BIGNUM *src); | |||
324 | int bn_isqrt(BIGNUM *out_sqrt, int *out_perfect, const BIGNUM *n, BN_CTX *ctx); | 324 | int bn_isqrt(BIGNUM *out_sqrt, int *out_perfect, const BIGNUM *n, BN_CTX *ctx); |
325 | int bn_is_perfect_square(int *out_perfect, const BIGNUM *n, BN_CTX *ctx); | 325 | int bn_is_perfect_square(int *out_perfect, const BIGNUM *n, BN_CTX *ctx); |
326 | 326 | ||
327 | int bn_is_prime_bpsw(int *is_prime, const BIGNUM *n, BN_CTX *in_ctx); | 327 | int bn_is_prime_bpsw(int *is_prime, const BIGNUM *n, BN_CTX *ctx, size_t rounds); |
328 | 328 | ||
329 | __END_HIDDEN_DECLS | 329 | __END_HIDDEN_DECLS |
330 | #endif /* !HEADER_BN_LOCAL_H */ | 330 | #endif /* !HEADER_BN_LOCAL_H */ |
diff --git a/src/lib/libcrypto/bn/bn_prime.c b/src/lib/libcrypto/bn/bn_prime.c index c2fd0fc2e9..b8f0eb69ce 100644 --- a/src/lib/libcrypto/bn/bn_prime.c +++ b/src/lib/libcrypto/bn/bn_prime.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: bn_prime.c,v 1.31 2023/04/25 19:57:59 tb Exp $ */ | 1 | /* $OpenBSD: bn_prime.c,v 1.32 2023/05/10 12:21:55 tb Exp $ */ |
2 | /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) | 2 | /* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) |
3 | * All rights reserved. | 3 | * All rights reserved. |
4 | * | 4 | * |
@@ -195,12 +195,12 @@ BN_generate_prime_ex(BIGNUM *ret, int bits, int safe, const BIGNUM *add, | |||
195 | goto err; | 195 | goto err; |
196 | 196 | ||
197 | if (!safe) { | 197 | if (!safe) { |
198 | if (!bn_is_prime_bpsw(&is_prime, ret, ctx)) | 198 | if (!bn_is_prime_bpsw(&is_prime, ret, ctx, 1)) |
199 | goto err; | 199 | goto err; |
200 | if (!is_prime) | 200 | if (!is_prime) |
201 | goto loop; | 201 | goto loop; |
202 | } else { | 202 | } else { |
203 | if (!bn_is_prime_bpsw(&is_prime, ret, ctx)) | 203 | if (!bn_is_prime_bpsw(&is_prime, ret, ctx, 1)) |
204 | goto err; | 204 | goto err; |
205 | if (!is_prime) | 205 | if (!is_prime) |
206 | goto loop; | 206 | goto loop; |
@@ -213,7 +213,7 @@ BN_generate_prime_ex(BIGNUM *ret, int bits, int safe, const BIGNUM *add, | |||
213 | if (!BN_rshift1(p, ret)) | 213 | if (!BN_rshift1(p, ret)) |
214 | goto err; | 214 | goto err; |
215 | 215 | ||
216 | if (!bn_is_prime_bpsw(&is_prime, p, ctx)) | 216 | if (!bn_is_prime_bpsw(&is_prime, p, ctx, 1)) |
217 | goto err; | 217 | goto err; |
218 | if (!is_prime) | 218 | if (!is_prime) |
219 | goto loop; | 219 | goto loop; |
@@ -243,8 +243,14 @@ BN_is_prime_fasttest_ex(const BIGNUM *a, int checks, BN_CTX *ctx_passed, | |||
243 | { | 243 | { |
244 | int is_prime; | 244 | int is_prime; |
245 | 245 | ||
246 | if (checks < 0) | ||
247 | return -1; | ||
248 | |||
249 | if (checks == BN_prime_checks) | ||
250 | checks = BN_prime_checks_for_size(BN_num_bits(a)); | ||
251 | |||
246 | /* XXX - tickle BN_GENCB in bn_is_prime_bpsw(). */ | 252 | /* XXX - tickle BN_GENCB in bn_is_prime_bpsw(). */ |
247 | if (!bn_is_prime_bpsw(&is_prime, a, ctx_passed)) | 253 | if (!bn_is_prime_bpsw(&is_prime, a, ctx_passed, checks)) |
248 | return -1; | 254 | return -1; |
249 | 255 | ||
250 | return is_prime; | 256 | return is_prime; |