1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
// Copyright 2020 Contributors to the Parsec project.
// SPDX-License-Identifier: Apache-2.0
use crate::{
    interface_types::algorithm::HashingAlgorithm,
    structures::{PcrSelectSize, PcrSlot, PcrSlotCollection},
    tss2_esys::TPMS_PCR_SELECTION,
    Error, Result, WrapperErrorKind,
};
use log::error;
use std::convert::TryFrom;
/// This module contains the PcrSelection struct.
/// The TSS counterpart of this struct is the
/// TPMS_PCR_SELECTION.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct PcrSelection {
    hashing_algorithm: HashingAlgorithm,
    pcr_slot_collection: PcrSlotCollection,
}

impl PcrSelection {
    /// Creates new PcrSelection
    ///
    /// # Errors
    /// Returns InconsistentParams error if a pcr slot
    /// has been provided that ends up in an octet outside the
    /// range specified by the `size_of_select` parameter.
    pub fn create(
        hashing_algorithm: HashingAlgorithm,
        size_of_select: PcrSelectSize,
        selected_pcr_slots: &[PcrSlot],
    ) -> Result<Self> {
        PcrSlotCollection::create(size_of_select, selected_pcr_slots).map(|pcr_slot_collection| {
            PcrSelection {
                hashing_algorithm,
                pcr_slot_collection,
            }
        })
    }

    /// Returns the hashing algorithm for the selection
    pub const fn hashing_algorithm(&self) -> HashingAlgorithm {
        self.hashing_algorithm
    }

    /// 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.pcr_slot_collection.size_of_select()
    }

    /// Returns the selected pcrs.
    pub fn selected(&self) -> Vec<PcrSlot> {
        self.pcr_slot_collection.collection()
    }

    /// Returns true if the specified [PcrSlot] is selected in
    /// the [PcrSelection].
    pub fn is_selected(&self, pcr_slot: PcrSlot) -> bool {
        self.pcr_slot_collection.contains(pcr_slot)
    }

    /// Removes the specified [PcrSlot]s from the selected pcrs.
    ///
    /// # Errors
    /// 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.pcr_slot_collection.remove_exact(pcr_slot)
    }

    /// Removes the specified [PcrSlot]s from the selected pcrs.
    pub fn deselect(&mut self, pcr_slot: PcrSlot) {
        self.pcr_slot_collection.remove(pcr_slot)
    }

    /// Merges another [PcrSelection] into `self` if the
    /// elements in the collection does not already exist
    /// in `self`.
    ///
    /// # Constraints
    /// * Cannot be called with `other` that has a hashing_algorithm
    ///   that is different from the one in `self`.
    /// * Cannot be called with `other` that has a size_of_select
    ///   that is different from the one in `self`.
    /// * Cannot be called with `other`that contains pcr slots present
    ///   in `self`.
    ///
    /// # Errors
    /// Returns InvalidParam if there is a hashing algorithm mismatch
    /// Returns InvalidParam if there is size of select mismatch.
    /// Returns InvalidParam if `other` contains items that are present in `self`
    pub fn merge_exact(&mut self, other: &Self) -> Result<()> {
        // Check that the hashing algorithm match
        if self.hashing_algorithm != other.hashing_algorithm {
            error!("Found inconsistencies in the hashing algorithm");
            return Err(Error::local_error(WrapperErrorKind::InvalidParam));
        }

        self.pcr_slot_collection
            .merge_exact(&other.pcr_slot_collection)
    }

    /// Removes the selected pcr slots in `other` from `self`if none
    /// of the pcr slots are present in `self`.
    ///
    /// # Constraints
    /// * Cannot be called with `other` that has a hashing_algorithm
    ///   that is different from the one in `self`.
    /// * Cannot be called with `other` that has a size_of_select
    ///   that is different from the one in `self`.
    /// * Cannot be called with `other`that contains pcr slots not present
    ///   in `self`.
    pub fn subtract_exact(&mut self, other: &Self) -> Result<()> {
        // Check that the hashing algorithm match
        if self.hashing_algorithm != other.hashing_algorithm {
            error!("Mismatched hashing algorithm ");
            return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
        }

        self.pcr_slot_collection
            .subtract_exact(&other.pcr_slot_collection)
    }

    /// Indicates whether the pcr selection is empty.
    pub fn is_empty(&self) -> bool {
        self.pcr_slot_collection.is_empty()
    }
}

impl TryFrom<TPMS_PCR_SELECTION> for PcrSelection {
    type Error = Error;

    fn try_from(tss_pcr_selection: TPMS_PCR_SELECTION) -> Result<Self> {
        // Parse hashing algorithm.
        let hashing_algorithm =
            HashingAlgorithm::try_from(tss_pcr_selection.hash).map_err(|e| {
                error!("Error converting hash to a HashingAlgorithm: {}", e);
                Error::local_error(WrapperErrorKind::InvalidParam)
            })?;

        let pcr_slot_collection = PcrSlotCollection::try_from((
            tss_pcr_selection.sizeofSelect,
            tss_pcr_selection.pcrSelect,
        ))?;

        Ok(PcrSelection {
            hashing_algorithm,
            pcr_slot_collection,
        })
    }
}

impl From<PcrSelection> for TPMS_PCR_SELECTION {
    fn from(pcr_selection: PcrSelection) -> Self {
        let (size_of_select, pcr_select) = pcr_selection.pcr_slot_collection.into();
        TPMS_PCR_SELECTION {
            hash: pcr_selection.hashing_algorithm.into(),
            sizeofSelect: size_of_select,
            pcrSelect: pcr_select,
        }
    }
}