feat(tpm): implement basic measurement of the bootloader configuration

This commit is contained in:
2025-10-31 02:35:58 -04:00
parent 81cf331158
commit afc650f944
2 changed files with 41 additions and 2 deletions

View File

@@ -1,5 +1,6 @@
use crate::config::{RootConfiguration, latest_version}; use crate::config::{RootConfiguration, latest_version};
use crate::options::SproutOptions; use crate::options::SproutOptions;
use crate::platform::tpm::PlatformTpm;
use crate::utils; use crate::utils;
use anyhow::{Context, Result, bail}; use anyhow::{Context, Result, bail};
use log::info; use log::info;
@@ -21,6 +22,11 @@ fn load_raw_config(options: &SproutOptions) -> Result<Vec<u8>> {
// Read the contents of the sprout config file. // Read the contents of the sprout config file.
let content = utils::read_file_contents(Some(&path), &options.config) let content = utils::read_file_contents(Some(&path), &options.config)
.context("unable to read sprout config file")?; .context("unable to read sprout config file")?;
// Measure the sprout.toml into the TPM, if needed and possible.
PlatformTpm::log_event(PlatformTpm::PCR_BOOT_LOADER_CONFIG, &content, "sprout.toml")
.context("unable to measure the sprout.toml file into the TPM")?;
// Return the contents of the sprout config file. // Return the contents of the sprout config file.
Ok(content) Ok(content)
} }

View File

@@ -1,8 +1,10 @@
use crate::utils; use crate::utils;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use uefi::boot::ScopedProtocol; use uefi::boot::ScopedProtocol;
use uefi::proto::tcg::v2::Tcg; use uefi::proto::tcg::PcrIndex;
use uefi_raw::protocol::tcg::v2::{Tcg2Protocol, Tcg2Version}; use uefi::proto::tcg::v2::{PcrEventInputs, Tcg};
use uefi_raw::protocol::tcg::EventType;
use uefi_raw::protocol::tcg::v2::{Tcg2HashLogExtendEventFlags, Tcg2Protocol, Tcg2Version};
/// Represents the platform TPM. /// Represents the platform TPM.
pub struct PlatformTpm; pub struct PlatformTpm;
@@ -33,6 +35,9 @@ impl TpmProtocolHandle {
} }
impl PlatformTpm { impl PlatformTpm {
/// The PCR for measuring the bootloader configuration into.
pub const PCR_BOOT_LOADER_CONFIG: PcrIndex = PcrIndex(5);
/// Acquire access to the TPM protocol handle, if possible. /// Acquire access to the TPM protocol handle, if possible.
/// Returns None if TPM is not available. /// Returns None if TPM is not available.
fn protocol() -> Result<Option<TpmProtocolHandle>> { fn protocol() -> Result<Option<TpmProtocolHandle>> {
@@ -93,4 +98,32 @@ impl PlatformTpm {
// Return the number of active PCR banks. // Return the number of active PCR banks.
Ok(banks.bits()) Ok(banks.bits())
} }
/// Log an event into the TPM pcr `pcr_index` with `buffer` as data. The `description`
/// is used to describe what the event is.
///
/// If a TPM is not available, this will do nothing.
pub fn log_event(pcr_index: PcrIndex, buffer: &[u8], description: &str) -> Result<()> {
// Acquire access to the TPM protocol handle.
let Some(mut handle) = PlatformTpm::protocol()? else {
return Ok(());
};
// Encode the description as a UTF-16 little endian string.
let description = description
.encode_utf16()
.flat_map(|c| c.to_le_bytes())
.collect::<Vec<u8>>();
// Construct an event input for the TPM.
let event = PcrEventInputs::new_in_box(pcr_index, EventType::IPL, &description)
.context("unable to construct pcr event inputs")?;
// Log the event into the TPM.
handle
.protocol()
.hash_log_extend_event(Tcg2HashLogExtendEventFlags::empty(), buffer, &event)
.context("unable to log event to tpm")?;
Ok(())
}
} }