Skip to content

Commit a1166cd

Browse files
committed
feat(tdx-attest): Rewrite TDX attestation in pure Rust
Implement TDX attestation without C library dependencies: - get_quote: ConfigFS (Linux 6.7+) → VSock/QGS fallback - extend_rtmr: sysfs → ioctl fallback - get_report: ioctl via /dev/tdx_guest - get_supported_att_key_ids: returns Intel TDQE UUID Key changes: - Add vsock crate for QGS protocol communication - Probe availability before execution (separate NotSupported from errors) - Include original error context in error messages - Support both new dstack OS (ConfigFS/sysfs) and legacy 0.5.4 (VSock/ioctl) Tested on: - New dstack OS: ConfigFS for quote, sysfs for extend_rtmr - dstack 0.5.4: VSock for quote, ioctl for extend_rtmr
1 parent 7f82973 commit a1166cd

8 files changed

Lines changed: 756 additions & 292 deletions

File tree

Cargo.lock

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

tdx-attest-sys/csrc/linux/ioctl.h

Lines changed: 0 additions & 66 deletions
This file was deleted.

tdx-attest-sys/csrc/linux/types.h

Lines changed: 0 additions & 32 deletions
This file was deleted.

tdx-attest-sys/csrc/linux/vm_sockets.h

Lines changed: 0 additions & 49 deletions
This file was deleted.

tdx-attest/Cargo.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ license.workspace = true
1313
[dependencies]
1414
anyhow.workspace = true
1515
hex.workspace = true
16-
num_enum.workspace = true
1716
scale.workspace = true
1817
serde.workspace = true
1918
serde-human-bytes.workspace = true
@@ -23,10 +22,13 @@ fs-err.workspace = true
2322
serde_json = { workspace = true, features = ["alloc"] }
2423
sha2.workspace = true
2524

26-
# Linux x86_64 with glibc or musl
25+
# Linux x86_64 - pure Rust implementation using libc for syscalls
2726
[target.'cfg(all(target_os = "linux", target_arch = "x86_64"))'.dependencies]
28-
tdx-attest-sys.workspace = true
27+
libc.workspace = true
28+
vsock = "0.5"
2929

3030
[dev-dependencies]
3131
insta.workspace = true
3232
serde_json.workspace = true
33+
dcap-qvl.workspace = true
34+
tokio = { workspace = true, features = ["rt-multi-thread", "macros"] }

tdx-attest/examples/test_tdx.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
// SPDX-FileCopyrightText: © 2025 Phala Network <dstack@phala.network>
2+
//
3+
// SPDX-License-Identifier: Apache-2.0
4+
5+
//! Test TDX attestation functions on real TDX hardware.
6+
7+
use dcap_qvl::collateral::get_collateral_and_verify;
8+
use dcap_qvl::quote::Quote;
9+
10+
#[tokio::main]
11+
async fn main() {
12+
println!("=== TDX Attestation Test ===\n");
13+
14+
// Test get_report
15+
println!("1. Testing get_report...");
16+
let report_data = [0u8; 64];
17+
match tdx_attest::get_report(&report_data) {
18+
Ok(report) => {
19+
println!(" ✓ get_report succeeded");
20+
println!(" Report size: {} bytes", report.0.len());
21+
println!(" Report hash: {:02x?}", &report.0[..32]);
22+
}
23+
Err(e) => {
24+
println!(" ✗ get_report failed: {}", e);
25+
}
26+
}
27+
28+
// Test get_quote
29+
println!("\n2. Testing get_quote...");
30+
let report_data = [0x42u8; 64];
31+
let quote = match tdx_attest::get_quote(&report_data) {
32+
Ok(quote) => {
33+
println!(" ✓ get_quote succeeded");
34+
println!(" Quote size: {} bytes", quote.len());
35+
println!(" Quote header: {:02x?}", &quote[..32.min(quote.len())]);
36+
Some(quote)
37+
}
38+
Err(e) => {
39+
println!(" ✗ get_quote failed: {}", e);
40+
None
41+
}
42+
};
43+
44+
// Parse and verify quote with dcap-qvl
45+
if let Some(ref quote) = quote {
46+
println!("\n3. Parsing quote...");
47+
match Quote::parse(quote) {
48+
Ok(q) => {
49+
println!(" ✓ Quote parsed");
50+
println!(" Version: {}", q.header.version);
51+
if let Some(report) = q.report.as_td10() {
52+
println!(" MRTD: {}...", hex::encode(&report.mr_td[..16]));
53+
println!(" RTMR3: {}...", hex::encode(&report.rt_mr3[..16]));
54+
}
55+
}
56+
Err(e) => {
57+
println!(" ✗ Quote parse failed: {:?}", e);
58+
}
59+
}
60+
61+
println!("\n4. Verifying quote with PCCS...");
62+
let pccs_url = std::env::var("PCCS_URL").ok();
63+
println!(
64+
" PCCS URL: {}",
65+
pccs_url.as_deref().unwrap_or("(default)")
66+
);
67+
match get_collateral_and_verify(quote, pccs_url.as_deref()).await {
68+
Ok(verified) => {
69+
println!(" ✓ Quote verified!");
70+
println!(" QE status: {:?}", verified.qe_status);
71+
if let Some(report) = verified.report.as_td10() {
72+
if report.report_data[..] == report_data[..] {
73+
println!(" ✓ Report data matches");
74+
} else {
75+
println!(" ✗ Report data mismatch!");
76+
}
77+
}
78+
}
79+
Err(e) => {
80+
println!(" ✗ Verification failed: {:?}", e);
81+
}
82+
}
83+
}
84+
85+
// Test extend_rtmr (RTMR 3 is user-extensible)
86+
println!("\n5. Testing extend_rtmr (RTMR 3)...");
87+
let digest = [0xABu8; 48];
88+
match tdx_attest::extend_rtmr(3, 0, digest) {
89+
Ok(()) => {
90+
println!(" ✓ extend_rtmr succeeded");
91+
}
92+
Err(e) => {
93+
println!(" ✗ extend_rtmr failed: {:?}", e);
94+
}
95+
}
96+
97+
// Test get_supported_att_key_ids
98+
println!("\n6. Testing get_supported_att_key_ids...");
99+
match tdx_attest::get_supported_att_key_ids() {
100+
Ok(ids) => {
101+
println!(" ✓ get_supported_att_key_ids succeeded");
102+
println!(" Supported key IDs: {}", ids.len());
103+
for (i, id) in ids.iter().enumerate() {
104+
println!(" [{}] {:02x?}", i, id.0);
105+
}
106+
}
107+
Err(e) => {
108+
println!(" ✗ get_supported_att_key_ids failed: {}", e);
109+
}
110+
}
111+
112+
println!("\n=== Test Complete ===");
113+
}

0 commit comments

Comments
 (0)