Skip to content

Commit 9563aae

Browse files
committed
Improved return values of pcr_read.
- Changed pcr_read to return a DigestList instead of PcrData. - Moved PcrData to abstractions and made it possible to construct PcrData from rust native types. - Added subtract to PcrSelectionList. - Added a pcr_read_all functiion to the pcr module in abstractions. This function tries to read all the values in a PcrSelectionList. - Removed lower limit for DigestList. - Improved linage between the read pcr list and the read pcr digests in tests. Signed-off-by: Jesper Brynolf <[email protected]>
1 parent 1caa3d5 commit 9563aae

File tree

19 files changed

+1282
-319
lines changed

19 files changed

+1282
-319
lines changed

tss-esapi/src/abstraction/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ pub mod ak;
55
pub mod cipher;
66
pub mod ek;
77
pub mod nv;
8+
pub mod pcr;
89
pub mod transient;
910

1011
use crate::{attributes::ObjectAttributesBuilder, structures::PublicBuilder};

tss-esapi/src/abstraction/pcr.rs

+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// Copyright 2021 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
mod bank;
4+
mod data;
5+
6+
use crate::{structures::PcrSelectionList, Context, Result};
7+
8+
pub use bank::PcrBank;
9+
pub use data::PcrData;
10+
11+
/// Function that reads all the PCRs in a selection list and returns
12+
/// the result as PCR data.
13+
///
14+
/// # Example
15+
///
16+
/// ```rust
17+
/// # use tss_esapi::{Context, TctiNameConf};
18+
/// # // Create context
19+
/// # let mut context =
20+
/// # Context::new(
21+
/// # TctiNameConf::from_environment_variable().expect("Failed to get TCTI"),
22+
/// # ).expect("Failed to create Context");
23+
/// #
24+
/// use tss_esapi::{
25+
/// interface_types::algorithm::HashingAlgorithm,
26+
/// structures::{PcrSelectionListBuilder, PcrSlot},
27+
/// };
28+
/// // Create PCR selection list with slots in a bank
29+
/// // that is going to be read.
30+
/// let pcr_selection_list = PcrSelectionListBuilder::new()
31+
/// .with_selection(HashingAlgorithm::Sha256,
32+
/// &[
33+
/// PcrSlot::Slot0,
34+
/// PcrSlot::Slot1,
35+
/// PcrSlot::Slot2,
36+
/// PcrSlot::Slot3,
37+
/// PcrSlot::Slot4,
38+
/// PcrSlot::Slot5,
39+
/// PcrSlot::Slot6,
40+
/// PcrSlot::Slot7,
41+
/// PcrSlot::Slot8,
42+
/// PcrSlot::Slot9,
43+
/// PcrSlot::Slot10,
44+
/// PcrSlot::Slot11,
45+
/// PcrSlot::Slot12,
46+
/// PcrSlot::Slot13,
47+
/// PcrSlot::Slot14,
48+
/// PcrSlot::Slot15,
49+
/// PcrSlot::Slot16,
50+
/// PcrSlot::Slot17,
51+
/// PcrSlot::Slot18,
52+
/// PcrSlot::Slot19,
53+
/// PcrSlot::Slot20,
54+
/// PcrSlot::Slot21,
55+
/// ])
56+
/// .build();
57+
/// let _pcr_data = tss_esapi::abstraction::pcr::read_all(&mut context, pcr_selection_list)
58+
/// .expect("pcr::read_all failed");
59+
/// ```
60+
pub fn read_all(
61+
context: &mut Context,
62+
mut pcr_selection_list: PcrSelectionList,
63+
) -> Result<PcrData> {
64+
let mut pcr_data = PcrData::new();
65+
while !pcr_selection_list.is_empty() {
66+
let (_, pcrs_read, pcr_digests) = context.pcr_read(&pcr_selection_list)?;
67+
pcr_data.add(&pcrs_read, &pcr_digests)?;
68+
pcr_selection_list.subtract(&pcrs_read)?;
69+
}
70+
Ok(pcr_data)
71+
}

