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;