From 9caf44a68e2715f2263037853b8045b025bec73f Mon Sep 17 00:00:00 2001 From: SPYFAMILY Date: Thu, 28 Aug 2025 14:54:23 +0800 Subject: [PATCH] Add support for virtcca platform token verify --- ...rt-for-virtcca-platform-token-verify.patch | 1737 +++++++++++++++++ secGear.spec | 12 +- 2 files changed, 1747 insertions(+), 2 deletions(-) create mode 100644 0107-Add-support-for-virtcca-platform-token-verify.patch diff --git a/0107-Add-support-for-virtcca-platform-token-verify.patch b/0107-Add-support-for-virtcca-platform-token-verify.patch new file mode 100644 index 0000000..c3c96c7 --- /dev/null +++ b/0107-Add-support-for-virtcca-platform-token-verify.patch @@ -0,0 +1,1737 @@ +From 444cf174af1fd3db71195a25dfb9cb86c74ed816 Mon Sep 17 00:00:00 2001 +From: SPYFAMILY +Date: Tue, 29 Jul 2025 20:01:13 +0800 +Subject: [PATCH] =?UTF-8?q?1.=20refactor=EF=BC=9A=E5=9C=A8mod.rs=E6=8F=90?= + =?UTF-8?q?=E5=8F=96CVM=20TOEKN=E3=80=81PLATFORM=20TOKEN=E7=B1=BB=E5=88=B0?= + =?UTF-8?q?ccatoken.rs=202.=20refactor=EF=BC=9A=E6=8F=90=E5=8F=96=E5=92=8C?= + =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=83=A8=E5=88=86=E5=87=BD=E6=95=B0=EF=BC=88?= + =?UTF-8?q?=E5=A6=82=E9=AA=8C=E7=AD=BE=E3=80=81=E8=A7=A3=E7=A0=81=E7=AD=89?= + =?UTF-8?q?=EF=BC=89=EF=BC=8C=E5=87=8F=E5=B0=91=E4=BB=A3=E7=A0=81=E9=87=8D?= + =?UTF-8?q?=E5=A4=8D=EF=BC=8C=E5=A2=9E=E5=8A=A0=E5=87=BD=E6=95=B0=E9=87=8D?= + =?UTF-8?q?=E7=94=A8=203.=20feat=EF=BC=9A=E6=94=AF=E6=8C=81=E4=B8=8D?= + =?UTF-8?q?=E5=90=8C=E8=AF=81=E4=B9=A6=EF=BC=88RSA+ECCP521=EF=BC=89?= + =?UTF-8?q?=E8=AF=81=E4=B9=A6=E9=93=BE=E9=AA=8C=E8=AF=81,=20=E5=90=8C?= + =?UTF-8?q?=E6=97=B6=E4=BE=BF=E4=BA=8E=E5=90=8E=E7=BB=AD=E6=89=A9=E5=B1=95?= + =?UTF-8?q?=E5=85=B6=E4=BB=96=E8=AF=81=E4=B9=A6=204.=20feat=EF=BC=9A?= + =?UTF-8?q?=E6=96=B0=E5=A2=9EPlatform=E3=80=81sw=E7=BB=93=E6=9E=84?= + =?UTF-8?q?=E4=BD=93=E3=80=81=E8=A7=A3=E7=A0=81=E8=AE=BE=E7=BD=AE=E5=87=BD?= + =?UTF-8?q?=E6=95=B0=E7=AD=89=205.=20feat=EF=BC=9A=E5=AE=9E=E7=8E=B0Platfo?= + =?UTF-8?q?rm=E9=AA=8C=E7=AD=BE=E3=80=82=EF=BC=88=E5=8C=85=E6=8B=AC?= + =?UTF-8?q?=E4=BD=BF=E7=94=A8platform=E7=9A=84challenge=E9=AA=8C=E8=AF=81c?= + =?UTF-8?q?vm=E7=9A=84publickey=EF=BC=89=206.=20feat=EF=BC=9Acvm=E3=80=81p?= + =?UTF-8?q?latform=E3=80=81sw=E7=BB=93=E6=9E=84=E4=BD=93=E5=BA=8F=E5=88=97?= + =?UTF-8?q?=E5=8C=96=E8=BE=93=E5=87=BA=207.=20feat=EF=BC=9A=E9=80=82?= + =?UTF-8?q?=E9=85=8D=E6=96=B0=E6=97=A7virtcca=E5=B9=B3=E5=8F=B0=EF=BC=88?= + =?UTF-8?q?=E6=98=AF=E5=90=A6=E5=AD=98=E5=9C=A8Platform=20token=EF=BC=89?= + =?UTF-8?q?=E7=9A=84=E8=BF=9C=E7=A8=8B=E8=AF=81=E6=98=8E=208.=20feat?= + =?UTF-8?q?=EF=BC=9A=E6=94=AF=E6=8C=81=E5=A4=96=E9=83=A8=E5=BC=95=E7=94=A8?= + =?UTF-8?q?platform=E5=92=8Ccvm=20token=E7=9A=84=E9=AA=8C=E8=AF=81?= + =?UTF-8?q?=E5=87=BD=E6=95=B0=209.=20feat=EF=BC=9Aplatform=20sw=E7=BB=84?= + =?UTF-8?q?=E4=BB=B6=E5=9F=BA=E7=BA=BF=E9=AA=8C=E8=AF=81=EF=BC=88=E5=9F=BA?= + =?UTF-8?q?=E4=BA=8E=E7=AD=96=E7=95=A5=EF=BC=89=2010.=20feat=EF=BC=9A?= + =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=BB=98=E8=AE=A4=E7=AD=96=E7=95=A5=EF=BC=8C?= + =?UTF-8?q?=E5=8F=AF=E5=8F=82=E8=80=83=E9=BB=98=E8=AE=A4=E7=AD=96=E7=95=A5?= + =?UTF-8?q?=E8=AE=BE=E7=BD=AEplatform=20sw=E6=A0=A1=E9=AA=8C=2011.=20refac?= + =?UTF-8?q?tor:=20=E8=A7=84=E8=8C=83ccel=E3=80=81uefi=5Flog=E7=AD=89?= + =?UTF-8?q?=E5=90=8D=E7=A7=B0=EF=BC=8C=E7=BB=9F=E4=B8=80=E4=BF=AE=E6=94=B9?= + =?UTF-8?q?=E4=B8=BAevent=5Flog=EF=BC=9B=E7=A7=BB=E9=99=A4=E6=9C=AA?= + =?UTF-8?q?=E5=8F=82=E4=B8=8E=E8=AF=81=E6=98=8E=E8=BF=87=E7=A8=8B=E7=9A=84?= + =?UTF-8?q?ccel=5Ftable?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +--- + .../attestation-agent/attester/src/lib.rs | 3 +- + .../attester/src/virtcca/mod.rs | 30 +- + .../attestation-service/policy/Cargo.toml | 1 + + .../policy/src/opa/default_itrustee.rego | 30 +- + .../policy/src/opa/default_vcca.rego | 51 +- + .../attestation-service/policy/src/opa/mod.rs | 3 +- + .../reference/src/reference/mod.rs | 25 +- + .../attestation-service/service/src/lib.rs | 15 +- + .../verifier/src/virtcca/ccatoken.rs | 439 +++++++++++++++ + .../src/virtcca/{uefi.rs => event_log.rs} | 96 ++-- + .../verifier/src/virtcca/mod.rs | 503 ++++++++---------- + .../attestation/attestation-types/src/lib.rs | 8 +- + .../src/resource/policy/opa/mod.rs | 2 +- + 13 files changed, 829 insertions(+), 377 deletions(-) + create mode 100644 service/attestation/attestation-service/verifier/src/virtcca/ccatoken.rs + rename service/attestation/attestation-service/verifier/src/virtcca/{uefi.rs => event_log.rs} (64%) + +diff --git a/service/attestation/attestation-agent/attester/src/lib.rs b/service/attestation/attestation-agent/attester/src/lib.rs +index c7d0096..25af6cb 100644 +--- a/service/attestation/attestation-agent/attester/src/lib.rs ++++ b/service/attestation/attestation-agent/attester/src/lib.rs +@@ -46,7 +46,6 @@ pub struct Attester {} + #[async_trait] + impl AttesterAPIs for Attester { + async fn tee_get_evidence(&self, _user_data: EvidenceRequest) -> Result> { +- + #[cfg(feature = "itrustee-attester")] + if itrustee::detect_platform() { + let evidence = itrustee::ItrusteeAttester::default() +@@ -67,7 +66,7 @@ impl AttesterAPIs for Attester { + .await?; + let aa_evidence = attestation_types::Evidence { + tee: attestation_types::TeeType::Virtcca, +- evidence: evidence, ++ evidence, + }; + let evidence = serde_json::to_vec(&aa_evidence)?; + return Ok(evidence); +diff --git a/service/attestation/attestation-agent/attester/src/virtcca/mod.rs b/service/attestation/attestation-agent/attester/src/virtcca/mod.rs +index 9be04f0..8b948dc 100644 +--- a/service/attestation/attestation-agent/attester/src/virtcca/mod.rs ++++ b/service/attestation/attestation-agent/attester/src/virtcca/mod.rs +@@ -15,18 +15,17 @@ + //! Call the hardware sdk or driver to get the specific evidence + + use anyhow::{bail, Result}; +-use attestation_types::{UefiLog, VirtccaEvidence}; ++use attestation_types::VirtccaEvidence; + use log; + use std::path::Path; + + use self::virtcca::{get_attestation_token, get_dev_cert, tsi_new_ctx}; ++use crate::ima; + use crate::virtcca::virtcca::tsi_free_ctx; + use crate::EvidenceRequest; +-use crate::ima; + + mod virtcca; + +-const CCEL_TABLE_PATH: &str = "/sys/firmware/acpi/tables/CCEL"; + const CCEL_DATA_PATH: &str = "/sys/firmware/acpi/tables/data/CCEL"; + + #[derive(Debug, Default)] +@@ -49,7 +48,7 @@ const MAX_CHALLENGE_LEN: usize = 64; + fn virtcca_get_token(user_data: EvidenceRequest) -> Result { + let mut challenge = base64_url::decode(&user_data.challenge)?; + let len = challenge.len(); +- if len <= 0 || len > MAX_CHALLENGE_LEN { ++ if len == 0 || len > MAX_CHALLENGE_LEN { + log::error!( + "challenge len is error, expecting 0 < len <= {}, got {}", + MAX_CHALLENGE_LEN, +@@ -97,27 +96,22 @@ fn virtcca_get_token(user_data: EvidenceRequest) -> Result { + // Use the new IMA module to read IMA log + let ima_log = ima::read_ima_log_if_requested(with_ima)?; + +- let ccel_table = std::fs::read(CCEL_TABLE_PATH).ok(); +- let ccel_data = std::fs::read(CCEL_DATA_PATH).ok(); +- let uefi_log = match (ccel_table, ccel_data) { +- (Some(table), Some(data)) => { +- log::info!("read ccel table and data success"); +- Some(UefiLog { +- ccel_table: table, +- ccel_data: data, +- }) ++ let event_log = match std::fs::read(CCEL_DATA_PATH) { ++ Ok(data) => { ++ log::info!("read ccel data success"); ++ Some(data) + } +- _ => { +- log::warn!("read ccel table or data failed"); ++ Err(e) => { ++ log::warn!("read ccel data failed: {}", e); + None + } + }; + + let evidence = VirtccaEvidence { + evidence: token, +- dev_cert: dev_cert, +- ima_log: ima_log, +- uefi_log: uefi_log, ++ dev_cert, ++ ima_log, ++ event_log, + }; + + let _ = tsi_free_ctx(ctx); +diff --git a/service/attestation/attestation-service/policy/Cargo.toml b/service/attestation/attestation-service/policy/Cargo.toml +index acf961f..43ad2bf 100644 +--- a/service/attestation/attestation-service/policy/Cargo.toml ++++ b/service/attestation/attestation-service/policy/Cargo.toml +@@ -11,3 +11,4 @@ base64.workspace = true + tokio.workspace = true + futures.workspace = true + async-trait.workspace = true ++log.workspace = true +diff --git a/service/attestation/attestation-service/policy/src/opa/default_itrustee.rego b/service/attestation/attestation-service/policy/src/opa/default_itrustee.rego +index f55449c..966f405 100644 +--- a/service/attestation/attestation-service/policy/src/opa/default_itrustee.rego ++++ b/service/attestation/attestation-service/policy/src/opa/default_itrustee.rego +@@ -1,10 +1,22 @@ +-# if create a new rego file, "output" should exist, +-# package name should be "attestation" + package attestation +-import rego.v1 +-expect_keys := ["itrustee.ta_img", "itrustee.ta_mem"] +-input_keys := object.keys(input) +-output[exist] := input[exist] if { +- some exist in expect_keys +- exist in input_keys +-} ++import future.keywords.if ++ ++allow := true if { ++ input["itrustee.ta_img"] != null ++ input["itrustee.ta_mem"] != null ++} else := false ++ ++ta_img := input["itrustee.ta_img"] if { ++ input["itrustee.ta_img"] != null ++} else := null ++ ++ta_mem := input["itrustee.ta_mem"] if { ++ input["itrustee.ta_mem"] != null ++} else := null ++ ++#output and output.allow must exist ++output := { ++ "allow": allow, ++ "itrustee.ta_img": ta_img, ++ "itrustee.ta_mem": ta_mem ++} +\ No newline at end of file +diff --git a/service/attestation/attestation-service/policy/src/opa/default_vcca.rego b/service/attestation/attestation-service/policy/src/opa/default_vcca.rego +index 32229e4..d526056 100644 +--- a/service/attestation/attestation-service/policy/src/opa/default_vcca.rego ++++ b/service/attestation/attestation-service/policy/src/opa/default_vcca.rego +@@ -1,10 +1,47 @@ +-# if create a new rego file, "output" should exist, + # package name should be "attestation" + package attestation +-import rego.v1 +-expect_keys := ["vcca.cvm.rim"] +-input_keys := object.keys(input) +-output[exist] := input[exist] if { +- some exist in expect_keys +- exist in input_keys ++import future.keywords.if ++ ++ ++#---------------Platform SW Verify Start--------------- ++ ++# 自定义policy验证platform sw时,请设置platform_sw_measure_value ++# platform_sw_measure_value数据摘取software_components.json的measure_value字段 ++# software_components.json是platform_token底层组件json文件,随BIOS发行版本带入 ++# hash_algorithm字段不验证 ++platform_sw_measure_value := [ ++ { ++ "firware_name": "firmware_name", ++ "measurement": "measurement", ++ "firmware_version": "firmware_version", ++ }, ++ { ++ "firware_name": "firmware_name", ++ "measurement": "measurement", ++ "firmware_version": "firmware_version", ++ } ++] ++ ++# 自定义policy验证platform sw时,请取消下方三行代码注释 ++# platform_sw_verify := true if { ++# input["vcca.is_platform"] == false ++# } else := input["vcca.platform.measure_value"] == platform_sw_measure_value ++ ++# 默认策略不验证platform sw;自定义policy验证platform sw时,请注释下一行代码 ++platform_sw_verify := "Not Verified" ++ ++#---------------Platform SW Verify End--------------- ++ ++allow := true if { ++ input["vcca.cvm.rim"] != null ++} else := false ++ ++rim := input["vcca.cvm.rim"] ++ ++# output and output.allow must exist ++# 如果不需要验证platform sw,则删除字段"vcca.platform.verify" ++output := { ++ "allow": allow, ++ "vcca.platform.verify": platform_sw_verify, ++ "vcca.cvm.rim": rim, + } +diff --git a/service/attestation/attestation-service/policy/src/opa/mod.rs b/service/attestation/attestation-service/policy/src/opa/mod.rs +index 7ce7f86..433160b 100644 +--- a/service/attestation/attestation-service/policy/src/opa/mod.rs ++++ b/service/attestation/attestation-service/policy/src/opa/mod.rs +@@ -13,7 +13,7 @@ use base64::Engine; + use policy_engine::{PolicyEngine, PolicyEngineError}; + use regorus::Value; + use std::{collections::HashMap, path::PathBuf}; +- ++use log; + use crate::policy_engine; + + #[derive(Debug, Clone, PartialEq)] +@@ -73,6 +73,7 @@ impl PolicyEngine for OPA { + let input = Value::from_json_str(refs).map_err(|err| { + PolicyEngineError::InvalidReport(format!("report to Value failed: {}", err)) + })?; ++ log::debug!("input: {}", input); + engine.set_input(input); + + if !data_for_policy.is_empty() { +diff --git a/service/attestation/attestation-service/reference/src/reference/mod.rs b/service/attestation/attestation-service/reference/src/reference/mod.rs +index b794de4..dc8c534 100644 +--- a/service/attestation/attestation-service/reference/src/reference/mod.rs ++++ b/service/attestation/attestation-service/reference/src/reference/mod.rs +@@ -15,13 +15,13 @@ use crate::store::{KvError, KvStore}; + use openssl::sha::sha256; + use serde::{Deserialize, Serialize}; + use serde_json::{json, Value}; +-use thiserror::{self, Error}; + use std::fs::File; +-use std::path::Path; + use std::io::Write; ++use std::path::Path; ++use thiserror::{self, Error}; + +-const ITRUSTEE_REF_VALUE_DIR: &str = +- "/etc/attestation/attestation-service/reference-itrustee/"; ++const ITRUSTEE_REF_VALUE_DIR: &str = "/etc/attestation/attestation-service/reference-itrustee/"; ++const VERIFY_BY_POLICY: [&str; 2] = ["vcca.is_platform", "vcca.platform.measure_value"]; + + pub struct ReferenceOps { + store: Box, +@@ -101,10 +101,17 @@ impl ReferenceOps { + if item.name.starts_with("itrustee_") { + let file_name = ITRUSTEE_REF_VALUE_DIR.to_string() + item.name.as_str(); + let path = Path::new(file_name.as_str()); +- let mut file = File::create(path) +- .map_err(|_|RefOpError::Err("create itrustee reference file failed: ".to_string() + file_name.as_str()))?; ++ let mut file = File::create(path).map_err(|_| { ++ RefOpError::Err( ++ "create itrustee reference file failed: ".to_string() + file_name.as_str(), ++ ) ++ })?; + file.write_all(&item.value.as_str().unwrap().as_bytes()) +- .map_err(|_|RefOpError::Err("write itrustee reference file failed".to_string() + file_name.as_str()))?; ++ .map_err(|_| { ++ RefOpError::Err( ++ "write itrustee reference file failed".to_string() + file_name.as_str(), ++ ) ++ })?; + } + } + Ok(()) +@@ -123,6 +130,10 @@ impl ReferenceOps { + let refs = Extractor::split(ref_set)?; + let mut ret: Value = json!({}); + for item in refs { ++ if VERIFY_BY_POLICY.contains(&item.name.as_str()) { ++ ret.as_object_mut().unwrap().insert(item.name, item.value); ++ continue; ++ } + // query each reference, reference is set to NULL if not found + match self.query_reference(&item) { + Some(ref_store) => { +diff --git a/service/attestation/attestation-service/service/src/lib.rs b/service/attestation/attestation-service/service/src/lib.rs +index 78e9491..c6feeba 100644 +--- a/service/attestation/attestation-service/service/src/lib.rs ++++ b/service/attestation/attestation-service/service/src/lib.rs +@@ -143,11 +143,12 @@ impl AttestationService { + + let mut passed = true; + AttestationService::evaluate_evidence_field(&claims_evidence, "ima", &mut passed).await; +- AttestationService::evaluate_evidence_field(&claims_evidence, "uefi", &mut passed).await; ++ AttestationService::evaluate_evidence_field(&claims_evidence, "event", &mut passed).await; + + // get reference by keys in claims_evidence + let mut ops_refs = ReferenceOps::default(); + let refs_of_claims = ops_refs.query(&claims_evidence["payload"].to_string()); ++ log::debug!("refs_of_claims: {:?}", refs_of_claims); + // apply policy to verify claims_evidence with reference value + let policy_ids = match policy_ids { + Some(policy_id) => policy_id.clone(), +@@ -169,7 +170,7 @@ impl AttestationService { + ) + .await; + let mut report = serde_json::json!({}); +- let mut ref_exist_null: bool = false; ++ let mut ref_verify: bool = true; + + match result { + Ok(eval) => { +@@ -187,8 +188,8 @@ impl AttestationService { + }; + for key in refs.keys() { + // reference value is null means not found +- if refs[key].is_null() { +- ref_exist_null = true; ++ if refs[key].is_null() || refs[key] == Value::Bool(false) { ++ ref_verify = false; + } + } + report +@@ -208,11 +209,11 @@ impl AttestationService { + .unwrap() + .insert("ima".to_string(), claims_evidence["ima"].clone()); + +- // add uefi detail result to report ++ // add event detail result to report + report + .as_object_mut() + .unwrap() +- .insert("uefi".to_string(), claims_evidence["uefi"].clone()); ++ .insert("event".to_string(), claims_evidence["event"].clone()); + + // issue attestation result token + let evl_report = EvlReport { +@@ -222,7 +223,7 @@ impl AttestationService { + .ok_or(anyhow!("tee type unknown"))?, + ), + result: EvlResult { +- eval_result: passed & !ref_exist_null, ++ eval_result: passed & ref_verify, + policy: policy_ids, + report: report, + }, +diff --git a/service/attestation/attestation-service/verifier/src/virtcca/ccatoken.rs b/service/attestation/attestation-service/verifier/src/virtcca/ccatoken.rs +new file mode 100644 +index 0000000..6aa252c +--- /dev/null ++++ b/service/attestation/attestation-service/verifier/src/virtcca/ccatoken.rs +@@ -0,0 +1,439 @@ ++use anyhow::{bail, Result}; ++use ciborium; ++use ciborium::Value; ++use hex; ++use log; ++use serde::{ser::SerializeSeq, Serialize, Serializer}; ++ ++/// cvm token相关标签定义 ++mod cvm_labels { ++ pub const CHALLENGE: i128 = 10; ++ pub const RPV: i128 = 44235; ++ pub const HASH_ALG: i128 = 44236; ++ pub const PUB_KEY: i128 = 44237; ++ pub const RIM: i128 = 44238; ++ pub const REM: i128 = 44239; ++ pub const PUB_KEY_HASH_ALG: i128 = 44240; ++} ++ ++/// platform token相关标签定义 ++mod platform_labels { ++ pub const PROFILE: i128 = 265; ++ pub const CHALLENGE: i128 = 10; ++ pub const IMPLEMENTATION: i128 = 2396; ++ pub const INSTANCE: i128 = 256; ++ pub const CONFIG: i128 = 2401; ++ pub const LIFESTYLE: i128 = 2395; ++ pub const SW_COMPONENTS: i128 = 2399; ++ pub const VERIFICATION_SERVICE: i128 = 2400; ++ pub const HASH_ALGO: i128 = 2402; ++ ++ /// 软件组件子标签定义 ++ pub mod sw_component { ++ pub const TYPE: i128 = 1; ++ pub const MEASUREMENT_VALUE: i128 = 2; ++ pub const VERSION: i128 = 4; ++ pub const SIGNER_ID: i128 = 5; ++ pub const ALGORITHM_ID: i128 = 6; ++ } ++} ++ ++/// cvm 数据大小定义 ++mod cvm_sizes { ++ pub const CHALLENGE: usize = 64; ++ pub const RPV: usize = 64; ++ pub const REM_ARR: usize = 4; ++ pub const PUB_KEY_ECC: usize = 133; ++ pub const PUB_KEY_RSA: usize = 550; ++} ++ ++/// platform 数据大小定义 ++mod platform_sizes { ++ pub const SW_MEASUREMENT: usize = 32; ++ pub const CHALLENGE: usize = 32; ++ pub const IMPLEMENTATION: usize = 32; ++ pub const INSTANCE: usize = 33; ++ pub const SW_COUNTS: usize = 6; ++} ++ ++fn serialize_hex(bytes: &[u8], s: S) -> Result ++where ++ S: Serializer, ++{ ++ let hex_string = hex::encode(bytes); ++ s.serialize_str(&hex_string) ++} ++ ++fn serialize_rem(rem: &[Vec; cvm_sizes::REM_ARR], serializer: S) -> Result ++where ++ S: Serializer, ++{ ++ let mut seq = serializer.serialize_seq(Some(rem.len()))?; ++ for (index, vec) in rem.iter().enumerate() { ++ let s = format!("rem{}: {}", index, hex::encode(vec)); ++ seq.serialize_element(&s)?; ++ } ++ seq.end() ++} ++ ++#[derive(Debug, Serialize)] ++pub struct CvmToken { ++ #[serde(serialize_with = "serialize_hex")] ++ pub challenge: [u8; cvm_sizes::CHALLENGE], // 10 => bytes .size 64 ++ #[serde(serialize_with = "serialize_hex")] ++ pub rpv: [u8; cvm_sizes::RPV], // 44235 => bytes .size 64 ++ #[serde(serialize_with = "serialize_hex")] ++ pub rim: Vec, // 44238 => bytes .size {32,48,64} ++ #[serde(serialize_with = "serialize_rem")] ++ pub rem: [Vec; cvm_sizes::REM_ARR], // 44239 => [ 4*4 bytes .size {32,48,64} ] ++ pub hash_alg: String, // 44236 => text ++ #[serde(serialize_with = "serialize_hex")] ++ pub pub_key: Vec, // 44237 => bytes .size {133,550} ++ pub pub_key_hash_alg: String, // 44240 => text ++} ++ ++#[derive(Debug, Serialize, Clone)] ++pub struct SwComp { ++ pub firware_name: String, ++ #[serde(serialize_with = "serialize_hex")] ++ pub measurement: [u8; platform_sizes::SW_MEASUREMENT], ++ pub firware_version: String, ++ #[serde(skip_serializing)] ++ pub signer: Vec, ++ #[serde(skip_serializing)] ++ pub hash_algorithm: String, ++} ++ ++#[derive(Debug, Serialize)] ++pub struct PlatformToken { ++ pub profile: String, ++ #[serde(serialize_with = "serialize_hex")] ++ pub challenge: [u8; platform_sizes::CHALLENGE], ++ #[serde(serialize_with = "serialize_hex")] ++ pub implementation: [u8; platform_sizes::IMPLEMENTATION], ++ #[serde(serialize_with = "serialize_hex")] ++ pub instance: [u8; platform_sizes::INSTANCE], ++ #[serde(serialize_with = "serialize_hex")] ++ pub config: Vec, ++ pub lifecycle: i128, ++ pub sw_components: Vec, ++ pub sw_comp_cnts: i128, ++ pub verification_service: String, ++ pub hash_algo: String, ++} ++ ++impl Default for CvmToken { ++ fn default() -> Self { ++ Self::new() ++ } ++} ++ ++impl Default for SwComp { ++ fn default() -> Self { ++ Self::new() ++ } ++} ++ ++impl Default for PlatformToken { ++ fn default() -> Self { ++ Self::new() ++ } ++} ++ ++#[derive(Debug, Default)] ++pub struct Decode {} ++ ++impl Decode { ++ pub fn get_vec(v: &Value, param: &str, size: Vec) -> Result, anyhow::Error> { ++ let tmp = v ++ .as_bytes() ++ .ok_or_else(|| anyhow::anyhow!("{} is none", param))? ++ .clone(); ++ if size.is_empty() { ++ return Ok(tmp); ++ } ++ if !size.contains(&tmp.len()) { ++ bail!("{} expecting {:?} bytes, got {}", param, size, tmp.len()); ++ } ++ ++ Ok(tmp) ++ } ++ ++ pub fn get_string(v: &Value, param: &str) -> Result { ++ let tmp = v ++ .as_text() ++ .ok_or_else(|| anyhow::anyhow!(" {} must be str", param))? ++ .trim_end_matches('\u{0}') ++ .to_string(); ++ log::debug!("get_string: {}", tmp); ++ Ok(tmp) ++ } ++ ++ pub fn get_num(v: &Value, param: &str) -> Result { ++ let tmp = v ++ .as_integer() ++ .ok_or_else(|| anyhow::anyhow!(" {} must be num", param))? ++ .into(); ++ Ok(tmp) ++ } ++ ++ fn get_array(v: &Value, param: &str, array_size: usize) -> Result, anyhow::Error> { ++ let tmp = v ++ .as_array() ++ .ok_or_else(|| anyhow::anyhow!(" {} must be array", param))? ++ .clone(); ++ if tmp.len() != array_size { ++ bail!("{} expecting size {}, got {}", param, array_size, tmp.len()); ++ } ++ Ok(tmp) ++ } ++} ++ ++impl CvmToken { ++ pub fn new() -> Self { ++ Self { ++ challenge: [0; cvm_sizes::CHALLENGE], ++ rpv: [0; cvm_sizes::RPV], ++ rim: Vec::new(), ++ rem: Default::default(), ++ hash_alg: String::from(""), ++ pub_key: Vec::new(), ++ pub_key_hash_alg: String::from(""), ++ } ++ } ++ pub fn decode(raw_payload: &Vec) -> Result { ++ let payload: Vec = ciborium::de::from_reader(raw_payload.as_slice())?; ++ log::debug!("After decode CBOR payload, payload {:?}", payload); ++ let payload: Value = ciborium::de::from_reader(payload.as_slice())?; ++ log::debug!("After decode CBOR payload agin, payload {:?}", payload); ++ let mut cvm_token: CvmToken = CvmToken::new(); ++ if let Value::Map(contents) = payload { ++ for (k, v) in contents.iter() { ++ if let Value::Integer(i) = k { ++ match (*i).into() { ++ cvm_labels::CHALLENGE => cvm_token.set_challenge(v)?, ++ cvm_labels::RPV => cvm_token.set_rpv(v)?, ++ cvm_labels::RIM => cvm_token.set_rim(v)?, ++ cvm_labels::REM => cvm_token.set_rem(v)?, ++ cvm_labels::HASH_ALG => cvm_token.set_hash_alg(v)?, ++ cvm_labels::PUB_KEY => cvm_token.set_pub_key(v)?, ++ cvm_labels::PUB_KEY_HASH_ALG => cvm_token.set_pub_key_hash_alg(v)?, ++ err => bail!("cvm payload unknown label {}", err), ++ } ++ } else { ++ bail!("cvm payload expecting integer key"); ++ } ++ } ++ } else { ++ bail!("expecting cvm payload map type"); ++ } ++ log::debug!("cvm_token decode from raw payload, {:?}", cvm_token); ++ Ok(cvm_token) ++ } ++ fn set_challenge(&mut self, v: &Value) -> Result<()> { ++ let tmp = Decode::get_vec(v, "cvm challenge", vec![cvm_sizes::CHALLENGE])?; ++ self.challenge[..].clone_from_slice(&tmp); ++ Ok(()) ++ } ++ fn set_rpv(&mut self, v: &Value) -> Result<()> { ++ let tmp = Decode::get_vec(v, "cvm rpv", vec![cvm_sizes::RPV])?; ++ self.rpv[..].clone_from_slice(&tmp); ++ Ok(()) ++ } ++ ++ fn set_rim(&mut self, v: &Value) -> Result<()> { ++ self.rim = Decode::get_vec(v, "cvm rim", vec![32, 48, 64])?; ++ Ok(()) ++ } ++ fn set_rem(&mut self, v: &Value) -> Result<()> { ++ let tmp = Decode::get_array(v, "cvm rem", cvm_sizes::REM_ARR)?; ++ for (i, val) in tmp.iter().enumerate() { ++ self.rem[i] = Decode::get_vec(val, "cvm rem[{i}]", vec![32, 48, 64])?; ++ } ++ Ok(()) ++ } ++ fn set_hash_alg(&mut self, v: &Value) -> Result<()> { ++ self.hash_alg = Decode::get_string(v, "cvm hash alg")?; ++ Ok(()) ++ } ++ fn set_pub_key(&mut self, v: &Value) -> Result<()> { ++ self.pub_key = Decode::get_vec( ++ v, ++ "cvm pub key", ++ vec![cvm_sizes::PUB_KEY_ECC, cvm_sizes::PUB_KEY_RSA], ++ )?; ++ Ok(()) ++ } ++ fn set_pub_key_hash_alg(&mut self, v: &Value) -> Result<()> { ++ self.pub_key_hash_alg = Decode::get_string(v, "cvm pub key hash alg")?; ++ Ok(()) ++ } ++} ++ ++impl SwComp { ++ pub fn new() -> Self { ++ Self { ++ firware_name: String::from(""), ++ measurement: [0; platform_sizes::SW_MEASUREMENT], ++ firware_version: String::from(""), ++ signer: Vec::new(), ++ hash_algorithm: String::from(""), ++ } ++ } ++ ++ pub fn decode(v: &Value) -> Result { ++ let mut sw_comp: SwComp = SwComp::new(); ++ if let Value::Map(contents) = v { ++ for (k, v) in contents.iter() { ++ if let Value::Integer(i) = k { ++ match (*i).into() { ++ platform_labels::sw_component::TYPE => sw_comp.set_type(v)?, ++ platform_labels::sw_component::VERSION => sw_comp.set_version(v)?, ++ platform_labels::sw_component::MEASUREMENT_VALUE => { ++ sw_comp.set_measurement(v)? ++ } ++ platform_labels::sw_component::SIGNER_ID => sw_comp.set_signer(v)?, ++ platform_labels::sw_component::ALGORITHM_ID => sw_comp.set_algo(v)?, ++ err => bail!("sw_comp payload unknown label {}", err), ++ } ++ } else { ++ bail!("Un-supported label type"); ++ } ++ } ++ } else { ++ bail!("expecting sw_comp payload map type"); ++ } ++ log::debug!("sw_comp decode from raw payload, {:?}", sw_comp); ++ Ok(sw_comp) ++ } ++ ++ fn set_measurement(&mut self, v: &Value) -> Result<()> { ++ let tmp = Decode::get_vec(v, "measurement", vec![platform_sizes::SW_MEASUREMENT])?; ++ self.measurement[..].clone_from_slice(&tmp); ++ Ok(()) ++ } ++ ++ fn set_signer(&mut self, v: &Value) -> Result<()> { ++ let tmp = Decode::get_vec(v, "signer", vec![])?; ++ self.signer[..].clone_from_slice(&tmp); ++ Ok(()) ++ } ++ ++ fn set_type(&mut self, v: &Value) -> Result<()> { ++ self.firware_name = Decode::get_string(v, "component_type")?; ++ Ok(()) ++ } ++ ++ fn set_version(&mut self, v: &Value) -> Result<()> { ++ self.firware_version = Decode::get_string(v, "version")?; ++ Ok(()) ++ } ++ ++ fn set_algo(&mut self, v: &Value) -> Result<()> { ++ self.hash_algorithm = Decode::get_string(v, "hash_algo")?; ++ Ok(()) ++ } ++} ++ ++impl PlatformToken { ++ pub fn new() -> Self { ++ Self { ++ profile: String::from(""), ++ challenge: [0; platform_sizes::CHALLENGE], ++ implementation: [0; platform_sizes::IMPLEMENTATION], ++ instance: [0; platform_sizes::INSTANCE], ++ config: Vec::new(), ++ lifecycle: 0, ++ sw_components: Vec::new(), ++ sw_comp_cnts: 0, ++ verification_service: String::from(""), ++ hash_algo: String::from(""), ++ } ++ } ++ pub fn decode(raw_payload: &Vec) -> Result { ++ let payload: Value = ciborium::de::from_reader(raw_payload.as_slice())?; ++ log::debug!("After decode CBOR payload agin, payload {:?}", payload); ++ let mut platform_token: PlatformToken = PlatformToken::new(); ++ if let Value::Map(contents) = payload { ++ for (k, v) in contents.iter() { ++ if let Value::Integer(i) = k { ++ match (*i).into() { ++ platform_labels::PROFILE => platform_token.set_profile(v)?, ++ platform_labels::CHALLENGE => platform_token.set_challenge(v)?, ++ platform_labels::IMPLEMENTATION => platform_token.set_implementation(v)?, ++ platform_labels::INSTANCE => platform_token.set_instance(v)?, ++ platform_labels::CONFIG => platform_token.set_config(v)?, ++ platform_labels::LIFESTYLE => platform_token.set_lifecycle(v)?, ++ platform_labels::SW_COMPONENTS => platform_token.set_sw_components(v)?, ++ platform_labels::VERIFICATION_SERVICE => { ++ platform_token.set_verification_service(v)? ++ } ++ platform_labels::HASH_ALGO => platform_token.set_hash_algo(v)?, ++ err => bail!("platform_token payload unknown label {}", err), ++ } ++ } else { ++ bail!("platform_token payload expecting integer key"); ++ } ++ } ++ } else { ++ bail!("expecting platform_token payload map type"); ++ } ++ log::debug!( ++ "platform_token decode from raw payload, {:?}", ++ platform_token ++ ); ++ Ok(platform_token) ++ } ++ ++ fn set_config(&mut self, v: &Value) -> Result<()> { ++ let tmp = Decode::get_vec(v, "config", vec![])?; ++ self.config[..].clone_from_slice(&tmp); ++ Ok(()) ++ } ++ fn set_challenge(&mut self, v: &Value) -> Result<()> { ++ let tmp = Decode::get_vec(v, "challenge", vec![platform_sizes::CHALLENGE])?; ++ self.challenge[..].clone_from_slice(&tmp); ++ Ok(()) ++ } ++ ++ fn set_implementation(&mut self, v: &Value) -> Result<()> { ++ let tmp = Decode::get_vec(v, "implementation id", vec![platform_sizes::IMPLEMENTATION])?; ++ self.implementation[..].clone_from_slice(&tmp); ++ Ok(()) ++ } ++ ++ fn set_instance(&mut self, v: &Value) -> Result<()> { ++ let tmp = Decode::get_vec(v, "instance id", vec![platform_sizes::INSTANCE])?; ++ self.instance[..].clone_from_slice(&tmp); ++ Ok(()) ++ } ++ ++ fn set_lifecycle(&mut self, v: &Value) -> Result<()> { ++ self.lifecycle = Decode::get_num(v, "lifecycle")?; ++ Ok(()) ++ } ++ ++ fn set_verification_service(&mut self, v: &Value) -> Result<()> { ++ self.verification_service = Decode::get_string(v, "verification_service")?; ++ Ok(()) ++ } ++ ++ fn set_hash_algo(&mut self, v: &Value) -> Result<()> { ++ self.hash_algo = Decode::get_string(v, "cvm token")?; ++ Ok(()) ++ } ++ ++ fn set_profile(&mut self, v: &Value) -> Result<()> { ++ self.profile = Decode::get_string(v, "profile")?; ++ Ok(()) ++ } ++ ++ fn set_sw_components(&mut self, v: &Value) -> Result<()> { ++ let tmp = Decode::get_array(v, "sw_components", platform_sizes::SW_COUNTS)?; ++ for value in tmp { ++ let sw_comp = SwComp::decode(&value)?; ++ self.sw_components.push(sw_comp); ++ } ++ Ok(()) ++ } ++} +diff --git a/service/attestation/attestation-service/verifier/src/virtcca/uefi.rs b/service/attestation/attestation-service/verifier/src/virtcca/event_log.rs +similarity index 64% +rename from service/attestation/attestation-service/verifier/src/virtcca/uefi.rs +rename to service/attestation/attestation-service/verifier/src/virtcca/event_log.rs +index 89d61ec..6a70572 100644 +--- a/service/attestation/attestation-service/verifier/src/virtcca/uefi.rs ++++ b/service/attestation/attestation-service/verifier/src/virtcca/event_log.rs +@@ -10,10 +10,8 @@ + * See the Mulan PSL v2 for more details. + */ + +-use super::CVM_REM_ARR_SIZE; + use crate::ima::file_reader; + use anyhow::{bail, Result}; +-use attestation_types::UefiLog; + use eventlog_rs::{self, Eventlog}; + use hex; + use log; +@@ -22,14 +20,15 @@ use std::collections::{HashMap, HashSet}; + use std::convert::TryFrom; + + #[cfg(not(feature = "no_as"))] +-const UEFI_REFERENCE_FILE: &str = +- "/etc/attestation/attestation-service/verifier/virtcca/uefi/digest_list_file"; ++const EVENT_REFERENCE_FILE: &str = ++ "/etc/attestation/attestation-service/verifier/virtcca/event/digest_list_file"; + +-// attestation agent local uefi reference ++// attestation agent local event reference + #[cfg(feature = "no_as")] +-const UEFI_REFERENCE_FILE: &str = +- "/etc/attestation/attestation-agent/local_verifier/virtcca/uefi/digest_list_file"; ++const EVENT_REFERENCE_FILE: &str = ++ "/etc/attestation/attestation-agent/local_verifier/virtcca/event/digest_list_file"; + ++const CVM_REM_ARR_SIZE: usize = 4; + #[derive(Debug)] + pub struct FirmwareState { + pub grub_image_count: u8, +@@ -38,12 +37,12 @@ pub struct FirmwareState { + } + + #[derive(Debug, Default)] +-pub struct UefiVerify {} ++pub struct EventVerify {} + +-impl UefiVerify { +- pub fn compare_rtmr_with_uefi_log( ++impl EventVerify { ++ pub fn compare_rtmr_with_event_log( + _replayed_rtmr: &HashMap>, +- uefi_log_hash: &[Vec; CVM_REM_ARR_SIZE], ++ event_log_hash: &[Vec; CVM_REM_ARR_SIZE], + ) -> bool { + for i in 1..CVM_REM_ARR_SIZE as u32 { + let index = i as usize - 1; +@@ -60,36 +59,36 @@ impl UefiVerify { + return false; + } + +- if uefi_log_hash[index].len() != 32 { ++ if event_log_hash[index].len() != 32 { + log::error!( +- "UEFI_LOG_HASH[{}] hash length invalid: {}", ++ "EVENT_LOG_HASH[{}] hash length invalid: {}", + i, +- uefi_log_hash[index].len() ++ event_log_hash[index].len() + ); + return false; + } + + // 如果哈希不匹配,记录详细信息 +- if rtmr_value != &uefi_log_hash[index] { +- log::error!("RTMR[{}] and UEFI_LOG_HASH[{}] do not match.", i, index); ++ if rtmr_value != &event_log_hash[index] { ++ log::error!("RTMR[{}] and EVENT_LOG_HASH[{}] do not match.", i, index); + log::debug!("RTMR[{}] = {}", i, hex::encode(rtmr_value)); + log::debug!( +- "UEFI_LOG_HASH[{}] = {}", ++ "EVENT_LOG_HASH[{}] = {}", + index, +- hex::encode(&uefi_log_hash[index]) ++ hex::encode(&event_log_hash[index]) + ); + return false; + } + + log::debug!("RTMR[{}] = {}", i, hex::encode(rtmr_value)); + log::debug!( +- "UEFI_LOG_HASH[{}] = {}", ++ "EVENT_LOG_HASH[{}] = {}", + index, +- hex::encode(&uefi_log_hash[index]) ++ hex::encode(&event_log_hash[index]) + ); + } + +- log::info!("All RTMR values match UEFI log hashes."); ++ log::info!("All RTMR values match EVENT log hashes."); + true + } + +@@ -147,19 +146,19 @@ impl UefiVerify { + state + } + +- pub fn check_uefi_references( ++ pub fn check_event_references( + firmware_state: &FirmwareState, +- uefi_refs: &HashSet, ++ event_refs: &HashSet, + ) -> serde_json::Value { +- let mut uefi_detail: Map = Map::new(); ++ let mut event_detail: Map = Map::new(); + + for (index, image) in firmware_state.grub_image_list.iter().enumerate() { +- let exists = uefi_refs.contains(image); ++ let exists = event_refs.contains(image); + let key = format!("Image[{}]", index); +- uefi_detail.insert(key, Value::Bool(exists)); ++ event_detail.insert(key, Value::Bool(exists)); + if !exists { + log::debug!( +- "GRUB Image[{}] ('{}') not found in UEFI reference set.", ++ "GRUB Image[{}] ('{}') not found in EVENT reference set.", + index, + image + ); +@@ -167,44 +166,43 @@ impl UefiVerify { + } + + for (key, value) in firmware_state.state_hash.iter() { +- let exists = uefi_refs.contains(value); +- uefi_detail.insert(key.clone(), Value::Bool(exists)); ++ let exists = event_refs.contains(value); ++ event_detail.insert(key.clone(), Value::Bool(exists)); + if !exists { +- log::debug!("'{}' : '{}' not found in UEFI reference set.", key, value); ++ log::debug!("'{}' : '{}' not found in EVENT reference set.", key, value); + } + } +- let js_uefi_detail: Value = uefi_detail.into(); +- log::debug!("uefi event verify detail result: {:?}", js_uefi_detail); +- log::info!("uefi event verify Finished"); +- js_uefi_detail ++ let js_event_detail: Value = event_detail.into(); ++ log::debug!("event event verify detail result: {:?}", js_event_detail); ++ log::info!("event event verify Finished"); ++ js_event_detail + } + +- pub fn uefi_verify( +- &self, +- uefi_log: UefiLog, +- uefi_log_hash: [Vec; CVM_REM_ARR_SIZE], ++ pub fn event_verify( ++ event_log: Vec, ++ event_log_hash: [Vec; CVM_REM_ARR_SIZE], + ) -> Result { +- if uefi_log.ccel_data.is_empty() { ++ if event_log.is_empty() { + return Ok(json!({})); + } + +- let event_log = eventlog_rs::Eventlog::try_from(uefi_log.ccel_data).unwrap(); +- let _replayed_rtmr = event_log.replay_measurement_registry(); ++ let event_parsed = eventlog_rs::Eventlog::try_from(event_log).unwrap(); ++ let _replayed_rtmr = event_parsed.replay_measurement_registry(); + +- if !UefiVerify::compare_rtmr_with_uefi_log(&_replayed_rtmr, &uefi_log_hash) { +- log::error!("uefi log hash verify failed"); +- bail!("uefi log hash verify failed"); ++ if !EventVerify::compare_rtmr_with_event_log(&_replayed_rtmr, &event_log_hash) { ++ log::error!("event log hash verify failed"); ++ bail!("event log hash verify failed"); + } + +- let uefi_refs = file_reader(UEFI_REFERENCE_FILE)?; +- log::debug!("uefi reference file: {:?}", uefi_refs); ++ let event_refs = file_reader(EVENT_REFERENCE_FILE)?; ++ log::debug!("event reference file: {:?}", event_refs); + +- let firmware_state: FirmwareState = UefiVerify::firmware_log_state(&event_log); ++ let firmware_state: FirmwareState = EventVerify::firmware_log_state(&event_parsed); + log::debug!("firmware state: {:?}", firmware_state); + +- Ok(UefiVerify::check_uefi_references( ++ Ok(EventVerify::check_event_references( + &firmware_state, +- &uefi_refs, ++ &event_refs, + )) + } + } +diff --git a/service/attestation/attestation-service/verifier/src/virtcca/mod.rs b/service/attestation/attestation-service/verifier/src/virtcca/mod.rs +index 9d152e9..147fc29 100644 +--- a/service/attestation/attestation-service/verifier/src/virtcca/mod.rs ++++ b/service/attestation/attestation-service/verifier/src/virtcca/mod.rs +@@ -11,46 +11,68 @@ + */ + + //! virtcca verifier plugin +-use super::TeeClaim; +-use crate::ima::ImaVerifier; ++pub mod ccatoken; ++pub mod event_log; ++use attestation_types::VirtccaEvidence; ++use ccatoken::{CvmToken, Decode, PlatformToken}; ++use event_log::EventVerify; + +-use anyhow::{anyhow, bail, Result}; +-use ciborium; +-use ciborium::Value; +-use cose::keys::CoseKey; +-use cose::message::CoseMessage; ++use super::TeeClaim; ++use crate::ima::{virtcca::VirtCCAImaVerify, ImaVerifier}; ++use anyhow::{anyhow, bail, Ok, Result}; ++use ciborium::{de, Value}; ++use cose::{ ++ keys::{CoseKey, EC2, KEY_OPS_VERIFY, RSA}, ++ message::CoseMessage, ++}; + use log; +-use openssl::pkey::PKey; +-use openssl::pkey::Public; +-use openssl::rsa; +-use openssl::x509; ++use openssl::{ ++ bn::BigNumContext, ++ ec::{EcGroup, EcKey, PointConversionForm}, ++ hash::{hash, MessageDigest}, ++ nid::Nid, ++ pkey::{Id, PKey, Public}, ++ x509::X509, ++}; + use serde_json::json; + +-pub use attestation_types::{UefiLog, VirtccaEvidence}; +-pub mod uefi; +- + #[cfg(not(feature = "no_as"))] +-const VIRTCCA_ROOT_CERT: &str = ++const RSA_ROOT_CERT: &str = + "/etc/attestation/attestation-service/verifier/virtcca/Huawei Equipment Root CA.pem"; + #[cfg(not(feature = "no_as"))] +-const VIRTCCA_SUB_CERT: &str = ++const RSA_SUB_CERT: &str = + "/etc/attestation/attestation-service/verifier/virtcca/Huawei IT Product CA.pem"; ++const ECCP_ROOT_CERT: &str = ++ "/etc/attestation/attestation-service/verifier/virtcca/eccp521_root_cert.pem"; ++#[cfg(not(feature = "no_as"))] ++const ECCP_SUB_CERT: &str = ++ "/etc/attestation/attestation-service/verifier/virtcca/eccp521_sub_cert.pem"; + + // attestation agent local reference + #[cfg(feature = "no_as")] + const VIRTCCA_REF_VALUE_FILE: &str = + "/etc/attestation/attestation-agent/local_verifier/virtcca/ref_value.json"; + #[cfg(feature = "no_as")] +-const VIRTCCA_ROOT_CERT: &str = ++const RSA_ROOT_CERT: &str = + "/etc/attestation/attestation-agent/local_verifier/virtcca/Huawei Equipment Root CA.pem"; + #[cfg(feature = "no_as")] +-const VIRTCCA_SUB_CERT: &str = ++const RSA_SUB_CERT: &str = + "/etc/attestation/attestation-agent/local_verifier/virtcca/Huawei IT Product CA.pem"; ++#[cfg(feature = "no_as")] ++const ECCP_ROOT_CERT: &str = ++ "/etc/attestation/attestation-agent/local_verifier/virtcca/eccp521_root_cert.pem"; ++#[cfg(feature = "no_as")] ++const ECCP_SUB_CERT: &str = ++ "/etc/attestation/attestation-agent/local_verifier/virtcca/eccp521_sub_cert.pem"; ++ ++const MAX_CHALLENGE_LEN: usize = 64; ++const CBOR_TAG: u64 = 399; ++const CVM_LABEL: i128 = 44241; ++const PLATFORM_LABEL: i128 = 44234; + + #[derive(Debug, Default)] + pub struct VirtCCAVerifier {} + +-const MAX_CHALLENGE_LEN: usize = 64; + impl VirtCCAVerifier { + pub async fn evaluate(&self, user_data: &[u8], evidence: &[u8]) -> Result { + let challenge = base64_url::decode(user_data)?; +@@ -71,38 +93,20 @@ impl VirtCCAVerifier { + } + } + +-const CBOR_TAG: u64 = 399; +-const CVM_LABEL: i128 = 44241; +- +-const CVM_CHALLENGE_LABEL: i128 = 10; +-const CVM_RPV_LABEL: i128 = 44235; +-const CVM_RIM_LABEL: i128 = 44238; +-const CVM_REM_LABEL: i128 = 44239; +-const CVM_HASH_ALG_LABEL: i128 = 44236; +-const CVM_PUB_KEY_LABEL: i128 = 44237; +-const CVM_PUB_KEY_HASH_ALG_LABEL: i128 = 44240; +- +-const CVM_CHALLENGE_SIZE: usize = 64; +-const CVM_RPV_SIZE: usize = 64; +-const CVM_REM_ARR_SIZE: usize = 4; +-const CVM_PUB_KEY_SIZE: usize = 550; +- +-#[derive(Debug)] +-pub struct CvmToken { +- pub challenge: [u8; CVM_CHALLENGE_SIZE], // 10 => bytes .size 64 +- pub rpv: [u8; CVM_RPV_SIZE], // 44235 => bytes .size 64 +- pub rim: Vec, // 44238 => bytes .size {32,48,64} +- pub rem: [Vec; CVM_REM_ARR_SIZE], // 44239 => [ 4*4 bytes .size {32,48,64} ] +- pub hash_alg: String, // 44236 => text +- pub pub_key: [u8; CVM_PUB_KEY_SIZE], // 44237 => bytes .size 550 +- pub pub_key_hash_alg: String, // 44240 => text +-} +- + pub struct Evidence { +- /// COSE Sign1 envelope for cvm_token ++ // COSE Sign1 envelope for cvm_token + pub cvm_envelop: CoseMessage, +- /// Decoded cvm token ++ // Decoded cvm token + pub cvm_token: CvmToken, ++ pub platform_envelop: CoseMessage, ++ pub platform_token: PlatformToken, ++ pub is_platform: bool, ++} ++ ++impl Default for Evidence { ++ fn default() -> Self { ++ Self::new() ++ } + } + + impl Evidence { +@@ -110,14 +114,22 @@ impl Evidence { + Self { + cvm_envelop: CoseMessage::new_sign(), + cvm_token: CvmToken::new(), ++ platform_envelop: CoseMessage::new_sign(), ++ platform_token: PlatformToken::new(), ++ is_platform: false, + } + } + pub fn verify(user_data: &[u8], evidence: &[u8]) -> Result { + let virtcca_ev: VirtccaEvidence = serde_json::from_slice(evidence)?; + let evidence = virtcca_ev.evidence; +- let dev_cert = virtcca_ev.dev_cert; + let mut evidence = Evidence::decode(evidence)?; + ++ let dev_cert = if evidence.is_platform { ++ X509::from_pem(&virtcca_ev.dev_cert)? ++ } else { ++ X509::from_der(&virtcca_ev.dev_cert)? ++ }; ++ + // verify platform token + evidence.verify_platform_token(&dev_cert)?; + +@@ -136,51 +148,42 @@ impl Evidence { + } + }; + +- let ima: serde_json::Value = crate::ima::virtcca::VirtCCAImaVerify::default() +- .ima_verify(&ima_log, &evidence.cvm_token.rem)?; ++ let ima: serde_json::Value = ++ VirtCCAImaVerify::default().ima_verify(&ima_log, &evidence.cvm_token.rem)?; + +- // verify uefi +- let uefi_log = if let Some(uefi_log) = virtcca_ev.uefi_log { +- if !uefi_log.ccel_table.is_empty() && !uefi_log.ccel_data.is_empty() { +- log::info!("get valid uefi log"); +- uefi_log +- } else { +- log::info!("uefi log is invalid (empty fields)"); +- UefiLog { +- ccel_table: vec![], +- ccel_data: vec![], +- } ++ // verify event ++ let event_log = match virtcca_ev.event_log { ++ Some(log) => { ++ log::info!("get event log"); ++ log + } +- } else { +- log::info!("no uefi log at all"); +- UefiLog { +- ccel_table: vec![], +- ccel_data: vec![], ++ None => { ++ log::info!("no event log"); ++ vec![] + } + }; + +- let uefi: serde_json::Value = +- uefi::UefiVerify::default().uefi_verify(uefi_log, evidence.cvm_token.rem.clone())?; ++ let event: serde_json::Value = ++ EventVerify::event_verify(event_log, evidence.cvm_token.rem.clone())?; + +- // todo parsed TeeClaim +- evidence.parse_claim_from_evidence(ima, uefi) ++ evidence.parse_claim_from_evidence(ima, event) + } +- + pub fn parse_evidence(evidence: &[u8]) -> Result { + let virtcca_ev: VirtccaEvidence = serde_json::from_slice(evidence)?; + let evidence = virtcca_ev.evidence; + let evidence = Evidence::decode(evidence)?; + + let ima = json!(""); +- let uefi = json!(""); ++ let event = json!(""); + // parsed TeeClaim +- let claim = evidence.parse_claim_from_evidence(ima, uefi).unwrap(); ++ let claim = evidence.parse_claim_from_evidence(ima, event).unwrap(); + Ok(claim["payload"].clone() as TeeClaim) + } ++ + fn parse_claim_from_evidence( + &self, + ima: serde_json::Value, +- uefi: serde_json::Value, ++ event: serde_json::Value, + ) -> Result { + let payload = json!({ + "vcca.cvm.challenge": hex::encode(self.cvm_token.challenge), +@@ -190,31 +193,105 @@ impl Evidence { + "vcca.cvm.rem.1": hex::encode(self.cvm_token.rem[1].clone()), + "vcca.cvm.rem.2": hex::encode(self.cvm_token.rem[2].clone()), + "vcca.cvm.rem.3": hex::encode(self.cvm_token.rem[3].clone()), +- "vcca.platform": "", ++ "vcca.is_platform": self.is_platform.clone(), ++ "vcca.platform.measure_value": self.platform_token.sw_components.clone(), + }); + let claim = json!({ + "tee": "vcca", + "payload" : payload, + "ima": ima, +- "uefi": uefi, ++ "event": event, + }); +- Ok(claim as TeeClaim) ++ Ok(claim) ++ } ++ ++ pub fn pkey_to_cosekey(pkey: &PKey) -> Result { ++ let mut cose_key = CoseKey::new(); ++ match pkey.id() { ++ Id::RSA => { ++ let rsa = pkey.rsa()?; ++ cose_key.kty(RSA); ++ cose_key.n(rsa.n().to_vec()); ++ cose_key.e(rsa.e().to_vec()); ++ } ++ Id::EC => { ++ let ec_key = pkey.ec_key()?; ++ cose_key.kty(EC2); ++ let group = ec_key.group(); ++ match group.curve_name() { ++ Some(openssl::nid::Nid::SECP521R1) => cose_key.crv(cose::keys::P_521), ++ Some(nid) => bail!("Unsupported EC curve: {:?}", nid), ++ None => bail!("EC key has no associated curve name"), ++ } ++ ++ let public_key = ec_key.public_key(); ++ let mut ctx = BigNumContext::new()?; ++ let encoded_point = ++ public_key.to_bytes(group, PointConversionForm::UNCOMPRESSED, &mut ctx)?; ++ if !encoded_point.is_empty() && encoded_point[0] == 0x04 { ++ let coordinate_len = (encoded_point.len() - 1) / 2; ++ let x = encoded_point[1..1 + coordinate_len].to_vec(); ++ let y = encoded_point[1 + coordinate_len..].to_vec(); ++ cose_key.x(x); ++ cose_key.y(y); ++ } else { ++ bail!("Unsupported EC point format"); ++ } ++ } ++ _ => bail!("Unsupported key type"), ++ } ++ ++ Ok(cose_key) ++ } ++ ++ pub fn verify_cose_sign1(envelop: &mut CoseMessage, pkey: &PKey) -> Result<()> { ++ let mut cose_key: CoseKey = Evidence::pkey_to_cosekey(pkey)?; ++ cose_key.key_ops(vec![KEY_OPS_VERIFY]); ++ match envelop.header.alg { ++ Some(alg) => cose_key.alg(alg), ++ None => bail!("cose sign verify alg is none"), ++ } ++ envelop ++ .key(&cose_key) ++ .map_err(|err| anyhow!("set cose_key to COSE_Sign1 envelop failed: {err:?}"))?; ++ envelop ++ .decode(None, None) ++ .map_err(|err| anyhow!("verify COSE_Sign1 signature failed:{err:?}"))?; ++ ++ Ok(()) + } +- fn verify_platform_token(&mut self, dev_cert: &[u8]) -> Result<()> { +- // todo verify platform COSE_Sign1 by dev_cert, virtCCA report has no platform token now + ++ fn verify_platform_token(&mut self, dev_cert: &X509) -> Result<()> { + // verify dev_cet by cert chain ++ log::info!("verify dev_cert by cert chain"); + Evidence::verify_dev_cert_chain(dev_cert)?; + ++ // verify platform token cose_sign1 ++ if self.is_platform { ++ log::info!("verify platform COSE_Sign1 by dev_cert"); ++ let pkey = dev_cert.public_key()?; ++ Evidence::verify_cose_sign1(&mut self.platform_envelop, &pkey)?; ++ } ++ + Ok(()) + } +- // todo verify cert chain, now only verify signature +- fn verify_dev_cert_chain(dev_cert: &[u8]) -> Result<()> { +- let dev_cert = x509::X509::from_der(dev_cert)?; +- let sub_cert_file = std::fs::read(VIRTCCA_SUB_CERT)?; +- let sub_cert = x509::X509::from_pem(&sub_cert_file)?; +- let root_cert_file = std::fs::read(VIRTCCA_ROOT_CERT)?; +- let root_cert = x509::X509::from_pem(&root_cert_file)?; ++ ++ //get cert path by cert_type ++ fn detect_cert_path(dev_cert: &X509) -> Result<(&'static str, &'static str)> { ++ let pubkey = dev_cert.public_key()?; ++ match pubkey.id() { ++ Id::RSA => Ok((RSA_ROOT_CERT, RSA_SUB_CERT)), ++ Id::EC => Ok((ECCP_ROOT_CERT, ECCP_SUB_CERT)), ++ _ => Err(anyhow!("unsupported cert type")), ++ } ++ } ++ ++ fn verify_dev_cert_chain(dev_cert: &X509) -> Result<()> { ++ let (root_cert_path, sub_cert_path) = Evidence::detect_cert_path(dev_cert)?; ++ let sub_cert_file = std::fs::read(sub_cert_path)?; ++ let sub_cert = X509::from_pem(&sub_cert_file)?; ++ let root_cert_file = std::fs::read(root_cert_path)?; ++ let root_cert = X509::from_pem(&root_cert_file)?; + + // verify dev_cert by sub_cert + let ret = dev_cert.verify(&(sub_cert.public_key()? as PKey))?; +@@ -236,6 +313,44 @@ impl Evidence { + } + Ok(()) + } ++ ++ pub fn verfiy_cvm_challenge(&self, challenge: &[u8], raw_pub_key: &[u8]) -> Result<()> { ++ let digest = match self.cvm_token.pub_key_hash_alg.as_str() { ++ "sha-256" => MessageDigest::sha256(), ++ "sha-384" => MessageDigest::sha384(), ++ "sha-512" => MessageDigest::sha512(), ++ _ => bail!("unsupported algorithm for pubkey verification"), ++ }; ++ ++ // Calculate the hash value of the CVM public key ++ let calculated_challenge = hash(digest, raw_pub_key)?; ++ ++ // Compare value with the platform challenge value ++ if calculated_challenge.as_ref() != challenge { ++ log::error!( ++ "verify cvm pubkey by platform challenge failed, expected: {:?}, got: {:?}", ++ challenge, ++ calculated_challenge ++ ); ++ bail!("verify cvm pubkey by platform challenge failed"); ++ } ++ ++ Ok(()) ++ } ++ ++ pub fn raw_ec_public_key_to_pkey(&self) -> Result> { ++ let raw_key = &self.cvm_token.pub_key; ++ // Check if it's in uncompressed format (the first byte should be 0x04) ++ if raw_key.is_empty() || raw_key[0] != 0x04 { ++ bail!("Invalid uncompressed EC public key format"); ++ } ++ let group = EcGroup::from_curve_name(Nid::SECP521R1)?; ++ let mut ctx = BigNumContext::new()?; ++ let point = openssl::ec::EcPoint::from_bytes(&group, raw_key, &mut ctx)?; ++ let ec_key = EcKey::from_public_key(&group, &point)?; ++ ++ Ok(PKey::from_ec_key(ec_key)?) ++ } + fn verify_cvm_token(&mut self, challenge: &[u8]) -> Result<()> { + // verify challenge + let len = challenge.len(); +@@ -253,23 +368,15 @@ impl Evidence { + ); + } + +- // todo verify cvm pubkey by platform.challenge, virtCCA report has no platform token now +- +- // verify COSE_Sign1 signature begin +- let raw_pub_key = self.cvm_token.pub_key; +- let mut cose_key: CoseKey = Evidence::from_raw_pub_key(&raw_pub_key)?; +- cose_key.key_ops(vec![cose::keys::KEY_OPS_VERIFY]); +- match self.cvm_envelop.header.alg { +- Some(alg) => cose_key.alg(alg), +- None => bail!("cose sign verify alg is none"), ++ if self.is_platform { ++ self.verfiy_cvm_challenge(&self.platform_token.challenge, &self.cvm_token.pub_key)?; ++ log::info!("verify cvm pubkey by platform challenge success"); + } +- self.cvm_envelop +- .key(&cose_key) +- .map_err(|err| anyhow!("set cose_key to COSE_Sign1 envelop failed: {err:?}"))?; +- self.cvm_envelop +- .decode(None, None) +- .map_err(|err| anyhow!("verify COSE_Sign1 signature failed:{err:?}"))?; +- // verify COSE_Sign1 signature end ++ ++ let pkey = PKey::public_key_from_der(&self.cvm_token.pub_key) ++ .or_else(|_| self.raw_ec_public_key_to_pkey())?; ++ ++ Evidence::verify_cose_sign1(&mut self.cvm_envelop, &pkey)?; + + // verfiy cvm token with reference value + #[cfg(feature = "no_as")] +@@ -303,20 +410,21 @@ impl Evidence { + + Ok(()) + } +- fn from_raw_pub_key(raw_pub_key: &[u8]) -> Result { +- let pub_key: rsa::Rsa = rsa::Rsa::public_key_from_der(raw_pub_key)?; +- let mut cose_key = CoseKey::new(); +- cose_key.kty(cose::keys::RSA); +- cose_key.e(pub_key.e().to_vec()); +- cose_key.n(pub_key.n().to_vec()); + +- Ok(cose_key) ++ fn cose_decode(envelop: &mut CoseMessage) -> Result<()> { ++ envelop.init_decoder(None).map_err(|e| { ++ log::error!("decode COSE failed, {:?}", e); ++ anyhow::anyhow!("decode COSE failed") ++ })?; ++ log::debug!("decode COSE success"); ++ Ok(()) + } ++ + pub fn decode(raw_evidence: Vec) -> Result { + let mut evidence: Evidence = Evidence::new(); + + // decode CBOR evidence to ciborium Value +- let val: Value = ciborium::de::from_reader(raw_evidence.as_slice())?; ++ let val: Value = de::from_reader(raw_evidence.as_slice())?; + log::debug!( + "[debug] decode CBOR virtcca token to ciborium Value:{:?}", + val +@@ -339,6 +447,10 @@ impl Evidence { + if let Value::Integer(i) = k { + match (*i).into() { + CVM_LABEL => evidence.set_cvm_token(v)?, ++ PLATFORM_LABEL => { ++ evidence.is_platform = true; ++ evidence.set_platform_token(v)? ++ } + err => bail!("unknown label {}", err), + } + } else { +@@ -352,170 +464,23 @@ impl Evidence { + bail!("expecting tag type"); + } + +- let ret = evidence.cvm_envelop.init_decoder(None); +- match ret { +- Ok(_) => log::debug!("decode COSE success"), +- Err(e) => { +- log::error!("decode COSE failed, {:?}", e); +- bail!("decode COSE failed"); +- } ++ Evidence::cose_decode(&mut evidence.cvm_envelop)?; ++ evidence.cvm_token = CvmToken::decode(&evidence.cvm_envelop.payload)?; ++ ++ if evidence.is_platform { ++ Evidence::cose_decode(&mut evidence.platform_envelop)?; ++ evidence.platform_token = PlatformToken::decode(&evidence.platform_envelop.payload)?; + } + +- // decode cvm CBOR payload +- evidence.cvm_token = CvmToken::decode(&evidence.cvm_envelop.payload)?; + Ok(evidence) + } + fn set_cvm_token(&mut self, v: &Value) -> Result<()> { +- let tmp = v.as_bytes(); +- if tmp.is_none() { +- log::error!("cvm token is none"); +- bail!("cvm token is none"); +- } +- self.cvm_envelop.bytes = tmp.unwrap().clone(); ++ self.cvm_envelop.bytes = Decode::get_vec(v, "cvm_token", vec![])?; + Ok(()) + } +-} + +-impl CvmToken { +- pub fn new() -> Self { +- Self { +- challenge: [0; CVM_CHALLENGE_SIZE], +- rpv: [0; CVM_RPV_SIZE], +- rim: vec![0, 64], +- rem: Default::default(), +- hash_alg: String::from(""), +- pub_key: [0; CVM_PUB_KEY_SIZE], +- pub_key_hash_alg: String::from(""), +- } +- } +- pub fn decode(raw_payload: &Vec) -> Result { +- let payload: Vec = ciborium::de::from_reader(raw_payload.as_slice())?; +- log::debug!("After decode CBOR payload, payload {:?}", payload); +- let payload: Value = ciborium::de::from_reader(payload.as_slice())?; +- log::debug!("After decode CBOR payload agin, payload {:?}", payload); +- let mut cvm_token: CvmToken = CvmToken::new(); +- if let Value::Map(contents) = payload { +- for (k, v) in contents.iter() { +- if let Value::Integer(i) = k { +- match (*i).into() { +- CVM_CHALLENGE_LABEL => cvm_token.set_challenge(v)?, +- CVM_RPV_LABEL => cvm_token.set_rpv(v)?, +- CVM_RIM_LABEL => cvm_token.set_rim(v)?, +- CVM_REM_LABEL => cvm_token.set_rem(v)?, +- CVM_HASH_ALG_LABEL => cvm_token.set_hash_alg(v)?, +- CVM_PUB_KEY_LABEL => cvm_token.set_pub_key(v)?, +- CVM_PUB_KEY_HASH_ALG_LABEL => cvm_token.set_pub_key_hash_alg(v)?, +- err => bail!("cvm payload unknown label {}", err), +- } +- } else { +- bail!("cvm payload expecting integer key"); +- } +- } +- } else { +- bail!("expecting cvm payload map type"); +- } +- log::debug!("cvm_token decode from raw payload, {:?}", cvm_token); +- Ok(cvm_token) +- } +- fn set_challenge(&mut self, v: &Value) -> Result<()> { +- let tmp = v.as_bytes(); +- if tmp.is_none() { +- bail!("cvm token challenge is none"); +- } +- let tmp = tmp.unwrap().clone(); +- if tmp.len() != CVM_CHALLENGE_SIZE { +- bail!( +- "cvm token challenge expecting {} bytes, got {}", +- CVM_CHALLENGE_SIZE, +- tmp.len() +- ); +- } +- self.challenge[..].clone_from_slice(&tmp); +- Ok(()) +- } +- fn set_rpv(&mut self, v: &Value) -> Result<()> { +- let tmp = v.as_bytes(); +- if tmp.is_none() { +- bail!("cvm token rpv is none"); +- } +- let tmp = tmp.unwrap().clone(); +- if tmp.len() != CVM_RPV_SIZE { +- bail!( +- "cvm token rpv expecting {} bytes, got {}", +- CVM_RPV_SIZE, +- tmp.len() +- ); +- } +- self.rpv[..].clone_from_slice(&tmp); +- Ok(()) +- } +- fn get_measurement(v: &Value, who: &str) -> Result> { +- let tmp = v.as_bytes(); +- if tmp.is_none() { +- bail!("cvm token {} is none", who); +- } +- let tmp = tmp.unwrap().clone(); +- if !matches!(tmp.len(), 32 | 48 | 64) { +- bail!( +- "cvm token {} expecting 32, 48 or 64 bytes, got {}", +- who, +- tmp.len() +- ); +- } +- Ok(tmp) +- } +- fn set_rim(&mut self, v: &Value) -> Result<()> { +- self.rim = Self::get_measurement(v, "rim")?; +- Ok(()) +- } +- fn set_rem(&mut self, v: &Value) -> Result<()> { +- let tmp = v.as_array(); +- if tmp.is_none() { +- bail!("cvm token rem is none"); +- } +- let tmp = tmp.unwrap().clone(); +- if tmp.len() != 4 { +- bail!( +- "cvm token rem expecting size {}, got {}", +- CVM_REM_ARR_SIZE, +- tmp.len() +- ); +- } +- +- for (i, val) in tmp.iter().enumerate() { +- self.rem[i] = Self::get_measurement(val, "rem[{i}]")?; +- } +- Ok(()) +- } +- fn get_hash_alg(v: &Value, who: &str) -> Result { +- let alg = v.as_text(); +- if alg.is_none() { +- bail!("{} hash alg must be str", who); +- } +- Ok(alg.unwrap().to_string()) +- } +- fn set_hash_alg(&mut self, v: &Value) -> Result<()> { +- self.hash_alg = Self::get_hash_alg(v, "cvm token")?; +- Ok(()) +- } +- fn set_pub_key(&mut self, v: &Value) -> Result<()> { +- let tmp = v.as_bytes(); +- if tmp.is_none() { +- bail!("cvm token pub key is none"); +- } +- let tmp = tmp.unwrap().clone(); +- if tmp.len() != CVM_PUB_KEY_SIZE { +- bail!( +- "cvm token pub key len expecting {}, got {}", +- CVM_PUB_KEY_SIZE, +- tmp.len() +- ); +- } +- self.pub_key[..].clone_from_slice(&tmp); +- Ok(()) +- } +- fn set_pub_key_hash_alg(&mut self, v: &Value) -> Result<()> { +- self.pub_key_hash_alg = Self::get_hash_alg(v, "pub key")?; ++ fn set_platform_token(&mut self, v: &Value) -> Result<()> { ++ self.platform_envelop.bytes = Decode::get_vec(v, "platform_token", vec![])?; + Ok(()) + } + } +@@ -533,9 +498,9 @@ mod tests { + let challenge = Vec::new(); + let virtcca_ev = VirtccaEvidence { + evidence: token.to_vec(), +- dev_cert: dev_cert, ++ dev_cert, + ima_log: None, +- uefi_log: None, ++ event_log: None, + }; + let virtcca_ev = serde_json::to_vec(&virtcca_ev).unwrap(); + let r = Evidence::verify(&challenge, &virtcca_ev); +diff --git a/service/attestation/attestation-types/src/lib.rs b/service/attestation/attestation-types/src/lib.rs +index d28837d..379d7f9 100644 +--- a/service/attestation/attestation-types/src/lib.rs ++++ b/service/attestation/attestation-types/src/lib.rs +@@ -18,18 +18,12 @@ use serde_json::Value; + + pub const SESSION_TIMEOUT_MIN: i64 = 1; + +-#[derive(Debug, Serialize, Deserialize)] +-pub struct UefiLog { +- pub ccel_table: Vec, +- pub ccel_data: Vec, +-} +- + #[derive(Debug, Serialize, Deserialize)] + pub struct VirtccaEvidence { + pub evidence: Vec, + pub dev_cert: Vec, + pub ima_log: Option>, +- pub uefi_log: Option, ++ pub event_log: Option>, + } + + #[derive(Debug, Serialize, Deserialize)] +diff --git a/service/attestation/attestation-types/src/resource/policy/opa/mod.rs b/service/attestation/attestation-types/src/resource/policy/opa/mod.rs +index be8c4f9..c5546f1 100644 +--- a/service/attestation/attestation-types/src/resource/policy/opa/mod.rs ++++ b/service/attestation/attestation-types/src/resource/policy/opa/mod.rs +@@ -329,7 +329,7 @@ mod tests { + "report": { + "default_vcca.rego": "{\"vcca.cvm.rim\":\"1ee366339c8245a34a8ad9d27a0b912a588af7da8aef514ae8dec22746956dd1\"}", + "ima": {} +- "uefi": {} ++ "event": {} + } + }, + "tee": "vcca", +-- +2.49.0.windows.1 + diff --git a/secGear.spec b/secGear.spec index 47e4e96..850f6c6 100644 --- a/secGear.spec +++ b/secGear.spec @@ -1,6 +1,6 @@ Name: secGear Version: 0.1.0 -Release: 64 +Release: 65 Summary: secGear is an SDK to develop confidential computing apps based on hardware enclave features @@ -116,6 +116,7 @@ Patch102: 0103-fix-ima-attestation-log-and-add-pcr-check.patch Patch103: 0104-attestation-service-Do-not-hardcode-the-token-path.patch Patch104: 0105-attestation-service-reference-support-itrustee.patch Patch105: 0106-support-attest-npu-firmware-file-combined-with-itrus.patch +Patch106: 0107-Add-support-for-virtcca-platform-token-verify.patch BuildRequires: gcc python automake autoconf libtool BUildRequires: glibc glibc-devel cmake ocaml-dune rpm gcc-c++ compat-openssl11-libs compat-openssl11-devel @@ -205,6 +206,7 @@ EOF cd %{_builddir}/%{name}/service/attestation/attestation-service/ tar xf %{SOURCE1} +touch digest_list_file mkdir -p .cargo cp %{_builddir}/%{name}/service/attestation/attestation-agent/.cargo/config.toml .cargo/ @@ -271,10 +273,13 @@ install -pm 644 service/attestation/attestation-agent/agent/attestation-agent.co install -d %{buildroot}%{_sysconfdir}/attestation/attestation-service/token install -d %{buildroot}%{_sysconfdir}/attestation/attestation-service/policy install -d %{buildroot}%{_sysconfdir}/attestation/attestation-service/verifier/itrustee -install -d %{buildroot}%{_sysconfdir}/attestation/attestation-service/verifier/virtcca +install -d %{buildroot}%{_sysconfdir}/attestation/attestation-service/verifier/virtcca/event +install -d %{buildroot}%{_sysconfdir}/attestation/attestation-service/verifier/virtcca/ima install -d %{buildroot}%{_sysconfdir}/attestation/attestation-service/resource/policy/oeas install -pm 644 service/attestation/attestation-service/service/attestation-service.conf %{buildroot}%{_sysconfdir}/attestation/attestation-service/ install -pm 644 service/attestation/attestation-service/policy/src/opa/*.rego %{buildroot}%{_sysconfdir}/attestation/attestation-service/policy/ +install -pm 644 %{_builddir}/%{name}/service/attestation/attestation-service/digest_list_file %{buildroot}%{_sysconfdir}/attestation/attestation-service/verifier/virtcca/event/ +install -pm 644 %{_builddir}/%{name}/service/attestation/attestation-service/digest_list_file %{buildroot}%{_sysconfdir}/attestation/attestation-service/verifier/virtcca/ima/ install -pm 644 service/attestation/attestation-types/src/resource/policy/opa/virtcca.rego %{buildroot}%{_sysconfdir}/attestation/attestation-service/resource/policy/oeas/ install -pm 751 service/attestation/attestation-service/target/release/attestation-service %{buildroot}/%{_bindir} install -pm 751 service/attestation/attestation-client/target/release/attestation-client %{buildroot}/%{_bindir} @@ -358,6 +363,9 @@ popd systemctl restart rsyslog %changelog +* Thu Aug 28 2025 SPYFAMILY - 0.1.0-65 +- Add support for virtcca platform token verify + * Thu Aug 28 2025 houmingyong - 0.1.0-64 - sync support attest npu firmware file combined with itrustee token -- Gitee