Dovecot: robustness; better msg on missing mech.
[users/jgh/exim.git] / src / util / gen_pkcs3.c
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 }