From 4a8f6b341e80bb005ca055be7b7e9358d598c974 Mon Sep 17 00:00:00 2001 From: Kartik Joshi Date: Mon, 15 Jan 2024 20:43:15 +0530 Subject: [PATCH] attestation-service: Replace anyhow error crate with thiserror crate Fixes: #231 Signed-off-by: Kartik Joshi --- .../attestation-service/src/bin/grpc/mod.rs | 23 ++++++++--- .../attestation-service/src/bin/restful-as.rs | 41 ++++++++++++++----- .../attestation-service/src/config.rs | 25 +++++++---- .../attestation-service/src/lib.rs | 34 ++++++++++----- .../attestation-service/src/rvps/builtin.rs | 7 ++-- .../attestation-service/src/rvps/grpc.rs | 6 +-- .../attestation-service/src/rvps/mod.rs | 25 +++++++++-- 7 files changed, 117 insertions(+), 44 deletions(-) diff --git a/attestation-service/attestation-service/src/bin/grpc/mod.rs b/attestation-service/attestation-service/src/bin/grpc/mod.rs index cd22fa7617..c732c207a9 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(#[source] 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)).map_err(GrpcError::Config)?, 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..c94f793518 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,30 @@ 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("read HTTPS private key")] + ReadHttpsKey(#[source] std::io::Error), + #[error("read HTTPS private key from pem")] + ReadHttpsKeyFromPem(#[source] openssl::error::ErrorStack), + #[error("set private key failed")] + SetPrivateKey(#[source] openssl::error::ErrorStack), + #[error("set HTTPS public key cert")] + SetHttpsCert(#[source] openssl::error::ErrorStack), + #[error("io error")] + IO(#[from] std::io::Error), + #[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 +81,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."); @@ -83,16 +105,16 @@ async fn main() -> Result<()> { let prikey = tokio::fs::read(prikey) .await - .context("read HTTPS private key")?; + .map_err(RestfulError::ReadHttpsKey)?; let prikey = - PKey::private_key_from_pem(&prikey).context("read HTTPS private key from pem")?; + PKey::private_key_from_pem(&prikey).map_err(RestfulError::ReadHttpsKeyFromPem)?; builder .set_private_key(&prikey) - .context("set private key failed")?; + .map_err(RestfulError::SetPrivateKey)?; builder .set_certificate_chain_file(pubkey_cert) - .context("set HTTPS public key cert")?; + .map_err(RestfulError::SetHttpsCert)?; log::info!("starting HTTPS server at https://{}", cli.socket); server.bind_openssl(cli.socket, builder)?.run() } @@ -105,6 +127,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 1c4ec5ebda..df27eb96cb 100644 --- a/attestation-service/attestation-service/src/config.rs +++ b/attestation-service/attestation-service/src/config.rs @@ -1,10 +1,10 @@ use crate::rvps::RvpsConfig; use crate::token::{AttestationTokenBrokerType, AttestationTokenConfig}; -use anyhow::{anyhow, Result}; use serde::Deserialize; 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"; @@ -31,6 +31,18 @@ pub struct Config { pub attestation_token_config: AttestationTokenConfig, } +#[derive(Error, Debug)] +pub enum ConfigError { + #[error("io error")] + IO(#[from] std::io::Error), + #[error("failed to parse AS config file")] + FileParse(#[source] std::io::Error), + #[error("failed to parse AS config file")] + JsonFileParse(#[source] serde_json::Error), + #[error("Illegal format of the content of the configuration file")] + SerdeJson(#[from] serde_json::Error), +} + impl Default for Config { // Construct a default instance of `Config` fn default() -> Config { @@ -63,12 +75,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).map_err(ConfigError::FileParse)?; + serde_json::from_reader::(file).map_err(ConfigError::JsonFileParse) } } diff --git a/attestation-service/attestation-service/src/lib.rs b/attestation-service/attestation-service/src/lib.rs index 6eff7b5c03..18b9136a8c 100644 --- a/attestation-service/attestation-service/src/lib.rs +++ b/attestation-service/attestation-service/src/lib.rs @@ -17,12 +17,13 @@ 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,20 @@ pub enum Data { Structured(Value), } +#[derive(Error, Debug)] +pub enum ServiceError { + #[error("io error")] + IO(#[from] std::io::Error), + #[error("Create AS work dir failed")] + CreateDir(#[source] std::io::Error), + #[error("Policy Engine is not supported")] + ParseError(#[source] strum::ParseError), + #[error("Create rvps failed.")] + Rvps(#[source] RvpsError), + #[error("Error")] + Anyhow(#[from] anyhow::Error), +} + pub struct AttestationService { _config: Config, policy_engine: Box, @@ -86,20 +101,20 @@ 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: {:?}")?; + .map_err(ServiceError::CreateDir)?; } let policy_engine = PolicyEngineType::from_str(&config.policy_engine) - .map_err(|_| anyhow!("Policy Engine {} is not supported", &config.policy_engine))? + .map_err(ServiceError::ParseError)? .to_policy_engine(config.work_dir.as_path())?; let rvps = rvps::initialize_rvps_client(&config.rvps_config) .await - .context("create rvps failed.")?; + .map_err(ServiceError::Rvps)?; let token_broker = config .attestation_token_broker @@ -114,11 +129,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. diff --git a/attestation-service/attestation-service/src/rvps/builtin.rs b/attestation-service/attestation-service/src/rvps/builtin.rs index 722c40a9ea..055dd51307 100644 --- a/attestation-service/attestation-service/src/rvps/builtin.rs +++ b/attestation-service/attestation-service/src/rvps/builtin.rs @@ -1,9 +1,9 @@ +use super::RvpsApi; use anyhow::*; use async_trait::async_trait; +use core::result::Result::Ok; use reference_value_provider_service::{Config, Core}; -use super::RvpsApi; - pub struct Rvps { core: Core, } @@ -18,7 +18,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 de8c9b0667..ce11da61fd 100644 --- a/attestation-service/attestation-service/src/rvps/mod.rs +++ b/attestation-service/attestation-service/src/rvps/mod.rs @@ -3,11 +3,12 @@ // SPDX-License-Identifier: Apache-2.0 // -use anyhow::*; +use anyhow::Result; use log::{info, warn}; use reference_value_provider_service::config::{Config as RvpsCrateConfig, DEFAULT_STORAGE_TYPE}; use serde::Deserialize; use serde_json::{json, Value}; +use thiserror::Error; /// The interfaces of Reference Value Provider Service /// * `verify_and_extract` is responsible for verify a message and @@ -72,7 +73,23 @@ impl Default for RvpsConfig { } } -pub async fn initialize_rvps_client(config: &RvpsConfig) -> Result> { +#[derive(Error, Debug)] +pub enum RvpsError { + #[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), +} + +pub async fn initialize_rvps_client( + config: &RvpsConfig, +) -> Result, RvpsError> { cfg_if::cfg_if! { if #[cfg(feature = "rvps-grpc")] { if !config.remote_addr.is_empty() { @@ -85,7 +102,7 @@ pub async fn initialize_rvps_client(config: &RvpsConfig) -> Result) } else { - Err(anyhow!("either feature `rvps-grpc` or `rvps-builtin` should be enabled.")) + return RvpsError::FeatureNotEnabled; } } } @@ -93,7 +110,7 @@ pub async fn initialize_rvps_client(config: &RvpsConfig) -> Result) } else { - Err(anyhow!("either feature `rvps-grpc` or `rvps-builtin` should be enabled.")) + return RvpsError::FeatureNotEnabled; } } }