1use std::arch::x86_64::{self, CpuidResult};
8use std::fs::File;
9use std::io::{Error as IoError, ErrorKind, Read, Result as IoResult};
10use std::ops::RangeInclusive;
11use std::os::raw::c_void;
12use std::path::Path;
13use std::{arch, str};
14
15use thiserror::Error as ThisError;
16use anyhow::{Context, format_err};
17
18#[cfg(feature = "crypto-openssl")]
19use openssl::{
20 hash::Hasher,
21 pkey::PKey,
22};
23
24use sgx_isa::{Attributes, AttributesFlags, Miscselect, Sigstruct};
25use sgxs::sgxs::PageReader;
26use sgxs::crypto::{SgxHashOps, SgxRsaOps};
27use sgxs::loader::{Load, MappingInfo, Tcs};
28use sgxs::sigstruct::{self, EnclaveHash, Signer};
29
30use crate::tcs::DebugBuffer;
31use crate::usercalls::UsercallExtension;
32use crate::{Command, Library};
33
34enum EnclaveSource<'a> {
35 Path(&'a Path),
36 File(File),
37 Data(&'a [u8]),
38}
39
40impl<'a> EnclaveSource<'a> {
41 fn try_clone(&self) -> Option<Self> {
42 match *self {
43 EnclaveSource::Path(path) => Some(EnclaveSource::Path(path)),
44 EnclaveSource::Data(data) => Some(EnclaveSource::Data(data)),
45 EnclaveSource::File(_) => None,
46 }
47 }
48}
49
50impl<'a> Read for EnclaveSource<'a> {
51 fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
52 if let &mut EnclaveSource::Path(path) = self {
53 let file = File::open(path)?;
54 *self = EnclaveSource::File(file);
55 }
56
57 match *self {
58 EnclaveSource::File(ref mut file) => file.read(buf),
59 EnclaveSource::Data(ref mut data) => data.read(buf),
60 EnclaveSource::Path(_) => unreachable!(),
61 }
62 }
63}
64
65pub struct EnclaveBuilder<'a> {
66 enclave: EnclaveSource<'a>,
67 signature: Option<Sigstruct>,
68 attributes: Option<Attributes>,
69 miscselect: Option<Miscselect>,
70 usercall_ext: Option<Box<dyn UsercallExtension>>,
71 load_and_sign: Option<Box<dyn FnOnce(Signer) -> Result<Sigstruct, anyhow::Error>>>,
72 hash_enclave: Option<Box<dyn FnOnce(&mut EnclaveSource<'_>) -> Result<EnclaveHash, anyhow::Error>>>,
73 forward_panics: bool,
74 force_time_usercalls: bool,
75 cmd_args: Option<Vec<Vec<u8>>>,
76 num_worker_threads: Option<usize>,
77}
78
79#[derive(Debug, ThisError)]
80pub enum EnclavePanic {
81 #[error("Enclave panicked.")]
83 NoDebugBuf,
84 #[error("Enclave panicked: {}", _0)]
86 DebugStr(String),
87 #[error("Enclave panicked: {:?}", _0)]
90 DebugBuf(Vec<u8>),
91}
92
93impl From<DebugBuffer> for EnclavePanic {
94 fn from(buf: DebugBuffer) -> EnclavePanic {
95 if buf[0] == 0 {
96 EnclavePanic::NoDebugBuf
97 } else {
98 match str::from_utf8(buf.split(|v| *v == 0).next().unwrap()) {
99 Ok(s) => EnclavePanic::DebugStr(s.to_owned()),
100 Err(_) => EnclavePanic::DebugBuf(buf.to_vec()),
101 }
102 }
103 }
104}
105
106#[derive(Debug)]
108pub(crate) struct ErasedTcs {
109 address: *mut c_void,
110 #[allow(dead_code)]
113 tcs: Box<dyn Tcs>,
114}
115
116unsafe impl Send for ErasedTcs {}
118
119impl ErasedTcs {
120 fn new<T: Tcs + 'static>(tcs: T) -> ErasedTcs {
121 ErasedTcs {
122 address: tcs.address(),
123 tcs: Box::new(tcs),
124 }
125 }
126}
127
128impl Tcs for ErasedTcs {
129 fn address(&self) -> *mut c_void {
130 self.address
131 }
132}
133
134impl<'a> EnclaveBuilder<'a> {
135 pub fn new(enclave_path: &'a Path) -> EnclaveBuilder<'a> {
136 Self::new_with_source(EnclaveSource::Path(enclave_path))
137 }
138
139 pub fn new_from_memory(enclave_data: &'a [u8]) -> EnclaveBuilder<'a> {
140 Self::new_with_source(EnclaveSource::Data(enclave_data))
141 }
142
143 fn new_with_source(enclave: EnclaveSource<'a>) -> EnclaveBuilder<'a> {
144 let mut ret = EnclaveBuilder {
145 enclave,
146 attributes: None,
147 miscselect: None,
148 signature: None,
149 usercall_ext: None,
150 load_and_sign: None,
151 hash_enclave: None,
152 forward_panics: false,
153 force_time_usercalls: true, cmd_args: None,
155 num_worker_threads: None,
156 };
157
158 let _ = ret.coresident_signature();
159
160 #[cfg(feature = "crypto-openssl")]
161 ret.with_dummy_signature_signer::<Hasher, _, _, _, _>(|der| {
162 PKey::private_key_from_der(der).unwrap().rsa().unwrap()
163 });
164
165 ret
166 }
167
168 fn generate_xfrm(max_ssaframesize_in_pages: u32) -> u64 {
169 fn cpuid(eax: u32, ecx: u32) -> Option<CpuidResult> {
170 unsafe {
171 if eax <= x86_64::__get_cpuid_max(0).0 {
172 Some(x86_64::__cpuid_count(eax, ecx))
173 } else {
174 None
175 }
176 }
177 }
178
179 fn xgetbv0() -> u64 {
180 unsafe { arch::x86_64::_xgetbv(0) }
181 }
182
183 debug_assert_ne!(0, max_ssaframesize_in_pages);
184 let xcr0 = xgetbv0();
185
186 let xfrm = (0..64)
188 .map(|bit| {
189 let select = 0x1 << bit;
190
191 if bit == 0 || bit == 1 {
192 return select; }
194
195 if xcr0 & select == 0 {
196 return 0;
197 }
198
199 let CpuidResult { ebx: base, eax: size, .. } = match cpuid(0x0d, bit) {
200 None | Some(CpuidResult { ebx: 0, .. }) => return 0,
201 Some(v) => v,
202 };
203
204 if max_ssaframesize_in_pages * 0x1000 < base + size {
205 return 0;
206 }
207
208 select
209 })
210 .fold(0, |xfrm, b| xfrm | b);
211
212 const XCR0_TILE_BITS: u64 = 0b11 << 17;
219 match xfrm & XCR0_TILE_BITS {
220 XCR0_TILE_BITS | 0 => xfrm,
221 _ => xfrm & !XCR0_TILE_BITS,
222 }
223 }
224
225 fn generate_dummy_signature(&mut self) -> Result<Sigstruct, anyhow::Error> {
226 let mut enclave = self.enclave.try_clone().unwrap();
227 let create_info = PageReader::new(&mut enclave)?;
228 let mut enclave = self.enclave.try_clone().unwrap();
229 let hash = match self.hash_enclave.take() {
230 Some(f) => f(&mut enclave)?,
231 None => return Err(format_err!("either compile with default features or use with_dummy_signature_signer()"))
232 };
233 let mut signer = Signer::new(hash);
234
235 let attributes = self.attributes.unwrap_or_else(|| Attributes {
236 flags: AttributesFlags::DEBUG | AttributesFlags::MODE64BIT,
237 xfrm: Self::generate_xfrm(create_info.0.ecreate.ssaframesize),
238 });
239 signer
240 .attributes_flags(attributes.flags, !0)
241 .attributes_xfrm(attributes.xfrm, !0);
242
243 if let Some(miscselect) = self.miscselect {
244 signer.miscselect(miscselect, !0);
245 }
246
247 match self.load_and_sign.take() {
248 Some(f) => f(signer),
249 None => Err(format_err!("either compile with default features or use with_dummy_signature_signer()"))
250 }
251 }
252
253 pub fn dummy_signature(&mut self) -> &mut Self {
254 self.signature = None;
255 self
256 }
257
258 pub fn with_dummy_signature_signer<H, S, F, E, T>(&mut self, load_key: F)
268 where
269 H: SgxHashOps,
270 E: std::error::Error + Send + Sync + 'static,
271 S: SgxRsaOps<Error = E>,
272 T: AsRef<S>,
273 F: 'static + FnOnce(&[u8]) -> T,
274 {
275 self.load_and_sign = Some(Box::new(move |signer| {
276 let key = load_key(include_bytes!("dummy.key"));
277 signer.sign::<_, H>(key.as_ref()).map_err(|e| e.into())
278 }));
279 self.hash_enclave = Some(Box::new(|stream| {
280 EnclaveHash::from_stream::<_, H>(stream)
281 }));
282 }
283
284 pub fn coresident_signature(&mut self) -> IoResult<&mut Self> {
285 if let EnclaveSource::Path(path) = self.enclave {
286 let sigfile = path.with_extension("sig");
287 self.signature(sigfile)
288 } else {
289 Err(IoError::new(
290 ErrorKind::NotFound,
291 "Can't load coresident signature for non-file enclave",
292 ))
293 }
294 }
295
296 pub fn signature<P: AsRef<Path>>(&mut self, path: P) -> IoResult<&mut Self> {
297 let mut file = File::open(path)?;
298 self.signature = Some(sigstruct::read(&mut file)?);
299 Ok(self)
300 }
301
302 pub fn sigstruct(&mut self, sigstruct: Sigstruct) -> &mut Self {
303 self.signature = Some(sigstruct);
304 self
305 }
306
307 pub fn attributes(&mut self, attributes: Attributes) -> &mut Self {
308 self.attributes = Some(attributes);
309 self
310 }
311
312 pub fn miscselect(&mut self, miscselect: Miscselect) -> &mut Self {
313 self.miscselect = Some(miscselect);
314 self
315 }
316
317 pub fn usercall_extension<T: Into<Box<dyn UsercallExtension>>>(&mut self, extension: T) {
318 self.usercall_ext = Some(extension.into());
319 }
320
321 pub fn forward_panics(&mut self, fp: bool) -> &mut Self {
327 self.forward_panics = fp;
328 self
329 }
330
331 pub fn force_insecure_time_usercalls(&mut self, force_time_usercalls: bool) -> &mut Self {
337 self.force_time_usercalls = force_time_usercalls;
338 self
339 }
340
341 fn initialized_args_mut(&mut self) -> &mut Vec<Vec<u8>> {
342 self.cmd_args.get_or_insert_with(|| vec![b"enclave".to_vec()])
343 }
344
345 pub fn args<I, S>(&mut self, args: I) -> &mut Self
356 where
357 I: IntoIterator<Item = S>,
358 S: AsRef<[u8]>,
359 {
360 let args = args.into_iter().map(|a| a.as_ref().to_owned());
361 self.initialized_args_mut().extend(args);
362 self
363 }
364
365 pub fn arg<S: AsRef<[u8]>>(&mut self, arg: S) -> &mut Self {
375 let arg = arg.as_ref().to_owned();
376 self.initialized_args_mut().push(arg);
377 self
378 }
379
380 pub fn num_worker_threads(&mut self, num_worker_threads: usize) -> &mut Self {
385 self.num_worker_threads = Some(num_worker_threads);
386 self
387 }
388
389 fn load<T: Load>(
390 mut self,
391 loader: &mut T,
392 ) -> Result<(Vec<ErasedTcs>, *mut c_void, usize, bool, bool), anyhow::Error> {
393 let signature = match self.signature {
394 Some(sig) => sig,
395 None => self
396 .generate_dummy_signature()
397 .context("While generating dummy signature")?,
398 };
399 let attributes = self.attributes.unwrap_or(signature.attributes);
400 let miscselect = self.miscselect.unwrap_or(signature.miscselect);
401 let mapping = loader.load(&mut self.enclave, &signature, attributes, miscselect)?;
402 let forward_panics = self.forward_panics;
403 let force_time_usercalls = self.force_time_usercalls;
404 if mapping.tcss.is_empty() {
405 unimplemented!()
406 }
407 Ok((
408 mapping.tcss.into_iter().map(ErasedTcs::new).collect(),
409 mapping.info.address(),
410 mapping.info.size(),
411 forward_panics,
412 force_time_usercalls,
413 ))
414 }
415
416 pub fn build<T: Load>(mut self, loader: &mut T) -> Result<Command, anyhow::Error> {
417 if let Some(num_worker_threads) = self.num_worker_threads {
418 const NUM_WORKER_THREADS_RANGE: RangeInclusive<usize> = 1..=65536;
419 anyhow::ensure!(
420 NUM_WORKER_THREADS_RANGE.contains(&num_worker_threads),
421 "`num_worker_threads` must be in range {NUM_WORKER_THREADS_RANGE:?}"
422 );
423 }
424 let num_worker_threads = self.num_worker_threads.unwrap_or_else(num_cpus::get);
425
426 self.initialized_args_mut();
427 let args = self.cmd_args.take().unwrap_or_default();
428 let c = self.usercall_ext.take();
429 self.load(loader)
430 .map(|(t, a, s, fp, dti)| Command::internal_new(t, a, s, c, fp, dti, args, num_worker_threads))
431 }
432
433 pub fn build_library<T: Load>(mut self, loader: &mut T) -> Result<Library, anyhow::Error> {
439 assert!(self.cmd_args.is_none(), "Command arguments do not apply to Library enclaves.");
440 assert!(self.num_worker_threads.is_none(), "`num_worker_threads` cannot be specified for Library enclaves.");
441 let c = self.usercall_ext.take();
442 self.load(loader)
443 .map(|(t, a, s, fp, dti)| Library::internal_new(t, a, s, c, fp, dti))
444 }
445}