Skip to content

Commit bd7640d

Browse files
authored
Merge pull request #966 from Dstack-TEE/codex/fix-simulator-legacy-attestation
[STACKED on #964] fix(simulator): preserve legacy attestation responses
2 parents aad2e3b + e48e62d commit bd7640d

3 files changed

Lines changed: 86 additions & 12 deletions

File tree

dstack/dstack-attest/src/attestation.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -868,6 +868,40 @@ impl TdxAttestationExt for AttestationV1 {
868868
}
869869

870870
impl AttestationV1 {
871+
/// Convert a V1 dstack attestation back to the legacy SCALE schema.
872+
///
873+
/// This is only lossless for the original dstack stack with V1 runtime
874+
/// events. Pod payloads and newer event encodings must remain on the V1
875+
/// msgpack wire format.
876+
pub fn try_into_legacy(self) -> Result<Attestation> {
877+
let Self {
878+
platform, stack, ..
879+
} = self;
880+
let StackEvidence::Dstack {
881+
report_data,
882+
runtime_events,
883+
config,
884+
} = stack
885+
else {
886+
bail!("dstack-pod attestation cannot be represented by the legacy schema");
887+
};
888+
if runtime_events
889+
.iter()
890+
.any(|event| !matches!(event.version, EventLogVersion::V1))
891+
{
892+
bail!("non-V1 runtime events cannot be represented by the legacy schema");
893+
}
894+
Ok(Attestation {
895+
quote: platform_into_legacy_quote(platform),
896+
runtime_events,
897+
report_data: report_data
898+
.try_into()
899+
.map_err(|_| anyhow!("stack.report_data must be 64 bytes"))?,
900+
config,
901+
report: (),
902+
})
903+
}
904+
871905
/// Decode the VM config from the external or embedded config.
872906
pub fn decode_vm_config<'a>(&'a self, config: &'a str) -> Result<VmConfig> {
873907
decode_vm_config_with_fallback(config, self.stack.config())
@@ -2886,6 +2920,40 @@ mod tests {
28862920
assert!(matches!(upgraded.stack, StackEvidence::Dstack { .. }));
28872921
}
28882922

2923+
#[test]
2924+
fn v1_dstack_with_v1_events_converts_losslessly_to_legacy() {
2925+
let mut legacy = dummy_tdx_attestation([0x5a; 64]);
2926+
legacy.runtime_events.push(cc_eventlog::RuntimeEvent::new(
2927+
"legacy-event".into(),
2928+
vec![1, 2, 3],
2929+
cc_eventlog::EventLogVersion::V1,
2930+
));
2931+
let converted = legacy.clone().into_v1().try_into_legacy().unwrap();
2932+
assert_eq!(converted.report_data, legacy.report_data);
2933+
assert_eq!(converted.runtime_events.len(), 1);
2934+
assert!(matches!(
2935+
converted.into_versioned(),
2936+
VersionedAttestation::V0 { .. }
2937+
));
2938+
}
2939+
2940+
#[test]
2941+
fn v1_conversion_rejects_lossy_legacy_projection() {
2942+
let pod = dummy_tdx_attestation([0x5b; 64])
2943+
.into_v1()
2944+
.into_dstack_pod("payload".into());
2945+
assert!(pod.try_into_legacy().is_err());
2946+
let mut v2 = dummy_tdx_attestation([0x5c; 64]).into_v1();
2947+
if let StackEvidence::Dstack { runtime_events, .. } = &mut v2.stack {
2948+
runtime_events.push(cc_eventlog::RuntimeEvent::new(
2949+
"v2-event".into(),
2950+
vec![4, 5, 6],
2951+
cc_eventlog::EventLogVersion::V2,
2952+
));
2953+
}
2954+
assert!(v2.try_into_legacy().is_err());
2955+
}
2956+
28892957
#[test]
28902958
fn versioned_v0_projects_to_v1() {
28912959
let projected = dummy_tdx_attestation([5u8; 64]).into_versioned().into_v1();

dstack/guest-agent-simulator/src/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,10 +180,11 @@ mod tests {
180180
}
181181

182182
#[test]
183-
fn simulator_attest_response_uses_supplied_report_data() {
183+
fn simulator_attest_response_preserves_legacy_wire_format() {
184184
let platform = load_fixture_platform();
185185
let report_data = [0x5a; 64];
186186
let response = platform.attest_response(report_data).unwrap();
187+
assert_eq!(response.attestation.first(), Some(&0x00));
187188
let patched = VersionedAttestation::from_bytes(&response.attestation)
188189
.unwrap()
189190
.into_v1();

dstack/guest-agent-simulator/src/simulator.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,23 +58,24 @@ pub fn simulated_quote_response(
5858
}
5959

6060
pub fn simulated_attest_response(
61-
attestation: &VersionedAttestation,
61+
source: &VersionedAttestation,
6262
report_data: [u8; 64],
6363
patch_report_data: bool,
6464
generator: Option<&TdxGenerator>,
6565
) -> Result<AttestResponse> {
66-
let mut attestation = prepare_attestation(
67-
attestation,
68-
report_data,
69-
patch_report_data,
70-
generator,
71-
"attest",
72-
)?;
66+
let preserve_legacy = matches!(source, VersionedAttestation::V0 { .. });
67+
let mut attestation =
68+
prepare_attestation(source, report_data, patch_report_data, generator, "attest")?;
7369
if let Some(event_log) = attestation.platform.tdx_event_log_mut() {
7470
cc_eventlog::tdx::fill_v2_preimages(event_log);
7571
}
72+
let attestation = if preserve_legacy {
73+
attestation.try_into_legacy()?.into_versioned()
74+
} else {
75+
VersionedAttestation::V1 { attestation }
76+
};
7677
Ok(AttestResponse {
77-
attestation: VersionedAttestation::V1 { attestation }.to_bytes()?,
78+
attestation: attestation.to_bytes()?,
7879
})
7980
}
8081

@@ -83,19 +84,23 @@ pub fn simulated_info_attestation(attestation: &VersionedAttestation) -> Version
8384
}
8485

8586
pub fn simulated_certificate_attestation(
86-
attestation: &VersionedAttestation,
87+
source: &VersionedAttestation,
8788
pubkey: &[u8],
8889
patch_report_data: bool,
8990
generator: Option<&TdxGenerator>,
9091
) -> Result<VersionedAttestation> {
92+
let preserve_legacy = matches!(source, VersionedAttestation::V0 { .. });
9193
let report_data = QuoteContentType::RaTlsCert.to_report_data(pubkey);
9294
let attestation = prepare_attestation(
93-
attestation,
95+
source,
9496
report_data,
9597
patch_report_data,
9698
generator,
9799
"certificate_attestation",
98100
)?;
101+
if preserve_legacy {
102+
return Ok(attestation.try_into_legacy()?.into_versioned());
103+
}
99104
Ok(VersionedAttestation::V1 { attestation })
100105
}
101106

0 commit comments

Comments
 (0)