use core::fmt;
#[cfg(target_arch = "x86_64")]
mod x86_64;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Detection {
Detected,
NotDetected,
Unknown,
}
impl Detection {
#[must_use]
pub fn is_detected(self) -> bool {
matches!(self, Self::Detected)
}
}
impl fmt::Display for Detection {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::Detected => "detected",
Self::NotDetected => "not detected",
Self::Unknown => "unknown",
};
f.write_str(s)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct TeeCapabilities {
pub sgx: Detection,
pub tdx: Detection,
pub sev: Detection,
pub sev_snp: Detection,
pub trustzone: Detection,
pub secure_enclave: Detection,
pub nitro: Detection,
}
impl TeeCapabilities {
#[must_use]
pub fn any_detected(self) -> bool {
self.sgx.is_detected()
|| self.tdx.is_detected()
|| self.sev.is_detected()
|| self.sev_snp.is_detected()
|| self.trustzone.is_detected()
|| self.secure_enclave.is_detected()
|| self.nitro.is_detected()
}
}
impl fmt::Display for TeeCapabilities {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"TeeCapabilities {{ sgx: {}, tdx: {}, sev: {}, sev_snp: {}, trustzone: {}, secure_enclave: {}, nitro: {} }}",
self.sgx,
self.tdx,
self.sev,
self.sev_snp,
self.trustzone,
self.secure_enclave,
self.nitro,
)
}
}
#[must_use]
pub fn detect_tee_capabilities() -> TeeCapabilities {
let (sgx, tdx, sev, sev_snp) = detect_x86_64();
TeeCapabilities {
sgx,
tdx,
sev,
sev_snp,
trustzone: detect_trustzone(),
secure_enclave: detect_secure_enclave(),
nitro: detect_nitro(),
}
}
#[cfg(target_arch = "x86_64")]
fn detect_x86_64() -> (Detection, Detection, Detection, Detection) {
self::x86_64::detect()
}
#[cfg(not(target_arch = "x86_64"))]
fn detect_x86_64() -> (Detection, Detection, Detection, Detection) {
(
Detection::Unknown,
Detection::Unknown,
Detection::Unknown,
Detection::Unknown,
)
}
#[cfg(target_arch = "aarch64")]
fn detect_trustzone() -> Detection {
Detection::Unknown
}
#[cfg(not(target_arch = "aarch64"))]
fn detect_trustzone() -> Detection {
Detection::Unknown
}
#[cfg(all(target_os = "macos", target_arch = "aarch64"))]
fn detect_secure_enclave() -> Detection {
Detection::Detected
}
#[cfg(all(target_os = "macos", not(target_arch = "aarch64")))]
fn detect_secure_enclave() -> Detection {
Detection::NotDetected
}
#[cfg(not(target_os = "macos"))]
fn detect_secure_enclave() -> Detection {
Detection::Unknown
}
#[cfg(target_os = "linux")]
fn detect_nitro() -> Detection {
match std::fs::read_to_string("/sys/devices/virtual/dmi/id/sys_vendor") {
Ok(vendor) => {
if vendor.trim().eq_ignore_ascii_case("Amazon EC2") {
Detection::Detected
} else {
Detection::NotDetected
}
}
Err(_) => Detection::Unknown,
}
}
#[cfg(not(target_os = "linux"))]
fn detect_nitro() -> Detection {
Detection::Unknown
}
#[cfg(test)]
mod tests {
use super::*;
use alloc::format;
#[test]
fn detection_renders_human_readable() {
assert_eq!(format!("{}", Detection::Detected), "detected");
assert_eq!(format!("{}", Detection::NotDetected), "not detected");
assert_eq!(format!("{}", Detection::Unknown), "unknown");
}
#[test]
fn detection_is_detected_predicate() {
assert!(Detection::Detected.is_detected());
assert!(!Detection::NotDetected.is_detected());
assert!(!Detection::Unknown.is_detected());
}
#[test]
fn detect_tee_capabilities_does_not_panic() {
let caps = detect_tee_capabilities();
let _ = format!("{caps}");
let _ = caps.any_detected();
}
#[cfg(not(target_arch = "x86_64"))]
#[test]
fn non_x86_64_reports_unknown_for_intel_amd() {
let caps = detect_tee_capabilities();
assert_eq!(caps.sgx, Detection::Unknown);
assert_eq!(caps.tdx, Detection::Unknown);
assert_eq!(caps.sev, Detection::Unknown);
assert_eq!(caps.sev_snp, Detection::Unknown);
}
}