quartz_contract_core/handler/execute/
attested.rs1use ciborium::{from_reader as from_cbor_slice, into_writer as into_cbor, Value as CborValue};
2use cosmwasm_std::{
3 to_json_binary, Deps, DepsMut, Env, MessageInfo, QueryRequest, Response, StdResult, WasmQuery,
4};
5use quartz_dcap_verifier_msgs::QueryMsg as DcapVerifierQueryMsg;
6use quartz_tcbinfo_msgs::{GetTcbInfoResponse, QueryMsg as TcbInfoQueryMsg};
7use quartz_tee_ra::{
8 intel_sgx::dcap::{Collateral, TrustedIdentity, TrustedMrEnclaveIdentity},
9 Error as RaVerificationError,
10};
11use serde::{de::DeserializeOwned, Serialize};
12
13use crate::{
14 error::Error,
15 handler::Handler,
16 msg::execute::attested::{
17 Attestation, Attested, DcapAttestation, HasUserData, MockAttestation, Noop, Quote,
18 },
19 state::CONFIG,
20};
21
22fn query_contract<T: DeserializeOwned>(
23 deps: Deps<'_>,
24 contract_addr: String,
25 query_msg: impl Serialize,
26) -> StdResult<T> {
27 let request = QueryRequest::Wasm(WasmQuery::Smart {
28 contract_addr,
29 msg: to_json_binary(&query_msg)?,
30 });
31
32 deps.querier.query(&request)
33}
34
35fn query_tcbinfo(deps: Deps<'_>, fmspc: String) -> Result<GetTcbInfoResponse, Error> {
36 let tcbinfo_addr = {
37 let config = CONFIG.load(deps.storage).map_err(Error::Std)?;
38 config
39 .tcbinfo_contract()
40 .expect("TcbInfo contract address is required for DCAP")
41 .to_string()
42 };
43
44 let fmspc_bytes =
45 hex::decode(&fmspc).map_err(|_| Error::InvalidFmspc("Invalid FMSPC format".to_string()))?;
46 if fmspc_bytes.len() != 6 {
47 return Err(Error::InvalidFmspc("FMSPC must be 6 bytes".to_string()));
48 }
49
50 let query_msg = TcbInfoQueryMsg::GetTcbInfo { fmspc };
51
52 query_contract(deps, tcbinfo_addr, &query_msg)
53 .map_err(|err| Error::TcbInfoQueryError(err.to_string()))
54}
55
56fn to_cbor_vec<T: Serialize>(value: &T) -> Vec<u8> {
57 let mut buffer = Vec::new();
58 into_cbor(&value, &mut buffer).expect("Serialization failed");
59 buffer
60}
61
62fn query_dcap_verifier(
63 deps: Deps<'_>,
64 quote: Quote,
65 mr_enclave: impl Into<TrustedIdentity>,
66 updated_collateral: Collateral,
67) -> Result<(), Error> {
68 let query_msg = DcapVerifierQueryMsg::VerifyDcapAttestation {
69 quote: quote.as_ref().to_vec().into(),
70 collateral: to_cbor_vec(&updated_collateral).into(),
71 identities: Some(to_cbor_vec(&[mr_enclave.into()])),
72 };
73
74 let dcap_verifier_contract = {
75 let config = CONFIG.load(deps.storage).map_err(Error::Std)?;
76 config
77 .dcap_verifier_contract()
78 .expect("verifier_contract address is required for DCAP")
79 .to_string()
80 };
81
82 query_contract(deps, dcap_verifier_contract, &query_msg)
83 .map_err(|err| Error::DcapVerificationQueryError(err.to_string()))
84}
85
86impl Handler for DcapAttestation {
87 fn handle(self, deps: DepsMut<'_>, _env: &Env, _info: &MessageInfo) -> Result<Response, Error> {
88 let (quote, collateral) = self.clone().into_tuple();
89 let mr_enclave = TrustedMrEnclaveIdentity::new(
90 self.mr_enclave().into(),
91 [""; 0],
92 ["INTEL-SA-00334", "INTEL-SA-00615"],
93 );
94
95 let fmspc_hex = collateral.tcb_info().to_string();
97
98 let tcb_info_response = query_tcbinfo(deps.as_ref(), fmspc_hex)?;
100
101 let collateral_serialized = to_cbor_vec(&collateral);
103 let mut collateral_value: CborValue = from_cbor_slice(collateral_serialized.as_slice())
104 .map_err(|e| {
105 Error::TcbInfoQueryError(format!("Failed to serialize collateral: {}", e))
106 })?;
107
108 fn try_get_tcb_info(collateral_value: &mut CborValue) -> Option<&mut CborValue> {
110 if let CborValue::Map(map) = collateral_value {
111 return map
112 .iter_mut()
113 .find(|(k, _)| k == &CborValue::Text("tcb_info".to_string()))
114 .map(|(_, v)| v);
115 }
116 None
117 }
118
119 let tcb_info_value = try_get_tcb_info(&mut collateral_value).expect("infallible serde");
120 *tcb_info_value = CborValue::Text(tcb_info_response.tcb_info.to_string());
121
122 let collateral_serialized = to_cbor_vec(&collateral_value);
124 let updated_collateral: Collateral = from_cbor_slice(collateral_serialized.as_slice())
125 .map_err(|e| {
126 Error::TcbInfoQueryError(format!("Failed to deserialize updated collateral: {}", e))
127 })?;
128
129 query_dcap_verifier(deps.as_ref(), quote, mr_enclave, updated_collateral)
130 .map(|_| Response::default())
131 }
132}
133
134impl Handler for MockAttestation {
135 fn handle(
136 self,
137 _deps: DepsMut<'_>,
138 _env: &Env,
139 _info: &MessageInfo,
140 ) -> Result<Response, Error> {
141 Ok(Response::default())
142 }
143}
144
145impl<M, A> Handler for Attested<M, A>
146where
147 M: Handler + HasUserData,
148 A: Handler + HasUserData + Attestation,
149{
150 fn handle(
151 self,
152 mut deps: DepsMut<'_>,
153 env: &Env,
154 info: &MessageInfo,
155 ) -> Result<Response, Error> {
156 let (msg, attestation) = self.into_tuple();
157 if msg.user_data() != attestation.user_data() {
158 return Err(RaVerificationError::UserDataMismatch.into());
159 }
160
161 if let Some(config) = CONFIG.may_load(deps.storage)? {
162 if config.mr_enclave() != attestation.mr_enclave() {
165 return Err(RaVerificationError::MrEnclaveMismatch.into());
166 }
167 }
168
169 let res_msg = Handler::handle(msg, deps.branch(), env, info)?;
175 let res_attest = Handler::handle(attestation, deps, env, info)?;
176
177 Ok(res_msg
178 .add_events(res_attest.events)
179 .add_attributes(res_attest.attributes))
180 }
181}
182
183impl<T> Handler for Noop<T> {
184 fn handle(
185 self,
186 _deps: DepsMut<'_>,
187 _env: &Env,
188 _info: &MessageInfo,
189 ) -> Result<Response, Error> {
190 Ok(Response::default())
191 }
192}