Add bootutil support for encrypted images

This allows storing encrypted images in slot1, that are automatically
decrypted when copying to slot0 and re-encrypted when copying from slot0
to slot1.

The encryption works by applying AES-CTR-128 on the image blocks
(excluding the header and TLVs) using a random key. This random key
is itself encrypted using either RSA-OAEP-2048 or AES-KW-128 (AES keywrap
as defined by RFC3394), and appended to the image as newly defined TLVs.

AES-CTR-128 was chosen primarily for having stream cipher proporties,
which basically means that any block being encrypted/decrypted does not
depend on any other previous blocks results.

The TLV adds about 256 bytes to the image in RSA-OAEP-2048 mode, and 24
bytes in AES-KW-128 mode. Resulting sizes for a Mynewt generated mcuboot
(frdm-k64f):

- swap mode and no signing: 12KB
- adding encryption with RSA-OAEP-2048: 28KB
- adding encryption with AES-KW-128: 20KB

Some extra comments:

- AES-KW-128 requires a fairly new mbedtls with nist_kw support.
- An alternative methods which could be added later are ECIES.
- Key-wrapping seems easy enough to implement using just standard
  AES-ECB mode that it should be straight-forward to also add support to
  tinycrypt.

Signed-off-by: Fabio Utzig <utzig@apache.org>
diff --git a/boot/bootutil/include/bootutil/enc_key.h b/boot/bootutil/include/bootutil/enc_key.h
new file mode 100644
index 0000000..e685839
--- /dev/null
+++ b/boot/bootutil/include/bootutil/enc_key.h
@@ -0,0 +1,53 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#ifndef BOOTUTIL_ENC_KEY_H
+#define BOOTUTIL_ENC_KEY_H
+
+#include <flash_map_backend/flash_map_backend.h>
+#include "bootutil/image.h"
+#include "mbedtls/aes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define BOOT_ENC_KEY_SIZE       16
+#define BOOT_ENC_KEY_SIZE_BITS  (BOOT_ENC_KEY_SIZE * 8)
+
+struct enc_key_data {
+    uint8_t valid;
+    mbedtls_aes_context aes;
+};
+
+extern const struct bootutil_key bootutil_enc_key;
+
+int boot_enc_set_key(uint8_t slot, uint8_t *enckey);
+int boot_enc_load(const struct image_header *hdr, const struct flash_area *fap,
+        uint8_t *enckey);
+int boot_enc_valid(const struct flash_area *fap);
+void boot_encrypt(const struct flash_area *fap, uint32_t off, uint32_t sz,
+        uint32_t blk_off, uint8_t *buf);
+void boot_enc_zeroize(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BOOTUTIL_ENC_KEY_H */
diff --git a/boot/bootutil/include/bootutil/image.h b/boot/bootutil/include/bootutil/image.h
index 8f243c6..021a3a4 100644
--- a/boot/bootutil/include/bootutil/image.h
+++ b/boot/bootutil/include/bootutil/image.h
@@ -40,6 +40,7 @@
  */
 #define IMAGE_F_PIC                      0x00000001 /* Not supported. */
 #define IMAGE_F_NON_BOOTABLE             0x00000010 /* Split image app. */
+#define IMAGE_F_ENCRYPTED                0x00000004 /* Encrypted. */
 /*
  * Indicates that this image should be loaded into RAM instead of run
  * directly from flash.  The address to load should be in the
@@ -67,6 +68,8 @@
 #define IMAGE_TLV_RSA2048_PSS       0x20   /* RSA2048 of hash output */
 #define IMAGE_TLV_ECDSA224          0x21   /* ECDSA of hash output */
 #define IMAGE_TLV_ECDSA256          0x22   /* ECDSA of hash output */
+#define IMAGE_TLV_ENC_RSA2048       0x30   /* Key encrypted with RSA-OAEP-2048 */
+#define IMAGE_TLV_ENC_KW128         0x31   /* Key encrypted with AES-KW-128 */
 
 struct image_version {
     uint8_t iv_major;
diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c
index 8dcca2a..6f85ff1 100644
--- a/boot/bootutil/src/bootutil_misc.c
+++ b/boot/bootutil/src/bootutil_misc.c
@@ -33,6 +33,9 @@
 #include "bootutil/bootutil.h"
 #include "bootutil_priv.h"
 #include "bootutil/bootutil_log.h"
+#ifdef MCUBOOT_ENC_IMAGES
+#include "bootutil/enc_key.h"
+#endif
 
 int boot_current_slot;
 
@@ -122,15 +125,26 @@
 {
     return /* state for all sectors */
            BOOT_STATUS_MAX_ENTRIES * BOOT_STATUS_STATE_COUNT * min_write_sz +
-           BOOT_MAX_ALIGN * 3 /* copy_done + image_ok + swap_size */        +
+#ifdef MCUBOOT_ENC_IMAGES
+           /* encryption keys */
+           BOOT_ENC_KEY_SIZE * 2                  +
+#endif
+           /* copy_done + image_ok + swap_size */
+           BOOT_MAX_ALIGN * 3                     +
            BOOT_MAGIC_SZ;
 }
 
 static uint32_t
 boot_scratch_trailer_sz(uint8_t min_write_sz)
 {
-    return BOOT_STATUS_STATE_COUNT * min_write_sz +  /* state for one sector */
-           BOOT_MAX_ALIGN * 2                     +  /* image_ok + swap_size */
+           /* state for one sector */
+    return BOOT_STATUS_STATE_COUNT * min_write_sz +
+#ifdef MCUBOOT_ENC_IMAGES
+           /* encryption keys */
+           BOOT_ENC_KEY_SIZE * 2                  +
+#endif
+           /* image_ok + swap_size */
+           BOOT_MAX_ALIGN * 2                     +
            BOOT_MAGIC_SZ;
 }
 
@@ -202,6 +216,20 @@
     return fap->fa_size - BOOT_MAGIC_SZ - BOOT_MAX_ALIGN * 3;
 }
 
+#ifdef MCUBOOT_ENC_IMAGES
+static uint32_t
+boot_enc_key_off(const struct flash_area *fap, uint8_t slot)
+{
+    if (fap->fa_id == FLASH_AREA_IMAGE_SCRATCH) {
+        return fap->fa_size - BOOT_MAGIC_SZ - BOOT_MAX_ALIGN * 2 -
+                              ((slot + 1) * BOOT_ENC_KEY_SIZE);
+    }
+
+    return fap->fa_size - BOOT_MAGIC_SZ - BOOT_MAX_ALIGN * 3 -
+                          ((slot + 1) * BOOT_ENC_KEY_SIZE);
+}
+#endif
+
 int
 boot_read_swap_state(const struct flash_area *fap,
                      struct boot_swap_state *state)
@@ -337,6 +365,60 @@
     return rc;
 }
 
+#ifdef MCUBOOT_ENC_IMAGES
+int
+boot_read_enc_key(uint8_t slot, uint8_t *enckey)
+{
+    uint32_t magic[BOOT_MAGIC_SZ];
+    uint32_t off;
+    const struct flash_area *fap;
+    int rc;
+
+    rc = flash_area_open(FLASH_AREA_IMAGE_0, &fap);
+    if (rc != 0) {
+        return BOOT_EFLASH;
+    }
+
+    off = boot_magic_off(fap);
+    rc = flash_area_read(fap, off, magic, BOOT_MAGIC_SZ);
+    if (rc != 0) {
+        rc = BOOT_EFLASH;
+        goto out;
+    }
+
+    if (memcmp(magic, boot_img_magic, BOOT_MAGIC_SZ) != 0) {
+        /*
+         * If Slot 0 's magic is not valid, try scratch...
+         */
+
+        flash_area_close(fap);
+
+        rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &fap);
+        if (rc != 0) {
+            return BOOT_EFLASH;
+        }
+
+        off = boot_magic_off(fap);
+        rc = flash_area_read(fap, off, magic, BOOT_MAGIC_SZ);
+        if (rc != 0) {
+            rc = BOOT_EFLASH;
+            goto out;
+        }
+
+        assert(memcmp(magic, boot_img_magic, BOOT_MAGIC_SZ) == 0);
+    }
+
+    off = boot_enc_key_off(fap, slot);
+    rc = flash_area_read(fap, off, enckey, BOOT_ENC_KEY_SIZE);
+    if (rc != 0) {
+        rc = BOOT_EFLASH;
+    }
+
+out:
+    flash_area_close(fap);
+    return rc;
+}
+#endif
 
 int
 boot_write_magic(const struct flash_area *fap)
@@ -427,6 +509,23 @@
     return 0;
 }
 
+#ifdef MCUBOOT_ENC_IMAGES
+int
+boot_write_enc_key(const struct flash_area *fap, uint8_t slot, const uint8_t *enckey)
+{
+    uint32_t off;
+    int rc;
+
+    off = boot_enc_key_off(fap, slot);
+    rc = flash_area_write(fap, off, enckey, BOOT_ENC_KEY_SIZE);
+    if (rc != 0) {
+        return BOOT_EFLASH;
+    }
+
+    return 0;
+}
+#endif
+
 int
 boot_swap_type(void)
 {
diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h
index 8cb7533..163d5e9 100644
--- a/boot/bootutil/src/bootutil_priv.h
+++ b/boot/bootutil/src/bootutil_priv.h
@@ -27,6 +27,10 @@
 #include "bootutil/image.h"
 #include "mcuboot_config/mcuboot_config.h"
 
+#ifdef MCUBOOT_ENC_IMAGES
+#include "bootutil/enc_key.h"
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -57,6 +61,9 @@
     uint8_t state;        /* Which part of the swapping process are we at */
     uint8_t use_scratch;  /* Are status bytes ever written to scratch? */
     uint32_t swap_size;   /* Total size of swapped image */
+#ifdef MCUBOOT_ENC_IMAGES
+    uint8_t enckey[2][BOOT_ENC_KEY_SIZE];
+#endif
 };
 
 #define BOOT_MAGIC_GOOD     1
@@ -179,6 +186,11 @@
 int boot_write_image_ok(const struct flash_area *fap);
 int boot_write_swap_size(const struct flash_area *fap, uint32_t swap_size);
 int boot_read_swap_size(uint32_t *swap_size);
