From fc853f3259e3ff4d16c5a2a02224a9eaab49e7d8 Mon Sep 17 00:00:00 2001 From: Timo Keller Date: Tue, 21 Jul 2026 14:29:56 +0200 Subject: [PATCH] pvimg/create-sehdr: Use hybrid keys Allow the creation of SE headers with hybrid (=quantum safe) keys. This results in using the headers in version 2 (0x200). Co-developed-by: Marc Hartmayer Signed-off-by: Marc Hartmayer Signed-off-by: Timo Keller Reviewed-by: Steffen Eiden Signed-off-by: Steffen Eiden --- rust/pv/src/req/request.rs | 70 +++++++++++++++++++++- rust/pvimg/examples/create-sehdr/main.rs | 74 ++++++++++++++++-------- 2 files changed, 118 insertions(+), 26 deletions(-) diff --git a/rust/pv/src/req/request.rs b/rust/pv/src/req/request.rs index 2bed8e53..0fca7999 100644 --- a/rust/pv/src/req/request.rs +++ b/rust/pv/src/req/request.rs @@ -148,10 +148,9 @@ mod tests { use zerocopy::IntoBytes; use super::*; + use crate::crypto::SymKey; use crate::get_test_asset; - use crate::req::header::RequestHdr; - use crate::req::{Aad, HostKey, Keyslot, ReqEncrCtx}; - use crate::request::SymKey; + use crate::req::{Aad, HybridPKey, Keyslot}; use crate::test_utils::*; static TEST_MAGIC: [u8; 8] = 0x12345689abcdef00u64.to_be_bytes(); @@ -187,6 +186,37 @@ mod tests { assert_eq!(&aad, &aad_exp); } + #[test] + fn encr_build_aad_v2() { + let (cust_key, host_key1, host_key2) = get_test_keys_hybrid(); + let ks = Keyslot::new(HostKey::V2(HybridPKey::new(host_key1, host_key2).unwrap())); + let ctx = ReqEncrCtx::new_aes_256( + Some([0x11; 12]), + Some(cust_key), + Some(SymKey::Aes256([0x17; 32].into())), + ) + .unwrap(); + let v = [0x55; 8]; + let aad = Aad::Plain(&v); + let aad = ctx + .build_aad(0x200, &vec![aad, Aad::Ks(&ks)], 16, Some(TEST_MAGIC)) + .unwrap(); + + let aad_exp = vec![ + 0x12, 0x34, 0x56, 0x89, 0xab, 0xcd, 0xef, 0, // progr + 0, 0, 2, 0, // vers + 0, 0, 6, 232, // size + 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, // iv + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // res + 1, // nks + 0, 0, 0, 0, // res + 0, 0, 0, 16, // sea + 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, // aad + ]; + // only compare non-randomized part + assert_eq!(aad[..aad_exp.len()], aad_exp); + } + #[test] fn encr_build_aad_nks_no() { let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap(); @@ -210,6 +240,21 @@ mod tests { let aad = ctx.build_aad(0x200, &aad, 16, Some(TEST_MAGIC)); assert!(matches!(aad, Err(Error::ManyHostkeys))); } + + #[test] + fn encr_build_aad_nks_many_v2() { + let (_, host_key1, host_key2) = get_test_keys_hybrid(); + let host_key = HostKey::V2(HybridPKey::new(host_key1, host_key2).unwrap()); + let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap(); + + let ks: Vec = (0..257).map(|_| Keyslot::new(host_key.clone())).collect(); + let mut aad = Vec::::new(); + ks.iter().for_each(|ks| aad.push(Aad::Ks(ks))); + + let aad = ctx.build_aad(0x200, &aad, 16, Some(TEST_MAGIC)); + assert!(matches!(aad, Err(Error::ManyHostkeys))); + } + #[test] fn encr_build_aad_nks() { let (_, host_key) = get_test_keys(); @@ -229,6 +274,25 @@ mod tests { assert_eq!(aad.get(39).unwrap(), &3u8); } + #[test] + fn encr_build_aad_nks_v2() { + let (_, host_key1, host_key2) = get_test_keys_hybrid(); + let host_key = HostKey::V2(HybridPKey::new(host_key1, host_key2).unwrap()); + let ctx = ReqEncrCtx::new_aes_256(Some([0x11; 12]), None, None).unwrap(); + + let ks = [ + Keyslot::new(host_key.clone()), + Keyslot::new(host_key.clone()), + Keyslot::new(host_key), + ]; + let mut aad = Vec::::new(); + ks.iter().for_each(|ks| aad.push(Aad::Ks(ks))); + + let aad = ctx.build_aad(0x200, &aad, 16, Some(TEST_MAGIC)).unwrap(); + + assert_eq!(aad.get(39).unwrap(), &3u8); + } + #[test] fn req_hdr() { let hdr = RequestHdr::new(0x200, 22, [0x11; 12], 15, 44, None); diff --git a/rust/pvimg/examples/create-sehdr/main.rs b/rust/pvimg/examples/create-sehdr/main.rs index f640d0a1..78d28902 100644 --- a/rust/pvimg/examples/create-sehdr/main.rs +++ b/rust/pvimg/examples/create-sehdr/main.rs @@ -10,11 +10,11 @@ use std::path::PathBuf; use std::str::FromStr; use anyhow::{anyhow, Context, Error}; -use clap::{Parser, ValueHint}; -use log::{info, warn}; -use pv::misc::{decode_hex, open_file, read_certs, read_file, try_parse_u64}; -use pv::request::SymKeyType; -use pv::{Error as PvError, Result}; +use clap::{Parser, ValueEnum, ValueHint}; +use log::info; +use pv::misc::{decode_hex, open_file, read_file, read_hkd, try_parse_u64}; +use pv::request::{HostKey, SymKeyType}; +use pv::Result; use pvimg::misc::PSW; use pvimg::secured_comp::{ComponentTrait, Layout, SecuredComponentBuilder}; use pvimg::uvdata::{BuilderTrait, SeHdrBuilder, SeHdrControlFlags, SeHdrVersion, SeTarget}; @@ -87,6 +87,38 @@ impl Display for ComponentArg { } } +/// SE-header version selection +#[derive(Debug, Clone, Copy, ValueEnum)] +enum SeHdrVersionArg { + /// SE-header version 1 + #[value(name = "1")] + V1, + /// SE-header version 2 + #[value(name = "2")] + V2, +} + +impl From for SeHdrVersion { + fn from(arg: SeHdrVersionArg) -> Self { + match arg { + SeHdrVersionArg::V1 => SeHdrVersion::V1, + SeHdrVersionArg::V2 => SeHdrVersion::V2, + } + } +} + +impl SeHdrVersionArg { + /// Detect the SE header version from the keys. + /// Returns V2 if any key is hybrid, otherwise V1. + pub fn detect>(keys: K) -> Self { + if keys.as_ref().iter().any(|k| !k.is_hybrid()) { + Self::V1 + } else { + Self::V2 + } + } +} + /// Create a Secure Execution header. #[derive(Parser, Debug)] pub struct Args { @@ -129,6 +161,10 @@ pub struct Args { #[arg(short, long)] pub output: PathBuf, + /// SE-header version to build + #[arg(long, value_enum)] + version: Option, + #[clap(flatten)] pub verbosity: VerbosityOptions, } @@ -203,34 +239,26 @@ fn main() -> anyhow::Result<()> { info!("\n# Creating Secure Execution Header"); let addr = args.psw_addr; let mask = args.psw_mask; - let version = SeHdrVersion::V1; - let mut builder = SeHdrBuilder::new(version, PSW { addr, mask }, secure_comp_builer.finish()?)?; let mut target_pub_keys = vec![]; for hkd_path in args.host_key_documents { info!( "Use the file '{}' as a host key document", hkd_path.display() ); - let hkd_data = read_file(&hkd_path, "host key document")?; - let certs = read_certs(&hkd_data)?; - if certs.is_empty() { - return Err(PvError::NoHkdInFile(hkd_path.display().to_string()).into()); - } - - if certs.len() > 1 { - warn!("The host key document in '{}' contains more than one certificate! All keys will be used.", - hkd_path.display()); - } - - for cert in &certs { - target_pub_keys.push(cert.public_key()?); - } + let cert = read_hkd(&hkd_path)?; + target_pub_keys.push(cert); } - builder.add_hostkeys(&target_pub_keys)?; - + let version: SeHdrVersion = args + .version + .unwrap_or(SeHdrVersionArg::detect(&target_pub_keys)) + .into(); let target = SeTarget::from_se_hdr_version(version); let pcf = SeHdrControlFlags::from_u64(try_parse_u64(&args.pcf, "pcf")?, target, true); let scf = SeHdrControlFlags::from_u64(try_parse_u64(&args.scf, "scf")?, target, false); + info!("SE-header version ...: {}", version); + let mut builder = SeHdrBuilder::new(version, PSW { addr, mask }, secure_comp_builer.finish()?)?; + builder.add_hostkeys(&target_pub_keys)?; + info!( "PSW addr ............: {addr:#018x}\n\ PSW mask ............: {mask:#018x}\n\