From 18cbea59ded672d8f803e523cad23660f4d2b1a2 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 19 Sep 2025 13:01:28 +0200 Subject: [PATCH 1/9] Refactoring of IV handling to prevent reusing IVs During fallback operations, use a different encryption IV than the one used to encrypt the backup during the update. This ensures that the same IV is never reused to encrypt different sectors. --- include/encrypt.h | 5 ++ include/wolfboot/wolfboot.h | 1 + src/libwolfboot.c | 57 +++++++++++++--- src/update_flash.c | 128 +++++++++++++++++++++++++++--------- 4 files changed, 149 insertions(+), 42 deletions(-) diff --git a/include/encrypt.h b/include/encrypt.h index aeda235c4f..231ae9dc30 100644 --- a/include/encrypt.h +++ b/include/encrypt.h @@ -69,5 +69,10 @@ void aes_set_iv(uint8_t *nonce, uint32_t address); int ext_flash_encrypt_write(uintptr_t address, const uint8_t *data, int len); int ext_flash_decrypt_read(uintptr_t address, uint8_t *data, int len); +#ifdef EXT_ENCRYPTED +int wolfBoot_enable_fallback_iv(int enable); +void wolfBoot_crypto_set_iv(const uint8_t *nonce, uint32_t iv_counter); +#endif + #endif /* __WOLFBOOT || UNIT_TEST */ #endif /* ENCRYPT_H_INCLUDED */ diff --git a/include/wolfboot/wolfboot.h b/include/wolfboot/wolfboot.h index 2fe7e31e8b..f51b380628 100644 --- a/include/wolfboot/wolfboot.h +++ b/include/wolfboot/wolfboot.h @@ -381,6 +381,7 @@ int wolfBoot_get_partition_state(uint8_t part, uint8_t *st); #ifdef EXT_ENCRYPTED /* Encryption support */ + #if defined(ENCRYPT_WITH_CHACHA) #define ENCRYPT_BLOCK_SIZE 64 #define ENCRYPT_KEY_SIZE 32 /* Chacha20 - 256bit */ diff --git a/src/libwolfboot.c b/src/libwolfboot.c index 9b9cb82416..ef196f1c1b 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -62,18 +62,23 @@ #include /* for size_t */ -#if defined(EXT_ENCRYPTED) +#if defined(EXT_ENCRYPTED) && (defined(__WOLFBOOT) || defined(UNIT_TEST)) +#include "encrypt.h" static int encrypt_initialized = 0; static uint8_t encrypt_iv_nonce[ENCRYPT_NONCE_SIZE] XALIGNED(4); - #if defined(__WOLFBOOT) - #include "encrypt.h" - #elif !defined(XMEMSET) +static uint32_t encrypt_iv_offset = 0; + +#define FALLBACK_IV_OFFSET 0x00100000U + #if !defined(XMEMSET) #include #define XMEMSET memset #define XMEMCPY memcpy #define XMEMCMP memcmp #endif +#if defined(ENCRYPT_WITH_AES128) || defined(ENCRYPT_WITH_AES256) +extern void aes_set_iv(uint8_t *nonce, uint32_t address); +#endif #if defined (__WOLFBOOT) || defined (UNIT_TEST) int wolfBoot_initialize_encryption(void) @@ -1027,7 +1032,7 @@ static int decrypt_header(uint8_t *src) uint32_t magic; uint32_t len; for (i = 0; i < IMAGE_HEADER_SIZE; i+=ENCRYPT_BLOCK_SIZE) { - crypto_set_iv(encrypt_iv_nonce, i / ENCRYPT_BLOCK_SIZE); + wolfBoot_crypto_set_iv(encrypt_iv_nonce, i / ENCRYPT_BLOCK_SIZE); crypto_decrypt(dec_hdr + i, src + i, ENCRYPT_BLOCK_SIZE); } magic = *((uint32_t*)(dec_hdr)); @@ -1359,6 +1364,7 @@ int wolfBoot_fallback_is_possible(void) #ifdef EXT_ENCRYPTED #include "encrypt.h" +#include "string.h" #if defined(WOLFBOOT_RENESAS_TSIP) #include "wolfssl/wolfcrypt/port/Renesas/renesas-tsip-crypt.h" @@ -1388,6 +1394,39 @@ int wolfBoot_fallback_is_possible(void) static uint8_t ENCRYPT_KEY[ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE]; #endif +#if defined(EXT_ENCRYPTED) && defined(__WOLFBOOT) +int RAMFUNCTION wolfBoot_enable_fallback_iv(int enable) +{ + int prev = 0; + if (encrypt_iv_offset != 0) + prev = 1; + + if (enable) + encrypt_iv_offset = FALLBACK_IV_OFFSET; + else + encrypt_iv_offset = 0; + + return prev; +} + +void RAMFUNCTION wolfBoot_crypto_set_iv(const uint8_t *nonce, uint32_t iv_counter) +{ +#if defined(ENCRYPT_WITH_CHACHA) + crypto_set_iv((uint8_t *)nonce, iv_counter + encrypt_iv_offset); +#elif defined(ENCRYPT_WITH_AES128) || defined(ENCRYPT_WITH_AES256) + uint8_t local_nonce[ENCRYPT_NONCE_SIZE]; + XMEMCPY(local_nonce, nonce, ENCRYPT_NONCE_SIZE); + crypto_set_iv(local_nonce, iv_counter + encrypt_iv_offset); +#else + (void)nonce; + (void)iv_counter; +#endif + + /* Fallback IV offset is single-use; clear it once applied. */ + encrypt_iv_offset = 0; +} +#endif /* EXT_ENCRYPTED && __WOLFBOOT */ + static int RAMFUNCTION hal_set_key(const uint8_t *k, const uint8_t *nonce) { #ifdef WOLFBOOT_RENESAS_TSIP @@ -1808,8 +1847,7 @@ int RAMFUNCTION ext_flash_encrypt_write(uintptr_t address, const uint8_t *data, } if (wolfBoot_initialize_encryption() < 0) return -1; - - crypto_set_iv(encrypt_iv_nonce, iv_counter); + wolfBoot_crypto_set_iv(encrypt_iv_nonce, iv_counter); break; case PART_SWAP: /* data is coming from update and is already encrypted */ @@ -1892,7 +1930,7 @@ int RAMFUNCTION ext_flash_decrypt_read(uintptr_t address, uint8_t *data, int len return -1; } } - crypto_set_iv(encrypt_iv_nonce, iv_counter); + wolfBoot_crypto_set_iv(encrypt_iv_nonce, iv_counter); break; case PART_SWAP: break; @@ -1988,7 +2026,7 @@ int wolfBoot_ram_decrypt(uint8_t *src, uint8_t *dst) /* decrypt content */ while (dst_offset < (len + IMAGE_HEADER_SIZE)) { - crypto_set_iv(encrypt_iv_nonce, iv_counter); + wolfBoot_crypto_set_iv(encrypt_iv_nonce, iv_counter); crypto_decrypt(dec_block, row_address, ENCRYPT_BLOCK_SIZE); XMEMCPY(dst + dst_offset, dec_block, ENCRYPT_BLOCK_SIZE); row_address += ENCRYPT_BLOCK_SIZE; @@ -2035,4 +2073,3 @@ int wolfBoot_nsc_write_update(uint32_t address, const uint8_t *buf, uint32_t len } #endif - diff --git a/src/update_flash.c b/src/update_flash.c index 80f2a48baa..e254487a81 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -39,6 +39,10 @@ int WP11_Library_Init(void); #endif +#ifdef EXT_ENCRYPTED +#include "encrypt.h" +#endif /* EXT_ENCRYPTED */ + #ifdef RAM_CODE #ifndef TARGET_rp2350 extern unsigned int _start_text; @@ -55,9 +59,6 @@ static uint8_t buffer[FLASHBUFFER_SIZE] XALIGNED(4); # endif #endif -#ifdef EXT_ENCRYPTED -#include "encrypt.h" -#endif static void RAMFUNCTION wolfBoot_erase_bootloader(void) { @@ -136,6 +137,7 @@ static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src, uint32_t src_sector_offset = (sector * WOLFBOOT_SECTOR_SIZE); uint32_t dst_sector_offset = src_sector_offset; #ifdef EXT_ENCRYPTED + uint32_t i; uint8_t key[ENCRYPT_KEY_SIZE]; uint8_t nonce[ENCRYPT_NONCE_SIZE]; uint32_t iv_counter; @@ -153,19 +155,21 @@ static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src, dst_sector_offset = 0; #ifdef EXT_ENCRYPTED - if (wolfBoot_initialize_encryption() < 0) { + if (wolfBoot_initialize_encryption() < 0) return -1; - } - wolfBoot_get_encrypt_key(key, nonce); + wolfBoot_get_encrypt_key(key, nonce); if (src->part == PART_SWAP) iv_counter = dst_sector_offset; else + /* + * Always re-derive the IV starting from the source address. + * This guarantees we do not reuse the same IV in the SWAP partition. + */ iv_counter = src_sector_offset; - iv_counter /= ENCRYPT_BLOCK_SIZE; - crypto_set_iv(nonce, iv_counter); -#endif + wolfBoot_crypto_set_iv(nonce, iv_counter); +#endif /* EXT_ENCRYPTED */ #ifdef EXT_FLASH if (PART_IS_EXT(src)) { @@ -222,7 +226,6 @@ static int RAMFUNCTION wolfBoot_backup_last_boot_sector(uint32_t sector) wolfBoot_open_image(src, PART_BOOT); wolfBoot_open_image(dst, PART_SWAP); - wolfBoot_printf("Copy sector %d (part %d->%d)\n", sector, src->part, dst->part); @@ -234,7 +237,12 @@ static int RAMFUNCTION wolfBoot_backup_last_boot_sector(uint32_t sector) iv_counter /= ENCRYPT_BLOCK_SIZE; if (wolfBoot_initialize_encryption() < 0) return -1; - crypto_set_iv(nonce, iv_counter); + /* + * Preserve the IV sequence used by the source sector so that the staging + * copy in SWAP can be decrypted with exactly the same keystream when it is + * restored to BOOT. + */ + wolfBoot_crypto_set_iv(nonce, iv_counter); /* Erase swap space */ wb_flash_erase(dst, dst_sector_offset, WOLFBOOT_SECTOR_SIZE); @@ -523,7 +531,7 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, } iv_counter /= ENCRYPT_BLOCK_SIZE; /* Encrypt + send */ - crypto_set_iv(nonce, iv_counter); + wolfBoot_crypto_set_iv(nonce, iv_counter); crypto_encrypt(enc_blk, delta_blk, ret); wr_ret = ext_flash_write( (uint32_t)(WOLFBOOT_PARTITION_SWAP_ADDRESS + len), @@ -659,26 +667,54 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) uint16_t update_type; uint32_t fw_size; uint32_t size; + int inverse = 0; + int fallback_image = 0; #if defined(DISABLE_BACKUP) && defined(EXT_ENCRYPTED) uint8_t key[ENCRYPT_KEY_SIZE]; uint8_t nonce[ENCRYPT_NONCE_SIZE]; #endif #ifdef DELTA_UPDATES uint8_t st; - int inverse = 0; int resume = 0; int stateRet = -1; - uint32_t cur_v; - uint32_t up_v; #endif uint32_t cur_ver, upd_ver; wolfBoot_printf("Staring Update (fallback allowed %d)\n", fallback_allowed); /* No Safety check on open: we might be in the middle of a broken update */ - wolfBoot_open_image(&update, PART_UPDATE); - wolfBoot_open_image(&boot, PART_BOOT); - wolfBoot_open_image(&swap, PART_SWAP); + { + int update_open; +#ifdef EXT_ENCRYPTED + /* Start with the standard IV mapping for every fresh update attempt. */ + wolfBoot_enable_fallback_iv(0); +#endif + update_open = wolfBoot_open_image(&update, PART_UPDATE); +#ifdef EXT_ENCRYPTED + if (update_open < 0) { + int prev = wolfBoot_enable_fallback_iv(1); + (void)prev; + update_open = wolfBoot_open_image(&update, PART_UPDATE); + if (update_open < 0) { + wolfBoot_enable_fallback_iv(0); + return -1; + } + fallback_image = 1; + } + wolfBoot_enable_fallback_iv(fallback_image); +#else + if (update_open < 0) + return -1; +#endif + wolfBoot_open_image(&boot, PART_BOOT); + wolfBoot_open_image(&swap, PART_SWAP); + +#ifdef EXT_ENCRYPTED + wolfBoot_printf("Update partition fallback image: %d\n", fallback_image); + if (fallback_image) + inverse = 1; +#endif + } /* get total size */ total_size = wolfBoot_get_total_size(&boot, &update); @@ -705,12 +741,23 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) wolfBoot_printf("Invalid update size %u\n", update.fw_size); return -1; } - if (!update.hdr_ok - || (wolfBoot_verify_integrity(&update) < 0) - || (wolfBoot_verify_authenticity(&update) < 0)) { - wolfBoot_printf("Update verify failed: Hdr %d, Hash %d, Sig %d\n", - update.hdr_ok, update.sha_ok, update.signature_ok); - return -1; + if (!fallback_image) { + if (!update.hdr_ok + || (wolfBoot_verify_integrity(&update) < 0) + || (wolfBoot_verify_authenticity(&update) < 0)) { + wolfBoot_printf("Update verify failed: Hdr %d, Hash %d, Sig %d\n", + update.hdr_ok, update.sha_ok, update.signature_ok); + return -1; + } + } else { + /* + * When we recover an already-encrypted fallback image, the + * manifest still contains hashes computed with the original IV + * stream. Skip the redundant integrity/authenticity checks here + * and let the bootloader verify the restored image after the swap. + */ + update.sha_ok = 1; + update.signature_ok = 1; } PART_SANITY_CHECK(&update); @@ -731,12 +778,11 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) } #endif } + if (cur_ver > upd_ver) + inverse = 1; #ifdef DELTA_UPDATES if ((update_type & 0x00F0) == HDR_IMG_TYPE_DIFF) { - cur_v = wolfBoot_current_firmware_version(); - up_v = wolfBoot_update_firmware_version(); - inverse = cur_v >= up_v; /* if magic isn't set stateRet will be -1 but that means we're on a * fresh partition and aren't resuming */ stateRet = wolfBoot_get_partition_state(PART_UPDATE, &st); @@ -745,7 +791,7 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) * header we can't determine the direction by version numbers. instead * use the update partition state, updating means regular, new means * reverting */ - if ((stateRet == 0) && ((flag != SECT_FLAG_NEW) || (cur_v == 0))) { + if ((stateRet == 0) && ((flag != SECT_FLAG_NEW) || (cur_ver == 0))) { resume = 1; if (st == IMG_STATE_UPDATING) { inverse = 0; @@ -780,7 +826,6 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) #ifdef EXT_FLASH ext_flash_unlock(); #endif - /* Interruptible swap * The status is saved in the sector flags of the update partition. * If something goes wrong, the operation will be resumed upon reboot. @@ -800,7 +845,22 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) if (size > sector_size) size = sector_size; flag = SECT_FLAG_BACKUP; - wolfBoot_copy_sector(&boot, &update, sector); + { +#ifdef EXT_ENCRYPTED + /* + * When we are performing a fallback, force the alternate + * IV offset only for the segment copied from BOOT into + * UPDATE. All other copies see the offset that was + * active beforehand (0 for the normal path, fallback + * offset for the recovery path). + */ + int prev_iv = wolfBoot_enable_fallback_iv(1); +#endif + wolfBoot_copy_sector(&boot, &update, sector); +#ifdef EXT_ENCRYPTED + wolfBoot_enable_fallback_iv(prev_iv); +#endif + } if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE) wolfBoot_set_update_sector_flag(sector, flag); /* FALL THROUGH */ @@ -945,10 +1005,14 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) hal_flash_lock(); /* Save the encryption key after swapping */ - #ifdef EXT_ENCRYPTED +#ifdef EXT_ENCRYPTED wolfBoot_set_encrypt_key(key, nonce); - #endif +#endif #endif /* DISABLE_BACKUP */ +#ifdef EXT_ENCRYPTED + /* Make sure we leave the global IV offset in its normal state. */ + wolfBoot_enable_fallback_iv(0); +#endif return 0; } From 6fba3b405426386f73c983f4581b0131d5427505 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 17 Oct 2025 12:03:42 +0200 Subject: [PATCH 2/9] Fixed unit tests --- src/libwolfboot.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libwolfboot.c b/src/libwolfboot.c index ef196f1c1b..6c08380daf 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -1394,7 +1394,7 @@ int wolfBoot_fallback_is_possible(void) static uint8_t ENCRYPT_KEY[ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE]; #endif -#if defined(EXT_ENCRYPTED) && defined(__WOLFBOOT) +#if defined(EXT_ENCRYPTED) && (defined(__WOLFBOOT) || defined(UNIT_TEST)) int RAMFUNCTION wolfBoot_enable_fallback_iv(int enable) { int prev = 0; @@ -1425,7 +1425,7 @@ void RAMFUNCTION wolfBoot_crypto_set_iv(const uint8_t *nonce, uint32_t iv_counte /* Fallback IV offset is single-use; clear it once applied. */ encrypt_iv_offset = 0; } -#endif /* EXT_ENCRYPTED && __WOLFBOOT */ +#endif /* EXT_ENCRYPTED && (__WOLFBOOT || UNIT_TEST) */ static int RAMFUNCTION hal_set_key(const uint8_t *k, const uint8_t *nonce) { From 8cab4bc6121edba83145aa70e2548ad2b5bcf23b Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 17 Oct 2025 12:04:53 +0200 Subject: [PATCH 3/9] Footprint adjustment (ML_DSA +4B) --- tools/test.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/test.mk b/tools/test.mk index 6944b499a7..2025e5bf87 100644 --- a/tools/test.mk +++ b/tools/test.mk @@ -996,5 +996,5 @@ test-size-all: LIMIT=8322 NO_ARM_ASM=1 make keysclean make clean - make test-size SIGN=ML_DSA ML_DSA_LEVEL=2 LIMIT=18866 \ + make test-size SIGN=ML_DSA ML_DSA_LEVEL=2 LIMIT=18870 \ IMAGE_SIGNATURE_SIZE=2420 IMAGE_HEADER_SIZE?=8192 From b35f3b63341b5c8c668f68e04f5941905610eb7e Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 17 Oct 2025 12:11:32 +0200 Subject: [PATCH 4/9] Ensure version numbers are initialized before the inversion check --- src/update_flash.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/update_flash.c b/src/update_flash.c index e254487a81..872319366b 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -728,6 +728,9 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) */ update_type = wolfBoot_get_image_type(PART_UPDATE); + cur_ver = wolfBoot_current_firmware_version(); + upd_ver = wolfBoot_update_firmware_version(); + wolfBoot_get_update_sector_flag(0, &flag); /* Check the first sector to detect interrupted update */ if (flag == SECT_FLAG_NEW) { @@ -761,8 +764,6 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) } PART_SANITY_CHECK(&update); - cur_ver = wolfBoot_current_firmware_version(); - upd_ver = wolfBoot_update_firmware_version(); wolfBoot_printf("Versions: Current 0x%x, Update 0x%x\n", cur_ver, upd_ver); From 38631d248177e2deb6c99f7da99059fe85cbaca0 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 17 Oct 2025 13:16:31 +0200 Subject: [PATCH 5/9] Fixed regression in delta fallback --- src/update_flash.c | 1 + tools/test.mk | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/update_flash.c b/src/update_flash.c index 872319366b..68ef8d409d 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -814,6 +814,7 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) ext_flash_lock(); #endif hal_flash_lock(); + inverse = 1; } return wolfBoot_delta_update(&boot, &update, &swap, inverse, resume); diff --git a/tools/test.mk b/tools/test.mk index 2025e5bf87..1491ebe51e 100644 --- a/tools/test.mk +++ b/tools/test.mk @@ -996,5 +996,5 @@ test-size-all: LIMIT=8322 NO_ARM_ASM=1 make keysclean make clean - make test-size SIGN=ML_DSA ML_DSA_LEVEL=2 LIMIT=18870 \ + make test-size SIGN=ML_DSA ML_DSA_LEVEL=2 LIMIT=18880 \ IMAGE_SIGNATURE_SIZE=2420 IMAGE_HEADER_SIZE?=8192 From 8555ce4f3e73fcfed01b9929853fdb37d080c8a3 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 17 Oct 2025 14:02:23 +0200 Subject: [PATCH 6/9] Fix interrupted delta update fallback --- src/update_flash.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/update_flash.c b/src/update_flash.c index 68ef8d409d..62f863b869 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -486,7 +486,9 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, #endif if (inverse) { - if (((cur_v == upd_v) && (delta_base_v < cur_v)) || resume) { + if (resume || + ((cur_v == upd_v) && (delta_base_v <= cur_v)) || + ((cur_v == delta_base_v) && (upd_v >= cur_v))) { ret = wb_patch_init(&ctx, boot->hdr, boot->fw_size + IMAGE_HEADER_SIZE, update->hdr + *img_offset, *img_size); } else { @@ -792,19 +794,22 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) * header we can't determine the direction by version numbers. instead * use the update partition state, updating means regular, new means * reverting */ - if ((stateRet == 0) && ((flag != SECT_FLAG_NEW) || (cur_ver == 0))) { + if ((flag != SECT_FLAG_NEW) || (cur_ver == 0)) { resume = 1; - if (st == IMG_STATE_UPDATING) { - inverse = 0; - } - else { - inverse = 1; + if (stateRet == 0) { + if (st == IMG_STATE_UPDATING) { + inverse = 0; + } + else { + inverse = 1; + } } } /* If we're dealing with a "ping-pong" fallback that wasn't interrupted * we need to set to UPDATING, otherwise there's no way to tell the * original direction of the update once interrupted */ - else if ((inverse == 0) && (fallback_allowed == 1)) { + else if ((inverse == 0) && (fallback_allowed == 1) && + (cur_ver >= upd_ver)) { hal_flash_unlock(); #ifdef EXT_FLASH ext_flash_unlock(); From 61a0bf58d16ab7eaea39c84f4d3196f73440b2d1 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 17 Oct 2025 14:05:07 +0200 Subject: [PATCH 7/9] Added comments in delta update --- src/update_flash.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/update_flash.c b/src/update_flash.c index 62f863b869..98d463b50c 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -486,6 +486,8 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, #endif if (inverse) { + /* Fallback path: accept the delta when resuming or when the base image + * matches the recorded diff origin. */ if (resume || ((cur_v == upd_v) && (delta_base_v <= cur_v)) || ((cur_v == delta_base_v) && (upd_v >= cur_v))) { @@ -794,9 +796,13 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) * header we can't determine the direction by version numbers. instead * use the update partition state, updating means regular, new means * reverting */ + /* Any touched sector (or lack of recorded version) means we are + * recovering from an interrupted delta application. */ if ((flag != SECT_FLAG_NEW) || (cur_ver == 0)) { resume = 1; if (stateRet == 0) { + /* Partition trailer tells us whether we were mid-upgrade + * (UPDATING) or reverting an older image. */ if (st == IMG_STATE_UPDATING) { inverse = 0; } From 68ee02d36e21834a0206af887a6231337ed46e4d Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 17 Oct 2025 15:25:07 +0200 Subject: [PATCH 8/9] Improved unit test coverage --- tools/unit-tests/Makefile | 6 +- tools/unit-tests/unit-image.c | 14 +++++ tools/unit-tests/unit-update-flash.c | 88 ++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 3 deletions(-) diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index a2edc11b7b..b0775c7257 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -26,9 +26,9 @@ LDFLAGS+=-ftest-coverage TESTS:=unit-parser unit-extflash unit-aes128 unit-aes256 unit-chacha20 unit-pci \ - unit-mock-state unit-sectorflags unit-image unit-nvm unit-nvm-flagshome \ - unit-enc-nvm unit-enc-nvm-flagshome unit-delta unit-update-flash \ - unit-update-ram unit-pkcs11_store + unit-mock-state unit-sectorflags unit-image unit-nvm unit-nvm-flagshome \ + unit-enc-nvm unit-enc-nvm-flagshome unit-delta unit-update-flash \ + unit-update-ram unit-pkcs11_store all: $(TESTS) diff --git a/tools/unit-tests/unit-image.c b/tools/unit-tests/unit-image.c index 6b82ad64b8..81f5b2d02c 100644 --- a/tools/unit-tests/unit-image.c +++ b/tools/unit-tests/unit-image.c @@ -551,6 +551,20 @@ START_TEST(test_open_image) ck_assert_ptr_eq(img.fw_base, (uint8_t *)WOLFBOOT_PARTITION_UPDATE_ADDRESS + 256); + /* External helper should accept the same mapped header pointer */ + ret = wolfBoot_open_image_external(NULL, PART_UPDATE, + (uint8_t *)WOLFBOOT_PARTITION_UPDATE_ADDRESS); + ck_assert_int_eq(ret, -1); + + memset(&img, 0, sizeof(img)); + hdr_cpy_done = 0; + ret = wolfBoot_open_image_external(&img, PART_UPDATE, + (uint8_t *)WOLFBOOT_PARTITION_UPDATE_ADDRESS); + ck_assert_int_eq(ret, 0); + ck_assert_uint_eq(img.hdr_ok, 1); + ck_assert_ptr_eq(img.hdr, (void *)WOLFBOOT_PARTITION_UPDATE_ADDRESS); + ck_assert_ptr_eq(img.fw_base, (uint8_t *)WOLFBOOT_PARTITION_UPDATE_ADDRESS + + 256); } END_TEST diff --git a/tools/unit-tests/unit-update-flash.c b/tools/unit-tests/unit-update-flash.c index c6002412e9..a67bd92f98 100644 --- a/tools/unit-tests/unit-update-flash.c +++ b/tools/unit-tests/unit-update-flash.c @@ -48,6 +48,29 @@ const char *argv0; +static void reset_mock_stats(void); +static void prepare_flash(void); +static void cleanup_flash(void); + +START_TEST (test_boot_success_sets_state) +{ + uint8_t state = 0; + + reset_mock_stats(); + prepare_flash(); + hal_flash_unlock(); + wolfBoot_set_partition_state(PART_BOOT, IMG_STATE_TESTING); + hal_flash_lock(); + + wolfBoot_success(); + + ck_assert_int_eq(wolfBoot_get_partition_state(PART_BOOT, &state), 0); + ck_assert_uint_eq(state, IMG_STATE_SUCCESS); + + cleanup_flash(); +} +END_TEST + Suite *wolfboot_suite(void); int wolfBoot_staged_ok = 0; @@ -412,6 +435,62 @@ START_TEST (test_empty_boot_but_update_sha_corrupted_denied) { cleanup_flash(); } +START_TEST (test_swap_resume_noop) +{ + reset_mock_stats(); + prepare_flash(); + ext_flash_unlock(); + wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_NEW); + ext_flash_lock(); + ck_assert_int_eq(wolfBoot_swap_and_final_erase(1), -1); + cleanup_flash(); +} +END_TEST + +START_TEST (test_diffbase_version_reads) +{ + uint32_t word; + uint32_t version = 0x01020304; + uint32_t delta_base = 0x33445566; + uint16_t img_type = HDR_IMG_TYPE_AUTH | HDR_IMG_TYPE_APP; + + reset_mock_stats(); + prepare_flash(); + + ext_flash_unlock(); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS, + (const uint8_t *)"WOLF", 4); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 4, + (const uint8_t *)&version, sizeof(version)); + + word = (4u << 16) | HDR_VERSION; + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 8, + (const uint8_t *)&word, sizeof(word)); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 12, + (const uint8_t *)&version, sizeof(version)); + + word = (2u << 16) | HDR_IMG_TYPE; + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 16, + (const uint8_t *)&word, sizeof(word)); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 20, + (const uint8_t *)&img_type, sizeof(img_type)); + + word = (4u << 16) | HDR_IMG_DELTA_BASE; + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 24, + (const uint8_t *)&word, sizeof(word)); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 28, + (const uint8_t *)&delta_base, sizeof(delta_base)); + ext_flash_lock(); + + ck_assert_uint_eq(wolfBoot_get_diffbase_version(PART_UPDATE), delta_base); + ck_assert_uint_eq(wolfBoot_get_diffbase_version(PART_BOOT), 0); + ck_assert_uint_eq(wolfBoot_get_image_version(PART_UPDATE), version); + ck_assert_uint_eq(wolfBoot_get_image_type(PART_UPDATE), img_type); + + cleanup_flash(); +} +END_TEST + Suite *wolfboot_suite(void) { @@ -439,6 +518,9 @@ Suite *wolfboot_suite(void) TCase *emergency_rollback_failure_due_to_bad_update = tcase_create("Emergency rollback failure due to bad update"); TCase *empty_boot_partition_update = tcase_create("Empty boot partition update"); TCase *empty_boot_but_update_sha_corrupted_denied = tcase_create("Empty boot partition but update SHA corrupted"); + TCase *swap_resume = tcase_create("Swap resume noop"); + TCase *diffbase_version = tcase_create("Diffbase version lookup"); + TCase *boot_success = tcase_create("Boot success state"); @@ -456,6 +538,9 @@ Suite *wolfboot_suite(void) tcase_add_test(emergency_rollback_failure_due_to_bad_update, test_emergency_rollback_failure_due_to_bad_update); tcase_add_test(empty_boot_partition_update, test_empty_boot_partition_update); tcase_add_test(empty_boot_but_update_sha_corrupted_denied, test_empty_boot_but_update_sha_corrupted_denied); + tcase_add_test(swap_resume, test_swap_resume_noop); + tcase_add_test(diffbase_version, test_diffbase_version_reads); + tcase_add_test(boot_success, test_boot_success_sets_state); @@ -473,6 +558,9 @@ Suite *wolfboot_suite(void) suite_add_tcase(s, emergency_rollback_failure_due_to_bad_update); suite_add_tcase(s, empty_boot_partition_update); suite_add_tcase(s, empty_boot_but_update_sha_corrupted_denied); + suite_add_tcase(s, swap_resume); + suite_add_tcase(s, diffbase_version); + suite_add_tcase(s, boot_success); From 480f3106a761a6c3a9bdced1f529e7a1d94a7780 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Fri, 17 Oct 2025 15:45:27 +0200 Subject: [PATCH 9/9] Fix regression in encrypted delta updates --- src/update_flash.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/update_flash.c b/src/update_flash.c index 98d463b50c..07fac446f0 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -805,9 +805,11 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) * (UPDATING) or reverting an older image. */ if (st == IMG_STATE_UPDATING) { inverse = 0; - } - else { - inverse = 1; + } else { + if (cur_ver < upd_ver) + inverse = 1; + else + inverse = 0; } } } @@ -820,7 +822,7 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed) #ifdef EXT_FLASH ext_flash_unlock(); #endif - wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_UPDATING); + wolfBoot_set_partition_state(PART_UPDATE, IMG_STATE_NEW); #ifdef EXT_FLASH ext_flash_lock(); #endif