Fixes to delta updates, reinforced size checks, improved hardening (#716)

Fixes to delta updates, reinforced size checks, improved hardening
pull/704/head
David Garske 2026-03-09 15:30:17 -04:00 committed by GitHub
commit a910645309
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 1089 additions and 57 deletions

View File

@ -82,6 +82,9 @@ const uint8_t __attribute__((section(".sig_wolfboot_raw")))
#define FSP_STATUS_RESET_REQUIRED_WARM 0x40000002
#define MEMORY_4GB (4ULL * 1024 * 1024 * 1024)
#define ENDLINE "\r\n"
/* Standard PCI capabilities live in conventional config space at 0x40-0xFC,
* on 4-byte alignment, so there are at most 48 distinct capability headers. */
#define PCI_MAX_STANDARD_CAPABILITIES 48
/* compile time alignment checks */
@ -257,13 +260,23 @@ static void jump_into_wolfboot(void)
/* The image needs to be already verified */
int wolfBoot_image_measure(uint8_t *image)
{
uint16_t hash_len;
uint8_t *hash;
struct wolfBoot_image img;
int ret;
hash_len = wolfBoot_find_header(image + IMAGE_HEADER_OFFSET,
WOLFBOOT_SHA_HDR, &hash);
wolfBoot_print_hexstr(hash, hash_len, 0);
return wolfBoot_tpm2_extend(WOLFBOOT_MEASURED_PCR_A, hash, __LINE__);
memset(&img, 0, sizeof(img));
ret = wolfBoot_open_image_address(&img, image);
if (ret != 0) {
return ret;
}
ret = wolfBoot_verify_integrity(&img);
if (ret != 0 || img.sha_hash == NULL) {
return -1;
}
wolfBoot_print_hexstr(img.sha_hash, WOLFBOOT_SHA_DIGEST_SIZE, 0);
return wolfBoot_tpm2_extend(WOLFBOOT_MEASURED_PCR_A, img.sha_hash,
__LINE__);
}
#endif /* WOLFBOOT_MEASURED_BOOT */
@ -332,19 +345,21 @@ static int pci_get_capability(uint8_t bus, uint8_t dev, uint8_t fun,
uint8_t cap_id, uint8_t *cap_off)
{
uint8_t r8, id;
uint8_t cap_count = 0;
uint32_t r32;
r32 = pci_config_read16(bus, dev, fun, PCI_STATUS_OFFSET);
if (!(r32 & PCI_STATUS_CAP_LIST))
return -1;
r8 = pci_config_read8(bus, dev, fun, PCI_CAP_OFFSET);
while (r8 != 0) {
while ((r8 != 0) && (cap_count < PCI_MAX_STANDARD_CAPABILITIES)) {
id = pci_config_read8(bus, dev, fun, r8);
if (id == cap_id) {
*cap_off = r8;
return 0;
}
r8 = pci_config_read8(bus, dev, fun, r8 + 1);
cap_count++;
}
return -1;
}

View File

@ -135,6 +135,8 @@ int wb_patch(WB_PATCH_CTX *ctx, uint8_t *dst, uint32_t len)
continue;
}
if (*pp == ESC) {
if ((ctx->patch_size - ctx->p_off) < 2)
return -1;
if (*(pp + 1) == ESC) {
*(dst + dst_off) = ESC;
/* Two bytes of the patch have been consumed to produce ESC */
@ -142,6 +144,8 @@ int wb_patch(WB_PATCH_CTX *ctx, uint8_t *dst, uint32_t len)
dst_off++;
continue;
} else {
if ((ctx->patch_size - ctx->p_off) < BLOCK_HDR_SIZE)
return -1;
hdr = (struct block_hdr *)pp;
src_off = (hdr->off[0] << 16) + (hdr->off[1] << 8) +
hdr->off[2];

View File

@ -34,6 +34,23 @@
#include "gpt.h"
static uint32_t gpt_crc32(const uint8_t *data, uint32_t len)
{
uint32_t crc = 0xFFFFFFFFU;
uint32_t i;
uint32_t j;
for (i = 0; i < len; i++) {
crc ^= data[i];
for (j = 0; j < 8; j++) {
uint32_t mask = -(crc & 1U);
crc = (crc >> 1) ^ (0xEDB88320U & mask);
}
}
return ~crc;
}
/**
* @brief Check MBR for protective GPT partition entry.
*
@ -98,6 +115,7 @@ int gpt_check_mbr_protective(const uint8_t *mbr_sector, uint32_t *gpt_lba)
int gpt_parse_header(const uint8_t *sector, struct guid_ptable *hdr)
{
const struct guid_ptable *src;
struct guid_ptable tmp;
if (sector == NULL || hdr == NULL) {
return -1;
@ -110,6 +128,16 @@ int gpt_parse_header(const uint8_t *sector, struct guid_ptable *hdr)
return -1;
}
if (src->hdr_size < 0x5C || src->hdr_size > GPT_SECTOR_SIZE) {
return -1;
}
memcpy(&tmp, src, sizeof(tmp));
tmp.hdr_crc32 = 0;
if (gpt_crc32((const uint8_t *)&tmp, src->hdr_size) != src->hdr_crc32) {
return -1;
}
/* Copy header to output */
memcpy(hdr, src, sizeof(struct guid_ptable));

View File

@ -58,6 +58,19 @@
/* Globals */
static uint8_t digest[WOLFBOOT_SHA_DIGEST_SIZE] XALIGNED(4);
static int image_CT_compare(const uint8_t *expected, const uint8_t *actual,
uint32_t len)
{
uint8_t diff = 0;
uint32_t i;
for (i = 0; i < len; i++) {
diff |= expected[i] ^ actual[i];
}
return diff == 0;
}
#if defined(WOLFBOOT_CERT_CHAIN_VERIFY) && \
(defined(WOLFBOOT_ENABLE_WOLFHSM_CLIENT) || \
defined(WOLFBOOT_ENABLE_WOLFHSM_SERVER))
@ -367,6 +380,9 @@ static void wolfBoot_verify_signature_ecc(uint8_t key_slot,
static inline int DecodeAsn1Tag(const uint8_t* input, int inputSz, int* inOutIdx,
int* tag_len, uint8_t tag)
{
if (*inOutIdx < 0 || *inOutIdx >= inputSz || (*inOutIdx + 1) >= inputSz) {
return -1;
}
if (input[*inOutIdx] != tag) {
return -1;
}
@ -1492,6 +1508,12 @@ int wolfBoot_open_self_address(struct wolfBoot_image* img, uint8_t* hdr,
img->hdr = hdr;
img->fw_size = wolfBoot_image_size(hdr);
#ifdef WOLFBOOT_FIXED_PARTITIONS
if (img->fw_size > (WOLFBOOT_PARTITION_SIZE - IMAGE_HEADER_SIZE)) {
img->fw_size = WOLFBOOT_PARTITION_SIZE - IMAGE_HEADER_SIZE;
return -1;
}
#endif
img->fw_base = image;
img->part = PART_SELF;
img->hdr_ok = 1;
@ -1518,7 +1540,7 @@ int wolfBoot_verify_integrity(struct wolfBoot_image *img)
return -1;
if (image_hash(img, digest) != 0)
return -1;
if (memcmp(digest, stored_sha, stored_sha_len) != 0)
if (!image_CT_compare(digest, stored_sha, stored_sha_len))
return -1;
img->sha_ok = 1;
img->sha_hash = stored_sha;
@ -2366,6 +2388,21 @@ uint8_t* wolfBoot_peek_image(struct wolfBoot_image *img, uint32_t offset,
#if !defined(WOLFBOOT_NO_SIGN) && !defined(WOLFBOOT_RENESAS_SCEPROTECT)
/* Compare fixed-size key hints without early exit to avoid leaking hash prefix
* matches through lookup timing. */
static int keyslot_CT_hint_matches(const uint8_t *expected,
const uint8_t *actual)
{
uint8_t diff = 0;
uint32_t i;
for (i = 0; i < WOLFBOOT_SHA_DIGEST_SIZE; i++) {
diff |= expected[i] ^ actual[i];
}
return diff == 0;
}
/**
* @brief Get the key slot ID by SHA hash.
*
@ -2378,13 +2415,14 @@ uint8_t* wolfBoot_peek_image(struct wolfBoot_image *img, uint32_t offset,
int keyslot_id_by_sha(const uint8_t *hint)
{
int id;
int match_id = -1;
for (id = 0; id < keystore_num_pubkeys(); id++) {
key_hash(id, digest);
if (memcmp(digest, hint, WOLFBOOT_SHA_DIGEST_SIZE) == 0) {
return id;
if ((match_id < 0) && keyslot_CT_hint_matches(digest, hint)) {
match_id = id;
}
}
return -1;
return match_id;
}
#endif /* !WOLFBOOT_NO_SIGN && !WOLFBOOT_RENESAS_SCEPROTECT */

View File

@ -107,8 +107,8 @@ int wolfBoot_initialize_encryption(void)
(2 * WOLFBOOT_SECTOR_SIZE)))
/* MAGIC (4B) + PART_FLAG (1B) + (N_SECTORS / 2) */
#define START_FLAGS_OFFSET (ENCRYPT_TMP_SECRET_OFFSET - TRAILER_OVERHEAD)
#define SECTOR_FLAGS_SIZE WOLFBOOT_SECTOR_SIZE - (4 + 1 + \
ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE)
#define SECTOR_FLAGS_SIZE (WOLFBOOT_SECTOR_SIZE - (4 + 1 + \
ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE))
/* MAGIC (4B) + PART_FLAG (1B) + ENCRYPT_KEY_SIZE + ENCRYPT_NONCE_SIZE */
#else
#define ENCRYPT_TMP_SECRET_OFFSET (WOLFBOOT_PARTITION_SIZE - (TRAILER_SKIP))

View File

@ -506,8 +506,8 @@ int wolfPKCS11_Store_Write(void* store, unsigned char* buffer, int len)
% WOLFBOOT_SECTOR_SIZE;
sector_base = (uintptr_t)handle->buffer + handle->in_buffer_offset - in_sector_offset;
in_sector_len = WOLFBOOT_SECTOR_SIZE - in_sector_offset;
if (in_sector_len > (uint32_t)len)
in_sector_len = len;
if (in_sector_len > (uint32_t)(len - written))
in_sector_len = len - written;
/* Cache the corresponding sector */
memcpy(cached_sector, (void *)(uintptr_t)sector_base, WOLFBOOT_SECTOR_SIZE);

View File

@ -512,8 +512,8 @@ int wolfPSA_Store_Write(void* store, unsigned char* buffer, int len)
% WOLFBOOT_SECTOR_SIZE;
sector_base = (uintptr_t)handle->buffer + handle->in_buffer_offset - in_sector_offset;
in_sector_len = WOLFBOOT_SECTOR_SIZE - in_sector_offset;
if (in_sector_len > (uint32_t)len)
in_sector_len = len;
if (in_sector_len > (uint32_t)(len - written))
in_sector_len = len - written;
/* Cache the corresponding sector */
memcpy(cached_sector, (void *)(uintptr_t)sector_base, WOLFBOOT_SECTOR_SIZE);

View File

@ -417,8 +417,10 @@ int spi_flash_read(uint32_t address, void *data, int len)
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;
uint8_t* ptr = (uint8_t*)data;
#ifdef DEBUG_QSPI
wolfBoot_printf("QSPI Flash Write: Len %d, %p -> 0x%x\n",
@ -430,13 +432,12 @@ int spi_flash_write(uint32_t address, const void *data, int len)
for (page = 0; page < pages; page++) {
ret = qspi_write_enable();
if (ret == 0) {
uint8_t* ptr;
xferSz = len;
if (xferSz > FLASH_PAGE_SIZE)
xferSz = (uint32_t)remaining;
if (xferSz > FLASH_PAGE_SIZE) {
xferSz = FLASH_PAGE_SIZE;
}
addr = address + (page * FLASH_PAGE_SIZE);
ptr = ((uint8_t*)data + (page * FLASH_PAGE_SIZE));
/* ------ Write Flash (page at a time) ------ */
ret = qspi_transfer(QSPI_MODE_WRITE, FLASH_WRITE_CMD,
@ -459,6 +460,8 @@ int spi_flash_write(uint32_t address, const void *data, int len)
break;
}
/* write disable is automatic */
remaining -= (int)xferSz;
ptr += xferSz;
}
}

View File

@ -361,7 +361,7 @@ void uart_vprintf(const char* fmt, va_list argp)
if (*fmtp == '0' && maxdigits == 0) {
zeropad = 1;
}
maxdigits <<= 8;
maxdigits *= 10;
maxdigits += (*fmtp - '0');
fmtp++;
}

View File

@ -382,6 +382,8 @@ int wolfBoot_load_pubkey(const uint8_t* pubkey_hint, WOLFTPM2_KEY* pubKey,
defined(WOLFBOOT_SIGN_RSA3072) || \
defined(WOLFBOOT_SIGN_RSA4096)
uint32_t inOutIdx = 0;
uint32_t exponent = 0;
uint32_t j;
const uint8_t*n = NULL, *e = NULL;
uint32_t nSz = 0, eSz = 0;
if (key_type != AUTH_KEY_RSA2048 && key_type != AUTH_KEY_RSA3072 &&
@ -395,10 +397,19 @@ int wolfBoot_load_pubkey(const uint8_t* pubkey_hint, WOLFTPM2_KEY* pubKey,
&e, &eSz /* exponent */
);
}
if (rc == 0) {
if (eSz == 0 || eSz > sizeof(exponent))
rc = -1;
}
if (rc == 0) {
for (j = 0; j < eSz; j++) {
exponent = (exponent << 8) | e[j];
}
}
if (rc == 0) {
/* Load public key into TPM */
rc = wolfTPM2_LoadRsaPublicKey_ex(&wolftpm_dev, pubKey,
n, nSz, *((uint32_t*)e),
n, nSz, exponent,
TPM_ALG_NULL, WOLFBOOT_TPM_HASH_ALG);
}
#else

View File

@ -45,6 +45,15 @@ int WP11_Library_Init(void);
#ifdef EXT_ENCRYPTED
#include "encrypt.h"
static void wolfBoot_zeroize(void *ptr, size_t len)
{
volatile uint8_t *p = (volatile uint8_t *)ptr;
while (len-- > 0) {
*p++ = 0;
}
}
#endif /* EXT_ENCRYPTED */
#ifdef MMU
@ -224,6 +233,7 @@ void RAMFUNCTION wolfBoot_check_self_update(void)
static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src,
struct wolfBoot_image *dst, uint32_t sector)
{
int ret = 0;
uint32_t pos = 0;
uint32_t src_sector_offset = (sector * WOLFBOOT_SECTOR_SIZE);
uint32_t dst_sector_offset = src_sector_offset;
@ -245,8 +255,10 @@ static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src,
dst_sector_offset = 0;
#ifdef EXT_ENCRYPTED
if (wolfBoot_initialize_encryption() < 0)
return -1;
if (wolfBoot_initialize_encryption() < 0) {
ret = -1;
goto out;
}
wolfBoot_get_encrypt_key(key, nonce);
if (src->part == PART_SWAP)
@ -286,7 +298,8 @@ static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src,
}
pos += FLASHBUFFER_SIZE;
}
return pos;
ret = pos;
goto out;
}
#endif
wb_flash_erase(dst, dst_sector_offset, WOLFBOOT_SECTOR_SIZE);
@ -298,12 +311,21 @@ static int RAMFUNCTION wolfBoot_copy_sector(struct wolfBoot_image *src,
}
pos += FLASHBUFFER_SIZE;
}
return pos;
ret = pos;
#if defined(EXT_FLASH) || defined(EXT_ENCRYPTED)
out:
#endif
#ifdef EXT_ENCRYPTED
wolfBoot_zeroize(key, sizeof(key));
wolfBoot_zeroize(nonce, sizeof(nonce));
#endif
return ret;
}
#ifdef EXT_ENCRYPTED
static int RAMFUNCTION wolfBoot_backup_last_boot_sector(uint32_t sector)
{
int ret = 0;
uint32_t pos = 0;
uint32_t src_sector_offset = (sector * WOLFBOOT_SECTOR_SIZE);
uint32_t dst_sector_offset = 0;
@ -325,8 +347,10 @@ static int RAMFUNCTION wolfBoot_backup_last_boot_sector(uint32_t sector)
iv_counter = src_sector_offset;
iv_counter /= ENCRYPT_BLOCK_SIZE;
if (wolfBoot_initialize_encryption() < 0)
return -1;
if (wolfBoot_initialize_encryption() < 0) {
ret = -1;
goto out;
}
/*
* Preserve the IV sequence used by the source sector so that the staging
* copy in SWAP can be decrypted with exactly the same keystream when it is
@ -345,9 +369,14 @@ static int RAMFUNCTION wolfBoot_backup_last_boot_sector(uint32_t sector)
wb_flash_write(dst, dst_sector_offset + pos, encrypted_block, ENCRYPT_BLOCK_SIZE);
pos += ENCRYPT_BLOCK_SIZE;
}
return 0;
} else
return wolfBoot_copy_sector(src, dst, sector);
ret = 0;
} else {
ret = wolfBoot_copy_sector(src, dst, sector);
}
out:
wolfBoot_zeroize(key, sizeof(key));
wolfBoot_zeroize(nonce, sizeof(nonce));
return ret;
}
#else
#define wolfBoot_backup_last_boot_sector(sec) wolfBoot_copy_sector(boot, swap, sec)
@ -701,6 +730,10 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot,
sector++;
}
out:
#ifdef EXT_ENCRYPTED
wolfBoot_zeroize(key, sizeof(key));
wolfBoot_zeroize(nonce, sizeof(nonce));
#endif
#ifdef EXT_FLASH
ext_flash_lock();
#endif

View File

@ -32,7 +32,7 @@ bmdiff.o:
gcc -c -o bmdiff.o bmdiff.c -I../../include -ggdb $(CFLAGS)
clean:
rm -f bmpatch bmdiff delta.o
rm -f bmpatch bmdiff bmdiff-test delta.o test-bmdiff.o
delta-test: FORCE bmdiff bmpatch
@./bmdiff delta-test/0.txt delta-test/1.txt 0-to-1.patch
@ -45,4 +45,13 @@ delta-test: FORCE bmdiff bmpatch
@diff 1p.txt delta-test/0.txt && echo "Test 1-to-0: OK"
@rm -f 0-to-1.patch 1-to-0.patch 0p.txt 1p.txt
bmdiff-test: test-bmdiff.o
gcc -o bmdiff-test test-bmdiff.o
test-bmdiff.o:
gcc -c -o test-bmdiff.o test-bmdiff.c -I../../include -ggdb $(CFLAGS)
test: FORCE bmdiff-test
@./bmdiff-test && echo "bmdiff mmap failure test: OK"
.PHONY: FORCE

View File

@ -98,7 +98,7 @@ int main(int argc, char *argv[])
}
len2 = st.st_size;
buffer = mmap(NULL, len2, PROT_READ, MAP_SHARED, fd2, 0);
if (base == (void *)(-1)) {
if (buffer == (void *)(-1)) {
perror("mmap");
exit(3);
}

View File

@ -0,0 +1,136 @@
#include <assert.h>
#include <setjmp.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
static jmp_buf exit_jmp;
static int exit_code = -1;
static int mmap_calls = 0;
static int patch_init_called = 0;
static int perror_called = 0;
static int mock_stat(const char *path, struct stat *st)
{
memset(st, 0, sizeof(*st));
if (strcmp(path, "source.bin") == 0) {
st->st_size = 16;
return 0;
}
if (strcmp(path, "patch.bin") == 0) {
st->st_size = 8;
return 0;
}
return -1;
}
static int mock_open(const char *path, int flags, ...)
{
(void)flags;
if ((strcmp(path, "source.bin") == 0) || (strcmp(path, "patch.bin") == 0))
return 3;
return -1;
}
static void *mock_mmap(void *addr, size_t len, int prot, int flags, int fd,
off_t offset)
{
static uint8_t source_map[16];
(void)addr;
(void)len;
(void)prot;
(void)flags;
(void)fd;
(void)offset;
mmap_calls++;
if (mmap_calls == 1)
return source_map;
return MAP_FAILED;
}
static void mock_perror(const char *s)
{
(void)s;
perror_called = 1;
}
static void mock_exit(int code)
{
exit_code = code;
longjmp(exit_jmp, 1);
}
#define WOLFBOOT_SECTOR_SIZE 1024
#define stat(path, st) mock_stat(path, st)
#define open(path, flags, ...) mock_open(path, flags, ##__VA_ARGS__)
#define mmap(addr, len, prot, flags, fd, offset) \
mock_mmap(addr, len, prot, flags, fd, offset)
#define perror(s) mock_perror(s)
#define exit(code) mock_exit(code)
#define main bmdiff_tool_main
#include "bmdiff.c"
#undef main
#undef exit
#undef perror
#undef mmap
#undef open
#undef stat
int wb_patch_init(WB_PATCH_CTX *bm, uint8_t *src, uint32_t ssz, uint8_t *patch,
uint32_t psz)
{
(void)bm;
(void)src;
(void)ssz;
(void)patch;
(void)psz;
patch_init_called = 1;
mock_exit(99);
return -1;
}
int wb_patch(WB_PATCH_CTX *ctx, uint8_t *dst, uint32_t len)
{
(void)ctx;
(void)dst;
(void)len;
return 0;
}
int wb_diff_init(WB_DIFF_CTX *ctx, uint8_t *src_a, uint32_t len_a,
uint8_t *src_b, uint32_t len_b)
{
(void)ctx;
(void)src_a;
(void)len_a;
(void)src_b;
(void)len_b;
return 0;
}
int wb_diff(WB_DIFF_CTX *ctx, uint8_t *patch, uint32_t len)
{
(void)ctx;
(void)patch;
(void)len;
return 0;
}
int main(void)
{
char *argv[] = { (char *)"bmpatch", (char *)"source.bin", (char *)"patch.bin" };
if (setjmp(exit_jmp) == 0)
bmdiff_tool_main(3, argv);
assert(exit_code == 3);
assert(perror_called == 1);
assert(patch_init_called == 0);
return 0;
}

View File

@ -1144,13 +1144,13 @@ test-all: clean
test-size-all:
make test-size SIGN=NONE LIMIT=5040 NO_ARM_ASM=1
make test-size SIGN=NONE LIMIT=5060 NO_ARM_ASM=1
make keysclean
make test-size SIGN=ED25519 LIMIT=11724 NO_ARM_ASM=1
make test-size SIGN=ED25519 LIMIT=11778 NO_ARM_ASM=1
make keysclean
make test-size SIGN=ECC256 LIMIT=18944 NO_ARM_ASM=1
make clean
make test-size SIGN=ECC256 NO_ASM=1 LIMIT=13856 NO_ARM_ASM=1
make test-size SIGN=ECC256 NO_ASM=1 LIMIT=13894 NO_ARM_ASM=1
make keysclean
make test-size SIGN=RSA2048 LIMIT=11916 NO_ARM_ASM=1
make clean
@ -1162,9 +1162,9 @@ test-size-all:
make keysclean
make test-size SIGN=ECC384 LIMIT=19888 NO_ARM_ASM=1
make clean
make test-size SIGN=ECC384 NO_ASM=1 LIMIT=15232 NO_ARM_ASM=1
make test-size SIGN=ECC384 NO_ASM=1 LIMIT=15270 NO_ARM_ASM=1
make keysclean
make test-size SIGN=ED448 LIMIT=13776 NO_ARM_ASM=1
make test-size SIGN=ED448 LIMIT=13846 NO_ARM_ASM=1
make keysclean
make test-size SIGN=RSA3072 LIMIT=12056 NO_ARM_ASM=1
make clean
@ -1172,12 +1172,12 @@ test-size-all:
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=7712 NO_ARM_ASM=1
IMAGE_HEADER_SIZE?=5288 LIMIT=7782 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=8568 NO_ARM_ASM=1
LIMIT=8638 NO_ARM_ASM=1
make keysclean
make clean
make test-size SIGN=ML_DSA ML_DSA_LEVEL=2 LIMIT=19362 \
make test-size SIGN=ML_DSA ML_DSA_LEVEL=2 LIMIT=19392 \
IMAGE_SIGNATURE_SIZE=2420 IMAGE_HEADER_SIZE?=8192

View File

@ -8,10 +8,21 @@ endif
# Default library paths (can be overridden)
WOLFBOOT_LIB_WOLFSSL?=../../lib/wolfssl
WOLFBOOT_LIB_WOLFPKCS11?=../../lib/wolfPKCS11
WOLFBOOT_LIB_WOLFPSA?=../../lib/wolfPSA
WOLFBOOT_LIB_WOLFTPM?=../../lib/wolfTPM
ifeq ($(wildcard $(WOLFBOOT_LIB_WOLFPSA)),)
WOLFBOOT_LIB_WOLFPSA=../../../external-libs/wolfPSA
endif
ifeq ($(wildcard $(WOLFBOOT_LIB_WOLFTPM)),)
WOLFBOOT_LIB_WOLFTPM=../../../external-libs/wolfTPM
endif
# Convert to absolute paths for standalone usage
WOLFBOOT_LIB_WOLFSSL:=$(abspath $(WOLFBOOT_LIB_WOLFSSL))
WOLFBOOT_LIB_WOLFPKCS11:=$(abspath $(WOLFBOOT_LIB_WOLFPKCS11))
WOLFBOOT_LIB_WOLFPSA:=$(abspath $(WOLFBOOT_LIB_WOLFPSA))
WOLFBOOT_LIB_WOLFTPM:=$(abspath $(WOLFBOOT_LIB_WOLFTPM))
CFLAGS=-I. -I../../src -I../../include -I$(WOLFBOOT_LIB_WOLFSSL)
CFLAGS+=-g -ggdb
@ -33,10 +44,10 @@ endif
TESTS:=unit-parser unit-extflash unit-string unit-spi-flash unit-aes128 \
unit-aes256 unit-chacha20 unit-pci unit-mock-state unit-sectorflags \
unit-image unit-nvm unit-nvm-flagshome unit-enc-nvm \
unit-image unit-image-rsa unit-nvm unit-nvm-flagshome unit-enc-nvm \
unit-enc-nvm-flagshome unit-delta unit-update-flash \
unit-update-flash-enc unit-update-ram unit-pkcs11_store unit-disk \
unit-multiboot
unit-update-flash-enc unit-update-ram unit-pkcs11_store unit-psa_store unit-disk \
unit-multiboot unit-boot-x86-fsp unit-qspi-flash unit-tpm-rsa-exp
all: $(TESTS)
@ -75,6 +86,7 @@ unit-enc-nvm-flagshome:CFLAGS+=-DNVM_FLASH_WRITEONCE -DMOCK_PARTITIONS \
unit-enc-nvm-flagshome:WOLFCRYPT_SRC+=$(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/chacha.c
unit-delta:CFLAGS+=-DNVM_FLASH_WRITEONCE -DMOCK_PARTITIONS -DDELTA_UPDATES -DDELTA_BLOCK_SIZE=512
unit-pkcs11_store:CFLAGS+=-I$(WOLFBOOT_LIB_WOLFPKCS11) -DMOCK_PARTITIONS -DMOCK_KEYVAULT -DSECURE_PKCS11
unit-psa_store:CFLAGS+=-I$(WOLFBOOT_LIB_WOLFPSA) -DMOCK_PARTITIONS -DMOCK_KEYVAULT -DWOLFCRYPT_TZ_PSA
unit-update-flash:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNIT_TEST_AUTH \
-DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH -DPART_UPDATE_EXT -DPART_SWAP_EXT
unit-update-ram:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNIT_TEST_AUTH \
@ -103,6 +115,15 @@ unit-extflash: ../../include/target.h unit-extflash.c
unit-spi-flash: ../../include/target.h unit-spi-flash.c
gcc -o $@ $^ $(CFLAGS) $(LDFLAGS)
unit-qspi-flash: ../../include/target.h unit-qspi-flash.c
gcc -o $@ $^ $(CFLAGS) $(LDFLAGS)
unit-tpm-rsa-exp: ../../include/target.h unit-tpm-rsa-exp.c
gcc -o $@ $^ $(CFLAGS) -I$(WOLFBOOT_LIB_WOLFTPM) -DWOLFBOOT_TPM \
-DWOLFTPM_USER_SETTINGS -DWOLFBOOT_TPM_VERIFY -DWOLFBOOT_SIGN_RSA2048 \
-DWOLFBOOT_HASH_SHA256 \
-ffunction-sections -fdata-sections $(LDFLAGS) -Wl,--gc-sections
unit-string: ../../include/target.h unit-string.c
gcc -o $@ $^ $(CFLAGS) -DDEBUG_UART -DPRINTF_ENABLED $(LDFLAGS)
@ -118,6 +139,11 @@ unit-chacha20: ../../include/target.h unit-extflash.c
unit-pci: unit-pci.c ../../src/pci.c
gcc -o $@ $< $(CFLAGS) -DWOLFBOOT_USE_PCI $(LDFLAGS)
unit-boot-x86-fsp: ../../include/target.h unit-boot-x86_fsp.c
gcc -o $@ $^ $(CFLAGS) -DWOLFBOOT_LOAD_BASE=0x100000 -DWOLFBOOT_FSP \
-DUCODE0_ADDRESS=0 -ffunction-sections -fdata-sections $(LDFLAGS) \
-Wl,--gc-sections
unit-mock-state: ../../include/target.h unit-mock-state.c
gcc -o $@ $^ $(CFLAGS) $(LDFLAGS)
@ -127,6 +153,11 @@ unit-sectorflags: ../../include/target.h unit-sectorflags.c
unit-image: unit-image.c unit-common.c $(WOLFCRYPT_SRC)
gcc -o $@ $^ $(CFLAGS) $(WOLFCRYPT_CFLAGS) $(LDFLAGS)
unit-image-rsa: CFLAGS += -DWOLFBOOT_SIGN_RSA2048
unit-image-rsa: ../../include/target.h unit-image.c unit-common.c
gcc -o $@ unit-image.c unit-common.c $(WOLFCRYPT_SRC) \
$(CFLAGS) -D__WOLFBOOT $(LDFLAGS)
unit-nvm: ../../include/target.h unit-nvm.c
gcc -o $@ unit-nvm.c $(CFLAGS) $(LDFLAGS)
@ -161,6 +192,9 @@ unit-update-ram: ../../include/target.h unit-update-ram.c
unit-pkcs11_store: ../../include/target.h unit-pkcs11_store.c
gcc -o $@ $(WOLFCRYPT_SRC) unit-pkcs11_store.c $(CFLAGS) $(WOLFCRYPT_CFLAGS) $(LDFLAGS)
unit-psa_store: ../../include/target.h unit-psa_store.c
gcc -o $@ $(WOLFCRYPT_SRC) unit-psa_store.c $(CFLAGS) $(WOLFCRYPT_CFLAGS) $(LDFLAGS)
gpt-sfdisk-test.h:
truncate -s 131072 .gpt-tmp.img
printf 'label: gpt\nfirst-lba: 34\nstart=34, size=67, name="boot"\nstart=101, size=100, name="rootfs"\n' \

View File

@ -0,0 +1,111 @@
/* unit-boot-x86_fsp.c
*
* Unit tests for selected boot_x86_fsp helpers.
*/
#include <check.h>
#include <stdint.h>
#include <string.h>
#define TEST_CFG_SIZE 256
#define TEST_READ_LIMIT 200
static uint8_t test_cfg[TEST_CFG_SIZE];
static unsigned int test_read_count;
uint8_t pci_config_read8(uint8_t bus, uint8_t dev, uint8_t fun, uint8_t off)
{
(void)bus;
(void)dev;
(void)fun;
test_read_count++;
ck_assert_msg(test_read_count < TEST_READ_LIMIT,
"pci_get_capability exceeded read limit on cyclic list");
return test_cfg[off];
}
uint16_t pci_config_read16(uint8_t bus, uint8_t dev, uint8_t fun, uint8_t off)
{
uint16_t value;
(void)bus;
(void)dev;
(void)fun;
test_read_count++;
ck_assert_msg(test_read_count < TEST_READ_LIMIT,
"pci_get_capability exceeded read limit on cyclic list");
memcpy(&value, &test_cfg[off], sizeof(value));
return value;
}
#include "../../src/boot_x86_fsp.c"
static void setup(void)
{
memset(test_cfg, 0, sizeof(test_cfg));
test_read_count = 0;
}
START_TEST(test_pci_get_capability_finds_requested_capability)
{
uint8_t cap_off = 0;
uint16_t status = PCI_STATUS_CAP_LIST;
memcpy(&test_cfg[PCI_STATUS_OFFSET], &status, sizeof(status));
test_cfg[PCI_CAP_OFFSET] = 0x40;
test_cfg[0x40] = 0x01;
test_cfg[0x41] = 0x48;
test_cfg[0x48] = PCI_PCIE_CAP_ID;
test_cfg[0x49] = 0x00;
ck_assert_int_eq(pci_get_capability(0, 0, 0, PCI_PCIE_CAP_ID, &cap_off), 0);
ck_assert_uint_eq(cap_off, 0x48);
}
END_TEST
START_TEST(test_pci_get_capability_rejects_cyclic_capability_lists)
{
uint8_t cap_off = 0xAA;
uint16_t status = PCI_STATUS_CAP_LIST;
memcpy(&test_cfg[PCI_STATUS_OFFSET], &status, sizeof(status));
test_cfg[PCI_CAP_OFFSET] = 0x40;
test_cfg[0x40] = 0x01;
test_cfg[0x41] = 0x48;
test_cfg[0x48] = 0x05;
test_cfg[0x49] = 0x40;
ck_assert_int_eq(pci_get_capability(0, 0, 0, PCI_PCIE_CAP_ID, &cap_off), -1);
ck_assert_uint_eq(cap_off, 0xAA);
}
END_TEST
static Suite *boot_x86_fsp_suite(void)
{
Suite *s;
TCase *tc;
s = suite_create("boot_x86_fsp");
tc = tcase_create("pci_get_capability");
tcase_add_checked_fixture(tc, setup, NULL);
tcase_add_test(tc, test_pci_get_capability_finds_requested_capability);
tcase_add_test(tc, test_pci_get_capability_rejects_cyclic_capability_lists);
suite_add_tcase(s, tc);
return s;
}
int main(void)
{
Suite *s;
SRunner *sr;
int failed;
s = boot_x86_fsp_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
failed = srunner_ntests_failed(sr);
srunner_free(sr);
return failed == 0 ? 0 : 1;
}

View File

@ -114,6 +114,22 @@ START_TEST(test_wb_patch_resume_large_len)
}
END_TEST
START_TEST(test_wb_patch_trailing_escape_invalid)
{
WB_PATCH_CTX patch_ctx;
uint8_t src[SRC_SIZE] = {0};
uint8_t patch[1] = {ESC};
uint8_t dst[DELTA_BLOCK_SIZE] = {0};
int ret;
ret = wb_patch_init(&patch_ctx, src, SRC_SIZE, patch, sizeof(patch));
ck_assert_int_eq(ret, 0);
ret = wb_patch(&patch_ctx, dst, sizeof(dst));
ck_assert_int_eq(ret, -1);
}
END_TEST
START_TEST(test_wb_diff_init_invalid)
{
WB_DIFF_CTX ctx;
@ -230,6 +246,7 @@ Suite *patch_diff_suite(void)
tcase_add_test(tc_wolfboot_delta, test_wb_patch_src_bounds_invalid);
tcase_add_test(tc_wolfboot_delta, test_wb_patch_resume_bounds_invalid);
tcase_add_test(tc_wolfboot_delta, test_wb_patch_resume_large_len);
tcase_add_test(tc_wolfboot_delta, test_wb_patch_trailing_escape_invalid);
tcase_add_test(tc_wolfboot_delta, test_wb_patch_and_diff);
suite_add_tcase(s, tc_wolfboot_delta);

View File

@ -65,6 +65,29 @@ static const int PART0_END = 100;
static const int PART1_OFF = 101;
static const int PART1_END = 200;
static uint32_t test_crc32(const uint8_t *data, uint32_t len)
{
uint32_t crc = 0xFFFFFFFFU;
uint32_t i;
uint32_t j;
for (i = 0; i < len; i++) {
crc ^= data[i];
for (j = 0; j < 8; j++) {
uint32_t mask = -(crc & 1U);
crc = (crc >> 1) ^ (0xEDB88320U & mask);
}
}
return ~crc;
}
static void finalize_gpt_header_crc(struct guid_ptable *hdr)
{
hdr->hdr_crc32 = 0;
hdr->hdr_crc32 = test_crc32((const uint8_t *)hdr, hdr->hdr_size);
}
/* --- Helpers to build fake disk layouts --- */
/* Write a UTF-16LE string into a buffer (no BOM).
@ -133,6 +156,8 @@ static void build_gpt_disk(void)
(PART0_END - PART0_OFF + 1) * GPT_SECTOR_SIZE);
memset(fake_disk + PART1_OFF * GPT_SECTOR_SIZE, 0xBB,
(PART1_END - PART1_OFF + 1) * GPT_SECTOR_SIZE);
finalize_gpt_header_crc(gpt_hdr);
}
/* Populate fake_disk with MBR-only layout (no GPT protective entry).
@ -200,6 +225,17 @@ START_TEST(test_gpt_parse_header)
ck_assert_uint_eq(hdr.start_array, 2);
ck_assert_uint_eq(hdr.array_sz, 128);
/* Corrupt a header field without updating CRC */
{
struct guid_ptable *gpt_hdr =
(struct guid_ptable *)(fake_disk + GPT_SECTOR_SIZE);
gpt_hdr->n_part = 3;
ck_assert_int_eq(
gpt_parse_header(fake_disk + GPT_SECTOR_SIZE, &hdr), -1);
gpt_hdr->n_part = 2;
finalize_gpt_header_crc(gpt_hdr);
}
/* Corrupt signature in the real header */
{
struct guid_ptable *gpt_hdr =
@ -208,6 +244,7 @@ START_TEST(test_gpt_parse_header)
ck_assert_int_eq(
gpt_parse_header(fake_disk + GPT_SECTOR_SIZE, &hdr), -1);
gpt_hdr->signature = GPT_SIGNATURE;
finalize_gpt_header_crc(gpt_hdr);
}
/* NULL inputs */
@ -537,6 +574,7 @@ START_TEST(test_disk_open_gpt_excess_partitions)
gpt_hdr = (struct guid_ptable *)(fake_disk + GPT_SECTOR_SIZE);
gpt_hdr->n_part = MAX_PARTITIONS + 10;
finalize_gpt_header_crc(gpt_hdr);
/* Only 2 actual entries on disk so loop will break after parsing them,
* but the capping branch is exercised. */
@ -555,6 +593,7 @@ START_TEST(test_disk_open_gpt_large_array_sz)
gpt_hdr = (struct guid_ptable *)(fake_disk + GPT_SECTOR_SIZE);
gpt_hdr->array_sz = GPT_PART_ENTRY_SIZE + 1; /* 257 > 256 */
finalize_gpt_header_crc(gpt_hdr);
ck_assert_int_eq(disk_open(0), 0); /* 0 partitions found */
}
@ -571,6 +610,7 @@ START_TEST(test_disk_open_gpt_empty_entry_mid_table)
gpt_hdr = (struct guid_ptable *)(fake_disk + GPT_SECTOR_SIZE);
gpt_hdr->n_part = 3;
finalize_gpt_header_crc(gpt_hdr);
/* Zero out entry 1's type GUID */
pe = (struct gpt_part_entry *)(fake_disk + 2 * GPT_SECTOR_SIZE + 128);

