detee-sgx/src/quote.rs
2024-10-27 15:55:09 +02:00

211 lines
5.7 KiB
Rust

use crate::bindings::*;
use crate::error::SgxError;
use crate::ioctl::SgxIoctlClient;
use lazy_static::lazy_static;
use std::fmt::Debug;
use std::ops::Deref;
use std::sync::Mutex;
pub struct Quote {
buf: Vec<u8>,
pub(crate) report_body: *const sgx_report_body_t,
}
impl TryFrom<Vec<u8>> for Quote {
type Error = SgxError;
fn try_from(buf: Vec<u8>) -> Result<Self, Self::Error> {
let report_body_offset = size_of::<QuoteHeader>();
let report_body_size = size_of::<sgx_report_body_t>();
if buf.len() < report_body_offset + report_body_size {
let minimal = report_body_offset + report_body_size;
let actual = buf.len();
return Err(SgxError::QuoteError(format!(
"Failed to parse DCAP quote, min {minimal}, act {actual}"
)));
}
let report_body = buf.as_slice()[report_body_offset..].as_ptr() as *const sgx_report_body_t;
Ok(Self { buf, report_body })
}
}
#[repr(C)]
struct QuoteHeader {
pub version: u16,
pub att_key_type: u16,
pub att_key_data_0: u32,
pub qe_svn: u16,
pub pce_svn: u16,
pub vendor_id: [u8; 16],
pub user_data: [u8; 20],
}
impl TryFrom<&[u8]> for Quote {
type Error = SgxError;
fn try_from(buf: &[u8]) -> Result<Self, Self::Error> {
buf.to_vec().try_into()
}
}
impl TryFrom<ReportData> for Quote {
type Error = SgxError;
fn try_from(value: ReportData) -> Result<Self, Self::Error> {
Self::from_report_data(value)
}
}
impl Deref for Quote {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.buf.as_ref()
}
}
impl Quote {
pub fn from_report_data(data: ReportData) -> Result<Self, SgxError> {
IOCTL_CLIENT
.lock()
.unwrap()
.generate_quote(data)?
.try_into()
}
pub fn from_slice(slice: &[u8]) -> Result<Self, SgxError> {
slice.try_into()
}
pub fn as_slice(&self) -> &[u8] {
self
}
pub fn verify(&self) -> Result<VerifyResult, SgxError> {
IOCTL_CLIENT.lock().unwrap().verify_quote(self.buf.as_ref())
}
pub fn isv_family_id(&self) -> sgx_isvfamily_id_t {
unsafe { (*self.report_body).isv_family_id }
}
pub fn isv_ext_prod_id(&self) -> sgx_isvext_prod_id_t {
unsafe { (*self.report_body).isv_ext_prod_id }
}
pub fn config_id(&self) -> sgx_config_id_t {
unsafe { (*self.report_body).config_id }
}
pub fn mrenclave(&self) -> sgx_measurement_t {
unsafe { (*self.report_body).mr_enclave }
}
pub fn mrsigner(&self) -> sgx_measurement_t {
unsafe { (*self.report_body).mr_signer }
}
pub fn product_id(&self) -> sgx_prod_id_t {
unsafe { (*self.report_body).isv_prod_id }
}
pub fn version(&self) -> sgx_isv_svn_t {
unsafe { (*self.report_body).isv_svn }
}
pub fn report_data(&self) -> ReportData {
unsafe { (*self.report_body).report_data.into() }
}
}
impl Debug for Quote {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("SGXQuote")
.field("mrenclave", &self.mrenclave())
.field("mrsigner", &self.mrsigner())
.field("report_body", &self.report_data())
.field("product_id", &self.product_id())
.field("version", &self.version())
.field("family_id", &self.isv_family_id())
.field("ext_prod_id", &self.isv_ext_prod_id())
.field("config_id", &self.config_id())
.finish()
}
}
lazy_static! {
pub static ref IOCTL_CLIENT: Mutex<SgxIoctlClient> = {
let client = SgxIoctlClient::new();
Mutex::new(client)
};
}
// lazy_static! {
// pub static ref STATIC_QUOTE: once_cell::sync::OnceCell<Quote> =
// OnceCell::with_value(Quote::from_report_data([0u8; 64]).unwrap());
// }
// unsafe impl Send for Quote {}
lazy_static! {
pub static ref STATIC_QUOTE: Result<Quote, SgxError> = Quote::from_report_data([0u8; 64]);
}
unsafe impl Sync for Quote {}
pub type ReportData = [u8; 64];
#[derive(Debug)]
pub enum VerifyResult {
Ok,
ConfigNeeded,
OutOfDate,
OutOfDateConfigNeeded,
InvalidSignature,
Revoked,
Unspecified,
SwHardeningNeeded,
ConfigAndSwHardeningNeeded,
}
impl From<sgx_ql_qv_result_t> for VerifyResult {
fn from(result: sgx_ql_qv_result_t) -> Self {
match result {
SGX_QL_QV_RESULT_OK => VerifyResult::Ok,
SGX_QL_QV_RESULT_CONFIG_NEEDED => VerifyResult::ConfigNeeded,
SGX_QL_QV_RESULT_OUT_OF_DATE => VerifyResult::OutOfDate,
SGX_QL_QV_RESULT_OUT_OF_DATE_CONFIG_NEEDED => VerifyResult::OutOfDateConfigNeeded,
SGX_QL_QV_RESULT_INVALID_SIGNATURE => VerifyResult::InvalidSignature,
SGX_QL_QV_RESULT_REVOKED => VerifyResult::Revoked,
SGX_QL_QV_RESULT_SW_HARDENING_NEEDED => VerifyResult::SwHardeningNeeded,
SGX_QL_QV_RESULT_CONFIG_AND_SW_HARDENING_NEEDED => {
VerifyResult::ConfigAndSwHardeningNeeded
}
//SGX_QL_QV_RESULT_UNSPECIFIED => QuoteVerifyResult::Unspecified,
_ => VerifyResult::Unspecified,
}
}
}
impl VerifyResult {
pub fn is_ok(&self) -> bool {
match self {
VerifyResult::Ok => true,
_ => false,
}
}
pub fn is_negligible(&self) -> bool {
match self {
VerifyResult::Ok => true,
VerifyResult::ConfigNeeded => true,
VerifyResult::OutOfDate => true,
VerifyResult::OutOfDateConfigNeeded => true,
VerifyResult::SwHardeningNeeded => true,
VerifyResult::ConfigAndSwHardeningNeeded => true,
_ => false,
}
}
}