fix(verify): fail closed on software-only and misbound external receipts#366
Merged
Conversation
Two fail-closed fixes in cmcp_verify.verify: 1. Software-only claims are no longer reported as VERIFIED. Previously a self-consistent software-only claim (no hardware-backed attestation) ended with failure=None and the status decision returned VERIFIED, contradicting LIMITATIONS.md, which states such claims are partially_verified. The status decision now forces PARTIALLY_VERIFIED whenever hardware_attestation is unverified and there is otherwise no failure. A real failure still takes precedence and is not downgraded. 2. External-execution-evidence misbinding now short-circuits. When external_evidence_keys is supplied and a receipt's linked_call_id does not match the entry call_id, verification recorded the mismatch but fell through and still ran the signature check, so a receipt bound to a different call could be reported signature-valid. A `continue` now stops processing that entry after the binding mismatch. Tests: software-only claim returns PARTIALLY_VERIFIED (not VERIFIED); hardware-backed happy path still returns VERIFIED; a real failure keeps its failure status; a linked_call_id mismatch is rejected and the receipt is not also reported signature-valid. Updated the CLI verify tests, which used a software-only fixture and previously asserted a full pass. Tracked Wave 2 follow-ups (not addressed here, require hardware-validated work): binding the audit-chain root into the attested report; making the cnf.jwk<->report_data binding fatal for SEV-SNP/TDX and fixing the TDX report_data offset; verifying the attestation report signatures (VCEK/DCAP/EK). Co-Authored-By: Claude Opus 4.8 (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.
Summary
Two fail-closed fixes in
cmcp_verify.verify, both insrc/cmcp_verify/verify.py.1. Software-only claims must not report VERIFIED. A self-consistent software-only claim (no hardware-backed attestation) previously ended with
failure=None, and the status decision returnedVERIFIED. This contradictsLIMITATIONS.md, which states software-only claims arepartially_verified. The status decision now forcesPARTIALLY_VERIFIEDwheneverhardware_attestationis in the unverified set and there is otherwise no failure. A genuine failure still takes precedence and is not downgraded to partial.2. External-execution-evidence misbinding now short-circuits. When
external_evidence_keysis supplied and a receipt'slinked_call_iddoes not match the entrycall_id, the code recorded the mismatch but fell through and still verified the receipt signature, so a receipt bound to a different call could be reported signature-valid. Acontinuenow stops processing that entry after the binding mismatch.Tests
test_verify.py: software-only claim returnsPARTIALLY_VERIFIED(tightened, no longer acceptsVERIFIED); hardware-backed happy path returnsVERIFIED; a real failure keeps its failure status (not silently downgraded).test_audit_conformance.py: strengthened thelinked_call_idmismatch test to assert the misbound receipt is not also reported signature-valid.test_verify_command.py: updated three CLI tests that used a software-only fixture and previously asserted a full pass; they now assertRESULT: FAIL (partially_verified)and a non-zero exit, matchingLIMITATIONS.md.Validation:
pytest tests/unit(758 passed, 3 skipped) andtests/conformance(58 passed) green;ruff check src/ --select E,F,W --ignore E501clean.Tracked Wave 2 follow-ups (not in this PR)
These require hardware-validated work and are intentionally out of scope:
cnf.jwk<->report_databinding fatal for SEV-SNP/TDX, and fix the TDXreport_dataoffset.🤖 Generated with Claude Code