/* -*- 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; static OPEN_SESSIONS: Mutex> = Mutex::new(None); // Helper functions for accessing OPEN_SESSIONS // type SessionSetGuard = MutexGuard<'static, Option>; fn get_open_sessions_guard() -> Result { 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; static SEARCHES: Mutex> = Mutex::new(None); // Helper functions for accessing SEARCHES // type SearchCacheGuard = MutexGuard<'static, Option>; fn get_search_cache_guard() -> Result { 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 { 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 { 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 { 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)); } }