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