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>
This commit is contained in:
Jamie McCrae 2023-07-21 10:23:17 +01:00 committed by Jamie
parent 25d2f2cfe0
commit c9fa60886b
12 changed files with 436 additions and 405 deletions

View File

@ -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

View File

@ -73,7 +73,7 @@
#endif #endif
#ifdef MCUBOOT_ENC_IMAGES #ifdef MCUBOOT_ENC_IMAGES
#include "single_loader.h" #include "boot_serial/boot_serial_encryption.h"
#endif #endif
#include "bootutil/boot_hooks.h" #include "bootutil/boot_hooks.h"
@ -293,18 +293,16 @@ bs_list(char *buf, int len)
if (FIH_EQ(fih_rc, FIH_BOOT_HOOK_REGULAR)) if (FIH_EQ(fih_rc, FIH_BOOT_HOOK_REGULAR))
{ {
#ifdef MCUBOOT_ENC_IMAGES #ifdef MCUBOOT_ENC_IMAGES
if (slot == 0 && IS_ENCRYPTED(&hdr)) { if (IS_ENCRYPTED(&hdr)) {
/* Clear the encrypted flag we didn't supply a key FIH_CALL(boot_image_validate_encrypted, fih_rc, fap,
* This flag could be set if there was a decryption in place &hdr, tmpbuf, sizeof(tmpbuf));
* performed before. We will try to validate the image without } else {
* decryption by clearing the flag in the heder. If #endif
* still encrypted the validation will fail. FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, &hdr,
*/ fap, tmpbuf, sizeof(tmpbuf), NULL, 0, NULL);
hdr.ih_flags &= ~(ENCRYPTIONFLAGS); #ifdef MCUBOOT_ENC_IMAGES
} }
#endif #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); fih_rc, image_index, 1);
if (FIH_EQ(fih_rc, FIH_BOOT_HOOK_REGULAR)) if (FIH_EQ(fih_rc, FIH_BOOT_HOOK_REGULAR))
{ {
FIH_CALL(bootutil_img_validate, fih_rc, NULL, 0, &hdr, fap, #ifdef MCUBOOT_ENC_IMAGES
tmpbuf, sizeof(tmpbuf), NULL, 0, NULL); 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)) { if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
@ -862,14 +869,23 @@ out:
zcbor_map_end_encode(cbor_state, 10); zcbor_map_end_encode(cbor_state, 10);
boot_serial_output(); boot_serial_output();
flash_area_close(fap);
#ifdef MCUBOOT_ENC_IMAGES #ifdef MCUBOOT_ENC_IMAGES
if (curr_off == img_size) { /* Check if this upload was for the primary slot */
/* Last sector received, now start a decryption on the image if it is encrypted*/ #if !defined(MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD)
rc = boot_handle_enc_fw(); 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 #ifdef MCUBOOT_BOOT_MGMT_ECHO

View File

@ -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), &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

View File

@ -43,6 +43,7 @@
#include <string.h> #include <string.h>
#include <flash_map_backend/flash_map_backend.h> #include <flash_map_backend/flash_map_backend.h>
#include <mcuboot_config/mcuboot_config.h> #include <mcuboot_config/mcuboot_config.h>
#include <bootutil/image.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@ -294,6 +295,18 @@ boot_read_swap_state(const struct flash_area *fa,
int int
boot_set_next(const struct flash_area *fa, bool active, bool confirm); 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 #ifdef __cplusplus
} }
#endif #endif

View File

