blob: f14601c8bc52e12360806d27d551b51891967173 [file] [log] [blame]
Juan Castillo11abdcd2014-10-21 11:30:42 +01001/*
Masahiro Yamadaa27c1662017-05-22 12:11:24 +09002 * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
Juan Castillo11abdcd2014-10-21 11:30:42 +01003 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
Juan Castillo11abdcd2014-10-21 11:30:42 +01005 */
6
Juan Castillo212f7382015-12-15 16:37:57 +00007#include <assert.h>
8#include <ctype.h>
Juan Castillo11abdcd2014-10-21 11:30:42 +01009#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
Masahiro Yamadaa27c1662017-05-22 12:11:24 +090021#if USE_TBBR_DEFS
22#include <tbbr_oid.h>
23#else
24#include <platform_oid.h>
25#endif
26
Juan Castillo11abdcd2014-10-21 11:30:42 +010027#include "cert.h"
Juan Castillo1218dd52015-07-03 16:23:16 +010028#include "cmd_opt.h"
Juan Castillo11abdcd2014-10-21 11:30:42 +010029#include "debug.h"
30#include "ext.h"
31#include "key.h"
Juan Castillo11abdcd2014-10-21 11:30:42 +010032#include "sha.h"
Juan Castilloe6d30e92015-06-12 11:27:59 +010033#include "tbbr/tbb_cert.h"
Isla Mitchell99305012017-07-11 14:54:08 +010034#include "tbbr/tbb_ext.h"
Juan Castilloe6d30e92015-06-12 11:27:59 +010035#include "tbbr/tbb_key.h"
Juan Castillo11abdcd2014-10-21 11:30:42 +010036
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)
Juan Castillof9f39c32015-06-01 16:34:23 +010066#define NUM_ELEM(x) ((sizeof(x)) / (sizeof(x[0])))
Juan Castillo212f7382015-12-15 16:37:57 +000067#define HELP_OPT_MAX_LEN 128
Juan Castillo11abdcd2014-10-21 11:30:42 +010068
69/* Global options */
Juan Castillof9f39c32015-06-01 16:34:23 +010070static int key_alg;
Juan Castillo11abdcd2014-10-21 11:30:42 +010071static int new_keys;
72static int save_keys;
73static int print_cert;
Juan Castillo11abdcd2014-10-21 11:30:42 +010074
Juan Castillo11abdcd2014-10-21 11:30:42 +010075/* Info messages created in the Makefile */
76extern const char build_msg[];
77extern const char platform_msg[];
78
79
80static char *strdup(const char *str)
81{
82 int n = strlen(str) + 1;
83 char *dup = malloc(n);
84 if (dup) {
85 strcpy(dup, str);
86 }
87 return dup;
88}
89
Juan Castillof9f39c32015-06-01 16:34:23 +010090static const char *key_algs_str[] = {
91 [KEY_ALG_RSA] = "rsa",
Juan Castilloa2224ab2015-06-30 13:36:57 +010092#ifndef OPENSSL_NO_EC
Juan Castillof9f39c32015-06-01 16:34:23 +010093 [KEY_ALG_ECDSA] = "ecdsa"
Juan Castilloa2224ab2015-06-30 13:36:57 +010094#endif /* OPENSSL_NO_EC */
Juan Castillof9f39c32015-06-01 16:34:23 +010095};
96
Juan Castillo1218dd52015-07-03 16:23:16 +010097static void print_help(const char *cmd, const struct option *long_opt)
Juan Castillo11abdcd2014-10-21 11:30:42 +010098{
Juan Castillo212f7382015-12-15 16:37:57 +000099 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
Juan Castillo11abdcd2014-10-21 11:30:42 +0100107 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");
Juan Castillo212f7382015-12-15 16:37:57 +0000114 printf("Usage:\n");
115 printf("\t%s [OPTIONS]\n\n", cmd);
116
117 printf("Available options:\n");
Juan Castillo212f7382015-12-15 16:37:57 +0000118 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++;
Juan Castillo11abdcd2014-10-21 11:30:42 +0100133 }
134 printf("\n");
Juan Castillo11abdcd2014-10-21 11:30:42 +0100135
136 exit(0);
137}
138
Juan Castillof9f39c32015-06-01 16:34:23 +0100139static 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
Juan Castillo11abdcd2014-10-21 11:30:42 +0100152static void check_cmd_params(void)
153{
Juan Castillo86958fd2015-07-08 12:11:38 +0100154 cert_t *cert;
155 ext_t *ext;
156 key_t *key;
157 int i, j;
158
Juan Castillof9f39c32015-06-01 16:34:23 +0100159 /* Only save new keys */
160 if (save_keys && !new_keys) {
161 ERROR("Only new keys can be saved to disk\n");
162 exit(1);
163 }
164
Juan Castillo86958fd2015-07-08 12:11:38 +0100165 /* Check that all required options have been specified in the
166 * command line */
167 for (i = 0; i < num_certs; i++) {
168 cert = &certs[i];
169 if (cert->fn == NULL) {
170 /* Certificate not requested. Skip to the next one */
171 continue;
Juan Castillo11abdcd2014-10-21 11:30:42 +0100172 }
173
Juan Castillo86958fd2015-07-08 12:11:38 +0100174 /* Check that all parameters required to create this certificate
175 * have been specified in the command line */
176 for (j = 0; j < cert->num_ext; j++) {
177 ext = &extensions[cert->ext[j]];
178 switch (ext->type) {
Juan Castillo43529982016-01-22 11:05:24 +0000179 case EXT_TYPE_NVCOUNTER:
180 /* Counter value must be specified */
181 if ((!ext->optional) && (ext->arg == NULL)) {
182 ERROR("Value for '%s' not specified\n",
183 ext->ln);
184 exit(1);
185 }
186 break;
Juan Castillo86958fd2015-07-08 12:11:38 +0100187 case EXT_TYPE_PKEY:
188 /* Key filename must be specified */
Juan Castillo43529982016-01-22 11:05:24 +0000189 key = &keys[ext->attr.key];
Juan Castillo86958fd2015-07-08 12:11:38 +0100190 if (!new_keys && key->fn == NULL) {
191 ERROR("Key '%s' required by '%s' not "
192 "specified\n", key->desc,
193 cert->cn);
194 exit(1);
195 }
196 break;
197 case EXT_TYPE_HASH:
Yatharth Kochar5752b592015-08-21 15:30:55 +0100198 /*
199 * Binary image must be specified
200 * unless it is explicitly made optional.
201 */
Juan Castillo43529982016-01-22 11:05:24 +0000202 if ((!ext->optional) && (ext->arg == NULL)) {
Juan Castillo86958fd2015-07-08 12:11:38 +0100203 ERROR("Image for '%s' not specified\n",
204 ext->ln);
205 exit(1);
206 }
207 break;
208 default:
Juan Castillo43529982016-01-22 11:05:24 +0000209 ERROR("Unknown extension type '%d' in '%s'\n",
210 ext->type, ext->ln);
Juan Castillo86958fd2015-07-08 12:11:38 +0100211 exit(1);
212 break;
213 }
Juan Castillo11abdcd2014-10-21 11:30:42 +0100214 }
215 }
216}
217
Juan Castillo212f7382015-12-15 16:37:57 +0000218/* Common command line options */
219static const cmd_opt_t common_cmd_opt[] = {
220 {
221 { "help", no_argument, NULL, 'h' },
222 "Print this message and exit"
223 },
224 {
225 { "key-alg", required_argument, NULL, 'a' },
226 "Key algorithm: 'rsa' (default), 'ecdsa'"
227 },
228 {
229 { "save-keys", no_argument, NULL, 'k' },
230 "Save key pairs into files. Filenames must be provided"
231 },
232 {
233 { "new-keys", no_argument, NULL, 'n' },
234 "Generate new key pairs if no key files are provided"
235 },
236 {
237 { "print-cert", no_argument, NULL, 'p' },
238 "Print the certificates in the standard output"
239 }
240};
241
Juan Castillo11abdcd2014-10-21 11:30:42 +0100242int main(int argc, char *argv[])
243{
Masahiro Yamada48cb5e52017-02-06 19:47:44 +0900244 STACK_OF(X509_EXTENSION) * sk;
245 X509_EXTENSION *cert_ext;
246 ext_t *ext;
247 key_t *key;
248 cert_t *cert;
249 FILE *file;
Juan Castillo43529982016-01-22 11:05:24 +0000250 int i, j, ext_nid, nvctr;
Juan Castillo11abdcd2014-10-21 11:30:42 +0100251 int c, opt_idx = 0;
Juan Castillo1218dd52015-07-03 16:23:16 +0100252 const struct option *cmd_opt;
253 const char *cur_opt;
Juan Castillof9f39c32015-06-01 16:34:23 +0100254 unsigned int err_code;
Juan Castillo11abdcd2014-10-21 11:30:42 +0100255 unsigned char md[SHA256_DIGEST_LENGTH];
Juan Castilloac402932015-03-05 14:30:00 +0000256 const EVP_MD *md_info;
Juan Castillo11abdcd2014-10-21 11:30:42 +0100257
258 NOTICE("CoT Generation Tool: %s\n", build_msg);
259 NOTICE("Target platform: %s\n", platform_msg);
260
Juan Castillof9f39c32015-06-01 16:34:23 +0100261 /* Set default options */
262 key_alg = KEY_ALG_RSA;
263
Juan Castillo1218dd52015-07-03 16:23:16 +0100264 /* Add common command line options */
Juan Castillo212f7382015-12-15 16:37:57 +0000265 for (i = 0; i < NUM_ELEM(common_cmd_opt); i++) {
266 cmd_opt_add(&common_cmd_opt[i]);
267 }
Juan Castillo1218dd52015-07-03 16:23:16 +0100268
269 /* Initialize the certificates */
270 if (cert_init() != 0) {
271 ERROR("Cannot initialize certificates\n");
272 exit(1);
273 }
274
275 /* Initialize the keys */
276 if (key_init() != 0) {
277 ERROR("Cannot initialize keys\n");
278 exit(1);
279 }
280
281 /* Initialize the new types and register OIDs for the extensions */
282 if (ext_init() != 0) {
283 ERROR("Cannot initialize TBB extensions\n");
284 exit(1);
285 }
286
287 /* Get the command line options populated during the initialization */
288 cmd_opt = cmd_opt_get_array();
289
Juan Castillo11abdcd2014-10-21 11:30:42 +0100290 while (1) {
291 /* getopt_long stores the option index here. */
Juan Castillo212f7382015-12-15 16:37:57 +0000292 c = getopt_long(argc, argv, "a:hknp", cmd_opt, &opt_idx);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100293
294 /* Detect the end of the options. */
295 if (c == -1) {
296 break;
297 }
298
299 switch (c) {
Juan Castillof9f39c32015-06-01 16:34:23 +0100300 case 'a':
301 key_alg = get_key_alg(optarg);
302 if (key_alg < 0) {
303 ERROR("Invalid key algorithm '%s'\n", optarg);
304 exit(1);
305 }
306 break;
Juan Castillo11abdcd2014-10-21 11:30:42 +0100307 case 'h':
Juan Castillo1218dd52015-07-03 16:23:16 +0100308 print_help(argv[0], cmd_opt);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100309 break;
310 case 'k':
311 save_keys = 1;
312 break;
313 case 'n':
314 new_keys = 1;
315 break;
316 case 'p':
317 print_cert = 1;
318 break;
Juan Castillo1218dd52015-07-03 16:23:16 +0100319 case CMD_OPT_EXT:
320 cur_opt = cmd_opt_get_name(opt_idx);
321 ext = ext_get_by_opt(cur_opt);
Juan Castillo43529982016-01-22 11:05:24 +0000322 ext->arg = strdup(optarg);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100323 break;
Juan Castillo1218dd52015-07-03 16:23:16 +0100324 case CMD_OPT_KEY:
325 cur_opt = cmd_opt_get_name(opt_idx);
326 key = key_get_by_opt(cur_opt);
327 key->fn = strdup(optarg);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100328 break;
Juan Castillo1218dd52015-07-03 16:23:16 +0100329 case CMD_OPT_CERT:
330 cur_opt = cmd_opt_get_name(opt_idx);
331 cert = cert_get_by_opt(cur_opt);
332 cert->fn = strdup(optarg);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100333 break;
334 case '?':
335 default:
Juan Castillo212f7382015-12-15 16:37:57 +0000336 print_help(argv[0], cmd_opt);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100337 exit(1);
338 }
339 }
340
Juan Castillo11abdcd2014-10-21 11:30:42 +0100341 /* Check command line arguments */
342 check_cmd_params();
343
Juan Castilloac402932015-03-05 14:30:00 +0000344 /* Indicate SHA256 as image hash algorithm in the certificate
345 * extension */
346 md_info = EVP_sha256();
347
Juan Castillo11abdcd2014-10-21 11:30:42 +0100348 /* Load private keys from files (or generate new ones) */
Juan Castilloe6d30e92015-06-12 11:27:59 +0100349 for (i = 0 ; i < num_keys ; i++) {
Masahiro Yamadabccb1092017-02-06 21:15:01 +0900350 if (!key_new(&keys[i])) {
351 ERROR("Failed to allocate key container\n");
352 exit(1);
353 }
354
Juan Castillof9f39c32015-06-01 16:34:23 +0100355 /* First try to load the key from disk */
356 if (key_load(&keys[i], &err_code)) {
357 /* Key loaded successfully */
358 continue;
359 }
360
361 /* Key not loaded. Check the error code */
Masahiro Yamadabccb1092017-02-06 21:15:01 +0900362 if (err_code == KEY_ERR_LOAD) {
Juan Castillof9f39c32015-06-01 16:34:23 +0100363 /* File exists, but it does not contain a valid private
364 * key. Abort. */
365 ERROR("Error loading '%s'\n", keys[i].fn);
366 exit(1);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100367 }
Juan Castillof9f39c32015-06-01 16:34:23 +0100368
369 /* File does not exist, could not be opened or no filename was
370 * given */
371 if (new_keys) {
372 /* Try to create a new key */
373 NOTICE("Creating new key for '%s'\n", keys[i].desc);
374 if (!key_create(&keys[i], key_alg)) {
375 ERROR("Error creating key '%s'\n", keys[i].desc);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100376 exit(1);
377 }
Juan Castillof9f39c32015-06-01 16:34:23 +0100378 } else {
379 if (err_code == KEY_ERR_OPEN) {
380 ERROR("Error opening '%s'\n", keys[i].fn);
381 } else {
382 ERROR("Key '%s' not specified\n", keys[i].desc);
383 }
384 exit(1);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100385 }
386 }
Juan Castillo11abdcd2014-10-21 11:30:42 +0100387
Juan Castilloe6d30e92015-06-12 11:27:59 +0100388 /* Create the certificates */
389 for (i = 0 ; i < num_certs ; i++) {
Juan Castillo11abdcd2014-10-21 11:30:42 +0100390
Juan Castilloe6d30e92015-06-12 11:27:59 +0100391 cert = &certs[i];
Juan Castillo11abdcd2014-10-21 11:30:42 +0100392
Juan Castilloe6d30e92015-06-12 11:27:59 +0100393 /* Create a new stack of extensions. This stack will be used
394 * to create the certificate */
Juan Castillo11abdcd2014-10-21 11:30:42 +0100395 CHECK_NULL(sk, sk_X509_EXTENSION_new_null());
Juan Castillo11abdcd2014-10-21 11:30:42 +0100396
Juan Castilloe6d30e92015-06-12 11:27:59 +0100397 for (j = 0 ; j < cert->num_ext ; j++) {
Juan Castillo11abdcd2014-10-21 11:30:42 +0100398
Juan Castilloe6d30e92015-06-12 11:27:59 +0100399 ext = &extensions[cert->ext[j]];
Juan Castillo11abdcd2014-10-21 11:30:42 +0100400
Juan Castilloe6d30e92015-06-12 11:27:59 +0100401 /* Get OpenSSL internal ID for this extension */
402 CHECK_OID(ext_nid, ext->oid);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100403
Juan Castilloe6d30e92015-06-12 11:27:59 +0100404 /*
405 * Three types of extensions are currently supported:
406 * - EXT_TYPE_NVCOUNTER
407 * - EXT_TYPE_HASH
408 * - EXT_TYPE_PKEY
409 */
410 switch (ext->type) {
411 case EXT_TYPE_NVCOUNTER:
Yatharth Kochar79f86402016-06-22 14:49:27 +0100412 if (ext->arg) {
413 nvctr = atoi(ext->arg);
414 CHECK_NULL(cert_ext, ext_new_nvcounter(ext_nid,
Juan Castillo43529982016-01-22 11:05:24 +0000415 EXT_CRIT, nvctr));
Yatharth Kochar79f86402016-06-22 14:49:27 +0100416 }
Juan Castilloe6d30e92015-06-12 11:27:59 +0100417 break;
418 case EXT_TYPE_HASH:
Juan Castillo43529982016-01-22 11:05:24 +0000419 if (ext->arg == NULL) {
Yatharth Kochar5752b592015-08-21 15:30:55 +0100420 if (ext->optional) {
421 /* Include a hash filled with zeros */
422 memset(md, 0x0, SHA256_DIGEST_LENGTH);
423 } else {
424 /* Do not include this hash in the certificate */
425 break;
426 }
427 } else {
428 /* Calculate the hash of the file */
Juan Castillo43529982016-01-22 11:05:24 +0000429 if (!sha_file(ext->arg, md)) {
Yatharth Kochar5752b592015-08-21 15:30:55 +0100430 ERROR("Cannot calculate hash of %s\n",
Juan Castillo43529982016-01-22 11:05:24 +0000431 ext->arg);
Yatharth Kochar5752b592015-08-21 15:30:55 +0100432 exit(1);
433 }
Juan Castilloe6d30e92015-06-12 11:27:59 +0100434 }
435 CHECK_NULL(cert_ext, ext_new_hash(ext_nid,
436 EXT_CRIT, md_info, md,
437 SHA256_DIGEST_LENGTH));
438 break;
439 case EXT_TYPE_PKEY:
440 CHECK_NULL(cert_ext, ext_new_key(ext_nid,
Juan Castillo43529982016-01-22 11:05:24 +0000441 EXT_CRIT, keys[ext->attr.key].key));
Juan Castilloe6d30e92015-06-12 11:27:59 +0100442 break;
443 default:
Juan Castillo43529982016-01-22 11:05:24 +0000444 ERROR("Unknown extension type '%d' in %s\n",
445 ext->type, cert->cn);
Juan Castilloe6d30e92015-06-12 11:27:59 +0100446 exit(1);
447 }
Juan Castillo11abdcd2014-10-21 11:30:42 +0100448
Juan Castilloe6d30e92015-06-12 11:27:59 +0100449 /* Push the extension into the stack */
450 sk_X509_EXTENSION_push(sk, cert_ext);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100451 }
Juan Castillo11abdcd2014-10-21 11:30:42 +0100452
Juan Castilloe6d30e92015-06-12 11:27:59 +0100453 /* Create certificate. Signed with ROT key */
Juan Castillo86958fd2015-07-08 12:11:38 +0100454 if (cert->fn && !cert_new(cert, VAL_DAYS, 0, sk)) {
Juan Castilloe6d30e92015-06-12 11:27:59 +0100455 ERROR("Cannot create %s\n", cert->cn);
Juan Castillo11abdcd2014-10-21 11:30:42 +0100456 exit(1);
457 }
458
459 sk_X509_EXTENSION_free(sk);
460 }
461
Juan Castillo11abdcd2014-10-21 11:30:42 +0100462
463 /* Print the certificates */
464 if (print_cert) {
Juan Castilloe6d30e92015-06-12 11:27:59 +0100465 for (i = 0 ; i < num_certs ; i++) {
Juan Castillo11abdcd2014-10-21 11:30:42 +0100466 if (!certs[i].x) {
467 continue;
468 }
469 printf("\n\n=====================================\n\n");
470 X509_print_fp(stdout, certs[i].x);
471 }
472 }
473
474 /* Save created certificates to files */
Juan Castilloe6d30e92015-06-12 11:27:59 +0100475 for (i = 0 ; i < num_certs ; i++) {
Juan Castillo11abdcd2014-10-21 11:30:42 +0100476 if (certs[i].x && certs[i].fn) {
477 file = fopen(certs[i].fn, "w");
478 if (file != NULL) {
479 i2d_X509_fp(file, certs[i].x);
480 fclose(file);
481 } else {
482 ERROR("Cannot create file %s\n", certs[i].fn);
483 }
484 }
485 }
486
487 /* Save keys */
488 if (save_keys) {
Juan Castilloe6d30e92015-06-12 11:27:59 +0100489 for (i = 0 ; i < num_keys ; i++) {
Juan Castillo11abdcd2014-10-21 11:30:42 +0100490 if (!key_store(&keys[i])) {
491 ERROR("Cannot save %s\n", keys[i].desc);
492 }
493 }
494 }
495
Juan Castillo11abdcd2014-10-21 11:30:42 +0100496#ifndef OPENSSL_NO_ENGINE
497 ENGINE_cleanup();
498#endif
499 CRYPTO_cleanup_all_ex_data();
500
501 return 0;
502}