|
3 | 3 | //! Object management functions
|
4 | 4 |
|
5 | 5 | use crate::context::Function;
|
6 |
| -use crate::error::{Result, Rv, RvError}; |
| 6 | +use crate::error::{Error, Result, Rv, RvError}; |
7 | 7 | use crate::object::{Attribute, AttributeInfo, AttributeType, ObjectHandle};
|
8 | 8 | use crate::session::Session;
|
9 | 9 | use cryptoki_sys::*;
|
10 | 10 | use std::collections::HashMap;
|
11 | 11 | use std::convert::TryInto;
|
| 12 | +use std::num::NonZeroUsize; |
12 | 13 |
|
13 | 14 | // Search 10 elements at a time
|
14 |
| -const MAX_OBJECT_COUNT: usize = 10; |
| 15 | +// Safety: the value provided (10) must be non-zero |
| 16 | +const MAX_OBJECT_COUNT: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(10) }; |
| 17 | + |
| 18 | +/// Iterator over object handles, in an active session. |
| 19 | +/// |
| 20 | +/// Used to iterate over the object handles returned by underlying calls to `C_FindObjects`. |
| 21 | +/// The iterator is created by calling the `iter_objects` and `iter_objects_with_cache_size` methods on a `Session` object. |
| 22 | +/// |
| 23 | +/// # Note |
| 24 | +/// |
| 25 | +/// The iterator `new()` method will call `C_FindObjectsInit`. It means that until the iterator is dropped, |
| 26 | +/// creating another iterator will result in an error (typically `RvError::OperationActive` ). |
| 27 | +/// |
| 28 | +/// # Example |
| 29 | +/// |
| 30 | +/// ```no_run |
| 31 | +/// use cryptoki::context::CInitializeArgs; |
| 32 | +/// use cryptoki::context::Pkcs11; |
| 33 | +/// use cryptoki::error::Error; |
| 34 | +/// use cryptoki::object::Attribute; |
| 35 | +/// use cryptoki::object::AttributeType; |
| 36 | +/// use cryptoki::session::UserType; |
| 37 | +/// use cryptoki::types::AuthPin; |
| 38 | +/// use std::env; |
| 39 | +/// |
| 40 | +/// # fn main() -> testresult::TestResult { |
| 41 | +/// # let pkcs11 = Pkcs11::new( |
| 42 | +/// # env::var("PKCS11_SOFTHSM2_MODULE") |
| 43 | +/// # .unwrap_or_else(|_| "/usr/local/lib/libsofthsm2.so".to_string()), |
| 44 | +/// # )?; |
| 45 | +/// # |
| 46 | +/// # pkcs11.initialize(CInitializeArgs::OsThreads)?; |
| 47 | +/// # let slot = pkcs11.get_slots_with_token()?.remove(0); |
| 48 | +/// # |
| 49 | +/// # let session = pkcs11.open_ro_session(slot).unwrap(); |
| 50 | +/// # session.login(UserType::User, Some(&AuthPin::new("fedcba".into())))?; |
| 51 | +/// |
| 52 | +/// let token_object = vec![Attribute::Token(true)]; |
| 53 | +/// let wanted_attr = vec![AttributeType::Label]; |
| 54 | +/// |
| 55 | +/// for (idx, obj) in session.iter_objects(&token_object)?.enumerate() { |
| 56 | +/// let obj = obj?; // handle potential error condition |
| 57 | +/// |
| 58 | +/// let attributes = session.get_attributes(obj, &wanted_attr)?; |
| 59 | +/// |
| 60 | +/// match attributes.get(0) { |
| 61 | +/// Some(Attribute::Label(l)) => { |
| 62 | +/// println!( |
| 63 | +/// "token object #{}: handle {}, label {}", |
| 64 | +/// idx, |
| 65 | +/// obj, |
| 66 | +/// String::from_utf8(l.to_vec()) |
| 67 | +/// .unwrap_or_else(|_| "*** not valid utf8 ***".to_string()) |
| 68 | +/// ); |
| 69 | +/// } |
| 70 | +/// _ => { |
| 71 | +/// println!("token object #{}: handle {}, label not found", idx, obj); |
| 72 | +/// } |
| 73 | +/// } |
| 74 | +/// } |
| 75 | +/// # Ok(()) |
| 76 | +/// # } |
| 77 | +/// |
| 78 | +/// ``` |
| 79 | +#[derive(Debug)] |
| 80 | +pub struct ObjectHandleIterator<'a> { |
| 81 | + session: &'a Session, |
| 82 | + object_count: usize, |
| 83 | + index: usize, |
| 84 | + cache: Vec<CK_OBJECT_HANDLE>, |
| 85 | +} |
| 86 | + |
| 87 | +impl<'a> ObjectHandleIterator<'a> { |
| 88 | + /// Create a new iterator over object handles. |
| 89 | + /// |
| 90 | + /// # Arguments |
| 91 | + /// |
| 92 | + /// * `session` - The session to iterate over |
| 93 | + /// * `template` - The template to match objects against |
| 94 | + /// * `cache_size` - The number of objects to cache (type is [`NonZeroUsize`]) |
| 95 | + /// |
| 96 | + /// # Returns |
| 97 | + /// |
| 98 | + /// This function will return a [`Result<ObjectHandleIterator>`] that can be used to iterate over the objects |
| 99 | + /// matching the template. The cache size corresponds to the size of the array provided to `C_FindObjects()`. |
| 100 | + /// |
| 101 | + /// # Errors |
| 102 | + /// |
| 103 | + /// This function will return an error if the call to `C_FindObjectsInit` fails. |
| 104 | + /// |
| 105 | + /// # Note |
| 106 | + /// |
| 107 | + /// The iterator `new()` method will call `C_FindObjectsInit`. It means that until the iterator is dropped, |
| 108 | + /// creating another iterator will result in an error (typically `RvError::OperationActive` ). |
| 109 | + /// |
| 110 | + fn new( |
| 111 | + session: &'a Session, |
| 112 | + mut template: Vec<CK_ATTRIBUTE>, |
| 113 | + cache_size: NonZeroUsize, |
| 114 | + ) -> Result<Self> { |
| 115 | + unsafe { |
| 116 | + Rv::from(get_pkcs11!(session.client(), C_FindObjectsInit)( |
| 117 | + session.handle(), |
| 118 | + template.as_mut_ptr(), |
| 119 | + template.len().try_into()?, |
| 120 | + )) |
| 121 | + .into_result(Function::FindObjectsInit)?; |
| 122 | + } |
| 123 | + |
| 124 | + let cache: Vec<CK_OBJECT_HANDLE> = vec![0; cache_size.get()]; |
| 125 | + Ok(ObjectHandleIterator { |
| 126 | + session, |
| 127 | + object_count: cache_size.get(), |
| 128 | + index: cache_size.get(), |
| 129 | + cache, |
| 130 | + }) |
| 131 | + } |
| 132 | +} |
| 133 | + |
| 134 | +// In this implementation, we use object_count to keep track of the number of objects |
| 135 | +// returned by the last C_FindObjects call; the index is used to keep track of |
| 136 | +// the next object in the cache to be returned. The size of cache is never changed. |
| 137 | +// In order to enter the loop for the first time, we set object_count to cache_size |
| 138 | +// and index to cache_size. That allows to jump directly to the C_FindObjects call |
| 139 | +// and start filling the cache. |
| 140 | + |
| 141 | +impl<'a> Iterator for ObjectHandleIterator<'a> { |
| 142 | + type Item = Result<ObjectHandle>; |
| 143 | + |
| 144 | + fn next(&mut self) -> Option<Self::Item> { |
| 145 | + // since the iterator is initialized with object_count and index both equal and > 0, |
| 146 | + // we are guaranteed to enter the loop at least once |
| 147 | + while self.object_count > 0 { |
| 148 | + // if index<object_count, we have items in the cache to return |
| 149 | + if self.index < self.object_count { |
| 150 | + self.index += 1; |
| 151 | + return Some(Ok(ObjectHandle::new(self.cache[self.index - 1]))); |
| 152 | + } else { |
| 153 | + // reset counters and proceed to the next section |
| 154 | + self.index = 0; |
| 155 | + |
| 156 | + if self.object_count < self.cache.len() { |
| 157 | + // if self.object_count is less than the cache size, |
| 158 | + // it means our last call to C_FindObjects returned less than the cache size |
| 159 | + // At this point, we have exhausted all objects in the cache |
| 160 | + // and we can safely break the loop and return None |
| 161 | + self.object_count = 0; |
| 162 | + break; |
| 163 | + } else { |
| 164 | + // reset the counter - C_FindObjects will adjust that value. |
| 165 | + self.object_count = 0; |
| 166 | + } |
| 167 | + } |
| 168 | + |
| 169 | + let p11rv = match get_pkcs11_func!(self.session.client(), C_FindObjects) { |
| 170 | + Some(f) => unsafe { |
| 171 | + f( |
| 172 | + self.session.handle(), |
| 173 | + self.cache.as_mut_ptr(), |
| 174 | + self.cache.len() as CK_ULONG, |
| 175 | + &mut self.object_count as *mut usize as CK_ULONG_PTR, |
| 176 | + ) |
| 177 | + }, |
| 178 | + None => { |
| 179 | + // C_FindObjects() is not implemented,, bark and return an error |
| 180 | + log::error!("C_FindObjects() is not implemented on this library"); |
| 181 | + return Some(Err(Error::NullFunctionPointer) as Result<ObjectHandle>); |
| 182 | + } |
| 183 | + }; |
| 184 | + |
| 185 | + if let Rv::Error(error) = Rv::from(p11rv) { |
| 186 | + return Some( |
| 187 | + Err(Error::Pkcs11(error, Function::FindObjects)) as Result<ObjectHandle> |
| 188 | + ); |
| 189 | + } |
| 190 | + } |
| 191 | + None |
| 192 | + } |
| 193 | +} |
| 194 | + |
| 195 | +impl Drop for ObjectHandleIterator<'_> { |
| 196 | + fn drop(&mut self) { |
| 197 | + if let Some(f) = get_pkcs11_func!(self.session.client(), C_FindObjectsFinal) { |
| 198 | + // swallow the return value, as we can't do anything about it, |
| 199 | + // but log the error |
| 200 | + if let Rv::Error(error) = Rv::from(unsafe { f(self.session.handle()) }) { |
| 201 | + log::error!("C_FindObjectsFinal() failed with error: {:?}", error); |
| 202 | + } |
| 203 | + } else { |
| 204 | + // bark but pass if C_FindObjectsFinal() is not implemented |
| 205 | + log::error!("C_FindObjectsFinal() is not implemented on this library"); |
| 206 | + } |
| 207 | + } |
| 208 | +} |
15 | 209 |
|
16 | 210 | impl Session {
|
| 211 | + /// Iterate over session objects matching a template. |
| 212 | + /// |
| 213 | + /// # Arguments |
| 214 | + /// |
| 215 | + /// * `template` - The template to match objects against |
| 216 | + /// |
| 217 | + /// # Returns |
| 218 | + /// |
| 219 | + /// This function will return a [`Result<ObjectHandleIterator>`] that can be used to iterate over the objects |
| 220 | + /// matching the template. Note that the cache size is managed internally and set to a default value (10) |
| 221 | + /// |
| 222 | + /// # See also |
| 223 | + /// |
| 224 | + /// * [`ObjectHandleIterator`] for more information on how to use the iterator |
| 225 | + /// * [`Session::iter_objects_with_cache_size`] for a way to specify the cache size |
| 226 | + #[inline(always)] |
| 227 | + pub fn iter_objects(&self, template: &[Attribute]) -> Result<ObjectHandleIterator> { |
| 228 | + self.iter_objects_with_cache_size(template, MAX_OBJECT_COUNT) |
| 229 | + } |
| 230 | + |
| 231 | + /// Iterate over session objects matching a template, with cache size |
| 232 | + /// |
| 233 | + /// # Arguments |
| 234 | + /// |
| 235 | + /// * `template` - The template to match objects against |
| 236 | + /// * `cache_size` - The number of objects to cache (type is [`NonZeroUsize`]) |
| 237 | + /// |
| 238 | + /// # Returns |
| 239 | + /// |
| 240 | + /// This function will return a [`Result<ObjectHandleIterator>`] that can be used to iterate over the objects |
| 241 | + /// matching the template. The cache size corresponds to the size of the array provided to `C_FindObjects()`. |
| 242 | + /// |
| 243 | + /// # See also |
| 244 | + /// |
| 245 | + /// * [`ObjectHandleIterator`] for more information on how to use the iterator |
| 246 | + /// * [`Session::iter_objects`] for a simpler way to iterate over objects |
| 247 | + pub fn iter_objects_with_cache_size( |
| 248 | + &self, |
| 249 | + template: &[Attribute], |
| 250 | + cache_size: NonZeroUsize, |
| 251 | + ) -> Result<ObjectHandleIterator> { |
| 252 | + let template: Vec<CK_ATTRIBUTE> = template.iter().map(Into::into).collect(); |
| 253 | + ObjectHandleIterator::new(self, template, cache_size) |
| 254 | + } |
| 255 | + |
17 | 256 | /// Search for session objects matching a template
|
18 | 257 | ///
|
19 | 258 | /// # Arguments
|
20 |
| - /// * `template` - A [Attribute] of search parameters that will be used |
21 |
| - /// to find objects. |
22 | 259 | ///
|
23 |
| - /// # Examples |
| 260 | + /// * `template` - A reference to [Attribute] of search parameters that will be used |
| 261 | + /// to find objects. |
| 262 | + /// |
| 263 | + /// # Returns |
| 264 | + /// |
| 265 | + /// Upon success, a vector of [`ObjectHandle`] wrapped in a Result. |
| 266 | + /// Upon failure, the first error encountered. |
| 267 | + /// |
| 268 | + /// # Note |
| 269 | + /// |
| 270 | + /// It is a convenience method that will call [`Session::iter_objects`] and collect the results. |
| 271 | + /// |
| 272 | + /// # See also |
| 273 | + /// |
| 274 | + /// * [`Session::iter_objects`] for a way to specify the cache size |
| 275 | +
|
| 276 | + /// # Example |
24 | 277 | ///
|
25 | 278 | /// ```rust
|
26 | 279 | /// # fn main() -> testresult::TestResult {
|
@@ -50,54 +303,10 @@ impl Session {
|
50 | 303 | /// }
|
51 | 304 | /// # Ok(()) }
|
52 | 305 | /// ```
|
| 306 | + /// |
| 307 | + #[inline(always)] |
53 | 308 | pub fn find_objects(&self, template: &[Attribute]) -> Result<Vec<ObjectHandle>> {
|
54 |
| - let mut template: Vec<CK_ATTRIBUTE> = template.iter().map(|attr| attr.into()).collect(); |
55 |
| - |
56 |
| - unsafe { |
57 |
| - Rv::from(get_pkcs11!(self.client(), C_FindObjectsInit)( |
58 |
| - self.handle(), |
59 |
| - template.as_mut_ptr(), |
60 |
| - template.len().try_into()?, |
61 |
| - )) |
62 |
| - .into_result(Function::FindObjectsInit)?; |
63 |
| - } |
64 |
| - |
65 |
| - let mut object_handles = [0; MAX_OBJECT_COUNT]; |
66 |
| - let mut object_count = MAX_OBJECT_COUNT as CK_ULONG; // set to MAX_OBJECT_COUNT to enter loop |
67 |
| - let mut objects = Vec::new(); |
68 |
| - |
69 |
| - // as long as the number of objects returned equals the maximum number |
70 |
| - // of objects that can be returned, we keep calling C_FindObjects |
71 |
| - while object_count == MAX_OBJECT_COUNT as CK_ULONG { |
72 |
| - unsafe { |
73 |
| - Rv::from(get_pkcs11!(self.client(), C_FindObjects)( |
74 |
| - self.handle(), |
75 |
| - object_handles.as_mut_ptr() as CK_OBJECT_HANDLE_PTR, |
76 |
| - MAX_OBJECT_COUNT.try_into()?, |
77 |
| - &mut object_count, |
78 |
| - )) |
79 |
| - .into_result(Function::FindObjects)?; |
80 |
| - } |
81 |
| - |
82 |
| - // exit loop, no more objects to be returned, no need to extend the objects vector |
83 |
| - if object_count == 0 { |
84 |
| - break; |
85 |
| - } |
86 |
| - |
87 |
| - // extend the objects vector with the new objects |
88 |
| - objects.extend_from_slice(&object_handles[..object_count.try_into()?]); |
89 |
| - } |
90 |
| - |
91 |
| - unsafe { |
92 |
| - Rv::from(get_pkcs11!(self.client(), C_FindObjectsFinal)( |
93 |
| - self.handle(), |
94 |
| - )) |
95 |
| - .into_result(Function::FindObjectsFinal)?; |
96 |
| - } |
97 |
| - |
98 |
| - let objects = objects.into_iter().map(ObjectHandle::new).collect(); |
99 |
| - |
100 |
| - Ok(objects) |
| 309 | + self.iter_objects(template)?.collect() |
101 | 310 | }
|
102 | 311 |
|
103 | 312 | /// Create a new object
|
|
0 commit comments