From a08573d4de2e4c56744fae231d9d02227953b5a8 Mon Sep 17 00:00:00 2001 From: peg Date: Thu, 9 Jul 2026 08:08:03 +0200 Subject: [PATCH 1/9] Add builder pattern for constructing AttestationVerifier --- crates/attestation/src/lib.rs | 106 ++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 23 deletions(-) diff --git a/crates/attestation/src/lib.rs b/crates/attestation/src/lib.rs index cb67f63..d7af567 100644 --- a/crates/attestation/src/lib.rs +++ b/crates/attestation/src/lib.rs @@ -343,10 +343,6 @@ impl AttestationGenerator { pub struct AttestationVerifier { /// The measurement policy with accepted values and attestation types pub measurement_policy: MeasurementPolicy, - /// If this is empty, anything will be accepted - but measurements are - /// always injected into HTTP headers, so that they can be verified - /// upstream A PCCS service to use - defaults to Intel PCS - pub pccs_url: Option, /// Whether to write quotes to files on disk pub dump_dcap_quotes: bool, /// Whether to override outdated TCB when on Azure @@ -361,38 +357,105 @@ pub struct AttestationVerifier { gcp_provenance_checker: GcpProvenanceChecker, } +/// Options used to construct an [AttestationVerifier] +pub struct AttestationVerifierBuilder { + /// The measurement policy with accepted values and attestation types + measurement_policy: MeasurementPolicy, + /// A PCCS service to use - defaults to Intel PCS + pccs_url: Option, + dump_dcap_quotes: bool, + override_azure_outdated_tcb: bool, + internal_pccs_prewarm: Option, +} + +impl AttestationVerifierBuilder { + pub fn build(self) -> AttestationVerifier { + AttestationVerifier::build(self) + } + + /// Whether to write quotes to files on disk + pub fn dump_dcap_quotes(mut self) -> Self { + self.dump_dcap_quotes = true; + self + } + + /// Whether to override outdated TCB when on Azure + /// + /// This provides a workaround for a known outdated FMSPC used by Azure + pub fn override_azure_outdated_tcb(mut self) -> Self { + self.override_azure_outdated_tcb = true; + self + } + + /// Do not keep an internal DCAP collateral cache + pub fn with_no_internal_pccs(mut self) -> Self { + self.internal_pccs_prewarm = None; + self + } + + /// Keep a DCAP collateral cache, and pre-fill it with all available + /// collateral + pub fn with_pccs_prewarmed(mut self) -> Self { + self.internal_pccs_prewarm = Some(true); + self + } + + /// Keep a DCAP collateral cache, starting empty + pub fn with_pccs_not_prewarmed(mut self) -> Self { + self.internal_pccs_prewarm = Some(false); + self + } + + /// Set the URL used by internal PCCS + pub fn pccs_url(mut self, pccs_url: String) -> Self { + self.pccs_url = Some(pccs_url); + self + } +} + impl AttestationVerifier { - fn build( - measurement_policy: MeasurementPolicy, - pccs_url: Option, - dump_dcap_quotes: bool, - override_azure_outdated_tcb: bool, - known_gcp_firmware: GcpFirmwareCache, - ) -> Self { + fn build(builder: AttestationVerifierBuilder) -> Self { + let internal_pccs = builder.internal_pccs_prewarm.map(|with_prewarm| { + if with_prewarm { + Pccs::new(builder.pccs_url) + } else { + Pccs::new_without_prewarm(builder.pccs_url) + } + }); + Self { - measurement_policy, - pccs_url: pccs_url.clone(), - dump_dcap_quotes, - override_azure_outdated_tcb, - internal_pccs: Some(Pccs::new(pccs_url)), - known_gcp_firmware, + measurement_policy: builder.measurement_policy, + dump_dcap_quotes: builder.dump_dcap_quotes, + override_azure_outdated_tcb: builder.override_azure_outdated_tcb, + internal_pccs, + known_gcp_firmware: GcpFirmwareCache::new(), gcp_provenance_checker: GcpProvenanceChecker::new(), } } + pub fn builder(measurement_policy: MeasurementPolicy) -> AttestationVerifierBuilder { + AttestationVerifierBuilder { + measurement_policy, + pccs_url: None, + dump_dcap_quotes: false, + override_azure_outdated_tcb: false, + internal_pccs_prewarm: Some(true), + } + } + pub fn new( measurement_policy: MeasurementPolicy, pccs_url: Option, dump_dcap_quotes: bool, override_azure_outdated_tcb: bool, ) -> Self { - Self::build( + Self::build(AttestationVerifierBuilder { measurement_policy, pccs_url, dump_dcap_quotes, override_azure_outdated_tcb, - gcp::GcpFirmwareCache::new(), - ) + internal_pccs_prewarm: Some(true), + }) } /// Create an [AttestationVerifier] which will only allow no attestation @@ -400,7 +463,6 @@ impl AttestationVerifier { pub fn expect_none() -> Self { Self { measurement_policy: MeasurementPolicy::expect_none(), - pccs_url: None, dump_dcap_quotes: false, override_azure_outdated_tcb: false, internal_pccs: None, @@ -414,7 +476,6 @@ impl AttestationVerifier { pub fn mock() -> Self { Self { measurement_policy: MeasurementPolicy::mock(), - pccs_url: None, dump_dcap_quotes: false, override_azure_outdated_tcb: false, internal_pccs: None, @@ -428,7 +489,6 @@ impl AttestationVerifier { pub fn mock_with_pccs(pccs_url: String) -> Self { Self { measurement_policy: MeasurementPolicy::mock(), - pccs_url: None, dump_dcap_quotes: false, override_azure_outdated_tcb: false, internal_pccs: Some(Pccs::new(Some(pccs_url))), From ecfa3b7fb6c5fd04583b30a91b944455155b292c Mon Sep 17 00:00:00 2001 From: peg Date: Thu, 9 Jul 2026 08:47:55 +0200 Subject: [PATCH 2/9] Add builder pattern for constructing AttestationVerifier --- .../attestation-provider-server/src/main.rs | 9 +++-- crates/attestation/src/lib.rs | 36 +++++++++---------- crates/attested-tls/src/lib.rs | 9 ++--- crates/attested-tls/tests/nested_tls.rs | 5 ++- 4 files changed, 29 insertions(+), 30 deletions(-) diff --git a/crates/attestation-provider-server/src/main.rs b/crates/attestation-provider-server/src/main.rs index e4d2b31..83fef20 100644 --- a/crates/attestation-provider-server/src/main.rs +++ b/crates/attestation-provider-server/src/main.rs @@ -97,8 +97,13 @@ async fn main() -> anyhow::Result<()> { None => MeasurementPolicy::accept_anything(), }; - let attestation_verifier = - AttestationVerifier::new(measurement_policy, None, cli.log_dcap_quote, false); + let mut attestation_verifier_builder = AttestationVerifier::builder(measurement_policy); + + if cli.log_dcap_quote { + attestation_verifier_builder = attestation_verifier_builder.dump_dcap_quotes(); + } + + let attestation_verifier = attestation_verifier_builder.build(); let attestation_message = attestation_provider_client(server_addr, attestation_verifier).await?; diff --git a/crates/attestation/src/lib.rs b/crates/attestation/src/lib.rs index d7af567..d86f2c0 100644 --- a/crates/attestation/src/lib.rs +++ b/crates/attestation/src/lib.rs @@ -342,15 +342,16 @@ impl AttestationGenerator { #[derive(Clone, Debug)] pub struct AttestationVerifier { /// The measurement policy with accepted values and attestation types - pub measurement_policy: MeasurementPolicy, + measurement_policy: MeasurementPolicy, /// Whether to write quotes to files on disk - pub dump_dcap_quotes: bool, + dump_dcap_quotes: bool, + #[cfg(feature = "azure-verifier")] /// Whether to override outdated TCB when on Azure /// /// This provides a workaround for a known outdated FMSPC used by Azure - pub override_azure_outdated_tcb: bool, + override_azure_outdated_tcb: bool, /// Internal cache for collateral - pub internal_pccs: Option, + internal_pccs: Option, /// Cached GCP firmware blobs indexed by MRTD known_gcp_firmware: GcpFirmwareCache, /// Cached PPIDs that have a valid GCP host-registry document @@ -364,6 +365,7 @@ pub struct AttestationVerifierBuilder { /// A PCCS service to use - defaults to Intel PCS pccs_url: Option, dump_dcap_quotes: bool, + #[cfg(feature = "azure-verifier")] override_azure_outdated_tcb: bool, internal_pccs_prewarm: Option, } @@ -382,6 +384,7 @@ impl AttestationVerifierBuilder { /// Whether to override outdated TCB when on Azure /// /// This provides a workaround for a known outdated FMSPC used by Azure + #[cfg(feature = "azure-verifier")] pub fn override_azure_outdated_tcb(mut self) -> Self { self.override_azure_outdated_tcb = true; self @@ -426,6 +429,7 @@ impl AttestationVerifier { Self { measurement_policy: builder.measurement_policy, dump_dcap_quotes: builder.dump_dcap_quotes, + #[cfg(feature = "azure-verifier")] override_azure_outdated_tcb: builder.override_azure_outdated_tcb, internal_pccs, known_gcp_firmware: GcpFirmwareCache::new(), @@ -438,32 +442,19 @@ impl AttestationVerifier { measurement_policy, pccs_url: None, dump_dcap_quotes: false, + #[cfg(feature = "azure-verifier")] override_azure_outdated_tcb: false, internal_pccs_prewarm: Some(true), } } - pub fn new( - measurement_policy: MeasurementPolicy, - pccs_url: Option, - dump_dcap_quotes: bool, - override_azure_outdated_tcb: bool, - ) -> Self { - Self::build(AttestationVerifierBuilder { - measurement_policy, - pccs_url, - dump_dcap_quotes, - override_azure_outdated_tcb, - internal_pccs_prewarm: Some(true), - }) - } - /// Create an [AttestationVerifier] which will only allow no attestation /// and will reject if one is given pub fn expect_none() -> Self { Self { measurement_policy: MeasurementPolicy::expect_none(), dump_dcap_quotes: false, + #[cfg(feature = "azure-verifier")] override_azure_outdated_tcb: false, internal_pccs: None, known_gcp_firmware: GcpFirmwareCache::new(), @@ -477,6 +468,7 @@ impl AttestationVerifier { Self { measurement_policy: MeasurementPolicy::mock(), dump_dcap_quotes: false, + #[cfg(feature = "azure-verifier")] override_azure_outdated_tcb: false, internal_pccs: None, known_gcp_firmware: GcpFirmwareCache::new(), @@ -490,6 +482,7 @@ impl AttestationVerifier { Self { measurement_policy: MeasurementPolicy::mock(), dump_dcap_quotes: false, + #[cfg(feature = "azure-verifier")] override_azure_outdated_tcb: false, internal_pccs: Some(Pccs::new(Some(pccs_url))), known_gcp_firmware: GcpFirmwareCache::new(), @@ -679,6 +672,11 @@ impl AttestationVerifier { pub fn has_remote_attestation(&self) -> bool { self.measurement_policy.has_remote_attestation() } + + /// Returns the measurement policy used + pub fn measurement_policy(&self) -> &MeasurementPolicy { + &self.measurement_policy + } } /// Write attestation data to a log file diff --git a/crates/attested-tls/src/lib.rs b/crates/attested-tls/src/lib.rs index 25a386d..c6da230 100644 --- a/crates/attested-tls/src/lib.rs +++ b/crates/attested-tls/src/lib.rs @@ -1104,9 +1104,7 @@ mod tests { let mock_pcs_server = spawn_mock_pcs_server(MockPcsConfig::default()).await.unwrap(); let verifier = AttestationVerifier::mock_with_pccs(mock_pcs_server.base_url.clone()); - if let Some(ref pccs) = verifier.internal_pccs { - pccs.ready().await.unwrap(); - } + verifier.ready().await.unwrap(); let mut builder = AttestedCertificateVerifier::build(verifier).with_crypto_provider(provider); @@ -1559,9 +1557,8 @@ mod tests { .unwrap(); let mock_pcs_server = spawn_mock_pcs_server(MockPcsConfig::default()).await.unwrap(); let verifier = AttestationVerifier::mock_with_pccs(mock_pcs_server.base_url.clone()); - if let Some(ref pccs) = verifier.internal_pccs { - pccs.ready().await.unwrap(); - } + verifier.ready().await.unwrap(); + let verifier = AttestedCertificateVerifier::build(verifier) .with_crypto_provider(provider) .with_allowed_leaf_cert_pubkey(&key_pair.public_key_der()) diff --git a/crates/attested-tls/tests/nested_tls.rs b/crates/attested-tls/tests/nested_tls.rs index a3dcdf2..0e47c6b 100644 --- a/crates/attested-tls/tests/nested_tls.rs +++ b/crates/attested-tls/tests/nested_tls.rs @@ -120,9 +120,8 @@ async fn attested_client_config(provider: Arc) -> ClientConfig { let mock_pcs_server = spawn_mock_pcs_server(MockPcsConfig::default()).await.unwrap(); let verifier = AttestationVerifier::mock_with_pccs(mock_pcs_server.base_url.clone()); - if let Some(ref pccs) = verifier.internal_pccs { - pccs.ready().await.unwrap(); - } + verifier.ready().await.unwrap(); + let verifier = AttestedCertificateVerifier::build(verifier) .with_crypto_provider(provider.clone()) .finish() From c20ad26014baffd2d5d154d80c012a557a7e4099 Mon Sep 17 00:00:00 2001 From: peg Date: Thu, 9 Jul 2026 08:08:03 +0200 Subject: [PATCH 3/9] Add builder pattern for constructing AttestationVerifier --- crates/attestation/src/lib.rs | 106 ++++++++++++++++++++++++++-------- 1 file changed, 83 insertions(+), 23 deletions(-) diff --git a/crates/attestation/src/lib.rs b/crates/attestation/src/lib.rs index cb67f63..d7af567 100644 --- a/crates/attestation/src/lib.rs +++ b/crates/attestation/src/lib.rs @@ -343,10 +343,6 @@ impl AttestationGenerator { pub struct AttestationVerifier { /// The measurement policy with accepted values and attestation types pub measurement_policy: MeasurementPolicy, - /// If this is empty, anything will be accepted - but measurements are - /// always injected into HTTP headers, so that they can be verified - /// upstream A PCCS service to use - defaults to Intel PCS - pub pccs_url: Option, /// Whether to write quotes to files on disk pub dump_dcap_quotes: bool, /// Whether to override outdated TCB when on Azure @@ -361,38 +357,105 @@ pub struct AttestationVerifier { gcp_provenance_checker: GcpProvenanceChecker, } +/// Options used to construct an [AttestationVerifier] +pub struct AttestationVerifierBuilder { + /// The measurement policy with accepted values and attestation types + measurement_policy: MeasurementPolicy, + /// A PCCS service to use - defaults to Intel PCS + pccs_url: Option, + dump_dcap_quotes: bool, + override_azure_outdated_tcb: bool, + internal_pccs_prewarm: Option, +} + +impl AttestationVerifierBuilder { + pub fn build(self) -> AttestationVerifier { + AttestationVerifier::build(self) + } + + /// Whether to write quotes to files on disk + pub fn dump_dcap_quotes(mut self) -> Self { + self.dump_dcap_quotes = true; + self + } + + /// Whether to override outdated TCB when on Azure + /// + /// This provides a workaround for a known outdated FMSPC used by Azure + pub fn override_azure_outdated_tcb(mut self) -> Self { + self.override_azure_outdated_tcb = true; + self + } + + /// Do not keep an internal DCAP collateral cache + pub fn with_no_internal_pccs(mut self) -> Self { + self.internal_pccs_prewarm = None; + self + } + + /// Keep a DCAP collateral cache, and pre-fill it with all available + /// collateral + pub fn with_pccs_prewarmed(mut self) -> Self { + self.internal_pccs_prewarm = Some(true); + self + } + + /// Keep a DCAP collateral cache, starting empty + pub fn with_pccs_not_prewarmed(mut self) -> Self { + self.internal_pccs_prewarm = Some(false); + self + } + + /// Set the URL used by internal PCCS + pub fn pccs_url(mut self, pccs_url: String) -> Self { + self.pccs_url = Some(pccs_url); + self + } +} + impl AttestationVerifier { - fn build( - measurement_policy: MeasurementPolicy, - pccs_url: Option, - dump_dcap_quotes: bool, - override_azure_outdated_tcb: bool, - known_gcp_firmware: GcpFirmwareCache, - ) -> Self { + fn build(builder: AttestationVerifierBuilder) -> Self { + let internal_pccs = builder.internal_pccs_prewarm.map(|with_prewarm| { + if with_prewarm { + Pccs::new(builder.pccs_url) + } else { + Pccs::new_without_prewarm(builder.pccs_url) + } + }); + Self { - measurement_policy, - pccs_url: pccs_url.clone(), - dump_dcap_quotes, - override_azure_outdated_tcb, - internal_pccs: Some(Pccs::new(pccs_url)), - known_gcp_firmware, + measurement_policy: builder.measurement_policy, + dump_dcap_quotes: builder.dump_dcap_quotes, + override_azure_outdated_tcb: builder.override_azure_outdated_tcb, + internal_pccs, + known_gcp_firmware: GcpFirmwareCache::new(), gcp_provenance_checker: GcpProvenanceChecker::new(), } } + pub fn builder(measurement_policy: MeasurementPolicy) -> AttestationVerifierBuilder { + AttestationVerifierBuilder { + measurement_policy, + pccs_url: None, + dump_dcap_quotes: false, + override_azure_outdated_tcb: false, + internal_pccs_prewarm: Some(true), + } + } + pub fn new( measurement_policy: MeasurementPolicy, pccs_url: Option, dump_dcap_quotes: bool, override_azure_outdated_tcb: bool, ) -> Self { - Self::build( + Self::build(AttestationVerifierBuilder { measurement_policy, pccs_url, dump_dcap_quotes, override_azure_outdated_tcb, - gcp::GcpFirmwareCache::new(), - ) + internal_pccs_prewarm: Some(true), + }) } /// Create an [AttestationVerifier] which will only allow no attestation @@ -400,7 +463,6 @@ impl AttestationVerifier { pub fn expect_none() -> Self { Self { measurement_policy: MeasurementPolicy::expect_none(), - pccs_url: None, dump_dcap_quotes: false, override_azure_outdated_tcb: false, internal_pccs: None, @@ -414,7 +476,6 @@ impl AttestationVerifier { pub fn mock() -> Self { Self { measurement_policy: MeasurementPolicy::mock(), - pccs_url: None, dump_dcap_quotes: false, override_azure_outdated_tcb: false, internal_pccs: None, @@ -428,7 +489,6 @@ impl AttestationVerifier { pub fn mock_with_pccs(pccs_url: String) -> Self { Self { measurement_policy: MeasurementPolicy::mock(), - pccs_url: None, dump_dcap_quotes: false, override_azure_outdated_tcb: false, internal_pccs: Some(Pccs::new(Some(pccs_url))), From 6dc312316c4ce2481b430f6f8b53b82adb5adfb7 Mon Sep 17 00:00:00 2001 From: peg Date: Thu, 9 Jul 2026 08:47:55 +0200 Subject: [PATCH 4/9] Add builder pattern for constructing AttestationVerifier --- .../attestation-provider-server/src/main.rs | 9 +++-- crates/attestation/src/lib.rs | 36 +++++++++---------- crates/attested-tls/src/lib.rs | 9 ++--- crates/attested-tls/tests/nested_tls.rs | 5 ++- 4 files changed, 29 insertions(+), 30 deletions(-) diff --git a/crates/attestation-provider-server/src/main.rs b/crates/attestation-provider-server/src/main.rs index e4d2b31..83fef20 100644 --- a/crates/attestation-provider-server/src/main.rs +++ b/crates/attestation-provider-server/src/main.rs @@ -97,8 +97,13 @@ async fn main() -> anyhow::Result<()> { None => MeasurementPolicy::accept_anything(), }; - let attestation_verifier = - AttestationVerifier::new(measurement_policy, None, cli.log_dcap_quote, false); + let mut attestation_verifier_builder = AttestationVerifier::builder(measurement_policy); + + if cli.log_dcap_quote { + attestation_verifier_builder = attestation_verifier_builder.dump_dcap_quotes(); + } + + let attestation_verifier = attestation_verifier_builder.build(); let attestation_message = attestation_provider_client(server_addr, attestation_verifier).await?; diff --git a/crates/attestation/src/lib.rs b/crates/attestation/src/lib.rs index d7af567..d86f2c0 100644 --- a/crates/attestation/src/lib.rs +++ b/crates/attestation/src/lib.rs @@ -342,15 +342,16 @@ impl AttestationGenerator { #[derive(Clone, Debug)] pub struct AttestationVerifier { /// The measurement policy with accepted values and attestation types - pub measurement_policy: MeasurementPolicy, + measurement_policy: MeasurementPolicy, /// Whether to write quotes to files on disk - pub dump_dcap_quotes: bool, + dump_dcap_quotes: bool, + #[cfg(feature = "azure-verifier")] /// Whether to override outdated TCB when on Azure /// /// This provides a workaround for a known outdated FMSPC used by Azure - pub override_azure_outdated_tcb: bool, + override_azure_outdated_tcb: bool, /// Internal cache for collateral - pub internal_pccs: Option, + internal_pccs: Option, /// Cached GCP firmware blobs indexed by MRTD known_gcp_firmware: GcpFirmwareCache, /// Cached PPIDs that have a valid GCP host-registry document @@ -364,6 +365,7 @@ pub struct AttestationVerifierBuilder { /// A PCCS service to use - defaults to Intel PCS pccs_url: Option, dump_dcap_quotes: bool, + #[cfg(feature = "azure-verifier")] override_azure_outdated_tcb: bool, internal_pccs_prewarm: Option, } @@ -382,6 +384,7 @@ impl AttestationVerifierBuilder { /// Whether to override outdated TCB when on Azure /// /// This provides a workaround for a known outdated FMSPC used by Azure + #[cfg(feature = "azure-verifier")] pub fn override_azure_outdated_tcb(mut self) -> Self { self.override_azure_outdated_tcb = true; self @@ -426,6 +429,7 @@ impl AttestationVerifier { Self { measurement_policy: builder.measurement_policy, dump_dcap_quotes: builder.dump_dcap_quotes, + #[cfg(feature = "azure-verifier")] override_azure_outdated_tcb: builder.override_azure_outdated_tcb, internal_pccs, known_gcp_firmware: GcpFirmwareCache::new(), @@ -438,32 +442,19 @@ impl AttestationVerifier { measurement_policy, pccs_url: None, dump_dcap_quotes: false, + #[cfg(feature = "azure-verifier")] override_azure_outdated_tcb: false, internal_pccs_prewarm: Some(true), } } - pub fn new( - measurement_policy: MeasurementPolicy, - pccs_url: Option, - dump_dcap_quotes: bool, - override_azure_outdated_tcb: bool, - ) -> Self { - Self::build(AttestationVerifierBuilder { - measurement_policy, - pccs_url, - dump_dcap_quotes, - override_azure_outdated_tcb, - internal_pccs_prewarm: Some(true), - }) - } - /// Create an [AttestationVerifier] which will only allow no attestation /// and will reject if one is given pub fn expect_none() -> Self { Self { measurement_policy: MeasurementPolicy::expect_none(), dump_dcap_quotes: false, + #[cfg(feature = "azure-verifier")] override_azure_outdated_tcb: false, internal_pccs: None, known_gcp_firmware: GcpFirmwareCache::new(), @@ -477,6 +468,7 @@ impl AttestationVerifier { Self { measurement_policy: MeasurementPolicy::mock(), dump_dcap_quotes: false, + #[cfg(feature = "azure-verifier")] override_azure_outdated_tcb: false, internal_pccs: None, known_gcp_firmware: GcpFirmwareCache::new(), @@ -490,6 +482,7 @@ impl AttestationVerifier { Self { measurement_policy: MeasurementPolicy::mock(), dump_dcap_quotes: false, + #[cfg(feature = "azure-verifier")] override_azure_outdated_tcb: false, internal_pccs: Some(Pccs::new(Some(pccs_url))), known_gcp_firmware: GcpFirmwareCache::new(), @@ -679,6 +672,11 @@ impl AttestationVerifier { pub fn has_remote_attestation(&self) -> bool { self.measurement_policy.has_remote_attestation() } + + /// Returns the measurement policy used + pub fn measurement_policy(&self) -> &MeasurementPolicy { + &self.measurement_policy + } } /// Write attestation data to a log file diff --git a/crates/attested-tls/src/lib.rs b/crates/attested-tls/src/lib.rs index 25a386d..c6da230 100644 --- a/crates/attested-tls/src/lib.rs +++ b/crates/attested-tls/src/lib.rs @@ -1104,9 +1104,7 @@ mod tests { let mock_pcs_server = spawn_mock_pcs_server(MockPcsConfig::default()).await.unwrap(); let verifier = AttestationVerifier::mock_with_pccs(mock_pcs_server.base_url.clone()); - if let Some(ref pccs) = verifier.internal_pccs { - pccs.ready().await.unwrap(); - } + verifier.ready().await.unwrap(); let mut builder = AttestedCertificateVerifier::build(verifier).with_crypto_provider(provider); @@ -1559,9 +1557,8 @@ mod tests { .unwrap(); let mock_pcs_server = spawn_mock_pcs_server(MockPcsConfig::default()).await.unwrap(); let verifier = AttestationVerifier::mock_with_pccs(mock_pcs_server.base_url.clone()); - if let Some(ref pccs) = verifier.internal_pccs { - pccs.ready().await.unwrap(); - } + verifier.ready().await.unwrap(); + let verifier = AttestedCertificateVerifier::build(verifier) .with_crypto_provider(provider) .with_allowed_leaf_cert_pubkey(&key_pair.public_key_der()) diff --git a/crates/attested-tls/tests/nested_tls.rs b/crates/attested-tls/tests/nested_tls.rs index a3dcdf2..0e47c6b 100644 --- a/crates/attested-tls/tests/nested_tls.rs +++ b/crates/attested-tls/tests/nested_tls.rs @@ -120,9 +120,8 @@ async fn attested_client_config(provider: Arc) -> ClientConfig { let mock_pcs_server = spawn_mock_pcs_server(MockPcsConfig::default()).await.unwrap(); let verifier = AttestationVerifier::mock_with_pccs(mock_pcs_server.base_url.clone()); - if let Some(ref pccs) = verifier.internal_pccs { - pccs.ready().await.unwrap(); - } + verifier.ready().await.unwrap(); + let verifier = AttestedCertificateVerifier::build(verifier) .with_crypto_provider(provider.clone()) .finish() From 7948199442536b6408e8fdf446ae9ef1ca61693f Mon Sep 17 00:00:00 2001 From: peg Date: Wed, 26 Aug 2026 08:23:46 +0200 Subject: [PATCH 5/9] Do not feature-gate an azure-specific option --- crates/attestation/src/lib.rs | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/crates/attestation/src/lib.rs b/crates/attestation/src/lib.rs index d86f2c0..42c5395 100644 --- a/crates/attestation/src/lib.rs +++ b/crates/attestation/src/lib.rs @@ -345,10 +345,10 @@ pub struct AttestationVerifier { measurement_policy: MeasurementPolicy, /// Whether to write quotes to files on disk dump_dcap_quotes: bool, - #[cfg(feature = "azure-verifier")] /// Whether to override outdated TCB when on Azure /// /// This provides a workaround for a known outdated FMSPC used by Azure + #[cfg_attr(not(feature = "azure-verifier"), allow(dead_code))] override_azure_outdated_tcb: bool, /// Internal cache for collateral internal_pccs: Option, @@ -365,7 +365,7 @@ pub struct AttestationVerifierBuilder { /// A PCCS service to use - defaults to Intel PCS pccs_url: Option, dump_dcap_quotes: bool, - #[cfg(feature = "azure-verifier")] + /// Whether to override outdated TCB when on Azure override_azure_outdated_tcb: bool, internal_pccs_prewarm: Option, } @@ -384,7 +384,8 @@ impl AttestationVerifierBuilder { /// Whether to override outdated TCB when on Azure /// /// This provides a workaround for a known outdated FMSPC used by Azure - #[cfg(feature = "azure-verifier")] + /// When `azure-verifier` is disabled, this option has no effect because + /// Azure attestations are not supported. pub fn override_azure_outdated_tcb(mut self) -> Self { self.override_azure_outdated_tcb = true; self @@ -429,7 +430,6 @@ impl AttestationVerifier { Self { measurement_policy: builder.measurement_policy, dump_dcap_quotes: builder.dump_dcap_quotes, - #[cfg(feature = "azure-verifier")] override_azure_outdated_tcb: builder.override_azure_outdated_tcb, internal_pccs, known_gcp_firmware: GcpFirmwareCache::new(), @@ -442,7 +442,6 @@ impl AttestationVerifier { measurement_policy, pccs_url: None, dump_dcap_quotes: false, - #[cfg(feature = "azure-verifier")] override_azure_outdated_tcb: false, internal_pccs_prewarm: Some(true), } @@ -454,7 +453,6 @@ impl AttestationVerifier { Self { measurement_policy: MeasurementPolicy::expect_none(), dump_dcap_quotes: false, - #[cfg(feature = "azure-verifier")] override_azure_outdated_tcb: false, internal_pccs: None, known_gcp_firmware: GcpFirmwareCache::new(), @@ -468,7 +466,6 @@ impl AttestationVerifier { Self { measurement_policy: MeasurementPolicy::mock(), dump_dcap_quotes: false, - #[cfg(feature = "azure-verifier")] override_azure_outdated_tcb: false, internal_pccs: None, known_gcp_firmware: GcpFirmwareCache::new(), @@ -482,7 +479,6 @@ impl AttestationVerifier { Self { measurement_policy: MeasurementPolicy::mock(), dump_dcap_quotes: false, - #[cfg(feature = "azure-verifier")] override_azure_outdated_tcb: false, internal_pccs: Some(Pccs::new(Some(pccs_url))), known_gcp_firmware: GcpFirmwareCache::new(), From 04d4fc726e3f45839992309b864f5e4c0e5a7668 Mon Sep 17 00:00:00 2001 From: peg Date: Wed, 26 Aug 2026 08:52:54 +0200 Subject: [PATCH 6/9] Improve builder api following review --- .../attestation-provider-server/src/main.rs | 15 +--- crates/attestation/src/gcp/provenance.rs | 2 +- crates/attestation/src/lib.rs | 83 +++++++++---------- 3 files changed, 43 insertions(+), 57 deletions(-) diff --git a/crates/attestation-provider-server/src/main.rs b/crates/attestation-provider-server/src/main.rs index 83fef20..88fb142 100644 --- a/crates/attestation-provider-server/src/main.rs +++ b/crates/attestation-provider-server/src/main.rs @@ -1,10 +1,7 @@ use std::{net::SocketAddr, path::PathBuf}; use attestation::{ - AttestationGenerator, - AttestationType, - AttestationVerifier, - measurements::MeasurementPolicy, + AttestationGenerator, AttestationType, AttestationVerifier, measurements::MeasurementPolicy, }; use attestation_provider_server::{attestation_provider_client, attestation_provider_server}; use clap::{Parser, Subcommand}; @@ -97,13 +94,9 @@ async fn main() -> anyhow::Result<()> { None => MeasurementPolicy::accept_anything(), }; - let mut attestation_verifier_builder = AttestationVerifier::builder(measurement_policy); - - if cli.log_dcap_quote { - attestation_verifier_builder = attestation_verifier_builder.dump_dcap_quotes(); - } - - let attestation_verifier = attestation_verifier_builder.build(); + let attestation_verifier = AttestationVerifier::builder(measurement_policy) + .with_dump_dcap_quotes(cli.log_dcap_quote) + .build(); let attestation_message = attestation_provider_client(server_addr, attestation_verifier).await?; diff --git a/crates/attestation/src/gcp/provenance.rs b/crates/attestation/src/gcp/provenance.rs index 56f7786..166fc03 100644 --- a/crates/attestation/src/gcp/provenance.rs +++ b/crates/attestation/src/gcp/provenance.rs @@ -254,7 +254,7 @@ pub enum GcpProvenanceError { #[cfg(test)] mod tests { use std::{ - io::{Read as _, Write as _}, + io::Write as _, net::SocketAddr, sync::mpsc, thread, diff --git a/crates/attestation/src/lib.rs b/crates/attestation/src/lib.rs index 42c5395..0d2e17f 100644 --- a/crates/attestation/src/lib.rs +++ b/crates/attestation/src/lib.rs @@ -338,6 +338,18 @@ impl AttestationGenerator { } } +/// How the verifier obtains DCAP collateral +#[derive(Clone, Debug)] +pub enum PccsMode { + /// No internal collateral cache. Collateral is always fetched from remote source. + None, + /// Internal cache pre-filled with all available collateral at build + /// time. + Prewarmed, + /// Internal cache that starts empty and fetches on demand. + Lazy, +} + /// Allows remote attestations to be verified #[derive(Clone, Debug)] pub struct AttestationVerifier { @@ -362,22 +374,36 @@ pub struct AttestationVerifier { pub struct AttestationVerifierBuilder { /// The measurement policy with accepted values and attestation types measurement_policy: MeasurementPolicy, + /// Internal PCCS setting + pccs_mode: PccsMode, /// A PCCS service to use - defaults to Intel PCS pccs_url: Option, dump_dcap_quotes: bool, /// Whether to override outdated TCB when on Azure override_azure_outdated_tcb: bool, - internal_pccs_prewarm: Option, } impl AttestationVerifierBuilder { pub fn build(self) -> AttestationVerifier { - AttestationVerifier::build(self) + let internal_pccs = match self.pccs_mode { + PccsMode::None => None, + PccsMode::Prewarmed => Some(Pccs::new(self.pccs_url)), + PccsMode::Lazy => Some(Pccs::new_without_prewarm(self.pccs_url)), + }; + + AttestationVerifier { + measurement_policy: self.measurement_policy, + dump_dcap_quotes: self.dump_dcap_quotes, + override_azure_outdated_tcb: self.override_azure_outdated_tcb, + internal_pccs, + known_gcp_firmware: GcpFirmwareCache::new(), + gcp_provenance_checker: GcpProvenanceChecker::new(), + } } /// Whether to write quotes to files on disk - pub fn dump_dcap_quotes(mut self) -> Self { - self.dump_dcap_quotes = true; + pub fn with_dump_dcap_quotes(mut self, dump_dcap_quotes: bool) -> Self { + self.dump_dcap_quotes = dump_dcap_quotes; self } @@ -386,64 +412,31 @@ impl AttestationVerifierBuilder { /// This provides a workaround for a known outdated FMSPC used by Azure /// When `azure-verifier` is disabled, this option has no effect because /// Azure attestations are not supported. - pub fn override_azure_outdated_tcb(mut self) -> Self { - self.override_azure_outdated_tcb = true; - self - } - - /// Do not keep an internal DCAP collateral cache - pub fn with_no_internal_pccs(mut self) -> Self { - self.internal_pccs_prewarm = None; + pub fn with_override_azure_outdated_tcb(mut self, override_azure_outdated_tcb: bool) -> Self { + self.override_azure_outdated_tcb = override_azure_outdated_tcb; self } - /// Keep a DCAP collateral cache, and pre-fill it with all available - /// collateral - pub fn with_pccs_prewarmed(mut self) -> Self { - self.internal_pccs_prewarm = Some(true); - self - } - - /// Keep a DCAP collateral cache, starting empty - pub fn with_pccs_not_prewarmed(mut self) -> Self { - self.internal_pccs_prewarm = Some(false); + pub fn with_pccs_mode(mut self, pccs_mode: PccsMode) -> Self { + self.pccs_mode = pccs_mode; self } /// Set the URL used by internal PCCS - pub fn pccs_url(mut self, pccs_url: String) -> Self { + pub fn with_pccs_url(mut self, pccs_url: String) -> Self { self.pccs_url = Some(pccs_url); self } } impl AttestationVerifier { - fn build(builder: AttestationVerifierBuilder) -> Self { - let internal_pccs = builder.internal_pccs_prewarm.map(|with_prewarm| { - if with_prewarm { - Pccs::new(builder.pccs_url) - } else { - Pccs::new_without_prewarm(builder.pccs_url) - } - }); - - Self { - measurement_policy: builder.measurement_policy, - dump_dcap_quotes: builder.dump_dcap_quotes, - override_azure_outdated_tcb: builder.override_azure_outdated_tcb, - internal_pccs, - known_gcp_firmware: GcpFirmwareCache::new(), - gcp_provenance_checker: GcpProvenanceChecker::new(), - } - } - pub fn builder(measurement_policy: MeasurementPolicy) -> AttestationVerifierBuilder { AttestationVerifierBuilder { measurement_policy, + pccs_mode: PccsMode::None, pccs_url: None, dump_dcap_quotes: false, override_azure_outdated_tcb: false, - internal_pccs_prewarm: Some(true), } } @@ -707,8 +700,8 @@ fn running_on_gcp() -> Result { let resp = agent.get(GCP_METADATA_API).call(); if let Ok(r) = resp { - return Ok(r.status() == 200 && - r.header("Metadata-Flavor").map(|v| v == "Google").unwrap_or(false)); + return Ok(r.status() == 200 + && r.header("Metadata-Flavor").map(|v| v == "Google").unwrap_or(false)); } Ok(false) From 81a2450180e0998da57e628de1d4eead6b95d992 Mon Sep 17 00:00:00 2001 From: peg Date: Wed, 26 Aug 2026 08:53:07 +0200 Subject: [PATCH 7/9] Fmt --- crates/attestation-provider-server/src/main.rs | 5 ++++- crates/attestation/src/lib.rs | 7 ++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/crates/attestation-provider-server/src/main.rs b/crates/attestation-provider-server/src/main.rs index 88fb142..84b2436 100644 --- a/crates/attestation-provider-server/src/main.rs +++ b/crates/attestation-provider-server/src/main.rs @@ -1,7 +1,10 @@ use std::{net::SocketAddr, path::PathBuf}; use attestation::{ - AttestationGenerator, AttestationType, AttestationVerifier, measurements::MeasurementPolicy, + AttestationGenerator, + AttestationType, + AttestationVerifier, + measurements::MeasurementPolicy, }; use attestation_provider_server::{attestation_provider_client, attestation_provider_server}; use clap::{Parser, Subcommand}; diff --git a/crates/attestation/src/lib.rs b/crates/attestation/src/lib.rs index 0d2e17f..2853712 100644 --- a/crates/attestation/src/lib.rs +++ b/crates/attestation/src/lib.rs @@ -341,7 +341,8 @@ impl AttestationGenerator { /// How the verifier obtains DCAP collateral #[derive(Clone, Debug)] pub enum PccsMode { - /// No internal collateral cache. Collateral is always fetched from remote source. + /// No internal collateral cache. Collateral is always fetched from + /// remote source. None, /// Internal cache pre-filled with all available collateral at build /// time. @@ -700,8 +701,8 @@ fn running_on_gcp() -> Result { let resp = agent.get(GCP_METADATA_API).call(); if let Ok(r) = resp { - return Ok(r.status() == 200 - && r.header("Metadata-Flavor").map(|v| v == "Google").unwrap_or(false)); + return Ok(r.status() == 200 && + r.header("Metadata-Flavor").map(|v| v == "Google").unwrap_or(false)); } Ok(false) From 929a7f801dabf664a146ba898d4b43e435942bcd Mon Sep 17 00:00:00 2001 From: peg Date: Thu, 27 Aug 2026 08:46:15 +0200 Subject: [PATCH 8/9] Apply suggestion from @samlaf Co-authored-by: Samuel Laferriere <9342524+samlaf@users.noreply.github.com> --- crates/attestation/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/attestation/src/lib.rs b/crates/attestation/src/lib.rs index 2853712..e8115e0 100644 --- a/crates/attestation/src/lib.rs +++ b/crates/attestation/src/lib.rs @@ -361,7 +361,6 @@ pub struct AttestationVerifier { /// Whether to override outdated TCB when on Azure /// /// This provides a workaround for a known outdated FMSPC used by Azure - #[cfg_attr(not(feature = "azure-verifier"), allow(dead_code))] override_azure_outdated_tcb: bool, /// Internal cache for collateral internal_pccs: Option, From 404aa14b1371711fc3042288d36a726d2f5cc7df Mon Sep 17 00:00:00 2001 From: peg Date: Thu, 27 Aug 2026 10:17:47 +0200 Subject: [PATCH 9/9] Suppress dead code warning for azure override field --- crates/attestation/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/attestation/src/lib.rs b/crates/attestation/src/lib.rs index e8115e0..2853712 100644 --- a/crates/attestation/src/lib.rs +++ b/crates/attestation/src/lib.rs @@ -361,6 +361,7 @@ pub struct AttestationVerifier { /// Whether to override outdated TCB when on Azure /// /// This provides a workaround for a known outdated FMSPC used by Azure + #[cfg_attr(not(feature = "azure-verifier"), allow(dead_code))] override_azure_outdated_tcb: bool, /// Internal cache for collateral internal_pccs: Option,