Quartz base impl (#10)

This commit is contained in:
Shoaib Ahmed 2024-02-19 21:25:46 +05:30 committed by GitHub
parent 3ffa3fac5e
commit b4e5f01cd1
26 changed files with 1183 additions and 0 deletions

View file

@ -0,0 +1,5 @@
[alias]
wasm = "build --release --lib --target wasm32-unknown-unknown"
wasm-debug = "build --lib --target wasm32-unknown-unknown"
schema = "run --bin schema"

View file

@ -0,0 +1,19 @@
[package]
name = "quartz-cw"
version = "0.1.0"
authors = ["hu55a1n1 <sufialhussaini@gmail.com>"]
edition = "2021"
[dependencies]
cw-storage-plus = "1.1.0"
cosmwasm-schema = "1.4.0"
cosmwasm-std = "1.4.0"
k256 = { version = "0.13.2", default-features = false, features = ["ecdsa", "alloc"] }
serde = { version = "1.0.188", default-features = false, features = ["derive"] }
sha2 = "0.10.8"
thiserror = "1.0.57"
quartz-tee-ra = { path = "../../packages/quartz-tee-ra" }
[dev-dependencies]
serde_json = "1.0.113"

View file

@ -0,0 +1 @@
# Quartz CosmWasm spec

View file

@ -0,0 +1,22 @@
use cosmwasm_std::StdError;
use k256::ecdsa::Error as K256Error;
use quartz_tee_ra::Error as RaVerificationError;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("{0}")]
Std(#[from] StdError),
#[error("{0}")]
RaVerification(#[from] RaVerificationError),
#[error("Not Secp256K1")]
K256(K256Error),
#[error("invalid session nonce or attempt to reset pub_key")]
BadSessionTransition,
}
impl From<K256Error> for Error {
fn from(e: K256Error) -> Self {
Self::K256(e)
}
}

View file

@ -0,0 +1,208 @@
pub mod execute;
pub mod instantiate;
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
use crate::error::Error;
use crate::msg::HasDomainType;
pub trait Handler {
fn handle(self, deps: DepsMut<'_>, env: &Env, info: &MessageInfo) -> Result<Response, Error>;
}
pub trait RawHandler: HasDomainType {
fn handle_raw(
self,
deps: DepsMut<'_>,
env: &Env,
info: &MessageInfo,
) -> Result<Response, Error>;
}
impl<RM> RawHandler for RM
where
RM: HasDomainType,
RM::DomainType: Handler,
{
fn handle_raw(
self,
deps: DepsMut<'_>,
env: &Env,
info: &MessageInfo,
) -> Result<Response, Error> {
let execute: RM::DomainType = self.try_into()?;
execute.handle(deps, env, info)
}
}
#[cfg(test)]
mod tests {
use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
use cosmwasm_std::DepsMut;
use serde::Deserialize;
use crate::handler::Handler;
use crate::msg::{HasDomainType, RawExecuteMsg, RawInstantiateMsg};
use crate::state::SESSION;
fn parse_msg<'a, R>(msg_str: &'a str) -> R::DomainType
where
R: HasDomainType + Deserialize<'a>,
{
let raw_msg: R =
serde_json::from_str(msg_str).expect("deserialization failure for hard-coded RawMsg");
raw_msg.try_into().expect("invalid hard-coded RawMsg")
}
fn handle_msg<'a, R>(mut deps: DepsMut<'_>, msg_str: &'a str)
where
R: HasDomainType + Deserialize<'a>,
R::DomainType: Handler,
{
let msg = parse_msg::<R>(msg_str);
let info = mock_info("creator", &[]);
let env = mock_env();
let res = msg
.handle(deps.branch(), &env, &info)
.expect("msg handler failure");
assert_eq!(0, res.messages.len());
}
fn instantiate(deps: DepsMut<'_>) {
handle_msg::<RawInstantiateMsg>(
deps,
r#"{
"msg": {
"mr_enclave": "e3c2f2a5b840d89e069acaffcadb6510ef866a73d3a9ee57100ed5f8646ee4bb"
},
"attestation": {
"report": {
"report": {
"id": "5246688123689513540899231107533660789",
"timestamp": "2024-02-07T17:06:23.913745",
"version": 4,
"epidPseudonym": "+CUyIi74LPqS6M0NF7YrSxLqPdX3MKs6D6LIPqRG/ZEB4WmxZVvxAJwdwg/0m9cYnUUQguLnJotthX645lAogfJgO8Xg5/91lSegwyUKvHmKgtjOHX/YTbVe/wmgWiBdaL+KmarY0Je459Px/FqGLWLsAF7egPAJRd1Xn88Znrs=",
"advisoryURL": "https://security-center.intel.com",
"advisoryIDs": [
"INTEL-SA-00161",
"INTEL-SA-00219",
"INTEL-SA-00289",
"INTEL-SA-00334",
"INTEL-SA-00615"
],
"isvEnclaveQuoteStatus": "CONFIGURATION_AND_SW_HARDENING_NEEDED",
"platformInfoBlob": "150200650000080000141402040180070000000000000000000D00000C000000020000000000000CB0F08115F3DE71AE97980FE5E10B042054930ACE356C79EC44603D3F890756EC6ED73927A7C58CDE9AF1E754AEC77E335E8D80294407936BEB6404F27669FF7BB1",
"isvEnclaveQuoteBody": "AgABALAMAAAPAA8AAAAAAFHK9aSLRQ1iSu/jKG0xSJQAAAAAAAAAAAAAAAAAAAAAFBQCBwGAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAHAAAAAAAAAOPC8qW4QNieBprK/8rbZRDvhmpz06nuVxAO1fhkbuS7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc8uUpEUEPvz8ZkFapjVh5WlWaLoAJM/f80T0EhGInHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACRE7C+d+1dDWhoDsdyBrjVh+1AZ5txMhzN1UBeTVSmggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
},
"reportsig": "YcY4SPvkfR4P2E8A5huutCeS+vY/ir+xq6disalNfNtAcUyOIOqTPVXhAZgY1M5B47Hjj1oYWf2qC2w+dnj7VcZjzO9oR0pJYdA+A7jaVrNzH2eXA79yICkuU8WE/x58I0j5vjXLoHXahaKlpZkMeTphqBY8u+FTVSdP3cWPho4viPapTfQRuEWmYq4KIq2zSr6wLg3Pz+yQ+G3e9BASVkLYxdYGTDFH1pMmfas9SEI7V4I+j8DaXmL8bucSRakmcQdmDMPGiA7mvIhSAlprzCrdxM7CHeUC6MPLN1fmFFcc9kyO/ved69j/651MWC83GgxSJ15L80U+DQzmrSW8xg=="
},
"mr_enclave": "e3c2f2a5b840d89e069acaffcadb6510ef866a73d3a9ee57100ed5f8646ee4bb",
"user_data": "9113b0be77ed5d0d68680ec77206b8d587ed40679b71321ccdd5405e4d54a6820000000000000000000000000000000000000000000000000000000000000000"
}
}"#,
);
}
fn session_create(deps: DepsMut<'_>) {
handle_msg::<RawExecuteMsg>(
deps,
r#"{
"session_create": {
"msg": {
"nonce": "425d87f8620e1dedeee70590cc55b164b8f01480ee59e0b1da35436a2f7c2777"
},
"attestation": {
"report": {
"report": {
"id": "5246688123689513540899231107533660789",
"timestamp": "2024-02-07T17:06:23.913745",
"version": 4,
"epidPseudonym": "+CUyIi74LPqS6M0NF7YrSxLqPdX3MKs6D6LIPqRG/ZEB4WmxZVvxAJwdwg/0m9cYnUUQguLnJotthX645lAogfJgO8Xg5/91lSegwyUKvHmKgtjOHX/YTbVe/wmgWiBdaL+KmarY0Je459Px/FqGLWLsAF7egPAJRd1Xn88Znrs=",
"advisoryURL": "https://security-center.intel.com",
"advisoryIDs": [
"INTEL-SA-00161",
"INTEL-SA-00219",
"INTEL-SA-00289",
"INTEL-SA-00334",
"INTEL-SA-00615"
],
"isvEnclaveQuoteStatus": "CONFIGURATION_AND_SW_HARDENING_NEEDED",
"platformInfoBlob": "150200650000080000141402040180070000000000000000000D00000C000000020000000000000CB0F08115F3DE71AE97980FE5E10B042054930ACE356C79EC44603D3F890756EC6ED73927A7C58CDE9AF1E754AEC77E335E8D80294407936BEB6404F27669FF7BB1",
"isvEnclaveQuoteBody": "AgABALAMAAAPAA8AAAAAAFHK9aSLRQ1iSu/jKG0xSJQAAAAAAAAAAAAAAAAAAAAAFBQCBwGAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAHAAAAAAAAAOPC8qW4QNieBprK/8rbZRDvhmpz06nuVxAO1fhkbuS7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc8uUpEUEPvz8ZkFapjVh5WlWaLoAJM/f80T0EhGInHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACRE7C+d+1dDWhoDsdyBrjVh+1AZ5txMhzN1UBeTVSmggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
},
"reportsig": "YcY4SPvkfR4P2E8A5huutCeS+vY/ir+xq6disalNfNtAcUyOIOqTPVXhAZgY1M5B47Hjj1oYWf2qC2w+dnj7VcZjzO9oR0pJYdA+A7jaVrNzH2eXA79yICkuU8WE/x58I0j5vjXLoHXahaKlpZkMeTphqBY8u+FTVSdP3cWPho4viPapTfQRuEWmYq4KIq2zSr6wLg3Pz+yQ+G3e9BASVkLYxdYGTDFH1pMmfas9SEI7V4I+j8DaXmL8bucSRakmcQdmDMPGiA7mvIhSAlprzCrdxM7CHeUC6MPLN1fmFFcc9kyO/ved69j/651MWC83GgxSJ15L80U+DQzmrSW8xg=="
},
"mr_enclave": "e3c2f2a5b840d89e069acaffcadb6510ef866a73d3a9ee57100ed5f8646ee4bb",
"user_data": "425d87f8620e1dedeee70590cc55b164b8f01480ee59e0b1da35436a2f7c27770000000000000000000000000000000000000000000000000000000000000000"
}
}
}"#,
);
}
fn session_set_pub_key(deps: DepsMut<'_>) {
handle_msg::<RawExecuteMsg>(
deps,
r#"{
"session_set_pub_key": {
"msg": {
"nonce": "425d87f8620e1dedeee70590cc55b164b8f01480ee59e0b1da35436a2f7c2777"
"pub_key": "03E67EF09213633074FB4FBF338643F4F0C574ED60EF11D03422EEB06FA38C8F3F"
},
"attestation": {
"report": {
"report": {
"id": "5246688123689513540899231107533660789",
"timestamp": "2024-02-07T17:06:23.913745",
"version": 4,
"epidPseudonym": "+CUyIi74LPqS6M0NF7YrSxLqPdX3MKs6D6LIPqRG/ZEB4WmxZVvxAJwdwg/0m9cYnUUQguLnJotthX645lAogfJgO8Xg5/91lSegwyUKvHmKgtjOHX/YTbVe/wmgWiBdaL+KmarY0Je459Px/FqGLWLsAF7egPAJRd1Xn88Znrs=",
"advisoryURL": "https://security-center.intel.com",
"advisoryIDs": [
"INTEL-SA-00161",
"INTEL-SA-00219",
"INTEL-SA-00289",
"INTEL-SA-00334",
"INTEL-SA-00615"
],
"isvEnclaveQuoteStatus": "CONFIGURATION_AND_SW_HARDENING_NEEDED",
"platformInfoBlob": "150200650000080000141402040180070000000000000000000D00000C000000020000000000000CB0F08115F3DE71AE97980FE5E10B042054930ACE356C79EC44603D3F890756EC6ED73927A7C58CDE9AF1E754AEC77E335E8D80294407936BEB6404F27669FF7BB1",
"isvEnclaveQuoteBody": "AgABALAMAAAPAA8AAAAAAFHK9aSLRQ1iSu/jKG0xSJQAAAAAAAAAAAAAAAAAAAAAFBQCBwGAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAHAAAAAAAAAOPC8qW4QNieBprK/8rbZRDvhmpz06nuVxAO1fhkbuS7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc8uUpEUEPvz8ZkFapjVh5WlWaLoAJM/f80T0EhGInHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACRE7C+d+1dDWhoDsdyBrjVh+1AZ5txMhzN1UBeTVSmggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
},
"reportsig": "YcY4SPvkfR4P2E8A5huutCeS+vY/ir+xq6disalNfNtAcUyOIOqTPVXhAZgY1M5B47Hjj1oYWf2qC2w+dnj7VcZjzO9oR0pJYdA+A7jaVrNzH2eXA79yICkuU8WE/x58I0j5vjXLoHXahaKlpZkMeTphqBY8u+FTVSdP3cWPho4viPapTfQRuEWmYq4KIq2zSr6wLg3Pz+yQ+G3e9BASVkLYxdYGTDFH1pMmfas9SEI7V4I+j8DaXmL8bucSRakmcQdmDMPGiA7mvIhSAlprzCrdxM7CHeUC6MPLN1fmFFcc9kyO/ved69j/651MWC83GgxSJ15L80U+DQzmrSW8xg=="
},
"mr_enclave": "e3c2f2a5b840d89e069acaffcadb6510ef866a73d3a9ee57100ed5f8646ee4bb",
"user_data": "425d87f8620e1dedeee70590cc55b164b8f01480ee59e0b1da35436a2f7c27770000000000000000000000000000000000000000000000000000000000000000"
}
}
}"#,
);
}
#[test]
#[ignore]
fn test_instantiate_handler() {
let mut deps = mock_dependencies();
instantiate(deps.as_mut());
}
#[test]
#[ignore]
fn test_session_create_handler() {
let mut deps = mock_dependencies();
instantiate(deps.as_mut());
session_create(deps.as_mut());
SESSION.load(&deps.storage).expect("Session not created");
}
#[test]
#[ignore]
fn test_session_set_pub_key_handler() {
let mut deps = mock_dependencies();
instantiate(deps.as_mut());
session_create(deps.as_mut());
session_set_pub_key(deps.as_mut());
SESSION.load(&deps.storage).expect("Session not created");
// TODO(hu55a1n1): check that nonce & pub_key match, etc.
}
}

