diff --git a/boot/boot_serial/src/boot_serial_encryption.c b/boot/boot_serial/src/boot_serial_encryption.c index ba8ad31496..9be5d0c574 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_take_enc_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); } @@ -232,7 +236,11 @@ decrypt_image_inplace(const struct flash_area *fa_p, } #endif /* 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) { goto total_out; } 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 d3a3ffc855..eca87ef13c 100644 --- a/boot/bootutil/src/bootutil_loader.c +++ b/boot/bootutil/src/bootutil_loader.c @@ -179,7 +179,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 360ea71c0c..f1d0513783 100644 --- a/boot/bootutil/src/bootutil_misc.c +++ b/boot/bootutil/src/bootutil_misc.c @@ -239,7 +239,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 f3eb66297a..228bc4f11b 100644 --- a/boot/bootutil/src/loader.c +++ b/boot/bootutil/src/loader.c @@ -632,6 +632,7 @@ boot_validate_slot(struct boot_loader_state *state, int slot, } #endif if (!boot_check_header_valid(state, slot)) { + BOOT_LOG_DBG("boot_validate_slot: header validation failed %d", slot); fih_rc = FIH_FAILURE; } else { BOOT_HOOK_CALL_FIH(boot_image_check_hook, FIH_BOOT_HOOK_REGULAR, @@ -644,16 +645,16 @@ boot_validate_slot(struct boot_loader_state *state, int slot, check_validity: #endif if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { +#if !defined(__BOOTSIM__) + BOOT_LOG_ERR("Image in the %s slot is not valid!", + (slot == BOOT_SLOT_PRIMARY) ? "primary" : "secondary"); +#endif if ((slot != BOOT_SLOT_PRIMARY) || ARE_SLOTS_EQUIVALENT()) { boot_scramble_slot(fap, slot); /* Image is invalid, erase it to prevent further unnecessary * attempts to validate and boot it. */ } -#if !defined(__BOOTSIM__) - BOOT_LOG_ERR("Image in the %s slot is not valid!", - (slot == BOOT_SLOT_PRIMARY) ? "primary" : "secondary"); -#endif fih_rc = FIH_NO_BOOTABLE_IMAGE; goto out; } @@ -1006,9 +1007,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; @@ -1130,7 +1135,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) { @@ -1154,7 +1163,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) { @@ -1191,7 +1204,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); @@ -1199,7 +1216,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, diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt index 1813ea3217..295decea85 100644 --- a/boot/zephyr/CMakeLists.txt +++ b/boot/zephyr/CMakeLists.txt @@ -393,57 +393,59 @@ if(NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "") 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") + if(CONFIG_BOOT_ENCRYPT_IMAGE_WITH_SHARED_KEY) + # 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() - endif() - endforeach() + endforeach() - 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}) - else() - set(KEY_FILE ${MCUBOOT_DIR}/${CONFIG_BOOT_ENCRYPTION_KEY_FILE}) - endif() - message("MCUBoot bootloader encryption 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}) + else() + set(KEY_FILE ${MCUBOOT_DIR}/${CONFIG_BOOT_ENCRYPTION_KEY_FILE}) + endif() + message("MCUBoot bootloader encryption key file: ${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 + ) - # 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(${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() - if(${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!") + 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 + ${KEY_FILE} + > ${GENERATED_ENCKEY} + DEPENDS ${KEY_FILE} + ) + 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 - ${KEY_FILE} - > ${GENERATED_ENCKEY} - DEPENDS ${KEY_FILE} - ) - zephyr_library_sources(${GENERATED_ENCKEY}) endif() if(CONFIG_MCUBOOT_CLEANUP_ARM_CORE) @@ -731,3 +733,18 @@ if(SYSBUILD) set(mcuboot_image_footer_size ${required_size} CACHE INTERNAL "Estimated MCUboot image trailer size" FORCE) set(mcuboot_image_upgrade_footer_size ${required_upgrade_size} CACHE INTERNAL "Estimated MCUboot update image trailer size" 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 ${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 bb2c87ac2c..9ee501dd42 100644 --- a/boot/zephyr/Kconfig +++ b/boot/zephyr/Kconfig @@ -94,6 +94,8 @@ config BOOT_ED25519_PSA_DEPENDENCIES if BOOT_ENCRYPT_IMAGE +if !BOOT_ENCRYPT_IMAGE_WITH_EMBEDDED_KEY + config BOOT_X25519_PSA_DEPENDENCIES bool select PSA_WANT_ALG_ECDH @@ -111,6 +113,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 @@ -359,7 +363,7 @@ config BOOT_ED25519_PSA select BOOT_IMG_HASH_ALG_SHA256_ALLOW select BOOT_IMG_HASH_ALG_SHA512_ALLOW 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 @@ -595,7 +599,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 @@ -663,12 +668,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" + 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" + 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 5dec8a2577..514ab24cf5 100644 --- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h +++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h @@ -151,6 +151,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; +} diff --git a/scripts/imgtool/image.py b/scripts/imgtool/image.py index 803aff69e0..10aaf59cca 100755 --- a/scripts/imgtool/image.py +++ b/scripts/imgtool/image.py @@ -517,7 +517,36 @@ def create(self, key, public_key_format, enckey, dependencies=None, 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): + + # This is old logic of image creation where lack of enckey indicated + # lack of encryption. + # New create requires a key to be provided from outside. + if 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 + raw_key = os.urandom(encrypt_keylen_bytes) + else: + raw_key = None + + self.create2(key, public_key_format, enckey, dependencies, + sw_type, custom_tlvs, compression_tlvs, + compression_type, raw_key, clear, + fixed_sig, pub_key, vector_to_sign, + user_sha, hmac_sha, is_pure, keep_comp_size, + dont_encrypt) + + def create2(self, key, public_key_format, enckey, dependencies=None, + sw_type=None, custom_tlvs=None, compression_tlvs=None, + compression_type=None, aes_raw=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): self.enckey = enckey + encrypt_keylen = len(aes_raw) * 8 if aes_raw else 0 # key decides on sha, then pub_key; of both are none default is used check_key = key if key is not None else pub_key @@ -620,10 +649,7 @@ 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) + self.add_header(enckey, protected_tlv_size, compression_flags, encrypt_keylen) prot_tlv = TLV(self.endian, TLV_PROT_INFO_MAGIC) @@ -743,11 +769,6 @@ def create(self, key, public_key_format, enckey, dependencies=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) - if not isinstance(enckey, rsa.RSAPublic): if hmac_sha == 'auto' or hmac_sha == '256': hmac_sha = '256' @@ -762,19 +783,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_raw, 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_raw, 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_raw, hmac_sha_alg) enctlv = pubk + mac + cipherkey self.enctlv_len = len(enctlv) if (hmac_sha == '256'): @@ -784,7 +805,7 @@ def create(self, key, public_key_format, enckey, dependencies=None, if not clear: nonce = bytes([0] * 16) - cipher = Cipher(algorithms.AES(plainkey), modes.CTR(nonce), + cipher = Cipher(algorithms.AES(aes_raw), modes.CTR(nonce), backend=default_backend()) encryptor = cipher.encryptor() img = bytes(self.payload[self.header_size:]) @@ -805,15 +826,15 @@ 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, enckey, protected_tlv_size, compression_flags, aes_length=0): """Install the image header.""" flags = 0 - if enckey is not None: - if aes_length == 128: - flags |= IMAGE_F['ENCRYPTED_AES128'] - else: - flags |= IMAGE_F['ENCRYPTED_AES256'] + if aes_length == 128: + flags |= IMAGE_F['ENCRYPTED_AES128'] + elif aes_length == 256: + flags |= IMAGE_F['ENCRYPTED_AES256'] + if self.load_addr != 0: # Indicates that this image should be loaded into RAM # instead of run directly from flash. diff --git a/scripts/imgtool/main.py b/scripts/imgtool/main.py index 3074234e2f..a2fd8c730a 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 @@ -322,6 +323,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' @@ -450,13 +459,17 @@ def convert(self, value, param, ctx): help='Unique vendor identifier, format: (|') @click.option('--cid', default=None, required=False, help='Unique image class identifier, format: (|)') -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): + vector_to_sign, non_bootable, vid, cid, aes_key): if confirm or test: # Confirmed but non-padded images don't make much sense, because @@ -473,16 +486,23 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, 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 + print('Raw AES key overrides --key, there will be no encrypted key added to the image') + if clear: + clear = False + print('Raw AES key overrides --clear, image will be encrypted') + if ctx.get_parameter_source('encrypt_keylen') != click.core.ParameterSource.DEFAULT: + print('Raw AES key len overrides --encrypt-keylen') + if pad_sig and hasattr(key, 'pad_sig'): key.pad_sig = True @@ -527,9 +547,20 @@ 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.') + aes_raw_key = None + if aes_key: + # Converting the command line provided raw AES key to byte array. + aes_raw_key = bytes.fromhex(aes_key) + aes_raw_key_len = len(aes_raw_key) + if aes_raw_key_len not in (16, 32): + raise click.UsageError("Provided keylen, {int(aes_raw_key_len)} in bytes, " + "not supported") + elif enckey: + aes_raw_key = os.urandom(int(int(encrypt_keylen) / 8)) + if compression in ["lzma2", "lzma2armthumb"]: - img.create(key, public_key_format, enckey, dependencies, boot_record, - custom_tlvs, compression_tlvs, None, int(encrypt_keylen), clear, + img.create2(key, public_key_format, enckey, dependencies, boot_record, + custom_tlvs, compression_tlvs, None, aes_raw_key, 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), @@ -573,15 +604,15 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, keep_comp_size = False if enckey: keep_comp_size = True - compressed_img.create(key, public_key_format, enckey, + compressed_img.create2(key, public_key_format, enckey, dependencies, boot_record, custom_tlvs, compression_tlvs, - compression, int(encrypt_keylen), clear, baked_signature, + compression, aes_raw_key, 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, + img.create2(key, public_key_format, enckey, dependencies, boot_record, + custom_tlvs, compression_tlvs, None, aes_raw_key, 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)