upstream: remove single-letter flags for moduli options

Move all moduli generation options to live under the -O flag.

Frees up seven single-letter flags.

NB. this change break existing ssh-keygen commandline syntax for moduli-
related operations. Very few people use these fortunately.

feedback and ok markus@

OpenBSD-Commit-ID: d498f3eaf28128484826a4fcb343612764927935
This commit is contained in:
djm@openbsd.org 2019-12-30 03:30:09 +00:00 committed by Damien Miller
parent 1e645fe767
commit 3e60d18fba
2 changed files with 228 additions and 163 deletions

View File

@ -1,4 +1,4 @@
.\" $OpenBSD: ssh-keygen.1,v 1.183 2019/12/30 03:28:41 djm Exp $
.\" $OpenBSD: ssh-keygen.1,v 1.184 2019/12/30 03:30:09 djm Exp $
.\"
.\" Author: Tatu Ylonen <ylo@cs.hut.fi>
.\" Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -99,20 +99,14 @@
.Op Fl g
.Op Fl f Ar input_keyfile
.Nm ssh-keygen
.Fl G Ar output_file
.Op Fl v
.Op Fl b Ar bits
.Op Fl M Ar memory
.Op Fl S Ar start_point
.Fl M Cm generate
.Op Fl O Ar option
.Ar
.Nm ssh-keygen
.Fl M Cm screen
.Fl f Ar input_file
.Fl T Ar output_file
.Op Fl v
.Op Fl a Ar rounds
.Op Fl J Ar num_lines
.Op Fl j Ar start_line
.Op Fl K Ar checkpt
.Op Fl W Ar generator
.Op Fl O Ar option
.Ar
.Nm ssh-keygen
.Fl I Ar certificate_identity
.Fl s Ar ca_key
@ -268,11 +262,6 @@ When saving a private key, this option specifies the number of KDF
(key derivation function) rounds used.
Higher numbers result in slower passphrase verification and increased
resistance to brute-force password cracking (should the keys be stolen).
.Pp
When screening DH-GEX candidates (using the
.Fl T
command),
this option specifies the number of primality tests to perform.
.It Fl B
Show the bubblebabble digest of specified private or public key file.
.It Fl b Ar bits
@ -333,12 +322,6 @@ used in conjunction with the
option to print found keys in a hashed format.
.It Fl f Ar filename
Specifies the filename of the key file.
.It Fl G Ar output_file
Generate candidate primes for DH-GEX.
These primes must be screened for
safety (using the
.Fl T
option) before use.
.It Fl g
Use generic DNS format when printing fingerprint resource records using the
.Fl r
@ -379,24 +362,6 @@ This option allows importing keys from other software, including several
commercial SSH implementations.
The default import format is
.Dq RFC4716 .
.It Fl J Ar num_lines
Exit after screening the specified number of lines
while performing DH candidate screening using the
.Fl T
option.
.It Fl j Ar start_line
Start screening at the specified line number
while performing DH candidate screening using the
.Fl T
option.
.It Fl K Ar checkpt
Write the last line processed to the file
.Ar checkpt
while performing DH candidate screening using the
.Fl T
option.
This will be used to skip lines in the input file that have already been
processed if the job is restarted.
.It Fl k
Generate a KRL file.
In this mode,
@ -419,9 +384,26 @@ If combined with
.Fl v ,
a visual ASCII art representation of the key is supplied with the
fingerprint.
.It Fl M Ar memory
Specify the amount of memory to use (in megabytes) when generating
candidate moduli for DH-GEX.
.It Fl M Cm generate
Generate candidate Diffie-Hellman Group Exchange (DH-GEX) parameters for
eventual use by the
.Sq diffie-hellman-group-exchange-*
key exchange methods.
The numbers generated by this operation must be further screened before
use.
See the
.Sx MODULI GENERATION
section for more information.
.It Fl M Cm screen
Screen candidate parameters for Diffie-Hellman Group Exchange.
This will accept a list of candidate numbers and test that they are
safe (Sophie Germain) primes with acceptable group generators.
The results of this operation may be added to the
.Pa /etc/moduli
file.
See the
.Sx MODULI GENERATION
section for more information.
.It Fl m Ar key_format
Specify a key format for key generation, the
.Fl i
@ -457,10 +439,20 @@ Please see the
.Sx CERTIFICATES
section for details.
.It Fl O Ar option
Specify a certificate option when signing a key.
See the
Specify a key/value option.
These are specific to the operation that
.Nm
has been requested to perform.
.Pp
When signing certificates, one of the options listed in the
.Sx CERTIFICATES
section for a list of available certificate options.
section may be specified here.
.Pp
When performing moduli generation or screening, one of the options
listed in the
.Sx MODULI GENERATION
section may be specified.
.Pp
This option may be specified multiple times.
.It Fl P Ar passphrase
Provides the (old) passphrase.
@ -489,8 +481,6 @@ option above).
Print the SSHFP fingerprint resource record named
.Ar hostname
for the specified public key file.
.It Fl S Ar start
Specify start point (in hex) when generating candidate moduli for DH-GEX.
.It Fl s Ar ca_key
Certify (sign) a public key using the specified CA key.
Please see the
@ -504,10 +494,6 @@ by key ID or serial number.
See the
.Sx KEY REVOCATION LISTS
section for details.
.It Fl T Ar output_file
Test DH group exchange candidate primes (generated using the
.Fl G
option) for safety.
.It Fl t Cm dsa | ecdsa | ecdsa-sk | ed25519 | ed25519-sk | rsa
Specifies the type of key to create.
The possible values are
@ -583,8 +569,6 @@ Multiple
.Fl v
options increase the verbosity.
The maximum is 3.
.It Fl W Ar generator
Specify desired generator when testing candidate moduli for DH-GEX.
.It Fl w Ar provider
Specifies a path to a library that will be used when creating
FIDO authenticator-hosted keys, overriding the default of using
@ -701,25 +685,25 @@ These candidate primes are then tested for suitability (a CPU-intensive
process).
.Pp
Generation of primes is performed using the
.Fl G
.Fl M Cm generate
option.
The desired length of the primes may be specified by the
.Fl b
.Fl O Cm bits
option.
For example:
.Pp
.Dl # ssh-keygen -G moduli-2048.candidates -b 2048
.Dl # ssh-keygen -M generate -O bits=2048 moduli-2048.candidates
.Pp
By default, the search for primes begins at a random point in the
desired length range.
This may be overridden using the
.Fl S
.Fl O Cm start
option, which specifies a different start point (in hex).
.Pp
Once a set of candidates have been generated, they must be screened for
suitability.
This may be performed using the
.Fl T
.Fl M Cm screen
option.
In this mode
.Nm
@ -728,16 +712,16 @@ will read candidates from standard input (or a file specified using the
option).
For example:
.Pp
.Dl # ssh-keygen -T moduli-2048 -f moduli-2048.candidates
.Dl # ssh-keygen -M screen -f moduli-2048.candidates moduli-2048
.Pp
By default, each candidate will be subjected to 100 primality tests.
This may be overridden using the
.Fl a
.Fl O Cm prime-tests
option.
The DH generator value will be chosen automatically for the
prime under consideration.
If a specific generator is desired, it may be requested using the
.Fl W
.Fl O Cm generator
option.
Valid generator values are 2, 3, and 5.
.Pp
@ -745,6 +729,36 @@ Screened DH groups may be installed in
.Pa /etc/moduli .
It is important that this file contains moduli of a range of bit lengths and
that both ends of a connection share common moduli.
.Pp
A number of options are available for moduli generation and screening via the
.Fl O
flag:
.Bl -tag -width Ds -compact
.Pp
.It Ic lines Ns = Ns Ar number
Exit after screening the specified number of lines while performing DH
candidate screening.
.Pp
.It Ic start-line Ns = Ns Ar line-number
Start screening at the specified line number while performing DH candidate
screening.
.Pp
.It Ic checkpoint Ns = Ns Ar filename
Write the last line processed to the specified file while performing DH
candidate screening.
This will be used to skip lines in the input file that have already been
processed if the job is restarted.
.Pp
.It Ic memory Ns = Ns Ar mbytes
Specify the amount of memory to use (in megabytes) when generating
candidate moduli for DH-GEX.
.Pp
.It Ic start Ns = Ns Ar hex-value
Specify start point (in hex) when generating candidate moduli for DH-GEX.
.Pp
.It Ic generator Ns = Ns Ar value
Specify desired generator (in decimal) when testing candidate moduli for DH-GEX.
.El
.Sh CERTIFICATES
.Nm
supports signing of keys to produce certificates that may be used for

