From 7ef08da3a55fbc4dc2c2b6c22ecb08882233ea9d Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Wed, 8 Jul 2026 03:15:17 -0700 Subject: [PATCH] verifier: report attestation mode --- verifier/README.md | 4 ++-- verifier/src/types.rs | 6 +++--- verifier/src/verification.rs | 21 +++++++++------------ 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/verifier/README.md b/verifier/README.md index 39b553c5b..92a6f8264 100644 --- a/verifier/README.md +++ b/verifier/README.md @@ -35,7 +35,7 @@ or "os_image_hash_verified": true, "os_image_is_dev": false, // true=dev image, false=prod, null=unknown/N/A "os_image_version": "0.5.10", // dstack OS version, null if unknown - "tee_platform": "tdx", // tdx | gcp-tdx | nitro + "attestation_mode": "dstack-tdx", // dstack-tdx | dstack-gcp-tdx | dstack-nitro-enclave | dstack-amd-sev-snp "report_data": "hex-encoded-64-byte-report-data", "tcb_status": "UpToDate", "advisory_ids": [], @@ -196,7 +196,7 @@ Beyond pass/fail, the result carries a few descriptive fields so a relying party - **`os_image_is_dev`** — `true` for a development OS image, `false` for production. Dev images are built for local testing and are not hardened for production use, so a relying party generally wants to reject them. - **`os_image_version`** — the dstack OS version (e.g. `0.5.10`), useful for enforcing a minimum version. -- **`tee_platform`** — which TEE produced the verified quote: `tdx`, `gcp-tdx`, or `nitro`. +- **`attestation_mode`** — the attestation mode that produced the verified quote, serialized as `AttestationMode`: `dstack-tdx`, `dstack-gcp-tdx`, `dstack-nitro-enclave`, or `dstack-amd-sev-snp`. - **`key_provider`** — the decoded `app_info.key_provider_info` (`{name, id}`); `name` is e.g. `kms` or `local`. A `local` key provider means the CVM is not KMS-backed, which is itself a dev/insecure posture signal. The raw bytes remain in `app_info.key_provider_info`. `os_image_is_dev` and `os_image_version` are read from the image's `metadata.json`, which is part of `sha256sum.txt` and therefore bound to the `os_image_hash` that step 3 verifies against the quote — so they are as trustworthy as the os-image-hash check itself. They are `null` when the platform does not expose them (e.g. GCP TDX / Nitro Enclave) or when the image predates the field (images without `is_dev` are always production). diff --git a/verifier/src/types.rs b/verifier/src/types.rs index 3acbfa1c5..85f80e302 100644 --- a/verifier/src/types.rs +++ b/verifier/src/types.rs @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 use dstack_types::KeyProviderInfo; -use ra_tls::attestation::AppInfo; +use ra_tls::attestation::{AppInfo, AttestationMode}; use serde::{Deserialize, Serialize}; use serde_human_bytes as serde_bytes; @@ -45,8 +45,8 @@ pub struct VerificationDetails { pub os_image_is_dev: Option, /// dstack OS version, from the same metadata.json. pub os_image_version: Option, - /// "tdx" | "gcp-tdx" | "nitro". - pub tee_platform: Option, + /// Attestation mode that produced the verified quote. + pub attestation_mode: Option, pub report_data: Option, pub tcb_status: Option, pub advisory_ids: Vec, diff --git a/verifier/src/verification.rs b/verifier/src/verification.rs index 92cddebf6..d9be64bf6 100644 --- a/verifier/src/verification.rs +++ b/verifier/src/verification.rs @@ -37,15 +37,6 @@ use crate::types::{ VerificationRequest, VerificationResponse, }; -fn tee_platform_name(quote: &AttestationQuote) -> &'static str { - match quote { - AttestationQuote::DstackTdx(_) => "tdx", - AttestationQuote::DstackGcpTdx(_) => "gcp-tdx", - AttestationQuote::DstackNitroEnclave(_) => "nitro", - AttestationQuote::DstackAmdSevSnp(_) => "sev-snp", - } -} - /// best-effort: None for empty/malformed blobs. fn decode_key_provider_info(bytes: &[u8]) -> Option { if bytes.is_empty() { @@ -560,7 +551,7 @@ impl CvmVerifier { let verified_attestation = match verified { Ok(att) => { details.quote_verified = true; - details.tee_platform = Some(tee_platform_name(&att.quote).to_string()); + details.attestation_mode = Some(att.quote.mode()); details.tcb_status = att.report.tdx_report().map(|r| r.status.clone()); details.advisory_ids = att .report @@ -1273,7 +1264,10 @@ mod tests { assert!(response.details.quote_verified); assert!(response.details.event_log_verified); assert!(response.details.os_image_hash_verified); - assert_eq!(response.details.tee_platform.as_deref(), Some("sev-snp")); + assert_eq!( + response.details.attestation_mode, + Some(ra_tls::attestation::AttestationMode::DstackAmdSevSnp) + ); assert!( !image_cache_dir.exists(), "SNP verification must not download or cache OS images" @@ -1298,6 +1292,9 @@ mod tests { let response = verifier.verify(request).await.expect("verifier runs"); assert!(response.is_valid, "{:?}", response.reason); - assert_eq!(response.details.tee_platform.as_deref(), Some("sev-snp")); + assert_eq!( + response.details.attestation_mode, + Some(ra_tls::attestation::AttestationMode::DstackAmdSevSnp) + ); } }