1 /*
2 * Copyright (c) 2015-2024, Arm Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #define _POSIX_C_SOURCE 200809L
8
9 #include <assert.h>
10 #include <ctype.h>
11 #include <getopt.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <stdbool.h>
16
17 #include <openssl/conf.h>
18 #include <openssl/engine.h>
19 #include <openssl/err.h>
20 #include <openssl/pem.h>
21 #include <openssl/sha.h>
22 #include <openssl/x509v3.h>
23
24 #include "cert.h"
25 #include "cmd_opt.h"
26 #include "debug.h"
27 #include "ext.h"
28 #include "key.h"
29 #include "sha.h"
30
31 /*
32 * Helper macros to simplify the code. This macro assigns the return value of
33 * the 'fn' function to 'v' and exits if the value is NULL.
34 */
35 #define CHECK_NULL(v, fn) \
36 do { \
37 v = fn; \
38 if (v == NULL) { \
39 ERROR("NULL object at %s:%d\n", __FILE__, __LINE__); \
40 exit(1); \
41 } \
42 } while (0)
43
44 /*
45 * This macro assigns the NID corresponding to 'oid' to 'v' and exits if the
46 * NID is undefined.
47 */
48 #define CHECK_OID(v, oid) \
49 do { \
50 v = OBJ_txt2nid(oid); \
51 if (v == NID_undef) { \
52 ERROR("Cannot find extension %s\n", oid); \
53 exit(1); \
54 } \
55 } while (0)
56
57 #define MAX_FILENAME_LEN 1024
58 #define VAL_DAYS 7300
59 #define ID_TO_BIT_MASK(id) (1 << id)
60 #define NUM_ELEM(x) ((sizeof(x)) / (sizeof(x[0])))
61 #define HELP_OPT_MAX_LEN 128
62
63 /* Global options */
64 static int key_alg;
65 static int hash_alg;
66 static int key_size;
67 static int new_keys;
68 static int save_keys;
69 static int print_cert;
70
71 static const char build_msg[] = "Built : " __TIME__ ", " __DATE__;
72 static const char platform_msg[] = PLAT_MSG;
73
74 static const char *key_algs_str[] = {
75 [KEY_ALG_RSA] = "rsa",
76 #ifndef OPENSSL_NO_EC
77 [KEY_ALG_ECDSA_NIST] = "ecdsa",
78 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
79 [KEY_ALG_ECDSA_BRAINPOOL_R] = "ecdsa-brainpool-regular",
80 [KEY_ALG_ECDSA_BRAINPOOL_T] = "ecdsa-brainpool-twisted",
81 #endif
82 #endif /* OPENSSL_NO_EC */
83 };
84
85 static const char *hash_algs_str[] = {
86 [HASH_ALG_SHA256] = "sha256",
87 [HASH_ALG_SHA384] = "sha384",
88 [HASH_ALG_SHA512] = "sha512",
89 };
90
print_help(const char * cmd,const struct option * long_opt)91 static void print_help(const char *cmd, const struct option *long_opt)
92 {
93 int rem, i = 0;
94 const struct option *opt;
95 char line[HELP_OPT_MAX_LEN];
96 char *p;
97
98 assert(cmd != NULL);
99 assert(long_opt != NULL);
100
101 printf("\n\n");
102 printf("The certificate generation tool loads the binary images and\n"
103 "optionally the RSA or ECC keys, and outputs the key and content\n"
104 "certificates properly signed to implement the chain of trust.\n"
105 "If keys are provided, they must be in PEM format.\n"
106 "Certificates are generated in DER format.\n");
107 printf("\n");
108 printf("Usage:\n");
109 printf("\t%s [OPTIONS]\n\n", cmd);
110
111 printf("Available options:\n");
112 opt = long_opt;
113 while (opt->name) {
114 p = line;
115 rem = HELP_OPT_MAX_LEN;
116 if (isalpha(opt->val)) {
117 /* Short format */
118 sprintf(p, "-%c,", (char)opt->val);
119 p += 3;
120 rem -= 3;
121 }
122 snprintf(p, rem, "--%s %s", opt->name,
123 (opt->has_arg == required_argument) ? "<arg>" : "");
124 printf("\t%-32s %s\n", line, cmd_opt_get_help_msg(i));
125 opt++;
126 i++;
127 }
128 printf("\n");
129 }
130
get_key_alg(const char * key_alg_str)131 static int get_key_alg(const char *key_alg_str)
132 {
133 int i;
134
135 for (i = 0 ; i < NUM_ELEM(key_algs_str) ; i++) {
136 if (0 == strcmp(key_alg_str, key_algs_str[i])) {
137 return i;
138 }
139 }
140
141 return -1;
142 }
143
get_key_size(const char * key_size_str)144 static int get_key_size(const char *key_size_str)
145 {
146 char *end;
147 long key_size;
148
149 key_size = strtol(key_size_str, &end, 10);
150 if (*end != '\0')
151 return -1;
152
153 return key_size;
154 }
155
get_hash_alg(const char * hash_alg_str)156 static int get_hash_alg(const char *hash_alg_str)
157 {
158 int i;
159
160 for (i = 0 ; i < NUM_ELEM(hash_algs_str) ; i++) {
161 if (0 == strcmp(hash_alg_str, hash_algs_str[i])) {
162 return i;
163 }
164 }
165
166 return -1;
167 }
168
check_cmd_params(void)169 static void check_cmd_params(void)
170 {
171 cert_t *cert;
172 ext_t *ext;
173 cert_key_t *key;
174 int i, j;
175 bool valid_size;
176
177 /* Only save new keys */
178 if (save_keys && !new_keys) {
179 ERROR("Only new keys can be saved to disk\n");
180 exit(1);
181 }
182
183 /* Validate key-size */
184 valid_size = false;
185 for (i = 0; i < KEY_SIZE_MAX_NUM; i++) {
186 if (key_size == KEY_SIZES[key_alg][i]) {
187 valid_size = true;
188 break;
189 }
190 }
191 if (!valid_size) {
192 ERROR("'%d' is not a valid key size for '%s'\n",
193 key_size, key_algs_str[key_alg]);
194 NOTICE("Valid sizes are: ");
195 for (i = 0; i < KEY_SIZE_MAX_NUM &&
196 KEY_SIZES[key_alg][i] != 0; i++) {
197 printf("%d ", KEY_SIZES[key_alg][i]);
198 }
199 printf("\n");
200 exit(1);
201 }
202
203 /* Check that all required options have been specified in the
204 * command line */
205 for (i = 0; i < num_certs; i++) {
206 cert = &certs[i];
207 if (cert->fn == NULL) {
208 /* Certificate not requested. Skip to the next one */
209 continue;
210 }
211
212 /* Check that all parameters required to create this certificate
213 * have been specified in the command line */
214 for (j = 0; j < cert->num_ext; j++) {
215 ext = &extensions[cert->ext[j]];
216 switch (ext->type) {
217 case EXT_TYPE_NVCOUNTER:
218 /* Counter value must be specified */
219 if ((!ext->optional) && (ext->arg == NULL)) {
220 ERROR("Value for '%s' not specified\n",
221 ext->ln);
222 exit(1);
223 }
224 break;
225 case EXT_TYPE_PKEY:
226 /* Key filename must be specified */
227 key = &keys[ext->attr.key];
228 if (!new_keys && key->fn == NULL) {
229 ERROR("Key '%s' required by '%s' not "
230 "specified\n", key->desc,
231 cert->cn);
232 exit(1);
233 }
234 break;
235 case EXT_TYPE_HASH:
236 /*
237 * Binary image must be specified
238 * unless it is explicitly made optional.
239 */
240 if ((!ext->optional) && (ext->arg == NULL)) {
241 ERROR("Image for '%s' not specified\n",
242 ext->ln);
243 exit(1);
244 }
245 break;
246 default:
247 ERROR("Unknown extension type '%d' in '%s'\n",
248 ext->type, ext->ln);
249 exit(1);
250 break;
251 }
252 }
253 }
254 }
255
256 /* Common command line options */
257 static const cmd_opt_t common_cmd_opt[] = {
258 {
259 { "help", no_argument, NULL, 'h' },
260 "Print this message and exit"
261 },
262 {
263 { "key-alg", required_argument, NULL, 'a' },
264 "Key algorithm: 'rsa' (default)- RSAPSS scheme as per PKCS#1 v2.1, "
265 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
266 "'ecdsa', 'ecdsa-brainpool-regular', 'ecdsa-brainpool-twisted'"
267 #else
268 "'ecdsa'"
269 #endif
270 },
271 {
272 { "key-size", required_argument, NULL, 'b' },
273 "Key size (for supported algorithms)."
274 },
275 {
276 { "hash-alg", required_argument, NULL, 's' },
277 "Hash algorithm : 'sha256' (default), 'sha384', 'sha512'"
278 },
279 {
280 { "save-keys", no_argument, NULL, 'k' },
281 "Save key pairs into files. Filenames must be provided"
282 },
283 {
284 { "new-keys", no_argument, NULL, 'n' },
285 "Generate new key pairs if no key files are provided"
286 },
287 {
288 { "print-cert", no_argument, NULL, 'p' },
289 "Print the certificates in the standard output"
290 }
291 };
292
main(int argc,char * argv[])293 int main(int argc, char *argv[])
294 {
295 STACK_OF(X509_EXTENSION) * sk;
296 X509_EXTENSION *cert_ext = NULL;
297 ext_t *ext;
298 cert_key_t *key;
299 cert_t *cert;
300 FILE *file;
301 int i, j, ext_nid, nvctr;
302 int c, opt_idx = 0;
303 const struct option *cmd_opt;
304 const char *cur_opt;
305 unsigned int err_code;
306 unsigned char md[SHA512_DIGEST_LENGTH];
307 unsigned int md_len;
308 const EVP_MD *md_info;
309
310 NOTICE("CoT Generation Tool: %s\n", build_msg);
311 NOTICE("Target platform: %s\n", platform_msg);
312
313 /* Set default options */
314 key_alg = KEY_ALG_RSA;
315 hash_alg = HASH_ALG_SHA256;
316 key_size = -1;
317
318 /* Add common command line options */
319 for (i = 0; i < NUM_ELEM(common_cmd_opt); i++) {
320 cmd_opt_add(&common_cmd_opt[i]);
321 }
322
323 /* Initialize the certificates */
324 if (cert_init() != 0) {
325 ERROR("Cannot initialize certificates\n");
326 exit(1);
327 }
328
329 /* Initialize the keys */
330 if (key_init() != 0) {
331 ERROR("Cannot initialize keys\n");
332 exit(1);
333 }
334
335 /* Initialize the new types and register OIDs for the extensions */
336 if (ext_init() != 0) {
337 ERROR("Cannot initialize extensions\n");
338 exit(1);
339 }
340
341 /* Get the command line options populated during the initialization */
342 cmd_opt = cmd_opt_get_array();
343
344 while (1) {
345 /* getopt_long stores the option index here. */
346 c = getopt_long(argc, argv, "a:b:hknps:", cmd_opt, &opt_idx);
347
348 /* Detect the end of the options. */
349 if (c == -1) {
350 break;
351 }
352
353 switch (c) {
354 case 'a':
355 key_alg = get_key_alg(optarg);
356 if (key_alg < 0) {
357 ERROR("Invalid key algorithm '%s'\n", optarg);
358 exit(1);
359 }
360 break;
361 case 'b':
362 key_size = get_key_size(optarg);
363 if (key_size <= 0) {
364 ERROR("Invalid key size '%s'\n", optarg);
365 exit(1);
366 }
367 break;
368 case 'h':
369 print_help(argv[0], cmd_opt);
370 exit(0);
371 case 'k':
372 save_keys = 1;
373 break;
374 case 'n':
375 new_keys = 1;
376 break;
377 case 'p':
378 print_cert = 1;
379 break;
380 case 's':
381 hash_alg = get_hash_alg(optarg);
382 if (hash_alg < 0) {
383 ERROR("Invalid hash algorithm '%s'\n", optarg);
384 exit(1);
385 }
386 break;
387 case CMD_OPT_EXT:
388 cur_opt = cmd_opt_get_name(opt_idx);
389 ext = ext_get_by_opt(cur_opt);
390 ext->arg = strdup(optarg);
391 break;
392 case CMD_OPT_KEY:
393 cur_opt = cmd_opt_get_name(opt_idx);
394 key = key_get_by_opt(cur_opt);
395 key->fn = strdup(optarg);
396 break;
397 case CMD_OPT_CERT:
398 cur_opt = cmd_opt_get_name(opt_idx);
399 cert = cert_get_by_opt(cur_opt);
400 cert->fn = strdup(optarg);
401 break;
402 case '?':
403 default:
404 print_help(argv[0], cmd_opt);
405 exit(1);
406 }
407 }
408
409 /* Select a reasonable default key-size */
410 if (key_size == -1) {
411 key_size = KEY_SIZES[key_alg][0];
412 }
413
414 /* Check command line arguments */
415 check_cmd_params();
416
417 /* Indicate SHA as image hash algorithm in the certificate
418 * extension */
419 if (hash_alg == HASH_ALG_SHA384) {
420 md_info = EVP_sha384();
421 md_len = SHA384_DIGEST_LENGTH;
422 } else if (hash_alg == HASH_ALG_SHA512) {
423 md_info = EVP_sha512();
424 md_len = SHA512_DIGEST_LENGTH;
425 } else {
426 md_info = EVP_sha256();
427 md_len = SHA256_DIGEST_LENGTH;
428 }
429
430 /* Load private keys from files (or generate new ones) */
431 for (i = 0 ; i < num_keys ; i++) {
432 #if !USING_OPENSSL3
433 if (!key_new(&keys[i])) {
434 ERROR("Failed to allocate key container\n");
435 exit(1);
436 }
437 #endif
438
439 /* First try to load the key from disk */
440 err_code = key_load(&keys[i]);
441 if (err_code == KEY_ERR_NONE) {
442 /* Key loaded successfully */
443 continue;
444 }
445
446 /* Key not loaded. Check the error code */
447 if (err_code == KEY_ERR_LOAD) {
448 /* File exists, but it does not contain a valid private
449 * key. Abort. */
450 ERROR("Error loading '%s'\n", keys[i].fn);
451 exit(1);
452 }
453
454 /* File does not exist, could not be opened or no filename was
455 * given */
456 if (new_keys) {
457 /* Try to create a new key */
458 NOTICE("Creating new key for '%s'\n", keys[i].desc);
459 if (!key_create(&keys[i], key_alg, key_size)) {
460 ERROR("Error creating key '%s'\n", keys[i].desc);
461 exit(1);
462 }
463 } else {
464 if (err_code == KEY_ERR_OPEN) {
465 ERROR("Error opening '%s'\n", keys[i].fn);
466 } else {
467 ERROR("Key '%s' not specified\n", keys[i].desc);
468 }
469 exit(1);
470 }
471 }
472
473 /* Create the certificates */
474 for (i = 0 ; i < num_certs ; i++) {
475
476 cert = &certs[i];
477
478 if (cert->fn == NULL) {
479 /* Certificate not requested. Skip to the next one */
480 continue;
481 }
482
483 /* Create a new stack of extensions. This stack will be used
484 * to create the certificate */
485 CHECK_NULL(sk, sk_X509_EXTENSION_new_null());
486
487 for (j = 0 ; j < cert->num_ext ; j++) {
488
489 ext = &extensions[cert->ext[j]];
490
491 /* Get OpenSSL internal ID for this extension */
492 CHECK_OID(ext_nid, ext->oid);
493
494 /*
495 * Three types of extensions are currently supported:
496 * - EXT_TYPE_NVCOUNTER
497 * - EXT_TYPE_HASH
498 * - EXT_TYPE_PKEY
499 */
500 switch (ext->type) {
501 case EXT_TYPE_NVCOUNTER:
502 if (ext->optional && ext->arg == NULL) {
503 /* Skip this NVCounter */
504 continue;
505 } else {
506 /* Checked by `check_cmd_params` */
507 assert(ext->arg != NULL);
508 nvctr = atoi(ext->arg);
509 CHECK_NULL(cert_ext, ext_new_nvcounter(ext_nid,
510 EXT_CRIT, nvctr));
511 }
512 break;
513 case EXT_TYPE_HASH:
514 if (ext->arg == NULL) {
515 if (ext->optional) {
516 /* Include a hash filled with zeros */
517 memset(md, 0x0, SHA512_DIGEST_LENGTH);
518 } else {
519 /* Do not include this hash in the certificate */
520 continue;
521 }
522 } else {
523 /* Calculate the hash of the file */
524 if (!sha_file(hash_alg, ext->arg, md)) {
525 ERROR("Cannot calculate hash of %s\n",
526 ext->arg);
527 exit(1);
528 }
529 }
530 CHECK_NULL(cert_ext, ext_new_hash(ext_nid,
531 EXT_CRIT, md_info, md,
532 md_len));
533 break;
534 case EXT_TYPE_PKEY:
535 CHECK_NULL(cert_ext, ext_new_key(ext_nid,
536 EXT_CRIT, keys[ext->attr.key].key));
537 break;
538 default:
539 ERROR("Unknown extension type '%d' in %s\n",
540 ext->type, cert->cn);
541 exit(1);
542 }
543
544 /* Push the extension into the stack */
545 sk_X509_EXTENSION_push(sk, cert_ext);
546 }
547
548 /* Create certificate. Signed with corresponding key */
549 if (!cert_new(hash_alg, cert, VAL_DAYS, 0, sk)) {
550 ERROR("Cannot create %s\n", cert->cn);
551 exit(1);
552 }
553
554 for (cert_ext = sk_X509_EXTENSION_pop(sk); cert_ext != NULL;
555 cert_ext = sk_X509_EXTENSION_pop(sk)) {
556 X509_EXTENSION_free(cert_ext);
557 }
558
559 sk_X509_EXTENSION_free(sk);
560 }
561
562
563 /* Print the certificates */
564 if (print_cert) {
565 for (i = 0 ; i < num_certs ; i++) {
566 if (!certs[i].x) {
567 continue;
568 }
569 printf("\n\n=====================================\n\n");
570 X509_print_fp(stdout, certs[i].x);
571 }
572 }
573
574 /* Save created certificates to files */
575 for (i = 0 ; i < num_certs ; i++) {
576 if (certs[i].x && certs[i].fn) {
577 file = fopen(certs[i].fn, "w");
578 if (file != NULL) {
579 i2d_X509_fp(file, certs[i].x);
580 fclose(file);
581 } else {
582 ERROR("Cannot create file %s\n", certs[i].fn);
583 }
584 }
585 }
586
587 /* Save keys */
588 if (save_keys) {
589 for (i = 0 ; i < num_keys ; i++) {
590 if (!key_store(&keys[i])) {
591 ERROR("Cannot save %s\n", keys[i].desc);
592 }
593 }
594 }
595
596 /* If we got here, then we must have filled the key array completely.
597 * We can then safely call free on all of the keys in the array
598 */
599 key_cleanup();
600
601 #ifndef OPENSSL_NO_ENGINE
602 ENGINE_cleanup();
603 #endif
604 CRYPTO_cleanup_all_ex_data();
605
606
607 /* We allocated strings through strdup, so now we have to free them */
608
609 ext_cleanup();
610
611 cert_cleanup();
612
613 return 0;
614 }
615