mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-10 01:48:00 +00:00
a7bdefb394
The old API was one of those horrible things I used to do when I was young and foolish, in which you have just one function, and indicate which of lots of things it's doing by passing in flags. It was crying out to be replaced with a vtable. While I'm at it, I've reworked the code on the Windows side that decides what to do with the progress bar, so that it's based on actually justifiable estimates of probability rather than magic integer constants. Since computers are generally faster now than they were at the start of this project, I've also decided there's no longer any point in making the fixed final part of RSA key generation bother to report progress at all. So the progress bars are now only for the variable part, i.e. the actual prime generations.
127 lines
3.6 KiB
C
127 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);
|
|
|
|
STARTOVER:
|
|
|
|
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) {
|
|
mp_free(p);
|
|
goto STARTOVER;
|
|
}
|
|
|
|
/*
|
|
* We have a prime!
|
|
*/
|
|
pcs_free(pcs);
|
|
return 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);
|
|
}
|