1#![doc(html_logo_url = "https://edp.fortanix.com/img/docs/edp-logo.svg",
12 html_favicon_url = "https://edp.fortanix.com/favicon.ico",
13 html_root_url = "https://edp.fortanix.com/docs/api/")]
14#![allow(non_local_definitions)] #![allow(renamed_and_removed_lints)]
19#![deny(warnings)]
20#![allow(renamed_and_removed_lints)]
23#![allow(unknown_lints)]
26#![allow(mismatched_lifetime_syntaxes)]
27
28extern crate byteorder;
29pub extern crate anyhow;
30pub extern crate thiserror;
31#[macro_use]
32#[cfg(unix)]
33extern crate lazy_static;
34extern crate protobuf;
35#[cfg(feature = "sgxs")]
36extern crate sgxs;
37#[cfg(unix)]
38extern crate unix_socket;
39#[cfg(windows)]
40extern crate winapi;
41extern crate sgx_isa;
42
43use std::convert::TryFrom;
44#[cfg(feature = "sgxs")]
45use std::result::Result as StdResult;
46
47#[cfg(feature = "sgxs")]
48use sgxs::einittoken::{Einittoken, EinittokenProvider};
49#[cfg(all(not(target_env = "sgx"),feature = "sgxs"))]
50use sgx_isa::{Attributes, Sigstruct};
51
52use protobuf::{MessageField, Result as ProtobufResult};
53include!(concat!(env!("OUT_DIR"), "/protos/mod_aesm_proto.rs"));
54mod error;
55use self::aesm_proto::*;
56pub use error::{AesmError, Error, Result};
57#[cfg(windows)]
58#[path = "imp/windows.rs"]
59mod imp;
60#[cfg(unix)]
61#[path = "imp/unix.rs"]
62mod imp;
63#[cfg(target_env = "sgx")]
64#[path = "imp/sgx.rs"]
65mod imp;
66#[cfg(unix)]
67pub mod unix {
68 use std::path::Path;
69 pub trait AesmClientExt {
70 fn with_path<P: AsRef<Path>>(path: P) -> Self;
71 }
72}
73
74#[cfg(target_env = "sgx")]
75pub mod sgx {
76 use std::net::TcpStream;
77 pub trait AesmClientExt {
78 fn new(tcp_stream: TcpStream) -> Self;
79 }
80}
81
82use request::{GetQuoteExRequest, GetQuoteRequest, GetQuoteSizeExRequest, GetLaunchTokenRequest, GetSupportedAttKeyIDNumRequest, GetSupportedAttKeyIDsRequest, InitQuoteExRequest, InitQuoteRequest};
83use response::{GetSupportedAttKeyIDNumResponse, GetSupportedAttKeyIDsResponse, GetLaunchTokenResponse, GetQuoteSizeExResponse, GetQuoteExResponse, GetQuoteResponse, InitQuoteExResponse, InitQuoteResponse};
84
85const AESM_SUCCESS: u32 = 0;
87
88#[repr(u32)]
90pub enum QuoteType {
91 Unlinkable = 0,
92 Linkable = 1,
93}
94
95impl Into<u32> for QuoteType {
96 fn into(self: QuoteType) -> u32 {
97 use self::QuoteType::*;
98 match self {
99 Unlinkable => 0,
100 Linkable => 1,
101 }
102 }
103}
104
105impl QuoteType {
106 pub fn from_u32(v: u32) -> Result<Self> {
107 use self::QuoteType::*;
108 Ok(match v {
109 0 => Unlinkable,
110 1 => Linkable,
111 _ => return Err(Error::InvalidQuoteType(v)),
112 })
113 }
114}
115
116#[derive(Debug)]
117pub struct QuoteInfo {
118 target_info: Vec<u8>,
119 pub_key_id: Vec<u8>,
120}
121
122impl QuoteInfo {
123 pub fn target_info(&self) -> &[u8] {
124 &self.target_info
125 }
126
127 pub fn gid(&self) -> Vec<u8> {
129 let mut pk = self.pub_key_id.clone();
131 pk.reverse();
132 pk
133 }
134
135 pub fn pub_key_id(&self) -> &[u8] {
136 &self.pub_key_id
137 }
138}
139
140fn quote_buffer_size(sig_rl: &[u8]) -> u32 {
144 let quote_length = 436 + 288 + 12 + 4 + 16;
146
147 let sig_length = 352 + 4 + 4 + (sig_rl.len() as u32 * 5 / 4) + 128;
156
157 quote_length + sig_length
158}
159
160#[derive(Debug, Clone, Eq, PartialEq)]
161pub struct QuoteResult {
162 quote: Vec<u8>,
164
165 qe_report: Vec<u8>,
167}
168
169impl QuoteResult {
170 pub fn new<T: Into<Vec<u8>>, U: Into<Vec<u8>>>(quote: T, qe_report: U) -> Self {
171 QuoteResult {
172 quote: quote.into(),
173 qe_report: qe_report.into(),
174 }
175 }
176
177 pub fn quote(&self) -> &[u8] {
178 &self.quote
179 }
180
181 pub fn qe_report(&self) -> &[u8] {
182 &self.qe_report
183 }
184}
185
186#[cfg_attr(not(target_env = "sgx"), derive(Default))]
187#[derive(Debug, Clone)]
188pub struct AesmClient {
189 inner: imp::AesmClient
190}
191
192
193impl AesmClient {
194 #[cfg(not(target_env = "sgx"))]
195 pub fn new() -> Self {
196 AesmClient { inner: imp::AesmClient::new() }
197 }
198
199 pub fn try_connect(&self) -> Result<()> {
205 self.inner.try_connect()
206 }
207
208 pub fn init_quote(&self) -> Result<QuoteInfo> {
210 self.inner.init_quote()
211 }
212
213 pub fn get_quote(
215 &self,
216 report: Vec<u8>,
217 spid: Vec<u8>,
218 sig_rl: Vec<u8>,
219 quote_type: QuoteType,
220 nonce: Vec<u8>,
221 ) -> Result<QuoteResult> {
222 self.inner.get_quote(
223 report,
224 spid,
225 sig_rl,
226 quote_type,
227 nonce,
228 )
229 }
230
231 #[cfg(all(not(target_env = "sgx"), feature = "sgxs"))]
232 pub fn get_launch_token(
233 &self,
234 sigstruct: &Sigstruct,
235 attributes: Attributes,
236 ) -> Result<Vec<u8>> {
237 self.inner.get_launch_token(
238 sigstruct,
239 attributes,
240 )
241 }
242
243 #[cfg(not(windows))]
245 pub fn get_supported_att_key_ids(&self) -> Result<Vec<Vec<u8>>> {
246 self.inner.get_supported_att_key_ids()
247 }
248
249 #[cfg(not(windows))]
253 pub fn init_quote_ex(&self, att_key_id: Vec<u8>) -> Result<QuoteInfo> {
254 self.inner.init_quote_ex(att_key_id)
255 }
256
257 #[cfg(not(windows))]
264 pub fn get_quote_ex(
265 &self,
266 att_key_id: Vec<u8>,
267 report: Vec<u8>,
268 target_info: Option<Vec<u8>>,
269 nonce: Vec<u8>
270 ) -> Result<QuoteResult> {
271 let target_info = target_info.unwrap_or_else( ||
272 AsRef::<[u8]>::as_ref(&sgx_isa::Targetinfo::from(sgx_isa::Report::try_copy_from(&report).unwrap()))
273 .to_owned()
274 );
275 self.inner.get_quote_ex(att_key_id, report, target_info, nonce)
276 }
277}
278
279#[cfg(feature = "sgxs")]
280impl EinittokenProvider for AesmClient {
281 fn token(
282 &mut self,
283 sigstruct: &Sigstruct,
284 attributes: Attributes,
285 _retry: bool,
286 ) -> StdResult<Einittoken, ::anyhow::Error> {
287 let token = self.get_launch_token(
288 sigstruct,
289 attributes,
290 )?;
291 Einittoken::try_copy_from(&token).ok_or(Error::InvalidTokenSize.into())
292 }
293
294 fn can_retry(&self) -> bool {
295 false
296 }
297}
298
299trait AesmRequest: protobuf::Message + Into<Request> {
300 type Response: protobuf::Message + TryFrom<ProtobufResult<Response>, Error = Error>;
301
302 #[cfg(not(target_env = "sgx"))]
303 fn get_timeout(&self) -> Option<u32>;
304}
305
306macro_rules! define_aesm_message {
307 ($request:ident, $req_field:ident, $response:ident, $resp_field:ident) => {
308 impl AesmRequest for $request {
309 type Response = $response;
310
311 #[cfg(not(target_env = "sgx"))]
312 fn get_timeout(&self) -> Option<u32> {
313 if self.has_timeout() {
314 Some($request::timeout(self))
315 } else {
316 None
317 }
318 }
319 }
320 impl From<$request> for Request {
321 fn from(r: $request) -> Request {
322 let mut req = Request::new();
323 req.$req_field = Some(r).into();
324 req
325 }
326 }
327 impl TryFrom<ProtobufResult<Response>> for $response {
328 type Error = Error;
329
330 fn try_from(res: ProtobufResult<Response>) -> Result<Self> {
331 if let Ok(Response { $resp_field: MessageField(Some(body)), .. }) = res {
332 match body.errorCode() {
333 AESM_SUCCESS => Ok(*body),
334 code => Err(Error::aesm_code(code)),
335 }
336 } else {
337 Err(Error::aesm_bad_response(stringify!($response)))
338 }
339 }
340 }
341 }
342}
343
344define_aesm_message!(GetQuoteRequest, getQuoteReq, GetQuoteResponse, getQuoteRes);
345define_aesm_message!(InitQuoteRequest, initQuoteReq, InitQuoteResponse, initQuoteRes);
346define_aesm_message!(GetLaunchTokenRequest, getLicTokenReq, GetLaunchTokenResponse, getLicTokenRes);
347define_aesm_message!(GetQuoteExRequest, getQuoteExReq, GetQuoteExResponse, getQuoteExRes);
348define_aesm_message!(InitQuoteExRequest, initQuoteExReq, InitQuoteExResponse, initQuoteExRes);
349define_aesm_message!(GetQuoteSizeExRequest, getQuoteSizeExReq, GetQuoteSizeExResponse, getQuoteSizeExRes);
350define_aesm_message!(GetSupportedAttKeyIDNumRequest, getSupportedAttKeyIDNumReq, GetSupportedAttKeyIDNumResponse, getSupportedAttKeyIDNumRes);
351define_aesm_message!(GetSupportedAttKeyIDsRequest, getSupportedAttKeyIDsReq, GetSupportedAttKeyIDsResponse, getSupportedAttKeyIDsRes);
352
353#[cfg(all(test, feature = "test-sgx"))]
354mod tests {
355 extern crate sgx_isa;
357
358 use self::sgx_isa::{Report, Targetinfo};
359 use super::*;
360
361 const SPID_SIZE: usize = 16;
362 const NONCE_SIZE: usize = 16;
363
364 #[test]
365 fn test_init_quote() {
366 let quote = AesmClient::new().init_quote().unwrap();
367 assert_eq!(
368 quote.target_info().len(),
369 ::std::mem::size_of::<Targetinfo>()
370 );
371 assert!(quote.gid().len() != 0);
372 }
373
374 #[test]
375 fn test_get_quote() {
376 let client = AesmClient::new();
380
381 let _quote_info = client.init_quote().unwrap();
382
383 let quote = client
384 .get_quote(
385 vec![0u8; Report::UNPADDED_SIZE],
386 vec![0u8; SPID_SIZE],
387 vec![],
388 QuoteType::Linkable,
389 vec![0u8; NONCE_SIZE],
390 )
391 .unwrap_err();
392
393 assert!(if let Error::AesmCode(_) = quote {
394 true
395 } else {
396 false
397 });
398 }
399}