| /* |
| * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * Redistributions of source code must retain the above copyright notice, this |
| * list of conditions and the following disclaimer. |
| * |
| * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * Neither the name of ARM nor the names of its contributors may be used |
| * to endorse or promote products derived from this software without specific |
| * prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| * POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include <getopt.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #include <openssl/conf.h> |
| #include <openssl/engine.h> |
| #include <openssl/err.h> |
| #include <openssl/pem.h> |
| #include <openssl/sha.h> |
| #include <openssl/x509v3.h> |
| |
| #include "cert.h" |
| #include "cmd_opt.h" |
| #include "debug.h" |
| #include "ext.h" |
| #include "key.h" |
| #include "platform_oid.h" |
| #include "sha.h" |
| #include "tbbr/tbb_ext.h" |
| #include "tbbr/tbb_cert.h" |
| #include "tbbr/tbb_key.h" |
| |
| /* |
| * Helper macros to simplify the code. This macro assigns the return value of |
| * the 'fn' function to 'v' and exits if the value is NULL. |
| */ |
| #define CHECK_NULL(v, fn) \ |
| do { \ |
| v = fn; \ |
| if (v == NULL) { \ |
| ERROR("NULL object at %s:%d\n", __FILE__, __LINE__); \ |
| exit(1); \ |
| } \ |
| } while (0) |
| |
| /* |
| * This macro assigns the NID corresponding to 'oid' to 'v' and exits if the |
| * NID is undefined. |
| */ |
| #define CHECK_OID(v, oid) \ |
| do { \ |
| v = OBJ_txt2nid(oid); \ |
| if (v == NID_undef) { \ |
| ERROR("Cannot find TBB extension %s\n", oid); \ |
| exit(1); \ |
| } \ |
| } while (0) |
| |
| #define MAX_FILENAME_LEN 1024 |
| #define VAL_DAYS 7300 |
| #define ID_TO_BIT_MASK(id) (1 << id) |
| #define NUM_ELEM(x) ((sizeof(x)) / (sizeof(x[0]))) |
| |
| /* Files */ |
| enum { |
| /* Image file names (inputs) */ |
| BL2_ID = 0, |
| BL30_ID, |
| BL31_ID, |
| BL32_ID, |
| BL33_ID, |
| /* Certificate file names (outputs) */ |
| BL2_CERT_ID, |
| TRUSTED_KEY_CERT_ID, |
| BL30_KEY_CERT_ID, |
| BL30_CERT_ID, |
| BL31_KEY_CERT_ID, |
| BL31_CERT_ID, |
| BL32_KEY_CERT_ID, |
| BL32_CERT_ID, |
| BL33_KEY_CERT_ID, |
| BL33_CERT_ID, |
| /* Key file names (input/output) */ |
| ROT_KEY_ID, |
| TRUSTED_WORLD_KEY_ID, |
| NON_TRUSTED_WORLD_KEY_ID, |
| BL30_KEY_ID, |
| BL31_KEY_ID, |
| BL32_KEY_ID, |
| BL33_KEY_ID, |
| NUM_OPTS |
| }; |
| |
| /* Global options */ |
| static int key_alg; |
| static int new_keys; |
| static int save_keys; |
| static int print_cert; |
| |
| /* Info messages created in the Makefile */ |
| extern const char build_msg[]; |
| extern const char platform_msg[]; |
| |
| |
| static char *strdup(const char *str) |
| { |
| int n = strlen(str) + 1; |
| char *dup = malloc(n); |
| if (dup) { |
| strcpy(dup, str); |
| } |
| return dup; |
| } |
| |
| static const char *key_algs_str[] = { |
| [KEY_ALG_RSA] = "rsa", |
| #ifndef OPENSSL_NO_EC |
| [KEY_ALG_ECDSA] = "ecdsa" |
| #endif /* OPENSSL_NO_EC */ |
| }; |
| |
| static void print_help(const char *cmd, const struct option *long_opt) |
| { |
| int i = 0; |
| printf("\n\n"); |
| printf("The certificate generation tool loads the binary images and\n" |
| "optionally the RSA keys, and outputs the key and content\n" |
| "certificates properly signed to implement the chain of trust.\n" |
| "If keys are provided, they must be in PEM format.\n" |
| "Certificates are generated in DER format.\n"); |
| printf("\n"); |
| printf("Usage:\n\n"); |
| printf(" %s [-hknp] \\\n", cmd); |
| for (i = 0; i < NUM_OPTS; i++) { |
| printf(" --%s <file> \\\n", long_opt[i].name); |
| } |
| printf("\n"); |
| printf("-a Key algorithm: rsa (default), ecdsa\n"); |
| printf("-h Print help and exit\n"); |
| printf("-k Save key pairs into files. Filenames must be provided\n"); |
| printf("-n Generate new key pairs if no key files are provided\n"); |
| printf("-p Print the certificates in the standard output\n"); |
| printf("\n"); |
| |
| exit(0); |
| } |
| |
| static int get_key_alg(const char *key_alg_str) |
| { |
| int i; |
| |
| for (i = 0 ; i < NUM_ELEM(key_algs_str) ; i++) { |
| if (0 == strcmp(key_alg_str, key_algs_str[i])) { |
| return i; |
| } |
| } |
| |
| return -1; |
| } |
| |
| static void check_cmd_params(void) |
| { |
| cert_t *cert; |
| ext_t *ext; |
| key_t *key; |
| int i, j; |
| |
| /* Only save new keys */ |
| if (save_keys && !new_keys) { |
| ERROR("Only new keys can be saved to disk\n"); |
| exit(1); |
| } |
| |
| /* Check that all required options have been specified in the |
| * command line */ |
| for (i = 0; i < num_certs; i++) { |
| cert = &certs[i]; |
| if (cert->fn == NULL) { |
| /* Certificate not requested. Skip to the next one */ |
| continue; |
| } |
| |
| /* Check that all parameters required to create this certificate |
| * have been specified in the command line */ |
| for (j = 0; j < cert->num_ext; j++) { |
| ext = &extensions[cert->ext[j]]; |
| switch (ext->type) { |
| case EXT_TYPE_PKEY: |
| /* Key filename must be specified */ |
| key = &keys[ext->data.key]; |
| if (!new_keys && key->fn == NULL) { |
| ERROR("Key '%s' required by '%s' not " |
| "specified\n", key->desc, |
| cert->cn); |
| exit(1); |
| } |
| break; |
| case EXT_TYPE_HASH: |
| /* Binary image must be specified */ |
| if (ext->data.fn == NULL) { |
| ERROR("Image for '%s' not specified\n", |
| ext->ln); |
| exit(1); |
| } |
| break; |
| default: |
| ERROR("Unknown extension type in '%s'\n", |
| ext->ln); |
| exit(1); |
| break; |
| } |
| } |
| } |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| STACK_OF(X509_EXTENSION) * sk = NULL; |
| X509_EXTENSION *cert_ext = NULL; |
| ext_t *ext = NULL; |
| key_t *key = NULL; |
| cert_t *cert = NULL; |
| FILE *file = NULL; |
| int i, j, ext_nid; |
| int c, opt_idx = 0; |
| const struct option *cmd_opt; |
| const char *cur_opt; |
| unsigned int err_code; |
| unsigned char md[SHA256_DIGEST_LENGTH]; |
| const EVP_MD *md_info; |
| |
| NOTICE("CoT Generation Tool: %s\n", build_msg); |
| NOTICE("Target platform: %s\n", platform_msg); |
| |
| /* Set default options */ |
| key_alg = KEY_ALG_RSA; |
| |
| /* Add common command line options */ |
| cmd_opt_add("key-alg", required_argument, 'a'); |
| cmd_opt_add("help", no_argument, 'h'); |
| cmd_opt_add("save-keys", no_argument, 'k'); |
| cmd_opt_add("new-chain", no_argument, 'n'); |
| cmd_opt_add("print-cert", no_argument, 'p'); |
| |
| /* Initialize the certificates */ |
| if (cert_init() != 0) { |
| ERROR("Cannot initialize certificates\n"); |
| exit(1); |
| } |
| |
| /* Initialize the keys */ |
| if (key_init() != 0) { |
| ERROR("Cannot initialize keys\n"); |
| exit(1); |
| } |
| |
| /* Initialize the new types and register OIDs for the extensions */ |
| if (ext_init() != 0) { |
| ERROR("Cannot initialize TBB extensions\n"); |
| exit(1); |
| } |
| |
| /* Get the command line options populated during the initialization */ |
| cmd_opt = cmd_opt_get_array(); |
| |
| while (1) { |
| /* getopt_long stores the option index here. */ |
| c = getopt_long(argc, argv, "ahknp", cmd_opt, &opt_idx); |
| |
| /* Detect the end of the options. */ |
| if (c == -1) { |
| break; |
| } |
| |
| switch (c) { |
| case 'a': |
| key_alg = get_key_alg(optarg); |
| if (key_alg < 0) { |
| ERROR("Invalid key algorithm '%s'\n", optarg); |
| exit(1); |
| } |
| break; |
| case 'h': |
| print_help(argv[0], cmd_opt); |
| break; |
| case 'k': |
| save_keys = 1; |
| break; |
| case 'n': |
| new_keys = 1; |
| break; |
| case 'p': |
| print_cert = 1; |
| break; |
| case CMD_OPT_EXT: |
| cur_opt = cmd_opt_get_name(opt_idx); |
| ext = ext_get_by_opt(cur_opt); |
| ext->data.fn = strdup(optarg); |
| break; |
| case CMD_OPT_KEY: |
| cur_opt = cmd_opt_get_name(opt_idx); |
| key = key_get_by_opt(cur_opt); |
| key->fn = strdup(optarg); |
| break; |
| case CMD_OPT_CERT: |
| cur_opt = cmd_opt_get_name(opt_idx); |
| cert = cert_get_by_opt(cur_opt); |
| cert->fn = strdup(optarg); |
| break; |
| case '?': |
| default: |
| printf("%s\n", optarg); |
| exit(1); |
| } |
| } |
| |
| /* Check command line arguments */ |
| check_cmd_params(); |
| |
| /* Indicate SHA256 as image hash algorithm in the certificate |
| * extension */ |
| md_info = EVP_sha256(); |
| |
| /* Load private keys from files (or generate new ones) */ |
| for (i = 0 ; i < num_keys ; i++) { |
| /* First try to load the key from disk */ |
| if (key_load(&keys[i], &err_code)) { |
| /* Key loaded successfully */ |
| continue; |
| } |
| |
| /* Key not loaded. Check the error code */ |
| if (err_code == KEY_ERR_MALLOC) { |
| /* Cannot allocate memory. Abort. */ |
| ERROR("Malloc error while loading '%s'\n", keys[i].fn); |
| exit(1); |
| } else if (err_code == KEY_ERR_LOAD) { |
| /* File exists, but it does not contain a valid private |
| * key. Abort. */ |
| ERROR("Error loading '%s'\n", keys[i].fn); |
| exit(1); |
| } |
| |
| /* File does not exist, could not be opened or no filename was |
| * given */ |
| if (new_keys) { |
| /* Try to create a new key */ |
| NOTICE("Creating new key for '%s'\n", keys[i].desc); |
| if (!key_create(&keys[i], key_alg)) { |
| ERROR("Error creating key '%s'\n", keys[i].desc); |
| exit(1); |
| } |
| } else { |
| if (err_code == KEY_ERR_OPEN) { |
| ERROR("Error opening '%s'\n", keys[i].fn); |
| } else { |
| ERROR("Key '%s' not specified\n", keys[i].desc); |
| } |
| exit(1); |
| } |
| } |
| |
| /* Create the certificates */ |
| for (i = 0 ; i < num_certs ; i++) { |
| |
| cert = &certs[i]; |
| |
| /* Create a new stack of extensions. This stack will be used |
| * to create the certificate */ |
| CHECK_NULL(sk, sk_X509_EXTENSION_new_null()); |
| |
| for (j = 0 ; j < cert->num_ext ; j++) { |
| |
| ext = &extensions[cert->ext[j]]; |
| |
| /* Get OpenSSL internal ID for this extension */ |
| CHECK_OID(ext_nid, ext->oid); |
| |
| /* |
| * Three types of extensions are currently supported: |
| * - EXT_TYPE_NVCOUNTER |
| * - EXT_TYPE_HASH |
| * - EXT_TYPE_PKEY |
| */ |
| switch (ext->type) { |
| case EXT_TYPE_NVCOUNTER: |
| CHECK_NULL(cert_ext, ext_new_nvcounter(ext_nid, |
| EXT_CRIT, ext->data.nvcounter)); |
| break; |
| case EXT_TYPE_HASH: |
| if (ext->data.fn == NULL) { |
| break; |
| } |
| if (!sha_file(ext->data.fn, md)) { |
| ERROR("Cannot calculate hash of %s\n", |
| ext->data.fn); |
| exit(1); |
| } |
| CHECK_NULL(cert_ext, ext_new_hash(ext_nid, |
| EXT_CRIT, md_info, md, |
| SHA256_DIGEST_LENGTH)); |
| break; |
| case EXT_TYPE_PKEY: |
| CHECK_NULL(cert_ext, ext_new_key(ext_nid, |
| EXT_CRIT, keys[ext->data.key].key)); |
| break; |
| default: |
| ERROR("Unknown extension type in %s\n", |
| cert->cn); |
| exit(1); |
| } |
| |
| /* Push the extension into the stack */ |
| sk_X509_EXTENSION_push(sk, cert_ext); |
| } |
| |
| /* Create certificate. Signed with ROT key */ |
| if (cert->fn && !cert_new(cert, VAL_DAYS, 0, sk)) { |
| ERROR("Cannot create %s\n", cert->cn); |
| exit(1); |
| } |
| |
| sk_X509_EXTENSION_free(sk); |
| } |
| |
| |
| /* Print the certificates */ |
| if (print_cert) { |
| for (i = 0 ; i < num_certs ; i++) { |
| if (!certs[i].x) { |
| continue; |
| } |
| printf("\n\n=====================================\n\n"); |
| X509_print_fp(stdout, certs[i].x); |
| } |
| } |
| |
| /* Save created certificates to files */ |
| for (i = 0 ; i < num_certs ; i++) { |
| if (certs[i].x && certs[i].fn) { |
| file = fopen(certs[i].fn, "w"); |
| if (file != NULL) { |
| i2d_X509_fp(file, certs[i].x); |
| fclose(file); |
| } else { |
| ERROR("Cannot create file %s\n", certs[i].fn); |
| } |
| } |
| } |
| |
| /* Save keys */ |
| if (save_keys) { |
| for (i = 0 ; i < num_keys ; i++) { |
| if (!key_store(&keys[i])) { |
| ERROR("Cannot save %s\n", keys[i].desc); |
| } |
| } |
| } |
| |
| #ifndef OPENSSL_NO_ENGINE |
| ENGINE_cleanup(); |
| #endif |
| CRYPTO_cleanup_all_ex_data(); |
| |
| return 0; |
| } |