View file

@ -0,0 +1,18 @@
pub mod attested;
pub mod session_create;
pub mod session_set_pub_key;
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
use crate::error::Error;
use crate::handler::Handler;
use crate::msg::execute::Execute;
impl Handler for Execute {
fn handle(self, deps: DepsMut<'_>, env: &Env, info: &MessageInfo) -> Result<Response, Error> {
match self {
Execute::SessionCreate(msg) => msg.handle(deps, env, info),
Execute::SessionSetPubKey(msg) => msg.handle(deps, env, info),
}
}
}

View file

@ -0,0 +1,49 @@
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
use quartz_tee_ra::{verify_epid_attestation, Error as RaVerificationError};
use crate::error::Error;
use crate::handler::Handler;
use crate::msg::execute::attested::{Attestation, Attested, EpidAttestation, HasUserData};
use crate::state::CONFIG;
impl Handler for EpidAttestation {
fn handle(
self,
_deps: DepsMut<'_>,
_env: &Env,
_info: &MessageInfo,
) -> Result<Response, Error> {
let (report, mr_enclave, user_data) = self.into_tuple();
verify_epid_attestation(report, mr_enclave, user_data)
.map(|_| Response::default())
.map_err(Error::RaVerification)
}
}
impl<M, A> Handler for Attested<M, A>
where
M: Handler + HasUserData,
A: Handler + HasUserData + Attestation,
{
fn handle(
self,
mut deps: DepsMut<'_>,
env: &Env,
info: &MessageInfo,
) -> Result<Response, Error> {
let (msg, attestation) = self.into_tuple();
if msg.user_data() != attestation.user_data() {
return Err(RaVerificationError::UserDataMismatch.into());
}
if let Some(config) = CONFIG.may_load(deps.storage)? {
// if we weren't able to load then the context was from InstantiateMsg so we don't fail
if *config.mr_enclave() != attestation.mr_enclave() {
return Err(RaVerificationError::MrEnclaveMismatch.into());
}
}
Handler::handle(attestation, deps.branch(), env, info)?;
Handler::handle(msg, deps, env, info)
}
}

