diff --git a/cmdgen.c b/cmdgen.c index 53b427f4..1b46e283 100644 --- a/cmdgen.c +++ b/cmdgen.c @@ -147,6 +147,7 @@ void help(void) " probable conventional probabilistic prime finding\n" " proven numbers that have been proven to be prime\n" " proven-even also try harder for an even distribution\n" + " --strong-rsa use \"strong\" primes as RSA key factors\n" ); } @@ -223,6 +224,7 @@ int main(int argc, char **argv) const char *random_device = NULL; int exit_status = 0; const PrimeGenerationPolicy *primegen = &primegen_probabilistic; + bool strong_rsa = false; if (is_interactive()) progress_fp = stderr; @@ -358,6 +360,8 @@ int main(int argc, char **argv) fprintf(stderr, "puttygen: unrecognised prime-" "generation mode `%s'\n", val); } + } else if (!strcmp(opt, "-strong-rsa")) { + strong_rsa = true; } else { errs = true; fprintf(stderr, @@ -775,7 +779,7 @@ int main(int argc, char **argv) ssh1key = NULL; } else { RSAKey *rsakey = snew(RSAKey); - rsa_generate(rsakey, bits, pgc, &cmdgen_progress); + rsa_generate(rsakey, bits, strong_rsa, pgc, &cmdgen_progress); rsakey->comment = NULL; if (keytype == RSA1) { ssh1key = rsakey; diff --git a/ssh1login-server.c b/ssh1login-server.c index bd8de7f9..d908bf97 100644 --- a/ssh1login-server.c +++ b/ssh1login-server.c @@ -145,7 +145,7 @@ static void ssh1_login_server_process_queue(PacketProtocolLayer *ppl) &primegen_probabilistic); ProgressReceiver null_progress; null_progress.vt = &null_progress_vt; - rsa_generate(s->servkey, server_key_bits, pgc, &null_progress); + rsa_generate(s->servkey, server_key_bits, false, pgc, &null_progress); primegen_free_context(pgc); s->servkey->comment = NULL; diff --git a/ssh2kex-server.c b/ssh2kex-server.c index 8a8c0f3a..5211c2a2 100644 --- a/ssh2kex-server.c +++ b/ssh2kex-server.c @@ -270,7 +270,8 @@ void ssh2kex_coroutine(struct ssh2_transport_state *s, bool *aborted) &primegen_probabilistic); ProgressReceiver null_progress; null_progress.vt = &null_progress_vt; - rsa_generate(s->rsa_kex_key, extra->minklen, pgc, &null_progress); + rsa_generate(s->rsa_kex_key, extra->minklen, false, + pgc, &null_progress); primegen_free_context(pgc); s->rsa_kex_key->comment = NULL; diff --git a/sshkeygen.h b/sshkeygen.h index 0f0df587..971a3633 100644 --- a/sshkeygen.h +++ b/sshkeygen.h @@ -284,8 +284,8 @@ extern const PrimeGenerationPolicy primegen_provable_maurer_complex; * The overall top-level API for generating entire key pairs. */ -int rsa_generate(RSAKey *key, int bits, PrimeGenerationContext *pgc, - ProgressReceiver *prog); +int rsa_generate(RSAKey *key, int bits, bool strong, + PrimeGenerationContext *pgc, ProgressReceiver *prog); int dsa_generate(struct dss_key *key, int bits, PrimeGenerationContext *pgc, ProgressReceiver *prog); int ecdsa_generate(struct ecdsa_key *key, int bits); diff --git a/sshrsag.c b/sshrsag.c index 278c9848..b9676e7a 100644 --- a/sshrsag.c +++ b/sshrsag.c @@ -14,8 +14,91 @@ static void invent_firstbits(unsigned *one, unsigned *two, unsigned min_separation); -int rsa_generate(RSAKey *key, int bits, PrimeGenerationContext *pgc, - ProgressReceiver *prog) +typedef struct RSAPrimeDetails RSAPrimeDetails; +struct RSAPrimeDetails { + bool strong; + int bits, bitsm1m1, bitsm1, bitsp1; + unsigned firstbits; + ProgressPhase phase_main, phase_m1m1, phase_m1, phase_p1; +}; + +#define STRONG_MARGIN (20 + NFIRSTBITS) + +static RSAPrimeDetails setup_rsa_prime( + int bits, bool strong, PrimeGenerationContext *pgc, ProgressReceiver *prog) +{ + RSAPrimeDetails pd; + pd.bits = bits; + if (strong) { + pd.bitsm1 = (bits - STRONG_MARGIN) / 2; + pd.bitsp1 = (bits - STRONG_MARGIN) - pd.bitsm1; + pd.bitsm1m1 = (pd.bitsm1 - STRONG_MARGIN) / 2; + if (pd.bitsm1m1 < STRONG_MARGIN) { + /* Absurdly small prime, but we should at least not crash. */ + strong = false; + } + } + pd.strong = strong; + + if (pd.strong) { + pd.phase_m1m1 = primegen_add_progress_phase(pgc, prog, pd.bitsm1m1); + pd.phase_m1 = primegen_add_progress_phase(pgc, prog, pd.bitsm1); + pd.phase_p1 = primegen_add_progress_phase(pgc, prog, pd.bitsp1); + } + pd.phase_main = primegen_add_progress_phase(pgc, prog, pd.bits); + + return pd; +} + +static mp_int *generate_rsa_prime( + RSAPrimeDetails pd, PrimeGenerationContext *pgc, ProgressReceiver *prog) +{ + mp_int *m1m1 = NULL, *m1 = NULL, *p1 = NULL, *p = NULL; + PrimeCandidateSource *pcs; + + if (pd.strong) { + progress_start_phase(prog, pd.phase_m1m1); + pcs = pcs_new_with_firstbits(pd.bitsm1m1, pd.firstbits, NFIRSTBITS); + m1m1 = primegen_generate(pgc, pcs, prog); + progress_report_phase_complete(prog); + + progress_start_phase(prog, pd.phase_m1); + pcs = pcs_new_with_firstbits(pd.bitsm1, pd.firstbits, NFIRSTBITS); + pcs_require_residue_1_mod_prime(pcs, m1m1); + m1 = primegen_generate(pgc, pcs, prog); + progress_report_phase_complete(prog); + + progress_start_phase(prog, pd.phase_p1); + pcs = pcs_new_with_firstbits(pd.bitsp1, pd.firstbits, NFIRSTBITS); + p1 = primegen_generate(pgc, pcs, prog); + progress_report_phase_complete(prog); + } + + progress_start_phase(prog, pd.phase_main); + pcs = pcs_new_with_firstbits(pd.bits, pd.firstbits, NFIRSTBITS); + pcs_avoid_residue_small(pcs, RSA_EXPONENT, 1); + if (pd.strong) { + pcs_require_residue_1_mod_prime(pcs, m1); + mp_int *p1_minus_1 = mp_copy(p1); + mp_sub_integer_into(p1_minus_1, p1, 1); + pcs_require_residue(pcs, p1, p1_minus_1); + mp_free(p1_minus_1); + } + p = primegen_generate(pgc, pcs, prog); + progress_report_phase_complete(prog); + + if (m1m1) + mp_free(m1m1); + if (m1) + mp_free(m1); + if (p1) + mp_free(p1); + + return p; +} + +int rsa_generate(RSAKey *key, int bits, bool strong, + PrimeGenerationContext *pgc, ProgressReceiver *prog) { key->sshk.vt = &ssh_rsa; @@ -41,26 +124,14 @@ int rsa_generate(RSAKey *key, int bits, PrimeGenerationContext *pgc, int pbits = bits - qbits; assert(pbits >= qbits); - ProgressPhase phase_p = primegen_add_progress_phase(pgc, prog, pbits); - ProgressPhase phase_q = primegen_add_progress_phase(pgc, prog, qbits); + RSAPrimeDetails pd = setup_rsa_prime(pbits, strong, pgc, prog); + RSAPrimeDetails qd = setup_rsa_prime(qbits, strong, pgc, prog); progress_ready(prog); - unsigned pfirst, qfirst; - invent_firstbits(&pfirst, &qfirst, 2); + invent_firstbits(&pd.firstbits, &qd.firstbits, 2); - 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_generate(pgc, 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_generate(pgc, pcs, prog); - progress_report_phase_complete(prog); + mp_int *p = generate_rsa_prime(pd, pgc, prog); + mp_int *q = generate_rsa_prime(qd, pgc, prog); /* * Ensure p > q, by swapping them if not. diff --git a/test/agenttestgen.py b/test/agenttestgen.py index f0c2cb3a..1eef2965 100755 --- a/test/agenttestgen.py +++ b/test/agenttestgen.py @@ -30,7 +30,7 @@ def generate(): import ssh keygen2 = [ - ('RSA-1024', lambda: rsa_generate(1024), + ('RSA-1024', lambda: rsa_generate(1024, False), (ssh.SSH_AGENT_RSA_SHA2_256, ssh.SSH_AGENT_RSA_SHA2_512)), ('DSA-1024', lambda: dsa_generate(1024)), ('ECDSA-p256', lambda: ecdsa_generate(256)), diff --git a/test/testcrypt.py b/test/testcrypt.py index 794373f3..e3f8a03d 100644 --- a/test/testcrypt.py +++ b/test/testcrypt.py @@ -168,6 +168,8 @@ def make_argword(arg, argtype, fnname, argindex, to_preserve): return ident if typename == "uint" and isinstance(arg, numbers.Integral): return "0x{:x}".format(arg) + if typename == "boolean": + return "true" if arg else "false" if typename in { "hashalg", "macalg", "keyalg", "cipheralg", "dh_group", "ecdh_alg", "rsaorder", "primegenpolicy"}: diff --git a/testcrypt.c b/testcrypt.c index 3cde56af..4737442d 100644 --- a/testcrypt.c +++ b/testcrypt.c @@ -407,6 +407,11 @@ static uintmax_t get_uint(BinarySource *in) return toret; } +static bool get_boolean(BinarySource *in) +{ + return ptrlen_eq_string(get_word(in), "true"); +} + static Value *lookup_value(ptrlen word) { Value *val = find234(values, &word, valuefind); @@ -1134,17 +1139,18 @@ mp_int *primegen_generate_wrapper( } #define primegen_generate primegen_generate_wrapper -RSAKey *rsa1_generate(int bits, PrimeGenerationContext *pgc) +RSAKey *rsa1_generate(int bits, bool strong, PrimeGenerationContext *pgc) { RSAKey *rsakey = snew(RSAKey); - rsa_generate(rsakey, bits, pgc, &null_progress); + rsa_generate(rsakey, bits, strong, pgc, &null_progress); rsakey->comment = NULL; return rsakey; } -ssh_key *rsa_generate_wrapper(int bits, PrimeGenerationContext *pgc) +ssh_key *rsa_generate_wrapper(int bits, bool strong, + PrimeGenerationContext *pgc) { - return &rsa1_generate(bits, pgc)->sshk; + return &rsa1_generate(bits, strong, pgc)->sshk; } #define rsa_generate rsa_generate_wrapper @@ -1217,6 +1223,7 @@ OPTIONAL_PTR_FUNC(mpint) OPTIONAL_PTR_FUNC(string) typedef uintmax_t TD_uint; +typedef bool TD_boolean; typedef ptrlen TD_val_string_ptrlen; typedef char *TD_val_string_asciz; typedef BinarySource *TD_val_string_binarysource; diff --git a/testcrypt.h b/testcrypt.h index 1ed0c1c0..70752f8f 100644 --- a/testcrypt.h +++ b/testcrypt.h @@ -262,11 +262,11 @@ FUNC3(val_string, rsa1_save_sb, val_rsa, opt_val_string_asciz, opt_val_string_as /* * Key generation functions. */ -FUNC2(val_key, rsa_generate, uint, val_pgc) +FUNC3(val_key, rsa_generate, uint, boolean, val_pgc) FUNC2(val_key, dsa_generate, uint, val_pgc) FUNC1(opt_val_key, ecdsa_generate, uint) FUNC1(opt_val_key, eddsa_generate, uint) -FUNC2(val_rsa, rsa1_generate, uint, val_pgc) +FUNC3(val_rsa, rsa1_generate, uint, boolean, val_pgc) FUNC1(val_pgc, primegen_new_context, primegenpolicy) FUNC2(opt_val_mpint, primegen_generate, val_pgc, consumed_val_pcs) FUNC2(val_string, primegen_mpu_certificate, val_pgc, val_mpint) diff --git a/windows/winpgen.c b/windows/winpgen.c index 0ffb1472..0c86d7b2 100644 --- a/windows/winpgen.c +++ b/windows/winpgen.c @@ -65,7 +65,6 @@ void nonfatal(const char *fmt, ...) */ #define PROGRESSRANGE 65535 -#define MAXPHASE 5 struct progressphase { double startpoint, total; @@ -74,8 +73,8 @@ struct progressphase { }; struct progress { - int nphases; - struct progressphase phases[MAXPHASE], *currphase; + size_t nphases, phasessize; + struct progressphase *phases, *currphase; double scale; HWND progbar; @@ -87,7 +86,7 @@ static ProgressPhase win_progress_add_linear( ProgressReceiver *prog, double overall_cost) { struct progress *p = container_of(prog, struct progress, rec); - assert(p->nphases < MAXPHASE); + sgrowarray(p->phases, p->phasessize, p->nphases); int phase = p->nphases++; p->phases[phase].total = overall_cost; @@ -100,7 +99,7 @@ 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); + sgrowarray(p->phases, p->phasessize, p->nphases); int phase = p->nphases++; p->phases[phase].exp_probability = 1.0 - probability; @@ -182,10 +181,16 @@ static const ProgressReceiverVtable win_progress_vt = { static void win_progress_initialise(struct progress *p) { - p->nphases = 0; + p->nphases = p->phasessize = 0; + p->phases = p->currphase = NULL; p->rec.vt = &win_progress_vt; } +static void win_progress_cleanup(struct progress *p) +{ + sfree(p->phases); +} + struct PassphraseProcStruct { char **passphrase; char *comment; @@ -395,6 +400,7 @@ struct rsa_key_thread_params { int curve_bits; /* bits in elliptic curve (ECDSA) */ keytype keytype; const PrimeGenerationPolicy *primepolicy; + bool rsa_strong; union { RSAKey *key; struct dss_key *dsskey; @@ -420,12 +426,15 @@ static DWORD WINAPI generate_key_thread(void *param) else if (params->keytype == EDDSA) eddsa_generate(params->edkey, params->curve_bits); else - rsa_generate(params->key, params->key_bits, pgc, &prog.rec); + rsa_generate(params->key, params->key_bits, params->rsa_strong, + pgc, &prog.rec); primegen_free_context(pgc); PostMessage(params->dialog, WM_DONEKEY, 0, 0); + win_progress_cleanup(&prog); + sfree(params); return 0; } @@ -439,6 +448,7 @@ struct MainDlgState { bool ssh2; keytype keytype; const PrimeGenerationPolicy *primepolicy; + bool rsa_strong; char **commentptr; /* points to key.comment or ssh2key.comment */ ssh2_userkey ssh2key; unsigned *entropy; @@ -518,6 +528,7 @@ enum { IDC_TYPESTATIC, IDC_KEYSSH1, IDC_KEYSSH2RSA, IDC_KEYSSH2DSA, IDC_KEYSSH2ECDSA, IDC_KEYSSH2EDDSA, IDC_PRIMEGEN_PROB, IDC_PRIMEGEN_MAURER_SIMPLE, IDC_PRIMEGEN_MAURER_COMPLEX, + IDC_RSA_STRONG, IDC_BITSSTATIC, IDC_BITS, IDC_ECCURVESTATIC, IDC_ECCURVE, IDC_EDCURVESTATIC, IDC_EDCURVE, @@ -720,6 +731,12 @@ void ui_set_primepolicy(HWND hwnd, struct MainDlgState *state, int option) break; } } +void ui_set_rsa_strong(HWND hwnd, struct MainDlgState *state, bool enable) +{ + state->rsa_strong = enable; + CheckMenuItem(state->keymenu, IDC_RSA_STRONG, + (enable ? MF_CHECKED : 0) | MF_BYCOMMAND); +} void load_key_file(HWND hwnd, struct MainDlgState *state, Filename *filename, bool was_import_cmd) @@ -911,6 +928,7 @@ static void start_generating_key(HWND hwnd, struct MainDlgState *state) params->curve_bits = state->curve_bits; params->keytype = state->keytype; params->primepolicy = state->primepolicy; + params->rsa_strong = state->rsa_strong; params->key = &state->key; params->dsskey = &state->dsskey; @@ -985,6 +1003,9 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, "Use proven primes (slower)"); AppendMenu(menu1, MF_ENABLED, IDC_PRIMEGEN_MAURER_COMPLEX, "Use proven primes with even distribution (slowest)"); + AppendMenu(menu1, MF_SEPARATOR, 0, 0); + AppendMenu(menu1, MF_ENABLED, IDC_RSA_STRONG, + "Use \"strong\" primes as RSA key factors"); AppendMenu(menu, MF_POPUP | MF_ENABLED, (UINT_PTR) menu1, "&Key"); state->keymenu = menu1; @@ -1120,6 +1141,7 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, } ui_set_key_type(hwnd, state, IDC_KEYSSH2RSA); ui_set_primepolicy(hwnd, state, IDC_PRIMEGEN_PROB); + ui_set_rsa_strong(hwnd, state, false); SetDlgItemInt(hwnd, IDC_BITS, DEFAULT_KEY_BITS, false); SendDlgItemMessage(hwnd, IDC_ECCURVE, CB_SETCURSEL, DEFAULT_ECCURVE_INDEX, 0); @@ -1186,6 +1208,12 @@ static INT_PTR CALLBACK MainDlgProc(HWND hwnd, UINT msg, ui_set_primepolicy(hwnd, state, LOWORD(wParam)); break; } + case IDC_RSA_STRONG: { + state = (struct MainDlgState *) + GetWindowLongPtr(hwnd, GWLP_USERDATA); + ui_set_rsa_strong(hwnd, state, !state->rsa_strong); + break; + } case IDC_QUIT: PostMessage(hwnd, WM_CLOSE, 0, 0); break;