Skip to content

feat(verify): publish a TDX capture that binds a TRACE record's signing key - #69

Merged
imran-siddique merged 1 commit into
mainfrom
feat/verify-key-binding
Sep 14, 2026
Merged

imran-siddique merged 1 commit into
mainfrom
feat/verify-key-binding

Conversation

@imran-siddique

Copy link
Copy Markdown
Member

The two GCP TDX captures from 2026-07-21 put a manifest hash in REPORTDATA, and the input to that hash was never published. So /verify, the homepage panel and llms.txt could only say that genuine Intel TDX silicon signed the quote, and had to add that nothing tied a TRACE record to that machine.

This adds a third capture that closes that gap, taken on 2026-09-14 in a new GCP C3 trust domain (configfs-tsm, provider tdx_guest). Inside the TD the capture program generated an Ed25519 key, set REPORTDATA to SHA-256 of its raw public key followed by 32 zero bytes, took the quote, and signed a TRACE v0.3 record carrying that quote with agentrust-trace 0.10.0. The VM was deleted after the serial console readout.

Published under verify/fixtures

  • gcp-tdx-2026-09-14-keybind_quote.bin, 8,000 bytes, SHA-256 2217b3d6...1d61, MRTD c1ee9c16...70a5.
  • gcp-tdx-2026-09-14-keybind_record.json: the signed record, the public key and REPORTDATA.
  • gcp-tdx-2026-09-14-capture.py: the program that ran, whose SHA-256 is the record's build_provenance.digest.

Checks

  • verify/key-binding.js checks in the browser that SHA-256 of the record's cnf.jwk.x equals REPORTDATA[0:32], and that the record carries this quote and claims its MRTD. /verify loads capture 3 by default, and the homepage panel runs it with a REPORTDATA row that now passes or fails instead of pointing at a note.
  • tools/check-key-binding.py runs in the verifier job with agent-manifest 0.12.0 and agentrust-trace 0.10.0. It checks the quote to the pinned Intel root, the binding, the quote and MRTD in the record, the Ed25519 record signature over the SDK's canonical bytes, and the capture program digest. The browser does not check the record signature, because that would need a second canonical JSON implementation.
  • The differential adds the capture: 5,526 inputs across three captures, up from 3,684. check-tdx-verifier.mjs now pins SHA-256 and MRTD per capture, and fails if the key binding passes for either July capture.

Wording. /verify, the homepage note and llms.txt now say the quote commits to the key that signed the TRACE record. The stated limits are the ones that remain: no TCB or revocation appraisal, MRTD and RTMRs are shown but not compared with an expected image, and the record claims no model or policy.

Also: .gitattributes marks verify/fixtures/** as -text. Every file there is pinned by hash, and autocrlf would otherwise rewrite the capture program on a Windows checkout.

Checked

  • The capture bundle reassembled from 34 serial chunks matches its announced SHA-256. trace-spec's reference appraise() (examples/runtime-evidence/generate.py, draft v0.3 schema plus signature) grades the record attested.
  • Locally: tdx-differential.py then check-tdx-verifier.mjs (port agrees on all 5,526 inputs, 1,872 accepted), check-key-binding.py, check-site.py (43 pages, 654 local links), build-controls.py --check, build-discovery.py --check, check-discovery.py, check-dashes.js and marketplace.test.js all pass.
  • check-key-binding.py fails when one byte of the record key changes (binding and signature both fail) and when a claim in the record is edited (signature fails).
  • In headless Chrome, /verify prints the key binding as PASS with an ACCEPTED verdict, and every homepage panel row reads PASS.

Generated with Claude Code

https://claude.ai/code/session_013aK3gVWzNdcM3hZ2o2awK2

…ng key

The July GCP captures bind a manifest hash whose input was never published,
so /verify and the homepage could only say genuine silicon. A capture taken
on 2026-09-14 in a new C3 trust domain puts SHA-256 of an Ed25519 key,
generated inside the TD, in REPORTDATA and signs a TRACE v0.3 record with it.

The quote, the record and the capture program are published under
verify/fixtures. verify/key-binding.js checks the binding in the browser and
the homepage panel; tools/check-key-binding.py checks the record signature,
binding, quote, MRTD and program digest with the Python SDK in the verifier
job. The differential now covers three captures, 5,526 inputs.

verify/fixtures is marked -text, since every file there is pinned by hash.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013aK3gVWzNdcM3hZ2o2awK2
@imran-siddique
imran-siddique merged commit 7cba24d into main Sep 14, 2026
5 checks passed
@imran-siddique
imran-siddique deleted the feat/verify-key-binding branch September 14, 2026 20:24
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