fix(keyless): bind proof-cache key to full entry + propagate audit-hash error (v0.9.2)#141
Merged
Merged
Conversation
…sh error (v0.9.2) Continues the STPA-Sec keyless-verify hardening from v0.9.1 (#136), closing two of the three clear-cut follow-ups from docs/security/stpa-keyless-2026-05-25.md. UCA-4 (#139): bind the proof-cache key to the full Rekor entry content via CacheKey::from_entry (every field, length-prefixed) instead of just (module_hash, uuid) — both attacker-supplied in the bundle. A cache hit can now only occur for a byte-identical entry that already passed SET verification, removing the defence-in-depth loss where a mutated entry rode a stale cache slot past the SET re-check on a cache hit. UCA-5 (#140): propagate the module serialize error in the audit-hash computation (? instead of .ok()), so a serialize failure aborts verify instead of recording the SHA-256 of zero bytes as a false artifact identity in the audit log and proof-cache key. UCA-1 (#137) was scoped in but DESCOPED: wiring the existing Merkle inclusion-proof verifier into verify() revealed it recomputes the wrong root for fresh production Rekor entries on the log2025-* shards (Rekor v2 / tiled-log migration) — the SET verifies but the computed root != the proof's root_hash. Enabling it fail-closed would reject all legitimate keyless signatures, so it stays unwired until the verifier is fixed. Reclassified on #137. UCA-3 (#138) remains deferred (clock-policy). Four new unit tests (3 cache-key binding + 1 fail-closed inclusion-proof building block); 609 library tests pass. Clean-room verified. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
9637ad8 to
4b8ca93
Compare
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
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.
Continues the STPA-Sec keyless-verify hardening campaign from v0.9.1 (#136). Closes UCA-4 (#139) and UCA-5 (#140) from
docs/security/stpa-keyless-2026-05-25.md.Wiring the existing Merkle inclusion-proof verifier into
verify()(the original plan for this release) revealed via the "Sign & Verify Example" CI job that the verifier recomputes the wrong Merkle root for fresh production Rekor entries on thelog2025-*shards (Rekor v2 / tiled-log migration):The verifier had never been on the production path, so this latent breakage was never observed. Enabling it fail-closed would reject all legitimate keyless signatures — strictly worse than the silent gap. UCA-1 is reclassified on #137 from "wire in the existing verifier" to "fix the verifier against Rekor v2, then wire it in". UCA-3 (#138) remains deferred (clock-policy decision).
What this PR fixes
CacheKey::from_entrybinds every entry field (length-prefixed, unambiguous) instead of(module_hash, uuid). A hit now requires a byte-identical entry that already passed SET verification.?instead of.ok()), so a serialize failure aborts verify instead of writing a false artifact identity to the forensic trail.Verification / oracles
CacheKey::from_entrytests prove every field mutation changes the key, plus a field-boundary-ambiguity test (("ab","c")≠("a","bc")).map_err(..)?; no.ok()remains on it.file:linecitations before the UCA-1 descope; the cache-binding and audit-hash claims it confirmed are exactly what ships here.Falsification statement
A keyless proof-cache hit can no longer be induced by mutating the embedded Rekor entry while keeping the module bytes — any field change forces full SET re-verification. A module that fails to serialize no longer records
sha256:e3b0c442…b855in the audit log; it aborts with a non-zero exit.Closes #139
Closes #140
🤖 Generated with Claude Code