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
// 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<PcrSlot, Digest>,
}

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.
    ///
    /// # Errors
    /// - 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<PcrSlot>, mut digests: Vec<Digest>) -> Result<PcrBank> {
        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::<PcrSlot, Digest>::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<Digest> {
        self.bank.remove(&pcr_slot)
    }

    /// Inserts [Digest] value associated with a [PcrSlot] into the bank.
    ///
    /// # Errors
    /// 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`.
    ///
    /// # Errors
    /// 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()
    }
}