Skip to content

Commit 3295dba

Browse files
authored
Merge pull request #324 from rshearman/nv-abstract
nv: Read, Seek and Write trait implementations
2 parents b62029d + 14306f5 commit 3295dba

File tree

2 files changed

+257
-24
lines changed
  • tss-esapi

2 files changed

+257
-24
lines changed

tss-esapi/src/abstraction/nv.rs

Lines changed: 200 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
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, 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,
10-
structures::{CapabilityData, Name, NvPublic},
13+
structures::{CapabilityData, MaxNvBuffer, Name, NvPublic},
1114
Context, Error, Result, WrapperErrorKind,
1215
};
1316

@@ -17,27 +20,19 @@ pub fn read_full(
1720
auth_handle: NvAuth,
1821
nv_index_handle: NvIndexTpmHandle,
1922
) -> Result<Vec<u8>> {
20-
let maxsize = context
21-
.get_tpm_property(PropertyTag::NvBufferMax)?
22-
.unwrap_or(512) as usize;
23-
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();
23+
let mut rw = NvOpenOptions::new().open(context, auth_handle, nv_index_handle)?;
24+
let mut result = Vec::with_capacity(rw.size());
3025

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()))?;
26+
let _ = rw.read_to_end(&mut result).map_err(|e| {
27+
// Try to convert the error back into a tss-esapi::Error if it was one originally
28+
match e.into_inner() {
29+
None => Error::WrapperError(WrapperErrorKind::InvalidParam),
30+
Some(e) => match e.downcast::<Error>() {
31+
Ok(e) => *e,
32+
Err(_) => Error::WrapperError(WrapperErrorKind::InvalidParam),
33+
},
34+
}
35+
})?;
4136

4237
Ok(result)
4338
}
@@ -83,3 +78,185 @@ pub fn list(context: &mut Context) -> Result<Vec<(NvPublic, Name)>> {
8378
})
8479
})
8580
}
81+
82+
/// Options and flags which can be used to determine how a non-volatile storage index is opened.
83+
///
84+
/// This builder exposes the ability to determine how a [`NvReaderWriter`] is opened, and is typically used by
85+
/// calling [`NvOpenOptions::new`], chaining method calls to set each option and then calling [`NvOpenOptions::open`].
86+
#[derive(Debug, Clone, Default)]
87+
pub struct NvOpenOptions {
88+
nv_public: Option<NvPublic>,
89+
}
90+
91+
impl NvOpenOptions {
92+
/// Creates a new blank set of options for opening a non-volatile storage index
93+
///
94+
/// All options are initially set to `false`/`None`.
95+
pub fn new() -> 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
106+
}
107+
108+
/// Opens a non-volatile storage index using the options specified by `self`
109+
///
110+
/// The non-volatile storage index may be used for reading or writing or both.
111+
pub fn open<'a>(
112+
&self,
113+
context: &'a mut Context,
114+
auth_handle: NvAuth,
115+
nv_index_handle: NvIndexTpmHandle,
116+
) -> Result<NvReaderWriter<'a>> {
117+
let buffer_size = context
118+
.get_tpm_property(PropertyTag::NvBufferMax)?
119+
.unwrap_or(MaxNvBuffer::MAX_SIZE as u32) as usize;
120+
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+
};
145+
146+
Ok(NvReaderWriter {
147+
context,
148+
auth_handle,
149+
buffer_size,
150+
nv_idx,
151+
data_size,
152+
offset: 0,
153+
})
154+
}
155+
}
156+
157+
/// Non-volatile storage index reader/writer
158+
///
159+
/// Provides methods and trait implementations to interact with a non-volatile storage index that has been opened.
160+
///
161+
/// Use [`NvOpenOptions::open`] to obtain an [`NvReaderWriter`] object.
162+
#[derive(Debug)]
163+
pub struct NvReaderWriter<'a> {
164+
context: &'a mut Context,
165+
auth_handle: NvAuth,
166+
167+
buffer_size: usize,
168+
nv_idx: NvIndexHandle,
169+
data_size: usize,
170+
offset: usize,
171+
}
172+
173+
impl NvReaderWriter<'_> {
174+
/// The size of the data in the non-volatile storage index
175+
pub fn size(&self) -> usize {
176+
self.data_size
177+
}
178+
}
179+
180+
impl Read for NvReaderWriter<'_> {
181+
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
182+
if self.data_size <= self.offset {
183+
return Ok(0);
184+
}
185+
186+
let desired_size = std::cmp::min(buf.len(), self.data_size - self.offset);
187+
let size: u16 = std::cmp::min(self.buffer_size, desired_size) as u16;
188+
189+
let res = self
190+
.context
191+
.nv_read(self.auth_handle, self.nv_idx, size, self.offset as u16)
192+
.map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
193+
buf[0..size as usize].copy_from_slice(&res);
194+
self.offset += size as usize;
195+
196+
Ok(size.into())
197+
}
198+
}
199+
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+
255+
impl Drop for NvReaderWriter<'_> {
256+
fn drop(&mut self) {
257+
let mut obj_handle = self.nv_idx.into();
258+
let _ = self
259+
.context
260+
.execute_without_session(|ctx| ctx.tr_close(&mut obj_handle));
261+
}
262+
}

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)