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 thiserror::Error;
11use zerocopy::IntoBytes;
12
13#[expect(missing_docs)] // self-explanatory fields
14#[derive(Debug, Error)]
15pub enum Error {
16    #[error("failed to open /dev/sev-guest")]
17    OpenDevSevGuest(#[source] sev_guest_device::Error),
18    #[error("failed to get an SNP report via /dev/sev-guest")]
19    GetSnpReport(#[source] sev_guest_device::Error),
20    #[error("failed to get an SNP derived key via /dev/sev-guest")]
21    GetSnpDerivedKey(#[source] sev_guest_device::Error),
22    #[error("got all-zeros key")]
23    AllZeroKey,
24    #[error("failed to open /dev/tdx_guest")]
25    OpenDevTdxGuest(#[source] tdx_guest_device::Error),
26    #[error("failed to get a TDX report via /dev/tdx_guest")]
27    GetTdxReport(#[source] tdx_guest_device::Error),
28}
29
30/// Use the SNP-defined derived key size for now.
31pub const HW_DERIVED_KEY_LENGTH: usize = x86defs::snp::SNP_DERIVED_KEY_SIZE;
32
33/// Use the SNP-defined report data size for now.
34// DEVNOTE: This value should be upper bound among all the supported TEE types.
35pub const REPORT_DATA_SIZE: usize = x86defs::snp::SNP_REPORT_DATA_SIZE;
36
37// TDX and SNP report data size are equal so we can use either of them
38static_assertions::const_assert_eq!(
39    x86defs::snp::SNP_REPORT_DATA_SIZE,
40    x86defs::tdx::TDX_REPORT_DATA_SIZE
41);
42
43/// Type of the TEE
44pub enum TeeType {
45    /// AMD SEV-SNP
46    Snp,
47    /// Intel TDX
48    Tdx,
49}
50
51/// The result of the `get_attestation_report`.
52pub struct GetAttestationReportResult {
53    /// The report in raw bytes
54    pub report: Vec<u8>,
55    /// The optional tcb version
56    pub tcb_version: Option<u64>,
57}
58
59/// Trait that defines the get attestation report interface for TEE.
60// TODO VBS: Implement the trait for VBS
61pub trait TeeCall: Send + Sync {
62    /// Get the hardware-backed attestation report.
63    fn get_attestation_report(
64        &self,
65        report_data: &[u8; REPORT_DATA_SIZE],
66    ) -> Result<GetAttestationReportResult, Error>;
67    /// Whether [`TeeCallGetDerivedKey`] is implemented.
68    fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey>;
69    /// Get the [`TeeType`].
70    fn tee_type(&self) -> TeeType;
71}
72
73/// Optional sub-trait that defines get derived key interface for TEE.
74pub trait TeeCallGetDerivedKey: TeeCall {
75    /// Get the derived key that should be deterministic based on the hardware and software
76    /// configurations.
77    fn get_derived_key(&self, tcb_version: u64) -> Result<[u8; HW_DERIVED_KEY_LENGTH], Error>;
78}
79
80/// Implementation of [`TeeCall`] for SNP
81pub struct SnpCall;
82
83impl TeeCall for SnpCall {
84    /// Get the attestation report from /dev/sev-guest.
85    fn get_attestation_report(
86        &self,
87        report_data: &[u8; REPORT_DATA_SIZE],
88    ) -> Result<GetAttestationReportResult, Error> {
89        let dev = sev_guest_device::SevGuestDevice::open().map_err(Error::OpenDevSevGuest)?;
90        let report = dev
91            .get_report(*report_data, 0)
92            .map_err(Error::GetSnpReport)?;
93
94        Ok(GetAttestationReportResult {
95            report: report.as_bytes().to_vec(),
96            tcb_version: Some(report.reported_tcb),
97        })
98    }
99
100    /// Key derivation is supported by SNP
101    fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
102        Some(self)
103    }
104
105    /// Return TeeType::Snp.
106    fn tee_type(&self) -> TeeType {
107        TeeType::Snp
108    }
109}
110
111impl TeeCallGetDerivedKey for SnpCall {
112    /// Get the derived key from /dev/sev-guest.
113    fn get_derived_key(&self, tcb_version: u64) -> Result<[u8; HW_DERIVED_KEY_LENGTH], Error> {
114        let dev = sev_guest_device::SevGuestDevice::open().map_err(Error::OpenDevSevGuest)?;
115
116        // Derive a key mixing in following data:
117        // - GuestPolicy (do not allow different polices to derive same secret)
118        // - Measurement (will not work across release)
119        // - TcbVersion (do not derive same key on older TCB that might have a bug)
120        let guest_field_select = x86defs::snp::GuestFieldSelect::default()
121            .with_guest_policy(true)
122            .with_measurement(true)
123            .with_tcb_version(true);
124
125        let derived_key = dev
126            .get_derived_key(
127                0, // VECK
128                guest_field_select.into(),
129                0, // VMPL 0
130                0, // default guest svn to 0
131                tcb_version,
132            )
133            .map_err(Error::GetSnpDerivedKey)?;
134
135        if derived_key.iter().all(|&x| x == 0) {
136            Err(Error::AllZeroKey)?
137        }
138
139        Ok(derived_key)
140    }
141}
142
143/// Implementation of [`TeeCall`] for TDX
144pub struct TdxCall;
145
146impl TeeCall for TdxCall {
147    fn get_attestation_report(
148        &self,
149        report_data: &[u8; REPORT_DATA_SIZE],
150    ) -> Result<GetAttestationReportResult, Error> {
151        let dev = tdx_guest_device::TdxGuestDevice::open().map_err(Error::OpenDevTdxGuest)?;
152        let report = dev
153            .get_report(*report_data, 0)
154            .map_err(Error::GetTdxReport)?;
155
156        Ok(GetAttestationReportResult {
157            report: report.as_bytes().to_vec(),
158            // Only needed by key derivation, return None for now
159            tcb_version: None,
160        })
161    }
162
163    /// Key derivation is currently not supported by TDX
164    fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
165        None
166    }
167
168    /// Return TeeType::Tdx.
169    fn tee_type(&self) -> TeeType {
170        TeeType::Tdx
171    }
172}