From 6d2e0eaca5408d55669dfc653bbb78bcc0a6d30b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Be=C3=B1at=20Gartzia=20Arruabarrena?= Date: Tue, 23 Dec 2025 08:33:31 +0100 Subject: [PATCH 1/6] rust: Update minimum toolchain to 1.88 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Minimum rust version was set to 1.85. Fedora is way above that threshold at the moment. Future EL releases will be above that as well. While on it, fix some of the linter errors that arise from the minimum version update. Signed-off-by: Beñat Gartzia Arruabarrena --- .github/workflows/lint.yml | 2 +- Cargo.toml | 2 +- test_utils/src/lib.rs | 24 +++++++++++------------- 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 09efcf63..2fc06111 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -22,7 +22,7 @@ concurrency: env: CARGO_TERM_COLOR: always # Pinned toolchain for linting - ACTIONS_LINTS_TOOLCHAIN: 1.85.0 + ACTIONS_LINTS_TOOLCHAIN: 1.88.0 jobs: linting: diff --git a/Cargo.toml b/Cargo.toml index 456eb488..c89220fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ resolver = "3" [workspace.package] edition = "2024" -rust-version = "1.85" +rust-version = "1.88" [workspace.dependencies] anyhow = "1.0.100" diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs index a87b9e97..5dee0c77 100644 --- a/test_utils/src/lib.rs +++ b/test_utils/src/lib.rs @@ -342,14 +342,14 @@ impl TestContext { let sa_src = workspace_root.join("config/rbac/service_account.yaml"); let sa_content = std::fs::read_to_string(&sa_src)? - .replace("namespace: system", &format!("namespace: {}", ns)); + .replace("namespace: system", &format!("namespace: {ns}")); let sa_dst = rbac_temp_dir.join("service_account.yaml"); std::fs::write(&sa_dst, sa_content)?; let role_path = rbac_temp_dir.join("role.yaml"); let role_content = std::fs::read_to_string(&role_path)?.replace( "name: trusted-cluster-operator-role", - &format!("name: {}-trusted-cluster-operator-role", ns), + &format!("name: {ns}-trusted-cluster-operator-role"), ); std::fs::write(&role_path, role_content)?; @@ -357,25 +357,25 @@ impl TestContext { let rb_content = std::fs::read_to_string(&rb_src)? .replace( "name: manager-rolebinding", - &format!("name: {}-manager-rolebinding", ns), + &format!("name: {ns}-manager-rolebinding"), ) .replace( "name: trusted-cluster-operator-role", - &format!("name: {}-trusted-cluster-operator-role", ns), + &format!("name: {ns}-trusted-cluster-operator-role"), ) - .replace("namespace: system", &format!("namespace: {}", ns)); + .replace("namespace: system", &format!("namespace: {ns}")); let rb_dst = rbac_temp_dir.join("role_binding.yaml"); std::fs::write(&rb_dst, rb_content)?; let le_role_src = workspace_root.join("config/rbac/leader_election_role.yaml"); let le_role_content = std::fs::read_to_string(&le_role_src)? - .replace("namespace: system", &format!("namespace: {}", ns)); + .replace("namespace: system", &format!("namespace: {ns}")); let le_role_dst = rbac_temp_dir.join("leader_election_role.yaml"); std::fs::write(&le_role_dst, le_role_content)?; let le_rb_src = workspace_root.join("config/rbac/leader_election_role_binding.yaml"); let le_rb_content = std::fs::read_to_string(&le_rb_src)? - .replace("namespace: system", &format!("namespace: {}", ns)); + .replace("namespace: system", &format!("namespace: {ns}")); let le_rb_dst = rbac_temp_dir.join("leader_election_role_binding.yaml"); std::fs::write(&le_rb_dst, le_rb_content)?; @@ -384,7 +384,7 @@ impl TestContext { r#"# SPDX-FileCopyrightText: Generated for testing # SPDX-License-Identifier: CC0-1.0 -namespace: {} +namespace: {ns} resources: - service_account.yaml @@ -392,8 +392,7 @@ resources: - role_binding.yaml - leader_election_role.yaml - leader_election_role_binding.yaml -"#, - ns +"# ); let temp_kustomization_path = rbac_temp_dir.join("kustomization.yaml"); @@ -416,7 +415,7 @@ resources: &self.test_name, "Updating CR manifest with publicTrusteeAddr" ); - let trustee_addr = format!("kbs-service.{}.svc.cluster.local:8080", ns); + let trustee_addr = format!("kbs-service.{ns}.svc.cluster.local:8080"); let cr_manifest_path = manifests_path.join("trusted_execution_cluster_cr.yaml"); let cr_content = std::fs::read_to_string(&cr_manifest_path)?; @@ -474,8 +473,7 @@ resources: .with_timeout(Duration::from_secs(60)) .with_interval(Duration::from_secs(5)) .with_error_message(format!( - "image-pcrs ConfigMap in the namespace {} not found", - ns + "image-pcrs ConfigMap in the namespace {ns} not found" )); let test_name_owned = self.test_name.clone(); From 338f05b50e183df4a5d7ce79a0860571b65856a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Be=C3=B1at=20Gartzia=20Arruabarrena?= Date: Tue, 23 Dec 2025 10:04:39 +0100 Subject: [PATCH 2/6] workspace: Add hex as a dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit And update those crates that were making use of it. Later we will also use it in some other crates. Signed-off-by: Beñat Gartzia Arruabarrena --- Cargo.toml | 1 + operator/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index c89220fa..4c62d507 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ clap = { version = "4.5.53", features = ["derive"] } clevis-pin-trustee-lib = { git = "https://github.com/latchset/clevis-pin-trustee" } compute-pcrs-lib = { git = "https://github.com/trusted-execution-clusters/compute-pcrs" } env_logger = "0.11.8" +hex = "0.4.3" ignition-config = "0.5.0" k8s-openapi = { version = "0.26.1", features = ["v1_33", "schemars"] } kube = { version = "2.0.1", default-features = false, features = ["derive", "runtime", "openssl-tls"] } diff --git a/operator/Cargo.toml b/operator/Cargo.toml index 08c6ded1..919cc019 100644 --- a/operator/Cargo.toml +++ b/operator/Cargo.toml @@ -16,7 +16,7 @@ trusted-cluster-operator-lib = { path = "../lib" } compute-pcrs-lib.workspace = true env_logger.workspace = true futures-util = "0.3.31" -hex = "0.4.3" +hex.workspace = true json-patch = "4.1.0" jsonptr = "0.7.1" k8s-openapi.workspace = true From cdadc28eebff39bb48a2d18b342b1ae1bc70a317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Be=C3=B1at=20Gartzia=20Arruabarrena?= Date: Tue, 23 Dec 2025 10:30:14 +0100 Subject: [PATCH 3/6] bump: bring latest compute-pcrs in MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I broke some of the compute-pcrs' lib APIs, so apart from just pointing into a newer commit, we also need to tweak a couple of things here and there. Signed-off-by: Beñat Gartzia Arruabarrena --- Cargo.lock | 89 +++++++++++++++++++++++++++--- operator/src/mock_client.rs | 8 +-- operator/src/trustee.rs | 14 ++--- tests/Cargo.toml | 1 + tests/trusted_execution_cluster.rs | 49 ++++++++-------- 5 files changed, 119 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5fead726..8aef8427 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -427,16 +427,20 @@ dependencies = [ [[package]] name = "compute-pcrs-lib" version = "0.1.0" -source = "git+https://github.com/trusted-execution-clusters/compute-pcrs#1e7b9f74206e436d1426c335e30b2f1a6bd1681e" +source = "git+https://github.com/trusted-execution-clusters/compute-pcrs#8294089eee8dc44bc38c1c6a2cc1a2605d107c89" dependencies = [ "anyhow", "glob", "hex", "hex-literal", + "itertools 0.14.0", "lief", + "log", "openssl", "serde", + "serde_with", "sha2", + "strum", "uuid", ] @@ -659,6 +663,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" dependencies = [ "powerfmt", + "serde_core", ] [[package]] @@ -863,7 +868,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1634,6 +1639,8 @@ checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", "hashbrown 0.16.0", + "serde", + "serde_core", ] [[package]] @@ -1669,7 +1676,7 @@ checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ "hermit-abi", "libc", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -1702,6 +1709,15 @@ dependencies = [ "either", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" @@ -1800,7 +1816,7 @@ checksum = "06d9e5e61dd037cdc51da0d7e2b2be10f497478ea7e120d85dad632adb99882b" dependencies = [ "base64 0.22.1", "chrono", - "schemars", + "schemars 1.1.0", "serde", "serde_json", ] @@ -1866,7 +1882,7 @@ dependencies = [ "http 1.4.0", "json-patch", "k8s-openapi", - "schemars", + "schemars 1.1.0", "serde", "serde-value", "serde_json", @@ -2941,7 +2957,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3005,6 +3021,18 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + [[package]] name = "schemars" version = "1.1.0" @@ -3190,6 +3218,37 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fa237f2807440d238e0364a218270b98f767a00d3dada77b1c53ae88940e2e7" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.12.0", + "schemars 0.9.0", + "schemars 1.1.0", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52a8e3ca0ca629121f70ab50f95249e5a6f925cc0f6ffe8256c45b728875706c" +dependencies = [ + "darling 0.21.3", + "proc-macro2", + "quote", + "syn 2.0.110", +] + [[package]] name = "serde_yaml" version = "0.9.34+deprecated" @@ -3369,6 +3428,9 @@ name = "strum" version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros", +] [[package]] name = "strum_macros" @@ -3495,7 +3557,7 @@ dependencies = [ "getrandom 0.3.4", "once_cell", "rustix", - "windows-sys 0.61.2", + "windows-sys 0.59.0", ] [[package]] @@ -3566,10 +3628,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" dependencies = [ "deranged", + "itoa", "num-conv", "powerfmt", "serde", "time-core", + "time-macros", ] [[package]] @@ -3578,6 +3642,16 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + [[package]] name = "tinystr" version = "0.8.2" @@ -3799,6 +3873,7 @@ version = "0.1.0" dependencies = [ "anyhow", "compute-pcrs-lib", + "hex", "k8s-openapi", "kube", "regex", diff --git a/operator/src/mock_client.rs b/operator/src/mock_client.rs index 98d30963..a0ef794b 100644 --- a/operator/src/mock_client.rs +++ b/operator/src/mock_client.rs @@ -157,13 +157,13 @@ pub fn dummy_pcrs() -> ImagePcrs { pcrs: vec![ Pcr { id: 0, - value: "pcr0_val".to_string(), - parts: vec![], + value: "pcr0_val".into(), + events: vec![], }, Pcr { id: 1, - value: "pcr1_val".to_string(), - parts: vec![], + value: "pcr1_val".into(), + events: vec![], }, ], reference: "ref".to_string(), diff --git a/operator/src/trustee.rs b/operator/src/trustee.rs index 624ac34d..4d6374cc 100644 --- a/operator/src/trustee.rs +++ b/operator/src/trustee.rs @@ -72,7 +72,7 @@ fn recompute_reference_values(image_pcrs: ImagePcrs) -> Vec { reference_values_in .entry(format!("pcr{}", pcr.id)) .or_default() - .push(JsonString(pcr.value.clone())); + .push(JsonString(hex::encode(pcr.value.clone()))); } reference_values_in .iter() @@ -412,13 +412,13 @@ mod tests { pcrs: vec![ Pcr { id: 0, - value: "pcr0_val".to_string(), - parts: vec![], + value: "pcr0_val".into(), + events: vec![], }, Pcr { id: 1, - value: "pcr1_val".to_string(), - parts: vec![], + value: "pcr1_val".into(), + events: vec![], }, ], reference: "".to_string(), @@ -442,7 +442,7 @@ mod tests { let config_map = dummy_pcrs_map(); let image_pcrs = get_image_pcrs(config_map).unwrap(); assert_eq!(image_pcrs.0["cos"].pcrs.len(), 2); - assert_eq!(image_pcrs.0["cos"].pcrs[0].value, "pcr0_val"); + assert_eq!(image_pcrs.0["cos"].pcrs[0].value, "pcr0_val".as_bytes()); } #[test] @@ -479,7 +479,7 @@ mod tests { let rv = result.iter().find(|rv| rv.name == "tpm_pcr0").unwrap(); let val_arr = rv.value.as_array().unwrap(); let vals: Vec<_> = val_arr.iter().map(|v| v.as_str().unwrap()).collect(); - assert_eq!(vals, vec!["pcr0_val".to_string()]); + assert_eq!(vals, vec![hex::encode("pcr0_val")]); } #[tokio::test] diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 8d6bd795..251918c4 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -17,6 +17,7 @@ anyhow.workspace = true trusted-cluster-operator-lib = { path = "../lib" } trusted-cluster-operator-test-utils = { path = "../test_utils" } compute-pcrs-lib.workspace = true +hex.workspace = true k8s-openapi.workspace = true kube = { workspace = true } regex = "1" diff --git a/tests/trusted_execution_cluster.rs b/tests/trusted_execution_cluster.rs index 752b0e82..6782ad8a 100644 --- a/tests/trusted_execution_cluster.rs +++ b/tests/trusted_execution_cluster.rs @@ -2,7 +2,8 @@ // // SPDX-License-Identifier: MIT -use compute_pcrs_lib::{Part, Pcr}; +use compute_pcrs_lib::Pcr; +use compute_pcrs_lib::tpmevents::{TPMEvent, TPMEventID}; use k8s_openapi::api::apps::v1::Deployment; use k8s_openapi::api::core::v1::ConfigMap; use kube::{Api, api::DeleteParams}; @@ -95,37 +96,37 @@ async fn test_image_pcrs_configmap_updates() -> anyhow::Result<()> { let expected_pcrs = vec![ Pcr { id: 4, - value: EXPECTED_PCR4.to_string(), - parts: vec![ - Part { name: "EV_EFI_ACTION".to_string(), hash: "3d6772b4f84ed47595d72a2c4c5ffd15f5bb72c7507fe26f2aaee2c69d5633ba".to_string() }, - Part { name: "EV_SEPARATOR".to_string(), hash: "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119".to_string() }, - Part { name: "EV_EFI_BOOT_SERVICES_APPLICATION".to_string(), hash: "94896c17d49fc8c8df0cc2836611586edab1615ce7cb58cf13fc5798de56b367".to_string() }, - Part { name: "EV_EFI_BOOT_SERVICES_APPLICATION".to_string(), hash: "bc6844fc7b59b4f0c7da70a307fc578465411d7a2c34b0f4dc2cc154c873b644".to_string() }, - Part { name: "EV_EFI_BOOT_SERVICES_APPLICATION".to_string(), hash: "2b1dc59bc61dbbc3db11a6f3b0708c948efd46cceb7f6c8ea2024b8d1b8c829a".to_string() }, + value: hex::decode(EXPECTED_PCR4).unwrap(), + events: vec![ + TPMEvent { pcr: 4, name: "EV_EFI_ACTION".to_string(), hash: hex::decode("3d6772b4f84ed47595d72a2c4c5ffd15f5bb72c7507fe26f2aaee2c69d5633ba").unwrap(), id: TPMEventID::Pcr4EfiCall }, + TPMEvent { pcr: 4, name: "EV_SEPARATOR".to_string(), hash: hex::decode("df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119").unwrap(), id: TPMEventID::Pcr4Separator }, + TPMEvent { pcr: 4, name: "EV_EFI_BOOT_SERVICES_APPLICATION".to_string(), hash: hex::decode("94896c17d49fc8c8df0cc2836611586edab1615ce7cb58cf13fc5798de56b367").unwrap(), id: TPMEventID::Pcr4Shim }, + TPMEvent { pcr: 4, name: "EV_EFI_BOOT_SERVICES_APPLICATION".to_string(), hash: hex::decode("bc6844fc7b59b4f0c7da70a307fc578465411d7a2c34b0f4dc2cc154c873b644").unwrap(), id: TPMEventID::Pcr4Grub }, + TPMEvent { pcr: 4, name: "EV_EFI_BOOT_SERVICES_APPLICATION".to_string(), hash: hex::decode("2b1dc59bc61dbbc3db11a6f3b0708c948efd46cceb7f6c8ea2024b8d1b8c829a").unwrap(), id: TPMEventID::Pcr4Vmlinuz }, ], }, Pcr { id: 7, - value: "b3a56a06c03a65277d0a787fcabc1e293eaa5d6dd79398f2dda741f7b874c65d".to_string(), - parts: vec![ - Part { name: "EV_EFI_VARIABLE_DRIVER_CONFIG".to_string(), hash: "ccfc4bb32888a345bc8aeadaba552b627d99348c767681ab3141f5b01e40a40e".to_string() }, - Part { name: "EV_EFI_VARIABLE_DRIVER_CONFIG".to_string(), hash: "adb6fc232943e39c374bf4782b6c697f43c39fca1f4b51dfceda21164e19a893".to_string() }, - Part { name: "EV_EFI_VARIABLE_DRIVER_CONFIG".to_string(), hash: "b5432fe20c624811cb0296391bfdf948ebd02f0705ab8229bea09774023f0ebf".to_string() }, - Part { name: "EV_EFI_VARIABLE_DRIVER_CONFIG".to_string(), hash: "4313e43de720194a0eabf4d6415d42b5a03a34fdc47bb1fc924cc4e665e6893d".to_string() }, - Part { name: "EV_EFI_VARIABLE_DRIVER_CONFIG".to_string(), hash: "001004ba58a184f09be6c1f4ec75a246cc2eefa9637b48ee428b6aa9bce48c55".to_string() }, - Part { name: "EV_SEPARATOR".to_string(), hash: "df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119".to_string() }, - Part { name: "EV_EFI_VARIABLE_AUTHORITY".to_string(), hash: "4d4a8e2c74133bbdc01a16eaf2dbb5d575afeb36f5d8dfcf609ae043909e2ee9".to_string() }, - Part { name: "EV_EFI_VARIABLE_AUTHORITY".to_string(), hash: "e8e9578f5951ef16b1c1aa18ef02944b8375ec45ed4b5d8cdb30428db4a31016".to_string() }, - Part { name: "EV_EFI_VARIABLE_AUTHORITY".to_string(), hash: "ad5901fd581e6640c742c488083b9ac2c48255bd28a16c106c6f9df52702ee3f".to_string() }, + value: hex::decode("b3a56a06c03a65277d0a787fcabc1e293eaa5d6dd79398f2dda741f7b874c65d").unwrap(), + events: vec![ + TPMEvent { pcr: 7, name: "EV_EFI_VARIABLE_DRIVER_CONFIG".to_string(), hash: hex::decode("ccfc4bb32888a345bc8aeadaba552b627d99348c767681ab3141f5b01e40a40e").unwrap(), id: TPMEventID::Pcr7SecureBoot }, + TPMEvent { pcr: 7, name: "EV_EFI_VARIABLE_DRIVER_CONFIG".to_string(), hash: hex::decode("adb6fc232943e39c374bf4782b6c697f43c39fca1f4b51dfceda21164e19a893").unwrap(), id: TPMEventID::Pcr7Pk }, + TPMEvent { pcr: 7, name: "EV_EFI_VARIABLE_DRIVER_CONFIG".to_string(), hash: hex::decode("b5432fe20c624811cb0296391bfdf948ebd02f0705ab8229bea09774023f0ebf").unwrap(), id: TPMEventID::Pcr7Kek }, + TPMEvent { pcr: 7, name: "EV_EFI_VARIABLE_DRIVER_CONFIG".to_string(), hash: hex::decode("4313e43de720194a0eabf4d6415d42b5a03a34fdc47bb1fc924cc4e665e6893d").unwrap(), id: TPMEventID::Pcr7Db }, + TPMEvent { pcr: 7, name: "EV_EFI_VARIABLE_DRIVER_CONFIG".to_string(), hash: hex::decode("001004ba58a184f09be6c1f4ec75a246cc2eefa9637b48ee428b6aa9bce48c55").unwrap(), id: TPMEventID::Pcr7Dbx }, + TPMEvent { pcr: 7, name: "EV_SEPARATOR".to_string(), hash: hex::decode("df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119").unwrap(), id: TPMEventID::Pcr7Separator }, + TPMEvent { pcr: 7, name: "EV_EFI_VARIABLE_AUTHORITY".to_string(), hash: hex::decode("4d4a8e2c74133bbdc01a16eaf2dbb5d575afeb36f5d8dfcf609ae043909e2ee9").unwrap(), id: TPMEventID::Pcr7ShimCert }, + TPMEvent { pcr: 7, name: "EV_EFI_VARIABLE_AUTHORITY".to_string(), hash: hex::decode("e8e9578f5951ef16b1c1aa18ef02944b8375ec45ed4b5d8cdb30428db4a31016").unwrap(), id: TPMEventID::Pcr7SbatLevel }, + TPMEvent { pcr: 7, name: "EV_EFI_VARIABLE_AUTHORITY".to_string(), hash: hex::decode("ad5901fd581e6640c742c488083b9ac2c48255bd28a16c106c6f9df52702ee3f").unwrap(), id: TPMEventID::Pcr7GrubMokListCert }, ], }, Pcr { id: 14, - value: "17cdefd9548f4383b67a37a901673bf3c8ded6f619d36c8007562de1d93c81cc".to_string(), - parts: vec![ - Part { name: "EV_IPL".to_string(), hash: "e8e48e3ad10bc243341b4663c0057aef0ec7894ccc9ecb0598f0830fa57f7220".to_string() }, - Part { name: "EV_IPL".to_string(), hash: "8d8a3aae50d5d25838c95c034aadce7b548c9a952eb7925e366eda537c59c3b0".to_string() }, - Part { name: "EV_IPL".to_string(), hash: "4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a".to_string() }, + value: hex::decode("17cdefd9548f4383b67a37a901673bf3c8ded6f619d36c8007562de1d93c81cc").unwrap(), + events: vec![ + TPMEvent { pcr: 14, name: "EV_IPL".to_string(), hash: hex::decode("e8e48e3ad10bc243341b4663c0057aef0ec7894ccc9ecb0598f0830fa57f7220").unwrap(), id: TPMEventID::Pcr14MokList }, + TPMEvent { pcr: 14, name: "EV_IPL".to_string(), hash: hex::decode("8d8a3aae50d5d25838c95c034aadce7b548c9a952eb7925e366eda537c59c3b0").unwrap(), id: TPMEventID::Pcr14MokListX }, + TPMEvent { pcr: 14, name: "EV_IPL".to_string(), hash: hex::decode("4bf5122f344554c53bde2ebb8cd2b7e3d1600ad631c385a5d7cce23c7785459a").unwrap(), id: TPMEventID::Pcr14MokListTrusted }, ], }, ]; From 1ca3a19329c32368a4f7b57b38fe497745ac36c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Be=C3=B1at=20Gartzia=20Arruabarrena?= Date: Mon, 22 Dec 2025 16:41:22 +0100 Subject: [PATCH 4/6] trustee.tests: Make dummy_pcrs a bit less dummy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit trustee tests were relying on some values that didn't hold any TPMEvent or any PCR value that related to them. This commit turns those dummy values into something that are closer to something we could expect in reality. The main reason to do this is that the compute-pcrs logic does not like empty event PCRs, as it's based on that to reconstruct them. This goes against the purpose of a unit test, as we should actually mock that external part so as not to rely on it, but decided this was simpler at least as an initial proposal. Signed-off-by: Beñat Gartzia Arruabarrena --- operator/src/trustee.rs | 67 ++++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/operator/src/trustee.rs b/operator/src/trustee.rs index 4d6374cc..3d2ee6a2 100644 --- a/operator/src/trustee.rs +++ b/operator/src/trustee.rs @@ -401,6 +401,7 @@ mod tests { use super::*; use crate::mock_client::*; use compute_pcrs_lib::Pcr; + use compute_pcrs_lib::tpmevents::{TPMEvent, TPMEventID}; use http::{Method, Request, StatusCode}; use kube::client::Body; @@ -411,14 +412,36 @@ mod tests { first_seen: Utc::now(), pcrs: vec![ Pcr { - id: 0, - value: "pcr0_val".into(), - events: vec![], + id: 4, + value: hex::decode( + "3f263b96ccbc33bb53d808771f9ab1e02d4dec8854f9530f749cde853a723273", + ) + .unwrap(), + events: vec![TPMEvent { + name: "EV_EFI_ACTION".into(), + pcr: 4, + hash: hex::decode( + "3d6772b4f84ed47595d72a2c4c5ffd15f5bb72c7507fe26f2aaee2c69d5633ba", + ) + .unwrap(), + id: TPMEventID::Pcr4EfiCall, + }], }, Pcr { - id: 1, - value: "pcr1_val".into(), - events: vec![], + id: 7, + value: hex::decode( + "e58ada1ba75f2e4722b539824598ad5e10c55f2e4aeab2033f3b0a8ee3f3eca6", + ) + .unwrap(), + events: vec![TPMEvent { + name: "EV_EFI_VARIABLE_DRIVER_CONFIG".into(), + pcr: 7, + hash: hex::decode( + "ccfc4bb32888a345bc8aeadaba552b627d99348c767681ab3141f5b01e40a40e", + ) + .unwrap(), + id: TPMEventID::Pcr7SecureBoot, + }], }, ], reference: "".to_string(), @@ -437,12 +460,28 @@ mod tests { } } + fn reference_values_from(reference_values: &[ReferenceValue], rv_name: &str) -> Vec { + let rv = reference_values + .iter() + .find(|rv| rv.name == rv_name) + .unwrap(); + let val_arr = rv.value.as_array().unwrap(); + val_arr.iter().map(|v| v.as_str().unwrap().into()).collect() + } + #[test] fn test_get_image_pcrs_success() { let config_map = dummy_pcrs_map(); let image_pcrs = get_image_pcrs(config_map).unwrap(); assert_eq!(image_pcrs.0["cos"].pcrs.len(), 2); - assert_eq!(image_pcrs.0["cos"].pcrs[0].value, "pcr0_val".as_bytes()); + assert_eq!( + hex::encode(image_pcrs.0["cos"].pcrs[0].value.clone()), + "3f263b96ccbc33bb53d808771f9ab1e02d4dec8854f9530f749cde853a723273" + ); + assert_eq!( + hex::encode(image_pcrs.0["cos"].pcrs[1].value.clone()), + "e58ada1ba75f2e4722b539824598ad5e10c55f2e4aeab2033f3b0a8ee3f3eca6" + ); } #[test] @@ -476,10 +515,16 @@ mod tests { fn test_recompute_reference_values() { let result = recompute_reference_values(dummy_pcrs()); assert_eq!(result.len(), 3); - let rv = result.iter().find(|rv| rv.name == "tpm_pcr0").unwrap(); - let val_arr = rv.value.as_array().unwrap(); - let vals: Vec<_> = val_arr.iter().map(|v| v.as_str().unwrap()).collect(); - assert_eq!(vals, vec![hex::encode("pcr0_val")]); + let vals = reference_values_from(&result, "tpm_pcr4"); + assert_eq!( + vals, + vec!["3f263b96ccbc33bb53d808771f9ab1e02d4dec8854f9530f749cde853a723273",] + ); + let vals = reference_values_from(&result, "tpm_pcr7"); + assert_eq!( + vals, + vec!["e58ada1ba75f2e4722b539824598ad5e10c55f2e4aeab2033f3b0a8ee3f3eca6",] + ); } #[tokio::test] From 75880197d85fb01f19c6aeb0a923c1fa1dbd0260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Be=C3=B1at=20Gartzia=20Arruabarrena?= Date: Mon, 22 Dec 2025 17:11:27 +0100 Subject: [PATCH 5/6] trustee: Combine approved image PCR values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update trustee reference values with all possible combinations between approved PCR values, not just those that come directly from image PCRs. This computes reference values for possible stages during node updates in which a node could be booting some components from image A and some other from image B. This commit also adds a test to check that pcr4 is well covered in front of bootloader and kernel updates. The test is closer to an integration test than to an unit-test as it relies on values that are close to real values, and how the compute-pcrs lib's combine_images processes them. Signed-off-by: Beñat Gartzia Arruabarrena --- operator/src/trustee.rs | 129 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 126 insertions(+), 3 deletions(-) diff --git a/operator/src/trustee.rs b/operator/src/trustee.rs index 3d2ee6a2..8dba0c37 100644 --- a/operator/src/trustee.rs +++ b/operator/src/trustee.rs @@ -7,6 +7,8 @@ use anyhow::{Context, Result}; use base64::{Engine as _, engine::general_purpose}; use clevis_pin_trustee_lib::Key as ClevisKey; +use compute_pcrs_lib::tpmevents::TPMEvent; +use compute_pcrs_lib::tpmevents::combine::combine_images; use k8s_openapi::api::apps::v1::{Deployment, DeploymentSpec}; use k8s_openapi::api::core::v1::{ ConfigMap, ConfigMapVolumeSource, Container, ContainerPort, EmptyDirVolumeSource, PodSpec, @@ -65,10 +67,16 @@ pub fn get_image_pcrs(image_pcrs_map: ConfigMap) -> Result { } fn recompute_reference_values(image_pcrs: ImagePcrs) -> Vec { - // TODO many grub+shim:many OS image recompute once supported let mut reference_values_in = BTreeMap::from([("svn".to_string(), vec![JsonString("1".to_string())])]); - for pcr in image_pcrs.0.values().flat_map(|v| &v.pcrs) { + let tpm_events: Vec> = image_pcrs + .0 + .values() + .map(|v| v.pcrs.iter().flat_map(|p| p.events.clone()).collect()) + .collect(); + + let pcr_combinations = combine_images(&tpm_events); + for pcr in pcr_combinations.iter().flatten() { reference_values_in .entry(format!("pcr{}", pcr.id)) .or_default() @@ -401,7 +409,7 @@ mod tests { use super::*; use crate::mock_client::*; use compute_pcrs_lib::Pcr; - use compute_pcrs_lib::tpmevents::{TPMEvent, TPMEventID}; + use compute_pcrs_lib::tpmevents::TPMEventID; use http::{Method, Request, StatusCode}; use kube::client::Body; @@ -785,4 +793,119 @@ mod tests { let clos = |client| generate_kbs_deployment(client, Default::default(), "image"); test_create_error(clos).await; } + + #[test] + fn test_recompute_reference_values_pcr4() { + let image_pcrs = ImagePcrs(BTreeMap::from([ + ( + "cos1".to_string(), + ImagePcr { + first_seen: Utc::now(), + pcrs: vec![Pcr { + id: 4, + value: hex::decode("852718920421131081032051205110114719423559841238794129122376912159784392212168").unwrap(), + events: vec![ + TPMEvent { + name: "EV_EFI_ACTION".into(), + pcr: 4, + hash: hex::decode("3d6772b4f84ed47595d72a2c4c5ffd15f5bb72c7507fe26f2aaee2c69d5633ba") + .unwrap(), + id: TPMEventID::Pcr4EfiCall, + }, + TPMEvent { + name: "EV_SEPARATOR".into(), + pcr: 4, + hash: hex::decode("df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119") + .unwrap(), + id: TPMEventID::Pcr4Separator, + }, + TPMEvent { + name: "EV_EFI_BOOT_SERVICES_APPLICATION".into(), + pcr: 4, + hash: hex::decode("94896c17d49fc8c8df0cc2836611586edab1615ce7cb58cf13fc5798de56b367") + .unwrap(), + id: TPMEventID::Pcr4Shim, + }, + TPMEvent { + name: "EV_EFI_BOOT_SERVICES_APPLICATION".into(), + pcr: 4, + hash: hex::decode("bc6844fc7b59b4f0c7da70a307fc578465411d7a2c34b0f4dc2cc154c873b644") + .unwrap(), + id: TPMEventID::Pcr4Grub, + }, + TPMEvent { + name: "EV_EFI_BOOT_SERVICES_APPLICATION".into(), + pcr: 4, + hash: hex::decode("2b1dc59bc61dbbc3db11a6f3b0708c948efd46cceb7f6c8ea2024b8d1b8c829a") + .unwrap(), + id: TPMEventID::Pcr4Vmlinuz, + }, + ], + }], + reference: "".to_string(), + }, + ), + ( + "cos2".to_string(), + ImagePcr { + first_seen: Utc::now(), + pcrs: vec![Pcr { + id: 4, + value: hex::decode("19925299236966772216371371471692276818611442625320115173412649113251558526237189").unwrap(), + events: vec![ + TPMEvent { + name: "EV_EFI_ACTION".into(), + pcr: 4, + hash: hex::decode("3d6772b4f84ed47595d72a2c4c5ffd15f5bb72c7507fe26f2aaee2c69d5633ba") + .unwrap(), + id: TPMEventID::Pcr4EfiCall, + }, + TPMEvent { + name: "EV_SEPARATOR".into(), + pcr: 4, + hash: hex::decode("df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119") + .unwrap(), + id: TPMEventID::Pcr4Separator, + }, + TPMEvent { + name: "EV_EFI_BOOT_SERVICES_APPLICATION".into(), + pcr: 4, + hash: hex::decode("1fed6fad5ca735adc80615d2a7e795e2f17f84e407b07979498c9edb1e04383f") + .unwrap(), + id: TPMEventID::Pcr4Shim, + }, + TPMEvent { + name: "EV_EFI_BOOT_SERVICES_APPLICATION".into(), + pcr: 4, + hash: hex::decode("8f3adc6b42da2defa6d5ef3202badc39a5a22ceec068f106760592163a505a0e") + .unwrap(), + id: TPMEventID::Pcr4Grub, + }, + TPMEvent { + name: "EV_EFI_BOOT_SERVICES_APPLICATION".into(), + pcr: 4, + hash: hex::decode("772c3a90820e4a76944d3715e6f700bc41e846b0049b7817f9feb3289a56d3f8") + .unwrap(), + id: TPMEventID::Pcr4Vmlinuz, + }, + ], + }], + reference: "".to_string(), + }, + ), + ])); + + let result = recompute_reference_values(image_pcrs); + assert_eq!(result.len(), 2); + let vals = reference_values_from(&result, "tpm_pcr4"); + assert_eq!( + vals, + vec![ + "551bbd142a716c67cd78336593c2eb3b547b575e810ced4501d761082b5cd4a8", + "c9c3add791efc98f59977c89e673a34ad0b357872e9eb2c43d14607488e5d9e2", + "47b742b3a2244cc7249ff3221ec640198044aac533a95abafded7921237508c1", + "c7fc63ec604348d8258993a9e344ba72041afd1473ad291a3171199b551aedbd", + ] + ); + } } From 0c7a05fbd778c07c511a26a4ca6cd0855479515b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Be=C3=B1at=20Gartzia=20Arruabarrena?= Date: Mon, 22 Dec 2025 17:17:00 +0100 Subject: [PATCH 6/6] WORKAROUND: handle unique image combination case This is an edge case that the compute-pcrs library should be able to handle by itself. A PR with a fix has been opened, but not merged yet https://github.com/trusted-execution-clusters/compute-pcrs/pull/56 Once fixed and merged, I will remove this workaround. --- operator/src/trustee.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/operator/src/trustee.rs b/operator/src/trustee.rs index 8dba0c37..844b3cc2 100644 --- a/operator/src/trustee.rs +++ b/operator/src/trustee.rs @@ -7,6 +7,7 @@ use anyhow::{Context, Result}; use base64::{Engine as _, engine::general_purpose}; use clevis_pin_trustee_lib::Key as ClevisKey; +use compute_pcrs_lib::pcrs::compile_pcrs; use compute_pcrs_lib::tpmevents::TPMEvent; use compute_pcrs_lib::tpmevents::combine::combine_images; use k8s_openapi::api::apps::v1::{Deployment, DeploymentSpec}; @@ -75,7 +76,10 @@ fn recompute_reference_values(image_pcrs: ImagePcrs) -> Vec { .map(|v| v.pcrs.iter().flat_map(|p| p.events.clone()).collect()) .collect(); - let pcr_combinations = combine_images(&tpm_events); + let pcr_combinations = match image_pcrs.0.len() { + 1 => vec![compile_pcrs(&tpm_events[0])], + _ => combine_images(&tpm_events), + }; for pcr in pcr_combinations.iter().flatten() { reference_values_in .entry(format!("pcr{}", pcr.id))