#![cfg_attr(feature = "sgx", no_std)]
#[cfg(feature = "sgx")]
extern crate sgx_tstd as std;
#[cfg(feature = "sgx")]
use sgx_types::*;
use super::SgxError;
pub struct RemoteAttestation {
#[cfg(feature = "sgx")]
context: sgx_ra_context_t,
sp_public_key: Option<[u8; 64]>,
spid: Option<[u8; 16]>,
quote: Option<Vec<u8>>,
}
impl RemoteAttestation {
pub fn new() -> Self {
Self {
#[cfg(feature = "sgx")]
context: 0,
sp_public_key: None,
spid: None,
quote: None,
}
}
pub fn configure_spid(&mut self, spid: [u8; 16]) {
self.spid = Some(spid);
}
#[cfg(feature = "sgx")]
pub fn init_attestation(&mut self, sp_pub_key: &[u8; 64]) -> Result<(), SgxError> {
self.sp_public_key = Some(*sp_pub_key);
let mut context = 0;
let result = unsafe {
sgx_ra_init(
sp_pub_key.as_ptr() as *const sgx_ec256_public_t,
0, &mut context,
)
};
if result != sgx_status_t::SGX_SUCCESS {
return Err(SgxError::AttestationError("Failed to initialize RA".into()));
}
self.context = context;
Ok(())
}
#[cfg(not(feature = "sgx"))]
pub fn init_attestation(&mut self, sp_pub_key: &[u8; 64]) -> Result<(), SgxError> {
self.sp_public_key = Some(*sp_pub_key);
Ok(())
}
#[cfg(feature = "sgx")]
pub fn generate_quote(&mut self, user_data: &[u8]) -> Result<Vec<u8>, SgxError> {
if user_data.len() > 64 {
return Err(SgxError::AttestationError("User data too large".into()));
}
let spid = self
.spid
.ok_or_else(|| SgxError::AttestationError("SPID not configured".into()))?;
let mut target_info = sgx_target_info_t::default();
let mut report_data = sgx_report_data_t::default();
report_data.d[..user_data.len()].copy_from_slice(user_data);
let mut report = sgx_report_t::default();
let result = unsafe { sgx_create_report(&target_info, &report_data, &mut report) };
if result != sgx_status_t::SGX_SUCCESS {
return Err(SgxError::AttestationError("Failed to create report".into()));
}
let quote_size = 2048;
let mut quote = vec![0u8; quote_size];
let qe_result = unsafe {
sgx_get_quote(
&report,
sgx_quote_sign_type_t::SGX_LINKABLE_SIGNATURE,
&spid as *const _ as *const sgx_spid_t,
std::ptr::null(),
std::ptr::null(),
0,
std::ptr::null_mut(),
quote.as_mut_ptr() as *mut sgx_quote_t,
quote_size as u32,
)
};
if qe_result != sgx_status_t::SGX_SUCCESS {
return Err(SgxError::AttestationError("Failed to generate quote".into()));
}
self.quote = Some(quote.clone());
Ok(quote)
}
#[cfg(not(feature = "sgx"))]
pub fn generate_quote(&mut self, user_data: &[u8]) -> Result<Vec<u8>, SgxError> {
if user_data.len() > 64 {
return Err(SgxError::AttestationError("User data too large".into()));
}
let mut quote = vec![0u8; 256];
quote[..user_data.len()].copy_from_slice(user_data);
self.quote = Some(quote.clone());
Ok(quote)
}
pub fn get_quote(&self) -> Option<&[u8]> {
self.quote.as_deref()
}
#[cfg(feature = "sgx")]
pub fn close(&mut self) -> Result<(), SgxError> {
if self.context != 0 {
let result = unsafe { sgx_ra_close(self.context) };
if result != sgx_status_t::SGX_SUCCESS {
return Err(SgxError::AttestationError("Failed to close RA".into()));
}
self.context = 0;
}
Ok(())
}
#[cfg(not(feature = "sgx"))]
pub fn close(&mut self) -> Result<(), SgxError> {
Ok(())
}
}
pub struct QuoteVerifier {
ias_api_key: Option<String>,
ias_url: Option<String>,
}
impl QuoteVerifier {
pub fn new() -> Self {
Self { ias_api_key: None, ias_url: None }
}
fn verify_with_ias(&self, _quote: &[u8], _api_key: &str, _url: &str) -> Result<bool, SgxError> {
Err(SgxError::AttestationError(
"IAS quote verification is not implemented; do not treat quotes as verified".into(),
))
}
pub fn configure_ias(&mut self, api_key: String, url: String) {
self.ias_api_key = Some(api_key);
self.ias_url = Some(url);
}
pub fn verify_quote(&self, quote: &[u8]) -> Result<QuoteVerificationResult, SgxError> {
let ias_api_key = self
.ias_api_key
.as_ref()
.ok_or_else(|| SgxError::AttestationError("IAS API key not configured".into()))?;
let ias_url = self
.ias_url
.as_ref()
.ok_or_else(|| SgxError::AttestationError("IAS URL not configured".into()))?;
let verification_result = self.verify_with_ias(quote, ias_api_key, ias_url)?;
if !verification_result {
return Err(SgxError::AttestationError("Quote verification failed".into()));
}
Ok(QuoteVerificationResult {
verified: verification_result,
mrenclave: extract_mrenclave(quote),
mrsigner: extract_mrsigner(quote),
product_id: 0,
isv_svn: 0,
tcb_status: TcbStatus::UpToDate,
})
}
}
#[derive(Debug, Clone)]
pub struct QuoteVerificationResult {
pub verified: bool,
pub mrenclave: [u8; 32],
pub mrsigner: [u8; 32],
pub product_id: u16,
pub isv_svn: u16,
pub tcb_status: TcbStatus,
}
#[derive(Debug, Clone)]
pub enum TcbStatus {
UpToDate,
SWHardeningNeeded,
ConfigurationNeeded,
OutOfDate,
Revoked,
}
fn extract_mrenclave(quote: &[u8]) -> [u8; 32] {
let mut mrenclave = [0u8; 32];
if quote.len() >= 64 {
mrenclave.copy_from_slice("e[..32]);
}
mrenclave
}
fn extract_mrsigner(quote: &[u8]) -> [u8; 32] {
let mut mrsigner = [0u8; 32];
if quote.len() >= 96 {
mrsigner.copy_from_slice("e[32..64]);
}
mrsigner
}
#[cfg(feature = "sgx")]
extern "C" {
fn sgx_ra_init(
p_pub_key: *const sgx_ec256_public_t,
b_pse: i32,
p_context: *mut sgx_ra_context_t,
) -> sgx_status_t;
fn sgx_ra_close(context: sgx_ra_context_t) -> sgx_status_t;
fn sgx_create_report(
p_ti: *const sgx_target_info_t,
p_report_data: *const sgx_report_data_t,
p_report: *mut sgx_report_t,
) -> sgx_status_t;
fn sgx_get_quote(
p_report: *const sgx_report_t,
quote_type: sgx_quote_sign_type_t,
p_spid: *const sgx_spid_t,
p_nonce: *const sgx_quote_nonce_t,
p_sig_rl: *const u8,
sig_rl_size: u32,
p_qe_report: *mut sgx_report_t,
p_quote: *mut sgx_quote_t,
quote_size: u32,
) -> sgx_status_t;
}