Skip to content

Commit 14306f5

Browse files
committed
Add Seek and Write trait implementations for nv abstraction
Implement std::io::Write trait for non-volatile storage on the NvReaderWriter object. Using the std::io:Write trait exposes lower-level functionality versus a stand-alone function meaning it is more flexible, but the trait also provides higher-level functionality as well. Given that non-volatile storage is random access, implement std::io:Seek as well, giving the benefit of being able to seek to arbitrary points, as well as query the size of the storage entry. Signed-off-by: Rob Shearman <[email protected]>
1 parent 0bec9c8 commit 14306f5

File tree

2 files changed

+154
-14
lines changed
  • tss-esapi

2 files changed

+154
-14
lines changed

tss-esapi/src/abstraction/nv.rs

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

4-
use std::{convert::TryFrom, io::Read};
4+
use std::{
5+
convert::{TryFrom, TryInto},
6+
io::Read,
7+
};
58

69
use crate::{
710
constants::{tss::*, CapabilityType, PropertyTag},
8-
handles::{NvIndexHandle, NvIndexTpmHandle, TpmHandle},
11+
handles::{AuthHandle, NvIndexHandle, NvIndexTpmHandle, TpmHandle},
912
interface_types::resource_handles::NvAuth,
1013
structures::{CapabilityData, MaxNvBuffer, Name, NvPublic},
1114
Context, Error, Result, WrapperErrorKind,
@@ -81,16 +84,25 @@ pub fn list(context: &mut Context) -> Result<Vec<(NvPublic, Name)>> {
8184
/// This builder exposes the ability to determine how a [`NvReaderWriter`] is opened, and is typically used by
8285
/// calling [`NvOpenOptions::new`], chaining method calls to set each option and then calling [`NvOpenOptions::open`].
8386
#[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+
pub struct NvOpenOptions {
88+
nv_public: Option<NvPublic>,
89+
}
8790

8891
impl NvOpenOptions {
8992
/// Creates a new blank set of options for opening a non-volatile storage index
9093
///
9194
/// All options are initially set to `false`/`None`.
9295
pub fn new() -> Self {
93-
Self {}
96+
Self { nv_public: None }
97+
}
98+
99+
/// Sets the public attributes to use when creating the non-volatile storage index
100+
///
101+
/// If the public attributes are `None` then the non-volatile storage index will be opened or otherwise
102+
/// it will be created.
103+
pub fn with_nv_public(&mut self, nv_public: Option<NvPublic>) -> &mut Self {
104+
self.nv_public = nv_public;
105+
self
94106
}
95107

96108
/// Opens a non-volatile storage index using the options specified by `self`
@@ -106,13 +118,30 @@ impl NvOpenOptions {
106118
.get_tpm_property(PropertyTag::NvBufferMax)?
107119
.unwrap_or(MaxNvBuffer::MAX_SIZE as u32) as usize;
108120

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())?;
121+
let (data_size, nv_idx) = match &self.nv_public {
122+
None => {
123+
let nv_idx = TpmHandle::NvIndex(nv_index_handle);
124+
let nv_idx = context
125+
.execute_without_session(|ctx| ctx.tr_from_tpm_public(nv_idx))?
126+
.into();
127+
(
128+
context
129+
.execute_without_session(|ctx| ctx.nv_read_public(nv_idx))
130+
.map(|(nvpub, _)| nvpub.data_size())?,
131+
nv_idx,
132+
)
133+
}
134+
Some(nv_public) => {
135+
if nv_public.nv_index() != nv_index_handle {
136+
return Err(Error::WrapperError(WrapperErrorKind::InconsistentParams));
137+
}
138+
let auth_handle = AuthHandle::from(auth_handle);
139+
(
140+
nv_public.data_size(),
141+
context.nv_define_space(auth_handle.try_into()?, None, nv_public.clone())?,
142+
)
143+
}
144+
};
116145

117146
Ok(NvReaderWriter {
118147
context,
@@ -168,6 +197,61 @@ impl Read for NvReaderWriter<'_> {
168197
}
169198
}
170199

200+
impl std::io::Write for NvReaderWriter<'_> {
201+
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
202+
if self.data_size < self.offset {
203+
return Ok(0);
204+
}
205+
206+
let desired_size = std::cmp::min(buf.len(), self.data_size - self.offset);
207+
let size = std::cmp::min(self.buffer_size, desired_size) as u16;
208+
209+
let data = buf[0..size.into()]
210+
.try_into()
211+
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
212+
self.context
213+
.nv_write(self.auth_handle, self.nv_idx, data, self.offset as u16)
214+
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
215+
self.offset += size as usize;
216+
217+
Ok(size.into())
218+
}
219+
220+
fn flush(&mut self) -> std::io::Result<()> {
221+
// Data isn't buffered
222+
Ok(())
223+
}
224+
}
225+
226+
impl std::io::Seek for NvReaderWriter<'_> {
227+
fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
228+
let inv_input_err = |_| {
229+
std::io::Error::new(
230+
std::io::ErrorKind::InvalidInput,
231+
"invalid seek to a negative or overflowing position",
232+
)
233+
};
234+
let (base, offset) = match pos {
235+
std::io::SeekFrom::Start(offset) => {
236+
(usize::try_from(offset).map_err(inv_input_err)?, 0)
237+
}
238+
std::io::SeekFrom::End(offset) => (self.data_size, offset),
239+
std::io::SeekFrom::Current(offset) => (self.offset, offset),
240+
};
241+
let new_offset = i64::try_from(base)
242+
.map_err(inv_input_err)?
243+
.checked_add(offset)
244+
.ok_or_else(|| {
245+
std::io::Error::new(
246+
std::io::ErrorKind::InvalidInput,
247+
"invalid seek to a negative or overflowing position",
248+
)
249+
})?;
250+
self.offset = new_offset.try_into().map_err(inv_input_err)?;
251+
self.offset.try_into().map_err(inv_input_err)
252+
}
253+
}
254+
171255
impl Drop for NvReaderWriter<'_> {
172256
fn drop(&mut self) {
173257
let mut obj_handle = self.nv_idx.into();

tss-esapi/tests/integration_tests/abstraction_tests/nv_tests.rs

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

4-
use std::convert::TryFrom;
4+
use std::{
5+
convert::TryFrom,
6+
io::{ErrorKind, Seek, SeekFrom, Write},
7+
};
58
use tss_esapi::{
69
abstraction::nv,
710
attributes::NvIndexAttributesBuilder,
@@ -102,3 +105,56 @@ fn read_full() {
102105
assert_eq!(read_result[0..7], [1, 2, 3, 4, 5, 6, 7]);
103106
assert_eq!(read_result[1024..1031], [1, 2, 3, 4, 5, 6, 7]);
104107
}
108+
109+
#[test]
110+
fn write() {
111+
let mut context = create_ctx_with_session();
112+
113+
let nv_index = NvIndexTpmHandle::new(0x01500015).unwrap();
114+
115+
let owner_nv_index_attributes = NvIndexAttributesBuilder::new()
116+
.with_owner_write(true)
117+
.with_owner_read(true)
118+
.with_pp_read(true)
119+
.with_owner_read(true)
120+
.build()
121+
.expect("Failed to create owner nv index attributes");
122+
let owner_nv_public = NvPublicBuilder::new()
123+
.with_nv_index(nv_index)
124+
.with_index_name_algorithm(HashingAlgorithm::Sha256)
125+
.with_index_attributes(owner_nv_index_attributes)
126+
.with_data_area_size(1540)
127+
.build()
128+
.unwrap();
129+
130+
let mut rw = nv::NvOpenOptions::new()
131+
.with_nv_public(Some(owner_nv_public))
132+
.open(&mut context, NvAuth::Owner, nv_index)
133+
.unwrap();
134+
135+
let value = [1, 2, 3, 4, 5, 6, 7];
136+
rw.write_all(&value).unwrap();
137+
rw.seek(SeekFrom::Start(1024)).unwrap();
138+
rw.write_all(&value).unwrap();
139+
140+
rw.seek(SeekFrom::Start(1540)).unwrap();
141+
let e = rw.write_all(&value).unwrap_err();
142+
assert_eq!(e.kind(), ErrorKind::WriteZero);
143+
144+
// Drop the reader/writer so we can use context again
145+
drop(rw);
146+
147+
// Now read it back
148+
let read_result = nv::read_full(&mut context, NvAuth::Owner, nv_index).unwrap();
149+
150+
assert_eq!(read_result.len(), 1540);
151+
assert_eq!(read_result[0..7], [1, 2, 3, 4, 5, 6, 7]);
152+
assert_eq!(read_result[1024..1031], [1, 2, 3, 4, 5, 6, 7]);
153+
154+
let owner_nv_index_handle = context
155+
.execute_without_session(|ctx| ctx.tr_from_tpm_public(nv_index.into()))
156+
.unwrap();
157+
context
158+
.nv_undefine_space(Provision::Owner, owner_nv_index_handle.into())
159+
.unwrap();
160+
}

0 commit comments

Comments
 (0)