Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
1c82453
sign: scrub key buffers before free
danielinux Apr 8, 2026
87d5a7f
Scrub RSA keygen private material
danielinux Apr 8, 2026
114140c
Zero ECC keygen private buffers
danielinux Apr 8, 2026
6f40afa
Zero EdDSA keygen private data
danielinux Apr 8, 2026
3278303
zero ML-DSA private key buffer
danielinux Apr 8, 2026
8399e3e
Add restricted key mask authenticity tests
danielinux Apr 8, 2026
1442e1c
Propagate encrypt key flash errors
danielinux Apr 8, 2026
de4c33e
Propagate erase encrypt key write failures
danielinux Apr 8, 2026
364b9d0
Fix policy_create PCR digest validation
danielinux Apr 8, 2026
c6e7f79
Fix sign encrypted output open failure
danielinux Apr 8, 2026
2ab04ed
Check image reopen failures in sign tool
danielinux Apr 8, 2026
9715b4d
Use constant-time RSA hash comparison
danielinux Apr 8, 2026
44c5b4a
Protect bootloader before application boot
danielinux Apr 8, 2026
12dd6be
Add auth type coverage for unit-image
danielinux Apr 8, 2026
30fb32f
Add auth-only invalid update test
danielinux Apr 8, 2026
47b61ba
Add RAM_CODE self-update unit coverage
danielinux Apr 8, 2026
0bcc49a
Strengthen same-version RAM update test
danielinux Apr 8, 2026
153ad2b
Fix sign header TLV overflow sizing
danielinux Apr 8, 2026
a5ea3ff
Reject oversized delta source offsets
danielinux Apr 8, 2026
e22eade
fix memmove large-length backward copy
danielinux Apr 8, 2026
1528bc8
Use constant-time TPM secret checks
danielinux Apr 8, 2026
dfc73ca
Use constant-time encryption key validation
danielinux Apr 8, 2026
8e2f8b3
Use fixed-length erased-key check
danielinux Apr 8, 2026
f5d50d4
enforce skip-verify prerequisites
danielinux Apr 8, 2026
0c4be70
Add equal-version update-disk regression test
danielinux Apr 8, 2026
e4e96ad
Reject valid zero-size delta images
danielinux Apr 8, 2026
35142e6
Fix total size type in update flash
danielinux Apr 8, 2026
04cc957
zeroize update key material
danielinux Apr 8, 2026
2f75363
zero custom encrypt stack buffers
danielinux Apr 8, 2026
e855c59
zeroize swap trailer key buffer
danielinux Apr 8, 2026
435e8d4
Scrub sign-tool encryption material
danielinux Apr 8, 2026
fc9e7a3
Use constant-time delta base hash compare
danielinux Apr 8, 2026
b705ca7
Warn when DISABLE_BACKUP is enabled
danielinux Apr 8, 2026
4d6f1d5
Add sign/parser roundtrip tests
danielinux Apr 8, 2026
6ffae1a
Add delta roundtrip edge-case coverage
danielinux Apr 8, 2026
1c07a99
libwolfboot: fix encrypted test-app builds
danielinux Apr 8, 2026
0ab76ea
tools/unit-tests: fix rebased sign test target
danielinux Apr 8, 2026
b8131a1
cmake: define bootloader protect macros
danielinux Apr 8, 2026
e60db57
cmake: link default flash-protect hook
danielinux Apr 8, 2026
399e875
unit-tests: fix update_ram protect context
danielinux Apr 8, 2026
c51bb6a
unit-tests: exclude keytools from coverage
danielinux Apr 8, 2026
e5a42e3
nrf5340: match hal_flash_protect signature
danielinux Apr 8, 2026
62f6eca
unit-tests: avoid gcov memmove timeout
danielinux Apr 8, 2026
040bcdb
unit-tests: skip large memmove under coverage
danielinux Apr 9, 2026
fab609d
options: normalize self-update monolithic flag
danielinux Apr 9, 2026
fff5222
hal: document flash protect API contract
danielinux Apr 9, 2026
d78c625
update: propagate encrypt key persist errors
danielinux Apr 9, 2026
cdf8402
docs: fix encrypt key erase return contract
danielinux Apr 9, 2026
4c704a9
boot: fail closed on flash protect errors
danielinux Apr 9, 2026
a5ef464
update: fix warning-only build regressions
danielinux Apr 9, 2026
eca76ef
string: fix unaligned backward word copies
danielinux Apr 9, 2026
47815c0
encrypt: check custom key fetch in aes_init
danielinux Apr 10, 2026
642def1
Fix C89 decl-after-statement
danielinux Apr 10, 2026
4a007ab
sign: size policy tlv before header build
danielinux Apr 10, 2026
9247787
keygen: zero private material on all errors
danielinux Apr 10, 2026
491595b
sign: close output file on failure
danielinux Apr 10, 2026
ee9f21c
update: propagate encrypt key read errors
danielinux Apr 10, 2026
9b36e62
string: gate memmove fast path
danielinux Apr 10, 2026
f8ec206
tpmtools: regenerate keystore in clean builds
danielinux Apr 10, 2026
8842493
update: always scrub final swap buffer
danielinux Apr 10, 2026
8033366
core: make constant compare common
danielinux Apr 10, 2026
498453e
keygen: fix double close on success
danielinux Apr 10, 2026
e43cdd7
update: keep zeroize helper available
danielinux Apr 10, 2026
087ff90
image: fix hardened hash compare sense
danielinux Apr 10, 2026
0a8a40f
sign: normalize short read failures
danielinux Apr 10, 2026
546d4f4
footprint: fix mldsa keygen and ed448 limit
danielinux Apr 10, 2026
8cdb8e9
unit-tests: link common compare helper
danielinux Apr 10, 2026
0d6ad20
tpm: localize constant compare again
danielinux Apr 10, 2026
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
15 changes: 14 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,20 @@ if(ARCH STREQUAL "AARCH64")

