Import Debian changes 4.89-2+deb9u4
[hcoop/debian/exim4.git] / util / gen_pkcs3.c
CommitLineData
2813c06e 1/* Copyright (C) 2012,2016 Phil Pennock.
420a0d19
CE
2 * This is distributed as part of Exim and licensed under the GPL.
3 * See the file "NOTICE" for more details.
4 */
5
6/* Build with:
7 * c99 $(pkg-config --cflags openssl) gen_pkcs3.c $(pkg-config --libs openssl)
8 */
9
10/*
11 * Rationale:
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.
15 *
16 * This tool provides convenient conversion, to reduce the risk of human
17 * error in transcription.
18 */
19
20#include <ctype.h>
21#include <errno.h>
22#include <stdbool.h>
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <unistd.h>
27
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>
33
34extern const char *__progname;
35
36
37void __attribute__((__noreturn__)) __attribute__((__format__(printf, 1, 2)))
38die(const char *fmt, ...)
39{
40 va_list ap;
41
42 fflush(NULL);
43 fprintf(stderr, "%s: ", __progname);
44 va_start(ap, fmt);
45 vfprintf(stderr, fmt, ap);
46 va_end(ap);
47 fprintf(stderr, "\n");
48 fflush(stderr);
49 exit(1);
50}
51
52
53void __attribute__((__noreturn__))
54die_openssl_err(const char *msg)
55{
56 char err_string[250];
57 unsigned long e;
58
59 ERR_error_string_n(ERR_get_error(), err_string, sizeof(err_string));
60 die("%s: %s", msg, err_string);
61}
62
63
64static BIGNUM *
65bn_from_text(const char *text)
66{
67 BIGNUM *b;
68 char *p, *spaceless;
69 const char *q, *end;
70 size_t len;
71 int rc;
72
73 len = strlen(text);
74 spaceless = malloc(len);
75 if (!spaceless)
76 die("malloc(%zu) failed: %s", len, strerror(errno));
77
78 for (p = spaceless, q = text, end = text + len;
79 q < end;
80 ++q) {
81 if (!isspace(*q))
82 *p++ = *q;
83 }
84
85 b = NULL;
86 rc = BN_hex2bn(&b, spaceless);
87
88 if (rc != p - spaceless)
2813c06e 89 die("BN_hex2bn did not convert entire input; took %d of %zu bytes",
420a0d19
CE
90 rc, p - spaceless);
91
92 return b;
93}
94
95
96static void
97our_dh_check(DH *dh)
98{
99 int rc, errflags = 0;
100
101 rc = DH_check(dh, &errflags);
102 if (!rc) die_openssl_err("DH_check() could not be performed");;
103
104 /* We ignore DH_UNABLE_TO_CHECK_GENERATOR because some of the invocations
105 * deliberately provide generators other than 2 or 5. */
106
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");
111}
112
113
114static void
115emit_c_format_dh(FILE *stream, DH *dh)
116{
117 BIO *bio;
118 long length;
119 char *data, *end, *p, *nl;
120
121 bio = BIO_new(BIO_s_mem());
122 PEM_write_bio_DHparams(bio, dh);
123 length = BIO_get_mem_data(bio, &data);
124 if (!length)
125 die("no data in memory BIO to format for printing");
126 if (length < 0)
127 die("grr, negative length memory not supported");
128 end = data + length;
129
130 for (p = data; p < end; /**/) {
131 nl = strchr(p, '\n');
132 if (!nl) {
133 fprintf(stream, "\"%s\\n\"\n/* missing final newline */\n", p);
134 break;
135 }
136 *nl = '\0';
2813c06e 137 fprintf(stream, "\"%s\\n\"%s\n", p, (nl == end - 1 ? ";" : ""));
420a0d19
CE
138 p = nl + 1;
139 }
140}
141
142
143void __attribute__((__noreturn__))
144usage(FILE *stream, int exitcode)
145{
2813c06e 146 fprintf(stream, "Usage: %s [-CPcst] <dh_p> <dh_g> [<dh_q>]\n"
420a0d19 147"Both dh_p and dh_g should be hex strings representing the numbers\n"
2813c06e 148"The same applies to the optional dh_q (prime-order subgroup).\n"
420a0d19 149"They may contain whitespace.\n"
2813c06e 150"Older values, dh_g is often just '2', not a long string.\n"
420a0d19
CE
151"\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"
157
158 , __progname);
159 exit(exitcode);
160}
161
162
163int
164main(int argc, char *argv[])
165{
2813c06e 166 BIGNUM *p, *g, *q;
420a0d19
CE
167 DH *dh;
168 int ch;
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;
2813c06e 174 bool given_q = false;
420a0d19
CE
175
176 while ((ch = getopt(argc, argv, "CPcsth")) != -1) {
177 switch (ch) {
178 case 'C':
179 show_c_form = true;
180 break;
181 case 'P':
182 show_pem = false;
183 break;
184 case 'c':
185 perform_dh_check = true;
186 break;
187 case 's':
188 show_numbers = true;
189 break;
190 case 't':
191 show_text = true;
192 break;
193
194 case 'h':
195 usage(stdout, 0);
196 case '?':
197 die("Unknown option or missing argument -%c", optopt);
198 default:
199 die("Unhandled option -%c", ch);
200 }
201 }
202
203 optind -= 1;
204 argc -= optind;
205 argv += optind;
206
2813c06e 207 if ((argc < 3) || (argc > 4)) {
420a0d19
CE
208 fprintf(stderr, "argc: %d\n", argc);
209 usage(stderr, 1);
210 }
211
2813c06e
CE
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.
215 p = g = q = NULL;
216
420a0d19
CE
217 p = bn_from_text(argv[1]);
218 g = bn_from_text(argv[2]);
2813c06e
CE
219 if (argc >= 4) {
220 q = bn_from_text(argv[3]);
221 given_q = true;
222 }
420a0d19
CE
223
224 if (show_numbers) {
225 printf("p = ");
226 BN_print_fp(stdout, p);
227 printf("\ng = ");
228 BN_print_fp(stdout, g);
2813c06e
CE
229 if (given_q) {
230 printf("\nq = ");
231 BN_print_fp(stdout, q);
232 }
420a0d19
CE
233 printf("\n");
234 }
235
236 dh = DH_new();
2813c06e
CE
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");
242 }
243#else
420a0d19
CE
244 dh->p = p;
245 dh->g = g;
2813c06e
CE
246 if (given_q) {
247 dh->q = q;
248 }
249#endif
420a0d19
CE
250
251 if (perform_dh_check)
252 our_dh_check(dh);
253
254 if (show_text)
255 DHparams_print_fp(stdout, dh);
256
257 if (show_pem) {
258 if (show_c_form)
259 emit_c_format_dh(stdout, dh);
260 else
261 PEM_write_DHparams(stdout, dh);
262 }
263
2813c06e 264 DH_free(dh); /* should free p,g (& q if non-NULL) too */
420a0d19
CE
265 return 0;
266}