From eb90043172cd4bc4ae9dd720986f78bd875c70a5 Mon Sep 17 00:00:00 2001 From: Shoaib Ahmed Date: Fri, 2 Aug 2024 18:31:01 +0200 Subject: [PATCH] feat(dcap): Impl DCAP RA types/handlers (#139) --- core/quartz/src/attestor.rs | 27 ++++++ .../quartz-cw/src/handler/execute/attested.rs | 31 ++++++- .../quartz-cw/src/handler/instantiate.rs | 13 ++- .../quartz-cw/src/msg/execute/attested.rs | 87 ++++++++++++++++++- .../packages/quartz-tee-ra/src/intel_sgx.rs | 2 + .../quartz-tee-ra/src/intel_sgx/dcap.rs | 17 ++-- .../intel_sgx/dcap/mc_attest_verifier/dcap.rs | 49 ++--------- .../dcap/mc_attest_verifier_types.rs | 4 - .../mc_attest_verifier_types/verification.rs | 60 ------------- cosmwasm/packages/quartz-tee-ra/src/lib.rs | 1 + 10 files changed, 162 insertions(+), 129 deletions(-) delete mode 100644 cosmwasm/packages/quartz-tee-ra/src/intel_sgx/dcap/mc_attest_verifier_types.rs delete mode 100644 cosmwasm/packages/quartz-tee-ra/src/intel_sgx/dcap/mc_attest_verifier_types/verification.rs diff --git a/core/quartz/src/attestor.rs b/core/quartz/src/attestor.rs index 934158a..2c74c81 100644 --- a/core/quartz/src/attestor.rs +++ b/core/quartz/src/attestor.rs @@ -14,6 +14,7 @@ pub type DefaultAttestor = EpidAttestor; #[cfg(feature = "mock-sgx")] pub type DefaultAttestor = MockAttestor; +/// The trait defines the interface for generating attestations from within an enclave. pub trait Attestor { type Error: ToString; @@ -22,6 +23,7 @@ pub trait Attestor { fn mr_enclave(&self) -> Result; } +/// An `Attestor` for generating EPID attestations for Gramine based enclaves. #[derive(Clone, PartialEq, Debug, Default)] pub struct EpidAttestor; @@ -44,6 +46,31 @@ impl Attestor for EpidAttestor { } } +/// An `Attestor` for generating DCAP attestations for Gramine based enclaves. +#[derive(Clone, PartialEq, Debug, Default)] +pub struct DcapAttestor; + +impl Attestor for DcapAttestor { + type Error = IoError; + + fn quote(&self, user_data: impl HasUserData) -> Result, 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 { + let quote = self.quote(NullUserData)?; + Ok(quote[112..(112 + 32)] + .try_into() + .expect("hardcoded array size")) + } +} + +/// A mock `Attestor` that creates a quote consisting of just the user report data. (only meant for +/// testing purposes) #[derive(Clone, PartialEq, Debug, Default)] pub struct MockAttestor; diff --git a/cosmwasm/packages/quartz-cw/src/handler/execute/attested.rs b/cosmwasm/packages/quartz-cw/src/handler/execute/attested.rs index db6ee07..101e125 100644 --- a/cosmwasm/packages/quartz-cw/src/handler/execute/attested.rs +++ b/cosmwasm/packages/quartz-cw/src/handler/execute/attested.rs @@ -1,12 +1,15 @@ use cosmwasm_std::{DepsMut, Env, MessageInfo, Response}; -use quartz_tee_ra::{verify_epid_attestation, Error as RaVerificationError}; +use quartz_tee_ra::{ + intel_sgx::dcap::TrustedMrEnclaveIdentity, verify_dcap_attestation, verify_epid_attestation, + Error as RaVerificationError, +}; use crate::{ error::Error, handler::Handler, msg::execute::attested::{ - Attestation, Attested, AttestedMsgSansHandler, EpidAttestation, HasUserData, - MockAttestation, + Attestation, Attested, AttestedMsgSansHandler, DcapAttestation, EpidAttestation, + HasUserData, MockAttestation, }, state::CONFIG, }; @@ -29,6 +32,28 @@ impl Handler for EpidAttestation { } } +impl Handler for DcapAttestation { + fn handle( + self, + _deps: DepsMut<'_>, + _env: &Env, + _info: &MessageInfo, + ) -> Result { + let (quote, collateral) = self.clone().into_tuple(); + let mr_enclave = TrustedMrEnclaveIdentity::new(self.mr_enclave().into(), [""; 0], [""; 0]); + + // attestation handler MUST verify that the user_data and mr_enclave match the config/msg + let verification_output = verify_dcap_attestation(quote, collateral, &[mr_enclave.into()]); + if verification_output.is_success().into() { + Ok(Response::default()) + } else { + Err(Error::RaVerification(RaVerificationError::Dcap( + verification_output, + ))) + } + } +} + impl Handler for MockAttestation { fn handle( self, diff --git a/cosmwasm/packages/quartz-cw/src/handler/instantiate.rs b/cosmwasm/packages/quartz-cw/src/handler/instantiate.rs index 0bf1275..34f1bf6 100644 --- a/cosmwasm/packages/quartz-cw/src/handler/instantiate.rs +++ b/cosmwasm/packages/quartz-cw/src/handler/instantiate.rs @@ -5,13 +5,16 @@ use crate::{ error::Error, handler::Handler, msg::{ - execute::attested::{Attestation, EpidAttestation, MockAttestation}, + execute::attested::{Attestation, HasUserData}, instantiate::{CoreInstantiate, Instantiate}, }, state::{RawConfig, CONFIG, EPOCH_COUNTER}, }; -impl Handler for Instantiate { +impl Handler for Instantiate +where + A: Attestation + Handler + HasUserData, +{ fn handle(self, deps: DepsMut<'_>, env: &Env, info: &MessageInfo) -> Result { if self.0.msg().config().mr_enclave() != self.0.attestation().mr_enclave() { return Err(RaVerificationError::MrEnclaveMismatch.into()); @@ -20,12 +23,6 @@ impl Handler for Instantiate { } } -impl Handler for Instantiate { - fn handle(self, deps: DepsMut<'_>, env: &Env, info: &MessageInfo) -> Result { - self.0.handle(deps, env, info) - } -} - impl Handler for CoreInstantiate { fn handle(self, deps: DepsMut<'_>, _env: &Env, _info: &MessageInfo) -> Result { CONFIG diff --git a/cosmwasm/packages/quartz-cw/src/msg/execute/attested.rs b/cosmwasm/packages/quartz-cw/src/msg/execute/attested.rs index 35b84f2..e77a0d9 100644 --- a/cosmwasm/packages/quartz-cw/src/msg/execute/attested.rs +++ b/cosmwasm/packages/quartz-cw/src/msg/execute/attested.rs @@ -2,9 +2,16 @@ use std::{convert::Into, default::Default}; use cosmwasm_schema::cw_serde; use cosmwasm_std::{HexBinary, StdError}; -use quartz_tee_ra::IASReport; +use quartz_tee_ra::{ + intel_sgx::dcap::{Collateral, Quote3, Quote3Error}, + IASReport, +}; use serde::Serialize; +/// Alias for an owned DCAP quote. This is the main part of a DCAP attestation generated by an +/// enclave that we want to verify on-chain. +pub type Quote = Quote3>; + #[cfg(not(feature = "mock-sgx"))] pub type DefaultAttestation = EpidAttestation; #[cfg(not(feature = "mock-sgx"))] @@ -20,6 +27,7 @@ use crate::{ state::{MrEnclave, UserData}, }; +/// A wrapper struct for holding a message and it's attestation. #[derive(Clone, Debug, PartialEq)] pub struct Attested { pub msg: M, @@ -87,10 +95,12 @@ where type DomainType = Attested; } +/// A trait that defines how to extract user data from a given type. pub trait HasUserData { fn user_data(&self) -> UserData; } +/// A verifiable EPID attestation report generated by an enclave. #[derive(Clone, Debug, PartialEq, Serialize)] pub struct EpidAttestation { report: IASReport, @@ -149,6 +159,81 @@ impl Attestation for EpidAttestation { } } +/// A verifiable DCAP attestation generated by an enclave. +#[derive(Clone, Debug, PartialEq, Serialize)] +pub struct DcapAttestation { + quote: Quote, + collateral: Collateral, +} + +impl DcapAttestation { + pub fn new(quote: Quote, collateral: Collateral) -> Self { + Self { quote, collateral } + } + + pub fn into_tuple(self) -> (Quote, Collateral) { + (self.quote, self.collateral) + } +} + +#[cw_serde] +pub struct RawDcapAttestation { + quote: HexBinary, + collateral: serde_json::Value, +} + +impl TryFrom for DcapAttestation { + type Error = StdError; + + fn try_from(value: RawDcapAttestation) -> Result { + let quote_bytes: Vec = value.quote.into(); + let quote = quote_bytes + .try_into() + .map_err(|e: Quote3Error| StdError::parse_err("Quote", e.to_string()))?; + let collateral = serde_json::from_value(value.collateral) + .map_err(|e| StdError::parse_err("Collateral", e.to_string()))?; + + Ok(Self { quote, collateral }) + } +} + +impl From for RawDcapAttestation { + fn from(value: DcapAttestation) -> Self { + Self { + quote: value.quote.as_ref().to_vec().into(), + collateral: serde_json::to_vec(&value.collateral) + .expect("infallible serializer") + .into(), + } + } +} + +impl HasDomainType for RawDcapAttestation { + type DomainType = DcapAttestation; +} + +impl HasUserData for DcapAttestation { + fn user_data(&self) -> UserData { + let report_data = self.quote.app_report_body().report_data(); + let report_data_slice: &[u8] = report_data.as_ref(); + report_data_slice + .to_owned() + .try_into() + .expect("fixed size array") + } +} + +impl Attestation for DcapAttestation { + fn mr_enclave(&self) -> MrEnclave { + let mr_enclave = self.quote.app_report_body().mr_enclave(); + let mr_enclave_slice: &[u8] = mr_enclave.as_ref(); + mr_enclave_slice + .to_owned() + .try_into() + .expect("fixed size array") + } +} + #[derive(Clone, Debug, PartialEq)] pub struct MockAttestation(pub UserData); diff --git a/cosmwasm/packages/quartz-tee-ra/src/intel_sgx.rs b/cosmwasm/packages/quartz-tee-ra/src/intel_sgx.rs index 3e7bd02..459198c 100644 --- a/cosmwasm/packages/quartz-tee-ra/src/intel_sgx.rs +++ b/cosmwasm/packages/quartz-tee-ra/src/intel_sgx.rs @@ -11,4 +11,6 @@ pub enum Error { MrEnclaveMismatch, #[error("EPID specific error: {0}")] Epid(#[from] epid::Error), + #[error("DCAP specific error: {0:?}")] + Dcap(dcap::VerificationOutput), } diff --git a/cosmwasm/packages/quartz-tee-ra/src/intel_sgx/dcap.rs b/cosmwasm/packages/quartz-tee-ra/src/intel_sgx/dcap.rs index f5402e4..b78e623 100644 --- a/cosmwasm/packages/quartz-tee-ra/src/intel_sgx/dcap.rs +++ b/cosmwasm/packages/quartz-tee-ra/src/intel_sgx/dcap.rs @@ -1,26 +1,25 @@ pub mod certificate_chain; pub mod mc_attest_verifier; -pub mod mc_attest_verifier_types; /// Root anchor PEM file for use with DCAP pub const DCAP_ROOT_ANCHOR: &str = include_str!("../../data/DcapRootCACert.pem"); -use mc_attestation_verifier::*; -use mc_sgx_dcap_types::{Collateral, Quote3}; - -use self::{ - mc_attest_verifier::dcap::{DcapVerifier, DcapVerifierOutput}, - mc_attest_verifier_types::verification::EnclaveReportDataContents, +use mc_attestation_verifier::Evidence; +pub use mc_attestation_verifier::{ + TrustedIdentity, TrustedMrEnclaveIdentity, TrustedMrSignerIdentity, VerificationOutput, }; +pub use mc_sgx_dcap_types::{Collateral, Quote3, Quote3Error}; + +use self::mc_attest_verifier::dcap::DcapVerifier; +pub use self::mc_attest_verifier::dcap::DcapVerifierOutput; pub fn verify( quote: Quote3>, collateral: Collateral, identities: &[TrustedIdentity], ) -> VerificationOutput { - let report_data_contents = EnclaveReportDataContents::new([0x42u8; 16].into(), [0xAAu8; 32]); let evidence = Evidence::new(quote, collateral).expect("Failed to get evidence"); - let verifier = DcapVerifier::new(identities, None, report_data_contents); + let verifier = DcapVerifier::new(identities, None); verifier.verify(&evidence) } diff --git a/cosmwasm/packages/quartz-tee-ra/src/intel_sgx/dcap/mc_attest_verifier/dcap.rs b/cosmwasm/packages/quartz-tee-ra/src/intel_sgx/dcap/mc_attest_verifier/dcap.rs index ec37d41..237ba85 100644 --- a/cosmwasm/packages/quartz-tee-ra/src/intel_sgx/dcap/mc_attest_verifier/dcap.rs +++ b/cosmwasm/packages/quartz-tee-ra/src/intel_sgx/dcap/mc_attest_verifier/dcap.rs @@ -4,21 +4,17 @@ use der::DateTime; use mc_attestation_verifier::{ - Accessor, And, AndOutput, Evidence, EvidenceValue, EvidenceVerifier, ReportDataVerifier, - TrustedIdentity, VerificationOutput, Verifier, + Evidence, EvidenceValue, EvidenceVerifier, TrustedIdentity, VerificationOutput, Verifier, }; -use mc_sgx_core_types::ReportData; -// use super::DCAP_ROOT_ANCHOR; use super::super::certificate_chain::TlsCertificateChainVerifier; -use super::super::mc_attest_verifier_types::verification::EnclaveReportDataContents; #[derive(Debug)] pub struct DcapVerifier { - verifier: And, ReportDataHashVerifier>, + verifier: EvidenceVerifier, } -pub type DcapVerifierOutput = AndOutput; +pub type DcapVerifierOutput = EvidenceValue; impl DcapVerifier { /// Create a new instance of the DcapVerifier. @@ -30,20 +26,13 @@ impl DcapVerifier { /// and collateral. If time is provided, verification will fail if this /// time is before or after any of the validity periods. Otherwise, time /// validation of certificates will be skipped. - pub fn new( - trusted_identities: I, - time: impl Into>, - report_data: EnclaveReportDataContents, - ) -> Self + pub fn new(trusted_identities: I, time: impl Into>) -> Self where I: IntoIterator, ID: Into, { let certificate_verifier = TlsCertificateChainVerifier; - let verifier = And::new( - EvidenceVerifier::new(certificate_verifier, trusted_identities, time), - ReportDataHashVerifier::new(report_data), - ); + let verifier = EvidenceVerifier::new(certificate_verifier, trusted_identities, time); Self { verifier } } @@ -52,31 +41,3 @@ impl DcapVerifier { self.verifier.verify(evidence) } } - -#[derive(Debug, Clone)] -pub struct ReportDataHashVerifier { - report_data_verifier: ReportDataVerifier, -} - -impl ReportDataHashVerifier { - pub fn new(report_data: EnclaveReportDataContents) -> Self { - let mut expected_report_data_bytes = [0u8; 64]; - expected_report_data_bytes[..32].copy_from_slice(report_data.sha256().as_ref()); - let mut mask = [0u8; 64]; - mask[..32].copy_from_slice([0xffu8; 32].as_ref()); - let report_data_verifier = - ReportDataVerifier::new(expected_report_data_bytes.into(), mask.into()); - - Self { - report_data_verifier, - } - } -} - -impl> Verifier for ReportDataHashVerifier { - type Value = ReportData; - - fn verify(&self, evidence: &E) -> VerificationOutput { - self.report_data_verifier.verify(evidence) - } -} diff --git a/cosmwasm/packages/quartz-tee-ra/src/intel_sgx/dcap/mc_attest_verifier_types.rs b/cosmwasm/packages/quartz-tee-ra/src/intel_sgx/dcap/mc_attest_verifier_types.rs deleted file mode 100644 index a40c715..0000000 --- a/cosmwasm/packages/quartz-tee-ra/src/intel_sgx/dcap/mc_attest_verifier_types.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Heavily trimmed down version of the `mc-attest-verifier-types` crate -// https://github.com/hu55a1n1/mobilecoin/blob/89d90c427bf9cf637a124c0afad266d52b573dc8/attest/verifier/types/src/verification.rs - -pub mod verification; diff --git a/cosmwasm/packages/quartz-tee-ra/src/intel_sgx/dcap/mc_attest_verifier_types/verification.rs b/cosmwasm/packages/quartz-tee-ra/src/intel_sgx/dcap/mc_attest_verifier_types/verification.rs deleted file mode 100644 index 266001c..0000000 --- a/cosmwasm/packages/quartz-tee-ra/src/intel_sgx/dcap/mc_attest_verifier_types/verification.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright (c) 2018-2022 The MobileCoin Foundation - -//! Attestation Verification Report type. - -use core::fmt::Debug; - -use mc_sgx_core_types::QuoteNonce; -use serde::{Deserialize, Serialize}; -use sha2::{Digest, Sha256}; - -/// Structure for holding the contents of the Enclave's Report Data. -/// The Enclave Quote's ReportData member contains a SHA256 hash of this -/// structure's contents. -#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq)] -pub struct EnclaveReportDataContents { - nonce: QuoteNonce, - custom_identity: Option<[u8; 32]>, -} - -impl EnclaveReportDataContents { - /// Create a new EnclaveReportDataContents. - /// - /// # Arguments - /// * `nonce` - The nonce provided from the enclave when generating the - /// Report. - /// * `key` - The public key of the enclave. Previously this was bytes 0..32 - /// of the enclave's [`ReportData`](mc-sgx-core-types::ReportData). - /// * `custom_identity` - The custom identity of the enclave. Previously - /// this was bytes 32..64 of the enclave's - /// [`ReportData`](mc-sgx-core-types::ReportData). - pub fn new(nonce: QuoteNonce, custom_identity: impl Into>) -> Self { - Self { - nonce, - custom_identity: custom_identity.into(), - } - } - - /// Get the nonce - pub fn nonce(&self) -> &QuoteNonce { - &self.nonce - } - - /// Get the custom identity - pub fn custom_identity(&self) -> Option<&[u8; 32]> { - self.custom_identity.as_ref() - } - - /// Returns a SHA256 hash of the contents of this structure. - /// - /// This is the value that is stored in bytes 0..32 of the enclave's - /// [`ReportData`](mc-sgx-core-types::ReportData). - pub fn sha256(&self) -> [u8; 32] { - let mut hasher = Sha256::new(); - hasher.update(&self.nonce); - if let Some(custom_identity) = &self.custom_identity { - hasher.update(custom_identity); - } - hasher.finalize().into() - } -} diff --git a/cosmwasm/packages/quartz-tee-ra/src/lib.rs b/cosmwasm/packages/quartz-tee-ra/src/lib.rs index 51952e0..f8025ae 100644 --- a/cosmwasm/packages/quartz-tee-ra/src/lib.rs +++ b/cosmwasm/packages/quartz-tee-ra/src/lib.rs @@ -19,6 +19,7 @@ pub mod intel_sgx; pub use intel_sgx::{ + dcap::verify as verify_dcap_attestation, epid::{types::IASReport, verifier::verify as verify_epid_attestation}, Error, };