diff --git a/Recipe b/Recipe index df040ab1..51b8c21c 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 mpunsafe +SSHPRIME = sshprime smallprimes primecandidate millerrabin 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 ebc55553..6d06c443 100644 --- a/cmdgen.c +++ b/cmdgen.c @@ -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); diff --git a/millerrabin.c b/millerrabin.c new file mode 100644 index 00000000..3358bc51 --- /dev/null +++ b/millerrabin.c @@ -0,0 +1,214 @@ +/* + * 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 4661c1c3..f017ed68 100644 --- a/ssh.h +++ b/ssh.h @@ -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: diff --git a/ssh1login-server.c b/ssh1login-server.c index ecc99640..528c9d09 100644 --- a/ssh1login-server.c +++ b/ssh1login-server.c @@ -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; } diff --git a/ssh2kex-server.c b/ssh2kex-server.c index 3a1907d9..d2aef99d 100644 --- a/ssh2kex-server.c +++ b/ssh2kex-server.c @@ -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; } diff --git a/sshdssg.c b/sshdssg.c index 79e11080..eba3c4e7 100644 --- a/sshdssg.c +++ b/sshdssg.c @@ -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, diff --git a/sshecdsag.c b/sshecdsag.c index 37048ea6..28a723b2 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, - 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; diff --git a/sshkeygen.h b/sshkeygen.h index 2e615ec9..f82d0dc9 100644 --- a/sshkeygen.h +++ b/sshkeygen.h @@ -67,10 +67,99 @@ 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, - 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); diff --git a/sshprime.c b/sshprime.c index 1953f4ef..1ec464fd 100644 --- a/sshprime.c +++ b/sshprime.c @@ -3,6 +3,8 @@ */ #include +#include + #include "ssh.h" #include "mpint.h" #include "mpunsafe.h" @@ -26,177 +28,54 @@ * - go back to square one if any M-R test fails. */ -/* - * 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) +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); - /* - * 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); - + MillerRabin *mr = miller_rabin_new(p); bool known_bad = false; - - /* - * 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) + 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; - - mp_free(wqp); + break; + } } - - mp_free(q); - mp_free(two); - mp_free(pm1); - monty_free(mc); - mp_free(m_pm1); + miller_rabin_free(mr); if (known_bad) { mp_free(p); @@ -209,3 +88,39 @@ 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); +} diff --git a/sshrsag.c b/sshrsag.c index 5dd09a35..91dbcdb1 100644 --- a/sshrsag.c +++ b/sshrsag.c @@ -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. diff --git a/testcrypt.c b/testcrypt.c index 7d9d3476..61204bd7 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 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; } diff --git a/windows/winpgen.c b/windows/winpgen.c index ab12b77c..d1ec8d0b 100644 --- a/windows/winpgen.c +++ b/windows/winpgen.c @@ -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);