#![doc(html_logo_url = "https://edp.fortanix.com/img/docs/edp-logo.svg",
html_favicon_url = "https://edp.fortanix.com/favicon.ico",
html_root_url = "https://edp.fortanix.com/docs/api/")]
#![allow(non_local_definitions)] #![deny(warnings)]
extern crate byteorder;
pub extern crate anyhow;
pub extern crate thiserror;
#[macro_use]
#[cfg(unix)]
extern crate lazy_static;
extern crate protobuf;
#[cfg(feature = "sgxs")]
extern crate sgxs;
#[cfg(unix)]
extern crate unix_socket;
#[cfg(windows)]
extern crate winapi;
extern crate sgx_isa;
#[cfg(feature = "sgxs")]
use std::result::Result as StdResult;
use protobuf::ProtobufResult;
#[cfg(feature = "sgxs")]
use sgxs::einittoken::{Einittoken, EinittokenProvider};
#[cfg(all(not(target_env = "sgx"),feature = "sgxs"))]
use sgx_isa::{Attributes, Sigstruct};
include!(concat!(env!("OUT_DIR"), "/mod_aesm_proto.rs"));
mod error;
use self::aesm_proto::*;
pub use error::{AesmError, Error, Result};
#[cfg(windows)]
#[path = "imp/windows.rs"]
mod imp;
#[cfg(unix)]
#[path = "imp/unix.rs"]
mod imp;
#[cfg(target_env = "sgx")]
#[path = "imp/sgx.rs"]
mod imp;
#[cfg(unix)]
pub mod unix {
use std::path::Path;
pub trait AesmClientExt {
fn with_path<P: AsRef<Path>>(path: P) -> Self;
}
}
#[cfg(target_env = "sgx")]
pub mod sgx {
use std::net::TcpStream;
pub trait AesmClientExt {
fn new(tcp_stream: TcpStream) -> Self;
}
}
const AESM_SUCCESS: u32 = 0;
#[repr(u32)]
pub enum QuoteType {
Unlinkable = 0,
Linkable = 1,
}
impl Into<u32> for QuoteType {
fn into(self: QuoteType) -> u32 {
use self::QuoteType::*;
match self {
Unlinkable => 0,
Linkable => 1,
}
}
}
impl QuoteType {
pub fn from_u32(v: u32) -> Result<Self> {
use self::QuoteType::*;
Ok(match v {
0 => Unlinkable,
1 => Linkable,
_ => return Err(Error::InvalidQuoteType(v)),
})
}
}
#[derive(Debug)]
pub struct QuoteInfo {
target_info: Vec<u8>,
pub_key_id: Vec<u8>,
}
impl QuoteInfo {
pub fn target_info(&self) -> &[u8] {
&self.target_info
}
pub fn gid(&self) -> Vec<u8> {
let mut pk = self.pub_key_id.clone();
pk.reverse();
pk
}
pub fn pub_key_id(&self) -> &[u8] {
&self.pub_key_id
}
}
fn quote_buffer_size(sig_rl: &[u8]) -> u32 {
let quote_length = 436 + 288 + 12 + 4 + 16;
let sig_length = 352 + 4 + 4 + (sig_rl.len() as u32 * 5 / 4) + 128;
quote_length + sig_length
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct QuoteResult {
quote: Vec<u8>,
qe_report: Vec<u8>,
}
impl QuoteResult {
pub fn new<T: Into<Vec<u8>>, U: Into<Vec<u8>>>(quote: T, qe_report: U) -> Self {
QuoteResult {
quote: quote.into(),
qe_report: qe_report.into(),
}
}
pub fn quote(&self) -> &[u8] {
&self.quote
}
pub fn qe_report(&self) -> &[u8] {
&self.qe_report
}
}
#[cfg_attr(not(target_env = "sgx"), derive(Default))]
#[derive(Debug, Clone)]
pub struct AesmClient {
inner: imp::AesmClient
}
impl AesmClient {
#[cfg(not(target_env = "sgx"))]
pub fn new() -> Self {
AesmClient { inner: imp::AesmClient::new() }
}
pub fn try_connect(&self) -> Result<()> {
self.inner.try_connect()
}
pub fn init_quote(&self) -> Result<QuoteInfo> {
self.inner.init_quote()
}
pub fn get_quote(
&self,
report: Vec<u8>,
spid: Vec<u8>,
sig_rl: Vec<u8>,
quote_type: QuoteType,
nonce: Vec<u8>,
) -> Result<QuoteResult> {
self.inner.get_quote(
report,
spid,
sig_rl,
quote_type,
nonce,
)
}
#[cfg(all(not(target_env = "sgx"), feature = "sgxs"))]
pub fn get_launch_token(
&self,
sigstruct: &Sigstruct,
attributes: Attributes,
) -> Result<Vec<u8>> {
self.inner.get_launch_token(
sigstruct,
attributes,
)
}
#[cfg(not(windows))]
pub fn get_supported_att_key_ids(&self) -> Result<Vec<Vec<u8>>> {
self.inner.get_supported_att_key_ids()
}
#[cfg(not(windows))]
pub fn init_quote_ex(&self, att_key_id: Vec<u8>) -> Result<QuoteInfo> {
self.inner.init_quote_ex(att_key_id)
}
#[cfg(not(windows))]
pub fn get_quote_ex(
&self,
att_key_id: Vec<u8>,
report: Vec<u8>,
target_info: Option<Vec<u8>>,
nonce: Vec<u8>
) -> Result<QuoteResult> {
let target_info = target_info.unwrap_or_else( ||
AsRef::<[u8]>::as_ref(&sgx_isa::Targetinfo::from(sgx_isa::Report::try_copy_from(&report).unwrap()))
.to_owned()
);
self.inner.get_quote_ex(att_key_id, report, target_info, nonce)
}
}
#[cfg(feature = "sgxs")]
impl EinittokenProvider for AesmClient {
fn token(
&mut self,
sigstruct: &Sigstruct,
attributes: Attributes,
_retry: bool,
) -> StdResult<Einittoken, ::anyhow::Error> {
let token = self.get_launch_token(
sigstruct,
attributes,
)?;
Einittoken::try_copy_from(&token).ok_or(Error::InvalidTokenSize.into())
}
fn can_retry(&self) -> bool {
false
}
}
trait AesmRequest: protobuf::Message + Into<Request> {
type Response: protobuf::Message + FromResponse;
#[cfg(not(target_env = "sgx"))]
fn get_timeout(&self) -> Option<u32>;
}
trait FromResponse: Sized {
fn from_response(res: ProtobufResult<Response>) -> Result<Self>;
}
macro_rules! define_aesm_message {
($request:ident, $response:ident, $set:ident, $has:ident, $take:ident) => {
impl AesmRequest for $request {
type Response = $response;
#[cfg(not(target_env = "sgx"))]
fn get_timeout(&self) -> Option<u32> {
if self.has_timeout() {
Some(Self::get_timeout(self))
} else {
None
}
}
}
impl From<$request> for Request {
fn from(r: $request) -> Request {
let mut req = Request::new();
req.$set(r);
req
}
}
impl FromResponse for $response {
fn from_response(mut res: ProtobufResult<Response>) -> Result<Self> {
match res {
Ok(ref mut res) if res.$has() => {
let body = res.$take();
match body.get_errorCode() {
AESM_SUCCESS => Ok(body),
code => Err(Error::aesm_code(code)),
}
}
_ => Err(Error::aesm_bad_response(stringify!($response))),
}
}
}
}
}
define_aesm_message!(Request_GetQuoteRequest, Response_GetQuoteResponse, set_getQuoteReq, has_getQuoteRes, take_getQuoteRes);
define_aesm_message!(Request_InitQuoteRequest, Response_InitQuoteResponse, set_initQuoteReq, has_initQuoteRes, take_initQuoteRes);
define_aesm_message!(Request_GetLaunchTokenRequest, Response_GetLaunchTokenResponse, set_getLicTokenReq, has_getLicTokenRes, take_getLicTokenRes);
define_aesm_message!(Request_GetQuoteExRequest, Response_GetQuoteExResponse, set_getQuoteExReq, has_getQuoteExRes, take_getQuoteExRes);
define_aesm_message!(Request_InitQuoteExRequest, Response_InitQuoteExResponse, set_initQuoteExReq, has_initQuoteExRes, take_initQuoteExRes);
define_aesm_message!(Request_GetQuoteSizeExRequest, Response_GetQuoteSizeExResponse, set_getQuoteSizeExReq, has_getQuoteSizeExRes, take_getQuoteSizeExRes);
define_aesm_message!(Request_GetSupportedAttKeyIDNumRequest, Response_GetSupportedAttKeyIDNumResponse, set_getSupportedAttKeyIDNumReq, has_getSupportedAttKeyIDNumRes, take_getSupportedAttKeyIDNumRes);
define_aesm_message!(Request_GetSupportedAttKeyIDsRequest, Response_GetSupportedAttKeyIDsResponse, set_getSupportedAttKeyIDsReq, has_getSupportedAttKeyIDsRes, take_getSupportedAttKeyIDsRes);
#[cfg(all(test, feature = "test-sgx"))]
mod tests {
extern crate sgx_isa;
use self::sgx_isa::{Report, Targetinfo};
use super::*;
const SPID_SIZE: usize = 16;
const NONCE_SIZE: usize = 16;
#[test]
fn test_init_quote() {
let quote = AesmClient::new().init_quote().unwrap();
assert_eq!(
quote.target_info().len(),
::std::mem::size_of::<Targetinfo>()
);
assert!(quote.gid().len() != 0);
}
#[test]
fn test_get_quote() {
let client = AesmClient::new();
let _quote_info = client.init_quote().unwrap();
let quote = client
.get_quote(
vec![0u8; Report::UNPADDED_SIZE],
vec![0u8; SPID_SIZE],
vec![],
QuoteType::Linkable,
vec![0u8; NONCE_SIZE],
)
.unwrap_err();
assert!(if let Error::AesmCode(_) = quote {
true
} else {
false
});
}
}