Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 57 additions & 5 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ use clap::{Args, Parser, Subcommand};
use log::LevelFilter;
use serde::{Deserialize, Serialize};

use std::path::PathBuf;

use compute_pcrs_lib::*;

#[derive(Parser, Debug)]
Expand All @@ -36,6 +38,11 @@ struct SecureBootVarStores {

#[derive(Subcommand, Debug)]
enum Command {
/// Compute the Authentihash of a PE binary
Authentihash {
/// Path to a PE binary
binary: String,
},
/// Compute all possible PCR values from the binaries available in the current environment. Meant to be run inside a Bootable Container.
All {
#[arg(
Expand Down Expand Up @@ -73,8 +80,8 @@ enum Command {
)]
mok_variables: String,
},
/// Compute PCR 4
Pcr4 {
/// Compute PCR 4 for the non UKI case
Pcr4NoUki {
#[arg(
long,
short,
Expand Down Expand Up @@ -102,6 +109,35 @@ enum Command {
)]
no_secureboot: bool,
},
/// Compute PCR 4 for the Bootable Container UKI case
Pcr4 {
#[arg(
long,
short,
default_value = "/usr/lib/bootupd/updates/EFI/fedora/shimx64.efi",
help = "Path to the shim binary"
)]
shim: String,
#[arg(
long,
short,
default_value = "/usr/lib/bootupd/updates/EFI/fedora/grubx64.efi",
help = "Path to the bootloader binary"
)]
bootloader: String,
#[arg(
long,
short,
default_value = "/boot/EFI/Linux/uki.efi",
help = "Path to the UKI binary"
)]
uki: String,
#[arg(
long,
help = "Path to a UKI addon (can be passed multiple times)"
)]
uki_addon: Vec<String>,
},
/// Compute PCR 7
Pcr7 {
#[arg(
Expand Down Expand Up @@ -157,6 +193,11 @@ fn main() -> Result<()> {
.init();

match &cli.command {
Command::Authentihash { binary} => {
let hash = authentihash(&PathBuf::from(binary)).unwrap();
println!("{hash}");
Ok(())
},
Command::All {
kernels,
esp,
Expand All @@ -166,7 +207,7 @@ fn main() -> Result<()> {
mok_variables,
} => {
let pcrs = vec![
compute_pcr4(kernels, esp, *uki, !no_secureboot),
compute_pcr4_nouki(kernels, esp, *uki, !no_secureboot),
compute_pcr7(secureboot_variables.efivars.as_deref(), esp, !no_secureboot),
/* compute_pcr11(), */
compute_pcr14(mok_variables),
Expand All @@ -177,13 +218,24 @@ fn main() -> Result<()> {
);
Ok(())
}
Command::Pcr4 {
Command::Pcr4NoUki {
kernels,
esp,
uki,
no_secureboot,
} => {
let pcr = compute_pcr4(kernels, esp, *uki, !no_secureboot);
let pcr = compute_pcr4_nouki(kernels, esp, *uki, !no_secureboot);
println!("{}", serde_json::to_string_pretty(&pcr).unwrap());
Ok(())
}
Command::Pcr4 {
shim,
bootloader,
uki,
uki_addon,
} => {
log::debug!("{uki_addon:?}");
let pcr = compute_pcr4(shim, bootloader, uki, uki_addon);
println!("{}", serde_json::to_string_pretty(&pcr).unwrap());
Ok(())
}
Expand Down
26 changes: 26 additions & 0 deletions compute-pcr4
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/bin/bash

if [[ "$1" == "quay.io/travier/fedora-coreos-uki-cocl:42.20250901.3.0" ]]; then
podman run --pull=always --rm -ti --security-opt label=disable \
-v $PWD/target/release/compute-pcrs:/usr/bin/compute-pcrs \
"${1}" \
compute-pcrs pcr4 \
--shim /usr/lib/bootupd/updates/EFI/fedora/shimx64.efi \
--bootloader /usr/lib/bootupd/updates/EFI/fedora/grubx64.efi \
--uki /boot/EFI/Linux/6.15.10-200.fc42.x86_64.efi \
--uki-addon /boot/EFI/Linux/6.15.10-200.fc42.x86_64.efi.extra.d/ignition.addon.efi
elif [[ "$1" == "quay.io/travier/fedora-coreos-uki-cocl:42.20250901.3.1" ]]; then
podman run --pull=always --rm -ti --security-opt label=disable \
-v $PWD/luks.addon.efi:/boot/EFI/Linux/luks.addon.efi \
-v $PWD/rd.neednet.addon.efi:/boot/EFI/Linux/rd.neednet.addon.efi \
-v $PWD/target/release/compute-pcrs:/usr/bin/compute-pcrs \
"${1}" \
compute-pcrs pcr4 \
--shim /usr/lib/bootupd/updates/EFI/fedora/shimx64.efi \
--bootloader /usr/lib/bootupd/updates/EFI/fedora/grubx64.efi \
--uki /boot/EFI/Linux/6.15.10-200.fc42.x86_64.efi \
--uki-addon /boot/EFI/Linux/luks.addon.efi \
--uki-addon /boot/EFI/Linux/rd.neednet.addon.efi
else
echo "Unknown"
fi
2 changes: 2 additions & 0 deletions compute-pcr7
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
./target/release/compute-pcrs pcr7 --esp . --efivars efivars
18 changes: 18 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,24 @@
image := "quay.io/fedora/fedora-coreos:42.20250705.3.0"
container_image_name := "compute-pcrs"

run container *args:
#!/bin/bash
set -euo pipefail
cargo build --release
set -x
podman run --rm -ti \
--security-opt label=disable \
--mount=type=image,source={{container}},destination=/var/srv/image,rw=false \
-v $PWD/target/release/compute-pcrs:/usr/bin/compute-pcrs \
-v $PWD/test-data/:/var/srv/test-data \
-v $PWD/systemd-bootx64.efi:/var/srv/image/usr/lib/bootupd/updates/EFI/fedora/grubx64.efi \
fedora:latest \
compute-pcrs pcr4 \
--shim /var/srv/image/usr/lib/bootupd/updates/EFI/fedora/shimx64.efi \
--bootloader /var/srv/image/usr/lib/bootupd/updates/EFI/fedora/grubx64.efi \
--uki /var/srv/image/boot/EFI/Linux/6.15.10-200.fc42.x86_64.efi \
--uki-addon /var/srv/image/boot/EFI/Linux/6.15.10-200.fc42.x86_64.efi.extra.d/ignition.addon.efi

build-container:
#!/bin/bash
set -euo pipefail
Expand Down
57 changes: 56 additions & 1 deletion lib/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::collections::HashSet;

use std::path::PathBuf;

pub mod certs;
mod esp;
mod linux;
Expand All @@ -32,7 +34,60 @@ pub struct Pcr {
pub parts: Vec<Part>,
}

pub fn compute_pcr4(kernels_dir: &str, esp_path: &str, uki: bool, secureboot: bool) -> Pcr {
pub fn authentihash(binary: &PathBuf) -> Option<String> {
let pe = pefile::PeFile::load_from_file(&binary.to_string_lossy(), false)?;
Some(hex::encode(pe.authenticode()))
}

pub fn compute_pcr4(shim: &str, bootloader: &str, uki: &str, uki_addons: &Vec<String>) -> Pcr {
let ev_efi_action_hash: Vec<u8> =
Sha256::digest(b"Calling EFI Application from Boot Option").to_vec();
let ev_separator_hash: Vec<u8> = Sha256::digest(hex::decode("00000000").unwrap()).to_vec();

let mut hashes: Vec<(String, Vec<u8>)> = vec![];
hashes.push(("EV_EFI_ACTION".into(), ev_efi_action_hash));
hashes.push(("EV_SEPARATOR".into(), ev_separator_hash));

let mut bins: Vec<PathBuf> = vec![shim.into(), bootloader.into(), uki.into()];
for addon in uki_addons {
bins.push(addon.into());
}

// println!("{bins:?}");

let mut bin_hashes: Vec<(String, Vec<u8>)> = bins
.iter()
.map(|b| ("EV_EFI_BOOT_SERVICES_APPLICATION".into(), pefile::PeFile::load_from_file(&b.to_string_lossy(), false).expect("Can't open binary").authenticode()))
.collect();
hashes.append(&mut bin_hashes);

// Start with 0
let mut result =
hex::decode("0000000000000000000000000000000000000000000000000000000000000000")
.unwrap()
.to_vec();

for (_p, h) in &hashes {
let mut hasher = Sha256::new();
hasher.update(result);
hasher.update(h);
result = hasher.finalize().to_vec();
}

Pcr {
id: 4,
value: hex::encode(result),
parts: hashes
.iter()
.map(|(p, h)| Part {
name: p.to_string(),
hash: hex::encode(h),
})
.collect(),
}
}

pub fn compute_pcr4_nouki(kernels_dir: &str, esp_path: &str, uki: bool, secureboot: bool) -> Pcr {
let esp = esp::Esp::new(esp_path).unwrap();

let ev_efi_action_hash: Vec<u8> =
Expand Down
Loading