feat(dcap): use tcbinfo
from contract for RA verification (#179)
Co-authored-by: hu55a1n1 <sufialhussaini@gmail.com>
This commit is contained in:
parent
e7cd6b1151
commit
1a2ab7d008
12 changed files with 123 additions and 15 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -4145,12 +4145,14 @@ dependencies = [
|
||||||
"cosmwasm-schema",
|
"cosmwasm-schema",
|
||||||
"cosmwasm-std",
|
"cosmwasm-std",
|
||||||
"cw-storage-plus",
|
"cw-storage-plus",
|
||||||
|
"hex",
|
||||||
"k256",
|
"k256",
|
||||||
"quartz-tee-ra",
|
"quartz-tee-ra",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"sha2 0.10.8",
|
"sha2 0.10.8",
|
||||||
|
"tcbinfo",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
27
apps/mtcs/contracts/cw-tee-mtcs/Cargo.lock
generated
27
apps/mtcs/contracts/cw-tee-mtcs/Cargo.lock
generated
|
@ -771,6 +771,7 @@ dependencies = [
|
||||||
"ff",
|
"ff",
|
||||||
"generic-array",
|
"generic-array",
|
||||||
"group",
|
"group",
|
||||||
|
"pem-rfc7468",
|
||||||
"pkcs8",
|
"pkcs8",
|
||||||
"rand_core",
|
"rand_core",
|
||||||
"sec1",
|
"sec1",
|
||||||
|
@ -858,6 +859,7 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ahash",
|
"ahash",
|
||||||
"allocator-api2",
|
"allocator-api2",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1208,12 +1210,14 @@ dependencies = [
|
||||||
"cosmwasm-schema",
|
"cosmwasm-schema",
|
||||||
"cosmwasm-std",
|
"cosmwasm-std",
|
||||||
"cw-storage-plus",
|
"cw-storage-plus",
|
||||||
|
"hex",
|
||||||
"k256",
|
"k256",
|
||||||
"quartz-tee-ra",
|
"quartz-tee-ra",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"sha2",
|
"sha2",
|
||||||
|
"tcbinfo",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1565,6 +1569,29 @@ dependencies = [
|
||||||
"syn 2.0.76",
|
"syn 2.0.76",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tcbinfo"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"cosmwasm-schema",
|
||||||
|
"cosmwasm-std",
|
||||||
|
"cw-storage-plus",
|
||||||
|
"cw2",
|
||||||
|
"der",
|
||||||
|
"getrandom",
|
||||||
|
"hashbrown 0.14.5",
|
||||||
|
"hex",
|
||||||
|
"mc-attestation-verifier",
|
||||||
|
"p256",
|
||||||
|
"quartz-tee-ra",
|
||||||
|
"schemars",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror",
|
||||||
|
"x509-cert",
|
||||||
|
"x509-parser",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.63"
|
version = "1.0.63"
|
||||||
|
|
|
@ -183,7 +183,8 @@ mod tests {
|
||||||
"trusting_period": 1209600,
|
"trusting_period": 1209600,
|
||||||
"max_clock_drift": 5,
|
"max_clock_drift": 5,
|
||||||
"max_block_lag": 5
|
"max_block_lag": 5
|
||||||
}
|
},
|
||||||
|
"tcbinfo_contract": "wasm14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s0phg4d"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"attestation": {
|
"attestation": {
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::net::SocketAddr;
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use color_eyre::eyre::{eyre, Result};
|
use color_eyre::eyre::{eyre, Result};
|
||||||
use cosmrs::tendermint::Hash;
|
use cosmrs::{tendermint::Hash, AccountId};
|
||||||
use tendermint_light_client::types::{Height, TrustThreshold};
|
use tendermint_light_client::types::{Height, TrustThreshold};
|
||||||
|
|
||||||
fn parse_trust_threshold(s: &str) -> Result<TrustThreshold> {
|
fn parse_trust_threshold(s: &str) -> Result<TrustThreshold> {
|
||||||
|
@ -26,6 +26,10 @@ pub struct Cli {
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub chain_id: String,
|
pub chain_id: String,
|
||||||
|
|
||||||
|
/// TcbInfo contract address
|
||||||
|
#[clap(long)]
|
||||||
|
pub tcbinfo_contract: AccountId,
|
||||||
|
|
||||||
/// Height of the trusted header (AKA root-of-trust)
|
/// Height of the trusted header (AKA root-of-trust)
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub trusted_height: Height,
|
pub trusted_height: Height,
|
||||||
|
|
|
@ -61,6 +61,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
attestor.mr_enclave()?,
|
attestor.mr_enclave()?,
|
||||||
Duration::from_secs(30 * 24 * 60),
|
Duration::from_secs(30 * 24 * 60),
|
||||||
light_client_opts,
|
light_client_opts,
|
||||||
|
args.tcbinfo_contract.to_string(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let sk = Arc::new(Mutex::new(None));
|
let sk = Arc::new(Mutex::new(None));
|
||||||
|
|
1
apps/transfers/contracts/Cargo.lock
generated
1
apps/transfers/contracts/Cargo.lock
generated
|
@ -1189,6 +1189,7 @@ dependencies = [
|
||||||
"cosmwasm-schema",
|
"cosmwasm-schema",
|
||||||
"cosmwasm-std",
|
"cosmwasm-std",
|
||||||
"cw-storage-plus",
|
"cw-storage-plus",
|
||||||
|
"hex",
|
||||||
"k256",
|
"k256",
|
||||||
"quartz-tee-ra",
|
"quartz-tee-ra",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::{env, net::SocketAddr};
|
||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use color_eyre::eyre::{eyre, Result};
|
use color_eyre::eyre::{eyre, Result};
|
||||||
use cosmrs::tendermint::Hash;
|
use cosmrs::{tendermint::Hash, AccountId};
|
||||||
use tendermint_light_client::types::{Height, TrustThreshold};
|
use tendermint_light_client::types::{Height, TrustThreshold};
|
||||||
|
|
||||||
fn parse_trust_threshold(s: &str) -> Result<TrustThreshold> {
|
fn parse_trust_threshold(s: &str) -> Result<TrustThreshold> {
|
||||||
|
@ -26,6 +26,10 @@ pub struct Cli {
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub chain_id: String,
|
pub chain_id: String,
|
||||||
|
|
||||||
|
/// TcbInfo contract address
|
||||||
|
#[clap(long)]
|
||||||
|
pub tcbinfo_contract: AccountId,
|
||||||
|
|
||||||
/// Height of the trusted header (AKA root-of-trust)
|
/// Height of the trusted header (AKA root-of-trust)
|
||||||
#[clap(long)]
|
#[clap(long)]
|
||||||
pub trusted_height: Height,
|
pub trusted_height: Height,
|
||||||
|
|
|
@ -61,6 +61,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
attestor.mr_enclave()?,
|
attestor.mr_enclave()?,
|
||||||
Duration::from_secs(30 * 24 * 60),
|
Duration::from_secs(30 * 24 * 60),
|
||||||
light_client_opts,
|
light_client_opts,
|
||||||
|
args.tcbinfo_contract.to_string(),
|
||||||
);
|
);
|
||||||
|
|
||||||
let sk = Arc::new(Mutex::new(None));
|
let sk = Arc::new(Mutex::new(None));
|
||||||
|
|
|
@ -22,7 +22,7 @@ serde_json.workspace = true
|
||||||
serde_with.workspace = true
|
serde_with.workspace = true
|
||||||
sha2.workspace = true
|
sha2.workspace = true
|
||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
|
hex.workspace = true
|
||||||
# cosmos
|
# cosmos
|
||||||
cw-storage-plus.workspace = true
|
cw-storage-plus.workspace = true
|
||||||
cosmwasm-schema.workspace = true
|
cosmwasm-schema.workspace = true
|
||||||
|
@ -31,5 +31,8 @@ cosmwasm-std.workspace = true
|
||||||
# quartz
|
# quartz
|
||||||
quartz-tee-ra.workspace = true
|
quartz-tee-ra.workspace = true
|
||||||
|
|
||||||
|
# tcbinfo
|
||||||
|
tcbinfo = { path = "../tcbinfo", features = ["library"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
|
|
|
@ -13,6 +13,10 @@ pub enum Error {
|
||||||
K256(K256Error),
|
K256(K256Error),
|
||||||
#[error("invalid session nonce or attempt to reset pub_key")]
|
#[error("invalid session nonce or attempt to reset pub_key")]
|
||||||
BadSessionTransition,
|
BadSessionTransition,
|
||||||
|
#[error("Invalid FMSPC: {0}")]
|
||||||
|
InvalidFmspc(String),
|
||||||
|
#[error("TCB Info query error: {0}")]
|
||||||
|
TcbInfoQueryError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<K256Error> for Error {
|
impl From<K256Error> for Error {
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
use cosmwasm_std::{DepsMut, Env, MessageInfo, Response};
|
use cosmwasm_std::{
|
||||||
use quartz_tee_ra::{
|
from_json, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, QueryRequest, Response,
|
||||||
intel_sgx::dcap::TrustedMrEnclaveIdentity, verify_dcap_attestation, verify_epid_attestation,
|
WasmQuery,
|
||||||
Error as RaVerificationError,
|
|
||||||
};
|
};
|
||||||
|
use quartz_tee_ra::{
|
||||||
|
intel_sgx::dcap::{Collateral, TrustedMrEnclaveIdentity},
|
||||||
|
verify_dcap_attestation, verify_epid_attestation, Error as RaVerificationError,
|
||||||
|
};
|
||||||
|
use tcbinfo::msg::{GetTcbInfoResponse, QueryMsg as TcbInfoQueryMsg};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
|
@ -14,6 +18,28 @@ use crate::{
|
||||||
state::CONFIG,
|
state::CONFIG,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn query_tcbinfo(deps: Deps<'_>, fmspc: String) -> Result<Binary, Error> {
|
||||||
|
let config = CONFIG.load(deps.storage).map_err(Error::Std)?;
|
||||||
|
let tcbinfo_addr = config.tcb_info();
|
||||||
|
|
||||||
|
let fmspc_bytes =
|
||||||
|
hex::decode(&fmspc).map_err(|_| Error::InvalidFmspc("Invalid FMSPC format".to_string()))?;
|
||||||
|
if fmspc_bytes.len() != 6 {
|
||||||
|
return Err(Error::InvalidFmspc("FMSPC must be 6 bytes".to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let query_msg = TcbInfoQueryMsg::GetTcbInfo { fmspc };
|
||||||
|
|
||||||
|
let request = QueryRequest::Wasm(WasmQuery::Smart {
|
||||||
|
contract_addr: tcbinfo_addr,
|
||||||
|
msg: to_json_binary(&query_msg).map_err(Error::Std)?,
|
||||||
|
});
|
||||||
|
|
||||||
|
deps.querier
|
||||||
|
.query(&request)
|
||||||
|
.map_err(|err| Error::TcbInfoQueryError(err.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
impl Handler for EpidAttestation {
|
impl Handler for EpidAttestation {
|
||||||
fn handle(
|
fn handle(
|
||||||
self,
|
self,
|
||||||
|
@ -33,17 +59,37 @@ impl Handler for EpidAttestation {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Handler for DcapAttestation {
|
impl Handler for DcapAttestation {
|
||||||
fn handle(
|
fn handle(self, deps: DepsMut<'_>, _env: &Env, _info: &MessageInfo) -> Result<Response, Error> {
|
||||||
self,
|
|
||||||
_deps: DepsMut<'_>,
|
|
||||||
_env: &Env,
|
|
||||||
_info: &MessageInfo,
|
|
||||||
) -> Result<Response, Error> {
|
|
||||||
let (quote, collateral) = self.clone().into_tuple();
|
let (quote, collateral) = self.clone().into_tuple();
|
||||||
let mr_enclave = TrustedMrEnclaveIdentity::new(self.mr_enclave().into(), [""; 0], [""; 0]);
|
let mr_enclave = TrustedMrEnclaveIdentity::new(self.mr_enclave().into(), [""; 0], [""; 0]);
|
||||||
|
|
||||||
|
// Retrieve the FMSPC from the collateral
|
||||||
|
let fmspc_hex = collateral.tcb_info().to_string();
|
||||||
|
|
||||||
|
// Query the tcbinfo contract with the FMSPC retrieved and validated
|
||||||
|
let tcb_info_query = query_tcbinfo(deps.as_ref(), fmspc_hex)?;
|
||||||
|
let tcb_info_response: GetTcbInfoResponse = from_json(tcb_info_query)?;
|
||||||
|
|
||||||
|
// Serialize the existing collateral
|
||||||
|
let mut collateral_json: serde_json::Value =
|
||||||
|
serde_json::to_value(&collateral).map_err(|e| {
|
||||||
|
Error::TcbInfoQueryError(format!("Failed to serialize collateral: {}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Update the tcb_info in the serialized data
|
||||||
|
collateral_json["tcb_info"] = tcb_info_response.tcb_info;
|
||||||
|
|
||||||
|
// Deserialize back into a Collateral
|
||||||
|
let updated_collateral: Collateral =
|
||||||
|
serde_json::from_value(collateral_json).map_err(|e| {
|
||||||
|
Error::TcbInfoQueryError(format!("Failed to deserialize updated collateral: {}", e))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// attestation handler MUST verify that the user_data and mr_enclave match the config/msg
|
||||||
|
let verification_output =
|
||||||
|
verify_dcap_attestation(quote, updated_collateral, &[mr_enclave.into()]);
|
||||||
|
|
||||||
// attestation handler MUST verify that the user_data and mr_enclave match the config/msg
|
// 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() {
|
if verification_output.is_success().into() {
|
||||||
Ok(Response::default())
|
Ok(Response::default())
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -17,6 +17,7 @@ pub struct Config {
|
||||||
mr_enclave: MrEnclave,
|
mr_enclave: MrEnclave,
|
||||||
epoch_duration: Duration,
|
epoch_duration: Duration,
|
||||||
light_client_opts: LightClientOpts,
|
light_client_opts: LightClientOpts,
|
||||||
|
tcbinfo_contract: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
|
@ -24,11 +25,13 @@ impl Config {
|
||||||
mr_enclave: MrEnclave,
|
mr_enclave: MrEnclave,
|
||||||
epoch_duration: Duration,
|
epoch_duration: Duration,
|
||||||
light_client_opts: LightClientOpts,
|
light_client_opts: LightClientOpts,
|
||||||
|
tcbinfo_contract: String,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mr_enclave,
|
mr_enclave,
|
||||||
epoch_duration,
|
epoch_duration,
|
||||||
light_client_opts,
|
light_client_opts,
|
||||||
|
tcbinfo_contract,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,6 +42,10 @@ impl Config {
|
||||||
pub fn mr_enclave(&self) -> MrEnclave {
|
pub fn mr_enclave(&self) -> MrEnclave {
|
||||||
self.mr_enclave
|
self.mr_enclave
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tcbinfo_contract(&self) -> &str {
|
||||||
|
&self.tcbinfo_contract
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cw_serde]
|
#[cw_serde]
|
||||||
|
@ -46,12 +53,17 @@ pub struct RawConfig {
|
||||||
mr_enclave: HexBinary,
|
mr_enclave: HexBinary,
|
||||||
epoch_duration: Duration,
|
epoch_duration: Duration,
|
||||||
light_client_opts: RawLightClientOpts,
|
light_client_opts: RawLightClientOpts,
|
||||||
|
tcbinfo_contract: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RawConfig {
|
impl RawConfig {
|
||||||
pub fn mr_enclave(&self) -> &[u8] {
|
pub fn mr_enclave(&self) -> &[u8] {
|
||||||
self.mr_enclave.as_slice()
|
self.mr_enclave.as_slice()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn tcb_info(&self) -> String {
|
||||||
|
self.tcbinfo_contract.to_string()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<RawConfig> for Config {
|
impl TryFrom<RawConfig> for Config {
|
||||||
|
@ -65,6 +77,7 @@ impl TryFrom<RawConfig> for Config {
|
||||||
.light_client_opts
|
.light_client_opts
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(|e| StdError::parse_err("light_client_opts", e))?,
|
.map_err(|e| StdError::parse_err("light_client_opts", e))?,
|
||||||
|
tcbinfo_contract: value.tcbinfo_contract,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,6 +88,7 @@ impl From<Config> for RawConfig {
|
||||||
mr_enclave: value.mr_enclave.into(),
|
mr_enclave: value.mr_enclave.into(),
|
||||||
epoch_duration: value.epoch_duration,
|
epoch_duration: value.epoch_duration,
|
||||||
light_client_opts: value.light_client_opts.into(),
|
light_client_opts: value.light_client_opts.into(),
|
||||||
|
tcbinfo_contract: value.tcbinfo_contract,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue