1/* $OpenBSD: dhparam.c,v 1.18 2023/07/23 11:39:29 tb Exp $ */
2/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
3 * All rights reserved.
4 *
5 * This package is an SSL implementation written
6 * by Eric Young (eay@cryptsoft.com).
7 * The implementation was written so as to conform with Netscapes SSL.
8 *
9 * This library is free for commercial and non-commercial use as long as
10 * the following conditions are aheared to.  The following conditions
11 * apply to all code found in this distribution, be it the RC4, RSA,
12 * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
13 * included with this distribution is covered by the same copyright terms
14 * except that the holder is Tim Hudson (tjh@cryptsoft.com).
15 *
16 * Copyright remains Eric Young's, and as such any Copyright notices in
17 * the code are not to be removed.
18 * If this package is used in a product, Eric Young should be given attribution
19 * as the author of the parts of the library used.
20 * This can be in the form of a textual message at program startup or
21 * in documentation (online or textual) provided with the package.
22 *
23 * Redistribution and use in source and binary forms, with or without
24 * modification, are permitted provided that the following conditions
25 * are met:
26 * 1. Redistributions of source code must retain the copyright
27 *    notice, this list of conditions and the following disclaimer.
28 * 2. Redistributions in binary form must reproduce the above copyright
29 *    notice, this list of conditions and the following disclaimer in the
30 *    documentation and/or other materials provided with the distribution.
31 * 3. All advertising materials mentioning features or use of this software
32 *    must display the following acknowledgement:
33 *    "This product includes cryptographic software written by
34 *     Eric Young (eay@cryptsoft.com)"
35 *    The word 'cryptographic' can be left out if the rouines from the library
36 *    being used are not cryptographic related :-).
37 * 4. If you include any Windows specific code (or a derivative thereof) from
38 *    the apps directory (application code) you must include an acknowledgement:
39 *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
40 *
41 * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
42 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
43 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
44 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
45 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
46 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
47 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
48 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
49 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
50 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51 * SUCH DAMAGE.
52 *
53 * The licence and distribution terms for any publically available version or
54 * derivative of this code cannot be changed.  i.e. this code cannot simply be
55 * copied and put under another distribution licence
56 * [including the GNU Public Licence.]
57 */
58/* ====================================================================
59 * Copyright (c) 1998-2000 The OpenSSL Project.  All rights reserved.
60 *
61 * Redistribution and use in source and binary forms, with or without
62 * modification, are permitted provided that the following conditions
63 * are met:
64 *
65 * 1. Redistributions of source code must retain the above copyright
66 *    notice, this list of conditions and the following disclaimer.
67 *
68 * 2. Redistributions in binary form must reproduce the above copyright
69 *    notice, this list of conditions and the following disclaimer in
70 *    the documentation and/or other materials provided with the
71 *    distribution.
72 *
73 * 3. All advertising materials mentioning features or use of this
74 *    software must display the following acknowledgment:
75 *    "This product includes software developed by the OpenSSL Project
76 *    for use in the OpenSSL Toolkit. (http://www.openssl.org/)"
77 *
78 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
79 *    endorse or promote products derived from this software without
80 *    prior written permission. For written permission, please contact
81 *    openssl-core@openssl.org.
82 *
83 * 5. Products derived from this software may not be called "OpenSSL"
84 *    nor may "OpenSSL" appear in their names without prior written
85 *    permission of the OpenSSL Project.
86 *
87 * 6. Redistributions of any form whatsoever must retain the following
88 *    acknowledgment:
89 *    "This product includes software developed by the OpenSSL Project
90 *    for use in the OpenSSL Toolkit (http://www.openssl.org/)"
91 *
92 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
93 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
94 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
95 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
96 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
97 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
98 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
99 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
100 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
101 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
102 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
103 * OF THE POSSIBILITY OF SUCH DAMAGE.
104 * ====================================================================
105 *
106 * This product includes cryptographic software written by Eric Young
107 * (eay@cryptsoft.com).  This product includes software written by Tim
108 * Hudson (tjh@cryptsoft.com).
109 *
110 */
111
112#include <openssl/opensslconf.h>	/* for OPENSSL_NO_DH */
113
114#ifndef OPENSSL_NO_DH
115
116#include <stdio.h>
117#include <stdlib.h>
118#include <string.h>
119#include <time.h>
120
121#include "apps.h"
122
123#include <openssl/bio.h>
124#include <openssl/bn.h>
125#include <openssl/err.h>
126#include <openssl/dh.h>
127#include <openssl/pem.h>
128#include <openssl/x509.h>
129
130#include <openssl/dsa.h>
131
132#define DEFBITS	2048
133
134static struct {
135	int C;
136	int check;
137	int dsaparam;
138	int g;
139	char *infile;
140	int informat;
141	int noout;
142	char *outfile;
143	int outformat;
144	int text;
145} cfg;
146
147static const struct option dhparam_options[] = {
148	{
149		.name = "2",
150		.desc = "Generate DH parameters with a generator value of 2 "
151		    "(default)",
152		.type = OPTION_VALUE,
153		.opt.value = &cfg.g,
154		.value = 2,
155	},
156	{
157		.name = "5",
158		.desc = "Generate DH parameters with a generator value of 5",
159		.type = OPTION_VALUE,
160		.opt.value = &cfg.g,
161		.value = 5,
162	},
163	{
164		.name = "C",
165		.desc = "Convert DH parameters into C code",
166		.type = OPTION_FLAG,
167		.opt.flag = &cfg.C,
168	},
169	{
170		.name = "check",
171		.desc = "Check the DH parameters",
172		.type = OPTION_FLAG,
173		.opt.flag = &cfg.check,
174	},
175	{
176		.name = "dsaparam",
177		.desc = "Read or generate DSA parameters and convert to DH",
178		.type = OPTION_FLAG,
179		.opt.flag = &cfg.dsaparam,
180	},
181	{
182		.name = "in",
183		.argname = "file",
184		.desc = "Input file (default stdin)",
185		.type = OPTION_ARG,
186		.opt.arg = &cfg.infile,
187	},
188	{
189		.name = "inform",
190		.argname = "format",
191		.desc = "Input format (DER or PEM (default))",
192		.type = OPTION_ARG_FORMAT,
193		.opt.value = &cfg.informat,
194	},
195	{
196		.name = "noout",
197		.desc = "Do not output encoded version of DH parameters",
198		.type = OPTION_FLAG,
199		.opt.flag = &cfg.noout,
200	},
201	{
202		.name = "out",
203		.argname = "file",
204		.desc = "Output file (default stdout)",
205		.type = OPTION_ARG,
206		.opt.arg = &cfg.outfile,
207	},
208	{
209		.name = "outform",
210		.argname = "format",
211		.desc = "Output format (DER or PEM (default))",
212		.type = OPTION_ARG_FORMAT,
213		.opt.value = &cfg.outformat,
214	},
215	{
216		.name = "text",
217		.desc = "Print DH parameters in plain text",
218		.type = OPTION_FLAG,
219		.opt.flag = &cfg.text,
220	},
221	{ NULL },
222};
223
224static void
225dhparam_usage(void)
226{
227	fprintf(stderr,
228	    "usage: dhparam [-2 | -5] [-C] [-check] [-dsaparam]\n"
229	    "    [-in file] [-inform DER | PEM] [-noout] [-out file]\n"
230	    "    [-outform DER | PEM] [-text] [numbits]\n\n");
231	options_usage(dhparam_options);
232}
233
234static int dh_cb(int p, int n, BN_GENCB *cb);
235
236int
237dhparam_main(int argc, char **argv)
238{
239	BIO *in = NULL, *out = NULL;
240	BN_GENCB *cb = NULL;
241	char *num_bits = NULL;
242	DH *dh = NULL;
243	int num = 0;
244	int ret = 1;
245	int i;
246
247	if (pledge("stdio cpath wpath rpath", NULL) == -1) {
248		perror("pledge");
249		exit(1);
250	}
251
252	memset(&cfg, 0, sizeof(cfg));
253
254	cfg.informat = FORMAT_PEM;
255	cfg.outformat = FORMAT_PEM;
256
257	if (options_parse(argc, argv, dhparam_options, &num_bits, NULL) != 0) {
258		dhparam_usage();
259		return (1);
260	}
261
262	if (num_bits != NULL) {
263		if(sscanf(num_bits, "%d", &num) == 0 || num <= 0) {
264			BIO_printf(bio_err, "invalid number of bits: %s\n",
265			    num_bits);
266			return (1);
267		}
268	}
269
270	if (cfg.g && !num)
271		num = DEFBITS;
272
273	if (cfg.dsaparam) {
274		if (cfg.g) {
275			BIO_printf(bio_err, "generator may not be chosen for DSA parameters\n");
276			goto end;
277		}
278	} else {
279		/* DH parameters */
280		if (num && !cfg.g)
281			cfg.g = 2;
282	}
283
284	if (num) {
285		if ((cb = BN_GENCB_new()) == NULL) {
286			BIO_printf(bio_err,
287			    "Error allocating BN_GENCB object\n");
288			goto end;
289		}
290
291		BN_GENCB_set(cb, dh_cb, bio_err);
292		if (cfg.dsaparam) {
293			DSA *dsa = DSA_new();
294
295			BIO_printf(bio_err, "Generating DSA parameters, %d bit long prime\n", num);
296			if (!dsa || !DSA_generate_parameters_ex(dsa, num,
297				NULL, 0, NULL, NULL, cb)) {
298				DSA_free(dsa);
299				ERR_print_errors(bio_err);
300				goto end;
301			}
302			dh = DSA_dup_DH(dsa);
303			DSA_free(dsa);
304			if (dh == NULL) {
305				ERR_print_errors(bio_err);
306				goto end;
307			}
308		} else {
309			dh = DH_new();
310			BIO_printf(bio_err, "Generating DH parameters, %d bit long safe prime, generator %d\n", num, cfg.g);
311			BIO_printf(bio_err, "This is going to take a long time\n");
312			if (!dh || !DH_generate_parameters_ex(dh, num, cfg.g, cb)) {
313				ERR_print_errors(bio_err);
314				goto end;
315			}
316		}
317	} else {
318
319		in = BIO_new(BIO_s_file());
320		if (in == NULL) {
321			ERR_print_errors(bio_err);
322			goto end;
323		}
324		if (cfg.infile == NULL)
325			BIO_set_fp(in, stdin, BIO_NOCLOSE);
326		else {
327			if (BIO_read_filename(in, cfg.infile) <= 0) {
328				perror(cfg.infile);
329				goto end;
330			}
331		}
332
333		if (cfg.informat != FORMAT_ASN1 &&
334		    cfg.informat != FORMAT_PEM) {
335			BIO_printf(bio_err, "bad input format specified\n");
336			goto end;
337		}
338		if (cfg.dsaparam) {
339			DSA *dsa;
340
341			if (cfg.informat == FORMAT_ASN1)
342				dsa = d2i_DSAparams_bio(in, NULL);
343			else	/* informat == FORMAT_PEM */
344				dsa = PEM_read_bio_DSAparams(in, NULL, NULL, NULL);
345
346			if (dsa == NULL) {
347				BIO_printf(bio_err, "unable to load DSA parameters\n");
348				ERR_print_errors(bio_err);
349				goto end;
350			}
351			dh = DSA_dup_DH(dsa);
352			DSA_free(dsa);
353			if (dh == NULL) {
354				ERR_print_errors(bio_err);
355				goto end;
356			}
357		} else
358		{
359			if (cfg.informat == FORMAT_ASN1)
360				dh = d2i_DHparams_bio(in, NULL);
361			else	/* informat == FORMAT_PEM */
362				dh = PEM_read_bio_DHparams(in, NULL, NULL, NULL);
363
364			if (dh == NULL) {
365				BIO_printf(bio_err, "unable to load DH parameters\n");
366				ERR_print_errors(bio_err);
367				goto end;
368			}
369		}
370
371		/* dh != NULL */
372	}
373
374	out = BIO_new(BIO_s_file());
375	if (out == NULL) {
376		ERR_print_errors(bio_err);
377		goto end;
378	}
379	if (cfg.outfile == NULL) {
380		BIO_set_fp(out, stdout, BIO_NOCLOSE);
381	} else {
382		if (BIO_write_filename(out, cfg.outfile) <= 0) {
383			perror(cfg.outfile);
384			goto end;
385		}
386	}
387
388
389	if (cfg.text) {
390		DHparams_print(out, dh);
391	}
392	if (cfg.check) {
393		if (!DH_check(dh, &i)) {
394			ERR_print_errors(bio_err);
395			goto end;
396		}
397		if (i & DH_CHECK_P_NOT_PRIME)
398			printf("p value is not prime\n");
399		if (i & DH_CHECK_P_NOT_SAFE_PRIME)
400			printf("p value is not a safe prime\n");
401		if (i & DH_UNABLE_TO_CHECK_GENERATOR)
402			printf("unable to check the generator value\n");
403		if (i & DH_NOT_SUITABLE_GENERATOR)
404			printf("the g value is not a generator\n");
405		if (i == 0)
406			printf("DH parameters appear to be ok.\n");
407	}
408	if (cfg.C) {
409		unsigned char *data;
410		int len, l, bits;
411
412		len = BN_num_bytes(DH_get0_p(dh));
413		bits = BN_num_bits(DH_get0_p(dh));
414		data = malloc(len);
415		if (data == NULL) {
416			perror("malloc");
417			goto end;
418		}
419		printf("#ifndef HEADER_DH_H\n"
420		    "#include <openssl/dh.h>\n"
421		    "#endif\n");
422		printf("DH *get_dh%d()\n\t{\n", bits);
423
424		l = BN_bn2bin(DH_get0_p(dh), data);
425		printf("\tstatic unsigned char dh%d_p[] = {", bits);
426		for (i = 0; i < l; i++) {
427			if ((i % 12) == 0)
428				printf("\n\t\t");
429			printf("0x%02X, ", data[i]);
430		}
431		printf("\n\t\t};\n");
432
433		l = BN_bn2bin(DH_get0_g(dh), data);
434		printf("\tstatic unsigned char dh%d_g[] = {", bits);
435		for (i = 0; i < l; i++) {
436			if ((i % 12) == 0)
437				printf("\n\t\t");
438			printf("0x%02X, ", data[i]);
439		}
440		printf("\n\t\t};\n");
441
442		printf("\tDH *dh;\n");
443		printf("\tBIGNUM *p = NULL, *g = NULL;\n\n");
444		printf("\tif ((dh = DH_new()) == NULL) return(NULL);\n");
445		printf("\tp = BN_bin2bn(dh%d_p, sizeof(dh%d_p), NULL);\n",
446		    bits, bits);
447		printf("\tg = BN_bin2bn(dh%d_g, sizeof(dh%d_g), NULL);\n",
448		    bits, bits);
449		printf("\tif (p == NULL || g == NULL)\n");
450		printf("\t\t{ BN_free(p); BN_free(g); DH_free(dh); return(NULL); }\n");
451		printf("\tDH_set0_pqg(dh, p, NULL, g);\n");
452		if (DH_get_length(dh) > 0)
453			printf("\tDH_set_length(dh, %ld);\n", DH_get_length(dh));
454		printf("\treturn(dh);\n\t}\n");
455		free(data);
456	}
457	if (!cfg.noout) {
458		if (cfg.outformat == FORMAT_ASN1)
459			i = i2d_DHparams_bio(out, dh);
460		else if (cfg.outformat == FORMAT_PEM)
461			i = PEM_write_bio_DHparams(out, dh);
462		else {
463			BIO_printf(bio_err, "bad output format specified for outfile\n");
464			goto end;
465		}
466		if (!i) {
467			BIO_printf(bio_err, "unable to write DH parameters\n");
468			ERR_print_errors(bio_err);
469			goto end;
470		}
471	}
472	ret = 0;
473
474 end:
475	BIO_free(in);
476	BIO_free_all(out);
477	BN_GENCB_free(cb);
478	DH_free(dh);
479
480	return (ret);
481}
482
483/* dh_cb is identical to dsa_cb in apps/dsaparam.c */
484static int
485dh_cb(int p, int n, BN_GENCB *cb)
486{
487	char c = '*';
488
489	if (p == 0)
490		c = '.';
491	if (p == 1)
492		c = '+';
493	if (p == 2)
494		c = '*';
495	if (p == 3)
496		c = '\n';
497	BIO_write(BN_GENCB_get_arg(cb), &c, 1);
498	(void) BIO_flush(BN_GENCB_get_arg(cb));
499	return 1;
500}
501
502#endif
503