@ -643,3 +643,36 @@ boot_set_confirmed(void)
{ {
return boot_set_confirmed_multi(0); 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;
}

View File

@ -130,7 +130,7 @@ zephyr_library_sources(
) )
endif() endif()
if(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256 OR CONFIG_BOOT_ENCRYPT_EC256 OR CONFIG_BOOT_SERIAL_ENCRYPT_EC256) if(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256 OR CONFIG_BOOT_ENCRYPT_EC256)
zephyr_library_include_directories( zephyr_library_include_directories(
${MBEDTLS_ASN1_DIR}/include ${MBEDTLS_ASN1_DIR}/include
) )
@ -219,7 +219,7 @@ elseif(CONFIG_BOOT_SIGNATURE_TYPE_ED25519 OR CONFIG_BOOT_ENCRYPT_X25519)
) )
endif() endif()
if(CONFIG_BOOT_ENCRYPT_EC256 OR CONFIG_BOOT_ENCRYPT_X25519 OR CONFIG_BOOT_SERIAL_ENCRYPT_EC256) if(CONFIG_BOOT_ENCRYPT_EC256 OR CONFIG_BOOT_ENCRYPT_X25519)
zephyr_library_sources( zephyr_library_sources(
${TINYCRYPT_DIR}/source/aes_encrypt.c ${TINYCRYPT_DIR}/source/aes_encrypt.c
${TINYCRYPT_DIR}/source/aes_decrypt.c ${TINYCRYPT_DIR}/source/aes_decrypt.c
@ -248,6 +248,12 @@ if(CONFIG_MCUBOOT_SERIAL)
CONFIG_BOOT_ERASE_PROGRESSIVELY CONFIG_BOOT_ERASE_PROGRESSIVELY
${BOOT_DIR}/bootutil/src ${BOOT_DIR}/bootutil/src
) )
if(CONFIG_BOOT_ENCRYPT_IMAGE)
zephyr_library_sources(
${BOOT_DIR}/boot_serial/src/boot_serial_encryption.c
)
endif()
endif() endif()
if(NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "") if(NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "")

View File

@ -136,15 +136,6 @@ config BOOT_MGMT_CUSTOM_IMG_LIST
endif # ENABLE_MGMT_PERUSER endif # ENABLE_MGMT_PERUSER
config BOOT_SERIAL_ENCRYPT_EC256
bool "Support for encrypted upgrade images using ECIES-P256 in serial recovery upload"
default n
help
If y, uploaded images via serial recovery can be decrypted
on the fly when upgrading to the primary slot. The
encryption mechanism used in this case is ECIES using primitives
described under "ECIES-P256 encryption" in docs/encrypted_images.md.
menu "Entrance methods" menu "Entrance methods"
menuconfig BOOT_SERIAL_ENTRANCE_GPIO menuconfig BOOT_SERIAL_ENTRANCE_GPIO

View File

@ -25,7 +25,6 @@
#include "config-rsa.h" #include "config-rsa.h"
#elif defined(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256) || \ #elif defined(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256) || \
defined(CONFIG_BOOT_ENCRYPT_EC256) || \ defined(CONFIG_BOOT_ENCRYPT_EC256) || \
defined(CONFIG_BOOT_SERIAL_ENCRYPT_EC256) || \
(defined(CONFIG_BOOT_ENCRYPT_X25519) && !defined(CONFIG_BOOT_SIGNATURE_TYPE_ED25519)) (defined(CONFIG_BOOT_ENCRYPT_X25519) && !defined(CONFIG_BOOT_SIGNATURE_TYPE_ED25519))
#include "config-asn1.h" #include "config-asn1.h"
#elif defined(CONFIG_BOOT_SIGNATURE_TYPE_ED25519) #elif defined(CONFIG_BOOT_SIGNATURE_TYPE_ED25519)

View File

@ -117,11 +117,6 @@
#define MCUBOOT_ENCRYPT_EC256 #define MCUBOOT_ENCRYPT_EC256
#endif #endif
#ifdef CONFIG_BOOT_SERIAL_ENCRYPT_EC256
#define MCUBOOT_ENC_IMAGES
#define MCUBOOT_ENCRYPT_EC256
#endif
#ifdef CONFIG_BOOT_ENCRYPT_X25519 #ifdef CONFIG_BOOT_ENCRYPT_X25519
#define MCUBOOT_ENC_IMAGES #define MCUBOOT_ENC_IMAGES
#define MCUBOOT_ENCRYPT_X25519 #define MCUBOOT_ENCRYPT_X25519

View File

@ -1,20 +0,0 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright (c) 2021-2021 Crodeon Technologies
*
*/
#ifndef H_SINGLE_LOADER_
#define H_SINGLE_LOADER_
#include "bootutil/fault_injection_hardening.h"
/**
* Handle an encrypted firmware in the main flash.
* This will decrypt the image inplace
*/
int boot_handle_enc_fw();
fih_ret boot_image_validate(const struct flash_area *fa_p,
struct image_header *hdr);
#endif

View File

