oasis_core_runtime/common/sgx/pcs/
mod.rs1mod certificates;
4mod constants;
5mod policy;
6mod quote;
7mod report;
8mod tcb;
9mod utils;
10
11#[derive(Debug, thiserror::Error)]
13pub enum Error {
14 #[error("unsupported QE vendor")]
15 UnsupportedQEVendor,
16 #[error("unsupported attestation key type")]
17 UnsupportedAttestationKeyType,
18 #[error("unsupported TEE type")]
19 UnsupportedTeeType,
20 #[error("failed to parse quote: {0}")]
21 QuoteParseError(String),
22 #[error("failed to verify quote: {0}")]
23 VerificationFailed(String),
24 #[error("unexpected certificate chain")]
25 UnexpectedCertificateChain,
26 #[error("unexpected certification data")]
27 UnexpectedCertificationData,
28 #[error("malformed certification data")]
29 MalformedCertificationData,
30 #[error("PCK is malformed")]
31 MalformedPCK,
32 #[error("failed to parse TCB bundle: {0}")]
33 TCBParseError(anyhow::Error),
34 #[error("TCB verification failed")]
35 TCBVerificationFailed,
36 #[error("TCB is expired or not yet valid")]
37 TCBExpired,
38 #[error("TCB is out of date")]
39 TCBOutOfDate,
40 #[error("TCB does not match the quote")]
41 TCBMismatch,
42 #[error("TCB evaluation data number is invalid")]
43 TCBEvaluationDataNumberInvalid,
44 #[error("FMSPC is not whitelisted")]
45 NotWhitelistedFMSPC,
46 #[error("FMSPC is blacklisted")]
47 BlacklistedFMSPC,
48 #[error("QE report is malformed")]
49 MalformedQEReport,
50 #[error("report is malformed")]
51 MalformedReport,
52 #[error("debug enclaves not allowed")]
53 DebugEnclave,
54 #[error("production enclaves not allowed")]
55 ProductionEnclave,
56 #[error("TEE type not allowed by policy")]
57 TeeTypeNotAllowed,
58 #[error("TDX module not allowed by policy")]
59 TdxModuleNotAllowed,
60 #[error("PCS quotes are disabled by policy")]
61 Disabled,
62 #[error(transparent)]
63 Other(#[from] anyhow::Error),
64}
65
66pub use policy::{QuotePolicy, TdxModulePolicy, TdxQuotePolicy};
67pub use quote::{Quote, QuoteBundle};
68pub use report::{td_enclave_identity, TdAttributes, TdReport};
69pub use tcb::TCBBundle;
70
71#[cfg(test)]
72mod tests {
73 use chrono::prelude::*;
74
75 use super::*;
76
77 #[test]
78 fn test_quote_v3_ecdsa_p256_pck_certificatechain() {
79 const RAW_QUOTE: &[u8] =
80 include_bytes!("../../../../testdata/quote_v3_ecdsa_p256_pck_chain.bin");
81 const RAW_TCB_INFO: &[u8] =
82 include_bytes!("../../../../testdata/tcb_info_v3_fmspc_00606A000000.json"); const RAW_CERTS: &[u8] =
84 include_bytes!("../../../../testdata/tcb_info_v3_fmspc_00606A000000_certs.pem"); const RAW_QE_IDENTITY: &[u8] = include_bytes!("../../../../testdata/qe_identity_v2.json"); let qb = QuoteBundle {
88 quote: RAW_QUOTE.to_owned(),
89 tcb: TCBBundle {
90 tcb_info: serde_json::from_slice(RAW_TCB_INFO).unwrap(),
91 qe_identity: serde_json::from_slice(RAW_QE_IDENTITY).unwrap(),
92 certificates: RAW_CERTS.to_owned(),
93 },
94 };
95
96 let now = Utc.timestamp_opt(1671497404, 0).unwrap();
97
98 let verified_quote = qb.verify(&QuotePolicy::default(), now).unwrap();
99 assert_eq!(
100 verified_quote.identity.mr_signer,
101 "9affcfae47b848ec2caf1c49b4b283531e1cc425f93582b36806e52a43d78d1a".into()
102 );
103 assert_eq!(
104 verified_quote.identity.mr_enclave,
105 "68823bc62f409ee33a32ea270cfe45d4b19a6fb3c8570d7bc186cbe062398e8f".into()
106 );
107 }
108
109 #[test]
110 fn test_quote_v4_tdx_ecdsa_p256() {
111 const RAW_QUOTE: &[u8] = include_bytes!("../../../../testdata/quote_v4_tdx_ecdsa_p256.bin");
112 const RAW_TCB_INFO: &[u8] =
113 include_bytes!("../../../../testdata/tcb_info_v3_tdx_fmspc_C0806F000000.json"); const RAW_CERTS: &[u8] =
115 include_bytes!("../../../../testdata/tcb_info_v3_fmspc_00606A000000_certs.pem"); const RAW_QE_IDENTITY: &[u8] =
117 include_bytes!("../../../../testdata/qe_identity_v2_tdx2.json"); let qb = QuoteBundle {
120 quote: RAW_QUOTE.to_owned(),
121 tcb: TCBBundle {
122 tcb_info: serde_json::from_slice(RAW_TCB_INFO).unwrap(),
123 qe_identity: serde_json::from_slice(RAW_QE_IDENTITY).unwrap(),
124 certificates: RAW_CERTS.to_owned(),
125 },
126 };
127
128 let now = Utc.timestamp_opt(1725263032, 0).unwrap();
129 let policy = QuotePolicy {
130 tdx: Some(TdxQuotePolicy::default()), ..Default::default()
132 };
133
134 let verified_quote = qb.verify(&policy, now).unwrap();
135 assert_eq!(
136 verified_quote.identity.mr_signer,
137 "0000000000000000000000000000000000000000000000000000000000000000".into()
138 );
139 assert_eq!(
140 verified_quote.identity.mr_enclave,
141 "63d522d975f7de879a8f3368b4f32dd1e8db635f5a24b651ce8ff81705028813".into()
142 );
143
144 let policy = QuotePolicy {
146 tdx: None,
147 ..Default::default()
148 };
149
150 let result = qb.verify(&policy, now);
151 assert!(matches!(result, Err(Error::TeeTypeNotAllowed)));
152
153 let policy = QuotePolicy {
155 tdx: Some(TdxQuotePolicy {
156 allowed_tdx_modules: vec![TdxModulePolicy {
157 mr_seam: None,
158 mr_signer_seam: [1; 48],
159 }],
160 }),
161 ..Default::default()
162 };
163
164 let result = qb.verify(&policy, now);
165 assert!(matches!(result, Err(Error::TdxModuleNotAllowed)));
166 }
167
168 #[test]
169 fn test_quote_v4_tdx_ecdsa_p256_out_of_date() {
170 const RAW_QUOTE: &[u8] =
171 include_bytes!("../../../../testdata/quote_v4_tdx_ecdsa_p256_out_of_date.bin");
172 const RAW_TCB_INFO: &[u8] =
173 include_bytes!("../../../../testdata/tcb_info_v3_tdx_fmspc_50806F000000.json"); const RAW_CERTS: &[u8] =
175 include_bytes!("../../../../testdata/tcb_info_v3_fmspc_00606A000000_certs.pem"); const RAW_QE_IDENTITY: &[u8] =
177 include_bytes!("../../../../testdata/qe_identity_v2_tdx.json"); let qb = QuoteBundle {
180 quote: RAW_QUOTE.to_owned(),
181 tcb: TCBBundle {
182 tcb_info: serde_json::from_slice(RAW_TCB_INFO).unwrap(),
183 qe_identity: serde_json::from_slice(RAW_QE_IDENTITY).unwrap(),
184 certificates: RAW_CERTS.to_owned(),
185 },
186 };
187
188 let now = Utc.timestamp_opt(1687091776, 0).unwrap();
189 let policy = QuotePolicy {
190 tdx: Some(TdxQuotePolicy::default()), ..Default::default()
192 };
193
194 let result = qb.verify(&policy, now);
195
196 assert!(matches!(result, Err(Error::TCBOutOfDate)));
198 }
199
200 #[test]
201 fn test_quote_bundle_decoding() {
202 const RAW_QUOTE_BUNDLE: &[u8] =
204 include_bytes!("../../../../testdata/pcs_quote_bundle.cbor");
205
206 let qb: QuoteBundle = cbor::from_slice(RAW_QUOTE_BUNDLE).unwrap();
207
208 let now = Utc.timestamp_opt(1671497404, 0).unwrap();
209
210 let verified_quote = qb.verify(&QuotePolicy::default(), now).unwrap();
211 assert_eq!(
212 verified_quote.identity.mr_signer,
213 "9affcfae47b848ec2caf1c49b4b283531e1cc425f93582b36806e52a43d78d1a".into()
214 );
215 assert_eq!(
216 verified_quote.identity.mr_enclave,
217 "68823bc62f409ee33a32ea270cfe45d4b19a6fb3c8570d7bc186cbe062398e8f".into()
218 );
219 }
220
221 #[test]
222 fn test_quote_whitelisted_fmscp() {
223 const RAW_QUOTE_BUNDLE: &[u8] =
225 include_bytes!("../../../../testdata/pcs_quote_bundle.cbor");
226
227 let qb: QuoteBundle = cbor::from_slice(RAW_QUOTE_BUNDLE).unwrap();
228 let now = Utc.timestamp_opt(1671497404, 0).unwrap();
229
230 let policy = &QuotePolicy {
231 ..Default::default()
232 };
233 qb.verify(policy, now)
234 .expect("quote verification should succeed for whitelisted FMSPCs");
235
236 let policy = &QuotePolicy {
237 fmspc_whitelist: vec!["00606A000000".to_string()],
238 ..Default::default()
239 };
240 qb.verify(policy, now)
241 .expect("quote verification should succeed for whitelisted FMSPCs");
242
243 let policy: &QuotePolicy = &QuotePolicy {
244 fmspc_whitelist: vec!["00606A000001".to_string()],
245 ..Default::default()
246 };
247 qb.verify(policy, now)
248 .expect_err("quote verification should fail for non-whitelisted FMSPCs");
249 }
250
251 #[test]
252 fn test_quote_blacklisted_fmscp() {
253 const RAW_QUOTE_BUNDLE: &[u8] =
255 include_bytes!("../../../../testdata/pcs_quote_bundle.cbor");
256
257 let qb: QuoteBundle = cbor::from_slice(RAW_QUOTE_BUNDLE).unwrap();
258 let now = Utc.timestamp_opt(1671497404, 0).unwrap();
259
260 let policy = &QuotePolicy {
261 fmspc_blacklist: vec!["00606A000000".to_string()],
262 ..Default::default()
263 };
264 qb.verify(policy, now)
265 .expect_err("quote verification should fail for blacklisted FMSPCs");
266 }
267}