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