@ -9,6 +9,7 @@
#include "bootutil/image.h" #include "bootutil/image.h"
#include "bootutil_priv.h" #include "bootutil_priv.h"
#include "bootutil/bootutil_log.h" #include "bootutil/bootutil_log.h"
#include "bootutil/bootutil_public.h"
#include "bootutil/fault_injection_hardening.h" #include "bootutil/fault_injection_hardening.h"
#include "mcuboot_config/mcuboot_config.h" #include "mcuboot_config/mcuboot_config.h"
@ -57,7 +58,6 @@ boot_image_validate(const struct flash_area *fa_p,
} }
#endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT || MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE*/ #endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT || MCUBOOT_VALIDATE_PRIMARY_SLOT_ONCE*/
inline static fih_ret inline static fih_ret
boot_image_validate_once(const struct flash_area *fa_p, boot_image_validate_once(const struct flash_area *fa_p,
struct image_header *hdr) struct image_header *hdr)
@ -89,349 +89,6 @@ boot_image_validate_once(const struct flash_area *fa_p,
FIH_RET(FIH_SUCCESS); FIH_RET(FIH_SUCCESS);
} }
/**
* 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
*/
static 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;
}
#ifdef MCUBOOT_ENC_IMAGES
/**
* 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
*
* @return FIH_SUCCESS on success, error code otherwise
*/
inline static fih_ret
boot_image_validate_encrypted(const struct flash_area *fa_p,
struct image_header *hdr)
{
static uint8_t tmpbuf[BOOT_TMPBUF_SZ];
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 (MUST_DECRYPT(fa_p, image_index, hdr)) {
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);
}
}
FIH_CALL(bootutil_img_validate, fih_rc, BOOT_CURR_ENC(state), image_index,
hdr, fa_p, tmpbuf, BOOT_TMPBUF_SZ, 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;
}
/* Get the SOC's flash erase block size from the DTS, fallback to 1024. */
#define SOC_FLASH_ERASE_BLK_SZ \
DT_PROP_OR(DT_CHOSEN(zephyr_flash), erase_block_size,1024)
/**
* 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.
*/
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;
static uint8_t buf[SOC_FLASH_ERASE_BLK_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 (MUST_DECRYPT(fa_p, image_index, hdr)) {
#if 0 //Skip this step?, the image will just not boot if it's not decrypted properly
/* First check if the encrypted image is a good image before decrypting */
FIH_CALL(boot_image_validate_encrypted,fih_rc,_fa_p,&_hdr);
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);
}
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()
{
int rc = -1;
FIH_DECLARE(fih_rc, FIH_FAILURE);
rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(0), &_fa_p);
assert(rc == 0);
rc = boot_image_load_header(_fa_p, &_hdr);
if (rc != 0) {
goto out;
}
if (IS_ENCRYPTED(&_hdr)) {
//encrypted, we need to decrypt in place
FIH_CALL(decrypt_image_inplace,fih_rc,_fa_p,&_hdr);
if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) {
rc = -1;
goto out;
}
}
else
{
rc = 0;
}
out:
flash_area_close(_fa_p);
return rc;
}
#endif
/** /**
* Gather information on image and prepare for booting. * Gather information on image and prepare for booting.
* *

View File

@ -251,12 +251,6 @@ Use the ``CONFIG_ENABLE_MGMT_PERUSER=y`` Kconfig option to enable the following
* Storage erase - This command allows erasing the storage partition (enable with ``CONFIG_BOOT_MGMT_CUSTOM_STORAGE_ERASE=y``). * Storage erase - This command allows erasing the storage partition (enable with ``CONFIG_BOOT_MGMT_CUSTOM_STORAGE_ERASE=y``).
* Custom image list - This command allows fetching version and installation status (custom properties) for all images (enable with ``CONFIG_BOOT_MGMT_CUSTOM_IMG_LIST=y``). * Custom image list - This command allows fetching version and installation status (custom properties) for all images (enable with ``CONFIG_BOOT_MGMT_CUSTOM_IMG_LIST=y``).
### In-place image decryption
Images uploaded by the serial recovery can be decrypted on the fly by using ECIES primitives described in the [ECIES encryption](encrypted_images.md#ecies-encryption) section.
Enable support for this feature by using ``CONFIG_BOOT_SERIAL_ENCRYPT_EC256=y``.
### More configuration ### More configuration
For details on other available configuration options for the serial recovery protocol, check the Kconfig options (for example by using ``menuconfig``). For details on other available configuration options for the serial recovery protocol, check the Kconfig options (for example by using ``menuconfig``).