bootutil: allow encryption key TLVs in swap status

Add a new option that when enabled, allows a swap status to store
an encrypted key TLV instead of plain keys. When a new swap operation is
started the encryption keys are saved to the swap status area to allow
for resuming (because it is challenging to find those TLV in the middle
of a swap operation).

Previously those keys were saved in plain text, so it would be easy to
dump them if the images were stored in external flash. With this new
option one can choose to save the TLV instead, which uses more flash
but does not leak secrets. The amount of flash required varies depending
on the size of the TLV, which is 48 for AES-128-KW, 512 for RSA and 240
for ECIES-P256.

Signed-off-by: Fabio Utzig <utzig@apache.org>
diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c
index a29a27d..ab79b8b 100644
--- a/boot/bootutil/src/bootutil_misc.c
+++ b/boot/bootutil/src/bootutil_misc.c
@@ -161,7 +161,11 @@
            boot_status_sz(min_write_sz)           +
 #ifdef MCUBOOT_ENC_IMAGES
            /* encryption keys */
+#  if MCUBOOT_SWAP_SAVE_ENCTLV
+           BOOT_ENC_TLV_ALIGN_SIZE * 2            +
+#  else
            BOOT_ENC_KEY_SIZE * 2                  +
+#  endif
 #endif
            /* swap_type + copy_done + image_ok + swap_size */
            BOOT_MAX_ALIGN * 4                     +
@@ -231,7 +235,12 @@
 static inline uint32_t
 boot_enc_key_off(const struct flash_area *fap, uint8_t slot)
 {
+#if MCUBOOT_SWAP_SAVE_ENCTLV
+    return boot_swap_size_off(fap) - ((slot + 1) *
+            ((((BOOT_ENC_TLV_SIZE - 1) / BOOT_MAX_ALIGN) + 1) * BOOT_MAX_ALIGN));
+#else
     return boot_swap_size_off(fap) - ((slot + 1) * BOOT_ENC_KEY_SIZE);
+#endif
 }
 #endif
 
@@ -390,16 +399,34 @@
 
 #ifdef MCUBOOT_ENC_IMAGES
 int
