boot: boot_serial: Fix issue with encrypted second slot images
Fixes issues whereby encrypted images were not properly listed due
to not treating them as encrypted, also removes a piece of wrong
hack code that would never run as the primary slot cannot be
encrypted.
Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
diff --git a/boot/boot_serial/include/boot_serial/boot_serial_encryption.h b/boot/boot_serial/include/boot_serial/boot_serial_encryption.h
new file mode 100644
index 0000000..b7cf9ff
--- /dev/null
+++ b/boot/boot_serial/include/boot_serial/boot_serial_encryption.h
@@ -0,0 +1,32 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Copyright (c) 2023 Nordic Semiconductor ASA
+ */
+
+#ifndef H_BOOT_SERIAL_ENCRYPTION_
+#define H_BOOT_SERIAL_ENCRYPTION_
+#include "bootutil/fault_injection_hardening.h"
+
+/**
+ * Validate hash of a primary boot image doing on the fly decryption as well
+ *
+ * @param[in] fa_p flash area pointer
+ * @param[in] hdr boot image header pointer
+ * @param[in] buf buffer which is used for validating data
+ * @param[in] buf_size size of input buffer
+ *
+ * @return FIH_SUCCESS on success, error code otherwise
+ */
+fih_ret
+boot_image_validate_encrypted(const struct flash_area *fa_p,
+ struct image_header *hdr, uint8_t *buf,
+ uint16_t buf_size);
+
+/**
+ * Handle an encrypted firmware in the main flash.
+ * This will decrypt the image inplace
+ */
+int boot_handle_enc_fw(const struct flash_area *flash_area);
+
+#endif
diff --git a/boot/boot_serial/src/boot_serial.c b/boot/boot_serial/src/boot_serial.c
index 7f6af91..cddf8e2 100644
--- a/boot/boot_serial/src/boot_serial.c
+++ b/boot/boot_serial/src/boot_serial.c
@@ -73,7 +73,7 @@
#endif
#ifdef MCUBOOT_ENC_IMAGES
-#include "single_loader.h"
+#include "boot_serial/boot_serial_encryption.h"
#endif
#include "bootutil/boot_hooks.h"
@@ -293,18 +293,16 @@
if (FIH_EQ(fih_rc, FIH_BOOT_HOOK_REGULAR))
{
#ifdef MCUBOOT_ENC_IMAGES
- if (slot == 0 && IS_ENCRYPTED(&hdr)) {
- /* Clear the encrypted flag we didn't supply a key
- * This flag could be set if there was a decryption in place
- * performed before. We will try to validate the image without
- * decryption by clearing the flag in the heder. If
- * still encrypted the validation will fail.
- */
- hdr.ih_flags &= ~(ENCRYPTIONFLAGS);
+ if (IS_ENCRYPTED(&hdr)) {
+ FIH_CALL(boot_image_validate_encrypted, fih_rc, fap,
+ &hdr, tmpbuf, sizeof(tmpbuf));
+ } else {
+#endif
+ FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, &hdr,
+ fap, tmpbuf, sizeof(tmpbuf), NULL, 0, NULL);
+#ifdef MCUBOOT_ENC_IMAGES
}
#endif
- FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, &hdr, fap, tmpbuf, sizeof(tmpbuf),
- NULL, 0, NULL);
}
}
@@ -483,8 +481,17 @@
fih_rc, image_index, 1);
if (FIH_EQ(fih_rc, FIH_BOOT_HOOK_REGULAR))
{
- FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, &hdr, fap,
- tmpbuf, sizeof(tmpbuf), NULL, 0, NULL);
+#ifdef MCUBOOT_ENC_IMAGES
+ if (IS_ENCRYPTED(&hdr)) {
+ FIH_CALL(boot_image_validate_encrypted, fih_rc, fap,
+ &hdr, tmpbuf, sizeof(tmpbuf));
+ } else {
+#endif
+ FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, &hdr,
+ fap, tmpbuf, sizeof(tmpbuf), NULL, 0, NULL);
+#ifdef MCUBOOT_ENC_IMAGES
+ }
+#endif
}
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
@@ -862,14 +869,23 @@
zcbor_map_end_encode(cbor_state, 10);
boot_serial_output();
- flash_area_close(fap);
#ifdef MCUBOOT_ENC_IMAGES
- if (curr_off == img_size) {
- /* Last sector received, now start a decryption on the image if it is encrypted*/
- rc = boot_handle_enc_fw();
+ /* Check if this upload was for the primary slot */
+#if !defined(MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD)
+ if (flash_area_id_from_multi_image_slot(img_num, 0) == FLASH_AREA_IMAGE_PRIMARY(0))
+#else
+ if (flash_area_id_from_direct_image(img_num) == FLASH_AREA_IMAGE_PRIMARY(0))
+#endif
+ {
+ if (curr_off == img_size) {
+ /* Last sector received, now start a decryption on the image if it is encrypted */
+ rc = boot_handle_enc_fw(fap);
+ }
}
-#endif //#ifdef MCUBOOT_ENC_IMAGES
+#endif
+
+ flash_area_close(fap);
}
#ifdef MCUBOOT_BOOT_MGMT_ECHO
diff --git a/boot/boot_serial/src/boot_serial_encryption.c b/boot/boot_serial/src/boot_serial_encryption.c
new file mode 100644
index 0000000..6201e6b
--- /dev/null
+++ b/boot/boot_serial/src/boot_serial_encryption.c
@@ -0,0 +1,315 @@
+/*
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Copyright (c) 2020-2023 Nordic Semiconductor ASA
+ * Copyright (c) 2020 Arm Limited
+ */
+
+#include <assert.h>
+#include "bootutil/image.h"
+#include <../src/bootutil_priv.h>
+#include "bootutil/bootutil_log.h"
+#include "bootutil/bootutil_public.h"
+#include "bootutil/fault_injection_hardening.h"
+#include "bootutil/enc_key.h"
+
+#include "mcuboot_config/mcuboot_config.h"
+
+#ifdef MCUBOOT_ENC_IMAGES
+
+BOOT_LOG_MODULE_DECLARE(serial_encryption);
+
+fih_ret
+boot_image_validate_encrypted(const struct flash_area *fa_p,
+ struct image_header *hdr, uint8_t *buf,
+ uint16_t buf_size)
+{
+ FIH_DECLARE(fih_rc, FIH_FAILURE);
+
+ struct boot_loader_state boot_data;
+ struct boot_loader_state *state = &boot_data;
+ struct boot_status _bs;
+ struct boot_status *bs = &_bs;
+ uint8_t image_index;
+ int rc;
+
+ memset(&boot_data, 0, sizeof(struct boot_loader_state));
+ image_index = BOOT_CURR_IMG(state);
+ if(IS_ENCRYPTED(hdr)) {
+ rc = boot_enc_load(BOOT_CURR_ENC(state), image_index, hdr, fa_p, bs);
+ if (rc < 0) {
+ FIH_RET(fih_rc);
+ }
+ rc = flash_area_id_to_multi_image_slot(image_index, flash_area_get_id(fa_p));
+ if (rc < 0) {
+ FIH_RET(fih_rc);
+ }
+ rc = boot_enc_set_key(BOOT_CURR_ENC(state), 1, bs);
+ if (rc < 0) {
+ FIH_RET(fih_rc);
+ }
+ }
+ FIH_CALL(bootutil_img_validate, fih_rc, BOOT_CURR_ENC(state), image_index,
+ hdr, fa_p, buf, buf_size, NULL, 0, NULL);
+
+ FIH_RET(fih_rc);
+}
+
+/*
+ * Compute the total size of the given image. Includes the size of
+ * the TLVs.
+ */
+static int
+read_image_size(const struct flash_area *fa_p,
+ struct image_header *hdr,
+ uint32_t *size)
+{
+ struct image_tlv_info info;
+ uint32_t off;
+ uint32_t protect_tlv_size;
+ int rc;
+
+ off = BOOT_TLV_OFF(hdr);
+
+ if (flash_area_read(fa_p, off, &info, sizeof(info))) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+
+ protect_tlv_size = hdr->ih_protect_tlv_size;
+ if (info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC) {
+ if (protect_tlv_size != info.it_tlv_tot) {
+ rc = BOOT_EBADIMAGE;
+ goto done;
+ }
+
+ if (flash_area_read(fa_p, off + info.it_tlv_tot, &info, sizeof(info))) {
+ rc = BOOT_EFLASH;
+ goto done;
+ }
+ } else if (protect_tlv_size != 0) {
+ rc = BOOT_EBADIMAGE;
+ goto done;
+ }
+
+ if (info.it_magic != IMAGE_TLV_INFO_MAGIC) {
+ rc = BOOT_EBADIMAGE;
+ goto done;
+ }
+
+ *size = off + protect_tlv_size + info.it_tlv_tot;
+ rc = 0;
+
+done:
+ return rc;
+}
+
+/**
+ * reads, decrypts in RAM & write back the decrypted image in the same region
+ * This function is NOT power failsafe since the image is decrypted in the RAM
+ * buffer.
+ *
+ * @param flash_area The ID of the source flash area.
+ * @param off_src The offset within the flash area to
+ * copy from.
+ * @param sz The number of bytes to copy. should match erase sector
+ *
+ * @return 0 on success; nonzero on failure.
+ */
+static int
+decrypt_region_inplace(struct boot_loader_state *state,
+ const struct flash_area *fap,
+ struct image_header *hdr,
+ uint32_t off, uint32_t sz)
+{
+ uint32_t bytes_copied;
+ int chunk_sz;
+ int rc;
+ uint32_t tlv_off;
+ size_t blk_off;
+ uint16_t idx;
+ uint32_t blk_sz;
+ uint8_t image_index;
+
+ uint8_t buf[sz] __attribute__((aligned));
+ assert(sz <= sizeof buf);
+
+ bytes_copied = 0;
+ while (bytes_copied < sz) {
+ if (sz - bytes_copied > sizeof buf) {
+ chunk_sz = sizeof buf;
+ } else {
+ chunk_sz = sz - bytes_copied;
+ }
+
+ rc = flash_area_read(fap, off + bytes_copied, buf, chunk_sz);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ image_index = BOOT_CURR_IMG(state);
+ if (IS_ENCRYPTED(hdr)) {
+ blk_sz = chunk_sz;
+ idx = 0;
+ if (off + bytes_copied < hdr->ih_hdr_size) {
+ /* do not decrypt header */
+ if (hdr->ih_hdr_size > (off + bytes_copied + chunk_sz)) {
+ /* all bytes in header, skip decryption */
+ blk_sz = 0;
+ }
+ else {
+ blk_sz = off + bytes_copied + chunk_sz - hdr->ih_hdr_size;
+ }
+
+ blk_off = 0;
+ idx = hdr->ih_hdr_size;
+ } else {
+ blk_off = ((off + bytes_copied) - hdr->ih_hdr_size) & 0xf;
+ }
+ tlv_off = BOOT_TLV_OFF(hdr);
+ if (off + bytes_copied + chunk_sz > tlv_off) {
+ /* do not decrypt TLVs */
+ if (off + bytes_copied >= tlv_off) {
+ blk_sz = 0;
+ } else {
+ blk_sz = tlv_off - (off + bytes_copied);
+ }
+ }
+ boot_encrypt(BOOT_CURR_ENC(state), image_index, fap,
+ (off + bytes_copied + idx) - hdr->ih_hdr_size, blk_sz,
+ blk_off, &buf[idx]);
+ }
+ rc = flash_area_erase(fap, off + bytes_copied, chunk_sz);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+ rc = flash_area_write(fap, off + bytes_copied, buf, chunk_sz);
+ if (rc != 0) {
+ return BOOT_EFLASH;
+ }
+
+ bytes_copied += chunk_sz;
+
+ MCUBOOT_WATCHDOG_FEED();
+ }
+
+ return 0;
+}
+
+/**
+ * Check if a image was encrypted into the first slot, and decrypt it
+ * in place. this operation is not power failsafe.
+ *
+ * The operation is done by checking the last flash sector, and using it as a
+ * temporarely scratch partition. The
+ *
+ * @param[in] fa_p flash area pointer
+ * @param[in] hdr boot image header pointer
+ *
+ * @return FIH_SUCCESS on success, error code otherwise
+ */
+inline static fih_ret
+decrypt_image_inplace(const struct flash_area *fa_p,
+ struct image_header *hdr)
+{
+ FIH_DECLARE(fih_rc, FIH_FAILURE);
+ int rc;
+ struct boot_loader_state boot_data;
+ struct boot_loader_state *state = &boot_data;
+ struct boot_status _bs;
+ struct boot_status *bs = &_bs;
+ size_t size;
+ size_t sect_size;
+ size_t sect_count;
+ size_t sect;
+ uint8_t image_index;
+ struct flash_sector sector;
+
+ memset(&boot_data, 0, sizeof(struct boot_loader_state));
+ memset(&_bs, 0, sizeof(struct boot_status));
+
+ /* Get size from last sector to know page/sector erase size */
+ rc = flash_area_get_sector(fa_p, boot_status_off(fa_p), §or);
+
+
+ image_index = BOOT_CURR_IMG(state);
+
+ if(IS_ENCRYPTED(hdr)) {
+#if 0 //Skip this step?, the image will just not boot if it's not decrypted properly
+ static uint8_t tmpbuf[BOOT_TMPBUF_SZ];
+ /* First check if the encrypted image is a good image before decrypting */
+ FIH_CALL(boot_image_validate_encrypted,fih_rc,fa_p,&_hdr,tmpbuf,BOOT_TMPBUF_SZ);
+ if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
+ FIH_RET(fih_rc);
+ }
+#endif
+ memset(&boot_data, 0, sizeof(struct boot_loader_state));
+ /* Load the encryption keys into cache */
+ rc = boot_enc_load(BOOT_CURR_ENC(state), image_index, hdr, fa_p, bs);
+ if (rc < 0) {
+ FIH_RET(fih_rc);
+ }
+ if (rc == 0 && boot_enc_set_key(BOOT_CURR_ENC(state), 0, bs)) {
+ FIH_RET(fih_rc);
+ }
+ }
+ else
+ {
+ /* Expected encrypted image! */
+ FIH_RET(fih_rc);
+ }
+
+ uint32_t src_size = 0;
+ rc = read_image_size(fa_p,hdr, &src_size);
+ if (rc != 0) {
+ FIH_RET(fih_rc);
+ }
+
+ /* TODO: This assumes every sector has an equal size, should instead use
+ * flash_area_get_sectors() to get the size of each sector and iterate
+ * over it.
+ */
+ sect_size = sector.fs_size;
+ sect_count = fa_p->fa_size / sect_size;
+ for (sect = 0, size = 0; size < src_size && sect < sect_count; sect++) {
+ rc = decrypt_region_inplace(state, fa_p,hdr, size, sect_size);
+ if (rc != 0) {
+ FIH_RET(fih_rc);
+ }
+ size += sect_size;
+ }
+
+ fih_rc = FIH_SUCCESS;
+ FIH_RET(fih_rc);
+}
+
+int
+boot_handle_enc_fw(const struct flash_area *flash_area)
+{
+ int rc = -1;
+ struct image_header _hdr = { 0 };
+ FIH_DECLARE(fih_rc, FIH_FAILURE);
+
+ rc = boot_image_load_header(flash_area, &_hdr);
+ if (rc != 0) {
+ goto out;
+ }
+
+ if (IS_ENCRYPTED(&_hdr)) {
+ //encrypted, we need to decrypt in place
+ FIH_CALL(decrypt_image_inplace,fih_rc,flash_area,&_hdr);
+ if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
+ rc = -1;
+ goto out;
+ }
+ }
+ else
+ {
+ rc = 0;
+ }
+
+out:
+ return rc;
+}
+
+#endif