Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions boot/bootutil/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ target_sources(bootutil
src/image_rsa.c
src/image_validate.c
src/loader.c
src/loader_manifest_xip.c
src/swap_misc.c
src/swap_move.c
src/swap_scratch.c
Expand Down
1 change: 1 addition & 0 deletions boot/bootutil/include/bootutil/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ extern "C" {
#define IMAGE_TLV_COMP_DEC_SIZE 0x73 /* Compressed decrypted image size */
#define IMAGE_TLV_UUID_VID 0x74 /* Vendor unique identifier */
#define IMAGE_TLV_UUID_CID 0x75 /* Device class unique identifier */
#define IMAGE_TLV_MANIFEST 0x76 /* Transaction manifest */
/*
* vendor reserved TLVs at xxA0-xxFF,
* where xx denotes the upper byte
Expand Down
99 changes: 99 additions & 0 deletions boot/bootutil/include/bootutil/mcuboot_manifest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
* Copyright (c) 2025 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/

#ifndef __MCUBOOT_MANIFEST_H__
#define __MCUBOOT_MANIFEST_H__

/**
* @file mcuboot_manifest.h
*
* @note This file is only used when MCUBOOT_MANIFEST_UPDATES is enabled.
*/

#include <stdint.h>
#include "bootutil/bootutil.h"
#include "bootutil/crypto/sha.h"

#ifndef __packed
#define __packed __attribute__((__packed__))
#endif

#ifdef __cplusplus
extern "C" {
#endif

/** Manifest structure for image updates. */
struct mcuboot_manifest {
uint32_t format;
uint32_t image_count;
/* Skip a digest of the MCUBOOT_MANIFEST_IMAGE_NUMBER image. */
uint8_t image_hash[MCUBOOT_IMAGE_NUMBER - 1][IMAGE_HASH_SIZE];
} __packed;

/**
* @brief Check if the specified manifest has the correct format.
*
* @param[in] manifest The reference to the manifest structure.
*
* @return true on success.
*/
static inline bool bootutil_verify_manifest(const struct mcuboot_manifest *manifest)
{
if (manifest == NULL) {
return false;
}

/* Currently only the simplest manifest format is supported */
if (manifest->format != 0x1) {
return false;
}

if (manifest->image_count != MCUBOOT_IMAGE_NUMBER - 1) {
return false;
}

return true;
}

/**
* @brief Get the image hash from the manifest.
*
* @param[in] manifest The reference to the manifest structure.
* @param[in] image_index The index of the image to get the hash for.
* Must be in range <0, MCUBOOT_IMAGE_NUMBER - 1>, but
* must not be equal to MCUBOOT_MANIFEST_IMAGE_NUMBER.
*
* @return true if hash matches with the manifest, false otherwise.
*/
static inline bool bootutil_verify_manifest_image_hash(const struct mcuboot_manifest *manifest,
const uint8_t *exp_hash, uint32_t image_index)
{
if (!bootutil_verify_manifest(manifest)) {
return false;
}

if (image_index >= MCUBOOT_IMAGE_NUMBER) {
return false;
}

if (image_index < MCUBOOT_MANIFEST_IMAGE_NUMBER) {
if (memcmp(exp_hash, manifest->image_hash[image_index], IMAGE_HASH_SIZE) == 0) {
return true;
}
} else if (image_index > MCUBOOT_MANIFEST_IMAGE_NUMBER) {
if (memcmp(exp_hash, manifest->image_hash[image_index - 1], IMAGE_HASH_SIZE) == 0) {
return true;
}
}

return false;
}

#ifdef __cplusplus
}
#endif

#endif /* __MCUBOOT_MANIFEST_H__ */
12 changes: 12 additions & 0 deletions boot/bootutil/src/bootutil_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
#include "bootutil/enc_key.h"
#endif

#ifdef MCUBOOT_MANIFEST_UPDATES
#include "bootutil/mcuboot_manifest.h"
#endif /* MCUBOOT_MANIFEST_UPDATES */

#ifdef __cplusplus
extern "C" {
#endif
Expand Down Expand Up @@ -271,6 +275,14 @@ struct boot_loader_state {
#endif
} slot_usage[BOOT_IMAGE_NUMBER];
#endif /* MCUBOOT_DIRECT_XIP || MCUBOOT_RAM_LOAD */

#if defined(MCUBOOT_MANIFEST_UPDATES)
struct mcuboot_manifest manifest[BOOT_NUM_SLOTS];
bool manifest_valid[BOOT_NUM_SLOTS];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move that into the mcuboot_manifest? We can have different structs for TLV and internal usage.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That'd need a struct within a struct, as the struct mcuboot_manifest is directly loaded from the TLV and the manifest_valid is not part of the incoming data, but an internal flag, indicating that the structure was loaded.

If you find the "valid" sate flag to be non-universal, I may move everything inside (including manifest_valid as well as matching_manifest), but I am hesitant to do so as those are not variables that holds information from the manifest, but a state variables for a loader logic and are not needed outside of the bootloader scope.

#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) || defined(MCUBOOT_SWAP_USING_OFFSET)
enum boot_slot matching_manifest[BOOT_IMAGE_NUMBER][BOOT_NUM_SLOTS];
#endif
#endif
};

struct boot_sector_buffer {
Expand Down
118 changes: 117 additions & 1 deletion boot/bootutil/src/image_validate.c
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ BOOT_LOG_MODULE_DECLARE(mcuboot);
#include "bootutil/mcuboot_uuid.h"
#endif /* MCUBOOT_UUID_VID || MCUBOOT_UUID_CID */

#ifdef MCUBOOT_MANIFEST_UPDATES
#include "bootutil/mcuboot_manifest.h"
#endif /* MCUBOOT_MANIFEST_UPDATES */

#ifdef MCUBOOT_ENC_IMAGES
#include "bootutil/enc_key.h"
#endif
Expand Down Expand Up @@ -206,7 +210,7 @@ bootutil_img_validate(struct boot_loader_state *state,
{
#if (defined(EXPECTED_KEY_TLV) && defined(MCUBOOT_HW_KEY)) || \
(defined(EXPECTED_SIG_TLV) && defined(MCUBOOT_BUILTIN_KEY)) || \
defined(MCUBOOT_HW_ROLLBACK_PROT) || \
defined(MCUBOOT_HW_ROLLBACK_PROT) || defined(MCUBOOT_MANIFEST_UPDATES) || \
defined(MCUBOOT_UUID_VID) || defined(MCUBOOT_UUID_CID)
int image_index = (state == NULL ? 0 : BOOT_CURR_IMG(state));
#endif
Expand Down Expand Up @@ -244,6 +248,11 @@ bootutil_img_validate(struct boot_loader_state *state,
uint32_t img_security_cnt = 0;
FIH_DECLARE(security_counter_valid, FIH_FAILURE);
#endif
#ifdef MCUBOOT_MANIFEST_UPDATES
bool manifest_found = false;
bool manifest_valid = false;
uint8_t slot = (flash_area_get_id(fap) == FLASH_AREA_IMAGE_SECONDARY(image_index) ? 1 : 0);
#endif
#ifdef MCUBOOT_UUID_VID
struct image_uuid img_uuid_vid = {0x00};
FIH_DECLARE(uuid_vid_valid, FIH_FAILURE);
Expand Down Expand Up @@ -356,6 +365,69 @@ bootutil_img_validate(struct boot_loader_state *state,
goto out;
}

#ifdef MCUBOOT_MANIFEST_UPDATES
if (image_index == MCUBOOT_MANIFEST_IMAGE_NUMBER) {
if (!state->manifest_valid[slot]) {
/* Manifest TLV must be processed before any of the image's hash TLV. */
BOOT_LOG_ERR("bootutil_img_validate: image rejected, manifest not found before "
"image %d hash", image_index);
rc = -1;
goto out;
}
/* Manifest image does not have hash in the manifest. */
image_hash_valid = 1;
break;
}
#if defined(MCUBOOT_SWAP_USING_SCRATCH) || defined(MCUBOOT_SWAP_USING_MOVE) || \
defined(MCUBOOT_SWAP_USING_OFFSET)
state->matching_manifest[image_index][slot] = BOOT_SLOT_NONE;
/* Try to match with the primary manifest first. */
if (state->manifest_valid[BOOT_SLOT_PRIMARY]) {
if (bootutil_verify_manifest_image_hash(&state->manifest[BOOT_SLOT_PRIMARY], hash,
image_index)) {
state->matching_manifest[image_index][slot] = BOOT_SLOT_PRIMARY;
}
}

/* Try to match with the secondary manifest if not matched with the primary. */
if(state->matching_manifest[image_index][slot] == BOOT_SLOT_NONE &&
state->manifest_valid[BOOT_SLOT_SECONDARY]) {
if (bootutil_verify_manifest_image_hash(&state->manifest[BOOT_SLOT_SECONDARY], hash,
image_index)) {
state->matching_manifest[image_index][slot] = BOOT_SLOT_SECONDARY;
}
}

/* No matching manifest found. */
if (state->matching_manifest[image_index][slot] == BOOT_SLOT_NONE) {
BOOT_LOG_ERR(
"bootutil_img_validate: image rejected, no valid manifest for image %d slot %d",
image_index, slot);
rc = -1;
goto out;
} else {
BOOT_LOG_INF("bootutil_img_validate: image %d slot %d matches manifest in slot %d",
image_index, slot, state->matching_manifest[image_index][slot]);
}
#else /* MCUBOOT_SWAP_USING_SCRATCH || MCUBOOT_SWAP_USING_MOVE || MCUBOOT_SWAP_USING_OFFSET */
/* Manifest image for a given slot must precede any of other images. */
if (!state->manifest_valid[slot]) {
/* Manifest TLV must be processed before any of the image's hash TLV. */
BOOT_LOG_ERR("bootutil_img_validate: image rejected, no valid manifest for slot %d",
slot);
rc = -1;
goto out;
}

/* Any image, not described by the manifest is considered as invalid. */
if (!bootutil_verify_manifest_image_hash(&state->manifest[slot], hash, image_index)) {
BOOT_LOG_ERR(
"bootutil_img_validate: image rejected, hash does not match manifest contents");
FIH_SET(fih_rc, FIH_FAILURE);
goto out;
}
#endif /* MCUBOOT_SWAP_USING_SCRATCH || MCUBOOT_SWAP_USING_MOVE || MCUBOOT_SWAP_USING_OFFSET */
#endif /* MCUBOOT_MANIFEST_UPDATES */
image_hash_valid = 1;
break;
}
Expand Down Expand Up @@ -484,6 +556,43 @@ bootutil_img_validate(struct boot_loader_state *state,
break;
}
#endif /* MCUBOOT_HW_ROLLBACK_PROT */
#ifdef MCUBOOT_MANIFEST_UPDATES
case IMAGE_TLV_MANIFEST:
{
/* There can be only one manifest and must be a part of image with specific index. */
if (manifest_found || image_index != MCUBOOT_MANIFEST_IMAGE_NUMBER ||
len != sizeof(struct mcuboot_manifest)) {
BOOT_LOG_ERR(
"bootutil_img_validate: image %d slot %d rejected, unexpected manifest TLV",
image_index, slot);
rc = -1;
goto out;
}

manifest_found = true;

rc = LOAD_IMAGE_DATA(hdr, fap, off, &state->manifest[slot],
sizeof(struct mcuboot_manifest));
if (rc) {
BOOT_LOG_ERR("bootutil_img_validate: slot %d rejected, unable to load manifest",
slot);
goto out;
}

manifest_valid = bootutil_verify_manifest(&state->manifest[slot]);
if (!manifest_valid) {
BOOT_LOG_ERR("bootutil_img_validate: slot %d rejected, invalid manifest contents",
slot);
rc = -1;
goto out;
}

/* The image's manifest has been successfully verified. */
state->manifest_valid[slot] = true;
BOOT_LOG_INF("bootutil_img_validate: slot %d manifest verified", slot);
break;
}
#endif
#ifdef MCUBOOT_UUID_VID
case IMAGE_TLV_UUID_VID:
{
Expand Down Expand Up @@ -564,6 +673,13 @@ bootutil_img_validate(struct boot_loader_state *state,
}
#endif

#ifdef MCUBOOT_MANIFEST_UPDATES
if (image_index == MCUBOOT_MANIFEST_IMAGE_NUMBER && (!manifest_found || !manifest_valid)) {
BOOT_LOG_ERR("bootutil_img_validate: slot %d rejected, manifest missing or invalid", slot);
rc = -1;
goto out;
}
#endif
#ifdef MCUBOOT_UUID_VID
if (FIH_NOT_EQ(uuid_vid_valid, FIH_SUCCESS)) {
rc = -1;
Expand Down
4 changes: 4 additions & 0 deletions boot/bootutil/src/loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@
#include "bootutil/mcuboot_status.h"
#include "bootutil_loader.h"

#ifndef MCUBOOT_MANIFEST_UPDATES

#ifdef MCUBOOT_ENC_IMAGES
#include "bootutil/enc_key.h"
#endif
Expand Down Expand Up @@ -2495,3 +2497,5 @@ uint32_t boot_get_state_secondary_offset(struct boot_loader_state *state,
return 0;
}
#endif

#endif /* !MCUBOOT_MANIFEST_UPDATES */
Loading
Loading