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