Skip to content

DCAP attestation: Add GCP provenence check to establish whether the associated PPID is endorsed by Google - #54

Merged
ameba23 merged 25 commits into
mainfrom
peg/gcp-provenance
Aug 25, 2026
Merged

DCAP attestation: Add GCP provenence check to establish whether the associated PPID is endorsed by Google#54
ameba23 merged 25 commits into
mainfrom
peg/gcp-provenance

Conversation

@ameba23

@ameba23 ameba23 commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

When verifying an attestation which claims to be of type AttestationType::GcpTdx this PR adds an additional check as to whether the PPID from the PCK certificate included in the attestation is present in GCP's public bucket, which indicates that the PPID belongs to them. This is essentially a 'proof-of-cloud' check specially for GCP.

This is based on Google's own provenance checker tool written in Go: https://github.com/google/go-tdx-guest/blob/main/tools/gceprovenance/main.go

In order to match the Go implementation we:

  • Validate that PPIDs must be 16 bytes
  • Have a 30s timeout on the complete fetch
  • Check that the response has 200 status
  • Check that the response is valid JSON

Unlike the Go implementation we additionally:

  • To avoid unnecessary network calls on subsequent verifications, known GCP PPIDs are cached for up to one week. We might want to make this cache time shorter.
  • Validate that the response has fields zone and timestamp.
  • Cap the response size at 16kb.

Note: The Go implementation offers an additional instance-verification check as well as the provenance check. This checks the MR_OWNER value against instance metadata. This is outside of the scope of this PR and not implemented.

Note: No checks are made on the timestamp or zone details in the response, other than making sure those fields are present. This is because the Go implementation also does not check those values. Such checks could be added later in a follow-up.

See relevant documentation from Google:

TODO:

  • Validity window for cached PPIDs (store retrieval timestamp in cache)
  • Fix measurement policy to explicitly match GCP attestation type - otherwise this check is useless
  • Review against the Go implementation: https://github.com/google/go-tdx-guest/tree/main/tools/provenance
  • Tests - do we check PPID extraction with our existing DCAP test assets?

@ameba23
ameba23 marked this pull request as draft June 12, 2026 07:15
@ameba23
ameba23 marked this pull request as ready for review July 6, 2026 09:56
* main:
  Bump reqwest to 0.13.4
  chore(deps): bump openssl from 0.10.79 to 0.10.80
