mirror of
https://git.tartarus.org/simon/putty.git
synced 2025-01-08 08:58:00 +00:00
RSA generation: option to generate strong primes.
A 'strong' prime, as defined by the Handbook of Applied Cryptography, is a prime p such that each of p-1 and p+1 has a large prime factor, and that the large factor q of p-1 is such that q-1 in turn _also_ has a large prime factor. HoAC says that making your RSA key using primes of this form defeats some factoring algorithms - but there are other faster algorithms to which it makes no difference. So this is probably not a useful precaution in practice. However, it has been recommended in the past by some official standards, and it's easy to implement given the new general facility in PrimeCandidateSource that lets you ask for your prime to satisfy an arbitrary modular congruence. (And HoAC also says there's no particular reason _not_ to use strong primes.) So I provide it as an option, just in case anyone wants to select it. The change to the key generation algorithm is entirely in sshrsag.c, and is neatly independent of the prime-generation system in use. If you're using Maurer provable prime generation, then the known factor q of p-1 can be used to help certify p, and the one for q-1 to help with q in turn; if you switch to probabilistic prime generation then you still get an RSA key with the right structure, except that every time the definition says 'prime factor' you just append '(probably)'. (The probabilistic version of this procedure is described as 'Gordon's algorithm' in HoAC section 4.4.2.)
This commit is contained in:
parent
365c1d2df7
commit
844e766b03
6
cmdgen.c
6
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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
109
sshrsag.c
109
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.
|
||||
|
@ -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)),
|
||||
|
@ -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"}:
|
||||
|
15
testcrypt.c
15
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;
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user