diff --git a/attestation-service/attestation-service/src/bin/grpc/mod.rs b/attestation-service/attestation-service/src/bin/grpc/mod.rs index cd22fa7617..15ef085f48 100644 --- a/attestation-service/attestation-service/src/bin/grpc/mod.rs +++ b/attestation-service/attestation-service/src/bin/grpc/mod.rs @@ -1,13 +1,15 @@ -use anyhow::{anyhow, Result}; use attestation_service::policy_engine::SetPolicyInput; use attestation_service::HashAlgorithm; -use attestation_service::{config::Config, AttestationService as Service, Tee}; +use attestation_service::{ + config::Config, config::ConfigError, AttestationService as Service, ServiceError, Tee, +}; use base64::engine::general_purpose::URL_SAFE_NO_PAD; use base64::Engine; use log::{debug, info}; use std::net::SocketAddr; use std::path::Path; use std::sync::Arc; +use thiserror::Error; use tokio::sync::RwLock; use tonic::transport::Server; use tonic::{Request, Response, Status}; @@ -40,15 +42,24 @@ fn to_kbs_tee(tee: GrpcTee) -> Tee { } } +#[derive(Error, Debug)] +pub enum GrpcError { + #[error("Read AS config file failed")] + Config(#[from] ConfigError), + #[error("Creating attestation service failed")] + Service(#[from] ServiceError), + #[error("tonic transport error")] + TonicTransport(#[from] tonic::transport::Error), +} + pub struct AttestationServer { attestation_service: Service, } impl AttestationServer { - pub async fn new(config_path: Option) -> Result { + pub async fn new(config_path: Option) -> Result { let config = match config_path { - Some(path) => Config::try_from(Path::new(&path)) - .map_err(|e| anyhow!("Read AS config file failed: {:?}", e))?, + Some(path) => Config::try_from(Path::new(&path))?, None => Config::default(), }; @@ -222,7 +233,7 @@ impl ReferenceValueProviderService for Arc> { } } -pub async fn start(socket: SocketAddr, config_path: Option) -> Result<()> { +pub async fn start(socket: SocketAddr, config_path: Option) -> Result<(), GrpcError> { info!("Listen socket: {}", &socket); let attestation_server = Arc::new(RwLock::new(AttestationServer::new(config_path).await?)); diff --git a/attestation-service/attestation-service/src/bin/restful-as.rs b/attestation-service/attestation-service/src/bin/restful-as.rs index de797f7e8e..3969611c77 100644 --- a/attestation-service/attestation-service/src/bin/restful-as.rs +++ b/attestation-service/attestation-service/src/bin/restful-as.rs @@ -1,8 +1,8 @@ use std::{net::SocketAddr, path::Path, sync::Arc}; use actix_web::{web, App, HttpServer}; -use anyhow::{anyhow, Context, Result}; -use attestation_service::{config::Config, AttestationService}; +use anyhow::Result; +use attestation_service::{config::Config, config::ConfigError, AttestationService, ServiceError}; use clap::{arg, command, Parser}; use log::info; use openssl::{ @@ -10,6 +10,7 @@ use openssl::{ ssl::{SslAcceptor, SslMethod}, }; use strum::{AsRefStr, EnumString}; +use thiserror::Error; use tokio::sync::RwLock; use crate::restful::{attestation, set_policy}; @@ -49,8 +50,24 @@ enum WebApi { Policy, } +#[derive(Error, Debug)] +pub enum RestfulError { + #[error("Creating service failed")] + Service(#[from] ServiceError), + #[error("Failed to read AS config file")] + Config(#[from] ConfigError), + #[error("Openssl errorstack")] + Openssl(#[from] openssl::error::ErrorStack), + #[error("io error")] + IO(#[from] std::io::Error), + #[error("Failed to start server: {0}")] + StartServer(String), + #[error("Error")] + Anyhow(#[from] anyhow::Error), +} + #[actix_web::main] -async fn main() -> Result<()> { +async fn main() -> Result<(), RestfulError> { env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); let cli = Cli::parse(); @@ -58,8 +75,7 @@ async fn main() -> Result<()> { let config = match cli.config_file { Some(path) => { info!("Using config file {path}"); - Config::try_from(Path::new(&path)) - .map_err(|e| anyhow!("Read AS config file failed: {:?}", e))? + Config::try_from(Path::new(&path))? } None => { info!("No confile path provided, use default one."); @@ -81,18 +97,11 @@ async fn main() -> Result<()> { (Some(prikey), Some(pubkey_cert)) => { let mut builder = SslAcceptor::mozilla_modern(SslMethod::tls())?; - let prikey = tokio::fs::read(prikey) - .await - .context("read HTTPS private key")?; - let prikey = - PKey::private_key_from_pem(&prikey).context("read HTTPS private key from pem")?; - - builder - .set_private_key(&prikey) - .context("set private key failed")?; - builder - .set_certificate_chain_file(pubkey_cert) - .context("set HTTPS public key cert")?; + let prikey = tokio::fs::read(prikey).await?; + let prikey = PKey::private_key_from_pem(&prikey)?; + + builder.set_private_key(&prikey)?; + builder.set_certificate_chain_file(pubkey_cert)?; log::info!("starting HTTPS server at https://{}", cli.socket); server.bind_openssl(cli.socket, builder)?.run() } @@ -105,6 +114,5 @@ async fn main() -> Result<()> { }; server.await?; - Ok(()) } diff --git a/attestation-service/attestation-service/src/config.rs b/attestation-service/attestation-service/src/config.rs index 814136ad64..7ae3b676a4 100644 --- a/attestation-service/attestation-service/src/config.rs +++ b/attestation-service/attestation-service/src/config.rs @@ -3,11 +3,11 @@ use crate::{ token::{AttestationTokenBrokerType, AttestationTokenConfig}, }; -use anyhow::{anyhow, Result}; use serde::Deserialize; use std::convert::TryFrom; use std::fs::File; use std::path::{Path, PathBuf}; +use thiserror::Error; /// Environment macro for Attestation Service work dir. const AS_WORK_DIR: &str = "AS_WORK_DIR"; @@ -34,6 +34,14 @@ pub struct Config { pub attestation_token_config: AttestationTokenConfig, } +#[derive(Error, Debug)] +pub enum ConfigError { + #[error("io error")] + IO(#[from] std::io::Error), + #[error("Serde Json Error")] + SerdeJson(#[from] serde_json::Error), +} + impl Default for Config { // Construct a default instance of `Config` fn default() -> Config { @@ -65,12 +73,9 @@ impl TryFrom<&Path> for Config { /// "duration_min": 5 /// } /// } - type Error = anyhow::Error; - fn try_from(config_path: &Path) -> Result { - let file = File::open(config_path) - .map_err(|e| anyhow!("failed to open AS config file {}", e.to_string()))?; - - serde_json::from_reader::(file) - .map_err(|e| anyhow!("failed to parse AS config file {}", e.to_string())) + type Error = ConfigError; + fn try_from(config_path: &Path) -> Result { + let file = File::open(config_path)?; + Ok(serde_json::from_reader::(file)?) } } diff --git a/attestation-service/attestation-service/src/lib.rs b/attestation-service/attestation-service/src/lib.rs index 85370f72b7..ea5e9ba643 100644 --- a/attestation-service/attestation-service/src/lib.rs +++ b/attestation-service/attestation-service/src/lib.rs @@ -12,17 +12,18 @@ mod utils; use crate::token::AttestationTokenBroker; -use anyhow::{anyhow, Context, Result}; +use anyhow::Result; use config::Config; pub use kbs_types::{Attestation, Tee}; use log::debug; use policy_engine::{PolicyEngine, PolicyEngineType, SetPolicyInput}; -use rvps::RvpsApi; +use rvps::{RvpsApi, RvpsError}; use serde_json::{json, Value}; use serde_variant::to_variant_name; use sha2::{Digest, Sha256, Sha384, Sha512}; use std::{collections::HashMap, str::FromStr}; use strum::{AsRefStr, EnumString}; +use thiserror::Error; use tokio::fs; use verifier::{InitDataHash, ReportData}; @@ -77,6 +78,18 @@ pub enum Data { Structured(Value), } +#[derive(Error, Debug)] +pub enum ServiceError { + #[error("io error")] + IO(#[from] std::io::Error), + #[error("Parse error")] + ParseError(#[from] strum::ParseError), + #[error("Create rvps failed.")] + Rvps(#[from] RvpsError), + #[error("Error")] + Anyhow(#[from] anyhow::Error), +} + pub struct AttestationService { _config: Config, policy_engine: Box, @@ -86,22 +99,15 @@ pub struct AttestationService { impl AttestationService { /// Create a new Attestation Service instance. - pub async fn new(config: Config) -> Result { + pub async fn new(config: Config) -> Result { if !config.work_dir.as_path().exists() { - fs::create_dir_all(&config.work_dir) - .await - .context("Create AS work dir failed: {:?}")?; + fs::create_dir_all(&config.work_dir).await?; } - let policy_engine = PolicyEngineType::from_str(&config.policy_engine) - .map_err(|_| anyhow!("Policy Engine {} is not supported", &config.policy_engine))? + let policy_engine = PolicyEngineType::from_str(&config.policy_engine)? .to_policy_engine(config.work_dir.as_path())?; - let rvps = config - .rvps_config - .to_rvps() - .await - .context("create rvps failed.")?; + let rvps = config.rvps_config.to_rvps().await?; let token_broker = config .attestation_token_broker @@ -116,11 +122,10 @@ impl AttestationService { } /// Set Attestation Verification Policy. - pub async fn set_policy(&mut self, input: SetPolicyInput) -> Result<()> { - self.policy_engine - .set_policy(input) - .await - .map_err(|e| anyhow!("Cannot Set Policy: {:?}", e)) + pub async fn set_policy(&mut self, input: SetPolicyInput) -> Result<(), ServiceError> { + self.policy_engine.set_policy(input).await?; + + Ok(()) } /// Evaluate Attestation Evidence. @@ -153,15 +158,14 @@ impl AttestationService { let verifier = verifier::to_verifier(&tee)?; let (report_data, runtime_data_claims) = - parse_data(runtime_data, &runtime_data_hash_algorithm).context("parse runtime data")?; + parse_data(runtime_data, &runtime_data_hash_algorithm)?; let report_data = match &report_data { Some(data) => ReportData::Value(data), None => ReportData::NotProvided, }; - let (init_data, init_data_claims) = - parse_data(init_data, &init_data_hash_algorithm).context("parse init data")?; + let (init_data, init_data_claims) = parse_data(init_data, &init_data_hash_algorithm)?; let init_data_hash = match &init_data { Some(data) => InitDataHash::Value(data), @@ -170,23 +174,18 @@ impl AttestationService { let claims_from_tee_evidence = verifier .evaluate(&evidence, &report_data, &init_data_hash) - .await - .map_err(|e| anyhow!("Verifier evaluate failed: {e:?}"))?; + .await?; let flattened_claims = flatten_claims(tee, &claims_from_tee_evidence)?; let tcb_json = serde_json::to_string(&flattened_claims)?; - let reference_data_map = self - .get_reference_data(flattened_claims.keys()) - .await - .map_err(|e| anyhow!("Generate reference data failed: {:?}", e))?; + let reference_data_map = self.get_reference_data(flattened_claims.keys()).await?; let evaluation_report = self .policy_engine .evaluate(reference_data_map.clone(), tcb_json, policy_ids.clone()) - .await - .map_err(|e| anyhow!("Policy Engine evaluation failed: {e}"))?; + .await?; let policies: Vec<_> = evaluation_report .into_iter() @@ -252,8 +251,7 @@ fn parse_data( Data::Raw(raw) => Ok((Some(raw), Value::Null)), Data::Structured(structured) => { // by default serde_json will enforence the alphabet order for keys - let hash_materials = - serde_json::to_vec(&structured).context("parse JSON structured data")?; + let hash_materials = serde_json::to_vec(&structured)?; let digest = hash_algorithm.accumulate_hash(hash_materials); Ok((Some(digest), structured)) } diff --git a/attestation-service/attestation-service/src/rvps/builtin.rs b/attestation-service/attestation-service/src/rvps/builtin.rs index 49fca776e9..06c72fc600 100644 --- a/attestation-service/attestation-service/src/rvps/builtin.rs +++ b/attestation-service/attestation-service/src/rvps/builtin.rs @@ -1,5 +1,7 @@ +use crate::rvps::RvpsError; use anyhow::*; use async_trait::async_trait; +use core::result::Result::Ok; use reference_value_provider_service::Core; use super::RvpsApi; @@ -9,8 +11,9 @@ pub struct Rvps { } impl Rvps { - pub fn new(store_type: &str) -> Result { - let core = Core::new(store_type)?; + pub fn new(store_type: &str) -> Result { + let core = + Core::new(store_type).map_err(|error| RvpsError::CreateRvps(error.to_string()))?; Ok(Self { core }) } } @@ -18,7 +21,8 @@ impl Rvps { #[async_trait] impl RvpsApi for Rvps { async fn verify_and_extract(&mut self, message: &str) -> Result<()> { - self.core.verify_and_extract(message).await + self.core.verify_and_extract(message).await?; + Ok(()) } async fn get_digests(&self, name: &str) -> Result> { diff --git a/attestation-service/attestation-service/src/rvps/grpc.rs b/attestation-service/attestation-service/src/rvps/grpc.rs index e749884825..5ddc232b5d 100644 --- a/attestation-service/attestation-service/src/rvps/grpc.rs +++ b/attestation-service/attestation-service/src/rvps/grpc.rs @@ -1,4 +1,5 @@ -use anyhow::*; +use crate::rvps::RvpsError; +use anyhow::{Context, Result}; use tokio::sync::Mutex; use self::rvps_api::{ @@ -17,7 +18,7 @@ pub struct Agent { } impl Agent { - pub async fn new(addr: &str) -> Result { + pub async fn new(addr: &str) -> Result { Ok(Self { client: Mutex::new( ReferenceValueProviderServiceClient::connect(addr.to_string()).await?, @@ -25,7 +26,6 @@ impl Agent { }) } } - #[async_trait::async_trait] impl RvpsApi for Agent { async fn verify_and_extract(&mut self, message: &str) -> Result<()> { diff --git a/attestation-service/attestation-service/src/rvps/mod.rs b/attestation-service/attestation-service/src/rvps/mod.rs index f826ab12ef..1baa542bd6 100644 --- a/attestation-service/attestation-service/src/rvps/mod.rs +++ b/attestation-service/attestation-service/src/rvps/mod.rs @@ -3,9 +3,10 @@ // SPDX-License-Identifier: Apache-2.0 // -use anyhow::*; +use anyhow::Result; use log::{info, warn}; use serde::Deserialize; +use thiserror::Error; /// The interfaces of Reference Value Provider Service /// * `verify_and_extract` is responsible for verify a message and @@ -55,10 +56,26 @@ impl Default for RvpsConfig { } } +#[derive(Error, Debug)] +pub enum RvpsError { + #[error("Failed creating rvps service: {0}")] + CreateRvps(String), + #[error("feature `rvps-grpc` or `rvps-builtin` should be enabled")] + FeatureNotEnabled, + #[error("Serde Json Error")] + SerdeJson(#[from] serde_json::Error), + #[error("Returned status")] + Status(#[from] tonic::Status), + #[error("tonic transport error")] + TonicTransport(#[from] tonic::transport::Error), + #[error("Error")] + Anyhow(#[from] anyhow::Error), +} + impl RvpsConfig { /// If remote addr is specified and the feature `rvps-grpc` is enabled when /// built, will try to connect the remote rvps. Or, will use a built-in rvps. - pub async fn to_rvps(&self) -> Result> { + pub async fn to_rvps(&self) -> Result, RvpsError> { cfg_if::cfg_if! { if #[cfg(feature = "rvps-grpc")] { if !self.remote_addr.is_empty() { @@ -70,7 +87,7 @@ impl RvpsConfig { warn!("No RVPS address provided and will launch a built-in rvps"); Ok(Box::new(builtin::Rvps::new(&self.store_type)?) as Box) } else { - Err(anyhow!("either feature `rvps-grpc` or `rvps-builtin` should be enabled.")) + return RvpsError::FeatureNotEnabled; } } } @@ -78,7 +95,7 @@ impl RvpsConfig { info!("launch a built-in RVPS."); Ok(Box::new(builtin::Rvps::new(&self.store_type)) as Box) } else { - Err(anyhow!("either feature `rvps-grpc` or `rvps-builtin` should be enabled.")) + return RvpsError::FeatureNotEnabled; } } }