tss-esapi/src/abstraction/pcr/bank.rs

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright 2021 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use crate::{
5+
structures::{Digest, PcrSlot},
6+
Error, Result, WrapperErrorKind,
7+
};
8+
use log::error;
9+
use std::collections::BTreeMap;
10+
11+
/// Struct for holding PcrSlots and their
12+
/// corresponding values.
13+
#[derive(Debug, Clone, Eq, PartialEq)]
14+
pub struct PcrBank {
15+
bank: BTreeMap<PcrSlot, Digest>,
16+
}
17+
18+
impl PcrBank {
19+
/// Function that creates PcrBank from a vector of pcr slots and
20+
/// a vector of pcr digests.
21+
///
22+
/// # Details
23+
/// The order of pcr slots are assumed to match the order of the Digests.
24+
///
25+
/// # Error
26+
/// - If number of pcr slots does not match the number of pcr digests
27+
/// InconsistentParams error is returned.
28+
///
29+
/// - If the vector of pcr slots contains duplicates then
30+
/// InconsistentParams error is returned.
31+
pub fn create(mut pcr_slots: Vec<PcrSlot>, mut digests: Vec<Digest>) -> Result<PcrBank> {
32+
if pcr_slots.len() != digests.len() {
33+
error!(
34+
"Number of PcrSlots does not match the number of PCR digests. ({} != {})",
35+
pcr_slots.len(),
36+
digests.len()
37+
);
38+
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
39+
}
40+
pcr_slots
41+
.drain(..)
42+
.zip(digests.drain(..))
43+
.try_fold(BTreeMap::<PcrSlot, Digest>::new(), |mut data, (pcr_slot, digest)| {
44+
if data.insert(pcr_slot, digest).is_none() {
45+
Ok(data)
46+
} else {
47+
error!("Error trying to insert data into PcrSlot {:?} where data have already been inserted", pcr_slot);
48+
Err(Error::local_error(WrapperErrorKind::InconsistentParams))
49+
}
50+
})
51+
.map(|bank| PcrBank { bank })
52+
}
53+
54+
/// Retrieves reference to a [Digest] associated with the provided [PcrSlot].
55+
///
56+
/// # Details
57+
/// Returns a reference to a [Digest] associated with the provided [PcrSlot]
58+
/// if one exists else returns None.
59+
pub fn get_digest(&self, pcr_slot: PcrSlot) -> Option<&Digest> {
60+
self.bank.get(&pcr_slot)
61+
}
62+
63+
/// Returns true if the [PcrBank] contains a digest
64+
/// for the provided [PcrSlot].
65+
pub fn has_digest(&self, pcr_slot: PcrSlot) -> bool {
66+
self.bank.contains_key(&pcr_slot)
67+
}
68+
69+
/// Number of digests in the [PcrBank]]
70+
pub fn len(&self) -> usize {
71+
self.bank.len()
72+
}
73+
74+
/// Returns true if the [PcrBank] is empty
75+
pub fn is_empty(&self) -> bool {
76+
self.bank.is_empty()
77+
}
78+
79+
/// Removees the [Digest] associated with the [PcrSlot] and
80+
/// returns it.
81+
///
82+
/// # Details
83+
/// Removes the [Digest] associated with the provided [PcrSlot]
84+
/// out of the bank and returns it if it exists else returns None.
85+
pub fn remove_digest(&mut self, pcr_slot: PcrSlot) -> Option<Digest> {
86+
self.bank.remove(&pcr_slot)
87+
}
88+
89+
/// Inserts [Digest] value associated with a [PcrSlot] into the bank.
90+
///
91+
/// # Error
92+
/// Returns an error if a [Digest] is already associated with the
93+
/// provided [PcrSlot].
94+
pub fn insert_digest(&mut self, pcr_slot: PcrSlot, digest: Digest) -> Result<()> {
95+
self.ensure_non_existing(pcr_slot, "Failed to insert")?;
96+
let _ = self.bank.insert(pcr_slot, digest);
97+
Ok(())
98+
}
99+
100+
/// Attempts to extend the [PcrBank] with `other`.
101+
///
102+
/// # Error
103+
/// Returns an error if the a value in `other`already
104+
/// exists.
105+
pub fn try_extend(&mut self, other: PcrBank) -> Result<()> {
106+
other
107+
.bank
108+
.keys()
109+
.try_for_each(|&pcr_slot| self.ensure_non_existing(pcr_slot, "Failed to extend"))?;
110+
self.bank.extend(other.bank);
111+
Ok(())
112+
}
113+
114+
/// Returns an error if a [Digest] for [PcrSlot] already exists in the bank
115+
fn ensure_non_existing(&self, pcr_slot: PcrSlot, error_msg: &str) -> Result<()> {
116+
if self.has_digest(pcr_slot) {
117+
error!(
118+
"{}, a digest already for PcrSlot {:?} exists in the bank",
119+
error_msg, pcr_slot
120+
);
121+
return Err(Error::local_error(WrapperErrorKind::InvalidParam));
122+
}
123+
Ok(())
124+
}
125+
}
126+
127+
impl<'a> IntoIterator for &'a PcrBank {
128+
type Item = (&'a PcrSlot, &'a Digest);
129+
type IntoIter = ::std::collections::btree_map::Iter<'a, PcrSlot, Digest>;
130+
131+
fn into_iter(self) -> Self::IntoIter {
132+
self.bank.iter()
133+
}
134+
}

