mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-25 01:02:24 +00:00
d711cc849c
The old system I removed in commit 79d3c1783b
had both linear and
exponential phase types, but the new one only had exponential, because
at that point I'd just thrown away all the clients of the linear phase
type. But I'm going to add another one shortly, so I have to put it
back in.
153 lines
4.5 KiB
C
153 lines
4.5 KiB
C
/*
|
|
* Prime generation.
|
|
*/
|
|
|
|
#include <assert.h>
|
|
#include <math.h>
|
|
|
|
#include "ssh.h"
|
|
#include "mpint.h"
|
|
#include "mpunsafe.h"
|
|
#include "sshkeygen.h"
|
|
|
|
/* ----------------------------------------------------------------------
|
|
* Standard probabilistic prime-generation algorithm:
|
|
*
|
|
* - 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.
|
|
*/
|
|
|
|
static PrimeGenerationContext *probprime_new_context(
|
|
const PrimeGenerationPolicy *policy)
|
|
{
|
|
PrimeGenerationContext *ctx = snew(PrimeGenerationContext);
|
|
ctx->vt = policy;
|
|
return ctx;
|
|
}
|
|
|
|
static void probprime_free_context(PrimeGenerationContext *ctx)
|
|
{
|
|
sfree(ctx);
|
|
}
|
|
|
|
static ProgressPhase probprime_add_progress_phase(
|
|
const PrimeGenerationPolicy *policy,
|
|
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);
|
|
}
|
|
|
|
static mp_int *probprime_generate(
|
|
PrimeGenerationContext *ctx,
|
|
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);
|
|
}
|
|
}
|
|
|
|
const PrimeGenerationPolicy primegen_probabilistic = {
|
|
probprime_add_progress_phase,
|
|
probprime_new_context,
|
|
probprime_free_context,
|
|
probprime_generate,
|
|
};
|
|
|
|
/* ----------------------------------------------------------------------
|
|
* Reusable null implementation of the progress-reporting API.
|
|
*/
|
|
|
|
static inline ProgressPhase null_progress_add(void) {
|
|
ProgressPhase ph = { .n = 0 };
|
|
return ph;
|
|
}
|
|
ProgressPhase null_progress_add_linear(
|
|
ProgressReceiver *prog, double c) { return null_progress_add(); }
|
|
ProgressPhase null_progress_add_probabilistic(
|
|
ProgressReceiver *prog, double c, double p) { return null_progress_add(); }
|
|
void null_progress_ready(ProgressReceiver *prog) {}
|
|
void null_progress_start_phase(ProgressReceiver *prog, ProgressPhase phase) {}
|
|
void null_progress_report(ProgressReceiver *prog, double progress) {}
|
|
void null_progress_report_attempt(ProgressReceiver *prog) {}
|
|
void null_progress_report_phase_complete(ProgressReceiver *prog) {}
|
|
const ProgressReceiverVtable null_progress_vt = {
|
|
null_progress_add_linear,
|
|
null_progress_add_probabilistic,
|
|
null_progress_ready,
|
|
null_progress_start_phase,
|
|
null_progress_report,
|
|
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);
|
|
}
|