View file

@ -0,0 +1,17 @@
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
use crate::error::Error;
use crate::handler::Handler;
use crate::msg::execute::session_create::SessionCreate;
use crate::state::{Session, SESSION};
impl Handler for SessionCreate {
fn handle(self, deps: DepsMut<'_>, _env: &Env, _info: &MessageInfo) -> Result<Response, Error> {
// TODO(hu55a1n1): overwrite previous session?
SESSION
.save(deps.storage, &Session::create(self.into_nonce()))
.map_err(Error::Std)?;
Ok(Response::new().add_attribute("action", "session_create"))
}
}

View file

@ -0,0 +1,25 @@
use cosmwasm_std::{DepsMut, Env, HexBinary, MessageInfo, Response};
use crate::error::Error;
use crate::handler::Handler;
use crate::msg::execute::session_set_pub_key::SessionSetPubKey;
use crate::state::SESSION;
impl Handler for SessionSetPubKey {
fn handle(self, deps: DepsMut<'_>, _env: &Env, _info: &MessageInfo) -> Result<Response, Error> {
let session = SESSION.load(deps.storage).map_err(Error::Std)?;
let (nonce, pub_key) = self.into_tuple();
let session = session
.with_pub_key(nonce, pub_key)
.ok_or(Error::BadSessionTransition)?;
SESSION.save(deps.storage, &session).map_err(Error::Std)?;
Ok(Response::new()
.add_attribute("action", "session_set_pub_key")
.add_attribute(
"pub_key",
HexBinary::from(pub_key.to_sec1_bytes().into_vec()).to_hex(),
))
}
}

View file

@ -0,0 +1,27 @@
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
use quartz_tee_ra::Error as RaVerificationError;
use crate::error::Error;
use crate::handler::Handler;
use crate::msg::execute::attested::{Attestation, EpidAttestation};
use crate::msg::instantiate::{CoreInstantiate, Instantiate};
use crate::state::Config;
use crate::state::CONFIG;
impl Handler for Instantiate<EpidAttestation> {
fn handle(self, deps: DepsMut<'_>, env: &Env, info: &MessageInfo) -> Result<Response, Error> {
if self.0.msg().mr_enclave() != self.0.attestation().mr_enclave() {
return Err(RaVerificationError::MrEnclaveMismatch.into());
}
self.0.handle(deps, env, info)
}
}
impl Handler for CoreInstantiate {
fn handle(self, deps: DepsMut<'_>, _env: &Env, _info: &MessageInfo) -> Result<Response, Error> {
CONFIG
.save(deps.storage, &Config::new(self.mr_enclave()))
.map_err(Error::Std)?;
Ok(Response::new().add_attribute("action", "instantiate"))
}
}

View file

@ -0,0 +1,22 @@
#![warn(
clippy::checked_conversions,
clippy::panic,
clippy::panic_in_result_fn,
clippy::unwrap_used,
rust_2018_idioms,
unused_lifetimes
)]
#![deny(
trivial_casts,
trivial_numeric_casts,
unused_import_braces,
unused_qualifications,
warnings
)]
#![forbid(unsafe_code)]
pub mod error;
pub mod handler;
pub mod msg;
pub mod prelude;
pub mod state;

View file

@ -0,0 +1,12 @@
pub mod execute;
pub mod instantiate;
pub mod query;
pub use execute::{Execute as ExecuteMsg, RawExecute as RawExecuteMsg};
pub use instantiate::{Instantiate as InstantiateMsg, RawInstantiate as RawInstantiateMsg};
use cosmwasm_std::StdError;
pub trait HasDomainType: From<Self::DomainType> {
type DomainType: TryFrom<Self, Error = StdError>;
}

