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::ioctl::Error),
18    #[error("failed to get an SNP report via /dev/sev-guest")]
19    GetSnpReport(#[source] sev_guest_device::ioctl::Error),
20    #[error("failed to get an SNP derived key via /dev/sev-guest")]
21    GetSnpDerivedKey(#[source] sev_guest_device::ioctl::Error),
22    #[error("got all-zeros key")]
23    AllZeroKey,
24    #[error("failed to open /dev/tdx_guest")]
25    OpenDevTdxGuest(#[source] tdx_guest_device::ioctl::Error),
26    #[error("failed to get a TDX report via /dev/tdx_guest")]
27    GetTdxReport(#[source] tdx_guest_device::ioctl::Error),
28}
29
30/// Use the SNP-defined derived key size for now.
31pub const HW_DERIVED_KEY_LENGTH: usize = sev_guest_device::protocol::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 = sev_guest_device::protocol::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    sev_guest_device::protocol::SNP_REPORT_DATA_SIZE,
40    tdx_guest_device::protocol::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 =
90            sev_guest_device::ioctl::SevGuestDevice::open().map_err(Error::OpenDevSevGuest)?;
91        let report = dev
92            .get_report(*report_data, 0)
93            .map_err(Error::GetSnpReport)?;
94
95        Ok(GetAttestationReportResult {
96            report: report.as_bytes().to_vec(),
97            tcb_version: Some(report.reported_tcb),
98        })
99    }
100
101    /// Key derivation is supported by SNP
102    fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
103        Some(self)
104    }
105
106    /// Return TeeType::Snp.
107    fn tee_type(&self) -> TeeType {
108        TeeType::Snp
109    }
110}
111
112impl TeeCallGetDerivedKey for SnpCall {
113    /// Get the derived key from /dev/sev-guest.
114    fn get_derived_key(&self, tcb_version: u64) -> Result<[u8; HW_DERIVED_KEY_LENGTH], Error> {
115        let dev =
116            sev_guest_device::ioctl::SevGuestDevice::open().map_err(Error::OpenDevSevGuest)?;
117
118        // Derive a key mixing in following data:
119        // - GuestPolicy (do not allow different polices to derive same secret)
120        // - Measurement (will not work across release)
121        // - TcbVersion (do not derive same key on older TCB that might have a bug)
122        let guest_field_select = sev_guest_device::protocol::GuestFieldSelect::default()
123            .with_guest_policy(true)
124            .with_measurement(true)
125            .with_tcb_version(true);
126
127        let derived_key = dev
128            .get_derived_key(
129                0, // VECK
130                guest_field_select.into(),
131                0, // VMPL 0
132                0, // default guest svn to 0
133                tcb_version,
134            )
135            .map_err(Error::GetSnpDerivedKey)?;
136
137        if derived_key.iter().all(|&x| x == 0) {
138            Err(Error::AllZeroKey)?
139        }
140
141        Ok(derived_key)
142    }
143}
144
145/// Implementation of [`TeeCall`] for TDX
146pub struct TdxCall;
147
148impl TeeCall for TdxCall {
149    fn get_attestation_report(
150        &self,
151        report_data: &[u8; REPORT_DATA_SIZE],
152    ) -> Result<GetAttestationReportResult, Error> {
153        let dev =
154            tdx_guest_device::ioctl::TdxGuestDevice::open().map_err(Error::OpenDevTdxGuest)?;
155        let report = dev
156            .get_report(*report_data, 0)
157            .map_err(Error::GetTdxReport)?;
158
159        Ok(GetAttestationReportResult {
160            report: report.as_bytes().to_vec(),
161            // Only needed by key derivation, return None for now
162            tcb_version: None,
163        })
164    }
165
166    /// Key derivation is currently not supported by TDX
167    fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
168        None
169    }
170
171    /// Return TeeType::Tdx.
172    fn tee_type(&self) -> TeeType {
173        TeeType::Tdx
174    }
175}