Skip to main content

tee_call/
lib.rs

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4//! This module includes the `TeeCall` trait and its implementation. The trait defines
5//! the trusted execution environment (TEE)-specific APIs for attestation and data dealing.
6
7#![cfg(target_os = "linux")]
8#![forbid(unsafe_code)]
9
10use hcl::ioctl::MshvHvcall;
11use hvdef::HypercallCode;
12use thiserror::Error;
13use zerocopy::IntoBytes;
14
15#[expect(missing_docs)] // self-explanatory fields
16#[derive(Debug, Error)]
17pub enum Error {
18    #[error("failed to open /dev/sev-guest")]
19    OpenDevSevGuest(#[source] sev_guest_device::Error),
20    #[error("failed to get an SNP report via /dev/sev-guest")]
21    GetSnpReport(#[source] sev_guest_device::Error),
22    #[error("failed to get an SNP derived key via /dev/sev-guest")]
23    GetSnpDerivedKey(#[source] sev_guest_device::Error),
24    #[error("got all-zeros key")]
25    AllZeroKey,
26    #[error("failed to open /dev/tdx_guest")]
27    OpenDevTdxGuest(#[source] tdx_guest_device::Error),
28    #[error("failed to get a TDX report via /dev/tdx_guest")]
29    GetTdxReport(#[source] tdx_guest_device::Error),
30    #[error("failed to open VBS guest device")]
31    OpenDevVbsGuest(#[source] hcl::ioctl::Error),
32    #[error("failed to get a VBS report via VBS guest device")]
33    GetVbsReport(#[source] hvdef::HvError),
34}
35
36/// Use the SNP-defined derived key size for now.
37pub const HW_DERIVED_KEY_LENGTH: usize = x86defs::snp::SNP_DERIVED_KEY_SIZE;
38
39/// Use the SNP-defined report data size for now.
40// DEVNOTE: This value should be upper bound among all the supported TEE types.
41pub const REPORT_DATA_SIZE: usize = x86defs::snp::SNP_REPORT_DATA_SIZE;
42
43// TDX and SNP report data size are equal so we can use either of them
44static_assertions::const_assert_eq!(
45    x86defs::snp::SNP_REPORT_DATA_SIZE,
46    x86defs::tdx::TDX_REPORT_DATA_SIZE
47);
48
49/// Type of the TEE
50#[derive(Debug)]
51pub enum TeeType {
52    /// AMD SEV-SNP
53    Snp,
54    /// Intel TDX
55    Tdx,
56    /// ARM CCA
57    Cca,
58    /// Virtualization-based Security (VBS)
59    Vbs,
60}
61
62/// The result of the `get_attestation_report`.
63pub struct GetAttestationReportResult {
64    /// The report in raw bytes
65    pub report: Vec<u8>,
66    /// The optional tcb version
67    pub tcb_version: Option<u64>,
68}
69
70/// Trait that defines the get attestation report interface for TEE.
71// TODO VBS: Implement the trait for VBS
72pub trait TeeCall: Send + Sync {
73    /// Get the hardware-backed attestation report.
74    fn get_attestation_report(
75        &self,
76        report_data: &[u8; REPORT_DATA_SIZE],
77    ) -> Result<GetAttestationReportResult, Error>;
78    /// Whether [`TeeCallGetDerivedKey`] is implemented.
79    fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey>;
80    /// Get the [`TeeType`].
81    fn tee_type(&self) -> TeeType;
82}
83
84/// Optional sub-trait that defines get derived key interface for TEE.
85pub trait TeeCallGetDerivedKey: TeeCall {
86    /// Get the derived key that should be deterministic based on the hardware and software
87    /// configurations.
88    fn get_derived_key(&self, tcb_version: u64) -> Result<[u8; HW_DERIVED_KEY_LENGTH], Error>;
89}
90
91/// Implementation of [`TeeCall`] for SNP
92pub struct SnpCall;
93
94impl TeeCall for SnpCall {
95    /// Get the attestation report from /dev/sev-guest.
96    fn get_attestation_report(
97        &self,
98        report_data: &[u8; REPORT_DATA_SIZE],
99    ) -> Result<GetAttestationReportResult, Error> {
100        let dev = sev_guest_device::SevGuestDevice::open().map_err(Error::OpenDevSevGuest)?;
101        let report = dev
102            .get_report(*report_data, 0)
103            .map_err(Error::GetSnpReport)?;
104
105        Ok(GetAttestationReportResult {
106            report: report.as_bytes().to_vec(),
107            tcb_version: Some(report.reported_tcb),
108        })
109    }
110
111    /// Key derivation is supported by SNP
112    fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
113        Some(self)
114    }
115
116    /// Return TeeType::Snp.
117    fn tee_type(&self) -> TeeType {
118        TeeType::Snp
119    }
120}
121
122impl TeeCallGetDerivedKey for SnpCall {
123    /// Get the derived key from /dev/sev-guest.
124    fn get_derived_key(&self, tcb_version: u64) -> Result<[u8; HW_DERIVED_KEY_LENGTH], Error> {
125        let dev = sev_guest_device::SevGuestDevice::open().map_err(Error::OpenDevSevGuest)?;
126
127        // Derive a key mixing in following data:
128        // - GuestPolicy (do not allow different polices to derive same secret)
129        // - Measurement (will not work across release)
130        // - TcbVersion (do not derive same key on older TCB that might have a bug)
131        let guest_field_select = x86defs::snp::GuestFieldSelect::default()
132            .with_guest_policy(true)
133            .with_measurement(true)
134            .with_tcb_version(true);
135
136        let derived_key = dev
137            .get_derived_key(
138                0, // VECK
139                guest_field_select.into(),
140                0, // VMPL 0
141                0, // default guest svn to 0
142                tcb_version,
143            )
144            .map_err(Error::GetSnpDerivedKey)?;
145
146        if derived_key.iter().all(|&x| x == 0) {
147            Err(Error::AllZeroKey)?
148        }
149
150        Ok(derived_key)
151    }
152}
153
154/// Implementation of [`TeeCall`] for TDX
155pub struct TdxCall;
156
157impl TeeCall for TdxCall {
158    fn get_attestation_report(
159        &self,
160        report_data: &[u8; REPORT_DATA_SIZE],
161    ) -> Result<GetAttestationReportResult, Error> {
162        let dev = tdx_guest_device::TdxGuestDevice::open().map_err(Error::OpenDevTdxGuest)?;
163        let report = dev
164            .get_report(*report_data, 0)
165            .map_err(Error::GetTdxReport)?;
166
167        Ok(GetAttestationReportResult {
168            report: report.as_bytes().to_vec(),
169            // Only needed by key derivation, return None for now
170            tcb_version: None,
171        })
172    }
173
174    /// Key derivation is currently not supported by TDX
175    fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
176        None
177    }
178
179    /// Return TeeType::Tdx.
180    fn tee_type(&self) -> TeeType {
181        TeeType::Tdx
182    }
183}
184
185/// Implementation of [`TeeCall`] for VBS
186pub struct VbsCall;
187
188impl TeeCall for VbsCall {
189    fn get_attestation_report(
190        &self,
191        report_data: &[u8; REPORT_DATA_SIZE],
192    ) -> Result<GetAttestationReportResult, Error> {
193        let mshv_hvcall = MshvHvcall::new().map_err(Error::OpenDevVbsGuest)?;
194        mshv_hvcall.set_allowed_hypercalls(&[HypercallCode::HvCallVbsVmCallReport]);
195        let report = mshv_hvcall
196            .vbs_vm_call_report(report_data)
197            .map_err(Error::GetVbsReport)?;
198
199        Ok(GetAttestationReportResult {
200            report: report[..hvdef::vbs::VBS_REPORT_SIZE].to_vec(),
201            // Only needed by key derivation, return None for now
202            tcb_version: None,
203        })
204    }
205
206    /// Key derivation is currently not supported by VBS
207    fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
208        None
209    }
210
211    /// Return TeeType::Vbs.
212    fn tee_type(&self) -> TeeType {
213        TeeType::Vbs
214    }
215}