1
1
use 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 } ;
3
3
use serde:: { Deserialize , Deserializer , Serialize } ;
4
4
use serde_json:: value:: RawValue ;
5
5
use std:: marker:: PhantomData ;
@@ -71,6 +71,84 @@ impl<V: VerificationType> TcbEvaluationDataNumbers<V> {
71
71
}
72
72
}
73
73
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
+
74
152
#[ derive( Clone , Debug , PartialEq , Eq , Serialize , Deserialize ) ]
75
153
#[ serde( rename_all = "camelCase" ) ]
76
154
pub struct TcbEvalNumber {
@@ -282,13 +360,21 @@ impl TcbPolicy {
282
360
}
283
361
}
284
362
285
- #[ cfg( all( not( target_env = "sgx" ) , feature = "verify" ) ) ]
286
363
#[ cfg( test) ]
287
364
mod 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" ) ) ]
290
375
use chrono:: { Duration , TimeZone , Utc } ;
291
376
377
+ #[ cfg( all( not( target_env = "sgx" ) , feature = "verify" ) ) ]
292
378
#[ test]
293
379
fn parse_tcb_evaluation_data_numbers ( ) {
294
380
let numbers = RawTcbEvaluationDataNumbers :: read_from_file ( "./tests/data" ) . unwrap ( ) ;
@@ -297,6 +383,7 @@ mod tests {
297
383
numbers. verify_ex ( & root_certificates, Platform :: SGX , & Utc . with_ymd_and_hms ( 2025 , 6 , 4 , 12 , 0 , 0 ) . unwrap ( ) ) . unwrap ( ) ;
298
384
}
299
385
386
+ #[ cfg( all( not( target_env = "sgx" ) , feature = "verify" ) ) ]
300
387
#[ test]
301
388
fn parse_tcb_evaluation_data_numbers_incorrect_signature ( ) {
302
389
let numbers = RawTcbEvaluationDataNumbers :: read_from_file ( "./tests/data" ) . unwrap ( ) ;
@@ -313,6 +400,7 @@ mod tests {
313
400
}
314
401
}
315
402
403
+ #[ cfg( all( not( target_env = "sgx" ) , feature = "verify" ) ) ]
316
404
#[ test]
317
405
fn tcb_eval_number ( ) {
318
406
let april_8_2025 = Utc . with_ymd_and_hms ( 2025 , 4 , 8 , 14 , 55 , 0 ) . unwrap ( ) ;
@@ -334,6 +422,7 @@ mod tests {
334
422
assert ! ( policy. needs_to_be_enforced( & number, & april_17_2025) ) ;
335
423
}
336
424
425
+ #[ cfg( all( not( target_env = "sgx" ) , feature = "verify" ) ) ]
337
426
#[ test]
338
427
fn minimum_tcb_evaluation_data_number ( ) {
339
428
let numbers = RawTcbEvaluationDataNumbers :: read_from_file ( "./tests/data" ) . unwrap ( ) ;
@@ -363,4 +452,35 @@ mod tests {
363
452
assert_eq ! ( policy. minimum_tcb_evaluation_data_number_ex( & numbers, & november_20_2024) ,
364
453
Some ( number_17) ) ;
365
454
}
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
+ }
366
486
}
0 commit comments