summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMary Ruthven <mruthven@chromium.org>2022-08-18 12:02:37 -0500
committerChromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com>2022-11-02 23:46:53 +0000
commitef3613e4a73f2df56cf653527ae03f7b8bceb7f4 (patch)
treebde1f1aadf6fbdc5d056db6cb423ac1e2f4d7050
parent97c4de9fdb47232a5ff14682ebd9e7c2d9c40f38 (diff)
downloadchrome-ec-ef3613e4a73f2df56cf653527ae03f7b8bceb7f4.tar.gz
apro: generate the hash with possible factory flags
The AP RO flags may have been non-zero when the factory generated the hash. The stored hash will not match finalized firmware since it was generated with non-zero gbb flags and the gbb flags are set to 0 during finalization. Cr50 can try to match the saved hash by using factory flags to calculate the AP RO hash. As long as the GBB flags are actually set to 0 it should be ok to try calculating the hash with a limited set of possible factory flags. Try to match the saved hash using GBB flags 0 to calculate the hash. If that doesn't match, cycle through the rest of the possible factory flags to see if any of them generate the saved hash. If none of the factory flags work, fail verification. This change adds 8 possible factory flag values: 0, 0x39, 0x239, 0x1039, 0x50b9, 0x40b9, 0x52b9, and 0x42b9 BUG=b:236844541,b:230071229 TEST=manual # add 0x42b9 possible_factory_flags # Set GBB flags to 0x42b9 /usr/share/vboot/bin/set_gbb_flags.sh 0x42b9 # save the hash with GBB 0x42b9 ap_ro_hash.py FMAP GBB # Verify AP RO verification fails because flags are 0x42b9 [349.029624 enable_spi_pinmux: AP] [349.030178 tpm_rst_asserted] [349.032382 spi_hash_pp_done: AP] [349.137962 validate_gbb: invalid flags 42b9] # reboot cr50 to release ec reset > reboot # Set GBB flags to 0 /usr/share/vboot/bin/set_gbb_flags.sh 0 # Verify ap ro verification passes. Change-Id: I17d191abada342263ea246911ce47ac24dbb940c Signed-off-by: Mary Ruthven <mruthven@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/3840653 Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
-rw-r--r--common/ap_ro_integrity_check.c138
1 files changed, 131 insertions, 7 deletions
diff --git a/common/ap_ro_integrity_check.c b/common/ap_ro_integrity_check.c
index 2e6b93fa2a..d038c320c5 100644
--- a/common/ap_ro_integrity_check.c
+++ b/common/ap_ro_integrity_check.c
@@ -201,6 +201,9 @@ struct gbb_descriptor {
* they're in the hash.
*/
struct ro_range gbb_flags;
+
+ /* Flags used to generate the hash */
+ uint32_t injected_flags;
} __packed;
/*****************************************************************************/
@@ -443,35 +446,99 @@ static bool is_in_range(const struct ro_range part_range,
}
/**
+ * Update ctx with the contents of full_range and the injected flags.
+ *
+ * Read the data before the flags and add it to ctx. Add the injected flags
+ * Invoke service function to sequentially calculate sha256 hash of the AP
+ * flash memory ranges, and compare the final hash with the expected value.
+ *
+ * Use the gbb_flags from gbbd. The AP hash could have been saved with the
+ * wrong GBB flags value in the hash. validate_gbb has already been called to
+ * validate the actual GBB contents. Use the gbbd->injected_flags when
+ * calculating the hash to see if it's possible to match the hash.
+ *
+ * @param ctx pointer to the sha256 context to update
+ * @param full_range range to include in hash calculation
+ * @param gbbd the descriptor with the gbb flag information.
+ */
+static void update_sha_with_gbb_range(struct sha256_ctx *ctx,
+ const struct ro_range full_range,
+ const struct gbb_descriptor *gbbd)
+{
+ struct ro_range range;
+
+ /* Use the factory flags to calculate the hash. */
+ CPRINTS("Using %x for GBB flags.", gbbd->injected_flags);
+ /* Add the data before the gbb flags */
+ range.flash_offset = full_range.flash_offset;
+ range.range_size = gbbd->gbb_flags.flash_offset -
+ full_range.flash_offset;
+ if (range.range_size > 0)
+ usb_spi_sha256_update(ctx, range.flash_offset,
+ range.range_size, 1);
+
+ /* Update hash with the injected gbb flags */
+ SHA256_update(ctx, &gbbd->injected_flags,
+ sizeof(gbbd->injected_flags));
+
+ /* Add the data after the gbb flags */
+ range.flash_offset = gbbd->gbb_flags.flash_offset +
+ gbbd->gbb_flags.range_size;
+ range.range_size = full_range.flash_offset +
+ full_range.range_size - range.flash_offset;
+ if (range.range_size > 0)
+ usb_spi_sha256_update(ctx, range.flash_offset,
+ range.range_size, 1);
+}
+
+/**
* Validate hash of AP flash ranges.
*
* Invoke service function to sequentially calculate sha256 hash of the AP
* flash memory ranges, and compare the final hash with the expected value.
*
+ * Use the gbb_flags from gbbd to generate the hash. The AP hash could have
+ * been generated with the wrong GBB flags. Use the injected flags value instead
+ * of the actual gbb flags to generate the hash to see if it's possible to match
+ * the hash.
+ *
* @param ranges array of ranges to include in hash calculation
* @param count number of ranges in the array
* @param expected_digest pointer to the expected sha256 digest value.
+ * @param gbbd pointer gbb_descriptor to adjust the hash for different gbb
+ * flags.
*
* @return ROV_SUCCEEDED if succeeded, ROV_FAILED otherwise.
*/
static
enum ap_ro_check_result validate_ranges_sha(const struct ro_range *ranges,
size_t count,
- const uint8_t *expected_digest)
+ const uint8_t *expected_digest,
+ struct gbb_descriptor *gbbd)
{
int8_t digest[SHA256_DIGEST_SIZE];
size_t i;
struct sha256_ctx ctx;
usb_spi_sha256_start(&ctx);
- for (i = 0; i < count; i++)
+ for (i = 0; i < count; i++) {
+ /*
+ * If the GBB is validated and the flags are in range, use
+ * the injected gbb flag value and the actual data from before
+ * and after the gbb flags to calculate the hash.
+ */
+ if (gbbd->validate_flags &&
+ is_in_range(gbbd->gbb_flags, ranges[i])) {
+ update_sha_with_gbb_range(&ctx, ranges[i], gbbd);
+ continue;
+ }
usb_spi_sha256_update(&ctx, ranges[i].flash_offset,
ranges[i].range_size, true);
+ }
usb_spi_sha256_final(&ctx, digest, sizeof(digest));
if (DCRYPTO_equals(digest, expected_digest, sizeof(digest)) !=
DCRYPTO_OK) {
- CPRINTS("AP RO verification FAILED!");
CPRINTS("Calculated digest %ph",
HEX_BUF(digest, sizeof(digest)));
CPRINTS("Stored digest %ph",
@@ -482,6 +549,53 @@ enum ap_ro_check_result validate_ranges_sha(const struct ro_range *ranges,
return ROV_SUCCEEDED;
}
+#define FACTORY_FLAG_COUNT 8
+const uint32_t possible_factory_flags[] = {
+ 0,
+ /* Factory flags from b/230071229 */
+ 0x39,
+ 0x239,
+ 0x1039,
+ 0x50b9,
+ 0x40b9,
+ 0x52b9,
+ 0x42b9
+};
+BUILD_ASSERT(ARRAY_SIZE(possible_factory_flags) == FACTORY_FLAG_COUNT);
+
+/**
+ * Validate hash of AP flash ranges with different GBB flags.
+ *
+ * Try to use different GBB values to see if any hashes match the saved hash.
+ * The GBB flags were already validated
+ *
+ * @param ranges array of ranges to include in hash calculation
+ * @param count number of ranges in the array
+ * @param expected_digest pointer to the expected sha256 digest value.
+ * @param gbbd pointer gbb_descriptor to adjust the hash for different gbb
+ * flags.
+ *
+ * @return ROV_SUCCEEDED if succeeded, ROV_FAILED otherwise.
+ */
+static enum ap_ro_check_result validate_ranges_sha_with_factory_flags(
+ const struct ro_range *ranges, size_t count,
+ const uint8_t *expected_digest, struct gbb_descriptor *gbbd)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(possible_factory_flags); i++) {
+ gbbd->injected_flags = possible_factory_flags[i];
+ if (validate_ranges_sha(ranges,
+ count,
+ expected_digest,
+ gbbd) == ROV_SUCCEEDED) {
+ CPRINTS("matched gbb %x", gbbd->injected_flags);
+ return ROV_SUCCEEDED;
+ }
+ }
+ return ROV_FAILED;
+}
+
/*****************************************************************************/
/* V1 Factory Verify GBB Support */
/**
@@ -785,10 +899,19 @@ static uint8_t do_ap_ro_check(void)
/* Check the GBB flags are 0 before validating any AP RO ranges. */
if (rv == ROV_SUCCEEDED)
rv = validate_gbbd(&gbbd);
- if (rv == ROV_SUCCEEDED)
- rv = validate_ranges_sha(p_chk->payload.ranges,
- p_chk->header.num_ranges,
- p_chk->payload.digest);
+ if (rv == ROV_SUCCEEDED) {
+ if (gbbd.validate_flags)
+ rv = validate_ranges_sha_with_factory_flags(
+ p_chk->payload.ranges,
+ p_chk->header.num_ranges,
+ p_chk->payload.digest,
+ &gbbd);
+ else
+ rv = validate_ranges_sha(p_chk->payload.ranges,
+ p_chk->header.num_ranges,
+ p_chk->payload.digest,
+ &gbbd);
+ }
disable_ap_spi_hash_shortcut();
@@ -809,6 +932,7 @@ static uint8_t do_ap_ro_check(void)
*/
return EC_ERROR_CRC;
}
+ /* TODO(b/236844541): save gbbd */
apro_result = AP_RO_PASS;
ap_ro_add_flash_event(APROF_CHECK_SUCCEEDED);
CPRINTS("AP RO PASS!");