1 /* 2 * Copyright 1995-2023 The OpenSSL Project Authors. All Rights Reserved. 3 * 4 * Licensed under the Apache License 2.0 (the "License"). You may not use 5 * this file except in compliance with the License. You can obtain a copy 6 * in the file LICENSE in the source distribution or at 7 * https://www.openssl.org/source/license.html 8 */ 9 10 #include <openssl/opensslconf.h> 11 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <time.h> 15 #include <string.h> 16 #include "apps.h" 17 #include "progs.h" 18 #include <openssl/bio.h> 19 #include <openssl/err.h> 20 #include <openssl/bn.h> 21 #include <openssl/dsa.h> 22 #include <openssl/dh.h> 23 #include <openssl/x509.h> 24 #include <openssl/pem.h> 25 #include <openssl/core_names.h> 26 #include <openssl/core_dispatch.h> 27 #include <openssl/param_build.h> 28 #include <openssl/encoder.h> 29 #include <openssl/decoder.h> 30 31 #define DEFBITS 2048 32 33 static EVP_PKEY *dsa_to_dh(EVP_PKEY *dh); 34 35 static int verbose = 1; 36 37 typedef enum OPTION_choice { 38 OPT_COMMON, 39 OPT_INFORM, OPT_OUTFORM, OPT_IN, OPT_OUT, 40 OPT_ENGINE, OPT_CHECK, OPT_TEXT, OPT_NOOUT, 41 OPT_DSAPARAM, OPT_2, OPT_3, OPT_5, OPT_VERBOSE, OPT_QUIET, 42 OPT_R_ENUM, OPT_PROV_ENUM 43 } OPTION_CHOICE; 44 45 const OPTIONS dhparam_options[] = { 46 {OPT_HELP_STR, 1, '-', "Usage: %s [options] [numbits]\n"}, 47 48 OPT_SECTION("General"), 49 {"help", OPT_HELP, '-', "Display this summary"}, 50 {"check", OPT_CHECK, '-', "Check the DH parameters"}, 51 #if !defined(OPENSSL_NO_DSA) || !defined(OPENSSL_NO_DEPRECATED_3_0) 52 {"dsaparam", OPT_DSAPARAM, '-', 53 "Read or generate DSA parameters, convert to DH"}, 54 #endif 55 #ifndef OPENSSL_NO_ENGINE 56 {"engine", OPT_ENGINE, 's', "Use engine e, possibly a hardware device"}, 57 #endif 58 59 OPT_SECTION("Input"), 60 {"in", OPT_IN, '<', "Input file"}, 61 {"inform", OPT_INFORM, 'F', "Input format, DER or PEM"}, 62 63 OPT_SECTION("Output"), 64 {"out", OPT_OUT, '>', "Output file"}, 65 {"outform", OPT_OUTFORM, 'F', "Output format, DER or PEM"}, 66 {"text", OPT_TEXT, '-', "Print a text form of the DH parameters"}, 67 {"noout", OPT_NOOUT, '-', "Don't output any DH parameters"}, 68 {"2", OPT_2, '-', "Generate parameters using 2 as the generator value"}, 69 {"3", OPT_3, '-', "Generate parameters using 3 as the generator value"}, 70 {"5", OPT_5, '-', "Generate parameters using 5 as the generator value"}, 71 {"verbose", OPT_VERBOSE, '-', "Verbose output"}, 72 {"quiet", OPT_QUIET, '-', "Terse output"}, 73 74 OPT_R_OPTIONS, 75 OPT_PROV_OPTIONS, 76 77 OPT_PARAMETERS(), 78 {"numbits", 0, 0, "Number of bits if generating parameters (optional)"}, 79 {NULL} 80 }; 81 82 int dhparam_main(int argc, char **argv) 83 { 84 BIO *in = NULL, *out = NULL; 85 EVP_PKEY *pkey = NULL, *tmppkey = NULL; 86 EVP_PKEY_CTX *ctx = NULL; 87 char *infile = NULL, *outfile = NULL, *prog; 88 ENGINE *e = NULL; 89 int dsaparam = 0; 90 int text = 0, ret = 1, num = 0, g = 0; 91 int informat = FORMAT_PEM, outformat = FORMAT_PEM, check = 0, noout = 0; 92 OPTION_CHOICE o; 93 94 prog = opt_init(argc, argv, dhparam_options); 95 while ((o = opt_next()) != OPT_EOF) { 96 switch (o) { 97 case OPT_EOF: 98 case OPT_ERR: 99 opthelp: 100 BIO_printf(bio_err, "%s: Use -help for summary.\n", prog); 101 goto end; 102 case OPT_HELP: 103 opt_help(dhparam_options); 104 ret = 0; 105 goto end; 106 case OPT_INFORM: 107 if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &informat)) 108 goto opthelp; 109 break; 110 case OPT_OUTFORM: 111 if (!opt_format(opt_arg(), OPT_FMT_PEMDER, &outformat)) 112 goto opthelp; 113 break; 114 case OPT_IN: 115 infile = opt_arg(); 116 break; 117 case OPT_OUT: 118 outfile = opt_arg(); 119 break; 120 case OPT_ENGINE: 121 e = setup_engine(opt_arg(), 0); 122 break; 123 case OPT_CHECK: 124 check = 1; 125 break; 126 case OPT_TEXT: 127 text = 1; 128 break; 129 case OPT_DSAPARAM: 130 dsaparam = 1; 131 break; 132 case OPT_2: 133 g = 2; 134 break; 135 case OPT_3: 136 g = 3; 137 break; 138 case OPT_5: 139 g = 5; 140 break; 141 case OPT_NOOUT: 142 noout = 1; 143 break; 144 case OPT_VERBOSE: 145 verbose = 1; 146 break; 147 case OPT_QUIET: 148 verbose = 0; 149 break; 150 case OPT_R_CASES: 151 if (!opt_rand(o)) 152 goto end; 153 break; 154 case OPT_PROV_CASES: 155 if (!opt_provider(o)) 156 goto end; 157 break; 158 } 159 } 160 161 /* One optional argument, bitsize to generate. */ 162 argc = opt_num_rest(); 163 argv = opt_rest(); 164 if (argc == 1) { 165 if (!opt_int(argv[0], &num) || num <= 0) 166 goto opthelp; 167 } else if (!opt_check_rest_arg(NULL)) { 168 goto opthelp; 169 } 170 if (!app_RAND_load()) 171 goto end; 172 173 if (g && !num) 174 num = DEFBITS; 175 176 if (dsaparam && g) { 177 BIO_printf(bio_err, 178 "Error, generator may not be chosen for DSA parameters\n"); 179 goto end; 180 } 181 182 /* DH parameters */ 183 if (num && !g) 184 g = 2; 185 186 if (num) { 187 const char *alg = dsaparam ? "DSA" : "DH"; 188 189 if (infile != NULL) { 190 BIO_printf(bio_err, "Warning, input file %s ignored\n", infile); 191 } 192 193 ctx = EVP_PKEY_CTX_new_from_name(app_get0_libctx(), alg, app_get0_propq()); 194 if (ctx == NULL) { 195 BIO_printf(bio_err, 196 "Error, %s param generation context allocation failed\n", 197 alg); 198 goto end; 199 } 200 EVP_PKEY_CTX_set_app_data(ctx, bio_err); 201 if (verbose) { 202 EVP_PKEY_CTX_set_cb(ctx, progress_cb); 203 BIO_printf(bio_err, 204 "Generating %s parameters, %d bit long %sprime\n", 205 alg, num, dsaparam ? "" : "safe "); 206 } 207 208 if (EVP_PKEY_paramgen_init(ctx) <= 0) { 209 BIO_printf(bio_err, 210 "Error, unable to initialise %s parameters\n", 211 alg); 212 goto end; 213 } 214 215 if (dsaparam) { 216 if (EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx, num) <= 0) { 217 BIO_printf(bio_err, "Error, unable to set DSA prime length\n"); 218 goto end; 219 } 220 } else { 221 if (EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx, num) <= 0) { 222 BIO_printf(bio_err, "Error, unable to set DH prime length\n"); 223 goto end; 224 } 225 if (EVP_PKEY_CTX_set_dh_paramgen_generator(ctx, g) <= 0) { 226 BIO_printf(bio_err, "Error, unable to set generator\n"); 227 goto end; 228 } 229 } 230 231 tmppkey = app_paramgen(ctx, alg); 232 if (tmppkey == NULL) 233 goto end; 234 EVP_PKEY_CTX_free(ctx); 235 ctx = NULL; 236 if (dsaparam) { 237 pkey = dsa_to_dh(tmppkey); 238 if (pkey == NULL) 239 goto end; 240 EVP_PKEY_free(tmppkey); 241 } else { 242 pkey = tmppkey; 243 } 244 tmppkey = NULL; 245 } else { 246 OSSL_DECODER_CTX *decoderctx = NULL; 247 const char *keytype = "DH"; 248 int done; 249 250 in = bio_open_default(infile, 'r', informat); 251 if (in == NULL) 252 goto end; 253 254 do { 255 /* 256 * We assume we're done unless we explicitly want to retry and set 257 * this to 0 below. 258 */ 259 done = 1; 260 /* 261 * We set NULL for the keytype to allow any key type. We don't know 262 * if we're going to get DH or DHX (or DSA in the event of dsaparam). 263 * We check that we got one of those key types afterwards. 264 */ 265 decoderctx 266 = OSSL_DECODER_CTX_new_for_pkey(&tmppkey, 267 (informat == FORMAT_ASN1) 268 ? "DER" : "PEM", 269 NULL, 270 (informat == FORMAT_ASN1) 271 ? keytype : NULL, 272 OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, 273 NULL, NULL); 274 275 if (decoderctx != NULL 276 && !OSSL_DECODER_from_bio(decoderctx, in) 277 && informat == FORMAT_ASN1 278 && strcmp(keytype, "DH") == 0) { 279 /* 280 * When reading DER we explicitly state the expected keytype 281 * because, unlike PEM, there is no header to declare what 282 * the contents of the DER file are. The decoders just try 283 * and guess. Unfortunately with DHX key types they may guess 284 * wrong and think we have a DSA keytype. Therefore, we try 285 * both DH and DHX sequentially. 286 */ 287 keytype = "DHX"; 288 /* 289 * BIO_reset() returns 0 for success for file BIOs only!!! 290 * This won't work for stdin (and never has done) 291 */ 292 if (BIO_reset(in) == 0) 293 done = 0; 294 } 295 OSSL_DECODER_CTX_free(decoderctx); 296 } while (!done); 297 if (tmppkey == NULL) { 298 BIO_printf(bio_err, "Error, unable to load parameters\n"); 299 goto end; 300 } 301 302 if (dsaparam) { 303 if (!EVP_PKEY_is_a(tmppkey, "DSA")) { 304 BIO_printf(bio_err, "Error, unable to load DSA parameters\n"); 305 goto end; 306 } 307 pkey = dsa_to_dh(tmppkey); 308 if (pkey == NULL) 309 goto end; 310 } else { 311 if (!EVP_PKEY_is_a(tmppkey, "DH") 312 && !EVP_PKEY_is_a(tmppkey, "DHX")) { 313 BIO_printf(bio_err, "Error, unable to load DH parameters\n"); 314 goto end; 315 } 316 pkey = tmppkey; 317 tmppkey = NULL; 318 } 319 } 320 321 out = bio_open_default(outfile, 'w', outformat); 322 if (out == NULL) 323 goto end; 324 325 if (text) 326 EVP_PKEY_print_params(out, pkey, 4, NULL); 327 328 if (check) { 329 ctx = EVP_PKEY_CTX_new_from_pkey(app_get0_libctx(), pkey, app_get0_propq()); 330 if (ctx == NULL) { 331 BIO_printf(bio_err, "Error, failed to check DH parameters\n"); 332 goto end; 333 } 334 if (EVP_PKEY_param_check(ctx) <= 0) { 335 BIO_printf(bio_err, "Error, invalid parameters generated\n"); 336 goto end; 337 } 338 BIO_printf(bio_err, "DH parameters appear to be ok.\n"); 339 } 340 341 if (!noout) { 342 OSSL_ENCODER_CTX *ectx = 343 OSSL_ENCODER_CTX_new_for_pkey(pkey, 344 OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, 345 outformat == FORMAT_ASN1 346 ? "DER" : "PEM", 347 NULL, NULL); 348 349 if (ectx == NULL || !OSSL_ENCODER_to_bio(ectx, out)) { 350 OSSL_ENCODER_CTX_free(ectx); 351 BIO_printf(bio_err, "Error, unable to write DH parameters\n"); 352 goto end; 353 } 354 OSSL_ENCODER_CTX_free(ectx); 355 } 356 ret = 0; 357 end: 358 if (ret != 0) 359 ERR_print_errors(bio_err); 360 BIO_free(in); 361 BIO_free_all(out); 362 EVP_PKEY_free(pkey); 363 EVP_PKEY_free(tmppkey); 364 EVP_PKEY_CTX_free(ctx); 365 release_engine(e); 366 return ret; 367 } 368 369 /* 370 * Historically we had the low-level call DSA_dup_DH() to do this. 371 * That is now deprecated with no replacement. Since we still need to do this 372 * for backwards compatibility reasons, we do it "manually". 373 */ 374 static EVP_PKEY *dsa_to_dh(EVP_PKEY *dh) 375 { 376 OSSL_PARAM_BLD *tmpl = NULL; 377 OSSL_PARAM *params = NULL; 378 BIGNUM *bn_p = NULL, *bn_q = NULL, *bn_g = NULL; 379 EVP_PKEY_CTX *ctx = NULL; 380 EVP_PKEY *pkey = NULL; 381 382 if (!EVP_PKEY_get_bn_param(dh, OSSL_PKEY_PARAM_FFC_P, &bn_p) 383 || !EVP_PKEY_get_bn_param(dh, OSSL_PKEY_PARAM_FFC_Q, &bn_q) 384 || !EVP_PKEY_get_bn_param(dh, OSSL_PKEY_PARAM_FFC_G, &bn_g)) { 385 BIO_printf(bio_err, "Error, failed to set DH parameters\n"); 386 goto err; 387 } 388 389 if ((tmpl = OSSL_PARAM_BLD_new()) == NULL 390 || !OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_FFC_P, 391 bn_p) 392 || !OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_FFC_Q, 393 bn_q) 394 || !OSSL_PARAM_BLD_push_BN(tmpl, OSSL_PKEY_PARAM_FFC_G, 395 bn_g) 396 || (params = OSSL_PARAM_BLD_to_param(tmpl)) == NULL) { 397 BIO_printf(bio_err, "Error, failed to set DH parameters\n"); 398 goto err; 399 } 400 401 ctx = EVP_PKEY_CTX_new_from_name(app_get0_libctx(), "DHX", app_get0_propq()); 402 if (ctx == NULL 403 || EVP_PKEY_fromdata_init(ctx) <= 0 404 || EVP_PKEY_fromdata(ctx, &pkey, EVP_PKEY_KEY_PARAMETERS, params) <= 0) { 405 BIO_printf(bio_err, "Error, failed to set DH parameters\n"); 406 goto err; 407 } 408 409 err: 410 EVP_PKEY_CTX_free(ctx); 411 OSSL_PARAM_free(params); 412 OSSL_PARAM_BLD_free(tmpl); 413 BN_free(bn_p); 414 BN_free(bn_q); 415 BN_free(bn_g); 416 return pkey; 417 } 418 419