From ce2e832ecc7ec272acb82dd32638506e5d60fbb6 Mon Sep 17 00:00:00 2001 From: houmingyong Date: Tue, 26 Nov 2024 11:24:16 +0800 Subject: [PATCH 1/2] fix multi thread request-as generate challenge --- ...-request-as-generate-challenge-and-v.patch | 438 ++++++++++++++++++ secGear.spec | 6 +- 2 files changed, 443 insertions(+), 1 deletion(-) create mode 100644 0085-fix-multi-thread-request-as-generate-challenge-and-v.patch diff --git a/0085-fix-multi-thread-request-as-generate-challenge-and-v.patch b/0085-fix-multi-thread-request-as-generate-challenge-and-v.patch new file mode 100644 index 0000000..e661b9e --- /dev/null +++ b/0085-fix-multi-thread-request-as-generate-challenge-and-v.patch @@ -0,0 +1,438 @@ +From f495edff1e1077f209596bca7f83a614ea5cd139 Mon Sep 17 00:00:00 2001 +From: houmingyong +Date: Fri, 6 Sep 2024 10:38:09 +0800 +Subject: [PATCH] fix multi-thread request as generate challenge and verify + report error + +Signed-off-by: houmingyong +--- + .../attestation/attestation-agent/Cargo.toml | 1 + + .../attestation-agent/agent/Cargo.toml | 2 + + .../attestation-agent/agent/src/lib.rs | 92 ++++++++++++------- + .../attestation-agent/agent/src/session.rs | 55 +++++++++++ + .../attestation-service/service/src/main.rs | 14 +++ + .../service/src/restapi/mod.rs | 32 ++++++- + .../service/src/result/mod.rs | 7 +- + .../attestation/attestation-types/src/lib.rs | 2 + + 8 files changed, 169 insertions(+), 36 deletions(-) + create mode 100644 service/attestation/attestation-agent/agent/src/session.rs + +diff --git a/service/attestation/attestation-agent/Cargo.toml b/service/attestation/attestation-agent/Cargo.toml +index bdc7b120..f6f31b18 100644 +--- a/service/attestation/attestation-agent/Cargo.toml ++++ b/service/attestation/attestation-agent/Cargo.toml +@@ -24,6 +24,7 @@ jsonwebtoken = "9.3.0" + thiserror = "1.0" + actix-web = "4.5" + clap = { version = "4.5.7", features = ["derive"] } ++scc = "2.1" + + verifier = {path = "../attestation-service/verifier", default-features = false} + attestation-types = {path = "../attestation-types"} +diff --git a/service/attestation/attestation-agent/agent/Cargo.toml b/service/attestation/attestation-agent/agent/Cargo.toml +index e29f89be..d2450c87 100644 +--- a/service/attestation/attestation-agent/agent/Cargo.toml ++++ b/service/attestation/attestation-agent/agent/Cargo.toml +@@ -41,6 +41,8 @@ base64-url.workspace = true + thiserror.workspace = true + actix-web.workspace = true + clap.workspace = true ++scc.workspace = true ++attestation-types.workspace = true + + attester = { path = "../attester" } + token_verifier = { path = "../token" } +diff --git a/service/attestation/attestation-agent/agent/src/lib.rs b/service/attestation/attestation-agent/agent/src/lib.rs +index 393914d6..f6e03c6c 100644 +--- a/service/attestation/attestation-agent/agent/src/lib.rs ++++ b/service/attestation/attestation-agent/agent/src/lib.rs +@@ -34,9 +34,16 @@ pub type TeeClaim = serde_json::Value; + use verifier::{Verifier, VerifierAPIs}; + + #[cfg(not(feature = "no_as"))] +-use {serde_json::json, reqwest, base64_url}; ++use { ++ serde_json::json, ++ reqwest::header::{HeaderMap, HeaderValue}, ++ base64_url ++}; + + pub use attester::EvidenceRequest; ++mod session; ++use session::{SessionMap, Session}; ++use attestation_types::SESSION_TIMEOUT_MIN; + + pub type AsTokenClaim = TokenRawData; + +@@ -171,6 +178,7 @@ impl TryFrom<&Path> for AAConfig { + #[derive(Debug)] + pub struct AttestationAgent { + config: AAConfig, ++ as_client_sessions: SessionMap, + } + + #[allow(dead_code)] +@@ -186,8 +194,20 @@ impl AttestationAgent { + AAConfig::default() + } + }; ++ let as_client_sessions = SessionMap::new(); ++ let sessions = as_client_sessions.clone(); ++ tokio::spawn(async move { ++ loop { ++ tokio::time::sleep(std::time::Duration::from_secs(60)).await; ++ sessions ++ .session_map ++ .retain_async(|_, v| !v.is_expired()) ++ .await; ++ } ++ }); + Ok(AttestationAgent { + config, ++ as_client_sessions, + }) + } + +@@ -197,16 +217,33 @@ impl AttestationAgent { + evidence: &[u8], + policy_id: Option> + ) -> Result { ++ let challenge = base64_url::encode(challenge); + let request_body = json!({ +- "challenge": base64_url::encode(challenge), ++ "challenge": challenge, + "evidence": base64_url::encode(evidence), + "policy_id": policy_id, + }); ++ let mut map = HeaderMap::new(); ++ map.insert("Content-Type", HeaderValue::from_static("application/json")); ++ let mut client = reqwest::Client::new(); ++ if !self.as_client_sessions.session_map.is_empty() { ++ let session = self.as_client_sessions ++ .session_map ++ .get_async(&challenge) ++ .await; ++ match session { ++ Some(entry) => { ++ map.insert("as-challenge", HeaderValue::from_static("as")); ++ client = entry.get().as_client.clone() ++ }, ++ None => log::info!("challenge is not as generate"), ++ } ++ } + + let attest_endpoint = format!("{}/attestation", self.config.svr_url); +- let res = reqwest::Client::new() ++ let res = client + .post(attest_endpoint) +- .header("Content-Type", "application/json") ++ .headers(map) + .json(&request_body) + .send() + .await?; +@@ -249,16 +286,18 @@ impl AttestationAgent { + } + async fn get_challenge_from_as(&self) -> Result { + let challenge_endpoint = format!("{}/challenge", self.config.svr_url); +- let res = reqwest::Client::new() ++ let client = reqwest::Client::builder() ++ .cookie_store(true) ++ .build()?; ++ let res = client + .get(challenge_endpoint) + .header("Content-Type", "application/json") + .header("content-length", 0) +- //.json(&request_body) + .send() + .await?; + let challenge = match res.status() { + reqwest::StatusCode::OK => { +- let respone = res.text().await?; ++ let respone: String = res.json().await.unwrap(); + log::debug!("get challenge success, AS Response: {:?}", respone); + respone + } +@@ -267,6 +306,8 @@ impl AttestationAgent { + bail!("get challenge Failed") + } + }; ++ let session = Session::new(challenge.clone(), client, SESSION_TIMEOUT_MIN)?; ++ self.as_client_sessions.insert(session); + Ok(challenge) + } + } +@@ -274,12 +315,19 @@ impl AttestationAgent { + + // attestation agent c interface + use safer_ffi::prelude::*; +-use futures::executor::block_on; + use tokio::runtime::Runtime; + ++#[ffi_export] ++pub fn init_env_logger(c_level: Option<&repr_c::String>) { ++ let level = match c_level { ++ Some(level) => &level, ++ None => "info", ++ }; ++ env_logger::init_from_env(env_logger::Env::new().default_filter_or(level)); ++} ++ + #[ffi_export] + pub fn get_report(c_challenge: Option<&repr_c::Vec>, c_ima: &repr_c::TaggedOption) -> repr_c::Vec { +- env_logger::init_from_env(env_logger::Env::new().default_filter_or("info")); + log::debug!("input challenge: {:?}, ima: {:?}", c_challenge, c_ima); + let ima = match c_ima { + repr_c::TaggedOption::None => false, +@@ -295,11 +343,12 @@ pub fn get_report(c_challenge: Option<&repr_c::Vec>, c_ima: &repr_c::TaggedO + challenge: challenge, + ima: Some(ima), + }; +- ++ let rt = Runtime::new().unwrap(); + let fut = async { + AttestationAgent::new(Some(DEFAULT_AACONFIG_FILE.to_string())).unwrap().get_evidence(input).await + }; +- let report: Vec = match block_on(fut) { ++ let ret = rt.block_on(fut); ++ let report: Vec = match ret { + Ok(report) => report, + Err(e) => { + log::error!("get report failed {:?}", e); +@@ -357,24 +406,3 @@ pub fn generate_headers() -> ::std::io::Result<()> { + .to_file("./c_header/rust_attestation_agent.h")? + .generate() + } +- +- +-#[cfg(test)] +-mod tests { +- use crate::*; +- +- #[test] +- fn aa_new_no_conf_path() { +- let aa = AttestationAgent::new(None).unwrap(); +- assert_eq!(aa.config.svr_url, "http://127.0.0.1:8080"); +- assert_eq!(aa.config.token_cfg.cert, "/etc/attestation/attestation-agent/as_cert.pem"); +- assert_eq!(aa.config.token_cfg.iss, "openEulerAS"); +- } +- +- #[test] +- fn aa_new_with_example_conf() { +- let aa = AttestationAgent::new(Some("attestation-agent.conf".to_string())).unwrap(); +- assert_eq!(aa.config.token_cfg.cert, "/home/cert/as_cert.pem"); +- assert_eq!(aa.config.token_cfg.iss, "oeas"); +- } +-} +diff --git a/service/attestation/attestation-agent/agent/src/session.rs b/service/attestation/attestation-agent/agent/src/session.rs +new file mode 100644 +index 00000000..5e1c1fc5 +--- /dev/null ++++ b/service/attestation/attestation-agent/agent/src/session.rs +@@ -0,0 +1,55 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. ++ * secGear is licensed under the Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR ++ * PURPOSE. ++ * See the Mulan PSL v2 for more details. ++ */ ++use actix_web::cookie::{time::{Duration, OffsetDateTime}}; ++use scc::HashMap; ++use anyhow::Result; ++ ++#[derive(Debug, Clone)] ++pub struct Session { ++ pub challenge: String, ++ pub as_client: reqwest::Client, ++ timeout: OffsetDateTime, ++ // pub token: Option, ++} ++ ++impl Session { ++ pub fn new(challenge: String, as_client: reqwest::Client, timeout_m: i64) -> Result { ++ ++ let timeout = OffsetDateTime::now_utc() + Duration::minutes(timeout_m); ++ // let token = None; ++ Ok(Session { ++ challenge, ++ as_client, ++ timeout, ++ // token, ++ }) ++ } ++ pub fn is_expired(&self) -> bool { ++ return self.timeout < OffsetDateTime::now_utc(); ++ } ++} ++ ++#[derive(Debug, Clone)] ++pub struct SessionMap { ++ pub session_map: HashMap, ++} ++ ++impl SessionMap { ++ pub fn new() -> Self { ++ SessionMap { ++ session_map: HashMap::new(), ++ } ++ } ++ pub fn insert(&self, session: Session) { ++ let _ = self.session_map.insert(session.challenge.clone(), session); ++ } ++} +\ No newline at end of file +diff --git a/service/attestation/attestation-service/service/src/main.rs b/service/attestation/attestation-service/service/src/main.rs +index 3ced10b9..88941b84 100644 +--- a/service/attestation/attestation-service/service/src/main.rs ++++ b/service/attestation/attestation-service/service/src/main.rs +@@ -15,6 +15,7 @@ use attestation_service::AttestationService; + mod restapi; + use restapi::{get_challenge, attestation, reference, get_policy, set_policy}; + mod session; ++use session::SessionMap; + + use anyhow::Result; + use env_logger; +@@ -54,11 +55,24 @@ async fn main() -> Result<()> { + + let cli = Cli::parse(); + let server:AttestationService = AttestationService::new(Some(cli.config)).unwrap(); ++ let session_map = web::Data::new(SessionMap::new()); ++ ++ let sessions_clone = session_map.clone(); ++ tokio::spawn(async move { ++ loop { ++ tokio::time::sleep(std::time::Duration::from_secs(60)).await; ++ sessions_clone ++ .session_map ++ .retain_async(|_, v| !v.is_expired()) ++ .await; ++ } ++ }); + + let service = web::Data::new(Arc::new(RwLock::new(server))); + HttpServer::new(move || { + App::new() + .app_data(web::Data::clone(&service)) ++ .app_data(web::Data::clone(&session_map)) + .service(get_challenge) + .service(attestation) + .service(reference) +diff --git a/service/attestation/attestation-service/service/src/restapi/mod.rs b/service/attestation/attestation-service/service/src/restapi/mod.rs +index 291b8657..a7e6012b 100644 +--- a/service/attestation/attestation-service/service/src/restapi/mod.rs ++++ b/service/attestation/attestation-service/service/src/restapi/mod.rs +@@ -10,7 +10,7 @@ + * See the Mulan PSL v2 for more details. + */ + use attestation_service::AttestationService; +-use attestation_service::result::{Result}; ++use attestation_service::result::{Result, Error}; + + use actix_web::{ post, get, web, HttpResponse, HttpRequest}; + use serde::{Deserialize, Serialize}; +@@ -18,6 +18,8 @@ use std::sync::Arc; + use tokio::sync::RwLock; + use log; + use base64_url; ++use attestation_types::SESSION_TIMEOUT_MIN; ++use crate::session::{Session, SessionMap}; + + const DEFAULT_POLICY_DIR: &str = "/etc/attestation/attestation-service/policy"; + #[derive(Deserialize, Serialize, Debug)] +@@ -25,12 +27,19 @@ pub struct ChallengeRequest {} + + #[get("/challenge")] + pub async fn get_challenge( ++ map: web::Data, + service: web::Data>>, + ) -> Result { + log::debug!("challenge request"); + + let challenge = service.read().await.generate_challenge().await; +- Ok(HttpResponse::Ok().body(challenge)) ++ let session = Session::new(challenge, SESSION_TIMEOUT_MIN); ++ let response = HttpResponse::Ok() ++ .cookie(session.cookie()) ++ .json(session.challenge.clone()); ++ map.insert(session); ++ ++ Ok(response) + } + + #[derive(Deserialize, Serialize, Debug)] +@@ -42,6 +51,8 @@ pub struct AttestationRequest { + + #[post("/attestation")] + pub async fn attestation( ++ http_req: HttpRequest, ++ map: web::Data, + request: web::Json, + service: web::Data>>, + ) -> Result { +@@ -49,6 +60,23 @@ pub async fn attestation( + let request = request.0; + let challenge = request.challenge; + ++ if http_req.headers().contains_key("as-challenge") { ++ log::info!("sessions map len:{}", map.session_map.len()); ++ let cookie = http_req.cookie("oeas-session-id").ok_or(Error::CookieMissing)?; ++ let session = map ++ .session_map ++ .get_async(cookie.value()) ++ .await ++ .ok_or(Error::SessionNotFound)?; ++ if session.is_expired() { ++ return Err(Error::SessionExpired); ++ } ++ if challenge != session.challenge { ++ log::error!("request challenge:{} does not match session challenge:{}", challenge, session.challenge); ++ return Err(Error::ChallengeInvalid); ++ } ++ } ++ + let nonce = base64_url::decode(&challenge).expect("base64 decode nonce"); + let evidence = base64_url::decode(&request.evidence).expect("base64 decode evidence"); + let ids = request.policy_id; +diff --git a/service/attestation/attestation-service/service/src/result/mod.rs b/service/attestation/attestation-service/service/src/result/mod.rs +index 667e80f5..fcb1c123 100644 +--- a/service/attestation/attestation-service/service/src/result/mod.rs ++++ b/service/attestation/attestation-service/service/src/result/mod.rs +@@ -38,12 +38,15 @@ pub enum Error { + #[error("Request cookie is missing")] + CookieMissing, + +- #[error("Request cookie is not found")] +- CookieNotFound, ++ #[error("Request cookie session is not found")] ++ SessionNotFound, + + #[error("The session of request cookie is expired")] + SessionExpired, + ++ #[error("Request challenge is invalid")] ++ ChallengeInvalid, ++ + #[error(transparent)] + Other(#[from] anyhow::Error), + } +diff --git a/service/attestation/attestation-types/src/lib.rs b/service/attestation/attestation-types/src/lib.rs +index fcf1d3ee..67dcf9f8 100644 +--- a/service/attestation/attestation-types/src/lib.rs ++++ b/service/attestation/attestation-types/src/lib.rs +@@ -12,6 +12,8 @@ + use serde::{Serialize, Deserialize}; + use serde_json::Value; + ++pub const SESSION_TIMEOUT_MIN: i64 = 1; ++ + #[derive(Debug, Serialize, Deserialize)] + pub struct VirtccaEvidence { + pub evidence: Vec, +-- +2.46.0 + diff --git a/secGear.spec b/secGear.spec index 1e0dcc3..489b5eb 100644 --- a/secGear.spec +++ b/secGear.spec @@ -1,6 +1,6 @@ Name: secGear Version: 0.1.0 -Release: 48 +Release: 49 Summary: secGear is an SDK to develop confidential computing apps based on hardware enclave features @@ -94,6 +94,7 @@ Patch80: 0081-modify-default-agent-config.patch Patch81: 0082-optimize-ima-verify.patch Patch82: 0083-optimize-log-level.patch Patch83: 0084-fix-concurrent-request-error-to-aa-or-as.patch +Patch84: 0085-fix-multi-thread-request-as-generate-challenge-and-v.patch BuildRequires: gcc python automake autoconf libtool @@ -291,6 +292,9 @@ popd systemctl restart rsyslog %changelog +* Tue Nov 26 2024 houmingyong - 0.1.0-49 +- fix multi thread request-as generate challenge + * Tue Nov 26 2024 houmingyong - 0.1.0-48 - fix concurrent request error to aa or as -- Gitee From f2fa9b7fc624189a8714f23c8a5dd66f099dc402 Mon Sep 17 00:00:00 2001 From: houmingyong Date: Tue, 26 Nov 2024 11:42:52 +0800 Subject: [PATCH 2/2] add error type for api --- 0086-add-error-type-for-api.patch | 680 ++++++++++++++++++++++++++++++ secGear.spec | 6 +- 2 files changed, 685 insertions(+), 1 deletion(-) create mode 100644 0086-add-error-type-for-api.patch diff --git a/0086-add-error-type-for-api.patch b/0086-add-error-type-for-api.patch new file mode 100644 index 0000000..a429559 --- /dev/null +++ b/0086-add-error-type-for-api.patch @@ -0,0 +1,680 @@ +From fae0b444629f5cd20a544d5513870d9435f72ef0 Mon Sep 17 00:00:00 2001 +From: xuraoqing +Date: Mon, 9 Sep 2024 19:23:48 +0800 +Subject: [PATCH] add error type for api + +Signed-off-by: houmingyong +--- + .../agent/src/bin/aa-test/main.rs | 1 + + .../attestation-agent/agent/src/lib.rs | 24 +++++++-- + .../agent/src/restapi/mod.rs | 31 +++++++----- + .../attestation-agent/agent/src/result/mod.rs | 8 +++ + .../attestation-agent/token/Cargo.toml | 3 +- + .../attestation-agent/token/src/lib.rs | 49 +++++++++---------- + .../policy/src/policy_engine.rs | 3 ++ + .../attestation-service/reference/Cargo.toml | 3 +- + .../reference/src/reference/mod.rs | 39 ++++----------- + .../reference/src/store/mod.rs | 9 ++++ + .../attestation-service/service/src/lib.rs | 9 ++-- + .../service/src/restapi/mod.rs | 25 +++------- + .../service/src/result/mod.rs | 20 ++++++++ + .../attestation-service/token/Cargo.toml | 3 +- + .../attestation-service/token/src/lib.rs | 33 +++++++++---- + 15 files changed, 153 insertions(+), 107 deletions(-) + +diff --git a/service/attestation/attestation-agent/agent/src/bin/aa-test/main.rs b/service/attestation/attestation-agent/agent/src/bin/aa-test/main.rs +index 48e3e68e..4867a234 100644 +--- a/service/attestation/attestation-agent/agent/src/bin/aa-test/main.rs ++++ b/service/attestation/attestation-agent/agent/src/bin/aa-test/main.rs +@@ -169,6 +169,7 @@ async fn aa_proc(i: i64) { + } + status => { + log::error!("thread {} case6 verify token failed response: {:?}", i, status); ++ log::error!("thread case6 verify token failed response:{}", res.text().await.unwrap()); + } + } + } +diff --git a/service/attestation/attestation-agent/agent/src/lib.rs b/service/attestation/attestation-agent/agent/src/lib.rs +index f6e03c6c..f1c4510b 100644 +--- a/service/attestation/attestation-agent/agent/src/lib.rs ++++ b/service/attestation/attestation-agent/agent/src/lib.rs +@@ -22,6 +22,7 @@ use async_trait::async_trait; + use std::fs::File; + use std::path::Path; + use rand::RngCore; ++use thiserror; + + use attester::{Attester, AttesterAPIs}; + use token_verifier::{TokenVerifyConfig, TokenVerifier, TokenRawData}; +@@ -30,6 +31,22 @@ pub mod result; + use result::Error; + pub type TeeClaim = serde_json::Value; + ++#[derive(Debug, thiserror::Error)] ++pub enum AgentError { ++ #[error("challenge error: {0}")] ++ ChallengeError(String), ++ #[error("get evidence error: {0}")] ++ DecodeError(String), ++ #[error("get evidence error: {0}")] ++ GetEvidenceError(String), ++ #[error("verify evidence error: {0}")] ++ VerifyEvidenceError(String), ++ #[error("get token error: {0}")] ++ GetTokenError(String), ++ #[error("verify token error: {0}")] ++ VerifyTokenError(String), ++} ++ + #[cfg(feature = "no_as")] + use verifier::{Verifier, VerifierAPIs}; + +@@ -136,11 +153,8 @@ impl AttestationAgentAPIs for AttestationAgent { + + async fn verify_token(&self, token: String) -> Result { + let verifier = TokenVerifier::new(self.config.token_cfg.clone())?; +- let result = verifier.verify(&token); +- match result { +- Ok(raw_token) => Ok(raw_token as AsTokenClaim), +- Err(e) => bail!("verify token failed {:?}", e), +- } ++ let result = verifier.verify(&token)?; ++ Ok(result) + } + } + +diff --git a/service/attestation/attestation-agent/agent/src/restapi/mod.rs b/service/attestation/attestation-agent/agent/src/restapi/mod.rs +index 490242aa..0570060b 100644 +--- a/service/attestation/attestation-agent/agent/src/restapi/mod.rs ++++ b/service/attestation/attestation-agent/agent/src/restapi/mod.rs +@@ -9,7 +9,7 @@ + * PURPOSE. + * See the Mulan PSL v2 for more details. + */ +-use attestation_agent::{AttestationAgent, AttestationAgentAPIs, TokenRequest}; ++use attestation_agent::{AttestationAgent, AttestationAgentAPIs, TokenRequest, AgentError}; + use attestation_agent::result::Result; + + use actix_web::{ post, get, web, HttpResponse}; +@@ -18,7 +18,6 @@ use serde::{Deserialize, Serialize}; + use std::sync::Arc; + use tokio::sync::RwLock; + use log; +-use base64_url; + + #[derive(Deserialize, Serialize, Debug)] + struct GetChallengeRequest {} +@@ -30,7 +29,8 @@ pub async fn get_challenge( + ) -> Result { + //let request = request.0; + log::debug!("get challenge request"); +- let challenge = agent.read().await.get_challenge().await?; ++ let challenge = agent.read().await.get_challenge().await ++ .map_err(|err| AgentError::ChallengeError(err.to_string()))?; + + Ok(HttpResponse::Ok().body(challenge)) + } +@@ -49,7 +49,8 @@ pub async fn get_evidence( + ) -> Result { + let request = request.0; + log::debug!("get evidence request: {:?}", request); +- let challenge = base64_url::decode(&request.challenge).expect("base64 decode challenge"); ++ let challenge = base64_url::decode(&request.challenge) ++ .map_err(|err|AgentError::DecodeError(err.to_string()))?; + let uuid = request.uuid; + let ima = request.ima; + let input = EvidenceRequest { +@@ -57,7 +58,8 @@ pub async fn get_evidence( + challenge: challenge, + ima: ima, + }; +- let evidence = agent.read().await.get_evidence(input).await?; ++ let evidence = agent.read().await.get_evidence(input).await ++ .map_err(|err|AgentError::GetEvidenceError(err.to_string()))?; + + + Ok(HttpResponse::Ok().body(evidence)) +@@ -76,11 +78,13 @@ pub async fn verify_evidence( + ) -> Result { + let request = request.0; + log::debug!("verify evidence request: {:?}", request); +- let challenge = base64_url::decode(&request.challenge).expect("base64 decode challenge"); ++ let challenge = base64_url::decode(&"request.challenge".to_string()) ++ .map_err(|err|AgentError::DecodeError(err.to_string()))?; + let evidence = request.evidence; + let policy_id = request.policy_id; + +- let claim = agent.read().await.verify_evidence(&challenge, evidence.as_bytes(), policy_id).await?; ++ let claim = agent.read().await.verify_evidence(&challenge, evidence.as_bytes(), policy_id).await ++ .map_err(|err|AgentError::VerifyEvidenceError(err.to_string()))?; + let string_claim = serde_json::to_string(&claim)?; + + Ok(HttpResponse::Ok().body(string_claim)) +@@ -101,7 +105,8 @@ pub async fn get_token( + ) -> Result { + let request = request.0; + log::debug!("get token request: {:?}", request); +- let challenge = base64_url::decode(&request.challenge).expect("base64 decode challenge"); ++ let challenge = base64_url::decode(&request.challenge) ++ .map_err(|err|AgentError::DecodeError(err.to_string()))?; + let uuid = request.uuid; + let ima = request.ima; + let policy_id = request.policy_id; +@@ -115,8 +120,8 @@ pub async fn get_token( + policy_id: policy_id, + }; + +- let token = agent.read().await.get_token(input).await?; +- ++ let token = agent.read().await.get_token(input).await ++ .map_err(|err|AgentError::GetTokenError(err.to_string()))?; + + Ok(HttpResponse::Ok().body(token)) + } +@@ -133,8 +138,10 @@ pub async fn verify_token( + let request = request.0; + log::debug!("verify token request: {:?}", request); + +- let claim = agent.read().await.verify_token(request.token).await?; +- let string_claim = serde_json::to_string(&claim)?; ++ let claim = agent.read().await.verify_token(request.token).await ++ .map_err(|err|AgentError::VerifyTokenError(err.to_string()))?; ++ let string_claim = serde_json::to_string(&claim) ++ .map_err(|err|AgentError::VerifyTokenError(err.to_string()))?; + + Ok(HttpResponse::Ok().body(string_claim)) + } +\ No newline at end of file +diff --git a/service/attestation/attestation-agent/agent/src/result/mod.rs b/service/attestation/attestation-agent/agent/src/result/mod.rs +index f06f064b..a33be0cb 100644 +--- a/service/attestation/attestation-agent/agent/src/result/mod.rs ++++ b/service/attestation/attestation-agent/agent/src/result/mod.rs +@@ -11,6 +11,8 @@ + */ + use actix_web::{body::BoxBody, HttpResponse, ResponseError}; + ++use crate::AgentError; ++ + pub type Result = std::result::Result; + + /// libdevice error +@@ -30,6 +32,12 @@ pub enum Error { + source: actix_web::error::Error, + }, + ++ #[error("Agent error: {source:?}")] ++ Agent { ++ #[from] ++ source: AgentError, ++ }, ++ + #[error("Deserialize error: {source:?}")] + Deserialize { + #[from] +diff --git a/service/attestation/attestation-agent/token/Cargo.toml b/service/attestation/attestation-agent/token/Cargo.toml +index aa5cafcf..916f2a2e 100644 +--- a/service/attestation/attestation-agent/token/Cargo.toml ++++ b/service/attestation/attestation-agent/token/Cargo.toml +@@ -10,4 +10,5 @@ jsonwebtoken.workspace = true + serde.workspace = true + serde_json.workspace = true + anyhow.workspace = true +-attestation-types.workspace = true +\ No newline at end of file ++attestation-types.workspace = true ++thiserror.workspace = true +\ No newline at end of file +diff --git a/service/attestation/attestation-agent/token/src/lib.rs b/service/attestation/attestation-agent/token/src/lib.rs +index 50a7a7a0..37aab9eb 100644 +--- a/service/attestation/attestation-agent/token/src/lib.rs ++++ b/service/attestation/attestation-agent/token/src/lib.rs +@@ -9,12 +9,23 @@ + * PURPOSE. + * See the Mulan PSL v2 for more details. + */ +-use anyhow::{Result, bail}; + use std::path::Path; + use serde::{Deserialize, Serialize}; + use jsonwebtoken::{decode, decode_header, Algorithm, DecodingKey, Validation }; + use attestation_types::Claims; + ++#[derive(thiserror::Error, Debug)] ++pub enum VerifyError { ++ #[error("parse fail:{0:?}")] ++ CommError(#[from] jsonwebtoken::errors::Error), ++ #[error("unknown algorithm:{0}")] ++ UnknownAlg(String), ++ #[error("certificate not exist:{0}")] ++ CertNotExist(String), ++ #[error("serialize fail:{0}")] ++ SerializeFail(#[from] serde_json::error::Error), ++} ++ + #[derive(Clone, Debug, Serialize, Deserialize)] + pub struct TokenVerifyConfig { + pub cert: String, // Attestation Service cert to verify jwt token signature +@@ -52,7 +63,7 @@ pub struct TokenRawData { + } + + impl TokenVerifier { +- pub fn new(config: TokenVerifyConfig) -> Result { ++ pub fn new(config: TokenVerifyConfig) -> Result { + Ok(TokenVerifier { config }) + } + fn support_rs(alg: &Algorithm) -> bool +@@ -72,43 +83,29 @@ impl TokenVerifier { + pub fn verify( + &self, + token: &String +- ) -> Result { +- let header = match decode_header(&token) { +- Ok(h) => h, +- Err(e) => bail!("decode jwt header error {:?}", e), +- }; ++ ) -> Result { ++ let header = decode_header(&token)?; + let alg: Algorithm = header.alg; + + if !Self::support_rs(&alg) && !Self::support_ps(&alg) { +- bail!("unknown algrithm {:?}", alg); ++ return Err(VerifyError::UnknownAlg(format!("unknown algrithm {:?}", alg))); + } + if !Path::new(&self.config.cert).exists() { +- bail!("token verfify failed, {:?} cert not exist", self.config.cert); ++ return Err(VerifyError::CertNotExist(format!("{:?} not exist", self.config.cert))); + } + let cert = std::fs::read(&self.config.cert).unwrap(); + + /* 使用配置的公钥 */ +- let key_value: DecodingKey = match DecodingKey::from_rsa_pem(&cert) +- { +- Ok(key) => key, +- Err(e) => bail!("get key from pem error {:?}", e), +- }; ++ let key_value: DecodingKey = DecodingKey::from_rsa_pem(&cert)?; + + let mut validation = Validation::new(alg); + validation.set_issuer(&[self.config.iss.clone()]); + validation.validate_exp = true; + +- let data = decode::(&token, &key_value, &validation); +- match data { +- Ok(d) => { +- let header = d.header.clone(); +- let claims = d.claims.clone(); +- Ok(TokenRawData { +- header: serde_json::to_string(&header).unwrap(), +- claim: serde_json::to_string(&claims).unwrap(), +- }) +- } +- Err(e) => bail!("verfiy jwt failed {:?}", e), +- } ++ let data = decode::(&token, &key_value, &validation)?; ++ Ok(TokenRawData { ++ header: serde_json::to_string(&data.header)?, ++ claim: serde_json::to_string(&data.claims)?, ++ }) + } + } +diff --git a/service/attestation/attestation-service/policy/src/policy_engine.rs b/service/attestation/attestation-service/policy/src/policy_engine.rs +index a03a8cc8..7a8508ef 100644 +--- a/service/attestation/attestation-service/policy/src/policy_engine.rs ++++ b/service/attestation/attestation-service/policy/src/policy_engine.rs +@@ -10,6 +10,7 @@ + * See the Mulan PSL v2 for more details. + */ + use std::{collections::HashMap, fmt::Display}; ++ + #[derive(Debug)] + pub enum PolicyEngineError { + InvalidPolicy(String), +@@ -50,6 +51,8 @@ impl Display for PolicyEngineError { + } + } + ++impl std::error::Error for PolicyEngineError {} ++ + pub trait PolicyEngine { + fn evaluate( + &self, +diff --git a/service/attestation/attestation-service/reference/Cargo.toml b/service/attestation/attestation-service/reference/Cargo.toml +index b36991e7..fb0a4bbb 100644 +--- a/service/attestation/attestation-service/reference/Cargo.toml ++++ b/service/attestation/attestation-service/reference/Cargo.toml +@@ -13,4 +13,5 @@ base64.workspace = true + sled.workspace = true + openssl.workspace = true + hex.workspace = true +-lazy_static.workspace = true +\ No newline at end of file ++lazy_static.workspace = true ++thiserror.workspace = true +\ No newline at end of file +diff --git a/service/attestation/attestation-service/reference/src/reference/mod.rs b/service/attestation/attestation-service/reference/src/reference/mod.rs +index bf56c854..6ec43714 100644 +--- a/service/attestation/attestation-service/reference/src/reference/mod.rs ++++ b/service/attestation/attestation-service/reference/src/reference/mod.rs +@@ -15,6 +15,7 @@ use crate::store::{KvError, KvStore}; + use openssl::sha::sha256; + use serde::{Deserialize, Serialize}; + use serde_json::{json, Value}; ++use thiserror::{self, Error}; + + pub struct ReferenceOps { + store: Box, +@@ -40,16 +41,12 @@ pub struct Ref { + pub value: Value, + } + +-#[derive(Debug, PartialEq)] ++#[derive(Error, Debug, PartialEq)] + pub enum RefOpError { ++ #[error("reference operation error {0}")] + Err(String), +-} +-impl From for RefOpError { +- fn from(value: KvError) -> Self { +- match value { +- KvError::Err(v) => RefOpError::Err(v), +- } +- } ++ #[error("reference store error: {0:?}")] ++ StoreErr(#[from] KvError) + } + + impl ReferenceOps { +@@ -68,36 +65,20 @@ impl ReferenceOps { + fn register_reference(&mut self, reference: &Ref) -> Result<(), RefOpError> { + // generate reference key + let key = Self::generate_reference_key(reference); +- match self.store.write( ++ self.store.write( + &key, + serde_json::to_string(&reference) + .unwrap() + .as_bytes() + .as_ref(), +- ) { +- Ok(_) => { +- return Ok(()); +- } +- Err(err) => match err { +- KvError::Err(err) => { +- return Err(RefOpError::Err(err)); +- } +- }, +- } ++ )?; ++ Ok(()) + } + + fn unregister_reference(&mut self, reference: &Ref) -> Result<(), RefOpError> { + let key = Self::generate_reference_key(reference); +- match self.store.delete(&key) { +- Ok(_) => { +- return Ok(()); +- } +- Err(err) => match err { +- KvError::Err(err) => { +- return Err(RefOpError::Err(err)); +- } +- }, +- } ++ self.store.delete(&key)?; ++ Ok(()) + } + + fn query_reference(&mut self, reference: &Ref) -> Option> { +diff --git a/service/attestation/attestation-service/reference/src/store/mod.rs b/service/attestation/attestation-service/reference/src/store/mod.rs +index c8c82607..c9597f6a 100644 +--- a/service/attestation/attestation-service/reference/src/store/mod.rs ++++ b/service/attestation/attestation-service/reference/src/store/mod.rs +@@ -9,9 +9,18 @@ + * PURPOSE. + * See the Mulan PSL v2 for more details. + */ ++#[derive(Debug, PartialEq)] + pub enum KvError { + Err(String), + } ++impl std::fmt::Display for KvError { ++ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { ++ match self { ++ KvError::Err(msg) => write!(f, "kv store error:{}", msg) ++ } ++ } ++} ++impl std::error::Error for KvError {} + pub trait KvStore { + fn write(&mut self, key: &str, value: &[u8]) -> Result<(), KvError>; + fn read(&mut self, key: &str) -> Option>; +diff --git a/service/attestation/attestation-service/service/src/lib.rs b/service/attestation/attestation-service/service/src/lib.rs +index 3ec73d3d..31e6305d 100644 +--- a/service/attestation/attestation-service/service/src/lib.rs ++++ b/service/attestation/attestation-service/service/src/lib.rs +@@ -134,7 +134,7 @@ impl AttestationService { + Ok(eval) => { + for id in eval.keys() { + let val = Value::from_str(&eval[id].clone())?; +- let refs = match val.as_object().ok_or(Err(anyhow!(""))) { ++ let refs = match val.as_object().ok_or(Err(anyhow!("json value to map fail"))) { + Err(err) => { return Err(err.unwrap()); } + Ok(ret) => { ret } + }; +@@ -165,7 +165,7 @@ impl AttestationService { + // demo get signer, todo default signer + let signer = TokenSigner::new(self.config.token_cfg.clone())?; + +- signer.sign(&evl_report) ++ Ok(signer.sign(&evl_report)?) + } + + pub async fn generate_challenge(&self) -> String { +@@ -174,7 +174,6 @@ impl AttestationService { + base64_url::encode(&nonce) + } + +- // todo pub fun set policy + pub async fn set_policy(&self, + id: &String, + policy: &String, +@@ -185,7 +184,7 @@ impl AttestationService { + .set_policy(id, policy) + .await + } +- // todo pub fun get policy ++ + pub async fn get_policy(&self, + policy_dir: &String, + ) -> Result { +@@ -203,7 +202,7 @@ impl AttestationService { + Err(err) => Err(err) + } + } +- // todo pub fun import reference value ++ + pub async fn register_reference(&self, + ref_set: &String + ) -> Result<(), RefOpError> { +diff --git a/service/attestation/attestation-service/service/src/restapi/mod.rs b/service/attestation/attestation-service/service/src/restapi/mod.rs +index a7e6012b..f49d1755 100644 +--- a/service/attestation/attestation-service/service/src/restapi/mod.rs ++++ b/service/attestation/attestation-service/service/src/restapi/mod.rs +@@ -77,8 +77,8 @@ pub async fn attestation( + } + } + +- let nonce = base64_url::decode(&challenge).expect("base64 decode nonce"); +- let evidence = base64_url::decode(&request.evidence).expect("base64 decode evidence"); ++ let nonce = base64_url::decode(&challenge)?; ++ let evidence = base64_url::decode(&request.evidence)?; + let ids = request.policy_id; + let token = service.read().await.evaluate(&nonce, &evidence, &ids).await?; + +@@ -97,10 +97,8 @@ pub async fn reference( + ) -> Result { + let request = request.0; + log::debug!("reference request: {:?}", request); +- match service.read().await.register_reference(&request.refs).await { +- Ok(_) => Ok(HttpResponse::Ok().body("set reference success")), +- Err(_err) => Ok(HttpResponse::Ok().body("set reference fail")), +- } ++ service.read().await.register_reference(&request.refs).await?; ++ Ok(HttpResponse::Ok().body("set reference success")) + } + + #[derive(Deserialize, Serialize, Debug)] +@@ -120,13 +118,8 @@ pub async fn set_policy( + let policy_id = request.id.clone(); + let policy = request.policy.clone(); + let dir:String = String::from(DEFAULT_POLICY_DIR); +- match service.read().await.set_policy(&policy_id, &policy, &dir).await { +- Ok(_) => Ok(HttpResponse::Ok().body("set policy success")), +- Err(err) => { +- log::debug!("set policy error: {:?}", err); +- Ok(HttpResponse::Ok().body("set policy fail")) +- } +- } ++ service.read().await.set_policy(&policy_id, &policy, &dir).await?; ++ Ok(HttpResponse::Ok().body("set policy success")) + } + + #[get("/policy")] +@@ -136,8 +129,6 @@ pub async fn get_policy( + ) -> Result { + log::debug!("get policy request: {:?}", request); + let dir:String = String::from(DEFAULT_POLICY_DIR); +- match service.read().await.get_policy(&dir).await { +- Ok(ret) => Ok(HttpResponse::Ok().body(ret)), +- Err(_err) => Ok(HttpResponse::Ok().body("get policy fail")), +- } ++ let ret = service.read().await.get_policy(&dir).await?; ++ Ok(HttpResponse::Ok().body(ret)) + } +diff --git a/service/attestation/attestation-service/service/src/result/mod.rs b/service/attestation/attestation-service/service/src/result/mod.rs +index fcb1c123..7261d19b 100644 +--- a/service/attestation/attestation-service/service/src/result/mod.rs ++++ b/service/attestation/attestation-service/service/src/result/mod.rs +@@ -22,6 +22,26 @@ pub enum Error { + #[from] + source: std::io::Error, + }, ++ #[error("attestation error: {source:?}")] ++ DecodeError { ++ #[from] ++ source: base64::DecodeError, ++ }, ++ #[error("Policy Engine error: {source:?}")] ++ PolicyEngine { ++ #[from] ++ source: policy::policy_engine::PolicyEngineError, ++ }, ++ #[error("Reference error: {source:?}")] ++ Reference { ++ #[from] ++ source: reference::reference::RefOpError, ++ }, ++ #[error("Sign error: {source:?}")] ++ Sign { ++ #[from] ++ source: token_signer::SignError, ++ }, + + #[error("Web error: {source:?}")] + Web { +diff --git a/service/attestation/attestation-service/token/Cargo.toml b/service/attestation/attestation-service/token/Cargo.toml +index c4b885c0..029008a1 100644 +--- a/service/attestation/attestation-service/token/Cargo.toml ++++ b/service/attestation/attestation-service/token/Cargo.toml +@@ -10,4 +10,5 @@ jsonwebtoken.workspace = true + serde.workspace = true + serde_json.workspace = true + anyhow.workspace = true +-attestation-types.workspace = true +\ No newline at end of file ++attestation-types.workspace = true ++thiserror.workspace = true +\ No newline at end of file +diff --git a/service/attestation/attestation-service/token/src/lib.rs b/service/attestation/attestation-service/token/src/lib.rs +index ed41a4ec..3ee785e5 100644 +--- a/service/attestation/attestation-service/token/src/lib.rs ++++ b/service/attestation/attestation-service/token/src/lib.rs +@@ -9,7 +9,7 @@ + * PURPOSE. + * See the Mulan PSL v2 for more details. + */ +-use anyhow::{Result, bail}; ++use anyhow::{Result}; + use jsonwebtoken::{encode, get_current_timestamp, + Algorithm, EncodingKey, Header, + }; +@@ -17,7 +17,21 @@ use std::path::Path; + use serde::{Deserialize, Serialize}; + use serde_json::Value; + use attestation_types::{EvlResult, Claims}; ++use thiserror; + ++#[derive(thiserror::Error, Debug)] ++pub enum SignError { ++ #[error("get unix time fail:{0:?}")] ++ ToUnixTimeFail(#[from] std::num::TryFromIntError), ++ #[error("unsupport algorith:{0}")] ++ UnsupportAlg(String), ++ #[error("key not exist:{0}")] ++ KeyNotExist(String), ++ #[error("key content read fail:{0}")] ++ ReadKeyFail(String), ++ #[error("sign fail:{0:?}")] ++ SignFail(#[from] jsonwebtoken::errors::Error) ++} + + #[derive(Debug, Clone, Serialize, Deserialize)] + pub struct TokenSignConfig { +@@ -79,36 +93,35 @@ impl TokenSigner { + } + return false; + } +- pub fn sign(&self, report: &EvlReport) -> Result { ++ pub fn sign(&self, report: &EvlReport) -> Result { + let alg: Algorithm = self.config.alg; + let mut header = Header::new(alg); + header.typ = Some("JWT".to_string()); + let unix_time = get_current_timestamp(); + let claims: Claims = Claims { + iss: self.config.iss.clone(), +- iat: usize::try_from(unix_time).expect("unix time to usize error"), +- nbf: usize::try_from(unix_time).expect("unix time to usize error"), +- exp: usize::try_from(unix_time).expect("unix time to usize error") +- + self.config.valid_duration, ++ iat: usize::try_from(unix_time)?, ++ nbf: usize::try_from(unix_time)?, ++ exp: usize::try_from(unix_time)? + self.config.valid_duration, + evaluation_reports: report.result.clone(), + tee: report.tee.clone(), + tcb_status: report.tcb_status.clone(), + }; + if !Self::support_rs(&alg) && !Self::support_ps(&alg) { +- bail!("unknown algrithm {:?}", alg); ++ return Err(SignError::UnsupportAlg(format!("unknown algrithm {:?}", alg))); + } + if !Path::new(&self.config.key).exists() { +- bail!("token verfify failed, {:?} cert not exist", self.config.key); ++ return Err(SignError::UnsupportAlg(format!("token verfify failed, {:?} cert not exist", self.config.key))); + } + let key = std::fs::read(&self.config.key).unwrap(); + let key_value: EncodingKey = match EncodingKey::from_rsa_pem(&key) { + Ok(val) => val, +- _ => bail!("get key from input error"), ++ _ => {return Err(SignError::ReadKeyFail(format!("get key from input error")));} + }; + + let token = match encode(&header, &claims, &key_value) { + Ok(val) => val, +- Err(e) => bail!("sign jwt token error {:?}", e), ++ Err(e) => {return Err(SignError::SignFail(e));} + }; + Ok(token) + } +-- +2.46.0 + diff --git a/secGear.spec b/secGear.spec index 489b5eb..39c0409 100644 --- a/secGear.spec +++ b/secGear.spec @@ -1,6 +1,6 @@ Name: secGear Version: 0.1.0 -Release: 49 +Release: 50 Summary: secGear is an SDK to develop confidential computing apps based on hardware enclave features @@ -95,6 +95,7 @@ Patch81: 0082-optimize-ima-verify.patch Patch82: 0083-optimize-log-level.patch Patch83: 0084-fix-concurrent-request-error-to-aa-or-as.patch Patch84: 0085-fix-multi-thread-request-as-generate-challenge-and-v.patch +Patch85: 0086-add-error-type-for-api.patch BuildRequires: gcc python automake autoconf libtool @@ -292,6 +293,9 @@ popd systemctl restart rsyslog %changelog +* Tue Nov 26 2024 houmingyong - 0.1.0-50 +- add error type for api + * Tue Nov 26 2024 houmingyong - 0.1.0-49 - fix multi thread request-as generate challenge -- Gitee