View file

@ -0,0 +1,63 @@
pub mod attested;
pub mod session_create;
pub mod session_set_pub_key;
use cosmwasm_schema::cw_serde;
use cosmwasm_std::StdError;
use crate::msg::execute::attested::{Attested, EpidAttestation};
use crate::msg::execute::attested::{RawAttested, RawEpidAttestation};
use crate::msg::execute::session_create::{RawSessionCreate, SessionCreate};
use crate::msg::execute::session_set_pub_key::{RawSessionSetPubKey, SessionSetPubKey};
use crate::msg::HasDomainType;
#[derive(Clone, Debug, PartialEq)]
pub enum Execute<Attestation = EpidAttestation> {
SessionCreate(Attested<SessionCreate, Attestation>),
SessionSetPubKey(Attested<SessionSetPubKey, Attestation>),
}
#[cw_serde]
pub enum RawExecute<RawAttestation = RawEpidAttestation> {
#[serde(rename = "session_create")]
RawSessionCreate(RawAttested<RawSessionCreate, RawAttestation>),
#[serde(rename = "session_set_pub_key")]
RawSessionSetPubKey(RawAttested<RawSessionSetPubKey, RawAttestation>),
}
impl<RA> TryFrom<RawExecute<RA>> for Execute<RA::DomainType>
where
RA: HasDomainType,
{
type Error = StdError;
fn try_from(value: RawExecute<RA>) -> Result<Self, Self::Error> {
match value {
RawExecute::RawSessionCreate(msg) => {
Ok(Execute::SessionCreate(TryFrom::try_from(msg)?))
}
RawExecute::RawSessionSetPubKey(msg) => {
Ok(Execute::SessionSetPubKey(TryFrom::try_from(msg)?))
}
}
}
}
impl<RA> From<Execute<RA::DomainType>> for RawExecute<RA>
where
RA: HasDomainType,
{
fn from(value: Execute<RA::DomainType>) -> Self {
match value {
Execute::SessionCreate(msg) => RawExecute::RawSessionCreate(From::from(msg)),
Execute::SessionSetPubKey(msg) => RawExecute::RawSessionSetPubKey(From::from(msg)),
}
}
}
impl<A> HasDomainType for RawExecute<A>
where
A: HasDomainType,
{
type DomainType = Execute<A::DomainType>;
}

View file

@ -0,0 +1,146 @@
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{HexBinary, StdError};
use quartz_tee_ra::IASReport;
use crate::msg::HasDomainType;
use crate::state::{MrEnclave, UserData};
#[derive(Clone, Debug, PartialEq)]
pub struct Attested<M, A> {
msg: M,
attestation: A,
}
impl<M, A> Attested<M, A> {
pub fn into_tuple(self) -> (M, A) {
let Attested { msg, attestation } = self;
(msg, attestation)
}
pub fn msg(&self) -> &M {
&self.msg
}
pub fn attestation(&self) -> &A {
&self.attestation
}
}
#[cw_serde]
pub struct RawAttested<RM, RA> {
pub msg: RM,
pub attestation: RA,
}
impl<RM, RA> TryFrom<RawAttested<RM, RA>> for Attested<RM::DomainType, RA::DomainType>
where
RM: HasDomainType,
RA: HasDomainType,
{
type Error = StdError;
fn try_from(value: RawAttested<RM, RA>) -> Result<Self, Self::Error> {
Ok(Self {
msg: value.msg.try_into()?,
attestation: value.attestation.try_into()?,
})
}
}
impl<RM, RA> From<Attested<RM::DomainType, RA::DomainType>> for RawAttested<RM, RA>
where
RM: HasDomainType,
RA: HasDomainType,
{
fn from(value: Attested<RM::DomainType, RA::DomainType>) -> Self {
Self {
msg: value.msg.into(),
attestation: value.attestation.into(),
}
}
}
impl<RM, RA> HasDomainType for RawAttested<RM, RA>
where
RM: HasDomainType,
RA: HasDomainType,
{
type DomainType = Attested<RM::DomainType, RA::DomainType>;
}
pub trait HasUserData {
fn user_data(&self) -> UserData;
}
#[derive(Clone, Debug, PartialEq)]
pub struct EpidAttestation {
report: IASReport,
mr_enclave: MrEnclave,
user_data: UserData,
}
impl EpidAttestation {
pub fn into_tuple(self) -> (IASReport, MrEnclave, UserData) {
let EpidAttestation {
report,
mr_enclave,
user_data,
} = self;
(report, mr_enclave, user_data)
}
pub fn report(&self) -> &IASReport {
&self.report
}
}
#[cw_serde]
pub struct RawEpidAttestation {
report: IASReport,
mr_enclave: HexBinary,
user_data: HexBinary,
}
impl TryFrom<RawEpidAttestation> for EpidAttestation {
type Error = StdError;
fn try_from(value: RawEpidAttestation) -> Result<Self, Self::Error> {
let mr_enclave = value.mr_enclave.to_array()?;
let user_data = value.user_data.to_array()?;
Ok(Self {
report: value.report,
mr_enclave,
user_data,
})
}
}
impl From<EpidAttestation> for RawEpidAttestation {
fn from(value: EpidAttestation) -> Self {
Self {
report: value.report,
mr_enclave: value.mr_enclave.into(),
user_data: value.user_data.into(),
}
}
}
impl HasDomainType for RawEpidAttestation {
type DomainType = EpidAttestation;
}
impl HasUserData for EpidAttestation {
fn user_data(&self) -> UserData {
self.user_data
}
}
pub trait Attestation {
fn mr_enclave(&self) -> MrEnclave;
}
impl Attestation for EpidAttestation {
fn mr_enclave(&self) -> MrEnclave {
self.report().report.isv_enclave_quote_body.mrenclave()
}
}

View file

@ -0,0 +1,51 @@
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{HexBinary, StdError};
use crate::msg::execute::attested::HasUserData;
use crate::msg::HasDomainType;
use crate::state::{Nonce, UserData};
#[derive(Clone, Debug, PartialEq)]
pub struct SessionCreate {
nonce: Nonce,
}
impl SessionCreate {
pub fn into_nonce(self) -> Nonce {
self.nonce
}
}
#[cw_serde]
pub struct RawSessionCreate {
nonce: HexBinary,
}
impl TryFrom<RawSessionCreate> for SessionCreate {
type Error = StdError;
fn try_from(value: RawSessionCreate) -> Result<Self, Self::Error> {
let nonce = value.nonce.to_array()?;
Ok(Self { nonce })
}
}
impl From<SessionCreate> for RawSessionCreate {
fn from(value: SessionCreate) -> Self {
Self {
nonce: value.nonce.into(),
}
}
}
impl HasDomainType for RawSessionCreate {
type DomainType = SessionCreate;
}
impl HasUserData for SessionCreate {
fn user_data(&self) -> UserData {
let mut user_data = [0u8; 64];
user_data[0..32].copy_from_slice(&self.nonce);
user_data
}
}

