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 00000000..b7cf9ff5 --- /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 7f6af91e..cddf8e28 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 @@ 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 - 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 00000000..6201e6b6 --- /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 +#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 diff --git a/boot/bootutil/include/bootutil/bootutil_public.h b/boot/bootutil/include/bootutil/bootutil_public.h index 90b1a1c9..e8d83a1d 100644 --- a/boot/bootutil/include/bootutil/bootutil_public.h +++ b/boot/bootutil/include/bootutil/bootutil_public.h @@ -43,6 +43,7 @@ #include #include #include +#include #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 diff --git a/boot/bootutil/src/bootutil_public.c b/boot/bootutil/src/bootutil_public.c index f245739b..8e700864 100644 --- a/boot/bootutil/src/bootutil_public.c +++ b/boot/bootutil/src/bootutil_public.c @@ -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; +} diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt index d9d474e5..d15977e7 100644 --- a/boot/zephyr/CMakeLists.txt +++ b/boot/zephyr/CMakeLists.txt @@ -130,7 +130,7 @@ zephyr_library_sources( ) 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( ${MBEDTLS_ASN1_DIR}/include ) @@ -219,7 +219,7 @@ elseif(CONFIG_BOOT_SIGNATURE_TYPE_ED25519 OR CONFIG_BOOT_ENCRYPT_X25519) ) 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( ${TINYCRYPT_DIR}/source/aes_encrypt.c ${TINYCRYPT_DIR}/source/aes_decrypt.c @@ -248,6 +248,12 @@ if(CONFIG_MCUBOOT_SERIAL) CONFIG_BOOT_ERASE_PROGRESSIVELY ${BOOT_DIR}/bootutil/src ) + + if(CONFIG_BOOT_ENCRYPT_IMAGE) + zephyr_library_sources( + ${BOOT_DIR}/boot_serial/src/boot_serial_encryption.c + ) + endif() endif() if(NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "") diff --git a/boot/zephyr/Kconfig.serial_recovery b/boot/zephyr/Kconfig.serial_recovery index 63306acc..52ec3b01 100644 --- a/boot/zephyr/Kconfig.serial_recovery +++ b/boot/zephyr/Kconfig.serial_recovery @@ -136,15 +136,6 @@ config BOOT_MGMT_CUSTOM_IMG_LIST 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" menuconfig BOOT_SERIAL_ENTRANCE_GPIO diff --git a/boot/zephyr/include/mcuboot-mbedtls-cfg.h b/boot/zephyr/include/mcuboot-mbedtls-cfg.h index 02bf0b08..2bab537d 100644 --- a/boot/zephyr/include/mcuboot-mbedtls-cfg.h +++ b/boot/zephyr/include/mcuboot-mbedtls-cfg.h @@ -25,7 +25,6 @@ #include "config-rsa.h" #elif defined(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256) || \ defined(CONFIG_BOOT_ENCRYPT_EC256) || \ - defined(CONFIG_BOOT_SERIAL_ENCRYPT_EC256) || \ (defined(CONFIG_BOOT_ENCRYPT_X25519) && !defined(CONFIG_BOOT_SIGNATURE_TYPE_ED25519)) #include "config-asn1.h" #elif defined(CONFIG_BOOT_SIGNATURE_TYPE_ED25519) diff --git a/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/boot/zephyr/include/mcuboot_config/mcuboot_config.h index 31cd9160..e20003bb 100644 --- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h +++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h @@ -117,11 +117,6 @@ #define MCUBOOT_ENCRYPT_EC256 #endif -#ifdef CONFIG_BOOT_SERIAL_ENCRYPT_EC256 -#define MCUBOOT_ENC_IMAGES -#define MCUBOOT_ENCRYPT_EC256 -#endif - #ifdef CONFIG_BOOT_ENCRYPT_X25519 #define MCUBOOT_ENC_IMAGES #define MCUBOOT_ENCRYPT_X25519 diff --git a/boot/zephyr/include/single_loader.h b/boot/zephyr/include/single_loader.h deleted file mode 100644 index 347fa098..00000000 --- a/boot/zephyr/include/single_loader.h +++ /dev/null @@ -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 diff --git a/boot/zephyr/single_loader.c b/boot/zephyr/single_loader.c index 86d5052e..5d1e76fc 100644 --- a/boot/zephyr/single_loader.c +++ b/boot/zephyr/single_loader.c @@ -9,6 +9,7 @@ #include "bootutil/image.h" #include "bootutil_priv.h" #include "bootutil/bootutil_log.h" +#include "bootutil/bootutil_public.h" #include "bootutil/fault_injection_hardening.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*/ - inline static fih_ret boot_image_validate_once(const struct flash_area *fa_p, struct image_header *hdr) @@ -89,349 +89,6 @@ boot_image_validate_once(const struct flash_area *fa_p, 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), §or); - - - 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. * diff --git a/docs/readme-zephyr.md b/docs/readme-zephyr.md index 2acc63db..a8f351df 100644 --- a/docs/readme-zephyr.md +++ b/docs/readme-zephyr.md @@ -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``). * 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 For details on other available configuration options for the serial recovery protocol, check the Kconfig options (for example by using ``menuconfig``).