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
37 changes: 32 additions & 5 deletions crates/attestation/src/azure/ak_certificate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use std::time::Duration;
use once_cell::sync::Lazy;
use tokio_rustls::rustls::pki_types::{CertificateDer, TrustAnchor, UnixTime};
use webpki::EndEntityCert;
use x509_parser::prelude::{FromDer, X509Certificate};

use crate::azure::{MaaError, nv_index};

Expand All @@ -30,8 +31,8 @@ const AZURE_VIRTUAL_TPM_ROOT_2023: &str =
// Valid: 2025-04-24 to 2027-04-24
const GLOBAL_VIRTUAL_TPMCA03_PEM: &str = include_str!("../../assets/global-virtual-tpm-ca-03.pem");

/// The intermediate chain for azure
static GLOBAL_VIRTUAL_TPMCA03: Lazy<Vec<CertificateDer<'static>>> = Lazy::new(|| {
/// Documented intermediate certificates for Azure.
static DOCUMENTED_AZURE_INTERMEDIATES: Lazy<Vec<CertificateDer<'static>>> = Lazy::new(|| {
let (_type_label, cert_der) =
pem_rfc7468::decode_vec(GLOBAL_VIRTUAL_TPMCA03_PEM.as_bytes()).expect("Cannot decode PEM");
vec![CertificateDer::from(cert_der)]
Expand All @@ -49,18 +50,24 @@ static AZURE_ROOT_ANCHORS: Lazy<Vec<TrustAnchor<'static>>> = Lazy::new(|| {

/// Verify an AK certificate against azure root CA
pub(crate) fn verify_ak_cert_with_azure_roots(
ak_cert_der: &[u8],
ak_cert_der_chain: &[u8],
now_secs: u64,
) -> Result<(), MaaError> {
let ak_cert_der: CertificateDer = ak_cert_der.into();
let (remaining_bytes, _) = X509Certificate::from_der(ak_cert_der_chain)?;
let leaf_len = ak_cert_der_chain.len() - remaining_bytes.len();

let ak_cert_der = CertificateDer::from(ak_cert_der_chain[..leaf_len].to_vec());
let end_entity_cert = EndEntityCert::try_from(&ak_cert_der)?;

let mut intermediates = DOCUMENTED_AZURE_INTERMEDIATES.clone();
intermediates.extend(parse_trailing_der_certificates(remaining_bytes)?);
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.

Im not sure if we should still be always adding this hard coded intermediary.


let now = UnixTime::since_unix_epoch(Duration::from_secs(now_secs));

end_entity_cert.verify_for_usage(
webpki::ALL_VERIFICATION_ALGS,
&AZURE_ROOT_ANCHORS,
&GLOBAL_VIRTUAL_TPMCA03,
&intermediates,
now,
AnyEku,
None,
Expand All @@ -71,6 +78,26 @@ pub(crate) fn verify_ak_cert_with_azure_roots(
Ok(())
}

/// Parse DER certificates appended after the AK leaf cert
fn parse_trailing_der_certificates(
mut remaining_bytes: &[u8],
) -> Result<Vec<CertificateDer<'static>>, MaaError> {
let mut certificates = Vec::new();

while !remaining_bytes.is_empty() {
if remaining_bytes.iter().all(|byte| *byte == 0) {
break;
}

let (next_remaining_bytes, _) = X509Certificate::from_der(remaining_bytes)?;
let cert_len = remaining_bytes.len() - next_remaining_bytes.len();
certificates.push(CertificateDer::from(remaining_bytes[..cert_len].to_vec()));
remaining_bytes = next_remaining_bytes;
}

Ok(certificates)
}

/// Retrieve an AK certificate from the vTPM
pub(crate) fn read_ak_certificate_from_tpm() -> Result<Vec<u8>, tss_esapi::Error> {
tracing::debug!("Reading AK certificate from vTPM");
Expand Down
8 changes: 2 additions & 6 deletions crates/attestation/src/azure/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ fn finish_azure_attestation_verification(
let (_type_label, ak_certificate_der) =
pem_rfc7468::decode_vec(tpm_attestation.ak_certificate_pem.as_bytes())?;

let (remaining_bytes, ak_certificate) = X509Certificate::from_der(&ak_certificate_der)?;
let (_, ak_certificate) = X509Certificate::from_der(&ak_certificate_der)?;

// Check that AK public key matches that from TPM quote and HCL claims
let ak_from_certificate = RsaPubKey::from_certificate(&ak_certificate)?;
Expand All @@ -309,12 +309,8 @@ fn finish_azure_attestation_verification(
return Err(MaaError::AkFromClaimsNotEqualAkFromCertificate);
}

// Strip trailing data from AK certificate
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 was (i think) the issue - here we are potentially discarding intermediary certs which are needed for verification.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Don't think you are discarding them. Afaict they aren't there in the first place. At least they weren't in my NV reponse. That's why in your link they mention "The AIA mechanism is the supported and expected way to validate the certificate hierarchy." They want you to fetch the certs dynamically from the URLs embedded in the leaf AK cert.

let leaf_len = ak_certificate_der.len() - remaining_bytes.len();
let ak_certificate_der_without_trailing_data = &ak_certificate_der[..leaf_len];

// Verify the AK certificate against microsoft root cert
verify_ak_cert_with_azure_roots(ak_certificate_der_without_trailing_data, now)?;
verify_ak_cert_with_azure_roots(&ak_certificate_der, now)?;

Ok(MultiMeasurements::from_pcrs(pcrs))
}
Expand Down
Loading