ucode-mod-pkgen: add ucode module for generating crypto keys/certificates
[openwrt/staging/nbd.git] / package / utils / ucode-mod-pkgen / src / ucode.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3 * Copyright (C) 2024 Felix Fietkau <nbd@nbd.name>
4 */
5 #include <sys/types.h>
6 #include <sys/random.h>
7
8 #include <stdint.h>
9 #include <limits.h>
10 #include <errno.h>
11
12 #include <mbedtls/entropy.h>
13 #include <mbedtls/x509_crt.h>
14 #include <mbedtls/ecp.h>
15 #include <mbedtls/rsa.h>
16
17 #include <ucode/module.h>
18
19 #include "pk.h"
20
21 /* mbedtls < 3.x compat */
22 #ifdef MBEDTLS_LEGACY
23 #define mbedtls_pk_parse_key(pk, key, keylen, passwd, passwdlen, random, random_ctx) \
24 mbedtls_pk_parse_key(pk, key, keylen, passwd, passwdlen)
25 #endif
26
27 static uc_resource_type_t *uc_pk_type, *uc_crt_type;
28 static uc_value_t *registry;
29 char buf[32 * 1024];
30 int mbedtls_errno;
31
32 struct uc_cert_wr {
33 mbedtls_x509write_cert crt; /* must be first */
34 mbedtls_mpi mpi;
35 unsigned int reg;
36 };
37
38 static unsigned int uc_reg_add(uc_value_t *val)
39 {
40 size_t i = 0;
41
42 while (ucv_array_get(registry, i))
43 i++;
44
45 ucv_array_set(registry, i, ucv_get(val));
46
47 return i;
48 }
49
50 int random_cb(void *ctx, unsigned char *out, size_t len)
51 {
52 #ifdef linux
53 if (getrandom(out, len, 0) != (ssize_t) len)
54 return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
55 #else
56 static FILE *f;
57
58 if (!f)
59 f = fopen("/dev/urandom", "r");
60 if (fread(out, len, 1, f) != 1)
61 return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
62 #endif
63
64 return 0;
65 }
66
67 int64_t get_int_arg(uc_value_t *obj, const char *key, int64_t defval)
68 {
69 uc_value_t *uval = ucv_object_get(obj, key, NULL);
70 int64_t val;
71
72 if (!uval)
73 return defval;
74
75 val = ucv_int64_get(uval);
76 if (errno || val < 0 || val > INT_MAX)
77 return INT_MIN;
78
79 return val ? val : defval;
80 }
81
82 static int
83 gen_rsa_key(mbedtls_pk_context *pk, uc_value_t *arg)
84 {
85 int64_t key_size, exp;
86
87 key_size = get_int_arg(arg, "size", 2048);
88 exp = get_int_arg(arg, "exponent", 65537);
89 if (key_size < 0 || exp < 0)
90 return -1;
91
92 return mbedtls_rsa_gen_key(mbedtls_pk_rsa(*pk), random_cb, NULL, key_size, exp);
93 }
94
95 static int
96 gen_ec_key(mbedtls_pk_context *pk, uc_value_t *arg)
97 {
98 mbedtls_ecp_group_id curve;
99 const char *c_name;
100 uc_value_t *c_arg;
101
102 c_arg = ucv_object_get(arg, "curve", NULL);
103 if (c_arg && ucv_type(c_arg) != UC_STRING)
104 return -1;
105
106 c_name = ucv_string_get(c_arg);
107 if (!c_name)
108 curve = MBEDTLS_ECP_DP_SECP256R1;
109 else {
110 const mbedtls_ecp_curve_info *curve_info;
111 curve_info = mbedtls_ecp_curve_info_from_name(c_name);
112 if (!curve_info)
113 return MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE;
114
115 curve = curve_info->grp_id;
116 }
117
118 return mbedtls_ecp_gen_key(curve, mbedtls_pk_ec(*pk), random_cb, NULL);
119 }
120
121 static void free_pk(void *pk)
122 {
123 if (!pk)
124 return;
125
126 mbedtls_pk_free(pk);
127 free(pk);
128 }
129
130 static void free_crt(void *ptr)
131 {
132 struct uc_cert_wr *crt = ptr;
133
134 if (!crt)
135 return;
136
137 mbedtls_x509write_crt_free(&crt->crt);
138 mbedtls_mpi_free(&crt->mpi);
139 ucv_array_set(registry, crt->reg, NULL);
140 free(crt);
141 }
142
143 static uc_value_t *
144 uc_generate_key(uc_vm_t *vm, size_t nargs)
145 {
146 uc_value_t *cur, *arg = uc_fn_arg(0);
147 mbedtls_pk_type_t pk_type;
148 mbedtls_pk_context *pk;
149 const char *type;
150 int ret;
151
152 if (ucv_type(arg) != UC_OBJECT)
153 INVALID_ARG();
154
155 cur = ucv_object_get(arg, "type", NULL);
156 type = ucv_string_get(cur);
157 if (!type)
158 INVALID_ARG();
159
160 if (!strcmp(type, "rsa"))
161 pk_type = MBEDTLS_PK_RSA;
162 else if (!strcmp(type, "ec"))
163 pk_type = MBEDTLS_PK_ECKEY;
164 else
165 INVALID_ARG();
166
167 pk = calloc(1, sizeof(*pk));
168 mbedtls_pk_init(pk);
169 mbedtls_pk_setup(pk, mbedtls_pk_info_from_type(pk_type));
170 switch (pk_type) {
171 case MBEDTLS_PK_RSA:
172 ret = C(gen_rsa_key(pk, arg));
173 break;
174 case MBEDTLS_PK_ECKEY:
175 ret = C(gen_ec_key(pk, arg));
176 break;
177 default:
178 ret = -1;
179 }
180
181 if (ret) {
182 free_pk(pk);
183 return NULL;
184 }
185
186 return uc_resource_new(uc_pk_type, pk);
187 }
188
189 static uc_value_t *
190 uc_load_key(uc_vm_t *vm, size_t nargs)
191 {
192 uc_value_t *keystr = uc_fn_arg(0);
193 uc_value_t *pub = uc_fn_arg(1);
194 uc_value_t *passwd = uc_fn_arg(2);
195 mbedtls_pk_context *pk;
196 int ret;
197
198 if (ucv_type(keystr) != UC_STRING ||
199 (pub && ucv_type(pub) != UC_BOOLEAN) ||
200 (passwd && ucv_type(passwd) != UC_STRING))
201 INVALID_ARG();
202
203 pk = calloc(1, sizeof(*pk));
204 mbedtls_pk_init(pk);
205 if (ucv_is_truish(pub))
206 ret = C(mbedtls_pk_parse_public_key(pk, (const uint8_t *)ucv_string_get(keystr),
207 ucv_string_length(keystr) + 1));
208 else
209 ret = C(mbedtls_pk_parse_key(pk, (const uint8_t *)ucv_string_get(keystr),
210 ucv_string_length(keystr) + 1,
211 (const uint8_t *)ucv_string_get(passwd),
212 ucv_string_length(passwd) + 1,
213 random_cb, NULL));
214 if (ret) {
215 free_pk(pk);
216 return NULL;
217 }
218
219 return uc_resource_new(uc_pk_type, pk);
220 }
221
222 static void
223 uc_cert_info_add(uc_value_t *info, const char *name, int len)
224 {
225 uc_value_t *val;
226
227 if (len < 0)
228 return;
229
230 val = ucv_string_new_length(buf, len);
231 ucv_object_add(info, name, ucv_get(val));
232 }
233
234 static void
235 uc_cert_info_add_name(uc_value_t *info, const char *name, mbedtls_x509_name *dn)
236 {
237 int len;
238
239 len = mbedtls_x509_dn_gets(buf, sizeof(buf), dn);
240 uc_cert_info_add(info, name, len);
241 }
242
243 static void
244 uc_cert_info_add_time(uc_value_t *info, const char *name, mbedtls_x509_time *t)
245 {
246 int len;
247
248 len = snprintf(buf, sizeof(buf), "%04d%02d%02d%02d%02d%02d",
249 t->year, t->mon, t->day, t->hour, t->min, t->sec);
250 uc_cert_info_add(info, name, len);
251 }
252
253 static uc_value_t *
254 uc_cert_info(uc_vm_t *vm, size_t nargs)
255 {
256 uc_value_t *arg = uc_fn_arg(0);
257 mbedtls_x509_crt crt, *cur;
258 uc_value_t *ret = NULL;
259
260 if (ucv_type(arg) != UC_STRING)
261 return NULL;
262
263 mbedtls_x509_crt_init(&crt);
264 if (C(mbedtls_x509_crt_parse(&crt, (const void *)ucv_string_get(arg), ucv_string_length(arg) + 1)) < 0)
265 goto out;
266
267 ret = ucv_array_new(vm);
268 for (cur = &crt; cur && cur->version != 0; cur = cur->next) {
269 uc_value_t *info = ucv_object_new(vm);
270 int len;
271
272 ucv_array_push(ret, ucv_get(info));
273 ucv_object_add(info, "version", ucv_int64_new(cur->version));
274
275 uc_cert_info_add_name(info, "issuer", &cur->issuer);
276 uc_cert_info_add_name(info, "subject", &cur->issuer);
277 uc_cert_info_add_time(info, "valid_from", &cur->valid_from);
278 uc_cert_info_add_time(info, "valid_to", &cur->valid_to);
279
280 len = mbedtls_x509_serial_gets(buf, sizeof(buf), &cur->serial);
281 uc_cert_info_add(info, "serial", len);
282 }
283
284 out:
285 mbedtls_x509_crt_free(&crt);
286 return ret;
287 }
288
289 static uc_value_t *
290 uc_pk_pem(uc_vm_t *vm, size_t nargs)
291 {
292 mbedtls_pk_context *pk = uc_fn_thisval("mbedtls.pk");
293 uc_value_t *pub = uc_fn_arg(0);
294
295 if (!pk)
296 return NULL;
297
298 if (ucv_is_truish(pub))
299 CHECK(mbedtls_pk_write_pubkey_pem(pk, (void *)buf, sizeof(buf)));
300 else
301 CHECK(mbedtls_pk_write_key_pem(pk, (void *)buf, sizeof(buf)));
302
303 return ucv_string_new(buf);
304 }
305
306 static uc_value_t *
307 uc_pk_der(uc_vm_t *vm, size_t nargs)
308 {
309 mbedtls_pk_context *pk = uc_fn_thisval("mbedtls.pk");
310 uc_value_t *pub = uc_fn_arg(0);
311 int len;
312
313 if (!pk)
314 return NULL;
315
316 if (ucv_is_truish(pub))
317 len = mbedtls_pk_write_pubkey_der(pk, (void *)buf, sizeof(buf));
318 else
319 len = mbedtls_pk_write_key_der(pk, (void *)buf, sizeof(buf));
320 if (len < 0)
321 CHECK(len);
322
323 return ucv_string_new_length(buf + sizeof(buf) - len, len);
324 }
325
326 static uc_value_t *
327 uc_crt_pem(uc_vm_t *vm, size_t nargs)
328 {
329 mbedtls_x509write_cert *crt = uc_fn_thisval("mbedtls.crt");
330 if (!crt)
331 return NULL;
332
333 CHECK(mbedtls_x509write_crt_pem(crt, (void *)buf, sizeof(buf), random_cb, NULL));
334
335 return ucv_string_new(buf);
336 }
337
338 static uc_value_t *
339 uc_crt_der(uc_vm_t *vm, size_t nargs)
340 {
341 mbedtls_x509write_cert *crt = uc_fn_thisval("mbedtls.crt");
342 int len;
343
344 if (!crt)
345 return NULL;
346
347 len = mbedtls_x509write_crt_der(crt, (void *)buf, sizeof(buf), random_cb, NULL);
348 if (len < 0)
349 CHECK(len);
350
351 return ucv_string_new_length(buf, len);
352 }
353
354 static int
355 uc_cert_set_validity(mbedtls_x509write_cert *crt, uc_value_t *arg)
356 {
357 uc_value_t *from = ucv_array_get(arg, 0);
358 uc_value_t *to = ucv_array_get(arg, 1);
359
360 if (ucv_type(from) != UC_STRING || ucv_type(to) != UC_STRING)
361 return -1;
362
363 return mbedtls_x509write_crt_set_validity(crt, ucv_string_get(from), ucv_string_get(to));
364 }
365
366 static int
367 uc_cert_init(mbedtls_x509write_cert *crt, mbedtls_mpi *mpi, uc_value_t *reg, uc_value_t *arg)
368 {
369 uc_value_t *cur;
370 int64_t serial;
371 int path_len;
372 int version;
373 bool ca;
374
375 mbedtls_mpi_init(mpi);
376 mbedtls_x509write_crt_init(crt);
377 mbedtls_x509write_crt_set_md_alg(crt, MBEDTLS_MD_SHA256);
378
379 ca = ucv_is_truish(ucv_object_get(arg, "ca", NULL));
380 path_len = get_int_arg(arg, "max_pathlen", ca ? -1 : 0);
381 if (path_len < -1)
382 return -1;
383
384 version = get_int_arg(arg, "version", 3);
385 if (version < 0 || version > 3)
386 return -1;
387
388 serial = get_int_arg(arg, "serial", 1);
389 if (serial < 0)
390 return -1;
391
392 mbedtls_mpi_lset(mpi, serial);
393 mbedtls_x509write_crt_set_serial(crt, mpi);
394 mbedtls_x509write_crt_set_version(crt, version - 1);
395 CHECK_INT(mbedtls_x509write_crt_set_basic_constraints(crt, ca, path_len));
396
397 cur = ucv_object_get(arg, "subject_name", NULL);
398 if (ucv_type(cur) == UC_STRING)
399 CHECK_INT(mbedtls_x509write_crt_set_subject_name(crt, ucv_string_get(cur)));
400 else
401 return -1;
402 cur = ucv_object_get(arg, "subject_key", NULL);
403 if (cur) {
404 mbedtls_pk_context *key = ucv_resource_data(cur, "mbedtls.pk");
405 if (!key)
406 return -1;
407
408 ucv_array_set(reg, 0, ucv_get(cur));
409 mbedtls_x509write_crt_set_subject_key(crt, key);
410 mbedtls_x509write_crt_set_subject_key_identifier(crt);
411 } else
412 return -1;
413
414 cur = ucv_object_get(arg, "issuer_name", NULL);
415 if (ucv_type(cur) == UC_STRING)
416 CHECK_INT(mbedtls_x509write_crt_set_issuer_name(crt, ucv_string_get(cur)));
417 else
418 return -1;
419 cur = ucv_object_get(arg, "issuer_key", NULL);
420 if (cur) {
421 mbedtls_pk_context *key = ucv_resource_data(cur, "mbedtls.pk");
422 if (!key)
423 return -1;
424
425 ucv_array_set(reg, 1, ucv_get(cur));
426 mbedtls_x509write_crt_set_issuer_key(crt, key);
427 mbedtls_x509write_crt_set_authority_key_identifier(crt);
428 } else
429 return -1;
430
431 cur = ucv_object_get(arg, "validity", NULL);
432 if (ucv_type(cur) != UC_ARRAY || ucv_array_length(cur) != 2)
433 return -1;
434 if (uc_cert_set_validity(crt, cur))
435 return -1;
436
437 cur = ucv_object_get(arg, "key_usage", NULL);
438 if (ucv_type(cur) == UC_ARRAY) {
439 static const struct {
440 const char *name;
441 uint8_t val;
442 } key_flags[] = {
443 { "digital_signature", MBEDTLS_X509_KU_DIGITAL_SIGNATURE },
444 { "non_repudiation", MBEDTLS_X509_KU_NON_REPUDIATION },
445 { "key_encipherment", MBEDTLS_X509_KU_KEY_ENCIPHERMENT },
446 { "data_encipherment", MBEDTLS_X509_KU_DATA_ENCIPHERMENT },
447 { "key_agreement", MBEDTLS_X509_KU_KEY_AGREEMENT },
448 { "key_cert_sign", MBEDTLS_X509_KU_KEY_CERT_SIGN },
449 { "crl_sign", MBEDTLS_X509_KU_CRL_SIGN },
450 };
451 uint8_t key_usage = 0;
452 size_t len = ucv_array_length(cur);
453
454 for (size_t i = 0; i < len; i++) {
455 uc_value_t *val = ucv_array_get(cur, i);
456 const char *str;
457 size_t k;
458
459 str = ucv_string_get(val);
460 if (!str)
461 return -1;
462
463 for (k = 0; k < ARRAY_SIZE(key_flags); k++)
464 if (!strcmp(str, key_flags[k].name))
465 break;
466 if (k == ARRAY_SIZE(key_flags))
467 return -1;
468
469 key_usage |= key_flags[k].val;
470 }
471 CHECK_INT(mbedtls_x509write_crt_set_key_usage(crt, key_usage));
472 } else if (cur)
473 return -1;
474
475 #ifndef MBEDTLS_LEGACY
476 cur = ucv_object_get(arg, "ext_key_usage", NULL);
477 if (ucv_type(cur) == UC_ARRAY && ucv_array_length(cur)) {
478 static const struct {
479 const char *name;
480 struct mbedtls_asn1_buf val;
481 } key_flags[] = {
482 #define __oid(name, val) \
483 { \
484 name, \
485 { \
486 .tag = MBEDTLS_ASN1_OID, \
487 .len = sizeof(MBEDTLS_OID_##val), \
488 .p = (uint8_t *)MBEDTLS_OID_##val, \
489 } \
490 }
491 __oid("server_auth", SERVER_AUTH),
492 __oid("client_auth", CLIENT_AUTH),
493 __oid("code_signing", CODE_SIGNING),
494 __oid("email_protection", EMAIL_PROTECTION),
495 __oid("time_stamping", TIME_STAMPING),
496 __oid("ocsp_signing", OCSP_SIGNING),
497 __oid("any", ANY_EXTENDED_KEY_USAGE)
498 };
499 struct mbedtls_asn1_sequence *elem;
500 size_t len = ucv_array_length(cur);
501
502 elem = calloc(len, sizeof(*elem));
503 for (size_t i = 0; i < len; i++) {
504 uc_value_t *val = ucv_array_get(cur, i);
505 const char *str;
506 size_t k;
507
508 str = ucv_string_get(val);
509 if (!str)
510 return -1;
511
512 for (k = 0; k < ARRAY_SIZE(key_flags); k++)
513 if (!strcmp(str, key_flags[k].name))
514 break;
515
516 if (k == ARRAY_SIZE(key_flags)) {
517 free(elem);
518 return -1;
519 }
520 elem[i].buf = key_flags[k].val;
521 if (i + 1 < len)
522 elem[i].next = &elem[i + 1];
523 }
524
525 CHECK_INT(mbedtls_x509write_crt_set_ext_key_usage(crt, elem));
526 } else if (cur)
527 return -1;
528 #endif
529
530 return 0;
531 }
532
533 static uc_value_t *
534 uc_generate_cert(uc_vm_t *vm, size_t nargs)
535 {
536 struct uc_cert_wr *crt;
537 uc_value_t *arg = uc_fn_arg(0);
538 uc_value_t *reg;
539
540 if (ucv_type(arg) != UC_OBJECT)
541 return NULL;
542
543 reg = ucv_array_new(vm);
544 crt = calloc(1, sizeof(*crt));
545 if (C(uc_cert_init(&crt->crt, &crt->mpi, reg, arg))) {
546 free(crt);
547 return NULL;
548 }
549
550 crt->reg = uc_reg_add(reg);
551
552 return uc_resource_new(uc_crt_type, crt);
553 }
554
555 static uc_value_t *
556 uc_mbedtls_error(uc_vm_t *vm, size_t nargs)
557 {
558 mbedtls_strerror(mbedtls_errno, buf, sizeof(buf));
559
560 return ucv_string_new(buf);
561 }
562
563 static uc_value_t *
564 uc_mbedtls_errno(uc_vm_t *vm, size_t nargs)
565 {
566 return ucv_int64_new(mbedtls_errno);
567 }
568
569
570 static const uc_function_list_t pk_fns[] = {
571 { "pem", uc_pk_pem },
572 { "der", uc_pk_der },
573 };
574
575 static const uc_function_list_t crt_fns[] = {
576 { "pem", uc_crt_pem },
577 { "der", uc_crt_der },
578 };
579
580 static const uc_function_list_t global_fns[] = {
581 { "load_key", uc_load_key },
582 { "cert_info", uc_cert_info },
583 { "generate_key", uc_generate_key },
584 { "generate_cert", uc_generate_cert },
585 { "generate_pkcs12", uc_generate_pkcs12 },
586 { "errno", uc_mbedtls_errno },
587 { "error", uc_mbedtls_error },
588 };
589
590 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
591 {
592 uc_pk_type = uc_type_declare(vm, "mbedtls.pk", pk_fns, free_pk);
593 uc_crt_type = uc_type_declare(vm, "mbedtls.crt", crt_fns, free_crt);
594 uc_function_list_register(scope, global_fns);
595
596 registry = ucv_array_new(vm);
597 uc_vm_registry_set(vm, "pkgen.registry", registry);
598 }