+#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(uint8_t slot, uint8_t *enckey);
+#endif
 
 /*
  * Accessors for the contents of struct boot_loader_state.
diff --git a/boot/bootutil/src/encrypted.c b/boot/bootutil/src/encrypted.c
new file mode 100644
index 0000000..5cf2603
--- /dev/null
+++ b/boot/bootutil/src/encrypted.c
@@ -0,0 +1,281 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#include "mcuboot_config/mcuboot_config.h"
+
+#if defined(MCUBOOT_ENC_IMAGES)
+#include <assert.h>
+#include <stddef.h>
+#include <inttypes.h>
+#include <string.h>
+
+#include "hal/hal_flash.h"
+
+#if defined(MCUBOOT_ENCRYPT_RSA)
+#include "mbedtls/rsa.h"
+#include "mbedtls/asn1.h"
+#endif
+
+#if defined(MCUBOOT_ENCRYPT_KW)
+#include "mbedtls/nist_kw.h"
+#endif
+
+#include "mbedtls/aes.h"
+
+#include "bootutil/image.h"
+#include "bootutil/enc_key.h"
+#include "bootutil/sign_key.h"
+
+#include "bootutil_priv.h"
+
+static struct enc_key_data enc_state[BOOT_NUM_SLOTS];
+
+#define TLV_ENC_RSA_SZ  256
+#define TLV_ENC_KW_SZ   24
+
+#if defined(MCUBOOT_ENCRYPT_RSA)
+static int
+parse_enckey(mbedtls_rsa_context *ctx, uint8_t **p, uint8_t *end)
+{
+    int rc;
+    size_t len;
+
+    if ((rc = mbedtls_asn1_get_tag(p, end, &len,
+                    MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)) != 0) {
+        return -1;
+    }
+
+    if (*p + len != end) {
+        return -2;
+    }
+
+    if ( /* version */
+        mbedtls_asn1_get_int(p, end, &ctx->ver) != 0 ||
+         /* public modulus */
+        mbedtls_asn1_get_mpi(p, end, &ctx->N) != 0 ||
+         /* public exponent */
+        mbedtls_asn1_get_mpi(p, end, &ctx->E) != 0 ||
+         /* private exponent */
+        mbedtls_asn1_get_mpi(p, end, &ctx->D) != 0 ||
+         /* primes */
+        mbedtls_asn1_get_mpi(p, end, &ctx->P) != 0 ||
+        mbedtls_asn1_get_mpi(p, end, &ctx->Q) != 0 ||
+         /* d mod (p-1) and d mod (q-1) */
+        mbedtls_asn1_get_mpi(p, end, &ctx->DP) != 0 ||
+        mbedtls_asn1_get_mpi(p, end, &ctx->DQ) != 0 ||
+         /* q ^ (-1) mod p */
+        mbedtls_asn1_get_mpi(p, end, &ctx->QP) != 0) {
+        return -3;
+    }
+
+    ctx->len = mbedtls_mpi_size(&ctx->N);
+
+    if (*p != end) {
+        return -4;
+    }
+
+    if (mbedtls_rsa_check_pubkey(ctx) != 0 ||
+        mbedtls_rsa_check_privkey(ctx) != 0) {
+        return -5;
+    }
+
+    return 0;
+}
+#endif
+
+int
+boot_enc_set_key(uint8_t slot, uint8_t *enckey)
+{
+    int rc;
+
+    mbedtls_aes_init(&enc_state[slot].aes);
+    rc = mbedtls_aes_setkey_enc(&enc_state[slot].aes, enckey, BOOT_ENC_KEY_SIZE_BITS);
+    if (rc) {
+        mbedtls_aes_free(&enc_state[slot].aes);
+        return -1;
+    }
+
+    enc_state[slot].valid = 1;
+
+    return 0;
+}
+
+#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
+#endif
+
+/*
+ * Load encryption key.
+ */
+int
+boot_enc_load(const struct image_header *hdr, const struct flash_area *fap,
+              uint8_t *enckey)
+{
+#if defined(MCUBOOT_ENCRYPT_RSA)
+    mbedtls_rsa_context rsa;
+    uint8_t *cp;
+    uint8_t *cpend;
+#endif
+#if defined(MCUBOOT_ENCRYPT_KW)
+    mbedtls_nist_kw_context kw;
+#endif
+    size_t olen;
+    uint32_t off;
+    uint32_t end;
+    struct image_tlv_info info;
+    struct image_tlv tlv;
+    uint8_t buf[TLV_ENC_RSA_SZ];
+    uint8_t slot;
+    uint8_t enckey_type;
+    int rc;
+
+    slot = fap->fa_id - FLASH_AREA_IMAGE_0;
+
+    /* Already loaded... */
+    if (enc_state[slot].valid) {
+        return 1;
+    }
+
+    off = hdr->ih_img_size + hdr->ih_hdr_size;
+
+    rc = flash_area_read(fap, off, &info, sizeof(info));
+    if (rc) {
+        return rc;
+    }
+    if (info.it_magic != IMAGE_TLV_INFO_MAGIC) {
+        return -1;
+    }
+    end = off + info.it_tlv_tot;
+    off += sizeof(info);
+
+    enckey_type = 0;
+    for (; off < end; off += sizeof(tlv) + tlv.it_len) {
+        rc = flash_area_read(fap, off, &tlv, sizeof tlv);
+        if (rc) {
+            return rc;
+        }
+
+        if (tlv.it_type == EXPECTED_ENC_TLV) {
+            if (tlv.it_len != EXPECTED_ENC_LEN) {
+                return -1;
+            }
+            rc = flash_area_read(fap, off + sizeof(tlv), buf, EXPECTED_ENC_LEN);
+            if (rc) {
+                return rc;
+            }
+            enckey_type = EXPECTED_ENC_TLV;
+            break;
+        }
+    }
+
+    if (enckey_type == 0) {
+        return -1;
+    }
+
+    if (enckey_type == EXPECTED_ENC_TLV) {
+#if defined(MCUBOOT_ENCRYPT_RSA)
+        mbedtls_rsa_init(&rsa, MBEDTLS_RSA_PKCS_V21, MBEDTLS_MD_SHA256);
+
+        cp = (uint8_t *)bootutil_enc_key.key;
+        cpend = cp + *bootutil_enc_key.len;
+
+        rc = parse_enckey(&rsa, &cp, cpend);
+        if (rc) {
+            mbedtls_rsa_free(&rsa);
+            goto done;
+        }
+
+        rc = mbedtls_rsa_rsaes_oaep_decrypt(&rsa, NULL, NULL, MBEDTLS_RSA_PRIVATE,
+                NULL, 0, &olen, buf, enckey, BOOT_ENC_KEY_SIZE);
+        mbedtls_rsa_free(&rsa);
+
+#elif defined(MCUBOOT_ENCRYPT_KW)
+        mbedtls_nist_kw_init(&kw);
+
+        assert(*bootutil_enc_key.len == 16);
+        rc = mbedtls_nist_kw_setkey(&kw, MBEDTLS_CIPHER_ID_AES,
+                bootutil_enc_key.key, *bootutil_enc_key.len * 8, 0);
+        if (rc) {
+            mbedtls_nist_kw_free(&kw);
+            goto done;
+        }
+
+        rc = mbedtls_nist_kw_unwrap(&kw, MBEDTLS_KW_MODE_KW, buf, TLV_ENC_KW_SZ,
+                enckey, &olen, BOOT_ENC_KEY_SIZE);
+
+        mbedtls_nist_kw_free(&kw);
+#endif
+    }
+
+done:
+    return rc;
+}
+
+int
+boot_enc_valid(const struct flash_area *fap)
+{
+    return enc_state[fap->fa_id - FLASH_AREA_IMAGE_0].valid;
+}
+
+void
+boot_encrypt(const struct flash_area *fap, uint32_t off, uint32_t sz,
+        uint32_t blk_off, uint8_t *buf)
+{
+    struct enc_key_data *enc;
+    uint32_t i, j;
+    uint8_t u8;
+    uint8_t nonce[16];
+    uint8_t blk[16];
+
+    memset(nonce, 0, 12);
+    off >>= 4;
+    nonce[12] = (uint8_t)(off >> 24);
+    nonce[13] = (uint8_t)(off >> 16);
+    nonce[14] = (uint8_t)(off >> 8);
+    nonce[15] = (uint8_t)off;
+
+    enc = &enc_state[fap->fa_id - FLASH_AREA_IMAGE_0];
+    assert(enc->valid == 1);
+    for (i = 0; i < sz; i++) {
+        if (i == 0 || blk_off == 0) {
+            mbedtls_aes_crypt_ecb(&enc->aes, MBEDTLS_AES_ENCRYPT, nonce, blk);
+
+            for (j = 16; j > 0; --j) {
+                if (++nonce[j - 1] != 0) {
+                    break;
+                }
+            }
+        }
+
+        u8 = *buf;
+        *buf++ = u8 ^ blk[blk_off];
+        blk_off = (blk_off + 1) & 0x0f;
+    }
+}
+
+void boot_enc_zeroize(void)
+{
+    memset(&enc_state, 0, sizeof(enc_state));
+}
+
+#endif /* MCUBOOT_ENC_IMAGES */
diff --git a/boot/bootutil/src/image_validate.c b/boot/bootutil/src/image_validate.c
index f9dcde3..e2e989c 100644
--- a/boot/bootutil/src/image_validate.c
+++ b/boot/bootutil/src/image_validate.c
@@ -32,7 +32,10 @@
 
 #include "mcuboot_config/mcuboot_config.h"
 
