summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Czapiga <jacz@semihalf.com>2022-04-07 14:44:44 +0200
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2022-06-30 14:55:17 +0000
commit9ae9d2c03f42b25238f803a26ebae4902541317e (patch)
tree4fb994c865757570ca5a40a5b376e7779dc9a584
parent57eb6ea8e8ebf6ecb859815ef15b962a38803bd2 (diff)
downloadvboot-9ae9d2c03f42b25238f803a26ebae4902541317e.tar.gz
futility/file_type_bios: Rework image signing
This patch reworks whole BIOS image signing to support images with CBFS, and with ponly RW/A slot. CBFS images will now be truncated to eliminate unnecessary empty space, and will sign only the part of firmware area which contains the data, and not empty space. This patch also adds more checks for potential errors, and does not allow for signing incorrect nor uses data from structures, which might not be valid. futility sign command tests are also greatly extended to cover a wide variety of possible errors, which have to be handled correctly. BUG=b:197114807 TEST=sudo emerge vboot_reference TEST=build whole chromeos-bootimage after making it and coreboot use `futility sign --type bios ...` TEST=make runtests BRANCH=none Signed-off-by: Jakub Czapiga <jacz@semihalf.com> Cq-Depend: chromium:3707104 Change-Id: I7c84aa38776e8890a87f0e9b7ec7f32d86f82c13 Disallow-Recycled-Builds: test-failures Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/vboot_reference/+/3575325 Tested-by: Jakub Czapiga <czapiga@google.com> Reviewed-by: Julius Werner <jwerner@chromium.org> Auto-Submit: Jakub Czapiga <czapiga@google.com> Commit-Queue: Jakub Czapiga <czapiga@google.com>
-rw-r--r--Makefile6
-rw-r--r--firmware/2lib/include/2return_codes.h3
-rw-r--r--futility/file_type_bios.c354
-rw-r--r--futility/file_type_bios.h4
-rw-r--r--host/lib/cbfstool.c69
-rw-r--r--host/lib/include/cbfstool.h12
-rw-r--r--tests/futility/data/README3
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_keyblock_data_key_offset_too_big.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_keyblock_data_key_size_big.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_keyblock_fmap_too_small.xxd.patch2
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_keyblock_fmap_too_small_for_whole.xxd.patch2
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_keyblock_hash_data_size_too_small.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_keyblock_hash_invalid_contents.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_keyblock_hash_offset_too_big.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_keyblock_hash_size_too_big.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_keyblock_invalid_magic.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_keyblock_invalid_major_version.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_keyblock_size_not_fully_signed.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_preamble_body_signature_offset_too_big.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_preamble_body_signature_size_too_big.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_preamble_fmap_too_small.xxd.patch2
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_preamble_fmap_too_small_for_whole.xxd.patch2
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_preamble_header_version_major.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_preamble_header_version_minor.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_preamble_kernel_subkey_offset_too_big.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_preamble_kernel_subkey_size_too_big.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_preamble_signature_data_size_too_big.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_preamble_signature_data_size_too_small.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_preamble_signature_invalid_contents.xxd.patch2
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_preamble_signature_offset_too_big.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.bad_preamble_signature_size_too_big.xxd.patch1
-rw-r--r--tests/futility/data/bios_peppy_dev.binbin0 -> 8388608 bytes
-rw-r--r--tests/futility/data/bios_voxel_dev.binbin0 -> 33554432 bytes
-rw-r--r--tests/futility/data/bios_voxel_dev.no_b_slot.xxd.patch20
-rw-r--r--tests/futility/data_bios_voxel_dev.bin_expect.txt6
-rwxr-xr-xtests/futility/test_sign_firmware.sh410
36 files changed, 725 insertions, 191 deletions
diff --git a/Makefile b/Makefile
index abbb343b..2c6f70c2 100644
--- a/Makefile
+++ b/Makefile
@@ -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
new file mode 100644
index 00000000..f241cd03
--- /dev/null
+++ b/tests/futility/data/bios_peppy_dev.bin
Binary files differ
diff --git a/tests/futility/data/bios_voxel_dev.bin b/tests/futility/data/bios_voxel_dev.bin
new file mode 100644
index 00000000..7cd85355
--- /dev/null
+++ b/tests/futility/data/bios_voxel_dev.bin
Binary files differ
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