diff --git a/.github/workflows/test-build-riscv.yml b/.github/workflows/test-build-riscv.yml index 5b13443c94..67c85fa4c7 100644 --- a/.github/workflows/test-build-riscv.yml +++ b/.github/workflows/test-build-riscv.yml @@ -18,6 +18,8 @@ jobs: build: runs-on: ubuntu-24.04 + container: + image: ghcr.io/wolfssl/wolfboot-ci-riscv:v1.0 timeout-minutes: 30 steps: - uses: actions/checkout@v4 @@ -31,69 +33,16 @@ jobs: path: freedom-e-sdk submodules: recursive - - name: Workaround for sources.list + - name: Trust workspace run: | - # Replace sources + git config --global --add safe.directory "$GITHUB_WORKSPACE" - set -euxo pipefail - - # Peek (what repos are active now) - apt-cache policy - grep -RInE '^(deb|Types|URIs)' /etc/apt || true - - # Enable nullglob so *.list/*.sources that don't exist don't break sed - shopt -s nullglob - - echo "Replace sources.list (legacy)" - sudo sed -i \ - -e "s|https\?://azure\.archive\.ubuntu\.com/ubuntu/?|http://mirror.arizona.edu/ubuntu/|g" \ - /etc/apt/sources.list || true - - echo "Replace sources.list.d/*.list (legacy)" - for f in /etc/apt/sources.list.d/*.list; do - sudo sed -i \ - -e "s|https\?://azure\.archive\.ubuntu\.com/ubuntu/?|http://mirror.arizona.edu/ubuntu/|g" \ - "$f" - done - - echo "Replace sources.list.d/*.sources (deb822)" - for f in /etc/apt/sources.list.d/*.sources; do - sudo sed -i \ - -e "s|https\?://azure\.archive\.ubuntu\.com/ubuntu/?|http://mirror.arizona.edu/ubuntu/|g" \ - -e "s|https\?://azure\.archive\.ubuntu\.com|http://mirror.arizona.edu|g" \ - "$f" - done - - echo "Fix /etc/apt/apt-mirrors.txt (used by URIs: mirror+file:...)" - if grep -qE '^[[:space:]]*https?://azure\.archive\.ubuntu\.com/ubuntu/?' /etc/apt/apt-mirrors.txt; then - # Replace azure with our mirror (idempotent) - sudo sed -i 's|https\?://azure\.archive\.ubuntu\.com/ubuntu/|http://mirror.arizona.edu/ubuntu/|g' /etc/apt/apt-mirrors.txt - fi - - # Peek (verify changes) - grep -RIn "azure.archive.ubuntu.com" /etc/apt || true - grep -RInE '^(deb|Types|URIs)' /etc/apt || true - echo "--- apt-mirrors.txt ---" - cat /etc/apt/apt-mirrors.txt || true - - - name: Update repository - run: sudo apt-get update -o Acquire::Retries=3 + - name: Add preinstalled RISC-V toolchain to PATH + run: | + echo "/opt/xpack-riscv-none-elf-gcc-15.2.0-1/bin" >> "$GITHUB_PATH" - # ============================================================ - # xPack RISC-V GCC — single toolchain with full multilib - # supporting both rv32 and rv64 targets (including rv32imac/ilp32 - # and rv64imac/lp64 which riscv-collab nightly builds lack). - # https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack - # ============================================================ - - name: Download and install xPack RISC-V toolchain + - name: Check RISC-V toolchain run: | - XPACK_VER="15.2.0-1" - XPACK_FILE="xpack-riscv-none-elf-gcc-${XPACK_VER}-linux-x64.tar.gz" - wget -q "https://github.com/xpack-dev-tools/riscv-none-elf-gcc-xpack/releases/download/v${XPACK_VER}/${XPACK_FILE}" - echo "aaaa8060c914851a3e5ee1ba82cc3d6f80972f90638a05c6e823a37557a33758 ${XPACK_FILE}" | sha256sum -c - - tar -xf "${XPACK_FILE}" - echo "$GITHUB_WORKSPACE/xpack-riscv-none-elf-gcc-${XPACK_VER}/bin" >> $GITHUB_PATH - export PATH="$GITHUB_WORKSPACE/xpack-riscv-none-elf-gcc-${XPACK_VER}/bin:$PATH" riscv-none-elf-gcc --version riscv-none-elf-gcc -print-multi-lib | head -5 diff --git a/hal/library.c b/hal/library.c index b0ad0be8f7..465bed7877 100644 --- a/hal/library.c +++ b/hal/library.c @@ -191,6 +191,7 @@ int main(int argc, const char* argv[]) if (((void*)gImage) == NULL) { wolfBoot_printf("failed to malloc %zu bytes for image\n", sz); ret = -1; + goto close_img; } bread = fread((void*)gImage, 1, sz, img); @@ -198,6 +199,8 @@ int main(int argc, const char* argv[]) ret = -2; wolfBoot_printf("read %zu of %zu bytes from %s\n", bread, sz, argv[1]); } + +close_img: fclose(img); } else { wolfBoot_printf("usage: %s image_file.bin\n", argv[0]); diff --git a/include/gpt.h b/include/gpt.h index fdc2cf361c..5e74ce0709 100644 --- a/include/gpt.h +++ b/include/gpt.h @@ -99,6 +99,13 @@ struct gpt_part_info { uint16_t name[GPT_PART_NAME_SIZE]; }; +/** + * @brief CRC32 context for GPT header and partition-array validation. + */ +struct gpt_crc32_ctx { + uint32_t value; +}; + /** * @brief Check MBR for protective GPT partition entry. * @@ -139,6 +146,32 @@ int gpt_parse_header(const uint8_t *sector, struct guid_ptable *hdr); int gpt_parse_partition(const uint8_t *entry_data, uint32_t entry_size, struct gpt_part_info *part); +/** + * @brief Initialize a GPT CRC32 calculation. + * + * @param[out] ctx Pointer to CRC32 context. + */ +void gpt_crc32_init(struct gpt_crc32_ctx *ctx); + +/** + * @brief Accumulate bytes into a GPT CRC32 calculation. + * + * @param[in,out] ctx Pointer to CRC32 context. + * @param[in] data Pointer to input bytes. + * @param[in] len Number of bytes to process. + */ +void gpt_crc32_update(struct gpt_crc32_ctx *ctx, const uint8_t *data, + uint32_t len); + +/** + * @brief Finalize a GPT CRC32 calculation. + * + * @param[in] ctx Pointer to CRC32 context. + * + * @return Final CRC32 value. + */ +uint32_t gpt_crc32_final(const struct gpt_crc32_ctx *ctx); + /** * @brief Compare UTF-16 partition name with ASCII string. * @@ -152,4 +185,3 @@ int gpt_parse_partition(const uint8_t *entry_data, uint32_t entry_size, int gpt_part_name_eq(const uint16_t *utf16_name, const char *ascii_label); #endif /* GPT_H */ - diff --git a/src/arm_tee_psa_ipc.c b/src/arm_tee_psa_ipc.c index b02f98108d..3aeb0da9e7 100644 --- a/src/arm_tee_psa_ipc.c +++ b/src/arm_tee_psa_ipc.c @@ -28,6 +28,7 @@ #include #include +#include #include #include #include @@ -240,6 +241,7 @@ static struct wolfboot_ps_entry *wolfboot_ps_alloc(psa_storage_uid_t uid) { for (size_t i = 0; i < WOLFBOOT_PS_MAX_ENTRIES; i++) { if (!g_ps_entries[i].in_use) { + wc_ForceZero(g_ps_entries[i].data, sizeof(g_ps_entries[i].data)); g_ps_entries[i].in_use = 1; g_ps_entries[i].uid = uid; g_ps_entries[i].size = 0; @@ -844,6 +846,7 @@ int32_t arm_tee_psa_call(psa_handle_t handle, int32_t type, if (entry == NULL) { return PSA_ERROR_DOES_NOT_EXIST; } + wc_ForceZero(entry->data, sizeof(entry->data)); entry->in_use = 0; entry->uid = 0; entry->size = 0; diff --git a/src/delta.c b/src/delta.c index 2f9e1d0c69..236a6c09f5 100644 --- a/src/delta.c +++ b/src/delta.c @@ -370,6 +370,7 @@ int wb_diff(WB_DIFF_CTX *ctx, uint8_t *patch, uint32_t len) pb+= BLOCK_HDR_SIZE; ctx->off_b += BLOCK_HDR_SIZE; while ((pb < pb_limit) && + (match_len < UINT16_MAX) && (ctx->off_b < ctx->size_b) && (*pb == *(ctx->src_b + ctx->off_b))) { /* Extend match as long as the areas have the diff --git a/src/disk.c b/src/disk.c index 4f8eaf955d..782c002f77 100644 --- a/src/disk.c +++ b/src/disk.c @@ -118,7 +118,11 @@ int disk_open(int drv) uint32_t i; uint32_t n_parts = 0; uint32_t gpt_lba = 0; + uint32_t chunk = 0; + uint64_t array_addr = 0; + uint64_t bytes_left = 0; uint8_t sector[GPT_SECTOR_SIZE] XALIGNED(4); + struct gpt_crc32_ctx part_crc; if ((drv < 0) || (drv >= MAX_DISKS)) { wolfBoot_printf("Attempting to access invalid drive %d\r\n", drv); @@ -162,6 +166,34 @@ int disk_open(int drv) wolfBoot_printf("Valid GPT partition table\r\n"); wolfBoot_printf("Max number of partitions: %d\r\n", ptable.n_part); + array_addr = ptable.start_array * GPT_SECTOR_SIZE; + bytes_left = (uint64_t)ptable.n_part * ptable.array_sz; + + gpt_crc32_init(&part_crc); + while (bytes_left > 0) { + chunk = GPT_SECTOR_SIZE; + + if (bytes_left < chunk) { + chunk = (uint32_t)bytes_left; + } + + r = disk_read(drv, array_addr, chunk, sector); + if (r < 0) { + Drives[drv].is_open = 0; + return -1; + } + + gpt_crc32_update(&part_crc, sector, chunk); + array_addr += chunk; + bytes_left -= chunk; + } + + if (gpt_crc32_final(&part_crc) != ptable.part_crc) { + wolfBoot_printf("Invalid GPT partition entry array CRC\r\n"); + Drives[drv].is_open = 0; + return -1; + } + n_parts = ptable.n_part; if (n_parts > MAX_PARTITIONS) n_parts = MAX_PARTITIONS; diff --git a/src/gpt.c b/src/gpt.c index 931a47c56d..f5b079d03e 100644 --- a/src/gpt.c +++ b/src/gpt.c @@ -34,21 +34,39 @@ #include "gpt.h" -static uint32_t gpt_crc32(const uint8_t *data, uint32_t len) +void gpt_crc32_init(struct gpt_crc32_ctx *ctx) +{ + if (ctx != NULL) { + ctx->value = 0xFFFFFFFFU; + } +} + +void gpt_crc32_update(struct gpt_crc32_ctx *ctx, const uint8_t *data, + uint32_t len) { - uint32_t crc = 0xFFFFFFFFU; uint32_t i; uint32_t j; + if (ctx == NULL || data == NULL) { + return; + } + for (i = 0; i < len; i++) { - crc ^= data[i]; + ctx->value ^= data[i]; for (j = 0; j < 8; j++) { - uint32_t mask = -(crc & 1U); - crc = (crc >> 1) ^ (0xEDB88320U & mask); + uint32_t mask = -(ctx->value & 1U); + ctx->value = (ctx->value >> 1) ^ (0xEDB88320U & mask); } } +} + +uint32_t gpt_crc32_final(const struct gpt_crc32_ctx *ctx) +{ + if (ctx == NULL) { + return 0; + } - return ~crc; + return ~ctx->value; } /** @@ -116,6 +134,7 @@ int gpt_parse_header(const uint8_t *sector, struct guid_ptable *hdr) { const struct guid_ptable *src; struct guid_ptable tmp; + struct gpt_crc32_ctx crc; if (sector == NULL || hdr == NULL) { return -1; @@ -134,7 +153,9 @@ int gpt_parse_header(const uint8_t *sector, struct guid_ptable *hdr) memcpy(&tmp, src, sizeof(tmp)); tmp.hdr_crc32 = 0; - if (gpt_crc32((const uint8_t *)&tmp, src->hdr_size) != src->hdr_crc32) { + gpt_crc32_init(&crc); + gpt_crc32_update(&crc, (const uint8_t *)&tmp, src->hdr_size); + if (gpt_crc32_final(&crc) != src->hdr_crc32) { return -1; } diff --git a/src/libwolfboot.c b/src/libwolfboot.c index 79fa0eddcb..5b8d3a6d26 100644 --- a/src/libwolfboot.c +++ b/src/libwolfboot.c @@ -65,11 +65,12 @@ #ifdef EXT_ENCRYPTED static int encrypt_key_is_erased(const uint8_t *key, uint32_t len) { + const volatile uint8_t *vkey = key; uint8_t diff = 0; uint32_t i; for (i = 0; i < len; i++) - diff |= key[i] ^ FLASH_BYTE_ERASED; + diff |= vkey[i] ^ FLASH_BYTE_ERASED; return diff == 0; } @@ -85,13 +86,14 @@ static int fallback_iv_forced = 0; static int encrypt_key_is_valid(const uint8_t *key, uint32_t len) { + const volatile uint8_t *vkey = key; uint8_t has_one = 0; uint8_t has_zero = 0; uint32_t i; for (i = 0; i < len; i++) { - has_one |= key[i]; - has_zero |= (uint8_t)~key[i]; + has_one |= vkey[i]; + has_zero |= (uint8_t)~vkey[i]; } return (has_one != 0) && (has_zero != 0); diff --git a/src/multiboot.c b/src/multiboot.c index 4bf03adc78..b618c76cb7 100644 --- a/src/multiboot.c +++ b/src/multiboot.c @@ -351,8 +351,10 @@ static void mb2_dump_header(void* mbHeader) { MB2_DEBUG_PRINTF("Checksum: 0x%x\r\n", header->checksum); tags = (uint8_t*)header + sizeof(*header); - if (header->header_length < sizeof(struct mb2_header)) + if (header->header_length < sizeof(struct mb2_header)) { MB2_DEBUG_PRINTF("Invalid header length\r\n"); + return; + } mb2_dump_tags(tags, header->header_length - sizeof(*header)); } #endif /* DEBUG_MB2 */ diff --git a/src/qspi_flash.c b/src/qspi_flash.c index 602df830c1..6a446af263 100644 --- a/src/qspi_flash.c +++ b/src/qspi_flash.c @@ -431,38 +431,40 @@ int spi_flash_write(uint32_t address, const void *data, int len) pages = ((len + (FLASH_PAGE_SIZE-1)) / FLASH_PAGE_SIZE); for (page = 0; page < pages; page++) { ret = qspi_write_enable(); - if (ret == 0) { - xferSz = (uint32_t)remaining; - if (xferSz > FLASH_PAGE_SIZE) { - xferSz = FLASH_PAGE_SIZE; - } - - addr = address + (page * FLASH_PAGE_SIZE); - - /* ------ Write Flash (page at a time) ------ */ - ret = qspi_transfer(QSPI_MODE_WRITE, FLASH_WRITE_CMD, - addr, QSPI_ADDR_SZ, QSPI_DATA_MODE_SPI, /* Address */ - 0, 0, QSPI_DATA_MODE_NONE, /* Alternate Bytes */ - 0, /* Dummy */ - ptr, /* Destination Ptr */ - xferSz, QSPI_DATA_MODE /* Data */ - ); + if (ret != 0) { + break; + } + + xferSz = (uint32_t)remaining; + if (xferSz > FLASH_PAGE_SIZE) { + xferSz = FLASH_PAGE_SIZE; + } + + addr = address + (page * FLASH_PAGE_SIZE); + + /* ------ Write Flash (page at a time) ------ */ + ret = qspi_transfer(QSPI_MODE_WRITE, FLASH_WRITE_CMD, + addr, QSPI_ADDR_SZ, QSPI_DATA_MODE_SPI, /* Address */ + 0, 0, QSPI_DATA_MODE_NONE, /* Alternate Bytes */ + 0, /* Dummy */ + ptr, /* Destination Ptr */ + xferSz, QSPI_DATA_MODE /* Data */ + ); #ifdef DEBUG_QSPI - wolfBoot_printf("QSPI Flash Sector Write: " - "Ret %d, Cmd 0x%x, Len %d, %p -> 0x%x\n", - ret, FLASH_WRITE_CMD, xferSz, ptr, address); + wolfBoot_printf("QSPI Flash Sector Write: " + "Ret %d, Cmd 0x%x, Len %d, %p -> 0x%x\n", + ret, FLASH_WRITE_CMD, xferSz, ptr, address); #endif - if (ret != 0) - break; + if (ret != 0) + break; - ret = qspi_wait_ready(); /* Wait for not busy */ - if (ret != 0) { - break; - } - /* write disable is automatic */ - remaining -= (int)xferSz; - ptr += xferSz; + ret = qspi_wait_ready(); /* Wait for not busy */ + if (ret != 0) { + break; } + /* write disable is automatic */ + remaining -= (int)xferSz; + ptr += xferSz; } return ret; diff --git a/src/tpm.c b/src/tpm.c index 56420286f5..98ed7f601d 100644 --- a/src/tpm.c +++ b/src/tpm.c @@ -1255,7 +1255,8 @@ const char* CSME_NSE_API wolfBoot_tpm2_get_alg_name(TPM_ALG_ID alg, name[name_sz - 1] = '\0'; } else { - strcpy(name, "Unknown"); + strncpy(name, "Unknown", name_sz - 1); + name[name_sz - 1] = '\0'; } return (const char*)name; } @@ -1272,7 +1273,8 @@ const char* CSME_NSE_API wolfBoot_tpm2_get_rc_string(int rc, char* error, int er error[error_sz - 1] = '\0'; } else { - strcpy(error, "Unknown"); + strncpy(error, "Unknown", error_sz - 1); + error[error_sz - 1] = '\0'; } return (const char*)error; } diff --git a/src/uart_flash.c b/src/uart_flash.c index 676ea3e2c6..3623c9c9eb 100644 --- a/src/uart_flash.c +++ b/src/uart_flash.c @@ -118,7 +118,7 @@ int ext_flash_read(uintptr_t address, uint8_t *data, int len) } for (i = 0; i < len; i++) { if (uart_rx_timeout(&data[i]) != 0) - return 0; + return -1; uart_tx(CMD_ACK); } return i; diff --git a/src/update_disk.c b/src/update_disk.c index 4254b17118..2503cf9755 100644 --- a/src/update_disk.c +++ b/src/update_disk.c @@ -99,6 +99,19 @@ static uint8_t disk_encrypt_nonce[ENCRYPT_NONCE_SIZE]; /* Module-level storage for encryption key */ static uint8_t disk_encrypt_key[ENCRYPT_KEY_SIZE]; +static uint16_t get_hdr_u16(const uint8_t *p) +{ + return (uint16_t)((uint16_t)p[0] | ((uint16_t)p[1] << 8)); +} + +static uint32_t get_hdr_u32(const uint8_t *p) +{ + return ((uint32_t)p[0]) | + ((uint32_t)p[1] << 8) | + ((uint32_t)p[2] << 16) | + ((uint32_t)p[3] << 24); +} + /** * @brief Get the version from an already-decrypted header. * @@ -113,7 +126,6 @@ static uint8_t disk_encrypt_key[ENCRYPT_KEY_SIZE]; static uint32_t get_decrypted_blob_version(uint8_t *hdr) { uint32_t *magic = (uint32_t *)hdr; - uint16_t tlv_type, tlv_len; uint8_t *p = hdr + IMAGE_HEADER_OFFSET; uint8_t *max_p = hdr + IMAGE_HEADER_SIZE; @@ -124,26 +136,25 @@ static uint32_t get_decrypted_blob_version(uint8_t *hdr) while ((size_t)(max_p - p) >= 4U) { size_t remaining = (size_t)(max_p - p); size_t tlv_total; + uint16_t tlv_type, tlv_len; - tlv_type = *((uint16_t*)p); - tlv_len = *((uint16_t*)(p + 2)); - - if (tlv_type == 0 || tlv_type == 0xFFFF) - break; - - /* Skip padding bytes */ - if ((p[0] == 0xFF) || ((((uintptr_t)p) & 0x01) != 0)) { + /* Skip padding bytes and unaligned half-words before reading TLVs. */ + if ((p[0] == HDR_PADDING) || ((((uintptr_t)p) & 0x01U) != 0U)) { p++; continue; } + tlv_type = get_hdr_u16(p); + if (tlv_type == 0 || tlv_type == 0xFFFF) + break; + + tlv_len = get_hdr_u16(p + 2); tlv_total = 4U + (size_t)tlv_len; if (remaining < tlv_total) break; if (tlv_type == HDR_VERSION && tlv_len == 4) { - uint32_t ver = *((uint32_t*)(p + 4)); - return ver; + return get_hdr_u32(p + 4); } p += tlv_total; diff --git a/src/update_flash.c b/src/update_flash.c index edf32dd312..64ac306d4d 100644 --- a/src/update_flash.c +++ b/src/update_flash.c @@ -46,7 +46,7 @@ static int wolfBoot_local_constant_compare(const uint8_t* a, const uint8_t* b, uint32_t len) { uint32_t i; - uint8_t diff = 0; + volatile uint8_t diff = 0; for (i = 0; i < len; i++) { diff |= a[i] ^ b[i]; @@ -633,7 +633,8 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, #endif if (wolfBoot_get_delta_info(PART_UPDATE, inverse, &img_offset, &img_size, &delta_base_hash, &delta_base_hash_sz) < 0) { - return -1; + ret = -1; + goto out; } delta_img_size = wb_delta_im2n(*img_size); if (inverse) { @@ -653,7 +654,8 @@ static int wolfBoot_delta_update(struct wolfBoot_image *boot, wolfBoot_printf("Error: delta update: Base hash size mismatch" " (size: %x expected %x)\n", delta_base_hash_sz, WOLFBOOT_SHA_DIGEST_SIZE); - return -1; + ret = -1; + goto out; } } @@ -1592,6 +1594,9 @@ void RAMFUNCTION wolfBoot_start(void) #ifdef WOLFBOOT_HOOK_BOOT wolfBoot_hook_boot(&boot); +#endif +#ifndef WOLFBOOT_SKIP_BOOT_VERIFY + PART_SANITY_CHECK(&boot); #endif do_boot((void *)boot.fw_base); } diff --git a/src/x86/ahci.c b/src/x86/ahci.c index 1caa5b5a96..059a9c8a74 100644 --- a/src/x86/ahci.c +++ b/src/x86/ahci.c @@ -113,7 +113,7 @@ static int wolfBoot_local_constant_compare(const uint8_t* a, const uint8_t* b, uint32_t len) { uint32_t i; - uint8_t diff = 0; + volatile uint8_t diff = 0; for (i = 0; i < len; i++) { diff |= a[i] ^ b[i]; diff --git a/tools/keytools/keygen.c b/tools/keytools/keygen.c index cdf123245a..cf4adfb590 100644 --- a/tools/keytools/keygen.c +++ b/tools/keytools/keygen.c @@ -987,7 +987,7 @@ static void keygen_xmss(const char *priv_fname, uint32_t id_mask) exit(1); } - if (xmss_params != NULL) + if (xmss_params == NULL) xmss_params = WOLFBOOT_XMSS_PARAMS; ret = wc_XmssKey_SetParamStr(&key, xmss_params); diff --git a/tools/lms/lms_common.h b/tools/lms/lms_common.h index 4005963117..f9b12938bf 100644 --- a/tools/lms/lms_common.h +++ b/tools/lms/lms_common.h @@ -82,22 +82,32 @@ static int lms_write_key(const byte * priv, word32 privSz, void * context) if (n_read != n_write) { fprintf(stderr, "error: read %d, expected %d: %d\n", (int)n_read, (int)n_write, ferror(file)); - return WC_LMS_RC_WRITE_FAIL; + goto verify_fail; } n_cmp = XMEMCMP(buff, priv, n_write); if (n_cmp != 0) { fprintf(stderr, "error: write data was corrupted: %d\n", n_cmp); - return WC_LMS_RC_WRITE_FAIL; + goto verify_fail; } err = fclose(file); if (err) { fprintf(stderr, "error: fclose returned %d\n", err); + wc_ForceZero(buff, sizeof(buff)); return WC_LMS_RC_WRITE_FAIL; } + wc_ForceZero(buff, sizeof(buff)); return WC_LMS_RC_SAVED_TO_NV_MEMORY; + +verify_fail: + wc_ForceZero(buff, sizeof(buff)); + err = fclose(file); + if (err) { + fprintf(stderr, "error: fclose returned %d\n", err); + } + return WC_LMS_RC_WRITE_FAIL; } static int lms_read_key(byte * priv, word32 privSz, void * context) diff --git a/tools/test.mk b/tools/test.mk index c84c2f1d7a..cfa7e64345 100644 --- a/tools/test.mk +++ b/tools/test.mk @@ -1187,7 +1187,7 @@ test-all: clean test-size-all: make test-size SIGN=NONE LIMIT=5066 NO_ARM_ASM=1 make keysclean - make test-size SIGN=ED25519 LIMIT=11818 NO_ARM_ASM=1 + make test-size SIGN=ED25519 LIMIT=11820 NO_ARM_ASM=1 make keysclean make test-size SIGN=ECC256 LIMIT=18944 NO_ARM_ASM=1 make clean @@ -1211,25 +1211,25 @@ test-size-all: make clean make test-size SIGN=RSA3072 NO_ASM=1 LIMIT=12600 NO_ARM_ASM=1 make keysclean - make test-size SIGN=RSAPSS2048 LIMIT=13670 NO_ARM_ASM=1 + make test-size SIGN=RSAPSS2048 LIMIT=13676 NO_ARM_ASM=1 make clean - make test-size SIGN=RSAPSS2048 NO_ASM=1 LIMIT=14230 NO_ARM_ASM=1 + make test-size SIGN=RSAPSS2048 NO_ASM=1 LIMIT=14236 NO_ARM_ASM=1 make keysclean - make test-size SIGN=RSAPSS3072 LIMIT=13840 NO_ARM_ASM=1 + make test-size SIGN=RSAPSS3072 LIMIT=13844 NO_ARM_ASM=1 make clean - make test-size SIGN=RSAPSS3072 NO_ASM=1 LIMIT=14360 NO_ARM_ASM=1 + make test-size SIGN=RSAPSS3072 NO_ASM=1 LIMIT=14368 NO_ARM_ASM=1 make keysclean - make test-size SIGN=RSAPSS4096 LIMIT=14010 NO_ARM_ASM=1 + make test-size SIGN=RSAPSS4096 LIMIT=14016 NO_ARM_ASM=1 make clean - make test-size SIGN=RSAPSS4096 NO_ASM=1 LIMIT=14550 NO_ARM_ASM=1 + make test-size SIGN=RSAPSS4096 NO_ASM=1 LIMIT=14556 NO_ARM_ASM=1 make keysclean make test-size SIGN=LMS LMS_LEVELS=2 LMS_HEIGHT=5 LMS_WINTERNITZ=8 \ WOLFBOOT_SMALL_STACK=0 IMAGE_SIGNATURE_SIZE=2644 \ - IMAGE_HEADER_SIZE?=5288 LIMIT=7798 NO_ARM_ASM=1 + IMAGE_HEADER_SIZE?=5288 LIMIT=7800 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=8658 NO_ARM_ASM=1 + LIMIT=8660 NO_ARM_ASM=1 make keysclean make clean make test-size SIGN=ML_DSA ML_DSA_LEVEL=2 LIMIT=19800 \ diff --git a/tools/unit-tests/Makefile b/tools/unit-tests/Makefile index 24b7ccab03..6aa9752bb1 100644 --- a/tools/unit-tests/Makefile +++ b/tools/unit-tests/Makefile @@ -44,17 +44,21 @@ endif TESTS:=unit-parser unit-fdt unit-extflash unit-string unit-spi-flash unit-aes128 \ + unit-uart-flash \ unit-aes256 unit-chacha20 unit-pci unit-mock-state unit-sectorflags \ unit-max-space \ 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-delta \ + unit-update-flash-hook \ unit-update-flash-self-update \ unit-update-flash-enc unit-update-ram unit-update-ram-nofixed unit-pkcs11_store unit-psa_store unit-disk \ unit-update-disk unit-multiboot unit-boot-x86-fsp unit-loader-tpm-init unit-qspi-flash unit-fwtpm-stub unit-tpm-rsa-exp \ unit-image-nopart unit-image-sha384 unit-image-sha3-384 unit-store-sbrk \ unit-tpm-blob unit-policy-create unit-policy-sign unit-rot-auth unit-sdhci-response-bits \ - unit-sdhci-disk-unaligned unit-sign-encrypted-output + unit-sdhci-disk-unaligned unit-sign-encrypted-output \ + unit-keygen-xmss-params TESTS+=unit-tpm-check-rot-auth +TESTS+=unit-tpm-api-names include unit-sign-encrypted-output.mkfrag @@ -104,6 +108,10 @@ unit-psa_store:CFLAGS+=-I$(WOLFBOOT_LIB_WOLFPSA) -DMOCK_PARTITIONS -DMOCK_KEYVAU 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 \ -DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE +unit-update-flash-hook:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNIT_TEST_AUTH \ + -DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH -DPART_UPDATE_EXT -DPART_SWAP_EXT \ + -DWOLFBOOT_HOOK_BOOT -DWOLFBOOT_ORIGIN=MOCK_ADDRESS_BOOT \ + -DBOOTLOADER_PARTITION_SIZE=WOLFBOOT_PARTITION_SIZE unit-update-flash-delta:CFLAGS+=-DMOCK_PARTITIONS -DWOLFBOOT_NO_SIGN -DUNIT_TEST_AUTH \ -DWOLFBOOT_HASH_SHA256 -DPRINTF_ENABLED -DEXT_FLASH -DPART_UPDATE_EXT -DPART_SWAP_EXT \ -DDELTA_UPDATES -DDELTA_BLOCK_SIZE=512 -D__WOLFBOOT \ @@ -156,6 +164,9 @@ unit-spi-flash: ../../include/target.h unit-spi-flash.c unit-qspi-flash: ../../include/target.h unit-qspi-flash.c gcc -o $@ $^ $(CFLAGS) $(LDFLAGS) +unit-uart-flash: ../../include/target.h unit-uart-flash.c ../../src/uart_flash.c + gcc -o $@ unit-uart-flash.c $(CFLAGS) $(LDFLAGS) + unit-tpm-rsa-exp: ../../include/target.h unit-tpm-rsa-exp.c ../../src/string.c gcc -o $@ $^ $(CFLAGS) -I$(WOLFBOOT_LIB_WOLFTPM) -DWOLFBOOT_TPM \ -DWOLFTPM_USER_SETTINGS -DWOLFBOOT_TPM_VERIFY -DWOLFBOOT_SIGN_RSA2048 \ @@ -168,6 +179,12 @@ unit-tpm-check-rot-auth: ../../include/target.h unit-tpm-check-rot-auth.c ../../ -DWOLFBOOT_HASH_SHA256 \ -ffunction-sections -fdata-sections $(LDFLAGS) -Wl,--gc-sections +unit-tpm-api-names: ../../include/target.h unit-tpm-api-names.c ../../src/string.c + gcc -o $@ $^ $(CFLAGS) -I$(WOLFBOOT_LIB_WOLFTPM) -DWOLFBOOT_TPM \ + -DWOLFTPM_USER_SETTINGS -DWOLFBOOT_SIGN_RSA2048 \ + -DWOLFBOOT_HASH_SHA256 \ + -ffunction-sections -fdata-sections $(LDFLAGS) -Wl,--gc-sections + unit-fwtpm-stub: ../../include/target.h unit-fwtpm-stub.c gcc -o $@ $^ $(CFLAGS) -I$(WOLFBOOT_LIB_WOLFTPM) \ -DWOLFTPM_USER_SETTINGS -ffunction-sections -fdata-sections \ @@ -199,6 +216,13 @@ unit-sign-encrypted-output: ../../include/target.h unit-sign-encrypted-output.c -DWOLFBOOT_XMSS_PARAMS=\"XMSS-SHA2_10_256\" \ -ffunction-sections -fdata-sections \ $(LDFLAGS) -Wl,--gc-sections + +unit-keygen-xmss-params: ../../include/target.h unit-keygen-xmss-params.c + gcc -o $@ $^ -I../keytools $(CFLAGS) -DML_DSA_LEVEL=2 \ + -D"LMS_LEVELS=1" -D"LMS_HEIGHT=10" -D"LMS_WINTERNITZ=8" \ + -DWOLFBOOT_XMSS_PARAMS=\"XMSS-SHA2_10_256\" \ + -ffunction-sections -fdata-sections \ + $(LDFLAGS) -Wl,--gc-sections unit-rot-auth: ../../include/target.h unit-rot-auth.c \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/memory.c gcc -o $@ $^ -I../tpm $(CFLAGS) -I$(WOLFBOOT_LIB_WOLFTPM) -DWOLFBOOT_TPM \ @@ -300,6 +324,9 @@ unit-delta: ../../include/target.h unit-delta.c unit-update-flash: ../../include/target.h unit-update-flash.c gcc -o $@ unit-update-flash.c ../../src/image.c $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS) +unit-update-flash-hook: ../../include/target.h unit-update-flash.c + gcc -o $@ unit-update-flash.c ../../src/image.c $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS) + unit-update-flash-delta: ../../include/target.h unit-update-flash.c gcc -o $@ unit-update-flash.c ../../src/image.c ../../src/delta.c \ $(WOLFBOOT_LIB_WOLFSSL)/wolfcrypt/src/sha256.c $(CFLAGS) $(LDFLAGS) diff --git a/tools/unit-tests/unit-delta.c b/tools/unit-tests/unit-delta.c index c394c75bf6..7257583d92 100644 --- a/tools/unit-tests/unit-delta.c +++ b/tools/unit-tests/unit-delta.c @@ -204,6 +204,53 @@ START_TEST(test_wb_diff_self_match_extends_to_src_b_end) } END_TEST +START_TEST(test_wb_diff_self_match_caps_16bit_length) +{ + WB_DIFF_CTX diff_ctx; + uint8_t *src_a; + uint8_t *src_b; + uint8_t patch[BLOCK_HDR_SIZE + 1] = {0}; + const char *saved = getenv("WOLFBOOT_SECTOR_SIZE"); + char *saved_copy = saved ? strdup(saved) : NULL; + uint32_t sector_size = UINT16_MAX; + uint32_t match_start = 2U * sector_size; + uint32_t size = match_start + UINT16_MAX + 1024U; + uint16_t encoded_len; + int ret; + + ck_assert_int_eq(setenv("WOLFBOOT_SECTOR_SIZE", "0xFFFF", 1), 0); + + src_a = malloc(size); + src_b = calloc(1, size); + ck_assert_ptr_nonnull(src_a); + ck_assert_ptr_nonnull(src_b); + + memset(src_a, 0xA5, size); + + ret = wb_diff_init(&diff_ctx, src_a, size, src_b, size); + ck_assert_int_eq(ret, 0); + + diff_ctx.off_b = match_start; + ret = wb_diff(&diff_ctx, patch, sizeof(patch)); + ck_assert_int_eq(ret, BLOCK_HDR_SIZE); + + encoded_len = (uint16_t)((patch[4] << 8) | patch[5]); + ck_assert_uint_eq(encoded_len, UINT16_MAX); + ck_assert_uint_eq(diff_ctx.off_b, match_start + UINT16_MAX); + + free(src_b); + free(src_a); + + if (saved_copy != NULL) { + ck_assert_int_eq(setenv("WOLFBOOT_SECTOR_SIZE", saved_copy, 1), 0); + free(saved_copy); + } + else { + ck_assert_int_eq(unsetenv("WOLFBOOT_SECTOR_SIZE"), 0); + } +} +END_TEST + START_TEST(test_wb_diff_preserves_trailing_header_margin_for_escape) { WB_DIFF_CTX diff_ctx; @@ -618,6 +665,7 @@ Suite *patch_diff_suite(void) tcase_add_test(tc_wolfboot_delta, test_wb_patch_trailing_escape_invalid); tcase_add_test(tc_wolfboot_delta, test_wb_diff_match_extends_to_src_b_end); tcase_add_test(tc_wolfboot_delta, test_wb_diff_self_match_extends_to_src_b_end); + tcase_add_test(tc_wolfboot_delta, test_wb_diff_self_match_caps_16bit_length); tcase_add_test(tc_wolfboot_delta, test_wb_diff_preserves_trailing_header_margin_for_escape); tcase_add_test(tc_wolfboot_delta, test_wb_diff_preserves_main_loop_header_margin_for_escape); tcase_add_test(tc_wolfboot_delta, test_wb_diff_rejects_match_offsets_beyond_24_bits); diff --git a/tools/unit-tests/unit-disk.c b/tools/unit-tests/unit-disk.c index c7c80805cf..40dd08741f 100644 --- a/tools/unit-tests/unit-disk.c +++ b/tools/unit-tests/unit-disk.c @@ -88,6 +88,14 @@ static void finalize_gpt_header_crc(struct guid_ptable *hdr) hdr->hdr_crc32 = test_crc32((const uint8_t *)hdr, hdr->hdr_size); } +static void finalize_gpt_part_array_crc(struct guid_ptable *hdr) +{ + uint32_t array_len = hdr->n_part * hdr->array_sz; + uint8_t *array = fake_disk + (hdr->start_array * GPT_SECTOR_SIZE); + + hdr->part_crc = test_crc32(array, array_len); +} + /* --- Helpers to build fake disk layouts --- */ /* Write a UTF-16LE string into a buffer (no BOM). @@ -157,6 +165,7 @@ static void build_gpt_disk(void) memset(fake_disk + PART1_OFF * GPT_SECTOR_SIZE, 0xBB, (PART1_END - PART1_OFF + 1) * GPT_SECTOR_SIZE); + finalize_gpt_part_array_crc(gpt_hdr); finalize_gpt_header_crc(gpt_hdr); } @@ -327,6 +336,21 @@ START_TEST(test_disk_open_gpt) } END_TEST +START_TEST(test_disk_open_gpt_rejects_part_array_crc_mismatch) +{ + struct gpt_part_entry *pe; + + build_gpt_disk(); + + pe = (struct gpt_part_entry *)(fake_disk + 2 * GPT_SECTOR_SIZE); + pe->first = PART1_OFF; + pe->last = PART1_END; + + ck_assert_int_eq(disk_open(0), -1); + ck_assert_int_eq(Drives[0].is_open, 0); +} +END_TEST + START_TEST(test_disk_open_mbr) { int n; @@ -574,6 +598,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_part_array_crc(gpt_hdr); finalize_gpt_header_crc(gpt_hdr); /* Only 2 actual entries on disk so loop will break after parsing them, @@ -593,6 +618,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_part_array_crc(gpt_hdr); finalize_gpt_header_crc(gpt_hdr); ck_assert_int_eq(disk_open(0), 0); /* 0 partitions found */ @@ -616,6 +642,8 @@ START_TEST(test_disk_open_gpt_empty_entry_mid_table) pe = (struct gpt_part_entry *)(fake_disk + 2 * GPT_SECTOR_SIZE + 128); pe->type[0] = 0; pe->type[1] = 0; + finalize_gpt_part_array_crc(gpt_hdr); + finalize_gpt_header_crc(gpt_hdr); ck_assert_int_eq(disk_open(0), 1); } @@ -792,6 +820,24 @@ START_TEST(test_gpt_parse_partition_first_gt_last) } END_TEST +START_TEST(test_gpt_parse_partition_first_eq_last) +{ + uint8_t entry[128]; + struct gpt_part_entry *pe = (struct gpt_part_entry *)entry; + struct gpt_part_info info; + + memset(entry, 0, sizeof(entry)); + pe->type[0] = 0x0001020304050607ULL; + pe->type[1] = 0x08090A0B0C0D0E0FULL; + pe->first = 5; + pe->last = 5; /* first == last is a valid single-sector partition */ + + ck_assert_int_eq(gpt_parse_partition(entry, 128, &info), 0); + ck_assert_uint_eq(info.start, 5 * GPT_SECTOR_SIZE); + ck_assert_uint_eq(info.end, (6 * GPT_SECTOR_SIZE) - 1); +} +END_TEST + START_TEST(test_gpt_part_name_eq_label_too_long) { uint16_t name[GPT_PART_NAME_SIZE]; @@ -896,6 +942,7 @@ Suite *wolfboot_suite(void) suite_add_tcase(s, tc_gpt); tcase_add_test(tc_disk, test_disk_open_gpt); + tcase_add_test(tc_disk, test_disk_open_gpt_rejects_part_array_crc_mismatch); tcase_add_test(tc_disk, test_disk_open_mbr); tcase_add_test(tc_disk, test_disk_part_read); tcase_add_test(tc_disk, test_disk_find_partition_by_label); @@ -926,6 +973,7 @@ Suite *wolfboot_suite(void) tcase_add_test(tc_cov, test_disk_open_gpt_entry_read_failure); tcase_add_test(tc_cov, test_gpt_check_mbr_bad_bootsig); tcase_add_test(tc_cov, test_gpt_parse_partition_first_gt_last); + tcase_add_test(tc_cov, test_gpt_parse_partition_first_eq_last); tcase_add_test(tc_cov, test_gpt_part_name_eq_label_too_long); tcase_add_test(tc_cov, test_gpt_part_name_eq_not_null_terminated); suite_add_tcase(s, tc_cov); diff --git a/tools/unit-tests/unit-keygen-xmss-params.c b/tools/unit-tests/unit-keygen-xmss-params.c new file mode 100644 index 0000000000..b8e9a45f06 --- /dev/null +++ b/tools/unit-tests/unit-keygen-xmss-params.c @@ -0,0 +1,195 @@ +#include +#include +#include +#include +#include + +static const char *mock_xmss_param; +static int mock_exit_code; +static jmp_buf mock_exit_env; + +static void mock_exit(int code); + +#define main wolfboot_keygen_main +#define exit mock_exit +#define wc_XmssKey_Init mock_wc_XmssKey_Init +#define wc_XmssKey_SetParamStr mock_wc_XmssKey_SetParamStr +#define wc_XmssKey_SetWriteCb mock_wc_XmssKey_SetWriteCb +#define wc_XmssKey_SetReadCb mock_wc_XmssKey_SetReadCb +#define wc_XmssKey_SetContext mock_wc_XmssKey_SetContext +#define wc_XmssKey_MakeKey mock_wc_XmssKey_MakeKey +#define wc_XmssKey_GetPrivLen mock_wc_XmssKey_GetPrivLen +#define wc_XmssKey_ExportPubRaw mock_wc_XmssKey_ExportPubRaw +#define wc_XmssKey_Free mock_wc_XmssKey_Free +#define wc_ForceZero mock_wc_ForceZero +#include "../keytools/keygen.c" +#undef wc_ForceZero +#undef wc_XmssKey_Free +#undef wc_XmssKey_ExportPubRaw +#undef wc_XmssKey_GetPrivLen +#undef wc_XmssKey_MakeKey +#undef wc_XmssKey_SetContext +#undef wc_XmssKey_SetReadCb +#undef wc_XmssKey_SetWriteCb +#undef wc_XmssKey_SetParamStr +#undef wc_XmssKey_Init +#undef exit +#undef main + +static void mock_exit(int code) +{ + mock_exit_code = code; + longjmp(mock_exit_env, 1); +} + +int mock_wc_XmssKey_Init(XmssKey *key, void *heap, int devId) +{ + (void)key; + (void)heap; + (void)devId; + + return 0; +} + +int mock_wc_XmssKey_SetParamStr(XmssKey *key, const char *str) +{ + (void)key; + mock_xmss_param = str; + return 0; +} + +int mock_wc_XmssKey_SetWriteCb(XmssKey *key, wc_xmss_write_private_key_cb write_cb) +{ + (void)key; + (void)write_cb; + + return -1; +} + +int mock_wc_XmssKey_SetReadCb(XmssKey *key, wc_xmss_read_private_key_cb read_cb) +{ + (void)key; + (void)read_cb; + + return 0; +} + +int mock_wc_XmssKey_SetContext(XmssKey *key, void *context) +{ + (void)key; + (void)context; + + return 0; +} + +int mock_wc_XmssKey_MakeKey(XmssKey *key, WC_RNG *rng) +{ + (void)key; + (void)rng; + + return 0; +} + +int mock_wc_XmssKey_GetPrivLen(const XmssKey *key, word32 *len) +{ + (void)key; + *len = 0; + return 0; +} + +int mock_wc_XmssKey_ExportPubRaw(const XmssKey *key, byte *out, word32 *outLen) +{ + (void)key; + (void)out; + (void)outLen; + + return 0; +} + +void mock_wc_XmssKey_Free(XmssKey *key) +{ + (void)key; +} + +void mock_wc_ForceZero(void *mem, size_t len) +{ + (void)mem; + (void)len; +} + +static void setup(void) +{ + mock_xmss_param = NULL; + mock_exit_code = 0; + unsetenv("XMSS_PARAMS"); +} + +static void teardown(void) +{ + unsetenv("XMSS_PARAMS"); +} + +static void run_keygen_xmss(void) +{ + int jumped; + + jumped = setjmp(mock_exit_env); + if (jumped == 0) { + keygen_xmss("ignored.xmss", 0); + } + + ck_assert_int_eq(jumped, 1); + ck_assert_int_eq(mock_exit_code, 1); +} + +START_TEST(test_keygen_xmss_uses_env_param_when_set) +{ + const char *expected = "XMSSMT-SHA2_20/2_256"; + + ck_assert_int_eq(setenv("XMSS_PARAMS", expected, 1), 0); + + run_keygen_xmss(); + + ck_assert_ptr_nonnull(mock_xmss_param); + ck_assert_str_eq(mock_xmss_param, expected); +} +END_TEST + +START_TEST(test_keygen_xmss_uses_default_param_when_env_unset) +{ + run_keygen_xmss(); + + ck_assert_ptr_nonnull(mock_xmss_param); + ck_assert_str_eq(mock_xmss_param, WOLFBOOT_XMSS_PARAMS); +} +END_TEST + +static Suite *keygen_xmss_suite(void) +{ + Suite *s; + TCase *tc; + + s = suite_create("keygen_xmss"); + tc = tcase_create("xmss_params"); + tcase_add_checked_fixture(tc, setup, teardown); + tcase_add_test(tc, test_keygen_xmss_uses_env_param_when_set); + tcase_add_test(tc, test_keygen_xmss_uses_default_param_when_env_unset); + suite_add_tcase(s, tc); + + return s; +} + +int main(void) +{ + Suite *s; + SRunner *sr; + int failed; + + s = keygen_xmss_suite(); + sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + failed = srunner_ntests_failed(sr); + srunner_free(sr); + + return failed == 0 ? 0 : 1; +} diff --git a/tools/unit-tests/unit-multiboot.c b/tools/unit-tests/unit-multiboot.c index 0e727750da..f63d7e1180 100644 --- a/tools/unit-tests/unit-multiboot.c +++ b/tools/unit-tests/unit-multiboot.c @@ -22,8 +22,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ #include +#include #include #include +#include #include #ifndef WOLFBOOT_MULTIBOOT2 @@ -422,6 +424,49 @@ START_TEST(test_build_info_header_length_underflow) } END_TEST +START_TEST(test_dump_header_length_underflow) +{ + uint8_t header[48] __attribute__((aligned(8))); + struct mb2_header *h = (struct mb2_header *)header; + struct mb2_tag *tag; + FILE *capture; + int stderr_fd; + char output[512]; + size_t output_len; + + memset(header, 0, sizeof(header)); + h->magic = MB2_MAGIC; + h->header_length = 8; /* less than sizeof(struct mb2_header) */ + + tag = (struct mb2_tag *)(header + sizeof(struct mb2_header)); + tag->type = 1; + tag->flags = 0; + tag->size = sizeof(*tag); + + capture = tmpfile(); + ck_assert_ptr_nonnull(capture); + + fflush(stderr); + stderr_fd = dup(fileno(stderr)); + ck_assert_int_ge(stderr_fd, 0); + ck_assert_int_ge(dup2(fileno(capture), fileno(stderr)), 0); + + mb2_dump_header(header); + + fflush(stderr); + ck_assert_int_ge(dup2(stderr_fd, fileno(stderr)), 0); + close(stderr_fd); + + rewind(capture); + output_len = fread(output, 1, sizeof(output) - 1, capture); + output[output_len] = '\0'; + fclose(capture); + + ck_assert_ptr_nonnull(strstr(output, "Invalid header length")); + ck_assert_ptr_null(strstr(output, "Tag Type: 1\r\n")); +} +END_TEST + /* ---- Group 5: mb2_add_basic_mem_info ---- */ START_TEST(test_basic_mem_info_normal) @@ -722,6 +767,7 @@ Suite *wolfboot_suite(void) tcase_add_test(tc_build, test_build_info_unsupported_tag); tcase_add_test(tc_build, test_build_info_malformed_size); tcase_add_test(tc_build, test_build_info_header_length_underflow); + tcase_add_test(tc_build, test_dump_header_length_underflow); suite_add_tcase(s, tc_build); TCase *tc_basic = tcase_create("mb2_add_basic_mem_info"); diff --git a/tools/unit-tests/unit-qspi-flash.c b/tools/unit-tests/unit-qspi-flash.c index 0494e8e022..17e1bf1648 100644 --- a/tools/unit-tests/unit-qspi-flash.c +++ b/tools/unit-tests/unit-qspi-flash.c @@ -4,6 +4,7 @@ */ #define QSPI_FLASH +#define QSPI_FLASH_READY_TRIES 4 #include #include @@ -13,6 +14,9 @@ static int program_call_count; static uint32_t program_sizes[8]; static const uint8_t *program_ptrs[8]; static uint32_t program_addrs[8]; +static int write_enable_call_count; +static int write_enable_status_seq[8]; +static int current_write_enable_call; void spi_init(int polarity, int phase) { @@ -41,10 +45,21 @@ int qspi_transfer(uint8_t fmode, const uint8_t cmd, (void)dummySz; (void)dataMode; + if (cmd == WRITE_ENABLE_CMD) { + write_enable_call_count++; + current_write_enable_call = write_enable_call_count; + return 0; + } + if (cmd == READ_SR_CMD) { ck_assert_ptr_nonnull(data); ck_assert_uint_ge(dataSz, 1); - data[0] = FLASH_SR_WRITE_EN; + if (current_write_enable_call > 0 && + write_enable_status_seq[current_write_enable_call - 1] != 0) { + data[0] = 0; + } else { + data[0] = FLASH_SR_WRITE_EN; + } return 0; } @@ -66,6 +81,9 @@ static void setup(void) memset(program_sizes, 0, sizeof(program_sizes)); memset(program_ptrs, 0, sizeof(program_ptrs)); memset(program_addrs, 0, sizeof(program_addrs)); + write_enable_call_count = 0; + current_write_enable_call = 0; + memset(write_enable_status_seq, 0, sizeof(write_enable_status_seq)); } START_TEST(test_qspi_write_splits_last_page_to_remaining_bytes) @@ -88,6 +106,24 @@ START_TEST(test_qspi_write_splits_last_page_to_remaining_bytes) } END_TEST +START_TEST(test_qspi_write_stops_after_midloop_write_enable_failure) +{ + uint8_t buf[FLASH_PAGE_SIZE * 3]; + int ret; + + memset(buf, 0x3C, sizeof(buf)); + write_enable_status_seq[1] = -1; + + ret = spi_flash_write(0x2000, buf, sizeof(buf)); + + ck_assert_int_ne(ret, 0); + ck_assert_int_eq(program_call_count, 1); + 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], 0x2000); +} +END_TEST + static Suite *qspi_flash_suite(void) { Suite *s; @@ -97,6 +133,7 @@ static Suite *qspi_flash_suite(void) tc = tcase_create("Write"); tcase_add_checked_fixture(tc, setup, NULL); tcase_add_test(tc, test_qspi_write_splits_last_page_to_remaining_bytes); + tcase_add_test(tc, test_qspi_write_stops_after_midloop_write_enable_failure); suite_add_tcase(s, tc); return s; } diff --git a/tools/unit-tests/unit-sectorflags.c b/tools/unit-tests/unit-sectorflags.c index eb4fa3c890..851bfe38a7 100644 --- a/tools/unit-tests/unit-sectorflags.c +++ b/tools/unit-tests/unit-sectorflags.c @@ -45,6 +45,7 @@ uint8_t *ut_get_endpart(void); static int locked = 0; static int ext_locked = 0; +static int ext_write_count = 0; void hal_init(void) { @@ -91,6 +92,19 @@ uint8_t *ut_get_endpart(void) return flash + WOLFBOOT_PARTITION_SIZE; } +static void reset_flash_state(void) +{ + memset(flash, 0xFF, sizeof(flash)); + locked = 0; + ext_locked = 0; + ext_write_count = 0; +} + +static uint32_t update_sector_flag_addr(uint32_t pos) +{ + return PART_UPDATE_ENDFLAGS - (sizeof(uint32_t) + 2 + pos); +} + /* 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); @@ -111,6 +125,7 @@ int ext_flash_write(uintptr_t address, const uint8_t *data, int len) { /* Copy the data from the input buffer to the flash memory */ memcpy(&flash[address], data, len); + ext_write_count++; return 0; } @@ -210,7 +225,40 @@ START_TEST(test_partition_flags) { END_TEST START_TEST(test_sector_flags) { - + uint8_t flag = 0; + uint32_t flag_addr = update_sector_flag_addr(0); + int writes_after_change; + + reset_flash_state(); + flash[flag_addr] = 0xA5; + set_partition_magic(PART_UPDATE); + ext_write_count = 0; + + wolfBoot_set_update_sector_flag(0, 0x03); + ck_assert_uint_eq(flash[flag_addr], 0xA3); + ck_assert_int_eq(ext_write_count, 1); + ck_assert_int_eq(wolfBoot_get_update_sector_flag(0, &flag), 0); + ck_assert_uint_eq(flag, 0x03); + ck_assert_int_eq(wolfBoot_get_update_sector_flag(1, &flag), 0); + ck_assert_uint_eq(flag, 0x0A); + + writes_after_change = ext_write_count; + wolfBoot_set_update_sector_flag(0, 0x03); + ck_assert_int_eq(ext_write_count, writes_after_change); + ck_assert_uint_eq(flash[flag_addr], 0xA3); + + wolfBoot_set_update_sector_flag(1, 0x0C); + ck_assert_uint_eq(flash[flag_addr], 0xC3); + ck_assert_int_eq(ext_write_count, writes_after_change + 1); + ck_assert_int_eq(wolfBoot_get_update_sector_flag(0, &flag), 0); + ck_assert_uint_eq(flag, 0x03); + ck_assert_int_eq(wolfBoot_get_update_sector_flag(1, &flag), 0); + ck_assert_uint_eq(flag, 0x0C); + + writes_after_change = ext_write_count; + wolfBoot_set_update_sector_flag(1, 0x0C); + ck_assert_int_eq(ext_write_count, writes_after_change); + ck_assert_uint_eq(flash[flag_addr], 0xC3); } END_TEST diff --git a/tools/unit-tests/unit-tpm-api-names.c b/tools/unit-tests/unit-tpm-api-names.c new file mode 100644 index 0000000000..6a61abb976 --- /dev/null +++ b/tools/unit-tests/unit-tpm-api-names.c @@ -0,0 +1,104 @@ +/* unit-tpm-api-names.c + * + * Unit tests for TPM API string helpers. + */ + +#include +#include +#include +#include + +#ifndef SPI_CS_TPM +#define SPI_CS_TPM 1 +#endif + +#include "tpm.h" + +struct small_buf { + char text[4]; + char guard[4]; +}; + +int wolfBoot_printf(const char* fmt, ...) +{ + (void)fmt; + return 0; +} + +const char* TPM2_GetAlgName(TPM_ALG_ID alg) +{ + (void)alg; + return NULL; +} + +const char* TPM2_GetRCString(int rc) +{ + (void)rc; + return NULL; +} + +#include "../../src/tpm.c" + +static void setup_small_buf(struct small_buf* buf) +{ + memset(buf->text, 'X', sizeof(buf->text)); + memcpy(buf->guard, "END", 4); +} + +START_TEST(test_wolfBoot_tpm2_get_alg_name_bounds_unknown_fallback) +{ + struct small_buf buf; + const char* ret; + + setup_small_buf(&buf); + + ret = wolfBoot_tpm2_get_alg_name((TPM_ALG_ID)0xFFFF, buf.text, + (int)sizeof(buf.text)); + + ck_assert_ptr_eq(ret, buf.text); + ck_assert_str_eq(buf.guard, "END"); + ck_assert_int_eq(buf.text[sizeof(buf.text) - 1], '\0'); +} +END_TEST + +START_TEST(test_wolfBoot_tpm2_get_rc_string_bounds_unknown_fallback) +{ + struct small_buf buf; + const char* ret; + + setup_small_buf(&buf); + + ret = wolfBoot_tpm2_get_rc_string(-1, buf.text, (int)sizeof(buf.text)); + + ck_assert_ptr_eq(ret, buf.text); + ck_assert_str_eq(buf.guard, "END"); + ck_assert_int_eq(buf.text[sizeof(buf.text) - 1], '\0'); +} +END_TEST + +static Suite* tpm_api_names_suite(void) +{ + Suite* s; + TCase* tc; + + s = suite_create("TPM API names"); + tc = tcase_create("fallback_bounds"); + tcase_add_test(tc, test_wolfBoot_tpm2_get_alg_name_bounds_unknown_fallback); + tcase_add_test(tc, test_wolfBoot_tpm2_get_rc_string_bounds_unknown_fallback); + suite_add_tcase(s, tc); + return s; +} + +int main(void) +{ + Suite* s; + SRunner* sr; + int failed; + + s = tpm_api_names_suite(); + sr = srunner_create(s); + srunner_run_all(sr, CK_NORMAL); + failed = srunner_ntests_failed(sr); + srunner_free(sr); + return failed == 0 ? 0 : 1; +} diff --git a/tools/unit-tests/unit-uart-flash.c b/tools/unit-tests/unit-uart-flash.c new file mode 100644 index 0000000000..c26e11e128 --- /dev/null +++ b/tools/unit-tests/unit-uart-flash.c @@ -0,0 +1,93 @@ +/* unit-uart-flash.c + * + * Unit tests for the UART flash transport. + */ + +#define UART_FLASH + +#include +#include +#include + +static const uint8_t CMD_ACK = 0x06; + +static uint8_t rx_script[16]; +static int rx_script_len; +static int rx_script_pos; +static uint8_t tx_log[32]; +static int tx_log_len; + +uint32_t wolfBoot_current_firmware_version(void) +{ + return 0; +} + +int uart_tx(const uint8_t c) +{ + ck_assert_int_lt(tx_log_len, (int)sizeof(tx_log)); + tx_log[tx_log_len++] = c; + return 1; +} + +int uart_rx(uint8_t *c) +{ + if (rx_script_pos >= rx_script_len) + return 0; + + *c = rx_script[rx_script_pos++]; + return 1; +} + +#include "../../src/uart_flash.c" + +static void reset_uart_script(const uint8_t *script, int len) +{ + ck_assert_int_le(len, (int)sizeof(rx_script)); + memset(rx_script, 0, sizeof(rx_script)); + memcpy(rx_script, script, len); + rx_script_len = len; + rx_script_pos = 0; + memset(tx_log, 0, sizeof(tx_log)); + tx_log_len = 0; +} + +START_TEST(test_ext_flash_read_timeout_returns_error) +{ + uint8_t data[2] = {0}; + uint8_t script[11]; + int ret; + + memset(script, CMD_ACK, 10); + script[10] = 0x5a; + reset_uart_script(script, sizeof(script)); + + ret = ext_flash_read(0x12345678, data, sizeof(data)); + + ck_assert_int_eq(ret, -1); + ck_assert_uint_eq(data[0], 0x5a); + ck_assert_uint_eq(data[1], 0x00); +} +END_TEST + +Suite *wolfboot_suite(void) +{ + Suite *s = suite_create("wolfBoot"); + TCase *uart_flash = tcase_create("UART flash"); + + tcase_add_test(uart_flash, test_ext_flash_read_timeout_returns_error); + tcase_set_timeout(uart_flash, 20); + suite_add_tcase(s, uart_flash); + + 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; +} diff --git a/tools/unit-tests/unit-update-disk.c b/tools/unit-tests/unit-update-disk.c index b694cdbc03..5c9473449a 100644 --- a/tools/unit-tests/unit-update-disk.c +++ b/tools/unit-tests/unit-update-disk.c @@ -264,6 +264,38 @@ START_TEST(test_update_disk_prefers_primary_partition_when_versions_equal) } END_TEST +START_TEST(test_update_disk_boots_from_A_when_B_is_blank) +{ + reset_mocks(); + build_image(part_a_image, 7, 0xA1); + memset(part_b_image, 0, sizeof(part_b_image)); + + wolfBoot_start(); + + ck_assert_int_eq(wolfBoot_panicked, 0); + ck_assert_int_eq(mock_do_boot_called, 1); + ck_assert_ptr_eq(mock_boot_address, (const uint32_t *)WOLFBOOT_LOAD_ADDRESS); + ck_assert_int_eq(memcmp(load_buffer, part_a_image + IMAGE_HEADER_SIZE, + TEST_PAYLOAD_SIZE), 0); +} +END_TEST + +START_TEST(test_update_disk_boots_from_B_when_A_is_blank) +{ + reset_mocks(); + memset(part_a_image, 0, sizeof(part_a_image)); + build_image(part_b_image, 7, 0xB2); + + wolfBoot_start(); + + ck_assert_int_eq(wolfBoot_panicked, 0); + ck_assert_int_eq(mock_do_boot_called, 1); + ck_assert_ptr_eq(mock_boot_address, (const uint32_t *)WOLFBOOT_LOAD_ADDRESS); + ck_assert_int_eq(memcmp(load_buffer, part_b_image + IMAGE_HEADER_SIZE, + TEST_PAYLOAD_SIZE), 0); +} +END_TEST + START_TEST(test_get_decrypted_blob_version_rejects_truncated_version_tlv) { uint8_t hdr[IMAGE_HEADER_SIZE + 2]; @@ -315,6 +347,8 @@ Suite *wolfboot_suite(void) 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_update_disk_prefers_primary_partition_when_versions_equal); + tcase_add_test(tc, test_update_disk_boots_from_A_when_B_is_blank); + tcase_add_test(tc, test_update_disk_boots_from_B_when_A_is_blank); tcase_add_test(tc, test_get_decrypted_blob_version_rejects_truncated_version_tlv); tcase_add_test(tc, test_update_disk_rejects_rollback_after_higher_image_failure); suite_add_tcase(s, tc); diff --git a/tools/unit-tests/unit-update-flash.c b/tools/unit-tests/unit-update-flash.c index 14ed3035e0..9603cead56 100644 --- a/tools/unit-tests/unit-update-flash.c +++ b/tools/unit-tests/unit-update-flash.c @@ -187,6 +187,9 @@ Suite *wolfboot_suite(void); int wolfBoot_staged_ok = 0; const uint32_t *wolfBoot_stage_address = (uint32_t *) 0xFFFFFFFF; +#ifdef WOLFBOOT_HOOK_BOOT +static int mock_hook_corrupt_signature = 0; +#endif #ifdef RAM_CODE static int arch_reboot_called = 0; unsigned int _start_text = MOCK_ADDRESS_BOOT; @@ -211,6 +214,14 @@ int hal_flash_protect(haladdr_t address, int len) return 0; } +#ifdef WOLFBOOT_HOOK_BOOT +void wolfBoot_hook_boot(struct wolfBoot_image *boot_img) +{ + if (mock_hook_corrupt_signature != 0) + boot_img->signature_ok = 0; +} +#endif + static void reset_mock_stats(void) { wolfBoot_staged_ok = 0; @@ -237,6 +248,9 @@ static void reset_mock_stats(void) mock_wb_patch_init_patch = NULL; mock_wb_patch_init_psz = 0; #endif +#ifdef WOLFBOOT_HOOK_BOOT + mock_hook_corrupt_signature = 0; +#endif } static void clear_erase_stats(void) @@ -715,6 +729,23 @@ START_TEST (test_sunnyday_noupdate) } END_TEST +#ifdef WOLFBOOT_HOOK_BOOT +START_TEST (test_hook_mutation_triggers_final_sanity_check) +{ + reset_mock_stats(); + prepare_flash(); + add_payload(PART_BOOT, 1, TEST_SIZE_SMALL); + + mock_hook_corrupt_signature = 1; + wolfBoot_start(); + + ck_assert(wolfBoot_panicked); + ck_assert(!wolfBoot_staged_ok); + cleanup_flash(); +} +END_TEST +#endif + START_TEST (test_forward_update_samesize_notrigger) { reset_mock_stats(); prepare_flash(); @@ -1120,6 +1151,47 @@ START_TEST (test_diffbase_version_reads_from_little_endian_bytes) END_TEST #ifdef DELTA_UPDATES +static void prepare_inverse_delta_update(struct wolfBoot_image *boot, + struct wolfBoot_image *update, struct wolfBoot_image *swap, + uint32_t boot_version, uint32_t update_version, uint32_t delta_base, + uint32_t delta_inverse_offset, uint32_t delta_inverse_size) +{ + uint32_t word; + + reset_mock_stats(); + prepare_flash(); + + add_payload(PART_BOOT, boot_version, TEST_SIZE_SMALL); + add_payload(PART_UPDATE, update_version, TEST_SIZE_SMALL); + + ext_flash_unlock(); + word = (4u << 16) | HDR_IMG_DELTA_INVERSE; + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 64, + (const uint8_t *)&word, sizeof(word)); + word = host_to_img_u32(delta_inverse_offset); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 68, + (const uint8_t *)&word, sizeof(word)); + word = (4u << 16) | HDR_IMG_DELTA_INVERSE_SIZE; + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 72, + (const uint8_t *)&word, sizeof(word)); + word = host_to_img_u32(delta_inverse_size); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 76, + (const uint8_t *)&word, sizeof(word)); + word = (4u << 16) | HDR_IMG_DELTA_BASE; + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 80, + (const uint8_t *)&word, sizeof(word)); + word = host_to_img_u32(delta_base); + ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 84, + (const uint8_t *)&word, sizeof(word)); + ext_flash_lock(); + + ck_assert_int_eq(wolfBoot_open_image(boot, PART_BOOT), 0); + ck_assert_int_eq(wolfBoot_open_image(update, PART_UPDATE), 0); + memset(swap, 0, sizeof(*swap)); + swap->part = PART_SWAP; + swap->hdr = (void *)(uintptr_t)WOLFBOOT_PARTITION_SWAP_ADDRESS; +} + START_TEST (test_delta_zero_size_valid_header_rejected_without_recovery_heuristic) { struct wolfBoot_image boot, update, swap; @@ -1253,46 +1325,54 @@ END_TEST START_TEST (test_delta_inverse_values_passed_with_native_endian) { struct wolfBoot_image boot, update, swap; - uint32_t word; uint32_t delta_inverse_offset = 0x00001020; uint32_t delta_inverse_size = 0x00002040; - uint32_t delta_base = 1; int ret; - reset_mock_stats(); - prepare_flash(); + prepare_inverse_delta_update(&boot, &update, &swap, 1, 2, 1, + delta_inverse_offset, delta_inverse_size); - add_payload(PART_BOOT, 1, TEST_SIZE_SMALL); - add_payload(PART_UPDATE, 2, TEST_SIZE_SMALL); + ret = wolfBoot_delta_update(&boot, &update, &swap, 1, 1); + ck_assert_int_eq(ret, 0); + ck_assert_int_eq(mock_wb_patch_init_calls, 1); + ck_assert_ptr_eq(mock_wb_patch_init_patch, update.hdr + delta_inverse_offset); + ck_assert_uint_eq(mock_wb_patch_init_psz, delta_inverse_size); - ext_flash_unlock(); - word = (4u << 16) | HDR_IMG_DELTA_INVERSE; - ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 64, - (const uint8_t *)&word, sizeof(word)); - word = host_to_img_u32(delta_inverse_offset); - ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 68, - (const uint8_t *)&word, sizeof(word)); - word = (4u << 16) | HDR_IMG_DELTA_INVERSE_SIZE; - ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 72, - (const uint8_t *)&word, sizeof(word)); - word = host_to_img_u32(delta_inverse_size); - ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 76, - (const uint8_t *)&word, sizeof(word)); - word = (4u << 16) | HDR_IMG_DELTA_BASE; - ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 80, - (const uint8_t *)&word, sizeof(word)); - word = host_to_img_u32(delta_base); - ext_flash_write(WOLFBOOT_PARTITION_UPDATE_ADDRESS + 84, - (const uint8_t *)&word, sizeof(word)); - ext_flash_lock(); + cleanup_flash(); +} +END_TEST - ck_assert_int_eq(wolfBoot_open_image(&boot, PART_BOOT), 0); - ck_assert_int_eq(wolfBoot_open_image(&update, PART_UPDATE), 0); - memset(&swap, 0, sizeof(swap)); - swap.part = PART_SWAP; - swap.hdr = (void *)(uintptr_t)WOLFBOOT_PARTITION_SWAP_ADDRESS; +START_TEST (test_delta_inverse_accepts_when_current_matches_update) +{ + struct wolfBoot_image boot, update, swap; + uint32_t delta_inverse_offset = 0x00001020; + uint32_t delta_inverse_size = 0x00002040; + int ret; - ret = wolfBoot_delta_update(&boot, &update, &swap, 1, 1); + prepare_inverse_delta_update(&boot, &update, &swap, 2, 2, 1, + delta_inverse_offset, delta_inverse_size); + + ret = wolfBoot_delta_update(&boot, &update, &swap, 1, 0); + ck_assert_int_eq(ret, 0); + ck_assert_int_eq(mock_wb_patch_init_calls, 1); + ck_assert_ptr_eq(mock_wb_patch_init_patch, update.hdr + delta_inverse_offset); + ck_assert_uint_eq(mock_wb_patch_init_psz, delta_inverse_size); + + cleanup_flash(); +} +END_TEST + +START_TEST (test_delta_inverse_accepts_when_current_matches_delta_base) +{ + struct wolfBoot_image boot, update, swap; + uint32_t delta_inverse_offset = 0x00001020; + uint32_t delta_inverse_size = 0x00002040; + int ret; + + prepare_inverse_delta_update(&boot, &update, &swap, 1, 2, 1, + delta_inverse_offset, delta_inverse_size); + + ret = wolfBoot_delta_update(&boot, &update, &swap, 1, 0); ck_assert_int_eq(ret, 0); ck_assert_int_eq(mock_wb_patch_init_calls, 1); ck_assert_ptr_eq(mock_wb_patch_init_patch, update.hdr + delta_inverse_offset); @@ -1301,6 +1381,38 @@ START_TEST (test_delta_inverse_values_passed_with_native_endian) cleanup_flash(); } END_TEST + +START_TEST (test_delta_inverse_rejects_when_delta_base_is_newer_than_current) +{ + struct wolfBoot_image boot, update, swap; + int ret; + + prepare_inverse_delta_update(&boot, &update, &swap, 2, 2, 3, + 0x00001020, 0x00002040); + + ret = wolfBoot_delta_update(&boot, &update, &swap, 1, 0); + ck_assert_int_eq(ret, -1); + ck_assert_int_eq(mock_wb_patch_init_calls, 0); + + cleanup_flash(); +} +END_TEST + +START_TEST (test_delta_inverse_rejects_when_versions_do_not_match_any_arm) +{ + struct wolfBoot_image boot, update, swap; + int ret; + + prepare_inverse_delta_update(&boot, &update, &swap, 2, 1, 3, + 0x00001020, 0x00002040); + + ret = wolfBoot_delta_update(&boot, &update, &swap, 1, 0); + ck_assert_int_eq(ret, -1); + ck_assert_int_eq(mock_wb_patch_init_calls, 0); + + cleanup_flash(); +} +END_TEST #endif #endif @@ -1388,6 +1500,9 @@ Suite *wolfboot_suite(void) tcase_add_test(empty_panic, test_empty_panic); tcase_add_test(empty_panic, test_part_sanity_check_panics_on_sha_mismatch); tcase_add_test(empty_panic, test_part_sanity_check_panics_on_signature_mismatch); +#ifdef WOLFBOOT_HOOK_BOOT + tcase_add_test(empty_panic, test_hook_mutation_triggers_final_sanity_check); +#endif tcase_add_test(sunnyday_noupdate, test_sunnyday_noupdate); tcase_add_test(forward_update_samesize, test_forward_update_samesize); tcase_add_test(forward_update_tolarger, test_forward_update_tolarger); @@ -1416,6 +1531,10 @@ Suite *wolfboot_suite(void) tcase_add_test(delta_base_version, test_delta_base_version_mismatch_rejected); tcase_add_test(delta_base_version, test_delta_base_version_match_accepts); tcase_add_test(delta_base_version, test_delta_inverse_values_passed_with_native_endian); + tcase_add_test(delta_base_version, test_delta_inverse_accepts_when_current_matches_update); + tcase_add_test(delta_base_version, test_delta_inverse_accepts_when_current_matches_delta_base); + tcase_add_test(delta_base_version, test_delta_inverse_rejects_when_delta_base_is_newer_than_current); + tcase_add_test(delta_base_version, test_delta_inverse_rejects_when_versions_do_not_match_any_arm); #endif #ifdef RAM_CODE tcase_add_test(self_update_sameversion, test_self_update_sameversion_erased); diff --git a/tools/xmss/xmss_common.h b/tools/xmss/xmss_common.h index 751a4118fb..4d152ae65d 100644 --- a/tools/xmss/xmss_common.h +++ b/tools/xmss/xmss_common.h @@ -88,11 +88,13 @@ static enum wc_XmssRc xmss_write_key(const byte * priv, word32 privSz, void * co if (n_read != n_write) { fprintf(stderr, "error: read %d, expected %d: %d\n", (int)n_read, (int)n_write, ferror(file)); + wc_ForceZero(buff, privSz); free(buff); return WC_XMSS_RC_WRITE_FAIL; } n_cmp = XMEMCMP(buff, priv, n_write); + wc_ForceZero(buff, privSz); free(buff); buff = NULL;