diff options
36 files changed, 725 insertions, 191 deletions
@@ -445,12 +445,14 @@ USE_FLASHROM ?= 1 ifneq ($(filter-out 0,${USE_FLASHROM}),) $(info building with libflashrom support) FLASHROM_LIBS := $(shell ${PKG_CONFIG} --libs flashrom) -COMMONLIB_SRCS = \ +COMMONLIB_SRCS += \ host/lib/flashrom.c \ host/lib/flashrom_drv.c CFLAGS += -DUSE_FLASHROM endif -COMMONLIB_SRCS += host/lib/subprocess.c +COMMONLIB_SRCS += \ + host/lib/subprocess.c \ + host/lib/cbfstool.c # Intermediate library for the vboot_reference utilities to link against. UTILLIB = ${BUILD}/libvboot_util.a diff --git a/firmware/2lib/include/2return_codes.h b/firmware/2lib/include/2return_codes.h index ca30ffa7..ccb50aa4 100644 --- a/firmware/2lib/include/2return_codes.h +++ b/firmware/2lib/include/2return_codes.h @@ -846,6 +846,9 @@ enum vb2_return_code { /* Flashrom exited with failure status */ VB2_ERROR_FLASHROM, + /* cbfstool exited with failure status */ + VB2_ERROR_CBFSTOOL, + /********************************************************************** * Errors generated by host library key functions */ diff --git a/futility/file_type_bios.c b/futility/file_type_bios.c index 13428c14..29ecfd4e 100644 --- a/futility/file_type_bios.c +++ b/futility/file_type_bios.c @@ -5,10 +5,12 @@ #include <errno.h> #include <limits.h> +#include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <string.h> +#include "cbfstool.h" #include "file_type_bios.h" #include "file_type.h" #include "fmap.h" @@ -31,7 +33,7 @@ static void fmap_limit_area(FmapAreaHeader *ah, uint32_t len) { uint32_t sum = ah->area_offset + ah->area_size; if (sum < ah->area_size || sum > len) { - VB2_DEBUG("%s %#x + %#x > %#x\n", + VB2_DEBUG("%.*s %#x + %#x > %#x\n", FMAP_NAMELEN, ah->area_name, ah->area_offset, ah->area_size, len); ah->area_offset = 0; ah->area_size = 0; @@ -237,114 +239,35 @@ int ft_show_bios(const char *name, void *data) /** Sign functions **/ -/* - * This handles FW_MAIN_A and FW_MAIN_B while signing a BIOS image. The data is - * just the RW firmware blob so there's nothing useful to do with it, but we'll - * mark it as valid so that we'll know that this FMAP area exists and can - * be signed. - */ -static int fmap_sign_fw_main(const char *name, uint8_t *buf, uint32_t len, - void *data) -{ - struct bios_state_s *state = (struct bios_state_s *)data; - state->area[state->c].is_valid = 1; - return 0; -} - -/* - * This handles VBLOCK_A and VBLOCK_B while processing a BIOS image. We don't - * do any signing here. We just check to see if the existing FMAP area contains - * a firmware preamble so we can preserve its contents. We do the signing once - * we've looked over all the components. - */ -static int fmap_sign_fw_preamble(const char *name, uint8_t *buf, uint32_t len, - void *data) -{ - static uint8_t workbuf[VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE] - __attribute__((aligned(VB2_WORKBUF_ALIGN))); - static struct vb2_workbuf wb; - vb2_workbuf_init(&wb, workbuf, sizeof(workbuf)); - - struct vb2_keyblock *keyblock = (struct vb2_keyblock *)buf; - struct bios_state_s *state = (struct bios_state_s *)data; - - /* - * If we have a valid keyblock and fw_preamble, then we can use them to - * determine the size of the firmware body. Otherwise, we'll have to - * just sign the whole region. - */ - if (VB2_SUCCESS != vb2_verify_keyblock_hash(keyblock, len, &wb)) { - fprintf(stderr, "Warning: %s keyblock is invalid. " - "Signing the entire FW FMAP region...\n", name); - goto whatever; - } - - if (vb2_packed_key_looks_ok(&keyblock->data_key, - keyblock->data_key.key_offset + - keyblock->data_key.key_size)) { - fprintf(stderr, "Warning: %s public key is invalid. " - "Signing the entire FW FMAP region...\n", name); - goto whatever; - } - uint32_t more = keyblock->keyblock_size; - struct vb2_fw_preamble *preamble = - (struct vb2_fw_preamble *)(buf + more); - uint32_t fw_size = preamble->body_signature.data_size; - struct bios_area_s *fw_body_area = 0; - - switch (state->c) { - case BIOS_FMAP_VBLOCK_A: - fw_body_area = &state->area[BIOS_FMAP_FW_MAIN_A]; - /* Preserve the flags if they're not specified */ - if (!sign_option.flags_specified) - sign_option.flags = preamble->flags; - break; - case BIOS_FMAP_VBLOCK_B: - fw_body_area = &state->area[BIOS_FMAP_FW_MAIN_B]; - break; - default: - FATAL("Can only handle VBLOCK_A or VBLOCK_B\n"); - } - - if (fw_size > fw_body_area->len) { - fprintf(stderr, - "%s says the firmware is larger than we have\n", - name); - return 1; - } - - /* Update the firmware size */ - fw_body_area->len = fw_size; - -whatever: - state->area[state->c].is_valid = 1; - - return 0; -} - static int write_new_preamble(struct bios_area_s *vblock, struct bios_area_s *fw_body, struct vb2_private_key *signkey, struct vb2_keyblock *keyblock) { - struct vb2_signature *body_sig; - struct vb2_fw_preamble *preamble; + struct vb2_signature *body_sig = NULL; + struct vb2_fw_preamble *preamble = NULL; + int retval = 1; body_sig = vb2_calculate_signature(fw_body->buf, fw_body->len, signkey); if (!body_sig) { - fprintf(stderr, "Error calculating body signature\n"); - return 1; + ERROR("Error calculating body signature\n"); + goto end; } - preamble = vb2_create_fw_preamble(sign_option.version, + preamble = vb2_create_fw_preamble(vblock->version, (struct vb2_packed_key *)sign_option.kernel_subkey, body_sig, signkey, - sign_option.flags); + vblock->flags); if (!preamble) { - fprintf(stderr, "Error creating firmware preamble.\n"); + ERROR("Error creating firmware preamble.\n"); free(body_sig); - return 1; + goto end; + } + + if (keyblock->keyblock_size + preamble->preamble_size > vblock->len) { + ERROR("Keyblock and preamble do not fit in VBLOCK.\n"); + goto end; } /* Write the new keyblock */ @@ -352,11 +275,13 @@ static int write_new_preamble(struct bios_area_s *vblock, memcpy(vblock->buf, keyblock, more); /* and the new preamble */ memcpy(vblock->buf + more, preamble, preamble->preamble_size); + retval = 0; +end: free(preamble); free(body_sig); - return 0; + return retval; } static int write_loem(const char *ab, struct bios_area_s *vblock) @@ -402,8 +327,7 @@ static int sign_bios_at_end(struct bios_state_s *state) struct bios_area_s *fw_b = &state->area[BIOS_FMAP_FW_MAIN_B]; int retval = 0; - if (!vblock_a->is_valid || !vblock_b->is_valid || - !fw_a->is_valid || !fw_b->is_valid) { + if (!vblock_a->is_valid || !fw_a->is_valid) { fprintf(stderr, "Something's wrong. Not changing anything\n"); return 1; } @@ -411,78 +335,197 @@ static int sign_bios_at_end(struct bios_state_s *state) retval |= write_new_preamble(vblock_a, fw_a, sign_option.signprivate, sign_option.keyblock); - retval |= write_new_preamble(vblock_b, fw_b, sign_option.signprivate, - sign_option.keyblock); + if (vblock_b->is_valid && fw_b->is_valid) + retval |= write_new_preamble(vblock_b, fw_b, + sign_option.signprivate, + sign_option.keyblock); + else + INFO("BIOS image does not have %s. Signing only %s\n", + fmap_name[BIOS_FMAP_FW_MAIN_B], + fmap_name[BIOS_FMAP_FW_MAIN_A]); if (sign_option.loemid) { retval |= write_loem("A", vblock_a); - retval |= write_loem("B", vblock_b); + if (vblock_b->is_valid) + retval |= write_loem("B", vblock_b); } return retval; } -/* Functions to call while preparing to sign the bios */ -static int (*fmap_sign_fn[])(const char *name, uint8_t *buf, uint32_t len, - void *data) = { - 0, - fmap_sign_fw_main, - fmap_sign_fw_main, - fmap_sign_fw_preamble, - fmap_sign_fw_preamble, -}; -_Static_assert(ARRAY_SIZE(fmap_sign_fn) == NUM_BIOS_COMPONENTS, - "Size of fmap_sign_fn[] should match NUM_BIOS_COMPONENTS"); +/* 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, + struct bios_state_s *state) +{ + FmapHeader *fmap; + FmapAreaHeader *ah; + const char *fw_main_name = fmap_name[fw_c]; + const char *vblock_name = fmap_name[vblock_c]; + static uint8_t workbuf[VB2_FIRMWARE_WORKBUF_RECOMMENDED_SIZE] + __attribute__((aligned(VB2_WORKBUF_ALIGN))); + static struct vb2_workbuf wb; + + fmap = fmap_find(buf, len); + vb2_workbuf_init(&wb, workbuf, sizeof(workbuf)); + + VB2_DEBUG("Preparing areas: %s and %s\n", fw_main_name, vblock_name); + + /* FW_MAIN */ + if (!fmap_find_by_name(buf, len, fmap, fw_main_name, &ah)) { + ERROR("%s area not found in FMAP\n", fw_main_name); + return 1; + } + 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) { + ERROR("%s size is incorrect.\n", fmap_name[fw_c]); + return 1; + } else if (fw_size) { + state->area[fw_c].len = fw_size; + } else { + WARN("%s does not contain CBFS. Trying to sign entire area.\n", + fmap_name[fw_c]); + state->area[fw_c].len = ah->area_size; + } + + /* Corresponding VBLOCK */ + if (!fmap_find_by_name(buf, len, fmap, vblock_name, &ah)) { + ERROR("%s area not found in FMAP\n", vblock_name); + return 1; + } + fmap_limit_area(ah, len); + state->area[vblock_c].buf = buf + ah->area_offset; + state->area[vblock_c].len = ah->area_size; + + struct vb2_keyblock *keyblock = + (struct vb2_keyblock *)state->area[vblock_c].buf; + int keyblock_valid = 0; + + if (vb2_verify_keyblock_hash(keyblock, state->area[vblock_c].len, + &wb) != VB2_SUCCESS) { + WARN("%s keyblock is invalid.\n", vblock_name); + goto end; + } + + if (vb2_packed_key_looks_ok(&keyblock->data_key, + keyblock->data_key.key_offset + + keyblock->data_key.key_size)) { + WARN("%s public key is invalid.\n", vblock_name); + goto end; + } + + struct vb2_public_key data_key; + if (vb2_unpack_key(&data_key, &keyblock->data_key) != VB2_SUCCESS) { + WARN("%s data key is invalid. Failed to parse.\n", vblock_name); + goto end; + } + + if (keyblock->keyblock_size + sizeof(struct vb2_fw_preamble) > + state->area[vblock_c].len) { + ERROR("%s is invalid. Keyblock and preamble do not fit.\n", + vblock_name); + goto end; + } + + struct vb2_fw_preamble *preamble = + (struct vb2_fw_preamble *)(state->area[vblock_c].buf + + keyblock->keyblock_size); + if (vb2_verify_fw_preamble(preamble, + state->area[vblock_c].len - + keyblock->keyblock_size, + &data_key, &wb)) { + WARN("%s preamble is invalid.\n", vblock_name); + goto end; + } + + if (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", + vblock_name); + goto end; + } else { + state->area[fw_c].len = + preamble->body_signature.data_size; + } + } + + keyblock_valid = 1; + +end: + if (sign_option.flags_specified) + state->area[vblock_c].flags = sign_option.flags; + else if (keyblock_valid) + state->area[vblock_c].flags = preamble->flags; + else + state->area[vblock_c].flags = 0; + + if (sign_option.version_specified) + state->area[vblock_c].version = sign_option.version; + else if (keyblock_valid) + state->area[vblock_c].version = preamble->firmware_version; + else + state->area[vblock_c].version = 1; + + state->area[vblock_c].is_valid = 1; + + return 0; +} int ft_sign_bios(const char *name, void *data) { - FmapHeader *fmap; - FmapAreaHeader *ah = 0; - char ah_name[FMAP_NAMELEN + 1]; - enum bios_component c; int retval = 0; struct bios_state_s state; int fd = -1; uint8_t *buf = NULL; uint32_t len = 0; - - retval = futil_open_and_map_file(name, &fd, FILE_MODE_SIGN(sign_option), - &buf, &len); - if (retval) + 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; + + 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]); + + 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]); + + if (futil_open_and_map_file(name, &fd, FILE_MODE_SIGN(sign_option), + &buf, &len)) return 1; memset(&state, 0, sizeof(state)); - /* We've already checked, so we know this will work. */ - fmap = fmap_find(buf, len); - for (c = 0; c < NUM_BIOS_COMPONENTS; c++) { - /* We know one of these will work, too */ - if (fmap_find_by_name(buf, len, fmap, fmap_name[c], &ah)) { - /* But the file might be truncated */ - fmap_limit_area(ah, len); - /* The name is not necessarily null-terminated */ - snprintf(ah_name, sizeof(ah_name), "%s", ah->area_name); - - /* Update the state we're passing around */ - state.c = c; - state.area[c].buf = buf + ah->area_offset; - state.area[c].len = ah->area_size; - - VB2_DEBUG("examining FMAP area %d (%s)," - " offset=0x%08x len=0x%08x\n", - c, ah_name, ah->area_offset, ah->area_size); - - /* Go look at it, but abort on error */ - if (fmap_sign_fn[c]) - retval += fmap_sign_fn[c](ah_name, - state.area[c].buf, - state.area[c].len, - &state); - } - } + retval = prepare_slot(buf, len, fw_main_a_size, BIOS_FMAP_FW_MAIN_A, + BIOS_FMAP_VBLOCK_A, &state); + if (retval) + goto done; - retval += sign_bios_at_end(&state); + retval = prepare_slot(buf, len, fw_main_b_size, BIOS_FMAP_FW_MAIN_B, + BIOS_FMAP_VBLOCK_B, &state); + if (retval && state.area[BIOS_FMAP_FW_MAIN_B].is_valid) + goto done; + retval = sign_bios_at_end(&state); +done: futil_unmap_and_close_file(fd, FILE_MODE_SIGN(sign_option), buf, len); return retval; } @@ -490,16 +533,23 @@ int ft_sign_bios(const char *name, void *data) enum futil_file_type ft_recognize_bios_image(uint8_t *buf, uint32_t len) { FmapHeader *fmap; - enum bios_component c; fmap = fmap_find(buf, len); if (!fmap) return FILE_TYPE_UNKNOWN; - for (c = 0; c < NUM_BIOS_COMPONENTS; c++) - if (!fmap_find_by_name(buf, len, fmap, fmap_name[c], 0)) - break; - if (c == NUM_BIOS_COMPONENTS) + /* Correct BIOS image should contain at least GBB, FW_MAIN_A and + VBLOCK_A areas. FW_MAIN_B and VBLOCK_B are optional, but will be + signed or shown if present. */ + const int gbb_and_a_slot_ok = + fmap_find_by_name(buf, len, fmap, fmap_name[BIOS_FMAP_GBB], + 0) != NULL && + fmap_find_by_name(buf, len, fmap, + fmap_name[BIOS_FMAP_FW_MAIN_A], 0) != NULL && + fmap_find_by_name(buf, len, fmap, fmap_name[BIOS_FMAP_VBLOCK_A], + 0) != NULL; + + if (gbb_and_a_slot_ok) return FILE_TYPE_BIOS_IMAGE; return FILE_TYPE_UNKNOWN; diff --git a/futility/file_type_bios.h b/futility/file_type_bios.h index ef401077..8aa2d633 100644 --- a/futility/file_type_bios.h +++ b/futility/file_type_bios.h @@ -28,6 +28,10 @@ struct bios_area_s { uint8_t *buf; uint32_t len; uint32_t is_valid; + + /* VBLOCK only */ + uint32_t flags; + uint32_t version; }; /* State to track as we visit all components */ diff --git a/host/lib/cbfstool.c b/host/lib/cbfstool.c new file mode 100644 index 00000000..ff70f3d2 --- /dev/null +++ b/host/lib/cbfstool.c @@ -0,0 +1,69 @@ +/* Copyright 2022 The ChromiumOS Authors. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "2common.h" +#include "2return_codes.h" +#include "subprocess.h" +#include "cbfstool.h" + +static const char *get_cbfstool_path(void) +{ + static const char *cbfstool = NULL; + + if (cbfstool) + return cbfstool; + + const char *env_cbfstool = getenv(ENV_CBFSTOOL); + if (env_cbfstool && env_cbfstool[0] != '\0') { + cbfstool = strdup(env_cbfstool); + return cbfstool; + } + + cbfstool = DEFAULT_CBFSTOOL; + return cbfstool; +} + +vb2_error_t cbfstool_truncate(const char *file, const char *region, + size_t *new_size) +{ + int status; + char output_buffer[128]; + const char *cbfstool = get_cbfstool_path(); + + struct subprocess_target output = { + .type = TARGET_BUFFER_NULL_TERMINATED, + .buffer = { + .buf = output_buffer, + .size = sizeof(output_buffer), + }, + }; + const char *const argv[] = { + cbfstool, file, "truncate", "-r", region, NULL, + }; + + VB2_DEBUG("Calling: %s '%s' truncate -r '%s'\n", cbfstool, file, + region); + status = subprocess_run(argv, &subprocess_null, &output, + &subprocess_null); + + if (status < 0) + return VB2_ERROR_CBFSTOOL; + + /* Positive exit code means something is wrong with image. Return zero + as new size, because it might be problem with missing CBFS.*/ + if (status > 0) { + *new_size = 0; + return VB2_ERROR_CBFSTOOL; + } + + if (sscanf(output_buffer, "%zi", new_size) != 1) { + VB2_DEBUG("Failed to parse command output. Unexpected " + "output.\n"); + *new_size = 0; + return VB2_ERROR_CBFSTOOL; + } + + return VB2_SUCCESS; +} diff --git a/host/lib/include/cbfstool.h b/host/lib/include/cbfstool.h new file mode 100644 index 00000000..1343d406 --- /dev/null +++ b/host/lib/include/cbfstool.h @@ -0,0 +1,12 @@ +/* Copyright 2022 The ChromiumOS Authors. + * Use of this source code is governed by a BSD-style license that can be + * found in the LICENSE file. + */ + +#include "2return_codes.h" + +#define ENV_CBFSTOOL "CBFSTOOL" +#define DEFAULT_CBFSTOOL "cbfstool" + +vb2_error_t cbfstool_truncate(const char *file, const char *region, + size_t *new_size); diff --git a/tests/futility/data/README b/tests/futility/data/README index 933de029..3d577b82 100644 --- a/tests/futility/data/README +++ b/tests/futility/data/README @@ -2,3 +2,6 @@ These are officially signed BIOS images from existing Chromebooks. bios_link_mp.bin uses the RO_NORMAL flag to skip RW firmware validation bios_peppy_mp.bin doesn't do any of those things + +This is dev-signed BIOS image with CBFS support: + bios_voxel_dev.bin diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_data_key_offset_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_data_key_offset_too_big.xxd.patch new file mode 100644 index 00000000..cb79555c --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_data_key_offset_too_big.xxd.patch @@ -0,0 +1 @@ +00200050: b808 0000 0000 0000 0804 0000 0000 0000 ................ diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_data_key_size_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_data_key_size_big.xxd.patch new file mode 100644 index 00000000..8667c3f9 --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_data_key_size_big.xxd.patch @@ -0,0 +1 @@ +00200050: 2000 0000 0000 0000 3412 0100 0000 0000 .......4....... diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_fmap_too_small.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_fmap_too_small.xxd.patch new file mode 100644 index 00000000..2c01f2aa --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_fmap_too_small.xxd.patch @@ -0,0 +1,2 @@ +00610100: 0000 0000 0000 0000 0100 0000 2000 6e00 ............ .n. +00610110: 0000 5642 4c4f 434b 5f41 0000 0000 0000 ..VBLOCK_A...... diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_fmap_too_small_for_whole.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_fmap_too_small_for_whole.xxd.patch new file mode 100644 index 00000000..3d797038 --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_fmap_too_small_for_whole.xxd.patch @@ -0,0 +1,2 @@ +00610100: 0000 0000 0000 0000 0100 0000 2000 b608 ............ ... +00610110: 0000 5642 4c4f 434b 5f41 0000 0000 0000 ..VBLOCK_A...... diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_data_size_too_small.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_data_size_too_small.xxd.patch new file mode 100644 index 00000000..4f284d90 --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_data_size_too_small.xxd.patch @@ -0,0 +1 @@ +00200040: 6c00 0000 0000 0000 1700 0000 0000 0000 l............... diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_invalid_contents.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_invalid_contents.xxd.patch new file mode 100644 index 00000000..8b5fefd2 --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_invalid_contents.xxd.patch @@ -0,0 +1 @@ +00200470: babb 3f4b 95db d458 4142 4344 4142 4344 ..?K...XABCDABCD diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_offset_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_offset_too_big.xxd.patch new file mode 100644 index 00000000..1a7044cf --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_offset_too_big.xxd.patch @@ -0,0 +1 @@ +00200030: 9808 0000 0000 0000 4000 0000 0000 0000 ........@....... diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_size_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_size_too_big.xxd.patch new file mode 100644 index 00000000..e6d90bf8 --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_hash_size_too_big.xxd.patch @@ -0,0 +1 @@ +00200030: 4804 0000 0000 0000 b808 0000 0000 0000 H............... diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_invalid_magic.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_invalid_magic.xxd.patch new file mode 100644 index 00000000..00015e2e --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_invalid_magic.xxd.patch @@ -0,0 +1 @@ +00200000: 4142 4344 4143 4244 0200 0000 0100 0000 ABCDACBD........ diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_invalid_major_version.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_invalid_major_version.xxd.patch new file mode 100644 index 00000000..637b90ac --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_invalid_major_version.xxd.patch @@ -0,0 +1 @@ +00200000: 4348 524f 4d45 4f53 0200 0000 0200 0000 CHROMEOS........ diff --git a/tests/futility/data/bios_peppy_dev.bad_keyblock_size_not_fully_signed.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_keyblock_size_not_fully_signed.xxd.patch new file mode 100644 index 00000000..1a24d433 --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_keyblock_size_not_fully_signed.xxd.patch @@ -0,0 +1 @@ +00200010: 7604 0000 0000 0000 a004 0000 0000 0000 v............... diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_body_signature_offset_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_body_signature_offset_too_big.xxd.patch new file mode 100644 index 00000000..31408e53 --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_preamble_body_signature_offset_too_big.xxd.patch @@ -0,0 +1 @@ +00200900: 0100 0000 0000 0000 7408 0000 0000 0000 ........t....... diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_body_signature_size_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_body_signature_size_too_big.xxd.patch new file mode 100644 index 00000000..083cfbe6 --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_preamble_body_signature_size_too_big.xxd.patch @@ -0,0 +1 @@ +00200910: 7408 0000 0000 0000 183c 0200 0000 0000 t........<...... diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_fmap_too_small.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_fmap_too_small.xxd.patch new file mode 100644 index 00000000..0f0968dd --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_preamble_fmap_too_small.xxd.patch @@ -0,0 +1,2 @@ +00610100: 0000 0000 0000 0000 0100 0000 2000 ee08 ............ ... +00610110: 0000 5642 4c4f 434b 5f41 0000 0000 0000 ..VBLOCK_A...... diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_fmap_too_small_for_whole.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_fmap_too_small_for_whole.xxd.patch new file mode 100644 index 00000000..bd756243 --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_preamble_fmap_too_small_for_whole.xxd.patch @@ -0,0 +1,2 @@ +00610100: 0000 0000 0000 0000 0100 0000 2000 2411 ............ .$. +00610110: 0000 5642 4c4f 434b 5f41 0000 0000 0000 ..VBLOCK_A...... diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_header_version_major.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_header_version_major.xxd.patch new file mode 100644 index 00000000..456a753d --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_preamble_header_version_major.xxd.patch @@ -0,0 +1 @@ +002008d0: 7406 0000 0000 0000 0300 0000 0100 0000 t............... diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_header_version_minor.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_header_version_minor.xxd.patch new file mode 100644 index 00000000..140b44e1 --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_preamble_header_version_minor.xxd.patch @@ -0,0 +1 @@ +002008d0: 7406 0000 0000 0000 0200 0000 0000 0000 t............... diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_kernel_subkey_offset_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_kernel_subkey_offset_too_big.xxd.patch new file mode 100644 index 00000000..3c5a14c7 --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_preamble_kernel_subkey_offset_too_big.xxd.patch @@ -0,0 +1 @@ +002008f0: 7408 0000 0000 0000 0700 0000 0000 0000 t............... diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_kernel_subkey_size_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_kernel_subkey_size_too_big.xxd.patch new file mode 100644 index 00000000..3c5a14c7 --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_preamble_kernel_subkey_size_too_big.xxd.patch @@ -0,0 +1 @@ +002008f0: 7408 0000 0000 0000 0700 0000 0000 0000 t............... diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_signature_data_size_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_data_size_too_big.xxd.patch new file mode 100644 index 00000000..f23f2e11 --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_data_size_too_big.xxd.patch @@ -0,0 +1 @@ +002008d0: 8408 0000 0000 0000 0200 0000 0100 0000 ................ diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_signature_data_size_too_small.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_data_size_too_small.xxd.patch new file mode 100644 index 00000000..b02b4976 --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_data_size_too_small.xxd.patch @@ -0,0 +1 @@ +002008d0: 6400 0000 0000 0000 0200 0000 0100 0000 d............... diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_signature_invalid_contents.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_invalid_contents.xxd.patch new file mode 100644 index 00000000..0fed2fe6 --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_invalid_contents.xxd.patch @@ -0,0 +1,2 @@ +00200f20: 78c9 3a24 85ab ca17 498e c238 4142 4344 x.:$....I..8ABCD +00200f30: 4142 4344 1cef bf68 b86b cdbc 3782 9f85 ABCD...h.k..7... diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_signature_offset_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_offset_too_big.xxd.patch new file mode 100644 index 00000000..5159fef1 --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_offset_too_big.xxd.patch @@ -0,0 +1 @@ +002008c0: 7408 0000 0000 0000 0002 0000 0000 0000 t............... diff --git a/tests/futility/data/bios_peppy_dev.bad_preamble_signature_size_too_big.xxd.patch b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_size_too_big.xxd.patch new file mode 100644 index 00000000..9afc0abf --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bad_preamble_signature_size_too_big.xxd.patch @@ -0,0 +1 @@ +002008c0: 6c06 0000 0000 0000 7408 0000 0000 0000 l.......t....... diff --git a/tests/futility/data/bios_peppy_dev.bin b/tests/futility/data/bios_peppy_dev.bin Binary files differnew file mode 100644 index 00000000..f241cd03 --- /dev/null +++ b/tests/futility/data/bios_peppy_dev.bin diff --git a/tests/futility/data/bios_voxel_dev.bin b/tests/futility/data/bios_voxel_dev.bin Binary files differnew file mode 100644 index 00000000..7cd85355 --- /dev/null +++ b/tests/futility/data/bios_voxel_dev.bin diff --git a/tests/futility/data/bios_voxel_dev.no_b_slot.xxd.patch b/tests/futility/data/bios_voxel_dev.no_b_slot.xxd.patch new file mode 100644 index 00000000..6a01277c --- /dev/null +++ b/tests/futility/data/bios_voxel_dev.no_b_slot.xxd.patch @@ -0,0 +1,20 @@ +01804030: 0000 0000 0000 1e00 0000 0000 0000 5000 ..............P. +018043a0: 0000 0000 0000 0000 0000 c0ff 4f01 4000 ............O.@. +018043b0: 0000 5257 5f46 5749 445f 4200 0000 0000 ..RW_FWID_B..... +018043d0: 0000 0000 0000 5001 0000 3000 4d45 5f52 ......P...0.ME_R +018043e0: 575f 4200 0000 0000 0000 0000 0000 0000 W_B............. +018043f0: 0000 0000 0000 0000 0000 0000 0000 0000 ................ +01804400: 8001 0000 8000 5750 5f52 4f00 0000 0000 ......WP_RO..... +01804420: 0000 0000 0000 0000 0000 8001 0040 0000 .............@.. +01804430: 524f 5f56 5044 0000 0000 0000 0000 0000 RO_VPD.......... +01804450: 0800 0040 8001 00c0 7f00 524f 5f53 4543 ...@......RO_SEC +01804460: 5449 4f4e 0000 0000 0000 0000 0000 0000 TION............ +01804470: 0000 0000 0000 0000 0000 0000 0040 8001 .............@.. +01804480: 0008 0000 464d 4150 0000 0000 0000 0000 ....FMAP........ +018044a0: 0000 0000 0000 0048 8001 4000 0000 524f .......H..@...RO +018044b0: 5f46 5249 4400 0000 0000 0000 0000 0000 _FRID........... +018044d0: 0050 8001 0000 0700 4742 4200 0000 0000 .P......GBB..... +018044f0: 0000 0000 0000 0000 0000 0050 8701 00b0 ...........P.... +01804500: 7800 434f 5245 424f 4f54 0000 0000 0000 x.COREBOOT...... +01804520: 0000 0000 0050 8701 00b0 7800 434f 5245 .....P....x.CORE +01804530: 424f 4f54 0000 0000 0000 0000 0000 0000 BOOT............ diff --git a/tests/futility/data_bios_voxel_dev.bin_expect.txt b/tests/futility/data_bios_voxel_dev.bin_expect.txt new file mode 100644 index 00000000..60cd1eab --- /dev/null +++ b/tests/futility/data_bios_voxel_dev.bin_expect.txt @@ -0,0 +1,6 @@ +b11d74edd286c144e1135b49e7f0bc20cf041f10 +c14bd720b70d97394257e3e826bd8f43de48d4ed +e2c1c92d7d7aa7dfed5e8375edd30b7ae52b7450 +5d2b220899c4403d564092ada3f12d3cc4483223 +e2c1c92d7d7aa7dfed5e8375edd30b7ae52b7450 +5d2b220899c4403d564092ada3f12d3cc4483223 diff --git a/tests/futility/test_sign_firmware.sh b/tests/futility/test_sign_firmware.sh index 587eed9f..c373803f 100755 --- a/tests/futility/test_sign_firmware.sh +++ b/tests/futility/test_sign_firmware.sh @@ -10,24 +10,90 @@ TMP="${me}.tmp" cd "$OUTDIR" KEYDIR="${SRCDIR}/tests/devkeys" +DATADIR="${SCRIPT_DIR}/futility/data" # The input BIOS images are all signed with MP keys. We resign them with dev # keys, which means we can precalculate the expected results. Note that the # script does not change the root or recovery keys in the GBB. INFILES=" -${SCRIPT_DIR}/futility/data/bios_link_mp.bin -${SCRIPT_DIR}/futility/data/bios_peppy_mp.bin +${DATADIR}/bios_link_mp.bin +${DATADIR}/bios_peppy_mp.bin +" + +# BIOS image containing CBFS RW/A and RW/B, and signed with developer keys. +GOOD_CBFS="${DATADIR}/bios_voxel_dev.bin" + +# BIOS image containing CBFS RW/A and RW/B, and signed with developer keys. +INFILES="${INFILES} +${GOOD_CBFS} " # We also want to test that we can sign an image without any valid firmware # preambles. That one won't be able to tell how much of the FW_MAIN region is # the valid firmware, so it'll have to sign the entire region. -GOOD_VBLOCKS="${SCRIPT_DIR}/futility/data/bios_peppy_mp.bin" +GOOD_VBLOCKS="${DATADIR}/bios_peppy_mp.bin" ONEMORE=bios_peppy_mp_no_vblock.bin +CLEAN_B=bios_peppy_mp_clean_b_slot.bin cp "${GOOD_VBLOCKS}" "${ONEMORE}" +cp "${GOOD_VBLOCKS}" "${CLEAN_B}" + +GOOD_DEV="${DATADIR}/bios_peppy_dev.bin" + +NO_B_SLOT_PATCH="${DATADIR}/bios_voxel_dev.no_b_slot.xxd.patch" + +BAD_KEYBLOCK_PATCHES=( +"${DATADIR}/bios_peppy_dev.bad_keyblock_data_key_offset_too_big.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_keyblock_data_key_size_big.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_keyblock_hash_data_size_too_small.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_keyblock_hash_invalid_contents.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_keyblock_hash_offset_too_big.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_keyblock_hash_size_too_big.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_keyblock_invalid_magic.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_keyblock_invalid_major_version.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_keyblock_size_not_fully_signed.xxd.patch" +) + +BAD_PREAMBLE_PATCHES=( +"${DATADIR}/bios_peppy_dev.bad_preamble_body_signature_offset_too_big.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_preamble_body_signature_size_too_big.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_preamble_header_version_major.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_preamble_header_version_minor.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_preamble_kernel_subkey_offset_too_big.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_preamble_kernel_subkey_size_too_big.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_preamble_signature_data_size_too_big.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_preamble_signature_data_size_too_small.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_preamble_signature_invalid_contents.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_preamble_signature_offset_too_big.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_preamble_signature_size_too_big.xxd.patch" +) + +BAD_FMAP_KEYBLOCK_PATCHES=( +"${DATADIR}/bios_peppy_dev.bad_keyblock_fmap_too_small_for_whole.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_keyblock_fmap_too_small.xxd.patch" +) + +BAD_FMAP_PREAMBLE_PATCHES=( +"${DATADIR}/bios_peppy_dev.bad_preamble_fmap_too_small_for_whole.xxd.patch" +"${DATADIR}/bios_peppy_dev.bad_preamble_fmap_too_small.xxd.patch" +) + "${FUTILITY}" load_fmap "${ONEMORE}" VBLOCK_A:/dev/urandom VBLOCK_B:/dev/zero INFILES="${INFILES} ${ONEMORE}" +# args: xxd_patch_file input_file +function apply_xxd_patch { + xxd -r "${1}" "${2}" +} + +# args: file1 file2 +function cmp_first_line { + cmp <(head -n1 "${1}") <(head -n1 "${2}") +} + +function cmp_last_line { + cmp <(tail -n1 "${1}") <(tail -n1 "${2}") +} + set -o pipefail count=0 @@ -44,33 +110,6 @@ for infile in $INFILES; do mkdir -p "${loemdir}" - # resign_firmwarefd.sh works on BIOS image files. The args are: - # - # infile - # outfile - # firmware_datakey - # firmware_keyblock - # dev_firmware_datakey (these are only used if RW A & RW B differ) - # dev_firmware_keyblock - # kernel_subkey - # firmware_version - # preamble_flag - # loem_output_dir (optional: dir for copy of new vblocks) - # loemid (optional: copy new vblocks using this name) - # - #OLD ${BIN_DIR}/resign_firmwarefd.sh \ - #OLD ${infile} \ - #OLD ${outfile} \ - #OLD ${KEYDIR}/firmware_data_key.vbprivk \ - #OLD ${KEYDIR}/firmware.keyblock \ - #OLD ${KEYDIR}/dev_firmware_data_key.vbprivk \ - #OLD ${KEYDIR}/dev_firmware.keyblock \ - #OLD ${KEYDIR}/kernel_subkey.vbpubk \ - #OLD 14 \ - #OLD 8 \ - #OLD ${loemdir} \ - #OLD ${loemid} - "${FUTILITY}" sign \ -s "${KEYDIR}/firmware_data_key.vbprivk" \ -b "${KEYDIR}/firmware.keyblock" \ @@ -94,10 +133,7 @@ for infile in $INFILES; do # and the LOEM stuff "${FUTILITY}" dump_fmap -x "${outfile}" \ - "FW_MAIN_A:${loemdir}/fw_main_A" "FW_MAIN_B:${loemdir}/fw_main_B" \ - "Firmware A Data:${loemdir}/fw_main_A" \ - "Firmware B Data:${loemdir}/fw_main_B" - + "FW_MAIN_A:${loemdir}/fw_main_A" "FW_MAIN_B:${loemdir}/fw_main_B" "${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \ --fv "${loemdir}/fw_main_A" \ @@ -118,6 +154,7 @@ done # Make sure that the BIOS with the good vblocks signed the right size. GOOD_OUT="${TMP}.${GOOD_VBLOCKS##*/}.new" MORE_OUT="${TMP}.${ONEMORE##*/}.new" +GOOD_CBFS_OUT="${TMP}.${GOOD_CBFS##*/}.new" "${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" "${GOOD_OUT}" \ | awk '/Firmware body size:/ {print $4}' > "${TMP}.good.body" @@ -135,9 +172,52 @@ if cmp "${TMP}.good.body" "${TMP}.good.fw_main"; then false; fi cmp "${TMP}.onemore.body" "${TMP}.onemore.fw_main" cmp "${TMP}.onemore.body" "${TMP}.good.fw_main" +"${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \ + "${GOOD_CBFS_OUT}" \ + | awk '/Firmware body size:/ {print $4}' > "${TMP}.good_cbfs.body" +"${FUTILITY}" dump_fmap -p "${GOOD_CBFS_OUT}" \ + | awk '/FW_MAIN_/ {print $3}' > "${TMP}.good_cbfs.fw_main" +if cmp "${TMP}.good_cbfs.body" "${TMP}.good_cbfs.fw_main"; then false; fi -# Sign the last one again but don't specify the version or the preamble flags. -# The version should default to 1, but the preamble flags should be preserved. + +# Sign CBFS image after adding new files. Size should increase but still be +# smaller than FlashMap size. +: $(( count++ )) +echo -n "${count} " 1>&3 + +cp "${GOOD_CBFS_OUT}" "${GOOD_CBFS_OUT}.1" +truncate -s 512 "${TMP}.zero_512" +cbfstool "${GOOD_CBFS_OUT}.1" expand -r FW_MAIN_A,FW_MAIN_B +cbfstool "${GOOD_CBFS_OUT}.1" add \ + -r FW_MAIN_A,FW_MAIN_B -f "${TMP}.zero_512" -n new-data-file -t raw + +"${FUTILITY}" sign \ + -s "${KEYDIR}/firmware_data_key.vbprivk" \ + -b "${KEYDIR}/firmware.keyblock" \ + -k "${KEYDIR}/kernel_subkey.vbpubk" \ + "${GOOD_CBFS_OUT}.1" + +"${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \ + "${GOOD_CBFS_OUT}.1" \ + | awk '/Firmware body size:/ {print $4}' > "${TMP}.good_cbfs.1.body" +"${FUTILITY}" dump_fmap -p "${GOOD_CBFS_OUT}" \ + | awk '/FW_MAIN_/ {print $3}' > "${TMP}.good_cbfs.1.fw_main" + +# Check if size increased, but also if it was correctly truncated, +# so it does not span over whole FlashMap area. +[[ $(head -n1 "${TMP}.good_cbfs.body") \ + < $(head -n1 "${TMP}.good_cbfs.1.body") ]] +[[ $(tail -n1 "${TMP}.good_cbfs.body") \ + < $(tail -n1 "${TMP}.good_cbfs.1.body") ]] +[[ $(head -n1 "${TMP}.good_cbfs.1.body") \ + < $(head -n1 "${TMP}.good_cbfs.1.fw_main") ]] +[[ $(tail -n1 "${TMP}.good_cbfs.1.body") \ + < $(tail -n1 "${TMP}.good_cbfs.1.fw_main") ]] + + +# Sign image again but don't specify the version or the preamble flags. +# The firmware version and preamble flags should be preserved. +# NOTICE: Version preservation behavior changed from defaulting to 1. : $(( count++ )) echo -n "${count} " 1>&3 @@ -148,7 +228,7 @@ echo -n "${count} " 1>&3 "${MORE_OUT}" "${MORE_OUT}.2" m=$("${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \ - "${MORE_OUT}.2" | grep -c -E 'Firmware version: +1$|Preamble flags: +8$') + "${MORE_OUT}.2" | grep -c -E 'Firmware version: +14$|Preamble flags: +8$') [ "${m}" = "4" ] @@ -168,6 +248,262 @@ m=$("${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \ [ "${m}" = "4" ] +# Check signing when B slot is empty +: $(( count++ )) +echo -n "${count} " 1>&3 + +"${FUTILITY}" load_fmap "${CLEAN_B}" VBLOCK_B:/dev/zero FW_MAIN_B:/dev/zero +"${FUTILITY}" sign \ + -s "${KEYDIR}/firmware_data_key.vbprivk" \ + -b "${KEYDIR}/firmware.keyblock" \ + -k "${KEYDIR}/kernel_subkey.vbpubk" \ + "${CLEAN_B}" "${CLEAN_B}.1" + +"${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" "${CLEAN_B}.1" \ + | awk '/Firmware body size:/ {print $4}' > "${TMP}.clean_b.body" +"${FUTILITY}" dump_fmap -p "${CLEAN_B}.1" \ + | awk '/FW_MAIN_/ {print $3}' > "${TMP}.clean_b.fw_main" + +# These should not be equal, as FW_MAIN_A size should be kept intact, when size +# of FW_MAIN_B should be taken from FlashMap. +if cmp "${TMP}.clean_b.body" "${TMP}.clean_b.fw_main" ; then false; fi +if cmp "${TMP}.clean_b.body" "${TMP}.good.body" ; then false; fi +cmp_first_line "${TMP}.clean_b.body" "${TMP}.good.body" +cmp_last_line "${TMP}.clean_b.body" "${TMP}.clean_b.fw_main" + +# Version for slot A should be kept intact, while for B slot it should default +# to 1. All flags should be zero. +m=$("${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \ + "${CLEAN_B}.1" \ + | grep -c -E \ + 'Firmware version: +1$|Preamble flags: +0$|Firmware version: +2$') +[ "${m}" = "4" ] + +# Check signing when there is no B slot +: $(( count++ )) +echo -n "${count} " 1>&3 + +NO_B_SLOT="${TMP}.${GOOD_CBFS##*/}.no_b_slot" +NO_B_SLOT_SIGNED_IMG="${NO_B_SLOT}.signed" + +cp "${GOOD_CBFS}" "${NO_B_SLOT}" +apply_xxd_patch "${NO_B_SLOT_PATCH}" "${NO_B_SLOT}" + +"${FUTILITY}" sign \ + -s "${KEYDIR}/firmware_data_key.vbprivk" \ + -b "${KEYDIR}/firmware.keyblock" \ + -k "${KEYDIR}/kernel_subkey.vbpubk" \ + -v 1 \ + "${NO_B_SLOT}" "${NO_B_SLOT_SIGNED_IMG}" + +"${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \ + "${NO_B_SLOT_SIGNED_IMG}" \ + | awk '/Firmware body size:/ {print $4}' > "${TMP}.no_b_slot.body" +"${FUTILITY}" dump_fmap -p "${NO_B_SLOT_SIGNED_IMG}" \ + | awk '/FW_MAIN_/ {print $3}' > "${TMP}.no_b_slot.fw_main" + +if cmp "${TMP}.no_b_slot.body" "${TMP}.no_b_slot.fw_main" ; then false; fi +cmp "${TMP}.no_b_slot.body" <(tail -n1 "${TMP}.good_cbfs.body") + +m=$("${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" \ + "${NO_B_SLOT_SIGNED_IMG}" \ + | grep -c -E 'Firmware version: +1$|Preamble flags: +0$') +[ "${m}" = "2" ] + +# Check signing when cbfstool reports incorrect size +# Signing should fail, as it should not be possible for CBFS contents to be +# bigger than FlashMap size of the area +: $(( count++ )) +echo -n "${count} " 1>&3 + +CBFSTOOL_STUB="$(realpath "${TMP}.cbfs_stub.sh")" +echo -en 'echo "0xFFEEDD0"; exit 0;' > "${CBFSTOOL_STUB}" +chmod +x "${CBFSTOOL_STUB}" + +if CBFSTOOL="${CBFSTOOL_STUB}" "${FUTILITY}" sign \ + -s "${KEYDIR}/firmware_data_key.vbprivk" \ + -b "${KEYDIR}/firmware.keyblock" \ + -k "${KEYDIR}/kernel_subkey.vbpubk" \ + -v 1 \ + "${GOOD_CBFS}" "${TMP}.1.${GOOD_CBFS##*/}" +then + false +fi + +# Redefine cbfstool stub to return valid value for FW_MAIN_A and invalid for +# FW_MAIN_B size. With this behavior futility should fail to sign this image, +# as cbfstool should never return incorrect size (larger than area). +cp "${GOOD_CBFS}" "${TMP}.good_cbfs.bin" +FW_MAIN_A_SIZE="$(printf '0x%x' \ + "$(cbfstool "${TMP}.good_cbfs.bin" truncate -r FW_MAIN_A)")" +MARK_FILE="$(realpath "${TMP}.mark1")" +rm -f "${MARK_FILE}" + +cat << EOF > "${CBFSTOOL_STUB}" +#!/usr/bin/env bash +if ! [ -f "${MARK_FILE}" ]; then + echo "${FW_MAIN_A_SIZE}"; + echo 1 > "${MARK_FILE}"; +else + echo 0xFFFFAA0; +fi +exit 0; +EOF + +if CBFSTOOL="${CBFSTOOL_STUB}" "${FUTILITY}" sign \ + -s "${KEYDIR}/firmware_data_key.vbprivk" \ + -b "${KEYDIR}/firmware.keyblock" \ + -k "${KEYDIR}/kernel_subkey.vbpubk" \ + -v 1 \ + "${GOOD_CBFS}" "${TMP}.2.${GOOD_CBFS##*/}" +then + false +fi + + +# Check various incorrect values in VBLOCK (keyblock and preamble) +: $(( count++ )) +echo -n "${count} " 1>&3 + +bad_counter=1 +for keyblock_patch in "${BAD_KEYBLOCK_PATCHES[@]}"; do + echo -n "${count}.${bad_counter} " 1>&3 + BAD_IN="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.in.bin" + BAD_OUT="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.out.bin" + cp "${GOOD_DEV}" "${BAD_IN}" + apply_xxd_patch "${keyblock_patch}" "${BAD_IN}" + + FUTIL_OUTPUT="$(if "${FUTILITY}" verify \ + --publickey "${KEYDIR}/root_key.vbpubk" "${BAD_IN}"; \ + then false; fi)" + grep -q 'VBLOCK_A keyblock component is invalid' <<< "${FUTIL_OUTPUT}" + + FUTIL_OUTPUT="$("${FUTILITY}" sign \ + -s "${KEYDIR}/firmware_data_key.vbprivk" \ + -b "${KEYDIR}/firmware.keyblock" \ + -k "${KEYDIR}/kernel_subkey.vbpubk" \ + "${BAD_IN}" "${BAD_OUT}" 2>&1)" + grep -q 'VBLOCK_A keyblock is invalid' <<< "${FUTIL_OUTPUT}" + + "${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" "${BAD_OUT}" \ + | awk '/Firmware body size:/ {print $4}' > "${BAD_OUT}.body" + "${FUTILITY}" dump_fmap -p "${BAD_OUT}" \ + | awk '/FW_MAIN_/ {print $3}' > "${BAD_OUT}.fw_main" + + cmp "${BAD_OUT}.fw_main" "${TMP}.good.fw_main" + cmp_first_line "${BAD_OUT}.body" "${TMP}.good.fw_main" + cmp_last_line "${BAD_OUT}.body" "${TMP}.good.body" + + : $(( bad_counter++ )) +done + +for vblock_patch in "${BAD_PREAMBLE_PATCHES[@]}"; do + echo -n "${count}.${bad_counter} " 1>&3 + BAD_IN="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.in.bin" + BAD_OUT="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.out.bin" + cp "${GOOD_DEV}" "${BAD_IN}" + apply_xxd_patch "${vblock_patch}" "${BAD_IN}" + + FUTIL_OUTPUT="$(if "${FUTILITY}" verify \ + --publickey "${KEYDIR}/root_key.vbpubk" "${BAD_IN}"; \ + then false; fi)" + grep -q 'VBLOCK_A is invalid' <<< "${FUTIL_OUTPUT}" + + FUTIL_OUTPUT="$("${FUTILITY}" sign \ + -s "${KEYDIR}/firmware_data_key.vbprivk" \ + -b "${KEYDIR}/firmware.keyblock" \ + -k "${KEYDIR}/kernel_subkey.vbpubk" \ + "${BAD_IN}" "${BAD_OUT}" 2>&1)" + grep -q 'VBLOCK_A preamble is invalid' <<< "${FUTIL_OUTPUT}" + + "${FUTILITY}" verify --publickey "${KEYDIR}/root_key.vbpubk" "${BAD_OUT}" \ + | awk '/Firmware body size:/ {print $4}' > "${BAD_OUT}.body" + "${FUTILITY}" dump_fmap -p "${BAD_OUT}" \ + | awk '/FW_MAIN_/ {print $3}' > "${BAD_OUT}.fw_main" + + cmp "${BAD_OUT}.fw_main" "${TMP}.good.fw_main" + cmp_first_line "${BAD_OUT}.body" "${TMP}.good.fw_main" + cmp_last_line "${BAD_OUT}.body" "${TMP}.good.body" + + : $(( bad_counter++ )) +done + +for vblock_patch in "${BAD_FMAP_KEYBLOCK_PATCHES[@]}"; do + echo -n "${count}.${bad_counter} " 1>&3 + BAD_IN="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.in.bin" + BAD_OUT="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.out.bin" + cp "${GOOD_DEV}" "${BAD_IN}" + apply_xxd_patch "${vblock_patch}" "${BAD_IN}" + + FUTIL_OUTPUT="$(if "${FUTILITY}" verify \ + --publickey "${KEYDIR}/root_key.vbpubk" "${BAD_IN}"; \ + then false; fi)" + grep -q 'VBLOCK_A keyblock component is invalid' <<< "${FUTIL_OUTPUT}" + + FUTIL_OUTPUT="$(if "${FUTILITY}" sign \ + -s "${KEYDIR}/firmware_data_key.vbprivk" \ + -b "${KEYDIR}/firmware.keyblock" \ + -k "${KEYDIR}/kernel_subkey.vbpubk" \ + "${BAD_IN}" "${BAD_OUT}" 2>&1; \ + then false; fi)" + m="$(grep -c -E \ + 'VBLOCK_A keyblock is invalid|Keyblock and preamble do not fit in VBLOCK' \ + <<< "${FUTIL_OUTPUT}")" + [ "${m}" = "2" ] + + : $(( bad_counter++ )) +done + +echo -n "${count}.${bad_counter} " 1>&3 +BAD_IN="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.in.bin" +BAD_OUT="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.out.bin" +cp "${GOOD_DEV}" "${BAD_IN}" +apply_xxd_patch "${BAD_FMAP_PREAMBLE_PATCHES[0]}" "${BAD_IN}" + +FUTIL_OUTPUT="$(if "${FUTILITY}" verify \ + --publickey "${KEYDIR}/root_key.vbpubk" "${BAD_IN}"; \ + then false; fi)" +grep -q 'VBLOCK_A is invalid' <<< "${FUTIL_OUTPUT}" + +FUTIL_OUTPUT="$(if "${FUTILITY}" sign \ + -s "${KEYDIR}/firmware_data_key.vbprivk" \ + -b "${KEYDIR}/firmware.keyblock" \ + -k "${KEYDIR}/kernel_subkey.vbpubk" \ + "${BAD_IN}" "${BAD_OUT}" 2>&1; \ + then false; fi)" +m="$(grep -c -E \ + 'VBLOCK_A preamble is invalid|Keyblock and preamble do not fit in VBLOCK' \ + <<< "${FUTIL_OUTPUT}")" +[ "${m}" = "2" ] + +: $(( bad_counter++ )) + +echo -n "${count}.${bad_counter} " 1>&3 +BAD_IN="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.in.bin" +BAD_OUT="${TMP}.${GOOD_DEV##*/}.bad.${bad_counter}.out.bin" +cp "${GOOD_DEV}" "${BAD_IN}" +apply_xxd_patch "${BAD_FMAP_PREAMBLE_PATCHES[1]}" "${BAD_IN}" + +FUTIL_OUTPUT="$(if "${FUTILITY}" verify \ + --publickey "${KEYDIR}/root_key.vbpubk" "${BAD_IN}"; \ + then false; fi)" +grep -q 'VBLOCK_A is invalid' <<< "${FUTIL_OUTPUT}" + +FUTIL_OUTPUT="$(if "${FUTILITY}" sign \ + -s "${KEYDIR}/firmware_data_key.vbprivk" \ + -b "${KEYDIR}/firmware.keyblock" \ + -k "${KEYDIR}/kernel_subkey.vbpubk" \ + "${BAD_IN}" "${BAD_OUT}" 2>&1; \ + then false; fi)" +m="$(grep -c -E \ + -e 'VBLOCK_A is invalid\. Keyblock and preamble do not fit' \ + -e 'Keyblock and preamble do not fit in VBLOCK' \ + <<< "${FUTIL_OUTPUT}")" +[ "${m}" = "2" ] + +: $(( bad_counter++ )) + + # cleanup rm -rf "${TMP}"* "${ONEMORE}" exit 0 |