diff --git a/cryptoki/src/context/mod.rs b/cryptoki/src/context/mod.rs index 8a3ad076..8facff6e 100644 --- a/cryptoki/src/context/mod.rs +++ b/cryptoki/src/context/mod.rs @@ -34,7 +34,6 @@ use log::error; use std::fmt; use std::path::Path; use std::ptr; -use std::sync::Arc; use std::sync::RwLock; /// Enum for various function lists @@ -50,8 +49,12 @@ enum FunctionList { V3_2(cryptoki_sys::CK_FUNCTION_LIST_3_2), } -// Implementation of Pkcs11 class that can be enclosed in a single Arc -pub(crate) struct Pkcs11Impl { +/// Implementation of Pkcs11 class that can be enclosed in a single [`std::sync::Arc`]. +/// +/// This object never finalizes the underlying PKCS#11 implementation. +/// For most cases it is advisable to use [`Pkcs11`][`crate::context::Pkcs11`] object instead, which implements proper lifecycle functions. +/// For advanced use cases, where the PKCS#11 lifecycle is managed externally use [`Pkcs11Impl::new_unchecked`]. +pub struct Pkcs11Impl { // Even if this field is never read, it is needed for the pointers in function_list to remain // valid. _pkcs11_lib: cryptoki_sys::Pkcs11, @@ -76,11 +79,79 @@ impl Pkcs11Impl { } } + /// Initializes Pkcs11 using raw Pkcs11 object. + /// + /// The caller is responsible for ensuring the validity of `pkcs11_lib` object and managing its lifecycle (e.g. calling `finalize`). + /// For most use cases it is advisable to use [`crate::context::Pkcs11`] instead. + /// + /// # Safety + /// + /// `pkcs11_lib` must point to a valid Pkcs11 object. + pub unsafe fn new_unchecked(pkcs11_lib: cryptoki_sys::Pkcs11) -> Result { + /* First try the 3.* API to get default interface. It might have some more functions than + * the 2.40 API */ + let mut interface: *mut cryptoki_sys::CK_INTERFACE = ptr::null_mut(); + if pkcs11_lib.C_GetInterface.is_ok() { + Rv::from(pkcs11_lib.C_GetInterface( + ptr::null_mut(), + ptr::null_mut(), + &mut interface, + 0, + )) + .into_result(Function::GetInterface)?; + if !interface.is_null() { + let ifce: cryptoki_sys::CK_INTERFACE = *interface; + + let list_ptr: *mut cryptoki_sys::CK_FUNCTION_LIST = + ifce.pFunctionList as *mut cryptoki_sys::CK_FUNCTION_LIST; + let list: cryptoki_sys::CK_FUNCTION_LIST = *list_ptr; + if list.version.major >= 3 { + if list.version.minor >= 2 { + let list32_ptr: *mut cryptoki_sys::CK_FUNCTION_LIST_3_2 = + ifce.pFunctionList as *mut cryptoki_sys::CK_FUNCTION_LIST_3_2; + return Ok(Pkcs11Impl { + _pkcs11_lib: pkcs11_lib, + function_list: FunctionList::V3_2(*list32_ptr), + }); + } + let list30_ptr: *mut cryptoki_sys::CK_FUNCTION_LIST_3_0 = + ifce.pFunctionList as *mut cryptoki_sys::CK_FUNCTION_LIST_3_0; + return Ok(Pkcs11Impl { + _pkcs11_lib: pkcs11_lib, + function_list: FunctionList::V3_0(v30tov32(*list30_ptr)), + }); + } + /* fall back to the 2.* API */ + } + } + + let mut list_ptr: *mut cryptoki_sys::CK_FUNCTION_LIST = ptr::null_mut(); + Rv::from(pkcs11_lib.C_GetFunctionList(&mut list_ptr)) + .into_result(Function::GetFunctionList)?; + + Ok(Pkcs11Impl { + _pkcs11_lib: pkcs11_lib, + function_list: FunctionList::V2(v2tov3(*list_ptr)), + }) + } +} + +/// Main PKCS11 context. Should usually be unique per application. +/// +/// After this object goes out of scope the `Finalize` function of the PKCS#11 implementation will be called. +#[derive(Debug)] +pub struct Pkcs11 { + pub(crate) impl_: Pkcs11Impl, + initialized: RwLock, +} + +impl Pkcs11 { // Private finalize call #[inline(always)] - fn finalize(&self) -> Result<()> { + fn finalize_ref(&self) -> Result<()> { unsafe { Rv::from(self + .impl_ .get_function_list() .C_Finalize .ok_or(Error::NullFunctionPointer)?( @@ -91,21 +162,14 @@ impl Pkcs11Impl { } } -impl Drop for Pkcs11Impl { +impl Drop for Pkcs11 { fn drop(&mut self) { - if let Err(e) = self.finalize() { + if let Err(e) = self.finalize_ref() { error!("Failed to finalize: {}", e); } } } -/// Main PKCS11 context. Should usually be unique per application. -#[derive(Clone, Debug)] -pub struct Pkcs11 { - pub(crate) impl_: Arc, - initialized: Arc>, -} - impl Pkcs11 { /// Instantiate a new context from the path of a PKCS11 dynamic library implementation. pub fn new

