bootutil: Introduce HW rollback protection
- Add image security counter verification (read security counter value
from the image manifest and compare it against the stored/active
security counter) as an optional part of the image validation process
to prevent the restoration of older, potentially vulnerable images.
- This feature can be enabled with the MCUBOOT_HW_ROLLBACK_PROT option.
- Add security counter interface to MCUBoot. If HW rollback protection
is enabled then the platform must provide a mechanism to store and
read the security counter value in a robust and secure way.
Change-Id: Iee4961c1da5275a98ef17982a65b361370d2a178
Signed-off-by: David Vincze <david.vincze@arm.com>
diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c
index 4b8a3a2..9d4f054 100644
--- a/boot/bootutil/src/loader.c
+++ b/boot/bootutil/src/loader.c
@@ -18,7 +18,7 @@
*/
/*
- * Modifications are Copyright (c) 2019 Arm Limited.
+ * Modifications are Copyright (c) 2019-2020 Arm Limited.
*/
/**
@@ -38,6 +38,7 @@
#include "bootutil_priv.h"
#include "swap_priv.h"
#include "bootutil/bootutil_log.h"
+#include "bootutil/security_cnt.h"
#ifdef MCUBOOT_ENC_IMAGES
#include "bootutil/enc_key.h"
@@ -374,7 +375,7 @@
}
/*
- * Validate image hash/signature in a slot.
+ * Validate image hash/signature and optionally the security counter in a slot.
*/
static int
boot_image_check(struct boot_loader_state *state, struct image_header *hdr,
@@ -649,6 +650,51 @@
return swap_type;
}
+#ifdef MCUBOOT_HW_ROLLBACK_PROT
+/**
+ * Updates the stored security counter value with the image's security counter
+ * value which resides in the given slot, only if it's greater than the stored
+ * value.
+ *
+ * @param image_index Index of the image to determine which security
+ * counter to update.
+ * @param slot Slot number of the image.
+ * @param hdr Pointer to the image header structure of the image
+ * that is currently stored in the given slot.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+boot_update_security_counter(uint8_t image_index, int slot,
+ struct image_header *hdr)
+{
+ const struct flash_area *fap = NULL;
+ uint32_t img_security_cnt;
+ int rc;
+
+ rc = flash_area_open(flash_area_id_from_multi_image_slot(image_index, slot),
+ &fap);
+ if (rc != 0) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ rc = bootutil_get_img_security_cnt(hdr, fap, &img_security_cnt);
+ if (rc != 0) {
+ goto done;
+ }
+
+ rc = boot_nv_security_counter_update(image_index, img_security_cnt);
+ if (rc != 0) {
+ goto done;
+ }
+
+done:
+ flash_area_close(fap);
+ return rc;
+}
+#endif /* MCUBOOT_HW_ROLLBACK_PROT */
+
/**
* Erases a region of flash.
*
@@ -860,6 +906,20 @@
size);
rc = boot_copy_region(state, fap_secondary_slot, fap_primary_slot, 0, 0, size);
+#ifdef MCUBOOT_HW_ROLLBACK_PROT
+ /* Update the stored security counter with the new image's security counter
+ * value. Both slots hold the new image at this point, but the secondary
+ * slot's image header must be passed since the image headers in the
+ * boot_data structure have not been updated yet.
+ */
+ rc = boot_update_security_counter(BOOT_CURR_IMG(state), BOOT_PRIMARY_SLOT,
+ boot_img_hdr(state, BOOT_SECONDARY_SLOT));
+ if (rc != 0) {
+ BOOT_LOG_ERR("Security counter update failed after image upgrade.");
+ return rc;
+ }
+#endif /* MCUBOOT_HW_ROLLBACK_PROT */
+
/*
* Erases header and trailer. The trailer is erased because when a new
* image is written without a trailer as is the case when using newt, the
@@ -1226,6 +1286,29 @@
}
}
+#ifdef MCUBOOT_HW_ROLLBACK_PROT
+ if (swap_type == BOOT_SWAP_TYPE_PERM) {
+ /* Update the stored security counter with the new image's security
+ * counter value. The primary slot holds the new image at this point,
+ * but the secondary slot's image header must be passed since image
+ * headers in the boot_data structure have not been updated yet.
+ *
+ * In case of a permanent image swap mcuboot will never attempt to
+ * revert the images on the next reboot. Therefore, the security
+ * counter must be increased right after the image upgrade.
+ */
+ rc = boot_update_security_counter(
+ BOOT_CURR_IMG(state),
+ BOOT_PRIMARY_SLOT,
+ boot_img_hdr(state, BOOT_SECONDARY_SLOT));
+ if (rc != 0) {
+ BOOT_LOG_ERR("Security counter update failed after "
+ "image upgrade.");
+ BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_PANIC;
+ }
+ }
+#endif /* MCUBOOT_HW_ROLLBACK_PROT */
+
if (BOOT_IS_UPGRADE(swap_type)) {
rc = swap_set_copy_done(BOOT_CURR_IMG(state));
if (rc != 0) {
@@ -1680,7 +1763,31 @@
rc = BOOT_EBADIMAGE;
goto out;
}
-#endif
+#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */
+
+#ifdef MCUBOOT_HW_ROLLBACK_PROT
+ /* Update the stored security counter with the active image's security
+ * counter value. It will only be updated if the new security counter is
+ * greater than the stored value.
+ *
+ * In case of a successful image swapping when the swap type is TEST the
+ * security counter can be increased only after a reset, when the swap
+ * type is NONE and the image has marked itself "OK" (the image_ok flag
+ * has been set). This way a "revert" can be performed when it's
+ * necessary.
+ */
+ if (BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_NONE) {
+ rc = boot_update_security_counter(
+ BOOT_CURR_IMG(state),
+ BOOT_PRIMARY_SLOT,
+ boot_img_hdr(state, BOOT_PRIMARY_SLOT));
+ if (rc != 0) {
+ BOOT_LOG_ERR("Security counter update failed after image "
+ "validation.");
+ goto out;
+ }
+ }
+#endif /* MCUBOOT_HW_ROLLBACK_PROT */
}
#if (BOOT_IMAGE_NUMBER > 1)