2 * Copyright 1995-2021 The OpenSSL Project Authors. All Rights Reserved.
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
10 #include <openssl/opensslconf.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>
33 static EVP_PKEY
*dsa_to_dh(EVP_PKEY
*dh
);
34 static int gendh_cb(EVP_PKEY_CTX
*ctx
);
36 typedef enum OPTION_choice
{
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
44 const OPTIONS dhparam_options
[] = {
45 {OPT_HELP_STR
, 1, '-', "Usage: %s [options] [numbits]\n"},
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"},
54 #ifndef OPENSSL_NO_ENGINE
55 {"engine", OPT_ENGINE
, 's', "Use engine e, possibly a hardware device"},
59 {"in", OPT_IN
, '<', "Input file"},
60 {"inform", OPT_INFORM
, 'F', "Input format, DER or PEM"},
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"},
75 {"numbits", 0, 0, "Number of bits if generating parameters (optional)"},
79 int dhparam_main(int argc
, char **argv
)
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
;
87 int text
= 0, ret
= 1, num
= 0, g
= 0;
88 int informat
= FORMAT_PEM
, outformat
= FORMAT_PEM
, check
= 0, noout
= 0;
91 prog
= opt_init(argc
, argv
, dhparam_options
);
92 while ((o
= opt_next()) != OPT_EOF
) {
97 BIO_printf(bio_err
, "%s: Use -help for summary.\n", prog
);
100 opt_help(dhparam_options
);
104 if (!opt_format(opt_arg(), OPT_FMT_PEMDER
, &informat
))
108 if (!opt_format(opt_arg(), OPT_FMT_PEMDER
, &outformat
))
118 e
= setup_engine(opt_arg(), 0);
146 if (!opt_provider(o
))
152 /* One optional argument, bitsize to generate. */
153 argc
= opt_num_rest();
156 if (!opt_int(argv
[0], &num
) || num
<= 0)
158 } else if (argc
!= 0) {
161 if (!app_RAND_load())
169 "Error, generator may not be chosen for DSA parameters\n");
173 out
= bio_open_default(outfile
, 'w', outformat
);
182 const char *alg
= dsaparam
? "DSA" : "DH";
184 ctx
= EVP_PKEY_CTX_new_from_name(NULL
, alg
, NULL
);
187 "Error, %s param generation context allocation failed\n",
191 EVP_PKEY_CTX_set_cb(ctx
, gendh_cb
);
192 EVP_PKEY_CTX_set_app_data(ctx
, bio_err
);
194 "Generating %s parameters, %d bit long %sprime\n",
195 alg
, num
, dsaparam
? "" : "safe ");
197 if (EVP_PKEY_paramgen_init(ctx
) <= 0) {
199 "Error, unable to initialise %s parameters\n",
205 if (!EVP_PKEY_CTX_set_dsa_paramgen_bits(ctx
, num
)) {
206 BIO_printf(bio_err
, "Error, unable to set DSA prime length\n");
210 if (!EVP_PKEY_CTX_set_dh_paramgen_prime_len(ctx
, num
)) {
211 BIO_printf(bio_err
, "Error, unable to set DH prime length\n");
214 if (!EVP_PKEY_CTX_set_dh_paramgen_generator(ctx
, g
)) {
215 BIO_printf(bio_err
, "Error, unable to set generator\n");
220 tmppkey
= app_paramgen(ctx
, alg
);
221 EVP_PKEY_CTX_free(ctx
);
224 pkey
= dsa_to_dh(tmppkey
);
227 EVP_PKEY_free(tmppkey
);
233 OSSL_DECODER_CTX
*decoderctx
= NULL
;
234 const char *keytype
= "DH";
237 in
= bio_open_default(infile
, 'r', informat
);
243 * We assume we're done unless we explicitly want to retry and set
248 * We set NULL for the keytype to allow any key type. We don't know
249 * if we're going to get DH or DHX (or DSA in the event of dsaparam).
250 * We check that we got one of those key types afterwards.
253 = OSSL_DECODER_CTX_new_for_pkey(&tmppkey
,
254 (informat
== FORMAT_ASN1
)
257 (informat
== FORMAT_ASN1
)
259 OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS
,
262 if (decoderctx
!= NULL
263 && !OSSL_DECODER_from_bio(decoderctx
, in
)
264 && informat
== FORMAT_ASN1
265 && strcmp(keytype
, "DH") == 0) {
267 * When reading DER we explicitly state the expected keytype
268 * because, unlike PEM, there is no header to declare what
269 * the contents of the DER file are. The decoders just try
270 * and guess. Unfortunately with DHX key types they may guess
271 * wrong and think we have a DSA keytype. Therefore we try
272 * both DH and DHX sequentially.
276 * BIO_reset() returns 0 for success for file BIOs only!!!
277 * This won't work for stdin (and never has done)
279 if (BIO_reset(in
) == 0)
282 OSSL_DECODER_CTX_free(decoderctx
);
284 if (tmppkey
== NULL
) {
285 BIO_printf(bio_err
, "Error, unable to load parameters\n");
290 if (!EVP_PKEY_is_a(tmppkey
, "DSA")) {
291 BIO_printf(bio_err
, "Error, unable to load DSA parameters\n");
294 pkey
= dsa_to_dh(tmppkey
);
298 if (!EVP_PKEY_is_a(tmppkey
, "DH")
299 && !EVP_PKEY_is_a(tmppkey
, "DHX")) {
300 BIO_printf(bio_err
, "Error, unable to load DH parameters\n");
309 EVP_PKEY_print_params(out
, pkey
, 4, NULL
);
312 ctx
= EVP_PKEY_CTX_new_from_pkey(NULL
, pkey
, NULL
);
314 BIO_printf(bio_err
, "Error, failed to check DH parameters\n");
317 if (!EVP_PKEY_param_check(ctx
)) {
318 BIO_printf(bio_err
, "Error, invalid parameters generated\n");
321 BIO_printf(bio_err
, "DH parameters appear to be ok.\n");
325 OSSL_ENCODER_CTX
*ectx
=
326 OSSL_ENCODER_CTX_new_for_pkey(pkey
,
327 OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS
,
328 outformat
== FORMAT_ASN1
332 if (ectx
== NULL
|| !OSSL_ENCODER_to_bio(ectx
, out
)) {
333 OSSL_ENCODER_CTX_free(ectx
);
334 BIO_printf(bio_err
, "Error, unable to write DH parameters\n");
337 OSSL_ENCODER_CTX_free(ectx
);
342 ERR_print_errors(bio_err
);
346 EVP_PKEY_free(tmppkey
);
347 EVP_PKEY_CTX_free(ctx
);
353 * Historically we had the low level call DSA_dup_DH() to do this.
354 * That is now deprecated with no replacement. Since we still need to do this
355 * for backwards compatibility reasons, we do it "manually".
357 static EVP_PKEY
*dsa_to_dh(EVP_PKEY
*dh
)
359 OSSL_PARAM_BLD
*tmpl
= NULL
;
360 OSSL_PARAM
*params
= NULL
;
361 BIGNUM
*bn_p
= NULL
, *bn_q
= NULL
, *bn_g
= NULL
;
362 EVP_PKEY_CTX
*ctx
= NULL
;
363 EVP_PKEY
*pkey
= NULL
;
365 if (!EVP_PKEY_get_bn_param(dh
, OSSL_PKEY_PARAM_FFC_P
, &bn_p
)
366 || !EVP_PKEY_get_bn_param(dh
, OSSL_PKEY_PARAM_FFC_Q
, &bn_q
)
367 || !EVP_PKEY_get_bn_param(dh
, OSSL_PKEY_PARAM_FFC_G
, &bn_g
)) {
368 BIO_printf(bio_err
, "Error, failed to set DH parameters\n");
372 if ((tmpl
= OSSL_PARAM_BLD_new()) == NULL
373 || !OSSL_PARAM_BLD_push_BN(tmpl
, OSSL_PKEY_PARAM_FFC_P
,
375 || !OSSL_PARAM_BLD_push_BN(tmpl
, OSSL_PKEY_PARAM_FFC_Q
,
377 || !OSSL_PARAM_BLD_push_BN(tmpl
, OSSL_PKEY_PARAM_FFC_G
,
379 || (params
= OSSL_PARAM_BLD_to_param(tmpl
)) == NULL
) {
380 BIO_printf(bio_err
, "Error, failed to set DH parameters\n");
384 ctx
= EVP_PKEY_CTX_new_from_name(NULL
, "DHX", NULL
);
386 || EVP_PKEY_fromdata_init(ctx
) <= 0
387 || EVP_PKEY_fromdata(ctx
, &pkey
, EVP_PKEY_KEY_PARAMETERS
, params
) <= 0) {
388 BIO_printf(bio_err
, "Error, failed to set DH parameters\n");
393 EVP_PKEY_CTX_free(ctx
);
394 OSSL_PARAM_free(params
);
395 OSSL_PARAM_BLD_free(tmpl
);
402 static int gendh_cb(EVP_PKEY_CTX
*ctx
)
404 int p
= EVP_PKEY_CTX_get_keygen_info(ctx
, 0);
405 BIO
*b
= EVP_PKEY_CTX_get_app_data(ctx
);
406 static const char symbols
[] = ".+*\n";
407 char c
= (p
>= 0 && (size_t)p
< sizeof(symbols
) - 1) ? symbols
[p
] : '?';