Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions crates/attestation-provider-server/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::net::SocketAddr;

pub use attestation::AttestationGenerator;
use attestation::{AttestationError, AttestationExchangeMessage, AttestationVerifier};
use attestation::{AttestationError, AttestationExchangeMessage, AttestationVerifier, VerifyMode};
use axum::{
extract::{Path, State},
http::StatusCode,
Expand Down Expand Up @@ -61,7 +61,9 @@ pub async fn attestation_provider_client(

println!("Remote attestation type: {remote_attestation_type}");

attestation_verifier.verify_attestation(remote_attestation_message.clone(), input_data).await?;
attestation_verifier
.verify_attestation(remote_attestation_message.clone(), input_data, VerifyMode::Live)
.await?;

Ok(remote_attestation_message)
}
Expand Down
5 changes: 4 additions & 1 deletion crates/attestation/src/azure/attester/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ use super::{
ak_certificate::verify_ak_cert_with_azure_roots,
ensure_azure_attestation_payload_size,
tpm_quote::TpmQuote,
unix_time_now_secs,
};

/// Used in attestation type detection to check if we are on Azure
Expand Down Expand Up @@ -151,6 +150,10 @@ impl TryFrom<&vtpm::Quote> for TpmQuote {
}
}

fn unix_time_now_secs() -> Result<u64, MaaError> {
Ok(std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?.as_secs())
}

/// Fetch intermediate certificates from the Authority Information Access
/// (AIA) CA Issuers URLs in the leaf and each fetched intermediate.
///
Expand Down
4 changes: 0 additions & 4 deletions crates/attestation/src/azure/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,6 @@ where
Ok(certificates)
}

fn unix_time_now_secs() -> Result<u64, MaaError> {
Ok(std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?.as_secs())
}

/// An error when generating or verifying a Microsoft Azure vTPM attestation
/// (MAA is short for Microsoft Azure Attestation)
#[derive(Error, Debug)]
Expand Down
154 changes: 49 additions & 105 deletions crates/attestation/src/azure/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
//! chain verification against pinned Azure roots.
use az_cvm_vtpm::{hcl, tdx};
use base64::{Engine as _, engine::general_purpose::URL_SAFE as BASE64_URL_SAFE};
use dcap_qvl::QuoteCollateralV3;
use num_bigint::BigUint;
use openssl::pkey::PKey;
use pccs::Pccs;
Expand All @@ -17,13 +16,13 @@ use super::{
TpmAttest,
ak_certificate::verify_ak_cert_with_azure_roots,
ensure_azure_attestation_payload_size,
unix_time_now_secs,
};
use crate::{
VerifiedAttestation,
VerifyMode,
dcap::{
verify_dcap_attestation_with_given_timestamp,
verify_dcap_attestation_with_timestamp_sync,
verify_dcap_attestation_with_tcb_override,
verify_dcap_attestation_with_tcb_override_sync,
},
measurements::MultiMeasurements,
};
Expand All @@ -39,57 +38,17 @@ struct PreparedAzureAttestation {
}

/// Verify a TDX attestation from Azure
pub async fn verify_azure_attestation(
input: Vec<u8>,
expected_input_data: [u8; 64],
pccs: Option<Pccs>,
override_azure_outdated_tcb: bool,
) -> Result<VerifiedAttestation, MaaError> {
let now = unix_time_now_secs()?;

verify_azure_attestation_with_given_timestamp(
input,
expected_input_data,
pccs,
None,
now,
override_azure_outdated_tcb,
)
.await
}

/// Verify a TDX attestation from Azure - synchronous version
///
/// This relies on having DCAP collateral already present in the cache
///
/// If possible, prefer the async version
pub fn verify_azure_attestation_sync(
input: Vec<u8>,
expected_input_data: [u8; 64],
pccs: Pccs,
override_azure_outdated_tcb: bool,
) -> Result<VerifiedAttestation, MaaError> {
let now = unix_time_now_secs()?;

verify_azure_attestation_with_given_timestamp_sync(
input,
expected_input_data,
pccs,
None,
now,
override_azure_outdated_tcb,
)
}

