Skip to content
Merged
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
59 changes: 59 additions & 0 deletions include/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ struct wolfBoot_image {
uint32_t sha_ok;
uint32_t canary_FEEDCAFE;
uint32_t not_sha_ok;
uintptr_t not_fw_base; /* complement of fw_base, for FI hardening */
uint32_t not_ext; /* image is no longer external */
};

Expand Down Expand Up @@ -229,6 +230,18 @@ static void NOINLINEFUNCTION wolfBoot_image_clear_sha_ok(
img->not_sha_ok = 1UL;
}

/**
* Records the image entry base together with its complement, so that a single
* fault on the pointer that do_boot() jumps through can be detected by
* FW_BASE_SANITY_CHECK() before the branch is taken.
*/
static void NOINLINEFUNCTION wolfBoot_image_set_fw_base(
struct wolfBoot_image *img, void *base)
{
img->fw_base = (uint8_t *)base;
img->not_fw_base = ~(uintptr_t)base;
}

/**
* Final sanity check, performed just before do_boot, or before starting an
* update that has been verified.
Expand Down Expand Up @@ -757,6 +770,43 @@ static void NOINLINEFUNCTION wolfBoot_image_clear_sha_ok(
asm volatile("cmp r2, #0xFFFFFFFE":::"cc"); \
asm volatile("bne .-12")

/**
* Hardened assertion that the image entry base is consistent with its stored
* complement (fw_base == ~not_fw_base), performed immediately before do_boot()
* dereferences fw_base. A single fault on either the pointer or the loads
* fails the check safely (spins) instead of redirecting the boot jump.
*/
#define FW_BASE_SANITY_CHECK(p) \
do { \
/* Single asm block so the compiler cannot allocate over r0/r2 between \
* steps: r2 = fw_base, r0 = ~not_fw_base (== expected fw_base), then \
* a redundant self-trapping comparison that spins on any mismatch. */ \
asm volatile( \
"mov r2, %[fwb]\n\t" \
"mov r0, %[nfwb]\n\t" \
"mvn r0, r0\n\t" \
"cmp r2, r0\n\t" \
"cmp r2, r0\n\t" \
"cmp r2, r0\n\t" \
"bne .\n\t" \
"cmp r2, r0\n\t" \
"cmp r2, r0\n\t" \
"cmp r2, r0\n\t" \
"bne .-4\n\t" \
"cmp r2, r0\n\t" \
"cmp r2, r0\n\t" \
"cmp r2, r0\n\t" \
"bne .-8\n\t" \
"cmp r2, r0\n\t" \
"cmp r2, r0\n\t" \
"cmp r2, r0\n\t" \
"bne .-12\n\t" \
: \
: [fwb] "r" ((uintptr_t)(p)->fw_base), \
[nfwb] "r" ((uintptr_t)(p)->not_fw_base) \
: "r0", "r2", "cc"); \
} while (0)

/**
* ECC / Ed / PQ signature verification.
* Those verify functions set an additional value 'p_res'
Expand Down Expand Up @@ -1521,6 +1571,11 @@ static void UNUSEDFUNCTION wolfBoot_image_clear_sha_ok(
{
img->sha_ok = 0;
}
static void UNUSEDFUNCTION wolfBoot_image_set_fw_base(
struct wolfBoot_image *img, void *base)
{
img->fw_base = (uint8_t *)base;
}

#define likely(x) (x)
#define unlikely(x) (x)
Expand Down Expand Up @@ -1560,6 +1615,8 @@ static void UNUSEDFUNCTION wolfBoot_image_clear_sha_ok(
if ((p)->sha_ok != 1) \
wolfBoot_panic()

#define FW_BASE_SANITY_CHECK(p) do{} while(0)

#define CONFIRM_MASK_VALID(id, mask) \
if ((mask & (1UL << id)) != (1UL << id)) \
wolfBoot_panic()
Expand All @@ -1571,6 +1628,8 @@ static void UNUSEDFUNCTION wolfBoot_image_clear_sha_ok(
/* Defined in image.c */
int image_CT_compare(const uint8_t *expected, const uint8_t *actual,
uint32_t len);
int wolfBoot_hardened_CT_compare(const uint8_t *expected, const uint8_t *actual,
uint32_t len);
int wolfBoot_open_image(struct wolfBoot_image *img, uint8_t part);
#ifdef EXT_FLASH
int wolfBoot_open_image_external(struct wolfBoot_image* img, uint8_t part, uint8_t* addr);
Expand Down
30 changes: 25 additions & 5 deletions src/image.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,25 @@ int NOINLINEFUNCTION image_CT_compare(
return (diff != 0U) ? 1 : 0;
}