tss-esapi/src/abstraction/pcr/data.rs

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright 2021 Contributors to the Parsec project.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use crate::{
5+
abstraction::pcr::PcrBank,
6+
interface_types::algorithm::HashingAlgorithm,
7+
structures::{Digest, DigestList, PcrSelectionList},
8+
tss2_esys::TPML_DIGEST,
9+
Error, Result, WrapperErrorKind,
10+
};
11+
use log::error;
12+
/// Struct holding pcr banks and their associated
13+
/// hashing algorithm
14+
#[derive(Debug, Clone, PartialEq, Eq)]
15+
pub struct PcrData {
16+
data: Vec<(HashingAlgorithm, PcrBank)>,
17+
}
18+
19+
impl PcrData {
20+
/// Creates new empty PcrData
21+
pub const fn new() -> Self {
22+
PcrData { data: Vec::new() }
23+
}
24+
25+
/// Function for creating PcrData from a pcr selection list and pcr digests list.
26+
pub fn create(
27+
pcr_selection_list: &PcrSelectionList,
28+
digest_list: &DigestList,
29+
) -> Result<PcrData> {
30+
Ok(PcrData {
31+
data: Self::create_data(pcr_selection_list, digest_list.value().to_vec())?,
32+
})
33+
}
34+
35+
/// Adds data to the PcrData
36+
pub fn add(
37+
&mut self,
38+
pcr_selection_list: &PcrSelectionList,
39+
digest_list: &DigestList,
40+
) -> Result<()> {
41+
Self::create_data(pcr_selection_list, digest_list.value().to_vec())?
42+
.drain(..)
43+
.try_for_each(|(hashing_algorithm, pcr_bank)| {
44+
if let Some(existing_pcr_bank) = self.pcr_bank_mut(hashing_algorithm) {
45+
existing_pcr_bank.try_extend(pcr_bank)?;
46+
} else {
47+
self.data.push((hashing_algorithm, pcr_bank));
48+
}
49+
Ok(())
50+
})
51+
}
52+
53+
/// Function for turning a pcr selection list and pcr digests values
54+
/// into the format in which data is stored in PcrData.
55+
fn create_data(
56+
pcr_selection_list: &PcrSelectionList,
57+
mut digests: Vec<Digest>,
58+
) -> Result<Vec<(HashingAlgorithm, PcrBank)>> {
59+
pcr_selection_list
60+
.get_selections()
61+
.iter()
62+
.map(|pcr_selection| {
63+
let pcr_slots = pcr_selection.selected();
64+
if pcr_slots.len() > digests.len() {
65+
error!("More pcr slots in selection then available digests");
66+
return Err(Error::local_error(WrapperErrorKind::InconsistentParams));
67+
}
68+
let digests_in_bank = digests.drain(..pcr_slots.len()).collect();
69+
Ok((
70+
pcr_selection.hashing_algorithm(),
71+
PcrBank::create(pcr_slots, digests_in_bank)?,
72+
))
73+
})
74+
.collect()
75+
}
76+
77+
/// Function for retrieving the first PCR values associated with hashing_algorithm.
78+
pub fn pcr_bank(&self, hashing_algorithm: HashingAlgorithm) -> Option<&PcrBank> {
79+
self.data
80+
.iter()
81+
.find(|(alg, _)| *alg == hashing_algorithm)
82+
.map(|(_, bank)| bank)
83+
}
84+
85+
/// Function for retrieving the number of banks in the data.
86+
pub fn len(&self) -> usize {
87+
self.data.len()
88+
}
89+
90+
/// Returns true if there are no banks in the data.
91+
pub fn is_empty(&self) -> bool {
92+
self.data.is_empty()
93+
}
94+
95+
/// Private method for finding a PCR bank.
96+
fn pcr_bank_mut(&mut self, hashing_algorithm: HashingAlgorithm) -> Option<&mut PcrBank> {
97+
self.data
98+
.iter_mut()
99+
.find(|(alg, _)| *alg == hashing_algorithm)
100+
.map(|(_, bank)| bank)
101+
}
102+
}
103+
104+
impl<'a> IntoIterator for PcrData {
105+
type Item = (HashingAlgorithm, PcrBank);
106+
type IntoIter = ::std::vec::IntoIter<(HashingAlgorithm, PcrBank)>;
107+
108+
fn into_iter(self) -> Self::IntoIter {
109+
self.data.into_iter()
110+
}
111+
}
112+
113+
impl From<PcrData> for Vec<TPML_DIGEST> {
114+
fn from(pcr_data: PcrData) -> Self {
115+
pcr_data
116+
.data
117+
.iter()
118+
.flat_map(|(_, pcr_bank)| pcr_bank.into_iter())
119+
.map(|(_, digest)| digest)
120+
.collect::<Vec<&Digest>>()
121+
.chunks(DigestList::MAX_SIZE)
122+
.map(|digests| {
123+
let mut tpml_digest: TPML_DIGEST = Default::default();
124+
for (index, digest) in digests.iter().enumerate() {
125+
tpml_digest.count += 1;
126+
tpml_digest.digests[index].size = digest.len() as u16;
127+
tpml_digest.digests[index].buffer[..digest.len()]
128+
.copy_from_slice(digest.value());
129+
}
130+
tpml_digest
131+
})
132+
.collect()
133+
}
134+
}

