use dashmap::DashMap;
use parking_lot::RwLock;
use std::sync::Arc;
use crate::error::{HsmError, HsmResult};
use crate::pkcs11_abi::constants::*;
use crate::pkcs11_abi::types::*;
use crate::session::handle::ObjectHandleAllocator;
use crate::store::encrypted_store::EncryptedStore;
use crate::store::key_material::RawKeyMaterial;
use crate::store::object::StoredObject;
const MAX_OBJECTS: usize = 10_000;
const MAX_KEY_MATERIAL_LEN: usize = 8192;
pub struct ObjectStore {
objects: DashMap<CK_OBJECT_HANDLE, Arc<RwLock<StoredObject>>>,
handle_alloc: ObjectHandleAllocator,
persist_store: Option<EncryptedStore>,
persist_key: parking_lot::Mutex<Option<zeroize::Zeroizing<[u8; 32]>>>,
handle_to_store_key: parking_lot::Mutex<std::collections::HashMap<CK_OBJECT_HANDLE, String>>,
}
impl Default for ObjectStore {
fn default() -> Self {
Self::new()
}
}
impl ObjectStore {
pub fn new() -> Self {
Self {
objects: DashMap::new(),
handle_alloc: ObjectHandleAllocator::new(),
persist_store: None,
persist_key: parking_lot::Mutex::new(None),
handle_to_store_key: parking_lot::Mutex::new(std::collections::HashMap::new()),
}
}
pub fn objects_len(&self) -> usize {
self.objects.len()
}
pub fn with_persistence(store: EncryptedStore) -> Self {
Self {
objects: DashMap::new(),
handle_alloc: ObjectHandleAllocator::new(),
persist_store: Some(store),
persist_key: parking_lot::Mutex::new(None),
handle_to_store_key: parking_lot::Mutex::new(std::collections::HashMap::new()),
}
}
fn generate_store_key(&self, handle: CK_OBJECT_HANDLE) -> String {
let mut random_bytes = [0u8; 16];
match crate::crypto::drbg::HmacDrbg::new()
.and_then(|mut drbg| drbg.generate(&mut random_bytes))
{
Ok(()) => {}
Err(e) => {
use rand::rngs::OsRng;
use rand::RngCore;
tracing::warn!(
"store-key DRBG instantiation failed ({:?}); \
falling back to OsRng for opaque store-key generation",
e
);
OsRng.fill_bytes(&mut random_bytes);
}
}
let key = format!("obj_{}", hex::encode(random_bytes));
self.handle_to_store_key.lock().insert(handle, key.clone());
key
}
fn get_store_key(&self, handle: CK_OBJECT_HANDLE) -> String {
let map = self.handle_to_store_key.lock();
if let Some(key) = map.get(&handle) {
return key.clone();
}
drop(map);
self.generate_store_key(handle)
}
pub fn set_persist_key(&self, key: [u8; 32]) {
*self.persist_key.lock() = Some(zeroize::Zeroizing::new(key));
}
pub fn clear_persist_key(&self) {
*self.persist_key.lock() = None;
}
pub fn load_from_store(&self) -> HsmResult<usize> {
let store = match &self.persist_store {
Some(s) => s,
None => return Ok(0),
};
let guard = self.persist_key.lock();
let key = match guard.as_ref() {
Some(k) => k,
None => return Ok(0), };
let keys = store.list_keys()?;
let mut loaded = 0;
for store_key in keys {
if let Some(data) = store.load_encrypted(&store_key, key)? {
let result = crate::store::object_codec::decode(&data);
match result {
Ok(obj) => {
if let Err(reason) = validate_deserialized_object(&obj) {
tracing::warn!(
"Rejected deserialized object '{}': {}",
store_key,
reason
);
continue;
}
let handle = obj.handle;
self.handle_to_store_key.lock().insert(handle, store_key);
self.handle_alloc.ensure_past(handle);
self.objects.insert(handle, Arc::new(RwLock::new(obj)));
loaded += 1;
}
Err(e) => {
tracing::warn!("Failed to deserialize object '{}': {}", store_key, e);
}
}
}
}
Ok(loaded)
}
fn persist_object(&self, obj: &StoredObject) {
if !obj.token_object {
return; }
let store = match &self.persist_store {
Some(s) => s,
None => return,
};
let guard = self.persist_key.lock();
let key = match guard.as_ref() {
Some(k) => k,
None => return, };
let store_key = {
let map = self.handle_to_store_key.lock();
map.get(&obj.handle).cloned()
}
.unwrap_or_else(|| self.generate_store_key(obj.handle));
match crate::store::object_codec::encode(obj) {
Ok(data) => {
let result = store.store_encrypted(&store_key, &data, key);
if let Err(e) = result {
tracing::error!("Failed to persist object {}: {:?}", obj.handle, e);
}
}
Err(e) => {
tracing::error!("Failed to serialize object {}: {}", obj.handle, e);
}
}
}
fn persist_objects(&self, objs: &[StoredObject]) {
let store = match &self.persist_store {
Some(s) => s,
None => return,
};
let guard = self.persist_key.lock();
let key = match guard.as_ref() {
Some(k) => k,
None => return, };
let mut items: Vec<(String, zeroize::Zeroizing<Vec<u8>>)> = Vec::with_capacity(objs.len());
for obj in objs {
if !obj.token_object {
continue;
}
let store_key = {
let map = self.handle_to_store_key.lock();
map.get(&obj.handle).cloned()
}
.unwrap_or_else(|| self.generate_store_key(obj.handle));
match crate::store::object_codec::encode(obj) {
Ok(data) => items.push((store_key, data)),
Err(e) => {
tracing::error!("Failed to serialize object {}: {}", obj.handle, e);
}
}
}
if items.is_empty() {
return;
}
if let Err(e) = store.store_encrypted_batch(&items, key) {
tracing::error!("Failed to persist {} object(s): {:?}", items.len(), e);
}
}
fn unpersist_object(&self, handle: CK_OBJECT_HANDLE) {
let store = match &self.persist_store {
Some(s) => s,
None => return,
};
let store_key = self.get_store_key(handle);
if let Err(e) = store.delete(&store_key) {
tracing::error!("Failed to unpersist object {}: {:?}", handle, e);
}
self.handle_to_store_key.lock().remove(&handle);
}
pub fn create_object(
&self,
template: &[(CK_ATTRIBUTE_TYPE, Vec<u8>)],
) -> HsmResult<CK_OBJECT_HANDLE> {
if self.objects.len() >= MAX_OBJECTS {
return Err(HsmError::DeviceMemory);
}
let class = template
.iter()
.find(|(t, _)| *t == CKA_CLASS)
.and_then(|(_, v)| read_ck_ulong(v))
.ok_or(HsmError::TemplateIncomplete)?;
let handle = self.handle_alloc.next()?;
let mut obj = StoredObject::new(handle, class);
for (attr_type, value) in template {
apply_attribute(&mut obj, *attr_type, value)?;
}
if obj.class == CKO_PRIVATE_KEY
&& obj.key_type == Some(CKK_RSA)
&& obj.key_material.is_none()
&& obj.extra_attributes.contains_key(&CKA_PRIVATE_EXPONENT)
{
let modulus = obj.modulus.as_ref().ok_or(HsmError::TemplateInconsistent)?;
let pub_exp = obj
.public_exponent
.as_ref()
.ok_or(HsmError::TemplateInconsistent)?;
let priv_exp = obj
.extra_attributes
.get(&CKA_PRIVATE_EXPONENT)
.ok_or(HsmError::TemplateInconsistent)?;
let prime1 = obj
.extra_attributes
.get(&CKA_PRIME_1)
.ok_or(HsmError::TemplateInconsistent)?;
let prime2 = obj
.extra_attributes
.get(&CKA_PRIME_2)
.ok_or(HsmError::TemplateInconsistent)?;
use rsa::pkcs8::EncodePrivateKey;
use rsa::{BigUint, RsaPrivateKey};
let n = BigUint::from_bytes_be(modulus);
let e = BigUint::from_bytes_be(pub_exp);
let d = BigUint::from_bytes_be(priv_exp);
let p = BigUint::from_bytes_be(prime1);
let q = BigUint::from_bytes_be(prime2);
let private_key = RsaPrivateKey::from_components(n, e, d, vec![p, q])
.map_err(|_| HsmError::AttributeValueInvalid)?;
private_key
.validate()
.map_err(|_| HsmError::AttributeValueInvalid)?;
let der = private_key
.to_pkcs8_der()
.map_err(|_| HsmError::GeneralError)?;
obj.key_material = Some(RawKeyMaterial::new(der.as_bytes().to_vec()));
if obj.modulus_bits.is_none() {
obj.modulus_bits = Some((modulus.len() * 8) as CK_ULONG);
}
}
self.persist_object(&obj);
self.objects.insert(handle, Arc::new(RwLock::new(obj)));
Ok(handle)
}
pub fn insert_object(&self, obj: StoredObject) -> HsmResult<CK_OBJECT_HANDLE> {
if self.objects.len() >= MAX_OBJECTS {
return Err(HsmError::DeviceMemory);
}
let handle = obj.handle;
self.persist_object(&obj);
self.objects.insert(handle, Arc::new(RwLock::new(obj)));
Ok(handle)
}
pub fn insert_objects(&self, objs: Vec<StoredObject>) -> HsmResult<Vec<CK_OBJECT_HANDLE>> {
if objs.is_empty() {
return Ok(Vec::new());
}
if self.objects.len().saturating_add(objs.len()) > MAX_OBJECTS {
return Err(HsmError::DeviceMemory);
}
let handles: Vec<CK_OBJECT_HANDLE> = objs.iter().map(|o| o.handle).collect();
self.persist_objects(&objs);
for obj in objs {
self.objects.insert(obj.handle, Arc::new(RwLock::new(obj)));
}
Ok(handles)
}
pub fn next_handle(&self) -> HsmResult<CK_OBJECT_HANDLE> {
self.handle_alloc.next()
}
pub fn destroy_object(&self, handle: CK_OBJECT_HANDLE) -> HsmResult<()> {
let removed = self
.objects
.remove_if(&handle, |_k, v| v.read().destroyable);
match removed {
Some(_) => {
self.unpersist_object(handle);
Ok(())
}
None => {
if self.objects.contains_key(&handle) {
Err(HsmError::GeneralError)
} else {
Err(HsmError::ObjectHandleInvalid)
}
}
}
}
pub fn get_object(&self, handle: CK_OBJECT_HANDLE) -> HsmResult<Arc<RwLock<StoredObject>>> {
self.objects
.get(&handle)
.map(|o| o.value().clone())
.ok_or(HsmError::ObjectHandleInvalid)
}
pub fn get_object_size(&self, handle: CK_OBJECT_HANDLE) -> HsmResult<CK_ULONG> {
let obj = self.get_object(handle)?;
let size = obj.read().approximate_size();
Ok(size)
}
pub fn clear(&self) {
self.objects.clear();
self.handle_to_store_key.lock().clear();
if let Some(ref store) = self.persist_store {
if let Err(e) = store.clear() {
tracing::error!("Failed to clear persistent store: {:?}", e);
}
}
}
pub fn clear_and_rotate(&self) -> HsmResult<()> {
self.clear();
let had_key = self.persist_key.lock().is_some();
if !had_key {
return Ok(());
}
let mut new_key = zeroize::Zeroizing::new([0u8; 32]);
let mut drbg = crate::crypto::drbg::HmacDrbg::new().map_err(|e| {
tracing::error!("clear_and_rotate: failed to instantiate DRBG: {:?}", e);
HsmError::GeneralError
})?;
drbg.generate(new_key.as_mut()).map_err(|e| {
tracing::error!("clear_and_rotate: DRBG generate failed: {:?}", e);
HsmError::GeneralError
})?;
*self.persist_key.lock() = Some(zeroize::Zeroizing::new(*new_key));
Ok(())
}
pub fn has_persistence(&self) -> bool {
self.persist_store.is_some()
}
pub fn find_objects(
&self,
template: &[(CK_ATTRIBUTE_TYPE, Vec<u8>)],
is_logged_in: bool,
) -> Vec<CK_OBJECT_HANDLE> {
self.find_objects_for_slot(template, is_logged_in, None)
}
pub fn find_objects_for_slot(
&self,
template: &[(CK_ATTRIBUTE_TYPE, Vec<u8>)],
is_logged_in: bool,
slot_id: Option<CK_ULONG>,
) -> Vec<CK_OBJECT_HANDLE> {
let total = self.objects.len();
let mut results = Vec::with_capacity(total);
for entry in self.objects.iter() {
let obj = entry.value().read();
let matches = obj.matches_template(template);
let visible = !obj.private || is_logged_in;
let slot_ok = slot_id.map_or(true, |sid| obj.slot_id == sid);
if matches && visible && slot_ok {
results.push(*entry.key());
}
}
results
}
pub fn sweep_lifecycle(&self) -> Vec<crate::store::lifecycle::LifecycleTransition> {
let mut transitions = Vec::new();
let mut to_persist: Vec<crate::store::object::StoredObject> = Vec::new();
for entry in self.objects.iter() {
let mut obj = entry.value().write();
if let Some((old_state, new_state)) = obj.transition_lifecycle_if_needed() {
let label = String::from_utf8_lossy(&obj.label).to_string();
transitions.push(crate::store::lifecycle::LifecycleTransition {
handle: obj.handle,
label,
old_state,
new_state,
});
if obj.token_object {
to_persist.push(obj.clone());
}
}
}
for obj_clone in to_persist {
self.persist_object(&obj_clone);
}
transitions
}
pub fn export_all_objects(&self) -> ZeroizingObjects {
let objects: Vec<StoredObject> = self
.objects
.iter()
.map(|entry| entry.value().read().clone())
.collect();
ZeroizingObjects(objects)
}
}
pub fn apply_attribute(
obj: &mut StoredObject,
attr_type: CK_ATTRIBUTE_TYPE,
value: &[u8],
) -> HsmResult<()> {
match attr_type {
CKA_CLASS => {
obj.class = read_ck_ulong(value).ok_or(HsmError::AttributeValueInvalid)?;
}
CKA_KEY_TYPE => {
let key_type = read_ck_ulong(value).ok_or(HsmError::AttributeValueInvalid)?;
obj.key_type = Some(normalize_key_type(key_type));
}
CKA_LABEL => {
obj.label = value.to_vec();
}
CKA_ID => {
obj.id = value.to_vec();
}
CKA_TOKEN => {
obj.token_object = !value.is_empty() && value[0] != 0;
}
CKA_PRIVATE => {
obj.private = !value.is_empty() && value[0] != 0;
}
CKA_SENSITIVE => {
obj.sensitive = !value.is_empty() && value[0] != 0;
}
CKA_EXTRACTABLE => {
obj.extractable = !value.is_empty() && value[0] != 0;
}
CKA_MODIFIABLE => {
obj.modifiable = !value.is_empty() && value[0] != 0;
}
CKA_DESTROYABLE => {
obj.destroyable = !value.is_empty() && value[0] != 0;
}
CKA_ENCRYPT => {
obj.can_encrypt = !value.is_empty() && value[0] != 0;
}
CKA_DECRYPT => {
obj.can_decrypt = !value.is_empty() && value[0] != 0;
}
CKA_SIGN => {
obj.can_sign = !value.is_empty() && value[0] != 0;
}
CKA_VERIFY => {
obj.can_verify = !value.is_empty() && value[0] != 0;
}
CKA_WRAP => {
obj.can_wrap = !value.is_empty() && value[0] != 0;
}
CKA_UNWRAP => {
obj.can_unwrap = !value.is_empty() && value[0] != 0;
}
CKA_DERIVE => {
obj.can_derive = !value.is_empty() && value[0] != 0;
}
CKA_VALUE => {
if value.len() > MAX_KEY_MATERIAL_LEN {
return Err(HsmError::AttributeValueInvalid);
}
if obj.class == CKO_SECRET_KEY && obj.value_len.is_none() {
obj.value_len = Some(value.len() as CK_ULONG);
}
obj.key_material = Some(RawKeyMaterial::new(value.to_vec()));
}
CKA_MODULUS => {
obj.modulus = Some(value.to_vec());
}
CKA_MODULUS_BITS => {
obj.modulus_bits = Some(read_ck_ulong(value).ok_or(HsmError::AttributeValueInvalid)?);
}
CKA_PUBLIC_EXPONENT => {
obj.public_exponent = Some(value.to_vec());
}
CKA_EC_PARAMS => {
obj.ec_params = Some(value.to_vec());
}
CKA_EC_POINT => {
obj.ec_point = Some(crate::store::ec_point::decode(value).to_vec());
}
CKA_VALUE_LEN => {
obj.value_len = Some(read_ck_ulong(value).ok_or(HsmError::AttributeValueInvalid)?);
}
CKA_START_DATE => {
if value.len() == 8 {
let mut date = [0u8; 8];
date.copy_from_slice(value);
obj.start_date = Some(date);
} else if value.is_empty() {
obj.start_date = None;
} else {
return Err(HsmError::GeneralError);
}
}
CKA_END_DATE => {
if value.len() == 8 {
let mut date = [0u8; 8];
date.copy_from_slice(value);
obj.end_date = Some(date);
} else if value.is_empty() {
obj.end_date = None;
} else {
return Err(HsmError::GeneralError);
}
}
_ => {
if value.len() > 8192 {
return Err(HsmError::ArgumentsBad);
}
if obj.extra_attributes.len() >= 64 && !obj.extra_attributes.contains_key(&attr_type) {
return Err(HsmError::ArgumentsBad);
}
obj.extra_attributes.insert(attr_type, value.to_vec());
}
}
Ok(())
}
pub fn modify_attribute(
obj: &mut StoredObject,
attr_type: CK_ATTRIBUTE_TYPE,
value: &[u8],
) -> HsmResult<()> {
if !obj.modifiable {
return Err(HsmError::AttributeReadOnly);
}
match attr_type {
CKA_LABEL => {
obj.label = value.to_vec();
Ok(())
}
CKA_ID => {
obj.id = value.to_vec();
Ok(())
}
CKA_SENSITIVE => {
let new_val = !value.is_empty() && value[0] != 0;
if obj.sensitive && !new_val {
return Err(HsmError::AttributeReadOnly);
}
obj.sensitive = new_val;
Ok(())
}
CKA_EXTRACTABLE => {
let new_val = !value.is_empty() && value[0] != 0;
if !obj.extractable && new_val {
return Err(HsmError::AttributeReadOnly);
}
obj.extractable = new_val;
Ok(())
}
_ => Err(HsmError::AttributeReadOnly),
}
}
pub fn read_attribute(
obj: &StoredObject,
attr_type: CK_ATTRIBUTE_TYPE,
is_logged_in: bool,
) -> Result<Option<Vec<u8>>, HsmError> {
if obj.private && !is_logged_in {
return Err(HsmError::ObjectHandleInvalid);
}
match attr_type {
CKA_CLASS => Ok(Some(ck_ulong_to_bytes(obj.class))),
CKA_KEY_TYPE => Ok(obj.key_type.map(ck_ulong_to_bytes)),
CKA_LABEL => Ok(Some(obj.label.clone())),
CKA_ID => Ok(Some(obj.id.clone())),
CKA_TOKEN => Ok(Some(vec![if obj.token_object { 1 } else { 0 }])),
CKA_PRIVATE => Ok(Some(vec![if obj.private { 1 } else { 0 }])),
CKA_SENSITIVE => Ok(Some(vec![if obj.sensitive { 1 } else { 0 }])),
CKA_EXTRACTABLE => Ok(Some(vec![if obj.extractable { 1 } else { 0 }])),
CKA_MODIFIABLE => Ok(Some(vec![if obj.modifiable { 1 } else { 0 }])),
CKA_DESTROYABLE => Ok(Some(vec![if obj.destroyable { 1 } else { 0 }])),
CKA_COPYABLE => Ok(Some(vec![if obj.copyable { 1 } else { 0 }])),
CKA_ENCRYPT => Ok(Some(vec![if obj.can_encrypt { 1 } else { 0 }])),
CKA_DECRYPT => Ok(Some(vec![if obj.can_decrypt { 1 } else { 0 }])),
CKA_SIGN => Ok(Some(vec![if obj.can_sign { 1 } else { 0 }])),
CKA_VERIFY => Ok(Some(vec![if obj.can_verify { 1 } else { 0 }])),
CKA_WRAP => Ok(Some(vec![if obj.can_wrap { 1 } else { 0 }])),
CKA_UNWRAP => Ok(Some(vec![if obj.can_unwrap { 1 } else { 0 }])),
CKA_DERIVE => Ok(Some(vec![if obj.can_derive { 1 } else { 0 }])),
CKA_VALUE => {
if crate::store::object::is_secret_class(obj.class) && obj.sensitive && !obj.extractable
{
return Err(HsmError::AttributeSensitive);
}
if obj.class == CKO_PUBLIC_KEY && obj.key_material.is_none() && obj.is_pqc_key() {
return Ok(obj.public_key_data.clone());
}
Ok(obj.key_material.as_ref().map(|km| km.as_bytes().to_vec()))
}
CKA_PARAMETER_SET => Ok(obj.parameter_set().map(ck_ulong_to_bytes)),
CKA_MODULUS => Ok(obj.modulus.clone()),
CKA_MODULUS_BITS => Ok(obj.modulus_bits.map(ck_ulong_to_bytes)),
CKA_PUBLIC_EXPONENT => Ok(obj.public_exponent.clone()),
CKA_EC_PARAMS => Ok(obj.ec_params.clone()),
CKA_EC_POINT => Ok(obj
.ec_point
.as_deref()
.map(crate::store::ec_point::encode_der)),
CKA_VALUE_LEN => Ok(obj.value_len.map(ck_ulong_to_bytes)),
CKA_START_DATE => Ok(obj.start_date.map(|d| d.to_vec())),
CKA_END_DATE => Ok(obj.end_date.map(|d| d.to_vec())),
t if crate::store::object::SENSITIVE_COMPONENT_ATTRIBUTES.contains(&t) => {
if obj.class == CKO_PRIVATE_KEY {
if obj.sensitive || !obj.extractable {
return Err(HsmError::AttributeSensitive);
}
Ok(obj.extra_attributes.get(&t).cloned())
} else {
Ok(None)
}
}
_ => Ok(obj.extra_attributes.get(&attr_type).cloned()),
}
}
pub fn read_ck_ulong(bytes: &[u8]) -> Option<CK_ULONG> {
let size = std::mem::size_of::<CK_ULONG>();
if bytes.len() < size {
return None;
}
let mut buf = [0u8; std::mem::size_of::<CK_ULONG>()];
buf.copy_from_slice(&bytes[..size]);
Some(CK_ULONG::from_ne_bytes(buf))
}
fn ck_ulong_to_bytes(val: CK_ULONG) -> Vec<u8> {
val.to_ne_bytes().to_vec()
}
fn validate_deserialized_object(obj: &StoredObject) -> Result<(), &'static str> {
if obj.handle == 0 {
return Err("handle is CK_INVALID_HANDLE (0)");
}
if obj.lifecycle_state == crate::store::object::KeyLifecycleState::Destroyed {
return Err("persisted object has Destroyed lifecycle state");
}
Ok(())
}
pub struct ZeroizingObjects(Vec<StoredObject>);
impl std::ops::Deref for ZeroizingObjects {
type Target = [StoredObject];
fn deref(&self) -> &[StoredObject] {
&self.0
}
}
impl Drop for ZeroizingObjects {
fn drop(&mut self) {
use zeroize::Zeroize;
for obj in self.0.iter_mut() {
obj.label.zeroize();
obj.id.zeroize();
if let Some(ref mut pk) = obj.public_key_data {
pk.zeroize();
}
if let Some(ref mut m) = obj.modulus {
m.zeroize();
}
if let Some(ref mut e) = obj.public_exponent {
e.zeroize();
}
if let Some(ref mut p) = obj.ec_params {
p.zeroize();
}
if let Some(ref mut p) = obj.ec_point {
p.zeroize();
}
for val in obj.extra_attributes.values_mut() {
val.zeroize();
}
}
self.0.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rsa_private_components_sensitivity() {
let mut obj = StoredObject::new(1, CKO_PRIVATE_KEY);
obj.key_type = Some(CKK_RSA);
obj.sensitive = true;
obj.extractable = false;
obj.extra_attributes
.insert(CKA_PRIVATE_EXPONENT, vec![1, 2, 3]);
obj.extra_attributes.insert(CKA_PRIME_1, vec![4, 5, 6]);
assert!(matches!(
read_attribute(&obj, CKA_PRIVATE_EXPONENT, true),
Err(HsmError::AttributeSensitive)
));
assert!(matches!(
read_attribute(&obj, CKA_PRIME_1, true),
Err(HsmError::AttributeSensitive)
));
obj.extractable = true;
assert!(matches!(
read_attribute(&obj, CKA_PRIVATE_EXPONENT, true),
Err(HsmError::AttributeSensitive)
));
obj.sensitive = false;
obj.extractable = true;
assert_eq!(
read_attribute(&obj, CKA_PRIVATE_EXPONENT, true).unwrap(),
Some(vec![1, 2, 3])
);
assert_eq!(
read_attribute(&obj, CKA_PRIME_1, true).unwrap(),
Some(vec![4, 5, 6])
);
}
#[test]
fn rsa_private_key_created_from_components_has_key_material() {
use crate::crypto::drbg::DrbgRng;
use rsa::traits::{PrivateKeyParts, PublicKeyParts};
use rsa::RsaPrivateKey;
let mut rng = DrbgRng::new().unwrap();
let priv_key = RsaPrivateKey::new(&mut rng, 2048).unwrap();
let n = priv_key.n().to_bytes_be();
let e = priv_key.e().to_bytes_be();
let d = priv_key.d().to_bytes_be();
let p = priv_key.primes()[0].to_bytes_be();
let q = priv_key.primes()[1].to_bytes_be();
let store = ObjectStore::new();
let template = vec![
(CKA_CLASS, ck_ulong_to_bytes(CKO_PRIVATE_KEY)),
(CKA_KEY_TYPE, ck_ulong_to_bytes(CKK_RSA)),
(CKA_MODULUS, n.clone()),
(CKA_PUBLIC_EXPONENT, e.clone()),
(CKA_PRIVATE_EXPONENT, d),
(CKA_PRIME_1, p),
(CKA_PRIME_2, q),
(CKA_DECRYPT, vec![CK_TRUE]),
];
let handle = store
.create_object(&template)
.expect("create_object should succeed");
let obj = store.get_object(handle).unwrap();
let obj = obj.read();
assert!(
obj.key_material.is_some(),
"key_material must be populated from components"
);
let priv_der = obj.key_material.as_ref().unwrap().as_bytes();
let pt = b"RSA discrete component decryption test";
let ct = crate::crypto::sign::rsa_oaep_encrypt(
&n,
&e,
pt,
crate::crypto::sign::OaepHash::Sha256,
&[],
)
.unwrap();
let recovered = crate::crypto::sign::rsa_oaep_decrypt(
priv_der,
&ct,
crate::crypto::sign::OaepHash::Sha256,
&[],
)
.unwrap();
assert_eq!(&recovered, pt);
}
}