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: confidential-containers#231
Signed-off-by: Kartik Joshi <kartikjoshi@microsoft.com>
  • Loading branch information
kartikjoshi21 committed Feb 15, 2024
1 parent 6a9be1c commit 75c19bd
Show file tree
Hide file tree
Showing 7 changed files with 133 additions and 65 deletions.
22 changes: 15 additions & 7 deletions attestation-service/attestation-service/src/bin/grpc/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use anyhow::{anyhow, Result};
use thiserror::Error;
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, Tee, ServiceError};
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
use base64::Engine;
use log::{debug, info};
Expand Down Expand Up @@ -40,15 +40,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 +229,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 +238,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(())
}
40 changes: 26 additions & 14 deletions attestation-service/attestation-service/src/bin/restful-as.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
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 thiserror::Error;
use anyhow::Result;
use attestation_service::{config::Config, AttestationService, ServiceError, config::ConfigError};
use clap::{arg, command, Parser};
use log::info;
use openssl::{
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 @@ -82,19 +96,17 @@ async fn main() -> Result<()> {
let mut builder = SslAcceptor::mozilla_modern(SslMethod::tls())?;

let prikey = tokio::fs::read(prikey)
.await
.context("read HTTPS private key")?;
.await?;
let prikey =
PKey::private_key_from_pem(&prikey).context("read HTTPS private key from pem")?;
PKey::private_key_from_pem(&prikey)?;

builder
.set_private_key(&prikey)
.context("set private key failed")?;
.set_private_key(&prikey)?;
builder
.set_certificate_chain_file(pubkey_cert)
.context("set HTTPS public key cert")?;
.set_certificate_chain_file(pubkey_cert)?;
log::info!("starting HTTPS server at https://{}", cli.socket);
server.bind_openssl(cli.socket, builder)?.run()
server.bind_openssl(cli.socket, builder)?
.run()
}
_ => {
log::info!("starting HTTP server at http://{}", cli.socket);
Expand All @@ -104,7 +116,7 @@ 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,7 +3,7 @@ use crate::{
token::{AttestationTokenBrokerType, AttestationTokenConfig},
};

use anyhow::{anyhow, Result};
use thiserror::Error;
use serde::Deserialize;
use std::convert::TryFrom;
use std::fs::File;
Expand Down Expand Up @@ -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)?)
}
}
63 changes: 40 additions & 23 deletions attestation-service/attestation-service/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@ mod utils;

use crate::token::AttestationTokenBroker;

use anyhow::{anyhow, Context, Result};
use anyhow::Result;
use thiserror::Error;
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};
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("Anyhow error: {0}")]
Anyhow(#[from] anyhow::Error)
}

pub struct AttestationService {
_config: Config,
policy_engine: Box<dyn PolicyEngine + Send + Sync>,
Expand All @@ -86,22 +105,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: {:?}")?;
.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.")?;
.await?;

let token_broker = config
.attestation_token_broker
Expand All @@ -116,11 +133,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 +172,15 @@ 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")?;
parse_data(init_data, &init_data_hash_algorithm)?;

let init_data_hash = match &init_data {
Some(data) => InitDataHash::Value(data),
Expand All @@ -170,23 +189,20 @@ 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))?;
.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 @@ -215,7 +231,8 @@ impl AttestationService {
},
});

let attestation_results_token = self.token_broker.issue(token_claims)?;
let attestation_results_token = self.token_broker
.issue(token_claims)?;

Ok(attestation_results_token)
}
Expand Down Expand Up @@ -253,7 +270,7 @@ fn parse_data(
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")?;
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,6 +1,7 @@
use anyhow::*;
use async_trait::async_trait;
use reference_value_provider_service::Core;
use crate::rvps::RvpsError;

use super::RvpsApi;

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))
.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
10 changes: 5 additions & 5 deletions attestation-service/attestation-service/src/rvps/grpc.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use anyhow::*;
use anyhow::Result;
use tokio::sync::Mutex;
use crate::rvps::RvpsError;

use self::rvps_api::{
reference_value_provider_service_client::ReferenceValueProviderServiceClient,
Expand All @@ -16,16 +17,16 @@ pub struct Agent {
client: Mutex<ReferenceValueProviderServiceClient<tonic::transport::Channel>>,
}


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 All @@ -37,8 +38,7 @@ impl RvpsApi for Agent {
.lock()
.await
.register_reference_value(req)
.await
.context("register failed")?;
.await?;
Ok(())
}

Expand Down
Loading

0 comments on commit 75c19bd

Please sign in to comment.