1use std::{
5 convert::{TryFrom, TryInto},
6 io::Read,
7};
8
9use crate::{
10 constants::{tss::*, CapabilityType, PropertyTag},
11 handles::{AuthHandle, NvIndexHandle, NvIndexTpmHandle, TpmHandle},
12 interface_types::resource_handles::NvAuth,
13 structures::{CapabilityData, MaxNvBuffer, Name, NvPublic},
14 Context, Error, Result, WrapperErrorKind,
15};
16
17pub fn read_full(
19 context: &mut Context,
20 auth_handle: NvAuth,
21 nv_index_handle: NvIndexTpmHandle,
22) -> Result<Vec<u8>> {
23 let mut rw = NvOpenOptions::ExistingIndex {
24 auth_handle,
25 nv_index_handle,
26 }
27 .open(context)?;
28 let mut result = Vec::with_capacity(rw.size());
29
30 let _ = rw.read_to_end(&mut result).map_err(|e| {
31 match e.into_inner() {
33 None => Error::WrapperError(WrapperErrorKind::InvalidParam),
34 Some(e) => match e.downcast::<Error>() {
35 Ok(e) => *e,
36 Err(_) => Error::WrapperError(WrapperErrorKind::InvalidParam),
37 },
38 }
39 })?;
40
41 Ok(result)
42}
43
44fn get_nv_index_info(
48 context: &mut Context,
49 nv_index_tpm_handle: NvIndexTpmHandle,
50) -> Result<(NvPublic, Name)> {
51 context
52 .tr_from_tpm_public(nv_index_tpm_handle.into())
53 .and_then(|mut object_handle| {
54 context
55 .nv_read_public(NvIndexHandle::from(object_handle))
56 .inspect_err(|_| {
57 let _ = context.tr_close(&mut object_handle);
58 })
59 .and_then(|(nv_public, name)| {
60 context.tr_close(&mut object_handle)?;
61 Ok((nv_public, name))
62 })
63 })
64}
65
66pub fn list(context: &mut Context) -> Result<Vec<(NvPublic, Name)>> {
70 context.execute_without_session(|ctx| {
71 ctx.get_capability(
72 CapabilityType::Handles,
73 TPM2_NV_INDEX_FIRST,
74 TPM2_PT_NV_INDEX_MAX,
75 )
76 .and_then(|(capability_data, _)| match capability_data {
77 CapabilityData::Handles(tpm_handles) => Ok(tpm_handles),
78 _ => Err(Error::local_error(WrapperErrorKind::WrongValueFromTpm)),
79 })
80 .and_then(|tpm_handles| {
81 tpm_handles
82 .iter()
83 .map(|&tpm_handle| get_nv_index_info(ctx, NvIndexTpmHandle::try_from(tpm_handle)?))
84 .collect()
85 })
86 })
87}
88
89#[non_exhaustive]
91#[derive(Debug, Clone)]
92pub enum NvOpenOptions {
93 NewIndex {
95 nv_public: NvPublic,
96 auth_handle: NvAuth,
97 },
98 ExistingIndex {
100 nv_index_handle: NvIndexTpmHandle,
101 auth_handle: NvAuth,
102 },
103}
104
105impl NvOpenOptions {
106 pub fn open<'a>(&self, context: &'a mut Context) -> Result<NvReaderWriter<'a>> {
110 let buffer_size = max_nv_buffer_size(context)?;
111
112 let (data_size, nv_idx, auth_handle) = match self {
113 NvOpenOptions::ExistingIndex {
114 nv_index_handle,
115 auth_handle,
116 } => {
117 let nv_idx = TpmHandle::NvIndex(*nv_index_handle);
118 let nv_idx = context
119 .execute_without_session(|ctx| ctx.tr_from_tpm_public(nv_idx))?
120 .into();
121 (
122 context
123 .execute_without_session(|ctx| ctx.nv_read_public(nv_idx))
124 .map(|(nvpub, _)| nvpub.data_size())?,
125 nv_idx,
126 auth_handle,
127 )
128 }
129 NvOpenOptions::NewIndex {
130 nv_public,
131 auth_handle,
132 } => (
133 nv_public.data_size(),
134 context.nv_define_space(
135 AuthHandle::from(*auth_handle).try_into()?,
136 None,
137 nv_public.clone(),
138 )?,
139 auth_handle,
140 ),
141 };
142
143 Ok(NvReaderWriter {
144 context,
145 auth_handle: *auth_handle,
146 buffer_size,
147 nv_idx,
148 data_size,
149 offset: 0,
150 })
151 }
152}
153
154pub fn max_nv_buffer_size(ctx: &mut Context) -> Result<usize> {
156 Ok(ctx
157 .get_tpm_property(PropertyTag::NvBufferMax)?
158 .map(usize::try_from)
159 .transpose()
160 .map_err(|_| {
161 log::error!("Failed to obtain valid maximum NV buffer size");
162 Error::WrapperError(WrapperErrorKind::InternalError)
163 })?
164 .unwrap_or(MaxNvBuffer::MAX_SIZE))
165}
166
167#[derive(Debug)]
175pub struct NvReaderWriter<'a> {
176 context: &'a mut Context,
177 auth_handle: NvAuth,
178
179 buffer_size: usize,
180 nv_idx: NvIndexHandle,
181 data_size: usize,
182 offset: usize,
183}
184
185impl NvReaderWriter<'_> {
186 pub fn size(&self) -> usize {
188 self.data_size
189 }
190}
191
192impl Read for NvReaderWriter<'_> {
193 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
194 if self.data_size <= self.offset {
195 return Ok(0);
196 }
197
198 let desired_size = std::cmp::min(buf.len(), self.data_size - self.offset);
199 let size: u16 = std::cmp::min(self.buffer_size, desired_size) as u16;
200
201 let res = self
202 .context
203 .nv_read(self.auth_handle, self.nv_idx, size, self.offset as u16)
204 .map_err(std::io::Error::other)?;
205 buf[0..size as usize].copy_from_slice(&res);
206 self.offset += size as usize;
207
208 Ok(size.into())
209 }
210}
211
212impl std::io::Write for NvReaderWriter<'_> {
213 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
214 if self.data_size < self.offset {
215 return Ok(0);
216 }
217
218 let desired_size = std::cmp::min(buf.len(), self.data_size - self.offset);
219 let size = std::cmp::min(self.buffer_size, desired_size) as u16;
220
221 let data = buf[0..size.into()]
222 .try_into()
223 .map_err(std::io::Error::other)?;
224 self.context
225 .nv_write(self.auth_handle, self.nv_idx, data, self.offset as u16)
226 .map_err(std::io::Error::other)?;
227 self.offset += size as usize;
228
229 Ok(size.into())
230 }
231
232 fn flush(&mut self) -> std::io::Result<()> {
233 Ok(())
235 }
236}
237
238impl std::io::Seek for NvReaderWriter<'_> {
239 fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
240 let inv_input_err = |_| {
241 std::io::Error::new(
242 std::io::ErrorKind::InvalidInput,
243 "invalid seek to a negative or overflowing position",
244 )
245 };
246 let (base, offset) = match pos {
247 std::io::SeekFrom::Start(offset) => {
248 (usize::try_from(offset).map_err(inv_input_err)?, 0)
249 }
250 std::io::SeekFrom::End(offset) => (self.data_size, offset),
251 std::io::SeekFrom::Current(offset) => (self.offset, offset),
252 };
253 let new_offset = i64::try_from(base)
254 .map_err(inv_input_err)?
255 .checked_add(offset)
256 .ok_or_else(|| {
257 std::io::Error::new(
258 std::io::ErrorKind::InvalidInput,
259 "invalid seek to a negative or overflowing position",
260 )
261 })?;
262 self.offset = new_offset.try_into().map_err(inv_input_err)?;
263 self.offset.try_into().map_err(inv_input_err)
264 }
265}
266
267impl Drop for NvReaderWriter<'_> {
268 fn drop(&mut self) {
269 let mut obj_handle = self.nv_idx.into();
270 let _ = self
271 .context
272 .execute_without_session(|ctx| ctx.tr_close(&mut obj_handle));
273 }
274}