1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

Revert "New vtable API for keygen progress reporting."

This reverts commit a7bdefb394.

I had accidentally mashed it together with another commit. I did
actually want to push both of them, but I'd rather push them
separately! So I'm backing out the combined blob, and I'll re-push
them with their proper comments and explanations.
This commit is contained in:
Simon Tatham 2020-02-29 16:32:16 +00:00
parent a7bdefb394
commit 62733a8389
13 changed files with 396 additions and 565 deletions

2
Recipe
View File

@ -282,7 +282,7 @@ UXSSH = SSH uxnoise uxagentc uxgss uxshare
SFTP = psftpcommon sftp sftpcommon logging cmdline
# Components of the prime-generation system.
SSHPRIME = sshprime smallprimes primecandidate millerrabin mpunsafe
SSHPRIME = sshprime smallprimes primecandidate mpunsafe
# Miscellaneous objects appearing in all the utilities, or all the
# network ones, or the Unix or Windows subsets of those in turn.

View File

@ -13,35 +13,36 @@
#include "putty.h"
#include "ssh.h"
#include "sshkeygen.h"
#include "mpint.h"
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,
struct progress {
int phase, current;
};
static ProgressReceiver cmdgen_progress = { .vt = &cmdgen_progress_vt };
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)
{
}
/*
* Stubs to let everything else link sensibly.
@ -183,12 +184,10 @@ 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)
/* ------------------------------------------------------------------
@ -334,7 +333,7 @@ int main(int argc, char **argv)
outtype = PUBLIC;
break;
case 'q':
progress_fp = NULL;
progressfn = no_progress;
break;
}
break;
@ -646,6 +645,10 @@ 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)
@ -670,25 +673,25 @@ int main(int argc, char **argv)
if (keytype == DSA) {
struct dss_key *dsskey = snew(struct dss_key);
dsa_generate(dsskey, bits, &cmdgen_progress);
dsa_generate(dsskey, bits, progressfn, &prog);
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);
ecdsa_generate(ek, bits, progressfn, &prog);
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);
eddsa_generate(ek, bits, progressfn, &prog);
ssh2key = snew(ssh2_userkey);
ssh2key->key = &ek->sshk;
ssh1key = NULL;
} else {
RSAKey *rsakey = snew(RSAKey);
rsa_generate(rsakey, bits, &cmdgen_progress);
rsa_generate(rsakey, bits, progressfn, &prog);
rsakey->comment = NULL;
if (keytype == RSA1) {
ssh1key = rsakey;
@ -697,6 +700,7 @@ 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

@ -1,214 +0,0 @@
/*
* millerrabin.c: Miller-Rabin probabilistic primality testing, as
* declared in sshkeygen.h.
*/
#include <assert.h>
#include "ssh.h"
#include "sshkeygen.h"
#include "mpint.h"
#include "mpunsafe.h"
/*
* The Miller-Rabin primality test is an extension to the Fermat
* test. The Fermat test just checks that a^(p-1) == 1 mod p; this
* is vulnerable to Carmichael numbers. Miller-Rabin considers how
* that 1 is derived as well.
*
* Lemma: if a^2 == 1 (mod p), and p is prime, then either a == 1
* or a == -1 (mod p).
*
* Proof: p divides a^2-1, i.e. p divides (a+1)(a-1). Hence,
* since p is prime, either p divides (a+1) or p divides (a-1).
* But this is the same as saying that either a is congruent to
* -1 mod p or a is congruent to +1 mod p. []
*
* Comment: This fails when p is not prime. Consider p=mn, so
* that mn divides (a+1)(a-1). Now we could have m dividing (a+1)
* and n dividing (a-1), without the whole of mn dividing either.
* For example, consider a=10 and p=99. 99 = 9 * 11; 9 divides
* 10-1 and 11 divides 10+1, so a^2 is congruent to 1 mod p
* without a having to be congruent to either 1 or -1.
*
* So the Miller-Rabin test, as well as considering a^(p-1),
* considers a^((p-1)/2), a^((p-1)/4), and so on as far as it can
* go. In other words. we write p-1 as q * 2^k, with k as large as
* possible (i.e. q must be odd), and we consider the powers
*
* a^(q*2^0) a^(q*2^1) ... a^(q*2^(k-1)) a^(q*2^k)
* i.e. a^((n-1)/2^k) a^((n-1)/2^(k-1)) ... a^((n-1)/2) a^(n-1)
*
* If p is to be prime, the last of these must be 1. Therefore, by
* the above lemma, the one before it must be either 1 or -1. And
* _if_ it's 1, then the one before that must be either 1 or -1,
* and so on ... In other words, we expect to see a trailing chain
* of 1s preceded by a -1. (If we're unlucky, our trailing chain of
* 1s will be as long as the list so we'll never get to see what
* lies before it. This doesn't count as a test failure because it
* hasn't _proved_ that p is not prime.)
*
* For example, consider a=2 and p=1729. 1729 is a Carmichael
* number: although it's not prime, it satisfies a^(p-1) == 1 mod p
* for any a coprime to it. So the Fermat test wouldn't have a
* problem with it at all, unless we happened to stumble on an a
* which had a common factor.
*
* So. 1729 - 1 equals 27 * 2^6. So we look at
*
* 2^27 mod 1729 == 645
* 2^108 mod 1729 == 1065
* 2^216 mod 1729 == 1
* 2^432 mod 1729 == 1
* 2^864 mod 1729 == 1
* 2^1728 mod 1729 == 1
*
* We do have a trailing string of 1s, so the Fermat test would
* have been happy. But this trailing string of 1s is preceded by
* 1065; whereas if 1729 were prime, we'd expect to see it preceded
* by -1 (i.e. 1728.). Guards! Seize this impostor.
*
* (If we were unlucky, we might have tried a=16 instead of a=2;
* now 16^27 mod 1729 == 1, so we would have seen a long string of
* 1s and wouldn't have seen the thing _before_ the 1s. So, just
* like the Fermat test, for a given p there may well exist values
* of a which fail to show up its compositeness. So we try several,
* just like the Fermat test. The difference is that Miller-Rabin
* is not _in general_ fooled by Carmichael numbers.)
*
* Put simply, then, the Miller-Rabin test requires us to:
*
* 1. write p-1 as q * 2^k, with q odd
* 2. compute z = (a^q) mod p.
* 3. report success if z == 1 or z == -1.
* 4. square z at most k-1 times, and report success if it becomes
* -1 at any point.
* 5. report failure otherwise.
*
* (We expect z to become -1 after at most k-1 squarings, because
* if it became -1 after k squarings then a^(p-1) would fail to be
* 1. And we don't need to investigate what happens after we see a
* -1, because we _know_ that -1 squared is 1 modulo anything at
* all, so after we've seen a -1 we can be sure of seeing nothing
* but 1s.)
*/
struct MillerRabin {
MontyContext *mc;
size_t k;
mp_int *q;
mp_int *two, *pm1, *m_pm1;
};
MillerRabin *miller_rabin_new(mp_int *p)
{
MillerRabin *mr = snew(MillerRabin);
assert(mp_hs_integer(p, 2));
assert(mp_get_bit(p, 0) == 1);
mr->k = 1;
while (!mp_get_bit(p, mr->k))
mr->k++;
mr->q = mp_rshift_safe(p, mr->k);
mr->two = mp_from_integer(2);
mr->pm1 = mp_unsafe_copy(p);
mp_sub_integer_into(mr->pm1, mr->pm1, 1);
mr->mc = monty_new(p);
mr->m_pm1 = monty_import(mr->mc, mr->pm1);
return mr;
}
void miller_rabin_free(MillerRabin *mr)
{
mp_free(mr->q);
mp_free(mr->two);
mp_free(mr->pm1);
mp_free(mr->m_pm1);
monty_free(mr->mc);
smemclr(mr, sizeof(*mr));
sfree(mr);
}
struct mr_result {
bool passed;
bool potential_primitive_root;
};
static struct mr_result miller_rabin_test_inner(MillerRabin *mr, mp_int *w)
{
/*
* Compute w^q mod p.
*/
mp_int *wqp = monty_pow(mr->mc, w, mr->q);
/*
* See if this is 1, or if it is -1, or if it becomes -1
* when squared at most k-1 times.
*/
struct mr_result result;
result.passed = false;
result.potential_primitive_root = false;
if (mp_cmp_eq(wqp, monty_identity(mr->mc))) {
result.passed = true;
} else {
for (size_t i = 0; i < mr->k; i++) {
if (mp_cmp_eq(wqp, mr->m_pm1)) {
result.passed = true;
result.potential_primitive_root = (i == mr->k - 1);
break;
}
if (i == mr->k - 1)
break;
monty_mul_into(mr->mc, wqp, wqp, wqp);
}
}
mp_free(wqp);
return result;
}
bool miller_rabin_test_random(MillerRabin *mr)
{
mp_int *mw = mp_random_in_range(mr->two, mr->pm1);
struct mr_result result = miller_rabin_test_inner(mr, mw);
mp_free(mw);
return result.passed;
}
mp_int *miller_rabin_find_potential_primitive_root(MillerRabin *mr)
{
while (true) {
mp_int *mw = mp_unsafe_shrink(mp_random_in_range(mr->two, mr->pm1));
struct mr_result result = miller_rabin_test_inner(mr, mw);
if (result.passed && result.potential_primitive_root) {
mp_int *pr = monty_export(mr->mc, mw);
mp_free(mw);
return pr;
}
mp_free(mw);
if (!result.passed) {
return NULL;
}
}
}
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);
}

