summaryrefslogtreecommitdiffstats
path: root/security/manager/ssl/builtins/src/pkcs11.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--security/manager/ssl/builtins/src/pkcs11.rs1227
1 files changed, 1227 insertions, 0 deletions
diff --git a/security/manager/ssl/builtins/src/pkcs11.rs b/security/manager/ssl/builtins/src/pkcs11.rs
new file mode 100644
index 0000000000..56620f4a68
--- /dev/null
+++ b/security/manager/ssl/builtins/src/pkcs11.rs
@@ -0,0 +1,1227 @@
+/* -*- Mode: rust; rust-indent-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#![allow(non_snake_case)]
+
+use pkcs11_bindings::*;
+use std::slice;
+
+use std::collections::btree_map::Entry;
+use std::collections::{BTreeMap, BTreeSet};
+use std::sync::atomic::{AtomicU32, Ordering};
+use std::sync::{Mutex, MutexGuard};
+
+use crate::internal::{get_attribute, search};
+use crate::internal::{ObjectHandle, Query, SearchResult};
+
+use crate::version::*;
+
+const BUILTINS_VERSION: CK_VERSION = CK_VERSION {
+ major: NSS_BUILTINS_LIBRARY_VERSION_MAJOR,
+ minor: NSS_BUILTINS_LIBRARY_VERSION_MINOR,
+};
+
+const FIRMWARE_VERSION: CK_VERSION = CK_VERSION {
+ major: NSS_BUILTINS_FIRMWARE_VERSION_MAJOR,
+ minor: NSS_BUILTINS_FIRMWARE_VERSION_MINOR,
+};
+
+const CRYPTOKI_VERSION: CK_VERSION = CK_VERSION {
+ major: NSS_BUILTINS_CRYPTOKI_VERSION_MAJOR,
+ minor: NSS_BUILTINS_CRYPTOKI_VERSION_MINOR,
+};
+const HARDWARE_VERSION: CK_VERSION = CK_VERSION {
+ major: NSS_BUILTINS_HARDWARE_VERSION_MAJOR,
+ minor: NSS_BUILTINS_HARDWARE_VERSION_MINOR,
+};
+
+const MANUFACTURER_ID_BYTES: &[u8; 32] = b"Mozilla Foundation ";
+const LIBRARY_DESCRIPTION_BYTES: &[u8; 32] = b"NSS Builtin Object Cryptoki Modu";
+
+const SLOT_COUNT: CK_ULONG = 1;
+const SLOT_ID_ROOTS: CK_SLOT_ID = 1;
+const SLOT_DESCRIPTION_ROOTS_BYTES: &[u8; 64] =
+ b"NSS Builtin Objects ";
+
+const TOKEN_LABEL_ROOTS_BYTES: &[u8; 32] = b"Builtin Object Token ";
+const TOKEN_MODEL_BYTES: &[u8; 16] = b"1 ";
+const TOKEN_SERIAL_NUMBER_BYTES: &[u8; 16] = b"1 ";
+const TOKEN_UTC_TIME: &[u8; 16] = b" ";
+
+#[derive(Debug)]
+struct PK11Error(CK_RV);
+
+// The token assigns session handles using a counter. It would make sense to use a 64 bit counter,
+// as there would then be no risk of exhausting the session handle space. However,
+// CK_SESSION_HANDLE is defined as a C unsigned long, which is a u32 on some platforms.
+//
+// We start the counter at 1 since PKCS#11 reserves 0 to signal an invalid handle
+//
+type SessionHandle = u32;
+static NEXT_HANDLE: AtomicU32 = AtomicU32::new(1);
+
+// The token needs to keep track of which sessions are open.
+//
+type SessionSet = BTreeSet<SessionHandle>;
+static OPEN_SESSIONS: Mutex<Option<SessionSet>> = Mutex::new(None);
+
+// Helper functions for accessing OPEN_SESSIONS
+//
+type SessionSetGuard = MutexGuard<'static, Option<SessionSet>>;
+
+fn get_open_sessions_guard() -> Result<SessionSetGuard, PK11Error> {
+ OPEN_SESSIONS
+ .lock()
+ .map_err(|_| PK11Error(CKR_DEVICE_ERROR))
+}
+
+fn get_open_sessions(guard: &mut SessionSetGuard) -> Result<&mut SessionSet, PK11Error> {
+ let sessions = guard
+ .as_mut()
+ .ok_or(PK11Error(CKR_CRYPTOKI_NOT_INITIALIZED))?;
+ Ok(sessions)
+}
+
+// The token needs to cache search results until the client reads them or closes the session.
+//
+type SearchCache = BTreeMap<SessionHandle, SearchResult>;
+static SEARCHES: Mutex<Option<SearchCache>> = Mutex::new(None);
+
+// Helper functions for accessing SEARCHES
+//
+type SearchCacheGuard = MutexGuard<'static, Option<SearchCache>>;
+
+fn get_search_cache_guard() -> Result<SearchCacheGuard, PK11Error> {
+ SEARCHES.lock().map_err(|_| PK11Error(CKR_DEVICE_ERROR))
+}
+
+fn get_search_cache(guard: &mut SearchCacheGuard) -> Result<&mut SearchCache, PK11Error> {
+ let searches = guard
+ .as_mut()
+ .ok_or(PK11Error(CKR_CRYPTOKI_NOT_INITIALIZED))?;
+ Ok(searches)
+}
+
+fn validate_session(handle: SessionHandle) -> Result<(), PK11Error> {
+ let mut guard = get_open_sessions_guard()?;
+ let sessions = get_open_sessions(&mut guard)?;
+ if sessions.contains(&handle) {
+ return Ok(());
+ }
+ if handle < NEXT_HANDLE.load(Ordering::SeqCst) {
+ Err(PK11Error(CKR_SESSION_CLOSED))
+ } else {
+ // Possible that NEXT_HANDLE wrapped and we should return CKR_SESSION_CLOSED.
+ // But this is best-effort.
+ Err(PK11Error(CKR_SESSION_HANDLE_INVALID))
+ }
+}
+
+// The internal implementation of C_Initialize
+fn initialize() -> Result<(), PK11Error> {
+ {
+ let mut search_cache_guard = get_search_cache_guard()?;
+ if (*search_cache_guard).is_some() {
+ return Err(PK11Error(CKR_CRYPTOKI_ALREADY_INITIALIZED));
+ }
+ *search_cache_guard = Some(SearchCache::default());
+ }
+
+ {
+ let mut session_guard = get_open_sessions_guard()?;
+ if (*session_guard).is_some() {
+ return Err(PK11Error(CKR_CRYPTOKI_ALREADY_INITIALIZED));
+ }
+ *session_guard = Some(SessionSet::default());
+ }
+
+ Ok(())
+}
+
+// The internal implementation of C_Finalize
+fn finalize() -> Result<(), PK11Error> {
+ {
+ let mut guard = get_search_cache_guard()?;
+ // Try to access the search cache to ensure we're initialized.
+ // Returns CKR_CRYPTOKI_NOT_INITIALIZED if we're not.
+ let _ = get_search_cache(&mut guard)?;
+ *guard = None;
+ }
+
+ let mut guard = get_open_sessions_guard()?;
+ let _ = get_open_sessions(&mut guard)?;
+ *guard = None;
+
+ Ok(())
+}
+
+// Internal implementation of C_OpenSession
+fn open_session() -> Result<SessionHandle, PK11Error> {
+ let mut handle = NEXT_HANDLE.fetch_add(1, Ordering::SeqCst);
+ if handle == 0 {
+ // skip handle 0 if the addition wraps
+ handle = NEXT_HANDLE.fetch_add(1, Ordering::SeqCst);
+ }
+
+ let mut guard = get_open_sessions_guard()?;
+ let sessions = get_open_sessions(&mut guard)?;
+ while !sessions.insert(handle) {
+ // this only executes if NEXT_HANDLE wraps while sessions with
+ // small handles are still open.
+ handle = NEXT_HANDLE.fetch_add(1, Ordering::SeqCst);
+ }
+
+ Ok(handle)
+}
+
+// Internal implementation of C_CloseSession
+fn close_session(session: SessionHandle) -> Result<(), PK11Error> {
+ {
+ let mut guard = get_search_cache_guard()?;
+ let searches = get_search_cache(&mut guard)?;
+ searches.remove(&session);
+ }
+
+ {
+ let mut guard = get_open_sessions_guard()?;
+ let sessions = get_open_sessions(&mut guard)?;
+ if sessions.remove(&session) {
+ Ok(())
+ } else if session < NEXT_HANDLE.load(Ordering::SeqCst) {
+ Err(PK11Error(CKR_SESSION_CLOSED))
+ } else {
+ Err(PK11Error(CKR_SESSION_HANDLE_INVALID))
+ }
+ }
+}
+
+// Internal implementation of C_CloseAllSessions
+fn close_all_sessions() -> Result<(), PK11Error> {
+ {
+ let mut guard = get_search_cache_guard()?;
+ let searches = get_search_cache(&mut guard)?;
+ searches.clear();
+ }
+
+ {
+ let mut guard = get_open_sessions_guard()?;
+ let sessions = get_open_sessions(&mut guard)?;
+ sessions.clear();
+ }
+
+ Ok(())
+}
+
+// Internal implementation of C_FindObjectsInit
+fn find_objects_init(session: SessionHandle, query: &Query) -> Result<usize, PK11Error> {
+ validate_session(session)?;
+
+ let results = search(query);
+ let count = results.len();
+
+ let mut guard = get_search_cache_guard()?;
+ let searches = get_search_cache(&mut guard)?;
+ match searches.entry(session) {
+ Entry::Occupied(_) => Err(PK11Error(CKR_OPERATION_ACTIVE)),
+ Entry::Vacant(v) => {
+ v.insert(results);
+ Ok(count)
+ }
+ }
+}
+
+// Internal implementation of C_FindObjects
+fn find_objects(session: SessionHandle, out: &mut [CK_OBJECT_HANDLE]) -> Result<usize, PK11Error> {
+ validate_session(session)?;
+
+ let mut guard = get_search_cache_guard()?;
+ let searches = get_search_cache(&mut guard)?;
+ if let Some(objects) = searches.get_mut(&session) {
+ for (i, out_i) in out.iter_mut().enumerate() {
+ match objects.pop() {
+ Some(object) => *out_i = object.into(),
+ None => return Ok(i),
+ }
+ }
+ Ok(out.len())
+ } else {
+ Ok(0)
+ }
+}
+
+// Internal implementation of C_FindObjectsFinal
+fn find_objects_final(session: SessionHandle) -> Result<(), PK11Error> {
+ validate_session(session)?;
+
+ let mut guard = get_search_cache_guard()?;
+ let searches = get_search_cache(&mut guard)?;
+ searches.remove(&session);
+ Ok(())
+}
+
+extern "C" fn C_Initialize(_pInitArgs: CK_VOID_PTR) -> CK_RV {
+ match initialize() {
+ Ok(_) => CKR_OK,
+ Err(PK11Error(e)) => e,
+ }
+}
+
+extern "C" fn C_Finalize(pReserved: CK_VOID_PTR) -> CK_RV {
+ if !pReserved.is_null() {
+ return CKR_ARGUMENTS_BAD;
+ }
+ match finalize() {
+ Ok(_) => CKR_OK,
+ Err(PK11Error(e)) => e,
+ }
+}
+
+extern "C" fn C_GetInfo(pInfo: CK_INFO_PTR) -> CK_RV {
+ if pInfo.is_null() {
+ return CKR_ARGUMENTS_BAD;
+ }
+ unsafe {
+ *pInfo = CK_INFO {
+ cryptokiVersion: CRYPTOKI_VERSION,
+ manufacturerID: *MANUFACTURER_ID_BYTES,
+ flags: 0,
+ libraryDescription: *LIBRARY_DESCRIPTION_BYTES,
+ libraryVersion: BUILTINS_VERSION,
+ };
+ }
+ CKR_OK
+}
+
+extern "C" fn C_GetSlotList(
+ _tokenPresent: CK_BBOOL,
+ pSlotList: CK_SLOT_ID_PTR,
+ pulCount: CK_ULONG_PTR,
+) -> CK_RV {
+ if pulCount.is_null() {
+ return CKR_ARGUMENTS_BAD;
+ }
+ if !pSlotList.is_null() {
+ if unsafe { *pulCount } < SLOT_COUNT {
+ return CKR_BUFFER_TOO_SMALL;
+ }
+ unsafe {
+ *pSlotList = SLOT_ID_ROOTS;
+ }
+ }
+ unsafe {
+ *pulCount = SLOT_COUNT;
+ }
+ CKR_OK
+}
+
+extern "C" fn C_GetSlotInfo(slotID: CK_SLOT_ID, pInfo: CK_SLOT_INFO_PTR) -> CK_RV {
+ if (slotID != SLOT_ID_ROOTS) || pInfo.is_null() {
+ return CKR_ARGUMENTS_BAD;
+ }
+ unsafe {
+ *pInfo = CK_SLOT_INFO {
+ slotDescription: *SLOT_DESCRIPTION_ROOTS_BYTES,
+ manufacturerID: *MANUFACTURER_ID_BYTES,
+ flags: CKF_TOKEN_PRESENT,
+ hardwareVersion: HARDWARE_VERSION,
+ firmwareVersion: FIRMWARE_VERSION,
+ };
+ }
+ CKR_OK
+}
+
+extern "C" fn C_GetTokenInfo(slotID: CK_SLOT_ID, pInfo: CK_TOKEN_INFO_PTR) -> CK_RV {
+ if (slotID != SLOT_ID_ROOTS) || pInfo.is_null() {
+ return CKR_ARGUMENTS_BAD;
+ }
+ unsafe {
+ *pInfo = CK_TOKEN_INFO {
+ label: *TOKEN_LABEL_ROOTS_BYTES,
+ manufacturerID: *MANUFACTURER_ID_BYTES,
+ model: *TOKEN_MODEL_BYTES,
+ serialNumber: *TOKEN_SERIAL_NUMBER_BYTES,
+ flags: CKF_WRITE_PROTECTED,
+ ulMaxSessionCount: CK_UNAVAILABLE_INFORMATION,
+ ulSessionCount: 0,
+ ulMaxRwSessionCount: CK_UNAVAILABLE_INFORMATION,
+ ulRwSessionCount: 0,
+ ulMaxPinLen: CK_UNAVAILABLE_INFORMATION,
+ ulMinPinLen: CK_UNAVAILABLE_INFORMATION,
+ ulTotalPublicMemory: CK_UNAVAILABLE_INFORMATION,
+ ulFreePublicMemory: CK_UNAVAILABLE_INFORMATION,
+ ulTotalPrivateMemory: CK_UNAVAILABLE_INFORMATION,
+ ulFreePrivateMemory: CK_UNAVAILABLE_INFORMATION,
+ hardwareVersion: HARDWARE_VERSION,
+ firmwareVersion: FIRMWARE_VERSION,
+ utcTime: *TOKEN_UTC_TIME,
+ };
+ }
+ CKR_OK
+}
+
+extern "C" fn C_GetMechanismList(
+ slotID: CK_SLOT_ID,
+ _pMechanismList: CK_MECHANISM_TYPE_PTR,
+ pulCount: CK_ULONG_PTR,
+) -> CK_RV {
+ if slotID != SLOT_ID_ROOTS || pulCount.is_null() {
+ return CKR_ARGUMENTS_BAD;
+ }
+ unsafe {
+ *pulCount = 0;
+ }
+ CKR_OK
+}
+
+extern "C" fn C_GetMechanismInfo(
+ _slotID: CK_SLOT_ID,
+ _type: CK_MECHANISM_TYPE,
+ _pInfo: CK_MECHANISM_INFO_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_InitToken(
+ _slotID: CK_SLOT_ID,
+ _pPin: CK_UTF8CHAR_PTR,
+ _ulPinLen: CK_ULONG,
+ _pLabel: CK_UTF8CHAR_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_InitPIN(
+ _hSession: CK_SESSION_HANDLE,
+ _pPin: CK_UTF8CHAR_PTR,
+ _ulPinLen: CK_ULONG,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_SetPIN(
+ _hSession: CK_SESSION_HANDLE,
+ _pOldPin: CK_UTF8CHAR_PTR,
+ _ulOldLen: CK_ULONG,
+ _pNewPin: CK_UTF8CHAR_PTR,
+ _ulNewLen: CK_ULONG,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_OpenSession(
+ slotID: CK_SLOT_ID,
+ flags: CK_FLAGS,
+ _pApplication: CK_VOID_PTR,
+ _Notify: CK_NOTIFY,
+ phSession: CK_SESSION_HANDLE_PTR,
+) -> CK_RV {
+ if slotID != SLOT_ID_ROOTS || phSession.is_null() {
+ return CKR_ARGUMENTS_BAD;
+ }
+ // [pkcs11-base-v3.0, Section 5.6.1]
+ // For legacy reasons, the CKF_SERIAL_SESSION bit MUST always be set; if a call to
+ // C_OpenSession does not have this bit set, the call should return unsuccessfully with the
+ // error code CKR_SESSION_PARALLEL_NOT_SUPPORTED.
+ if flags & CKF_SERIAL_SESSION == 0 {
+ return CKR_SESSION_PARALLEL_NOT_SUPPORTED;
+ }
+ let session_id = match open_session() {
+ Ok(session_id) => session_id as CK_SESSION_HANDLE,
+ Err(PK11Error(e)) => return e,
+ };
+ unsafe { *phSession = session_id };
+ CKR_OK
+}
+
+extern "C" fn C_CloseSession(hSession: CK_SESSION_HANDLE) -> CK_RV {
+ let session: SessionHandle = match hSession.try_into() {
+ Ok(session) => session,
+ Err(_) => return CKR_SESSION_HANDLE_INVALID,
+ };
+ match close_session(session) {
+ Ok(_) => CKR_OK,
+ Err(PK11Error(e)) => e,
+ }
+}
+
+extern "C" fn C_CloseAllSessions(slotID: CK_SLOT_ID) -> CK_RV {
+ if slotID != SLOT_ID_ROOTS {
+ return CKR_ARGUMENTS_BAD;
+ }
+ match close_all_sessions() {
+ Ok(_) => CKR_OK,
+ Err(PK11Error(e)) => e,
+ }
+}
+
+extern "C" fn C_GetSessionInfo(_hSession: CK_SESSION_HANDLE, _pInfo: CK_SESSION_INFO_PTR) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_GetOperationState(
+ _hSession: CK_SESSION_HANDLE,
+ _pOperationState: CK_BYTE_PTR,
+ _pulOperationStateLen: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_SetOperationState(
+ _hSession: CK_SESSION_HANDLE,
+ _pOperationState: CK_BYTE_PTR,
+ _ulOperationStateLen: CK_ULONG,
+ _hEncryptionKey: CK_OBJECT_HANDLE,
+ _hAuthenticationKey: CK_OBJECT_HANDLE,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_Login(
+ _hSession: CK_SESSION_HANDLE,
+ _userType: CK_USER_TYPE,
+ _pPin: CK_UTF8CHAR_PTR,
+ _ulPinLen: CK_ULONG,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_Logout(_hSession: CK_SESSION_HANDLE) -> CK_RV {
+ CKR_OK
+}
+
+extern "C" fn C_CreateObject(
+ _hSession: CK_SESSION_HANDLE,
+ _pTemplate: CK_ATTRIBUTE_PTR,
+ _ulCount: CK_ULONG,
+ _phObject: CK_OBJECT_HANDLE_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_CopyObject(
+ _hSession: CK_SESSION_HANDLE,
+ _hObject: CK_OBJECT_HANDLE,
+ _pTemplate: CK_ATTRIBUTE_PTR,
+ _ulCount: CK_ULONG,
+ _phNewObject: CK_OBJECT_HANDLE_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_DestroyObject(_hSession: CK_SESSION_HANDLE, _hObject: CK_OBJECT_HANDLE) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_GetObjectSize(
+ _hSession: CK_SESSION_HANDLE,
+ _hObject: CK_OBJECT_HANDLE,
+ _pulSize: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_GetAttributeValue(
+ _hSession: CK_SESSION_HANDLE,
+ hObject: CK_OBJECT_HANDLE,
+ pTemplate: CK_ATTRIBUTE_PTR,
+ ulCount: CK_ULONG,
+) -> CK_RV {
+ if pTemplate.is_null() {
+ return CKR_ARGUMENTS_BAD;
+ }
+
+ let count: usize = match ulCount.try_into() {
+ Ok(count) => count,
+ Err(_) => return CKR_ARGUMENTS_BAD,
+ };
+
+ // C_GetAttributeValue has a session handle parameter because PKCS#11 objects can have
+ // session-bound lifetimes and access controls. We don't have any session objects, and all of
+ // our token objects are public. So there's no good reason to validate the session handle.
+ //
+ //let session: SessionHandle = match hSession.try_into() {
+ // Ok(session) => session,
+ // Err(_) => return CKR_SESSION_HANDLE_INVALID,
+ //};
+ //
+ //if let Err(PK11Error(e)) = validate_session(session) {
+ // return e;
+ //}
+
+ let handle: ObjectHandle = match hObject.try_into() {
+ Ok(handle) => handle,
+ Err(_) => return CKR_OBJECT_HANDLE_INVALID,
+ };
+
+ let attrs: &mut [CK_ATTRIBUTE] = unsafe { slice::from_raw_parts_mut(pTemplate, count) };
+
+ let mut rv = CKR_OK;
+
+ // Handle requests with null pValue fields
+ for attr in attrs.iter_mut().filter(|x| x.pValue.is_null()) {
+ attr.ulValueLen = match get_attribute(attr.type_, &handle) {
+ None => {
+ // [pkcs11-base-v3.0, Section 5.7.5]
+ // 2. [...] if the specified value for the object is invalid (the object does not possess
+ // such an attribute), then the ulValueLen field in that triple is modified to hold the
+ // value CK_UNAVAILABLE_INFORMATION.
+ rv = CKR_ATTRIBUTE_TYPE_INVALID;
+ CK_UNAVAILABLE_INFORMATION
+ }
+ Some(attr) => {
+ // [pkcs11-base-v3.0, Section 5.7.5]
+ // 3. [...] if the pValue field has the value NULL_PTR, then the ulValueLen field is modified
+ // to hold the exact length of the specified attribute for the object.
+ attr.len() as CK_ULONG
+ }
+ }
+ }
+
+ // Handle requests with non-null pValue fields
+ for attr in attrs.iter_mut().filter(|x| !x.pValue.is_null()) {
+ let dst_len: usize = match attr.ulValueLen.try_into() {
+ Ok(dst_len) => dst_len,
+ Err(_) => return CKR_ARGUMENTS_BAD,
+ };
+ attr.ulValueLen = match get_attribute(attr.type_, &handle) {
+ None => {
+ // [pkcs11-base-v3.0, Section 5.7.5]
+ // 2. [...] if the specified value for the object is invalid (the object does not possess
+ // such an attribute), then the ulValueLen field in that triple is modified to hold the
+ // value CK_UNAVAILABLE_INFORMATION.
+ rv = CKR_ATTRIBUTE_TYPE_INVALID;
+ CK_UNAVAILABLE_INFORMATION
+ }
+ Some(src) if dst_len >= src.len() => {
+ // [pkcs11-base-v3.0, Section 5.7.5]
+ // 4. [...] if the length specified in ulValueLen is large enough to hold the value
+ // of the specified attribute for the object, then that attribute is copied into
+ // the buffer located at pValue, and the ulValueLen field is modified to hold
+ // the exact length of the attribute.
+ let dst: &mut [u8] =
+ unsafe { slice::from_raw_parts_mut(attr.pValue as *mut u8, dst_len) };
+ dst[..src.len()].copy_from_slice(src);
+ src.len() as CK_ULONG
+ }
+ _ => {
+ // [pkcs11-base-v3.0, Section 5.7.5]
+ // 5. Otherwise, the ulValueLen field is modified to hold the value
+ // CK_UNAVAILABLE_INFORMATION.
+ rv = CKR_BUFFER_TOO_SMALL;
+ CK_UNAVAILABLE_INFORMATION
+ }
+ };
+ }
+
+ // [pkcs11-base-v3.0, Section 5.7.5]
+ // If case 2 applies to any of the requested attributes, then the call should return the value
+ // CKR_ATTRIBUTE_TYPE_INVALID. If case 5 applies to any of the requested attributes, then the
+ // call should return the value CKR_BUFFER_TOO_SMALL. As usual, if more than one of these
+ // error codes is applicable, Cryptoki may return any of them. Only if none of them applies to
+ // any of the requested attributes will CKR_OK be returned.
+ rv
+}
+
+extern "C" fn C_SetAttributeValue(
+ _hSession: CK_SESSION_HANDLE,
+ _hObject: CK_OBJECT_HANDLE,
+ _pTemplate: CK_ATTRIBUTE_PTR,
+ _ulCount: CK_ULONG,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_FindObjectsInit(
+ hSession: CK_SESSION_HANDLE,
+ pTemplate: CK_ATTRIBUTE_PTR,
+ ulCount: CK_ULONG,
+) -> CK_RV {
+ if pTemplate.is_null() {
+ return CKR_ARGUMENTS_BAD;
+ }
+ let count: usize = match ulCount.try_into() {
+ Ok(count) => count,
+ Err(_) => return CKR_ARGUMENTS_BAD,
+ };
+ let session: SessionHandle = match hSession.try_into() {
+ Ok(session) => session,
+ Err(_) => return CKR_SESSION_HANDLE_INVALID,
+ };
+
+ let raw_attrs: &[CK_ATTRIBUTE] = unsafe { slice::from_raw_parts_mut(pTemplate, count) };
+
+ let mut query: Vec<(CK_ATTRIBUTE_TYPE, &[u8])> = Vec::with_capacity(raw_attrs.len());
+ for attr in raw_attrs {
+ match usize::try_from(attr.ulValueLen) {
+ Ok(len) => query.push((attr.type_, unsafe {
+ slice::from_raw_parts_mut(attr.pValue as *mut u8, len)
+ })),
+ Err(_) => return CKR_ARGUMENTS_BAD,
+ }
+ }
+
+ match find_objects_init(session, &query) {
+ Ok(_) => CKR_OK,
+ Err(PK11Error(e)) => e,
+ }
+}
+
+extern "C" fn C_FindObjects(
+ hSession: CK_SESSION_HANDLE,
+ phObject: CK_OBJECT_HANDLE_PTR,
+ ulMaxObjectCount: CK_ULONG,
+ pulObjectCount: CK_ULONG_PTR,
+) -> CK_RV {
+ if phObject.is_null() || pulObjectCount.is_null() {
+ return CKR_ARGUMENTS_BAD;
+ }
+ let max_object_count: usize = match ulMaxObjectCount.try_into() {
+ Ok(max_object_count) => max_object_count,
+ Err(_) => return CKR_ARGUMENTS_BAD,
+ };
+ let session: SessionHandle = match hSession.try_into() {
+ Ok(session) => session,
+ Err(_) => return CKR_SESSION_HANDLE_INVALID,
+ };
+ let out: &mut [CK_OBJECT_HANDLE] =
+ unsafe { slice::from_raw_parts_mut(phObject, max_object_count) };
+ match find_objects(session, out) {
+ Ok(num_found) => {
+ unsafe { *pulObjectCount = num_found as CK_ULONG };
+ CKR_OK
+ }
+ Err(PK11Error(e)) => e,
+ }
+}
+
+extern "C" fn C_FindObjectsFinal(hSession: CK_SESSION_HANDLE) -> CK_RV {
+ let session: SessionHandle = match hSession.try_into() {
+ Ok(session) => session,
+ Err(_) => return CKR_SESSION_HANDLE_INVALID,
+ };
+ match find_objects_final(session) {
+ Ok(()) => CKR_OK,
+ Err(PK11Error(e)) => e,
+ }
+}
+
+extern "C" fn C_EncryptInit(
+ _hSession: CK_SESSION_HANDLE,
+ _pMechanism: CK_MECHANISM_PTR,
+ _hKey: CK_OBJECT_HANDLE,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_Encrypt(
+ _hSession: CK_SESSION_HANDLE,
+ _pData: CK_BYTE_PTR,
+ _ulDataLen: CK_ULONG,
+ _pEncryptedData: CK_BYTE_PTR,
+ _pulEncryptedDataLen: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_EncryptUpdate(
+ _hSession: CK_SESSION_HANDLE,
+ _pPart: CK_BYTE_PTR,
+ _ulPartLen: CK_ULONG,
+ _pEncryptedPart: CK_BYTE_PTR,
+ _pulEncryptedPartLen: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_EncryptFinal(
+ _hSession: CK_SESSION_HANDLE,
+ _pLastEncryptedPart: CK_BYTE_PTR,
+ _pulLastEncryptedPartLen: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_DecryptInit(
+ _hSession: CK_SESSION_HANDLE,
+ _pMechanism: CK_MECHANISM_PTR,
+ _hKey: CK_OBJECT_HANDLE,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_Decrypt(
+ _hSession: CK_SESSION_HANDLE,
+ _pEncryptedData: CK_BYTE_PTR,
+ _ulEncryptedDataLen: CK_ULONG,
+ _pData: CK_BYTE_PTR,
+ _pulDataLen: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_DecryptUpdate(
+ _hSession: CK_SESSION_HANDLE,
+ _pEncryptedPart: CK_BYTE_PTR,
+ _ulEncryptedPartLen: CK_ULONG,
+ _pPart: CK_BYTE_PTR,
+ _pulPartLen: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_DecryptFinal(
+ _hSession: CK_SESSION_HANDLE,
+ _pLastPart: CK_BYTE_PTR,
+ _pulLastPartLen: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_DigestInit(_hSession: CK_SESSION_HANDLE, _pMechanism: CK_MECHANISM_PTR) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_Digest(
+ _hSession: CK_SESSION_HANDLE,
+ _pData: CK_BYTE_PTR,
+ _ulDataLen: CK_ULONG,
+ _pDigest: CK_BYTE_PTR,
+ _pulDigestLen: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_DigestUpdate(
+ _hSession: CK_SESSION_HANDLE,
+ _pPart: CK_BYTE_PTR,
+ _ulPartLen: CK_ULONG,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_DigestKey(_hSession: CK_SESSION_HANDLE, _hKey: CK_OBJECT_HANDLE) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_DigestFinal(
+ _hSession: CK_SESSION_HANDLE,
+ _pDigest: CK_BYTE_PTR,
+ _pulDigestLen: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_SignInit(
+ _hSession: CK_SESSION_HANDLE,
+ _pMechanism: CK_MECHANISM_PTR,
+ _hKey: CK_OBJECT_HANDLE,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_Sign(
+ _hSession: CK_SESSION_HANDLE,
+ _pData: CK_BYTE_PTR,
+ _ulDataLen: CK_ULONG,
+ _pSignature: CK_BYTE_PTR,
+ _pulSignatureLen: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_SignUpdate(
+ _hSession: CK_SESSION_HANDLE,
+ _pPart: CK_BYTE_PTR,
+ _ulPartLen: CK_ULONG,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_SignFinal(
+ _hSession: CK_SESSION_HANDLE,
+ _pSignature: CK_BYTE_PTR,
+ _pulSignatureLen: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_SignRecoverInit(
+ _hSession: CK_SESSION_HANDLE,
+ _pMechanism: CK_MECHANISM_PTR,
+ _hKey: CK_OBJECT_HANDLE,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_SignRecover(
+ _hSession: CK_SESSION_HANDLE,
+ _pData: CK_BYTE_PTR,
+ _ulDataLen: CK_ULONG,
+ _pSignature: CK_BYTE_PTR,
+ _pulSignatureLen: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_VerifyInit(
+ _hSession: CK_SESSION_HANDLE,
+ _pMechanism: CK_MECHANISM_PTR,
+ _hKey: CK_OBJECT_HANDLE,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_Verify(
+ _hSession: CK_SESSION_HANDLE,
+ _pData: CK_BYTE_PTR,
+ _ulDataLen: CK_ULONG,
+ _pSignature: CK_BYTE_PTR,
+ _ulSignatureLen: CK_ULONG,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_VerifyUpdate(
+ _hSession: CK_SESSION_HANDLE,
+ _pPart: CK_BYTE_PTR,
+ _ulPartLen: CK_ULONG,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_VerifyFinal(
+ _hSession: CK_SESSION_HANDLE,
+ _pSignature: CK_BYTE_PTR,
+ _ulSignatureLen: CK_ULONG,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_VerifyRecoverInit(
+ _hSession: CK_SESSION_HANDLE,
+ _pMechanism: CK_MECHANISM_PTR,
+ _hKey: CK_OBJECT_HANDLE,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_VerifyRecover(
+ _hSession: CK_SESSION_HANDLE,
+ _pSignature: CK_BYTE_PTR,
+ _ulSignatureLen: CK_ULONG,
+ _pData: CK_BYTE_PTR,
+ _pulDataLen: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_DigestEncryptUpdate(
+ _hSession: CK_SESSION_HANDLE,
+ _pPart: CK_BYTE_PTR,
+ _ulPartLen: CK_ULONG,
+ _pEncryptedPart: CK_BYTE_PTR,
+ _pulEncryptedPartLen: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_DecryptDigestUpdate(
+ _hSession: CK_SESSION_HANDLE,
+ _pEncryptedPart: CK_BYTE_PTR,
+ _ulEncryptedPartLen: CK_ULONG,
+ _pPart: CK_BYTE_PTR,
+ _pulPartLen: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_SignEncryptUpdate(
+ _hSession: CK_SESSION_HANDLE,
+ _pPart: CK_BYTE_PTR,
+ _ulPartLen: CK_ULONG,
+ _pEncryptedPart: CK_BYTE_PTR,
+ _pulEncryptedPartLen: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_DecryptVerifyUpdate(
+ _hSession: CK_SESSION_HANDLE,
+ _pEncryptedPart: CK_BYTE_PTR,
+ _ulEncryptedPartLen: CK_ULONG,
+ _pPart: CK_BYTE_PTR,
+ _pulPartLen: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_GenerateKey(
+ _hSession: CK_SESSION_HANDLE,
+ _pMechanism: CK_MECHANISM_PTR,
+ _pTemplate: CK_ATTRIBUTE_PTR,
+ _ulCount: CK_ULONG,
+ _phKey: CK_OBJECT_HANDLE_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_GenerateKeyPair(
+ _hSession: CK_SESSION_HANDLE,
+ _pMechanism: CK_MECHANISM_PTR,
+ _pPublicKeyTemplate: CK_ATTRIBUTE_PTR,
+ _ulPublicKeyAttributeCount: CK_ULONG,
+ _pPrivateKeyTemplate: CK_ATTRIBUTE_PTR,
+ _ulPrivateKeyAttributeCount: CK_ULONG,
+ _phPublicKey: CK_OBJECT_HANDLE_PTR,
+ _phPrivateKey: CK_OBJECT_HANDLE_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_WrapKey(
+ _hSession: CK_SESSION_HANDLE,
+ _pMechanism: CK_MECHANISM_PTR,
+ _hWrappingKey: CK_OBJECT_HANDLE,
+ _hKey: CK_OBJECT_HANDLE,
+ _pWrappedKey: CK_BYTE_PTR,
+ _pulWrappedKeyLen: CK_ULONG_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_UnwrapKey(
+ _hSession: CK_SESSION_HANDLE,
+ _pMechanism: CK_MECHANISM_PTR,
+ _hUnwrappingKey: CK_OBJECT_HANDLE,
+ _pWrappedKey: CK_BYTE_PTR,
+ _ulWrappedKeyLen: CK_ULONG,
+ _pTemplate: CK_ATTRIBUTE_PTR,
+ _ulAttributeCount: CK_ULONG,
+ _phKey: CK_OBJECT_HANDLE_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_DeriveKey(
+ _hSession: CK_SESSION_HANDLE,
+ _pMechanism: CK_MECHANISM_PTR,
+ _hBaseKey: CK_OBJECT_HANDLE,
+ _pTemplate: CK_ATTRIBUTE_PTR,
+ _ulAttributeCount: CK_ULONG,
+ _phKey: CK_OBJECT_HANDLE_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_SeedRandom(
+ _hSession: CK_SESSION_HANDLE,
+ _pSeed: CK_BYTE_PTR,
+ _ulSeedLen: CK_ULONG,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_GenerateRandom(
+ _hSession: CK_SESSION_HANDLE,
+ _RandomData: CK_BYTE_PTR,
+ _ulRandomLen: CK_ULONG,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_GetFunctionStatus(_hSession: CK_SESSION_HANDLE) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_CancelFunction(_hSession: CK_SESSION_HANDLE) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+extern "C" fn C_WaitForSlotEvent(
+ _flags: CK_FLAGS,
+ _pSlot: CK_SLOT_ID_PTR,
+ _pRserved: CK_VOID_PTR,
+) -> CK_RV {
+ CKR_FUNCTION_NOT_SUPPORTED
+}
+
+pub static mut FUNCTION_LIST: CK_FUNCTION_LIST = CK_FUNCTION_LIST {
+ version: CRYPTOKI_VERSION,
+ C_Initialize: Some(C_Initialize),
+ C_Finalize: Some(C_Finalize),
+ C_GetInfo: Some(C_GetInfo),
+ C_GetFunctionList: None,
+ C_GetSlotList: Some(C_GetSlotList),
+ C_GetSlotInfo: Some(C_GetSlotInfo),
+ C_GetTokenInfo: Some(C_GetTokenInfo),
+ C_GetMechanismList: Some(C_GetMechanismList),
+ C_GetMechanismInfo: Some(C_GetMechanismInfo),
+ C_InitToken: Some(C_InitToken),
+ C_InitPIN: Some(C_InitPIN),
+ C_SetPIN: Some(C_SetPIN),
+ C_OpenSession: Some(C_OpenSession),
+ C_CloseSession: Some(C_CloseSession),
+ C_CloseAllSessions: Some(C_CloseAllSessions),
+ C_GetSessionInfo: Some(C_GetSessionInfo),
+ C_GetOperationState: Some(C_GetOperationState),
+ C_SetOperationState: Some(C_SetOperationState),
+ C_Login: Some(C_Login),
+ C_Logout: Some(C_Logout),
+ C_CreateObject: Some(C_CreateObject),
+ C_CopyObject: Some(C_CopyObject),
+ C_DestroyObject: Some(C_DestroyObject),
+ C_GetObjectSize: Some(C_GetObjectSize),
+ C_GetAttributeValue: Some(C_GetAttributeValue),
+ C_SetAttributeValue: Some(C_SetAttributeValue),
+ C_FindObjectsInit: Some(C_FindObjectsInit),
+ C_FindObjects: Some(C_FindObjects),
+ C_FindObjectsFinal: Some(C_FindObjectsFinal),
+ C_EncryptInit: Some(C_EncryptInit),
+ C_Encrypt: Some(C_Encrypt),
+ C_EncryptUpdate: Some(C_EncryptUpdate),
+ C_EncryptFinal: Some(C_EncryptFinal),
+ C_DecryptInit: Some(C_DecryptInit),
+ C_Decrypt: Some(C_Decrypt),
+ C_DecryptUpdate: Some(C_DecryptUpdate),
+ C_DecryptFinal: Some(C_DecryptFinal),
+ C_DigestInit: Some(C_DigestInit),
+ C_Digest: Some(C_Digest),
+ C_DigestUpdate: Some(C_DigestUpdate),
+ C_DigestKey: Some(C_DigestKey),
+ C_DigestFinal: Some(C_DigestFinal),
+ C_SignInit: Some(C_SignInit),
+ C_Sign: Some(C_Sign),
+ C_SignUpdate: Some(C_SignUpdate),
+ C_SignFinal: Some(C_SignFinal),
+ C_SignRecoverInit: Some(C_SignRecoverInit),
+ C_SignRecover: Some(C_SignRecover),
+ C_VerifyInit: Some(C_VerifyInit),
+ C_Verify: Some(C_Verify),
+ C_VerifyUpdate: Some(C_VerifyUpdate),
+ C_VerifyFinal: Some(C_VerifyFinal),
+ C_VerifyRecoverInit: Some(C_VerifyRecoverInit),
+ C_VerifyRecover: Some(C_VerifyRecover),
+ C_DigestEncryptUpdate: Some(C_DigestEncryptUpdate),
+ C_DecryptDigestUpdate: Some(C_DecryptDigestUpdate),
+ C_SignEncryptUpdate: Some(C_SignEncryptUpdate),
+ C_DecryptVerifyUpdate: Some(C_DecryptVerifyUpdate),
+ C_GenerateKey: Some(C_GenerateKey),
+ C_GenerateKeyPair: Some(C_GenerateKeyPair),
+ C_WrapKey: Some(C_WrapKey),
+ C_UnwrapKey: Some(C_UnwrapKey),
+ C_DeriveKey: Some(C_DeriveKey),
+ C_SeedRandom: Some(C_SeedRandom),
+ C_GenerateRandom: Some(C_GenerateRandom),
+ C_GetFunctionStatus: Some(C_GetFunctionStatus),
+ C_CancelFunction: Some(C_CancelFunction),
+ C_WaitForSlotEvent: Some(C_WaitForSlotEvent),
+};
+
+#[no_mangle]
+pub unsafe fn BUILTINSC_GetFunctionList(ppFunctionList: CK_FUNCTION_LIST_PTR_PTR) -> CK_RV {
+ if ppFunctionList.is_null() {
+ return CKR_ARGUMENTS_BAD;
+ }
+ *ppFunctionList = &mut FUNCTION_LIST;
+ CKR_OK
+}
+
+#[cfg(test)]
+mod pkcs11_tests {
+ use crate::certdata::*;
+ use crate::internal::*;
+ use crate::pkcs11::*;
+
+ #[test]
+ fn test_main() {
+ // We need to run tests serially because of C_Initialize / C_Finalize calls.
+ test_simple();
+ test_c_get_function_list();
+ test_c_get_attribute();
+ }
+
+ fn test_simple() {
+ let query = &[(CKA_CLASS, CKO_CERTIFICATE_BYTES)];
+ initialize().expect("initialize should not fail.");
+ let hSession = open_session().expect("open_session should not fail.");
+ let count = find_objects_init(hSession, query).expect("find_objects_init should not fail.");
+ assert_eq!(count, BUILTINS.len());
+ let mut results: [CK_OBJECT_HANDLE; 10] = [0; 10];
+ let n_read =
+ find_objects(hSession, &mut results).expect("find_objects_init should not fail.");
+ assert_eq!(n_read, 10);
+ finalize().expect("finalize should not fail.");
+ }
+
+ fn test_c_get_function_list() {
+ let c_null = 0 as *mut std::ffi::c_void;
+ let mut pFunctionList: CK_FUNCTION_LIST_PTR = c_null as CK_FUNCTION_LIST_PTR;
+ let rv = unsafe { crate::pkcs11::BUILTINSC_GetFunctionList(&mut pFunctionList) };
+ assert_eq!(CKR_OK, rv);
+ if let Some(pC_Initialize) = unsafe { (*pFunctionList).C_Initialize } {
+ let rv = unsafe { pC_Initialize(c_null) };
+ assert_eq!(CKR_OK, rv);
+ } else {
+ assert!(false);
+ }
+
+ if let Some(pC_Finalize) = unsafe { (*pFunctionList).C_Finalize } {
+ let rv = unsafe { pC_Finalize(c_null) };
+ assert_eq!(CKR_OK, rv);
+ } else {
+ assert!(false);
+ }
+ }
+
+ fn test_c_get_attribute() {
+ let c_null = 0 as *mut std::ffi::c_void;
+ let template: &mut [CK_ATTRIBUTE] = &mut [CK_ATTRIBUTE {
+ type_: CKA_SUBJECT,
+ pValue: c_null,
+ ulValueLen: 0,
+ }];
+ let template_ptr = &mut template[0] as CK_ATTRIBUTE_PTR;
+ let object: CK_OBJECT_HANDLE = 2;
+ let mut session: CK_SESSION_HANDLE = 0;
+ assert_eq!(CKR_OK, C_Initialize(c_null));
+ assert_eq!(
+ CKR_OK,
+ C_OpenSession(
+ SLOT_ID_ROOTS,
+ CKF_SERIAL_SESSION,
+ c_null,
+ None,
+ &mut session as *mut CK_SESSION_HANDLE
+ )
+ );
+ assert_eq!(
+ CKR_OK,
+ C_GetAttributeValue(session, object, template_ptr, 1)
+ );
+ let len = template[0].ulValueLen as usize;
+ assert_eq!(len, BUILTINS[0].der_name.len());
+
+ let value: &mut [u8] = &mut vec![0; 1];
+ let value_ptr: *mut u8 = &mut value[0] as *mut u8;
+ template[0].pValue = value_ptr as *mut std::ffi::c_void;
+ template[0].ulValueLen = 1;
+ assert_eq!(
+ CKR_BUFFER_TOO_SMALL,
+ C_GetAttributeValue(session, object, template_ptr, 1)
+ );
+ assert_eq!(template[0].ulValueLen, CK_UNAVAILABLE_INFORMATION);
+
+ let value: &mut [u8] = &mut vec![0; len];
+ let value_ptr: *mut u8 = &mut value[0] as *mut u8;
+ template[0].pValue = value_ptr as *mut std::ffi::c_void;
+ template[0].ulValueLen = len as CK_ULONG;
+ assert_eq!(
+ CKR_OK,
+ C_GetAttributeValue(session, object, template_ptr, 1)
+ );
+ assert_eq!(value, BUILTINS[0].der_name);
+ assert_eq!(CKR_OK, C_Finalize(c_null));
+ }
+}