View file

@ -0,0 +1,65 @@
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{HexBinary, StdError};
use k256::ecdsa::VerifyingKey;
use sha2::{Digest, Sha256};
use crate::error::Error;
use crate::msg::execute::attested::HasUserData;
use crate::msg::HasDomainType;
use crate::state::{Nonce, UserData};
#[derive(Clone, Debug, PartialEq)]
pub struct SessionSetPubKey {
nonce: Nonce,
pub_key: VerifyingKey,
}
impl SessionSetPubKey {
pub fn into_tuple(self) -> (Nonce, VerifyingKey) {
(self.nonce, self.pub_key)
}
}
#[cw_serde]
pub struct RawSessionSetPubKey {
nonce: HexBinary,
pub_key: HexBinary,
}
impl TryFrom<RawSessionSetPubKey> for SessionSetPubKey {
type Error = StdError;
fn try_from(value: RawSessionSetPubKey) -> Result<Self, Self::Error> {
let nonce = value.nonce.to_array()?;
let pub_key = VerifyingKey::from_sec1_bytes(&value.pub_key)
.map_err(Error::from)
.map_err(|e| StdError::generic_err(e.to_string()))?;
Ok(Self { nonce, pub_key })
}
}
impl From<SessionSetPubKey> for RawSessionSetPubKey {
fn from(value: SessionSetPubKey) -> Self {
Self {
nonce: value.nonce.into(),
pub_key: value.pub_key.to_sec1_bytes().into_vec().into(),
}
}
}
impl HasDomainType for RawSessionSetPubKey {
type DomainType = SessionSetPubKey;
}
impl HasUserData for SessionSetPubKey {
fn user_data(&self) -> UserData {
let mut hasher = Sha256::new();
hasher.update(self.nonce);
hasher.update(self.pub_key.to_sec1_bytes());
let digest: [u8; 32] = hasher.finalize().into();
let mut user_data = [0u8; 64];
user_data[0..32].copy_from_slice(&digest);
user_data
}
}

View file

@ -0,0 +1,89 @@
use cosmwasm_schema::cw_serde;
use cosmwasm_std::{HexBinary, StdError};
use sha2::{Digest, Sha256};
use crate::msg::execute::attested::{
Attested, EpidAttestation, HasUserData, RawAttested, RawEpidAttestation,
};
use crate::msg::HasDomainType;
use crate::state::{MrEnclave, UserData};
#[derive(Clone, Debug, PartialEq)]
pub struct Instantiate<A = EpidAttestation>(pub(crate) Attested<CoreInstantiate, A>);
#[cw_serde]
pub struct RawInstantiate<RA = RawEpidAttestation>(RawAttested<RawCoreInstantiate, RA>);
impl<RA> TryFrom<RawInstantiate<RA>> for Instantiate<RA::DomainType>
where
RA: HasDomainType,
{
type Error = StdError;
fn try_from(value: RawInstantiate<RA>) -> Result<Self, Self::Error> {
Ok(Self(TryFrom::try_from(value.0)?))
}
}
impl<RA> From<Instantiate<RA::DomainType>> for RawInstantiate<RA>
where
RA: HasDomainType,
{
fn from(value: Instantiate<RA::DomainType>) -> Self {
Self(From::from(value.0))
}
}
impl HasDomainType for RawInstantiate {
type DomainType = Instantiate;
}
#[derive(Clone, Debug, PartialEq)]
pub struct CoreInstantiate {
mr_enclave: MrEnclave,
// TODO(hu55a1n1): config - e.g. Epoch duration, light client opts
}
impl CoreInstantiate {
pub fn mr_enclave(&self) -> MrEnclave {
self.mr_enclave
}
}
#[cw_serde]
pub struct RawCoreInstantiate {
mr_enclave: HexBinary,
}
impl TryFrom<RawCoreInstantiate> for CoreInstantiate {
type Error = StdError;
fn try_from(value: RawCoreInstantiate) -> Result<Self, Self::Error> {
let mr_enclave = value.mr_enclave.to_array()?;
Ok(Self { mr_enclave })
}
}
impl From<CoreInstantiate> for RawCoreInstantiate {
fn from(value: CoreInstantiate) -> Self {
Self {
mr_enclave: value.mr_enclave.into(),
}
}
}
impl HasDomainType for RawCoreInstantiate {
type DomainType = CoreInstantiate;
}
impl HasUserData for CoreInstantiate {
fn user_data(&self) -> UserData {
let mut hasher = Sha256::new();
hasher.update(self.mr_enclave);
let digest: [u8; 32] = hasher.finalize().into();
let mut user_data = [0u8; 64];
user_data[0..32].copy_from_slice(&digest);
user_data
}
}

View file

@ -0,0 +1 @@

View file

@ -0,0 +1,3 @@
pub use crate::handler::RawHandler;
pub use crate::msg::execute::RawExecute as QuartzExecuteMsg;
pub use crate::msg::instantiate::RawInstantiate as QuartzInstantiateMsg;

View file

@ -0,0 +1,52 @@
use cosmwasm_schema::cw_serde;
use cosmwasm_std::HexBinary;
use cw_storage_plus::Item;
use k256::ecdsa::VerifyingKey;
pub type MrEnclave = [u8; 32];
pub type Nonce = [u8; 32];
pub type UserData = [u8; 64];
#[cw_serde]
pub struct Config {
mr_enclave: HexBinary,
}
impl Config {
pub fn new(mr_enclave: MrEnclave) -> Self {
Self {
mr_enclave: mr_enclave.into(),
}
}
pub fn mr_enclave(&self) -> &HexBinary {
&self.mr_enclave
}
}
#[cw_serde]
pub struct Session {
nonce: HexBinary,
pub_key: Option<HexBinary>,
}
impl Session {
pub fn create(nonce: Nonce) -> Self {
Self {
nonce: nonce.into(),
pub_key: None,
}
}
pub fn with_pub_key(mut self, nonce: Nonce, pub_key: VerifyingKey) -> Option<Self> {
if self.nonce == nonce && self.pub_key.is_none() {
self.pub_key = Some(pub_key.to_sec1_bytes().into_vec().into());
Some(self)
} else {
None
}
}
}
pub const CONFIG: Item<'_, Config> = Item::new("quartz_config");
pub const SESSION: Item<'_, Session> = Item::new("quartz_session");

View file

@ -0,0 +1,15 @@
[package]
name = "quartz-tee-ra"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
cosmwasm-schema = "1.4.0"
cosmwasm-std = "1.4.0"
hex-literal = "0.4.1"
num-bigint = "0.4.4"
serde_json = "1.0.108"
sha2 = "0.10.8"
thiserror = { version = "1.0.49" }

