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