20
ssh.h
View File

@ -1313,6 +1313,26 @@ 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,6 +70,8 @@ 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)
@ -140,9 +142,7 @@ 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);
ProgressReceiver null_progress;
null_progress.vt = &null_progress_vt;
rsa_generate(s->servkey, server_key_bits, &null_progress);
rsa_generate(s->servkey, server_key_bits, no_progress, NULL);
s->servkey->comment = NULL;
s->servkey_generated_here = true;
}

View File

@ -36,6 +36,10 @@ 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 */
@ -97,9 +101,7 @@ 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.
*/
ProgressReceiver null_progress;
null_progress.vt = &null_progress_vt;
s->p = primegen(pcs_new(s->pbits), &null_progress);
s->p = primegen(pcs_new(s->pbits), 1, no_progress, NULL);
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;
@ -261,9 +263,7 @@ 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);
ProgressReceiver null_progress;
null_progress.vt = &null_progress_vt;
rsa_generate(s->rsa_kex_key, extra->minklen, &null_progress);
rsa_generate(s->rsa_kex_key, extra->minklen, no_progress, NULL);
s->rsa_kex_key->comment = NULL;
s->rsa_kex_key_needs_freeing = true;
}

View File

@ -7,50 +7,71 @@
#include "sshkeygen.h"
#include "mpint.h"
int dsa_generate(struct dss_key *key, int bits, ProgressReceiver *prog)
int dsa_generate(struct dss_key *key, int bits, progfn_t pfn,
void *pfnparam)
{
/*
* Progress-reporting setup.
* Set up the phase limits for the progress report. We do this
* by passing minus the phase number.
*
* 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.
* 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.
*
* 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.
* 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 the probability of success will end up indistinguishable
* from 1 in IEEE standard floating point! But what can you do.)
* 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.
*/
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);
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);
PrimeCandidateSource *pcs;
/*
* Generate q: a prime of length 160.
*/
progress_start_phase(prog, phase_q);
pcs = pcs_new(160);
mp_int *q = primegen(pcs, prog);
progress_report_phase_complete(prog);
mp_int *q = primegen(pcs, 1, pfn, pfnparam);
/*
* 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, prog);
progress_report_phase_complete(prog);
mp_int *p = primegen(pcs, 2, pfn, pfnparam);
/*
* Next we need g. Raise 2 to the power (p-1)/q modulo p, and
@ -58,12 +79,12 @@ int dsa_generate(struct dss_key *key, int bits, ProgressReceiver *prog)
* 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) {
progress_report_attempt(prog);
pfn(pfnparam, PROGFN_PROGRESS, 3, ++progress);
g = mp_modpow(h, power, p);
if (mp_hs_integer(g, 2))
break; /* got one */
@ -72,7 +93,6 @@ int dsa_generate(struct dss_key *key, int bits, ProgressReceiver *prog)
}
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)
int ecdsa_generate(struct ecdsa_key *ek, int bits,
progfn_t pfn, void *pfnparam)
{
if (!ec_nist_alg_and_curve_by_bits(bits, &ek->curve, &ek->sshk.vt))
return 0;
@ -20,7 +20,8 @@ int ecdsa_generate(struct ecdsa_key *ek, int bits)
return 1;
}
int eddsa_generate(struct eddsa_key *ek, int bits)
int eddsa_generate(struct eddsa_key *ek, int bits,
progfn_t pfn, void *pfnparam)
{
if (!ec_ed_alg_and_curve_by_bits(bits, &ek->curve, &ek->sshk.vt))
return 0;

View File

@ -67,99 +67,10 @@ void pcs_inspect(PrimeCandidateSource *pcs, mp_int **limit_out,
/* Query functions for primegen to use */
unsigned pcs_get_bits(PrimeCandidateSource *pcs);
/* ----------------------------------------------------------------------
* A system for doing Miller-Rabin probabilistic primality tests.
* These benefit from having set up some context beforehand, if you're
* going to do more than one of them on the same candidate prime, so
* we declare an object type here to store that context.
*/
typedef struct MillerRabin MillerRabin;
/* Make and free a Miller-Rabin context. */
MillerRabin *miller_rabin_new(mp_int *p);
void miller_rabin_free(MillerRabin *mr);
/* Perform a single Miller-Rabin test, using a random witness value. */
bool miller_rabin_test_random(MillerRabin *mr);
/* Suggest how many tests are needed to make it sufficiently unlikely
* that a composite number will pass them all */
unsigned miller_rabin_checks_needed(unsigned bits);
/* An extension to the M-R test, which iterates until it either finds
* a witness value that is potentially a primitive root, or one
* that proves the number to be composite. */
mp_int *miller_rabin_find_potential_primitive_root(MillerRabin *mr);
/* ----------------------------------------------------------------------
* 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, 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);
mp_int *primegen(PrimeCandidateSource *pcs,
int phase, progfn_t pfn, void *pfnparam);

View File

@ -3,8 +3,6 @@
*/
#include <assert.h>
#include <math.h>
#include "ssh.h"
#include "mpint.h"
#include "mpunsafe.h"
@ -28,54 +26,177 @@
* - 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).
/*
* The Miller-Rabin primality test is an extension to the Fermat
* test. The Fermat test just checks that a^(p-1) == 1 mod p; this
* is vulnerable to Carmichael numbers. Miller-Rabin considers how
* that 1 is derived as well.
*
* 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.
* Lemma: if a^2 == 1 (mod p), and p is prime, then either a == 1
* or a == -1 (mod p).
*
* Proof: p divides a^2-1, i.e. p divides (a+1)(a-1). Hence,
* since p is prime, either p divides (a+1) or p divides (a-1).
* But this is the same as saying that either a is congruent to
* -1 mod p or a is congruent to +1 mod p. []
*
* Comment: This fails when p is not prime. Consider p=mn, so
* that mn divides (a+1)(a-1). Now we could have m dividing (a+1)
* and n dividing (a-1), without the whole of mn dividing either.
* For example, consider a=10 and p=99. 99 = 9 * 11; 9 divides
* 10-1 and 11 divides 10+1, so a^2 is congruent to 1 mod p
* without a having to be congruent to either 1 or -1.
*
* So the Miller-Rabin test, as well as considering a^(p-1),
* considers a^((p-1)/2), a^((p-1)/4), and so on as far as it can
* go. In other words. we write p-1 as q * 2^k, with k as large as
* possible (i.e. q must be odd), and we consider the powers
*
* a^(q*2^0) a^(q*2^1) ... a^(q*2^(k-1)) a^(q*2^k)
* i.e. a^((n-1)/2^k) a^((n-1)/2^(k-1)) ... a^((n-1)/2) a^(n-1)
*
* If p is to be prime, the last of these must be 1. Therefore, by
* the above lemma, the one before it must be either 1 or -1. And
* _if_ it's 1, then the one before that must be either 1 or -1,
* and so on ... In other words, we expect to see a trailing chain
* of 1s preceded by a -1. (If we're unlucky, our trailing chain of
* 1s will be as long as the list so we'll never get to see what
* lies before it. This doesn't count as a test failure because it
* hasn't _proved_ that p is not prime.)
*
* For example, consider a=2 and p=1729. 1729 is a Carmichael
* number: although it's not prime, it satisfies a^(p-1) == 1 mod p
* for any a coprime to it. So the Fermat test wouldn't have a
* problem with it at all, unless we happened to stumble on an a
* which had a common factor.
*
* So. 1729 - 1 equals 27 * 2^6. So we look at
*
* 2^27 mod 1729 == 645
* 2^108 mod 1729 == 1065
* 2^216 mod 1729 == 1
* 2^432 mod 1729 == 1
* 2^864 mod 1729 == 1
* 2^1728 mod 1729 == 1
*
* We do have a trailing string of 1s, so the Fermat test would
* have been happy. But this trailing string of 1s is preceded by
* 1065; whereas if 1729 were prime, we'd expect to see it preceded
* by -1 (i.e. 1728.). Guards! Seize this impostor.
*
* (If we were unlucky, we might have tried a=16 instead of a=2;
* now 16^27 mod 1729 == 1, so we would have seen a long string of
* 1s and wouldn't have seen the thing _before_ the 1s. So, just
* like the Fermat test, for a given p there may well exist values
* of a which fail to show up its compositeness. So we try several,
* just like the Fermat test. The difference is that Miller-Rabin
* is not _in general_ fooled by Carmichael numbers.)
*
* Put simply, then, the Miller-Rabin test requires us to:
*
* 1. write p-1 as q * 2^k, with q odd
* 2. compute z = (a^q) mod p.
* 3. report success if z == 1 or z == -1.
* 4. square z at most k-1 times, and report success if it becomes
* -1 at any point.
* 5. report failure otherwise.
*
* (We expect z to become -1 after at most k-1 squarings, because
* if it became -1 after k squarings then a^(p-1) would fail to be
* 1. And we don't need to investigate what happens after we see a
* -1, because we _know_ that -1 squared is 1 modulo anything at
* all, so after we've seen a -1 we can be sure of seeing nothing
* but 1s.)
*/
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)
mp_int *primegen(PrimeCandidateSource *pcs,
int phase, progfn_t pfn, void *pfnparam)
{
pcs_ready(pcs);
int progress = 0;
STARTOVER:
progress_report_attempt(prog);
pfn(pfnparam, PROGFN_PROGRESS, phase, ++progress);
mp_int *p = pcs_generate(pcs);
MillerRabin *mr = miller_rabin_new(p);
/*
* 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;
/*
* Next, write p-1 as q*2^k.
*/
size_t k;
for (k = 0; mp_get_bit(p, k) == !k; k++)
continue; /* find first 1 bit in p-1 */
mp_int *q = mp_rshift_safe(p, k);
/*
* Set up stuff for the Miller-Rabin checks.
*/
mp_int *two = mp_from_integer(2);
mp_int *pm1 = mp_copy(p);
mp_sub_integer_into(pm1, pm1, 1);
MontyContext *mc = monty_new(p);
mp_int *m_pm1 = monty_import(mc, pm1);
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;
/*
* Now, for each check ...
*/
for (unsigned check = 0; check < checks && !known_bad; check++) {
/*
* Invent a random number between 1 and p-1.
*/
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.
*/
mp_int *wqp = monty_pow(mc, w, q);
mp_free(w);
/*
* See if this is 1, or if it is -1, or if it becomes -1
* when squared at most k-1 times.
*/
bool passed = false;
if (mp_cmp_eq(wqp, monty_identity(mc)) || mp_cmp_eq(wqp, m_pm1)) {
passed = true;
} else {
for (size_t i = 0; i < k - 1; i++) {
monty_mul_into(mc, wqp, wqp, wqp);
if (mp_cmp_eq(wqp, m_pm1)) {
passed = true;
break;
}
}
miller_rabin_free(mr);
}
if (!passed)
known_bad = true;
mp_free(wqp);
}
mp_free(q);
mp_free(two);
mp_free(pm1);
monty_free(mc);
mp_free(m_pm1);
if (known_bad) {
mp_free(p);
@ -88,39 +209,3 @@ mp_int *primegen(PrimeCandidateSource *pcs, ProgressReceiver *prog)
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,12 +14,49 @@
static void invent_firstbits(unsigned *one, unsigned *two,
unsigned min_separation);
int rsa_generate(RSAKey *key, int bits, ProgressReceiver *prog)
int rsa_generate(RSAKey *key, int bits, progfn_t pfn,
void *pfnparam)
{
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.
*/
@ -43,23 +80,15 @@ int rsa_generate(RSAKey *key, int bits, ProgressReceiver *prog)
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, prog);
progress_report_phase_complete(prog);
mp_int *p = primegen(pcs, 1, pfn, pfnparam);
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, prog);
progress_report_phase_complete(prog);
mp_int *q = primegen(pcs, 2, pfn, pfnparam);
/*
* Ensure p > q, by swapping them if not.
@ -79,17 +108,22 @@ int rsa_generate(RSAKey *key, int bits, ProgressReceiver *prog)
* 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 ProgressReceiver null_progress = { .vt = &null_progress_vt };
static void no_progress(void *param, int action, int phase, int iprogress) {}
mp_int *primegen_wrapper(PrimeCandidateSource *pcs)
{
return primegen(pcs, &null_progress);
return primegen(pcs, 0, no_progress, NULL);
}
#define primegen primegen_wrapper
RSAKey *rsa1_generate(int bits)
{
RSAKey *rsakey = snew(RSAKey);
rsa_generate(rsakey, bits, &null_progress);
rsa_generate(rsakey, bits, no_progress, NULL);
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, &null_progress);
dsa_generate(dsskey, bits, no_progress, NULL);
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)) {
if (!ecdsa_generate(ek, bits, no_progress, NULL)) {
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)) {
if (!eddsa_generate(ek, bits, no_progress, NULL)) {
sfree(ek);
return NULL;
}

View File

@ -9,7 +9,6 @@
#include "putty.h"
#include "ssh.h"
#include "sshkeygen.h"
#include "licence.h"
#include "winsecur.h"
@ -60,106 +59,76 @@ void nonfatal(const char *fmt, ...)
}
/* ----------------------------------------------------------------------
* ProgressReceiver implementation.
* Progress report code. This is really horrible :-)
*/
#define PROGRESSRANGE 65535
#define MAXPHASE 5
struct progressphase {
double startpoint, total;
double exp_probability, exp_current_value;
};
struct progress {
int nphases;
struct progressphase phases[MAXPHASE], *currphase;
double scale;
struct {
bool exponential;
unsigned startpoint, total;
unsigned param, current, n; /* if exponential */
unsigned mult; /* if linear */
} phases[MAXPHASE];
unsigned total, divisor, range;
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)
static void progress_update(void *param, int action, int phase, int iprogress)
{
struct progress *p = container_of(prog, struct progress, rec);
struct progress *p = (struct progress *) param;
unsigned progress = iprogress;
int position;
double total = 0;
for (int i = 0; i < p->nphases; i++) {
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->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;
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;
}
}
struct PassphraseProcStruct {
@ -384,16 +353,17 @@ static DWORD WINAPI generate_key_thread(void *param)
struct progress prog;
prog.progbar = params->progressbar;
win_progress_initialise(&prog);
progress_update(&prog, PROGFN_INITIALISE, 0, 0);
if (params->keytype == DSA)
dsa_generate(params->dsskey, params->key_bits, &prog.rec);
dsa_generate(params->dsskey, params->key_bits, progress_update, &prog);
else if (params->keytype == ECDSA)
ecdsa_generate(params->eckey, params->curve_bits);
ecdsa_generate(params->eckey, params->curve_bits,
progress_update, &prog);
else if (params->keytype == ED25519)
eddsa_generate(params->edkey, 255);
eddsa_generate(params->edkey, 255, progress_update, &prog);
else
rsa_generate(params->key, params->key_bits, &prog.rec);
rsa_generate(params->key, params->key_bits, progress_update, &prog);
PostMessage(params->dialog, WM_DONEKEY, 0, 0);