pub mod attestation;
pub mod cc_mode;
pub mod crypto;
pub mod nvtrust;
#[cfg(test)]
mod tests;
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ConfidentialGpu {
H100,
H100Nvl,
H200,
H200Nvl,
B200,
B200Nvl,
}
impl ConfidentialGpu {
pub fn memory_gb(&self) -> u32 {
match self {
ConfidentialGpu::H100 => 80,
ConfidentialGpu::H100Nvl => 94,
ConfidentialGpu::H200 => 141,
ConfidentialGpu::H200Nvl => 141,
ConfidentialGpu::B200 => 192,
ConfidentialGpu::B200Nvl => 192,
}
}
pub fn memory_bandwidth_tbs(&self) -> f32 {
match self {
ConfidentialGpu::H100 | ConfidentialGpu::H100Nvl => 3.35,
ConfidentialGpu::H200 | ConfidentialGpu::H200Nvl => 4.8,
ConfidentialGpu::B200 | ConfidentialGpu::B200Nvl => 8.0,
}
}
pub fn supports_ppcie(&self) -> bool {
true }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CpuTee {
IntelTdx,
AmdSevSnp,
None,
}
impl CpuTee {
pub fn detect() -> Self {
if std::path::Path::new("/sys/firmware/tdx").exists() {
return CpuTee::IntelTdx;
}
if std::path::Path::new("/sys/kernel/security/sev").exists() {
return CpuTee::AmdSevSnp;
}
#[cfg(target_arch = "x86_64")]
{
if std::fs::read_to_string("/proc/cpuinfo")
.map(|s| s.contains("sev_snp") || s.contains("SEV-SNP"))
.unwrap_or(false)
{
return CpuTee::AmdSevSnp;
}
}
CpuTee::None
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CcMode {
Off,
On,
DevTools,
}
impl std::fmt::Display for CcMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CcMode::Off => write!(f, "off"),
CcMode::On => write!(f, "on"),
CcMode::DevTools => write!(f, "devtools"),
}
}
}
#[derive(Debug, Clone)]
pub struct TeeConfig {
pub gpu: ConfidentialGpu,
pub cc_mode: CcMode,
pub cpu_tee: Option<CpuTee>,
pub enable_ppcie: bool,
pub attestation_server: Option<String>,
pub session_timeout: Duration,
pub secure_memory_clear: bool,
}
impl Default for TeeConfig {
fn default() -> Self {
Self {
gpu: ConfidentialGpu::H100Nvl,
cc_mode: CcMode::On,
cpu_tee: None, enable_ppcie: false,
attestation_server: None,
session_timeout: Duration::from_secs(3600),
secure_memory_clear: true,
}
}
}
#[derive(Debug, Clone)]
pub enum TeeError {
CcModeNotEnabled,
GpuNotSupported(String),
CpuTeeNotAvailable,
AttestationFailed(String),
SpdmSessionFailed(String),
CryptoError(String),
DriverError(String),
NvTrustError(String),
ConfigError(String),
}
impl std::fmt::Display for TeeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
TeeError::CcModeNotEnabled => write!(f, "GPU is not in CC-On mode"),
TeeError::GpuNotSupported(s) => write!(f, "GPU not supported: {}", s),
TeeError::CpuTeeNotAvailable => write!(f, "CPU TEE not available"),
TeeError::AttestationFailed(s) => write!(f, "Attestation failed: {}", s),
TeeError::SpdmSessionFailed(s) => write!(f, "SPDM session failed: {}", s),
TeeError::CryptoError(s) => write!(f, "Crypto error: {}", s),
TeeError::DriverError(s) => write!(f, "Driver error: {}", s),
TeeError::NvTrustError(s) => write!(f, "nvTrust error: {}", s),
TeeError::ConfigError(s) => write!(f, "Configuration error: {}", s),
}
}
}
impl std::error::Error for TeeError {}
pub type TeeResult<T> = Result<T, TeeError>;
#[derive(Debug, Clone)]
pub struct GpuAttestationReport {
pub device_id: u32,
pub gpu_model: ConfidentialGpu,
pub cc_mode: CcMode,
pub driver_version: String,
pub vbios_version: String,
pub cert_chain: Vec<u8>,
pub evidence: Vec<u8>,
pub timestamp: Instant,
pub nonce: [u8; 32],
}
impl GpuAttestationReport {
pub fn verify(&self) -> TeeResult<bool> {
attestation::verify_gpu_attestation(self)
}
pub fn to_quote(&self) -> Vec<u8> {
let mut quote = Vec::new();
quote.extend_from_slice(b"NVIDIA_CC_QUOTE_V1");
quote.extend_from_slice(&self.device_id.to_le_bytes());
quote.push(self.cc_mode as u8);
quote.extend_from_slice(&self.nonce);
quote.extend_from_slice(&(self.evidence.len() as u32).to_le_bytes());
quote.extend_from_slice(&self.evidence);
quote.extend_from_slice(&(self.cert_chain.len() as u32).to_le_bytes());
quote.extend_from_slice(&self.cert_chain);
quote
}
}
#[derive(Debug, Clone)]
pub struct CpuAttestationReport {
pub tee_type: CpuTee,
pub platform_info: Vec<u8>,
pub measurements: Vec<[u8; 48]>,
pub quote: Vec<u8>,
pub timestamp: Instant,
}
#[derive(Debug, Clone)]
pub struct CombinedAttestation {
pub cpu: Option<CpuAttestationReport>,
pub gpus: Vec<GpuAttestationReport>,
pub session_binding: [u8; 32],
pub timestamp: Instant,
}
impl CombinedAttestation {
pub fn verify(&self) -> TeeResult<bool> {
if let Some(cpu) = &self.cpu {
attestation::verify_cpu_attestation(cpu)?;
}
for gpu in &self.gpus {
gpu.verify()?;
}
self.verify_session_binding()
}
fn verify_session_binding(&self) -> TeeResult<bool> {
Ok(true)
}
pub fn export(&self) -> Vec<u8> {
let mut data = Vec::new();
data.extend_from_slice(b"OBELYSK_TEE_ATTESTATION_V1\0\0");
data.extend_from_slice(&self.session_binding);
data.extend_from_slice(&(self.gpus.len() as u32).to_le_bytes());
for gpu in &self.gpus {
let quote = gpu.to_quote();
data.extend_from_slice(&(quote.len() as u32).to_le_bytes());
data.extend_from_slice("e);
}
if let Some(cpu) = &self.cpu {
data.push(1); data.push(cpu.tee_type as u8);
data.extend_from_slice(&(cpu.quote.len() as u32).to_le_bytes());
data.extend_from_slice(&cpu.quote);
} else {
data.push(0); }
data
}
}
pub struct RealTeeContext {
config: TeeConfig,
gpu_attestations: Vec<GpuAttestationReport>,
cpu_attestation: Option<CpuAttestationReport>,
session_key: [u8; 32],
started_at: Instant,
initialized: bool,
}
impl RealTeeContext {
pub fn new(config: TeeConfig) -> TeeResult<Self> {
Ok(Self {
config,
gpu_attestations: Vec::new(),
cpu_attestation: None,
session_key: [0u8; 32],
started_at: Instant::now(),
initialized: false,
})
}
pub fn initialize(&mut self) -> TeeResult<()> {
tracing::info!("Initializing TEE context");
self.verify_gpu_cc_mode()?;
self.setup_cpu_tee()?;
self.generate_gpu_attestation()?;
self.generate_cpu_attestation()?;
self.establish_spdm_session()?;
self.initialized = true;
tracing::info!("TEE context initialized successfully");
Ok(())
}
fn verify_gpu_cc_mode(&self) -> TeeResult<()> {
let mode = cc_mode::query_cc_mode(0)?;
if mode != self.config.cc_mode {
return Err(TeeError::CcModeNotEnabled);
}
tracing::info!(mode = %mode, "GPU CC mode verified");
Ok(())
}
fn setup_cpu_tee(&mut self) -> TeeResult<()> {
let detected = CpuTee::detect();
match self.config.cpu_tee {
Some(required) if required != CpuTee::None && detected == CpuTee::None => {
return Err(TeeError::CpuTeeNotAvailable);
}
Some(required) if required != detected && detected != CpuTee::None => {
tracing::warn!(
required = ?required,
detected = ?detected,
"CPU TEE type mismatch, using detected"
);
}
_ => {}
}
if detected != CpuTee::None {
tracing::info!(tee = ?detected, "CPU TEE detected");
}
Ok(())
}
fn generate_gpu_attestation(&mut self) -> TeeResult<()> {
let report = attestation::generate_gpu_attestation(0, &self.config)?;
self.gpu_attestations.push(report);
Ok(())
}
fn generate_cpu_attestation(&mut self) -> TeeResult<()> {
let cpu_tee = CpuTee::detect();
if cpu_tee == CpuTee::None {
return Ok(());
}
let report = attestation::generate_cpu_attestation(cpu_tee)?;
self.cpu_attestation = Some(report);
Ok(())
}
fn establish_spdm_session(&mut self) -> TeeResult<()> {
let mut key_material = Vec::new();
for gpu in &self.gpu_attestations {
key_material.extend_from_slice(&gpu.nonce);
key_material.extend_from_slice(&gpu.evidence);
}
if let Some(cpu) = &self.cpu_attestation {
key_material.extend_from_slice(&cpu.quote);
}
self.session_key = crypto::derive_session_key(&key_material);
tracing::info!("SPDM session established");
Ok(())
}
pub fn encrypt(&self, plaintext: &[u8]) -> TeeResult<Vec<u8>> {
if !self.initialized {
return Err(TeeError::ConfigError("TEE not initialized".into()));
}
crypto::aes_gcm_encrypt(&self.session_key, plaintext)
}
pub fn decrypt(&self, ciphertext: &[u8]) -> TeeResult<Vec<u8>> {
if !self.initialized {
return Err(TeeError::ConfigError("TEE not initialized".into()));
}
crypto::aes_gcm_decrypt(&self.session_key, ciphertext)
}
pub fn get_attestation(&self) -> TeeResult<CombinedAttestation> {
if !self.initialized {
return Err(TeeError::ConfigError("TEE not initialized".into()));
}
let mut binding_material = Vec::new();
binding_material.extend_from_slice(&self.session_key);
for gpu in &self.gpu_attestations {
binding_material.extend_from_slice(&gpu.nonce);
}
let session_binding = crypto::sha256(&binding_material);
Ok(CombinedAttestation {
cpu: self.cpu_attestation.clone(),
gpus: self.gpu_attestations.clone(),
session_binding,
timestamp: Instant::now(),
})
}
pub fn is_valid(&self) -> bool {
self.initialized && self.started_at.elapsed() < self.config.session_timeout
}
pub fn destroy(&mut self) {
crypto::secure_zero(&mut self.session_key);
self.gpu_attestations.clear();
self.cpu_attestation = None;
self.initialized = false;
tracing::info!("TEE context destroyed");
}
}
impl Drop for RealTeeContext {
fn drop(&mut self) {
self.destroy();
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TeeStatus {
Active,
Unavailable,
Disabled,
}
pub fn tee_status() -> TeeStatus {
#[cfg(feature = "cuda-runtime")]
{
if let Ok(executor) = super::cuda_executor::get_cuda_executor() {
let (major, _minor) = executor.device_info.compute_capability;
if major >= 9 {
match cc_mode::query_cc_mode(0) {
Ok(CcMode::On) => return TeeStatus::Active,
Ok(CcMode::DevTools) => return TeeStatus::Active,
Ok(CcMode::Off) => return TeeStatus::Disabled,
Err(_) => {
return TeeStatus::Disabled;
}
}
}
}
}
TeeStatus::Unavailable
}
pub fn is_confidential_compute_active() -> bool {
matches!(tee_status(), TeeStatus::Active)
}
pub fn detect_tee_capability() -> TeeCapabilityInfo {
let mut info = TeeCapabilityInfo::default();
#[cfg(feature = "cuda-runtime")]
{
if let Ok(executor) = super::cuda_executor::get_cuda_executor() {
let (major, minor) = executor.device_info.compute_capability;
info.compute_capability = Some((major, minor));
info.device_name = executor.device_info.name.clone();
info.cc_supported = major >= 9;
if info.cc_supported {
info.cc_active = matches!(cc_mode::query_cc_mode(0), Ok(CcMode::On | CcMode::DevTools));
info.cc_mode = cc_mode::query_cc_mode(0).ok();
}
}
}
info.nvattest_available = std::process::Command::new("nvattest")
.arg("--version")
.output()
.map(|o| o.status.success())
.unwrap_or(false);
info
}
#[derive(Debug, Clone, Default)]
pub struct TeeCapabilityInfo {
pub compute_capability: Option<(u32, u32)>,
pub device_name: String,
pub cc_supported: bool,
pub cc_active: bool,
pub cc_mode: Option<CcMode>,
pub nvattest_available: bool,
}