occlum_sgx/
lib.rs

1#![doc(issue_tracker_base_url = "https://github.com/aggregion/occlum-sgx/issues")]
2//! Generate and verify [`SGXQuote`] with [Occlum] DCAP
3//!
4//! RFC: <https://download.01.org/intel-sgx/sgx-dcap/1.16/linux/docs/Intel_SGX_ECDSA_QuoteLibReference_DCAP_API.pdf>
5//!
6//! # Generate [`SGXQuote`]
7//! ```rust ignore
8//! use occlum_sgx::SGXQuote;
9//! let quote: SGXQuote = [0u8; 64].try_into().unwrap();
10//! // or
11//! let quote = SGXQuote::from_report_data(&[0u8; 64]).unwrap();
12//! // convert to &[u8] and send to remote for verification
13//! let quote_buf = quote.as_slice();
14//! ```
15//!
16//! # Verify [`SGXQuote`] on remote
17//! ```rust ignore
18//! use occlum_sgx::SGXQuote;
19//! let quote_buf: &[u8] = ...;
20//! let quote = SGXQuote::from_slice(quote_buf).unwrap();
21//! // verify quote
22//! quote.verify().unwrap();
23//! // check report data
24//! assert_eq!(quote.report_data(), &[0u8; 64]);
25//! // and check measurement data if required
26//! assert_eq!(
27//!     quote.mrsigner(),
28//!     SGXMeasurement::from_hex("538c416a279b18e54f719d6332af1bc95082d4ef79153fb8c6a31ef70957d82b").unwrap()
29//! );
30//! ...
31//! ```
32//!
33//! [Occlum]: https://github.com/occlum/occlum
34use 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
52/// SGX Quote
53pub 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    /// Create a new [SGXQuote] from [ReportData], it needs to be run on the SGX server in an [Occlum] instance, also requires [PCCS].
103    ///
104    /// # Example
105    /// ```rust ignore
106    /// let quote = SGXQuote::from_report_data(&value).unwrap();
107    /// ```
108    ///
109    /// [Occlum]: https://github.com/occlum/occlum
110    /// [PCCS]: https://github.com/intel/SGXDataCenterAttestationPrimitives/blob/master/QuoteGeneration/pccs/README.md
111    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    /// Restore SGXQuote from slice of bytes.
125    /// # Example
126    /// ```rust
127    /// use occlum_sgx::SGXQuote;
128    /// let quote_buf: &[u8] = &[0u8; 4356];
129    /// let quote = SGXQuote::from_slice(quote_buf).unwrap();
130    /// ```
131    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    /// Verify [`SGXQuote`] and return [`SGXQuoteVerifyResult`]
140    ///
141    /// # Example
142    /// ```rust ignore
143    /// let status = quote.verify().unwrap();
144    /// match status {
145    ///     SGXQuoteVerifyResult::Ok => println!("SGX Quote verified"),
146    ///     SGXQuoteVerifyResult::ConfigNeeded
147    ///     | SGXQuoteVerifyResult::OutOfDate
148    ///     | SGXQuoteVerifyResult::OutOfDateConfigNeeded
149    ///     | SGXQuoteVerifyResult::SwHardeningNeeded
150    ///     | SGXQuoteVerifyResult::ConfigAndSwHardeningNeeded => {
151    ///         println!(
152    ///             "SGX Quote Verification completed with non-terminal result: {:?}",
153    ///             result
154    ///         )
155    ///     }
156    ///     _ => println!("SGX Quote Verification failed"),
157    /// }
158    /// ```
159    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    /// Verify [SGXQuote], if it is not valid, return error [`SGXError::VerifyQuoteFailed`]
167    ///
168    /// See also [`SGXQuote::verify_result`]
169    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}