Files
s390-tools/rust/pv_core/src/policy.rs
Finn Callies 1741ecff96 rust: Add EBC support to pv_core library
Add EBC (Early Boot Customization) utility functions to pv_core library
for parsing and verifying Add-Secret-Request structures.

Introduce the core library functionality needed for EBC:
- Add ebc_utils module to pv_core with ASR parsing and verification
- Export ebc_utils in pv_core lib.rs
- Re-export ebc_utils in pv lib.rs for downstream consumers
- Update pvsecret Cargo.toml dependencies

The library provides the foundation for tools that work with
integrity-protected ASR structures used in SEL guest customization.

Assisted-by: IBM Bob:1.0.1
Acked-by: Holger Dengler <dengler@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Finn Callies <fcallies@linux.ibm.com>
Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
2026-04-30 13:46:31 +02:00

113 lines
3.5 KiB
Rust

// SPDX-License-Identifier: MIT
//
// Copyright IBM Corp.
use crate::misc::encode_hex;
use crate::utils::open_file;
use crate::{Error, Result};
use std::{
fmt::{Display, Formatter, Result as Resfmt},
fs::File,
os::unix::ffi::OsStrExt,
path::{Path, PathBuf},
str::from_utf8,
};
use zerocopy::{FromBytes, Immutable, IntoBytes};
const HASH_LEN: usize = 32;
// UserDataType::Unsigned.max() returns 512
const USER_DATA_MAX_SIZE: usize = 512;
/// A reference to a policy file containing its SHA-256 hash and file path.
///
/// This structure is used in Early Boot Customization (EBC) to store
/// a reference to a policy file. It contains the SHA-256 hash of the policy
/// file content and the file path as a fixed-size byte array.
///
/// The total size is constrained by `USER_DATA_MAX_SIZE` (512 bytes), with
/// 32 bytes allocated for the hash and the remaining bytes for the file path.
#[derive(Debug, FromBytes, IntoBytes, Immutable, Copy, Clone)]
#[repr(C)]
pub struct PolicyReference {
/// SHA-256 hash of the policy file content (32 bytes)
pub hash: [u8; HASH_LEN],
/// File path stored as a null-terminated byte array
pub name: [u8; USER_DATA_MAX_SIZE - HASH_LEN],
}
impl PolicyReference {
/// Creates a new `PolicyReference` from a file path.
///
/// Opens the file, reads its content, and computes the SHA-256 hash.
///
/// # Parameters
///
/// * `src` - The path to the policy file
/// * `sha256` - A function that computes the SHA-256 hash of the content
///
/// # Returns
///
/// Returns a `PolicyReference` containing the SHA-256 hash and the file path,
/// or an error if the file cannot be opened or the hash computation fails.
///
/// # Note
///
/// The file path is truncated if it exceeds the available space in the `name` field.
pub fn new<P, H>(src: P, sha256: H) -> Result<Self>
where
P: AsRef<Path>,
H: Fn(File) -> Result<Vec<u8>>,
{
let mut ret = Self {
hash: [0; HASH_LEN],
name: [0; USER_DATA_MAX_SIZE - HASH_LEN],
};
let file = open_file(src.as_ref())?;
ret.hash.copy_from_slice(sha256(file)?.as_bytes());
let strbytes = src.as_ref().as_os_str().as_bytes();
let nbytes = strbytes.len().min(ret.name.len());
ret.name[..nbytes].copy_from_slice(&strbytes[..nbytes]);
Ok(ret)
}
/// Converts the stored file path back to a `PathBuf`.
///
/// # Returns
///
/// Returns the file path as a `PathBuf`, or an error if the stored name
/// is not valid UTF-8.
///
/// # Errors
///
/// * `Error::ParseError` - If the name contains invalid UTF-8
pub fn to_path(&self) -> Result<PathBuf> {
// Extract bytes until the first null byte (null-terminated string)
let name_bytes: Vec<u8> = self
.name
.iter()
.copied()
.take_while(|&byte| byte != 0)
.collect();
let rust_string = String::from_utf8(name_bytes).map_err(|e| Error::ParseError {
subject: "PolicyReference name".to_string(),
content: format!("Invalid UTF-8 in name: {}", e),
})?;
Ok(Path::new(&rust_string).to_owned())
}
}
impl Display for PolicyReference {
fn fmt(&self, f: &mut Formatter) -> Resfmt {
write!(
f,
"{} {}",
encode_hex(self.hash),
from_utf8(&self.name).expect("unable to convert name")
)
}
}