tss_esapi/structures/lists/
pcr_selection.rs

1// Copyright 2020 Contributors to the Parsec project.
2// SPDX-License-Identifier: Apache-2.0
3use crate::interface_types::algorithm::HashingAlgorithm;
4use crate::structures::{PcrSelectSize, PcrSelection, PcrSlot};
5use crate::tss2_esys::TPML_PCR_SELECTION;
6use crate::{Error, Result, WrapperErrorKind};
7use log::error;
8use std::collections::HashMap;
9use std::convert::TryFrom;
10
11/// A struct representing a pcr selection list. This
12/// corresponds to the TSS TPML_PCR_SELECTION.
13#[derive(Debug, Clone, PartialEq, Eq, Default)]
14pub struct PcrSelectionList {
15    items: Vec<PcrSelection>,
16}
17
18impl PcrSelectionList {
19    pub const MAX_SIZE: usize = 16;
20    /// Function for retrieiving the number of banks in the selection
21    pub fn len(&self) -> usize {
22        self.items.len()
23    }
24
25    /// Returns true if the selection is empty.
26    pub fn is_empty(&self) -> bool {
27        self.items.is_empty()
28    }
29
30    /// Gets the selections
31    pub fn get_selections(&self) -> &[PcrSelection] {
32        &self.items
33    }
34
35    /// Subtracts other from self
36    pub fn subtract(&mut self, other: &Self) -> Result<()> {
37        if self == other {
38            self.items.clear();
39            return Ok(());
40        }
41
42        if self.is_empty() {
43            error!("Cannot remove items that does not exist");
44            return Err(Error::local_error(WrapperErrorKind::InvalidParam));
45        }
46
47        for other_pcr_selection in other.get_selections() {
48            self.remove_selection(other_pcr_selection)?;
49        }
50
51        self.remove_empty_selections();
52        Ok(())
53    }
54
55    /// Function for retrieving the PcrSelectionList from `Option<PcrSelectionList>`
56    ///
57    /// This returns an empty list if None is passed
58    pub fn list_from_option(pcr_list: Option<PcrSelectionList>) -> PcrSelectionList {
59        pcr_list.unwrap_or_default()
60    }
61
62    /// Private methods for removing pcr selections that are empty.
63    fn remove_empty_selections(&mut self) {
64        self.items.retain(|v| !v.is_empty());
65    }
66
67    /// Private method for removing the items defined in a [PcrSelection]
68    /// from the data in the [PcrSelectionList].
69    fn remove_selection(&mut self, pcr_selection: &PcrSelection) -> Result<()> {
70        pcr_selection.selected().iter().try_for_each(|&pcr_slot| {
71            self.items
72                .iter_mut()
73                .find(|existing_pcr_selection| {
74                    existing_pcr_selection.hashing_algorithm() == pcr_selection.hashing_algorithm()
75                        && existing_pcr_selection.is_selected(pcr_slot)
76                })
77                .ok_or_else(|| {
78                    error!("Cannot remove items from a selection that does not exists");
79                    Error::local_error(WrapperErrorKind::InvalidParam)
80                })
81                .and_then(|existing_pcr_selection| existing_pcr_selection.deselect_exact(pcr_slot))
82        })
83    }
84
85    /// Get a builder for this structure
86    pub fn builder() -> PcrSelectionListBuilder {
87        PcrSelectionListBuilder::new()
88    }
89}
90
91impl From<PcrSelectionList> for TPML_PCR_SELECTION {
92    fn from(pcr_selections: PcrSelectionList) -> Self {
93        let mut tss_pcr_selection_list: TPML_PCR_SELECTION = Default::default();
94        for pcr_selection in pcr_selections.items {
95            tss_pcr_selection_list.pcrSelections[tss_pcr_selection_list.count as usize] =
96                pcr_selection.into();
97            tss_pcr_selection_list.count += 1;
98        }
99        tss_pcr_selection_list
100    }
101}
102
103impl TryFrom<TPML_PCR_SELECTION> for PcrSelectionList {
104    type Error = Error;
105    fn try_from(tpml_pcr_selection: TPML_PCR_SELECTION) -> Result<PcrSelectionList> {
106        let size = tpml_pcr_selection.count as usize;
107
108        if size > PcrSelectionList::MAX_SIZE {
109            error!(
110                "Invalid size value in TPML_PCR_SELECTION (> {})",
111                PcrSelectionList::MAX_SIZE
112            );
113            return Err(Error::local_error(WrapperErrorKind::InvalidParam));
114        }
115
116        let mut items = Vec::<PcrSelection>::with_capacity(size);
117        // Loop over available selections
118        for tpms_pcr_selection in tpml_pcr_selection.pcrSelections[..size].iter() {
119            // Parse pcr selection.
120            let parsed_pcr_selection = PcrSelection::try_from(*tpms_pcr_selection)?;
121            items.push(parsed_pcr_selection);
122        }
123        Ok(PcrSelectionList { items })
124    }
125}
126
127/// A builder for the PcrSelectionList struct.
128#[derive(Debug, Default)]
129pub struct PcrSelectionListBuilder {
130    size_of_select: Option<PcrSelectSize>,
131    items: HashMap<HashingAlgorithm, Vec<PcrSlot>>,
132}
133
134impl PcrSelectionListBuilder {
135    pub fn new() -> Self {
136        PcrSelectionListBuilder {
137            size_of_select: None,
138            items: Default::default(),
139        }
140    }
141
142    /// Set the size of the pcr selection(sizeofSelect)
143    ///
144    /// # Arguments
145    /// size_of_select -- The size that will be used for all selections(sizeofSelect).
146    pub fn with_size_of_select(mut self, size_of_select: PcrSelectSize) -> Self {
147        self.size_of_select = Some(size_of_select);
148        self
149    }
150
151    /// Adds a selection associated with a specific HashingAlgorithm.
152    ///
153    /// This function will not overwrite the values already associated
154    /// with a specific HashingAlgorithm only update.
155    ///
156    /// # Arguments
157    /// hash_algorithm -- The HashingAlgorithm associated with the pcr selection
158    /// pcr_slots      -- The PCR slots in the selection.
159    pub fn with_selection(
160        mut self,
161        hash_algorithm: HashingAlgorithm,
162        pcr_slots: &[PcrSlot],
163    ) -> Self {
164        // let selected_pcr_slots: BitFlags<PcrSlot> = pcr_slots.iter().cloned().collect();
165        match self.items.get_mut(&hash_algorithm) {
166            Some(previously_selected_pcr_slots) => {
167                // *previously_selected_pcr_slots |= selected_pcr_slots;
168                previously_selected_pcr_slots.extend_from_slice(pcr_slots);
169            }
170            None => {
171                let _ = self.items.insert(hash_algorithm, pcr_slots.to_vec());
172            }
173        }
174        self
175    }
176
177    /// Builds a PcrSelections with the values that have been
178    /// provided.
179    ///
180    /// If no size of select have been provided then it will
181    /// be defaulted to to the most suitable with regard to TPM2_PCR_SELECT_MAX.
182    /// This may not be the correct size for
183    /// the current platform. The correct values can be obtained
184    /// by querying the tpm for its capabilities.
185    pub fn build(self) -> Result<PcrSelectionList> {
186        let size_of_select = self.size_of_select.unwrap_or_default();
187        self.items
188            .iter()
189            .try_fold(Vec::<PcrSelection>::new(), |mut acc, (&k, v)| {
190                PcrSelection::create(k, size_of_select, v.as_slice()).map(|pcr_select| {
191                    acc.push(pcr_select);
192                    acc
193                })
194            })
195            .map(|items| PcrSelectionList { items })
196    }
197}