summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authortb <>2023-05-10 12:21:55 +0000
committertb <>2023-05-10 12:21:55 +0000
commitb06ec6236f52401a06b0546ab08856db818aee02 (patch)
tree19201b080903229104292ae2352a17e8ddb4af50 /src/lib
parent11d060ebfebf1118b35368fbf7d74f0777c8086e (diff)
downloadopenbsd-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.c143
-rw-r--r--src/lib/libcrypto/bn/bn_local.h4
-rw-r--r--src/lib/libcrypto/bn/bn_prime.c16
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
307static int 320static int
308bn_miller_rabin_base_2(int *is_prime, const BIGNUM *n, BN_CTX *ctx) 321bn_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
383static int
384bn_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
394int 487int
395bn_is_prime_bpsw(int *is_prime, const BIGNUM *n, BN_CTX *in_ctx) 488bn_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);
324int bn_isqrt(BIGNUM *out_sqrt, int *out_perfect, const BIGNUM *n, BN_CTX *ctx); 324int bn_isqrt(BIGNUM *out_sqrt, int *out_perfect, const BIGNUM *n, BN_CTX *ctx);
325int bn_is_perfect_square(int *out_perfect, const BIGNUM *n, BN_CTX *ctx); 325int bn_is_perfect_square(int *out_perfect, const BIGNUM *n, BN_CTX *ctx);
326 326
327int bn_is_prime_bpsw(int *is_prime, const BIGNUM *n, BN_CTX *in_ctx); 327int 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;