1
1
// Copyright 2020 Contributors to the Parsec project.
2
2
// SPDX-License-Identifier: Apache-2.0
3
3
4
- use std:: convert:: TryFrom ;
4
+ use std:: {
5
+ convert:: { TryFrom , TryInto } ,
6
+ io:: Read ,
7
+ } ;
5
8
6
9
use crate :: {
7
10
constants:: { tss:: * , CapabilityType , PropertyTag } ,
8
- handles:: { NvIndexHandle , NvIndexTpmHandle , TpmHandle } ,
11
+ handles:: { AuthHandle , NvIndexHandle , NvIndexTpmHandle , TpmHandle } ,
9
12
interface_types:: resource_handles:: NvAuth ,
10
- structures:: { CapabilityData , Name , NvPublic } ,
13
+ structures:: { CapabilityData , MaxNvBuffer , Name , NvPublic } ,
11
14
Context , Error , Result , WrapperErrorKind ,
12
15
} ;
13
16
@@ -17,27 +20,19 @@ pub fn read_full(
17
20
auth_handle : NvAuth ,
18
21
nv_index_handle : NvIndexTpmHandle ,
19
22
) -> 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 ( ) ) ;
30
25
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
+ } ) ?;
41
36
42
37
Ok ( result)
43
38
}
@@ -83,3 +78,185 @@ pub fn list(context: &mut Context) -> Result<Vec<(NvPublic, Name)>> {
83
78
} )
84
79
} )
85
80
}
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
+ }
0 commit comments