mirror of https://github.com/wolfSSL/wolfBoot.git
Merge pull request #851 from danielinux/fenrir-fixes-2026-08-11
Fenrir fixes 2026-08 -11 + build regressions fixespull/852/head
commit
b1c2db191a
|
|
@ -784,6 +784,21 @@ jobs:
|
|||
arch: aarch64
|
||||
config-file: ./config/examples/zynqmp_sdcard.config
|
||||
|
||||
# Only build that compiles the DISK_ENCRYPT paths of src/update_disk.c.
|
||||
zynqmp_sdcard_encrypt_test:
|
||||
uses: ./.github/workflows/test-build-aarch64.yml
|
||||
with:
|
||||
arch: aarch64
|
||||
config-file: ./config/examples/zynqmp_sdcard.config
|
||||
make-args: ENCRYPT=1 ENCRYPT_WITH_AES256=1
|
||||
|
||||
zynqmp_sdcard_encrypt_chacha_test:
|
||||
uses: ./.github/workflows/test-build-aarch64.yml
|
||||
with:
|
||||
arch: aarch64
|
||||
config-file: ./config/examples/zynqmp_sdcard.config
|
||||
make-args: ENCRYPT=1 ENCRYPT_WITH_CHACHA=1
|
||||
|
||||
zynqmp_fsbl_test:
|
||||
uses: ./.github/workflows/test-build-aarch64.yml
|
||||
with:
|
||||
|
|
|
|||
|
|
@ -204,6 +204,8 @@ tools/unit-tests/unit-otp-keystore
|
|||
tools/unit-tests/unit-otp-keystore-gen-zeroize
|
||||
tools/unit-tests/unit-tpm-api-names
|
||||
tools/unit-tests/unit-tpm-nsc-cert
|
||||
tools/unit-tests/unit-tpm-advio-zeroize
|
||||
tools/unit-tests/unit-tpm-mfgid-eh-zeroize
|
||||
tools/unit-tests/unit-elf-bss-guard
|
||||
tools/unit-tests/unit-fit-fpga
|
||||
tools/unit-tests/unit-flash-erase-c0
|
||||
|
|
@ -211,17 +213,20 @@ tools/unit-tests/unit-flash-erase-g0
|
|||
tools/unit-tests/unit-flash-erase-l0
|
||||
tools/unit-tests/unit-flash-erase-u3
|
||||
tools/unit-tests/unit-flash-erase-wb
|
||||
tools/unit-tests/unit-flash-erase-mcxw
|
||||
tools/unit-tests/unit-fwtpm-nv-oob
|
||||
tools/unit-tests/unit-x86-paging-oob
|
||||
tools/unit-tests/unit-ahci-unlock-panic
|
||||
tools/unit-tests/unit-ata-security-passphrase-zeroize
|
||||
tools/unit-tests/unit-arm-tee-psa-ipc
|
||||
tools/unit-tests/unit-flash-write-mcxa
|
||||
tools/unit-tests/unit-flash-write-nrf52
|
||||
tools/unit-tests/unit-flash-write-same51
|
||||
tools/unit-tests/unit-flash-write-samr21
|
||||
tools/unit-tests/unit-image-elf-scatter
|
||||
tools/unit-tests/unit-image-hybrid
|
||||
tools/unit-tests/unit-imx-rt-cache-align
|
||||
tools/unit-tests/unit-update-disk-fit
|
||||
tools/unit-tests/unit-update-disk-oob
|
||||
tools/unit-tests/unit-update-ram-enc
|
||||
tools/unit-tests/unit-update-ram-enc-nopart
|
||||
|
|
|
|||
23
hal/mcxw.c
23
hal/mcxw.c
|
|
@ -22,6 +22,7 @@
|
|||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
*/
|
||||
|
||||
#ifndef WOLFBOOT_UNIT_TEST_FLASH_ERASE
|
||||
#include <stdint.h>
|
||||
#include <target.h>
|
||||
#include "image.h"
|
||||
|
|
@ -44,8 +45,11 @@
|
|||
/*!< Core clock frequency: 48000000Hz */
|
||||
#define BOARD_BOOTCLOCKRUN_CORE_CLOCK 48000000U
|
||||
static flash_config_t pflash;
|
||||
#endif /* !WOLFBOOT_UNIT_TEST_FLASH_ERASE */
|
||||
|
||||
static uint32_t pflash_sector_size = WOLFBOOT_SECTOR_SIZE;
|
||||
|
||||
#ifndef WOLFBOOT_UNIT_TEST_FLASH_ERASE
|
||||
uint32_t SystemCoreClock;
|
||||
|
||||
#ifdef TZEN
|
||||
|
|
@ -223,15 +227,26 @@ static void erase_flash_sector(uint32_t *dst) {
|
|||
/* Wait for completion */
|
||||
while (!(FMU0->FSTAT & 0x00000080)) {}
|
||||
}
|
||||
#endif /* !WOLFBOOT_UNIT_TEST_FLASH_ERASE */
|
||||
|
||||
int RAMFUNCTION hal_flash_erase(uint32_t address, int len)
|
||||
{
|
||||
if (address % pflash_sector_size)
|
||||
address -= address % pflash_sector_size;
|
||||
uint32_t sector_size = pflash_sector_size;
|
||||
|
||||
if (sector_size == 0U)
|
||||
sector_size = WOLFBOOT_SECTOR_SIZE;
|
||||
|
||||
/* Rounding the start down extends the range, so the length must grow by
|
||||
* the same amount or the last sector of the request is left unerased. */
|
||||
if (address % sector_size) {
|
||||
uint32_t offset = address % sector_size;
|
||||
address -= offset;
|
||||
len += (int)offset;
|
||||
}
|
||||
while (len > 0) {
|
||||
erase_flash_sector((uint32_t *)address);
|
||||
address += WOLFBOOT_SECTOR_SIZE;
|
||||
len -= WOLFBOOT_SECTOR_SIZE;
|
||||
address += sector_size;
|
||||
len -= (int)sector_size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
26
hal/nrf52.c
26
hal/nrf52.c
|
|
@ -73,25 +73,33 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
|
|||
|
||||
while (i < len) {
|
||||
if ((len - i > 3) && ((((address + i) & 0x03) == 0) && ((((uint32_t)data) + i) & 0x03) == 0)) {
|
||||
src = (uint32_t *)data;
|
||||
dst = (uint32_t *)address;
|
||||
/* Index by "i" directly: the condition above only guarantees
|
||||
* that "address + i" and "data + i" are word aligned, so
|
||||
* dst[i >> 2] off the unaligned base would address the wrong
|
||||
* word (and fault on a strict-alignment core). */
|
||||
src = (uint32_t *)(data + i);
|
||||
dst = (uint32_t *)(address + i);
|
||||
NVMC_CONFIG = NVMC_CONFIG_WEN;
|
||||
flash_wait_complete();
|
||||
dst[i >> 2] = src[i >> 2];
|
||||
*dst = *src;
|
||||
flash_wait_complete();
|
||||
i+=4;
|
||||
} else {
|
||||
uint32_t val;
|
||||
uint8_t *vbytes = (uint8_t *)(&val);
|
||||
int off = (address + i) - (((address + i) >> 2) << 2);
|
||||
dst = (uint32_t *)(address - off);
|
||||
val = dst[i >> 2];
|
||||
vbytes[off] = data[i];
|
||||
uint32_t off = ((address + i) % 4);
|
||||
dst = (uint32_t *)(address + i - off);
|
||||
val = *dst;
|
||||
while (off < 4) {
|
||||
if (i < len)
|
||||
vbytes[off++] = data[i++];
|
||||
else
|
||||
off++;
|
||||
}
|
||||
NVMC_CONFIG = NVMC_CONFIG_WEN;
|
||||
flash_wait_complete();
|
||||
dst[i >> 2] = val;
|
||||
*dst = val;
|
||||
flash_wait_complete();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -317,31 +317,39 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
|
|||
while (i < len) {
|
||||
if ((len - i > 3) && ((((address + i) & 0x03) == 0) &&
|
||||
((((uint32_t)data) + i) & 0x03) == 0)) {
|
||||
src = (uint32_t *)data;
|
||||
dst = (uint32_t *)address;
|
||||
/* Index by "i" directly: the condition above only guarantees
|
||||
* that "address + i" and "data + i" are word aligned, so
|
||||
* dst[i >> 2] off the unaligned base would address the wrong
|
||||
* word (and fault on a strict-alignment core). */
|
||||
src = (uint32_t *)(data + i);
|
||||
dst = (uint32_t *)(address + i);
|
||||
#if TZ_SECURE() || defined(TARGET_nrf5340_net)
|
||||
NVMC_CONFIG = NVMC_CONFIG_WEN;
|
||||
#endif
|
||||
NVMC_CONFIGNS = NVMC_CONFIG_WEN;
|
||||
while (NVMC_READY == 0);
|
||||
dst[i >> 2] = src[i >> 2];
|
||||
*dst = *src;
|
||||
while (NVMC_READY == 0);
|
||||
i+=4;
|
||||
} else {
|
||||
uint32_t val;
|
||||
uint8_t *vbytes = (uint8_t *)(&val);
|
||||
int off = (address + i) - (((address + i) >> 2) << 2);
|
||||
dst = (uint32_t *)(address - off);
|
||||
val = dst[i >> 2];
|
||||
vbytes[off] = data[i];
|
||||
uint32_t off = ((address + i) % 4);
|
||||
dst = (uint32_t *)(address + i - off);
|
||||
val = *dst;
|
||||
while (off < 4) {
|
||||
if (i < len)
|
||||
vbytes[off++] = data[i++];
|
||||
else
|
||||
off++;
|
||||
}
|
||||
#if TZ_SECURE() || defined(TARGET_nrf5340_net)
|
||||
NVMC_CONFIG = NVMC_CONFIG_WEN;
|
||||
#endif
|
||||
NVMC_CONFIGNS = NVMC_CONFIG_WEN;
|
||||
while (NVMC_READY == 0);
|
||||
dst[i >> 2] = val;
|
||||
*dst = val;
|
||||
while (NVMC_READY == 0);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -111,23 +111,31 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len)
|
|||
|
||||
while (i < len) {
|
||||
if ((len - i > 3) && ((((address + i) & 0x03) == 0) && ((((uint32_t)data) + i) & 0x03) == 0)) {
|
||||
src = (uint32_t *)data;
|
||||
dst = (uint32_t *)(address + FLASHMEM_ADDRESS_SPACE);
|
||||
/* Index by "i" directly: the condition above only guarantees
|
||||
* that "address + i" and "data + i" are word aligned, so
|
||||
* dst[i >> 2] off the unaligned base would address the wrong
|
||||
* word, and the Cortex-M0+ faults on the unaligned access. */
|
||||
src = (uint32_t *)(data + i);
|
||||
dst = (uint32_t *)(address + i + FLASHMEM_ADDRESS_SPACE);
|
||||
flash_wait_complete();
|
||||
dst[i >> 2] = src[i >> 2];
|
||||
*dst = *src;
|
||||
flash_wait_complete();
|
||||
i+=4;
|
||||
} else {
|
||||
uint32_t val;
|
||||
uint8_t *vbytes = (uint8_t *)(&val);
|
||||
int off = (address + i) - (((address + i) >> 2) << 2);
|
||||
dst = (uint32_t *)(address + FLASHMEM_ADDRESS_SPACE - off);
|
||||
val = dst[i >> 2];
|
||||
vbytes[off] = data[i];
|
||||
uint32_t off = ((address + i) % 4);
|
||||
dst = (uint32_t *)(address + FLASHMEM_ADDRESS_SPACE + i - off);
|
||||
val = *dst;
|
||||
while (off < 4) {
|
||||
if (i < len)
|
||||
vbytes[off++] = data[i++];
|
||||
else
|
||||
off++;
|
||||
}
|
||||
flash_wait_complete();
|
||||
dst[i >> 2] = val;
|
||||
*dst = val;
|
||||
flash_wait_complete();
|
||||
i++;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
|
|
|||
11
options.mk
11
options.mk
|
|
@ -1885,3 +1885,14 @@ endif
|
|||
# includers (test-app), where a self-referencing += would not terminate.
|
||||
AUX_WOLFCRYPT_OBJS_NEW:=$(filter-out $(WOLFCRYPT_OBJS),$(sort $(AUX_WOLFCRYPT_OBJS)))
|
||||
WOLFCRYPT_OBJS+=$(AUX_WOLFCRYPT_OBJS_NEW)
|
||||
|
||||
# Under WOLFSSL_ARMASM, chacha.c defers the block function to
|
||||
# wc_chacha_crypt_bytes(), which lives in the port. arch.mk adds the aes/sha
|
||||
# equivalents unconditionally; ChaCha is only selected here, so add it last.
|
||||
ifeq ($(ARCH),AARCH64)
|
||||
ifneq ($(NO_ARM_ASM),1)
|
||||
ifneq (,$(filter %/wolfcrypt/src/chacha.o,$(WOLFCRYPT_OBJS)))
|
||||
WOLFCRYPT_OBJS+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/port/arm/armv8-chacha-asm_c.o
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -107,9 +107,6 @@ int elf_load_image_mmu(uint8_t *image, uint32_t image_sz, uintptr_t *pentry,
|
|||
is_elf32 ? 32 : 64, is_le ? "little" : "big");
|
||||
#endif
|
||||
|
||||
/* set entry point */
|
||||
*pentry = GET_H64(entry);
|
||||
|
||||
/* programs */
|
||||
ph_offset = GET_H32(ph_offset);
|
||||
entry_size = GET_H16(ph_entry_size);
|
||||
|
|
@ -222,6 +219,11 @@ int elf_load_image_mmu(uint8_t *image, uint32_t image_sz, uintptr_t *pentry,
|
|||
#endif /* !ELF_PARSER */
|
||||
}
|
||||
|
||||
/* Publish the entry point only once every check above has passed: callers
|
||||
* fall back to the raw binary on failure and must not be left with a
|
||||
* partially validated ELF's declared entry. */
|
||||
*pentry = GET_H64(entry);
|
||||
|
||||
#ifdef DEBUG_ELF
|
||||
wolfBoot_printf("Entry point %p\r\n", (void*)*pentry);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -183,7 +183,10 @@ static uint32_t ext_cache;
|
|||
#endif
|
||||
|
||||
|
||||
#if defined(__WOLFBOOT) || defined(UNIT_TEST)
|
||||
/* EXT_ENCRYPTED is listed because the key-handling code below calls
|
||||
* ForceZero() unconditionally, including from the test-app build of this file,
|
||||
* where __WOLFBOOT is not defined. */
|
||||
#if defined(__WOLFBOOT) || defined(UNIT_TEST) || defined(EXT_ENCRYPTED)
|
||||
#define WOLFSSL_MISC_INCLUDED /* allow misc.c code to be inlined */
|
||||
#include <wolfssl/wolfcrypt/types.h>
|
||||
#include <wolfssl/wolfcrypt/wc_port.h>
|
||||
|
|
@ -2557,6 +2560,18 @@ static uint8_t RAMFUNCTION part_address(uintptr_t a)
|
|||
}
|
||||
|
||||
#ifdef EXT_FLASH
|
||||
|
||||
/* ENCRYPT_CACHE is staged one whole encryption block at a time, so the amount
|
||||
* written per pass must be a block multiple: a partial block at the end of a
|
||||
* pass would be written as stale cache content and would leave the address
|
||||
* unaligned, desynchronising the keystream from ext_flash_decrypt_read().
|
||||
* NVM_CACHE_SIZE defaults to WOLFBOOT_SECTOR_SIZE, which is always a multiple,
|
||||
* but it can be overridden. */
|
||||
#define ENCRYPT_STAGE_SIZE \
|
||||
((NVM_CACHE_SIZE) - ((NVM_CACHE_SIZE) % ENCRYPT_BLOCK_SIZE))
|
||||
typedef char wolfBoot_encrypt_stage_size_check[
|
||||
(ENCRYPT_STAGE_SIZE >= ENCRYPT_BLOCK_SIZE) ? 1 : -1];
|
||||
|
||||
/**
|
||||
* @brief Write encrypted data to an external flash.
|
||||
*
|
||||
|
|
@ -2576,7 +2591,7 @@ int RAMFUNCTION ext_flash_encrypt_write(uintptr_t address, const uint8_t *data,
|
|||
uint8_t block[ENCRYPT_BLOCK_SIZE];
|
||||
uint8_t enc_block[ENCRYPT_BLOCK_SIZE];
|
||||
uint32_t row_address = address, row_offset;
|
||||
int sz = len, i, step;
|
||||
int sz = len, i, step, ret;
|
||||
uint8_t part;
|
||||
uint32_t iv_counter = 0;
|
||||
#if defined(EXT_ENCRYPTED) && !defined(WOLFBOOT_SMALL_STACK) && \
|
||||
|
|
@ -2584,6 +2599,13 @@ int RAMFUNCTION ext_flash_encrypt_write(uintptr_t address, const uint8_t *data,
|
|||
uint8_t ENCRYPT_CACHE[NVM_CACHE_SIZE] XALIGNED_STACK(32);
|
||||
#endif
|
||||
|
||||
/* A zero-length request must not turn into a read-modify-write of the
|
||||
* containing block. */
|
||||
if (len < 0)
|
||||
return -1;
|
||||
if (len == 0)
|
||||
return 0;
|
||||
|
||||
row_offset = address & (ENCRYPT_BLOCK_SIZE - 1);
|
||||
if (row_offset != 0) {
|
||||
row_address = address & ~(ENCRYPT_BLOCK_SIZE - 1);
|
||||
|
|
@ -2616,27 +2638,61 @@ int RAMFUNCTION ext_flash_encrypt_write(uintptr_t address, const uint8_t *data,
|
|||
/* encrypt blocks */
|
||||
if (sz > len) {
|
||||
step = ENCRYPT_BLOCK_SIZE - row_offset;
|
||||
/* Never consume more than the caller provided */
|
||||
if (step > len)
|
||||
step = len;
|
||||
if (ext_flash_read(row_address, block, ENCRYPT_BLOCK_SIZE)
|
||||
!= ENCRYPT_BLOCK_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
XMEMCPY(block + row_offset, data, step);
|
||||
crypto_encrypt(enc_block, block, ENCRYPT_BLOCK_SIZE);
|
||||
ext_flash_write(row_address, enc_block, ENCRYPT_BLOCK_SIZE);
|
||||
ret = ext_flash_write(row_address, enc_block, ENCRYPT_BLOCK_SIZE);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
/* The request fits entirely within this block: nothing left to do */
|
||||
if (step == len)
|
||||
return ret;
|
||||
address += step;
|
||||
data += step;
|
||||
sz = len - step;
|
||||
}
|
||||
|
||||
/* encrypt remainder */
|
||||
/* encrypt remainder, staging at most one cache worth at a time */
|
||||
ret = 0;
|
||||
step = sz & ~(ENCRYPT_BLOCK_SIZE - 1);
|
||||
for (i = 0; i < step / ENCRYPT_BLOCK_SIZE; i++) {
|
||||
XMEMCPY(block, data + (ENCRYPT_BLOCK_SIZE * i), ENCRYPT_BLOCK_SIZE);
|
||||
crypto_encrypt(ENCRYPT_CACHE + (ENCRYPT_BLOCK_SIZE * i), block,
|
||||
ENCRYPT_BLOCK_SIZE);
|
||||
while (step > 0) {
|
||||
int chunk = step;
|
||||
if (chunk > (int)ENCRYPT_STAGE_SIZE)
|
||||
chunk = (int)ENCRYPT_STAGE_SIZE;
|
||||
for (i = 0; i < chunk / ENCRYPT_BLOCK_SIZE; i++) {
|
||||
XMEMCPY(block, data + (ENCRYPT_BLOCK_SIZE * i), ENCRYPT_BLOCK_SIZE);
|
||||
crypto_encrypt(ENCRYPT_CACHE + (ENCRYPT_BLOCK_SIZE * i), block,
|
||||
ENCRYPT_BLOCK_SIZE);
|
||||
}
|
||||
ret = ext_flash_write(address, ENCRYPT_CACHE, chunk);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
address += chunk;
|
||||
data += chunk;
|
||||
step -= chunk;
|
||||
}
|
||||
|
||||
return ext_flash_write(address, ENCRYPT_CACHE, step);
|
||||
/* Trailing bytes that do not fill a whole block. "address" is block
|
||||
* aligned here, so merge them into the block that already backs them,
|
||||
* the same way the unaligned head above is handled. */
|
||||
step = sz & (ENCRYPT_BLOCK_SIZE - 1);
|
||||
if (step > 0) {
|
||||
if (ext_flash_read(address, block, ENCRYPT_BLOCK_SIZE)
|
||||
!= ENCRYPT_BLOCK_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
XMEMCPY(block, data, step);
|
||||
crypto_encrypt(enc_block, block, ENCRYPT_BLOCK_SIZE);
|
||||
ret = ext_flash_write(address, enc_block, ENCRYPT_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2702,6 +2758,9 @@ int RAMFUNCTION ext_flash_decrypt_read(uintptr_t address, uint8_t *data, int len
|
|||
*/
|
||||
if (row_offset != 0) {
|
||||
unaligned_head_size = ENCRYPT_BLOCK_SIZE - row_offset;
|
||||
/* Never copy more than the caller asked for */
|
||||
if (unaligned_head_size > read_remaining)
|
||||
unaligned_head_size = read_remaining;
|
||||
if (ext_flash_read(row_address, block, ENCRYPT_BLOCK_SIZE)
|
||||
!= ENCRYPT_BLOCK_SIZE) {
|
||||
return -1;
|
||||
|
|
|
|||
|
|
@ -419,8 +419,8 @@ int spi_flash_write(uint32_t address, const void *data, int len)
|
|||
{
|
||||
int ret = 0;
|
||||
int remaining = len;
|
||||
uint32_t xferSz, page, pages;
|
||||
uintptr_t addr;
|
||||
uint32_t xferSz;
|
||||
uintptr_t addr = address;
|
||||
uint8_t* ptr = (uint8_t*)data;
|
||||
|
||||
#ifdef DEBUG_QSPI
|
||||
|
|
@ -437,21 +437,19 @@ int spi_flash_write(uint32_t address, const void *data, int len)
|
|||
return -1;
|
||||
}
|
||||
|
||||
/* write by page */
|
||||
pages = ((len + (FLASH_PAGE_SIZE-1)) / FLASH_PAGE_SIZE);
|
||||
for (page = 0; page < pages; page++) {
|
||||
/* write by page: the device's page program wraps within its own page, so
|
||||
* each transfer must terminate at the next page boundary */
|
||||
while (remaining > 0) {
|
||||
ret = qspi_write_enable();
|
||||
if (ret != 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
xferSz = (uint32_t)remaining;
|
||||
if (xferSz > FLASH_PAGE_SIZE) {
|
||||
xferSz = FLASH_PAGE_SIZE;
|
||||
xferSz = FLASH_PAGE_SIZE - ((uint32_t)addr % FLASH_PAGE_SIZE);
|
||||
if (xferSz > (uint32_t)remaining) {
|
||||
xferSz = (uint32_t)remaining;
|
||||
}
|
||||
|
||||
addr = address + (page * FLASH_PAGE_SIZE);
|
||||
|
||||
/* ------ Write Flash (page at a time) ------ */
|
||||
ret = qspi_transfer(QSPI_MODE_WRITE, FLASH_WRITE_CMD,
|
||||
addr, QSPI_ADDR_SZ, QSPI_DATA_MODE_SPI, /* Address */
|
||||
|
|
@ -463,7 +461,7 @@ int spi_flash_write(uint32_t address, const void *data, int len)
|
|||
#ifdef DEBUG_QSPI
|
||||
wolfBoot_printf("QSPI Flash Sector Write: "
|
||||
"Ret %d, Cmd 0x%x, Len %d, %p -> 0x%x\n",
|
||||
ret, FLASH_WRITE_CMD, xferSz, ptr, address);
|
||||
ret, FLASH_WRITE_CMD, xferSz, ptr, (uint32_t)addr);
|
||||
#endif
|
||||
if (ret != 0)
|
||||
break;
|
||||
|
|
@ -475,6 +473,7 @@ int spi_flash_write(uint32_t address, const void *data, int len)
|
|||
/* write disable is automatic */
|
||||
remaining -= (int)xferSz;
|
||||
ptr += xferSz;
|
||||
addr += xferSz;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
|
|||
11
src/tpm.c
11
src/tpm.c
|
|
@ -202,6 +202,11 @@ static int TPM2_IoCb(TPM2_CTX* ctx, const uint8_t* txBuf, uint8_t* rxBuf,
|
|||
/* On error make sure SPI is de-asserted */
|
||||
else {
|
||||
spi_xfer(SPI_CS_TPM, NULL, NULL, 0, 0);
|
||||
#ifdef WOLFTPM_ADV_IO
|
||||
/* don't leave the command (may hold an authValue) on the stack */
|
||||
TPM2_ForceZero(txBuf, sizeof(txBuf));
|
||||
TPM2_ForceZero(rxBuf, sizeof(rxBuf));
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
#else /* Send Entire Message - no wait states */
|
||||
|
|
@ -221,6 +226,10 @@ static int TPM2_IoCb(TPM2_CTX* ctx, const uint8_t* txBuf, uint8_t* rxBuf,
|
|||
wolfBoot_print_bin(buf, size);
|
||||
#endif
|
||||
}
|
||||
/* the staging buffers hold the raw command / response, which can carry
|
||||
* a plaintext authValue - wipe them like TPM2_TIS_Read/Write() do */
|
||||
TPM2_ForceZero(txBuf, sizeof(txBuf));
|
||||
TPM2_ForceZero(rxBuf, sizeof(rxBuf));
|
||||
#endif
|
||||
|
||||
return ret;
|
||||
|
|
@ -1459,6 +1468,8 @@ int CSME_NSE_API wolfBoot_tpm2_get_timestamp(WOLFTPM2_KEY* aik, GetTime_Out* get
|
|||
|
||||
wolfTPM2_UnsetAuth(&wolftpm_dev, 1);
|
||||
wolfTPM2_UnsetAuth(&wolftpm_dev, 0);
|
||||
/* EH authValue consumed; clear it from the stack */
|
||||
TPM2_ForceZero(&eh_handle, sizeof(eh_handle));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@
|
|||
defined(ENCRYPT_WITH_CHACHA)
|
||||
#define DISK_ENCRYPT
|
||||
#include "encrypt.h"
|
||||
#include <wolfssl/wolfcrypt/memory.h> /* wc_ForceZero */
|
||||
|
||||
/* Module-level storage for encryption nonce */
|
||||
static uint8_t disk_encrypt_nonce[ENCRYPT_NONCE_SIZE];
|
||||
|
|
@ -235,13 +236,13 @@ static int decrypt_header(const uint8_t *src, uint8_t *dst)
|
|||
|
||||
static void disk_crypto_clear(void)
|
||||
{
|
||||
ForceZero(disk_encrypt_key, sizeof(disk_encrypt_key));
|
||||
ForceZero(disk_encrypt_nonce, sizeof(disk_encrypt_nonce));
|
||||
wc_ForceZero(disk_encrypt_key, sizeof(disk_encrypt_key));
|
||||
wc_ForceZero(disk_encrypt_nonce, sizeof(disk_encrypt_nonce));
|
||||
}
|
||||
|
||||
static void disk_decrypted_header_clear(uint8_t *hdr)
|
||||
{
|
||||
ForceZero(hdr, IMAGE_HEADER_SIZE);
|
||||
wc_ForceZero(hdr, IMAGE_HEADER_SIZE);
|
||||
}
|
||||
|
||||
#endif /* DISK_ENCRYPT */
|
||||
|
|
@ -633,6 +634,10 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
dts_ptr, dts_addr, dts_size);
|
||||
if (wolfBoot_fit_memcpy(dts_addr, dts_ptr, dts_size) != 0) {
|
||||
wolfBoot_printf("FIT: failed to load DTS\r\n");
|
||||
#ifdef DISK_ENCRYPT
|
||||
disk_decrypted_header_clear(dec_hdr);
|
||||
disk_crypto_clear();
|
||||
#endif
|
||||
wolfBoot_panic();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -312,22 +312,37 @@ static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src,
|
|||
#define BUFFER_DECLARED
|
||||
static uint8_t buffer[FLASHBUFFER_SIZE] XALIGNED(4);
|
||||
#endif
|
||||
wb_flash_erase(dst, dst_sector_offset, WOLFBOOT_SECTOR_SIZE);
|
||||
if (wb_flash_erase(dst, dst_sector_offset, WOLFBOOT_SECTOR_SIZE) < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
while (pos < WOLFBOOT_SECTOR_SIZE) {
|
||||
if (src_sector_offset + pos <
|
||||
(src->fw_size + IMAGE_HEADER_SIZE + FLASHBUFFER_SIZE)) {
|
||||
/* bypass decryption, copy encrypted data into swap if its external */
|
||||
if (dst->part == PART_SWAP && SWAP_EXT) {
|
||||
ext_flash_read((uintptr_t)(src->hdr) + src_sector_offset + pos,
|
||||
(void *)buffer, FLASHBUFFER_SIZE);
|
||||
if (ext_flash_read((uintptr_t)(src->hdr) + src_sector_offset +
|
||||
pos,
|
||||
(void *)buffer, FLASHBUFFER_SIZE)
|
||||
!= FLASHBUFFER_SIZE) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
ext_flash_check_read((uintptr_t)(src->hdr) + src_sector_offset +
|
||||
pos,
|
||||
(void *)buffer, FLASHBUFFER_SIZE);
|
||||
if (ext_flash_check_read((uintptr_t)(src->hdr) +
|
||||
src_sector_offset + pos,
|
||||
(void *)buffer, FLASHBUFFER_SIZE)
|
||||
!= FLASHBUFFER_SIZE) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
wb_flash_write(dst, dst_sector_offset + pos, buffer,
|
||||
FLASHBUFFER_SIZE);
|
||||
if (wb_flash_write(dst, dst_sector_offset + pos, buffer,
|
||||
FLASHBUFFER_SIZE) < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
pos += FLASHBUFFER_SIZE;
|
||||
}
|
||||
|
|
@ -335,19 +350,24 @@ static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src,
|
|||
goto out;
|
||||
}
|
||||
#endif
|
||||
wb_flash_erase(dst, dst_sector_offset, WOLFBOOT_SECTOR_SIZE);
|
||||
if (wb_flash_erase(dst, dst_sector_offset, WOLFBOOT_SECTOR_SIZE) < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
while (pos < WOLFBOOT_SECTOR_SIZE) {
|
||||
if (src_sector_offset + pos < (src->fw_size + IMAGE_HEADER_SIZE +
|
||||
FLASHBUFFER_SIZE)) {
|
||||
uint8_t *orig = (uint8_t*)(src->hdr + src_sector_offset + pos);
|
||||
wb_flash_write(dst, dst_sector_offset + pos, orig, FLASHBUFFER_SIZE);
|
||||
if (wb_flash_write(dst, dst_sector_offset + pos, orig,
|
||||
FLASHBUFFER_SIZE) < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
pos += FLASHBUFFER_SIZE;
|
||||
}
|
||||
ret = pos;
|
||||
#if defined(EXT_FLASH) || defined(EXT_ENCRYPTED)
|
||||
out:
|
||||
#endif
|
||||
#ifdef EXT_ENCRYPTED
|
||||
wolfBoot_zeroize(key, sizeof(key));
|
||||
wolfBoot_zeroize(nonce, sizeof(nonce));
|
||||
|
|
@ -605,6 +625,7 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot,
|
|||
{
|
||||
int sector = 0;
|
||||
int ret;
|
||||
int copy_ret;
|
||||
uint8_t flag;
|
||||
uint8_t delta_blk[DELTA_BLOCK_SIZE];
|
||||
uint32_t *img_offset;
|
||||
|
|
@ -710,9 +731,11 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot,
|
|||
cur_v, delta_base_v);
|
||||
ret = -1;
|
||||
} else if (!resume && delta_base_hash &&
|
||||
wolfBoot_hardened_CT_compare(base_hash, delta_base_hash,
|
||||
base_hash_sz) != 0) {
|
||||
/* Wrong base image digest, cannot apply delta patch */
|
||||
((base_hash == NULL) ||
|
||||
(base_hash_sz != WOLFBOOT_SHA_DIGEST_SIZE) ||
|
||||
(wolfBoot_hardened_CT_compare(base_hash, delta_base_hash,
|
||||
WOLFBOOT_SHA_DIGEST_SIZE) != 0))) {
|
||||
/* Wrong or missing base image digest, cannot apply delta patch */
|
||||
wolfBoot_printf("Delta Base hash mismatch\n");
|
||||
ret = -1;
|
||||
} else {
|
||||
|
|
@ -775,7 +798,11 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot,
|
|||
}
|
||||
}
|
||||
if (flag == SECT_FLAG_SWAPPING) {
|
||||
wolfBoot_copy_sector(swap, boot, sector);
|
||||
copy_ret = wolfBoot_copy_sector(swap, boot, sector);
|
||||
if (copy_ret < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
flag = SECT_FLAG_UPDATED;
|
||||
if (((sector + 1) * WOLFBOOT_SECTOR_SIZE) < WOLFBOOT_PARTITION_SIZE)
|
||||
wolfBoot_set_update_sector_flag(sector, flag);
|
||||
|
|
@ -917,6 +944,7 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
|
|||
int bootStateRet = -1;
|
||||
uint8_t bootState = 0;
|
||||
#endif
|
||||
int copy_ret = 0;
|
||||
#if defined(DISABLE_BACKUP) && defined(EXT_ENCRYPTED)
|
||||
uint8_t key[ENCRYPT_KEY_SIZE];
|
||||
uint8_t nonce[ENCRYPT_NONCE_SIZE];
|
||||
|
|
@ -1123,7 +1151,9 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
|
|||
switch (flag) {
|
||||
case SECT_FLAG_NEW:
|
||||
flag = SECT_FLAG_SWAPPING;
|
||||
wolfBoot_copy_sector(&update, &swap, sector);
|
||||
copy_ret = wolfBoot_copy_sector(&update, &swap, sector);
|
||||
if (copy_ret < 0)
|
||||
break;
|
||||
if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE)
|
||||
wolfBoot_set_update_sector_flag(sector, flag);
|
||||
/* FALL THROUGH */
|
||||
|
|
@ -1143,11 +1173,13 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
|
|||
*/
|
||||
int prev_iv = wolfBoot_enable_fallback_iv(1);
|
||||
#endif
|
||||
wolfBoot_copy_sector(&boot, &update, sector);
|
||||
copy_ret = wolfBoot_copy_sector(&boot, &update, sector);
|
||||
#ifdef EXT_ENCRYPTED
|
||||
wolfBoot_enable_fallback_iv(prev_iv);
|
||||
#endif
|
||||
}
|
||||
if (copy_ret < 0)
|
||||
break;
|
||||
if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE)
|
||||
wolfBoot_set_update_sector_flag(sector, flag);
|
||||
/* FALL THROUGH */
|
||||
|
|
@ -1156,7 +1188,9 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
|
|||
if (size > sector_size)
|
||||
size = sector_size;
|
||||
flag = SECT_FLAG_UPDATED;
|
||||
wolfBoot_copy_sector(&swap, &boot, sector);
|
||||
copy_ret = wolfBoot_copy_sector(&swap, &boot, sector);
|
||||
if (copy_ret < 0)
|
||||
break;
|
||||
if (((sector + 1) * sector_size) < WOLFBOOT_PARTITION_SIZE)
|
||||
wolfBoot_set_update_sector_flag(sector, flag);
|
||||
break;
|
||||
|
|
@ -1165,6 +1199,20 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
|
|||
default:
|
||||
break;
|
||||
}
|
||||
if (copy_ret < 0) {
|
||||
/* A flash operation failed: do not advance any further, the
|
||||
* sector flags still describe the last completed step so the
|
||||
* swap can be resumed from there. */
|
||||
wolfBoot_printf("Sector %d copy failed, aborting swap\n", sector);
|
||||
#ifdef EXT_FLASH
|
||||
ext_flash_lock();
|
||||
#endif
|
||||
hal_flash_lock();
|
||||
#ifdef EXT_ENCRYPTED
|
||||
wolfBoot_enable_fallback_iv(0);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
sector++;
|
||||
|
||||
/* headers that can be in different positions depending on when the
|
||||
|
|
@ -1289,7 +1337,21 @@ static int RAMFUNCTION wolfBoot_update(int fallback_allowed)
|
|||
/* Directly copy the content of the UPDATE partition into the BOOT
|
||||
* partition. */
|
||||
while ((sector * sector_size) < total_size) {
|
||||
wolfBoot_copy_sector(&update, &boot, sector);
|
||||
copy_ret = wolfBoot_copy_sector(&update, &boot, sector);
|
||||
if (copy_ret < 0) {
|
||||
/* Never confirm a boot image that was not fully written. */
|
||||
wolfBoot_printf("Sector %d copy failed, aborting swap\n", sector);
|
||||
#ifdef EXT_FLASH
|
||||
ext_flash_lock();
|
||||
#endif
|
||||
hal_flash_lock();
|
||||
#ifdef EXT_ENCRYPTED
|
||||
wolfBoot_zeroize(key, sizeof(key));
|
||||
wolfBoot_zeroize(nonce, sizeof(nonce));
|
||||
wolfBoot_enable_fallback_iv(0);
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
sector++;
|
||||
}
|
||||
/* erase remainder of partition */
|
||||
|
|
|
|||
|
|
@ -273,6 +273,12 @@ void RAMFUNCTION wolfBoot_start(void)
|
|||
BENCHMARK_DECLARE();
|
||||
#ifdef WOLFBOOT_UBOOT_LEGACY
|
||||
uint8_t *image_ptr;
|
||||
/* uImage ih_ep, kept only when the entry point differs from the load
|
||||
* address (see the do_boot() entry override below). */
|
||||
uint32_t *uboot_entry = NULL;
|
||||
/* Set when a later stage (ELF/FIT) re-derives the load address and so
|
||||
* supplies its own entry point, which then wins over ih_ep. */
|
||||
int stage_entry_override = 0;
|
||||
#endif
|
||||
uint32_t *load_address = NULL;
|
||||
uint32_t *source_address = NULL;
|
||||
|
|
@ -506,14 +512,24 @@ backup_on_failure:
|
|||
os_image.fw_size);
|
||||
}
|
||||
#endif
|
||||
/* bootm relocates to ih_load but enters at ih_ep: kernels built
|
||||
* with a preamble ahead of the entry point set the two to
|
||||
* different addresses. Remember the entry point; ih_load remains
|
||||
* the relocation destination. */
|
||||
if ((ih_ep != 0) && (ih_ep != ih_load)) {
|
||||
uboot_entry = (uint32_t*)(uintptr_t)ih_ep;
|
||||
}
|
||||
} else {
|
||||
/* Linux PPC path: leave load_address alone, just advance it
|
||||
* past the header to match upstream behaviour. load_address is
|
||||
* a uint32_t*, so advance by BYTES, not words. */
|
||||
* a uint32_t*, so advance by BYTES, not words.
|
||||
* ih_ep is deliberately ignored here: with ih_load == 0 there is
|
||||
* no relocation destination to enter past, and upstream enters at
|
||||
* the payload start. A uImage built with "mkimage -a 0 -e <ep>"
|
||||
* is therefore entered at the header offset, not at ih_ep. */
|
||||
load_address = (uint32_t*)((uint8_t*)load_address +
|
||||
UBOOT_IMG_HDR_SZ);
|
||||
}
|
||||
(void)ih_ep; /* TODO: pass through to do_boot when ih_ep != ih_load */
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -549,6 +565,11 @@ backup_on_failure:
|
|||
(uintptr_t*)&load_address, NULL) != 0){
|
||||
wolfBoot_printf("Invalid elf, falling back to raw binary\n");
|
||||
}
|
||||
#ifdef WOLFBOOT_UBOOT_LEGACY
|
||||
else {
|
||||
stage_entry_override = 1;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef MMU
|
||||
|
|
@ -584,6 +605,9 @@ backup_on_failure:
|
|||
wolfBoot_panic();
|
||||
}
|
||||
load_address = new_load;
|
||||
#ifdef WOLFBOOT_UBOOT_LEGACY
|
||||
stage_entry_override = 1;
|
||||
#endif
|
||||
}
|
||||
#if defined(WOLFBOOT_ZYNQMP_FSBL) && defined(MMU)
|
||||
/* Load BL31 (ARM Trusted Firmware) to its DDR exec address. Its entry
|
||||
|
|
@ -650,6 +674,17 @@ backup_on_failure:
|
|||
}
|
||||
#endif /* MMU */
|
||||
|
||||
#ifdef WOLFBOOT_UBOOT_LEGACY
|
||||
/* Enter the uImage at ih_ep. Skipped if a later stage (ELF/FIT) re-derived
|
||||
* the load address, since that stage provides its own entry point. The
|
||||
* flag is tracked explicitly rather than by comparing load_address:
|
||||
* elf_load_image_mmu() publishes its entry point before it finishes
|
||||
* validating, so a rejected ELF also leaves load_address rewritten. */
|
||||
if ((uboot_entry != NULL) && !stage_entry_override) {
|
||||
load_address = uboot_entry;
|
||||
}
|
||||
#endif
|
||||
|
||||
wolfBoot_printf("Booting at %p\n", load_address);
|
||||
|
||||
#ifdef WOLFBOOT_ENABLE_WOLFHSM_CLIENT
|
||||
|
|
|
|||
|
|
@ -307,7 +307,7 @@ static void header_append_tag_u64(uint8_t *header, uint32_t *idx, uint16_t tag,
|
|||
/* Globals */
|
||||
static const char wolfboot_delta_file[] = "/tmp/wolfboot-delta.bin";
|
||||
|
||||
static struct {
|
||||
struct signing_key {
|
||||
ed25519_key ed;
|
||||
ed448_key ed4;
|
||||
ecc_key ecc;
|
||||
|
|
@ -315,7 +315,56 @@ static struct {
|
|||
LmsKey lms;
|
||||
XmssKey xmss;
|
||||
wc_MlDsaKey ml_dsa;
|
||||
} key;
|
||||
};
|
||||
|
||||
/* Hybrid signing keeps the primary and the secondary private key decoded at
|
||||
* the same time, so the two signers must not share the same storage. */
|
||||
static struct signing_key key;
|
||||
static struct signing_key key2;
|
||||
|
||||
static struct signing_key *key_obj(int secondary)
|
||||
{
|
||||
return secondary ? &key2 : &key;
|
||||
}
|
||||
|
||||
/* Run the algorithm specific (zeroizing) free on a decoded signing key. */
|
||||
/* Safe to call on an object that was never initialized, or twice: "key" and
|
||||
* "key2" are zero-initialized file-scope statics and every wolfCrypt free
|
||||
* below is NULL-checked and idempotent. load_key() has paths that never
|
||||
* initialize the object (--manual-sign, --sha-only, raw-public-key inputs) and
|
||||
* paths that already free it, so both cases do occur. */
|
||||
static void free_key(int sign, int secondary)
|
||||
{
|
||||
struct signing_key *k = key_obj(secondary);
|
||||
if (sign == SIGN_ED25519) {
|
||||
wc_ed25519_free(&k->ed);
|
||||
}
|
||||
else if (sign == SIGN_ED448) {
|
||||
wc_ed448_free(&k->ed4);
|
||||
}
|
||||
else if (sign == SIGN_ECC256 ||
|
||||
sign == SIGN_ECC384 ||
|
||||
sign == SIGN_ECC521) {
|
||||
wc_ecc_free(&k->ecc);
|
||||
}
|
||||
else if (sign == SIGN_RSA2048 ||
|
||||
sign == SIGN_RSA3072 ||
|
||||
sign == SIGN_RSA4096 ||
|
||||
sign == SIGN_RSAPSS2048 ||
|
||||
sign == SIGN_RSAPSS3072 ||
|
||||
sign == SIGN_RSAPSS4096) {
|
||||
wc_FreeRsaKey(&k->rsa);
|
||||
}
|
||||
else if (sign == SIGN_LMS) {
|
||||
wc_LmsKey_Free(&k->lms);
|
||||
}
|
||||
else if (sign == SIGN_XMSS) {
|
||||
wc_XmssKey_Free(&k->xmss);
|
||||
}
|
||||
else if (sign == SIGN_ML_DSA) {
|
||||
wc_MlDsaKey_Free(&k->ml_dsa);
|
||||
}
|
||||
}
|
||||
|
||||
struct cmd_options {
|
||||
int manual_sign;
|
||||
|
|
@ -443,6 +492,7 @@ static int load_key_ecc(int sign_type, uint32_t curve_sz, int curve_id,
|
|||
uint32_t idx;
|
||||
uint32_t qxSz = curve_sz;
|
||||
uint32_t qySz = curve_sz;
|
||||
struct signing_key *k = key_obj(secondary);
|
||||
|
||||
*pubkey_sz = curve_sz * 2;
|
||||
*pubkey = malloc(*pubkey_sz); /* assume malloc works */
|
||||
|
|
@ -450,7 +500,7 @@ static int load_key_ecc(int sign_type, uint32_t curve_sz, int curve_id,
|
|||
printf("Pubkey malloc error!\n");
|
||||
return -1;
|
||||
}
|
||||
initRet = ret = wc_ecc_init(&key.ecc);
|
||||
initRet = ret = wc_ecc_init(&k->ecc);
|
||||
if (CMD.manual_sign || CMD.sha_only) {
|
||||
/* raw (public x + public y) */
|
||||
if (*key_buffer_sz == (curve_sz * 2)) {
|
||||
|
|
@ -460,16 +510,16 @@ static int load_key_ecc(int sign_type, uint32_t curve_sz, int curve_id,
|
|||
else {
|
||||
if (ret == 0) {
|
||||
idx = 0;
|
||||
ret = wc_EccPublicKeyDecode(*key_buffer, &idx, &key.ecc,
|
||||
ret = wc_EccPublicKeyDecode(*key_buffer, &idx, &k->ecc,
|
||||
*key_buffer_sz);
|
||||
}
|
||||
|
||||
/* we could decode another type of key in auto so check */
|
||||
if (ret == 0 && key.ecc.dp->id != curve_id) {
|
||||
if (ret == 0 && k->ecc.dp->id != curve_id) {
|
||||
ret = -1;
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = wc_ecc_export_public_raw(&key.ecc,
|
||||
ret = wc_ecc_export_public_raw(&k->ecc,
|
||||
*pubkey, &qxSz, /* public x */
|
||||
*pubkey + curve_sz, &qySz /* public y */
|
||||
);
|
||||
|
|
@ -481,7 +531,7 @@ static int load_key_ecc(int sign_type, uint32_t curve_sz, int curve_id,
|
|||
memcpy(*pubkey, *key_buffer, *pubkey_sz);
|
||||
|
||||
if (ret == 0) {
|
||||
ret = wc_ecc_import_unsigned(&key.ecc,
|
||||
ret = wc_ecc_import_unsigned(&k->ecc,
|
||||
*key_buffer, /* public x */
|
||||
(*key_buffer) + curve_sz, /* public y */
|
||||
(*key_buffer) + (curve_sz * 2), /* private d */
|
||||
|
|
@ -497,15 +547,15 @@ static int load_key_ecc(int sign_type, uint32_t curve_sz, int curve_id,
|
|||
else {
|
||||
if (ret == 0) {
|
||||
idx = 0;
|
||||
ret = wc_EccPrivateKeyDecode(*key_buffer, &idx, &key.ecc,
|
||||
ret = wc_EccPrivateKeyDecode(*key_buffer, &idx, &k->ecc,
|
||||
*key_buffer_sz);
|
||||
}
|
||||
/* we could decode another type of key in auto so check */
|
||||
if (ret == 0 && key.ecc.dp->id != curve_id) {
|
||||
if (ret == 0 && k->ecc.dp->id != curve_id) {
|
||||
ret = -1;
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = wc_ecc_export_public_raw(&key.ecc,
|
||||
ret = wc_ecc_export_public_raw(&k->ecc,
|
||||
*pubkey, &qxSz, /* public x */
|
||||
*pubkey + curve_sz, &qySz /* public y */
|
||||
);
|
||||
|
|
@ -517,7 +567,7 @@ static int load_key_ecc(int sign_type, uint32_t curve_sz, int curve_id,
|
|||
}
|
||||
|
||||
if (ret != 0 && initRet == 0) {
|
||||
wc_ecc_free(&key.ecc);
|
||||
wc_ecc_free(&k->ecc);
|
||||
}
|
||||
if (ret != 0) {
|
||||
free(*pubkey);
|
||||
|
|
@ -549,6 +599,7 @@ static int load_key_rsa(int sign_type, uint32_t rsa_keysz, uint32_t rsa_pubkeysz
|
|||
int initRet = -1;
|
||||
uint32_t idx;
|
||||
uint32_t keySzOut = 0;
|
||||
struct signing_key *k = key_obj(secondary);
|
||||
|
||||
if (CMD.manual_sign || CMD.sha_only) {
|
||||
/* Allocate and copy pubkey instead of using key_buffer directly */
|
||||
|
|
@ -573,15 +624,15 @@ static int load_key_rsa(int sign_type, uint32_t rsa_keysz, uint32_t rsa_pubkeysz
|
|||
ret = 0;
|
||||
}
|
||||
else {
|
||||
initRet = ret = wc_InitRsaKey(&key.rsa, NULL);
|
||||
initRet = ret = wc_InitRsaKey(&k->rsa, NULL);
|
||||
if (ret == 0) {
|
||||
idx = 0;
|
||||
ret = wc_RsaPrivateKeyDecode(*key_buffer, &idx, &key.rsa,
|
||||
ret = wc_RsaPrivateKeyDecode(*key_buffer, &idx, &k->rsa,
|
||||
*key_buffer_sz);
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
ret = wc_RsaKeyToPublicDer(&key.rsa, *key_buffer, *key_buffer_sz);
|
||||
ret = wc_RsaKeyToPublicDer(&k->rsa, *key_buffer, *key_buffer_sz);
|
||||
}
|
||||
|
||||
if (ret > 0) {
|
||||
|
|
@ -592,7 +643,7 @@ static int load_key_rsa(int sign_type, uint32_t rsa_keysz, uint32_t rsa_pubkeysz
|
|||
printf("Pubkey malloc error!\n");
|
||||
ret = -1;
|
||||
if (initRet == 0) {
|
||||
wc_FreeRsaKey(&key.rsa);
|
||||
wc_FreeRsaKey(&k->rsa);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -601,11 +652,11 @@ static int load_key_rsa(int sign_type, uint32_t rsa_keysz, uint32_t rsa_pubkeysz
|
|||
}
|
||||
|
||||
if (ret == 0) {
|
||||
keySzOut = wc_RsaEncryptSize(&key.rsa);
|
||||
keySzOut = wc_RsaEncryptSize(&k->rsa);
|
||||
}
|
||||
|
||||
if (ret != 0 && initRet == 0) {
|
||||
wc_FreeRsaKey(&key.rsa);
|
||||
wc_FreeRsaKey(&k->rsa);
|
||||
}
|
||||
|
||||
if (ret == 0 || CMD.sign != SIGN_AUTO) {
|
||||
|
|
@ -636,6 +687,7 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
|
|||
word32 pub_sz = 0;
|
||||
int sign = CMD.sign;
|
||||
const char *key_file = CMD.key_file;
|
||||
struct signing_key *k = key_obj(secondary);
|
||||
|
||||
/* open and load key buffer */
|
||||
*key_buffer = NULL;
|
||||
|
|
@ -692,20 +744,20 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
|
|||
ret = 0;
|
||||
}
|
||||
else {
|
||||
initRet = ret = wc_ed25519_init(&key.ed);
|
||||
initRet = ret = wc_ed25519_init(&k->ed);
|
||||
if (ret == 0) {
|
||||
idx = 0;
|
||||
ret = wc_Ed25519PublicKeyDecode(*key_buffer, &idx,
|
||||
&key.ed, *key_buffer_sz);
|
||||
&k->ed, *key_buffer_sz);
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = wc_ed25519_export_public(&key.ed, *pubkey,
|
||||
ret = wc_ed25519_export_public(&k->ed, *pubkey,
|
||||
pubkey_sz);
|
||||
}
|
||||
|
||||
/* free key no matter what */
|
||||
if (initRet == 0)
|
||||
wc_ed25519_free(&key.ed);
|
||||
wc_ed25519_free(&k->ed);
|
||||
}
|
||||
}
|
||||
/* raw only */
|
||||
|
|
@ -713,15 +765,15 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
|
|||
memcpy(*pubkey, *key_buffer + ED25519_KEY_SIZE,
|
||||
KEYSTORE_PUBKEY_SIZE_ED25519);
|
||||
|
||||
initRet = ret = wc_ed25519_init(&key.ed);
|
||||
initRet = ret = wc_ed25519_init(&k->ed);
|
||||
if (ret == 0) {
|
||||
ret = wc_ed25519_import_private_key(*key_buffer,
|
||||
ED25519_KEY_SIZE, *pubkey, *pubkey_sz, &key.ed);
|
||||
ED25519_KEY_SIZE, *pubkey, *pubkey_sz, &k->ed);
|
||||
}
|
||||
|
||||
/* only free the key if we failed after allocating */
|
||||
if (ret != 0 && initRet == 0)
|
||||
wc_ed25519_free(&key.ed);
|
||||
wc_ed25519_free(&k->ed);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
|
|
@ -760,20 +812,20 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
|
|||
ret = 0;
|
||||
}
|
||||
else {
|
||||
initRet = ret = wc_ed448_init(&key.ed4);
|
||||
initRet = ret = wc_ed448_init(&k->ed4);
|
||||
if (ret == 0) {
|
||||
idx = 0;
|
||||
ret = wc_Ed448PublicKeyDecode(*key_buffer, &idx,
|
||||
&key.ed4, *key_buffer_sz);
|
||||
&k->ed4, *key_buffer_sz);
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = wc_ed448_export_public(&key.ed4, *pubkey,
|
||||
ret = wc_ed448_export_public(&k->ed4, *pubkey,
|
||||
pubkey_sz);
|
||||
}
|
||||
|
||||
/* free key no matter what */
|
||||
if (initRet == 0)
|
||||
wc_ed448_free(&key.ed4);
|
||||
wc_ed448_free(&k->ed4);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -782,15 +834,15 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
|
|||
memcpy(*pubkey, *key_buffer + ED448_KEY_SIZE,
|
||||
ED448_PUB_KEY_SIZE);
|
||||
|
||||
initRet = ret = wc_ed448_init(&key.ed4);
|
||||
initRet = ret = wc_ed448_init(&k->ed4);
|
||||
if (ret == 0) {
|
||||
ret = wc_ed448_import_private_key(*key_buffer,
|
||||
ED448_KEY_SIZE, *pubkey, *pubkey_sz, &key.ed4);
|
||||
ED448_KEY_SIZE, *pubkey, *pubkey_sz, &k->ed4);
|
||||
}
|
||||
|
||||
/* only free the key if we failed after allocating */
|
||||
if (ret != 0 && initRet == 0)
|
||||
wc_ed448_free(&key.ed4);
|
||||
wc_ed448_free(&k->ed4);
|
||||
}
|
||||
|
||||
if (ret != 0) {
|
||||
|
|
@ -935,7 +987,7 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
|
|||
* If both priv/pub are present:
|
||||
* - The first ?? bytes is the private key.
|
||||
* - The next 68 bytes is the public key. */
|
||||
ret = wc_XmssKey_GetPrivLen(&key.xmss, &priv_sz);
|
||||
ret = wc_XmssKey_GetPrivLen(&k->xmss, &priv_sz);
|
||||
if (ret != 0 || priv_sz <= 0) {
|
||||
printf("error: wc_XmssKey_GetPrivLen returned %d\n", ret);
|
||||
break;
|
||||
|
|
@ -977,7 +1029,7 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
|
|||
}
|
||||
FALL_THROUGH; /* we didn't solve the key, keep trying */
|
||||
case SIGN_ML_DSA:
|
||||
ret = wc_MlDsaKey_GetPubLen(&key.ml_dsa, (int *)&pub_sz);
|
||||
ret = wc_MlDsaKey_GetPubLen(&k->ml_dsa, (int *)&pub_sz);
|
||||
|
||||
if (ret != 0 || pub_sz <= 0) {
|
||||
printf("error: wc_MlDsaKey_GetPubLen returned %d\n", ret);
|
||||
|
|
@ -986,7 +1038,7 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
|
|||
|
||||
/* Get the ML-DSA private key length. This API returns
|
||||
* the public + private length. */
|
||||
ret = wc_MlDsaKey_GetPrivLen(&key.ml_dsa, (int*)&priv_sz);
|
||||
ret = wc_MlDsaKey_GetPrivLen(&k->ml_dsa, (int*)&priv_sz);
|
||||
|
||||
if (ret != 0 || priv_sz <= 0) {
|
||||
printf("error: wc_MlDsaKey_GetPrivLen returned %d\n", ret);
|
||||
|
|
@ -1007,7 +1059,7 @@ static uint8_t *load_key(uint8_t **key_buffer, uint32_t *key_buffer_sz,
|
|||
|
||||
if (*key_buffer_sz == (priv_sz + pub_sz)) {
|
||||
/* priv + pub */
|
||||
ret = wc_MlDsaKey_ImportPrivRaw(&key.ml_dsa, *key_buffer,
|
||||
ret = wc_MlDsaKey_ImportPrivRaw(&k->ml_dsa, *key_buffer,
|
||||
priv_sz);
|
||||
*pubkey_sz = pub_sz;
|
||||
*pubkey = malloc(*pubkey_sz);
|
||||
|
|
@ -1072,8 +1124,8 @@ static int sign_digest(int sign, int hash_algo,
|
|||
{
|
||||
int ret;
|
||||
WC_RNG rng;
|
||||
struct signing_key *k = key_obj(secondary);
|
||||
printf("Sign: %02x\n", sign >> 8);
|
||||
(void)secondary;
|
||||
|
||||
if ((ret = wc_InitRng(&rng)) != 0) {
|
||||
return ret;
|
||||
|
|
@ -1081,12 +1133,12 @@ static int sign_digest(int sign, int hash_algo,
|
|||
|
||||
if (sign == SIGN_ED25519) {
|
||||
ret = wc_ed25519_sign_msg(digest, digest_sz, signature,
|
||||
signature_sz, &key.ed);
|
||||
signature_sz, &k->ed);
|
||||
}
|
||||
else
|
||||
if (sign == SIGN_ED448) {
|
||||
ret = wc_ed448_sign_msg(digest, digest_sz, signature,
|
||||
signature_sz, &key.ed4, NULL, 0);
|
||||
signature_sz, &k->ed4, NULL, 0);
|
||||
}
|
||||
else
|
||||
if (sign == SIGN_ECC256 ||
|
||||
|
|
@ -1103,7 +1155,7 @@ static int sign_digest(int sign, int hash_algo,
|
|||
memset(signature, 0, *signature_sz);
|
||||
|
||||
mp_init(&r); mp_init(&s);
|
||||
ret = wc_ecc_sign_hash_ex(digest, digest_sz, &rng, &key.ecc,
|
||||
ret = wc_ecc_sign_hash_ex(digest, digest_sz, &rng, &k->ecc,
|
||||
&r, &s);
|
||||
if (ret == 0) {
|
||||
word32 rSz, sSz;
|
||||
|
|
@ -1139,7 +1191,7 @@ static int sign_digest(int sign, int hash_algo,
|
|||
enchash = buf;
|
||||
}
|
||||
ret = wc_RsaSSL_Sign(enchash, enchash_sz, signature, *signature_sz,
|
||||
&key.rsa, &rng);
|
||||
&k->rsa, &rng);
|
||||
if (ret > 0) {
|
||||
*signature_sz = ret;
|
||||
ret = 0;
|
||||
|
|
@ -1163,7 +1215,7 @@ static int sign_digest(int sign, int hash_algo,
|
|||
return -1;
|
||||
}
|
||||
ret = wc_RsaPSS_Sign(digest, digest_sz, signature, *signature_sz,
|
||||
hash_type, mgf, &key.rsa, &rng);
|
||||
hash_type, mgf, &k->rsa, &rng);
|
||||
if (ret > 0) {
|
||||
*signature_sz = ret;
|
||||
ret = 0;
|
||||
|
|
@ -1176,18 +1228,18 @@ static int sign_digest(int sign, int hash_algo,
|
|||
key_file = CMD.secondary_key_file;
|
||||
}
|
||||
/* Set the callbacks, so LMS can update the private key while signing */
|
||||
ret = wc_LmsKey_SetWriteCb(&key.lms, lms_write_key);
|
||||
ret = wc_LmsKey_SetWriteCb(&k->lms, lms_write_key);
|
||||
if (ret == 0) {
|
||||
ret = wc_LmsKey_SetReadCb(&key.lms, lms_read_key);
|
||||
ret = wc_LmsKey_SetReadCb(&k->lms, lms_read_key);
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = wc_LmsKey_SetContext(&key.lms, (void*)key_file);
|
||||
ret = wc_LmsKey_SetContext(&k->lms, (void*)key_file);
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = wc_LmsKey_Reload(&key.lms);
|
||||
ret = wc_LmsKey_Reload(&k->lms);
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = wc_LmsKey_Sign(&key.lms, signature, signature_sz, digest,
|
||||
ret = wc_LmsKey_Sign(&k->lms, signature, signature_sz, digest,
|
||||
digest_sz);
|
||||
}
|
||||
if (ret != 0) {
|
||||
|
|
@ -1200,25 +1252,25 @@ static int sign_digest(int sign, int hash_algo,
|
|||
if (secondary) {
|
||||
key_file = CMD.secondary_key_file;
|
||||
}
|
||||
ret = wc_XmssKey_Init(&key.xmss, NULL, INVALID_DEVID);
|
||||
ret = wc_XmssKey_Init(&k->xmss, NULL, INVALID_DEVID);
|
||||
/* Set the callbacks, so XMSS can update the private key while signing */
|
||||
if (ret == 0) {
|
||||
ret = wc_XmssKey_SetWriteCb(&key.xmss, xmss_write_key);
|
||||
ret = wc_XmssKey_SetWriteCb(&k->xmss, xmss_write_key);
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = wc_XmssKey_SetReadCb(&key.xmss, xmss_read_key);
|
||||
ret = wc_XmssKey_SetReadCb(&k->xmss, xmss_read_key);
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = wc_XmssKey_SetContext(&key.xmss, (void*)key_file);
|
||||
ret = wc_XmssKey_SetContext(&k->xmss, (void*)key_file);
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = wc_XmssKey_SetParamStr(&key.xmss, WOLFBOOT_XMSS_PARAMS);
|
||||
ret = wc_XmssKey_SetParamStr(&k->xmss, WOLFBOOT_XMSS_PARAMS);
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = wc_XmssKey_Reload(&key.xmss);
|
||||
ret = wc_XmssKey_Reload(&k->xmss);
|
||||
}
|
||||
if (ret == 0) {
|
||||
ret = wc_XmssKey_Sign(&key.xmss, signature, signature_sz, digest,
|
||||
ret = wc_XmssKey_Sign(&k->xmss, signature, signature_sz, digest,
|
||||
digest_sz);
|
||||
}
|
||||
if (ret != 0) {
|
||||
|
|
@ -1229,7 +1281,7 @@ static int sign_digest(int sign, int hash_algo,
|
|||
if (sign == SIGN_ML_DSA) {
|
||||
/* Nothing else to do, ready to sign. */
|
||||
if (ret == 0) {
|
||||
ret = wc_MlDsaKey_SignCtx(&key.ml_dsa, NULL, 0,
|
||||
ret = wc_MlDsaKey_SignCtx(&k->ml_dsa, NULL, 0,
|
||||
signature, signature_sz,
|
||||
digest, digest_sz, &rng);
|
||||
}
|
||||
|
|
@ -1465,6 +1517,7 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz,
|
|||
image_sz = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
fclose(f);
|
||||
f = NULL;
|
||||
|
||||
/* Append Magic header (spells 'WOLF') */
|
||||
header_append_u32(header, &header_idx, WOLFBOOT_MAGIC);
|
||||
|
|
@ -1524,26 +1577,26 @@ static int make_header_ex(int is_diff, uint8_t *pubkey, uint32_t pubkey_sz,
|
|||
ALIGN_8(header_idx);
|
||||
if (!base_hash) {
|
||||
fprintf(stderr, "Base hash for delta image not found.\n");
|
||||
exit(1);
|
||||
goto failure;
|
||||
}
|
||||
if (CMD.hash_algo == HASH_SHA256) {
|
||||
if (base_hash_sz != HDR_SHA256_LEN) {
|
||||
fprintf(stderr, "Invalid base hash size for SHA256.\n");
|
||||
exit(1);
|
||||
goto failure;
|
||||
}
|
||||
header_append_tag(header, &header_idx, HDR_IMG_DELTA_BASE_HASH,
|
||||
HDR_SHA256_LEN, base_hash);
|
||||
} else if (CMD.hash_algo == HASH_SHA384) {
|
||||
if (base_hash_sz != HDR_SHA384_LEN) {
|
||||
fprintf(stderr, "Invalid base hash size for SHA384.\n");
|
||||
exit(1);
|
||||
goto failure;
|
||||
}
|
||||
header_append_tag(header, &header_idx, HDR_IMG_DELTA_BASE_HASH,
|
||||
HDR_SHA384_LEN, base_hash);
|
||||
} else if (CMD.hash_algo == HASH_SHA3) {
|
||||
if (base_hash_sz != HDR_SHA3_384_LEN) {
|
||||
fprintf(stderr, "Invalid base hash size for SHA3-384.\n");
|
||||
exit(1);
|
||||
goto failure;
|
||||
}
|
||||
header_append_tag(header, &header_idx, HDR_IMG_DELTA_BASE_HASH,
|
||||
HDR_SHA3_384_LEN, base_hash);
|
||||
|
|
@ -2879,6 +2932,7 @@ static void set_signature_sizes(int secondary)
|
|||
int *sign = &CMD.sign;
|
||||
uint32_t suggested_sz = 0;
|
||||
char *env_image_header_size;
|
||||
struct signing_key *k = key_obj(secondary);
|
||||
if (secondary) {
|
||||
sz = &CMD.secondary_signature_sz;
|
||||
sign = &CMD.secondary_sign;
|
||||
|
|
@ -2965,12 +3019,12 @@ static void set_signature_sizes(int secondary)
|
|||
else
|
||||
lms_winternitz = atoi(lms_winternitz_str);
|
||||
|
||||
lms_ret = wc_LmsKey_Init(&key.lms, NULL, INVALID_DEVID);
|
||||
lms_ret = wc_LmsKey_Init(&k->lms, NULL, INVALID_DEVID);
|
||||
if (lms_ret != 0) {
|
||||
fprintf(stderr, "error: wc_LmsKey_Init returned %d\n", lms_ret);
|
||||
exit(1);
|
||||
}
|
||||
lms_ret = wc_LmsKey_SetParameters(&key.lms, lms_levels, lms_height,
|
||||
lms_ret = wc_LmsKey_SetParameters(&k->lms, lms_levels, lms_height,
|
||||
lms_winternitz);
|
||||
if (lms_ret != 0) {
|
||||
fprintf(stderr, "error: wc_LmsKey_SetParameters(%d, %d, %d)" \
|
||||
|
|
@ -2982,7 +3036,7 @@ static void set_signature_sizes(int secondary)
|
|||
printf("info: using LMS parameters: L%d-H%d-W%d\n", lms_levels,
|
||||
lms_height, lms_winternitz);
|
||||
|
||||
lms_ret = wc_LmsKey_GetSigLen(&key.lms, &sig_sz);
|
||||
lms_ret = wc_LmsKey_GetSigLen(&k->lms, &sig_sz);
|
||||
if (lms_ret != 0) {
|
||||
fprintf(stderr, "error: wc_LmsKey_GetSigLen returned %d\n",
|
||||
lms_ret);
|
||||
|
|
@ -3006,13 +3060,13 @@ static void set_signature_sizes(int secondary)
|
|||
|
||||
printf("info: using XMSS parameters: %s\n", xmss_params);
|
||||
|
||||
xmss_ret = wc_XmssKey_Init(&key.xmss, NULL, INVALID_DEVID);
|
||||
xmss_ret = wc_XmssKey_Init(&k->xmss, NULL, INVALID_DEVID);
|
||||
if (xmss_ret != 0) {
|
||||
fprintf(stderr, "error: wc_XmssKey_Init returned %d\n", xmss_ret);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
xmss_ret = wc_XmssKey_SetParamStr(&key.xmss, xmss_params);
|
||||
xmss_ret = wc_XmssKey_SetParamStr(&k->xmss, xmss_params);
|
||||
if (xmss_ret != 0) {
|
||||
fprintf(stderr, "error: wc_XmssKey_SetParamStr(%s)" \
|
||||
" returned %d\n", xmss_params, xmss_ret);
|
||||
|
|
@ -3020,7 +3074,7 @@ static void set_signature_sizes(int secondary)
|
|||
}
|
||||
|
||||
|
||||
xmss_ret = wc_XmssKey_GetSigLen(&key.xmss, &sig_sz);
|
||||
xmss_ret = wc_XmssKey_GetSigLen(&k->xmss, &sig_sz);
|
||||
if (xmss_ret != 0) {
|
||||
fprintf(stderr, "error: wc_XmssKey_GetSigLen returned %d\n",
|
||||
xmss_ret);
|
||||
|
|
@ -3042,13 +3096,13 @@ static void set_signature_sizes(int secondary)
|
|||
if (env_ml_dsa_level)
|
||||
ml_dsa_level = atoi(env_ml_dsa_level);
|
||||
|
||||
ml_dsa_ret = wc_MlDsaKey_Init(&key.ml_dsa, NULL, INVALID_DEVID);
|
||||
ml_dsa_ret = wc_MlDsaKey_Init(&k->ml_dsa, NULL, INVALID_DEVID);
|
||||
if (ml_dsa_ret != 0) {
|
||||
fprintf(stderr, "error: wc_MlDsaKey_Init returned %d\n", ml_dsa_ret);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ml_dsa_ret = wc_MlDsaKey_SetParams(&key.ml_dsa, ml_dsa_level);
|
||||
ml_dsa_ret = wc_MlDsaKey_SetParams(&k->ml_dsa, ml_dsa_level);
|
||||
if (ml_dsa_ret != 0) {
|
||||
fprintf(stderr, "error: wc_MlDsaKey_SetParamStr(%d)" \
|
||||
" returned %d\n", ml_dsa_level, ml_dsa_ret);
|
||||
|
|
@ -3057,7 +3111,7 @@ static void set_signature_sizes(int secondary)
|
|||
|
||||
printf("info: using ML-DSA parameters: %d\n", ml_dsa_level);
|
||||
|
||||
ml_dsa_ret = wc_MlDsaKey_GetSigLen(&key.ml_dsa, (int *)&sig_sz);
|
||||
ml_dsa_ret = wc_MlDsaKey_GetSigLen(&k->ml_dsa, (int *)&sig_sz);
|
||||
if (ml_dsa_ret != 0) {
|
||||
fprintf(stderr, "error: wc_MlDsaKey_GetSigLen returned %d\n",
|
||||
ml_dsa_ret);
|
||||
|
|
@ -3764,7 +3818,8 @@ int main(int argc, char** argv)
|
|||
} else {
|
||||
kbuf = load_key(&key_buffer, &key_buffer_sz, &pubkey, &pubkey_sz, 0);
|
||||
if (!kbuf) {
|
||||
exit(1);
|
||||
ret = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
} /* CMD.sign != NO_SIGN */
|
||||
|
||||
|
|
@ -3775,7 +3830,11 @@ int main(int argc, char** argv)
|
|||
DEBUG_PRINT("Loading secondary key\n");
|
||||
kbuf2 = load_key(&key_buffer2, &key_buffer_sz2, &pubkey2, &pubkey_sz2, 1);
|
||||
if (!kbuf2) {
|
||||
exit(1);
|
||||
/* Fall through to the tail cleanup: the primary raw key buffer is
|
||||
* still live and the primary key object is initialized, and
|
||||
* exiting here would scrub neither. */
|
||||
ret = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
printf("Creating hybrid signature\n");
|
||||
ret = make_hybrid_header(pubkey, pubkey_sz, CMD.image_file,
|
||||
|
|
@ -3801,39 +3860,16 @@ int main(int argc, char** argv)
|
|||
ret = base_diff(CMD.delta_base_file, pubkey, pubkey_sz, 16);
|
||||
}
|
||||
|
||||
cleanup:
|
||||
/* Add pubkey cleanup */
|
||||
if (pubkey)
|
||||
free(pubkey);
|
||||
|
||||
if (kbuf)
|
||||
zero_and_free(kbuf, key_buffer_sz);
|
||||
if (CMD.sign == SIGN_ED25519) {
|
||||
wc_ed25519_free(&key.ed);
|
||||
}
|
||||
else if (CMD.sign == SIGN_ED448) {
|
||||
wc_ed448_free(&key.ed4);
|
||||
}
|
||||
else if (CMD.sign == SIGN_ECC256 ||
|
||||
CMD.sign == SIGN_ECC384 ||
|
||||
CMD.sign == SIGN_ECC521) {
|
||||
wc_ecc_free(&key.ecc);
|
||||
}
|
||||
else if (CMD.sign == SIGN_RSA2048 ||
|
||||
CMD.sign == SIGN_RSA3072 ||
|
||||
CMD.sign == SIGN_RSA4096 ||
|
||||
CMD.sign == SIGN_RSAPSS2048 ||
|
||||
CMD.sign == SIGN_RSAPSS3072 ||
|
||||
CMD.sign == SIGN_RSAPSS4096) {
|
||||
wc_FreeRsaKey(&key.rsa);
|
||||
}
|
||||
else if (CMD.sign == SIGN_LMS) {
|
||||
wc_LmsKey_Free(&key.lms);
|
||||
}
|
||||
else if (CMD.sign == SIGN_XMSS) {
|
||||
wc_XmssKey_Free(&key.xmss);
|
||||
}
|
||||
else if (CMD.sign == SIGN_ML_DSA) {
|
||||
wc_MlDsaKey_Free(&key.ml_dsa);
|
||||
free_key(CMD.sign, 0);
|
||||
if (CMD.hybrid) {
|
||||
free_key(CMD.secondary_sign, 1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1185,52 +1185,52 @@ test-all: clean
|
|||
|
||||
|
||||
test-size-all:
|
||||
make test-size SIGN=NONE LIMIT=5072 NO_ARM_ASM=1
|
||||
make test-size SIGN=NONE LIMIT=5112 NO_ARM_ASM=1
|
||||
make keysclean
|
||||
make test-size SIGN=ED25519 LIMIT=12184 NO_ARM_ASM=1
|
||||
make test-size SIGN=ED25519 LIMIT=12224 NO_ARM_ASM=1
|
||||
make keysclean
|
||||
make test-size SIGN=ECC256 LIMIT=18880 NO_ARM_ASM=1
|
||||
make test-size SIGN=ECC256 LIMIT=18920 NO_ARM_ASM=1
|
||||
make clean
|
||||
make test-size SIGN=ECC256 NO_ASM=1 LIMIT=13912 NO_ARM_ASM=1
|
||||
make test-size SIGN=ECC256 NO_ASM=1 LIMIT=13952 NO_ARM_ASM=1
|
||||
make keysclean
|
||||
make test-size SIGN=RSA2048 LIMIT=11768 NO_ARM_ASM=1
|
||||
make test-size SIGN=RSA2048 LIMIT=11808 NO_ARM_ASM=1
|
||||
make clean
|
||||
make test-size SIGN=RSA2048 NO_ASM=1 LIMIT=12328 NO_ARM_ASM=1
|
||||
make test-size SIGN=RSA2048 NO_ASM=1 LIMIT=12368 NO_ARM_ASM=1
|
||||
make keysclean
|
||||
make test-size SIGN=RSA4096 LIMIT=12068 NO_ARM_ASM=1
|
||||
make test-size SIGN=RSA4096 LIMIT=12108 NO_ARM_ASM=1
|
||||
make clean
|
||||
make test-size SIGN=RSA4096 NO_ASM=1 LIMIT=12608 NO_ARM_ASM=1
|
||||
make test-size SIGN=RSA4096 NO_ASM=1 LIMIT=12648 NO_ARM_ASM=1
|
||||
make keysclean
|
||||
make test-size SIGN=ECC384 LIMIT=19564 NO_ARM_ASM=1
|
||||
make test-size SIGN=ECC384 LIMIT=19604 NO_ARM_ASM=1
|
||||
make clean
|
||||
make test-size SIGN=ECC384 NO_ASM=1 LIMIT=15260 NO_ARM_ASM=1
|
||||
make test-size SIGN=ECC384 NO_ASM=1 LIMIT=15300 NO_ARM_ASM=1
|
||||
make keysclean
|
||||
make test-size SIGN=ED448 LIMIT=14212 NO_ARM_ASM=1
|
||||
make test-size SIGN=ED448 LIMIT=14252 NO_ARM_ASM=1
|
||||
make keysclean
|
||||
make test-size SIGN=RSA3072 LIMIT=11908 NO_ARM_ASM=1
|
||||
make test-size SIGN=RSA3072 LIMIT=11948 NO_ARM_ASM=1
|
||||
make clean
|
||||
make test-size SIGN=RSA3072 NO_ASM=1 LIMIT=12436 NO_ARM_ASM=1
|
||||
make test-size SIGN=RSA3072 NO_ASM=1 LIMIT=12476 NO_ARM_ASM=1
|
||||
make keysclean
|
||||
make test-size SIGN=RSAPSS2048 LIMIT=13704 NO_ARM_ASM=1
|
||||
make test-size SIGN=RSAPSS2048 LIMIT=13744 NO_ARM_ASM=1
|
||||
make clean
|
||||
make test-size SIGN=RSAPSS2048 NO_ASM=1 LIMIT=14264 NO_ARM_ASM=1
|
||||
make test-size SIGN=RSAPSS2048 NO_ASM=1 LIMIT=14304 NO_ARM_ASM=1
|
||||
make keysclean
|
||||
make test-size SIGN=RSAPSS3072 LIMIT=13872 NO_ARM_ASM=1
|
||||
make test-size SIGN=RSAPSS3072 LIMIT=13912 NO_ARM_ASM=1
|
||||
make clean
|
||||
make test-size SIGN=RSAPSS3072 NO_ASM=1 LIMIT=14396 NO_ARM_ASM=1
|
||||
make test-size SIGN=RSAPSS3072 NO_ASM=1 LIMIT=14436 NO_ARM_ASM=1
|
||||
make keysclean
|
||||
make test-size SIGN=RSAPSS4096 LIMIT=14044 NO_ARM_ASM=1
|
||||
make test-size SIGN=RSAPSS4096 LIMIT=14084 NO_ARM_ASM=1
|
||||
make clean
|
||||
make test-size SIGN=RSAPSS4096 NO_ASM=1 LIMIT=14584 NO_ARM_ASM=1
|
||||
make test-size SIGN=RSAPSS4096 NO_ASM=1 LIMIT=14624 NO_ARM_ASM=1
|
||||
make keysclean
|
||||
make test-size SIGN=LMS LMS_LEVELS=2 LMS_HEIGHT=5 LMS_WINTERNITZ=8 \
|
||||
WOLFBOOT_SMALL_STACK=0 IMAGE_SIGNATURE_SIZE=2644 \
|
||||
IMAGE_HEADER_SIZE?=5288 LIMIT=8076 NO_ARM_ASM=1
|
||||
IMAGE_HEADER_SIZE?=5288 LIMIT=8116 NO_ARM_ASM=1
|
||||
make keysclean
|
||||
make test-size SIGN=XMSS XMSS_PARAMS='XMSS-SHA2_10_256' \
|
||||
IMAGE_SIGNATURE_SIZE=2500 IMAGE_HEADER_SIZE?=4096 \
|
||||
LIMIT=8728 NO_ARM_ASM=1
|
||||
LIMIT=8768 NO_ARM_ASM=1
|
||||
make keysclean
|
||||
make clean
|
||||
make test-size SIGN=ML_DSA ML_DSA_LEVEL=2 LIMIT=19538 \
|
||||
make test-size SIGN=ML_DSA ML_DSA_LEVEL=2 LIMIT=19578 \
|
||||
IMAGE_SIGNATURE_SIZE=2420 IMAGE_HEADER_SIZE?=8192
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ TESTS:=unit-parser unit-fdt unit-extflash unit-string unit-spi-flash unit-aes128
|
|||
unit-update-flash-hook \
|
||||
unit-update-flash-self-update \
|
||||
unit-update-flash-enc unit-update-ram unit-update-ram-uboot unit-update-ram-enc unit-update-ram-enc-nopart unit-update-ram-nofixed unit-update-ram-noramboot unit-update-flash-hwswap unit-pkcs11_store unit-psa_store unit-wolfhsm_flash_hal unit-disk \
|
||||
unit-update-disk unit-update-disk-oob unit-multiboot unit-boot-x86-fsp unit-loader-tpm-init unit-qspi-flash unit-fwtpm-stub unit-tpm-rsa-exp \
|
||||
unit-update-disk unit-update-disk-oob unit-update-disk-fit unit-multiboot unit-boot-x86-fsp unit-loader-tpm-init unit-qspi-flash unit-fwtpm-stub unit-tpm-rsa-exp \
|
||||
unit-image-nopart unit-image-sha384 unit-image-sha3-384 unit-store-sbrk \
|
||||
unit-tpm-blob unit-policy-create unit-policy-sign unit-rot-auth unit-sdhci-response-bits \
|
||||
unit-sdhci-disk-unaligned unit-sign-encrypted-output \
|
||||
|
|
@ -72,6 +72,8 @@ TESTS:=unit-parser unit-fdt unit-extflash unit-string unit-spi-flash unit-aes128
|
|||
TESTS+=unit-tpm-check-rot-auth
|
||||
TESTS+=unit-tpm-api-names
|
||||
TESTS+=unit-tpm-nsc-cert
|
||||
TESTS+=unit-tpm-advio-zeroize
|
||||
TESTS+=unit-tpm-mfgid-eh-zeroize
|
||||
TESTS+=unit-pkcs11-nsc-zeroize
|
||||
TESTS+=unit-diagnostics
|
||||
TESTS+=unit-diagnostics-256
|
||||
|
|
@ -84,6 +86,7 @@ TESTS+=unit-flash-erase-l0
|
|||
TESTS+=unit-flash-erase-g0
|
||||
TESTS+=unit-flash-erase-c0
|
||||
TESTS+=unit-flash-erase-u3
|
||||
TESTS+=unit-flash-erase-mcxw
|
||||
TESTS+=unit-otp-keystore
|
||||
TESTS+=unit-otp-keystore-gen-zeroize
|
||||
TESTS+=unit-x86-paging-oob
|
||||
|
|
@ -97,6 +100,7 @@ TESTS+=unit-dice-token-size
|
|||
TESTS+=unit-dice-token-nosign
|
||||
TESTS+=unit-va416x0-fram
|
||||
TESTS+=unit-flash-write-mcxa
|
||||
TESTS+=unit-flash-write-nrf52
|
||||
TESTS+=unit-flash-write-samr21
|
||||
TESTS+=unit-flash-write-same51
|
||||
TESTS+=unit-imx-rt-cache-align
|
||||
|
|
@ -139,6 +143,7 @@ run: $(TESTS)
|
|||
done
|
||||
python3 unit-sign-delta-tlv.py || exit 1
|
||||
python3 unit-sign-delta-cert-inv-off.py || exit 1
|
||||
python3 unit-sign-delta-basehash-cleanup.py || exit 1
|
||||
python3 unit-sign-custom-tlv-le.py || exit 1
|
||||
python3 unit-sign-custom-tlv-large.py || exit 1
|
||||
python3 unit-sign-custom-tlv-pubkey-der.py || exit 1
|
||||
|
|
@ -234,6 +239,12 @@ unit-update-disk:CFLAGS+=-DMOCK_PARTITIONS -DPRINTF_ENABLED -DWOLFBOOT_RAMBOOT_M
|
|||
unit-update-disk-oob:CFLAGS+=-DMOCK_PARTITIONS -DPRINTF_ENABLED \
|
||||
-DWOLFBOOT_RAMBOOT_MAX_SIZE=0x1000 \
|
||||
-DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE
|
||||
# FIT (flattened uImage tree) exits of the encrypted disk loader. The panic hook
|
||||
# is what lets the test observe the key material at the instant wolfBoot_panic()
|
||||
# is entered, since on target that call never returns.
|
||||
unit-update-disk-fit:CFLAGS+=-DMOCK_PARTITIONS -DPRINTF_ENABLED -DWOLFBOOT_FDT \
|
||||
-DWOLFBOOT_HOOK_PANIC -DWOLFBOOT_RAMBOOT_MAX_SIZE=0x40 \
|
||||
-DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE
|
||||
# Regression coverage for wolfBoot_check_flash_image_elf() (scattered-ELF
|
||||
# integrity check). WOLFBOOT_NO_SIGN keeps this to the hashing path only (no
|
||||
# signature verification is exercised by that function).
|
||||
|
|
@ -323,6 +334,20 @@ unit-tpm-blob: ../../include/target.h unit-tpm-blob.c
|
|||
-DWOLFBOOT_HASH_SHA256 \
|
||||
-ffunction-sections -fdata-sections $(LDFLAGS) -Wl,--gc-sections
|
||||
|
||||
unit-tpm-advio-zeroize: ../../include/target.h unit-tpm-advio-zeroize.c
|
||||
gcc -o $@ $^ $(CFLAGS) -I$(WOLFBOOT_LIB_WOLFTPM) -DWOLFBOOT_TPM \
|
||||
-DWOLFTPM_USER_SETTINGS -DWOLFTPM_ADV_IO \
|
||||
-DWOLFTPM_CHECK_WAIT_STATE -DWOLFBOOT_SIGN_RSA2048 \
|
||||
-DWOLFBOOT_HASH_SHA256 \
|
||||
-ffunction-sections -fdata-sections $(LDFLAGS) -Wl,--gc-sections
|
||||
|
||||
unit-tpm-mfgid-eh-zeroize: ../../include/target.h unit-tpm-mfgid-eh-zeroize.c
|
||||
gcc -o $@ $^ $(CFLAGS) -I$(WOLFBOOT_LIB_WOLFTPM) -DWOLFBOOT_TPM \
|
||||
-DWOLFTPM_USER_SETTINGS -DWOLFTPM_MFG_IDENTITY \
|
||||
-DWOLFBOOT_TPM_MFG_AUTH_DERIVE -DWOLFBOOT_SIGN_RSA2048 \
|
||||
-DWOLFBOOT_HASH_SHA256 -D__ARM_FEATURE_CMSE=3U -DCSME_NSE_API= \
|
||||
-ffunction-sections -fdata-sections $(LDFLAGS) -Wl,--gc-sections
|
||||
|
||||
unit-policy-create: ../../include/target.h unit-policy-create.c \
|
||||
$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/memory.c
|
||||
gcc -o $@ $^ -I../tpm $(CFLAGS) -I$(WOLFBOOT_LIB_WOLFTPM) -DWOLFBOOT_TPM \
|
||||
|
|
@ -449,6 +474,15 @@ unit-flash-erase-c0: unit-flash-erase-c0.c ../../hal/stm32c0.c
|
|||
unit-flash-erase-u3: unit-flash-erase-u3.c ../../hal/stm32u3.c ../../hal/stm32u3.h
|
||||
gcc -o $@ unit-flash-erase-u3.c -I../../ $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
# unit-flash-erase-mcxw includes hal/mcxw.c directly (guarded to hal_flash_erase
|
||||
# via WOLFBOOT_UNIT_TEST_FLASH_ERASE), so mcxw.c is not a separate input and the
|
||||
# (not vendored) NXP MCUXpresso SDK headers are not needed. The erase command
|
||||
# takes a uint32_t flash address as a pointer, which is only a narrowing cast on
|
||||
# the 64-bit host.
|
||||
unit-flash-erase-mcxw: unit-flash-erase-mcxw.c ../../hal/mcxw.c
|
||||
gcc -o $@ unit-flash-erase-mcxw.c -Wno-int-to-pointer-cast \
|
||||
$(CFLAGS) $(LDFLAGS)
|
||||
|
||||
# unit-otp-keystore includes src/flash_otp_keystore.c directly (guarded to its
|
||||
# host-portable code via WOLFBOOT_UNIT_TEST_OTP_KEYSTORE), so it is not a
|
||||
# separate input.
|
||||
|
|
@ -647,6 +681,9 @@ unit-update-disk: ../../include/target.h unit-update-disk.c
|
|||
unit-update-disk-oob: ../../include/target.h unit-update-disk-oob.c
|
||||
gcc -o $@ unit-update-disk-oob.c $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
unit-update-disk-fit: ../../include/target.h unit-update-disk-fit.c
|
||||
gcc -o $@ unit-update-disk-fit.c $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
unit-pkcs11_store: ../../include/target.h unit-pkcs11_store.c
|
||||
gcc -o $@ $(WOLFCRYPT_SRC) unit-pkcs11_store.c $(CFLAGS) $(WOLFCRYPT_CFLAGS) $(LDFLAGS)
|
||||
|
||||
|
|
@ -691,6 +728,9 @@ unit-ata-security-passphrase-zeroize: ../../include/target.h unit-ata-security-p
|
|||
unit-flash-write-mcxa: unit-flash-write-mcxa.c ../../hal/mcxa.c
|
||||
gcc -o $@ unit-flash-write-mcxa.c -Imcxa_fsl_stub $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
unit-flash-write-nrf52: unit-flash-write-nrf52.c ../../hal/nrf52.c
|
||||
gcc -o $@ unit-flash-write-nrf52.c -DTARGET_nrf52 -I../../hal $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
unit-flash-write-samr21: unit-flash-write-samr21.c ../../hal/samr21.c
|
||||
gcc -o $@ unit-flash-write-samr21.c $(CFLAGS) $(LDFLAGS)
|
||||
|
||||
|
|
|
|||
|
|
@ -96,6 +96,9 @@ uint8_t flash[FLASH_SIZE];
|
|||
int ext_flash_read(uintptr_t address, uint8_t *data, int len) {
|
||||
printf("Called ext_flash_read %p %p %d\n", (void *)address, (void *)data, len);
|
||||
|
||||
/* A negative length is never a valid request */
|
||||
ck_assert_int_ge(len, 0);
|
||||
|
||||
/* Check that the read address and size are within the bounds of the flash memory */
|
||||
ck_assert_int_le(address + len, FLASH_SIZE);
|
||||
|
||||
|
|
@ -108,6 +111,9 @@ int ext_flash_read(uintptr_t address, uint8_t *data, int len) {
|
|||
int ext_flash_write(uintptr_t address, const uint8_t *data, int len) {
|
||||
printf("Called ext_flash_write %p %p %d\n", (void *)address, (const void *)data, len);
|
||||
|
||||
/* A negative length is never a valid request */
|
||||
ck_assert_int_ge(len, 0);
|
||||
|
||||
|
||||
/* Check that the write address and size are within the bounds of the flash memory */
|
||||
ck_assert_int_le(address + len, FLASH_SIZE);
|
||||
|
|
@ -260,6 +266,112 @@ START_TEST(test_ext_enc_flash_operations) {
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_ext_enc_flash_short_unaligned_read) {
|
||||
uint32_t address = 0x1000;
|
||||
uint32_t size = 64;
|
||||
uint8_t data[64];
|
||||
uint8_t dataw[64];
|
||||
/* Reads shorter than the remainder of the encryption block they start in:
|
||||
* { offset within the block, number of bytes requested } */
|
||||
static const int cases[][2] = { {1, 1}, {4, 4}, {8, 3}, {15, 1} };
|
||||
unsigned int c;
|
||||
int i, rres, wres;
|
||||
|
||||
memcpy(dataw, test_buffer, size);
|
||||
wres = ext_flash_check_write(address, dataw, size);
|
||||
ck_assert_int_eq(wres, 0);
|
||||
|
||||
for (c = 0; c < sizeof(cases) / sizeof(cases[0]); c++) {
|
||||
int off = cases[c][0];
|
||||
int len = cases[c][1];
|
||||
|
||||
memset(data, 0xA5, sizeof(data));
|
||||
rres = ext_flash_check_read(address + off, data, len);
|
||||
ck_assert_int_eq(rres, len);
|
||||
ck_assert_mem_eq(data, test_buffer + off, len);
|
||||
|
||||
/* No byte past the requested length may be written */
|
||||
for (i = len; i < (int)sizeof(data); i++)
|
||||
ck_assert_uint_eq(data[i], 0xA5);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* This test is also built without EXT_ENCRYPTED, where there is no block size */
|
||||
#ifdef ENCRYPT_BLOCK_SIZE
|
||||
#define TEST_BLOCK_SIZE ENCRYPT_BLOCK_SIZE
|
||||
#else
|
||||
#define TEST_BLOCK_SIZE 16
|
||||
#endif
|
||||
|
||||
START_TEST(test_ext_enc_flash_short_unaligned_write) {
|
||||
uint32_t address = 0x1000;
|
||||
uint8_t data[TEST_BLOCK_SIZE];
|
||||
uint8_t dataw[TEST_BLOCK_SIZE];
|
||||
/* Writes shorter than the remainder of the encryption block they start in:
|
||||
* { offset within the block, number of bytes provided } */
|
||||
static const int cases[][2] = { {1, 1}, {0, 4}, {8, 3},
|
||||
{TEST_BLOCK_SIZE - 1, 1} };
|
||||
unsigned int c;
|
||||
int i, rres, wres;
|
||||
|
||||
/* Prime the target block with known content */
|
||||
memcpy(dataw, test_buffer, TEST_BLOCK_SIZE);
|
||||
wres = ext_flash_check_write(address, dataw, TEST_BLOCK_SIZE);
|
||||
ck_assert_int_eq(wres, 0);
|
||||
|
||||
for (c = 0; c < sizeof(cases) / sizeof(cases[0]); c++) {
|
||||
int off = cases[c][0];
|
||||
int len = cases[c][1];
|
||||
int tail = TEST_BLOCK_SIZE - (off + len);
|
||||
|
||||
/* Payload followed by a guard pattern that must never be consumed */
|
||||
memset(dataw, 0x5A, sizeof(dataw));
|
||||
for (i = 0; i < len; i++)
|
||||
dataw[i] = (uint8_t)(0xC0 + i);
|
||||
|
||||
wres = ext_flash_check_write(address + off, dataw, len);
|
||||
ck_assert_int_eq(wres, 0);
|
||||
|
||||
rres = ext_flash_check_read(address + off, data, len);
|
||||
ck_assert_int_eq(rres, len);
|
||||
ck_assert_mem_eq(data, dataw, len);
|
||||
|
||||
/* Bytes past the requested length must not have been taken from the
|
||||
* caller's buffer */
|
||||
if (tail > 0) {
|
||||
rres = ext_flash_check_read(address + off + len, data, tail);
|
||||
ck_assert_int_eq(rres, tail);
|
||||
for (i = 0; i < tail; i++) {
|
||||
if (data[i] != 0x5A)
|
||||
break;
|
||||
}
|
||||
ck_assert_int_lt(i, tail);
|
||||
}
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
/* A single request longer than the staging cache must not overrun it */
|
||||
START_TEST(test_ext_enc_flash_oversized_write) {
|
||||
uint32_t address = 0x1000;
|
||||
static uint8_t dataw[3 * WOLFBOOT_SECTOR_SIZE];
|
||||
static uint8_t data[3 * WOLFBOOT_SECTOR_SIZE];
|
||||
int i, rres, wres;
|
||||
|
||||
for (i = 0; i < (int)sizeof(dataw); i++)
|
||||
dataw[i] = (uint8_t)(i ^ (i >> 8));
|
||||
|
||||
wres = ext_flash_check_write(address, dataw, sizeof(dataw));
|
||||
ck_assert_int_eq(wres, 0);
|
||||
|
||||
memset(data, 0xA5, sizeof(data));
|
||||
rres = ext_flash_check_read(address, data, sizeof(data));
|
||||
ck_assert_int_eq(rres, (int)sizeof(data));
|
||||
ck_assert_mem_eq(data, dataw, sizeof(dataw));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
|
||||
Suite *wolfboot_suite(void)
|
||||
|
|
@ -271,15 +383,30 @@ Suite *wolfboot_suite(void)
|
|||
/* Test cases */
|
||||
TCase *ext_flash_operations = tcase_create("External flash operations: API");
|
||||
TCase *ext_enc_flash_operations = tcase_create("External encrypted flash operations");
|
||||
TCase *ext_enc_flash_short_read = tcase_create("External encrypted flash short unaligned read");
|
||||
TCase *ext_enc_flash_short_write = tcase_create("External encrypted flash short unaligned write");
|
||||
TCase *ext_enc_flash_oversized_write = tcase_create("External encrypted flash oversized write");
|
||||
|
||||
/* Set parameters + add to suite */
|
||||
tcase_add_test(ext_flash_operations, test_ext_flash_operations);
|
||||
tcase_add_test(ext_enc_flash_operations, test_ext_enc_flash_operations);
|
||||
tcase_add_test(ext_enc_flash_short_read,
|
||||
test_ext_enc_flash_short_unaligned_read);
|
||||
tcase_add_test(ext_enc_flash_short_write,
|
||||
test_ext_enc_flash_short_unaligned_write);
|
||||
tcase_add_test(ext_enc_flash_oversized_write,
|
||||
test_ext_enc_flash_oversized_write);
|
||||
|
||||
tcase_set_timeout(ext_flash_operations, 20);
|
||||
tcase_set_timeout(ext_enc_flash_operations, 20);
|
||||
tcase_set_timeout(ext_enc_flash_short_read, 20);
|
||||
tcase_set_timeout(ext_enc_flash_short_write, 20);
|
||||
tcase_set_timeout(ext_enc_flash_oversized_write, 20);
|
||||
suite_add_tcase(s, ext_flash_operations);
|
||||
suite_add_tcase(s, ext_enc_flash_operations);
|
||||
suite_add_tcase(s, ext_enc_flash_short_read);
|
||||
suite_add_tcase(s, ext_enc_flash_short_write);
|
||||
suite_add_tcase(s, ext_enc_flash_oversized_write);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,182 @@
|
|||
/* unit-flash-erase-mcxw.c
|
||||
*
|
||||
* Unit tests for the sector stride in hal_flash_erase() (hal/mcxw.c).
|
||||
* Regression for F-7383: the start address was rounded down with the runtime
|
||||
* pflash_sector_size (queried from FLASH_GetProperty() in hal_init()) while
|
||||
* the loop stepped address/len by the compile-time WOLFBOOT_SECTOR_SIZE.
|
||||
* When the two differ, a larger WOLFBOOT_SECTOR_SIZE steps over hardware
|
||||
* sectors inside the requested range, leaving them unerased.
|
||||
* hal/mcxn.c already uses one consistent sector_size, with a zero guard.
|
||||
*
|
||||
* Copyright (C) 2026 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfBoot.
|
||||
*
|
||||
* wolfBoot is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfBoot is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
*/
|
||||
|
||||
#include <check.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* hal/mcxw.c is tightly coupled to the MCXW FMU registers and to the (not
|
||||
* vendored) NXP MCUXpresso SDK headers. Compile only hal_flash_erase() in
|
||||
* isolation by defining this guard; everything else is excluded and replaced
|
||||
* below. */
|
||||
#define WOLFBOOT_UNIT_TEST_FLASH_ERASE
|
||||
|
||||
/* RAMFUNCTION must be empty on the host */
|
||||
#define RAMFUNCTION
|
||||
|
||||
/* Same value as config/examples/mcxw.config */
|
||||
#define WOLFBOOT_SECTOR_SIZE 0x2000
|
||||
|
||||
/* Record every sector-erase command issued by hal_flash_erase(). */
|
||||
#define ERASE_LOG_MAX 64
|
||||
static uint32_t erase_addr[ERASE_LOG_MAX];
|
||||
static int erase_log_n;
|
||||
|
||||
static void erase_flash_sector(uint32_t *dst)
|
||||
{
|
||||
if (erase_log_n < ERASE_LOG_MAX)
|
||||
erase_addr[erase_log_n] = (uint32_t)(uintptr_t)dst;
|
||||
erase_log_n++;
|
||||
}
|
||||
|
||||
#include "../../hal/mcxw.c"
|
||||
|
||||
#define FLASH_BASE 0x00008000UL
|
||||
|
||||
static void reset_mocks(uint32_t sector_size)
|
||||
{
|
||||
erase_log_n = 0;
|
||||
pflash_sector_size = sector_size;
|
||||
}
|
||||
|
||||
/* Baseline: runtime and compile-time sector size agree (the stock mcxw
|
||||
* configuration), so two sectors take exactly two erase commands. */
|
||||
START_TEST(test_erase_two_sectors_matching_size)
|
||||
{
|
||||
reset_mocks(WOLFBOOT_SECTOR_SIZE);
|
||||
ck_assert_int_eq(hal_flash_erase(FLASH_BASE, 2 * WOLFBOOT_SECTOR_SIZE), 0);
|
||||
|
||||
ck_assert_int_eq(erase_log_n, 2);
|
||||
ck_assert_uint_eq(erase_addr[0], FLASH_BASE);
|
||||
ck_assert_uint_eq(erase_addr[1], FLASH_BASE + WOLFBOOT_SECTOR_SIZE);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* Regression for F-7383: the part reports 4KB hardware sectors while
|
||||
* WOLFBOOT_SECTOR_SIZE is 8KB. Erasing 0x4000 bytes must issue four erase
|
||||
* commands, one per hardware sector. Before the fix the loop stepped by
|
||||
* WOLFBOOT_SECTOR_SIZE and issued only two, leaving the sectors at
|
||||
* FLASH_BASE + 0x1000 and FLASH_BASE + 0x3000 unerased. */
|
||||
START_TEST(test_erase_runtime_sector_smaller_covers_range)
|
||||
{
|
||||
int i;
|
||||
|
||||
reset_mocks(0x1000);
|
||||
ck_assert_int_eq(hal_flash_erase(FLASH_BASE, 0x4000), 0);
|
||||
|
||||
ck_assert_int_eq(erase_log_n, 4);
|
||||
for (i = 0; i < 4; i++)
|
||||
ck_assert_uint_eq(erase_addr[i], FLASH_BASE + (uint32_t)i * 0x1000U);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* The mirror case: the part reports 16KB sectors. Every erase command must
|
||||
* land on a hardware sector boundary; before the fix the 8KB step issued
|
||||
* commands in the middle of a sector, and erased the same sector twice. */
|
||||
START_TEST(test_erase_runtime_sector_larger_stays_aligned)
|
||||
{
|
||||
reset_mocks(0x4000);
|
||||
ck_assert_int_eq(hal_flash_erase(FLASH_BASE, 0x8000), 0);
|
||||
|
||||
ck_assert_int_eq(erase_log_n, 2);
|
||||
ck_assert_uint_eq(erase_addr[0], FLASH_BASE);
|
||||
ck_assert_uint_eq(erase_addr[1], FLASH_BASE + 0x4000U);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* An unaligned start address is rounded down to the runtime sector boundary,
|
||||
* and the stride keeps every following command aligned too. */
|
||||
START_TEST(test_erase_unaligned_start_rounds_down)
|
||||
{
|
||||
reset_mocks(0x1000);
|
||||
ck_assert_int_eq(hal_flash_erase(FLASH_BASE + 0x800, 0x1800), 0);
|
||||
|
||||
ck_assert_int_eq(erase_log_n, 2);
|
||||
ck_assert_uint_eq(erase_addr[0], FLASH_BASE);
|
||||
ck_assert_uint_eq(erase_addr[1], FLASH_BASE + 0x1000U);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* Rounding the start down extends the range backwards, so the length has to
|
||||
* grow by the same amount. A request that starts near the end of one sector
|
||||
* and reaches into the next must erase both, not just the first. */
|
||||
START_TEST(test_erase_unaligned_start_spanning_next_sector)
|
||||
{
|
||||
reset_mocks(0x1000);
|
||||
ck_assert_int_eq(hal_flash_erase(FLASH_BASE + 0xF00, 0x200), 0);
|
||||
|
||||
ck_assert_int_eq(erase_log_n, 2);
|
||||
ck_assert_uint_eq(erase_addr[0], FLASH_BASE);
|
||||
ck_assert_uint_eq(erase_addr[1], FLASH_BASE + 0x1000U);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* FLASH_GetProperty() failing to report a size must not divide by zero:
|
||||
* fall back to WOLFBOOT_SECTOR_SIZE, as hal/mcxn.c does. The requested range
|
||||
* starts 0x10 into the first sector and so ends 0x10 into the second, which
|
||||
* takes two erase commands. */
|
||||
START_TEST(test_erase_zero_runtime_sector_falls_back)
|
||||
{
|
||||
reset_mocks(0);
|
||||
ck_assert_int_eq(hal_flash_erase(FLASH_BASE + 0x10, WOLFBOOT_SECTOR_SIZE),
|
||||
0);
|
||||
|
||||
ck_assert_int_eq(erase_log_n, 2);
|
||||
ck_assert_uint_eq(erase_addr[0], FLASH_BASE);
|
||||
ck_assert_uint_eq(erase_addr[1], FLASH_BASE + WOLFBOOT_SECTOR_SIZE);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite *flash_erase_suite(void)
|
||||
{
|
||||
Suite *s = suite_create("flash-erase-mcxw");
|
||||
TCase *tc = tcase_create("flash-erase-mcxw");
|
||||
|
||||
tcase_add_test(tc, test_erase_two_sectors_matching_size);
|
||||
tcase_add_test(tc, test_erase_runtime_sector_smaller_covers_range);
|
||||
tcase_add_test(tc, test_erase_runtime_sector_larger_stays_aligned);
|
||||
tcase_add_test(tc, test_erase_unaligned_start_rounds_down);
|
||||
tcase_add_test(tc, test_erase_unaligned_start_spanning_next_sector);
|
||||
tcase_add_test(tc, test_erase_zero_runtime_sector_falls_back);
|
||||
|
||||
suite_add_tcase(s, tc);
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int fails;
|
||||
Suite *s = flash_erase_suite();
|
||||
SRunner *sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
fails = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
|
||||
return fails;
|
||||
}
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
/* unit-flash-write-nrf52.c
|
||||
*
|
||||
* Regression test for F-6757: in the byte-wise (partial word) path of
|
||||
* hal_flash_write() (hal/nrf52.c, hal/nrf5340.c, hal/stm32l0.c) the base of
|
||||
* the containing word was derived from the original (call-time) "address"
|
||||
* instead of the current position "address + i":
|
||||
* int off = (address + i) - (((address + i) >> 2) << 2);
|
||||
* dst = (uint32_t *)(address - off);
|
||||
* val = dst[i >> 2];
|
||||
* "dst[i >> 2]" then addresses physical byte "address - off + (i & ~3)"
|
||||
* rather than the intended word containing "address + i". For every
|
||||
* iteration with "i" not a multiple of 4 the wrong destination byte is
|
||||
* modified (and, when off != 0, through a misaligned 32-bit flash access,
|
||||
* which faults outright on the Cortex-M0+ of stm32l0). This is the same
|
||||
* defect already fixed for hal/samr21.c and hal/same51.c under F-5964.
|
||||
*
|
||||
* Copyright (C) 2026 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfBoot.
|
||||
*
|
||||
* wolfBoot is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfBoot is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
*/
|
||||
|
||||
#include <check.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <sys/mman.h>
|
||||
|
||||
#include "image.h"
|
||||
|
||||
#include "../../hal/nrf52.c"
|
||||
|
||||
/* hal_flash_write() polls NVMC_READY and pokes NVMC_CONFIG directly (both
|
||||
* within one page of the fixed NVMC_BASE); map that page and leave READY
|
||||
* asserted so the flash state machine appears idle. */
|
||||
static void map_nvmc(void)
|
||||
{
|
||||
int flags = MAP_PRIVATE | MAP_ANONYMOUS;
|
||||
#ifdef MAP_FIXED_NOREPLACE
|
||||
flags |= MAP_FIXED_NOREPLACE;
|
||||
#else
|
||||
flags |= MAP_FIXED;
|
||||
#endif
|
||||
void *p = mmap((void *)(uintptr_t)NVMC_BASE, 4096,
|
||||
PROT_READ | PROT_WRITE, flags, -1, 0);
|
||||
ck_assert_ptr_eq(p, (void *)(uintptr_t)NVMC_BASE);
|
||||
NVMC_READY = 1;
|
||||
}
|
||||
|
||||
static void unmap_nvmc(void)
|
||||
{
|
||||
munmap((void *)(uintptr_t)NVMC_BASE, 4096);
|
||||
}
|
||||
|
||||
/* "address" is treated as a real pointer into memory-mapped flash. Keep the
|
||||
* mock flash buffer inside the 32-bit range, matching how "address" (a
|
||||
* uint32_t) is used by the real target. The buffer sits in the middle of a
|
||||
* larger mapping so that the out-of-word accesses made by the buggy code
|
||||
* report as byte mismatches instead of killing the test with SIGSEGV. */
|
||||
#define MOCK_FLASH_SIZE 64
|
||||
#define MOCK_MAP_SIZE 4096
|
||||
#define MOCK_MAP_OFFSET 128
|
||||
static uint8_t *mock_map;
|
||||
static uint8_t *mock_flash;
|
||||
|
||||
static void setup(void)
|
||||
{
|
||||
map_nvmc();
|
||||
mock_map = mmap(NULL, MOCK_MAP_SIZE, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
|
||||
ck_assert_ptr_ne(mock_map, MAP_FAILED);
|
||||
memset(mock_map, 0xFF, MOCK_MAP_SIZE);
|
||||
mock_flash = mock_map + MOCK_MAP_OFFSET;
|
||||
}
|
||||
|
||||
static void teardown(void)
|
||||
{
|
||||
munmap(mock_map, MOCK_MAP_SIZE);
|
||||
unmap_nvmc();
|
||||
}
|
||||
|
||||
/* Word-aligned destination and source, length not a multiple of 4: the fast
|
||||
* 32-bit path copies the first word, then the 2-byte tail goes through the
|
||||
* byte-wise path. Before the fix the second tail byte (i = 5) lands on
|
||||
* physical byte "address + 4", overwriting the first tail byte and leaving
|
||||
* "address + 5" erased. */
|
||||
START_TEST(test_aligned_write_unaligned_tail)
|
||||
{
|
||||
uint8_t data[6] __attribute__((aligned(4)));
|
||||
uint32_t base = (uint32_t)(uintptr_t)mock_flash;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
data[i] = (uint8_t)(0xA0 + i);
|
||||
|
||||
ck_assert_int_eq(hal_flash_write(base, data, 6), 0);
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
ck_assert_uint_eq(mock_flash[i], data[i]);
|
||||
for (i = 6; i < MOCK_FLASH_SIZE; i++)
|
||||
ck_assert_uint_eq(mock_flash[i], 0xFF);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* Destination misaligned by 1 (mod 4), source buffer misaligned by 2 (mod
|
||||
* 4): the two never share the same alignment, so the fast 32-bit path is
|
||||
* never taken and the whole transfer runs through the byte-wise path. */
|
||||
START_TEST(test_unaligned_write_mismatched_alignment)
|
||||
{
|
||||
uint8_t rawbuf[64];
|
||||
uint8_t *data = rawbuf;
|
||||
uint32_t base = (uint32_t)(uintptr_t)mock_flash;
|
||||
int i;
|
||||
|
||||
while (((uintptr_t)data % 4) != 2)
|
||||
data++;
|
||||
for (i = 0; i < 8; i++)
|
||||
data[i] = (uint8_t)(0xC0 + i);
|
||||
|
||||
ck_assert_int_eq(hal_flash_write(base + 5, data, 8), 0);
|
||||
|
||||
for (i = 0; i < 5; i++)
|
||||
ck_assert_uint_eq(mock_flash[i], 0xFF);
|
||||
for (i = 0; i < 8; i++)
|
||||
ck_assert_uint_eq(mock_flash[5 + i], data[i]);
|
||||
for (i = 13; i < MOCK_FLASH_SIZE; i++)
|
||||
ck_assert_uint_eq(mock_flash[i], 0xFF);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* Destination and source share the same non-zero misalignment, so once the
|
||||
* byte-wise path has advanced i to the next word boundary both fast-path
|
||||
* conditions hold and the 32-bit branch is entered with i != 0. Before the
|
||||
* fix that branch indexed dst[i >> 2]/src[i >> 2] off the unaligned bases,
|
||||
* writing data[0..3] to "address..address+3" instead of data[3..6] to
|
||||
* "address+3..address+6" -- through a misaligned 32-bit flash access. */
|
||||
START_TEST(test_unaligned_write_matching_alignment_fast_path)
|
||||
{
|
||||
uint8_t rawbuf[64];
|
||||
uint8_t *data = rawbuf;
|
||||
uint32_t base = (uint32_t)(uintptr_t)mock_flash;
|
||||
int i;
|
||||
|
||||
while (((uintptr_t)data % 4) != 1)
|
||||
data++;
|
||||
for (i = 0; i < 12; i++)
|
||||
data[i] = (uint8_t)(0xD0 + i);
|
||||
|
||||
ck_assert_int_eq(hal_flash_write(base + 1, data, 12), 0);
|
||||
|
||||
ck_assert_uint_eq(mock_flash[0], 0xFF);
|
||||
for (i = 0; i < 12; i++)
|
||||
ck_assert_uint_eq(mock_flash[1 + i], data[i]);
|
||||
for (i = 13; i < MOCK_FLASH_SIZE; i++)
|
||||
ck_assert_uint_eq(mock_flash[i], 0xFF);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* A write that fits entirely inside a single flash word must still work:
|
||||
* buggy and fixed forms agree here (i is always 0 in the byte-wise path),
|
||||
* guarding against a fix that breaks the common case. */
|
||||
START_TEST(test_unaligned_write_single_word)
|
||||
{
|
||||
uint8_t data[3];
|
||||
uint32_t base = (uint32_t)(uintptr_t)mock_flash;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
data[i] = (uint8_t)(0xB0 + i);
|
||||
|
||||
ck_assert_int_eq(hal_flash_write(base + 1, data, 3), 0);
|
||||
|
||||
ck_assert_uint_eq(mock_flash[0], 0xFF);
|
||||
for (i = 0; i < 3; i++)
|
||||
ck_assert_uint_eq(mock_flash[1 + i], data[i]);
|
||||
for (i = 4; i < MOCK_FLASH_SIZE; i++)
|
||||
ck_assert_uint_eq(mock_flash[i], 0xFF);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite *flash_write_suite(void)
|
||||
{
|
||||
Suite *s = suite_create("flash-write-nrf52");
|
||||
TCase *tc = tcase_create("flash-write-nrf52");
|
||||
|
||||
tcase_add_checked_fixture(tc, setup, teardown);
|
||||
tcase_add_test(tc, test_aligned_write_unaligned_tail);
|
||||
tcase_add_test(tc, test_unaligned_write_mismatched_alignment);
|
||||
tcase_add_test(tc, test_unaligned_write_matching_alignment_fast_path);
|
||||
tcase_add_test(tc, test_unaligned_write_single_word);
|
||||
|
||||
suite_add_tcase(s, tc);
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int fails;
|
||||
Suite *s = flash_write_suite();
|
||||
SRunner *sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
fails = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
|
||||
return fails;
|
||||
}
|
||||
|
|
@ -25,6 +25,9 @@
|
|||
|
||||
static int locked = 1;
|
||||
static int ext_locked = 1;
|
||||
/* When set, the next ext_flash_write() fails and the hook clears itself,
|
||||
* so a test can target one specific write in a multi-write sequence. */
|
||||
static int ext_flash_write_fail = 0;
|
||||
static int erased_boot = 0;
|
||||
static int erased_update = 0;
|
||||
static int erased_swap = 0;
|
||||
|
|
@ -184,6 +187,10 @@ int ext_flash_write(uintptr_t address, const uint8_t *data, int len)
|
|||
uint8_t *a = (uint8_t *)address;
|
||||
ck_assert_msg(!ext_locked, "Attempting to write to a locked FLASH");
|
||||
ck_assert_msg(len >= 0, "ext_flash_write invalid len %d", len);
|
||||
if (ext_flash_write_fail) {
|
||||
ext_flash_write_fail = 0;
|
||||
return -1;
|
||||
}
|
||||
ck_assert_msg(
|
||||
((address >= WOLFBOOT_PARTITION_BOOT_ADDRESS) &&
|
||||
(address < WOLFBOOT_PARTITION_BOOT_ADDRESS + WOLFBOOT_PARTITION_SIZE) &&
|
||||
|
|
|
|||
|
|
@ -113,6 +113,32 @@ START_TEST(test_qspi_write_splits_last_page_to_remaining_bytes)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_qspi_write_clips_first_page_at_page_boundary)
|
||||
{
|
||||
uint8_t buf[FLASH_PAGE_SIZE + 32];
|
||||
uint32_t off = FLASH_PAGE_SIZE - 16;
|
||||
int ret;
|
||||
|
||||
memset(buf, 0x5A, sizeof(buf));
|
||||
|
||||
/* Start 16 bytes before a page boundary: the device's page program wraps
|
||||
* within its own page, so the first transfer must stop at the boundary. */
|
||||
ret = spi_flash_write(0x1000 + off, buf, sizeof(buf));
|
||||
|
||||
ck_assert_int_eq(ret, 0);
|
||||
ck_assert_int_eq(program_call_count, 3);
|
||||
ck_assert_uint_eq(program_addrs[0], 0x1000 + off);
|
||||
ck_assert_uint_eq(program_sizes[0], 16);
|
||||
ck_assert_ptr_eq(program_ptrs[0], buf);
|
||||
ck_assert_uint_eq(program_addrs[1], 0x1000 + FLASH_PAGE_SIZE);
|
||||
ck_assert_uint_eq(program_sizes[1], FLASH_PAGE_SIZE);
|
||||
ck_assert_ptr_eq(program_ptrs[1], buf + 16);
|
||||
ck_assert_uint_eq(program_addrs[2], 0x1000 + (FLASH_PAGE_SIZE * 2));
|
||||
ck_assert_uint_eq(program_sizes[2], 16);
|
||||
ck_assert_ptr_eq(program_ptrs[2], buf + 16 + FLASH_PAGE_SIZE);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_qspi_write_stops_after_midloop_write_enable_failure)
|
||||
{
|
||||
uint8_t buf[FLASH_PAGE_SIZE * 3];
|
||||
|
|
@ -176,6 +202,7 @@ static Suite *qspi_flash_suite(void)
|
|||
tc = tcase_create("Write");
|
||||
tcase_add_checked_fixture(tc, setup, NULL);
|
||||
tcase_add_test(tc, test_qspi_write_splits_last_page_to_remaining_bytes);
|
||||
tcase_add_test(tc, test_qspi_write_clips_first_page_at_page_boundary);
|
||||
tcase_add_test(tc, test_qspi_write_stops_after_midloop_write_enable_failure);
|
||||
tcase_add_test(tc, test_qspi_read_rejects_address_at_device_size);
|
||||
tcase_add_test(tc, test_qspi_read_rejects_transfer_extending_past_device_size);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,136 @@
|
|||
#!/usr/bin/env python3
|
||||
# unit-sign-delta-basehash-cleanup.py
|
||||
#
|
||||
# Regression test for the delta base-hash validation error path in the C
|
||||
# signing tool.
|
||||
#
|
||||
# make_header_ex() in tools/keytools/sign.c validates the base image digest
|
||||
# handed to it by base_diff() when signing a delta update (is_diff=1). If the
|
||||
# base image carries no digest for the selected hash algorithm, or one whose
|
||||
# size does not match, the function used to call exit(1) directly instead of
|
||||
# taking its 'failure:' path. That terminates the process from deep inside the
|
||||
# call chain, so none of the unwinding runs: base_diff()'s 'cleanup:' block
|
||||
# never unlinks the temporary patch file, and main() never reaches
|
||||
# zero_and_free(kbuf, key_buffer_sz) or the algorithm-specific key free, so the
|
||||
# raw and decoded private signing key stay in memory unscrubbed.
|
||||
#
|
||||
# The reachable trigger is a base image signed with a different hash algorithm
|
||||
# than the delta: base_diff() looks up HDR_SHA256/HDR_SHA384/HDR_SHA3_384
|
||||
# according to CMD.hash_algo, finds nothing, and still calls
|
||||
# make_header_delta().
|
||||
#
|
||||
# Key zeroization is not directly observable from outside the process, but the
|
||||
# leftover temporary patch file is: it proves that the error return unwound
|
||||
# through base_diff()'s cleanup instead of aborting the process. This test
|
||||
# signs a base image with SHA256, asks for a SHA384 delta against it, and
|
||||
# asserts that the run fails cleanly with /tmp/wolfboot-delta.bin removed.
|
||||
# Before the fix the file is left behind.
|
||||
#
|
||||
# Copyright (C) 2026 wolfSSL Inc.
|
||||
#
|
||||
# This file is part of wolfBoot.
|
||||
#
|
||||
# wolfBoot is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# wolfBoot is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
SECTOR_SIZE = 0x1000
|
||||
|
||||
# wolfboot_delta_file[] in tools/keytools/sign.c
|
||||
DELTA_TMP = "/tmp/wolfboot-delta.bin"
|
||||
|
||||
THIS_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
ROOT = os.path.abspath(os.path.join(THIS_DIR, "..", ".."))
|
||||
KEYTOOLS = os.path.join(ROOT, "tools", "keytools")
|
||||
SIGN = os.path.join(KEYTOOLS, "sign")
|
||||
KEYGEN = os.path.join(KEYTOOLS, "keygen")
|
||||
|
||||
|
||||
def skip(msg):
|
||||
print("SKIP unit-sign-delta-basehash-cleanup: " + msg)
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def ensure_tool(path, target):
|
||||
if os.path.exists(path):
|
||||
return True
|
||||
try:
|
||||
subprocess.run(["make", target], cwd=KEYTOOLS,
|
||||
check=True, capture_output=True, text=True)
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
return False
|
||||
return os.path.exists(path)
|
||||
|
||||
|
||||
def main():
|
||||
if not ensure_tool(SIGN, "sign"):
|
||||
skip("could not build tools/keytools/sign")
|
||||
if not ensure_tool(KEYGEN, "keygen"):
|
||||
skip("could not build tools/keytools/keygen")
|
||||
|
||||
with tempfile.TemporaryDirectory() as work:
|
||||
key = os.path.join(work, "priv.der")
|
||||
r = subprocess.run([KEYGEN, "--ed25519", "-g", key,
|
||||
"-keystoreDir", work],
|
||||
cwd=work, capture_output=True, text=True)
|
||||
if r.returncode != 0 or not os.path.exists(key):
|
||||
skip("keygen failed: " + r.stderr.strip())
|
||||
|
||||
base = os.path.join(work, "image_v1.bin")
|
||||
upd = os.path.join(work, "image_v2.bin")
|
||||
payload = bytes((i * 7) & 0xFF for i in range(2048))
|
||||
with open(base, "wb") as f:
|
||||
f.write(payload)
|
||||
with open(upd, "wb") as f:
|
||||
f.write(payload[:512] + b"PATCHED!" + payload[520:])
|
||||
|
||||
env = dict(os.environ)
|
||||
env["WOLFBOOT_SECTOR_SIZE"] = str(SECTOR_SIZE)
|
||||
|
||||
# Sign the base image (v1) with SHA256, so it carries HDR_SHA256 only.
|
||||
r = subprocess.run([SIGN, "--ed25519", "--sha256", base, key, "1"],
|
||||
cwd=ROOT, env=env, capture_output=True, text=True)
|
||||
if r.returncode != 0:
|
||||
skip("sign base failed: " + r.stderr.strip())
|
||||
signed_base = base.replace(".bin", "_v1_signed.bin")
|
||||
if not os.path.exists(signed_base):
|
||||
skip("sign did not produce a signed base image")
|
||||
|
||||
if os.path.exists(DELTA_TMP):
|
||||
os.unlink(DELTA_TMP)
|
||||
|
||||
# Ask for a SHA384 delta: base_diff() looks up HDR_SHA384 in the base
|
||||
# image, finds nothing, and make_header_ex() must fail cleanly.
|
||||
r = subprocess.run([SIGN, "--ed25519", "--sha384", "--delta",
|
||||
signed_base, upd, key, "2"],
|
||||
cwd=ROOT, env=env, capture_output=True, text=True)
|
||||
if r.returncode == 0:
|
||||
print("FAIL unit-sign-delta-basehash-cleanup: signing a delta "
|
||||
"against a base image with no matching digest succeeded")
|
||||
sys.exit(1)
|
||||
|
||||
if os.path.exists(DELTA_TMP):
|
||||
os.unlink(DELTA_TMP)
|
||||
print("FAIL unit-sign-delta-basehash-cleanup: %s was left behind, "
|
||||
"so make_header_ex() aborted the process instead of "
|
||||
"returning an error; base_diff() cleanup and main()'s "
|
||||
"signing key zeroization were skipped" % DELTA_TMP)
|
||||
sys.exit(1)
|
||||
|
||||
print("unit-sign-delta-basehash-cleanup: OK")
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -133,6 +133,122 @@ START_TEST(test_sign_main_fails_when_secondary_key_missing)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
/* Export a freshly generated ECC key as the raw Qx || Qy || d blob accepted
|
||||
* by load_key(). */
|
||||
static int make_raw_ecc_key(int curve_id, int curve_sz, uint8_t *raw)
|
||||
{
|
||||
WC_RNG rng;
|
||||
ecc_key ek;
|
||||
word32 qxSz = curve_sz, qySz = curve_sz, dSz = curve_sz;
|
||||
int ret;
|
||||
|
||||
if (wc_InitRng(&rng) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (wc_ecc_init(&ek) != 0) {
|
||||
wc_FreeRng(&rng);
|
||||
return -1;
|
||||
}
|
||||
ret = wc_ecc_make_key_ex(&rng, curve_sz, &ek, curve_id);
|
||||
if (ret == 0) {
|
||||
ret = wc_ecc_export_private_raw(&ek, raw, &qxSz, raw + curve_sz, &qySz,
|
||||
raw + (curve_sz * 2), &dSz);
|
||||
}
|
||||
wc_ecc_free(&ek);
|
||||
wc_FreeRng(&rng);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Check a raw r || s signature against a raw Qx || Qy public key. */
|
||||
static int verify_raw_ecc(int curve_id, int curve_sz, const uint8_t *pubkey,
|
||||
const uint8_t *signature, const uint8_t *digest, uint32_t digest_sz)
|
||||
{
|
||||
ecc_key vk;
|
||||
mp_int r, s;
|
||||
int res = 0;
|
||||
int ret;
|
||||
|
||||
if (wc_ecc_init(&vk) != 0) {
|
||||
return -1;
|
||||
}
|
||||
ret = wc_ecc_import_unsigned(&vk, (byte*)pubkey, (byte*)pubkey + curve_sz,
|
||||
NULL, curve_id);
|
||||
if (ret == 0) {
|
||||
mp_init(&r);
|
||||
mp_init(&s);
|
||||
mp_read_unsigned_bin(&r, signature, curve_sz);
|
||||
mp_read_unsigned_bin(&s, signature + curve_sz, curve_sz);
|
||||
ret = wc_ecc_verify_hash_ex(&r, &s, digest, digest_sz, &res, &vk);
|
||||
mp_clear(&r);
|
||||
mp_clear(&s);
|
||||
}
|
||||
wc_ecc_free(&vk);
|
||||
|
||||
if (ret != 0) {
|
||||
return -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Hybrid signing loads both private keys before either signature is made, so
|
||||
* the secondary key must not land on top of the decoded primary key. */
|
||||
START_TEST(test_hybrid_secondary_key_does_not_clobber_primary)
|
||||
{
|
||||
char tempdir[] = "/tmp/wolfboot-sign-XXXXXX";
|
||||
char primary_path[PATH_MAX];
|
||||
char secondary_path[PATH_MAX];
|
||||
uint8_t primary_raw[66 * 3]; /* ECC521 Qx + Qy + d */
|
||||
uint8_t secondary_raw[32 * 3]; /* ECC256 Qx + Qy + d */
|
||||
uint8_t *kbuf = NULL, *kbuf2 = NULL;
|
||||
uint32_t kbuf_sz = 0, kbuf2_sz = 0;
|
||||
uint8_t *pubkey = NULL, *pubkey2 = NULL;
|
||||
uint32_t pubkey_sz = 0, pubkey_sz2 = 0;
|
||||
uint8_t digest[32];
|
||||
uint8_t signature[132];
|
||||
uint8_t signature2[64];
|
||||
uint32_t signature_sz = sizeof(signature);
|
||||
uint32_t signature_sz2 = sizeof(signature2);
|
||||
|
||||
ck_assert_int_eq(make_raw_ecc_key(ECC_SECP521R1, 66, primary_raw), 0);
|
||||
ck_assert_int_eq(make_raw_ecc_key(ECC_SECP256R1, 32, secondary_raw), 0);
|
||||
|
||||
ck_assert_ptr_nonnull(mkdtemp(tempdir));
|
||||
snprintf(primary_path, sizeof(primary_path), "%s/ecc521.raw", tempdir);
|
||||
snprintf(secondary_path, sizeof(secondary_path), "%s/ecc256.raw", tempdir);
|
||||
ck_assert_int_eq(write_file(primary_path, primary_raw,
|
||||
sizeof(primary_raw)), 0);
|
||||
ck_assert_int_eq(write_file(secondary_path, secondary_raw,
|
||||
sizeof(secondary_raw)), 0);
|
||||
|
||||
reset_cmd_defaults();
|
||||
CMD.sign = SIGN_ECC521;
|
||||
CMD.key_file = primary_path;
|
||||
CMD.hybrid = 1;
|
||||
CMD.secondary_sign = SIGN_ECC256;
|
||||
CMD.secondary_key_file = secondary_path;
|
||||
|
||||
ck_assert_ptr_nonnull(load_key(&kbuf, &kbuf_sz, &pubkey, &pubkey_sz, 0));
|
||||
ck_assert_ptr_nonnull(load_key(&kbuf2, &kbuf2_sz, &pubkey2, &pubkey_sz2,
|
||||
1));
|
||||
|
||||
memset(digest, 0x5C, sizeof(digest));
|
||||
ck_assert_int_eq(sign_digest(CMD.sign, CMD.hash_algo, signature,
|
||||
&signature_sz, digest, sizeof(digest), 0), 0);
|
||||
ck_assert_int_eq(sign_digest(CMD.secondary_sign, CMD.hash_algo, signature2,
|
||||
&signature_sz2, digest, sizeof(digest), 1), 0);
|
||||
|
||||
ck_assert_int_eq(verify_raw_ecc(ECC_SECP521R1, 66, pubkey, signature,
|
||||
digest, sizeof(digest)), 1);
|
||||
ck_assert_int_eq(verify_raw_ecc(ECC_SECP256R1, 32, pubkey2, signature2,
|
||||
digest, sizeof(digest)), 1);
|
||||
|
||||
unlink(primary_path);
|
||||
unlink(secondary_path);
|
||||
rmdir(tempdir);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite *wolfboot_suite(void)
|
||||
{
|
||||
Suite *s = suite_create("sign-hybrid-keyload");
|
||||
|
|
@ -140,6 +256,7 @@ Suite *wolfboot_suite(void)
|
|||
|
||||
tcase_add_test(tcase, test_load_key_clears_pubkey_when_file_missing);
|
||||
tcase_add_test(tcase, test_load_key_clears_pubkey_when_decode_fails);
|
||||
tcase_add_test(tcase, test_hybrid_secondary_key_does_not_clobber_primary);
|
||||
tcase_add_exit_test(tcase, test_sign_main_fails_when_secondary_key_missing,
|
||||
1);
|
||||
suite_add_tcase(s, tcase);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,260 @@
|
|||
/* unit-tpm-advio-zeroize.c
|
||||
*
|
||||
* Regression test for the WOLFTPM_ADV_IO variant of TPM2_IoCb() in src/tpm.c
|
||||
* leaving the TPM command/response frame resident in its stack staging
|
||||
* buffers (txBuf/rxBuf) when it returns. With advanced IO the TIS layer in
|
||||
* wolfTPM hands the raw payload straight to the HAL callback, so the wipe
|
||||
* that TPM2_TIS_Read()/TPM2_TIS_Write() perform on their own txBuf/rxBuf
|
||||
* (lib/wolfTPM/src/tpm2_tis.c) is only done here. A TPM command carrying a
|
||||
* plaintext password authorization therefore stays readable in bootloader
|
||||
* stack SRAM after the transfer completes.
|
||||
*
|
||||
* Copyright (C) 2026 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfBoot.
|
||||
*
|
||||
* wolfBoot is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfBoot is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
*/
|
||||
|
||||
#include <check.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SPI_CS_TPM
|
||||
#define SPI_CS_TPM 1
|
||||
#endif
|
||||
|
||||
#include "wolfboot/wolfboot.h"
|
||||
#include "tpm.h"
|
||||
#include "wolftpm/tpm2_tis.h"
|
||||
|
||||
#define ADV_BUF_SZ (MAX_SPI_FRAMESIZE + TPM_TIS_HEADER_SZ)
|
||||
|
||||
/* Plaintext authValue as it would appear inside a TPM2_NV_Write /
|
||||
* TPM2_Load authorization area handed down to the HAL callback. */
|
||||
static const uint8_t test_auth[] = {
|
||||
'u', 'n', 'i', 't', '-', 't', 'p', 'm', '-', 'a', 'u', 't', 'h'
|
||||
};
|
||||
|
||||
static uint8_t* captured_tx;
|
||||
static uint8_t* captured_rx;
|
||||
static int spi_calls;
|
||||
static int spi_fail_payload;
|
||||
static int spi_never_ready;
|
||||
|
||||
/* Snapshots of the (now dead) TPM2_IoCb() frame, taken by the tests with an
|
||||
* inline volatile copy loop so that no intervening call can reuse the stack
|
||||
* before the contents are inspected. */
|
||||
static uint8_t snapshot_tx[ADV_BUF_SZ];
|
||||
static uint8_t snapshot_rx[ADV_BUF_SZ];
|
||||
|
||||
int wolfBoot_printf(const char* fmt, ...)
|
||||
{
|
||||
(void)fmt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void spi_init(int polarity, int phase)
|
||||
{
|
||||
(void)polarity;
|
||||
(void)phase;
|
||||
}
|
||||
|
||||
void spi_release(void)
|
||||
{
|
||||
}
|
||||
|
||||
/* Minimal TIS-speaking SPI slave: acknowledges the header (LSB of the last
|
||||
* header byte set) and answers a payload read with a recognizable pattern. */
|
||||
int spi_xfer(int cs, const uint8_t* tx, uint8_t* rx, uint32_t sz, int flags)
|
||||
{
|
||||
uint32_t i;
|
||||
|
||||
(void)cs;
|
||||
(void)flags;
|
||||
spi_calls++;
|
||||
|
||||
if (sz == 0) /* de-assert only */
|
||||
return 0;
|
||||
|
||||
if (spi_calls == 1) {
|
||||
captured_tx = (uint8_t*)tx;
|
||||
captured_rx = rx;
|
||||
for (i = 0; i < sz; i++)
|
||||
rx[i] = 0;
|
||||
if (!spi_never_ready)
|
||||
rx[sz - 1] = TPM_TIS_READY_MASK;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (spi_fail_payload)
|
||||
return -1;
|
||||
|
||||
for (i = 0; i < sz; i++)
|
||||
rx[i] = (uint8_t)(0xA0 + (i & 0x0F));
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TPM2_ForceZero(void* mem, word32 len)
|
||||
{
|
||||
volatile uint8_t* p = (volatile uint8_t*)mem;
|
||||
word32 i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
p[i] = 0;
|
||||
}
|
||||
|
||||
#include "../../src/tpm.c"
|
||||
|
||||
/* Copy the dead frame without calling anything (a memcpy() or a helper
|
||||
* function would push its own frame over the bytes under test). */
|
||||
#define SNAPSHOT_FRAME() \
|
||||
do { \
|
||||
volatile const uint8_t* _t = (volatile const uint8_t*)captured_tx;\
|
||||
volatile const uint8_t* _r = (volatile const uint8_t*)captured_rx;\
|
||||
unsigned _i; \
|
||||
for (_i = 0; _i < ADV_BUF_SZ; _i++) { \
|
||||
snapshot_tx[_i] = _t[_i]; \
|
||||
snapshot_rx[_i] = _r[_i]; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void assert_no_residue(const uint8_t* snap, const char* which)
|
||||
{
|
||||
unsigned i, j;
|
||||
|
||||
for (i = 0; i + sizeof(test_auth) <= ADV_BUF_SZ; i++) {
|
||||
for (j = 0; j < sizeof(test_auth); j++) {
|
||||
if (snap[i + j] != test_auth[j])
|
||||
break;
|
||||
}
|
||||
ck_assert_msg(j != sizeof(test_auth),
|
||||
"%s still holds the plaintext TPM authValue at offset %u", which,
|
||||
i);
|
||||
}
|
||||
}
|
||||
|
||||
static void setup(void)
|
||||
{
|
||||
captured_tx = NULL;
|
||||
captured_rx = NULL;
|
||||
spi_calls = 0;
|
||||
spi_fail_payload = 0;
|
||||
spi_never_ready = 0;
|
||||
memset(snapshot_tx, 0xFF, sizeof(snapshot_tx));
|
||||
memset(snapshot_rx, 0xFF, sizeof(snapshot_rx));
|
||||
}
|
||||
|
||||
/* Normal return: the command (including its authorization area) was copied
|
||||
* into txBuf and must not survive the call. */
|
||||
START_TEST(test_advio_write_wipes_txbuf)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = TPM2_IoCb(&wolftpm_dev.ctx, 0 /* write */, 0x24, (uint8_t*)test_auth,
|
||||
(word16)sizeof(test_auth), NULL);
|
||||
SNAPSHOT_FRAME();
|
||||
|
||||
ck_assert_int_eq(rc, 0);
|
||||
ck_assert_ptr_ne(captured_tx, NULL);
|
||||
assert_no_residue(snapshot_tx, "txBuf");
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* Wait-state error return: spi_xfer() fails on the payload transfer, so the
|
||||
* function returns early - the copy of the command is still in txBuf. */
|
||||
START_TEST(test_advio_write_wipes_txbuf_on_error)
|
||||
{
|
||||
int rc;
|
||||
|
||||
spi_fail_payload = 1;
|
||||
rc = TPM2_IoCb(&wolftpm_dev.ctx, 0 /* write */, 0x24, (uint8_t*)test_auth,
|
||||
(word16)sizeof(test_auth), NULL);
|
||||
SNAPSHOT_FRAME();
|
||||
|
||||
ck_assert_int_ne(rc, 0);
|
||||
ck_assert_ptr_ne(captured_tx, NULL);
|
||||
assert_no_residue(snapshot_tx, "txBuf");
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* Timeout error return: the wait-state loop never sees the ready bit, so
|
||||
* TPM2_IoCb() bails out through the de-assert path. */
|
||||
START_TEST(test_advio_write_wipes_txbuf_on_timeout)
|
||||
{
|
||||
int rc;
|
||||
|
||||
spi_never_ready = 1;
|
||||
rc = TPM2_IoCb(&wolftpm_dev.ctx, 0 /* write */, 0x24, (uint8_t*)test_auth,
|
||||
(word16)sizeof(test_auth), NULL);
|
||||
SNAPSHOT_FRAME();
|
||||
|
||||
ck_assert_int_ne(rc, 0);
|
||||
ck_assert_ptr_ne(captured_tx, NULL);
|
||||
assert_no_residue(snapshot_tx, "txBuf");
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* Read: the TPM response lands in rxBuf and is copied out to the caller;
|
||||
* the staging copy must not be left behind. */
|
||||
START_TEST(test_advio_read_wipes_rxbuf)
|
||||
{
|
||||
uint8_t out[sizeof(test_auth)];
|
||||
unsigned i;
|
||||
int rc;
|
||||
|
||||
rc = TPM2_IoCb(&wolftpm_dev.ctx, 1 /* read */, 0x24, out,
|
||||
(word16)sizeof(out), NULL);
|
||||
SNAPSHOT_FRAME();
|
||||
|
||||
ck_assert_int_eq(rc, 0);
|
||||
ck_assert_ptr_ne(captured_rx, NULL);
|
||||
/* the response really was delivered to the caller ... */
|
||||
for (i = 0; i < sizeof(out); i++)
|
||||
ck_assert_uint_eq(out[i], (uint8_t)(0xA0 + (i & 0x0F)));
|
||||
/* ... and no copy of it remains in the staging buffer */
|
||||
for (i = 0; i < ADV_BUF_SZ; i++) {
|
||||
ck_assert_msg(snapshot_rx[i] == 0,
|
||||
"rxBuf still holds TPM response byte 0x%02x at offset %u",
|
||||
snapshot_rx[i], i);
|
||||
}
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static Suite *tpm_advio_zeroize_suite(void)
|
||||
{
|
||||
Suite *s = suite_create("tpm_advio_zeroize");
|
||||
TCase *tc = tcase_create("zeroize");
|
||||
tcase_add_checked_fixture(tc, setup, NULL);
|
||||
tcase_add_test(tc, test_advio_write_wipes_txbuf);
|
||||
tcase_add_test(tc, test_advio_write_wipes_txbuf_on_error);
|
||||
tcase_add_test(tc, test_advio_write_wipes_txbuf_on_timeout);
|
||||
tcase_add_test(tc, test_advio_read_wipes_rxbuf);
|
||||
suite_add_tcase(s, tc);
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int failed;
|
||||
Suite *s = tpm_advio_zeroize_suite();
|
||||
SRunner *sr = srunner_create(s);
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
failed = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
return failed == 0 ? 0 : 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
/* unit-tpm-mfgid-eh-zeroize.c
|
||||
*
|
||||
* Regression test for wolfBoot_tpm2_get_timestamp() in src/tpm.c leaving the
|
||||
* endorsement-hierarchy authValue in its stack-local WOLFTPM2_HANDLE when it
|
||||
* returns. In derive mode (WOLFBOOT_TPM_MFG_AUTH_DERIVE) that value is the
|
||||
* per-device secret computed by wolfTPM2_SetIdentityAuth() from the reel
|
||||
* master secret, and it authorises use of the endorsement hierarchy. The
|
||||
* function already scrubs the master secret from the stack and clears the
|
||||
* copies wolfTPM keeps in the device session slots (wolfTPM2_UnsetAuth()),
|
||||
* but the handle holding the derived value is left untouched, so it survives
|
||||
* in Secure stack SRAM after the non-secure entry veneer returns.
|
||||
*
|
||||
* Copyright (C) 2026 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfBoot.
|
||||
*
|
||||
* wolfBoot is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfBoot is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
*/
|
||||
|
||||
#include <check.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifndef SPI_CS_TPM
|
||||
#define SPI_CS_TPM 1
|
||||
#endif
|
||||
|
||||
#include "tpm.h"
|
||||
|
||||
/* Size and value of the authValue wolfTPM2_SetIdentityAuth() derives: the low
|
||||
* 16 bytes of SHA-256(TPM serial || master), see lib/wolfTPM/src/tpm2_wrap.c */
|
||||
#define EH_AUTH_SZ 16
|
||||
static const uint8_t derived_eh_auth[EH_AUTH_SZ] = {
|
||||
0x5A, 0xC3, 0x11, 0x92, 0x7E, 0x40, 0xB6, 0x08,
|
||||
0xD1, 0x2F, 0x63, 0xAA, 0x0C, 0x74, 0xE9, 0x35
|
||||
};
|
||||
|
||||
/* The stack-local handle wolfBoot_tpm2_get_timestamp() derives into, captured
|
||||
* through the wolfTPM entry point it hands the handle to. */
|
||||
static WOLFTPM2_HANDLE* captured_eh;
|
||||
static int gettime_rc;
|
||||
|
||||
/* Snapshot of the (now dead) frame, taken by the tests with an inline
|
||||
* volatile copy loop so no intervening call can reuse the stack first. */
|
||||
static uint8_t snapshot_eh[sizeof(WOLFTPM2_HANDLE)];
|
||||
|
||||
void *cmse_check_address_range(void *ptr, size_t size, int flags)
|
||||
{
|
||||
(void)size;
|
||||
(void)flags;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
int wolfBoot_printf(const char* fmt, ...)
|
||||
{
|
||||
(void)fmt;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Stand-in for wolfTPM2_SetIdentityAuth(): the real one hashes the TPM serial
|
||||
* number with the master secret and stores the low 16 bytes of the digest in
|
||||
* the handle's auth field. */
|
||||
int wolfTPM2_SetIdentityAuth(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* handle,
|
||||
uint8_t* masterPassword, uint16_t masterPasswordSz)
|
||||
{
|
||||
(void)dev;
|
||||
(void)masterPassword;
|
||||
(void)masterPasswordSz;
|
||||
|
||||
captured_eh = handle;
|
||||
handle->auth.size = EH_AUTH_SZ;
|
||||
memcpy(handle->auth.buffer, derived_eh_auth, EH_AUTH_SZ);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wolfTPM2_SetAuthHandle(WOLFTPM2_DEV* dev, int index,
|
||||
const WOLFTPM2_HANDLE* handle)
|
||||
{
|
||||
(void)dev;
|
||||
(void)index;
|
||||
(void)handle;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wolfTPM2_UnsetAuth(WOLFTPM2_DEV* dev, int index)
|
||||
{
|
||||
if (dev == NULL || index < 0 || index >= MAX_SESSION_NUM) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
memset(&dev->session[index], 0, sizeof(dev->session[index]));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wolfTPM2_GetTime(WOLFTPM2_KEY* aikKey, GetTime_Out* getTimeOut)
|
||||
{
|
||||
(void)aikKey;
|
||||
(void)getTimeOut;
|
||||
return gettime_rc;
|
||||
}
|
||||
|
||||
void TPM2_ForceZero(void* mem, word32 len)
|
||||
{
|
||||
volatile uint8_t* p = (volatile uint8_t*)mem;
|
||||
word32 i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
p[i] = 0;
|
||||
}
|
||||
|
||||
#include "../../src/tpm.c"
|
||||
|
||||
/* Copy the dead frame without calling anything (a memcpy() or a helper
|
||||
* function would push its own frame over the bytes under test). */
|
||||
#define SNAPSHOT_EH() \
|
||||
do { \
|
||||
volatile const uint8_t* _h = (volatile const uint8_t*)captured_eh; \
|
||||
unsigned _i; \
|
||||
for (_i = 0; _i < sizeof(snapshot_eh); _i++) { \
|
||||
snapshot_eh[_i] = _h[_i]; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void assert_no_residue(void)
|
||||
{
|
||||
unsigned i, j;
|
||||
|
||||
for (i = 0; i + EH_AUTH_SZ <= sizeof(snapshot_eh); i++) {
|
||||
for (j = 0; j < EH_AUTH_SZ; j++) {
|
||||
if (snapshot_eh[i + j] != derived_eh_auth[j])
|
||||
break;
|
||||
}
|
||||
ck_assert_msg(j != EH_AUTH_SZ,
|
||||
"eh_handle still holds the derived EH authValue at offset %u", i);
|
||||
}
|
||||
}
|
||||
|
||||
static void setup(void)
|
||||
{
|
||||
captured_eh = NULL;
|
||||
gettime_rc = 0;
|
||||
memset(snapshot_eh, 0xFF, sizeof(snapshot_eh));
|
||||
memset(&wolftpm_dev, 0, sizeof(wolftpm_dev));
|
||||
}
|
||||
|
||||
/* Normal return: the derived EH authValue must not survive the veneer. */
|
||||
START_TEST(test_get_timestamp_wipes_eh_auth)
|
||||
{
|
||||
WOLFTPM2_KEY aik;
|
||||
GetTime_Out getTime;
|
||||
int rc;
|
||||
|
||||
memset(&aik, 0, sizeof(aik));
|
||||
rc = wolfBoot_tpm2_get_timestamp(&aik, &getTime);
|
||||
SNAPSHOT_EH();
|
||||
|
||||
ck_assert_int_eq(rc, 0);
|
||||
ck_assert_ptr_ne(captured_eh, NULL);
|
||||
assert_no_residue();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* Error return: TPM2_GetTime fails after the authValue was derived, so the
|
||||
* handle still holds it on the way out. */
|
||||
START_TEST(test_get_timestamp_wipes_eh_auth_on_error)
|
||||
{
|
||||
WOLFTPM2_KEY aik;
|
||||
GetTime_Out getTime;
|
||||
int rc;
|
||||
|
||||
memset(&aik, 0, sizeof(aik));
|
||||
gettime_rc = TPM_RC_FAILURE;
|
||||
rc = wolfBoot_tpm2_get_timestamp(&aik, &getTime);
|
||||
SNAPSHOT_EH();
|
||||
|
||||
ck_assert_int_ne(rc, 0);
|
||||
ck_assert_ptr_ne(captured_eh, NULL);
|
||||
assert_no_residue();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
static Suite *tpm_mfgid_eh_zeroize_suite(void)
|
||||
{
|
||||
Suite *s = suite_create("tpm_mfgid_eh_zeroize");
|
||||
TCase *tc = tcase_create("zeroize");
|
||||
tcase_add_checked_fixture(tc, setup, NULL);
|
||||
tcase_add_test(tc, test_get_timestamp_wipes_eh_auth);
|
||||
tcase_add_test(tc, test_get_timestamp_wipes_eh_auth_on_error);
|
||||
suite_add_tcase(s, tc);
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int failed;
|
||||
Suite *s = tpm_mfgid_eh_zeroize_suite();
|
||||
SRunner *sr = srunner_create(s);
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
failed = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
return failed == 0 ? 0 : 1;
|
||||
}
|
||||
|
|
@ -0,0 +1,356 @@
|
|||
/* unit-update-disk-fit.c
|
||||
*
|
||||
* Regression coverage for the FIT (flattened uImage tree) exit paths of
|
||||
* wolfBoot_start() in src/update_disk.c, with DISK_ENCRYPT enabled.
|
||||
*
|
||||
* Every terminal exit of wolfBoot_start() must scrub the disk decryption
|
||||
* key/nonce before handing control away. wolfBoot_panic() is an unbounded
|
||||
* spin on real targets, so key material left resident there stays resident
|
||||
* forever. These tests snapshot the module statics from the panic hook,
|
||||
* which runs at the top of wolfBoot_panic(), i.e. exactly at the moment the
|
||||
* bootloader stops making progress.
|
||||
*
|
||||
* Copyright (C) 2026 wolfSSL Inc.
|
||||
*
|
||||
* This file is part of wolfBoot.
|
||||
*
|
||||
* wolfBoot is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* wolfBoot is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
|
||||
*/
|
||||
|
||||
#define WOLFBOOT_UPDATE_DISK
|
||||
#define WOLFBOOT_SELF_UPDATE_MONOLITHIC
|
||||
#define RAM_CODE
|
||||
#define WOLFBOOT_SELF_HEADER
|
||||
#define EXT_ENCRYPTED
|
||||
#define ENCRYPT_WITH_CHACHA
|
||||
#define HAVE_CHACHA
|
||||
#define IMAGE_HEADER_SIZE 256
|
||||
#define BOOT_PART_A 0
|
||||
#define BOOT_PART_B 1
|
||||
#define MOCK_ADDRESS_BOOT 0xCD000000
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <check.h>
|
||||
|
||||
#include "hal.h"
|
||||
#include "target.h"
|
||||
#include "wolfboot/wolfboot.h"
|
||||
#include "image.h"
|
||||
#include "loader.h"
|
||||
#include <wolfssl/wolfcrypt/chacha.h>
|
||||
|
||||
#define TEST_PAYLOAD_SIZE 64
|
||||
#define TEST_DTS_SIZE 32
|
||||
|
||||
static uint8_t load_buffer[TEST_PAYLOAD_SIZE];
|
||||
#define WOLFBOOT_LOAD_ADDRESS ((uintptr_t)load_buffer)
|
||||
|
||||
static uint8_t dts_buffer[TEST_DTS_SIZE];
|
||||
#define WOLFBOOT_LOAD_DTS_ADDRESS ((uintptr_t)dts_buffer)
|
||||
|
||||
static uint8_t part_a_image[IMAGE_HEADER_SIZE + TEST_PAYLOAD_SIZE];
|
||||
static uint8_t part_b_image[IMAGE_HEADER_SIZE + TEST_PAYLOAD_SIZE];
|
||||
static uint8_t fit_dts_image[TEST_DTS_SIZE];
|
||||
static int mock_do_boot_called;
|
||||
static int mock_fit_memcpy_ret;
|
||||
static int mock_fit_memcpy_called;
|
||||
static int mock_panic_hook_called;
|
||||
/* Snapshot of the key material taken from inside wolfBoot_panic() */
|
||||
static uint8_t panic_key_snapshot[ENCRYPT_KEY_SIZE];
|
||||
static uint8_t panic_nonce_snapshot[ENCRYPT_NONCE_SIZE];
|
||||
|
||||
ChaCha chacha;
|
||||
|
||||
static void set_u16_le(uint8_t *dst, uint16_t value)
|
||||
{
|
||||
dst[0] = (uint8_t)(value & 0xFF);
|
||||
dst[1] = (uint8_t)(value >> 8);
|
||||
}
|
||||
|
||||
static void set_u32_le(uint8_t *dst, uint32_t value)
|
||||
{
|
||||
dst[0] = (uint8_t)(value & 0xFF);
|
||||
dst[1] = (uint8_t)((value >> 8) & 0xFF);
|
||||
dst[2] = (uint8_t)((value >> 16) & 0xFF);
|
||||
dst[3] = (uint8_t)(value >> 24);
|
||||
}
|
||||
|
||||
static void build_image(uint8_t *image, uint32_t version, uint8_t fill)
|
||||
{
|
||||
memset(image, 0, IMAGE_HEADER_SIZE + TEST_PAYLOAD_SIZE);
|
||||
set_u32_le(image, WOLFBOOT_MAGIC);
|
||||
set_u32_le(image + sizeof(uint32_t), TEST_PAYLOAD_SIZE);
|
||||
set_u16_le(image + IMAGE_HEADER_OFFSET, HDR_VERSION);
|
||||
set_u16_le(image + IMAGE_HEADER_OFFSET + sizeof(uint16_t), 4);
|
||||
set_u32_le(image + IMAGE_HEADER_OFFSET + 2 * sizeof(uint16_t), version);
|
||||
memset(image + IMAGE_HEADER_SIZE, fill, TEST_PAYLOAD_SIZE);
|
||||
}
|
||||
|
||||
static void reset_mocks(void)
|
||||
{
|
||||
memset(load_buffer, 0, sizeof(load_buffer));
|
||||
memset(dts_buffer, 0, sizeof(dts_buffer));
|
||||
build_image(part_a_image, 1, 0xA1);
|
||||
build_image(part_b_image, 2, 0xB2);
|
||||
memset(fit_dts_image, 0xDD, sizeof(fit_dts_image));
|
||||
mock_do_boot_called = 0;
|
||||
mock_fit_memcpy_ret = 0;
|
||||
mock_fit_memcpy_called = 0;
|
||||
mock_panic_hook_called = 0;
|
||||
memset(panic_key_snapshot, 0xFF, sizeof(panic_key_snapshot));
|
||||
memset(panic_nonce_snapshot, 0xFF, sizeof(panic_nonce_snapshot));
|
||||
wolfBoot_panicked = 0;
|
||||
}
|
||||
|
||||
int chacha_init(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wc_Chacha_SetIV(ChaCha* ctx, const byte* inIv, word32 counter)
|
||||
{
|
||||
(void)ctx;
|
||||
(void)inIv;
|
||||
(void)counter;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wc_Chacha_Process(ChaCha* ctx, byte* output, const byte* input, word32 msglen)
|
||||
{
|
||||
(void)ctx;
|
||||
memmove(output, input, msglen);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void wc_ForceZero(void* mem, size_t len)
|
||||
{
|
||||
volatile uint8_t *p = (volatile uint8_t *)mem;
|
||||
while (len-- > 0) {
|
||||
*p++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
int wolfBoot_initialize_encryption(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wolfBoot_get_encrypt_key(uint8_t *key, uint8_t *nonce)
|
||||
{
|
||||
memset(key, 0x5A, ENCRYPT_KEY_SIZE);
|
||||
memset(nonce, 0xC3, ENCRYPT_NONCE_SIZE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int disk_init(int drv)
|
||||
{
|
||||
(void)drv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int disk_open(int drv)
|
||||
{
|
||||
(void)drv;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void disk_close(int drv)
|
||||
{
|
||||
(void)drv;
|
||||
}
|
||||
|
||||
int disk_part_read(int drv, int part, uint64_t off, uint64_t sz, uint8_t *buf)
|
||||
{
|
||||
uint8_t *image;
|
||||
uint64_t max = IMAGE_HEADER_SIZE + TEST_PAYLOAD_SIZE;
|
||||
|
||||
(void)drv;
|
||||
image = (part == BOOT_PART_B) ? part_b_image : part_a_image;
|
||||
if ((off > max) || (sz > (max - off)))
|
||||
return -1;
|
||||
memcpy(buf, image + off, (size_t)sz);
|
||||
return (int)sz;
|
||||
}
|
||||
|
||||
int wolfBoot_open_image_address(struct wolfBoot_image* img, uint8_t* image)
|
||||
{
|
||||
uint32_t magic;
|
||||
uint32_t fw_size;
|
||||
|
||||
memcpy(&magic, image, sizeof(magic));
|
||||
|
||||
if (magic != WOLFBOOT_MAGIC)
|
||||
return -1;
|
||||
memset(img, 0, sizeof(*img));
|
||||
img->hdr = image;
|
||||
memcpy(&fw_size, image + sizeof(uint32_t), sizeof(fw_size));
|
||||
img->fw_size = fw_size;
|
||||
img->fw_base = image + IMAGE_HEADER_SIZE;
|
||||
img->hdr_ok = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wolfBoot_verify_integrity(struct wolfBoot_image* img)
|
||||
{
|
||||
img->sha_ok = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wolfBoot_verify_authenticity(struct wolfBoot_image* img)
|
||||
{
|
||||
img->signature_ok = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* The loaded payload is treated as a FIT container, and the sub-image
|
||||
* returned by fit_load_image() is a valid flat device tree. */
|
||||
int wolfBoot_get_dts_size(void *dts_addr)
|
||||
{
|
||||
(void)dts_addr;
|
||||
return TEST_DTS_SIZE;
|
||||
}
|
||||
|
||||
/* Only reached through the fdt_version()/fdt_totalsize() trace macros here. */
|
||||
uint32_t fdt32_to_cpu(uint32_t x)
|
||||
{
|
||||
return ((x & 0x000000FFU) << 24) | ((x & 0x0000FF00U) << 8) |
|
||||
((x & 0x00FF0000U) >> 8) | ((x & 0xFF000000U) >> 24);
|
||||
}
|
||||
|
||||
const char* fit_find_images(void* fdt, const char** pkernel,
|
||||
const char** pflat_dt, const char** pramdisk, const char** pfpga)
|
||||
{
|
||||
(void)fdt;
|
||||
if (pkernel != NULL)
|
||||
*pkernel = NULL;
|
||||
if (pflat_dt != NULL)
|
||||
*pflat_dt = "fdt";
|
||||
if (pramdisk != NULL)
|
||||
*pramdisk = NULL;
|
||||
if (pfpga != NULL)
|
||||
*pfpga = NULL;
|
||||
return "conf";
|
||||
}
|
||||
|
||||
void* fit_load_image(void* fdt, const char* image, int* lenp)
|
||||
{
|
||||
(void)fdt;
|
||||
(void)image;
|
||||
if (lenp != NULL)
|
||||
*lenp = TEST_DTS_SIZE;
|
||||
return fit_dts_image;
|
||||
}
|
||||
|
||||
int wolfBoot_fit_memcpy(void *dst, const void *src, uint32_t len)
|
||||
{
|
||||
mock_fit_memcpy_called++;
|
||||
if (mock_fit_memcpy_ret != 0)
|
||||
return mock_fit_memcpy_ret;
|
||||
memcpy(dst, src, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hal_prepare_boot(void)
|
||||
{
|
||||
}
|
||||
|
||||
void do_boot(const uint32_t *address, const uint32_t *dts_address)
|
||||
{
|
||||
(void)dts_address;
|
||||
(void)address;
|
||||
mock_do_boot_called++;
|
||||
}
|
||||
|
||||
int hal_flash_protect(haladdr_t address, int len)
|
||||
{
|
||||
(void)address;
|
||||
(void)len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include "update_disk.c"
|
||||
|
||||
/* Runs from inside wolfBoot_panic(), before it spins forever on target. */
|
||||
void wolfBoot_hook_panic(void)
|
||||
{
|
||||
mock_panic_hook_called++;
|
||||
memcpy(panic_key_snapshot, disk_encrypt_key, sizeof(panic_key_snapshot));
|
||||
memcpy(panic_nonce_snapshot, disk_encrypt_nonce,
|
||||
sizeof(panic_nonce_snapshot));
|
||||
}
|
||||
|
||||
static void assert_snapshot_zeroized(void)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < sizeof(panic_key_snapshot); i++) {
|
||||
ck_assert_uint_eq(panic_key_snapshot[i], 0);
|
||||
}
|
||||
for (i = 0; i < sizeof(panic_nonce_snapshot); i++) {
|
||||
ck_assert_uint_eq(panic_nonce_snapshot[i], 0);
|
||||
}
|
||||
}
|
||||
|
||||
START_TEST(test_update_disk_fit_dts_copy_failure_zeroizes_key_material)
|
||||
{
|
||||
reset_mocks();
|
||||
mock_fit_memcpy_ret = -1;
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
ck_assert_int_gt(mock_fit_memcpy_called, 0);
|
||||
ck_assert_int_gt(wolfBoot_panicked, 0);
|
||||
ck_assert_int_gt(mock_panic_hook_called, 0);
|
||||
assert_snapshot_zeroized();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST(test_update_disk_fit_dts_copy_success_boots)
|
||||
{
|
||||
reset_mocks();
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
ck_assert_int_eq(wolfBoot_panicked, 0);
|
||||
ck_assert_int_eq(mock_do_boot_called, 1);
|
||||
ck_assert_int_eq(memcmp(dts_buffer, fit_dts_image, TEST_DTS_SIZE), 0);
|
||||
}
|
||||
END_TEST
|
||||
|
||||
Suite *wolfboot_suite(void)
|
||||
{
|
||||
Suite *s = suite_create("wolfBoot");
|
||||
TCase *tc = tcase_create("update-disk-fit");
|
||||
|
||||
tcase_add_test(tc, test_update_disk_fit_dts_copy_failure_zeroizes_key_material);
|
||||
tcase_add_test(tc, test_update_disk_fit_dts_copy_success_boots);
|
||||
suite_add_tcase(s, tc);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int fails;
|
||||
Suite *s = wolfboot_suite();
|
||||
SRunner *sr = srunner_create(s);
|
||||
|
||||
srunner_run_all(sr, CK_NORMAL);
|
||||
fails = srunner_ntests_failed(sr);
|
||||
srunner_free(sr);
|
||||
return fails;
|
||||
}
|
||||
|
|
@ -108,7 +108,7 @@ int wc_Chacha_Process(ChaCha* ctx, byte* output, const byte* input, word32 msgle
|
|||
return 0;
|
||||
}
|
||||
|
||||
void ForceZero(void* mem, size_t len)
|
||||
void wc_ForceZero(void* mem, size_t len)
|
||||
{
|
||||
volatile uint8_t *p = (volatile uint8_t *)mem;
|
||||
while (len-- > 0) {
|
||||
|
|
|
|||
|
|
@ -601,6 +601,95 @@ static int add_payload_encrypted(uint8_t part, uint32_t version, uint32_t size,
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef EXT_ENCRYPTED
|
||||
/* ext_flash_encrypt_write() writes whole ENCRYPT_BLOCK_SIZE blocks. A request
|
||||
* whose length is not a multiple of the block size used to drop the trailing
|
||||
* bytes, and a zero-length request used to rewrite the containing block. */
|
||||
START_TEST (test_encrypt_write_keeps_trailing_partial_block)
|
||||
{
|
||||
uintptr_t base = (uintptr_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS;
|
||||
int len = (2 * ENCRYPT_BLOCK_SIZE) + 5;
|
||||
uint8_t out[(2 * ENCRYPT_BLOCK_SIZE) + 5];
|
||||
uint8_t in[(2 * ENCRYPT_BLOCK_SIZE) + 5];
|
||||
int i, ret;
|
||||
|
||||
reset_mock_stats();
|
||||
prepare_flash();
|
||||
for (i = 0; i < len; i++)
|
||||
in[i] = (uint8_t)(0x30 + i);
|
||||
|
||||
ext_flash_unlock();
|
||||
ret = ext_flash_encrypt_write(base, in, len);
|
||||
ext_flash_lock();
|
||||
ck_assert_int_ge(ret, 0);
|
||||
|
||||
memset(out, 0, sizeof(out));
|
||||
ck_assert_int_eq(ext_flash_decrypt_read(base, out, len), len);
|
||||
ck_assert_int_eq(memcmp(out, in, len), 0);
|
||||
|
||||
cleanup_flash();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* wb_flash_write() on an external encrypted partition is this function, and
|
||||
* F-7987 makes wolfBoot_copy_sector() abort the swap on a negative return. A
|
||||
* failure programming the unaligned head block must therefore propagate,
|
||||
* rather than be overwritten by the remainder loop's own status. */
|
||||
START_TEST (test_encrypt_write_reports_head_block_write_failure)
|
||||
{
|
||||
uintptr_t base = (uintptr_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS;
|
||||
uint8_t in[3 * ENCRYPT_BLOCK_SIZE];
|
||||
int i, ret;
|
||||
|
||||
reset_mock_stats();
|
||||
prepare_flash();
|
||||
for (i = 0; i < (int)sizeof(in); i++)
|
||||
in[i] = (uint8_t)(0x10 + i);
|
||||
|
||||
ext_flash_unlock();
|
||||
/* Start mid-block so the head path runs, and extend past it so the
|
||||
* remainder loop runs too. */
|
||||
ext_flash_write_fail = 1;
|
||||
ret = ext_flash_encrypt_write(base + 4, in, (2 * ENCRYPT_BLOCK_SIZE));
|
||||
ext_flash_lock();
|
||||
|
||||
ck_assert_int_lt(ret, 0);
|
||||
|
||||
cleanup_flash();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (test_encrypt_write_zero_length_leaves_flash_untouched)
|
||||
{
|
||||
uintptr_t base = (uintptr_t)WOLFBOOT_PARTITION_UPDATE_ADDRESS;
|
||||
uint8_t before[ENCRYPT_BLOCK_SIZE];
|
||||
uint8_t after[ENCRYPT_BLOCK_SIZE];
|
||||
uint8_t in[ENCRYPT_BLOCK_SIZE];
|
||||
int i, ret;
|
||||
|
||||
reset_mock_stats();
|
||||
prepare_flash();
|
||||
for (i = 0; i < ENCRYPT_BLOCK_SIZE; i++)
|
||||
in[i] = (uint8_t)(0x70 + i);
|
||||
|
||||
ext_flash_unlock();
|
||||
ck_assert_int_ge(ext_flash_encrypt_write(base, in, ENCRYPT_BLOCK_SIZE), 0);
|
||||
ck_assert_int_eq(ext_flash_read(base, before, ENCRYPT_BLOCK_SIZE),
|
||||
ENCRYPT_BLOCK_SIZE);
|
||||
|
||||
ret = ext_flash_encrypt_write(base, in, 0);
|
||||
ext_flash_lock();
|
||||
ck_assert_int_eq(ret, 0);
|
||||
|
||||
ck_assert_int_eq(ext_flash_read(base, after, ENCRYPT_BLOCK_SIZE),
|
||||
ENCRYPT_BLOCK_SIZE);
|
||||
ck_assert_int_eq(memcmp(before, after, ENCRYPT_BLOCK_SIZE), 0);
|
||||
|
||||
cleanup_flash();
|
||||
}
|
||||
END_TEST
|
||||
#endif /* EXT_ENCRYPTED */
|
||||
|
||||
START_TEST (test_empty_panic)
|
||||
{
|
||||
reset_mock_stats();
|
||||
|
|
@ -787,6 +876,27 @@ START_TEST (test_forward_update_samesize) {
|
|||
}
|
||||
END_TEST
|
||||
|
||||
/* A failing flash write must abort the swap instead of marking the sector as
|
||||
* updated: the sector flags are the only record used to resume an
|
||||
* interrupted swap. */
|
||||
START_TEST (test_update_aborts_on_sector_copy_failure) {
|
||||
uint8_t flag = SECT_FLAG_NEW;
|
||||
reset_mock_stats();
|
||||
prepare_flash();
|
||||
add_payload(PART_BOOT, 1, TEST_SIZE_SMALL);
|
||||
add_payload(PART_UPDATE, 2, TEST_SIZE_SMALL);
|
||||
wolfBoot_update_trigger();
|
||||
/* BOOT is the only internal partition here, so the first write to
|
||||
* internal flash is the copy of sector 0 from SWAP into BOOT. */
|
||||
hal_flash_write_fail = 1;
|
||||
ck_assert_int_lt(wolfBoot_update(0), 0);
|
||||
ck_assert_int_eq(hal_flash_write_fail, 0);
|
||||
wolfBoot_get_update_sector_flag(0, &flag);
|
||||
ck_assert_int_ne(flag, SECT_FLAG_UPDATED);
|
||||
cleanup_flash();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (test_forward_update_tolarger) {
|
||||
reset_mock_stats();
|
||||
prepare_flash();
|
||||
|
|
@ -1358,6 +1468,67 @@ START_TEST (test_delta_base_version_match_accepts)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (test_delta_base_hash_missing_in_boot_header_rejected)
|
||||
{
|
||||
struct wolfBoot_image boot, update, swap;
|
||||
uint32_t word;
|
||||
uint32_t delta_sz = 0x00001020;
|
||||
uint32_t delta_base = 1;
|
||||
uint8_t base_hash[SHA256_DIGEST_SIZE];
|
||||
uint8_t *boot_base = (uint8_t *)(uintptr_t)WOLFBOOT_PARTITION_BOOT_ADDRESS;
|
||||
int ret;
|
||||
|
||||
reset_mock_stats();
|
||||
prepare_flash();
|
||||
|
||||
add_payload(PART_BOOT, 1, TEST_SIZE_SMALL);
|
||||
add_payload(PART_UPDATE, 2, TEST_SIZE_SMALL);
|
||||
|
||||
/* Remove the digest TLV from the boot header, keeping the TLV chain
|
||||
* well-formed by retagging it to an unused custom type */
|
||||
hal_flash_unlock();
|
||||
word = SHA256_DIGEST_SIZE << 16 | 0x0031;
|
||||
hal_flash_write((uintptr_t)boot_base + DIGEST_TLV_OFF_IN_HDR,
|
||||
(void *)&word, 4);
|
||||
hal_flash_lock();
|
||||
|
||||
/* The delta patch declares a base digest that cannot match */
|
||||
memset(base_hash, 0xA5, sizeof(base_hash));
|
||||
|
||||
ext_flash_unlock();
|
||||
word = (4u << 16) | HDR_IMG_DELTA_SIZE;
|
||||
ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 64,
|
||||
(const uint8_t *)&word, sizeof(word));
|
||||
word = host_to_img_u32(delta_sz);
|
||||
ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 68,
|
||||
(const uint8_t *)&word, sizeof(word));
|
||||
word = (4u << 16) | HDR_IMG_DELTA_BASE;
|
||||
ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 72,
|
||||
(const uint8_t *)&word, sizeof(word));
|
||||
word = host_to_img_u32(delta_base);
|
||||
ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 76,
|
||||
(const uint8_t *)&word, sizeof(word));
|
||||
word = (SHA256_DIGEST_SIZE << 16) | HDR_IMG_DELTA_BASE_HASH;
|
||||
ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 80,
|
||||
(const uint8_t *)&word, sizeof(word));
|
||||
ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 84,
|
||||
base_hash, sizeof(base_hash));
|
||||
ext_flash_lock();
|
||||
|
||||
ck_assert_int_eq(wolfBoot_open_image(&boot, PART_BOOT), 0);
|
||||
ck_assert_int_eq(wolfBoot_open_image(&update, PART_UPDATE), 0);
|
||||
memset(&swap, 0, sizeof(swap));
|
||||
swap.part = PART_SWAP;
|
||||
swap.hdr = (void *)(uintptr_t)WOLFBOOT_PARTITION_SWAP_ADDRESS;
|
||||
|
||||
ret = wolfBoot_delta_update(&boot, &update, &swap, 0, 0);
|
||||
ck_assert_int_eq(ret, -1);
|
||||
ck_assert_int_eq(mock_wb_patch_init_calls, 0);
|
||||
|
||||
cleanup_flash();
|
||||
}
|
||||
END_TEST
|
||||
|
||||
START_TEST (test_delta_inverse_values_passed_with_native_endian)
|
||||
{
|
||||
struct wolfBoot_image boot, update, swap;
|
||||
|
|
@ -1522,6 +1693,9 @@ Suite *wolfboot_suite(void)
|
|||
TCase *fallback_verify = tcase_create("Fallback verify");
|
||||
#endif
|
||||
#endif
|
||||
#ifdef EXT_ENCRYPTED
|
||||
TCase *encrypt_write_bounds = tcase_create("Encrypted write bounds");
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef UNIT_TEST_FALLBACK_ONLY
|
||||
|
|
@ -1530,6 +1704,13 @@ Suite *wolfboot_suite(void)
|
|||
tcase_add_test(fallback_verify, test_final_swap_propagates_encrypt_key_read_failure);
|
||||
tcase_add_test(fallback_verify, test_final_swap_propagates_encrypt_key_persist_failure);
|
||||
suite_add_tcase(s, fallback_verify);
|
||||
tcase_add_test(encrypt_write_bounds,
|
||||
test_encrypt_write_keeps_trailing_partial_block);
|
||||
tcase_add_test(encrypt_write_bounds,
|
||||
test_encrypt_write_zero_length_leaves_flash_untouched);
|
||||
tcase_add_test(encrypt_write_bounds,
|
||||
test_encrypt_write_reports_head_block_write_failure);
|
||||
suite_add_tcase(s, encrypt_write_bounds);
|
||||
#endif
|
||||
return s;
|
||||
#else
|
||||
|
|
@ -1541,6 +1722,7 @@ Suite *wolfboot_suite(void)
|
|||
#endif
|
||||
tcase_add_test(sunnyday_noupdate, test_sunnyday_noupdate);
|
||||
tcase_add_test(forward_update_samesize, test_forward_update_samesize);
|
||||
tcase_add_test(forward_update_samesize, test_update_aborts_on_sector_copy_failure);
|
||||
tcase_add_test(forward_update_tolarger, test_forward_update_tolarger);
|
||||
tcase_add_test(forward_update_tosmaller, test_forward_update_tosmaller);
|
||||
tcase_add_test(forward_update_sameversion_denied, test_forward_update_sameversion_denied);
|
||||
|
|
@ -1567,6 +1749,7 @@ Suite *wolfboot_suite(void)
|
|||
tcase_add_test(delta_zero_size, test_delta_zero_size_erased_header_uses_recovery_heuristic);
|
||||
tcase_add_test(delta_base_version, test_delta_base_version_mismatch_rejected);
|
||||
tcase_add_test(delta_base_version, test_delta_base_version_match_accepts);
|
||||
tcase_add_test(delta_base_version, test_delta_base_hash_missing_in_boot_header_rejected);
|
||||
tcase_add_test(delta_base_version, test_delta_inverse_values_passed_with_native_endian);
|
||||
tcase_add_test(delta_base_version, test_delta_inverse_accepts_when_current_matches_update);
|
||||
tcase_add_test(delta_base_version, test_delta_inverse_accepts_when_current_matches_delta_base);
|
||||
|
|
|
|||
|
|
@ -170,11 +170,11 @@ static void cleanup_ram(void)
|
|||
|
||||
#define DIGEST_TLV_OFF_IN_HDR 28
|
||||
/* Write a wolfBoot image to the BOOT partition whose firmware payload is a
|
||||
* uImage: [64-byte uImage header][KERNEL_LEN kernel bytes]. ih_load is set to
|
||||
* the caller-provided value; the uImage magic/size/header-CRC are made valid so
|
||||
* uboot_legacy_header_valid() accepts it. Fills expected_kernel[] with the
|
||||
* kernel pattern for later comparison. Returns 0 on success. */
|
||||
static int add_uimage_payload(uint32_t version, uint32_t ih_load)
|
||||
* uImage: [64-byte uImage header][KERNEL_LEN kernel bytes]. ih_load/ih_ep are
|
||||
* set to the caller-provided values; the uImage magic/size/header-CRC are made
|
||||
* valid so uboot_legacy_header_valid() accepts it. Fills expected_kernel[] with
|
||||
* the kernel pattern for later comparison. Returns 0 on success. */
|
||||
static int add_uimage_payload(uint32_t version, uint32_t ih_load, uint32_t ih_ep)
|
||||
{
|
||||
uint8_t *base = (uint8_t *)WOLFBOOT_PARTITION_BOOT_ADDRESS;
|
||||
uint8_t uimg[UBOOT_IMG_HDR_SZ + KERNEL_LEN];
|
||||
|
|
@ -194,7 +194,7 @@ static int add_uimage_payload(uint32_t version, uint32_t ih_load)
|
|||
/* uimg[4..8] = ih_hcrc, left 0 while computing the header CRC. */
|
||||
store_be32(uimg + 0x0C, KERNEL_LEN); /* ih_size */
|
||||
store_be32(uimg + 0x10, ih_load); /* ih_load */
|
||||
store_be32(uimg + 0x14, ih_load); /* ih_ep (unused by wolfBoot) */
|
||||
store_be32(uimg + 0x14, ih_ep); /* ih_ep */
|
||||
for (i = 0; i < KERNEL_LEN; i++) {
|
||||
uint8_t b = (uint8_t)(0xA5u ^ (uint8_t)i);
|
||||
uimg[UBOOT_IMG_HDR_SZ + i] = b;
|
||||
|
|
@ -265,21 +265,28 @@ static void fixture_teardown(void)
|
|||
cleanup_flash();
|
||||
}
|
||||
|
||||
/* Run the full wolfBoot_start() flow for a given uImage ih_load and check that
|
||||
* do_boot() was reached with expect_addr and the kernel payload is present
|
||||
* there. (mmap setup/teardown is handled by the checked fixture.) */
|
||||
static void run_and_check(uint32_t ih_load, uintptr_t expect_addr)
|
||||
/* Run the full wolfBoot_start() flow for a given uImage ih_load/ih_ep and check
|
||||
* that the kernel payload landed at expect_load and that do_boot() was reached
|
||||
* with expect_boot. (mmap setup/teardown is handled by the checked fixture.) */
|
||||
static void run_and_check_ep(uint32_t ih_load, uint32_t ih_ep,
|
||||
uintptr_t expect_load, uintptr_t expect_boot)
|
||||
{
|
||||
ck_assert_int_eq(add_uimage_payload(1, ih_load), 0);
|
||||
ck_assert_int_eq(add_uimage_payload(1, ih_load, ih_ep), 0);
|
||||
|
||||
wolfBoot_start();
|
||||
|
||||
ck_assert_int_eq(g_boot_called, 1);
|
||||
ck_assert_uint_eq((uintptr_t)g_boot_addr, expect_addr);
|
||||
ck_assert_int_eq(memcmp((void *)expect_addr, expected_kernel, KERNEL_LEN),
|
||||
ck_assert_uint_eq((uintptr_t)g_boot_addr, expect_boot);
|
||||
ck_assert_int_eq(memcmp((void *)expect_load, expected_kernel, KERNEL_LEN),
|
||||
0);
|
||||
}
|
||||
|
||||
/* Common case: entry point == load address. */
|
||||
static void run_and_check(uint32_t ih_load, uintptr_t expect_addr)
|
||||
{
|
||||
run_and_check_ep(ih_load, ih_load, expect_addr, expect_addr);
|
||||
}
|
||||
|
||||
/* Case 1: ih_load coincides with the staged kernel address -> no relocation
|
||||
* needed; the payload is already there. */
|
||||
START_TEST (test_uboot_ihload_coincident)
|
||||
|
|
@ -306,7 +313,21 @@ START_TEST (test_uboot_ihload_lower_overlap)
|
|||
}
|
||||
END_TEST
|
||||
|
||||
/* Case 4: ih_load == 0 (Linux/PPC convention) -> load_address is only advanced
|
||||
/* Case 4: ih_ep distinct from ih_load (kernels built with a preamble ahead of
|
||||
* the entry point). U-Boot bootm copies the payload to ih_load and jumps to
|
||||
* ih_ep: the payload must land at ih_load, but do_boot() must be entered at
|
||||
* ih_ep. */
|
||||
#define UIMAGE_EP_OFFSET 0x40
|
||||
START_TEST (test_uboot_ihep_distinct)
|
||||
{
|
||||
run_and_check_ep((uint32_t)IHLOAD_HI_BASE,
|
||||
(uint32_t)(IHLOAD_HI_BASE + UIMAGE_EP_OFFSET),
|
||||
(uintptr_t)IHLOAD_HI_BASE,
|
||||
(uintptr_t)(IHLOAD_HI_BASE + UIMAGE_EP_OFFSET));
|
||||
}
|
||||
END_TEST
|
||||
|
||||
/* Case 5: ih_load == 0 (Linux/PPC convention) -> load_address is only advanced
|
||||
* past the 64-byte header; behavior is unchanged by the fix. */
|
||||
START_TEST (test_uboot_ihload_zero)
|
||||
{
|
||||
|
|
@ -320,26 +341,31 @@ Suite *wolfboot_suite(void)
|
|||
TCase *coincident = tcase_create("uImage ih_load coincident");
|
||||
TCase *higher = tcase_create("uImage ih_load higher");
|
||||
TCase *lower = tcase_create("uImage ih_load lower overlap");
|
||||
TCase *ep = tcase_create("uImage ih_ep distinct");
|
||||
TCase *zero = tcase_create("uImage ih_load zero");
|
||||
|
||||
tcase_add_checked_fixture(coincident, fixture_setup, fixture_teardown);
|
||||
tcase_add_checked_fixture(higher, fixture_setup, fixture_teardown);
|
||||
tcase_add_checked_fixture(lower, fixture_setup, fixture_teardown);
|
||||
tcase_add_checked_fixture(ep, fixture_setup, fixture_teardown);
|
||||
tcase_add_checked_fixture(zero, fixture_setup, fixture_teardown);
|
||||
|
||||
tcase_add_test(coincident, test_uboot_ihload_coincident);
|
||||
tcase_add_test(higher, test_uboot_ihload_higher);
|
||||
tcase_add_test(lower, test_uboot_ihload_lower_overlap);
|
||||
tcase_add_test(ep, test_uboot_ihep_distinct);
|
||||
tcase_add_test(zero, test_uboot_ihload_zero);
|
||||
|
||||
suite_add_tcase(s, coincident);
|
||||
suite_add_tcase(s, higher);
|
||||
suite_add_tcase(s, lower);
|
||||
suite_add_tcase(s, ep);
|
||||
suite_add_tcase(s, zero);
|
||||
|
||||
tcase_set_timeout(coincident, 5);
|
||||
tcase_set_timeout(higher, 5);
|
||||
tcase_set_timeout(lower, 5);
|
||||
tcase_set_timeout(ep, 5);
|
||||
tcase_set_timeout(zero, 5);
|
||||
|
||||
return s;
|
||||
|
|
|
|||
Loading…
Reference in New Issue