From 73c227fa9a7ce9edaca3f92a465a67cbbecdf6a0 Mon Sep 17 00:00:00 2001 From: Steffen Eiden Date: Wed, 22 Jul 2026 17:24:38 +0200 Subject: [PATCH] pvattest: Verify hybrid keys Allow pvattest verify to verify hybrid keys. The hybrid key is represented by a sha512 hash truncated to 32 bytes. Signed-off-by: Steffen Eiden Reviewed-by: Marc Hartmayer Signed-off-by: Steffen Eiden --- rust/pv/src/req/keyslot.rs | 23 ++++++++++------ rust/pvattest/src/cli.rs | 9 +++---- rust/pvattest/src/cmd/check/host_key.rs | 35 +++++++++++++++++++------ rust/pvattest/src/cmd/create.rs | 9 ++++--- 4 files changed, 51 insertions(+), 25 deletions(-) diff --git a/rust/pv/src/req/keyslot.rs b/rust/pv/src/req/keyslot.rs index 0d925f00..b8415fac 100644 --- a/rust/pv/src/req/keyslot.rs +++ b/rust/pv/src/req/keyslot.rs @@ -4,7 +4,7 @@ //! IBM Z Host key-slot implementations. -use openssl::hash::MessageDigest; +use openssl::hash::{DigestBytes, MessageDigest}; use openssl::pkey::{PKey, PKeyRef, Private, Public}; use crate::crypto::{derive_aes256_gcm_key, derive_aes256_gcm_key_hybrid, encrypt_aead, hash}; @@ -101,6 +101,19 @@ impl KeyslotV2 { } } + /// calculates the sha512 of this hybrid key + pub fn sha512(&self) -> Result { + let mut phk_buf = Vec::::with_capacity(160 + 1568); + let ec_phk: EcPubKeyCoord = self.ec_hostkey.as_ref().try_into()?; + phk_buf.extend_from_slice(ec_phk.as_ref()); + phk_buf.extend_from_slice(&self.mlkem_hostkey.raw_public_key()?); + assert_eq!(phk_buf.len(), 160 + 1568); + + let hash = hash(MessageDigest::sha512(), &phk_buf)?; + assert_eq!(hash.len(), 64); + Ok(hash) + } + /// Encrypts `secret` using `self` and `priv_key` the encryption. /// /// # Returns @@ -132,12 +145,6 @@ impl KeyslotV2 { priv_key: &PKeyRef, to: &mut Vec, ) -> Result<()> { - let mut phk_buf = Vec::::with_capacity(160 + 1568); - let ec_phk: EcPubKeyCoord = self.ec_hostkey.as_ref().try_into()?; - phk_buf.extend_from_slice(ec_phk.as_ref()); - phk_buf.extend_from_slice(&self.mlkem_hostkey.raw_public_key()?); - assert_eq!(phk_buf.len(), 160 + 1568); - let (derived_key, ciphertext) = derive_aes256_gcm_key_hybrid(priv_key, &self.ec_hostkey, &self.mlkem_hostkey)?; let mut wrpk_and_kst = @@ -145,7 +152,7 @@ impl KeyslotV2 { assert_eq!(wrpk_and_kst.len(), 48); to.reserve(1680); - let hash = hash(MessageDigest::sha512(), &phk_buf)?; + let hash = self.sha512()?; assert_eq!(hash.len(), 64); to.extend_from_slice(&hash); to.append(&mut wrpk_and_kst); diff --git a/rust/pvattest/src/cli.rs b/rust/pvattest/src/cli.rs index e3a84de5..3346d107 100644 --- a/rust/pvattest/src/cli.rs +++ b/rust/pvattest/src/cli.rs @@ -6,8 +6,8 @@ use std::path::PathBuf; use clap::{Args, Parser, Subcommand, ValueEnum, ValueHint}; use utils::{ - AutoOrExplicit, AutoOrExplicitParser, CertificateOptions, DeprecatedVerbosityOptions, - HkdVersion, ValueEnumDisplay, ValueEnumFromStr, + AutoOrExplicit, CertificateOptions, DeprecatedVerbosityOptions, HkdVersion, ValueEnumDisplay, + ValueEnumFromStr, }; /// create, perform, and verify attestation measurements @@ -82,7 +82,6 @@ pub enum AttVersion { } pub type AttVersionSelection = AutoOrExplicit; -pub type AttVersionSelectionParser = AutoOrExplicitParser; impl From for HkdVersion { fn from(val: AttVersion) -> Self { @@ -122,8 +121,8 @@ pub struct CreateAttOpt { pub add_data: Vec, /// Specify the Attestation Request version to use. - #[arg(long = "att-version", value_name = "VERSION", default_value_t = AttVersionSelection::Explicit(AttVersion::V1), value_parser = AttVersionSelectionParser::default())] - pub att_version: AttVersionSelection, + #[arg(long = "att-version", value_name = "VERSION", default_value_t = AttVersion::V1)] + pub att_version: AttVersion, } #[derive(Debug, ValueEnum, Clone, Copy)] diff --git a/rust/pvattest/src/cmd/check/host_key.rs b/rust/pvattest/src/cmd/check/host_key.rs index 777219ef..bc0484b5 100644 --- a/rust/pvattest/src/cmd/check/host_key.rs +++ b/rust/pvattest/src/cmd/check/host_key.rs @@ -6,10 +6,9 @@ use std::fmt::Display; use std::path::Path; use anyhow::Result; -use log::{debug, info}; +use log::{debug, info, warn}; use pv::misc::{read_certs, read_file}; -use pv::request::openssl::DigestBytes; -use pv::request::EcPubKeyCoord; +use pv::request::{EcPubKeyCoord, HybridPKey, KeyslotV2}; use serde::Serialize; use utils::HexSlice; @@ -36,7 +35,7 @@ impl Display for HkCheck { } } -fn load_host_keys>(hkds: &[A]) -> Result> { +fn load_host_keys>(hkds: &[A]) -> Result)>> { let mut hkd_hash = Vec::with_capacity(hkds.len()); for hkd in hkds { let hkd = hkd.as_ref(); @@ -45,21 +44,41 @@ fn load_host_keys>(hkds: &[A]) -> Result { + let ec_coord: EcPubKeyCoord = ec_key.as_ref().try_into()?; + hkd_hash.push((hkd, ec_coord.sha256()?.as_ref().to_vec())); + } + 2 => { + let mlkem_key = certs[1].public_key()?; + let ks = KeyslotV2::new(HybridPKey::new(ec_key, mlkem_key)?); + let hash = ks.sha512()?; + hkd_hash.push((hkd, hash[..32].to_vec())); + } + _ => { + warn!( + "The host-key document in '{}' contains more than two certificates!", + hkd.display() + ); + Err(pv::Error::WrongNumberOfKeys(hkd.display().to_string()))?; + } + } } Ok(hkd_hash) } fn contains_phkh<'a>( - hkd_hashes: &[(&'a Path, DigestBytes)], + hkd_hashes: &[(&'a Path, Vec)], phkh: &HexSlice<'_>, mode: HkCheck, check_enforced: bool, ) -> CheckState> { let hk: Vec<_> = hkd_hashes .iter() - .filter_map(|(path, hash)| match hash.as_ref() == phkh.as_ref() { + .filter_map(|(path, hash)| match hash == phkh.as_ref() { true => Some(*path), false => None, }) diff --git a/rust/pvattest/src/cmd/create.rs b/rust/pvattest/src/cmd/create.rs index ba2b0b8f..71852007 100644 --- a/rust/pvattest/src/cmd/create.rs +++ b/rust/pvattest/src/cmd/create.rs @@ -62,11 +62,12 @@ fn determine_version( } pub fn create(opt: &CreateAttOpt) -> Result { - let hkds = opt - .certificate_args - .get_verified_hkds_new("attestation request", opt.att_version.map(|v| v.into()))?; + let hkds = opt.certificate_args.get_verified_hkds_new( + "attestation request", + AttVersionSelection::Explicit(opt.att_version).map(|v| v.into()), + )?; - let att_version = determine_version(opt.att_version, &hkds); + let att_version = determine_version(AttVersionSelection::Explicit(opt.att_version), &hkds); let meas_alg = AttestationMeasAlg::HmacSha512; let mut arcb = AttestationRequest::new(att_version, meas_alg, flags(&opt.add_data))?;