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