px5g: support EC keys
[librecmc/librecmc.git] / package / utils / px5g / px5g.c
1 /*
2  * px5g - Embedded x509 key and certificate generator based on PolarSSL
3  *
4  *   Copyright (C) 2009 Steven Barth <steven@midlink.org>
5  *   Copyright (C) 2014 Felix Fietkau <nbd@nbd.name>
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public
9  *  License, version 2.1 as published by the Free Software Foundation.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *  MA  02110-1301  USA
20  */
21
22 #include <sys/types.h>
23
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <time.h>
28 #include <limits.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <stdbool.h>
32
33 #include <mbedtls/bignum.h>
34 #include <mbedtls/x509_crt.h>
35 #include <mbedtls/ecp.h>
36 #include <mbedtls/rsa.h>
37 #include <mbedtls/pk.h>
38
39 #define PX5G_VERSION "0.2"
40 #define PX5G_COPY "Copyright (c) 2009 Steven Barth <steven@midlink.org>"
41 #define PX5G_LICENSE "Licensed under the GNU Lesser General Public License v2.1"
42
43 static int urandom_fd;
44 static char buf[16384];
45
46 static int _urandom(void *ctx, unsigned char *out, size_t len)
47 {
48         read(urandom_fd, out, len);
49         return 0;
50 }
51
52 static void write_file(const char *path, int len, bool pem)
53 {
54         FILE *f = stdout;
55         const char *buf_start = buf;
56
57         if (!pem)
58                 buf_start += sizeof(buf) - len;
59
60         if (!len) {
61                 fprintf(stderr, "No data to write\n");
62                 exit(1);
63         }
64
65         if (!f) {
66                 fprintf(stderr, "error: I/O error\n");
67                 exit(1);
68         }
69
70         if (path)
71                 f = fopen(path, "w");
72
73         fwrite(buf_start, 1, len, f);
74         fclose(f);
75 }
76
77 static mbedtls_ecp_group_id ecp_curve(const char *name)
78 {
79         const mbedtls_ecp_curve_info *curve_info;
80
81         if (!strcmp(name, "P-256"))
82                 return MBEDTLS_ECP_DP_SECP256R1;
83         else if (!strcmp(name, "P-384"))
84                 return MBEDTLS_ECP_DP_SECP384R1;
85         else if (!strcmp(name, "P-521"))
86                 return MBEDTLS_ECP_DP_SECP521R1;
87         curve_info = mbedtls_ecp_curve_info_from_name(name);
88         if (curve_info == NULL)
89                 return MBEDTLS_ECP_DP_NONE;
90         else
91                 return curve_info->grp_id;
92 }
93
94 static void write_key(mbedtls_pk_context *key, const char *path, bool pem)
95 {
96         int len = 0;
97
98         if (pem) {
99                 if (mbedtls_pk_write_key_pem(key, (void *) buf, sizeof(buf)) == 0)
100                         len = strlen(buf);
101         } else {
102                 len = mbedtls_pk_write_key_der(key, (void *) buf, sizeof(buf));
103                 if (len < 0)
104                         len = 0;
105         }
106
107         write_file(path, len, pem);
108 }
109
110 static void gen_key(mbedtls_pk_context *key, bool rsa, int ksize, int exp,
111                     mbedtls_ecp_group_id curve, bool pem)
112 {
113         mbedtls_pk_init(key);
114         if (rsa) {
115                 fprintf(stderr, "Generating RSA private key, %i bit long modulus\n", ksize);
116                 mbedtls_pk_setup(key, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA));
117                 if (!mbedtls_rsa_gen_key(mbedtls_pk_rsa(*key), _urandom, NULL, ksize, exp))
118                         return;
119         } else {
120                 fprintf(stderr, "Generating EC private key\n");
121                 mbedtls_pk_setup(key, mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY));
122                 if (!mbedtls_ecp_gen_key(curve, mbedtls_pk_ec(*key), _urandom, NULL))
123                         return;
124         }
125         fprintf(stderr, "error: key generation failed\n");
126         exit(1);
127 }
128
129 int dokey(bool rsa, char **arg)
130 {
131         mbedtls_pk_context key;
132         unsigned int ksize = 512;
133         int exp = 65537;
134         char *path = NULL;
135         bool pem = true;
136         mbedtls_ecp_group_id curve = MBEDTLS_ECP_DP_SECP256R1;
137
138         while (*arg && **arg == '-') {
139                 if (!strcmp(*arg, "-out") && arg[1]) {
140                         path = arg[1];
141                         arg++;
142                 } else if (!strcmp(*arg, "-3")) {
143                         exp = 3;
144                 } else if (!strcmp(*arg, "-der")) {
145                         pem = false;
146                 }
147                 arg++;
148         }
149
150         if (*arg && rsa) {
151                 ksize = (unsigned int)atoi(*arg);
152         } else if (*arg) {
153                 curve = ecp_curve((const char *)*arg);
154                 if (curve == MBEDTLS_ECP_DP_NONE) {
155                         fprintf(stderr, "error: invalid curve name: %s\n", *arg);
156                         return 1;
157                 }
158         }
159
160         gen_key(&key, rsa, ksize, exp, curve, pem);
161         write_key(&key, path, pem);
162
163         mbedtls_pk_free(&key);
164
165         return 0;
166 }
167
168 int selfsigned(char **arg)
169 {
170         mbedtls_pk_context key;
171         mbedtls_x509write_cert cert;
172         mbedtls_mpi serial;
173
174         char *subject = "";
175         unsigned int ksize = 512;
176         int exp = 65537;
177         unsigned int days = 30;
178         char *keypath = NULL, *certpath = NULL;
179         bool pem = true;
180         time_t from = time(NULL), to;
181         char fstr[20], tstr[20], sstr[17];
182         int len;
183         bool rsa = true;
184         mbedtls_ecp_group_id curve = MBEDTLS_ECP_DP_SECP256R1;
185
186         while (*arg && **arg == '-') {
187                 if (!strcmp(*arg, "-der")) {
188                         pem = false;
189                 } else if (!strcmp(*arg, "-newkey") && arg[1]) {
190                         if (!strncmp(arg[1], "rsa:", 4)) {
191                                 rsa = true;
192                                 ksize = (unsigned int)atoi(arg[1] + 4);
193                         } else if (!strcmp(arg[1], "ec")) {
194                                 rsa = false;
195                         } else {
196                                 fprintf(stderr, "error: invalid algorithm\n");
197                                 return 1;
198                         }
199                         arg++;
200                 } else if (!strcmp(*arg, "-days") && arg[1]) {
201                         days = (unsigned int)atoi(arg[1]);
202                         arg++;
203                 } else if (!strcmp(*arg, "-pkeyopt") && arg[1]) {
204                         if (strncmp(arg[1], "ec_paramgen_curve:", 18)) {
205                                 fprintf(stderr, "error: invalid pkey option: %s\n", arg[1]);
206                                 return 1;
207                         }
208                         curve = ecp_curve((const char *)(arg[1] + 18));
209                         if (curve == MBEDTLS_ECP_DP_NONE) {
210                                 fprintf(stderr, "error: invalid curve name: %s\n", arg[1] + 18);
211                                 return 1;
212                         }
213                         arg++;
214                 } else if (!strcmp(*arg, "-keyout") && arg[1]) {
215                         keypath = arg[1];
216                         arg++;
217                 } else if (!strcmp(*arg, "-out") && arg[1]) {
218                         certpath = arg[1];
219                         arg++;
220                 } else if (!strcmp(*arg, "-subj") && arg[1]) {
221                         if (arg[1][0] != '/' || strchr(arg[1], ';')) {
222                                 fprintf(stderr, "error: invalid subject");
223                                 return 1;
224                         }
225                         subject = calloc(strlen(arg[1]) + 1, 1);
226                         char *oldc = arg[1] + 1, *newc = subject, *delim;
227                         do {
228                                 delim = strchr(oldc, '=');
229                                 if (!delim) {
230                                         fprintf(stderr, "error: invalid subject");
231                                         return 1;
232                                 }
233                                 memcpy(newc, oldc, delim - oldc + 1);
234                                 newc += delim - oldc + 1;
235                                 oldc = delim + 1;
236
237                                 delim = strchr(oldc, '/');
238                                 if (!delim) {
239                                         delim = arg[1] + strlen(arg[1]);
240                                 }
241                                 memcpy(newc, oldc, delim - oldc);
242                                 newc += delim - oldc;
243                                 *newc++ = ',';
244                                 oldc = delim + 1;
245                         } while(*delim);
246                         arg++;
247                 }
248                 arg++;
249         }
250         gen_key(&key, rsa, ksize, exp, curve, pem);
251
252         if (keypath)
253                 write_key(&key, keypath, pem);
254
255         from = (from < 1000000000) ? 1000000000 : from;
256         strftime(fstr, sizeof(fstr), "%Y%m%d%H%M%S", gmtime(&from));
257         to = from + 60 * 60 * 24 * days;
258         if (to < from)
259                 to = INT_MAX;
260         strftime(tstr, sizeof(tstr), "%Y%m%d%H%M%S", gmtime(&to));
261
262         fprintf(stderr, "Generating selfsigned certificate with subject '%s'"
263                         " and validity %s-%s\n", subject, fstr, tstr);
264
265         mbedtls_x509write_crt_init(&cert);
266         mbedtls_x509write_crt_set_md_alg(&cert, MBEDTLS_MD_SHA256);
267         mbedtls_x509write_crt_set_issuer_key(&cert, &key);
268         mbedtls_x509write_crt_set_subject_key(&cert, &key);
269         mbedtls_x509write_crt_set_subject_name(&cert, subject);
270         mbedtls_x509write_crt_set_issuer_name(&cert, subject);
271         mbedtls_x509write_crt_set_validity(&cert, fstr, tstr);
272         mbedtls_x509write_crt_set_basic_constraints(&cert, 0, -1);
273         mbedtls_x509write_crt_set_subject_key_identifier(&cert);
274         mbedtls_x509write_crt_set_authority_key_identifier(&cert);
275
276         _urandom(NULL, (void *) buf, 8);
277         for (len = 0; len < 8; len++)
278                 sprintf(sstr + len*2, "%02x", (unsigned char) buf[len]);
279
280         mbedtls_mpi_init(&serial);
281         mbedtls_mpi_read_string(&serial, 16, sstr);
282         mbedtls_x509write_crt_set_serial(&cert, &serial);
283
284         if (pem) {
285                 if (mbedtls_x509write_crt_pem(&cert, (void *) buf, sizeof(buf), _urandom, NULL) < 0) {
286                         fprintf(stderr, "Failed to generate certificate\n");
287                         return 1;
288                 }
289
290                 len = strlen(buf);
291         } else {
292                 len = mbedtls_x509write_crt_der(&cert, (void *) buf, sizeof(buf), _urandom, NULL);
293                 if (len < 0) {
294                         fprintf(stderr, "Failed to generate certificate: %d\n", len);
295                         return 1;
296                 }
297         }
298         write_file(certpath, len, pem);
299
300         mbedtls_x509write_crt_free(&cert);
301         mbedtls_mpi_free(&serial);
302         mbedtls_pk_free(&key);
303
304         return 0;
305 }
306
307 int main(int argc, char *argv[])
308 {
309         urandom_fd = open("/dev/urandom", O_RDONLY);
310
311         if (!argv[1]) {
312                 //Usage
313         } else if (!strcmp(argv[1], "eckey")) {
314                 return dokey(false, argv+2);
315         } else if (!strcmp(argv[1], "rsakey")) {
316                 return dokey(true, argv+2);
317         } else if (!strcmp(argv[1], "selfsigned")) {
318                 return selfsigned(argv+2);
319         }
320
321         fprintf(stderr,
322                 "PX5G X.509 Certificate Generator Utility v" PX5G_VERSION "\n" PX5G_COPY
323                 "\nbased on PolarSSL by Christophe Devine and Paul Bakker\n\n");
324         fprintf(stderr, "Usage: %s [eckey|rsakey|selfsigned]\n", *argv);
325         return 1;
326 }