1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-08 08:58:00 +00:00

New vtable API for keygen progress reporting.

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.

(This is a reapplication of commit a7bdefb39, without the Miller-Rabin
refactoring accidentally folded into it. Also this time I've added -lm
to the link options, which for some reason _didn't_ cause me a link
failure last time round. No idea why not.)
This commit is contained in:
Simon Tatham 2020-02-23 15:29:40 +00:00
parent 62733a8389
commit 79d3c1783b
12 changed files with 334 additions and 251 deletions

View File

@ -13,36 +13,35 @@
#include "putty.h"
#include "ssh.h"
#include "sshkeygen.h"
#include "mpint.h"
struct progress {
int phase, current;
FILE *progress_fp = NULL;
static void cmdgen_progress_report_attempt(ProgressReceiver *prog)
{
if (progress_fp) {
fputc('+', progress_fp);
fflush(progress_fp);
}
}
static void cmdgen_progress_report_phase_complete(ProgressReceiver *prog)
{
if (progress_fp) {
fputc('\n', progress_fp);
fflush(progress_fp);
}
}
static const ProgressReceiverVtable cmdgen_progress_vt = {
null_progress_add_probabilistic,
null_progress_ready,
null_progress_start_phase,
cmdgen_progress_report_attempt,
cmdgen_progress_report_phase_complete,
};
static void progress_update(void *param, int action, int phase, int iprogress)
{
struct progress *p = (struct progress *)param;
if (action != PROGFN_PROGRESS)
return;
if (phase > p->phase) {
if (p->phase >= 0)
fputc('\n', stderr);
p->phase = phase;
if (iprogress >= 0)
p->current = iprogress - 1;
else
p->current = iprogress;
}
while (p->current < iprogress) {
fputc('+', stdout);
p->current++;
}
fflush(stdout);
}
static void no_progress(void *param, int action, int phase, int iprogress)
{
}
static ProgressReceiver cmdgen_progress = { .vt = &cmdgen_progress_vt };
/*
* Stubs to let everything else link sensibly.
@ -184,10 +183,12 @@ int main(int argc, char **argv)
char *ssh2alg = NULL;
char *old_passphrase = NULL, *new_passphrase = NULL;
bool load_encrypted;
progfn_t progressfn = is_interactive() ? progress_update : no_progress;
const char *random_device = NULL;
int exit_status = 0;
if (is_interactive())
progress_fp = stderr;
#define RETURN(status) do { exit_status = (status); goto out; } while (0)
/* ------------------------------------------------------------------
@ -333,7 +334,7 @@ int main(int argc, char **argv)
outtype = PUBLIC;
break;
case 'q':
progressfn = no_progress;
progress_fp = NULL;
break;
}
break;
@ -645,10 +646,6 @@ int main(int argc, char **argv)
char *entropy;
char default_comment[80];
struct tm tm;
struct progress prog;
prog.phase = -1;
prog.current = -1;
tm = ltime();
if (keytype == DSA)
@ -673,25 +670,25 @@ int main(int argc, char **argv)
if (keytype == DSA) {
struct dss_key *dsskey = snew(struct dss_key);
dsa_generate(dsskey, bits, progressfn, &prog);
dsa_generate(dsskey, bits, &cmdgen_progress);
ssh2key = snew(ssh2_userkey);
ssh2key->key = &dsskey->sshk;
ssh1key = NULL;
} else if (keytype == ECDSA) {
struct ecdsa_key *ek = snew(struct ecdsa_key);
ecdsa_generate(ek, bits, progressfn, &prog);
ecdsa_generate(ek, bits);
ssh2key = snew(ssh2_userkey);
ssh2key->key = &ek->sshk;
ssh1key = NULL;
} else if (keytype == ED25519) {
struct eddsa_key *ek = snew(struct eddsa_key);
eddsa_generate(ek, bits, progressfn, &prog);
eddsa_generate(ek, bits);
ssh2key = snew(ssh2_userkey);
ssh2key->key = &ek->sshk;
ssh1key = NULL;
} else {
RSAKey *rsakey = snew(RSAKey);
rsa_generate(rsakey, bits, progressfn, &prog);
rsa_generate(rsakey, bits, &cmdgen_progress);
rsakey->comment = NULL;
if (keytype == RSA1) {
ssh1key = rsakey;
@ -700,7 +697,6 @@ int main(int argc, char **argv)
ssh2key->key = &rsakey->sshk;
}
}
progressfn(&prog, PROGFN_PROGRESS, INT_MAX, -1);
if (ssh2key)
ssh2key->comment = dupstr(default_comment);

View File

@ -204,6 +204,8 @@ else
AC_SUBST(WARNINGOPTS, [])
fi
AC_SEARCH_LIBS([pow], [m])
AC_OUTPUT
if test "$gtk_version_desired" = "no"; then cat <<EOF

20
ssh.h
View File

@ -1313,26 +1313,6 @@ void openssh_bcrypt(const char *passphrase,
const unsigned char *salt, int saltbytes,
int rounds, unsigned char *out, int outbytes);
/*
* For progress updates in the key generation utility.
*/
#define PROGFN_INITIALISE 1
#define PROGFN_LIN_PHASE 2
#define PROGFN_EXP_PHASE 3
#define PROGFN_PHASE_EXTENT 4
#define PROGFN_READY 5
#define PROGFN_PROGRESS 6
typedef void (*progfn_t) (void *param, int action, int phase, int progress);
int rsa_generate(RSAKey *key, int bits, progfn_t pfn,
void *pfnparam);
int dsa_generate(struct dss_key *key, int bits, progfn_t pfn,
void *pfnparam);
int ecdsa_generate(struct ecdsa_key *key, int bits, progfn_t pfn,
void *pfnparam);
int eddsa_generate(struct eddsa_key *key, int bits, progfn_t pfn,
void *pfnparam);
/*
* Connection-sharing API provided by platforms. This function must
* either:

View File

@ -70,8 +70,6 @@ static const struct PacketProtocolLayerVtable ssh1_login_server_vtable = {
NULL /* no layer names in SSH-1 */,
};
static void no_progress(void *param, int action, int phase, int iprogress) {}
PacketProtocolLayer *ssh1_login_server_new(
PacketProtocolLayer *successor_layer, RSAKey *hostkey,
AuthPolicy *authpolicy, const SshServerConfig *ssc)
@ -142,7 +140,9 @@ static void ssh1_login_server_process_queue(PacketProtocolLayer *ppl)
if (server_key_bits < 512)
server_key_bits = s->hostkey->bytes + 256;
s->servkey = snew(RSAKey);
rsa_generate(s->servkey, server_key_bits, no_progress, NULL);
ProgressReceiver null_progress;
null_progress.vt = &null_progress_vt;
rsa_generate(s->servkey, server_key_bits, &null_progress);
s->servkey->comment = NULL;
s->servkey_generated_here = true;
}

