| /* Copyright 2017 IBM Corp. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| * implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <config.h> |
| |
| #include <alloca.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <getopt.h> |
| #include <limits.h> |
| #include <openssl/bn.h> |
| #include <openssl/ec.h> |
| #include <openssl/ecdsa.h> |
| #include <openssl/obj_mac.h> |
| #include <openssl/opensslv.h> |
| #include <openssl/ossl_typ.h> |
| #include <openssl/sha.h> |
| #include <stdbool.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| #include <sysexits.h> |
| #include <unistd.h> |
| |
| #include "ccan/endian/endian.h" |
| #include "ccan/short_types/short_types.h" |
| #include "container-utils.h" |
| #include "container.h" |
| |
| #define PASSED 1 |
| #define FAILED 0 |
| #define UNATTEMPTED -1 |
| |
| char *progname; |
| |
| bool print_stats; |
| bool verbose, debug; |
| int wrap = 100; |
| |
| ecc_key_t ECDSA_KEY_NULL; |
| |
| typedef struct keyprops { |
| char index; |
| const char *name; |
| const ecc_key_t *key; |
| const ecc_signature_t *sig; |
| } Keyprops; |
| |
| static void usage(int status); |
| |
| static bool getPayloadHash(int fdin, unsigned char *md); |
| static bool getVerificationHash(char *input, unsigned char *md, int len); |
| static bool verify_signature(const char *moniker, const unsigned char *dgst, |
| int dgst_len, const ecc_signature_t sig_raw, const ecc_key_t key_raw); |
| |
| static void print_bytes(char *lead, uint8_t *buffer, size_t buflen) |
| { |
| unsigned int i; |
| unsigned int width; |
| unsigned int leadbytes = strlen(lead); |
| leadbytes = leadbytes > 30 ? 30 : leadbytes; |
| width = (wrap - leadbytes) / 2; |
| width = (width < 1) ? INT_MAX : width; |
| |
| fprintf(stdout, "%s", lead); |
| for (i = 1; i < buflen + 1; i++) { |
| fprintf(stdout, "%02x", buffer[i - 1]); |
| if (((i % width) == 0) && (i < buflen)) |
| fprintf(stdout, "\n%*c", leadbytes, ' '); |
| } |
| fprintf(stdout, "\n"); |
| } |
| |
| bool stb_is_container(const void *buf, size_t size) |
| { |
| ROM_container_raw *c; |
| |
| c = (ROM_container_raw*) buf; |
| if (!buf || size < SECURE_BOOT_HEADERS_SIZE) |
| return false; |
| if (be32_to_cpu(c->magic_number) != ROM_MAGIC_NUMBER) |
| return false; |
| return true; |
| } |
| |
| int parse_stb_container(const void* data, size_t len, |
| struct parsed_stb_container *c) |
| { |
| const size_t prefix_data_min_size = 3 * (EC_COORDBYTES * 2); |
| c->buf = data; |
| c->bufsz = len; |
| c->c = data; |
| c->ph = data += sizeof(ROM_container_raw); |
| c->pd = data += sizeof(ROM_prefix_header_raw) |
| + (c->ph->ecid_count * ECID_SIZE); |
| c->sh = data += prefix_data_min_size |
| + c->ph->sw_key_count * (EC_COORDBYTES * 2); |
| c->ssig = data += sizeof(ROM_sw_header_raw) + c->sh->ecid_count * ECID_SIZE; |
| |
| return 0; |
| } |
| |
| static void display_version_raw(const ROM_version_raw v) |
| { |
| printf("ver_alg:\n"); |
| printf(" version: %04x\n", be16_to_cpu(v.version)); |
| printf(" hash_alg: %02x (%s)\n", v.hash_alg, |
| (v.hash_alg == 1) ? "SHA512" : "UNKNOWN"); |
| printf(" sig_alg: %02x (%s)\n", v.sig_alg, |
| (v.sig_alg == 1) ? "SHA512/ECDSA-521" : "UNKNOWN"); |
| } |
| |
| static void display_container_stats(const struct parsed_stb_container *c) |
| { |
| unsigned int size, offset; |
| |
| printf("Container stats:\n"); |
| size = (uint8_t*) c->ph - (uint8_t *) c->c; |
| offset = (uint8_t*) c->c - (uint8_t *) c->buf; |
| printf(" HW header size = %4u (%#06x) at offset %4u (%#06x)\n", |
| size, size, offset, offset); |
| size = (uint8_t*) c->pd - (uint8_t *) c->ph; |
| offset = (uint8_t*) c->ph - (uint8_t *) c->buf; |
| printf(" Prefix header size = %4u (%#06x) at offset %4u (%#06x)\n", |
| size, size, offset, offset); |
| size = (uint8_t*) c->sh - (uint8_t *) c->pd; |
| offset = (uint8_t*) c->pd - (uint8_t *) c->buf; |
| printf(" Prefix data size = %4u (%#06x) at offset %4u (%#06x)\n", |
| size, size, offset, offset); |
| size = (uint8_t*) c->ssig - (uint8_t *) c->sh; |
| offset = (uint8_t*) c->sh - (uint8_t *) c->buf; |
| printf(" SW header size = %4u (%#06x) at offset %4u (%#06x)\n", |
| size, size, offset, offset); |
| size = sizeof(ecc_key_t) * c->ph->sw_key_count; |
| offset = (uint8_t*) c->ssig - (uint8_t *) c->buf; |
| printf(" SW signature size = %4u (%#06x) at offset %4u (%#06x)\n", |
| size, size, offset, offset); |
| |
| printf(" TOTAL HEADER SIZE = %4lu (%#0lx)\n", c->bufsz, c->bufsz); |
| printf(" PAYLOAD SIZE = %4lu (%#0lx)\n", |
| be64_to_cpu(c->sh->payload_size), be64_to_cpu(c->sh->payload_size)); |
| printf(" TOTAL CONTAINER SIZE = %4lu (%#0lx)\n", |
| be64_to_cpu(c->c->container_size), |
| be64_to_cpu(c->c->container_size)); |
| printf("\n"); |
| } |
| |
| static void display_container(struct parsed_stb_container c) |
| { |
| unsigned char md[SHA512_DIGEST_LENGTH]; |
| void *p; |
| |
| printf("Container:\n"); |
| printf("magic: 0x%04x\n", be32_to_cpu(c.c->magic_number)); |
| printf("version: 0x%02x\n", be16_to_cpu(c.c->version)); |
| printf("container_size: 0x%08lx (%lu)\n", be64_to_cpu(c.c->container_size), |
| be64_to_cpu(c.c->container_size)); |
| printf("target_hrmor: 0x%08lx\n", be64_to_cpu(c.c->target_hrmor)); |
| printf("stack_pointer: 0x%08lx\n", be64_to_cpu(c.c->stack_pointer)); |
| print_bytes((char *) "hw_pkey_a: ", (uint8_t *) c.c->hw_pkey_a, |
| sizeof(c.c->hw_pkey_a)); |
| print_bytes((char *) "hw_pkey_b: ", (uint8_t *) c.c->hw_pkey_b, |
| sizeof(c.c->hw_pkey_b)); |
| print_bytes((char *) "hw_pkey_c: ", (uint8_t *) c.c->hw_pkey_c, |
| sizeof(c.c->hw_pkey_c)); |
| |
| p = SHA512(c.c->hw_pkey_a, sizeof(ecc_key_t) * 3, md); |
| if (!p) |
| die(EX_SOFTWARE, "%s", "Cannot get SHA512"); |
| printf("HW keys hash (calculated):\n"); |
| print_bytes((char *) " ", (uint8_t *) md, sizeof(md)); |
| printf("\n"); |
| |
| printf("Prefix Header:\n"); |
| display_version_raw(c.ph->ver_alg); |
| printf("code_start_offset: %08lx\n", be64_to_cpu(c.ph->code_start_offset)); |
| printf("reserved: %08lx\n", be64_to_cpu(c.ph->reserved)); |
| printf("flags: %08x\n", be32_to_cpu(c.ph->flags)); |
| printf("sw_key_count: %02x\n", c.ph->sw_key_count); |
| printf("payload_size: %08lx\n", be64_to_cpu(c.ph->payload_size)); |
| print_bytes((char *) "payload_hash: ", (uint8_t *) c.ph->payload_hash, |
| sizeof(c.ph->payload_hash)); |
| printf("ecid_count: %02x\n", c.ph->ecid_count); |
| |
| for (int i = 0; i < c.ph->ecid_count; i++) { |
| printf("ecid: "); |
| print_bytes((char *) "ecid: ", |
| (uint8_t *) c.ph->ecid[i].ecid, sizeof(c.ph->ecid[i].ecid)); |
| printf("\n"); |
| } |
| printf("\n"); |
| |
| printf("Prefix Data:\n"); |
| print_bytes((char *) "hw_sig_a: ", (uint8_t *) c.pd->hw_sig_a, sizeof(c.pd->hw_sig_a)); |
| print_bytes((char *) "hw_sig_b: ", (uint8_t *) c.pd->hw_sig_b, sizeof(c.pd->hw_sig_b)); |
| print_bytes((char *) "hw_sig_c: ", (uint8_t *) c.pd->hw_sig_c, sizeof(c.pd->hw_sig_c)); |
| |
| if (c.ph->sw_key_count >=1) |
| print_bytes((char *) "sw_pkey_p: ", (uint8_t *) c.pd->sw_pkey_p, sizeof(c.pd->sw_pkey_p)); |
| if (c.ph->sw_key_count >=2) |
| print_bytes((char *) "sw_pkey_q: ", (uint8_t *) c.pd->sw_pkey_q, sizeof(c.pd->sw_pkey_q)); |
| if (c.ph->sw_key_count >=3) |
| print_bytes((char *) "sw_pkey_r: ", (uint8_t *) c.pd->sw_pkey_r, sizeof(c.pd->sw_pkey_r)); |
| |
| printf("\n"); |
| |
| printf("Software Header:\n"); |
| display_version_raw(c.sh->ver_alg); |
| printf("code_start_offset: %08lx\n", be64_to_cpu(c.sh->code_start_offset)); |
| printf("reserved: %08lx\n", be64_to_cpu(c.sh->reserved)); |
| printf("reserved (ASCII): %.8s\n", (char *) &(c.sh->reserved)); |
| printf("flags: %08x\n", be32_to_cpu(c.sh->flags)); |
| printf("reserved_0: %02x\n", c.sh->reserved_0); |
| printf("payload_size: %08lx (%lu)\n", be64_to_cpu(c.sh->payload_size), |
| be64_to_cpu(c.sh->payload_size)); |
| print_bytes((char *) "payload_hash: ", (uint8_t *) c.sh->payload_hash, |
| sizeof(c.sh->payload_hash)); |
| printf("ecid_count: %02x\n", c.sh->ecid_count); |
| |
| for (int i = 0; i < c.sh->ecid_count; i++) { |
| printf("ecid: "); |
| print_bytes((char *) "ecid: ", |
| (uint8_t *) c.sh->ecid[i].ecid, sizeof(c.sh->ecid[i].ecid)); |
| printf("\n"); |
| } |
| printf("\n"); |
| |
| printf("Software Signatures:\n"); |
| print_bytes((char *) "sw_sig_p: ", (uint8_t *) c.ssig->sw_sig_p, |
| sizeof(c.ssig->sw_sig_p)); |
| print_bytes((char *) "sw_sig_q: ", (uint8_t *) c.ssig->sw_sig_q, |
| sizeof(c.ssig->sw_sig_q)); |
| print_bytes((char *) "sw_sig_r: ", (uint8_t *) c.ssig->sw_sig_r, |
| sizeof(c.ssig->sw_sig_r)); |
| printf("\n"); |
| |
| if (print_stats) |
| display_container_stats(&c); |
| } |
| |
| static bool validate_container(struct parsed_stb_container c, int fdin) |
| { |
| static int n; |
| static int status = true; |
| |
| Keyprops *k; |
| |
| Keyprops hwKeylist[] = { |
| { 'a', "HW_key_A", &(c.c->hw_pkey_a), &(c.pd->hw_sig_a) }, |
| { 'b', "HW_key_B", &(c.c->hw_pkey_b), &(c.pd->hw_sig_b) }, |
| { 'c', "HW_key_C", &(c.c->hw_pkey_c), &(c.pd->hw_sig_c) }, |
| { 0, NULL, NULL, NULL }, |
| }; |
| Keyprops swKeylist[] = { |
| { 'p', "SW_key_P", &(c.pd->sw_pkey_p), &(c.ssig->sw_sig_p) }, |
| { 'q', "SW_key_Q", &(c.pd->sw_pkey_q), &(c.ssig->sw_sig_q) }, |
| { 'r', "SW_key_R", &(c.pd->sw_pkey_r), &(c.ssig->sw_sig_r) }, |
| { 0, NULL, NULL, NULL }, |
| }; |
| |
| void *md = alloca(SHA512_DIGEST_LENGTH); |
| void *p; |
| |
| // Get Prefix header hash. |
| p = SHA512((uint8_t *) c.ph, sizeof(ROM_prefix_header_raw), md); |
| if (!p) |
| die(EX_SOFTWARE, "%s", "Cannot get SHA512"); |
| if (verbose) print_bytes((char *) "PR header hash = ", (uint8_t *) md, |
| SHA512_DIGEST_LENGTH); |
| |
| // Verify HW key sigs. |
| for (k = hwKeylist; k->index; k++) { |
| |
| if (memcmp(k->key, &ECDSA_KEY_NULL, sizeof(ecc_key_t))) |
| status = verify_signature(k->name, md, SHA512_DIGEST_LENGTH, |
| *(k->sig), *(k->key)) && status; |
| else |
| if (verbose) printf("%s is NULL, skipping signature check.\n", k->name); |
| } |
| if (verbose) printf("\n"); |
| |
| // Get SW header hash. |
| p = SHA512((uint8_t *) c.sh, sizeof(ROM_sw_header_raw), md); |
| if (!p) |
| die(EX_SOFTWARE, "%s", "Cannot get SHA512"); |
| if (verbose) print_bytes((char *) "SW header hash = ", (uint8_t *) md, |
| SHA512_DIGEST_LENGTH); |
| |
| // Verify SW key sigs. |
| for (k = swKeylist, n = 1; k->index && n <= c.ph->sw_key_count; k++, n++) { |
| |
| if (memcmp(k->key, &ECDSA_KEY_NULL, sizeof(ecc_key_t))) |
| status = verify_signature(k->name, md, SHA512_DIGEST_LENGTH, |
| *(k->sig), *(k->key)) && status; |
| else |
| if (verbose) printf("%s is NULL, skipping\n", k->name); |
| } |
| if (verbose) printf("\n"); |
| |
| // Verify Payload hash. |
| status = getPayloadHash(fdin, md) && status; |
| if (verbose) print_bytes((char *) "Payload hash = ", (uint8_t *) md, |
| SHA512_DIGEST_LENGTH); |
| |
| if (memcmp((uint8_t *) c.sh->payload_hash, md, SHA512_DIGEST_LENGTH)) { |
| if (verbose) |
| printf("Payload hash does not agree with value in SW header: MISMATCH\n"); |
| status = false; |
| } else { |
| if (verbose) |
| printf("Payload hash agrees with value in SW header: VERIFIED ./\n"); |
| status = status && true; |
| } |
| if (verbose) printf("\n"); |
| |
| // Verify SW keys hash. |
| p = SHA512(c.pd->sw_pkey_p, sizeof(ecc_key_t) * c.ph->sw_key_count, md); |
| if (!p) |
| die(EX_SOFTWARE, "%s", "Cannot get SHA512"); |
| if (verbose) print_bytes((char *) "SW keys hash = ", (uint8_t *) md, |
| SHA512_DIGEST_LENGTH); |
| |
| if (memcmp((uint8_t *) c.ph->payload_hash, md, SHA512_DIGEST_LENGTH)) { |
| if (verbose) |
| printf("SW keys hash does not agree with value in Prefix header: MISMATCH\n"); |
| status = false; |
| } else { |
| if (verbose) |
| printf("SW keys hash agrees with value in Prefix header: VERIFIED ./\n"); |
| status = status && true; |
| } |
| if (verbose) printf("\n"); |
| return status; |
| } |
| |
| static bool verify_container(struct parsed_stb_container c, char * verify) |
| { |
| static int status = false; |
| |
| void *md = alloca(SHA512_DIGEST_LENGTH); |
| void *p; |
| void *md_verify; |
| |
| p = SHA512(c.c->hw_pkey_a, sizeof(ecc_key_t) * 3, md); |
| if (!p) |
| die(EX_SOFTWARE, "%s", "Cannot get SHA512"); |
| if (verbose) print_bytes((char *) "HW keys hash = ", (uint8_t *) md, |
| SHA512_DIGEST_LENGTH); |
| |
| md_verify = alloca(SHA512_DIGEST_LENGTH); |
| getVerificationHash(verify, md_verify, SHA512_DIGEST_LENGTH); |
| |
| if (memcmp((uint8_t *) md_verify, md, SHA512_DIGEST_LENGTH )) { |
| if (verbose) |
| printf("HW keys hash does not agree with provided value: MISMATCH\n"); |
| } else { |
| if (verbose) |
| printf("HW keys hash agrees with provided value: VERIFIED ./\n"); |
| status = true; |
| } |
| if (verbose) printf("\n"); |
| return status; |
| } |
| |
| static bool verify_signature(const char *moniker, const unsigned char *dgst, |
| int dgst_len, const ecc_signature_t sig_raw, const ecc_key_t key_raw) |
| { |
| int r; |
| bool status = false; |
| BIGNUM *r_bn, *s_bn; |
| ECDSA_SIG* ecdsa_sig; |
| EC_KEY *ec_key; |
| const EC_GROUP *ec_group; |
| unsigned char *buffer; |
| BIGNUM *key_bn; |
| EC_POINT *ec_point; |
| |
| // Convert the raw sig to a structure that can be handled by openssl. |
| debug_print((char *) "Raw sig = ", (uint8_t *) sig_raw, |
| sizeof(ecc_signature_t)); |
| |
| r_bn = BN_new(); |
| s_bn = BN_new(); |
| |
| BN_bin2bn((const unsigned char*) &sig_raw[0], 66, r_bn); |
| BN_bin2bn((const unsigned char*) &sig_raw[66], 66, s_bn); |
| |
| #if OPENSSL_VERSION_NUMBER >= 0x10100000L |
| ecdsa_sig = ECDSA_SIG_new(); |
| ECDSA_SIG_set0(ecdsa_sig, r_bn, s_bn); |
| #else |
| ecdsa_sig = malloc(sizeof(ECDSA_SIG)); |
| ecdsa_sig->r = r_bn; |
| ecdsa_sig->s = s_bn; |
| #endif |
| |
| // Convert the raw key to a structure that can be handled by openssl. |
| debug_print((char *) "Raw key = ", (uint8_t *) key_raw, |
| sizeof(ecc_key_t)); |
| |
| ec_key = EC_KEY_new(); |
| if (!ec_key) |
| die(EX_SOFTWARE, "%s", "Cannot EC_KEY_new"); |
| |
| ec_group = EC_GROUP_new_by_curve_name(NID_secp521r1); |
| if (!ec_group) |
| die(EX_SOFTWARE, "%s", "Cannot EC_GROUP_new_by_curve_name"); |
| |
| r = EC_KEY_set_group(ec_key, ec_group); |
| if (r == 0) |
| die(EX_SOFTWARE, "%s", "Cannot EC_KEY_set_group"); |
| |
| // Add prefix 0x04, for uncompressed key. |
| buffer = alloca(sizeof(ecc_key_t) + 1); |
| *buffer = 0x04; |
| memcpy(buffer + 1, key_raw, sizeof(ecc_key_t)); |
| |
| key_bn = BN_new(); |
| BN_bin2bn((const unsigned char*) buffer, EC_COORDBYTES * 2 + 1, key_bn); |
| |
| ec_point = EC_POINT_bn2point(ec_group, key_bn, NULL, NULL); |
| if (!ec_point) |
| die(EX_SOFTWARE, "%s", "Cannot EC_POINT_bn2point"); |
| |
| r = EC_KEY_set_public_key(ec_key, (const EC_POINT*) ec_point); |
| if (r == 0) |
| die(EX_SOFTWARE, "%s", "Cannot EC_KEY_set_public_key"); |
| |
| // Verify the signature. |
| r = ECDSA_do_verify(dgst, dgst_len, ecdsa_sig, ec_key); |
| if (r == 1) { |
| if (verbose) printf("%s signature is good: VERIFIED ./\n", moniker); |
| status = true; |
| } else if (r == 0) { |
| if (verbose) printf("%s signature FAILED to verify.\n", moniker); |
| status = false; |
| } else { |
| die(EX_SOFTWARE, "%s", "Cannot ECDSA_do_verify"); |
| } |
| |
| BN_free(r_bn); |
| BN_free(s_bn); |
| BN_free(key_bn); |
| |
| EC_KEY_free(ec_key); |
| |
| #if OPENSSL_VERSION_NUMBER >= 0x10100000L |
| ECDSA_SIG_free(ecdsa_sig); |
| #else |
| free(ecdsa_sig); |
| #endif |
| return status; |
| } |
| |
| static bool getPayloadHash(int fdin, unsigned char *md) |
| { |
| struct stat payload_st; |
| void *payload; |
| int r; |
| void *p; |
| |
| r = fstat(fdin, &payload_st); |
| if (r != 0) |
| die(EX_NOINPUT, "Cannot stat payload file at descriptor: %d (%s)", fdin, |
| strerror(errno)); |
| |
| payload = mmap(NULL, payload_st.st_size - SECURE_BOOT_HEADERS_SIZE, |
| PROT_READ, MAP_PRIVATE, fdin, SECURE_BOOT_HEADERS_SIZE); |
| if (!payload) |
| die(EX_OSERR, "Cannot mmap file at descriptor: %d (%s)", fdin, |
| strerror(errno)); |
| |
| p = SHA512(payload, payload_st.st_size - SECURE_BOOT_HEADERS_SIZE, md); |
| if (!p) |
| die(EX_SOFTWARE, "%s", "Cannot get SHA512"); |
| |
| return true; |
| } |
| |
| static bool getVerificationHash(char *input, unsigned char *md, int len) |
| { |
| char buf[len * 2 + 1 + 2]; // allow trailing \n and leading "0x" |
| char *p; |
| struct stat s; |
| int r; |
| |
| if (isValidHex(input, len)) { |
| p = input; |
| } else { |
| int fdin = open(input, O_RDONLY); |
| if (fdin <= 0) |
| die(EX_NOINPUT, "%s", |
| "Verify requested but no valid hash or hash file provided"); |
| |
| r = fstat(fdin, &s); |
| if (r != 0) |
| die(EX_NOINPUT, "Cannot stat hash file: %s (%s)", input, |
| strerror(errno)); |
| if ((size_t) s.st_size > (sizeof(buf))) |
| die(EX_DATAERR, |
| "Verify hash file \"%s\" invalid size: expected a %d byte hexadecimal value", |
| input, len); |
| |
| r = read(fdin, buf, s.st_size); |
| if (r <= 0) |
| die(EX_NOINPUT, "Cannot read hash file: %s (%s)", input, |
| strerror(errno)); |
| p = (char *) buf; |
| |
| for (unsigned int i = 0; i < sizeof(buf); i++) // strip newline char |
| if (buf[i] == '\n') |
| buf[i] = '\0'; |
| |
| close(fdin); |
| } |
| |
| // Convert hexascii to binary. |
| if (isValidHex(p, len)) { |
| if (!strncmp(p, "0x", 2)) // skip leading "0x" |
| p += 2; |
| for (int count = 0; count < len; count++) { |
| sscanf(p, "%2hhx", &md[count]); |
| p += 2; |
| } |
| } else |
| die(EX_DATAERR, |
| "Verify hash file \"%s\" invalid data: expected a %d byte hexadecimal value", |
| input, len); |
| |
| return true; |
| } |
| |
| __attribute__((__noreturn__)) static void usage (int status) |
| { |
| if (status != 0) { |
| fprintf(stderr, "Try '%s --help' for more information.\n", progname); |
| } |
| else { |
| printf("Usage: %s [options]\n", progname); |
| printf( |
| "\n" |
| "Options:\n" |
| " -h, --help display this message and exit\n" |
| " -v, --verbose show verbose output\n" |
| " -d, --debug show additional debug output\n" |
| " -w, --wrap column at which to wrap long output (wrap=0 => unlimited)\n" |
| " -s, --stats additionally print container stats\n" |
| " -I, --imagefile containerized image to display (input)\n" |
| " --validate perform all checks to ensure is container valid for secure boot\n" |
| " --verify value, or filename containing value, of the HW Keys hash to\n" |
| " verify the container against. must be valid 64 byte hexascii.\n" |
| "\n"); |
| }; |
| exit(status); |
| } |
| |
| static struct option const opts[] = { |
| { "help", no_argument, 0, 'h' }, |
| { "verbose", no_argument, 0, 'v' }, |
| { "debug", no_argument, 0, 'd' }, |
| { "wrap", required_argument, 0, 'w' }, |
| { "stats", no_argument, 0, 's' }, |
| { "imagefile", required_argument, 0, 'I' }, |
| { "validate", no_argument, 0, 128 }, |
| { "verify", required_argument, 0, 129 }, |
| { "no-print", no_argument, 0, 130 }, |
| { "print", no_argument, 0, 131 }, |
| { NULL, 0, NULL, 0 } |
| }; |
| |
| static struct { |
| char *imagefn; |
| bool validate; |
| char *verify; |
| bool print_container; |
| } params; |
| |
| |
| int main(int argc, char* argv[]) |
| { |
| int indexptr; |
| int r; |
| struct stat st; |
| void *container; |
| struct parsed_stb_container c; |
| int container_status = EX_OK; |
| int validate_status = UNATTEMPTED; |
| int verify_status = UNATTEMPTED; |
| int fdin; |
| |
| params.print_container = true; |
| |
| progname = strrchr(argv[0], '/'); |
| if (progname != NULL) |
| ++progname; |
| else |
| progname = argv[0]; |
| |
| while (1) { |
| int opt; |
| opt = getopt_long(argc, argv, "hvdw:sI:", opts, &indexptr); |
| if (opt == -1) |
| break; |
| |
| switch (opt) { |
| case 'h': |
| case '?': |
| usage(EX_OK); |
| break; |
| case 'v': |
| verbose = true; |
| break; |
| case 'd': |
| debug = true; |
| break; |
| case 'w': |
| wrap = atoi(optarg); |
| wrap = (wrap < 2) ? INT_MAX : wrap; |
| break; |
| case 's': |
| print_stats = true; |
| break; |
| case 'I': |
| params.imagefn = optarg; |
| break; |
| case 128: |
| params.validate = true; |
| break; |
| case 129: |
| params.verify = optarg; |
| break; |
| case 130: |
| params.print_container = false; |
| break; |
| case 131: |
| params.print_container = true; |
| break; |
| default: |
| usage(EX_USAGE); |
| } |
| } |
| |
| fdin = open(params.imagefn, O_RDONLY); |
| if (fdin <= 0) |
| die(EX_NOINPUT, "Cannot open container file: %s (%s)", params.imagefn, |
| strerror(errno)); |
| |
| r = fstat(fdin, &st); |
| if (r != 0) |
| die(EX_NOINPUT, "Cannot stat container file: %s (%s)", params.imagefn, |
| strerror(errno)); |
| |
| container = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fdin, 0); |
| if (!container) |
| die(EX_OSERR, "Cannot mmap file: %s (%s)", params.imagefn, |
| strerror(errno)); |
| |
| if (!stb_is_container(container, SECURE_BOOT_HEADERS_SIZE)) |
| die(EX_DATAERR, "%s", "Not a container, missing magic number"); |
| |
| if (parse_stb_container(container, SECURE_BOOT_HEADERS_SIZE, &c) != 0) |
| die(EX_DATAERR, "%s", "Failed to parse container"); |
| |
| if (params.print_container) |
| display_container(c); |
| |
| if (params.validate) |
| validate_status = validate_container(c, fdin); |
| |
| if (params.verify) |
| verify_status = verify_container(c, params.verify); |
| |
| if ((validate_status != UNATTEMPTED) || (verify_status != UNATTEMPTED)) { |
| |
| printf("Container validity check %s. Container verification check %s.\n\n", |
| (validate_status == UNATTEMPTED) ? |
| "not attempted" : |
| ((validate_status == PASSED) ? "PASSED" : "FAILED"), |
| (verify_status == UNATTEMPTED) ? |
| "not attempted" : |
| ((verify_status == PASSED) ? "PASSED" : "FAILED")); |
| |
| if ((validate_status == FAILED) || (verify_status == FAILED)) |
| container_status = 1; |
| } |
| |
| close(fdin); |
| return container_status; |
| } |