Skip to content

Commit 0bec9c8

Browse files
committed
Add Read trait implementation for nv abstraction
There is an existing read_full function that abstracts non-volatile storage reading, but it is specific to the one task and is hard to use more generically. So implement std::io::Read trait for non-volatile storage by adding an NvReaderWriter object which the trait is implemented for, allowing the object to be obtained through an NvOpenOptions builder object. Using the std::io::Read trait exposes lower-level functionality meaning it is more flexible, but the trait also provides higher-level functionality as well, with the only downside being potentially it not being as efficient. It also allows the use of TPM non-volatile storage in any place that a Read trait is implemented, such as std::io::copy. Implement the existing read_full function in terms of the NvOpenOptions builder, NvReaderWriter::size and NvReaderWriter::read_to_end, which is provided by the Read trait. Signed-off-by: Rob Shearman <[email protected]>
1 parent 6f1b80b commit 0bec9c8

File tree

1 file changed

+115
-22
lines changed
  • tss-esapi/src/abstraction

1 file changed

+115
-22
lines changed

tss-esapi/src/abstraction/nv.rs

Lines changed: 115 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
// Copyright 2020 Contributors to the Parsec project.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
use std::convert::TryFrom;
4+
use std::{convert::TryFrom, io::Read};
55

66
use crate::{
77
constants::{tss::*, CapabilityType, PropertyTag},
88
handles::{NvIndexHandle, NvIndexTpmHandle, TpmHandle},
99
interface_types::resource_handles::NvAuth,
10-
structures::{CapabilityData, Name, NvPublic},
10+
structures::{CapabilityData, MaxNvBuffer, Name, NvPublic},
1111
Context, Error, Result, WrapperErrorKind,
1212
};
1313