View File

@ -36,10 +36,6 @@ static strbuf *finalise_and_sign_exhash(struct ssh2_transport_state *s)
return sb;
}
static void no_progress(void *param, int action, int phase, int iprogress)
{
}
void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
{
PacketProtocolLayer *ppl = &s->ppl; /* for ppl_logevent */
@ -101,7 +97,9 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
* group! It's good enough for testing a client against,
* but not for serious use.
*/
s->p = primegen(pcs_new(s->pbits), 1, no_progress, NULL);
ProgressReceiver null_progress;
null_progress.vt = &null_progress_vt;
s->p = primegen(pcs_new(s->pbits), &null_progress);
s->g = mp_from_integer(2);
s->dh_ctx = dh_setup_gex(s->p, s->g);
s->kex_init_value = SSH2_MSG_KEX_DH_GEX_INIT;
@ -263,7 +261,9 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted)
ppl_logevent("Generating a %d-bit RSA key", extra->minklen);
s->rsa_kex_key = snew(RSAKey);
rsa_generate(s->rsa_kex_key, extra->minklen, no_progress, NULL);
ProgressReceiver null_progress;
null_progress.vt = &null_progress_vt;
rsa_generate(s->rsa_kex_key, extra->minklen, &null_progress);
s->rsa_kex_key->comment = NULL;
s->rsa_kex_key_needs_freeing = true;
}

