Boot: Introduce rollback protection
- Add image security counter verification (read image security counter
from the manifest and compare it against the stored security counter)
as a mandatory part of the image validation process.
- Store the newest value of security counter in a non-volatile (NV)
counter.
- Add security counter interface to MCUBoot.
Change-Id: I608508e707d01c3777788bc754810407fae610e2
Signed-off-by: David Vincze <david.vincze@arm.com>
diff --git a/bl2/ext/mcuboot/bootutil/src/loader.c b/bl2/ext/mcuboot/bootutil/src/loader.c
index 42459c3..0a40342 100644
--- a/bl2/ext/mcuboot/bootutil/src/loader.c
+++ b/bl2/ext/mcuboot/bootutil/src/loader.c
@@ -41,6 +41,7 @@
#include "bootutil_priv.h"
#include "bl2/include/tfm_boot_status.h"
#include "bl2/include/boot_record.h"
+#include "security_cnt.h"
#define BOOT_LOG_LEVEL BOOT_LOG_LEVEL_INFO
#include "bootutil/bootutil_log.h"
@@ -260,8 +261,8 @@
return 0;
}
-/*
- * Validate image hash/signature in a slot.
+/**
+ * Validate image hash/signature and security counter in a slot.
*/
static int
boot_image_check(struct image_header *hdr, const struct flash_area *fap)
@@ -320,6 +321,8 @@
}
BOOT_LOG_ERR("Authentication failed! Image in slot %d is not valid.",
slot);
+
+ flash_area_close(fap);
return -1;
}
@@ -329,6 +332,44 @@
return 0;
}
+/**
+ * Updates the stored security counter value with the image's security counter
+ * value which resides in the given slot if it's greater than the stored value.
+ *
+ * @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(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_image_slot(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(0, img_security_cnt);
+ if (rc != 0) {
+ goto done;
+ }
+
+done:
+ flash_area_close(fap);
+ return rc;
+}
+
#if !defined(MCUBOOT_NO_SWAP) && !defined(MCUBOOT_OVERWRITE_ONLY)
/*
* Compute the total size of the given image. Includes the size of
@@ -1094,6 +1135,17 @@
rc = boot_copy_sector(FLASH_AREA_IMAGE_1, FLASH_AREA_IMAGE_0,
0, 0, size);
+ /* Update the stored security counter with the new image's security counter
+ * value. Both slots hold the new image at this point, but slot 1's image
+ * header must be passed because the read image headers in the boot_data
+ * structure have not been updated yet.
+ */
+ rc = boot_update_security_counter(0, boot_img_hdr(&boot_data, 1));
+ if (rc != 0) {
+ BOOT_LOG_ERR("Security counter update failed after image upgrade.");
+ return rc;
+ }
+
/*
* 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
@@ -1399,6 +1451,25 @@
slot = 1;
reload_headers = true;
#ifndef MCUBOOT_OVERWRITE_ONLY
+ if (swap_type == BOOT_SWAP_TYPE_PERM) {
+ /* Update the stored security counter with the new image's security
+ * counter value (the one in the primary slot). Slot 0 holds the
+ * new image at this point, but slot 1's image header must be
+ * passed because the read 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(0, boot_img_hdr(&boot_data, 1));
+ if (rc != 0) {
+ BOOT_LOG_ERR("Security counter update failed after "
+ "image upgrade.");
+ goto out;
+ }
+ }
+
rc = boot_set_copy_done();
if (rc != 0) {
swap_type = BOOT_SWAP_TYPE_PANIC;
@@ -1459,6 +1530,24 @@
}
#endif /* MCUBOOT_VALIDATE_SLOT0 */
+ /* Update the stored security counter with the active image's security
+ * counter value. It will be updated only 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" swap can be performed if it's necessary.
+ */
+ if (swap_type == BOOT_SWAP_TYPE_NONE) {
+ rc = boot_update_security_counter(0, boot_img_hdr(&boot_data, 0));
+ if (rc != 0) {
+ BOOT_LOG_ERR("Security counter update failed after image "
+ "validation.");
+ goto out;
+ }
+ }
+
/* Always boot from the primary slot. */
rsp->br_flash_dev_id = boot_img_fa_device_id(&boot_data, 0);
rsp->br_image_off = boot_img_slot_off(&boot_data, 0);
@@ -1746,6 +1835,16 @@
/* The slot variable now refers to the newest image's slot in flash */
newest_image_header = boot_img_hdr(&boot_data, slot);
+ /* Update the security counter with the newest image's security
+ * counter value.
+ */
+ rc = boot_update_security_counter(slot, newest_image_header);
+ if (rc != 0) {
+ BOOT_LOG_ERR("Security counter update failed after image "
+ "validation.");
+ goto out;
+ }
+
#ifdef MCUBOOT_RAM_LOADING
if (newest_image_header->ih_flags & IMAGE_F_RAM_LOAD) {
/* Copy image to the load address from where it
@@ -1790,6 +1889,7 @@
} else {
/* No candidate image available */
rc = BOOT_EBADIMAGE;
+ goto out;
}
/* Save boot status to shared memory area */