summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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