diff --git a/tss-esapi/src/abstraction/mod.rs b/tss-esapi/src/abstraction/mod.rs index 0efa5a57..2b3b5ecf 100644 --- a/tss-esapi/src/abstraction/mod.rs +++ b/tss-esapi/src/abstraction/mod.rs @@ -5,6 +5,7 @@ pub mod ak; pub mod cipher; pub mod ek; pub mod nv; +pub mod pcr; pub mod transient; use crate::{attributes::ObjectAttributesBuilder, structures::PublicBuilder}; diff --git a/tss-esapi/src/abstraction/pcr.rs b/tss-esapi/src/abstraction/pcr.rs new file mode 100644 index 00000000..79608253 --- /dev/null +++ b/tss-esapi/src/abstraction/pcr.rs @@ -0,0 +1,71 @@ +// Copyright 2021 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 +mod bank; +mod data; + +use crate::{structures::PcrSelectionList, Context, Result}; + +pub use bank::PcrBank; +pub use data::PcrData; + +/// Function that reads all the PCRs in a selection list and returns +/// the result as PCR data. +/// +/// # Example +/// +/// ```rust +/// # use tss_esapi::{Context, TctiNameConf}; +/// # // Create context +/// # let mut context = +/// # Context::new( +/// # TctiNameConf::from_environment_variable().expect("Failed to get TCTI"), +/// # ).expect("Failed to create Context"); +/// # +/// use tss_esapi::{ +/// interface_types::algorithm::HashingAlgorithm, +/// structures::{PcrSelectionListBuilder, PcrSlot}, +/// }; +/// // Create PCR selection list with slots in a bank +/// // that is going to be read. +/// let pcr_selection_list = PcrSelectionListBuilder::new() +/// .with_selection(HashingAlgorithm::Sha256, +/// &[ +/// PcrSlot::Slot0, +/// PcrSlot::Slot1, +/// PcrSlot::Slot2, +/// PcrSlot::Slot3, +/// PcrSlot::Slot4, +/// PcrSlot::Slot5, +/// PcrSlot::Slot6, +/// PcrSlot::Slot7, +/// PcrSlot::Slot8, +/// PcrSlot::Slot9, +/// PcrSlot::Slot10, +/// PcrSlot::Slot11, +/// PcrSlot::Slot12, +/// PcrSlot::Slot13, +/// PcrSlot::Slot14, +/// PcrSlot::Slot15, +/// PcrSlot::Slot16, +/// PcrSlot::Slot17, +/// PcrSlot::Slot18, +/// PcrSlot::Slot19, +/// PcrSlot::Slot20, +/// PcrSlot::Slot21, +/// ]) +/// .build(); +/// let _pcr_data = tss_esapi::abstraction::pcr::read_all(&mut context, pcr_selection_list) +/// .expect("pcr::read_all failed"); +/// ``` +pub fn read_all( + context: &mut Context, + mut pcr_selection_list: PcrSelectionList, +) -> Result { + let mut pcr_data = PcrData::new(); + while !pcr_selection_list.is_empty() { + let (_, pcrs_read, pcr_digests) = context.pcr_read(&pcr_selection_list)?; + pcr_data.add(&pcrs_read, &pcr_digests)?; + pcr_selection_list.subtract(&pcrs_read)?; + } + Ok(pcr_data) +} diff --git a/tss-esapi/src/abstraction/pcr/bank.rs b/tss-esapi/src/abstraction/pcr/bank.rs new file mode 100644 index 00000000..21ac0721 --- /dev/null +++ b/tss-esapi/src/abstraction/pcr/bank.rs @@ -0,0 +1,134 @@ +// Copyright 2021 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + structures::{Digest, PcrSlot}, + Error, Result, WrapperErrorKind, +}; +use log::error; +use std::collections::BTreeMap; + +/// Struct for holding PcrSlots and their +/// corresponding values. +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct PcrBank { + bank: BTreeMap, +} + +impl PcrBank { + /// Function that creates PcrBank from a vector of pcr slots and + /// a vector of pcr digests. + /// + /// # Details + /// The order of pcr slots are assumed to match the order of the Digests. + /// + /// # Error + /// - If number of pcr slots does not match the number of pcr digests + /// InconsistentParams error is returned. + /// + /// - If the vector of pcr slots contains duplicates then + /// InconsistentParams error is returned. + pub fn create(mut pcr_slots: Vec, mut digests: Vec) -> Result { + if pcr_slots.len() != digests.len() { + error!( + "Number of PcrSlots does not match the number of PCR digests. ({} != {})", + pcr_slots.len(), + digests.len() + ); + return Err(Error::local_error(WrapperErrorKind::InconsistentParams)); + } + pcr_slots + .drain(..) + .zip(digests.drain(..)) + .try_fold(BTreeMap::::new(), |mut data, (pcr_slot, digest)| { + if data.insert(pcr_slot, digest).is_none() { + Ok(data) + } else { + error!("Error trying to insert data into PcrSlot {:?} where data have already been inserted", pcr_slot); + Err(Error::local_error(WrapperErrorKind::InconsistentParams)) + } + }) + .map(|bank| PcrBank { bank }) + } + + /// Retrieves reference to a [Digest] associated with the provided [PcrSlot]. + /// + /// # Details + /// Returns a reference to a [Digest] associated with the provided [PcrSlot] + /// if one exists else returns None. + pub fn get_digest(&self, pcr_slot: PcrSlot) -> Option<&Digest> { + self.bank.get(&pcr_slot) + } + + /// Returns true if the [PcrBank] contains a digest + /// for the provided [PcrSlot]. + pub fn has_digest(&self, pcr_slot: PcrSlot) -> bool { + self.bank.contains_key(&pcr_slot) + } + + /// Number of digests in the [PcrBank] + pub fn len(&self) -> usize { + self.bank.len() + } + + /// Returns true if the [PcrBank] is empty + pub fn is_empty(&self) -> bool { + self.bank.is_empty() + } + + /// Removees the [Digest] associated with the [PcrSlot] and + /// returns it. + /// + /// # Details + /// Removes the [Digest] associated with the provided [PcrSlot] + /// out of the bank and returns it if it exists else returns None. + pub fn remove_digest(&mut self, pcr_slot: PcrSlot) -> Option { + self.bank.remove(&pcr_slot) + } + + /// Inserts [Digest] value associated with a [PcrSlot] into the bank. + /// + /// # Error + /// Returns an error if a [Digest] is already associated with the + /// provided [PcrSlot]. + pub fn insert_digest(&mut self, pcr_slot: PcrSlot, digest: Digest) -> Result<()> { + self.ensure_non_existing(pcr_slot, "Failed to insert")?; + let _ = self.bank.insert(pcr_slot, digest); + Ok(()) + } + + /// Attempts to extend the [PcrBank] with `other`. + /// + /// # Error + /// Returns an error if the a value in `other` already + /// exists. + pub fn try_extend(&mut self, other: PcrBank) -> Result<()> { + other + .bank + .keys() + .try_for_each(|&pcr_slot| self.ensure_non_existing(pcr_slot, "Failed to extend"))?; + self.bank.extend(other.bank); + Ok(()) + } + + /// Returns an error if a [Digest] for [PcrSlot] already exists in the bank + fn ensure_non_existing(&self, pcr_slot: PcrSlot, error_msg: &str) -> Result<()> { + if self.has_digest(pcr_slot) { + error!( + "{}, a digest already for PcrSlot {:?} exists in the bank", + error_msg, pcr_slot + ); + return Err(Error::local_error(WrapperErrorKind::InvalidParam)); + } + Ok(()) + } +} + +impl<'a> IntoIterator for &'a PcrBank { + type Item = (&'a PcrSlot, &'a Digest); + type IntoIter = ::std::collections::btree_map::Iter<'a, PcrSlot, Digest>; + + fn into_iter(self) -> Self::IntoIter { + self.bank.iter() + } +} diff --git a/tss-esapi/src/abstraction/pcr/data.rs b/tss-esapi/src/abstraction/pcr/data.rs new file mode 100644 index 00000000..6da022db --- /dev/null +++ b/tss-esapi/src/abstraction/pcr/data.rs @@ -0,0 +1,134 @@ +// Copyright 2021 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +use crate::{ + abstraction::pcr::PcrBank, + interface_types::algorithm::HashingAlgorithm, + structures::{Digest, DigestList, PcrSelectionList}, + tss2_esys::TPML_DIGEST, + Error, Result, WrapperErrorKind, +}; +use log::error; +/// Struct holding pcr banks and their associated +/// hashing algorithm +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PcrData { + data: Vec<(HashingAlgorithm, PcrBank)>, +} + +impl PcrData { + /// Creates new empty PcrData + pub const fn new() -> Self { + PcrData { data: Vec::new() } + } + + /// Function for creating PcrData from a pcr selection list and pcr digests list. + pub fn create( + pcr_selection_list: &PcrSelectionList, + digest_list: &DigestList, + ) -> Result { + Ok(PcrData { + data: Self::create_data(pcr_selection_list, digest_list.value().to_vec())?, + }) + } + + /// Adds data to the PcrData + pub fn add( + &mut self, + pcr_selection_list: &PcrSelectionList, + digest_list: &DigestList, + ) -> Result<()> { + Self::create_data(pcr_selection_list, digest_list.value().to_vec())? + .drain(..) + .try_for_each(|(hashing_algorithm, pcr_bank)| { + if let Some(existing_pcr_bank) = self.pcr_bank_mut(hashing_algorithm) { + existing_pcr_bank.try_extend(pcr_bank)?; + } else { + self.data.push((hashing_algorithm, pcr_bank)); + } + Ok(()) + }) + } + + /// Function for turning a pcr selection list and pcr digests values + /// into the format in which data is stored in PcrData. + fn create_data( + pcr_selection_list: &PcrSelectionList, + mut digests: Vec, + ) -> Result> { + pcr_selection_list + .get_selections() + .iter() + .map(|pcr_selection| { + let pcr_slots = pcr_selection.selected(); + if pcr_slots.len() > digests.len() { + error!("More pcr slots in selection then available digests"); + return Err(Error::local_error(WrapperErrorKind::InconsistentParams)); + } + let digests_in_bank = digests.drain(..pcr_slots.len()).collect(); + Ok(( + pcr_selection.hashing_algorithm(), + PcrBank::create(pcr_slots, digests_in_bank)?, + )) + }) + .collect() + } + + /// Function for retrieving the first PCR values associated with hashing_algorithm. + pub fn pcr_bank(&self, hashing_algorithm: HashingAlgorithm) -> Option<&PcrBank> { + self.data + .iter() + .find(|(alg, _)| *alg == hashing_algorithm) + .map(|(_, bank)| bank) + } + + /// Function for retrieving the number of banks in the data. + pub fn len(&self) -> usize { + self.data.len() + } + + /// Returns true if there are no banks in the data. + pub fn is_empty(&self) -> bool { + self.data.is_empty() + } + + /// Private method for finding a PCR bank. + fn pcr_bank_mut(&mut self, hashing_algorithm: HashingAlgorithm) -> Option<&mut PcrBank> { + self.data + .iter_mut() + .find(|(alg, _)| *alg == hashing_algorithm) + .map(|(_, bank)| bank) + } +} + +impl<'a> IntoIterator for PcrData { + type Item = (HashingAlgorithm, PcrBank); + type IntoIter = ::std::vec::IntoIter<(HashingAlgorithm, PcrBank)>; + + fn into_iter(self) -> Self::IntoIter { + self.data.into_iter() + } +} + +impl From for Vec { + fn from(pcr_data: PcrData) -> Self { + pcr_data + .data + .iter() + .flat_map(|(_, pcr_bank)| pcr_bank.into_iter()) + .map(|(_, digest)| digest) + .collect::>() + .chunks(DigestList::MAX_SIZE) + .map(|digests| { + let mut tpml_digest: TPML_DIGEST = Default::default(); + for (index, digest) in digests.iter().enumerate() { + tpml_digest.count += 1; + tpml_digest.digests[index].size = digest.len() as u16; + tpml_digest.digests[index].buffer[..digest.len()] + .copy_from_slice(digest.value()); + } + tpml_digest + }) + .collect() + } +} diff --git a/tss-esapi/src/context/tpm_commands/enhanced_authorization_ea_commands.rs b/tss-esapi/src/context/tpm_commands/enhanced_authorization_ea_commands.rs index 29d30daa..7b070790 100644 --- a/tss-esapi/src/context/tpm_commands/enhanced_authorization_ea_commands.rs +++ b/tss-esapi/src/context/tpm_commands/enhanced_authorization_ea_commands.rs @@ -132,7 +132,7 @@ impl Context { /// by the value of the different hashes. /// /// # Constraints - /// * `hash_list` must be at least 2 and at most 8 elements long + /// * `digest_list` must be at least 2 and at most 8 elements long /// /// # Errors /// * if the hash list provided is too short or too long, a `WrongParamSize` wrapper error will be returned @@ -141,7 +141,13 @@ impl Context { policy_session: PolicySession, digest_list: DigestList, ) -> Result<()> { - let digest_list = TPML_DIGEST::try_from(digest_list)?; + if digest_list.len() < 2 { + error!( + "The digest list only contains {} digests, it must contain at least 2", + digest_list.len() + ); + return Err(Error::local_error(ErrorKind::WrongParamSize)); + } let ret = unsafe { Esys_PolicyOR( @@ -150,7 +156,7 @@ impl Context { self.optional_session_1(), self.optional_session_2(), self.optional_session_3(), - &digest_list, + &TPML_DIGEST::try_from(digest_list)?, ) }; let ret = Error::from_tss_rc(ret); diff --git a/tss-esapi/src/context/tpm_commands/integrity_collection_pcr.rs b/tss-esapi/src/context/tpm_commands/integrity_collection_pcr.rs index b5c62908..051d34fc 100644 --- a/tss-esapi/src/context/tpm_commands/integrity_collection_pcr.rs +++ b/tss-esapi/src/context/tpm_commands/integrity_collection_pcr.rs @@ -2,9 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ handles::PcrHandle, - structures::{DigestValues, PcrSelectionList}, + structures::{DigestList, DigestValues, PcrSelectionList}, tss2_esys::*, - utils::PcrData, Context, Error, Result, }; use log::error; @@ -149,16 +148,16 @@ impl Context { /// .with_selection(HashingAlgorithm::Sha256, &[PcrSlot::Slot0, PcrSlot::Slot1]) /// .build(); /// - /// let (update_counter, read_pcr_list, pcr_data) = context.pcr_read(&pcr_selection_list) + /// let (update_counter, read_pcr_list, digest_list) = context.pcr_read(&pcr_selection_list) /// .expect("Call to pcr_read failed"); /// ``` pub fn pcr_read( &mut self, pcr_selection_list: &PcrSelectionList, - ) -> Result<(u32, PcrSelectionList, PcrData)> { + ) -> Result<(u32, PcrSelectionList, DigestList)> { let mut pcr_update_counter: u32 = 0; let mut tss_pcr_selection_list_out_ptr = null_mut(); - let mut tss_digest_ptr = null_mut(); + let mut tss_digest_list_out_ptr = null_mut(); let ret = unsafe { Esys_PCR_Read( self.mut_context(), @@ -168,7 +167,7 @@ impl Context { &pcr_selection_list.clone().into(), &mut pcr_update_counter, &mut tss_pcr_selection_list_out_ptr, - &mut tss_digest_ptr, + &mut tss_digest_list_out_ptr, ) }; let ret = Error::from_tss_rc(ret); @@ -176,11 +175,12 @@ impl Context { if ret.is_success() { let tss_pcr_selection_list_out = unsafe { MBox::::from_raw(tss_pcr_selection_list_out_ptr) }; - let tss_digest = unsafe { MBox::::from_raw(tss_digest_ptr) }; + let tss_digest_list_out = + unsafe { MBox::::from_raw(tss_digest_list_out_ptr) }; Ok(( pcr_update_counter, PcrSelectionList::try_from(*tss_pcr_selection_list_out)?, - PcrData::new(tss_pcr_selection_list_out.as_ref(), tss_digest.as_ref())?, + DigestList::try_from(*tss_digest_list_out)?, )) } else { error!("Error when reading PCR: {}", ret); diff --git a/tss-esapi/src/structures/lists/digest.rs b/tss-esapi/src/structures/lists/digest.rs index 134544c7..26e51b30 100644 --- a/tss-esapi/src/structures/lists/digest.rs +++ b/tss-esapi/src/structures/lists/digest.rs @@ -12,19 +12,31 @@ pub struct DigestList { } impl DigestList { - // minimum is two for TPM2_PolicyOR(). - pub const MIN_SIZE: usize = 2; pub const MAX_SIZE: usize = 8; - pub fn new() -> Self { + + /// Creates a nnew empty DigestList + pub const fn new() -> Self { DigestList { digests: Vec::new(), } } + /// Returns the values in the digest list. pub fn value(&self) -> &[Digest] { &self.digests } + /// Returns the number of digests in the digestlist + pub fn len(&self) -> usize { + self.digests.len() + } + + /// Indicates if the digest list contains any digests. + pub fn is_empty(&self) -> bool { + self.digests.is_empty() + } + + /// Adds a new digest to the digest list. pub fn add(&mut self, dig: Digest) -> Result<()> { if self.digests.len() >= DigestList::MAX_SIZE { error!("Error: Exceeded maximum count(> {})", DigestList::MAX_SIZE); @@ -39,13 +51,7 @@ impl TryFrom for DigestList { type Error = Error; fn try_from(tpml_digest: TPML_DIGEST) -> Result { let digests_count = tpml_digest.count as usize; - if digests_count < DigestList::MIN_SIZE { - error!( - "Error: Invalid TPML_DIGEST count(< {})", - DigestList::MIN_SIZE - ); - return Err(Error::local_error(WrapperErrorKind::InvalidParam)); - } + if digests_count > DigestList::MAX_SIZE { error!( "Error: Invalid TPML_DIGEST count(> {})", @@ -53,22 +59,18 @@ impl TryFrom for DigestList { ); return Err(Error::local_error(WrapperErrorKind::InvalidParam)); } - let digests = &tpml_digest.digests[..digests_count]; - let digests: Result> = digests.iter().map(|x| Digest::try_from(*x)).collect(); - Ok(DigestList { digests: digests? }) + + tpml_digest.digests[..digests_count] + .iter() + .map(|&tss_digest| Digest::try_from(tss_digest)) + .collect::>>() + .map(|digests| DigestList { digests }) } } impl TryFrom for TPML_DIGEST { type Error = Error; fn try_from(digest_list: DigestList) -> Result { - if digest_list.digests.len() < DigestList::MIN_SIZE { - error!( - "Error: Invalid digest list size(< {})", - DigestList::MIN_SIZE - ); - return Err(Error::local_error(WrapperErrorKind::WrongParamSize)); - } if digest_list.digests.len() > DigestList::MAX_SIZE { error!( "Error: Invalid digest list size(> {})", @@ -78,8 +80,8 @@ impl TryFrom for TPML_DIGEST { } let mut tss_digest_list: TPML_DIGEST = Default::default(); - for digest in digest_list.digests.iter() { - tss_digest_list.digests[tss_digest_list.count as usize] = digest.clone().into(); + for digest in digest_list.digests { + tss_digest_list.digests[tss_digest_list.count as usize] = digest.into(); tss_digest_list.count += 1; } Ok(tss_digest_list) diff --git a/tss-esapi/src/structures/lists/pcr_selection.rs b/tss-esapi/src/structures/lists/pcr_selection.rs index d2fbdd20..bdadc051 100644 --- a/tss-esapi/src/structures/lists/pcr_selection.rs +++ b/tss-esapi/src/structures/lists/pcr_selection.rs @@ -7,6 +7,7 @@ use crate::{Error, Result, WrapperErrorKind}; use log::error; use std::collections::HashMap; use std::convert::TryFrom; + /// A struct representing a pcr selection list. This /// corresponds to the TSS TPML_PCR_SELECTION. #[derive(Debug, Clone, PartialEq, Eq)] @@ -31,12 +32,55 @@ impl PcrSelectionList { &self.items } + /// Subtracts other from self + pub fn subtract(&mut self, other: &Self) -> Result<()> { + if self == other { + self.items.clear(); + return Ok(()); + } + + if self.is_empty() { + error!("Cannot remove items that does not exist"); + return Err(Error::local_error(WrapperErrorKind::InvalidParam)); + } + + for other_pcr_selection in other.get_selections() { + self.remove_selection(other_pcr_selection)?; + } + + self.remove_empty_selections(); + Ok(()) + } + /// Function for retrieving the PcrSelectionList from Option /// /// This returns an empty list if None is passed pub fn list_from_option(pcr_list: Option) -> PcrSelectionList { pcr_list.unwrap_or_else(|| PcrSelectionListBuilder::new().build()) } + + /// Private methods for removing pcr selections that are empty. + fn remove_empty_selections(&mut self) { + self.items.retain(|v| !v.is_empty()); + } + + /// Private method for removing the items defined in a [PcrSelection] + /// from the data in the [PcrSelectionList]. + fn remove_selection(&mut self, pcr_selection: &PcrSelection) -> Result<()> { + pcr_selection.selected().iter().try_for_each(|&pcr_slot| { + self.items + .iter_mut() + .find(|existing_pcr_selection| { + existing_pcr_selection.hashing_algorithm() == pcr_selection.hashing_algorithm() + && existing_pcr_selection.is_selected(pcr_slot) + }) + .ok_or_else(|| { + error!("Cannot remove items from a selection that does not exists"); + Error::local_error(WrapperErrorKind::InvalidParam) + }) + .and_then(|existing_pcr_selection| existing_pcr_selection.deselect_exact(pcr_slot)) + }) + } } impl From for TPML_PCR_SELECTION { @@ -54,8 +98,6 @@ impl From for TPML_PCR_SELECTION { impl TryFrom for PcrSelectionList { type Error = Error; fn try_from(tpml_pcr_selection: TPML_PCR_SELECTION) -> Result { - // let mut ret: PcrSelectionList = Default::default(); - let size = tpml_pcr_selection.count as usize; if size > PcrSelectionList::MAX_SIZE { diff --git a/tss-esapi/src/structures/pcr/select.rs b/tss-esapi/src/structures/pcr/select.rs index dd1ceba1..4a395ef9 100644 --- a/tss-esapi/src/structures/pcr/select.rs +++ b/tss-esapi/src/structures/pcr/select.rs @@ -133,6 +133,11 @@ impl PcrSelect { } /// Returns the size of the select. + /// + /// NB! This is not the same as how many [PcrSlot] + /// there are in the select but rather how many + /// octets that are needed to hold the bit field + /// that indicate what slots that are selected. pub fn size_of_select(&self) -> PcrSelectSize { self.size_of_select } diff --git a/tss-esapi/src/structures/pcr/selection.rs b/tss-esapi/src/structures/pcr/selection.rs index 599fc7db..4c1d4a52 100644 --- a/tss-esapi/src/structures/pcr/selection.rs +++ b/tss-esapi/src/structures/pcr/selection.rs @@ -8,6 +8,7 @@ use enumflags2::BitFlags; use log::error; use num_traits::{FromPrimitive, ToPrimitive}; use std::convert::{From, TryFrom}; +use std::iter::FromIterator; /// This module contains the PcrSelection struct. /// The TSS counterpart of this struct is the /// TPMS_PCR_SELECTION. @@ -19,6 +20,7 @@ pub struct PcrSelection { } impl PcrSelection { + /// Creates new PcrSelection pub fn new( hashing_algorithm: HashingAlgorithm, size_of_select: PcrSelectSize, @@ -27,18 +29,50 @@ impl PcrSelection { PcrSelection { hashing_algorithm, size_of_select, - selected_pcrs: selected_pcr_slots.iter().cloned().collect(), + selected_pcrs: Self::to_internal_representation(selected_pcr_slots), } } - pub fn hashing_algorithm(&self) -> HashingAlgorithm { + /// Returns the hashing algorithm for the selection + pub const fn hashing_algorithm(&self) -> HashingAlgorithm { self.hashing_algorithm } - pub fn selected_pcrs(&self) -> &BitFlags { - &self.selected_pcrs + /// Returns 'Size of Select' + /// + /// NB! This is not the same as how many [PcrSlot] + /// there are in the selection but rather how many + /// octets that are needed to hold the bit field + /// that indicate what slots that are selected. + pub const fn size_of_select(&self) -> PcrSelectSize { + self.size_of_select + } + + /// Returns the selected pcrs. + pub fn selected(&self) -> Vec { + self.selected_pcrs.iter().collect() } + /// Returns true if the specified [PcrSlot] is selected in + /// the [PcrSelection]. + pub fn is_selected(&self, pcr_slot: PcrSlot) -> bool { + self.selected_pcrs.contains(pcr_slot) + } + + /// Removes the specified [PcrSlot]s from the selected pcrs. + /// + /// # Error + /// If one of the specified pcr slots does not exist in the selected pcrs. + pub fn deselect_exact(&mut self, pcr_slot: PcrSlot) -> Result<()> { + self.remove_exact(pcr_slot.into()) + } + + /// Removes the specified [PcrSlot]s from the selected pcrs. + pub fn deselect(&mut self, pcr_slot: PcrSlot) { + self.remove(pcr_slot.into()) + } + + /// Merges another [PcrSelection] into this one. pub fn merge(&mut self, other: &Self) -> Result<()> { // Check that the hashing algorithm match if self.hashing_algorithm != other.hashing_algorithm { @@ -64,28 +98,48 @@ impl PcrSelection { pub fn subtract(&mut self, other: &Self) -> Result<()> { // Check that the hashing algorithm match if self.hashing_algorithm != other.hashing_algorithm { - error!("Error: Found inconsistencies in the hashing algorithm"); + error!("Mismatched hashing algorithm "); return Err(Error::local_error(WrapperErrorKind::InconsistentParams)); } // Check that size of select match. if self.size_of_select != other.size_of_select { - error!("Error: Found inconsistencies in the size of select"); + error!("Mismatched size of select"); return Err(Error::local_error(WrapperErrorKind::InconsistentParams)); } - // Check if the value in other is contained in current select - if !self.selected_pcrs.contains(other.selected_pcrs) { - error!("Error: Trying to remove item that did not exist"); - return Err(Error::local_error(WrapperErrorKind::InvalidParam)); - } - self.selected_pcrs.remove(other.selected_pcrs); - Ok(()) + self.remove_exact(other.selected_pcrs) } /// Indicates wether the pcr selection is empty. pub fn is_empty(&self) -> bool { self.selected_pcrs.is_empty() } + + /// Private function for converting a slize of pcr slots to + /// internal representation. + fn to_internal_representation(pcr_slots: &[PcrSlot]) -> BitFlags { + BitFlags::::from_iter(pcr_slots.iter().copied()) + } + + /// Removes bitflags from selected pcrs + /// + /// # Error + /// Returns an error if the any of the bit flags does + /// not exist in the selected pcrs. + fn remove_exact(&mut self, bit_flags: BitFlags) -> Result<()> { + if self.selected_pcrs.contains(bit_flags) { + self.remove(bit_flags); + Ok(()) + } else { + error!("Failed to remove item, it does not exist"); + Err(Error::local_error(WrapperErrorKind::InvalidParam)) + } + } + + /// Removes bitflags from selected pcrs + fn remove(&mut self, bit_flags: BitFlags) { + self.selected_pcrs.remove(bit_flags) + } } impl TryFrom for PcrSelection { diff --git a/tss-esapi/src/utils/mod.rs b/tss-esapi/src/utils/mod.rs index f10a93d0..e1e22c03 100644 --- a/tss-esapi/src/utils/mod.rs +++ b/tss-esapi/src/utils/mod.rs @@ -16,17 +16,14 @@ use crate::interface_types::{ key_bits::RsaKeyBits, }; use crate::structures::{ - Digest, EccPoint, EccScheme, PcrSlot, Public, PublicBuilder, PublicEccParametersBuilder, - PublicKeyRsa, PublicRsaParametersBuilder, RsaExponent, RsaScheme, SymmetricDefinitionObject, + EccPoint, EccScheme, Public, PublicBuilder, PublicEccParametersBuilder, PublicKeyRsa, + PublicRsaParametersBuilder, RsaExponent, RsaScheme, SymmetricDefinitionObject, }; use crate::tss2_esys::*; use crate::{Context, Error, Result, WrapperErrorKind}; -use enumflags2::BitFlags; -use log::error; use zeroize::Zeroize; use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; use std::convert::{TryFrom, TryInto}; /// Rust native wrapper for `TPMS_CONTEXT` objects. @@ -296,165 +293,6 @@ impl TryFrom for PublicKey { } } -type PcrValue = Digest; - -/// Struct for holding PcrSlots and their -/// corresponding values. -#[derive(Debug, Clone, Eq, PartialEq)] -pub struct PcrBank { - bank: BTreeMap, -} - -impl PcrBank { - /// Function for retrieving a pcr value corresponding to a pcr slot. - pub fn pcr_value(&self, pcr_slot: PcrSlot) -> Option<&PcrValue> { - self.bank.get(&pcr_slot) - } - - /// Function for retrieiving the number of pcr slot values in the bank. - pub fn len(&self) -> usize { - self.bank.len() - } - - /// Returns true if there are no pcr slot values in the bank. - pub fn is_empty(&self) -> bool { - self.bank.is_empty() - } -} - -impl<'a> IntoIterator for &'a PcrBank { - type Item = (&'a PcrSlot, &'a PcrValue); - type IntoIter = ::std::collections::btree_map::Iter<'a, PcrSlot, PcrValue>; - - fn into_iter(self) -> Self::IntoIter { - self.bank.iter() - } -} - -/// Struct holding pcr banks and their associated -/// hashing algorithm -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct PcrData { - data: Vec<(HashingAlgorithm, PcrBank)>, -} - -impl PcrData { - /// Contrustctor that creates a PcrData from - /// tss types. - pub fn new( - tpml_pcr_selections: &TPML_PCR_SELECTION, - tpml_digests: &TPML_DIGEST, - ) -> Result { - // Check digests - let digests_count = tpml_digests.count as usize; - if digests_count > 8 { - error!("Error: Invalid TPML_DIGEST count(> 8)"); - return Err(Error::local_error(WrapperErrorKind::InvalidParam)); - } - let digests = &tpml_digests.digests[..digests_count]; - // Check selections - let selections_count = tpml_pcr_selections.count as usize; - if selections_count > 16 { - error!("Error: Invalid TPML_SELECTIONS count(> 16)"); - return Err(Error::local_error(WrapperErrorKind::InvalidParam)); - } - let pcr_selections = &tpml_pcr_selections.pcrSelections[..selections_count]; - - let mut digest_iter = digests.iter(); - let mut data = Vec::<(HashingAlgorithm, PcrBank)>::new(); - for &pcr_selection in pcr_selections { - // Parse hash algorithm from selection - let parsed_hash_algorithm = - HashingAlgorithm::try_from(pcr_selection.hash).map_err(|e| { - error!("Error converting hash to a HashingAlgorithm: {}", e); - Error::local_error(WrapperErrorKind::InvalidParam) - })?; - // Parse pcr slots from selection - let parsed_pcr_slots: BitFlags = - BitFlags::::try_from(u32::from_le_bytes(pcr_selection.pcrSelect)) - .map_err(|e| { - error!("Error parsing pcrSelect to a BitFlags: {}", e); - Error::local_error(WrapperErrorKind::UnsupportedParam) - })?; - // Create PCR bank by mapping the pcr slots to the pcr values - let mut parsed_pcr_bank = PcrBank { - bank: Default::default(), - }; - for pcr_slot in parsed_pcr_slots.iter() { - // Make sure there are still data - let digest = match digest_iter.next() { - Some(val) => val, - None => { - error!("Error number of items in selection does not match number of items in data"); - return Err(Error::local_error(WrapperErrorKind::InconsistentParams)); - } - }; - // Add the value corresponding to the pcr slot. - if parsed_pcr_bank - .bank - .insert(pcr_slot, PcrValue::try_from(*digest)?) - .is_some() - { - error!("Error trying to insert data into PcrSlot where data have already been inserted"); - return Err(Error::local_error(WrapperErrorKind::InconsistentParams)); - } - } - data.push((parsed_hash_algorithm, parsed_pcr_bank)); - } - // Make sure all values in the digest have been read. - if digest_iter.next().is_some() { - error!("Error not all values in the digest have been handled"); - return Err(Error::local_error(WrapperErrorKind::InconsistentParams)); - } - - Ok(PcrData { data }) - } - - /// Function for retrieving the first PCR values associated with hashing_algorithm. - pub fn pcr_bank(&self, hashing_algorithm: HashingAlgorithm) -> Option<&PcrBank> { - self.data - .iter() - .find(|(alg, _)| alg == &hashing_algorithm) - .map(|(_, bank)| bank) - } - - /// Function for retrieving the number of banks in the data. - pub fn len(&self) -> usize { - self.data.len() - } - - /// Returns true if there are no banks in the data. - pub fn is_empty(&self) -> bool { - self.data.is_empty() - } -} - -impl<'a> IntoIterator for PcrData { - type Item = (HashingAlgorithm, PcrBank); - type IntoIter = ::std::vec::IntoIter<(HashingAlgorithm, PcrBank)>; - - fn into_iter(self) -> Self::IntoIter { - self.data.into_iter() - } -} - -impl From for TPML_DIGEST { - fn from(pcr_data: PcrData) -> Self { - let mut tpml_digest: TPML_DIGEST = Default::default(); - - for (_, pcr_bank) in pcr_data.into_iter() { - for (_, pcr_value) in pcr_bank.into_iter() { - let i = tpml_digest.count as usize; - let size = pcr_value.value().len() as u16; - tpml_digest.digests[i].size = size; - tpml_digest.digests[i].buffer[..size as usize].copy_from_slice(pcr_value.value()); - tpml_digest.count += 1; - } - } - tpml_digest - } -} - fn tpm_int_to_string(num: u32) -> String { num.to_be_bytes() .iter() diff --git a/tss-esapi/tests/integration_tests/abstraction_tests/mod.rs b/tss-esapi/tests/integration_tests/abstraction_tests/mod.rs index 08b47172..e3b886a5 100644 --- a/tss-esapi/tests/integration_tests/abstraction_tests/mod.rs +++ b/tss-esapi/tests/integration_tests/abstraction_tests/mod.rs @@ -3,4 +3,6 @@ mod ak_tests; mod ek_tests; mod nv_tests; +mod pcr_data_tests; +mod pcr_tests; mod transient_key_context_tests; diff --git a/tss-esapi/tests/integration_tests/abstraction_tests/pcr_data_tests.rs b/tss-esapi/tests/integration_tests/abstraction_tests/pcr_data_tests.rs new file mode 100644 index 00000000..e25c613f --- /dev/null +++ b/tss-esapi/tests/integration_tests/abstraction_tests/pcr_data_tests.rs @@ -0,0 +1,128 @@ +use tss_esapi::{ + abstraction::pcr::PcrData, + interface_types::algorithm::HashingAlgorithm, + structures::{Digest, DigestList, PcrSelectionListBuilder, PcrSlot}, + tss2_esys::TPML_DIGEST, + Error, WrapperErrorKind, +}; + +use std::convert::TryFrom; + +#[test] +fn test_valid_to_tpml_digest_conversion() { + let pcr_selection_list_1 = PcrSelectionListBuilder::new() + .with_selection( + HashingAlgorithm::Sha256, + &[ + PcrSlot::Slot0, + PcrSlot::Slot1, + PcrSlot::Slot2, + PcrSlot::Slot3, + PcrSlot::Slot4, + PcrSlot::Slot5, + PcrSlot::Slot6, + PcrSlot::Slot7, + ], + ) + .build(); + + let mut pcr_digest_list_1 = DigestList::new(); + for i in 0u8..8u8 { + let value: [u8; 1] = [i]; + pcr_digest_list_1 + .add(Digest::try_from(&value[..]).expect("Failed to create digest value")) + .expect("Failed to add value to digest"); + } + + let pcr_selection_list_2 = PcrSelectionListBuilder::new() + .with_selection( + HashingAlgorithm::Sha256, + &[ + PcrSlot::Slot8, + PcrSlot::Slot9, + PcrSlot::Slot10, + PcrSlot::Slot11, + PcrSlot::Slot12, + PcrSlot::Slot13, + PcrSlot::Slot14, + PcrSlot::Slot15, + ], + ) + .build(); + + let mut pcr_digest_list_2 = DigestList::new(); + for i in 8u8..16u8 { + let value: [u8; 1] = [i]; + pcr_digest_list_2 + .add(Digest::try_from(&value[..]).expect("Failed to create digest value")) + .expect("Failed to add value to digest"); + } + + let mut pcr_data = PcrData::new(); + pcr_data + .add(&pcr_selection_list_1, &pcr_digest_list_1) + .expect("Failed to add selection and digests nr1"); + pcr_data + .add(&pcr_selection_list_2, &pcr_digest_list_2) + .expect("Failed to add selection and digests nr2"); + + let tpml_digests: Vec = pcr_data.into(); + assert_eq!( + tpml_digests.len(), + 2, + "PcrData did not convert into 2 TPML_DIGEST items as expected" + ); + for (tpml_digest, count) in tpml_digests.iter().zip(0u8..2u8) { + assert_eq!( + tpml_digest.count as usize, + DigestList::MAX_SIZE, + "The converted digest list did not contain expected number of elements" + ); + for (tpm2b_digest, value) in tpml_digest.digests.iter().zip(8 * count..8 * (count + 1)) { + assert_eq!( + tpm2b_digest.size, 1, + "The converted digest did not contain expected number of bytes" + ); + assert_eq!( + tpm2b_digest.buffer[0], value, + "Thw converted digest did not contain expected values" + ); + } + } +} + +#[test] +fn test_invalid_to_tpml_digest_conversion() { + let pcr_selection_list_1 = PcrSelectionListBuilder::new() + .with_selection( + HashingAlgorithm::Sha256, + &[ + PcrSlot::Slot0, + PcrSlot::Slot1, + PcrSlot::Slot2, + PcrSlot::Slot3, + PcrSlot::Slot4, + PcrSlot::Slot5, + PcrSlot::Slot6, + PcrSlot::Slot7, + ], + ) + .build(); + + let mut pcr_digest_list_1 = DigestList::new(); + for i in 0u8..7u8 { + let value: [u8; 1] = [i]; + pcr_digest_list_1 + .add(Digest::try_from(&value[..]).expect("Failed to create digest value")) + .expect("Failed to add value to digest"); + } + + let mut pcr_data = PcrData::new(); + let pcr_data_add_result = pcr_data.add(&pcr_selection_list_1, &pcr_digest_list_1); + + assert_eq!( + Err(Error::WrapperError(WrapperErrorKind::InconsistentParams)), + pcr_data_add_result, + "Did not receive expected error" + ); +} diff --git a/tss-esapi/tests/integration_tests/abstraction_tests/pcr_tests.rs b/tss-esapi/tests/integration_tests/abstraction_tests/pcr_tests.rs new file mode 100644 index 00000000..0bbb5d62 --- /dev/null +++ b/tss-esapi/tests/integration_tests/abstraction_tests/pcr_tests.rs @@ -0,0 +1,137 @@ +// Copyright 2021 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 +use crate::common::create_ctx_without_session; + +use tss_esapi::{ + interface_types::algorithm::HashingAlgorithm, + structures::{PcrSelectionListBuilder, PcrSlot}, +}; + +#[test] +fn test_pcr_read_all() { + let mut context = create_ctx_without_session(); + + let pcr_selection_list = PcrSelectionListBuilder::new() + .with_selection( + HashingAlgorithm::Sha256, + &[ + PcrSlot::Slot0, + PcrSlot::Slot1, + PcrSlot::Slot2, + PcrSlot::Slot3, + PcrSlot::Slot4, + PcrSlot::Slot5, + PcrSlot::Slot6, + PcrSlot::Slot7, + PcrSlot::Slot8, + PcrSlot::Slot9, + PcrSlot::Slot10, + PcrSlot::Slot11, + PcrSlot::Slot12, + PcrSlot::Slot13, + PcrSlot::Slot14, + PcrSlot::Slot15, + PcrSlot::Slot16, + PcrSlot::Slot17, + PcrSlot::Slot18, + PcrSlot::Slot19, + PcrSlot::Slot20, + PcrSlot::Slot21, + PcrSlot::Slot22, + PcrSlot::Slot23, + ], + ) + .build(); + + let pcr_data = tss_esapi::abstraction::pcr::read_all(&mut context, pcr_selection_list) + .expect("Call to pcr_read_all failed"); + + assert_eq!( + pcr_data + .pcr_bank(HashingAlgorithm::Sha256) + .expect("PcrData did not conatin expected PcrBank") + .len(), + 24, + "PcrData did not contain expected amount of digests" + ); + + let (_count, _read_pcr_selection_1, read_pcrs_1) = context + .pcr_read( + &PcrSelectionListBuilder::new() + .with_selection( + HashingAlgorithm::Sha256, + &[ + PcrSlot::Slot0, + PcrSlot::Slot1, + PcrSlot::Slot2, + PcrSlot::Slot3, + PcrSlot::Slot4, + PcrSlot::Slot5, + PcrSlot::Slot6, + PcrSlot::Slot7, + ], + ) + .build(), + ) + .expect("Call 1 to pcr_read failed"); + + let (_count, _read_pcr_selection_2, read_pcrs_2) = context + .pcr_read( + &PcrSelectionListBuilder::new() + .with_selection( + HashingAlgorithm::Sha256, + &[ + PcrSlot::Slot8, + PcrSlot::Slot9, + PcrSlot::Slot10, + PcrSlot::Slot11, + PcrSlot::Slot12, + PcrSlot::Slot13, + PcrSlot::Slot14, + PcrSlot::Slot15, + ], + ) + .build(), + ) + .expect("Call 2 to pcr_read failed"); + + let (_count, _read_pcr_selection_3, read_pcrs_3) = context + .pcr_read( + &PcrSelectionListBuilder::new() + .with_selection( + HashingAlgorithm::Sha256, + &[ + PcrSlot::Slot16, + PcrSlot::Slot17, + PcrSlot::Slot18, + PcrSlot::Slot19, + PcrSlot::Slot20, + PcrSlot::Slot21, + PcrSlot::Slot22, + PcrSlot::Slot23, + ], + ) + .build(), + ) + .expect("Call 2 to pcr_read failed"); + + vec![read_pcrs_1, read_pcrs_2, read_pcrs_3] + .iter() + .enumerate() + .for_each(|(idx, dl)| { + assert_eq!(dl.len(), 8); + let d = pcr_data + .pcr_bank(HashingAlgorithm::Sha256) + .expect("PcrData did not conatin expected PcrBank") + .into_iter() + .skip(idx * 8) + .take(8); + assert_eq!(d.len(), 8); + dl.value() + .iter() + .zip(d) + .for_each(|(actual, (_, expected))| { + assert_eq!(actual, expected); + }) + }) +} diff --git a/tss-esapi/tests/integration_tests/common/mod.rs b/tss-esapi/tests/integration_tests/common/mod.rs index 01005ea8..44c642b6 100644 --- a/tss-esapi/tests/integration_tests/common/mod.rs +++ b/tss-esapi/tests/integration_tests/common/mod.rs @@ -8,7 +8,7 @@ use std::{ }; use tss_esapi::{ - abstraction::cipher::Cipher, + abstraction::{cipher::Cipher, pcr::PcrData}, attributes::{ObjectAttributesBuilder, SessionAttributesBuilder}, constants::SessionType, interface_types::{ @@ -161,8 +161,17 @@ pub fn get_pcr_policy_digest( .with_selection(HashingAlgorithm::Sha256, &[PcrSlot::Slot0, PcrSlot::Slot1]) .build(); - let (_update_counter, pcr_selection_list_out, pcr_data) = - context.pcr_read(&pcr_selection_list).unwrap(); + let (_update_counter, pcr_selection_list_out, pcr_data) = context + .pcr_read(&pcr_selection_list) + .map(|(update_counter, read_pcr_selections, read_pcr_digests)| { + ( + update_counter, + read_pcr_selections.clone(), + PcrData::create(&read_pcr_selections, &read_pcr_digests) + .expect("Failed to create PcrData"), + ) + }) + .expect("Failed to call pcr_read"); assert_eq!(pcr_selection_list, pcr_selection_list_out); // Run pcr_policy command. @@ -176,13 +185,13 @@ pub fn get_pcr_policy_digest( pcr_data .pcr_bank(HashingAlgorithm::Sha256) .unwrap() - .pcr_value(PcrSlot::Slot0) + .get_digest(PcrSlot::Slot0) .unwrap() .value(), pcr_data .pcr_bank(HashingAlgorithm::Sha256) .unwrap() - .pcr_value(PcrSlot::Slot1) + .get_digest(PcrSlot::Slot1) .unwrap() .value(), ] diff --git a/tss-esapi/tests/integration_tests/context_tests/tpm_commands/enhanced_authorization_ea_commands_tests.rs b/tss-esapi/tests/integration_tests/context_tests/tpm_commands/enhanced_authorization_ea_commands_tests.rs index 0f1223cb..c7421d76 100644 --- a/tss-esapi/tests/integration_tests/context_tests/tpm_commands/enhanced_authorization_ea_commands_tests.rs +++ b/tss-esapi/tests/integration_tests/context_tests/tpm_commands/enhanced_authorization_ea_commands_tests.rs @@ -197,6 +197,7 @@ mod test_policy_pcr { use crate::common::create_ctx_without_session; use std::convert::TryFrom; use tss_esapi::{ + abstraction::pcr::PcrData, attributes::SessionAttributesBuilder, constants::SessionType, interface_types::{ @@ -240,7 +241,15 @@ mod test_policy_pcr { let (_update_counter, pcr_selection_list_out, pcr_data) = context .pcr_read(&pcr_selection_list) - .expect("Failed to call policy_pcr"); + .map(|(update_counter, read_pcr_selections, read_pcr_digests)| { + ( + update_counter, + read_pcr_selections.clone(), + PcrData::create(&read_pcr_selections, &read_pcr_digests) + .expect("Failed to create PcrData"), + ) + }) + .expect("Failed to call pcr_read"); assert_eq!(pcr_selection_list, pcr_selection_list_out); // Run pcr_policy command. @@ -255,13 +264,13 @@ mod test_policy_pcr { pcr_data .pcr_bank(HashingAlgorithm::Sha256) .unwrap() - .pcr_value(PcrSlot::Slot0) + .get_digest(PcrSlot::Slot0) .unwrap() .value(), pcr_data .pcr_bank(HashingAlgorithm::Sha256) .unwrap() - .pcr_value(PcrSlot::Slot1) + .get_digest(PcrSlot::Slot1) .unwrap() .value(), ] @@ -682,6 +691,7 @@ mod test_policy_get_digest { use crate::common::create_ctx_without_session; use std::convert::TryFrom; use tss_esapi::{ + abstraction::pcr::PcrData, attributes::SessionAttributesBuilder, constants::SessionType, interface_types::{ @@ -726,6 +736,14 @@ mod test_policy_get_digest { .expect("Failed to convert auth session into policy session"); let (_update_counter, pcr_selection_list_out, pcr_data) = context .pcr_read(&pcr_selection_list) + .map(|(update_counter, read_pcr_selections, read_pcr_digests)| { + ( + update_counter, + read_pcr_selections.clone(), + PcrData::create(&read_pcr_selections, &read_pcr_digests) + .expect("Failed to create PcrData"), + ) + }) .expect("Failed to call pcr_read"); assert_eq!(pcr_selection_list, pcr_selection_list_out); @@ -741,13 +759,13 @@ mod test_policy_get_digest { pcr_data .pcr_bank(HashingAlgorithm::Sha256) .unwrap() - .pcr_value(PcrSlot::Slot0) + .get_digest(PcrSlot::Slot0) .unwrap() .value(), pcr_data .pcr_bank(HashingAlgorithm::Sha256) .unwrap() - .pcr_value(PcrSlot::Slot1) + .get_digest(PcrSlot::Slot1) .unwrap() .value(), ] diff --git a/tss-esapi/tests/integration_tests/context_tests/tpm_commands/integrity_collection_pcr_tests.rs b/tss-esapi/tests/integration_tests/context_tests/tpm_commands/integrity_collection_pcr_tests.rs index 2ea3d4d3..190fb14f 100644 --- a/tss-esapi/tests/integration_tests/context_tests/tpm_commands/integrity_collection_pcr_tests.rs +++ b/tss-esapi/tests/integration_tests/context_tests/tpm_commands/integrity_collection_pcr_tests.rs @@ -4,6 +4,7 @@ mod test_pcr_extend_reset { use crate::common::create_ctx_with_session; use std::convert::TryFrom; use tss_esapi::{ + abstraction::pcr::PcrData, handles::PcrHandle, interface_types::algorithm::HashingAlgorithm, structures::{Digest, DigestValues, PcrSelectionListBuilder, PcrSlot}, @@ -26,15 +27,27 @@ mod test_pcr_extend_reset { .with_selection(HashingAlgorithm::Sha256, &[PcrSlot::Slot16]) .build(); // pcr_read is NO_SESSIONS - let (_, _, pcr_data) = - context.execute_without_session(|ctx| ctx.pcr_read(&pcr_selection_list).unwrap()); - let pcr_sha1_bank = pcr_data.pcr_bank(HashingAlgorithm::Sha1).unwrap(); - let pcr_sha256_bank = pcr_data.pcr_bank(HashingAlgorithm::Sha256).unwrap(); - let pcr_sha1_value = pcr_sha1_bank.pcr_value(PcrSlot::Slot16).unwrap(); - let pcr_sha256_value = pcr_sha256_bank.pcr_value(PcrSlot::Slot16).unwrap(); + let (_, read_pcr_selections, read_pcr_digests) = context.execute_without_session(|ctx| { + ctx.pcr_read(&pcr_selection_list) + .expect("Failed to call pcr_read") + }); + // Needs to have the length of associated with the hashing algorithm - assert_eq!(pcr_sha1_value.value(), [0; 20]); - assert_eq!(pcr_sha256_value.value(), [0; 32]); + read_pcr_selections + .get_selections() + .iter() + .zip(read_pcr_digests.value().iter()) + .for_each(|(pcr_selection, digest)| { + if pcr_selection.hashing_algorithm() == HashingAlgorithm::Sha1 { + assert_eq!(digest.len(), 20); + assert_eq!(digest.value(), [0; 20]); + } else if pcr_selection.hashing_algorithm() == HashingAlgorithm::Sha256 { + assert_eq!(digest.len(), 32); + assert_eq!(digest.value(), [0; 32]); + } else { + panic!("Read pcr selections contained unexpected HashingAlgorithm"); + } + }); // Extend both sha256 and sha1 let mut vals = DigestValues::new(); @@ -59,12 +72,8 @@ mod test_pcr_extend_reset { }); // Read PCR contents - let (_, _, pcr_data) = + let (_, read_pcr_selections_2, read_pcr_digests_2) = context.execute_without_session(|ctx| ctx.pcr_read(&pcr_selection_list).unwrap()); - let pcr_sha1_bank = pcr_data.pcr_bank(HashingAlgorithm::Sha1).unwrap(); - let pcr_sha256_bank = pcr_data.pcr_bank(HashingAlgorithm::Sha256).unwrap(); - let pcr_sha1_value = pcr_sha1_bank.pcr_value(PcrSlot::Slot16).unwrap(); - let pcr_sha256_value = pcr_sha256_bank.pcr_value(PcrSlot::Slot16).unwrap(); // Needs to have the length of associated with the hashing algorithm /* Right Hand Side determined by: @@ -77,21 +86,35 @@ mod test_pcr_extend_reset { >>> res = ["0x"+a+b for a,b in zip(it, it)] >>> ", ".join(res) */ - assert_eq!( - pcr_sha1_value.value(), - [ - 0x5f, 0x42, 0x0e, 0x04, 0x95, 0x8b, 0x2e, 0x3f, 0x18, 0x07, 0x39, 0x1e, 0x99, 0xd9, - 0x49, 0x2c, 0x67, 0xaa, 0xef, 0xfd - ] - ); - assert_eq!( - pcr_sha256_value.value(), - [ - 0x0b, 0x8f, 0x4c, 0x5b, 0x6a, 0xdc, 0x4c, 0x08, 0x7a, 0xb9, 0xf4, 0x3a, 0xae, 0xb6, - 0x00, 0x70, 0x84, 0xc2, 0x64, 0xad, 0xca, 0xa3, 0xcb, 0x07, 0x17, 0x6b, 0x79, 0x23, - 0x42, 0x85, 0x04, 0x12 - ] - ); + + read_pcr_selections_2 + .get_selections() + .iter() + .zip(read_pcr_digests_2.value().iter()) + .for_each(|(pcr_selection, digest)| { + if pcr_selection.hashing_algorithm() == HashingAlgorithm::Sha1 { + assert_eq!(digest.len(), 20); + assert_eq!( + digest.value(), + [ + 0x5f, 0x42, 0x0e, 0x04, 0x95, 0x8b, 0x2e, 0x3f, 0x18, 0x07, 0x39, 0x1e, + 0x99, 0xd9, 0x49, 0x2c, 0x67, 0xaa, 0xef, 0xfd + ] + ); + } else if pcr_selection.hashing_algorithm() == HashingAlgorithm::Sha256 { + assert_eq!(digest.len(), 32); + assert_eq!( + digest.value(), + [ + 0x0b, 0x8f, 0x4c, 0x5b, 0x6a, 0xdc, 0x4c, 0x08, 0x7a, 0xb9, 0xf4, 0x3a, + 0xae, 0xb6, 0x00, 0x70, 0x84, 0xc2, 0x64, 0xad, 0xca, 0xa3, 0xcb, 0x07, + 0x17, 0x6b, 0x79, 0x23, 0x42, 0x85, 0x04, 0x12 + ] + ); + } else { + panic!("Read pcr selections contained unexpected HashingAlgorithm"); + } + }); // Now reset it again to test it's again zeroes context.execute_with_session(pcr_ses, |ctx| ctx.pcr_reset(PcrHandle::Pcr16).unwrap()); @@ -101,12 +124,20 @@ mod test_pcr_extend_reset { .with_selection(HashingAlgorithm::Sha1, &[PcrSlot::Slot16]) .with_selection(HashingAlgorithm::Sha256, &[PcrSlot::Slot16]) .build(); - let (_, _, pcr_data) = - context.execute_without_session(|ctx| ctx.pcr_read(&pcr_selection_list).unwrap()); + let pcr_data = context + .execute_without_session(|ctx| { + ctx.pcr_read(&pcr_selection_list).map( + |(_, read_pcr_selections, read_pcr_digests)| { + PcrData::create(&read_pcr_selections, &read_pcr_digests) + .expect("Failed to create PcrData") + }, + ) + }) + .expect("Failed to call pcr_read"); let pcr_sha1_bank = pcr_data.pcr_bank(HashingAlgorithm::Sha1).unwrap(); let pcr_sha256_bank = pcr_data.pcr_bank(HashingAlgorithm::Sha256).unwrap(); - let pcr_sha1_value = pcr_sha1_bank.pcr_value(PcrSlot::Slot16).unwrap(); - let pcr_sha256_value = pcr_sha256_bank.pcr_value(PcrSlot::Slot16).unwrap(); + let pcr_sha1_value = pcr_sha1_bank.get_digest(PcrSlot::Slot16).unwrap(); + let pcr_sha256_value = pcr_sha256_bank.get_digest(PcrSlot::Slot16).unwrap(); // Needs to have the length of associated with the hashing algorithm assert_eq!(pcr_sha1_value.value(), [0; 20]); assert_eq!(pcr_sha256_value.value(), [0; 32]); @@ -118,7 +149,7 @@ mod test_pcr_read { use tss_esapi::{ interface_types::algorithm::HashingAlgorithm, structures::{PcrSelectionListBuilder, PcrSlot}, - tss2_esys::{TPM2_ALG_ID, TPM2_SHA256_DIGEST_SIZE, TPML_PCR_SELECTION}, + tss2_esys::{TPM2_SHA256_DIGEST_SIZE, TPML_PCR_SELECTION}, }; #[test] @@ -133,20 +164,18 @@ mod test_pcr_read { assert_eq!(pcr_selection_list.len(), 1); assert_eq!(input.count as usize, pcr_selection_list.len()); assert_eq!(input.pcrSelections[0].sizeofSelect, 3); - assert_eq!( - input.pcrSelections[0].hash, - Into::::into(HashingAlgorithm::Sha256) - ); + assert_eq!(input.pcrSelections[0].hash, HashingAlgorithm::Sha256.into()); assert_eq!(input.pcrSelections[0].pcrSelect[0], 0b0000_0001); assert_eq!(input.pcrSelections[0].pcrSelect[1], 0b0000_0000); assert_eq!(input.pcrSelections[0].pcrSelect[2], 0b0000_0000); // Read the pcr slots. - let (update_counter, pcr_selection_list_out, pcr_data) = - context.pcr_read(&pcr_selection_list).unwrap(); + let (update_counter, read_pcr_selections, read_pcr_digests) = context + .pcr_read(&pcr_selection_list) + .expect("Failed to call pcr_read"); // Verify that the selected slots have been read. assert_ne!(update_counter, 0); - let output: TPML_PCR_SELECTION = pcr_selection_list_out.into(); + let output: TPML_PCR_SELECTION = read_pcr_selections.into(); assert_eq!(output.count, input.count); assert_eq!( output.pcrSelections[0].sizeofSelect, @@ -167,13 +196,13 @@ mod test_pcr_read { ); // Only the specified in the selection should be present. - assert_eq!(pcr_data.len(), output.count as usize); - let pcr_bank = pcr_data.pcr_bank(HashingAlgorithm::Sha256).unwrap(); - // Only one value selected - assert_eq!(pcr_bank.len(), 1); - let pcr_value = pcr_bank.pcr_value(PcrSlot::Slot0).unwrap(); + assert_eq!(read_pcr_digests.len(), 1); + assert_eq!(read_pcr_digests.len(), output.count as usize); // Needs to have the length of associated with the hashing algorithm - assert_eq!(pcr_value.value().len(), TPM2_SHA256_DIGEST_SIZE as usize); + assert_eq!( + read_pcr_digests.value()[0].len(), + TPM2_SHA256_DIGEST_SIZE as usize + ); } #[test] diff --git a/tss-esapi/tests/integration_tests/structures_tests/lists_tests/digest_list_tests.rs b/tss-esapi/tests/integration_tests/structures_tests/lists_tests/digest_list_tests.rs index f75af1f1..f0c5e6d9 100644 --- a/tss-esapi/tests/integration_tests/structures_tests/lists_tests/digest_list_tests.rs +++ b/tss-esapi/tests/integration_tests/structures_tests/lists_tests/digest_list_tests.rs @@ -44,34 +44,3 @@ fn test_add_exceeding_max_limit() { } digest_list.add(digest).unwrap_err(); } - -#[test] -fn test_conversion_from_tss_digest_list_with_count_below_min() { - // These tests might be removed in the future becuase the - // min limit only applies when the DigestList is used as - // argument to policy_or. - let mut tss_digest_list: TPML_DIGEST = Default::default(); - let mut tss_digest = TPM2B_DIGEST { - size: 32, - ..Default::default() - }; - tss_digest.buffer[..32].copy_from_slice(&[1; 32]); - tss_digest_list.count = 1; - tss_digest_list.digests[0] = tss_digest; - - let _ = DigestList::try_from(tss_digest_list).unwrap_err(); -} - -#[test] -fn test_conversion_to_tss_digest_list_with_count_below_min() { - // These tests might be removed in the future becuase the - // min limit only applies when the DigestList is used as - // argument to policy_or. - let mut digest_list: DigestList = Default::default(); - digest_list - .add(Digest::try_from(vec![1, 2, 3, 4, 5, 6, 7]).unwrap()) - .unwrap(); - if TPML_DIGEST::try_from(digest_list).is_ok() { - panic!("No error regarding a digest list that is to small was reported."); - } -} diff --git a/tss-esapi/tests/integration_tests/structures_tests/lists_tests/pcr_selection_list_tests.rs b/tss-esapi/tests/integration_tests/structures_tests/lists_tests/pcr_selection_list_tests.rs index 68c77c59..4987576c 100644 --- a/tss-esapi/tests/integration_tests/structures_tests/lists_tests/pcr_selection_list_tests.rs +++ b/tss-esapi/tests/integration_tests/structures_tests/lists_tests/pcr_selection_list_tests.rs @@ -5,6 +5,7 @@ use tss_esapi::{ interface_types::algorithm::HashingAlgorithm, structures::{PcrSelectSize, PcrSelection, PcrSelectionList, PcrSlot}, tss2_esys::{TPML_PCR_SELECTION, TPMS_PCR_SELECTION}, + Error, WrapperErrorKind, }; #[test] @@ -37,9 +38,6 @@ fn from_tpml_retains_order() { ); let selection_4 = TPMS_PCR_SELECTION::try_from(selection_4).unwrap(); - let empty_selection = PcrSelection::new(HashingAlgorithm::Sha1, PcrSelectSize::ThreeBytes, &[]); - let empty_selection = TPMS_PCR_SELECTION::try_from(empty_selection).unwrap(); - let tpml_selections = TPML_PCR_SELECTION { count: 4, pcrSelections: [ @@ -47,18 +45,18 @@ fn from_tpml_retains_order() { selection_2, selection_3, selection_4, - empty_selection, - empty_selection, - empty_selection, - empty_selection, - empty_selection, - empty_selection, - empty_selection, - empty_selection, - empty_selection, - empty_selection, - empty_selection, - empty_selection, + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), ], }; @@ -75,18 +73,374 @@ fn from_tpml_retains_order() { assert_eq!(sel_1.hashing_algorithm(), HashingAlgorithm::Sha256); assert!(!sel_1.is_empty()); - assert!(sel_1.selected_pcrs().contains(PcrSlot::Slot10)); - assert!(!sel_1.selected_pcrs().contains(PcrSlot::Slot11)); + assert!(sel_1.is_selected(PcrSlot::Slot10)); + assert!(!sel_1.is_selected(PcrSlot::Slot11)); assert_eq!(sel_2.hashing_algorithm(), HashingAlgorithm::Sha256); assert!(!sel_2.is_empty()); - assert!(sel_2.selected_pcrs().contains(PcrSlot::Slot11)); + assert!(sel_2.is_selected(PcrSlot::Slot11)); assert_eq!(sel_3.hashing_algorithm(), HashingAlgorithm::Sha1); assert!(!sel_3.is_empty()); - assert!(sel_3.selected_pcrs().contains(PcrSlot::Slot16)); + assert!(sel_3.is_selected(PcrSlot::Slot16)); assert_eq!(sel_4.hashing_algorithm(), HashingAlgorithm::Sha1); assert!(!sel_4.is_empty()); - assert!(sel_4.selected_pcrs().contains(PcrSlot::Slot2)); + assert!(sel_4.is_selected(PcrSlot::Slot2)); +} + +#[test] +fn test_subtract() { + let pcr_selection_1 = PcrSelection::new( + HashingAlgorithm::Sha256, + PcrSelectSize::ThreeBytes, + &[PcrSlot::Slot10], + ); + let tpms_pcr_selection_1 = TPMS_PCR_SELECTION::try_from(pcr_selection_1) + .expect("Failed to create tpms_pcr_selection_1"); + + let pcr_selection_2 = PcrSelection::new( + HashingAlgorithm::Sha256, + PcrSelectSize::ThreeBytes, + &[PcrSlot::Slot11], + ); + let tpms_pcr_selection_2 = TPMS_PCR_SELECTION::try_from(pcr_selection_2) + .expect("Failed to create tpms_pcr_selection_2"); + + let tpms_pcr_selection_3 = TPMS_PCR_SELECTION::try_from(PcrSelection::new( + HashingAlgorithm::Sha1, + PcrSelectSize::ThreeBytes, + &[PcrSlot::Slot16], + )) + .expect("Failed to create tpms_pcr_selection_3"); + + let tpms_pcr_selection_4 = TPMS_PCR_SELECTION::try_from(PcrSelection::new( + HashingAlgorithm::Sha1, + PcrSelectSize::ThreeBytes, + &[PcrSlot::Slot2], + )) + .expect("Failed to create tpms_pcr_selection_4"); + + let mut selection_list_1 = PcrSelectionList::try_from(TPML_PCR_SELECTION { + count: 4, + pcrSelections: [ + tpms_pcr_selection_1, + tpms_pcr_selection_2, + tpms_pcr_selection_3, + tpms_pcr_selection_4, + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + ], + }) + .expect("Failed to convert selection list 1"); + + let selection_list_2 = PcrSelectionList::try_from(TPML_PCR_SELECTION { + count: 2, + pcrSelections: [ + tpms_pcr_selection_3, + tpms_pcr_selection_4, + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + ], + }) + .expect("Failed to convert selection list 2"); + + selection_list_1 + .subtract(&selection_list_2) + .expect("Failed to subtract selection_list_2 from selection_list_1"); + let selected_pcrs = selection_list_1.get_selections(); + assert_eq!( + selected_pcrs.len(), + 2, + "There are more pcr selections in the pcr selection list then expected" + ); + assert_eq!( + selected_pcrs[0], pcr_selection_1, + "The first selection does not have expected values" + ); + assert_eq!( + selected_pcrs[1], pcr_selection_2, + "The second selection does not have expected values" + ); +} + +#[test] +fn test_subtract_overlapping_without_remaining() { + let tpms_pcr_selection_1 = TPMS_PCR_SELECTION::try_from(PcrSelection::new( + HashingAlgorithm::Sha256, + PcrSelectSize::ThreeBytes, + &[PcrSlot::Slot10], + )) + .expect("Failed to create tpms_pcr_selection_1"); + + let tpms_pcr_selection_2 = TPMS_PCR_SELECTION::try_from(PcrSelection::new( + HashingAlgorithm::Sha256, + PcrSelectSize::ThreeBytes, + &[PcrSlot::Slot11], + )) + .expect("Failed to create tpms_pcr_selection_2"); + + let tpms_pcr_selection_3 = TPMS_PCR_SELECTION::try_from(PcrSelection::new( + HashingAlgorithm::Sha256, + PcrSelectSize::ThreeBytes, + &[PcrSlot::Slot10, PcrSlot::Slot11], + )) + .expect("Failed to create tpms_pcr_selection_3"); + + let mut selection_list_1 = PcrSelectionList::try_from(TPML_PCR_SELECTION { + count: 2, + pcrSelections: [ + tpms_pcr_selection_1, + tpms_pcr_selection_2, + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + ], + }) + .expect("Failed to convert selection list 1"); + + let selection_list_2 = PcrSelectionList::try_from(TPML_PCR_SELECTION { + count: 1, + pcrSelections: [ + tpms_pcr_selection_3, + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + ], + }) + .expect("Failed to convert selection list 2"); + + selection_list_1 + .subtract(&selection_list_2) + .expect("Failed to subtract selection_list_2 from selection_list_1"); + let selected_pcrs = selection_list_1.get_selections(); + assert_eq!( + selected_pcrs.len(), + 0, + "There are more pcr selections in the pcr selection list then expected" + ); +} + +#[test] +fn test_subtract_overlapping_with_remaining() { + let tpms_pcr_selection_1 = TPMS_PCR_SELECTION::try_from(PcrSelection::new( + HashingAlgorithm::Sha256, + PcrSelectSize::ThreeBytes, + &[PcrSlot::Slot10], + )) + .expect("Failed to create tpms_pcr_selection_1"); + + let tpms_pcr_selection_2 = TPMS_PCR_SELECTION::try_from(PcrSelection::new( + HashingAlgorithm::Sha256, + PcrSelectSize::ThreeBytes, + &[PcrSlot::Slot11], + )) + .expect("Failed to create tpms_pcr_selection_2"); + + let expected = PcrSelection::new( + HashingAlgorithm::Sha256, + PcrSelectSize::ThreeBytes, + &[PcrSlot::Slot12], + ); + let tpms_pcr_selection_3 = + TPMS_PCR_SELECTION::try_from(expected).expect("Failed to create tpms_pcr_selection_2"); + + let tpms_pcr_selection_4 = TPMS_PCR_SELECTION::try_from(PcrSelection::new( + HashingAlgorithm::Sha256, + PcrSelectSize::ThreeBytes, + &[PcrSlot::Slot10, PcrSlot::Slot11], + )) + .expect("Failed to create tpms_pcr_selection_3"); + + let mut selection_list_1 = PcrSelectionList::try_from(TPML_PCR_SELECTION { + count: 3, + pcrSelections: [ + tpms_pcr_selection_1, + tpms_pcr_selection_2, + tpms_pcr_selection_3, + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + ], + }) + .expect("Failed to convert selection list 1"); + + let selection_list_2 = PcrSelectionList::try_from(TPML_PCR_SELECTION { + count: 1, + pcrSelections: [ + tpms_pcr_selection_4, + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + ], + }) + .expect("Failed to convert selection list 2"); + + selection_list_1 + .subtract(&selection_list_2) + .expect("Failed to subtract selection_list_2 from selection_list_1"); + let selected_pcrs = selection_list_1.get_selections(); + assert_eq!( + selected_pcrs.len(), + 1, + "There are more pcr selections in the pcr selection list then expected" + ); + assert_eq!( + selected_pcrs[0], expected, + "The first selection does not have expected values" + ); +} + +#[test] +fn test_invalid_subtraction() { + let pcr_selection_1 = PcrSelection::new( + HashingAlgorithm::Sha256, + PcrSelectSize::ThreeBytes, + &[PcrSlot::Slot10], + ); + let tpms_pcr_selection_1 = TPMS_PCR_SELECTION::try_from(pcr_selection_1) + .expect("Failed to create tpms_pcr_selection_1"); + + let pcr_selection_2 = PcrSelection::new( + HashingAlgorithm::Sha256, + PcrSelectSize::ThreeBytes, + &[PcrSlot::Slot11], + ); + let tpms_pcr_selection_2 = TPMS_PCR_SELECTION::try_from(pcr_selection_2) + .expect("Failed to create tpms_pcr_selection_2"); + + let tpms_pcr_selection_3 = TPMS_PCR_SELECTION::try_from(PcrSelection::new( + HashingAlgorithm::Sha1, + PcrSelectSize::ThreeBytes, + &[PcrSlot::Slot16], + )) + .expect("Failed to create tpms_pcr_selection_3"); + + let tpms_pcr_selection_4 = TPMS_PCR_SELECTION::try_from(PcrSelection::new( + HashingAlgorithm::Sha1, + PcrSelectSize::ThreeBytes, + &[PcrSlot::Slot2], + )) + .expect("Failed to create tpms_pcr_selection_4"); + + let mut selection_list_1 = PcrSelectionList::try_from(TPML_PCR_SELECTION { + count: 2, + pcrSelections: [ + tpms_pcr_selection_1, + tpms_pcr_selection_2, + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + ], + }) + .expect("Failed to convert selection list 1"); + + let selection_list_2 = PcrSelectionList::try_from(TPML_PCR_SELECTION { + count: 4, + pcrSelections: [ + tpms_pcr_selection_1, + tpms_pcr_selection_2, + tpms_pcr_selection_3, + tpms_pcr_selection_4, + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + TPMS_PCR_SELECTION::default(), + ], + }) + .expect("Failed to convert selection list 2"); + + let subtract_result = selection_list_1.subtract(&selection_list_2); + + assert_eq!( + subtract_result, + Err(Error::WrapperError(WrapperErrorKind::InvalidParam)), + "PcrSelectionList subtract method did not produce expected error for invalid parameters" + ); } diff --git a/tss-esapi/tests/integration_tests/structures_tests/pcr_tests/pcr_selection_tests.rs b/tss-esapi/tests/integration_tests/structures_tests/pcr_tests/pcr_selection_tests.rs index e3878629..abd0a500 100644 --- a/tss-esapi/tests/integration_tests/structures_tests/pcr_tests/pcr_selection_tests.rs +++ b/tss-esapi/tests/integration_tests/structures_tests/pcr_tests/pcr_selection_tests.rs @@ -41,3 +41,40 @@ fn test_conversion_from_tss_pcr_selection() { ); assert_eq!(expected, actual); } + +#[test] +fn test_subtract() { + let mut pcr_select_1 = PcrSelection::new( + HashingAlgorithm::Sha256, + PcrSelectSize::TwoBytes, + &[PcrSlot::Slot4, PcrSlot::Slot15], + ); + + let pcr_select_2 = PcrSelection::new( + HashingAlgorithm::Sha256, + PcrSelectSize::TwoBytes, + &[PcrSlot::Slot4], + ); + + pcr_select_1 + .subtract(&pcr_select_2) + .expect("Failed to subtract pcr_select_2 from pcr_select_1"); + + assert_eq!( + pcr_select_1.hashing_algorithm(), + HashingAlgorithm::Sha256, + "The pcr_select_1 did not contain expected HashingAlgorithm after subtract" + ); + + assert_eq!( + pcr_select_1.size_of_select(), + PcrSelectSize::TwoBytes, + "The pcr_select_1 did not have the expected size of select after subtract" + ); + + assert_eq!( + pcr_select_1.selected(), + vec![PcrSlot::Slot15], + "The pcr_select_1 did not contain expected PcrSlots after subtract" + ); +}