/// Do the verification, passing in the current time
/// This allows us to test this function without time checks going out of
/// date
async fn verify_azure_attestation_with_given_timestamp(
/// `mode` gates the DCAP leg and the vTPM leg alike: on
/// [VerifyMode::Archived] the AK certificate chain is checked as of the
/// same instant as the collateral, and nothing reaches the network.
/// `pccs` only matters on [VerifyMode::Live]; see
/// [crate::dcap::verify_dcap_attestation].
pub async fn verify_azure_attestation(
input: Vec<u8>,
expected_input_data: [u8; 64],
mode: VerifyMode,
pccs: Option<Pccs>,
collateral: Option<QuoteCollateralV3>,
now: u64,
override_azure_outdated_tcb: bool,
) -> Result<VerifiedAttestation, MaaError> {
let PreparedAzureAttestation {
Expand All @@ -100,37 +59,44 @@ async fn verify_azure_attestation_with_given_timestamp(
tpm_attestation,
} = prepare_azure_attestation(input)?;

// Only the endorsements travel upward: this platform is judged on the
// vTPM PCRs, not the TD quote
let (dcap, _) = verify_dcap_attestation_with_given_timestamp(
// The DCAP leg reports the instant it evaluated at, so the vTPM leg
// below is held to the same one - on [VerifyMode::Live] the clock
// is read once, not once per leg. Only the endorsements travel
// upward: this platform is judged on the vTPM PCRs, not the TD
// quote
let (dcap, _) = verify_dcap_attestation_with_tcb_override(
tdx_quote_bytes,
expected_tdx_input_data,
mode,
pccs,
collateral,
now,
override_azure_outdated_tcb,
)
.await?;

// The vTPM leg fetches nothing AK chain in the evidence, roots
// compiled in so it adds no endorsements of its own
// The vTPM leg fetches nothing - AK chain in the evidence, roots
// compiled in - so it adds no endorsements of its own
let measurements = finish_azure_attestation_verification(
hcl_report,
var_data_hash,
tpm_attestation,
expected_input_data,
now,
dcap.endorsements.at,
)?;
Ok(VerifiedAttestation { measurements, endorsements: dcap.endorsements })
}

/// Synchronous version of the verifier
fn verify_azure_attestation_with_given_timestamp_sync(
/// Verify a TDX attestation from Azure - synchronous version
///
/// `pccs` only matters on [VerifyMode::Live], and then the collateral has
/// to be in its cache already; see
/// [crate::dcap::verify_dcap_attestation_sync].
///
/// If possible, prefer the async version
pub fn verify_azure_attestation_sync(
input: Vec<u8>,
expected_input_data: [u8; 64],
mode: VerifyMode,
pccs: Pccs,
collateral: Option<QuoteCollateralV3>,
now: u64,
override_azure_outdated_tcb: bool,
) -> Result<VerifiedAttestation, MaaError> {
let PreparedAzureAttestation {
Expand All @@ -141,12 +107,11 @@ fn verify_azure_attestation_with_given_timestamp_sync(
tpm_attestation,
} = prepare_azure_attestation(input)?;

let (dcap, _) = verify_dcap_attestation_with_timestamp_sync(
let (dcap, _) = verify_dcap_attestation_with_tcb_override_sync(
tdx_quote_bytes,
expected_tdx_input_data,
mode,
pccs,
collateral,
now,
override_azure_outdated_tcb,
)?;

Expand All @@ -155,7 +120,7 @@ fn verify_azure_attestation_with_given_timestamp_sync(
var_data_hash,
tpm_attestation,
expected_input_data,
now,
dcap.endorsements.at,
)?;
Ok(VerifiedAttestation { measurements, endorsements: dcap.endorsements })
}
Expand Down Expand Up @@ -386,43 +351,24 @@ mod tests {
}

/// All verification entry points must reject an oversized payload, and
/// must do so before attempting DCAP verification (no collateral or
/// usable PCCS is provided here).
/// must do so before attempting DCAP verification. [VerifyMode::Live]
/// with no PCCS is the strict case: were the size gate to miss, the
/// verification would reach out to Intel.
#[tokio::test]
async fn verify_rejects_oversized_payload_before_deserialize() {
let actual = MAX_AZURE_ATTESTATION_PAYLOAD_SIZE + 1;
let input = vec![b'{'; actual];

let err = verify_azure_attestation(input.clone(), [0; 64], None, false).await.unwrap_err();
let err = verify_azure_attestation(input.clone(), [0; 64], VerifyMode::Live, None, false)
.await
.unwrap_err();
assert_payload_too_large(err, actual);

let err = verify_azure_attestation_sync(
input.clone(),
[0; 64],
Pccs::new_without_prewarm(None),
false,
)
.unwrap_err();
assert_payload_too_large(err, actual);

let err = verify_azure_attestation_with_given_timestamp(
input.clone(),
[0; 64],
None,
None,
0,
false,
)
.await
.unwrap_err();
assert_payload_too_large(err, actual);

let err = verify_azure_attestation_with_given_timestamp_sync(
input,
[0; 64],
VerifyMode::Live,
Pccs::new_without_prewarm(None),
None,
0,
false,
)
.unwrap_err();
Expand Down Expand Up @@ -470,12 +416,11 @@ mod tests {
let VerifiedAttestation {
measurements: async_measurements,
endorsements: async_endorsements,
} = verify_azure_attestation_with_given_timestamp(
} = verify_azure_attestation(
attestation_json.clone(),
[0; 64],
VerifyMode::Archived(EndorsementSnapshot::dcap(fixture_collateral.clone(), now)),
None,
Some(fixture_collateral.clone()),
now,
false,
)
.await
Expand All @@ -484,20 +429,20 @@ mod tests {
let VerifiedAttestation {
measurements: sync_measurements,
endorsements: sync_endorsements,
} = verify_azure_attestation_with_given_timestamp_sync(
} = verify_azure_attestation_sync(
attestation_json,
[0; 64],
VerifyMode::Archived(EndorsementSnapshot::dcap(fixture_collateral.clone(), now)),
Pccs::new_without_prewarm(None),
Some(fixture_collateral.clone()),
now,
false,
)
.unwrap();

assert_eq!(async_measurements, sync_measurements);
// The bundle handed back is the one the DCAP leg consumed, which is
// what makes archiving it provenance rather than a second copy, and
// it arrives paired with the instant both legs were held to
// The bundle handed back is the one the verification consumed,
// which is what makes archiving it provenance rather than a
// second copy, and it arrives paired with the instant it
// was held to
let expected = EndorsementSnapshot::dcap(fixture_collateral, now);
assert_eq!(async_endorsements, expected);
assert_eq!(sync_endorsements, expected);
Expand All @@ -520,12 +465,11 @@ mod tests {
)
.unwrap();

let err = verify_azure_attestation_with_given_timestamp(
let err = verify_azure_attestation(
attestation_json,
expected_input_data,
VerifyMode::Archived(EndorsementSnapshot::dcap(collateral, now)),
None,
Some(collateral),
now,
false,
)
.await
Expand Down
Loading
Loading