-#ifdef MCUBOOT_SIGN_RSA
+#ifdef MCUBOOT_ENC_IMAGES
+#include "bootutil/enc_key.h"
+#endif
+#if defined(MCUBOOT_SIGN_RSA)
 #include "mbedtls/rsa.h"
 #endif
 #if defined(MCUBOOT_SIGN_EC) || defined(MCUBOOT_SIGN_EC256)
@@ -55,6 +58,10 @@
     uint32_t size;
     uint32_t off;
     int rc;
+#ifdef MCUBOOT_ENC_IMAGES
+    uint32_t blk_off;
+    uint8_t idx;
+#endif
 
     bootutil_sha256_init(&sha256_ctx);
 
@@ -64,11 +71,21 @@
         bootutil_sha256_update(&sha256_ctx, seed, seed_len);
     }
 
+#ifdef MCUBOOT_ENC_IMAGES
+    /* Encrypted images only exist in slot1 */
+    if (fap->fa_id == FLASH_AREA_IMAGE_1 &&
+            (hdr->ih_flags & IMAGE_F_ENCRYPTED) &&
+            !boot_enc_valid(fap)) {
+        return -1;
+    }
+#endif
+
     /*
      * Hash is computed over image header and image itself. No TLV is
      * included ATM.
      */
     size = hdr->ih_img_size + hdr->ih_hdr_size;
+    assert(tmp_buf_sz > hdr->ih_hdr_size);
     for (off = 0; off < size; off += blk_sz) {
         blk_sz = size - off;
         if (blk_sz > tmp_buf_sz) {
@@ -78,6 +95,20 @@
         if (rc) {
             return rc;
         }
+#ifdef MCUBOOT_ENC_IMAGES
+        if (fap->fa_id == FLASH_AREA_IMAGE_1 && hdr->ih_flags & IMAGE_F_ENCRYPTED) {
+            /* FIXME: fails if header size is larger than blk_sz */
+            if (off < hdr->ih_hdr_size) {
+                idx = hdr->ih_hdr_size;
+                blk_off = 0;
+            } else {
+                idx = 0;
+                blk_off = (off - hdr->ih_hdr_size) & 0xf;
+            }
+            boot_encrypt(fap, (off + idx) - hdr->ih_hdr_size, blk_sz - idx,
+                    blk_off, &tmp_buf[idx]);
+        }
+#endif
         bootutil_sha256_update(&sha256_ctx, tmp_buf, blk_sz);
     }
     bootutil_sha256_finish(&sha256_ctx, hash_result);
@@ -154,8 +185,7 @@
     uint8_t hash[32];
     int rc;
 
-    rc = bootutil_img_hash(hdr, fap, tmp_buf, tmp_buf_sz, hash,
-                           seed, seed_len);
+    rc = bootutil_img_hash(hdr, fap, tmp_buf, tmp_buf_sz, hash, seed, seed_len);
     if (rc) {
         return rc;
     }
diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c
index f61a29f..e7c8d73 100644
--- a/boot/bootutil/src/loader.c
+++ b/boot/bootutil/src/loader.c
@@ -34,6 +34,10 @@
 #include "bootutil_priv.h"
 #include "bootutil/bootutil_log.h"
 
+#ifdef MCUBOOT_ENC_IMAGES
+#include "bootutil/enc_key.h"
+#endif
+
 #include "mcuboot_config/mcuboot_config.h"
 
 static struct boot_loader_state boot_data;
@@ -572,9 +576,26 @@
  * Validate image hash/signature in a slot.
  */
 static int
-boot_image_check(struct image_header *hdr, const struct flash_area *fap)
+boot_image_check(struct image_header *hdr, const struct flash_area *fap,
+        struct boot_status *bs)
 {
     static uint8_t tmpbuf[BOOT_TMPBUF_SZ];
+    int rc;
+
+#ifndef MCUBOOT_ENC_IMAGES
+    (void)bs;
+    (void)rc;
+#else
+    if (fap->fa_id == FLASH_AREA_IMAGE_1 && hdr->ih_flags & IMAGE_F_ENCRYPTED) {
+        rc = boot_enc_load(hdr, fap, bs->enckey[1]);
+        if (rc < 0) {
+            return BOOT_EBADIMAGE;
+        }
+        if (rc == 0 && boot_enc_set_key(1, bs->enckey[1])) {
+            return BOOT_EBADIMAGE;
+        }
+    }
+#endif
 
     if (bootutil_img_validate(hdr, fap, tmpbuf, BOOT_TMPBUF_SZ,
                               NULL, 0, NULL)) {
@@ -625,7 +646,7 @@
 }
 
 static int
-boot_validate_slot(int slot)
+boot_validate_slot(int slot, struct boot_status *bs)
 {
     const struct flash_area *fap;
     struct image_header *hdr;
@@ -643,7 +664,7 @@
         return -1;
     }
 
-    if ((hdr->ih_magic != IMAGE_MAGIC || boot_image_check(hdr, fap) != 0)) {
+    if ((hdr->ih_magic != IMAGE_MAGIC || boot_image_check(hdr, fap, bs) != 0)) {
         if (slot != 0) {
             flash_area_erase(fap, 0, fap->fa_size);
             /* Image in slot 1 is invalid. Erase the image and
@@ -669,7 +690,7 @@
  * @return                      The type of swap to perform (BOOT_SWAP_TYPE...)
  */
 static int
-boot_validated_swap_type(void)
+boot_validated_swap_type(struct boot_status *bs)
 {
     int swap_type;
 
@@ -679,7 +700,7 @@
     case BOOT_SWAP_TYPE_PERM:
     case BOOT_SWAP_TYPE_REVERT:
         /* Boot loader wants to switch to slot 1. Ensure image is valid. */
-        if (boot_validate_slot(1) != 0) {
+        if (boot_validate_slot(1, bs) != 0) {
             swap_type = BOOT_SWAP_TYPE_FAIL;
         }
     }
@@ -731,37 +752,17 @@
 /**
  * Erases a region of flash.
  *
- * @param flash_area_idx        The ID of the flash area containing the region
- *                                  to erase.
+ * @param flash_area           The flash_area containing the region to erase.
  * @param off                   The offset within the flash area to start the
  *                                  erase.
  * @param sz                    The number of bytes to erase.
  *
  * @return                      0 on success; nonzero on failure.
  */
-static int
-boot_erase_sector(int flash_area_id, uint32_t off, uint32_t sz)
+static inline int
+boot_erase_sector(const struct flash_area *fap, uint32_t off, uint32_t sz)
 {
-    const struct flash_area *fap;
-    int rc;
-
-    rc = flash_area_open(flash_area_id, &fap);
-    if (rc != 0) {
-        rc = BOOT_EFLASH;
-        goto done;
-    }
-
-    rc = flash_area_erase(fap, off, sz);
-    if (rc != 0) {
-        rc = BOOT_EFLASH;
-        goto done;
-    }
-
-    rc = 0;
-
-done:
-    flash_area_close(fap);
-    return rc;
+    return flash_area_erase(fap, off, sz);
 }
 
 /**
@@ -779,32 +780,23 @@
  * @return                      0 on success; nonzero on failure.
  */
 static int
-boot_copy_sector(int flash_area_id_src, int flash_area_id_dst,
+boot_copy_sector(const struct flash_area *fap_src,
+                 const struct flash_area *fap_dst,
                  uint32_t off_src, uint32_t off_dst, uint32_t sz)
 {
-    const struct flash_area *fap_src;
-    const struct flash_area *fap_dst;
     uint32_t bytes_copied;
     int chunk_sz;
     int rc;
+#ifdef MCUBOOT_ENC_IMAGES
+    uint32_t off;
+    size_t blk_off;
+    struct image_header *hdr;
+    uint16_t idx;
+    uint32_t blk_sz;
+#endif
 
     static uint8_t buf[1024];
 
-    fap_src = NULL;
-    fap_dst = NULL;
-
-    rc = flash_area_open(flash_area_id_src, &fap_src);
-    if (rc != 0) {
-        rc = BOOT_EFLASH;
-        goto done;
-    }
-
-    rc = flash_area_open(flash_area_id_dst, &fap_dst);
-    if (rc != 0) {
-        rc = BOOT_EFLASH;
-        goto done;
-    }
-
     bytes_copied = 0;
     while (bytes_copied < sz) {
         if (sz - bytes_copied > sizeof buf) {
@@ -815,42 +807,63 @@
 
         rc = flash_area_read(fap_src, off_src + bytes_copied, buf, chunk_sz);
         if (rc != 0) {
-            rc = BOOT_EFLASH;
-            goto done;
+            return BOOT_EFLASH;
         }
 
+#ifdef MCUBOOT_ENC_IMAGES
+        if (fap_src->fa_id == FLASH_AREA_IMAGE_1 ||
+                fap_dst->fa_id == FLASH_AREA_IMAGE_1) {
+            /* assume slot1 as src, needs decryption */
+            hdr = boot_img_hdr(&boot_data, 1);
+            off = off_src;
+            if (fap_dst->fa_id == FLASH_AREA_IMAGE_1) {
+                /* might need encryption (metadata from slot0) */
+                hdr = boot_img_hdr(&boot_data, 0);
+                off = off_dst;
+            }
+            if (hdr->ih_flags & IMAGE_F_ENCRYPTED) {
+                blk_sz = chunk_sz;
+                idx = 0;
+                if (off + bytes_copied < hdr->ih_hdr_size) {
+                    /* do not decrypt header */
+                    blk_off = 0;
+                    blk_sz = chunk_sz - hdr->ih_hdr_size;
+                    idx = hdr->ih_hdr_size;
+                } else {
+                    blk_off = ((off + bytes_copied) - hdr->ih_hdr_size) & 0xf;
+                }
+                if (off + bytes_copied + chunk_sz > hdr->ih_hdr_size + hdr->ih_img_size) {
+                    /* do not decrypt TLVs */
+                    if (off + bytes_copied >= hdr->ih_hdr_size + hdr->ih_img_size) {
+                        blk_sz = 0;
+                    } else {
+                        blk_sz = (hdr->ih_hdr_size + hdr->ih_img_size) - (off + bytes_copied);
+                    }
+                }
+                boot_encrypt(fap_src, (off + bytes_copied + idx) - hdr->ih_hdr_size,
+                        blk_sz, blk_off, &buf[idx]);
+            }
+        }
+#endif
+
         rc = flash_area_write(fap_dst, off_dst + bytes_copied, buf, chunk_sz);
         if (rc != 0) {
-            rc = BOOT_EFLASH;
-            goto done;
+            return BOOT_EFLASH;
         }
 
         bytes_copied += chunk_sz;
     }
 
-    rc = 0;
-
-done:
-    if (fap_src) {
-        flash_area_close(fap_src);
-    }
-    if (fap_dst) {
-        flash_area_close(fap_dst);
-    }
-    return rc;
+    return 0;
 }
 
 #ifndef MCUBOOT_OVERWRITE_ONLY
 static inline int
-boot_status_init_by_id(int flash_area_id, const struct boot_status *bs)
+boot_status_init(const struct flash_area *fap, const struct boot_status *bs)
 {
-    const struct flash_area *fap;
     struct boot_swap_state swap_state;
     int rc;
 
-    rc = flash_area_open(flash_area_id, &fap);
-    assert(rc == 0);
-
     rc = boot_read_swap_state_by_id(FLASH_AREA_IMAGE_1, &swap_state);
     assert(rc == 0);
 
@@ -862,10 +875,16 @@
     rc = boot_write_swap_size(fap, bs->swap_size);
     assert(rc == 0);
 
-    rc = boot_write_magic(fap);
+#ifdef MCUBOOT_ENC_IMAGES
+    rc = boot_write_enc_key(fap, 0, bs->enckey[0]);
     assert(rc == 0);
 
-    flash_area_close(fap);
+    rc = boot_write_enc_key(fap, 1, bs->enckey[1]);
+    assert(rc == 0);
+#endif
+
+    rc = boot_write_magic(fap);
+    assert(rc == 0);
 
     return 0;
 }
@@ -873,13 +892,13 @@
 
 #ifndef MCUBOOT_OVERWRITE_ONLY
 static int
-boot_erase_last_sector_by_id(int flash_area_id)
+boot_erase_last_sector(const struct flash_area *fap)
 {
     uint8_t slot;
     uint32_t last_sector;
     int rc;
 
-    switch (flash_area_id) {
+    switch (fap->fa_id) {
     case FLASH_AREA_IMAGE_0:
         slot = 0;
         break;
@@ -891,7 +910,7 @@
     }
 
     last_sector = boot_img_num_sectors(&boot_data, slot) - 1;
-    rc = boot_erase_sector(flash_area_id,
+    rc = boot_erase_sector(fap,
             boot_img_sector_off(&boot_data, slot, last_sector),
             boot_img_sector_size(&boot_data, slot, last_sector));
     assert(rc == 0);
@@ -915,7 +934,9 @@
 static void
 boot_swap_sectors(int idx, uint32_t sz, struct boot_status *bs)
 {
-    const struct flash_area *fap;
+    const struct flash_area *fap_slot0;
+    const struct flash_area *fap_slot1;
+    const struct flash_area *fap_scratch;
     uint32_t copy_sz;
     uint32_t trailer_sz;
     uint32_t img_off;
@@ -946,26 +967,34 @@
 
     bs->use_scratch = (bs->idx == BOOT_STATUS_IDX_0 && copy_sz != sz);
 
+    rc = flash_area_open(FLASH_AREA_IMAGE_0, &fap_slot0);
+    assert (rc == 0);
+
+    rc = flash_area_open(FLASH_AREA_IMAGE_1, &fap_slot1);
+    assert (rc == 0);
+
+    rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &fap_scratch);
+    assert (rc == 0);
+
     if (bs->state == BOOT_STATUS_STATE_0) {
-        rc = boot_erase_sector(FLASH_AREA_IMAGE_SCRATCH, 0, sz);
+        rc = boot_erase_sector(fap_scratch, 0, sz);
         assert(rc == 0);
 
-        rc = boot_copy_sector(FLASH_AREA_IMAGE_1, FLASH_AREA_IMAGE_SCRATCH,
-                              img_off, 0, copy_sz);
+        rc = boot_copy_sector(fap_slot1, fap_scratch, img_off, 0, copy_sz);
         assert(rc == 0);
 
         if (bs->idx == BOOT_STATUS_IDX_0) {
             if (bs->use_scratch) {
-                boot_status_init_by_id(FLASH_AREA_IMAGE_SCRATCH, bs);
+                boot_status_init(fap_scratch, bs);
             } else {
                 /* Prepare the status area... here it is known that the
                  * last sector is not being used by the image data so it's
                  * safe to erase.
                  */
-                rc = boot_erase_last_sector_by_id(FLASH_AREA_IMAGE_0);
+                rc = boot_erase_last_sector(fap_slot0);
                 assert(rc == 0);
 
-                boot_status_init_by_id(FLASH_AREA_IMAGE_0, bs);
+                boot_status_init(fap_slot0, bs);
             }
         }
 
@@ -975,18 +1004,17 @@
     }
 
     if (bs->state == BOOT_STATUS_STATE_1) {
-        rc = boot_erase_sector(FLASH_AREA_IMAGE_1, img_off, sz);
+        rc = boot_erase_sector(fap_slot1, img_off, sz);
         assert(rc == 0);
 
-        rc = boot_copy_sector(FLASH_AREA_IMAGE_0, FLASH_AREA_IMAGE_1,
-                              img_off, img_off, copy_sz);
+        rc = boot_copy_sector(fap_slot0, fap_slot1, img_off, img_off, copy_sz);
         assert(rc == 0);
 
         if (bs->idx == BOOT_STATUS_IDX_0 && !bs->use_scratch) {
             /* If not all sectors of the slot are being swapped,
              * guarantee here that only slot0 will have the state.
              */
-            rc = boot_erase_last_sector_by_id(FLASH_AREA_IMAGE_1);
+            rc = boot_erase_last_sector(fap_slot1);
             assert(rc == 0);
         }
 
@@ -996,28 +1024,18 @@
     }
 
     if (bs->state == BOOT_STATUS_STATE_2) {
-        rc = boot_erase_sector(FLASH_AREA_IMAGE_0, img_off, sz);
+        rc = boot_erase_sector(fap_slot0, img_off, sz);
         assert(rc == 0);
 
         /* NOTE: also copy trailer from scratch (has status info) */
-        rc = boot_copy_sector(FLASH_AREA_IMAGE_SCRATCH, FLASH_AREA_IMAGE_0,
-                              0, img_off, copy_sz);
+        rc = boot_copy_sector(fap_scratch, fap_slot0, 0, img_off, copy_sz);
         assert(rc == 0);
 
         if (bs->use_scratch) {
-            rc = flash_area_open(FLASH_AREA_IMAGE_SCRATCH, &fap);
-            assert(rc == 0);
-
-            scratch_trailer_off = boot_status_off(fap);
-
-            flash_area_close(fap);
-
-            rc = flash_area_open(FLASH_AREA_IMAGE_0, &fap);
-            assert(rc == 0);
+            scratch_trailer_off = boot_status_off(fap_scratch);
 
             /* copy current status that is being maintained in scratch */
-            rc = boot_copy_sector(FLASH_AREA_IMAGE_SCRATCH, FLASH_AREA_IMAGE_0,
-                        scratch_trailer_off,
+            rc = boot_copy_sector(fap_scratch, fap_slot0, scratch_trailer_off,
                         img_off + copy_sz,
                         BOOT_STATUS_STATE_COUNT * BOOT_WRITE_SZ(&boot_data));
             BOOT_STATUS_ASSERT(rc == 0);
@@ -1027,17 +1045,23 @@
             assert(rc == 0);
 
             if (swap_state.image_ok == BOOT_FLAG_SET) {
-                rc = boot_write_image_ok(fap);
+                rc = boot_write_image_ok(fap_slot0);
                 assert(rc == 0);
             }
 
-            rc = boot_write_swap_size(fap, bs->swap_size);
+            rc = boot_write_swap_size(fap_slot0, bs->swap_size);
             assert(rc == 0);
 
-            rc = boot_write_magic(fap);
+#ifdef MCUBOOT_ENC_IMAGES
+            rc = boot_write_enc_key(fap_slot0, 0, bs->enckey[0]);
             assert(rc == 0);
 
-            flash_area_close(fap);
+            rc = boot_write_enc_key(fap_slot0, 1, bs->enckey[1]);
+            assert(rc == 0);
+#endif
+
+            rc = boot_write_magic(fap_slot0);
+            assert(rc == 0);
         }
 
         bs->idx++;
@@ -1046,12 +1070,17 @@
         rc = boot_write_status(bs);
         BOOT_STATUS_ASSERT(rc == 0);
     }
+
+    flash_area_close(fap_slot0);
+    flash_area_close(fap_slot1);
+    flash_area_close(fap_scratch);
 }
 #endif /* !MCUBOOT_OVERWRITE_ONLY */
 
 /**
- * Swaps the two images in flash.  If a prior copy operation was interrupted
- * by a system reset, this function completes that operation.
+ * Overwrite slot 0 with the image contained in slot 1.  If a prior copy
+ * operation was interrupted by a system reset, this function redos the
+ * copy.
  *
  * @param bs                    The current boot status.  This function reads
  *                                  this struct to determine if it is resuming
@@ -1071,6 +1100,11 @@
     size_t size;
     size_t this_size;
     size_t last_sector;
+    const struct flash_area *fap_slot0;
+    const struct flash_area *fap_slot1;
+#ifdef MCUBOOT_ENC_IMAGES
+    uint8_t enckey[BOOT_ENC_KEY_SIZE];
+#endif
 
     (void)bs;
 
@@ -1083,12 +1117,16 @@
     BOOT_LOG_INF("Image upgrade slot1 -> slot0");
     BOOT_LOG_INF("Erasing slot0");
 
+    rc = flash_area_open(FLASH_AREA_IMAGE_0, &fap_slot0);
+    assert (rc == 0);
+
+    rc = flash_area_open(FLASH_AREA_IMAGE_1, &fap_slot1);
+    assert (rc == 0);
+
     sect_count = boot_img_num_sectors(&boot_data, 0);
     for (sect = 0, size = 0; sect < sect_count; sect++) {
         this_size = boot_img_sector_size(&boot_data, 0, sect);
-        rc = boot_erase_sector(FLASH_AREA_IMAGE_0,
-                               size,
-                               this_size);
+        rc = boot_erase_sector(fap_slot0, size, this_size);
         assert(rc == 0);
 
         size += this_size;
@@ -1100,30 +1138,58 @@
 #endif
     }
 
+#ifdef MCUBOOT_ENC_IMAGES
+    if (boot_img_hdr(&boot_data, 1)->ih_flags & IMAGE_F_ENCRYPTED) {
+        rc = boot_enc_load(boot_img_hdr(&boot_data, 1), fap_slot1, enckey);
+        if (rc < 0) {
+            return BOOT_EBADIMAGE;
+        }
+        if (rc == 0 && boot_enc_set_key(1, enckey)) {
+            return BOOT_EBADIMAGE;
+        }
+    }
+#endif
+
     BOOT_LOG_INF("Copying slot 1 to slot 0: 0x%lx bytes", size);
-    rc = boot_copy_sector(FLASH_AREA_IMAGE_1, FLASH_AREA_IMAGE_0,
-                          0, 0, size);
+    rc = boot_copy_sector(fap_slot1, fap_slot0, 0, 0, size);
 
     /*
      * Erases header and trailer. The trailer is erased because when a new
      * image is written without a trailer as is the case when using newt, the
      * trailer that was left might trigger a new upgrade.
      */
-    rc = boot_erase_sector(FLASH_AREA_IMAGE_1,
+    rc = boot_erase_sector(fap_slot1,
                            boot_img_sector_off(&boot_data, 1, 0),
                            boot_img_sector_size(&boot_data, 1, 0));
     assert(rc == 0);
     last_sector = boot_img_num_sectors(&boot_data, 1) - 1;
-    rc = boot_erase_sector(FLASH_AREA_IMAGE_1,
+    rc = boot_erase_sector(fap_slot1,
                            boot_img_sector_off(&boot_data, 1, last_sector),
                            boot_img_sector_size(&boot_data, 1, last_sector));
     assert(rc == 0);
 
+    flash_area_close(fap_slot0);
+    flash_area_close(fap_slot1);
+
     /* TODO: Perhaps verify slot 0's signature again? */
 
     return 0;
 }
+
 #else
+
+/**
+ * Swaps the two images in flash.  If a prior copy operation was interrupted
+ * by a system reset, this function completes that operation.
+ *
+ * @param bs                    The current boot status.  This function reads
+ *                                  this struct to determine if it is resuming
+ *                                  an interrupted swap operation.  This
+ *                                  function writes the updated status to this
+ *                                  function on return.
+ *
+ * @return                      0 on success; nonzero on failure.
+ */
 static int
 boot_copy_image(struct boot_status *bs)
 {
@@ -1132,6 +1198,12 @@
     int last_sector_idx;
     uint32_t swap_idx;
     struct image_header *hdr;
+#ifdef MCUBOOT_ENC_IMAGES
+    const struct flash_area *fap;
+    uint8_t slot;
+    uint8_t i;
+    uint8_t enckey[BOOT_ENC_KEY_SIZE];
+#endif
     uint32_t size;
     uint32_t copy_size;
     int rc;
@@ -1151,17 +1223,53 @@
             assert(rc == 0);
         }
 
+#ifdef MCUBOOT_ENC_IMAGES
+        if (hdr->ih_flags & IMAGE_F_ENCRYPTED) {
+            fap = BOOT_IMG_AREA(&boot_data, 0);
+            rc = boot_enc_load(hdr, fap, bs->enckey[0]);
+            assert(rc >= 0);
+
+            if (rc == 0) {
+                rc = boot_enc_set_key(0, bs->enckey[0]);
+                assert(rc == 0);
+            } else {
+                rc = 0;
+            }
+        } else {
+            memset(bs->enckey[0], 0xff, BOOT_ENC_KEY_SIZE);
+        }
+#endif
+
         hdr = boot_img_hdr(&boot_data, 1);
         if (hdr->ih_magic == IMAGE_MAGIC) {
             rc = boot_read_image_size(1, hdr, &size);
             assert(rc == 0);
         }
 
+#ifdef MCUBOOT_ENC_IMAGES
+        hdr = boot_img_hdr(&boot_data, 1);
+        if (hdr->ih_flags & IMAGE_F_ENCRYPTED) {
+            fap = BOOT_IMG_AREA(&boot_data, 1);
+            rc = boot_enc_load(hdr, fap, bs->enckey[1]);
+            assert(rc >= 0);
+
+            if (rc == 0) {
+                rc = boot_enc_set_key(1, bs->enckey[1]);
+                assert(rc == 0);
+            } else {
+                rc = 0;
+            }
+        } else {
+            memset(bs->enckey[1], 0xff, BOOT_ENC_KEY_SIZE);
+        }
+#endif
+
         if (size > copy_size) {
             copy_size = size;
         }
 
         bs->swap_size = copy_size;
+
     } else {
         /*
          * If a swap was under way, the swap_size should already be present
@@ -1171,6 +1279,23 @@
         assert(rc == 0);
 
         copy_size = bs->swap_size;
+
+#ifdef MCUBOOT_ENC_IMAGES
+        for (slot = 0; slot <= 1; slot++) {
+            rc = boot_read_enc_key(slot, bs->enckey[slot]);
+            assert(rc == 0);
+
+            for (i = 0; i < BOOT_ENC_KEY_SIZE; i++) {
+                if (enckey[i] != 0xff) {
+                    break;
+                }
+            }
+
+            if (i != BOOT_ENC_KEY_SIZE) {
+                boot_enc_set_key(slot, bs->enckey[slot]);
+            }
+        }
+#endif
     }
 
     size = 0;
@@ -1303,7 +1428,7 @@
          */
         swap_type = boot_previous_swap_type();
     } else {
-        swap_type = boot_validated_swap_type();
+        swap_type = boot_validated_swap_type(&bs);
         switch (swap_type) {
         case BOOT_SWAP_TYPE_TEST:
         case BOOT_SWAP_TYPE_PERM:
@@ -1345,6 +1470,11 @@
     boot_data.imgs[0].sectors = slot0_sectors;
     boot_data.imgs[1].sectors = slot1_sectors;
 
+#ifdef MCUBOOT_ENC_IMAGES
+    /* FIXME: remove this after RAM is cleared by sim */
+    boot_enc_zeroize();
+#endif
+
     /* Open boot_data image areas for the duration of this call. */
     for (slot = 0; slot < BOOT_NUM_SLOTS; slot++) {
         fa_id = flash_area_id_from_image_slot(slot);
@@ -1448,7 +1578,7 @@
     }
 
 #ifdef MCUBOOT_VALIDATE_SLOT0
-    rc = boot_validate_slot(0);
+    rc = boot_validate_slot(0, NULL);
     ASSERT(rc == 0);
     if (rc != 0) {
         rc = BOOT_EBADIMAGE;