diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 000000000..e986738ff --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,31 @@ +name: Backport +on: + pull_request_target: + types: + - closed + - labeled + branches: + - main + +jobs: + backport: + name: Backport + runs-on: ubuntu-22.04 + # Only react to merged PRs for security reasons. + # See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target. + if: > + github.event.pull_request.merged && + ( + github.event.action == 'closed' || + ( + github.event.action == 'labeled' && + contains(github.event.label.name, 'backport') + ) + ) + steps: + - name: Backport + uses: zephyrproject-rtos/action-backport@v2.0.3-3 + with: + github_token: ${{ secrets.NCS_GITHUB_TOKEN }} + issue_labels: Backport + labels_template: '["Backport"]' diff --git a/.github/workflows/commit-tags.yml b/.github/workflows/commit-tags.yml new file mode 100644 index 000000000..534ed5b58 --- /dev/null +++ b/.github/workflows/commit-tags.yml @@ -0,0 +1,28 @@ +name: Commit tags + +on: + pull_request: + types: [synchronize, opened, reopened, edited, labeled, unlabeled, + milestoned, demilestoned, assigned, unassigned, ready_for_review, + review_requested] + +jobs: + commit_tags: + runs-on: ubuntu-22.04 + name: Run commit tags checks on patch series (PR) + steps: + - name: Update PATH for west + run: | + echo "$HOME/.local/bin" >> $GITHUB_PATH + + - name: Checkout the code + uses: actions/checkout@v3 + with: + ref: ${{ github.event.pull_request.head.sha }} + fetch-depth: 0 + + - name: Run the commit tags + uses: nrfconnect/action-commit-tags@main + with: + target: . + upstream: mcu-tools/mcuboot/main diff --git a/boot/bootutil/include/bootutil/bootutil_macros.h b/boot/bootutil/include/bootutil/bootutil_macros.h new file mode 100644 index 000000000..e8d27b792 --- /dev/null +++ b/boot/bootutil/include/bootutil/bootutil_macros.h @@ -0,0 +1,19 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2025 Nordic Semiconductor ASA + * + */ + +#ifndef H_BOOTUTIL_MACROS +#define H_BOOTUTIL_MACROS + +#ifndef ALIGN_UP +#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) +#endif + +#ifndef ALIGN_DOWN +#define ALIGN_DOWN(num, align) ((num) & ~((align) - 1)) +#endif + +#endif diff --git a/boot/bootutil/include/bootutil/bootutil_public.h b/boot/bootutil/include/bootutil/bootutil_public.h index e2795ab3e..421d854b1 100644 --- a/boot/bootutil/include/bootutil/bootutil_public.h +++ b/boot/bootutil/include/bootutil/bootutil_public.h @@ -43,20 +43,13 @@ #include #include #include +#include #include #ifdef __cplusplus extern "C" { #endif -#ifndef ALIGN_UP -#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) -#endif - -#ifndef ALIGN_DOWN -#define ALIGN_DOWN(num, align) ((num) & ~((align) - 1)) -#endif - /** Attempt to boot the contents of the primary slot. */ #define BOOT_SWAP_TYPE_NONE 1 @@ -302,6 +295,12 @@ boot_set_next(const struct flash_area *fa, bool active, bool confirm); /** * Attempts to load image header from flash; verifies flash header fields. * + * The selected update method (i.e. swap move) may impose additional restrictions + * on the image size (i.e. due to the presence of the image trailer). + * Such restrictions are not verified by this function. + * These checks are implemented as part of the boot_image_validate(..) that uses + * sizes from the bootutil_max_image_size(..). + * * @param[in] fa_p flash area pointer * @param[out] hdr buffer for image header * diff --git a/boot/bootutil/include/bootutil/crypto/ecdsa.h b/boot/bootutil/include/bootutil/crypto/ecdsa.h index 3b0541072..85355f20c 100644 --- a/boot/bootutil/include/bootutil/crypto/ecdsa.h +++ b/boot/bootutil/include/bootutil/crypto/ecdsa.h @@ -34,6 +34,7 @@ #if (defined(MCUBOOT_USE_TINYCRYPT) + \ defined(MCUBOOT_USE_CC310) + \ + defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO) + \ defined(MCUBOOT_USE_PSA_OR_MBED_TLS)) != 1 #error "One crypto backend must be defined: either CC310/TINYCRYPT/MBED_TLS/PSA_CRYPTO" #endif @@ -70,12 +71,18 @@ #include "bootutil/sign_key.h" #include "common.h" +#if defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO) + #include + #define NUM_ECC_BYTES (256 / 8) +#endif /* MCUBOOT_USE_NRF_EXTERNAL_CRYPTO */ + #ifdef __cplusplus extern "C" { #endif #if (defined(MCUBOOT_USE_TINYCRYPT) || defined(MCUBOOT_USE_MBED_TLS) || \ - defined(MCUBOOT_USE_CC310)) && !defined(MCUBOOT_USE_PSA_CRYPTO) + defined(MCUBOOT_USE_CC310) || defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO)) \ + && !defined(MCUBOOT_USE_PSA_CRYPTO) /* * Declaring these like this adds NULL termination. */ @@ -127,8 +134,6 @@ static int bootutil_import_key(uint8_t **cp, uint8_t *end) } #endif /* (MCUBOOT_USE_TINYCRYPT || MCUBOOT_USE_MBED_TLS || MCUBOOT_USE_CC310) && !MCUBOOT_USE_PSA_CRYPTO */ -#if defined(MCUBOOT_USE_TINYCRYPT) -#ifndef MCUBOOT_ECDSA_NEED_ASN1_SIG /* * cp points to ASN1 string containing an integer. * Verify the tag, and that the length is 32 bytes. Helper function. @@ -178,8 +183,8 @@ static int bootutil_decode_sig(uint8_t signature[NUM_ECC_BYTES * 2], uint8_t *cp } return 0; } -#endif /* not MCUBOOT_ECDSA_NEED_ASN1_SIG */ +#if defined(MCUBOOT_USE_TINYCRYPT) typedef uintptr_t bootutil_ecdsa_context; static inline void bootutil_ecdsa_init(bootutil_ecdsa_context *ctx) { @@ -248,8 +253,12 @@ static inline int bootutil_ecdsa_verify(bootutil_ecdsa_context *ctx, { (void)ctx; (void)pk_len; - (void)sig_len; (void)hash_len; + uint8_t dsig[2 * NUM_ECC_BYTES]; + + if (bootutil_decode_sig(dsig, sig, sig + sig_len)) { + return -1; + } /* Only support uncompressed keys. */ if (pk[0] != 0x04) { @@ -257,7 +266,7 @@ static inline int bootutil_ecdsa_verify(bootutil_ecdsa_context *ctx, } pk++; - return cc310_ecdsa_verify_secp256r1(hash, pk, sig, BOOTUTIL_CRYPTO_ECDSA_P256_HASH_SIZE); + return cc310_ecdsa_verify_secp256r1(hash, pk, dsig, BOOTUTIL_CRYPTO_ECDSA_P256_HASH_SIZE); } static inline int bootutil_ecdsa_parse_public_key(bootutil_ecdsa_context *ctx, @@ -613,6 +622,49 @@ static inline int bootutil_ecdsa_parse_public_key(bootutil_ecdsa_context *ctx, #endif /* MCUBOOT_USE_MBED_TLS */ +#if defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO) +typedef uintptr_t bootutil_ecdsa_context; +static inline void bootutil_ecdsa_init(bootutil_ecdsa_context *ctx) +{ + (void)ctx; +} + +static inline void bootutil_ecdsa_drop(bootutil_ecdsa_context *ctx) +{ + (void)ctx; +} + +static inline int bootutil_ecdsa_verify(bootutil_ecdsa_context *ctx, + uint8_t *pk, size_t pk_len, + uint8_t *hash, size_t hash_len, + uint8_t *sig, size_t sig_len) +{ + (void)ctx; + (void)pk_len; + (void)hash_len; + uint8_t dsig[2 * NUM_ECC_BYTES]; + + if (bootutil_decode_sig(dsig, sig, sig + sig_len)) { + return -1; + } + + /* Only support uncompressed keys. */ + if (pk[0] != 0x04) { + return -1; + } + pk++; + + return bl_secp256r1_validate(hash, BOOTUTIL_CRYPTO_ECDSA_P256_HASH_SIZE, pk, dsig); +} + +static inline int bootutil_ecdsa_parse_public_key(bootutil_ecdsa_context *ctx, + uint8_t **cp,uint8_t *end) +{ + (void)ctx; + return bootutil_import_key(cp, end); +} +#endif /* MCUBOOT_USE_NRF_EXTERNAL_CRYPTO */ + #ifdef __cplusplus } #endif diff --git a/boot/bootutil/include/bootutil/crypto/rsa.h b/boot/bootutil/include/bootutil/crypto/rsa.h index 581e4ec9b..87ab1de56 100644 --- a/boot/bootutil/include/bootutil/crypto/rsa.h +++ b/boot/bootutil/include/bootutil/crypto/rsa.h @@ -100,12 +100,12 @@ static int bootutil_rsa_oaep_decrypt( return -1; } size_t input_size = PSA_BITS_TO_BYTES(psa_get_key_bits(&key_attr)); - if (input_size != TLV_ENC_RSA_SZ) { + if (input_size != BOOT_ENC_TLV_SIZE) { return -1; } status = psa_asymmetric_decrypt(ctx->key_id, PSA_ALG_RSA_OAEP(PSA_ALG_SHA_256), - input, TLV_ENC_RSA_SZ, NULL, 0, + input, BOOT_ENC_TLV_SIZE, NULL, 0, output, output_max_len, olen); return (int)status; } diff --git a/boot/bootutil/include/bootutil/crypto/sha.h b/boot/bootutil/include/bootutil/crypto/sha.h index 6a009ff95..b83a3ec40 100644 --- a/boot/bootutil/include/bootutil/crypto/sha.h +++ b/boot/bootutil/include/bootutil/crypto/sha.h @@ -30,6 +30,7 @@ #if (defined(MCUBOOT_USE_PSA_OR_MBED_TLS) + \ defined(MCUBOOT_USE_TINYCRYPT) + \ + defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO) + \ defined(MCUBOOT_USE_CC310)) != 1 #error "One crypto backend must be defined: either CC310/MBED_TLS/TINYCRYPT/PSA_CRYPTO" #endif @@ -270,6 +271,37 @@ static inline int bootutil_sha_finish(bootutil_sha_context *ctx, } #endif /* MCUBOOT_USE_CC310 */ +#if defined(MCUBOOT_USE_NRF_EXTERNAL_CRYPTO) + +#include + +typedef bl_sha256_ctx_t bootutil_sha_context; + +static inline void bootutil_sha_init(bootutil_sha_context *ctx) +{ + bl_sha256_init(ctx); +} + +static inline void bootutil_sha_drop(bootutil_sha_context *ctx) +{ + (void)ctx; +} + +static inline int bootutil_sha_update(bootutil_sha_context *ctx, + const void *data, + uint32_t data_len) +{ + return bl_sha256_update(ctx, data, data_len); +} + +static inline int bootutil_sha_finish(bootutil_sha_context *ctx, + uint8_t *output) +{ + bl_sha256_finalize(ctx, output); + return 0; +} +#endif /* MCUBOOT_USE_NRF_EXTERNAL_CRYPTO */ + #ifdef __cplusplus } #endif diff --git a/boot/bootutil/include/bootutil/enc_key.h b/boot/bootutil/include/bootutil/enc_key.h index 9240d699d..85cb7e1b9 100644 --- a/boot/bootutil/include/bootutil/enc_key.h +++ b/boot/bootutil/include/bootutil/enc_key.h @@ -39,8 +39,6 @@ extern "C" { #endif -#define BOOT_ENC_TLV_ALIGN_SIZE ALIGN_UP(BOOT_ENC_TLV_SIZE, BOOT_MAX_ALIGN) - struct enc_key_data { uint8_t valid; bootutil_aes_ctr_context aes_ctr; diff --git a/boot/bootutil/include/bootutil/enc_key_public.h b/boot/bootutil/include/bootutil/enc_key_public.h index 6874cfbc8..4582dfcf7 100644 --- a/boot/bootutil/include/bootutil/enc_key_public.h +++ b/boot/bootutil/include/bootutil/enc_key_public.h @@ -28,37 +28,93 @@ #ifndef BOOTUTIL_ENC_KEY_PUBLIC_H #define BOOTUTIL_ENC_KEY_PUBLIC_H #include +#include + #ifdef __cplusplus extern "C" { #endif -#ifndef ALIGN_UP -#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1)) -#endif +/* The unit provides following system wide definitions: + * BOOT_ENC_TLV_SIZE -- is the complete size of TLV with encryption data. + * BOOT_ENC_TLV -- is the encryption TLV type, should be given value + * of one of IMAGE_TVL_ENC_ identifiers. + * BOOT_ENC_KEY_SIZE -- is the encryption key size; this includes portion + * of TLV data stream taken by key. + * + * For ECIES based key exchange there is additionally provided: + * EC_PUBK_LEN -- is the length, in bytes, of a public key; depends + * selected key exchange. + * EC_PRIVK_LEN -- is the length, in bytes, of a private key; depends + * on selected key exchange. + * EC_SHARED_LEN -- is the length, in bytes, of a shared key resulting + * from processing of private and public key; depends + * on selected key exchange parameters. + * + * ECIES TLV processing uses following TLVs, from this header: + * EC_TAG_INDEX -- is the HMAC tag of encryption key index within TLV data + * stream. + * EC_TAG_LEN -- is the HMAC tag length. + * EC_PUBK_INDEX -- is the index of shared public key within TLV data stream; + * EC_PUBK_LEN represents length in bytes. + * EC_CIPHERKEY_INDEX -- is the encryption key index within TLV data stream. + * EC_CIPHERKEY_LEN -- is the length of an encryption key; depends on selected + * encryption. + * + * Note that in case of ECIES, the BOOT_ENC_TLV_SIZE will be defined as + * a sum of EC_*_LEN TLV components, defined for selected key exchange. + */ #ifdef MCUBOOT_AES_256 -#define BOOT_ENC_KEY_SIZE 32 +# define BOOT_ENC_KEY_SIZE 32 #else -#define BOOT_ENC_KEY_SIZE 16 +# define BOOT_ENC_KEY_SIZE 16 #endif -#define BOOT_ENC_KEY_ALIGN_SIZE ALIGN_UP(BOOT_ENC_KEY_SIZE, BOOT_MAX_ALIGN) - -#define TLV_ENC_RSA_SZ 256 -#define TLV_ENC_KW_SZ (BOOT_ENC_KEY_SIZE + 8) -#define TLV_ENC_EC256_SZ (65 + 32 + BOOT_ENC_KEY_SIZE) -#define TLV_ENC_X25519_SZ (32 + 32 + BOOT_ENC_KEY_SIZE) +#ifdef MCUBOOT_HMAC_SHA512 +# define BOOT_HMAC_SIZE 64 +#else +# define BOOT_HMAC_SIZE 32 +#endif #if defined(MCUBOOT_ENCRYPT_RSA) -#define BOOT_ENC_TLV_SIZE TLV_ENC_RSA_SZ +# define BOOT_ENC_TLV_SIZE (256) +# define BOOT_ENC_TLV IMAGE_TLV_ENC_RSA2048 #elif defined(MCUBOOT_ENCRYPT_EC256) -#define BOOT_ENC_TLV_SIZE TLV_ENC_EC256_SZ +# if defined(MCUBOOT_HMAC_SHA512) +# error "ECIES-P256 does not support HMAC-SHA512" +# endif +# define EC_PUBK_LEN (65) +# define EC_PRIVK_LEN (32) +# define EC_SHARED_LEN (32) +# define BOOT_ENC_TLV IMAGE_TLV_ENC_EC256 #elif defined(MCUBOOT_ENCRYPT_X25519) -#define BOOT_ENC_TLV_SIZE TLV_ENC_X25519_SZ -#else -#define BOOT_ENC_TLV_SIZE TLV_ENC_KW_SZ +# define EC_PUBK_LEN (32) +# define EC_PRIVK_LEN (32) +# define EC_SHARED_LEN (32) +# if !defined(MCUBOOT_HMAC_SHA512) +# define BOOT_ENC_TLV IMAGE_TLV_ENC_X25519 +# else +# define BOOT_ENC_TLV IMAGE_TLV_ENC_X25519_SHA512 +# endif +#elif defined(MCUBOOT_ENCRYPT_KW) +# define BOOT_ENC_TLV_SIZE (BOOT_ENC_KEY_SIZE + 8) +# define BOOT_ENC_TLV IMAGE_TLV_ENC_KW #endif +/* Common ECIES definitions */ +#if defined(EC_PUBK_LEN) +# define EC_PUBK_INDEX (0) +# define EC_TAG_LEN (BOOT_HMAC_SIZE) +# define EC_TAG_INDEX (EC_PUBK_INDEX + EC_PUBK_LEN) +# define EC_CIPHERKEY_INDEX (EC_TAG_INDEX + EC_TAG_LEN) +# define EC_CIPHERKEY_LEN BOOT_ENC_KEY_SIZE +# define EC_SHARED_KEY_LEN (32) +# define BOOT_ENC_TLV_SIZE (EC_PUBK_LEN + EC_TAG_LEN + EC_CIPHERKEY_LEN) +#endif + +#define BOOT_ENC_KEY_ALIGN_SIZE ALIGN_UP(BOOT_ENC_KEY_SIZE, BOOT_MAX_ALIGN) +#define BOOT_ENC_TLV_ALIGN_SIZE ALIGN_UP(BOOT_ENC_TLV_SIZE, BOOT_MAX_ALIGN) + #ifdef __cplusplus } #endif diff --git a/boot/bootutil/include/bootutil/image.h b/boot/bootutil/include/bootutil/image.h index 15de3e01a..11c54b633 100644 --- a/boot/bootutil/include/bootutil/image.h +++ b/boot/bootutil/include/bootutil/image.h @@ -46,8 +46,6 @@ extern "C" { #define STRUCT_PACKED struct __attribute__((__packed__)) #endif -struct flash_area; - #define IMAGE_MAGIC 0x96f3b83d #define IMAGE_MAGIC_V1 0x96f3b83c #define IMAGE_MAGIC_NONE 0xffffffff @@ -98,50 +96,53 @@ struct flash_area; * 1st on identifies the public key which should be used to verify it. * 2nd one is the actual signature. */ -#define IMAGE_TLV_KEYHASH 0x01 /* hash of the public key */ -#define IMAGE_TLV_PUBKEY 0x02 /* public key */ -#define IMAGE_TLV_SHA256 0x10 /* SHA256 of image hdr and body */ -#define IMAGE_TLV_SHA384 0x11 /* SHA384 of image hdr and body */ -#define IMAGE_TLV_SHA512 0x12 /* SHA512 of image hdr and body */ -#define IMAGE_TLV_RSA2048_PSS 0x20 /* RSA2048 of hash output */ -#define IMAGE_TLV_ECDSA224 0x21 /* ECDSA of hash output - Not supported anymore */ -#define IMAGE_TLV_ECDSA_SIG 0x22 /* ECDSA of hash output */ -#define IMAGE_TLV_RSA3072_PSS 0x23 /* RSA3072 of hash output */ -#define IMAGE_TLV_ED25519 0x24 /* ed25519 of hash output */ -#define IMAGE_TLV_SIG_PURE 0x25 /* Indicator that attached signature has been prepared - * over image rather than its digest. - */ -#define IMAGE_TLV_ENC_RSA2048 0x30 /* Key encrypted with RSA-OAEP-2048 */ -#define IMAGE_TLV_ENC_KW 0x31 /* Key encrypted with AES-KW 128 or 256*/ -#define IMAGE_TLV_ENC_EC256 0x32 /* Key encrypted with ECIES-EC256 */ -#define IMAGE_TLV_ENC_X25519 0x33 /* Key encrypted with ECIES-X25519 */ -#define IMAGE_TLV_DEPENDENCY 0x40 /* Image depends on other image */ -#define IMAGE_TLV_SEC_CNT 0x50 /* security counter */ -#define IMAGE_TLV_BOOT_RECORD 0x60 /* measured boot record */ +#define IMAGE_TLV_KEYHASH 0x01 /* hash of the public key */ +#define IMAGE_TLV_PUBKEY 0x02 /* public key */ +#define IMAGE_TLV_SHA256 0x10 /* SHA256 of image hdr and body */ +#define IMAGE_TLV_SHA384 0x11 /* SHA384 of image hdr and body */ +#define IMAGE_TLV_SHA512 0x12 /* SHA512 of image hdr and body */ +#define IMAGE_TLV_RSA2048_PSS 0x20 /* RSA2048 of hash output */ +#define IMAGE_TLV_ECDSA224 0x21 /* ECDSA of hash output - Not supported anymore */ +#define IMAGE_TLV_ECDSA_SIG 0x22 /* ECDSA of hash output */ +#define IMAGE_TLV_RSA3072_PSS 0x23 /* RSA3072 of hash output */ +#define IMAGE_TLV_ED25519 0x24 /* ed25519 of hash output */ +#define IMAGE_TLV_SIG_PURE 0x25 /* Indicator that attached signature has been prepared + * over image rather than its digest. + */ +#define IMAGE_TLV_ENC_RSA2048 0x30 /* Key encrypted with RSA-OAEP-2048 */ +#define IMAGE_TLV_ENC_KW 0x31 /* Key encrypted with AES-KW 128 or 256*/ +#define IMAGE_TLV_ENC_EC256 0x32 /* Key encrypted with ECIES-EC256 */ +#define IMAGE_TLV_ENC_X25519 0x33 /* Key encrypted with ECIES-X25519 */ +#define IMAGE_TLV_ENC_X25519_SHA512 0x34 /* Key exchange using ECIES-X25519 and SHA512 for MAC + * tag and HKDF in key derivation process + */ +#define IMAGE_TLV_DEPENDENCY 0x40 /* Image depends on other image */ +#define IMAGE_TLV_SEC_CNT 0x50 /* security counter */ +#define IMAGE_TLV_BOOT_RECORD 0x60 /* measured boot record */ /* The following flags relate to compressed images and are for the decompressed image data */ -#define IMAGE_TLV_DECOMP_SIZE 0x70 /* Decompressed image size excluding header/TLVs */ -#define IMAGE_TLV_DECOMP_SHA 0x71 /* - * Decompressed image shaX hash, this field must match - * the format and size of the raw slot (compressed) - * shaX hash - */ -#define IMAGE_TLV_DECOMP_SIGNATURE 0x72 /* - * Decompressed image signature, this field must match - * the format and size of the raw slot (compressed) - * signature - */ -#define IMAGE_TLV_COMP_DEC_SIZE 0x73 /* Compressed decrypted image size */ - /* - * vendor reserved TLVs at xxA0-xxFF, - * where xx denotes the upper byte - * range. Examples: - * 0x00a0 - 0x00ff - * 0x01a0 - 0x01ff - * 0x02a0 - 0x02ff - * ... - * 0xffa0 - 0xfffe - */ -#define IMAGE_TLV_ANY 0xffff /* Used to iterate over all TLV */ +#define IMAGE_TLV_DECOMP_SIZE 0x70 /* Decompressed image size excluding header/TLVs */ +#define IMAGE_TLV_DECOMP_SHA 0x71 /* + * Decompressed image shaX hash, this field must match + * the format and size of the raw slot (compressed) + * shaX hash + */ +#define IMAGE_TLV_DECOMP_SIGNATURE 0x72 /* + * Decompressed image signature, this field must match + * the format and size of the raw slot (compressed) + * signature + */ +#define IMAGE_TLV_COMP_DEC_SIZE 0x73 /* Compressed decrypted image size */ + /* + * vendor reserved TLVs at xxA0-xxFF, + * where xx denotes the upper byte + * range. Examples: + * 0x00a0 - 0x00ff + * 0x01a0 - 0x01ff + * 0x02a0 - 0x02ff + * ... + * 0xffa0 - 0xfffe + */ +#define IMAGE_TLV_ANY 0xffff /* Used to iterate over all TLV */ STRUCT_PACKED image_version { uint8_t iv_major; @@ -164,10 +165,10 @@ struct image_dependency { STRUCT_PACKED image_header { uint32_t ih_magic; uint32_t ih_load_addr; - uint16_t ih_hdr_size; /* Size of image header (bytes). */ - uint16_t ih_protect_tlv_size; /* Size of protected TLV area (bytes). */ - uint32_t ih_img_size; /* Does not include header. */ - uint32_t ih_flags; /* IMAGE_F_[...]. */ + uint16_t ih_hdr_size; /* Size of image header (bytes). */ + uint16_t ih_protect_tlv_size; /* Size of protected TLV area (bytes). */ + uint32_t ih_img_size; /* Does not include header. */ + uint32_t ih_flags; /* IMAGE_F_[...]. */ struct image_version ih_ver; uint32_t _pad1; }; @@ -175,13 +176,13 @@ STRUCT_PACKED image_header { /** Image TLV header. All fields in little endian. */ STRUCT_PACKED image_tlv_info { uint16_t it_magic; - uint16_t it_tlv_tot; /* size of TLV area (including tlv_info header) */ + uint16_t it_tlv_tot; /* size of TLV area (including tlv_info header) */ }; /** Image trailer TLV format. All fields in little endian. */ STRUCT_PACKED image_tlv { - uint16_t it_type; /* IMAGE_TLV_[...]. */ - uint16_t it_len; /* Data length (not including TLV header). */ + uint16_t it_type; /* IMAGE_TLV_[...]. */ + uint16_t it_len; /* Data length (not including TLV header). */ }; #define ENCRYPTIONFLAGS (IMAGE_F_ENCRYPTED_AES128 | IMAGE_F_ENCRYPTED_AES256) @@ -199,8 +200,9 @@ STRUCT_PACKED image_tlv { _Static_assert(sizeof(struct image_header) == IMAGE_HEADER_SIZE, "struct image_header not required size"); -struct enc_key_data; struct boot_loader_state; +struct flash_area; + fih_ret bootutil_img_validate(struct boot_loader_state *state, struct image_header *hdr, const struct flash_area *fap, diff --git a/boot/bootutil/include/bootutil/key_revocation.h b/boot/bootutil/include/bootutil/key_revocation.h new file mode 100644 index 000000000..d184c9579 --- /dev/null +++ b/boot/bootutil/include/bootutil/key_revocation.h @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef H_KEY_REVOCATION_ +#define H_KEY_REVOCATION_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define BOOT_KEY_REVOKE_OK 0 +#define BOOT_KEY_REVOKE_NOT_READY 1 +#define BOOT_KEY_REVOKE_INVALID 2 +#define BOOT_KEY_REVOKE_FAILED 2 + + +void allow_revoke(void); + +int revoke(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/boot/bootutil/include/bootutil/security_cnt.h b/boot/bootutil/include/bootutil/security_cnt.h index e1562d2e9..7e1389618 100644 --- a/boot/bootutil/include/bootutil/security_cnt.h +++ b/boot/bootutil/include/bootutil/security_cnt.h @@ -39,6 +39,15 @@ extern "C" { */ fih_ret boot_nv_security_counter_init(void); +/** + * Checks if the specified image should have a security counter present on it or not + * + * @param image_index Index of the image to check (from 0). + * + * @return FIH_SUCCESS if security counter should be present; FIH_FAILURE if otherwise + */ +fih_ret boot_nv_image_should_have_security_counter(uint32_t image_index); + /** * Reads the stored value of a given image's security counter. * diff --git a/boot/bootutil/src/bootutil_misc.c b/boot/bootutil/src/bootutil_misc.c index 0091165d5..96be26692 100644 --- a/boot/bootutil/src/bootutil_misc.c +++ b/boot/bootutil/src/bootutil_misc.c @@ -42,6 +42,15 @@ #ifdef MCUBOOT_ENC_IMAGES #include "bootutil/enc_key.h" #endif +#if defined(MCUBOOT_SWAP_USING_MOVE) || defined(MCUBOOT_SWAP_USING_OFFSET) || \ + defined(MCUBOOT_SWAP_USING_SCRATCH) +#include "swap_priv.h" +#endif + +#if defined(MCUBOOT_DECOMPRESS_IMAGES) +#include +#include +#endif BOOT_LOG_MODULE_DECLARE(mcuboot); @@ -155,6 +164,9 @@ int boot_trailer_scramble_offset(const struct flash_area *fa, size_t alignment, { int ret = 0; + BOOT_LOG_DBG("boot_trailer_scramble_offset: flash_area %p, alignment %u", + fa, (unsigned int)alignment); + /* Not allowed to enforce alignment smaller than device allows */ if (alignment < flash_area_align(fa)) { alignment = flash_area_align(fa); @@ -176,6 +188,9 @@ int boot_trailer_scramble_offset(const struct flash_area *fa, size_t alignment, *off = flash_area_get_size(fa) - ALIGN_DOWN(boot_trailer_sz(alignment), alignment); } + BOOT_LOG_DBG("boot_trailer_scramble_offset: final alignment %u, offset %u", + (unsigned int)alignment, (unsigned int)*off); + return ret; } @@ -187,6 +202,8 @@ int boot_header_scramble_off_sz(const struct flash_area *fa, int slot, size_t *o size_t loff = 0; struct flash_sector sector; + BOOT_LOG_DBG("boot_header_scramble_off_sz: slot %d", slot); + (void)slot; #if defined(MCUBOOT_SWAP_USING_OFFSET) /* In case of swap offset, header of secondary slot image is positioned @@ -215,6 +232,8 @@ int boot_header_scramble_off_sz(const struct flash_area *fa, int slot, size_t *o } *off = loff; + BOOT_LOG_DBG("boot_header_scramble_off_sz: size %u", (unsigned int)*size); + return ret; } @@ -225,8 +244,7 @@ int boot_header_scramble_off_sz(const struct flash_area *fa, int slot, size_t *o * status during the swap of the last sector from primary/secondary (which * is the first swap operation) and thus only requires space for one swap. */ -static uint32_t -boot_scratch_trailer_sz(uint32_t min_write_sz) +uint32_t boot_scratch_trailer_sz(uint32_t min_write_sz) { return boot_status_entry_sz(min_write_sz) + boot_trailer_info_sz(); } @@ -412,106 +430,29 @@ boot_write_enc_key(const struct flash_area *fap, uint8_t slot, } #endif -#ifdef MCUBOOT_SWAP_USING_SCRATCH -size_t -boot_get_first_trailer_sector(struct boot_loader_state *state, size_t slot, size_t trailer_sz) +uint32_t bootutil_max_image_size(struct boot_loader_state *state, const struct flash_area *fap) { - size_t first_trailer_sector = boot_img_num_sectors(state, slot) - 1; - size_t sector_sz = boot_img_sector_size(state, slot, first_trailer_sector); - size_t trailer_sector_sz = sector_sz; - - while (trailer_sector_sz < trailer_sz) { - /* Consider that the image trailer may span across sectors of different sizes */ - --first_trailer_sector; - sector_sz = boot_img_sector_size(state, slot, first_trailer_sector); - - trailer_sector_sz += sector_sz; +#if defined(CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + /* NSIB is a direct upgrade without any status or trailer, get the full size of the + * primary slot. + */ + const struct flash_area *fap_nsib = BOOT_IMG_AREA(state, 0); + assert(fap_nsib != NULL); + + return flash_area_get_size(fap_nsib); } +#endif /* CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 */ - return first_trailer_sector; -} - -/** - * Returns the offset to the end of the first sector of a given slot that holds image trailer data. - * - * @param state Current bootloader's state. - * @param slot The index of the slot to consider. - * @param trailer_sz The size of the trailer, in bytes. - * - * @return The offset to the end of the first sector of the slot that holds image trailer data. - */ -static uint32_t -get_first_trailer_sector_end_off(struct boot_loader_state *state, size_t slot, size_t trailer_sz) -{ - size_t first_trailer_sector = boot_get_first_trailer_sector(state, slot, trailer_sz); - - return boot_img_sector_off(state, slot, first_trailer_sector) + - boot_img_sector_size(state, slot, first_trailer_sector); -} -#endif /* MCUBOOT_SWAP_USING_SCRATCH */ - -uint32_t bootutil_max_image_size(struct boot_loader_state *state, const struct flash_area *fap) -{ #if defined(MCUBOOT_SINGLE_APPLICATION_SLOT) || \ defined(MCUBOOT_FIRMWARE_LOADER) || \ defined(MCUBOOT_SINGLE_APPLICATION_SLOT_RAM_LOAD) (void) state; return boot_status_off(fap); -#elif defined(MCUBOOT_SWAP_USING_SCRATCH) - size_t slot_trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state)); - size_t slot_trailer_off = flash_area_get_size(fap) - slot_trailer_sz; - - /* If the trailer doesn't fit in the last sector of the primary or secondary slot, some padding - * might have to be inserted between the end of the firmware image and the beginning of the - * trailer to ensure there is enough space for the trailer in the scratch area when the last - * sector of the secondary will be copied to the scratch area. - * - * The value of the padding depends on the amount of trailer data that is contained in the first - * trailer containing part of the trailer in the primary and secondary slot. - */ - size_t trailer_sector_primary_end_off = - get_first_trailer_sector_end_off(state, BOOT_PRIMARY_SLOT, slot_trailer_sz); - size_t trailer_sector_secondary_end_off = - get_first_trailer_sector_end_off(state, BOOT_SECONDARY_SLOT, slot_trailer_sz); - - size_t trailer_sz_in_first_sector; - - if (trailer_sector_primary_end_off > trailer_sector_secondary_end_off) { - trailer_sz_in_first_sector = trailer_sector_primary_end_off - slot_trailer_off; - } else { - trailer_sz_in_first_sector = trailer_sector_secondary_end_off - slot_trailer_off; - } - - size_t trailer_padding = 0; - size_t scratch_trailer_sz = boot_scratch_trailer_sz(BOOT_WRITE_SZ(state)); - - if (scratch_trailer_sz > trailer_sz_in_first_sector) { - trailer_padding = scratch_trailer_sz - trailer_sz_in_first_sector; - } - - return slot_trailer_off - trailer_padding; -#elif defined(MCUBOOT_SWAP_USING_MOVE) || defined(MCUBOOT_SWAP_USING_OFFSET) +#elif defined(MCUBOOT_SWAP_USING_MOVE) || defined(MCUBOOT_SWAP_USING_OFFSET) \ + || defined(MCUBOOT_SWAP_USING_SCRATCH) (void) fap; - - /* The slot whose size is used to compute the maximum image size must be the one containing the - * padding required for the swap. */ -#ifdef MCUBOOT_SWAP_USING_MOVE - size_t slot = BOOT_PRIMARY_SLOT; -#else - size_t slot = BOOT_SECONDARY_SLOT; -#endif - - const struct flash_area *fap_padded_slot = BOOT_IMG_AREA(state, slot); - assert(fap_padded_slot != NULL); - - size_t trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state)); - size_t sector_sz = boot_img_sector_size(state, slot, 0); - size_t padding_sz = sector_sz; - - /* The trailer size needs to be sector-aligned */ - trailer_sz = ALIGN_UP(trailer_sz, sector_sz); - - return flash_area_get_size(fap_padded_slot) - trailer_sz - padding_sz; + return app_max_size(state); #elif defined(MCUBOOT_OVERWRITE_ONLY) (void) state; return boot_swap_info_off(fap); @@ -546,35 +487,76 @@ boot_read_image_size(struct boot_loader_state *state, int slot, uint32_t *size) fap = BOOT_IMG_AREA(state, slot); assert(fap != NULL); - off = BOOT_TLV_OFF(boot_img_hdr(state, slot)); +#ifdef MCUBOOT_DECOMPRESS_IMAGES + if (MUST_DECOMPRESS(fap, BOOT_CURR_IMG(state), boot_img_hdr(state, slot))) { + uint32_t tmp_size = 0; - if (flash_area_read(fap, off, &info, sizeof(info))) { - rc = BOOT_EFLASH; - goto done; - } + rc = bootutil_get_img_decomp_size(boot_img_hdr(state, slot), fap, &tmp_size); - protect_tlv_size = boot_img_hdr(state, slot)->ih_protect_tlv_size; - if (info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC) { - if (protect_tlv_size != info.it_tlv_tot) { + if (rc) { rc = BOOT_EBADIMAGE; goto done; } - if (flash_area_read(fap, off + info.it_tlv_tot, &info, sizeof(info))) { + off = boot_img_hdr(state, slot)->ih_hdr_size + tmp_size; + + rc = boot_size_protected_tlvs(boot_img_hdr(state, slot), fap, &tmp_size); + + if (rc) { + rc = BOOT_EBADIMAGE; + goto done; + } + + off += tmp_size; + + if (flash_area_read(fap, (BOOT_TLV_OFF(boot_img_hdr(state, slot)) + + boot_img_hdr(state, slot)->ih_protect_tlv_size), &info, + sizeof(info))) { rc = BOOT_EFLASH; goto done; } - } else if (protect_tlv_size != 0) { - rc = BOOT_EBADIMAGE; - goto done; - } - if (info.it_magic != IMAGE_TLV_INFO_MAGIC) { - rc = BOOT_EBADIMAGE; - goto done; + if (info.it_magic != IMAGE_TLV_INFO_MAGIC) { + rc = BOOT_EBADIMAGE; + goto done; + } + + *size = off + info.it_tlv_tot; + } else { +#else + if (1) { +#endif + off = BOOT_TLV_OFF(boot_img_hdr(state, slot)); + + if (flash_area_read(fap, off, &info, sizeof(info))) { + rc = BOOT_EFLASH; + goto done; + } + + protect_tlv_size = boot_img_hdr(state, slot)->ih_protect_tlv_size; + if (info.it_magic == IMAGE_TLV_PROT_INFO_MAGIC) { + if (protect_tlv_size != info.it_tlv_tot) { + rc = BOOT_EBADIMAGE; + goto done; + } + + if (flash_area_read(fap, off + info.it_tlv_tot, &info, sizeof(info))) { + rc = BOOT_EFLASH; + goto done; + } + } else if (protect_tlv_size != 0) { + rc = BOOT_EBADIMAGE; + goto done; + } + + if (info.it_magic != IMAGE_TLV_INFO_MAGIC) { + rc = BOOT_EBADIMAGE; + goto done; + } + + *size = off + protect_tlv_size + info.it_tlv_tot; } - *size = off + protect_tlv_size + info.it_tlv_tot; rc = 0; done: @@ -601,6 +583,9 @@ boot_erase_region(const struct flash_area *fa, uint32_t off, uint32_t size, bool { int rc = 0; + BOOT_LOG_DBG("boot_erase_region: flash_area %p, offset %d, size %d, backwards == %d", + fa, off, size, (int)backwards); + if (off >= flash_area_get_size(fa) || (flash_area_get_size(fa) - off) < size) { rc = -1; goto end; @@ -608,6 +593,8 @@ boot_erase_region(const struct flash_area *fa, uint32_t off, uint32_t size, bool uint32_t end_offset = 0; struct flash_sector sector; + BOOT_LOG_DBG("boot_erase_region: device with erase"); + if (backwards) { /* Get the lowest page offset first */ rc = flash_area_get_sector(fa, off, §or); @@ -681,6 +668,8 @@ boot_erase_region(const struct flash_area *fa, uint32_t off, uint32_t size, bool off += 1; } } + } else { + BOOT_LOG_DBG("boot_erase_region: device without erase"); } end: diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h index 72e94aee9..5d5a58a04 100644 --- a/boot/bootutil/src/bootutil_priv.h +++ b/boot/bootutil/src/bootutil_priv.h @@ -363,18 +363,14 @@ int boot_read_enc_key(const struct flash_area *fap, uint8_t slot, struct boot_status *bs); #endif -#ifdef MCUBOOT_SWAP_USING_SCRATCH -/** - * Finds the first sector of a given slot that holds image trailer data. - * - * @param state Current bootloader's state. - * @param slot The index of the slot to consider. - * @param trailer_sz The size of the trailer, in bytes. - * - * @return The index of the first sector of the slot that holds image trailer data. +#if MCUBOOT_SWAP_USING_SCRATCH +/* + * Similar to `boot_trailer_sz` but this function returns the space used to + * store status in the scratch partition. The scratch partition only stores + * status during the swap of the last sector from primary/secondary (which + * is the first swap operation) and thus only requires space for one swap. */ -size_t -boot_get_first_trailer_sector(struct boot_loader_state *state, size_t slot, size_t trailer_sz); +uint32_t boot_scratch_trailer_sz(uint32_t min_write_sz); #endif /** diff --git a/boot/bootutil/src/bootutil_public.c b/boot/bootutil/src/bootutil_public.c index 102118864..8860fca41 100644 --- a/boot/bootutil/src/bootutil_public.c +++ b/boot/bootutil/src/bootutil_public.c @@ -324,7 +324,7 @@ boot_write_magic(const struct flash_area *fap) memset(&magic[0], erased_val, sizeof(magic)); memcpy(&magic[BOOT_MAGIC_ALIGN_SIZE - BOOT_MAGIC_SZ], BOOT_IMG_MAGIC, BOOT_MAGIC_SZ); - BOOT_LOG_DBG("writing magic; fa_id=%d off=0x%lx (0x%lx)", + BOOT_LOG_DBG("boot_write_magic: fa_id=%d off=0x%lx (0x%lx)", flash_area_get_id(fap), (unsigned long)off, (unsigned long)(flash_area_get_off(fap) + off)); rc = flash_area_write(fap, pad_off, &magic[0], BOOT_MAGIC_ALIGN_SIZE); @@ -350,9 +350,14 @@ boot_write_trailer(const struct flash_area *fap, uint32_t off, uint32_t align; int rc; + BOOT_LOG_DBG("boot_write_trailer: for %p at %d, size = %d", + fap, off, inlen); + align = flash_area_align(fap); align = ALIGN_UP(inlen, align); if (align > BOOT_MAX_ALIGN) { + /* This should never happen */ + assert(0); return -1; } erased_val = flash_area_erased_val(fap); @@ -596,6 +601,9 @@ boot_set_next(const struct flash_area *fa, bool active, bool confirm) struct boot_swap_state slot_state; int rc; + BOOT_LOG_DBG("boot_set_next: fa %p active == %d, confirm == %d", + fa, (int)active, (int)confirm); + if (active) { /* The only way to set active slot for next boot is to confirm it, * as DirectXIP will conclude that, since slot has not been confirmed @@ -606,6 +614,7 @@ boot_set_next(const struct flash_area *fa, bool active, bool confirm) rc = boot_read_swap_state(fa, &slot_state); if (rc != 0) { + BOOT_LOG_DBG("boot_set_next: error %d reading state", rc); return rc; } @@ -733,6 +742,8 @@ boot_set_confirmed_multi(int image_index) rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index), &fap); if (rc != 0) { + BOOT_LOG_DBG("boot_set_confirmed_multi: error %d opening image %d", + rc, image_index); return BOOT_EFLASH; } @@ -760,13 +771,14 @@ int boot_image_load_header(const struct flash_area *fa_p, struct image_header *hdr) { - uint32_t size; + uint32_t size = 0; int rc = flash_area_read(fa_p, 0, hdr, sizeof *hdr); + BOOT_LOG_DBG("boot_image_load_header: from %p, result %d", fa_p, rc); + if (rc != 0) { - rc = BOOT_EFLASH; BOOT_LOG_ERR("Failed reading image header"); - return BOOT_EFLASH; + return BOOT_EFLASH; } if (hdr->ih_magic != IMAGE_MAGIC) { @@ -783,6 +795,8 @@ boot_image_load_header(const struct flash_area *fa_p, if (!boot_u32_safe_add(&size, hdr->ih_img_size, hdr->ih_hdr_size) || size >= flash_area_get_size(fa_p)) { + BOOT_LOG_ERR("Image size bigger than designated area: %lu > %lu", + (unsigned long)size, (unsigned long)flash_area_get_size(fa_p)); return BOOT_EBADIMAGE; } diff --git a/boot/bootutil/src/ed25519_psa.c b/boot/bootutil/src/ed25519_psa.c index 12ba20ac1..6393d996e 100644 --- a/boot/bootutil/src/ed25519_psa.c +++ b/boot/bootutil/src/ed25519_psa.c @@ -12,6 +12,10 @@ #include #include +#include +#if defined(CONFIG_BOOT_SIGNATURE_USING_KMU) +#include +#endif BOOT_LOG_MODULE_REGISTER(ed25519_psa); @@ -19,6 +23,25 @@ BOOT_LOG_MODULE_REGISTER(ed25519_psa); #define EDDSA_KEY_LENGTH 32 #define EDDSA_SIGNAGURE_LENGTH 64 +#if defined(CONFIG_BOOT_SIGNATURE_USING_KMU) +/* List of KMU stored key ids available for MCUboot */ +#define MAKE_PSA_KMU_KEY_ID(id) PSA_KEY_HANDLE_FROM_CRACEN_KMU_SLOT(CRACEN_KMU_KEY_USAGE_SCHEME_RAW, id) +static psa_key_id_t kmu_key_ids[3] = { + MAKE_PSA_KMU_KEY_ID(226), + MAKE_PSA_KMU_KEY_ID(228), + MAKE_PSA_KMU_KEY_ID(230) +}; + +#if defined(CONFIG_BOOT_KMU_KEYS_REVOCATION) +#include +static psa_key_id_t *validated_with = NULL; +#endif + +BUILD_ASSERT(CONFIG_BOOT_SIGNATURE_KMU_SLOTS <= ARRAY_SIZE(kmu_key_ids), + "Invalid number of KMU slots, up to 3 are supported on nRF54L15"); +#endif + +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) int ED25519_verify(const uint8_t *message, size_t message_len, const uint8_t signature[EDDSA_SIGNAGURE_LENGTH], const uint8_t public_key[EDDSA_KEY_LENGTH]) @@ -29,6 +52,8 @@ int ED25519_verify(const uint8_t *message, size_t message_len, psa_key_id_t kid; int ret = 0; /* Fail by default */ + BOOT_LOG_DBG("ED25519_verify: PSA implementation"); + /* Initialize PSA Crypto */ status = psa_crypto_init(); if (status != PSA_SUCCESS) { @@ -69,3 +94,75 @@ int ED25519_verify(const uint8_t *message, size_t message_len, return ret; } +#else +int ED25519_verify(const uint8_t *message, size_t message_len, + const uint8_t signature[EDDSA_SIGNAGURE_LENGTH], + const uint8_t public_key[EDDSA_KEY_LENGTH]) +{ + ARG_UNUSED(public_key); + /* Set to any error */ + psa_status_t status = PSA_ERROR_BAD_STATE; + int ret = 0; /* Fail by default */ + + /* Initialize PSA Crypto */ + status = psa_crypto_init(); + if (status != PSA_SUCCESS) { + BOOT_LOG_ERR("PSA crypto init failed %d", status); + return 0; + } + + status = PSA_ERROR_BAD_STATE; + + for (int i = 0; i < CONFIG_BOOT_SIGNATURE_KMU_SLOTS; ++i) { + psa_key_id_t kid = kmu_key_ids[i]; + + status = psa_verify_message(kid, PSA_ALG_PURE_EDDSA, message, + message_len, signature, + EDDSA_SIGNAGURE_LENGTH); + if (status == PSA_SUCCESS) { + ret = 1; +#if defined(CONFIG_BOOT_KMU_KEYS_REVOCATION) + validated_with = kmu_key_ids + i; +#endif + break; + } + + BOOT_LOG_ERR("ED25519 signature verification failed %d", status); + } + + return ret; +} +#if defined(CONFIG_BOOT_KMU_KEYS_REVOCATION) +int exec_revoke(void) +{ + int ret = BOOT_KEY_REVOKE_OK; + psa_status_t status = psa_crypto_init(); + + if (!validated_with) { + ret = BOOT_KEY_REVOKE_INVALID; + goto out; + } + + if (status != PSA_SUCCESS) { + BOOT_LOG_ERR("PSA crypto init failed with error %d", status); + ret = BOOT_KEY_REVOKE_FAILED; + goto out; + } + for (int i = 0; i < CONFIG_BOOT_SIGNATURE_KMU_SLOTS; i++) { + if ((kmu_key_ids + i) == validated_with) { + break; + } + BOOT_LOG_DBG("Invalidating key ID %d", i); + + status = psa_destroy_key(kmu_key_ids[i]); + if (status == PSA_SUCCESS) { + BOOT_LOG_DBG("Success on key ID %d", i); + } else { + BOOT_LOG_ERR("Key invalidation failed with: %d", status); + } + } +out: + return ret; +} +#endif /* CONFIG_BOOT_KMU_KEYS_REVOCATION */ +#endif diff --git a/boot/bootutil/src/encrypted.c b/boot/bootutil/src/encrypted.c index bbe40751f..cf6f380e2 100644 --- a/boot/bootutil/src/encrypted.c +++ b/boot/bootutil/src/encrypted.c @@ -43,30 +43,11 @@ #include "bootutil/enc_key.h" #include "bootutil/sign_key.h" #include "bootutil/crypto/common.h" +#include "bootutil/bootutil_log.h" -#include "bootutil_priv.h" - -#define EXPECTED_ENC_LEN BOOT_ENC_TLV_SIZE +BOOT_LOG_MODULE_DECLARE(mcuboot); -#if defined(MCUBOOT_ENCRYPT_RSA) -# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_RSA2048 -#elif defined(MCUBOOT_ENCRYPT_KW) -# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_KW -#elif defined(MCUBOOT_ENCRYPT_EC256) -# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_EC256 -# define EC_PUBK_INDEX (0) -# define EC_TAG_INDEX (65) -# define EC_CIPHERKEY_INDEX (65 + 32) -_Static_assert(EC_CIPHERKEY_INDEX + BOOT_ENC_KEY_SIZE == EXPECTED_ENC_LEN, - "Please fix ECIES-P256 component indexes"); -#elif defined(MCUBOOT_ENCRYPT_X25519) -# define EXPECTED_ENC_TLV IMAGE_TLV_ENC_X25519 -# define EC_PUBK_INDEX (0) -# define EC_TAG_INDEX (32) -# define EC_CIPHERKEY_INDEX (32 + 32) -_Static_assert(EC_CIPHERKEY_INDEX + BOOT_ENC_KEY_SIZE == EXPECTED_ENC_LEN, - "Please fix ECIES-X25519 component indexes"); -#endif +#include "bootutil_priv.h" /* NOUP Fixme: */ #if !defined(CONFIG_BOOT_ED25519_PSA) @@ -104,7 +85,7 @@ key_unwrap(const uint8_t *wrapped, uint8_t *enckey, struct bootutil_key *bootuti if (rc != 0) { goto done; } - rc = bootutil_aes_kw_unwrap(&aes_kw, wrapped, TLV_ENC_KW_SZ, enckey, BOOT_ENC_KEY_SIZE); + rc = bootutil_aes_kw_unwrap(&aes_kw, wrapped, BOOT_ENC_TLV_SIZE, enckey, BOOT_ENC_KEY_SIZE); if (rc != 0) { goto done; } @@ -119,9 +100,6 @@ key_unwrap(const uint8_t *wrapped, uint8_t *enckey, struct bootutil_key *bootuti static const uint8_t ec_pubkey_oid[] = MBEDTLS_OID_EC_ALG_UNRESTRICTED; static const uint8_t ec_secp256r1_oid[] = MBEDTLS_OID_EC_GRP_SECP256R1; -#define SHARED_KEY_LEN NUM_ECC_BYTES -#define PRIV_KEY_LEN NUM_ECC_BYTES - /* * Parses the output of `imgtool keygen`, which produces a PKCS#8 elliptic * curve keypair. See RFC5208 and RFC5915. @@ -201,9 +179,6 @@ parse_ec256_enckey(uint8_t **p, uint8_t *end, uint8_t *private_key) static const uint8_t ec_pubkey_oid[] = MBEDTLS_OID_ISO_IDENTIFIED_ORG \ MBEDTLS_OID_ORG_GOV X25519_OID; -#define SHARED_KEY_LEN 32 -#define PRIV_KEY_LEN 32 - static int parse_x25519_enckey(uint8_t **p, uint8_t *end, uint8_t *private_key) { @@ -243,11 +218,11 @@ parse_x25519_enckey(uint8_t **p, uint8_t *end, uint8_t *private_key) return -7; } - if (len != PRIV_KEY_LEN) { + if (len != EC_PRIVK_LEN) { return -8; } - memcpy(private_key, *p, PRIV_KEY_LEN); + memcpy(private_key, *p, EC_PRIVK_LEN); return 0; } #endif /* defined(MCUBOOT_ENCRYPT_X25519) */ @@ -411,6 +386,8 @@ boot_decrypt_key(const uint8_t *buf, uint8_t *enckey) uint8_t *cpend; size_t olen; #endif + + BOOT_LOG_DBG("boot_decrypt_key"); #if defined(MCUBOOT_ENCRYPT_EC256) bootutil_ecdh_p256_context ecdh_p256; #endif @@ -421,11 +398,11 @@ boot_decrypt_key(const uint8_t *buf, uint8_t *enckey) bootutil_hmac_sha256_context hmac; bootutil_aes_ctr_context aes_ctr; uint8_t tag[BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE]; - uint8_t shared[SHARED_KEY_LEN]; + uint8_t shared[EC_SHARED_LEN]; uint8_t derived_key[BOOT_ENC_KEY_SIZE + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE]; uint8_t *cp; uint8_t *cpend; - uint8_t private_key[PRIV_KEY_LEN]; + uint8_t private_key[EC_PRIVK_LEN]; uint8_t counter[BOOT_ENC_BLOCK_SIZE]; uint16_t len; #endif @@ -531,7 +508,7 @@ boot_decrypt_key(const uint8_t *buf, uint8_t *enckey) */ len = BOOT_ENC_KEY_SIZE + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE; - rc = hkdf(shared, SHARED_KEY_LEN, (uint8_t *)"MCUBoot_ECIES_v1", 16, + rc = hkdf(shared, EC_SHARED_LEN, (uint8_t *)"MCUBoot_ECIES_v1", 16, derived_key, &len); if (rc != 0 || len != (BOOT_ENC_KEY_SIZE + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE)) { return -1; @@ -543,6 +520,9 @@ boot_decrypt_key(const uint8_t *buf, uint8_t *enckey) bootutil_hmac_sha256_init(&hmac); + /* First BOOT_ENC_KEY_SIZE are used for decryption, remaining 32 bytes are used + * for MAC tag key + */ rc = bootutil_hmac_sha256_set_key(&hmac, &derived_key[BOOT_ENC_KEY_SIZE], 32); if (rc != 0) { (void)bootutil_hmac_sha256_drop(&hmac); @@ -562,7 +542,7 @@ boot_decrypt_key(const uint8_t *buf, uint8_t *enckey) return -1; } - if (bootutil_constant_time_compare(tag, &buf[EC_TAG_INDEX], 32) != 0) { + if (bootutil_constant_time_compare(tag, &buf[EC_TAG_INDEX], EC_TAG_LEN) != 0) { (void)bootutil_hmac_sha256_drop(&hmac); return -1; } @@ -621,12 +601,15 @@ boot_enc_load(struct boot_loader_state *state, int slot, #if MCUBOOT_SWAP_SAVE_ENCTLV uint8_t *buf; #else - uint8_t buf[EXPECTED_ENC_LEN]; + uint8_t buf[BOOT_ENC_TLV_SIZE]; #endif int rc; + BOOT_LOG_DBG("boot_enc_load: slot %d", slot); + /* Already loaded... */ if (enc_state[slot].valid) { + BOOT_LOG_DBG("boot_enc_load: already loaded"); return 1; } @@ -641,7 +624,7 @@ boot_enc_load(struct boot_loader_state *state, int slot, #endif #endif - rc = bootutil_tlv_iter_begin(&it, hdr, fap, EXPECTED_ENC_TLV, false); + rc = bootutil_tlv_iter_begin(&it, hdr, fap, BOOT_ENC_TLV, false); if (rc) { return -1; } @@ -651,7 +634,7 @@ boot_enc_load(struct boot_loader_state *state, int slot, return rc; } - if (len != EXPECTED_ENC_LEN) { + if (len != BOOT_ENC_TLV_SIZE) { return -1; } @@ -660,7 +643,7 @@ boot_enc_load(struct boot_loader_state *state, int slot, memset(buf, 0xff, BOOT_ENC_TLV_ALIGN_SIZE); #endif - rc = flash_area_read(fap, off, buf, EXPECTED_ENC_LEN); + rc = flash_area_read(fap, off, buf, BOOT_ENC_TLV_SIZE); if (rc) { return -1; } diff --git a/boot/bootutil/src/encrypted_psa.c b/boot/bootutil/src/encrypted_psa.c index 313372fb1..0cfdc9768 100644 --- a/boot/bootutil/src/encrypted_psa.c +++ b/boot/bootutil/src/encrypted_psa.c @@ -27,22 +27,16 @@ BOOT_LOG_MODULE_DECLARE(mcuboot_psa_enc); -#define EXPECTED_ENC_LEN BOOT_ENC_TLV_SIZE -#define EC_PUBK_INDEX (0) -#define EC_PUBK_LEN (32) -#define EC_TAG_INDEX (EC_PUBK_INDEX + EC_PUBK_LEN) -#define EC_TAG_LEN (32) -#define EC_CIPHERKEY_INDEX (EC_TAG_INDEX + EC_TAG_LEN) -#define EC_CIPHERKEY_LEN BOOT_ENC_KEY_SIZE -_Static_assert(EC_CIPHERKEY_INDEX + BOOT_ENC_KEY_SIZE == EXPECTED_ENC_LEN, - "Please fix ECIES-X25519 component indexes"); +#if defined(MCUBOOT_HMAC_SHA512) +#define PSA_HMAC_HKDF_SHA PSA_ALG_SHA_512 +#else +#define PSA_HMAC_HKDF_SHA PSA_ALG_SHA_256 +#endif #define X25519_OID "\x6e" static const uint8_t ec_pubkey_oid[] = MBEDTLS_OID_ISO_IDENTIFIED_ORG \ MBEDTLS_OID_ORG_GOV X25519_OID; -#define PRIV_KEY_LEN 32 - /* Partitioning of HKDF derived material, from the exchange derived key */ /* AES key encryption key */ #define HKDF_AES_KEY_INDEX 0 @@ -93,11 +87,11 @@ parse_x25519_enckey(uint8_t **p, uint8_t *end, uint8_t *private_key) return -7; } - if (len != PRIV_KEY_LEN) { + if (len != EC_PRIVK_LEN) { return -8; } - memcpy(private_key, *p, PRIV_KEY_LEN); + memcpy(private_key, *p, EC_PRIVK_LEN); return 0; } @@ -127,7 +121,7 @@ boot_decrypt_key(const uint8_t *buf, uint8_t *enckey) uint8_t derived_key[HKDF_SIZE]; uint8_t *cp; uint8_t *cpend; - uint8_t private_key[PRIV_KEY_LEN]; + uint8_t private_key[EC_PRIVK_LEN]; size_t len; psa_status_t psa_ret = PSA_ERROR_BAD_STATE; psa_status_t psa_cleanup_ret = PSA_ERROR_BAD_STATE; @@ -146,9 +140,11 @@ boot_decrypt_key(const uint8_t *buf, uint8_t *enckey) uint8_t iv_and_key[PSA_CIPHER_IV_LENGTH(PSA_KEY_TYPE_AES, PSA_ALG_CTR) + BOOT_ENC_KEY_SIZE]; + BOOT_LOG_DBG("boot_decrypt_key: PSA ED25519"); + psa_ret = psa_crypto_init(); if (psa_ret != PSA_SUCCESS) { - BOOT_LOG_ERR("AES crypto init failed %d", psa_ret); + BOOT_LOG_ERR("PSA crypto init failed %d", psa_ret); return -1; } @@ -172,7 +168,7 @@ boot_decrypt_key(const uint8_t *buf, uint8_t *enckey) return -1; } - key_do_alg = PSA_ALG_KEY_AGREEMENT(PSA_ALG_ECDH, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + key_do_alg = PSA_ALG_KEY_AGREEMENT(PSA_ALG_ECDH, PSA_ALG_HKDF(PSA_HMAC_HKDF_SHA)); psa_ret = psa_key_derivation_setup(&key_do, key_do_alg); if (psa_ret != PSA_SUCCESS) { @@ -235,7 +231,7 @@ boot_decrypt_key(const uint8_t *buf, uint8_t *enckey) */ psa_set_key_type(&kattr, PSA_KEY_TYPE_HMAC); psa_set_key_usage_flags(&kattr, PSA_KEY_USAGE_VERIFY_MESSAGE); - psa_set_key_algorithm(&kattr, PSA_ALG_HMAC(PSA_ALG_SHA_256)); + psa_set_key_algorithm(&kattr, PSA_ALG_HMAC(PSA_HMAC_HKDF_SHA)); /* Import the MAC tag key part of derived key */ psa_ret = psa_import_key(&kattr, @@ -249,7 +245,7 @@ boot_decrypt_key(const uint8_t *buf, uint8_t *enckey) } /* Verify the MAC tag of the random encryption key */ - psa_ret = psa_mac_verify(kid, PSA_ALG_HMAC(PSA_ALG_SHA_256), + psa_ret = psa_mac_verify(kid, PSA_ALG_HMAC(PSA_HMAC_HKDF_SHA), &buf[EC_CIPHERKEY_INDEX], EC_CIPHERKEY_LEN, &buf[EC_TAG_INDEX], EC_TAG_LEN); diff --git a/boot/bootutil/src/image_ecdsa.c b/boot/bootutil/src/image_ecdsa.c index 4604913b4..30c7d0d0f 100644 --- a/boot/bootutil/src/image_ecdsa.c +++ b/boot/bootutil/src/image_ecdsa.c @@ -28,6 +28,9 @@ #include #include "mcuboot_config/mcuboot_config.h" +#include "bootutil/bootutil_log.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); #if defined(MCUBOOT_SIGN_EC256) || defined(MCUBOOT_SIGN_EC384) @@ -46,6 +49,8 @@ bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, size_t slen, uint8_t *pubkey; uint8_t *end; + BOOT_LOG_DBG("bootutil_verify_sig: ECDSA builtin key %d", key_id); + pubkey = (uint8_t *)bootutil_keys[key_id].key; end = pubkey + *bootutil_keys[key_id].len; bootutil_ecdsa_init(&ctx); @@ -75,6 +80,8 @@ bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, size_t slen, bootutil_ecdsa_context ctx; FIH_DECLARE(fih_rc, FIH_FAILURE); + BOOT_LOG_DBG("bootutil_verify_sig: ECDSA embedded key %hhd", key_id); + /* Use builtin key for image verification, no key parsing is required. */ ctx.key_id = key_id; bootutil_ecdsa_init(&ctx); diff --git a/boot/bootutil/src/image_ed25519.c b/boot/bootutil/src/image_ed25519.c index ffb8cec3b..1a02811e3 100644 --- a/boot/bootutil/src/image_ed25519.c +++ b/boot/bootutil/src/image_ed25519.c @@ -21,8 +21,11 @@ #include "bootutil/crypto/common.h" #endif -#include "bootutil_priv.h" +#include "bootutil/bootutil_log.h" #include "bootutil/crypto/sha.h" +#include "bootutil_priv.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); #define EDDSA_SIGNATURE_LENGTH 64 #define NUM_ED25519_BYTES 32 @@ -31,6 +34,7 @@ extern int ED25519_verify(const uint8_t *message, size_t message_len, const uint8_t signature[EDDSA_SIGNATURE_LENGTH], const uint8_t public_key[NUM_ED25519_BYTES]); +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) #if !defined(MCUBOOT_KEY_IMPORT_BYPASS_ASN) /* * Parse the public key used for signing. @@ -73,6 +77,7 @@ bootutil_import_key(uint8_t **cp, uint8_t *end) return 0; } #endif /* !defined(MCUBOOT_KEY_IMPORT_BYPASS_ASN) */ +#endif /* Signature verification base function. * The function takes buffer of specified length and tries to verify @@ -87,20 +92,28 @@ bootutil_verify(uint8_t *buf, uint32_t blen, { int rc; FIH_DECLARE(fih_rc, FIH_FAILURE); - uint8_t *pubkey; + uint8_t *pubkey = NULL; +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) uint8_t *end; +#endif + + BOOT_LOG_DBG("bootutil_verify: ED25519 key_id %d", (int)key_id); if (slen != EDDSA_SIGNATURE_LENGTH) { + BOOT_LOG_DBG("bootutil_verify: expected slen %d, got %u", + EDDSA_SIGNATURE_LENGTH, (unsigned int)slen); FIH_SET(fih_rc, FIH_FAILURE); goto out; } +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) pubkey = (uint8_t *)bootutil_keys[key_id].key; end = pubkey + *bootutil_keys[key_id].len; #if !defined(MCUBOOT_KEY_IMPORT_BYPASS_ASN) rc = bootutil_import_key(&pubkey, end); if (rc) { + BOOT_LOG_DBG("bootutil_verify: import key failed %d", rc); FIH_SET(fih_rc, FIH_FAILURE); goto out; } @@ -110,12 +123,15 @@ bootutil_verify(uint8_t *buf, uint32_t blen, * There is no check whether this is the correct key, * here, by the algorithm selected. */ + BOOT_LOG_DBG("bootutil_verify: bypass ASN1"); if (*bootutil_keys[key_id].len < NUM_ED25519_BYTES) { FIH_SET(fih_rc, FIH_FAILURE); goto out; } pubkey = end - NUM_ED25519_BYTES; +#endif + #endif rc = ED25519_verify(buf, blen, sig, pubkey); @@ -144,7 +160,11 @@ bootutil_verify_sig(uint8_t *hash, uint32_t hlen, { FIH_DECLARE(fih_rc, FIH_FAILURE); + BOOT_LOG_DBG("bootutil_verify_sig: ED25519 key_id %d", (int)key_id); + if (hlen != IMAGE_HASH_SIZE) { + BOOT_LOG_DBG("bootutil_verify_sig: expected hlen %d, got %d", + IMAGE_HASH_SIZE, hlen); FIH_SET(fih_rc, FIH_FAILURE); goto out; } @@ -167,6 +187,8 @@ bootutil_verify_img(uint8_t *img, uint32_t size, { FIH_DECLARE(fih_rc, FIH_FAILURE); + BOOT_LOG_DBG("bootutil_verify_img: ED25519 key_id %d", (int)key_id); + FIH_CALL(bootutil_verify, fih_rc, img, size, sig, slen, key_id); diff --git a/boot/bootutil/src/image_rsa.c b/boot/bootutil/src/image_rsa.c index 778a92b46..5479b75eb 100644 --- a/boot/bootutil/src/image_rsa.c +++ b/boot/bootutil/src/image_rsa.c @@ -28,6 +28,9 @@ #include #include "mcuboot_config/mcuboot_config.h" +#include "bootutil/bootutil_log.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); #ifdef MCUBOOT_SIGN_RSA #include "bootutil_priv.h" @@ -267,6 +270,8 @@ bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, size_t slen, uint8_t *cp; uint8_t *end; + BOOT_LOG_DBG("bootutil_verify_sig: RSA key_id %d", key_id); + bootutil_rsa_init(&ctx); cp = (uint8_t *)bootutil_keys[key_id].key; diff --git a/boot/bootutil/src/image_validate.c b/boot/bootutil/src/image_validate.c index 521251a40..96c1853dd 100644 --- a/boot/bootutil/src/image_validate.c +++ b/boot/bootutil/src/image_validate.c @@ -41,6 +41,14 @@ #include "bootutil/fault_injection_hardening.h" #include "mcuboot_config/mcuboot_config.h" +#include "bootutil/bootutil_log.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +#if defined(MCUBOOT_DECOMPRESS_IMAGES) +#include +#include +#endif #ifdef MCUBOOT_ENC_IMAGES #include "bootutil/enc_key.h" @@ -84,6 +92,10 @@ bootutil_img_hash(struct boot_loader_state *state, uint32_t off; uint32_t blk_sz; #endif +#ifdef MCUBOOT_HASH_STORAGE_DIRECTLY + uintptr_t base = 0; + int fa_ret; +#endif #if defined(MCUBOOT_ENC_IMAGES) struct enc_key_data *enc_state; int image_index; @@ -107,6 +119,7 @@ bootutil_img_hash(struct boot_loader_state *state, (void)tmp_buf_sz; #endif #endif + BOOT_LOG_DBG("bootutil_img_hash"); #ifdef MCUBOOT_ENC_IMAGES if (state == NULL) { @@ -120,6 +133,7 @@ bootutil_img_hash(struct boot_loader_state *state, /* Encrypted images only exist in the secondary slot */ if (MUST_DECRYPT(fap, image_index, hdr) && !boot_enc_valid(enc_state, 1)) { + BOOT_LOG_DBG("bootutil_img_hash: error encrypted image found in primary slot"); return -1; } #endif @@ -155,7 +169,12 @@ bootutil_img_hash(struct boot_loader_state *state, /* No chunk loading, storage is mapped to address space and can * be directly given to hashing function. */ - bootutil_sha_update(&sha_ctx, (void *)flash_area_get_off(fap), size); + fa_ret = flash_device_base(flash_area_get_device_id(fap), &base); + if (fa_ret != 0) { + base = 0; + } + + bootutil_sha_update(&sha_ctx, (void *)(base + flash_area_get_off(fap)), size); #else /* MCUBOOT_HASH_STORAGE_DIRECTLY */ #ifdef MCUBOOT_RAM_LOAD bootutil_sha_update(&sha_ctx, @@ -187,6 +206,8 @@ bootutil_img_hash(struct boot_loader_state *state, #endif if (rc) { bootutil_sha_drop(&sha_ctx); + BOOT_LOG_DBG("bootutil_img_validate Error %d reading data chunk %p %u %u", + rc, fap, off, blk_sz); return rc; } #ifdef MCUBOOT_ENC_IMAGES @@ -271,6 +292,7 @@ bootutil_img_hash(struct boot_loader_state *state, # define KEY_BUF_SIZE (SIG_BUF_SIZE + 24) #endif /* !MCUBOOT_HW_KEY */ +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) #if !defined(MCUBOOT_HW_KEY) static int bootutil_find_key(uint8_t *keyhash, uint8_t keyhash_len) @@ -280,6 +302,8 @@ bootutil_find_key(uint8_t *keyhash, uint8_t keyhash_len) const struct bootutil_key *key; uint8_t hash[IMAGE_HASH_SIZE]; + BOOT_LOG_DBG("bootutil_find_key"); + if (keyhash_len > IMAGE_HASH_SIZE) { return -1; } @@ -308,6 +332,8 @@ bootutil_find_key(uint8_t image_index, uint8_t *key, uint16_t key_len) int rc; FIH_DECLARE(fih_rc, FIH_FAILURE); + BOOT_LOG_DBG("bootutil_find_key: image_index %d", image_index); + bootutil_sha_init(&sha_ctx); bootutil_sha_update(&sha_ctx, key, key_len); bootutil_sha_finish(&sha_ctx, hash); @@ -335,6 +361,7 @@ bootutil_find_key(uint8_t image_index, uint8_t *key, uint16_t key_len) } #endif /* !MCUBOOT_HW_KEY */ #endif /* !MCUBOOT_BUILTIN_KEY */ +#endif /* !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) */ #endif /* EXPECTED_SIG_TLV */ /** @@ -463,7 +490,11 @@ static const uint16_t allowed_unprot_tlvs[] = { IMAGE_TLV_ENC_RSA2048, IMAGE_TLV_ENC_KW, IMAGE_TLV_ENC_EC256, +#if !defined(MCUBOOT_HMAC_SHA512) IMAGE_TLV_ENC_X25519, +#else + IMAGE_TLV_ENC_X25519_SHA512, +#endif /* Mark end with ANY. */ IMAGE_TLV_ANY, }; @@ -483,7 +514,7 @@ bootutil_img_validate(struct boot_loader_state *state, #endif ) { -#if (defined(EXPECTED_KEY_TLV) && defined(MCUBOOT_HW_KEY)) || defined(MCUBOOT_HW_ROLLBACK_PROT) +#if (defined(EXPECTED_KEY_TLV) && defined(MCUBOOT_HW_KEY)) || defined(MCUBOOT_HW_ROLLBACK_PROT) || defined(MCUBOOT_DECOMPRESS_IMAGES) int image_index = (state == NULL ? 0 : BOOT_CURR_IMG(state)); #endif uint32_t off; @@ -516,6 +547,78 @@ bootutil_img_validate(struct boot_loader_state *state, fih_int security_cnt = fih_int_encode(INT_MAX); uint32_t img_security_cnt = 0; FIH_DECLARE(security_counter_valid, FIH_FAILURE); + FIH_DECLARE(security_counter_should_be_present, FIH_FAILURE); + + FIH_CALL(boot_nv_image_should_have_security_counter, security_counter_should_be_present, + image_index); + if (FIH_NOT_EQ(security_counter_should_be_present, FIH_SUCCESS) && + FIH_NOT_EQ(security_counter_should_be_present, FIH_FAILURE)) { + rc = -1; + goto out; + } +#endif + + BOOT_LOG_DBG("bootutil_img_validate: flash area %p", fap); +#ifdef MCUBOOT_DECOMPRESS_IMAGES + /* If the image is compressed, the integrity of the image must also be validated */ + if (MUST_DECOMPRESS(fap, image_index, hdr)) { + bool found_decompressed_size = false; + bool found_decompressed_sha = false; + bool found_decompressed_signature = false; + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, true); + if (rc) { + goto out; + } + + if (it.tlv_end > bootutil_max_image_size(state, fap)) { + rc = -1; + goto out; + } + + while (true) { + uint16_t expected_size = 0; + bool *found_flag = NULL; + + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + break; + } + + switch (type) { + case IMAGE_TLV_DECOMP_SIZE: + expected_size = sizeof(size_t); + found_flag = &found_decompressed_size; + break; + case IMAGE_TLV_DECOMP_SHA: + expected_size = IMAGE_HASH_SIZE; + found_flag = &found_decompressed_sha; + break; + case IMAGE_TLV_DECOMP_SIGNATURE: + found_flag = &found_decompressed_signature; + break; + default: + continue; + }; + + if (type == IMAGE_TLV_DECOMP_SIGNATURE && !EXPECTED_SIG_LEN(len)) { + rc = -1; + goto out; + } else if (type != IMAGE_TLV_DECOMP_SIGNATURE && len != expected_size) { + rc = -1; + goto out; + } + + *found_flag = true; + } + + rc = (!found_decompressed_size || !found_decompressed_sha || !found_decompressed_signature); + if (rc) { + goto out; + } + } #endif #if defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE) @@ -538,7 +641,8 @@ bootutil_img_validate(struct boot_loader_state *state, /* If Pure type signature is expected then it has to be there */ rc = bootutil_check_for_pure(hdr, fap); if (rc != 0) { - goto out; + BOOT_LOG_DBG("bootutil_img_validate: pure expected"); + goto out; } #endif @@ -552,6 +656,7 @@ bootutil_img_validate(struct boot_loader_state *state, rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, false); if (rc) { + BOOT_LOG_DBG("bootutil_img_validate: TLV iteration failed %d", rc); goto out; } @@ -560,9 +665,11 @@ bootutil_img_validate(struct boot_loader_state *state, #else img_sz = it.tlv_end; #endif + BOOT_LOG_DBG("bootutil_img_validate: TLV off %u, end %u", it.tlv_off, it.tlv_end); if (img_sz > bootutil_max_image_size(state, fap)) { rc = -1; + BOOT_LOG_DBG("bootutil_img_validate: TLV beyond image size"); goto out; } @@ -593,6 +700,7 @@ bootutil_img_validate(struct boot_loader_state *state, } } if (!found) { + BOOT_LOG_DBG("bootutil_img_validate: TLV %d not permitted", type); FIH_SET(fih_rc, FIH_FAILURE); goto out; } @@ -602,6 +710,7 @@ bootutil_img_validate(struct boot_loader_state *state, #if defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE) case EXPECTED_HASH_TLV: { + BOOT_LOG_DBG("bootutil_img_validate: EXPECTED_HASH_TLV == %d", EXPECTED_HASH_TLV); /* Verify the image hash. This must always be present. */ if (len != sizeof(hash)) { rc = -1; @@ -622,9 +731,11 @@ bootutil_img_validate(struct boot_loader_state *state, break; } #endif /* defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE) */ +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) #ifdef EXPECTED_KEY_TLV case EXPECTED_KEY_TLV: { + BOOT_LOG_DBG("bootutil_img_validate: EXPECTED_KEY_TLV == %d", EXPECTED_KEY_TLV); /* * Determine which key we should be checking. */ @@ -652,14 +763,18 @@ bootutil_img_validate(struct boot_loader_state *state, break; } #endif /* EXPECTED_KEY_TLV */ +#endif /* !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) */ #ifdef EXPECTED_SIG_TLV case EXPECTED_SIG_TLV: { + BOOT_LOG_DBG("bootutil_img_validate: EXPECTED_SIG_TLV == %d", EXPECTED_SIG_TLV); +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) /* Ignore this signature if it is out of bounds. */ if (key_id < 0 || key_id >= bootutil_key_cnt) { key_id = -1; continue; } +#endif /* !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) */ if (!EXPECTED_SIG_LEN(len) || len > sizeof(buf)) { rc = -1; goto out; @@ -702,6 +817,10 @@ bootutil_img_validate(struct boot_loader_state *state, goto out; } + if (FIH_EQ(security_counter_should_be_present, FIH_FAILURE)) { + goto skip_security_counter_read; + } + FIH_CALL(boot_nv_security_counter_get, fih_rc, image_index, &security_cnt); if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { @@ -721,6 +840,7 @@ bootutil_img_validate(struct boot_loader_state *state, /* The image's security counter has been successfully verified. */ security_counter_valid = fih_rc; +skip_security_counter_read: break; } #endif /* MCUBOOT_HW_ROLLBACK_PROT */ @@ -740,10 +860,173 @@ bootutil_img_validate(struct boot_loader_state *state, FIH_SET(fih_rc, valid_signature); #endif #ifdef MCUBOOT_HW_ROLLBACK_PROT + if (FIH_EQ(security_counter_should_be_present, FIH_FAILURE)) { + goto skip_security_counter_check; + } + if (FIH_NOT_EQ(security_counter_valid, FIH_SUCCESS)) { rc = -1; goto out; } + +skip_security_counter_check: +#endif + +#ifdef MCUBOOT_DECOMPRESS_IMAGES + /* Only after all previous verifications have passed, perform a dry-run of the decompression + * and ensure the image is valid + */ + if (!rc && MUST_DECOMPRESS(fap, image_index, hdr)) { + image_hash_valid = 0; + FIH_SET(valid_signature, FIH_FAILURE); + + rc = bootutil_img_hash_decompress(state, hdr, fap, tmp_buf, tmp_buf_sz, + hash, seed, seed_len); + if (rc) { + goto out; + } + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_DECOMP_SHA, true); + if (rc) { + goto out; + } + + if (it.tlv_end > bootutil_max_image_size(state, fap)) { + rc = -1; + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + break; + } + + if (type == IMAGE_TLV_DECOMP_SHA) { + /* Verify the image hash. This must always be present. */ + if (len != sizeof(hash)) { + rc = -1; + goto out; + } + rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, sizeof(hash)); + if (rc) { + goto out; + } + + FIH_CALL(boot_fih_memequal, fih_rc, hash, buf, sizeof(hash)); + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS)) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + + image_hash_valid = 1; + } + } + + rc = !image_hash_valid; + if (rc) { + goto out; + } + +#ifdef EXPECTED_SIG_TLV +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) && defined(EXPECTED_KEY_TLV) + rc = bootutil_tlv_iter_begin(&it, hdr, fap, EXPECTED_KEY_TLV, false); + if (rc) { + goto out; + } + + if (it.tlv_end > bootutil_max_image_size(state, fap)) { + rc = -1; + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + break; + } + + if (type == EXPECTED_KEY_TLV) { + /* + * Determine which key we should be checking. + */ + if (len > KEY_BUF_SIZE) { + rc = -1; + goto out; + } +#ifndef MCUBOOT_HW_KEY + rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, len); + if (rc) { + goto out; + } + key_id = bootutil_find_key(buf, len); +#else + rc = LOAD_IMAGE_DATA(hdr, fap, off, key_buf, len); + if (rc) { + goto out; + } + key_id = bootutil_find_key(image_index, key_buf, len); +#endif /* !MCUBOOT_HW_KEY */ + /* + * The key may not be found, which is acceptable. There + * can be multiple signatures, each preceded by a key. + */ + } + } +#endif /* !CONFIG_BOOT_SIGNATURE_USING_KMU && EXPECTED_KEY_TLV */ + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_DECOMP_SIGNATURE, true); + if (rc) { + goto out; + } + + if (it.tlv_end > bootutil_max_image_size(state, fap)) { + rc = -1; + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } + + if (type == IMAGE_TLV_DECOMP_SIGNATURE) { + /* Ignore this signature if it is out of bounds. */ +#if !defined(CONFIG_BOOT_SIGNATURE_USING_KMU) + if (key_id < 0 || key_id >= bootutil_key_cnt) { + key_id = -1; + continue; + } +#endif + + if (!EXPECTED_SIG_LEN(len) || len > sizeof(buf)) { + rc = -1; + goto out; + } + rc = LOAD_IMAGE_DATA(hdr, fap, off, buf, len); + if (rc) { + goto out; + } + + FIH_CALL(bootutil_verify_sig, valid_signature, hash, sizeof(hash), + buf, len, key_id); + key_id = -1; + } + } +#endif /* EXPECTED_SIG_TLV */ + } +#endif + +#ifdef EXPECTED_SIG_TLV + FIH_SET(fih_rc, valid_signature); #endif out: diff --git a/boot/bootutil/src/key_revocation.c b/boot/bootutil/src/key_revocation.c new file mode 100644 index 000000000..0768a3188 --- /dev/null +++ b/boot/bootutil/src/key_revocation.c @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include + +extern int exec_revoke(void); + +static uint8_t ready_to_revoke; + +void allow_revoke(void) +{ + ready_to_revoke = 1; +} + +int revoke(void) +{ + if (ready_to_revoke) { + return exec_revoke(); + } + return BOOT_KEY_REVOKE_NOT_READY; +} diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c index ffd2456a7..09271a9c1 100644 --- a/boot/bootutil/src/loader.c +++ b/boot/bootutil/src/loader.c @@ -50,6 +50,26 @@ #include "bootutil/boot_hooks.h" #include "bootutil/mcuboot_status.h" +#if defined(MCUBOOT_DECOMPRESS_IMAGES) +#include +#include +#endif + +#ifdef __ZEPHYR__ +#include +#if defined(CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS) +#include +#endif /* CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS */ +#endif + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(PM_CPUNET_B0N_ADDRESS) +#include +#ifdef CONFIG_PCD_READ_NETCORE_APP_VERSION +#include +int pcd_version_cmp_net(const struct flash_area *fap, struct image_header *hdr); +#endif +#endif + #ifdef MCUBOOT_ENC_IMAGES #include "bootutil/enc_key.h" #endif @@ -60,9 +80,16 @@ #include "mcuboot_config/mcuboot_config.h" +#if defined(CONFIG_BOOT_KEYS_REVOCATION) +#include "bootutil/key_revocation.h" +#endif + BOOT_LOG_MODULE_DECLARE(mcuboot); static struct boot_loader_state boot_data; +#ifdef PM_S1_ADDRESS +static bool owner_nsib[BOOT_IMAGE_NUMBER] = {false}; +#endif #if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) || defined(MCUBOOT_DATA_SHARING) static struct image_max_size image_max_sizes[BOOT_IMAGE_NUMBER] = {0}; @@ -86,6 +113,17 @@ static struct sector_buffer_t sector_buffers; #endif #endif +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 && defined(MCUBOOT_OVERWRITE_ONLY) && \ + defined(MCUBOOT_DOWNGRADE_PREVENTION) +/* s0/s1 package version of the current MCUboot image */ +static const struct image_version mcuboot_s0_s1_image_version = { + .iv_major = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_MAJOR, + .iv_minor = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_MINOR, + .iv_revision = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_REVISION, + .iv_build_num = CONFIG_MCUBOOT_MCUBOOT_S0_S1_VERSION_BUILD_NUMBER, +}; +#endif + #if (BOOT_IMAGE_NUMBER > 1) #define IMAGES_ITER(x) for ((x) = 0; (x) < BOOT_IMAGE_NUMBER; ++(x)) #else @@ -145,6 +183,15 @@ boot_read_image_headers(struct boot_loader_state *state, bool require_all, * * Failure to read any headers is a fatal error. */ +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + /* Patch needed for NCS. The primary slot of the second image + * (image 1) will not contain a valid image header until an upgrade + * of mcuboot has happened (filling S1 with the new version). + */ + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER && i == 0) { + continue; + } +#endif /* CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 */ if (i > 0 && !require_all) { return 0; } else { @@ -283,6 +330,19 @@ static int boot_version_cmp(const struct image_version *ver1, const struct image_version *ver2) { +#if !defined(MCUBOOT_VERSION_CMP_USE_BUILD_NUMBER) + BOOT_LOG_DBG("boot_version_cmp: ver1 %u.%u.%u vs ver2 %u.%u.%u", + (unsigned)ver1->iv_major, (unsigned)ver1->iv_minor, + (unsigned)ver1->iv_revision, (unsigned)ver2->iv_major, + (unsigned)ver2->iv_minor, (unsigned)ver2->iv_revision); +#else + BOOT_LOG_DBG("boot_version_cmp: ver1 %u.%u.%u.%u vs ver2 %u.%u.%u.%u", + (unsigned)ver1->iv_major, (unsigned)ver1->iv_minor, + (unsigned)ver1->iv_revision, (unsigned)ver1->iv_build_num, + (unsigned)ver2->iv_major, (unsigned)ver2->iv_minor, + (unsigned)ver2->iv_revision, (unsigned)ver2->iv_build_num); +#endif + if (ver1->iv_major > ver2->iv_major) { return 1; } @@ -479,7 +539,7 @@ boot_verify_dependencies(struct boot_loader_state *state) if (rc == 0) { /* All dependencies've been satisfied, continue with next image. */ BOOT_CURR_IMG(state)++; - } else { + } else if (rc == BOOT_EBADIMAGE) { /* Cannot upgrade due to non-met dependencies, so disable all * image upgrades. */ @@ -488,7 +548,10 @@ boot_verify_dependencies(struct boot_loader_state *state) BOOT_SWAP_TYPE(state) = BOOT_SWAP_TYPE_NONE; } break; - } + } else { + /* Other error happened, images are inconsistent */ + return rc; + } } return rc; } @@ -554,6 +617,7 @@ boot_verify_slot_dependencies(struct boot_loader_state *state, uint32_t slot) fap = BOOT_IMG_AREA(state, slot); assert(fap != NULL); + BOOT_LOG_DBG("boot_verify_slot_dependencies"); #if defined(MCUBOOT_SWAP_USING_OFFSET) it.start_off = boot_get_state_secondary_offset(state, fap); #endif @@ -581,6 +645,8 @@ boot_verify_slot_dependencies(struct boot_loader_state *state, uint32_t slot) rc = LOAD_IMAGE_DATA(boot_img_hdr(state, slot), fap, off, &dep, len); if (rc != 0) { + BOOT_LOG_DBG("boot_verify_slot_dependencies: error %d reading dependency %p %d %d", + rc, fap, off, len); rc = BOOT_EFLASH; goto done; } @@ -593,6 +659,7 @@ boot_verify_slot_dependencies(struct boot_loader_state *state, uint32_t slot) /* Verify dependency and modify the swap type if not satisfied. */ rc = boot_verify_slot_dependency(state, &dep); if (rc != 0) { + BOOT_LOG_DBG("boot_verify_slot_dependencies: not satisfied"); /* Dependency not satisfied */ goto done; } @@ -798,14 +865,42 @@ boot_image_check(struct boot_loader_state *state, struct image_header *hdr, } #endif + for (int i = 1; i <= CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT; i++ ) { +#if CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 + BOOT_LOG_DBG("Image validation attempt %d/%d", i, CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT); +#endif /* CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 */ + #if defined(MCUBOOT_SWAP_USING_OFFSET) && defined(MCUBOOT_SERIAL_RECOVERY) - FIH_CALL(bootutil_img_validate, fih_rc, state, hdr, fap, tmpbuf, BOOT_TMPBUF_SZ, - NULL, 0, NULL, 0); + FIH_CALL(bootutil_img_validate, fih_rc, state, hdr, fap, tmpbuf, BOOT_TMPBUF_SZ, + NULL, 0, NULL, 0); #else - FIH_CALL(bootutil_img_validate, fih_rc, state, hdr, fap, tmpbuf, BOOT_TMPBUF_SZ, - NULL, 0, NULL); + FIH_CALL(bootutil_img_validate, fih_rc, state, hdr, fap, tmpbuf, BOOT_TMPBUF_SZ, + NULL, 0, NULL); #endif + if (FIH_EQ(fih_rc, FIH_SUCCESS)) { +#if CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 + BOOT_LOG_DBG("Image validation attempt %d/%d success", i, CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT); +#endif /* CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 */ + break; + } else { +#if CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 + BOOT_LOG_WRN("Image validation attempt %d/%d failure: %d", + i, + CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT, fih_rc); +#endif /* CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 */ + + if (i < CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT) { +#if defined(CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS) +#if CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 + BOOT_LOG_DBG("Waiting %d ms before next attempt", + CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS); +#endif /* CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 */ + k_busy_wait(CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS * 1000); +#endif /* CONFIG_NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS */ + } + } + } FIH_RET(fih_rc); } @@ -906,10 +1001,10 @@ boot_is_header_valid(const struct image_header *hdr, const struct flash_area *fa return false; } #else - if ((hdr->ih_flags & IMAGE_F_COMPRESSED_LZMA1) && - (hdr->ih_flags & IMAGE_F_COMPRESSED_LZMA2)) - { - return false; + if (MUST_DECOMPRESS(fap, BOOT_CURR_IMG(state), hdr)) { + if (!boot_is_compressed_header_valid(hdr, fap, state)) { + return false; + } } #endif @@ -1005,6 +1100,9 @@ boot_validate_slot(struct boot_loader_state *state, int slot, struct image_header *hdr; FIH_DECLARE(fih_rc, FIH_FAILURE); + BOOT_LOG_DBG("boot_validate_slot: slot %d, expected_swap_type %d", + slot, expected_swap_type); + #if !defined(MCUBOOT_SWAP_USING_OFFSET) (void)expected_swap_type; #endif @@ -1075,9 +1173,55 @@ boot_validate_slot(struct boot_loader_state *state, int slot, int rc; /* Check if version of secondary slot is sufficient */ - rc = boot_version_cmp( - &boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, - &boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver); + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) \ + && defined(CONFIG_PCD_APP) && defined(CONFIG_PCD_READ_NETCORE_APP_VERSION) + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { + rc = pcd_version_cmp_net(fap, boot_img_hdr(state, BOOT_SECONDARY_SLOT)); + } else { + rc = boot_version_cmp( + &boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, + &boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver); + +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (rc >= 0 && BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + /* Also check the new version of MCUboot against that of the current s0/s1 MCUboot + * trailer version to prevent downgrades + */ + int version_check; + + version_check = boot_version_cmp(&boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, + &mcuboot_s0_s1_image_version); + + /* Only update rc if the currently running version is newer */ + if (version_check < rc) { + rc = version_check; + } + } +#endif + } +#else + rc = boot_version_cmp( + &boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, + &boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver); + +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (rc >= 0 && BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + /* Also check the new version of MCUboot against that of the current s0/s1 MCUboot + * trailer version to prevent downgrades + */ + int version_check; + + version_check = boot_version_cmp(&boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, + &mcuboot_s0_s1_image_version); + + /* Only update rc if the currently running version is newer */ + if (version_check < rc) { + rc = version_check; + } + } +#endif +#endif if (rc < 0 && boot_check_header_erased(state, BOOT_PRIMARY_SLOT)) { BOOT_LOG_ERR("insufficient version in secondary slot"); boot_scramble_slot(fap, slot); @@ -1108,6 +1252,7 @@ boot_validate_slot(struct boot_loader_state *state, int slot, * attempts to validate and boot it. */ } + #if !defined(__BOOTSIM__) BOOT_LOG_ERR("Image in the %s slot is not valid!", (slot == BOOT_PRIMARY_SLOT) ? "primary" : "secondary"); @@ -1122,18 +1267,69 @@ boot_validate_slot(struct boot_loader_state *state, int slot, * overwriting an application written to the incorrect slot. * This feature is only supported by ARM platforms. */ +#if MCUBOOT_IMAGE_NUMBER >= 3 + /* Currently the MCUboot can be configured for up to 3 image, where image number 2 is + * designated for XIP, where it is the second part of image stored in slots of image + * 0. This part of image is not bootable, as the XIP setup is done by the app in + * image 0 slot, and it does not carry the reset vector. + */ + if (fap == state->imgs[2][BOOT_SECONDARY_SLOT].area) { + goto out; + } +#endif if (fap == BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT)) { const struct flash_area *pri_fa = BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT); struct image_header *secondary_hdr = boot_img_hdr(state, slot); uint32_t reset_value = 0; uint32_t reset_addr = secondary_hdr->ih_hdr_size + sizeof(reset_value); + uint32_t min_addr, max_addr; + bool check_addresses = false; if (flash_area_read(fap, reset_addr, &reset_value, sizeof(reset_value)) != 0) { fih_rc = FIH_NO_BOOTABLE_IMAGE; goto out; } - if (reset_value < pri_fa->fa_off || reset_value> (pri_fa->fa_off + pri_fa->fa_size)) { +#ifdef PM_CPUNET_APP_ADDRESS + /* The primary slot for the network core is emulated in RAM. + * Its flash_area hasn't got relevant boundaries. + * Therfore need to override its boundaries for the check. + */ + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { + min_addr = PM_CPUNET_APP_ADDRESS; + max_addr = PM_CPUNET_APP_ADDRESS + PM_CPUNET_APP_SIZE; + check_addresses = true; + } else +#endif +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + min_addr = PM_S0_ADDRESS; + max_addr = (PM_S0_ADDRESS + PM_S0_SIZE); +#else + min_addr = PM_S1_ADDRESS; + max_addr = (PM_S1_ADDRESS + PM_S1_SIZE); +#endif + check_addresses = true; + } else +#endif + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) { +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + min_addr = MIN(pri_fa->fa_off, PM_S0_ADDRESS); + max_addr = MAX((pri_fa->fa_off + pri_fa->fa_size), (PM_S0_ADDRESS + PM_S0_SIZE)); +#else + min_addr = MIN(pri_fa->fa_off, PM_S1_ADDRESS); + max_addr = MAX((pri_fa->fa_off + pri_fa->fa_size), (PM_S1_ADDRESS + PM_S1_SIZE)); +#endif +#else + min_addr = pri_fa->fa_off; + max_addr = pri_fa->fa_off + pri_fa->fa_size; +#endif + check_addresses = true; + } + + if (check_addresses == true && (reset_value < min_addr || reset_value > max_addr)) { BOOT_LOG_ERR("Reset address of image in secondary slot is not in the primary slot"); BOOT_LOG_ERR("Erasing image from secondary slot"); @@ -1155,6 +1351,38 @@ boot_validate_slot(struct boot_loader_state *state, int slot, } #ifdef MCUBOOT_HW_ROLLBACK_PROT +/** + * Checks if the specified image should have a security counter present on it or not + * + * @param image_index Index of the image to check. + * + * @return true if security counter should be present; false if otherwise + */ +fih_ret boot_nv_image_should_have_security_counter(uint32_t image_index) +{ +#if defined(PM_S1_ADDRESS) + if (owner_nsib[image_index]) { + /* + * Downgrade prevention on S0/S1 image is managed by NSIB, which is a software (not + * hardware) check + */ + return FIH_FAILURE; + } +#endif + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER != -1 + if (image_index == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { + /* + * Downgrade prevention on network core image is managed by NSIB which is a software (not + * hardware) check + */ + return FIH_FAILURE; + } +#endif + + return FIH_SUCCESS; +} + /** * Updates the stored security counter value with the image's security counter * value which resides in the given slot, only if it's greater than the stored @@ -1176,6 +1404,26 @@ boot_update_security_counter(struct boot_loader_state *state, int slot, int hdr_ uint32_t img_security_cnt; int rc; +#if defined(PM_S1_ADDRESS) + if (owner_nsib[BOOT_CURR_IMG(state)]) { + /* + * Downgrade prevention on S0/S1 image is managed by NSIB which is a software (not + * hardware) check + */ + return 0; + } +#endif + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { + /* + * Downgrade prevention on network core image is managed by NSIB which is a software (not + * hardware) check + */ + return 0; + } +#endif + fap = BOOT_IMG_AREA(state, slot); assert(fap != NULL); @@ -1195,6 +1443,109 @@ boot_update_security_counter(struct boot_loader_state *state, int slot, int hdr_ #endif /* MCUBOOT_HW_ROLLBACK_PROT */ #if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) + +#if defined(CONFIG_MCUBOOT_CLEANUP_UNUSABLE_SECONDARY) &&\ +(defined(PM_S1_ADDRESS) || defined(CONFIG_SOC_NRF5340_CPUAPP)) + +#define SEC_SLOT_VIRGIN 0 +#define SEC_SLOT_TOUCHED 1 +#define SEC_SLOT_ASSIGNED 2 + +static uint8_t sec_slot_assignment[MCUBOOT_IMAGE_NUMBER] = {0}; + +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 +static inline void sec_slot_untouch(struct boot_loader_state *state) +{ + sec_slot_assignment[CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER] = SEC_SLOT_VIRGIN; + sec_slot_assignment[CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER] = SEC_SLOT_VIRGIN; +} +#else +static inline void sec_slot_untouch(struct boot_loader_state *state) +{ +} +#endif + +static inline void sec_slot_touch(struct boot_loader_state *state) +{ +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + if (sec_slot_assignment[CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER] == SEC_SLOT_VIRGIN) { + sec_slot_assignment[CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER] = SEC_SLOT_TOUCHED; + } + } else if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) { + if (sec_slot_assignment[CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER] == SEC_SLOT_VIRGIN) { + sec_slot_assignment[CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER] = SEC_SLOT_TOUCHED; + } + } +#endif + + if (sec_slot_assignment[BOOT_CURR_IMG(state)] == SEC_SLOT_VIRGIN) { + sec_slot_assignment[BOOT_CURR_IMG(state)] = SEC_SLOT_TOUCHED; + } +} + +static inline void sec_slot_mark_assigned(struct boot_loader_state *state) +{ +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + sec_slot_assignment[CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER] = SEC_SLOT_ASSIGNED; + } else if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) { + sec_slot_assignment[CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER] = SEC_SLOT_ASSIGNED; + } +#endif + + sec_slot_assignment[BOOT_CURR_IMG(state)] = SEC_SLOT_ASSIGNED; +} + +/** + * Cleanup up all secondary slot which couldn't be assigned to any primary slot. + * + * This function erases content of each secondary slot which contains valid + * header but couldn't be assigned to any of supported primary images. + * + * This function is supposed to be called after boot_validated_swap_type() + * iterates over all the images in context_boot_go(). + */ +static void sec_slot_cleanup_if_unusable(void) +{ + uint8_t idx; + + for (idx = 0; idx < MCUBOOT_IMAGE_NUMBER; idx++) { + if (SEC_SLOT_TOUCHED == sec_slot_assignment[idx]) { + const struct flash_area *secondary_fa; + int rc; + + rc = flash_area_open(flash_area_id_from_multi_image_slot(idx, BOOT_SECONDARY_SLOT), + &secondary_fa); + if (!rc) { + rc = flash_area_erase(secondary_fa, 0, secondary_fa->fa_size); + if (!rc) { + BOOT_LOG_ERR("Cleaned-up secondary slot of image %d", idx); + } + } + + if (rc) { + BOOT_LOG_ERR("Failed to clean-up secondary slot of image %d: %d", idx, rc); + } + } + } +} +#else +static inline void sec_slot_untouch(struct boot_loader_state *state) +{ +} +static inline void sec_slot_touch(struct boot_loader_state *state) +{ +} +static inline void sec_slot_mark_assigned(struct boot_loader_state *state) +{ +} +static inline void sec_slot_cleanup_if_unusable(void) +{ +} +#endif /* defined(CONFIG_MCUBOOT_CLEANUP_UNUSABLE_SECONDARY) &&\ + defined(PM_S1_ADDRESS) || defined(CONFIG_SOC_NRF5340_CPUAPP) */ + /** * Determines which swap operation to perform, if any. If it is determined * that a swap operation is required, the image in the secondary slot is checked @@ -1209,6 +1560,87 @@ boot_validated_swap_type(struct boot_loader_state *state, { int swap_type; FIH_DECLARE(fih_rc, FIH_FAILURE); + bool upgrade_valid = false; +#if defined(PM_S1_ADDRESS) + owner_nsib[BOOT_CURR_IMG(state)] = false; +#endif + +#if defined(PM_S1_ADDRESS) || defined(PM_CPUNET_B0N_ADDRESS) + const struct flash_area *secondary_fa = + BOOT_IMG_AREA(state, BOOT_SECONDARY_SLOT); + struct image_header *hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + uint32_t reset_addr = 0; + int rc = 0; + /* Patch needed for NCS. Since image 0 (the app) and image 1 (the other + * B1 slot S0 or S1) share the same secondary slot, we need to check + * whether the update candidate in the secondary slot is intended for + * image 0 or image 1 primary by looking at the address of the reset + * vector. Note that there are good reasons for not using img_num from + * the swap info. + */ + + if (hdr->ih_magic == IMAGE_MAGIC) { + rc = flash_area_read(secondary_fa, hdr->ih_hdr_size + + sizeof(uint32_t), &reset_addr, + sizeof(reset_addr)); + if (rc != 0) { + return BOOT_SWAP_TYPE_FAIL; + } + + sec_slot_touch(state); + +#ifdef PM_S1_ADDRESS +#ifdef PM_CPUNET_B0N_ADDRESS + if(!(reset_addr >= PM_CPUNET_APP_ADDRESS && reset_addr < PM_CPUNET_APP_END_ADDRESS)) +#endif + { + const struct flash_area *primary_fa; + rc = flash_area_open(flash_area_id_from_multi_image_slot( + BOOT_CURR_IMG(state), BOOT_PRIMARY_SLOT), + &primary_fa); + if (rc != 0) { + return BOOT_SWAP_TYPE_FAIL; + } + + /* Check start and end of primary slot for current image */ +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + if (reset_addr >= PM_S0_ADDRESS && reset_addr <= (PM_S0_ADDRESS + PM_S0_SIZE)) { +#else + if (reset_addr >= PM_S1_ADDRESS && reset_addr <= (PM_S1_ADDRESS + PM_S1_SIZE)) { +#endif + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_APPLICATION_IMAGE_NUMBER) { + /* This is not the s0/s1 upgrade image but the application image, pretend + * there is no image so the NSIB update can be loaded + */ + return BOOT_SWAP_TYPE_NONE; + } + + owner_nsib[BOOT_CURR_IMG(state)] = true; +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + } else if (reset_addr >= PM_S1_ADDRESS && reset_addr <= (PM_S1_ADDRESS + PM_S1_SIZE)) { +#else + } else if (reset_addr >= PM_S0_ADDRESS && reset_addr <= (PM_S0_ADDRESS + PM_S0_SIZE)) { +#endif + /* NSIB upgrade but for the wrong slot, must be erased */ + BOOT_LOG_ERR("Image in slot is for wrong s0/s1 image"); + flash_area_erase(secondary_fa, 0, secondary_fa->fa_size); + sec_slot_untouch(state); + BOOT_LOG_ERR("Cleaned-up secondary slot of image %d", BOOT_CURR_IMG(state)); + return BOOT_SWAP_TYPE_FAIL; + } else if (reset_addr < primary_fa->fa_off || reset_addr > (primary_fa->fa_off + primary_fa->fa_size)) { + /* The image in the secondary slot is not intended for any */ + return BOOT_SWAP_TYPE_NONE; + } + + if ((primary_fa->fa_off == PM_S0_ADDRESS) || (primary_fa->fa_off == PM_S1_ADDRESS)) { + owner_nsib[BOOT_CURR_IMG(state)] = true; + } + } +#endif /* PM_S1_ADDRESS */ + sec_slot_mark_assigned(state); + } + +#endif /* PM_S1_ADDRESS || PM_CPUNET_B0N_ADDRESS */ swap_type = boot_swap_type_multi(BOOT_CURR_IMG(state)); if (BOOT_IS_UPGRADE(swap_type)) { @@ -1222,7 +1654,42 @@ boot_validated_swap_type(struct boot_loader_state *state, } else { swap_type = BOOT_SWAP_TYPE_FAIL; } + } else { + upgrade_valid = true; } + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(PM_CPUNET_B0N_ADDRESS) \ + && !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) && defined(CONFIG_PCD_APP) + /* If the update is valid, and it targets the network core: perform the + * update and indicate to the caller of this function that no update is + * available + */ + if (upgrade_valid && reset_addr >= PM_CPUNET_APP_ADDRESS && + reset_addr < PM_CPUNET_APP_END_ADDRESS) { + struct image_header *hdr = (struct image_header *)secondary_fa->fa_off; + uint32_t vtable_addr = (uint32_t)hdr + hdr->ih_hdr_size; + uint32_t *net_core_fw_addr = (uint32_t *)(vtable_addr); + uint32_t fw_size = hdr->ih_img_size; + BOOT_LOG_INF("Starting network core update"); + rc = pcd_network_core_update(net_core_fw_addr, fw_size); + + if (rc != 0) { + swap_type = BOOT_SWAP_TYPE_FAIL; + } else { + BOOT_LOG_INF("Done updating network core"); +#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) + /* swap_erase_trailer_sectors is undefined if upgrade only + * method is used. There is no need to erase sectors, because + * the image cannot be reverted. + */ + rc = swap_erase_trailer_sectors(state, + secondary_fa); +#endif + swap_type = BOOT_SWAP_TYPE_NONE; + } + } +#endif /* CONFIG_SOC_NRF5340_CPUAPP && PM_CPUNET_B0N_ADDRESS && + !CONFIG_NRF53_MULTI_IMAGE_UPDATE && CONFIG_PCD_APP */ } return swap_type; @@ -1250,6 +1717,8 @@ boot_scramble_region(const struct flash_area *fa, uint32_t off, uint32_t size, b { int rc = 0; + BOOT_LOG_DBG("boot_scramble_region: %p %d %d %d", fa, off, size, (int)backwards); + if (size == 0) { goto done; } @@ -1264,6 +1733,7 @@ boot_scramble_region(const struct flash_area *fa, uint32_t off, uint32_t size, b const size_t write_block = flash_area_align(fa); uint32_t end_offset; + BOOT_LOG_DBG("boot_scramble_region: device without erase, overwriting"); memset(buf, flash_area_erased_val(fa), sizeof(buf)); if (backwards) { @@ -1273,11 +1743,14 @@ boot_scramble_region(const struct flash_area *fa, uint32_t off, uint32_t size, b } else { end_offset = ALIGN_DOWN((off + size), write_block); } + BOOT_LOG_DBG("boot_scramble_region: start offset %u, end offset %u", off, end_offset); - while (true) { + while (off != end_offset) { /* Write over the area to scramble data that is there */ rc = flash_area_write(fa, off, buf, write_block); if (rc != 0) { + BOOT_LOG_DBG("boot_scramble_region: error %d for %p %d %u", + rc, fa, off, (unsigned int)write_block); break; } @@ -1291,12 +1764,12 @@ boot_scramble_region(const struct flash_area *fa, uint32_t off, uint32_t size, b off -= write_block; } else { - if (end_offset < off) { + off += write_block; + + if (end_offset <= off) { /* Reached the end offset in range and already scrambled it */ break; } - - off += write_block; } } } @@ -1436,6 +1909,9 @@ boot_copy_region(struct boot_loader_state *state, #else (void)state; #endif +#if defined(MCUBOOT_DECOMPRESS_IMAGES) && !defined(MCUBOOT_ENC_IMAGES) + struct image_header *hdr; +#endif TARGET_STATIC uint8_t buf[BUF_SZ] __attribute__((aligned(4))); @@ -1461,6 +1937,16 @@ boot_copy_region(struct boot_loader_state *state, } #endif +#ifdef MCUBOOT_DECOMPRESS_IMAGES + hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + + if (MUST_DECOMPRESS(fap_src, BOOT_CURR_IMG(state), hdr)) { + /* Use alternative function for compressed images */ + return boot_copy_region_decompress(state, fap_src, fap_dst, off_src, off_dst, sz, buf, + BUF_SZ); + } +#endif + bytes_copied = 0; while (bytes_copied < sz) { if (sz - bytes_copied > sizeof buf) { @@ -1687,19 +2173,19 @@ boot_copy_image(struct boot_loader_state *state, struct boot_status *bs) * trailer that was left might trigger a new upgrade. */ BOOT_LOG_DBG("erasing secondary header"); - rc = boot_erase_region(fap_secondary_slot, - boot_img_sector_off(state, BOOT_SECONDARY_SLOT, 0), - boot_img_sector_size(state, BOOT_SECONDARY_SLOT, 0), false); + rc = boot_scramble_region(fap_secondary_slot, + boot_img_sector_off(state, BOOT_SECONDARY_SLOT, 0), + boot_img_sector_size(state, BOOT_SECONDARY_SLOT, 0), false); assert(rc == 0); #endif last_sector = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT) - 1; BOOT_LOG_DBG("erasing secondary trailer"); - rc = boot_erase_region(fap_secondary_slot, - boot_img_sector_off(state, BOOT_SECONDARY_SLOT, - last_sector), - boot_img_sector_size(state, BOOT_SECONDARY_SLOT, - last_sector), false); + rc = boot_scramble_region(fap_secondary_slot, + boot_img_sector_off(state, BOOT_SECONDARY_SLOT, + last_sector), + boot_img_sector_size(state, BOOT_SECONDARY_SLOT, + last_sector), false); assert(rc == 0); /* TODO: Perhaps verify the primary slot's signature again? */ @@ -1839,7 +2325,22 @@ boot_swap_image(struct boot_loader_state *state, struct boot_status *bs) flash_area_close(fap); } - swap_run(state, bs, copy_size); +#if defined(PM_S1_ADDRESS) && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 + if (owner_nsib[BOOT_CURR_IMG(state)]) { + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER) { + /* For NSIB, move the image instead of swapping it */ + nsib_swap_run(state, bs); + +#if defined(CONFIG_REBOOT) + /* Should also reboot at this point so the new S0/S1 update is applied */ + sys_reboot(SYS_REBOOT_COLD); +#endif + } + } else +#endif + { + swap_run(state, bs, copy_size); + } #ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT extern int boot_status_fails; @@ -2301,6 +2802,26 @@ check_downgrade_prevention(struct boot_loader_state *state) uint32_t security_counter[2]; int rc; +#if defined(PM_S1_ADDRESS) + if (owner_nsib[BOOT_CURR_IMG(state)]) { + /* + * Downgrade prevention on S0/S1 image is managed by NSIB which is a software (not + * hardware) check + */ + return 0; + } +#endif + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER != -1 + if (BOOT_CURR_IMG(state) == CONFIG_MCUBOOT_NETWORK_CORE_IMAGE_NUMBER) { + /* + * Downgrade prevention on network core image is managed by NSIB which is a software (not + * hardware) check + */ + return 0; + } +#endif + if (MCUBOOT_DOWNGRADE_PREVENTION_SECURITY_COUNTER) { /* If there was security no counter in slot 0, allow swap */ rc = bootutil_get_img_security_cnt(state, BOOT_PRIMARY_SLOT, @@ -2348,6 +2869,8 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) bool has_upgrade; volatile int fih_cnt; + BOOT_LOG_DBG("context_boot_go"); + #if defined(__BOOTSIM__) /* The array of slot sectors are defined here (as opposed to file scope) so * that they don't get allocated for non-boot-loader apps. This is @@ -2438,6 +2961,9 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) } } + /* cleanup secondary slots which were recognized unusable*/ + sec_slot_cleanup_if_unusable(); + #if (BOOT_IMAGE_NUMBER > 1) if (has_upgrade) { /* Iterate over all the images and verify whether the image dependencies @@ -2537,6 +3063,11 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) } } +#if defined(CONFIG_BOOT_KEYS_REVOCATION) + if (BOOT_SWAP_TYPE(state) == BOOT_SWAP_TYPE_NONE) { + allow_revoke(); + } +#endif /* Iterate over all the images. At this point all required update operations * have finished. By the end of the loop each image in the primary slot will * have been re-validated. @@ -2575,16 +3106,30 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) */ } -#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT - FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, NULL, 0); - /* Check for all possible values is redundant in normal operation it - * is meant to prevent FI attack. +#ifdef PM_S1_ADDRESS + /* Patch needed for NCS. Image 1 primary is the currently + * executing MCUBoot image, and is therefore already validated by NSIB and + * does not need to also be validated by MCUBoot. */ - if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS) || - FIH_EQ(fih_rc, FIH_FAILURE) || - FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) { - FIH_SET(fih_rc, FIH_FAILURE); - goto out; + bool image_validated_by_nsib = BOOT_CURR_IMG(state) == + CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER; +#endif + +#ifdef MCUBOOT_VALIDATE_PRIMARY_SLOT +#ifdef PM_S1_ADDRESS + if (!image_validated_by_nsib) +#endif + { + FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, NULL, 0); + /* Check for all possible values is redundant in normal operation it + * is meant to prevent FI attack. + */ + if (FIH_NOT_EQ(fih_rc, FIH_SUCCESS) || + FIH_EQ(fih_rc, FIH_FAILURE) || + FIH_EQ(fih_rc, FIH_NO_BOOTABLE_IMAGE)) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } } #else /* Even if we're not re-validating the primary slot, we could be booting @@ -2601,11 +3146,16 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) } #endif /* MCUBOOT_VALIDATE_PRIMARY_SLOT */ +#ifdef PM_S1_ADDRESS + if (!image_validated_by_nsib) +#endif + { rc = boot_update_hw_rollback_protection(state); if (rc != 0) { FIH_SET(fih_rc, FIH_FAILURE); goto out; } + } rc = boot_add_shared_data(state, BOOT_PRIMARY_SLOT); if (rc != 0) { @@ -2626,6 +3176,13 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) fill_rsp(state, rsp); fih_rc = FIH_SUCCESS; +#if defined(CONFIG_BOOT_KEYS_REVOCATION) + rc = revoke(); + if (rc != BOOT_KEY_REVOKE_OK && + rc != BOOT_KEY_REVOKE_NOT_READY) { + FIH_SET(fih_rc, FIH_FAILURE); + } +#endif /* CONFIG_BOOT_KEYS_REVOCATION */ out: /* * Since the boot_status struct stores plaintext encryption keys, reset @@ -3216,6 +3773,8 @@ static void boot_fetch_slot_state_sizes(void) assert(rc == 0); if (rc != 0) { + BOOT_LOG_DBG("boot_fetch_slot_state_sizes: error %d for %d", + rc, fa_id); goto finish; } } diff --git a/boot/bootutil/src/swap_misc.c b/boot/bootutil/src/swap_misc.c index ac9412b0f..a4c39c20a 100644 --- a/boot/bootutil/src/swap_misc.c +++ b/boot/bootutil/src/swap_misc.c @@ -80,7 +80,7 @@ swap_scramble_trailer_sectors(const struct boot_loader_state *state, size_t off; int rc; - BOOT_LOG_DBG("Scrambling trailer; fa_id=%d", flash_area_get_id(fap)); + BOOT_LOG_DBG("swap_scramble_trailer_sectors: fa_id=%d", flash_area_get_id(fap)); /* Delete starting from last sector and moving to beginning */ rc = boot_trailer_scramble_offset(fap, BOOT_WRITE_SZ(state), &off); diff --git a/boot/bootutil/src/swap_move.c b/boot/bootutil/src/swap_move.c index 9349cde37..ed0947076 100644 --- a/boot/bootutil/src/swap_move.c +++ b/boot/bootutil/src/swap_move.c @@ -227,50 +227,36 @@ boot_status_internal_off(const struct boot_status *bs, int elem_sz) return off; } -static int app_max_sectors(struct boot_loader_state *state) -{ - uint32_t sz = 0; - uint32_t sector_sz; - uint32_t trailer_sz; - uint32_t first_trailer_idx; - - sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0); - trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state)); - /* subtract 1 for swap and at least 1 for trailer */ - first_trailer_idx = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - 2; - - while (1) { - sz += sector_sz; - if (sz >= trailer_sz) { - break; - } - first_trailer_idx--; - } - - return first_trailer_idx; -} - int boot_slots_compatible(struct boot_loader_state *state) { +#ifdef PM_S1_ADDRESS + /* Patch needed for NCS. In this case, image 1 primary points to the other + * B1 slot (ie S0 or S1), and image 0 primary points to the app. + * With this configuration, image 0 and image 1 share the secondary slot. + * Hence, the primary slot of image 1 will be *smaller* than image 1's + * secondary slot. This is not allowed in upstream mcuboot, so we need + * this patch to allow it. Also, all of these checks are redundant when + * partition manager is in use, and since we have the same sector size + * in all of our flash. + */ + return 1; +#else size_t num_sectors_pri; size_t num_sectors_sec; size_t sector_sz_pri = 0; size_t sector_sz_sec = 0; size_t i; - size_t num_usable_sectors_pri; num_sectors_pri = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT); num_sectors_sec = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT); - num_usable_sectors_pri = app_max_sectors(state); if ((num_sectors_pri != num_sectors_sec) && - (num_sectors_pri != (num_sectors_sec + 1)) && - (num_usable_sectors_pri != (num_sectors_sec + 1))) { + (num_sectors_pri != (num_sectors_sec + 1))) { BOOT_LOG_WRN("Cannot upgrade: not a compatible amount of sectors"); BOOT_LOG_DBG("slot0 sectors: %d, slot1 sectors: %d, usable slot0 sectors: %d", (int)num_sectors_pri, (int)num_sectors_sec, - (int)(num_usable_sectors_pri - 1)); + (int)(num_sectors_pri - 1)); return 0; } else if (num_sectors_pri > BOOT_MAX_IMG_SECTORS) { BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed"); @@ -280,7 +266,7 @@ boot_slots_compatible(struct boot_loader_state *state) /* Optimal says primary has one more than secondary. Always. Both have trailers. */ if (num_sectors_pri != (num_sectors_sec + 1)) { BOOT_LOG_DBG("Non-optimal sector distribution, slot0 has %d usable sectors (%d assigned) " - "but slot1 has %d assigned", (int)num_usable_sectors_pri, + "but slot1 has %d assigned", (int)(num_sectors_pri - 1), (int)num_sectors_pri, (int)num_sectors_sec); } @@ -321,6 +307,7 @@ boot_slots_compatible(struct boot_loader_state *state) } return 1; +#endif /* PM_S1_ADDRESS */ } #define BOOT_LOG_SWAP_STATE(area, state) \ @@ -340,7 +327,6 @@ swap_status_source(struct boot_loader_state *state) struct boot_swap_state state_primary_slot; struct boot_swap_state state_secondary_slot; int rc; - uint8_t source; uint8_t image_index; #if (BOOT_IMAGE_NUMBER == 1) @@ -365,10 +351,8 @@ swap_status_source(struct boot_loader_state *state) state_primary_slot.copy_done == BOOT_FLAG_UNSET && state_secondary_slot.magic != BOOT_MAGIC_GOOD) { - source = BOOT_STATUS_SOURCE_PRIMARY_SLOT; - BOOT_LOG_INF("Boot source: primary slot"); - return source; + return BOOT_STATUS_SOURCE_PRIMARY_SLOT; } BOOT_LOG_INF("Boot source: none"); @@ -590,11 +574,23 @@ swap_run(struct boot_loader_state *state, struct boot_status *bs, int app_max_size(struct boot_loader_state *state) { - uint32_t sector_sz_primary; + uint32_t available_pri_sz; + uint32_t available_sec_sz; + + size_t trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state)); + size_t sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0); + size_t padding_sz = sector_sz; - sector_sz_primary = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0); + /* The trailer size needs to be sector-aligned */ + trailer_sz = ALIGN_UP(trailer_sz, sector_sz); + + /* The slot whose size is used to compute the maximum image size must be the one containing the + * padding required for the swap. + */ + available_pri_sz = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) * sector_sz - trailer_sz - padding_sz; + available_sec_sz = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT) * sector_sz - trailer_sz; - return app_max_sectors(state) * sector_sz_primary; + return (available_pri_sz < available_sec_sz ? available_pri_sz : available_sec_sz); } #endif diff --git a/boot/bootutil/src/swap_nsib.c b/boot/bootutil/src/swap_nsib.c new file mode 100644 index 000000000..079e193d5 --- /dev/null +++ b/boot/bootutil/src/swap_nsib.c @@ -0,0 +1,70 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include "bootutil/bootutil.h" +#include "bootutil_priv.h" +#include "swap_priv.h" +#include "bootutil/bootutil_log.h" + +#include "mcuboot_config/mcuboot_config.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +void nsib_swap_run(struct boot_loader_state *state, struct boot_status *bs) +{ + uint32_t sector_sz; + uint8_t image_index; + const struct flash_area *fap_pri; + const struct flash_area *fap_sec; + int rc; + + BOOT_LOG_INF("Starting swap using nsib algorithm."); + + sector_sz = boot_img_sector_size(state, BOOT_SECONDARY_SLOT, 0); + +#if (CONFIG_NCS_IS_VARIANT_IMAGE) + rc = flash_area_open(PM_S0_ID, &fap_pri); +#else + rc = flash_area_open(PM_S1_ID, &fap_pri); +#endif + assert (rc == 0); + image_index = BOOT_CURR_IMG(state); + + rc = flash_area_open(FLASH_AREA_IMAGE_SECONDARY(image_index), &fap_sec); + assert (rc == 0); + + rc = boot_erase_region(fap_pri, 0, fap_pri->fa_size, false); + assert(rc == 0); + + rc = boot_copy_region(state, fap_sec, fap_pri, 0, 0, fap_pri->fa_size); + assert(rc == 0); + + rc = swap_scramble_trailer_sectors(state, fap_sec); + assert(rc == 0); + + rc = boot_scramble_region(fap_sec, 0, MIN((fap_pri->fa_size + sector_sz), fap_sec->fa_size), false); + assert(rc == 0); + + flash_area_close(fap_pri); + flash_area_close(fap_sec); +} diff --git a/boot/bootutil/src/swap_offset.c b/boot/bootutil/src/swap_offset.c index 8bc6e760a..d8bfac318 100644 --- a/boot/bootutil/src/swap_offset.c +++ b/boot/bootutil/src/swap_offset.c @@ -302,33 +302,6 @@ uint32_t boot_status_internal_off(const struct boot_status *bs, int elem_sz) return off; } -static int app_max_sectors(struct boot_loader_state *state) -{ - uint32_t sz = 0; - uint32_t sector_sz; - uint32_t trailer_sz; - uint32_t available_sectors_pri; - uint32_t available_sectors_sec; - uint32_t trailer_sectors = 0; - - sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0); - trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state)); - - while (1) { - sz += sector_sz; - ++trailer_sectors; - - if (sz >= trailer_sz) { - break; - } - } - - available_sectors_pri = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) - trailer_sectors; - available_sectors_sec = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT) - 1; - - return (available_sectors_pri < available_sectors_sec ? available_sectors_pri : available_sectors_sec); -} - int boot_slots_compatible(struct boot_loader_state *state) { size_t num_sectors_pri; @@ -336,32 +309,30 @@ int boot_slots_compatible(struct boot_loader_state *state) size_t sector_sz_pri = 0; size_t sector_sz_sec = 0; size_t i; - size_t num_usable_sectors; num_sectors_pri = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT); num_sectors_sec = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT); - num_usable_sectors = app_max_sectors(state); if (num_sectors_pri != num_sectors_sec && - (num_sectors_pri + 1) != num_sectors_sec && - num_usable_sectors != (num_sectors_sec - 1)) { + (num_sectors_pri + 1) != num_sectors_sec) { BOOT_LOG_WRN("Cannot upgrade: not a compatible amount of sectors"); BOOT_LOG_DBG("slot0 sectors: %d, slot1 sectors: %d, usable sectors: %d", (int)num_sectors_pri, (int)num_sectors_sec, - (int)(num_usable_sectors)); + (int)(num_sectors_sec - 1)); return 0; } else if (num_sectors_pri > BOOT_MAX_IMG_SECTORS) { BOOT_LOG_WRN("Cannot upgrade: more sectors than allowed"); return 0; } - if ((num_usable_sectors + 1) != num_sectors_sec) { + /* Optimal says secondary has one more than primary. Always. Both have trailers. */ + if ((num_sectors_pri + 1) != num_sectors_sec) { BOOT_LOG_DBG("Non-optimal sector distribution, slot0 has %d usable sectors " - "but slot1 has %d usable sectors", (int)(num_usable_sectors), + "but slot1 has %d usable sectors", (int)(num_sectors_pri), ((int)num_sectors_sec - 1)); } - for (i = 0; i < num_usable_sectors; i++) { + for (i = 0; i < (num_sectors_sec - 1); i++) { sector_sz_pri = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, i); sector_sz_sec = boot_img_sector_size(state, BOOT_SECONDARY_SLOT, i); @@ -417,7 +388,6 @@ int swap_status_source(struct boot_loader_state *state) struct boot_swap_state state_primary_slot; struct boot_swap_state state_secondary_slot; int rc; - uint8_t source; uint8_t image_index; #if (BOOT_IMAGE_NUMBER == 1) @@ -439,10 +409,8 @@ int swap_status_source(struct boot_loader_state *state) state_primary_slot.copy_done == BOOT_FLAG_UNSET && state_secondary_slot.magic != BOOT_MAGIC_GOOD) { - source = BOOT_STATUS_SOURCE_PRIMARY_SLOT; - BOOT_LOG_INF("Boot source: primary slot"); - return source; + return BOOT_STATUS_SOURCE_PRIMARY_SLOT; } BOOT_LOG_INF("Boot source: none"); @@ -729,11 +697,23 @@ void swap_run(struct boot_loader_state *state, struct boot_status *bs, int app_max_size(struct boot_loader_state *state) { - uint32_t sector_sz_primary; + uint32_t available_pri_sz; + uint32_t available_sec_sz; + + size_t trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state)); + size_t sector_sz = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0); + size_t padding_sz = sector_sz; + + /* The trailer size needs to be sector-aligned */ + trailer_sz = ALIGN_UP(trailer_sz, sector_sz); - sector_sz_primary = boot_img_sector_size(state, BOOT_PRIMARY_SLOT, 0); + /* The slot whose size is used to compute the maximum image size must be the one containing the + * padding required for the swap. + */ + available_pri_sz = boot_img_num_sectors(state, BOOT_PRIMARY_SLOT) * sector_sz - trailer_sz; + available_sec_sz = boot_img_num_sectors(state, BOOT_SECONDARY_SLOT) * sector_sz - trailer_sz - padding_sz; - return app_max_sectors(state) * sector_sz_primary; + return (available_pri_sz < available_sec_sz ? available_pri_sz : available_sec_sz); } /* Compute the total size of the given image. Includes the size of the TLVs. */ diff --git a/boot/bootutil/src/swap_priv.h b/boot/bootutil/src/swap_priv.h index b564ea99e..10473a9cc 100644 --- a/boot/bootutil/src/swap_priv.h +++ b/boot/bootutil/src/swap_priv.h @@ -130,4 +130,12 @@ bool swap_write_block_size_check(struct boot_loader_state *state); */ int app_max_size(struct boot_loader_state *state); +#if defined(PM_S1_ADDRESS) && !defined(MCUBOOT_OVERWRITE_ONLY) && \ +CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 +/** + * Performs an NSIB update + */ +void nsib_swap_run(struct boot_loader_state *state, struct boot_status *bs); +#endif + #endif /* H_SWAP_PRIV_ */ diff --git a/boot/bootutil/src/swap_scratch.c b/boot/bootutil/src/swap_scratch.c index f9dbb7103..360dbe88c 100644 --- a/boot/bootutil/src/swap_scratch.c +++ b/boot/bootutil/src/swap_scratch.c @@ -47,6 +47,136 @@ int boot_status_fails = 0; #define BOOT_STATUS_ASSERT(x) ASSERT(x) #endif +#if MCUBOOT_SWAP_USING_SCRATCH +/** + * Finds the first sector of a given slot that holds image trailer data. + * + * @param state Current bootloader's state. + * @param slot The index of the slot to consider. + * @param trailer_sz The size of the trailer, in bytes. + * + * @return The index of the first sector of the slot that holds image trailer data. + */ +static size_t +boot_get_first_trailer_sector(struct boot_loader_state *state, size_t slot, size_t trailer_sz) +{ + size_t first_trailer_sector = boot_img_num_sectors(state, slot) - 1; + size_t sector_sz = boot_img_sector_size(state, slot, first_trailer_sector); + size_t trailer_sector_sz = sector_sz; + + while (trailer_sector_sz < trailer_sz) { + /* Consider that the image trailer may span across sectors of different sizes */ + --first_trailer_sector; + sector_sz = boot_img_sector_size(state, slot, first_trailer_sector); + + trailer_sector_sz += sector_sz; + } + + return first_trailer_sector; +} + +/** + * Returns the offset to the end of the first sector of a given slot that holds image trailer data. + * + * @param state Current bootloader's state. + * @param slot The index of the slot to consider. + * @param trailer_sz The size of the trailer, in bytes. + * + * @return The offset to the end of the first sector of the slot that holds image trailer data. + */ +static uint32_t +get_first_trailer_sector_end_off(struct boot_loader_state *state, size_t slot, size_t trailer_sz) +{ + size_t first_trailer_sector = boot_get_first_trailer_sector(state, slot, trailer_sz); + + return boot_img_sector_off(state, slot, first_trailer_sector) + + boot_img_sector_size(state, slot, first_trailer_sector); +} + +/** + * Returns the size of the part of the slot that can be used for storing image data. + * + * @param state Current bootloader's state. + * @param slot_size The size of the slot partition. + * + * @return The offset to the end of the first sector of the slot that holds image trailer data. + */ +static size_t app_max_size_adjust_to_trailer(struct boot_loader_state *state, size_t slot_size) +{ + size_t slot_trailer_sz = boot_trailer_sz(BOOT_WRITE_SZ(state)); + size_t slot_trailer_off = slot_size - slot_trailer_sz; + size_t trailer_sz_in_first_sector; + size_t trailer_sector_end_off; + + + size_t trailer_sector_primary_end_off = + get_first_trailer_sector_end_off(state, BOOT_PRIMARY_SLOT, slot_trailer_sz); + size_t trailer_sector_secondary_end_off = + get_first_trailer_sector_end_off(state, BOOT_SECONDARY_SLOT, slot_trailer_sz); + + /* If slots have sectors of different sizes, we need to find the "common" sector + * boundary (slot compatibility checks ensure that the larger sector contains a multiple + * of the smaller sector size). This will be the larger of the + * trailer_sector_primary_end_off/trailer_sector_secondary_end_off. + * + * <-------copy size-------> <--------copy size------> <----copy size---> + * v v v v + * +------------+------------+-------------------------+------------------+ + * | sector | sector | sector | sector | + * +------------+------------+------------+------------+------------------+ + * | sector | sector | sector | sector | + * +-------------------------+------------+------------+------------------+ + * + * The swap logic always uses the common boundary when performing the copy. + * Hence, the first trailer sector used for calculation is the larger + * sector from the two slots. + * + * <-----------copy size---------------> + * | sector | sector | + * +-----------------------------------+ + * | sector | + * +-----------------------------------+ + * |Image->| |<-trailer------------| + * +-----------------------------------+ + * | |<-scratch trailer>| + * +-----------------------------------+ + */ + if (trailer_sector_primary_end_off > trailer_sector_secondary_end_off) { + trailer_sector_end_off = trailer_sector_primary_end_off; + } else { + trailer_sector_end_off = trailer_sector_secondary_end_off; + } + + trailer_sz_in_first_sector = trailer_sector_end_off - slot_trailer_off; + + size_t trailer_padding = 0; + size_t scratch_trailer_sz = boot_scratch_trailer_sz(BOOT_WRITE_SZ(state)); + + /* Some padding might have to be inserted between the end of the firmware image and the + * beginning of the trailer to ensure there is enough space for the trailer in the scratch area + * when the last sector of the secondary will be copied to the scratch area. + * + * +-----------------------------------+-----------------------------------+ + * | sector | sector | + * +-----------------------------------+-----------------------------------+ + * |Image->| |<--trailer---|-----------trailer (cont.)-------->| + * +-----------------------------------+-----------------------------------+ + * | |<----scratch trailer---->| + * +-----------------------------------+ + * <-padding-> + * <--------scratch area size--------> + * + * The value of the padding depends on the amount of trailer data that is contained in the first + * sector containing part of the trailer in the primary and secondary slot. + */ + if (scratch_trailer_sz > trailer_sz_in_first_sector) { + trailer_padding = scratch_trailer_sz - trailer_sz_in_first_sector; + } + + return slot_trailer_off - trailer_padding; +} +#endif /* MCUBOOT_SWAP_USING_SCRATCH */ + #if !defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD) /** * Reads the status of a partially-completed swap, if any. This is necessary @@ -141,6 +271,18 @@ boot_status_internal_off(const struct boot_status *bs, int elem_sz) int boot_slots_compatible(struct boot_loader_state *state) { +#ifdef PM_S1_ADDRESS + /* Patch needed for NCS. In this case, image 1 primary points to the other + * B1 slot (ie S0 or S1), and image 0 primary points to the app. + * With this configuration, image 0 and image 1 share the secondary slot. + * Hence, the primary slot of image 1 will be *smaller* than image 1's + * secondary slot. This is not allowed in upstream mcuboot, so we need + * this patch to allow it. Also, all of these checks are redundant when + * partition manager is in use, and since we have the same sector size + * in all of our flash. + */ + return 1; +#else size_t num_sectors_primary; size_t num_sectors_secondary; size_t sz0, sz1; @@ -238,6 +380,7 @@ boot_slots_compatible(struct boot_loader_state *state) #endif return 1; +#endif /* PM_S1_ADDRESS */ } #define BOOT_LOG_SWAP_STATE(area, state) \ @@ -821,8 +964,8 @@ int app_max_size(struct boot_loader_state *state) size_t num_sectors_primary; size_t num_sectors_secondary; size_t sz0, sz1; - size_t primary_slot_sz, secondary_slot_sz; #ifndef MCUBOOT_OVERWRITE_ONLY + size_t slot_sz; size_t scratch_sz; #endif size_t i, j; @@ -842,8 +985,11 @@ int app_max_size(struct boot_loader_state *state) * number of a slot's sectors are able to fit into another, which only * excludes cases where sector sizes are not a multiple of each other. */ - i = sz0 = primary_slot_sz = 0; - j = sz1 = secondary_slot_sz = 0; +#ifndef MCUBOOT_OVERWRITE_ONLY + slot_sz = 0; +#endif + i = sz0 = 0; + j = sz1 = 0; smaller = 0; while (i < num_sectors_primary || j < num_sectors_secondary) { if (sz0 == sz1) { @@ -876,8 +1022,7 @@ int app_max_size(struct boot_loader_state *state) } #ifndef MCUBOOT_OVERWRITE_ONLY if (sz0 == sz1) { - primary_slot_sz += sz0; - secondary_slot_sz += sz1; + slot_sz += sz0; /* Scratch has to fit each swap operation to the size of the larger * sector among the primary slot and the secondary slot. */ @@ -892,8 +1037,10 @@ int app_max_size(struct boot_loader_state *state) #ifdef MCUBOOT_OVERWRITE_ONLY return (sz1 < sz0 ? sz1 : sz0); +#elif MCUBOOT_SWAP_USING_SCRATCH + return app_max_size_adjust_to_trailer(state, slot_sz); #else - return (secondary_slot_sz < primary_slot_sz ? secondary_slot_sz : primary_slot_sz); + return slot_sz; #endif } #else diff --git a/boot/bootutil/src/tlv.c b/boot/bootutil/src/tlv.c index 629bc235d..a69b3d8bd 100644 --- a/boot/bootutil/src/tlv.c +++ b/boot/bootutil/src/tlv.c @@ -21,9 +21,12 @@ #include #include "bootutil/bootutil.h" +#include "bootutil/bootutil_log.h" #include "bootutil/image.h" #include "bootutil_priv.h" +BOOT_LOG_MODULE_DECLARE(mcuboot); + /* * Initialize a TLV iterator. * @@ -43,6 +46,8 @@ bootutil_tlv_iter_begin(struct image_tlv_iter *it, const struct image_header *hd uint32_t off_; struct image_tlv_info info; + BOOT_LOG_DBG("bootutil_tlv_iter_begin: type %d, prot == %d", type, (int)prot); + if (it == NULL || hdr == NULL || fap == NULL) { return -1; } @@ -108,6 +113,9 @@ bootutil_tlv_iter_next(struct image_tlv_iter *it, uint32_t *off, uint16_t *len, return -1; } + BOOT_LOG_DBG("bootutil_tlv_iter_next: searching for %d (%d is any) starting at %d ending at %d", + it->type, IMAGE_TLV_ANY, it->tlv_off, it->tlv_end); + while (it->tlv_off < it->tlv_end) { if (it->hdr->ih_protect_tlv_size > 0 && it->tlv_off == it->prot_end) { it->tlv_off += sizeof(struct image_tlv_info); @@ -115,11 +123,14 @@ bootutil_tlv_iter_next(struct image_tlv_iter *it, uint32_t *off, uint16_t *len, rc = LOAD_IMAGE_DATA(it->hdr, it->fap, it->tlv_off, &tlv, sizeof tlv); if (rc) { + BOOT_LOG_DBG("bootutil_tlv_iter_next: load failed with %d for %p %d", + rc, it->fap, it->tlv_off); return -1; } /* No more TLVs in the protected area */ if (it->prot && it->tlv_off >= it->prot_end) { + BOOT_LOG_DBG("bootutil_tlv_iter_next: protected TLV %d not found", it->type); return 1; } @@ -130,12 +141,15 @@ bootutil_tlv_iter_next(struct image_tlv_iter *it, uint32_t *off, uint16_t *len, *off = it->tlv_off + sizeof(tlv); *len = tlv.it_len; it->tlv_off += sizeof(tlv) + tlv.it_len; + BOOT_LOG_DBG("bootutil_tlv_iter_next: TLV %d found at %d (size %d)", + *type, *off, *len); return 0; } it->tlv_off += sizeof(tlv) + tlv.it_len; } + BOOT_LOG_DBG("bootutil_tlv_iter_next: TLV %d not found", it->type); return 1; } diff --git a/boot/bootutil/zephyr/CMakeLists.txt b/boot/bootutil/zephyr/CMakeLists.txt index f6d37441c..44f78f395 100644 --- a/boot/bootutil/zephyr/CMakeLists.txt +++ b/boot/bootutil/zephyr/CMakeLists.txt @@ -40,7 +40,7 @@ if(CONFIG_BOOT_USE_PSA_CRYPTO) ) endif() -if(CONFIG_BOOT_USE_MBEDTLS OR CONFIG_BOOT_USE_PSA_CRYPTO) +if(CONFIG_BOOT_USE_MBEDTLS OR CONFIG_BOOT_USE_PSA_CRYPTO AND NOT CONFIG_NRF_SECURITY) zephyr_link_libraries(mbedTLS) endif() endif() diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt index 159962543..e7412e968 100644 --- a/boot/zephyr/CMakeLists.txt +++ b/boot/zephyr/CMakeLists.txt @@ -35,23 +35,20 @@ if(NOT CONFIG_MBEDTLS_BUILTIN AND NOT CONFIG_BOOT_KEY_IMPORT_BYPASS_ASN) set(MBEDTLS_ASN1_DIR "${MCUBOOT_DIR}/ext/mbedtls-asn1") assert_exists(MBEDTLS_ASN1_DIR) endif() -set(NRF_DIR "${MCUBOOT_DIR}/ext/nrf") +set(MCUBOOT_NRF_EXT_DIR "${MCUBOOT_DIR}/ext/nrf") if(CONFIG_BOOT_USE_NRF_CC310_BL) -set(NRFXLIB_DIR ${ZEPHYR_BASE}/../nrfxlib) -if(NOT EXISTS ${NRFXLIB_DIR}) - message(FATAL_ERROR " + if(NOT EXISTS ${ZEPHYR_NRFXLIB_MODULE_DIR}) + message(FATAL_ERROR " ------------------------------------------------------------------------ - No such file or directory: ${NRFXLIB_DIR} + No such file or directory: ${ZEPHYR_NRFXLIB_MODULE_DIR} The current configuration enables nRF CC310 crypto accelerator hardware with the `CONFIG_BOOT_USE_NRF_CC310_BL` option. Please follow `ext/nrf/README.md` guide to fix your setup or use tinycrypt instead of the HW accelerator. To use the tinycrypt set `CONFIG_BOOT_ECDSA_TINYCRYPT` to y. ------------------------------------------------------------------------") -endif() -# Don't include this if we are using west - add_subdirectory(${NRFXLIB_DIR} ${PROJECT_BINARY_DIR}/nrfxlib) + endif() endif() zephyr_library_include_directories( @@ -67,12 +64,17 @@ endif() # Zephyr port-specific sources. zephyr_library_sources( main.c - io.c flash_map_extended.c os.c keys.c ) +if(CONFIG_NCS_BM) + zephyr_library_sources(io_bm.c) +else() + zephyr_library_sources(io.c) +endif() + if(DEFINED CONFIG_ENABLE_MGMT_PERUSER) zephyr_library_sources( boot_serial_extensions.c @@ -102,6 +104,12 @@ if(DEFINED CONFIG_BOOT_SHARE_BACKEND_RETENTION) ) endif() +if(DEFINED CONFIG_BOOT_KEYS_REVOCATION) + zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/key_revocation.c +) +endif() + # Generic bootutil sources and includes. zephyr_library_include_directories(${BOOT_DIR}/bootutil/include) zephyr_library_sources( @@ -153,9 +161,15 @@ elseif(CONFIG_SINGLE_APPLICATION_SLOT) ) zephyr_library_include_directories(${BOOT_DIR}/bootutil/src) elseif(CONFIG_BOOT_FIRMWARE_LOADER) - zephyr_library_sources( - ${BOOT_DIR}/zephyr/firmware_loader.c - ) + if(CONFIG_NCS_BM) + zephyr_library_sources( + ${BOOT_DIR}/zephyr/firmware_loader_bm.c + ) + else() + zephyr_library_sources( + ${BOOT_DIR}/zephyr/firmware_loader.c + ) + endif() zephyr_library_include_directories(${BOOT_DIR}/bootutil/src) else() zephyr_library_sources( @@ -183,6 +197,12 @@ else() ) endif() endif() + + if(NOT CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER EQUAL "-1" AND NOT CONFIG_BOOT_UPGRADE_ONLY) + zephyr_library_sources( + ${BOOT_DIR}/bootutil/src/swap_nsib.c + ) + endif() endif() if(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256 OR CONFIG_BOOT_ENCRYPT_EC256) @@ -211,9 +231,11 @@ if(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256 OR CONFIG_BOOT_ENCRYPT_EC256) ${TINYCRYPT_DIR}/source/utils.c ) elseif(CONFIG_BOOT_USE_NRF_CC310_BL) - zephyr_library_sources(${NRF_DIR}/cc310_glue.c) - zephyr_library_include_directories(${NRF_DIR}) + zephyr_library_sources(${MCUBOOT_NRF_EXT_DIR}/cc310_glue.c) + zephyr_library_include_directories(${MCUBOOT_NRF_EXT_DIR}) zephyr_link_libraries(nrfxlib_crypto) + elseif(CONFIG_BOOT_USE_NRF_EXTERNAL_CRYPTO) + zephyr_include_directories(${BL_CRYPTO_DIR}/../include) endif() if(CONFIG_MBEDTLS_CFG_FILE) @@ -309,6 +331,12 @@ if(CONFIG_BOOT_ENCRYPT_EC256) ) endif() +if(CONFIG_BOOT_DECOMPRESSION) + zephyr_library_sources( + decompression.c + ) +endif() + if(CONFIG_MCUBOOT_SERIAL) zephyr_sources(${BOOT_DIR}/zephyr/serial_adapter.c) zephyr_sources(${BOOT_DIR}/boot_serial/src/boot_serial.c) @@ -330,7 +358,7 @@ if(CONFIG_MCUBOOT_SERIAL) endif() endif() -if(NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "") +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) @@ -354,6 +382,13 @@ if(NOT CONFIG_BOOT_SIGNATURE_KEY_FILE STREQUAL "") endif() message("MCUBoot bootloader key file: ${KEY_FILE}") + set_property( + GLOBAL + PROPERTY + KEY_FILE + ${KEY_FILE} + ) + set(mcuboot_default_signature_files ${MCUBOOT_DIR}/root-ec-p256-pkcs8.pem ${MCUBOOT_DIR}/root-ec-p384.pem @@ -711,3 +746,20 @@ 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_MCUBOOT_NRF_CLEANUP_PERIPHERAL OR CONFIG_MCUBOOT_CLEANUP_NONSECURE_RAM) +zephyr_library_sources( + ${BOOT_DIR}/zephyr/nrf_cleanup.c +) +endif() + +if(SYSBUILD AND CONFIG_PCD_APP) + # Sysbuild requires details of the RAM flash device are stored to the cache of MCUboot so + # that they can be read when running partition manager + dt_nodelabel(ram_flash_dev NODELABEL flash_sim0) + dt_reg_addr(ram_flash_addr PATH ${ram_flash_dev}) + dt_reg_size(ram_flash_size PATH ${ram_flash_dev}) + + set(RAM_FLASH_ADDR "${ram_flash_addr}" CACHE STRING "" FORCE) + set(RAM_FLASH_SIZE "${ram_flash_size}" CACHE STRING "" FORCE) +endif() diff --git a/boot/zephyr/Kconfig b/boot/zephyr/Kconfig index acc0314a6..c9b24fa0a 100644 --- a/boot/zephyr/Kconfig +++ b/boot/zephyr/Kconfig @@ -9,6 +9,8 @@ mainmenu "MCUboot configuration" comment "MCUboot-specific configuration options" +source "$(ZEPHYR_NRF_MODULE_DIR)/modules/mcuboot/boot/zephyr/Kconfig" + # Hidden option to mark a project as MCUboot config MCUBOOT default y @@ -16,6 +18,7 @@ config MCUBOOT select MPU_ALLOW_FLASH_WRITE if ARM_MPU select USE_DT_CODE_PARTITION if HAS_FLASH_LOAD_OFFSET select MCUBOOT_BOOTUTIL_LIB + select REBOOT if SECURE_BOOT config BOOT_USE_MBEDTLS bool @@ -77,13 +80,12 @@ config BOOT_PSA_IMG_HASH_ALG_SHA256_DEPENDENCIES config BOOT_ED25519_PSA_DEPENDENCIES bool - select PSA_WANT_ALG_SHA_256 - select PSA_WANT_ALG_SHA_512 + select PSA_WANT_ALG_SHA_256 if !PSA_CORE_LITE + select PSA_WANT_ALG_SHA_512 if !PSA_CORE_LITE select PSA_WANT_ALG_PURE_EDDSA - # Seems that upstream mbedTLS does not have TE - #select PSA_WANT_ECC_TWISTED_EDWARDS_255 + select PSA_WANT_ECC_TWISTED_EDWARDS_255 select PSA_WANT_ECC_MONTGOMERY_255 - select PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT + select PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT if !PSA_CORE_LITE help Dependencies for ed25519 signature @@ -146,6 +148,7 @@ config BOOT_IMG_HASH_ALG_SHA512_ALLOW config BOOT_IMG_HASH_DIRECTLY_ON_STORAGE bool "Hash calculation functions access storage through address space" + default y if NRF_SECURITY && SOC_NRF54H20 depends on !BOOT_ENCRYPT_IMAGE help When possible to map storage device, at least for read operations, @@ -200,12 +203,13 @@ config BOOT_SIGNATURE_TYPE_PURE_ALLOW choice BOOT_SIGNATURE_TYPE prompt "Signature type" - default BOOT_SIGNATURE_TYPE_ED25519 if SOC_NRF54L15_CPUAPP + default BOOT_SIGNATURE_TYPE_ED25519 if SOC_NRF54L15_CPUAPP || SOC_NRF54H20_CPUAPP default BOOT_SIGNATURE_TYPE_RSA config BOOT_SIGNATURE_TYPE_NONE bool "No signature; use only hash check" - select BOOT_USE_TINYCRYPT + select BOOT_USE_TINYCRYPT if !SOC_SERIES_NRF54LX + select BOOT_USE_PSA_CRYPTO if SOC_SERIES_NRF54LX select BOOT_IMG_HASH_ALG_SHA256_ALLOW config BOOT_SIGNATURE_TYPE_RSA @@ -218,6 +222,8 @@ config BOOT_SIGNATURE_TYPE_RSA select MBEDTLS_PKCS1_V15 if MBEDTLS_BUILTIN select MBEDTLS_PKCS1_V21 if MBEDTLS_BUILTIN select MBEDTLS_KEY_EXCHANGE_RSA_ENABLED if MBEDTLS_BUILTIN + select MBEDTLS_PLATFORM_NO_STD_FUNCTIONS if MBEDTLS_BUILTIN + select MBEDTLS_PLATFORM_SNPRINTF_ALT if MBEDTLS_BUILTIN select BOOT_ENCRYPTION_SUPPORT select BOOT_IMG_HASH_ALG_SHA256_ALLOW select BOOT_AES_MBEDTLS_DEPENDENCIES if MBEDTLS_BUILTIN && BOOT_ENCRYPT_IMAGE @@ -279,6 +285,7 @@ config BOOT_SIGNATURE_TYPE_PURE choice BOOT_ED25519_IMPLEMENTATION prompt "Ecdsa implementation" + default BOOT_ED25519_PSA if NRF_SECURITY default BOOT_ED25519_TINYCRYPT config BOOT_ED25519_TINYCRYPT @@ -299,15 +306,15 @@ config BOOT_ED25519_MBEDTLS config BOOT_ED25519_PSA bool "Use PSA crypto" - select MBEDTLS + depends on NRF_SECURITY select BOOT_USE_PSA_CRYPTO select PSA_CRYPTO_CLIENT select PSA_CRYPTO_C select MBEDTLS_PSA_CRYPTO_C select MBEDTLS_ASN1_PARSE_C if MBEDTLS_BUILTIN - select MBEDTLS_ENABLE_HEAP - select BOOT_IMG_HASH_ALG_SHA256_ALLOW - select BOOT_IMG_HASH_ALG_SHA512_ALLOW + select MBEDTLS_ENABLE_HEAP if !PSA_CORE_LITE + 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 @@ -325,6 +332,46 @@ endif endchoice +config BOOT_SIGNATURE_USING_KMU + bool "Use KMU stored keys for signature verification" + depends on NRF_SECURITY + depends on CRACEN_LIB_KMU + select PSA_WANT_ALG_GCM + select PSA_WANT_KEY_TYPE_AES + select PSA_WANT_AES_KEY_SIZE_256 + select PSA_WANT_ALG_SP800_108_COUNTER_CMAC + select PSA_WANT_ALG_CMAC + select PSA_WANT_ALG_ECB_NO_PADDING + help + MCUboot will use keys provisioned to the device key management unit for signature + verification instead of compiling in key data from a file. + +if BOOT_SIGNATURE_USING_KMU + +config BOOT_SIGNATURE_KMU_SLOTS + int "KMU key slots" + range 1 3 + default 1 + help + Selects the number of KMU key slots (also known as generations) to use when verifying + an image. + +endif + +config BOOT_KEYS_REVOCATION + bool "Auto revoke previous gen key" + help + Automatically revoke previous generation key upon new valid key usage. + +config BOOT_KMU_KEYS_REVOCATION + bool + depends on BOOT_KEYS_REVOCATION + default y if BOOT_SIGNATURE_USING_KMU + help + Enabling KMU key revocation backend. + +if !BOOT_SIGNATURE_USING_KMU + config BOOT_SIGNATURE_KEY_FILE string "PEM key file" default "root-ec-p256.pem" if BOOT_SIGNATURE_TYPE_ECDSA_P256 @@ -342,6 +389,8 @@ config BOOT_SIGNATURE_KEY_FILE with the public key information will be written in a format expected by MCUboot. +endif + config MCUBOOT_CLEANUP_ARM_CORE bool "Perform core cleanup before chain-load the application" depends on CPU_CORTEX_M @@ -364,6 +413,21 @@ config MCUBOOT_CLEANUP_RAM help Sets contents of memory to 0 before jumping to application. +config MCUBOOT_INFINITE_LOOP_AFTER_RAM_CLEANUP + bool "Infinite loop after RAM cleanup" + depends on MCUBOOT_CLEANUP_RAM + help + Verification option that keeps execution in infinite loop after + RAM cleanup has been performed. + +# Disable MBEDTLS from being selected if NRF_SECURITY is enabled, and use default NRF_SECURITY +# configuration file for MBEDTLS +config MBEDTLS + depends on !NRF_SECURITY + +config NRF_SECURITY + select MBEDTLS_PROMPTLESS + config MBEDTLS_CFG_FILE # It might be awkward to define an Mbed TLS header file when TinyCrypt # is used, but the fact is that Mbed TLS' ASN1 parse module is used @@ -601,6 +665,15 @@ config BOOT_ENCRYPT_X25519 help Hidden option selecting x25519 encryption. +config BOOT_HMAC_SHA512 + bool "Use SHA512 for HMAC/HKDF" + depends on BOOT_ENCRYPT_X25519 + depends on BOOT_USE_PSA_CRYPTO + help + By default SHA256 is used for HKDF/HMAC in key exchange expansion + and verification. This options switches to SHA512. The option is + mainly useful to reduce numer of compiled in SHA algorithms. + config BOOT_ENCRYPTION_KEY_FILE string "Encryption key file" depends on BOOT_ENCRYPT_IMAGE @@ -620,7 +693,7 @@ config BOOT_ENCRYPTION_KEY_FILE config BOOT_MAX_IMG_SECTORS_AUTO bool "Calculate maximum sectors automatically" - default y + default y if !PARTITION_MANAGER_ENABLED help If this option is enabled then the maximum number of supported sectors per image will be calculated automatically from the flash erase sizes and size of each partition for @@ -981,6 +1054,7 @@ config BOOT_DISABLE_CACHES config MCUBOOT_BOOT_BANNER bool "Use MCUboot boot banner" depends on BOOT_BANNER + depends on !NCS_BOOT_BANNER depends on "$(APP_VERSION_EXTENDED_STRING)" != "" default y help @@ -997,13 +1071,19 @@ config BOOT_BANNER_STRING config BOOT_DECOMPRESSION_SUPPORT bool + depends on NRF_COMPRESS && NRF_COMPRESS_DECOMPRESSION && (NRF_COMPRESS_LZMA_VERSION_LZMA1 || NRF_COMPRESS_LZMA_VERSION_LZMA2) + depends on !SINGLE_APPLICATION_SLOT && BOOT_UPGRADE_ONLY + default y help Hidden symbol which should be selected if a system provided decompression support. if BOOT_DECOMPRESSION_SUPPORT menuconfig BOOT_DECOMPRESSION - bool "Decompression" + bool "Decompression [EXPERIMENTAL]" + select NRF_COMPRESS_CLEANUP + select PM_USE_CONFIG_SRAM_SIZE if SOC_NRF54L15_CPUAPP + select EXPERIMENTAL help If enabled, will include support for compressed images being loaded to the secondary slot which then get decompressed into the primary slot. This mode allows the secondary slot to @@ -1012,9 +1092,9 @@ menuconfig BOOT_DECOMPRESSION if BOOT_DECOMPRESSION config BOOT_DECOMPRESSION_BUFFER_SIZE - int "Write buffer size" + int range 16 16384 - default 4096 + default NRF_COMPRESS_CHUNK_SIZE help The size of a secondary buffer used for writing decompressed data to the storage device. @@ -1140,4 +1220,21 @@ config MCUBOOT_VERIFY_IMG_ADDRESS also be useful when BOOT_DIRECT_XIP is enabled, to ensure that the image linked at the correct address is loaded. +config NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT + int "Number of image validation attempts" + default 1 + help + Number of image validation attempts performed before an image is considered invalid. + A wait is done between each attempt to allow for recovery from a temporary disruption. + This can prevent erasing an image when initial validation fails. + Wait time is controlled by MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS. + +config NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_WAIT_MS + int "Time between image validation attempts" + depends on NRF_MCUBOOT_IMG_VALIDATE_ATTEMPT_COUNT > 1 + default 5000 + help + Time between image validation attempts, in milliseconds. + Allows for recovery from transient bit flips or similar situations. + source "Kconfig.zephyr" diff --git a/boot/zephyr/Kconfig.serial_recovery b/boot/zephyr/Kconfig.serial_recovery index 45d252408..5b4ba3e11 100644 --- a/boot/zephyr/Kconfig.serial_recovery +++ b/boot/zephyr/Kconfig.serial_recovery @@ -46,9 +46,14 @@ config BOOT_SERIAL_CDC_ACM endchoice +DT_COMPAT_SIM_FLASH:= zephyr,sim-flash +DT_SIM_FLASH_PATH := $(dt_nodelabel_path,flash_sim0) + config MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD bool "Allow to select image number for DFU" - depends on !SINGLE_APPLICATION_SLOT + # Allow this option to be selected in cases where support for direct uploading to nRF5340 + # network core should be supported + depends on !SINGLE_APPLICATION_SLOT || (SINGLE_APPLICATION_SLOT && SOC_NRF5340_CPUAPP && BOOT_IMAGE_ACCESS_HOOK_NRF5340 && FLASH_SIMULATOR && $(dt_compat_enabled,$(DT_COMPAT_SIM_FLASH))) help With the option enabled, the mcuboot serial recovery will respect the "image" field in mcumgr image update frame diff --git a/boot/zephyr/boards/nrf52840dk_qspi_secondary_boot.conf b/boot/zephyr/boards/nrf52840dk_qspi_secondary_boot.conf index 9f984be4f..5f3b7808b 100644 --- a/boot/zephyr/boards/nrf52840dk_qspi_secondary_boot.conf +++ b/boot/zephyr/boards/nrf52840dk_qspi_secondary_boot.conf @@ -1,2 +1 @@ -CONFIG_MULTITHREADING=y CONFIG_BOOT_MAX_IMG_SECTORS=256 diff --git a/boot/zephyr/boards/nrf52840dongle_nrf52840_bare.conf b/boot/zephyr/boards/nrf52840dongle_nrf52840_bare.conf new file mode 100644 index 000000000..d219f351d --- /dev/null +++ b/boot/zephyr/boards/nrf52840dongle_nrf52840_bare.conf @@ -0,0 +1,26 @@ +# The UART is used for Serial Recovery, so logging requires +# an RTT console, which is not available out of the box on this board. +# Disable logging. +CONFIG_LOG=n + +# Serial +CONFIG_CONSOLE=n +CONFIG_SERIAL=y +CONFIG_UART_NRFX=n +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_UART_LINE_CTRL=y + +# MCUBoot serial +CONFIG_GPIO=y +CONFIG_MCUBOOT_SERIAL=y +CONFIG_BOOT_SERIAL_CDC_ACM=y + +# Required by USB +CONFIG_MULTITHREADING=y + +# USB +CONFIG_USB_DEVICE_STACK=y +CONFIG_USB_DEVICE_REMOTE_WAKEUP=n +CONFIG_USB_DEVICE_PRODUCT="MCUBOOT" + +CONFIG_NORDIC_QSPI_NOR=n diff --git a/boot/zephyr/boards/nrf5340dk_nrf5340_cpuapp_minimal.conf b/boot/zephyr/boards/nrf5340dk_nrf5340_cpuapp_minimal.conf new file mode 100644 index 000000000..dd5468106 --- /dev/null +++ b/boot/zephyr/boards/nrf5340dk_nrf5340_cpuapp_minimal.conf @@ -0,0 +1,13 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# CC3xx is currently not used for nrf53 +CONFIG_HW_CC3XX=n +CONFIG_NRF_CC3XX_PLATFORM=n + +# Required for kernel operation +CONFIG_CLOCK_CONTROL=y +CONFIG_SYS_CLOCK_EXISTS=y diff --git a/boot/zephyr/boards/nrf54h20dk_nrf54h20_cpuapp_iron.conf b/boot/zephyr/boards/nrf54h20dk_nrf54h20_cpuapp_iron.conf index 50d349255..31666d9fe 100644 --- a/boot/zephyr/boards/nrf54h20dk_nrf54h20_cpuapp_iron.conf +++ b/boot/zephyr/boards/nrf54h20dk_nrf54h20_cpuapp_iron.conf @@ -6,6 +6,7 @@ # Ensure that the SPI NOR driver is disabled by default CONFIG_SPI_NOR=n -CONFIG_BOOT_WATCHDOG_FEED=n +# TODO: below are not yet supported and need fixing +CONFIG_FPROTECT=n -CONFIG_MULTITHREADING=y +CONFIG_BOOT_WATCHDOG_FEED=n diff --git a/boot/zephyr/boards/nrf54l15dk_nrf54l10_cpuapp.conf b/boot/zephyr/boards/nrf54l15dk_nrf54l10_cpuapp.conf deleted file mode 100644 index f911aa248..000000000 --- a/boot/zephyr/boards/nrf54l15dk_nrf54l10_cpuapp.conf +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2024 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: Apache-2.0 -# -CONFIG_BOOT_MAX_IMG_SECTORS=256 - -# Ensure that the SPI NOR driver is disabled by default -CONFIG_SPI_NOR=n - -CONFIG_FPROTECT=y - -CONFIG_BOOT_WATCHDOG_FEED=n - -# Ensure the fastest RRAM write operations -CONFIG_NRF_RRAM_WRITE_BUFFER_SIZE=32 diff --git a/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.conf b/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.conf new file mode 100644 index 000000000..ec944f828 --- /dev/null +++ b/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.conf @@ -0,0 +1,9 @@ +CONFIG_SPI=y +CONFIG_SPI_NOR=y +CONFIG_FLASH=y +CONFIG_PM_PARTITION_SIZE_MCUBOOT=0x14000 +CONFIG_MAIN_STACK_SIZE=20480 +CONFIG_BOOT_MAX_IMG_SECTORS=512 +CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +# Ensure that the qspi driver is disabled by default +CONFIG_NORDIC_QSPI_NOR=n diff --git a/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.overlay b/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.overlay new file mode 100644 index 000000000..60ee6fe51 --- /dev/null +++ b/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp_ext_flash.overlay @@ -0,0 +1,47 @@ +/ { + chosen { + nordic,pm-ext-flash = &mx25r64; + zephyr,code-partition = &boot_partition; + }; +}; + +/delete-node/ &boot_partition; +/delete-node/ &slot0_partition; +/delete-node/ &slot1_partition; + +/delete-node/ &slot0_ns_partition; +/delete-node/ &slot1_ns_partition; + +/delete-node/ &storage_partition; + +&cpuapp_rram { + reg = < 0x0 DT_SIZE_K(1524) >; + partitions { + boot_partition: partition@0 { + label = "mcuboot"; + reg = <0x000000000 0x00014000>; + }; + slot0_partition: partition@14000 { + label = "image-0"; + reg = <0x000014000 0x0015A000>; + }; + storage_partition: partition@16E000 { + label = "storage"; + reg = < 0x16E000 0x9000 >; + }; + }; +}; + +&mx25r64 { + status = "okay"; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + slot1_partition: partition@0 { + label = "image-1"; + reg = <0x000000000 0x0015A000>; + }; + }; +}; diff --git a/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp.conf b/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp.conf deleted file mode 100644 index 43d8cebe3..000000000 --- a/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp.conf +++ /dev/null @@ -1,10 +0,0 @@ -# Copyright (c) 2024 Nordic Semiconductor ASA -# -# SPDX-License-Identifier: Apache-2.0 -# -CONFIG_BOOT_MAX_IMG_SECTORS=256 - -# Ensure that the SPI NOR driver is disabled by default -CONFIG_SPI_NOR=n - -CONFIG_BOOT_WATCHDOG_FEED=n diff --git a/boot/zephyr/boards/nrf54lm20pdk_nrf54lm20a_cpuapp.conf b/boot/zephyr/boards/nrf54lm20pdk_nrf54lm20a_cpuapp.conf new file mode 100644 index 000000000..4944f7b13 --- /dev/null +++ b/boot/zephyr/boards/nrf54lm20pdk_nrf54lm20a_cpuapp.conf @@ -0,0 +1,16 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# Ensure that the SPI NOR driver is disabled by default +CONFIG_SPI_NOR=n + +# TODO: below are not yet supported and need fixing +CONFIG_FPROTECT=n + +CONFIG_BOOT_WATCHDOG_FEED=n + +CONFIG_PSA_CRYPTO_DRIVER_CRACEN=n +CONFIG_PSA_CRYPTO_DRIVER_OBERON=y diff --git a/boot/zephyr/boards/thingy53_nrf5340_cpuapp.conf b/boot/zephyr/boards/thingy53_nrf5340_cpuapp.conf index f2e42fd64..bbef18460 100644 --- a/boot/zephyr/boards/thingy53_nrf5340_cpuapp.conf +++ b/boot/zephyr/boards/thingy53_nrf5340_cpuapp.conf @@ -1,3 +1,75 @@ -CONFIG_NORDIC_QSPI_NOR=n -CONFIG_SPI=n +CONFIG_SIZE_OPTIMIZATIONS=y + +CONFIG_SYSTEM_CLOCK_NO_WAIT=y +CONFIG_PM=n + +CONFIG_MAIN_STACK_SIZE=10240 +CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h" + +CONFIG_BOOT_MAX_IMG_SECTORS=2048 +CONFIG_BOOT_SIGNATURE_TYPE_RSA=y + +# Flash +CONFIG_FLASH=y +CONFIG_BOOT_ERASE_PROGRESSIVELY=y +CONFIG_SOC_FLASH_NRF_EMULATE_ONE_BYTE_WRITE_ACCESS=y +CONFIG_FPROTECT=y + +# Serial +CONFIG_SERIAL=y +CONFIG_UART_LINE_CTRL=y + +# MCUBoot serial +CONFIG_GPIO=y +CONFIG_GPIO_NRFX_INTERRUPT=n +CONFIG_MCUBOOT_SERIAL=y +CONFIG_MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD=y +CONFIG_BOOT_SERIAL_CDC_ACM=y + +# Required by QSPI +CONFIG_NORDIC_QSPI_NOR=y +CONFIG_NORDIC_QSPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_NORDIC_QSPI_NOR_STACK_WRITE_BUFFER_SIZE=16 + +# Required by USB CONFIG_MULTITHREADING=y + +# USB +CONFIG_BOARD_SERIAL_BACKEND_CDC_ACM=n +CONFIG_USB_DEVICE_REMOTE_WAKEUP=n +CONFIG_USB_DEVICE_MANUFACTURER="Nordic Semiconductor ASA" +CONFIG_USB_DEVICE_PRODUCT="Bootloader Thingy:53" +CONFIG_USB_DEVICE_VID=0x1915 +CONFIG_USB_DEVICE_PID=0x5300 +CONFIG_USB_CDC_ACM=y + +# Decrease memory footprint +CONFIG_CBPRINTF_NANO=y +CONFIG_TIMESLICING=n +CONFIG_BOOT_BANNER=n +CONFIG_NCS_BOOT_BANNER=n +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_UART_CONSOLE=n +CONFIG_USE_SEGGER_RTT=n +CONFIG_LOG=n +CONFIG_ERRNO=n +CONFIG_PRINTK=n +CONFIG_RESET_ON_FATAL_ERROR=n +CONFIG_SPI=n +CONFIG_I2C=n +CONFIG_UART_NRFX=n + +# The following configurations are required to support simultaneous multi image update +CONFIG_PCD_APP=y +CONFIG_UPDATEABLE_IMAGE_NUMBER=2 +CONFIG_BOOT_UPGRADE_ONLY=y +# The network core cannot access external flash directly. The flash simulator must be used to +# provide a memory region that is used to forward the new firmware to the network core. +CONFIG_FLASH_SIMULATOR=y +CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=y +CONFIG_FLASH_SIMULATOR_STATS=n + +# Enable custom command to erase settings partition. +CONFIG_ENABLE_MGMT_PERUSER=y +CONFIG_BOOT_MGMT_CUSTOM_STORAGE_ERASE=y diff --git a/boot/zephyr/boards/thingy91_nrf52840.conf b/boot/zephyr/boards/thingy91_nrf52840.conf new file mode 100644 index 000000000..c0d183401 --- /dev/null +++ b/boot/zephyr/boards/thingy91_nrf52840.conf @@ -0,0 +1,34 @@ +# Disable Zephyr console +CONFIG_LOG=n +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_UART_CONSOLE=n + +# The build won't fit on the partition allocated for it without size +# optimizations. +CONFIG_SIZE_OPTIMIZATIONS=y +CONFIG_PM_PARTITION_SIZE_MCUBOOT=0x12000 + +# Serial +CONFIG_SERIAL=y +CONFIG_UART_NRFX=y +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_UART_LINE_CTRL=y + +# MCUboot serial recovery +CONFIG_GPIO=y +CONFIG_MCUBOOT_SERIAL=y +CONFIG_BOOT_SERIAL_CDC_ACM=y + +# Required by USB +CONFIG_MULTITHREADING=y + +# USB +CONFIG_USB_DEVICE_STACK=y +CONFIG_USB_DEVICE_PRODUCT="MCUBOOT" +CONFIG_USB_CDC_ACM=y +CONFIG_USB_COMPOSITE_DEVICE=y +CONFIG_USB_MASS_STORAGE=n +CONFIG_USB_DEVICE_MANUFACTURER="Nordic Semiconductor" +CONFIG_USB_DEVICE_VID=0x1915 +CONFIG_USB_DEVICE_PID=0x520F diff --git a/boot/zephyr/boards/thingy91_nrf9160.conf b/boot/zephyr/boards/thingy91_nrf9160.conf new file mode 100644 index 000000000..1bf2e424d --- /dev/null +++ b/boot/zephyr/boards/thingy91_nrf9160.conf @@ -0,0 +1,13 @@ +# Disable Zephyr console +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_UART_CONSOLE=n + +# Disable Flash protection +CONFIG_FPROTECT=n + +# MCUBoot settings +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# MCUboot serial recovery +CONFIG_MCUBOOT_SERIAL=y diff --git a/boot/zephyr/boards/thingy91x_nrf5340_cpuapp.conf b/boot/zephyr/boards/thingy91x_nrf5340_cpuapp.conf new file mode 100644 index 000000000..d3e253b65 --- /dev/null +++ b/boot/zephyr/boards/thingy91x_nrf5340_cpuapp.conf @@ -0,0 +1,63 @@ +# MCUBoot settings +CONFIG_BOOT_MAX_IMG_SECTORS=110 + +# MCUboot serial recovery +CONFIG_MCUBOOT_SERIAL=y + +# Disable Zephyr console +CONFIG_LOG=n +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_UART_CONSOLE=n + +# Serial +CONFIG_SERIAL=y +CONFIG_UART_NRFX=y +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_UART_LINE_CTRL=y + +# MCUboot serial recovery +CONFIG_GPIO=y +CONFIG_MCUBOOT_SERIAL=y +CONFIG_BOOT_SERIAL_CDC_ACM=y + +# Required by USB +CONFIG_MULTITHREADING=y + +# USB +CONFIG_USB_DEVICE_STACK=y +CONFIG_USB_DEVICE_PRODUCT="MCUBOOT" +CONFIG_USB_CDC_ACM=y +CONFIG_USB_COMPOSITE_DEVICE=y +CONFIG_USB_MASS_STORAGE=n +CONFIG_USB_DEVICE_MANUFACTURER="Nordic Semiconductor" +CONFIG_USB_DEVICE_VID=0x1915 +CONFIG_USB_DEVICE_PID=0x910A + +CONFIG_BOOT_SERIAL_BOOT_MODE=y + +CONFIG_PM_PARTITION_SIZE_MCUBOOT=0x13E00 + +# The following configurations are required to support simultaneous multi image update +CONFIG_PCD_APP=y +CONFIG_UPDATEABLE_IMAGE_NUMBER=2 +CONFIG_BOOT_UPGRADE_ONLY=y +# The network core cannot access external flash directly. The flash simulator must be used to +# provide a memory region that is used to forward the new firmware to the network core. +CONFIG_FLASH_SIMULATOR=y +CONFIG_FLASH_SIMULATOR_DOUBLE_WRITES=y +CONFIG_FLASH_SIMULATOR_STATS=n + +CONFIG_BOOT_IMAGE_ACCESS_HOOKS=y + +# Makes it possible to update the network core using the flash simulator +CONFIG_NRF53_RECOVERY_NETWORK_CORE=y + +CONFIG_MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD=y +CONFIG_BOOT_SERIAL_IMG_GRP_IMAGE_STATE=y + +# Skip checks on the secondary image to make it possible to update MCUBoot on S1/S0 +CONFIG_MCUBOOT_VERIFY_IMG_ADDRESS=n + +CONFIG_BOOT_SERIAL_NO_APPLICATION=y +CONFIG_FW_INFO_FIRMWARE_VERSION=2 diff --git a/boot/zephyr/boards/thingy91x_nrf9151.conf b/boot/zephyr/boards/thingy91x_nrf9151.conf new file mode 100644 index 000000000..8088686e0 --- /dev/null +++ b/boot/zephyr/boards/thingy91x_nrf9151.conf @@ -0,0 +1,20 @@ +# MCUBoot settings +CONFIG_BOOT_MAX_IMG_SECTORS=512 + +CONFIG_SPI=y +CONFIG_SPI_NOR=y +CONFIG_SPI_NOR_FLASH_LAYOUT_PAGE_SIZE=4096 +CONFIG_SPI_NOR_SFDP_DEVICETREE=y + +# Disable Zephyr console and use UART for MCUboot serial recovery instead +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_UART_CONSOLE=n +CONFIG_MCUBOOT_SERIAL=y +CONFIG_MCUBOOT_SERIAL_DIRECT_IMAGE_UPLOAD=y +CONFIG_BOOT_SERIAL_IMG_GRP_IMAGE_STATE=y + +CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY=y +CONFIG_PM_OVERRIDE_EXTERNAL_DRIVER_CHECK=y + +CONFIG_FW_INFO_FIRMWARE_VERSION=2 diff --git a/boot/zephyr/boards/thingy91x_nrf9151.overlay b/boot/zephyr/boards/thingy91x_nrf9151.overlay new file mode 100644 index 000000000..7f2818c0d --- /dev/null +++ b/boot/zephyr/boards/thingy91x_nrf9151.overlay @@ -0,0 +1,4 @@ +&uart0 { + status = "okay"; + current-speed = < 1000000 >; +}; diff --git a/boot/zephyr/decompression.c b/boot/zephyr/decompression.c new file mode 100644 index 000000000..ce4fe0b2b --- /dev/null +++ b/boot/zephyr/decompression.c @@ -0,0 +1,1524 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include "compression/decompression.h" +#include "bootutil/crypto/sha.h" +#include "bootutil/bootutil_log.h" + +#if !defined(__BOOTSIM__) +#define TARGET_STATIC static +#else +#define TARGET_STATIC +#endif + +#if defined(MCUBOOT_SIGN_RSA) +#if MCUBOOT_SIGN_RSA_LEN == 2048 +#define EXPECTED_SIG_TLV IMAGE_TLV_RSA2048_PSS +#elif MCUBOOT_SIGN_RSA_LEN == 3072 +#define EXPECTED_SIG_TLV IMAGE_TLV_RSA3072_PSS +#endif +#elif defined(MCUBOOT_SIGN_EC256) || \ + defined(MCUBOOT_SIGN_EC384) || \ + defined(MCUBOOT_SIGN_EC) +#define EXPECTED_SIG_TLV IMAGE_TLV_ECDSA_SIG +#elif defined(MCUBOOT_SIGN_ED25519) +#define EXPECTED_SIG_TLV IMAGE_TLV_ED25519 +#endif + +#define DECOMP_BUF_SIZE CONFIG_BOOT_DECOMPRESSION_BUFFER_SIZE +#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB) +/* Extra buffer space for being able to writeback ARM thumb decompression output, + * which may be of +2 bytes more size than its input. + */ +#define DECOMP_BUF_EXTRA_SIZE 2 +#else +#define DECOMP_BUF_EXTRA_SIZE 0 +#endif +#define DECOMP_BUF_ALLOC_SIZE (DECOMP_BUF_SIZE + DECOMP_BUF_EXTRA_SIZE) + +#define DECRYPTION_BLOCK_SIZE_AES128 16 +#define DECRYPTION_BLOCK_SIZE_AES256 32 + +/* Number of times that consumed data by decompression system can be 0 in a row before aborting */ +#define OFFSET_ZERO_CHECK_TIMES 3 + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +static int boot_sha_protected_tlvs(const struct image_header *hdr, + const struct flash_area *fap_src, uint32_t protected_size, + uint8_t *buf, size_t buf_size, bootutil_sha_context *sha_ctx); + +bool boot_is_compressed_header_valid(const struct image_header *hdr, const struct flash_area *fap, + struct boot_loader_state *state) +{ + /* Image is compressed in secondary slot, need to check if fits into the primary slot */ + bool opened_flash_area = false; + int primary_fa_id; + int rc; + int size_check; + int size; + uint32_t protected_tlvs_size; + uint32_t decompressed_size; + + primary_fa_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), BOOT_PRIMARY_SLOT); + + if (primary_fa_id == fap->fa_id) { + BOOT_LOG_ERR("Primary slots cannot be compressed, image: %d", BOOT_CURR_IMG(state)); + return false; + } + + if (BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT) == NULL) { + opened_flash_area = true; + } + + rc = flash_area_open(primary_fa_id, &BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT)); + assert(rc == 0); + + size_check = flash_area_get_size(BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT)); + + if (opened_flash_area) { + (void)flash_area_close(BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT)); + } + + rc = bootutil_get_img_decomp_size(hdr, fap, &decompressed_size); + + if (rc) { + return false; + } + + if (!boot_u32_safe_add(&size, decompressed_size, hdr->ih_hdr_size)) { + return false; + } + + rc = boot_size_protected_tlvs(hdr, fap, &protected_tlvs_size); + + if (rc) { + return false; + } + + if (!boot_u32_safe_add(&size, size, protected_tlvs_size)) { + return false; + } + + if (size >= size_check) { + BOOT_LOG_ERR("Compressed image too large, decompressed image size: 0x%x, slot size: 0x%x", + size, size_check); + return false; + } + + return true; +} + +static bool is_compression_object_valid(struct nrf_compress_implementation *compression) +{ + if (compression == NULL || compression->init == NULL || compression->deinit == NULL || + compression->decompress_bytes_needed == NULL || compression->decompress == NULL) { + return false; + } + + return true; +} + +#ifdef MCUBOOT_ENC_IMAGES +int bootutil_get_img_decrypted_comp_size(const struct image_header *hdr, + const struct flash_area *fap, uint32_t *img_comp_size) +{ + if (hdr == NULL || fap == NULL || img_comp_size == NULL) { + return BOOT_EBADARGS; + } else if (hdr->ih_protect_tlv_size == 0) { + return BOOT_EBADIMAGE; + } + + if (!IS_ENCRYPTED(hdr)) { + /* Update is not encrypted so use size from header */ + *img_comp_size = hdr->ih_img_size; + } else { + struct image_tlv_iter it; + uint32_t off; + uint16_t len; + int32_t rc; + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_COMP_DEC_SIZE, true); + + if (rc) { + return rc; + } + + rc = bootutil_tlv_iter_next(&it, &off, &len, NULL); + + if (rc != 0) { + return -1; + } + + if (len != sizeof(*img_comp_size)) { + BOOT_LOG_ERR("Invalid decompressed image size TLV: %d", len); + return BOOT_EBADIMAGE; + } + + rc = LOAD_IMAGE_DATA(hdr, fap, off, img_comp_size, len); + + if (rc) { + BOOT_LOG_ERR("Image data load failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + off, len, fap->fa_id, rc); + return BOOT_EFLASH; + } + } + + return 0; +} +#endif + +int bootutil_img_hash_decompress(struct boot_loader_state *state, struct image_header *hdr, + const struct flash_area *fap, uint8_t *tmp_buf, + uint32_t tmp_buf_sz, uint8_t *hash_result, + uint8_t *seed, int seed_len) +{ + int rc; + uint32_t read_pos = 0; + uint32_t write_pos = 0; + uint32_t protected_tlv_size = 0; + uint32_t decompressed_image_size; + uint32_t output_size_total = 0; + struct nrf_compress_implementation *compression_lzma = NULL; + struct nrf_compress_implementation *compression_arm_thumb = NULL; + TARGET_STATIC struct image_header modified_hdr; + bootutil_sha_context sha_ctx; + +#ifdef MCUBOOT_ENC_IMAGES + struct enc_key_data *enc_state; + int image_index; + uint32_t comp_size = 0; + uint8_t decryption_block_size = 0; + + rc = bootutil_get_img_decrypted_comp_size(hdr, fap, &comp_size); + + if (rc) { + BOOT_LOG_ERR("Invalid/missing image decrypted compressed size value"); + rc = BOOT_EBADIMAGE; + goto finish_end; + } + + if (state == NULL) { + enc_state = NULL; + image_index = 0; + } else { + enc_state = BOOT_CURR_ENC(state); + image_index = BOOT_CURR_IMG(state); + } + + /* Encrypted images only exist in the secondary slot */ + if (MUST_DECRYPT(fap, image_index, hdr) && + !boot_enc_valid(enc_state, 1)) { + return -1; + } + + if (MUST_DECRYPT(fap, image_index, hdr)) { + if (hdr->ih_flags & IMAGE_F_ENCRYPTED_AES128) { + decryption_block_size = DECRYPTION_BLOCK_SIZE_AES128; + } else if (hdr->ih_flags & IMAGE_F_ENCRYPTED_AES256) { + decryption_block_size = DECRYPTION_BLOCK_SIZE_AES256; + } else { + LOG_ERR("Unknown decryption block size"); + rc = BOOT_EBADIMAGE; + goto finish_end; + } + } +#endif + + bootutil_sha_init(&sha_ctx); + + /* Setup decompression system */ +#if CONFIG_NRF_COMPRESS_LZMA_VERSION_LZMA1 + if (!(hdr->ih_flags & IMAGE_F_COMPRESSED_LZMA1)) { +#elif CONFIG_NRF_COMPRESS_LZMA_VERSION_LZMA2 + if (!(hdr->ih_flags & IMAGE_F_COMPRESSED_LZMA2)) { +#endif + /* Compressed image does not use the correct compression type which is supported by this + * build + */ + BOOT_LOG_ERR("Invalid image compression flags: no supported compression found"); + rc = BOOT_EBADIMAGE; + goto finish_without_clean; + } + + compression_lzma = nrf_compress_implementation_find(NRF_COMPRESS_TYPE_LZMA); + compression_arm_thumb = nrf_compress_implementation_find(NRF_COMPRESS_TYPE_ARM_THUMB); + + if (!is_compression_object_valid(compression_lzma) || + !is_compression_object_valid(compression_arm_thumb)) { + /* Compression library missing or missing required function pointer */ + BOOT_LOG_ERR("Decompression library fatal error"); + rc = BOOT_EBADSTATUS; + goto finish_without_clean; + } + + /* We need a modified header which has the updated sizes, start with the original header */ + memcpy(&modified_hdr, hdr, sizeof(modified_hdr)); + + /* Extract the decompressed image size from the protected TLV, set it and remove the + * compressed image flags + */ + rc = bootutil_get_img_decomp_size(hdr, fap, &decompressed_image_size); + + if (rc) { + BOOT_LOG_ERR("Unable to determine decompressed size of compressed image"); + rc = BOOT_EBADIMAGE; + goto finish_without_clean; + } + + modified_hdr.ih_flags &= ~COMPRESSIONFLAGS; + modified_hdr.ih_img_size = decompressed_image_size; + + rc = compression_lzma->init(NULL, decompressed_image_size); + + if (rc) { + BOOT_LOG_ERR("Decompression library fatal error"); + rc = BOOT_EBADSTATUS; + goto finish_without_clean; + } + + rc = compression_arm_thumb->init(NULL, decompressed_image_size); + + if (rc) { + BOOT_LOG_ERR("Decompression library fatal error"); + rc = BOOT_EBADSTATUS; + goto finish; + } + + /* Calculate the protected TLV size, these will not include the decompressed + * sha/size/signature entries + */ + rc = boot_size_protected_tlvs(hdr, fap, &protected_tlv_size); + + if (rc) { + BOOT_LOG_ERR("Unable to determine protected TLV size of compressed image"); + rc = BOOT_EBADIMAGE; + goto finish; + } + + modified_hdr.ih_protect_tlv_size = protected_tlv_size; + bootutil_sha_update(&sha_ctx, &modified_hdr, sizeof(modified_hdr)); + read_pos = sizeof(modified_hdr); + + while (read_pos < modified_hdr.ih_hdr_size) { + uint32_t copy_size = tmp_buf_sz; + + if ((read_pos + copy_size) > modified_hdr.ih_hdr_size) { + copy_size = modified_hdr.ih_hdr_size - read_pos; + } + + rc = flash_area_read(fap, read_pos, tmp_buf, copy_size); + + if (rc != 0) { + BOOT_LOG_ERR("Flash read failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (hdr->ih_hdr_size + read_pos), copy_size, fap->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + + bootutil_sha_update(&sha_ctx, tmp_buf, copy_size); + read_pos += copy_size; + } + + /* Read in compressed data, decompress and add to hash calculation */ + read_pos = 0; + +#ifdef MCUBOOT_ENC_IMAGES + while (read_pos < comp_size) { + uint32_t copy_size = comp_size - read_pos; +#else + while (read_pos < hdr->ih_img_size) { + uint32_t copy_size = hdr->ih_img_size - read_pos; +#endif + uint32_t tmp_off = 0; + uint8_t offset_zero_check = 0; + + if (copy_size > tmp_buf_sz) { + copy_size = tmp_buf_sz; + } + + rc = flash_area_read(fap, (hdr->ih_hdr_size + read_pos), tmp_buf, copy_size); + + if (rc != 0) { + BOOT_LOG_ERR("Flash read failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (hdr->ih_hdr_size + read_pos), copy_size, fap->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + +#ifdef MCUBOOT_ENC_IMAGES + if (MUST_DECRYPT(fap, image_index, hdr)) { + uint8_t dummy_bytes = 0; + + if ((copy_size % decryption_block_size)) { + dummy_bytes = decryption_block_size - (copy_size % decryption_block_size); + memset(&tmp_buf[copy_size], 0x00, dummy_bytes); + } + + boot_enc_decrypt(enc_state, 1, read_pos, (copy_size + dummy_bytes), (read_pos & 0xf), + tmp_buf); + } +#endif + + /* Decompress data in chunks, writing it back with a larger write offset of the primary + * slot than read size of the secondary slot + */ + while (tmp_off < copy_size) { + uint32_t offset = 0; + uint8_t *output = NULL; + uint32_t output_size = 0; + uint32_t chunk_size; + bool last_packet = false; + + chunk_size = compression_lzma->decompress_bytes_needed(NULL); + + if (chunk_size > (copy_size - tmp_off)) { + chunk_size = (copy_size - tmp_off); + } + +#ifdef MCUBOOT_ENC_IMAGES + if ((read_pos + tmp_off + chunk_size) >= comp_size) { +#else + if ((read_pos + tmp_off + chunk_size) >= hdr->ih_img_size) { +#endif + last_packet = true; + } + + rc = compression_lzma->decompress(NULL, &tmp_buf[tmp_off], chunk_size, last_packet, + &offset, &output, &output_size); + + if (rc) { + BOOT_LOG_ERR("Decompression error: %d", rc); + rc = BOOT_EBADSTATUS; + goto finish; + } + + write_pos += output_size; + + if (write_pos > decompressed_image_size) { + BOOT_LOG_ERR("Decompressed image larger than claimed TLV size, at least: %d", + write_pos); + rc = BOOT_EBADIMAGE; + goto finish; + } + + /* Additional dry-run validity checks */ + if (last_packet == true && write_pos == 0) { + /* Last packet and we still have no output, this is a faulty update */ + BOOT_LOG_ERR("All compressed data consumed without any output, image not valid"); + rc = BOOT_EBADIMAGE; + goto finish; + } + + if (offset == 0) { + /* If the decompression system continually consumes 0 bytes, then there is a + * problem with this update image, abort and mark image as bad + */ + if (offset_zero_check >= OFFSET_ZERO_CHECK_TIMES) { + BOOT_LOG_ERR("Decompression system returning no output data, image not valid"); + rc = BOOT_EBADIMAGE; + goto finish; + } + + ++offset_zero_check; + + break; + } else { + offset_zero_check = 0; + } + + /* Copy data to secondary buffer for calculating hash */ + if (output_size > 0) { + if (hdr->ih_flags & IMAGE_F_COMPRESSED_ARM_THUMB_FLT) { + /* Run this through the ARM thumb filter */ + uint32_t offset_arm_thumb = 0; + uint8_t *output_arm_thumb = NULL; + uint32_t processed_size = 0; + uint32_t output_size_arm_thumb = 0; + + while (processed_size < output_size) { + uint32_t current_size = output_size - processed_size; + bool arm_thumb_last_packet = false; + + if (current_size > CONFIG_NRF_COMPRESS_CHUNK_SIZE) { + current_size = CONFIG_NRF_COMPRESS_CHUNK_SIZE; + } + + if (last_packet && (processed_size + current_size) == + output_size) { + arm_thumb_last_packet = true; + } + + rc = compression_arm_thumb->decompress(NULL, &output[processed_size], + current_size, arm_thumb_last_packet, + &offset_arm_thumb, + &output_arm_thumb, + &output_size_arm_thumb); + + if (rc) { + BOOT_LOG_ERR("Decompression error: %d", rc); + rc = BOOT_EBADSTATUS; + goto finish; + } + + bootutil_sha_update(&sha_ctx, output_arm_thumb, output_size_arm_thumb); + output_size_total += output_size_arm_thumb; + processed_size += current_size; + } + } else { + bootutil_sha_update(&sha_ctx, output, output_size); + output_size_total += output_size; + } + } + + tmp_off += offset; + } + + read_pos += copy_size; + } + + if (modified_hdr.ih_img_size != output_size_total) { + BOOT_LOG_ERR("Decompression expected output_size mismatch: %d vs %d", + modified_hdr.ih_img_size, output_size_total); + rc = BOOT_EBADSTATUS; + goto finish; + } + + /* If there are any protected TLVs present, add them after the main decompressed image */ + if (modified_hdr.ih_protect_tlv_size > 0) { + rc = boot_sha_protected_tlvs(hdr, fap, modified_hdr.ih_protect_tlv_size, tmp_buf, + tmp_buf_sz, &sha_ctx); + } + + bootutil_sha_finish(&sha_ctx, hash_result); + +finish: + /* Clean up decompression system */ + (void)compression_lzma->deinit(NULL); + (void)compression_arm_thumb->deinit(NULL); + +finish_without_clean: + bootutil_sha_drop(&sha_ctx); + +#ifdef MCUBOOT_ENC_IMAGES +finish_end: +#endif + return rc; +} + +static int boot_copy_protected_tlvs(const struct image_header *hdr, + const struct flash_area *fap_src, + const struct flash_area *fap_dst, uint32_t off_dst, + uint32_t protected_size, uint8_t *buf, size_t buf_size, + uint16_t *buf_pos, uint32_t *written) +{ + int rc; + uint32_t off; + uint32_t write_pos = 0; + uint16_t len; + uint16_t type; + struct image_tlv_iter it; + struct image_tlv tlv_header; + struct image_tlv_info tlv_info_header = { + .it_magic = IMAGE_TLV_PROT_INFO_MAGIC, + .it_tlv_tot = protected_size, + }; + uint16_t info_size_left = sizeof(tlv_info_header); + + while (info_size_left > 0) { + uint16_t copy_size = buf_size - *buf_pos; + + if (info_size_left > 0 && copy_size > 0) { + uint16_t single_copy_size = copy_size; + uint8_t *tlv_info_header_address = (uint8_t *)&tlv_info_header; + + if (single_copy_size > info_size_left) { + single_copy_size = info_size_left; + } + + memcpy(&buf[*buf_pos], &tlv_info_header_address[sizeof(tlv_info_header) - + info_size_left], single_copy_size); + *buf_pos += single_copy_size; + info_size_left -= single_copy_size; + } + + if (*buf_pos == buf_size) { + rc = flash_area_write(fap_dst, (off_dst + write_pos), buf, *buf_pos); + + if (rc != 0) { + BOOT_LOG_ERR("Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + write_pos), *buf_pos, fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto out; + } + + write_pos += *buf_pos; + *buf_pos = 0; + } + } + + rc = bootutil_tlv_iter_begin(&it, hdr, fap_src, IMAGE_TLV_ANY, true); + + if (rc) { + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } + + if (type == IMAGE_TLV_DECOMP_SIZE || type == IMAGE_TLV_DECOMP_SHA || + type == IMAGE_TLV_DECOMP_SIGNATURE || type == IMAGE_TLV_COMP_DEC_SIZE) { + /* Skip these TLVs as they are not needed */ + continue; + } else { + uint16_t header_size_left = sizeof(tlv_header); + uint16_t data_size_left = len; + + tlv_header.it_type = type; + tlv_header.it_len = len; + + while (header_size_left > 0 || data_size_left > 0) { + uint16_t copy_size = buf_size - *buf_pos; + uint8_t *tlv_header_address = (uint8_t *)&tlv_header; + + if (header_size_left > 0 && copy_size > 0) { + uint16_t single_copy_size = copy_size; + + if (single_copy_size > header_size_left) { + single_copy_size = header_size_left; + } + + memcpy(&buf[*buf_pos], &tlv_header_address[sizeof(tlv_header) - + header_size_left], + single_copy_size); + *buf_pos += single_copy_size; + copy_size -= single_copy_size; + header_size_left -= single_copy_size; + } + + if (data_size_left > 0 && copy_size > 0) { + uint16_t single_copy_size = copy_size; + + if (single_copy_size > data_size_left) { + single_copy_size = data_size_left; + } + + rc = LOAD_IMAGE_DATA(hdr, fap_src, (off + (len - data_size_left)), + &buf[*buf_pos], single_copy_size); + + if (rc) { + BOOT_LOG_ERR( + "Image data load failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off + (len - data_size_left)), single_copy_size, fap_src->fa_id, rc); + goto out; + } + + *buf_pos += single_copy_size; + data_size_left -= single_copy_size; + } + + if (*buf_pos == buf_size) { + rc = flash_area_write(fap_dst, (off_dst + write_pos), buf, *buf_pos); + + if (rc != 0) { + BOOT_LOG_ERR( + "Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + write_pos), *buf_pos, fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto out; + } + + write_pos += *buf_pos; + *buf_pos = 0; + } + } + } + } + + *written = write_pos; + +out: + return rc; +} + +static int boot_sha_protected_tlvs(const struct image_header *hdr, + const struct flash_area *fap_src, uint32_t protected_size, + uint8_t *buf, size_t buf_size, bootutil_sha_context *sha_ctx) +{ + int rc; + uint32_t off; + uint16_t len; + uint16_t type; + struct image_tlv_iter it; + struct image_tlv tlv_header; + struct image_tlv_info tlv_info_header = { + .it_magic = IMAGE_TLV_PROT_INFO_MAGIC, + .it_tlv_tot = protected_size, + }; + + bootutil_sha_update(sha_ctx, &tlv_info_header, sizeof(tlv_info_header)); + + rc = bootutil_tlv_iter_begin(&it, hdr, fap_src, IMAGE_TLV_ANY, true); + if (rc) { + goto out; + } + + while (true) { + uint32_t read_off = 0; + + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } + + if (type == IMAGE_TLV_DECOMP_SIZE || type == IMAGE_TLV_DECOMP_SHA || + type == IMAGE_TLV_DECOMP_SIGNATURE || type == IMAGE_TLV_COMP_DEC_SIZE) { + /* Skip these TLVs as they are not needed */ + continue; + } + + tlv_header.it_type = type; + tlv_header.it_len = len; + + bootutil_sha_update(sha_ctx, &tlv_header, sizeof(tlv_header)); + + while (read_off < len) { + uint32_t copy_size = buf_size; + + if (copy_size > (len - read_off)) { + copy_size = len - read_off; + } + + rc = LOAD_IMAGE_DATA(hdr, fap_src, (off + read_off), buf, copy_size); + + if (rc) { + BOOT_LOG_ERR( + "Image data load failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off + read_off), copy_size, fap_src->fa_id, rc); + goto out; + } + + bootutil_sha_update(sha_ctx, buf, copy_size); + read_off += copy_size; + } + } + +out: + return rc; +} + +int boot_size_protected_tlvs(const struct image_header *hdr, const struct flash_area *fap, + uint32_t *sz) +{ + int rc = 0; + uint32_t tlv_size; + uint32_t off; + uint16_t len; + uint16_t type; + struct image_tlv_iter it; + + *sz = 0; + tlv_size = hdr->ih_protect_tlv_size; + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, true); + + if (rc) { + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } + + if (type == IMAGE_TLV_DECOMP_SIZE || type == IMAGE_TLV_DECOMP_SHA || + type == IMAGE_TLV_DECOMP_SIGNATURE || type == IMAGE_TLV_COMP_DEC_SIZE) { + /* Exclude these TLVs as they will be copied to the unprotected area */ + tlv_size -= len + sizeof(struct image_tlv); + } + } + + if (!rc) { + if (tlv_size == sizeof(struct image_tlv_info)) { + /* If there are no entries then omit protected TLV section entirely */ + tlv_size = 0; + } + + *sz = tlv_size; + } + +out: + return rc; +} + +int boot_size_unprotected_tlvs(const struct image_header *hdr, const struct flash_area *fap, + uint32_t *sz) +{ + int rc = 0; + uint32_t tlv_size; + uint32_t off; + uint16_t len; + uint16_t type; + struct image_tlv_iter it; + + *sz = 0; + tlv_size = sizeof(struct image_tlv_info); + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, false); + + if (rc) { + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } else if (bootutil_tlv_iter_is_prot(&it, off) && type != IMAGE_TLV_DECOMP_SHA && + type != IMAGE_TLV_DECOMP_SIGNATURE) { + /* Include size of protected hash and signature as these will be replacing the + * original ones + */ + continue; + } else if (type == EXPECTED_HASH_TLV || type == EXPECTED_SIG_TLV || type == IMAGE_TLV_COMP_DEC_SIZE) { + /* Exclude the original unprotected TLVs for signature and hash, the length of the + * signature of the compressed data might not be the same size as the signaute of the + * decompressed data, as is the case when using ECDSA-P256 + */ + continue; + } + + tlv_size += len + sizeof(struct image_tlv); + } + + if (!rc) { + if (tlv_size == sizeof(struct image_tlv_info)) { + /* If there are no entries in the unprotected TLV section then there is something wrong + * with this image + */ + BOOT_LOG_ERR("No unprotected TLVs in post-decompressed image output, image is invalid"); + rc = BOOT_EBADIMAGE; + goto out; + } + + *sz = tlv_size; + } + +out: + return rc; +} + +static int boot_copy_unprotected_tlvs(const struct image_header *hdr, + const struct flash_area *fap_src, + const struct flash_area *fap_dst, uint32_t off_dst, + uint32_t unprotected_size, uint8_t *buf, size_t buf_size, + uint16_t *buf_pos, uint32_t *written) +{ + int rc; + uint32_t write_pos = 0; + uint32_t off; + uint16_t len; + uint16_t type; + struct image_tlv_iter it; + struct image_tlv_iter it_protected; + struct image_tlv tlv_header; + struct image_tlv_info tlv_info_header = { + .it_magic = IMAGE_TLV_INFO_MAGIC, + .it_tlv_tot = unprotected_size, + }; + uint16_t info_size_left = sizeof(tlv_info_header); + + while (info_size_left > 0) { + uint16_t copy_size = buf_size - *buf_pos; + + if (info_size_left > 0 && copy_size > 0) { + uint16_t single_copy_size = copy_size; + uint8_t *tlv_info_header_address = (uint8_t *)&tlv_info_header; + + if (single_copy_size > info_size_left) { + single_copy_size = info_size_left; + } + + memcpy(&buf[*buf_pos], &tlv_info_header_address[sizeof(tlv_info_header) - + info_size_left], single_copy_size); + *buf_pos += single_copy_size; + info_size_left -= single_copy_size; + } + + if (*buf_pos == buf_size) { + rc = flash_area_write(fap_dst, (off_dst + write_pos), buf, *buf_pos); + + if (rc != 0) { + BOOT_LOG_ERR("Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + write_pos), *buf_pos, fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto out; + } + + write_pos += *buf_pos; + *buf_pos = 0; + } + } + + rc = bootutil_tlv_iter_begin(&it, hdr, fap_src, IMAGE_TLV_ANY, false); + if (rc) { + goto out; + } + + while (true) { + uint16_t header_size_left = sizeof(tlv_header); + uint16_t data_size_left; + + rc = bootutil_tlv_iter_next(&it, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } else if (bootutil_tlv_iter_is_prot(&it, off)) { + /* Skip protected TLVs */ + continue; + } + + /* Change the values of these fields from having the data in the compressed image + * unprotected TLV (which is valid only for the compressed image data) to having the + * fields in the protected TLV section (which is valid for the decompressed image data). + * The compressed data is no longer needed + */ + if (type == EXPECTED_HASH_TLV || type == EXPECTED_SIG_TLV) { + rc = bootutil_tlv_iter_begin(&it_protected, hdr, fap_src, (type == EXPECTED_HASH_TLV ? + IMAGE_TLV_DECOMP_SHA : + IMAGE_TLV_DECOMP_SIGNATURE), + true); + + if (rc) { + goto out; + } + + while (true) { + rc = bootutil_tlv_iter_next(&it_protected, &off, &len, &type); + if (rc < 0) { + goto out; + } else if (rc > 0) { + rc = 0; + break; + } + } + + if (type == IMAGE_TLV_DECOMP_SHA) { + type = EXPECTED_HASH_TLV; + } else { + type = EXPECTED_SIG_TLV; + } + } + + data_size_left = len; + tlv_header.it_type = type; + tlv_header.it_len = len; + + while (header_size_left > 0 || data_size_left > 0) { + uint16_t copy_size = buf_size - *buf_pos; + + if (header_size_left > 0 && copy_size > 0) { + uint16_t single_copy_size = copy_size; + uint8_t *tlv_header_address = (uint8_t *)&tlv_header; + + if (single_copy_size > header_size_left) { + single_copy_size = header_size_left; + } + + memcpy(&buf[*buf_pos], &tlv_header_address[sizeof(tlv_header) - header_size_left], + single_copy_size); + *buf_pos += single_copy_size; + copy_size -= single_copy_size; + header_size_left -= single_copy_size; + } + + if (data_size_left > 0 && copy_size > 0) { + uint16_t single_copy_size = copy_size; + + if (single_copy_size > data_size_left) { + single_copy_size = data_size_left; + } + + rc = LOAD_IMAGE_DATA(hdr, fap_src, (off + len - data_size_left), + &buf[*buf_pos], single_copy_size); + + if (rc) { + BOOT_LOG_ERR( + "Image data load failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off + (len - data_size_left)), single_copy_size, fap_src->fa_id, rc); + goto out; + } + + *buf_pos += single_copy_size; + data_size_left -= single_copy_size; + } + + if (*buf_pos == buf_size) { + rc = flash_area_write(fap_dst, (off_dst + write_pos), buf, *buf_pos); + + if (rc != 0) { + BOOT_LOG_ERR( + "Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + write_pos), *buf_pos, fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto out; + } + + write_pos += *buf_pos; + *buf_pos = 0; + } + } + } + + *written = write_pos; + +out: + return rc; +} + +#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB) +/** + * @brief Helper function for in-place ARM Thumb filtering. + * This function places the decompressed data back into the same buffer + * at the beginning, overwriting the compressed data. WARNING: because + * ARM Thumb filtering can return +-2 more/less bytes than the input, + * the buffer provided needs to have free DECOMP_BUF_EXTRA_SIZE bytes at + * the beginning and provide valid data for filtering after these. + * + * @param[in] arm_thumb_impl Pointer to the ARM Thumb decompression implementation. + * @param[in,out] buf Pointer to the buffer containing the compressed data / filtered data. + * @param[in] buf_size Size of the buffer (including DECOMP_BUF_EXTRA_SIZE bytes at the beginning). + * @param[out] out_size Pointer to a variable where the size of the filtered data will be stored. + * @param[in] last_part Indicates if this is the last part of the data to be filtered. + * + * @return 0 on success, BOOT_EBADSTATUS on error. + */ +static int boot_arm_thumb_filter(struct nrf_compress_implementation * const arm_thumb_impl, + uint8_t *buf, size_t buf_size, size_t *out_size, bool last_part) { + + uint32_t filter_writeback_pos = 0; + uint32_t processed_size = 0; + int rc; + + while (processed_size < (buf_size - DECOMP_BUF_EXTRA_SIZE)) { + uint32_t offset_arm_thumb = 0; + uint32_t output_size_arm_thumb = 0; + uint8_t *output_arm_thumb = NULL; + uint32_t current_size = (buf_size - DECOMP_BUF_EXTRA_SIZE - processed_size); + bool arm_thumb_last_packet = false; + + if (current_size > CONFIG_NRF_COMPRESS_CHUNK_SIZE) { + current_size = CONFIG_NRF_COMPRESS_CHUNK_SIZE; + } + + if (last_part && (processed_size + current_size) == (buf_size - DECOMP_BUF_EXTRA_SIZE)) { + arm_thumb_last_packet = true; + } + + rc = arm_thumb_impl->decompress(NULL, + &buf[processed_size + + DECOMP_BUF_EXTRA_SIZE], + current_size, + arm_thumb_last_packet, + &offset_arm_thumb, + &output_arm_thumb, + &output_size_arm_thumb); + + if (rc) { + BOOT_LOG_ERR("Decompression error: %d", rc); + return BOOT_EBADSTATUS; + } + + if (output_size_arm_thumb > (buf_size - filter_writeback_pos)) { + BOOT_LOG_ERR("Filter writeback position exceeds buffer size"); + return BOOT_EBADSTATUS; + } + + memcpy(&buf[filter_writeback_pos], output_arm_thumb, + output_size_arm_thumb); + filter_writeback_pos += output_size_arm_thumb; + processed_size += offset_arm_thumb; + } + *out_size = filter_writeback_pos; + + return 0; +} +#endif /* CONFIG_NRF_COMPRESS_ARM_THUMB */ + +int boot_copy_region_decompress(struct boot_loader_state *state, const struct flash_area *fap_src, + const struct flash_area *fap_dst, uint32_t off_src, + uint32_t off_dst, uint32_t sz, uint8_t *buf, size_t buf_size) +{ + int rc; + uint32_t pos = 0; + uint16_t decomp_buf_size = 0; + uint16_t write_alignment; + uint32_t write_pos = 0; + uint32_t protected_tlv_size = 0; + uint32_t unprotected_tlv_size = 0; + uint32_t tlv_write_size = 0; + uint32_t decompressed_image_size; + struct nrf_compress_implementation *compression_lzma = NULL; + struct nrf_compress_implementation *compression_arm_thumb = NULL; + struct image_header *hdr; + TARGET_STATIC uint8_t decomp_buf[DECOMP_BUF_ALLOC_SIZE] __attribute__((aligned(4))); + TARGET_STATIC struct image_header modified_hdr; + uint16_t decomp_buf_max_size; + +#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB) + uint8_t unaligned_data_length = 0; +#endif + +#ifdef MCUBOOT_ENC_IMAGES + uint32_t comp_size = 0; + uint8_t decryption_block_size = 0; +#endif + + hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + +#ifdef MCUBOOT_ENC_IMAGES + rc = bootutil_get_img_decrypted_comp_size(hdr, fap_src, &comp_size); + + if (rc) { + BOOT_LOG_ERR("Invalid/missing image decrypted compressed size value"); + rc = BOOT_EBADIMAGE; + goto finish_without_clean; + } + + if (IS_ENCRYPTED(hdr)) { + if (hdr->ih_flags & IMAGE_F_ENCRYPTED_AES128) { + decryption_block_size = DECRYPTION_BLOCK_SIZE_AES128; + } else if (hdr->ih_flags & IMAGE_F_ENCRYPTED_AES256) { + decryption_block_size = DECRYPTION_BLOCK_SIZE_AES256; + } + } +#endif + + /* Setup decompression system */ +#if CONFIG_NRF_COMPRESS_LZMA_VERSION_LZMA1 + if (!(hdr->ih_flags & IMAGE_F_COMPRESSED_LZMA1)) { +#elif CONFIG_NRF_COMPRESS_LZMA_VERSION_LZMA2 + if (!(hdr->ih_flags & IMAGE_F_COMPRESSED_LZMA2)) { +#endif + /* Compressed image does not use the correct compression type which is supported by this + * build + */ + BOOT_LOG_ERR("Invalid image compression flags: no supported compression found"); + rc = BOOT_EBADIMAGE; + goto finish_without_clean; + } + + compression_lzma = nrf_compress_implementation_find(NRF_COMPRESS_TYPE_LZMA); + compression_arm_thumb = nrf_compress_implementation_find(NRF_COMPRESS_TYPE_ARM_THUMB); + + if (!is_compression_object_valid(compression_lzma) || + !is_compression_object_valid(compression_arm_thumb)) { + /* Compression library missing or missing required function pointer */ + BOOT_LOG_ERR("Decompression library fatal error"); + rc = BOOT_EBADSTATUS; + goto finish_without_clean; + } + + write_alignment = flash_area_align(fap_dst); + + decomp_buf_max_size = DECOMP_BUF_SIZE - (DECOMP_BUF_SIZE % write_alignment); + + memcpy(&modified_hdr, hdr, sizeof(modified_hdr)); + + rc = bootutil_get_img_decomp_size(hdr, fap_src, &decompressed_image_size); + + if (rc) { + BOOT_LOG_ERR("Unable to determine decompressed size of compressed image"); + rc = BOOT_EBADIMAGE; + goto finish_without_clean; + } + + modified_hdr.ih_flags &= ~COMPRESSIONFLAGS; + modified_hdr.ih_img_size = decompressed_image_size; + + rc = compression_lzma->init(NULL, decompressed_image_size); + + if (rc) { + BOOT_LOG_ERR("Decompression library fatal error"); + rc = BOOT_EBADSTATUS; + goto finish_without_clean; + } + + rc = compression_arm_thumb->init(NULL, decompressed_image_size); + + if (rc) { + BOOT_LOG_ERR("Decompression library fatal error"); + rc = BOOT_EBADSTATUS; + goto finish; + } + + /* Calculate protected TLV size for target image once items are removed */ + rc = boot_size_protected_tlvs(hdr, fap_src, &protected_tlv_size); + + if (rc) { + BOOT_LOG_ERR("Unable to determine protected TLV size of compressed image"); + rc = BOOT_EBADIMAGE; + goto finish; + } + + modified_hdr.ih_protect_tlv_size = protected_tlv_size; + + rc = boot_size_unprotected_tlvs(hdr, fap_src, &unprotected_tlv_size); + + if (rc) { + BOOT_LOG_ERR("Unable to determine unprotected TLV size of compressed image"); + rc = BOOT_EBADIMAGE; + goto finish; + } + + /* Write out the image header first, this should be a multiple of the write size */ + rc = flash_area_write(fap_dst, off_dst, &modified_hdr, sizeof(modified_hdr)); + + if (rc != 0) { + BOOT_LOG_ERR("Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + off_dst, sizeof(modified_hdr), fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + + /* Read in, decompress and write out data */ +#ifdef MCUBOOT_ENC_IMAGES + while (pos < comp_size) { + uint32_t copy_size = comp_size - pos; +#else + while (pos < hdr->ih_img_size) { + uint32_t copy_size = hdr->ih_img_size - pos; +#endif + uint32_t tmp_off = 0; + + if (copy_size > buf_size) { + copy_size = buf_size; + } + + rc = flash_area_read(fap_src, off_src + hdr->ih_hdr_size + pos, buf, copy_size); + + if (rc != 0) { + BOOT_LOG_ERR("Flash read failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_src + hdr->ih_hdr_size + pos), copy_size, fap_src->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + +#ifdef MCUBOOT_ENC_IMAGES + if (IS_ENCRYPTED(hdr)) { + uint8_t dummy_bytes = 0; + + if ((copy_size % decryption_block_size)) { + dummy_bytes = decryption_block_size - (copy_size % decryption_block_size); + memset(&buf[copy_size], 0x00, dummy_bytes); + } + + boot_enc_decrypt(BOOT_CURR_ENC(state), 1, pos, (copy_size + dummy_bytes), (pos & 0xf), buf); + } +#endif + + /* Decompress data in chunks, writing it back with a larger write offset of the primary + * slot than read size of the secondary slot + */ + while (tmp_off < copy_size) { + uint32_t offset = 0; + uint32_t output_size = 0; + uint32_t chunk_size; + uint32_t compression_buffer_pos = 0; + uint8_t *output = NULL; + bool last_packet = false; + + chunk_size = compression_lzma->decompress_bytes_needed(NULL); + + if (chunk_size > (copy_size - tmp_off)) { + chunk_size = (copy_size - tmp_off); + } + +#ifdef MCUBOOT_ENC_IMAGES + if ((pos + tmp_off + chunk_size) >= comp_size) { +#else + if ((pos + tmp_off + chunk_size) >= hdr->ih_img_size) { +#endif + last_packet = true; + } + + rc = compression_lzma->decompress(NULL, &buf[tmp_off], chunk_size, last_packet, + &offset, &output, &output_size); + + if (rc) { + BOOT_LOG_ERR("Decompression error: %d", rc); + rc = BOOT_EBADSTATUS; + goto finish; + } + + /* Copy data to secondary buffer for writing out */ + while (output_size > 0) { + uint32_t data_size = (decomp_buf_max_size - decomp_buf_size); + + if (data_size > output_size) { + data_size = output_size; + } + +#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB) + if (hdr->ih_flags & IMAGE_F_COMPRESSED_ARM_THUMB_FLT) { + memcpy(&decomp_buf[decomp_buf_size + DECOMP_BUF_EXTRA_SIZE], + &output[compression_buffer_pos], data_size); + } else +#endif + { + memcpy(&decomp_buf[decomp_buf_size], &output[compression_buffer_pos], + data_size); + } + + compression_buffer_pos += data_size; + + decomp_buf_size += data_size; + output_size -= data_size; + + /* Write data out from secondary buffer when it is full */ + if (decomp_buf_size == decomp_buf_max_size) { +#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB) + if (hdr->ih_flags & IMAGE_F_COMPRESSED_ARM_THUMB_FLT) { + + uint32_t filter_output_size; + + /* Run this through the ARM thumb filter */ + rc = boot_arm_thumb_filter(compression_arm_thumb, + &decomp_buf[unaligned_data_length], + decomp_buf_size - unaligned_data_length + DECOMP_BUF_EXTRA_SIZE, + &filter_output_size, + last_packet && output_size == 0); + + if (rc) { + goto finish; + } + + decomp_buf_size = filter_output_size + unaligned_data_length; + unaligned_data_length = decomp_buf_size % write_alignment; + + rc = flash_area_write(fap_dst, + (off_dst + hdr->ih_hdr_size + write_pos), + decomp_buf, + (decomp_buf_size - unaligned_data_length)); + + if (rc != 0) { + BOOT_LOG_ERR( + "Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + hdr->ih_hdr_size + write_pos), + (decomp_buf_size - unaligned_data_length), + fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + + memmove(decomp_buf, + &decomp_buf[decomp_buf_size - unaligned_data_length], + unaligned_data_length); + write_pos += decomp_buf_size - unaligned_data_length; + decomp_buf_size = unaligned_data_length; + } else +#endif + { + rc = flash_area_write(fap_dst, (off_dst + hdr->ih_hdr_size + write_pos), + decomp_buf, decomp_buf_max_size); + + if (rc != 0) { + BOOT_LOG_ERR( + "Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + hdr->ih_hdr_size + write_pos), decomp_buf_max_size, + fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + + write_pos += decomp_buf_max_size; + decomp_buf_size = 0; + } + } + } + + tmp_off += offset; + } + + pos += copy_size; + } + +#if defined(CONFIG_NRF_COMPRESS_ARM_THUMB) + if (hdr->ih_flags & IMAGE_F_COMPRESSED_ARM_THUMB_FLT && decomp_buf_size > 0) { + /* Extra data that has not been written out that needs ARM thumb filter applied */ + + uint32_t filter_output_size; + + rc = boot_arm_thumb_filter(compression_arm_thumb, + &decomp_buf[unaligned_data_length], + decomp_buf_size - unaligned_data_length + DECOMP_BUF_EXTRA_SIZE, + &filter_output_size, + true); + + if (rc) { + goto finish; + } + + decomp_buf_size = filter_output_size + unaligned_data_length; + + if (decomp_buf_size > decomp_buf_max_size) { + /* It can happen if ARM thumb decompression returned +2 bytes and we had near full + * decomp_buf. We still can hold these additional 2 bytes because of + * DECOMP_BUF_EXTRA_SIZE allocated. */ + + rc = flash_area_write(fap_dst, (off_dst + hdr->ih_hdr_size + write_pos), + decomp_buf, decomp_buf_max_size); + + if (rc != 0) { + BOOT_LOG_ERR("Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + hdr->ih_hdr_size + write_pos), decomp_buf_max_size, + fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + memmove(decomp_buf, &decomp_buf[decomp_buf_max_size], + (decomp_buf_size - decomp_buf_max_size)); + + decomp_buf_size = decomp_buf_size - decomp_buf_max_size; + write_pos += decomp_buf_max_size; + } + } +#endif + + /* Clean up decompression system */ + (void)compression_lzma->deinit(NULL); + (void)compression_arm_thumb->deinit(NULL); + + if (protected_tlv_size > 0) { + rc = boot_copy_protected_tlvs(hdr, fap_src, fap_dst, (off_dst + hdr->ih_hdr_size + + write_pos), protected_tlv_size, + decomp_buf, decomp_buf_max_size, &decomp_buf_size, + &tlv_write_size); + + if (rc) { + BOOT_LOG_ERR("Protected TLV copy failure: %d", rc); + goto finish; + } + + write_pos += tlv_write_size; + } + + tlv_write_size = 0; + rc = boot_copy_unprotected_tlvs(hdr, fap_src, fap_dst, (off_dst + hdr->ih_hdr_size + + write_pos), unprotected_tlv_size, + decomp_buf, decomp_buf_max_size, &decomp_buf_size, + &tlv_write_size); + + if (rc) { + BOOT_LOG_ERR("Protected TLV copy failure: %d", rc); + goto finish; + } + + write_pos += tlv_write_size; + + /* Check if we have unwritten data buffered up and, if so, write it out */ + if (decomp_buf_size > 0) { + uint32_t write_padding_size = write_alignment - (decomp_buf_size % write_alignment); + + /* Check if additional write padding should be applied to meet the minimum write size */ + if (write_alignment > 1 && write_padding_size) { + uint8_t flash_erased_value; + + flash_erased_value = flash_area_erased_val(fap_dst); + memset(&decomp_buf[decomp_buf_size], flash_erased_value, write_padding_size); + decomp_buf_size += write_padding_size; + } + + rc = flash_area_write(fap_dst, (off_dst + hdr->ih_hdr_size + write_pos), decomp_buf, + decomp_buf_size); + + if (rc != 0) { + BOOT_LOG_ERR("Flash write failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + (off_dst + hdr->ih_hdr_size + write_pos), decomp_buf_size, + fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + + write_pos += decomp_buf_size; + decomp_buf_size = 0; + } + +finish: + /* Clean up decompression system */ + (void)compression_lzma->deinit(NULL); + (void)compression_arm_thumb->deinit(NULL); + +finish_without_clean: + memset(decomp_buf, 0, sizeof(decomp_buf)); + + return rc; +} + +int bootutil_get_img_decomp_size(const struct image_header *hdr, const struct flash_area *fap, + uint32_t *img_decomp_size) +{ + struct image_tlv_iter it; + uint32_t off; + uint16_t len; + int32_t rc; + + if (hdr == NULL || fap == NULL || img_decomp_size == NULL) { + return BOOT_EBADARGS; + } else if (hdr->ih_protect_tlv_size == 0) { + return BOOT_EBADIMAGE; + } + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_DECOMP_SIZE, true); + + if (rc) { + return rc; + } + + rc = bootutil_tlv_iter_next(&it, &off, &len, NULL); + + if (rc != 0) { + return -1; + } + + if (len != sizeof(*img_decomp_size)) { + BOOT_LOG_ERR("Invalid decompressed image size TLV: %d", len); + return BOOT_EBADIMAGE; + } + + rc = LOAD_IMAGE_DATA(hdr, fap, off, img_decomp_size, len); + + if (rc) { + BOOT_LOG_ERR("Image data load failed at offset: 0x%x, size: 0x%x, area: %d, rc: %d", + off, len, fap->fa_id, rc); + return BOOT_EFLASH; + } + + return 0; +} diff --git a/boot/zephyr/external_crypto.conf b/boot/zephyr/external_crypto.conf new file mode 100644 index 000000000..8181ad51c --- /dev/null +++ b/boot/zephyr/external_crypto.conf @@ -0,0 +1,20 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +# These configurations should be used when using nrf/samples/bootloader +# as the immutable bootloader (B0), and MCUBoot as the second stage updateable +# bootloader. + +# Set ECDSA as signing mechanism +CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256=y + +# Use crypto backend from B0 +CONFIG_BOOT_NRF_EXTERNAL_CRYPTO=y +CONFIG_SECURE_BOOT_CRYPTO=y +CONFIG_SB_CRYPTO_CLIENT_ECDSA_SECP256R1=y +CONFIG_SB_CRYPTO_CLIENT_SHA256=y +CONFIG_BL_SHA256_EXT_API_REQUIRED=y +CONFIG_BL_SECP256R1_EXT_API_REQUIRED=y diff --git a/boot/zephyr/firmware_loader.c b/boot/zephyr/firmware_loader.c index d0f70af4a..1df848634 100644 --- a/boot/zephyr/firmware_loader.c +++ b/boot/zephyr/firmware_loader.c @@ -40,6 +40,8 @@ boot_image_validate(const struct flash_area *fa_p, static uint8_t tmpbuf[BOOT_TMPBUF_SZ]; FIH_DECLARE(fih_rc, FIH_FAILURE); + BOOT_LOG_DBG("boot_image_validate: encrypted == %d", (int)IS_ENCRYPTED(hdr)); + /* NOTE: The first argument to boot_image_validate, for enc_state pointer, * is allowed to be NULL only because the single image loader compiles * with BOOT_IMAGE_NUMBER == 1, which excludes the code that uses @@ -71,6 +73,8 @@ boot_image_validate_once(const struct flash_area *fa_p, int rc; FIH_DECLARE(fih_rc, FIH_FAILURE); + BOOT_LOG_DBG("boot_image_validate_once: flash area %p", fap_p); + memset(&state, 0, sizeof(struct boot_swap_state)); rc = boot_read_swap_state(fa_p, &state); if (rc != 0) @@ -108,6 +112,8 @@ static fih_ret validate_image_slot(int slot, struct boot_rsp *rsp) int rc = -1; FIH_DECLARE(fih_rc, FIH_FAILURE); + BOOT_LOG_DBG("validate_image_slot: slot %d", slot); + rc = flash_area_open(slot, &_fa_p); assert(rc == 0); @@ -156,6 +162,8 @@ boot_go(struct boot_rsp *rsp) bool boot_firmware_loader = false; FIH_DECLARE(fih_rc, FIH_FAILURE); + BOOT_LOG_DBG("boot_go: firmware loader"); + #ifdef CONFIG_BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO if (io_detect_pin() && !io_boot_skip_serial_recovery()) { diff --git a/boot/zephyr/firmware_loader_bm.c b/boot/zephyr/firmware_loader_bm.c new file mode 100644 index 000000000..14d5c96bd --- /dev/null +++ b/boot/zephyr/firmware_loader_bm.c @@ -0,0 +1,283 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include +#include +#include +#include "bootutil/image.h" +#include "bootutil_priv.h" +#include "bootutil/bootutil_log.h" +#include "bootutil/bootutil_public.h" +#include "bootutil/fault_injection_hardening.h" +#include + +#include "io/io.h" +#include "mcuboot_config/mcuboot_config.h" + +#define IMAGE_TLV_INSTALLER_IMAGE 0xa0 + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +static struct flash_area fa_app_installer = { + .fa_id = 1, + .fa_off = FIXED_PARTITION_OFFSET(slot0_partition), + .fa_size = FIXED_PARTITION_SIZE(slot0_partition), + .fa_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller)), +}; + +static struct image_header hdr_app_installer = { 0 }; + +static struct flash_area fa_softdevice = { + .fa_id = 2, + .fa_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller)), +}; + +static struct image_header hdr_softdevice = { 0 }; + +#ifdef CONFIG_BOOT_FIRMWARE_LOADER +static struct flash_area fa_firmware_loader = { + .fa_id = 3, + .fa_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_flash_controller)), +}; + +static struct image_header hdr_firmware_loader = { 0 }; +#endif + +/** + * Validate hash of a primary boot image. + * + * @param[in] fa_p flash area pointer + * @param[in] hdr boot image header pointer + * + * @return FIH_SUCCESS on success, error code otherwise + */ +static fih_ret validate_image(const struct flash_area *fap, struct image_header *hdr) +{ + static uint8_t tmpbuf[BOOT_TMPBUF_SZ]; + FIH_DECLARE(fih_rc, FIH_FAILURE); + + FIH_CALL(bootutil_img_validate, fih_rc, NULL, hdr, fap, tmpbuf, BOOT_TMPBUF_SZ, NULL, 0, NULL); + FIH_RET(fih_rc); +} + +/** + * Gather information on image and prepare for booting. Will boot from main + * image if none of the enabled entrance modes for the firmware loader are set, + * otherwise will boot the firmware loader. Note: firmware loader must be a + * valid signed image with the same signing key as the application image. + * + * @param[out] rsp Parameters for booting image, on success + * + * @return FIH_SUCCESS on success; non-zero on failure. + */ +fih_ret +boot_go(struct boot_rsp *rsp) +{ + bool boot_firmware_loader = false; + FIH_DECLARE(fih_rc, FIH_FAILURE); + bool softdevice_area_valid = false; + bool firmware_loader_area_valid = false; + int rc; + bool app_installer_image_valid = false; + bool softdevice_image_valid = false; + bool firmware_loader_image_valid = false; + bool app_installer_is_installer_image = false; + + bm_installs_init(); + + if (bm_installs_is_valid()) { + off_t start_address = 0; + size_t image_size = 0; + + rc = bm_installs_get_image_data(BM_INSTALLS_IMAGE_INDEX_SOFTDEVICE, &start_address, + &image_size); + + if (!rc) { + fa_softdevice.fa_off = start_address; + fa_softdevice.fa_size = image_size; + + if (start_address < fa_app_installer.fa_off) { + /* Invalid start address for SoftDevice */ + goto invalid_softdevice; + } + + fa_app_installer.fa_size = start_address - fa_app_installer.fa_off; + + rc = boot_image_load_header(&fa_softdevice, &hdr_softdevice); + + if (!rc) { + softdevice_area_valid = true; + } + } + +invalid_softdevice: +#ifdef CONFIG_BOOT_FIRMWARE_LOADER + start_address = 0; + image_size = 0; + rc = bm_installs_get_image_data(BM_INSTALLS_IMAGE_INDEX_FIRMWARE_LOADER, &start_address, + &image_size); + + if (!rc) { + fa_firmware_loader.fa_off = start_address; + fa_firmware_loader.fa_size = image_size; + + if (start_address < fa_app_installer.fa_off) { + /* Invalid start address for firmware loader */ + goto invalid_firmware_loader; + } + + fa_app_installer.fa_size = start_address - fa_app_installer.fa_off; + + rc = boot_image_load_header(&fa_firmware_loader, &hdr_softdevice); + + if (!rc) { + firmware_loader_area_valid = true; + } + } +#endif + } + +invalid_firmware_loader: + rc = boot_image_load_header(&fa_app_installer, &hdr_app_installer); + + if (rc) { + BOOT_LOG_ERR("Failed loading application/installer image header: %d", rc); + } else { + FIH_CALL(validate_image, fih_rc, &fa_app_installer, &hdr_app_installer); + + if (FIH_EQ(fih_rc, FIH_SUCCESS)) { + struct image_tlv_iter it; + uint32_t off2; + uint16_t len2; + + app_installer_image_valid = true; + + if (hdr_app_installer.ih_protect_tlv_size > 0) { + rc = bootutil_tlv_iter_begin(&it, &hdr_app_installer, &fa_app_installer, + IMAGE_TLV_INSTALLER_IMAGE, true); + + if (rc == 0) { + rc = bootutil_tlv_iter_next(&it, &off2, &len2, NULL); + + if (rc == 0 && len2 == sizeof(app_installer_is_installer_image)) { + rc = LOAD_IMAGE_DATA(&hdr_app_installer, &fa_app_installer, off2, + &app_installer_is_installer_image, len2); + + if (rc != 0) { + app_installer_is_installer_image = false; + } + } + } + } + } + } + + if (softdevice_area_valid) { + fih_rc = FIH_FAILURE; + rc = boot_image_load_header(&fa_softdevice, &hdr_softdevice); + + if (rc) { + BOOT_LOG_ERR("Failed loading SoftDevice image header: %d", rc); + } else { + FIH_CALL(validate_image, fih_rc, &fa_softdevice, &hdr_softdevice); + + if (FIH_EQ(fih_rc, FIH_SUCCESS)) { + softdevice_image_valid = true; + } + } + } + +#ifdef CONFIG_BOOT_FIRMWARE_LOADER + if (firmware_loader_area_valid) { + fih_rc = FIH_FAILURE; + rc = boot_image_load_header(&fa_firmware_loader, &hdr_firmware_loader); + + if (rc) { + BOOT_LOG_ERR("Failed loading firmware loader image header: %d", rc); + } else { + FIH_CALL(validate_image, fih_rc, &fa_firmware_loader, &hdr_firmware_loader); + + if (FIH_EQ(fih_rc, FIH_SUCCESS)) { + firmware_loader_image_valid = true; + } + } + } +#endif + + BOOT_LOG_DBG("Application/installer partition offset: 0x%lx, size: 0x%x, type: %d", + fa_app_installer.fa_off, fa_app_installer.fa_size, + app_installer_is_installer_image); + BOOT_LOG_DBG("SoftDevice partition offset: 0x%lx, size: 0x%x", fa_softdevice.fa_off, + fa_softdevice.fa_size); +#ifdef CONFIG_BOOT_FIRMWARE_LOADER + BOOT_LOG_DBG("Firmware loader off: 0x%lx, size: 0x%x", fa_firmware_loader.fa_off, + fa_firmware_loader.fa_size); + BOOT_LOG_DBG("SoftDevice area valid: %d, Firmware loader area valid: %d, " + "Application/installer image valid: %d, SoftDevice image valid: %d, " + "Firmware loader image valid: %d", softdevice_area_valid, + firmware_loader_area_valid, app_installer_image_valid, softdevice_image_valid, + firmware_loader_image_valid); +#else + BOOT_LOG_DBG("SoftDevice area valid: %d, Application/installer image valid: %d, " + "SoftDevice image valid: %d", softdevice_area_valid, app_installer_image_valid, + softdevice_image_valid); +#endif + +#ifdef CONFIG_BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO + if (io_detect_pin() && !io_boot_skip_serial_recovery()) { + BOOT_LOG_DBG("GPIO detected for firmware loader mode"); + boot_firmware_loader = true; + } +#endif + +#ifdef CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET + if (io_detect_pin_reset()) { + BOOT_LOG_DBG("Pin reset detected for firmware loader mode"); + boot_firmware_loader = true; + } +#endif + +#ifdef CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE + if (io_detect_boot_mode()) { + BOOT_LOG_DBG("Boot mode detected for firmware loader mode"); + boot_firmware_loader = true; + } +#endif + + if (app_installer_image_valid == true && app_installer_is_installer_image == true) { + /* Installer image is present, this gets priority */ + BOOT_LOG_DBG("Booting installer"); + rsp->br_image_off = flash_area_get_off(&fa_app_installer); + rsp->br_hdr = &hdr_app_installer; + } else if (boot_firmware_loader == true && softdevice_image_valid == true && + firmware_loader_image_valid == true) { + /* Boot firmware loader */ + BOOT_LOG_INF("Booting firmware loader"); + rsp->br_image_off = flash_area_get_off(&fa_firmware_loader); + rsp->br_hdr = &hdr_firmware_loader; + } else if (app_installer_image_valid == true && softdevice_image_valid == true) { + /* Boot main application */ + BOOT_LOG_INF("Booting main application"); + rsp->br_image_off = flash_area_get_off(&fa_app_installer); + rsp->br_hdr = &hdr_app_installer; + } else if (app_installer_image_valid == false && softdevice_image_valid == true && + firmware_loader_image_valid == true) { + /* Boot firmware loader due to missing main image */ + BOOT_LOG_INF("Booting firmware loader due to missing application image"); + rsp->br_image_off = flash_area_get_off(&fa_firmware_loader); + rsp->br_hdr = &hdr_firmware_loader; + } else { + /* Cannot boot in this configuration */ + BOOT_LOG_ERR("Error: no bootable configuration found"); + return -1; + } + + rsp->br_flash_dev_id = flash_area_get_device_id(&fa_app_installer); + + return 0; +} diff --git a/boot/zephyr/include/compression/decompression.h b/boot/zephyr/include/compression/decompression.h new file mode 100644 index 000000000..2104c4eb6 --- /dev/null +++ b/boot/zephyr/include/compression/decompression.h @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef H_DECOMPRESSION_ +#define H_DECOMPRESSION_ + +#include +#include +#include +#include "bootutil/bootutil.h" +#include "bootutil/bootutil_public.h" +#include "bootutil/image.h" +#include "../src/bootutil_priv.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Checks if a compressed image header is valid. + * + * @param hdr Image header. + * @param fap Flash area of the slot. + * @param state Bootloader state object. + * + * @return true if valid; false if invalid. + */ +bool boot_is_compressed_header_valid(const struct image_header *hdr, const struct flash_area *fap, + struct boot_loader_state *state); + +/** + * Reads in compressed image data from a slot, decompresses it and writes it out to a destination + * slot, including corresponding image headers and TLVs. + * + * @param state Bootloader state object. + * @param fap_src Flash area of the source slot. + * @param fap_dst Flash area of the destination slot. + * @param off_src Offset of the source slot to read from (should be 0). + * @param off_dst Offset of the destination slot to write to (should be 0). + * @param sz Size of the source slot data. + * @param buf Temporary buffer for reading data from. + * @param buf_size Size of temporary buffer. + * + * @return 0 on success; nonzero on failure. + */ +int boot_copy_region_decompress(struct boot_loader_state *state, const struct flash_area *fap_src, + const struct flash_area *fap_dst, uint32_t off_src, + uint32_t off_dst, uint32_t sz, uint8_t *buf, size_t buf_size); + +/** + * Gets the total data size (excluding headers and TLVs) of a compressed image when it is + * decompressed. + * + * @param hdr Image header. + * @param fap Flash area of the slot. + * @param img_decomp_size Pointer to variable that will be updated with the decompressed image + * size. + * + * @return 0 on success; nonzero on failure. + */ +int bootutil_get_img_decomp_size(const struct image_header *hdr, const struct flash_area *fap, + uint32_t *img_decomp_size); + +/** + * Calculate MCUboot-compatible image hash of compressed image slot. + * + * @param state MCUboot state. + * @param hdr Image header. + * @param fap Flash area of the slot. + * @param tmp_buf Temporary buffer for reading data from. + * @param tmp_buf_sz Size of temporary buffer. + * @param hash_result Pointer to a variable that will be updated with the image hash. + * @param seed Not currently used, set to NULL. + * @param seed_len Not currently used, set to 0. + * + * @return 0 on success; nonzero on failure. + */ +int bootutil_img_hash_decompress(struct boot_loader_state *state, struct image_header *hdr, + const struct flash_area *fap, uint8_t *tmp_buf, + uint32_t tmp_buf_sz, uint8_t *hash_result, + uint8_t *seed, int seed_len); + +/** + * Calculates the size that the compressed image protected TLV section will occupy once the image + * has been decompressed. + * + * @param hdr Image header. + * @param fap Flash area of the slot. + * @param sz Pointer to variable that will be updated with the protected TLV size. + * + * @return 0 on success; nonzero on failure. + */ +int boot_size_protected_tlvs(const struct image_header *hdr, const struct flash_area *fap_src, + uint32_t *sz); + +#ifdef __cplusplus +} +#endif + +#endif /* H_DECOMPRESSION_ */ diff --git a/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/boot/zephyr/include/mcuboot_config/mcuboot_config.h index fd003565a..616beea91 100644 --- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h +++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h @@ -49,6 +49,8 @@ #endif #elif defined(CONFIG_BOOT_USE_PSA_CRYPTO) #define MCUBOOT_USE_PSA_CRYPTO +#elif defined(CONFIG_BOOT_USE_NRF_EXTERNAL_CRYPTO) +#define MCUBOOT_USE_NRF_EXTERNAL_CRYPTO #endif #ifdef CONFIG_BOOT_IMG_HASH_ALG_SHA512 @@ -153,6 +155,13 @@ #define MCUBOOT_ENCRYPT_X25519 #endif +/* Support for HMAC/HKDF using SHA512; this is used in key exchange where + * HKDF is used for key expansion and HMAC is used for key verification. + */ +#ifdef CONFIG_BOOT_HMAC_SHA512 +#define MCUBOOT_HMAC_SHA512 +#endif + #ifdef CONFIG_BOOT_DECOMPRESSION #define MCUBOOT_DECOMPRESS_IMAGES #endif diff --git a/boot/zephyr/include/nrf_cleanup.h b/boot/zephyr/include/nrf_cleanup.h new file mode 100644 index 000000000..9e87e13f5 --- /dev/null +++ b/boot/zephyr/include/nrf_cleanup.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef H_NRF_CLEANUP_ +#define H_NRF_CLEANUP_ + +/** + * Perform cleanup on some peripheral resources used by MCUBoot prior chainload + * the application. + * + * This function disables all RTC instances and UARTE instances. + * It Disables their interrupts signals as well. + */ +void nrf_cleanup_peripheral(void); + +/** + * Perform cleanup of non-secure RAM that may have been used by MCUBoot. + */ +void nrf_cleanup_ns_ram(void); + +#endif diff --git a/boot/zephyr/include/sysflash/pm_sysflash.h b/boot/zephyr/include/sysflash/pm_sysflash.h new file mode 100644 index 000000000..0cb16292f --- /dev/null +++ b/boot/zephyr/include/sysflash/pm_sysflash.h @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef __PM_SYSFLASH_H__ +#define __PM_SYSFLASH_H__ +/* Blocking the __SYSFLASH_H__ */ +#define __SYSFLASH_H__ + +#include +#include +#include + +#ifndef CONFIG_SINGLE_APPLICATION_SLOT + +/* Each pair of slots is separated by , and there is no terminating character */ +#define FLASH_AREA_IMAGE_0_SLOTS PM_MCUBOOT_PRIMARY_ID, PM_MCUBOOT_SECONDARY_ID, +#define FLASH_AREA_IMAGE_1_SLOTS PM_MCUBOOT_PRIMARY_1_ID, PM_MCUBOOT_SECONDARY_1_ID, +#define FLASH_AREA_IMAGE_2_SLOTS PM_MCUBOOT_PRIMARY_2_ID, PM_MCUBOOT_SECONDARY_2_ID, +#define FLASH_AREA_IMAGE_3_SLOTS PM_MCUBOOT_PRIMARY_3_ID, PM_MCUBOOT_SECONDARY_3_ID, + +#if CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1 +#ifdef CONFIG_NCS_IS_VARIANT_IMAGE +#define MCUBOOT_S0_S1_SLOTS PM_S0_ID, PM_MCUBOOT_SECONDARY_ID, +#else +#define MCUBOOT_S0_S1_SLOTS PM_S1_ID, PM_MCUBOOT_SECONDARY_ID, +#endif +#else +#define MCUBOOT_S0_S1_SLOTS +#endif + +#if (MCUBOOT_IMAGE_NUMBER == 1) || (MCUBOOT_IMAGE_NUMBER == 2 && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS +#elif (MCUBOOT_IMAGE_NUMBER == 2) || (MCUBOOT_IMAGE_NUMBER == 3 && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS \ + FLASH_AREA_IMAGE_1_SLOTS +#elif (MCUBOOT_IMAGE_NUMBER == 3) || (MCUBOOT_IMAGE_NUMBER == 4 && CONFIG_MCUBOOT_MCUBOOT_IMAGE_NUMBER != -1) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS \ + FLASH_AREA_IMAGE_1_SLOTS \ + FLASH_AREA_IMAGE_2_SLOTS +#elif (MCUBOOT_IMAGE_NUMBER == 4) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS \ + FLASH_AREA_IMAGE_1_SLOTS \ + FLASH_AREA_IMAGE_2_SLOTS \ + FLASH_AREA_IMAGE_3_SLOTS +#else +#error Unsupported number of images +#endif + +static inline uint32_t __flash_area_ids_for_slot(int img, int slot) +{ + static const int all_slots[] = { + ALL_AVAILABLE_SLOTS + MCUBOOT_S0_S1_SLOTS + }; + return all_slots[img * 2 + slot]; +}; + +#undef FLASH_AREA_IMAGE_0_SLOTS +#undef FLASH_AREA_IMAGE_1_SLOTS +#undef FLASH_AREA_IMAGE_2_SLOTS +#undef FLASH_AREA_IMAGE_3_SLOTS +#undef MCUBOOT_S0_S1_SLOTS +#undef ALL_AVAILABLE_SLOTS + +#define FLASH_AREA_IMAGE_PRIMARY(x) __flash_area_ids_for_slot(x, 0) +#define FLASH_AREA_IMAGE_SECONDARY(x) __flash_area_ids_for_slot(x, 1) + +#if !defined(CONFIG_BOOT_SWAP_USING_MOVE) +#define FLASH_AREA_IMAGE_SCRATCH PM_MCUBOOT_SCRATCH_ID +#endif + +#else /* CONFIG_SINGLE_APPLICATION_SLOT */ + +#define FLASH_AREA_IMAGE_PRIMARY(x) PM_MCUBOOT_PRIMARY_ID +#define FLASH_AREA_IMAGE_SECONDARY(x) PM_MCUBOOT_PRIMARY_ID +/* NOTE: Scratch parition is not used by single image DFU but some of + * functions in common files reference it, so the definitions has been + * provided to allow compilation of common units. + */ +#define FLASH_AREA_IMAGE_SCRATCH 0 + +#endif /* CONFIG_SINGLE_APPLICATION_SLOT */ + +#ifndef SOC_FLASH_0_ID +#define SOC_FLASH_0_ID 0 +#endif + +#ifndef SPI_FLASH_0_ID +#define SPI_FLASH_0_ID 1 +#endif + +#endif /* __PM_SYSFLASH_H__ */ diff --git a/boot/zephyr/include/sysflash/sysflash.h b/boot/zephyr/include/sysflash/sysflash.h index 16d222280..3c3638d7f 100644 --- a/boot/zephyr/include/sysflash/sysflash.h +++ b/boot/zephyr/include/sysflash/sysflash.h @@ -4,6 +4,12 @@ * SPDX-License-Identifier: Apache-2.0 */ +#if USE_PARTITION_MANAGER +/* Blocking the rest of the file */ +#define __SYSFLASH_H__ +#include +#endif + #ifndef __SYSFLASH_H__ #define __SYSFLASH_H__ diff --git a/boot/zephyr/include/target.h b/boot/zephyr/include/target.h index ea160752e..856686785 100644 --- a/boot/zephyr/include/target.h +++ b/boot/zephyr/include/target.h @@ -8,6 +8,8 @@ #ifndef H_TARGETS_TARGET_ #define H_TARGETS_TARGET_ +#ifndef USE_PARTITION_MANAGER + #if defined(MCUBOOT_TARGET_CONFIG) /* * Target-specific definitions are permitted in legacy cases that @@ -47,4 +49,6 @@ #error "Target support is incomplete; cannot build mcuboot." #endif +#endif /* ifndef USE_PARTITION_MANAGER */ + #endif /* H_TARGETS_TARGET_ */ diff --git a/boot/zephyr/io.c b/boot/zephyr/io.c index 309f1ab94..f2342c1ad 100644 --- a/boot/zephyr/io.c +++ b/boot/zephyr/io.c @@ -181,7 +181,7 @@ bool io_detect_pin_reset(void) rc = hwinfo_get_reset_cause(&reset_cause); - if (rc == 0 && reset_cause == RESET_PIN) { + if (rc == 0 && (reset_cause & RESET_PIN)) { (void)hwinfo_clear_reset_cause(); return true; } diff --git a/boot/zephyr/io_bm.c b/boot/zephyr/io_bm.c new file mode 100644 index 000000000..3f65a5d0e --- /dev/null +++ b/boot/zephyr/io_bm.c @@ -0,0 +1,198 @@ +/* + * Copyright (c) 2012-2014 Wind River Systems, Inc. + * Copyright (c) 2020 Arm Limited + * Copyright (c) 2021-2025 Nordic Semiconductor ASA + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "target.h" +#include "bootutil/bootutil_log.h" + +#include +#include + +#if defined(CONFIG_BOOT_SERIAL_PIN_RESET) || defined(CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET) +#include +#endif + +#if defined(CONFIG_BOOT_SERIAL_BOOT_MODE) || defined(CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE) +#include +#endif + +/* Validate serial recovery configuration */ +#ifdef CONFIG_MCUBOOT_SERIAL +#if !defined(CONFIG_BOOT_SERIAL_ENTRANCE_GPIO) && \ + !defined(CONFIG_BOOT_SERIAL_WAIT_FOR_DFU) && \ + !defined(CONFIG_BOOT_SERIAL_BOOT_MODE) && \ + !defined(CONFIG_BOOT_SERIAL_NO_APPLICATION) && \ + !defined(CONFIG_BOOT_SERIAL_PIN_RESET) +#error "Serial recovery selected without an entrance mode set" +#endif +#endif + +/* Validate firmware loader configuration */ +#ifdef CONFIG_BOOT_FIRMWARE_LOADER +#if !defined(CONFIG_BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO) && \ + !defined(CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE) && \ + !defined(CONFIG_BOOT_FIRMWARE_LOADER_NO_APPLICATION) && \ + !defined(CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET) +#error "Firmware loader selected without an entrance mode set" +#endif +#endif + +BOOT_LOG_MODULE_DECLARE(mcuboot); + +#ifdef CONFIG_MCUBOOT_INDICATION_LED + +void io_led_init(void) +{ + nrf_gpio_cfg_output(BOARD_PIN_LED_0); + nrf_gpio_pin_write(BOARD_PIN_LED_0, BOARD_LED_ACTIVE_STATE); +} + +void io_led_set(int value) +{ + nrf_gpio_pin_write(BOARD_PIN_LED_0, (value == 0 ? !BOARD_LED_ACTIVE_STATE : BOARD_LED_ACTIVE_STATE)); +} +#endif /* CONFIG_MCUBOOT_INDICATION_LED */ + +#if defined(CONFIG_BOOT_SERIAL_ENTRANCE_GPIO) || defined(CONFIG_BOOT_USB_DFU_GPIO) || \ + defined(CONFIG_BOOT_FIRMWARE_LOADER_ENTRANCE_GPIO) + +#if defined(CONFIG_MCUBOOT_SERIAL) +#define BUTTON_0_DETECT_DELAY CONFIG_BOOT_SERIAL_DETECT_DELAY +#elif defined(CONFIG_BOOT_FIRMWARE_LOADER) +#define BUTTON_0_DETECT_DELAY CONFIG_BOOT_FIRMWARE_LOADER_DETECT_DELAY +#else +#define BUTTON_0_DETECT_DELAY CONFIG_BOOT_USB_DFU_DETECT_DELAY +#endif + +bool io_detect_pin(void) +{ + int rc; + bool pin_active; + + rc = bm_buttons_init( + &(struct bm_buttons_config){ + .pin_number = BOARD_PIN_BTN_0, + .active_state = BM_BUTTONS_ACTIVE_LOW, + .pull_config = BM_BUTTONS_PIN_PULLUP, + }, + 1, + BM_BUTTONS_DETECTION_DELAY_MIN_US); + if (rc) { + BOOT_LOG_ERR("Failed to initialize buttons: %d", rc); + return false; + } + + rc = bm_buttons_enable(); + if (rc) { + BOOT_LOG_ERR("Failed to enable button detection: %d", rc); + return false; + } + + pin_active = bm_buttons_is_pressed(BOARD_PIN_BTN_0); + + if (pin_active) { + if (BUTTON_0_DETECT_DELAY > 0) { +#ifdef CONFIG_MULTITHREADING + k_sleep(K_MSEC(50)); +#else + k_busy_wait(50000); +#endif + + /* Get the uptime for debounce purposes. */ + int64_t timestamp = k_uptime_get(); + + for(;;) { + pin_active = bm_buttons_is_pressed(BOARD_PIN_BTN_0); + + /* Get delta from when this started */ + uint32_t delta = k_uptime_get() - timestamp; + + /* If not pressed OR if pressed > debounce period, stop. */ + if (delta >= BUTTON_0_DETECT_DELAY || !pin_active) { + break; + } + + /* Delay 1 ms */ +#ifdef CONFIG_MULTITHREADING + k_sleep(K_MSEC(1)); +#else + k_busy_wait(1000); +#endif + } + } + } + + rc = bm_buttons_disable(); + + if (rc) { + BOOT_LOG_ERR("Failed to disable buttons: %d", rc); + } + + rc = bm_buttons_deinit(); + if (rc) { + BOOT_LOG_ERR("Failed to de-initialize buttons: %d", rc); + } + + return (bool)pin_active; +} +#endif + +#if defined(CONFIG_BOOT_SERIAL_PIN_RESET) || defined(CONFIG_BOOT_FIRMWARE_LOADER_PIN_RESET) +bool io_detect_pin_reset(void) +{ + uint32_t reset_cause; + int rc; + + rc = hwinfo_get_reset_cause(&reset_cause); + + if (rc == 0 && (reset_cause & RESET_PIN)) { + (void)hwinfo_clear_reset_cause(); + return true; + } + + return false; +} +#endif + +#if defined(CONFIG_BOOT_SERIAL_BOOT_MODE) || defined(CONFIG_BOOT_FIRMWARE_LOADER_BOOT_MODE) +bool io_detect_boot_mode(void) +{ + int32_t boot_mode; + + boot_mode = bootmode_check(BOOT_MODE_TYPE_BOOTLOADER); + + if (boot_mode == 1) { + /* Boot mode to stay in bootloader, clear status and enter serial + * recovery mode + */ + bootmode_clear(); + + return true; + } + + return false; +} +#endif diff --git a/boot/zephyr/main.c b/boot/zephyr/main.c index 4ecf191e7..7a52aa209 100644 --- a/boot/zephyr/main.c +++ b/boot/zephyr/main.c @@ -2,6 +2,7 @@ * Copyright (c) 2012-2014 Wind River Systems, Inc. * Copyright (c) 2020 Arm Limited * Copyright (c) 2021-2023 Nordic Semiconductor ASA + * Copyright (c) 2025 Aerlync Labs Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -70,6 +71,10 @@ #endif /* CONFIG_SOC_FAMILY_ESPRESSIF_ESP32 */ +#ifdef CONFIG_FW_INFO +#include +#endif + #ifdef CONFIG_MCUBOOT_SERIAL #include "boot_serial/boot_serial.h" #include "serial_adapter/serial_adapter.h" @@ -88,6 +93,10 @@ const struct boot_uart_funcs boot_funcs = { #include #endif +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(PM_CPUNET_B0N_ADDRESS) +#include +#endif + /* CONFIG_LOG_MINIMAL is the legacy Kconfig property, * replaced by CONFIG_LOG_MODE_MINIMAL. */ @@ -130,6 +139,15 @@ K_SEM_DEFINE(boot_log_sem, 1, 1); * !defined(ZEPHYR_LOG_MODE_MINIMAL) */ +#if USE_PARTITION_MANAGER && CONFIG_FPROTECT +#include +#include +#endif + +#if CONFIG_MCUBOOT_NRF_CLEANUP_PERIPHERAL || CONFIG_MCUBOOT_NRF_CLEANUP_NONSECURE_RAM +#include +#endif + BOOT_LOG_MODULE_REGISTER(mcuboot); void os_heap_init(void); @@ -181,6 +199,34 @@ static void do_boot(struct boot_rsp *rsp) /* Disable the USB to prevent it from firing interrupts */ usb_disable(); #endif + +#if defined(CONFIG_FW_INFO) && !defined(CONFIG_EXT_API_PROVIDE_EXT_API_UNUSED) + uintptr_t fw_start_addr; + + rc = flash_device_base(rsp->br_flash_dev_id, &fw_start_addr); + assert(rc == 0); + + fw_start_addr += rsp->br_image_off + rsp->br_hdr->ih_hdr_size; + + const struct fw_info *firmware_info = fw_info_find(fw_start_addr); + bool provided = fw_info_ext_api_provide(firmware_info, true); + +#ifdef PM_S0_ADDRESS + /* Only fail if the immutable bootloader is present. */ + if (!provided) { + if (firmware_info == NULL) { + BOOT_LOG_WRN("Unable to find firmware info structure in %p", vt); + } + BOOT_LOG_ERR("Failed to provide EXT_APIs to %p", vt); + } +#endif +#endif +#if CONFIG_MCUBOOT_NRF_CLEANUP_PERIPHERAL + nrf_cleanup_peripheral(); +#endif +#if CONFIG_MCUBOOT_NRF_CLEANUP_NONSECURE_RAM && defined(PM_SRAM_NONSECURE_NAME) + nrf_cleanup_ns_ram(); +#endif #if CONFIG_MCUBOOT_CLEANUP_ARM_CORE cleanup_arm_nvic(); /* cleanup NVIC registers */ @@ -248,6 +294,9 @@ static void do_boot(struct boot_rsp *rsp) " b clear\n" "out:\n" " dsb\n" +#if CONFIG_MCUBOOT_INFINITE_LOOP_AFTER_RAM_CLEANUP + " b out\n" +#endif /*CONFIG_MCUBOOT_INFINITE_LOOP_AFTER_RAM_CLEANUP */ /* jump to reset vector of an app */ " bx r0\n" : @@ -434,6 +483,9 @@ int main(void) { struct boot_rsp rsp; int rc; +#if defined(CONFIG_BOOT_USB_DFU_GPIO) || defined(CONFIG_BOOT_USB_DFU_WAIT) + bool usb_dfu_requested = false; +#endif FIH_DECLARE(fih_rc, FIH_FAILURE); MCUBOOT_WATCHDOG_SETUP(); @@ -459,6 +511,7 @@ int main(void) mcuboot_status_change(MCUBOOT_STATUS_STARTUP); #ifdef CONFIG_BOOT_SERIAL_ENTRANCE_GPIO + BOOT_LOG_DBG("Checking GPIO for serial recovery"); if (io_detect_pin() && !io_boot_skip_serial_recovery()) { boot_serial_enter(); @@ -466,42 +519,49 @@ int main(void) #endif #ifdef CONFIG_BOOT_SERIAL_PIN_RESET + BOOT_LOG_DBG("Checking RESET pin for serial recovery"); if (io_detect_pin_reset()) { boot_serial_enter(); } #endif #if defined(CONFIG_BOOT_USB_DFU_GPIO) + BOOT_LOG_DBG("Checking GPIO for USB DFU request"); if (io_detect_pin()) { + BOOT_LOG_DBG("Entering USB DFU"); + + usb_dfu_requested = true; + #ifdef CONFIG_MCUBOOT_INDICATION_LED io_led_set(1); #endif mcuboot_status_change(MCUBOOT_STATUS_USB_DFU_ENTERED); + } +#elif defined(CONFIG_BOOT_USB_DFU_WAIT) + usb_dfu_requested = true; +#endif +#if defined(CONFIG_BOOT_USB_DFU_GPIO) || defined(CONFIG_BOOT_USB_DFU_WAIT) + if (usb_dfu_requested) { rc = usb_enable(NULL); if (rc) { - BOOT_LOG_ERR("Cannot enable USB"); + BOOT_LOG_ERR("Cannot enable USB: %d", rc); } else { BOOT_LOG_INF("Waiting for USB DFU"); - wait_for_usb_dfu(K_FOREVER); + +#if defined(CONFIG_BOOT_USB_DFU_WAIT) + BOOT_LOG_DBG("Waiting for USB DFU for %dms", CONFIG_BOOT_USB_DFU_WAIT_DELAY_MS); + mcuboot_status_change(MCUBOOT_STATUS_USB_DFU_WAITING); + wait_for_usb_dfu(K_MSEC(CONFIG_BOOT_USB_DFU_WAIT_DELAY_MS)); BOOT_LOG_INF("USB DFU wait time elapsed"); + mcuboot_status_change(MCUBOOT_STATUS_USB_DFU_TIMED_OUT); +#else + wait_for_usb_dfu(K_FOREVER); + BOOT_LOG_INF("USB DFU wait terminated"); +#endif } } -#elif defined(CONFIG_BOOT_USB_DFU_WAIT) - rc = usb_enable(NULL); - if (rc) { - BOOT_LOG_ERR("Cannot enable USB"); - } else { - BOOT_LOG_INF("Waiting for USB DFU"); - - mcuboot_status_change(MCUBOOT_STATUS_USB_DFU_WAITING); - - wait_for_usb_dfu(K_MSEC(CONFIG_BOOT_USB_DFU_WAIT_DELAY_MS)); - BOOT_LOG_INF("USB DFU wait time elapsed"); - - mcuboot_status_change(MCUBOOT_STATUS_USB_DFU_TIMED_OUT); - } #endif #ifdef CONFIG_BOOT_SERIAL_WAIT_FOR_DFU @@ -523,12 +583,14 @@ int main(void) if (FIH_EQ(fih_rc, FIH_BOOT_HOOK_REGULAR)) { FIH_CALL(boot_go, fih_rc, &rsp); } + BOOT_LOG_DBG("Left boot_go with success == %d", FIH_EQ(fih_rc, FIH_SUCCESS) ? 1 : 0); #ifdef CONFIG_BOOT_SERIAL_BOOT_MODE if (io_detect_boot_mode()) { /* Boot mode to stay in bootloader, clear status and enter serial * recovery mode */ + BOOT_LOG_DBG("Staying in serial recovery"); boot_serial_enter(); } #endif @@ -589,7 +651,37 @@ int main(void) mcuboot_status_change(MCUBOOT_STATUS_BOOTABLE_IMAGE_FOUND); +#if USE_PARTITION_MANAGER && CONFIG_FPROTECT + +#ifdef PM_S1_ADDRESS +/* MCUBoot is stored in either S0 or S1, protect both */ +#define PROTECT_SIZE (PM_MCUBOOT_PRIMARY_ADDRESS - PM_S0_ADDRESS) +#define PROTECT_ADDR PM_S0_ADDRESS +#else +/* There is only one instance of MCUBoot */ +#define PROTECT_SIZE (PM_MCUBOOT_PRIMARY_ADDRESS - PM_MCUBOOT_ADDRESS) +#define PROTECT_ADDR PM_MCUBOOT_ADDRESS +#endif + + rc = fprotect_area(PROTECT_ADDR, PROTECT_SIZE); + + if (rc != 0) { + BOOT_LOG_ERR("Protect mcuboot flash failed, cancel startup."); + while (1) + ; + } + +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(PM_CPUNET_B0N_ADDRESS) && defined(CONFIG_PCD_APP) +#if defined(PM_TFM_SECURE_ADDRESS) + pcd_lock_ram(false); +#else + pcd_lock_ram(true); +#endif +#endif +#endif /* USE_PARTITION_MANAGER && CONFIG_FPROTECT */ + ZEPHYR_BOOT_LOG_STOP(); + do_boot(&rsp); mcuboot_status_change(MCUBOOT_STATUS_BOOT_FAILED); diff --git a/boot/zephyr/nrf_cleanup.c b/boot/zephyr/nrf_cleanup.c new file mode 100644 index 000000000..39dfcbc41 --- /dev/null +++ b/boot/zephyr/nrf_cleanup.c @@ -0,0 +1,169 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#if defined(CONFIG_NRFX_CLOCK) +#include +#endif +#include +#include +#include +#if defined(NRF_RTC0) || defined(NRF_RTC1) || defined(NRF_RTC2) + #include +#endif +#if defined(CONFIG_NRF_GRTC_TIMER) + #include +#endif +#if defined(NRF_PPI) + #include +#endif +#if defined(NRF_DPPIC) + #include +#endif + +#include + +#if USE_PARTITION_MANAGER +#include +#endif + +#if defined(NRF_UARTE0) || defined(NRF_UARTE1) || defined(NRF_UARTE20) || \ + defined(NRF_UARTE30) +#define NRF_UARTE_CLEANUP +#endif + +#define NRF_UARTE_SUBSCRIBE_CONF_OFFS offsetof(NRF_UARTE_Type, SUBSCRIBE_STARTRX) +#define NRF_UARTE_SUBSCRIBE_CONF_SIZE (offsetof(NRF_UARTE_Type, EVENTS_CTS) -\ + NRF_UARTE_SUBSCRIBE_CONF_OFFS) + +#define NRF_UARTE_PUBLISH_CONF_OFFS offsetof(NRF_UARTE_Type, PUBLISH_CTS) +#define NRF_UARTE_PUBLISH_CONF_SIZE (offsetof(NRF_UARTE_Type, SHORTS) -\ + NRF_UARTE_PUBLISH_CONF_OFFS) + +#if defined(NRF_RTC0) || defined(NRF_RTC1) || defined(NRF_RTC2) +static inline void nrf_cleanup_rtc(NRF_RTC_Type * rtc_reg) +{ + nrf_rtc_task_trigger(rtc_reg, NRF_RTC_TASK_STOP); + nrf_rtc_event_disable(rtc_reg, 0xFFFFFFFF); + nrf_rtc_int_disable(rtc_reg, 0xFFFFFFFF); +} +#endif + +#if defined(CONFIG_NRF_GRTC_TIMER) +static inline void nrf_cleanup_grtc(void) +{ + nrfx_grtc_uninit(); +} +#endif + +#if defined(NRF_UARTE_CLEANUP) +static NRF_UARTE_Type *nrf_uarte_to_clean[] = { +#if defined(NRF_UARTE0) + NRF_UARTE0, +#endif +#if defined(NRF_UARTE1) + NRF_UARTE1, +#endif +#if defined(NRF_UARTE20) + NRF_UARTE20, +#endif +#if defined(NRF_UARTE30) + NRF_UARTE30, +#endif +#if defined(NRF_UARTE136) + NRF_UARTE136, +#endif +}; +#endif + +#if defined(CONFIG_NRFX_CLOCK) +static void nrf_cleanup_clock(void) +{ + nrf_clock_int_disable(NRF_CLOCK, 0xFFFFFFFF); +} +#endif + +void nrf_cleanup_peripheral(void) +{ +#if defined(NRF_RTC0) + nrf_cleanup_rtc(NRF_RTC0); +#endif +#if defined(NRF_RTC1) + nrf_cleanup_rtc(NRF_RTC1); +#endif +#if defined(NRF_RTC2) + nrf_cleanup_rtc(NRF_RTC2); +#endif + +#if defined(CONFIG_NRF_GRTC_TIMER) + nrf_cleanup_grtc(); +#endif + +#if defined(NRF_UARTE_CLEANUP) + for (int i = 0; i < sizeof(nrf_uarte_to_clean) / sizeof(nrf_uarte_to_clean[0]); ++i) { + NRF_UARTE_Type *current = nrf_uarte_to_clean[i]; + + nrfy_uarte_int_disable(current, 0xFFFFFFFF); + nrfy_uarte_int_uninit(current); + nrfy_uarte_task_trigger(current, NRF_UARTE_TASK_STOPRX); + + nrfy_uarte_event_clear(current, NRF_UARTE_EVENT_RXSTARTED); + nrfy_uarte_event_clear(current, NRF_UARTE_EVENT_ENDRX); + nrfy_uarte_event_clear(current, NRF_UARTE_EVENT_RXTO); + nrfy_uarte_disable(current); + +#ifndef CONFIG_SOC_SERIES_NRF54LX + /* Disconnect pins UARTE pins + * causes issues on nRF54l SoCs, + * could be enabled once fix to NCSDK-33039 will be implemented. + */ + + uint32_t pin[4]; + + pin[0] = nrfy_uarte_tx_pin_get(current); + pin[1] = nrfy_uarte_rx_pin_get(current); + pin[2] = nrfy_uarte_rts_pin_get(current); + pin[3] = nrfy_uarte_cts_pin_get(current); + + nrfy_uarte_pins_disconnect(current); + + for (int j = 0; j < 4; j++) { + if (pin[j] != NRF_UARTE_PSEL_DISCONNECTED) { + nrfy_gpio_cfg_default(pin[i]); + } + } +#endif + +#if defined(NRF_DPPIC) + /* Clear all SUBSCRIBE configurations. */ + memset((uint8_t *)current + NRF_UARTE_SUBSCRIBE_CONF_OFFS, 0, + NRF_UARTE_SUBSCRIBE_CONF_SIZE); + /* Clear all PUBLISH configurations. */ + memset((uint8_t *)current + NRF_UARTE_PUBLISH_CONF_OFFS, 0, + NRF_UARTE_PUBLISH_CONF_SIZE); +#endif + } +#endif + +#if defined(NRF_PPI) + nrf_ppi_channels_disable_all(NRF_PPI); +#endif +#if defined(NRF_DPPIC) + nrf_dppi_channels_disable_all(NRF_DPPIC); +#endif + +#if defined(CONFIG_NRFX_CLOCK) + nrf_cleanup_clock(); +#endif +} + +#if USE_PARTITION_MANAGER \ + && defined(CONFIG_ARM_TRUSTZONE_M) \ + && defined(PM_SRAM_NONSECURE_NAME) +void nrf_cleanup_ns_ram(void) +{ + memset((void *) PM_SRAM_NONSECURE_ADDRESS, 0, PM_SRAM_NONSECURE_SIZE); +} +#endif diff --git a/boot/zephyr/pm.yml b/boot/zephyr/pm.yml new file mode 100644 index 000000000..eec62473c --- /dev/null +++ b/boot/zephyr/pm.yml @@ -0,0 +1,94 @@ +#include + +mcuboot: + size: CONFIG_PM_PARTITION_SIZE_MCUBOOT + placement: + before: [mcuboot_primary] + align: {end: 0x1000} + +mcuboot_primary_app: + # All images to be placed in MCUboot's slot 0 should be placed in this + # partition + span: [app] + +mcuboot_primary: + span: [mcuboot_pad, mcuboot_primary_app] + +# Partition for secondary slot is not created if building in single application +# slot configuration. +#if !defined(CONFIG_SINGLE_APPLICATION_SLOT) && !defined(CONFIG_BOOT_DIRECT_XIP) +mcuboot_secondary: + share_size: [mcuboot_primary] +#if defined(CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY) + region: external_flash + placement: + align: {start: 4} +#else + placement: + align: {start: CONFIG_FPROTECT_BLOCK_SIZE} + align_next: CONFIG_FPROTECT_BLOCK_SIZE # Ensure that the next partition does not interfere with this image + after: mcuboot_primary +#endif /* CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY */ + +#endif /* !defined(CONFIG_SINGLE_APPLICATION_SLOT) && !defined(CONFIG_BOOT_DIRECT_XIP) */ + +#if CONFIG_BOOT_DIRECT_XIP + +# Direct XIP is enabled, reserve area for metadata (padding) and name the +# partition so that its clear that it is not the secondary slot, but the direct +# XIP alternative. + +mcuboot_secondary_pad: + share_size: mcuboot_pad + placement: + after: mcuboot_primary + align: {start: CONFIG_FPROTECT_BLOCK_SIZE} + +mcuboot_secondary_app: + share_size: mcuboot_primary_app + placement: + after: mcuboot_secondary_pad + +mcuboot_secondary: + span: [mcuboot_secondary_pad, mcuboot_secondary_app] + +#endif /* CONFIG_BOOT_DIRECT_XIP */ + +#if CONFIG_BOOT_SWAP_USING_SCRATCH +mcuboot_scratch: + size: CONFIG_PM_PARTITION_SIZE_MCUBOOT_SCRATCH + placement: + after: app + align: {start: CONFIG_FPROTECT_BLOCK_SIZE} +#endif /* CONFIG_BOOT_SWAP_USING_SCRATCH */ + +# Padding placed before image to boot. This reserves space for the MCUboot image header +# and it ensures that the boot image gets linked with the correct address offset in flash. +mcuboot_pad: + # MCUboot pad must be placed before the primary application partition. + # The primary application partition includes the secure firmware if present. + size: CONFIG_PM_PARTITION_SIZE_MCUBOOT_PAD + placement: + before: [mcuboot_primary_app] +#ifdef CONFIG_FPROTECT + align: {start: CONFIG_FPROTECT_BLOCK_SIZE} +#endif + +#if (CONFIG_NRF53_MCUBOOT_PRIMARY_1_RAM_FLASH) +mcuboot_primary_1: + region: ram_flash + size: CONFIG_NRF53_RAM_FLASH_SIZE +#endif /* CONFIG_NRF53_MCUBOOT_PRIMARY_1_RAM_FLASH */ + +#if (CONFIG_NRF53_MULTI_IMAGE_UPDATE) +mcuboot_secondary_1: +#if defined(CONFIG_PM_EXTERNAL_FLASH_MCUBOOT_SECONDARY) + region: external_flash +#else + placement: + align: {start: CONFIG_FPROTECT_BLOCK_SIZE} + after: mcuboot_secondary +#endif + size: CONFIG_NRF53_RAM_FLASH_SIZE + +#endif /* CONFIG_NRF53_MULTI_IMAGE_UPDATE */ diff --git a/boot/zephyr/prj.conf b/boot/zephyr/prj.conf index 119e07579..eecc1cbca 100644 --- a/boot/zephyr/prj.conf +++ b/boot/zephyr/prj.conf @@ -34,3 +34,7 @@ CONFIG_MCUBOOT_LOG_LEVEL_INF=y CONFIG_CBPRINTF_NANO=y ### Use the minimal C library to reduce flash usage CONFIG_MINIMAL_LIBC=y +CONFIG_NRF_RTC_TIMER_USER_CHAN_COUNT=0 + +# NCS boot banner +CONFIG_NCS_APPLICATION_BOOT_BANNER_STRING="MCUboot" diff --git a/boot/zephyr/prj_minimal.conf b/boot/zephyr/prj_minimal.conf new file mode 100644 index 000000000..91cf1bc96 --- /dev/null +++ b/boot/zephyr/prj_minimal.conf @@ -0,0 +1,41 @@ +# +# Copyright (c) 2021 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +CONFIG_MAIN_STACK_SIZE=10240 +CONFIG_MBEDTLS_CFG_FILE="mcuboot-mbedtls-cfg.h" + +CONFIG_FLASH=y +CONFIG_FPROTECT=y +CONFIG_PM=n + +CONFIG_BOOT_SWAP_SAVE_ENCTLV=n +CONFIG_BOOT_ENCRYPT_IMAGE=n + +CONFIG_BOOT_BOOTSTRAP=n +CONFIG_BOOT_UPGRADE_ONLY=n + +### Minimal Configurations ### +CONFIG_BOOT_USE_MIN_PARTITION_SIZE=y +CONFIG_ASSERT=n +CONFIG_BOOT_BANNER=n +CONFIG_NCS_BOOT_BANNER=n +CONFIG_CLOCK_CONTROL=n +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_GPIO=n +CONFIG_KERNEL_MEM_POOL=n +CONFIG_LOG=n +CONFIG_COMMON_LIBC_CALLOC=n +CONFIG_COMMON_LIBC_MALLOC=n +CONFIG_COMMON_LIBC_REALLOCARRAY=n +CONFIG_NCS_SAMPLES_DEFAULTS=n +CONFIG_NO_RUNTIME_CHECKS=y +CONFIG_NRF_RTC_TIMER=n +CONFIG_PRINTK=n +CONFIG_SERIAL=n +CONFIG_SIZE_OPTIMIZATIONS=y +CONFIG_SYS_CLOCK_EXISTS=n +CONFIG_UART_CONSOLE=n diff --git a/boot/zephyr/single_loader.c b/boot/zephyr/single_loader.c index 28e9cdf15..31c4a8bf7 100644 --- a/boot/zephyr/single_loader.c +++ b/boot/zephyr/single_loader.c @@ -44,6 +44,8 @@ boot_image_validate(const struct flash_area *fa_p, static uint8_t tmpbuf[BOOT_TMPBUF_SZ]; FIH_DECLARE(fih_rc, FIH_FAILURE); + BOOT_LOG_DBG("boot_image_validate: encrypted == %d", (int)IS_ENCRYPTED(hdr)); + /* NOTE: The first argument to boot_image_validate, for enc_state pointer, * is allowed to be NULL only because the single image loader compiles * with BOOT_IMAGE_NUMBER == 1, which excludes the code that uses @@ -75,6 +77,8 @@ boot_image_validate_once(const struct flash_area *fa_p, int rc; FIH_DECLARE(fih_rc, FIH_FAILURE); + BOOT_LOG_DBG("boot_image_validate_once: flash area %p", fap_p); + memset(&state, 0, sizeof(struct boot_swap_state)); rc = boot_read_swap_state(fa_p, &state); if (rc != 0) @@ -112,6 +116,8 @@ boot_go(struct boot_rsp *rsp) int rc = -1; FIH_DECLARE(fih_rc, FIH_FAILURE); + BOOT_LOG_DBG("boot_go: Single loader"); + rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(0), &_fa_p); assert(rc == 0); diff --git a/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp.conf b/boot/zephyr/socs/nrf54l05_cpuapp.conf similarity index 100% rename from boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp.conf rename to boot/zephyr/socs/nrf54l05_cpuapp.conf diff --git a/boot/zephyr/boards/nrf54l15dk_nrf54l05_cpuapp.conf b/boot/zephyr/socs/nrf54l10_cpuapp.conf similarity index 94% rename from boot/zephyr/boards/nrf54l15dk_nrf54l05_cpuapp.conf rename to boot/zephyr/socs/nrf54l10_cpuapp.conf index f911aa248..c8fcd32c3 100644 --- a/boot/zephyr/boards/nrf54l15dk_nrf54l05_cpuapp.conf +++ b/boot/zephyr/socs/nrf54l10_cpuapp.conf @@ -7,8 +7,6 @@ CONFIG_BOOT_MAX_IMG_SECTORS=256 # Ensure that the SPI NOR driver is disabled by default CONFIG_SPI_NOR=n -CONFIG_FPROTECT=y - CONFIG_BOOT_WATCHDOG_FEED=n # Ensure the fastest RRAM write operations diff --git a/boot/zephyr/socs/nrf54l15_cpuapp.conf b/boot/zephyr/socs/nrf54l15_cpuapp.conf index 8db9d2d23..645325513 100644 --- a/boot/zephyr/socs/nrf54l15_cpuapp.conf +++ b/boot/zephyr/socs/nrf54l15_cpuapp.conf @@ -1,3 +1,17 @@ +# Copyright (c) 2024 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: Apache-2.0 +# +CONFIG_BOOT_MAX_IMG_SECTORS=256 + +# Ensure that the SPI NOR driver is disabled by default +CONFIG_SPI_NOR=n + +CONFIG_BOOT_WATCHDOG_FEED=n + +# Ensure the fastest RRAM write operations +CONFIG_NRF_RRAM_WRITE_BUFFER_SIZE=32 + # Link Time Optimizations CONFIG_ISR_TABLES_LOCAL_DECLARATION=y CONFIG_LTO=y diff --git a/docs/encrypted_images.md b/docs/encrypted_images.md index ebc4f46ed..5f775ef18 100644 --- a/docs/encrypted_images.md +++ b/docs/encrypted_images.md @@ -92,24 +92,33 @@ libraries. The whole key encryption can be summarized as: keypair. Those keys will be our ephemeral keys. * Generate a new secret (DH) using the ephemeral private key and the public key that corresponds to the private key embedded in the HW. -* Derive the new keys from the secret using HKDF (built on HMAC-SHA256). We - are not using a `salt` and using an `info` of `MCUBoot_ECIES_v1`, generating - 48 bytes of key material. +* Derive the new keys from the secret using HKDF. We are not using a `salt` + and using an `info` of `MCUBoot_ECIES_v1`, generating 48 bytes of key material. * A new random encryption key is generated (for AES). This is the AES key used to encrypt the images. * The key is encrypted with AES-128-CTR or AES-256-CTR and a `nonce` of 0 using the first 16 bytes of key material generated previously by the HKDF. -* The encrypted key now goes through a HMAC-SHA256 using the remaining 32 +* The encrypted key now goes through a HMAC using the remaining 32 bytes of key material from the HKDF. +There are different TLVs for ECIES-P256, ECIES-X25519 with SHA256 HKDF/HMAC +and ECIES-X25519 with SHA512 HKDF/HMAC. The final TLV is built from the 65 bytes for ECIES-P256 or 32 bytes for ECIES-X25519, which correspond to the ephemeral public key, followed by the -32 bytes of MAC tag and the 16 or 32 bytes of the encrypted key, resulting in -a TLV of 113 or 129 bytes for ECIES-P256 and 80 or 96 bytes for ECIES-X25519. +MAC tag and the 16 or 32 bytes of the encrypted key, resulting in final TLV +length: + * ECIES-P256 has TLV length 113 to 129 bytes, depending on AES key length. + * ECIES-X25519 on SHA256 TLV length is 80 or 96 bytes, depending on AES key + length. + * ECIES-X25519 on SHA512 TLV length is 112 or 128, depending on AES key + length. The implemenation of ECIES-P256 is named ENC_EC256 in the source code and artifacts while ECIES-X25519 is named ENC_X25519. +Note that MCUboot is built to support only one ECIES and HMAC SHA at once, +and truncated HMAC is not supported at this time + ## [Upgrade process](#upgrade-process) When starting a new upgrade process, `MCUboot` checks that the image in the diff --git a/docs/release-notes.d/fix-direct-hash-base-address.md b/docs/release-notes.d/fix-direct-hash-base-address.md new file mode 100644 index 000000000..041a45134 --- /dev/null +++ b/docs/release-notes.d/fix-direct-hash-base-address.md @@ -0,0 +1,2 @@ + - Fixed issue in image_validate when `MCUBOOT_HASH_STORAGE_DIRECTLY` is enabled + for platforms with NVM memory that does not start at 0x00. diff --git a/ext/nrf/cc310_glue.h b/ext/nrf/cc310_glue.h index ed3ed5c00..22eb94911 100644 --- a/ext/nrf/cc310_glue.h +++ b/ext/nrf/cc310_glue.h @@ -22,7 +22,7 @@ #include #include #include -#include +#include #include /* diff --git a/scripts/imgtool/image.py b/scripts/imgtool/image.py index 566a47e00..03e2cbb62 100644 --- a/scripts/imgtool/image.py +++ b/scripts/imgtool/image.py @@ -88,6 +88,7 @@ 'ENCKW': 0x31, 'ENCEC256': 0x32, 'ENCX25519': 0x33, + 'ENCX25519_SHA512': 0x34, 'DEPENDENCY': 0x40, 'SEC_CNT': 0x50, 'BOOT_RECORD': 0x60, @@ -348,7 +349,6 @@ def load(self, path): self.payload = copy.copy(self.infile_data) except FileNotFoundError: raise click.UsageError("Input file not found") - self.image_size = len(self.payload) # Add the image header if needed. if self.pad_header and self.header_size > 0: @@ -358,6 +358,8 @@ def load(self, path): self.payload = bytes([self.erased_val] * self.header_size) + \ self.payload + self.image_size = len(self.payload) - self.header_size + self.check_header() def load_compressed(self, data, compression_header): @@ -366,14 +368,19 @@ def load_compressed(self, data, compression_header): self.image_size = len(self.payload) # Add the image header if needed. - if self.pad_header and self.header_size > 0: - if self.base_addr: - # Adjust base_addr for new header - self.base_addr -= self.header_size - self.payload = bytes([self.erased_val] * self.header_size) + \ - self.payload - - self.check_header() + if self.header_size > 0: + if self.pad_header: + if self.base_addr: + # Adjust base_addr for new header + self.base_addr -= self.header_size + self.payload = bytes([self.erased_val] * self.header_size) + \ + self.payload + else: + # Fill header padding with zeros to align with what is expected + # for uncompressed images when no pad_header is requested + # (see self.check_header()) + self.payload = bytes([0] * self.header_size) + \ + self.payload def save(self, path, hex_addr=None): """Save an image from a given file""" @@ -429,7 +436,7 @@ def check_trailer(self): len(self.payload), tsize, self.slot_size) raise click.UsageError(msg) - def ecies_hkdf(self, enckey, plainkey): + def ecies_hkdf(self, enckey, plainkey, hmac_sha_alg): if isinstance(enckey, ecdsa.ECDSA256P1Public): newpk = ec.generate_private_key(ec.SECP256R1(), default_backend()) shared = newpk.exchange(ec.ECDH(), enckey._get_public()) @@ -437,13 +444,13 @@ def ecies_hkdf(self, enckey, plainkey): newpk = X25519PrivateKey.generate() shared = newpk.exchange(enckey._get_public()) derived_key = HKDF( - algorithm=hashes.SHA256(), length=48, salt=None, + algorithm=hmac_sha_alg, length=48, salt=None, info=b'MCUBoot_ECIES_v1', backend=default_backend()).derive(shared) encryptor = Cipher(algorithms.AES(derived_key[:16]), modes.CTR(bytes([0] * 16)), backend=default_backend()).encryptor() cipherkey = encryptor.update(plainkey) + encryptor.finalize() - mac = hmac.HMAC(derived_key[16:], hashes.SHA256(), + mac = hmac.HMAC(derived_key[16:], hmac_sha_alg, backend=default_backend()) mac.update(cipherkey) ciphermac = mac.finalize() @@ -461,7 +468,8 @@ 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, fixed_sig=None, pub_key=None, vector_to_sign=None, - user_sha='auto', is_pure=False, keep_comp_size=False, dont_encrypt=False): + user_sha='auto', hmac_sha='auto', is_pure=False, keep_comp_size=False, + dont_encrypt=False): self.enckey = enckey # key decides on sha, then pub_key; of both are none default is used @@ -668,6 +676,17 @@ def create(self, key, public_key_format, enckey, dependencies=None, else: plainkey = os.urandom(16) + if not isinstance(enckey, rsa.RSAPublic): + if hmac_sha == 'auto' or hmac_sha == '256': + hmac_sha = '256' + hmac_sha_alg = hashes.SHA256() + elif hmac_sha == '512': + if not isinstance(enckey, x25519.X25519Public): + raise click.UsageError("Currently only ECIES-X25519 supports HMAC-SHA512") + hmac_sha_alg = hashes.SHA512() + else: + raise click.UsageError("Unsupported HMAC-SHA") + if isinstance(enckey, rsa.RSAPublic): cipherkey = enckey._get_public().encrypt( plainkey, padding.OAEP( @@ -676,15 +695,19 @@ def create(self, key, public_key_format, enckey, dependencies=None, label=None)) self.enctlv_len = len(cipherkey) tlv.add('ENCRSA2048', cipherkey) - elif isinstance(enckey, (ecdsa.ECDSA256P1Public, - x25519.X25519Public)): - cipherkey, mac, pubk = self.ecies_hkdf(enckey, plainkey) + elif isinstance(enckey, ecdsa.ECDSA256P1Public): + cipherkey, mac, pubk = self.ecies_hkdf(enckey, plainkey, hmac_sha_alg) enctlv = pubk + mac + cipherkey self.enctlv_len = len(enctlv) - if isinstance(enckey, ecdsa.ECDSA256P1Public): - tlv.add('ENCEC256', enctlv) - else: + tlv.add('ENCEC256', enctlv) + elif isinstance(enckey, x25519.X25519Public): + cipherkey, mac, pubk = self.ecies_hkdf(enckey, plainkey, hmac_sha_alg) + enctlv = pubk + mac + cipherkey + self.enctlv_len = len(enctlv) + if (hmac_sha == '256'): tlv.add('ENCX25519', enctlv) + else: + tlv.add('ENCX25519_SHA512', enctlv) if not clear: nonce = bytes([0] * 16) diff --git a/scripts/imgtool/main.py b/scripts/imgtool/main.py index 1cdb792a5..5ff1f8f9f 100755 --- a/scripts/imgtool/main.py +++ b/scripts/imgtool/main.py @@ -84,6 +84,7 @@ def gen_x25519(keyfile, passwd): } valid_formats = ['openssl', 'pkcs8'] valid_sha = [ 'auto', '256', '384', '512' ] +valid_hmac_sha = [ 'auto', '256', '512' ] def load_signature(sigfile): @@ -437,6 +438,8 @@ def convert(self, value, param, ctx): @click.option('--sha', 'user_sha', type=click.Choice(valid_sha), default='auto', help='selected sha algorithm to use; defaults to "auto" which is 256 if ' 'no cryptographic signature is used, or default for signature type') +@click.option('--hmac-sha', 'hmac_sha', type=click.Choice(valid_hmac_sha), default='auto', + help='sha algorithm used in HKDF/HMAC in ECIES key exchange TLV') @click.option('--vector-to-sign', type=click.Choice(['payload', 'digest']), help='send to OUTFILE the payload or payload''s digest instead ' 'of complied image. These data can be used for external image ' @@ -449,7 +452,7 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, 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, is_pure, + clear, fix_sig, fix_sig_pubkey, sig_out, user_sha, hmac_sha, is_pure, vector_to_sign, non_bootable): if confirm: @@ -526,7 +529,7 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, img.create(key, public_key_format, enckey, dependencies, boot_record, custom_tlvs, compression_tlvs, None, int(encrypt_keylen), clear, baked_signature, pub_key, vector_to_sign, user_sha=user_sha, - is_pure=is_pure, keep_comp_size=False, dont_encrypt=True) + hmac_sha=hmac_sha, is_pure=is_pure, keep_comp_size=False, dont_encrypt=True) compressed_img = image.Image(version=decode_version(version), header_size=header_size, pad_header=pad_header, pad=pad, confirm=confirm, align=int(align), @@ -542,9 +545,11 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, ] if compression == "lzma2armthumb": compression_filters.insert(0, {"id":lzma.FILTER_ARMTHUMB}) - compressed_data = lzma.compress(img.get_infile_data(),filters=compression_filters, - format=lzma.FORMAT_RAW) - uncompressed_size = len(img.get_infile_data()) + + infile_offset = 0 if pad_header else header_size + compressed_data = lzma.compress(img.get_infile_data()[infile_offset:], + filters=compression_filters, format=lzma.FORMAT_RAW) + uncompressed_size = len(img.get_infile_data()[infile_offset:]) compressed_size = len(compressed_data) print(f"compressed image size: {compressed_size} bytes") print(f"original image size: {uncompressed_size} bytes") @@ -568,14 +573,14 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, compressed_img.create(key, public_key_format, enckey, dependencies, boot_record, custom_tlvs, compression_tlvs, compression, int(encrypt_keylen), clear, baked_signature, - pub_key, vector_to_sign, user_sha=user_sha, + 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, baked_signature, pub_key, vector_to_sign, user_sha=user_sha, - is_pure=is_pure) + hmac_sha=hmac_sha, is_pure=is_pure) img.save(outfile, hex_addr) if sig_out is not None: new_signature = img.get_signature() diff --git a/zephyr/module.yml b/zephyr/module.yml index d2af55384..b73ae2a0d 100644 --- a/zephyr/module.yml +++ b/zephyr/module.yml @@ -1,7 +1,8 @@ samples: - boot/zephyr build: - cmake: ./boot/bootutil/zephyr + cmake-ext: True + kconfig-ext: True sysbuild-cmake: boot/zephyr/sysbuild package-managers: pip: