diff options
-rw-r--r-- | firmware/2lib/2api.c | 45 | ||||
-rw-r--r-- | firmware/2lib/2common.c | 3 | ||||
-rw-r--r-- | firmware/2lib/include/2api.h | 46 | ||||
-rw-r--r-- | futility/cmd_show.c | 68 | ||||
-rw-r--r-- | futility/file_type_bios.c | 138 | ||||
-rw-r--r-- | futility/file_type_bios.h | 16 | ||||
-rw-r--r-- | futility/file_type_rwsig.c | 10 | ||||
-rw-r--r-- | futility/futility.h | 5 | ||||
-rw-r--r-- | futility/misc.c | 52 | ||||
-rw-r--r-- | futility/vb2_helper.c | 16 | ||||
-rw-r--r-- | host/lib/cbfstool.c | 168 | ||||
-rw-r--r-- | host/lib/host_misc.c | 50 | ||||
-rw-r--r-- | host/lib/host_signature2.c | 25 | ||||
-rw-r--r-- | host/lib/include/cbfstool.h | 25 | ||||
-rw-r--r-- | host/lib/include/host_misc.h | 23 | ||||
-rw-r--r-- | host/lib/include/host_signature.h | 11 | ||||
-rw-r--r-- | tests/vb2_api_tests.c | 96 |
17 files changed, 657 insertions, 140 deletions
diff --git a/firmware/2lib/2api.c b/firmware/2lib/2api.c index 0d1ebe86..698e2bb7 100644 --- a/firmware/2lib/2api.c +++ b/firmware/2lib/2api.c @@ -305,14 +305,10 @@ vb2_error_t vb2api_init_hash(struct vb2_context *ctx, uint32_t tag) /* * Unpack the firmware data key to see which hashing algorithm we - * should use. - * - * TODO: really, the firmware body should be hashed, and not signed, - * because the signature we're checking is already signed as part of - * the firmware preamble. But until we can change the signing scripts, - * we're stuck with a signature here instead of a hash. + * should use. Zero body data size means, that signature contains + * metadata hash, so vb2api_get_metadata_hash() should be used instead. */ - if (!sd->data_key_size) + if (!sd->data_key_size || !pre->body_signature.data_size) return VB2_ERROR_API_INIT_HASH_DATA_KEY; VB2_TRY(vb2_unpack_key_buffer(&key, @@ -369,8 +365,9 @@ vb2_error_t vb2api_check_hash_get_digest(struct vb2_context *ctx, return VB2_ERROR_API_CHECK_HASH_TAG; /* - * The body signature is currently a *signature* of the body data, not - * just its hash. So we need to verify the signature. + * In case of verifying a whole memory region the body signature + * is a *signature* of the body data, not just its hash. + * So we need to verify the signature. */ /* Unpack the data key */ @@ -433,3 +430,33 @@ union vb2_fw_boot_info vb2api_get_fw_boot_info(struct vb2_context *ctx) return info; } + +vb2_error_t vb2api_get_metadata_hash(struct vb2_context *ctx, + struct vb2_hash **hash_ptr_out) +{ + struct vb2_shared_data *sd = vb2_get_sd(ctx); + struct vb2_workbuf wb; + struct vb2_fw_preamble *pre; + + vb2_workbuf_from_ctx(ctx, &wb); + + if (!sd->preamble_size) + return VB2_ERROR_API_CHECK_HASH_PREAMBLE; + pre = vb2_member_of(sd, sd->preamble_offset); + + /* Zero size of body signature indicates, that signature holds + vb2_hash inside. */ + if (pre->body_signature.data_size) + return VB2_ERROR_API_INIT_HASH_DATA_KEY; + + struct vb2_hash *hash = + (struct vb2_hash *)vb2_signature_data(&pre->body_signature); + const uint32_t hsize = vb2_digest_size(hash->algo); + if (!hsize || pre->body_signature.sig_size < + offsetof(struct vb2_hash, raw) + hsize) + return VB2_ERROR_API_CHECK_HASH_SIG_SIZE; + + *hash_ptr_out = hash; + + return VB2_SUCCESS; +} diff --git a/firmware/2lib/2common.c b/firmware/2lib/2common.c index 1d1ad690..3d08f5b2 100644 --- a/firmware/2lib/2common.c +++ b/firmware/2lib/2common.c @@ -156,6 +156,9 @@ vb2_error_t vb2_verify_digest(const struct vb2_public_key *key, /* A signature is destroyed in the process of being verified. */ uint8_t *sig_data = vb2_signature_data_mutable(sig); + if (!sig->data_size) + return VB2_ERROR_VDATA_NOT_ENOUGH_DATA; + if (sig->sig_size != vb2_rsa_sig_size(key->sig_alg)) { VB2_DEBUG("Wrong data signature size for algorithm, " "sig_size=%d, expected %d for algorithm %d.\n", diff --git a/firmware/2lib/include/2api.h b/firmware/2lib/include/2api.h index 03e49fe0..869d2361 100644 --- a/firmware/2lib/include/2api.h +++ b/firmware/2lib/include/2api.h @@ -150,19 +150,37 @@ enum vb2_pcr_digest { * Verify the hash of each section of code/data you need to boot the RW * firmware. For each section: * - * Call vb2_init_hash() to see if the hash exists. + * 1) Normal verification: * - * Load the data for the section. Call vb2_extend_hash() on the + * Call vb2api_init_hash() to see if the hash exists. + * + * Load the data for the section. Call vb2api_extend_hash() on the * data as you load it. You can load it all at once and make one * call, or load and hash-extend a block at a time. * - * Call vb2_check_hash() to see if the hash is valid. + * Call vb2api_check_hash() to see if the hash is valid. * * If it is valid, you may use the data and/or execute * code from that section. * * If the hash was invalid, you must reboot. * + * 2) Verification with CBFS integration: + * + * Call vb2api_get_metadata_hash() to get hash of CBFS metadata. + * + * Initialize CBFS using stored hash as correct metadata hash. + * + * If CBFS initialization fails because of metadata hash + * mismatch, you must reboot. + * + * If CBFS initialization succeeds, you may use the data + * and/or execute code from that section. + * IMPORTANT: Be aware, that to have full section + * verification, the CBFS_VERIFICATION has to be enabled. + * Initialization of CBFS volume only checks hash of files + * metadata, not their contents! + * * At this point, firmware verification is done, and vb2_context contains the * kernel key needed to verify the kernel. That context should be preserved * and passed on to kernel selection. The kernel selection process may be @@ -442,6 +460,9 @@ vb2_error_t vb2api_fw_phase3(struct vb2_context *ctx); /** * Initialize hashing data for the specified tag. + * This function is not legal when running from a coreboot image that has + * CONFIG_VBOOT_CBFS_INTEGRATION=y set. In that case, vb2api_get_metadata_hash() + * must be used instead. * * @param ctx Vboot context * @param tag Tag to start hashing (enum vb2_hash_tag) @@ -484,6 +505,23 @@ vb2_error_t vb2api_check_hash_get_digest(struct vb2_context *ctx, uint32_t digest_out_size); /** + * Get pointer to metadata hash from body signature in preamble. + * Body signature data size has to be zero to indicate that it contains + * metadata hash. This is only legal to call after vb2api_fw_phase3() has + * returned successfully, and will return with error otherwise. + * This function is only legal to call from coreboot with + * CONFIG_VBOOT_CBFS_INTEGRATION=y. `futility sign` will automatically detect + * the presence of that option in an image and prepare the correct kind + * of signature. + * + * @param ctx Vboot context + * @param hash_ptr_out pointer to output hash to + * @return VB2_SUCCESS, or error code on error. + */ +vb2_error_t vb2api_get_metadata_hash(struct vb2_context *ctx, + struct vb2_hash **hash_ptr_out); + +/** * Get a PCR digest * * @param ctx Vboot context @@ -726,6 +764,8 @@ vb2_gbb_flags_t vb2api_gbb_get_flags(struct vb2_context *ctx); /** * Get the size of the signed firmware body. This is only legal to call after * vb2api_fw_phase3() has returned successfully, and will return 0 otherwise. + * It will also return 0 when body signature contains metadata hash instead + * of body hash. * * @param ctx Vboot context * diff --git a/futility/cmd_show.c b/futility/cmd_show.c index 6ed15435..4b11b50f 100644 --- a/futility/cmd_show.c +++ b/futility/cmd_show.c @@ -22,6 +22,7 @@ #include "2common.h" #include "2sha.h" #include "2sysincludes.h" +#include "cbfstool.h" #include "file_type_bios.h" #include "file_type.h" #include "fmap.h" @@ -181,6 +182,53 @@ done: return retval; } +static int fw_show_metadata_hash(const char *name, enum bios_component body_c, + struct vb2_fw_preamble *pre) +{ + struct vb2_hash real_hash; + struct vb2_hash *body_hash = + (struct vb2_hash *)vb2_signature_data(&pre->body_signature); + const uint32_t bhsize = vb2_digest_size(body_hash->algo); + + if (!bhsize || pre->body_signature.sig_size < + offsetof(struct vb2_hash, raw) + bhsize) { + fprintf(stderr, "Body signature data is too small to " + "fit metadata hash.\n"); + return 1; + } + + printf(" Body metadata hash: %s", + vb2_get_hash_algorithm_name(body_hash->algo)); + if (vb2_digest_size(body_hash->algo)) { + putchar(':'); + print_bytes((uint8_t *)body_hash->raw, + vb2_digest_size(body_hash->algo)); + } + putchar('\n'); + + if (cbfstool_get_metadata_hash(name, fmap_name[body_c], &real_hash) != + VB2_SUCCESS || + real_hash.algo == VB2_HASH_INVALID) { + fprintf(stderr, "Failed to get metadata hash. Firmware body is" + " corrupted or is not a valid CBFS.\n"); + return 1; + } + + if (body_hash->algo != real_hash.algo || + !vb2_digest_size(body_hash->algo) || + memcmp(body_hash->raw, real_hash.raw, + vb2_digest_size(body_hash->algo))) { + printf(" MISMATCH! Real hash: %s:", + vb2_get_hash_algorithm_name(real_hash.algo)); + print_bytes(&real_hash.raw, vb2_digest_size(real_hash.algo)); + putchar('\n'); + fprintf(stderr, "Signature hash does not match with" + " real metadata hash.\n"); + return 1; + } + return 0; +} + int show_fw_preamble_buf(const char *name, uint8_t *buf, uint32_t len, void *data) { @@ -190,6 +238,7 @@ int show_fw_preamble_buf(const char *name, uint8_t *buf, uint32_t len, uint8_t *fv_data = show_option.fv; uint64_t fv_size = show_option.fv_size; struct bios_area_s *fw_body_area = 0; + enum bios_component body_c; int good_sig = 0; int retval = 0; @@ -217,9 +266,8 @@ int show_fw_preamble_buf(const char *name, uint8_t *buf, uint32_t len, } /* Identify the firmware body for this VBLOCK */ - enum bios_component body_c = state->c == BIOS_FMAP_VBLOCK_A - ? BIOS_FMAP_FW_MAIN_A - : BIOS_FMAP_FW_MAIN_B; + body_c = state->c == BIOS_FMAP_VBLOCK_A ? BIOS_FMAP_FW_MAIN_A + : BIOS_FMAP_FW_MAIN_B; fw_body_area = &state->area[body_c]; } @@ -288,11 +336,15 @@ int show_fw_preamble_buf(const char *name, uint8_t *buf, uint32_t len, return 0; } - if (VB2_SUCCESS != - vb2_verify_data(fv_data, fv_size, &pre2->body_signature, - &data_key, &wb)) { - fprintf(stderr, "Error verifying firmware body.\n"); - return 1; + if (pre2->body_signature.data_size) { + if (vb2_verify_data(fv_data, fv_size, &pre2->body_signature, + &data_key, &wb) != VB2_SUCCESS) { + fprintf(stderr, "Error verifying firmware body.\n"); + return 1; + } + } else if (state) { /* Only works for images with at least FW_MAIN_A */ + if (fw_show_metadata_hash(name, body_c, pre2)) + return 1; } done: diff --git a/futility/file_type_bios.c b/futility/file_type_bios.c index e900f271..884dfc8e 100644 --- a/futility/file_type_bios.c +++ b/futility/file_type_bios.c @@ -19,15 +19,6 @@ #include "host_common.h" #include "vb1_helper.h" -static const char * const fmap_name[] = { - "GBB", /* BIOS_FMAP_GBB */ - "FW_MAIN_A", /* BIOS_FMAP_FW_MAIN_A */ - "FW_MAIN_B", /* BIOS_FMAP_FW_MAIN_B */ - "VBLOCK_A", /* BIOS_FMAP_VBLOCK_A */ - "VBLOCK_B", /* BIOS_FMAP_VBLOCK_B */ -}; -_Static_assert(ARRAY_SIZE(fmap_name) == NUM_BIOS_COMPONENTS, - "Size of fmap_name[] should match NUM_BIOS_COMPONENTS"); static void fmap_limit_area(FmapAreaHeader *ah, uint32_t len) { @@ -248,9 +239,15 @@ static int write_new_preamble(struct bios_area_s *vblock, struct vb2_fw_preamble *preamble = NULL; int retval = 1; - body_sig = vb2_calculate_signature(fw_body->buf, fw_body->len, signkey); + if (fw_body->metadata_hash.algo != VB2_HASH_INVALID) + body_sig = + vb2_create_signature_from_hash(&fw_body->metadata_hash); + else + body_sig = vb2_calculate_signature(fw_body->buf, fw_body->len, + signkey); + if (!body_sig) { - ERROR("Error calculating body signature\n"); + ERROR("Error calculating or creating body signature\n"); goto end; } @@ -355,8 +352,8 @@ static int sign_bios_at_end(struct bios_state_s *state) /* Prepare firmware slot for signing. If fw_size is not zero, then it will be used as new length of signed area, for zero the length will be taken form FlashMap or preamble. */ -static int prepare_slot(uint8_t *buf, uint32_t len, size_t fw_size, - enum bios_component fw_c, enum bios_component vblock_c, +static int prepare_slot(uint8_t *buf, uint32_t len, enum bios_component fw_c, + enum bios_component vblock_c, struct bios_state_s *state) { FmapHeader *fmap; @@ -380,14 +377,16 @@ static int prepare_slot(uint8_t *buf, uint32_t len, size_t fw_size, fmap_limit_area(ah, len); state->area[fw_c].buf = buf + ah->area_offset; state->area[fw_c].is_valid = 1; - if (fw_size > ah->area_size) { + if (state->area[fw_c].fw_size > ah->area_size) { ERROR("%s size is incorrect.\n", fmap_name[fw_c]); return 1; - } else if (fw_size) { - state->area[fw_c].len = fw_size; + } else if (state->area[fw_c].fw_size) { + state->area[fw_c].len = state->area[fw_c].fw_size; } else { - WARN("%s does not contain CBFS. Trying to sign entire area.\n", - fmap_name[fw_c]); + if (state->area[fw_c].metadata_hash.algo == VB2_HASH_INVALID) + WARN("%s does not contain CBFS. Trying to sign entire " + "area.\n", + fmap_name[fw_c]); state->area[fw_c].len = ah->area_size; } @@ -441,7 +440,7 @@ static int prepare_slot(uint8_t *buf, uint32_t len, size_t fw_size, goto end; } - if (fw_size == 0) { + if (state->area[fw_c].fw_size == 0) { if (preamble->body_signature.data_size > state->area[fw_c].len) { ERROR("%s says the firmware is larger than we have.\n", @@ -475,6 +474,63 @@ end: return 0; } +static bool image_uses_cbfs_integration(const char *file) +{ + char *value; + bool rv = false; + + if (cbfstool_get_config_value(file, NULL, + "CONFIG_VBOOT_CBFS_INTEGRATION", + &value) != VB2_SUCCESS) + return false; + + if (value && strcmp("y", value) == 0) + rv = true; + + free(value); + return rv; +} + +static void image_check_and_prepare_cbfs(const char *file, + enum bios_component fw_c, + bool uses_cbfs_integration, + struct bios_state_s *state) +{ + if (!uses_cbfs_integration) { + if (cbfstool_truncate(file, fmap_name[fw_c], + &state->area[fw_c].fw_size) != + VB2_SUCCESS) { + VB2_DEBUG("CBFS not found in area %s\n", + fmap_name[fw_c]); + return; + } + VB2_DEBUG("CBFS found in area %s\n", fmap_name[fw_c]); + return; + } + + if (cbfstool_get_metadata_hash(file, fmap_name[fw_c], + &state->area[fw_c].metadata_hash) != + VB2_SUCCESS) + FATAL("CBFS metadata hash not found in area" + " %s. It is required for images with" + " VBOOT_CBFS_INTEGRATION", + fmap_name[fw_c]); + + VB2_DEBUG("CBFS metadata hash found in area %s\n", fmap_name[fw_c]); +} + +static void check_slot_after_prepare(enum bios_component fw_c, + bool uses_cbfs_integration, + struct bios_state_s *state) +{ + if (state->area[fw_c].is_valid && uses_cbfs_integration && + state->area[fw_c].metadata_hash.algo == VB2_HASH_INVALID) + FATAL("CBFS with metadata hash not found in area %s." + " It is required for images with" + " VBOOT_CBFS_INTEGRATION", + fmap_name[fw_c]); +} + int ft_sign_bios(const char *name, void *data) { int retval = 0; @@ -482,47 +538,35 @@ int ft_sign_bios(const char *name, void *data) int fd = -1; uint8_t *buf = NULL; uint32_t len = 0; - size_t fw_main_a_size = 0; - size_t fw_main_b_size = 0; - - bool fw_main_a_in_cbfs_mode = - cbfstool_truncate(name, fmap_name[BIOS_FMAP_FW_MAIN_A], - &fw_main_a_size) == VB2_SUCCESS; - - bool fw_main_b_in_cbfs_mode = - cbfstool_truncate(name, fmap_name[BIOS_FMAP_FW_MAIN_B], - &fw_main_b_size) == VB2_SUCCESS; + bool uses_cbfs_integration = + image_uses_cbfs_integration(name); - if (fw_main_a_in_cbfs_mode) - VB2_DEBUG("CBFS found in area %s\n", - fmap_name[BIOS_FMAP_FW_MAIN_A]); - else - VB2_DEBUG("CBFS not found in area %s\n", - fmap_name[BIOS_FMAP_FW_MAIN_A]); + memset(&state, 0, sizeof(state)); - if (fw_main_b_in_cbfs_mode) - VB2_DEBUG("CBFS found in area %s\n", - fmap_name[BIOS_FMAP_FW_MAIN_B]); - else - VB2_DEBUG("CBFS not found in area %s\n", - fmap_name[BIOS_FMAP_FW_MAIN_B]); + image_check_and_prepare_cbfs(name, BIOS_FMAP_FW_MAIN_A, + uses_cbfs_integration, &state); + image_check_and_prepare_cbfs(name, BIOS_FMAP_FW_MAIN_B, + uses_cbfs_integration, &state); if (futil_open_and_map_file(name, &fd, FILE_MODE_SIGN(sign_option), &buf, &len)) return 1; - memset(&state, 0, sizeof(state)); - - retval = prepare_slot(buf, len, fw_main_a_size, BIOS_FMAP_FW_MAIN_A, - BIOS_FMAP_VBLOCK_A, &state); + retval = prepare_slot(buf, len, BIOS_FMAP_FW_MAIN_A, BIOS_FMAP_VBLOCK_A, + &state); if (retval) goto done; - retval = prepare_slot(buf, len, fw_main_b_size, BIOS_FMAP_FW_MAIN_B, - BIOS_FMAP_VBLOCK_B, &state); + retval = prepare_slot(buf, len, BIOS_FMAP_FW_MAIN_B, BIOS_FMAP_VBLOCK_B, + &state); if (retval && state.area[BIOS_FMAP_FW_MAIN_B].is_valid) goto done; + check_slot_after_prepare(BIOS_FMAP_FW_MAIN_A, uses_cbfs_integration, + &state); + check_slot_after_prepare(BIOS_FMAP_FW_MAIN_B, uses_cbfs_integration, + &state); + retval = sign_bios_at_end(&state); done: futil_unmap_and_close_file(fd, FILE_MODE_SIGN(sign_option), buf, len); diff --git a/futility/file_type_bios.h b/futility/file_type_bios.h index 469e3fd0..1f957b1c 100644 --- a/futility/file_type_bios.h +++ b/futility/file_type_bios.h @@ -8,6 +8,8 @@ #include <stdint.h> +#include "futility.h" + /* * The Chrome OS BIOS must contain specific FMAP areas, which we want to look * at in a certain order. @@ -22,6 +24,16 @@ enum bios_component { NUM_BIOS_COMPONENTS }; +static const char * const fmap_name[] = { + "GBB", /* BIOS_FMAP_GBB */ + "FW_MAIN_A", /* BIOS_FMAP_FW_MAIN_A */ + "FW_MAIN_B", /* BIOS_FMAP_FW_MAIN_B */ + "VBLOCK_A", /* BIOS_FMAP_VBLOCK_A */ + "VBLOCK_B", /* BIOS_FMAP_VBLOCK_B */ +}; +_Static_assert(ARRAY_SIZE(fmap_name) == NUM_BIOS_COMPONENTS, + "Size of fmap_name[] should match NUM_BIOS_COMPONENTS"); + /* Location information for each component */ struct bios_area_s { uint32_t offset; /* to avoid pointer math */ @@ -32,6 +44,10 @@ struct bios_area_s { /* VBLOCK only */ uint32_t flags; uint32_t version; + + /* FW_MAIN only */ + size_t fw_size; /* effective size from cbfstool (if available) */ + struct vb2_hash metadata_hash; }; /* State to track as we visit all components */ diff --git a/futility/file_type_rwsig.c b/futility/file_type_rwsig.c index 86e9bf29..56d92c5f 100644 --- a/futility/file_type_rwsig.c +++ b/futility/file_type_rwsig.c @@ -32,14 +32,6 @@ #define SIGNATURE_RSVD_SIZE 1024 #define EC_RW_FILENAME "EC_RW.bin" -static inline void vb2_print_bytes(const void *ptr, uint32_t len) -{ - const uint8_t *buf = (const uint8_t *)ptr; - int i; - for (i = 0; i < len; i++) - printf("%02x", *buf++); -} - static void show_sig(const char *name, const struct vb21_signature *sig) { printf("Signature: %s\n", name); @@ -52,7 +44,7 @@ static void show_sig(const char *name, const struct vb21_signature *sig) printf(" Total size: %#x (%d)\n", sig->c.total_size, sig->c.total_size); printf(" ID: "); - vb2_print_bytes(&sig->id, sizeof(sig->id)); + print_bytes(&sig->id, sizeof(sig->id)); printf("\n"); printf(" Data size: %#x (%d)\n", sig->data_size, sig->data_size); diff --git a/futility/futility.h b/futility/futility.h index 18063d97..2bf3d7f4 100644 --- a/futility/futility.h +++ b/futility/futility.h @@ -159,6 +159,11 @@ enum futil_file_err futil_unmap_and_close_file(int fd, enum file_mode mode, */ void parse_digest_or_die(uint8_t *buf, int len, const char *str); +/* + * Print provided buffer as hex string + */ +void print_bytes(const void *ptr, size_t len); + /* The CPU architecture is occasionally important */ enum arch_t { ARCH_UNSPECIFIED, diff --git a/futility/misc.c b/futility/misc.c index 8366bc92..85358b29 100644 --- a/futility/misc.c +++ b/futility/misc.c @@ -27,6 +27,7 @@ #include "cgptlib_internal.h" #include "file_type.h" #include "futility.h" +#include "host_misc.h" /* Default is to support everything we can */ enum vboot_version vboot_version = VBOOT_VERSION_ALL; @@ -420,53 +421,18 @@ enum futil_file_type ft_recognize_gpt(uint8_t *buf, uint32_t len) return FILE_TYPE_CHROMIUMOS_DISK; } -static int parse_hex(uint8_t *val, const char *str) +void parse_digest_or_die(uint8_t *buf, int len, const char *str) { - uint8_t v = 0; - char c; - int digit; - - for (digit = 0; digit < 2; digit++) { - c = *str; - if (!c) - return 0; - if (!isxdigit(c)) - return 0; - c = tolower(c); - if (c >= '0' && c <= '9') - v += c - '0'; - else - v += 10 + c - 'a'; - if (!digit) - v <<= 4; - str++; + if (!parse_hash(buf, len, str)) { + fprintf(stderr, "Invalid DIGEST \"%s\"\n", str); + exit(1); } - - *val = v; - return 1; } -void parse_digest_or_die(uint8_t *buf, int len, const char *str) +void print_bytes(const void *ptr, size_t len) { - const char *s = str; - int i; - - for (i = 0; i < len; i++) { - /* skip whitespace */ - while (*s && isspace(*s)) - s++; - if (!*s) - break; - if (!parse_hex(buf, s)) - break; - - /* on to the next byte */ - s += 2; - buf++; - } + const uint8_t *buf = (const uint8_t *)ptr; - if ((i != len) || *s) { - fprintf(stderr, "Invalid DIGEST \"%s\"\n", str); - exit(1); - } + for (size_t i = 0; i < len; i++) + printf("%02x", *buf++); } diff --git a/futility/vb2_helper.c b/futility/vb2_helper.c index 9e875b98..afc0019f 100644 --- a/futility/vb2_helper.c +++ b/futility/vb2_helper.c @@ -38,14 +38,6 @@ enum futil_file_type ft_recognize_vb21_key(uint8_t *buf, uint32_t len) return FILE_TYPE_UNKNOWN; } -static inline void vb2_print_bytes(const void *ptr, uint32_t len) -{ - const uint8_t *buf = (const uint8_t *)ptr; - int i; - for (i = 0; i < len; i++) - printf("%02x", *buf++); -} - static int vb2_public_key_sha1sum(struct vb2_public_key *key, struct vb2_hash *hash) { @@ -81,12 +73,12 @@ int show_vb21_pubkey_buf(const char *name, uint8_t *buf, uint32_t len, vb2_get_hash_algorithm_name(key.hash_alg)); printf(" Version: 0x%08x\n", key.version); printf(" ID: "); - vb2_print_bytes(key.id, sizeof(*key.id)); + print_bytes(key.id, sizeof(*key.id)); printf("\n"); if (vb2_public_key_sha1sum(&key, &hash) && memcmp(key.id, hash.sha1, sizeof(*key.id))) { printf(" Key sha1sum: "); - vb2_print_bytes(hash.sha1, sizeof(hash.sha1)); + print_bytes(hash.sha1, sizeof(hash.sha1)); printf("\n"); } return 0; @@ -148,12 +140,12 @@ int ft_show_vb21_privkey(const char *name, void *data) printf(" Hash Algorithm: %d %s\n", key->hash_alg, vb2_get_hash_algorithm_name(key->hash_alg)); printf(" ID: "); - vb2_print_bytes(&key->id, sizeof(key->id)); + print_bytes(&key->id, sizeof(key->id)); printf("\n"); if (vb2_private_key_sha1sum(key, &hash) && memcmp(&key->id, hash.sha1, sizeof(key->id))) { printf(" Key sha1sum: "); - vb2_print_bytes(hash.sha1, sizeof(hash.sha1)); + print_bytes(hash.sha1, sizeof(hash.sha1)); printf("\n"); } vb2_private_key_free(key); diff --git a/host/lib/cbfstool.c b/host/lib/cbfstool.c index 4bbed028..0f6ed16f 100644 --- a/host/lib/cbfstool.c +++ b/host/lib/cbfstool.c @@ -4,9 +4,12 @@ */ #include "2common.h" +#include "2crypto.h" #include "2return_codes.h" -#include "subprocess.h" #include "cbfstool.h" +#include "host_misc.h" +#include "subprocess.h" +#include "vboot_host.h" static const char *get_cbfstool_path(void) { @@ -68,3 +71,166 @@ vb2_error_t cbfstool_truncate(const char *file, const char *region, return VB2_SUCCESS; } + +/* Requires null-terminated buffer */ +static vb2_error_t extract_metadata_hash(const char *buf, struct vb2_hash *hash) +{ + enum vb2_hash_algorithm algo; + const char *to_find = "\n[METADATA HASH]"; + char *algo_str = NULL; + char *hash_str = NULL; + vb2_error_t rv = VB2_ERROR_CBFSTOOL; + + const char *start = strstr(buf, to_find); + if (start) + start += strlen(to_find); + + if (start) { + const int matches = sscanf(start, " %m[^:\n\t ]:%m[^:\n\t ]", + &algo_str, &hash_str); + + if (matches < 2) + goto done; + + if (!algo_str || !vb2_lookup_hash_alg(algo_str, &algo) || + algo == VB2_HASH_INVALID) + goto done; + hash->algo = algo; + + if (!hash_str || + strlen(hash_str) != (vb2_digest_size(algo) * 2) || + !parse_hash(&hash->raw[0], vb2_digest_size(algo), hash_str)) + goto done; + + if (!strstr(buf, "] fully valid")) + goto done; + + rv = VB2_SUCCESS; + } + +done: + if (rv != VB2_SUCCESS) + hash->algo = VB2_HASH_INVALID; + + free(algo_str); + free(hash_str); + + return rv; +} + +vb2_error_t cbfstool_get_metadata_hash(const char *file, const char *region, + struct vb2_hash *hash) +{ + int status; + const char *cbfstool = get_cbfstool_path(); + const size_t data_buffer_sz = 1024 * 1024; + char *data_buffer = malloc(data_buffer_sz); + vb2_error_t rv = VB2_ERROR_CBFSTOOL; + + if (!data_buffer) + goto done; + + memset(hash, 0, sizeof(*hash)); + hash->algo = VB2_HASH_INVALID; + + struct subprocess_target output = { + .type = TARGET_BUFFER_NULL_TERMINATED, + .buffer = { + .buf = data_buffer, + .size = data_buffer_sz, + }, + }; + const char *argv[] = { + cbfstool, file, "print", "-kv", + region ? "-r" : NULL, region, NULL + }; + + status = subprocess_run(argv, &subprocess_null, &output, + &subprocess_null); + + if (status < 0) { + fprintf(stderr, "%s(): cbfstool invocation failed: %m\n", + __func__); + exit(1); + } + + if (status > 0) + goto done; + + rv = extract_metadata_hash(data_buffer, hash); + +done: + free(data_buffer); + return rv; +} + +/* Requires null-terminated buffer */ +static char *extract_config_value(const char *buf, const char *config_field) +{ + char *to_find = NULL; + + if (asprintf(&to_find, "\n%s=", config_field) == -1) { + fprintf(stderr, "Out of Memory\n"); + VB2_DIE("Out of memory\n"); + } + + const char *start = strstr(buf, to_find); + if (start) + start += strlen(to_find); + + free(to_find); + + if (start) { + char *end = strchr(start, '\n'); + if (end) + return strndup(start, end - start); + } + + return NULL; +} + +vb2_error_t cbfstool_get_config_value(const char *file, const char *region, + const char *config_field, char **value) +{ + int status; + const char *cbfstool = get_cbfstool_path(); + const size_t data_buffer_sz = 1024 * 1024; + char *data_buffer = malloc(data_buffer_sz); + vb2_error_t rv = VB2_ERROR_CBFSTOOL; + + *value = NULL; + + if (!data_buffer) + goto done; + + struct subprocess_target output = { + .type = TARGET_BUFFER_NULL_TERMINATED, + .buffer = { + .buf = data_buffer, + .size = data_buffer_sz, + }, + }; + const char *argv[] = { + cbfstool, file, "extract", "-n", "config", "-f", "/dev/stdout", + region ? "-r" : NULL, region, NULL + }; + + status = subprocess_run(argv, &subprocess_null, &output, + &subprocess_null); + + if (status < 0) { + fprintf(stderr, "%s(): cbfstool invocation failed: %m\n", + __func__); + exit(1); + } + + if (status > 0) + goto done; + + *value = extract_config_value(data_buffer, config_field); + + rv = VB2_SUCCESS; +done: + free(data_buffer); + return rv; +} diff --git a/host/lib/host_misc.c b/host/lib/host_misc.c index 63d1fee4..899d807f 100644 --- a/host/lib/host_misc.c +++ b/host/lib/host_misc.c @@ -116,3 +116,53 @@ vb2_error_t WriteFile(const char* filename, const void *data, uint64_t size) fclose(f); return 0; } + +bool parse_hex(uint8_t *val, const char *str) +{ + uint8_t v = 0; + char c; + int digit; + + for (digit = 0; digit < 2; digit++) { + c = *str; + if (!c) + return false; + if (!isxdigit(c)) + return false; + c = tolower(c); + if (c >= '0' && c <= '9') + v += c - '0'; + else + v += 10 + c - 'a'; + if (!digit) + v <<= 4; + str++; + } + + *val = v; + return true; +} + +bool parse_hash(uint8_t *buf, size_t len, const char *str) +{ + const char *s = str; + int i; + + for (i = 0; i < len; i++) { + /* skip whitespace */ + while (*s && isspace(*s)) + s++; + if (!*s) + break; + if (!parse_hex(buf, s)) + break; + + /* on to the next byte */ + s += 2; + buf++; + } + + if (i != len || *s) + return false; + return true; +} diff --git a/host/lib/host_signature2.c b/host/lib/host_signature2.c index 5ec307d6..a588d1ee 100644 --- a/host/lib/host_signature2.c +++ b/host/lib/host_signature2.c @@ -132,3 +132,28 @@ struct vb2_signature *vb2_calculate_signature( /* Return the signature */ return sig; } + +struct vb2_signature * +vb2_create_signature_from_hash(const struct vb2_hash *hash) +{ + const uint32_t hsize = vb2_digest_size(hash->algo); + + /* Unsupported algorithm */ + if (!hsize) + return NULL; + + const uint32_t full_hsize = offsetof(struct vb2_hash, raw) + hsize; + + /* The body size is unknown, so set it to zero */ + struct vb2_signature *sig = + (struct vb2_signature *)vb2_alloc_signature(full_hsize, 0); + if (!sig) + return NULL; + + if (!memcpy(vb2_signature_data_mutable(sig), hash, full_hsize)) { + free(sig); + return NULL; + } + + return sig; +} diff --git a/host/lib/include/cbfstool.h b/host/lib/include/cbfstool.h index acc6e927..863039ec 100644 --- a/host/lib/include/cbfstool.h +++ b/host/lib/include/cbfstool.h @@ -4,9 +4,34 @@ */ #include "2return_codes.h" +#include "2sha.h" #define ENV_CBFSTOOL "CBFSTOOL" #define DEFAULT_CBFSTOOL "cbfstool" vb2_error_t cbfstool_truncate(const char *file, const char *region, size_t *new_size); + +/* + * Check whether image under `file` path supports CBFS_VERIFICATION, + * and contains metadata hash. Hash found is available under *hash. If it was + * not found, then hash type will be set to VB2_HASH_INVALID. + * + * If `region` is NULL, then region option will not be passed to cbfstool. + * Operations will be performed on default `COREBOOT` region. + */ +vb2_error_t cbfstool_get_metadata_hash(const char *file, const char *region, + struct vb2_hash *hash); + +/* + * Get value of `config` file field. + * + * This function extracts "config" file from selected region, parses it to find + * value of `config_field`, and returns it to `value` as allocated string + * (which has to be freed) or NULL if value was not found. + * + * If `region` is NULL, then region option will not be passed to cbfstool. + * Operations will be performed on default `COREBOOT` region. + */ +vb2_error_t cbfstool_get_config_value(const char *file, const char *region, + const char *config_field, char **value); diff --git a/host/lib/include/host_misc.h b/host/lib/include/host_misc.h index 1a59af54..96aba057 100644 --- a/host/lib/include/host_misc.h +++ b/host/lib/include/host_misc.h @@ -8,6 +8,8 @@ #ifndef VBOOT_REFERENCE_HOST_MISC_H_ #define VBOOT_REFERENCE_HOST_MISC_H_ +#include <stdbool.h> + #include "vboot_struct.h" #include "vboot_api.h" @@ -99,4 +101,25 @@ static inline const uint32_t roundup32(uint32_t v) */ uint32_t vb2_desc_size(const char *desc); +/** + * Parse byte from hex string. + * + * @param val Pointer to the value buffer + * @param str String to parse + * @return true on success, false otherwise. + * */ +bool parse_hex(uint8_t *val, const char *str); + + +/** + * Parse hash from string. + * + * @param buf Output buffer. Has to be at least `len` bytes long + * @param len Hash length in bytes + * @param str Hash string form. Has to be at least 2 * `len` long + * Whitespaces are ignored + * @return true on success, false otherwise. + */ +bool parse_hash(uint8_t *buf, size_t len, const char *str); + #endif /* VBOOT_REFERENCE_HOST_MISC_H_ */ diff --git a/host/lib/include/host_signature.h b/host/lib/include/host_signature.h index 47ffe764..d154a9d5 100644 --- a/host/lib/include/host_signature.h +++ b/host/lib/include/host_signature.h @@ -85,4 +85,15 @@ struct vb2_signature *vb2_external_signature(const uint8_t *data, uint32_t size, uint32_t key_algorithm, const char *external_signer); +/** + * Create signature using the provided hash as its body. Created signature + * contains vb2_hash trimmed to fit digest of its algorithm and nothing more. + * + * @param hash Hash to create signature from + * + * @return The signature, or NULL if error. Caller must free() it. + */ +struct vb2_signature * +vb2_create_signature_from_hash(const struct vb2_hash *hash); + #endif /* VBOOT_REFERENCE_HOST_SIGNATURE_H_ */ diff --git a/tests/vb2_api_tests.c b/tests/vb2_api_tests.c index 23078cf9..3ed8bd5f 100644 --- a/tests/vb2_api_tests.c +++ b/tests/vb2_api_tests.c @@ -38,6 +38,16 @@ const int mock_sig_size = 64; static uint8_t digest_result[VB2_SHA256_DIGEST_SIZE]; static const uint32_t digest_result_size = sizeof(digest_result); +static const struct vb2_hash mock_mhash = { + .algo = VB2_HASH_SHA256, + .sha256 = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + }, +}; + /* Mocked function data */ static int force_dev_mode; @@ -56,6 +66,7 @@ enum reset_type { FOR_MISC, FOR_EXTEND_HASH, FOR_CHECK_HASH, + FOR_METADATA_HASH, }; static void reset_common_data(enum reset_type t) @@ -93,15 +104,31 @@ static void reset_common_data(enum reset_type t) sd->preamble_size = sizeof(*pre); vb2_set_workbuf_used(ctx, sd->preamble_offset + sd->preamble_size); pre = vb2_member_of(sd, sd->preamble_offset); - pre->body_signature.data_size = mock_body_size; - pre->body_signature.sig_size = mock_sig_size; - pre->flags = 0; - sd->data_key_offset = sd->workbuf_used; - sd->data_key_size = sizeof(*k) + 8; - vb2_set_workbuf_used(ctx, sd->data_key_offset + sd->data_key_size); - k = vb2_member_of(sd, sd->data_key_offset); - k->algorithm = mock_algorithm; + if (t == FOR_METADATA_HASH) { + struct vb2_hash *mhash; + pre->body_signature.data_size = 0; + pre->body_signature.sig_size = offsetof(struct vb2_hash, raw) + + vb2_digest_size(mock_mhash.algo); + pre->body_signature.sig_offset = + vb2_offset_of(&pre->body_signature, + vb2_member_of(sd, sd->workbuf_used)); + pre->flags = 0; + mhash = vb2_member_of(sd, sd->workbuf_used); + vb2_set_workbuf_used(ctx, pre->body_signature.sig_size); + memcpy(mhash, &mock_mhash, pre->body_signature.sig_size); + } else { + pre->body_signature.data_size = mock_body_size; + pre->body_signature.sig_size = mock_sig_size; + pre->flags = 0; + + sd->data_key_offset = sd->workbuf_used; + sd->data_key_size = sizeof(*k) + 8; + vb2_set_workbuf_used(ctx, + sd->data_key_offset + sd->data_key_size); + k = vb2_member_of(sd, sd->data_key_offset); + k->algorithm = mock_algorithm; + } if (t == FOR_EXTEND_HASH || t == FOR_CHECK_HASH) vb2api_init_hash(ctx, VB2_HASH_TAG_FW_BODY); @@ -604,6 +631,7 @@ static void init_hash_tests(void) { struct vb2_packed_key *k; int wb_used_before; + struct vb2_fw_preamble *pre; /* For now, all we support is body signature hash */ reset_common_data(FOR_MISC); @@ -649,6 +677,12 @@ static void init_hash_tests(void) VB2_ERROR_API_INIT_HASH_DATA_KEY, "init hash data key"); reset_common_data(FOR_MISC); + pre = vb2_member_of(sd, sd->preamble_offset); + pre->body_signature.data_size = 0; + TEST_EQ(vb2api_init_hash(ctx, VB2_HASH_TAG_FW_BODY), + VB2_ERROR_API_INIT_HASH_DATA_KEY, "init hash data size"); + + reset_common_data(FOR_MISC); sd->data_key_size--; TEST_EQ(vb2api_init_hash(ctx, VB2_HASH_TAG_FW_BODY), VB2_ERROR_UNPACK_KEY_SIZE, "init hash data key size"); @@ -792,6 +826,50 @@ static void check_hash_tests(void) VB2_ERROR_RSA_VERIFY_DIGEST, "check hash finalize"); } +static void get_metadata_hash_test(void) +{ + struct vb2_hash *hash; + struct vb2_fw_preamble *pre; + + reset_common_data(FOR_MISC); + TEST_EQ(vb2api_get_metadata_hash(ctx, &hash), + VB2_ERROR_API_INIT_HASH_DATA_KEY, "check body sig data size"); + + reset_common_data(FOR_MISC); + sd->preamble_size = 0; + TEST_EQ(vb2api_get_metadata_hash(ctx, &hash), + VB2_ERROR_API_CHECK_HASH_PREAMBLE, "check preamble size"); + + reset_common_data(FOR_METADATA_HASH); + pre = vb2_member_of(sd, sd->preamble_offset); + pre->body_signature.sig_size = 8; + TEST_EQ(vb2api_get_metadata_hash(ctx, &hash), + VB2_ERROR_API_CHECK_HASH_SIG_SIZE, "check too small sig size"); + + reset_common_data(FOR_METADATA_HASH); + pre = vb2_member_of(sd, sd->preamble_offset); + pre->body_signature.data_size = mock_body_size; + TEST_EQ(vb2api_get_metadata_hash(ctx, &hash), + VB2_ERROR_API_INIT_HASH_DATA_KEY, "check incorrect data size"); + + reset_common_data(FOR_METADATA_HASH); + pre = vb2_member_of(sd, sd->preamble_offset); + hash = (struct vb2_hash *)vb2_signature_data_mutable( + &pre->body_signature); + hash->algo = VB2_HASH_INVALID; + TEST_EQ(vb2api_get_metadata_hash(ctx, &hash), + VB2_ERROR_API_CHECK_HASH_SIG_SIZE, "check unsupported algo"); + + reset_common_data(FOR_METADATA_HASH); + TEST_EQ(vb2api_get_metadata_hash(ctx, &hash), VB2_SUCCESS, + "check metadata hash get"); + TEST_PTR_NEQ(hash, NULL, "check metadata hash not null"); + TEST_EQ(hash->algo, mock_mhash.algo, "check metadata hash algo"); + TEST_EQ(memcmp(hash->raw, mock_mhash.raw, + vb2_digest_size(mock_mhash.algo)), + 0, "check metadata hash digest"); +} + int main(int argc, char* argv[]) { misc_tests(); @@ -803,6 +881,8 @@ int main(int argc, char* argv[]) extend_hash_tests(); check_hash_tests(); + get_metadata_hash_test(); + get_pcr_digest_tests(); return gTestSuccess ? 0 : 255; |