summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--futility/cmd_gscvd.c85
-rw-r--r--tests/futility/data/nivviks.FMAPbin0 -> 2048 bytes
-rw-r--r--tests/futility/data/nivviks.GBBbin0 -> 12288 bytes
-rw-r--r--tests/futility/data/nivviks.RO_GSCVDbin0 -> 8192 bytes
-rwxr-xr-xtests/futility/run_test_scripts.sh1
-rwxr-xr-xtests/futility/test_gscvd.sh107
6 files changed, 186 insertions, 7 deletions
diff --git a/futility/cmd_gscvd.c b/futility/cmd_gscvd.c
index f7dc73b6..5d860452 100644
--- a/futility/cmd_gscvd.c
+++ b/futility/cmd_gscvd.c
@@ -447,6 +447,68 @@ static int add_gbb(struct gscvd_ro_ranges *ranges, const struct file_buf *file)
}
/**
+ * Extend AP RO hash digest with data from an address range.
+ *
+ * If the flags_offset value is non zero and happens to fall into the passed
+ * in range, do not read values from flash in the flags_offset..+flags_size
+ * range, instead feed zeros to the hashing function.
+ *
+ * NOTE that flags are expected to fully fit into the range, cases of overlap
+ * are not supported.
+ *
+ * @param ap_firmware_file pointer to the AP firmware file layout descriptor
+ * @param dc pointer to the hash calculating context
+ * @param offset offset of the beginning of the range in AP SPI flash
+ * @param size size of the range
+ * @param flags_offset if nonzero - offset of the GBB flags field in
+ * AP SPI flash
+ *
+ * @return VB2_SUCCESS or digest extension error, if any.
+ */
+static vb2_error_t extend_digest(const struct file_buf *ap_firmware_file,
+ struct vb2_digest_context *dc,
+ uint32_t offset,
+ uint32_t size,
+ uint32_t flags_offset)
+{
+ /* Define it as array to simplify calling vb2_digest_extend() below. */
+ const uint8_t flags[sizeof(vb2_gbb_flags_t)] = {0};
+
+ if (flags_offset &&
+ (flags_offset >= offset) &&
+ (flags_offset < (offset + size))) {
+ uint32_t flags_size;
+ vb2_error_t rv;
+
+ /*
+ * This range includes GBB flags, which need to be zeroized.
+ *
+ * First get the hash of up to the flags.
+ */
+ rv = vb2_digest_extend(dc, ap_firmware_file->data + offset,
+ flags_offset - offset);
+ if (rv != VB2_SUCCESS)
+ return rv;
+
+ size -= flags_offset - offset;
+ offset = flags_offset;
+
+ /* Now hash the flag space, maybe partially. */
+ flags_size = VB2_MIN(size, sizeof(flags));
+ rv = vb2_digest_extend(dc, flags, flags_size);
+ if (rv != VB2_SUCCESS)
+ return rv;
+
+ /* Update size and offset to cover the rest of the range. */
+ size -= flags_size;
+
+ offset += flags_size;
+ }
+
+ return vb2_digest_extend(dc,ap_firmware_file->data + offset, size);
+}
+
+/**
* Calculate hash of the RO ranges.
*
* @param ap_firmware_file pointer to the AP firmware file layout descriptor
@@ -456,16 +518,23 @@ static int add_gbb(struct gscvd_ro_ranges *ranges, const struct file_buf *file)
* @param digest memory to copy the calculated hash to
* @param digest_ size requested size of the digest, padded with zeros if the
* SHA digest size is smaller than digest_size
+ * @param override_gbb_flags if true, replace GBB flags value with zero
*
* @return zero on success, -1 on failure.
*/
static int calculate_ranges_digest(const struct file_buf *ap_firmware_file,
const struct gscvd_ro_ranges *ranges,
enum vb2_hash_algorithm hash_alg,
- void *digest, size_t digest_size)
+ void *digest, size_t digest_size,
+ bool override_gbb_flags)
{
struct vb2_digest_context dc;
size_t i;
+ uint32_t flags_offset = 0;
+
+ if (override_gbb_flags && ap_firmware_file->gbb_area)
+ flags_offset = offsetof(struct vb2_gbb_header, flags) +
+ ap_firmware_file->gbb_area->area_offset;
/* Calculate the ranges digest. */
if (vb2_digest_init(&dc, false, hash_alg, 0) != VB2_SUCCESS) {
@@ -474,10 +543,10 @@ static int calculate_ranges_digest(const struct file_buf *ap_firmware_file,
}
for (i = 0; i < ranges->range_count; i++) {
- if (vb2_digest_extend(&dc,
- ap_firmware_file->data +
- ranges->ranges[i].offset,
- ranges->ranges[i].size) != VB2_SUCCESS) {
+ if (extend_digest(ap_firmware_file, &dc,
+ ranges->ranges[i].offset,
+ ranges->ranges[i].size,
+ flags_offset) != VB2_SUCCESS) {
ERROR("Failed to extend digest!\n");
return -1;
}
@@ -548,7 +617,8 @@ struct gsc_verification_data *create_gvd(struct file_buf *ap_firmware_file,
if (calculate_ranges_digest(ap_firmware_file, ranges, gvd->hash_alg,
gvd->ranges_digest,
- sizeof(gvd->ranges_digest))) {
+ sizeof(gvd->ranges_digest),
+ true)) {
free(gvd);
return NULL;
}
@@ -971,7 +1041,8 @@ static int validate_gscvd(int argc, char *argv[])
if (calculate_ranges_digest(&ap_firmware_file, &ranges,
gvd->hash_alg, digest,
- sizeof(digest)))
+ sizeof(digest),
+ false))
break;
if (memcmp(digest, gvd->ranges_digest, sizeof(digest))) {
diff --git a/tests/futility/data/nivviks.FMAP b/tests/futility/data/nivviks.FMAP
new file mode 100644
index 00000000..06f408eb
--- /dev/null
+++ b/tests/futility/data/nivviks.FMAP
Binary files differ
diff --git a/tests/futility/data/nivviks.GBB b/tests/futility/data/nivviks.GBB
new file mode 100644
index 00000000..39f5c57a
--- /dev/null
+++ b/tests/futility/data/nivviks.GBB
Binary files differ
diff --git a/tests/futility/data/nivviks.RO_GSCVD b/tests/futility/data/nivviks.RO_GSCVD
new file mode 100644
index 00000000..165c689b
--- /dev/null
+++ b/tests/futility/data/nivviks.RO_GSCVD
Binary files differ
diff --git a/tests/futility/run_test_scripts.sh b/tests/futility/run_test_scripts.sh
index e0c8292d..dc87f76b 100755
--- a/tests/futility/run_test_scripts.sh
+++ b/tests/futility/run_test_scripts.sh
@@ -36,6 +36,7 @@ ${SCRIPT_DIR}/futility/test_sign_keyblocks.sh
${SCRIPT_DIR}/futility/test_sign_usbpd1.sh
${SCRIPT_DIR}/futility/test_update.sh
${SCRIPT_DIR}/futility/test_file_types.sh
+${SCRIPT_DIR}/futility/test_gscvd.sh
"
# Get ready...
diff --git a/tests/futility/test_gscvd.sh b/tests/futility/test_gscvd.sh
new file mode 100755
index 00000000..54d48479
--- /dev/null
+++ b/tests/futility/test_gscvd.sh
@@ -0,0 +1,107 @@
+#!/bin/bash -eux
+# 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.
+
+if [[ -z ${SCRIPT_DIR+x} ]]; then
+ # must be running standalone
+ SCRIPT_DIR="$(readlink -f "$(dirname "$0")"/..)"
+ FUTILITY="${SCRIPT_DIR}/../build/futility/futility"
+fi
+
+if [[ ! -e ${FUTILITY} ]]; then
+ echo "The futility app not available, run 'make futil' in the top directory" \
+ >&2
+ exit 1
+fi
+
+KEYS_DIR="$(readlink -f "${SCRIPT_DIR}/devkeys")"
+TMPD="$(mktemp -d /tmp/"$(basename "$0")".XXXXX)"
+trap '/bin/rm -rf "${TMPD}"' EXIT
+
+# Test FMAP sections were taken from Nivviks image, which is 32M in size.
+FW_IMAGE_SIZE_K=$(( 32 * 1024 ))
+
+# FMAP offset in the original image
+FMAP_OFFSET_K=28696
+
+main() {
+ local bios_blob
+ local command_args
+ local fmap_blob
+ local hwid
+ local pubkhash
+ local section
+
+ cd "${SCRIPT_DIR}/futility"
+
+ # Create a blob of the firmware image size.
+ bios_blob="${TMPD}/image.bin"
+ cat /dev/zero | tr '\000' '\377' | \
+ dd of="${bios_blob}" bs=1K count="${FW_IMAGE_SIZE_K}" status=none
+
+ # Paste the FMAP blob at the known location
+ fmap_blob="data/nivviks.FMAP"
+ dd if="${fmap_blob}" of="${bios_blob}" bs=1K seek="${FMAP_OFFSET_K}" \
+ conv=notrunc status=none
+
+ # Paste other available FMAP areas into the image.
+ command_args=()
+ for section in data/nivviks.[A-Z]*; do
+ local name
+
+ if [[ ${section} =~ .*FMAP ]]; then
+ continue
+ fi
+
+ name="${section##*.}"
+ command_args+=( "${name}:${section}" )
+ done
+ "${FUTILITY}" load_fmap "${bios_blob}" "${command_args[@]}"
+
+ # Make sure gbb flags are nonzero
+ "${FUTILITY}" gbb --set --flags=1 "${bios_blob}"
+
+ # Sign the blob using ranges already present in the RO_GSCVD section.
+ "${FUTILITY}" gscvd --keyblock "${KEYS_DIR}"/arv_platform.keyblock \
+ --platform_priv "${KEYS_DIR}"/arv_platform.vbprivk \
+ --board_id XYZ1 \
+ --root_pub_key "${KEYS_DIR}"/arv_root.vbpubk "${bios_blob}"
+
+ # Calculate root pub key hash
+ pubkhash="$( "${FUTILITY}" gscvd --root_pub_key \
+ "${KEYS_DIR}"/arv_root.vbpubk | tail -1)"
+
+ # Run verification, this one is expected to fail because GBB flags are not
+ # zero.
+ if "${FUTILITY}" gscvd "${bios_blob}" "${pubkhash}" 2>/dev/null ; then
+ echo "Unexpected signature match!" >&2
+ exit 1
+ fi
+
+ # Clear the flags and try verifying again, should succeed this time.
+ "${FUTILITY}" gbb --set --flags=0 "${bios_blob}"
+ if ! "${FUTILITY}" gscvd "${bios_blob}" "${pubkhash}" 2>/dev/null ; then
+ echo "Unexpected signature MISmatch!" >&2
+ exit 1
+ fi
+
+ # Change HWID and see that signature still matches.
+ hwid="$("${FUTILITY}" gbb --hwid "${bios_blob}" | sed 's/.*: //')"
+ "${FUTILITY}" gbb --set --hwid="${hwid}xx" "${bios_blob}"
+ if ! "${FUTILITY}" gscvd "${bios_blob}" "${pubkhash}" 2>/dev/null ; then
+ echo "Unexpected signature MISmatch after modifying HWID!" >&2
+ exit 1
+ fi
+
+ # Modify the recovery key and see that signature verification fails.
+ "${FUTILITY}" gbb --set \
+ --recoverykey="${KEYS_DIR}"/recovery_kernel_data_key.vbpubk \
+ "${bios_blob}"
+ if "${FUTILITY}" gscvd "${bios_blob}" "${pubkhash}" 2>/dev/null ; then
+ echo "Unexpected signature match after updating recovery key!" >&2
+ exit 1
+ fi
+}
+
+main "$@"