diff --git a/Recipe b/Recipe index 51b8c21c..df040ab1 100644 --- a/Recipe +++ b/Recipe @@ -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. diff --git a/cmdgen.c b/cmdgen.c index 6d06c443..ebc55553 100644 --- a/cmdgen.c +++ b/cmdgen.c @@ -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); diff --git a/millerrabin.c b/millerrabin.c deleted file mode 100644 index 3358bc51..00000000 --- a/millerrabin.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - * millerrabin.c: Miller-Rabin probabilistic primality testing, as - * declared in sshkeygen.h. - */ - -#include -#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); -} - diff --git a/ssh.h b/ssh.h index f017ed68..4661c1c3 100644 --- a/ssh.h +++ b/ssh.h @@ -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: diff --git a/ssh1login-server.c b/ssh1login-server.c index 528c9d09..ecc99640 100644 --- a/ssh1login-server.c +++ b/ssh1login-server.c @@ -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; } diff --git a/ssh2kex-server.c b/ssh2kex-server.c index d2aef99d..3a1907d9 100644 --- a/ssh2kex-server.c +++ b/ssh2kex-server.c @@ -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; } diff --git a/sshdssg.c b/sshdssg.c index eba3c4e7..79e11080 100644 --- a/sshdssg.c +++ b/sshdssg.c @@ -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, diff --git a/sshecdsag.c b/sshecdsag.c index 28a723b2..37048ea6 100644 --- a/sshecdsag.c +++ b/sshecdsag.c @@ -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; diff --git a/sshkeygen.h b/sshkeygen.h index f82d0dc9..2e615ec9 100644 --- a/sshkeygen.h +++ b/sshkeygen.h @@ -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); diff --git a/sshprime.c b/sshprime.c index 1ec464fd..1953f4ef 100644 --- a/sshprime.c +++ b/sshprime.c @@ -3,8 +3,6 @@ */ #include -#include - #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). - * - * 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) +/* + * 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.) + */ +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; - break; + + /* + * 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; + } + } } + + if (!passed) + known_bad = true; + + mp_free(wqp); } - miller_rabin_free(mr); + + 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); -} diff --git a/sshrsag.c b/sshrsag.c index 91dbcdb1..5dd09a35 100644 --- a/sshrsag.c +++ b/sshrsag.c @@ -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. diff --git a/testcrypt.c b/testcrypt.c index 61204bd7..7d9d3476 100644 --- a/testcrypt.c +++ b/testcrypt.c @@ -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; } diff --git a/windows/winpgen.c b/windows/winpgen.c index d1ec8d0b..ab12b77c 100644 --- a/windows/winpgen.c +++ b/windows/winpgen.c @@ -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++) { - p->phases[i].startpoint = total; - total += p->phases[i].total; + 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; } - 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 { @@ -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);