Commit | Line | Data |
---|---|---|
420a0d19 CE |
1 | /* Copyright (C) 2012 Phil Pennock. |
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 | ||
34 | extern const char *__progname; | |
35 | ||
36 | ||
37 | void __attribute__((__noreturn__)) __attribute__((__format__(printf, 1, 2))) | |
38 | die(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 | ||
53 | void __attribute__((__noreturn__)) | |
54 | die_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 | ||
64 | static BIGNUM * | |
65 | bn_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) | |
89 | die("BN_hex2bn did not convert entire input; took %d of %z bytes", | |
90 | rc, p - spaceless); | |
91 | ||
92 | return b; | |
93 | } | |
94 | ||
95 | ||
96 | static void | |
97 | our_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 | ||
114 | static void | |
115 | emit_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'; | |
137 | fprintf(stream, "\"%s\\n\"\n", p); | |
138 | p = nl + 1; | |
139 | } | |
140 | } | |
141 | ||
142 | ||
143 | void __attribute__((__noreturn__)) | |
144 | usage(FILE *stream, int exitcode) | |
145 | { | |
146 | fprintf(stream, "Usage: %s [-CPcst] <dh_p> <dh_g>\n" | |
147 | "Both dh_p and dh_g should be hex strings representing the numbers\n" | |
148 | "They may contain whitespace.\n" | |
149 | "\n" | |
150 | " -C show C string form of PEM result\n" | |
151 | " -P do not show PEM\n" | |
152 | " -c run OpenSSL DH_check() on the DH object\n" | |
153 | " -s show the parsed p and g\n" | |
154 | " -t show text form of certificate\n" | |
155 | ||
156 | , __progname); | |
157 | exit(exitcode); | |
158 | } | |
159 | ||
160 | ||
161 | int | |
162 | main(int argc, char *argv[]) | |
163 | { | |
164 | BIGNUM *p, *g; | |
165 | DH *dh; | |
166 | int ch; | |
167 | bool perform_dh_check = false; | |
168 | bool show_c_form = false; | |
169 | bool show_numbers = false; | |
170 | bool show_pem = true; | |
171 | bool show_text = false; | |
172 | ||
173 | while ((ch = getopt(argc, argv, "CPcsth")) != -1) { | |
174 | switch (ch) { | |
175 | case 'C': | |
176 | show_c_form = true; | |
177 | break; | |
178 | case 'P': | |
179 | show_pem = false; | |
180 | break; | |
181 | case 'c': | |
182 | perform_dh_check = true; | |
183 | break; | |
184 | case 's': | |
185 | show_numbers = true; | |
186 | break; | |
187 | case 't': | |
188 | show_text = true; | |
189 | break; | |
190 | ||
191 | case 'h': | |
192 | usage(stdout, 0); | |
193 | case '?': | |
194 | die("Unknown option or missing argument -%c", optopt); | |
195 | default: | |
196 | die("Unhandled option -%c", ch); | |
197 | } | |
198 | } | |
199 | ||
200 | optind -= 1; | |
201 | argc -= optind; | |
202 | argv += optind; | |
203 | ||
204 | if (argc != 3) { | |
205 | fprintf(stderr, "argc: %d\n", argc); | |
206 | usage(stderr, 1); | |
207 | } | |
208 | ||
209 | p = bn_from_text(argv[1]); | |
210 | g = bn_from_text(argv[2]); | |
211 | ||
212 | if (show_numbers) { | |
213 | printf("p = "); | |
214 | BN_print_fp(stdout, p); | |
215 | printf("\ng = "); | |
216 | BN_print_fp(stdout, g); | |
217 | printf("\n"); | |
218 | } | |
219 | ||
220 | dh = DH_new(); | |
221 | dh->p = p; | |
222 | dh->g = g; | |
223 | ||
224 | if (perform_dh_check) | |
225 | our_dh_check(dh); | |
226 | ||
227 | if (show_text) | |
228 | DHparams_print_fp(stdout, dh); | |
229 | ||
230 | if (show_pem) { | |
231 | if (show_c_form) | |
232 | emit_c_format_dh(stdout, dh); | |
233 | else | |
234 | PEM_write_DHparams(stdout, dh); | |
235 | } | |
236 | ||
237 | DH_free(dh); /* should free p & g too */ | |
238 | return 0; | |
239 | } |