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
dhparam_main(int argc,char ** argv)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 */
dsa_to_dh(EVP_PKEY * dh)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