View File

@ -94,7 +94,7 @@ uint8_t flash[FLASH_SIZE];
/* Mocks for ext_flash_read, ext_flash_write, and ext_flash_erase functions */
int ext_flash_read(uintptr_t address, uint8_t *data, int len) {
printf("Called ext_flash_read %p %p %d\n", address, data, len);
printf("Called ext_flash_read %p %p %d\n", (void *)address, (void *)data, len);
/* Check that the read address and size are within the bounds of the flash memory */
ck_assert_int_le(address + len, FLASH_SIZE);
@ -106,7 +106,7 @@ 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", address, data, len);
printf("Called ext_flash_write %p %p %d\n", (void *)address, (const void *)data, len);
/* Check that the write address and size are within the bounds of the flash memory */

View File

@ -28,6 +28,7 @@
#define EXT_FLASH
#define PART_UPDATE_EXT
#define NVM_FLASH_WRITEONCE
#define WOLFBOOT_SELF_HEADER
#if defined(ENCRYPT_WITH_AES256) || defined(ENCRYPT_WITH_AES128)
#define WOLFSSL_AES_COUNTER
@ -61,6 +62,10 @@
#include "wolfssl/wolfcrypt/sha.h"
#include "wolfboot/wolfboot.h"
#ifndef ARCH_FLASH_OFFSET
#define ARCH_FLASH_OFFSET WOLFBOOT_PARTITION_BOOT_ADDRESS
#endif
#include "unit-keystore.c"
#include "image.c"
@ -76,6 +81,11 @@ static int find_header_fail = 0;
static int find_header_called = 0;
static int find_header_mocked = 1;
uint8_t *wolfBoot_get_self_header(void)
{
return NULL;
}
#if defined(WOLFBOOT_SIGN_ECC256)
static const unsigned char pubkey_digest[SHA256_DIGEST_SIZE] = {
0x17, 0x20, 0xa5, 0x9b, 0xe0, 0x9b, 0x80, 0x0c, 0xaa, 0xc4, 0xf5, 0x3f,
@ -309,7 +319,7 @@ uint16_t wolfBoot_find_header(uint8_t *haystack, uint16_t type, uint8_t **ptr)
}
}
#if defined(WOLFBOOT_SIGN_ECC256)
int wc_ecc_init(ecc_key* key) {
if (ecc_init_fail)
return -1;
@ -338,7 +348,9 @@ int wc_ecc_verify_hash_ex(mp_int *r, mp_int *s, const byte* hash,
*res = 1;
return 0;
}
#endif
#if defined(WOLFBOOT_SIGN_ECC256)
START_TEST(test_verify_signature)
{
uint8_t pubkey[32];
@ -373,6 +385,73 @@ START_TEST(test_verify_signature)
}
END_TEST
START_TEST(test_keyslot_id_by_sha_scans_all_slots)
{
int id;
unit_keystore_reset_counters();
id = keyslot_id_by_sha(pubkey_digest);
ck_assert_int_eq(id, 0);
ck_assert_int_eq(unit_keystore_get_buffer_calls(), keystore_num_pubkeys());
ck_assert_int_eq(unit_keystore_get_size_calls(), keystore_num_pubkeys());
}
END_TEST
#endif
#if defined(WOLFBOOT_SIGN_RSA2048) || defined(WOLFBOOT_SIGN_RSA3072) || \
defined(WOLFBOOT_SIGN_RSA4096) || defined(WOLFBOOT_SIGN_SECONDARY_RSA2048) || \
defined(WOLFBOOT_SIGN_SECONDARY_RSA3072) || \
defined(WOLFBOOT_SIGN_SECONDARY_RSA4096)
int wc_InitRsaKey(RsaKey* key, void* heap)
{
(void)key;
(void)heap;
return 0;
}
int wc_FreeRsaKey(RsaKey* key)
{
(void)key;
return 0;
}
int wc_RsaPublicKeyDecode(const byte* input, word32* inOutIdx, RsaKey* key,
word32 inSz)
{
(void)input;
(void)inOutIdx;
(void)key;
(void)inSz;
return 0;
}
int wc_RsaSSL_VerifyInline(byte* in, word32 inLen, byte** out, RsaKey* key)
{
(void)in;
(void)inLen;
(void)out;
(void)key;
return 0;
}
START_TEST(test_decode_asn1_tag_start_bounds)
{
uint8_t *input = malloc(1);
int idx = 0;
volatile int input_sz = 1;
int tag_len = -1;
ck_assert_ptr_nonnull(input);
input[0] = ASN_SEQUENCE | ASN_CONSTRUCTED;
ck_assert_int_eq(DecodeAsn1Tag(input, input_sz, &idx, &tag_len,
ASN_SEQUENCE | ASN_CONSTRUCTED), -1);
free(input);
}
END_TEST
#endif
START_TEST(test_sha_ops)
{
@ -507,6 +586,7 @@ START_TEST(test_headers)
ck_assert_uint_eq(sz, test_img_len - 256);
}
#if defined(WOLFBOOT_SIGN_ECC256)
START_TEST(test_verify_authenticity)
{
struct wolfBoot_image test_img;
@ -573,6 +653,7 @@ START_TEST(test_verify_authenticity_bad_siglen)
ck_assert_int_eq(ret, -1);
}
END_TEST
#endif
START_TEST(test_verify_integrity)
{
@ -606,6 +687,8 @@ START_TEST(test_open_image)
{
struct wolfBoot_image img;
int ret;
uint8_t self_hdr[IMAGE_HEADER_SIZE];
uint32_t oversize;
/* invalid argument */
@ -651,6 +734,17 @@ START_TEST(test_open_image)
ck_assert_ptr_eq(img.hdr, (void *)WOLFBOOT_PARTITION_UPDATE_ADDRESS);
ck_assert_ptr_eq(img.fw_base, (uint8_t *)WOLFBOOT_PARTITION_UPDATE_ADDRESS
+ 256);
/* Self header must reject sizes beyond the partition payload budget */
memset(self_hdr, 0xFF, sizeof(self_hdr));
((uint32_t *)self_hdr)[0] = WOLFBOOT_MAGIC;
oversize = WOLFBOOT_PARTITION_SIZE - IMAGE_HEADER_SIZE + 1;
((uint32_t *)self_hdr)[1] = oversize;
memset(&img, 0, sizeof(img));
ret = wolfBoot_open_self_address(&img, self_hdr,
(uint8_t *)WOLFBOOT_PARTITION_BOOT_ADDRESS);
ck_assert_int_eq(ret, -1);
}
END_TEST
@ -660,11 +754,36 @@ Suite *wolfboot_suite(void)
/* Suite initialization */
Suite *s = suite_create("wolfBoot");
#if defined(WOLFBOOT_SIGN_ECC256)
TCase* tcase_verify_signature = tcase_create("verify_signature");
tcase_set_timeout(tcase_verify_signature, 20);
tcase_add_test(tcase_verify_signature, test_verify_signature);
tcase_add_test(tcase_verify_signature, test_keyslot_id_by_sha_scans_all_slots);
suite_add_tcase(s, tcase_verify_signature);
#endif
#if defined(WOLFBOOT_SIGN_RSA2048) || defined(WOLFBOOT_SIGN_RSA3072) || \
defined(WOLFBOOT_SIGN_RSA4096) || defined(WOLFBOOT_SIGN_SECONDARY_RSA2048) || \
defined(WOLFBOOT_SIGN_SECONDARY_RSA3072) || \
defined(WOLFBOOT_SIGN_SECONDARY_RSA4096)
TCase* tcase_rsa_asn1 = tcase_create("rsa_asn1");
tcase_set_timeout(tcase_rsa_asn1, 20);
tcase_add_test(tcase_rsa_asn1, test_decode_asn1_tag_start_bounds);
suite_add_tcase(s, tcase_rsa_asn1);
#endif
#if defined(WOLFBOOT_SIGN_ECC256)
TCase* tcase_verify_authenticity = tcase_create("verify_authenticity");
tcase_set_timeout(tcase_verify_authenticity, 20);
tcase_add_test(tcase_verify_authenticity, test_verify_authenticity);
tcase_add_test(tcase_verify_authenticity, test_verify_authenticity_bad_siglen);
suite_add_tcase(s, tcase_verify_authenticity);
#endif
#if !defined(WOLFBOOT_SIGN_RSA2048) && !defined(WOLFBOOT_SIGN_RSA3072) && \
!defined(WOLFBOOT_SIGN_RSA4096) && !defined(WOLFBOOT_SIGN_SECONDARY_RSA2048) && \
!defined(WOLFBOOT_SIGN_SECONDARY_RSA3072) && \
!defined(WOLFBOOT_SIGN_SECONDARY_RSA4096)
TCase* tcase_sha_ops = tcase_create("sha_ops");
tcase_set_timeout(tcase_sha_ops, 20);
tcase_add_test(tcase_sha_ops, test_sha_ops);
@ -675,12 +794,6 @@ Suite *wolfboot_suite(void)
tcase_add_test(tcase_headers, test_headers);
suite_add_tcase(s, tcase_headers);
TCase* tcase_verify_authenticity = tcase_create("verify_authenticity");
tcase_set_timeout(tcase_verify_authenticity, 20);
tcase_add_test(tcase_verify_authenticity, test_verify_authenticity);
tcase_add_test(tcase_verify_authenticity, test_verify_authenticity_bad_siglen);
suite_add_tcase(s, tcase_verify_authenticity);
TCase* tcase_verify_integrity = tcase_create("verify_integrity");
tcase_set_timeout(tcase_verify_integrity, 20);
tcase_add_test(tcase_verify_integrity, test_verify_integrity);
@ -690,6 +803,7 @@ Suite *wolfboot_suite(void)
tcase_set_timeout(tcase_open_image, 20);
tcase_add_test(tcase_open_image, test_open_image);
suite_add_tcase(s, tcase_open_image);
#endif
return s;
}

View File

@ -99,7 +99,11 @@
#endif
#endif
#define NUM_PUBKEYS 1
#define NUM_PUBKEYS 3
static int keystore_get_buffer_calls;
static int keystore_get_size_calls;
const KEYSTORE_SECTION struct keystore_slot PubKeys[NUM_PUBKEYS] = {
/* Key associated to file 'wolfboot_signing_private_key.der' */
@ -110,6 +114,20 @@ const KEYSTORE_SECTION struct keystore_slot PubKeys[NUM_PUBKEYS] = {
.pubkey_size = UNIT_PUBKEY_SIZE,
.pubkey = UNIT_PUBKEY_INIT,
},
{
.slot_id = 1,
.key_type = UNIT_KEY_TYPE,
.part_id_mask = 0xFFFFFFFF,
.pubkey_size = UNIT_PUBKEY_SIZE,
.pubkey = { 0x00 },
},
{
.slot_id = 2,
.key_type = UNIT_KEY_TYPE,
.part_id_mask = 0xFFFFFFFF,
.pubkey_size = UNIT_PUBKEY_SIZE,
.pubkey = { 0x01 },
},
};
@ -123,6 +141,7 @@ uint8_t *keystore_get_buffer(int id)
{
if (id >= keystore_num_pubkeys())
return (uint8_t *)0;
keystore_get_buffer_calls++;
return (uint8_t *)PubKeys[id].pubkey;
}
@ -130,6 +149,7 @@ int keystore_get_size(int id)
{
if (id >= keystore_num_pubkeys())
return -1;
keystore_get_size_calls++;
return (int)PubKeys[id].pubkey_size;
}
@ -145,4 +165,20 @@ uint32_t keystore_get_key_type(int id)
return PubKeys[id].key_type;
}
void unit_keystore_reset_counters(void)
{
keystore_get_buffer_calls = 0;
keystore_get_size_calls = 0;
}
int unit_keystore_get_buffer_calls(void)
{
return keystore_get_buffer_calls;
}
int unit_keystore_get_size_calls(void)
{
return keystore_get_size_calls;
}
#endif /* WOLFBOOT_NO_SIGN */

