mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-09 17:38:00 +00:00
750f5222b2
This further cleans up the prime-generation code, to the point where the main primegen() function has almost nothing in it. Also now I'll be able to reuse M-R as a primitive in more sophisticated alternatives to primegen().
126 lines
3.6 KiB
C
126 lines
3.6 KiB
C
/*
|
|
* Prime generation.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
|
|
#include "ssh.h"
|
|
#include "mpint.h"
|
|
#include "mpunsafe.h"
|
|
#include "sshkeygen.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.
|
|
*/
|
|
|
|
ProgressPhase primegen_add_progress_phase(ProgressReceiver *prog,
|
|
unsigned bits)
|
|
{
|
|
/*
|
|
* The density of primes near x is 1/(log x). When x is about 2^b,
|
|
* that's 1/(b log 2).
|
|
*
|
|
* But we're only doing the expensive part of the process (the M-R
|
|
* checks) for a number that passes the initial winnowing test of
|
|
* having no factor less than 2^16 (at least, unless the prime is
|
|
* so small that PrimeCandidateSource gives up on that winnowing).
|
|
* The density of _those_ numbers is about 1/19.76. So the odds of
|
|
* hitting a prime per expensive attempt are boosted by a factor
|
|
* of 19.76.
|
|
*/
|
|
const double log_2 = 0.693147180559945309417232121458;
|
|
double winnow_factor = (bits < 32 ? 1.0 : 19.76);
|
|
double prob = winnow_factor / (bits * log_2);
|
|
|
|
/*
|
|
* Estimate the cost of prime generation as the cost of the M-R
|
|
* modexps.
|
|
*/
|
|
double cost = (miller_rabin_checks_needed(bits) *
|
|
estimate_modexp_cost(bits));
|
|
return progress_add_probabilistic(prog, cost, prob);
|
|
}
|
|
|
|
mp_int *primegen(PrimeCandidateSource *pcs, ProgressReceiver *prog)
|
|
{
|
|
pcs_ready(pcs);
|
|
|
|
while (true) {
|
|
progress_report_attempt(prog);
|
|
|
|
mp_int *p = pcs_generate(pcs);
|
|
|
|
MillerRabin *mr = miller_rabin_new(p);
|
|
bool known_bad = false;
|
|
unsigned nchecks = miller_rabin_checks_needed(mp_get_nbits(p));
|
|
for (unsigned check = 0; check < nchecks; check++) {
|
|
if (!miller_rabin_test_random(mr)) {
|
|
known_bad = true;
|
|
break;
|
|
}
|
|
}
|
|
miller_rabin_free(mr);
|
|
|
|
if (!known_bad) {
|
|
/*
|
|
* We have a prime!
|
|
*/
|
|
pcs_free(pcs);
|
|
return p;
|
|
}
|
|
|
|
mp_free(p);
|
|
}
|
|
}
|
|
|
|
|
|
/* ----------------------------------------------------------------------
|
|
* Reusable null implementation of the progress-reporting API.
|
|
*/
|
|
|
|
ProgressPhase null_progress_add_probabilistic(
|
|
ProgressReceiver *prog, double c, double p) {
|
|
ProgressPhase ph = { .n = 0 };
|
|
return ph;
|
|
}
|
|
void null_progress_ready(ProgressReceiver *prog) {}
|
|
void null_progress_start_phase(ProgressReceiver *prog, ProgressPhase phase) {}
|
|
void null_progress_report_attempt(ProgressReceiver *prog) {}
|
|
void null_progress_report_phase_complete(ProgressReceiver *prog) {}
|
|
const ProgressReceiverVtable null_progress_vt = {
|
|
null_progress_add_probabilistic,
|
|
null_progress_ready,
|
|
null_progress_start_phase,
|
|
null_progress_report_attempt,
|
|
null_progress_report_phase_complete,
|
|
};
|
|
|
|
/* ----------------------------------------------------------------------
|
|
* Helper function for progress estimation.
|
|
*/
|
|
|
|
double estimate_modexp_cost(unsigned bits)
|
|
{
|
|
/*
|
|
* A modexp of n bits goes roughly like O(n^2.58), on the grounds
|
|
* that our modmul is O(n^1.58) (Karatsuba) and you need O(n) of
|
|
* them in a modexp.
|
|
*/
|
|
return pow(bits, 2.58);
|
|
}
|