Skip to content

Commit b953c66

Browse files
committed
feat: add canonical JSON event log v2
1 parent b304efe commit b953c66

29 files changed

Lines changed: 1052 additions & 427 deletions

File tree

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,5 @@ node_modules/
1616
__pycache__
1717
/.ruff_cache/
1818
.planning/
19-
/dstack/vmm/src/console_v1.html
19+
/vmm/src/console_v1.html
2020
.claude/worktrees/

dstack/Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dstack/cc-eventlog/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ license.workspace = true
1313
[dependencies]
1414
anyhow.workspace = true
1515
digest = "0.10.7"
16+
dstack-types.workspace = true
1617
ez-hash.workspace = true
1718
fs-err.workspace = true
1819
hex.workspace = true
20+
or-panic.workspace = true
1921
scale.workspace = true
2022
serde.workspace = true
2123
serde-human-bytes.workspace = true
24+
serde_jcs = "0.2.0"
2225
serde_json = { workspace = true, features = ["alloc"] }
2326
sha2.workspace = true
2427

dstack/cc-eventlog/src/lib.rs

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,24 @@
22
//
33
// SPDX-License-Identifier: Apache-2.0
44

5-
pub use runtime_events::{replay_events, RuntimeEvent};
5+
pub use dstack_types::EventLogVersion;
6+
pub use runtime_events::{
7+
canonical_event_json_v2, replay_events, RuntimeEvent, DSTACK_RUNTIME_EVENT_TYPE,
8+
};
69
pub use tdx::TdxEvent;
710

811
mod codecs;
912
mod runtime_events;
10-
mod tcg;
13+
pub mod tcg;
1114
pub mod tdx;
1215
pub mod tpm;
1316

1417
#[cfg(test)]
1518
mod tests {
1619
use super::*;
20+
use dstack_types::EventLogVersion;
21+
use ez_hash::{Hasher, Sha384};
22+
use tdx::TdxEvent;
1723

1824
#[test]
1925
fn parse_ccel() {
@@ -24,4 +30,56 @@ mod tests {
2430
let json = serde_json::to_string_pretty(&tdx_event_logs).unwrap();
2531
insta::assert_snapshot!(json);
2632
}
33+
34+
#[test]
35+
fn encode_runtime_events_roundtrip() {
36+
// Synthesize a CCEL: sample boot-time bytes + encoded runtime events.
37+
// Decode with the same TcgEventLog parser and verify:
38+
// 1. all boot-time events are preserved
39+
// 2. runtime events appear with the expected pcrIndex / event_type / digest
40+
// 3. sha384(event_data) == digest (the property we document)
41+
let boot_raw = include_bytes!("../samples/ccel.bin");
42+
let valid = tcg::ccel_content_len(boot_raw).unwrap();
43+
44+
let runtime_v1: TdxEvent = RuntimeEvent::new(
45+
"app-id".to_string(),
46+
vec![0xaa, 0xbb, 0xcc],
47+
EventLogVersion::V1,
48+
)
49+
.into();
50+
let runtime_v2: TdxEvent = RuntimeEvent::new(
51+
"compose-hash".to_string(),
52+
vec![0xde, 0xad, 0xbe, 0xef],
53+
EventLogVersion::V2,
54+
)
55+
.into();
56+
let runtime_events = vec![runtime_v1.clone(), runtime_v2.clone()];
57+
58+
let mut merged = boot_raw[..valid].to_vec();
59+
merged.extend_from_slice(&tcg::encode_runtime_events_as_tcg(&runtime_events));
60+
merged.extend_from_slice(&0xFFFF_FFFFu32.to_le_bytes());
61+
62+
let parsed = tcg::TcgEventLog::decode(&mut merged.as_slice()).unwrap();
63+
let boot_count = tcg::TcgEventLog::decode(&mut boot_raw.as_slice())
64+
.unwrap()
65+
.event_logs
66+
.len();
67+
assert_eq!(parsed.event_logs.len(), boot_count + 2);
68+
69+
// Convert and verify runtime tail matches what we put in.
70+
let converted = parsed.to_cc_event_log().unwrap();
71+
let tail = &converted[converted.len() - 2..];
72+
for (orig, got) in runtime_events.iter().zip(tail.iter()) {
73+
assert_eq!(got.imr, orig.imr);
74+
assert_eq!(got.event_type, orig.event_type);
75+
assert_eq!(got.digest, orig.digest());
76+
// The event_payload carried in TdxEvent after TCG round-trip is the
77+
// hash_input bytes we stored as TCG event data.
78+
let runtime = orig.to_runtime_event().unwrap();
79+
assert_eq!(got.event_payload, runtime.hash_input());
80+
// Property: sha384(event_data) == digest
81+
let h = Sha384::hash([got.event_payload.as_slice()]);
82+
assert_eq!(h.as_slice(), got.digest.as_slice());
83+
}
84+
}
2785
}

0 commit comments

Comments
 (0)