From 45749ed2518eb05aead1bc7a565a02bdd1e9f411 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 14:53:07 +0100 Subject: [PATCH 01/17] STM32H7: Fix wrong block size in flash ops F/367 --- hal/stm32h7.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/hal/stm32h7.c b/hal/stm32h7.c index 839af50116..9fbf229607 100644 --- a/hal/stm32h7.c +++ b/hal/stm32h7.c @@ -160,7 +160,7 @@ int RAMFUNCTION hal_flash_write(uint32_t address, const uint8_t *data, int len) void RAMFUNCTION hal_flash_unlock(void) { - flash_wait_complete(1); + flash_wait_complete(0); if ((FLASH_CR1 & FLASH_CR_LOCK) != 0) { FLASH_KEYR1 = FLASH_KEY1; DMB(); @@ -170,7 +170,7 @@ void RAMFUNCTION hal_flash_unlock(void) ; } - flash_wait_complete(2); + flash_wait_complete(1); if ((FLASH_CR2 & FLASH_CR_LOCK) != 0) { FLASH_KEYR2 = FLASH_KEY1; DMB(); @@ -183,11 +183,11 @@ void RAMFUNCTION hal_flash_unlock(void) void RAMFUNCTION hal_flash_lock(void) { - flash_wait_complete(1); + flash_wait_complete(0); if ((FLASH_CR1 & FLASH_CR_LOCK) == 0) FLASH_CR1 |= FLASH_CR_LOCK; - flash_wait_complete(2); + flash_wait_complete(1); if ((FLASH_CR2 & FLASH_CR_LOCK) == 0) FLASH_CR2 |= FLASH_CR_LOCK; } @@ -619,4 +619,3 @@ int hal_flash_otp_read(uint32_t flashAddress, void* data, uint32_t length) } #endif /* FLASH_OTP_KEYSTORE */ - From 68a1b010e6422b8e43ed3074c04dbd8e2adb6326 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 15:07:08 +0100 Subject: [PATCH 02/17] Validate blob size from TPM NV storage F/372 --- src/tpm.c | 22 ++-- tools/unit-tests/Makefile | 9 +- tools/unit-tests/unit-tpm-blob.c | 173 +++++++++++++++++++++++++++++++ 3 files changed, 197 insertions(+), 7 deletions(-) create mode 100644 tools/unit-tests/unit-tpm-blob.c diff --git a/src/tpm.c b/src/tpm.c index f63bc53b85..d629cc8a3e 100644 --- a/src/tpm.c +++ b/src/tpm.c @@ -696,9 +696,14 @@ int wolfBoot_read_blob(uint32_t nvIndex, WOLFTPM2_KEYBLOB* blob, (uint8_t*)&blob->pub.size, &readSz, pos); if (rc == 0) { pos += readSz; - readSz = blob->pub.size; - rc = wolfTPM2_NVReadAuth(&wolftpm_dev, &nv, nv.handle.hndl, - pubAreaBuffer, &readSz, pos); + if (blob->pub.size > sizeof(pubAreaBuffer)) { + rc = BUFFER_E; + } + else { + readSz = blob->pub.size; + rc = wolfTPM2_NVReadAuth(&wolftpm_dev, &nv, nv.handle.hndl, + pubAreaBuffer, &readSz, pos); + } } if (rc == 0) { pos += readSz; @@ -712,9 +717,14 @@ int wolfBoot_read_blob(uint32_t nvIndex, WOLFTPM2_KEYBLOB* blob, } if (rc == 0) { pos += sizeof(blob->priv.size); - readSz = blob->priv.size; - rc = wolfTPM2_NVReadAuth(&wolftpm_dev, &nv, nv.handle.hndl, - blob->priv.buffer, &readSz, pos); + if (blob->priv.size > sizeof(blob->priv.buffer)) { + rc = BUFFER_E; + } + else { + readSz = blob->priv.size; + rc = wolfTPM2_NVReadAuth(&wolftpm_dev, &nv, nv.handle.hndl, + blob->priv.buffer, &readSz, pos); + } } if (rc == 0) { pos += blob->priv.size; diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index 19033cb009..a7fed7203e 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -47,7 +47,8 @@ TESTS:=unit-parser unit-extflash unit-string unit-spi-flash unit-aes128 \ 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-psa_store unit-disk \ - unit-multiboot unit-boot-x86-fsp unit-qspi-flash unit-tpm-rsa-exp + unit-multiboot unit-boot-x86-fsp unit-qspi-flash unit-tpm-rsa-exp \ + unit-tpm-blob all: $(TESTS) @@ -124,6 +125,12 @@ unit-tpm-rsa-exp: ../../include/target.h unit-tpm-rsa-exp.c -DWOLFBOOT_HASH_SHA256 \ -ffunction-sections -fdata-sections $(LDFLAGS) -Wl,--gc-sections +unit-tpm-blob: ../../include/target.h unit-tpm-blob.c + gcc -o $@ $^ $(CFLAGS) -I$(WOLFBOOT_LIB_WOLFTPM) -DWOLFBOOT_TPM \ + -DWOLFTPM_USER_SETTINGS -DWOLFBOOT_TPM_SEAL -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) diff --git a/tools/unit-tests/unit-tpm-blob.c b/tools/unit-tests/unit-tpm-blob.c new file mode 100644 index 0000000000..b96a12ebb6 --- /dev/null +++ b/tools/unit-tests/unit-tpm-blob.c @@ -0,0 +1,173 @@ +/* unit-tpm-blob.c + * + * Unit tests for TPM blob NV reads. + */ + +#include +#include +#include +#include +#include + +#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 "tpm.h" + +enum mock_mode { + MOCK_OVERSIZE_PUB, + MOCK_OVERSIZE_PRIV +}; + +static enum mock_mode current_mode; +static int nvread_calls; +static int oversized_pub_read_attempted; +static int oversized_priv_read_attempted; + +int wolfBoot_printf(const char* fmt, ...) +{ + (void)fmt; + return 0; +} + +int wolfTPM2_SetAuthHandle(WOLFTPM2_DEV* dev, int index, + const WOLFTPM2_HANDLE* handle) +{ + (void)dev; + (void)index; + (void)handle; + return 0; +} + +const char* TPM2_GetRCString(int rc) +{ + (void)rc; + return "mock"; +} + +int TPM2_ParsePublic(TPM2B_PUBLIC* pub, byte* buf, word32 size, int* sizeUsed) +{ + (void)buf; + *sizeUsed = 0; + pub->size = (UINT16)size; + return 0; +} + +int wolfTPM2_NVReadAuth(WOLFTPM2_DEV* dev, WOLFTPM2_NV* nv, + word32 nvIndex, byte* dataBuf, word32* pDataSz, word32 offset) +{ + (void)dev; + (void)nv; + (void)nvIndex; + (void)offset; + + nvread_calls++; + + switch (nvread_calls) { + case 1: + if (current_mode == MOCK_OVERSIZE_PUB) { + *(uint16_t*)dataBuf = (uint16_t)(sizeof(TPM2B_PUBLIC) + 1); + } + else { + *(uint16_t*)dataBuf = 4; + } + *pDataSz = sizeof(uint16_t); + return 0; + case 2: + if (current_mode == MOCK_OVERSIZE_PUB) { + oversized_pub_read_attempted = 1; + return -100; + } + memset(dataBuf, 0, *pDataSz); + return 0; + case 3: + *(uint16_t*)dataBuf = (uint16_t)(sizeof(((WOLFTPM2_KEYBLOB*)0)->priv.buffer) + 1); + *pDataSz = sizeof(uint16_t); + return 0; + case 4: + oversized_priv_read_attempted = 1; + return -101; + default: + ck_abort_msg("Unexpected NV read call %d", nvread_calls); + return -102; + } +} + +#include "../../src/tpm.c" + +static void setup(void) +{ + current_mode = MOCK_OVERSIZE_PUB; + nvread_calls = 0; + oversized_pub_read_attempted = 0; + oversized_priv_read_attempted = 0; +} + +START_TEST(test_wolfBoot_read_blob_rejects_oversized_public_area) +{ + WOLFTPM2_KEYBLOB blob; + int rc; + + memset(&blob, 0, sizeof(blob)); + current_mode = MOCK_OVERSIZE_PUB; + + rc = wolfBoot_read_blob(0x01400300, &blob, NULL, 0); + + ck_assert_int_eq(rc, BUFFER_E); + ck_assert_int_eq(nvread_calls, 1); + ck_assert_int_eq(oversized_pub_read_attempted, 0); +} +END_TEST + +START_TEST(test_wolfBoot_read_blob_rejects_oversized_private_area) +{ + WOLFTPM2_KEYBLOB blob; + int rc; + + memset(&blob, 0, sizeof(blob)); + current_mode = MOCK_OVERSIZE_PRIV; + blob.pub.size = 4; + + rc = wolfBoot_read_blob(0x01400300, &blob, NULL, 0); + + ck_assert_int_eq(rc, BUFFER_E); + ck_assert_int_eq(nvread_calls, 3); + ck_assert_int_eq(oversized_priv_read_attempted, 0); +} +END_TEST + +static Suite *tpm_blob_suite(void) +{ + Suite *s; + TCase *tc; + + s = suite_create("TPM Blob"); + tc = tcase_create("wolfBoot_read_blob"); + tcase_add_checked_fixture(tc, setup, NULL); + tcase_add_test(tc, test_wolfBoot_read_blob_rejects_oversized_public_area); + tcase_add_test(tc, test_wolfBoot_read_blob_rejects_oversized_private_area); + suite_add_tcase(s, tc); + return s; +} + +int main(void) +{ + Suite *s; + SRunner *sr; + int failed; + + s = tpm_blob_suite(); + sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + failed = srunner_ntests_failed(sr); + srunner_free(sr); + return failed == 0 ? 0 : 1; +} From 0365eb7671ecb609ea0d37f483a79dc465197507 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 15:13:57 +0100 Subject: [PATCH 03/17] Check fw_size when WOLFBOOT_FIXED_PARTITIONS is off F/373 --- src/image.c | 8 ++++++++ tools/unit-tests/Makefile | 6 ++++++ tools/unit-tests/unit-image.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/src/image.c b/src/image.c index 827bd33f78..5893efb28b 100644 --- a/src/image.c +++ b/src/image.c @@ -1317,6 +1317,14 @@ int wolfBoot_open_image_address(struct wolfBoot_image *img, uint8_t *image) } img->trailer = img->hdr + WOLFBOOT_PARTITION_SIZE; #else +#ifdef WOLFBOOT_RAMBOOT_MAX_SIZE + if (img->fw_size > WOLFBOOT_RAMBOOT_MAX_SIZE) { + wolfBoot_printf("Image size %d > max %d\n", + (unsigned int)img->fw_size, + (unsigned int)WOLFBOOT_RAMBOOT_MAX_SIZE); + return -1; + } +#endif if (img->hdr == NULL) { img->hdr = image; } diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index a7fed7203e..a2072fc5c0 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -48,6 +48,7 @@ TESTS:=unit-parser unit-extflash unit-string unit-spi-flash unit-aes128 \ unit-enc-nvm-flagshome unit-delta unit-update-flash \ 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 \ + unit-image-nopart \ unit-tpm-blob all: $(TESTS) @@ -160,6 +161,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-nopart: ../../include/target.h unit-image.c unit-common.c $(WOLFCRYPT_SRC) + gcc -o $@ unit-image.c unit-common.c $(WOLFCRYPT_SRC) \ + $(CFLAGS) $(WOLFCRYPT_CFLAGS) -DWOLFBOOT_NO_PARTITIONS -DMOCK_PARTITIONS \ + -DWOLFBOOT_RAMBOOT_MAX_SIZE=0x1000 $(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) \ diff --git a/tools/unit-tests/unit-image.c b/tools/unit-tests/unit-image.c index becc17ce54..f12cc7efb5 100644 --- a/tools/unit-tests/unit-image.c +++ b/tools/unit-tests/unit-image.c @@ -655,6 +655,7 @@ START_TEST(test_verify_authenticity_bad_siglen) END_TEST #endif +#ifdef WOLFBOOT_FIXED_PARTITIONS START_TEST(test_verify_integrity) { struct wolfBoot_image test_img; @@ -682,7 +683,9 @@ START_TEST(test_verify_integrity) ck_assert_int_eq(ret, 0); } END_TEST +#endif +#ifdef WOLFBOOT_FIXED_PARTITIONS START_TEST(test_open_image) { struct wolfBoot_image img; @@ -747,6 +750,24 @@ START_TEST(test_open_image) ck_assert_int_eq(ret, -1); } END_TEST +#else +START_TEST(test_open_image_address_without_partitions_rejects_oversized_fw_size) +{ + struct wolfBoot_image img; + uint8_t image[IMAGE_HEADER_SIZE] = {0}; + int ret; + + memset(&img, 0, sizeof(img)); + ((uint32_t *)image)[0] = WOLFBOOT_MAGIC; + ((uint32_t *)image)[1] = WOLFBOOT_RAMBOOT_MAX_SIZE + 1; + + ret = wolfBoot_open_image_address(&img, image); + + ck_assert_int_eq(ret, -1); + ck_assert_uint_eq(img.hdr_ok, 0); +} +END_TEST +#endif Suite *wolfboot_suite(void) @@ -794,14 +815,21 @@ Suite *wolfboot_suite(void) tcase_add_test(tcase_headers, test_headers); suite_add_tcase(s, tcase_headers); +#ifdef WOLFBOOT_FIXED_PARTITIONS TCase* tcase_verify_integrity = tcase_create("verify_integrity"); tcase_set_timeout(tcase_verify_integrity, 20); tcase_add_test(tcase_verify_integrity, test_verify_integrity); suite_add_tcase(s, tcase_verify_integrity); +#endif TCase* tcase_open_image = tcase_create("open_image"); tcase_set_timeout(tcase_open_image, 20); +#ifdef WOLFBOOT_FIXED_PARTITIONS tcase_add_test(tcase_open_image, test_open_image); +#else + tcase_add_test(tcase_open_image, + test_open_image_address_without_partitions_rejects_oversized_fw_size); +#endif suite_add_tcase(s, tcase_open_image); #endif return s; From 3e07cbef900f1842df024b1fe9e96bc8d44e2bb5 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 17:12:26 +0100 Subject: [PATCH 04/17] wolfBoot_unseal_auth(): secretSz is now in/out param The caller is now required to pass the available space in buffer. The actual size of the unsealed secret is returned. F/442 --- docs/TPM.md | 4 + src/tpm.c | 16 ++- src/update_flash.c | 4 +- src/x86/ahci.c | 3 +- tools/unit-tests/unit-tpm-blob.c | 237 ++++++++++++++++++++++++++++++- 5 files changed, 257 insertions(+), 7 deletions(-) diff --git a/docs/TPM.md b/docs/TPM.md index 6a58185da3..d936a74013 100644 --- a/docs/TPM.md +++ b/docs/TPM.md @@ -47,6 +47,10 @@ int wolfBoot_unseal_auth(const uint8_t* pubkey_hint, const uint8_t* policy, uint int index, uint8_t* secret, int* secret_sz, const byte* auth, int authSz); ``` +For `wolfBoot_unseal_auth()`, `*secret_sz` is an in/out parameter: set it to the +capacity of `secret` before the call, and on success it is updated to the +number of bytes unsealed. + By default this index will be based on an NV Index at `(0x01400300 + index)`. The default NV base can be overridden with `WOLFBOOT_TPM_SEAL_NV_BASE`. diff --git a/src/tpm.c b/src/tpm.c index d629cc8a3e..e69c5a4d65 100644 --- a/src/tpm.c +++ b/src/tpm.c @@ -935,6 +935,7 @@ int wolfBoot_unseal_blob(const uint8_t* pubkey_hint, const uint8_t* auth, int authSz) { int rc, i; + int secret_capacity; WOLFTPM2_SESSION policy_session; uint32_t key_type; TPM_ALG_ID pcrAlg = WOLFBOOT_TPM_PCR_ALG; @@ -959,8 +960,13 @@ int wolfBoot_unseal_blob(const uint8_t* pubkey_hint, return -1; } + secret_capacity = *secret_sz; *secret_sz = 0; /* init */ + if (secret_capacity < 0) { + return BAD_FUNC_ARG; + } + /* extract pcrMask and populate PCR selection array */ memcpy(&pcrMask, policy, sizeof(pcrMask)); memset(pcrArray, 0, sizeof(pcrArray)); @@ -1079,8 +1085,14 @@ int wolfBoot_unseal_blob(const uint8_t* pubkey_hint, rc = TPM2_Unseal(&unsealIn, &unsealOut); } if (rc == 0) { - *secret_sz = unsealOut.outData.size; - memcpy(secret, unsealOut.outData.buffer, *secret_sz); + if (unsealOut.outData.size > WOLFBOOT_MAX_SEAL_SZ || + (int)unsealOut.outData.size > secret_capacity) { + rc = BUFFER_E; + } + else { + *secret_sz = unsealOut.outData.size; + memcpy(secret, unsealOut.outData.buffer, *secret_sz); + } } wolfTPM2_UnloadHandle(&wolftpm_dev, &seal_blob->handle); diff --git a/src/update_flash.c b/src/update_flash.c index 29a4d1fbdd..b8b12c7da4 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -1206,7 +1206,7 @@ int wolfBoot_unlock_disk(void) int ret; struct wolfBoot_image img; uint8_t secret[WOLFBOOT_MAX_SEAL_SZ]; - int secretSz; + int secretSz = sizeof(secret); uint8_t* policy = NULL, *pubkey_hint = NULL; uint16_t policySz = 0; int nvIndex = 0; /* where the sealed blob is stored in NV */ @@ -1249,7 +1249,7 @@ int wolfBoot_unlock_disk(void) } if (ret == 0) { uint8_t secretCheck[WOLFBOOT_MAX_SEAL_SZ]; - int secretCheckSz = 0; + int secretCheckSz = sizeof(secretCheck); /* unseal again to make sure it works */ memset(secretCheck, 0, sizeof(secretCheck)); diff --git a/src/x86/ahci.c b/src/x86/ahci.c index 39152e8cf9..f00c7029de 100644 --- a/src/x86/ahci.c +++ b/src/x86/ahci.c @@ -272,7 +272,7 @@ static int sata_create_and_seal_unlock_secret(const uint8_t *pubkey_hint, int *secret_size) { uint8_t secret_check[WOLFBOOT_MAX_SEAL_SZ]; - int secret_check_sz; + int secret_check_sz = sizeof(secret_check); int ret; if (*secret_size < ATA_UNLOCK_DISK_KEY_SZ) @@ -797,4 +797,3 @@ void sata_disable(uint32_t base) } #endif /* AHCI_H_ */ - diff --git a/tools/unit-tests/unit-tpm-blob.c b/tools/unit-tests/unit-tpm-blob.c index b96a12ebb6..1786206be1 100644 --- a/tools/unit-tests/unit-tpm-blob.c +++ b/tools/unit-tests/unit-tpm-blob.c @@ -24,13 +24,17 @@ enum mock_mode { MOCK_OVERSIZE_PUB, - MOCK_OVERSIZE_PRIV + MOCK_OVERSIZE_PRIV, + MOCK_UNSEAL_OVERSIZE }; static enum mock_mode current_mode; static int nvread_calls; static int oversized_pub_read_attempted; static int oversized_priv_read_attempted; +static uint8_t test_hdr[64]; +static uint8_t test_modulus[256]; +static uint8_t test_exponent_der[] = { 0xAA, 0x01, 0x00, 0x01, 0x7B }; int wolfBoot_printf(const char* fmt, ...) { @@ -47,6 +51,205 @@ int wolfTPM2_SetAuthHandle(WOLFTPM2_DEV* dev, int index, return 0; } +int wolfTPM2_SetAuthSession(WOLFTPM2_DEV* dev, int index, + WOLFTPM2_SESSION* tpmSession, TPMA_SESSION sessionAttributes) +{ + (void)dev; + (void)index; + (void)tpmSession; + (void)sessionAttributes; + return 0; +} + +int wolfTPM2_UnsetAuthSession(WOLFTPM2_DEV* dev, int index, + WOLFTPM2_SESSION* tpmSession) +{ + (void)dev; + (void)index; + (void)tpmSession; + return 0; +} + +int wolfTPM2_SetAuthHandleName(WOLFTPM2_DEV* dev, int index, + const WOLFTPM2_HANDLE* handle) +{ + (void)dev; + (void)index; + (void)handle; + return 0; +} + +int wolfTPM2_StartSession(WOLFTPM2_DEV* dev, WOLFTPM2_SESSION* session, + WOLFTPM2_KEY* tpmKey, WOLFTPM2_HANDLE* bind, TPM_SE sesType, + int encDecAlg) +{ + (void)dev; + (void)tpmKey; + (void)bind; + (void)sesType; + (void)encDecAlg; + session->handle.hndl = 1; + return 0; +} + +int wolfTPM2_LoadKey(WOLFTPM2_DEV* dev, WOLFTPM2_KEYBLOB* keyBlob, + WOLFTPM2_HANDLE* parent) +{ + (void)dev; + (void)parent; + keyBlob->handle.hndl = 2; + return 0; +} + +int wolfTPM2_UnloadHandle(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* handle) +{ + (void)dev; + (void)handle; + return 0; +} + +int wolfTPM2_LoadEccPublicKey(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* pubKey, + int curveID, const byte* qx, word32 qxSz, const byte* qy, word32 qySz) +{ + (void)dev; + (void)pubKey; + (void)curveID; + (void)qx; + (void)qxSz; + (void)qy; + (void)qySz; + return 0; +} + +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)exponent; + (void)scheme; + (void)hashAlg; + return 0; +} + +int wolfTPM2_VerifyHashTicket(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, + const byte* sig, int sigSz, const byte* digest, int digestSz, + TPMI_ALG_SIG_SCHEME sigAlg, TPMI_ALG_HASH hashAlg, + TPMT_TK_VERIFIED* checkTicket) +{ + (void)dev; + (void)key; + (void)sig; + (void)sigSz; + (void)digest; + (void)digestSz; + (void)sigAlg; + (void)hashAlg; + (void)checkTicket; + return 0; +} + +int wolfTPM2_GetPolicyDigest(WOLFTPM2_DEV* dev, TPM_HANDLE sessionHandle, + byte* policyDigest, word32* policyDigestSz) +{ + (void)dev; + (void)sessionHandle; + memset(policyDigest, 0x11, *policyDigestSz); + return 0; +} + +int wolfTPM2_PolicyPCR(WOLFTPM2_DEV* dev, TPM_HANDLE sessionHandle, + TPM_ALG_ID pcrAlg, byte* pcrArray, word32 pcrArraySz) +{ + (void)dev; + (void)sessionHandle; + (void)pcrAlg; + (void)pcrArray; + (void)pcrArraySz; + return 0; +} + +int wolfTPM2_PolicyAuthorize(WOLFTPM2_DEV* dev, TPM_HANDLE sessionHandle, + const TPM2B_PUBLIC* pub, const TPMT_TK_VERIFIED* checkTicket, + const byte* pcrDigest, word32 pcrDigestSz, + const byte* policyRef, word32 policyRefSz) +{ + (void)dev; + (void)sessionHandle; + (void)pub; + (void)checkTicket; + (void)pcrDigest; + (void)pcrDigestSz; + (void)policyRef; + (void)policyRefSz; + return 0; +} + +int wolfTPM2_PolicyRefMake(TPM_ALG_ID pcrAlg, byte* digest, word32* digestSz, + const byte* policyRef, word32 policyRefSz) +{ + (void)pcrAlg; + (void)digest; + (void)digestSz; + (void)policyRef; + (void)policyRefSz; + return 0; +} + +TPM_RC TPM2_Unseal(Unseal_In* in, Unseal_Out* out) +{ + (void)in; + + if (current_mode != MOCK_UNSEAL_OVERSIZE) { + ck_abort_msg("Unexpected TPM2_Unseal call in mode %d", current_mode); + } + + out->outData.size = 16; + memset(out->outData.buffer, 0x5A, out->outData.size); + return 0; +} + +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); +} + const char* TPM2_GetRCString(int rc) { (void)rc; @@ -109,6 +312,8 @@ static void setup(void) nvread_calls = 0; oversized_pub_read_attempted = 0; oversized_priv_read_attempted = 0; + memset(test_hdr, 0x22, sizeof(test_hdr)); + memset(test_modulus, 0x33, sizeof(test_modulus)); } START_TEST(test_wolfBoot_read_blob_rejects_oversized_public_area) @@ -127,6 +332,35 @@ START_TEST(test_wolfBoot_read_blob_rejects_oversized_public_area) } END_TEST +START_TEST(test_wolfBoot_unseal_blob_rejects_output_larger_than_capacity) +{ + struct { + uint8_t secret[8]; + uint8_t canary[8]; + } output; + WOLFTPM2_KEYBLOB blob; + uint8_t pubkey_hint[WOLFBOOT_SHA_DIGEST_SIZE] = {0}; + uint8_t policy[sizeof(uint32_t) + 4] = {0}; + int secret_sz; + int rc; + int i; + + memset(&blob, 0, sizeof(blob)); + memset(&output, 0xA5, sizeof(output)); + current_mode = MOCK_UNSEAL_OVERSIZE; + secret_sz = (int)sizeof(output.secret); + + rc = wolfBoot_unseal_blob(pubkey_hint, policy, sizeof(policy), &blob, + output.secret, &secret_sz, NULL, 0); + + ck_assert_int_eq(rc, BUFFER_E); + ck_assert_int_eq(secret_sz, 0); + for (i = 0; i < (int)sizeof(output.canary); i++) { + ck_assert_uint_eq(output.canary[i], 0xA5); + } +} +END_TEST + START_TEST(test_wolfBoot_read_blob_rejects_oversized_private_area) { WOLFTPM2_KEYBLOB blob; @@ -154,6 +388,7 @@ static Suite *tpm_blob_suite(void) tcase_add_checked_fixture(tc, setup, NULL); tcase_add_test(tc, test_wolfBoot_read_blob_rejects_oversized_public_area); tcase_add_test(tc, test_wolfBoot_read_blob_rejects_oversized_private_area); + tcase_add_test(tc, test_wolfBoot_unseal_blob_rejects_output_larger_than_capacity); suite_add_tcase(s, tc); return s; } From d3ff22e83a0bf93c10b066fe398ab2bbdc401766 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 17:21:05 +0100 Subject: [PATCH 05/17] wolfBoot_unseal_blob: ensure secret is zeroed from stack after use F/444 --- src/tpm.c | 1 + tools/unit-tests/unit-tpm-blob.c | 43 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/src/tpm.c b/src/tpm.c index e69c5a4d65..661cdda122 100644 --- a/src/tpm.c +++ b/src/tpm.c @@ -1094,6 +1094,7 @@ int wolfBoot_unseal_blob(const uint8_t* pubkey_hint, memcpy(secret, unsealOut.outData.buffer, *secret_sz); } } + TPM2_ForceZero(&unsealOut, sizeof(unsealOut)); wolfTPM2_UnloadHandle(&wolftpm_dev, &seal_blob->handle); wolfTPM2_UnloadHandle(&wolftpm_dev, &policy_session.handle); diff --git a/tools/unit-tests/unit-tpm-blob.c b/tools/unit-tests/unit-tpm-blob.c index 1786206be1..bbc7672fd5 100644 --- a/tools/unit-tests/unit-tpm-blob.c +++ b/tools/unit-tests/unit-tpm-blob.c @@ -25,6 +25,7 @@ enum mock_mode { MOCK_OVERSIZE_PUB, MOCK_OVERSIZE_PRIV, + MOCK_UNSEAL_OK, MOCK_UNSEAL_OVERSIZE }; @@ -32,6 +33,8 @@ static enum mock_mode current_mode; static int nvread_calls; static int oversized_pub_read_attempted; static int oversized_priv_read_attempted; +static int forcezero_calls; +static word32 last_forcezero_len; static uint8_t test_hdr[64]; static uint8_t test_modulus[256]; static uint8_t test_exponent_der[] = { 0xAA, 0x01, 0x00, 0x01, 0x7B }; @@ -217,6 +220,12 @@ TPM_RC TPM2_Unseal(Unseal_In* in, Unseal_Out* out) { (void)in; + if (current_mode == MOCK_UNSEAL_OK) { + out->outData.size = 4; + memset(out->outData.buffer, 0x5A, out->outData.size); + return 0; + } + if (current_mode != MOCK_UNSEAL_OVERSIZE) { ck_abort_msg("Unexpected TPM2_Unseal call in mode %d", current_mode); } @@ -226,6 +235,13 @@ TPM_RC TPM2_Unseal(Unseal_In* in, Unseal_Out* out) return 0; } +void TPM2_ForceZero(void* mem, word32 len) +{ + forcezero_calls++; + last_forcezero_len = len; + memset(mem, 0, len); +} + int keyslot_id_by_sha(const uint8_t* pubkey_hint) { (void)pubkey_hint; @@ -312,6 +328,8 @@ static void setup(void) nvread_calls = 0; oversized_pub_read_attempted = 0; oversized_priv_read_attempted = 0; + forcezero_calls = 0; + last_forcezero_len = 0; memset(test_hdr, 0x22, sizeof(test_hdr)); memset(test_modulus, 0x33, sizeof(test_modulus)); } @@ -332,6 +350,30 @@ START_TEST(test_wolfBoot_read_blob_rejects_oversized_public_area) } END_TEST +START_TEST(test_wolfBoot_unseal_blob_zeroes_unseal_output) +{ + uint8_t secret[WOLFBOOT_MAX_SEAL_SZ]; + WOLFTPM2_KEYBLOB blob; + uint8_t pubkey_hint[WOLFBOOT_SHA_DIGEST_SIZE] = {0}; + uint8_t policy[sizeof(uint32_t) + 4] = {0}; + int secret_sz; + int rc; + + memset(&blob, 0, sizeof(blob)); + memset(secret, 0, sizeof(secret)); + current_mode = MOCK_UNSEAL_OK; + secret_sz = (int)sizeof(secret); + + rc = wolfBoot_unseal_blob(pubkey_hint, policy, sizeof(policy), &blob, + secret, &secret_sz, NULL, 0); + + ck_assert_int_eq(rc, 0); + ck_assert_int_eq(secret_sz, 4); + ck_assert_int_eq(forcezero_calls, 1); + ck_assert_uint_eq(last_forcezero_len, sizeof(Unseal_Out)); +} +END_TEST + START_TEST(test_wolfBoot_unseal_blob_rejects_output_larger_than_capacity) { struct { @@ -388,6 +430,7 @@ static Suite *tpm_blob_suite(void) tcase_add_checked_fixture(tc, setup, NULL); tcase_add_test(tc, test_wolfBoot_read_blob_rejects_oversized_public_area); tcase_add_test(tc, test_wolfBoot_read_blob_rejects_oversized_private_area); + tcase_add_test(tc, test_wolfBoot_unseal_blob_zeroes_unseal_output); tcase_add_test(tc, test_wolfBoot_unseal_blob_rejects_output_larger_than_capacity); suite_add_tcase(s, tc); return s; From 1c6ee12ea24938f54e402098322ec4d9c33b36ea Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 17:32:36 +0100 Subject: [PATCH 06/17] Fix regression in WOLFBOOT_RAMBOOT_MAX_SIZE --- Makefile | 2 +- include/target.h.in | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 39f5965cd0..321322a9fe 100644 --- a/Makefile +++ b/Makefile @@ -612,7 +612,7 @@ include/target.h: $(TARGET_H_TEMPLATE) FORCE sed -e "s/@WOLFBOOT_DTS_UPDATE_ADDRESS@/$(WOLFBOOT_DTS_UPDATE_ADDRESS)/g" | \ sed -e "s/@WOLFBOOT_LOAD_ADDRESS@/$(WOLFBOOT_LOAD_ADDRESS)/g" | \ sed -e "s/@WOLFBOOT_LOAD_DTS_ADDRESS@/$(WOLFBOOT_LOAD_DTS_ADDRESS)/g" | \ - sed -e "s/@WOLFBOOT_RAMBOOT_MAX_SIZE@/$(WOLFBOOT_RAMBOOT_MAX_SIZE)/g" | \ + sed -e "s|@WOLFBOOT_RAMBOOT_MAX_SIZE_DEFINE@|$(if $(strip $(WOLFBOOT_RAMBOOT_MAX_SIZE)),#define WOLFBOOT_RAMBOOT_MAX_SIZE $(WOLFBOOT_RAMBOOT_MAX_SIZE),/* WOLFBOOT_RAMBOOT_MAX_SIZE undefined */)|g" | \ sed -e "s/@WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS@/$(WOLFBOOT_PARTITION_SELF_HEADER_ADDRESS)/g" \ > $@ diff --git a/include/target.h.in b/include/target.h.in index 00069b8cd5..c4a113eb75 100644 --- a/include/target.h.in +++ b/include/target.h.in @@ -118,7 +118,7 @@ #endif /* Optional RAM-boot image size cap for targets without partitions */ -#define WOLFBOOT_RAMBOOT_MAX_SIZE @WOLFBOOT_RAMBOOT_MAX_SIZE@ +@WOLFBOOT_RAMBOOT_MAX_SIZE_DEFINE@ #define WOLFBOOT_LOAD_DTS_ADDRESS @WOLFBOOT_LOAD_DTS_ADDRESS@ From 3153775431a4e8f5f4148165722547f65522b71f Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 17:40:15 +0100 Subject: [PATCH 07/17] Fix xmalloc bucket size issue with "SPMATH=1 WOLFBOOT_SMALL_STACK=1" + Re-enabled old faulty tests F/366 --- .github/workflows/test-library.yml | 6 ------ src/xmalloc.c | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/test-library.yml b/.github/workflows/test-library.yml index 8bf760dee9..f2257dedc2 100644 --- a/.github/workflows/test-library.yml +++ b/.github/workflows/test-library.yml @@ -33,11 +33,6 @@ jobs: asym: [ed25519, ecc256, ecc384, ecc521, rsa2048, rsa3072, rsa4096, ed448] hash: [sha256, sha384, sha3] - # See https://github.com/wolfSSL/wolfBoot/issues/614 regarding exclusions: - exclude: - - math: "SPMATH=1 WOLFBOOT_SMALL_STACK=1" - - math: "SPMATHALL=1 WOLFBOOT_SMALL_STACK=1" - steps: - uses: actions/checkout@v4 with: @@ -201,4 +196,3 @@ jobs: fi echo "Got expected non-zero exit and 'Failure' message." - diff --git a/src/xmalloc.c b/src/xmalloc.c index 8605aec788..140d73ae36 100644 --- a/src/xmalloc.c +++ b/src/xmalloc.c @@ -117,7 +117,7 @@ struct xmalloc_slot { static uint8_t mp_points_3[MP_POINT_SIZE]; #endif static uint8_t mp_points_0[MP_POINT_SIZE * 2]; - static uint8_t mp_points_1[MP_POINT_SIZE * 2]; + static uint8_t mp_points_1[MP_POINT_SIZE * 3]; static uint8_t mp_points_2[MP_POINT_SIZE * (16 + 1)]; static uint8_t mp_digits_buffer_0[MP_DIGITS_BUFFER_SIZE_0]; static uint8_t mp_digits_buffer_1[MP_DIGITS_BUFFER_SIZE_1]; From 919412955a10229d3a809da94007ef9adfe5259e Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 18:25:11 +0100 Subject: [PATCH 08/17] key_sha384: zero hash buffer to cover for early error F/370 --- src/image.c | 2 ++ tools/unit-tests/Makefile | 14 ++++++++- tools/unit-tests/unit-image.c | 54 ++++++++++++++++++++++++++--------- 3 files changed, 56 insertions(+), 14 deletions(-) diff --git a/src/image.c b/src/image.c index 5893efb28b..e7ff7f3798 100644 --- a/src/image.c +++ b/src/image.c @@ -1143,6 +1143,7 @@ static void key_sha384(uint8_t key_slot, uint8_t *hash) int pubkey_sz = keystore_get_size(key_slot); wc_Sha384 sha384_ctx; + memset(hash, 0, SHA384_DIGEST_SIZE); if (!pubkey || (pubkey_sz < 0)) return; @@ -1237,6 +1238,7 @@ static void key_sha3_384(uint8_t key_slot, uint8_t *hash) int pubkey_sz = keystore_get_size(key_slot); wc_Sha3 sha3_ctx; + memset(hash, 0, WC_SHA3_384_DIGEST_SIZE); if (!pubkey || (pubkey_sz < 0)) return; wc_InitSha3_384(&sha3_ctx, NULL, INVALID_DEVID); diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index a2072fc5c0..f9fd0a4df4 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -48,7 +48,7 @@ TESTS:=unit-parser unit-extflash unit-string unit-spi-flash unit-aes128 \ unit-enc-nvm-flagshome unit-delta unit-update-flash \ 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 \ - unit-image-nopart \ + unit-image-nopart unit-image-sha384 unit-image-sha3-384 \ unit-tpm-blob all: $(TESTS) @@ -166,6 +166,18 @@ unit-image-nopart: ../../include/target.h unit-image.c unit-common.c $(WOLFCRYPT $(CFLAGS) $(WOLFCRYPT_CFLAGS) -DWOLFBOOT_NO_PARTITIONS -DMOCK_PARTITIONS \ -DWOLFBOOT_RAMBOOT_MAX_SIZE=0x1000 $(LDFLAGS) +unit-image-sha384: ../../include/target.h unit-image.c unit-common.c + gcc -o $@ unit-image.c unit-common.c $(WOLFCRYPT_SRC) \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha512.c \ + $(CFLAGS) $(WOLFCRYPT_CFLAGS) -DUNIT_IMAGE_KEYHASH_ONLY \ + -DWOLFBOOT_HASH_SHA384 $(LDFLAGS) + +unit-image-sha3-384: ../../include/target.h unit-image.c unit-common.c + gcc -o $@ unit-image.c unit-common.c $(WOLFCRYPT_SRC) \ + $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha3.c \ + $(CFLAGS) $(WOLFCRYPT_CFLAGS) -DUNIT_IMAGE_KEYHASH_ONLY \ + -DWOLFBOOT_HASH_SHA3_384 $(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) \ diff --git a/tools/unit-tests/unit-image.c b/tools/unit-tests/unit-image.c index f12cc7efb5..b20611182c 100644 --- a/tools/unit-tests/unit-image.c +++ b/tools/unit-tests/unit-image.c @@ -24,7 +24,10 @@ /* Option to enable sign tool debugging */ /* Must also define DEBUG_WOLFSSL in user_settings.h */ +#if !defined(WOLFBOOT_HASH_SHA256) && !defined(WOLFBOOT_HASH_SHA384) && \ + !defined(WOLFBOOT_HASH_SHA3_384) #define WOLFBOOT_HASH_SHA256 +#endif #define EXT_FLASH #define PART_UPDATE_EXT #define NVM_FLASH_WRITEONCE @@ -86,7 +89,7 @@ uint8_t *wolfBoot_get_self_header(void) return NULL; } -#if defined(WOLFBOOT_SIGN_ECC256) +#if defined(WOLFBOOT_SIGN_ECC256) && defined(WOLFBOOT_HASH_SHA256) static const unsigned char pubkey_digest[SHA256_DIGEST_SIZE] = { 0x17, 0x20, 0xa5, 0x9b, 0xe0, 0x9b, 0x80, 0x0c, 0xaa, 0xc4, 0xf5, 0x3f, 0xae, 0xe5, 0x72, 0x4f, 0xf2, 0x1f, 0x33, 0x53, 0xd1, 0xd4, 0xcd, 0x8b, @@ -187,12 +190,12 @@ static void patch_pubkey_hint(uint8_t *img, uint32_t img_len) { uint8_t *ptr = NULL; uint16_t len; - uint8_t hash[SHA256_DIGEST_SIZE]; + uint8_t hash[WOLFBOOT_SHA_DIGEST_SIZE]; (void)img_len; len = _find_header(img + IMAGE_HEADER_OFFSET, HDR_PUBKEY, &ptr); ck_assert_int_eq(len, WOLFBOOT_SHA_DIGEST_SIZE); - key_sha256(0, hash); + key_hash(0, hash); memcpy(ptr, hash, WOLFBOOT_SHA_DIGEST_SIZE); } @@ -388,15 +391,31 @@ END_TEST START_TEST(test_keyslot_id_by_sha_scans_all_slots) { int id; + uint8_t digest[WOLFBOOT_SHA_DIGEST_SIZE]; + key_hash(0, digest); unit_keystore_reset_counters(); - id = keyslot_id_by_sha(pubkey_digest); + id = keyslot_id_by_sha(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 + +START_TEST(test_key_hash_zeroes_output_on_invalid_slot) +{ + uint8_t hash[WOLFBOOT_SHA_DIGEST_SIZE]; + size_t i; + + memset(hash, 0xA5, sizeof(hash)); + key_hash(0xFF, hash); + + for (i = 0; i < sizeof(hash); i++) { + ck_assert_uint_eq(hash[i], 0); + } +} +END_TEST #endif #if defined(WOLFBOOT_SIGN_RSA2048) || defined(WOLFBOOT_SIGN_RSA3072) || \ @@ -455,7 +474,7 @@ END_TEST START_TEST(test_sha_ops) { - uint8_t hash[SHA256_DIGEST_SIZE]; + uint8_t hash[WOLFBOOT_SHA_DIGEST_SIZE]; static uint8_t FlashImg[32 * 1024]; uint8_t *retp = NULL; struct wolfBoot_image test_img; @@ -499,15 +518,15 @@ START_TEST(test_sha_ops) ck_assert_ptr_eq(retp, ext_hash_block); ck_assert_uint_eq(sz, WOLFBOOT_SHA_BLOCK_SIZE); - /* Test image_sha256 */ + /* Test image hash */ /* NULL img */ - ck_assert_int_lt(image_sha256(NULL, hash), 0); + ck_assert_int_lt(image_hash(NULL, hash), 0); /* Too short, internal partition field */ test_img.part = PART_BOOT; test_img.fw_size = 0x1000; - ck_assert_int_lt(image_sha256(&test_img, hash), 0); + ck_assert_int_lt(image_hash(&test_img, hash), 0); /* Ext partition with a valid SHA */ find_header_mocked = 0; @@ -518,14 +537,14 @@ START_TEST(test_sha_ops) test_img.part = PART_UPDATE; test_img.fw_base = 0; test_img.fw_size = test_img_len; - ck_assert_int_eq(image_sha256(&test_img, hash), 0); + ck_assert_int_eq(image_hash(&test_img, hash), 0); - /* key_sha256 */ - key_sha256(0, hash); -#if defined(WOLFBOOT_SIGN_ECC256) + /* key hash */ + key_hash(0, hash); +#if defined(WOLFBOOT_SIGN_ECC256) && defined(WOLFBOOT_HASH_SHA256) ck_assert_mem_eq(hash, pubkey_digest, SHA256_DIGEST_SIZE); #else - /* For non-ECC256 configurations we do not have a fixed expected digest. */ + /* Only the SHA-256 ECC256 fixture has a fixed expected digest here. */ (void)hash; #endif } @@ -775,11 +794,20 @@ Suite *wolfboot_suite(void) /* Suite initialization */ Suite *s = suite_create("wolfBoot"); +#ifdef UNIT_IMAGE_KEYHASH_ONLY + TCase* tcase_key_hash = tcase_create("key_hash"); + tcase_set_timeout(tcase_key_hash, 20); + tcase_add_test(tcase_key_hash, test_key_hash_zeroes_output_on_invalid_slot); + suite_add_tcase(s, tcase_key_hash); + return s; +#endif + #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); + tcase_add_test(tcase_verify_signature, test_key_hash_zeroes_output_on_invalid_slot); suite_add_tcase(s, tcase_verify_signature); #endif From 45eaf6f8b8f30128557d067fe70055115daa1dd3 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 18:30:13 +0100 Subject: [PATCH 09/17] Propagate hal_flash_write() error when writing partition magic F/437 --- src/libwolfboot.c | 2 ++ tools/unit-tests/unit-mock-flash.c | 5 ++++ tools/unit-tests/unit-nvm.c | 37 ++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+) diff --git a/src/libwolfboot.c b/src/libwolfboot.c index 5e8c2ebbeb..ff0f11bdc1 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -374,6 +374,8 @@ static int RAMFUNCTION partition_magic_write(uint8_t part, uintptr_t addr) XMEMCPY(NVM_CACHE, (void*)addr_read, NVM_CACHE_SIZE); XMEMCPY(NVM_CACHE + off, &wolfboot_magic_trail, sizeof(uint32_t)); ret = hal_flash_write(addr_write, NVM_CACHE, WOLFBOOT_SECTOR_SIZE); + if (ret != 0) + return ret; nvm_cached_sector = !nvm_cached_sector; ret = hal_flash_erase(addr_read, WOLFBOOT_SECTOR_SIZE); return ret; diff --git a/tools/unit-tests/unit-mock-flash.c b/tools/unit-tests/unit-mock-flash.c index f0aeae90cd..2d5482e6ba 100644 --- a/tools/unit-tests/unit-mock-flash.c +++ b/tools/unit-tests/unit-mock-flash.c @@ -31,6 +31,7 @@ static int erased_swap = 0; static int erased_nvm_bank0 = 0; static int erased_nvm_bank1 = 0; static int erased_vault = 0; +static int hal_flash_write_fail = 0; const char *argv0; #include @@ -45,6 +46,10 @@ int hal_flash_write(haladdr_t address, const uint8_t *data, int len) int i; uint8_t *a = (uint8_t *)(uintptr_t)address; ck_assert_msg(!locked, "Attempting to write to a locked FLASH"); + if (hal_flash_write_fail) { + hal_flash_write_fail = 0; + return -1; + } if ((address >= WOLFBOOT_PARTITION_SWAP_ADDRESS) && (address < WOLFBOOT_PARTITION_UPDATE_ADDRESS + WOLFBOOT_SECTOR_SIZE)) { for (i = 0; i < len; i++) { diff --git a/tools/unit-tests/unit-nvm.c b/tools/unit-tests/unit-nvm.c index 9d6a3cc3f7..a5b245c3a0 100644 --- a/tools/unit-tests/unit-nvm.c +++ b/tools/unit-tests/unit-nvm.c @@ -285,6 +285,41 @@ START_TEST (test_nvm_select_fresh_sector) } END_TEST +START_TEST(test_partition_magic_write_stops_on_flash_write_error) +{ + int ret; + uint32_t *magic; + + ret = mmap_file("/tmp/wolfboot-unit-file-error.bin", (void *)MOCK_ADDRESS, + WOLFBOOT_PARTITION_SIZE, NULL); + ck_assert(ret >= 0); + ret = mmap_file("/tmp/wolfboot-unit-swap-error.bin", (void *)MOCK_ADDRESS_SWAP, + WOLFBOOT_SECTOR_SIZE, NULL); + ck_assert(ret >= 0); + + hal_flash_unlock(); + wolfBoot_erase_partition(PART_UPDATE); + + magic = get_partition_magic(PART_UPDATE); + *magic = wolfboot_magic_trail; + + erased_update = 0; + erased_nvm_bank0 = 0; + erased_nvm_bank1 = 0; + hal_flash_write_fail = 1; + + ret = partition_magic_write(PART_UPDATE, + PART_UPDATE_ENDFLAGS - sizeof(uint32_t)); + ck_assert_int_eq(ret, -1); + ck_assert_int_eq(erased_update, 0); + ck_assert_int_eq(erased_nvm_bank0, 0); + ck_assert_int_eq(erased_nvm_bank1, 0); + ck_assert_uint_eq(*magic, wolfboot_magic_trail); + + hal_flash_lock(); +} +END_TEST + Suite *wolfboot_suite(void) { @@ -294,6 +329,8 @@ Suite *wolfboot_suite(void) /* Test cases */ TCase *nvm_select_fresh_sector = tcase_create("NVM select fresh sector"); tcase_add_test(nvm_select_fresh_sector, test_nvm_select_fresh_sector); + tcase_add_test(nvm_select_fresh_sector, + test_partition_magic_write_stops_on_flash_write_error); suite_add_tcase(s, nvm_select_fresh_sector); return s; From ff20310c4b030b9f058d8d82c3dfdcce69fea014 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 18:34:18 +0100 Subject: [PATCH 10/17] strings.c: enforce null-termination on strncpy, optimize strcat F/439 F/440 --- src/string.c | 7 ++++++- tools/unit-tests/unit-string.c | 12 ++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/string.c b/src/string.c index 0925a46172..576f382583 100644 --- a/src/string.c +++ b/src/string.c @@ -98,8 +98,9 @@ char *strcat(char *dest, const char *src) { size_t i = 0; size_t j = strlen(dest); + size_t src_len = strlen(src); - for (i = 0; i < strlen(src); i++) { + for (i = 0; i < src_len; i++) { dest[j++] = src[i]; } dest[j] = '\0'; @@ -186,6 +187,10 @@ char *strncpy(char *dst, const char *src, size_t n) break; } + while (++i < n) { + dst[i] = '\0'; + } + return dst; } diff --git a/tools/unit-tests/unit-string.c b/tools/unit-tests/unit-string.c index 1920eb748f..604a6eccfe 100644 --- a/tools/unit-tests/unit-string.c +++ b/tools/unit-tests/unit-string.c @@ -237,6 +237,7 @@ START_TEST(test_strcpy_strncpy_strcat_strncat) char buf[8]; char dest[8]; char short_dest[4]; + char padded[8]; strcpy(buf, "hi"); ck_assert_str_eq(buf, "hi"); @@ -245,6 +246,17 @@ START_TEST(test_strcpy_strncpy_strcat_strncat) strncpy(dest, "abc", 4); ck_assert_str_eq(dest, "abc"); + memset(padded, 'X', sizeof(padded)); + strncpy(padded, "abc", sizeof(padded)); + ck_assert_int_eq(padded[0], 'a'); + ck_assert_int_eq(padded[1], 'b'); + ck_assert_int_eq(padded[2], 'c'); + ck_assert_int_eq(padded[3], '\0'); + ck_assert_int_eq(padded[4], '\0'); + ck_assert_int_eq(padded[5], '\0'); + ck_assert_int_eq(padded[6], '\0'); + ck_assert_int_eq(padded[7], '\0'); + memset(short_dest, 'X', sizeof(short_dest)); strncpy(short_dest, "abcdef", 3); ck_assert_int_eq(short_dest[0], 'a'); From 927b8c952feab45487dabaf269487987b3b7ed41 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 18:47:50 +0100 Subject: [PATCH 11/17] Force-zero secrets in update_disk.c F/97 --- src/update_disk.c | 24 ++++++++++++++++++++++++ tools/unit-tests/Makefile | 5 ++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/update_disk.c b/src/update_disk.c index c10c000d27..294ab3d341 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -208,6 +208,12 @@ static int decrypt_header(const uint8_t *src, uint8_t *dst) return 0; } +static void disk_crypto_clear(void) +{ + ForceZero(disk_encrypt_key, sizeof(disk_encrypt_key)); + ForceZero(disk_encrypt_nonce, sizeof(disk_encrypt_nonce)); +} + #endif /* DISK_ENCRYPT */ extern int wolfBoot_get_dts_size(void *dts_addr); @@ -254,11 +260,13 @@ void RAMFUNCTION wolfBoot_start(void) #ifdef DISK_ENCRYPT /* Initialize encryption - this sets up the cipher with key from storage */ if (wolfBoot_initialize_encryption() != 0) { + disk_crypto_clear(); wolfBoot_printf("Error initializing encryption\r\n"); wolfBoot_panic(); } /* Retrieve encryption key and nonce for disk decryption */ if (wolfBoot_get_encrypt_key(disk_encrypt_key, disk_encrypt_nonce) != 0) { + disk_crypto_clear(); wolfBoot_printf("Error getting encryption key\r\n"); wolfBoot_panic(); } @@ -267,10 +275,16 @@ void RAMFUNCTION wolfBoot_start(void) ret = disk_init(BOOT_DISK); if (ret != 0) { +#ifdef DISK_ENCRYPT + disk_crypto_clear(); +#endif wolfBoot_panic(); } if (disk_open(BOOT_DISK) < 0) { +#ifdef DISK_ENCRYPT + disk_crypto_clear(); +#endif wolfBoot_printf("Error opening disk %d\r\n", BOOT_DISK); wolfBoot_panic(); } @@ -306,6 +320,9 @@ void RAMFUNCTION wolfBoot_start(void) } if ((pB_ver == 0) && (pA_ver == 0)) { +#ifdef DISK_ENCRYPT + disk_crypto_clear(); +#endif wolfBoot_printf("No valid OS image found in either partition %d or %d\r\n", BOOT_PART_A, BOOT_PART_B); wolfBoot_panic(); @@ -409,6 +426,7 @@ void RAMFUNCTION wolfBoot_start(void) wolfBoot_printf("Decrypting image..."); BENCHMARK_START(); if ((IMAGE_HEADER_SIZE % ENCRYPT_BLOCK_SIZE) != 0) { + disk_crypto_clear(); wolfBoot_printf("Encrypted disk images require aligned header size\r\n"); wolfBoot_panic(); } @@ -456,6 +474,9 @@ void RAMFUNCTION wolfBoot_start(void) } while (failures < MAX_FAILURES); if (failures) { +#ifdef DISK_ENCRYPT + disk_crypto_clear(); +#endif wolfBoot_printf("Unable to find a valid partition!\r\n"); wolfBoot_panic(); } @@ -512,6 +533,9 @@ void RAMFUNCTION wolfBoot_start(void) #ifdef WOLFBOOT_HOOK_BOOT wolfBoot_hook_boot(&os_image); +#endif +#ifdef DISK_ENCRYPT + disk_crypto_clear(); #endif do_boot((uint32_t*)load_address #ifdef MMU diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index f9fd0a4df4..310c0f9103 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -47,7 +47,7 @@ TESTS:=unit-parser unit-extflash unit-string unit-spi-flash unit-aes128 \ 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-psa_store unit-disk \ - unit-multiboot unit-boot-x86-fsp unit-qspi-flash unit-tpm-rsa-exp \ + unit-update-disk unit-multiboot unit-boot-x86-fsp unit-qspi-flash unit-tpm-rsa-exp \ unit-image-nopart unit-image-sha384 unit-image-sha3-384 \ unit-tpm-blob @@ -214,6 +214,9 @@ unit-update-flash-enc: ../../include/target.h unit-update-flash.c unit-update-ram: ../../include/target.h unit-update-ram.c gcc -o $@ unit-update-ram.c ../../src/image.c $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS) +unit-update-disk: ../../include/target.h unit-update-disk.c + gcc -o $@ unit-update-disk.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) From 73a10e56bb2c5ade2d6625d8c64e63d2d33b0d09 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 18:55:01 +0100 Subject: [PATCH 12/17] Added bound check to get_decrypted_blob_version F/374 --- src/update_disk.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/update_disk.c b/src/update_disk.c index 294ab3d341..e7756bafc3 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -134,6 +134,9 @@ static uint32_t get_decrypted_blob_version(uint8_t *hdr) continue; } + if (p + 4 + tlv_len > max_p) + break; + if (tlv_type == HDR_VERSION && tlv_len == 4) { uint32_t ver = *((uint32_t*)(p + 4)); return ver; From 1b5448128caca5df8a2d7218e77744d7325941d5 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 18:57:54 +0100 Subject: [PATCH 13/17] TPM NV blob functions: Limit authsz to buffer capacity F/375 --- src/tpm.c | 6 ++ tools/unit-tests/unit-tpm-blob.c | 133 +++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+) diff --git a/src/tpm.c b/src/tpm.c index 661cdda122..9b90fc3b3b 100644 --- a/src/tpm.c +++ b/src/tpm.c @@ -605,6 +605,8 @@ int wolfBoot_store_blob(TPMI_RH_NV_AUTH authHandle, uint32_t nvIndex, if (authSz > 0) { if (auth == NULL) return BAD_FUNC_ARG; + if (authSz > sizeof(nv.handle.auth.buffer)) + return BAD_FUNC_ARG; nv.handle.auth.size = authSz; memcpy(nv.handle.auth.buffer, auth, authSz); } @@ -685,6 +687,8 @@ int wolfBoot_read_blob(uint32_t nvIndex, WOLFTPM2_KEYBLOB* blob, if (authSz > 0) { if (auth == NULL) return BAD_FUNC_ARG; + if (authSz > sizeof(nv.handle.auth.buffer)) + return BAD_FUNC_ARG; nv.handle.auth.size = authSz; memcpy(nv.handle.auth.buffer, auth, authSz); } @@ -754,6 +758,8 @@ int wolfBoot_delete_blob(TPMI_RH_NV_AUTH authHandle, uint32_t nvIndex, if (authSz > 0) { if (auth == NULL) return BAD_FUNC_ARG; + if (authSz > sizeof(nv.handle.auth.buffer)) + return BAD_FUNC_ARG; nv.handle.auth.size = authSz; memcpy(nv.handle.auth.buffer, auth, authSz); } diff --git a/tools/unit-tests/unit-tpm-blob.c b/tools/unit-tests/unit-tpm-blob.c index bbc7672fd5..f80e03e2f3 100644 --- a/tools/unit-tests/unit-tpm-blob.c +++ b/tools/unit-tests/unit-tpm-blob.c @@ -31,6 +31,10 @@ enum mock_mode { static enum mock_mode current_mode; static int nvread_calls; +static int unexpected_nvcreate_calls; +static int unexpected_nvwrite_calls; +static int unexpected_nvopen_calls; +static int unexpected_nvdelete_calls; static int oversized_pub_read_attempted; static int oversized_priv_read_attempted; static int forcezero_calls; @@ -54,6 +58,13 @@ int wolfTPM2_SetAuthHandle(WOLFTPM2_DEV* dev, int index, return 0; } +int wolfTPM2_UnsetAuth(WOLFTPM2_DEV* dev, int index) +{ + (void)dev; + (void)index; + return 0; +} + int wolfTPM2_SetAuthSession(WOLFTPM2_DEV* dev, int index, WOLFTPM2_SESSION* tpmSession, TPMA_SESSION sessionAttributes) { @@ -280,6 +291,71 @@ int TPM2_ParsePublic(TPM2B_PUBLIC* pub, byte* buf, word32 size, int* sizeUsed) return 0; } +int TPM2_AppendPublic(byte* out, word32 outSz, int* pubAreaSize, + TPM2B_PUBLIC* pub) +{ + (void)pub; + ck_assert_uint_ge(outSz, 4); + memset(out, 0, 4); + *pubAreaSize = 4; + return 0; +} + +int wolfTPM2_NVCreateAuth(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* parent, + WOLFTPM2_NV* nv, word32 nvIndex, word32 nvAttributes, word32 maxSize, + const byte* auth, int authSz) +{ + (void)dev; + (void)parent; + (void)nv; + (void)nvIndex; + (void)nvAttributes; + (void)maxSize; + (void)auth; + (void)authSz; + unexpected_nvcreate_calls++; + ck_abort_msg("Unexpected wolfTPM2_NVCreateAuth call"); + return -1; +} + +int wolfTPM2_NVWriteAuth(WOLFTPM2_DEV* dev, WOLFTPM2_NV* nv, + word32 nvIndex, byte* dataBuf, word32 dataSz, word32 offset) +{ + (void)dev; + (void)nv; + (void)nvIndex; + (void)dataBuf; + (void)dataSz; + (void)offset; + unexpected_nvwrite_calls++; + ck_abort_msg("Unexpected wolfTPM2_NVWriteAuth call"); + return -1; +} + +int wolfTPM2_NVOpen(WOLFTPM2_DEV* dev, WOLFTPM2_NV* nv, + word32 nvIndex, const byte* auth, word32 authSz) +{ + (void)dev; + (void)nv; + (void)nvIndex; + (void)auth; + (void)authSz; + unexpected_nvopen_calls++; + ck_abort_msg("Unexpected wolfTPM2_NVOpen call"); + return -1; +} + +int wolfTPM2_NVDeleteAuth(WOLFTPM2_DEV* dev, WOLFTPM2_HANDLE* parent, + word32 nvIndex) +{ + (void)dev; + (void)parent; + (void)nvIndex; + unexpected_nvdelete_calls++; + ck_abort_msg("Unexpected wolfTPM2_NVDeleteAuth call"); + return -1; +} + int wolfTPM2_NVReadAuth(WOLFTPM2_DEV* dev, WOLFTPM2_NV* nv, word32 nvIndex, byte* dataBuf, word32* pDataSz, word32 offset) { @@ -326,6 +402,10 @@ static void setup(void) { current_mode = MOCK_OVERSIZE_PUB; nvread_calls = 0; + unexpected_nvcreate_calls = 0; + unexpected_nvwrite_calls = 0; + unexpected_nvopen_calls = 0; + unexpected_nvdelete_calls = 0; oversized_pub_read_attempted = 0; oversized_priv_read_attempted = 0; forcezero_calls = 0; @@ -350,6 +430,56 @@ START_TEST(test_wolfBoot_read_blob_rejects_oversized_public_area) } END_TEST +START_TEST(test_wolfBoot_store_blob_rejects_oversized_auth) +{ + WOLFTPM2_KEYBLOB blob; + uint8_t auth[sizeof(((WOLFTPM2_NV*)0)->handle.auth.buffer) + 1]; + int rc; + + memset(&blob, 0, sizeof(blob)); + memset(auth, 0x44, sizeof(auth)); + + rc = wolfBoot_store_blob(TPM_RH_PLATFORM, 0x01400300, 0, &blob, + auth, (uint32_t)sizeof(auth)); + + ck_assert_int_eq(rc, BAD_FUNC_ARG); + ck_assert_int_eq(unexpected_nvcreate_calls, 0); + ck_assert_int_eq(unexpected_nvwrite_calls, 0); +} +END_TEST + +START_TEST(test_wolfBoot_read_blob_rejects_oversized_auth) +{ + WOLFTPM2_KEYBLOB blob; + uint8_t auth[sizeof(((WOLFTPM2_NV*)0)->handle.auth.buffer) + 1]; + int rc; + + memset(&blob, 0, sizeof(blob)); + memset(auth, 0x55, sizeof(auth)); + + rc = wolfBoot_read_blob(0x01400300, &blob, auth, (uint32_t)sizeof(auth)); + + ck_assert_int_eq(rc, BAD_FUNC_ARG); + ck_assert_int_eq(nvread_calls, 0); +} +END_TEST + +START_TEST(test_wolfBoot_delete_blob_rejects_oversized_auth) +{ + uint8_t auth[sizeof(((WOLFTPM2_NV*)0)->handle.auth.buffer) + 1]; + int rc; + + memset(auth, 0x66, sizeof(auth)); + + rc = wolfBoot_delete_blob(TPM_RH_PLATFORM, 0x01400300, auth, + (uint32_t)sizeof(auth)); + + ck_assert_int_eq(rc, BAD_FUNC_ARG); + ck_assert_int_eq(unexpected_nvopen_calls, 0); + ck_assert_int_eq(unexpected_nvdelete_calls, 0); +} +END_TEST + START_TEST(test_wolfBoot_unseal_blob_zeroes_unseal_output) { uint8_t secret[WOLFBOOT_MAX_SEAL_SZ]; @@ -428,6 +558,9 @@ static Suite *tpm_blob_suite(void) s = suite_create("TPM Blob"); tc = tcase_create("wolfBoot_read_blob"); tcase_add_checked_fixture(tc, setup, NULL); + tcase_add_test(tc, test_wolfBoot_store_blob_rejects_oversized_auth); + tcase_add_test(tc, test_wolfBoot_read_blob_rejects_oversized_auth); + tcase_add_test(tc, test_wolfBoot_delete_blob_rejects_oversized_auth); tcase_add_test(tc, test_wolfBoot_read_blob_rejects_oversized_public_area); tcase_add_test(tc, test_wolfBoot_read_blob_rejects_oversized_private_area); tcase_add_test(tc, test_wolfBoot_unseal_blob_zeroes_unseal_output); From 83091e52998937de5f5012370ce3a96af9d7401c Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 19:07:04 +0100 Subject: [PATCH 14/17] TPM: RoT comparison made Constant time F/448 --- src/tpm.c | 4 +- tools/unit-tests/unit-tpm-rsa-exp.c | 102 ++++++++++++++++++++++++++++ 2 files changed, 105 insertions(+), 1 deletion(-) diff --git a/src/tpm.c b/src/tpm.c index 9b90fc3b3b..623ac2d17a 100644 --- a/src/tpm.c +++ b/src/tpm.c @@ -32,6 +32,7 @@ #include "spi_drv.h" #include "tpm.h" #include "wolftpm/tpm2_tis.h" /* for TIS header size and wait state */ +#include WOLFTPM2_DEV wolftpm_dev; #if defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL) @@ -1515,7 +1516,8 @@ int wolfBoot_check_rot(int key_slot, uint8_t* pubkey_hint) if (rc == 0) { /* verify the hint (hash) matches */ if (digestSz == WOLFBOOT_SHA_DIGEST_SIZE && - memcmp(digest, pubkey_hint, WOLFBOOT_SHA_DIGEST_SIZE) == 0) { + ConstantCompare(digest, pubkey_hint, + WOLFBOOT_SHA_DIGEST_SIZE) == 0) { wolfBoot_printf("TPM Root of Trust valid (id %d)\n", key_slot); } else { diff --git a/tools/unit-tests/unit-tpm-rsa-exp.c b/tools/unit-tests/unit-tpm-rsa-exp.c index 18b21d700e..a66c2d358e 100644 --- a/tools/unit-tests/unit-tpm-rsa-exp.c +++ b/tools/unit-tests/unit-tpm-rsa-exp.c @@ -5,6 +5,7 @@ #include #include +#include #include #ifndef SPI_CS_TPM @@ -16,6 +17,7 @@ #ifndef WOLFBOOT_TPM_HASH_ALG #define WOLFBOOT_TPM_HASH_ALG TPM_ALG_SHA256 #endif +#define WOLFBOOT_TPM_KEYSTORE #include "wolfboot/wolfboot.h" #include "keystore.h" @@ -24,7 +26,9 @@ static uint8_t test_hdr[16]; static uint8_t test_modulus[256]; static uint8_t test_exponent_der[] = { 0xAA, 0x01, 0x00, 0x01, 0x7B }; +static uint8_t test_nv_digest[WOLFBOOT_SHA_DIGEST_SIZE]; static uint32_t captured_exponent; +static int forbidden_memcmp_calls; int keyslot_id_by_sha(const uint8_t* pubkey_hint) { @@ -79,13 +83,96 @@ int wolfTPM2_LoadRsaPublicKey_ex(WOLFTPM2_DEV* dev, WOLFTPM2_KEY* key, return 0; } +int wolfTPM2_SetAuthHandle(WOLFTPM2_DEV* dev, int index, + const WOLFTPM2_HANDLE* handle) +{ + (void)dev; + (void)index; + (void)handle; + return 0; +} + +int wolfTPM2_SetAuthSession(WOLFTPM2_DEV* dev, int index, + WOLFTPM2_SESSION* tpmSession, TPMA_SESSION sessionAttributes) +{ + (void)dev; + (void)index; + (void)tpmSession; + (void)sessionAttributes; + return 0; +} + +int wolfTPM2_UnsetAuth(WOLFTPM2_DEV* dev, int index) +{ + (void)dev; + (void)index; + return 0; +} + +int wolfTPM2_UnsetAuthSession(WOLFTPM2_DEV* dev, int index, + WOLFTPM2_SESSION* tpmSession) +{ + (void)dev; + (void)index; + (void)tpmSession; + return 0; +} + +int wolfTPM2_NVReadAuth(WOLFTPM2_DEV* dev, WOLFTPM2_NV* nv, + word32 nvIndex, byte* dataBuf, word32* pDataSz, word32 offset) +{ + (void)dev; + (void)nv; + (void)nvIndex; + (void)offset; + ck_assert_uint_eq(*pDataSz, WOLFBOOT_SHA_DIGEST_SIZE); + memcpy(dataBuf, test_nv_digest, WOLFBOOT_SHA_DIGEST_SIZE); + *pDataSz = WOLFBOOT_SHA_DIGEST_SIZE; + return 0; +} + +const char* wolfTPM2_GetRCString(int rc) +{ + (void)rc; + return "mock"; +} + +int ConstantCompare(const byte* a, const byte* b, int length) +{ + int diff = 0; + int i; + + for (i = 0; i < length; i++) { + diff |= a[i] ^ b[i]; + } + return diff; +} + +static int forbidden_memcmp(const void *a, const void *b, size_t n) +{ + const uint8_t *lhs = (const uint8_t *)a; + const uint8_t *rhs = (const uint8_t *)b; + size_t i; + + forbidden_memcmp_calls++; + for (i = 0; i < n; i++) { + if (lhs[i] != rhs[i]) + return (int)lhs[i] - (int)rhs[i]; + } + return 0; +} + +#define memcmp forbidden_memcmp #include "../../src/tpm.c" +#undef memcmp static void setup(void) { memset(test_hdr, 0x42, sizeof(test_hdr)); memset(test_modulus, 0x5A, sizeof(test_modulus)); + memset(test_nv_digest, 0x7C, sizeof(test_nv_digest)); captured_exponent = 0; + forbidden_memcmp_calls = 0; } START_TEST(test_wolfBoot_load_pubkey_decodes_der_exponent_bytes) @@ -105,6 +192,20 @@ START_TEST(test_wolfBoot_load_pubkey_decodes_der_exponent_bytes) } END_TEST +START_TEST(test_wolfBoot_check_rot_avoids_memcmp_on_digest_compare) +{ + uint8_t hint[WOLFBOOT_SHA_DIGEST_SIZE]; + int rc; + + memcpy(hint, test_nv_digest, sizeof(hint)); + + rc = wolfBoot_check_rot(0, hint); + + ck_assert_int_eq(rc, 0); + ck_assert_int_eq(forbidden_memcmp_calls, 0); +} +END_TEST + static Suite *tpm_suite(void) { Suite *s; @@ -114,6 +215,7 @@ static Suite *tpm_suite(void) 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); + tcase_add_test(tc, test_wolfBoot_check_rot_avoids_memcmp_on_digest_compare); suite_add_tcase(s, tc); return s; } From 8b78a314bff87c70fc175e888fe3a6ec32707fdc Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Wed, 11 Mar 2026 19:14:55 +0100 Subject: [PATCH 15/17] Added missing unit test file --- tools/unit-tests/unit-update-disk.c | 280 ++++++++++++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 tools/unit-tests/unit-update-disk.c diff --git a/tools/unit-tests/unit-update-disk.c b/tools/unit-tests/unit-update-disk.c new file mode 100644 index 0000000000..377813ac7a --- /dev/null +++ b/tools/unit-tests/unit-update-disk.c @@ -0,0 +1,280 @@ +#define WOLFBOOT_UPDATE_DISK +#define WOLFBOOT_SKIP_BOOT_VERIFY +#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 + +#include +#include +#include +#include + +#include "target.h" +#include "wolfboot/wolfboot.h" +#include "image.h" +#include "loader.h" +#include + +#define TEST_PAYLOAD_SIZE 64 + +static uint8_t load_buffer[TEST_PAYLOAD_SIZE]; +#define WOLFBOOT_LOAD_ADDRESS ((uintptr_t)load_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 int mock_disk_init_ret; +static int mock_disk_close_called; +static int mock_do_boot_called; +static const uint32_t *mock_boot_address; + +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(part_a_image, 0, sizeof(part_a_image)); + memset(part_b_image, 0, sizeof(part_b_image)); + build_image(part_a_image, 1, 0xA1); + build_image(part_b_image, 2, 0xB2); + mock_disk_init_ret = 0; + mock_disk_close_called = 0; + mock_do_boot_called = 0; + mock_boot_address = NULL; + 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 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 mock_disk_init_ret; +} + +int disk_open(int drv) +{ + (void)drv; + return 0; +} + +void disk_close(int drv) +{ + (void)drv; + mock_disk_close_called++; +} + +int disk_part_read(int drv, int part, uint64_t off, uint64_t sz, uint8_t *buf) +{ + uint8_t *image; + + (void)drv; + image = (part == BOOT_PART_B) ? part_b_image : part_a_image; + if (off + sz > IMAGE_HEADER_SIZE + TEST_PAYLOAD_SIZE) + 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 *)image; + + if (magic != WOLFBOOT_MAGIC) + return -1; + memset(img, 0, sizeof(*img)); + img->hdr = image; + img->fw_size = *(uint32_t *)(image + sizeof(uint32_t)); + img->fw_base = image + IMAGE_HEADER_SIZE; + img->hdr_ok = 1; + return 0; +} + +int wolfBoot_verify_integrity(struct wolfBoot_image* img) +{ + (void)img; + return 0; +} + +int wolfBoot_verify_authenticity(struct wolfBoot_image* img) +{ + (void)img; + return 0; +} + +int wolfBoot_get_dts_size(void *dts_addr) +{ + (void)dts_addr; + return -1; +} + +void hal_prepare_boot(void) +{ +} + +void do_boot(const uint32_t *address) +{ + mock_do_boot_called++; + mock_boot_address = address; +} + +#include "update_disk.c" + +START_TEST(test_update_disk_zeroizes_key_material_on_panic) +{ + size_t i; + + reset_mocks(); + mock_disk_init_ret = -1; + + wolfBoot_start(); + + ck_assert_int_eq(wolfBoot_panicked, 1); + for (i = 0; i < ENCRYPT_KEY_SIZE; i++) { + ck_assert_uint_eq(disk_encrypt_key[i], 0); + } + for (i = 0; i < ENCRYPT_NONCE_SIZE; i++) { + ck_assert_uint_eq(disk_encrypt_nonce[i], 0); + } +} +END_TEST + +START_TEST(test_update_disk_zeroizes_key_material_before_boot) +{ + size_t i; + + reset_mocks(); + + wolfBoot_start(); + + ck_assert_int_eq(wolfBoot_panicked, 0); + ck_assert_int_eq(mock_disk_close_called, 1); + ck_assert_int_eq(mock_do_boot_called, 1); + ck_assert_ptr_eq(mock_boot_address, (const uint32_t *)WOLFBOOT_LOAD_ADDRESS); + for (i = 0; i < ENCRYPT_KEY_SIZE; i++) { + ck_assert_uint_eq(disk_encrypt_key[i], 0); + } + for (i = 0; i < ENCRYPT_NONCE_SIZE; i++) { + ck_assert_uint_eq(disk_encrypt_nonce[i], 0); + } +} +END_TEST + +START_TEST(test_get_decrypted_blob_version_rejects_truncated_version_tlv) +{ + uint8_t hdr[IMAGE_HEADER_SIZE + 2]; + uint8_t *p; + + memset(hdr, 0, sizeof(hdr)); + set_u32_le(hdr, WOLFBOOT_MAGIC); + + p = hdr + IMAGE_HEADER_SIZE - 6; + { + uint8_t *q; + + for (q = hdr + IMAGE_HEADER_OFFSET; q < p; q += 2) { + q[0] = 0xFF; + if (q + 1 < p) + q[1] = 0x00; + } + } + set_u16_le(p, HDR_VERSION); + set_u16_le(p + sizeof(uint16_t), 4); + p[4] = 0x11; + p[5] = 0x22; + p[6] = 0x33; + p[7] = 0x44; + + ck_assert_uint_eq(get_decrypted_blob_version(hdr), 0); +} +END_TEST + +Suite *wolfboot_suite(void) +{ + Suite *s = suite_create("wolfBoot"); + TCase *tc = tcase_create("update-disk"); + + tcase_add_test(tc, test_update_disk_zeroizes_key_material_on_panic); + tcase_add_test(tc, test_update_disk_zeroizes_key_material_before_boot); + tcase_add_test(tc, test_get_decrypted_blob_version_rejects_truncated_version_tlv); + 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; +} From d1ab9ebb6a8a9aec38a7cc02e9a3e74b6835e7df Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Thu, 12 Mar 2026 00:25:32 +0100 Subject: [PATCH 16/17] Fix build errors --- src/tpm.c | 16 ++++++++++++++-- stage1/Makefile | 3 ++- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/tpm.c b/src/tpm.c index 623ac2d17a..6a6edc72b5 100644 --- a/src/tpm.c +++ b/src/tpm.c @@ -32,7 +32,6 @@ #include "spi_drv.h" #include "tpm.h" #include "wolftpm/tpm2_tis.h" /* for TIS header size and wait state */ -#include WOLFTPM2_DEV wolftpm_dev; #if defined(WOLFBOOT_TPM_KEYSTORE) || defined(WOLFBOOT_TPM_SEAL) @@ -45,6 +44,19 @@ WOLFTPM2_KEY wolftpm_srk; #endif #if defined(WOLFBOOT_TPM_SEAL) || defined(WOLFBOOT_TPM_KEYSTORE) +static int wolfBoot_constant_compare(const uint8_t* a, const uint8_t* b, + uint32_t len) +{ + uint32_t i; + uint8_t diff = 0; + + for (i = 0; i < len; i++) { + diff |= a[i] ^ b[i]; + } + + return diff; +} + void wolfBoot_print_hexstr(const unsigned char* bin, unsigned long sz, unsigned long maxLine) { @@ -1516,7 +1528,7 @@ int wolfBoot_check_rot(int key_slot, uint8_t* pubkey_hint) if (rc == 0) { /* verify the hint (hash) matches */ if (digestSz == WOLFBOOT_SHA_DIGEST_SIZE && - ConstantCompare(digest, pubkey_hint, + wolfBoot_constant_compare(digest, pubkey_hint, WOLFBOOT_SHA_DIGEST_SIZE) == 0) { wolfBoot_printf("TPM Root of Trust valid (id %d)\n", key_slot); } diff --git a/stage1/Makefile b/stage1/Makefile index 2267e2cf78..b0a296fa61 100644 --- a/stage1/Makefile +++ b/stage1/Makefile @@ -178,7 +178,8 @@ $(LSCRIPT): $(LSCRIPT_IN) FORCE sed -e "s/@WOLFBOOT_DTS_BOOT_ADDRESS@/$(WOLFBOOT_DTS_BOOT_ADDRESS)/g" | \ sed -e "s/@WOLFBOOT_DTS_UPDATE_ADDRESS@/$(WOLFBOOT_DTS_UPDATE_ADDRESS)/g" | \ sed -e "s/@WOLFBOOT_LOAD_ADDRESS@/$(WOLFBOOT_LOAD_ADDRESS)/g" | \ - sed -e "s/@WOLFBOOT_LOAD_DTS_ADDRESS@/$(WOLFBOOT_LOAD_DTS_ADDRESS)/g" \ + sed -e "s/@WOLFBOOT_LOAD_DTS_ADDRESS@/$(WOLFBOOT_LOAD_DTS_ADDRESS)/g" | \ + sed -e "s|@WOLFBOOT_RAMBOOT_MAX_SIZE_DEFINE@|$(if $(strip $(WOLFBOOT_RAMBOOT_MAX_SIZE)),#define WOLFBOOT_RAMBOOT_MAX_SIZE $(WOLFBOOT_RAMBOOT_MAX_SIZE),/* WOLFBOOT_RAMBOOT_MAX_SIZE undefined */)|g" \ > $@ $(BUILD_DIR)/%.o: %.c From ab52d527084fdde5405130ca944328f8fd2c8f05 Mon Sep 17 00:00:00 2001 From: Daniele Lacamera Date: Thu, 12 Mar 2026 00:31:24 +0100 Subject: [PATCH 17/17] Fix broken test: FLAGS_HOME requires boot partition mapping --- tools/unit-tests/unit-nvm.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tools/unit-tests/unit-nvm.c b/tools/unit-tests/unit-nvm.c index a5b245c3a0..12d69a3c60 100644 --- a/tools/unit-tests/unit-nvm.c +++ b/tools/unit-tests/unit-nvm.c @@ -293,6 +293,11 @@ START_TEST(test_partition_magic_write_stops_on_flash_write_error) ret = mmap_file("/tmp/wolfboot-unit-file-error.bin", (void *)MOCK_ADDRESS, WOLFBOOT_PARTITION_SIZE, NULL); ck_assert(ret >= 0); +#ifdef FLAGS_HOME + ret = mmap_file("/tmp/wolfboot-unit-int-file-error.bin", + (void *)MOCK_ADDRESS_BOOT, WOLFBOOT_PARTITION_SIZE, NULL); + ck_assert(ret >= 0); +#endif ret = mmap_file("/tmp/wolfboot-unit-swap-error.bin", (void *)MOCK_ADDRESS_SWAP, WOLFBOOT_SECTOR_SIZE, NULL); ck_assert(ret >= 0); @@ -312,6 +317,11 @@ START_TEST(test_partition_magic_write_stops_on_flash_write_error) PART_UPDATE_ENDFLAGS - sizeof(uint32_t)); ck_assert_int_eq(ret, -1); ck_assert_int_eq(erased_update, 0); +#ifdef FLAGS_HOME + ck_assert_int_eq(erased_boot, 1); +#else + ck_assert_int_eq(erased_boot, 0); +#endif ck_assert_int_eq(erased_nvm_bank0, 0); ck_assert_int_eq(erased_nvm_bank1, 0); ck_assert_uint_eq(*magic, wolfboot_magic_trail);