Skip to main content

quartz_contract_core/msg/execute/
attested.rs

1use std::{convert::Into, default::Default};
2
3use cosmwasm_schema::cw_serde;
4use cosmwasm_std::{HexBinary, StdError};
5use quartz_tee_ra::intel_sgx::dcap::{Collateral, Quote3, Quote3Error};
6use serde::Serialize;
7
8/// Alias for an owned DCAP quote. This is the main part of a DCAP attestation generated by an
9/// enclave that we want to verify on-chain.
10pub type Quote = Quote3<Vec<u8>>;
11
12#[cfg(not(feature = "mock-sgx"))]
13pub type DefaultAttestation = DcapAttestation;
14#[cfg(not(feature = "mock-sgx"))]
15pub type RawDefaultAttestation = RawDcapAttestation;
16
17#[cfg(feature = "mock-sgx")]
18pub type DefaultAttestation = MockAttestation;
19#[cfg(feature = "mock-sgx")]
20pub type RawDefaultAttestation = RawMockAttestation;
21
22use crate::{
23    msg::HasDomainType,
24    state::{MrEnclave, UserData},
25};
26
27/// A wrapper struct for holding a message and it's attestation.
28#[derive(Clone, Debug, PartialEq)]
29pub struct Attested<M, A> {
30    pub msg: M,
31    pub attestation: A,
32}
33
34impl<M, A> Attested<M, A> {
35    pub fn new(msg: M, attestation: A) -> Self {
36        Self { msg, attestation }
37    }
38
39    pub fn into_tuple(self) -> (M, A) {
40        let Attested { msg, attestation } = self;
41        (msg, attestation)
42    }
43
44    pub fn msg(&self) -> &M {
45        &self.msg
46    }
47
48    pub fn attestation(&self) -> &A {
49        &self.attestation
50    }
51}
52
53#[cw_serde]
54pub struct RawAttested<RM, RA> {
55    pub msg: RM,
56    pub attestation: RA,
57}
58
59impl<RM, RA> TryFrom<RawAttested<RM, RA>> for Attested<RM::DomainType, RA::DomainType>
60where
61    RM: HasDomainType,
62    RA: HasDomainType,
63{
64    type Error = StdError;
65
66    fn try_from(value: RawAttested<RM, RA>) -> Result<Self, Self::Error> {
67        Ok(Self {
68            msg: value.msg.try_into()?,
69            attestation: value.attestation.try_into()?,
70        })
71    }
72}
73
74impl<RM, RA> From<Attested<RM::DomainType, RA::DomainType>> for RawAttested<RM, RA>
75where
76    RM: HasDomainType,
77    RA: HasDomainType,
78{
79    fn from(value: Attested<RM::DomainType, RA::DomainType>) -> Self {
80        Self {
81            msg: value.msg.into(),
82            attestation: value.attestation.into(),
83        }
84    }
85}
86
87impl<RM, RA> HasDomainType for RawAttested<RM, RA>
88where
89    RM: HasDomainType,
90    RA: HasDomainType,
91{
92    type DomainType = Attested<RM::DomainType, RA::DomainType>;
93}
94
95/// A trait that defines how to extract user data from a given type.
96pub trait HasUserData {
97    fn user_data(&self) -> UserData;
98}
99
100pub fn user_data_json<T: Serialize>(value: &T) -> UserData {
101    use serde_json::to_string;
102    use sha2::{Digest, Sha256};
103
104    let mut hasher = Sha256::new();
105    hasher.update(to_string(value).expect("infallible serializer"));
106    let digest: [u8; 32] = hasher.finalize().into();
107
108    let mut user_data = [0u8; 64];
109    user_data[0..32].copy_from_slice(&digest);
110    user_data
111}
112
113pub trait Attestation {
114    fn mr_enclave(&self) -> MrEnclave;
115}
116
117/// A verifiable DCAP attestation generated by an enclave.
118#[derive(Clone, Debug, PartialEq, Serialize)]
119pub struct DcapAttestation {
120    quote: Quote,
121    collateral: Collateral,
122}
123
124impl DcapAttestation {
125    pub fn new(quote: Quote, collateral: Collateral) -> Self {
126        Self { quote, collateral }
127    }
128
129    pub fn into_tuple(self) -> (Quote, Collateral) {
130        (self.quote, self.collateral)
131    }
132}
133
134#[cw_serde]
135pub struct RawDcapAttestation {
136    pub quote: HexBinary,
137    pub collateral: HexBinary,
138}
139
140impl TryFrom<RawDcapAttestation> for DcapAttestation {
141    type Error = StdError;
142
143    fn try_from(value: RawDcapAttestation) -> Result<Self, Self::Error> {
144        let quote_bytes: Vec<u8> = value.quote.into();
145        let collateral_bytes: Vec<u8> = value.collateral.into();
146        let quote = quote_bytes
147            .try_into()
148            .map_err(|e: Quote3Error| StdError::parse_err("Quote", e.to_string()))?;
149        let collateral = ciborium::from_reader(collateral_bytes.as_slice())
150            .map_err(|e| StdError::parse_err("Collateral", e.to_string()))?;
151
152        Ok(Self { quote, collateral })
153    }
154}
155
156impl From<DcapAttestation> for RawDcapAttestation {
157    fn from(value: DcapAttestation) -> Self {
158        let mut collateral_serialized = Vec::new();
159        ciborium::into_writer(&value.collateral, &mut collateral_serialized)
160            .expect("infallible serializer");
161
162        Self {
163            quote: value.quote.as_ref().to_vec().into(),
164            collateral: collateral_serialized.into(),
165        }
166    }
167}
168
169impl HasDomainType for RawDcapAttestation {
170    type DomainType = DcapAttestation;
171}
172
173impl HasUserData for DcapAttestation {
174    fn user_data(&self) -> UserData {
175        let report_data = self.quote.app_report_body().report_data();
176        let report_data_slice: &[u8] = report_data.as_ref();
177        report_data_slice
178            .to_owned()
179            .try_into()
180            .expect("fixed size array")
181    }
182}
183
184impl Attestation for DcapAttestation {
185    fn mr_enclave(&self) -> MrEnclave {
186        let mr_enclave = self.quote.app_report_body().mr_enclave();
187        let mr_enclave_slice: &[u8] = mr_enclave.as_ref();
188        mr_enclave_slice
189            .to_owned()
190            .try_into()
191            .expect("fixed size array")
192    }
193}
194
195#[derive(Clone, Debug, PartialEq)]
196pub struct MockAttestation(pub UserData);
197
198impl Default for MockAttestation {
199    fn default() -> Self {
200        Self([0u8; 64])
201    }
202}
203
204#[cw_serde]
205pub struct RawMockAttestation(pub HexBinary);
206
207impl TryFrom<RawMockAttestation> for MockAttestation {
208    type Error = StdError;
209
210    fn try_from(value: RawMockAttestation) -> Result<Self, Self::Error> {
211        Ok(Self(value.0.to_array()?))
212    }
213}
214
215impl From<MockAttestation> for RawMockAttestation {
216    fn from(value: MockAttestation) -> Self {
217        Self(value.0.into())
218    }
219}
220
221impl HasDomainType for RawMockAttestation {
222    type DomainType = MockAttestation;
223}
224
225impl HasUserData for MockAttestation {
226    fn user_data(&self) -> UserData {
227        self.0
228    }
229}
230
231impl Attestation for MockAttestation {
232    fn mr_enclave(&self) -> MrEnclave {
233        Default::default()
234    }
235}
236
237#[derive(Clone, Debug, PartialEq)]
238pub struct Noop<T>(pub T);
239
240#[cw_serde]
241pub struct RawNoop<T>(pub T);
242
243impl<T: Serialize> HasDomainType for RawNoop<T> {
244    type DomainType = Noop<T>;
245}
246
247impl<T> HasUserData for Noop<T>
248where
249    T: HasUserData,
250{
251    fn user_data(&self) -> UserData {
252        self.0.user_data()
253    }
254}
255
256impl<T> TryFrom<RawNoop<T>> for Noop<T> {
257    type Error = StdError;
258
259    fn try_from(value: RawNoop<T>) -> Result<Self, Self::Error> {
260        Ok(Self(value.0))
261    }
262}
263
264impl<T> From<Noop<T>> for RawNoop<T> {
265    fn from(value: Noop<T>) -> Self {
266        Self(value.0)
267    }
268}