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/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 */