1 /* Copyright (C) 2012,2016 Phil Pennock.
2 * Copyright (c) The Exim Maintainers 2021
3 * This is distributed as part of Exim and licensed under the GPL.
4 * See the file "NOTICE" for more details.
5 * SPDX-License-Identifier: GPL-2.0-or-later
9 * c99 $(pkg-config --cflags openssl) gen_pkcs3.c $(pkg-config --libs openssl)
14 * The Diffie-Hellman primes which are embedded into Exim as named primes for
15 * the tls_dhparam option are in the std-crypto.c file. The source for those
16 * comes from various RFCs, where they are given in hexadecimal form.
18 * This tool provides convenient conversion, to reduce the risk of human
19 * error in transcription.
30 #include <openssl/bio.h>
31 #include <openssl/bn.h>
32 #include <openssl/dh.h>
33 #include <openssl/err.h>
34 #include <openssl/pem.h>
36 extern const char *__progname;
39 void __attribute__((__noreturn__)) __attribute__((__format__(printf, 1, 2)))
40 die(const char *fmt, ...)
45 fprintf(stderr, "%s: ", __progname);
47 vfprintf(stderr, fmt, ap);
49 fprintf(stderr, "\n");
55 void __attribute__((__noreturn__))
56 die_openssl_err(const char *msg)
60 ERR_error_string_n(ERR_get_error(), err_string, sizeof(err_string));
61 die("%s: %s", msg, err_string);
66 bn_from_text(const char *text)
75 spaceless = malloc(len + 1);
77 die("malloc(%zu) failed: %s", len + 1, strerror(errno));
79 for (p = spaceless, q = text, end = text + len;
89 rc = BN_hex2bn(&b, spaceless);
92 die("BN_hex2bn did not convert entire input; took %d of %zu bytes",
102 int rc, errflags = 0;
104 rc = DH_check(dh, &errflags);
105 if (!rc) die_openssl_err("DH_check() could not be performed");;
107 /* We ignore DH_UNABLE_TO_CHECK_GENERATOR because some of the invocations
108 * deliberately provide generators other than 2 or 5. */
110 if (errflags & DH_CHECK_P_NOT_SAFE_PRIME)
111 die("DH_check(): p not a safe prime");
112 if (errflags & DH_NOT_SUITABLE_GENERATOR)
113 die("DH_check(): g not suitable as generator");
118 emit_c_format_dh(FILE *stream, DH *dh)
122 char *data, *end, *p, *nl;
124 bio = BIO_new(BIO_s_mem());
125 PEM_write_bio_DHparams(bio, dh);
126 length = BIO_get_mem_data(bio, &data);
128 die("no data in memory BIO to format for printing");
130 die("grr, negative length memory not supported");
133 for (p = data; p < end; /**/) {
134 nl = strchr(p, '\n');
136 fprintf(stream, "\"%s\\n\"\n/* missing final newline */\n", p);
140 fprintf(stream, "\"%s\\n\"%s\n", p, (nl == end - 1 ? ";" : ""));
146 void __attribute__((__noreturn__))
147 usage(FILE *stream, int exitcode)
149 fprintf(stream, "Usage: %s [-CPcst] <dh_p> <dh_g> [<dh_q>]\n"
150 "Both dh_p and dh_g should be hex strings representing the numbers\n"
151 "The same applies to the optional dh_q (prime-order subgroup).\n"
152 "They may contain whitespace.\n"
153 "Older values, dh_g is often just '2', not a long string.\n"
155 " -C show C string form of PEM result\n"
156 " -P do not show PEM\n"
157 " -c run OpenSSL DH_check() on the DH object\n"
158 " -s show the parsed p and g\n"
159 " -t show text form of certificate\n"
167 main(int argc, char *argv[])
172 bool perform_dh_check = false;
173 bool show_c_form = false;
174 bool show_numbers = false;
175 bool show_pem = true;
176 bool show_text = false;
177 bool given_q = false;
179 while ((ch = getopt(argc, argv, "CPcsth")) != -1) {
188 perform_dh_check = true;
200 die("Unknown option or missing argument -%c", optopt);
202 die("Unhandled option -%c", ch);
210 if ((argc < 3) || (argc > 4)) {
211 fprintf(stderr, "argc: %d\n", argc);
215 // If we use DH_set0_pqg instead of setting dh fields directly; the q value
216 // is optional and may be NULL.
217 // Just blank them all.
220 p = bn_from_text(argv[1]);
221 g = bn_from_text(argv[2]);
223 q = bn_from_text(argv[3]);
229 BN_print_fp(stdout, p);
231 BN_print_fp(stdout, g);
234 BN_print_fp(stdout, q);
240 // The documented method for setting q appeared in OpenSSL 1.1.0.
241 #if OPENSSL_VERSION_NUMBER >= 0x1010000f
242 // NULL okay for q; yes, the optional value is in the middle.
243 if (DH_set0_pqg(dh, p, q, g) != 1) {
244 die_openssl_err("initialising DH pqg values failed");
254 if (perform_dh_check)
258 DHparams_print_fp(stdout, dh);
262 emit_c_format_dh(stdout, dh);
264 PEM_write_DHparams(stdout, dh);
267 DH_free(dh); /* should free p,g (& q if non-NULL) too */