use crate::as_cptr;
use crate::context::Function;
use crate::error::{Error, Result, Rv, RvError};
use crate::object::{Attribute, AttributeInfo, AttributeType, ObjectHandle};
use crate::session::Session;
use cryptoki_sys::*;
use std::collections::HashMap;
use std::convert::TryInto;
use std::num::NonZeroUsize;
const MAX_OBJECT_COUNT: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(10) };
#[derive(Debug)]
pub struct ObjectHandleIterator<'a> {
session: &'a Session,
object_count: usize,
index: usize,
cache: Vec<CK_OBJECT_HANDLE>,
}
impl<'a> ObjectHandleIterator<'a> {
fn new(
session: &'a Session,
mut template: Vec<CK_ATTRIBUTE>,
cache_size: NonZeroUsize,
) -> Result<Self> {
unsafe {
Rv::from(get_pkcs11!(session.client(), C_FindObjectsInit)(
session.handle(),
template.as_mut_ptr(),
template.len().try_into()?,
))
.into_result(Function::FindObjectsInit)?;
}
let cache: Vec<CK_OBJECT_HANDLE> = vec![0; cache_size.get()];
Ok(ObjectHandleIterator {
session,
object_count: cache_size.get(),
index: cache_size.get(),
cache,
})
}
}
impl Iterator for ObjectHandleIterator<'_> {
type Item = Result<ObjectHandle>;
fn next(&mut self) -> Option<Self::Item> {
while self.object_count > 0 {
if self.index < self.object_count {
self.index += 1;
return Some(Ok(ObjectHandle::new(self.cache[self.index - 1])));
} else {
self.index = 0;
if self.object_count < self.cache.len() {
self.object_count = 0;
break;
} else {
self.object_count = 0;
}
}
let p11rv = match get_pkcs11_func!(self.session.client(), C_FindObjects) {
Some(f) => unsafe {
f(
self.session.handle(),
self.cache.as_mut_ptr(),
self.cache.len() as CK_ULONG,
&mut self.object_count as *mut usize as CK_ULONG_PTR,
)
},
None => {
log::error!("C_FindObjects() is not implemented on this library");
return Some(Err(Error::NullFunctionPointer) as Result<ObjectHandle>);
}
};
if let Rv::Error(error) = Rv::from(p11rv) {
return Some(
Err(Error::Pkcs11(error, Function::FindObjects)) as Result<ObjectHandle>
);
}
}
None
}
}
impl Drop for ObjectHandleIterator<'_> {
fn drop(&mut self) {
if let Some(f) = get_pkcs11_func!(self.session.client(), C_FindObjectsFinal) {
if let Rv::Error(err) = Rv::from(unsafe { f(self.session.handle()) }) {
log::error!("C_FindObjectsFinal() failed with error: {err:?}");
}
} else {
log::error!("C_FindObjectsFinal() is not implemented on this library");
}
}
}
impl Session {
#[inline(always)]
pub fn iter_objects(&self, template: &[Attribute]) -> Result<ObjectHandleIterator<'_>> {
self.iter_objects_with_cache_size(template, MAX_OBJECT_COUNT)
}
pub fn iter_objects_with_cache_size(
&self,
template: &[Attribute],
cache_size: NonZeroUsize,
) -> Result<ObjectHandleIterator<'_>> {
let template: Vec<CK_ATTRIBUTE> = template.iter().map(Into::into).collect();
ObjectHandleIterator::new(self, template, cache_size)
}
#[inline(always)]
pub fn find_objects(&self, template: &[Attribute]) -> Result<Vec<ObjectHandle>> {
self.iter_objects(template)?.collect()
}
pub fn create_object(&self, template: &[Attribute]) -> Result<ObjectHandle> {
let mut template: Vec<CK_ATTRIBUTE> = template.iter().map(|attr| attr.into()).collect();
let mut object_handle = 0;
unsafe {
Rv::from(get_pkcs11!(self.client(), C_CreateObject)(
self.handle(),
template.as_mut_ptr(),
template.len().try_into()?,
&mut object_handle as CK_OBJECT_HANDLE_PTR,
))
.into_result(Function::CreateObject)?;
}
Ok(ObjectHandle::new(object_handle))
}
pub fn destroy_object(&self, object: ObjectHandle) -> Result<()> {
unsafe {
Rv::from(get_pkcs11!(self.client(), C_DestroyObject)(
self.handle(),
object.handle(),
))
.into_result(Function::DestroyObject)
}
}
pub fn copy_object(
&self,
object: ObjectHandle,
template: &[Attribute],
) -> Result<ObjectHandle> {
let mut template: Vec<CK_ATTRIBUTE> = template.iter().map(|attr| attr.into()).collect();
let mut object_handle = 0;
unsafe {
Rv::from(get_pkcs11!(self.client(), C_CopyObject)(
self.handle(),
object.handle(),
template.as_mut_ptr(),
template.len().try_into()?,
&mut object_handle as CK_OBJECT_HANDLE_PTR,
))
.into_result(Function::CopyObject)?;
}
Ok(ObjectHandle::new(object_handle))
}
pub fn get_attribute_info(
&self,
object: ObjectHandle,
attributes: &[AttributeType],
) -> Result<Vec<AttributeInfo>> {
let mut results = Vec::new();
for attrib in attributes.iter() {
let mut template: Vec<CK_ATTRIBUTE> = vec![CK_ATTRIBUTE {
type_: (*attrib).into(),
pValue: std::ptr::null_mut(),
ulValueLen: 0,
}];
match unsafe {
Rv::from(get_pkcs11!(self.client(), C_GetAttributeValue)(
self.handle(),
object.handle(),
template.as_mut_ptr(),
template.len().try_into()?,
))
} {
Rv::Ok => {
if template[0].ulValueLen == CK_UNAVAILABLE_INFORMATION {
results.push(AttributeInfo::Unavailable)
} else {
results.push(AttributeInfo::Available(template[0].ulValueLen.try_into()?))
}
}
Rv::Error(RvError::AttributeSensitive) => results.push(AttributeInfo::Sensitive),
Rv::Error(RvError::AttributeTypeInvalid) => {
results.push(AttributeInfo::TypeInvalid)
}
rv => rv.into_result(Function::GetAttributeValue)?,
}
}
Ok(results)
}
pub fn get_attribute_info_map(
&self,
object: ObjectHandle,
attributes: &[AttributeType],
) -> Result<HashMap<AttributeType, AttributeInfo>> {
let attrib_info = self.get_attribute_info(object, attributes)?;
Ok(attributes
.iter()
.cloned()
.zip(attrib_info.iter().cloned())
.collect::<HashMap<_, _>>())
}
pub fn get_attributes(
&self,
object: ObjectHandle,
attributes: &[AttributeType],
) -> Result<Vec<Attribute>> {
let mut buffers: Vec<Vec<u8>> = Vec::with_capacity(attributes.len());
let mut template1: Vec<CK_ATTRIBUTE> = Vec::with_capacity(attributes.len());
for attr_type in attributes.iter() {
if let Some(size) = attr_type.fixed_size() {
let buffer = vec![0u8; size];
template1.push(CK_ATTRIBUTE {
type_: (*attr_type).into(),
pValue: as_cptr!(buffer),
ulValueLen: size as CK_ULONG,
});
buffers.push(buffer);
} else {
template1.push(CK_ATTRIBUTE {
type_: (*attr_type).into(),
pValue: std::ptr::null_mut(),
ulValueLen: 0,
});
buffers.push(Vec::new());
}
}
let rv = unsafe {
Rv::from(get_pkcs11!(self.client(), C_GetAttributeValue)(
self.handle(),
object.handle(),
template1.as_mut_ptr(),
template1.len().try_into()?,
))
};
match rv {
Rv::Ok
| Rv::Error(RvError::BufferTooSmall)
| Rv::Error(RvError::AttributeSensitive)
| Rv::Error(RvError::AttributeTypeInvalid) => {
}
_ => {
rv.into_result(Function::GetAttributeValue)?;
}
}
let mut pass1_indices: Vec<usize> = Vec::new();
let mut pass2_indices: Vec<usize> = Vec::new();
for (i, attr) in template1.iter().enumerate() {
if attr.ulValueLen == CK_UNAVAILABLE_INFORMATION {
continue;
} else if attr.pValue.is_null() {
pass2_indices.push(i);
} else {
if attr.ulValueLen > buffers[i].len() as CK_ULONG {
pass2_indices.push(i);
} else {
pass1_indices.push(i);
}
}
}
let pass2_template_and_indices: Option<(Vec<CK_ATTRIBUTE>, Vec<usize>)> =
if pass2_indices.is_empty() {
None
} else {
let mut template2: Vec<CK_ATTRIBUTE> = Vec::with_capacity(pass2_indices.len());
for &idx in pass2_indices.iter() {
let size = template1[idx].ulValueLen as usize;
buffers[idx].resize(size, 0);
template2.push(CK_ATTRIBUTE {
type_: template1[idx].type_,
pValue: as_cptr!(buffers[idx]),
ulValueLen: buffers[idx].len() as CK_ULONG,
});
}
let rv = unsafe {
Rv::from(get_pkcs11!(self.client(), C_GetAttributeValue)(
self.handle(),
object.handle(),
template2.as_mut_ptr(),
template2.len().try_into()?,
))
};
match rv {
Rv::Ok
| Rv::Error(RvError::AttributeSensitive)
| Rv::Error(RvError::AttributeTypeInvalid) => {
}
_ => {
rv.into_result(Function::GetAttributeValue)?;
}
}
for (i, &idx) in pass2_indices.iter().enumerate() {
if template2[i].ulValueLen != CK_UNAVAILABLE_INFORMATION {
pass1_indices.push(idx);
}
}
Some((template2, pass2_indices))
};
pass1_indices.sort_unstable();
let mut results = Vec::new();
for &idx in pass1_indices.iter() {
let attr = if let Some((ref template2, ref indices2)) = pass2_template_and_indices {
if let Some(pos) = indices2.iter().position(|&i| i == idx) {
template2[pos].try_into()?
} else {
template1[idx].try_into()?
}
} else {
template1[idx].try_into()?
};
results.push(attr);
}
Ok(results)
}
pub fn update_attributes(&self, object: ObjectHandle, template: &[Attribute]) -> Result<()> {
let mut template: Vec<CK_ATTRIBUTE> = template.iter().map(|attr| attr.into()).collect();
unsafe {
Rv::from(get_pkcs11!(self.client(), C_SetAttributeValue)(
self.handle(),
object.handle(),
template.as_mut_ptr(),
template.len().try_into()?,
))
.into_result(Function::SetAttributeValue)?;
}
Ok(())
}
}