1#![cfg(target_os = "linux")]
8#![forbid(unsafe_code)]
9
10use thiserror::Error;
11use zerocopy::IntoBytes;
12
13#[expect(missing_docs)] #[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
30pub const HW_DERIVED_KEY_LENGTH: usize = x86defs::snp::SNP_DERIVED_KEY_SIZE;
32
33pub const REPORT_DATA_SIZE: usize = x86defs::snp::SNP_REPORT_DATA_SIZE;
36
37static_assertions::const_assert_eq!(
39 x86defs::snp::SNP_REPORT_DATA_SIZE,
40 x86defs::tdx::TDX_REPORT_DATA_SIZE
41);
42
43pub enum TeeType {
45 Snp,
47 Tdx,
49}
50
51pub struct GetAttestationReportResult {
53 pub report: Vec<u8>,
55 pub tcb_version: Option<u64>,
57}
58
59pub trait TeeCall: Send + Sync {
62 fn get_attestation_report(
64 &self,
65 report_data: &[u8; REPORT_DATA_SIZE],
66 ) -> Result<GetAttestationReportResult, Error>;
67 fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey>;
69 fn tee_type(&self) -> TeeType;
71}
72
73pub trait TeeCallGetDerivedKey: TeeCall {
75 fn get_derived_key(&self, tcb_version: u64) -> Result<[u8; HW_DERIVED_KEY_LENGTH], Error>;
78}
79
80pub struct SnpCall;
82
83impl TeeCall for SnpCall {
84 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 fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
102 Some(self)
103 }
104
105 fn tee_type(&self) -> TeeType {
107 TeeType::Snp
108 }
109}
110
111impl TeeCallGetDerivedKey for SnpCall {
112 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 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, guest_field_select.into(),
129 0, 0, 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
143pub 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 tcb_version: None,
160 })
161 }
162
163 fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
165 None
166 }
167
168 fn tee_type(&self) -> TeeType {
170 TeeType::Tdx
171 }
172}