• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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