Add downgrade prevention for swaps

Currently, downgrade prevention was limited to overwrite only
builds (version check) or devices with hardware storage for
security counter.

This extends downgrade prevention to be used when swap update
is selected.
Unlike MCUBOOT_HW_ROLLBACK_PROT option it does not require user
code to provide external way to store security counter.
Security counter from slot 1 image is used for comparison.
With security counter usage it is possible to have limited
software rollback if security counter was not incremented.

It is possible to use image version where strict rule for
image version comparison prevents any downgrades.

Downgrade prevention is also added to mynewt configuration.

If image in slot 1 is marked as pending and downgrade prevention
is in place, image will be deleted to avoid check on next boot.

Signed-off-by: Jerzy Kasenberg <jerzy.kasenberg@codecoup.pl>
diff --git a/boot/bootutil/src/image_validate.c b/boot/bootutil/src/image_validate.c
index 9be80f0..6891394 100644
--- a/boot/bootutil/src/image_validate.c
+++ b/boot/bootutil/src/image_validate.c
@@ -268,7 +268,6 @@
 #endif /* !MCUBOOT_HW_KEY */
 #endif
 
-#ifdef MCUBOOT_HW_ROLLBACK_PROT
 /**
  * Reads the value of an image's security counter.
  *
@@ -328,7 +327,6 @@
 
     return 0;
 }
-#endif /* MCUBOOT_HW_ROLLBACK_PROT */
 
 /*
  * Verify the integrity of the image.
diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c
index 24dcab4..e59fad7 100644
--- a/boot/bootutil/src/loader.c
+++ b/boot/bootutil/src/loader.c
@@ -616,7 +616,7 @@
 #if (BOOT_IMAGE_NUMBER > 1) || \
     defined(MCUBOOT_DIRECT_XIP) || \
     defined(MCUBOOT_RAM_LOAD) || \
-    (defined(MCUBOOT_OVERWRITE_ONLY) && defined(MCUBOOT_DOWNGRADE_PREVENTION))
+    defined(MCUBOOT_DOWNGRADE_PREVENTION)
 /**
  * Compare image version numbers not including the build number
  *
@@ -1905,6 +1905,60 @@
 #endif
 }
 
+/**
+ * Checks test swap downgrade prevention conditions.
+ *
+ * Function called only for swap upgrades test run.  It may prevent
+ * swap if slot 1 image has <= version number or < security counter
+ *
+ * @param  state        Boot loader status information.
+ *
+ * @return              0 - image can be swapped, -1 downgrade prevention
+ */
+static int
+check_downgrade_prevention(struct boot_loader_state *state)
+{
+#if defined(MCUBOOT_DOWNGRADE_PREVENTION) && \
+    (defined(MCUBOOT_SWAP_USING_MOVE) || defined(MCUBOOT_SWAP_USING_SCRATCH))
+    uint32_t security_counter[2];
+    int rc;
+
+    if (MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER) {
+        /* If there was security no counter in slot 0, allow swap */
+        rc = bootutil_get_img_security_cnt(&(BOOT_IMG(state, 0).hdr),
+                                           BOOT_IMG(state, 0).area,
+                                           &security_counter[0]);
+        if (rc != 0) {
+            return 0;
+        }
+        /* If there is no security counter in slot 1, or it's lower than
+         * that of slot 0, prevent downgrade */
+        rc = bootutil_get_img_security_cnt(&(BOOT_IMG(state, 1).hdr),
+                                           BOOT_IMG(state, 1).area,
+                                           &security_counter[1]);
+        if (rc != 0 || security_counter[0] > security_counter[1]) {
+            rc = -1;
+        }
+    }
+    else {
+        rc = boot_version_cmp(&boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver,
+                              &boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver);
+    }
+    if (rc < 0) {
+        /* Image in slot 0 prevents downgrade, delete image in slot 1 */
+        BOOT_LOG_INF("Image in slot 1 erased due to downgrade prevention");
+        flash_area_erase(BOOT_IMG(state, 1).area, 0,
+                         flash_area_get_size(BOOT_IMG(state, 1).area));
+    } else {
+        rc = 0;
+    }
+    return rc;
+#else
+    (void)state;
+    return 0;
+#endif
+}
+
 fih_int
 context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp)
 {
@@ -2033,7 +2087,13 @@
         case BOOT_SWAP_TYPE_NONE:
             break;
 
-        case BOOT_SWAP_TYPE_TEST:          /* fallthrough */
+        case BOOT_SWAP_TYPE_TEST:
+            if (check_downgrade_prevention(state) != 0) {
+                /* Downgrade prevented */
+                BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE;
+                break;
+            }
+            /* fallthrough */
         case BOOT_SWAP_TYPE_PERM:          /* fallthrough */
         case BOOT_SWAP_TYPE_REVERT:
             rc = BOOT_HOOK_CALL(boot_perform_update_hook, BOOT_HOOK_REGULAR,
diff --git a/boot/mynewt/mcuboot_config/include/mcuboot_config/mcuboot_config.h b/boot/mynewt/mcuboot_config/include/mcuboot_config/mcuboot_config.h
index 980eda9..f02e40d 100644
--- a/boot/mynewt/mcuboot_config/include/mcuboot_config/mcuboot_config.h
+++ b/boot/mynewt/mcuboot_config/include/mcuboot_config/mcuboot_config.h
@@ -85,6 +85,19 @@
 #if MYNEWT_VAL(BOOTUTIL_BOOTSTRAP)
 #define MCUBOOT_BOOTSTRAP 1
 #endif
+#if MYNEWT_VAL_CHOICE(BOOTUTIL_DOWNGRADE_PREVENTION, version)
+#define MCUBOOT_DOWNGRADE_PREVENTION                     1
+/* MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER is used later as bool value so it is
+ * always defined, (unlike MCUBOOT_DOWNGRADE_PREVENTION which is only used in
+ * preprocessor condition and my be not defined) */
+#define MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER    0
+#elif MYNEWT_VAL_CHOICE(BOOTUTIL_DOWNGRADE_PREVENTION, security_counter)
+#define MCUBOOT_DOWNGRADE_PREVENTION                     1
+#define MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER    1
+#endif
+#if MYNEWT_VAL(BOOTUTIL_HW_DOWNGRADE_PREVENTION)
+#define MCUBOOT_HW_ROLLBACK_PROT 1
+#endif
 
 #if MYNEWT_VAL(MCUBOOT_MEASURED_BOOT)
 #define MCUBOOT_MEASURED_BOOT       1
diff --git a/boot/mynewt/mcuboot_config/syscfg.yml b/boot/mynewt/mcuboot_config/syscfg.yml
index 74570f2..b2e5868 100644
--- a/boot/mynewt/mcuboot_config/syscfg.yml
+++ b/boot/mynewt/mcuboot_config/syscfg.yml
@@ -76,6 +76,34 @@
     BOOTUTIL_MAX_IMG_SECTORS:
         description: 'Maximum number of sectors that are swapped.'
         value: 128
+    BOOTUTIL_DOWNGRADE_PREVENTION:
+        description: >
+            Select downgrade prevention strategy.
+            - none downgrades are allowed
+            - version:
+                Prevent downgrades by enforcing incrementing version numbers.
+                When this option is set, any upgrade must have greater major version
+                or greater minor version with equal major version. This mechanism
+                only protects against some attacks against version downgrades (for
+                example, a JTAG could be used to write an older version).
+            - security_counter:
+                security counter is used for version eligibility check instead of pure
+                version.  When this option is set, any upgrade must have greater or
+                equal security counter value.
+                Because of the acceptance of equal values it allows for software
+                downgrades to some extent.
+        choices:
+            - none
+            - version
+            - security_counter
+        value: none
+    BOOTUTIL_HW_ROLLBACK_PROT:
+        description: >
+            Prevent undesirable/malicious software downgrades. When this option is
+            set, any upgrade must have greater or equal security counter value.
+            Because of the acceptance of equal values it allows for software
+            downgrade to some extent
+        value: 0
     BOOTUTIL_HAVE_LOGGING:
         description: 'Enable serial logging'
         value: 0