From 9707d9a39f57f70bb52fead77364834c35767dd1 Mon Sep 17 00:00:00 2001 From: peg Date: Thu, 9 Jul 2026 08:08:03 +0200 Subject: [PATCH 1/2] 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 a2c05f1..9c23bc7 100644 --- a/crates/attestation/src/lib.rs +++ b/crates/attestation/src/lib.rs @@ -22,7 +22,7 @@ use pccs::{Pccs, PccsError}; use serde::{Deserialize, Serialize}; use thiserror::Error; -use crate::{dcap::DcapVerificationError, measurements::MeasurementPolicy}; +use crate::{dcap::DcapVerificationError, gcp::GcpFirmwareCache, measurements::MeasurementPolicy}; #[cfg(test)] static TEST_CRYPTO_PROVIDER: OnceLock<()> = OnceLock::new(); @@ -327,10 +327,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 @@ -343,21 +339,88 @@ pub struct AttestationVerifier { known_gcp_firmware: gcp::GcpFirmwareCache, } +/// 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: gcp::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: 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(), + } + } + + pub fn builder(measurement_policy: MeasurementPolicy) -> AttestationVerifierBuilder { + AttestationVerifierBuilder { measurement_policy, - pccs_url: pccs_url.clone(), - dump_dcap_quotes, - override_azure_outdated_tcb, - internal_pccs: Some(Pccs::new(pccs_url)), - known_gcp_firmware, + pccs_url: None, + dump_dcap_quotes: false, + override_azure_outdated_tcb: false, + internal_pccs_prewarm: Some(true), } } @@ -367,13 +430,13 @@ impl AttestationVerifier { 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 @@ -381,7 +444,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, @@ -394,7 +456,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, @@ -407,7 +468,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 ea6b96b41f606d3efc260d9a2f9dab8b87dba362 Mon Sep 17 00:00:00 2001 From: peg Date: Thu, 9 Jul 2026 08:47:55 +0200 Subject: [PATCH 2/2] 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 9c23bc7..fa92797 100644 --- a/crates/attestation/src/lib.rs +++ b/crates/attestation/src/lib.rs @@ -326,15 +326,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")] /// 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: gcp::GcpFirmwareCache, } @@ -346,6 +347,7 @@ pub struct AttestationVerifierBuilder { /// A PCCS service to use - defaults to Intel PCS pccs_url: Option, dump_dcap_quotes: bool, + #[cfg(feature = "azure")] override_azure_outdated_tcb: bool, internal_pccs_prewarm: Option, } @@ -364,6 +366,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")] pub fn override_azure_outdated_tcb(mut self) -> Self { self.override_azure_outdated_tcb = true; self @@ -408,6 +411,7 @@ impl AttestationVerifier { Self { measurement_policy: builder.measurement_policy, dump_dcap_quotes: builder.dump_dcap_quotes, + #[cfg(feature = "azure")] override_azure_outdated_tcb: builder.override_azure_outdated_tcb, internal_pccs, known_gcp_firmware: GcpFirmwareCache::new(), @@ -419,32 +423,19 @@ impl AttestationVerifier { measurement_policy, pccs_url: None, dump_dcap_quotes: false, + #[cfg(feature = "azure")] 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")] override_azure_outdated_tcb: false, internal_pccs: None, known_gcp_firmware: gcp::GcpFirmwareCache::new(), @@ -457,6 +448,7 @@ impl AttestationVerifier { Self { measurement_policy: MeasurementPolicy::mock(), dump_dcap_quotes: false, + #[cfg(feature = "azure")] override_azure_outdated_tcb: false, internal_pccs: None, known_gcp_firmware: gcp::GcpFirmwareCache::new(), @@ -469,6 +461,7 @@ impl AttestationVerifier { Self { measurement_policy: MeasurementPolicy::mock(), dump_dcap_quotes: false, + #[cfg(feature = "azure")] override_azure_outdated_tcb: false, internal_pccs: Some(Pccs::new(Some(pccs_url))), known_gcp_firmware: gcp::GcpFirmwareCache::new(), @@ -649,6 +642,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()