use crate::config::TeeProvider;
use crate::errors::TeeError;
use crate::runtime::backend::{
TeeDeployRequest, TeeDeploymentHandle, TeeDeploymentStatus, TeePublicKey, TeeRuntimeBackend,
};
use std::collections::BTreeMap;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
trait ErasedBackend: Send + Sync {
fn deploy(&self, req: TeeDeployRequest)
-> BoxFuture<'_, Result<TeeDeploymentHandle, TeeError>>;
fn get_attestation<'a>(
&'a self,
handle: &'a TeeDeploymentHandle,
) -> BoxFuture<'a, Result<crate::attestation::report::AttestationReport, TeeError>>;
fn cached_attestation<'a>(
&'a self,
handle: &'a TeeDeploymentHandle,
) -> BoxFuture<'a, Result<Option<crate::attestation::report::AttestationReport>, TeeError>>;
fn derive_public_key<'a>(
&'a self,
handle: &'a TeeDeploymentHandle,
) -> BoxFuture<'a, Result<TeePublicKey, TeeError>>;
fn status<'a>(
&'a self,
handle: &'a TeeDeploymentHandle,
) -> BoxFuture<'a, Result<TeeDeploymentStatus, TeeError>>;
fn stop<'a>(&'a self, handle: &'a TeeDeploymentHandle) -> BoxFuture<'a, Result<(), TeeError>>;
fn destroy<'a>(
&'a self,
handle: &'a TeeDeploymentHandle,
) -> BoxFuture<'a, Result<(), TeeError>>;
}
impl<T: TeeRuntimeBackend + 'static> ErasedBackend for T {
fn deploy(
&self,
req: TeeDeployRequest,
) -> BoxFuture<'_, Result<TeeDeploymentHandle, TeeError>> {
Box::pin(TeeRuntimeBackend::deploy(self, req))
}
fn get_attestation<'a>(
&'a self,
handle: &'a TeeDeploymentHandle,
) -> BoxFuture<'a, Result<crate::attestation::report::AttestationReport, TeeError>> {
Box::pin(TeeRuntimeBackend::get_attestation(self, handle))
}
fn cached_attestation<'a>(
&'a self,
handle: &'a TeeDeploymentHandle,
) -> BoxFuture<'a, Result<Option<crate::attestation::report::AttestationReport>, TeeError>>
{
Box::pin(TeeRuntimeBackend::cached_attestation(self, handle))
}
fn derive_public_key<'a>(
&'a self,
handle: &'a TeeDeploymentHandle,
) -> BoxFuture<'a, Result<TeePublicKey, TeeError>> {
Box::pin(TeeRuntimeBackend::derive_public_key(self, handle))
}
fn status<'a>(
&'a self,
handle: &'a TeeDeploymentHandle,
) -> BoxFuture<'a, Result<TeeDeploymentStatus, TeeError>> {
Box::pin(TeeRuntimeBackend::status(self, handle))
}
fn stop<'a>(&'a self, handle: &'a TeeDeploymentHandle) -> BoxFuture<'a, Result<(), TeeError>> {
Box::pin(TeeRuntimeBackend::stop(self, handle))
}
fn destroy<'a>(
&'a self,
handle: &'a TeeDeploymentHandle,
) -> BoxFuture<'a, Result<(), TeeError>> {
Box::pin(TeeRuntimeBackend::destroy(self, handle))
}
}
struct DynBackend {
inner: Arc<dyn ErasedBackend>,
}
impl core::fmt::Debug for DynBackend {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("DynBackend").finish()
}
}
#[derive(Debug, Default)]
pub struct BackendRegistry {
backends: BTreeMap<TeeProvider, DynBackend>,
}
impl BackendRegistry {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, provider: TeeProvider, backend: impl TeeRuntimeBackend + 'static) {
self.backends.insert(
provider,
DynBackend {
inner: Arc::new(backend),
},
);
}
pub fn has_provider(&self, provider: TeeProvider) -> bool {
self.backends.contains_key(&provider)
}
pub fn providers(&self) -> Vec<TeeProvider> {
self.backends.keys().copied().collect()
}
pub async fn deploy(
&self,
provider: TeeProvider,
req: TeeDeployRequest,
) -> Result<TeeDeploymentHandle, TeeError> {
let backend = self.backends.get(&provider).ok_or_else(|| {
TeeError::UnsupportedProvider(format!("no backend registered for {provider}"))
})?;
backend.inner.deploy(req).await
}
fn get_backend(&self, provider: TeeProvider) -> Result<&DynBackend, TeeError> {
self.backends.get(&provider).ok_or_else(|| {
TeeError::UnsupportedProvider(format!("no backend registered for {provider}"))
})
}
pub async fn get_attestation(
&self,
handle: &TeeDeploymentHandle,
) -> Result<crate::attestation::report::AttestationReport, TeeError> {
self.get_backend(handle.provider)?
.inner
.get_attestation(handle)
.await
}
pub async fn cached_attestation(
&self,
handle: &TeeDeploymentHandle,
) -> Result<Option<crate::attestation::report::AttestationReport>, TeeError> {
self.get_backend(handle.provider)?
.inner
.cached_attestation(handle)
.await
}
pub async fn derive_public_key(
&self,
handle: &TeeDeploymentHandle,
) -> Result<TeePublicKey, TeeError> {
self.get_backend(handle.provider)?
.inner
.derive_public_key(handle)
.await
}
pub async fn status(
&self,
handle: &TeeDeploymentHandle,
) -> Result<TeeDeploymentStatus, TeeError> {
self.get_backend(handle.provider)?
.inner
.status(handle)
.await
}
pub async fn stop(&self, handle: &TeeDeploymentHandle) -> Result<(), TeeError> {
self.get_backend(handle.provider)?.inner.stop(handle).await
}
pub async fn destroy(&self, handle: &TeeDeploymentHandle) -> Result<(), TeeError> {
self.get_backend(handle.provider)?
.inner
.destroy(handle)
.await
}
pub async fn from_env() -> Result<Self, TeeError> {
let mut registry = Self::new();
let backends = match std::env::var("TEE_BACKEND") {
Ok(val) => val,
Err(_) => return Ok(registry),
};
for backend in backends.split(',').map(str::trim) {
match backend {
"direct" | "direct-tdx" => {
registry.register(
TeeProvider::IntelTdx,
crate::runtime::direct::DirectBackend::tdx(),
);
}
"direct-sev-snp" => {
registry.register(
TeeProvider::AmdSevSnp,
crate::runtime::direct::DirectBackend::sev_snp(),
);
}
#[cfg(feature = "aws-nitro")]
"aws-nitro" => {
let backend = crate::runtime::aws_nitro::NitroBackend::from_env().await?;
registry.register(TeeProvider::AwsNitro, backend);
}
#[cfg(not(feature = "aws-nitro"))]
"aws-nitro" => {
return Err(TeeError::UnsupportedProvider(
"aws-nitro backend requested but the 'aws-nitro' feature is not enabled; \
recompile with --features aws-nitro"
.to_string(),
));
}
#[cfg(feature = "gcp-confidential")]
"gcp-confidential" => {
let backend =
crate::runtime::gcp_confidential::GcpConfidentialBackend::from_env()
.await?;
registry.register(TeeProvider::GcpConfidential, backend);
}
#[cfg(not(feature = "gcp-confidential"))]
"gcp-confidential" => {
return Err(TeeError::UnsupportedProvider(
"gcp-confidential backend requested but the 'gcp-confidential' feature \
is not enabled; recompile with --features gcp-confidential"
.to_string(),
));
}
#[cfg(feature = "azure-snp")]
"azure-snp" => {
let backend = crate::runtime::azure_skr::AzureSkrBackend::from_env()?;
registry.register(TeeProvider::AzureSnp, backend);
}
#[cfg(not(feature = "azure-snp"))]
"azure-snp" => {
return Err(TeeError::UnsupportedProvider(
"azure-snp backend requested but the 'azure-snp' feature is not enabled; \
recompile with --features azure-snp"
.to_string(),
));
}
other => {
return Err(TeeError::Config(format!(
"unknown TEE_BACKEND value: '{other}'; \
valid options: direct, direct-tdx, direct-sev-snp, \
aws-nitro, gcp-confidential, azure-snp"
)));
}
}
}
Ok(registry)
}
}