1#![doc(issue_tracker_base_url = "https://github.com/aggregion/occlum-sgx/issues")]
2use std::fmt::Debug;
35use std::mem::size_of;
36use std::ops::Deref;
37use std::time::Instant;
38
39#[macro_use]
40extern crate lazy_static;
41
42pub use error::SGXError;
43use ioctl::IOCTL_CLIENT;
44use log::{trace, warn};
45pub use types::*;
46
47mod constants;
48mod error;
49mod ioctl;
50mod types;
51
52pub struct SGXQuote {
54 buf: Vec<u8>,
55 report_body: *const SGXReportBody,
56}
57
58impl TryFrom<Vec<u8>> for SGXQuote {
59 type Error = SGXError;
60 fn try_from(buf: Vec<u8>) -> Result<Self, Self::Error> {
61 let report_body_offset = size_of::<SGXQuoteHeader>();
62 let report_body_size = size_of::<SGXReportBody>();
63
64 if buf.len() < report_body_offset + report_body_size {
65 return Err(SGXError::BadQuoteLength {
66 min: report_body_offset + report_body_size,
67 actual: buf.len(),
68 });
69 }
70
71 let report_body = buf.as_slice()[report_body_offset..].as_ptr() as *const SGXReportBody;
72
73 Ok(Self { buf, report_body })
74 }
75}
76
77impl TryFrom<&[u8]> for SGXQuote {
78 type Error = SGXError;
79
80 fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
81 buf.to_vec().try_into()
82 }
83}
84
85impl TryFrom<ReportData> for SGXQuote {
86 type Error = SGXError;
87
88 fn try_from(value: ReportData) -> Result<Self, Self::Error> {
89 Self::from_report_data(&value)
90 }
91}
92
93impl Deref for SGXQuote {
94 type Target = [u8];
95
96 fn deref(&self) -> &Self::Target {
97 self.buf.as_ref()
98 }
99}
100
101impl SGXQuote {
102 pub fn from_report_data(data: &ReportData) -> Result<Self, SGXError> {
112 let start = Instant::now();
113
114 let result = IOCTL_CLIENT
115 .lock()
116 .unwrap()
117 .generate_quote(SGXReportData::new(*data))?;
118
119 trace!("Generated quote in {:?}ms", start.elapsed().as_millis());
120
121 result.try_into()
122 }
123
124 pub fn from_slice(slice: &[u8]) -> Result<Self, SGXError> {
132 slice.try_into()
133 }
134
135 pub fn as_slice(&self) -> &[u8] {
136 self
137 }
138
139 pub fn verify_result(&self) -> Result<SGXQuoteVerifyResult, SGXError> {
160 let start = Instant::now();
161 let result = IOCTL_CLIENT.lock().unwrap().verify_quote(self.buf.as_ref());
162 trace!("Verify quote in {:?}ms", start.elapsed().as_millis());
163 result
164 }
165
166 pub fn verify(&self) -> Result<(), SGXError> {
170 let result = self.verify_result()?;
171
172 match result {
173 SGXQuoteVerifyResult::Ok => Ok(()),
174 SGXQuoteVerifyResult::ConfigNeeded
175 | SGXQuoteVerifyResult::OutOfDate
176 | SGXQuoteVerifyResult::OutOfDateConfigNeeded
177 | SGXQuoteVerifyResult::SwHardeningNeeded
178 | SGXQuoteVerifyResult::ConfigAndSwHardeningNeeded => {
179 warn!(
180 "SGX Quote Verification completed with non-terminal result: {:?}",
181 result
182 );
183 Ok(())
184 }
185 _ => Err(SGXError::VerifyQuoteFailed(result)),
186 }
187 }
188
189 pub fn isv_family_id(&self) -> SGXFamilyId {
190 unsafe { (*self.report_body).isv_family_id }
191 }
192
193 pub fn isv_ext_prod_id(&self) -> SGXExtProdId {
194 unsafe { (*self.report_body).isv_ext_prod_id }
195 }
196
197 pub fn config_id(&self) -> SGXConfigId {
198 unsafe { (*self.report_body).config_id }
199 }
200
201 pub fn mrenclave(&self) -> SGXMeasurement {
202 unsafe { (*self.report_body).mr_enclave }
203 }
204
205 pub fn mrsigner(&self) -> SGXMeasurement {
206 unsafe { (*self.report_body).mr_signer }
207 }
208
209 pub fn product_id(&self) -> u16 {
210 unsafe { (*self.report_body).isv_prod_id }
211 }
212
213 pub fn version(&self) -> u16 {
214 unsafe { (*self.report_body).isv_svn }
215 }
216
217 pub fn report_data(&self) -> SGXReportData {
218 unsafe { (*self.report_body).report_data }
219 }
220}
221
222impl Debug for SGXQuote {
223 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
224 f.debug_struct("SGXQuote")
225 .field("mrenclave", &self.mrenclave())
226 .field("mrsigner", &self.mrsigner())
227 .field("report_body", &self.report_data())
228 .field("product_id", &self.product_id())
229 .field("version", &self.version())
230 .field("family_id", &self.isv_family_id())
231 .field("ext_prod_id", &self.isv_ext_prod_id())
232 .field("config_id", &self.config_id())
233 .finish()
234 }
235}
236
237#[cfg(test)]
238mod tests {
239 use crate::*;
240
241 #[test]
242 fn create_from_vec() {
243 let quote_buf = include_bytes!("../tests/fixtures/quote.raw");
244 let quote = SGXQuote::from_slice(quote_buf.as_slice()).unwrap();
245
246 insta::assert_yaml_snapshot!(format!("{:?}", quote));
247 }
248}