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 Feb 26, 2024
1 parent 6a9be1c commit 57e7d2e
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 63 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(#[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<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))?,
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
42 changes: 24 additions & 18 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,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("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 @@ -81,18 +95,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()
}
Expand All @@ -105,6 +112,5 @@ async fn main() -> Result<()> {
};

server.await?;

Ok(())
}
21 changes: 13 additions & 8 deletions attestation-service/attestation-service/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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 {
Expand Down Expand Up @@ -65,12 +73,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)?;
Ok(serde_json::from_reader::<File, Config>(file)?)
}
}
47 changes: 25 additions & 22 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,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<dyn PolicyEngine + Send + Sync>,
Expand All @@ -86,22 +99,15 @@ 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: {:?}")?;
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
Expand All @@ -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.
Expand Down Expand Up @@ -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),
Expand All @@ -170,8 +174,7 @@ 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)?;

Expand Down
7 changes: 5 additions & 2 deletions attestation-service/attestation-service/src/rvps/builtin.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -9,7 +11,7 @@ pub struct Rvps {
}

impl Rvps {
pub fn new(store_type: &str) -> Result<Self> {
pub fn new(store_type: &str) -> Result<Self, RvpsError> {
let core = Core::new(store_type)?;
Ok(Self { core })
}
Expand All @@ -18,7 +20,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
23 changes: 19 additions & 4 deletions attestation-service/attestation-service/src/rvps/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -55,10 +56,24 @@ impl Default for RvpsConfig {
}
}

#[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),
}

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<Box<dyn RvpsApi + Send + Sync>> {
pub async fn to_rvps(&self) -> Result<Box<dyn RvpsApi + Send + Sync>, RvpsError> {
cfg_if::cfg_if! {
if #[cfg(feature = "rvps-grpc")] {
if !self.remote_addr.is_empty() {
Expand All @@ -70,15 +85,15 @@ 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<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(&self.store_type)) as Box<dyn RvpsApi + Send + Sync>)
} else {
Err(anyhow!("either feature `rvps-grpc` or `rvps-builtin` should be enabled."))
return RvpsError::FeatureNotEnabled;
}
}
}
Expand Down

0 comments on commit 57e7d2e

Please sign in to comment.