expected_input_data: [u8; 64],
pccs: Option<Pccs>,
) -> Result<MultiMeasurements, DcapVerificationError> {
) -> Result<(MultiMeasurements, Quote), DcapVerificationError> {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid parsing the quote a second time to extract the PPID after verification, the verifier function now returns the parsed quote.

@@ -0,0 +1,70 @@
//! On GCP check MRTD values map to Google endorsed firmware

@ameba23 ameba23 Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is mostly unchanged from main - i just refactored it into a separate file to avoid having both provenance and firmware stuff together in one file.

/// OS image
pub measurement_id: String,
/// The attestation type this record accepts
pub attestation_type: AttestationType,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To enforce the GCP provenance check, attestation policies need to explictly state the expected attestation type

Comment on lines +14 to +15
const GCP_PROVENANCE_REGISTRY_URL: &str =
"https://storage.googleapis.com/confidential-host-registry";

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be always fixed or should we also make it optionally set by an env var and if not provided it defaults to this URL instead?
This could also serve for future path in case PPID is deprecated or if the verifier would like to pull the registry from a different source.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree this would be nice to have configurable. I started adding this, and realised it would be nice to have this option on the builder API for attestation verifier, which is still an open PR awaiting review: #70 so will make an issue and add that once the builder API is merged.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Made #82

@MoeMahhouk MoeMahhouk Aug 25, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I was recently thinking about this more and maybe for some of our workloads that soon going to migrate to bare-metal, we could also have something similar to the GCP provenance where we provide an endpoint as a registery URL for the TEE hardware inventory until a more standardized version comes out like Intel's PoE

Comment thread crates/attestation/src/gcp/provenance.rs Outdated
Comment on lines +211 to +212
#[error("blocking task join: {0}")]
TaskJoin(String),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: semantically doesn't seem to be a GcpProvenanceError type but rather a threading or async kind of error type, or did I misunderstand something here ?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, semantically its not. This would happen if the task handling the provenance check panics. Which if this is implemented correctly should be highly unlikely to happen but we have to handle this case anyway. I would argue it make sense to categorize it as this because it is a possible bad outcome of running the provenance check.

@MoeMahhouk

Copy link
Copy Markdown
Member

Small suggestion:
This provenance check is supposed to be a new addition for the verification path only, probably it would be good to gate it behind a flag instead of making it enforced for every gcp dcap attestation verification process. wdyt?
This way, it would leave it up to the verifier to choose if they want to check attestation & provenance or only attestation would suffice for their workload?

@ameba23

ameba23 commented Aug 17, 2026

Copy link
Copy Markdown
Collaborator Author

Small suggestion: This provenance check is supposed to be a new addition for the verification path only, probably it would be good to gate it behind a flag instead of making it enforced for every gcp dcap attestation verification process. wdyt? This way, it would leave it up to the verifier to choose if they want to check attestation & provenance or only attestation would suffice for their workload?

If i understand you right, you mean we should handle the case that the verifier doesn't care whether or not its a GCP attestation - any DCAP attestation will do. Currently you can get this by specifying 'DcapTdx' as the attestation type in the policy. However, if the server claims their attestation type is GcpTdx in the PlatformMetadata, we still do this check and bail if it fails, even though the verifier doesn't require it. So maybe we should only do the check if the verifier enforces attestation type to be GcpTdx.

I see the logic here, but i'd be wary of skipping the check in that case, because if the server indicates that they are running on GCP but is not, i don't think we should treat that as a valid attestation. In the happy path, the non-GCP server correctly submits platform metadata as DcapTdx and the provenance check never happens.

If your concern is more just that we are starting to bloat the whole process with GCP-specific logic, then yes i can see an argument for putting it behind a feature flag and not compiling it on builds where we don't care about GCP.

* main:
  Bump crates from attest repo following fix for counting disks
  attestation: keep deprecated azure feature as alias for azure-attester
  ci: check azure-verifier portability on macOS
  attestation: move azure generation into attester/, verification into verify.rs
  attestation: vendor portable vTPM quote verification, split the azure feature
@ameba23
ameba23 requested a review from MoeMahhouk August 25, 2026 06:16
@ameba23
ameba23 merged commit 17afee1 into main Aug 25, 2026
4 checks passed
@ameba23
ameba23 deleted the peg/gcp-provenance branch August 25, 2026 08:32
samlaf added a commit to SeismicSystems/attested-tls that referenced this pull request Aug 25, 2026
…fyMode

Second of two changes for flashbots#84, stacked on
flashbots#85.

Reporting the collateral a verification consumed is half of provenance.
The other half is running the same verification again later, against
that bundle, and getting the same answer. Nothing exposed that: the
public entry points always fetched, and the only way to supply a bundle
was through test-only variants that also took a bare timestamp.

Verification now takes a VerifyMode. Live fetches collateral and reads
the wall clock. Archived carries the bundle and the instant it was
collected, so the two can never be supplied apart - a pinned bundle
evaluated at the wrong instant is the mistake the old
(Option<collateral>, now) pair permitted. Azure holds its AK certificate
chain to the instant the DCAP leg reported, so both legs evaluate at one
time in either mode.

The Azure TCB override leaves the public DCAP entry points. Only the
Azure verifier has a reason to relax TCB checks, so it reaches the
override through a crate-private variant; verify_dcap_attestation always
verifies at full strictness. The mock verifier likewise becomes its own
function rather than a cfg switch inside the production one, so
verify_dcap_attestation means Intel-rooted in every build and the
fixture tests replay real captures against it. AttestationVerifier is
the one place that picks between them.

BREAKING CHANGE: verify_attestation and verify_attestation_sync take a
VerifyMode; the DCAP and Azure entry points take mode before pccs and
the DCAP ones drop override_azure_outdated_tcb; the
*_with_given_timestamp variants are gone, replaced by
VerifyMode::Archived.

GCP checks and Archived mode
----------------------------

The GCP host provenance check from flashbots#54 stays live
in either mode, as does the firmware fetch for the quote's MRTD. Neither
rests on signed material a replay could re-verify: the provenance
document is an unsigned JSON object whose trust is the TLS connection to
Google's bucket, so archiving it would not make a replay stronger. The
docs on VerifyMode::Archived and verify_attestation state the carve-out.
Whether Archived should skip the provenance lookup instead is left open.

Closes flashbots#84
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.

2 participants