diff --git a/0102-Add-support-for-UEFI-measured-boot-attestation.patch b/0102-Add-support-for-UEFI-measured-boot-attestation.patch new file mode 100644 index 0000000000000000000000000000000000000000..5ca864eb2fdfefa404fa34c4d2df19b59b64eb88 --- /dev/null +++ b/0102-Add-support-for-UEFI-measured-boot-attestation.patch @@ -0,0 +1,1388 @@ +From fd5d243308369d6045d331be8a24676cd30becc8 Mon Sep 17 00:00:00 2001 +From: SPYFAMILY +Date: Mon, 12 May 2025 14:42:51 +0800 +Subject: [PATCH] =?UTF-8?q?=E8=BF=9C=E7=A8=8B=E8=AF=81=E6=98=8E=E7=BB=9F?= + =?UTF-8?q?=E4=B8=80=E6=A1=86=E6=9E=B6uefi=E5=BA=A6=E9=87=8F=E5=90=AF?= + =?UTF-8?q?=E5=8A=A8=E9=80=82=E9=85=8D?= +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +--- + .../attestation-agent/attester/src/lib.rs | 1 - + .../attester/src/virtcca/mod.rs | 39 +++- + .../attestation-service/service/src/lib.rs | 44 +++- + .../attestation-service/verifier/Cargo.toml | 1 + + .../verifier/eventlog-rs/Cargo.toml | 18 ++ + .../verifier/eventlog-rs/LICENSE | 201 +++++++++++++++++ + .../verifier/eventlog-rs/README.md | 2 + + .../verifier/eventlog-rs/src/bios_eventlog.rs | 87 ++++++++ + .../verifier/eventlog-rs/src/enums.rs | 54 +++++ + .../verifier/eventlog-rs/src/lib.rs | 177 +++++++++++++++ + .../verifier/eventlog-rs/src/read.rs | 111 ++++++++++ + .../attestation-service/verifier/src/lib.rs | 1 - + .../verifier/src/virtcca/ima.rs | 42 ++-- + .../verifier/src/virtcca/mod.rs | 68 +++++- + .../verifier/src/virtcca/uefi.rs | 209 ++++++++++++++++++ + .../attestation/attestation-types/src/lib.rs | 7 + + .../src/resource/policy/opa/mod.rs | 1 + + 17 files changed, 1010 insertions(+), 53 deletions(-) + create mode 100644 service/attestation/attestation-service/verifier/eventlog-rs/Cargo.toml + create mode 100644 service/attestation/attestation-service/verifier/eventlog-rs/LICENSE + create mode 100644 service/attestation/attestation-service/verifier/eventlog-rs/README.md + create mode 100644 service/attestation/attestation-service/verifier/eventlog-rs/src/bios_eventlog.rs + create mode 100644 service/attestation/attestation-service/verifier/eventlog-rs/src/enums.rs + create mode 100644 service/attestation/attestation-service/verifier/eventlog-rs/src/lib.rs + create mode 100644 service/attestation/attestation-service/verifier/eventlog-rs/src/read.rs + create mode 100644 service/attestation/attestation-service/verifier/src/virtcca/uefi.rs + +diff --git a/service/attestation/attestation-agent/attester/src/lib.rs b/service/attestation/attestation-agent/attester/src/lib.rs +index 6dd549d..d495a6f 100644 +--- a/service/attestation/attestation-agent/attester/src/lib.rs ++++ b/service/attestation/attestation-agent/attester/src/lib.rs +@@ -16,7 +16,6 @@ + + use anyhow::*; + use async_trait::async_trait; +-use log; + + #[cfg(feature = "itrustee-attester")] + mod itrustee; +diff --git a/service/attestation/attestation-agent/attester/src/virtcca/mod.rs b/service/attestation/attestation-agent/attester/src/virtcca/mod.rs +index 86f0061..9f84ed4 100644 +--- a/service/attestation/attestation-agent/attester/src/virtcca/mod.rs ++++ b/service/attestation/attestation-agent/attester/src/virtcca/mod.rs +@@ -15,7 +15,7 @@ + //! Call the hardware sdk or driver to get the specific evidence + + use anyhow::{bail, Result}; +-use attestation_types::VirtccaEvidence; ++use attestation_types::{UefiLog, VirtccaEvidence}; + use log; + use std::path::Path; + +@@ -25,6 +25,10 @@ use crate::EvidenceRequest; + + mod virtcca; + ++const IMA_LOG_PATH: &str = "/sys/kernel/security/ima/binary_runtime_measurements"; ++const CCEL_TABLE_PATH: &str = "/sys/firmware/acpi/tables/CCEL"; ++const CCEL_DATA_PATH: &str = "/sys/firmware/acpi/tables/data/CCEL"; ++ + #[derive(Debug, Default)] + pub struct VirtccaAttester {} + +@@ -41,6 +45,7 @@ pub fn detect_platform() -> bool { + } + + 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(); +@@ -88,18 +93,46 @@ fn virtcca_get_token(user_data: EvidenceRequest) -> Result { + Some(ima) => ima, + None => false, + }; ++ + let ima_log = match with_ima { + true => { +- Some(std::fs::read("/sys/kernel/security/ima/binary_runtime_measurements").unwrap()) +- } ++ match std::fs::read(IMA_LOG_PATH) { ++ Ok(d) => { ++ log::info!("read ima log success"); ++ Some(d) ++ }, ++ Err(e) => { ++ log::error!("read IMA log failed: {}", e); ++ bail!("get ima log failed"); ++ } ++ } ++ }, + false => None, + }; + ++ 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, ++ }) ++ }, ++ _ => { ++ log::warn!("read ccel table or data failed"); ++ None ++ } ++ }; ++ + let evidence = VirtccaEvidence { + evidence: token, + dev_cert: dev_cert, + ima_log: ima_log, ++ uefi_log: uefi_log, + }; ++ + let _ = tsi_free_ctx(ctx); + Ok(evidence) + } +diff --git a/service/attestation/attestation-service/service/src/lib.rs b/service/attestation/attestation-service/service/src/lib.rs +index 99ae818..96328ce 100644 +--- a/service/attestation/attestation-service/service/src/lib.rs ++++ b/service/attestation/attestation-service/service/src/lib.rs +@@ -109,6 +109,28 @@ impl AttestationService { + sessions: web::Data::new(SessionMap::new()), + }) + } ++ ++ async fn evaluate_evidence_field(claims_evidence: &Value, field: &str, mut passed: &mut bool) { ++ log::debug!( ++ "claims evidence {}: {:?}", ++ field, ++ claims_evidence[field].clone() ++ ); ++ if *passed { ++ match claims_evidence[field].clone() { ++ Value::Object(obj) => { ++ for (_k, v) in obj { ++ if v == Value::Bool(false) { ++ *passed = false; ++ break; ++ } ++ } ++ } ++ _ => log::debug!("no {} result", field), ++ } ++ } ++ } ++ + /// evaluate tee evidence with reference and policy, and issue attestation result token + pub async fn evaluate( + &self, +@@ -120,18 +142,8 @@ impl AttestationService { + let claims_evidence = verifier.verify_evidence(user_data, evidence).await?; + + let mut passed = true; +- log::debug!("claims evidece ima: {:?}", claims_evidence["ima"].clone()); +- match claims_evidence["ima"].clone() { +- serde_json::Value::Object(obj) => { +- for (_k, v) in obj { +- if v == Value::Bool(false) { +- passed = false; +- break; +- } +- } +- } +- _ => log::debug!("no ima result"), +- } ++ AttestationService::evaluate_evidence_field(&claims_evidence, "ima", &mut passed).await; ++ AttestationService::evaluate_evidence_field(&claims_evidence, "uefi", &mut passed).await; + + // get reference by keys in claims_evidence + let mut ops_refs = ReferenceOps::default(); +@@ -158,8 +170,10 @@ impl AttestationService { + .await; + let mut report = serde_json::json!({}); + let mut ref_exist_null: bool = false; ++ + match result { + Ok(eval) => { ++ log::debug!("policy: {:?}", eval); + for id in eval.keys() { + let val = Value::from_str(&eval[id].clone())?; + let refs = match val +@@ -194,6 +208,12 @@ impl AttestationService { + .unwrap() + .insert("ima".to_string(), claims_evidence["ima"].clone()); + ++ // add uefi detail result to report ++ report ++ .as_object_mut() ++ .unwrap() ++ .insert("uefi".to_string(), claims_evidence["uefi"].clone()); ++ + // issue attestation result token + let evl_report = EvlReport { + tee: String::from( +diff --git a/service/attestation/attestation-service/verifier/Cargo.toml b/service/attestation/attestation-service/verifier/Cargo.toml +index baab873..e66e5ea 100644 +--- a/service/attestation/attestation-service/verifier/Cargo.toml ++++ b/service/attestation/attestation-service/verifier/Cargo.toml +@@ -20,6 +20,7 @@ attestation-types.workspace = true + ccatoken.workspace = true + ear.workspace = true + base64-url.workspace = true ++eventlog-rs = { path = "./eventlog-rs" } + + [dev-dependencies] + +diff --git a/service/attestation/attestation-service/verifier/eventlog-rs/Cargo.toml b/service/attestation/attestation-service/verifier/eventlog-rs/Cargo.toml +new file mode 100644 +index 0000000..20796fe +--- /dev/null ++++ b/service/attestation/attestation-service/verifier/eventlog-rs/Cargo.toml +@@ -0,0 +1,18 @@ ++[package] ++name = "eventlog-rs" ++description = "Rust Parsing tool for TCG EventLog" ++version = "0.1.7" ++authors = ["Jiale Zhang "] ++edition = "2021" ++homepage = "https://github.com/inclavare-containers/eventlog-rs" ++license = "Apache-2.0" ++ ++# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html ++ ++[dependencies] ++log = "0.4.14" ++lazy_static = "1.4.0" ++byteorder = "1" ++hex = "0.4.3" ++sha2 = "0.10" ++anyhow = "1.0" +diff --git a/service/attestation/attestation-service/verifier/eventlog-rs/LICENSE b/service/attestation/attestation-service/verifier/eventlog-rs/LICENSE +new file mode 100644 +index 0000000..261eeb9 +--- /dev/null ++++ b/service/attestation/attestation-service/verifier/eventlog-rs/LICENSE +@@ -0,0 +1,201 @@ ++ Apache License ++ Version 2.0, January 2004 ++ http://www.apache.org/licenses/ ++ ++ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION ++ ++ 1. Definitions. ++ ++ "License" shall mean the terms and conditions for use, reproduction, ++ and distribution as defined by Sections 1 through 9 of this document. ++ ++ "Licensor" shall mean the copyright owner or entity authorized by ++ the copyright owner that is granting the License. ++ ++ "Legal Entity" shall mean the union of the acting entity and all ++ other entities that control, are controlled by, or are under common ++ control with that entity. For the purposes of this definition, ++ "control" means (i) the power, direct or indirect, to cause the ++ direction or management of such entity, whether by contract or ++ otherwise, or (ii) ownership of fifty percent (50%) or more of the ++ outstanding shares, or (iii) beneficial ownership of such entity. ++ ++ "You" (or "Your") shall mean an individual or Legal Entity ++ exercising permissions granted by this License. ++ ++ "Source" form shall mean the preferred form for making modifications, ++ including but not limited to software source code, documentation ++ source, and configuration files. ++ ++ "Object" form shall mean any form resulting from mechanical ++ transformation or translation of a Source form, including but ++ not limited to compiled object code, generated documentation, ++ and conversions to other media types. ++ ++ "Work" shall mean the work of authorship, whether in Source or ++ Object form, made available under the License, as indicated by a ++ copyright notice that is included in or attached to the work ++ (an example is provided in the Appendix below). ++ ++ "Derivative Works" shall mean any work, whether in Source or Object ++ form, that is based on (or derived from) the Work and for which the ++ editorial revisions, annotations, elaborations, or other modifications ++ represent, as a whole, an original work of authorship. For the purposes ++ of this License, Derivative Works shall not include works that remain ++ separable from, or merely link (or bind by name) to the interfaces of, ++ the Work and Derivative Works thereof. ++ ++ "Contribution" shall mean any work of authorship, including ++ the original version of the Work and any modifications or additions ++ to that Work or Derivative Works thereof, that is intentionally ++ submitted to Licensor for inclusion in the Work by the copyright owner ++ or by an individual or Legal Entity authorized to submit on behalf of ++ the copyright owner. For the purposes of this definition, "submitted" ++ means any form of electronic, verbal, or written communication sent ++ to the Licensor or its representatives, including but not limited to ++ communication on electronic mailing lists, source code control systems, ++ and issue tracking systems that are managed by, or on behalf of, the ++ Licensor for the purpose of discussing and improving the Work, but ++ excluding communication that is conspicuously marked or otherwise ++ designated in writing by the copyright owner as "Not a Contribution." ++ ++ "Contributor" shall mean Licensor and any individual or Legal Entity ++ on behalf of whom a Contribution has been received by Licensor and ++ subsequently incorporated within the Work. ++ ++ 2. Grant of Copyright License. Subject to the terms and conditions of ++ this License, each Contributor hereby grants to You a perpetual, ++ worldwide, non-exclusive, no-charge, royalty-free, irrevocable ++ copyright license to reproduce, prepare Derivative Works of, ++ publicly display, publicly perform, sublicense, and distribute the ++ Work and such Derivative Works in Source or Object form. ++ ++ 3. Grant of Patent License. Subject to the terms and conditions of ++ this License, each Contributor hereby grants to You a perpetual, ++ worldwide, non-exclusive, no-charge, royalty-free, irrevocable ++ (except as stated in this section) patent license to make, have made, ++ use, offer to sell, sell, import, and otherwise transfer the Work, ++ where such license applies only to those patent claims licensable ++ by such Contributor that are necessarily infringed by their ++ Contribution(s) alone or by combination of their Contribution(s) ++ with the Work to which such Contribution(s) was submitted. If You ++ institute patent litigation against any entity (including a ++ cross-claim or counterclaim in a lawsuit) alleging that the Work ++ or a Contribution incorporated within the Work constitutes direct ++ or contributory patent infringement, then any patent licenses ++ granted to You under this License for that Work shall terminate ++ as of the date such litigation is filed. ++ ++ 4. Redistribution. You may reproduce and distribute copies of the ++ Work or Derivative Works thereof in any medium, with or without ++ modifications, and in Source or Object form, provided that You ++ meet the following conditions: ++ ++ (a) You must give any other recipients of the Work or ++ Derivative Works a copy of this License; and ++ ++ (b) You must cause any modified files to carry prominent notices ++ stating that You changed the files; and ++ ++ (c) You must retain, in the Source form of any Derivative Works ++ that You distribute, all copyright, patent, trademark, and ++ attribution notices from the Source form of the Work, ++ excluding those notices that do not pertain to any part of ++ the Derivative Works; and ++ ++ (d) If the Work includes a "NOTICE" text file as part of its ++ distribution, then any Derivative Works that You distribute must ++ include a readable copy of the attribution notices contained ++ within such NOTICE file, excluding those notices that do not ++ pertain to any part of the Derivative Works, in at least one ++ of the following places: within a NOTICE text file distributed ++ as part of the Derivative Works; within the Source form or ++ documentation, if provided along with the Derivative Works; or, ++ within a display generated by the Derivative Works, if and ++ wherever such third-party notices normally appear. The contents ++ of the NOTICE file are for informational purposes only and ++ do not modify the License. You may add Your own attribution ++ notices within Derivative Works that You distribute, alongside ++ or as an addendum to the NOTICE text from the Work, provided ++ that such additional attribution notices cannot be construed ++ as modifying the License. ++ ++ You may add Your own copyright statement to Your modifications and ++ may provide additional or different license terms and conditions ++ for use, reproduction, or distribution of Your modifications, or ++ for any such Derivative Works as a whole, provided Your use, ++ reproduction, and distribution of the Work otherwise complies with ++ the conditions stated in this License. ++ ++ 5. Submission of Contributions. Unless You explicitly state otherwise, ++ any Contribution intentionally submitted for inclusion in the Work ++ by You to the Licensor shall be under the terms and conditions of ++ this License, without any additional terms or conditions. ++ Notwithstanding the above, nothing herein shall supersede or modify ++ the terms of any separate license agreement you may have executed ++ with Licensor regarding such Contributions. ++ ++ 6. Trademarks. This License does not grant permission to use the trade ++ names, trademarks, service marks, or product names of the Licensor, ++ except as required for reasonable and customary use in describing the ++ origin of the Work and reproducing the content of the NOTICE file. ++ ++ 7. Disclaimer of Warranty. Unless required by applicable law or ++ agreed to in writing, Licensor provides the Work (and each ++ Contributor provides its Contributions) on an "AS IS" BASIS, ++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or ++ implied, including, without limitation, any warranties or conditions ++ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A ++ PARTICULAR PURPOSE. You are solely responsible for determining the ++ appropriateness of using or redistributing the Work and assume any ++ risks associated with Your exercise of permissions under this License. ++ ++ 8. Limitation of Liability. In no event and under no legal theory, ++ whether in tort (including negligence), contract, or otherwise, ++ unless required by applicable law (such as deliberate and grossly ++ negligent acts) or agreed to in writing, shall any Contributor be ++ liable to You for damages, including any direct, indirect, special, ++ incidental, or consequential damages of any character arising as a ++ result of this License or out of the use or inability to use the ++ Work (including but not limited to damages for loss of goodwill, ++ work stoppage, computer failure or malfunction, or any and all ++ other commercial damages or losses), even if such Contributor ++ has been advised of the possibility of such damages. ++ ++ 9. Accepting Warranty or Additional Liability. While redistributing ++ the Work or Derivative Works thereof, You may choose to offer, ++ and charge a fee for, acceptance of support, warranty, indemnity, ++ or other liability obligations and/or rights consistent with this ++ License. However, in accepting such obligations, You may act only ++ on Your own behalf and on Your sole responsibility, not on behalf ++ of any other Contributor, and only if You agree to indemnify, ++ defend, and hold each Contributor harmless for any liability ++ incurred by, or claims asserted against, such Contributor by reason ++ of your accepting any such warranty or additional liability. ++ ++ END OF TERMS AND CONDITIONS ++ ++ APPENDIX: How to apply the Apache License to your work. ++ ++ To apply the Apache License to your work, attach the following ++ boilerplate notice, with the fields enclosed by brackets "[]" ++ replaced with your own identifying information. (Don't include ++ the brackets!) The text should be enclosed in the appropriate ++ comment syntax for the file format. We also recommend that a ++ file or class name and description of purpose be included on the ++ same "printed page" as the copyright notice for easier ++ identification within third-party archives. ++ ++ Copyright [yyyy] [name of copyright owner] ++ ++ Licensed under the Apache License, Version 2.0 (the "License"); ++ you may not use this file except in compliance with the License. ++ You may obtain a copy of the License at ++ ++ http://www.apache.org/licenses/LICENSE-2.0 ++ ++ Unless required by applicable law or agreed to in writing, software ++ distributed under the License is distributed on an "AS IS" BASIS, ++ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++ See the License for the specific language governing permissions and ++ limitations under the License. +diff --git a/service/attestation/attestation-service/verifier/eventlog-rs/README.md b/service/attestation/attestation-service/verifier/eventlog-rs/README.md +new file mode 100644 +index 0000000..d7c242f +--- /dev/null ++++ b/service/attestation/attestation-service/verifier/eventlog-rs/README.md +@@ -0,0 +1,2 @@ ++# eventlog-rs ++lib to analyze and extract TEE's event log +diff --git a/service/attestation/attestation-service/verifier/eventlog-rs/src/bios_eventlog.rs b/service/attestation/attestation-service/verifier/eventlog-rs/src/bios_eventlog.rs +new file mode 100644 +index 0000000..5cbcf8b +--- /dev/null ++++ b/service/attestation/attestation-service/verifier/eventlog-rs/src/bios_eventlog.rs +@@ -0,0 +1,87 @@ ++// TCG Eventlog for Conventional BIOS ++// Spec: https://trustedcomputinggroup.org/wp-content/uploads/TCG_PCClientImplementation_1-21_1_00.pdf ++ ++use crate::enums::EVENTLOG_TYPES; ++use anyhow::*; ++use byteorder::{LittleEndian, ReadBytesExt}; ++use core::fmt; ++use std::convert::TryFrom; ++ ++const SHA1_DIGEST_SIZE: usize = 20; ++ ++#[derive(Clone)] ++pub struct BiosEventlog { ++ pub log: Vec, ++} ++ ++impl fmt::Display for BiosEventlog { ++ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ++ let mut parsed_el = String::default(); ++ for event_entry in self.log.clone() { ++ parsed_el = format!( ++ "{}\nEvent Entry:\n\tPCR: {}\n\tEvent Type: {}\n\tDigest: {}\n\tEvent Data: {}\n", ++ parsed_el, ++ event_entry.pcr_index, ++ event_entry.event_type, ++ hex::encode(event_entry.digest.clone()), ++ String::from_utf8(event_entry.event_data.clone()) ++ .unwrap_or_else(|_| hex::encode(event_entry.event_data.clone())), ++ ); ++ } ++ ++ write!(f, "{parsed_el}") ++ } ++} ++ ++#[derive(Clone)] ++pub struct BiosEventlogEntry { ++ pub pcr_index: u32, ++ pub event_type: String, ++ pub digest: Vec, ++ pub event_data: Vec, ++} ++ ++impl TryFrom> for BiosEventlog { ++ type Error = anyhow::Error; ++ ++ fn try_from(data: Vec) -> Result { ++ let mut index = 0; ++ let mut event_log: Vec = Vec::new(); ++ ++ while index < data.len() as usize { ++ let stop_flag = (&data[index..(index + 8)]).read_u64::()?; ++ let pcr_index = (&data[index..(index + 4)]).read_u32::()?; ++ index += 4; ++ ++ let event_type_num = (&data[index..(index + 4)]).read_u32::()?; ++ index += 4; ++ let event_type = match EVENTLOG_TYPES.get(&event_type_num) { ++ Some(type_name) => type_name.to_string(), ++ None => format!("UNKOWN_TYPE: {:x}", &event_type_num), ++ }; ++ ++ if stop_flag == 0xFFFFFFFFFFFFFFFF || stop_flag == 0x0000000000000000 { ++ break; ++ } ++ ++ let digest = data[index..(index + SHA1_DIGEST_SIZE)].to_vec(); ++ index += SHA1_DIGEST_SIZE; ++ ++ let event_data_size = (&data[index..(index + 4)]).read_u32::()? as usize; ++ index += 4; ++ let event_data = data[index..(index + event_data_size)].to_vec(); ++ index += event_data_size; ++ ++ let eventlog_entry = BiosEventlogEntry { ++ pcr_index, ++ event_type, ++ digest, ++ event_data, ++ }; ++ ++ event_log.push(eventlog_entry) ++ } ++ ++ Ok(BiosEventlog { log: event_log }) ++ } ++} +diff --git a/service/attestation/attestation-service/verifier/eventlog-rs/src/enums.rs b/service/attestation/attestation-service/verifier/eventlog-rs/src/enums.rs +new file mode 100644 +index 0000000..be3fefc +--- /dev/null ++++ b/service/attestation/attestation-service/verifier/eventlog-rs/src/enums.rs +@@ -0,0 +1,54 @@ ++use lazy_static::lazy_static; ++use std::collections::HashMap; ++ ++lazy_static! { ++ pub static ref TCG_ALGORITHMS: HashMap = HashMap::from([ ++ (0x1, "TPM_ALG_RSA"), ++ (0x3, "TPM_ALG_TDES"), ++ (0x4, "TPM_ALG_SHA1"), ++ (0xB, "TPM_ALG_SHA256"), ++ (0xC, "TPM_ALG_SHA384"), ++ (0xD, "TPM_ALG_SHA512"), ++ ]); ++} ++ ++lazy_static! { ++ pub static ref EVENTLOG_TYPES: HashMap = HashMap::from( ++ [ ++ (0x0, "EV_PREBOOT_CERT"), ++ (0x1, "EV_POST_CODE"), ++ (0x2, "EV_UNUSED"), ++ (0x3, "EV_NO_ACTION"), ++ (0x4, "EV_SEPARATOR"), ++ (0x5, "EV_ACTION"), ++ (0x6, "EV_EVENT_TAG"), ++ (0x7, "EV_S_CRTM_CONTENTS"), ++ (0x8, "EV_S_CRTM_VERSION"), ++ (0x9, "EV_CPU_MICROCODE"), ++ (0xa, "EV_PLATFORM_CONFIG_FLAGS"), ++ (0xb, "EV_TABLE_OF_DEVICES"), ++ (0xc, "EV_COMPACT_HASH"), ++ (0xd, "EV_IPL"), ++ (0xe, "EV_IPL_PARTITION_DATA"), ++ (0xf, "EV_NONHOST_CODE"), ++ (0x10, "EV_NONHOST_CONFIG"), ++ (0x11, "EV_NONHOST_INFO"), ++ (0x12, "EV_OMIT_BOOT_DEVICE_EVENTS"), ++ ++ // TCG EFI Platform Specification For TPM Family 1.1 or 1.2 ++ (0x80000000, "EV_EFI_EVENT_BASE"), ++ (0x80000001, "EV_EFI_VARIABLE_DRIVER_CONFIG"), ++ (0x80000002, "EV_EFI_VARIABLE_BOOT"), ++ (0x80000003, "EV_EFI_BOOT_SERVICES_APPLICATION"), ++ (0x80000004, "EV_EFI_BOOT_SERVICES_DRIVER"), ++ (0x80000005, "EV_EFI_RUNTIME_SERVICES_DRIVER"), ++ (0x80000006, "EV_EFI_GPT_EVENT"), ++ (0x80000007, "EV_EFI_ACTION"), ++ (0x80000008, "EV_EFI_PLATFORM_FIRMWARE_BLOB"), ++ (0x80000009, "EV_EFI_HANDOFF_TABLES"), ++ (0x8000000a, "EV_EFI_PLATFORM_FIRMWARE_BLOB2"), ++ (0x8000000b, "EV_EFI_HANDOFF_TABLES2"), ++ (0x800000e0, "EV_EFI_VARIABLE_AUTHORITY"), ++ ] ++ ); ++} +diff --git a/service/attestation/attestation-service/verifier/eventlog-rs/src/lib.rs b/service/attestation/attestation-service/verifier/eventlog-rs/src/lib.rs +new file mode 100644 +index 0000000..ced292b +--- /dev/null ++++ b/service/attestation/attestation-service/verifier/eventlog-rs/src/lib.rs +@@ -0,0 +1,177 @@ ++use anyhow::{anyhow, Result}; ++use byteorder::{LittleEndian, ReadBytesExt}; ++use core::fmt; ++use enums::{EVENTLOG_TYPES, TCG_ALGORITHMS}; ++use sha2::{Digest, Sha256}; ++use std::collections::HashMap; ++use std::convert::TryFrom; ++ ++const RTMR_LENGTH_BY_BYTES: usize = 32; ++ ++mod bios_eventlog; ++mod enums; ++ ++pub use bios_eventlog::BiosEventlog; ++pub mod read; ++ ++#[derive(Clone)] ++pub struct Eventlog { ++ pub log: Vec, ++} ++ ++impl fmt::Display for Eventlog { ++ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { ++ let mut parsed_el = String::default(); ++ for event_entry in self.log.clone() { ++ parsed_el = format!( ++ "{}\nEvent Entry:\nPCR(CC Event Log MR): {}\n\tEvent Type id: {}\n\tEvent Type: {}\n\tDigest Algorithm: {}\n\tDigest: {}\n\tEvent Desc: {}\n", ++ parsed_el, ++ event_entry.target_measurement_registry, ++ format!("0x{:08X}", event_entry.event_type_id), ++ event_entry.event_type, ++ event_entry.digests[0].algorithm, ++ hex::encode(event_entry.digests[0].digest.clone()), ++ String::from_utf8(event_entry.event_desc.clone()) ++ .unwrap_or_else(|_| hex::encode(event_entry.event_desc.clone())), ++ ); ++ } ++ ++ write!(f, "{parsed_el}") ++ } ++} ++ ++#[derive(Clone)] ++pub struct EventlogEntry { ++ pub target_measurement_registry: u32, ++ pub event_type_id: u32, ++ pub event_type: String, ++ pub digests: Vec, ++ pub event_desc: Vec, ++} ++ ++#[derive(Debug, Clone)] ++pub struct ElDigest { ++ pub algorithm: String, ++ pub digest: Vec, ++} ++ ++impl Eventlog { ++ pub fn replay_measurement_registry(&self) -> HashMap> { ++ // result dictionary for classifying event logs by rtmr index ++ // the key is a integer, which represents rtmr index ++ // the value is a list of event log entries whose rtmr index is equal to its related key ++ let mut event_logs_by_mr_index: HashMap> = HashMap::new(); ++ ++ let mut result: HashMap> = HashMap::new(); ++ ++ for log_entry in self.log.iter() { ++ match event_logs_by_mr_index.get_mut(&log_entry.target_measurement_registry) { ++ Some(logs) => logs.push(log_entry.clone()), ++ None => { ++ event_logs_by_mr_index.insert( ++ log_entry.target_measurement_registry, ++ vec![log_entry.clone()], ++ ); ++ } ++ } ++ } ++ ++ for (mr_index, log_set) in event_logs_by_mr_index.iter() { ++ let mut mr_value = [0; RTMR_LENGTH_BY_BYTES]; ++ ++ for log in log_set.iter() { ++ let digest = &log.digests[0].digest; ++ let mut sha_algo = Sha256::new(); ++ sha_algo.update(&mr_value); ++ sha_algo.update(digest.as_slice()); ++ mr_value.copy_from_slice(sha_algo.finalize().as_slice()); ++ } ++ result.insert(mr_index.clone(), mr_value.to_vec()); ++ } ++ ++ result ++ } ++} ++ ++impl TryFrom> for Eventlog { ++ type Error = anyhow::Error; ++ ++ fn try_from(data: Vec) -> Result { ++ let mut index = 0; ++ let mut event_log: Vec = Vec::new(); ++ let mut digest_size_map: HashMap = HashMap::new(); ++ ++ while index < data.len() as usize { ++ let stop_flag = (&data[index..(index + 8)]).read_u64::()?; ++ let target_measurement_registry = ++ (&data[index..(index + 4)]).read_u32::()?; ++ index += 4; ++ ++ let event_type_num = (&data[index..(index + 4)]).read_u32::()?; ++ index += 4; ++ let event_type = match EVENTLOG_TYPES.get(&event_type_num) { ++ Some(type_name) => type_name.to_string(), ++ None => format!("UNKNOWN_TYPE: {:x}", &event_type_num), ++ }; ++ ++ let event_type_id = event_type_num; ++ if event_type == "EV_NO_ACTION".to_string() { ++ index += 48; ++ let algo_number = (&data[index..(index + 4)]).read_u32::()?; ++ index += 4; ++ for _ in 0..algo_number { ++ digest_size_map.insert( ++ (&data[index..(index + 2)]).read_u16::()?, ++ (&data[(index + 2)..(index + 4)]).read_u16::()?, ++ ); ++ index += 4; ++ } ++ let vendor_size = data[index]; ++ index += vendor_size as usize + 1; ++ continue; ++ } ++ ++ if stop_flag == 0xFFFFFFFFFFFFFFFF || stop_flag == 0x0000000000000000 { ++ break; ++ } ++ ++ let digest_count = (&data[index..(index + 4)]).read_u32::()?; ++ index += 4; ++ let mut digests: Vec = Vec::new(); ++ for _ in 0..digest_count { ++ let digest_algo_num = (&data[index..(index + 2)]).read_u16::()?; ++ index += 2; ++ let algorithm = match TCG_ALGORITHMS.get(&digest_algo_num) { ++ Some(digest_algo_name) => digest_algo_name.to_string(), ++ None => format!("UNKNOWN_ALGORITHM: {:x}", &digest_algo_num), ++ }; ++ let digest_size = digest_size_map ++ .get(&digest_algo_num) ++ .ok_or(anyhow!( ++ "Internal Error: get digest size failed when parse eventlog entry, digest_algo_num: {:?}", &digest_algo_num ++ ))? ++ .to_owned() as usize; ++ let digest = data[index..(index + digest_size)].to_vec(); ++ index += digest_size; ++ digests.push(ElDigest { algorithm, digest }); ++ } ++ ++ let event_desc_size = (&data[index..(index + 4)]).read_u32::()? as usize; ++ index += 4; ++ let event_desc = data[index..(index + event_desc_size)].to_vec(); ++ index += event_desc_size; ++ ++ let eventlog_entry = EventlogEntry { ++ target_measurement_registry, ++ event_type_id, ++ event_type, ++ digests, ++ event_desc, ++ }; ++ ++ event_log.push(eventlog_entry) ++ } ++ ++ Ok(Eventlog { log: event_log }) ++ } ++} +diff --git a/service/attestation/attestation-service/verifier/eventlog-rs/src/read.rs b/service/attestation/attestation-service/verifier/eventlog-rs/src/read.rs +new file mode 100644 +index 0000000..bbc7dd8 +--- /dev/null ++++ b/service/attestation/attestation-service/verifier/eventlog-rs/src/read.rs +@@ -0,0 +1,111 @@ ++// Copyright (c) 2024 Alibaba Cloud ++// ++// SPDX-License-Identifier: Apache-2.0 ++// ++ ++use anyhow::{bail, Context, Result}; ++use byteorder::{LittleEndian, ReadBytesExt}; ++ ++use std::{ ++ fs, ++ io::{Read, Seek}, ++ path::Path, ++}; ++ ++pub const CCEL_PATH: &str = "/sys/firmware/acpi/tables/data/CCEL"; ++ ++/// Path to the ACPI table CCEL description ++pub const CCEL_ACPI_DESCRIPTION: &str = "/sys/firmware/acpi/tables/CCEL"; ++ ++/// Guest memory which is used to read the CCEL ++pub const GUEST_MEMORY: &str = "/dev/mem"; ++ ++/// Signature of CCEL's ACPI Description Header ++pub const CCEL_SIGNATURE: &[u8] = b"CCEL"; ++ ++/// Try to read CCEL from either ACPI data or guest memory ++/// ++/// If read from guest memory, the offset and length will be read from the CCEL ACPI description. ++/// defined as ++/// ```no-run ++/// pub struct EfiAcpiDescriptionHeader { ++/// signature: u32, ++/// length: u32, ++/// revision: u8, ++/// checksum: u8, ++/// oem_id: [u8; 6], ++/// oem_table_id: u64, ++/// oem_revision: u32, ++/// creator_id: u32, ++/// creator_revision: u32, ++/// } ++/// ++/// pub struct TdxEventLogACPITable { ++/// efi_acpi_description_header: EfiAcpiDescriptionHeader, ++/// rsv: u32, ++/// laml: u64, ++/// lasa: u64, ++/// } ++/// ``` ++pub fn read_ccel() -> Result> { ++ if Path::new(CCEL_PATH).exists() { ++ let ccel = fs::read(CCEL_PATH)?; ++ return Ok(ccel); ++ } ++ ++ let efi_acpi_description = ++ fs::read(CCEL_ACPI_DESCRIPTION).context("ccel description does not exist")?; ++ if efi_acpi_description.len() < 56 { ++ bail!("invalid CCEL ACPI description"); ++ } ++ ++ let mut index = 0; ++ ++ let signature = (&efi_acpi_description[index..index + 4]).read_u32::()?; ++ index += 4; ++ ++ let length = (&efi_acpi_description[index..index + 4]).read_u32::()?; ++ index += 32; ++ ++ let rsv = (&efi_acpi_description[index..index + 4]).read_u32::()?; ++ index += 4; ++ ++ let laml = (&efi_acpi_description[index..index + 8]).read_u64::()?; ++ index += 8; ++ ++ let lasa = (&efi_acpi_description[index..index + 8]).read_u64::()?; ++ ++ let ccel_signature = u32::from_le_bytes(CCEL_SIGNATURE.try_into()?); ++ if signature != ccel_signature { ++ bail!("invalid CCEL ACPI table: wrong CCEL signature"); ++ } ++ ++ if rsv != 0 { ++ bail!("invalid CCEL ACPI table: RSV must be 0"); ++ } ++ ++ if length != efi_acpi_description.len() as u32 { ++ bail!("invalid CCEL ACPI table: header length not match"); ++ } ++ ++ let mut guest_memory = fs::OpenOptions::new().read(true).open(GUEST_MEMORY)?; ++ guest_memory.seek(std::io::SeekFrom::Start(lasa))?; ++ let mut ccel = vec![0; laml as usize]; ++ let read_size = guest_memory.read(&mut ccel)?; ++ if read_size == 0 { ++ bail!("read CCEL failed"); ++ } ++ ++ Ok(ccel) ++} ++ ++#[cfg(test)] ++mod tests { ++ use super::read_ccel; ++ ++ #[ignore] ++ #[test] ++ fn test_read_ccel() { ++ let _ccel = read_ccel().unwrap(); ++ } ++} +diff --git a/service/attestation/attestation-service/verifier/src/lib.rs b/service/attestation/attestation-service/verifier/src/lib.rs +index 43894db..c2ef3bc 100644 +--- a/service/attestation/attestation-service/verifier/src/lib.rs ++++ b/service/attestation/attestation-service/verifier/src/lib.rs +@@ -16,7 +16,6 @@ + + use anyhow::*; + use async_trait::async_trait; +-use serde_json; + + use attestation_types::{Evidence, TeeType}; + +diff --git a/service/attestation/attestation-service/verifier/src/virtcca/ima.rs b/service/attestation/attestation-service/verifier/src/virtcca/ima.rs +index 220a52d..271c9d2 100644 +--- a/service/attestation/attestation-service/verifier/src/virtcca/ima.rs ++++ b/service/attestation/attestation-service/verifier/src/virtcca/ima.rs +@@ -9,6 +9,7 @@ + * PURPOSE. + * See the Mulan PSL v2 for more details. + */ ++use super::{file_reader, CVM_REM_ARR_SIZE}; + use anyhow::{bail, Result}; + use fallible_iterator::FallibleIterator; + use ima_measurements::{Event, EventData, Parser}; +@@ -27,7 +28,7 @@ const IMA_REFERENCE_FILE: &str = + pub struct ImaVerify {} + + impl ImaVerify { +- pub fn ima_verify(&self, ima_log: &[u8], ima_log_hash: Vec) -> Result { ++ pub fn ima_verify(&self, ima_log: &[u8], cvm_rem: &[Vec; CVM_REM_ARR_SIZE]) -> Result { + if ima_log.to_vec().is_empty() { + return Ok(json!({})); + } +@@ -38,10 +39,16 @@ impl ImaVerify { + events.push(event); + } + ++ let pcr_index = events[1].pcr_index; ++ let ima_index : usize = match (pcr_index-1).try_into() { ++ Ok(idx) => idx, ++ Err(_) => bail!("Invalid pcr_index for IMA"), ++ }; + let pcr_values = parser.pcr_values(); +- let pcr_10 = pcr_values.get(&10).expect("PCR 10 not measured"); +- let string_pcr_sha256 = hex::encode(pcr_10.sha256); +- let string_ima_log_hash = hex::encode(ima_log_hash); ++ let pcr_value = pcr_values.get(&pcr_index).expect("PCR not measured"); ++ let string_pcr_sha256 = hex::encode(pcr_value.sha256); ++ let string_ima_log_hash = hex::encode(cvm_rem[ima_index].clone()); ++ log::debug!("pcr_index: {}, string_pcr_sha256: {}, string_ima_log_hash: {}",pcr_index, string_pcr_sha256, string_ima_log_hash); + + if string_pcr_sha256.clone() != string_ima_log_hash { + log::error!( +@@ -49,13 +56,10 @@ impl ImaVerify { + string_pcr_sha256, + string_ima_log_hash + ); +- bail!("ima log hash verify failed"); ++ bail!("IMA log hash verification failed. Please check the log and reference data, and verify if PCR has been extended to PCR4."); + } + +- let ima_refs: Vec<_> = file_reader(IMA_REFERENCE_FILE)? +- .into_iter() +- .map(String::from) +- .collect(); ++ let ima_refs = file_reader(IMA_REFERENCE_FILE)?; + + let mut ima_detail = Map::new(); + // parser each file digest in ima log, and compare with reference base value +@@ -84,23 +88,3 @@ impl ImaVerify { + Ok(js_ima_detail) + } + } +- +-use std::io::BufRead; +-use std::io::BufReader; +-fn file_reader(file_path: &str) -> ::std::io::Result> { +- let file = std::fs::File::open(file_path).expect("open ima reference file failed"); +- let mut strings = Vec::::new(); +- let mut reader = BufReader::new(file); +- let mut buf = String::new(); +- let mut n: usize; +- loop { +- n = reader.read_line(&mut buf)?; +- if n == 0 { +- break; +- } +- buf.pop(); +- strings.push(buf.clone()); +- buf.clear(); +- } +- Ok(strings) +-} +diff --git a/service/attestation/attestation-service/verifier/src/virtcca/mod.rs b/service/attestation/attestation-service/verifier/src/virtcca/mod.rs +index ff72f77..a8c0959 100644 +--- a/service/attestation/attestation-service/verifier/src/virtcca/mod.rs ++++ b/service/attestation/attestation-service/verifier/src/virtcca/mod.rs +@@ -25,8 +25,9 @@ use openssl::rsa; + use openssl::x509; + use serde_json::json; + +-pub use attestation_types::VirtccaEvidence; ++pub use attestation_types::{UefiLog, VirtccaEvidence}; + pub mod ima; ++pub mod uefi; + + #[cfg(not(feature = "no_as"))] + const VIRTCCA_ROOT_CERT: &str = +@@ -125,29 +126,62 @@ impl Evidence { + + // verify ima + let ima_log = match virtcca_ev.ima_log { +- Some(ima_log) => ima_log, +- _ => { ++ Some(ima_log) => { ++ log::info!("get ima log"); ++ ima_log ++ } ++ None => { + log::info!("no ima log"); + vec![] + } + }; ++ + let ima: serde_json::Value = +- ima::ImaVerify::default().ima_verify(&ima_log, evidence.cvm_token.rem[0].clone())?; ++ ima::ImaVerify::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![], ++ } ++ } ++ } else { ++ log::info!("no uefi log at all"); ++ UefiLog { ++ ccel_table: vec![], ++ ccel_data: vec![], ++ } ++ }; ++ ++ let uefi: serde_json::Value = ++ uefi::UefiVerify::default().uefi_verify(uefi_log, evidence.cvm_token.rem.clone())?; + + // todo parsed TeeClaim +- evidence.parse_claim_from_evidence(ima) ++ evidence.parse_claim_from_evidence(ima, uefi) + } ++ + 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!(""); + // parsed TeeClaim +- let claim = evidence.parse_claim_from_evidence(ima).unwrap(); ++ let claim = evidence.parse_claim_from_evidence(ima, uefi).unwrap(); + Ok(claim["payload"].clone() as TeeClaim) + } +- fn parse_claim_from_evidence(&self, ima: serde_json::Value) -> Result { ++ fn parse_claim_from_evidence( ++ &self, ++ ima: serde_json::Value, ++ uefi: serde_json::Value, ++ ) -> Result { + let payload = json!({ + "vcca.cvm.challenge": hex::encode(self.cvm_token.challenge.clone()), + "vcca.cvm.rpv": hex::encode(self.cvm_token.rpv.clone()), +@@ -162,6 +196,7 @@ impl Evidence { + "tee": "vcca", + "payload" : payload, + "ima": ima, ++ "uefi": uefi, + }); + Ok(claim as TeeClaim) + } +@@ -242,6 +277,7 @@ impl Evidence { + + Ok(()) + } ++ + #[cfg(feature = "no_as")] + fn compare_with_ref(&mut self) -> Result<()> { + let ref_file = std::fs::read(VIRTCCA_REF_VALUE_FILE)?; +@@ -499,6 +535,7 @@ mod tests { + evidence: token.to_vec(), + dev_cert: dev_cert, + ima_log: None, ++ uefi_log: None, + }; + let virtcca_ev = serde_json::to_vec(&virtcca_ev).unwrap(); + let r = Evidence::verify(&challenge, &virtcca_ev); +@@ -508,3 +545,20 @@ mod tests { + } + } + } ++ ++use std::collections::HashSet; ++use std::fs::File; ++use std::io::{self, BufRead, BufReader}; ++ ++pub fn file_reader(file_path: &str) -> io::Result> { ++ let file = File::open(file_path)?; ++ let reader = BufReader::new(file); ++ let mut set = HashSet::new(); ++ ++ for line in reader.lines() { ++ let line = line?; ++ set.insert(line.trim_end().to_string()); ++ } ++ ++ Ok(set) ++} +diff --git a/service/attestation/attestation-service/verifier/src/virtcca/uefi.rs b/service/attestation/attestation-service/verifier/src/virtcca/uefi.rs +new file mode 100644 +index 0000000..e2397ae +--- /dev/null ++++ b/service/attestation/attestation-service/verifier/src/virtcca/uefi.rs +@@ -0,0 +1,209 @@ ++/* ++ * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. ++ * secGear is licensed under the Mulan PSL v2. ++ * You can use this software according to the terms and conditions of the Mulan PSL v2. ++ * You may obtain a copy of Mulan PSL v2 at: ++ * http://license.coscl.org.cn/MulanPSL2 ++ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR ++ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR ++ * PURPOSE. ++ * See the Mulan PSL v2 for more details. ++*/ ++ ++use super::{file_reader, CVM_REM_ARR_SIZE}; ++use anyhow::{bail, Result}; ++use attestation_types::UefiLog; ++use eventlog_rs::{self, Eventlog}; ++use hex; ++use log; ++use serde_json::{json, Map, Value}; ++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"; ++ ++// attestation agent local uefi reference ++#[cfg(feature = "no_as")] ++const UEFI_REFERENCE_FILE: &str = ++ "/etc/attestation/attestation-agent/local_verifier/virtcca/uefi/digest_list_file"; ++ ++#[derive(Debug)] ++pub struct FirmwareState { ++ pub grub_image_count: u8, ++ pub grub_image_list: Vec, ++ pub state_hash: HashMap, ++} ++ ++#[derive(Debug, Default)] ++pub struct UefiVerify {} ++ ++impl UefiVerify { ++ pub fn compare_rtmr_with_uefi_log( ++ _replayed_rtmr: &HashMap>, ++ uefi_log_hash: &[Vec; CVM_REM_ARR_SIZE], ++ ) -> bool { ++ for i in 1..CVM_REM_ARR_SIZE as u32 { ++ let index = i as usize - 1; ++ ++ // 获取 RTMR 值 ++ let Some(rtmr_value) = _replayed_rtmr.get(&i) else { ++ log::error!("RTMR[{}] not found in hashmap", i); ++ return false; ++ }; ++ ++ // 检查长度是否为 SHA256 (32 bytes) ++ if rtmr_value.len() != 32 { ++ log::error!("RTMR[{}] hash length invalid: {}", i, rtmr_value.len()); ++ return false; ++ } ++ ++ if uefi_log_hash[index].len() != 32 { ++ log::error!( ++ "UEFI_LOG_HASH[{}] hash length invalid: {}", ++ i, ++ uefi_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); ++ log::debug!("RTMR[{}] = {}", i, hex::encode(rtmr_value)); ++ log::debug!( ++ "UEFI_LOG_HASH[{}] = {}", ++ index, ++ hex::encode(&uefi_log_hash[index]) ++ ); ++ return false; ++ } ++ ++ log::debug!("RTMR[{}] = {}", i, hex::encode(rtmr_value)); ++ log::debug!( ++ "UEFI_LOG_HASH[{}] = {}", ++ index, ++ hex::encode(&uefi_log_hash[index]) ++ ); ++ } ++ ++ log::info!("All RTMR values match UEFI log hashes."); ++ true ++ } ++ ++ pub fn firmware_log_state(event_log: &Eventlog) -> FirmwareState { ++ let grub_event_type: &str = "EV_EFI_BOOT_SERVICES_APPLICATION"; ++ let exclude_event_descs: &[&str] = &["grub_cmd:"]; ++ let event_descs: HashMap<_, _> = HashMap::from([ ++ ("grub_cfg", "grub.cfg"), ++ ("kernel", "/vmlinuz-"), ++ ("initramfs", "/initramfs-"), ++ ]); ++ ++ let mut state = FirmwareState { ++ grub_image_count: 0, ++ grub_image_list: vec![], ++ state_hash: HashMap::new(), ++ }; ++ ++ //based event_type get grub image info ++ for event_entry in event_log.log.iter() { ++ if event_entry.event_type == grub_event_type { ++ state.grub_image_count += 1; ++ state ++ .grub_image_list ++ .push(hex::encode(event_entry.digests[0].digest.clone())); ++ } else { ++ let event_desc = match std::str::from_utf8(&event_entry.event_desc) { ++ Ok(s) => s, ++ Err(_) => continue, ++ }; ++ ++ if exclude_event_descs.iter().any(|&s| event_desc.contains(s)) { ++ continue; ++ } ++ ++ for (&key, &pattern) in event_descs.iter() { ++ if state.state_hash.contains_key(key) { ++ continue; ++ } ++ ++ if event_desc.contains(pattern) { ++ state.state_hash.insert( ++ key.to_string(), ++ hex::encode(event_entry.digests[0].digest.clone()), ++ ); ++ } ++ } ++ ++ if state.state_hash.len() == event_descs.len() { ++ break; ++ } ++ } ++ } ++ ++ state ++ } ++ ++ pub fn check_uefi_references( ++ firmware_state: &FirmwareState, ++ uefi_refs: &HashSet, ++ ) -> serde_json::Value { ++ let mut uefi_detail: Map = Map::new(); ++ ++ for (index, image) in firmware_state.grub_image_list.iter().enumerate() { ++ let exists = uefi_refs.contains(image); ++ let key = format!("Image[{}]", index); ++ uefi_detail.insert(key, Value::Bool(exists)); ++ if !exists { ++ log::debug!( ++ "GRUB Image[{}] ('{}') not found in UEFI reference set.", ++ index, ++ image ++ ); ++ } ++ } ++ ++ for (key, value) in firmware_state.state_hash.iter() { ++ let exists = uefi_refs.contains(value); ++ uefi_detail.insert(key.clone(), Value::Bool(exists)); ++ if !exists { ++ log::debug!("'{}' : '{}' not found in UEFI 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 ++ } ++ ++ pub fn uefi_verify( ++ &self, ++ uefi_log: UefiLog, ++ uefi_log_hash: [Vec; CVM_REM_ARR_SIZE], ++ ) -> Result { ++ if uefi_log.ccel_data.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(); ++ ++ 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"); ++ } ++ ++ let uefi_refs = file_reader(UEFI_REFERENCE_FILE)?; ++ log::debug!("uefi reference file: {:?}", uefi_refs); ++ ++ let firmware_state: FirmwareState = UefiVerify::firmware_log_state(&event_log); ++ log::debug!("firmware state: {:?}", firmware_state); ++ ++ Ok(UefiVerify::check_uefi_references( ++ &firmware_state, ++ &uefi_refs, ++ )) ++ } ++} +diff --git a/service/attestation/attestation-types/src/lib.rs b/service/attestation/attestation-types/src/lib.rs +index 82c124c..5f3d43b 100644 +--- a/service/attestation/attestation-types/src/lib.rs ++++ b/service/attestation/attestation-types/src/lib.rs +@@ -18,11 +18,18 @@ 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, + } + + #[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 d702061..be8c4f9 100644 +--- a/service/attestation/attestation-types/src/resource/policy/opa/mod.rs ++++ b/service/attestation/attestation-types/src/resource/policy/opa/mod.rs +@@ -329,6 +329,7 @@ mod tests { + "report": { + "default_vcca.rego": "{\"vcca.cvm.rim\":\"1ee366339c8245a34a8ad9d27a0b912a588af7da8aef514ae8dec22746956dd1\"}", + "ima": {} ++ "uefi": {} + } + }, + "tee": "vcca", +-- +2.49.0.windows.1 + diff --git a/secGear.spec b/secGear.spec index 5034f1fbe668f37064ce45bc54f5dd622f52925c..4273f4050f2b4aaa8faef1eb302a5ed18d3bd917 100644 --- a/secGear.spec +++ b/secGear.spec @@ -1,6 +1,6 @@ Name: secGear Version: 0.1.0 -Release: 55 +Release: 56 Summary: secGear is an SDK to develop confidential computing apps based on hardware enclave features @@ -111,6 +111,7 @@ Patch97: 0098-fix-avoid-clear-resource-policies-if-illegal-vendor-.patch Patch98: 0099-fix-permit-dots-in-the-resource-policy-id.patch Patch99: 0100-challenge-may-generate-by-requester-so-aa-and-as-may.patch Patch100: 0101-generate-random-by-ra_tls-itself.patch +Patch101: 0102-Add-support-for-UEFI-measured-boot-attestation.patch BuildRequires: gcc python automake autoconf libtool BUildRequires: glibc glibc-devel cmake ocaml-dune rpm gcc-c++ compat-openssl11-libs compat-openssl11-devel @@ -353,6 +354,9 @@ popd systemctl restart rsyslog %changelog +* Wed May 14 2025 SPYFAMILY - 0.1.0-56 +- Add support for UEFI measured boot attestation + * Thu May 15 2025 huyubiao - 0.1.0-55 - sync patches from upstream and sync with 25.03-0.1.0-59