Commit | Line | Data |
---|---|---|
a799883d PP |
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 | #include <ctype.h> | |
11 | #include <errno.h> | |
12 | #include <stdbool.h> | |
13 | #include <stdio.h> | |
14 | #include <stdlib.h> | |
15 | #include <string.h> | |
16 | #include <unistd.h> | |
17 | ||
18 | #include <openssl/bio.h> | |
19 | #include <openssl/bn.h> | |
20 | #include <openssl/dh.h> | |
21 | #include <openssl/err.h> | |
22 | #include <openssl/pem.h> | |
23 | ||
24 | extern const char *__progname; | |
25 | ||
26 | ||
27 | void __attribute__((__noreturn__)) __attribute__((__format__(printf, 1, 2))) | |
28 | die(const char *fmt, ...) | |
29 | { | |
30 | va_list ap; | |
31 | ||
32 | fflush(NULL); | |
33 | fprintf(stderr, "%s: ", __progname); | |
34 | va_start(ap, fmt); | |
35 | vfprintf(stderr, fmt, ap); | |
36 | va_end(ap); | |
37 | fprintf(stderr, "\n"); | |
38 | fflush(stderr); | |
39 | exit(1); | |
40 | } | |
41 | ||
42 | ||
43 | void __attribute__((__noreturn__)) | |
44 | die_openssl_err(const char *msg) | |
45 | { | |
46 | char err_string[250]; | |
47 | unsigned long e; | |
48 | ||
49 | ERR_error_string_n(ERR_get_error(), err_string, sizeof(err_string)); | |
50 | die("%s: %s", msg, err_string); | |
51 | } | |
52 | ||
53 | ||
54 | static BIGNUM * | |
55 | bn_from_text(const char *text) | |
56 | { | |
57 | BIGNUM *b; | |
58 | char *p, *spaceless; | |
59 | const char *q, *end; | |
60 | size_t len; | |
61 | int rc; | |
62 | ||
63 | len = strlen(text); | |
64 | spaceless = malloc(len); | |
65 | if (!spaceless) | |
66 | die("malloc(%zu) failed: %s", len, strerror(errno)); | |
67 | ||
68 | for (p = spaceless, q = text, end = text + len; | |
69 | q < end; | |
70 | ++q) { | |
71 | if (!isspace(*q)) | |
72 | *p++ = *q; | |
73 | } | |
74 | ||
75 | b = NULL; | |
76 | rc = BN_hex2bn(&b, spaceless); | |
77 | ||
78 | if (rc != p - spaceless) | |
79 | die("BN_hex2bn did not convert entire input; took %d of %z bytes", | |
80 | rc, p - spaceless); | |
81 | ||
82 | return b; | |
83 | } | |
84 | ||
85 | ||
86 | static void | |
87 | our_dh_check(DH *dh) | |
88 | { | |
89 | int rc, errflags = 0; | |
90 | ||
91 | rc = DH_check(dh, &errflags); | |
92 | if (!rc) die_openssl_err("DH_check() could not be performed");; | |
93 | ||
94 | /* We ignore DH_UNABLE_TO_CHECK_GENERATOR because some of the invocations | |
95 | * deliberately provide generators other than 2 or 5. */ | |
96 | ||
97 | if (errflags & DH_CHECK_P_NOT_SAFE_PRIME) | |
98 | die("DH_check(): p not a safe prime"); | |
99 | if (errflags & DH_NOT_SUITABLE_GENERATOR) | |
100 | die("DH_check(): g not suitable as generator"); | |
101 | } | |
102 | ||
103 | ||
104 | static void | |
105 | emit_c_format_dh(FILE *stream, DH *dh) | |
106 | { | |
107 | BIO *bio; | |
108 | long length; | |
109 | char *data, *end, *p, *nl; | |
110 | ||
111 | bio = BIO_new(BIO_s_mem()); | |
112 | PEM_write_bio_DHparams(bio, dh); | |
113 | length = BIO_get_mem_data(bio, &data); | |
114 | if (!length) | |
115 | die("no data in memory BIO to format for printing"); | |
116 | if (length < 0) | |
117 | die("grr, negative length memory not supported"); | |
118 | end = data + length; | |
119 | ||
120 | for (p = data; p < end; /**/) { | |
121 | nl = strchr(p, '\n'); | |
122 | if (!nl) { | |
123 | fprintf(stream, "\"%s\\n\"\n/* missing final newline */\n", p); | |
124 | break; | |
125 | } | |
126 | *nl = '\0'; | |
127 | fprintf(stream, "\"%s\\n\"\n", p); | |
128 | p = nl + 1; | |
129 | } | |
130 | } | |
131 | ||
132 | ||
133 | void __attribute__((__noreturn__)) | |
134 | usage(FILE *stream, int exitcode) | |
135 | { | |
136 | fprintf(stream, "Usage: %s [-CPcst] <dh_p> <dh_g>\n" | |
137 | "Both dh_p and dh_g should be hex strings representing the numbers\n" | |
138 | "They may contain whitespace.\n" | |
139 | "\n" | |
140 | " -C show C string form of PEM result\n" | |
141 | " -P do not show PEM\n" | |
142 | " -c run OpenSSL DH_check() on the DH object\n" | |
143 | " -s show the parsed p and g\n" | |
144 | " -t show text form of certificate\n" | |
145 | ||
146 | , __progname); | |
147 | exit(exitcode); | |
148 | } | |
149 | ||
150 | ||
151 | int | |
152 | main(int argc, char *argv[]) | |
153 | { | |
154 | BIGNUM *p, *g; | |
155 | DH *dh; | |
156 | int ch; | |
157 | bool perform_dh_check = false; | |
158 | bool show_c_form = false; | |
159 | bool show_numbers = false; | |
160 | bool show_pem = true; | |
161 | bool show_text = false; | |
162 | ||
163 | while ((ch = getopt(argc, argv, "CPcsth")) != -1) { | |
164 | switch (ch) { | |
165 | case 'C': | |
166 | show_c_form = true; | |
167 | break; | |
168 | case 'P': | |
169 | show_pem = false; | |
170 | break; | |
171 | case 'c': | |
172 | perform_dh_check = true; | |
173 | break; | |
174 | case 's': | |
175 | show_numbers = true; | |
176 | break; | |
177 | case 't': | |
178 | show_text = true; | |
179 | break; | |
180 | ||
181 | case 'h': | |
182 | usage(stdout, 0); | |
183 | case '?': | |
184 | die("Unknown option or missing argument -%c", optopt); | |
185 | default: | |
186 | die("Unhandled option -%c", ch); | |
187 | } | |
188 | } | |
189 | ||
190 | optind -= 1; | |
191 | argc -= optind; | |
192 | argv += optind; | |
193 | ||
194 | if (argc != 3) { | |
195 | fprintf(stderr, "argc: %d\n", argc); | |
196 | usage(stderr, 1); | |
197 | } | |
198 | ||
199 | p = bn_from_text(argv[1]); | |
200 | g = bn_from_text(argv[2]); | |
201 | ||
202 | if (show_numbers) { | |
203 | printf("p = "); | |
204 | BN_print_fp(stdout, p); | |
205 | printf("\ng = "); | |
206 | BN_print_fp(stdout, g); | |
207 | printf("\n"); | |
208 | } | |
209 | ||
210 | dh = DH_new(); | |
211 | dh->p = p; | |
212 | dh->g = g; | |
213 | ||
214 | if (perform_dh_check) | |
215 | our_dh_check(dh); | |
216 | ||
217 | if (show_text) | |
218 | DHparams_print_fp(stdout, dh); | |
219 | ||
220 | if (show_pem) { | |
221 | if (show_c_form) | |
222 | emit_c_format_dh(stdout, dh); | |
223 | else | |
224 | PEM_write_DHparams(stdout, dh); | |
225 | } | |
226 | ||
227 | DH_free(dh); /* should free p & g too */ | |
228 | return 0; | |
229 | } |