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 22, 2024
1 parent 6a9be1c commit 693500c
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 76 deletions.
24 changes: 17 additions & 7 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,22 @@ fn to_kbs_tee(tee: GrpcTee) -> Tee {
}
}

#[derive(Error, Debug)]
pub enum GrpcError {
#[error("Read AS config file failed: {0}")]
Config(#[from] ConfigError),
#[error("Creating attestation service failed: {0}")]
Service(#[from] ServiceError),
}

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 +231,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 All @@ -231,6 +240,7 @@ pub async fn start(socket: SocketAddr, config_path: Option<String>) -> Result<()
.add_service(AttestationServiceServer::new(attestation_server.clone()))
.add_service(ReferenceValueProviderServiceServer::new(attestation_server))
.serve(socket)
.await?;
.await
.map_err(|e| ServiceError::StartService(e.to_string()))?;
Ok(())
}
45 changes: 27 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: {0}: {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("io error")]
IO(#[from] std::io::Error),
#[error("Failed to start server: {0}")]
StartServer(String),
}

#[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 @@ -104,7 +111,9 @@ async fn main() -> Result<()> {
}
};

server.await?;
server
.await
.map_err(|e| RestfulError::StartServer(e.to_string()))?;

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)?)
}
}
69 changes: 39 additions & 30 deletions attestation-service/attestation-service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -77,6 +78,24 @@ pub enum Data {
Structured(Value),
}

#[derive(Error, Debug)]
pub enum ServiceError {
#[error("io error: {0}")]
IO(#[from] std::io::Error),
#[error("Parse error:{0}")]
ParseError(#[from] strum::ParseError),
#[error("Policy Engine {0} is not supported")]
UnsupportedPolicyEngine(String),
#[error("Create rvps failed.")]
Rvps(#[from] RvpsError),
#[error("Set Policy failed {0}")]
SetPolicyFailed(String),
#[error("Starting service failed: {0}")]
StartService(String),
#[error("token broker error: {0}")]
TokenBroker(String),
}

pub struct AttestationService {
_config: Config,
policy_engine: Box<dyn PolicyEngine + Send + Sync>,
Expand All @@ -86,26 +105,21 @@ 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))?
.to_policy_engine(config.work_dir.as_path())?;
let policy_engine = PolicyEngineType::from_str(&config.policy_engine)?
.to_policy_engine(config.work_dir.as_path())
.map_err(|e| ServiceError::UnsupportedPolicyEngine(e.to_string()))?;

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
.to_token_broker(config.attestation_token_config.clone())?;
.to_token_broker(config.attestation_token_config.clone())
.map_err(|e| ServiceError::TokenBroker(e.to_string()))?;

Ok(Self {
_config: config,
Expand All @@ -116,11 +130,13 @@ impl AttestationService {
}

/// Set Attestation Verification Policy.
pub async fn set_policy(&mut self, input: SetPolicyInput) -> Result<()> {
pub async fn set_policy(&mut self, input: SetPolicyInput) -> Result<(), ServiceError> {
self.policy_engine
.set_policy(input)
.await
.map_err(|e| anyhow!("Cannot Set Policy: {:?}", e))
.map_err(|e| ServiceError::SetPolicyFailed(e.to_string()))?;

Ok(())
}

/// Evaluate Attestation Evidence.
Expand Down Expand Up @@ -153,15 +169,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,23 +185,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()
Expand Down Expand Up @@ -252,8 +262,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))
}
Expand Down
14 changes: 10 additions & 4 deletions attestation-service/attestation-service/src/rvps/builtin.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::rvps::RvpsError;
use anyhow::*;
use async_trait::async_trait;
use reference_value_provider_service::Core;
Expand All @@ -9,16 +10,21 @@ pub struct Rvps {
}

impl Rvps {
pub fn new(store_type: &str) -> Result<Self> {
let core = Core::new(store_type)?;
Ok(Self { core })
pub fn new(store_type: &str) -> Result<Self, RvpsError> {
Core::new(store_type)
.map(|core| Self::from_core(core))

Check failure on line 15 in attestation-service/attestation-service/src/rvps/builtin.rs

View workflow job for this annotation

GitHub Actions / Check

redundant closure
.map_err(|error| RvpsError::CreateRvps(error.to_string()))
}

fn from_core(core: Core) -> Self {
Self { core }
}
}

#[async_trait]
impl RvpsApi for Rvps {
async fn verify_and_extract(&mut self, message: &str) -> Result<()> {
self.core.verify_and_extract(message).await
Ok(self.core.verify_and_extract(message).await?)
}

async fn get_digests(&self, name: &str) -> Result<Vec<String>> {
Expand Down
Loading

0 comments on commit 693500c

Please sign in to comment.