mc_sgx_urts/
lib.rs

1// Copyright (c) 2022-2024 The MobileCoin Foundation
2
3#![doc = include_str!("../README.md")]
4
5use mc_sgx_core_types::{ConfigId, ConfigSvn, Error, TargetInfo};
6use mc_sgx_urts_sys::{
7    sgx_create_enclave_from_buffer_ex, sgx_destroy_enclave, sgx_get_target_info,
8};
9use mc_sgx_urts_sys_types::{
10    sgx_enclave_id_t, sgx_kss_config_t, MAX_EX_FEATURES_COUNT, SGX_CREATE_ENCLAVE_EX_KSS,
11    SGX_CREATE_ENCLAVE_EX_KSS_BIT_IDX, SGX_CREATE_ENCLAVE_EX_PCL,
12    SGX_CREATE_ENCLAVE_EX_PCL_BIT_IDX,
13};
14use mc_sgx_util::ResultInto;
15use std::{ffi::c_void, fs::File, io::Read, os::raw::c_int, path::Path, ptr};
16
17/// Structure defining configuration for Key Sharing and Separation
18#[repr(transparent)]
19#[derive(Debug, Clone, Hash, PartialEq, Eq, Default)]
20pub struct KssConfig(sgx_kss_config_t);
21
22impl KssConfig {
23    pub fn new(config_id: ConfigId, config_svn: ConfigSvn) -> KssConfig {
24        KssConfig(sgx_kss_config_t {
25            config_id: config_id.into(),
26            config_svn: config_svn.into(),
27        })
28    }
29}
30
31impl From<KssConfig> for sgx_kss_config_t {
32    fn from(input: KssConfig) -> sgx_kss_config_t {
33        input.0
34    }
35}
36
37impl From<sgx_kss_config_t> for KssConfig {
38    fn from(input: sgx_kss_config_t) -> KssConfig {
39        KssConfig(input)
40    }
41}
42
43/// Struct for interfacing with the SGX SDK.  This should be used in
44/// sgx calls as `ecall_some_function(*enclave.id(), ...)`.
45///
46/// Avoid storing the de-referenced ID of the enclave.  The de-referenced
47/// ID of the enclave will result in failures to the SGX SDK after the
48/// enclave is dropped.
49#[derive(Debug, PartialEq, Eq)]
50pub struct Enclave {
51    // The enclave ID, assigned by the SGX interface
52    id: sgx_enclave_id_t,
53}
54
55/// Build an [Enclave] for use with SGX calls.
56pub struct EnclaveBuilder {
57    // The bytes for the enclave.
58    bytes: Vec<u8>,
59
60    // `true` if the enclave should be created in debug mode
61    debug: bool,
62
63    // Sealed key to use with Intel Protected Code Loader. None if PCL disabled.
64    pcl_key: Option<Vec<u8>>,
65
66    // Configuration to use with Key Separation & Sharing. None if KSS disabled.
67    kss_config: Option<KssConfig>,
68}
69
70impl EnclaveBuilder {
71    /// Returns an EnclaveBuilder for the provided signed enclave.
72    ///
73    /// # Arguments
74    ///
75    /// * `path` - The path to the enclave file. This should be a signed
76    ///   enclave.
77    pub fn new(path: impl AsRef<Path>) -> std::io::Result<EnclaveBuilder> {
78        let file = File::open(path)?;
79        file.try_into()
80    }
81
82    /// Enable debugging of the enclave
83    #[must_use]
84    pub fn debug(mut self) -> EnclaveBuilder {
85        self.debug = true;
86        self
87    }
88
89    /// Enable Intel's Protected Code Loader for the enclave
90    ///
91    /// # Arguments
92    ///
93    /// * `key` - The sealed PCL key to use for loading the enclave
94    #[must_use]
95    pub fn pcl(mut self, key: Vec<u8>) -> EnclaveBuilder {
96        self.pcl_key = Some(key);
97        self
98    }
99
100    /// Enable Key Separation & Sharing for the enclave
101    ///
102    /// # Arguments
103    ///
104    /// * `config` - The KSS configuration to use when loading the enclave
105    #[must_use]
106    pub fn kss(mut self, config: KssConfig) -> EnclaveBuilder {
107        self.kss_config = Some(config);
108        self
109    }
110
111    /// Create the enclave
112    ///
113    /// Will talk to the SGX SDK to create the enclave.  Once the enclave has
114    /// been created then calls into the enclave can be made using the enclave
115    /// ID.
116    pub fn create(mut self) -> Result<Enclave, Error> {
117        let mut enclave_id: sgx_enclave_id_t = 0;
118        let mut ex_features = 0;
119        let mut ex_features_p: [*const c_void; MAX_EX_FEATURES_COUNT] =
120            [ptr::null(); MAX_EX_FEATURES_COUNT];
121
122        if let Some(pcl_key) = self.pcl_key {
123            ex_features |= SGX_CREATE_ENCLAVE_EX_PCL;
124            ex_features_p[SGX_CREATE_ENCLAVE_EX_PCL_BIT_IDX] = pcl_key.as_ptr() as *const c_void;
125        }
126
127        if let Some(kss_config) = self.kss_config {
128            ex_features |= SGX_CREATE_ENCLAVE_EX_KSS;
129            ex_features_p[SGX_CREATE_ENCLAVE_EX_KSS_BIT_IDX] =
130                &kss_config.into() as *const sgx_kss_config_t as *const c_void;
131        }
132
133        unsafe {
134            // Per the API reference `buffer` is an input, however the signature
135            // lacks the const qualifier.  Through testing it has been shown
136            // that `sgx_create_enclave_from_buffer_ex()` *will* modify the
137            // `buffer` parameter.  This can be seen by copying the input bytes
138            // and comparing before and after.
139            //
140            //      let mut buffer = self.bytes.to_vec();
141            //      println!("Pre comparing {}", buffer.as_slice() == self.bytes);
142            //      let result = unsafe {sgx_create_enclave_from_buffer_ex(...)};
143            //      println!("Post comparing {}", buffer.as_slice() == self.bytes);
144            //
145            // The modification that `sgx_create_enclave_from_buffer_ex()`
146            // makes to the `buffer` is such that if one were to re-use the
147            // modified buffer in another call to
148            // `sgx_create_enclave_from_buffer_ex()` then
149            // `SGX_ERROR_INVALID_ENCLAVE_ID` would be returned.
150            sgx_create_enclave_from_buffer_ex(
151                self.bytes.as_mut_ptr(),
152                self.bytes.len(),
153                self.debug as c_int,
154                &mut enclave_id,
155                ptr::null_mut(),
156                ex_features,
157                &mut ex_features_p as *mut *const c_void,
158            )
159        }
160        .into_result()
161        .map(|_| Enclave { id: enclave_id })
162    }
163}
164
165impl From<Vec<u8>> for EnclaveBuilder {
166    fn from(bytes: Vec<u8>) -> EnclaveBuilder {
167        EnclaveBuilder {
168            bytes,
169            debug: false,
170            pcl_key: None,
171            kss_config: None,
172        }
173    }
174}
175
176impl From<&[u8]> for EnclaveBuilder {
177    fn from(input: &[u8]) -> EnclaveBuilder {
178        input.to_vec().into()
179    }
180}
181
182impl<const N: usize> From<&[u8; N]> for EnclaveBuilder {
183    fn from(input: &[u8; N]) -> EnclaveBuilder {
184        input.to_vec().into()
185    }
186}
187
188impl TryFrom<File> for EnclaveBuilder {
189    type Error = std::io::Error;
190    fn try_from(mut input: File) -> std::io::Result<EnclaveBuilder> {
191        let mut bytes = vec![];
192        input.read_to_end(&mut bytes)?;
193        Ok(EnclaveBuilder {
194            bytes,
195            debug: false,
196            pcl_key: None,
197            kss_config: None,
198        })
199    }
200}
201
202impl Enclave {
203    /// Returns the target info for the enclave.
204    pub fn target_info(&self) -> Result<TargetInfo, Error> {
205        let mut target_info = TargetInfo::default().into();
206        unsafe { sgx_get_target_info(self.id, &mut target_info) }.into_result()?;
207        Ok(target_info.into())
208    }
209
210    /// Returns a reference to the enclave ID.
211    /// Returns by reference because enclave ID will not be valid after the
212    /// enclave is dropped.
213    pub fn id(&self) -> &sgx_enclave_id_t {
214        &self.id
215    }
216}
217
218impl Drop for Enclave {
219    /// Destroys the enclave through the SGX interface.
220    /// Any de-referenced value from [Enclave] is not valid after
221    /// dropping.
222    fn drop(&mut self) {
223        // Per the docs, this will only return SGX_SUCCESS or
224        // SGX_ERROR_INVALID_ENCLAVE_ID. The invalid ID error will only
225        // happen when the ID is invalid, the enclave hasn't been loaded,
226        // or the enclave has already been destroyed. Any of these cases
227        // don't afford corrective action, so ignore the return value
228        unsafe { sgx_destroy_enclave(self.id) };
229    }
230}
231
232#[cfg(test)]
233mod tests {
234    use super::*;
235
236    #[test]
237    fn fail_to_create_enclave_with_bogus_bytes() {
238        let builder = EnclaveBuilder::from(b"garbage bytes");
239        assert_eq!(builder.create(), Err(Error::InvalidEnclave));
240    }
241
242    #[test]
243    fn default_debug_flag_is_0() {
244        // For the debug flag it's not easy, in a unit test, to test it was
245        // passed to `sgx_create_enclave()`, instead we focus on the
246        // `as c_int` portion maps correctly to 0 or 1
247        let builder = EnclaveBuilder::from(b"");
248        assert_eq!(builder.debug as c_int, 0);
249    }
250
251    #[test]
252    fn when_debug_flag_is_true_it_is_1() {
253        let builder = EnclaveBuilder::from(b"").debug();
254        assert_eq!(builder.debug as c_int, 1);
255    }
256}