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::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
30pub const HW_DERIVED_KEY_LENGTH: usize = sev_guest_device::protocol::SNP_DERIVED_KEY_SIZE;
32
33pub const REPORT_DATA_SIZE: usize = sev_guest_device::protocol::SNP_REPORT_DATA_SIZE;
36
37static_assertions::const_assert_eq!(
39 sev_guest_device::protocol::SNP_REPORT_DATA_SIZE,
40 tdx_guest_device::protocol::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 =
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 fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
103 Some(self)
104 }
105
106 fn tee_type(&self) -> TeeType {
108 TeeType::Snp
109 }
110}
111
112impl TeeCallGetDerivedKey for SnpCall {
113 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 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, guest_field_select.into(),
131 0, 0, 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
145pub 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 tcb_version: None,
163 })
164 }
165
166 fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
168 None
169 }
170
171 fn tee_type(&self) -> TeeType {
173 TeeType::Tdx
174 }
175}