/* * Copyright (C) 2011-2018 Intel Corporation. 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 Intel Corporation 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 * OWNER 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 "ref_wl_gen.h" #include "arch.h" #include "parse_key_file.h" #include "ref_le.h" #include #include #include #include #include #define USAGE \ "ref_wl_gen \n" \ "Command:\n"\ " gen-wl: Generate a white-list file based on the information provided in the config file. \n" \ "Options:\n" \ " -out : The output file name for the white-list. \n" \ " -cfg : A CSV configuration file with the list of hash values or keys to sign. \n" \ " -key : The private key to sign the white-list with. \n" \ " -ver : An integer value of the white-list version. \n" \ " -verbose: Print extended report while generating the white-list. \n" \ "CSV file columns: \n" \ " allow provision key, mr_enclave valid, mr_signer hash, mr_signer file, mr_enclave hash, mr_enclave file, comments (ignored) \n" \ "Notes: \n" \ " * Column 1 and 2 should be true or false. \n" \ " * If mr_enclave valid is false the mr_enclave columns will be ignored. \n" \ " * If mr_signer/mr_enclave hash is not empty the mr_signer/mr_enclave file will be ignored. \n" \ " * mr_signer file should be key file (pem), mr_enclave file should be sigstruct (bin). \n" \ " * Key hash representation should be in little endian, i.e. LSB byte first. \n" \ "Example:\n" \ " ref_wl_gen gen-wl -out wl.bin -cfg cfg.csv -key private.pem \n" #define LINE_LEN 1024 #define MAX_NUM_OF_RECORDS 512 #define WL_FILE_VERSION 1 CRefWLGen::CRefWLGen() : m_cfgfile(NULL), m_outfile(NULL), m_keyfile(NULL), m_version(0), m_verbose(false) { } CRefWLGen::~CRefWLGen() { } void CRefWLGen::print_line(bool always_print, const char* format, ...) { if (!always_print && !m_verbose) { return; } va_list args; va_start(args, format); vprintf(format, args); va_end(args); } void CRefWLGen::print_byte_array(bool always_print, const uint8_t *array, size_t size, const char *line_prefix, int line_len) { if (!always_print && !m_verbose) { return; } for (size_t i = 0; i < size; ++i) { if (i % line_len == 0) { print_line(always_print, "\n%s", line_prefix); } print_line(always_print, "%02X ", array[i]); } print_line(always_print, "\n"); } void CRefWLGen::reverse_byte_array(uint8_t *array, size_t size) { for (size_t i = 0; i < size / 2; i++) { uint8_t temp = array[i]; array[i] = array[size - i - 1]; array[size - i - 1] = temp; } } gen_wl_cmd_t CRefWLGen::parse_cmd(int argc, char **argv) { gen_wl_cmd_t cmd = UNKNOWN; argc--; argv++; if (argc <= 0 || argv == NULL) { return cmd; } if (strcmp(*argv, "gen-wl") == 0) cmd = GEN_WL; argc--; argv++; while (argc >= 1) { if (strcmp(*argv, "-out") == 0) { if (--argc < 1) { return UNKNOWN; } m_outfile = *(++argv); } else if (strcmp(*argv, "-cfg") == 0) { if (--argc < 1) { return UNKNOWN; } m_cfgfile = *(++argv); } else if (strcmp(*argv, "-key") == 0) { if (--argc < 1) { return UNKNOWN; } m_keyfile = *(++argv); } else if (strcmp(*argv, "-ver") == 0) { if (--argc < 1) { return UNKNOWN; } m_version = atoi(*(++argv)); } else if (strcmp(*argv, "-verbose") == 0) { m_verbose = true; } else { return UNKNOWN; } argc--; argv++; } return cmd; } char* CRefWLGen::clean_start(char *str) { if (str == NULL) { return NULL; } while (isspace(*str)) { str++; } return str; } bool CRefWLGen::get_hash_from_pubkey_file(const char* pubkey_file, sgx_measurement_t* p_hash) { if (pubkey_file == NULL || p_hash == NULL || *pubkey_file == 0) { return false; } rsa_params_t rsa_params; int key_type; if (!parse_key_file(pubkey_file, &rsa_params, &key_type)) { return false; } if (sgx_sha256_msg((uint8_t*)&(rsa_params.n), sizeof(rsa_params.n), (sgx_sha256_hash_t*)p_hash) != SGX_SUCCESS) { return false; } return true; } bool CRefWLGen::get_hash_from_string(char* hash_str, sgx_measurement_t* p_hash) { if (hash_str == NULL || p_hash == NULL) { return false; } char* ptr = clean_start(hash_str); for (size_t count = 0; count < sizeof(sgx_measurement_t); ++count) { ptr = clean_start(ptr); if (*ptr == 0) { return false; } p_hash->m[count] = (char)strtol(ptr, &ptr, 16); } return true; } bool CRefWLGen::get_hash_from_sigstruct_file(const char* sig_file, sgx_measurement_t* p_hash) { if (sig_file == NULL || p_hash == NULL || *sig_file == 0) { return false; } FILE *p_sig_file = fopen(sig_file, "rb"); if (!p_sig_file) { print_line(true, "Failed to open the input file '%s'.\n", sig_file); return false; } fseek(p_sig_file, 0, SEEK_END); size_t length = ftell(p_sig_file); rewind(p_sig_file); if (length < sizeof(enclave_css_t)) { fclose(p_sig_file); return false; } enclave_css_t sigstruct; length = fread((&sigstruct), 1, sizeof(enclave_css_t), p_sig_file); fclose(p_sig_file); if (length < sizeof(enclave_css_t)) { return false; } memcpy((char*)p_hash, &(sigstruct.body.enclave_hash), sizeof(sgx_measurement_t)); return true; } bool CRefWLGen::process_line(char *line, ref_le_white_list_entry_t *entry) { char *item = strtok(line, ","); // parse item: allow provision key item = clean_start(item); if (strncmp(item, "true", 4) == 0) { entry->provision_key = 1; } else { entry->provision_key = 0; } print_line(false, " provision_key: %d\n", entry->provision_key); // parse item: mr_enclave valid item = strtok(NULL, ","); item = clean_start(item); if (strncmp(item, "true", 4) == 0) { entry->match_mr_enclave = 1; } else { entry->match_mr_enclave = 0; } print_line(false, " match_mr_enclave: %d\n", entry->match_mr_enclave); // parse item: mr_signer hash/file item = strtok(NULL, ","); item = clean_start(item); if (*item != 0) { // hash if (get_hash_from_string(item, &(entry->mr_signer)) == false) { return false; } item = strtok(NULL, ","); // skip the next item } else { // file item = strtok(NULL, ","); item = clean_start(item); if (get_hash_from_pubkey_file(item, &(entry->mr_signer)) == false) { return false; } } print_line(false, " mr_signer: "); print_byte_array(false, (uint8_t*)&(entry->mr_signer), sizeof(sgx_measurement_t), " "); reverse_byte_array((uint8_t*)&(entry->mr_signer), sizeof(entry->mr_signer)); // parse item: mr_enclave hash/file if (entry->match_mr_enclave == 1) { item = strtok(NULL, ","); item = clean_start(item); if (*item != 0) { // hash if (get_hash_from_string(item, &(entry->mr_enclave)) == false) { return false; } item = strtok(NULL, ","); // skip the next item } else { // file item = strtok(NULL, ","); item = clean_start(item); if (get_hash_from_sigstruct_file(item, &(entry->mr_enclave)) == false) { return false; } } print_line(false, " mr_enclave: "); print_byte_array(false, (uint8_t*)&(entry->mr_enclave), sizeof(sgx_measurement_t), " "); reverse_byte_array((uint8_t*)&(entry->mr_enclave), sizeof(entry->mr_enclave)); } print_line(false, " Full entry (big endian): "); print_byte_array(false, (uint8_t*)entry, sizeof(ref_le_white_list_entry_t), " "); return true; } bool CRefWLGen::set_key_and_sign(const char* prikey_file, ref_le_white_list_t *p_wl, uint16_t wl_count, sgx_rsa3072_signature_t* p_signature) { rsa_params_t rsa_params; int key_type; if (!parse_key_file(prikey_file, &rsa_params, &key_type)) { return false; } rsa_params.e[0] = 3; sgx_rsa3072_key_t rsa_key; memcpy(&(rsa_key.mod), &(rsa_params.n), sizeof(rsa_key.mod)); memcpy(&(rsa_key.d), &(rsa_params.d), sizeof(rsa_key.d)); memcpy(&(rsa_key.e), &(rsa_params.e), sizeof(rsa_key.e)); memcpy(&(p_wl->signer_pubkey.mod), &(rsa_params.n), sizeof(p_wl->signer_pubkey.mod)); memcpy(&(p_wl->signer_pubkey.exp), &(rsa_params.e), sizeof(p_wl->signer_pubkey.exp)); print_line(false, " Signer public key modolus: "); print_byte_array(false, (uint8_t*)&(p_wl->signer_pubkey.mod), sizeof(p_wl->signer_pubkey.mod), " "); print_line(false, " Signer public key exponent: %d\n", p_wl->signer_pubkey.exp); reverse_byte_array((uint8_t*)&(p_wl->signer_pubkey.mod), sizeof(p_wl->signer_pubkey.mod)); reverse_byte_array((uint8_t*)&(p_wl->signer_pubkey.exp), sizeof(p_wl->signer_pubkey.exp)); int len = REF_LE_WL_SIZE(wl_count); sgx_status_t res = sgx_rsa3072_sign((const uint8_t*) p_wl, len, &rsa_key, p_signature); if (res != SGX_SUCCESS) { return false; } return true; } bool CRefWLGen::generate_wl() { print_line(true, "Building white list...\n"); ref_le_white_list_t* wl = (ref_le_white_list_t*)malloc(REF_LE_WL_SIZE(MAX_NUM_OF_RECORDS)); memset(wl, 0, REF_LE_WL_SIZE(MAX_NUM_OF_RECORDS)); // Init values of white-list print_line(true, "While list format version: %d\n", WL_FILE_VERSION); wl->version = WL_FILE_VERSION; print_line(true, "While list instance version: %d\n", m_version); wl->wl_version = m_version; // Read configuration information print_line(true, "Reading configuration file: %s\n", m_cfgfile); FILE *cfgfile = fopen(m_cfgfile, "r"); if (!cfgfile) { print_line(true, "Failed to open the configuration file '%s'.\n", m_cfgfile); free(wl); return false; } char line[LINE_LEN]; // TODO: what if line len is not enough? while (fgets(line, LINE_LEN, cfgfile) != NULL) { char* start = clean_start(line); if (*start == 0) { continue; } print_line(false, "Entry #%d:\n", wl->entries_count); if (process_line(line, &(wl->wl_entries[wl->entries_count])) == false) { print_line(true, "Failed to process the configuration line.\n"); fclose(cfgfile); free(wl); return false; } wl->entries_count++; } fclose(cfgfile); print_line(false, "Parsed entries count: %d\n", wl->entries_count); uint16_t wl_count = wl->entries_count; reverse_byte_array((uint8_t*)&(wl->version), sizeof(wl->version)); reverse_byte_array((uint8_t*)&(wl->wl_version), sizeof(wl->wl_version)); reverse_byte_array((uint8_t*)&(wl->entries_count), sizeof(wl->entries_count)); print_line(true, "Signing using key file: %s\n", m_keyfile); sgx_rsa3072_signature_t sig; if (!set_key_and_sign(m_keyfile, wl, wl_count, &sig)) { free(wl); return false; } reverse_byte_array((uint8_t*)&sig, sizeof(sgx_rsa3072_signature_t)); print_line(false, "Complete white list (big endian): "); print_byte_array(false, (uint8_t*)wl, REF_LE_WL_SIZE(wl_count), " "); print_line(false, "Signature (big endian): "); print_byte_array(false, (uint8_t*)&sig, sizeof(sgx_rsa3072_signature_t), " "); print_line(true, "Writing output file: %s\n", m_outfile); FILE *pOut = fopen(m_outfile, "wb"); size_t written = fwrite(wl, 1, REF_LE_WL_SIZE(wl_count), pOut); written += fwrite(&sig, 1, sizeof(sgx_rsa3072_signature_t), pOut); fclose(pOut); free(wl); if (written != REF_LE_WL_SIZE(wl_count) + sizeof(sgx_rsa3072_signature_t)) { return false; } print_line(true, "White list generation completed successfully. \n"); return true; } bool CRefWLGen::run(int argc, char **argv) { gen_wl_cmd_t cmd = parse_cmd(argc, argv); switch (cmd) { case GEN_WL: if (m_cfgfile == NULL || m_outfile == NULL || m_keyfile == NULL) { print_line(true, "Mising parameters. \n%s", USAGE); return false; } if (generate_wl() == false) { print_line(true, "Failed to generate white-list.\n"); return false; } break; default: print_line(true, "Command line is not correct. \n%s", USAGE); return false; } return true; }