View File

@ -1,4 +1,4 @@
/* $OpenBSD: ssh-keygen.c,v 1.375 2019/12/30 03:28:41 djm Exp $ */
/* $OpenBSD: ssh-keygen.c,v 1.376 2019/12/30 03:30:09 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@ -162,10 +162,7 @@ static int private_key_format = SSHKEY_PRIVATE_OPENSSH;
/* Cipher for new-format private keys */
static char *openssh_format_cipher = NULL;
/*
* Number of KDF rounds to derive new format keys /
* number of primality trials when screening moduli.
*/
/* Number of KDF rounds to derive new format keys. */
static int rounds = 0;
/* argv0 */
@ -2758,6 +2755,122 @@ done:
return ret;
}
static void
do_moduli_gen(const char *out_file, char **opts, size_t nopts)
{
#ifdef WITH_OPENSSL
/* Moduli generation/screening */
u_int32_t memory = 0;
BIGNUM *start = NULL;
int moduli_bits = 0;
FILE *out;
size_t i;
const char *errstr;
/* Parse options */
for (i = 0; i < nopts; i++) {
if (strncmp(opts[i], "memory=", 7) == 0) {
memory = (u_int32_t)strtonum(opts[i]+7, 1,
UINT_MAX, &errstr);
if (errstr) {
fatal("Memory limit is %s: %s",
errstr, opts[i]+7);
}
} else if (strncmp(opts[i], "start=", 6) == 0) {
/* XXX - also compare length against bits */
if (BN_hex2bn(&start, opts[i]+6) == 0)
fatal("Invalid start point.");
} else if (strncmp(opts[i], "bits=", 5) == 0) {
moduli_bits = (int)strtonum(opts[i]+5, 1,
INT_MAX, &errstr);
if (errstr) {
fatal("Invalid number: %s (%s)",
opts[i]+12, errstr);
}
} else {
fatal("Option \"%s\" is unsupported for moduli "
"generation", opts[i]);
}
}
if ((out = fopen(out_file, "w")) == NULL) {
fatal("Couldn't open modulus candidate file \"%s\": %s",
out_file, strerror(errno));
}
setvbuf(out, NULL, _IOLBF, 0);
if (moduli_bits == 0)
moduli_bits = DEFAULT_BITS;
if (gen_candidates(out, memory, moduli_bits, start) != 0)
fatal("modulus candidate generation failed");
#else /* WITH_OPENSSL */
fatal("Moduli generation is not supported");
#endif /* WITH_OPENSSL */
}
static void
do_moduli_screen(const char *out_file, char **opts, size_t nopts)
{
#ifdef WITH_OPENSSL
/* Moduli generation/screening */
char *checkpoint = NULL;
u_int32_t generator_wanted = 0;
unsigned long start_lineno = 0, lines_to_process = 0;
int prime_tests = 0;
FILE *out, *in = stdin;
size_t i;
const char *errstr;
/* Parse options */
for (i = 0; i < nopts; i++) {
if (strncmp(opts[i], "lines=", 6) == 0) {
lines_to_process = strtoul(opts[i]+6, NULL, 10);
} else if (strncmp(opts[i], "start-line=", 11) == 0) {
start_lineno = strtoul(opts[i]+11, NULL, 10);
} else if (strncmp(opts[i], "checkpoint=", 11) == 0) {
checkpoint = xstrdup(opts[i]+11);
} else if (strncmp(opts[i], "generator=", 10) == 0) {
generator_wanted = (u_int32_t)strtonum(
opts[i]+10, 1, UINT_MAX, &errstr);
if (errstr != NULL) {
fatal("Generator invalid: %s (%s)",
opts[i]+10, errstr);
}
} else if (strncmp(opts[i], "prime-tests=", 12) == 0) {
prime_tests = (int)strtonum(opts[i]+12, 1,
INT_MAX, &errstr);
if (errstr) {
fatal("Invalid number: %s (%s)",
opts[i]+12, errstr);
}
} else {
fatal("Option \"%s\" is unsupported for moduli "
"screening", opts[i]);
}
}
if (have_identity && strcmp(identity_file, "-") != 0) {
if ((in = fopen(identity_file, "r")) == NULL) {
fatal("Couldn't open modulus candidate "
"file \"%s\": %s", identity_file,
strerror(errno));
}
}
if ((out = fopen(out_file, "a")) == NULL) {
fatal("Couldn't open moduli file \"%s\": %s",
out_file, strerror(errno));
}
setvbuf(out, NULL, _IOLBF, 0);
if (prime_test(in, out, prime_tests == 0 ? 100 : prime_tests,
generator_wanted, checkpoint,
start_lineno, lines_to_process) != 0)
fatal("modulus screening failed");
#else /* WITH_OPENSSL */
fatal("Moduli screening is not supported");
#endif /* WITH_OPENSSL */
}
static void
usage(void)
{
@ -2783,9 +2896,8 @@ usage(void)
" ssh-keygen -R hostname [-f known_hosts_file]\n"
" ssh-keygen -r hostname [-g] [-f input_keyfile]\n"
#ifdef WITH_OPENSSL
" ssh-keygen -G output_file [-v] [-b bits] [-M memory] [-S start_point]\n"
" ssh-keygen -f input_file -T output_file [-v] [-a rounds] [-J num_lines]\n"
" [-j start_line] [-K checkpt] [-W generator]\n"
" ssh-keygen -M generate [-O option] output\n"
" ssh-keygen -M screen [-f input_file] [-O option] [-a rounds] output_file\n"
#endif
" ssh-keygen -I certificate_identity -s ca_key [-hU] [-D pkcs11_provider]\n"
" [-n principals] [-O option] [-V validity_interval]\n"
@ -2819,6 +2931,7 @@ main(int argc, char **argv)
int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0;
int prefer_agent = 0, convert_to = 0, convert_from = 0;
int print_public = 0, print_generic = 0, cert_serial_autoinc = 0;
int do_gen_candidates = 0, do_screen_candidates = 0;
unsigned long long ull, cert_serial = 0;
char *identity_comment = NULL, *ca_key_path = NULL, **opts = NULL;
size_t i, nopts = 0;
@ -2828,14 +2941,6 @@ main(int argc, char **argv)
const char *errstr;
int log_level = SYSLOG_LEVEL_INFO;
char *sign_op = NULL;
#ifdef WITH_OPENSSL
/* Moduli generation/screening */
char out_file[PATH_MAX], *checkpoint = NULL;
u_int32_t memory = 0, generator_wanted = 0;
int do_gen_candidates = 0, do_screen_candidates = 0;
unsigned long start_lineno = 0, lines_to_process = 0;
BIGNUM *start = NULL;
#endif
extern int optind;
extern char *optarg;
@ -2860,10 +2965,10 @@ main(int argc, char **argv)
sk_provider = getenv("SSH_SK_PROVIDER");
/* Remaining character: d */
/* Remaining characters: dGjJKSTW */
while ((opt = getopt(argc, argv, "ABHLQUXceghiklopquvy"
"C:D:E:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Y:Z:"
"a:b:f:g:j:m:n:r:s:t:w:x:z:")) != -1) {
"C:D:E:F:I:M:N:O:P:R:V:Y:Z:"
"a:b:f:g:m:n:r:s:t:w:x:z:")) != -1) {
switch (opt) {
case 'A':
gen_all_hostkeys = 1;
@ -3053,50 +3158,14 @@ main(int argc, char **argv)
(errno == ERANGE && cert_serial == ULLONG_MAX))
fatal("Invalid serial number \"%s\"", optarg);
break;
#ifdef WITH_OPENSSL
/* Moduli generation/screening */
case 'G':
do_gen_candidates = 1;
if (strlcpy(out_file, optarg, sizeof(out_file)) >=
sizeof(out_file))
fatal("Output filename too long");
break;
case 'J':
lines_to_process = strtoul(optarg, NULL, 10);
break;
case 'j':
start_lineno = strtoul(optarg, NULL, 10);
break;
case 'K':
if (strlen(optarg) >= PATH_MAX)
fatal("Checkpoint filename too long");
checkpoint = xstrdup(optarg);
break;
case 'M':
memory = (u_int32_t)strtonum(optarg, 1, UINT_MAX,
&errstr);
if (errstr)
fatal("Memory limit is %s: %s", errstr, optarg);
if (strcmp(optarg, "generate") == 0)
do_gen_candidates = 1;
else if (strcmp(optarg, "screen") == 0)
do_screen_candidates = 1;
else
fatal("Unsupported moduli option %s", optarg);
break;
case 'S':
/* XXX - also compare length against bits */
if (BN_hex2bn(&start, optarg) == 0)
fatal("Invalid start point.");
break;
case 'T':
do_screen_candidates = 1;
if (strlcpy(out_file, optarg, sizeof(out_file)) >=
sizeof(out_file))
fatal("Output filename too long");
break;
case 'W':
generator_wanted = (u_int32_t)strtonum(optarg, 1,
UINT_MAX, &errstr);
if (errstr != NULL)
fatal("Desired generator invalid: %s (%s)",
optarg, errstr);
break;
#endif /* WITH_OPENSSL */
case '?':
default:
usage();
@ -3163,7 +3232,8 @@ main(int argc, char **argv)
error("Too few arguments.");
usage();
}
} else if (argc > 0 && !gen_krl && !check_krl) {
} else if (argc > 0 && !gen_krl && !check_krl &&
!do_gen_candidates && !do_screen_candidates) {
error("Too many arguments.");
usage();
}
@ -3176,13 +3246,21 @@ main(int argc, char **argv)
usage();
}
if (gen_krl) {
#ifdef WITH_OPENSSL
do_gen_krl(pw, update_krl, ca_key_path,
cert_serial, identity_comment, argc, argv);
return (0);
#else
fatal("KRL generation not supported");
#endif
}
if (check_krl) {
#ifdef WITH_OPENSSL
do_check_krl(pw, argc, argv);
return (0);
#else
fatal("KRL checking not supported");
#endif
}
if (ca_key_path != NULL) {
if (cert_key_id == NULL)
@ -3249,47 +3327,20 @@ main(int argc, char **argv)
}
}
#ifdef WITH_OPENSSL
if (do_gen_candidates || do_screen_candidates) {
if (argc <= 0)
fatal("No output file specified");
else if (argc > 1)
fatal("Too many output files specified");
}
if (do_gen_candidates) {
FILE *out = fopen(out_file, "w");
if (out == NULL) {
error("Couldn't open modulus candidate file \"%s\": %s",
out_file, strerror(errno));
return (1);
}
if (bits == 0)
bits = DEFAULT_BITS;
if (gen_candidates(out, memory, bits, start) != 0)
fatal("modulus candidate generation failed");
return (0);
do_moduli_gen(argv[0], opts, nopts);
return 0;
}
if (do_screen_candidates) {
FILE *in;
FILE *out = fopen(out_file, "a");
if (have_identity && strcmp(identity_file, "-") != 0) {
if ((in = fopen(identity_file, "r")) == NULL) {
fatal("Couldn't open modulus candidate "
"file \"%s\": %s", identity_file,
strerror(errno));
}
} else
in = stdin;
if (out == NULL) {
fatal("Couldn't open moduli file \"%s\": %s",
out_file, strerror(errno));
}
if (prime_test(in, out, rounds == 0 ? 100 : rounds,
generator_wanted, checkpoint,
start_lineno, lines_to_process) != 0)
fatal("modulus screening failed");
return (0);
do_moduli_screen(argv[0], opts, nopts);
return 0;
}
#endif
if (gen_all_hostkeys) {
do_gen_all_hostkeys(pw);