From 0b24d5fae10b9933c5069f502b4319ca6b02e555 Mon Sep 17 00:00:00 2001 From: Xynnn007 Date: Mon, 6 Nov 2023 14:52:14 +0800 Subject: [PATCH 01/11] repo: delete as-types repo Signed-off-by: Xynnn007 --- Cargo.lock | 13 ------------- Cargo.toml | 2 -- attestation-service/as-types/Cargo.toml | 10 ---------- attestation-service/as-types/src/lib.rs | 10 ---------- attestation-service/attestation-service/Cargo.toml | 1 - attestation-service/attestation-service/src/lib.rs | 3 +-- .../attestation-service/src/policy_engine/mod.rs | 10 ++++++++-- .../src/policy_engine/opa/mod.rs | 3 ++- .../attestation-service/src/utils.rs | 3 ++- .../attestation-service/src/verifier/mod.rs | 3 ++- .../attestation-service/src/verifier/sgx/mod.rs | 3 +-- .../attestation-service/src/verifier/tdx/claims.rs | 3 ++- attestation-service/bin/grpc-as/Cargo.toml | 1 - attestation-service/bin/grpc-as/src/server.rs | 3 ++- kbs/src/api/Cargo.toml | 1 - kbs/src/api/src/attestation/coco/builtin.rs | 11 ++++++++--- kbs/src/api/src/attestation/coco/grpc.rs | 7 +++---- kbs/src/api/src/attestation/mod.rs | 2 +- kbs/src/api/src/http/config.rs | 4 ++-- kbs/tools/client/Cargo.toml | 2 -- 20 files changed, 34 insertions(+), 61 deletions(-) delete mode 100644 attestation-service/as-types/Cargo.toml delete mode 100644 attestation-service/as-types/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 9f68350d1..aeeb8387c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -405,7 +405,6 @@ dependencies = [ "actix-web-httpauth", "aes-gcm", "anyhow", - "as-types", "async-trait", "attestation-service", "base64 0.21.5", @@ -440,15 +439,6 @@ dependencies = [ "uuid", ] -[[package]] -name = "as-types" -version = "0.1.0" -dependencies = [ - "kbs-types", - "serde", - "serde_json", -] - [[package]] name = "asn1-rs" version = "0.5.2" @@ -536,7 +526,6 @@ name = "attestation-service" version = "0.1.0" dependencies = [ "anyhow", - "as-types", "asn1-rs", "assert-json-diff", "async-trait", @@ -1973,7 +1962,6 @@ name = "grpc-as" version = "0.1.0" dependencies = [ "anyhow", - "as-types", "async-trait", "attestation-service", "clap 3.2.25", @@ -2508,7 +2496,6 @@ version = "0.1.0" dependencies = [ "anyhow", "api-server", - "as-types", "base64 0.21.5", "clap 4.4.7", "env_logger 0.10.0", diff --git a/Cargo.toml b/Cargo.toml index b73ad9ccc..199f96b9f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,6 @@ members = [ "kbs/src/kbs", "kbs/src/api", "kbs/tools/client", - "attestation-service/as-types", "attestation-service/attestation-service", "attestation-service/bin/rvps", "attestation-service/bin/grpc-as", @@ -21,7 +20,6 @@ edition = "2021" anyhow = "1.0" api-server = { path = "kbs/src/api", default-features = false } assert-json-diff = "2.0.2" -as-types.path = "attestation-service/as-types" async-trait = "0.1.31" attestation-service = { path = "attestation-service/attestation-service", default-features = false } base64 = "0.21" diff --git a/attestation-service/as-types/Cargo.toml b/attestation-service/as-types/Cargo.toml deleted file mode 100644 index fcdf2f611..000000000 --- a/attestation-service/as-types/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "as-types" -version = "0.1.0" -edition = "2021" - -[dependencies] -# TODO: change it to "0.5", once released. -kbs-types = { git = "https://github.com/virtee/kbs-types", rev = "c90df0e" } -serde.workspace = true -serde_json.workspace = true diff --git a/attestation-service/as-types/src/lib.rs b/attestation-service/as-types/src/lib.rs deleted file mode 100644 index 9e02ee8f1..000000000 --- a/attestation-service/as-types/src/lib.rs +++ /dev/null @@ -1,10 +0,0 @@ -use serde::{Deserialize, Serialize}; - -pub type TeeEvidenceParsedClaim = serde_json::Value; - -#[derive(Serialize, Deserialize, Debug, Clone)] -pub struct SetPolicyInput { - pub r#type: String, - pub policy_id: String, - pub policy: String, -} diff --git a/attestation-service/attestation-service/Cargo.toml b/attestation-service/attestation-service/Cargo.toml index eb4eada4c..e529a17fb 100644 --- a/attestation-service/attestation-service/Cargo.toml +++ b/attestation-service/attestation-service/Cargo.toml @@ -20,7 +20,6 @@ rvps-grpc = [ "tonic" ] anyhow.workspace = true asn1-rs = { version = "0.5.1", optional = true } async-trait.workspace = true -as-types.workspace = true az-snp-vtpm = { version = "0.3.0", default-features = false, features = ["verifier"], optional = true } base64 = "0.21" bincode = "1.3.3" diff --git a/attestation-service/attestation-service/src/lib.rs b/attestation-service/attestation-service/src/lib.rs index 144b6b47d..f4c098205 100644 --- a/attestation-service/attestation-service/src/lib.rs +++ b/attestation-service/attestation-service/src/lib.rs @@ -24,10 +24,9 @@ pub mod verifier; use crate::token::AttestationTokenBroker; use anyhow::{anyhow, Context, Result}; -use as_types::SetPolicyInput; use config::Config; pub use kbs_types::{Attestation, Tee}; -use policy_engine::PolicyEngine; +use policy_engine::{PolicyEngine, SetPolicyInput}; use rvps::{Message, RVPSAPI}; use serde_json::json; use std::collections::HashMap; diff --git a/attestation-service/attestation-service/src/policy_engine/mod.rs b/attestation-service/attestation-service/src/policy_engine/mod.rs index 9cdaff07b..5ddaf0829 100644 --- a/attestation-service/attestation-service/src/policy_engine/mod.rs +++ b/attestation-service/attestation-service/src/policy_engine/mod.rs @@ -1,12 +1,18 @@ use anyhow::Result; -use as_types::SetPolicyInput; use async_trait::async_trait; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::path::Path; pub mod opa; +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct SetPolicyInput { + pub r#type: String, + pub policy_id: String, + pub policy: String, +} + #[derive(Debug, EnumString, Deserialize)] #[strum(ascii_case_insensitive)] pub enum PolicyEngineType { diff --git a/attestation-service/attestation-service/src/policy_engine/opa/mod.rs b/attestation-service/attestation-service/src/policy_engine/opa/mod.rs index adf365ac1..014982b70 100644 --- a/attestation-service/attestation-service/src/policy_engine/opa/mod.rs +++ b/attestation-service/attestation-service/src/policy_engine/opa/mod.rs @@ -1,6 +1,5 @@ use crate::policy_engine::{PolicyEngine, PolicyType}; use anyhow::{anyhow, bail, Result}; -use as_types::SetPolicyInput; use async_trait::async_trait; use base64::Engine; use serde_json::Value; @@ -11,6 +10,8 @@ use std::os::raw::c_char; use std::path::PathBuf; use std::str::FromStr; +use super::SetPolicyInput; + // Link import cgo function #[link(name = "cgo")] extern "C" { diff --git a/attestation-service/attestation-service/src/utils.rs b/attestation-service/attestation-service/src/utils.rs index f0ed5987f..4e479dfd4 100644 --- a/attestation-service/attestation-service/src/utils.rs +++ b/attestation-service/attestation-service/src/utils.rs @@ -4,10 +4,11 @@ // use anyhow::*; -use as_types::TeeEvidenceParsedClaim; use serde_json::{Map, Value}; use serde_variant::to_variant_name; +use crate::verifier::TeeEvidenceParsedClaim; + /// This funciton will transpose the following structured json /// ```json /// { diff --git a/attestation-service/attestation-service/src/verifier/mod.rs b/attestation-service/attestation-service/src/verifier/mod.rs index 139371159..6b7252514 100644 --- a/attestation-service/attestation-service/src/verifier/mod.rs +++ b/attestation-service/attestation-service/src/verifier/mod.rs @@ -1,5 +1,4 @@ use anyhow::*; -use as_types::TeeEvidenceParsedClaim; use async_trait::async_trait; use kbs_types::{Attestation, Tee}; @@ -86,6 +85,8 @@ pub(crate) fn to_verifier(tee: &Tee) -> Result> } } +pub type TeeEvidenceParsedClaim = serde_json::Value; + #[async_trait] pub trait Verifier { /// Verify the hardware signature and report data in TEE quote. diff --git a/attestation-service/attestation-service/src/verifier/sgx/mod.rs b/attestation-service/attestation-service/src/verifier/sgx/mod.rs index 7e746caf3..21a97af30 100644 --- a/attestation-service/attestation-service/src/verifier/sgx/mod.rs +++ b/attestation-service/attestation-service/src/verifier/sgx/mod.rs @@ -9,7 +9,6 @@ use std::{ }; use anyhow::*; -use as_types::TeeEvidenceParsedClaim; use async_trait::async_trait; use base64::Engine; use kbs_types::Attestation; @@ -24,7 +23,7 @@ use sha2::{Digest, Sha384}; use self::types::sgx_quote3_t; -use super::Verifier; +use super::{TeeEvidenceParsedClaim, Verifier}; #[allow(non_camel_case_types)] mod types; diff --git a/attestation-service/attestation-service/src/verifier/tdx/claims.rs b/attestation-service/attestation-service/src/verifier/tdx/claims.rs index 4cdb833e0..fb7579f2b 100644 --- a/attestation-service/attestation-service/src/verifier/tdx/claims.rs +++ b/attestation-service/attestation-service/src/verifier/tdx/claims.rs @@ -42,10 +42,11 @@ //! ``` use anyhow::*; -use as_types::TeeEvidenceParsedClaim; use byteorder::{LittleEndian, ReadBytesExt}; use serde_json::{Map, Value}; +use crate::verifier::TeeEvidenceParsedClaim; + use super::{ eventlog::{CcEventLog, MeasuredEntity}, quote::Quote, diff --git a/attestation-service/bin/grpc-as/Cargo.toml b/attestation-service/bin/grpc-as/Cargo.toml index 5fd51291e..01978b179 100644 --- a/attestation-service/bin/grpc-as/Cargo.toml +++ b/attestation-service/bin/grpc-as/Cargo.toml @@ -5,7 +5,6 @@ edition = "2021" [dependencies] anyhow.workspace = true -as-types = { path = "../../as-types" } async-trait.workspace = true attestation-service = { path = "../../attestation-service", features = ["rvps-grpc"] } clap.workspace = true diff --git a/attestation-service/bin/grpc-as/src/server.rs b/attestation-service/bin/grpc-as/src/server.rs index 863307f9f..374673f6e 100644 --- a/attestation-service/bin/grpc-as/src/server.rs +++ b/attestation-service/bin/grpc-as/src/server.rs @@ -1,4 +1,5 @@ use anyhow::{anyhow, Result}; +use attestation_service::policy_engine::SetPolicyInput; use attestation_service::{config::Config, AttestationService as Service, Tee}; use log::{debug, info}; use std::path::Path; @@ -74,7 +75,7 @@ impl AttestationService for Arc> { debug!("SetPolicyInput: {}", &request.input); - let set_policy_input: as_types::SetPolicyInput = serde_json::from_str(&request.input) + let set_policy_input: SetPolicyInput = serde_json::from_str(&request.input) .map_err(|_| Status::aborted("Bad SetPolicyInput"))?; self.write() diff --git a/kbs/src/api/Cargo.toml b/kbs/src/api/Cargo.toml index 81da5d92e..e1a900162 100644 --- a/kbs/src/api/Cargo.toml +++ b/kbs/src/api/Cargo.toml @@ -26,7 +26,6 @@ actix-web-httpauth = "0.8.0" aes-gcm = { version = "0.10.1", optional = true } anyhow.workspace = true async-trait.workspace = true -as-types.workspace = true attestation-service = { workspace = true, default-features = false, optional = true } base64.workspace = true cfg-if.workspace = true diff --git a/kbs/src/api/src/attestation/coco/builtin.rs b/kbs/src/api/src/attestation/coco/builtin.rs index e9c17b756..66d31378a 100644 --- a/kbs/src/api/src/attestation/coco/builtin.rs +++ b/kbs/src/api/src/attestation/coco/builtin.rs @@ -5,7 +5,9 @@ use crate::attestation::Attest; use anyhow::*; use async_trait::async_trait; -use attestation_service::{config::Config as AsConfig, AttestationService}; +use attestation_service::{ + config::Config as AsConfig, policy_engine::SetPolicyInput, AttestationService, +}; use kbs_types::Tee; pub struct Native { @@ -14,9 +16,12 @@ pub struct Native { #[async_trait] impl Attest for Native { - async fn set_policy(&mut self, input: as_types::SetPolicyInput) -> Result<()> { - self.inner.set_policy(input).await + async fn set_policy(&mut self, input: &[u8]) -> Result<()> { + let request: SetPolicyInput = + serde_json::from_slice(input).context("parse SetPolicyInput")?; + self.inner.set_policy(request).await } + async fn verify(&mut self, tee: Tee, nonce: &str, attestation: &str) -> Result { self.inner.evaluate(tee, nonce, attestation).await } diff --git a/kbs/src/api/src/attestation/coco/grpc.rs b/kbs/src/api/src/attestation/coco/grpc.rs index 0406e6625..ae73e588a 100644 --- a/kbs/src/api/src/attestation/coco/grpc.rs +++ b/kbs/src/api/src/attestation/coco/grpc.rs @@ -71,10 +71,9 @@ impl Grpc { #[async_trait] impl Attest for Grpc { - async fn set_policy(&mut self, input: as_types::SetPolicyInput) -> Result<()> { - let req = tonic::Request::new(SetPolicyRequest { - input: serde_json::to_string(&input)?, - }); + async fn set_policy(&mut self, input: &[u8]) -> Result<()> { + let input = String::from_utf8(input.to_vec()).context("parse SetPolicyInput")?; + let req = tonic::Request::new(SetPolicyRequest { input }); let _ = self .inner diff --git a/kbs/src/api/src/attestation/mod.rs b/kbs/src/api/src/attestation/mod.rs index 03c8ce6d5..67d51b40e 100644 --- a/kbs/src/api/src/attestation/mod.rs +++ b/kbs/src/api/src/attestation/mod.rs @@ -27,7 +27,7 @@ pub mod amber; #[async_trait] pub trait Attest: Send + Sync { /// Set Attestation Policy - async fn set_policy(&mut self, _input: as_types::SetPolicyInput) -> Result<()> { + async fn set_policy(&mut self, _input: &[u8]) -> Result<()> { Err(anyhow!("Set Policy API is unimplemented")) } diff --git a/kbs/src/api/src/http/config.rs b/kbs/src/api/src/http/config.rs index fa64bd170..7f8ac53f4 100644 --- a/kbs/src/api/src/http/config.rs +++ b/kbs/src/api/src/http/config.rs @@ -8,7 +8,7 @@ use super::*; /// POST /attestation-policy pub(crate) async fn attestation_policy( request: HttpRequest, - input: web::Json, + input: web::Bytes, user_pub_key: web::Data>, insecure: web::Data, attestation_service: web::Data, @@ -28,7 +28,7 @@ pub(crate) async fn attestation_policy( .0 .lock() .await - .set_policy(input.into_inner()) + .set_policy(&input) .await .map_err(|e| Error::PolicyEndpoint(format!("Set policy error {e}")))?; diff --git a/kbs/tools/client/Cargo.toml b/kbs/tools/client/Cargo.toml index e0e2c9592..37aae0c2e 100644 --- a/kbs/tools/client/Cargo.toml +++ b/kbs/tools/client/Cargo.toml @@ -13,8 +13,6 @@ name = "kbs-client" path = "src/main.rs" [dependencies] -# TODO: change it to "0.8.0", once released. -as-types.workspace = true anyhow.workspace = true api-server.workspace = true base64.workspace = true From 0c239d0782589cf472f32632b521fcfcc6cef4ed Mon Sep 17 00:00:00 2001 From: Xynnn007 Date: Mon, 13 Nov 2023 21:21:56 +0800 Subject: [PATCH 02/11] AS: bring verifier out as a separate crate Signed-off-by: Xynnn007 --- Cargo.lock | 55 +++-- Cargo.toml | 1 + .../attestation-service/Cargo.toml | 33 +-- .../attestation-service/src/lib.rs | 36 +++- .../attestation-service/src/utils.rs | 3 +- .../src/verifier/sample/mod.rs | 68 ------- attestation-service/verifier/Cargo.toml | 55 +++++ .../src}/az_snp_vtpm/mod.rs | 56 ++--- .../src/verifier => verifier/src}/cca/mod.rs | 51 +++-- .../verifier => verifier/src}/csv/hrk.cert | Bin .../src/verifier => verifier/src}/csv/mod.rs | 65 +++--- .../verifier/mod.rs => verifier/src/lib.rs} | 58 ++++-- .../verifier/src/sample/mod.rs | 85 ++++++++ .../verifier/src/sgx/claims.rs | 191 ++++++++++++++++++ .../src/verifier => verifier/src}/sgx/mod.rs | 74 +++---- .../verifier => verifier/src}/sgx/types.rs | 61 ++---- .../src}/snp/milan_ask_ark.pem | 0 .../src/verifier => verifier/src}/snp/mod.rs | 43 ++-- .../src}/snp/test-report.bin | Bin .../src}/snp/test-vcek-invalid-legacy.der | Bin .../src}/snp/test-vcek-invalid-new.der | Bin .../src}/snp/test-vcek.der | Bin .../verifier => verifier/src}/tdx/claims.rs | 9 +- .../verifier => verifier/src}/tdx/eventlog.rs | 9 +- .../src/verifier => verifier/src}/tdx/mod.rs | 61 +++--- .../verifier => verifier/src}/tdx/quote.rs | 5 +- .../{ => verifier}/test_data/CCEL_data | Bin .../{ => verifier}/test_data/az-hcl-data.bin | Bin .../{ => verifier}/test_data/az-vcek.pem | 0 .../test_data/az-vtpm-quote-msg.bin | Bin .../test_data/az-vtpm-quote-sig.bin | Bin .../{ => verifier}/test_data/cca-claims.json | 0 .../{ => verifier}/test_data/occlum_quote.dat | Bin .../{ => verifier}/test_data/tdx_quote_4.dat | Bin kbs/tools/client/src/lib.rs | 8 +- 35 files changed, 675 insertions(+), 352 deletions(-) delete mode 100644 attestation-service/attestation-service/src/verifier/sample/mod.rs create mode 100644 attestation-service/verifier/Cargo.toml rename attestation-service/{attestation-service/src/verifier => verifier/src}/az_snp_vtpm/mod.rs (66%) rename attestation-service/{attestation-service/src/verifier => verifier/src}/cca/mod.rs (87%) rename attestation-service/{attestation-service/src/verifier => verifier/src}/csv/hrk.cert (100%) rename attestation-service/{attestation-service/src/verifier => verifier/src}/csv/mod.rs (73%) rename attestation-service/{attestation-service/src/verifier/mod.rs => verifier/src/lib.rs} (53%) create mode 100644 attestation-service/verifier/src/sample/mod.rs create mode 100644 attestation-service/verifier/src/sgx/claims.rs rename attestation-service/{attestation-service/src/verifier => verifier/src}/sgx/mod.rs (77%) rename attestation-service/{attestation-service/src/verifier => verifier/src}/sgx/types.rs (78%) rename attestation-service/{attestation-service/src/verifier => verifier/src}/snp/milan_ask_ark.pem (100%) rename attestation-service/{attestation-service/src/verifier => verifier/src}/snp/mod.rs (93%) rename attestation-service/{attestation-service/src/verifier => verifier/src}/snp/test-report.bin (100%) rename attestation-service/{attestation-service/src/verifier => verifier/src}/snp/test-vcek-invalid-legacy.der (100%) rename attestation-service/{attestation-service/src/verifier => verifier/src}/snp/test-vcek-invalid-new.der (100%) rename attestation-service/{attestation-service/src/verifier => verifier/src}/snp/test-vcek.der (100%) rename attestation-service/{attestation-service/src/verifier => verifier/src}/tdx/claims.rs (98%) rename attestation-service/{attestation-service/src/verifier => verifier/src}/tdx/eventlog.rs (95%) rename attestation-service/{attestation-service/src/verifier => verifier/src}/tdx/mod.rs (65%) rename attestation-service/{attestation-service/src/verifier => verifier/src}/tdx/quote.rs (98%) rename attestation-service/{ => verifier}/test_data/CCEL_data (100%) rename attestation-service/{ => verifier}/test_data/az-hcl-data.bin (100%) rename attestation-service/{ => verifier}/test_data/az-vcek.pem (100%) rename attestation-service/{ => verifier}/test_data/az-vtpm-quote-msg.bin (100%) rename attestation-service/{ => verifier}/test_data/az-vtpm-quote-sig.bin (100%) rename attestation-service/{ => verifier}/test_data/cca-claims.json (100%) rename attestation-service/{ => verifier}/test_data/occlum_quote.dat (100%) rename attestation-service/{ => verifier}/test_data/tdx_quote_4.dat (100%) diff --git a/Cargo.lock b/Cargo.lock index aeeb8387c..8def2400e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -526,22 +526,12 @@ name = "attestation-service" version = "0.1.0" dependencies = [ "anyhow", - "asn1-rs", "assert-json-diff", "async-trait", - "az-snp-vtpm 0.3.0", "base64 0.21.5", - "bincode", - "byteorder", - "cbor-diag", "cfg-if", "chrono", - "codicon", - "csv-rs", - "ear", - "eventlog-rs", "futures", - "hex", "jsonwebtoken", "jwt", "kbs-types", @@ -558,23 +548,19 @@ dependencies = [ "serde_json", "serde_variant", "serial_test", - "sev", - "sgx-dcap-quoteverify-rs", "sha2", "shadow-rs", "sled", "strum 0.24.1", "strum_macros 0.24.3", - "tempfile", "testing_logger", "time", "tokio", "tonic 0.8.3", "tonic-build", "uuid", - "veraison-apiclient", + "verifier", "walkdir", - "x509-parser", ] [[package]] @@ -5203,6 +5189,45 @@ dependencies = [ "url", ] +[[package]] +name = "verifier" +version = "0.1.0" +dependencies = [ + "anyhow", + "asn1-rs", + "assert-json-diff", + "async-trait", + "az-snp-vtpm 0.3.0", + "base64 0.21.5", + "bincode", + "byteorder", + "cbor-diag", + "cfg-if", + "codicon", + "csv-rs", + "ear", + "eventlog-rs", + "hex", + "jsonwebtoken", + "kbs-types", + "log", + "openssl", + "rstest", + "scroll", + "serde", + "serde_json", + "serial_test", + "sev", + "sgx-dcap-quoteverify-rs", + "shadow-rs", + "strum 0.24.1", + "strum_macros 0.24.3", + "tokio", + "tonic-build", + "veraison-apiclient", + "x509-parser", +] + [[package]] name = "version_check" version = "0.9.4" diff --git a/Cargo.toml b/Cargo.toml index 199f96b9f..4a69e7557 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "kbs/src/api", "kbs/tools/client", "attestation-service/attestation-service", + "attestation-service/verifier", "attestation-service/bin/rvps", "attestation-service/bin/grpc-as", "attestation-service/bin/rvps-client" diff --git a/attestation-service/attestation-service/Cargo.toml b/attestation-service/attestation-service/Cargo.toml index e529a17fb..1ba70e162 100644 --- a/attestation-service/attestation-service/Cargo.toml +++ b/attestation-service/attestation-service/Cargo.toml @@ -4,35 +4,25 @@ version = "0.1.0" edition = "2021" [features] -default = [ "rvps-native", "all-verifier" ] -all-verifier = [ "tdx-verifier", "sgx-verifier", "snp-verifier", "az-snp-vtpm-verifier", "csv-verifier", "cca-verifier" ] -tdx-verifier = [ "eventlog-rs", "scroll", "sgx-dcap-quoteverify-rs" ] -sgx-verifier = [ "scroll", "sgx-dcap-quoteverify-rs" ] -az-snp-vtpm-verifier = [ "az-snp-vtpm", "sev", "snp-verifier" ] -snp-verifier = [ "asn1-rs", "openssl", "sev", "x509-parser" ] -csv-verifier = [ "openssl", "csv-rs", "codicon" ] -cca-verifier = [ "cbor-diag", "veraison-apiclient" ] +default = [ "rvps-grpc", "all-verifier" ] +all-verifier = [ "verifier/all-verifier" ] +tdx-verifier = [ "verifier/tdx-verifier" ] +sgx-verifier = [ "verifier/sgx-verifier" ] +az-snp-vtpm-verifier = [ "verifier/az-snp-vtpm-verifier" ] +snp-verifier = [ "verifier/snp-verifier" ] +csv-verifier = [ "verifier/csv-verifier" ] +cca-verifier = [ "verifier/cca-verifier" ] rvps-native = [] rvps-grpc = [ "tonic" ] [dependencies] anyhow.workspace = true -asn1-rs = { version = "0.5.1", optional = true } async-trait.workspace = true -az-snp-vtpm = { version = "0.3.0", default-features = false, features = ["verifier"], optional = true } base64 = "0.21" -bincode = "1.3.3" -byteorder = "1" -cbor-diag = { version = "0.1.11", optional = true } cfg-if = "1.0.0" chrono = { version = "0.4.19", features = [ "serde" ] } -codicon = { version = "3.0", optional = true } -# TODO: change it to "0.1", once released. -csv-rs = { git = "https://gitee.com/anolis/csv-rs", rev = "9d8882e", optional = true } -eventlog-rs = { version = "0.1.3", optional = true } futures = "0.3.17" -hex = "0.4.3" jsonwebtoken = "8" jwt = { version = "0.16.0", features = ["openssl"]} # TODO: change it to "0.5", once released. @@ -48,21 +38,16 @@ scroll = { version = "0.11.0", default-features = false, features = ["derive"], serde.workspace = true serde_json.workspace = true serde_variant = "0.1.2" -sev = { version = "1.2.0", features = ["openssl", "snp"], optional = true } -sgx-dcap-quoteverify-rs = { git = "https://github.com/intel/SGXDataCenterAttestationPrimitives", tag = "DCAP_1.16", optional = true } sha2.workspace = true shadow-rs.workspace = true sled = "0.34.7" strum = "0.24.0" strum_macros = "0.24.0" -tempfile = "3.3.0" time = { version = "0.3.23", features = ["std"] } tokio.workspace = true tonic = { workspace = true, optional = true } uuid = { version = "1.1.2", features = ["v4"] } -veraison-apiclient = { git = "https://github.com/chendave/rust-apiclient", branch = "token", optional = true } -ear = { git = "https://github.com/veraison/rust-ear", rev = "cc6ea53" } -x509-parser = { version = "0.14.0", optional = true } +verifier = { path = "../verifier", default-features = false } [build-dependencies] shadow-rs.workspace = true diff --git a/attestation-service/attestation-service/src/lib.rs b/attestation-service/attestation-service/src/lib.rs index f4c098205..d4a49fc86 100644 --- a/attestation-service/attestation-service/src/lib.rs +++ b/attestation-service/attestation-service/src/lib.rs @@ -19,7 +19,6 @@ pub mod policy_engine; pub mod rvps; mod token; mod utils; -pub mod verifier; use crate::token::AttestationTokenBroker; @@ -29,6 +28,8 @@ pub use kbs_types::{Attestation, Tee}; use policy_engine::{PolicyEngine, SetPolicyInput}; use rvps::{Message, RVPSAPI}; use serde_json::json; +use sha2::{Digest, Sha384}; +use verifier::{ReportData, InitDataHash}; use std::collections::HashMap; #[cfg(any(feature = "rvps-grpc", feature = "rvps-native"))] @@ -108,15 +109,44 @@ impl AttestationService { .map_err(|e| anyhow!("Cannot Set Policy: {:?}", e)) } + fn accumulate_hash(materials: &[Vec]) -> Option> { + if materials.is_empty() { + return None; + } + let mut hasher = Sha384::new(); + materials.iter().for_each(|m| hasher.update(m)); + Some(hasher.finalize().to_vec()) + } + /// Evaluate Attestation Evidence. /// Issue an attestation results token which contain TCB status and TEE public key. pub async fn evaluate(&self, tee: Tee, nonce: &str, attestation: &str) -> Result { let attestation = serde_json::from_str::(attestation) .context("Failed to deserialize Attestation")?; - let verifier = crate::verifier::to_verifier(&tee)?; + let verifier = verifier::to_verifier(&tee)?; + + let report_data = Self::accumulate_hash(&[ + nonce.as_bytes().to_vec(), + attestation.tee_pubkey.k_mod.as_bytes().to_vec(), + attestation.tee_pubkey.k_exp.as_bytes().to_vec(), + ]); + + let report_data = match &report_data { + Some(value) => ReportData::Value(value), + None => ReportData::NotProvided, + }; let claims_from_tee_evidence = verifier - .evaluate(nonce.to_string(), &attestation) + .evaluate( + attestation.tee_evidence.as_bytes(), + &report_data, + // We currently do not need to check the initdata hash in AS when using + // `verifier` crate. + // + // We will refactor the CoCo-AS' API to leverage the parameter. + // See https://github.com/confidential-containers/kbs/issues/177 + &InitDataHash::NotProvided, + ) .await .map_err(|e| anyhow!("Verifier evaluate failed: {e:?}"))?; diff --git a/attestation-service/attestation-service/src/utils.rs b/attestation-service/attestation-service/src/utils.rs index 4e479dfd4..39eed788b 100644 --- a/attestation-service/attestation-service/src/utils.rs +++ b/attestation-service/attestation-service/src/utils.rs @@ -6,8 +6,7 @@ use anyhow::*; use serde_json::{Map, Value}; use serde_variant::to_variant_name; - -use crate::verifier::TeeEvidenceParsedClaim; +use verifier::TeeEvidenceParsedClaim; /// This funciton will transpose the following structured json /// ```json diff --git a/attestation-service/attestation-service/src/verifier/sample/mod.rs b/attestation-service/attestation-service/src/verifier/sample/mod.rs deleted file mode 100644 index 316c2f464..000000000 --- a/attestation-service/attestation-service/src/verifier/sample/mod.rs +++ /dev/null @@ -1,68 +0,0 @@ -use anyhow::{anyhow, Context, Result}; -extern crate serde; -use self::serde::{Deserialize, Serialize}; -use super::*; -use async_trait::async_trait; -use base64::Engine; -use serde_json::json; -use sha2::{Digest, Sha384}; - -#[derive(Serialize, Deserialize, Debug)] -struct SampleTeeEvidence { - svn: String, - report_data: String, -} - -#[derive(Debug, Default)] -pub struct Sample {} - -#[async_trait] -impl Verifier for Sample { - async fn evaluate( - &self, - nonce: String, - attestation: &Attestation, - ) -> Result { - let tee_evidence = serde_json::from_str::(&attestation.tee_evidence) - .context("Deserialize Quote failed.")?; - - let mut hasher = Sha384::new(); - hasher.update(&nonce); - hasher.update(&attestation.tee_pubkey.k_mod); - hasher.update(&attestation.tee_pubkey.k_exp); - let reference_report_data = - base64::engine::general_purpose::STANDARD.encode(hasher.finalize()); - - verify_tee_evidence(reference_report_data, &tee_evidence) - .await - .context("Evidence's identity verification error.")?; - - debug!("TEE-Evidence: {:?}", tee_evidence); - - parse_tee_evidence(&tee_evidence) - } -} - -async fn verify_tee_evidence( - reference_report_data: String, - tee_evidence: &SampleTeeEvidence, -) -> Result<()> { - // Verify the TEE Hardware signature. (Null for sample TEE) - - // Emulate the report data. - if tee_evidence.report_data != reference_report_data { - return Err(anyhow!("Report data verification failed!")); - } - - Ok(()) -} - -// Dump the TCB status from the quote. -// Example: CPU SVN, RTMR, etc. -fn parse_tee_evidence(quote: &SampleTeeEvidence) -> Result { - let claims_map = json!({ - "svn": quote.svn - }); - - Ok(claims_map as TeeEvidenceParsedClaim) -} diff --git a/attestation-service/verifier/Cargo.toml b/attestation-service/verifier/Cargo.toml new file mode 100644 index 000000000..4cbac099f --- /dev/null +++ b/attestation-service/verifier/Cargo.toml @@ -0,0 +1,55 @@ +[package] +name = "verifier" +version = "0.1.0" +edition = "2021" + +[features] +default = [ "all-verifier" ] +all-verifier = [ "tdx-verifier", "sgx-verifier", "snp-verifier", "az-snp-vtpm-verifier", "csv-verifier", "cca-verifier" ] +tdx-verifier = [ "eventlog-rs", "scroll", "sgx-dcap-quoteverify-rs" ] +sgx-verifier = [ "scroll", "sgx-dcap-quoteverify-rs" ] +az-snp-vtpm-verifier = [ "az-snp-vtpm", "sev", "snp-verifier" ] +snp-verifier = [ "asn1-rs", "openssl", "sev", "x509-parser" ] +csv-verifier = [ "openssl", "csv-rs", "codicon" ] +cca-verifier = [ "cbor-diag", "veraison-apiclient" ] + +[dependencies] +anyhow.workspace = true +asn1-rs = { version = "0.5.1", optional = true } +async-trait.workspace = true +az-snp-vtpm = { version = "0.3.0", default-features = false, features = ["verifier"], optional = true } +base64 = "0.21" +bincode = "1.3.3" +byteorder = "1" +cbor-diag = { version = "0.1.11", optional = true } +cfg-if = "1.0.0" +codicon = { version = "3.0", optional = true } +# TODO: change it to "0.1", once released. +csv-rs = { git = "https://gitee.com/anolis/csv-rs", rev = "9d8882e", optional = true } +eventlog-rs = { version = "0.1.3", optional = true } +hex = "0.4.3" +jsonwebtoken = "8" +# TODO: change it to "0.5", once released. +kbs-types = { git = "https://github.com/virtee/kbs-types", rev = "c90df0e" } +log.workspace = true +openssl = { version = "0.10.55", optional = true } +scroll = { version = "0.11.0", default-features = false, features = ["derive"], optional = true } +serde.workspace = true +serde_json.workspace = true +sev = { version = "1.2.0", features = ["openssl", "snp"], optional = true } +sgx-dcap-quoteverify-rs = { git = "https://github.com/intel/SGXDataCenterAttestationPrimitives", tag = "DCAP_1.16", optional = true } +strum = "0.24.0" +strum_macros = "0.24.0" +veraison-apiclient = { git = "https://github.com/chendave/rust-apiclient", branch = "token", optional = true } +ear = { git = "https://github.com/veraison/rust-ear", rev = "cc6ea53" } +x509-parser = { version = "0.14.0", optional = true } + +[build-dependencies] +shadow-rs.workspace = true +tonic-build.workspace = true + +[dev-dependencies] +assert-json-diff.workspace = true +rstest.workspace = true +serial_test.workspace = true +tokio.workspace = true diff --git a/attestation-service/attestation-service/src/verifier/az_snp_vtpm/mod.rs b/attestation-service/verifier/src/az_snp_vtpm/mod.rs similarity index 66% rename from attestation-service/attestation-service/src/verifier/az_snp_vtpm/mod.rs rename to attestation-service/verifier/src/az_snp_vtpm/mod.rs index 2a64db024..c1c7d6729 100644 --- a/attestation-service/attestation-service/src/verifier/az_snp_vtpm/mod.rs +++ b/attestation-service/verifier/src/az_snp_vtpm/mod.rs @@ -3,17 +3,20 @@ // SPDX-License-Identifier: Apache-2.0 // -use super::snp::{parse_tee_evidence, verify_report_signature}; -use super::{Attestation, TeeEvidenceParsedClaim, Verifier}; -use anyhow::{anyhow, Context, Result}; +use crate::snp::parse_tee_evidence; +use crate::{InitDataHash, ReportData}; + +use super::snp::verify_report_signature; +use super::{TeeEvidenceParsedClaim, Verifier}; +use anyhow::{bail, Context, Result}; use async_trait::async_trait; use az_snp_vtpm::certs::Vcek; use az_snp_vtpm::hcl::HclData; use az_snp_vtpm::vtpm::{Quote, VerifyVTpmQuote}; +use log::warn; use serde::{Deserialize, Serialize}; use sev::firmware::guest::AttestationReport; use sev::firmware::host::{CertTableEntry, CertType}; -use sha2::{Digest, Sha384}; const HCL_VMPL_VALUE: u32 = 0; @@ -31,19 +34,26 @@ pub struct AzSnpVtpm; impl Verifier for AzSnpVtpm { async fn evaluate( &self, - nonce: String, - attestation: &Attestation, + evidence: &[u8], + expected_report_data: &ReportData, + expected_init_data_hash: &InitDataHash, ) -> Result { - let evidence = serde_json::from_str::(&attestation.tee_evidence) - .context("Failed to deserialize vTPM SEV-SNP evidence")?; + let ReportData::Value(expected_report_data) = expected_report_data else { + bail!("unexpected empty report data"); + }; + + if let InitDataHash::Value(_) = expected_init_data_hash { + warn!("Azure SNP vTPM verifier does not support verify init data hash, will ignore the input `init_data_hash`."); + } + + let evidence = serde_json::from_slice::(evidence) + .context("Failed to deserialize Azure vTPM SEV-SNP evidence")?; let hcl_data: HclData = evidence.report[..].try_into()?; let snp_report = hcl_data.report().snp_report(); let vcek = Vcek::from_pem(&evidence.vcek)?; - let hashed_quote = nonced_pub_key_hash(attestation, &nonce); - verify_quote(&evidence.quote, &hcl_data, &hashed_quote)?; - + verify_quote(&evidence.quote, &hcl_data, expected_report_data)?; verify_snp_report(snp_report, &vcek)?; let var_data = hcl_data.var_data(); hcl_data.report().verify_report_data(var_data)?; @@ -53,11 +63,11 @@ impl Verifier for AzSnpVtpm { } } -fn verify_quote(quote: &Quote, hcl_data: &HclData, hashed_nonce: &[u8]) -> Result<()> { +fn verify_quote(quote: &Quote, hcl_data: &HclData, report_data: &[u8]) -> Result<()> { let ak_pub = hcl_data.var_data().ak_pub()?; ak_pub - .verify_quote(quote, hashed_nonce) + .verify_quote(quote, report_data) .context("Failed to verify vTPM quote")?; Ok(()) @@ -69,29 +79,21 @@ fn verify_snp_report(snp_report: &AttestationReport, vcek: &Vcek) -> Result<()> verify_report_signature(snp_report, &cert_chain)?; if snp_report.vmpl != HCL_VMPL_VALUE { - return Err(anyhow!("VMPL of SNP report is not {HCL_VMPL_VALUE}")); + bail!("VMPL of SNP report is not {HCL_VMPL_VALUE}"); } Ok(()) } -fn nonced_pub_key_hash(attestation: &Attestation, nonce: &str) -> Vec { - let mut hasher = Sha384::new(); - hasher.update(nonce); - hasher.update(&attestation.tee_pubkey.k_mod); - hasher.update(&attestation.tee_pubkey.k_exp); - hasher.finalize().to_vec() -} - #[cfg(test)] mod tests { use super::*; #[test] fn test_verify_snp_report() { - let report = include_bytes!("../../../../test_data/az-hcl-data.bin"); + let report = include_bytes!("../../test_data/az-hcl-data.bin"); let hcl_data: HclData = report.as_slice().try_into().unwrap(); - let vcek = Vcek::from_pem(include_str!("../../../../test_data/az-vcek.pem")).unwrap(); + let vcek = Vcek::from_pem(include_str!("../../test_data/az-vcek.pem")).unwrap(); verify_snp_report(hcl_data.report().snp_report(), &vcek).unwrap(); let mut wrong_report = *report; @@ -104,10 +106,10 @@ mod tests { #[test] fn test_verify_quote() { - let signature = include_bytes!("../../../../test_data/az-vtpm-quote-sig.bin").to_vec(); - let message = include_bytes!("../../../../test_data/az-vtpm-quote-msg.bin").to_vec(); + let signature = include_bytes!("../../test_data/az-vtpm-quote-sig.bin").to_vec(); + let message = include_bytes!("../../test_data/az-vtpm-quote-msg.bin").to_vec(); let quote = Quote { signature, message }; - let report = include_bytes!("../../../../test_data/az-hcl-data.bin"); + let report = include_bytes!("../../test_data/az-hcl-data.bin"); let hcl_data: HclData = report.as_slice().try_into().unwrap(); let nonce = "challenge".as_bytes(); verify_quote("e, &hcl_data, nonce).unwrap(); diff --git a/attestation-service/attestation-service/src/verifier/cca/mod.rs b/attestation-service/verifier/src/cca/mod.rs similarity index 87% rename from attestation-service/attestation-service/src/verifier/cca/mod.rs rename to attestation-service/verifier/src/cca/mod.rs index 899424634..8c9047d4b 100644 --- a/attestation-service/attestation-service/src/verifier/cca/mod.rs +++ b/attestation-service/verifier/src/cca/mod.rs @@ -10,9 +10,9 @@ use base64::Engine; use core::result::Result::Ok; use ear::Ear; use jsonwebtoken::{self as jwt}; +use log::{error, info, warn}; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; -use sha2::{Digest, Sha384}; use std::str; use veraison_apiclient::*; @@ -57,10 +57,29 @@ fn my_evidence_builder( impl Verifier for CCA { async fn evaluate( &self, - nonce: String, - attestation: &Attestation, + evidence: &[u8], + expected_report_data: &ReportData, + expected_init_data_hash: &InitDataHash, ) -> Result { - let evidence = serde_json::from_str::(&attestation.tee_evidence) + let ReportData::Value(expected_report_data) = expected_report_data else { + bail!("CCA verifier must provide report data field!"); + }; + + let mut expected_report_data = expected_report_data.to_vec(); + + match expected_report_data.len() { + 0..=63 => { + warn!("The input report_data of CCA is shorter than 64 bytes, will be padded with '\\0'."); + expected_report_data.resize(64, b'\0'); + } + 64 => {} + _ => { + warn!("The input report_data of CCA is longer than 64 bytes, will be truncated to 64 bytes."); + expected_report_data.truncate(64); + } + }; + + let evidence = serde_json::from_slice::(evidence) .context("Deserialize CCA Evidence failed.")?; let host_url = @@ -81,20 +100,8 @@ impl Verifier for CCA { .with_new_session_url(api_endpoint) .build()?; - let mut hasher = Sha384::new(); - hasher.update(&nonce); - hasher.update(&attestation.tee_pubkey.k_mod); - hasher.update(&attestation.tee_pubkey.k_exp); - let mut hash_of_nonce_pubkey = hasher.finalize().to_vec(); - hash_of_nonce_pubkey.resize(64, 0); - - log::info!( - "HASH(nonce||pubkey):\n\t{}\n", - hex::encode(&hash_of_nonce_pubkey) - ); - let token = evidence.token; - let n = Nonce::Value(hash_of_nonce_pubkey.clone()); + let n = Nonce::Value(expected_report_data.clone()); let result = match cr.run(n, my_evidence_builder, token.clone()).await { Err(e) => { log::error!("Error: {}", e); @@ -115,8 +122,12 @@ impl Verifier for CCA { .decode(ear_nonce.to_string()) .context("decode nonce byte from ear")?; - if hash_of_nonce_pubkey != nonce_byte { - bail!("HASH(nonce||pubkey) is different from that in ear's session nonce"); + if expected_report_data != nonce_byte { + bail!("report data is different from that in ear's session nonce"); + } + + if let InitDataHash::Value(_) = expected_init_data_hash { + warn!("CCA currently does not support parse `cca_realm_personalization_value`. Init data hash check skipped."); } // NOTE: The tcb returned is actually an empty `Evidence`, the code here is just a show case the parse of the CCA token @@ -278,7 +289,7 @@ mod tests { #[test] fn test_cca_generate_parsed_claim() { - let s = fs::read("../test_data/cca-claims.json").unwrap(); + let s = fs::read("./test_data/cca-claims.json").unwrap(); let evidence = String::from_utf8_lossy(&s); let tcb = serde_json::from_str::(&evidence).unwrap(); let parsed_claim = cca_generate_parsed_claim(tcb); diff --git a/attestation-service/attestation-service/src/verifier/csv/hrk.cert b/attestation-service/verifier/src/csv/hrk.cert similarity index 100% rename from attestation-service/attestation-service/src/verifier/csv/hrk.cert rename to attestation-service/verifier/src/csv/hrk.cert diff --git a/attestation-service/attestation-service/src/verifier/csv/mod.rs b/attestation-service/verifier/src/csv/mod.rs similarity index 73% rename from attestation-service/attestation-service/src/verifier/csv/mod.rs rename to attestation-service/verifier/src/csv/mod.rs index 880dde119..bd9a62b18 100644 --- a/attestation-service/attestation-service/src/verifier/csv/mod.rs +++ b/attestation-service/verifier/src/csv/mod.rs @@ -4,6 +4,7 @@ // use anyhow::{Context, Result}; +use log::{debug, warn}; extern crate serde; use self::serde::{Deserialize, Serialize}; use super::*; @@ -14,9 +15,7 @@ use csv_rs::{ api::guest::{AttestationReport, Body}, certs::{ca, csv, Verifiable}, }; -use kbs_types::TeePubKey; use serde_json::json; -use sha2::{Digest, Sha384}; #[derive(Serialize, Deserialize)] struct CertificateChain { @@ -27,7 +26,7 @@ struct CertificateChain { #[derive(Serialize, Deserialize)] struct CsvEvidence { - attestation_report: AttestationReport, + attestation_report: String, cert_chain: CertificateChain, } @@ -40,58 +39,60 @@ pub struct CsvVerifier {} impl Verifier for CsvVerifier { async fn evaluate( &self, - nonce: String, - attestation: &Attestation, + evidence: &[u8], + expected_report_data: &ReportData, + expected_init_data_hash: &InitDataHash, ) -> Result { - let tee_evidence = serde_json::from_str::(&attestation.tee_evidence) - .context("Deserialize Quote failed.")?; + let tee_evidence = + serde_json::from_slice::(evidence).context("Deserialize Quote failed.")?; - verify_report_signature(&tee_evidence)?; + let attestation_report = base64::engine::general_purpose::STANDARD + .decode(tee_evidence.attestation_report) + .context("base64 decode attestation report")?; - let report_raw = restore_attestation_report(tee_evidence.attestation_report)?; + let attestation_report: AttestationReport = serde_json::from_slice(&attestation_report) + .context("parse attestation report failed")?; + verify_report_signature(&attestation_report, &tee_evidence.cert_chain)?; - let expected_report_data = calculate_expected_report_data(&nonce, &attestation.tee_pubkey); - if report_raw.body.report_data != expected_report_data { - bail!("Report Data Mismatch"); + let report_raw = restore_attestation_report(attestation_report)?; + + if let ReportData::Value(expected_report_data) = expected_report_data { + debug!("Check the binding of REPORT_DATA."); + if *expected_report_data != report_raw.body.report_data { + bail!("REPORT_DATA is different from that in CSV Quote"); + } + } + + if let InitDataHash::Value(_) = expected_init_data_hash { + warn!("CSV does not support init data hash mechanism. skip."); } parse_tee_evidence(&report_raw) } } -fn calculate_expected_report_data(nonce: &String, tee_pubkey: &TeePubKey) -> [u8; 64] { - let mut hasher = Sha384::new(); - - hasher.update(nonce.as_bytes()); - hasher.update(&tee_pubkey.k_mod); - hasher.update(&tee_pubkey.k_exp); - - let partial_hash = hasher.finalize(); - - let mut hash = [0u8; 64]; - hash[..48].copy_from_slice(&partial_hash); - - hash -} - -fn verify_report_signature(evidence: &CsvEvidence) -> Result<()> { +fn verify_report_signature( + attestation_report: &AttestationReport, + cert_chain: &CertificateChain, +) -> Result<()> { // Verify certificate chain let hrk = ca::Certificate::decode(&mut &HRK[..], ())?; (&hrk, &hrk) .verify() .context("HRK cert Signature validation failed.")?; - (&hrk, &evidence.cert_chain.hsk) + (&hrk, &cert_chain.hsk) .verify() .context("HSK cert Signature validation failed.")?; - (&evidence.cert_chain.hsk, &evidence.cert_chain.cek) + (&cert_chain.hsk, &cert_chain.cek) .verify() .context("CEK cert Signature validation failed.")?; - (&evidence.cert_chain.cek, &evidence.cert_chain.pek) + (&cert_chain.cek, &cert_chain.pek) .verify() .context("PEK cert Signature validation failed.")?; // Verify the TEE Hardware signature. - (&evidence.cert_chain.pek, &evidence.attestation_report) + + (&cert_chain.pek, attestation_report) .verify() .context("Attestation Report Signature validation failed.")?; diff --git a/attestation-service/attestation-service/src/verifier/mod.rs b/attestation-service/verifier/src/lib.rs similarity index 53% rename from attestation-service/attestation-service/src/verifier/mod.rs rename to attestation-service/verifier/src/lib.rs index 6b7252514..e50f8731a 100644 --- a/attestation-service/attestation-service/src/verifier/mod.rs +++ b/attestation-service/verifier/src/lib.rs @@ -1,6 +1,6 @@ use anyhow::*; use async_trait::async_trait; -use kbs_types::{Attestation, Tee}; +use kbs_types::Tee; pub mod sample; @@ -22,7 +22,7 @@ pub mod csv; #[cfg(feature = "cca-verifier")] pub mod cca; -pub(crate) fn to_verifier(tee: &Tee) -> Result> { +pub fn to_verifier(tee: &Tee) -> Result> { match tee { Tee::Sev => todo!(), Tee::AzSnpVtpm => { @@ -30,7 +30,7 @@ pub(crate) fn to_verifier(tee: &Tee) -> Result> if #[cfg(feature = "az-snp-vtpm-verifier")] { Ok(Box::::default() as Box) } else { - todo!() + bail!("feature `az-snp-vtpm-verifier` is not enabled for `verifier` crate.") } } } @@ -39,7 +39,7 @@ pub(crate) fn to_verifier(tee: &Tee) -> Result> if #[cfg(feature = "tdx-verifier")] { Ok(Box::::default() as Box) } else { - todo!() + bail!("feature `tdx-verifier` is not enabled for `verifier` crate.") } } } @@ -48,7 +48,7 @@ pub(crate) fn to_verifier(tee: &Tee) -> Result> if #[cfg(feature = "snp-verifier")] { Ok(Box::::default() as Box) } else { - bail!("SNP Verifier not enabled.") + bail!("feature `snp-verifier` is not enabled for `verifier` crate.") } } } @@ -58,7 +58,7 @@ pub(crate) fn to_verifier(tee: &Tee) -> Result> if #[cfg(feature = "sgx-verifier")] { Ok(Box::::default() as Box) } else { - anyhow::bail!("feature `sgx-verifier` is not enabled!"); + bail!("feature `sgx-verifier` is not enabled for `verifier` crate.") } } } @@ -68,7 +68,7 @@ pub(crate) fn to_verifier(tee: &Tee) -> Result> if #[cfg(feature = "csv-verifier")] { Ok(Box::::default() as Box) } else { - anyhow::bail!("feature `csv-verifier` is not enabled!"); + bail!("feature `csv-verifier` is not enabled for `verifier` crate.") } } } @@ -78,7 +78,7 @@ pub(crate) fn to_verifier(tee: &Tee) -> Result> if #[cfg(feature = "cca-verifier")] { Ok(Box::::default() as Box) } else { - anyhow::bail!("feature `cca-verifier` is not enabled!"); + bail!("feature `cca-verifier` is not enabled for `verifier` crate.") } } } @@ -87,14 +87,46 @@ pub(crate) fn to_verifier(tee: &Tee) -> Result> pub type TeeEvidenceParsedClaim = serde_json::Value; +pub enum ReportData<'a> { + Value(&'a [u8]), + NotProvided, +} + +pub enum InitDataHash<'a> { + Value(&'a [u8]), + NotProvided, +} + #[async_trait] pub trait Verifier { - /// Verify the hardware signature and report data in TEE quote. - /// If the verification is successful, a key-value pairs map of TCB status will be returned, - /// The policy engine of AS will carry out the verification of TCB status. + /// Verify the hardware signature. + /// + /// + /// `evidence` is a bytes slice of TEE evidence. Please note that + /// it might not be the raw attestation quote/evidence from hardware. + /// On some platforms they are wrapped by some extra context information. + /// Please see the concrete verifier implementations to check the format. + /// + /// + /// If `report_data` is given, the binding of the `report_data` + /// against the `report_data` inside the hardware evidence will + /// be checked. So do `init_data_hash`. + /// + /// + /// Semantically, a `report_data` is a byte slice given when + /// a hardware evidence is generated. The `report_data` will be + /// included inside the hardware evidence, thus its integrity will + /// be protected by the signature of the hardware. + /// + /// + /// A `init_data_hash` is another byte slice given when the TEE + /// instance is created. It is always provided by untrusted host, + /// but its integrity will be protected by the tee evidence. + /// Typical `init_data_hash` is `HOSTDATA` for SNP. async fn evaluate( &self, - nonce: String, - attestation: &Attestation, + evidence: &[u8], + expected_report_data: &ReportData, + expected_init_data_hash: &InitDataHash, ) -> Result; } diff --git a/attestation-service/verifier/src/sample/mod.rs b/attestation-service/verifier/src/sample/mod.rs new file mode 100644 index 000000000..6f6cdcded --- /dev/null +++ b/attestation-service/verifier/src/sample/mod.rs @@ -0,0 +1,85 @@ +use anyhow::{Context, Result}; +use log::debug; +extern crate serde; +use self::serde::{Deserialize, Serialize}; +use super::*; +use async_trait::async_trait; +use base64::Engine; +use serde_json::json; + +#[derive(Serialize, Deserialize, Debug)] +struct SampleTeeEvidence { + svn: String, + + #[serde(default = "String::default")] + report_data: String, + + #[serde(default = "String::default")] + init_data_hash: String, +} + +#[derive(Debug, Default)] +pub struct Sample {} + +#[async_trait] +impl Verifier for Sample { + async fn evaluate( + &self, + evidence: &[u8], + expected_report_data: &ReportData, + expected_init_data_hash: &InitDataHash, + ) -> Result { + let tee_evidence = serde_json::from_slice::(evidence) + .context("Deserialize Quote failed.")?; + + verify_tee_evidence(expected_report_data, expected_init_data_hash, &tee_evidence) + .await + .context("Evidence's identity verification error.")?; + + debug!("TEE-Evidence: {:?}", tee_evidence); + + parse_tee_evidence(&tee_evidence) + } +} + +async fn verify_tee_evidence( + expected_report_data: &ReportData<'_>, + expected_init_data_hash: &InitDataHash<'_>, + evidence: &SampleTeeEvidence, +) -> Result<()> { + // Verify the TEE Hardware signature. (Null for sample TEE) + + // Emulate the report data. + if let ReportData::Value(expected_report_data) = expected_report_data { + debug!("Check the binding of REPORT_DATA."); + let ev_report_data = base64::engine::general_purpose::STANDARD + .decode(&evidence.report_data) + .context("base64 decode report data for sample evidence")?; + if *expected_report_data != ev_report_data { + bail!("REPORT_DATA is different from that in Sample Quote"); + } + } + + // Emulate the init data hash. + if let InitDataHash::Value(expected_init_data_hash) = expected_init_data_hash { + debug!("Check the binding of init_data_digest."); + let ev_init_data_hash = base64::engine::general_purpose::STANDARD + .decode(&evidence.init_data_hash) + .context("base64 decode init data hash for sample evidence")?; + if *expected_init_data_hash != ev_init_data_hash { + bail!("INIT DATA HASH is different from that in Sample Quote"); + } + } + + Ok(()) +} + +// Dump the TCB status from the quote. +// Example: CPU SVN, RTMR, etc. +fn parse_tee_evidence(quote: &SampleTeeEvidence) -> Result { + let claims_map = json!({ + "svn": quote.svn, + }); + + Ok(claims_map as TeeEvidenceParsedClaim) +} diff --git a/attestation-service/verifier/src/sgx/claims.rs b/attestation-service/verifier/src/sgx/claims.rs new file mode 100644 index 000000000..6f30f06b0 --- /dev/null +++ b/attestation-service/verifier/src/sgx/claims.rs @@ -0,0 +1,191 @@ +// Copyright (c) 2023 Alibaba Cloud +// +// SPDX-License-Identifier: Apache-2.0 +// + +//! This module helps parse all fields inside an SGX Quote and +//! serialize it into a JSON. A sample JSON looks like +//! ```json +//! { +//! "header":{ +//! "version": "0300", +//! "att_key_type": "0200", +//! "att_key_data_0": "00000000", +//! "qe_svn": "0800", +//! "pce_svn": "0d00", +//! "vendor_id": "939a7233f79c4ca9940a0db3957f0607", +//! "user_data": "dccde9b31ce8860548173bb4a2a57a1600000000" +//! }, +//! "body":{ +//! "cpu_svn": "06060c0cffff00000000000000000000", +//! "misc_select": "01000000", +//! "reserved1": "000000000000000000000000", +//! "isv_ext_prod_id": "00000000000000000000000000000000", +//! "attributes.flags": "0700000000000000", +//! "attributes.xfrm": "e700000000000000", +//! "mr_enclave": "8f173e4613ff05c52aaf04162d234edae8c9977eae47eb2299ae16a553011c68", +//! "reserved2": "0000000000000000000000000000000000000000000000000000000000000000", +//! "mr_signer": "83d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e", +//! "reserved3": "0000000000000000000000000000000000000000000000000000000000000000", +//! "config_id": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", +//! "isv_prod_id": "0000", +//! "isv_svn": "0000", +//! "config_svn": "0000", +//! "reserved4": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000", +//! "isv_family_id": "00000000000000000000000000000000", +//! "report_data": "74657374000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" +//! } +//! } +//! ``` + +use anyhow::*; +use byteorder::{LittleEndian, ReadBytesExt}; +use serde_json::{Map, Value}; + +use crate::TeeEvidenceParsedClaim; + +use super::types::*; + +macro_rules! parse_claim { + ($map_name: ident, $key_name: literal, $field: ident) => { + $map_name.insert($key_name.to_string(), serde_json::Value::Object($field)) + }; + ($map_name: ident, $key_name: literal, $field: expr) => { + $map_name.insert( + $key_name.to_string(), + serde_json::Value::String(hex::encode($field)), + ) + }; +} + +pub fn generate_parsed_claims(quote: sgx_quote3_t) -> Result { + let mut quote_body = Map::new(); + let mut quote_header = Map::new(); + + // Claims from SGX Quote Header. + parse_claim!(quote_header, "version", quote.header.version); + parse_claim!(quote_header, "att_key_type", quote.header.att_key_type); + parse_claim!(quote_header, "att_key_data_0", quote.header.att_key_data_0); + parse_claim!(quote_header, "qe_svn", quote.header.qe_svn); + parse_claim!(quote_header, "pce_svn", quote.header.pce_svn); + parse_claim!(quote_header, "vendor_id", quote.header.vendor_id); + parse_claim!(quote_header, "user_data", quote.header.user_data); + + parse_claim!(quote_body, "cpu_svn", quote.report_body.cpu_svn); + parse_claim!(quote_body, "misc_select", quote.report_body.misc_select); + parse_claim!(quote_body, "reserved1", quote.report_body.reserved1); + parse_claim!( + quote_body, + "isv_ext_prod_id", + quote.report_body.isv_ext_prod_id + ); + parse_claim!( + quote_body, + "attributes.flags", + quote.report_body.attributes.flags + ); + parse_claim!( + quote_body, + "attributes.xfrm", + quote.report_body.attributes.xfrm + ); + parse_claim!(quote_body, "mr_enclave", quote.report_body.mr_enclave); + parse_claim!(quote_body, "reserved2", quote.report_body.reserved2); + parse_claim!(quote_body, "mr_signer", quote.report_body.mr_signer); + parse_claim!(quote_body, "reserved3", quote.report_body.reserved3); + parse_claim!(quote_body, "config_id", quote.report_body.config_id); + parse_claim!(quote_body, "isv_prod_id", quote.report_body.isv_prod_id); + parse_claim!(quote_body, "isv_svn", quote.report_body.isv_svn); + parse_claim!(quote_body, "config_svn", quote.report_body.config_svn); + parse_claim!(quote_body, "reserved4", quote.report_body.reserved4); + parse_claim!(quote_body, "isv_family_id", quote.report_body.isv_family_id); + parse_claim!(quote_body, "report_data", quote.report_body.report_data); + + let mut claims = Map::new(); + parse_claim!(claims, "header", quote_header); + parse_claim!(claims, "body", quote_body); + + log::info!("\nParsed Evidence claims map: \n{:?}\n", &claims); + + Ok(Value::Object(claims) as TeeEvidenceParsedClaim) +} + +/// Kernel Commandline Event inside Eventlog +pub struct TdShimPlatformConfigInfo<'a> { + pub descriptor: [u8; 16], + pub info_length: u32, + pub data: &'a [u8], +} + +impl<'a> TryFrom<&'a [u8]> for TdShimPlatformConfigInfo<'a> { + type Error = anyhow::Error; + + fn try_from(data: &'a [u8]) -> std::result::Result { + if data.len() < core::mem::size_of::<[u8; 16]>() + core::mem::size_of::() { + bail!("give data slice is too short"); + } + + let descriptor = data[0..core::mem::size_of::<[u8; 16]>()].try_into()?; + let info_length = (&data[core::mem::size_of::<[u8; 16]>() + ..core::mem::size_of::<[u8; 16]>() + core::mem::size_of::()]) + .read_u32::()?; + let data = &data[core::mem::size_of::<[u8; 16]>() + core::mem::size_of::() + ..core::mem::size_of::<[u8; 16]>() + + core::mem::size_of::() + + info_length as usize]; + Ok(Self { + descriptor, + info_length, + data, + }) + } +} + +#[cfg(test)] +mod tests { + use assert_json_diff::assert_json_eq; + use serde_json::json; + + use crate::sgx::parse_sgx_quote; + + use super::generate_parsed_claims; + + #[test] + fn parse_sgx_claims() { + let quote_bin = include_bytes!("../../test_data/occlum_quote.dat"); + let quote = parse_sgx_quote(quote_bin.as_slice()).expect("parse quote"); + let claims = generate_parsed_claims(quote).expect("parse claim failed"); + let expected = json!({ + "header":{ + "version": "0300", + "att_key_type": "0200", + "att_key_data_0": "00000000", + "qe_svn": "0800", + "pce_svn": "0d00", + "vendor_id": "939a7233f79c4ca9940a0db3957f0607", + "user_data": "dccde9b31ce8860548173bb4a2a57a1600000000" + }, + "body":{ + "cpu_svn": "06060c0cffff00000000000000000000", + "misc_select": "01000000", + "reserved1": "000000000000000000000000", + "isv_ext_prod_id": "00000000000000000000000000000000", + "attributes.flags": "0700000000000000", + "attributes.xfrm": "e700000000000000", + "mr_enclave": "8f173e4613ff05c52aaf04162d234edae8c9977eae47eb2299ae16a553011c68", + "reserved2": "0000000000000000000000000000000000000000000000000000000000000000", + "mr_signer": "83d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e", + "reserved3": "0000000000000000000000000000000000000000000000000000000000000000", + "config_id": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "isv_prod_id": "0000", + "isv_svn": "0000", + "config_svn": "0000", + "reserved4": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "isv_family_id": "00000000000000000000000000000000", + "report_data": "74657374000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + }); + + assert_json_eq!(expected, claims); + } +} diff --git a/attestation-service/attestation-service/src/verifier/sgx/mod.rs b/attestation-service/verifier/src/sgx/mod.rs similarity index 77% rename from attestation-service/attestation-service/src/verifier/sgx/mod.rs rename to attestation-service/verifier/src/sgx/mod.rs index 21a97af30..c94f9b820 100644 --- a/attestation-service/attestation-service/src/verifier/sgx/mod.rs +++ b/attestation-service/verifier/src/sgx/mod.rs @@ -11,15 +11,15 @@ use std::{ use anyhow::*; use async_trait::async_trait; use base64::Engine; -use kbs_types::Attestation; +use log::{debug, warn}; use scroll::Pread; use serde::{Deserialize, Serialize}; -use serde_json::{Map, Value}; use sgx_dcap_quoteverify_rs::{ sgx_ql_qv_result_t, sgx_ql_qv_supplemental_t, tee_get_supplemental_data_version_and_size, tee_qv_get_collateral, tee_supp_data_descriptor_t, tee_verify_quote, }; -use sha2::{Digest, Sha384}; + +use crate::{InitDataHash, ReportData}; use self::types::sgx_quote3_t; @@ -28,6 +28,8 @@ use super::{TeeEvidenceParsedClaim, Verifier}; #[allow(non_camel_case_types)] mod types; +mod claims; + pub const QUOTE_SIZE: usize = 436; #[derive(Debug, Serialize, Deserialize)] @@ -43,22 +45,18 @@ pub struct SgxVerifier {} impl Verifier for SgxVerifier { async fn evaluate( &self, - nonce: String, - attestation: &Attestation, + evidence: &[u8], + expected_report_data: &ReportData, + expected_init_data_hash: &InitDataHash, ) -> Result { - let tee_evidence = serde_json::from_str::(&attestation.tee_evidence) - .context("Deserialize Quote failed.")?; - - let mut hasher = Sha384::new(); - hasher.update(&nonce); - hasher.update(&attestation.tee_pubkey.k_mod); - hasher.update(&attestation.tee_pubkey.k_exp); - let mut hash_of_nonce_pubkey = hasher.finalize().to_vec(); - hash_of_nonce_pubkey.extend([0; 16]); + let tee_evidence = + serde_json::from_slice::(evidence).context("Deserialize Quote failed.")?; - debug!("TEE-Evidence: {:?}", &tee_evidence); + debug!("TEE-Evidence: {:?}", &tee_evidence); - verify_evidence(hash_of_nonce_pubkey, tee_evidence).await + verify_evidence(expected_report_data, expected_init_data_hash, tee_evidence) + .await + .map_err(|e| anyhow!("SGX Verifier: {:?}", e)) } } @@ -70,21 +68,32 @@ pub fn parse_sgx_quote(quote: &[u8]) -> Result { } async fn verify_evidence( - hash_of_nonce_pubkey: Vec, + expected_report_data: &ReportData<'_>, + expected_init_data_hash: &InitDataHash<'_>, evidence: SgxEvidence, ) -> Result { - let quote_bin = base64::engine::general_purpose::STANDARD.decode(evidence.quote.clone())?; + let quote_bin = base64::engine::general_purpose::STANDARD.decode(evidence.quote)?; ecdsa_quote_verification("e_bin) .await .context("Evidence's identity verification error.")?; let quote = parse_sgx_quote("e_bin)?; - if quote.report_body.report_data.d.to_vec() != hash_of_nonce_pubkey { - bail!("HASH(nonce||pubkey) is different from that in SGX Quote"); + if let ReportData::Value(expected_report_data) = expected_report_data { + debug!("Check the binding of REPORT_DATA."); + if *expected_report_data != quote.report_body.report_data { + bail!("REPORT_DATA is different from that in SGX Quote"); + } } - generate_parsed_claims(quote) + if let InitDataHash::Value(expected_init_data_hash) = expected_init_data_hash { + debug!("Check the binding of CONFIGID."); + if *expected_init_data_hash != quote.report_body.config_id { + bail!("MRCONFIGID is different from that in SGX Quote"); + } + } + + claims::generate_parsed_claims(quote) } async fn ecdsa_quote_verification(quote: &[u8]) -> Result<()> { @@ -181,23 +190,6 @@ async fn ecdsa_quote_verification(quote: &[u8]) -> Result<()> { Ok(()) } -fn generate_parsed_claims(quote: sgx_quote3_t) -> Result { - // TODO: Add more claims - // related issue: https://github.com/confidential-containers/enclave-cc/issues/121 - let mut claim_map = Map::new(); - - claim_map.insert( - "mr-signer".to_string(), - Value::String(hex::encode(quote.report_body.mr_signer.m)), - ); - claim_map.insert( - "mr-enclave".to_string(), - Value::String(hex::encode(quote.report_body.mr_enclave.m)), - ); - - Ok(Value::Object(claim_map) as TeeEvidenceParsedClaim) -} - #[cfg(test)] mod tests { use rstest::rstest; @@ -206,20 +198,20 @@ mod tests { use std::fs; #[rstest] - #[case("../test_data/occlum_quote.dat")] + #[case("./test_data/occlum_quote.dat")] fn test_parse_sgx_quote(#[case] quote_dir: &str) { let quote_bin = fs::read(quote_dir).expect("read quote"); let quote = parse_sgx_quote("e_bin); assert!(quote.is_ok()); let parsed_quote = format!("{}", quote.unwrap()); - let _ = fs::write("../test_data/parse_sgx_quote_output.txt", parsed_quote); + let _ = fs::write("./test_data/parse_sgx_quote_output.txt", parsed_quote); } #[ignore] #[rstest] #[tokio::test] - #[case("../test_data/occlum_quote.dat")] + #[case("./test_data/occlum_quote.dat")] async fn test_verify_sgx_quote(#[case] quote_dir: &str) { let quote_bin = fs::read(quote_dir).unwrap(); let res = ecdsa_quote_verification(quote_bin.as_slice()).await; diff --git a/attestation-service/attestation-service/src/verifier/sgx/types.rs b/attestation-service/verifier/src/sgx/types.rs similarity index 78% rename from attestation-service/attestation-service/src/verifier/sgx/types.rs rename to attestation-service/verifier/src/sgx/types.rs index 52fae2a60..19ae489ff 100644 --- a/attestation-service/attestation-service/src/verifier/sgx/types.rs +++ b/attestation-service/verifier/src/sgx/types.rs @@ -7,37 +7,20 @@ use core::fmt; use scroll::Pread; -pub type sgx_misc_select_t = u32; -pub type sgx_prod_id_t = u16; -pub type sgx_isv_svn_t = u16; -pub type sgx_config_svn_t = u16; - -#[repr(C)] -#[derive(Debug, Pread)] -pub struct sgx_measurement_t { - pub m: [u8; 32], -} - #[derive(Debug, Pread)] pub struct sgx_attributes_t { - pub flags: u64, - pub xfrm: u64, -} - -#[repr(C)] -#[derive(Debug, Pread)] -pub struct sgx_report_data_t { - pub d: [u8; 64], + pub flags: [u8; 8], + pub xfrm: [u8; 8], } #[repr(C)] #[derive(Debug, Pread)] pub struct sgx_report_body_t { /// ( 0) Security Version of the CPU - pub cpu_svn: sgx_cpu_svn_t, + pub cpu_svn: [u8; 16], /// ( 16) Which fields defined in SSA.MISC - pub misc_select: sgx_misc_select_t, + pub misc_select: [u8; 4], /// ( 20) pub reserved1: [u8; 12], @@ -49,13 +32,13 @@ pub struct sgx_report_body_t { pub attributes: sgx_attributes_t, /// ( 64) The value of the enclave's ENCLAVE measurement - pub mr_enclave: sgx_measurement_t, + pub mr_enclave: [u8; 32], /// ( 96) pub reserved2: [u8; 32], /// (128) The value of the enclave's SIGNER measurement - pub mr_signer: sgx_measurement_t, + pub mr_signer: [u8; 32], /// (160) pub reserved3: [u8; 32], @@ -64,13 +47,13 @@ pub struct sgx_report_body_t { pub config_id: [u8; 64], /// (256) Product ID of the Enclave - pub isv_prod_id: sgx_prod_id_t, + pub isv_prod_id: [u8; 2], /// (258) Security Version of the Enclave - pub isv_svn: sgx_isv_svn_t, + pub isv_svn: [u8; 2], /// (260) CONFIGSVN - pub config_svn: sgx_config_svn_t, + pub config_svn: [u8; 2], /// (262) pub reserved4: [u8; 42], @@ -79,33 +62,27 @@ pub struct sgx_report_body_t { pub isv_family_id: [u8; 16], /// (320) Data provided by the user - pub report_data: sgx_report_data_t, -} - -#[repr(C)] -#[derive(Debug, Pread)] -pub struct sgx_cpu_svn_t { - pub svn: [u8; 16], + pub report_data: [u8; 64], } #[repr(C)] #[derive(Debug, Pread)] pub struct sgx_quote_header_t { ///< 0: The version this quote structure. - pub version: u16, + pub version: [u8; 2], ///< 2: sgx_attestation_algorithm_id_t. Describes the type of /// signature in the signature_data[] field. - pub att_key_type: u16, + pub att_key_type: [u8; 2], ///< 4: Optionally stores additional data associated with the att_key_type. - pub att_key_data_0: u32, + pub att_key_data_0: [u8; 4], ///< 8: The ISV_SVN of the Quoting Enclave when the quote was generated. - pub qe_svn: sgx_isv_svn_t, + pub qe_svn: [u8; 2], ///< 10: The ISV_SVN of the PCE when the quote was generated. - pub pce_svn: sgx_isv_svn_t, + pub pce_svn: [u8; 2], ///< 12: Unique identifier of QE Vendor. pub vendor_id: [u8; 16], @@ -173,15 +150,15 @@ REPORT BODY \treserved4:\t{:X?} \tisv_family_id:\t{:X?} \treport_data:\t{:X?}\n", - self.report_body.cpu_svn.svn, + self.report_body.cpu_svn, self.report_body.misc_select, self.report_body.reserved1, self.report_body.isv_ext_prod_id, self.report_body.attributes.flags, self.report_body.attributes.xfrm, - self.report_body.mr_enclave.m, + self.report_body.mr_enclave, self.report_body.reserved2, - self.report_body.mr_signer.m, + self.report_body.mr_signer, self.report_body.reserved3, self.report_body.config_id, self.report_body.isv_prod_id, @@ -189,7 +166,7 @@ REPORT BODY self.report_body.config_svn, self.report_body.reserved4, self.report_body.isv_family_id, - self.report_body.report_data.d, + self.report_body.report_data, )?; write!( diff --git a/attestation-service/attestation-service/src/verifier/snp/milan_ask_ark.pem b/attestation-service/verifier/src/snp/milan_ask_ark.pem similarity index 100% rename from attestation-service/attestation-service/src/verifier/snp/milan_ask_ark.pem rename to attestation-service/verifier/src/snp/milan_ask_ark.pem diff --git a/attestation-service/attestation-service/src/verifier/snp/mod.rs b/attestation-service/verifier/src/snp/mod.rs similarity index 93% rename from attestation-service/attestation-service/src/verifier/snp/mod.rs rename to attestation-service/verifier/src/snp/mod.rs index abb324063..326f4b125 100644 --- a/attestation-service/attestation-service/src/verifier/snp/mod.rs +++ b/attestation-service/verifier/src/snp/mod.rs @@ -1,11 +1,11 @@ use anyhow::{anyhow, Context, Result}; use base64::Engine; +use log::debug; extern crate serde; use self::serde::{Deserialize, Serialize}; use super::*; use asn1_rs::{oid, Integer, OctetString, Oid}; use async_trait::async_trait; -use kbs_types::TeePubKey; use openssl::{ ec::EcKey, ecdsa, @@ -16,7 +16,6 @@ use openssl::{ use serde_json::json; use sev::firmware::guest::AttestationReport; use sev::firmware::host::{CertTableEntry, CertType}; -use sha2::{Digest, Sha384}; use x509_parser::prelude::*; #[derive(Serialize, Deserialize)] @@ -38,13 +37,14 @@ pub struct Snp {} impl Verifier for Snp { async fn evaluate( &self, - nonce: String, - attestation: &Attestation, + evidence: &[u8], + expected_report_data: &ReportData, + expected_init_data_hash: &InitDataHash, ) -> Result { let SnpEvidence { attestation_report: report, cert_chain, - } = serde_json::from_str(&attestation.tee_evidence).context("Deserialize Quote failed.")?; + } = serde_json::from_slice(evidence).context("Deserialize Quote failed.")?; verify_report_signature(&report, &cert_chain)?; @@ -56,9 +56,21 @@ impl Verifier for Snp { return Err(anyhow!("VMPL Check Failed")); } - let expected_report_data = calculate_expected_report_data(&nonce, &attestation.tee_pubkey); - if report.report_data != expected_report_data { - return Err(anyhow!("Report Data Mismatch")); + let ReportData::Value(expected_report_data) = expected_report_data else { + bail!("Report Data unset"); + }; + + debug!("Check the binding of REPORT_DATA."); + + if *expected_report_data != report.report_data { + bail!("Report Data Mismatch"); + } + + if let InitDataHash::Value(expected_init_data_hash) = expected_init_data_hash { + debug!("Check the binding of HOST_DATA."); + if *expected_init_data_hash != report.host_data { + bail!("Host Data Mismatch"); + } } Ok(parse_tee_evidence(&report)) @@ -182,21 +194,6 @@ fn verify_cert_chain(cert_chain: &[CertTableEntry], ask: X509, ark: X509) -> Res Ok(vcek) } -fn calculate_expected_report_data(nonce: &String, tee_pubkey: &TeePubKey) -> [u8; 64] { - let mut hasher = Sha384::new(); - - hasher.update(nonce.as_bytes()); - hasher.update(&tee_pubkey.k_mod); - hasher.update(&tee_pubkey.k_exp); - - let partial_hash = hasher.finalize(); - - let mut hash = [0u8; 64]; - hash[..48].copy_from_slice(&partial_hash); - - hash -} - pub(crate) fn parse_tee_evidence(report: &AttestationReport) -> TeeEvidenceParsedClaim { let claims_map = json!({ // policy fields diff --git a/attestation-service/attestation-service/src/verifier/snp/test-report.bin b/attestation-service/verifier/src/snp/test-report.bin similarity index 100% rename from attestation-service/attestation-service/src/verifier/snp/test-report.bin rename to attestation-service/verifier/src/snp/test-report.bin diff --git a/attestation-service/attestation-service/src/verifier/snp/test-vcek-invalid-legacy.der b/attestation-service/verifier/src/snp/test-vcek-invalid-legacy.der similarity index 100% rename from attestation-service/attestation-service/src/verifier/snp/test-vcek-invalid-legacy.der rename to attestation-service/verifier/src/snp/test-vcek-invalid-legacy.der diff --git a/attestation-service/attestation-service/src/verifier/snp/test-vcek-invalid-new.der b/attestation-service/verifier/src/snp/test-vcek-invalid-new.der similarity index 100% rename from attestation-service/attestation-service/src/verifier/snp/test-vcek-invalid-new.der rename to attestation-service/verifier/src/snp/test-vcek-invalid-new.der diff --git a/attestation-service/attestation-service/src/verifier/snp/test-vcek.der b/attestation-service/verifier/src/snp/test-vcek.der similarity index 100% rename from attestation-service/attestation-service/src/verifier/snp/test-vcek.der rename to attestation-service/verifier/src/snp/test-vcek.der diff --git a/attestation-service/attestation-service/src/verifier/tdx/claims.rs b/attestation-service/verifier/src/tdx/claims.rs similarity index 98% rename from attestation-service/attestation-service/src/verifier/tdx/claims.rs rename to attestation-service/verifier/src/tdx/claims.rs index fb7579f2b..faf63854d 100644 --- a/attestation-service/attestation-service/src/verifier/tdx/claims.rs +++ b/attestation-service/verifier/src/tdx/claims.rs @@ -43,9 +43,10 @@ use anyhow::*; use byteorder::{LittleEndian, ReadBytesExt}; +use log::{debug, warn}; use serde_json::{Map, Value}; -use crate::verifier::TeeEvidenceParsedClaim; +use crate::TeeEvidenceParsedClaim; use super::{ eventlog::{CcEventLog, MeasuredEntity}, @@ -228,7 +229,7 @@ mod tests { use assert_json_diff::assert_json_eq; use serde_json::{json, to_value, Map, Value}; - use crate::verifier::tdx::{eventlog::CcEventLog, quote::parse_tdx_quote}; + use crate::tdx::{eventlog::CcEventLog, quote::parse_tdx_quote}; use super::{generate_parsed_claim, parse_kernel_parameters}; @@ -241,8 +242,8 @@ mod tests { #[test] fn parse_tdx_claims() { - let quote_bin = std::fs::read("../test_data/tdx_quote_4.dat").expect("read quote failed"); - let ccel_bin = std::fs::read("../test_data/CCEL_data").expect("read ccel failed"); + let quote_bin = std::fs::read("./test_data/tdx_quote_4.dat").expect("read quote failed"); + let ccel_bin = std::fs::read("./test_data/CCEL_data").expect("read ccel failed"); let quote = parse_tdx_quote("e_bin).expect("parse quote"); let ccel = CcEventLog::try_from(ccel_bin).expect("parse ccel"); let claims = generate_parsed_claim(quote, Some(ccel)).expect("parse claim failed"); diff --git a/attestation-service/attestation-service/src/verifier/tdx/eventlog.rs b/attestation-service/verifier/src/tdx/eventlog.rs similarity index 95% rename from attestation-service/attestation-service/src/verifier/tdx/eventlog.rs rename to attestation-service/verifier/src/tdx/eventlog.rs index 6c9007843..4f4e124b6 100644 --- a/attestation-service/attestation-service/src/verifier/tdx/eventlog.rs +++ b/attestation-service/verifier/src/tdx/eventlog.rs @@ -4,6 +4,7 @@ use core::mem::size_of; use eventlog_rs::Eventlog; use std::convert::{TryFrom, TryInto}; use std::string::ToString; +use strum_macros::{Display, EnumString}; #[derive(Debug, Clone, EnumString, Display)] pub enum MeasuredEntity { @@ -163,7 +164,7 @@ mod tests { #[test] fn test_parse_eventlog() { - let ccel_bin = fs::read("../test_data/CCEL_data").unwrap(); + let ccel_bin = fs::read("./test_data/CCEL_data").unwrap(); let ccel = CcEventLog::try_from(ccel_bin).unwrap(); let _ = fs::write( @@ -174,7 +175,7 @@ mod tests { #[test] fn test_rebuild_rtmr() { - let ccel_bin = fs::read("../test_data/CCEL_data").unwrap(); + let ccel_bin = fs::read("./test_data/CCEL_data").unwrap(); let ccel = CcEventLog::try_from(ccel_bin).unwrap(); let rtmr_result = ccel.rebuild_rtmr(); @@ -189,12 +190,12 @@ mod tests { hex::encode(rtmr.rtmr3) ); - let _ = fs::write("../test_data/rebuild_rtmr_output.txt", output); + let _ = fs::write("./test_data/rebuild_rtmr_output.txt", output); } #[test] fn test_query_digest() { - let ccel_bin = fs::read("../test_data/CCEL_data").unwrap(); + let ccel_bin = fs::read("./test_data/CCEL_data").unwrap(); let ccel = CcEventLog::try_from(ccel_bin).unwrap(); let kernel_hash = ccel.query_digest(MeasuredEntity::TdShimKernel); diff --git a/attestation-service/attestation-service/src/verifier/tdx/mod.rs b/attestation-service/verifier/src/tdx/mod.rs similarity index 65% rename from attestation-service/attestation-service/src/verifier/tdx/mod.rs rename to attestation-service/verifier/src/tdx/mod.rs index 4cdecbb98..e697b691c 100644 --- a/attestation-service/attestation-service/src/verifier/tdx/mod.rs +++ b/attestation-service/verifier/src/tdx/mod.rs @@ -1,7 +1,9 @@ use anyhow::{anyhow, Context, Result}; +use log::{debug, warn}; extern crate serde; extern crate strum; -use crate::verifier::tdx::claims::generate_parsed_claim; + +use crate::tdx::claims::generate_parsed_claim; use self::serde::{Deserialize, Serialize}; use super::*; @@ -9,7 +11,6 @@ use async_trait::async_trait; use base64::Engine; use eventlog::{CcEventLog, Rtmr}; use quote::{ecdsa_quote_verification, parse_tdx_quote}; -use sha2::{Digest, Sha384}; mod claims; mod eventlog; @@ -31,37 +32,26 @@ pub struct Tdx {} impl Verifier for Tdx { async fn evaluate( &self, - nonce: String, - attestation: &Attestation, + evidence: &[u8], + expected_report_data: &ReportData, + expected_init_data_hash: &InitDataHash, ) -> Result { - let tdx_evidence = serde_json::from_str::(&attestation.tee_evidence) + let tdx_evidence = serde_json::from_slice::(evidence) .context("Deserialize TDX Evidence failed.")?; - let mut hasher = Sha384::new(); - hasher.update(&nonce); - hasher.update(&attestation.tee_pubkey.k_mod); - hasher.update(&attestation.tee_pubkey.k_exp); - let mut hash_of_nonce_pubkey = hasher.finalize().to_vec(); - hash_of_nonce_pubkey.extend([0; 16]); - - log::info!( - "HASH(nonce||pubkey):\n\t{}\n", - hex::encode(&hash_of_nonce_pubkey) - ); - - verify_evidence(hash_of_nonce_pubkey, &tdx_evidence) + verify_evidence(expected_report_data, expected_init_data_hash, tdx_evidence) .await .map_err(|e| anyhow!("TDX Verifier: {:?}", e)) } } -#[allow(unused_assignments)] async fn verify_evidence( - hash_of_nonce_pubkey: Vec, - evidence: &TdxEvidence, + expected_report_data: &ReportData<'_>, + expected_init_data_hash: &InitDataHash<'_>, + evidence: TdxEvidence, ) -> Result { // Verify TD quote ECDSA signature. - let quote_bin = base64::engine::general_purpose::STANDARD.decode(evidence.quote.clone())?; + let quote_bin = base64::engine::general_purpose::STANDARD.decode(evidence.quote)?; ecdsa_quote_verification(quote_bin.as_slice()).await?; // Parse quote and Compare report data @@ -69,18 +59,25 @@ async fn verify_evidence( log::info!("{}\n", "e); - if hash_of_nonce_pubkey != quote.report_body.report_data.to_vec() { - return Err(anyhow!( - "HASH(nonce||pubkey) is different from that in TDX Quote" - )); + if let ReportData::Value(expected_report_data) = expected_report_data { + debug!("Check the binding of REPORT_DATA."); + if *expected_report_data != quote.report_body.report_data { + bail!("REPORT_DATA is different from that in TDX Quote"); + } + } + + if let InitDataHash::Value(expected_init_data_hash) = expected_init_data_hash { + debug!("Check the binding of MRCONFIGID."); + if *expected_init_data_hash != quote.report_body.mr_config_id { + bail!("MRCONFIGID is different from that in TDX Quote"); + } } // Verify Integrity of CC Eventlog - let mut ccel_data = Vec::default(); let mut ccel_option = Option::default(); match &evidence.cc_eventlog { Some(el) => { - ccel_data = base64::engine::general_purpose::STANDARD.decode(el)?; + let ccel_data = base64::engine::general_purpose::STANDARD.decode(el)?; let ccel = CcEventLog::try_from(ccel_data) .map_err(|e| anyhow!("Parse CC Eventlog failed: {:?}", e))?; ccel_option = Some(ccel.clone()); @@ -107,21 +104,23 @@ async fn verify_evidence( #[cfg(test)] mod tests { + use crate::tdx::claims::generate_parsed_claim; + use super::*; use std::fs; #[test] fn test_generate_parsed_claim() { - let ccel_bin = fs::read("../test_data/CCEL_data").unwrap(); + let ccel_bin = fs::read("./test_data/CCEL_data").unwrap(); let ccel = CcEventLog::try_from(ccel_bin).unwrap(); - let quote_bin = fs::read("../test_data/tdx_quote_4.dat").unwrap(); + let quote_bin = fs::read("./test_data/tdx_quote_4.dat").unwrap(); let quote = parse_tdx_quote("e_bin).unwrap(); let parsed_claim = generate_parsed_claim(quote, Some(ccel)); assert!(parsed_claim.is_ok()); let _ = fs::write( - "../test_data/evidence_claim_output.txt", + "./test_data/evidence_claim_output.txt", format!("{:?}", parsed_claim.unwrap()), ); } diff --git a/attestation-service/attestation-service/src/verifier/tdx/quote.rs b/attestation-service/verifier/src/tdx/quote.rs similarity index 98% rename from attestation-service/attestation-service/src/verifier/tdx/quote.rs rename to attestation-service/verifier/src/tdx/quote.rs index 3bbe79cbb..a7e65295a 100644 --- a/attestation-service/attestation-service/src/verifier/tdx/quote.rs +++ b/attestation-service/verifier/src/tdx/quote.rs @@ -1,5 +1,6 @@ use anyhow::{anyhow, bail, Result}; use core::fmt; +use log::{debug, warn}; use qvl::{ sgx_ql_qv_result_t, sgx_ql_qv_supplemental_t, tee_get_supplemental_data_version_and_size, tee_qv_get_collateral, tee_supp_data_descriptor_t, tee_verify_quote, @@ -252,7 +253,7 @@ mod tests { #[test] fn test_parse_tdx_quote() { - let quote_bin = fs::read("../test_data/tdx_quote_4.dat").unwrap(); + let quote_bin = fs::read("./test_data/tdx_quote_4.dat").unwrap(); let quote = parse_tdx_quote("e_bin); assert!(quote.is_ok()); @@ -264,7 +265,7 @@ mod tests { #[ignore] #[tokio::test] async fn test_verify_tdx_quote() { - let quote_bin = fs::read("../test_data/quote.dat").unwrap(); + let quote_bin = fs::read("./test_data/quote.dat").unwrap(); let res = ecdsa_quote_verification(quote_bin.as_slice()).await; assert!(res.is_ok(), "error"); } diff --git a/attestation-service/test_data/CCEL_data b/attestation-service/verifier/test_data/CCEL_data similarity index 100% rename from attestation-service/test_data/CCEL_data rename to attestation-service/verifier/test_data/CCEL_data diff --git a/attestation-service/test_data/az-hcl-data.bin b/attestation-service/verifier/test_data/az-hcl-data.bin similarity index 100% rename from attestation-service/test_data/az-hcl-data.bin rename to attestation-service/verifier/test_data/az-hcl-data.bin diff --git a/attestation-service/test_data/az-vcek.pem b/attestation-service/verifier/test_data/az-vcek.pem similarity index 100% rename from attestation-service/test_data/az-vcek.pem rename to attestation-service/verifier/test_data/az-vcek.pem diff --git a/attestation-service/test_data/az-vtpm-quote-msg.bin b/attestation-service/verifier/test_data/az-vtpm-quote-msg.bin similarity index 100% rename from attestation-service/test_data/az-vtpm-quote-msg.bin rename to attestation-service/verifier/test_data/az-vtpm-quote-msg.bin diff --git a/attestation-service/test_data/az-vtpm-quote-sig.bin b/attestation-service/verifier/test_data/az-vtpm-quote-sig.bin similarity index 100% rename from attestation-service/test_data/az-vtpm-quote-sig.bin rename to attestation-service/verifier/test_data/az-vtpm-quote-sig.bin diff --git a/attestation-service/test_data/cca-claims.json b/attestation-service/verifier/test_data/cca-claims.json similarity index 100% rename from attestation-service/test_data/cca-claims.json rename to attestation-service/verifier/test_data/cca-claims.json diff --git a/attestation-service/test_data/occlum_quote.dat b/attestation-service/verifier/test_data/occlum_quote.dat similarity index 100% rename from attestation-service/test_data/occlum_quote.dat rename to attestation-service/verifier/test_data/occlum_quote.dat diff --git a/attestation-service/test_data/tdx_quote_4.dat b/attestation-service/verifier/test_data/tdx_quote_4.dat similarity index 100% rename from attestation-service/test_data/tdx_quote_4.dat rename to attestation-service/verifier/test_data/tdx_quote_4.dat diff --git a/kbs/tools/client/src/lib.rs b/kbs/tools/client/src/lib.rs index 8e9228ddd..23e45dae9 100644 --- a/kbs/tools/client/src/lib.rs +++ b/kbs/tools/client/src/lib.rs @@ -5,7 +5,6 @@ //! KBS client SDK. use anyhow::{anyhow, bail, Result}; -use as_types::SetPolicyInput; use base64::engine::general_purpose::STANDARD; use base64::Engine; use jwt_simple::prelude::{Claims, Duration, Ed25519KeyPair, EdDSAKeyPairLike}; @@ -104,6 +103,13 @@ pub async fn get_resource_with_attestation( Ok(resource_bytes) } +#[derive(Serialize)] +pub struct SetPolicyInput { + pub r#type: String, + pub policy_id: String, + pub policy: String, +} + /// Set attestation policy /// Input parameters: /// - url: KBS server root URL. From f825bcd2eefcbb9ab641d937de7672f63992ed0a Mon Sep 17 00:00:00 2001 From: Xynnn007 Date: Tue, 7 Nov 2023 15:48:38 +0800 Subject: [PATCH 03/11] AS: move rvps as a separate crate Signed-off-by: Xynnn007 --- Cargo.lock | 108 +++++----------- Cargo.toml | 7 +- .../attestation-service/Cargo.toml | 16 +-- .../attestation-service/src/config.rs | 17 ++- .../attestation-service/src/lib.rs | 101 +++++---------- .../src/policy_engine/mod.rs | 1 + .../src/policy_engine/opa/mod.rs | 1 + .../attestation-service/src/rvps/builtin.rs | 33 +++++ .../attestation-service/src/rvps/grpc.rs | 19 +-- .../attestation-service/src/rvps/mod.rs | 117 ++++++++++------- .../attestation-service/src/token/mod.rs | 2 +- .../attestation-service/src/utils.rs | 4 +- attestation-service/bin/grpc-as/Cargo.toml | 2 +- attestation-service/bin/grpc-as/src/main.rs | 11 +- attestation-service/bin/grpc-as/src/server.rs | 23 +--- .../bin/rvps-client/Cargo.toml | 20 --- .../bin/rvps-client/src/main.rs | 121 ------------------ attestation-service/bin/rvps/Cargo.toml | 20 --- attestation-service/bin/rvps/build.rs | 23 ---- attestation-service/bin/rvps/src/main.rs | 66 ---------- attestation-service/rvps/Cargo.toml | 46 +++++++ .../{Dockerfile.rvps => rvps/Dockerfile} | 9 +- .../{bin/rvps-client => rvps}/build.rs | 2 +- attestation-service/rvps/cgo/go.mod | 15 +++ attestation-service/rvps/cgo/go.sum | 11 ++ attestation-service/rvps/cgo/intoto.go | 85 ++++++++++++ attestation-service/rvps/src/bin/rvps-tool.rs | 105 +++++++++++++++ attestation-service/rvps/src/bin/rvps.rs | 49 +++++++ .../server.rs => rvps/src/bin/server/mod.rs} | 14 +- .../extractor_modules/in_toto/README.md | 0 .../extractor_modules/in_toto/mod.rs | 0 .../extractor_modules/in_toto/shim/README.md | 0 .../extractor_modules/in_toto/shim/mod.rs | 0 .../src}/extractors/extractor_modules/mod.rs | 2 +- .../extractor_modules/sample/README.md | 0 .../extractor_modules/sample/mod.rs | 3 +- .../src/rvps => rvps/src}/extractors/mod.rs | 0 attestation-service/rvps/src/lib.rs | 41 ++++++ .../src/rvps => rvps/src}/native.rs | 30 +++-- .../rvps => rvps/src}/pre_processor/mod.rs | 0 .../src/rvps => rvps/src}/reference_value.rs | 0 .../src}/store/local_fs/README.md | 0 .../rvps => rvps/src}/store/local_fs/mod.rs | 6 +- .../src/rvps => rvps/src}/store/mod.rs | 2 +- kbs/src/api/Cargo.toml | 2 +- kbs/src/api/src/attestation/coco/builtin.rs | 4 +- kbs/src/api/src/attestation/mod.rs | 4 +- kbs/src/kbs/src/main.rs | 2 +- kbs/test/data/e2e/kbs.toml | 4 + 49 files changed, 601 insertions(+), 547 deletions(-) create mode 100644 attestation-service/attestation-service/src/rvps/builtin.rs delete mode 100644 attestation-service/bin/rvps-client/Cargo.toml delete mode 100644 attestation-service/bin/rvps-client/src/main.rs delete mode 100644 attestation-service/bin/rvps/Cargo.toml delete mode 100644 attestation-service/bin/rvps/build.rs delete mode 100644 attestation-service/bin/rvps/src/main.rs create mode 100644 attestation-service/rvps/Cargo.toml rename attestation-service/{Dockerfile.rvps => rvps/Dockerfile} (70%) rename attestation-service/{bin/rvps-client => rvps}/build.rs (80%) create mode 100644 attestation-service/rvps/cgo/go.mod create mode 100644 attestation-service/rvps/cgo/go.sum create mode 100644 attestation-service/rvps/cgo/intoto.go create mode 100644 attestation-service/rvps/src/bin/rvps-tool.rs create mode 100644 attestation-service/rvps/src/bin/rvps.rs rename attestation-service/{bin/rvps/src/server.rs => rvps/src/bin/server/mod.rs} (85%) rename attestation-service/{attestation-service/src/rvps => rvps/src}/extractors/extractor_modules/in_toto/README.md (100%) rename attestation-service/{attestation-service/src/rvps => rvps/src}/extractors/extractor_modules/in_toto/mod.rs (100%) rename attestation-service/{attestation-service/src/rvps => rvps/src}/extractors/extractor_modules/in_toto/shim/README.md (100%) rename attestation-service/{attestation-service/src/rvps => rvps/src}/extractors/extractor_modules/in_toto/shim/mod.rs (100%) rename attestation-service/{attestation-service/src/rvps => rvps/src}/extractors/extractor_modules/mod.rs (98%) rename attestation-service/{attestation-service/src/rvps => rvps/src}/extractors/extractor_modules/sample/README.md (100%) rename attestation-service/{attestation-service/src/rvps => rvps/src}/extractors/extractor_modules/sample/mod.rs (98%) rename attestation-service/{attestation-service/src/rvps => rvps/src}/extractors/mod.rs (100%) create mode 100644 attestation-service/rvps/src/lib.rs rename attestation-service/{attestation-service/src/rvps => rvps/src}/native.rs (74%) rename attestation-service/{attestation-service/src/rvps => rvps/src}/pre_processor/mod.rs (100%) rename attestation-service/{attestation-service/src/rvps => rvps/src}/reference_value.rs (100%) rename attestation-service/{attestation-service/src/rvps => rvps/src}/store/local_fs/README.md (100%) rename attestation-service/{attestation-service/src/rvps => rvps/src}/store/local_fs/mod.rs (97%) rename attestation-service/{attestation-service/src/rvps => rvps/src}/store/mod.rs (98%) diff --git a/Cargo.lock b/Cargo.lock index 8def2400e..72a344718 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -530,29 +530,23 @@ dependencies = [ "async-trait", "base64 0.21.5", "cfg-if", - "chrono", "futures", - "jsonwebtoken", - "jwt", "kbs-types", "lazy_static", "log", "openssl", - "path-clean", "prost", "rand", + "reference-value-provider-service", "rsa 0.9.3", "rstest", - "scroll", "serde", "serde_json", "serde_variant", "serial_test", "sha2", "shadow-rs", - "sled", - "strum 0.24.1", - "strum_macros 0.24.3", + "strum 0.25.0", "testing_logger", "time", "tokio", @@ -1040,10 +1034,8 @@ checksum = "4ea181bf566f71cb9a5d17a59e1871af638180a18fb0035c92ae62b705207123" dependencies = [ "atty", "bitflags 1.3.2", - "clap_derive 3.2.25", "clap_lex 0.2.4", "indexmap 1.9.3", - "once_cell", "strsim 0.10.0", "termcolor", "textwrap 0.16.0", @@ -1056,7 +1048,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" dependencies = [ "clap_builder", - "clap_derive 4.4.7", + "clap_derive", ] [[package]] @@ -1071,19 +1063,6 @@ dependencies = [ "strsim 0.10.0", ] -[[package]] -name = "clap_derive" -version = "3.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae6371b8bdc8b7d3959e9cf7b22d4435ef3e79e138688421ec654acf8c81b008" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "clap_derive" version = "4.4.7" @@ -2407,22 +2386,6 @@ dependencies = [ "simple_asn1", ] -[[package]] -name = "jwt" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f" -dependencies = [ - "base64 0.13.1", - "crypto-common", - "digest", - "hmac", - "openssl", - "serde", - "serde_json", - "sha2", -] - [[package]] name = "jwt-simple" version = "0.11.9" @@ -3099,12 +3062,6 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" -[[package]] -name = "path-clean" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef" - [[package]] name = "pathdiff" version = "0.2.1" @@ -3605,6 +3562,33 @@ dependencies = [ "thiserror", ] +[[package]] +name = "reference-value-provider-service" +version = "0.1.0" +dependencies = [ + "anyhow", + "assert-json-diff", + "async-trait", + "base64 0.21.5", + "cfg-if", + "chrono", + "clap 4.4.7", + "env_logger 0.10.0", + "log", + "prost", + "rstest", + "serde", + "serde_json", + "serial_test", + "shadow-rs", + "sled", + "strum 0.25.0", + "tempfile", + "tokio", + "tonic 0.8.3", + "tonic-build", +] + [[package]] name = "regex" version = "1.10.2" @@ -3949,38 +3933,6 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" -[[package]] -name = "rvps" -version = "0.1.0" -dependencies = [ - "anyhow", - "attestation-service", - "clap 3.2.25", - "env_logger 0.10.0", - "log", - "prost", - "serde_json", - "shadow-rs", - "tokio", - "tonic 0.8.3", - "tonic-build", -] - -[[package]] -name = "rvps-client" -version = "0.1.0" -dependencies = [ - "anyhow", - "clap 3.2.25", - "env_logger 0.10.0", - "log", - "prost", - "shadow-rs", - "tokio", - "tonic 0.8.3", - "tonic-build", -] - [[package]] name = "ryu" version = "1.0.15" diff --git a/Cargo.toml b/Cargo.toml index 4a69e7557..318933112 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,9 +5,8 @@ members = [ "kbs/tools/client", "attestation-service/attestation-service", "attestation-service/verifier", - "attestation-service/bin/rvps", + "attestation-service/rvps", "attestation-service/bin/grpc-as", - "attestation-service/bin/rvps-client" ] [workspace.package] @@ -25,7 +24,8 @@ async-trait = "0.1.31" attestation-service = { path = "attestation-service/attestation-service", default-features = false } base64 = "0.21" cfg-if = "1.0.0" -clap = { version = "3.2.6", features = ["derive"] } +chrono = "0.4.19" +clap = { version = "4", features = ["derive"] } env_logger = "0.10.0" kbs-types.path = "attestation-service/as-types" log = "0.4.17" @@ -36,6 +36,7 @@ serde_json = "1.0.89" serial_test = "0.9.0" sha2 = "0.10" shadow-rs = "0.19.0" +strum = { version = "0.25", features = ["derive"] } thiserror = "1.0" tokio = { version = "1.23.0", features = ["full"] } tempfile = "3.4.0" diff --git a/attestation-service/attestation-service/Cargo.toml b/attestation-service/attestation-service/Cargo.toml index 1ba70e162..ae45cbe9b 100644 --- a/attestation-service/attestation-service/Cargo.toml +++ b/attestation-service/attestation-service/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2021" [features] -default = [ "rvps-grpc", "all-verifier" ] +default = [ "rvps-grpc", "rvps-builtin", "all-verifier" ] all-verifier = [ "verifier/all-verifier" ] tdx-verifier = [ "verifier/tdx-verifier" ] sgx-verifier = [ "verifier/sgx-verifier" ] @@ -13,7 +13,9 @@ snp-verifier = [ "verifier/snp-verifier" ] csv-verifier = [ "verifier/csv-verifier" ] cca-verifier = [ "verifier/cca-verifier" ] -rvps-native = [] +# Only for testing and CI +rvps-builtin = [ "reference-value-provider-service" ] + rvps-grpc = [ "tonic" ] [dependencies] @@ -21,28 +23,22 @@ anyhow.workspace = true async-trait.workspace = true base64 = "0.21" cfg-if = "1.0.0" -chrono = { version = "0.4.19", features = [ "serde" ] } futures = "0.3.17" -jsonwebtoken = "8" -jwt = { version = "0.16.0", features = ["openssl"]} # TODO: change it to "0.5", once released. kbs-types = { git = "https://github.com/virtee/kbs-types", rev = "c90df0e" } lazy_static = "1.4.0" log.workspace = true openssl = { version = "0.10.55", optional = true } -path-clean = "1.0.1" prost.workspace = true rand = "0.8.5" rsa = { version = "0.9.2", features = ["sha2"] } -scroll = { version = "0.11.0", default-features = false, features = ["derive"], optional = true } +reference-value-provider-service = { path = "../rvps", optional = true } serde.workspace = true serde_json.workspace = true serde_variant = "0.1.2" sha2.workspace = true shadow-rs.workspace = true -sled = "0.34.7" -strum = "0.24.0" -strum_macros = "0.24.0" +strum.workspace = true time = { version = "0.3.23", features = ["std"] } tokio.workspace = true tonic = { workspace = true, optional = true } diff --git a/attestation-service/attestation-service/src/config.rs b/attestation-service/attestation-service/src/config.rs index 2a71f6522..814136ad6 100644 --- a/attestation-service/attestation-service/src/config.rs +++ b/attestation-service/attestation-service/src/config.rs @@ -1,4 +1,7 @@ -use crate::token::{AttestationTokenBrokerType, AttestationTokenConfig}; +use crate::{ + rvps::RvpsConfig, + token::{AttestationTokenBrokerType, AttestationTokenConfig}, +}; use anyhow::{anyhow, Result}; use serde::Deserialize; @@ -6,8 +9,6 @@ use std::convert::TryFrom; use std::fs::File; use std::path::{Path, PathBuf}; -use crate::rvps::store::StoreType; - /// Environment macro for Attestation Service work dir. const AS_WORK_DIR: &str = "AS_WORK_DIR"; const DEFAULT_WORK_DIR: &str = "/opt/confidential-containers/attestation-service"; @@ -20,7 +21,8 @@ pub struct Config { /// Policy Engine type. pub policy_engine: String, - pub rvps_store_type: StoreType, + /// Configurations for RVPS. + pub rvps_config: RvpsConfig, /// The Attestation Result Token Broker type. /// @@ -42,7 +44,7 @@ impl Default for Config { Config { work_dir, policy_engine: "opa".to_string(), - rvps_store_type: StoreType::LocalFs, + rvps_config: RvpsConfig::default(), attestation_token_broker: AttestationTokenBrokerType::Simple, attestation_token_config: AttestationTokenConfig::default(), } @@ -54,7 +56,10 @@ impl TryFrom<&Path> for Config { /// { /// "work_dir": "/var/lib/attestation-service/", /// "policy_engine": "opa", - /// "rvps_store_type": "LocalFs", + /// "rvps_config": { + /// "store_type": "LocalFs", + /// "remote_addr": "" + /// }, /// "attestation_token_broker": "Simple", /// "attestation_token_config": { /// "duration_min": 5 diff --git a/attestation-service/attestation-service/src/lib.rs b/attestation-service/attestation-service/src/lib.rs index d4a49fc86..97cc08463 100644 --- a/attestation-service/attestation-service/src/lib.rs +++ b/attestation-service/attestation-service/src/lib.rs @@ -4,19 +4,9 @@ //! - `rvps-grpc`: The AS will connect a remote RVPS. //! - `rvps-native`: The AS will integrate RVPS functionalities itself. -extern crate serde; - -#[macro_use] -extern crate log; - -extern crate strum; - -#[macro_use] -extern crate strum_macros; - pub mod config; pub mod policy_engine; -pub mod rvps; +mod rvps; mod token; mod utils; @@ -25,69 +15,42 @@ use crate::token::AttestationTokenBroker; use anyhow::{anyhow, Context, Result}; use config::Config; pub use kbs_types::{Attestation, Tee}; -use policy_engine::{PolicyEngine, SetPolicyInput}; -use rvps::{Message, RVPSAPI}; +use log::debug; +use policy_engine::{PolicyEngine, PolicyEngineType, SetPolicyInput}; +use rvps::RvpsApi; use serde_json::json; use sha2::{Digest, Sha384}; -use verifier::{ReportData, InitDataHash}; -use std::collections::HashMap; - -#[cfg(any(feature = "rvps-grpc", feature = "rvps-native"))] -use std::{fs, str::FromStr}; - -#[cfg(any(feature = "rvps-grpc", feature = "rvps-native"))] -use policy_engine::PolicyEngineType; +use std::{collections::HashMap, str::FromStr}; +use tokio::fs; +use verifier::{InitDataHash, ReportData}; use crate::utils::flatten_claims; pub struct AttestationService { _config: Config, policy_engine: Box, - rvps: Box, + rvps: Box, token_broker: Box, } impl AttestationService { /// Create a new Attestation Service instance. - #[cfg(feature = "rvps-native")] - pub fn new(config: Config) -> Result { - if !config.work_dir.as_path().exists() { - fs::create_dir_all(&config.work_dir) - .map_err(|e| anyhow!("Create AS work dir failed: {:?}", e))?; - } - - 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 rvps_store = config.rvps_store_type.to_store()?; - let rvps = Box::new(rvps::Core::new(rvps_store)); - - let token_broker = config - .attestation_token_broker - .to_token_broker(config.attestation_token_config.clone())?; - - Ok(Self { - _config: config, - policy_engine, - rvps, - token_broker, - }) - } - - /// Create a new Attestation Service, and connect to a remote rvps. - #[cfg(feature = "rvps-grpc")] - pub async fn new_with_rvps_grpc(rvps_addr: &str, config: Config) -> Result { + pub async fn new(config: Config) -> Result { if !config.work_dir.as_path().exists() { fs::create_dir_all(&config.work_dir) - .map_err(|e| anyhow!("Create AS work dir failed: {:?}", e))?; + .await + .context("Create AS work dir failed: {:?}")?; } 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 rvps = Box::new(rvps::Agent::new(rvps_addr).await?); + let rvps = config + .rvps_config + .to_rvps() + .await + .context("create rvps failed.")?; let token_broker = config .attestation_token_broker @@ -151,16 +114,18 @@ impl AttestationService { .map_err(|e| anyhow!("Verifier evaluate failed: {e:?}"))?; let flattened_claims = flatten_claims(tee.clone(), &claims_from_tee_evidence)?; - let tcb = serde_json::to_string(&flattened_claims)?; + + let tcb_json = serde_json::to_string(&flattened_claims)?; + let reference_data_map = self - .get_reference_data(&tcb) + .get_reference_data(flattened_claims.keys()) .await .map_err(|e| anyhow!("Generate reference data failed{:?}", e))?; // Now only support using default policy to evaluate let evaluation_report = self .policy_engine - .evaluate(reference_data_map, tcb.clone(), None) + .evaluate(reference_data_map, tcb_json, None) .await .map_err(|e| anyhow!("Policy Engine evaluation failed: {e}"))?; @@ -174,25 +139,23 @@ impl AttestationService { Ok(attestation_results_token) } - async fn get_reference_data(&self, tcb_claims: &str) -> Result>> { + async fn get_reference_data<'a, I>(&self, tcb_claims: I) -> Result>> + where + I: Iterator, + { let mut data = HashMap::new(); - let tcb_claims_map: HashMap = serde_json::from_str(tcb_claims)?; - for key in tcb_claims_map.keys() { - data.insert( - key.to_string(), - self.rvps - .get_digests(key) - .await? - .unwrap_or_default() - .hash_values - .clone(), - ); + for key in tcb_claims { + let reference_value = self.rvps.get_digests(key).await?; + if !reference_value.is_empty() { + debug!("Successfully get reference values of {key} from RVPS."); + } + data.insert(key.to_string(), reference_value); } Ok(data) } /// Registry a new reference value - pub async fn registry_reference_value(&mut self, message: Message) -> Result<()> { + pub async fn registry_reference_value(&mut self, message: &str) -> Result<()> { self.rvps.verify_and_extract(message).await } } diff --git a/attestation-service/attestation-service/src/policy_engine/mod.rs b/attestation-service/attestation-service/src/policy_engine/mod.rs index 5ddaf0829..ea36c0d4d 100644 --- a/attestation-service/attestation-service/src/policy_engine/mod.rs +++ b/attestation-service/attestation-service/src/policy_engine/mod.rs @@ -3,6 +3,7 @@ use async_trait::async_trait; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::path::Path; +use strum::EnumString; pub mod opa; diff --git a/attestation-service/attestation-service/src/policy_engine/opa/mod.rs b/attestation-service/attestation-service/src/policy_engine/opa/mod.rs index 014982b70..49b50c9e0 100644 --- a/attestation-service/attestation-service/src/policy_engine/opa/mod.rs +++ b/attestation-service/attestation-service/src/policy_engine/opa/mod.rs @@ -2,6 +2,7 @@ use crate::policy_engine::{PolicyEngine, PolicyType}; use anyhow::{anyhow, bail, Result}; use async_trait::async_trait; use base64::Engine; +use log::debug; use serde_json::Value; use std::collections::HashMap; use std::ffi::CStr; diff --git a/attestation-service/attestation-service/src/rvps/builtin.rs b/attestation-service/attestation-service/src/rvps/builtin.rs new file mode 100644 index 000000000..49fca776e --- /dev/null +++ b/attestation-service/attestation-service/src/rvps/builtin.rs @@ -0,0 +1,33 @@ +use anyhow::*; +use async_trait::async_trait; +use reference_value_provider_service::Core; + +use super::RvpsApi; + +pub struct Rvps { + core: Core, +} + +impl Rvps { + pub fn new(store_type: &str) -> Result { + let core = Core::new(store_type)?; + Ok(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 + } + + async fn get_digests(&self, name: &str) -> Result> { + let hashes = self + .core + .get_digests(name) + .await? + .unwrap_or_default() + .hash_values; + Ok(hashes) + } +} diff --git a/attestation-service/attestation-service/src/rvps/grpc.rs b/attestation-service/attestation-service/src/rvps/grpc.rs index 2e1e78134..e74988482 100644 --- a/attestation-service/attestation-service/src/rvps/grpc.rs +++ b/attestation-service/attestation-service/src/rvps/grpc.rs @@ -1,8 +1,3 @@ -// Copyright (c) 2022 Alibaba Cloud -// -// SPDX-License-Identifier: Apache-2.0 -// - use anyhow::*; use tokio::sync::Mutex; @@ -11,13 +6,12 @@ use self::rvps_api::{ ReferenceValueQueryRequest, ReferenceValueRegisterRequest, }; -use super::{Message, TrustedDigest, RVPSAPI}; +use super::RvpsApi; pub mod rvps_api { tonic::include_proto!("reference"); } -/// An agent for rvps, uses grpc to connect pub struct Agent { client: Mutex>, } @@ -33,10 +27,11 @@ impl Agent { } #[async_trait::async_trait] -impl RVPSAPI for Agent { - async fn verify_and_extract(&mut self, message: Message) -> Result<()> { - let message = serde_json::to_string(&message)?; - let req = tonic::Request::new(ReferenceValueRegisterRequest { message }); +impl RvpsApi for Agent { + async fn verify_and_extract(&mut self, message: &str) -> Result<()> { + let req = tonic::Request::new(ReferenceValueRegisterRequest { + message: message.to_string(), + }); let _ = self .client .lock() @@ -47,7 +42,7 @@ impl RVPSAPI for Agent { Ok(()) } - async fn get_digests(&self, name: &str) -> Result> { + async fn get_digests(&self, name: &str) -> Result> { let req = tonic::Request::new(ReferenceValueQueryRequest { name: name.to_string(), }); diff --git a/attestation-service/attestation-service/src/rvps/mod.rs b/attestation-service/attestation-service/src/rvps/mod.rs index 445a31982..f826ab12e 100644 --- a/attestation-service/attestation-service/src/rvps/mod.rs +++ b/attestation-service/attestation-service/src/rvps/mod.rs @@ -1,62 +1,85 @@ -// Copyright (c) 2022 Alibaba Cloud +// Copyright (c) 2023 Alibaba Cloud // // SPDX-License-Identifier: Apache-2.0 // -extern crate strum; +use anyhow::*; +use log::{info, warn}; +use serde::Deserialize; + +/// The interfaces of Reference Value Provider Service +/// * `verify_and_extract` is responsible for verify a message and +/// store reference values from it. +/// * `get_digests` gets trusted digests by the artifact's name. +#[async_trait::async_trait] +pub trait RvpsApi { + /// Verify the given message and register the reference value included. + async fn verify_and_extract(&mut self, message: &str) -> Result<()>; -#[allow(clippy::new_without_default)] -pub mod extractors; -pub mod pre_processor; -pub mod reference_value; -pub mod store; + /// Get the reference values / golden values / expected digests in hex of the + /// given component name. + async fn get_digests(&self, name: &str) -> Result>; +} #[cfg(feature = "rvps-grpc")] pub mod grpc; -#[cfg(feature = "rvps-grpc")] -pub use grpc::Agent; -#[cfg(feature = "rvps-native")] -pub mod native; -#[cfg(feature = "rvps-native")] -pub use native::Core; +#[cfg(feature = "rvps-builtin")] +pub mod builtin; -use anyhow::*; -use serde::{Deserialize, Serialize}; - -pub use reference_value::{ReferenceValue, TrustedDigest}; -pub use store::Store; - -/// Default version of Message -static MESSAGE_VERSION: &str = "0.1.0"; - -/// Message is an overall packet that Reference Value Provider Service -/// receives. It will contain payload (content of different provenance, -/// JSON format), provenance type (indicates the type of the payload) -/// and a version number (use to distinguish different version of -/// message, for extendability). -/// * `version`: version of this message. -/// * `payload`: content of the provenance, JSON encoded. -/// * `type`: provenance type of the payload. -#[derive(Serialize, Deserialize, Debug)] -pub struct Message { - #[serde(default = "default_version")] - version: String, - payload: String, - r#type: String, +#[derive(Clone, Debug, Deserialize)] +pub struct RvpsConfig { + /// Specify the underlying storage type of RVPS, e.g. + /// + /// - `localfs`: store inside local filesystem. + /// + /// Only used when feature `rvps-builtin` is enabled + #[serde(default = "String::default")] + pub store_type: String, + + /// The address of the remote RVPS server, e.g. + /// + /// - `http://127.0.0.1:50002` + /// + /// Only used when feature `rvps-rpc` is enabled + #[serde(default = "String::default")] + pub remote_addr: String, } -/// Set the default version for Message -fn default_version() -> String { - MESSAGE_VERSION.into() +impl Default for RvpsConfig { + fn default() -> Self { + Self { + store_type: "LocalFs".into(), + remote_addr: Default::default(), + } + } } -/// The interfaces of Reference Value Provider Service -/// * `verify_and_extract` is responsible for verify a message and -/// store reference values from it. -/// * `get_digests` gets trusted digests by the artifact's name. -#[async_trait::async_trait] -pub trait RVPSAPI { - async fn verify_and_extract(&mut self, message: Message) -> Result<()>; - async fn get_digests(&self, name: &str) -> Result>; +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> { + cfg_if::cfg_if! { + if #[cfg(feature = "rvps-grpc")] { + if !self.remote_addr.is_empty() { + info!("connect to remote RVPS: {}", self.remote_addr); + Ok(Box::new(grpc::Agent::new(&self.remote_addr).await?) as Box) + } else { + cfg_if::cfg_if! { + if #[cfg(feature = "rvps-builtin")] { + warn!("No RVPS address provided and will launch a built-in rvps"); + Ok(Box::new(builtin::Rvps::new(&self.store_type)?) as Box) + } else { + Err(anyhow!("either feature `rvps-grpc` or `rvps-builtin` should be enabled.")) + } + } + } + } else if #[cfg(feature = "rvps-builtin")] { + info!("launch a built-in RVPS."); + Ok(Box::new(builtin::Rvps::new(&self.store_type)) as Box) + } else { + Err(anyhow!("either feature `rvps-grpc` or `rvps-builtin` should be enabled.")) + } + } + } } diff --git a/attestation-service/attestation-service/src/token/mod.rs b/attestation-service/attestation-service/src/token/mod.rs index bea6429e4..ccc44db1a 100644 --- a/attestation-service/attestation-service/src/token/mod.rs +++ b/attestation-service/attestation-service/src/token/mod.rs @@ -6,7 +6,7 @@ use anyhow::*; use serde::Deserialize; use serde_json::Value; -use strum_macros::EnumString; +use strum::EnumString; mod simple; diff --git a/attestation-service/attestation-service/src/utils.rs b/attestation-service/attestation-service/src/utils.rs index 39eed788b..931642523 100644 --- a/attestation-service/attestation-service/src/utils.rs +++ b/attestation-service/attestation-service/src/utils.rs @@ -27,7 +27,7 @@ use verifier::TeeEvidenceParsedClaim; pub fn flatten_claims( tee: kbs_types::Tee, claims: &TeeEvidenceParsedClaim, -) -> Result { +) -> Result> { let mut map = Map::new(); let tee_type = to_variant_name(&tee)?; match claims { @@ -39,7 +39,7 @@ pub fn flatten_claims( _ => bail!("input claims must be a map"), } - Ok(serde_json::Value::Object(map)) + Ok(map) } /// Recursion algorithm helper of `flatten_claims` diff --git a/attestation-service/bin/grpc-as/Cargo.toml b/attestation-service/bin/grpc-as/Cargo.toml index 01978b179..5c4c13113 100644 --- a/attestation-service/bin/grpc-as/Cargo.toml +++ b/attestation-service/bin/grpc-as/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" anyhow.workspace = true async-trait.workspace = true attestation-service = { path = "../../attestation-service", features = ["rvps-grpc"] } -clap.workspace = true +clap = "3" env_logger.workspace = true log.workspace = true prost.workspace = true diff --git a/attestation-service/bin/grpc-as/src/main.rs b/attestation-service/bin/grpc-as/src/main.rs index 0b3602f67..1dc524006 100644 --- a/attestation-service/bin/grpc-as/src/main.rs +++ b/attestation-service/bin/grpc-as/src/main.rs @@ -36,14 +36,6 @@ async fn main() -> Result<()> { .help("Socket that the server will listen on to accept requests.") .takes_value(true), ) - .arg( - Arg::with_name("rvps-addr") - .long("rvps-address") - .value_name("rvps-addr") - .help("Address of Reference Value Provider Service") - .required(false) - .takes_value(true), - ) .arg( Arg::with_name("config") .long("config") @@ -54,9 +46,8 @@ async fn main() -> Result<()> { ) .get_matches(); - let rvps_addr = matches.value_of("rvps-addr"); let config_path = matches.value_of("config"); - let server = server::start(matches.value_of("socket"), rvps_addr, config_path); + let server = server::start(matches.value_of("socket"), config_path); tokio::try_join!(server)?; Ok(()) diff --git a/attestation-service/bin/grpc-as/src/server.rs b/attestation-service/bin/grpc-as/src/server.rs index 374673f6e..e8dbce586 100644 --- a/attestation-service/bin/grpc-as/src/server.rs +++ b/attestation-service/bin/grpc-as/src/server.rs @@ -41,23 +41,14 @@ pub struct AttestationServer { } impl AttestationServer { - pub async fn new(rvps_addr: Option<&str>, config_path: Option<&str>) -> Result { + pub async fn new(config_path: Option<&str>) -> Result { let config = match config_path { Some(path) => Config::try_from(Path::new(path)) .map_err(|e| anyhow!("Read AS config file failed: {:?}", e))?, None => Config::default(), }; - let service = match rvps_addr { - Some(addr) => { - info!("Connect to remote RVPS [{addr}] (gRPC Mode)"); - Service::new_with_rvps_grpc(addr, config).await? - } - None => { - info!("Start a local RVPS (Server mode)"); - Service::new(config)? - } - }; + let service = Service::new(config).await?; Ok(Self { attestation_service: service, @@ -152,17 +143,11 @@ impl ReferenceValueProviderService for Arc> { } } -pub async fn start( - socket: Option<&str>, - rvps_addr: Option<&str>, - config_path: Option<&str>, -) -> Result<()> { +pub async fn start(socket: Option<&str>, config_path: Option<&str>) -> Result<()> { let socket = socket.unwrap_or(DEFAULT_SOCK).parse()?; info!("Listen socket: {}", &socket); - let attestation_server = Arc::new(RwLock::new( - AttestationServer::new(rvps_addr, config_path).await?, - )); + let attestation_server = Arc::new(RwLock::new(AttestationServer::new(config_path).await?)); Server::builder() .add_service(AttestationServiceServer::new(attestation_server.clone())) diff --git a/attestation-service/bin/rvps-client/Cargo.toml b/attestation-service/bin/rvps-client/Cargo.toml deleted file mode 100644 index 76e972422..000000000 --- a/attestation-service/bin/rvps-client/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "rvps-client" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -anyhow.workspace = true -clap.workspace = true -env_logger.workspace = true -log.workspace = true -prost.workspace = true -shadow-rs.workspace = true -tokio.workspace = true -tonic.workspace = true - -[build-dependencies] -shadow-rs.workspace = true -tonic-build.workspace = true \ No newline at end of file diff --git a/attestation-service/bin/rvps-client/src/main.rs b/attestation-service/bin/rvps-client/src/main.rs deleted file mode 100644 index d7bcd2895..000000000 --- a/attestation-service/bin/rvps-client/src/main.rs +++ /dev/null @@ -1,121 +0,0 @@ -//! This tool is to connect the RVPS - -use anyhow::*; -use clap::{App, Arg, Command}; -use log::info; -use shadow_rs::shadow; - -pub mod rvps_api { - tonic::include_proto!("reference"); -} - -use crate::rvps_api::{ - reference_value_provider_service_client::ReferenceValueProviderServiceClient, - ReferenceValueQueryRequest, ReferenceValueRegisterRequest, -}; - -shadow!(build); - -/// Default address of RVPS -const DEFAULT_ADDR: &str = "http://127.0.0.1:50003"; - -async fn register(addr: &str, provenance_path: &str) -> Result<()> { - let message = std::fs::read_to_string(provenance_path).context("read provenance")?; - let mut client = ReferenceValueProviderServiceClient::connect(addr.to_string()).await?; - let req = tonic::Request::new(ReferenceValueRegisterRequest { message }); - - client.register_reference_value(req).await?; - - info!("Register provenance succeeded."); - - Ok(()) -} - -async fn query(addr: &str, name: &str) -> Result<()> { - let mut client = ReferenceValueProviderServiceClient::connect(addr.to_string()).await?; - let req = tonic::Request::new(ReferenceValueQueryRequest { - name: name.to_string(), - }); - - let rvs = client - .query_reference_value(req) - .await? - .into_inner() - .reference_value_results; - info!("Get reference values succeeded:\n {rvs}"); - Ok(()) -} - -#[tokio::main] -async fn main() -> Result<()> { - env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); - - let version = format!( - "\nv{}\ncommit: {}\nbuildtime: {}", - build::PKG_VERSION, - build::COMMIT_HASH, - build::BUILD_TIME - ); - - let matches = App::new("RVPS-client") - .version(version.as_str()) - .long_version(version.as_str()) - .author("Confidential-Containers Team") - .subcommand( - Command::new("register") - .about("Register a reference value into the RVPS") - .arg( - Arg::with_name("addr") - .long("addr") - .value_name("addr") - .help("The address of target RVPS") - .takes_value(true) - .default_value(DEFAULT_ADDR) - .required(false), - ) - .arg( - Arg::with_name("path") - .long("path") - .value_name("path") - .help("The path to the provenance json file") - .takes_value(true) - .required(true), - ), - ) - .subcommand( - Command::new("query") - .about("Query a reference value from the RVPS") - .arg( - Arg::with_name("addr") - .long("addr") - .value_name("addr") - .help("The address of target RVPS") - .takes_value(true) - .default_value(DEFAULT_ADDR) - .required(false), - ) - .arg( - Arg::with_name("name") - .long("name") - .value_name("name") - .help("The name to query reference value") - .takes_value(true) - .required(true), - ), - ) - .get_matches(); - - match matches.subcommand() { - Some(("register", sub_cmd)) => { - let addr = sub_cmd.value_of("addr").expect("no rvps addr input"); - let path = sub_cmd.value_of("path").expect("no rv provenance input"); - register(addr, path).await - } - Some(("query", sub_cmd)) => { - let addr = sub_cmd.value_of("addr").expect("no rvps addr input"); - let name = sub_cmd.value_of("name").expect("no artifact name input"); - query(addr, name).await - } - _ => bail!("error occurs for subcommand"), - } -} diff --git a/attestation-service/bin/rvps/Cargo.toml b/attestation-service/bin/rvps/Cargo.toml deleted file mode 100644 index e9d3a0f5e..000000000 --- a/attestation-service/bin/rvps/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "rvps" -version = "0.1.0" -edition = "2021" - -[dependencies] -anyhow.workspace = true -attestation-service = { path = "../../attestation-service", default-features = false, features = [ "rvps-native", "rvps-grpc"] } -clap.workspace = true -env_logger.workspace = true -log.workspace = true -prost.workspace = true -serde_json.workspace = true -shadow-rs.workspace = true -tokio.workspace = true -tonic.workspace = true - -[build-dependencies] -shadow-rs.workspace = true -tonic-build.workspace = true diff --git a/attestation-service/bin/rvps/build.rs b/attestation-service/bin/rvps/build.rs deleted file mode 100644 index 60d856e6a..000000000 --- a/attestation-service/bin/rvps/build.rs +++ /dev/null @@ -1,23 +0,0 @@ -use std::process::exit; - -fn real_main() -> Result<(), String> { - let out_dir = std::env::var("OUT_DIR").unwrap(); - println!("cargo:rerun-if-changed={out_dir}"); - println!("cargo:rustc-link-search=native={out_dir}"); - println!("cargo:rustc-link-lib=static=cgo"); - - tonic_build::compile_protos("../../protos/attestation.proto").map_err(|e| format!("{e}"))?; - - tonic_build::compile_protos("../../protos/reference.proto").map_err(|e| format!("{e}"))?; - - Ok(()) -} - -fn main() -> shadow_rs::SdResult<()> { - if let Err(e) = real_main() { - eprintln!("ERROR: {e}"); - exit(1); - } - - shadow_rs::new() -} diff --git a/attestation-service/bin/rvps/src/main.rs b/attestation-service/bin/rvps/src/main.rs deleted file mode 100644 index fce81875b..000000000 --- a/attestation-service/bin/rvps/src/main.rs +++ /dev/null @@ -1,66 +0,0 @@ -use anyhow::{Context, Result}; -use attestation_service::rvps::store::StoreType; -use clap::{App, Arg}; -use log::info; -use shadow_rs::shadow; - -pub mod rvps_api { - tonic::include_proto!("reference"); -} - -shadow!(build); - -mod server; - -const DEFAULT_ADDR: &str = "127.0.0.1:50003"; -const DEFAULT_STORAGE: &str = "LocalFs"; - -#[tokio::main] -async fn main() -> Result<()> { - env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); - - let version = format!( - "\nv{}\ncommit: {}\nbuildtime: {}", - build::PKG_VERSION, - build::COMMIT_HASH, - build::BUILD_TIME - ); - - let matches = App::new("reference-value-provider-service") - .version(version.as_str()) - .long_version(version.as_str()) - .author("Confidential-Containers Team") - .arg( - Arg::with_name("socket") - .long("socket") - .value_name("SOCKET") - .help("Socket that the server will listen on to accept requests.") - .takes_value(true) - .default_value(DEFAULT_ADDR) - .required(false), - ) - .arg( - Arg::with_name("storage") - .long("storage type") - .value_name("STORAGE") - .help("Underlying storage type used by RVPS.") - .takes_value(true) - .default_value(DEFAULT_STORAGE) - .required(false), - ) - .get_matches(); - - let socket = matches.value_of("socket").expect("socket addr get failed."); - let storage = matches - .value_of("storage") - .expect("storage type get failed."); - - info!("Listen socket: {}", socket); - - let socket = socket.parse().context("parse socket addr failed")?; - let storage = StoreType::try_from(storage) - .context("storage type")? - .to_store() - .context("create storage failed")?; - server::start(socket, storage).await -} diff --git a/attestation-service/rvps/Cargo.toml b/attestation-service/rvps/Cargo.toml new file mode 100644 index 000000000..9c3b7f995 --- /dev/null +++ b/attestation-service/rvps/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "reference-value-provider-service" +version = "0.1.0" +edition = "2021" + +[features] +default = [ "bin" ] +# Used to build rvps binary +bin = [ "clap", "env_logger", "prost", "shadow-rs", "tokio", "tonic" ] + +[[bin]] +name = "rvps" +required-features = [ "bin" ] + +[[bin]] +name = "rvps-tool" +required-features = [ "bin" ] + +[dependencies] +anyhow.workspace = true +async-trait.workspace = true +base64.workspace = true +cfg-if.workspace = true +chrono = { workspace = true, features = [ "serde" ] } +clap = { workspace = true, optional = true } +env_logger = { workspace = true, optional = true } +log.workspace = true +prost = { workspace = true, optional = true } +serde.workspace = true +serde_json.workspace = true +shadow-rs = { workspace = true, optional = true } +sled = "0.34.7" +strum.workspace = true +tokio = { workspace = true, optional = true } +tonic = { workspace = true, optional = true } + +[build-dependencies] +shadow-rs.workspace = true +tonic-build.workspace = true + +[dev-dependencies] +assert-json-diff.workspace = true +rstest.workspace = true +serial_test.workspace = true +tempfile = "3.3.0" +tokio.workspace = true diff --git a/attestation-service/Dockerfile.rvps b/attestation-service/rvps/Dockerfile similarity index 70% rename from attestation-service/Dockerfile.rvps rename to attestation-service/rvps/Dockerfile index 7d1cf06b3..3a73215f6 100644 --- a/attestation-service/Dockerfile.rvps +++ b/attestation-service/rvps/Dockerfile @@ -6,18 +6,13 @@ FROM rust:latest as builder WORKDIR /usr/src/rvps -RUN wget https://go.dev/dl/go1.20.2.linux-amd64.tar.gz && \ - tar -C /usr/local -xzf go1.20.2.linux-amd64.tar.gz - -ENV PATH="/usr/local/go/bin:${PATH}" - COPY . . RUN apt-get update && apt-get install protobuf-compiler -y -RUN cargo install --path attestation-service/bin/rvps +RUN cargo install --bin rvps --path attestation-service/rvps -FROM debian:bullseye-slim +FROM debian LABEL org.opencontainers.image.source="https://github.com/confidential-containers/attestation-service" diff --git a/attestation-service/bin/rvps-client/build.rs b/attestation-service/rvps/build.rs similarity index 80% rename from attestation-service/bin/rvps-client/build.rs rename to attestation-service/rvps/build.rs index 31c91ccf4..587b89e23 100644 --- a/attestation-service/bin/rvps-client/build.rs +++ b/attestation-service/rvps/build.rs @@ -5,7 +5,7 @@ fn real_main() -> Result<(), String> { println!("cargo:rerun-if-changed={out_dir}"); println!("cargo:rustc-link-search=native={out_dir}"); - tonic_build::compile_protos("../../protos/reference.proto").map_err(|e| format!("{e}"))?; + tonic_build::compile_protos("../protos/reference.proto").map_err(|e| format!("{e}"))?; Ok(()) } diff --git a/attestation-service/rvps/cgo/go.mod b/attestation-service/rvps/cgo/go.mod new file mode 100644 index 000000000..fc30a4c45 --- /dev/null +++ b/attestation-service/rvps/cgo/go.mod @@ -0,0 +1,15 @@ +module cgo + +go 1.20 + +require ( + github.com/in-toto/in-toto-golang v0.9.0 + github.com/open-policy-agent/opa v0.56.0 +) + +require ( + github.com/secure-systems-lab/go-securesystemslib v0.7.0 // indirect + github.com/shibumi/go-pathspec v1.3.0 // indirect + golang.org/x/crypto v0.14.0 // indirect + golang.org/x/sys v0.14.0 // indirect +) diff --git a/attestation-service/rvps/cgo/go.sum b/attestation-service/rvps/cgo/go.sum new file mode 100644 index 000000000..1495a0a6a --- /dev/null +++ b/attestation-service/rvps/cgo/go.sum @@ -0,0 +1,11 @@ +github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU= +github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo= +github.com/open-policy-agent/opa v0.56.0/go.mod h1:un01L10fkolr00KJMDSqGb2FXCjVyVQOybLtHOfSEfY= +github.com/secure-systems-lab/go-securesystemslib v0.7.0 h1:OwvJ5jQf9LnIAS83waAjPbcMsODrTQUpJ02eNLUoxBg= +github.com/secure-systems-lab/go-securesystemslib v0.7.0/go.mod h1:/2gYnlnHVQ6xeGtfIqFy7Do03K4cdCY0A/GlJLDKLHI= +github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= +github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= +golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= diff --git a/attestation-service/rvps/cgo/intoto.go b/attestation-service/rvps/cgo/intoto.go new file mode 100644 index 000000000..f47d49906 --- /dev/null +++ b/attestation-service/rvps/cgo/intoto.go @@ -0,0 +1,85 @@ +package main + +import "C" + +import ( + "encoding/json" + "fmt" + "io" + "os" + + intoto "github.com/in-toto/in-toto-golang/in_toto" +) + +//export verifyGo +func verifyGo( + layoutPath string, + pubKeyPaths []string, + intermediatePaths []string, + linkDir string, + lineNormalizationInt int) *C.char { + layoutMb, err := intoto.LoadMetadata(layoutPath) + if err != nil { + e := fmt.Errorf("failed to load layout at %s: %w", layoutPath, err) + return C.CString("Error:: " + e.Error()) + } + + pubKeyCount := len(pubKeyPaths) + layoutKeys := make(map[string]intoto.Key, pubKeyCount) + + for _, pubKeyPath := range pubKeyPaths { + var pubKey intoto.Key + if err := pubKey.LoadKeyDefaults(pubKeyPath); err != nil { + e := fmt.Errorf("invalid key at %s: %w", pubKeyPath, err) + return C.CString("Error:: " + e.Error()) + } + + layoutKeys[pubKey.KeyID] = pubKey + } + + intermediatePathCount := len(intermediatePaths) + intermediatePems := make([][]byte, 0, int(intermediatePathCount)) + + for _, intermediate := range intermediatePaths { + f, err := os.Open(intermediate) + if err != nil { + e := fmt.Errorf("failed to open intermediate %s: %w", intermediate, err) + return C.CString("Error:: " + e.Error()) + } + defer f.Close() + + pemBytes, err := io.ReadAll(f) + if err != nil { + e := fmt.Errorf("failed to read intermediate %s: %w", intermediate, err) + return C.CString("Error:: " + e.Error()) + } + + intermediatePems = append(intermediatePems, pemBytes) + + if err := f.Close(); err != nil { + e := fmt.Errorf("could not close intermediate cert: %w", err) + return C.CString("Error:: " + e.Error()) + } + } + + var lineNormalization bool + if lineNormalizationInt == 0 { + lineNormalization = false + } else { + lineNormalization = true + } + + summaryLink, err := intoto.InTotoVerify(layoutMb, layoutKeys, linkDir, "", make(map[string]string), intermediatePems, lineNormalization) + if err != nil { + e := fmt.Errorf("inspection failed: %w", err) + return C.CString("Error:: " + e.Error()) + } + + jsonBytes, err := json.Marshal(summaryLink) + if err != nil { + e := fmt.Errorf("json failed: %w", err) + return C.CString("Error:: " + e.Error()) + } + + return C.CString(string(jsonBytes)) +} diff --git a/attestation-service/rvps/src/bin/rvps-tool.rs b/attestation-service/rvps/src/bin/rvps-tool.rs new file mode 100644 index 000000000..8065eee92 --- /dev/null +++ b/attestation-service/rvps/src/bin/rvps-tool.rs @@ -0,0 +1,105 @@ +//! This tool is to connect the RVPS + +use anyhow::*; +use clap::{Args, Parser}; +use log::info; +use shadow_rs::shadow; + +pub mod rvps_api { + tonic::include_proto!("reference"); +} + +use crate::rvps_api::{ + reference_value_provider_service_client::ReferenceValueProviderServiceClient, + ReferenceValueQueryRequest, ReferenceValueRegisterRequest, +}; + +shadow!(build); + +/// Default address of RVPS +const DEFAULT_ADDR: &str = "http://127.0.0.1:50003"; + +async fn register(addr: &str, provenance_path: &str) -> Result<()> { + let message = std::fs::read_to_string(provenance_path).context("read provenance")?; + let mut client = ReferenceValueProviderServiceClient::connect(addr.to_string()).await?; + let req = tonic::Request::new(ReferenceValueRegisterRequest { message }); + + client.register_reference_value(req).await?; + + info!("Register provenance succeeded."); + + Ok(()) +} + +async fn query(addr: &str, name: &str) -> Result<()> { + let mut client = ReferenceValueProviderServiceClient::connect(addr.to_string()).await?; + let req = tonic::Request::new(ReferenceValueQueryRequest { + name: name.to_string(), + }); + + let rvs = client + .query_reference_value(req) + .await? + .into_inner() + .reference_value_results; + info!("Get reference values succeeded:\n {rvs}"); + Ok(()) +} + +/// RVPS command-line arguments. +#[derive(Parser)] +#[command(name = "rvps-tool")] +#[command(bin_name = "rvps-tool")] +#[command(author, version, about, long_about = None)] +enum Cli { + /// Register reference values + Register(RegisterArgs), + + /// Query reference values + Query(QueryArgs), +} + +#[derive(Args)] +#[command(author, version, about, long_about = None)] +struct RegisterArgs { + /// The address of target RVPS + #[arg(short, long, default_value = DEFAULT_ADDR)] + addr: String, + + /// The path to the provenance json file + #[arg(short, long)] + path: String, +} + +#[derive(Args)] +#[command(author, version, about, long_about = None)] +struct QueryArgs { + /// The address of target RVPS + #[arg(short, long, default_value = DEFAULT_ADDR)] + addr: String, + + /// The name to query reference value + #[arg(short, long)] + name: String, +} + +#[tokio::main] +async fn main() -> Result<()> { + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); + + let version = format!( + "\nv{}\ncommit: {}\nbuildtime: {}", + build::PKG_VERSION, + build::COMMIT_HASH, + build::BUILD_TIME + ); + + info!("CoCo RVPS Client tool: {version}"); + + let cli = Cli::parse(); + + match cli { + Cli::Register(para) => register(¶.addr, ¶.path).await, + Cli::Query(para) => query(¶.addr, ¶.name).await, + } +} diff --git a/attestation-service/rvps/src/bin/rvps.rs b/attestation-service/rvps/src/bin/rvps.rs new file mode 100644 index 000000000..e83649117 --- /dev/null +++ b/attestation-service/rvps/src/bin/rvps.rs @@ -0,0 +1,49 @@ +use anyhow::{Context, Result}; +use clap::Parser; +use log::info; +use shadow_rs::shadow; + +pub mod rvps_api { + tonic::include_proto!("reference"); +} + +shadow!(build); + +mod server; + +const DEFAULT_ADDR: &str = "127.0.0.1:50003"; +const DEFAULT_STORAGE: &str = "LocalFs"; + +/// RVPS command-line arguments. +#[derive(Debug, Parser)] +#[command(author, version, about, long_about = None)] +pub struct Cli { + /// Underlying storage engine that RVPS uses. + #[arg(short, long, default_value = DEFAULT_STORAGE)] + pub storage: String, + + /// Socket addresses (IP:port) to listen on, e.g. 127.0.0.1:50003. + #[arg(short, long, default_value = DEFAULT_ADDR)] + pub socket: String, +} + +#[tokio::main] +async fn main() -> Result<()> { + env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); + + let version = format!( + "\nv{}\ncommit: {}\nbuildtime: {}", + build::PKG_VERSION, + build::COMMIT_HASH, + build::BUILD_TIME + ); + + info!("CoCo RVPS: {version}"); + + let cli = Cli::parse(); + + info!("Listen socket: {}", cli.socket); + + let socket = cli.socket.parse().context("parse socket addr failed")?; + server::start(socket, &cli.storage).await +} diff --git a/attestation-service/bin/rvps/src/server.rs b/attestation-service/rvps/src/bin/server/mod.rs similarity index 85% rename from attestation-service/bin/rvps/src/server.rs rename to attestation-service/rvps/src/bin/server/mod.rs index 99ec227b9..22ce97eb3 100644 --- a/attestation-service/bin/rvps/src/server.rs +++ b/attestation-service/rvps/src/bin/server/mod.rs @@ -1,6 +1,6 @@ use anyhow::{Context, Result}; -use attestation_service::rvps::{Core, Store, RVPSAPI}; use log::{debug, info}; +use reference_value_provider_service::Core; use std::net::SocketAddr; use std::sync::Arc; use tokio::sync::Mutex; @@ -41,7 +41,9 @@ impl ReferenceValueProviderService for RVPSServer { .await .get_digests(&request.name) .await - .map_err(|e| Status::aborted(format!("Query reference value: {e}")))?; + .map_err(|e| Status::aborted(format!("Query reference value: {e}")))? + .map(|rvs| rvs.hash_values) + .unwrap_or_default(); let reference_value_results = serde_json::to_string(&rvs) .map_err(|e| Status::aborted(format!("Serde reference value: {e}")))?; info!("Reference values: {}", reference_value_results); @@ -60,12 +62,10 @@ impl ReferenceValueProviderService for RVPSServer { debug!("registry reference value: {}", request.message); - let message = serde_json::from_str(&request.message) - .map_err(|e| Status::aborted(format!("Parse message: {e}")))?; self.rvps .lock() .await - .verify_and_extract(message) + .verify_and_extract(&request.message) .await .map_err(|e| Status::aborted(format!("Register reference value: {e}")))?; @@ -74,8 +74,8 @@ impl ReferenceValueProviderService for RVPSServer { } } -pub async fn start(socket: SocketAddr, storage: Box) -> Result<()> { - let service = Core::new(storage); +pub async fn start(socket: SocketAddr, storage: &str) -> Result<()> { + let service = Core::new(storage)?; let inner = Arc::new(Mutex::new(service)); let rvps_server = RVPSServer::new(inner.clone()); diff --git a/attestation-service/attestation-service/src/rvps/extractors/extractor_modules/in_toto/README.md b/attestation-service/rvps/src/extractors/extractor_modules/in_toto/README.md similarity index 100% rename from attestation-service/attestation-service/src/rvps/extractors/extractor_modules/in_toto/README.md rename to attestation-service/rvps/src/extractors/extractor_modules/in_toto/README.md diff --git a/attestation-service/attestation-service/src/rvps/extractors/extractor_modules/in_toto/mod.rs b/attestation-service/rvps/src/extractors/extractor_modules/in_toto/mod.rs similarity index 100% rename from attestation-service/attestation-service/src/rvps/extractors/extractor_modules/in_toto/mod.rs rename to attestation-service/rvps/src/extractors/extractor_modules/in_toto/mod.rs diff --git a/attestation-service/attestation-service/src/rvps/extractors/extractor_modules/in_toto/shim/README.md b/attestation-service/rvps/src/extractors/extractor_modules/in_toto/shim/README.md similarity index 100% rename from attestation-service/attestation-service/src/rvps/extractors/extractor_modules/in_toto/shim/README.md rename to attestation-service/rvps/src/extractors/extractor_modules/in_toto/shim/README.md diff --git a/attestation-service/attestation-service/src/rvps/extractors/extractor_modules/in_toto/shim/mod.rs b/attestation-service/rvps/src/extractors/extractor_modules/in_toto/shim/mod.rs similarity index 100% rename from attestation-service/attestation-service/src/rvps/extractors/extractor_modules/in_toto/shim/mod.rs rename to attestation-service/rvps/src/extractors/extractor_modules/in_toto/shim/mod.rs diff --git a/attestation-service/attestation-service/src/rvps/extractors/extractor_modules/mod.rs b/attestation-service/rvps/src/extractors/extractor_modules/mod.rs similarity index 98% rename from attestation-service/attestation-service/src/rvps/extractors/extractor_modules/mod.rs rename to attestation-service/rvps/src/extractors/extractor_modules/mod.rs index 996dedbc8..6fc2dcc34 100644 --- a/attestation-service/attestation-service/src/rvps/extractors/extractor_modules/mod.rs +++ b/attestation-service/rvps/src/extractors/extractor_modules/mod.rs @@ -8,7 +8,7 @@ use anyhow::*; use std::collections::HashMap; -use crate::rvps::ReferenceValue; +use crate::ReferenceValue; #[cfg(feature = "in-toto")] pub mod in_toto; diff --git a/attestation-service/attestation-service/src/rvps/extractors/extractor_modules/sample/README.md b/attestation-service/rvps/src/extractors/extractor_modules/sample/README.md similarity index 100% rename from attestation-service/attestation-service/src/rvps/extractors/extractor_modules/sample/README.md rename to attestation-service/rvps/src/extractors/extractor_modules/sample/README.md diff --git a/attestation-service/attestation-service/src/rvps/extractors/extractor_modules/sample/mod.rs b/attestation-service/rvps/src/extractors/extractor_modules/sample/mod.rs similarity index 98% rename from attestation-service/attestation-service/src/rvps/extractors/extractor_modules/sample/mod.rs rename to attestation-service/rvps/src/extractors/extractor_modules/sample/mod.rs index b6991f128..46af628f7 100644 --- a/attestation-service/attestation-service/src/rvps/extractors/extractor_modules/sample/mod.rs +++ b/attestation-service/rvps/src/extractors/extractor_modules/sample/mod.rs @@ -10,9 +10,10 @@ use std::collections::HashMap; use anyhow::*; use base64::Engine; use chrono::{Months, Timelike, Utc}; +use log::warn; use serde::{Deserialize, Serialize}; -use crate::rvps::{ +use crate::{ reference_value::{HashValuePair, REFERENCE_VALUE_VERSION}, ReferenceValue, }; diff --git a/attestation-service/attestation-service/src/rvps/extractors/mod.rs b/attestation-service/rvps/src/extractors/mod.rs similarity index 100% rename from attestation-service/attestation-service/src/rvps/extractors/mod.rs rename to attestation-service/rvps/src/extractors/mod.rs diff --git a/attestation-service/rvps/src/lib.rs b/attestation-service/rvps/src/lib.rs new file mode 100644 index 000000000..c3eaf8edd --- /dev/null +++ b/attestation-service/rvps/src/lib.rs @@ -0,0 +1,41 @@ +// Copyright (c) 2022 Alibaba Cloud +// +// SPDX-License-Identifier: Apache-2.0 +// + +pub mod extractors; +pub mod pre_processor; +pub mod reference_value; +pub mod store; + +pub mod native; +pub use native::Core; + +use serde::{Deserialize, Serialize}; + +pub use reference_value::{ReferenceValue, TrustedDigest}; +pub use store::Store; + +/// Default version of Message +static MESSAGE_VERSION: &str = "0.1.0"; + +/// Message is an overall packet that Reference Value Provider Service +/// receives. It will contain payload (content of different provenance, +/// JSON format), provenance type (indicates the type of the payload) +/// and a version number (use to distinguish different version of +/// message, for extendability). +/// * `version`: version of this message. +/// * `payload`: content of the provenance, JSON encoded. +/// * `type`: provenance type of the payload. +#[derive(Serialize, Deserialize, Debug)] +pub struct Message { + #[serde(default = "default_version")] + version: String, + payload: String, + r#type: String, +} + +/// Set the default version for Message +fn default_version() -> String { + MESSAGE_VERSION.into() +} diff --git a/attestation-service/attestation-service/src/rvps/native.rs b/attestation-service/rvps/src/native.rs similarity index 74% rename from attestation-service/attestation-service/src/rvps/native.rs rename to attestation-service/rvps/src/native.rs index 63a80093c..3867cf6b5 100644 --- a/attestation-service/attestation-service/src/rvps/native.rs +++ b/attestation-service/rvps/src/native.rs @@ -3,15 +3,17 @@ // SPDX-License-Identifier: Apache-2.0 // -use anyhow::{bail, Result}; +use anyhow::{bail, Context, Result}; use chrono::{DateTime, Utc}; use log::{info, warn}; use std::time::SystemTime; +use crate::store::StoreType; + use super::{ extractors::{Extractors, ExtractorsImpl}, - pre_processor::{PreProcessor, PreProcessorAPI, Ware}, - Message, Store, TrustedDigest, MESSAGE_VERSION, RVPSAPI, + pre_processor::{PreProcessor, PreProcessorAPI}, + Message, Store, TrustedDigest, MESSAGE_VERSION, }; /// The core of the RVPS, s.t. componants except communication componants. @@ -23,28 +25,30 @@ pub struct Core { impl Core { /// Instantiate a new RVPS Core - pub fn new(store: Box) -> Self { + pub fn new(store_type: &str) -> Result { let pre_processor = PreProcessor::default(); let extractors = ExtractorsImpl::default(); - Core { + let store_type = StoreType::try_from(store_type)?; + let store = store_type.to_store()?; + + Ok(Core { pre_processor, extractors, store, - } + }) } /// Add Ware to the Core's Pre-Processor - pub fn with_ware(&mut self, ware: Box) -> &Self { - self.pre_processor.add_ware(ware); + pub fn with_ware(&mut self, _ware: &str) -> &Self { + // TODO: no wares implemented now. self } -} -#[async_trait::async_trait] -impl RVPSAPI for Core { - async fn verify_and_extract(&mut self, mut message: Message) -> Result<()> { + pub async fn verify_and_extract(&mut self, message: &str) -> Result<()> { + let mut message: Message = serde_json::from_str(message).context("parse message")?; + // Judge the version field if message.version != MESSAGE_VERSION { bail!( @@ -67,7 +71,7 @@ impl RVPSAPI for Core { Ok(()) } - async fn get_digests(&self, name: &str) -> Result> { + pub async fn get_digests(&self, name: &str) -> Result> { let rv = self.store.get(name)?; match rv { None => Ok(None), diff --git a/attestation-service/attestation-service/src/rvps/pre_processor/mod.rs b/attestation-service/rvps/src/pre_processor/mod.rs similarity index 100% rename from attestation-service/attestation-service/src/rvps/pre_processor/mod.rs rename to attestation-service/rvps/src/pre_processor/mod.rs diff --git a/attestation-service/attestation-service/src/rvps/reference_value.rs b/attestation-service/rvps/src/reference_value.rs similarity index 100% rename from attestation-service/attestation-service/src/rvps/reference_value.rs rename to attestation-service/rvps/src/reference_value.rs diff --git a/attestation-service/attestation-service/src/rvps/store/local_fs/README.md b/attestation-service/rvps/src/store/local_fs/README.md similarity index 100% rename from attestation-service/attestation-service/src/rvps/store/local_fs/README.md rename to attestation-service/rvps/src/store/local_fs/README.md diff --git a/attestation-service/attestation-service/src/rvps/store/local_fs/mod.rs b/attestation-service/rvps/src/store/local_fs/mod.rs similarity index 97% rename from attestation-service/attestation-service/src/rvps/store/local_fs/mod.rs rename to attestation-service/rvps/src/store/local_fs/mod.rs index b8dd663ba..aa717d621 100644 --- a/attestation-service/attestation-service/src/rvps/store/local_fs/mod.rs +++ b/attestation-service/rvps/src/store/local_fs/mod.rs @@ -9,7 +9,7 @@ use std::path::Path; use anyhow::*; -use crate::rvps::ReferenceValue; +use crate::ReferenceValue; use super::Store; @@ -75,7 +75,9 @@ impl Store for LocalFs { mod tests { use serial_test::serial; - use crate::rvps::{store::local_fs::LocalFs, ReferenceValue, Store}; + use crate::{ReferenceValue, Store}; + + use super::LocalFs; const KEY: &str = "test1"; diff --git a/attestation-service/attestation-service/src/rvps/store/mod.rs b/attestation-service/rvps/src/store/mod.rs similarity index 98% rename from attestation-service/attestation-service/src/rvps/store/mod.rs rename to attestation-service/rvps/src/store/mod.rs index 71f8b277a..4c28e4af0 100644 --- a/attestation-service/attestation-service/src/rvps/store/mod.rs +++ b/attestation-service/rvps/src/store/mod.rs @@ -7,6 +7,7 @@ use anyhow::Result; use serde::Deserialize; +use strum::EnumString; use self::local_fs::LocalFs; @@ -20,7 +21,6 @@ pub enum StoreType { } impl StoreType { - #[allow(dead_code)] pub fn to_store(&self) -> Result> { match self { StoreType::LocalFs => Ok(Box::::default() as Box), diff --git a/kbs/src/api/Cargo.toml b/kbs/src/api/Cargo.toml index e1a900162..9e28555fe 100644 --- a/kbs/src/api/Cargo.toml +++ b/kbs/src/api/Cargo.toml @@ -14,7 +14,7 @@ policy = [] opa = ["policy"] coco-as = ["as"] coco-as-builtin = ["coco-as", "attestation-service/default"] -coco-as-builtin-no-verifier = ["coco-as", "attestation-service/rvps-native"] +coco-as-builtin-no-verifier = ["coco-as", "attestation-service/rvps-builtin"] coco-as-grpc = ["coco-as", "tonic", "tonic-build", "prost"] amber-as = ["as", "reqwest", "jsonwebtoken"] rustls = ["actix-web/rustls", "dep:rustls", "dep:rustls-pemfile"] diff --git a/kbs/src/api/src/attestation/coco/builtin.rs b/kbs/src/api/src/attestation/coco/builtin.rs index 66d31378a..416e451fe 100644 --- a/kbs/src/api/src/attestation/coco/builtin.rs +++ b/kbs/src/api/src/attestation/coco/builtin.rs @@ -28,9 +28,9 @@ impl Attest for Native { } impl Native { - pub fn new(config: &AsConfig) -> Result { + pub async fn new(config: &AsConfig) -> Result { Ok(Self { - inner: AttestationService::new(config.clone())?, + inner: AttestationService::new(config.clone()).await?, }) } } diff --git a/kbs/src/api/src/attestation/mod.rs b/kbs/src/api/src/attestation/mod.rs index 67d51b40e..b1c51b87c 100644 --- a/kbs/src/api/src/attestation/mod.rs +++ b/kbs/src/api/src/attestation/mod.rs @@ -43,9 +43,9 @@ pub struct AttestationService(pub Arc>); impl AttestationService { /// Create and initialize AttestationService. #[cfg(any(feature = "coco-as-builtin", feature = "coco-as-builtin-no-verifier"))] - pub fn new(config: &AsConfig) -> Result { + pub async fn new(config: &AsConfig) -> Result { let attestation_service: Arc> = - Arc::new(Mutex::new(coco::builtin::Native::new(config)?)); + Arc::new(Mutex::new(coco::builtin::Native::new(config).await?)); Ok(Self(attestation_service)) } diff --git a/kbs/src/kbs/src/main.rs b/kbs/src/kbs/src/main.rs index 64fae44d5..2c14d3043 100644 --- a/kbs/src/kbs/src/main.rs +++ b/kbs/src/kbs/src/main.rs @@ -43,7 +43,7 @@ async fn main() -> Result<()> { let attestation_service = { cfg_if::cfg_if! { if #[cfg(any(feature = "coco-as-builtin", feature = "coco-as-builtin-no-verifier"))] { - AttestationService::new(&kbs_config.as_config.unwrap_or_default())? + AttestationService::new(&kbs_config.as_config.unwrap_or_default()).await? } else if #[cfg(feature = "coco-as-grpc")] { AttestationService::new(&kbs_config.grpc_config.unwrap_or_default()).await? } else if #[cfg(feature = "amber-as")] { diff --git a/kbs/test/data/e2e/kbs.toml b/kbs/test/data/e2e/kbs.toml index 853056955..009abb5da 100644 --- a/kbs/test/data/e2e/kbs.toml +++ b/kbs/test/data/e2e/kbs.toml @@ -15,5 +15,9 @@ attestation_token_broker = "Simple" [as_config.attestation_token_config] duration_min = 5 +[as_config.rvps_config] +store_type = "LocalFs" +remote_addr = "" + [policy_engine_config] policy_path = "./data/policy_1.rego" From 87e30983ae9e5c256982072d6be2c6f3f84264b0 Mon Sep 17 00:00:00 2001 From: Xynnn007 Date: Mon, 13 Nov 2023 10:13:28 +0800 Subject: [PATCH 04/11] AS/RVPS: fix document Signed-off-by: Xynnn007 --- attestation-service/README.md | 4 +- .../{docs/rvps.md => rvps/README.md} | 39 ++++++++++--------- .../{docs => rvps/diagrams}/rvps-grpc.svg | 0 .../{docs => rvps/diagrams}/rvps-native.svg | 0 .../{docs => rvps/diagrams}/rvps.svg | 0 5 files changed, 22 insertions(+), 21 deletions(-) rename attestation-service/{docs/rvps.md => rvps/README.md} (76%) rename attestation-service/{docs => rvps/diagrams}/rvps-grpc.svg (100%) rename attestation-service/{docs => rvps/diagrams}/rvps-native.svg (100%) rename attestation-service/{docs => rvps/diagrams}/rvps.svg (100%) diff --git a/attestation-service/README.md b/attestation-service/README.md index 0c8d1f734..2f6b10e64 100644 --- a/attestation-service/README.md +++ b/attestation-service/README.md @@ -64,7 +64,7 @@ The main architecture of the Attestation Service is shown in the figure below: ``` The Reference Value Provider Service supports different deploy mode, -please refer to [the doc](./docs/rvps.md#run-mode) for more details. +please refer to [the doc](./rvps/README.md#run-mode) for more details. ### Evidence format: @@ -145,6 +145,6 @@ If the user does not need to customize his own policy, AS will use the [default ## Reference Value Provider -[Reference Value Provider Service](docs/rvps.md) (RVPS for short) is a module integrated in the AS to verify, +[Reference Value Provider Service](rvps/README.md) (RVPS for short) is a module integrated in the AS to verify, store and provide reference values. RVPS receives and verifies the provenance input from the software supply chain, stores the measurement values, and generates reference value claims for the AS according to the evidence content when the AS verifies the evidence. \ No newline at end of file diff --git a/attestation-service/docs/rvps.md b/attestation-service/rvps/README.md similarity index 76% rename from attestation-service/docs/rvps.md rename to attestation-service/rvps/README.md index e81332e61..a3d1df131 100644 --- a/attestation-service/docs/rvps.md +++ b/attestation-service/rvps/README.md @@ -16,7 +16,7 @@ RVPS contains the following componants: ## Message Flow The message flow of RVPS is like the following figure -![](./rvps.svg) +![](./diagrams/rvps.svg) ### Message @@ -51,7 +51,9 @@ In this way, the RVPS can run as a single service. The [gRPC protos](../protos/r We can run using the following command ```bash -cargo run --bin rvps --features="rvps-native rvps-grpc tokio/rt-multi-thread" +git clone https://github.com/confidential-containers/kbs +cd kbs/attestation-service/rvps +make build && make install ``` To by default listen to `localhost:50003` to wait for requests @@ -61,7 +63,7 @@ To by default listen to `localhost:50003` to wait for requests We can build RVPS docker image ```bash -docker build -t rvps -f Dockerfile.rvps . +cd ../.. && docker build -t rvps -f attestation-service/rvps/Dockerfile . ``` Run @@ -71,25 +73,23 @@ docker run -d -p 50003:50003 rvps ## Integrate RVPS into AS -### Native Mode +### Native Mode (Not Recommend) -In this way RVPS will work as a crate inside AS binary. +In this way RVPS will work as a crate inside AS binary. If AS is built without feature `rvps-grpc` +and with feature `rvps-builtin`, the RVPS will be built-in AS. -```bash -cargo run --bin grpc-as -``` - -![](./rvps-native.svg) +![](./diagrams/rvps-native.svg) ### gRPC Mode -In this way AS will connect to a remote RVPS. +In this way AS will connect to a remote RVPS. If AS is built with feature `rvps-grpc`, the remote RVPS +will be connected to. ```bash -cargo run --bin grpc-as -- --rvps-address $RVPS_ADDR +cd ../attestation-service && cargo run --bin as-grpc -- --config-file config.json ``` -![](./rvps-grpc.svg) +![](./diagrams/rvps-grpc.svg) ## Client Tool @@ -99,12 +99,13 @@ A client tool helps to perform as a client to rvps. It can ### Quick guide to interact with RVPS -Run RVPS +Run RVPS in docker or the following commands ```bash -cargo run --bin rvps -- --socket $RVPS_ADDR +RVPS_ADDR=127.0.0.1:50003 +rvps --socket $RVPS_ADDR ``` -Edit an test message in [sample format](../attestation-service/src/rvps/extractors/extractor_modules/sample/README.md) +Edit an test message in [sample format](./src/extractors/extractor_modules/sample/README.md) ```bash cat << EOF > sample { @@ -130,7 +131,7 @@ EOF Register the provenance into RVPS ```bash -cargo run --bin rvps-client -- register --path ./message --addr $RVPS_HTTP_ADDR +rvps-tool register --path ./message --addr http://$RVPS_ADDR ``` Then it will say something like @@ -140,11 +141,11 @@ Then it will say something like Let's then query the reference value ```bash -cargo run --bin rvps-client -- query --name test-binary-1 --addr $RVPS_HTTP_ADDR +rvps-tool query --name test-binary-1 --addr http://$RVPS_ADDR ``` Then the reference values will be output ``` [2023-03-09T05:13:50Z INFO rvps_client] Get reference values succeeded: - {"name":"test-binary-1","hash_values":["reference-value-1","reference-value-2"]} + ["reference-value-1","reference-value-2"] ``` \ No newline at end of file diff --git a/attestation-service/docs/rvps-grpc.svg b/attestation-service/rvps/diagrams/rvps-grpc.svg similarity index 100% rename from attestation-service/docs/rvps-grpc.svg rename to attestation-service/rvps/diagrams/rvps-grpc.svg diff --git a/attestation-service/docs/rvps-native.svg b/attestation-service/rvps/diagrams/rvps-native.svg similarity index 100% rename from attestation-service/docs/rvps-native.svg rename to attestation-service/rvps/diagrams/rvps-native.svg diff --git a/attestation-service/docs/rvps.svg b/attestation-service/rvps/diagrams/rvps.svg similarity index 100% rename from attestation-service/docs/rvps.svg rename to attestation-service/rvps/diagrams/rvps.svg From eba524be107ef8a2b5d91b4a593cdf9491f4328b Mon Sep 17 00:00:00 2001 From: Xynnn007 Date: Mon, 13 Nov 2023 20:56:46 +0800 Subject: [PATCH 05/11] AS/RVPS: add Makefile for RVPS Signed-off-by: Xynnn007 --- attestation-service/rvps/Makefile | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 attestation-service/rvps/Makefile diff --git a/attestation-service/rvps/Makefile b/attestation-service/rvps/Makefile new file mode 100644 index 000000000..f9045f579 --- /dev/null +++ b/attestation-service/rvps/Makefile @@ -0,0 +1,23 @@ +PREFIX := /usr/local + +TARGET_DIR := ../../target/release +DESTDIR ?= $(PREFIX)/bin +BIN_NAMES := rvps rvps-tool + +build: bin tool + +# Build RVPS in gRPC version +bin: + cargo build --release --bin rvps --no-default-features --features bin + +# Build RVPS client tool +tool: + cargo build --release --bin rvps-tool --no-default-features --features bin + +install: + for bin_name in $(BIN_NAMES); do \ + install -D -m0755 $(TARGET_DIR)/$$bin_name $(DESTDIR); \ + done + +clean: + cargo clean \ No newline at end of file From 4b38c2bab721ced7caf17d86ec6454a4aae9fe80 Mon Sep 17 00:00:00 2001 From: Xynnn007 Date: Mon, 13 Nov 2023 21:52:31 +0800 Subject: [PATCH 06/11] AS: use workspace dependency Signed-off-by: Xynnn007 --- Cargo.lock | 21 +++------ attestation-service/bin/grpc-as/Cargo.toml | 2 +- attestation-service/bin/grpc-as/src/main.rs | 46 +++++++++---------- attestation-service/bin/grpc-as/src/server.rs | 10 ++-- attestation-service/verifier/Cargo.toml | 3 +- .../verifier/src/tdx/eventlog.rs | 2 +- attestation-service/verifier/src/tdx/mod.rs | 4 +- 7 files changed, 37 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 72a344718..a6aca2cff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -428,7 +428,7 @@ dependencies = [ "semver 1.0.20", "serde", "serde_json", - "strum 0.25.0", + "strum", "strum_macros 0.24.3", "tempfile", "thiserror", @@ -546,7 +546,7 @@ dependencies = [ "serial_test", "sha2", "shadow-rs", - "strum 0.25.0", + "strum", "testing_logger", "time", "tokio", @@ -577,7 +577,7 @@ dependencies = [ "serde", "serde_json", "sev", - "strum 0.25.0", + "strum", "tdx-attest-rs", "tokio", ] @@ -1287,7 +1287,7 @@ dependencies = [ "serde", "serde_json", "sha2", - "strum 0.25.0", + "strum", "zeroize", ] @@ -1929,7 +1929,7 @@ dependencies = [ "anyhow", "async-trait", "attestation-service", - "clap 3.2.25", + "clap 4.4.7", "env_logger 0.10.0", "log", "prost", @@ -3582,7 +3582,7 @@ dependencies = [ "serial_test", "shadow-rs", "sled", - "strum 0.25.0", + "strum", "tempfile", "tokio", "tonic 0.8.3", @@ -4439,12 +4439,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "strum" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" - [[package]] name = "strum" version = "0.25.0" @@ -5172,8 +5166,7 @@ dependencies = [ "sev", "sgx-dcap-quoteverify-rs", "shadow-rs", - "strum 0.24.1", - "strum_macros 0.24.3", + "strum", "tokio", "tonic-build", "veraison-apiclient", diff --git a/attestation-service/bin/grpc-as/Cargo.toml b/attestation-service/bin/grpc-as/Cargo.toml index 5c4c13113..01978b179 100644 --- a/attestation-service/bin/grpc-as/Cargo.toml +++ b/attestation-service/bin/grpc-as/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" anyhow.workspace = true async-trait.workspace = true attestation-service = { path = "../../attestation-service", features = ["rvps-grpc"] } -clap = "3" +clap.workspace = true env_logger.workspace = true log.workspace = true prost.workspace = true diff --git a/attestation-service/bin/grpc-as/src/main.rs b/attestation-service/bin/grpc-as/src/main.rs index 1dc524006..9e6a8d323 100644 --- a/attestation-service/bin/grpc-as/src/main.rs +++ b/attestation-service/bin/grpc-as/src/main.rs @@ -1,5 +1,8 @@ +use std::net::SocketAddr; + use anyhow::Result; -use clap::{App, Arg}; +use clap::Parser; +use log::info; use shadow_rs::shadow; pub mod as_api { @@ -14,6 +17,19 @@ shadow!(build); mod server; +/// gRPC CoCo-AS command-line arguments. +#[derive(Debug, Parser)] +#[command(author, version, about, long_about = None)] +pub struct Cli { + /// Path to a CoCo-AS config file. + #[arg(short, long)] + pub config_file: Option, + + /// Socket that the server will listen on to accept requests. + #[arg(short, long, default_value = "127.0.0.1:3000")] + pub socket: SocketAddr, +} + #[tokio::main] async fn main() -> Result<()> { env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info")).init(); @@ -25,29 +41,11 @@ async fn main() -> Result<()> { build::BUILD_TIME ); - let matches = App::new("grpc-attestation-service") - .version(version.as_str()) - .long_version(version.as_str()) - .author("Confidential-Containers Team") - .arg( - Arg::with_name("socket") - .long("socket") - .value_name("SOCKET") - .help("Socket that the server will listen on to accept requests.") - .takes_value(true), - ) - .arg( - Arg::with_name("config") - .long("config") - .value_name("config") - .help("File path of AS config (JSON), left blank to use default config") - .required(false) - .takes_value(true), - ) - .get_matches(); - - let config_path = matches.value_of("config"); - let server = server::start(matches.value_of("socket"), config_path); + info!("CoCo AS: {version}"); + + let cli = Cli::parse(); + + let server = server::start(cli.socket, cli.config_file); tokio::try_join!(server)?; Ok(()) diff --git a/attestation-service/bin/grpc-as/src/server.rs b/attestation-service/bin/grpc-as/src/server.rs index e8dbce586..f1352e67f 100644 --- a/attestation-service/bin/grpc-as/src/server.rs +++ b/attestation-service/bin/grpc-as/src/server.rs @@ -2,6 +2,7 @@ use anyhow::{anyhow, Result}; use attestation_service::policy_engine::SetPolicyInput; use attestation_service::{config::Config, AttestationService as Service, Tee}; use log::{debug, info}; +use std::net::SocketAddr; use std::path::Path; use std::sync::Arc; use tokio::sync::RwLock; @@ -22,8 +23,6 @@ use crate::rvps_api::{ ReferenceValueRegisterResponse, }; -const DEFAULT_SOCK: &str = "127.0.0.1:3000"; - fn to_kbs_tee(tee: GrpcTee) -> Tee { match tee { GrpcTee::Sev => Tee::Sev, @@ -41,9 +40,9 @@ pub struct AttestationServer { } impl AttestationServer { - pub async fn new(config_path: Option<&str>) -> Result { + pub async fn new(config_path: Option) -> Result { let config = match config_path { - Some(path) => Config::try_from(Path::new(path)) + Some(path) => Config::try_from(Path::new(&path)) .map_err(|e| anyhow!("Read AS config file failed: {:?}", e))?, None => Config::default(), }; @@ -143,8 +142,7 @@ impl ReferenceValueProviderService for Arc> { } } -pub async fn start(socket: Option<&str>, config_path: Option<&str>) -> Result<()> { - let socket = socket.unwrap_or(DEFAULT_SOCK).parse()?; +pub async fn start(socket: SocketAddr, config_path: Option) -> Result<()> { info!("Listen socket: {}", &socket); let attestation_server = Arc::new(RwLock::new(AttestationServer::new(config_path).await?)); diff --git a/attestation-service/verifier/Cargo.toml b/attestation-service/verifier/Cargo.toml index 4cbac099f..95324b884 100644 --- a/attestation-service/verifier/Cargo.toml +++ b/attestation-service/verifier/Cargo.toml @@ -38,8 +38,7 @@ serde.workspace = true serde_json.workspace = true sev = { version = "1.2.0", features = ["openssl", "snp"], optional = true } sgx-dcap-quoteverify-rs = { git = "https://github.com/intel/SGXDataCenterAttestationPrimitives", tag = "DCAP_1.16", optional = true } -strum = "0.24.0" -strum_macros = "0.24.0" +strum.workspace = true veraison-apiclient = { git = "https://github.com/chendave/rust-apiclient", branch = "token", optional = true } ear = { git = "https://github.com/veraison/rust-ear", rev = "cc6ea53" } x509-parser = { version = "0.14.0", optional = true } diff --git a/attestation-service/verifier/src/tdx/eventlog.rs b/attestation-service/verifier/src/tdx/eventlog.rs index 4f4e124b6..52c359953 100644 --- a/attestation-service/verifier/src/tdx/eventlog.rs +++ b/attestation-service/verifier/src/tdx/eventlog.rs @@ -4,7 +4,7 @@ use core::mem::size_of; use eventlog_rs::Eventlog; use std::convert::{TryFrom, TryInto}; use std::string::ToString; -use strum_macros::{Display, EnumString}; +use strum::{Display, EnumString}; #[derive(Debug, Clone, EnumString, Display)] pub enum MeasuredEntity { diff --git a/attestation-service/verifier/src/tdx/mod.rs b/attestation-service/verifier/src/tdx/mod.rs index e697b691c..a6bc70b42 100644 --- a/attestation-service/verifier/src/tdx/mod.rs +++ b/attestation-service/verifier/src/tdx/mod.rs @@ -1,16 +1,14 @@ use anyhow::{anyhow, Context, Result}; use log::{debug, warn}; -extern crate serde; -extern crate strum; use crate::tdx::claims::generate_parsed_claim; -use self::serde::{Deserialize, Serialize}; use super::*; use async_trait::async_trait; use base64::Engine; use eventlog::{CcEventLog, Rtmr}; use quote::{ecdsa_quote_verification, parse_tdx_quote}; +use serde::{Deserialize, Serialize}; mod claims; mod eventlog; From f20f1e7b35b4cda8467f82559e61cfc3d29bbf6f Mon Sep 17 00:00:00 2001 From: Xynnn007 Date: Mon, 13 Nov 2023 22:39:37 +0800 Subject: [PATCH 07/11] KBS: resolve AS dep Signed-off-by: Xynnn007 --- Cargo.toml | 1 - kbs/src/api/Cargo.toml | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 318933112..b4cc5fe98 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,6 @@ anyhow = "1.0" api-server = { path = "kbs/src/api", default-features = false } assert-json-diff = "2.0.2" async-trait = "0.1.31" -attestation-service = { path = "attestation-service/attestation-service", default-features = false } base64 = "0.21" cfg-if = "1.0.0" chrono = "0.4.19" diff --git a/kbs/src/api/Cargo.toml b/kbs/src/api/Cargo.toml index 9e28555fe..d04883ec6 100644 --- a/kbs/src/api/Cargo.toml +++ b/kbs/src/api/Cargo.toml @@ -26,7 +26,7 @@ actix-web-httpauth = "0.8.0" aes-gcm = { version = "0.10.1", optional = true } anyhow.workspace = true async-trait.workspace = true -attestation-service = { workspace = true, default-features = false, optional = true } +attestation-service = { path = "../../../attestation-service/attestation-service", default-features = false, optional = true } base64.workspace = true cfg-if.workspace = true clap = { version = "4.3.21", features = ["derive", "env"] } From f3eb3dd696cb261522e0a6231a932578d8a0ad25 Mon Sep 17 00:00:00 2001 From: Xynnn007 Date: Mon, 13 Nov 2023 22:40:14 +0800 Subject: [PATCH 08/11] ci: fix AS ci Signed-off-by: Xynnn007 --- .github/workflows/as-basic.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/as-basic.yml b/.github/workflows/as-basic.yml index 01a52ae4c..c30b2b5ea 100644 --- a/.github/workflows/as-basic.yml +++ b/.github/workflows/as-basic.yml @@ -73,17 +73,17 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: -p attestation-service -p as-types -p grpc-as -p rvps -p rvps-client + args: -p attestation-service -p grpc-as -p reference-value-provider-service - name: Run cargo fmt check uses: actions-rs/cargo@v1 with: command: fmt - args: -p attestation-service -p as-types -p grpc-as -p rvps -p rvps-client --check + args: -p attestation-service -p grpc-as -p reference-value-provider-service --check - name: Run rust lint check uses: actions-rs/cargo@v1 with: command: clippy # We are getting error in generated code due to derive_partial_eq_without_eq check, so ignore it for now - args: -p attestation-service -p as-types -p grpc-as -p rvps -p rvps-client -- -D warnings -A clippy::derive_partial_eq_without_eq + args: -p attestation-service -p grpc-as -p reference-value-provider-service -- -D warnings -A clippy::derive_partial_eq_without_eq From a2260690c6213f624857c61cbb3866279966d3cb Mon Sep 17 00:00:00 2001 From: Xynnn007 Date: Tue, 14 Nov 2023 10:31:58 +0800 Subject: [PATCH 09/11] ci: fix RVPS image building Path of Dockerfile Signed-off-by: Xynnn007 --- .github/workflows/as-dockerbuild.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/as-dockerbuild.yml b/.github/workflows/as-dockerbuild.yml index bf10a4bb1..69513f022 100644 --- a/.github/workflows/as-dockerbuild.yml +++ b/.github/workflows/as-dockerbuild.yml @@ -32,4 +32,4 @@ jobs: - name: Build RVPS Container Image run: | - Docker_BUILDKIT=1 docker build -t rvps:latest . -f attestation-service/Dockerfile.rvps \ No newline at end of file + Docker_BUILDKIT=1 docker build -t rvps:latest . -f attestation-service/rvps/Dockerfile \ No newline at end of file From ca329bf60f2646531d0a9b5dbd3ecdceeecae16b Mon Sep 17 00:00:00 2001 From: Xynnn007 Date: Tue, 14 Nov 2023 10:33:13 +0800 Subject: [PATCH 10/11] Docker: add dockerignore Signed-off-by: Xynnn007 --- .dockerignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..81834c87e --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +.github +.git +target +**Dockerfile** \ No newline at end of file From da32a48c4361db83204dafdcec95e276ba3c4a2b Mon Sep 17 00:00:00 2001 From: Xynnn007 Date: Tue, 14 Nov 2023 18:03:34 +0800 Subject: [PATCH 11/11] AS: fix function name Signed-off-by: Xynnn007 --- attestation-service/attestation-service/src/lib.rs | 2 +- attestation-service/bin/grpc-as/src/server.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/attestation-service/attestation-service/src/lib.rs b/attestation-service/attestation-service/src/lib.rs index 97cc08463..a94cf2b8f 100644 --- a/attestation-service/attestation-service/src/lib.rs +++ b/attestation-service/attestation-service/src/lib.rs @@ -155,7 +155,7 @@ impl AttestationService { } /// Registry a new reference value - pub async fn registry_reference_value(&mut self, message: &str) -> Result<()> { + pub async fn register_reference_value(&mut self, message: &str) -> Result<()> { self.rvps.verify_and_extract(message).await } } diff --git a/attestation-service/bin/grpc-as/src/server.rs b/attestation-service/bin/grpc-as/src/server.rs index f1352e67f..88e9242f0 100644 --- a/attestation-service/bin/grpc-as/src/server.rs +++ b/attestation-service/bin/grpc-as/src/server.rs @@ -133,7 +133,7 @@ impl ReferenceValueProviderService for Arc> { self.write() .await .attestation_service - .registry_reference_value(message) + .register_reference_value(message) .await .map_err(|e| Status::aborted(format!("Register reference value: {e}")))?;