Repository navigation
Port boot_integrity to Rust, and pin it with two real fixtures - #21
Merged
Merged
Conversation
The engine learned to read what the bootloader actually verified. Until the CLI
does too, the offline verdict says "I cannot tell whether images are checked"
about captures that answer the question on their own, and the two
implementations disagree about the same device, which is the failure the shared
expectation file exists to prevent.
So this ports it, and the fixtures are the point rather than an afterthought.
Neither new fixture is synthetic:
hab-unblown.log bootintel-7 lines 1-45. Reaches the prompt via
`u-boot=> setenv factorymode 1`, never dumps the
environment, and reports `hab fuse not enabled`. It pins
that integrity facts are read WITHOUT a printenv, and that a
harness line quoting U-Boot error strings
('Bad Linux ARM64 Image magic!') does not trip the anchored
failure pattern.
verified-image.log bootintel-20 lines 55-160. Environment provable only
retroactively from `Environment size:`, and the capture
carries a FAILED check (`Bad Magic Number`, from an
operator's `iminfo`) and a PASSED one
(`Verifying Checksum ... OK`) in that order. Both recorded,
which is the honest reading of a capture holding both.
Both implementations now reproduce the expectation byte for byte across five
fixtures, which is the first time the guarantee has covered anything but a
synthetic capture and two environments.
One bug found while porting, in code already shipped on the engine side. The
signature pattern included `## Checking (hash|sign)`, and U-Boot's ordinary FIT
output is `## Checking hash(es) for FIT Image at ...`. Every device using
UNSIGNED FIT hashes would have been reported as having had a signature verified,
and the verdict would have told a client authenticity was established. No corpus
log prints that line, which is exactly why it survived: absence of evidence read
as correctness. The real tell is the algorithm list, which reads
`sha256,rsa2048:dev+ OK` when a signature node was checked, so that is what is
keyed on now.
The renderer behind the expectation was also duplicated between the generator
and the test that checks it. Two renderers behind a file whose entire job is to
prove two implementations agree is the same mistake one level up; it now lives in
`analysis_engine/parity_render.py` and both import it. The Rust mirror stays a
mirror on purpose: the claim is that two independent implementations agree, which
sharing code would quietly stop testing.
349 tests, clippy clean under -D warnings, rustfmt clean.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Zenofex
added a commit
that referenced
this pull request
Sep 28, 2026
…nment (#22) Version bump, lockfile, changelog. No code changes: everything shipping here is already on `main` and was reviewed in #21. ## What ships The boot-chain verdict stops hedging when the capture answers the question itself. A log containing `Verifying Checksum ... OK` no longer gets "bootcmd boots an image without a visible verification step" while the visible step sits twenty lines away. New `Secure boot anchor` entry for an unblown i.MX HAB fuse, emitted without needing a `printenv` since the fact doesn't depend on one. What it still won't do is call a checksum a signature: a passing CRC is `confirmed`, never `hardened`. Both implementations now reproduce `expect.txt` byte for byte across five fixtures, two of them real corpus captures. ## Why MINOR Either reason alone is enough: - New verdict behaviour. - **Breaking library API**: `boot_chain::assess` returns an `Assessment { session, integrity, verdicts }` instead of a `(UbootSession, Vec<Verdict>)` tuple, and `verdict` takes the integrity alongside the session. The policy at the top of `CHANGELOG.md` puts a pre-1.0 breaking change on the minor. **No CLI flag, output or exit code changed**, so nothing changes for anyone using the binary. ## The step that used to get missed `crates/cli/Cargo.toml` pins `bootintel-detectors` by exact version and cargo does not derive it. Both bumps landed first try because `docs/releasing.md` step 1 now names it — added in #19 after it bit the 0.8.0 cut. ## Verification - `cargo test --workspace`: 355 passed, 0 failed - `clippy --workspace --all-targets -- -D warnings` and `fmt --all --check`: clean - `cargo build --release` then `bootintel --version` reports `bootintel 0.9.0` - Ran the release binary against the new fixtures After merge: dispatch `cli-release` for `0.9.0` with `publish_crates`, verify the draft against `SHA256SUMS`, publish and mark latest, then move the tap (step 7) and sync the in-repo reference copy. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follows #17/#18. The engine learned to read what the bootloader actually verified (BootIntel.com@fd96903); until the CLI does too, the offline verdict says "I cannot tell whether images are checked" about captures that answer the question themselves, and the two implementations disagree about the same device.
What the CLI now reports
A passing checksum is
confirmed, neverhardened. Only a verified signature (sha256,rsa2048:dev+ OK) ishardened. The anchor entry is emitted without needing aprintenv, since the fuse fact does not depend on one.The fixtures are the point
Neither is synthetic:
hab-unblown.log'Bad Linux ARM64 Image magic!'that must not trip the anchored failure patternverified-image.logBad Magic Number, from an operator'siminfo) and a passed one in that orderBoth implementations reproduce
expect.txtbyte for byte across five fixtures. That is the first time the guarantee has covered anything but a synthetic capture and two environments.A bug found while porting
The engine's signature pattern included
## Checking (hash|sign), and U-Boot's ordinary FIT output is## Checking hash(es) for FIT Image at .... Every device using unsigned FIT hashes would have been reported as having had a signature verified, and the verdict would have told a client authenticity was established. No corpus log prints that line, which is exactly why it survived review: absence of evidence read as correctness. The real tell is the algorithm list, so that is what both sides key on now, with a test for each shape.Breaking, library only
boot_chain::assessreturns anAssessment { session, integrity, verdicts }instead of a tuple, andverdicttakes the integrity alongside the session. The alternative was a second entry point for the same operation; two functions differing only in how much they tell you is worse for whoever reads it next. No CLI flag, output or exit code changed.verdict --jsongains aboot_integrityobject mirroring the engine's key names, omitting fields the capture said nothing about rather than emitting nulls.Under the policy at the top of
CHANGELOG.mdthat puts the next release at 0.9.0.Verification
cargo test --workspace: 349 passed, 0 failedclippy --workspace --all-targets -- -D warningsandfmt --check: cleanci-api-regression, pushed and deployed🤖 Generated with Claude Code