pvimg: Use indexed array for UV key hashes

Replace individual UV key hash fields with an indexed array and
introduce dedicated types for hash indices and key hash kinds. This
simplifies hash handling.

Co-developed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Marc Hartmayer <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
This commit is contained in:
Marc Hartmayer
2026-07-28 18:05:40 +02:00
committed by Steffen Eiden
parent ed74e84862
commit 08d21b24b4
2 changed files with 136 additions and 47 deletions

View File

@@ -33,32 +33,25 @@ fn hdr_test_target_hashes(hdr: &SeHdr, key_hashes: &Path) -> Result<bool> {
err => Error::PvCore(err),
})?;
let hashes = UvKeyHashesV1::read_from_io(file)?;
let mut contains = hdr.contains_hash(&hashes.pchkh);
if contains {
log_println!(
" ✓ Host key hash {:#} is included",
HexSlice::from(&hashes.pchkh)
);
}
if hdr.contains_hash(&hashes.pbhkh) {
log_println!(
" ✓ Backup host key hash {:#} is included",
HexSlice::from(&hashes.pbhkh)
);
contains = true;
};
for hash in hashes.res {
if hdr.contains_hash(&hash) {
log_println!(" ✓ Key hash {:#} is included", HexSlice::from(&hash));
contains = true;
}
}
if !contains {
let matches = hashes.matching_hashes(hdr);
if matches.is_empty() {
warn!(" ✘ None of the key hashes is included");
Ok(false)
} else {
for m in matches {
match m.idx.kind() {
Some(kind) => {
log_println!(" ✓ {kind} {:#} is included", HexSlice::from(&m.hash))
}
None => log_println!(
" ✓ Key hash {:#} is included (zero-based index {})",
HexSlice::from(&m.hash),
m.idx.index()
),
}
}
Ok(true)
}
Ok(contains)
}
/// Returns `Ok(true)` if at least one of the given public key of the host key

View File

@@ -7,7 +7,7 @@ use std::io::{BufRead, BufReader, Read};
use enum_dispatch::enum_dispatch;
use pv::misc::decode_hex;
use super::try_copy_slice_to_array;
use super::{try_copy_slice_to_array, KeyExchangeTrait, SeHdr};
use crate::error::{Error, Result};
/// The `enum_dispatch` macros needs at least one local trait to be implemented.
@@ -41,11 +41,111 @@ impl UvKeyHashV1 {
}
}
use std::fmt::{self, Display};
use std::ops::{Index, IndexMut};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UvKeyHashV1Kind {
PCHKH,
PBHKH,
PCHHKH,
PBHHKH,
}
impl Display for UvKeyHashV1Kind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::PCHKH => write!(f, "Classical Host key hash"),
Self::PBHKH => write!(f, "Backup classical host key hash"),
Self::PCHHKH => write!(f, "Hybrid host key hash"),
Self::PBHHKH => write!(f, "Backup hybrid host key hash"),
}
}
}
/// Index into the UV key hash array.
///
/// The indices follow the UV specification layout:
/// - 0: PCHKH (Classical host key hash)
/// - 1: PBHKH (Backup classical host key hash)
/// - 2-3: Reserved for future use
/// - 4: PCHHKH (Hybrid host key hash)
/// - 5: PBHHKH (Backup hybrid host key hash)
/// - 6-14: Reserved for future use
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct UvKeyHashIdx(u8);
impl UvKeyHashIdx {
pub const PCHKH: Self = Self(0);
pub const PBHKH: Self = Self(1);
pub const PCHHKH: Self = Self(4);
pub const PBHHKH: Self = Self(5);
pub fn index(self) -> usize {
self.0 as usize
}
pub fn kind(self) -> Option<UvKeyHashV1Kind> {
match self.0 {
0 => Some(UvKeyHashV1Kind::PCHKH),
1 => Some(UvKeyHashV1Kind::PBHKH),
4 => Some(UvKeyHashV1Kind::PCHHKH),
5 => Some(UvKeyHashV1Kind::PBHHKH),
_ => None,
}
}
}
impl TryFrom<usize> for UvKeyHashIdx {
type Error = ();
fn try_from(value: usize) -> Result<Self, Self::Error> {
match value {
0..=14 => Ok(Self(value as u8)),
_ => Err(()),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct UvKeyHashesV1 {
pub pchkh: UvKeyHashV1,
pub pbhkh: UvKeyHashV1,
pub res: [UvKeyHashV1; 13],
pub hashes: [UvKeyHashV1; 15],
}
impl Index<UvKeyHashIdx> for UvKeyHashesV1 {
type Output = UvKeyHashV1;
fn index(&self, pos: UvKeyHashIdx) -> &Self::Output {
&self.hashes[pos.index()]
}
}
impl IndexMut<UvKeyHashIdx> for UvKeyHashesV1 {
fn index_mut(&mut self, pos: UvKeyHashIdx) -> &mut Self::Output {
&mut self.hashes[pos.index()]
}
}
#[derive(Debug)]
pub struct MatchingUvKeyHash<'a> {
pub idx: UvKeyHashIdx,
pub hash: &'a UvKeyHashV1,
}
impl UvKeyHashesV1 {
pub fn matching_hashes(&self, hdr: &SeHdr) -> Vec<MatchingUvKeyHash<'_>> {
self.hashes
.iter()
.enumerate()
.filter(|(_, hash)| hdr.contains_hash(hash))
.filter_map(|(idx, hash)| {
Some(MatchingUvKeyHash {
idx: idx.try_into().ok()?,
hash,
})
})
.collect()
}
}
impl UvKeyHashV1 {
@@ -108,9 +208,8 @@ impl UvKeyHashesV1 {
return Err(Error::InvalidUvKeyHashes);
}
let [pchkh, pbhkh, res @ ..]: [UvKeyHashV1; 15] =
hashes.try_into().map_err(|_| Error::InvalidUvKeyHashes)?;
Ok(Self { pchkh, pbhkh, res })
let hashes: [UvKeyHashV1; 15] = hashes.try_into().map_err(|_| Error::InvalidUvKeyHashes)?;
Ok(Self { hashes })
}
}
@@ -142,21 +241,18 @@ mod tests {
0000000000000000000000000000000000000000000000000000000000000000
";
let result = UvKeyHashesV1::read_from_io(Cursor::new(data)).expect("should not fail");
assert_eq!(
result,
UvKeyHashesV1 {
pchkh: UvKeyHashV1::new(
decode_hex("0b729fd62241b339840d61b964a06bb6a1fd4976d9ebea2b4fb48d44de3a2461")
.unwrap()
)
.unwrap(),
pbhkh: UvKeyHashV1::new(
decode_hex("8ec6bc2f77d5d6474b1417cf0a8c914f576245a5b9bb0eefacc7b821483ece7d")
.unwrap()
)
.unwrap(),
res: [UvKeyHashV1::UV_KEY_HASH_NULL; 13],
}
);
let mut exp_hashes = [UvKeyHashV1::UV_KEY_HASH_NULL; 15];
exp_hashes[0] = UvKeyHashV1::new(
decode_hex("0b729fd62241b339840d61b964a06bb6a1fd4976d9ebea2b4fb48d44de3a2461").unwrap(),
)
.unwrap();
exp_hashes[1] = UvKeyHashV1::new(
decode_hex("8ec6bc2f77d5d6474b1417cf0a8c914f576245a5b9bb0eefacc7b821483ece7d").unwrap(),
)
.unwrap();
let uv_hashes = UvKeyHashesV1 { hashes: exp_hashes };
assert_eq!(result, uv_hashes);
}
}