// SPDX-License-Identifier: Apache-2.0 use detee_sgx::prelude::*; use detee_sgx::HRaTlsConfigBuilder; use detee_shared::common_proto::Empty; use detee_shared::sgx::pb::dtpm_proto::DtpmGetConfigRes; use detee_shared::sgx::types::dtpm::FileEntry; use hyper_rustls::HttpsConnectorBuilder; use rustls::ClientConfig; use std::sync::{Arc, RwLock}; use tokio::sync::mpsc; use tokio_stream::wrappers::ReceiverStream; use tonic::codec::CompressionEncoding; use tonic::transport::{Channel, Endpoint}; use detee_shared::sgx::pb::dtpm_proto::dtpm_config_manager_client::DtpmConfigManagerClient; use detee_shared::sgx::pb::dtpm_proto::{DtpmSetConfigReq, FileEntry as FileEntryPb}; use detee_shared::sgx::types::dtpm::DtpmConfig; use crate::config::Config; use crate::sgx::utils::hratls_url_and_mr_enclave_from_app_id; #[derive(thiserror::Error, Debug)] pub enum Error { #[error("Failed to connect to DTPM: {0}")] DtpmConnection(#[from] tonic::transport::Error), #[error("Received error from DTPM: {}", _0.message())] ResponseStatus(#[from] tonic::Status), #[error("Disk access error: {0}")] DiskAccess(#[from] std::io::Error), #[error("HRatls: {0}")] SgxHRatls(#[from] detee_sgx::error::SgxError), #[error("Dtpm: {0}")] Dtpm(String), #[error(transparent)] ConfigError(#[from] crate::config::Error), } type Result = std::result::Result; pub async fn dtpm_client( hratls_uri: &str, mr_enclave: &[u8; 32], ) -> Result> { let private_key_pem = Config::get_hratls_private_key()?; let hratls_config = Arc::new(RwLock::new(HRaTlsConfig::new().with_hratls_private_key_pem(private_key_pem))); hratls_config.write().unwrap().allow_more_instance_measurement( InstanceMeasurement::new().with_mrenclaves(vec![*mr_enclave]), ); let client_tls_config = ClientConfig::from_hratls_config(hratls_config.clone())?; let connector = HttpsConnectorBuilder::new() .with_tls_config(client_tls_config) .https_only() .enable_http2() .build(); let channel = Endpoint::from_shared(hratls_uri.to_string())?.connect_with_connector(connector).await?; Ok(DtpmConfigManagerClient::new(channel).send_compressed(CompressionEncoding::Zstd)) } pub async fn update_config(app_id: &str, config: DtpmConfig) -> Result<()> { let (hratls_uri, mr_enclave) = hratls_url_and_mr_enclave_from_app_id(app_id).await?; let mr_enclave = mr_enclave.expect("App contract does not have a mr_enclave"); let dtpm_client = dtpm_client(&hratls_uri, &mr_enclave).await?; upload_files_pb(config.filesystems.clone(), &dtpm_client).await?; let req = DtpmSetConfigReq { config_data: Some(config.into()), ..Default::default() }; set_config_pb(req, &dtpm_client).await?; Ok(()) } pub async fn get_config(app_id: &str) -> Result { let (hratls_uri, mr_enclave) = hratls_url_and_mr_enclave_from_app_id(app_id).await?; let mr_enclave = mr_enclave.expect("App contract does not have a mr_enclave"); let dtpm_client = dtpm_client(&hratls_uri, &mr_enclave).await?; let config_res = get_config_pb(&dtpm_client).await?; let config: DtpmConfig = config_res.config_data.ok_or(Error::Dtpm("config data not found".to_string()))?.into(); Ok(config) } pub async fn upload_files_pb( reqs: Vec, client: &DtpmConfigManagerClient, ) -> Result<()> { let (tx, rx) = mpsc::channel(6); tokio::spawn(async move { for file in reqs { let file_pb: FileEntryPb = file.into(); let _ = tx.send(file_pb).await; } }); let fs_stream = ReceiverStream::new(rx); let _ = client.clone().upload_files(tonic::Request::new(fs_stream)).await?; Ok(()) } pub(crate) async fn set_config_pb( req: DtpmSetConfigReq, client: &DtpmConfigManagerClient, ) -> Result<()> { let response = client.clone().set_config(tonic::Request::new(req)).await?; log::trace!("Received respose from the server...{:?}", response.into_inner()); Ok(()) } pub(crate) async fn get_config_pb( client: &DtpmConfigManagerClient, ) -> Result { let mgr_config_pb = client.clone().get_config(tonic::Request::new(Empty {})).await?.into_inner(); Ok(mgr_config_pb) }