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/image_validate.c b/boot/bootutil/src/image_validate.c
index dd379dc..4390306 100644
--- a/boot/bootutil/src/image_validate.c
+++ b/boot/bootutil/src/image_validate.c
@@ -18,10 +18,11 @@
  */
 
 /*
- * Modifications are Copyright (c) 2019 Arm Limited.
+ * Modifications are Copyright (c) 2019-2020 Arm Limited.
  */
 
 #include <stddef.h>
+#include <stdint.h>
 #include <inttypes.h>
 #include <string.h>
 
@@ -30,6 +31,7 @@
 #include "bootutil/image.h"
 #include "bootutil/sha256.h"
 #include "bootutil/sign_key.h"
+#include "bootutil/security_cnt.h"
 
 #include "mcuboot_config/mcuboot_config.h"
 
@@ -200,6 +202,68 @@
 }
 #endif
 
+#ifdef MCUBOOT_HW_ROLLBACK_PROT
+/**
+ * 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_iter it;
+    uint32_t off;
+    uint16_t len;
+    int32_t rc;
+
+    if ((hdr == NULL) ||
+        (fap == NULL) ||
+        (img_security_cnt == NULL)) {
+        /* Invalid parameter. */
+        return BOOT_EBADARGS;
+    }
+
+    /* The security counter TLV is in the protected part of the TLV area. */
+    if (hdr->ih_protect_tlv_size == 0) {
+        return BOOT_EBADIMAGE;
+    }
+
+    rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_SEC_CNT, true);
+    if (rc) {
+        return rc;
+    }
+
+    /* Traverse through the protected TLV area to find
+     * the security counter TLV.
+     */
+
+    rc = bootutil_tlv_iter_next(&it, &off, &len, NULL);
+    if (rc != 0) {
+        /* Security counter TLV has not been found. */
+        return -1;
+    }
+
+    if (len != sizeof(*img_security_cnt)) {
+        /* Security counter is not valid. */
+        return BOOT_EBADIMAGE;
+    }
+
+    rc = flash_area_read(fap, off, img_security_cnt, len);
+    if (rc != 0) {
+        return BOOT_EFLASH;
+    }
+
+    return 0;
+}
+#endif /* MCUBOOT_HW_ROLLBACK_PROT */
+
 /*
  * Verify the integrity of the image.
  * Return non-zero if image could not be validated/does not validate.
@@ -222,6 +286,11 @@
     uint8_t buf[SIG_BUF_SIZE];
     uint8_t hash[32];
     int rc;
+#ifdef MCUBOOT_HW_ROLLBACK_PROT
+    uint32_t security_cnt = UINT32_MAX;
+    uint32_t img_security_cnt = 0;
+    int32_t security_counter_valid = 0;
+#endif
 
     rc = bootutil_img_hash(enc_state, image_index, hdr, fap, tmp_buf,
             tmp_buf_sz, hash, seed, seed_len);
@@ -302,19 +371,53 @@
                 valid_signature = 1;
             }
             key_id = -1;
-#endif
+#endif /* EXPECTED_SIG_TLV */
+#ifdef MCUBOOT_HW_ROLLBACK_PROT
+        } else if (type == IMAGE_TLV_SEC_CNT) {
+            /*
+             * Verify the image's security counter.
+             * This must always be present.
+             */
+            if (len != sizeof(img_security_cnt)) {
+                /* Security counter is not valid. */
+                return -1;
+            }
+
+            rc = flash_area_read(fap, off, &img_security_cnt, len);
+            if (rc) {
+                return rc;
+            }
+
+            rc = boot_nv_security_counter_get(image_index, &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;
+#endif /* MCUBOOT_HW_ROLLBACK_PROT */
         }
     }
 
     if (!sha256_valid) {
         return -1;
-    }
-
 #ifdef EXPECTED_SIG_TLV
-    if (!valid_signature) {
+    } else if (!valid_signature) {
         return -1;
-    }
 #endif
+#ifdef MCUBOOT_HW_ROLLBACK_PROT
+    } else if (!security_counter_valid) {
+        return -1;
+#endif
+    }
 
     return 0;
 }