From 115c7bcf0b65a92530aa5d21a1685f3502a40127 Mon Sep 17 00:00:00 2001 From: Daniel DeGrasse Date: Sat, 27 Sep 2025 08:52:30 -0500 Subject: [PATCH 01/18] [nrf fromtree] imgtool: support producing images in test mode Add --test flag, which allows users to append a trailer that marks the image as ready for a test swap. This can be used for cases where the user wants to load an image to flash that MCUBoot will boot in test mode after system reset. Signed-off-by: Daniel DeGrasse (cherry picked from commit 48b0f6da9af8d009eb8eafba023998a7d85320a1) Signed-off-by: Dominik Ermel --- scripts/imgtool/image.py | 17 +++++++++++------ scripts/imgtool/main.py | 9 ++++++--- 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/scripts/imgtool/image.py b/scripts/imgtool/image.py index 4476b687e5..4d5f4e7b0b 100755 --- a/scripts/imgtool/image.py +++ b/scripts/imgtool/image.py @@ -344,8 +344,8 @@ def __repr__(self): class Image: def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE, - pad_header=False, pad=False, confirm=False, align=1, - slot_size=0, max_sectors=DEFAULT_MAX_SECTORS, + pad_header=False, pad=False, confirm=False, test=False, + align=1, slot_size=0, max_sectors=DEFAULT_MAX_SECTORS, overwrite_only=False, endian="little", load_addr=0, rom_fixed=None, erased_val=None, save_enctlv=False, security_counter=None, max_align=None, @@ -362,6 +362,7 @@ def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE, self.pad_header = pad_header self.pad = pad self.confirm = confirm + self.test = test self.align = align self.slot_size = slot_size self.max_sectors = max_sectors @@ -494,12 +495,14 @@ def save(self, path, hex_addr=None): self.save_enctlv, self.enctlv_len) trailer_addr = (self.base_addr + self.slot_size) - trailer_size - if self.confirm and not self.overwrite_only: + if (self.test or self.confirm) and not self.overwrite_only: magic_align_size = align_up(len(self.boot_magic), self.max_align) image_ok_idx = -(magic_align_size + self.max_align) + # If test is set, we leave image_ok at the erased value flag = bytearray([self.erased_val] * self.max_align) - flag[0] = 0x01 # image_ok = 0x01 + if self.confirm: + flag[0] = 0x01 # image_ok = 0x01 h.puts(trailer_addr + trailer_size + image_ok_idx, bytes(flag)) h.puts(trailer_addr + (trailer_size - len(self.boot_magic)), @@ -944,11 +947,13 @@ def pad_to(self, size): pbytes = bytearray([self.erased_val] * padding) pbytes += bytearray([self.erased_val] * (tsize - len(self.boot_magic))) pbytes += self.boot_magic - if self.confirm and not self.overwrite_only: + if (self.test or self.confirm) and not self.overwrite_only: magic_size = 16 magic_align_size = align_up(magic_size, self.max_align) image_ok_idx = -(magic_align_size + self.max_align) - pbytes[image_ok_idx] = 0x01 # image_ok = 0x01 + # If test is set, set leave image_ok at the erased value + if self.confirm: + pbytes[image_ok_idx] = 0x01 # image_ok = 0x01 self.payload += pbytes @staticmethod diff --git a/scripts/imgtool/main.py b/scripts/imgtool/main.py index a7567bad41..ae5115d0f5 100755 --- a/scripts/imgtool/main.py +++ b/scripts/imgtool/main.py @@ -388,6 +388,9 @@ def convert(self, value, param, ctx): @click.option('--confirm', default=False, is_flag=True, help='When padding the image, mark it as confirmed (implies ' '--pad)') +@click.option('--test', default=False, is_flag=True, + help='When padding the image, mark it for a test swap (implies ' + '--pad)') @click.option('--pad', default=False, is_flag=True, help='Pad image to --slot-size bytes, adding trailer magic') @click.option('-S', '--slot-size', type=BasedIntParamType(), required=True, @@ -452,20 +455,20 @@ def convert(self, value, param, ctx): @click.option('--manifest', default=None, required=False, help='Path to the update manifest file') def sign(key, public_key_format, align, version, pad_sig, header_size, - pad_header, slot_size, pad, confirm, max_sectors, overwrite_only, + pad_header, slot_size, pad, confirm, test, max_sectors, overwrite_only, endian, encrypt_keylen, encrypt, compression, infile, outfile, dependencies, load_addr, hex_addr, erased_val, save_enctlv, security_counter, boot_record, custom_tlv, rom_fixed, max_align, clear, fix_sig, fix_sig_pubkey, sig_out, user_sha, hmac_sha, is_pure, vector_to_sign, non_bootable, vid, cid, manifest): - if confirm: + if confirm or test: # Confirmed but non-padded images don't make much sense, because # otherwise there's no trailer area for writing the confirmed status. pad = True img = image.Image(version=decode_version(version), header_size=header_size, pad_header=pad_header, pad=pad, confirm=confirm, - align=int(align), slot_size=slot_size, + test=test, align=int(align), slot_size=slot_size, max_sectors=max_sectors, overwrite_only=overwrite_only, endian=endian, load_addr=load_addr, rom_fixed=rom_fixed, erased_val=erased_val, save_enctlv=save_enctlv, From 530f60fb9a6146d1a378f0d7d3eafb0c5dfdb065 Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Tue, 21 Oct 2025 14:27:15 +0000 Subject: [PATCH 02/18] [nrf fromtree] bootutil: Refactor boot_read_enc_key Move code around to reduce ifdes and make it more clear, and allow to reuse TLV read check loop for key read verification. (cherry picked from commit 32b3c18b60a4b41139cb80bec2642fa15e5dcacd) Signed-off-by: Dominik Ermel --- boot/bootutil/src/bootutil_misc.c | 41 ++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c index 6484323812..946be85078 100644 --- a/boot/bootutil/src/bootutil_misc.c +++ b/boot/bootutil/src/bootutil_misc.c @@ -245,28 +245,47 @@ int boot_read_enc_key(const struct flash_area *fap, uint8_t slot, struct boot_status *bs) { uint32_t off; -#if MCUBOOT_SWAP_SAVE_ENCTLV uint32_t i; -#endif int rc; + uint8_t *read_dst; + uint32_t read_size; - off = boot_enc_key_off(fap, slot); #if MCUBOOT_SWAP_SAVE_ENCTLV - rc = flash_area_read(fap, off, bs->enctlv[slot], BOOT_ENC_TLV_ALIGN_SIZE); + /* In this case we have stored entire encryted TLV in swap-state and bs->enckey + * will be decrypted from the TLV. + */ + BOOT_LOG_DBG("boot_read_enc_key: TLV"); + read_dst = bs->enctlv[slot]; + read_size = BOOT_ENC_TLV_ALIGN_SIZE; +#else + BOOT_LOG_DBG("boot_read_enc_key: RAW key"); + read_dst = bs->enckey[slot]; + read_size = BOOT_ENC_KEY_ALIGN_SIZE; +#endif + + off = boot_enc_key_off(fap, slot); + + rc = flash_area_read(fap, off, read_dst, read_size); if (rc == 0) { - for (i = 0; i < BOOT_ENC_TLV_ALIGN_SIZE; i++) { - if (bs->enctlv[slot][i] != 0xff) { + for (i = 0; i < read_size; i++) { + if (read_dst[i] != 0xff) { break; } } - /* Only try to decrypt non-erased TLV metadata */ - if (i != BOOT_ENC_TLV_ALIGN_SIZE) { + + if (i == read_size) { + BOOT_LOG_ERR("boot_read_enc_key: No key, read all 0xFF"); + rc = 1; + } +#if MCUBOOT_SWAP_SAVE_ENCTLV + else { + /* read_dst is the same as bs->enctlv[slot], and serves as a source + * of the encrypted key. + */ rc = boot_decrypt_key(bs->enctlv[slot], bs->enckey[slot]); } - } -#else - rc = flash_area_read(fap, off, bs->enckey[slot], BOOT_ENC_KEY_ALIGN_SIZE); #endif + } return rc; } From 00656daf5a9d499a7ea31b1e89933cb0a84a6295 Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Tue, 21 Oct 2025 14:29:55 +0000 Subject: [PATCH 03/18] [nrf fromtree] bootutil: In boot_swap_image add return check from boot_read_enc_key Check return code instead of running loop over the key, to check if it has been read correctly. (cherry picked from commit 0ccce2f7421681e1b7e218e3ee24326f97aa812d) Signed-off-by: Dominik Ermel --- boot/bootutil/src/loader.c | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c index b777b99cd4..4f59ed459f 100644 --- a/boot/bootutil/src/loader.c +++ b/boot/bootutil/src/loader.c @@ -1520,7 +1520,6 @@ boot_swap_image(struct boot_loader_state *state, struct boot_status *bs) const struct flash_area *fap; #ifdef MCUBOOT_ENC_IMAGES uint8_t slot; - uint8_t i; #endif uint32_t size; uint32_t copy_size; @@ -1608,15 +1607,10 @@ boot_swap_image(struct boot_loader_state *state, struct boot_status *bs) boot_enc_init(BOOT_CURR_ENC_SLOT(state, slot)); rc = boot_read_enc_key(fap, slot, bs); - assert(rc == 0); - - for (i = 0; i < BOOT_ENC_KEY_SIZE; i++) { - if (bs->enckey[slot][i] != 0xff) { - break; - } - } - - if (i != BOOT_ENC_KEY_SIZE) { + if (rc) { + BOOT_LOG_DBG("boot_swap_image: Failed loading key (%d, %d)", + image_index, slot); + } else { boot_enc_set_key(BOOT_CURR_ENC_SLOT(state, slot), bs->enckey[slot]); } } From 278e1f715b1342cbb9d1c9c651d55df7dcb8a2f4 Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Wed, 15 Oct 2025 07:54:19 +0100 Subject: [PATCH 04/18] [nrf fromtree] boot: zephyr: Allow disabling default multiple RAM region file Allows disabling the default MCUboot Zephyr file specifying the multiple RAM regions, in order to allow users to supply their own versions Signed-off-by: Jamie McCrae (cherry picked from commit f846e9e9411bb0f2dad33fff7965b4eb6b8ab2f6) Signed-off-by: Dominik Ermel --- boot/zephyr/CMakeLists.txt | 2 +- boot/zephyr/Kconfig | 10 ++++++++++ boot/zephyr/ram_load.c | 3 --- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt index f724da8540..40160cce65 100644 --- a/boot/zephyr/CMakeLists.txt +++ b/boot/zephyr/CMakeLists.txt @@ -627,7 +627,7 @@ if((CONFIG_BOOT_SWAP_USING_SCRATCH OR CONFIG_BOOT_SWAP_USING_MOVE OR CONFIG_BOOT zephyr_library_sources(flash_check.c) endif() -if(CONFIG_BOOT_RAM_LOAD) +if(CONFIG_MULTIPLE_EXECUTABLE_RAM_REGIONS_DEFAULT_FILE) zephyr_library_sources(ram_load.c) endif() diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig index a99e0df2c8..02bb1de092 100644 --- a/boot/zephyr/Kconfig +++ b/boot/zephyr/Kconfig @@ -736,6 +736,16 @@ config MULTIPLE_EXECUTABLE_RAM_REGIONS When selected, boot_get_image_exec_ram_info() should be updated to provide the information about the areas. +config MULTIPLE_EXECUTABLE_RAM_REGIONS_DEFAULT_FILE + bool "Default MCUboot multiple executable RAM region source file" + default y + depends on MULTIPLE_EXECUTABLE_RAM_REGIONS + help + When enabled, will include the default MCUboot file that has the + boot_get_image_exec_ram_info() implementation in it, for users with out-of-tree or + custom configuration then this option can be disabled and a custom file can be added + by a CMake module which defines the configuration for the intended board. + config FLASH_RUNTIME_SOURCES bool "Images are read from flash partitions defined at runtime" depends on SINGLE_APPLICATION_SLOT diff --git a/boot/zephyr/ram_load.c b/boot/zephyr/ram_load.c index 92d521d809..85d054dcdd 100644 --- a/boot/zephyr/ram_load.c +++ b/boot/zephyr/ram_load.c @@ -19,12 +19,10 @@ #include -#ifdef MULTIPLE_EXECUTABLE_RAM_REGIONS int boot_get_image_exec_ram_info(uint32_t image_id, uint32_t *exec_ram_start, uint32_t *exec_ram_size) { - #ifdef CONFIG_SOC_SERIES_STM32N6X *exec_ram_start = DT_PROP_BY_IDX(DT_NODELABEL(axisram1), reg, 0); *exec_ram_size = DT_PROP_BY_IDX(DT_NODELABEL(axisram1), reg, 1); @@ -32,4 +30,3 @@ int boot_get_image_exec_ram_info(uint32_t image_id, return 0; } -#endif /* MULTIPLE_EXECUTABLE_RAM_REGIONS */ From 1f0ad354c70117c06deb78a260fa69b21e4331e2 Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Sat, 29 Nov 2025 11:13:44 +0000 Subject: [PATCH 05/18] [nrf fromtree] boot: zephyr: Remove weird custom key directory handling This code seems to have been introduced by someone without knowledge of the zephyr build system, specifying a Kconfig value in multiple files is a completely legal operation, the one that was applied last is the one that is used, and the default directory for keys should be the application configuration directory, not assuming where a .conf file is specifies the same folder as a key file (which is completely at odds with how Zephyr's file finding CMake code works). Signed-off-by: Jamie McCrae (cherry picked from commit 716f338ad93f45814dea1f07b6180de29236da64) Signed-off-by: Dominik Ermel --- boot/zephyr/CMakeLists.txt | 67 +++++++++++++------------------------- boot/zephyr/Kconfig | 18 ++++++---- 2 files changed, 34 insertions(+), 51 deletions(-) diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt index 40160cce65..2a6187ed9d 100644 --- a/boot/zephyr/CMakeLists.txt +++ b/boot/zephyr/CMakeLists.txt @@ -402,28 +402,17 @@ if(CONFIG_MCUBOOT_SERIAL) endif() if(NOT CONFIG_BOOT_SIGNATURE_USING_KMU AND NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "") - # CONF_FILE points to the KConfig configuration files of the bootloader. - foreach (filepath ${CONF_FILE}) - file(READ ${filepath} temp_text) - string(FIND "${temp_text}" ${CONFIG_BOOT_SIGNATURE_KEY_FILE} match) - if (${match} GREATER_EQUAL 0) - if (NOT DEFINED CONF_DIR) - get_filename_component(CONF_DIR ${filepath} DIRECTORY) - else() - message(FATAL_ERROR "Signature key file defined in multiple conf files") - endif() - endif() - endforeach() + set(key_file "${CONFIG_BOOT_SIGNATURE_KEY_FILE}") + string(CONFIGURE "${key_file}" key_file) - if(IS_ABSOLUTE ${CONFIG_BOOT_SIGNATURE_KEY_FILE}) - set(KEY_FILE ${CONFIG_BOOT_SIGNATURE_KEY_FILE}) - elseif((DEFINED CONF_DIR) AND - (EXISTS ${CONF_DIR}/${CONFIG_BOOT_SIGNATURE_KEY_FILE})) - set(KEY_FILE ${CONF_DIR}/${CONFIG_BOOT_SIGNATURE_KEY_FILE}) + if(IS_ABSOLUTE ${key_file}) + set(signing_key_file ${key_file}) + elseif(EXISTS ${APPLICATION_CONFIG_DIR}/${key_file}) + set(signing_key_file ${APPLICATION_CONFIG_DIR}/${key_file}) else() - set(KEY_FILE ${MCUBOOT_DIR}/${CONFIG_BOOT_SIGNATURE_KEY_FILE}) + set(signing_key_file ${MCUBOOT_DIR}/${key_file}) endif() - message("MCUBoot bootloader key file: ${KEY_FILE}") + message("MCUBoot bootloader key file: ${signing_key_file}") set_property( GLOBAL @@ -443,7 +432,7 @@ if(NOT CONFIG_BOOT_SIGNATURE_USING_KMU AND NOT CONFIG_BOOT_SIGNATURE_KEY_FILE ST ) # Emit a warning if using one of the default MCUboot key files - if(${KEY_FILE} IN_LIST mcuboot_default_signature_files) + if(${signing_key_file} IN_LIST mcuboot_default_signature_files) message(WARNING "WARNING: Using default MCUboot signing key file, this file is for debug use only and is not secure!") endif() @@ -455,37 +444,25 @@ if(NOT CONFIG_BOOT_SIGNATURE_USING_KMU AND NOT CONFIG_BOOT_SIGNATURE_KEY_FILE ST ${MCUBOOT_DIR}/scripts/imgtool.py getpub -k - ${KEY_FILE} + ${signing_key_file} > ${GENERATED_PUBKEY} - DEPENDS ${KEY_FILE} + DEPENDS ${signing_key_file} ) zephyr_library_sources(${GENERATED_PUBKEY}) endif() if(CONFIG_BOOT_ENCRYPTION_KEY_FILE AND NOT CONFIG_BOOT_ENCRYPTION_KEY_FILE STREQUAL "") - # CONF_FILE points to the KConfig configuration files of the bootloader. - unset(CONF_DIR) - foreach(filepath ${CONF_FILE}) - file(READ ${filepath} temp_text) - string(FIND "${temp_text}" ${CONFIG_BOOT_ENCRYPTION_KEY_FILE} match) - if(${match} GREATER_EQUAL 0) - if(NOT DEFINED CONF_DIR) - get_filename_component(CONF_DIR ${filepath} DIRECTORY) - else() - message(FATAL_ERROR "Encryption key file defined in multiple conf files") - endif() - endif() - endforeach() + set(key_file "${CONFIG_BOOT_ENCRYPTION_KEY_FILE}") + string(CONFIGURE "${key_file}" key_file) - if(IS_ABSOLUTE ${CONFIG_BOOT_ENCRYPTION_KEY_FILE}) - set(KEY_FILE ${CONFIG_BOOT_ENCRYPTION_KEY_FILE}) - elseif((DEFINED CONF_DIR) AND - (EXISTS ${CONF_DIR}/${CONFIG_BOOT_ENCRYPTION_KEY_FILE})) - set(KEY_FILE ${CONF_DIR}/${CONFIG_BOOT_ENCRYPTION_KEY_FILE}) + if(IS_ABSOLUTE ${key_file}) + set(encryption_key_file ${key_file}) + elseif(EXISTS ${APPLICATION_CONFIG_DIR}/${key_file}) + set(encryption_key_file ${APPLICATION_CONFIG_DIR}/${key_file}) else() - set(KEY_FILE ${MCUBOOT_DIR}/${CONFIG_BOOT_ENCRYPTION_KEY_FILE}) + set(encryption_key_file ${MCUBOOT_DIR}/${key_file}) endif() - message("MCUBoot bootloader encryption key file: ${KEY_FILE}") + message("MCUBoot bootloader encryption key file: ${encryption_key_file}") # Emit a warning if using one of the default MCUboot key files set(mcuboot_default_encryption_files @@ -497,7 +474,7 @@ if(CONFIG_BOOT_ENCRYPTION_KEY_FILE AND NOT CONFIG_BOOT_ENCRYPTION_KEY_FILE STREQ ${MCUBOOT_DIR}/enc-x25519-pub.pem ) - if(${KEY_FILE} IN_LIST mcuboot_default_encryption_files) + if(${encryption_key_file} IN_LIST mcuboot_default_encryption_files) message(WARNING "WARNING: Using default MCUboot encryption key file, this file is for debug use only and is not secure!") endif() @@ -509,9 +486,9 @@ if(CONFIG_BOOT_ENCRYPTION_KEY_FILE AND NOT CONFIG_BOOT_ENCRYPTION_KEY_FILE STREQ ${MCUBOOT_DIR}/scripts/imgtool.py getpriv -k - ${KEY_FILE} + ${encryption_key_file} > ${GENERATED_ENCKEY} - DEPENDS ${KEY_FILE} + DEPENDS ${encryption_key_file} ) zephyr_library_sources(${GENERATED_ENCKEY}) endif() diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig index 02bb1de092..d492cd69d4 100644 --- a/boot/zephyr/Kconfig +++ b/boot/zephyr/Kconfig @@ -474,13 +474,16 @@ config BOOT_SIGNATURE_KEY_FILE help You can use either absolute or relative path. In case relative path is used, the build system assumes that it starts - from the directory where the MCUBoot KConfig configuration file is - located. If the key file is not there, the build system uses relative - path that starts from the MCUBoot repository root directory. + from the APPLICATION_CONFIG_DIR directory. If the key file is not there, the build + system uses relative path that starts from the MCUBoot repository root directory. The key file will be parsed by imgtool's getpub command and a .c source with the public key information will be written in a format expected by MCUboot. + Note: In configuration files, escaped CMake variables can be used to refer to paths + e.g. \${CMAKE_CURRENT_LIST_DIR} will allow referencing a file in that directory, these + will be automatically configured by the build system. + endif config MCUBOOT_CLEANUP_ARM_CORE @@ -856,13 +859,16 @@ config BOOT_ENCRYPTION_KEY_FILE help You can use either absolute or relative path. In case relative path is used, the build system assumes that it starts - from the directory where the MCUBoot KConfig configuration file is - located. If the key file is not there, the build system uses relative - path that starts from the MCUBoot repository root directory. + from the APPLICATION_CONFIG_DIR directory. If the key file is not there, the build + system uses relative path that starts from the MCUBoot repository root directory. The key file will be parsed by imgtool's getpriv command and a .c source with the public key information will be written in a format expected by MCUboot. + Note: In configuration files, escaped CMake variables can be used to refer to paths + e.g. \${CMAKE_CURRENT_LIST_DIR} will allow referencing a file in that directory, these + will be automatically configured by the build system. + config BOOT_MAX_IMG_SECTORS_AUTO bool "Calculate maximum sectors automatically" default y if !PARTITION_MANAGER_ENABLED From 1825735536ce8f8cdbbbd856400ce48ff0e2dbd6 Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Sat, 29 Nov 2025 11:19:04 +0000 Subject: [PATCH 06/18] [nrf fromtree] boot: zephyr: kconfig: Only show signature file when needed Hides this Kconfig option when the operating mode is set to work as hash only without signature Signed-off-by: Jamie McCrae (cherry picked from commit 92854b7b2b17e2c119ba7b6cf415df6cf224ea5c) Signed-off-by: Dominik Ermel --- boot/zephyr/CMakeLists.txt | 3 ++- boot/zephyr/Kconfig | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt index 2a6187ed9d..879384f152 100644 --- a/boot/zephyr/CMakeLists.txt +++ b/boot/zephyr/CMakeLists.txt @@ -401,7 +401,8 @@ if(CONFIG_MCUBOOT_SERIAL) endif() endif() -if(NOT CONFIG_BOOT_SIGNATURE_USING_KMU AND NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "") +if(NOT CONFIG_BOOT_SIGNATURE_USING_KMU AND NOT CONFIG_BOOT_SIGNATURE_TYPE_NONE AND + NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "") set(key_file "${CONFIG_BOOT_SIGNATURE_KEY_FILE}") string(CONFIGURE "${key_file}" key_file) diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig index d492cd69d4..e849072f87 100644 --- a/boot/zephyr/Kconfig +++ b/boot/zephyr/Kconfig @@ -466,6 +466,7 @@ if !BOOT_SIGNATURE_USING_KMU && !NCS_BOOT_SIGNATURE_USING_ITS config BOOT_SIGNATURE_KEY_FILE string "PEM key file" + depends on !BOOT_SIGNATURE_TYPE_NONE default "root-ec-p256.pem" if BOOT_SIGNATURE_TYPE_ECDSA_P256 default "root-ed25519.pem" if BOOT_SIGNATURE_TYPE_ED25519 default "root-rsa-3072.pem" if BOOT_SIGNATURE_TYPE_RSA && BOOT_SIGNATURE_TYPE_RSA_LEN=3072 From 014e1e074baa6f53d17d38ce12ae87edd23f7e9b Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Fri, 24 Oct 2025 13:25:51 +0000 Subject: [PATCH 07/18] [nrf fromtree] bootutil: Split header with encryption functions Split definitions to crypto backend specific headers. (cherry picked from commit 5a161e4cc1cd5329c073d996f0300d2661c6b768) Signed-off-by: Dominik Ermel --- .../include/bootutil/crypto/aes_ctr.h | 128 +----------------- .../include/bootutil/crypto/aes_ctr_mbedtls.h | 58 ++++++++ .../include/bootutil/crypto/aes_ctr_psa.h | 53 ++++++++ .../bootutil/crypto/aes_ctr_tinycrypt.h | 77 +++++++++++ 4 files changed, 191 insertions(+), 125 deletions(-) create mode 100644 boot/bootutil/include/bootutil/crypto/aes_ctr_mbedtls.h create mode 100644 boot/bootutil/include/bootutil/crypto/aes_ctr_psa.h create mode 100644 boot/bootutil/include/bootutil/crypto/aes_ctr_tinycrypt.h diff --git a/boot/bootutil/include/bootutil/crypto/aes_ctr.h b/boot/bootutil/include/bootutil/crypto/aes_ctr.h index 88ae87c391..38a6002284 100644 --- a/boot/bootutil/include/bootutil/crypto/aes_ctr.h +++ b/boot/bootutil/include/bootutil/crypto/aes_ctr.h @@ -10,8 +10,6 @@ #ifndef __BOOTUTIL_CRYPTO_AES_CTR_H_ #define __BOOTUTIL_CRYPTO_AES_CTR_H_ -#include - #include "mcuboot_config/mcuboot_config.h" #if (defined(MCUBOOT_USE_MBED_TLS) + \ @@ -19,136 +17,16 @@ #error "One crypto backend must be defined: either MBED_TLS or TINYCRYPT or PSA" #endif -#include "bootutil/enc_key_public.h" - #if defined(MCUBOOT_USE_MBED_TLS) - #include - #define BOOT_ENC_BLOCK_SIZE (16) + #include "bootutil/crypto/aes_ctr_mbedtls.h" #endif /* MCUBOOT_USE_MBED_TLS */ #if defined(MCUBOOT_USE_TINYCRYPT) - #include - #include - #include - #include - #if defined(MCUBOOT_AES_256) || (BOOT_ENC_KEY_SIZE != TC_AES_KEY_SIZE) - #error "Cannot use AES-256 for encryption with Tinycrypt library." - #endif - #define BOOT_ENC_BLOCK_SIZE TC_AES_BLOCK_SIZE + #include "bootutil/crypto/aes_ctr_tinycrypt.h" #endif /* MCUBOOT_USE_TINYCRYPT */ #if defined(MCUBOOT_USE_PSA_CRYPTO) - #include - #define BOOT_ENC_BLOCK_SIZE (16) -#endif - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -#if defined(MCUBOOT_USE_PSA_CRYPTO) -typedef struct { - /* Fixme: This should not be, here, psa_key_id should be passed */ - uint8_t key[BOOT_ENC_KEY_SIZE]; -} bootutil_aes_ctr_context; - -void bootutil_aes_ctr_init(bootutil_aes_ctr_context *ctx); - -static inline void bootutil_aes_ctr_drop(bootutil_aes_ctr_context *ctx) -{ - memset(ctx, 0, sizeof(*ctx)); -} - -static inline int bootutil_aes_ctr_set_key(bootutil_aes_ctr_context *ctx, const uint8_t *k) -{ - memcpy(ctx->key, k, sizeof(ctx->key)); - - return 0; -} - -int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, - const uint8_t *m, uint32_t mlen, size_t blk_off, uint8_t *c); -int bootutil_aes_ctr_decrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, - const uint8_t *c, uint32_t clen, size_t blk_off, uint8_t *m); -#endif - -#if defined(MCUBOOT_USE_MBED_TLS) -typedef mbedtls_aes_context bootutil_aes_ctr_context; -static inline void bootutil_aes_ctr_init(bootutil_aes_ctr_context *ctx) -{ - (void)mbedtls_aes_init(ctx); -} - -static inline void bootutil_aes_ctr_drop(bootutil_aes_ctr_context *ctx) -{ - mbedtls_aes_free(ctx); -} - -static inline int bootutil_aes_ctr_set_key(bootutil_aes_ctr_context *ctx, const uint8_t *k) -{ - return mbedtls_aes_setkey_enc(ctx, k, BOOT_ENC_KEY_SIZE * 8); -} - -static inline int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *m, uint32_t mlen, size_t blk_off, uint8_t *c) -{ - uint8_t stream_block[BOOT_ENC_BLOCK_SIZE]; - return mbedtls_aes_crypt_ctr(ctx, mlen, &blk_off, counter, stream_block, m, c); -} - -static inline int bootutil_aes_ctr_decrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *c, uint32_t clen, size_t blk_off, uint8_t *m) -{ - uint8_t stream_block[BOOT_ENC_BLOCK_SIZE]; - return mbedtls_aes_crypt_ctr(ctx, clen, &blk_off, counter, stream_block, c, m); -} -#endif /* MCUBOOT_USE_MBED_TLS */ - -#if defined(MCUBOOT_USE_TINYCRYPT) -typedef struct tc_aes_key_sched_struct bootutil_aes_ctr_context; -static inline void bootutil_aes_ctr_init(bootutil_aes_ctr_context *ctx) -{ - (void)ctx; -} - -static inline void bootutil_aes_ctr_drop(bootutil_aes_ctr_context *ctx) -{ - (void)ctx; -} - -static inline int bootutil_aes_ctr_set_key(bootutil_aes_ctr_context *ctx, const uint8_t *k) -{ - int rc; - rc = tc_aes128_set_encrypt_key(ctx, k); - if (rc != TC_CRYPTO_SUCCESS) { - return -1; - } - return 0; -} - -static int _bootutil_aes_ctr_crypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *in, uint32_t inlen, uint32_t blk_off, uint8_t *out) -{ - int rc; - rc = tc_ctr_mode(out, inlen, in, inlen, counter, &blk_off, ctx); - if (rc != TC_CRYPTO_SUCCESS) { - return -1; - } - return 0; -} - -static inline int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *m, uint32_t mlen, uint32_t blk_off, uint8_t *c) -{ - return _bootutil_aes_ctr_crypt(ctx, counter, m, mlen, blk_off, c); -} - -static inline int bootutil_aes_ctr_decrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *c, uint32_t clen, uint32_t blk_off, uint8_t *m) -{ - return _bootutil_aes_ctr_crypt(ctx, counter, c, clen, blk_off, m); -} -#endif /* MCUBOOT_USE_TINYCRYPT */ - -#ifdef __cplusplus -} + #include "bootutil/crypto/aes_ctr_psa.h" #endif #endif /* __BOOTUTIL_CRYPTO_AES_CTR_H_ */ diff --git a/boot/bootutil/include/bootutil/crypto/aes_ctr_mbedtls.h b/boot/bootutil/include/bootutil/crypto/aes_ctr_mbedtls.h new file mode 100644 index 0000000000..9e37aaa49e --- /dev/null +++ b/boot/bootutil/include/bootutil/crypto/aes_ctr_mbedtls.h @@ -0,0 +1,58 @@ +/* + * This module provides a thin abstraction over some of the crypto + * primitives to make it easier to swap out the used crypto library. + * + * At this point, there are two choices: MCUBOOT_USE_MBED_TLS, or + * MCUBOOT_USE_TINYCRYPT. It is a compile error there is not exactly + * one of these defined. + */ + +#ifndef __BOOTUTIL_CRYPTO_AES_CTR_MBEDTLS_H_ +#define __BOOTUTIL_CRYPTO_AES_CTR_MBEDTLS_H_ + +#include +#include +#include "mcuboot_config/mcuboot_config.h" +#include "bootutil/enc_key_public.h" +#include + +#define BOOT_ENC_BLOCK_SIZE (16) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef mbedtls_aes_context bootutil_aes_ctr_context; + +static inline void bootutil_aes_ctr_init(bootutil_aes_ctr_context *ctx) +{ + (void)mbedtls_aes_init(ctx); +} + +static inline void bootutil_aes_ctr_drop(bootutil_aes_ctr_context *ctx) +{ + mbedtls_aes_free(ctx); +} + +static inline int bootutil_aes_ctr_set_key(bootutil_aes_ctr_context *ctx, const uint8_t *k) +{ + return mbedtls_aes_setkey_enc(ctx, k, BOOT_ENC_KEY_SIZE * 8); +} + +static inline int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *m, uint32_t mlen, size_t blk_off, uint8_t *c) +{ + uint8_t stream_block[BOOT_ENC_BLOCK_SIZE]; + return mbedtls_aes_crypt_ctr(ctx, mlen, &blk_off, counter, stream_block, m, c); +} + +static inline int bootutil_aes_ctr_decrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *c, uint32_t clen, size_t blk_off, uint8_t *m) +{ + uint8_t stream_block[BOOT_ENC_BLOCK_SIZE]; + return mbedtls_aes_crypt_ctr(ctx, clen, &blk_off, counter, stream_block, c, m); +} + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOTUTIL_CRYPTO_AES_CTR_MBEDTLS_H_ */ diff --git a/boot/bootutil/include/bootutil/crypto/aes_ctr_psa.h b/boot/bootutil/include/bootutil/crypto/aes_ctr_psa.h new file mode 100644 index 0000000000..249afe94ff --- /dev/null +++ b/boot/bootutil/include/bootutil/crypto/aes_ctr_psa.h @@ -0,0 +1,53 @@ +/* + * This module provides a thin abstraction over some of the crypto + * primitives to make it easier to swap out the used crypto library. + * + * At this point, there are two choices: MCUBOOT_USE_MBED_TLS, or + * MCUBOOT_USE_TINYCRYPT. It is a compile error there is not exactly + * one of these defined. + */ + +#ifndef __BOOTUTIL_CRYPTO_AES_CTR_PSA_H_ +#define __BOOTUTIL_CRYPTO_AES_CTR_PSA_H_ + +#include +#include +#include "mcuboot_config/mcuboot_config.h" +#include "bootutil/enc_key_public.h" +#include + +#define BOOT_ENC_BLOCK_SIZE (16) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + /* Fixme: This should not be, here, psa_key_id should be passed */ + uint8_t key[BOOT_ENC_KEY_SIZE]; +} bootutil_aes_ctr_context; + +void bootutil_aes_ctr_init(bootutil_aes_ctr_context *ctx); + +static inline void bootutil_aes_ctr_drop(bootutil_aes_ctr_context *ctx) +{ + memset(ctx, 0, sizeof(*ctx)); +} + +static inline int bootutil_aes_ctr_set_key(bootutil_aes_ctr_context *ctx, const uint8_t *k) +{ + memcpy(ctx->key, k, sizeof(ctx->key)); + + return 0; +} + +int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, + const uint8_t *m, uint32_t mlen, size_t blk_off, uint8_t *c); +int bootutil_aes_ctr_decrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, + const uint8_t *c, uint32_t clen, size_t blk_off, uint8_t *m); + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOTUTIL_CRYPTO_AES_CTR_H_ */ diff --git a/boot/bootutil/include/bootutil/crypto/aes_ctr_tinycrypt.h b/boot/bootutil/include/bootutil/crypto/aes_ctr_tinycrypt.h new file mode 100644 index 0000000000..76831c4ef2 --- /dev/null +++ b/boot/bootutil/include/bootutil/crypto/aes_ctr_tinycrypt.h @@ -0,0 +1,77 @@ +/* + * This module provides a thin abstraction over some of the crypto + * primitives to make it easier to swap out the used crypto library. + * + * At this point, there are two choices: MCUBOOT_USE_MBED_TLS, or + * MCUBOOT_USE_TINYCRYPT. It is a compile error there is not exactly + * one of these defined. + */ + +#ifndef __BOOTUTIL_CRYPTO_AES_CTR_TINYCRYPT_H_ +#define __BOOTUTIL_CRYPTO_AES_CTR_TINYCRYPT_H_ + +#include +#include +#include "mcuboot_config/mcuboot_config.h" +#include "bootutil/enc_key_public.h" + +#include +#include +#include + +#if defined(MCUBOOT_AES_256) || (BOOT_ENC_KEY_SIZE != TC_AES_KEY_SIZE) + #error "Cannot use AES-256 for encryption with Tinycrypt library." +#endif + +#define BOOT_ENC_BLOCK_SIZE TC_AES_BLOCK_SIZE + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct tc_aes_key_sched_struct bootutil_aes_ctr_context; +static inline void bootutil_aes_ctr_init(bootutil_aes_ctr_context *ctx) +{ + (void)ctx; +} + +static inline void bootutil_aes_ctr_drop(bootutil_aes_ctr_context *ctx) +{ + (void)ctx; +} + +static inline int bootutil_aes_ctr_set_key(bootutil_aes_ctr_context *ctx, const uint8_t *k) +{ + int rc; + rc = tc_aes128_set_encrypt_key(ctx, k); + if (rc != TC_CRYPTO_SUCCESS) { + return -1; + } + return 0; +} + +static int common_bootutil_aes_ctr_crypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *in, uint32_t inlen, uint32_t blk_off, uint8_t *out) +{ + int rc; + rc = tc_ctr_mode(out, inlen, in, inlen, counter, &blk_off, ctx); + if (rc != TC_CRYPTO_SUCCESS) { + return -1; + } + return 0; +} + +static inline int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *m, uint32_t mlen, uint32_t blk_off, uint8_t *c) +{ + return common_bootutil_aes_ctr_crypt(ctx, counter, m, mlen, blk_off, c); +} + +static inline int bootutil_aes_ctr_decrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *c, uint32_t clen, uint32_t blk_off, uint8_t *m) +{ + return common_bootutil_aes_ctr_crypt(ctx, counter, c, clen, blk_off, m); +} + +#ifdef __cplusplus +} +#endif + +#endif /* __BOOTUTIL_CRYPTO_AES_CTR_TINYCRYPT_H_ */ From 728deabc5ad8058e8939770719741ae1c81b967c Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Tue, 28 Oct 2025 19:03:26 +0000 Subject: [PATCH 08/18] [nrf fromtree] bootutil: Allow using psa_key_id_t in AES crypto context Store psa_key_id_t key in AES context instead of RAW key. (cherry picked from commit e92551838e18b6219af0891a18a757e538bd3ff6) Signed-off-by: Dominik Ermel --- .../include/bootutil/crypto/aes_ctr_psa.h | 18 +--- boot/bootutil/src/encrypted_psa.c | 90 ++++++++----------- 2 files changed, 42 insertions(+), 66 deletions(-) diff --git a/boot/bootutil/include/bootutil/crypto/aes_ctr_psa.h b/boot/bootutil/include/bootutil/crypto/aes_ctr_psa.h index 249afe94ff..f6846b93c4 100644 --- a/boot/bootutil/include/bootutil/crypto/aes_ctr_psa.h +++ b/boot/bootutil/include/bootutil/crypto/aes_ctr_psa.h @@ -16,30 +16,18 @@ #include "bootutil/enc_key_public.h" #include -#define BOOT_ENC_BLOCK_SIZE (16) - #ifdef __cplusplus extern "C" { #endif typedef struct { - /* Fixme: This should not be, here, psa_key_id should be passed */ - uint8_t key[BOOT_ENC_KEY_SIZE]; + psa_key_id_t key; } bootutil_aes_ctr_context; void bootutil_aes_ctr_init(bootutil_aes_ctr_context *ctx); -static inline void bootutil_aes_ctr_drop(bootutil_aes_ctr_context *ctx) -{ - memset(ctx, 0, sizeof(*ctx)); -} - -static inline int bootutil_aes_ctr_set_key(bootutil_aes_ctr_context *ctx, const uint8_t *k) -{ - memcpy(ctx->key, k, sizeof(ctx->key)); - - return 0; -} +void bootutil_aes_ctr_drop(bootutil_aes_ctr_context *ctx); +int bootutil_aes_ctr_set_key(bootutil_aes_ctr_context *ctx, const uint8_t *k); int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, const uint8_t *m, uint32_t mlen, size_t blk_off, uint8_t *c); diff --git a/boot/bootutil/src/encrypted_psa.c b/boot/bootutil/src/encrypted_psa.c index 1ef57184df..d6b470b948 100644 --- a/boot/bootutil/src/encrypted_psa.c +++ b/boot/bootutil/src/encrypted_psa.c @@ -190,12 +190,45 @@ void bootutil_aes_ctr_init(bootutil_aes_ctr_context *ctx) { psa_status_t psa_ret = psa_crypto_init(); - (void)ctx; - if (psa_ret != PSA_SUCCESS) { BOOT_LOG_ERR("AES init PSA crypto init failed %d", psa_ret); assert(0); } + + ctx->key = PSA_KEY_ID_NULL; +} + +void bootutil_aes_ctr_drop(bootutil_aes_ctr_context *ctx) +{ + psa_status_t psa_ret = psa_destroy_key(ctx->key); + + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("aes_ctr_drop: destruction failed %d", psa_ret); + /* This should never happen. If we fail to destroy key this happens + * either because it is invalid key number or something is really + * wrong; either way we have no way to recover. + */ + assert(0); + } + + ctx->key = PSA_KEY_ID_NULL; +} + +int bootutil_aes_ctr_set_key(bootutil_aes_ctr_context *ctx, const uint8_t *k) +{ + psa_status_t psa_ret = PSA_ERROR_BAD_STATE; + psa_key_attributes_t kattr = PSA_KEY_ATTRIBUTES_INIT; + + psa_set_key_type(&kattr, PSA_KEY_TYPE_AES); + psa_set_key_usage_flags(&kattr, PSA_KEY_USAGE_DECRYPT | PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&kattr, PSA_ALG_CTR); + + psa_ret = psa_import_key(&kattr, k, HKDF_AES_KEY_SIZE, &ctx->key); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("aes_ctr_set_key; import failed %d", psa_ret); + return -1; + } + return 0; } #if defined(MCUBOOT_ENC_IMAGES) @@ -394,8 +427,7 @@ int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, { int ret = 0; psa_status_t psa_ret = PSA_ERROR_BAD_STATE; - psa_key_attributes_t kattr = PSA_KEY_ATTRIBUTES_INIT; - psa_key_id_t kid; + const psa_key_id_t kid = ctx->key; psa_cipher_operation_t psa_op; size_t elen = 0; /* Decrypted length */ @@ -411,21 +443,6 @@ int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, psa_op = psa_cipher_operation_init(); - /* Fixme: Import should happen when key is decrypted, but due to lack - * of key destruction there is no way to destroy key stored by - * psa other way than here. */ - psa_set_key_type(&kattr, PSA_KEY_TYPE_AES); - psa_set_key_usage_flags(&kattr, PSA_KEY_USAGE_ENCRYPT); - psa_set_key_algorithm(&kattr, PSA_ALG_CTR); - - psa_ret = psa_import_key(&kattr, ctx->key, BOOT_ENC_KEY_SIZE, &kid); - psa_reset_key_attributes(&kattr); - if (psa_ret != PSA_SUCCESS) { - BOOT_LOG_ERR("AES enc import key failed %d", psa_ret); - ret = -1; - goto gone; - } - /* This could be done with psa_cipher_decrypt one-shot operation, but * multi-part operation is used to avoid re-allocating input buffer * to account for IV in front of data. @@ -434,7 +451,7 @@ int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, if (psa_ret != PSA_SUCCESS) { BOOT_LOG_ERR("AES enc setup failed %d", psa_ret); ret = -1; - goto gone_with_key; + goto gone; } /* Fixme: hardcoded counter size, but it is hardcoded everywhere */ @@ -458,13 +475,6 @@ int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, BOOT_LOG_WRN("AES enc cipher abort failed %d", psa_ret); /* Intentionally not changing the ret */ } -gone_with_key: - /* Fixme: Should be removed once key is shared by id */ - psa_ret = psa_destroy_key(kid); - if (psa_ret != PSA_SUCCESS) { - BOOT_LOG_WRN("AES enc destroy key failed %d", psa_ret); - /* Intentionally not changing the ret */ - } gone: return ret; } @@ -474,8 +484,7 @@ int bootutil_aes_ctr_decrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, { int ret = 0; psa_status_t psa_ret = PSA_ERROR_BAD_STATE; - psa_key_attributes_t kattr = PSA_KEY_ATTRIBUTES_INIT; - psa_key_id_t kid; + const psa_key_id_t kid = ctx->key; psa_cipher_operation_t psa_op; size_t dlen = 0; /* Decrypted length */ @@ -491,21 +500,6 @@ int bootutil_aes_ctr_decrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, psa_op = psa_cipher_operation_init(); - /* Fixme: Import should happen when key is decrypted, but due to lack - * of key destruction there is no way to destroy key stored by - * psa other way than here. */ - psa_set_key_type(&kattr, PSA_KEY_TYPE_AES); - psa_set_key_usage_flags(&kattr, PSA_KEY_USAGE_DECRYPT); - psa_set_key_algorithm(&kattr, PSA_ALG_CTR); - - psa_ret = psa_import_key(&kattr, ctx->key, BOOT_ENC_KEY_SIZE, &kid); - psa_reset_key_attributes(&kattr); - if (psa_ret != PSA_SUCCESS) { - BOOT_LOG_ERR("AES dec import key failed %d", psa_ret); - ret = -1; - goto gone; - } - /* This could be done with psa_cipher_decrypt one-shot operation, but * multi-part operation is used to avoid re-allocating input buffer * to account for IV in front of data. @@ -514,7 +508,7 @@ int bootutil_aes_ctr_decrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, if (psa_ret != PSA_SUCCESS) { BOOT_LOG_ERR("AES dec setup failed %d", psa_ret); ret = -1; - goto gone_with_key; + goto gone; } /* Fixme: hardcoded counter size, but it is hardcoded everywhere */ @@ -538,12 +532,6 @@ int bootutil_aes_ctr_decrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, BOOT_LOG_WRN("PSA dec abort failed %d", psa_ret); /* Intentionally not changing the ret */ } -gone_with_key: - psa_ret = psa_destroy_key(kid); - if (psa_ret != PSA_SUCCESS) { - BOOT_LOG_WRN("PSA dec key failed %d", psa_ret); - /* Intentionally not changing the ret */ - } gone: return ret; } From a04481ef326284027682f88c86e421c5dc43d4e7 Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Fri, 24 Oct 2025 14:27:31 +0000 Subject: [PATCH 09/18] [nrf fromtree] bootutil: Add boot_loader_state_init boot_loader_state initialization function. (cherry picked from commit a312656b60c8af0572b7b45e21d53c748908eb69) Signed-off-by: Dominik Ermel --- boot/bootutil/include/bootutil/bootutil.h | 7 +++++++ boot/bootutil/src/bootutil_loader.c | 18 ++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/boot/bootutil/include/bootutil/bootutil.h b/boot/bootutil/include/bootutil/bootutil.h index 48d56d1a85..98e7fa992f 100644 --- a/boot/bootutil/include/bootutil/bootutil.h +++ b/boot/bootutil/include/bootutil/bootutil.h @@ -96,6 +96,13 @@ fih_ret context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp); */ struct boot_loader_state *boot_get_loader_state(void); +/** + * Initialize boot_loader_state object + * + * @param state Bootloader state. + */ +void boot_state_init(struct boot_loader_state *state); + #if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) /** * Returns pointer to array of image maximum sizes. diff --git a/boot/bootutil/src/bootutil_loader.c b/boot/bootutil/src/bootutil_loader.c index 7a0e3a1f20..bfe5727f2c 100644 --- a/boot/bootutil/src/bootutil_loader.c +++ b/boot/bootutil/src/bootutil_loader.c @@ -434,3 +434,21 @@ boot_close_all_flash_areas(struct boot_loader_state *state) } } #endif /* !MCUBOOT_SINGLE_APPLICATION_SLOT_RAM_LOAD && !MCUBOOT_SINGLE_APPLICATION_SLOT */ + +void boot_state_init(struct boot_loader_state *state) +{ +#if defined(MCUBOOT_ENC_IMAGES) + int image; + int slot; +#endif + + memset(state, 0, sizeof(*state)); + +#if defined(MCUBOOT_ENC_IMAGES) + for (image = 0; image < BOOT_IMAGE_NUMBER; ++image) { + for (slot = 0; slot < BOOT_NUM_SLOTS; ++slot) { + boot_enc_init(&state->enc[image][slot]); + } + } +#endif +} From 8215f7b3b872560124a62a4678799f05f034d32f Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Tue, 28 Oct 2025 15:57:38 +0000 Subject: [PATCH 10/18] [nrf fromtree] boot_serial: Initialize state with boot_state_init Replace boot_state_clear with boot_state_init, where used for initialization and add boot_state_clear to remove state after use. (cherry picked from commit dd4b01f45f446dd6ebbb04090fc6370e0cc78c80) Signed-off-by: Dominik Ermel --- boot/boot_serial/src/boot_serial.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/boot/boot_serial/src/boot_serial.c b/boot/boot_serial/src/boot_serial.c index 495488ca97..9b4e689bc3 100644 --- a/boot/boot_serial/src/boot_serial.c +++ b/boot/boot_serial/src/boot_serial.c @@ -660,7 +660,7 @@ bs_list_set(uint8_t op, char *buf, int len) bool area_opened = false; state = boot_get_loader_state(); - boot_state_clear(state); + boot_state_init(state); rc = boot_open_all_flash_areas(state); if (rc != 0) { @@ -696,6 +696,7 @@ bs_list_set(uint8_t op, char *buf, int len) if (area_opened) { boot_close_all_flash_areas(state); } + boot_state_clear(state); if (rc != 0) { bs_rc_rsp(rc); From 4cf9c0092f7c8976d092c2e13572870418a770b4 Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Tue, 28 Oct 2025 16:03:00 +0000 Subject: [PATCH 11/18] [nrf fromtree] bootutil: Use boot_state_init instead of boot_state_clear Initialize boot_loader_state with boot_state init, then clean it up, after use, with boot_state_clear. (cherry picked from commit 8ff6b678f518da37e850f99550aef4e48d960efa) Signed-off-by: Dominik Ermel --- boot/bootutil/src/loader.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c index 4f59ed459f..c2302eb38a 100644 --- a/boot/bootutil/src/loader.c +++ b/boot/bootutil/src/loader.c @@ -2954,9 +2954,12 @@ boot_go(struct boot_rsp *rsp) { FIH_DECLARE(fih_rc, FIH_FAILURE); - boot_state_clear(NULL); + boot_state_init(&boot_data); FIH_CALL(context_boot_go, fih_rc, &boot_data, rsp); + + boot_state_clear(&boot_data); + FIH_RET(fih_rc); } From 53e3ad3dba92e6eab62982f238e1c7ce3ebc8f87 Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Tue, 28 Oct 2025 12:38:35 +0000 Subject: [PATCH 12/18] [nrf fromtree] bootutil: Remove NULL state logic from boot_state_clear No need to keep this defaulting logic. (cherry picked from commit d8c4cc6970371ef59494cec9c4d0d1d3b9ad65b2) Signed-off-by: Dominik Ermel --- boot/bootutil/src/bootutil_misc.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c index 946be85078..5687284b57 100644 --- a/boot/bootutil/src/bootutil_misc.c +++ b/boot/bootutil/src/bootutil_misc.c @@ -697,15 +697,9 @@ const struct image_max_size *boot_get_max_app_size(void) * Clears the boot state, so that previous operations have no effect on new * ones. * - * @param state The state that should be cleared. If the value - * is NULL, the default bootloader state will be - * cleared. + * @param state The state that should be cleared. */ void boot_state_clear(struct boot_loader_state *state) { - if (state != NULL) { - memset(state, 0, sizeof(struct boot_loader_state)); - } else { - memset(boot_get_loader_state(), 0, sizeof(struct boot_loader_state)); - } + memset(state, 0, sizeof(struct boot_loader_state)); } From 6a8c5fc6ee272c89918375205af19eb63aafd0ef Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Tue, 28 Oct 2025 12:29:57 +0000 Subject: [PATCH 13/18] [nrf fromtree] bootutil: Fix encryption context de initialization in boot_state_clear Call boot_enc_deinit before memset to 0. (cherry picked from commit d14ba22d1f59b3a4fe75dd41375e7d26b1ba2225) Signed-off-by: Dominik Ermel --- boot/bootutil/src/bootutil_misc.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c index 5687284b57..845848e490 100644 --- a/boot/bootutil/src/bootutil_misc.c +++ b/boot/bootutil/src/bootutil_misc.c @@ -701,5 +701,18 @@ const struct image_max_size *boot_get_max_app_size(void) */ void boot_state_clear(struct boot_loader_state *state) { +#if defined(MCUBOOT_ENC_IMAGES) + int image; + int slot; + + for (image = 0; image < BOOT_IMAGE_NUMBER; ++image) { + for (slot = 0; slot < BOOT_NUM_SLOTS; ++slot) { + /* Not using boot_enc_zeorize here, as it is redundant + * to the memset below that clears entire boot_loader_state. + */ + boot_enc_drop(&state->enc[image][slot]); + } + } +#endif memset(state, 0, sizeof(struct boot_loader_state)); } From 0812a81f4e1f4cf2dc0dd9558c39d7de0110f054 Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Wed, 12 Nov 2025 13:40:04 +0000 Subject: [PATCH 14/18] [nrf fromtree] bootutil: Temporarly drop mem cleanup from boot_state_clear Temporal removal of memset clear of boot_sector_clear that was causing boot to stop. The problem happens due to fill_rsp assigning pointer to header stored in cleared boot_state object, and the memset makes it inaccessible. Once change where header is copied to rsp the memset should be brought back as the boot_loader state should be completed before we pass further in execution. Fixes #2535 (cherry picked from commit e6fefac2a347696e489abd03a6dd698f85fe9a43) Signed-off-by: Dominik Ermel --- boot/bootutil/src/bootutil_misc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c index 845848e490..d691aa0e4d 100644 --- a/boot/bootutil/src/bootutil_misc.c +++ b/boot/bootutil/src/bootutil_misc.c @@ -713,6 +713,7 @@ void boot_state_clear(struct boot_loader_state *state) boot_enc_drop(&state->enc[image][slot]); } } +#else + (void)state; #endif - memset(state, 0, sizeof(struct boot_loader_state)); } From d8bcee0af8bb5a689c23b1b728c6356d839681a6 Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Thu, 16 Oct 2025 16:20:35 +0000 Subject: [PATCH 15/18] [nrf noup] imgtool: Add support for encrypting image with raw AES key The change adds --aes-key option that allows to pass a key via command line. The key is used to encrypt the image and there is not key exchange TLV added to the image. The options is provided for encrypting images for devices that store AES key on them so they do not expect it to be passed with image, in encrypted form. Signed-off-by: Dominik Ermel --- scripts/imgtool/image.py | 45 ++++++++++++--------------- scripts/imgtool/main.py | 66 +++++++++++++++++++++++++++++++--------- 2 files changed, 71 insertions(+), 40 deletions(-) diff --git a/scripts/imgtool/image.py b/scripts/imgtool/image.py index 4d5f4e7b0b..3c5fc31d90 100755 --- a/scripts/imgtool/image.py +++ b/scripts/imgtool/image.py @@ -570,7 +570,7 @@ def ecies_hkdf(self, enckey, plainkey, hmac_sha_alg): def create(self, key, public_key_format, enckey, dependencies=None, sw_type=None, custom_tlvs=None, compression_tlvs=None, - compression_type=None, encrypt_keylen=128, clear=False, + compression_type=None, aes_key=None, clear=False, fixed_sig=None, pub_key=None, vector_to_sign=None, user_sha='auto', hmac_sha='auto', is_pure=False, keep_comp_size=False, dont_encrypt=False): @@ -667,7 +667,7 @@ def create(self, key, public_key_format, enckey, dependencies=None, # # This adds the padding if image is not aligned to the 16 Bytes # in encrypted mode - if self.enckey is not None and dont_encrypt is False: + if aes_key is not None and dont_encrypt is False: pad_len = len(self.payload) % 16 if pad_len > 0: pad = bytes(16 - pad_len) @@ -682,10 +682,8 @@ def create(self, key, public_key_format, enckey, dependencies=None, if compression_type == "lzma2armthumb": compression_flags |= IMAGE_F['COMPRESSED_ARM_THUMB'] # This adds the header to the payload as well - if encrypt_keylen == 256: - self.add_header(enckey, protected_tlv_size, compression_flags, 256) - else: - self.add_header(enckey, protected_tlv_size, compression_flags) + aes_key_bits = 0 if aes_key is None else len(aes_key) * 8 + self.add_header(protected_tlv_size, compression_flags, aes_key_bits) prot_tlv = TLV(self.endian, TLV_PROT_INFO_MAGIC) @@ -806,12 +804,18 @@ def create(self, key, public_key_format, enckey, dependencies=None, if protected_tlv_off is not None: self.payload = self.payload[:protected_tlv_off] - if enckey is not None and dont_encrypt is False: - if encrypt_keylen == 256: - plainkey = os.urandom(32) - else: - plainkey = os.urandom(16) + # When passed AES key and clear flag, then do not encrypt, because the key + # is only passed to be stored in encryption key TLV, the payload stays clear text. + if aes_key and not clear: + nonce = bytes([0] * 16) + cipher = Cipher(algorithms.AES(aes_key), modes.CTR(nonce), + backend=default_backend()) + encryptor = cipher.encryptor() + img = bytes(self.payload[self.header_size:]) + self.payload[self.header_size:] = \ + encryptor.update(img) + encryptor.finalize() + if enckey is not None and dont_encrypt is False: if not isinstance(enckey, rsa.RSAPublic): if hmac_sha == 'auto' or hmac_sha == '256': hmac_sha = '256' @@ -825,19 +829,19 @@ def create(self, key, public_key_format, enckey, dependencies=None, if isinstance(enckey, rsa.RSAPublic): cipherkey = enckey._get_public().encrypt( - plainkey, padding.OAEP( + aes_key, padding.OAEP( mgf=padding.MGF1(algorithm=hashes.SHA256()), algorithm=hashes.SHA256(), label=None)) self.enctlv_len = len(cipherkey) tlv.add('ENCRSA2048', cipherkey) elif isinstance(enckey, ecdsa.ECDSA256P1Public): - cipherkey, mac, pubk = self.ecies_hkdf(enckey, plainkey, hmac_sha_alg) + cipherkey, mac, pubk = self.ecies_hkdf(enckey, aes_key, hmac_sha_alg) enctlv = pubk + mac + cipherkey self.enctlv_len = len(enctlv) tlv.add('ENCEC256', enctlv) elif isinstance(enckey, x25519.X25519Public): - cipherkey, mac, pubk = self.ecies_hkdf(enckey, plainkey, hmac_sha_alg) + cipherkey, mac, pubk = self.ecies_hkdf(enckey, aes_key, hmac_sha_alg) enctlv = pubk + mac + cipherkey self.enctlv_len = len(enctlv) if (hmac_sha == '256'): @@ -845,15 +849,6 @@ def create(self, key, public_key_format, enckey, dependencies=None, else: tlv.add('ENCX25519_SHA512', enctlv) - if not clear: - nonce = bytes([0] * 16) - cipher = Cipher(algorithms.AES(plainkey), modes.CTR(nonce), - backend=default_backend()) - encryptor = cipher.encryptor() - img = bytes(self.payload[self.header_size:]) - self.payload[self.header_size:] = \ - encryptor.update(img) + encryptor.finalize() - self.payload += prot_tlv.get() self.payload += tlv.get() @@ -868,11 +863,11 @@ def get_signature(self): def get_infile_data(self): return self.infile_data - def add_header(self, enckey, protected_tlv_size, compression_flags, aes_length=128): + def add_header(self, protected_tlv_size, compression_flags, aes_length=0): """Install the image header.""" flags = 0 - if enckey is not None: + if aes_length != 0: if aes_length == 128: flags |= IMAGE_F['ENCRYPTED_AES128'] else: diff --git a/scripts/imgtool/main.py b/scripts/imgtool/main.py index ae5115d0f5..1256bd5fa7 100755 --- a/scripts/imgtool/main.py +++ b/scripts/imgtool/main.py @@ -20,6 +20,7 @@ import base64 import getpass import lzma +import os import re import struct import sys @@ -324,6 +325,14 @@ def create_lzma2_header(dictsize, pb, lc, lp): header.append( ( pb * 5 + lp) * 9 + lc) return header +def match_sig_enc_key(skey, ekey): + ok = ((isinstance(skey, keys.ECDSA256P1) and isinstance(ekey, keys.ECDSA256P1Public)) or + (isinstance(skey, keys.ECDSA384P1) and isinstance(ekey, keys.ECDSA384P1Public)) or + (isinstance(skey, keys.RSA) and isinstance(ekey, keys.RSAPublic)) + ) + + return ok + class BasedIntParamType(click.ParamType): name = 'integer' @@ -454,13 +463,17 @@ def convert(self, value, param, ctx): help='Unique image class identifier, format: (|)') @click.option('--manifest', default=None, required=False, help='Path to the update manifest file') -def sign(key, public_key_format, align, version, pad_sig, header_size, +@click.option('--aes-key', default=None, required=False, + help='String representing raw AES key, format: hex byte string of 32 or 64' + 'hexadecimal characters') +@click.pass_context +def sign(ctx, key, public_key_format, align, version, pad_sig, header_size, pad_header, slot_size, pad, confirm, test, max_sectors, overwrite_only, endian, encrypt_keylen, encrypt, compression, infile, outfile, dependencies, load_addr, hex_addr, erased_val, save_enctlv, security_counter, boot_record, custom_tlv, rom_fixed, max_align, clear, fix_sig, fix_sig_pubkey, sig_out, user_sha, hmac_sha, is_pure, - vector_to_sign, non_bootable, vid, cid, manifest): + vector_to_sign, non_bootable, vid, cid, manifest, aes_key): if confirm or test: # Confirmed but non-padded images don't make much sense, because @@ -477,17 +490,23 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, manifest=manifest) compression_tlvs = {} img.load(infile) + key = load_key(key) if key else None - enckey = load_key(encrypt) if encrypt else None - if enckey and key and ((isinstance(key, keys.ECDSA256P1) and - not isinstance(enckey, keys.ECDSA256P1Public)) - or (isinstance(key, keys.ECDSA384P1) and - not isinstance(enckey, keys.ECDSA384P1Public)) - or (isinstance(key, keys.RSA) and - not isinstance(enckey, keys.RSAPublic))): - # FIXME - raise click.UsageError("Signing and encryption must use the same " - "type of key") + enckey = None + if not aes_key: + enckey = load_key(encrypt) if encrypt else None + if enckey and not match_sig_enc_key(key, enckey): + # FIXME + raise click.UsageError("Signing and encryption must use the same " + "type of key") + else: + if encrypt: + encrypt = None + click.secho('Raw AES key overrides --key, there will be no encrypted key added to the image', err=True) + if clear: + click.secho('--clear overrides raw AES key, image will not be encrypted', err=True) + if ctx.get_parameter_source('encrypt_keylen') != click.core.ParameterSource.DEFAULT: + click.secho('Raw AES key len overrides --encrypt-keylen', err=True) if pad_sig and hasattr(key, 'pad_sig'): key.pad_sig = True @@ -532,9 +551,26 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, 'Pure signatures, currently, enforces preferred hash algorithm, ' 'and forbids sha selection by user.') + plainkey = None + if aes_key: + # Converting the command line provided raw AES key to byte array; + # this aray will be truncated to desired len. + plainkey = bytes.fromhex(aes_key) + plainkey_len = len(plainkey) + if plainkey_len not in (16, 32): + raise click.UsageError("Provided keylen, {int(plainkey_len)} in bytes, not supported") + elif enckey: + if encrypt_keylen == 256: + encrypt_keylen_bytes = 32 + else: + encrypt_keylen_bytes = 16 + + # No AES plain key and there is request to encrypt, generate random AES key + plainkey = os.urandom(encrypt_keylen_bytes) + if compression in ["lzma2", "lzma2armthumb"]: img.create(key, public_key_format, enckey, dependencies, boot_record, - custom_tlvs, compression_tlvs, None, int(encrypt_keylen), clear, + custom_tlvs, compression_tlvs, None, None, clear, baked_signature, pub_key, vector_to_sign, user_sha=user_sha, hmac_sha=hmac_sha, is_pure=is_pure, keep_comp_size=False, dont_encrypt=True) compressed_img = image.Image(version=decode_version(version), @@ -580,13 +616,13 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, keep_comp_size = True compressed_img.create(key, public_key_format, enckey, dependencies, boot_record, custom_tlvs, compression_tlvs, - compression, int(encrypt_keylen), clear, baked_signature, + compression, plainkey, clear, baked_signature, pub_key, vector_to_sign, user_sha=user_sha, hmac_sha=hmac_sha, is_pure=is_pure, keep_comp_size=keep_comp_size) img = compressed_img else: img.create(key, public_key_format, enckey, dependencies, boot_record, - custom_tlvs, compression_tlvs, None, int(encrypt_keylen), clear, + custom_tlvs, compression_tlvs, None, plainkey, clear, baked_signature, pub_key, vector_to_sign, user_sha=user_sha, hmac_sha=hmac_sha, is_pure=is_pure) img.save(outfile, hex_addr) From b37f991498222d3b488ffb405251f3758c14e182 Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Thu, 16 Oct 2025 18:20:31 +0000 Subject: [PATCH 16/18] [nrf noup] bootutil: Provide support for embedded AES keys Commit provides support for MCUBOOT_EMBEDDED_ENC_KEY config option, that allows to compile code with embedded key. When this option is enabled, compilation requires definition of boot_take_enc_key function to be provided by user; prototype for the function is provided. The boot_take_enc_key function is supposed to provide encryption AES key to be used for image encryption and decryption. Signed-off-by: Dominik Ermel --- boot/boot_serial/src/boot_serial_encryption.c | 8 ++++++++ boot/bootutil/include/bootutil/enc_key.h | 3 +++ boot/bootutil/src/bootutil_loader.c | 4 ++++ boot/bootutil/src/bootutil_misc.c | 2 +- boot/bootutil/src/encrypted.c | 4 ++++ boot/bootutil/src/loader.c | 18 +++++++++++++++++- boot/mynewt/src/single_loader.c | 1 + 7 files changed, 38 insertions(+), 2 deletions(-) diff --git a/boot/boot_serial/src/boot_serial_encryption.c b/boot/boot_serial/src/boot_serial_encryption.c index de1f8b57a2..5ee5f98d63 100644 --- a/boot/boot_serial/src/boot_serial_encryption.c +++ b/boot/boot_serial/src/boot_serial_encryption.c @@ -31,7 +31,11 @@ boot_image_validate_encrypted(struct boot_loader_state *state, int rc; if (MUST_DECRYPT(fa_p, BOOT_CURR_IMG(state), hdr)) { +#ifdef MCUBOOT_EMBEDDED_ENC_KEY + rc = boot_en_take_key(bs->enckey[BOOT_SLOT_SECONDARY], BOOT_CUR_IMG(state), BOOT_SLOT_SECONDARY); +#else rc = boot_enc_load(state, BOOT_SLOT_SECONDARY, hdr, fa_p, bs); +#endif if (rc < 0) { FIH_RET(fih_rc); } @@ -235,7 +239,11 @@ decrypt_image_inplace(const struct flash_area *fa_p, #endif memset(&boot_data, 0, sizeof(struct boot_loader_state)); /* Load the encryption keys into cache */ +#ifdef MCUBOOT_EMBEDDED_ENC_KEY + rc = boot_take_enc_key(bs->enckey[BOOT_SLOT_PRIMARY], BOOT_CURR_IMG(state), BOOT_SLOT_PRIMARY); +#else rc = boot_enc_load(state, BOOT_SLOT_PRIMARY, hdr, fa_p, bs); +#endif if (rc < 0) { FIH_RET(fih_rc); } diff --git a/boot/bootutil/include/bootutil/enc_key.h b/boot/bootutil/include/bootutil/enc_key.h index 6fa0db18e4..461d4c00af 100644 --- a/boot/bootutil/include/bootutil/enc_key.h +++ b/boot/bootutil/include/bootutil/enc_key.h @@ -75,6 +75,9 @@ void boot_enc_decrypt(struct enc_key_data *enc_state, /* Note that boot_enc_zeorize takes BOOT_CURR_ENC, not BOOT_CURR_ENC_SLOT */ void boot_enc_zeroize(struct enc_key_data *enc_state); +/* Retrieve key for a slot */ +int boot_take_enc_key(uint8_t *key, int image, int slot); + #ifdef __cplusplus } #endif diff --git a/boot/bootutil/src/bootutil_loader.c b/boot/bootutil/src/bootutil_loader.c index bfe5727f2c..97184d63f5 100644 --- a/boot/bootutil/src/bootutil_loader.c +++ b/boot/bootutil/src/bootutil_loader.c @@ -197,7 +197,11 @@ boot_check_image(struct boot_loader_state *state, struct boot_status *bs, int sl */ #if defined(MCUBOOT_ENC_IMAGES) && !defined(MCUBOOT_RAM_LOAD) if (MUST_DECRYPT(fap, BOOT_CURR_IMG(state), hdr)) { +#ifdef MCUBOOT_EMBEDDED_ENC_KEY + rc = boot_take_enc_key(bs->enckey[BOOT_SLOT_SECONDARY], BOOT_CURR_IMG(state), BOOT_SLOT_SECONDARY); +#else rc = boot_enc_load(state, BOOT_SLOT_SECONDARY, hdr, fap, bs); +#endif if (rc < 0) { FIH_RET(fih_rc); } diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c index d691aa0e4d..502454cd03 100644 --- a/boot/bootutil/src/bootutil_misc.c +++ b/boot/bootutil/src/bootutil_misc.c @@ -240,7 +240,7 @@ boot_read_unprotected_tlv_sizes(const struct flash_area *fap, uint16_t *tlv_size } #endif -#ifdef MCUBOOT_ENC_IMAGES +#if defined(MCUBOOT_ENC_IMAGES) && !defined(MCUBOOT_EMBEDDED_ENC_KEY) int boot_read_enc_key(const struct flash_area *fap, uint8_t slot, struct boot_status *bs) { diff --git a/boot/bootutil/src/encrypted.c b/boot/bootutil/src/encrypted.c index 9f86bb457b..049bf316af 100644 --- a/boot/bootutil/src/encrypted.c +++ b/boot/bootutil/src/encrypted.c @@ -370,6 +370,7 @@ static int fake_rng(void *p_rng, unsigned char *output, size_t len) #endif /* (MCUBOOT_ENCRYPT_RSA && MCUBOOT_USE_MBED_TLS && !MCUBOOT_USE_PSA_CRYPTO) || (MCUBOOT_ENCRYPT_EC256 && MCUBOOT_USE_MBED_TLS) */ +#if !defined(MCUBOOT_EMBEDDED_ENC_KEY) /* * Decrypt an encryption key TLV. * @@ -564,7 +565,9 @@ boot_decrypt_key(const uint8_t *buf, uint8_t *enckey) return rc; } #endif /* CONFIG_BOOT_ED25519_PSA && CONFIG_BOOT_ECDSA_PSA */ +#endif /* defined(MCUBOOT_EMBEDDED_ENC_KEY) */ +#if !defined(MCUBOOT_EMBEDDED_ENC_KEY) /* * Load encryption key. */ @@ -625,6 +628,7 @@ boot_enc_load(struct boot_loader_state *state, int slot, return boot_decrypt_key(buf, bs->enckey[slot]); } +#endif /* defined(MCUBOOT_EMBEDDED_ENC_KEY */ int boot_enc_init(struct enc_key_data *enc_state) diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c index c2302eb38a..df23f82cb3 100644 --- a/boot/bootutil/src/loader.c +++ b/boot/bootutil/src/loader.c @@ -1421,9 +1421,13 @@ boot_copy_image(struct boot_loader_state *state, struct boot_status *bs) #ifdef MCUBOOT_ENC_IMAGES if (IS_ENCRYPTED(boot_img_hdr(state, BOOT_SLOT_SECONDARY))) { +#ifdef MCUBOOT_EMBEDDED_ENC_KEY + rc = boot_take_enc_key(bs->enckey[BOOT_SLOT_SECONDARY], BOOT_CURR_IMG(state), BOOT_SLOT_SECONDARY); +#else rc = boot_enc_load(state, BOOT_SLOT_SECONDARY, boot_img_hdr(state, BOOT_SLOT_SECONDARY), fap_secondary_slot, bs); +#endif /* MCUBOOT_EMBEDDED_ENC_KEY */ if (rc < 0) { return BOOT_EBADIMAGE; @@ -1545,7 +1549,11 @@ boot_swap_image(struct boot_loader_state *state, struct boot_status *bs) #ifdef MCUBOOT_ENC_IMAGES if (IS_ENCRYPTED(hdr)) { fap = BOOT_IMG_AREA(state, BOOT_SLOT_PRIMARY); +#ifdef MCUBOOT_EMBEDDED_ENC_KEY + rc = boot_take_enc_key(bs->enckey[BOOT_SLOT_PRIMARY], BOOT_CURR_IMG(state), BOOT_SLOT_PRIMARY); +#else rc = boot_enc_load(state, BOOT_SLOT_PRIMARY, hdr, fap, bs); +#endif /* MCUBOOT_EMBEDDED_ENC_KEY */ assert(rc >= 0); if (rc == 0) { @@ -1569,7 +1577,11 @@ boot_swap_image(struct boot_loader_state *state, struct boot_status *bs) hdr = boot_img_hdr(state, BOOT_SLOT_SECONDARY); if (IS_ENCRYPTED(hdr)) { fap = BOOT_IMG_AREA(state, BOOT_SLOT_SECONDARY); +#ifdef MCUBOOT_EMBEDDED_ENC_KEY + rc = boot_take_enc_key(bs->enckey[BOOT_SLOT_SECONDARY], BOOT_CURR_IMG(state), BOOT_SLOT_SECONDARY); +#else rc = boot_enc_load(state, BOOT_SLOT_SECONDARY, hdr, fap, bs); +#endif /* MCUBOOT_EMBEDDED_ENC_KEY */ assert(rc >= 0); if (rc == 0) { @@ -1606,7 +1618,11 @@ boot_swap_image(struct boot_loader_state *state, struct boot_status *bs) boot_enc_init(BOOT_CURR_ENC_SLOT(state, slot)); +#ifdef MCUBOOT_EMBEDDED_ENC_KEY + rc = boot_take_enc_key(bs->enckey[slot], image_index, slot); +#else rc = boot_read_enc_key(fap, slot, bs); +#endif /* MCUBOOT_EMBEDDED_ENC_KEY */ if (rc) { BOOT_LOG_DBG("boot_swap_image: Failed loading key (%d, %d)", image_index, slot); @@ -1614,7 +1630,7 @@ boot_swap_image(struct boot_loader_state *state, struct boot_status *bs) boot_enc_set_key(BOOT_CURR_ENC_SLOT(state, slot), bs->enckey[slot]); } } -#endif +#endif /* MCUBOOT_ENC_IMAGES */ flash_area_close(fap); } diff --git a/boot/mynewt/src/single_loader.c b/boot/mynewt/src/single_loader.c index 394fc372ac..935d04881c 100644 --- a/boot/mynewt/src/single_loader.c +++ b/boot/mynewt/src/single_loader.c @@ -49,6 +49,7 @@ boot_image_validate(const struct flash_area *fa_p, * was performed. We will try to validate the image, and if still * encrypted the validation will fail, and go in panic mode */ + BOOT_LOG_DBG("boot_image_validate: clearing encryption flags"); hdr->ih_flags &= ~(ENCRYPTIONFLAGS); } FIH_CALL(bootutil_img_validate, fih_rc, NULL, hdr, fa_p, tmpbuf, From f308c3717672ebb4bacf1bb48825552c21ac9dd5 Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Tue, 21 Oct 2025 18:07:46 +0000 Subject: [PATCH 17/18] [nrf noup] zephyr: Add support for embedded AES key The commit provides Kconfig options that allow to configure MCUboot to use embedded AES key. Primary option is CONFIG_BOOT_ENCRYPT_IMAGE_WITH_EMBEDDED_KEY that allows to select usage of embedded key in the code. After it follow sets of Kconfigs: - CONFIG_BOOT_ENCRYPT_IMAGE_GENERATE_BASIC_KEY_PROVIDER - CONFIG_BOOT_ENCRYPT_IMAGE_USE_CUSTOM_KEY_PROVIDER The above set allows to select source of the key. The first option will choose to generate default key provider, with a single embedded key, where the key is provided as a string assigned to CONFIG_BOOOT_ENCRYPT_IMAGE_EMBEDDED_RAW_KEY. The second option selects user provided code as source of key(s). Signed-off-by: Dominik Ermel --- boot/zephyr/CMakeLists.txt | 91 +++++++++++-------- boot/zephyr/Kconfig | 78 ++++++++++++++-- .../include/mcuboot_config/mcuboot_config.h | 5 + ...single_builtin_aes_key_provider.c.template | 26 ++++++ 4 files changed, 156 insertions(+), 44 deletions(-) create mode 100644 boot/zephyr/templates/single_builtin_aes_key_provider.c.template diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt index 879384f152..bdc7ecfa98 100644 --- a/boot/zephyr/CMakeLists.txt +++ b/boot/zephyr/CMakeLists.txt @@ -129,8 +129,7 @@ zephyr_library_sources( ${BOOT_DIR}/bootutil/src/fault_injection_hardening.c ) -if((CONFIG_BOOT_ENCRYPT_X25519 AND CONFIG_BOOT_ED25519_PSA) - OR (CONFIG_BOOT_ENCRYPT_EC256 AND CONFIG_BOOT_ECDSA_PSA)) +if(CONFIG_BOOT_ENCRYPT_IMAGE AND CONFIG_BOOT_USE_PSA_CRYPTO) zephyr_library_sources(${BOOT_DIR}/bootutil/src/encrypted_psa.c) endif() @@ -453,45 +452,48 @@ if(NOT CONFIG_BOOT_SIGNATURE_USING_KMU AND NOT CONFIG_BOOT_SIGNATURE_TYPE_NONE A endif() if(CONFIG_BOOT_ENCRYPTION_KEY_FILE AND NOT CONFIG_BOOT_ENCRYPTION_KEY_FILE STREQUAL "") - set(key_file "${CONFIG_BOOT_ENCRYPTION_KEY_FILE}") - string(CONFIGURE "${key_file}" key_file) + if(CONFIG_BOOT_ENCRYPT_IMAGE_WITH_SHARED_KEY) + set(key_file "${CONFIG_BOOT_ENCRYPTION_KEY_FILE}") + string(CONFIGURE "${key_file}" key_file) + + if(IS_ABSOLUTE ${key_file}) + set(encryption_key_file ${key_file}) + elseif(EXISTS ${APPLICATION_CONFIG_DIR}/${key_file}) + set(encryption_key_file ${APPLICATION_CONFIG_DIR}/${key_file}) + else() + set(encryption_key_file ${MCUBOOT_DIR}/${key_file}) + endif() + message("MCUBoot bootloader encryption key file: ${encryption_key_file}") + + # Emit a warning if using one of the default MCUboot key files + set(mcuboot_default_encryption_files + ${MCUBOOT_DIR}/enc-ec256-priv.pem + ${MCUBOOT_DIR}/enc-ec256-pub.pem + ${MCUBOOT_DIR}/enc-rsa2048-priv.pem + ${MCUBOOT_DIR}/enc-rsa2048-pub.pem + ${MCUBOOT_DIR}/enc-x25519-priv.pem + ${MCUBOOT_DIR}/enc-x25519-pub.pem + ) - if(IS_ABSOLUTE ${key_file}) - set(encryption_key_file ${key_file}) - elseif(EXISTS ${APPLICATION_CONFIG_DIR}/${key_file}) - set(encryption_key_file ${APPLICATION_CONFIG_DIR}/${key_file}) - else() - set(encryption_key_file ${MCUBOOT_DIR}/${key_file}) - endif() - message("MCUBoot bootloader encryption key file: ${encryption_key_file}") + if(${encryption_key_file} IN_LIST mcuboot_default_encryption_files) + message(WARNING "WARNING: Using default MCUboot encryption key file, this file is for debug use only and is not secure!") + endif() - # Emit a warning if using one of the default MCUboot key files - set(mcuboot_default_encryption_files - ${MCUBOOT_DIR}/enc-ec256-priv.pem - ${MCUBOOT_DIR}/enc-ec256-pub.pem - ${MCUBOOT_DIR}/enc-rsa2048-priv.pem - ${MCUBOOT_DIR}/enc-rsa2048-pub.pem - ${MCUBOOT_DIR}/enc-x25519-priv.pem - ${MCUBOOT_DIR}/enc-x25519-pub.pem - ) + set(GENERATED_ENCKEY ${ZEPHYR_BINARY_DIR}/autogen-enckey.c) + add_custom_command( + OUTPUT ${GENERATED_ENCKEY} + COMMAND + ${PYTHON_EXECUTABLE} + ${MCUBOOT_DIR}/scripts/imgtool.py + getpriv + -k + ${encryption_key_file} + > ${GENERATED_ENCKEY} + DEPENDS ${encryption_key_file} + ) - if(${encryption_key_file} IN_LIST mcuboot_default_encryption_files) - message(WARNING "WARNING: Using default MCUboot encryption key file, this file is for debug use only and is not secure!") + zephyr_library_sources(${GENERATED_ENCKEY}) endif() - - set(GENERATED_ENCKEY ${ZEPHYR_BINARY_DIR}/autogen-enckey.c) - add_custom_command( - OUTPUT ${GENERATED_ENCKEY} - COMMAND - ${PYTHON_EXECUTABLE} - ${MCUBOOT_DIR}/scripts/imgtool.py - getpriv - -k - ${encryption_key_file} - > ${GENERATED_ENCKEY} - DEPENDS ${encryption_key_file} - ) - zephyr_library_sources(${GENERATED_ENCKEY}) endif() if(CONFIG_MCUBOOT_CLEANUP_ARM_CORE) @@ -801,3 +803,18 @@ if(SYSBUILD AND CONFIG_PCD_APP) set(RAM_FLASH_ADDR "${ram_flash_addr}" CACHE STRING "" FORCE) set(RAM_FLASH_SIZE "${ram_flash_size}" CACHE STRING "" FORCE) endif() + +if(${CONFIG_BOOT_ENCRYPT_IMAGE_GENERATE_BASIC_KEY_PROVIDER}) + # Need to generate single key provider source, from template. + # Take provided key, in form of a string and make it into C array, BOOT_AES_RAW_KEY_HEX_ARRAY, + # of byte size hex values. + set(BOOT_AES_RAW_KEY_HEX_STRING ${CONFIG_BOOT_ENCRYPT_IMAGE_EMBEDDED_RAW_KEY}) + string(REGEX REPLACE "(..)" "0x\\1, " BOOT_AES_RAW_KEY_HEX_ARRAY "${BOOT_AES_RAW_KEY_HEX_STRING}") + + # The tamplate references BOOT_AES_RAW_KEY_HEX_ARRAY where it expects the array to be substituted. + set(OUTPUT_BOOT_AES_RAW_KEY_SRC ${ZEPHYR_BINARY_DIR}/mcuboot_generated/builtin_aes_key_provider.c) + configure_file(templates/single_builtin_aes_key_provider.c.template ${OUTPUT_BOOT_AES_RAW_KEY_SRC} @ONLY) + + # Add generated source file to build + zephyr_library_sources(${OUTPUT_BOOT_AES_RAW_KEY_SRC}) +endif() diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig index e849072f87..5d12ac4a3d 100644 --- a/boot/zephyr/Kconfig +++ b/boot/zephyr/Kconfig @@ -96,15 +96,26 @@ config BOOT_ED25519_PSA_DEPENDENCIES if BOOT_ENCRYPT_IMAGE +config BOOT_AES_DEPENDENCIES + bool + default y if BOOT_USE_PSA_CRYPTO + select PSA_WANT_ALG_CTR + select PSA_WANT_KEY_TYPE_AES + select PSA_WANT_AES_KEY_SIZE_256 if BOOT_ENCRYPT_ALG_AES_256 + select PSA_WANT_AES_KEY_SIZE_128 if BOOT_ENCRYPT_ALG_AES_128 + help + PSA Dependencies for image encryption. At this point they select + AES CTR mode with support for key as selected by user. + +if !BOOT_ENCRYPT_IMAGE_WITH_EMBEDDED_KEY + config BOOT_X25519_PSA_DEPENDENCIES bool select PSA_WANT_ALG_ECDH select PSA_WANT_ALG_HMAC select PSA_WANT_ALG_HKDF - select PSA_WANT_ALG_CTR select PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT select PSA_WANT_KEY_TYPE_DERIVE - select PSA_WANT_KEY_TYPE_AES select PSA_WANT_ECC_MONTGOMERY_255 help Dependencies for x25519 shared-random key encryption and AES @@ -113,6 +124,8 @@ config BOOT_X25519_PSA_DEPENDENCIES to use with it; the others are used for shared key decryption and derivation. +endif # !BOOT_ENCRYPT_IMAGE_WITH_EMBEDDED_KEY + endif # BOOT_ENCRYPT_IMAGE config BOOT_ECDSA_PSA_DEPENDENCIES @@ -368,7 +381,7 @@ config BOOT_ED25519_PSA select BOOT_IMG_HASH_ALG_SHA256_ALLOW if !PSA_CORE_LITE select BOOT_IMG_HASH_ALG_SHA512_ALLOW if !PSA_CORE_LITE select BOOT_ED25519_PSA_DEPENDENCIES - select BOOT_X25519_PSA_DEPENDENCIES if BOOT_ENCRYPT_IMAGE + select BOOT_X25519_PSA_DEPENDENCIES if BOOT_ENCRYPT_IMAGE_WITH_SHARED_KEY endchoice @@ -698,7 +711,8 @@ config BOOT_BOOTSTRAP config BOOT_SWAP_SAVE_ENCTLV bool "Save encrypted key TLVs instead of plaintext keys in swap metadata" - depends on BOOT_ENCRYPT_IMAGE + depends on BOOT_ENCRYPT_IMAGE_WITH_SHARED_KEY + depends on !BOOT_ENCRYPT_IMAGE_WITH_EMBEDDED_KEY help If y, instead of saving the encrypted image keys in plaintext in the swap resume metadata, save the encrypted image TLVs. This should be used @@ -766,12 +780,62 @@ config BOOT_ENCRYPTION_SUPPORT help Hidden option used to check if image encryption is supported. -config BOOT_ENCRYPT_IMAGE - bool "Support for encrypted image updates" - depends on BOOT_ENCRYPTION_SUPPORT +config BOOT_ENCRYPT_IMAGE_WITH_EMBEDDED_KEY + bool "Use key that is already on board with MCUboot [EXPERIMENTAL]" + depends on BOOT_ENCRYPT_IMAGE + help + The key is supposed to be either compiled in or on board. + User is responsible for providing boot_enc_take_key + function that will be able to retrieve the key. + +if BOOT_ENCRYPT_IMAGE_WITH_EMBEDDED_KEY + +choice BOOT_ENCRYPT_IMAGE_EMBEDDED_KEY_PROVIDER + prompt "Embedded AES key provider" + default BOOT_ENCRYPT_IMAGE_GENERATE_BASIC_KEY_PROVIDER + +config BOOT_ENCRYPT_IMAGE_GENERATE_BASIC_KEY_PROVIDER + bool "Generate basic boot_enc_take_key" + depends on BOOT_ENCRYPT_IMAGE_WITH_EMBEDDED_KEY + help + Basic implementation of boot_enc_take_key will be implemented, + that will have single key built in, used for all images and + slots. + +config BOOT_ENCRYPT_IMAGE_USE_CUSTOM_KEY_PROVIDER + bool "User provides source code for key provider" + help + User is required to provide implementation for + the boot_enc_take_key function. + +endchoice # BOOT_ENCRYPT_IMAGE_EMBEDDED_KEY_PROVIDER + +config BOOT_ENCRYPT_IMAGE_EMBEDDED_RAW_KEY + string "Hexadecimal string representing AES key [EXPERIMENTAL]" + depends on BOOT_ENCRYPT_IMAGE_GENERATE_BASIC_KEY_PROVIDER + help + AES key in form of hexadecimal string that will be used to + generate boot_enc_take_key function, returning the key for + decryption and encryption of image. + The key character length should be the double of expected + AES key length in bytes. + +endif # BOOT_ENCRYPT_IMAGE_WITH_EMBEDDED_KEY + +config BOOT_ENCRYPT_IMAGE_WITH_SHARED_KEY + bool + default y if !BOOT_ENCRYPT_IMAGE_WITH_EMBEDDED_KEY + depends on BOOT_ENCRYPT_IMAGE select BOOT_ENCRYPT_RSA if BOOT_SIGNATURE_TYPE_RSA select BOOT_ENCRYPT_EC256 if BOOT_SIGNATURE_TYPE_ECDSA_P256 select BOOT_ENCRYPT_X25519 if BOOT_SIGNATURE_TYPE_ED25519 + help + Hidden option for default behaviour where AES encryption key + is derived from Public Key Cryptography key exchange. + +config BOOT_ENCRYPT_IMAGE + bool "Support for encrypted image updates" + depends on BOOT_ENCRYPTION_SUPPORT depends on !SINGLE_APPLICATION_SLOT || MCUBOOT_SERIAL help If y, images in the secondary slot can be encrypted and are decrypted diff --git a/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/boot/zephyr/include/mcuboot_config/mcuboot_config.h index f67eea7a12..317ecb6045 100644 --- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h +++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h @@ -157,6 +157,11 @@ #define MCUBOOT_USE_TLV_ALLOW_LIST 1 #endif +#ifdef CONFIG_BOOT_ENCRYPT_IMAGE_WITH_EMBEDDED_KEY +#define MCUBOOT_ENC_IMAGES +#define MCUBOOT_EMBEDDED_ENC_KEY +#endif + #ifdef CONFIG_BOOT_ENCRYPT_RSA #define MCUBOOT_ENC_IMAGES #define MCUBOOT_ENCRYPT_RSA diff --git a/boot/zephyr/templates/single_builtin_aes_key_provider.c.template b/boot/zephyr/templates/single_builtin_aes_key_provider.c.template new file mode 100644 index 0000000000..ac3f9a78d3 --- /dev/null +++ b/boot/zephyr/templates/single_builtin_aes_key_provider.c.template @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2025 Nordic Semiconductor ASA + * + */ + +#include +#include +#include +#include +#include + +#include "mcuboot_config/mcuboot_config.h" +#include "bootutil/enc_key.h" + +int boot_take_enc_key(uint8_t *key, int image, int slot) +{ + const unsigned char array[] = { + @BOOT_AES_RAW_KEY_HEX_ARRAY@ + }; + + memcpy(key, array, sizeof(array)); + + return 0; +} From 894ec54d9329c4af6b5d4d4b28f1bfe6beaff7ca Mon Sep 17 00:00:00 2001 From: Dominik Ermel Date: Fri, 19 Dec 2025 15:08:09 +0000 Subject: [PATCH 18/18] [nrf noup] bootutil: Use boot_state_init/clear in loader manifest Now boot_state_init is used for state initialization and boot_state_clear to clean it after it is no longer need. Signed-off-by: Dominik Ermel --- boot/bootutil/src/loader_manifest_xip.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/boot/bootutil/src/loader_manifest_xip.c b/boot/bootutil/src/loader_manifest_xip.c index abd60fcb1c..f81880dcbd 100644 --- a/boot/bootutil/src/loader_manifest_xip.c +++ b/boot/bootutil/src/loader_manifest_xip.c @@ -646,9 +646,12 @@ boot_go(struct boot_rsp *rsp) { FIH_DECLARE(fih_rc, FIH_FAILURE); - boot_state_clear(NULL); + boot_state_init(&boot_data); FIH_CALL(context_boot_go, fih_rc, &boot_data, rsp); + + boot_state_clear(&boot_data); + FIH_RET(fih_rc); }