diff --git a/crates/attestation-provider-server/src/main.rs b/crates/attestation-provider-server/src/main.rs index e4d2b31..84b2436 100644 --- a/crates/attestation-provider-server/src/main.rs +++ b/crates/attestation-provider-server/src/main.rs @@ -97,8 +97,9 @@ async fn main() -> anyhow::Result<()> { None => MeasurementPolicy::accept_anything(), }; - let attestation_verifier = - AttestationVerifier::new(measurement_policy, None, cli.log_dcap_quote, false); + 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 cb67f63..2853712 100644 --- a/crates/attestation/src/lib.rs +++ b/crates/attestation/src/lib.rs @@ -338,61 +338,107 @@ 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 { /// 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, + measurement_policy: MeasurementPolicy, /// Whether to write quotes to files on disk - pub dump_dcap_quotes: bool, + dump_dcap_quotes: bool, /// 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, + #[cfg_attr(not(feature = "azure-verifier"), allow(dead_code))] + 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 gcp_provenance_checker: GcpProvenanceChecker, } -impl AttestationVerifier { - fn build( - measurement_policy: MeasurementPolicy, - pccs_url: Option, - dump_dcap_quotes: bool, - override_azure_outdated_tcb: bool, - known_gcp_firmware: GcpFirmwareCache, - ) -> Self { - 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, +/// Options used to construct an [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, +} + +impl AttestationVerifierBuilder { + pub fn build(self) -> AttestationVerifier { + 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(), } } - pub fn new( - measurement_policy: MeasurementPolicy, - pccs_url: Option, - dump_dcap_quotes: bool, - override_azure_outdated_tcb: bool, - ) -> Self { - Self::build( + /// Whether to write quotes to files on disk + pub fn with_dump_dcap_quotes(mut self, dump_dcap_quotes: bool) -> Self { + self.dump_dcap_quotes = dump_dcap_quotes; + self + } + + /// Whether to override outdated TCB when on Azure + /// + /// 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 with_override_azure_outdated_tcb(mut self, override_azure_outdated_tcb: bool) -> Self { + self.override_azure_outdated_tcb = override_azure_outdated_tcb; + self + } + + 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 with_pccs_url(mut self, pccs_url: String) -> Self { + self.pccs_url = Some(pccs_url); + self + } +} + +impl AttestationVerifier { + pub fn builder(measurement_policy: MeasurementPolicy) -> AttestationVerifierBuilder { + AttestationVerifierBuilder { measurement_policy, - pccs_url, - dump_dcap_quotes, - override_azure_outdated_tcb, - gcp::GcpFirmwareCache::new(), - ) + pccs_mode: PccsMode::None, + pccs_url: None, + dump_dcap_quotes: false, + override_azure_outdated_tcb: false, + } } /// Create an [AttestationVerifier] which will only allow no attestation @@ -400,7 +446,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 +459,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 +472,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))), @@ -619,6 +662,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()