From 7a74faa54ce0352ae5da20f37db772c53b96ea3e 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 | 31 ++++++++++---- .../attestation-service/src/rvps/builtin.rs | 7 ++-- .../attestation-service/src/rvps/grpc.rs | 6 +-- .../attestation-service/src/rvps/mod.rs | 25 +++++++++-- 7 files changed, 115 insertions(+), 43 deletions(-) diff --git a/attestation-service/attestation-service/src/bin/grpc/mod.rs b/attestation-service/attestation-service/src/bin/grpc/mod.rs index e6f3ee039..a0f0947ca 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: {0}")] + Config(#[source] ConfigError), + #[error("Creating attestation service failed: {0}")] + Service(#[from] ServiceError), + #[error("tonic transport error: {0}")] + 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(), }; @@ -225,7 +236,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 de797f7e8..e2e2057ac 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: {0}")] + Service(#[from] ServiceError), + #[error("Failed to read AS config file: {0}")] + Config(#[from] ConfigError), + #[error("Openssl errorstack: {0}")] + Openssl(#[from] openssl::error::ErrorStack), + #[error("failed to read HTTPS private key: {0}")] + ReadHttpsKey(#[source] std::io::Error), + #[error("failed to get HTTPS private key from pem: {0}")] + ReadHttpsKeyFromPem(#[source] openssl::error::ErrorStack), + #[error("set private key failed: {0}")] + SetPrivateKey(#[source] openssl::error::ErrorStack), + #[error("set HTTPS public key cert: {0}")] + SetHttpsCert(#[source] openssl::error::ErrorStack), + #[error("io error: {0}")] + IO(#[from] std::io::Error), + #[error(transparent)] + 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 1c4ec5ebd..e2becb1b6 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: {0}")] + IO(#[from] std::io::Error), + #[error("failed to parse AS config file: {0}")] + FileParse(#[source] std::io::Error), + #[error("failed to parse AS config file: {0}")] + JsonFileParse(#[source] serde_json::Error), + #[error("Illegal format of the content of the configuration file: {0}")] + 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)?; + 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 c5472e719..f25f948d9 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, info}; 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: {0}")] + IO(#[from] std::io::Error), + #[error("Create AS work dir failed: {0}")] + CreateDir(#[source] std::io::Error), + #[error("Policy Engine is not supported: {0}")] + UnsupportedPolicy(#[source] strum::ParseError), + #[error("Create rvps failed: {0}")] + Rvps(#[source] RvpsError), + #[error(transparent)] + 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::UnsupportedPolicy)? .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 @@ -115,10 +130,8 @@ 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)) + 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 722c40a9e..055dd5130 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 e74988482..5ddc232b5 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 de8c9b066..fe43a7f15 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: {0}")] + SerdeJson(#[from] serde_json::Error), + #[error("Returned status: {0}")] + Status(#[from] tonic::Status), + #[error("tonic transport error: {0}")] + TonicTransport(#[from] tonic::transport::Error), + #[error(transparent)] + 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; } } }