/**
* Fault-hardened equality check around image_CT_compare(): the constant-time
* comparison is run twice and a match is reported only when both independent
* calls agree. A single instruction-skip fault can subvert at most one of the
* two calls (or one of the two result checks), so a genuine mismatch is still
* detected. Returns 0 when equal, non-zero otherwise.
*/
int NOINLINEFUNCTION wolfBoot_hardened_CT_compare(
const uint8_t *expected, const uint8_t *actual, uint32_t len)
{
volatile int r1 = image_CT_compare(expected, actual, len);
volatile int r2 = image_CT_compare(expected, actual, len);
/* Combine both results without branching: non-zero if either independent
* comparison reported a mismatch. This preserves image_CT_compare()'s 0/1
* return semantics and avoids data-dependent control flow, while a single
* fault can still subvert at most one of the two calls. */
return (r1 | r2);
}

#if defined(WOLFBOOT_CERT_CHAIN_VERIFY) && \
(defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) || \
defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER))
Expand Down Expand Up @@ -1424,7 +1443,7 @@ int wolfBoot_open_image_address(struct wolfBoot_image *img, uint8_t *image)
}
#endif
img->hdr_ok = 1;
img->fw_base = img->hdr + IMAGE_HEADER_SIZE;
wolfBoot_image_set_fw_base(img, img->hdr + IMAGE_HEADER_SIZE);
#ifdef EXT_FLASH
img->hdr_cache = image;
#endif
Expand Down Expand Up @@ -1488,7 +1507,7 @@ int wolfBoot_open_image(struct wolfBoot_image *img, uint8_t part)
if (part == PART_SWAP) {
img->hdr = (void*)WOLFBOOT_PARTITION_SWAP_ADDRESS;
img->hdr_ok = 1;
img->fw_base = img->hdr;
wolfBoot_image_set_fw_base(img, img->hdr);
img->fw_size = WOLFBOOT_SECTOR_SIZE;
return 0;
}
Expand All @@ -1507,7 +1526,7 @@ int wolfBoot_open_image(struct wolfBoot_image *img, uint8_t part)
if (ret < 0)
return -1;
img->hdr_ok = 1;
img->fw_base = img->hdr;
wolfBoot_image_set_fw_base(img, img->hdr);
img->fw_size = (uint32_t)ret;
return 0;
}
Expand Down Expand Up @@ -1620,7 +1639,7 @@ int wolfBoot_open_self_address(struct wolfBoot_image* img, uint8_t* hdr,
return -1;
}
#endif
img->fw_base = image;
wolfBoot_image_set_fw_base(img, image);
img->part = PART_SELF;
img->hdr_ok = 1;

Expand Down Expand Up @@ -2111,7 +2130,8 @@ int wolfBoot_check_flash_image_elf(uint8_t part, unsigned long* entry_out)

/* Finalize SHA calculation */
final_hash(&ctx, calc_digest);
if (image_CT_compare(exp_digest, calc_digest, WOLFBOOT_SHA_DIGEST_SIZE) != 0) {
if (wolfBoot_hardened_CT_compare(exp_digest, calc_digest,
WOLFBOOT_SHA_DIGEST_SIZE) != 0) {
wolfBoot_printf("ELF: [CHECK] SHA verification FAILED\n");
wolfBoot_printf(
"ELF: [CHECK] Expected %02x%02x%02x%02x%02x%02x%02x%02x\n",
Expand Down
8 changes: 5 additions & 3 deletions src/update_flash.c
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,8 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot,
cur_v, delta_base_v);
ret = -1;
} else if (!resume && delta_base_hash &&
image_CT_compare(base_hash, delta_base_hash, base_hash_sz) != 0) {
wolfBoot_hardened_CT_compare(base_hash, delta_base_hash,
base_hash_sz) != 0) {
/* Wrong base image digest, cannot apply delta patch */
wolfBoot_printf("Delta Base hash mismatch\n");
ret = -1;
Expand Down Expand Up @@ -1270,7 +1271,7 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
wolfBoot_printf(
"Scattered image correctly verified. Setting entry point to %lx\n",
entry);
boot.fw_base = (void*)entry;
wolfBoot_image_set_fw_base(&boot, (void*)entry);
#endif
/* Direct Swap without power fail safety */

Expand Down Expand Up @@ -1618,7 +1619,7 @@ void RAMFUNCTION wolfBoot_start(void)
wolfBoot_printf(
"Scattered image correctly verified. Setting entry point to %lx\n",
entry);
boot.fw_base = (void*)entry;
wolfBoot_image_set_fw_base(&boot, (void*)entry);
#endif


Expand Down Expand Up @@ -1650,6 +1651,7 @@ void RAMFUNCTION wolfBoot_start(void)
#endif
#ifndef WOLFBOOT_SKIP_BOOT_VERIFY
PART_SANITY_CHECK(&boot);
FW_BASE_SANITY_CHECK(&boot);
#endif
do_boot((void *)boot.fw_base);
}
Expand Down
Loading