sandbox: one report_data binding for a run, a relying-party challenge, and evidence a third party can verify #392
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/sandbox-run-binding"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Sweep 2026-10-19 item #5, software side. Each claim was checked against origin/main before building:
BLAKE3(unit) ‖ BLAKE3(receipt), with no domain tag.[0; 8].verify_eatcompared no nonce, so any token the Supervisor key signed verified, whichever run it was for.The binding (SPEC §7.6)
The receipt binds the metered energy, so the report vouches for the joules through it.
TierRequest.challengecarries the relying party's 32 bytes intorun_and_seal.seal_runand its callers are unchanged.The sealed
TeeEvidencecarriesreport_hex(the report itself) andchallenge_hex. Both are omitted when absent, so evidence sealed before them keeps its bytes.verify_run_evidencelets a party other than the host check:report_databinds the attestation's unit, receipt and outcome, and the verifier's own challenge, never the recorded one;Evidence without its report is refused.
verify_eat(…, expect_nonce)refuses a token for another run. The CLI, MCP and build paths now pass the run's nonce. The MCP tool takes an optionalexpected_nonce_hexand reportsnonce_checked.SPEC: §7.6 gives the binding, the challenge and the third-party check; §7.8,
spec/eat.mdandspec/attestation.mdare updated to match.Also: an orphaned doc comment in sandbox-conformance that clippy refused. It predates this change.
Verification (build host)
--locked); every conformance class passes.-D warningsis clean on the seven touched crates.run_and_sealdropping the challenge;Not in this PR
🤖 Generated with Claude Code
A confidential run's TEE report bound BLAKE3(unit) ‖ BLAKE3(receipt), with no domain tag. The outcome sat only under the Supervisor's signature, the only freshness input was a host-chosen nonce (every call site passed [0; 8]), and the report was dropped once the host had checked it, so no one else could. verify_eat compared no nonce, so any token the Supervisor key signed verified, whichever run it was for. sandbox-runtime (SPEC §7.6): - run_report_data is one preimage: BLAKE3-XOF-64 in derive-key mode ("sandbox/1 run report_data v1") over length-prefixed unit_id, receipt_id and JCS(outcome), then 0x01 ‖ challenge, or 0x00 when there is none. The receipt binds the metered energy, so the report vouches for the joules through it. It replaces tee_report_data. - TierRequest.challenge carries the relying party's 32 bytes into run_and_seal. seal_run_with_mechanism takes a challenge; seal_run and its callers are unchanged. - The sealed TeeEvidence carries report_hex (the report itself) and challenge_hex. - verify_run_evidence lets a party other than the host check: - the report's signature and policy; - that its report_data binds the attestation's unit, receipt and outcome and the verifier's OWN challenge, never the recorded one; - that the summary is the report's. Evidence without its report is refused. sandbox-attest: - Outcome::core is shared by the attestation core and the preimage. - TeeEvidence.report_hex and challenge_hex are omitted when absent, so evidence sealed before them keeps its bytes. - run_nonce is exposed. - verify_eat takes expect_nonce and refuses a token whose nonce is not that run's. The CLI, MCP and build paths now pass the run's nonce. The MCP verify_eat tool takes an optional expected_nonce_hex and reports nonce_checked. SPEC §7.6 gives the binding, the challenge and the third-party check; §7.8, spec/eat.md and spec/attestation.md are updated to match. Also: an orphaned doc comment in sandbox-conformance's vectors.rs that clippy refused (it predates this change). Tests: - Binding and challenge. - An unchallenged report is never taken for a challenged one. - The preimage separates its inputs, including a length-delimitation collision. - A requested challenge reaches the report through run_and_seal. - An EAT is checked against the run the verifier holds. Mutation testing: 12 mutants, all killed. They cover: - the challenge, its presence tag, the outcome, the receipt and the length prefixes; - the verifier trusting the recorded challenge, the summary check, and the report and challenge being carried; - the seal and run_and_seal dropping the challenge; - the EAT nonce check. Verified on the build host: - sandbox-rs: 421 tests (--locked); every conformance class passes. - Clippy -D warnings clean on sandbox-runtime, -attest, -tee, -build, -mcp, -cli and -conformance. Not in this change: running it on SEV-SNP hardware (a billable cloud cell), a challenge slot in jcp-wire, and TDX on the run path, which still verifies SEV-SNP only. Co-Authored-By: Claude Opus 5.5 <[email protected]>