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 .map_err(|e| {
57 let _ = context.tr_close(&mut object_handle);
58 e
59 })
60 .and_then(|(nv_public, name)| {
61 context.tr_close(&mut object_handle)?;
62 Ok((nv_public, name))
63 })
64 })
65}
66
67pub fn list(context: &mut Context) -> Result<Vec<(NvPublic, Name)>> {
71 context.execute_without_session(|ctx| {
72 ctx.get_capability(
73 CapabilityType::Handles,
74 TPM2_NV_INDEX_FIRST,
75 TPM2_PT_NV_INDEX_MAX,
76 )
77 .and_then(|(capability_data, _)| match capability_data {
78 CapabilityData::Handles(tpm_handles) => Ok(tpm_handles),
79 _ => Err(Error::local_error(WrapperErrorKind::WrongValueFromTpm)),
80 })
81 .and_then(|tpm_handles| {
82 tpm_handles
83 .iter()
84 .map(|&tpm_handle| get_nv_index_info(ctx, NvIndexTpmHandle::try_from(tpm_handle)?))
85 .collect()
86 })
87 })
88}
89
90#[non_exhaustive]
92#[derive(Debug, Clone)]
93pub enum NvOpenOptions {
94 NewIndex {
96 nv_public: NvPublic,
97 auth_handle: NvAuth,
98 },
99 ExistingIndex {
101 nv_index_handle: NvIndexTpmHandle,
102 auth_handle: NvAuth,
103 },
104}
105
106impl NvOpenOptions {
107 pub fn open<'a>(&self, context: &'a mut Context) -> Result<NvReaderWriter<'a>> {
111 let buffer_size = max_nv_buffer_size(context)?;
112
113 let (data_size, nv_idx, auth_handle) = match self {
114 NvOpenOptions::ExistingIndex {
115 nv_index_handle,
116 auth_handle,
117 } => {
118 let nv_idx = TpmHandle::NvIndex(*nv_index_handle);
119 let nv_idx = context
120 .execute_without_session(|ctx| ctx.tr_from_tpm_public(nv_idx))?
121 .into();
122 (
123 context
124 .execute_without_session(|ctx| ctx.nv_read_public(nv_idx))
125 .map(|(nvpub, _)| nvpub.data_size())?,
126 nv_idx,
127 auth_handle,
128 )
129 }
130 NvOpenOptions::NewIndex {
131 nv_public,
132 auth_handle,
133 } => (
134 nv_public.data_size(),
135 context.nv_define_space(
136 AuthHandle::from(*auth_handle).try_into()?,
137 None,
138 nv_public.clone(),
139 )?,
140 auth_handle,
141 ),
142 };
143
144 Ok(NvReaderWriter {
145 context,
146 auth_handle: *auth_handle,
147 buffer_size,
148 nv_idx,
149 data_size,
150 offset: 0,
151 })
152 }
153}
154
155pub fn max_nv_buffer_size(ctx: &mut Context) -> Result<usize> {
157 Ok(ctx
158 .get_tpm_property(PropertyTag::NvBufferMax)?
159 .map(usize::try_from)
160 .transpose()
161 .map_err(|_| {
162 log::error!("Failed to obtain valid maximum NV buffer size");
163 Error::WrapperError(WrapperErrorKind::InternalError)
164 })?
165 .unwrap_or(MaxNvBuffer::MAX_SIZE))
166}
167
168#[derive(Debug)]
176pub struct NvReaderWriter<'a> {
177 context: &'a mut Context,
178 auth_handle: NvAuth,
179
180 buffer_size: usize,
181 nv_idx: NvIndexHandle,
182 data_size: usize,
183 offset: usize,
184}
185
186impl NvReaderWriter<'_> {
187 pub fn size(&self) -> usize {
189 self.data_size
190 }
191}
192
193impl Read for NvReaderWriter<'_> {
194 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
195 if self.data_size <= self.offset {
196 return Ok(0);
197 }
198
199 let desired_size = std::cmp::min(buf.len(), self.data_size - self.offset);
200 let size: u16 = std::cmp::min(self.buffer_size, desired_size) as u16;
201
202 let res = self
203 .context
204 .nv_read(self.auth_handle, self.nv_idx, size, self.offset as u16)
205 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
206 buf[0..size as usize].copy_from_slice(&res);
207 self.offset += size as usize;
208
209 Ok(size.into())
210 }
211}
212
213impl std::io::Write for NvReaderWriter<'_> {
214 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
215 if self.data_size < self.offset {
216 return Ok(0);
217 }
218
219 let desired_size = std::cmp::min(buf.len(), self.data_size - self.offset);
220 let size = std::cmp::min(self.buffer_size, desired_size) as u16;
221
222 let data = buf[0..size.into()]
223 .try_into()
224 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
225 self.context
226 .nv_write(self.auth_handle, self.nv_idx, data, self.offset as u16)
227 .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
228 self.offset += size as usize;
229
230 Ok(size.into())
231 }
232
233 fn flush(&mut self) -> std::io::Result<()> {
234 Ok(())
236 }
237}
238
239impl std::io::Seek for NvReaderWriter<'_> {
240 fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
241 let inv_input_err = |_| {
242 std::io::Error::new(
243 std::io::ErrorKind::InvalidInput,
244 "invalid seek to a negative or overflowing position",
245 )
246 };
247 let (base, offset) = match pos {
248 std::io::SeekFrom::Start(offset) => {
249 (usize::try_from(offset).map_err(inv_input_err)?, 0)
250 }
251 std::io::SeekFrom::End(offset) => (self.data_size, offset),
252 std::io::SeekFrom::Current(offset) => (self.offset, offset),
253 };
254 let new_offset = i64::try_from(base)
255 .map_err(inv_input_err)?
256 .checked_add(offset)
257 .ok_or_else(|| {
258 std::io::Error::new(
259 std::io::ErrorKind::InvalidInput,
260 "invalid seek to a negative or overflowing position",
261 )
262 })?;
263 self.offset = new_offset.try_into().map_err(inv_input_err)?;
264 self.offset.try_into().map_err(inv_input_err)
265 }
266}
267
268impl Drop for NvReaderWriter<'_> {
269 fn drop(&mut self) {
270 let mut obj_handle = self.nv_idx.into();
271 let _ = self
272 .context
273 .execute_without_session(|ctx| ctx.tr_close(&mut obj_handle));
274 }
275}