diff --git a/.github/workflows/commit-tags.yml b/.github/workflows/commit-tags.yml new file mode 100644 index 000000000..9e0323f94 --- /dev/null +++ b/.github/workflows/commit-tags.yml @@ -0,0 +1,31 @@ +name: Commit tags + +on: pull_request + +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: Install python dependencies + run: | + pip3 install setuptools + pip3 install wheel + pip3 install gitlint + + - name: Run the commit tags + uses: nrfconnect/action-commit-tags@main + with: + target: '.' + baserev: origin/${{ github.base_ref }} + revrange: 'none' diff --git a/boot/boot_serial/src/boot_serial_encryption.c b/boot/boot_serial/src/boot_serial_encryption.c index 7d3b47c72..c4bd7d87b 100644 --- a/boot/boot_serial/src/boot_serial_encryption.c +++ b/boot/boot_serial/src/boot_serial_encryption.c @@ -171,7 +171,7 @@ decrypt_region_inplace(struct boot_loader_state *state, blk_sz = tlv_off - (off + bytes_copied); } } - boot_encrypt(BOOT_CURR_ENC(state), slot, + boot_enc_decrypt(BOOT_CURR_ENC(state), slot, (off + bytes_copied + idx) - hdr->ih_hdr_size, blk_sz, blk_off, &buf[idx]); } diff --git a/boot/bootutil/include/bootutil/crypto/aes_ctr.h b/boot/bootutil/include/bootutil/crypto/aes_ctr.h index e69b0372f..fd2416176 100644 --- a/boot/bootutil/include/bootutil/crypto/aes_ctr.h +++ b/boot/bootutil/include/bootutil/crypto/aes_ctr.h @@ -15,8 +15,8 @@ #include "mcuboot_config/mcuboot_config.h" #if (defined(MCUBOOT_USE_MBED_TLS) + \ - defined(MCUBOOT_USE_TINYCRYPT)) != 1 - #error "One crypto backend must be defined: either MBED_TLS or TINYCRYPT" + defined(MCUBOOT_USE_TINYCRYPT) + defined(MCUBOOT_USE_PSA_CRYPTO)) != 1 + #error "One crypto backend must be defined: either MBED_TLS or TINYCRYPT or PSA" #endif #if defined(MCUBOOT_USE_MBED_TLS) @@ -38,12 +38,46 @@ #define BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE TC_AES_BLOCK_SIZE #endif /* MCUBOOT_USE_TINYCRYPT */ + +#if defined(MCUBOOT_USE_PSA_CRYPTO) + #include + #include "bootutil/enc_key_public.h" + #define BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE BOOT_ENC_KEY_SIZE + #define BOOTUTIL_CRYPTO_AES_CTR_BLOCK_SIZE (16) +#endif + #include #ifdef __cplusplus extern "C" { #endif +#if defined(MCUBOOT_USE_PSA_CRYPTO) +typedef struct { + /* Fixme: This should not be, here, psa_key_id should be passed */ + uint8_t key[BOOT_ENC_KEY_SIZE]; +} bootutil_aes_ctr_context; + +void bootutil_aes_ctr_init(bootutil_aes_ctr_context *ctx); + +static inline void bootutil_aes_ctr_drop(bootutil_aes_ctr_context *ctx) +{ + memset(ctx, 0, sizeof(ctx)); +} + +static inline int bootutil_aes_ctr_set_key(bootutil_aes_ctr_context *ctx, const uint8_t *k) +{ + memcpy(ctx->key, k, sizeof(ctx->key)); + + return 0; +} + +int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, + const uint8_t *m, uint32_t mlen, size_t blk_off, uint8_t *c); +int bootutil_aes_ctr_decrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, + const uint8_t *c, uint32_t clen, size_t blk_off, uint8_t *m); +#endif + #if defined(MCUBOOT_USE_MBED_TLS) typedef mbedtls_aes_context bootutil_aes_ctr_context; static inline void bootutil_aes_ctr_init(bootutil_aes_ctr_context *ctx) 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/sha.h b/boot/bootutil/include/bootutil/crypto/sha.h index 9ce54bee5..aeedff40e 100644 --- a/boot/bootutil/include/bootutil/crypto/sha.h +++ b/boot/bootutil/include/bootutil/crypto/sha.h @@ -30,17 +30,21 @@ #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 -#if defined(MCUBOOT_SIGN_EC384) +#if defined(MCUBOOT_SHA512) + #define IMAGE_HASH_SIZE (64) + #define EXPECTED_HASH_TLV IMAGE_TLV_SHA512 +#elif defined(MCUBOOT_SIGN_EC384) #define IMAGE_HASH_SIZE (48) #define EXPECTED_HASH_TLV IMAGE_TLV_SHA384 #else #define IMAGE_HASH_SIZE (32) #define EXPECTED_HASH_TLV IMAGE_TLV_SHA256 -#endif /* MCUBOOT_SIGN_EC384 */ +#endif /* MCUBOOT_SIGN */ /* Universal defines for SHA-256 */ #define BOOTUTIL_CRYPTO_SHA256_BLOCK_SIZE (64) @@ -82,7 +86,9 @@ typedef psa_hash_operation_t bootutil_sha_context; static inline int bootutil_sha_init(bootutil_sha_context *ctx) { *ctx = psa_hash_operation_init(); -#if defined(MCUBOOT_SIGN_EC384) +#if defined(MCUBOOT_SHA512) + psa_status_t status = psa_hash_setup(ctx, PSA_ALG_SHA_512); +#elif defined(MCUBOOT_SIGN_EC384) psa_status_t status = psa_hash_setup(ctx, PSA_ALG_SHA_384); #else psa_status_t status = psa_hash_setup(ctx, PSA_ALG_SHA_256); @@ -107,7 +113,9 @@ static inline int bootutil_sha_finish(bootutil_sha_context *ctx, { size_t hash_length = 0; /* Assumes the output buffer is at least the expected size of the hash */ -#if defined(MCUBOOT_SIGN_EC384) +#if defined(MCUBOOT_SHA512) + return (int)psa_hash_finish(ctx, output, PSA_HASH_LENGTH(PSA_ALG_SHA_512), &hash_length); +#elif defined(MCUBOOT_SIGN_EC384) return (int)psa_hash_finish(ctx, output, PSA_HASH_LENGTH(PSA_ALG_SHA_384), &hash_length); #else return (int)psa_hash_finish(ctx, output, PSA_HASH_LENGTH(PSA_ALG_SHA_256), &hash_length); @@ -206,6 +214,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 84e05e72f..4ff2432cd 100644 --- a/boot/bootutil/include/bootutil/enc_key.h +++ b/boot/bootutil/include/bootutil/enc_key.h @@ -70,7 +70,9 @@ int boot_enc_load(struct enc_key_data *enc_state, int slot, const struct image_header *hdr, const struct flash_area *fap, struct boot_status *bs); bool boot_enc_valid(struct enc_key_data *enc_state, int slot); -void boot_encrypt(struct enc_key_data *enc_state, int slot, +void boot_enc_encrypt(struct enc_key_data *enc_state, int slot, + uint32_t off, uint32_t sz, uint32_t blk_off, uint8_t *buf); +void boot_enc_decrypt(struct enc_key_data *enc_state, int slot, uint32_t off, uint32_t sz, uint32_t blk_off, uint8_t *buf); void boot_enc_zeroize(struct enc_key_data *enc_state); diff --git a/boot/bootutil/include/bootutil/image.h b/boot/bootutil/include/bootutil/image.h index 3e03f80dd..836712458 100644 --- a/boot/bootutil/include/bootutil/image.h +++ b/boot/bootutil/include/bootutil/image.h @@ -96,11 +96,15 @@ struct flash_area; #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 /* Whatever signature has been selected, it will be used + * as "pure" where signature is verified over entire + * image rather than hash of an image */ #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 */ diff --git a/boot/bootutil/src/bootutil_priv.h b/boot/bootutil/src/bootutil_priv.h index 208d189b9..c23f61689 100644 --- a/boot/bootutil/src/bootutil_priv.h +++ b/boot/bootutil/src/bootutil_priv.h @@ -268,6 +268,9 @@ struct boot_loader_state { fih_ret bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, size_t slen, uint8_t key_id); +fih_ret bootutil_verify_img(const uint8_t *img, uint32_t size, + uint8_t *sig, size_t slen, uint8_t key_id); + fih_ret boot_fih_memequal(const void *s1, const void *s2, size_t n); int boot_find_status(int image_index, const struct flash_area **fap); diff --git a/boot/bootutil/src/ed25519_psa.c b/boot/bootutil/src/ed25519_psa.c new file mode 100644 index 000000000..3d7274307 --- /dev/null +++ b/boot/bootutil/src/ed25519_psa.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ +#include +#include +#include + +#include +#include "bootutil/bootutil_log.h" + +#include +#include + +BOOT_LOG_MODULE_DECLARE(ed25519_psa); + +#define SHA512_DIGEST_LENGTH 64 +#define EDDSA_KEY_LENGTH 32 +#define EDDSA_SIGNAGURE_LENGTH 64 + +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]) +{ + /* Set to any error */ + psa_status_t status = PSA_ERROR_BAD_STATE; + psa_key_attributes_t key_attr = PSA_KEY_ATTRIBUTES_INIT; + psa_key_id_t kid; + 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\n", status); + return 0; + } + + status = PSA_ERROR_BAD_STATE; + + psa_set_key_type(&key_attr, + PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_TWISTED_EDWARDS)); + psa_set_key_usage_flags(&key_attr, PSA_KEY_USAGE_VERIFY_MESSAGE); + psa_set_key_algorithm(&key_attr, PSA_ALG_PURE_EDDSA); + + status = psa_import_key(&key_attr, public_key, EDDSA_KEY_LENGTH, &kid); + if (status != PSA_SUCCESS) { + BOOT_LOG_ERR("ED25519 key import failed %d", status); + return 0; + } + + status = psa_verify_message(kid, PSA_ALG_PURE_EDDSA, message, message_len, + signature, EDDSA_SIGNAGURE_LENGTH); + if (status != PSA_SUCCESS) { + BOOT_LOG_ERR("ED25519 signature verification failed %d", status); + ret = 0; + /* Pass through to destroy key */ + } else { + ret = 1; + /* Pass through to destroy key */ + } + + status = psa_destroy_key(kid); + + if (status != PSA_SUCCESS) { + /* Just for logging */ + BOOT_LOG_WRN("Failed to destroy key %d", status); + } + + return ret; +} diff --git a/boot/bootutil/src/encrypted.c b/boot/bootutil/src/encrypted.c index d094ea77b..67fa819bb 100644 --- a/boot/bootutil/src/encrypted.c +++ b/boot/bootutil/src/encrypted.c @@ -25,6 +25,7 @@ #include "bootutil/crypto/ecdh_p256.h" #endif +#if !defined(MCUBOOT_USE_PSA_CRYPTO) #if defined(MCUBOOT_ENCRYPT_X25519) #include "bootutil/crypto/ecdh_x25519.h" #endif @@ -35,6 +36,7 @@ #include "mbedtls/oid.h" #include "mbedtls/asn1.h" #endif +#endif #include "bootutil/image.h" #include "bootutil/enc_key.h" @@ -43,6 +45,30 @@ #include "bootutil_priv.h" +#define EXPECTED_ENC_LEN BOOT_ENC_TLV_SIZE + +#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 + +/* NOUP Fixme: */ +#if !defined(CONFIG_BOOT_ED25519_PSA) #if defined(MCUBOOT_ENCRYPT_EC256) || defined(MCUBOOT_ENCRYPT_X25519) #if defined(_compare) static inline int bootutil_constant_time_compare(const uint8_t *a, const uint8_t *b, size_t size) @@ -351,60 +377,6 @@ int boot_enc_retrieve_private_key(struct bootutil_key **private_key) } #endif /* !MCUBOOT_ENC_BUILTIN_KEY */ -int -boot_enc_init(struct enc_key_data *enc_state, uint8_t slot) -{ - bootutil_aes_ctr_init(&enc_state[slot].aes_ctr); - return 0; -} - -int -boot_enc_drop(struct enc_key_data *enc_state, uint8_t slot) -{ - bootutil_aes_ctr_drop(&enc_state[slot].aes_ctr); - enc_state[slot].valid = 0; - return 0; -} - -int -boot_enc_set_key(struct enc_key_data *enc_state, uint8_t slot, - const struct boot_status *bs) -{ - int rc; - - rc = bootutil_aes_ctr_set_key(&enc_state[slot].aes_ctr, bs->enckey[slot]); - if (rc != 0) { - boot_enc_drop(enc_state, slot); - return -1; - } - - enc_state[slot].valid = 1; - - return 0; -} - -#define EXPECTED_ENC_LEN BOOT_ENC_TLV_SIZE - -#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 - #if ( (defined(MCUBOOT_ENCRYPT_RSA) && defined(MCUBOOT_USE_MBED_TLS) && !defined(MCUBOOT_USE_PSA_CRYPTO)) || \ (defined(MCUBOOT_ENCRYPT_EC256) && defined(MCUBOOT_USE_MBED_TLS)) ) #if MBEDTLS_VERSION_NUMBER >= 0x03000000 @@ -627,6 +599,7 @@ boot_decrypt_key(const uint8_t *buf, uint8_t *enckey) return rc; } +#endif /* CONFIG_BOOT_ED25519_PSA */ /* * Load encryption key. @@ -681,6 +654,39 @@ boot_enc_load(struct enc_key_data *enc_state, int slot, return boot_decrypt_key(buf, bs->enckey[slot]); } +int +boot_enc_init(struct enc_key_data *enc_state, uint8_t slot) +{ + bootutil_aes_ctr_init(&enc_state[slot].aes_ctr); + return 0; +} + +int +boot_enc_drop(struct enc_key_data *enc_state, uint8_t slot) +{ + bootutil_aes_ctr_drop(&enc_state[slot].aes_ctr); + enc_state[slot].valid = 0; + return 0; +} + +int +boot_enc_set_key(struct enc_key_data *enc_state, uint8_t slot, + const struct boot_status *bs) +{ + int rc; + + rc = bootutil_aes_ctr_set_key(&enc_state[slot].aes_ctr, bs->enckey[slot]); + if (rc != 0) { + boot_enc_drop(enc_state, slot); + return -1; + } + + enc_state[slot].valid = 1; + + return 0; +} + + bool boot_enc_valid(struct enc_key_data *enc_state, int slot) { @@ -688,14 +694,13 @@ boot_enc_valid(struct enc_key_data *enc_state, int slot) } void -boot_encrypt(struct enc_key_data *enc_state, int slot, uint32_t off, +boot_enc_encrypt(struct enc_key_data *enc_state, int slot, uint32_t off, uint32_t sz, uint32_t blk_off, uint8_t *buf) { - struct enc_key_data *enc; + struct enc_key_data *enc = &enc_state[slot]; uint8_t nonce[16]; - /* boot_copy_region will call boot_encrypt with sz = 0 when skipping over - the TLVs. */ + /* Nothing to do with size == 0 */ if (sz == 0) { return; } @@ -707,11 +712,33 @@ boot_encrypt(struct enc_key_data *enc_state, int slot, uint32_t off, nonce[14] = (uint8_t)(off >> 8); nonce[15] = (uint8_t)off; - enc = &enc_state[slot]; assert(enc->valid == 1); bootutil_aes_ctr_encrypt(&enc->aes_ctr, nonce, buf, sz, blk_off, buf); } +void +boot_enc_decrypt(struct enc_key_data *enc_state, int slot, uint32_t off, + uint32_t sz, uint32_t blk_off, uint8_t *buf) +{ + struct enc_key_data *enc = &enc_state[slot]; + uint8_t nonce[16]; + + /* Nothing to do with size == 0 */ + if (sz == 0) { + return; + } + + memset(nonce, 0, 12); + off >>= 4; + nonce[12] = (uint8_t)(off >> 24); + nonce[13] = (uint8_t)(off >> 16); + nonce[14] = (uint8_t)(off >> 8); + nonce[15] = (uint8_t)off; + + assert(enc->valid == 1); + bootutil_aes_ctr_decrypt(&enc->aes_ctr, nonce, buf, sz, blk_off, buf); +} + /** * Clears encrypted state after use. */ diff --git a/boot/bootutil/src/encrypted_psa.c b/boot/bootutil/src/encrypted_psa.c new file mode 100644 index 000000000..927ce2d6b --- /dev/null +++ b/boot/bootutil/src/encrypted_psa.c @@ -0,0 +1,453 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include "mcuboot_config/mcuboot_config.h" + +#include +#include +#include + +/* We are not really using the MBEDTLS but need the ASN.1 parsing functions */ +#define MBEDTLS_ASN1_PARSE_C + +#include "bootutil/crypto/sha.h" +#include "mbedtls/oid.h" +#include "mbedtls/asn1.h" + +#include "bootutil/image.h" +#include "bootutil/enc_key.h" +#include "bootutil/sign_key.h" +#include "bootutil/crypto/common.h" + +#include "bootutil_priv.h" +#include "bootutil/bootutil_log.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot_psa_enc); + +#define EXPECTED_ENC_LEN BOOT_ENC_TLV_SIZE +#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"); + +#define X25519_OID "\x6e" +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 + +/* Fixme: This duplicates code from encrypted.c and depends on mbedtls */ +static int +parse_x25519_enckey(uint8_t **p, uint8_t *end, uint8_t *private_key) +{ + size_t len; + int version; + mbedtls_asn1_buf alg; + mbedtls_asn1_buf param; + + if (mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | + MBEDTLS_ASN1_SEQUENCE) != 0) { + return -1; + } + + if (*p + len != end) { + return -2; + } + + version = 0; + if (mbedtls_asn1_get_int(p, end, &version) || version != 0) { + return -3; + } + + if (mbedtls_asn1_get_alg(p, end, &alg, ¶m) != 0) { + return -4; + } + + if (alg.ASN1_CONTEXT_MEMBER(len) != sizeof(ec_pubkey_oid) - 1 || + memcmp(alg.ASN1_CONTEXT_MEMBER(p), ec_pubkey_oid, sizeof(ec_pubkey_oid) - 1)) { + return -5; + } + + if (mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_OCTET_STRING) != 0) { + return -6; + } + + if (mbedtls_asn1_get_tag(p, end, &len, MBEDTLS_ASN1_OCTET_STRING) != 0) { + return -7; + } + + if (len != PRIV_KEY_LEN) { + return -8; + } + + memcpy(private_key, *p, PRIV_KEY_LEN); + return 0; +} + +void bootutil_aes_ctr_init(bootutil_aes_ctr_context *ctx) +{ + psa_status_t psa_ret = psa_crypto_init(); + + (void)ctx; + + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES init PSA crypto init failed %d", psa_ret); + assert(0); + } +} + +#if defined(MCUBOOT_ENC_IMAGES) +/* + * Decrypt an encryption key TLV. + * + * @param buf An encryption TLV read from flash (build time fixed length) + * @param enckey An AES-128 or AES-256 key sized buffer to store to plain key. + */ +int +boot_decrypt_key(const uint8_t *buf, uint8_t *enckey) +{ + uint8_t derived_key[BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE]; + uint8_t *cp; + uint8_t *cpend; + uint8_t private_key[PRIV_KEY_LEN]; + size_t len; + psa_status_t psa_ret = PSA_ERROR_BAD_STATE; + psa_status_t psa_cleanup_ret = PSA_ERROR_BAD_STATE; + psa_key_id_t kid; + psa_key_attributes_t kattr = PSA_KEY_ATTRIBUTES_INIT; + psa_key_derivation_operation_t key_do = PSA_KEY_DERIVATION_OPERATION_INIT; + psa_algorithm_t key_do_alg; + int rc = -1; + + cp = (uint8_t *)bootutil_enc_key.key; + cpend = cp + *bootutil_enc_key.len; + + /* The psa_cipher_decrypt needs initialization vector of proper length at + * the beginning of the input buffer. + */ + uint8_t iv_and_key[PSA_CIPHER_IV_LENGTH(PSA_KEY_TYPE_AES, PSA_ALG_CTR) + + BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE]; + + psa_ret = psa_crypto_init(); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES crypto init failed %d", psa_ret); + return -1; + } + + /* + * Load the stored X25519 decryption private key + */ + rc = parse_x25519_enckey(&cp, cpend, private_key); + if (rc) { + return rc; + } + + psa_set_key_type(&kattr, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_MONTGOMERY)); + psa_set_key_usage_flags(&kattr, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&kattr, PSA_ALG_ECDH); + + psa_ret = psa_import_key(&kattr, private_key, sizeof(private_key), &kid); + memset(private_key, 0, sizeof(private_key)); + psa_reset_key_attributes(&kattr); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("Built-in key import failed %d", psa_ret); + return -1; + } + + key_do_alg = PSA_ALG_KEY_AGREEMENT(PSA_ALG_ECDH, PSA_ALG_HKDF(PSA_ALG_SHA_256)); + + psa_ret = psa_key_derivation_setup(&key_do, key_do_alg); + if (psa_ret != PSA_SUCCESS) { + psa_cleanup_ret = psa_destroy_key(kid); + if (psa_cleanup_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("Built-in key destruction failed %d", psa_cleanup_ret); + } + BOOT_LOG_ERR("Key derivation setup failed %d", psa_ret); + return -1; + } + + /* Note: PSA 1.1.2 does not have psa_key_agreement that would be useful here + * as it could just add the derived key to the storage and return key id. + * Instead, we have to use the code below to generate derived key and put it + * into storage, to obtain the key id we can then use with psa_mac_* functions. + */ + psa_ret = psa_key_derivation_key_agreement(&key_do, PSA_KEY_DERIVATION_INPUT_SECRET, + kid, &buf[EC_PUBK_INDEX], + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE); + psa_cleanup_ret = psa_destroy_key(kid); + if (psa_cleanup_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("Built-in key destruction failed %d", psa_cleanup_ret); + } + if (psa_ret != PSA_SUCCESS) { + psa_cleanup_ret = psa_key_derivation_abort(&key_do); + if (psa_cleanup_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("Key derivation abort failed %d", psa_ret); + } + + BOOT_LOG_ERR("Key derivation failed %d", psa_ret); + return -1; + } + + /* Only info, no salt */ + psa_ret = psa_key_derivation_input_bytes(&key_do, PSA_KEY_DERIVATION_INPUT_INFO, + "MCUBoot_ECIES_v1", 16); + if (psa_ret != PSA_SUCCESS) { + psa_cleanup_ret = psa_key_derivation_abort(&key_do); + if (psa_cleanup_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("Key derivation abort failed %d", psa_ret); + } + BOOT_LOG_ERR("Key derivation failed %d", psa_ret); + return -1; + } + + len = BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE; + psa_ret = psa_key_derivation_output_bytes(&key_do, derived_key, len); + psa_cleanup_ret = psa_key_derivation_abort(&key_do); + if (psa_cleanup_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("Key derivation cleanup failed %d", psa_ret); + } + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("Key derivation failed %d", psa_ret); + return -1; + } + + /* The derived key consists of BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE bytes + * followed by BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE bytes. Both parts will + * be imported at the point where needed and discarded immediately after. + */ + 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)); + + /* Import the MAC tag key part of derived key, that is the part that starts + * after BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE and has length of + * BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE bytes. + */ + psa_ret = psa_import_key(&kattr, + &derived_key[BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE], + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE, &kid); + psa_reset_key_attributes(&kattr); + if (psa_ret != PSA_SUCCESS) { + memset(derived_key, 0, sizeof(derived_key)); + BOOT_LOG_ERR("MAC key import failed %d", psa_ret); + return -1; + } + + /* Verify the MAC tag of the random encryption key */ + psa_ret = psa_mac_verify(kid, PSA_ALG_HMAC(PSA_ALG_SHA_256), + &buf[EC_CIPHERKEY_INDEX], BOOT_ENC_KEY_SIZE, + &buf[EC_TAG_INDEX], + BOOTUTIL_CRYPTO_SHA256_DIGEST_SIZE); + psa_cleanup_ret = psa_destroy_key(kid); + if (psa_cleanup_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("MAC key destruction failed %d", psa_cleanup_ret); + } + if (psa_ret != PSA_SUCCESS) { + memset(derived_key, 0, sizeof(derived_key)); + BOOT_LOG_ERR("MAC verification failed %d", psa_ret); + return -1; + } + + /* The derived key is used in AES decryption of random key */ + psa_set_key_type(&kattr, PSA_KEY_TYPE_AES); + psa_set_key_usage_flags(&kattr, PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&kattr, PSA_ALG_CTR); + + /* Import the AES partition of derived key, the first 16 bytes */ + psa_ret = psa_import_key(&kattr, &derived_key[0], + BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE, &kid); + memset(derived_key, 0, sizeof(derived_key)); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES key import failed %d", psa_ret); + return -1; + } + + /* Decrypt the random AES encryption key with AES and the key obtained + * at derivation. */ + memset(&iv_and_key[0], 0, PSA_CIPHER_IV_LENGTH(PSA_KEY_TYPE_AES, PSA_ALG_CTR)); + memcpy(&iv_and_key[PSA_CIPHER_IV_LENGTH(PSA_KEY_TYPE_AES, PSA_ALG_CTR)], + &buf[EC_CIPHERKEY_INDEX], + sizeof(iv_and_key) - PSA_CIPHER_IV_LENGTH(PSA_KEY_TYPE_AES, PSA_ALG_CTR)); + + len = 0; + psa_ret = psa_cipher_decrypt(kid, PSA_ALG_CTR, iv_and_key, sizeof(iv_and_key), + enckey, BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE, &len); + memset(iv_and_key, 0, sizeof(iv_and_key)); + psa_cleanup_ret = psa_destroy_key(kid); + if (psa_cleanup_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("AES key destruction failed %d", psa_cleanup_ret); + } + if (psa_ret != PSA_SUCCESS || len != BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE) { + memset(enckey, 0, BOOTUTIL_CRYPTO_AES_CTR_KEY_SIZE); + BOOT_LOG_ERR("Random key decryption failed %d", psa_ret); + return -1; + } + + return 0; +} + +int bootutil_aes_ctr_encrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, + const uint8_t *m, uint32_t mlen, size_t blk_off, uint8_t *c) +{ + int ret = 0; + psa_status_t psa_ret = PSA_ERROR_BAD_STATE; + psa_key_attributes_t kattr = PSA_KEY_ATTRIBUTES_INIT; + psa_key_id_t kid; + psa_cipher_operation_t psa_op; + size_t elen = 0; /* Decrypted length */ + + /* Fixme: calling psa_crypto_init multiple times is not a problem, + * yet the code here is only present because there is not general + * crypto init. */ + psa_ret = psa_crypto_init(); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("PSA crypto init failed %d", psa_ret); + ret = -1; + goto gone; + } + + psa_op = psa_cipher_operation_init(); + + /* Fixme: Import should happen when key is decrypted, but due to lack + * of key destruction there is no way to destroy key stored by + * psa other way than here. */ + psa_set_key_type(&kattr, PSA_KEY_TYPE_AES); + psa_set_key_usage_flags(&kattr, PSA_KEY_USAGE_ENCRYPT); + psa_set_key_algorithm(&kattr, PSA_ALG_CTR); + + psa_ret = psa_import_key(&kattr, ctx->key, BOOT_ENC_KEY_SIZE, &kid); + psa_reset_key_attributes(&kattr); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES enc import key failed %d", psa_ret); + ret = -1; + goto gone; + } + + /* This could be done with psa_cipher_decrypt one-shot operation, but + * multi-part operation is used to avoid re-allocating input buffer + * to account for IV in front of data. + */ + psa_ret = psa_cipher_encrypt_setup(&psa_op, kid, PSA_ALG_CTR); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES enc setup failed %d", psa_ret); + ret = -1; + goto gone_with_key; + } + + /* Fixme: hardcoded counter size, but it is hardcoded everywhere */ + psa_ret = psa_cipher_set_iv(&psa_op, counter, 16); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES enc IV set failed %d", psa_ret); + ret = -1; + goto gone_after_setup; + } + + psa_ret = psa_cipher_update(&psa_op, m, mlen, c, mlen, &elen); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES enc encryption failed %d", psa_ret); + ret = -1; + goto gone_after_setup; + } + +gone_after_setup: + psa_ret = psa_cipher_abort(&psa_op); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("AES enc cipher abort failed %d", psa_ret); + /* Intentionally not changing the ret */ + } +gone_with_key: + /* Fixme: Should be removed once key is shared by id */ + psa_ret = psa_destroy_key(kid); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("AES enc destroy key failed %d", psa_ret); + /* Intentionally not changing the ret */ + } +gone: + return ret; +} + +int bootutil_aes_ctr_decrypt(bootutil_aes_ctr_context *ctx, uint8_t *counter, + const uint8_t *c, uint32_t clen, size_t blk_off, uint8_t *m) +{ + int ret = 0; + psa_status_t psa_ret = PSA_ERROR_BAD_STATE; + psa_key_attributes_t kattr = PSA_KEY_ATTRIBUTES_INIT; + psa_key_id_t kid; + psa_cipher_operation_t psa_op; + size_t dlen = 0; /* Decrypted length */ + + /* Fixme: the init should already happen before calling the function, but + * somehow it does not, for example when recovering in swap. + */ + psa_ret = psa_crypto_init(); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("PSA crypto init failed %d", psa_ret); + ret = -1; + goto gone; + } + + psa_op = psa_cipher_operation_init(); + + /* Fixme: Import should happen when key is decrypted, but due to lack + * of key destruction there is no way to destroy key stored by + * psa other way than here. */ + psa_set_key_type(&kattr, PSA_KEY_TYPE_AES); + psa_set_key_usage_flags(&kattr, PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&kattr, PSA_ALG_CTR); + + psa_ret = psa_import_key(&kattr, ctx->key, BOOT_ENC_KEY_SIZE, &kid); + psa_reset_key_attributes(&kattr); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES dec import key failed %d", psa_ret); + ret = -1; + goto gone; + } + + /* This could be done with psa_cipher_decrypt one-shot operation, but + * multi-part operation is used to avoid re-allocating input buffer + * to account for IV in front of data. + */ + psa_ret = psa_cipher_decrypt_setup(&psa_op, kid, PSA_ALG_CTR); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES dec setup failed %d", psa_ret); + ret = -1; + goto gone_with_key; + } + + /* Fixme: hardcoded counter size, but it is hardcoded everywhere */ + psa_ret = psa_cipher_set_iv(&psa_op, counter, 16); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES dec IV set failed %d", psa_ret); + ret = -1; + goto gone_after_setup; + } + + psa_ret = psa_cipher_update(&psa_op, c, clen, m, clen, &dlen); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_ERR("AES dec decryption failed %d", psa_ret); + ret = -1; + goto gone_after_setup; + } + +gone_after_setup: + psa_ret = psa_cipher_abort(&psa_op); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("PSA dec abort failed %d", psa_ret); + /* Intentionally not changing the ret */ + } +gone_with_key: + psa_ret = psa_destroy_key(kid); + if (psa_ret != PSA_SUCCESS) { + BOOT_LOG_WRN("PSA dec key failed %d", psa_ret); + /* Intentionally not changing the ret */ + } +gone: + return ret; +} +#endif /* defined(MCUBOOT_ENC_IMAGES) */ diff --git a/boot/bootutil/src/image_ed25519.c b/boot/bootutil/src/image_ed25519.c index 7a597d4c0..f4914ac00 100644 --- a/boot/bootutil/src/image_ed25519.c +++ b/boot/bootutil/src/image_ed25519.c @@ -9,6 +9,11 @@ #include "mcuboot_config/mcuboot_config.h" +#if defined(CONFIG_NRF_SECURITY) +/* We are not really using the MBEDTLS but need the ASN.1 parsing funcitons */ +#define MBEDTLS_ASN1_PARSE_C +#endif + #ifdef MCUBOOT_SIGN_ED25519 #include "bootutil/sign_key.h" @@ -17,17 +22,22 @@ #include "bootutil_priv.h" #include "bootutil/crypto/common.h" +#include "bootutil/crypto/sha.h" + +#define EDDSA_SIGNATURE_LENGTH 64 -static const uint8_t ed25519_pubkey_oid[] = MBEDTLS_OID_ISO_IDENTIFIED_ORG "\x65\x70"; #define NUM_ED25519_BYTES 32 extern int ED25519_verify(const uint8_t *message, size_t message_len, - const uint8_t signature[64], - const uint8_t public_key[32]); + const uint8_t signature[EDDSA_SIGNATURE_LENGTH], + const uint8_t public_key[NUM_ED25519_BYTES]); +#if !defined(MCUBOOT_KEY_IMPORT_BYPASS_ASN) /* * Parse the public key used for signing. */ +static const uint8_t ed25519_pubkey_oid[] = MBEDTLS_OID_ISO_IDENTIFIED_ORG "\x65\x70"; + static int bootutil_import_key(uint8_t **cp, uint8_t *end) { @@ -63,6 +73,7 @@ bootutil_import_key(uint8_t **cp, uint8_t *end) return 0; } +#endif /* !defined(MCUBOOT_KEY_IMPORT_BYPASS_ASN) */ fih_ret bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, size_t slen, @@ -73,7 +84,58 @@ bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, size_t slen, uint8_t *pubkey; uint8_t *end; - if (hlen != 32 || slen != 64) { + if (hlen != IMAGE_HASH_SIZE || slen != EDDSA_SIGNATURE_LENGTH) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + + 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) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } +#else + /* Directly use the key contents from the ASN stream, + * these are the last NUM_ED25519_BYTES. + * There is no check whether this is the correct key, + * here, by the algorithm selected. + */ + if (*bootutil_keys[key_id].len < NUM_ED25519_BYTES) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + + pubkey = end - NUM_ED25519_BYTES; +#endif + + rc = ED25519_verify(hash, IMAGE_HASH_SIZE, sig, pubkey); + + if (rc == 0) { + /* if verify returns 0, there was an error. */ + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + + FIH_SET(fih_rc, FIH_SUCCESS); +out: + + FIH_RET(fih_rc); +} + +fih_ret +bootutil_verify_img(const uint8_t *img, uint32_t size, + uint8_t *sig, size_t slen, uint8_t key_id) +{ + int rc; + FIH_DECLARE(fih_rc, FIH_FAILURE); + uint8_t *pubkey; + uint8_t *end; + + if (slen != EDDSA_SIGNATURE_LENGTH) { FIH_SET(fih_rc, FIH_FAILURE); goto out; } @@ -81,13 +143,27 @@ bootutil_verify_sig(uint8_t *hash, uint32_t hlen, uint8_t *sig, size_t slen, 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) { FIH_SET(fih_rc, FIH_FAILURE); goto out; } +#else + /* Directly use the key contents from the ASN stream, + * these are the last NUM_ED25519_BYTES. + * There is no check whether this is the correct key, + * here, by the algorithm selected. + */ + if (*bootutil_keys[key_id].len < NUM_ED25519_BYTES) { + FIH_SET(fih_rc, FIH_FAILURE); + goto out; + } + + pubkey = end - NUM_ED25519_BYTES; +#endif - rc = ED25519_verify(hash, 32, sig, pubkey); + rc = ED25519_verify(img, size, sig, pubkey); if (rc == 0) { /* if verify returns 0, there was an error. */ diff --git a/boot/bootutil/src/image_validate.c b/boot/bootutil/src/image_validate.c index 5953658b0..600d25a1a 100644 --- a/boot/bootutil/src/image_validate.c +++ b/boot/bootutil/src/image_validate.c @@ -40,6 +40,15 @@ #include "mcuboot_config/mcuboot_config.h" +#if defined(MCUBOOT_DECOMPRESS_IMAGES) +#include +#include +#endif + +#include "bootutil/bootutil_log.h" + +BOOT_LOG_MODULE_DECLARE(mcuboot); + #ifdef MCUBOOT_ENC_IMAGES #include "bootutil/enc_key.h" #endif @@ -56,6 +65,7 @@ #include "bootutil_priv.h" +#ifndef MCUBOOT_SIGN_PURE /* * Compute SHA hash over the image. * (SHA384 if ECDSA-P384 is being used, @@ -68,13 +78,15 @@ bootutil_img_hash(struct enc_key_data *enc_state, int image_index, uint8_t *seed, int seed_len) { bootutil_sha_context sha_ctx; - uint32_t blk_sz; uint32_t size; uint16_t hdr_size; - uint32_t off; - int rc; uint32_t blk_off; uint32_t tlv_off; +#if !defined(MCUBOOT_HASH_STORAGE_DIRECTLY) + int rc; + uint32_t off; + uint32_t blk_sz; +#endif #if (BOOT_IMAGE_NUMBER == 1) || !defined(MCUBOOT_ENC_IMAGES) || \ defined(MCUBOOT_RAM_LOAD) @@ -117,6 +129,12 @@ bootutil_img_hash(struct enc_key_data *enc_state, int image_index, /* If protected TLVs are present they are also hashed. */ size += hdr->ih_protect_tlv_size; +#ifdef MCUBOOT_HASH_STORAGE_DIRECTLY + /* 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); +#else /* MCUBOOT_HASH_STORAGE_DIRECTLY */ #ifdef MCUBOOT_RAM_LOAD bootutil_sha_update(&sha_ctx, (void*)(IMAGE_RAM_BASE + hdr->ih_load_addr), @@ -153,19 +171,21 @@ bootutil_img_hash(struct enc_key_data *enc_state, int image_index, if (off >= hdr_size && off < tlv_off) { blk_off = (off - hdr_size) & 0xf; - boot_encrypt(enc_state, slot, off - hdr_size, - blk_sz, blk_off, tmp_buf); + boot_enc_decrypt(enc_state, slot, off - hdr_size, + blk_sz, blk_off, tmp_buf); } } #endif bootutil_sha_update(&sha_ctx, tmp_buf, blk_sz); } #endif /* MCUBOOT_RAM_LOAD */ +#endif /* MCUBOOT_HASH_STORAGE_DIRECTLY */ bootutil_sha_finish(&sha_ctx, hash_result); bootutil_sha_drop(&sha_ctx); return 0; } +#endif /* * Currently, we only support being able to verify one type of @@ -352,6 +372,35 @@ bootutil_get_img_security_cnt(struct image_header *hdr, return 0; } +#if defined(MCUBOOT_SIGN_PURE) +/* Returns: + * 0 -- found + * 1 -- not found + * -1 -- failed for some reason + * + * Value of TLV does not matter, presence decides. + */ +static int bootutil_check_for_pure(const struct image_header *hdr, + const struct flash_area *fap) +{ + struct image_tlv_iter it; + uint32_t off; + uint16_t len; + int32_t rc; + + rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_SIG_PURE, false); + if (rc) { + return rc; + } + + /* Search for the TLV */ + rc = bootutil_tlv_iter_next(&it, &off, &len, NULL); + + return rc; +} +#endif + + #ifndef ALLOW_ROGUE_TLVS /* * The following list of TLVs are the only entries allowed in the unprotected @@ -362,11 +411,15 @@ static const uint16_t allowed_unprot_tlvs[] = { IMAGE_TLV_PUBKEY, IMAGE_TLV_SHA256, IMAGE_TLV_SHA384, + IMAGE_TLV_SHA512, IMAGE_TLV_RSA2048_PSS, IMAGE_TLV_ECDSA224, IMAGE_TLV_ECDSA_SIG, IMAGE_TLV_RSA3072_PSS, IMAGE_TLV_ED25519, +#if defined(MCUBOOT_SIGN_PURE) + IMAGE_TLV_SIG_PURE, +#endif IMAGE_TLV_ENC_RSA2048, IMAGE_TLV_ENC_KW, IMAGE_TLV_ENC_EC256, @@ -389,7 +442,6 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index, uint32_t off; uint16_t len; uint16_t type; - int image_hash_valid = 0; #ifdef EXPECTED_SIG_TLV FIH_DECLARE(valid_signature, FIH_FAILURE); #ifndef MCUBOOT_BUILTIN_KEY @@ -406,7 +458,10 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index, #endif /* EXPECTED_SIG_TLV */ struct image_tlv_iter it; uint8_t buf[SIG_BUF_SIZE]; +#if defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE) + int image_hash_valid = 0; uint8_t hash[IMAGE_HASH_SIZE]; +#endif int rc = 0; FIH_DECLARE(fih_rc, FIH_FAILURE); #ifdef MCUBOOT_HW_ROLLBACK_PROT @@ -415,6 +470,69 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index, FIH_DECLARE(security_counter_valid, FIH_FAILURE); #endif +#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(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) rc = bootutil_img_hash(enc_state, image_index, hdr, fap, tmp_buf, tmp_buf_sz, hash, seed, seed_len); if (rc) { @@ -424,6 +542,15 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index, if (out_hash) { memcpy(out_hash, hash, IMAGE_HASH_SIZE); } +#endif + +#if defined(MCUBOOT_SIGN_PURE) + /* If Pure type signature is expected then it has to be there */ + rc = bootutil_check_for_pure(hdr, fap); + if (rc != 0) { + goto out; + } +#endif rc = bootutil_tlv_iter_begin(&it, hdr, fap, IMAGE_TLV_ANY, false); if (rc) { @@ -467,8 +594,10 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index, } } #endif - - if (type == EXPECTED_HASH_TLV) { + switch(type) { +#if defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE) + case EXPECTED_HASH_TLV: + { /* Verify the image hash. This must always be present. */ if (len != sizeof(hash)) { rc = -1; @@ -486,8 +615,12 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index, } image_hash_valid = 1; + break; + } +#endif /* defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE) */ #ifdef EXPECTED_KEY_TLV - } else if (type == EXPECTED_KEY_TLV) { + case EXPECTED_KEY_TLV: + { /* * Determine which key we should be checking. */ @@ -512,9 +645,12 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index, * The key may not be found, which is acceptable. There * can be multiple signatures, each preceded by a key. */ + break; + } #endif /* EXPECTED_KEY_TLV */ #ifdef EXPECTED_SIG_TLV - } else if (type == EXPECTED_SIG_TLV) { + case EXPECTED_SIG_TLV: + { /* Ignore this signature if it is out of bounds. */ if (key_id < 0 || key_id >= bootutil_key_cnt) { key_id = -1; @@ -528,12 +664,25 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index, if (rc) { goto out; } +#ifndef MCUBOOT_SIGN_PURE FIH_CALL(bootutil_verify_sig, valid_signature, hash, sizeof(hash), buf, len, key_id); +#else + /* Directly check signature on the image, by using the mapping of + * a device to memory. The pointer is beginning of image in flash, + * so offset of area, the range is header + image + protected tlvs. + */ + FIH_CALL(bootutil_verify_img, valid_signature, (void *)flash_area_get_off(fap), + hdr->ih_hdr_size + hdr->ih_img_size + hdr->ih_protect_tlv_size, + buf, len, key_id); +#endif key_id = -1; + break; + } #endif /* EXPECTED_SIG_TLV */ #ifdef MCUBOOT_HW_ROLLBACK_PROT - } else if (type == IMAGE_TLV_SEC_CNT) { + case IMAGE_TLV_SEC_CNT: + { /* * Verify the image's security counter. * This must always be present. @@ -568,14 +717,21 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index, /* The image's security counter has been successfully verified. */ security_counter_valid = fih_rc; + break; + } #endif /* MCUBOOT_HW_ROLLBACK_PROT */ } } +#if defined(EXPECTED_HASH_TLV) && !defined(MCUBOOT_SIGN_PURE) rc = !image_hash_valid; if (rc) { goto out; } +#elif defined(MCUBOOT_SIGN_PURE) + /* This returns true on EQ, rc is err on non-0 */ + rc = !FIH_EQ(valid_signature, FIH_SUCCESS); +#endif #ifdef EXPECTED_SIG_TLV FIH_SET(fih_rc, valid_signature); #endif @@ -586,6 +742,161 @@ bootutil_img_validate(struct enc_key_data *enc_state, int image_index, } #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(enc_state, image_index, 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(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 +#ifdef 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(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 /* 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(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 (key_id < 0 || key_id >= bootutil_key_cnt) { + key_id = -1; + continue; + } + + 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: if (rc) { FIH_SET(fih_rc, FIH_FAILURE); diff --git a/boot/bootutil/src/loader.c b/boot/bootutil/src/loader.c index 3f0793388..53f40c33a 100644 --- a/boot/bootutil/src/loader.c +++ b/boot/bootutil/src/loader.c @@ -49,6 +49,23 @@ #include "bootutil/boot_hooks.h" #include "bootutil/mcuboot_status.h" +#if defined(MCUBOOT_DECOMPRESS_IMAGES) +#include +#include +#endif + +#ifdef __ZEPHYR__ +#include +#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 @@ -62,22 +79,30 @@ 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}; #endif +#if (!defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)) || \ +defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) #if !defined(__BOOTSIM__) /* Used for holding static buffers in multiple functions to work around issues * in older versions of gcc (e.g. 4.8.4) */ struct sector_buffer_t { - boot_sector_t *primary; - boot_sector_t *secondary; + boot_sector_t primary[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS]; + boot_sector_t secondary[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS]; #if MCUBOOT_SWAP_USING_SCRATCH - boot_sector_t *scratch; + boot_sector_t scratch[BOOT_MAX_IMG_SECTORS]; #endif }; + +static struct sector_buffer_t sector_buffers; +#endif #endif #if (BOOT_IMAGE_NUMBER > 1) @@ -128,6 +153,15 @@ boot_read_image_headers(struct boot_loader_state *state, bool require_all, * * Failure to read any headers is a fatal error. */ +#ifdef PM_S1_ADDRESS + /* 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) == 1 && i == 0) { + continue; + } +#endif /* PM_S1_ADDRESS */ if (i > 0 && !require_all) { return 0; } else { @@ -301,6 +335,71 @@ boot_version_cmp(const struct image_version *ver1, } #endif +#if (!defined(MCUBOOT_DIRECT_XIP) && !defined(MCUBOOT_RAM_LOAD)) || \ +defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) +static int +boot_initialize_area(struct boot_loader_state *state, int flash_area) +{ + uint32_t num_sectors = BOOT_MAX_IMG_SECTORS; + boot_sector_t *out_sectors; + uint32_t *out_num_sectors; + int rc; + + num_sectors = BOOT_MAX_IMG_SECTORS; + + if (flash_area == FLASH_AREA_IMAGE_PRIMARY(BOOT_CURR_IMG(state))) { + out_sectors = BOOT_IMG(state, BOOT_PRIMARY_SLOT).sectors; + out_num_sectors = &BOOT_IMG(state, BOOT_PRIMARY_SLOT).num_sectors; + } else if (flash_area == FLASH_AREA_IMAGE_SECONDARY(BOOT_CURR_IMG(state))) { + out_sectors = BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors; + out_num_sectors = &BOOT_IMG(state, BOOT_SECONDARY_SLOT).num_sectors; +#if MCUBOOT_SWAP_USING_SCRATCH + } else if (flash_area == FLASH_AREA_IMAGE_SCRATCH) { + out_sectors = state->scratch.sectors; + out_num_sectors = &state->scratch.num_sectors; +#endif + } else { + return BOOT_EFLASH; + } + +#ifdef MCUBOOT_USE_FLASH_AREA_GET_SECTORS + rc = flash_area_get_sectors(flash_area, &num_sectors, out_sectors); +#else + _Static_assert(sizeof(int) <= sizeof(uint32_t), "Fix needed"); + rc = flash_area_to_sectors(flash_area, (int *)&num_sectors, out_sectors); +#endif /* defined(MCUBOOT_USE_FLASH_AREA_GET_SECTORS) */ + if (rc != 0) { + return rc; + } + *out_num_sectors = num_sectors; + return 0; +} +#endif + +#if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) +static int +boot_read_sectors_recovery(struct boot_loader_state *state) +{ + uint8_t image_index; + int rc; + + image_index = BOOT_CURR_IMG(state); + + rc = boot_initialize_area(state, FLASH_AREA_IMAGE_PRIMARY(image_index)); + if (rc != 0) { + return BOOT_EFLASH; + } + + rc = boot_initialize_area(state, FLASH_AREA_IMAGE_SECONDARY(image_index)); + if (rc != 0) { + /* We need to differentiate from the primary image issue */ + return BOOT_EFLASH_SEC; + } + + return 0; +} +#endif + #if (BOOT_IMAGE_NUMBER > 1) @@ -397,7 +496,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. */ @@ -406,7 +505,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; } @@ -556,35 +658,76 @@ boot_read_image_size(struct boot_loader_state *state, int slot, uint32_t *size) goto done; } - 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); + + if (rc) { + rc = BOOT_EBADIMAGE; + goto done; + } + + off = boot_img_hdr(state, slot)->ih_hdr_size + 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) { + rc = boot_size_protected_tlvs(boot_img_hdr(state, slot), fap, &tmp_size); + + if (rc) { rc = BOOT_EBADIMAGE; goto done; } - if (flash_area_read(fap, off + info.it_tlv_tot, &info, sizeof(info))) { + 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: @@ -617,44 +760,6 @@ boot_write_sz(struct boot_loader_state *state) return elem_sz; } -static int -boot_initialize_area(struct boot_loader_state *state, int flash_area) -{ - uint32_t num_sectors = BOOT_MAX_IMG_SECTORS; - boot_sector_t *out_sectors; - uint32_t *out_num_sectors; - int rc; - - num_sectors = BOOT_MAX_IMG_SECTORS; - - if (flash_area == FLASH_AREA_IMAGE_PRIMARY(BOOT_CURR_IMG(state))) { - out_sectors = BOOT_IMG(state, BOOT_PRIMARY_SLOT).sectors; - out_num_sectors = &BOOT_IMG(state, BOOT_PRIMARY_SLOT).num_sectors; - } else if (flash_area == FLASH_AREA_IMAGE_SECONDARY(BOOT_CURR_IMG(state))) { - out_sectors = BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors; - out_num_sectors = &BOOT_IMG(state, BOOT_SECONDARY_SLOT).num_sectors; -#if MCUBOOT_SWAP_USING_SCRATCH - } else if (flash_area == FLASH_AREA_IMAGE_SCRATCH) { - out_sectors = state->scratch.sectors; - out_num_sectors = &state->scratch.num_sectors; -#endif - } else { - return BOOT_EFLASH; - } - -#ifdef MCUBOOT_USE_FLASH_AREA_GET_SECTORS - rc = flash_area_get_sectors(flash_area, &num_sectors, out_sectors); -#else - _Static_assert(sizeof(int) <= sizeof(uint32_t), "Fix needed"); - rc = flash_area_to_sectors(flash_area, (int *)&num_sectors, out_sectors); -#endif /* defined(MCUBOOT_USE_FLASH_AREA_GET_SECTORS) */ - if (rc != 0) { - return rc; - } - *out_num_sectors = num_sectors; - return 0; -} - /** * Determines the sector layout of both image slots and the scratch area. * This information is necessary for calculating the number of bytes to erase @@ -911,10 +1016,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 @@ -1052,9 +1157,21 @@ boot_validate_slot(struct boot_loader_state *state, int slot, #if defined(MCUBOOT_OVERWRITE_ONLY) && defined(MCUBOOT_DOWNGRADE_PREVENTION) if (slot != BOOT_PRIMARY_SLOT) { /* 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) == 1) { + 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); + } +#else + rc = boot_version_cmp( + &boot_img_hdr(state, BOOT_SECONDARY_SLOT)->ih_ver, + &boot_img_hdr(state, BOOT_PRIMARY_SLOT)->ih_ver); +#endif if (rc < 0 && boot_check_header_erased(state, BOOT_PRIMARY_SLOT)) { BOOT_LOG_ERR("insufficient version in secondary slot"); flash_area_erase(fap, 0, flash_area_get_size(fap)); @@ -1082,6 +1199,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"); @@ -1096,6 +1214,16 @@ 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 (area_id == FLASH_AREA_IMAGE_SECONDARY(2)) { + goto out; + } +#endif if (area_id == FLASH_AREA_IMAGE_SECONDARY(BOOT_CURR_IMG(state))) { const struct flash_area *pri_fa = BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT); struct image_header *secondary_hdr = boot_img_hdr(state, slot); @@ -1108,7 +1236,29 @@ boot_validate_slot(struct boot_loader_state *state, int slot, goto out; } - if (reset_value < pri_fa->fa_off || reset_value> (pri_fa->fa_off + pri_fa->fa_size)) { + uint32_t min_addr, max_addr; + +#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) == 1) { + min_addr = PM_CPUNET_APP_ADDRESS; + max_addr = PM_CPUNET_APP_ADDRESS + PM_CPUNET_APP_SIZE; +#ifdef PM_S1_ADDRESS + } else if (BOOT_CURR_IMG(state) == 0) { + min_addr = PM_S0_ADDRESS; + max_addr = pri_fa->fa_off + pri_fa->fa_size; +#endif + } else +#endif + { + min_addr = pri_fa->fa_off; + max_addr = pri_fa->fa_off + pri_fa->fa_size; + } + + if (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"); @@ -1177,6 +1327,88 @@ boot_update_security_counter(uint8_t image_index, int slot, #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 + +#if (MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) && \ + !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) +/* This configuration is peculiar - the one physical secondary slot is + * mocking two logical secondary + */ +#define SEC_SLOT_PHYSICAL_CNT 1 +#else +#define SEC_SLOT_PHYSICAL_CNT MCUBOOT_IMAGE_NUMBER +#endif + +static uint8_t sec_slot_assignmnet[SEC_SLOT_PHYSICAL_CNT] = {0}; + +static inline void sec_slot_touch(struct boot_loader_state *state) +{ + uint8_t idx = (SEC_SLOT_PHYSICAL_CNT == 1) ? 0 : BOOT_CURR_IMG(state); + + if (SEC_SLOT_VIRGIN == sec_slot_assignmnet[idx]) { + sec_slot_assignmnet[idx] = SEC_SLOT_TOUCHED; + } +} + +static inline void sec_slot_mark_assigned(struct boot_loader_state *state) +{ + uint8_t idx = (SEC_SLOT_PHYSICAL_CNT == 1) ? 0 : BOOT_CURR_IMG(state); + + sec_slot_assignmnet[idx] = SEC_SLOT_ASSIGNED; +} + +/** + * Cleanu 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 < SEC_SLOT_PHYSICAL_CNT; idx++) { + if (SEC_SLOT_TOUCHED == sec_slot_assignmnet[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 %d. image.", idx); + } + } + + if (rc) { + BOOT_LOG_ERR("Can not cleanup secondary slot of %d. image.", idx); + } + } + } +} +#else +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 @@ -1191,6 +1423,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(CONFIG_SOC_NRF5340_CPUAPP) + 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 (reset_addr < primary_fa->fa_off) { +#if defined(CONFIG_SOC_NRF5340_CPUAPP) && defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) + const struct flash_area *nsib_fa; + + /* NSIB upgrade slot */ + rc = flash_area_open((uint32_t)_image_1_primary_slot_id, + &nsib_fa); + + if (rc != 0) { + return BOOT_SWAP_TYPE_FAIL; + } + + /* Image is placed before Primary and within the NSIB slot */ + if (reset_addr > nsib_fa->fa_off + && reset_addr < (nsib_fa->fa_off + nsib_fa->fa_size)) { + /* Set primary to be NSIB upgrade slot */ + BOOT_IMG_AREA(state, 0) = nsib_fa; + owner_nsib[BOOT_CURR_IMG(state)] = true; + } +#else + return BOOT_SWAP_TYPE_NONE; + +#endif + + } else if (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 || CONFIG_SOC_NRF5340_CPUAPP */ swap_type = boot_swap_type_multi(BOOT_CURR_IMG(state)); if (BOOT_IS_UPGRADE(swap_type)) { @@ -1204,7 +1517,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; @@ -1288,6 +1636,9 @@ boot_copy_region(struct boot_loader_state *state, #else (void)state; #endif +#ifdef MCUBOOT_DECOMPRESS_IMAGES + struct image_header *hdr; +#endif TARGET_STATIC uint8_t buf[BUF_SZ] __attribute__((aligned(4))); @@ -1313,6 +1664,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) { @@ -1359,9 +1720,15 @@ boot_copy_region(struct boot_loader_state *state, blk_sz = tlv_off - abs_off; } } - boot_encrypt(BOOT_CURR_ENC(state), source_slot, - (abs_off + idx) - hdr->ih_hdr_size, blk_sz, - blk_off, &buf[idx]); + if (source_slot == 0) { + boot_enc_encrypt(BOOT_CURR_ENC(state), source_slot, + (abs_off + idx) - hdr->ih_hdr_size, blk_sz, + blk_off, &buf[idx]); + } else { + boot_enc_decrypt(BOOT_CURR_ENC(state), source_slot, + (abs_off + idx) - hdr->ih_hdr_size, blk_sz, + blk_off, &buf[idx]); + } } } #endif @@ -1426,7 +1793,7 @@ boot_copy_image(struct boot_loader_state *state, struct boot_status *bs) BOOT_LOG_INF("Image %d upgrade secondary slot -> primary slot", image_index); BOOT_LOG_INF("Erasing the primary slot"); - rc = flash_area_open(FLASH_AREA_IMAGE_PRIMARY(image_index), + rc = flash_area_open(flash_area_get_id(BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT)), &fap_primary_slot); assert (rc == 0); @@ -1684,7 +2051,6 @@ boot_swap_image(struct boot_loader_state *state, struct boot_status *bs) } #endif - /** * Performs a clean (not aborted) image update. * @@ -2119,6 +2485,13 @@ 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 */ + 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(&(BOOT_IMG(state, 0).hdr), @@ -2155,36 +2528,11 @@ check_downgrade_prevention(struct boot_loader_state *state) #endif } -#if !defined(__BOOTSIM__) -static void boot_get_sector_buffers(struct sector_buffer_t *buffers) -{ - /* 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 - * necessary because the gcc option "-fdata-sections" doesn't seem to have - * any effect in older gcc versions (e.g., 4.8.4). - */ - static boot_sector_t primary_slot_sectors[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS]; - static boot_sector_t secondary_slot_sectors[BOOT_IMAGE_NUMBER][BOOT_MAX_IMG_SECTORS]; -#if MCUBOOT_SWAP_USING_SCRATCH - static boot_sector_t scratch_sectors[BOOT_MAX_IMG_SECTORS]; -#endif - - buffers->primary = (boot_sector_t *)&primary_slot_sectors; - buffers->secondary = (boot_sector_t *)&secondary_slot_sectors; -#if MCUBOOT_SWAP_USING_SCRATCH - buffers->scratch = (boot_sector_t *)&scratch_sectors; -#endif -} -#endif - fih_ret context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) { size_t slot; struct boot_status bs; -#if !defined(__BOOTSIM__) - struct sector_buffer_t sector_buffers; -#endif int rc = -1; FIH_DECLARE(fih_rc, FIH_FAILURE); int fa_id; @@ -2211,10 +2559,6 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) (void)has_upgrade; #endif -#if !defined(__BOOTSIM__) - boot_get_sector_buffers(§or_buffers); -#endif - /* Iterate over all the images. By the end of the loop the swap type has * to be determined for each image and all aborted swaps have to be * completed. @@ -2237,9 +2581,9 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) #if !defined(__BOOTSIM__) BOOT_IMG(state, BOOT_PRIMARY_SLOT).sectors = - §or_buffers.primary[image_index]; + sector_buffers.primary[image_index]; BOOT_IMG(state, BOOT_SECONDARY_SLOT).sectors = - §or_buffers.secondary[image_index]; + sector_buffers.secondary[image_index]; #if MCUBOOT_SWAP_USING_SCRATCH state->scratch.sectors = sector_buffers.scratch; #endif @@ -2286,6 +2630,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 @@ -2354,6 +2701,12 @@ context_boot_go(struct boot_loader_state *state, struct boot_rsp *rsp) rc = boot_perform_update(state, &bs); } assert(rc == 0); +#if defined(PM_S1_ADDRESS) && defined(CONFIG_REBOOT) + if (owner_nsib[BOOT_CURR_IMG(state)]) { + sys_reboot(SYS_REBOOT_COLD); + + } +#endif break; case BOOT_SWAP_TYPE_FAIL: @@ -2422,15 +2775,25 @@ 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); - /* 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) == 1; + if (!image_validated_by_nsib) +#endif + { + FIH_CALL(boot_validate_slot, fih_rc, state, BOOT_PRIMARY_SLOT, NULL); + /* 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 @@ -2447,11 +2810,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) { @@ -2894,10 +3262,9 @@ boot_decrypt_and_copy_image_to_sram(struct boot_loader_state *state, * Part of the chunk is encrypted payload */ blk_sz = tlv_off - (bytes_copied); } - boot_encrypt(BOOT_CURR_ENC(state), slot, - (bytes_copied + idx) - hdr->ih_hdr_size, blk_sz, - blk_off, cur_dst); - + boot_enc_decrypt(BOOT_CURR_ENC(state), slot, + (bytes_copied + idx) - hdr->ih_hdr_size, blk_sz, + blk_off, cur_dst); bytes_copied += chunk_sz; } rc = 0; @@ -3436,30 +3803,27 @@ void boot_state_clear(struct boot_loader_state *state) #if defined(MCUBOOT_SERIAL_IMG_GRP_SLOT_INFO) /** * Reads image data to find out the maximum application sizes. Only needs to - * be called in serial recovery mode, as the state informatio is unpopulated + * be called in serial recovery mode, as the state information is unpopulated * at that time */ static void boot_fetch_slot_state_sizes(void) { - struct sector_buffer_t sector_buffers; size_t slot; int rc = -1; int fa_id; int image_index; - boot_get_sector_buffers(§or_buffers); - IMAGES_ITER(BOOT_CURR_IMG(&boot_data)) { int max_size = 0; image_index = BOOT_CURR_IMG(&boot_data); BOOT_IMG(&boot_data, BOOT_PRIMARY_SLOT).sectors = - §or_buffers.primary[image_index]; + sector_buffers.primary[image_index]; BOOT_IMG(&boot_data, BOOT_SECONDARY_SLOT).sectors = - §or_buffers.secondary[image_index]; + sector_buffers.secondary[image_index]; #if MCUBOOT_SWAP_USING_SCRATCH - boot_data.scratch.sectors = sector_buffers.scratch;; + boot_data.scratch.sectors = sector_buffers.scratch; #endif /* Open primary and secondary image areas for the duration @@ -3486,7 +3850,7 @@ static void boot_fetch_slot_state_sizes(void) #endif /* Determine the sector layout of the image slots and scratch area. */ - rc = boot_read_sectors(&boot_data); + rc = boot_read_sectors_recovery(&boot_data); if (rc == 0) { max_size = app_max_size(&boot_data); diff --git a/boot/bootutil/src/swap_move.c b/boot/bootutil/src/swap_move.c index 8999403c5..20caa2b5c 100644 --- a/boot/bootutil/src/swap_move.c +++ b/boot/bootutil/src/swap_move.c @@ -259,6 +259,18 @@ static int app_max_sectors(struct boot_loader_state *state) 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; @@ -306,6 +318,7 @@ boot_slots_compatible(struct boot_loader_state *state) } return 1; +#endif /* PM_S1_ADDRESS */ } #define BOOT_LOG_SWAP_STATE(area, state) \ diff --git a/boot/bootutil/src/swap_scratch.c b/boot/bootutil/src/swap_scratch.c index 24159d1db..66dca83e9 100644 --- a/boot/bootutil/src/swap_scratch.c +++ b/boot/bootutil/src/swap_scratch.c @@ -141,6 +141,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; @@ -191,7 +203,17 @@ boot_slots_compatible(struct boot_loader_state *state) smaller = 1; i++; } else { - sz1 += boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j); + size_t sector_size = boot_img_sector_size(state, BOOT_SECONDARY_SLOT, j); + +#ifdef MCUBOOT_DECOMPRESS_IMAGES + if (sector_size == 0) { + /* Since this supports decompressed images, we can safely exit if slot1 is + * smaller than slot0. + */ + break; + } +#endif + sz1 += sector_size; /* Guarantee that multiple sectors of the primary slot * fit into the secondary slot. */ @@ -228,6 +250,7 @@ boot_slots_compatible(struct boot_loader_state *state) #endif return 1; +#endif /* PM_S1_ADDRESS */ } #define BOOT_LOG_SWAP_STATE(area, state) \ diff --git a/boot/bootutil/zephyr/CMakeLists.txt b/boot/bootutil/zephyr/CMakeLists.txt index 72a6a8638..d5364d025 100644 --- a/boot/bootutil/zephyr/CMakeLists.txt +++ b/boot/bootutil/zephyr/CMakeLists.txt @@ -29,12 +29,18 @@ zephyr_library_link_libraries(MCUBOOT_BOOTUTIL) target_link_libraries(MCUBOOT_BOOTUTIL INTERFACE zephyr_interface) if(CONFIG_BOOT_USE_TINYCRYPT) -target_include_directories(MCUBOOT_BOOTUTIL INTERFACE - ../../../ext/tinycrypt/lib/include -) + target_include_directories(MCUBOOT_BOOTUTIL INTERFACE + ../../../ext/tinycrypt/lib/include + ) +endif() + +if(CONFIG_BOOT_USE_PSA_CRYPTO) + target_include_directories(MCUBOOT_BOOTUTIL INTERFACE + ${ZEPHYR_MBEDTLS_MODULE_DIR}/include + ) endif() -if(CONFIG_BOOT_USE_MBEDTLS) +if(CONFIG_BOOT_USE_MBEDTLS OR CONFIG_BOOT_USE_PSA_CRYPTO AND NOT CONFIG_PSA_CORE_OBERON) zephyr_link_libraries(mbedTLS) endif() endif() diff --git a/boot/zephyr/CMakeLists.txt b/boot/zephyr/CMakeLists.txt index d02f93a40..20d38b49b 100644 --- a/boot/zephyr/CMakeLists.txt +++ b/boot/zephyr/CMakeLists.txt @@ -27,23 +27,20 @@ assert_exists(FIAT_DIR) # Path to mbed-tls' asn1 parser library. set(MBEDTLS_ASN1_DIR "${MCUBOOT_DIR}/ext/mbedtls-asn1") assert_exists(MBEDTLS_ASN1_DIR) -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( @@ -54,6 +51,12 @@ if(EXISTS targets/${BOARD}.h) zephyr_library_compile_definitions(MCUBOOT_TARGET_CONFIG="${BOARD}.h") endif() +if(DEFINED CONFIG_MBEDTLS) + zephyr_library_include_directories( + ${ZEPHYR_MBEDTLS_MODULE_DIR}/include + ) +endif() + # Zephyr port-specific sources. zephyr_library_sources( main.c @@ -105,6 +108,10 @@ zephyr_library_sources( ${BOOT_DIR}/bootutil/src/fault_injection_hardening.c ) +if(DEFINED CONFIG_BOOT_ENCRYPT_X25519 AND DEFINED CONFIG_BOOT_ED25519_PSA) + zephyr_library_sources(${BOOT_DIR}/bootutil/src/encrypted_psa.c) +endif() + if(DEFINED CONFIG_MEASURED_BOOT OR DEFINED CONFIG_BOOT_SHARE_DATA) zephyr_library_sources( ${BOOT_DIR}/bootutil/src/boot_record.c @@ -171,9 +178,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() # Since here we are not using Zephyr's mbedTLS but rather our own, we need @@ -231,19 +240,28 @@ elseif(CONFIG_BOOT_SIGNATURE_TYPE_ED25519 OR CONFIG_BOOT_ENCRYPT_X25519) ${FIAT_DIR}/include/ ) - zephyr_library_sources( - ${FIAT_DIR}/src/curve25519.c - ) + if(NOT CONFIG_BOOT_ED25519_PSA) + zephyr_library_sources( + ${FIAT_DIR}/src/curve25519.c + ) + else() + zephyr_library_sources( + ${MBEDTLS_ASN1_DIR}/src/asn1parse.c + ${BOOT_DIR}/bootutil/src/ed25519_psa.c + ) + endif() endif() -if(CONFIG_BOOT_ENCRYPT_EC256 OR CONFIG_BOOT_ENCRYPT_X25519) - zephyr_library_sources( - ${TINYCRYPT_DIR}/source/aes_encrypt.c - ${TINYCRYPT_DIR}/source/aes_decrypt.c - ${TINYCRYPT_DIR}/source/ctr_mode.c - ${TINYCRYPT_DIR}/source/hmac.c - ${TINYCRYPT_DIR}/source/ecc_dh.c +if(NOT CONFIG_BOOT_ED25519_PSA) + if(CONFIG_BOOT_ENCRYPT_EC256 OR CONFIG_BOOT_ENCRYPT_X25519) + zephyr_library_sources( + ${TINYCRYPT_DIR}/source/aes_encrypt.c + ${TINYCRYPT_DIR}/source/aes_decrypt.c + ${TINYCRYPT_DIR}/source/ctr_mode.c + ${TINYCRYPT_DIR}/source/hmac.c + ${TINYCRYPT_DIR}/source/ecc_dh.c ) + endif() endif() if(CONFIG_BOOT_ENCRYPT_EC256) @@ -252,6 +270,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) @@ -297,6 +321,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(GENERATED_PUBKEY ${ZEPHYR_BINARY_DIR}/autogen-pubkey.c) add_custom_command( OUTPUT ${GENERATED_PUBKEY} @@ -569,3 +600,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 64e23ac6d..c82ed629c 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 @@ -24,6 +27,15 @@ config BOOT_USE_MBEDTLS help Use mbedTLS for crypto primitives. +config BOOT_USE_PSA_CRYPTO + bool + default y if NRF_SECURITY + # This is counter intuitive but that is how PSA heap is enabled. + select MBEDTLS_ENABLE_HEAP + select MBEDTLS_PSA_CRYPTO_C + help + Hidden option set if using PSA crypt for cryptography functionality + config BOOT_USE_TINYCRYPT bool # Hidden option @@ -57,6 +69,58 @@ config NRF_CC310_BL bool default n +if BOOT_USE_PSA_CRYPTO + +config BOOT_PSA_IMG_HASH_ALG_SHA256_DEPENDENCIES + bool + default y if BOOT_IMG_HASH_ALG_SHA256 + select PSA_WANT_ALG_SHA_256 + help + Dependencies for hashing with SHA256 + +config BOOT_ED25519_PSA_DEPENDENCIES + bool + select PSA_WANT_ALG_SHA_256 if BOOT_IMG_HASH_ALG_SHA256 + select PSA_WANT_ALG_SHA_512 + select PSA_WANT_ALG_PURE_EDDSA + select PSA_WANT_ECC_TWISTED_EDWARDS_255 + select PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT + help + Dependencies for ed25519 signature + +if BOOT_ENCRYPT_IMAGE + +config BOOT_X25519_PSA_DEPENDENCIES + bool + select PSA_WANT_ALG_ECDH + select PSA_WANT_ALG_HMAC + select PSA_WANT_ALG_HKDF + select PSA_WANT_ALG_CTR + select PSA_WANT_KEY_TYPE_ECC_KEY_PAIR_IMPORT + select PSA_WANT_KEY_TYPE_DERIVE + select PSA_WANT_KEY_TYPE_AES + select PSA_WANT_ECC_MONTGOMERY_255 + help + Dependencies for x25519 shared-random key encryption and AES + encryption. The PSA_WANT_ALG_CTR and PSA_WANT_KEY_TYPE_AES + enable Counter based block cipher and AES key, and algorithm support, + to use with it; the others are used for shared key decryption + and derivation. + +endif # BOOT_ENCRYPT_IMAGE + +if MBEDTLS_ENABLE_HEAP + +config MBEDTLS_HEAP_SIZE + default 2048 if BOOT_USE_PSA_CRYPTO + help + The PSA internals need to be able to allocate memory for operation + and it uses mbedTLS heap for that. + +endif # MBEDTLS_ENABLE_HEAP + +endif # BOOT_USE_PSA_CRYPTO + menu "MCUBoot settings" config SINGLE_APPLICATION_SLOT @@ -67,19 +131,92 @@ config SINGLE_APPLICATION_SLOT uploading a new application overwrites the one that previously occupied the area. +config BOOT_IMG_HASH_ALG_SHA256_ALLOW + bool + help + Hidden option set by configurations that allow SHA256 + +config BOOT_IMG_HASH_ALG_SHA384_ALLOW + bool + help + Hidden option set by configurations that allow SHA384 + +config BOOT_IMG_HASH_ALG_SHA512_ALLOW + bool + depends on BOOT_USE_PSA_CRYPTO + help + Hidden option set by configurations that allow SHA512 + +config BOOT_IMG_HASH_DIRECTLY_ON_STORAGE + bool "Hash calculation functions access storage through address space" + depends on !BOOT_ENCRYPT_IMAGE + help + When possible to map storage device, at least for read operations, + to address space or RAM area, enabling this option allows hash + calculation functions to directly access the storage through that address + space or using its own DMA. This reduces flash read overhead done + by the MCUboot. + Notes: + - not supported when encrypted images are in use, because calculating + SHA requires image to be decrypted first, which is done to RAM. + - currently only supported on internal storage of devices; this + option will not work with devices that use external storage for + either of image slots. + +choice BOOT_IMG_HASH_ALG + prompt "Selected image hash algorithm" + default BOOT_IMG_HASH_ALG_SHA256 if BOOT_IMG_HASH_ALG_SHA256_ALLOW + default BOOT_IMG_HASH_ALG_SHA384 if BOOT_IMG_HASH_ALG_SHA384_ALLOW + default BOOT_IMG_HASH_ALG_SHA512 if BOOT_IMG_HASH_ALG_SHA512_ALLOW + help + Hash algorithm used for image verification. Selection + here may be limited by other configurations, like for + example selected cryptographic signature. + +config BOOT_IMG_HASH_ALG_SHA256 + bool "SHA256" + depends on BOOT_IMG_HASH_ALG_SHA256_ALLOW + help + SHA256 algorithm + +config BOOT_IMG_HASH_ALG_SHA384 + bool "SHA384" + depends on BOOT_IMG_HASH_ALG_SHA384_ALLOW + help + SHA384 algorithm + +config BOOT_IMG_HASH_ALG_SHA512 + bool "SHA512" + depends on BOOT_IMG_HASH_ALG_SHA512_ALLOW + help + SHA512 algorithm + +endchoice # BOOT_IMG_HASH_ALG + +config BOOT_SIGNATURE_TYPE_PURE_ALLOW + bool + help + Hidden option set by configurations that allow Pure variant, + for example ed25519. The pure variant means that image + signature is calculated over entire image instead of hash + of an image. + choice BOOT_SIGNATURE_TYPE prompt "Signature type" + default BOOT_SIGNATURE_TYPE_ED25519 if BOARD_NRF54L15PDK_NRF54L15_CPUAPP default BOOT_SIGNATURE_TYPE_RSA config BOOT_SIGNATURE_TYPE_NONE bool "No signature; use only hash check" select BOOT_USE_TINYCRYPT + select BOOT_IMG_HASH_ALG_SHA256_ALLOW config BOOT_SIGNATURE_TYPE_RSA bool "RSA signatures" select BOOT_USE_MBEDTLS select MBEDTLS select BOOT_ENCRYPTION_SUPPORT + select BOOT_IMG_HASH_ALG_SHA256_ALLOW if BOOT_SIGNATURE_TYPE_RSA config BOOT_SIGNATURE_TYPE_RSA_LEN @@ -91,6 +228,7 @@ endif config BOOT_SIGNATURE_TYPE_ECDSA_P256 bool "Elliptic curve digital signatures with curve P-256" select BOOT_ENCRYPTION_SUPPORT + select BOOT_IMG_HASH_ALG_SHA256_ALLOW if BOOT_SIGNATURE_TYPE_ECDSA_P256 choice BOOT_ECDSA_IMPLEMENTATION @@ -113,20 +251,63 @@ endif config BOOT_SIGNATURE_TYPE_ED25519 bool "Edwards curve digital signatures using ed25519" - select BOOT_ENCRYPTION_SUPPORT + select BOOT_ENCRYPTION_SUPPORT if !BOOT_SIGNATURE_TYPE_PURE + select BOOT_IMG_HASH_ALG_SHA256_ALLOW if !BOOT_SIGNATURE_TYPE_PURE + # The SHA is used only for key hashing, not for images. + select BOOT_IMG_HASH_ALG_SHA512_ALLOW + select BOOT_SIGNATURE_TYPE_PURE_ALLOW + help + This is ed25519 signature calculated over SHA512 of SHA256 of application + image; that is not completely correct approach as the SHA512 should be + rather directly calculated over an image. + Select BOOT_SIGNATURE_TYPE_PURE to have a PureEdDSA calculating image + signature directly on image, rather than hash of the image. if BOOT_SIGNATURE_TYPE_ED25519 + +config BOOT_SIGNATURE_TYPE_PURE + bool "Use Pure signature of image" + depends on BOOT_SIGNATURE_TYPE_PURE_ALLOW + help + The Pure signature is calculated directly over image rather than + hash of an image. + This is more secure signature, specifically if hardware can do the + verification without need to share key. + Note that this requires that all slots for which signature is to be + verified need to be accessible through memory address space that + cryptography can access. + choice BOOT_ED25519_IMPLEMENTATION prompt "Ecdsa implementation" default BOOT_ED25519_TINYCRYPT + config BOOT_ED25519_TINYCRYPT bool "Use tinycrypt" select BOOT_USE_TINYCRYPT + depends on !NRF_SECURITY + config BOOT_ED25519_MBEDTLS bool "Use mbedTLS" select BOOT_USE_MBEDTLS select MBEDTLS + depends on !NRF_SECURITY + +config BOOT_ED25519_PSA + bool "Use PSA crypto" + select BOOT_USE_PSA_CRYPTO + select BOOT_ED25519_PSA_DEPENDENCIES + select BOOT_X25519_PSA_DEPENDENCIES if BOOT_ENCRYPT_IMAGE + endchoice + +config BOOT_KEY_IMPORT_BYPASS_ASN + bool "Directly access key value without ASN.1 parsing" + help + Originally, public keys compiled into MCUboot were + stored in ASN.1 encoded format. Enabling this option + bypasses the ASN.1 decoding and directly accesses the key + in ASN.1 bitstream; this reduces MCUboot code by removing + the ASN.1 processing. endif endchoice @@ -164,9 +345,13 @@ config MCUBOOT_CLEANUP_ARM_CORE start-up code which can cause a module fault and potentially make the module irrecoverable. +if MBEDTLS + config MBEDTLS_CFG_FILE default "mcuboot-mbedtls-cfg.h" +endif + config BOOT_HW_KEY bool "Use HW key for image verification" default n @@ -382,7 +567,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 @@ -715,6 +900,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 @@ -731,6 +917,10 @@ 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_ENCRYPT_IMAGE && BOOT_UPGRADE_ONLY + depends on UPDATEABLE_IMAGE_NUMBER = 1 + default y help Hidden symbol which should be selected if a system provided decompression support. @@ -738,6 +928,8 @@ if BOOT_DECOMPRESSION_SUPPORT menuconfig BOOT_DECOMPRESSION bool "Decompression" + select NRF_COMPRESS_CLEANUP + select PM_USE_CONFIG_SRAM_SIZE if SOC_NRF54L15_CPUAPP 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 diff --git a/boot/zephyr/Kconfig.serial_recovery b/boot/zephyr/Kconfig.serial_recovery index 7ed9a7d9c..4d31844c1 100644 --- a/boot/zephyr/Kconfig.serial_recovery +++ b/boot/zephyr/Kconfig.serial_recovery @@ -204,7 +204,8 @@ config BOOT_SERIAL_IMG_GRP_IMAGE_STATE config BOOT_SERIAL_IMG_GRP_SLOT_INFO bool "Slot info" - default y if UPDATEABLE_IMAGE_NUMBER > 1 + depends on !BOOT_DIRECT_XIP && !BOOT_RAM_LOAD + default y if (UPDATEABLE_IMAGE_NUMBER > 1) help If y, will include the slot info command which lists what available slots there are in the system. 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/nrf54l15dk_nrf54l15_cpuapp.conf b/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp.conf index 43d8cebe3..8d8eb845f 100644 --- a/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp.conf +++ b/boot/zephyr/boards/nrf54l15dk_nrf54l15_cpuapp.conf @@ -7,4 +7,7 @@ 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 diff --git a/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp.conf b/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp.conf index 43d8cebe3..8d8eb845f 100644 --- a/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp.conf +++ b/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp.conf @@ -7,4 +7,7 @@ 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 diff --git a/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp_ext_flash.conf b/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp_ext_flash.conf new file mode 100644 index 000000000..8fc12e074 --- /dev/null +++ b/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp_ext_flash.conf @@ -0,0 +1,15 @@ +CONFIG_MULTITHREADING=y +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 + +# TODO: below are not yet supported and need fixing +CONFIG_FPROTECT=n + +CONFIG_BOOT_WATCHDOG_FEED=n diff --git a/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp_ext_flash.overlay b/boot/zephyr/boards/nrf54l15pdk_nrf54l15_cpuapp_ext_flash.overlay new file mode 100644 index 000000000..60ee6fe51 --- /dev/null +++ b/boot/zephyr/boards/nrf54l15pdk_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/thingy53_nrf5340_cpuapp.conf b/boot/zephyr/boards/thingy53_nrf5340_cpuapp.conf index f2e42fd64..e10656678 100644 --- a/boot/zephyr/boards/thingy53_nrf5340_cpuapp.conf +++ b/boot/zephyr/boards/thingy53_nrf5340_cpuapp.conf @@ -1,3 +1,74 @@ -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 and QSPI 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_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..37c7e95b1 --- /dev/null +++ b/boot/zephyr/boards/thingy91x_nrf5340_cpuapp.conf @@ -0,0 +1,60 @@ +# 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 diff --git a/boot/zephyr/boards/thingy91x_nrf9151.conf b/boot/zephyr/boards/thingy91x_nrf9151.conf new file mode 100644 index 000000000..2efe1e170 --- /dev/null +++ b/boot/zephyr/boards/thingy91x_nrf9151.conf @@ -0,0 +1,17 @@ +# 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 +CONFIG_MULTITHREADING=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 diff --git a/boot/zephyr/decompression.c b/boot/zephyr/decompression.c new file mode 100644 index 000000000..f958a0609 --- /dev/null +++ b/boot/zephyr/decompression.c @@ -0,0 +1,1180 @@ +/* + * 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 + +/* 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; + + if (BOOT_IMG_AREA(state, BOOT_PRIMARY_SLOT) == NULL) { + opened_flash_area = true; + } + + primary_fa_id = flash_area_id_from_multi_image_slot(BOOT_CURR_IMG(state), BOOT_PRIMARY_SLOT); + 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; +} + +int bootutil_img_hash_decompress(struct enc_key_data *enc_state, int image_index, + 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; + 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; + uint8_t flash_erased_value; + + 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; + } + + rc = compression_lzma->init(NULL); + rc = compression_arm_thumb->init(NULL); + + if (rc) { + 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; + } + + modified_hdr.ih_flags &= ~COMPRESSIONFLAGS; + modified_hdr.ih_img_size = decompressed_image_size; + + /* 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); + flash_erased_value = flash_area_erased_val(fap); + memset(tmp_buf, flash_erased_value, tmp_buf_sz); + + 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; + } + + 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; + + while (read_pos < hdr->ih_img_size) { + uint32_t copy_size = hdr->ih_img_size - read_pos; + 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; + } + + /* 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); + } + + if ((read_pos + tmp_off + chunk_size) >= hdr->ih_img_size) { + 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; + uint32_t output_size_arm_thumb_total = 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; + } else if (current_size != offset_arm_thumb) { + BOOT_LOG_ERR("Decompression expected offset mismatch: %d vs %d", + current_size, offset_arm_thumb); + rc = BOOT_EBADSTATUS; + goto finish; + } + + bootutil_sha_update(&sha_ctx, output_arm_thumb, output_size_arm_thumb); + output_size_arm_thumb_total += output_size_arm_thumb; + processed_size += current_size; + } + + if (output_size != output_size_arm_thumb_total) { + BOOT_LOG_ERR("Decompression expected output_size mismatch: %d vs %d", + output_size, output_size_arm_thumb); + rc = BOOT_EBADSTATUS; + goto finish; + } + } else { + bootutil_sha_update(&sha_ctx, output, output_size); + } + } + + tmp_off += offset; + } + + read_pos += copy_size; + } + + /* 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); + + 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) { + /* 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) { + /* 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) { + /* 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)) { + 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; +} + +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[CONFIG_BOOT_DECOMPRESSION_BUFFER_SIZE] __attribute__((aligned(4))); + TARGET_STATIC struct image_header modified_hdr; + + hdr = boot_img_hdr(state, BOOT_SECONDARY_SLOT); + + /* 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; + } + + 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; + } + + rc = compression_lzma->init(NULL); + rc = compression_arm_thumb->init(NULL); + + if (rc) { + BOOT_LOG_ERR("Decompression library fatal error"); + rc = BOOT_EBADSTATUS; + goto finish; + } + + write_alignment = flash_area_align(fap_dst); + + 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; + } + + modified_hdr.ih_flags &= ~COMPRESSIONFLAGS; + modified_hdr.ih_img_size = decompressed_image_size; + + /* 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 */ + while (pos < hdr->ih_img_size) { + uint32_t copy_size = hdr->ih_img_size - pos; + 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; + } + + /* 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); + } + + if ((pos + tmp_off + chunk_size) >= hdr->ih_img_size) { + 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 = (sizeof(decomp_buf) - decomp_buf_size); + + if (data_size > output_size) { + data_size = output_size; + } + + 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 == sizeof(decomp_buf)) { + if (hdr->ih_flags & IMAGE_F_COMPRESSED_ARM_THUMB_FLT) { + /* Run this through the ARM thumb filter */ + uint32_t offset_arm_thumb = 0; + uint32_t output_size_arm_thumb = 0; + uint32_t processed_size = 0; + uint8_t *output_arm_thumb = NULL; + + while (processed_size < sizeof(decomp_buf)) { + uint32_t current_size = sizeof(decomp_buf); + 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) == + sizeof(decomp_buf)) { + arm_thumb_last_packet = true; + } + + rc = compression_arm_thumb->decompress(NULL, + &decomp_buf[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; + } + + memcpy(&decomp_buf[processed_size], output_arm_thumb, current_size); + processed_size += current_size; + } + } + + rc = flash_area_write(fap_dst, (off_dst + hdr->ih_hdr_size + write_pos), + decomp_buf, sizeof(decomp_buf)); + + 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), sizeof(decomp_buf), + fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + + write_pos += sizeof(decomp_buf); + decomp_buf_size = 0; + } + } + + tmp_off += offset; + } + + pos += copy_size; + } + + /* 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, sizeof(decomp_buf_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, sizeof(decomp_buf_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_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), sizeof(decomp_buf_size), + fap_dst->fa_id, rc); + rc = BOOT_EFLASH; + goto finish; + } + + write_pos += decomp_buf_size; + decomp_buf_size = 0; + } + +finish: + 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/flash_map_extended.c b/boot/zephyr/flash_map_extended.c index 4631da75b..d0744afbd 100644 --- a/boot/zephyr/flash_map_extended.c +++ b/boot/zephyr/flash_map_extended.c @@ -141,12 +141,8 @@ int flash_area_sector_from_off(off_t off, struct flash_sector *sector) uint8_t flash_area_get_device_id(const struct flash_area *fa) { -#if defined(CONFIG_ARM) - return fa->fa_id; -#else - (void)fa; - return FLASH_DEVICE_ID; -#endif + (void)fa; + return FLASH_DEVICE_ID; } #define ERASED_VAL 0xff diff --git a/boot/zephyr/include/compression/decompression.h b/boot/zephyr/include/compression/decompression.h new file mode 100644 index 000000000..f8a676ac5 --- /dev/null +++ b/boot/zephyr/include/compression/decompression.h @@ -0,0 +1,104 @@ +/* + * 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 enc_state Not currently used, set to NULL. + * @param image_index Image number. + * @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 enc_key_data *enc_state, int image_index, + 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-mbedtls-cfg.h b/boot/zephyr/include/mcuboot-mbedtls-cfg.h index 2bab537d7..a46fbb09f 100644 --- a/boot/zephyr/include/mcuboot-mbedtls-cfg.h +++ b/boot/zephyr/include/mcuboot-mbedtls-cfg.h @@ -23,7 +23,7 @@ #if defined(CONFIG_BOOT_SIGNATURE_TYPE_RSA) || defined(CONFIG_BOOT_ENCRYPT_RSA) #include "config-rsa.h" -#elif defined(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256) || \ +#elif defined(CONFIG_BOOT_USE_PSA_CRYPTO) || defined(CONFIG_BOOT_SIGNATURE_TYPE_ECDSA_P256) || \ defined(CONFIG_BOOT_ENCRYPT_EC256) || \ (defined(CONFIG_BOOT_ENCRYPT_X25519) && !defined(CONFIG_BOOT_SIGNATURE_TYPE_ED25519)) #include "config-asn1.h" diff --git a/boot/zephyr/include/mcuboot_config/mcuboot_config.h b/boot/zephyr/include/mcuboot_config/mcuboot_config.h index 0891a4b11..bc5188138 100644 --- a/boot/zephyr/include/mcuboot_config/mcuboot_config.h +++ b/boot/zephyr/include/mcuboot_config/mcuboot_config.h @@ -34,15 +34,28 @@ # error "One crypto library implementation allowed at a time." #endif +#if defined(CONFIG_BOOT_KEY_IMPORT_BYPASS_ASN) +#define MCUBOOT_KEY_IMPORT_BYPASS_ASN +#endif + #ifdef CONFIG_BOOT_USE_MBEDTLS #define MCUBOOT_USE_MBED_TLS #elif defined(CONFIG_BOOT_USE_TINYCRYPT) #define MCUBOOT_USE_TINYCRYPT #elif defined(CONFIG_BOOT_USE_CC310) #define MCUBOOT_USE_CC310 -#ifdef CONFIG_BOOT_USE_NRF_CC310_BL -#define MCUBOOT_USE_NRF_CC310_BL +#elif defined(CONFIG_MBEDTLS_PSA_CRYPTO_CLIENT) +#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 +#define MCUBOOT_SHA512 +#endif + +#ifdef CONFIG_BOOT_IMG_HASH_ALG_SHA256 +#define MCUBOOT_SHA256 #endif /* Zephyr, regardless of C library used, provides snprintf */ @@ -131,6 +144,17 @@ #define MCUBOOT_DECOMPRESS_IMAGES #endif +/* Invoke hashing functions directly on storage. This requires for device + * to be able to map storage to address space or RAM. + */ +#ifdef CONFIG_BOOT_IMG_HASH_DIRECTLY_ON_STORAGE +#define MCUBOOT_HASH_STORAGE_DIRECTLY +#endif + +#ifdef CONFIG_BOOT_SIGNATURE_TYPE_PURE +#define MCUBOOT_SIGN_PURE +#endif + #ifdef CONFIG_BOOT_BOOTSTRAP #define MCUBOOT_BOOTSTRAP 1 #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..db60ddd03 --- /dev/null +++ b/boot/zephyr/include/sysflash/pm_sysflash.h @@ -0,0 +1,100 @@ +/* + * 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 + +#if (MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) +/* If B0 is present then two bootloaders are present, and we must use + * a single secondary slot for both primary slots. + */ +extern uint32_t _image_1_primary_slot_id[]; +#endif /* (MCUBOOT_IMAGE_NUMBER == 2 && defined(PM_B0_ADDRESS) */ + +#if (MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) && \ + !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) + +#define FLASH_AREA_IMAGE_PRIMARY(x) \ + ((x == 0) ? \ + PM_MCUBOOT_PRIMARY_ID : \ + (x == 1) ? \ + (uint32_t)_image_1_primary_slot_id : \ + 255 ) + +#define FLASH_AREA_IMAGE_SECONDARY(x) \ + ((x == 0) ? \ + PM_MCUBOOT_SECONDARY_ID: \ + (x == 1) ? \ + PM_MCUBOOT_SECONDARY_ID: \ + 255 ) + +#else /* MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) && \ + * !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) + */ + +/* 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 + +#if (MCUBOOT_IMAGE_NUMBER == 1) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS +#elif (MCUBOOT_IMAGE_NUMBER == 2) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS, \ + FLASH_AREA_IMAGE_1_SLOTS +#elif (MCUBOOT_IMAGE_NUMBER == 3) +#define ALL_AVAILABLE_SLOTS FLASH_AREA_IMAGE_0_SLOTS, \ + FLASH_AREA_IMAGE_1_SLOTS, \ + FLASH_AREA_IMAGE_2_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 + }; + 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 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 + +#endif /* MCUBOOT_IMAGE_NUMBER == 2) && defined(PM_B0_ADDRESS) && \ + * !defined(CONFIG_NRF53_MULTI_IMAGE_UPDATE) + */ + +#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 */ + +#endif /* __PM_SYSFLASH_H__ */ diff --git a/boot/zephyr/include/sysflash/sysflash.h b/boot/zephyr/include/sysflash/sysflash.h index 1952950b9..f231c3d02 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 9bbfd4b19..40287d515 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 @@ -45,4 +47,6 @@ #error "Target support is incomplete; cannot build mcuboot." #endif +#endif /* ifndef USE_PARTITION_MANAGER */ + #endif /* H_TARGETS_TARGET_ */ diff --git a/boot/zephyr/main.c b/boot/zephyr/main.c index ccabc2857..b2d0ea8a1 100644 --- a/boot/zephyr/main.c +++ b/boot/zephyr/main.c @@ -69,6 +69,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" @@ -87,6 +91,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. */ @@ -129,6 +137,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); @@ -157,26 +174,16 @@ static void do_boot(struct boot_rsp *rsp) /* Get ram address for image */ vt = (struct arm_vector_table *)(rsp->br_hdr->ih_load_addr + rsp->br_hdr->ih_hdr_size); #else + uintptr_t flash_base; int rc; - const struct flash_area *fap; - static uint32_t dst[2]; /* Jump to flash image */ - rc = flash_area_open(rsp->br_flash_dev_id, &fap); - assert(rc == 0); - - rc = flash_area_read(fap, rsp->br_hdr->ih_hdr_size, dst, sizeof(dst)); + rc = flash_device_base(rsp->br_flash_dev_id, &flash_base); assert(rc == 0); -#ifndef CONFIG_ASSERT - /* Enter a lock up as asserts are disabled */ - if (rc != 0) { - while (1); - } -#endif - - flash_area_close(fap); - vt = (struct arm_vector_table *)dst; + vt = (struct arm_vector_table *)(flash_base + + rsp->br_image_off + + rsp->br_hdr->ih_hdr_size); #endif if (IS_ENABLED(CONFIG_SYSTEM_TIMER_HAS_DISABLE_SUPPORT)) { @@ -187,6 +194,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 */ @@ -552,7 +587,33 @@ 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) + pcd_lock_ram(); +#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..051705ec9 --- /dev/null +++ b/boot/zephyr/nrf_cleanup.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#if defined(NRF_RTC0) || defined(NRF_RTC1) || defined(NRF_RTC2) + #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(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 +}; +#endif + +static void nrf_cleanup_clock(void) +{ + nrf_clock_int_disable(NRF_CLOCK, 0xFFFFFFFF); +} + +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(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); + +#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 + nrf_cleanup_clock(); +} + +#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..eabe3d08e --- /dev/null +++ b/boot/zephyr/pm.yml @@ -0,0 +1,87 @@ +#include + +mcuboot: + size: CONFIG_PM_PARTITION_SIZE_MCUBOOT + placement: + before: [mcuboot_primary] + +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_MULTI_IMAGE_UPDATE */ + +#if (CONFIG_NRF53_MULTI_IMAGE_UPDATE) +mcuboot_secondary_1: + region: external_flash + 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 851c133ec..e4f7d9030 100644 --- a/boot/zephyr/prj.conf +++ b/boot/zephyr/prj.conf @@ -19,6 +19,7 @@ CONFIG_BOOT_BOOTSTRAP=n # CONFIG_TINYCRYPT_SHA256 is not set CONFIG_FLASH=y +CONFIG_FPROTECT=y ### Various Zephyr boards enable features that we don't want. # CONFIG_BT is not set @@ -35,3 +36,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..55d4c6167 --- /dev/null +++ b/boot/zephyr/prj_minimal.conf @@ -0,0 +1,40 @@ +# +# 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_CLOCK_CONTROL=n +CONFIG_CONSOLE=n +CONFIG_CONSOLE_HANDLER=n +CONFIG_GPIO=n +CONFIG_KERNEL_MEM_POOL=n +CONFIG_LOG=n +CONFIG_MINIMAL_LIBC_CALLOC=n +CONFIG_MINIMAL_LIBC_MALLOC=n +CONFIG_MINIMAL_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/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 5c4732b53..84811806d 100644 --- a/scripts/imgtool/image.py +++ b/scripts/imgtool/image.py @@ -20,7 +20,15 @@ Image signing and management. """ +from . import version as versmod +from .boot_record import create_sw_component_data +import click +import copy +from enum import Enum +import array +from intelhex import IntelHex import hashlib +import array import os.path import struct from enum import Enum @@ -40,6 +48,8 @@ from .boot_record import create_sw_component_data from .keys import rsa, ecdsa, x25519 +from collections import namedtuple + IMAGE_MAGIC = 0x96f3b83d IMAGE_HEADER_SIZE = 32 BIN_EXT = "bin" @@ -58,6 +68,9 @@ 'NON_BOOTABLE': 0x0000010, 'RAM_LOAD': 0x0000020, 'ROM_FIXED': 0x0000100, + 'COMPRESSED_LZMA1': 0x0000200, + 'COMPRESSED_LZMA2': 0x0000400, + 'COMPRESSED_ARM_THUMB': 0x0000800, } TLV_VALUES = { @@ -65,6 +78,7 @@ 'PUBKEY': 0x02, 'SHA256': 0x10, 'SHA384': 0x11, + 'SHA512': 0x12, 'RSA2048': 0x20, 'ECDSASIG': 0x22, 'RSA3072': 0x23, @@ -76,6 +90,9 @@ 'DEPENDENCY': 0x40, 'SEC_CNT': 0x50, 'BOOT_RECORD': 0x60, + 'DECOMP_SIZE': 0x70, + 'DECOMP_SHA': 0x71, + 'DECOMP_SIGNATURE': 0x72, } TLV_SIZE = 4 @@ -135,11 +152,73 @@ def get(self): return header + bytes(self.buf) +SHAAndAlgT = namedtuple('SHAAndAlgT', ['sha', 'alg']) + +TLV_SHA_TO_SHA_AND_ALG = { + TLV_VALUES['SHA256'] : SHAAndAlgT('256', hashlib.sha256), + TLV_VALUES['SHA384'] : SHAAndAlgT('384', hashlib.sha384), + TLV_VALUES['SHA512'] : SHAAndAlgT('512', hashlib.sha512), +} + + +USER_SHA_TO_ALG_AND_TLV = { + 'auto' : (hashlib.sha256, 'SHA256'), + '256' : (hashlib.sha256, 'SHA256'), + '384' : (hashlib.sha384, 'SHA384'), + '512' : (hashlib.sha512, 'SHA512') +} + + +def is_sha_tlv(tlv): + return tlv in TLV_SHA_TO_SHA_AND_ALG.keys() + + +def tlv_sha_to_sha(tlv): + return TLV_SHA_TO_SHA_AND_ALG[tlv].sha + + +# Auto selecting hash algorithm for type(key) +ALLOWED_KEY_SHA = { + keys.ECDSA384P1 : ['384'], + keys.ECDSA384P1Public : ['384'], + keys.ECDSA256P1 : ['256'], + keys.RSA : ['256'], + # This two are set to 256 for compatibility, the right would be 512 + keys.Ed25519 : ['256', '512'], + keys.X25519 : ['256', '512'] +} + +def key_and_user_sha_to_alg_and_tlv(key, user_sha): + """Matches key and user requested sha to sha alogrithm and TLV name. + + The returned tuple will contain hash functions and TVL name. + The function is designed to succeed or completely fail execution, + as providing incorrect pair here basically prevents doing + any more work. + """ + if key is None: + # If key is none, we allow whatever user has selected for sha + return USER_SHA_TO_ALG_AND_TLV[user_sha] + + # If key is not None, then we have to filter hash to only allowed + allowed = None + try: + allowed = ALLOWED_KEY_SHA[type(key)] + except KeyError: + raise click.UsageError("Colud not find allowed hash algorithms for {}" + .format(type(key))) + if user_sha == 'auto': + return USER_SHA_TO_ALG_AND_TLV[allowed[0]] + + if user_sha in allowed: + return USER_SHA_TO_ALG_AND_TLV[user_sha] + + raise click.UsageError("Key {} can not be used with --sha {}; allowed sha are one of {}" + .format(key.sig_type(), user_sha, allowed)) + + def get_digest(tlv_type, hash_region): - if tlv_type == TLV_VALUES["SHA384"]: - sha = hashlib.sha384() - elif tlv_type == TLV_VALUES["SHA256"]: - sha = hashlib.sha256() + sha = TLV_SHA_TO_SHA_AND_ALG[tlv_type].alg() sha.update(hash_region) return sha.digest() @@ -147,9 +226,16 @@ def get_digest(tlv_type, hash_region): def tlv_matches_key_type(tlv_type, key): """Check if provided key matches to TLV record in the image""" - return (key is None or - type(key) == keys.ECDSA384P1 and tlv_type == TLV_VALUES["SHA384"] or - type(key) != keys.ECDSA384P1 and tlv_type == TLV_VALUES["SHA256"]) + try: + # We do not need the result here, and the key_and_user_sha_to_alg_and_tlv + # will either succeed finding match or rise exception, so on success we + # return True, on exception we return False. + _, _ = key_and_user_sha_to_alg_and_tlv(key, tlv_sha_to_sha(tlv_type)) + return True + except: + pass + + return False class Image: @@ -165,6 +251,9 @@ def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE, if load_addr and rom_fixed: raise click.UsageError("Can not set rom_fixed and load_addr at the same time") + self.image_hash = None + self.image_size = None + self.signature = None self.version = version or versmod.decode_version("0") self.header_size = header_size self.pad_header = pad_header @@ -180,6 +269,7 @@ def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE, self.rom_fixed = rom_fixed self.erased_val = 0xff if erased_val is None else int(erased_val, 0) self.payload = [] + self.infile_data = [] self.enckey = None self.save_enctlv = save_enctlv self.enctlv_len = 0 @@ -234,13 +324,31 @@ def load(self, path): try: if ext == INTEL_HEX_EXT: ih = IntelHex(path) - self.payload = ih.tobinarray() + self.infile_data = ih.tobinarray() + self.payload = copy.copy(self.infile_data) self.base_addr = ih.minaddr() else: with open(path, 'rb') as f: - self.payload = f.read() + self.infile_data = f.read() + 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: + 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() + + def load_compressed(self, data, compression_header): + """Load an image from buffer""" + self.payload = compression_header + data + self.image_size = len(self.payload) # Add the image header if needed. if self.pad_header and self.header_size > 0: @@ -335,18 +443,15 @@ def ecies_hkdf(self, enckey, plainkey): return cipherkey, ciphermac, pubk def create(self, key, public_key_format, enckey, dependencies=None, - sw_type=None, custom_tlvs=None, encrypt_keylen=128, clear=False, - fixed_sig=None, pub_key=None, vector_to_sign=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'): self.enckey = enckey - # Check what hashing algorithm should be used - if (key and isinstance(key, ecdsa.ECDSA384P1) - or pub_key and isinstance(pub_key, ecdsa.ECDSA384P1Public)): - hash_algorithm = hashlib.sha384 - hash_tlv = "SHA384" - else: - hash_algorithm = hashlib.sha256 - hash_tlv = "SHA256" + # key decides on sha, then pub_key; of both are none default is used + check_key = key if key is not None else pub_key + hash_algorithm, hash_tlv = key_and_user_sha_to_alg_and_tlv(check_key, user_sha) + # Calculate the hash of the public key if key is not None: pub = key.get_public_bytes() @@ -402,6 +507,9 @@ def create(self, key, public_key_format, enckey, dependencies=None, dependencies_num = len(dependencies[DEP_IMAGES_KEY]) protected_tlv_size += (dependencies_num * 16) + if compression_tlvs is not None: + for value in compression_tlvs.values(): + protected_tlv_size += TLV_SIZE + len(value) if custom_tlvs is not None: for value in custom_tlvs.values(): protected_tlv_size += TLV_SIZE + len(value) @@ -423,11 +531,17 @@ def create(self, key, public_key_format, enckey, dependencies=None, else: self.payload.extend(pad) + compression_flags = 0x0 + if compression_tlvs is not None: + if compression_type in ["lzma2", "lzma2armthumb"]: + compression_flags = IMAGE_F['COMPRESSED_LZMA2'] + if compression_type == "lzma2armthumb": + compression_flags |= IMAGE_F['COMPRESSED_ARM_THUMB'] # This adds the header to the payload as well if encrypt_keylen == 256: - self.add_header(enckey, protected_tlv_size, 256) + self.add_header(enckey, protected_tlv_size, compression_flags, 256) else: - self.add_header(enckey, protected_tlv_size) + self.add_header(enckey, protected_tlv_size, compression_flags) prot_tlv = TLV(self.endian, TLV_PROT_INFO_MAGIC) @@ -457,6 +571,9 @@ def create(self, key, public_key_format, enckey, dependencies=None, ) prot_tlv.add('DEPENDENCY', payload) + if compression_tlvs is not None: + for tag, value in compression_tlvs.items(): + prot_tlv.add(tag, value) if custom_tlvs is not None: for tag, value in custom_tlvs.items(): prot_tlv.add(tag, value) @@ -466,12 +583,16 @@ def create(self, key, public_key_format, enckey, dependencies=None, tlv = TLV(self.endian) - # Note that ecdsa wants to do the hashing itself, which means - # we get to hash it twice. + # These signature is done over sha of image. In case of + # EC signatures so called Pure algorithm, designated to be run + # over entire message is used with sha of image as message, + # so, for example, in case of ED25519 we have here SHAxxx-ED25519-SHA512. sha = hash_algorithm() sha.update(self.payload) digest = sha.digest() + message = digest; tlv.add(hash_tlv, digest) + self.image_hash = digest if vector_to_sign == 'payload': # Stop amending data to the image @@ -499,7 +620,7 @@ def create(self, key, public_key_format, enckey, dependencies=None, sig = key.sign(bytes(self.payload)) else: print(os.path.basename(__file__) + ": sign the digest") - sig = key.sign_digest(digest) + sig = key.sign_digest(message) tlv.add(key.sig_tlv(), sig) self.signature = sig elif fixed_sig is not None and key is None: @@ -551,10 +672,16 @@ def create(self, key, public_key_format, enckey, dependencies=None, self.check_trailer() + def get_struct_endian(self): + return STRUCT_ENDIAN_DICT[self.endian] + def get_signature(self): return self.signature - def add_header(self, enckey, protected_tlv_size, aes_length=128): + def get_infile_data(self): + return self.infile_data + + def add_header(self, enckey, protected_tlv_size, compression_flags, aes_length=128): """Install the image header.""" flags = 0 @@ -592,7 +719,7 @@ def add_header(self, enckey, protected_tlv_size, aes_length=128): protected_tlv_size, # TLV Info header + # Protected TLVs len(self.payload) - self.header_size, # ImageSz - flags, + flags | compression_flags, self.version.major, self.version.minor or 0, self.version.revision or 0, @@ -678,7 +805,7 @@ def verify(imgfile, key): while tlv_off < tlv_end: tlv = b[tlv_off:tlv_off + TLV_SIZE] tlv_type, _, tlv_len = struct.unpack('BBH', tlv) - if tlv_type == TLV_VALUES["SHA256"] or tlv_type == TLV_VALUES["SHA384"]: + if is_sha_tlv(tlv_type): if not tlv_matches_key_type(tlv_type, key): return VerifyResult.KEY_MISMATCH, None, None off = tlv_off + TLV_SIZE diff --git a/scripts/imgtool/main.py b/scripts/imgtool/main.py index cc2cf9c58..94952a838 100755 --- a/scripts/imgtool/main.py +++ b/scripts/imgtool/main.py @@ -22,6 +22,10 @@ import getpass import imgtool.keys as keys import sys +import struct +import os +import lzma +import hashlib import base64 from imgtool import image, imgtool_version from imgtool.version import decode_version @@ -29,6 +33,13 @@ from .keys import ( RSAUsageError, ECDSAUsageError, Ed25519UsageError, X25519UsageError) +comp_default_dictsize=131072 +comp_default_pb=2 +comp_default_lc=3 +comp_default_lp=1 +comp_default_preset=9 + + MIN_PYTHON_VERSION = (3, 6) if sys.version_info < MIN_PYTHON_VERSION: sys.exit("Python %s.%s or newer is required by imgtool." @@ -72,6 +83,7 @@ def gen_x25519(keyfile, passwd): 'x25519': gen_x25519, } valid_formats = ['openssl', 'pkcs8'] +valid_sha = [ 'auto', '256', '384', '512' ] def load_signature(sigfile): @@ -299,6 +311,14 @@ def get_dependencies(ctx, param, value): dependencies[image.DEP_VERSIONS_KEY] = versions return dependencies +def create_lzma2_header(dictsize, pb, lc, lp): + header = bytearray() + for i in range(0, 40): + if dictsize <= ((2 | ((i) & 1)) << int((i) / 2 + 11)): + header.append(i) + break + header.append( ( pb * 5 + lp) * 9 + lc) + return header class BasedIntParamType(click.ParamType): name = 'integer' @@ -342,6 +362,11 @@ def convert(self, value, param, ctx): type=click.Choice(['128', '256']), help='When encrypting the image using AES, select a 128 bit or ' '256 bit key len.') +@click.option('--compression', default='disabled', + type=click.Choice(['disabled', 'lzma2', 'lzma2armthumb']), + help='Enable image compression using specified type. ' + 'Will fall back without image compression automatically ' + 'if the compression increases the image size.') @click.option('-c', '--clear', required=False, is_flag=True, default=False, help='Output a non-encrypted image with encryption capabilities,' 'so it can be installed in the primary slot, and encrypted ' @@ -401,6 +426,9 @@ def convert(self, value, param, ctx): @click.option('--sig-out', metavar='filename', help='Path to the file to which signature will be written. ' 'The image signature will be encoded as base64 formatted string') +@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('--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 ' @@ -410,10 +438,11 @@ def convert(self, value, param, ctx): .hex extension, otherwise binary format is used''') def sign(key, public_key_format, align, version, pad_sig, header_size, pad_header, slot_size, pad, confirm, max_sectors, overwrite_only, - endian, encrypt_keylen, encrypt, 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, vector_to_sign, non_bootable): + 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, vector_to_sign, + non_bootable): if confirm: # Confirmed but non-padded images don't make much sense, because @@ -427,6 +456,7 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, erased_val=erased_val, save_enctlv=save_enctlv, security_counter=security_counter, max_align=max_align, non_bootable=non_bootable) + compression_tlvs = {} img.load(infile) key = load_key(key) if key else None enckey = load_key(encrypt) if encrypt else None @@ -480,10 +510,51 @@ def sign(key, public_key_format, align, version, pad_sig, header_size, } img.create(key, public_key_format, enckey, dependencies, boot_record, - custom_tlvs, int(encrypt_keylen), clear, baked_signature, + custom_tlvs, compression_tlvs, int(encrypt_keylen), clear, + baked_signature, pub_key, vector_to_sign, user_sha) + + if compression in ["lzma2", "lzma2armthumb"]: + compressed_img = image.Image(version=decode_version(version), + header_size=header_size, pad_header=pad_header, + pad=pad, confirm=confirm, align=int(align), + slot_size=slot_size, max_sectors=max_sectors, + overwrite_only=overwrite_only, endian=endian, + load_addr=load_addr, rom_fixed=rom_fixed, + erased_val=erased_val, save_enctlv=save_enctlv, + security_counter=security_counter, max_align=max_align) + compression_filters = [ + {"id": lzma.FILTER_LZMA2, "preset": comp_default_preset, + "dict_size": comp_default_dictsize, "lp": comp_default_lp, + "lc": comp_default_lc} + ] + 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()) + compressed_size = len(compressed_data) + print(f"compressed image size: {compressed_size} bytes") + print(f"original image size: {uncompressed_size} bytes") + compression_tlvs["DECOMP_SIZE"] = struct.pack( + img.get_struct_endian() + 'L', img.image_size) + compression_tlvs["DECOMP_SHA"] = img.image_hash + compression_tlvs_size = len(compression_tlvs["DECOMP_SIZE"]) + compression_tlvs_size += len(compression_tlvs["DECOMP_SHA"]) + if img.get_signature(): + compression_tlvs["DECOMP_SIGNATURE"] = img.get_signature() + compression_tlvs_size += len(compression_tlvs["DECOMP_SIGNATURE"]) + if (compressed_size + compression_tlvs_size) < uncompressed_size: + compression_header = create_lzma2_header( + dictsize = comp_default_dictsize, pb = comp_default_pb, + lc = comp_default_lc, lp = comp_default_lp) + compressed_img.load_compressed(compressed_data, compression_header) + compressed_img.base_addr = img.base_addr + 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) + img = compressed_img img.save(outfile, hex_addr) - if sig_out is not None: new_signature = img.get_signature() save_signature(sig_out, new_signature) diff --git a/zephyr/module.yml b/zephyr/module.yml index 014a21956..9360dbf70 100644 --- a/zephyr/module.yml +++ b/zephyr/module.yml @@ -1,5 +1,6 @@ samples: - boot/zephyr build: - cmake: ./boot/bootutil/zephyr + cmake-ext: True + kconfig-ext: True sysbuild-cmake: boot/zephyr/sysbuild