attestation!: report the DCAP collateral a verification consumed - #4
Open
samlaf wants to merge 3 commits into
Open
attestation!: report the DCAP collateral a verification consumed#4samlaf wants to merge 3 commits into
samlaf wants to merge 3 commits into
Conversation
The crate is usable as a relying party for a one-time event rather than a live handshake: evidence is verified once against a measurement policy and archived as permanent provenance. Nothing reported which collateral bundle a verification used, so there was nothing to archive alongside the evidence it verified. Fetching a second copy next to the verification is the obvious workaround, and it is subtly wrong. A PCCS cache refresh between the two fetches makes the archived bundle *a* bundle rather than *the* bundle the verification consumed, and for provenance that distinction is the whole point. Verification now returns VerifiedAttestation: the measurements, the collateral it consumed, and the instant every freshness check was evaluated at. The public entry points return Option<VerifiedAttestation> - None when the evidence carried no attestation and none was expected, the one case with no bundle and no instant to report. Nesting the absence in one Option keeps the three fields from ever disagreeing. One type serves every platform. A GCP TDX quote is a DCAP quote, and Azure wraps one in an HCL report and a vTPM attestation, so a verification always consumes exactly one collateral bundle, whichever platform produced the evidence. Azure holds its vTPM leg to the instant its DCAP leg reported, so verified_at is the single instant behind every freshness check rather than one per leg. That instant is the other half of what archiving buys: with the bundle, the same evidence and the same instant give the same answer forever. QuoteCollateralV3 is re-exported so callers can keep the bundle without taking a direct dependency on dcap-qvl. The return type of verify_attestation and verify_attestation_sync changes from Option<MultiMeasurements> to Option<VerifiedAttestation>. Both in-tree callers discard the value, so neither needed a change. Addresses the reporting half of flashbots#84. Verifying archived evidence against a pinned bundle at an explicit instant, the other half, follows separately.
The bundle and the instant were two independent public fields, so the pair a later replay needs arrived pre-split. Taking one without the other is not a mistake a caller has to work at: it is the shape of least resistance, and nothing objects. The input side already refuses that - VerifyMode::Archived carries both or neither - so the output was the one place the pair could come apart. CollateralSnapshot binds them. It is one value coming out and, in the change that follows, the same value going back in, so archiving is "keep the snapshot" and re-verifying is "hand it back". VerifiedAttestation becomes AttestationResult. RFC 9334 calls what an attester produces Evidence and what a verifier produces from it an Attestation Result. The crate already takes AttestationEvidence in, so this names the far end of one appraisal; "attestation" on its own named no field of the struct. The measurements docs now say where the values come from, which the Azure path made worth stating. They are read out of the quote on DCAP and GCP. On Azure they are the vTPM PCRs, measuring the guest boot rather than the launched TD - chained to the quote, whose report data commits to the HCL var data carrying the AK public key that signs the vTPM quote, but no field of it. The fixture test now asserts the whole snapshot, so the pairing itself is covered.
samlaf
force-pushed
the
sei-208/pr1-report-verified-collateral
branch
from
August 31, 2026 17:54
baca4a1 to
9b7c1ca
Compare
AttestationResult sat at the boundary that is meant to hide which platform produced the evidence, and two of its three fields were Intel types: a dcap-qvl Quote and a QuoteCollateralV3. The AWS Nitro work in flashbots#45 has neither, so the struct would break a second time the moment a non-TDX platform lands. flashbots#65 breaks it with no new platform at all - it moves the collateral into the evidence, so the verifier fetches none. The parsed quote leaves the shared type. It had exactly one consumer: AttestationVerifier binds it only to hand to the GCP provenance check, which reads the PPID out of the PCK leaf. gcp/firmware.rs discards it, and so does our own downstream. dcap.rs still hands it back, now as the second element of a tuple, so nothing re-parses the same bytes (34346c6) and the module gains no public type of its own - flashbots#40 may replace that module with attest-verify, which already owns a ValidatedDcapQuote one letter away. CollateralSnapshot becomes EndorsementSnapshot, and the bundle becomes an optional field on it. What a verifier fetches varies; the instant it holds that material to does not. So the instant stays a plain field and the fetched material hangs off it, with #[non_exhaustive] and a ::dcap() constructor keeping the pairing structural and later fields additive. A struct rather than an enum keyed by platform, because a verification consumes a set of fetched material rather than one of several alternatives, and because the axis it varies along is not the platform: whether endorsements ride in the evidence or get fetched is a transport choice of the protocol. flashbots#49 already made that choice one way for the Azure AK chain, and flashbots#65 proposes the opposite for DCAP collateral. "Collateral" is Intel's DCAP word, and would read as a category error the moment the snapshot holds Azure vTPM roots or whatever Nitro needs. "Endorsements" is the loose umbrella rather than RFC 9334's strict term - a DCAP bundle spans Endorsements and Reference Values both - and the doc comment says so, so the imprecision is deliberate rather than sloppy. The type is called VerifiedAttestation again, which reverses the rename in the commit before this one. That rename argued the value is the far end of one appraisal. It is not: the appraisal policy runs after the value is built, and a caller can configure it to check nothing, which is exactly what our enclave does before appraising in its own admission predicate. So no Reference Value comparison is implied, and what comes back is verified evidence rather than an Attestation Result. Leaving the RFC's name unspent keeps it for the type that would earn it if appraisal is ever separated from verification. Result is also taken in Rust, with AttestationError in this same crate. The docs on both types are shorter for it. The cache-refresh caveat is stated once instead of three times, and where the value sits in the RATS pipeline is stated once, linked to RFC 9334. Addresses review feedback on flashbots#85. BREAKING CHANGE: AttestationResult is renamed VerifiedAttestation and loses its quote field. CollateralSnapshot is renamed EndorsementSnapshot, is #[non_exhaustive], and its collateral field becomes dcap: Option<QuoteCollateralV3>, built through EndorsementSnapshot::dcap. The dcap::verify_* functions return (VerifiedAttestation, Quote), the azure::verify_* functions return VerifiedAttestation, and AttestationVerifier::verify_attestation{,_sync} return Option<VerifiedAttestation>.
samlaf
force-pushed
the
sei-208/pr1-report-verified-collateral
branch
from
August 31, 2026 22:17
21b8888 to
f828787
Compare
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.
The crate is usable as a relying party for a one-time event rather than a live handshake: evidence is verified once against a measurement policy and archived as permanent provenance. Nothing reported which collateral bundle a verification used, so there was nothing to archive alongside the evidence it verified.
Fetching a second copy next to the verification is the obvious workaround, and it is subtly wrong. A PCCS cache refresh between the two fetches makes the archived bundle a bundle rather than the bundle the verification consumed, and for provenance that distinction is the whole point.
Verification now returns VerifiedAttestation: the measurements, the collateral it consumed, and the instant every freshness check was evaluated at. The public entry points return Option
One type serves every platform. A GCP TDX quote is a DCAP quote, and Azure wraps one in an HCL report and a vTPM attestation, so a verification always consumes exactly one collateral bundle, whichever platform produced the evidence. Azure holds its vTPM leg to the instant its DCAP leg reported, so verified_at is the single instant behind every freshness check rather than one per leg.
That instant is the other half of what archiving buys: with the bundle, the same evidence and the same instant give the same answer forever. QuoteCollateralV3 is re-exported so callers can keep the bundle without taking a direct dependency on dcap-qvl.
The return type of verify_attestation and verify_attestation_sync changes from Option to Option. Both in-tree callers discard the value, so neither needed a change.
Addresses the reporting half of flashbots#84. Verifying archived evidence against a pinned bundle at an explicit instant, the other half, follows separately.