use crate::interface_types::algorithm::HashingAlgorithm;
use crate::structures::{PcrSelectSize, PcrSelection, PcrSlot};
use crate::tss2_esys::TPML_PCR_SELECTION;
use crate::{Error, Result, WrapperErrorKind};
use log::error;
use std::collections::HashMap;
use std::convert::TryFrom;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PcrSelectionList {
items: HashMap<HashingAlgorithm, PcrSelection>,
}
impl PcrSelectionList {
pub const MAX_SIZE: usize = 16;
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn list_from_option(pcr_list: Option<PcrSelectionList>) -> PcrSelectionList {
pcr_list.unwrap_or_else(|| PcrSelectionListBuilder::new().build())
}
pub fn subtract(&mut self, other: &Self) -> Result<()> {
if self == other {
self.items.clear();
return Ok(());
}
if self.is_empty() {
error!("Error: Trying to remove item that did not exist");
return Err(Error::local_error(WrapperErrorKind::InvalidParam));
}
for hashing_algorithm in other.items.keys() {
let pcr_selection = match self.items.get_mut(&hashing_algorithm) {
Some(val) => val,
None => {
error!("Error: Trying to remove item that did not exist");
return Err(Error::local_error(WrapperErrorKind::InvalidParam));
}
};
if let Some(val) = other.items.get(&hashing_algorithm) {
pcr_selection.subtract(val)?;
if pcr_selection.is_empty() {
let _ = self.items.remove(&hashing_algorithm);
}
}
}
Ok(())
}
}
impl From<PcrSelectionList> for TPML_PCR_SELECTION {
fn from(pcr_selections: PcrSelectionList) -> TPML_PCR_SELECTION {
let mut tss_pcr_selection_list: TPML_PCR_SELECTION = Default::default();
for (_, pcr_selection) in pcr_selections.items {
tss_pcr_selection_list.pcrSelections[tss_pcr_selection_list.count as usize] =
pcr_selection.into();
tss_pcr_selection_list.count += 1;
}
tss_pcr_selection_list
}
}
impl TryFrom<TPML_PCR_SELECTION> for PcrSelectionList {
type Error = Error;
fn try_from(tpml_pcr_selection: TPML_PCR_SELECTION) -> Result<PcrSelectionList> {
let size = tpml_pcr_selection.count as usize;
if size > PcrSelectionList::MAX_SIZE {
error!(
"Invalid size value in TPML_PCR_SELECTION (> {})",
PcrSelectionList::MAX_SIZE
);
return Err(Error::local_error(WrapperErrorKind::InvalidParam));
}
let mut items = HashMap::<HashingAlgorithm, PcrSelection>::new();
for tpms_pcr_selection in tpml_pcr_selection.pcrSelections[..size].iter() {
let parsed_pcr_selection = PcrSelection::try_from(*tpms_pcr_selection)?;
match items.get_mut(&parsed_pcr_selection.hashing_algorithm()) {
Some(previously_parsed_pcr_selection) => {
previously_parsed_pcr_selection.merge(&parsed_pcr_selection)?;
}
None => {
let _ = items.insert(
parsed_pcr_selection.hashing_algorithm(),
parsed_pcr_selection,
);
}
}
}
Ok(PcrSelectionList { items })
}
}
#[derive(Debug, Default)]
pub struct PcrSelectionListBuilder {
size_of_select: Option<PcrSelectSize>,
items: HashMap<HashingAlgorithm, Vec<PcrSlot>>,
}
impl PcrSelectionListBuilder {
pub fn new() -> Self {
PcrSelectionListBuilder {
size_of_select: None,
items: Default::default(),
}
}
pub fn with_size_of_select(mut self, size_of_select: PcrSelectSize) -> Self {
self.size_of_select = Some(size_of_select);
self
}
pub fn with_selection(
mut self,
hash_algorithm: HashingAlgorithm,
pcr_slots: &[PcrSlot],
) -> Self {
match self.items.get_mut(&hash_algorithm) {
Some(previously_selected_pcr_slots) => {
previously_selected_pcr_slots.extend_from_slice(pcr_slots);
}
None => {
let _ = self.items.insert(hash_algorithm, pcr_slots.to_vec());
}
}
self
}
pub fn build(self) -> PcrSelectionList {
let size_of_select = self.size_of_select.unwrap_or_default();
PcrSelectionList {
items: self
.items
.iter()
.map(|(k, v)| (*k, PcrSelection::new(*k, size_of_select, v.as_slice())))
.collect(),
}
}
}