View File

@ -7,71 +7,50 @@
#include "sshkeygen.h"
#include "mpint.h"
int dsa_generate(struct dss_key *key, int bits, progfn_t pfn,
void *pfnparam)
int dsa_generate(struct dss_key *key, int bits, ProgressReceiver *prog)
{
/*
* Set up the phase limits for the progress report. We do this
* by passing minus the phase number.
* Progress-reporting setup.
*
* For prime generation: our initial filter finds things
* coprime to everything below 2^16. Computing the product of
* (p-1)/p for all prime p below 2^16 gives about 20.33; so
* among B-bit integers, one in every 20.33 will get through
* the initial filter to be a candidate prime.
* DSA generation involves three potentially long jobs: inventing
* the small prime q, the large prime p, and finding an order-q
* element of the multiplicative group of p.
*
* Meanwhile, we are searching for primes in the region of 2^B;
* since pi(x) ~ x/log(x), when x is in the region of 2^B, the
* prime density will be d/dx pi(x) ~ 1/log(B), i.e. about
* 1/0.6931B. So the chance of any given candidate being prime
* is 20.33/0.6931B, which is roughly 29.34 divided by B.
* The latter is done by finding an element whose order is
* _divisible_ by q and raising it to the power of (p-1)/q. Every
* element whose order is not divisible by q is a qth power of q
* distinct elements whose order _is_ divisible by q, so the
* probability of not finding a suitable element on the first try
* is in the region of 1/q, i.e. at most 2^-159.
*
* So now we have this probability P, we're looking at an
* exponential distribution with parameter P: we will manage in
* one attempt with probability P, in two with probability
* P(1-P), in three with probability P(1-P)^2, etc. The
* probability that we have still not managed to find a prime
* after N attempts is (1-P)^N.
*
* We therefore inform the progress indicator of the number B
* (29.34/B), so that it knows how much to increment by each
* time. We do this in 16-bit fixed point, so 29.34 becomes
* 0x1D.57C4.
* (So the probability of success will end up indistinguishable
* from 1 in IEEE standard floating point! But what can you do.)
*/
pfn(pfnparam, PROGFN_PHASE_EXTENT, 1, 0x2800);
pfn(pfnparam, PROGFN_EXP_PHASE, 1, -0x1D57C4 / 160);
pfn(pfnparam, PROGFN_PHASE_EXTENT, 2, 0x40 * bits);
pfn(pfnparam, PROGFN_EXP_PHASE, 2, -0x1D57C4 / bits);
/*
* In phase three we are finding an order-q element of the
* multiplicative group of p, by finding an element whose order
* is _divisible_ by q and raising it to the power of (p-1)/q.
* _Most_ elements will have order divisible by q, since for a
* start phi(p) of them will be primitive roots. So
* realistically we don't need to set this much below 1 (64K).
* Still, we'll set it to 1/2 (32K) to be on the safe side.
*/
pfn(pfnparam, PROGFN_PHASE_EXTENT, 3, 0x2000);
pfn(pfnparam, PROGFN_EXP_PHASE, 3, -32768);
pfn(pfnparam, PROGFN_READY, 0, 0);
ProgressPhase phase_q = primegen_add_progress_phase(prog, 160);
ProgressPhase phase_p = primegen_add_progress_phase(prog, bits);
ProgressPhase phase_g = progress_add_probabilistic(
prog, estimate_modexp_cost(bits), 1.0 - 0x1.0p-159);
progress_ready(prog);
PrimeCandidateSource *pcs;
/*
* Generate q: a prime of length 160.
*/
progress_start_phase(prog, phase_q);
pcs = pcs_new(160);
mp_int *q = primegen(pcs, 1, pfn, pfnparam);
mp_int *q = primegen(pcs, prog);
progress_report_phase_complete(prog);
/*
* Now generate p: a prime of length `bits', such that p-1 is
* divisible by q.
*/
progress_start_phase(prog, phase_p);
pcs = pcs_new(bits);
pcs_require_residue_1(pcs, q);
mp_int *p = primegen(pcs, 2, pfn, pfnparam);
mp_int *p = primegen(pcs, prog);
progress_report_phase_complete(prog);
/*
* Next we need g. Raise 2 to the power (p-1)/q modulo p, and
@ -79,12 +58,12 @@ int dsa_generate(struct dss_key *key, int bits, progfn_t pfn,
* soon as we hit a non-unit (and non-zero!) one, that'll do
* for g.
*/
progress_start_phase(prog, phase_g);
mp_int *power = mp_div(p, q); /* this is floor(p/q) == (p-1)/q */
mp_int *h = mp_from_integer(1);
int progress = 0;
mp_int *g;
while (1) {
pfn(pfnparam, PROGFN_PROGRESS, 3, ++progress);
progress_report_attempt(prog);
g = mp_modpow(h, power, p);
if (mp_hs_integer(g, 2))
break; /* got one */
@ -93,6 +72,7 @@ int dsa_generate(struct dss_key *key, int bits, progfn_t pfn,
}
mp_free(h);
mp_free(power);
progress_report_phase_complete(prog);
/*
* Now we're nearly done. All we need now is our private key x,

View File

@ -3,10 +3,10 @@
*/
#include "ssh.h"
#include "sshkeygen.h"
#include "mpint.h"
int ecdsa_generate(struct ecdsa_key *ek, int bits,
progfn_t pfn, void *pfnparam)
int ecdsa_generate(struct ecdsa_key *ek, int bits)
{
if (!ec_nist_alg_and_curve_by_bits(bits, &ek->curve, &ek->sshk.vt))
return 0;
@ -20,8 +20,7 @@ int ecdsa_generate(struct ecdsa_key *ek, int bits,
return 1;
}
int eddsa_generate(struct eddsa_key *ek, int bits,
progfn_t pfn, void *pfnparam)
int eddsa_generate(struct eddsa_key *ek, int bits)
{
if (!ec_ed_alg_and_curve_by_bits(bits, &ek->curve, &ek->sshk.vt))
return 0;

View File

@ -67,10 +67,74 @@ void pcs_inspect(PrimeCandidateSource *pcs, mp_int **limit_out,
/* Query functions for primegen to use */
unsigned pcs_get_bits(PrimeCandidateSource *pcs);
/* ----------------------------------------------------------------------
* Callback API that allows key generation to report progress to its
* caller.
*/
typedef struct ProgressReceiverVtable ProgressReceiverVtable;
typedef struct ProgressReceiver ProgressReceiver;
typedef union ProgressPhase ProgressPhase;
union ProgressPhase {
int n;
void *p;
};
struct ProgressReceiver {
const ProgressReceiverVtable *vt;
};
struct ProgressReceiverVtable {
ProgressPhase (*add_probabilistic)(ProgressReceiver *prog,
double cost_per_attempt,
double attempt_probability);
void (*ready)(ProgressReceiver *prog);
void (*start_phase)(ProgressReceiver *prog, ProgressPhase phase);
void (*report_attempt)(ProgressReceiver *prog);
void (*report_phase_complete)(ProgressReceiver *prog);
};
static inline ProgressPhase progress_add_probabilistic(ProgressReceiver *prog,
double c, double p)
{ return prog->vt->add_probabilistic(prog, c, p); }
static inline void progress_ready(ProgressReceiver *prog)
{ prog->vt->ready(prog); }
static inline void progress_start_phase(
ProgressReceiver *prog, ProgressPhase phase)
{ prog->vt->start_phase(prog, phase); }
static inline void progress_report_attempt(ProgressReceiver *prog)
{ prog->vt->report_attempt(prog); }
static inline void progress_report_phase_complete(ProgressReceiver *prog)
{ prog->vt->report_phase_complete(prog); }
ProgressPhase null_progress_add_probabilistic(
ProgressReceiver *prog, double c, double p);
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);
extern const ProgressReceiverVtable null_progress_vt;
/* A helper function for dreaming up progress cost estimates. */
double estimate_modexp_cost(unsigned bits);
/* ----------------------------------------------------------------------
* The top-level API for generating primes.
*/
/* This function consumes and frees the PrimeCandidateSource you give it */
mp_int *primegen(PrimeCandidateSource *pcs,
int phase, progfn_t pfn, void *pfnparam);
mp_int *primegen(PrimeCandidateSource *pcs, ProgressReceiver *prog);
/* Estimate how long it will take, and add a phase to a ProgressReceiver */
ProgressPhase primegen_add_progress_phase(ProgressReceiver *prog,
unsigned bits);
/* ----------------------------------------------------------------------
* The overall top-level API for generating entire key pairs.
*/
int rsa_generate(RSAKey *key, int bits, ProgressReceiver *prog);
int dsa_generate(struct dss_key *key, int bits, ProgressReceiver *prog);
int ecdsa_generate(struct ecdsa_key *key, int bits);
int eddsa_generate(struct eddsa_key *key, int bits);

View File

@ -3,6 +3,8 @@
*/
#include <assert.h>
#include <math.h>
#include "ssh.h"
#include "mpint.h"
#include "mpunsafe.h"
@ -108,16 +110,51 @@
* all, so after we've seen a -1 we can be sure of seeing nothing
* but 1s.)
*/
mp_int *primegen(PrimeCandidateSource *pcs,
int phase, progfn_t pfn, void *pfnparam)
static unsigned miller_rabin_checks_needed(unsigned bits)
{
/* Table 4.4 from Handbook of Applied Cryptography */
return (bits >= 1300 ? 2 : bits >= 850 ? 3 : bits >= 650 ? 4 :
bits >= 550 ? 5 : bits >= 450 ? 6 : bits >= 400 ? 7 :
bits >= 350 ? 8 : bits >= 300 ? 9 : bits >= 250 ? 12 :
bits >= 200 ? 15 : bits >= 150 ? 18 : 27);
}
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);
int progress = 0;
STARTOVER:
pfn(pfnparam, PROGFN_PROGRESS, phase, ++progress);
progress_report_attempt(prog);
mp_int *p = pcs_generate(pcs);
@ -125,11 +162,7 @@ mp_int *primegen(PrimeCandidateSource *pcs,
* Now apply the Miller-Rabin primality test a few times. First
* work out how many checks are needed.
*/
unsigned checks =
bits >= 1300 ? 2 : bits >= 850 ? 3 : bits >= 650 ? 4 :
bits >= 550 ? 5 : bits >= 450 ? 6 : bits >= 400 ? 7 :
bits >= 350 ? 8 : bits >= 300 ? 9 : bits >= 250 ? 12 :
bits >= 200 ? 15 : bits >= 150 ? 18 : 27;
unsigned checks = miller_rabin_checks_needed(pcs_get_bits(pcs));
/*
* Next, write p-1 as q*2^k.
@ -160,8 +193,6 @@ mp_int *primegen(PrimeCandidateSource *pcs,
mp_int *w = mp_random_in_range(two, pm1);
monty_import_into(mc, w, w);
pfn(pfnparam, PROGFN_PROGRESS, phase, ++progress);
/*
* Compute w^q mod p.
*/
@ -209,3 +240,38 @@ mp_int *primegen(PrimeCandidateSource *pcs,
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);
}

View File

@ -14,49 +14,12 @@
static void invent_firstbits(unsigned *one, unsigned *two,
unsigned min_separation);
int rsa_generate(RSAKey *key, int bits, progfn_t pfn,
void *pfnparam)
int rsa_generate(RSAKey *key, int bits, ProgressReceiver *prog)
{
unsigned pfirst, qfirst;
key->sshk.vt = &ssh_rsa;
/*
* Set up the phase limits for the progress report. We do this
* by passing minus the phase number.
*
* For prime generation: our initial filter finds things
* coprime to everything below 2^16. Computing the product of
* (p-1)/p for all prime p below 2^16 gives about 20.33; so
* among B-bit integers, one in every 20.33 will get through
* the initial filter to be a candidate prime.
*
* Meanwhile, we are searching for primes in the region of 2^B;
* since pi(x) ~ x/log(x), when x is in the region of 2^B, the
* prime density will be d/dx pi(x) ~ 1/log(B), i.e. about
* 1/0.6931B. So the chance of any given candidate being prime
* is 20.33/0.6931B, which is roughly 29.34 divided by B.
*
* So now we have this probability P, we're looking at an
* exponential distribution with parameter P: we will manage in
* one attempt with probability P, in two with probability
* P(1-P), in three with probability P(1-P)^2, etc. The
* probability that we have still not managed to find a prime
* after N attempts is (1-P)^N.
*
* We therefore inform the progress indicator of the number B
* (29.34/B), so that it knows how much to increment by each
* time. We do this in 16-bit fixed point, so 29.34 becomes
* 0x1D.57C4.
*/
pfn(pfnparam, PROGFN_PHASE_EXTENT, 1, 0x10000);
pfn(pfnparam, PROGFN_EXP_PHASE, 1, -0x1D57C4 / (bits / 2));
pfn(pfnparam, PROGFN_PHASE_EXTENT, 2, 0x10000);
pfn(pfnparam, PROGFN_EXP_PHASE, 2, -0x1D57C4 / (bits - bits / 2));
pfn(pfnparam, PROGFN_PHASE_EXTENT, 3, 0x4000);
pfn(pfnparam, PROGFN_LIN_PHASE, 3, 5);
pfn(pfnparam, PROGFN_READY, 0, 0);
/*
* We don't generate e; we just use a standard one always.
*/
@ -80,15 +43,23 @@ int rsa_generate(RSAKey *key, int bits, progfn_t pfn,
int pbits = bits - qbits;
assert(pbits >= qbits);
ProgressPhase phase_p = primegen_add_progress_phase(prog, pbits);
ProgressPhase phase_q = primegen_add_progress_phase(prog, qbits);
progress_ready(prog);
PrimeCandidateSource *pcs;
progress_start_phase(prog, phase_p);
pcs = pcs_new_with_firstbits(pbits, pfirst, NFIRSTBITS);
pcs_avoid_residue_small(pcs, RSA_EXPONENT, 1);
mp_int *p = primegen(pcs, 1, pfn, pfnparam);
mp_int *p = primegen(pcs, prog);
progress_report_phase_complete(prog);
progress_start_phase(prog, phase_q);
pcs = pcs_new_with_firstbits(qbits, qfirst, NFIRSTBITS);
pcs_avoid_residue_small(pcs, RSA_EXPONENT, 1);
mp_int *q = primegen(pcs, 2, pfn, pfnparam);
mp_int *q = primegen(pcs, prog);
progress_report_phase_complete(prog);
/*
* Ensure p > q, by swapping them if not.
@ -108,22 +79,17 @@ int rsa_generate(RSAKey *key, int bits, progfn_t pfn,
* the other helpful quantities: n=pq, d=e^-1 mod (p-1)(q-1),
* and (q^-1 mod p).
*/
pfn(pfnparam, PROGFN_PROGRESS, 3, 1);
mp_int *modulus = mp_mul(p, q);
pfn(pfnparam, PROGFN_PROGRESS, 3, 2);
mp_int *pm1 = mp_copy(p);
mp_sub_integer_into(pm1, pm1, 1);
mp_int *qm1 = mp_copy(q);
mp_sub_integer_into(qm1, qm1, 1);
mp_int *phi_n = mp_mul(pm1, qm1);
pfn(pfnparam, PROGFN_PROGRESS, 3, 3);
mp_free(pm1);
mp_free(qm1);
mp_int *private_exponent = mp_invert(exponent, phi_n);
pfn(pfnparam, PROGFN_PROGRESS, 3, 4);
mp_free(phi_n);
mp_int *iqmp = mp_invert(q, p);
pfn(pfnparam, PROGFN_PROGRESS, 3, 5);
/*
* Populate the returned structure.

View File

@ -1047,18 +1047,18 @@ strbuf *rsa1_save_sb_wrapper(RSAKey *key, const char *comment,
#define return_void(out, expression) (expression)
static void no_progress(void *param, int action, int phase, int iprogress) {}
static ProgressReceiver null_progress = { .vt = &null_progress_vt };
mp_int *primegen_wrapper(PrimeCandidateSource *pcs)
{
return primegen(pcs, 0, no_progress, NULL);
return primegen(pcs, &null_progress);
}
#define primegen primegen_wrapper
RSAKey *rsa1_generate(int bits)
{
RSAKey *rsakey = snew(RSAKey);
rsa_generate(rsakey, bits, no_progress, NULL);
rsa_generate(rsakey, bits, &null_progress);
rsakey->comment = NULL;
return rsakey;
}
@ -1072,7 +1072,7 @@ ssh_key *rsa_generate_wrapper(int bits)
ssh_key *dsa_generate_wrapper(int bits)
{
struct dss_key *dsskey = snew(struct dss_key);
dsa_generate(dsskey, bits, no_progress, NULL);
dsa_generate(dsskey, bits, &null_progress);
return &dsskey->sshk;
}
#define dsa_generate dsa_generate_wrapper
@ -1080,7 +1080,7 @@ ssh_key *dsa_generate_wrapper(int bits)
ssh_key *ecdsa_generate_wrapper(int bits)
{
struct ecdsa_key *ek = snew(struct ecdsa_key);
if (!ecdsa_generate(ek, bits, no_progress, NULL)) {
if (!ecdsa_generate(ek, bits)) {
sfree(ek);
return NULL;
}
@ -1091,7 +1091,7 @@ ssh_key *ecdsa_generate_wrapper(int bits)
ssh_key *eddsa_generate_wrapper(int bits)
{
struct eddsa_key *ek = snew(struct eddsa_key);
if (!eddsa_generate(ek, bits, no_progress, NULL)) {
if (!eddsa_generate(ek, bits)) {
sfree(ek);
return NULL;
}

View File

@ -9,6 +9,7 @@
#include "putty.h"
#include "ssh.h"
#include "sshkeygen.h"
#include "licence.h"
#include "winsecur.h"
@ -59,76 +60,106 @@ void nonfatal(const char *fmt, ...)
}
/* ----------------------------------------------------------------------
* Progress report code. This is really horrible :-)
* ProgressReceiver implementation.
*/
#define PROGRESSRANGE 65535
#define MAXPHASE 5
struct progress {
int nphases;
struct {
bool exponential;
unsigned startpoint, total;
unsigned param, current, n; /* if exponential */
unsigned mult; /* if linear */
} phases[MAXPHASE];
unsigned total, divisor, range;
HWND progbar;
struct progressphase {
double startpoint, total;
double exp_probability, exp_current_value;
};
static void progress_update(void *param, int action, int phase, int iprogress)
{
struct progress *p = (struct progress *) param;
unsigned progress = iprogress;
int position;
struct progress {
int nphases;
struct progressphase phases[MAXPHASE], *currphase;
if (action < PROGFN_READY && p->nphases < phase)
p->nphases = phase;
switch (action) {
case PROGFN_INITIALISE:
p->nphases = 0;
break;
case PROGFN_LIN_PHASE:
p->phases[phase-1].exponential = false;
p->phases[phase-1].mult = p->phases[phase].total / progress;
break;
case PROGFN_EXP_PHASE:
p->phases[phase-1].exponential = true;
p->phases[phase-1].param = 0x10000 + progress;
p->phases[phase-1].current = p->phases[phase-1].total;
p->phases[phase-1].n = 0;
break;
case PROGFN_PHASE_EXTENT:
p->phases[phase-1].total = progress;
break;
case PROGFN_READY: {
unsigned total = 0;
int i;
for (i = 0; i < p->nphases; i++) {
p->phases[i].startpoint = total;
total += p->phases[i].total;
}
p->total = total;
p->divisor = ((p->total + PROGRESSRANGE - 1) / PROGRESSRANGE);
p->range = p->total / p->divisor;
SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, p->range));
break;
}
case PROGFN_PROGRESS:
if (p->phases[phase-1].exponential) {
while (p->phases[phase-1].n < progress) {
p->phases[phase-1].n++;
p->phases[phase-1].current *= p->phases[phase-1].param;
p->phases[phase-1].current /= 0x10000;
}
position = (p->phases[phase-1].startpoint +
p->phases[phase-1].total - p->phases[phase-1].current);
} else {
position = (p->phases[phase-1].startpoint +
progress * p->phases[phase-1].mult);
}
SendMessage(p->progbar, PBM_SETPOS, position / p->divisor, 0);
break;
double scale;
HWND progbar;
ProgressReceiver rec;
};
static ProgressPhase win_progress_add_probabilistic(
ProgressReceiver *prog, double cost_per_attempt, double probability) {
struct progress *p = container_of(prog, struct progress, rec);
assert(p->nphases < MAXPHASE);
int phase = p->nphases++;
p->phases[phase].exp_probability = 1.0 - probability;
p->phases[phase].exp_current_value = 1.0;
/* Expected number of attempts = 1 / probability of attempt succeeding */
p->phases[phase].total = cost_per_attempt / probability;
ProgressPhase ph = { .n = phase };
return ph;
}
static void win_progress_ready(ProgressReceiver *prog)
{
struct progress *p = container_of(prog, struct progress, rec);
double total = 0;
for (int i = 0; i < p->nphases; i++) {
p->phases[i].startpoint = total;
total += p->phases[i].total;
}
p->scale = PROGRESSRANGE / total;
SendMessage(p->progbar, PBM_SETRANGE, 0, MAKELPARAM(0, PROGRESSRANGE));
}
static void win_progress_start_phase(ProgressReceiver *prog,
ProgressPhase phase)
{
struct progress *p = container_of(prog, struct progress, rec);
assert(phase.n < p->nphases);
p->currphase = &p->phases[phase.n];
}
static void win_progress_update(struct progress *p, double phasepos)
{
double position = (p->currphase->startpoint +
p->currphase->total * phasepos);
position *= p->scale;
if (position < 0)
position = 0;
if (position > PROGRESSRANGE)
position = PROGRESSRANGE;
SendMessage(p->progbar, PBM_SETPOS, (WPARAM)position, 0);
}
static void win_progress_report_attempt(ProgressReceiver *prog)
{
struct progress *p = container_of(prog, struct progress, rec);
p->currphase->exp_current_value *= p->currphase->exp_probability;
win_progress_update(p, 1.0 - p->currphase->exp_current_value);
}
static void win_progress_report_phase_complete(ProgressReceiver *prog)
{
struct progress *p = container_of(prog, struct progress, rec);
win_progress_update(p, 1.0);
}
static const ProgressReceiverVtable win_progress_vt = {
win_progress_add_probabilistic,
win_progress_ready,
win_progress_start_phase,
win_progress_report_attempt,
win_progress_report_phase_complete,
};
static void win_progress_initialise(struct progress *p)
{
p->nphases = 0;
p->rec.vt = &win_progress_vt;
}
struct PassphraseProcStruct {
@ -353,17 +384,16 @@ static DWORD WINAPI generate_key_thread(void *param)
struct progress prog;
prog.progbar = params->progressbar;
progress_update(&prog, PROGFN_INITIALISE, 0, 0);
win_progress_initialise(&prog);
if (params->keytype == DSA)
dsa_generate(params->dsskey, params->key_bits, progress_update, &prog);
dsa_generate(params->dsskey, params->key_bits, &prog.rec);
else if (params->keytype == ECDSA)
ecdsa_generate(params->eckey, params->curve_bits,
progress_update, &prog);
ecdsa_generate(params->eckey, params->curve_bits);
else if (params->keytype == ED25519)
eddsa_generate(params->edkey, 255, progress_update, &prog);
eddsa_generate(params->edkey, 255);
else
rsa_generate(params->key, params->key_bits, progress_update, &prog);
rsa_generate(params->key, params->key_bits, &prog.rec);
PostMessage(params->dialog, WM_DONEKEY, 0, 0);