// Copyright 2023 Simo Sorce // See LICENSE.txt file for terms //! This module defines the `Token` structure, which represents a PKCS#11 token. //! It manages token information, login state, object storage interaction, //! object handles, session objects, and access to cryptographic facilities //! (mechanisms and object factories). use std::borrow::Cow; use std::collections::HashMap; use std::vec::Vec; use crate::attribute::CkAttrs; use crate::config::ObjectsDedup; use crate::defaults; use crate::error::Result; #[cfg(feature = "fips")] use crate::fips; use crate::mechanism::Mechanisms; use crate::misc::copy_sized_string; use crate::object::{Object, ObjectFactories, ObjectType}; use crate::pkcs11::vendor::KRY_UNSPEC; use crate::pkcs11::*; use crate::register_all; use crate::storage::*; use bimap; /// Manages the mapping between persistent internal object UIDs (String) and /// temporary object handles (`CK_OBJECT_HANDLE`) assigned during runtime. /// Also responsible for generating new unique handles. #[derive(Debug)] pub struct Handles { /// Bidirectional map for handle <-> UID lookups. map: bimap::hash::BiHashMap, /// The next available object handle ID. next: CK_OBJECT_HANDLE, } impl Handles { /// Creates a new, empty handle manager. pub fn new() -> Handles { Handles { map: bimap::hash::BiHashMap::new(), next: 1, } } /// Inserts a new handle-UID mapping. /// Fails if the handle already exists. pub fn insert( &mut self, handle: CK_OBJECT_HANDLE, value: String, ) -> Result<()> { match self.map.insert_no_overwrite(handle, value) { Ok(()) => Ok(()), Err(_) => Err(CKR_GENERAL_ERROR)?, } } /// Gets the UID (String) associated with a given handle. pub fn get(&self, handle: CK_OBJECT_HANDLE) -> Option<&String> { self.map.get_by_left(&handle) } /// Gets the handle associated with a given UID (String). pub fn get_by_uid(&self, uid: &String) -> Option<&CK_OBJECT_HANDLE> { self.map.get_by_right(uid) } /// Removes a mapping by handle. pub fn remove(&mut self, handle: CK_OBJECT_HANDLE) { let _ = self.map.remove_by_left(&handle); } /// Returns the next available unique handle ID and increments the internal /// counter. Note: This may eventually wrap around, but the handle space /// is large. Collisions are checked during insertion. pub fn next(&mut self) -> CK_OBJECT_HANDLE { let next = self.next; self.next += 1; next } } /// Container for token-wide cryptographic facilities like mechanism /// implementations, object factories, and the handle manager. #[derive(Debug)] pub struct TokenFacilities { /// Registry of available cryptographic mechanisms. pub mechanisms: Mechanisms, /// Registry of available object factories. pub factories: ObjectFactories, /// Manager for object handles and UIDs. pub handles: Handles, } /// Represents a PKCS#11 Token, managing its state, information, storage, /// objects, and cryptographic facilities. #[derive(Debug)] pub struct Token { /// Static token information (label, serial, flags, etc.). info: CK_TOKEN_INFO, /// Cryptographic mechanisms, object factories, and handle manager. facilities: TokenFacilities, /// Interface to the persistent storage backend (e.g., database). storage: Box, /// Map of session objects created during the lifetime of sessions. session_objects: HashMap, /// Current login state (SO, User, or None). logged: CK_USER_TYPE, /// Filtered list of mechanisms, if any filtermechs: Option, dedup: Option, } impl Token { /// Creates a new Token instance. /// /// Initializes storage based on `dbtype` and `dbargs`, registers all /// cryptographic mechanisms and object factories, and attempts to open /// the storage to load existing token information. pub fn new(dbtype: &str, dbargs: Option) -> Result { let mut token: Token = Token { info: CK_TOKEN_INFO { label: [b' '; 32], manufacturerID: [b' '; 32], model: [b' '; 16], serialNumber: [b' '; 16], flags: CKF_RNG, ulMaxSessionCount: CK_EFFECTIVELY_INFINITE, ulSessionCount: 0, ulMaxRwSessionCount: CK_EFFECTIVELY_INFINITE, ulRwSessionCount: 0, ulMaxPinLen: CK_EFFECTIVELY_INFINITE, ulMinPinLen: 8, ulTotalPublicMemory: CK_UNAVAILABLE_INFORMATION, ulFreePublicMemory: CK_UNAVAILABLE_INFORMATION, ulTotalPrivateMemory: CK_UNAVAILABLE_INFORMATION, ulFreePrivateMemory: CK_UNAVAILABLE_INFORMATION, hardwareVersion: defaults::hardware_version(), firmwareVersion: defaults::firmware_version(), utcTime: *b"0000000000000000", }, facilities: TokenFacilities { mechanisms: Mechanisms::new(), factories: ObjectFactories::new(), handles: Handles::new(), }, storage: new_storage(dbtype, &dbargs)?, session_objects: HashMap::new(), logged: KRY_UNSPEC, filtermechs: None, dedup: None, }; /* register mechanisms and factories */ register_all( &mut token.facilities.mechanisms, &mut token.facilities.factories, ); match token.storage.open() { Ok(info) => token.fill_token_info(&info), Err(err) => match err.rv() { CKR_CRYPTOKI_NOT_INITIALIZED => { token.uninitialized_token_info(); } _ => return Err(err), }, } token.token_internal_objects_init()?; Ok(token) } /// Helper function to set up internal objects whenever the token is /// initialized or re-initialized. This function is meant to provision /// exclusively ephemeral data like internal session objects. fn token_internal_objects_init(&mut self) -> Result<()> { // Add FIPS Validation objects #[cfg(feature = "fips")] { insert_builtin_object( self, CKO_VALIDATION, fips::FIPS_VALIDATION_OBJ, )?; } // Add Profile Objects #[cfg(feature = "profiles")] { insert_builtin_object(self, CKO_PROFILE, CKP_BASELINE_PROVIDER)?; insert_builtin_object(self, CKO_PROFILE, CKP_EXTENDED_PROVIDER)?; insert_builtin_object(self, CKO_PROFILE, CKP_AUTHENTICATION_TOKEN)?; insert_builtin_object( self, CKO_PROFILE, CKP_PUBLIC_CERTIFICATES_TOKEN, )?; // We do not support *all* mechanisms so keep this off for now // insert_builtin_object(self, CKO_PROFILE, CKP_COMPLETE_PROVIDER)?; #[cfg(feature = "hkdf")] insert_builtin_object(self, CKO_PROFILE, CKP_HKDF_TLS_TOKEN)?; } // Generate Mechanism Objects for each known Mechanism for mech in self.get_mechs_list() { insert_builtin_object(self, CKO_MECHANISM, mech)?; } Ok(()) } /// Updates the internal `CK_TOKEN_INFO` from the storage backend's /// information. fn fill_token_info(&mut self, info: &StorageTokenInfo) { self.info.label = info.label; self.info.manufacturerID = info.manufacturer; self.info.model = info.model; self.info.serialNumber = info.serial; self.info.flags = info.flags | CKF_RNG; } /// Set the token info for uninitialized tokens fn uninitialized_token_info(&mut self) { self.fill_token_info(&StorageTokenInfo::default()); self.info.flags &= !CKF_TOKEN_INITIALIZED } /// Returns a reference to the token's information structure. pub fn get_token_info(&self) -> &CK_TOKEN_INFO { &self.info } /// Returns `true` if the token has been initialized /// (CKF_TOKEN_INITIALIZED flag is set). pub fn is_initialized(&self) -> bool { self.info.flags & CKF_TOKEN_INITIALIZED == CKF_TOKEN_INITIALIZED } /// Initializes the token. /// /// Requires SO authentication if already initialized. Clears existing /// objects and handles. Reinitializes storage, sets the SO PIN (if supported /// by storage), stores the new token label, and updates token flags. pub fn initialize(&mut self, pin: &[u8], label: &[u8]) -> Result<()> { if self.is_initialized() { self.auth_user(CKU_SO, pin, true)?; }; self.facilities.handles = Handles::new(); self.session_objects.clear(); self.logged = KRY_UNSPEC; /* this inits from scratch or deletes and reinits an existing db */ let mut info = self.storage.reinit(&self.facilities)?; /* Add SO PIN */ match self.set_pin(CKU_SO, pin, &[]) { Ok(()) => (), Err(e) => { /* not all storage dbs support setting a CKU_SO Pin */ if e.rv() != CKR_USER_TYPE_INVALID { return Err(e); } } } /* copy Label */ copy_sized_string(label, &mut info.label); /* save token info with provided label */ self.storage.store_token_info(&info)?; /* copy info on Token object */ self.fill_token_info(&info); /* IMPORTANT: we always forcibly unauth here (A reinit * creates the token as if CKU_SO was logged in in oreder * to properly store data and set PINs). * The caller must ensure authentication after a reset * to be able to correctly access the database. */ self.storage.unauth_user(CKU_SO)?; self.token_internal_objects_init() } /// Sets or changes the PIN for a given user type (SO or User). /// /// If changing an existing PIN (`old.len() != 0`), it first authenticates /// the user with the old PIN. Updates the user's PIN in storage and sets /// the `CKF_USER_PIN_INITIALIZED` flag if setting the User PIN. pub fn set_pin( &mut self, user_type: CK_USER_TYPE, pin: &[u8], old: &[u8], ) -> Result<()> { let utype = match user_type { CK_UNAVAILABLE_INFORMATION => self.logged, CKU_USER => CKU_USER, CKU_SO => CKU_SO, _ => return Err(CKR_GENERAL_ERROR)?, }; if old.len() != 0 { self.auth_user(utype, old, true)?; } self.storage.set_user_pin(&self.facilities, utype, pin)?; if utype == CKU_USER { self.info.flags |= CKF_USER_PIN_INITIALIZED; } Ok(()) } /// Checks if the specified user type (or any user) is currently logged in. pub fn is_logged_in(&self, user_type: CK_USER_TYPE) -> bool { match user_type { KRY_UNSPEC => self.logged == CKU_SO || self.logged == CKU_USER, CKU_SO => self.logged == CKU_SO, CKU_USER => self.logged == CKU_USER, _ => false, } } /// Updates token flags related to PIN status (locked, final try, count low). fn update_auth_flags(&mut self, user_type: CK_USER_TYPE, flags: CK_FLAGS) { match user_type { CKU_USER => { self.info.flags &= !(CKF_USER_PIN_LOCKED | CKF_USER_PIN_FINAL_TRY | CKF_USER_PIN_COUNT_LOW); self.info.flags |= flags; } CKU_SO => { self.info.flags &= !(CKF_SO_PIN_LOCKED | CKF_SO_PIN_FINAL_TRY | CKF_SO_PIN_COUNT_LOW); self.info.flags |= flags; } _ => (), } } /// Authenticates a user with the storage backend. /// /// If `check_only` is false, successfully authenticating also updates the /// token's `logged` state. Updates PIN status flags based on the result /// from the storage backend. Returns `Ok(())` on success, or an error /// (e.g., `CKR_PIN_INCORRECT`, `CKR_PIN_LOCKED`) on failure. fn auth_user( &mut self, user_type: CK_USER_TYPE, pin: &[u8], check_only: bool, ) -> Result<()> { if user_type != CKU_SO && user_type != CKU_USER { return Err(CKR_USER_TYPE_INVALID)?; } let mut flags: CK_FLAGS = 0; let ret = self.storage.auth_user( &self.facilities, user_type, pin, &mut flags, check_only, ); self.update_auth_flags(user_type, flags); if ret.is_err() { return ret; } if !check_only { self.logged = user_type; } Ok(()) } /// Attempts to log in a user (SO or User). /// /// Checks if the user type is valid and if another user is already logged /// in. Calls `auth_user` to perform the authentication against storage. /// Returns a `CK_RV` code indicating the result (e.g., `CKR_OK`, /// `CKR_USER_ALREADY_LOGGED_IN`, `CKR_PIN_INCORRECT`). pub fn login(&mut self, user_type: CK_USER_TYPE, pin: &[u8]) -> CK_RV { let result = match user_type { CKU_SO | CKU_USER => { if user_type == self.logged { return CKR_USER_ALREADY_LOGGED_IN; } if self.logged != KRY_UNSPEC { return CKR_USER_ANOTHER_ALREADY_LOGGED_IN; } self.auth_user(user_type, pin, false) } CKU_CONTEXT_SPECIFIC => self.auth_user(self.logged, pin, true), _ => return CKR_USER_TYPE_INVALID, }; match result { Ok(()) => CKR_OK, Err(e) => e.rv(), } } /// Logs out the currently logged-in user. /// /// Clears private session objects, updates the `logged` state to /// `KRY_UNSPEC`, and informs the storage backend. pub fn logout(&mut self) -> CK_RV { match self.logged { KRY_UNSPEC => CKR_USER_NOT_LOGGED_IN, CKU_SO | CKU_USER => { self.clear_private_session_objects(); let user_type = self.logged; self.logged = KRY_UNSPEC; if self.storage.unauth_user(user_type).is_err() { return CKR_GENERAL_ERROR; } CKR_OK } _ => CKR_GENERAL_ERROR, } } /// Flushes any buffered changes in the storage backend to persistent /// storage. pub fn save(&mut self) -> Result<()> { self.storage.flush() } /// Removes all private session objects from the in-memory map and /// handle manager. fn clear_private_session_objects(&mut self) { let mut priv_handles = Vec::::new(); for (handle, obj) in self.session_objects.iter() { if obj.get_session() == CK_INVALID_HANDLE { /* skip builtin objects identified by an invalid * session handle */ continue; } if obj.is_private() { priv_handles.push(*handle); } } /* remove all private session objects */ for handle in priv_handles { let _ = self.session_objects.remove(&handle); self.facilities.handles.remove(handle); } } /// Removes all session objects associated with a specific session handle /// from the in-memory map and handle manager. fn clear_session_objects(&mut self, session: CK_SESSION_HANDLE) { /* intentionally matches only valid handles, as we use invalid * handles in some places to preserve special internal objects * like validation objects in FIPS mode */ let mut handles: Vec = Vec::new(); for (_, obj) in self.session_objects.iter() { if obj.get_session() == session { handles.push(obj.get_handle()); } } for h in handles { let _ = self.session_objects.remove(&h); self.facilities.handles.remove(h); } } /// Drops all session objects associated with a specific session handle. pub fn drop_session_objects(&mut self, handle: CK_SESSION_HANDLE) { self.clear_session_objects(handle); } /// Sets a filtered mechanisms list with only the mechanism listed pub fn set_mech_allow_list(&mut self, list: Vec) { self.filtermechs = Some(self.facilities.mechanisms.filter_copy(|k| list.contains(k))); } /// Sets a filtered mechanisms list with iall but the mechanism listed pub fn set_mech_deny_list(&mut self, list: Vec) { self.filtermechs = Some( self.facilities .mechanisms .filter_copy(|k| !list.contains(k)), ); } /// Returns the number of mechanisms supported by the token. pub fn get_mechs_num(&self) -> usize { let mechanisms = self.get_mechanisms(); mechanisms.len() } /// Returns a list of all mechanism types supported by the token. pub fn get_mechs_list(&self) -> Vec { let mechanisms = self.get_mechanisms(); mechanisms.list() } /// Gets the `CK_MECHANISM_INFO` for a specific mechanism type. /// Returns `CKR_MECHANISM_INVALID` if the type is not supported. pub fn get_mech_info( &self, typ: CK_MECHANISM_TYPE, ) -> Result<&CK_MECHANISM_INFO> { let mechanisms = self.get_mechanisms(); match mechanisms.info(typ) { Some(m) => Ok(m), None => Err(CKR_MECHANISM_INVALID)?, } } /// Gets a reference to the token's mechanism registry. pub fn get_mechanisms(&self) -> &Mechanisms { if let Some(m) = &self.filtermechs { return m; } &self.facilities.mechanisms } /// Gets a reference to the token's object factory registry. pub fn get_object_factories(&self) -> &ObjectFactories { &self.facilities.factories } /// Retrieves an object by its handle. /// /// Checks the session object cache first, then falls back to fetching /// from storage. Performs login checks for private token objects. /// Marks sensitive objects for zeroization on drop. Returns /// `CKR_OBJECT_HANDLE_INVALID` if not found or not accessible. pub fn get_object_by_handle( &mut self, o_handle: CK_OBJECT_HANDLE, ) -> Result { let mut obj = match self.session_objects.get(&o_handle) { Some(o) => o.clone(), None => self.storage.fetch(&self.facilities, o_handle, &[])?, }; if !self.is_logged_in(KRY_UNSPEC) && obj.is_token() && obj.is_private() { return Err(CKR_USER_NOT_LOGGED_IN)?; } if obj.is_sensitive() { obj.set_zeroize() } Ok(obj) } /// Sets dedup option pub fn set_dedup(&mut self, dedup: Option) { self.dedup = dedup; } /// Inserts a new object into the token. /// /// If the object has `CKA_TOKEN=true`, it is stored persistently via the /// storage backend. Otherwise (session object), it's added to the in-memory /// session object map and assigned a new handle. /// Requires login to store token objects. /// /// Returns the assigned handle. pub fn insert_object( &mut self, s_handle: CK_SESSION_HANDLE, mut obj: Object, ) -> Result { let handle: CK_OBJECT_HANDLE; if obj.is_token() { if !self.is_logged_in(KRY_UNSPEC) { return Err(CKR_USER_NOT_LOGGED_IN)?; } handle = self.storage.store(&mut self.facilities, obj)?; } else { handle = self.facilities.handles.next(); obj.set_handle(handle); obj.set_session(s_handle); let uid = obj.get_attr_as_string(CKA_UNIQUE_ID)?; self.facilities.handles.insert(handle, uid)?; self.session_objects.insert(handle, obj); } Ok(handle) } /// Internal function to check for cert/trust object duplicates fn _is_duplicate( &mut self, template: &[CK_ATTRIBUTE], ) -> Result> { let r = self.storage.search(&mut self.facilities, template)?; return match r.len() { 0 => Ok(None), 1 => Ok(Some(r[0])), _ => Err(CKR_GENERAL_ERROR)?, }; } fn _is_cert_obj_duplicate( &mut self, obj: &Object, ) -> Result> { if let (Some(issuer), Some(serial)) = (obj.get_attr(CKA_ISSUER), obj.get_attr(CKA_SERIAL_NUMBER)) { let class = obj.get_class(); let mut tmpl = CkAttrs::with_capacity(3); tmpl.add_ulong(CKA_CLASS, &class); tmpl.add_slice(CKA_ISSUER, issuer.get_value())?; tmpl.add_slice(CKA_SERIAL_NUMBER, serial.get_value())?; return self._is_duplicate(tmpl.as_slice()); } return Ok(None); } /// Internal function to check for key object duplicates fn _is_key_obj_duplicate( &mut self, obj: &Object, ) -> Result> { if let (Some(id), Some(key_type)) = (obj.get_attr(CKA_ID), obj.get_attr(CKA_KEY_TYPE)) { let class = obj.get_class(); let mut tmpl = CkAttrs::with_capacity(3); tmpl.add_ulong(CKA_CLASS, &class); tmpl.add_slice(CKA_ID, id.get_value())?; tmpl.add_slice(CKA_KEY_TYPE, key_type.get_value())?; return self._is_duplicate(tmpl.as_slice()); } return Ok(None); } /// The dedup option is set generally for softokn compatibility, /// in which case if a "duplicate object" is added to the db, /// the original object data is wiped and replaced with the /// new object data. /// if empty, only check that no duplicate trust object is /// being created as the spec says tokens should not allow to /// create multiple trust objects for the same certificate. fn check_dedup( &mut self, obj: &Object, ) -> Result> { let class = obj.get_class(); let is_cert = class == CKO_CERTIFICATE; let is_key = matches!(class, CKO_PUBLIC_KEY | CKO_PRIVATE_KEY | CKO_SECRET_KEY); #[cfg(not(feature = "nssdb"))] let is_trust = class == CKO_TRUST; #[cfg(feature = "nssdb")] let is_trust = matches!( class, CKO_TRUST | crate::pkcs11::vendor::nss::CKO_NSS_TRUST ); if let Some(dedup) = self.dedup { if let Some(handle) = match dedup { ObjectsDedup::TrustOnly => { if is_trust { self._is_cert_obj_duplicate(obj)? } else { None } } ObjectsDedup::TrustAndCertificates => { if is_cert || is_trust { self._is_cert_obj_duplicate(obj)? } else { None } } ObjectsDedup::All => { if is_cert || is_trust { self._is_cert_obj_duplicate(obj)? } else if is_key { self._is_key_obj_duplicate(obj)? } else { None } } } { let mut updt = CkAttrs::from_attributes(obj.get_attributes())?; let _ = updt.remove_attr(CKA_UNIQUE_ID); self.storage.update( &self.facilities, handle, updt.as_slice(), )?; return Ok(Some(handle)); } } else { if is_trust { let found = self._is_cert_obj_duplicate(obj)?; if found.is_some() { return Err(CKR_ATTRIBUTE_VALUE_INVALID)?; } } } Ok(None) } /// Creates a new object based on a template. /// /// Dispatches to the appropriate object factory via /// [ObjectFactories::create] and then inserts the created object using /// [Self::insert_object]. /// /// Returns the handle of the newly created object. pub fn create_object( &mut self, s_handle: CK_SESSION_HANDLE, template: &[CK_ATTRIBUTE], ) -> Result { let object = self.facilities.factories.create(template)?; if object.is_token() { if !self.is_logged_in(KRY_UNSPEC) { return Err(CKR_USER_NOT_LOGGED_IN)?; } if let Some(handle) = self.check_dedup(&object)? { return Ok(handle); } } self.insert_object(s_handle, object) } /// Destroys an object identified by its handle. /// /// Checks if the object is destroyable (`CKA_DESTROYABLE`). Removes the /// object from the session cache or persistent storage and from the handle /// map. pub fn destroy_object(&mut self, o_handle: CK_OBJECT_HANDLE) -> Result<()> { match self.session_objects.get(&o_handle) { Some(obj) => { if !obj.is_destroyable() { return Err(CKR_ACTION_PROHIBITED)?; } let _ = self.session_objects.remove(&o_handle); } None => { let destroyable: CK_ATTRIBUTE = CK_ATTRIBUTE { type_: CKA_DESTROYABLE, pValue: std::ptr::null_mut(), ulValueLen: 0, }; let obj = self.storage.fetch( &self.facilities, o_handle, &[destroyable], )?; if !obj.is_destroyable() { return Err(CKR_ACTION_PROHIBITED)?; } let _ = self.storage.remove(&self.facilities, o_handle); } } self.facilities.handles.remove(o_handle); Ok(()) } /// Retrieves specified attributes for an object identified by its handle. /// /// Checks session cache then storage. Performs login checks. Uses the /// object factory to determine sensitivity and validity of requested /// attributes. /// /// Fills the provided template array. pub fn get_object_attrs( &mut self, o_handle: CK_OBJECT_HANDLE, template: &mut [CK_ATTRIBUTE], ) -> Result<()> { let is_logged = self.is_logged_in(KRY_UNSPEC); /* value does not matter, only type does */ let dnm: CK_BBOOL = CK_FALSE; let mut attrs = CkAttrs::from(template); if !is_logged { attrs.add_bool(CKA_TOKEN, &dnm); attrs.add_bool(CKA_PRIVATE, &dnm); } let obj = match self.session_objects.get(&o_handle) { Some(o) => Cow::Borrowed(o), None => Cow::Owned(self.storage.fetch( &self.facilities, o_handle, attrs.as_slice(), )?), }; if !is_logged && obj.is_token() && obj.is_private() { /* do not reveal if the object exists or not */ return Err(CKR_OBJECT_HANDLE_INVALID)?; } drop(attrs); self.facilities .factories .get_object_attributes(&obj, template) } /// Modifies attributes of an existing object. /// /// Checks if the object is modifiable. Locates the object (session or /// token) and uses the appropriate object factory to validate and apply /// the changes defined in the template. Updates storage if it's a token /// object. pub fn set_object_attrs( &mut self, o_handle: CK_OBJECT_HANDLE, template: &[CK_ATTRIBUTE], ) -> Result<()> { match self.session_objects.get_mut(&o_handle) { Some(mut obj) => self .facilities .factories .get_object_factory(obj)? .set_object_attributes(&mut obj, template), None => { if !self.is_logged_in(KRY_UNSPEC) { return Err(CKR_USER_NOT_LOGGED_IN)?; } /* We just need the object type to find the correct factory */ /* value does not matter, only type does */ let dnmu = CK_UNAVAILABLE_INFORMATION; let dnmb = CK_FALSE; let mut attrs = CkAttrs::with_capacity(3); attrs.add_ulong(CKA_CLASS, &dnmu); attrs.add_ulong(CKA_KEY_TYPE, &dnmu); attrs.add_bool(CKA_MODIFIABLE, &dnmb); let obj = self.storage.fetch( &self.facilities, o_handle, attrs.as_slice(), )?; let factory = self.facilities.factories.get_object_factory(&obj)?; if !obj.is_modifiable() { return Err(CKR_ACTION_PROHIBITED)?; } factory.check_set_attributes(template)?; self.storage.update(&self.facilities, o_handle, template) } } } /// Estimates the memory size of an object (primarily for C_GetInfo /// reporting). Currently provides a rough estimate based on attribute /// count and value sizes. pub fn get_object_size(&self, o_handle: CK_OBJECT_HANDLE) -> Result { let obj = match self.session_objects.get(&o_handle) { Some(o) => Cow::Borrowed(o), _ => Cow::Owned(self.storage.fetch( &self.facilities, o_handle, &[], )?), }; obj.rough_size() } /// Creates a copy of an object. /// /// Checks if the original object is copyable (`CKA_COPYABLE`). Retrieves /// the original object, dispatches to the appropriate object factory's /// `copy` method, and inserts the new copy as a session or token object. pub fn copy_object( &mut self, s_handle: CK_SESSION_HANDLE, o_handle: CK_OBJECT_HANDLE, template: &[CK_ATTRIBUTE], ) -> Result { let obj = match self.session_objects.get_mut(&o_handle) { Some(o) => Cow::Borrowed(o), _ => { let o = self.storage.fetch(&mut self.facilities, o_handle, &[])?; if !self.is_logged_in(KRY_UNSPEC) && o.is_private() { return Err(CKR_USER_NOT_LOGGED_IN)?; } Cow::Owned(o) } }; let newobj = self.facilities.factories.copy(&obj, template)?; self.insert_object(s_handle, newobj) } /// Searches for objects matching a given template. /// /// First searches in-memory session objects, applying sensitivity checks /// if needed. Then, performs a search against the storage backend, /// applying login checks (adding `CKA_PRIVATE=false` to the template if /// not logged in). /// /// Returns a combined vector of matching object handles. pub fn search_objects( &mut self, template: &[CK_ATTRIBUTE], ) -> Result> { let mut tmpl = CkAttrs::from(template); let mut handles = Vec::::new(); /* First add internal session objects */ for (_, o) in &self.session_objects { if o.is_sensitive() { match self.facilities.factories.check_sensitive(o, template) { Err(_) => continue, Ok(()) => (), } } if o.match_template(template) { handles.push(o.get_handle()); } } if !self.is_logged_in(KRY_UNSPEC) { tmpl.add_owned_bool(CKA_PRIVATE, CK_FALSE)?; } /* Then search storage */ let mut storage_handles = self.storage.search(&mut self.facilities, tmpl.as_slice())?; handles.append(&mut storage_handles); Ok(handles) } } /// Helper function to add a built-in object like Profiles and Mechanisms /// /// Note that the 'id' parameter here identifies the object and is also /// used to generate stable CKA_UNIQUE_IDs. /// For CKO_PROFILE objects the id is also the CKA_PROFILE_ID /// For CKO_MECHANISM objects the id is the mechanism id. fn insert_builtin_object( token: &mut Token, class: CK_OBJECT_CLASS, id: CK_ULONG, ) -> Result<()> { let factory = token .get_object_factories() .get_factory(ObjectType::new(class, 0))?; let obj = factory.builtin_create(id)?; /* invalid session handle will prevent it from being removed when * session objects are cleared on session closings */ let _ = token.insert_object(CK_INVALID_HANDLE, obj)?; Ok(()) }