From 9fa32477d9b782afd92c436cb952345eedc89a02 Mon Sep 17 00:00:00 2001
From: xuce <xuce10@h-partners.com>
Date: Fri, 17 Oct 2025 16:07:24 +0800
Subject: [PATCH] dim support virtcca
Signed-off-by: xuce <xuce10@h-partners.com>
Cargo.lock | 16 ++++++++
Cargo.toml | 2 +
deps/verifier/Cargo.toml | 4 +-
deps/verifier/src/virtcca/dim.rs | 48 +++++++++++++++++++++++
deps/verifier/src/virtcca/mod.rs | 65 ++++++++++++++++++++++++++++++--
5 files changed, 131 insertions(+), 4 deletions(-)
create mode 100644 deps/verifier/src/virtcca/dim.rs
@@ -5960,6 +5960,7 @@ dependencies = [
"tonic-build",
"veraison-apiclient",
"x509-parser",
+ "ima-measurements",
]
[[package]]
@@ -6589,3 +6590,18 @@ dependencies = [
"cc",
"pkg-config",
]
+
+[[package]]
+name = "ima-measurements"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "352224641e2e2c25761629713f5059995750ad52ec59358d0f47cb71d1f836f2"
+dependencies = [
+ "byteorder",
+ "fallible-iterator",
+ "hex",
+ "serde",
+ "serde_yaml 0.8.26",
+ "thiserror 1.0.69",
+ "tpmless-tpm2",
+]
\ No newline at end of file
@@ -64,3 +64,5 @@ tokio = { version = "1", features = ["full"], default-features = false }
tempfile = "3.14.0"
tonic = "0.12"
tonic-build = "0.12"
+ima-measurements = "0.2.0"
+fallible-iterator = "0.2.0"
\ No newline at end of file
@@ -44,7 +44,7 @@ serde_json.workspace = true
serde_with = { workspace = true, optional = true }
sev = { version = "4.0.0", features = ["openssl", "snp"], optional = true }
sha2.workspace = true
-tokio = { workspace = true, optional = true }
+tokio = { workspace = true, features = ["full"] }
intel-tee-quote-verification-rs = { git = "https://github.com/intel/SGXDataCenterAttestationPrimitives", tag = "DCAP_1.22", optional = true }
strum.workspace = true
veraison-apiclient = { git = "https://github.com/chendave/rust-apiclient", branch = "token", optional = true }
@@ -54,6 +54,8 @@ reqwest.workspace = true
bitflags = { version = "2.8.0", features = ["serde"] }
cose-rust = { version = "0.1.7", optional = true }
ciborium = { version = "0.2.2", optional = true }
+ima-measurements.workspace = true
+fallible-iterator.workspace = true
[build-dependencies]
shadow-rs.workspace = true
new file mode 100644
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+//! DIM verifier module
+//!
+//! This module provides DIM (Dynamic Integrity Measurement) verification functionality
+//! for TEE attestation.
+
+use anyhow::{bail, Result};
+use ima_measurements::Event;
+
+/// Common function to parse DIM events
+/// which is shared between different TEE implementations.
+pub fn parse_dim_events(events: &[Event]) -> Result<Vec<String>> {
+ if events.len() < 2 {
+ bail!("No DIM measurement records for files found.");
+ }
+
+ let mut dim_digests = Vec::new();
+ let mut skip_first = true;
+ // parse each file digest in dim log
+ for event in events {
+ let (name, file_digest) = match &event.data {
+ ima_measurements::EventData::ImaNg { digest, name } => (name, &digest.digest),
+ _ => bail!("Invalid event {:?}", event),
+ };
+ if skip_first && name == "boot_aggregate" {
+ skip_first = false;
+ continue;
+ }
+
+ let hex_str_digest = hex::encode(file_digest);
+ dim_digests.push(hex_str_digest);
+ }
+
+ log::debug!("dim digests: {:?}", dim_digests);
+
+ Ok(dim_digests)
+}
\ No newline at end of file
@@ -17,6 +17,11 @@ use serde::Deserialize;
use cose::message::CoseMessage;
use cose::keys::CoseKey;
+use fallible_iterator::FallibleIterator;
+mod dim;
+use dim::parse_dim_events;
+use ima_measurements::{Event, Parser};
+
// Define constants for the tags
const TAG_CCA_TOKEN_COLLECTION: u64 = 399;
const TAG_CCA_REALM_TOKEN: i128 = 44241;
@@ -41,6 +46,7 @@ const CVM_ECC_PUB_KEY_SIZE: usize = 133;
pub struct VirtccaEvidence {
pub evidence_virtcca: Vec<u8>,
pub dev_cert: Vec<u8>,
+ pub dim_log: Vec<u8>
}
pub struct CcaCoseEvidence {
@@ -73,7 +79,6 @@ const SUB_CERT_ENV: &str = "VIRTCCA_SUB_CERT";
const ECC_ROOT_CERT_OF_DEVICE_CERT: &str = "/etc/attestation/attestation-service/verifier/virtcca/Huawei ECC Attestation Root CA 1.pem";
const ECC_SUB_CERT_OF_DEVICE_CERT: &str = "/etc/attestation/attestation-service/verifier/virtcca/Huawei Computing ECC CCA CA 1.pem";
-
#[derive(Debug, Default)]
pub struct VirtCCAVerifier {}
@@ -191,6 +196,12 @@ impl Verifier for VirtCCAVerifier {
realm_claims.challenge, expected_report_data);
}
+ // Verify the DIM events
+ let dim_digests = match dim_verify(&evidence.dim_log, &realm_claims.rem) {
+ Ok(result) => Some(result),
+ Err(err) => bail!("Failed to verify DIM events: {:?}", err),
+ };
+
// Not to verify if no init data from attester
if let InitDataHash::Value(expected_init_data_hash) = expected_init_data_hash {
let expected_init_data_hash = regularize_data(expected_init_data_hash, 64, "REPORT_DATA", "VIRTCCA");
@@ -201,7 +212,7 @@ impl Verifier for VirtCCAVerifier {
}
// Extract and parse TeeClaim from evidence_virtcca
- let tee_claim = build_tee_claims(realm_claims, platform_claims)
+ let tee_claim = build_tee_claims(realm_claims, platform_claims, dim_digests)
.context("Failed to TeeEvidenceParsedClaim")?;
Ok(tee_claim)
@@ -371,6 +382,53 @@ fn verify_evidence_integrity(dev_cert_is_ecc: bool, mut cose_envelop: CoseMessag
Ok(())
}
+fn dim_verify(dim_log: &[u8], cvm_rem: &[Vec<u8>]) -> Result<Vec<String>> {
+ if dim_log.is_empty() {
+ return Ok(Vec::new());
+ }
+
+ // Parse DIM events
+ let mut parser = Parser::new(dim_log);
+ let mut events: Vec<Event> = Vec::new();
+ while let Some(event) = parser.next()? {
+ events.push(event);
+ }
+
+ if events.len() < 2 {
+ bail!("No DIM measurement records for files found.");
+ }
+
+ let pcr_index = events[1].pcr_index;
+ if pcr_index < 1 || pcr_index > CVM_REM_ARR_SIZE as u32 {
+ bail!("Invalid pcr_index for DIM");
+ }
+
+ let dim_index = (pcr_index - 1) as usize;
+ let pcr_values = parser.pcr_values();
+ let pcr_value = pcr_values.get(&pcr_index).expect("PCR not measured");
+ let string_pcr_sha256 = hex::encode(pcr_value.sha256);
+ let string_dim_log_hash = hex::encode(cvm_rem[dim_index].clone());
+
+ log::debug!(
+ "pcr_index: {}, string_pcr_sha256: {}, string_dim_log_hash: {}",
+ pcr_index,
+ string_pcr_sha256,
+ string_dim_log_hash
+ );
+
+ if string_pcr_sha256 != string_dim_log_hash {
+ log::error!(
+ "dim log verify failed string_pcr_sha256 {}, string_dim_log_hash {}",
+ string_pcr_sha256,
+ string_dim_log_hash
+ );
+ bail!("DIM log hash verification failed. Please check the log and reference data, and verify if PCR has been extended to PCR4.");
+ }
+
+ // Use th+e common function to parse DIM events
+ parse_dim_events(&events)
+}
+
fn verify_dev_cert_chain(dev_cert: &x509::X509, sub_cert: &x509::X509, root_cert: &x509::X509) -> Result<Vec<u8>> {
// verify if pub key of dev_cert match expected_public_key
@@ -395,7 +453,7 @@ fn verify_dev_cert_chain(dev_cert: &x509::X509, sub_cert: &x509::X509, root_cert
Ok(dev_cert_pub_key_der)
}
-fn build_tee_claims(realm: CcaRealmClaims, platform: Option<CcaPlatformClaims>) -> Result<TeeEvidenceParsedClaim> {
+fn build_tee_claims(realm: CcaRealmClaims, platform: Option<CcaPlatformClaims>, dim_digests: Option<Vec<String>>) -> Result<TeeEvidenceParsedClaim> {
let claim = json!({
"realm": {
"challenge": hex::encode(realm.challenge.clone()),
@@ -412,6 +470,7 @@ fn build_tee_claims(realm: CcaRealmClaims, platform: Option<CcaPlatformClaims>)
"payload": platform.as_ref().map(|p| hex::encode(&p.payload)).unwrap_or_default(),
},
"report_data" : hex::encode(realm.challenge.clone()),
+ "dim": dim_digests.unwrap_or_default(),
});
Ok(claim as TeeEvidenceParsedClaim)
}
--
2.43.0