Skip to content

fix(verify): fail closed on software-only and misbound external receipts#366

Merged
imran-siddique merged 1 commit into
mainfrom
fix/verify-fail-closed
Jun 30, 2026
Merged

fix(verify): fail closed on software-only and misbound external receipts#366
imran-siddique merged 1 commit into
mainfrom
fix/verify-fail-closed

Conversation

@imran-siddique

Copy link
Copy Markdown
Contributor

Summary

Two fail-closed fixes in cmcp_verify.verify, both in src/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 returned VERIFIED. This contradicts LIMITATIONS.md, which states software-only claims are partially_verified. The status decision now forces PARTIALLY_VERIFIED whenever hardware_attestation is 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_keys is supplied and a receipt's linked_call_id does not match the entry call_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. A continue now stops processing that entry after the binding mismatch.

Tests

  • test_verify.py: software-only claim returns PARTIALLY_VERIFIED (tightened, no longer accepts VERIFIED); hardware-backed happy path returns VERIFIED; a real failure keeps its failure status (not silently downgraded).
  • test_audit_conformance.py: strengthened the linked_call_id mismatch 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 assert RESULT: FAIL (partially_verified) and a non-zero exit, matching LIMITATIONS.md.

Validation: pytest tests/unit (758 passed, 3 skipped) and tests/conformance (58 passed) green; ruff check src/ --select E,F,W --ignore E501 clean.

Tracked Wave 2 follow-ups (not in this PR)

These require hardware-validated work and are intentionally out of scope:

  • Bind the audit-chain root into the attested report.
  • Make the cnf.jwk <-> report_data binding fatal for SEV-SNP/TDX, and fix the TDX report_data offset.
  • Verify the attestation report signatures (VCEK / DCAP / EK).

🤖 Generated with Claude Code

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>
@imran-siddique imran-siddique merged commit 0216ff5 into main Jun 30, 2026
12 checks passed
@imran-siddique imran-siddique deleted the fix/verify-fail-closed branch June 30, 2026 19:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant