11use chrono:: { DateTime , Duration , Utc } ;
2- use crate :: { io, Error , Platform , Unverified , VerificationType , Verified } ;
2+ use crate :: { io, Error , Fmspc , Platform , pckcrt :: TcbComponents , QeIdentity , QeIdentitySigned , TcbData , TcbInfo , TcbStatus , Unverified , VerificationType , Verified } ;
33use serde:: { Deserialize , Deserializer , Serialize } ;
44use serde_json:: value:: RawValue ;
55use std:: marker:: PhantomData ;
@@ -71,6 +71,84 @@ impl<V: VerificationType> TcbEvaluationDataNumbers<V> {
7171 }
7272}
7373
74+ impl TcbEvaluationDataNumbers < Unverified > {
75+ /// Given a particular TCB level, select the best available TCB eval number.
76+ /// That is the one that gives the most favorable TCB status, and the higher
77+ /// one if there's a tie.
78+ pub fn select_best ( input_dir : & str , fmspc : & Fmspc , tcb_components : & TcbComponents , qesvn : u16 ) -> Result < TcbEvalNumber , Error > {
79+ let evalnums = RawTcbEvaluationDataNumbers :: read_from_file ( input_dir) ?. evaluation_data_numbers ( ) ?;
80+ let mut tcb_levels: std:: collections:: HashMap < _ , _ > = evalnums. numbers ( ) . map ( |num| ( num. number as u64 , ( num, None , None ) ) ) . collect ( ) ;
81+
82+ for tcbinfo in TcbInfo :: read_all ( input_dir, fmspc) {
83+ let tcb_data = TcbData :: parse ( tcbinfo?. raw_tcb_info ( ) ) ?;
84+ if let Some ( level) = tcb_data. tcb_levels ( )
85+ . iter ( )
86+ . find ( |level| level. tcb <= * tcb_components)
87+ {
88+ if let Some ( entry) = tcb_levels. get_mut ( & tcb_data. tcb_evaluation_data_number ( ) ) {
89+ entry. 1 = Some ( level. tcb_status ) ;
90+ }
91+ }
92+ } ;
93+
94+ for qeid in QeIdentitySigned :: read_all ( input_dir) {
95+ let qeid: QeIdentity :: < Unverified > = serde_json:: from_str ( & qeid?. raw_qe_identity ( ) ) . map_err ( |e| Error :: ParseError ( e) ) ?;
96+ if let Some ( level) = qeid. tcb_levels ( )
97+ . iter ( )
98+ . find ( |level| level. tcb . isvsvn <= qesvn)
99+ {
100+ if let Some ( entry) = tcb_levels. get_mut ( & qeid. tcb_evaluation_data_number ( ) ) {
101+ entry. 2 = Some ( level. tcb_status ) ;
102+ }
103+ }
104+ } ;
105+
106+ // NB: QE Identity TCB status can only be UpToDate, OutOfDate, or Revoked
107+ fn tcb_total_order ( platform_status : Option < TcbStatus > , qe_status : Option < TcbStatus > ) -> i8 {
108+ use std:: ops:: Neg ;
109+ use self :: TcbStatus :: * ;
110+ // Since we don't have any information here to judge the enclave
111+ // has the needed SW hardening, we assume that it does and we
112+ // upgrade SWHardeningNeeded to the next level
113+ match ( platform_status. map ( TcbStatus :: drop_sw_hardening_needed) , qe_status) {
114+ ( Some ( UpToDate ) , Some ( UpToDate ) ) => 0i8 ,
115+ ( Some ( UpToDate ) , Some ( OutOfDate ) ) => 1 ,
116+ ( Some ( UpToDate ) , Some ( Revoked ) ) => 1 ,
117+ ( Some ( ConfigurationNeeded ) , Some ( UpToDate ) ) => 2 ,
118+ ( Some ( ConfigurationNeeded ) , Some ( OutOfDate ) ) => 3 ,
119+ ( Some ( ConfigurationNeeded ) , Some ( Revoked ) ) => 3 ,
120+ ( Some ( OutOfDate ) , Some ( UpToDate ) ) => 4 ,
121+ ( Some ( OutOfDateConfigurationNeeded ) , Some ( UpToDate ) ) => 4 ,
122+ ( Some ( Revoked ) , Some ( UpToDate ) ) => 4 ,
123+ ( Some ( OutOfDate ) , Some ( OutOfDate ) ) => 5 ,
124+ ( Some ( OutOfDate ) , Some ( Revoked ) ) => 5 ,
125+ ( Some ( Revoked ) , Some ( OutOfDate ) ) => 5 ,
126+ ( Some ( Revoked ) , Some ( Revoked ) ) => 5 ,
127+ ( Some ( OutOfDateConfigurationNeeded ) , Some ( OutOfDate ) ) => 5 ,
128+ ( Some ( OutOfDateConfigurationNeeded ) , Some ( Revoked ) ) => 5 ,
129+ ( Some ( UpToDate ) , None ) => 6 ,
130+ ( Some ( ConfigurationNeeded ) , None ) => 7 ,
131+ ( Some ( OutOfDate ) , None ) => 8 ,
132+ ( Some ( OutOfDateConfigurationNeeded ) , None ) => 8 ,
133+ ( Some ( Revoked ) , None ) => 8 ,
134+ ( None , Some ( UpToDate ) ) => 9 ,
135+ ( None , Some ( OutOfDate ) ) => 10 ,
136+ ( None , Some ( Revoked ) ) => 10 ,
137+ _ => 11 ,
138+ } . neg ( )
139+ }
140+
141+ tcb_levels. into_iter ( )
142+ . max_by ( |& ( a_num, ( _, a_platform_status, a_qe_status) ) , & ( b_num, ( _, b_platform_status, b_qe_status) ) | {
143+ tcb_total_order ( a_platform_status, a_qe_status)
144+ . cmp ( & tcb_total_order ( b_platform_status, b_qe_status) )
145+ . then_with ( || a_num. cmp ( & b_num) )
146+ } )
147+ . map ( |( _, ( num, _, _) ) | num. clone ( ) )
148+ . ok_or ( Error :: InvalidTcbEvaluationDataNumbers ( "Empty TCB evaluation data numbers" . into ( ) ) )
149+ }
150+ }
151+
74152#[ derive( Clone , Debug , PartialEq , Eq , Serialize , Deserialize ) ]
75153#[ serde( rename_all = "camelCase" ) ]
76154pub struct TcbEvalNumber {
@@ -282,13 +360,21 @@ impl TcbPolicy {
282360 }
283361}
284362
285- #[ cfg( all( not( target_env = "sgx" ) , feature = "verify" ) ) ]
286363#[ cfg( test) ]
287364mod tests {
288- use super :: { RawTcbEvaluationDataNumbers , TcbEvaluationDataNumbers , TcbEvalNumber , TcbPolicy } ;
289- use crate :: { Error , Platform , Unverified } ;
365+ #[ cfg( not( target_env = "sgx" ) ) ]
366+ use {
367+ super :: TcbEvaluationDataNumbers ,
368+ crate :: { Error , Unverified }
369+ } ;
370+ #[ cfg( all( not( target_env = "sgx" ) , feature = "verify" ) ) ]
371+ use super :: { RawTcbEvaluationDataNumbers , TcbPolicy , TcbEvalNumber } ;
372+ #[ cfg( all( not( target_env = "sgx" ) , feature = "verify" ) ) ]
373+ use crate :: Platform ;
374+ #[ cfg( all( not( target_env = "sgx" ) , feature = "verify" ) ) ]
290375 use chrono:: { Duration , TimeZone , Utc } ;
291376
377+ #[ cfg( all( not( target_env = "sgx" ) , feature = "verify" ) ) ]
292378 #[ test]
293379 fn parse_tcb_evaluation_data_numbers ( ) {
294380 let numbers = RawTcbEvaluationDataNumbers :: read_from_file ( "./tests/data" ) . unwrap ( ) ;
@@ -297,6 +383,7 @@ mod tests {
297383 numbers. verify_ex ( & root_certificates, Platform :: SGX , & Utc . with_ymd_and_hms ( 2025 , 6 , 4 , 12 , 0 , 0 ) . unwrap ( ) ) . unwrap ( ) ;
298384 }
299385
386+ #[ cfg( all( not( target_env = "sgx" ) , feature = "verify" ) ) ]
300387 #[ test]
301388 fn parse_tcb_evaluation_data_numbers_incorrect_signature ( ) {
302389 let numbers = RawTcbEvaluationDataNumbers :: read_from_file ( "./tests/data" ) . unwrap ( ) ;
@@ -313,6 +400,7 @@ mod tests {
313400 }
314401 }
315402
403+ #[ cfg( all( not( target_env = "sgx" ) , feature = "verify" ) ) ]
316404 #[ test]
317405 fn tcb_eval_number ( ) {
318406 let april_8_2025 = Utc . with_ymd_and_hms ( 2025 , 4 , 8 , 14 , 55 , 0 ) . unwrap ( ) ;
@@ -334,6 +422,7 @@ mod tests {
334422 assert ! ( policy. needs_to_be_enforced( & number, & april_17_2025) ) ;
335423 }
336424
425+ #[ cfg( all( not( target_env = "sgx" ) , feature = "verify" ) ) ]
337426 #[ test]
338427 fn minimum_tcb_evaluation_data_number ( ) {
339428 let numbers = RawTcbEvaluationDataNumbers :: read_from_file ( "./tests/data" ) . unwrap ( ) ;
@@ -363,4 +452,35 @@ mod tests {
363452 assert_eq ! ( policy. minimum_tcb_evaluation_data_number_ex( & numbers, & november_20_2024) ,
364453 Some ( number_17) ) ;
365454 }
455+
456+ #[ cfg( not( target_env = "sgx" ) ) ]
457+ #[ test]
458+ fn select_best ( ) {
459+ use crate :: pckcrt:: TcbComponents ;
460+ fn select ( tcb_components : & TcbComponents , qesvn : u16 ) -> Result < u16 , Error > {
461+ use std:: convert:: TryInto ;
462+ TcbEvaluationDataNumbers :: < Unverified > :: select_best ( "./tests/data/eval-num-select-best" , & "00606a000000" . try_into ( ) . unwrap ( ) , tcb_components, qesvn)
463+ . map ( |num| num. number )
464+ }
465+ // platform and QE are nonsensical: just choose highest
466+ assert_eq ! ( select( & TcbComponents :: from_raw( [ 0 ; 16 ] , 0 ) , 0 ) . unwrap( ) , 19 ) ;
467+ // platform is nonsensical: choose eval nums based on QE up-to-date
468+ assert_eq ! ( select( & TcbComponents :: from_raw( [ 0 ; 16 ] , 0 ) , 8 ) . unwrap( ) , 19 ) ;
469+ assert_eq ! ( select( & TcbComponents :: from_raw( [ 0 ; 16 ] , 0 ) , 6 ) . unwrap( ) , 8 ) ;
470+ // QE is nonsensical: choose eval nums based on platform up-to-date
471+ assert_eq ! ( select( & TcbComponents :: from_raw( [ 16 , 16 , 3 , 3 , 255 , 255 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , 13 ) , 0 ) . unwrap( ) , 19 ) ;
472+ assert_eq ! ( select( & TcbComponents :: from_raw( [ 15 , 16 , 3 , 3 , 255 , 255 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , 13 ) , 0 ) . unwrap( ) , 18 ) ;
473+ assert_eq ! ( select( & TcbComponents :: from_raw( [ 14 , 16 , 3 , 3 , 255 , 255 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , 13 ) , 0 ) . unwrap( ) , 17 ) ;
474+ assert_eq ! ( select( & TcbComponents :: from_raw( [ 7 , 16 , 3 , 3 , 255 , 255 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , 13 ) , 0 ) . unwrap( ) , 14 ) ;
475+ // platform and QE are fully up to date: choose highest
476+ assert_eq ! ( select( & TcbComponents :: from_raw( [ 16 , 16 , 3 , 3 , 255 , 255 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , 13 ) , 8 ) . unwrap( ) , 19 ) ;
477+ // QE is up to date: choose up-to-date eval nums based on platform
478+ assert_eq ! ( select( & TcbComponents :: from_raw( [ 15 , 16 , 3 , 3 , 255 , 255 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , 13 ) , 8 ) . unwrap( ) , 18 ) ;
479+ assert_eq ! ( select( & TcbComponents :: from_raw( [ 14 , 16 , 3 , 3 , 255 , 255 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , 13 ) , 8 ) . unwrap( ) , 17 ) ;
480+ assert_eq ! ( select( & TcbComponents :: from_raw( [ 7 , 16 , 3 , 3 , 255 , 255 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , 13 ) , 8 ) . unwrap( ) , 8 ) ;
481+ // platform is up to date: choose up-to-date eval nums based on QE
482+ assert_eq ! ( select( & TcbComponents :: from_raw( [ 16 , 16 , 3 , 3 , 255 , 255 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , 13 ) , 6 ) . unwrap( ) , 8 ) ;
483+ // neither platform and QE are up to date: choose highest eval nums where they were both up to date
484+ assert_eq ! ( select( & TcbComponents :: from_raw( [ 4 , 16 , 3 , 3 , 255 , 255 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ] , 13 ) , 5 ) . unwrap( ) , 8 ) ;
485+ }
366486}
0 commit comments