tss-esapi/src/context/tpm_commands/enhanced_authorization_ea_commands.rs

+9-3
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ impl Context {
131131
/// by the value of the different hashes.
132132
///
133133
/// # Constraints
134-
/// * `hash_list` must be at least 2 and at most 8 elements long
134+
/// * `digest_list` must be at least 2 and at most 8 elements long
135135
///
136136
/// # Errors
137137
/// * if the hash list provided is too short or too long, a `WrongParamSize` wrapper error will be returned
@@ -140,7 +140,13 @@ impl Context {
140140
policy_session: PolicySession,
141141
digest_list: DigestList,
142142
) -> Result<()> {
143-
let digest_list = TPML_DIGEST::try_from(digest_list)?;
143+
if 2 > digest_list.len() {
144+
error!(
145+
"The digest list only contains {} digests, it must contain at least 2",
146+
digest_list.len()
147+
);
148+
return Err(Error::local_error(ErrorKind::WrongParamSize));
149+
}
144150

145151
let ret = unsafe {
146152
Esys_PolicyOR(
@@ -149,7 +155,7 @@ impl Context {
149155
self.optional_session_1(),
150156
self.optional_session_2(),
151157
self.optional_session_3(),
152-
&digest_list,
158+
&TPML_DIGEST::try_from(digest_list)?,
153159
)
154160
};
155161
let ret = Error::from_tss_rc(ret);

0 commit comments

Comments
 (0)