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
50pub enum TeeType {
51    /// AMD SEV-SNP
52    Snp,
53    /// Intel TDX
54    Tdx,
55    /// Virtualization-based Security (VBS)
56    Vbs,
57}
58
59/// The result of the `get_attestation_report`.
60pub struct GetAttestationReportResult {
61    /// The report in raw bytes
62    pub report: Vec<u8>,
63    /// The optional tcb version
64    pub tcb_version: Option<u64>,
65}
66
67/// Trait that defines the get attestation report interface for TEE.
68// TODO VBS: Implement the trait for VBS
69pub trait TeeCall: Send + Sync {
70    /// Get the hardware-backed attestation report.
71    fn get_attestation_report(
72        &self,
73        report_data: &[u8; REPORT_DATA_SIZE],
74    ) -> Result<GetAttestationReportResult, Error>;
75    /// Whether [`TeeCallGetDerivedKey`] is implemented.
76    fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey>;
77    /// Get the [`TeeType`].
78    fn tee_type(&self) -> TeeType;
79}
80
81/// Optional sub-trait that defines get derived key interface for TEE.
82pub trait TeeCallGetDerivedKey: TeeCall {
83    /// Get the derived key that should be deterministic based on the hardware and software
84    /// configurations.
85    fn get_derived_key(&self, tcb_version: u64) -> Result<[u8; HW_DERIVED_KEY_LENGTH], Error>;
86}
87
88/// Implementation of [`TeeCall`] for SNP
89pub struct SnpCall;
90
91impl TeeCall for SnpCall {
92    /// Get the attestation report from /dev/sev-guest.
93    fn get_attestation_report(
94        &self,
95        report_data: &[u8; REPORT_DATA_SIZE],
96    ) -> Result<GetAttestationReportResult, Error> {
97        let dev = sev_guest_device::SevGuestDevice::open().map_err(Error::OpenDevSevGuest)?;
98        let report = dev
99            .get_report(*report_data, 0)
100            .map_err(Error::GetSnpReport)?;
101
102        Ok(GetAttestationReportResult {
103            report: report.as_bytes().to_vec(),
104            tcb_version: Some(report.reported_tcb),
105        })
106    }
107
108    /// Key derivation is supported by SNP
109    fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
110        Some(self)
111    }
112
113    /// Return TeeType::Snp.
114    fn tee_type(&self) -> TeeType {
115        TeeType::Snp
116    }
117}
118
119impl TeeCallGetDerivedKey for SnpCall {
120    /// Get the derived key from /dev/sev-guest.
121    fn get_derived_key(&self, tcb_version: u64) -> Result<[u8; HW_DERIVED_KEY_LENGTH], Error> {
122        let dev = sev_guest_device::SevGuestDevice::open().map_err(Error::OpenDevSevGuest)?;
123
124        // Derive a key mixing in following data:
125        // - GuestPolicy (do not allow different polices to derive same secret)
126        // - Measurement (will not work across release)
127        // - TcbVersion (do not derive same key on older TCB that might have a bug)
128        let guest_field_select = x86defs::snp::GuestFieldSelect::default()
129            .with_guest_policy(true)
130            .with_measurement(true)
131            .with_tcb_version(true);
132
133        let derived_key = dev
134            .get_derived_key(
135                0, // VECK
136                guest_field_select.into(),
137                0, // VMPL 0
138                0, // default guest svn to 0
139                tcb_version,
140            )
141            .map_err(Error::GetSnpDerivedKey)?;
142
143        if derived_key.iter().all(|&x| x == 0) {
144            Err(Error::AllZeroKey)?
145        }
146
147        Ok(derived_key)
148    }
149}
150
151/// Implementation of [`TeeCall`] for TDX
152pub struct TdxCall;
153
154impl TeeCall for TdxCall {
155    fn get_attestation_report(
156        &self,
157        report_data: &[u8; REPORT_DATA_SIZE],
158    ) -> Result<GetAttestationReportResult, Error> {
159        let dev = tdx_guest_device::TdxGuestDevice::open().map_err(Error::OpenDevTdxGuest)?;
160        let report = dev
161            .get_report(*report_data, 0)
162            .map_err(Error::GetTdxReport)?;
163
164        Ok(GetAttestationReportResult {
165            report: report.as_bytes().to_vec(),
166            // Only needed by key derivation, return None for now
167            tcb_version: None,
168        })
169    }
170
171    /// Key derivation is currently not supported by TDX
172    fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
173        None
174    }
175
176    /// Return TeeType::Tdx.
177    fn tee_type(&self) -> TeeType {
178        TeeType::Tdx
179    }
180}
181
182/// Implementation of [`TeeCall`] for VBS
183pub struct VbsCall;
184
185impl TeeCall for VbsCall {
186    fn get_attestation_report(
187        &self,
188        report_data: &[u8; REPORT_DATA_SIZE],
189    ) -> Result<GetAttestationReportResult, Error> {
190        let mshv_hvcall = MshvHvcall::new().map_err(Error::OpenDevVbsGuest)?;
191        mshv_hvcall.set_allowed_hypercalls(&[HypercallCode::HvCallVbsVmCallReport]);
192        let report = mshv_hvcall
193            .vbs_vm_call_report(report_data)
194            .map_err(Error::GetVbsReport)?;
195
196        Ok(GetAttestationReportResult {
197            report: report[..hvdef::vbs::VBS_REPORT_SIZE].to_vec(),
198            // Only needed by key derivation, return None for now
199            tcb_version: None,
200        })
201    }
202
203    /// Key derivation is currently not supported by VBS
204    fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
205        None
206    }
207
208    /// Return TeeType::Vbs.
209    fn tee_type(&self) -> TeeType {
210        TeeType::Vbs
211    }
212}