Skip to content

Commit 024976f

Browse files
authored
Merge pull request #223 from keldonin/implement_objecthandle_iterator
implements session object handle iterator, with caching
2 parents ef8aff0 + de706ce commit 024976f

File tree

4 files changed

+376
-68
lines changed

4 files changed

+376
-68
lines changed

cryptoki/src/context/mod.rs

+10-5
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,18 @@
33
//! Pkcs11 context and initialization types
44
55
/// Directly get the PKCS #11 operation from the context structure and check for null pointers.
6+
/// Note that this macro depends on the get_pkcs11_func! macro.
67
macro_rules! get_pkcs11 {
78
($pkcs11:expr, $func_name:ident) => {
8-
($pkcs11
9-
.impl_
10-
.function_list
11-
.$func_name
12-
.ok_or(crate::error::Error::NullFunctionPointer)?)
9+
(get_pkcs11_func!($pkcs11, $func_name).ok_or(crate::error::Error::NullFunctionPointer)?)
10+
};
11+
}
12+
13+
/// Same as get_pkcs11! but does not attempt to apply '?' syntactic sugar.
14+
/// Suitable only if the caller can't return a Result.
15+
macro_rules! get_pkcs11_func {
16+
($pkcs11:expr, $func_name:ident) => {
17+
($pkcs11.impl_.function_list.$func_name)
1318
};
1419
}
1520

cryptoki/src/session/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ mod session_management;
1919
mod signing_macing;
2020
mod slot_token_management;
2121

22+
pub use object_management::ObjectHandleIterator;
2223
pub use session_info::{SessionInfo, SessionState};
2324

2425
/// Type that identifies a session

cryptoki/src/session/object_management.rs

+261-52
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,277 @@
33
//! Object management functions
44
55
use crate::context::Function;
6-
use crate::error::{Result, Rv, RvError};
6+
use crate::error::{Error, Result, Rv, RvError};
77
use crate::object::{Attribute, AttributeInfo, AttributeType, ObjectHandle};
88
use crate::session::Session;
99
use cryptoki_sys::*;
1010
use std::collections::HashMap;
1111
use std::convert::TryInto;
12+
use std::num::NonZeroUsize;
1213

1314
// 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+
}
15209

16210
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+
17256
/// Search for session objects matching a template
18257
///
19258
/// # Arguments
20-
/// * `template` - A [Attribute] of search parameters that will be used
21-
/// to find objects.
22259
///
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
24277
///
25278
/// ```rust
26279
/// # fn main() -> testresult::TestResult {
@@ -50,54 +303,10 @@ impl Session {
50303
/// }
51304
/// # Ok(()) }
52305
/// ```
306+
///
307+
#[inline(always)]
53308
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()
101310
}
102311

103312
/// Create a new object

0 commit comments

Comments
 (0)