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