Commit Graph

1259 Commits (2f9a8ea15dcdebd6b0eb9f28fa47a3df8e7db85e)

Author SHA1 Message Date
Daniele Lacamera 2f9a8ea15d F-7064: give the erase-completion ACK its long timeout
ERASE_TIMEOUT was defined but never used; the final erase ACK used
the plain short WAIT_CYCLES budget, so a slow remote flash made
ext_flash_erase() fail even when the erase succeeded. Factor
wait_ack_cycles() out of wait_ack() and use WAIT_CYCLES *
ERASE_TIMEOUT for the erase ACK, like uart_rx_timeout() does with
READ_TIMEOUT. Add unit tests for the success and timeout paths.
2026-09-23 01:48:54 +02:00
Daniele Lacamera 53f1d4e056 F-7060: fill the swap area with erased bytes, not the update flags
The loop wrote the 5-byte 'pBOOT' trailer SWAP_SIZE times, emitting
5x SWAP_SIZE bytes past the 4KB swap region (the file grew 16KB past
the mmap'd device) and leaving the swap filled with 'pBOOT' instead
of the erased 0xFF state real flash has. Use the 0xFF pad loop the
non-WOLF path already uses.
2026-09-23 01:48:54 +02:00
Daniele Lacamera 16ab1e086f F-7057: use the size of the destination buffer
The snprintf building output_diff_file passed
sizeof(CMD.output_image_file); both are char[PATH_MAX] today, so the
guarantee was accidental. Use sizeof of the destination.
2026-09-23 01:48:41 +02:00
Daniele Lacamera 96f507e0f9 F-7058: bound-check positional and option arguments
argv[argc] is NULL, so a missing trailing argument (e.g. 'sign
--ecc256 img.bin key.der') yielded a NULL fw_version that crashed
strtoul, and --id/--encrypt/--delta/--policy as the last token
dereferenced NULL directly. Guard the four value-taking options like
--cert-chain already does, and validate the positional argument
count against the selected signing mode before indexing.
2026-09-23 01:48:41 +02:00
Daniele Lacamera bfda601a05 F-7059: detect short reads in the image hashing loops
fread() returns size_t, so the (io_sz < 0) guard could never trigger,
and a short read silently folded stale buffer bytes into the image
digest. Compare the read count against the requested size in all
three loops (SHA-256, SHA-384, SHA3-384), matching the existing
io_sz != expected pattern used elsewhere in this file.
2026-09-23 01:48:41 +02:00
Daniele Lacamera 76db63f13e F-7063: add missing break in arg2num case 4
case 4 masked to 32 bits then fell through into case 8, whose only
statement is break. Harmless today, but indistinguishable from a
genuine missing break; add the explicit break.
2026-09-23 01:48:41 +02:00
Aidan Garske 6aecce0463 Enable the secure application handoff on STM32H5 2026-09-22 15:35:42 +02:00
Aidan Garske c48a882cd9 Add a generic secure application handoff with a measured boot record 2026-09-22 15:35:42 +02:00
David Garske 4f33091f74 uart: replace the duplicated NS16550 console code with a shared driver 2026-09-21 18:12:32 +02:00
David Garske fae9e53653 hooks: add pre-boot hook, DTB accessor and FDT alias/reg helpers 2026-09-21 18:10:09 +02:00
David Garske 4093fa0e17 Add NXP i.MX 8QuadMax MEK port including BL33 support 2026-09-21 18:07:44 +02:00
David Garske e353b20504 va416x0: fix vector table alignment and length, FRAM defects and ML-DSA 2026-09-21 17:13:42 +02:00
Daniele Lacamera ac519daf11 Address 3 Fenrir findings on PR 905 2026-09-18 21:38:27 +02:00
Daniele Lacamera d45f3446c7 Address Copilot review comments
- Add missing END_TEST after each START_TEST in unit-nrf5340-uart-crlf.c
- Fix partition overlap checks in target.h.in to use standard interval
  intersection (catches partitions that start below WOLFBOOT_ORIGIN and
  extend into the bootloader region)
2026-09-18 17:26:22 +02:00
Daniele Lacamera a2f7d8b651 Fix nrf5340 link error + cypsoc6 partition-overlap false positive
nrf5340: the target references nrf5340_uart_crlf (src/update_flash.c)
but the Makefile does not compile hal/nrf5340_uart.c, so the linker
fails. Add the object to OBJS (Makefile) and APP_OBJS (test-app).

cypsoc6: the new boot/update/swap vs bootloader-region #error guards
in include/target.h.in fire on the cypsoc6 layout, where the boot
partition intentionally overlaps the bootloader region (the bootloader
and the boot image share the same flash region). Introduce
WOLFBOOT_ALLOW_PART_OVERLAP (defined in cypsoc6.config, gated in
target.h.in, added to CONFIG_VARS in tools/config.mk) to suppress the
guards for targets that legitimately overlap.

Verified: nrf5340 builds (make distclean + cp config + make, exit 0,
wolfboot.bin created). cypsoc6 cannot be built locally (the CI checks
out lib/psoc6pdl from Infineon, which is not available locally), but
the WOLFBOOT_ALLOW_PART_OVERLAP guard is verified to suppress the
#error for the cypsoc6 layout.
2026-09-18 15:07:34 +02:00
Daniele Lacamera 6cc8437a66 F-13643: test swap round-trip restores the original boot image
A completed swap must leave the update partition as a faithful copy of
the previous boot image, so the emergency-rollback path (the
IMG_STATE_TESTING branch calling wolfBoot_update(1) to swap back) can
restore the original byte-for-byte. The backup half of the swap (the
boot->update copy, which under EXT_ENCRYPTED runs under
wolfBoot_enable_fallback_iv(1)) was otherwise never read back: the
forward direction is implicitly checked by wolfBoot_verify_integrity, but
the reverse direction had no backstop. Add
test_update_then_rollback_{samesize,larger,smaller}: install version 2,
snapshot the original boot image, re-run wolfBoot_start (the fallback
branch), and assert version 1 + memcmp(boot, snapshot) == 0. Parameterised
over same/larger/smaller payloads to cover the tail-sector copy guard in
both directions. Mutation-verified: skipping the boot->update copy breaks
the tests. Runs in the enc-full target, which exercises the encrypted
backup path.
2026-09-18 13:06:59 +02:00
Daniele Lacamera f80dde6d5d F-9753: accept the exact-fit update size in wolfBoot_update
The signer (sign.c) accepts total_img_sz == max_img_sz (the exact fit:
header + payload + trailer == partition size), but wolfBoot_update
rejected fw_size == MAX_UPDATE_SIZE via '> MAX_UPDATE_SIZE - 1', leaving
a 1-byte gap and an off-by-one between the two components. Drop the '- 1'
so the updater accepts the exact fit the signer produces. Rename
test_update_max_size_rejected to _accepted (version 2 now stages) and add
test_update_max_size_plus_one_rejected (MAX_UPDATE_SIZE + 1 still rejected).
2026-09-18 12:58:52 +02:00
Daniele Lacamera cf56410d11 F-9752: guard swap-resume test out of EXT_ENCRYPTED targets
The resume test stages a plain image, which the encrypted swap path
(unit-update-flash-enc-full) does not accept, so the test failed there.
The resume logic is identical with or without encryption, so the
non-encrypted target covers it; guard the test and its registration
behind !EXT_ENCRYPTED.
2026-09-18 12:58:46 +02:00
Daniele Lacamera 963bec3cc2 F-6874: test whFlashH5 Erase/Verify/BlankCheck OOB bounds guards
The region guard (offset > ctx->size || size > ctx->size - offset) in
whFlashH5_Erase/Verify/BlankCheck was untested for out-of-bounds input.
Erase's alignment checks run after the bounds check, so an aligned OOB
erase would pass alignment and erase adjacent flash with the guard removed;
Verify/BlankCheck would walk OOB. Add aligned OOB cases (offset = ctx.size,
size = sector; and offset 0, size = ctx.size + sector) asserting
WH_ERROR_BADARGS. Mutation-verified: removing the Erase guard makes the OOB
erase return ABORTED (the mock rejects the OOB erase) instead of BADARGS.
2026-09-18 12:45:16 +02:00
Daniele Lacamera c62b509646 F-9752: test interrupted per-sector swap resumes from BACKUP
A power fail mid-swap leaves the sector loop at the SECT_FLAG_BACKUP
fall-through entry point, which no prior test reached (the fault-injection
tests only aborted on the first write, and the vault resume tests cover a
different path). Fault the first internal write (the swap->BOOT copy of
sector 0) via the existing single-shot hal_flash_write_fail, re-run
wolfBoot_update, and verify it re-enters the loop at case SECT_FLAG_BACKUP
and ends with the partitions swapped. Only the BACKUP state is a recoverable
power fail: faulting the BOOT->update copy instead (SWAPPING state) erases
the update header, so the resume's re-open fails - that entry point is not
testable as a roundtrip. Mutation-verified (skipping the swap->boot copy
breaks the test).
2026-09-18 12:40:52 +02:00
Daniele Lacamera 59c47a87b7 F-13624: guard keygen-emitted keystore accessors against negative id
The keystore keygen emits (default for non-OTP, non-wolfHSM builds)
lacked the id < 0 guard the OTP backend has: get_buffer/get_size/get_mask
tested only id >= num (a negative id indexed PubKeys[] out of bounds) and
get_key_type had no bounds check. Add 'id < 0 ||' to all four. Add
unit-keygen-keystore, which sed-extracts the real Keystore_API template
from keygen.c, emits a self-contained keystore.c (keystore_gen.c), compiles
it, and asserts the same out-of-range contract unit-otp-keystore pins.
Mutation (drop the get_buffer guard) makes keystore_get_buffer(-1) return
non-NULL, caught.
2026-09-18 11:56:19 +02:00
Daniele Lacamera dfe715056e F-13623: nRF5340 hal_flash_protect lock the full requested range
The region count truncated (n = len / SPU_FLASH_BLOCK_SIZE), so a len
smaller than one 16 KiB SPU block locked nothing and a non-multiple len
left the tail block writable, while the function returned 0 either way.
Round the block count up so the locked range covers [start, start+len)
whole (partial blocks at both ends locked whole, which only ever widens
protection), and reject len < 0. Add unit-nrf5340-flash-protect, which
sed-extracts the real function and runs it against a mock SPU region
array; mutation (revert to the truncating count) fails 3 of 6 tests.
2026-09-18 11:43:50 +02:00
Daniele Lacamera 03ea0bbd9e F-13636: unit target for NSC update-partition bounds checks
Add unit-nsc-update, the first unit target defining TZEN. It compiles
libwolfboot.c with __WOLFBOOT + TZEN (non-CMSE: WOLFBOOT_NSC_NS_RW is a
pass-through) against the mock flash and checks the accept/reject sides
of the wolfBoot_nsc_erase_update / wolfBoot_nsc_write_update range checks,
which had no unit coverage. A weakened len bound is caught by the straddle
case writing past the partition end.
2026-09-18 11:33:20 +02:00
Daniele Lacamera ef4d73dea6 Add header_required_size cross-branch regression test (F-13644)
The size model was only compared against make_header_ex for NO_SIGN,
no-timestamp, non-delta. Drive the cross product of sign/policy/
timestamp/delta/dts/hash-algo and assert the writer's final content
size stays <= the model, via a test hook that records header_idx
before the 0xFF padding. Hybrid is excluded (its secondary signature
needs a real key context). Mutation-verified: removing the model's
timestamp term makes the test fail (idx > required).

Also reject a custom TLV reusing the --dts digest tag (F-9754): it
would serialize ahead of the real digest and shadow it.
2026-09-18 11:19:58 +02:00
Daniele Lacamera ba74601e91 F-13646: test HDR_CMDLINE sign/decode roundtrip
The --cmdline option stores the OS command line as a signature-covered
TLV (tag HDR_CMDLINE) that the bootloader recovers via
wolfBoot_find_header(); the roundtrip had zero coverage. Add
test_make_header_ex_roundtrip_cmdline_tlv: for lengths 1, 2, 3, 69, 70,
71, 255 (odd/even + extremes + the ~70-byte default-capacity boundary)
sign via make_header_ex and assert the decoder returns exactly the
signed bytes; 255 also pins the 256->512 header auto-grow.
2026-09-18 11:01:38 +02:00
Daniele Lacamera bdfddc5fad Add gpt_parse_header hdr_size bounds test (F-6759)
The hdr_size < GPT_HDR_MIN_SIZE || > GPT_SECTOR_SIZE guard was untested;
a crafted 0xFFFFFFFF hdr_size would drive the CRC read ~4GB past the
512-byte stack header. New test rejects both bounds; the lower case
recomputes the CRC over the reduced size so a deleted lower clause would
accept the header (return 0), pinning the clause.
2026-09-18 10:44:17 +02:00
Daniele Lacamera f1aa75afeb F-13633: test encrypt_key_is_valid erased-key rejection
encrypt_key_is_valid() rejects an erased key (all 0x00 or all 0xFF) but
had no test, so deleting the check or flipping its && to || survived.
Add three cases (all-0x00, all-0xFF, valid); both erased cases fail
under ||, which pins the &&.
2026-09-18 10:34:16 +02:00
Daniele Lacamera bad0fa3fed F-13635: isolate each diag_read_header content gate
The one negative test corrupted byte 0, tripping the magic and CRC gates
at once, so a deleted gate was still caught by its siblings. Add three
tests (magic/version/crc), each recomputing the header CRC so only the
corrupted field's gate can reject.
2026-09-18 10:30:36 +02:00
Daniele Lacamera 21c82fa4d8 F-12883: nRF5340 uart_write CRLF dropped multiline tail data
After emitting CRLF the buffer advanced to the newline, not the byte
after it, so multiline input reprocessed the newline (stray CRLFs) and
lost the following text. Extract the CRLF conversion to
hal/nrf5340_uart.c (no nrfx) for host unit testing; add a regression
test.
2026-09-18 04:27:28 +02:00
Daniele Lacamera 703065b68f Fix wolfCrypt build and footprint limits after the wolfSSL update
The wolfSSL bump to master 4aa1ad7a5 wraps all of coding.h in
"#ifndef NO_CODING".  That block also holds the auto-enable that turns
WOLFSSL_KEY_GEN into WOLFSSL_BASE64_ENCODE, so wolfBoot configurations
that set both WOLFSSL_KEY_GEN (SECURE_PKCS11) and NO_CODING lost the
Base64_Encode() declaration -- and coding.c compiles to nothing under
NO_CODING, so the definition is gone too.  settings.h still derives
WOLFSSL_DER_TO_PEM from WOLFSSL_KEY_GEN, so asn.c kept compiling
wc_DerToPemEx() and failed with an implicit declaration of
Base64_Encode().  This broke every TrustZone and PKCS11 target.

wolfBoot never emits PEM, so define WOLFSSL_NO_DER_TO_PEM whenever
NO_CODING is set rather than pulling base64 back in.  The check sits
after the HAVE_FIPS block, which may undef NO_CODING.  wolfTPM's
wc_DerToPem() callers are themselves guarded by WOLFSSL_DER_TO_PEM, so
they simply drop their optional PEM encoding path.

Separately, the update grows wolfboot by 8-136 bytes depending on the
configuration, so re-measure every test-size-all entry with the CI
container and record the new sizes.
2026-09-17 19:13:42 +02:00
Daniele Lacamera edf41b1c35 Verify partition isolation in custom-trailer state test
After writing both partitions, re-read BOOT and confirm it still
holds TESTING, so a backend that routes both IDs to the same
trailer is caught (previously each read followed its own write).

Verification: make -C tools/unit-tests unit-custom-trailer-nopart
exits 0; suite runs 1 check, 0 failures.
2026-09-17 09:29:03 +02:00
Daniele Lacamera d0ec587c7a Use literal exit codes in unit-custom-trailer-nopart
Replace EXIT_SUCCESS/EXIT_FAILURE with 0/1; the test does not
include <stdlib.h> and does not need the named macros.

Verification: make -C tools/unit-tests unit-custom-trailer-nopart
exits 0; suite runs 1 check, 0 failures.
2026-09-17 00:10:32 +02:00
Daniele Lacamera 80f109ea15 Drop tautology test from unit-custom-trailer-nopart
test_fixed_partition_apis_absent only asserted the build config
(0==0 when WOLFBOOT_FIXED_PARTITIONS is off), which the successful
compile of the same target already proves. Reviewer nit.

Verification: make -C tools/unit-tests unit-custom-trailer-nopart
exits 0; suite runs 1 check, 0 failures.
2026-09-16 23:20:22 +02:00
Daniele Lacamera bb2e1a70f8 Fix DQ0 wording in p1021 erase-fail test comment
STATUS_ERASE_FAIL is 0x81, so the fail bit (DQ0) is set, not clear.
2026-09-16 19:53:59 +02:00
Daniele Lacamera c3f9717fb5 PR 892: address 3 Fenrir review findings
- libwolfboot.c: keep the partition state APIs (set/get_partition_state)
  available to CUSTOM_PARTITION_TRAILER / WOLFBOOT_NO_PARTITIONS builds;
  guard the fixed-partition APIs (sector flags, erase, trigger, success)
  behind WOLFBOOT_FIXED_PARTITIONS. Add the unit-custom-trailer-nopart
  compile target to lock in that configuration.
- unit-p1021-fcm-bytes.c: add program tests for status 0x81 (fail) and
  0x00 (write-protected), asserting no later page is programmed.
- unit-p1021-erase-advance.c: add an erase test for status 0x00
  (write-protected), asserting failure before advancing to the next block.
2026-09-16 17:19:16 +02:00
Daniele Lacamera 6814b503ef PR 892: address 6 Fenrir review findings
- hal/nxp_p1021.c: fix ONFI status polarity (bit 0 set = fail, bit 7
  clear = protected; was checking bits 0+1 set = success)
- src/libwolfboot.c: move partition state APIs under HAVE_PARTITION_TRAILERS
  so CUSTOM_PARTITION_TRAILER without WOLFBOOT_FIXED_PARTITIONS links
- src/x86/ata.c: do not zeroize DMA buffer when another async op is
  in progress (buffer still owned by first command's transfer)
- unit-p1021-fcm-bytes/erase-advance: update mocks to ONFI status bytes
- unit-update-ram-custom-trailer: add BOOT selection assertion via
  mock_max_read_addr, PID-suffixed temp files, unlink in cleanup
- unit-update-ram-nofixed-noramboot: unlink PID-specific temp files
2026-09-16 16:18:49 +02:00
Daniele Lacamera d316af6567 PR 892: TFES PxCI check + unique test temp files
- src/x86/ata.c: verify PxCI is clear in the TFES path before scrubbing
  the DMA buffer (symmetric with the success path; prevents scrubbing
  while the HBA may still reference the buffer)
- unit-update-ram-nofixed-noramboot.c: append PID to /tmp filenames to
  avoid collisions under parallel make -j or shared runners
2026-09-16 14:40:50 +02:00
Daniele Lacamera 66be5eb520 PR 892: P1021 NAND status bits + test coverage for F-12883/F-12922
- hal/nxp_p1021.c: fix JEDEC status bit polarity (DQ0 clear = fail,
  DQ1 clear = protected; was checking set bits)
- hal/s32k1xx.c: clarify watchdog timeout comment (65535 ticks at
  128kHz LPO = ~512ms unprescaled, ~131s with 1:256 prescaler)
- unit-keygen-xmss-params.c: assert wc_ForceZero + wc_FreeRng called
  after keygen (F-12883)
- unit-mock-flash.c: track largest ext_flash_read source addr/len
- unit-p1021-erase-advance.c: add test_erase_stops_on_status_fail
  (cmd succeeds but NAND reports DQ0 clear)
- unit-update-ram-noramboot.c: verify fallback loads from UPDATE
  partition, not oversize BOOT (F-12922)
2026-09-16 14:40:42 +02:00
Daniele Lacamera cc7fec1ea2 PR 892: anti-rollback for CUSTOM_PARTITION_TRAILER targets
Add HAVE_PARTITION_TRAILERS macro (set when WOLFBOOT_FIXED_PARTITIONS or
CUSTOM_PARTITION_TRAILER is defined) and use it to gate the TESTING state
transition in update_ram.c. Previously the gate was WOLFBOOT_FIXED_PARTITIONS
only, so targets using custom partition trailers never transitioned from
UPDATING to TESTING, allowing rollback to older versions.

Add unit-update-ram-custom-trailer test covering the CUSTOM_PARTITION_TRAILER
path with mocked get/set_trailer_at callbacks.
2026-09-16 14:30:26 +02:00
Daniele Lacamera dcc6c64bd7 unit-tests: pin -std=gnu17 for host test builds
GCC 14+ defaults to gnu23, where glibc string.h defines memchr
and memcpy as _Generic macros that clash with wolfBoot's own
declarations in tests that #include a .c file (unit-string).
gnu17 matches the CI toolchain default.
2026-09-15 18:04:21 +02:00
Daniele Lacamera c1a7d89b2d unit-tests: mock wc_FreeRng for keygen_die
keygen_die() (F-9767) calls wc_FreeRng(); the test mocks
keygen.c's wolfCrypt dependencies one by one and was missing
this one, breaking the unit_tests and test_external_libs jobs.
2026-09-15 18:04:21 +02:00
Daniele Lacamera 5f6eb05552 F-6764: scrub passphrase buffer when async ATA command completes
security_command_passphrase() only wiped the static DMA buffer on the
synchronous path; in async mode (ata_security_erase_unit) the
passphrase stayed resident in the file-static buffer for the rest of
the boot, since ata_cmd_complete_async() never scrubbed it.

Add a scrub_buffer flag to the async state, set it when the command
goes in flight (or wipe immediately if it never started), and scrub in
ata_cmd_complete_async() on both the success and task-file-error exits,
once the HBA has retired the command.

Covered by two new unit tests: scrub after async completion and after
an async port error; the in-flight state is asserted unscrubbed.

Reported by Fenrir.
2026-09-15 15:43:11 +02:00
Daniele Lacamera b97d94deeb F-9768: rot: scrub NV auth and session state at exit
The TPM NV auth copy and the HMAC session state lived on the
stack until process teardown. Zeroize both at the common exit
label after the TPM device is unloaded, on success and error
paths alike.
2026-09-15 15:21:54 +02:00
Daniele Lacamera 483facf339 F-9767: keygen: free RNG before helper failure exit
keygen_ed25519/ed448/lms/xmss/ml_dsa have the same bypass as
the RSA/ECC helpers: exit() from their cleanup labels skips
main()'s wc_FreeRng(). Route their failure exits through
keygen_die() as well.
2026-09-15 15:19:25 +02:00
Daniele Lacamera a08badf880 F-8008: keygen: free RNG before helper failure exit
keygen_rsa/keygen_ecc exit() from their own cleanup labels,
bypassing main()'s wc_FreeRng() and leaving DRBG state resident.
Add keygen_die() that frees and zeroes the RNG before exit and
use it in the two helpers' failure paths.
2026-09-15 15:14:38 +02:00
Daniele Lacamera b1884b03d7 F-7394: sign: free RNG on RSA-PSS unsupported-hash error
The error path returned before the common wc_FreeRng(), leaving
DRBG state on the stack. Route it through the shared cleanup and
guard the sign call so hash_type/mgf are only used when set.
2026-09-15 15:14:38 +02:00
Daniele Lacamera e38e844381 unit-tests: rename noramboot fallback TCase
The F-12922 test checks fallback to the lower version, but the
TCase was still named "rollback denied" from before the fix,
describing the opposite behavior.
2026-09-15 14:32:18 +02:00
Daniele Lacamera efc5753b28 unit-tests: extract NAND_STATUS_* for p1021 erase test
ext_flash_erase() now checks NAND_STATUS_FAIL | NAND_STATUS_WPS
(F-12881), but the sed extraction rule only pulled the command
defines, so the unit_tests CI jobs failed to compile the test.
2026-09-15 14:32:18 +02:00
Daniele Lacamera bad522f654 F-12942: pkcs11: wipe PIN on terminal init failure
pkcs11_crypto_init() tore down the session on failure but left the
pre-populated pkcs11_pin credential in retained bootloader memory
after the handoff. Wipe it in the failure path, same as the deinit
path (F-12114). Regression test in unit-pkcs11-pin-zeroize: login
rejected -> init fails -> every pin byte is zero.
2026-09-15 13:24:14 +02:00
Daniele Lacamera 63f1ce7872 F-12922: do not block fallback to a valid image in wolfBoot_start
The version gate in the retry loop panicked whenever the selected
candidate failed verification and the fallback partition carried a
lower version, even though that image is valid and signed. A single
corrupted higher-version image bricked the device instead of falling
back. The gate only ever fired on the failure path (candidate
selection already prefers the higher version), so remove it; the
TESTING-state anti-rollback check in wolfBoot_dualboot_candidate()
still guards the confirmed-update path.
2026-09-15 08:24:11 +02:00