From ebb70e0c03ee499ca47bb591d9e7dcc7f6c2a670 Mon Sep 17 00:00:00 2001 From: Damien Miller Date: Mon, 30 Dec 2019 03:30:10 +0000 Subject: 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@ --- usr.bin/ssh/ssh-keygen.1 | 142 +++++++++++++++------------ usr.bin/ssh/ssh-keygen.c | 251 ++++++++++++++++++++++++++++------------------- 2 files changed, 228 insertions(+), 165 deletions(-) (limited to 'usr.bin') diff --git a/usr.bin/ssh/ssh-keygen.1 b/usr.bin/ssh/ssh-keygen.1 index 67a57b9f7da..9afb9294378 100644 --- a/usr.bin/ssh/ssh-keygen.1 +++ b/usr.bin/ssh/ssh-keygen.1 @@ -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 .\" Copyright (c) 1995 Tatu Ylonen , 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 diff --git a/usr.bin/ssh/ssh-keygen.c b/usr.bin/ssh/ssh-keygen.c index f9d2b41c452..34c65121d5a 100644 --- a/usr.bin/ssh/ssh-keygen.c +++ b/usr.bin/ssh/ssh-keygen.c @@ -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 * Copyright (c) 1994 Tatu Ylonen , Espoo, Finland @@ -156,10 +156,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 */ @@ -2740,6 +2737,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) { @@ -2765,9 +2878,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" @@ -2801,6 +2913,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; @@ -2810,14 +2923,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; @@ -2841,10 +2946,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; @@ -3034,50 +3139,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); - 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); + 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; -#endif /* WITH_OPENSSL */ case '?': default: usage(); @@ -3142,7 +3211,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(); } @@ -3154,17 +3224,23 @@ main(int argc, char **argv) error("Cannot use -l with -H or -R."); usage(); } -#ifdef WITH_OPENSSL 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) fatal("Must specify key id (-I) when certifying"); @@ -3230,47 +3306,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); -- cgit v1.2.3