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/image_validate.c b/bl2/ext/mcuboot/bootutil/src/image_validate.c
index 68f1c40..b9db6de 100644
--- a/bl2/ext/mcuboot/bootutil/src/image_validate.c
+++ b/bl2/ext/mcuboot/bootutil/src/image_validate.c
@@ -33,6 +33,7 @@
#include "bootutil/image.h"
#include "bootutil/sha256.h"
#include "bootutil/sign_key.h"
+#include "security_cnt.h"
#ifdef MCUBOOT_SIGN_RSA
#include "mbedtls/rsa.h"
@@ -164,7 +165,7 @@
info = *((struct image_tlv_info *)(load_address + off));
if (info.it_magic != IMAGE_TLV_INFO_MAGIC) {
- return -1;
+ return BOOT_EBADMAGIC;
}
end = off + info.it_tlv_tot;
off += sizeof(info);
@@ -173,7 +174,7 @@
* Traverse through all of the TLVs, performing any checks we know
* and are able to do.
*/
- for (; off < end; off += sizeof(tlv) + tlv.it_len) {
+ while (off < end) {
tlv = *((struct image_tlv *)(load_address + off));
tlv_sz = sizeof(tlv);
@@ -192,6 +193,14 @@
sha256_valid = 1;
}
+
+ /* Avoid integer overflow. */
+ if ((UINT32_MAX - off) < (sizeof(tlv) + tlv.it_len)) {
+ /* Potential overflow. */
+ break;
+ } else {
+ off += sizeof(tlv) + tlv.it_len;
+ }
}
if (!sha256_valid) {
@@ -202,6 +211,97 @@
}
#endif /* MCUBOOT_RAM_LOADING */
+/**
+ * Reads the value of an image's security counter.
+ *
+ * @param hdr Pointer to the image header structure.
+ * @param fap Pointer to a description structure of the image's
+ * flash area.
+ * @param security_cnt Pointer to store the security counter value.
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+int32_t
+bootutil_get_img_security_cnt(struct image_header *hdr,
+ const struct flash_area *fap,
+ uint32_t *img_security_cnt)
+{
+ struct image_tlv_info info;
+ struct image_tlv tlv;
+ uint32_t off;
+ uint32_t end;
+ uint32_t found = 0;
+ int32_t rc;
+
+ if ((hdr == NULL) ||
+ (fap == NULL) ||
+ (img_security_cnt == NULL)) {
+ /* Invalid parameter. */
+ return BOOT_EBADARGS;
+ }
+
+ /* The TLVs come after the image. */
+ off = hdr->ih_hdr_size + hdr->ih_img_size;
+
+ /* The TLV area always starts with an image_tlv_info structure. */
+ rc = flash_area_read(fap, off, &info, sizeof(info));
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ if (info.it_magic != IMAGE_TLV_INFO_MAGIC) {
+ return BOOT_EBADMAGIC;
+ }
+
+ /* The security counter TLV is in the protected part of the TLV area. */
+ if (hdr->ih_protect_tlv_size != 0) {
+ end = off + (uint32_t)hdr->ih_protect_tlv_size;
+ off += sizeof(info);
+
+ /* Traverse through the protected TLV area to find the
+ * security counter TLV.
+ */
+ while (off < end) {
+ rc = flash_area_read(fap, off, &tlv, sizeof(tlv));
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ if (tlv.it_type == IMAGE_TLV_SEC_CNT) {
+
+ if (tlv.it_len != sizeof(*img_security_cnt)) {
+ /* Security counter is not valid. */
+ break;
+ }
+
+ rc = flash_area_read(fap, off + sizeof(tlv),
+ img_security_cnt, tlv.it_len);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ /* Security counter has been found. */
+ found = 1;
+ break;
+ }
+
+ /* Avoid integer overflow. */
+ if ((UINT32_MAX - off) < (sizeof(tlv) + tlv.it_len)) {
+ /* Potential overflow. */
+ break;
+ } else {
+ off += sizeof(tlv) + tlv.it_len;
+ }
+ }
+ }
+
+ if (found) {
+ return 0;
+ }
+
+ return -1;
+}
+
/*
* Verify the integrity of the image.
* Return non-zero if image could not be validated/does not validate.
@@ -222,6 +322,9 @@
struct image_tlv tlv;
uint8_t buf[256];
uint8_t hash[32] = {0};
+ uint32_t security_cnt;
+ uint32_t img_security_cnt;
+ int32_t security_counter_valid = 0;
int rc;
rc = bootutil_img_hash(hdr, fap, tmp_buf, tmp_buf_sz, hash,
@@ -242,7 +345,7 @@
return rc;
}
if (info.it_magic != IMAGE_TLV_INFO_MAGIC) {
- return -1;
+ return BOOT_EBADMAGIC;
}
end = off + info.it_tlv_tot;
off += sizeof(info);
@@ -251,7 +354,7 @@
* Traverse through all of the TLVs, performing any checks we know
* and are able to do.
*/
- for (; off < end; off += sizeof(tlv) + tlv.it_len) {
+ while (off < end) {
rc = flash_area_read(fap, off, &tlv, sizeof(tlv));
if (rc) {
return rc;
@@ -311,10 +414,49 @@
}
key_id = -1;
#endif
+ } else if (tlv.it_type == IMAGE_TLV_SEC_CNT) {
+ /*
+ * Verify the image's security counter.
+ * This must always be present.
+ */
+ if (tlv.it_len != sizeof(img_security_cnt)) {
+ /* Security counter is not valid. */
+ return -1;
+ }
+
+ rc = flash_area_read(fap, off + sizeof(tlv),
+ &img_security_cnt, tlv.it_len);
+ if (rc) {
+ return rc;
+ }
+
+ rc = boot_nv_security_counter_get(0, &security_cnt);
+ if (rc) {
+ return rc;
+ }
+
+ /* Compare the new image's security counter value against the
+ * stored security counter value.
+ */
+ if (img_security_cnt < security_cnt) {
+ /* The image's security counter is not accepted. */
+ return -1;
+ }
+
+ /* The image's security counter has been successfully verified. */
+ security_counter_valid = 1;
+ }
+
+ /* Avoid integer overflow. */
+ if ((UINT32_MAX - off) < (sizeof(tlv) + tlv.it_len)) {
+ /* Potential overflow. */
+ break;
+ } else {
+ off += sizeof(tlv) + tlv.it_len;
}
}
- if (!sha256_valid) {
+ if (!sha256_valid || !security_counter_valid) {
return -1;
}