Skip to content

Commit

Permalink
attestation-service: Replace anyhow error crate with thiserror crate
Browse files Browse the repository at this point in the history
Fixes: #231
Signed-off-by: Kartik Joshi <kartikjoshi@microsoft.com>
  • Loading branch information
kartikjoshi21 committed Mar 11, 2024
1 parent 9b8ef6c commit 4a8f6b3
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 44 deletions.
23 changes: 17 additions & 6 deletions attestation-service/attestation-service/src/bin/grpc/mod.rs
Original file line number Diff line number Diff line change
@@ -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};
Expand Down Expand Up @@ -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<String>) -> Result<Self> {
pub async fn new(config_path: Option<String>) -> Result<Self, GrpcError> {
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(),
};

Expand Down Expand Up @@ -222,7 +233,7 @@ impl ReferenceValueProviderService for Arc<RwLock<AttestationServer>> {
}
}

pub async fn start(socket: SocketAddr, config_path: Option<String>) -> Result<()> {
pub async fn start(socket: SocketAddr, config_path: Option<String>) -> Result<(), GrpcError> {
info!("Listen socket: {}", &socket);

let attestation_server = Arc::new(RwLock::new(AttestationServer::new(config_path).await?));
Expand Down
41 changes: 31 additions & 10 deletions attestation-service/attestation-service/src/bin/restful-as.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
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::{
pkey::PKey,
ssl::{SslAcceptor, SslMethod},
};
use strum::{AsRefStr, EnumString};
use thiserror::Error;
use tokio::sync::RwLock;

use crate::restful::{attestation, set_policy};
Expand Down Expand Up @@ -49,17 +50,38 @@ 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();

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.");
Expand All @@ -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()
}
Expand All @@ -105,6 +127,5 @@ async fn main() -> Result<()> {
};

server.await?;

Ok(())
}
25 changes: 17 additions & 8 deletions attestation-service/attestation-service/src/config.rs
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -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 {
Expand Down Expand Up @@ -63,12 +75,9 @@ impl TryFrom<&Path> for Config {
/// "duration_min": 5
/// }
/// }
type Error = anyhow::Error;
fn try_from(config_path: &Path) -> Result<Self, Self::Error> {
let file = File::open(config_path)
.map_err(|e| anyhow!("failed to open AS config file {}", e.to_string()))?;

serde_json::from_reader::<File, Config>(file)
.map_err(|e| anyhow!("failed to parse AS config file {}", e.to_string()))
type Error = ConfigError;
fn try_from(config_path: &Path) -> Result<Self, ConfigError> {
let file = File::open(config_path).map_err(ConfigError::FileParse)?;
serde_json::from_reader::<File, Config>(file).map_err(ConfigError::JsonFileParse)
}
}
34 changes: 24 additions & 10 deletions attestation-service/attestation-service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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<dyn PolicyEngine + Send + Sync>,
Expand All @@ -86,20 +101,20 @@ pub struct AttestationService {

impl AttestationService {
/// Create a new Attestation Service instance.
pub async fn new(config: Config) -> Result<Self> {
pub async fn new(config: Config) -> Result<Self, ServiceError> {
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
Expand All @@ -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.
Expand Down
7 changes: 4 additions & 3 deletions attestation-service/attestation-service/src/rvps/builtin.rs
Original file line number Diff line number Diff line change
@@ -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,
}
Expand All @@ -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<Vec<String>> {
Expand Down
6 changes: 3 additions & 3 deletions attestation-service/attestation-service/src/rvps/grpc.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use anyhow::*;
use crate::rvps::RvpsError;
use anyhow::{Context, Result};
use tokio::sync::Mutex;

use self::rvps_api::{
Expand All @@ -17,15 +18,14 @@ pub struct Agent {
}

impl Agent {
pub async fn new(addr: &str) -> Result<Self> {
pub async fn new(addr: &str) -> Result<Self, RvpsError> {
Ok(Self {
client: Mutex::new(
ReferenceValueProviderServiceClient::connect(addr.to_string()).await?,
),
})
}
}

#[async_trait::async_trait]
impl RvpsApi for Agent {
async fn verify_and_extract(&mut self, message: &str) -> Result<()> {
Expand Down
25 changes: 21 additions & 4 deletions attestation-service/attestation-service/src/rvps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -72,7 +73,23 @@ impl Default for RvpsConfig {
}
}

pub async fn initialize_rvps_client(config: &RvpsConfig) -> Result<Box<dyn RvpsApi + Send + Sync>> {
#[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<Box<dyn RvpsApi + Send + Sync>, RvpsError> {
cfg_if::cfg_if! {
if #[cfg(feature = "rvps-grpc")] {
if !config.remote_addr.is_empty() {
Expand All @@ -85,15 +102,15 @@ pub async fn initialize_rvps_client(config: &RvpsConfig) -> Result<Box<dyn RvpsA
warn!("No RVPS address provided and will launch a built-in rvps");
Ok(Box::new(builtin::Rvps::new(config.clone().into())?) as Box<dyn RvpsApi + Send + Sync>)
} else {
Err(anyhow!("either feature `rvps-grpc` or `rvps-builtin` should be enabled."))
return RvpsError::FeatureNotEnabled;
}
}
}
} else if #[cfg(feature = "rvps-builtin")] {
info!("launch a built-in RVPS.");
Ok(Box::new(builtin::Rvps::new(config.clone().into())) as Box<dyn RvpsApi + Send + Sync>)
} else {
Err(anyhow!("either feature `rvps-grpc` or `rvps-builtin` should be enabled."))
return RvpsError::FeatureNotEnabled;
}
}
}

0 comments on commit 4a8f6b3

Please sign in to comment.