44
55use anyhow:: { anyhow, bail, Context , Result } ;
66use hex_literal:: hex;
7+ use scale:: Decode ;
78use sha2:: { Digest , Sha384 } ;
89
910use crate :: acpi:: Tables ;
@@ -24,7 +25,13 @@ pub enum PageAddOrder {
2425 SinglePass ,
2526}
2627
27- #[ derive( Debug ) ]
28+ /// Helper to decode little-endian integers from byte slice using scale codec
29+ fn decode_le < T : Decode > ( data : & [ u8 ] , context : & str ) -> Result < T > {
30+ T :: decode ( & mut & data[ ..] )
31+ . with_context ( || format ! ( "failed to decode {} as little-endian" , context) )
32+ }
33+
34+ #[ derive( Debug , Decode ) ]
2835struct TdvfSection {
2936 data_offset : u32 ,
3037 raw_data_size : u32 ,
@@ -34,6 +41,14 @@ struct TdvfSection {
3441 attributes : u32 ,
3542}
3643
44+ #[ derive( Debug , Decode ) ]
45+ struct TdvfDescriptor {
46+ signature : [ u8 ; 4 ] , // "TDVF"
47+ _length : u32 ,
48+ version : u32 ,
49+ num_sections : u32 ,
50+ }
51+
3752#[ derive( Debug ) ]
3853pub ( crate ) struct Tdvf < ' a > {
3954 fw : & ' a [ u8 ] ,
@@ -77,6 +92,11 @@ fn measure_tdx_efi_variable(vendor_guid: &str, var_name: &str) -> Result<Vec<u8>
7792}
7893
7994impl < ' a > Tdvf < ' a > {
95+ /// Parse TDVF firmware metadata
96+ ///
97+ /// This function uses scale codec for clean, panic-free parsing.
98+ /// Correctness is verified by integration test in tests/tdvf_parse.rs
99+ /// which ensures identical measurements to the original implementation.
80100 pub fn parse ( fw : & ' a [ u8 ] ) -> Result < Tdvf < ' a > > {
81101 const TDX_METADATA_OFFSET_GUID : & str = "e47a6535-984a-4798-865e-4685a7bf8ec2" ;
82102 const TABLE_FOOTER_GUID : & str = "96b582de-1fb2-45f7-baea-a366c55a082d" ;
@@ -99,8 +119,7 @@ impl<'a> Tdvf<'a> {
99119 if offset < 18 {
100120 bail ! ( "TDVF firmware offset too small for tables length" ) ;
101121 }
102- let tables_len =
103- u16:: from_le_bytes ( fw[ offset - 18 ..offset - 16 ] . try_into ( ) . unwrap ( ) ) as usize ;
122+ let tables_len = decode_le :: < u16 > ( & fw[ offset - 18 ..offset - 16 ] , "tables length" ) ? as usize ;
104123 if tables_len == 0 || tables_len > offset. saturating_sub ( 18 ) {
105124 bail ! ( "Failed to parse TDVF metadata: Invalid tables length" ) ;
106125 }
@@ -133,37 +152,34 @@ impl<'a> Tdvf<'a> {
133152 bail ! ( "TDVF metadata data too small" ) ;
134153 }
135154 let tdvf_meta_offset_raw =
136- u32 :: from_le_bytes ( data[ data. len ( ) - 4 ..] . try_into ( ) . unwrap ( ) ) as usize ;
155+ decode_le :: < u32 > ( & data[ data. len ( ) - 4 ..] , "TDVF metadata offset" ) ? as usize ;
137156 if tdvf_meta_offset_raw > fw. len ( ) {
138157 bail ! ( "TDVF metadata offset exceeds firmware size" ) ;
139158 }
140159 let tdvf_meta_offset = fw. len ( ) - tdvf_meta_offset_raw;
141- let tdvf_meta_desc = & fw[ tdvf_meta_offset..tdvf_meta_offset + 16 ] ;
142160
143- if & tdvf_meta_desc[ ..4 ] != b"TDVF" {
161+ // Decode TDVF descriptor using scale codec
162+ let descriptor = TdvfDescriptor :: decode ( & mut & fw[ tdvf_meta_offset..] )
163+ . context ( "failed to decode TDVF descriptor" ) ?;
164+
165+ if & descriptor. signature != b"TDVF" {
144166 bail ! ( "Failed to parse TDVF metadata: Invalid TDVF descriptor" ) ;
145167 }
146- let tdvf_version = u32:: from_le_bytes ( tdvf_meta_desc[ 8 ..12 ] . try_into ( ) . unwrap ( ) ) ;
147- if tdvf_version != 1 {
168+ if descriptor. version != 1 {
148169 bail ! ( "Failed to parse TDVF metadata: Unsupported TDVF version" ) ;
149170 }
150- let num_sections = u32 :: from_le_bytes ( tdvf_meta_desc [ 12 .. 16 ] . try_into ( ) . unwrap ( ) ) as usize ;
171+ let num_sections = descriptor . num_sections as usize ;
151172
152173 let mut meta = Tdvf {
153174 fw,
154175 sections : Vec :: new ( ) ,
155176 } ;
177+
178+ // Decode all sections using scale codec
156179 for i in 0 ..num_sections {
157180 let sec_offset = tdvf_meta_offset + 16 + 32 * i;
158- let sec_data = & fw[ sec_offset..sec_offset + 32 ] ;
159- let s = TdvfSection {
160- data_offset : u32:: from_le_bytes ( sec_data[ 0 ..4 ] . try_into ( ) . unwrap ( ) ) ,
161- raw_data_size : u32:: from_le_bytes ( sec_data[ 4 ..8 ] . try_into ( ) . unwrap ( ) ) ,
162- memory_address : u64:: from_le_bytes ( sec_data[ 8 ..16 ] . try_into ( ) . unwrap ( ) ) ,
163- memory_data_size : u64:: from_le_bytes ( sec_data[ 16 ..24 ] . try_into ( ) . unwrap ( ) ) ,
164- sec_type : u32:: from_le_bytes ( sec_data[ 24 ..28 ] . try_into ( ) . unwrap ( ) ) ,
165- attributes : u32:: from_le_bytes ( sec_data[ 28 ..32 ] . try_into ( ) . unwrap ( ) ) ,
166- } ;
181+ let s = TdvfSection :: decode ( & mut & fw[ sec_offset..] )
182+ . with_context ( || format ! ( "failed to decode TDVF section {}" , i) ) ?;
167183
168184 if s. memory_address % PAGE_SIZE != 0 {
169185 bail ! ( "Failed to parse TDVF metadata: Section memory address not aligned" ) ;
0 commit comments