From c87d11abfe58dcc4a9d8005275dae2fb57f677a9 Mon Sep 17 00:00:00 2001 From: Marc Hartmayer Date: Wed, 24 Jun 2026 12:47:01 +0200 Subject: [PATCH] pvimg and create-sehdr: Refactor code to use generic flags Replace version-specific flag enums (PcfV1, ScfV1) with a unified SeHdrFlag enum and generic ControlFlagsModel. This refactoring provides: - Single SeHdrFlag enum for all control flags across SE header versions - Generic SeHdrControlFlagsModel with version-specific configurations - EffectiveControlFlags type replacing PlaintextControlFlagsV1/SecretControlFlagsV1 - FlagsOverride mechanism for customizing flag configurations - Enhanced FromStr implementation for flag parsing - Comprehensive test coverage for flag operations and conversions The new design improves maintainability by eliminating code duplication and provides a more flexible API for working with SE header control flags. Assisted-by: IBM Bob:1.0.4 Signed-off-by: Marc Hartmayer Reviewed-by: Steffen Eiden Signed-off-by: Steffen Eiden --- rust/pvimg/examples/create-sehdr/main.rs | 14 +- rust/pvimg/src/cli.rs | 253 ++++- rust/pvimg/src/cmd/create.rs | 380 +++++-- rust/pvimg/src/lib.rs | 10 +- rust/pvimg/src/pv_utils.rs | 7 +- rust/pvimg/src/pv_utils/se_hdr.rs | 6 +- rust/pvimg/src/pv_utils/se_hdr/brb.rs | 6 +- rust/pvimg/src/pv_utils/se_hdr/builder.rs | 8 +- rust/pvimg/src/pv_utils/se_hdr/flags.rs | 1171 ++++++++++++--------- rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs | 17 +- rust/pvimg/src/se_img.rs | 8 +- 11 files changed, 1264 insertions(+), 616 deletions(-) diff --git a/rust/pvimg/examples/create-sehdr/main.rs b/rust/pvimg/examples/create-sehdr/main.rs index abc13885..f640d0a1 100644 --- a/rust/pvimg/examples/create-sehdr/main.rs +++ b/rust/pvimg/examples/create-sehdr/main.rs @@ -17,7 +17,7 @@ use pv::request::SymKeyType; use pv::{Error as PvError, Result}; use pvimg::misc::PSW; use pvimg::secured_comp::{ComponentTrait, Layout, SecuredComponentBuilder}; -use pvimg::uvdata::{BuilderTrait, SeHdrBuilder, SeHdrVersion}; +use pvimg::uvdata::{BuilderTrait, SeHdrBuilder, SeHdrControlFlags, SeHdrVersion, SeTarget}; use utils::{AtomicFile, AtomicFileOperation, HexSlice, PvLogger, VerbosityOptions}; /// Converts the hexstring into a byte vector. @@ -203,11 +203,8 @@ fn main() -> anyhow::Result<()> { info!("\n# Creating Secure Execution Header"); let addr = args.psw_addr; let mask = args.psw_mask; - let mut builder = SeHdrBuilder::new( - SeHdrVersion::V1, - PSW { addr, mask }, - secure_comp_builer.finish()?, - )?; + 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!( @@ -231,8 +228,9 @@ fn main() -> anyhow::Result<()> { } builder.add_hostkeys(&target_pub_keys)?; - let pcf = try_parse_u64(&args.pcf, "pcf")?.into(); - let scf = try_parse_u64(&args.scf, "scf")?.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!( "PSW addr ............: {addr:#018x}\n\ PSW mask ............: {mask:#018x}\n\ diff --git a/rust/pvimg/src/cli.rs b/rust/pvimg/src/cli.rs index 2c80bd53..c168233d 100644 --- a/rust/pvimg/src/cli.rs +++ b/rust/pvimg/src/cli.rs @@ -13,7 +13,31 @@ use std::string::ToString; use clap::builder::{PossibleValue, TypedValueParser}; use clap::{Arg, ArgGroup, Args, Command, CommandFactory, Parser, ValueEnum, ValueHint}; use log::warn; -use utils::{CertificateOptions, DeprecatedVerbosityOptions}; +use utils::{CertificateOptions, DeprecatedVerbosityOptions, ValueEnumDisplay}; + +/// SE header control flags for CLI +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, ValueEnum, ValueEnumDisplay)] +#[value(rename_all = "kebab-case")] +pub enum SeHdrFlagName { + /// Confidential guest dump support + ConfidentialDump, + /// DEA/TDEA PCKMO key encryption support + PckmoDeaTdea, + /// AES PCKMO key encryption support + PckmoAes, + /// ECC PCKMO key encryption support + PckmoEcc, + /// HMAC PCKMO key encryption support + PckmoHmac, + /// Backup target keys support + BackupTargetKeys, + /// CCK-derived extension secret enforcement for add-secret requests + CckExtensionSecretEnforcement, + /// CCK update support + CckUpdate, + /// Image components without encryption + NoComponentEncryption, +} /// Create and inspect IBM Secure Execution images. /// @@ -56,6 +80,28 @@ impl CliOptions { } } +/// Defines a requirement rule for CLI validation. +/// +/// A requirement specifies that certain flags require a specific option to be present. +/// For example, the `ConfidentialDump` flag requires the `--cck` option. +struct Requirement { + /// The flags that trigger this requirement + flags: &'static [SeHdrFlagName], + /// The option name that must be present (e.g., "cck") + option: &'static str, + /// Whether the required option is present + present: bool, +} + +/// Defines a set of flags that cannot be used together. +/// +/// When multiple flags from this set are present in the command line, +/// a validation error is raised with a dynamically generated message +/// listing the conflicting flags. +struct MutuallyExclusiveFlags { + flags: &'static [SeHdrFlagName], +} + /// Validates the given command line options. /// /// # Errors @@ -71,6 +117,92 @@ pub fn validate_cli(opts: &CliOptions) -> Result<(), clap::error::Error> { { warn!("Use bootloader directory: {}", dir.display()); } + + // Check that a user provided CCK is available + let rules = [Requirement { + flags: &[ + SeHdrFlagName::ConfidentialDump, + SeHdrFlagName::CckExtensionSecretEnforcement, + ], + option: LONG_FLAG_CCK, + present: create_opts.keys.cck.is_some(), + }]; + for r in rules { + let offenders: Vec<_> = r + .flags + .iter() + .filter(|f| create_opts.flags.contains(f)) + .collect(); + + if !offenders.is_empty() && !r.present { + return Err(clap::Error::raw( + clap::error::ErrorKind::MissingRequiredArgument, + format!( + "flag(s) {} require(s) --{}", + offenders + .iter() + .map(|f| format!("{:?}", f)) + .collect::>() + .join(", "), + r.option + ), + )); + } + } + + // Check for conflicts between --flags and --disable-flags + if !create_opts.flags.is_empty() && !create_opts.disable_flags.is_empty() { + use std::collections::HashSet; + let flags_set: HashSet<_> = create_opts.flags.iter().collect(); + let disable_flags_set: HashSet<_> = create_opts.disable_flags.iter().collect(); + + let conflicts: Vec<_> = flags_set + .intersection(&disable_flags_set) + .copied() + .collect(); + + if !conflicts.is_empty() { + return Err(clap::Error::raw( + clap::error::ErrorKind::ArgumentConflict, + // Print the flag name using the kebab-case notation (using to_possible_value) + format!( + "Conflicting flags detected: the following flags are specified in both --flags and --disable-flags: {}", + conflicts.iter().map(|x| format!("{x}")).collect::>().join(", ") + ), + )); + } + } + + // Check for mutually exclusive flags within --flags + let exclusion_rules = [MutuallyExclusiveFlags { + flags: &[ + SeHdrFlagName::CckExtensionSecretEnforcement, + SeHdrFlagName::CckUpdate, + ], + }]; + + for rule in exclusion_rules { + let conflicting_flags: Vec<_> = rule + .flags + .iter() + .filter(|f| create_opts.flags.contains(f)) + .collect(); + + if conflicting_flags.len() > 1 { + return Err(clap::Error::raw( + clap::error::ErrorKind::ArgumentConflict, + format!( + "The following flags cannot be used together: {}", + conflicting_flags + .iter() + .map(|f| format!("'{}'", f)) + .collect::>() + .join(", ") + ), + )); + } + } + Ok(()) } _ => Ok(()), @@ -100,6 +232,8 @@ pub struct ComponentPaths { pub parmfile: Option, } +const LONG_FLAG_CCK: &str = "cck"; + /// CLI Argument collection for handling user-provided keys. #[derive(Args, Debug)] #[cfg_attr(test, derive(Default))] @@ -109,7 +243,7 @@ pub struct UserKeys { /// The file must contain exactly 32 bytes of data. In previous versions, /// this option was called '--comm-key'. #[arg( - long, + long = LONG_FLAG_CCK, value_name = "FILE", group = "cck-available", visible_alias = "comm-key" @@ -546,8 +680,31 @@ pub struct CreateBootImageArgs { pub keys: UserKeys, #[clap(flatten)] + // TODO Declare as deprecated pub legacy_flags: CreateBootImageLegacyFlags, + /// Set control flags using comma-separated flag names. + /// + /// Specify flags to enable using their names. + #[arg( + long, + value_name = "FLAGS", + value_delimiter = ',', + conflicts_with_all = ["header-flags", "x_pcf", "x_scf"] + )] + pub flags: Vec, + + /// Set control flags using comma-separated flag names. + /// + /// Specify flags to disable using their names. + #[arg( + long, + value_name = "FLAGS", + value_delimiter = ',', + conflicts_with_all = ["header-flags", "x_pcf", "x_scf"] + )] + pub disable_flags: Vec, + #[clap(flatten)] pub experimental_args: CreateBootImageExperimentalArgs, } @@ -734,14 +891,44 @@ mod test { // --comm-key with --enable-cck-update (alias test) flat_map_collect(insert(mvca.clone(), vec![CliOption::new("comm-key", ["--comm-key", "/dev/null"]), CliOption::new("enable-cck-update", ["--enable-cck-update"])])), + + // --disable-flags tests + // Test --disable-flags alone + flat_map_collect(insert(mvca.clone(), vec![CliOption::new("disable-flags", ["--disable-flags", &SeHdrFlagName::PckmoHmac.to_string()])])), + // Test --flags and --disable-flags without conflict + flat_map_collect(insert(mvca.clone(), vec![ + CliOption::new("flags", ["--flags", &SeHdrFlagName::ConfidentialDump.to_string()]), + CliOption::new("disable-flags", ["--disable-flags", &SeHdrFlagName::PckmoHmac.to_string()]), + CliOption::new("cck", ["--cck", "/dev/null"]) + ])), + // Test multiple --disable-flags + flat_map_collect(insert(mvca.clone(), vec![CliOption::new("disable-flags", ["--disable-flags", &format!("{},{}", SeHdrFlagName::PckmoHmac, SeHdrFlagName::BackupTargetKeys)])])), + // Test --flags and --disable-flags with different flags + flat_map_collect(insert(mvca.clone(), vec![ + CliOption::new("flags", ["--flags", &format!("{},{}", SeHdrFlagName::ConfidentialDump, SeHdrFlagName::BackupTargetKeys)]), + CliOption::new("disable-flags", ["--disable-flags", &format!("{},{}", SeHdrFlagName::PckmoHmac, SeHdrFlagName::NoComponentEncryption)]), + CliOption::new("cck", ["--cck", "/dev/null"]) + ])), + + // --flags tests (equivalent to --enable-* tests) + // Test --flags with confidential-dump (equivalent to --enable-dump) + flat_map_collect(insert(mvca.clone(), vec![ + CliOption::new("flags", ["--flags", &SeHdrFlagName::ConfidentialDump.to_string()]), + CliOption::new("cck", ["--cck", "/dev/null"]) + ])), + // Test with all PCKMO flags + flat_map_collect(insert(mvca.clone(), vec![CliOption::new("flags", ["--flags", &format!("{},{},{},{}", + SeHdrFlagName::PckmoDeaTdea, SeHdrFlagName::PckmoAes, SeHdrFlagName::PckmoEcc, SeHdrFlagName::PckmoHmac)])])), + + // Test with NoComponentEncryption flag + flat_map_collect(insert(mvca.clone(), vec![CliOption::new("flags", ["--flags", &SeHdrFlagName::NoComponentEncryption.to_string()])])), ]; let invalid_create_args = [ flat_map_collect(remove(mvcanv.clone(), "no-verify")), flat_map_collect(remove(mvcanv.clone(), "image")), flat_map_collect(remove(mvcanv.clone(), "hkd")), flat_map_collect(remove(mvcanv, "output")), - - // missing both `--cck' and `--enable-cck-update' + // missing both `--cck' and `--enable-cck-update' (required by --enable-dump) flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-dump", ["--enable-dump"])])), // -v and -q cannot be combined @@ -783,6 +970,51 @@ mod test { flat_map_collect(insert(mvca.clone(), vec![CliOption::new("enable-cck-extension-secret", ["--enable-cck-extension-secret"]), CliOption::new("enable-cck-update", ["--enable-cck-update"]), CliOption::new("cck", ["--cck", "/dev/null"])])), + + // --disable-flags conflict tests + // Test conflict between --flags and --disable-flags (same flag in both) + flat_map_collect(insert(mvca.clone(), vec![ + CliOption::new("flags", ["--flags", &SeHdrFlagName::ConfidentialDump.to_string()]), + CliOption::new("disable-flags", ["--disable-flags", &SeHdrFlagName::ConfidentialDump.to_string()]), + CliOption::new("cck", ["--cck", "/dev/null"]) + ])), + // Test multiple conflicts + flat_map_collect(insert(mvca.clone(), vec![ + CliOption::new("flags", ["--flags", &format!("{},{}", SeHdrFlagName::ConfidentialDump, SeHdrFlagName::PckmoHmac)]), + CliOption::new("disable-flags", ["--disable-flags", &format!("{},{}", SeHdrFlagName::ConfidentialDump, SeHdrFlagName::PckmoHmac)]), + CliOption::new("cck", ["--cck", "/dev/null"]) + ])), + + // --flags conflicts with --x-pcf + flat_map_collect(insert(mvca.clone(), vec![ + CliOption::new("flags", ["--flags", &SeHdrFlagName::ConfidentialDump.to_string()]), + CliOption::new("x-pcf", ["--x-pcf", "0x0"]), + CliOption::new("cck", ["--cck", "/dev/null"]) + ])), + // --flags conflicts with --x-scf + flat_map_collect(insert(mvca.clone(), vec![ + CliOption::new("flags", ["--flags", &SeHdrFlagName::ConfidentialDump.to_string()]), + CliOption::new("x-scf", ["--x-scf", "0x0"]), + CliOption::new("cck", ["--cck", "/dev/null"]) + ])), + // --disable-flags conflicts with --x-pcf + flat_map_collect(insert(mvca.clone(), vec![ + CliOption::new("disable-flags", ["--disable-flags", &SeHdrFlagName::PckmoHmac.to_string()]), + CliOption::new("x-pcf", ["--x-pcf", "0x0"]) + ])), + // --disable-flags conflicts with --x-scf + flat_map_collect(insert(mvca.clone(), vec![ + CliOption::new("disable-flags", ["--disable-flags", &SeHdrFlagName::PckmoHmac.to_string()]), + CliOption::new("x-scf", ["--x-scf", "0x0"]) + ])), + + // Test --flags with cck-extension-secret and cck-update and --cck + // (conflict, equivalent to --enable-cck-extension-secret) + flat_map_collect(insert(mvca, vec![ + CliOption::new("flags-cck-extension-secret", ["--flags", &SeHdrFlagName::CckExtensionSecretEnforcement.to_string()]), + CliOption::new("flags-enable-cck-update", ["--flags", &SeHdrFlagName::CckUpdate.to_string()]), + CliOption::new("cck", ["--cck", "/dev/null"]) + ])), ]; let mut genprotimg_valid_args = vec![ @@ -825,7 +1057,11 @@ mod test { for arg in pvimg_invalid_args { let res = CliOptions::try_parse_from(&arg); - assert!(res.is_err()); + // For some conflict tests, parsing succeeds but validation should fail + if let Ok(opts) = res { + // This should be caught by validate_cli + assert!(validate_cli(&opts).is_err(), "Expected validation to fail for conflicting flags"); + } } for arg in genprotimg_valid_args { @@ -840,7 +1076,12 @@ mod test { for arg in genprotimg_invalid_args { let res = GenprotimgCliOptions::try_parse_from(&arg); - assert!(res.is_err()); + // For conflict tests, parsing succeeds but validation should fail + if let Ok(genprotimg_opts) = res { + let opts: CliOptions = genprotimg_opts.into(); + // This should be caught by validate_cli + assert!(validate_cli(&opts).is_err(), "Expected validation to fail for conflicting flags"); + } } } diff --git a/rust/pvimg/src/cmd/create.rs b/rust/pvimg/src/cmd/create.rs index 293c94a4..57f4bd4a 100644 --- a/rust/pvimg/src/cmd/create.rs +++ b/rust/pvimg/src/cmd/create.rs @@ -5,18 +5,18 @@ use std::fs::OpenOptions; use std::io::BufReader; -use anyhow::{Context, Result}; -use log::{info, debug, warn}; +use anyhow::{anyhow, Context, Result}; +use log::{debug, info, warn}; use pv::misc::{open_file, try_parse_u64}; use pvimg::error::OwnExitCode; use pvimg::secured_comp::ComponentTrait; use pvimg::uvdata::{ - ControlFlagTrait, ControlFlagsTrait, FlagData, PcfV1, PlaintextControlFlagsV1, ScfV1, - SeHdrDataV1, SecretControlFlagsV1, + EffectiveControlFlags, FlagState, FlagsOverride, SeHdrControlFlags, SeHdrControlFlagsModel, + SeHdrDataV1, SeHdrFlag, SeHdrVersion, SeTarget, }; use utils::{AtomicFile, AtomicFileOperation}; -use crate::cli::{ComponentPaths, CreateBootImageArgs}; +use crate::cli::{ComponentPaths, CreateBootImageArgs, SeHdrFlagName}; use crate::cmd::common::read_user_provided_keys; use crate::se_img::{SeHdrArgs, SeImgBuilder}; use crate::se_img_comps::cmdline::Cmdline; @@ -24,6 +24,21 @@ use crate::se_img_comps::kernel::S390Kernel; use crate::se_img_comps::ramdisk::Ramdisk; use crate::se_img_comps::{check_components, Component}; +/// Convert CLI flag name to internal SeHdrFlag +fn convert_flag_name(name: SeHdrFlagName) -> SeHdrFlag { + match name { + SeHdrFlagName::ConfidentialDump => SeHdrFlag::ConfidentialDump, + SeHdrFlagName::PckmoDeaTdea => SeHdrFlag::PckmoDeaTdea, + SeHdrFlagName::PckmoAes => SeHdrFlag::PckmoAes, + SeHdrFlagName::PckmoEcc => SeHdrFlag::PckmoEcc, + SeHdrFlagName::PckmoHmac => SeHdrFlag::PckmoHmac, + SeHdrFlagName::BackupTargetKeys => SeHdrFlag::BackupTargetKeys, + SeHdrFlagName::CckExtensionSecretEnforcement => SeHdrFlag::CckExtensionSecretEnforcement, + SeHdrFlagName::CckUpdate => SeHdrFlag::CckUpdate, + SeHdrFlagName::NoComponentEncryption => SeHdrFlag::NoComponentEncryption, + } +} + /// The returned vector is sorted by the occurrence in the memory layout: /// First the kernel, then the ramdisk and then the kernel cmdline. /// @@ -43,76 +58,218 @@ fn components(component_args: &ComponentPaths) -> Result> { Ok(components) } -fn parse_flags( - args: &CreateBootImageArgs, -) -> Result<(PlaintextControlFlagsV1, SecretControlFlagsV1)> { - let lf = &args.legacy_flags; +/// Parse new-style flags (--flags and --disable-flags) +fn parse_new_style_flags( + flags: &[SeHdrFlagName], + disable_flags: &[SeHdrFlagName], + version: SeHdrVersion, +) -> Result<(FlagsOverride, FlagsOverride)> { + let target = SeTarget::from_se_hdr_version(version); + let mut pcf_overrides: FlagsOverride = FlagsOverride::new(); + let mut scf_overrides: FlagsOverride = FlagsOverride::new(); + + if flags.is_empty() && disable_flags.is_empty() { + return Ok((pcf_overrides, scf_overrides)); + } + + let pcf_model = SeHdrControlFlagsModel::pcf_for_target(target); + let scf_model = SeHdrControlFlagsModel::scf_for_target(target); + let pcf_supported = pcf_model.supported_flags(); + let scf_supported = scf_model.supported_flags(); + + // Helper function to process flags + let mut process_flags = + |flag_list: &[SeHdrFlagName], enable: bool, flag_type: &str| -> Result<()> { + if flag_list.is_empty() { + return Ok(()); + } + + let converted_flags: Vec = + flag_list.iter().map(|&f| convert_flag_name(f)).collect(); + + for flag in &converted_flags { + if pcf_supported.contains(flag) { + if enable { + pcf_overrides.enable(*flag); + } else { + pcf_overrides.disable(*flag); + } + } + if scf_supported.contains(flag) { + if enable { + scf_overrides.enable(*flag); + } else { + scf_overrides.disable(*flag); + } + } + } + + // Check if all flags were consumed (supported by either pcf or scf) + let unsupported_flags: Vec<&SeHdrFlag> = converted_flags + .iter() + .filter(|flag| !pcf_supported.contains(flag) && !scf_supported.contains(flag)) + .collect(); + + if !unsupported_flags.is_empty() { + return Err(anyhow!( + "The following {} are not supported for SE header version {:?}: {:?}", + flag_type, + version, + unsupported_flags + )); + } + + Ok(()) + }; + + process_flags(flags, true, "flags")?; + process_flags(disable_flags, false, "disable flags")?; + + Ok((pcf_overrides, scf_overrides)) +} + +/// Parse legacy-style flags +fn parse_legacy_flags( + legacy_flags: &crate::cli::CreateBootImageLegacyFlags, +) -> (FlagsOverride, FlagsOverride) { macro_rules! flag_disabled { ($cli_flag:expr, $control_flags:expr) => { - $cli_flag - .filter(|x| *x) - .and(Some(ControlFlagTrait::all_disabled($control_flags))) + $cli_flag.filter(|x| *x).map(|_| { + let mut flags = FlagsOverride::new(); + flags.disable_all($control_flags); + flags + }) }; } macro_rules! flag_enabled { ($cli_flag:expr, $control_flags:expr) => { - $cli_flag - .filter(|x| *x) - .and(Some(ControlFlagTrait::all_enabled($control_flags))) + $cli_flag.filter(|x| *x).map(|_| { + let mut flags = FlagsOverride::new(); + flags.enable_all($control_flags); + flags + }) }; } - let plaintext_flags: Vec> = [ - flag_disabled!(lf.disable_dump, [PcfV1::AllowDumping]), - flag_enabled!(lf.enable_dump, [PcfV1::AllowDumping]), - flag_disabled!(lf.disable_pckmo, PlaintextControlFlagsV1::PCKMO), - flag_enabled!(lf.enable_pckmo, PlaintextControlFlagsV1::PCKMO), - flag_disabled!(lf.disable_pckmo_hmac, [PcfV1::PckmoHmac]), - flag_enabled!(lf.enable_pckmo_hmac, [PcfV1::PckmoHmac]), - flag_disabled!(lf.disable_backup_keys, [PcfV1::BackupTargetKeys]), - flag_enabled!(lf.enable_backup_keys, [PcfV1::BackupTargetKeys]), - flag_enabled!(lf.disable_image_encryption, [PcfV1::NoComponentEncryption]), - flag_disabled!(lf.enable_image_encryption, [PcfV1::NoComponentEncryption]), - ] - .into_iter() - .flatten() - .flatten() - .collect(); - // This is ensured by Clap's `conflicts_with`. - assert!(PlaintextControlFlagsV1::no_duplicates(&plaintext_flags)); - - let secret_flags: Vec> = [ + let pcf_overrides = [ + flag_disabled!(legacy_flags.disable_dump, [SeHdrFlag::ConfidentialDump]), + flag_enabled!(legacy_flags.enable_dump, [SeHdrFlag::ConfidentialDump]), + flag_disabled!(legacy_flags.disable_pckmo, SeHdrControlFlagsModel::PCKMO), + flag_enabled!(legacy_flags.enable_pckmo, SeHdrControlFlagsModel::PCKMO), + flag_disabled!(legacy_flags.disable_pckmo_hmac, [SeHdrFlag::PckmoHmac]), + flag_enabled!(legacy_flags.enable_pckmo_hmac, [SeHdrFlag::PckmoHmac]), flag_disabled!( - lf.disable_cck_extension_secret, - [ScfV1::CckExtensionSecretEnforcement] + legacy_flags.disable_backup_keys, + [SeHdrFlag::BackupTargetKeys] ), flag_enabled!( - lf.enable_cck_extension_secret, - [ScfV1::CckExtensionSecretEnforcement] + legacy_flags.enable_backup_keys, + [SeHdrFlag::BackupTargetKeys] + ), + flag_enabled!( + legacy_flags.disable_image_encryption, + [SeHdrFlag::NoComponentEncryption] + ), + flag_disabled!( + legacy_flags.enable_image_encryption, + [SeHdrFlag::NoComponentEncryption] ), - flag_disabled!(lf.disable_cck_update, [ScfV1::CckUpdateAllowed]), - flag_enabled!(lf.enable_cck_update, [ScfV1::CckUpdateAllowed]), ] .into_iter() .flatten() + .fold(FlagsOverride::new(), |mut acc, override_set| { + for (flag, state) in override_set.iter() { + match state { + FlagState::Enabled => acc.enable(*flag), + FlagState::Disabled => acc.disable(*flag), + } + } + acc + }); + + let scf_overrides = [ + flag_disabled!( + legacy_flags.disable_cck_extension_secret, + [SeHdrFlag::CckExtensionSecretEnforcement] + ), + flag_enabled!( + legacy_flags.enable_cck_extension_secret, + [SeHdrFlag::CckExtensionSecretEnforcement] + ), + flag_disabled!(legacy_flags.disable_cck_update, [SeHdrFlag::CckUpdate]), + flag_enabled!(legacy_flags.enable_cck_update, [SeHdrFlag::CckUpdate]), + ] + .into_iter() .flatten() - .collect(); - // This is ensured by Clap's `conflicts_with`. - assert!(SecretControlFlagsV1::no_duplicates(&secret_flags)); + .fold(FlagsOverride::new(), |mut acc, override_set| { + for (flag, state) in override_set.iter() { + match state { + FlagState::Enabled => acc.enable(*flag), + FlagState::Disabled => acc.disable(*flag), + } + } + acc + }); - let mut pcf: PlaintextControlFlagsV1 = match &args.experimental_args.x_pcf { - Some(v) => try_parse_u64(v, "x-pcf")?.into(), - None => PlaintextControlFlagsV1::default(), - }; - pcf.parse_flags(&plaintext_flags); - info!("Using plaintext flags: {pcf:#x}"); + (pcf_overrides, scf_overrides) +} - let mut scf: SecretControlFlagsV1 = match &args.experimental_args.x_scf { - Some(v) => try_parse_u64(v, "x-scf")?.into(), - None => SecretControlFlagsV1::default(), +/// Apply experimental overrides to control flags +fn apply_experimental_overrides( + pcf_overrides: &FlagsOverride, + scf_overrides: &FlagsOverride, + x_pcf: &Option, + x_scf: &Option, + target: SeTarget, +) -> Result<( + EffectiveControlFlags, + EffectiveControlFlags, +)> { + let pcf = match x_pcf { + Some(v) => { + assert_eq!(pcf_overrides.len(), 0); + SeHdrControlFlags::from_u64(try_parse_u64(v, "x-pcf")?, target, true) + } + None => SeHdrControlFlagsModel::pcf_for_target(target).with_overrides(pcf_overrides)?, }; - scf.parse_flags(&secret_flags); - info!("Using secret flags: {scf:#x}"); + + let scf = match x_scf { + Some(v) => { + assert_eq!(scf_overrides.len(), 0); + SeHdrControlFlags::from_u64(try_parse_u64(v, "x-scf")?, target, false) + } + None => SeHdrControlFlagsModel::scf_for_target(target).with_overrides(scf_overrides)?, + }; + + Ok((pcf, scf)) +} + +fn parse_flags( + args: &CreateBootImageArgs, + version: SeHdrVersion, +) -> Result<( + EffectiveControlFlags, + EffectiveControlFlags, +)> { + let target = SeTarget::from_se_hdr_version(version); + + // Legacy flags and --(disable-)flags are mutually exclusive. Clap semantics + // is used for that. + let (pcf_overrides, scf_overrides) = if args.flags.is_empty() && args.disable_flags.is_empty() { + parse_legacy_flags(&args.legacy_flags) + } else { + parse_new_style_flags(&args.flags, &args.disable_flags, version)? + }; + + let (pcf, scf) = apply_experimental_overrides( + &pcf_overrides, + &scf_overrides, + &args.experimental_args.x_pcf, + &args.experimental_args.x_scf, + target, + )?; + info!("Using plaintext flags:\n{pcf}"); + info!("Using secret flags:\n{scf}"); Ok((pcf, scf)) } @@ -125,9 +282,9 @@ pub fn create(opt: &CreateBootImageArgs) -> Result { .certificate_args .get_verified_hkds("Secure Execution image")?; let user_provided_keys = read_user_provided_keys(&opt.keys)?; - let (plaintext_flags, secret_flags) = parse_flags(opt)?; + let (plaintext_flags, secret_flags) = parse_flags(opt, SeHdrVersion::V1)?; - if plaintext_flags.is_set(PcfV1::NoComponentEncryption) { + if plaintext_flags.has(SeHdrFlag::NoComponentEncryption) { warn!("The components encryption is disabled, make sure that the components do not contain any confidential content."); } @@ -144,7 +301,7 @@ pub fn create(opt: &CreateBootImageArgs) -> Result { let mut writer = AtomicFile::with_extension(&opt.output, "part", &mut OpenOptions::new())?; let mut seimg_ctx = SeImgBuilder::new_v1( &mut writer, - plaintext_flags.is_unset(PcfV1::NoComponentEncryption), + !plaintext_flags.has(SeHdrFlag::NoComponentEncryption), Some(expected_se_hdr_size), opt.experimental_args.x_bootloader_directory.as_ref(), )?; @@ -218,13 +375,104 @@ mod test { }, ..Default::default() }; - let parsed_flags = super::parse_flags(&args).expect("Failed to parse flags {args:?}"); - let mut exp_pcf = Vec::from(PlaintextControlFlagsV1::PCKMO); - exp_pcf.push(PcfV1::AllowDumping); - let pcf = PlaintextControlFlagsV1::from_flags(PcfV1::all_enabled(exp_pcf)); - assert_eq!(parsed_flags.0, pcf); - let exp_scf = vec![ScfV1::CckUpdateAllowed]; - let scf = SecretControlFlagsV1::from_flags(ScfV1::all_enabled(exp_scf)); - assert_eq!(parsed_flags.1, scf); + let parsed_flags = + super::parse_flags(&args, SeHdrVersion::V1).expect("Failed to parse flags {args:?}"); + + // Build expected PCF + let mut exp_pcf = Vec::from(SeHdrControlFlagsModel::PCKMO); + exp_pcf.push(SeHdrFlag::ConfidentialDump); + let mut pcf_overrides = FlagsOverride::new(); + for flag in &exp_pcf { + pcf_overrides.enable(*flag); + } + let expected_pcf = SeHdrControlFlagsModel::pcf_for_target(SeTarget::V1Max) + .with_overrides(&pcf_overrides) + .expect("Failed to create expected PCF"); + assert_eq!(parsed_flags.0, expected_pcf); + + // Build expected SCF + let exp_scf = vec![SeHdrFlag::CckUpdate]; + let mut scf_overrides = FlagsOverride::new(); + for flag in &exp_scf { + scf_overrides.enable(*flag); + } + let expected_scf = SeHdrControlFlagsModel::scf_for_target(SeTarget::V1Max) + .with_overrides(&scf_overrides) + .expect("Failed to create expected SCF"); + assert_eq!(parsed_flags.1, expected_scf); + } + + #[test] + fn parse_flags_with_disable_flags_no_conflict() { + let args = CreateBootImageArgs { + flags: vec![SeHdrFlagName::ConfidentialDump], + disable_flags: vec![SeHdrFlagName::PckmoHmac], + ..Default::default() + }; + let result = super::parse_flags(&args, SeHdrVersion::V1); + assert!(result.is_ok()); + + let (pcf, scf) = result.unwrap(); + // ConfidentialDump should be enabled + assert!(pcf.has(SeHdrFlag::ConfidentialDump)); + // PckmoHmac should be disabled + assert!(!pcf.has(SeHdrFlag::PckmoHmac)); + assert_eq!(pcf.to_u64(), 0b100000000000000000000011100000_u64); + assert_eq!(scf.to_u64(), 0b0_u64); + } + + #[test] + fn parse_flags_with_multiple_disable_flags() { + let args = CreateBootImageArgs { + disable_flags: vec![SeHdrFlagName::PckmoHmac, SeHdrFlagName::BackupTargetKeys], + ..Default::default() + }; + let result = super::parse_flags(&args, SeHdrVersion::V1); + assert!(result.is_ok()); + + let (pcf, _scf) = result.unwrap(); + // Both flags should be disabled + assert!(!pcf.has(SeHdrFlag::PckmoHmac)); + assert!(!pcf.has(SeHdrFlag::BackupTargetKeys)); + } + + #[test] + fn parse_flags_with_only_disable_flags() { + let args = CreateBootImageArgs { + disable_flags: vec![SeHdrFlagName::PckmoDeaTdea, SeHdrFlagName::PckmoAes], + ..Default::default() + }; + let result = super::parse_flags(&args, SeHdrVersion::V1); + assert!(result.is_ok()); + + let (pcf, _scf) = result.unwrap(); + // PCKMO DEA/TDEA and AES should be disabled + assert!(!pcf.has(SeHdrFlag::PckmoDeaTdea)); + assert!(!pcf.has(SeHdrFlag::PckmoAes)); + } + + #[test] + fn parse_flags_enable_and_disable_different_flags() { + let args = CreateBootImageArgs { + flags: vec![ + SeHdrFlagName::ConfidentialDump, + SeHdrFlagName::BackupTargetKeys, + ], + disable_flags: vec![ + SeHdrFlagName::PckmoHmac, + SeHdrFlagName::NoComponentEncryption, + ], + ..Default::default() + }; + let result = super::parse_flags(&args, SeHdrVersion::V1); + assert!(result.is_ok()); + + let (pcf, _scf) = result.unwrap(); + // Enabled flags should be set + assert!(pcf.has(SeHdrFlag::ConfidentialDump)); + assert!(pcf.has(SeHdrFlag::BackupTargetKeys)); + // Disabled flags should not be set + assert!(!pcf.has(SeHdrFlag::PckmoHmac)); + assert!(!pcf.has(SeHdrFlag::NoComponentEncryption)); } } diff --git a/rust/pvimg/src/lib.rs b/rust/pvimg/src/lib.rs index 3f72bd43..002adfad 100644 --- a/rust/pvimg/src/lib.rs +++ b/rust/pvimg/src/lib.rs @@ -36,11 +36,11 @@ pub mod misc { pub mod uvdata { pub use crate::pv_utils::{ - AeadPlainDataTrait, BuilderTrait, ComponentMetadataV1, ControlFlagTrait, ControlFlagsTrait, - EnvelopeSeHdrV1, FlagData, KeyExchangeTrait, PcfV1, PlaintextControlFlagsV1, ScfV1, SeH, - SeHdr, SeHdrAadV1, SeHdrBinV1, SeHdrBuilder, SeHdrData, SeHdrDataV1, SeHdrPlain, - SeHdrVersion, SeHdrVersioned, SecretControlFlagsV1, UvDataPlainTrait, UvDataTrait, - UvKeyHashesV1, + AeadPlainDataTrait, BuilderTrait, ComponentMetadataV1, ControlFlagTrait, + EffectiveControlFlags, EnvelopeSeHdrV1, FlagData, FlagState, FlagsOverride, + IntoEnumIterator, KeyExchangeTrait, SeH, SeHdr, SeHdrAadV1, SeHdrBinV1, SeHdrBuilder, + SeHdrControlFlags, SeHdrControlFlagsModel, SeHdrData, SeHdrDataV1, SeHdrFlag, SeHdrPlain, + SeHdrVersion, SeHdrVersioned, SeTarget, UvDataPlainTrait, UvDataTrait, UvKeyHashesV1, }; } diff --git a/rust/pvimg/src/pv_utils.rs b/rust/pvimg/src/pv_utils.rs index be645916..0c6955a8 100644 --- a/rust/pvimg/src/pv_utils.rs +++ b/rust/pvimg/src/pv_utils.rs @@ -18,9 +18,10 @@ pub use layout::{Interval, Layout}; pub use misc::{round_up, try_copy_slice_to_array}; pub use psw::{ShortPsw, PSW, PSW_MASK_BA, PSW_MASK_EA}; pub use se_hdr::{ - ComponentMetadataV1, ControlFlagTrait, ControlFlagsTrait, EnvelopeSeHdrV1, FlagData, PcfV1, - PlaintextControlFlagsV1, ScfV1, SeH, SeHdr, SeHdrAadV1, SeHdrBinV1, SeHdrBuilder, SeHdrData, - SeHdrDataV1, SeHdrPlain, SeHdrVersion, SeHdrVersioned, SecretControlFlagsV1, + ComponentMetadataV1, ControlFlagTrait, EffectiveControlFlags, EnvelopeSeHdrV1, FlagData, + FlagState, FlagsOverride, IntoEnumIterator, SeH, SeHdr, SeHdrAadV1, SeHdrBinV1, SeHdrBuilder, + SeHdrControlFlags, SeHdrControlFlagsModel, SeHdrData, SeHdrDataV1, SeHdrFlag, SeHdrPlain, + SeHdrVersion, SeHdrVersioned, SeTarget, }; pub use secured_comp::{ComponentTrait, SecuredComponent, SecuredComponentBuilder}; pub use serializing::{bytesize, serialize_to_bytes}; diff --git a/rust/pvimg/src/pv_utils/se_hdr.rs b/rust/pvimg/src/pv_utils/se_hdr.rs index 6e7d926f..2de26b4f 100644 --- a/rust/pvimg/src/pv_utils/se_hdr.rs +++ b/rust/pvimg/src/pv_utils/se_hdr.rs @@ -5,6 +5,7 @@ mod brb; mod builder; mod flags; +mod generic_flags; mod hdr_v1; mod keys; @@ -14,7 +15,8 @@ pub use brb::{ }; pub use builder::SeHdrBuilder; pub use flags::{ - ControlFlagTrait, ControlFlagsTrait, FlagData, PcfV1, PlaintextControlFlagsV1, ScfV1, - SecretControlFlagsV1, + ControlFlagTrait, EffectiveControlFlags, FlagData, FlagState, FlagsOverride, SeHdrControlFlags, + SeHdrControlFlagsModel, SeHdrFlag, SeTarget, }; +pub use generic_flags::IntoEnumIterator; pub use hdr_v1::SeHdrAadV1; diff --git a/rust/pvimg/src/pv_utils/se_hdr/brb.rs b/rust/pvimg/src/pv_utils/se_hdr/brb.rs index 0cd527ac..976a58bd 100644 --- a/rust/pvimg/src/pv_utils/se_hdr/brb.rs +++ b/rust/pvimg/src/pv_utils/se_hdr/brb.rs @@ -15,7 +15,7 @@ use serde::{Deserialize, Serialize}; use utils::S390ToolsMetaData; pub use super::hdr_v1::{SeHdrBinV1, SeHdrDataV1}; -use super::{PlaintextControlFlagsV1, SecretControlFlagsV1}; +use super::{EffectiveControlFlags, SeHdrFlag}; use crate::misc::PAGESIZE; use crate::pv_utils::error::{Error, Result}; use crate::pv_utils::misc::display_indented; @@ -267,7 +267,7 @@ impl AeadCipherBuilderTrait for SeHdrData { pub trait SeHdrPubBuilderTrait { // Payload related methods fn set_components(&mut self, meta: ComponentMetadata) -> Result<()>; - fn set_pcf(&mut self, pcf: &PlaintextControlFlagsV1) -> Result<()>; + fn set_pcf(&mut self, pcf: &EffectiveControlFlags) -> Result<()>; } #[allow(dead_code)] @@ -276,7 +276,7 @@ pub trait SeHdrConfBuilderTrait { fn generate_cck(&self) -> Result; fn set_cck(&mut self, cck: Confidential>) -> Result<()>; fn set_psw(&mut self, psw: &PSW); - fn set_scf(&mut self, scf: &SecretControlFlagsV1) -> Result<()>; + fn set_scf(&mut self, scf: &EffectiveControlFlags) -> Result<()>; } #[enum_dispatch(SeHdr)] diff --git a/rust/pvimg/src/pv_utils/se_hdr/builder.rs b/rust/pvimg/src/pv_utils/se_hdr/builder.rs index 292a30a3..1223848c 100644 --- a/rust/pvimg/src/pv_utils/se_hdr/builder.rs +++ b/rust/pvimg/src/pv_utils/se_hdr/builder.rs @@ -5,7 +5,7 @@ use pv::request::Confidential; use super::hdr_v1::SeHdrDataV1; -use super::SeHdr; +use super::{EffectiveControlFlags, SeHdr, SeHdrFlag}; use crate::pv_utils::error::{Error, Result}; use crate::pv_utils::se_hdr::brb::{ SeHdrCommon, SeHdrConfBuilderTrait, SeHdrData, SeHdrPubBuilderTrait, @@ -15,7 +15,7 @@ use crate::pv_utils::uvdata::UvDataPlainTrait; use crate::pv_utils::uvdata_builder::{ AeadCipherBuilderTrait, BuilderTrait, KeyExchangeBuilderTrait, UvDataBuilder, }; -use crate::pv_utils::{PlaintextControlFlagsV1, SecretControlFlagsV1, PSW}; +use crate::pv_utils::PSW; /// `SeHdrBuilder` pub type SeHdrBuilder<'a> = UvDataBuilder<'a, SeHdrPlain>; @@ -67,12 +67,12 @@ impl SeHdrBuilder<'_> { Ok(self) } - pub fn with_pcf(&mut self, flags: &PlaintextControlFlagsV1) -> Result<&mut Self> { + pub fn with_pcf(&mut self, flags: &EffectiveControlFlags) -> Result<&mut Self> { self.plain_data.data.set_pcf(flags)?; Ok(self) } - pub fn with_scf(&mut self, flags: &SecretControlFlagsV1) -> Result<&mut Self> { + pub fn with_scf(&mut self, flags: &EffectiveControlFlags) -> Result<&mut Self> { self.plain_data.data.set_scf(flags)?; Ok(self) } diff --git a/rust/pvimg/src/pv_utils/se_hdr/flags.rs b/rust/pvimg/src/pv_utils/se_hdr/flags.rs index 39a5dbf2..2d9e364e 100644 --- a/rust/pvimg/src/pv_utils/se_hdr/flags.rs +++ b/rust/pvimg/src/pv_utils/se_hdr/flags.rs @@ -1,583 +1,744 @@ // SPDX-License-Identifier: MIT // -// Copyright IBM Corp. 2024 +// Copyright IBM Corp. //! Control flags for Secure Execution (SE) headers. //! -//! This module provides types and traits for managing control flags used in -//! IBM Secure Execution headers. It supports two types of flags: -//! - Plaintext Control Flags (PCF) -//! - Secret Control Flags (SCF) +//! This module provides a flexible, type-safe API for managing control flags used in +//! IBM Secure Execution (SE) headers. It supports both plaintext and secret control flags +//! with version-specific configurations. //! -//! # Examples +//! # Architecture //! +//! The module is built around several key components: +//! +//! ## Core Types +//! +//! - [`SeHdrFlag`] - Unified enum containing all control flags (plaintext and secret) +//! - [`SeHdrControlFlagsModel`] - Version-specific configuration model that defines: +//! - Which flags are supported in a given SE header version +//! - Which flags are enabled by default +//! - [`FlagsOverride`] - Container for user-specified flag overrides (enable/disable) +//! - [`FlagState`] - Represents whether a flag is enabled or disabled +//! +//! ## Traits +//! +//! - [`ControlFlagTrait`] - Core trait for flag types, providing: +//! - `bit_position()` - Returns the bit position in the control flags bitfield +//! - `enabled()`/`disabled()` - Create flag data with specific states +//! - `AsRef` - Enables flexible API usage with both owned and borrowed values +//! +//! - [`IntoEnumIterator`] - Enables iteration over all flag variants +//! +//! # Usage Patterns +//! +//! ## 1. Getting Version-Specific Configuration +//! +//! ```rust +//! use pvimg::uvdata::{SeHdrControlFlagsModel, SeTarget}; +//! +//! // Get plaintext control flags configuration for V1-max +//! let pcf_v1 = SeHdrControlFlagsModel::pcf_for_target(SeTarget::V1Max); //! ``` -//! use pvimg::uvdata::{ControlFlagTrait, ControlFlagsTrait, PcfV1, PlaintextControlFlagsV1}; //! -//! // Create flags with specific settings -//! let flags = PlaintextControlFlagsV1::from_flags([ -//! PcfV1::AllowDumping.enabled(), -//! PcfV1::PckmoAes.enabled(), -//! ]); +//! ## 2. Checking Flag Support and Defaults +//! +//! ```rust +//! use pvimg::uvdata::{SeHdrControlFlagsModel, SeHdrFlag, SeTarget}; +//! +//! let pcf_v1 = SeHdrControlFlagsModel::pcf_for_target(SeTarget::V1Max); +//! +//! // Check if a flag is supported in this target +//! assert!(pcf_v1.supports(SeHdrFlag::ConfidentialDump)); +//! +//! // Check if a flag is enabled by default +//! assert!(pcf_v1.is_default(SeHdrFlag::PckmoAes)); +//! assert!(!pcf_v1.is_default(SeHdrFlag::ConfidentialDump)); +//! ``` +//! +//! ## 3. Applying Overrides +//! +//! ```rust +//! use pvimg::uvdata::{FlagsOverride, SeHdrControlFlagsModel, SeHdrFlag, SeTarget}; +//! +//! let pcf_v1 = SeHdrControlFlagsModel::pcf_for_target(SeTarget::V1Max); +//! +//! // Create overrides to customize flags +//! let mut overrides = FlagsOverride::new(); +//! overrides.enable(SeHdrFlag::ConfidentialDump); +//! overrides.disable(SeHdrFlag::PckmoAes); +//! +//! // Validate and apply overrides +//! let result = pcf_v1.with_overrides(&overrides); +//! assert!(result.is_ok()); +//! ``` +//! +//! # Error Handling +//! +//! The API uses [`FlagValidationError`] to report issues: +//! +//! - `NotSupported` - Attempted to override a flag not supported in the target version //! -//! // Check if a flag is set -//! assert!(flags.is_set(PcfV1::AllowDumping)); //! ``` -use std::fmt::{Display, LowerHex}; -use std::marker::PhantomData; -use std::mem::size_of; +use std::collections::HashSet; use pv::misc::{Flags, Msb0Flags64}; +use utils::ControlFlag; -pub trait IntoEnumIterator: Sized { - /// Returns an iterator over all variants of this enum. - fn iter() -> impl Iterator; -} +// Re-export IntoEnumIterator for external use +pub use super::generic_flags::IntoEnumIterator; +// Re-export generic types and traits from generic_flags module +pub use super::generic_flags::{ + ControlFlagTrait, ControlFlagsModel, EffectiveControlFlags, FlagData, FlagState, FlagsOverride, + SeTarget, UnknownFlags, +}; -/// Trait for individual control flag types. -/// -/// This trait defines the interface for control flag enums, providing methods -/// to get the flag's bit position and create enabled/disabled flag data. -/// Implementors must be enum types with `#[repr(u8)]` to ensure proper bit positioning. -pub trait ControlFlagTrait: - std::fmt::Debug + std::hash::Hash + Copy + Eq + Ord + Display + IntoEnumIterator -{ - /// Returns the bit position (0-63) for this flag in MSB0 ordering. - /// - /// # Safety - /// - /// This method assumes the implementing type is `#[repr(u8)]` and performs - /// an unsafe cast to extract the discriminant value. - fn discriminant(&self) -> u8 { - assert!(size_of::() == size_of::()); - unsafe { *(self as *const Self as *const u8) } - } +pub type SeHdrControlFlags = EffectiveControlFlags; - /// Creates flag data with this flag in the enabled state. - fn enabled(self) -> FlagData { - FlagData::new(self, FlagState::Enabled) - } - - /// Creates flag data with this flag in the disabled state. - fn disabled(self) -> FlagData { - FlagData::new(self, FlagState::Disabled) - } - - /// Creates a vector of flag data with all specified flags enabled. +impl SeHdrControlFlags { + /// Creates an `EffectiveControlFlags` from a u64 value for the specified target. /// /// # Arguments /// - /// * `flags` - A collection of flags to enable - fn all_enabled>(flags: F) -> Vec> { - flags - .as_ref() - .iter() - .map(|flag| (*flag).enabled()) - .collect() - } - - /// Creates a vector of flag data with all specified flags disabled. - /// - /// # Arguments - /// - /// * `flags` - A collection of flags to disable - fn all_disabled>(flags: F) -> Vec> { - flags - .as_ref() - .iter() - .map(|flag| (*flag).disabled()) - .collect() - } -} - -/// Internal state of a control flag (enabled or disabled). -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] -enum FlagState { - /// Flag is enabled (bit set to 1) - Enabled, - /// Flag is disabled (bit set to 0) - Disabled, -} - -/// Represents a control flag with its associated state. -/// -/// This structure pairs a flag with its enabled/disabled state, used when -/// constructing or modifying `ControlFlags` instances. -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] -pub struct FlagData { - value: T, - state: FlagState, -} - -impl FlagData { - const fn new(value: T, state: FlagState) -> Self { - Self { value, state } - } -} - -/// Trait for managing control flags in Secure Execution headers. -/// -/// This trait provides methods for parsing, checking, and validating -/// control flags used in Secure Execution headers. -pub trait ControlFlagsTrait { - /// The underlying control flag type - type T: ControlFlagTrait; - - /// Creates a new instance from a collection of flag data - fn from_flags]>>(flags: F) -> Self; - - /// Parses and applies flag data to this instance - fn parse_flags]>>(&mut self, flags: F); - - /// Checks if a specific flag is set - fn is_set(&self, flag: Self::T) -> bool; - - /// Checks if a specific flag is not set - fn is_unset(&self, flag: Self::T) -> bool { - !self.is_set(flag) - } - - /// Validates that there are no duplicate flags in the collection - fn no_duplicates]>>(flags: F) -> bool { - let mut flags_sorted = flags.as_ref().to_vec(); - flags_sorted.sort_by_key(|data| data.value); - flags_sorted.dedup_by_key(|data| data.value); - - flags_sorted.len() == flags.as_ref().len() - } - - /// Checks if all specified flags are set. - /// - /// # Arguments - /// - /// * `flags` - A collection of flags to check + /// * `value` - The u64 bitfield value + /// * `target` - The target SE header flags configuration + /// * `is_pcf` - Whether this is for plaintext control flags (true) or secret control flags + /// (false) /// /// # Returns /// - /// `true` if all flags are set, `false` otherwise - fn all_set>(&self, flags: F) -> bool { - flags.as_ref().iter().all(|flag| self.is_set(*flag)) - } - - /// Checks if all specified flags are unset. - /// - /// # Arguments - /// - /// * `flags` - A collection of flags to check - /// - /// # Returns - /// - /// `true` if all flags are unset, `false` otherwise - fn all_unset>(&self, flags: F) -> bool { - flags.as_ref().iter().all(|flag| self.is_unset(*flag)) - } -} - -/// Bitflags container for Secure Execution control flags. -/// -/// This structure wraps a 64-bit value with MSB0 (Most Significant Bit first) -/// ordering, as used by IBM Secure Execution. Each bit position corresponds to -/// a specific control flag defined by the generic type parameter `T`. -/// -/// # Type Parameters -/// -/// * `T` - The control flag enum type (e.g., [`PcfV1`] or [`ScfV1`]) -/// -/// # Examples -/// -/// ```rust -/// use pvimg::uvdata::{ControlFlagsTrait, PcfV1, PlaintextControlFlagsV1}; -/// -/// // Create from u64 (bit 57 MSB0 = 0x40) -/// let flags: PlaintextControlFlagsV1 = 0x0000000000000040_u64.into(); -/// -/// // Check if a flag is set -/// assert!(flags.is_set(PcfV1::PckmoAes)); -/// -/// // Convert back to u64 -/// let value: u64 = flags.into(); -/// assert_eq!(value, 0x0000000000000040_u64); -/// ``` -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct ControlFlags { - flags: Msb0Flags64, - t: PhantomData, -} - -impl ControlFlags { - /// Creates a new instance with all flags disabled. - fn new() -> Self { - Self { - flags: 0x0.into(), - t: PhantomData {}, - } - } - - /// Returns a vector of all currently enabled flags. - /// - /// # Returns - /// - /// A vector containing all flags that are currently set (enabled) - pub fn flags(&self) -> Vec { - T::iter() - .filter(|flag| self.flags.is_set(flag.discriminant())) - .collect() - } -} - -impl From for ControlFlags { - fn from(value: u64) -> Self { - Self { - flags: value.into(), - t: PhantomData, - } - } -} - -impl From<&ControlFlags> for u64 { - fn from(value: &ControlFlags) -> Self { - value.flags.into() - } -} - -impl From> for u64 { - fn from(value: ControlFlags) -> Self { - value.flags.into() - } -} - -impl ControlFlagsTrait for ControlFlags { - type T = T; - - fn from_flags]>>(flags: F) -> Self { - let mut ret = Self::new(); - ret.parse_flags(flags); - ret - } - - fn parse_flags]>>(&mut self, flags: F) { - flags.as_ref().iter().for_each(|v| match v.state { - FlagState::Enabled => self.flags.set_bit(v.value.discriminant()), - FlagState::Disabled => self.flags.unset_bit(v.value.discriminant()), - }); - } - - fn is_set(&self, flag: T) -> bool { - self.flags.is_set(flag.discriminant()) - } -} - -impl LowerHex for ControlFlags { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let value: u64 = self.flags.into(); - write!(f, "{value:#018x}") - } -} - -/// Plaintext Control Flags for Secure Execution header version 1. -/// -/// These flags control various aspects of Protected Virtualization (PV) guest -/// behavior and capabilities. Each variant represents a specific bit position -/// in the 64-bit control flags field (MSB0 ordering). -/// -/// # Bit Positions -/// -/// The numeric values represent bit positions in MSB0 ordering (bit 0 is the -/// most significant bit). For example, `AllowDumping = 34` means bit 34 from -/// the left (MSB). -#[repr(u8)] -#[non_exhaustive] -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub enum PcfV1 { - /// Enables Protected Virtualization guest dump support. - /// - /// When set, allows dumping of the PV guest for debugging purposes. - AllowDumping = 34, - - /// Disables component encryption during image unpacking. - /// - /// When set, components are not decrypted during the SE image unpack process. - NoComponentEncryption = 35, - - /// Enables DEA/TDEA PCKMO encryption functions. - /// - /// Allows the guest to use Data Encryption Algorithm (DEA) and Triple DEA - /// with the Perform Cryptographic Key Management Operation (PCKMO) instruction. - PckmoDeaTdea = 56, - - /// Enables AES PCKMO encryption functions. - /// - /// Allows the guest to use Advanced Encryption Standard (AES) with PCKMO. - PckmoAes = 57, - - /// Enables ECC PCKMO encryption functions. - /// - /// Allows the guest to use Elliptic Curve Cryptography (ECC) with PCKMO. - PckmoEcc = 58, - - /// Enables HMAC PCKMO encryption functions. - /// - /// Allows the guest to use Hash-based Message Authentication Code (HMAC) with PCKMO. - PckmoHmac = 59, - - /// Enables backup target keys support. - /// - /// When set, allows the use of backup target keys for key management operations. - BackupTargetKeys = 62, -} - -impl IntoEnumIterator for PcfV1 { - fn iter() -> impl Iterator { - [ - Self::AllowDumping, - Self::NoComponentEncryption, - Self::PckmoDeaTdea, - Self::PckmoAes, - Self::PckmoEcc, - Self::PckmoHmac, - Self::BackupTargetKeys, - ] - .into_iter() - } -} - -impl ControlFlagTrait for PcfV1 {} - -impl Display for ControlFlags { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if f.alternate() { - write!(f, "{:#066b}", ::from(self.flags)) + /// An `EffectiveControlFlags` with flags extracted from the u64 value + pub fn from_u64(value: u64, target: SeTarget, is_pcf: bool) -> Self { + let flags = Msb0Flags64::from(value); + let target_model = if is_pcf { + SeHdrControlFlagsModel::pcf_for_target(target) } else { - let known_flags = self.flags(); - let mut flags_s = known_flags - .iter() - .map(|flag| format!(" - {flag}")) - .collect::>(); - let known_flags_u64: u64 = Self::from_flags(T::all_enabled(known_flags)).into(); - let unknown_flags = ::from(self.flags) - known_flags_u64; - if unknown_flags != 0x0 { - flags_s.push(format!( - " - unknown flags {:#}", - ::from(unknown_flags) - )); - } + SeHdrControlFlagsModel::scf_for_target(target) + }; - write!(f, "{}", flags_s.join("\n")) + // Parse the flags and extract known flags + let mut known_flags = HashSet::new(); + let mut unhandled_bits = flags; + + for flag in SeHdrFlag::iter() { + if flags.is_set(flag.bit_position()) && target_model.supports(flag) { + known_flags.insert(flag); + // Clear this bit from unhandled + unhandled_bits.unset_bit(flag.bit_position()); + } } - } -} -/// Type alias for plaintext control flags version 1. -/// -/// This is the primary type used for managing plaintext control flags in -/// SE header version 1. -pub type PlaintextControlFlagsV1 = ControlFlags; -impl PlaintextControlFlagsV1 { - /// Array of all PCKMO-related flags (excluding HMAC). - /// - /// This constant provides convenient access to the three main PCKMO flags - /// that are typically enabled together. - pub const PCKMO: [PcfV1; 3] = [PcfV1::PckmoAes, PcfV1::PckmoDeaTdea, PcfV1::PckmoEcc]; -} - -impl Default for PlaintextControlFlagsV1 { - /// Creates default plaintext control flags with PCKMO support enabled. - fn default() -> Self { - Self::from_flags(PcfV1::all_enabled(PlaintextControlFlagsV1::PCKMO)) - } -} - -impl Display for PcfV1 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Self::AllowDumping => "allow dumping", - Self::NoComponentEncryption => "no component encryption", - Self::PckmoDeaTdea => "DEA and TDEA PCKMO support", - Self::PckmoAes => "AES PCKMO support", - Self::PckmoEcc => "ECC PCKMO support", - Self::PckmoHmac => "HMAC PCKMO support", - Self::BackupTargetKeys => "backup target keys support", - } + EffectiveControlFlags::new( + target.to_se_hdr_version(), + known_flags, + UnknownFlags::from_bits(unhandled_bits), ) } } -/// Secret Control Flags for Secure Execution header version 1. +/// Type alias for plaintext control flags configuration. +pub type SeHdrControlFlagsModel = ControlFlagsModel; + +impl SeHdrControlFlagsModel { + /// Array of all PCKMO-related flags (excluding HMAC). + pub const PCKMO: [SeHdrFlag; 3] = [ + SeHdrFlag::PckmoDeaTdea, + SeHdrFlag::PckmoAes, + SeHdrFlag::PckmoEcc, + ]; + + /// Creates a new PlaintextControlFlags for the specified SE header flags target with predefined + /// defaults. + pub fn pcf_for_target(target: SeTarget) -> Self { + use SeHdrFlag::*; + + // Common flags for both V1 and V2 + let common_supported = [ + ConfidentialDump, + NoComponentEncryption, + PckmoDeaTdea, + PckmoAes, + PckmoEcc, + PckmoHmac, + BackupTargetKeys, + ]; + let common_defaults = [PckmoDeaTdea, PckmoAes, PckmoEcc]; + + let (default, supported) = match target { + SeTarget::V1Max => ( + common_defaults.into_iter().collect(), + common_supported.into_iter().collect(), + ), + }; + + Self::new(target, default, supported) + } + + /// Creates a new SecretControlFlags for the specified SE header flags target with predefined + /// defaults. + pub fn scf_for_target(target: SeTarget) -> Self { + use SeHdrFlag::*; + + let supported = [CckExtensionSecretEnforcement, CckUpdate] + .into_iter() + .collect(); + let default = HashSet::new(); + + Self::new(target, default, supported) + } +} + +/// Control Flags - All possible flags across all versions. /// -/// These flags control various aspects of Protected Virtualization (PV) guest -/// behavior and capabilities. Each variant represents a specific bit position -/// in the 64-bit control flags field (MSB0 ordering). -/// -/// # Bit Positions -/// -/// The numeric values represent bit positions in MSB0 ordering (bit 0 is the -/// most significant bit). For example, `CckExtensionSecretEnforcement = 1` means bit 1 from -/// the left (MSB). -#[repr(u8)] +/// This enum contains all control flags (both plaintext and secret) that can be used across +/// different SE header versions. +#[derive(ControlFlag, Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] #[non_exhaustive] -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub enum ScfV1 { +pub enum SeHdrFlag { + #[flag(display = "CCK extension secret enforced", value = 1)] /// Enforces extension secret requirement for add-secret requests. /// /// When set, all add-secret requests must provide an extension secret. /// This adds an additional layer of security to secret management. - CckExtensionSecretEnforcement = 1, + CckExtensionSecretEnforcement, + #[flag(display = "CCK update allowed", value = 2)] /// Allows Customer Communication Key (CCK) updates. /// /// When set, permits updating the CCK after initial configuration. - CckUpdateAllowed = 2, + CckUpdate, + + #[flag(display = "confidential guest dump support", value = 34)] + /// Enables Confidential guest dump support. + /// + /// When set, allows dumping of the Secure Execution guest for debugging purposes. + ConfidentialDump, + + #[flag(display = "no component encryption", value = 35)] + /// Disables component encryption during image unpacking. + /// + /// When set, components are not decrypted during the SE image unpack process. + NoComponentEncryption, + + #[flag(display = "DEA and TDEA PCKMO support", value = 56)] + /// Enables DEA/TDEA PCKMO encryption functions. + /// + /// Allows the guest to use Data Encryption Algorithm (DEA) and Triple DEA + /// with the Perform Cryptographic Key Management Operation (PCKMO) instruction. + PckmoDeaTdea, + + #[flag(display = "AES PCKMO support", value = 57)] + /// Enables AES PCKMO encryption functions. + /// + /// Allows the guest to use Advanced Encryption Standard (AES) with PCKMO. + PckmoAes, + + #[flag(display = "ECC PCKMO support", value = 58)] + /// Enables ECC PCKMO encryption functions. + /// + /// Allows the guest to use Elliptic Curve Cryptography (ECC) with PCKMO. + PckmoEcc, + + #[flag(display = "HMAC PCKMO support", value = 59)] + /// Enables HMAC PCKMO encryption functions. + /// + /// Allows the guest to use Hash-based Message Authentication Code (HMAC) with PCKMO. + PckmoHmac, + + #[flag(display = "backup target keys support", value = 62)] + /// Enables backup target keys support. + /// + /// When set, allows the use of backup target keys for key management operations. + BackupTargetKeys, } -impl IntoEnumIterator for ScfV1 { - fn iter() -> impl Iterator { - [Self::CckExtensionSecretEnforcement, Self::CckUpdateAllowed].into_iter() +// Implement AsRef for SeHdrFlag to enable flexible API usage +impl AsRef for SeHdrFlag { + fn as_ref(&self) -> &Self { + self } } -impl ControlFlagTrait for ScfV1 {} +impl std::str::FromStr for SeHdrFlag { + type Err = String; -impl Display for ScfV1 { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - Self::CckExtensionSecretEnforcement => "CCK extension secret enforced", - Self::CckUpdateAllowed => "CCK update allowed", + fn from_str(s: &str) -> Result { + let normalized = s.to_lowercase().replace('_', "-"); + + // Try to find a flag whose display name matches the input + for flag in Self::iter() { + let display_name = format!("{}", flag).to_lowercase().replace(' ', "-"); + + // Check for exact match with display name + if normalized == display_name { + return Ok(flag); } - ) - } -} -/// Type alias for secret control flags version 1. -/// -/// This is the primary type used for managing secret control flags in -/// SE header version 1. -pub type SecretControlFlagsV1 = ControlFlags; + // Check for common abbreviations + let matches = match flag { + Self::PckmoDeaTdea => normalized == "pckmo-dea-tdea" || normalized == "pckmo-dea", + Self::BackupTargetKeys => { + normalized == "backup-keys" || normalized == "backup-target-keys" + } + Self::CckExtensionSecretEnforcement => { + normalized == "cck-extension-secret" + || normalized == "cck-extension-secret-enforcement" + } + Self::CckUpdate => normalized == "cck-update" || normalized == "cck-update-allowed", + _ => false, + }; -impl Default for SecretControlFlagsV1 { - /// Creates default secret control flags. - fn default() -> Self { - Self::from_flags(ScfV1::all_enabled([])) + if matches { + return Ok(flag); + } + } + + Err(format!("Unknown flag name: '{}'", s)) } } #[allow(clippy::shadow_unrelated)] #[cfg(test)] mod test { - use super::{ - ControlFlagTrait, ControlFlagsTrait, PcfV1, PlaintextControlFlagsV1, ScfV1, - SecretControlFlagsV1, - }; + use pv::misc::Flags; + + use super::super::brb::SeHdrVersion; + use super::*; #[test] - fn test_from_flags() { - let flags = PlaintextControlFlagsV1::from_flags(&[]); - assert_eq!(u64::from(flags), 0_u64); + fn test_pcfs_v1() { + let pcfs_v1 = SeHdrControlFlagsModel::pcf_for_target(SeTarget::V1Max); - let flags = PlaintextControlFlagsV1::from_flags([PcfV1::AllowDumping.enabled()]); - assert_eq!(u64::from(&flags), 536870912); - assert!(flags.is_set(PcfV1::AllowDumping)); + // V1 flags + assert!(pcfs_v1.supports(SeHdrFlag::ConfidentialDump)); + assert!(pcfs_v1.supports(SeHdrFlag::NoComponentEncryption)); + assert!(pcfs_v1.supports(SeHdrFlag::PckmoDeaTdea)); + assert!(pcfs_v1.supports(SeHdrFlag::PckmoAes)); + assert!(pcfs_v1.supports(SeHdrFlag::PckmoEcc)); + assert!(pcfs_v1.supports(SeHdrFlag::PckmoHmac)); + assert!(pcfs_v1.supports(SeHdrFlag::BackupTargetKeys)); - let flags = PlaintextControlFlagsV1::from_flags([ - PcfV1::AllowDumping.enabled(), - PcfV1::AllowDumping.disabled(), - ]); - assert_eq!(u64::from(flags), 0); - - let flags = PlaintextControlFlagsV1::from_flags([ - PcfV1::AllowDumping.disabled(), - PcfV1::AllowDumping.enabled(), - ]); - assert_eq!(u64::from(&flags), 536870912); - - let flags = PlaintextControlFlagsV1::from_flags([ - PcfV1::AllowDumping.enabled(), - PcfV1::BackupTargetKeys.enabled(), - ]); - assert_eq!(u64::from(&flags), 536870914); + // Check defaults (PCKMO flags) + assert!(pcfs_v1.is_default(SeHdrFlag::PckmoDeaTdea)); + assert!(pcfs_v1.is_default(SeHdrFlag::PckmoAes)); + assert!(pcfs_v1.is_default(SeHdrFlag::PckmoEcc)); + assert!(!pcfs_v1.is_default(SeHdrFlag::ConfidentialDump)); } #[test] - fn test_all_set_unset() { - let flags = PlaintextControlFlagsV1::from_flags([ - PcfV1::AllowDumping.enabled(), - PcfV1::BackupTargetKeys.enabled(), - ]); - assert!(flags.all_set([PcfV1::AllowDumping, PcfV1::BackupTargetKeys])); - assert!(!flags.all_set([PcfV1::NoComponentEncryption, PcfV1::BackupTargetKeys])); - assert!(!flags.all_unset([PcfV1::NoComponentEncryption, PcfV1::BackupTargetKeys])); - assert!(flags.all_unset([PcfV1::NoComponentEncryption, PcfV1::PckmoHmac])); + fn test_scfs_v1() { + let scfs_v1 = SeHdrControlFlagsModel::scf_for_target(SeTarget::V1Max); + + assert!(scfs_v1.supports(SeHdrFlag::CckExtensionSecretEnforcement)); + assert!(scfs_v1.supports(SeHdrFlag::CckUpdate)); + + // No defaults for secret flags + assert!(!scfs_v1.is_default(SeHdrFlag::CckExtensionSecretEnforcement)); + assert!(!scfs_v1.is_default(SeHdrFlag::CckUpdate)); } #[test] - fn test_display() { - let flags = PlaintextControlFlagsV1::from_flags([PcfV1::NoComponentEncryption.enabled()]); - assert_eq!("0x0000000010000000", format!("{flags:#x}")); - assert_eq!(format!("{flags}"), " - no component encryption"); + fn test_scfs_v1_with_overrides() { + // Create base model for V1 secret control flags + let scf_v1 = SeHdrControlFlagsModel::scf_for_target(SeTarget::V1Max); - let flags = PlaintextControlFlagsV1::from_flags([ - PcfV1::AllowDumping.enabled(), - PcfV1::BackupTargetKeys.enabled(), - PcfV1::NoComponentEncryption.enabled(), - PcfV1::PckmoAes.enabled(), - PcfV1::PckmoDeaTdea.enabled(), - PcfV1::PckmoEcc.enabled(), - PcfV1::PckmoHmac.enabled(), + // Verify defaults are empty + assert!(scf_v1.default_flags().is_empty()); + + // Create overrides to enable specific flags + let mut overrides = FlagsOverride::new(); + overrides.enable_all([ + SeHdrFlag::CckExtensionSecretEnforcement, + SeHdrFlag::CckUpdate, ]); - assert_eq!("0x00000000300000f2", format!("{flags:#x}")); + + // Verify overrides were set + assert_eq!(overrides.len(), 2); + assert!(overrides.has_override(SeHdrFlag::CckExtensionSecretEnforcement)); + assert!(overrides.has_override(SeHdrFlag::CckUpdate)); assert_eq!( - format!("{flags}"), - " - allow dumping - - no component encryption - - DEA and TDEA PCKMO support - - AES PCKMO support - - ECC PCKMO support - - HMAC PCKMO support - - backup target keys support" + overrides.get(SeHdrFlag::CckExtensionSecretEnforcement), + Some(FlagState::Enabled) + ); + assert_eq!( + overrides.get(SeHdrFlag::CckUpdate), + Some(FlagState::Enabled) ); - let flags = SecretControlFlagsV1::from_flags([ - ScfV1::CckExtensionSecretEnforcement.enabled(), - ScfV1::CckUpdateAllowed.enabled(), - ]); - assert_eq!(format!("{flags:#x}"), "0x6000000000000000"); - assert_eq!( - format!("{flags}"), - " - CCK extension secret enforced - - CCK update allowed" - ); + // Apply overrides to get configured model + let configured_scf = scf_v1 + .with_overrides(&overrides) + .expect("Valid overrides should succeed"); + + // Verify the configured model still has the same version and supported flags + assert_eq!(configured_scf.version(), SeHdrVersion::V1); + assert_eq!(configured_scf.known_flags().len(), 2); + assert!(configured_scf.has(SeHdrFlag::CckExtensionSecretEnforcement)); + assert!(configured_scf.has(SeHdrFlag::CckUpdate)); } #[test] - fn test_no_duplicates() { - let flags: Vec<_> = [ - PcfV1::all_disabled([PcfV1::PckmoAes, PcfV1::PckmoDeaTdea, PcfV1::PckmoEcc]), - PcfV1::all_enabled([PcfV1::PckmoAes, PcfV1::PckmoDeaTdea, PcfV1::PckmoEcc]), - ] - .into_iter() - .flatten() - .collect(); - assert!(!PlaintextControlFlagsV1::no_duplicates(flags)); + fn test_scfs_v1_partial_overrides() { + // Create base model for V1 secret control flags + let scf_v1 = SeHdrControlFlagsModel::scf_for_target(SeTarget::V1Max); - let flags: Vec<_> = [ - PcfV1::all_disabled([PcfV1::PckmoAes]), - PcfV1::all_enabled([PcfV1::PckmoDeaTdea, PcfV1::PckmoEcc]), - ] - .into_iter() - .flatten() - .collect(); - assert!(PlaintextControlFlagsV1::no_duplicates(flags)); + // Create overrides to enable only one flag + let mut overrides = FlagsOverride::new(); + overrides.enable(SeHdrFlag::CckExtensionSecretEnforcement); - let flags: Vec<_> = - std::iter::once(PcfV1::all_disabled([PcfV1::PckmoAes, PcfV1::PckmoAes])) - .flatten() - .collect(); - assert!(!PlaintextControlFlagsV1::no_duplicates(flags)); + // Verify only one override was set + assert_eq!(overrides.len(), 1); + assert!(overrides.has_override(SeHdrFlag::CckExtensionSecretEnforcement)); + assert!(!overrides.has_override(SeHdrFlag::CckUpdate)); + + // Apply overrides + let configured_scf = scf_v1 + .with_overrides(&overrides) + .expect("Valid overrides should succeed"); + + // Verify the configured model maintains version and support + assert_eq!(configured_scf.version(), SeHdrVersion::V1); + assert!(configured_scf.has(SeHdrFlag::CckExtensionSecretEnforcement)); + assert!(!configured_scf.has(SeHdrFlag::CckUpdate)); + } + + #[test] + fn test_pcfs_v1_with_overrides_enable_additional() { + // Create base model for V1 plaintext control flags + let pcf_v1 = SeHdrControlFlagsModel::pcf_for_target(SeTarget::V1Max); + + // Verify V1 defaults: PckmoDeaTdea, PckmoAes, PckmoEcc (but NOT PckmoHmac) + assert_eq!(pcf_v1.default_flags().len(), 3); + assert!(pcf_v1.is_default(SeHdrFlag::PckmoDeaTdea)); + assert!(pcf_v1.is_default(SeHdrFlag::PckmoAes)); + assert!(pcf_v1.is_default(SeHdrFlag::PckmoEcc)); + assert!(!pcf_v1.is_default(SeHdrFlag::PckmoHmac)); + assert!(!pcf_v1.is_default(SeHdrFlag::ConfidentialDump)); + + // Create overrides to enable additional flags + let mut overrides = FlagsOverride::new(); + overrides.enable_all([SeHdrFlag::PckmoHmac, SeHdrFlag::ConfidentialDump]); + + // Verify overrides were set + assert_eq!(overrides.len(), 2); + assert!(overrides.has_override(SeHdrFlag::PckmoHmac)); + assert!(overrides.has_override(SeHdrFlag::ConfidentialDump)); + assert_eq!( + overrides.get(SeHdrFlag::PckmoHmac), + Some(FlagState::Enabled) + ); + assert_eq!( + overrides.get(SeHdrFlag::ConfidentialDump), + Some(FlagState::Enabled) + ); + + // Apply overrides to get configured model + let configured_pcf = pcf_v1 + .with_overrides(&overrides) + .expect("Valid overrides should succeed"); + + // Verify the configured model maintains version and support + assert_eq!(configured_pcf.version(), SeHdrVersion::V1); + assert!(configured_pcf.has(SeHdrFlag::PckmoHmac)); + assert!(configured_pcf.has(SeHdrFlag::ConfidentialDump)); + } + + #[test] + fn test_pcfs_v1_with_overrides_disable_defaults() { + // Create base model for V1 plaintext control flags + let pcf_v1 = SeHdrControlFlagsModel::pcf_for_target(SeTarget::V1Max); + + // Verify V1 defaults include PckmoAes and PckmoEcc + assert!(pcf_v1.is_default(SeHdrFlag::PckmoAes)); + assert!(pcf_v1.is_default(SeHdrFlag::PckmoEcc)); + + // Create overrides to disable some default flags + let mut overrides = FlagsOverride::new(); + overrides.disable_all([SeHdrFlag::PckmoAes, SeHdrFlag::PckmoEcc]); + + // Verify overrides were set to disabled + assert_eq!(overrides.len(), 2); + assert_eq!( + overrides.get(SeHdrFlag::PckmoAes), + Some(FlagState::Disabled) + ); + assert_eq!( + overrides.get(SeHdrFlag::PckmoEcc), + Some(FlagState::Disabled) + ); + + // Apply overrides to get configured model + let configured_pcf = pcf_v1 + .with_overrides(&overrides) + .expect("Valid overrides should succeed"); + + // Verify the configured model maintains version and support + assert_eq!(configured_pcf.version(), SeHdrVersion::V1); + assert!(!configured_pcf.has(SeHdrFlag::PckmoAes)); + assert!(!configured_pcf.has(SeHdrFlag::PckmoEcc)); + } + + #[test] + fn test_asref_flexibility() { + // Test that both owned values and references work with AsRef-based API + let pcf_v1 = SeHdrControlFlagsModel::pcf_for_target(SeTarget::V1Max); + + // Using references (explicit) + assert!(pcf_v1.supports(SeHdrFlag::PckmoAes)); + assert!(pcf_v1.is_default(SeHdrFlag::PckmoAes)); + + // Using owned values (also works thanks to AsRef) + assert!(pcf_v1.supports(SeHdrFlag::PckmoAes)); + assert!(pcf_v1.is_default(SeHdrFlag::PckmoAes)); + + assert!(!pcf_v1.is_default(SeHdrFlag::ConfidentialDump)); + assert!(!pcf_v1.is_default(SeHdrFlag::ConfidentialDump)); + } + + #[test] + fn test_with_overrides_supported_flags_succeed() { + // Create V1 model + let pcf_v1 = SeHdrControlFlagsModel::pcf_for_target(SeTarget::V1Max); + + // Enable only supported flags + let mut overrides = FlagsOverride::new(); + overrides.enable_all([SeHdrFlag::ConfidentialDump, SeHdrFlag::PckmoHmac]); + + // Should succeed + let result = pcf_v1.with_overrides(&overrides); + assert!(result.is_ok()); + + let configured = result.unwrap(); + assert_eq!(configured.version(), SeHdrVersion::V1); + } + + #[test] + fn test_overrides_update_existing() { + // Test that enable/disable update existing overrides (canonical way) + let mut overrides = FlagsOverride::new(); + + // Initially enable a flag + overrides.enable(SeHdrFlag::PckmoAes); + assert_eq!(overrides.get(SeHdrFlag::PckmoAes), Some(FlagState::Enabled)); + assert_eq!(overrides.len(), 1); + + // Update the same flag to disabled (canonical way) + overrides.disable(SeHdrFlag::PckmoAes); + assert_eq!( + overrides.get(SeHdrFlag::PckmoAes), + Some(FlagState::Disabled) + ); + assert_eq!(overrides.len(), 1); // Still only one override + + // Update again to enabled (canonical way) + overrides.enable(SeHdrFlag::PckmoAes); + assert_eq!(overrides.get(SeHdrFlag::PckmoAes), Some(FlagState::Enabled)); + assert_eq!(overrides.len(), 1); // Still only one override + + // Add a different flag + overrides.enable(SeHdrFlag::PckmoEcc); + assert_eq!(overrides.len(), 2); // Now we have two overrides + + // Update the first flag again + overrides.disable(SeHdrFlag::PckmoAes); + assert_eq!( + overrides.get(SeHdrFlag::PckmoAes), + Some(FlagState::Disabled) + ); + assert_eq!(overrides.get(SeHdrFlag::PckmoEcc), Some(FlagState::Enabled)); + assert_eq!(overrides.len(), 2); // Still two overrides + + // Alternative: set() can also be used but enable/disable are preferred + overrides.set(SeHdrFlag::PckmoAes, FlagState::Enabled); + assert_eq!(overrides.get(SeHdrFlag::PckmoAes), Some(FlagState::Enabled)); + assert_eq!(overrides.len(), 2); // Still two overrides + } + + #[test] + fn test_plaintext_control_flags_construction_v1() { + // Test construction of PlaintextControlFlags for V1 + let pcf_v1 = SeHdrControlFlagsModel::pcf_for_target(SeTarget::V1Max); + + // Verify version + assert_eq!(pcf_v1.version(), SeHdrVersion::V1); + + // Verify default flags for V1 + let default_flags = pcf_v1.default_flags(); + assert_eq!(default_flags.len(), 3); + assert!(default_flags.contains(&SeHdrFlag::PckmoDeaTdea)); + assert!(default_flags.contains(&SeHdrFlag::PckmoAes)); + assert!(default_flags.contains(&SeHdrFlag::PckmoEcc)); + assert!(!default_flags.contains(&SeHdrFlag::PckmoHmac)); + + // Verify supported flags for V1 + let supported_flags = pcf_v1.supported_flags(); + assert_eq!(supported_flags.len(), 7); + assert!(supported_flags.contains(&SeHdrFlag::ConfidentialDump)); + assert!(supported_flags.contains(&SeHdrFlag::NoComponentEncryption)); + assert!(supported_flags.contains(&SeHdrFlag::PckmoDeaTdea)); + assert!(supported_flags.contains(&SeHdrFlag::PckmoAes)); + assert!(supported_flags.contains(&SeHdrFlag::PckmoEcc)); + assert!(supported_flags.contains(&SeHdrFlag::PckmoHmac)); + assert!(supported_flags.contains(&SeHdrFlag::BackupTargetKeys)); + } + + #[test] + fn test_secret_control_flags_construction_v1() { + // Test construction of SecretControlFlags for V1 + let scf_v1 = SeHdrControlFlagsModel::scf_for_target(SeTarget::V1Max); + + // Verify version + assert_eq!(scf_v1.version(), SeHdrVersion::V1); + + // Verify default flags for V1 (should be empty) + let default_flags = scf_v1.default_flags(); + assert_eq!(default_flags.len(), 0); + assert!(default_flags.is_empty()); + + // Verify supported flags for V1 + let supported_flags = scf_v1.supported_flags(); + assert_eq!(supported_flags.len(), 2); + assert!(supported_flags.contains(&SeHdrFlag::CckExtensionSecretEnforcement)); + assert!(supported_flags.contains(&SeHdrFlag::CckUpdate)); + } + + // Tests for Into trait implementations + + #[test] + fn test_into_msb0_flags_owned() { + // Test Into for owned ControlFlagsModel + let pcf_v1 = SeHdrControlFlagsModel::pcf_for_target(SeTarget::V1Max); + + // V1 defaults: PckmoDeaTdea, PckmoAes, PckmoEcc + let flags: Msb0Flags64 = pcf_v1.into(); + + // Verify the default flags are set + assert!(flags.is_set(SeHdrFlag::PckmoDeaTdea.bit_position())); + assert!(flags.is_set(SeHdrFlag::PckmoAes.bit_position())); + assert!(flags.is_set(SeHdrFlag::PckmoEcc.bit_position())); + + // Verify non-default flags are not set + assert!(!flags.is_set(SeHdrFlag::PckmoHmac.bit_position())); + assert!(!flags.is_set(SeHdrFlag::ConfidentialDump.bit_position())); + } + + #[test] + fn test_to_msb0_flags_with_overrides() { + let pcf_v1 = SeHdrControlFlagsModel::pcf_for_target(SeTarget::V1Max); + + // Create overrides + let mut overrides = FlagsOverride::new(); + overrides.enable(SeHdrFlag::ConfidentialDump); + overrides.disable(SeHdrFlag::PckmoAes); + + let flags: Msb0Flags64 = pcf_v1.with_overrides(&overrides).unwrap().into(); + // Verify overrides were applied + assert!(flags.is_set(SeHdrFlag::ConfidentialDump.bit_position())); + assert!(!flags.is_set(SeHdrFlag::PckmoAes.bit_position())); + + // Verify other defaults remain + assert!(flags.is_set(SeHdrFlag::PckmoDeaTdea.bit_position())); + assert!(flags.is_set(SeHdrFlag::PckmoEcc.bit_position())); + } + + #[test] + fn test_bit_position() { + // Verify bit_position returns expected values + assert_eq!(SeHdrFlag::CckExtensionSecretEnforcement.bit_position(), 1); + assert_eq!(SeHdrFlag::CckUpdate.bit_position(), 2); + assert_eq!(SeHdrFlag::ConfidentialDump.bit_position(), 34); + assert_eq!(SeHdrFlag::NoComponentEncryption.bit_position(), 35); + assert_eq!(SeHdrFlag::PckmoDeaTdea.bit_position(), 56); + assert_eq!(SeHdrFlag::PckmoAes.bit_position(), 57); + assert_eq!(SeHdrFlag::PckmoEcc.bit_position(), 58); + assert_eq!(SeHdrFlag::PckmoHmac.bit_position(), 59); + assert_eq!(SeHdrFlag::BackupTargetKeys.bit_position(), 62); + } + + #[test] + fn test_asref_trait() { + // Test that AsRef works for flags + let flag = SeHdrFlag::PckmoAes; + let flag_ref: &SeHdrFlag = flag.as_ref(); + assert_eq!(*flag_ref, flag); + } + + // Tests for from_u64 method + + #[test] + fn test_effective_control_flags_from_u64_pcf_v1() { + // Test creating EffectiveControlFlags from u64 for PCF V1 + let mut value = 0u64; + // Set some PCF V1 flags + value |= 1u64 << (63 - SeHdrFlag::ConfidentialDump.bit_position()); + value |= 1u64 << (63 - SeHdrFlag::PckmoAes.bit_position()); + value |= 1u64 << (63 - SeHdrFlag::PckmoDeaTdea.bit_position()); + + let flags = SeHdrControlFlags::from_u64(value, SeTarget::V1Max, true); + + assert_eq!(flags.version(), SeHdrVersion::V1); + assert!(flags.has(SeHdrFlag::ConfidentialDump)); + assert!(flags.has(SeHdrFlag::PckmoAes)); + assert!(flags.has(SeHdrFlag::PckmoDeaTdea)); + assert!(!flags.has(SeHdrFlag::PckmoHmac)); // Not set + } + + #[test] + fn test_effective_control_flags_from_u64_scf_v1() { + // Test creating EffectiveControlFlags from u64 for SCF V1 + let mut value = 0u64; + // Set some SCF V1 flags + value |= 1u64 << (63 - SeHdrFlag::CckExtensionSecretEnforcement.bit_position()); + value |= 1u64 << (63 - SeHdrFlag::CckUpdate.bit_position()); + + let flags = SeHdrControlFlags::from_u64(value, SeTarget::V1Max, false); + + assert_eq!(flags.version(), SeHdrVersion::V1); + assert!(flags.has(SeHdrFlag::CckExtensionSecretEnforcement)); + assert!(flags.has(SeHdrFlag::CckUpdate)); + } + + #[test] + fn test_effective_control_flags_from_u64_with_unknown_bits() { + // Test that unknown/unsupported bits are tracked + let mut value = 0u64; + // Set a known flag + value |= 1u64 << (63 - SeHdrFlag::PckmoAes.bit_position()); + // Set some unknown bits + value |= 1u64 << 10; // Random bit that's not a known flag + + let flags = SeHdrControlFlags::from_u64(value, SeTarget::V1Max, true); + + assert!(flags.has(SeHdrFlag::PckmoAes)); + // Unknown flags should be tracked + assert_ne!(flags.unknown_flags().bits(), 0); + } + + #[test] + fn test_effective_control_flags_from_u64_roundtrip() { + // Test roundtrip: create flags, convert to u64, convert back + let mut value = 0u64; + value |= 1u64 << (63 - SeHdrFlag::ConfidentialDump.bit_position()); + value |= 1u64 << (63 - SeHdrFlag::PckmoAes.bit_position()); + value |= 1u64 << (63 - SeHdrFlag::BackupTargetKeys.bit_position()); + + let flags1 = SeHdrControlFlags::from_u64(value, SeTarget::V1Max, true); + let value2 = flags1.to_u64(); + let flags2 = SeHdrControlFlags::from_u64(value2, SeTarget::V1Max, true); + + assert_eq!(flags1.known_flags(), flags2.known_flags()); + assert_eq!(flags1.unknown_flags(), flags2.unknown_flags()); + } + + #[test] + fn test_effective_control_flags_from_u64_empty() { + // Test with no flags set + let value = 0u64; + let flags = SeHdrControlFlags::from_u64(value, SeTarget::V1Max, true); + + assert_eq!(flags.version(), SeHdrVersion::V1); + assert_eq!(flags.known_flags().len(), 0); + assert_eq!(flags.unknown_flags().bits(), 0); } } diff --git a/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs b/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs index 1ad4db20..6c937aa9 100644 --- a/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs +++ b/rust/pvimg/src/pv_utils/se_hdr/hdr_v1.rs @@ -18,6 +18,7 @@ use serde::{Deserialize, Serialize}; use utils::HexSlice; use super::keys::phkh_v1; +use super::{EffectiveControlFlags, SeHdrControlFlags}; use crate::error::Error; use crate::misc::PAGESIZE; use crate::pv_utils::error::Result; @@ -37,9 +38,7 @@ use crate::pv_utils::uvdata::{ UvDataTrait, }; use crate::pv_utils::uvdata_builder::{AeadCipherBuilderTrait, KeyExchangeBuilderTrait}; -use crate::pv_utils::{ - try_copy_slice_to_array, PlaintextControlFlagsV1, SecretControlFlagsV1, PSW, -}; +use crate::pv_utils::{try_copy_slice_to_array, SeHdrFlag, SeTarget, PSW}; #[derive(Debug)] struct HdrSizesV1 { @@ -100,7 +99,7 @@ impl Display for SeHdrAadV1 { writeln!( f, "plaintext control flags:\n{}", - PlaintextControlFlagsV1::from(self.pcf) + SeHdrControlFlags::from_u64(self.pcf, SeTarget::V1Max, true) )?; Ok(()) } @@ -176,7 +175,7 @@ impl Display for SeHdrConfV1 { writeln!( f, "secret control flags:\n{}", - SecretControlFlagsV1::from(self.scf) + SeHdrControlFlags::from_u64(self.scf, SeTarget::V1Max, false) )?; // Support verbose mode if the `alternate` (`{:#}`) flag is used. @@ -483,8 +482,8 @@ impl SeHdrConfBuilderTrait for SeHdrDataV1 { self.data.value_mut().psw = psw.clone(); } - fn set_scf(&mut self, scf: &SecretControlFlagsV1) -> Result<()> { - self.data.value_mut().scf = scf.into(); + fn set_scf(&mut self, scf: &EffectiveControlFlags) -> Result<()> { + self.data.value_mut().scf = scf.to_u64(); Ok(()) } @@ -499,8 +498,8 @@ impl SeHdrConfBuilderTrait for SeHdrDataV1 { } impl SeHdrPubBuilderTrait for SeHdrDataV1 { - fn set_pcf(&mut self, pcf: &PlaintextControlFlagsV1) -> Result<()> { - self.aad.pcf = pcf.into(); + fn set_pcf(&mut self, pcf: &EffectiveControlFlags) -> Result<()> { + self.aad.pcf = pcf.to_u64(); Ok(()) } diff --git a/rust/pvimg/src/se_img.rs b/rust/pvimg/src/se_img.rs index 14090c60..de422bd8 100644 --- a/rust/pvimg/src/se_img.rs +++ b/rust/pvimg/src/se_img.rs @@ -18,9 +18,7 @@ use pvimg::misc::{round_up, serialize_to_bytes, ShortPsw, PSW, PSW_MASK_BA, PSW_ use pvimg::secured_comp::{ ComponentTrait, Interval, Layout, SecuredComponent, SecuredComponentBuilder, }; -use pvimg::uvdata::{ - BuilderTrait, PlaintextControlFlagsV1, SeHdrBuilder, SeHdrVersion, SecretControlFlagsV1, -}; +use pvimg::uvdata::{BuilderTrait, EffectiveControlFlags, SeHdrBuilder, SeHdrFlag, SeHdrVersion}; use crate::se_img_comps::ipib::Ipib; use crate::se_img_comps::kernel::S390Kernel; @@ -34,8 +32,8 @@ use crate::se_img_comps::{ pub struct SeHdrArgs<'a> { pub keys: &'a [PKey], - pub pcf: &'a PlaintextControlFlagsV1, - pub scf: &'a SecretControlFlagsV1, + pub pcf: &'a EffectiveControlFlags, + pub scf: &'a EffectiveControlFlags, pub cck: &'a Option<(PathBuf, Confidential>)>, pub hdr_aead_key: &'a Option<(PathBuf, Confidential>)>, pub psw_addr: &'a Option,