Skip to content
Merged
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
8 changes: 4 additions & 4 deletions docs/aws-ec2-production-verifier-runbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ jq -e \
.is_valid == true and
.details.quote_verified == true and
.details.report_data == $expected_report_data and
.details.boot_info.attestationMode == "dstack-aws-nitro-tpm" and
.details.boot_info.teeVariant == "dstack-aws-nitro-tpm" and
.details.boot_info.tcbStatus == "UpToDate" and
.details.boot_info.osImageHash == $expected_os_image_hash
' request.json.verification.json
Expand Down Expand Up @@ -226,9 +226,9 @@ key_provider_id })` and compare it to PCR8. dstack's own verifier and KMS do
**not** check PCR8 — they rely on PCR14 replay — so it is not part of the
required policy above.

The `attestationMode` field is carried in the verified boot info for
The `teeVariant` field is carried in the verified boot info for
observability; a relying party may additionally assert
`attestationMode == "dstack-aws-nitro-tpm"` against the verifier output, but the
`teeVariant == "dstack-aws-nitro-tpm"` against the verifier output, but the
authorization contract itself keys on the standard fields above.

Same-account AWS KMS is not a trusted secret authority in this threat model if
Expand Down Expand Up @@ -261,7 +261,7 @@ Require the generated `endpoint-cert.pem.ratls-verification.json` to contain:
```bash
jq -e '
.is_valid == true and
.details.attestation_mode == "dstack-aws-nitro-tpm" and
.details.tee_variant == "dstack-aws-nitro-tpm" and
.details.app_info.os_image_hash_verified == true
' endpoint-cert.pem.ratls-verification.json
```
Expand Down
2 changes: 1 addition & 1 deletion dstack/crates/dstack-auth/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,7 @@ mod tests {
#[test]
fn deserializes_the_kms_bootinfo_wire_contract() {
let wire = r#"{
"attestationMode": "dstack",
"teeVariant": "dstack",
"mrAggregated": "0xAABB",
"osImageHash": "0xC2AA",
"mrSystem": "0xdead",
Expand Down
20 changes: 10 additions & 10 deletions dstack/dstack-attest/src/attestation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ fn has_sev_snp_tsm_provider() -> bool {
false
}

fn choose_dstack_attestation_mode(has_tdx: bool, has_sev_snp: bool) -> Result<TeeVariant> {
fn choose_dstack_tee_variant(has_tdx: bool, has_sev_snp: bool) -> Result<TeeVariant> {
if has_tdx {
return Ok(TeeVariant::DstackTdx);
}
Expand All @@ -468,7 +468,7 @@ pub fn detect_tee_variant() -> Result<TeeVariant> {
// First, try to detect platform from DMI product name
let platform = Platform::detect_or_dstack();
match platform {
Platform::Dstack => choose_dstack_attestation_mode(has_tdx, has_sev_snp),
Platform::Dstack => choose_dstack_tee_variant(has_tdx, has_sev_snp),
Platform::Gcp => {
// GCP platform: TDX + TPM dual mode
if has_tdx {
Expand Down Expand Up @@ -1286,7 +1286,7 @@ pub enum AttestationQuote {
}

impl AttestationQuote {
pub fn mode(&self) -> TeeVariant {
pub fn variant(&self) -> TeeVariant {
match self {
AttestationQuote::DstackTdx(_) => TeeVariant::DstackTdx,
AttestationQuote::DstackAmdSevSnp(_) => TeeVariant::DstackAmdSevSnp,
Expand All @@ -1303,7 +1303,7 @@ mod compatibility_tests {
use scale::Encode;

#[test]
fn attestation_mode_scale_discriminants_preserve_existing_wire_values() {
fn tee_variant_scale_discriminants_preserve_existing_wire_values() {
assert_eq!(TeeVariant::DstackTdx.encode(), vec![0]);
assert_eq!(TeeVariant::DstackGcpTdx.encode(), vec![1]);
assert_eq!(TeeVariant::DstackNitroEnclave.encode(), vec![2]);
Expand All @@ -1312,7 +1312,7 @@ mod compatibility_tests {
}

#[test]
fn attestation_mode_deserializes_canonical_names() {
fn tee_variant_deserializes_canonical_names() {
let parse = |value| serde_json::from_str::<TeeVariant>(value).unwrap();
assert_eq!(parse(r#""dstack-tdx""#), TeeVariant::DstackTdx);
assert_eq!(parse(r#""dstack-gcp-tdx""#), TeeVariant::DstackGcpTdx);
Expand Down Expand Up @@ -1360,17 +1360,17 @@ mod compatibility_tests {
}

#[test]
fn dstack_attestation_mode_prefers_tdx_when_both_tdx_and_tsm_exist() {
fn dstack_tee_variant_prefers_tdx_when_both_tdx_and_tsm_exist() {
assert_eq!(
choose_dstack_attestation_mode(true, true).unwrap(),
choose_dstack_tee_variant(true, true).unwrap(),
TeeVariant::DstackTdx
);
}

#[test]
fn dstack_attestation_mode_uses_snp_when_only_snp_exists() {
fn dstack_tee_variant_uses_snp_when_only_snp_exists() {
assert_eq!(
choose_dstack_attestation_mode(false, true).unwrap(),
choose_dstack_tee_variant(false, true).unwrap(),
TeeVariant::DstackAmdSevSnp
);
}
Expand Down Expand Up @@ -2365,7 +2365,7 @@ impl Attestation {
let report = verifier.gcp_tpm.fetch_and_verify(quote).await?;
let pcr_ind = self
.quote
.mode()
.variant()
.tpm_event_pcr_and_bank()
.map(|(pcr, _)| pcr)
.context("Failed to get event PCR no")?;
Expand Down
4 changes: 2 additions & 2 deletions dstack/dstack-util/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -450,7 +450,7 @@ fn cmd_attest_info(args: AttestInfoArgs) -> Result<()> {
match attestation {
VersionedAttestation::V0 { attestation } => {
println!("version: V0");
println!("mode: {:?}", attestation.quote.mode());
println!("mode: {:?}", attestation.quote.variant());
println!("config_bytes: {}", attestation.config.len());
match attestation.tdx_quote() {
Some(tdx) => {
Expand Down Expand Up @@ -495,7 +495,7 @@ fn cmd_attest_json(args: AttestJsonArgs) -> Result<()> {

let json = match attestation {
VersionedAttestation::V0 { attestation } => {
let mode = attestation.quote.mode().as_str();
let mode = attestation.quote.variant().as_str();
let tdx_quote = match attestation.tdx_quote() {
Some(tdx) => serde_json::json!({
"quote": hex::encode(&tdx.quote),
Expand Down
2 changes: 1 addition & 1 deletion dstack/kms/auth-simple/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ App boot authorization.
**Request:**
```json
{
"attestationMode": "dstack-tdx",
"teeVariant": "dstack-tdx",
"mrAggregated": "0x...",
"osImageHash": "0x...",
"appId": "0x...",
Expand Down
4 changes: 2 additions & 2 deletions dstack/kms/auth-simple/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import app from './index';
const TEST_CONFIG_PATH = './test-auth-config.json';

const baseBootInfo = {
attestationMode: 'dstack-tdx',
teeVariant: 'dstack-tdx',
mrAggregated: '0xabc123',
osImageHash: '0x1fbb0cf9cc6cfbf23d6b779776fabad2c5403d643badb9e5e238615e4960a78a',
appId: '0xapp123',
Expand Down Expand Up @@ -106,7 +106,7 @@ describe('auth-simple', () => {

const sevSnpBootInfo = {
...baseBootInfo,
attestationMode: 'dstack-amd-sev-snp',
teeVariant: 'dstack-amd-sev-snp',
tcbStatus: 'OutOfDate'
};
const denied = await app.fetch(new Request('http://localhost/bootAuth/kms', {
Expand Down
2 changes: 1 addition & 1 deletion dstack/kms/auth-simple/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { readFileSync, existsSync } from 'fs';

// zod schemas for validation - compatible with auth-eth implementation
const BootInfoSchema = z.object({
attestationMode: z.string().optional().default(''),
teeVariant: z.string().optional().default(''),
mrAggregated: z.string().describe('aggregated MR measurement'),
osImageHash: z.string().describe('OS Image hash'),
appId: z.string().describe('application ID'),
Expand Down
4 changes: 2 additions & 2 deletions dstack/kms/rpc/proto/kms_rpc.proto
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ message AttestationInfoResponse {
bytes mr_aggregated = 2;
// OS image hash
bytes os_image_hash = 3;
// Attestation mode (e.g. "dstack-tdx", "dstack-gcp-tdx")
string attestation_mode = 4;
// TEE variant (e.g. "dstack-tdx", "dstack-gcp-tdx")
string tee_variant = 4;
// Custom site name for display
string site_name = 5;
// Ethereum RPC URL from auth API
Expand Down
14 changes: 7 additions & 7 deletions dstack/kms/src/main_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ fn ensure_key_release_allowed(
snp_enabled: bool,
aws_nitro_tpm_enabled: bool,
) -> Result<()> {
match boot_info.attestation_mode {
match boot_info.tee_variant {
TeeVariant::DstackAmdSevSnp if !snp_enabled => {
bail!("amd sev-snp key release is not enabled")
}
Expand Down Expand Up @@ -320,7 +320,7 @@ impl RpcHandler {
// SNP rootfs/app/config binding is handled by the SNP launch-measurement
// helper above. The legacy OS-image verifier is TDX-oriented and still
// rejects SNP quotes; keep SNP on the explicit fail-closed helper path.
if boot_info.attestation_mode != TeeVariant::DstackAmdSevSnp {
if boot_info.tee_variant != TeeVariant::DstackAmdSevSnp {
self.verify_os_image_hash(vm_config_str.into(), att)
.await
.context("Failed to verify os image hash")?;
Expand Down Expand Up @@ -875,7 +875,7 @@ mod tests {
.expect("aws nitrotpm attestation should produce KMS boot info");
let pcrs = verified_aws_nitro_tpm_pcrs(&attestation);

assert_eq!(boot_info.attestation_mode, TeeVariant::DstackAwsNitroTpm);
assert_eq!(boot_info.tee_variant, TeeVariant::DstackAwsNitroTpm);
assert_eq!(boot_info.tcb_status, "UpToDate");
assert!(boot_info.advisory_ids.is_empty());
assert_eq!(boot_info.app_id, vec![0x11; 20]);
Expand Down Expand Up @@ -930,7 +930,7 @@ mod tests {
ensure_key_release_allowed(&boot_info, false, true).unwrap();

// A TDX boot info is unaffected by the AWS gate even when it is disabled.
boot_info.attestation_mode = TeeVariant::DstackTdx;
boot_info.tee_variant = TeeVariant::DstackTdx;
ensure_key_release_allowed(&boot_info, false, false).unwrap();
}

Expand Down Expand Up @@ -1023,7 +1023,7 @@ mod tests {
let boot_info = build_boot_info_for_attestation(&attestation, false, &vm_config)
.expect("snp attestation should build boot info through vm_config path");

assert_eq!(boot_info.attestation_mode, TeeVariant::DstackAmdSevSnp);
assert_eq!(boot_info.tee_variant, TeeVariant::DstackAmdSevSnp);
assert_eq!(boot_info.mr_aggregated.len(), 32);
assert_eq!(boot_info.device_id, vec![0xab; 64]);
assert_eq!(boot_info.app_id, vec![0x11; 20]);
Expand All @@ -1045,7 +1045,7 @@ mod tests {
let boot_info = build_boot_info_for_attestation(&attestation, false, "")
.expect("snp local KMS attestation should use embedded vm_config");

assert_eq!(boot_info.attestation_mode, TeeVariant::DstackAmdSevSnp);
assert_eq!(boot_info.tee_variant, TeeVariant::DstackAmdSevSnp);
assert_eq!(boot_info.mr_aggregated.len(), 32);
assert_eq!(boot_info.app_id, vec![0x11; 20]);
}
Expand All @@ -1060,7 +1060,7 @@ mod tests {

let boot_info = build_boot_info_for_attestation(&attestation, false, &vm_config)
.expect("self-contained SNP vm_config should not require KMS-local sev_snp config");
assert_eq!(boot_info.attestation_mode, TeeVariant::DstackAmdSevSnp);
assert_eq!(boot_info.tee_variant, TeeVariant::DstackAmdSevSnp);
assert_eq!(boot_info.device_id, vec![0xab; 64]);
}

Expand Down
4 changes: 2 additions & 2 deletions dstack/kms/src/main_service/amd_attest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ fn build_amd_snp_boot_info_with_tcb_status(
let key_provider_info = mr_config_key_provider_info(&mr_config)?;

Ok(BootInfo {
attestation_mode: TeeVariant::DstackAmdSevSnp,
tee_variant: TeeVariant::DstackAmdSevSnp,
mr_aggregated,
os_image_hash: os_image_hash.to_vec(),
mr_system,
Expand Down Expand Up @@ -366,7 +366,7 @@ mod tests {

let boot_info = build_amd_snp_boot_info(&verified, &chip_id, &input)
.expect("matching measurement should build snp boot info");
assert_eq!(boot_info.attestation_mode, TeeVariant::DstackAmdSevSnp);
assert_eq!(boot_info.tee_variant, TeeVariant::DstackAmdSevSnp);
assert_eq!(boot_info.mr_aggregated.len(), 32);
assert_eq!(boot_info.device_id, chip_id.to_vec());
assert_eq!(boot_info.app_id, vec![0x11; 20]);
Expand Down
4 changes: 2 additions & 2 deletions dstack/kms/src/main_service/upgrade_authority.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ pub(crate) fn build_boot_info(
use_boottime_mr: bool,
vm_config_str: &str,
) -> Result<BootInfo> {
let mode = att.quote.mode();
let variant = att.quote.variant();
let (tcb_status, advisory_ids) = dstack_verifier::policy_tcb_fields(att);
let app_info = att.decode_app_info_ex(use_boottime_mr, vm_config_str)?;
ensure_app_id_len(&app_info.app_id)?;
Ok(BootInfo {
attestation_mode: mode,
tee_variant: variant,
mr_aggregated: app_info.mr_aggregated.to_vec(),
os_image_hash: app_info.os_image_hash,
mr_system: app_info.mr_system.to_vec(),
Expand Down
10 changes: 5 additions & 5 deletions dstack/kms/src/onboard_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ impl OnboardRpc for OnboardHandler {
// Decode and verify the attestation to get real device ID
let attestation = VersionedAttestation::from_bytes(&response.attestation)
.context("Failed to decode attestation")?;
let attestation_mode = match &attestation.clone().into_v1().platform {
let tee_variant = match &attestation.clone().into_v1().platform {
PlatformEvidence::Tdx { .. } => "dstack-tdx",
PlatformEvidence::SevSnp { .. } => "dstack-amd-sev-snp",
PlatformEvidence::GcpTdx { .. } => "dstack-gcp-tdx",
Expand Down Expand Up @@ -161,7 +161,7 @@ impl OnboardRpc for OnboardHandler {

build_attestation_info_response(
&verified,
attestation_mode,
tee_variant,
&info.vm_config,
self.state.config.site_name.clone(),
eth_rpc_url,
Expand All @@ -176,7 +176,7 @@ impl OnboardRpc for OnboardHandler {

fn build_attestation_info_response(
verified: &VerifiedAttestation,
attestation_mode: String,
tee_variant: String,
vm_config: &str,
site_name: String,
eth_rpc_url: String,
Expand All @@ -189,7 +189,7 @@ fn build_attestation_info_response(
device_id: sha2::Sha256::digest(&raw_device_id).to_vec(),
mr_aggregated: boot_info.mr_aggregated,
os_image_hash: boot_info.os_image_hash,
attestation_mode,
tee_variant,
site_name,
eth_rpc_url,
kms_contract_address,
Expand Down Expand Up @@ -336,7 +336,7 @@ mod tests {
assert_eq!(response.ppid, vec![0xab; 64]);
assert_eq!(response.mr_aggregated.len(), 32);
assert_eq!(response.os_image_hash, os_image_hash.to_vec());
assert_eq!(response.attestation_mode, "dstack-amd-sev-snp");
assert_eq!(response.tee_variant, "dstack-amd-sev-snp");
assert_eq!(response.site_name, "test-site");
assert_eq!(response.eth_rpc_url, "https://rpc.example");
assert_eq!(response.kms_contract_address, "0x1234");
Expand Down
4 changes: 2 additions & 2 deletions dstack/kms/src/www/onboard.html
Original file line number Diff line number Diff line change
Expand Up @@ -174,8 +174,8 @@ <h1>{{ siteName || 'dstack KMS Setup' }}</h1>
<div v-else-if="attestationInfo" class="attestation-info">
<h3>Attestation Info (for on-chain registration)</h3>
<div class="info-row">
<span class="info-label">Attestation Mode:</span>
<span class="info-value">{{ attestationInfo.attestation_mode }}</span>
<span class="info-label">TEE Variant:</span>
<span class="info-value">{{ attestationInfo.tee_variant }}</span>
</div>
<div class="info-row" v-if="attestationInfo.ppid">
<span class="info-label">PPID:</span>
Expand Down
2 changes: 1 addition & 1 deletion dstack/tests/docs/kms-self-authorization.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ Expected fields:
- `device_id`
- `mr_aggregated`
- `os_image_hash`
- `attestation_mode`
- `tee_variant`

Extract values:

Expand Down
10 changes: 5 additions & 5 deletions dstack/verifier/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ against the returned evidence.
"acpi_tables_verified": true, // true only when TDX ACPI table contents are verified
"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
"attestation_mode": "dstack-tdx", // dstack-tdx | dstack-gcp-tdx | dstack-nitro-enclave | dstack-amd-sev-snp | dstack-aws-nitro-tpm
"tee_variant": "dstack-tdx", // dstack-tdx | dstack-gcp-tdx | dstack-nitro-enclave | dstack-amd-sev-snp | dstack-aws-nitro-tpm
"report_data": "hex-encoded-64-byte-report-data",
"tcb_status": "UpToDate",
"advisory_ids": [],
Expand All @@ -61,7 +61,7 @@ against the returned evidence.
"key_provider_info": "hex-string"
},
"boot_info": {
"attestationMode": "dstack-aws-nitro-tpm",
"teeVariant": "dstack-aws-nitro-tpm",
"mrAggregated": "hex-string",
"osImageHash": "hex-string",
"mrSystem": "hex-string",
Expand Down Expand Up @@ -161,7 +161,7 @@ The verification checks that:

Clients, gateways, or release validators should combine this certificate-level
check with the policy fields emitted by `/verify`: accepted OS image,
`attestationMode`, `mrAggregated`, app compose hash, KMS identity, a
`teeVariant`, `mrAggregated`, app compose hash, KMS identity, a
`report_data` challenge, and any deployment-specific endpoint allowlist.

### Running with Docker Compose
Expand Down Expand Up @@ -263,9 +263,9 @@ 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.
- **`attestation_mode`** — the TEE variant that produced the verified quote, serialized as `TeeVariant`: `dstack-tdx`, `dstack-gcp-tdx`, `dstack-nitro-enclave`, `dstack-amd-sev-snp`, or `dstack-aws-nitro-tpm`.
- **`tee_variant`** — the TEE variant that produced the verified quote, serialized as `TeeVariant`: `dstack-tdx`, `dstack-gcp-tdx`, `dstack-nitro-enclave`, `dstack-amd-sev-snp`, or `dstack-aws-nitro-tpm`.
- **`acpi_tables_verified`** — whether TDX ACPI table contents were verified. This is useful for relying parties that require `requirements.tdx_measure_acpi_tables = true`.
- **`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`.
- **`boot_info`** — the policy object a relying party should feed to its auth/governance layer. For AWS EC2 NitroTPM this includes `attestationMode = dstack-aws-nitro-tpm`, PCR4/7/12-derived `osImageHash`, PCR14-bound `mrAggregated`, app identity, instance/device identity, and a `tcbStatus` normalized to `UpToDate`.
- **`boot_info`** — the policy object a relying party should feed to its auth/governance layer. For AWS EC2 NitroTPM this includes `teeVariant = dstack-aws-nitro-tpm`, PCR4/7/12-derived `osImageHash`, PCR14-bound `mrAggregated`, app identity, instance/device identity, and a `tcbStatus` normalized to `UpToDate`.

`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).
2 changes: 1 addition & 1 deletion dstack/verifier/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ async fn run_cert_oneshot(file_path: &str, config: &Config) -> anyhow::Result<()
let output = serde_json::json!({
"is_valid": true,
"details": {
"attestation_mode": verified.attestation.quote.mode(),
"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),
Expand Down
Loading
Loading