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 <marc@linux.ibm.com>
Reviewed-by: Steffen Eiden <seiden@linux.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
This commit is contained in:
Marc Hartmayer
2026-06-24 12:47:01 +02:00
committed by Steffen Eiden
parent 20fffb31d7
commit c87d11abfe
11 changed files with 1264 additions and 616 deletions

View File

@@ -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\

View File

@@ -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::<Vec<_>>()
.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::<Vec<_>>().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::<Vec<_>>()
.join(", ")
),
));
}
}
Ok(())
}
_ => Ok(()),
@@ -100,6 +232,8 @@ pub struct ComponentPaths {
pub parmfile: Option<PathBuf>,
}
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<SeHdrFlagName>,
/// 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<SeHdrFlagName>,
#[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");
}
}
}

View File

@@ -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<Vec<Component>> {
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<SeHdrFlag>, FlagsOverride<SeHdrFlag>)> {
let target = SeTarget::from_se_hdr_version(version);
let mut pcf_overrides: FlagsOverride<SeHdrFlag> = FlagsOverride::new();
let mut scf_overrides: FlagsOverride<SeHdrFlag> = 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<SeHdrFlag> =
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<SeHdrFlag>, FlagsOverride<SeHdrFlag>) {
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<FlagData<PcfV1>> = [
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<FlagData<ScfV1>> = [
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<SeHdrFlag>,
scf_overrides: &FlagsOverride<SeHdrFlag>,
x_pcf: &Option<String>,
x_scf: &Option<String>,
target: SeTarget,
) -> Result<(
EffectiveControlFlags<SeHdrFlag>,
EffectiveControlFlags<SeHdrFlag>,
)> {
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<SeHdrFlag>,
EffectiveControlFlags<SeHdrFlag>,
)> {
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<OwnExitCode> {
.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<OwnExitCode> {
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));
}
}

View File

@@ -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,
};
}

View File

@@ -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};

View File

@@ -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;

View File

@@ -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<SeHdrFlag>) -> Result<()>;
}
#[allow(dead_code)]
@@ -276,7 +276,7 @@ pub trait SeHdrConfBuilderTrait {
fn generate_cck(&self) -> Result<SymKey>;
fn set_cck(&mut self, cck: Confidential<Vec<u8>>) -> Result<()>;
fn set_psw(&mut self, psw: &PSW);
fn set_scf(&mut self, scf: &SecretControlFlagsV1) -> Result<()>;
fn set_scf(&mut self, scf: &EffectiveControlFlags<SeHdrFlag>) -> Result<()>;
}
#[enum_dispatch(SeHdr)]

View File

@@ -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<SeHdrFlag>) -> 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<SeHdrFlag>) -> Result<&mut Self> {
self.plain_data.data.set_scf(flags)?;
Ok(self)
}

File diff suppressed because it is too large Load Diff

View File

@@ -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<SeHdrFlag>) -> 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<SeHdrFlag>) -> Result<()> {
self.aad.pcf = pcf.to_u64();
Ok(())
}

View File

@@ -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<Public>],
pub pcf: &'a PlaintextControlFlagsV1,
pub scf: &'a SecretControlFlagsV1,
pub pcf: &'a EffectiveControlFlags<SeHdrFlag>,
pub scf: &'a EffectiveControlFlags<SeHdrFlag>,
pub cck: &'a Option<(PathBuf, Confidential<Vec<u8>>)>,
pub hdr_aead_key: &'a Option<(PathBuf, Confidential<Vec<u8>>)>,
pub psw_addr: &'a Option<u64>,