oasis_core_runtime/common/sgx/pcs/
report.rs

1//! TDX structures.
2use std::convert::TryInto;
3
4use byteorder::{ByteOrder, LittleEndian};
5pub use sgx_isa::Report as SgxReport;
6use tiny_keccak::{Hasher, TupleHash};
7
8use super::{constants::*, utils::*, Error};
9use crate::common::sgx::{EnclaveIdentity, MrEnclave};
10
11/// TDX TD report.
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub struct TdReport {
14    /// Describes the TCB of TDX.
15    pub tee_tcb_svn: [u8; 16],
16    /// Measurement of the TDX Module.
17    pub mr_seam: [u8; 48],
18    /// Signer of the TDX Module (zero for Intel).
19    pub mr_signer_seam: [u8; 48],
20    /// TDX Module attributes (must be zero for TDX 1.0).
21    pub seam_attributes: [u8; 8],
22    /// TD attributes.
23    pub td_attributes: TdAttributes,
24    /// XFAM (eXtended Features Available Mask).
25    pub xfam: [u8; 8],
26    /// Measurement of the initial contents of the TD.
27    pub mr_td: [u8; 48],
28    /// Software-defined ID for non-owner-defined configuration of the TD, e.g., runtime or OS
29    /// configuration.
30    pub mr_config_id: [u8; 48],
31    /// Software-defined ID for the TD’s owner.
32    pub mr_owner: [u8; 48],
33    /// Software-defined ID for owner-defined configuration of the TD, e.g., specific to the
34    /// workload rather than the runtime or OS.
35    pub mr_owner_config: [u8; 48],
36    /// Runtime extendable measurement register 0.
37    pub rtmr0: [u8; 48],
38    /// Runtime extendable measurement register 1.
39    pub rtmr1: [u8; 48],
40    /// Runtime extendable measurement register 2.
41    pub rtmr2: [u8; 48],
42    /// Runtime extendable measurement register 3.
43    pub rtmr3: [u8; 48],
44    /// Custom report data.
45    pub report_data: [u8; 64],
46}
47
48impl TdReport {
49    /// Parse a TDX report.
50    pub fn parse(mut data: &[u8]) -> Result<Self, Error> {
51        if data.len() != TDX_REPORT_BODY_LEN {
52            return Err(Error::MalformedReport);
53        }
54
55        let report = Self {
56            tee_tcb_svn: data
57                .take_prefix(16)
58                .map_err(|_| Error::MalformedReport)?
59                .try_into()
60                .unwrap(),
61            mr_seam: data
62                .take_prefix(48)
63                .map_err(|_| Error::MalformedReport)?
64                .try_into()
65                .unwrap(),
66            mr_signer_seam: data
67                .take_prefix(48)
68                .map_err(|_| Error::MalformedReport)?
69                .try_into()
70                .unwrap(),
71            seam_attributes: data
72                .take_prefix(8)
73                .map_err(|_| Error::MalformedReport)?
74                .try_into()
75                .unwrap(),
76            td_attributes: TdAttributes::parse(
77                data.take_prefix(8).map_err(|_| Error::MalformedReport)?,
78            )?,
79            xfam: data
80                .take_prefix(8)
81                .map_err(|_| Error::MalformedReport)?
82                .try_into()
83                .unwrap(),
84            mr_td: data
85                .take_prefix(48)
86                .map_err(|_| Error::MalformedReport)?
87                .try_into()
88                .unwrap(),
89            mr_config_id: data
90                .take_prefix(48)
91                .map_err(|_| Error::MalformedReport)?
92                .try_into()
93                .unwrap(),
94            mr_owner: data
95                .take_prefix(48)
96                .map_err(|_| Error::MalformedReport)?
97                .try_into()
98                .unwrap(),
99            mr_owner_config: data
100                .take_prefix(48)
101                .map_err(|_| Error::MalformedReport)?
102                .try_into()
103                .unwrap(),
104            rtmr0: data
105                .take_prefix(48)
106                .map_err(|_| Error::MalformedReport)?
107                .try_into()
108                .unwrap(),
109            rtmr1: data
110                .take_prefix(48)
111                .map_err(|_| Error::MalformedReport)?
112                .try_into()
113                .unwrap(),
114            rtmr2: data
115                .take_prefix(48)
116                .map_err(|_| Error::MalformedReport)?
117                .try_into()
118                .unwrap(),
119            rtmr3: data
120                .take_prefix(48)
121                .map_err(|_| Error::MalformedReport)?
122                .try_into()
123                .unwrap(),
124            report_data: data
125                .take_prefix(64)
126                .map_err(|_| Error::MalformedReport)?
127                .try_into()
128                .unwrap(),
129        };
130
131        // SEAM attributes must be zero for TDX 1.0.
132        if report.seam_attributes != [0; 8] {
133            return Err(Error::MalformedReport);
134        }
135
136        Ok(report)
137    }
138
139    /// Converts this report into an enclave identity.
140    pub fn as_enclave_identity(&self) -> EnclaveIdentity {
141        td_enclave_identity(
142            &self.mr_td,
143            &self.rtmr0,
144            &self.rtmr1,
145            &self.rtmr2,
146            &self.rtmr3,
147        )
148    }
149}
150
151/// Compute enclave identity from the given measurements.
152pub fn td_enclave_identity(
153    mr_td: &[u8; 48],
154    rtmr0: &[u8; 48],
155    rtmr1: &[u8; 48],
156    rtmr2: &[u8; 48],
157    rtmr3: &[u8; 48],
158) -> EnclaveIdentity {
159    // TODO: Change the EnclaveIdentity structure to allow specifying all the different things.
160
161    // Compute MRENCLAVE as TupleHash[TD_ENCLAVE_IDENTITY_CONTEXT](MRTD, RTMR0, RTMR1, RTMR2, RTMR3).
162    //
163    // MRTD  -- Measurement of virtual firmware.
164    // RTMR0 -- Measurement of virtual firmware data and configuration.
165    // RTMR1 -- Measurement of OS loader, option ROM, boot parameters.
166    // RTMR2 -- Measurement of OS kernel, initrd, boot parameters.
167    // RTMR3 -- Reserved.
168    //
169    let mut mr_enclave = MrEnclave::default();
170    let mut h = TupleHash::v256(TD_ENCLAVE_IDENTITY_CONTEXT);
171    h.update(mr_td);
172    h.update(rtmr0);
173    h.update(rtmr1);
174    h.update(rtmr2);
175    h.update(rtmr3);
176    h.finalize(&mut mr_enclave.0);
177
178    EnclaveIdentity {
179        mr_signer: Default::default(), // All-zero MRSIGNER (invalid in SGX).
180        mr_enclave,
181    }
182}
183
184/// TD enclave identity conversion context.
185pub const TD_ENCLAVE_IDENTITY_CONTEXT: &[u8] = b"oasis-core/tdx: TD enclave identity";
186
187bitflags::bitflags! {
188    /// TDX TD attributes.
189    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
190    pub struct TdAttributes: u64 {
191        /// TUD.DEBUG (TD runs in debug mode).
192        const DEBUG = 0b00000000_00000000_00000000_00000000_00000000_00000000_00000000_00000001;
193        // TUD bits 7:1 reserved for future use and must be zero.
194
195        // SEC bits 27:8 reserved for future use and must be zero.
196        /// SEC.SEPT_VE_DISABLE (Disable EPT violation conversion to #VE on TD access of PENDING pages).
197        const SEPT_VE_DISABLE = 0b00000000_00000000_00000000_00000000_00010000_00000000_00000000_00000000;
198        // SEC bit 28 reserved for future use and must be zero.
199        /// SEC.PKS (TD is allowed to use Supervisor Protection Keys).
200        const PKS = 0b00000000_00000000_00000000_00000000_01000000_00000000_00000000_00000000;
201        /// SEC.KL (TD is allowed to use Key Locker).
202        const KL = 0b00000000_00000000_00000000_00000000_10000000_00000000_00000000_00000000;
203
204        // OTHER bits 62:32 reserved for future use and must be zero.
205        /// OTHER.PERFMON (TD is allowed to use Perfmon and PERF_METRICS capabilities).
206        const PERFMON = 0b10000000_00000000_00000000_00000000_00000000_00000000_00000000_00000000;
207    }
208}
209
210impl TdAttributes {
211    /// Parse raw TDX attributes.
212    pub fn parse(data: &[u8]) -> Result<Self, Error> {
213        if data.len() != 8 {
214            return Err(Error::MalformedReport);
215        }
216
217        let attrs = LittleEndian::read_u64(data);
218
219        Self::from_bits(attrs).ok_or(Error::MalformedReport)
220    }
221}
222
223#[cfg(test)]
224mod test {
225    use super::*;
226
227    #[test]
228    fn test_td_attributes() {
229        let attrs = TdAttributes::DEBUG | TdAttributes::SEPT_VE_DISABLE | TdAttributes::PKS;
230        assert!(attrs.contains(TdAttributes::DEBUG));
231        assert!(attrs.contains(TdAttributes::SEPT_VE_DISABLE));
232        assert!(attrs.contains(TdAttributes::PKS));
233        assert!(attrs.contains(TdAttributes::DEBUG | TdAttributes::SEPT_VE_DISABLE));
234        assert!(!attrs.contains(TdAttributes::KL));
235        assert!(!attrs.contains(TdAttributes::DEBUG | TdAttributes::KL));
236
237        let reserved = vec![0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff];
238        let result = TdAttributes::parse(&reserved);
239        assert!(matches!(result, Err(Error::MalformedReport)));
240
241        let reserved = vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01];
242        let result = TdAttributes::parse(&reserved);
243        assert!(matches!(result, Err(Error::MalformedReport)));
244    }
245}