-boot_read_enc_key(int image_index, uint8_t slot, uint8_t *enckey)
+boot_read_enc_key(int image_index, uint8_t slot, struct boot_status *bs)
 {
     uint32_t off;
     const struct flash_area *fap;
+#if MCUBOOT_SWAP_SAVE_ENCTLV
+    int i;
+#endif
     int rc;
 
     rc = boot_find_status(image_index, &fap);
     if (rc == 0) {
         off = boot_enc_key_off(fap, slot);
-        rc = flash_area_read(fap, off, enckey, BOOT_ENC_KEY_SIZE);
+#if MCUBOOT_SWAP_SAVE_ENCTLV
+        rc = flash_area_read(fap, off, bs->enctlv[slot], BOOT_ENC_TLV_ALIGN_SIZE);
+        if (rc == 0) {
+            for (i = 0; i < BOOT_ENC_TLV_ALIGN_SIZE; i++) {
+                if (bs->enctlv[slot][i] != 0xff) {
+                    break;
+                }
+            }
+            /* Only try to decrypt non-erased TLV metadata */
+            if (i != BOOT_ENC_TLV_ALIGN_SIZE) {
+                rc = boot_enc_decrypt(bs->enctlv[slot], bs->enckey[slot]);
+            }
+        }
+#else
+        rc = flash_area_read(fap, off, bs->enckey[slot], BOOT_ENC_KEY_SIZE);
+#endif
         flash_area_close(fap);
     }
 
@@ -526,7 +553,8 @@
 
 #ifdef MCUBOOT_ENC_IMAGES
 int
-boot_write_enc_key(const struct flash_area *fap, uint8_t slot, const uint8_t *enckey)
+boot_write_enc_key(const struct flash_area *fap, uint8_t slot,
+        const struct boot_status *bs)
 {
     uint32_t off;
     int rc;
@@ -535,7 +563,11 @@
     BOOT_LOG_DBG("writing enc_key; fa_id=%d off=0x%lx (0x%lx)",
                  fap->fa_id, (unsigned long)off,
                  (unsigned long)fap->fa_off + off);
-    rc = flash_area_write(fap, off, enckey, BOOT_ENC_KEY_SIZE);
+#if MCUBOOT_SWAP_SAVE_ENCTLV
+    rc = flash_area_write(fap, off, bs->enctlv[slot], BOOT_ENC_TLV_ALIGN_SIZE);
+#else
+    rc = flash_area_write(fap, off, bs->enckey[slot], BOOT_ENC_KEY_SIZE);
+#endif
     if (rc != 0) {
         return BOOT_EFLASH;
     }
diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h
index df49453..99c94dd 100644
--- a/boot/bootutil/src/bootutil_priv.h
+++ b/boot/bootutil/src/bootutil_priv.h
@@ -85,6 +85,9 @@
     uint32_t swap_size;   /* Total size of swapped image */
 #ifdef MCUBOOT_ENC_IMAGES
     uint8_t enckey[BOOT_NUM_SLOTS][BOOT_ENC_KEY_SIZE];
+#if MCUBOOT_SWAP_SAVE_ENCTLV
+    uint8_t enctlv[BOOT_NUM_SLOTS][BOOT_ENC_TLV_ALIGN_SIZE];
+#endif
 #endif
     int source;           /* Which slot contains swap status metadata */
 };
@@ -280,8 +283,8 @@
 
 #ifdef MCUBOOT_ENC_IMAGES
 int boot_write_enc_key(const struct flash_area *fap, uint8_t slot,
-                       const uint8_t *enckey);
-int boot_read_enc_key(int image_index, uint8_t slot, uint8_t *enckey);
+                       const struct boot_status *bs);
+int boot_read_enc_key(int image_index, uint8_t slot, struct boot_status *bs);
 #endif
 
 /**
diff --git a/boot/bootutil/src/encrypted.c b/boot/bootutil/src/encrypted.c
index 1e04741..951c800 100644
--- a/boot/bootutil/src/encrypted.c
+++ b/boot/bootutil/src/encrypted.c
@@ -60,13 +60,10 @@
 
 #include "bootutil_priv.h"
 
-#define TLV_ENC_RSA_SZ  256
-#define TLV_ENC_KW_SZ   24
-
 #if defined(MCUBOOT_ENCRYPT_KW)
 #if defined(MCUBOOT_USE_MBED_TLS)
 static int
-key_unwrap(uint8_t *wrapped, uint8_t *enckey)
+key_unwrap(const uint8_t *wrapped, uint8_t *enckey)
 {
     mbedtls_nist_kw_context kw;
     int rc;
@@ -93,7 +90,7 @@
  * tinycrypt for AES-128 decryption.
  */
 static int
