2024-02-26 10:56:55 +00:00
|
|
|
use std::{
|
2024-09-25 19:14:06 +00:00
|
|
|
error::Error,
|
2024-02-26 10:56:55 +00:00
|
|
|
fs::{read, File},
|
2024-09-25 19:14:06 +00:00
|
|
|
io::{Error as IoError, ErrorKind, Write},
|
2024-02-26 10:56:55 +00:00
|
|
|
};
|
|
|
|
|
2024-09-25 19:14:06 +00:00
|
|
|
use mc_sgx_dcap_sys_types::sgx_ql_qve_collateral_t;
|
2024-02-27 22:30:36 +00:00
|
|
|
use quartz_cw::{
|
2024-09-25 19:14:06 +00:00
|
|
|
msg::{
|
|
|
|
execute::attested::{
|
|
|
|
Attestation, DcapAttestation, EpidAttestation, HasUserData, MockAttestation,
|
|
|
|
RawDcapAttestation, RawEpidAttestation, RawMockAttestation,
|
|
|
|
},
|
|
|
|
HasDomainType,
|
|
|
|
},
|
2024-02-27 22:30:36 +00:00
|
|
|
state::{MrEnclave, UserData},
|
|
|
|
};
|
2024-09-25 19:14:06 +00:00
|
|
|
use quartz_tee_ra::intel_sgx::dcap::{Collateral, Quote3Error};
|
|
|
|
use reqwest::blocking::Client;
|
|
|
|
use serde::Serialize;
|
2024-02-26 10:56:55 +00:00
|
|
|
|
2024-09-25 19:14:06 +00:00
|
|
|
use crate::types::Fmspc;
|
2024-07-18 23:34:31 +00:00
|
|
|
|
2024-09-26 12:07:07 +00:00
|
|
|
#[cfg(not(feature = "mock-sgx"))]
|
|
|
|
pub type DefaultAttestor = DcapAttestor;
|
|
|
|
|
|
|
|
#[cfg(feature = "mock-sgx")]
|
|
|
|
pub type DefaultAttestor = MockAttestor;
|
|
|
|
|
2024-08-02 16:31:01 +00:00
|
|
|
/// The trait defines the interface for generating attestations from within an enclave.
|
2024-09-18 20:04:33 +00:00
|
|
|
pub trait Attestor: Send + Sync + 'static {
|
2024-02-26 10:56:55 +00:00
|
|
|
type Error: ToString;
|
2024-09-25 19:14:06 +00:00
|
|
|
type Attestation: Attestation;
|
|
|
|
type RawAttestation: HasDomainType<DomainType = Self::Attestation> + Serialize;
|
2024-02-26 10:56:55 +00:00
|
|
|
|
|
|
|
fn quote(&self, user_data: impl HasUserData) -> Result<Vec<u8>, Self::Error>;
|
2024-02-27 22:30:36 +00:00
|
|
|
|
|
|
|
fn mr_enclave(&self) -> Result<MrEnclave, Self::Error>;
|
2024-09-25 19:14:06 +00:00
|
|
|
|
|
|
|
fn attestation(&self, user_data: impl HasUserData) -> Result<Self::Attestation, Self::Error>;
|
2024-02-26 10:56:55 +00:00
|
|
|
}
|
|
|
|
|
2024-08-02 16:31:01 +00:00
|
|
|
/// An `Attestor` for generating EPID attestations for Gramine based enclaves.
|
2024-07-18 23:34:31 +00:00
|
|
|
#[derive(Clone, PartialEq, Debug, Default)]
|
2024-02-26 10:56:55 +00:00
|
|
|
pub struct EpidAttestor;
|
|
|
|
|
|
|
|
impl Attestor for EpidAttestor {
|
|
|
|
type Error = IoError;
|
2024-09-25 19:14:06 +00:00
|
|
|
type Attestation = EpidAttestation;
|
|
|
|
type RawAttestation = RawEpidAttestation;
|
2024-02-26 10:56:55 +00:00
|
|
|
|
|
|
|
fn quote(&self, user_data: impl HasUserData) -> Result<Vec<u8>, Self::Error> {
|
|
|
|
let user_data = user_data.user_data();
|
|
|
|
let mut user_report_data = File::create("/dev/attestation/user_report_data")?;
|
|
|
|
user_report_data.write_all(user_data.as_slice())?;
|
|
|
|
user_report_data.flush()?;
|
|
|
|
read("/dev/attestation/quote")
|
|
|
|
}
|
2024-02-27 22:30:36 +00:00
|
|
|
|
|
|
|
fn mr_enclave(&self) -> Result<MrEnclave, Self::Error> {
|
|
|
|
let quote = self.quote(NullUserData)?;
|
|
|
|
Ok(quote[112..(112 + 32)]
|
|
|
|
.try_into()
|
|
|
|
.expect("hardcoded array size"))
|
|
|
|
}
|
2024-09-25 19:14:06 +00:00
|
|
|
|
|
|
|
fn attestation(&self, _user_data: impl HasUserData) -> Result<Self::Attestation, Self::Error> {
|
|
|
|
unimplemented!()
|
|
|
|
}
|
2024-02-26 10:56:55 +00:00
|
|
|
}
|
2024-02-27 19:52:23 +00:00
|
|
|
|
2024-08-02 16:31:01 +00:00
|
|
|
/// An `Attestor` for generating DCAP attestations for Gramine based enclaves.
|
2024-09-25 19:14:06 +00:00
|
|
|
#[derive(Clone, PartialEq, Debug)]
|
|
|
|
pub struct DcapAttestor {
|
|
|
|
pub fmspc: Fmspc,
|
|
|
|
}
|
2024-08-02 16:31:01 +00:00
|
|
|
|
|
|
|
impl Attestor for DcapAttestor {
|
|
|
|
type Error = IoError;
|
2024-09-25 19:14:06 +00:00
|
|
|
type Attestation = DcapAttestation;
|
|
|
|
type RawAttestation = RawDcapAttestation;
|
2024-08-02 16:31:01 +00:00
|
|
|
|
|
|
|
fn quote(&self, user_data: impl HasUserData) -> Result<Vec<u8>, Self::Error> {
|
|
|
|
let user_data = user_data.user_data();
|
|
|
|
let mut user_report_data = File::create("/dev/attestation/user_report_data")?;
|
|
|
|
user_report_data.write_all(user_data.as_slice())?;
|
|
|
|
user_report_data.flush()?;
|
|
|
|
read("/dev/attestation/quote")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mr_enclave(&self) -> Result<MrEnclave, Self::Error> {
|
|
|
|
let quote = self.quote(NullUserData)?;
|
|
|
|
Ok(quote[112..(112 + 32)]
|
|
|
|
.try_into()
|
|
|
|
.expect("hardcoded array size"))
|
|
|
|
}
|
2024-09-25 19:14:06 +00:00
|
|
|
|
|
|
|
fn attestation(&self, user_data: impl HasUserData) -> Result<Self::Attestation, Self::Error> {
|
|
|
|
fn pccs_query_pck() -> Result<(Vec<u8>, String), Box<dyn Error>> {
|
|
|
|
let url = "https://127.0.0.1:8081/sgx/certification/v4/pckcrl?ca=processor";
|
|
|
|
|
|
|
|
let client = Client::builder()
|
|
|
|
.danger_accept_invalid_certs(true) // FIXME(hu55a1n1): required?
|
|
|
|
.build()?;
|
|
|
|
let response = client.get(url).send()?;
|
|
|
|
|
|
|
|
// Parse relevant headers
|
|
|
|
let pck_crl_issuer_chain = response
|
|
|
|
.headers()
|
|
|
|
.get("SGX-PCK-CRL-Issuer-Chain")
|
|
|
|
.ok_or("Missing PCK-Issuer-Chain header")?
|
|
|
|
.to_str()?
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
let pck_crl = response.bytes()?;
|
|
|
|
|
|
|
|
Ok((pck_crl.to_vec(), pck_crl_issuer_chain))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn collateral(
|
|
|
|
tcb_info: &str,
|
|
|
|
pck_crl: Vec<u8>,
|
|
|
|
pck_crl_issuer_chain: String,
|
|
|
|
) -> Collateral {
|
|
|
|
let mut sgx_collateral = sgx_ql_qve_collateral_t::default();
|
|
|
|
|
|
|
|
// SAFETY: Version is a union which is inherently unsafe
|
|
|
|
#[allow(unsafe_code)]
|
|
|
|
let version = unsafe { sgx_collateral.__bindgen_anon_1.__bindgen_anon_1.as_mut() };
|
|
|
|
version.major_version = 3;
|
|
|
|
version.minor_version = 1;
|
|
|
|
|
|
|
|
let mut root_crl =
|
|
|
|
include_bytes!("../../../cosmwasm/packages/quartz-tee-ra/data/root_crl.der")
|
|
|
|
.to_vec();
|
|
|
|
root_crl.push(0);
|
|
|
|
sgx_collateral.root_ca_crl = root_crl.as_ptr() as _;
|
|
|
|
sgx_collateral.root_ca_crl_size = root_crl.len() as u32;
|
|
|
|
|
|
|
|
let mut pck_crl = hex::decode(pck_crl).unwrap();
|
|
|
|
pck_crl.push(0);
|
|
|
|
sgx_collateral.pck_crl = pck_crl.as_ptr() as _;
|
|
|
|
sgx_collateral.pck_crl_size = pck_crl.len() as u32;
|
|
|
|
|
|
|
|
let pck_crl_issuer_chain = urlencoding::decode(&pck_crl_issuer_chain).unwrap();
|
|
|
|
// pck_crl_issuer_chain.push(0);
|
|
|
|
sgx_collateral.pck_crl_issuer_chain = pck_crl_issuer_chain.as_ptr() as _;
|
|
|
|
sgx_collateral.pck_crl_issuer_chain_size = pck_crl_issuer_chain.len() as u32;
|
|
|
|
|
|
|
|
let root_cert =
|
|
|
|
include_str!("../../../cosmwasm/packages/quartz-tee-ra/data/root_ca.pem");
|
|
|
|
let tcb_cert =
|
|
|
|
include_str!("../../../cosmwasm/packages/quartz-tee-ra/data/tcb_signer.pem");
|
|
|
|
let mut tcb_chain = [tcb_cert, root_cert].join("\n").as_bytes().to_vec();
|
|
|
|
tcb_chain.push(0);
|
|
|
|
sgx_collateral.tcb_info_issuer_chain = tcb_chain.as_ptr() as _;
|
|
|
|
sgx_collateral.tcb_info_issuer_chain_size = tcb_chain.len() as u32;
|
|
|
|
|
|
|
|
sgx_collateral.tcb_info = tcb_info.as_ptr() as _;
|
|
|
|
sgx_collateral.tcb_info_size = tcb_info.len() as u32;
|
|
|
|
|
|
|
|
// For live data the QE identity uses the same chain as the TCB info
|
|
|
|
sgx_collateral.qe_identity_issuer_chain = tcb_chain.as_ptr() as _;
|
|
|
|
sgx_collateral.qe_identity_issuer_chain_size = tcb_chain.len() as u32;
|
|
|
|
|
|
|
|
const QE_IDENTITY_JSON: &str =
|
|
|
|
include_str!("../../../cosmwasm/packages/quartz-tee-ra/data/qe_identity.json");
|
|
|
|
sgx_collateral.qe_identity = QE_IDENTITY_JSON.as_ptr() as _;
|
|
|
|
sgx_collateral.qe_identity_size = QE_IDENTITY_JSON.len() as u32;
|
|
|
|
|
|
|
|
Collateral::try_from(&sgx_collateral).expect("Failed to parse collateral")
|
|
|
|
}
|
|
|
|
|
|
|
|
let quote = self.quote(user_data)?;
|
|
|
|
|
|
|
|
let collateral = {
|
|
|
|
let (pck_crl, pck_crl_issuer_chain) =
|
|
|
|
pccs_query_pck().map_err(|e| IoError::new(ErrorKind::Other, e.to_string()))?;
|
|
|
|
collateral(&self.fmspc.to_string(), pck_crl, pck_crl_issuer_chain)
|
|
|
|
};
|
|
|
|
|
|
|
|
Ok(DcapAttestation::new(
|
|
|
|
quote
|
|
|
|
.try_into()
|
|
|
|
.map_err(|e: Quote3Error| IoError::other(e.to_string()))?,
|
|
|
|
collateral,
|
|
|
|
))
|
|
|
|
}
|
2024-08-02 16:31:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A mock `Attestor` that creates a quote consisting of just the user report data. (only meant for
|
|
|
|
/// testing purposes)
|
2024-07-18 23:34:31 +00:00
|
|
|
#[derive(Clone, PartialEq, Debug, Default)]
|
2024-02-27 19:52:23 +00:00
|
|
|
pub struct MockAttestor;
|
|
|
|
|
|
|
|
impl Attestor for MockAttestor {
|
|
|
|
type Error = String;
|
2024-09-25 19:14:06 +00:00
|
|
|
type Attestation = MockAttestation;
|
|
|
|
type RawAttestation = RawMockAttestation;
|
2024-02-27 19:52:23 +00:00
|
|
|
|
2024-07-18 23:34:31 +00:00
|
|
|
fn quote(&self, user_data: impl HasUserData) -> Result<Vec<u8>, Self::Error> {
|
|
|
|
let user_data = user_data.user_data();
|
|
|
|
Ok(user_data.to_vec())
|
2024-02-27 19:52:23 +00:00
|
|
|
}
|
2024-02-27 22:30:36 +00:00
|
|
|
|
|
|
|
fn mr_enclave(&self) -> Result<MrEnclave, Self::Error> {
|
2024-07-18 23:34:31 +00:00
|
|
|
Ok(Default::default())
|
2024-02-27 22:30:36 +00:00
|
|
|
}
|
2024-09-25 19:14:06 +00:00
|
|
|
|
|
|
|
fn attestation(&self, user_data: impl HasUserData) -> Result<Self::Attestation, Self::Error> {
|
|
|
|
Ok(MockAttestation(user_data.user_data()))
|
|
|
|
}
|
2024-02-27 22:30:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct NullUserData;
|
|
|
|
|
|
|
|
impl HasUserData for NullUserData {
|
|
|
|
fn user_data(&self) -> UserData {
|
|
|
|
[0u8; 64]
|
|
|
|
}
|
2024-02-27 19:52:23 +00:00
|
|
|
}
|