Add config option for caching of validation state of an image in primary slot for single loader

Signed-off-by: Wouter Cappelle <wouter.cappelle@crodeon.com>
diff --git a/boot/boot_serial/src/boot_serial.c b/boot/boot_serial/src/boot_serial.c
index 7209474..7c1d0d9 100644
--- a/boot/boot_serial/src/boot_serial.c
+++ b/boot/boot_serial/src/boot_serial.c
@@ -333,6 +333,14 @@
         if (data_len > flash_area_get_size(fap)) {
             goto out_invalid_data;
         }
+#if defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE)
+        /* We are using swap state at end of flash area to store validation
+         * result. Make sure the user cannot write it from an image to skip validation.
+         */
+        if (data_len > (flash_area_get_size(fap) - BOOT_MAGIC_SZ)) {
+            goto out_invalid_data;
+        }
+#endif
 #ifndef MCUBOOT_ERASE_PROGRESSIVELY
         rc = flash_area_erase(fap, 0, flash_area_get_size(fap));
         if (rc) {
diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig
index 1d16b03..0d32431 100644
--- a/boot/zephyr/Kconfig
+++ b/boot/zephyr/Kconfig
@@ -182,6 +182,18 @@
 	  every boot, but can mitigate against some changes that are
 	  able to modify the flash image itself.
 
+config BOOT_VALIDATE_SLOT0_ONCE
+	bool "Validate image in the primary slot just once after after upgrade"
+	depends on !BOOT_VALIDATE_SLOT0 && SINGLE_APPLICATION_SLOT
+	default n
+	help
+	  If y, the bootloader attempts to validate the signature of the
+	  primary slot only once after an upgrade of the main slot.
+	  It caches the result in the magic area, which makes it an unsecure
+	  method. This option is usefull for lowering the boot up time for
+	  low end devices with as a compromise lowering the security level.
+	  If unsure, leave at the default value.
+
 if !SINGLE_APPLICATION_SLOT
 choice BOOT_IMAGE_UPGRADE_MODE
 	prompt "Image upgrade modes"
diff --git a/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/boot/zephyr/include/mcuboot_config/mcuboot_config.h
index a737a17..2539b5f 100644
--- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h
+++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h
@@ -61,6 +61,10 @@
 #define MCUBOOT_VALIDATE_PRIMARY_SLOT
 #endif
 
+#ifdef CONFIG_BOOT_VALIDATE_SLOT0_ONCE
+#define MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE
+#endif
+
 #ifdef CONFIG_BOOT_UPGRADE_ONLY
 #define MCUBOOT_OVERWRITE_ONLY
 #define MCUBOOT_OVERWRITE_ONLY_FAST
diff --git a/boot/zephyr/single_loader.c b/boot/zephyr/single_loader.c
index 643977f..260f9cc 100644
--- a/boot/zephyr/single_loader.c
+++ b/boot/zephyr/single_loader.c
@@ -58,6 +58,37 @@
 #endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT || MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE*/
 
 
+inline static fih_int
+boot_image_validate_once(const struct flash_area *fa_p,
+                    struct image_header *hdr)
+{
+    static struct boot_swap_state state;
+    int rc;
+    fih_int fih_rc = FIH_FAILURE;
+
+    memset(&state, 0, sizeof(struct boot_swap_state));
+    rc = boot_read_swap_state(fa_p, &state);
+    if (rc != 0)
+        FIH_RET(FIH_FAILURE);
+    if (state.magic != BOOT_MAGIC_GOOD
+            || state.image_ok != BOOT_FLAG_SET) {
+        /* At least validate the image once */
+        FIH_CALL(boot_image_validate, fih_rc, fa_p, hdr);
+        if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
+            FIH_RET(FIH_FAILURE);
+        }
+        if (state.magic != BOOT_MAGIC_GOOD) {
+            rc = boot_write_magic(fa_p);
+            if (rc != 0)
+                FIH_RET(FIH_FAILURE);
+        }
+        rc = boot_write_image_ok(fa_p);
+        if (rc != 0)
+            FIH_RET(FIH_FAILURE);
+    }
+    FIH_RET(FIH_SUCCESS);
+}
+
 /**
  * Attempts to load image header from flash; verifies flash header fields.
  *
@@ -421,6 +452,11 @@
     if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
         goto out;
     }
+#elif defined(MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE)
+    FIH_CALL(boot_image_validate_once, fih_rc, _fa_p, &_hdr);
+    if (fih_not_eq(fih_rc, FIH_SUCCESS)) {
+        goto out;
+    }
 #else
     fih_rc = FIH_SUCCESS;
 #endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */
diff --git a/docs/design.md b/docs/design.md
index 06b4764..548580b 100755
--- a/docs/design.md
+++ b/docs/design.md
@@ -1147,6 +1147,15 @@
     keys will then be iterated over looking for the matching key, which then
     will then be used to verify the image contents.
 
+For low performance MCU's where the validation is a heavy process at boot
+(~1-2 seconds on a arm-cortex-M0), the `MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE`
+could be used. This option will cache the validation result as described above
+into the magic area of the primary slot. The next boot, the validation will be
+skipped if the previous validation was succesfull. This option is reducing the
+security level since if an attacker could modify the contents of the flash after
+a good image has been validated, the attacker could run his own image without
+running validation again. Enabling this option should be done with care.
+
 ## [Security](#security)
 
 As indicated above, the final step of the integrity check is signature