xref: /rk3399_ARM-atf/tools/cert_create/src/main.c (revision cf2dd17ddda2f44f5dedddcaf48300d16358597a)
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 
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] = "ecdsa"
88 #endif /* OPENSSL_NO_EC */
89 };
90 
91 static const char *hash_algs_str[] = {
92 	[HASH_ALG_SHA256] = "sha256",
93 	[HASH_ALG_SHA384] = "sha384",
94 	[HASH_ALG_SHA512] = "sha512",
95 };
96 
97 static void print_help(const char *cmd, const struct option *long_opt)
98 {
99 	int rem, i = 0;
100 	const struct option *opt;
101 	char line[HELP_OPT_MAX_LEN];
102 	char *p;
103 
104 	assert(cmd != NULL);
105 	assert(long_opt != NULL);
106 
107 	printf("\n\n");
108 	printf("The certificate generation tool loads the binary images and\n"
109 	       "optionally the RSA keys, and outputs the key and content\n"
110 	       "certificates properly signed to implement the chain of trust.\n"
111 	       "If keys are provided, they must be in PEM format.\n"
112 	       "Certificates are generated in DER format.\n");
113 	printf("\n");
114 	printf("Usage:\n");
115 	printf("\t%s [OPTIONS]\n\n", cmd);
116 
117 	printf("Available options:\n");
118 	opt = long_opt;
119 	while (opt->name) {
120 		p = line;
121 		rem = HELP_OPT_MAX_LEN;
122 		if (isalpha(opt->val)) {
123 			/* Short format */
124 			sprintf(p, "-%c,", (char)opt->val);
125 			p += 3;
126 			rem -= 3;
127 		}
128 		snprintf(p, rem, "--%s %s", opt->name,
129 			 (opt->has_arg == required_argument) ? "<arg>" : "");
130 		printf("\t%-32s %s\n", line, cmd_opt_get_help_msg(i));
131 		opt++;
132 		i++;
133 	}
134 	printf("\n");
135 }
136 
137 static int get_key_alg(const char *key_alg_str)
138 {
139 	int i;
140 
141 	for (i = 0 ; i < NUM_ELEM(key_algs_str) ; i++) {
142 		if (0 == strcmp(key_alg_str, key_algs_str[i])) {
143 			return i;
144 		}
145 	}
146 
147 	return -1;
148 }
149 
150 static int get_key_size(const char *key_size_str)
151 {
152 	char *end;
153 	long key_size;
154 
155 	key_size = strtol(key_size_str, &end, 10);
156 	if (*end != '\0')
157 		return -1;
158 
159 	return key_size;
160 }
161 
162 static int get_hash_alg(const char *hash_alg_str)
163 {
164 	int i;
165 
166 	for (i = 0 ; i < NUM_ELEM(hash_algs_str) ; i++) {
167 		if (0 == strcmp(hash_alg_str, hash_algs_str[i])) {
168 			return i;
169 		}
170 	}
171 
172 	return -1;
173 }
174 
175 static void check_cmd_params(void)
176 {
177 	cert_t *cert;
178 	ext_t *ext;
179 	key_t *key;
180 	int i, j;
181 	bool valid_size;
182 
183 	/* Only save new keys */
184 	if (save_keys && !new_keys) {
185 		ERROR("Only new keys can be saved to disk\n");
186 		exit(1);
187 	}
188 
189 	/* Validate key-size */
190 	valid_size = false;
191 	for (i = 0; i < KEY_SIZE_MAX_NUM; i++) {
192 		if (key_size == KEY_SIZES[key_alg][i]) {
193 			valid_size = true;
194 			break;
195 		}
196 	}
197 	if (!valid_size) {
198 		ERROR("'%d' is not a valid key size for '%s'\n",
199 				key_size, key_algs_str[key_alg]);
200 		NOTICE("Valid sizes are: ");
201 		for (i = 0; i < KEY_SIZE_MAX_NUM &&
202 				KEY_SIZES[key_alg][i] != 0; i++) {
203 			printf("%d ", KEY_SIZES[key_alg][i]);
204 		}
205 		printf("\n");
206 		exit(1);
207 	}
208 
209 	/* Check that all required options have been specified in the
210 	 * command line */
211 	for (i = 0; i < num_certs; i++) {
212 		cert = &certs[i];
213 		if (cert->fn == NULL) {
214 			/* Certificate not requested. Skip to the next one */
215 			continue;
216 		}
217 
218 		/* Check that all parameters required to create this certificate
219 		 * have been specified in the command line */
220 		for (j = 0; j < cert->num_ext; j++) {
221 			ext = &extensions[cert->ext[j]];
222 			switch (ext->type) {
223 			case EXT_TYPE_NVCOUNTER:
224 				/* Counter value must be specified */
225 				if ((!ext->optional) && (ext->arg == NULL)) {
226 					ERROR("Value for '%s' not specified\n",
227 					      ext->ln);
228 					exit(1);
229 				}
230 				break;
231 			case EXT_TYPE_PKEY:
232 				/* Key filename must be specified */
233 				key = &keys[ext->attr.key];
234 				if (!new_keys && key->fn == NULL) {
235 					ERROR("Key '%s' required by '%s' not "
236 					      "specified\n", key->desc,
237 					      cert->cn);
238 					exit(1);
239 				}
240 				break;
241 			case EXT_TYPE_HASH:
242 				/*
243 				 * Binary image must be specified
244 				 * unless it is explicitly made optional.
245 				 */
246 				if ((!ext->optional) && (ext->arg == NULL)) {
247 					ERROR("Image for '%s' not specified\n",
248 					      ext->ln);
249 					exit(1);
250 				}
251 				break;
252 			default:
253 				ERROR("Unknown extension type '%d' in '%s'\n",
254 				      ext->type, ext->ln);
255 				exit(1);
256 				break;
257 			}
258 		}
259 	}
260 }
261 
262 /* Common command line options */
263 static const cmd_opt_t common_cmd_opt[] = {
264 	{
265 		{ "help", no_argument, NULL, 'h' },
266 		"Print this message and exit"
267 	},
268 	{
269 		{ "key-alg", required_argument, NULL, 'a' },
270 		"Key algorithm: 'rsa' (default)- RSAPSS scheme as per PKCS#1 v2.1, 'ecdsa'"
271 	},
272 	{
273 		{ "key-size", required_argument, NULL, 'b' },
274 		"Key size (for supported algorithms)."
275 	},
276 	{
277 		{ "hash-alg", required_argument, NULL, 's' },
278 		"Hash algorithm : 'sha256' (default), 'sha384', 'sha512'"
279 	},
280 	{
281 		{ "save-keys", no_argument, NULL, 'k' },
282 		"Save key pairs into files. Filenames must be provided"
283 	},
284 	{
285 		{ "new-keys", no_argument, NULL, 'n' },
286 		"Generate new key pairs if no key files are provided"
287 	},
288 	{
289 		{ "print-cert", no_argument, NULL, 'p' },
290 		"Print the certificates in the standard output"
291 	}
292 };
293 
294 int main(int argc, char *argv[])
295 {
296 	STACK_OF(X509_EXTENSION) * sk;
297 	X509_EXTENSION *cert_ext = NULL;
298 	ext_t *ext;
299 	key_t *key;
300 	cert_t *cert;
301 	FILE *file;
302 	int i, j, ext_nid, nvctr;
303 	int c, opt_idx = 0;
304 	const struct option *cmd_opt;
305 	const char *cur_opt;
306 	unsigned int err_code;
307 	unsigned char md[SHA512_DIGEST_LENGTH];
308 	unsigned int  md_len;
309 	const EVP_MD *md_info;
310 
311 	NOTICE("CoT Generation Tool: %s\n", build_msg);
312 	NOTICE("Target platform: %s\n", platform_msg);
313 
314 	/* Set default options */
315 	key_alg = KEY_ALG_RSA;
316 	hash_alg = HASH_ALG_SHA256;
317 	key_size = -1;
318 
319 	/* Add common command line options */
320 	for (i = 0; i < NUM_ELEM(common_cmd_opt); i++) {
321 		cmd_opt_add(&common_cmd_opt[i]);
322 	}
323 
324 	/* Initialize the certificates */
325 	if (cert_init() != 0) {
326 		ERROR("Cannot initialize certificates\n");
327 		exit(1);
328 	}
329 
330 	/* Initialize the keys */
331 	if (key_init() != 0) {
332 		ERROR("Cannot initialize keys\n");
333 		exit(1);
334 	}
335 
336 	/* Initialize the new types and register OIDs for the extensions */
337 	if (ext_init() != 0) {
338 		ERROR("Cannot initialize extensions\n");
339 		exit(1);
340 	}
341 
342 	/* Get the command line options populated during the initialization */
343 	cmd_opt = cmd_opt_get_array();
344 
345 	while (1) {
346 		/* getopt_long stores the option index here. */
347 		c = getopt_long(argc, argv, "a:b:hknps:", cmd_opt, &opt_idx);
348 
349 		/* Detect the end of the options. */
350 		if (c == -1) {
351 			break;
352 		}
353 
354 		switch (c) {
355 		case 'a':
356 			key_alg = get_key_alg(optarg);
357 			if (key_alg < 0) {
358 				ERROR("Invalid key algorithm '%s'\n", optarg);
359 				exit(1);
360 			}
361 			break;
362 		case 'b':
363 			key_size = get_key_size(optarg);
364 			if (key_size <= 0) {
365 				ERROR("Invalid key size '%s'\n", optarg);
366 				exit(1);
367 			}
368 			break;
369 		case 'h':
370 			print_help(argv[0], cmd_opt);
371 			exit(0);
372 		case 'k':
373 			save_keys = 1;
374 			break;
375 		case 'n':
376 			new_keys = 1;
377 			break;
378 		case 'p':
379 			print_cert = 1;
380 			break;
381 		case 's':
382 			hash_alg = get_hash_alg(optarg);
383 			if (hash_alg < 0) {
384 				ERROR("Invalid hash algorithm '%s'\n", optarg);
385 				exit(1);
386 			}
387 			break;
388 		case CMD_OPT_EXT:
389 			cur_opt = cmd_opt_get_name(opt_idx);
390 			ext = ext_get_by_opt(cur_opt);
391 			ext->arg = strdup(optarg);
392 			break;
393 		case CMD_OPT_KEY:
394 			cur_opt = cmd_opt_get_name(opt_idx);
395 			key = key_get_by_opt(cur_opt);
396 			key->fn = strdup(optarg);
397 			break;
398 		case CMD_OPT_CERT:
399 			cur_opt = cmd_opt_get_name(opt_idx);
400 			cert = cert_get_by_opt(cur_opt);
401 			cert->fn = strdup(optarg);
402 			break;
403 		case '?':
404 		default:
405 			print_help(argv[0], cmd_opt);
406 			exit(1);
407 		}
408 	}
409 
410 	/* Select a reasonable default key-size */
411 	if (key_size == -1) {
412 		key_size = KEY_SIZES[key_alg][0];
413 	}
414 
415 	/* Check command line arguments */
416 	check_cmd_params();
417 
418 	/* Indicate SHA as image hash algorithm in the certificate
419 	 * extension */
420 	if (hash_alg == HASH_ALG_SHA384) {
421 		md_info = EVP_sha384();
422 		md_len  = SHA384_DIGEST_LENGTH;
423 	} else if (hash_alg == HASH_ALG_SHA512) {
424 		md_info = EVP_sha512();
425 		md_len  = SHA512_DIGEST_LENGTH;
426 	} else {
427 		md_info = EVP_sha256();
428 		md_len  = SHA256_DIGEST_LENGTH;
429 	}
430 
431 	/* Load private keys from files (or generate new ones) */
432 	for (i = 0 ; i < num_keys ; i++) {
433 #if !USING_OPENSSL3
434 		if (!key_new(&keys[i])) {
435 			ERROR("Failed to allocate key container\n");
436 			exit(1);
437 		}
438 #endif
439 
440 		/* First try to load the key from disk */
441 		if (key_load(&keys[i], &err_code)) {
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