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
49#[derive(Debug)]
51pub enum TeeType {
52 Snp,
54 Tdx,
56 Vbs,
58}
59
60pub struct GetAttestationReportResult {
62 pub report: Vec<u8>,
64 pub tcb_version: Option<u64>,
66}
67
68pub trait TeeCall: Send + Sync {
71 fn get_attestation_report(
73 &self,
74 report_data: &[u8; REPORT_DATA_SIZE],
75 ) -> Result<GetAttestationReportResult, Error>;
76 fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey>;
78 fn tee_type(&self) -> TeeType;
80}
81
82pub trait TeeCallGetDerivedKey: TeeCall {
84 fn get_derived_key(&self, tcb_version: u64) -> Result<[u8; HW_DERIVED_KEY_LENGTH], Error>;
87}
88
89pub struct SnpCall;
91
92impl TeeCall for SnpCall {
93 fn get_attestation_report(
95 &self,
96 report_data: &[u8; REPORT_DATA_SIZE],
97 ) -> Result<GetAttestationReportResult, Error> {
98 let dev = sev_guest_device::SevGuestDevice::open().map_err(Error::OpenDevSevGuest)?;
99 let report = dev
100 .get_report(*report_data, 0)
101 .map_err(Error::GetSnpReport)?;
102
103 Ok(GetAttestationReportResult {
104 report: report.as_bytes().to_vec(),
105 tcb_version: Some(report.reported_tcb),
106 })
107 }
108
109 fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
111 Some(self)
112 }
113
114 fn tee_type(&self) -> TeeType {
116 TeeType::Snp
117 }
118}
119
120impl TeeCallGetDerivedKey for SnpCall {
121 fn get_derived_key(&self, tcb_version: u64) -> Result<[u8; HW_DERIVED_KEY_LENGTH], Error> {
123 let dev = sev_guest_device::SevGuestDevice::open().map_err(Error::OpenDevSevGuest)?;
124
125 let guest_field_select = x86defs::snp::GuestFieldSelect::default()
130 .with_guest_policy(true)
131 .with_measurement(true)
132 .with_tcb_version(true);
133
134 let derived_key = dev
135 .get_derived_key(
136 0, guest_field_select.into(),
138 0, 0, tcb_version,
141 )
142 .map_err(Error::GetSnpDerivedKey)?;
143
144 if derived_key.iter().all(|&x| x == 0) {
145 Err(Error::AllZeroKey)?
146 }
147
148 Ok(derived_key)
149 }
150}
151
152pub struct TdxCall;
154
155impl TeeCall for TdxCall {
156 fn get_attestation_report(
157 &self,
158 report_data: &[u8; REPORT_DATA_SIZE],
159 ) -> Result<GetAttestationReportResult, Error> {
160 let dev = tdx_guest_device::TdxGuestDevice::open().map_err(Error::OpenDevTdxGuest)?;
161 let report = dev
162 .get_report(*report_data, 0)
163 .map_err(Error::GetTdxReport)?;
164
165 Ok(GetAttestationReportResult {
166 report: report.as_bytes().to_vec(),
167 tcb_version: None,
169 })
170 }
171
172 fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
174 None
175 }
176
177 fn tee_type(&self) -> TeeType {
179 TeeType::Tdx
180 }
181}
182
183pub struct VbsCall;
185
186impl TeeCall for VbsCall {
187 fn get_attestation_report(
188 &self,
189 report_data: &[u8; REPORT_DATA_SIZE],
190 ) -> Result<GetAttestationReportResult, Error> {
191 let mshv_hvcall = MshvHvcall::new().map_err(Error::OpenDevVbsGuest)?;
192 mshv_hvcall.set_allowed_hypercalls(&[HypercallCode::HvCallVbsVmCallReport]);
193 let report = mshv_hvcall
194 .vbs_vm_call_report(report_data)
195 .map_err(Error::GetVbsReport)?;
196
197 Ok(GetAttestationReportResult {
198 report: report[..hvdef::vbs::VBS_REPORT_SIZE].to_vec(),
199 tcb_version: None,
201 })
202 }
203
204 fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
206 None
207 }
208
209 fn tee_type(&self) -> TeeType {
211 TeeType::Vbs
212 }
213}