View file

@ -0,0 +1,13 @@
use thiserror::Error;
pub mod epid;
#[derive(Error, Debug)]
pub enum Error {
#[error("Specified user data does not match the report")]
UserDataMismatch,
#[error("Specified MRENCLAVE does not match the report")]
MrEnclaveMismatch,
#[error("EPID specific error: {0}")]
Epid(#[from] epid::Error),
}

View file

@ -0,0 +1,17 @@
use hex_literal::hex;
use thiserror::Error;
pub const INTEL_ROOT_MODULUS: &[u8] = &hex!("a97a2de0e66ea6147c9ee745ac0162686c7192099afc4b3f040fad6de093511d74e802f510d716038157dcaf84f4104bd3fed7e6b8f99c8817fd1ff5b9b864296c3d81fa8f1b729e02d21d72ffee4ced725efe74bea68fbc4d4244286fcdd4bf64406a439a15bcb4cf67754489c423972b4a80df5c2e7c5bc2dbaf2d42bb7b244f7c95bf92c75d3b33fc5410678a89589d1083da3acc459f2704cd99598c275e7c1878e00757e5bdb4e840226c11c0a17ff79c80b15c1ddb5af21cc2417061fbd2a2da819ed3b72b7efaa3bfebe2805c9b8ac19aa346512d484cfc81941e15f55881cc127e8f7aa12300cd5afb5742fa1d20cb467a5beb1c666cf76a368978b5");
pub const INTEL_ROOT_EXPONENT: &[u8] =
&hex!("0000000000000000000000000000000000000000000000000000000000010001");
pub mod types;
pub mod verifier;
#[derive(Error, Debug)]
pub enum Error {
#[error("Recovered digest from signature does not match the specified report")]
RecoveredDigestMismatch,
}

View file

@ -0,0 +1,49 @@
use cosmwasm_schema::cw_serde;
use cosmwasm_std::Binary;
#[cw_serde]
pub struct IASReport {
pub report: ReportBody,
#[serde(rename = "reportsig")]
pub report_sig: Binary,
}
#[cw_serde]
pub struct ReportBody {
pub id: String,
pub timestamp: String,
pub version: u64,
#[serde(rename = "epidPseudonym")]
pub epid_pseudonym: Binary,
#[serde(rename = "advisoryURL")]
pub advisory_url: String,
#[serde(rename = "advisoryIDs")]
pub advisory_ids: Vec<String>,
#[serde(rename = "isvEnclaveQuoteStatus")]
pub isv_enclave_quote_status: String,
#[serde(rename = "platformInfoBlob")]
pub platform_info_blob: String,
#[serde(rename = "isvEnclaveQuoteBody")]
pub isv_enclave_quote_body: IsvEnclaveQuoteBody,
}
#[cw_serde]
#[serde(transparent)]
pub struct IsvEnclaveQuoteBody(Binary);
impl IsvEnclaveQuoteBody {
pub fn mrenclave(&self) -> [u8; 32] {
Self::array_chunk(self.0.as_slice(), 112)
}
pub fn user_data(&self) -> [u8; 64] {
Self::array_chunk(self.0.as_slice(), 368)
}
fn array_chunk<const N: usize>(quote_body: &[u8], offset: usize) -> [u8; N] {
assert!(offset + N <= quote_body.len());
quote_body[offset..offset + N]
.try_into()
.expect("array length mismatch")
}
}

View file

@ -0,0 +1,172 @@
use cosmwasm_std::ensure_eq;
use num_bigint::BigUint;
use sha2::{Digest, Sha256};
use crate::intel_sgx::epid::types::IASReport;
use crate::intel_sgx::epid::Error as EpidError;
use crate::intel_sgx::epid::{INTEL_ROOT_EXPONENT, INTEL_ROOT_MODULUS};
use crate::intel_sgx::Error;
/// Given an RSA signature and the signer's exponent + modulus we recover the digest that was signed by the signature.
pub fn recover_signature_digest(signature: &[u8], exponent: &[u8], modulus: &[u8]) -> Vec<u8> {
let sig_as_bignum_be = BigUint::from_bytes_be(signature);
let intel_modulus_be = BigUint::from_bytes_be(modulus);
let intel_exponent_be = BigUint::from_bytes_be(exponent);
let digest_be = sig_as_bignum_be.modpow(&intel_exponent_be, &intel_modulus_be);
// last 32 bytes contain the digest
let digest_bytes = digest_be.to_bytes_be();
let n = digest_bytes.len();
digest_bytes[n - 32..n].to_vec()
}
pub fn verify(
ias_report: IASReport,
mrenclave: impl AsRef<[u8]>,
user_data: impl AsRef<[u8]>,
) -> Result<(), Error> {
// Extract the payload from the quote body
let user_data_in_quote = ias_report.report.isv_enclave_quote_body.user_data();
// check user_report_data
ensure_eq!(
user_data_in_quote,
user_data.as_ref(),
Error::UserDataMismatch
);
// Extract the mrenclave from the quote body
let mrenclave_in_quote = ias_report.report.isv_enclave_quote_body.mrenclave();
// check mrenclave
ensure_eq!(
mrenclave_in_quote,
mrenclave.as_ref(),
Error::MrEnclaveMismatch
);
// Recover the RSA signature's digest
let recovered_digest = recover_signature_digest(
ias_report.report_sig.as_slice(),
INTEL_ROOT_EXPONENT,
INTEL_ROOT_MODULUS,
);
// Convert the recovered digest into a byte slice
let recovered_digest = recovered_digest.as_slice();
// Convert the ias report as a json string, removing all the backslashes to escape stuff
let ias_report_asjson = serde_json::to_string(&ias_report.report)
.expect("infallible serializer for IASReport")
.replace('\\', "");
// Convert the ias report without the back slashes to bytes
let ias_report_asbytes = ias_report_asjson.as_bytes();
// We are going to calculate our own digest of the ias report
let mut hasher = Sha256::default();
// Update the hasher contents with that of the ias report json
hasher.update(ias_report_asbytes);
// finalize the sha256 hasher so that it produces a byte slice of the digest, should be 32 bytes
let ias_report_digest = &hasher.finalize()[..];
// ensure that the recovered digest from the signature matches the digest of the report
ensure_eq!(
ias_report_digest,
recovered_digest,
EpidError::RecoveredDigestMismatch
);
Ok(())
}
#[cfg(test)]
mod tests {
use cosmwasm_schema::cw_serde;
use cosmwasm_std::HexBinary;
use super::*;
#[cw_serde]
pub enum QueryMsg {
/// Verify an attestation
VerifyEpidAttestation {
// The report that is generated by an enclave
report: IASReport,
// The MRENCLAVE of this enclave (as hex string)
mrenclave: HexBinary,
// User data - whose commitment is in the `user_report_data` (as hex string)
user_data: HexBinary,
},
}
fn parse_attestation(query_verifier: &str) -> (IASReport, HexBinary, HexBinary) {
let query_verifier: QueryMsg =
serde_json::from_str(query_verifier).expect("deserialize query");
match query_verifier {
QueryMsg::VerifyEpidAttestation {
report,
mrenclave,
user_data,
} => (report, mrenclave, user_data),
}
}
fn verify_attestation(query_verifier: &str) {
let (report, mrenclave, user_data) = parse_attestation(query_verifier);
verify(report, mrenclave, user_data).expect("RA verification failure");
}
#[test]
fn test_verifier_ok() {
verify_attestation(
r#"{
"verify_epid_attestation": {
"report": {"report":{"id":"5246688123689513540899231107533660789","timestamp":"2024-02-07T17:06:23.913745","version":4,"epidPseudonym":"+CUyIi74LPqS6M0NF7YrSxLqPdX3MKs6D6LIPqRG/ZEB4WmxZVvxAJwdwg/0m9cYnUUQguLnJotthX645lAogfJgO8Xg5/91lSegwyUKvHmKgtjOHX/YTbVe/wmgWiBdaL+KmarY0Je459Px/FqGLWLsAF7egPAJRd1Xn88Znrs=","advisoryURL":"https://security-center.intel.com","advisoryIDs":["INTEL-SA-00161","INTEL-SA-00219","INTEL-SA-00289","INTEL-SA-00334","INTEL-SA-00615"],"isvEnclaveQuoteStatus":"CONFIGURATION_AND_SW_HARDENING_NEEDED","platformInfoBlob":"150200650000080000141402040180070000000000000000000D00000C000000020000000000000CB0F08115F3DE71AE97980FE5E10B042054930ACE356C79EC44603D3F890756EC6ED73927A7C58CDE9AF1E754AEC77E335E8D80294407936BEB6404F27669FF7BB1","isvEnclaveQuoteBody":"AgABALAMAAAPAA8AAAAAAFHK9aSLRQ1iSu/jKG0xSJQAAAAAAAAAAAAAAAAAAAAAFBQCBwGAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAHAAAAAAAAAOPC8qW4QNieBprK/8rbZRDvhmpz06nuVxAO1fhkbuS7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc8uUpEUEPvz8ZkFapjVh5WlWaLoAJM/f80T0EhGInHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACRE7C+d+1dDWhoDsdyBrjVh+1AZ5txMhzN1UBeTVSmggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"},"reportsig":"YcY4SPvkfR4P2E8A5huutCeS+vY/ir+xq6disalNfNtAcUyOIOqTPVXhAZgY1M5B47Hjj1oYWf2qC2w+dnj7VcZjzO9oR0pJYdA+A7jaVrNzH2eXA79yICkuU8WE/x58I0j5vjXLoHXahaKlpZkMeTphqBY8u+FTVSdP3cWPho4viPapTfQRuEWmYq4KIq2zSr6wLg3Pz+yQ+G3e9BASVkLYxdYGTDFH1pMmfas9SEI7V4I+j8DaXmL8bucSRakmcQdmDMPGiA7mvIhSAlprzCrdxM7CHeUC6MPLN1fmFFcc9kyO/ved69j/651MWC83GgxSJ15L80U+DQzmrSW8xg=="},
"mrenclave": "e3c2f2a5b840d89e069acaffcadb6510ef866a73d3a9ee57100ed5f8646ee4bb",
"user_data": "9113b0be77ed5d0d68680ec77206b8d587ed40679b71321ccdd5405e4d54a6820000000000000000000000000000000000000000000000000000000000000000"
}
}"#,
)
}
#[test]
#[should_panic]
fn test_verifier_bad_mrenclave() {
verify_attestation(
r#"{
"verify_epid_attestation": {
"report": {"report":{"id":"5246688123689513540899231107533660789","timestamp":"2024-02-07T17:06:23.913745","version":4,"epidPseudonym":"+CUyIi74LPqS6M0NF7YrSxLqPdX3MKs6D6LIPqRG/ZEB4WmxZVvxAJwdwg/0m9cYnUUQguLnJotthX645lAogfJgO8Xg5/91lSegwyUKvHmKgtjOHX/YTbVe/wmgWiBdaL+KmarY0Je459Px/FqGLWLsAF7egPAJRd1Xn88Znrs=","advisoryURL":"https://security-center.intel.com","advisoryIDs":["INTEL-SA-00161","INTEL-SA-00219","INTEL-SA-00289","INTEL-SA-00334","INTEL-SA-00615"],"isvEnclaveQuoteStatus":"CONFIGURATION_AND_SW_HARDENING_NEEDED","platformInfoBlob":"150200650000080000141402040180070000000000000000000D00000C000000020000000000000CB0F08115F3DE71AE97980FE5E10B042054930ACE356C79EC44603D3F890756EC6ED73927A7C58CDE9AF1E754AEC77E335E8D80294407936BEB6404F27669FF7BB1","isvEnclaveQuoteBody":"AgABALAMAAAPAA8AAAAAAFHK9aSLRQ1iSu/jKG0xSJQAAAAAAAAAAAAAAAAAAAAAFBQCBwGAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAHAAAAAAAAAOPC8qW4QNieBprK/8rbZRDvhmpz06nuVxAO1fhkbuS7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc8uUpEUEPvz8ZkFapjVh5WlWaLoAJM/f80T0EhGInHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACRE7C+d+1dDWhoDsdyBrjVh+1AZ5txMhzN1UBeTVSmggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"},"reportsig":"YcY4SPvkfR4P2E8A5huutCeS+vY/ir+xq6disalNfNtAcUyOIOqTPVXhAZgY1M5B47Hjj1oYWf2qC2w+dnj7VcZjzO9oR0pJYdA+A7jaVrNzH2eXA79yICkuU8WE/x58I0j5vjXLoHXahaKlpZkMeTphqBY8u+FTVSdP3cWPho4viPapTfQRuEWmYq4KIq2zSr6wLg3Pz+yQ+G3e9BASVkLYxdYGTDFH1pMmfas9SEI7V4I+j8DaXmL8bucSRakmcQdmDMPGiA7mvIhSAlprzCrdxM7CHeUC6MPLN1fmFFcc9kyO/ved69j/651MWC83GgxSJ15L80U+DQzmrSW8xg=="},
"mrenclave": "f3c2f2a5b840d89e069acaffcadb6510ef866a73d3a9ee57100ed5f8646ee4bb",
"user_data": "9113b0be77ed5d0d68680ec77206b8d587ed40679b71321ccdd5405e4d54a6820000000000000000000000000000000000000000000000000000000000000000"
}
}"#,
)
}
#[test]
#[should_panic]
fn test_verifier_bad_user_data() {
verify_attestation(
r#"{
"verify_epid_attestation": {
"report": {"report":{"id":"5246688123689513540899231107533660789","timestamp":"2024-02-07T17:06:23.913745","version":4,"epidPseudonym":"+CUyIi74LPqS6M0NF7YrSxLqPdX3MKs6D6LIPqRG/ZEB4WmxZVvxAJwdwg/0m9cYnUUQguLnJotthX645lAogfJgO8Xg5/91lSegwyUKvHmKgtjOHX/YTbVe/wmgWiBdaL+KmarY0Je459Px/FqGLWLsAF7egPAJRd1Xn88Znrs=","advisoryURL":"https://security-center.intel.com","advisoryIDs":["INTEL-SA-00161","INTEL-SA-00219","INTEL-SA-00289","INTEL-SA-00334","INTEL-SA-00615"],"isvEnclaveQuoteStatus":"CONFIGURATION_AND_SW_HARDENING_NEEDED","platformInfoBlob":"150200650000080000141402040180070000000000000000000D00000C000000020000000000000CB0F08115F3DE71AE97980FE5E10B042054930ACE356C79EC44603D3F890756EC6ED73927A7C58CDE9AF1E754AEC77E335E8D80294407936BEB6404F27669FF7BB1","isvEnclaveQuoteBody":"AgABALAMAAAPAA8AAAAAAFHK9aSLRQ1iSu/jKG0xSJQAAAAAAAAAAAAAAAAAAAAAFBQCBwGAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAHAAAAAAAAAOPC8qW4QNieBprK/8rbZRDvhmpz06nuVxAO1fhkbuS7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc8uUpEUEPvz8ZkFapjVh5WlWaLoAJM/f80T0EhGInHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACRE7C+d+1dDWhoDsdyBrjVh+1AZ5txMhzN1UBeTVSmggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"},"reportsig":"YcY4SPvkfR4P2E8A5huutCeS+vY/ir+xq6disalNfNtAcUyOIOqTPVXhAZgY1M5B47Hjj1oYWf2qC2w+dnj7VcZjzO9oR0pJYdA+A7jaVrNzH2eXA79yICkuU8WE/x58I0j5vjXLoHXahaKlpZkMeTphqBY8u+FTVSdP3cWPho4viPapTfQRuEWmYq4KIq2zSr6wLg3Pz+yQ+G3e9BASVkLYxdYGTDFH1pMmfas9SEI7V4I+j8DaXmL8bucSRakmcQdmDMPGiA7mvIhSAlprzCrdxM7CHeUC6MPLN1fmFFcc9kyO/ved69j/651MWC83GgxSJ15L80U+DQzmrSW8xg=="},
"mrenclave": "e3c2f2a5b840d89e069acaffcadb6510ef866a73d3a9ee57100ed5f8646ee4bb",
"user_data": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
}
}"#,
)
}
#[test]
#[should_panic]
fn test_verifier_bad_platform_info_blob() {
verify_attestation(
r#"{
"verify_epid_attestation": {
"report": {"report":{"id":"5246688123689513540899231107533660789","timestamp":"2024-02-07T17:06:23.913745","version":4,"epidPseudonym":"+CUyIi74LPqS6M0NF7YrSxLqPdX3MKs6D6LIPqRG/ZEB4WmxZVvxAJwdwg/0m9cYnUUQguLnJotthX645lAogfJgO8Xg5/91lSegwyUKvHmKgtjOHX/YTbVe/wmgWiBdaL+KmarY0Je459Px/FqGLWLsAF7egPAJRd1Xn88Znrs=","advisoryURL":"https://security-center.intel.com","advisoryIDs":["INTEL-SA-00161","INTEL-SA-00219","INTEL-SA-00289","INTEL-SA-00334","INTEL-SA-00615"],"isvEnclaveQuoteStatus":"CONFIGURATION_AND_SW_HARDENING_NEEDED","platformInfoBlob":"150200650000080000141402040180070000000000000000000D00000C000000020000000000000CB0F08115F3DE71AE97980FE5E10B042054930ACE356C79EC44603D3F890756EC6ED73927A7C58CDE9AF1E754AEC77E335E8D80294407936BEB6404F27669FF7BB1","isvEnclaveQuoteBody":"AgABALAMAAAPAA8AAAAAAFHK9aSLRQ1iSu/jKG0xSJQAAAAAAAAAAAAAAAAAAAAAFBQCBwGAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABQAAAAAAAAAHAAAAAAAAAOPC8qW4QNieBprK/8rbZRDvhmpz06nuVxAO1fhkbuS7AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc8uUpEUEPvz8ZkFapjVh5WlWaLoAJM/f80T0EhGInHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACRE7C+d+1dDWhoDsdyBrjVh+1AZ5txMhzN1UBeTVSmggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"},"reportsig":"YaY4SPvkfR4P2E8A5huutCeS+vY/ir+xq6disalNfNtAcUyOIOqTPVXhAZgY1M5B47Hjj1oYWf2qC2w+dnj7VcZjzO9oR0pJYdA+A7jaVrNzH2eXA79yICkuU8WE/x58I0j5vjXLoHXahaKlpZkMeTphqBY8u+FTVSdP3cWPho4viPapTfQRuEWmYq4KIq2zSr6wLg3Pz+yQ+G3e9BASVkLYxdYGTDFH1pMmfas9SEI7V4I+j8DaXmL8bucSRakmcQdmDMPGiA7mvIhSAlprzCrdxM7CHeUC6MPLN1fmFFcc9kyO/ved69j/651MWC83GgxSJ15L80U+DQzmrSW8xg=="},
"mrenclave": "e3c2f2a5b840d89e069acaffcadb6510ef866a73d3a9ee57100ed5f8646ee4bb",
"user_data": "9113b0be77ed5d0d68680ec77206b8d587ed40679b71321ccdd5405e4d54a6820000000000000000000000000000000000000000000000000000000000000000"
}
}"#,
)
}
}

View file

@ -0,0 +1,22 @@
#![warn(
clippy::checked_conversions,
clippy::panic,
clippy::panic_in_result_fn,
clippy::unwrap_used,
rust_2018_idioms,
unused_lifetimes
)]
#![deny(
trivial_casts,
trivial_numeric_casts,
unused_import_braces,
unused_qualifications,
warnings
)]
#![forbid(unsafe_code)]
pub mod intel_sgx;
pub use intel_sgx::epid::types::IASReport;
pub use intel_sgx::epid::verifier::verify as verify_epid_attestation;
pub use intel_sgx::Error;