diff --git a/Cargo.lock b/Cargo.lock index c257451..4d937c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4145,12 +4145,14 @@ dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", + "hex", "k256", "quartz-tee-ra", "serde", "serde_json", "serde_with", "sha2 0.10.8", + "tcbinfo", "thiserror", ] diff --git a/apps/mtcs/contracts/cw-tee-mtcs/Cargo.lock b/apps/mtcs/contracts/cw-tee-mtcs/Cargo.lock index 722a55a..1e74213 100644 --- a/apps/mtcs/contracts/cw-tee-mtcs/Cargo.lock +++ b/apps/mtcs/contracts/cw-tee-mtcs/Cargo.lock @@ -771,6 +771,7 @@ dependencies = [ "ff", "generic-array", "group", + "pem-rfc7468", "pkcs8", "rand_core", "sec1", @@ -858,6 +859,7 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", "allocator-api2", + "serde", ] [[package]] @@ -1208,12 +1210,14 @@ dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", + "hex", "k256", "quartz-tee-ra", "serde", "serde_json", "serde_with", "sha2", + "tcbinfo", "thiserror", ] @@ -1565,6 +1569,29 @@ dependencies = [ "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]] name = "thiserror" version = "1.0.63" diff --git a/apps/mtcs/contracts/cw-tee-mtcs/src/msg.rs b/apps/mtcs/contracts/cw-tee-mtcs/src/msg.rs index 5b7b4f1..91f3aa2 100644 --- a/apps/mtcs/contracts/cw-tee-mtcs/src/msg.rs +++ b/apps/mtcs/contracts/cw-tee-mtcs/src/msg.rs @@ -183,7 +183,8 @@ mod tests { "trusting_period": 1209600, "max_clock_drift": 5, "max_block_lag": 5 - } + }, + "tcbinfo_contract": "wasm14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9s0phg4d" } }, "attestation": { diff --git a/apps/mtcs/enclave/src/cli.rs b/apps/mtcs/enclave/src/cli.rs index 7c59e62..1e00732 100644 --- a/apps/mtcs/enclave/src/cli.rs +++ b/apps/mtcs/enclave/src/cli.rs @@ -2,7 +2,7 @@ use std::net::SocketAddr; use clap::Parser; use color_eyre::eyre::{eyre, Result}; -use cosmrs::tendermint::Hash; +use cosmrs::{tendermint::Hash, AccountId}; use tendermint_light_client::types::{Height, TrustThreshold}; fn parse_trust_threshold(s: &str) -> Result { @@ -26,6 +26,10 @@ pub struct Cli { #[clap(long)] pub chain_id: String, + /// TcbInfo contract address + #[clap(long)] + pub tcbinfo_contract: AccountId, + /// Height of the trusted header (AKA root-of-trust) #[clap(long)] pub trusted_height: Height, diff --git a/apps/mtcs/enclave/src/main.rs b/apps/mtcs/enclave/src/main.rs index 6a00641..500fe53 100644 --- a/apps/mtcs/enclave/src/main.rs +++ b/apps/mtcs/enclave/src/main.rs @@ -61,6 +61,7 @@ async fn main() -> Result<(), Box> { attestor.mr_enclave()?, Duration::from_secs(30 * 24 * 60), light_client_opts, + args.tcbinfo_contract.to_string(), ); let sk = Arc::new(Mutex::new(None)); diff --git a/apps/transfers/contracts/Cargo.lock b/apps/transfers/contracts/Cargo.lock index 43ddb92..2e00708 100644 --- a/apps/transfers/contracts/Cargo.lock +++ b/apps/transfers/contracts/Cargo.lock @@ -1189,6 +1189,7 @@ dependencies = [ "cosmwasm-schema", "cosmwasm-std", "cw-storage-plus", + "hex", "k256", "quartz-tee-ra", "serde", diff --git a/apps/transfers/enclave/src/cli.rs b/apps/transfers/enclave/src/cli.rs index 55dbf7d..958d2a3 100644 --- a/apps/transfers/enclave/src/cli.rs +++ b/apps/transfers/enclave/src/cli.rs @@ -2,7 +2,7 @@ use std::{env, net::SocketAddr}; use clap::Parser; use color_eyre::eyre::{eyre, Result}; -use cosmrs::tendermint::Hash; +use cosmrs::{tendermint::Hash, AccountId}; use tendermint_light_client::types::{Height, TrustThreshold}; fn parse_trust_threshold(s: &str) -> Result { @@ -26,6 +26,10 @@ pub struct Cli { #[clap(long)] pub chain_id: String, + /// TcbInfo contract address + #[clap(long)] + pub tcbinfo_contract: AccountId, + /// Height of the trusted header (AKA root-of-trust) #[clap(long)] pub trusted_height: Height, diff --git a/apps/transfers/enclave/src/main.rs b/apps/transfers/enclave/src/main.rs index d65771f..64f62f1 100644 --- a/apps/transfers/enclave/src/main.rs +++ b/apps/transfers/enclave/src/main.rs @@ -61,6 +61,7 @@ async fn main() -> Result<(), Box> { attestor.mr_enclave()?, Duration::from_secs(30 * 24 * 60), light_client_opts, + args.tcbinfo_contract.to_string(), ); let sk = Arc::new(Mutex::new(None)); diff --git a/cosmwasm/packages/quartz-cw/Cargo.toml b/cosmwasm/packages/quartz-cw/Cargo.toml index 1f9da8e..889f13f 100644 --- a/cosmwasm/packages/quartz-cw/Cargo.toml +++ b/cosmwasm/packages/quartz-cw/Cargo.toml @@ -22,7 +22,7 @@ serde_json.workspace = true serde_with.workspace = true sha2.workspace = true thiserror.workspace = true - +hex.workspace = true # cosmos cw-storage-plus.workspace = true cosmwasm-schema.workspace = true @@ -31,5 +31,8 @@ cosmwasm-std.workspace = true # quartz quartz-tee-ra.workspace = true +# tcbinfo +tcbinfo = { path = "../tcbinfo", features = ["library"] } + [dev-dependencies] serde_json.workspace = true diff --git a/cosmwasm/packages/quartz-cw/src/error.rs b/cosmwasm/packages/quartz-cw/src/error.rs index 228d20d..bb78fb1 100644 --- a/cosmwasm/packages/quartz-cw/src/error.rs +++ b/cosmwasm/packages/quartz-cw/src/error.rs @@ -13,6 +13,10 @@ pub enum Error { K256(K256Error), #[error("invalid session nonce or attempt to reset pub_key")] BadSessionTransition, + #[error("Invalid FMSPC: {0}")] + InvalidFmspc(String), + #[error("TCB Info query error: {0}")] + TcbInfoQueryError(String), } impl From for Error { diff --git a/cosmwasm/packages/quartz-cw/src/handler/execute/attested.rs b/cosmwasm/packages/quartz-cw/src/handler/execute/attested.rs index 61cf8be..319b4fb 100644 --- a/cosmwasm/packages/quartz-cw/src/handler/execute/attested.rs +++ b/cosmwasm/packages/quartz-cw/src/handler/execute/attested.rs @@ -1,8 +1,12 @@ -use cosmwasm_std::{DepsMut, Env, MessageInfo, Response}; -use quartz_tee_ra::{ - intel_sgx::dcap::TrustedMrEnclaveIdentity, verify_dcap_attestation, verify_epid_attestation, - Error as RaVerificationError, +use cosmwasm_std::{ + from_json, to_json_binary, Binary, Deps, DepsMut, Env, MessageInfo, QueryRequest, Response, + WasmQuery, }; +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::{ error::Error, @@ -14,6 +18,28 @@ use crate::{ state::CONFIG, }; +pub fn query_tcbinfo(deps: Deps<'_>, fmspc: String) -> Result { + 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 { fn handle( self, @@ -33,17 +59,37 @@ impl Handler for EpidAttestation { } impl Handler for DcapAttestation { - fn handle( - self, - _deps: DepsMut<'_>, - _env: &Env, - _info: &MessageInfo, - ) -> Result { + 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]); + // 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 - let verification_output = verify_dcap_attestation(quote, collateral, &[mr_enclave.into()]); if verification_output.is_success().into() { Ok(Response::default()) } else { diff --git a/cosmwasm/packages/quartz-cw/src/state.rs b/cosmwasm/packages/quartz-cw/src/state.rs index b8a42d1..d8c2a7a 100644 --- a/cosmwasm/packages/quartz-cw/src/state.rs +++ b/cosmwasm/packages/quartz-cw/src/state.rs @@ -17,6 +17,7 @@ pub struct Config { mr_enclave: MrEnclave, epoch_duration: Duration, light_client_opts: LightClientOpts, + tcbinfo_contract: String, } impl Config { @@ -24,11 +25,13 @@ impl Config { mr_enclave: MrEnclave, epoch_duration: Duration, light_client_opts: LightClientOpts, + tcbinfo_contract: String, ) -> Self { Self { mr_enclave, epoch_duration, light_client_opts, + tcbinfo_contract, } } @@ -39,6 +42,10 @@ impl Config { pub fn mr_enclave(&self) -> MrEnclave { self.mr_enclave } + + pub fn tcbinfo_contract(&self) -> &str { + &self.tcbinfo_contract + } } #[cw_serde] @@ -46,12 +53,17 @@ pub struct RawConfig { mr_enclave: HexBinary, epoch_duration: Duration, light_client_opts: RawLightClientOpts, + tcbinfo_contract: String, } impl RawConfig { pub fn mr_enclave(&self) -> &[u8] { self.mr_enclave.as_slice() } + + pub fn tcb_info(&self) -> String { + self.tcbinfo_contract.to_string() + } } impl TryFrom for Config { @@ -65,6 +77,7 @@ impl TryFrom for Config { .light_client_opts .try_into() .map_err(|e| StdError::parse_err("light_client_opts", e))?, + tcbinfo_contract: value.tcbinfo_contract, }) } } @@ -75,6 +88,7 @@ impl From for RawConfig { mr_enclave: value.mr_enclave.into(), epoch_duration: value.epoch_duration, light_client_opts: value.light_client_opts.into(), + tcbinfo_contract: value.tcbinfo_contract, } } }