-key_unwrap(uint8_t *wrapped, uint8_t *enckey)
+key_unwrap(const uint8_t *wrapped, uint8_t *enckey)
 {
     struct tc_aes_key_sched_struct aes;
     uint8_t A[8];
@@ -388,13 +385,15 @@
 #endif
 
 int
-boot_enc_set_key(struct enc_key_data *enc_state, uint8_t slot, uint8_t *enckey)
+boot_enc_set_key(struct enc_key_data *enc_state, uint8_t slot,
+        const struct boot_status *bs)
 {
     int rc;
 
 #if defined(MCUBOOT_USE_MBED_TLS)
     mbedtls_aes_init(&enc_state[slot].aes);
-    rc = mbedtls_aes_setkey_enc(&enc_state[slot].aes, enckey, BOOT_ENC_KEY_SIZE_BITS);
+    rc = mbedtls_aes_setkey_enc(&enc_state[slot].aes, bs->enckey[slot],
+            BOOT_ENC_KEY_SIZE_BITS);
     if (rc) {
         mbedtls_aes_free(&enc_state[slot].aes);
         return -1;
@@ -403,7 +402,7 @@
     (void)rc;
 
     /* set_encrypt and set_decrypt do the same thing in tinycrypt */
-    tc_aes128_set_encrypt_key(&enc_state[slot].aes, enckey);
+    tc_aes128_set_encrypt_key(&enc_state[slot].aes, bs->enckey[slot]);
 #endif
 
     enc_state[slot].valid = 1;
@@ -411,27 +410,29 @@
     return 0;
 }
 
+#define EXPECTED_ENC_LEN        BOOT_ENC_TLV_SIZE
+
 #if defined(MCUBOOT_ENCRYPT_RSA)
 #    define EXPECTED_ENC_TLV    IMAGE_TLV_ENC_RSA2048
-#    define EXPECTED_ENC_LEN    TLV_ENC_RSA_SZ
 #elif defined(MCUBOOT_ENCRYPT_KW)
 #    define EXPECTED_ENC_TLV    IMAGE_TLV_ENC_KW128
-#    define EXPECTED_ENC_LEN    TLV_ENC_KW_SZ
 #elif defined(MCUBOOT_ENCRYPT_EC256)
 #    define EXPECTED_ENC_TLV    IMAGE_TLV_ENC_EC256
-#    define EXPECTED_ENC_LEN    (65 + 32 + 16)
 #    define EC_PUBK_INDEX       (1)
 #    define EC_TAG_INDEX        (65)
 #    define EC_CIPHERKEY_INDEX  (65 + 32)
+_Static_assert(EC_CIPHERKEY_INDEX + 16 == EXPECTED_ENC_LEN,
+        "Please fix ECIES-P256 component indexes");
 #endif
 
 /*
- * Load encryption key.
+ * Decrypt an encryption key TLV.
+ *
+ * @param buf An encryption TLV read from flash (build time fixed length)
+ * @param enckey An AES-128 key sized buffer to store to plain key.
  */
 int
-boot_enc_load(struct enc_key_data *enc_state, int image_index,
-        const struct image_header *hdr, const struct flash_area *fap,
-        uint8_t *enckey)
+boot_enc_decrypt(const uint8_t *buf, uint8_t *enckey)
 {
 #if defined(MCUBOOT_ENCRYPT_RSA)
     mbedtls_rsa_context rsa;
@@ -449,43 +450,9 @@
     uint8_t *cpend;
     uint8_t pk[NUM_ECC_BYTES];
     uint8_t counter[TC_AES_BLOCK_SIZE];
-#endif
-    uint32_t off;
     uint16_t len;
-    struct image_tlv_iter it;
-    uint8_t buf[EXPECTED_ENC_LEN];
-    uint8_t slot;
-    int rc;
-
-    rc = flash_area_id_to_multi_image_slot(image_index, fap->fa_id);
-    if (rc < 0) {
-        return rc;
-    }
-    slot = rc;
-
-    /* Already loaded... */
-    if (enc_state[slot].valid) {
-        return 1;
-    }
-
-    rc = bootutil_tlv_iter_begin(&it, hdr, fap, EXPECTED_ENC_TLV, false);
-    if (rc) {
-        return -1;
-    }
-
-    rc = bootutil_tlv_iter_next(&it, &off, &len, NULL);
-    if (rc != 0) {
-        return rc;
-    }
-
-    if (len != EXPECTED_ENC_LEN) {
-        return -1;
-    }
-
-    rc = flash_area_read(fap, off, buf, EXPECTED_ENC_LEN);
-    if (rc) {
-        return -1;
-    }
+#endif
+    int rc = -1;
 
 #if defined(MCUBOOT_ENCRYPT_RSA)
 
@@ -604,6 +571,63 @@
     return rc;
 }
 
+/*
+ * Load encryption key.
+ */
+int
+boot_enc_load(struct enc_key_data *enc_state, int image_index,
+        const struct image_header *hdr, const struct flash_area *fap,
+        struct boot_status *bs)
+{
+    uint32_t off;
+    uint16_t len;
+    struct image_tlv_iter it;
+#if MCUBOOT_SWAP_SAVE_ENCTLV
+    uint8_t *buf;
+#else
+    uint8_t buf[EXPECTED_ENC_LEN];
+#endif
+    uint8_t slot;
+    int rc;
+
+    rc = flash_area_id_to_multi_image_slot(image_index, fap->fa_id);
+    if (rc < 0) {
+        return rc;
+    }
+    slot = rc;
+
+    /* Already loaded... */
+    if (enc_state[slot].valid) {
+        return 1;
+    }
+
+    rc = bootutil_tlv_iter_begin(&it, hdr, fap, EXPECTED_ENC_TLV, false);
+    if (rc) {
+        return -1;
+    }
+
+    rc = bootutil_tlv_iter_next(&it, &off, &len, NULL);
+    if (rc != 0) {
+        return rc;
+    }
+
+    if (len != EXPECTED_ENC_LEN) {
+        return -1;
+    }
+
+#if MCUBOOT_SWAP_SAVE_ENCTLV
+    buf = bs->enctlv[slot];
+    memset(buf, 0xff, BOOT_ENC_TLV_ALIGN_SIZE);
+#endif
+
+    rc = flash_area_read(fap, off, buf, EXPECTED_ENC_LEN);
+    if (rc) {
+        return -1;
+    }
+
+    return boot_enc_decrypt(buf, bs->enckey[slot]);
+}
+
 bool
 boot_enc_valid(struct enc_key_data *enc_state, int image_index,
         const struct flash_area *fap)
diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c
index 8f143a0..06463a8 100644
--- a/boot/bootutil/src/loader.c
+++ b/boot/bootutil/src/loader.c
@@ -285,7 +285,17 @@
 void
 boot_status_reset(struct boot_status *bs)
 {
-    memset(bs, 0, sizeof *bs);
+#ifdef MCUBOOT_ENC_IMAGES
+    memset(&bs->enckey, 0xff, BOOT_NUM_SLOTS * BOOT_ENC_KEY_SIZE);
+#if MCUBOOT_SWAP_SAVE_ENCTLV
+    memset(&bs->enctlv, 0xff, BOOT_NUM_SLOTS * BOOT_ENC_TLV_ALIGN_SIZE);
+#endif
+#endif /* MCUBOOT_ENC_IMAGES */
+
+    bs->use_scratch = 0;
+    bs->swap_size = 0;
+    bs->source = 0;
+
     bs->op = BOOT_STATUS_OP_MOVE;
     bs->idx = BOOT_STATUS_IDX_0;
     bs->state = BOOT_STATUS_STATE_0;
@@ -385,11 +395,11 @@
 
 #ifdef MCUBOOT_ENC_IMAGES
     if (MUST_DECRYPT(fap, image_index, hdr)) {
-        rc = boot_enc_load(BOOT_CURR_ENC(state), image_index, hdr, fap, bs->enckey[1]);
+        rc = boot_enc_load(BOOT_CURR_ENC(state), image_index, hdr, fap, bs);
         if (rc < 0) {
             return BOOT_EBADIMAGE;
         }
-        if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs->enckey[1])) {
+        if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs)) {
             return BOOT_EBADIMAGE;
         }
     }
@@ -781,12 +791,12 @@
     if (IS_ENCRYPTED(boot_img_hdr(state, BOOT_SECONDARY_SLOT))) {
         rc = boot_enc_load(BOOT_CURR_ENC(state), image_index,
                 boot_img_hdr(state, BOOT_SECONDARY_SLOT),
-                fap_secondary_slot, bs->enckey[1]);
+                fap_secondary_slot, bs);
 
         if (rc < 0) {
             return BOOT_EBADIMAGE;
         }
-        if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs->enckey[1])) {
+        if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs)) {
             return BOOT_EBADIMAGE;
         }
     }
@@ -870,11 +880,11 @@
 #ifdef MCUBOOT_ENC_IMAGES
         if (IS_ENCRYPTED(hdr)) {
             fap = BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT);
-            rc = boot_enc_load(BOOT_CURR_ENC(state), image_index, hdr, fap, bs->enckey[0]);
+            rc = boot_enc_load(BOOT_CURR_ENC(state), image_index, hdr, fap, bs);
             assert(rc >= 0);
 
             if (rc == 0) {
-                rc = boot_enc_set_key(BOOT_CURR_ENC(state), 0, bs->enckey[0]);
+                rc = boot_enc_set_key(BOOT_CURR_ENC(state), 0, bs);
                 assert(rc == 0);
             } else {
                 rc = 0;
@@ -894,11 +904,11 @@
         hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT);
         if (IS_ENCRYPTED(hdr)) {
             fap = BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT);
