/* * Copyright (C) 2007 Stef Walter * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include #include "ckcapi.h" #include "ckcapi-object.h" #include "ckcapi-session.h" #include "ckcapi-rsa.h" #include "ckcapi-token.h" /* Warns about all the raw string usage in this file */ #pragma warning (disable : 4996) /* ------------------------------------------------------------------- * GLOBALS / DEFINES */ static int cryptoki_initialized = 0; static HANDLE global_mutex = NULL; #define MANUFACTURER_ID "Cryptoki CAPI " #define LIBRARY_DESCRIPTION "Cryptoki CAPI Provider " #define LIBRARY_VERSION_MAJOR 1 #define LIBRARY_VERSION_MINOR 1 #define HARDWARE_VERSION_MAJOR 0 #define HARDWARE_VERSION_MINOR 0 #define FIRMWARE_VERSION_MAJOR 0 #define FIRMWARE_VERSION_MINOR 0 #define SLOT_TOKEN_SERIAL "1.0 " #define SLOT_TOKEN_MODEL "1.0 " #define MAX_PIN_LEN 256 #define MIN_PIN_LEN 1 static CK_MECHANISM_TYPE all_mechanisms[] = { CKM_RSA_PKCS }; /* ------------------------------------------------------------------- * MODULE GLOBAL FUNCTIONS */ #define LINE 1024 void ckcapi_debug(const char* msg, ...) { char buf[LINE]; va_list va; size_t len; va_start(va, msg); _vsnprintf(buf, 1024, msg, va); va_end(va); buf[LINE - 1] = 0; len = strlen (buf); strncpy(buf + len, "\n", 1024 - len); buf[LINE - 1] = 0; // OutputDebugStringA(buf); } /* Bah humbug, MSVC doesn't have __func__ */ #define ENTER(func) \ char* _func = #func; \ ckcapi_debug("%s: enter", _func) #define RETURN(ret) \ return (ckcapi_debug("%s: %d", _func, ret), ret) #define PREREQ(cond, ret) \ if (!(cond)) { ckcapi_debug("%s: %s failed: %d", _func, #cond, ret); return ret; } void ckcapi_lock_global(void) { DWORD r; ASSERT(global_mutex); r = WaitForSingleObject(global_mutex, INFINITE); ASSERT(r == WAIT_OBJECT_0); } void ckcapi_unlock_global(void) { BOOL r; ASSERT(global_mutex); r = ReleaseMutex(global_mutex); ASSERT(r); } CK_RV ckcapi_winerr_to_ckr(DWORD werr) { switch(werr) { case ERROR_NOT_ENOUGH_MEMORY: case ERROR_OUTOFMEMORY: return CKR_HOST_MEMORY; break; case NTE_NO_MEMORY: return CKR_DEVICE_MEMORY; break; case ERROR_MORE_DATA: return CKR_BUFFER_TOO_SMALL; case ERROR_INVALID_PARAMETER: /* these params were derived from the */ case ERROR_INVALID_HANDLE: /* inputs, so if they are bad, the input */ case NTE_BAD_ALGID: /* data is bad */ case NTE_BAD_HASH: case NTE_BAD_TYPE: case NTE_BAD_PUBLIC_KEY: return CKR_DATA_INVALID; case ERROR_BUSY: case NTE_FAIL: case NTE_BAD_UID: return CKR_DEVICE_ERROR; default: return CKR_GENERAL_ERROR; }; } CK_RV ckcapi_return_data_raw(CK_VOID_PTR output, CK_ULONG_PTR n_output, CK_VOID_PTR input, CK_ULONG n_input) { ASSERT(n_output); ASSERT(input); /* Just asking for the length */ if(!output) { *n_output = n_input; return CKR_OK; } /* Buffer is too short */ if(n_input > *n_output) { *n_output = n_input; return CKR_BUFFER_TOO_SMALL; } *n_output = n_input; memcpy(output, input, n_input); return CKR_OK; } CK_RV ckcapi_return_data(CK_ATTRIBUTE_PTR attr, CK_VOID_PTR input, DWORD n_input) { return ckcapi_return_data_raw(attr->pValue, &(attr->ulValueLen), input, n_input); } CK_RV ckcapi_return_string(CK_ATTRIBUTE_PTR attr, WCHAR* string) { CK_UTF8CHAR_PTR buffer; int result; SetLastError(0); /* * Sadly WideCharToMultiByte doesn't handle zero * length strings properly. So we have to special * case this part. */ if(!string[0]) return ckcapi_return_data(attr, "", 0); /* The length of the string, including null termination */ result = WideCharToMultiByte(CP_UTF8, 0, string, -1, NULL, 0, NULL, NULL); if(result) { /* Did the caller just want the length? */ if(!attr->pValue) { attr->ulValueLen = result - 1; return CKR_OK; } /* Is the callers buffer too short? */ if((int)attr->ulValueLen < result - 1) { attr->ulValueLen = result - 1; return CKR_BUFFER_TOO_SMALL; } /* * Allocate a buffer for the conversion. We have to * do this because strings in PKCS#11 are not null * terminated and strings returned from * WideCharToMultiByte are always null terminated. */ buffer = malloc(result); if(!buffer) return CKR_HOST_MEMORY; /* Do the actual conversion */ result = WideCharToMultiByte(CP_UTF8, 0, string, -1, buffer, result, NULL, NULL); if(result) { attr->ulValueLen = result - 1; memcpy(attr->pValue, buffer, attr->ulValueLen); free(buffer); return CKR_OK; } free(buffer); } /* * We should never have too little buffer, or * get a zero length success code. It's a very * strange error that arrives here. */ return CKR_GENERAL_ERROR; } CK_RV ckcapi_return_dword_as_bytes(CK_ATTRIBUTE_PTR attr, DWORD value) { int i; CK_ULONG count = 0; BOOL first = TRUE; BYTE* at = attr->pValue; CK_RV ret = CKR_OK; for(i = 0; i < sizeof(DWORD); i++) { BYTE digit = (BYTE)((value >> (((sizeof(DWORD)-1)*8))) & 0xFF); value = value << 8; /* No leading zero */ if (first && digit == 0) continue; first = FALSE; if(at) { if(count > attr->ulValueLen) ret = CKR_BUFFER_TOO_SMALL; else *(at++) = digit; } count++; } attr->ulValueLen = count; return ret; } CK_RV ckcapi_return_reversed_data(CK_ATTRIBUTE_PTR attr, CK_VOID_PTR data, CK_ULONG length) { CK_RV ret = ckcapi_return_data(attr, data, length); if(ret != CKR_OK || !attr->pValue) return ret; ckcapi_reverse_memory(attr->pValue, attr->ulValueLen); return CKR_OK; } static void print_zero_decimal(CK_BYTE_PTR buffer, CK_ULONG length, WORD value) { int i; for(i = (int)length - 1; i >= 0; --i) { BYTE digit = value % 10; buffer[i] = '0' + digit; value /= 10; } } CK_RV ckcapi_return_filetime(CK_ATTRIBUTE_PTR attr, FILETIME *ftime) { SYSTEMTIME stime; CK_DATE* date; ASSERT(attr); ASSERT(ftime); if(!attr->pValue) { attr->ulValueLen = sizeof(CK_DATE); return CKR_OK; } if(attr->ulValueLen < sizeof(CK_DATE)) { attr->ulValueLen = sizeof(CK_DATE); return CKR_BUFFER_TOO_SMALL; } if(!FileTimeToSystemTime(ftime, &stime)) { DBG(("An invalid FILETIME was encountered")); return CKR_GENERAL_ERROR; } date = (CK_DATE*)attr->pValue; attr->ulValueLen = sizeof(CK_DATE); print_zero_decimal(date->year, sizeof(date->year), stime.wYear); print_zero_decimal(date->month, sizeof(date->month), stime.wMonth); print_zero_decimal(date->day, sizeof(date->day), stime.wDay); return CKR_OK; } /* ---------------------------------------------------------------- */ static CK_RV CC_C_Initialize(CK_VOID_PTR init_args) { ENTER(C_Initialize); PREREQ(!cryptoki_initialized, CKR_CRYPTOKI_ALREADY_INITIALIZED); if (init_args != NULL) { CK_C_INITIALIZE_ARGS_PTR args; int supplied_ok; /* pReserved must be NULL */ args = init_args; PREREQ(!args->pReserved, CKR_ARGUMENTS_BAD); /* ALL supplied function pointers need to have the value either NULL or non-NULL. */ supplied_ok = (args->CreateMutex == NULL && args->DestroyMutex == NULL && args->LockMutex == NULL && args->UnlockMutex == NULL) || (args->CreateMutex != NULL && args->DestroyMutex != NULL && args->LockMutex != NULL && args->UnlockMutex != NULL); PREREQ(supplied_ok, CKR_ARGUMENTS_BAD); /* * When the CKF_OS_LOCKING_OK flag isn't set and mutex function pointers are supplied * by an application, return an error. We must be able to use our own locks. */ if(!(args->flags & CKF_OS_LOCKING_OK) && (args->CreateMutex != NULL)) RETURN(CKR_CANT_LOCK); } if(!global_mutex) { global_mutex = CreateMutex(NULL, FALSE, NULL); if(!global_mutex) RETURN(CKR_CANT_LOCK); } cryptoki_initialized = 1; RETURN(CKR_OK); } static CK_RV CC_C_Finalize(CK_VOID_PTR pReserved) { ENTER(C_Finalize); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); PREREQ(!pReserved, CKR_ARGUMENTS_BAD); cryptoki_initialized = 0; ckcapi_session_cleanup_all(); ckcapi_token_cleanup_all(); RETURN(CKR_OK); } static CK_RV CC_C_GetInfo(CK_INFO_PTR info) { ENTER(C_GetInfo); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); PREREQ(info, CKR_ARGUMENTS_BAD); ASSERT(strlen(MANUFACTURER_ID) == 32); ASSERT(strlen(LIBRARY_DESCRIPTION) == 32); info->cryptokiVersion.major = CRYPTOKI_VERSION_MAJOR; info->cryptokiVersion.minor = CRYPTOKI_VERSION_MINOR; info->libraryVersion.major = LIBRARY_VERSION_MAJOR; info->libraryVersion.minor = LIBRARY_VERSION_MINOR; info->flags = 0; strncpy((char*)info->manufacturerID, MANUFACTURER_ID, 32); strncpy((char*)info->libraryDescription, LIBRARY_DESCRIPTION, 32); RETURN(CKR_OK); } static CK_RV CC_C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR list) { /* This would be a strange call to receive */ return C_GetFunctionList(list); } static CK_RV CC_C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR slot_list, CK_ULONG_PTR count) { unsigned int n_tokens, i; ENTER(C_GetSlotList); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); PREREQ(count, CKR_ARGUMENTS_BAD); /* All tokens are always present */ n_tokens = ckcapi_token_get_count(); /* Application only wants to know the number of slots. */ if(slot_list == NULL) { *count = n_tokens; RETURN(CKR_OK); } if(*count < n_tokens) { *count = n_tokens; RETURN(CKR_BUFFER_TOO_SMALL); } *count = n_tokens; for(i = 0; i < n_tokens; ++i) slot_list[i] = ckcapi_token_get_slot_id (i); RETURN(CKR_OK); } static CK_RV CC_C_GetSlotInfo(CK_SLOT_ID id, CK_SLOT_INFO_PTR info) { const char* name; ENTER(C_GetSlotInfo); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); PREREQ(info, CKR_ARGUMENTS_BAD); /* Make sure the slot ID is valid */ if(!ckcapi_token_is_valid(id)) RETURN(CKR_SLOT_ID_INVALID); ASSERT(strlen(MANUFACTURER_ID) == 32); /* Provide information about the slot in the provided buffer */ strncpy((char*)info->manufacturerID, MANUFACTURER_ID, 32); info->hardwareVersion.major = HARDWARE_VERSION_MAJOR; info->hardwareVersion.minor = HARDWARE_VERSION_MINOR; info->firmwareVersion.major = FIRMWARE_VERSION_MAJOR; info->firmwareVersion.minor = FIRMWARE_VERSION_MINOR; /* Token is always present */ info->flags = CKF_TOKEN_PRESENT; /* Slot name is blank padded, odd */ name = ckcapi_token_get_display_name(id); memset((char*)info->slotDescription, ' ', sizeof(info->slotDescription)); memcpy((char*)info->slotDescription, name, min(strlen(name), sizeof(info->slotDescription))); RETURN(CKR_OK); } static CK_RV CC_C_GetTokenInfo(CK_SLOT_ID id, CK_TOKEN_INFO_PTR info) { const char* name; ENTER(C_GetTokenInfo); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); PREREQ(info, CKR_ARGUMENTS_BAD); /* Make sure the slot ID is valid */ if(!ckcapi_token_is_valid(id)) RETURN(CKR_SLOT_ID_INVALID); ASSERT(strlen(MANUFACTURER_ID) == 32); ASSERT(strlen(SLOT_TOKEN_MODEL) == 16); ASSERT(strlen(SLOT_TOKEN_SERIAL) == 16); /* Provide information about a token in the provided buffer */ strncpy((char*)info->manufacturerID, MANUFACTURER_ID, 32); strncpy((char*)info->model, SLOT_TOKEN_MODEL, 16); strncpy((char*)info->serialNumber, SLOT_TOKEN_SERIAL, 16); /* Protected authentication path: Windows prompts for it's own PINs */ info->flags = CKF_TOKEN_INITIALIZED | CKF_PROTECTED_AUTHENTICATION_PATH; info->ulMaxSessionCount = CK_EFFECTIVELY_INFINITE; info->ulSessionCount = CK_EFFECTIVELY_INFINITE; info->ulMaxRwSessionCount = CK_EFFECTIVELY_INFINITE; info->ulRwSessionCount = CK_EFFECTIVELY_INFINITE; info->ulMaxPinLen = MAX_PIN_LEN; info->ulMinPinLen = MIN_PIN_LEN; info->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION; info->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION; info->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION; info->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION; info->hardwareVersion.major = HARDWARE_VERSION_MAJOR; info->hardwareVersion.minor = HARDWARE_VERSION_MINOR; info->firmwareVersion.major = FIRMWARE_VERSION_MAJOR; info->firmwareVersion.minor = FIRMWARE_VERSION_MINOR; memset(info->utcTime, ' ', 16); /* Slot name is blank padded, odd */ name = ckcapi_token_get_display_name(id); memset((char*)info->label, ' ', sizeof(info->label)); memcpy((char*)info->label, name, min(strlen(name), sizeof(info->label))); RETURN(CKR_OK); } static CK_RV CC_C_GetMechanismList(CK_SLOT_ID id, CK_MECHANISM_TYPE_PTR mechanism_list, CK_ULONG_PTR count) { CK_ULONG n_mechs; ENTER(C_GetMechanismList); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); PREREQ(count, CKR_ARGUMENTS_BAD); if(!ckcapi_token_is_valid(id)) RETURN(CKR_SLOT_ID_INVALID); n_mechs = sizeof(all_mechanisms) / sizeof(all_mechanisms[0]); if(mechanism_list == NULL) { *count = n_mechs; RETURN(CKR_OK); } if(*count < n_mechs) { *count = n_mechs; RETURN(CKR_BUFFER_TOO_SMALL); } memcpy(mechanism_list, all_mechanisms, n_mechs * sizeof(CK_MECHANISM_TYPE)); *count = n_mechs; RETURN(CKR_OK); } static CK_RV CC_C_GetMechanismInfo(CK_SLOT_ID id, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR info) { ENTER(C_GetMechanismInfo); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); PREREQ(info, CKR_ARGUMENTS_BAD); if(!ckcapi_token_is_valid(id)) RETURN(CKR_SLOT_ID_INVALID); if(type == CKM_RSA_PKCS) { ckcapi_rsa_pkcs_get_info(type, info); RETURN(CKR_OK); } RETURN(CKR_MECHANISM_INVALID); } static CK_RV CC_C_InitToken(CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, CK_ULONG pin_len, CK_UTF8CHAR_PTR label) { ENTER(C_InitToken); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_WaitForSlotEvent(CK_FLAGS flags, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR pReserved) { ENTER(C_WaitForSlotEvent); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* * PKCS#11 GRAY AREA: What happens when we know we'll *never* * have any slot events, and someone calls us without CKR_DONT_BLOCK? * In case there's a thread dedicated to calling this function in a * loop, we wait 1 seconds when called without CKR_DONT_BLOCK. */ if(!(flags & CKF_DONT_BLOCK)) Sleep(1000); RETURN(CKR_NO_EVENT); } static CK_RV CC_C_OpenSession(CK_SLOT_ID id, CK_FLAGS flags, CK_VOID_PTR application, CK_NOTIFY notify, CK_SESSION_HANDLE_PTR session) { CkCapiSession* sess; CK_RV ret; ENTER(C_OpenSession); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); PREREQ(session, CKR_ARGUMENTS_BAD); PREREQ(flags & CKF_SERIAL_SESSION, CKR_SESSION_PARALLEL_NOT_SUPPORTED); if(!ckcapi_token_is_valid(id)) RETURN(CKR_SLOT_ID_INVALID); ret = ckcapi_session_create(id, &sess); if(ret != CKR_OK) RETURN(ret); sess->notify_callback = notify; sess->user_data = application; if(flags & CKF_RW_SESSION) sess->read_write = TRUE; ret = ckcapi_session_register(sess); if(ret == CKR_OK) { /* ID should have been assigned when registering */ ASSERT(sess->id > 0); *session = sess->id; } else { ckcapi_session_destroy(sess); } RETURN(ret); } static CK_RV CC_C_CloseSession(CK_SESSION_HANDLE session) { CkCapiSession* sess; CK_RV ret; ENTER(C_CloseSession); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); ret = ckcapi_session_remove_lock_ref(session, &sess); if(ret == CKR_OK) { /* This will unref and possibly destroy the session */ ckcapi_session_unref_unlock(sess); } RETURN(ret); } static CK_RV CC_C_CloseAllSessions(CK_SLOT_ID id) { ENTER(C_CloseAllSession); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); if(!ckcapi_token_is_valid(id)) RETURN(CKR_SLOT_ID_INVALID); ckcapi_session_close_all(id); RETURN(CKR_OK); } static CK_RV CC_C_GetFunctionStatus(CK_SESSION_HANDLE session) { ENTER(C_GetFunctionStatus); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); RETURN(CKR_FUNCTION_NOT_PARALLEL); } static CK_RV CC_C_CancelFunction(CK_SESSION_HANDLE session) { ENTER(C_CancelFunction); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); RETURN(CKR_FUNCTION_NOT_PARALLEL); } static CK_RV CC_C_GetSessionInfo(CK_SESSION_HANDLE session, CK_SESSION_INFO_PTR info) { CkCapiSession* sess; CK_RV ret; ENTER(C_GetSessionInfo); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); PREREQ(info, CKR_ARGUMENTS_BAD); ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); if(ret == CKR_OK) { ckcapi_session_get_info(sess, info); ckcapi_session_unref_unlock(sess); } RETURN(ret); } static CK_RV CC_C_InitPIN(CK_SESSION_HANDLE session, CK_UTF8CHAR_PTR pin, CK_ULONG pin_len) { ENTER(C_InitPIN); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* We don't support this stuff. We don't support 'SO' logins. */ RETURN(CKR_USER_NOT_LOGGED_IN); } static CK_RV CC_C_SetPIN(CK_SESSION_HANDLE session, CK_UTF8CHAR_PTR old_pin, CK_ULONG old_len, CK_UTF8CHAR_PTR new_pin, CK_ULONG new_len) { ENTER(C_SetPIN); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* Not supported, Windows takes care of this */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_GetOperationState(CK_SESSION_HANDLE session, CK_BYTE_PTR operation_state, CK_ULONG_PTR operation_state_len) { ENTER(C_GetOperationState); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* Nasty, no sirrr */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_SetOperationState(CK_SESSION_HANDLE session, CK_BYTE_PTR operation_state, CK_ULONG operation_state_len, CK_OBJECT_HANDLE encryption_key, CK_OBJECT_HANDLE authentication_key) { ENTER(C_SetOperationState); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* Nasty, no sirrr */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_Login(CK_SESSION_HANDLE session, CK_USER_TYPE user_type, CK_UTF8CHAR_PTR pin, CK_ULONG pin_len) { CkCapiSession* sess; CK_RV ret; ENTER(C_Login); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); if(ret == CKR_OK) { switch(user_type) { case CKU_USER: ret = ckcapi_token_login(sess->slot); break; case CKU_SO: ret = CKR_USER_TYPE_INVALID; break; default: ret = CKR_USER_TYPE_INVALID; break; } ckcapi_session_unref_unlock(sess); } RETURN(ret); } static CK_RV CC_C_Logout(CK_SESSION_HANDLE session) { CkCapiSession* sess; CK_RV ret; ENTER(C_Logout); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); if(ret == CKR_OK) { ret = ckcapi_token_logout(sess->slot); ckcapi_session_unref_unlock(sess); } RETURN(ret); } static CK_RV CC_C_CreateObject(CK_SESSION_HANDLE session, CK_ATTRIBUTE_PTR templ, CK_ULONG count, CK_OBJECT_HANDLE_PTR object) { ENTER(C_CreateObject); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* TODO: See if we need to support this */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_CopyObject(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, CK_ATTRIBUTE_PTR templ, CK_ULONG count, CK_OBJECT_HANDLE_PTR new_object) { ENTER(C_CopyObject); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* TODO: See if we need to support this */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_DestroyObject(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object) { ENTER(C_DestroyObject); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* TODO: See if we need to support this */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_GetObjectSize(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, CK_ULONG_PTR size) { ENTER(C_GetObjectSize); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* TODO: Implement this */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_GetAttributeValue(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, CK_ATTRIBUTE_PTR templ, CK_ULONG count) { CkCapiSession* sess; CkCapiObjectData* objdata; CK_RV ret; ENTER(C_GetAttributeValue); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); PREREQ(object, CKR_OBJECT_HANDLE_INVALID); PREREQ(!count || templ, CKR_ARGUMENTS_BAD); ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); if(ret == CKR_OK) { ret = ckcapi_session_get_object_data_for(sess, object, &objdata); if(ret == CKR_OK) ret = ckcapi_object_data_get_attrs(objdata, templ, count); ckcapi_session_unref_unlock(sess); } RETURN(ret); } static CK_RV CC_C_SetAttributeValue(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, CK_ATTRIBUTE_PTR templ, CK_ULONG count) { ENTER(C_SetAttributeValue); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* TODO: See if we need to implement this */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_FindObjectsInit(CK_SESSION_HANDLE session, CK_ATTRIBUTE_PTR templ, CK_ULONG count) { CkCapiSession* sess; CK_RV ret; ENTER(C_FindObjectsInit); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); PREREQ(!count || templ, CKR_ARGUMENTS_BAD); ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); if(ret == CKR_OK) { ret = ckcapi_session_find_init(sess, templ, count); ckcapi_session_unref_unlock(sess); } RETURN(ret); } static CK_RV CC_C_FindObjects(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE_PTR objects, CK_ULONG max_object_count, CK_ULONG_PTR object_count) { CkCapiSession* sess; CK_RV ret; ENTER(C_FindObjects); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); PREREQ(object_count, CKR_ARGUMENTS_BAD); PREREQ(!max_object_count || objects, CKR_ARGUMENTS_BAD); ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); if(ret == CKR_OK) { ret = ckcapi_session_find(sess, objects, max_object_count, object_count); ckcapi_session_unref_unlock(sess); } RETURN(ret); } static CK_RV CC_C_FindObjectsFinal(CK_SESSION_HANDLE session) { CkCapiSession* sess; CK_RV ret; ENTER(C_FindObjectsFinal); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); if(ret == CKR_OK) { ret = ckcapi_session_find_final(sess); ckcapi_session_unref_unlock(sess); } RETURN(ret); } static CK_RV CC_C_EncryptInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) { ENTER(C_EncryptInit); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* TODO: See if we need to implement this */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_Encrypt(CK_SESSION_HANDLE session, CK_BYTE_PTR data, CK_ULONG data_len, CK_BYTE_PTR encrypted_data, CK_ULONG_PTR encrypted_data_len) { ENTER(C_Encrypt); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* TODO: See if we need to implement this */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_EncryptUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len, CK_BYTE_PTR encrypted_part, CK_ULONG_PTR encrypted_part_len) { ENTER(C_EncryptUpdate); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_EncryptFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR last_encrypted_part, CK_ULONG_PTR last_encrypted_part_len) { ENTER(C_EncryptFinal); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_DecryptInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) { CkCapiObjectData* objdata; CkCapiSession* sess; CK_RV ret; ENTER(C_DecryptInit); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); PREREQ(mechanism, CKR_ARGUMENTS_BAD); PREREQ(key, CKR_ARGUMENTS_BAD); ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); if(ret == CKR_OK) { ret = ckcapi_session_get_object_data_for(sess, key, &objdata); if(ret == CKR_OK) ret = ckcapi_session_decrypt_init(sess, mechanism, objdata); ckcapi_session_unref_unlock(sess); } RETURN(ret); } static CK_RV CC_C_Decrypt(CK_SESSION_HANDLE session, CK_BYTE_PTR encrypted_data, CK_ULONG encrypted_data_len, CK_BYTE_PTR data, CK_ULONG_PTR data_len) { CkCapiSession* sess; CK_RV ret; ENTER(C_Decrypt); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); PREREQ(encrypted_data, CKR_ARGUMENTS_BAD); PREREQ(encrypted_data_len, CKR_ARGUMENTS_BAD); ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); if(ret == CKR_OK) { ret = ckcapi_session_decrypt(sess, encrypted_data, encrypted_data_len, data, data_len); ckcapi_session_unref_unlock(sess); } RETURN(ret); } static CK_RV CC_C_DecryptUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR encrypted_part, CK_ULONG encrypted_part_len, CK_BYTE_PTR part, CK_ULONG_PTR part_len) { ENTER(C_DecryptUpdate); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_DecryptFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR pLastPart, CK_ULONG_PTR last_part_len) { ENTER(C_DecryptFinal); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_DigestInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism) { ENTER(C_DigestInit); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* RSA/DSA mechs don't support digest. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_Digest(CK_SESSION_HANDLE session, CK_BYTE_PTR data, CK_ULONG data_len, CK_BYTE_PTR digest, CK_ULONG_PTR digest_len) { ENTER(C_Digest); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* RSA/DSA mechs don't support digest. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_DigestUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len) { ENTER(C_DigestUpdate); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* RSA/DSA mechs don't support digest. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_DigestKey(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) { ENTER(C_DigestKey); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* RSA/DSA mechs don't support digest. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_DigestFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR digest, CK_ULONG_PTR digest_len) { ENTER(C_DigestFinal); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* RSA/DSA mechs don't support digest. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_SignInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) { CkCapiObjectData* objdata; CkCapiSession* sess; CK_RV ret; ENTER(C_SignInit); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); PREREQ(mechanism, CKR_ARGUMENTS_BAD); PREREQ(key, CKR_ARGUMENTS_BAD); ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); if(ret == CKR_OK) { ret = ckcapi_session_get_object_data_for(sess, key, &objdata); if(ret == CKR_OK) ret = ckcapi_session_sign_init(sess, mechanism, objdata); ckcapi_session_unref_unlock(sess); } RETURN(ret); } static CK_RV CC_C_Sign(CK_SESSION_HANDLE session, CK_BYTE_PTR data, CK_ULONG data_len, CK_BYTE_PTR signature, CK_ULONG_PTR signature_len) { CkCapiSession* sess; CK_RV ret; ENTER(C_Sign); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); PREREQ(data, CKR_ARGUMENTS_BAD); PREREQ(data_len, CKR_ARGUMENTS_BAD); ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); if(ret == CKR_OK) { ret = ckcapi_session_sign(sess, data, data_len, signature, signature_len); ckcapi_session_unref_unlock(sess); } RETURN(ret); } static CK_RV CC_C_SignUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len) { ENTER(C_SignUpdate); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_SignFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR signature, CK_ULONG_PTR signature_len) { ENTER(C_SignFinal); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_SignRecoverInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) { ENTER(C_SignRecoverInit); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* TODO: Implement this */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_SignRecover(CK_SESSION_HANDLE session, CK_BYTE_PTR data, CK_ULONG data_len, CK_BYTE_PTR signature, CK_ULONG_PTR signature_len) { ENTER(C_SignRecover); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* TODO: Implement this */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_VerifyInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) { ENTER(C_VerifyInit); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* TODO: See if we need to implement this */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_Verify(CK_SESSION_HANDLE session, CK_BYTE_PTR data, CK_ULONG data_len, CK_BYTE_PTR signature, CK_ULONG signature_len) { ENTER(C_Verify); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* TODO: See if we need to implement this */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_VerifyUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len) { ENTER(C_VerifyUpdate); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_VerifyFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR signature, CK_ULONG signature_len) { ENTER(C_VerifyFinal); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_VerifyRecoverInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE key) { ENTER(C_VerifyRecoverInit); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* TODO: See if we need to implement this */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_VerifyRecover(CK_SESSION_HANDLE session, CK_BYTE_PTR signature, CK_ULONG signature_len, CK_BYTE_PTR data, CK_ULONG_PTR data_len) { ENTER(C_VerifyRecover); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* TODO: See if we need to implement this */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_DigestEncryptUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len, CK_BYTE_PTR encrypted_part, CK_ULONG_PTR encrypted_part_len) { ENTER(C_DigestEncryptUpdate); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_DecryptDigestUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR encrypted_part, CK_ULONG encrypted_part_len, CK_BYTE_PTR part, CK_ULONG_PTR part_len) { ENTER(C_DecryptDigestUpdate); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_SignEncryptUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len, CK_BYTE_PTR encrypted_part, CK_ULONG_PTR encrypted_part_len) { ENTER(C_SignEncryptUpdate); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_DecryptVerifyUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR encrypted_part, CK_ULONG encrypted_part_len, CK_BYTE_PTR part, CK_ULONG_PTR part_len) { ENTER(C_DecryptVerifyUpdate); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* RSA/DSA mechs don't support incremental crypto operations. */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_GenerateKey(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_ATTRIBUTE_PTR templ, CK_ULONG count, CK_OBJECT_HANDLE_PTR key) { ENTER(C_GenerateKey); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* Let key generation happen via Windows interfaces */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_GenerateKeyPair(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_ATTRIBUTE_PTR public_key_template, CK_ULONG public_key_attribute_count, CK_ATTRIBUTE_PTR private_key_template, CK_ULONG private_key_attribute_count, CK_OBJECT_HANDLE_PTR public_key, CK_OBJECT_HANDLE_PTR private_key) { ENTER(C_GenerateKeyPair); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* Let key generation happen via Windows interfaces */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_WrapKey(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE wrapping_key, CK_OBJECT_HANDLE key, CK_BYTE_PTR wrapped_key, CK_ULONG_PTR wrapped_key_len) { ENTER(C_WrapKey); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* TODO: See if we need to implement this */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_UnwrapKey(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE unwrapping_key, CK_BYTE_PTR wrapped_key, CK_ULONG wrapped_key_len, CK_ATTRIBUTE_PTR templ, CK_ULONG count, CK_OBJECT_HANDLE_PTR key) { ENTER(C_UnwrapKey); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* TODO: See if we need to implement this */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_DeriveKey(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, CK_OBJECT_HANDLE base_key, CK_ATTRIBUTE_PTR templ, CK_ULONG count, CK_OBJECT_HANDLE_PTR key) { ENTER(C_DeriveKey); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* Can't do this with RSA */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_SeedRandom(CK_SESSION_HANDLE session, CK_BYTE_PTR seed, CK_ULONG seed_len) { ENTER(C_SeedRandom); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* * TODO: Perhaps at some point in the future we may want * to see if we can hook into the Windows RNG */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_RV CC_C_GenerateRandom(CK_SESSION_HANDLE session, CK_BYTE_PTR random_data, CK_ULONG random_len) { ENTER(C_GenerateRandom); PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); /* * TODO: Perhaps at some point in the future we may want * to see if we can hook into the Windows RNG */ RETURN(CKR_FUNCTION_NOT_SUPPORTED); } static CK_FUNCTION_LIST functionList = { { 2, 11 }, /* version */ CC_C_Initialize, CC_C_Finalize, CC_C_GetInfo, CC_C_GetFunctionList, CC_C_GetSlotList, CC_C_GetSlotInfo, CC_C_GetTokenInfo, CC_C_GetMechanismList, CC_C_GetMechanismInfo, CC_C_InitToken, CC_C_InitPIN, CC_C_SetPIN, CC_C_OpenSession, CC_C_CloseSession, CC_C_CloseAllSessions, CC_C_GetSessionInfo, CC_C_GetOperationState, CC_C_SetOperationState, CC_C_Login, CC_C_Logout, CC_C_CreateObject, CC_C_CopyObject, CC_C_DestroyObject, CC_C_GetObjectSize, CC_C_GetAttributeValue, CC_C_SetAttributeValue, CC_C_FindObjectsInit, CC_C_FindObjects, CC_C_FindObjectsFinal, CC_C_EncryptInit, CC_C_Encrypt, CC_C_EncryptUpdate, CC_C_EncryptFinal, CC_C_DecryptInit, CC_C_Decrypt, CC_C_DecryptUpdate, CC_C_DecryptFinal, CC_C_DigestInit, CC_C_Digest, CC_C_DigestUpdate, CC_C_DigestKey, CC_C_DigestFinal, CC_C_SignInit, CC_C_Sign, CC_C_SignUpdate, CC_C_SignFinal, CC_C_SignRecoverInit, CC_C_SignRecover, CC_C_VerifyInit, CC_C_Verify, CC_C_VerifyUpdate, CC_C_VerifyFinal, CC_C_VerifyRecoverInit, CC_C_VerifyRecover, CC_C_DigestEncryptUpdate, CC_C_DecryptDigestUpdate, CC_C_SignEncryptUpdate, CC_C_DecryptVerifyUpdate, CC_C_GenerateKey, CC_C_GenerateKeyPair, CC_C_WrapKey, CC_C_UnwrapKey, CC_C_DeriveKey, CC_C_SeedRandom, CC_C_GenerateRandom, CC_C_GetFunctionStatus, CC_C_CancelFunction, CC_C_WaitForSlotEvent }; __declspec(dllexport) CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR list) { if(!list) return CKR_ARGUMENTS_BAD; *list = &functionList; return CKR_OK; }