View File

@ -242,8 +242,8 @@ START_TEST (test_store_and_load_objs) {
/* Read out the content */
memset(secret_rd, 0, KEYVAULT_OBJ_SIZE);
ret = wolfPKCS11_Store_Read(store, secret_rd, KEYVAULT_OBJ_SIZE);
ck_assert(ret == KEYVAULT_OBJ_SIZE - 8);
ck_assert(strncmp(dante_filler, secret_rd, KEYVAULT_OBJ_SIZE - 8) == 0);
ck_assert_int_eq(ret, strlen(dante_filler) + 1);
ck_assert(strncmp(dante_filler, secret_rd, strlen(dante_filler) + 1) == 0);
wolfPKCS11_Store_Close(store);
/* Reopen for writing, test truncate */
@ -280,14 +280,55 @@ START_TEST (test_store_and_load_objs) {
}
END_TEST
START_TEST(test_cross_sector_write_preserves_length)
{
const int type = DYNAMIC_TYPE_RSA;
const CK_ULONG id_tok = 7;
const CK_ULONG id_obj = 9;
void *store = NULL;
unsigned char *payload;
struct store_handle *handle;
int ret;
payload = malloc(WOLFBOOT_SECTOR_SIZE);
ck_assert_ptr_nonnull(payload);
for (ret = 0; ret < WOLFBOOT_SECTOR_SIZE; ret++)
payload[ret] = (unsigned char)(ret & 0xFF);
ret = mmap_file("/tmp/wolfboot-unit-keyvault.bin", vault_base,
keyvault_size, NULL);
ck_assert_int_eq(ret, 0);
memset(vault_base, 0xEE, keyvault_size);
ret = wolfPKCS11_Store_Open(type, id_tok, id_obj, 0, &store);
ck_assert_int_eq(ret, 0);
ck_assert_ptr_nonnull(store);
ret = wolfPKCS11_Store_Write(store, payload, WOLFBOOT_SECTOR_SIZE);
ck_assert_int_eq(ret, WOLFBOOT_SECTOR_SIZE);
handle = store;
ck_assert_uint_eq(handle->in_buffer_offset,
2 * sizeof(uint32_t) + WOLFBOOT_SECTOR_SIZE);
ck_assert_uint_eq(handle->hdr->size,
2 * sizeof(uint32_t) + WOLFBOOT_SECTOR_SIZE);
wolfPKCS11_Store_Close(store);
free(payload);
}
END_TEST
Suite *wolfboot_suite(void)
{
/* Suite initialization */
Suite *s = suite_create("wolfBoot-pkcs11-store");
TCase* tcase_store_and_load_objs = tcase_create("store_and_load_objs");
TCase* tcase_cross_sector_write = tcase_create("cross_sector_write");
tcase_add_test(tcase_store_and_load_objs, test_store_and_load_objs);
tcase_add_test(tcase_cross_sector_write, test_cross_sector_write_preserves_length);
suite_add_tcase(s, tcase_store_and_load_objs);
suite_add_tcase(s, tcase_cross_sector_write);
return s;
}

View File

@ -0,0 +1,113 @@
/* unit-psa_store.c
*
* Unit test for PSA storage module
*
* 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_HASH_SHA256
#define EXT_FLASH
#define PART_UPDATE_EXT
#define NVM_FLASH_WRITEONCE
#define KEYSTORE_PUBKEY_SIZE KEYSTORE_PUBKEY_SIZE_ECC256
#include <check.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#define XMALLOC_OVERRIDE
#define XMALLOC(n,h,t) malloc(n)
#define XFREE(p,h,t) free(p)
#include "user_settings.h"
#include "wolfssl/wolfcrypt/sha.h"
#include "wolfssl/wolfcrypt/error-crypt.h"
#include "wolfboot/wolfboot.h"
#include "wolfpsa/psa_store.h"
#include "hal.h"
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#define MOCK_ADDRESS 0xCF000000
uint8_t *vault_base = (uint8_t *)MOCK_ADDRESS;
#include "psa_store.c"
const uint32_t keyvault_size = KEYVAULT_OBJ_SIZE * KEYVAULT_MAX_ITEMS + 2 * WOLFBOOT_SECTOR_SIZE;
#include "unit-mock-flash.c"
START_TEST(test_cross_sector_write_preserves_length)
{
enum { type = WOLFPSA_STORE_KEY };
const unsigned long id1 = 7;
const unsigned long id2 = 9;
void *store = NULL;
unsigned char *payload;
struct store_handle *handle;
int ret;
payload = malloc(WOLFBOOT_SECTOR_SIZE);
ck_assert_ptr_nonnull(payload);
for (ret = 0; ret < WOLFBOOT_SECTOR_SIZE; ret++)
payload[ret] = (unsigned char)(ret & 0xFF);
ret = mmap_file("/tmp/wolfboot-unit-psa-keyvault.bin", vault_base,
keyvault_size, NULL);
ck_assert_int_eq(ret, 0);
memset(vault_base, 0xEE, keyvault_size);
ret = wolfPSA_Store_Open(type, id1, id2, 0, &store);
ck_assert_int_eq(ret, 0);
ck_assert_ptr_nonnull(store);
ret = wolfPSA_Store_Write(store, payload, WOLFBOOT_SECTOR_SIZE);
ck_assert_int_eq(ret, WOLFBOOT_SECTOR_SIZE);
handle = store;
ck_assert_uint_eq(handle->in_buffer_offset,
2 * sizeof(uint32_t) + WOLFBOOT_SECTOR_SIZE);
ck_assert_uint_eq(handle->hdr->size,
2 * sizeof(uint32_t) + WOLFBOOT_SECTOR_SIZE);
wolfPSA_Store_Close(store);
free(payload);
}
END_TEST
Suite *wolfboot_suite(void)
{
Suite *s = suite_create("wolfBoot-psa-store");
TCase *tcase_write = tcase_create("cross_sector_write");
tcase_add_test(tcase_write, test_cross_sector_write_preserves_length);
suite_add_tcase(s, tcase_write);
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;
}

View File

@ -0,0 +1,116 @@
/* unit-qspi-flash.c
*
* Unit tests for qspi_flash.c.
*/
#define QSPI_FLASH
#include <check.h>
#include <stdint.h>
#include <string.h>
static int program_call_count;
static uint32_t program_sizes[8];
static const uint8_t *program_ptrs[8];
static uint32_t program_addrs[8];
void spi_init(int polarity, int phase)
{
(void)polarity;
(void)phase;
}
void spi_release(void)
{
}
#include "../../src/qspi_flash.c"
int qspi_transfer(uint8_t fmode, const uint8_t cmd,
uint32_t addr, uint32_t addrSz, uint32_t addrMode,
uint32_t alt, uint32_t altSz, uint32_t altMode,
uint32_t dummySz,
uint8_t* data, uint32_t dataSz, uint32_t dataMode)
{
(void)fmode;
(void)addrSz;
(void)addrMode;
(void)alt;
(void)altSz;
(void)altMode;
(void)dummySz;
(void)dataMode;
if (cmd == READ_SR_CMD) {
ck_assert_ptr_nonnull(data);
ck_assert_uint_ge(dataSz, 1);
data[0] = FLASH_SR_WRITE_EN;
return 0;
}
if (cmd == FLASH_WRITE_CMD) {
ck_assert_int_lt(program_call_count, (int)(sizeof(program_sizes) / sizeof(program_sizes[0])));
program_sizes[program_call_count] = dataSz;
program_ptrs[program_call_count] = data;
program_addrs[program_call_count] = addr;
program_call_count++;
return 0;
}
return 0;
}
static void setup(void)
{
program_call_count = 0;
memset(program_sizes, 0, sizeof(program_sizes));
memset(program_ptrs, 0, sizeof(program_ptrs));
memset(program_addrs, 0, sizeof(program_addrs));
}
START_TEST(test_qspi_write_splits_last_page_to_remaining_bytes)
{
uint8_t buf[300];
int ret;
memset(buf, 0xA5, sizeof(buf));
ret = spi_flash_write(0x1000, buf, sizeof(buf));
ck_assert_int_eq(ret, 0);
ck_assert_int_eq(program_call_count, 2);
ck_assert_uint_eq(program_sizes[0], FLASH_PAGE_SIZE);
ck_assert_ptr_eq(program_ptrs[0], buf);
ck_assert_uint_eq(program_addrs[0], 0x1000);
ck_assert_uint_eq(program_sizes[1], sizeof(buf) - FLASH_PAGE_SIZE);
ck_assert_ptr_eq(program_ptrs[1], buf + FLASH_PAGE_SIZE);
ck_assert_uint_eq(program_addrs[1], 0x1000 + FLASH_PAGE_SIZE);
}
END_TEST
static Suite *qspi_flash_suite(void)
{
Suite *s;
TCase *tc;
s = suite_create("QSPI Flash");
tc = tcase_create("Write");
tcase_add_checked_fixture(tc, setup, NULL);
tcase_add_test(tc, test_qspi_write_splits_last_page_to_remaining_bytes);
suite_add_tcase(s, tc);
return s;
}
int main(void)
{
Suite *s;
SRunner *sr;
int failed;
s = qspi_flash_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
failed = srunner_ntests_failed(sr);
srunner_free(sr);
return failed == 0 ? 0 : 1;
}

View File

@ -0,0 +1,133 @@
/* unit-tpm-rsa-exp.c
*
* Unit tests for TPM RSA public-key loading.
*/
#include <check.h>
#include <stdint.h>
#include <string.h>
#ifndef SPI_CS_TPM
#define SPI_CS_TPM 1
#endif
#ifndef WOLFBOOT_SHA_DIGEST_SIZE
#define WOLFBOOT_SHA_DIGEST_SIZE 32
#endif
#ifndef WOLFBOOT_TPM_HASH_ALG
#define WOLFBOOT_TPM_HASH_ALG TPM_ALG_SHA256
#endif
#include "wolfboot/wolfboot.h"
#include "keystore.h"
#include "tpm.h"
static uint8_t test_hdr[16];
static uint8_t test_modulus[256];
static uint8_t test_exponent_der[] = { 0xAA, 0x01, 0x00, 0x01, 0x7B };
static uint32_t captured_exponent;
int keyslot_id_by_sha(const uint8_t* pubkey_hint)
{
(void)pubkey_hint;
return 0;
}
uint32_t keystore_get_key_type(int id)
{
ck_assert_int_eq(id, 0);
return AUTH_KEY_RSA2048;
}
uint8_t *keystore_get_buffer(int id)
{
ck_assert_int_eq(id, 0);
return test_hdr;
}
int keystore_get_size(int id)
{
ck_assert_int_eq(id, 0);
return (int)sizeof(test_hdr);
}
int wc_RsaPublicKeyDecode_ex(const byte* input, word32* inOutIdx, word32 inSz,
const byte** n, word32* nSz, const byte** e, word32* eSz)
{
(void)input;
(void)inSz;
*inOutIdx = 0;
*n = test_modulus;
*nSz = sizeof(test_modulus);
*e = &test_exponent_der[1];
*eSz = 3;
return 0;
}
int wolfTPM2_LoadRsaPublicKey_ex(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key,
const byte* rsaPub, word32 rsaPubSz, word32 exponent,
TPM_ALG_ID scheme, TPMI_ALG_HASH hashAlg)
{
(void)dev;
(void)key;
(void)rsaPub;
(void)rsaPubSz;
(void)scheme;
(void)hashAlg;
captured_exponent = exponent;
return 0;
}
#include "../../src/tpm.c"
static void setup(void)
{
memset(test_hdr, 0x42, sizeof(test_hdr));
memset(test_modulus, 0x5A, sizeof(test_modulus));
captured_exponent = 0;
}
START_TEST(test_wolfBoot_load_pubkey_decodes_der_exponent_bytes)
{
uint8_t hint[WOLFBOOT_SHA_DIGEST_SIZE] = { 0 };
WOLFTPM2_KEY key;
TPM_ALG_ID alg = TPM_ALG_NULL;
int rc;
memset(&key, 0, sizeof(key));
rc = wolfBoot_load_pubkey(hint, &key, &alg);
ck_assert_int_eq(rc, 0);
ck_assert_int_eq(alg, TPM_ALG_RSA);
ck_assert_uint_eq(captured_exponent, 65537U);
}
END_TEST
static Suite *tpm_suite(void)
{
Suite *s;
TCase *tc;
s = suite_create("TPM RSA");
tc = tcase_create("wolfBoot_load_pubkey");
tcase_add_checked_fixture(tc, setup, NULL);
tcase_add_test(tc, test_wolfBoot_load_pubkey_decodes_der_exponent_bytes);
suite_add_tcase(s, tc);
return s;
}
int main(void)
{
Suite *s;
SRunner *sr;
int failed;
s = tpm_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
failed = srunner_ntests_failed(sr);
srunner_free(sr);
return failed == 0 ? 0 : 1;
}