Commit c9fa6088 authored by Jamie McCrae's avatar Jamie McCrae Committed by Jamie
Browse files

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: default avatarJamie McCrae <jamie.mccrae@nordicsemi.no>
parent 25d2f2cf
Loading
Loading
Loading
Loading
+32 −0
Original line number Diff line number Diff line
/*
 * 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
+34 −18
Original line number Diff line number Diff line
@@ -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 @@ bs_list(char *buf, int len)
                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 @@ bs_set(char *buf, int len)
                                   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 @@ out:
    zcbor_map_end_encode(cbor_state, 10);

    boot_serial_output();
    flash_area_close(fap);

#ifdef MCUBOOT_ENC_IMAGES
    /* 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();
            rc = boot_handle_enc_fw(fap);
        }
#endif //#ifdef MCUBOOT_ENC_IMAGES
    }
#endif

    flash_area_close(fap);
}

#ifdef MCUBOOT_BOOT_MGMT_ECHO
+315 −0
Original line number Diff line number Diff line
/*
 * 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), &sector);


    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
+13 −0
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@
#include <string.h>
#include <flash_map_backend/flash_map_backend.h>
#include <mcuboot_config/mcuboot_config.h>
#include <bootutil/image.h>

#ifdef __cplusplus
extern "C" {
@@ -294,6 +295,18 @@ boot_read_swap_state(const struct flash_area *fa,
int
boot_set_next(const struct flash_area *fa, bool active, bool confirm);

/**
 * Attempts to load image header from flash; verifies flash header fields.
 *
 * @param[in]   fa_p    flash area pointer
 * @param[out]  hdr     buffer for image header
 *
 * @return              0 on success, error code otherwise
 */
int
boot_image_load_header(const struct flash_area *fa_p,
                       struct image_header *hdr);

#ifdef __cplusplus
}
#endif
+33 −0
Original line number Diff line number Diff line
@@ -643,3 +643,36 @@ boot_set_confirmed(void)
{
    return boot_set_confirmed_multi(0);
}

int
boot_image_load_header(const struct flash_area *fa_p,
                       struct image_header *hdr)
{
    uint32_t size;
    int rc = flash_area_read(fa_p, 0, hdr, sizeof *hdr);

    if (rc != 0) {
        rc = BOOT_EFLASH;
        BOOT_LOG_ERR("Failed reading image header");
	return BOOT_EFLASH;
    }

    if (hdr->ih_magic != IMAGE_MAGIC) {
        BOOT_LOG_ERR("Bad image magic 0x%lx", (unsigned long)hdr->ih_magic);

        return BOOT_EBADIMAGE;
    }

    if (hdr->ih_flags & IMAGE_F_NON_BOOTABLE) {
        BOOT_LOG_ERR("Image not bootable");

        return BOOT_EBADIMAGE;
    }

    if (!boot_u32_safe_add(&size, hdr->ih_img_size, hdr->ih_hdr_size) ||
        size >= flash_area_get_size(fa_p)) {
        return BOOT_EBADIMAGE;
    }

    return 0;
}
Loading