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;
     }