use crate::{AttestationParams, EatClaimsSet};
use std::fmt;
pub mod eat;
#[cfg(any(feature = "nitro", feature = "mock"))]
pub mod nitro_doc;
#[cfg(feature = "nitro")]
pub mod nitro;
#[cfg(feature = "nitro")]
pub use nitro::NsmSession;
#[cfg(any(feature = "sev-snp", feature = "tdx"))]
pub mod tsm;
#[cfg(feature = "sev-snp")]
pub mod sev_snp;
#[cfg(feature = "tdx")]
pub mod tdx;
#[cfg(feature = "mock")]
pub mod mock;
#[cfg(feature = "mock")]
pub use mock::MockSession;
pub const PROVIDER_ENV: &str = "TTK_ATTESTATION";
#[derive(Debug)]
pub enum AttestationError {
DeviceOpenFailed(String),
Driver(String),
UnexpectedResponse(String),
InvalidInput(String),
DocumentDecodingFailed(String),
Unsupported(String),
NoProvider,
Io(std::io::Error),
}
impl fmt::Display for AttestationError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::DeviceOpenFailed(msg) => write!(f, "Failed to open TEE device: {msg}"),
Self::Driver(msg) => write!(f, "TEE driver returned an error: {msg}"),
Self::UnexpectedResponse(msg) => {
write!(f, "Unexpected response from TEE driver: {msg}")
}
Self::InvalidInput(msg) => write!(f, "Invalid attestation input: {msg}"),
Self::DocumentDecodingFailed(msg) => write!(f, "Document decoding failure: {msg}"),
Self::Unsupported(msg) => write!(f, "Unsupported attestation provider: {msg}"),
Self::NoProvider => write!(f, "No attestation provider matches this hardware"),
Self::Io(err) => write!(f, "I/O error: {err}"),
}
}
}
impl std::error::Error for AttestationError {}
impl From<std::io::Error> for AttestationError {
fn from(err: std::io::Error) -> Self {
Self::Io(err)
}
}
pub trait AttestationProvider: Send + Sync {
fn name(&self) -> &'static str;
fn is_available() -> bool
where
Self: Sized;
fn generate_document(
&self,
params: &AttestationParams,
) -> Result<EatClaimsSet, AttestationError>;
}
pub fn detect() -> Result<Box<dyn AttestationProvider>, AttestationError> {
if let Ok(name) = std::env::var(PROVIDER_ENV) {
return by_name(&name);
}
#[cfg(feature = "nitro")]
if nitro::NsmSession::is_available() {
return by_name("aws-nitro");
}
#[cfg(feature = "sev-snp")]
if sev_snp::SevSnpSession::is_available() {
return by_name("sev-snp");
}
#[cfg(feature = "tdx")]
if tdx::TdxSession::is_available() {
return by_name("tdx");
}
fallback()
}
#[cfg(feature = "mock")]
fn fallback() -> Result<Box<dyn AttestationProvider>, AttestationError> {
log::warn!("No TEE hardware detected; using MOCK attestation. Evidence is NOT trustworthy.");
by_name("mock")
}
#[cfg(not(feature = "mock"))]
fn fallback() -> Result<Box<dyn AttestationProvider>, AttestationError> {
Err(AttestationError::NoProvider)
}
pub fn by_name(name: &str) -> Result<Box<dyn AttestationProvider>, AttestationError> {
match name {
#[cfg(feature = "nitro")]
"aws-nitro" => Ok(Box::new(nitro::NsmSession::open()?)),
#[cfg(feature = "sev-snp")]
"sev-snp" => Ok(Box::new(sev_snp::SevSnpSession::open()?)),
#[cfg(feature = "tdx")]
"tdx" => Ok(Box::new(tdx::TdxSession::open()?)),
#[cfg(feature = "mock")]
"mock" => Ok(Box::new(mock::MockSession)),
other => Err(AttestationError::Unsupported(format!(
"'{other}' is unknown or not compiled into this build"
))),
}
}