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