@@ -17,27 +17,19 @@ pub fn read_full(
1717
auth_handle: NvAuth,
1818
nv_index_handle: NvIndexTpmHandle,
1919
) -> Result<Vec<u8>> {
20-
let maxsize = context
21-
.get_tpm_property(PropertyTag::NvBufferMax)?
22-
.unwrap_or(512) as usize;
20+
let mut rw = NvOpenOptions::new().open(context, auth_handle, nv_index_handle)?;
21+
let mut result = Vec::with_capacity(rw.size());
2322

24-
let nv_idx = TpmHandle::NvIndex(nv_index_handle);
25-
let nv_idx = context.execute_without_session(|ctx| ctx.tr_from_tpm_public(nv_idx))?;
26-
let nv_idx: NvIndexHandle = nv_idx.into();
27-
28-
let (nvpub, _) = context.execute_without_session(|ctx| ctx.nv_read_public(nv_idx))?;
29-
let nvsize = nvpub.data_size();
30-
31-
let mut result = Vec::new();
32-
result.reserve_exact(nvsize);
33-
34-
for offset in (0..nvsize).step_by(maxsize) {
35-
let size: u16 = std::cmp::min(maxsize, nvsize - offset) as u16;
36-
37-
let res = context.nv_read(auth_handle, nv_idx, size, offset as u16)?;
38-
result.extend_from_slice(&res);
39-
}
40-
context.execute_without_session(|ctx| ctx.tr_close(&mut nv_idx.into()))?;
23+
let _ = rw.read_to_end(&mut result).map_err(|e| {
24+
// Try to convert the error back into a tss-esapi::Error if it was one originally
25+
match e.into_inner() {
26+
None => Error::WrapperError(WrapperErrorKind::InvalidParam),
27+
Some(e) => match e.downcast::<Error>() {
28+
Ok(e) => *e,
29+
Err(_) => Error::WrapperError(WrapperErrorKind::InvalidParam),
30+
},
31+
}
32+
})?;
4133

4234
Ok(result)
4335
}
@@ -83,3 +75,104 @@ pub fn list(context: &mut Context) -> Result<Vec<(NvPublic, Name)>> {
8375
})
8476
})
8577
}
78+
79+
/// Options and flags which can be used to determine how a non-volatile storage index is opened.
80+
///
81+
/// This builder exposes the ability to determine how a [`NvReaderWriter`] is opened, and is typically used by
82+
/// calling [`NvOpenOptions::new`], chaining method calls to set each option and then calling [`NvOpenOptions::open`].
83+
#[derive(Debug, Clone, Default)]
84+
// The type is going to get more complex in the future
85+
#[allow(missing_copy_implementations)]
86+
pub struct NvOpenOptions {}
87+
88+
impl NvOpenOptions {
89+
/// Creates a new blank set of options for opening a non-volatile storage index
90+
///
91+
/// All options are initially set to `false`/`None`.
92+
pub fn new() -> Self {
93+
Self {}
94+
}
95+
96+
/// Opens a non-volatile storage index using the options specified by `self`
97+
///
98+
/// The non-volatile storage index may be used for reading or writing or both.
99+
pub fn open<'a>(
100+
&self,
101+
context: &'a mut Context,
102+
auth_handle: NvAuth,
103+
nv_index_handle: NvIndexTpmHandle,
104+
) -> Result<NvReaderWriter<'a>> {
105+
let buffer_size = context
106+
.get_tpm_property(PropertyTag::NvBufferMax)?
107+
.unwrap_or(MaxNvBuffer::MAX_SIZE as u32) as usize;
108+
109+
let nv_idx = TpmHandle::NvIndex(nv_index_handle);
110+
let nv_idx = context
111+
.execute_without_session(|ctx| ctx.tr_from_tpm_public(nv_idx))?
112+
.into();
113+
let data_size = context
114+
.execute_without_session(|ctx| ctx.nv_read_public(nv_idx))
115+
.map(|(nvpub, _)| nvpub.data_size())?;
116+
117+
Ok(NvReaderWriter {
118+
context,
119+
auth_handle,
120+
buffer_size,
121+
nv_idx,
122+
data_size,
123+
offset: 0,
124+
})
125+
}
126+
}
127+
128+
/// Non-volatile storage index reader/writer
129+
///
130+
/// Provides methods and trait implementations to interact with a non-volatile storage index that has been opened.
131+
///
132+
/// Use [`NvOpenOptions::open`] to obtain an [`NvReaderWriter`] object.
133+
#[derive(Debug)]
134+
pub struct NvReaderWriter<'a> {
135+
context: &'a mut Context,
136+
auth_handle: NvAuth,
137+
138+
buffer_size: usize,
139+
nv_idx: NvIndexHandle,
140+
data_size: usize,
141+
offset: usize,
142+
}
143+
144+
impl NvReaderWriter<'_> {
145+
/// The size of the data in the non-volatile storage index
146+
pub fn size(&self) -> usize {
147+
self.data_size
148+
}
149+
}
150+
151+
impl Read for NvReaderWriter<'_> {
152+
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
153+
if self.data_size <= self.offset {
154+
return Ok(0);
155+
}
156+
157+
let desired_size = std::cmp::min(buf.len(), self.data_size - self.offset);
158+
let size: u16 = std::cmp::min(self.buffer_size, desired_size) as u16;
159+
160+
let res = self
161+
.context
162+
.nv_read(self.auth_handle, self.nv_idx, size, self.offset as u16)
163+
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
164+
buf[0..size as usize].copy_from_slice(&res);
165+
self.offset += size as usize;
166+
167+
Ok(size.into())
168+
}
169+
}
170+
171+
impl Drop for NvReaderWriter<'_> {
172+
fn drop(&mut self) {
173+
let mut obj_handle = self.nv_idx.into();
174+
let _ = self
175+
.context
176+
.execute_without_session(|ctx| ctx.tr_close(&mut obj_handle));
177+
}
178+
}

0 commit comments

Comments
 (0)