1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-25 01:02:24 +00:00

Use a Miller-Rabin test instead of a Fermat test; add comments

[originally from svn r801]
This commit is contained in:
Simon Tatham 2000-11-15 15:03:17 +00:00
parent 694fd2eedf
commit 7ac98ae071

View File

@ -4,6 +4,54 @@
#include "ssh.h"
/*
* This prime generation algorithm is pretty much cribbed from
* OpenSSL. The algorithm is:
*
* - invent a B-bit random number and ensure the top and bottom
* bits are set (so it's definitely B-bit, and it's definitely
* odd)
*
* - see if it's coprime to all primes below 2^16; increment it by
* two until it is (this shouldn't take long in general)
*
* - perform the Miller-Rabin primality test enough times to
* ensure the probability of it being composite is 2^-80 or
* less
*
* - go back to square one if any M-R test fails.
*/
/*
* The Miller-Rabin primality test is an extension to the Fermat
* test. The Fermat test just checks that a^(n-1) == 1 mod n; this
* is vulnerable to Carmichael numbers. Miller-Rabin makes use of
* the fact that if p is truly prime and a^K == 1 mod p for even K,
* then a^(K/2) must be congruent to either 1 or -1. In Hence, we
* write n-1 as q * 2^k, with odd q, and then we compute a^q, a^2q,
* a^4q, a^8q, ..., a^(n-1) mod n. If n is prime, the last of these
* must be 1, and the last one that _isn't_ 1 must be -1. So we
* expect to see either a^q congruent to 1, or a^q congruent to -1,
* or a^q to become congruent to -1 after squaring at most k-1
* times.
*
* For example, consider a=2 and n=1729 (a Carmichael number).
* 2^1728 mod 1729 is 1, so the Fermat test would have no problem
* with this. But Miller-Rabin looks at 2^(1728/2), 2^(1728/4),
* ..., 2^(1728/64) as well. Now 2^(1728/64) == 645, 2^(1728/32) ==
* 1065, 2^(1728/16) == 1. Hang on! The value before the first 1
* was 1065, and we expected 1728 (i.e. -1). Guards! Seize this
* impostor.
*
* (It doesn't work for all bases. Try a=932 and n=1729, and even
* the Miller-Rabin test can't tell the difference, because
* 932^(1728/64) is already 1 and so we don't get to see what
* happens before the first 1. But there isn't any class of numbers
* which give false positives on Miller-Rabin for _all_ bases, so
* by trying several bases we probabilistically rule out Carmichael
* numbers as well as everything else composite.)
*/
/*
* The first few odd primes.
*
@ -561,7 +609,7 @@ Bignum primegen(int bits, int modulus, int residue,
int phase, progfn_t pfn, void *pfnparam) {
int i, k, v, byte, bitsleft, check, checks;
unsigned long delta, moduli[NPRIMES+1], residues[NPRIMES+1];
Bignum p, q, wqp, wqp2;
Bignum p, pm1, q, wqp, wqp2;
int progress = 0;
byte = 0; bitsleft = 0;
@ -596,7 +644,8 @@ Bignum primegen(int bits, int modulus, int residue,
residues[i] = bignum_mod_short(p, primes[i]);
}
moduli[NPRIMES] = modulus;
residues[NPRIMES] = bignum_mod_short(p, modulus) + modulus - residue;
residues[NPRIMES] = (bignum_mod_short(p, (unsigned short)modulus)
+ modulus - residue);
delta = 0;
while (1) {
for (i = 0; i < (sizeof(moduli) / sizeof(*moduli)); i++)
@ -617,8 +666,8 @@ Bignum primegen(int bits, int modulus, int residue,
freebn(q);
/*
* Now apply the Fermat primality test a few times. First work
* out how many checks are needed.
* Now apply the Miller-Rabin primality test a few times. First
* work out how many checks are needed.
*/
checks = 27;
if (bits >= 150) checks = 18;
@ -638,6 +687,9 @@ Bignum primegen(int bits, int modulus, int residue,
*/
for (k = 0; bignum_bit(p, k) == !k; k++); /* find first 1 bit in p-1 */
q = bignum_rshift(p, k);
/* And store p-1 itself, which we'll need. */
pm1 = copybn(p);
decbn(pm1);
/*
* Now, for each check ...
@ -674,21 +726,21 @@ Bignum primegen(int bits, int modulus, int residue,
freebn(w);
/*
* See if this is 1, or if it becomes 1 when squared at
* most k times.
* See if this is 1, or if it is -1, or if it becomes -1
* when squared at most k-1 times.
*/
if (bignum_cmp(wqp, One) == 0) {
if (bignum_cmp(wqp, One) == 0 || bignum_cmp(wqp, pm1) == 0) {
freebn(wqp);
continue;
}
for (i = 0; i < k; i++) {
for (i = 0; i < k-1; i++) {
wqp2 = modmul(wqp, wqp, p);
freebn(wqp);
wqp = wqp2;
if (bignum_cmp(wqp, One) == 0)
if (bignum_cmp(wqp, pm1) == 0)
break;
}
if (i < k) {
if (i < k-1) {
freebn(wqp);
continue;
}
@ -698,6 +750,7 @@ Bignum primegen(int bits, int modulus, int residue,
* compositeness of p.
*/
freebn(p);
freebn(pm1);
freebn(q);
goto STARTOVER;
}
@ -706,5 +759,6 @@ Bignum primegen(int bits, int modulus, int residue,
* We have a prime!
*/
freebn(q);
freebn(pm1);
return p;
}