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