1#![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)] #[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
36pub const HW_DERIVED_KEY_LENGTH: usize = x86defs::snp::SNP_DERIVED_KEY_SIZE;
38
39pub const REPORT_DATA_SIZE: usize = x86defs::snp::SNP_REPORT_DATA_SIZE;
42
43static_assertions::const_assert_eq!(
45 x86defs::snp::SNP_REPORT_DATA_SIZE,
46 x86defs::tdx::TDX_REPORT_DATA_SIZE
47);
48
49pub enum TeeType {
51 Snp,
53 Tdx,
55 Vbs,
57}
58
59pub struct GetAttestationReportResult {
61 pub report: Vec<u8>,
63 pub tcb_version: Option<u64>,
65}
66
67pub trait TeeCall: Send + Sync {
70 fn get_attestation_report(
72 &self,
73 report_data: &[u8; REPORT_DATA_SIZE],
74 ) -> Result<GetAttestationReportResult, Error>;
75 fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey>;
77 fn tee_type(&self) -> TeeType;
79}
80
81pub trait TeeCallGetDerivedKey: TeeCall {
83 fn get_derived_key(&self, tcb_version: u64) -> Result<[u8; HW_DERIVED_KEY_LENGTH], Error>;
86}
87
88pub struct SnpCall;
90
91impl TeeCall for SnpCall {
92 fn get_attestation_report(
94 &self,
95 report_data: &[u8; REPORT_DATA_SIZE],
96 ) -> Result<GetAttestationReportResult, Error> {
97 let dev = sev_guest_device::SevGuestDevice::open().map_err(Error::OpenDevSevGuest)?;
98 let report = dev
99 .get_report(*report_data, 0)
100 .map_err(Error::GetSnpReport)?;
101
102 Ok(GetAttestationReportResult {
103 report: report.as_bytes().to_vec(),
104 tcb_version: Some(report.reported_tcb),
105 })
106 }
107
108 fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
110 Some(self)
111 }
112
113 fn tee_type(&self) -> TeeType {
115 TeeType::Snp
116 }
117}
118
119impl TeeCallGetDerivedKey for SnpCall {
120 fn get_derived_key(&self, tcb_version: u64) -> Result<[u8; HW_DERIVED_KEY_LENGTH], Error> {
122 let dev = sev_guest_device::SevGuestDevice::open().map_err(Error::OpenDevSevGuest)?;
123
124 let guest_field_select = x86defs::snp::GuestFieldSelect::default()
129 .with_guest_policy(true)
130 .with_measurement(true)
131 .with_tcb_version(true);
132
133 let derived_key = dev
134 .get_derived_key(
135 0, guest_field_select.into(),
137 0, 0, tcb_version,
140 )
141 .map_err(Error::GetSnpDerivedKey)?;
142
143 if derived_key.iter().all(|&x| x == 0) {
144 Err(Error::AllZeroKey)?
145 }
146
147 Ok(derived_key)
148 }
149}
150
151pub struct TdxCall;
153
154impl TeeCall for TdxCall {
155 fn get_attestation_report(
156 &self,
157 report_data: &[u8; REPORT_DATA_SIZE],
158 ) -> Result<GetAttestationReportResult, Error> {
159 let dev = tdx_guest_device::TdxGuestDevice::open().map_err(Error::OpenDevTdxGuest)?;
160 let report = dev
161 .get_report(*report_data, 0)
162 .map_err(Error::GetTdxReport)?;
163
164 Ok(GetAttestationReportResult {
165 report: report.as_bytes().to_vec(),
166 tcb_version: None,
168 })
169 }
170
171 fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
173 None
174 }
175
176 fn tee_type(&self) -> TeeType {
178 TeeType::Tdx
179 }
180}
181
182pub struct VbsCall;
184
185impl TeeCall for VbsCall {
186 fn get_attestation_report(
187 &self,
188 report_data: &[u8; REPORT_DATA_SIZE],
189 ) -> Result<GetAttestationReportResult, Error> {
190 let mshv_hvcall = MshvHvcall::new().map_err(Error::OpenDevVbsGuest)?;
191 mshv_hvcall.set_allowed_hypercalls(&[HypercallCode::HvCallVbsVmCallReport]);
192 let report = mshv_hvcall
193 .vbs_vm_call_report(report_data)
194 .map_err(Error::GetVbsReport)?;
195
196 Ok(GetAttestationReportResult {
197 report: report[..hvdef::vbs::VBS_REPORT_SIZE].to_vec(),
198 tcb_version: None,
200 })
201 }
202
203 fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
205 None
206 }
207
208 fn tee_type(&self) -> TeeType {
210 TeeType::Vbs
211 }
212}