From 7d6f4496b27b45d8c97ebee90c57d14a8cfae194 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Wed, 5 Aug 2026 09:24:26 -0700 Subject: [PATCH 1/2] refactor(ra-tls): separate attestation from issuer claims --- dstack/ra-tls/src/attestation.rs | 52 ++++++++++++++++++-------------- dstack/verifier/src/main.rs | 4 +-- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/dstack/ra-tls/src/attestation.rs b/dstack/ra-tls/src/attestation.rs index 6d120020a..b97769b63 100644 --- a/dstack/ra-tls/src/attestation.rs +++ b/dstack/ra-tls/src/attestation.rs @@ -9,22 +9,25 @@ pub use dstack_attest::attestation::*; use crate::{oids, traits::CertExt}; use anyhow::{bail, Context, Result}; -/// Verified RA-TLS certificate evidence. +/// Attestation evidence verified and bound to an RA-TLS certificate key. /// -/// This is the certificate-level endpoint identity proof: the embedded dstack -/// attestation has been cryptographically verified and its `report_data` is -/// bound to the certificate SubjectPublicKeyInfo. -pub struct VerifiedRaTlsCert { - /// DER-encoded SubjectPublicKeyInfo from the verified certificate. +/// This type proves only the embedded attestation and its binding to the +/// certificate SubjectPublicKeyInfo. It does not authenticate the certificate +/// issuer or any other certificate extension. Application identity asserted by +/// a KMS-issued certificate is an issuer claim and must be consumed only after +/// normal certificate-chain verification. +pub struct VerifiedRaTlsAttestation { + /// DER-encoded SubjectPublicKeyInfo bound to the attestation. pub public_key_der: Vec, /// Verified dstack attestation embedded in the certificate. pub attestation: VerifiedAttestation, - /// Optional app id certificate extension. - pub app_id: Option>, - /// Optional app info certificate extension. - pub app_info: Option, - /// Optional dstack certificate usage extension. - pub special_usage: Option, +} + +impl VerifiedRaTlsAttestation { + /// Decode application identity from the verified attestation evidence. + pub fn decode_app_info(&self, allow_dummy: bool) -> Result { + self.attestation.decode_app_info(allow_dummy) + } } /// Extract attestation from x509 certificate @@ -41,14 +44,25 @@ pub fn from_der(cert: &[u8]) -> Result> { /// DER SubjectPublicKeyInfo. That binding prevents an operator-controlled /// network endpoint from reusing valid attestation evidence with a different /// TLS key. -pub async fn verify_der(cert: &[u8], verifier: &AttestationVerifier) -> Result { +/// +/// This function does not verify a certificate chain and deliberately does not +/// return non-attestation certificate extensions. KMS-issued certificates may +/// omit attestation entirely; authenticate their issuer claims through the +/// certificate chain instead. +pub async fn verify_der( + cert: &[u8], + verifier: &AttestationVerifier, +) -> Result { let (_, cert) = x509_parser::parse_x509_certificate(cert).context("failed to parse certificate")?; verify_cert(&cert, verifier).await } /// Verify the RA-TLS attestation embedded in a PEM-encoded X.509 certificate. -pub async fn verify_pem(cert: &[u8], verifier: &AttestationVerifier) -> Result { +pub async fn verify_pem( + cert: &[u8], + verifier: &AttestationVerifier, +) -> Result { let (_, pem) = x509_parser::pem::parse_x509_pem(cert).context("failed to parse PEM")?; verify_der(&pem.contents, verifier).await } @@ -57,26 +71,20 @@ pub async fn verify_pem(cert: &[u8], verifier: &AttestationVerifier) -> Result, verifier: &AttestationVerifier, -) -> Result { +) -> Result { let attestation = from_cert(cert)?.context("RA-TLS attestation extension missing")?; let public_key_der = cert.tbs_certificate.public_key().raw.to_vec(); if public_key_der.is_empty() { bail!("certificate SubjectPublicKeyInfo is empty"); } - let app_id = cert.get_app_id()?; - let app_info = cert.get_app_info()?; - let special_usage = cert.get_special_usage()?; let attestation = attestation .into_v1() .verify_with_ra_pubkey(&public_key_der, verifier) .await .context("RA-TLS attestation verification failed")?; - Ok(VerifiedRaTlsCert { + Ok(VerifiedRaTlsAttestation { public_key_der, attestation, - app_id, - app_info, - special_usage, }) } diff --git a/dstack/verifier/src/main.rs b/dstack/verifier/src/main.rs index 47e49d299..102d7cb5b 100644 --- a/dstack/verifier/src/main.rs +++ b/dstack/verifier/src/main.rs @@ -220,7 +220,7 @@ async fn run_cert_oneshot(file_path: &str, config: &Config) -> anyhow::Result<() .await .map_err(|e| anyhow::anyhow!("failed to verify RA-TLS certificate: {:#}", e))?; - let app_info = verified.attestation.decode_app_info(false).ok(); + let app_info = verified.decode_app_info(false).ok(); // Bind the reported os_image_hash to the attested boot measurement. For // every platform except TDX legacy this is a self-contained check (no image // download); relying parties should only trust `os_image_hash` when @@ -233,8 +233,6 @@ async fn run_cert_oneshot(file_path: &str, config: &Config) -> anyhow::Result<() "tee_variant": verified.attestation.quote.variant(), "report_data": hex::encode(verified.attestation.report_data), "public_key_der": hex::encode(&verified.public_key_der), - "app_id_extension": verified.app_id.as_ref().map(hex::encode), - "special_usage": verified.special_usage, "app_info": app_info.map(|info| serde_json::json!({ "app_id": hex::encode(info.app_id), "compose_hash": hex::encode(info.compose_hash), From 9c6525ff98f67ccca75b2d9a89b52f8a016f5c4a Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Wed, 5 Aug 2026 18:06:26 -0700 Subject: [PATCH 2/2] fix(mock-attestation): handle collateral generation errors --- dstack/crates/mock-attestation/src/server.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dstack/crates/mock-attestation/src/server.rs b/dstack/crates/mock-attestation/src/server.rs index 9dbb6a151..1b29be459 100644 --- a/dstack/crates/mock-attestation/src/server.rs +++ b/dstack/crates/mock-attestation/src/server.rs @@ -93,12 +93,12 @@ async fn pccs_root_crl(State(state): State>) -> impl In } async fn pck_crl(State(state): State>) -> impl IntoResponse { + let Ok(collateral) = state.tdx.sample_collateral() else { + return StatusCode::INTERNAL_SERVER_ERROR.into_response(); + }; binary( state.tdx.pck_crl_der(), - Some(( - "SGX-PCK-CRL-Issuer-Chain", - state.tdx.sample_collateral().unwrap().pck_crl_issuer_chain, - )), + Some(("SGX-PCK-CRL-Issuer-Chain", collateral.pck_crl_issuer_chain)), ) }