endif()

if(NOT DEFINED WOLFBOOT_ORIGIN)
set(WOLFBOOT_ORIGIN ${ARCH_FLASH_OFFSET})
endif()

if(NOT DEFINED BOOTLOADER_PARTITION_SIZE)
math(EXPR BOOTLOADER_PARTITION_SIZE
"${WOLFBOOT_PARTITION_BOOT_ADDRESS} - ${ARCH_FLASH_OFFSET}"
OUTPUT_FORMAT HEXADECIMAL)
endif()

list(APPEND WOLFBOOT_DEFS ARCH_FLASH_OFFSET=${ARCH_FLASH_OFFSET})
list(APPEND WOLFBOOT_DEFS
WOLFBOOT_ORIGIN=${WOLFBOOT_ORIGIN}
BOOTLOADER_PARTITION_SIZE=${BOOTLOADER_PARTITION_SIZE})

if(${WOLFBOOT_TARGET} STREQUAL "x86_64_efi")
if(NOT DEFINED GNU_EFI_LIB_PATH)
Expand Down Expand Up @@ -1139,7 +1152,7 @@ if(TZEN)
endif()
endif()

target_sources(wolfboothal PRIVATE include/hal.h hal/${WOLFBOOT_TARGET}.c ${WOLFBOOT_FLASH_SOURCES}
target_sources(wolfboothal PRIVATE include/hal.h hal/hal.c hal/${WOLFBOOT_TARGET}.c ${WOLFBOOT_FLASH_SOURCES}
${PARTITION_SOURCE} ${WOLFBOOT_TZ_HAL_SOURCES})


Expand Down
4 changes: 3 additions & 1 deletion docs/compile.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,9 @@ To circumvent the compile-time checks on the maximum allowed stack size, use `WO

Optionally, it is possible to disable the backup copy of the current running firmware upon the installation of the
update. This implies that no fall-back mechanism is protecting the target from a faulty firmware installation, but may be useful
in some cases where it is not possible to write on the update partition from the bootloader.
in some cases where it is not possible to write on the update partition from the bootloader. This also removes the
power-fail-safe swap behavior: if power is lost while the update is being copied into the BOOT partition, the original
firmware may already be partially overwritten and the device can be left unrecoverable.
The associated compile-time option is

`DISABLE_BACKUP=1`
Expand Down
7 changes: 7 additions & 0 deletions hal/hal.c
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,13 @@ int hal_flash_test_dualbank(void)

#endif /* TEST_FLASH */

WEAKFUNCTION int RAMFUNCTION hal_flash_protect(haladdr_t address, int len)
{
(void)address;
(void)len;
return 0;
}

WEAKFUNCTION int hal_uds_derive_key(uint8_t *out, size_t out_len)
{
(void)out;
Expand Down
10 changes: 1 addition & 9 deletions hal/nrf5340.c
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,7 @@ void hal_init(void)

#ifdef __WOLFBOOT
/* enable write protection for the region of flash specified */
int hal_flash_protect(uint32_t start, uint32_t len)
int RAMFUNCTION hal_flash_protect(haladdr_t start, int len)
{
/* only application core supports SPU */
#ifdef TARGET_nrf5340_app
Expand Down Expand Up @@ -884,14 +884,6 @@ static void periph_unsecure()

void hal_prepare_boot(void)
{
/* Write protect bootloader region of flash.
* Not needed in TrustZone configs because the application
* runs in non-secure mode and the bootloader partition is marked as
* secure. */
#ifndef TZEN
hal_flash_protect(WOLFBOOT_ORIGIN, BOOTLOADER_PARTITION_SIZE);
#endif

if (enableShm) {
#ifdef TARGET_nrf5340_net
if (doUpdateNet) {
Expand Down
7 changes: 6 additions & 1 deletion hal/skeleton.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ void hal_init(void)

void hal_prepare_boot(void)
{
/* wolfBoot calls hal_flash_protect() before this hook.
* Override int hal_flash_protect(haladdr_t address, int len) to lock
* the bootloader region on targets that support runtime write
* protection. Return 0 on success or a negative value on failure, and
* use this hook only for any remaining platform-specific handoff work.
*/
}

#endif
Expand All @@ -55,4 +61,3 @@ int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
{
return 0; /* on success. */
}

5 changes: 5 additions & 0 deletions include/hal.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ uint64_t hal_get_timer_us(void);
#endif
void hal_flash_unlock(void);
void hal_flash_lock(void);
/*
* Lock the flash region [address, address + len) against writes.
* Return 0 on success, or a negative value on failure.
*/
int hal_flash_protect(haladdr_t address, int len);
void hal_prepare_boot(void);

#ifdef DUALBANK_SWAP
Expand Down
16 changes: 10 additions & 6 deletions include/image.h
Original file line number Diff line number Diff line change
Expand Up @@ -424,8 +424,9 @@ static void __attribute__((noinline)) wolfBoot_image_clear_signature_ok(
asm volatile("mov r0, #50":::"r0"); \
asm volatile("mov r0, #50":::"r0"); \
asm volatile("mov r0, #50":::"r0"); \
compare_res = XMEMCMP(digest, img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE); \
/* Redundant checks that ensure the function actually returned 0 */ \
compare_res = image_CT_compare(digest, img->sha_hash, \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🔴 [Critical] RSA_VERIFY_HASH armored inline asm checks r0==0 but image_CT_compare returns 1 on match
🚫 BLOCK bug

The RSA_VERIFY_HASH macro was changed from XMEMCMP (returns 0 on match) to image_CT_compare (returns 1 on match, confirmed in src/image.c:71 return diff == 0). However, the inline ARM assembly was not updated to match the new return value semantics. The asm does cmp r0, #0 / bne hnope which means "if r0 != 0, jump to failure." Since image_CT_compare returns 1 (non-zero) on a successful match, the asm will ALWAYS branch to hnope on a valid signature, causing RSA hash verification to always fail on WOLFBOOT_ARMORED builds. The comment was updated to say "Redundant checks that ensure the function actually returned 1" but the assembly operand is still #0. This inverts the security check: valid images are always rejected.

Suggestion:

Suggested change
compare_res = image_CT_compare(digest, img->sha_hash, \
Change all `cmp r0, #0` to `cmp r0, #1` throughout the RSA_VERIFY_HASH macro (both call sites, all 8 redundant check blocks). This makes the `bne hnope` branch when r0 != 1 (i.e., mismatch), correctly rejecting only non-matching hashes.

WOLFBOOT_SHA_DIGEST_SIZE); \
/* Redundant checks that ensure the function actually returned 1 */ \
asm volatile("cmp r0, #0":::"cc"); \
asm volatile("cmp r0, #0":::"cc"); \
asm volatile("cmp r0, #0":::"cc"); \
Expand All @@ -442,10 +443,11 @@ static void __attribute__((noinline)) wolfBoot_image_clear_signature_ok(
asm volatile("cmp r0, #0":::"cc"); \
asm volatile("cmp r0, #0":::"cc"); \
asm volatile("bne hnope"); \
/* Repeat memcmp call */ \
compare_res = XMEMCMP(digest, img->sha_hash, WOLFBOOT_SHA_DIGEST_SIZE); \
/* Repeat comparison call */ \
compare_res = image_CT_compare(digest, img->sha_hash, \
WOLFBOOT_SHA_DIGEST_SIZE); \
compare_res; \
/* Redundant checks that ensure the function actually returned 0 */ \
/* Redundant checks that ensure the function actually returned 1 */ \
asm volatile("cmp r0, #0":::"cc"); \
asm volatile("cmp r0, #0":::"cc"); \
asm volatile("cmp r0, #0":::"cc"); \
Expand Down Expand Up @@ -1234,7 +1236,7 @@ static void UNUSEDFUNCTION wolfBoot_image_clear_signature_ok(
ret = fn(__VA_ARGS__);

#define RSA_VERIFY_HASH(img,digest) \
if (XMEMCMP(img->sha_hash, digest, WOLFBOOT_SHA_DIGEST_SIZE) == 0) \
if (image_CT_compare(img->sha_hash, digest, WOLFBOOT_SHA_DIGEST_SIZE)) \
wolfBoot_image_confirm_signature_ok(img);

#define PART_SANITY_CHECK(p) \
Expand All @@ -1250,6 +1252,8 @@ static void UNUSEDFUNCTION wolfBoot_image_clear_signature_ok(
#endif

/* Defined in image.c */
int image_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
4 changes: 4 additions & 0 deletions include/tpm.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@ int wolfBoot_load_pubkey(const uint8_t* pubkey_hint, WOLFTPM2_KEY* pubKey,
TPM_ALG_ID* pAlg);
#endif

#if defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL)
int wolfBoot_constant_compare(const uint8_t* a, const uint8_t* b, uint32_t len);
#endif

#ifdef WOLFBOOT_TPM_KEYSTORE
int wolfBoot_check_rot(int key_slot, uint8_t* pubkey_hint);
#endif
Expand Down
9 changes: 9 additions & 0 deletions include/wolfboot/wolfboot.h
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,15 @@ extern "C" {
#endif
#endif /* WOLFBOOT_SELF_HEADER */

#if defined(WOLFBOOT_SKIP_BOOT_VERIFY) && !defined(WOLFBOOT_SELF_HEADER)
#error "WOLFBOOT_SKIP_BOOT_VERIFY requires WOLFBOOT_SELF_HEADER"
#endif

#if defined(WOLFBOOT_SKIP_BOOT_VERIFY) && \
!defined(WOLFBOOT_SELF_UPDATE_MONOLITHIC)
#error "WOLFBOOT_SKIP_BOOT_VERIFY requires WOLFBOOT_SELF_UPDATE_MONOLITHIC"
#endif

#ifdef BIG_ENDIAN_ORDER
# define WOLFBOOT_MAGIC 0x574F4C46 /* WOLF */
# define WOLFBOOT_MAGIC_TRAIL 0x424F4F54 /* BOOT */
Expand Down
10 changes: 10 additions & 0 deletions options.mk
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@ endif

## Monolithic self-update: erase covers fw_size so the payload can span
## the bootloader region into the contiguous boot partition.
ifeq ($(WOLFBOOT_SELF_UPDATE_MONOLITHIC),1)
SELF_UPDATE_MONOLITHIC:=1
endif
ifeq ($(SELF_UPDATE_MONOLITHIC),1)
CFLAGS+=-DWOLFBOOT_SELF_UPDATE_MONOLITHIC
endif
Expand Down Expand Up @@ -710,6 +713,12 @@ ifeq ($(ALLOW_DOWNGRADE),1)
endif

ifeq ($(WOLFBOOT_SKIP_BOOT_VERIFY),1)
ifneq ($(WOLFBOOT_SELF_HEADER),1)
$(error WOLFBOOT_SKIP_BOOT_VERIFY=1 requires WOLFBOOT_SELF_HEADER=1)
endif
ifneq ($(SELF_UPDATE_MONOLITHIC),1)
$(error WOLFBOOT_SKIP_BOOT_VERIFY=1 requires WOLFBOOT_SELF_UPDATE_MONOLITHIC (set SELF_UPDATE_MONOLITHIC=1))
endif
CFLAGS+=-D"WOLFBOOT_SKIP_BOOT_VERIFY"
endif

Expand All @@ -718,6 +727,7 @@ ifeq ($(NVM_FLASH_WRITEONCE),1)
endif

ifeq ($(DISABLE_BACKUP),1)
$(warning DISABLE_BACKUP=1 disables power-fail-safe updates; losing power during an update can leave BOOT partially written and unrecoverable)
CFLAGS+= -D"DISABLE_BACKUP"
endif

Expand Down
5 changes: 5 additions & 0 deletions src/delta.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ struct BLOCK_HDR_PACKED block_hdr {
};

#define BLOCK_HDR_SIZE (sizeof (struct block_hdr))
#define BLOCK_OFF_MAX 0xFFFFFFU

#if defined(EXT_ENCRYPTED) && defined(__WOLFBOOT)
#include "image.h"
Expand Down Expand Up @@ -300,6 +301,8 @@ int wb_diff(WB_DIFF_CTX *ctx, uint8_t *patch, uint32_t len)
*/
match_len = BLOCK_HDR_SIZE;
blk_start = pa - ctx->src_a;
if (blk_start > BLOCK_OFF_MAX)
return -1;
b_start = ctx->off_b;
pa+= BLOCK_HDR_SIZE;
ctx->off_b += BLOCK_HDR_SIZE;
Expand Down Expand Up @@ -362,6 +365,8 @@ int wb_diff(WB_DIFF_CTX *ctx, uint8_t *patch, uint32_t len)
*/
match_len = BLOCK_HDR_SIZE;
blk_start = pb - ctx->src_b;
if (blk_start > BLOCK_OFF_MAX)
return -1;
pb+= BLOCK_HDR_SIZE;
ctx->off_b += BLOCK_HDR_SIZE;
while ((pb < pb_limit) &&
Expand Down
4 changes: 2 additions & 2 deletions src/image.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,8 @@
/* Globals */
static uint8_t digest[WOLFBOOT_SHA_DIGEST_SIZE] XALIGNED(4);

static int image_CT_compare(const uint8_t *expected, const uint8_t *actual,
uint32_t len)
int __attribute__((noinline)) image_CT_compare(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚪ [Info] image_CT_compare uses attribute((noinline)) without portability guard
🔧 NIT convention

The function uses __attribute__((noinline)) directly, which is GCC/Clang-specific. If non-GCC compilers (IAR, MSVC, CCRX) are supported for this file, this syntax may not compile. The codebase already uses some portable macros for attributes.

Suggestion:

Suggested change
int __attribute__((noinline)) image_CT_compare(
Use a portable macro wrapper if non-GCC compilers need to compile this file.

const uint8_t *expected, const uint8_t *actual, uint32_t len)
{
uint8_t diff = 0;
uint32_t i;
Expand Down
Loading
Loading