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