Track C: wohl-ota — verified dual-bank OTA state machine#11
Merged
Conversation
New no_std crate at crates/wohl-ota/ providing the firmware-update
state machine for Wohl sensor nodes. Pure software, hardware-free.
- enum OtaState { Idle, Downloading, Verifying, Ready, Swapping }
- Dual-bank slot model (A/B) with structural guarantee that downloads
always target the standby slot
- SignatureVerifier trait keeps the core crypto-free; AlwaysAccept /
AlwaysReject stubs drive tests and BMC harnesses
- Kani harnesses for OTA-P01..P04: target never active, swap only
from Ready, byte counter bounded, no panic on any 4-op sequence
- 11 unit tests + 4 proptest properties
Backs the NodeBootloader / OTAManager / OTAManagerProcess components
declared in spar/wohl_firmware.aadl lines 89-115, 186-200.
Not yet wired into the workspace — see WORKSPACE_INTEGRATION.md in
the next commit for the orchestrator-side edits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Documents the two-line edit to the root Cargo.toml needed to enrol wohl-ota into the workspace, plus the optional [workspace.dependencies] entry and the CI-matrix follow-up. Kept out of the crate commit so the orchestrator can apply workspace changes atomically with their own CI update. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Applies the WORKSPACE_INTEGRATION.md handoff (that file is now removed): - root Cargo.toml: add crates/wohl-ota to workspace members - [workspace.dependencies]: add wohl-ota path entry for future consumers Verified from the worktree: cargo test -p wohl-ota 15/15 cargo +1.85.0 clippy -p wohl-ota --all-targets -- -D warnings clean cargo +1.85.0 fmt -p wohl-ota -- --check clean Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
wohl-ota ships 4 Kani harnesses (OTA-P01..P04). The CI kani job had a hardcoded component list that didn't include the new crate, so its proofs would not have been gated on merge. Added wohl-ota to the loop. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixes two blocking review gaps in the verified OTA core. Issue 1: verify() never compared the downloaded image against manifest.sha256, so a correctly-signed manifest with a corrupt payload could reach Ready. verify() now also takes the caller- computed image digest and requires a byte-for-byte [u8;32] match (core stays crypto-free); add OtaError::BadImageDigest and Kani harness OTA-P05. Issue 2: rollback() flipped the active slot from any Idle state, even on fresh boot or twice after one swap. Track last_swap: Option<Slot>; rollback only reverts a recorded pre-swap slot, confirm_swap() commits the update, and a guardless second rollback fails cleanly with OtaError::NothingToRollback. Add Kani harness OTA-P06. OTA-P01..P04 still pass; P04 unwind raised 5->33 for the new 32-byte digest memcmp. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The kani job name said "all 6 components" but the loop now covers 7 (wohl-ota was added). Use "all components" so the name doesn't drift as more crates gain harnesses. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9aa0562 to
3361785
Compare
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.
Summary
Phase 2 Track C. New
crates/wohl-ota— theno_stdcore for over-the-air firmware update. Design-before-hardware: the state machine is proven correct in software first, then bolted onto a board later.What's in it
Dual-bank update state machine over two slots (A/B), one Active, one Standby:
OtaManifest(version, size, sha256, Ed25519 signature)SignatureVerifiertrait — no concrete crypto dependency;AlwaysAccept/AlwaysRejectstubs for tests + Kani. Real Ed25519 is a later, isolated change.no_std,no_alloc— fixed arrays, no Vec.Verification
Readybytes_receivednever exceedstotal_bytescargo +1.85.0 clippy --all-targets -- -D warnings+fmtcleanOpen design questions (for reviewers)
SignatureVerifier::verifytakes the parsed&OtaManifest— should it take canonical&[u8]instead, to decouple the trait from the struct?activefromIdlewith no post-swap watchdog window — add a rollback-budget state?Swappingis modelled as instantaneous; real hardware has a power-loss window — addbegin_swap/commit_swapfor durability proofs?Radio-agnostic — independent of the transport decisions (see #10).
🤖 Generated with Claude Code