Skip to content

Commit 1d6e245

Browse files
haitaohuangjyao1
authored andcommitted
td-shim-AzCVMEmu/tdx-tdcall: support mock quote file
Load a quote from a file and emulate REPORT and QUOTE using the data from the file. Check in a v4 quote binary as example Update AzCVMEmu scripts to use this feature Signed-off-by: Haitao Huang <[email protected]>
1 parent 9b2d101 commit 1d6e245

File tree

4 files changed

+358
-42
lines changed

4 files changed

+358
-42
lines changed
4.89 KB
Binary file not shown.

deps/td-shim-AzCVMEmu/tdx-tdcall/src/tdreport_emu.rs

Lines changed: 265 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use az_tdx_vtpm::tdx;
1313
#[cfg(not(feature = "test_mock_report"))]
1414
use az_tdx_vtpm::{hcl, imds, vtpm};
1515
use log::{debug, info};
16-
#[cfg(not(feature = "test_mock_report"))]
1716
use log::error;
1817
use original_tdx_tdcall::TdCallError;
1918

@@ -196,7 +195,12 @@ pub fn get_quote_emulated(td_report_data: &[u8]) -> Result<Vec<u8>, QuoteError>
196195
/// Create a mock TD report for testing purposes
197196
#[cfg(any(feature = "test_mock_report", feature = "mock_report_tools"))]
198197
pub fn create_mock_td_report() -> tdx::TdReport {
199-
debug!("Creating mock TD report");
198+
// Check if a custom quote file is specified
199+
if let Ok(quote_file_path) = std::env::var("MOCK_QUOTE_FILE") {
200+
return create_td_report_from_file(quote_file_path);
201+
}
202+
// No custom quote file - use hardcoded default TD report
203+
debug!("Creating mock TD report with hardcoded data");
200204

201205
// Use actual TD report data from logs
202206
let report_bytes = [
@@ -282,11 +286,247 @@ pub fn create_mock_td_report() -> tdx::TdReport {
282286
unsafe { core::mem::transmute(td_report) }
283287
}
284288

285-
/// Create a mock quote for testing purposes
286289
#[cfg(any(feature = "test_mock_report", feature = "mock_report_tools"))]
287-
pub fn create_mock_quote(td_report_data: &[u8]) -> Vec<u8> {
288-
debug!("Creating mock quote from TD report data (size: {})", td_report_data.len());
290+
fn create_td_report_from_file(quote_file_path: String) -> tdx::TdReport {
291+
// Custom quote file specified - must load and parse it
292+
use original_tdx_tdcall::tdreport::{ReportMac, ReportType, TdInfo, TdxReport, TeeTcbInfo};
293+
294+
debug!("Creating mock TD report from custom quote file: {}", quote_file_path);
295+
296+
let quote_data = match std::fs::read(&quote_file_path) {
297+
Ok(data) => {
298+
debug!("Successfully loaded quote file ({} bytes)", data.len());
299+
data
300+
}
301+
Err(e) => {
302+
error!("Failed to load quote from {}: {:?}", quote_file_path, e);
303+
if let Ok(cwd) = std::env::current_dir() {
304+
error!("Current working directory: {:?}", cwd);
305+
error!("Hint: Use absolute path or ensure the path is relative to: {:?}", cwd);
306+
}
307+
panic!("Cannot create mock TD report without valid quote file");
308+
}
309+
};
310+
311+
// Parse the quote structure (v4 or v5 format)
312+
// Quote v4 layout: header (48 bytes) + sgx_report2_body_t (584 bytes) + signature_data_len (4 bytes) + signature_data
313+
// Quote v5 layout: header (48 bytes) + type (2 bytes) + size (4 bytes) + body (variable) + signature_data_len (4 bytes) + signature_data
314+
const QUOTE_HEADER_SIZE: usize = 48;
315+
const QUOTE_V4_BODY_SIZE: usize = 584; // sgx_report2_body_t size for TDX 1.0
316+
const QUOTE_V5_BODY_SIZE_10: usize = 584; // TD Report 1.0 (type=2)
317+
const QUOTE_V5_BODY_SIZE_15: usize = 648; // TD Report 1.5 (type=3)
318+
const MIN_QUOTE_V4_SIZE: usize = QUOTE_HEADER_SIZE + QUOTE_V4_BODY_SIZE + 4;
319+
const MIN_QUOTE_V5_SIZE: usize = QUOTE_HEADER_SIZE + 2 + 4 + QUOTE_V5_BODY_SIZE_10 + 4;
320+
321+
if quote_data.len() < QUOTE_HEADER_SIZE + 2 {
322+
error!("Quote file too small: {} bytes (expected at least {})", quote_data.len(), QUOTE_HEADER_SIZE + 2);
323+
panic!("Invalid quote file: too small");
324+
}
325+
326+
// Parse quote header (48 bytes)
327+
let header_version = u16::from_le_bytes([quote_data[0], quote_data[1]]);
328+
let tee_type = u32::from_le_bytes([quote_data[4], quote_data[5], quote_data[6], quote_data[7]]);
329+
330+
debug!("Quote header - version: {}, tee_type: 0x{:02x}", header_version, tee_type);
289331

332+
// Determine quote version and parse accordingly
333+
let (body_offset, body_size) = if header_version == 4 {
334+
// Quote v4: body starts immediately after header
335+
if quote_data.len() < MIN_QUOTE_V4_SIZE {
336+
error!("Quote v4 file too small: {} bytes (expected at least {})", quote_data.len(), MIN_QUOTE_V4_SIZE);
337+
panic!("Invalid quote v4 file: too small");
338+
}
339+
debug!("Parsing Quote v4 format");
340+
(QUOTE_HEADER_SIZE, QUOTE_V4_BODY_SIZE)
341+
} else if header_version == 5 {
342+
// Quote v5: has type and size fields after header
343+
if quote_data.len() < MIN_QUOTE_V5_SIZE {
344+
error!("Quote v5 file too small: {} bytes (expected at least {})", quote_data.len(), MIN_QUOTE_V5_SIZE);
345+
panic!("Invalid quote v5 file: too small");
346+
}
347+
348+
let body_type = u16::from_le_bytes([quote_data[48], quote_data[49]]);
349+
let body_size = u32::from_le_bytes([quote_data[50], quote_data[51], quote_data[52], quote_data[53]]) as usize;
350+
351+
debug!("Parsing Quote v5 format - body_type: {}, body_size: {}", body_type, body_size);
352+
353+
// Body type 2 = TD Report 1.0 (584 bytes), type 3 = TD Report 1.5 (648 bytes)
354+
let expected_size = match body_type {
355+
2 => QUOTE_V5_BODY_SIZE_10,
356+
3 => QUOTE_V5_BODY_SIZE_15,
357+
_ => {
358+
error!("Unsupported Quote v5 body type: {}", body_type);
359+
panic!("Unsupported Quote v5 body type");
360+
}
361+
};
362+
363+
if body_size != expected_size {
364+
error!("Quote v5 body size mismatch: {} (expected {})", body_size, expected_size);
365+
panic!("Invalid Quote v5 body size");
366+
}
367+
368+
(QUOTE_HEADER_SIZE + 6, body_size)
369+
} else {
370+
error!("Unsupported quote version: {}", header_version);
371+
panic!("Unsupported quote version");
372+
};
373+
374+
// Quote body structure (sgx_report2_body_t from Intel DCAP):
375+
// Offset 0: tee_tcb_svn [16 bytes]
376+
// Offset 16: mr_seam [48 bytes]
377+
// Offset 64: mrsigner_seam [48 bytes]
378+
// Offset 112: seam_attributes [8 bytes]
379+
// Offset 120: td_attributes [8 bytes]
380+
// Offset 128: xfam [8 bytes]
381+
// Offset 136: mr_td [48 bytes]
382+
// Offset 184: mr_config_id [48 bytes]
383+
// Offset 232: mr_owner [48 bytes]
384+
// Offset 280: mr_owner_config [48 bytes]
385+
// Offset 328: rt_mr[4] [192 bytes = 4 x 48]
386+
// Offset 520: report_data [64 bytes]
387+
// [v5 only] Offset 584: tee_tcb_svn2 [16 bytes] (for TD preserving)
388+
// [v5 only] Offset 600: mr_servicetd [48 bytes] (service TD hash)
389+
// Total v4/v5.0: 584 bytes, v5.5: 648 bytes
390+
391+
#[repr(C, packed)]
392+
struct SgxReport2Body {
393+
tee_tcb_svn: [u8; 16],
394+
mr_seam: [u8; 48],
395+
mrsigner_seam: [u8; 48],
396+
seam_attributes: [u8; 8],
397+
td_attributes: [u8; 8],
398+
xfam: [u8; 8],
399+
mr_td: [u8; 48],
400+
mr_config_id: [u8; 48],
401+
mr_owner: [u8; 48],
402+
mr_owner_config: [u8; 48],
403+
rt_mr: [[u8; 48]; 4],
404+
report_data: [u8; 64],
405+
}
406+
407+
#[repr(C, packed)]
408+
struct SgxReport2BodyV15 {
409+
tee_tcb_svn: [u8; 16],
410+
mr_seam: [u8; 48],
411+
mrsigner_seam: [u8; 48],
412+
seam_attributes: [u8; 8],
413+
td_attributes: [u8; 8],
414+
xfam: [u8; 8],
415+
mr_td: [u8; 48],
416+
mr_config_id: [u8; 48],
417+
mr_owner: [u8; 48],
418+
mr_owner_config: [u8; 48],
419+
rt_mr: [[u8; 48]; 4],
420+
report_data: [u8; 64],
421+
tee_tcb_svn2: [u8; 16],
422+
mr_servicetd: [u8; 48],
423+
}
424+
425+
// Get report body from quote
426+
let (report_body, servtd_hash) = if body_size == QUOTE_V5_BODY_SIZE_15 {
427+
// v5 with TD Report 1.5 (648 bytes) - includes mr_servicetd
428+
let report_v15 = unsafe {
429+
&*(quote_data[body_offset..body_offset + body_size].as_ptr() as *const SgxReport2BodyV15)
430+
};
431+
debug!("Successfully parsed TD quote v5.5 body (648 bytes) from file");
432+
433+
// Extract the base report body fields
434+
let base_body = SgxReport2Body {
435+
tee_tcb_svn: report_v15.tee_tcb_svn,
436+
mr_seam: report_v15.mr_seam,
437+
mrsigner_seam: report_v15.mrsigner_seam,
438+
seam_attributes: report_v15.seam_attributes,
439+
td_attributes: report_v15.td_attributes,
440+
xfam: report_v15.xfam,
441+
mr_td: report_v15.mr_td,
442+
mr_config_id: report_v15.mr_config_id,
443+
mr_owner: report_v15.mr_owner,
444+
mr_owner_config: report_v15.mr_owner_config,
445+
rt_mr: report_v15.rt_mr,
446+
report_data: report_v15.report_data,
447+
};
448+
(base_body, report_v15.mr_servicetd)
449+
} else {
450+
// v4 or v5 with TD Report 1.0 (584 bytes)
451+
let report = unsafe {
452+
&*(quote_data[body_offset..body_offset + body_size].as_ptr() as *const SgxReport2Body)
453+
};
454+
debug!("Successfully parsed TD quote v{} body (584 bytes) from file", header_version);
455+
456+
// Copy the struct to move it out of the unsafe block
457+
let base_body = SgxReport2Body {
458+
tee_tcb_svn: report.tee_tcb_svn,
459+
mr_seam: report.mr_seam,
460+
mrsigner_seam: report.mrsigner_seam,
461+
seam_attributes: report.seam_attributes,
462+
td_attributes: report.td_attributes,
463+
xfam: report.xfam,
464+
mr_td: report.mr_td,
465+
mr_config_id: report.mr_config_id,
466+
mr_owner: report.mr_owner,
467+
mr_owner_config: report.mr_owner_config,
468+
rt_mr: report.rt_mr,
469+
report_data: report.report_data,
470+
};
471+
(base_body, [0u8; 48]) // SERVTD_HASH always zero for MigTD
472+
};
473+
474+
// Create TD report with values from parsed quote body
475+
let td_report = TdxReport {
476+
report_mac: ReportMac {
477+
report_type: ReportType {
478+
r#type: tee_type as u8,
479+
subtype: 0x00,
480+
version: header_version as u8,
481+
reserved: 0x00,
482+
},
483+
reserved0: [0u8; 12],
484+
cpu_svn: report_body.tee_tcb_svn,
485+
tee_tcb_info_hash: [0x42; 48], // Mock hash (not in quote)
486+
tee_info_hash: [0x43; 48], // Mock hash (not in quote)
487+
report_data: report_body.report_data,
488+
reserved1: [0u8; 32],
489+
mac: [0xBB; 32], // Mock MAC, not used for policy tests
490+
},
491+
tee_tcb_info: TeeTcbInfo {
492+
valid: [0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
493+
tee_tcb_svn: report_body.tee_tcb_svn,
494+
mrseam: report_body.mr_seam,
495+
mrsigner_seam: report_body.mrsigner_seam,
496+
attributes: report_body.seam_attributes,
497+
reserved: [0u8; 111],
498+
},
499+
reserved: [0u8; 17],
500+
td_info: TdInfo {
501+
attributes: report_body.td_attributes,
502+
xfam: report_body.xfam,
503+
mrtd: report_body.mr_td,
504+
mrconfig_id: report_body.mr_config_id,
505+
mrowner: report_body.mr_owner,
506+
mrownerconfig: report_body.mr_owner_config,
507+
rtmr0: report_body.rt_mr[0],
508+
rtmr1: report_body.rt_mr[1],
509+
rtmr2: report_body.rt_mr[2],
510+
rtmr3: report_body.rt_mr[3],
511+
servtd_hash: servtd_hash,
512+
reserved: [0u8; 64],
513+
},
514+
};
515+
516+
debug!("Mock TD report created successfully from quote file");
517+
518+
// Convert to az-tdx-vtpm TdReport for compatibility
519+
unsafe { core::mem::transmute(td_report) }
520+
}
521+
522+
#[cfg(any(feature = "test_mock_report", feature = "mock_report_tools"))]
523+
pub fn create_mock_quote(_td_report_data: &[u8]) -> Vec<u8> {
524+
// Check if a custom quote file is specified
525+
if let Ok(quote_file_path) = std::env::var("MOCK_QUOTE_FILE") {
526+
return create_td_quote_from_file(quote_file_path);
527+
}
528+
// No custom quote file - use hardcoded default quote
529+
debug!("Creating mock TD quote with hardcoded data");
290530
// Using actual quote data captured from Azure CVM environment
291531
let quote = [
292532
0x04, 0x00, 0x02, 0x00, 0x81, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x9a, 0x72, 0x33, 0xf7, 0x9c, 0x4c, 0xa9, 0x94, 0x0a, 0x0d, 0xb3, 0x95, 0x7f, 0x06, 0x07, 0xe3, 0xe9, 0x78, 0xc1, 0x07, 0x97, 0xc3, 0x24, 0xb3, 0xea, 0xac, 0x4a, 0xd8, 0xa6, 0xdd, 0x8e, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
@@ -372,3 +612,23 @@ pub fn create_mock_quote(td_report_data: &[u8]) -> Vec<u8> {
372612
debug!("Mock quote created with size: {}", quote.len());
373613
quote.to_vec()
374614
}
615+
616+
#[cfg(any(feature = "test_mock_report", feature = "mock_report_tools"))]
617+
fn create_td_quote_from_file(quote_file_path: String) -> Vec<u8> {
618+
debug!("Creating TD quote from file: {}", quote_file_path);
619+
620+
match std::fs::read(&quote_file_path) {
621+
Ok(quote) => {
622+
debug!("Successfully loaded mock quote from {} ({} bytes)", quote_file_path, quote.len());
623+
quote
624+
}
625+
Err(e) => {
626+
error!("Failed to load mock quote from {}: {:?}", quote_file_path, e);
627+
if let Ok(cwd) = std::env::current_dir() {
628+
error!("Current working directory: {:?}", cwd);
629+
error!("Hint: Use absolute path or ensure the path is relative to: {:?}", cwd);
630+
}
631+
Vec::new()
632+
}
633+
}
634+
}

0 commit comments

Comments
 (0)