@@ -13,7 +13,6 @@ use az_tdx_vtpm::tdx;
1313#[ cfg( not( feature = "test_mock_report" ) ) ]
1414use az_tdx_vtpm:: { hcl, imds, vtpm} ;
1515use log:: { debug, info} ;
16- #[ cfg( not( feature = "test_mock_report" ) ) ]
1716use log:: error;
1817use 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" ) ) ]
198197pub 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