Skip to content

Commit befd2df

Browse files
authored
Merge pull request #82 from Phala-Network/reject-unknown
Add rejection for Revoked TCB status
2 parents d7a739f + 19743ce commit befd2df

File tree

11 files changed

+193
-118
lines changed

11 files changed

+193
-118
lines changed

cli/src/bin/generate_all_samples.rs

Lines changed: 60 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1878,13 +1878,13 @@ fn main() -> Result<()> {
18781878
name: "unknown_tcb_status".to_string(),
18791879
description: "Quote with no matching TCB level (empty tcbLevels array)".to_string(),
18801880
should_succeed: false,
1881-
expected_error: Some("TCB status is Unknown".to_string()),
1881+
expected_error: Some("No matching TCB level found".to_string()),
18821882
quote_generator: Box::new(|| generate_base_quote(3, 2, false)),
18831883
collateral_modifier: Some(Box::new(|collateral| {
18841884
// Use empty tcbLevels array - no matching level will be found
18851885
if let Some(tcb_str) = collateral["tcb_info"].as_str() {
18861886
if let Ok(mut tcb) = serde_json::from_str::<serde_json::Value>(tcb_str) {
1887-
tcb["tcbLevels"] = json!([]); // Empty array
1887+
tcb["tcbLevels"] = json!([]); // Empty array
18881888
let new_tcb_info = serde_json::to_string(&tcb)?;
18891889

18901890
let key_path = &format!("{}/tcb_signing.pkcs8.key", CERT_DIR);
@@ -1899,6 +1899,64 @@ fn main() -> Result<()> {
18991899
})),
19001900
});
19011901

1902+
samples.push(TestSample {
1903+
name: "revoked_platform_tcb".to_string(),
1904+
description: "Quote with Revoked platform TCB status".to_string(),
1905+
should_succeed: false,
1906+
expected_error: Some("TCB status is invalid: Revoked".to_string()),
1907+
quote_generator: Box::new(|| generate_base_quote(3, 2, false)),
1908+
collateral_modifier: Some(Box::new(|collateral| {
1909+
// Set platform TCB status to Revoked
1910+
if let Some(tcb_str) = collateral["tcb_info"].as_str() {
1911+
if let Ok(mut tcb) = serde_json::from_str::<serde_json::Value>(tcb_str) {
1912+
if let Some(levels) = tcb["tcbLevels"].as_array_mut() {
1913+
for level in levels {
1914+
level["tcbStatus"] = json!("Revoked");
1915+
}
1916+
}
1917+
let new_tcb_info = serde_json::to_string(&tcb)?;
1918+
1919+
let key_path = &format!("{}/tcb_signing.pkcs8.key", CERT_DIR);
1920+
let key_pair = load_private_key(key_path)?;
1921+
let tcb_signature = sign_data(&key_pair, new_tcb_info.as_bytes())?;
1922+
1923+
collateral["tcb_info"] = json!(new_tcb_info);
1924+
collateral["tcb_info_signature"] = json!(hex::encode(tcb_signature));
1925+
}
1926+
}
1927+
Ok(())
1928+
})),
1929+
});
1930+
1931+
samples.push(TestSample {
1932+
name: "revoked_qe_tcb".to_string(),
1933+
description: "Quote with Revoked QE TCB status".to_string(),
1934+
should_succeed: false,
1935+
expected_error: Some("TCB status is invalid: Revoked".to_string()),
1936+
quote_generator: Box::new(|| generate_base_quote(3, 2, false)),
1937+
collateral_modifier: Some(Box::new(|collateral| {
1938+
// Set QE TCB status to Revoked
1939+
if let Some(qe_str) = collateral["qe_identity"].as_str() {
1940+
if let Ok(mut qe_identity) = serde_json::from_str::<serde_json::Value>(qe_str) {
1941+
if let Some(levels) = qe_identity["tcbLevels"].as_array_mut() {
1942+
for level in levels {
1943+
level["tcbStatus"] = json!("Revoked");
1944+
}
1945+
}
1946+
let new_qe_identity = serde_json::to_string(&qe_identity)?;
1947+
1948+
let key_path = &format!("{}/tcb_signing.pkcs8.key", CERT_DIR);
1949+
let key_pair = load_private_key(key_path)?;
1950+
let qe_signature = sign_data(&key_pair, new_qe_identity.as_bytes())?;
1951+
1952+
collateral["qe_identity"] = json!(new_qe_identity);
1953+
collateral["qe_identity_signature"] = json!(hex::encode(qe_signature));
1954+
}
1955+
}
1956+
Ok(())
1957+
})),
1958+
});
1959+
19021960
// Category 13: PCK certificate chain in collateral (cert_type 3 support)
19031961
println!("\nCategory 13: PCK certificate chain in collateral");
19041962

cli/src/bin/test_case.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ fn run_verify(quote_file: PathBuf, collateral_file: PathBuf, root_ca_file: Optio
109109
match verifier.verify(&quote_bytes, &collateral, now) {
110110
Ok(verified_report) => {
111111
println!("Verification successful");
112-
println!("Status: {}", verified_report.status);
112+
println!("Status: {:?}", verified_report.status);
113113
0
114114
}
115115
Err(e) => {

dcap-qvl-js/src/tcb_info.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,23 @@ class TcbStatus {
9090
return new TcbStatus('Unknown', []);
9191
}
9292

93+
// Check if the TCB status is valid (not Revoked)
94+
isValid() {
95+
switch (this.status) {
96+
case 'UpToDate':
97+
case 'SWHardeningNeeded':
98+
case 'ConfigurationNeeded':
99+
case 'ConfigurationAndSWHardeningNeeded':
100+
case 'OutOfDate':
101+
case 'OutOfDateConfigurationNeeded':
102+
return true;
103+
case 'Revoked':
104+
return false;
105+
default:
106+
return false; // Unknown or other statuses are invalid
107+
}
108+
}
109+
93110
// Merge two TCB statuses, taking the worse status and combining advisory IDs
94111
merge(other) {
95112
const finalStatus = tcbStatusSeverity(other.status) > tcbStatusSeverity(this.status)

dcap-qvl-js/src/verify.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -293,9 +293,9 @@ function verifyImpl(rawQuote, collateral, nowSecs, rootCaDer) {
293293
// Step 9 & 10: QE TCB matching is done in verifyQeIdentityPolicy, merge statuses
294294
const finalStatus = platformTcbStatus.merge(qeTcbStatus);
295295

296-
// Reject Unknown TCB status
297-
if (finalStatus.status === 'Unknown') {
298-
throw new Error('TCB status is Unknown - no matching TCB level found');
296+
// Reject invalid TCB status (including Revoked)
297+
if (!finalStatus.isValid()) {
298+
throw new Error(`TCB status is invalid: ${finalStatus.status}`);
299299
}
300300

301301
// Validate attributes
@@ -343,7 +343,7 @@ function matchPlatformTcb(tcbInfo, quote, cpuSvn, pceSvn) {
343343
return new TcbStatus(tcbLevel.tcbStatus, [...tcbLevel.advisoryIDs]);
344344
}
345345

346-
return TcbStatus.unknown();
346+
throw new Error('No matching TCB level found');
347347
}
348348

349349
// Step 6 & 9: Verify QE Report policy and match QE TCB
@@ -398,7 +398,7 @@ function matchQeTcbLevel(isvSvn, tcbLevels) {
398398

399399
// No matching level found
400400
if (tcbLevels.length === 0) {
401-
return TcbStatus.unknown();
401+
throw new Error('No TCB levels found in QE Identity');
402402
}
403403

404404
// ISVSVN is below all defined TCB levels

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ pub mod oids;
8484
mod constants;
8585
pub mod intel;
8686
mod qe_identity;
87-
mod tcb_info;
87+
pub mod tcb_info;
8888
mod utils;
8989

9090
pub mod quote;

src/python.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use serde_json;
77
use crate::{
88
collateral::get_collateral_for_fmspc,
99
quote::Quote,
10+
tcb_info::TcbStatus,
1011
verify::{verify, VerifiedReport},
1112
QuoteCollateralV3,
1213
};
@@ -115,7 +116,15 @@ pub struct PyVerifiedReport {
115116
impl PyVerifiedReport {
116117
#[getter]
117118
fn status(&self) -> &str {
118-
&self.inner.status
119+
match self.inner.status {
120+
TcbStatus::UpToDate => "UpToDate",
121+
TcbStatus::OutOfDateConfigurationNeeded => "OutOfDateConfigurationNeeded",
122+
TcbStatus::OutOfDate => "OutOfDate",
123+
TcbStatus::ConfigurationAndSWHardeningNeeded => "ConfigurationAndSWHardeningNeeded",
124+
TcbStatus::ConfigurationNeeded => "ConfigurationNeeded",
125+
TcbStatus::SWHardeningNeeded => "SWHardeningNeeded",
126+
TcbStatus::Revoked => "Revoked",
127+
}
119128
}
120129

121130
#[getter]

src/qe_identity.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use borsh::BorshSchema;
77
#[cfg(feature = "borsh")]
88
use borsh::{BorshDeserialize, BorshSerialize};
99

10+
use crate::tcb_info::TcbStatus;
11+
1012
/// QE Identity structure as returned by Intel's PCCS
1113
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize)]
1214
#[serde(rename_all = "camelCase")]
@@ -44,7 +46,7 @@ pub struct QeIdentity {
4446
pub struct QeTcbLevel {
4547
pub tcb: QeTcb,
4648
pub tcb_date: String,
47-
pub tcb_status: String,
49+
pub tcb_status: TcbStatus,
4850
#[serde(rename = "advisoryIDs", default)]
4951
pub advisory_ids: Vec<String>,
5052
}

src/tcb_info.rs

Lines changed: 61 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ pub struct TcbInfo {
3030
pub struct TcbLevel {
3131
pub tcb: Tcb,
3232
pub tcb_date: String,
33-
pub tcb_status: String,
33+
pub tcb_status: TcbStatus,
3434
#[serde(rename = "advisoryIDs", default)]
3535
pub advisory_ids: Vec<String>,
3636
}
@@ -56,43 +56,68 @@ pub struct TcbComponents {
5656
pub svn: u8,
5757
}
5858

59+
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Serialize, Deserialize)]
60+
#[cfg_attr(feature = "borsh", derive(BorshSerialize, BorshDeserialize))]
61+
#[cfg_attr(feature = "borsh_schema", derive(BorshSchema))]
62+
pub enum TcbStatus {
63+
UpToDate,
64+
OutOfDateConfigurationNeeded,
65+
OutOfDate,
66+
ConfigurationAndSWHardeningNeeded,
67+
ConfigurationNeeded,
68+
SWHardeningNeeded,
69+
Revoked,
70+
}
71+
72+
impl TcbStatus {
73+
fn severity(&self) -> u8 {
74+
match self {
75+
Self::UpToDate => 0,
76+
Self::SWHardeningNeeded => 1,
77+
Self::ConfigurationNeeded => 2,
78+
Self::ConfigurationAndSWHardeningNeeded => 3,
79+
Self::OutOfDate => 4,
80+
Self::OutOfDateConfigurationNeeded => 5,
81+
Self::Revoked => 6,
82+
}
83+
}
84+
85+
pub fn is_valid(&self) -> bool {
86+
match self {
87+
Self::UpToDate => true,
88+
Self::SWHardeningNeeded => true,
89+
Self::ConfigurationNeeded => true,
90+
Self::ConfigurationAndSWHardeningNeeded => true,
91+
Self::OutOfDate => true,
92+
Self::OutOfDateConfigurationNeeded => true,
93+
Self::Revoked => false,
94+
}
95+
}
96+
}
97+
5998
/// TCB status with advisory IDs
6099
///
61100
/// This is the result of matching a TCB level, used by both
62101
/// platform TCB matching and QE Identity verification.
63-
#[derive(Clone, Debug, Default)]
64-
pub struct TcbStatus {
65-
pub status: String,
102+
#[derive(Clone, Debug)]
103+
pub struct TcbStatusWithAdvisory {
104+
pub status: TcbStatus,
66105
pub advisory_ids: Vec<String>,
67106
}
68107

69-
impl TcbStatus {
108+
impl TcbStatusWithAdvisory {
70109
/// Create a new TcbStatus with the given status and advisory IDs
71-
pub fn new(status: impl Into<String>, advisory_ids: Vec<String>) -> Self {
110+
pub fn new(status: TcbStatus, advisory_ids: Vec<String>) -> Self {
72111
Self {
73-
status: status.into(),
112+
status,
74113
advisory_ids,
75114
}
76115
}
77116

78-
/// Create an unknown status (no matching TCB level found)
79-
pub fn unknown() -> Self {
80-
Self {
81-
status: "Unknown".into(),
82-
advisory_ids: vec![],
83-
}
84-
}
85-
86-
/// Check if the TCB status is unknown
87-
pub fn is_unknown(&self) -> bool {
88-
self.status == "Unknown"
89-
}
90-
91117
/// Merge two TCB statuses, taking the worse status and combining advisory IDs
92-
pub fn merge(self, other: &TcbStatus) -> Self {
93-
let final_status = if tcb_status_severity(&other.status) > tcb_status_severity(&self.status)
94-
{
95-
other.status.clone()
118+
pub fn merge(self, other: &TcbStatusWithAdvisory) -> Self {
119+
let final_status = if other.status.severity() > self.status.severity() {
120+
other.status
96121
} else {
97122
self.status
98123
};
@@ -111,48 +136,35 @@ impl TcbStatus {
111136
}
112137
}
113138

114-
/// TCB status severity ordering (higher number = worse status)
115-
fn tcb_status_severity(status: &str) -> u8 {
116-
match status {
117-
"UpToDate" => 0,
118-
"SWHardeningNeeded" => 1,
119-
"ConfigurationNeeded" => 2,
120-
"ConfigurationAndSWHardeningNeeded" => 3,
121-
"OutOfDate" => 4,
122-
"OutOfDateConfigurationNeeded" => 5,
123-
"Revoked" => 6,
124-
_ => 100, // Unknown status treated as worst
125-
}
126-
}
127-
128139
#[cfg(test)]
129140
mod tests {
130141
use super::*;
142+
use TcbStatus::*;
131143

132144
#[test]
133145
fn test_tcb_status_merge_both_up_to_date() {
134-
let a = TcbStatus::new("UpToDate", vec![]);
135-
let b = TcbStatus::new("UpToDate", vec![]);
146+
let a = TcbStatusWithAdvisory::new(UpToDate, vec![]);
147+
let b = TcbStatusWithAdvisory::new(UpToDate, vec![]);
136148
let result = a.merge(&b);
137-
assert_eq!(result.status, "UpToDate");
149+
assert_eq!(result.status, UpToDate);
138150
assert!(result.advisory_ids.is_empty());
139151
}
140152

141153
#[test]
142154
fn test_tcb_status_merge_takes_worse() {
143-
let a = TcbStatus::new("UpToDate", vec![]);
144-
let b = TcbStatus::new("OutOfDate", vec!["INTEL-SA-00001".into()]);
155+
let a = TcbStatusWithAdvisory::new(UpToDate, vec![]);
156+
let b = TcbStatusWithAdvisory::new(OutOfDate, vec!["INTEL-SA-00001".into()]);
145157
let result = a.merge(&b);
146-
assert_eq!(result.status, "OutOfDate");
158+
assert_eq!(result.status, OutOfDate);
147159
assert_eq!(result.advisory_ids, vec!["INTEL-SA-00001"]);
148160
}
149161

150162
#[test]
151163
fn test_tcb_status_merge_combines_advisories() {
152-
let a = TcbStatus::new("OutOfDate", vec!["INTEL-SA-00001".into()]);
153-
let b = TcbStatus::new("SWHardeningNeeded", vec!["INTEL-SA-00002".into()]);
164+
let a = TcbStatusWithAdvisory::new(OutOfDate, vec!["INTEL-SA-00001".into()]);
165+
let b = TcbStatusWithAdvisory::new(SWHardeningNeeded, vec!["INTEL-SA-00002".into()]);
154166
let result = a.merge(&b);
155-
assert_eq!(result.status, "OutOfDate");
167+
assert_eq!(result.status, OutOfDate);
156168
assert_eq!(
157169
result.advisory_ids,
158170
vec!["INTEL-SA-00001", "INTEL-SA-00002"]
@@ -161,31 +173,9 @@ mod tests {
161173

162174
#[test]
163175
fn test_tcb_status_merge_deduplicates_advisories() {
164-
let a = TcbStatus::new("OutOfDate", vec!["INTEL-SA-00001".into()]);
165-
let b = TcbStatus::new("OutOfDate", vec!["INTEL-SA-00001".into()]);
176+
let a = TcbStatusWithAdvisory::new(OutOfDate, vec!["INTEL-SA-00001".into()]);
177+
let b = TcbStatusWithAdvisory::new(OutOfDate, vec!["INTEL-SA-00001".into()]);
166178
let result = a.merge(&b);
167179
assert_eq!(result.advisory_ids, vec!["INTEL-SA-00001"]);
168180
}
169-
170-
#[test]
171-
fn test_tcb_status_severity_ordering() {
172-
assert!(tcb_status_severity("UpToDate") < tcb_status_severity("SWHardeningNeeded"));
173-
assert!(
174-
tcb_status_severity("SWHardeningNeeded") < tcb_status_severity("ConfigurationNeeded")
175-
);
176-
assert!(
177-
tcb_status_severity("ConfigurationNeeded")
178-
< tcb_status_severity("ConfigurationAndSWHardeningNeeded")
179-
);
180-
assert!(
181-
tcb_status_severity("ConfigurationAndSWHardeningNeeded")
182-
< tcb_status_severity("OutOfDate")
183-
);
184-
assert!(
185-
tcb_status_severity("OutOfDate") < tcb_status_severity("OutOfDateConfigurationNeeded")
186-
);
187-
assert!(
188-
tcb_status_severity("OutOfDateConfigurationNeeded") < tcb_status_severity("Revoked")
189-
);
190-
}
191181
}

0 commit comments

Comments
 (0)