-            rc = boot_enc_load(BOOT_CURR_ENC(state), image_index, hdr, fap, bs->enckey[1]);
+            rc = boot_enc_load(BOOT_CURR_ENC(state), image_index, hdr, fap, bs);
             assert(rc >= 0);
 
             if (rc == 0) {
-                rc = boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs->enckey[1]);
+                rc = boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs);
                 assert(rc == 0);
             } else {
                 rc = 0;
@@ -924,8 +934,8 @@
         copy_size = bs->swap_size;
 
 #ifdef MCUBOOT_ENC_IMAGES
-        for (slot = 0; slot <= 1; slot++) {
-            rc = boot_read_enc_key(image_index, slot, bs->enckey[slot]);
+        for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
+            rc = boot_read_enc_key(image_index, slot, bs);
             assert(rc == 0);
 
             for (i = 0; i < BOOT_ENC_KEY_SIZE; i++) {
@@ -935,7 +945,7 @@
             }
 
             if (i != BOOT_ENC_KEY_SIZE) {
-                boot_enc_set_key(BOOT_CURR_ENC(state), slot, bs->enckey[slot]);
+                boot_enc_set_key(BOOT_CURR_ENC(state), slot, bs);
             }
         }
 #endif
diff --git a/boot/bootutil/src/swap_misc.c b/boot/bootutil/src/swap_misc.c
index 9db3299..31995b6 100644
--- a/boot/bootutil/src/swap_misc.c
+++ b/boot/bootutil/src/swap_misc.c
@@ -114,10 +114,10 @@
     assert(rc == 0);
 
 #ifdef MCUBOOT_ENC_IMAGES
-    rc = boot_write_enc_key(fap, 0, bs->enckey[0]);
+    rc = boot_write_enc_key(fap, 0, bs);
     assert(rc == 0);
 
-    rc = boot_write_enc_key(fap, 1, bs->enckey[1]);
+    rc = boot_write_enc_key(fap, 1, bs);
     assert(rc == 0);
 #endif
 
diff --git a/boot/bootutil/src/swap_scratch.c b/boot/bootutil/src/swap_scratch.c
index 8dc7ce0..ff93b41 100644
--- a/boot/bootutil/src/swap_scratch.c
+++ b/boot/bootutil/src/swap_scratch.c
@@ -629,10 +629,10 @@
             assert(rc == 0);
 
 #ifdef MCUBOOT_ENC_IMAGES
-            rc = boot_write_enc_key(fap_primary_slot, 0, bs->enckey[0]);
+            rc = boot_write_enc_key(fap_primary_slot, 0, bs);
             assert(rc == 0);
 
-            rc = boot_write_enc_key(fap_primary_slot, 1, bs->enckey[1]);
+            rc = boot_write_enc_key(fap_primary_slot, 1, bs);
             assert(rc == 0);
 #endif
             rc = boot_write_magic(fap_primary_slot);