From c948c67400a79e8fda5ba72addea40159f5ee8ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Be=C3=B1at=20Gartzia=20Arruabarrena?= Date: Wed, 15 Apr 2026 16:27:33 +0200 Subject: [PATCH] combine: Multiple events per event ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It was assumed that each TPMEventID would hold only 1 event at a time. Now that other PCR predictions and support for UKI are on the table, that does not happened to longer be true. This commit only updates internal plumbing. Pretty much the logic in tpmevents/combine so as to be able to process a list of TPMEvents per each kind of TPMEventID. It's now assumed that TPMEvents of a given TPMEventID move in block. In other words, that they can't be combined with TPMEvents from the other images independently. It's all TPMEvents of TPMEvent ID X from image A or from image B. It can't be TPMEvent 1 from image A and TPMEvent 2 from image B. Signed-off-by: BeƱat Gartzia Arruabarrena --- lib/src/tpmevents/combine.rs | 26 +++++++---- lib/src/tpmevents/combine/tests.rs | 69 ++++++++++++++++++++++++++++-- 2 files changed, 82 insertions(+), 13 deletions(-) diff --git a/lib/src/tpmevents/combine.rs b/lib/src/tpmevents/combine.rs index 47503eb..2c0986a 100644 --- a/lib/src/tpmevents/combine.rs +++ b/lib/src/tpmevents/combine.rs @@ -77,7 +77,7 @@ mod tests; #[derive(Clone, Debug)] pub struct EventCombinationError {} -pub type EventNode = tree::ResultNode; +pub type EventNode = tree::ResultNode, EventCombinationError>; pub fn combine_images(images: &[Vec]) -> Vec> { if images.len() == 1 { @@ -101,7 +101,7 @@ pub fn combine(this: &[TPMEvent], that: &[TPMEvent]) -> Vec> { Some(st) => st .iter() .flat_map(|t| t.valid_branches()) - .map(|e| compile_pcrs(&e)) + .map(|e| compile_pcrs(&e.into_iter().flatten().collect::>())) .collect(), None => vec![], } @@ -109,8 +109,8 @@ pub fn combine(this: &[TPMEvent], that: &[TPMEvent]) -> Vec> { fn event_subtree( event_id: &TPMEventID, - map_this: &HashMap, - map_that: &HashMap, + map_this: &HashMap>, + map_that: &HashMap>, group_this: u32, group_that: u32, ) -> Option> { @@ -119,7 +119,7 @@ fn event_subtree( let opt_this = map_this.get(event_id); let opt_that = map_that.get(event_id); // Divergences contains tuples with events, and this/that masked groups - let mut divs: Vec<(&TPMEvent, u32, u32)> = vec![]; + let mut divs: Vec<(&Vec, u32, u32)> = vec![]; let mut nodes: Vec = vec![]; let mut event_required = false; let event_groups = event_id.groups(); @@ -182,8 +182,8 @@ fn event_subtree( } } - for (event, g_this, g_that) in divs { - let mut node = EventNode::new_ok(event.clone()); + for (events, g_this, g_that) in divs { + let mut node = EventNode::new_ok(events.clone()); if let Some(children) = event_subtree(&event_id.next()?, map_this, map_that, g_this, g_that) { for c in children { @@ -196,6 +196,14 @@ fn event_subtree( Some(nodes) } -fn tpm_event_id_hashmap(events: &[TPMEvent]) -> HashMap { - events.iter().map(|e| (e.id.clone(), e.clone())).collect() +fn tpm_event_id_hashmap(events: &[TPMEvent]) -> HashMap> { + let mut lookup = HashMap::>::new(); + for event in events { + lookup + .entry(event.id.clone()) + .or_default() + .push(event.clone()); + } + + lookup } diff --git a/lib/src/tpmevents/combine/tests.rs b/lib/src/tpmevents/combine/tests.rs index 9afcf2f..c5c921c 100644 --- a/lib/src/tpmevents/combine/tests.rs +++ b/lib/src/tpmevents/combine/tests.rs @@ -30,15 +30,21 @@ fn test_tpm_event_id_hashmap() { hash: vec![1, 2, 3, 4, 5], id: TPMEventID::Pcr11UnameContent, }; - let events = vec![foo.clone(), bar.clone(), foobar.clone()]; + let foobaz = TPMEvent { + name: "FOOBAR".into(), + pcr: 0xe8, + hash: vec![6, 7, 8, 9, 10], + id: TPMEventID::Pcr11UnameContent, + }; + let events = vec![foo.clone(), bar.clone(), foobar.clone(), foobaz.clone()]; let res = tpm_event_id_hashmap(&events); assert_eq!( res, HashMap::from([ - (TPMEventID::PcrRootNodeEvent, foo), - (TPMEventID::Pcr11Sbat, bar), - (TPMEventID::Pcr11UnameContent, foobar), + (TPMEventID::PcrRootNodeEvent, vec![foo]), + (TPMEventID::Pcr11Sbat, vec![bar]), + (TPMEventID::Pcr11UnameContent, vec![foobar, foobaz]), ]) ); } @@ -1682,3 +1688,58 @@ fn test_all_pcrs_2_images() { ] ); } + +#[test] +fn test_combine_multiple_events_same_id() { + let this = vec![ + TPMEvent { + pcr: 4, + name: "EV_EFI_ACTION".to_string(), + hash: hex::decode("0000000000000000000000000000000000000000000000000000000000000000") + .unwrap(), + id: TPMEventID::Pcr4EfiCall, + }, + TPMEvent { + pcr: 4, + name: "EV_EFI_ACTION".to_string(), + hash: hex::decode("1111111111111111111111111111111111111111111111111111111111111111") + .unwrap(), + id: TPMEventID::Pcr4EfiCall, + }, + ]; + let that = vec![ + TPMEvent { + pcr: 4, + name: "EV_EFI_ACTION".to_string(), + hash: hex::decode("2222222222222222222222222222222222222222222222222222222222222222") + .unwrap(), + id: TPMEventID::Pcr4EfiCall, + }, + TPMEvent { + pcr: 4, + name: "EV_EFI_ACTION".to_string(), + hash: hex::decode("3333333333333333333333333333333333333333333333333333333333333333") + .unwrap(), + id: TPMEventID::Pcr4EfiCall, + }, + ]; + let foobar = vec![this, that]; + + let res = combine_images(&foobar); + let pcr_values: Vec> = res + .iter() + .map(|i| { + i.iter() + .map(|p| hex::encode(p.value.clone())) + .collect::>() + }) + .collect(); + + assert_eq!( + pcr_values, + vec![ + vec!["4cb4c04374037e0ddde15a714a4295501e2cbc3b0971e4c3eebce23ef433dc4d",], + vec!["22d0d408147e904fa9fe6feba5fc51374e44f07101e208e62fd5453841595ade",], + ], + ); +}