xref: /rk3399_ARM-atf/tools/cert_create/src/main.c (revision e264b5573952c72805a14e69e438168c00163e9a)
1 /*
2  * Copyright (c) 2015-2024, 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 static const char build_msg[] = "Built : " __TIME__ ", " __DATE__;
70 static const char platform_msg[] = PLAT_MSG;
71 
72 static char *strdup(const char *str)
73 {
74 	int n = strlen(str) + 1;
75 	char *dup = malloc(n);
76 	if (dup) {
77 		strcpy(dup, str);
78 	}
79 	return dup;
80 }
81 
82 static const char *key_algs_str[] = {
83 	[KEY_ALG_RSA] = "rsa",
84 #ifndef OPENSSL_NO_EC
85 	[KEY_ALG_ECDSA_NIST] = "ecdsa",
86 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
87 	[KEY_ALG_ECDSA_BRAINPOOL_R] = "ecdsa-brainpool-regular",
88 	[KEY_ALG_ECDSA_BRAINPOOL_T] = "ecdsa-brainpool-twisted",
89 #endif
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 
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 
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 
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 
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 
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 #if OPENSSL_VERSION_NUMBER >= 0x10100000L
274 		"'ecdsa', 'ecdsa-brainpool-regular', 'ecdsa-brainpool-twisted'"
275 #else
276 		"'ecdsa'"
277 #endif
278 	},
279 	{
280 		{ "key-size", required_argument, NULL, 'b' },
281 		"Key size (for supported algorithms)."
282 	},
283 	{
284 		{ "hash-alg", required_argument, NULL, 's' },
285 		"Hash algorithm : 'sha256' (default), 'sha384', 'sha512'"
286 	},
287 	{
288 		{ "save-keys", no_argument, NULL, 'k' },
289 		"Save key pairs into files. Filenames must be provided"
290 	},
291 	{
292 		{ "new-keys", no_argument, NULL, 'n' },
293 		"Generate new key pairs if no key files are provided"
294 	},
295 	{
296 		{ "print-cert", no_argument, NULL, 'p' },
297 		"Print the certificates in the standard output"
298 	}
299 };
300 
301 int main(int argc, char *argv[])
302 {
303 	STACK_OF(X509_EXTENSION) * sk;
304 	X509_EXTENSION *cert_ext = NULL;
305 	ext_t *ext;
306 	key_t *key;
307 	cert_t *cert;
308 	FILE *file;
309 	int i, j, ext_nid, nvctr;
310 	int c, opt_idx = 0;
311 	const struct option *cmd_opt;
312 	const char *cur_opt;
313 	unsigned int err_code;
314 	unsigned char md[SHA512_DIGEST_LENGTH];
315 	unsigned int  md_len;
316 	const EVP_MD *md_info;
317 
318 	NOTICE("CoT Generation Tool: %s\n", build_msg);
319 	NOTICE("Target platform: %s\n", platform_msg);
320 
321 	/* Set default options */
322 	key_alg = KEY_ALG_RSA;
323 	hash_alg = HASH_ALG_SHA256;
324 	key_size = -1;
325 
326 	/* Add common command line options */
327 	for (i = 0; i < NUM_ELEM(common_cmd_opt); i++) {
328 		cmd_opt_add(&common_cmd_opt[i]);
329 	}
330 
331 	/* Initialize the certificates */
332 	if (cert_init() != 0) {
333 		ERROR("Cannot initialize certificates\n");
334 		exit(1);
335 	}
336 
337 	/* Initialize the keys */
338 	if (key_init() != 0) {
339 		ERROR("Cannot initialize keys\n");
340 		exit(1);
341 	}
342 
343 	/* Initialize the new types and register OIDs for the extensions */
344 	if (ext_init() != 0) {
345 		ERROR("Cannot initialize extensions\n");
346 		exit(1);
347 	}
348 
349 	/* Get the command line options populated during the initialization */
350 	cmd_opt = cmd_opt_get_array();
351 
352 	while (1) {
353 		/* getopt_long stores the option index here. */
354 		c = getopt_long(argc, argv, "a:b:hknps:", cmd_opt, &opt_idx);
355 
356 		/* Detect the end of the options. */
357 		if (c == -1) {
358 			break;
359 		}
360 
361 		switch (c) {
362 		case 'a':
363 			key_alg = get_key_alg(optarg);
364 			if (key_alg < 0) {
365 				ERROR("Invalid key algorithm '%s'\n", optarg);
366 				exit(1);
367 			}
368 			break;
369 		case 'b':
370 			key_size = get_key_size(optarg);
371 			if (key_size <= 0) {
372 				ERROR("Invalid key size '%s'\n", optarg);
373 				exit(1);
374 			}
375 			break;
376 		case 'h':
377 			print_help(argv[0], cmd_opt);
378 			exit(0);
379 		case 'k':
380 			save_keys = 1;
381 			break;
382 		case 'n':
383 			new_keys = 1;
384 			break;
385 		case 'p':
386 			print_cert = 1;
387 			break;
388 		case 's':
389 			hash_alg = get_hash_alg(optarg);
390 			if (hash_alg < 0) {
391 				ERROR("Invalid hash algorithm '%s'\n", optarg);
392 				exit(1);
393 			}
394 			break;
395 		case CMD_OPT_EXT:
396 			cur_opt = cmd_opt_get_name(opt_idx);
397 			ext = ext_get_by_opt(cur_opt);
398 			ext->arg = strdup(optarg);
399 			break;
400 		case CMD_OPT_KEY:
401 			cur_opt = cmd_opt_get_name(opt_idx);
402 			key = key_get_by_opt(cur_opt);
403 			key->fn = strdup(optarg);
404 			break;
405 		case CMD_OPT_CERT:
406 			cur_opt = cmd_opt_get_name(opt_idx);
407 			cert = cert_get_by_opt(cur_opt);
408 			cert->fn = strdup(optarg);
409 			break;
410 		case '?':
411 		default:
412 			print_help(argv[0], cmd_opt);
413 			exit(1);
414 		}
415 	}
416 
417 	/* Select a reasonable default key-size */
418 	if (key_size == -1) {
419 		key_size = KEY_SIZES[key_alg][0];
420 	}
421 
422 	/* Check command line arguments */
423 	check_cmd_params();
424 
425 	/* Indicate SHA as image hash algorithm in the certificate
426 	 * extension */
427 	if (hash_alg == HASH_ALG_SHA384) {
428 		md_info = EVP_sha384();
429 		md_len  = SHA384_DIGEST_LENGTH;
430 	} else if (hash_alg == HASH_ALG_SHA512) {
431 		md_info = EVP_sha512();
432 		md_len  = SHA512_DIGEST_LENGTH;
433 	} else {
434 		md_info = EVP_sha256();
435 		md_len  = SHA256_DIGEST_LENGTH;
436 	}
437 
438 	/* Load private keys from files (or generate new ones) */
439 	for (i = 0 ; i < num_keys ; i++) {
440 #if !USING_OPENSSL3
441 		if (!key_new(&keys[i])) {
442 			ERROR("Failed to allocate key container\n");
443 			exit(1);
444 		}
445 #endif
446 
447 		/* First try to load the key from disk */
448 		err_code = key_load(&keys[i]);
449 		if (err_code == KEY_ERR_NONE) {
450 			/* Key loaded successfully */
451 			continue;
452 		}
453 
454 		/* Key not loaded. Check the error code */
455 		if (err_code == KEY_ERR_LOAD) {
456 			/* File exists, but it does not contain a valid private
457 			 * key. Abort. */
458 			ERROR("Error loading '%s'\n", keys[i].fn);
459 			exit(1);
460 		}
461 
462 		/* File does not exist, could not be opened or no filename was
463 		 * given */
464 		if (new_keys) {
465 			/* Try to create a new key */
466 			NOTICE("Creating new key for '%s'\n", keys[i].desc);
467 			if (!key_create(&keys[i], key_alg, key_size)) {
468 				ERROR("Error creating key '%s'\n", keys[i].desc);
469 				exit(1);
470 			}
471 		} else {
472 			if (err_code == KEY_ERR_OPEN) {
473 				ERROR("Error opening '%s'\n", keys[i].fn);
474 			} else {
475 				ERROR("Key '%s' not specified\n", keys[i].desc);
476 			}
477 			exit(1);
478 		}
479 	}
480 
481 	/* Create the certificates */
482 	for (i = 0 ; i < num_certs ; i++) {
483 
484 		cert = &certs[i];
485 
486 		if (cert->fn == NULL) {
487 			/* Certificate not requested. Skip to the next one */
488 			continue;
489 		}
490 
491 		/* Create a new stack of extensions. This stack will be used
492 		 * to create the certificate */
493 		CHECK_NULL(sk, sk_X509_EXTENSION_new_null());
494 
495 		for (j = 0 ; j < cert->num_ext ; j++) {
496 
497 			ext = &extensions[cert->ext[j]];
498 
499 			/* Get OpenSSL internal ID for this extension */
500 			CHECK_OID(ext_nid, ext->oid);
501 
502 			/*
503 			 * Three types of extensions are currently supported:
504 			 *     - EXT_TYPE_NVCOUNTER
505 			 *     - EXT_TYPE_HASH
506 			 *     - EXT_TYPE_PKEY
507 			 */
508 			switch (ext->type) {
509 			case EXT_TYPE_NVCOUNTER:
510 				if (ext->optional && ext->arg == NULL) {
511 					/* Skip this NVCounter */
512 					continue;
513 				} else {
514 					/* Checked by `check_cmd_params` */
515 					assert(ext->arg != NULL);
516 					nvctr = atoi(ext->arg);
517 					CHECK_NULL(cert_ext, ext_new_nvcounter(ext_nid,
518 						EXT_CRIT, nvctr));
519 				}
520 				break;
521 			case EXT_TYPE_HASH:
522 				if (ext->arg == NULL) {
523 					if (ext->optional) {
524 						/* Include a hash filled with zeros */
525 						memset(md, 0x0, SHA512_DIGEST_LENGTH);
526 					} else {
527 						/* Do not include this hash in the certificate */
528 						continue;
529 					}
530 				} else {
531 					/* Calculate the hash of the file */
532 					if (!sha_file(hash_alg, ext->arg, md)) {
533 						ERROR("Cannot calculate hash of %s\n",
534 							ext->arg);
535 						exit(1);
536 					}
537 				}
538 				CHECK_NULL(cert_ext, ext_new_hash(ext_nid,
539 						EXT_CRIT, md_info, md,
540 						md_len));
541 				break;
542 			case EXT_TYPE_PKEY:
543 				CHECK_NULL(cert_ext, ext_new_key(ext_nid,
544 					EXT_CRIT, keys[ext->attr.key].key));
545 				break;
546 			default:
547 				ERROR("Unknown extension type '%d' in %s\n",
548 						ext->type, cert->cn);
549 				exit(1);
550 			}
551 
552 			/* Push the extension into the stack */
553 			sk_X509_EXTENSION_push(sk, cert_ext);
554 		}
555 
556 		/* Create certificate. Signed with corresponding key */
557 		if (!cert_new(hash_alg, cert, VAL_DAYS, 0, sk)) {
558 			ERROR("Cannot create %s\n", cert->cn);
559 			exit(1);
560 		}
561 
562 		for (cert_ext = sk_X509_EXTENSION_pop(sk); cert_ext != NULL;
563 				cert_ext = sk_X509_EXTENSION_pop(sk)) {
564 			X509_EXTENSION_free(cert_ext);
565 		}
566 
567 		sk_X509_EXTENSION_free(sk);
568 	}
569 
570 
571 	/* Print the certificates */
572 	if (print_cert) {
573 		for (i = 0 ; i < num_certs ; i++) {
574 			if (!certs[i].x) {
575 				continue;
576 			}
577 			printf("\n\n=====================================\n\n");
578 			X509_print_fp(stdout, certs[i].x);
579 		}
580 	}
581 
582 	/* Save created certificates to files */
583 	for (i = 0 ; i < num_certs ; i++) {
584 		if (certs[i].x && certs[i].fn) {
585 			file = fopen(certs[i].fn, "w");
586 			if (file != NULL) {
587 				i2d_X509_fp(file, certs[i].x);
588 				fclose(file);
589 			} else {
590 				ERROR("Cannot create file %s\n", certs[i].fn);
591 			}
592 		}
593 	}
594 
595 	/* Save keys */
596 	if (save_keys) {
597 		for (i = 0 ; i < num_keys ; i++) {
598 			if (!key_store(&keys[i])) {
599 				ERROR("Cannot save %s\n", keys[i].desc);
600 			}
601 		}
602 	}
603 
604 	/* If we got here, then we must have filled the key array completely.
605 	 * We can then safely call free on all of the keys in the array
606 	 */
607 	key_cleanup();
608 
609 #ifndef OPENSSL_NO_ENGINE
610 	ENGINE_cleanup();
611 #endif
612 	CRYPTO_cleanup_all_ex_data();
613 
614 
615 	/* We allocated strings through strdup, so now we have to free them */
616 
617 	ext_cleanup();
618 
619 	cert_cleanup();
620 
621 	return 0;
622 }
623