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.
pull/610/head
Daniele Lacamera 2025-09-19 13:01:28 +02:00
parent 68d28fc482
commit 18cbea59de
4 changed files with 149 additions and 42 deletions

View File

@ -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 */

View File

@ -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 */

View File

@ -62,18 +62,23 @@
#include <stddef.h> /* 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 <string.h>
#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

View File

@ -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;
}