#![cfg(target_os = "linux")]
#![forbid(unsafe_code)]
use thiserror::Error;
use zerocopy::IntoBytes;
#[expect(missing_docs)] #[derive(Debug, Error)]
pub enum Error {
#[error("failed to open /dev/sev-guest")]
OpenDevSevGuest(#[source] sev_guest_device::ioctl::Error),
#[error("failed to get an SNP report via /dev/sev-guest")]
GetSnpReport(#[source] sev_guest_device::ioctl::Error),
#[error("failed to get an SNP derived key via /dev/sev-guest")]
GetSnpDerivedKey(#[source] sev_guest_device::ioctl::Error),
#[error("got all-zeros key")]
AllZeroKey,
#[error("failed to open /dev/tdx_guest")]
OpenDevTdxGuest(#[source] tdx_guest_device::ioctl::Error),
#[error("failed to get a TDX report via /dev/tdx_guest")]
GetTdxReport(#[source] tdx_guest_device::ioctl::Error),
}
pub const HW_DERIVED_KEY_LENGTH: usize = sev_guest_device::protocol::SNP_DERIVED_KEY_SIZE;
pub const REPORT_DATA_SIZE: usize = sev_guest_device::protocol::SNP_REPORT_DATA_SIZE;
static_assertions::const_assert_eq!(
sev_guest_device::protocol::SNP_REPORT_DATA_SIZE,
tdx_guest_device::protocol::TDX_REPORT_DATA_SIZE
);
pub enum TeeType {
Snp,
Tdx,
}
pub struct GetAttestationReportResult {
pub report: Vec<u8>,
pub tcb_version: Option<u64>,
}
pub trait TeeCall: Send + Sync {
fn get_attestation_report(
&self,
report_data: &[u8; REPORT_DATA_SIZE],
) -> Result<GetAttestationReportResult, Error>;
fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey>;
fn tee_type(&self) -> TeeType;
}
pub trait TeeCallGetDerivedKey: TeeCall {
fn get_derived_key(&self, tcb_version: u64) -> Result<[u8; HW_DERIVED_KEY_LENGTH], Error>;
}
pub struct SnpCall;
impl TeeCall for SnpCall {
fn get_attestation_report(
&self,
report_data: &[u8; REPORT_DATA_SIZE],
) -> Result<GetAttestationReportResult, Error> {
let dev =
sev_guest_device::ioctl::SevGuestDevice::open().map_err(Error::OpenDevSevGuest)?;
let report = dev
.get_report(*report_data, 0)
.map_err(Error::GetSnpReport)?;
Ok(GetAttestationReportResult {
report: report.as_bytes().to_vec(),
tcb_version: Some(report.reported_tcb),
})
}
fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
Some(self)
}
fn tee_type(&self) -> TeeType {
TeeType::Snp
}
}
impl TeeCallGetDerivedKey for SnpCall {
fn get_derived_key(&self, tcb_version: u64) -> Result<[u8; HW_DERIVED_KEY_LENGTH], Error> {
let dev =
sev_guest_device::ioctl::SevGuestDevice::open().map_err(Error::OpenDevSevGuest)?;
let guest_field_select = sev_guest_device::protocol::GuestFieldSelect::default()
.with_guest_policy(true)
.with_measurement(true)
.with_tcb_version(true);
let derived_key = dev
.get_derived_key(
0, guest_field_select.into(),
0, 0, tcb_version,
)
.map_err(Error::GetSnpDerivedKey)?;
if derived_key.iter().all(|&x| x == 0) {
Err(Error::AllZeroKey)?
}
Ok(derived_key)
}
}
pub struct TdxCall;
impl TeeCall for TdxCall {
fn get_attestation_report(
&self,
report_data: &[u8; REPORT_DATA_SIZE],
) -> Result<GetAttestationReportResult, Error> {
let dev =
tdx_guest_device::ioctl::TdxGuestDevice::open().map_err(Error::OpenDevTdxGuest)?;
let report = dev
.get_report(*report_data, 0)
.map_err(Error::GetTdxReport)?;
Ok(GetAttestationReportResult {
report: report.as_bytes().to_vec(),
tcb_version: None,
})
}
fn supports_get_derived_key(&self) -> Option<&dyn TeeCallGetDerivedKey> {
None
}
fn tee_type(&self) -> TeeType {
TeeType::Tdx
}
}