1 /* Copyright (C) 2012,2016 Phil Pennock.
2 * This is distributed as part of Exim and licensed under the GPL.
3 * See the file "NOTICE" for more details.
7 * c99 $(pkg-config --cflags openssl) gen_pkcs3.c $(pkg-config --libs openssl)
12 * The Diffie-Hellman primes which are embedded into Exim as named primes for
13 * the tls_dhparam option are in the std-crypto.c file. The source for those
14 * comes from various RFCs, where they are given in hexadecimal form.
16 * This tool provides convenient conversion, to reduce the risk of human
17 * error in transcription.
28 #include <openssl/bio.h>
29 #include <openssl/bn.h>
30 #include <openssl/dh.h>
31 #include <openssl/err.h>
32 #include <openssl/pem.h>
34 extern const char *__progname
;
37 void __attribute__((__noreturn__
)) __attribute__((__format__(printf
, 1, 2)))
38 die(const char *fmt
, ...)
43 fprintf(stderr
, "%s: ", __progname
);
45 vfprintf(stderr
, fmt
, ap
);
47 fprintf(stderr
, "\n");
53 void __attribute__((__noreturn__
))
54 die_openssl_err(const char *msg
)
59 ERR_error_string_n(ERR_get_error(), err_string
, sizeof(err_string
));
60 die("%s: %s", msg
, err_string
);
65 bn_from_text(const char *text
)
74 spaceless
= malloc(len
);
76 die("malloc(%zu) failed: %s", len
, strerror(errno
));
78 for (p
= spaceless
, q
= text
, end
= text
+ len
;
86 rc
= BN_hex2bn(&b
, spaceless
);
88 if (rc
!= p
- spaceless
)
89 die("BN_hex2bn did not convert entire input; took %d of %zu bytes",
101 rc
= DH_check(dh
, &errflags
);
102 if (!rc
) die_openssl_err("DH_check() could not be performed");;
104 /* We ignore DH_UNABLE_TO_CHECK_GENERATOR because some of the invocations
105 * deliberately provide generators other than 2 or 5. */
107 if (errflags
& DH_CHECK_P_NOT_SAFE_PRIME
)
108 die("DH_check(): p not a safe prime");
109 if (errflags
& DH_NOT_SUITABLE_GENERATOR
)
110 die("DH_check(): g not suitable as generator");
115 emit_c_format_dh(FILE *stream
, DH
*dh
)
119 char *data
, *end
, *p
, *nl
;
121 bio
= BIO_new(BIO_s_mem());
122 PEM_write_bio_DHparams(bio
, dh
);
123 length
= BIO_get_mem_data(bio
, &data
);
125 die("no data in memory BIO to format for printing");
127 die("grr, negative length memory not supported");
130 for (p
= data
; p
< end
; /**/) {
131 nl
= strchr(p
, '\n');
133 fprintf(stream
, "\"%s\\n\"\n/* missing final newline */\n", p
);
137 fprintf(stream
, "\"%s\\n\"%s\n", p
, (nl
== end
- 1 ? ";" : ""));
143 void __attribute__((__noreturn__
))
144 usage(FILE *stream
, int exitcode
)
146 fprintf(stream
, "Usage: %s [-CPcst] <dh_p> <dh_g> [<dh_q>]\n"
147 "Both dh_p and dh_g should be hex strings representing the numbers\n"
148 "The same applies to the optional dh_q (prime-order subgroup).\n"
149 "They may contain whitespace.\n"
150 "Older values, dh_g is often just '2', not a long string.\n"
152 " -C show C string form of PEM result\n"
153 " -P do not show PEM\n"
154 " -c run OpenSSL DH_check() on the DH object\n"
155 " -s show the parsed p and g\n"
156 " -t show text form of certificate\n"
164 main(int argc
, char *argv
[])
169 bool perform_dh_check
= false;
170 bool show_c_form
= false;
171 bool show_numbers
= false;
172 bool show_pem
= true;
173 bool show_text
= false;
174 bool given_q
= false;
176 while ((ch
= getopt(argc
, argv
, "CPcsth")) != -1) {
185 perform_dh_check
= true;
197 die("Unknown option or missing argument -%c", optopt
);
199 die("Unhandled option -%c", ch
);
207 if ((argc
< 3) || (argc
> 4)) {
208 fprintf(stderr
, "argc: %d\n", argc
);
212 // If we use DH_set0_pqg instead of setting dh fields directly; the q value
213 // is optional and may be NULL.
214 // Just blank them all.
217 p
= bn_from_text(argv
[1]);
218 g
= bn_from_text(argv
[2]);
220 q
= bn_from_text(argv
[3]);
226 BN_print_fp(stdout
, p
);
228 BN_print_fp(stdout
, g
);
231 BN_print_fp(stdout
, q
);
237 // The documented method for setting q appeared in OpenSSL 1.1.0.
238 #if OPENSSL_VERSION_NUMBER >= 0x1010000f
239 // NULL okay for q; yes, the optional value is in the middle.
240 if (DH_set0_pqg(dh
, p
, q
, g
) != 1) {
241 die_openssl_err("initialising DH pqg values failed");
251 if (perform_dh_check
)
255 DHparams_print_fp(stdout
, dh
);
259 emit_c_format_dh(stdout
, dh
);
261 PEM_write_DHparams(stdout
, dh
);
264 DH_free(dh
); /* should free p,g (& q if non-NULL) too */