From de898d2fd9d4f99b2b8ed7fb88049cbc52158cc1 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Wed, 29 Jul 2026 02:12:34 +0000 Subject: [PATCH 1/3] fix(mock-attestation): serve legacy DCAP paths --- dstack/crates/mock-attestation/src/server.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dstack/crates/mock-attestation/src/server.rs b/dstack/crates/mock-attestation/src/server.rs index 19480b3e0..3d303db59 100644 --- a/dstack/crates/mock-attestation/src/server.rs +++ b/dstack/crates/mock-attestation/src/server.rs @@ -60,6 +60,10 @@ pub fn router(state: Arc) -> Router { Router::new() .route("/sgx/certification/v4/pckcrl", get(pck_crl)) .route("/sgx/certification/v4/rootcacrl", get(pccs_root_crl)) + // Older guest images use the SGX PCS prefix for shared DCAP collateral. + // Serve both spellings so simulated TDX evidence remains compatible. + .route("/sgx/certification/v4/tcb", get(tcb_info)) + .route("/sgx/certification/v4/qe/identity", get(qe_identity)) .route("/tdx/certification/v4/tcb", get(tcb_info)) .route("/tdx/certification/v4/qe/identity", get(qe_identity)) .route("/vcek/v1/Milan/cert_chain", get(sev_ca_chain)) From a96d311e0a46694a81458247eb84a0eacbf78291 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Wed, 29 Jul 2026 02:17:10 +0000 Subject: [PATCH 2/3] fix(mock-attestation): model the PCK CA chain --- dstack/crates/mock-attestation/src/tdx.rs | 75 ++++++++++++++++++----- 1 file changed, 58 insertions(+), 17 deletions(-) diff --git a/dstack/crates/mock-attestation/src/tdx.rs b/dstack/crates/mock-attestation/src/tdx.rs index f3eb703fe..09cbf070b 100644 --- a/dstack/crates/mock-attestation/src/tdx.rs +++ b/dstack/crates/mock-attestation/src/tdx.rs @@ -3,17 +3,17 @@ // SPDX-License-Identifier: Apache-2.0 use anyhow::Result; -use dcap_qvl::QuoteCollateralV3; use dcap_qvl::quote::{ AuthData, AuthDataV4, CertificationData, Data, EnclaveReport, Header, QEReportCertificationData, Quote, Report, TDReport10, }; -use p256::ecdsa::{Signature, SigningKey, signature::Signer}; +use dcap_qvl::QuoteCollateralV3; +use p256::ecdsa::{signature::Signer, Signature, SigningKey}; use p256::pkcs8::{DecodePrivateKey, EncodePrivateKey}; use rcgen::{ BasicConstraints, Certificate, CertificateParams, CertificateRevocationListParams, CertifiedKey, CustomExtension, DnType, ExtendedKeyUsagePurpose, IsCa, KeyIdMethod, KeyPair, - KeyUsagePurpose, PKCS_ECDSA_P256_SHA256, RemoteKeyPair, SerialNumber, SignatureAlgorithm, + KeyUsagePurpose, RemoteKeyPair, SerialNumber, SignatureAlgorithm, PKCS_ECDSA_P256_SHA256, }; use scale::Encode; use serde_json::json; @@ -30,8 +30,10 @@ const INTEL_QE_VENDOR_ID: [u8; 16] = [ pub struct TdxGenerator { root: Certificate, root_signing_key: SigningKey, + pck_ca: Certificate, pck: Certificate, pck_key: SigningKey, + pck_crl: Vec, tcb_signer: Certificate, tcb_signer_key: SigningKey, qe_signer: Certificate, @@ -88,12 +90,19 @@ impl TdxGenerator { }, root_signing_key, ) = make_root(&seed)?; + let (pck_ca, pck_ca_key) = make_ca( + "Mock Intel SGX PCK Platform CA", + "tdx-pck-ca", + &seed, + &root, + &root_key, + )?; let (pck, pck_key) = make_leaf( "Mock Intel SGX PCK Certificate", "tdx-pck", &seed, - &root, - &root_key, + &pck_ca, + &pck_ca_key, true, )?; let (tcb_signer, tcb_signer_key) = make_leaf( @@ -112,6 +121,17 @@ impl TdxGenerator { &root_key, false, )?; + let pck_crl = CertificateRevocationListParams { + this_update: fixed_time(MOCK_PKI_NOT_BEFORE)?, + next_update: fixed_time(MOCK_PKI_NOT_AFTER)?, + crl_number: SerialNumber::from(1u64), + issuing_distribution_point: None, + revoked_certs: Vec::new(), + key_identifier_method: KeyIdMethod::Sha256, + } + .signed_by(&pck_ca, &pck_ca_key)? + .der() + .to_vec(); let root_crl = CertificateRevocationListParams { this_update: fixed_time(MOCK_PKI_NOT_BEFORE)?, next_update: fixed_time(MOCK_PKI_NOT_AFTER)?, @@ -126,8 +146,10 @@ impl TdxGenerator { Ok(Self { root, root_signing_key, + pck_ca, pck, pck_key, + pck_crl, tcb_signer, tcb_signer_key, qe_signer, @@ -207,7 +229,8 @@ impl TdxGenerator { .map_err(|bytes: Vec| anyhow::anyhow!("invalid QE report size {}", bytes.len()))?; let qe_sig: Signature = self.pck_key.sign(&qe_report_bytes); - let pck_chain = format!("{}{}", self.pck.pem(), self.root.pem()).into_bytes(); + let pck_chain = + format!("{}{}{}", self.pck.pem(), self.pck_ca.pem(), self.root.pem()).into_bytes(); let qe_certification = QEReportCertificationData { qe_report: qe_report_bytes, qe_report_signature: qe_sig.to_bytes().into(), @@ -287,9 +310,9 @@ impl TdxGenerator { "isvprodid":1, "tcbLevels":[{"tcb":{"isvsvn":1},"tcbDate":issue,"tcbStatus":"UpToDate"}] }).to_string(); Ok(QuoteCollateralV3 { - pck_crl_issuer_chain: self.root.pem(), + pck_crl_issuer_chain: format!("{}{}", self.pck_ca.pem(), self.root.pem()), root_ca_crl: self.root_crl.clone(), - pck_crl: self.root_crl.clone(), + pck_crl: self.pck_crl.clone(), tcb_info_issuer_chain: format!("{}{}", self.tcb_signer.pem(), self.root.pem()), tcb_info_signature: sign_raw(&self.tcb_signer_key, tcb_info.as_bytes())?, tcb_info, @@ -322,6 +345,25 @@ fn make_root(seed: &[u8; 32]) -> Result<(CertifiedKey, SigningKey)> { Ok((CertifiedKey { cert, key_pair }, signing_key)) } +fn make_ca( + name: &str, + label: &str, + seed: &[u8; 32], + issuer: &Certificate, + issuer_key: &KeyPair, +) -> Result<(Certificate, KeyPair)> { + let (key, _) = deterministic_key_pair(seed, label)?; + let mut params = cert_params(name)?; + params.is_ca = IsCa::Ca(BasicConstraints::Unconstrained); + params.key_usages.extend([ + KeyUsagePurpose::DigitalSignature, + KeyUsagePurpose::KeyCertSign, + KeyUsagePurpose::CrlSign, + ]); + let cert = params.signed_by(&key, issuer, issuer_key)?; + Ok((cert, key)) +} + fn make_leaf( name: &str, label: &str, @@ -446,14 +488,13 @@ mod tests { ); let mut tampered = evidence.quote.clone(); tampered[100] ^= 1; - assert!( - verifier - .verify(&tampered, &evidence.collateral, now) - .is_err() - ); - assert!( - crate::ensure_report_data(&verified.report.as_td10().unwrap().report_data, &[0x24; 64]) - .is_err() - ); + assert!(verifier + .verify(&tampered, &evidence.collateral, now) + .is_err()); + assert!(crate::ensure_report_data( + &verified.report.as_td10().unwrap().report_data, + &[0x24; 64] + ) + .is_err()); } } From 1c9d6f7833e5ba3f9a46b3e3f52e8a0285080fc2 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Wed, 29 Jul 2026 02:18:04 +0000 Subject: [PATCH 3/3] fix(mock-attestation): serve the PCK issuer CRL --- dstack/crates/mock-attestation/src/server.rs | 7 +++++-- dstack/crates/mock-attestation/src/tdx.rs | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/dstack/crates/mock-attestation/src/server.rs b/dstack/crates/mock-attestation/src/server.rs index 3d303db59..9dbb6a151 100644 --- a/dstack/crates/mock-attestation/src/server.rs +++ b/dstack/crates/mock-attestation/src/server.rs @@ -94,8 +94,11 @@ async fn pccs_root_crl(State(state): State>) -> impl In async fn pck_crl(State(state): State>) -> impl IntoResponse { binary( - state.tdx.root_crl_der(), - Some(("SGX-PCK-CRL-Issuer-Chain", state.tdx.root_ca_pem())), + state.tdx.pck_crl_der(), + Some(( + "SGX-PCK-CRL-Issuer-Chain", + state.tdx.sample_collateral().unwrap().pck_crl_issuer_chain, + )), ) } diff --git a/dstack/crates/mock-attestation/src/tdx.rs b/dstack/crates/mock-attestation/src/tdx.rs index 09cbf070b..d7bd146d0 100644 --- a/dstack/crates/mock-attestation/src/tdx.rs +++ b/dstack/crates/mock-attestation/src/tdx.rs @@ -179,6 +179,10 @@ impl TdxGenerator { self.root_crl.clone() } + pub fn pck_crl_der(&self) -> Vec { + self.pck_crl.clone() + } + pub fn attest(&self, report_data: [u8; 64]) -> Result { self.attest_with_rtmrs( report_data,