(filename: P) -> Result @@ -115,7 +179,10 @@ impl Pkcs11 { unsafe { let pkcs11_lib = cryptoki_sys::Pkcs11::new(filename.as_ref()).map_err(Error::LibraryLoading)?; - Self::_new(pkcs11_lib) + Ok(Self { + impl_: Pkcs11Impl::new_unchecked(pkcs11_lib)?, + initialized: RwLock::new(false), + }) } } @@ -127,74 +194,16 @@ impl Pkcs11 { #[cfg(windows)] let this_lib = libloading::os::windows::Library::this()?; let pkcs11_lib = cryptoki_sys::Pkcs11::from_library(this_lib)?; - Self::_new(pkcs11_lib) - } - } - - unsafe fn _new(pkcs11_lib: cryptoki_sys::Pkcs11) -> Result { - /* First try the 3.* API to get default interface. It might have some more functions than - * the 2.40 API */ - let mut interface: *mut cryptoki_sys::CK_INTERFACE = ptr::null_mut(); - if pkcs11_lib.C_GetInterface.is_ok() { - Rv::from(pkcs11_lib.C_GetInterface( - ptr::null_mut(), - ptr::null_mut(), - &mut interface, - 0, - )) - .into_result(Function::GetInterface)?; - if !interface.is_null() { - let ifce: cryptoki_sys::CK_INTERFACE = *interface; - - let list_ptr: *mut cryptoki_sys::CK_FUNCTION_LIST = - ifce.pFunctionList as *mut cryptoki_sys::CK_FUNCTION_LIST; - let list: cryptoki_sys::CK_FUNCTION_LIST = *list_ptr; - if list.version.major >= 3 { - if list.version.minor >= 2 { - let list32_ptr: *mut cryptoki_sys::CK_FUNCTION_LIST_3_2 = - ifce.pFunctionList as *mut cryptoki_sys::CK_FUNCTION_LIST_3_2; - return Ok(Pkcs11 { - impl_: Arc::new(Pkcs11Impl { - _pkcs11_lib: pkcs11_lib, - function_list: FunctionList::V3_2(*list32_ptr), - }), - initialized: Arc::new(RwLock::new(false)), - }); - } - let list30_ptr: *mut cryptoki_sys::CK_FUNCTION_LIST_3_0 = - ifce.pFunctionList as *mut cryptoki_sys::CK_FUNCTION_LIST_3_0; - return Ok(Pkcs11 { - impl_: Arc::new(Pkcs11Impl { - _pkcs11_lib: pkcs11_lib, - function_list: FunctionList::V3_0(v30tov32(*list30_ptr)), - }), - initialized: Arc::new(RwLock::new(false)), - }); - } - /* fall back to the 2.* API */ - } + Ok(Self { + impl_: Pkcs11Impl::new_unchecked(pkcs11_lib)?, + initialized: RwLock::new(false), + }) } - - let mut list_ptr: *mut cryptoki_sys::CK_FUNCTION_LIST = ptr::null_mut(); - Rv::from(pkcs11_lib.C_GetFunctionList(&mut list_ptr)) - .into_result(Function::GetFunctionList)?; - - Ok(Pkcs11 { - impl_: Arc::new(Pkcs11Impl { - _pkcs11_lib: pkcs11_lib, - function_list: FunctionList::V2(v2tov3(*list_ptr)), - }), - initialized: Arc::new(RwLock::new(false)), - }) } /// Initialize the PKCS11 library pub fn initialize(&self, init_args: CInitializeArgs) -> Result<()> { - let mut init_lock = self - .initialized - .as_ref() - .write() - .expect("lock not to be poisoned"); + let mut init_lock = self.initialized.write().expect("lock not to be poisoned"); if *init_lock { Err(Error::AlreadyInitialized)? } @@ -203,11 +212,7 @@ impl Pkcs11 { /// Check whether the PKCS11 library has been initialized pub fn is_initialized(&self) -> bool { - *self - .initialized - .as_ref() - .read() - .expect("lock not to be poisoned") + *self.initialized.read().expect("lock not to be poisoned") } /// Finalize the PKCS11 library. Indicates that the application no longer needs to use PKCS11. diff --git a/cryptoki/src/context/session_management.rs b/cryptoki/src/context/session_management.rs index 382c45da..3edc85e1 100644 --- a/cryptoki/src/context/session_management.rs +++ b/cryptoki/src/context/session_management.rs @@ -13,7 +13,7 @@ use super::Function; impl Pkcs11 { #[inline(always)] - fn open_session(&self, slot_id: Slot, read_write: bool) -> Result { + fn open_session(&self, slot_id: Slot, read_write: bool) -> Result> { let mut session_handle = 0; let flags = if read_write { @@ -33,7 +33,7 @@ impl Pkcs11 { .into_result(Function::OpenSession)?; } - Ok(Session::new(session_handle, self.clone())) + Ok(Session::new(session_handle, self)) } /// Open a new Read-Only session @@ -60,14 +60,14 @@ impl Pkcs11 { /// let session = client.open_ro_session(slot)?; /// # let _ = session; Ok(()) } /// ``` - pub fn open_ro_session(&self, slot_id: Slot) -> Result { + pub fn open_ro_session(&self, slot_id: Slot) -> Result> { self.open_session(slot_id, false) } /// Open a new Read/Write session /// /// Note: No callback is set when opening the session. - pub fn open_rw_session(&self, slot_id: Slot) -> Result { + pub fn open_rw_session(&self, slot_id: Slot) -> Result> { self.open_session(slot_id, true) } } diff --git a/cryptoki/src/session/decryption.rs b/cryptoki/src/session/decryption.rs index 4017091b..8d4f2514 100644 --- a/cryptoki/src/session/decryption.rs +++ b/cryptoki/src/session/decryption.rs @@ -10,7 +10,7 @@ use crate::session::Session; use cryptoki_sys::*; use std::convert::TryInto; -impl Session { +impl Session<'_> { /// Single-part decryption operation pub fn decrypt( &self, diff --git a/cryptoki/src/session/digesting.rs b/cryptoki/src/session/digesting.rs index 615ec89d..2079d6e5 100644 --- a/cryptoki/src/session/digesting.rs +++ b/cryptoki/src/session/digesting.rs @@ -10,7 +10,7 @@ use crate::session::Session; use cryptoki_sys::*; use std::convert::TryInto; -impl Session { +impl Session<'_> { /// Single-part digesting operation pub fn digest(&self, m: &Mechanism, data: &[u8]) -> Result> { let mut mechanism: CK_MECHANISM = m.into(); diff --git a/cryptoki/src/session/encapsulation.rs b/cryptoki/src/session/encapsulation.rs index 842fa1af..1610b30c 100644 --- a/cryptoki/src/session/encapsulation.rs +++ b/cryptoki/src/session/encapsulation.rs @@ -10,7 +10,7 @@ use crate::session::Session; use cryptoki_sys::*; use std::convert::TryInto; -impl Session { +impl Session<'_> { /// Encapsulate key pub fn encapsulate_key( &self, diff --git a/cryptoki/src/session/encryption.rs b/cryptoki/src/session/encryption.rs index 7aabb1e9..66cff3fd 100644 --- a/cryptoki/src/session/encryption.rs +++ b/cryptoki/src/session/encryption.rs @@ -10,7 +10,7 @@ use crate::session::Session; use cryptoki_sys::*; use std::convert::TryInto; -impl Session { +impl Session<'_> { /// Single-part encryption operation pub fn encrypt( &self, diff --git a/cryptoki/src/session/key_management.rs b/cryptoki/src/session/key_management.rs index 0d5dfd13..689076b4 100644 --- a/cryptoki/src/session/key_management.rs +++ b/cryptoki/src/session/key_management.rs @@ -10,7 +10,7 @@ use crate::session::Session; use cryptoki_sys::{CK_ATTRIBUTE, CK_MECHANISM, CK_MECHANISM_PTR}; use std::convert::TryInto; -impl Session { +impl Session<'_> { /// Generate a secret key pub fn generate_key( &self, diff --git a/cryptoki/src/session/message_decryption.rs b/cryptoki/src/session/message_decryption.rs index cd86db2f..4d71de88 100644 --- a/cryptoki/src/session/message_decryption.rs +++ b/cryptoki/src/session/message_decryption.rs @@ -10,7 +10,7 @@ use crate::session::Session; use cryptoki_sys::*; use std::convert::TryInto; -impl Session { +impl Session<'_> { /// Prepare a session for one or more Message-based decryption using the same mechanism and key pub fn message_decrypt_init(&self, mechanism: &Mechanism, key: ObjectHandle) -> Result<()> { let mut mechanism: CK_MECHANISM = mechanism.into(); diff --git a/cryptoki/src/session/message_encryption.rs b/cryptoki/src/session/message_encryption.rs index 1808f8e0..70638039 100644 --- a/cryptoki/src/session/message_encryption.rs +++ b/cryptoki/src/session/message_encryption.rs @@ -10,7 +10,7 @@ use crate::session::Session; use cryptoki_sys::*; use std::convert::TryInto; -impl Session { +impl Session<'_> { /// Prepare a session for one or more Message-based encryption using the same mechanism and key pub fn message_encrypt_init(&self, mechanism: &Mechanism, key: ObjectHandle) -> Result<()> { let mut mechanism: CK_MECHANISM = mechanism.into(); diff --git a/cryptoki/src/session/mod.rs b/cryptoki/src/session/mod.rs index d71dab05..ab518101 100644 --- a/cryptoki/src/session/mod.rs +++ b/cryptoki/src/session/mod.rs @@ -34,37 +34,33 @@ pub use validation::ValidationFlagsType; /// threads. A Session needs to be created in its own thread or to be passed by ownership to /// another thread. #[derive(Debug)] -pub struct Session { +pub struct Session<'a> { handle: CK_SESSION_HANDLE, - client: Pkcs11, + client: &'a Pkcs11, // This is not used but to prevent Session to automatically implement Send and Sync _guard: PhantomData<*mut u32>, } -impl std::fmt::Display for Session { +impl<'a> std::fmt::Display for Session<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.handle) } } -impl std::fmt::LowerHex for Session { +impl<'a> std::fmt::LowerHex for Session<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{:08x}", self.handle) } } -impl std::fmt::UpperHex for Session { +impl<'a> std::fmt::UpperHex for Session<'a> { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{:08X}", self.handle) } } -// Session does not implement Sync to prevent the same Session instance to be used from multiple -// threads. -unsafe impl Send for Session {} - -impl Session { - pub(crate) fn new(handle: CK_SESSION_HANDLE, client: Pkcs11) -> Self { +impl<'a> Session<'a> { + pub(crate) fn new(handle: CK_SESSION_HANDLE, client: &'a Pkcs11) -> Self { Session { handle, client, @@ -73,7 +69,7 @@ impl Session { } } -impl Session { +impl<'a> Session<'a> { /// Close a session /// This will be called on drop as well. pub fn close(self) {} @@ -84,7 +80,7 @@ impl Session { } pub(crate) fn client(&self) -> &Pkcs11 { - &self.client + self.client } } diff --git a/cryptoki/src/session/object_management.rs b/cryptoki/src/session/object_management.rs index e5cf7582..5cb6eec9 100644 --- a/cryptoki/src/session/object_management.rs +++ b/cryptoki/src/session/object_management.rs @@ -78,7 +78,7 @@ const MAX_OBJECT_COUNT: NonZeroUsize = unsafe { NonZeroUsize::new_unchecked(10) /// ``` #[derive(Debug)] pub struct ObjectHandleIterator<'a> { - session: &'a Session, + session: &'a Session<'a>, object_count: usize, index: usize, cache: Vec, @@ -207,7 +207,7 @@ impl Drop for ObjectHandleIterator<'_> { } } -impl Session { +impl Session<'_> { /// Iterate over session objects matching a template. /// /// # Arguments diff --git a/cryptoki/src/session/random.rs b/cryptoki/src/session/random.rs index 4926933b..2269fcbb 100644 --- a/cryptoki/src/session/random.rs +++ b/cryptoki/src/session/random.rs @@ -7,7 +7,7 @@ use crate::error::{Result, Rv}; use crate::session::Session; use std::convert::TryInto; -impl Session { +impl Session<'_> { /// Generates a random number and sticks it in a slice /// /// # Arguments diff --git a/cryptoki/src/session/session_management.rs b/cryptoki/src/session/session_management.rs index c5f22b03..ce498217 100644 --- a/cryptoki/src/session/session_management.rs +++ b/cryptoki/src/session/session_management.rs @@ -14,7 +14,7 @@ use log::error; use secrecy::ExposeSecret; use std::convert::{TryFrom, TryInto}; -impl Drop for Session { +impl Drop for Session<'_> { fn drop(&mut self) { #[inline(always)] fn close(session: &Session) -> Result<()> { @@ -32,7 +32,7 @@ impl Drop for Session { } } -impl Session { +impl Session<'_> { /// Log a session in. /// /// # Arguments diff --git a/cryptoki/src/session/signing_macing.rs b/cryptoki/src/session/signing_macing.rs index 0927c5c4..d4ea1157 100644 --- a/cryptoki/src/session/signing_macing.rs +++ b/cryptoki/src/session/signing_macing.rs @@ -10,7 +10,7 @@ use crate::session::Session; use cryptoki_sys::*; use std::convert::TryInto; -impl Session { +impl Session<'_> { /// Sign data in single-part pub fn sign(&self, mechanism: &Mechanism, key: ObjectHandle, data: &[u8]) -> Result> { let mut mechanism: CK_MECHANISM = mechanism.into(); diff --git a/cryptoki/src/session/slot_token_management.rs b/cryptoki/src/session/slot_token_management.rs index 4dde294b..4983fac1 100644 --- a/cryptoki/src/session/slot_token_management.rs +++ b/cryptoki/src/session/slot_token_management.rs @@ -9,7 +9,7 @@ use crate::types::AuthPin; use secrecy::ExposeSecret; use std::convert::TryInto; -impl Session { +impl Session<'_> { /// Initialize the normal user's pin for a token pub fn init_pin(&self, pin: &AuthPin) -> Result<()> { unsafe { diff --git a/cryptoki/src/session/validation.rs b/cryptoki/src/session/validation.rs index 69db3984..74f324e6 100644 --- a/cryptoki/src/session/validation.rs +++ b/cryptoki/src/session/validation.rs @@ -46,7 +46,7 @@ impl From for CK_SESSION_VALIDATION_FLAGS_TYPE { } } -impl Session { +impl Session<'_> { /// Get requested validation flags from the session /// /// The only supported flag as for PKCS#11 3.2 is `ValidationFlagsType::VALIDATION_OK` diff --git a/cryptoki/tests/basic.rs b/cryptoki/tests/basic.rs index 927d56d4..a6c48ac7 100644 --- a/cryptoki/tests/basic.rs +++ b/cryptoki/tests/basic.rs @@ -25,6 +25,7 @@ use cryptoki::types::{AuthPin, Ulong}; use serial_test::serial; use std::collections::HashMap; use std::num::NonZeroUsize; +use std::sync::Arc; use std::thread; use cryptoki::mechanism::ekdf::AesCbcDeriveParams; @@ -1466,6 +1467,7 @@ fn login_feast() { const SESSIONS: usize = 100; let (pkcs11, slot) = init_pins(); + let pkcs11 = Arc::new(pkcs11); let mut threads = Vec::new(); for _ in 0..SESSIONS { @@ -1802,7 +1804,7 @@ fn is_initialized_test() { fn test_clone_initialize() { use cryptoki::context::CInitializeArgs; - let pkcs11 = get_pkcs11(); + let pkcs11 = Arc::new(get_pkcs11()); let clone = pkcs11.clone(); assert!(