// SPDX-License-Identifier: MIT // // Copyright IBM Corp. 2024 use std::{fmt::Display, marker::PhantomData, mem::size_of}; use pv::misc::{Flags, Msb0Flags64}; pub trait ControlFlagTrait: std::fmt::Debug + std::hash::Hash + Copy + Eq + Ord { fn discriminant(&self) -> u8 { assert!(size_of::() == size_of::()); unsafe { *(self as *const Self as *const u8) } } fn enabled(self) -> FlagData { FlagData::new(self, FlagState::Enabled) } fn disabled(self) -> FlagData { FlagData::new(self, FlagState::Disabled) } fn all_enabled>(flags: F) -> Vec> { flags .as_ref() .iter() .map(|flag| (*flag).enabled()) .collect() } fn all_disabled>(flags: F) -> Vec> { flags .as_ref() .iter() .map(|flag| (*flag).disabled()) .collect() } } #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] enum FlagState { Enabled, Disabled, } #[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 } } } pub trait ControlFlagsTrait: Display { type T: ControlFlagTrait; fn from_flags]>>(flags: F) -> Self; fn parse_flags]>>(&mut self, flags: F); fn is_set(&self, flag: Self::T) -> bool; fn is_unset(&self, flag: Self::T) -> bool { !self.is_set(flag) } 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() } fn all_set>(&self, flags: F) -> bool { flags.as_ref().iter().all(|flag| self.is_set(*flag)) } fn all_unset>(&self, flags: F) -> bool { flags.as_ref().iter().all(|flag| self.is_unset(*flag)) } } /// Bitflags as used by the Secure Execution in MSB0 ordering /// /// Wraps an u64 to set/get individual bits #[derive(Clone, Copy, Debug)] pub struct ControlFlags { flags: Msb0Flags64, t: PhantomData, } impl ControlFlags { fn new() -> Self { Self { flags: 0x0.into(), t: PhantomData {}, } } } 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 Display for ControlFlags { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let value: u64 = self.flags.into(); write!(f, "{:#018x}", value) } } #[repr(u8)] #[non_exhaustive] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum PcfV1 { /// PV guest dump support. AllowDumping = 34, /// The components are not decrypted during the image unpack. NoComponentEncryption = 35, /// DEA/TDEA PCKMO encryption function are allowed. PckmoDeaTdea = 56, /// AES PCKMO encryption function are allowed. PckmoAes = 57, /// ECC PCKMO encryption function are allowed. PckmoEcc = 58, /// HMAC PCKMO encryption function are allowed. PckmoHmac = 59, /// Backup target keys can be used. BackupTargetKeys = 62, } pub type PlaintextControlFlagsV1 = ControlFlags; impl Default for PlaintextControlFlagsV1 { fn default() -> Self { Self::from_flags(PcfV1::all_enabled([ PcfV1::PckmoAes, PcfV1::PckmoDeaTdea, PcfV1::PckmoEcc, ])) } } 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 PCMKO", Self::PckmoAes => "AES", Self::PckmoEcc => "ECC PCKMO", Self::PckmoHmac => "HMAC PCKMO", Self::BackupTargetKeys => "backup target keys", } ) } } impl ControlFlagTrait for PcfV1 {} #[repr(u8)] #[non_exhaustive] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] pub enum ScfV1 { /// All add-secret requests must provide an extension secret CCKExtensionSecretEnforcment = 1, } pub type SecretControlFlagsV1 = ControlFlags; impl ControlFlagTrait for ScfV1 {} impl Default for SecretControlFlagsV1 { fn default() -> Self { Self::from_flags(ScfV1::all_enabled([])) } } #[allow(clippy::shadow_unrelated)] #[cfg(test)] mod test { use super::{ControlFlagTrait, ControlFlagsTrait, PcfV1, PlaintextControlFlagsV1}; #[test] fn test_from_flags() { let flags = PlaintextControlFlagsV1::from_flags(&[]); assert_eq!(u64::from(flags), 0_u64); let flags = PlaintextControlFlagsV1::from_flags([PcfV1::AllowDumping.enabled()]); assert_eq!(u64::from(&flags), 536870912); assert!(flags.is_set(PcfV1::AllowDumping)); 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); } #[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])); } #[test] fn test_display() { let flags = PlaintextControlFlagsV1::from_flags([PcfV1::NoComponentEncryption.enabled()]); assert_eq!("0x0000000010000000", format!("{}", flags)); 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(), ]); assert_eq!("0x00000000300000f2", format!("{}", flags)); } #[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)); let flags: Vec<_> = [ PcfV1::all_disabled([PcfV1::PckmoAes]), PcfV1::all_enabled([PcfV1::PckmoDeaTdea, PcfV1::PckmoEcc]), ] .into_iter() .flatten() .collect(); assert!(PlaintextControlFlagsV1::no_duplicates(flags)); let flags: Vec<_> = std::iter::once(PcfV1::all_disabled([PcfV1::PckmoAes, PcfV1::PckmoAes])) .flatten() .collect(); assert!(!PlaintextControlFlagsV1::no_duplicates(flags)); } }