/*
 * gnome-keyring
 *
 * Copyright (C) 2008 Stefan Walter
 *
 * This program 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.1 of
 * the License, or (at your option) any later version.
 *
 * This program 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 program; if not, see
 * <http://www.gnu.org/licenses/>.
 */

#include "gkm-crypto.h"
#include "gkm-module.h"
#include "gkm-session.h"

#include "pkcs11/pkcs11.h"

#include <unistd.h>

/* Forward declaration, this must be defined manually or using GKM_DEFINE_MODULE */
static GkmModule* gkm_module_instantiate (CK_C_INITIALIZE_ARGS_PTR args, GMutex *mutex);

static GkmModule *pkcs11_module = NULL;
static pid_t pkcs11_module_pid = 0;
static GMutex pkcs11_module_mutex = { 0, };

static CK_FUNCTION_LIST gkm_module_function_list;

static CK_RV
gkm_C_Initialize (CK_VOID_PTR init_args)
{
	CK_C_INITIALIZE_ARGS_PTR args = (CK_C_INITIALIZE_ARGS_PTR)init_args;
	CK_RV rv = CKR_OK;
	pid_t pid = getpid ();
	gboolean supplied_ok;

	if (args) {

		/* 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);

		if (!supplied_ok) {
			g_message ("invalid set of mutex calls supplied");
			return CKR_ARGUMENTS_BAD;
		}

		if (!(args->flags & CKF_OS_LOCKING_OK)) {
			g_message ("must be able to use our own locking and multi-thread primitives");
			return CKR_CANT_LOCK;
		}
	}

	gkm_crypto_initialize ();

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			if (pkcs11_module_pid == pid)
				rv = CKR_CRYPTOKI_ALREADY_INITIALIZED;
			else
				pkcs11_module_pid = pid;
		} else {
			pkcs11_module = gkm_module_instantiate (args, &pkcs11_module_mutex);
			if (!pkcs11_module) {
				g_warning ("module could not be instantiated");
				rv = CKR_GENERAL_ERROR;
			} else {
				pkcs11_module_pid = pid;
			}
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_Finalize (CK_VOID_PTR reserved)
{
	CK_RV rv = CKR_OK;

	if (reserved)
		return CKR_ARGUMENTS_BAD;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module == NULL) {
			rv = CKR_CRYPTOKI_NOT_INITIALIZED;
		} else {
			g_object_run_dispose (G_OBJECT (pkcs11_module));
			g_object_unref (pkcs11_module);
			pkcs11_module = NULL;
			pkcs11_module_pid = 0;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_GetInfo (CK_INFO_PTR info)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL)
			rv = gkm_module_C_GetInfo (pkcs11_module, info);

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR list)
{
	if (!list)
		return CKR_ARGUMENTS_BAD;
	*list = &gkm_module_function_list;
	return CKR_OK;
}

static CK_RV
gkm_C_GetSlotList (CK_BBOOL token_present, CK_SLOT_ID_PTR slot_list, CK_ULONG_PTR count)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL)
			rv = gkm_module_C_GetSlotList (pkcs11_module, token_present, slot_list, count);

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_GetSlotInfo (CK_SLOT_ID id, CK_SLOT_INFO_PTR info)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL)
			rv = gkm_module_C_GetSlotInfo (pkcs11_module, id, info);

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_GetTokenInfo (CK_SLOT_ID id, CK_TOKEN_INFO_PTR info)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL)
			rv = gkm_module_C_GetTokenInfo (pkcs11_module, id, info);

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_GetMechanismList (CK_SLOT_ID id, CK_MECHANISM_TYPE_PTR mechanism_list, CK_ULONG_PTR count)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL)
			rv = gkm_module_C_GetMechanismList (pkcs11_module, id, mechanism_list, count);

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_GetMechanismInfo (CK_SLOT_ID id, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR info)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL)
			rv = gkm_module_C_GetMechanismInfo (pkcs11_module, id, type, info);

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_InitToken (CK_SLOT_ID id, CK_UTF8CHAR_PTR pin, CK_ULONG pin_len, CK_UTF8CHAR_PTR label)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL)
			rv = gkm_module_C_InitToken (pkcs11_module, id, pin, pin_len, label);

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_WaitForSlotEvent (CK_FLAGS flags, CK_SLOT_ID_PTR slot, CK_VOID_PTR reserved)
{
	/*
	 * 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 5 seconds when called without CKR_DONT_BLOCK.
	 */

	if (!(flags & CKF_DONT_BLOCK))
		sleep (5);

	return CKR_NO_EVENT;
}

static CK_RV
gkm_C_OpenSession (CK_SLOT_ID id, CK_FLAGS flags, CK_VOID_PTR user_data, CK_NOTIFY callback, CK_SESSION_HANDLE_PTR handle)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL)
			rv = gkm_module_C_OpenSession (pkcs11_module, id, flags, user_data, callback, handle);

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_CloseSession (CK_SESSION_HANDLE handle)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL)
			rv = gkm_module_C_CloseSession (pkcs11_module, handle);

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_CloseAllSessions (CK_SLOT_ID id)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL)
			rv = gkm_module_C_CloseAllSessions (pkcs11_module, id);

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_GetFunctionStatus (CK_SESSION_HANDLE handle)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_GetFunctionStatus (session);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_CancelFunction (CK_SESSION_HANDLE handle)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_CancelFunction (session);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_GetSessionInfo (CK_SESSION_HANDLE handle, CK_SESSION_INFO_PTR info)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_GetSessionInfo (session,
				                                   info);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_InitPIN (CK_SESSION_HANDLE handle, CK_UTF8CHAR_PTR pin, CK_ULONG pin_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL)
			rv = gkm_module_C_InitPIN (pkcs11_module, handle, pin, pin_len);

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_SetPIN (CK_SESSION_HANDLE handle, CK_UTF8CHAR_PTR old_pin, CK_ULONG old_pin_len, CK_UTF8CHAR_PTR new_pin, CK_ULONG new_pin_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL)
			rv = gkm_module_C_SetPIN (pkcs11_module, handle, old_pin, old_pin_len, new_pin, new_pin_len);

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_GetOperationState (CK_SESSION_HANDLE handle, CK_BYTE_PTR operation_state, CK_ULONG_PTR operation_state_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_GetOperationState (session,
				                                      operation_state, operation_state_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_SetOperationState (CK_SESSION_HANDLE handle, CK_BYTE_PTR operation_state,
                         CK_ULONG operation_state_len, CK_OBJECT_HANDLE encryption_key,
                         CK_OBJECT_HANDLE authentication_key)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_SetOperationState (session,
				                                      operation_state, operation_state_len, encryption_key, authentication_key);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_Login (CK_SESSION_HANDLE handle, CK_USER_TYPE user_type,
             CK_UTF8CHAR_PTR pin, CK_ULONG pin_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL)
			rv = gkm_module_C_Login (pkcs11_module, handle, user_type, pin, pin_len);

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_Logout (CK_SESSION_HANDLE handle)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL)
			rv = gkm_module_C_Logout (pkcs11_module, handle);

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_CreateObject (CK_SESSION_HANDLE handle, CK_ATTRIBUTE_PTR template,
                    CK_ULONG count, CK_OBJECT_HANDLE_PTR new_object)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_CreateObject (session,
				                                 template, count, new_object);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_CopyObject (CK_SESSION_HANDLE handle, CK_OBJECT_HANDLE object,
                  CK_ATTRIBUTE_PTR template, CK_ULONG count,
                  CK_OBJECT_HANDLE_PTR new_object)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_CopyObject (session,
				                               object, template, count, new_object);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}


static CK_RV
gkm_C_DestroyObject (CK_SESSION_HANDLE handle, CK_OBJECT_HANDLE object)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_DestroyObject (session, object);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_GetObjectSize (CK_SESSION_HANDLE handle, CK_OBJECT_HANDLE object,
                     CK_ULONG_PTR size)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_GetObjectSize (session, object, size);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_GetAttributeValue (CK_SESSION_HANDLE handle, CK_OBJECT_HANDLE object,
                         CK_ATTRIBUTE_PTR template, CK_ULONG count)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_GetAttributeValue (session, object, template, count);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_SetAttributeValue (CK_SESSION_HANDLE handle, CK_OBJECT_HANDLE object,
                         CK_ATTRIBUTE_PTR template, CK_ULONG count)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_SetAttributeValue (session, object, template, count);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_FindObjectsInit (CK_SESSION_HANDLE handle, CK_ATTRIBUTE_PTR template,
                       CK_ULONG count)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_FindObjectsInit (session, template, count);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_FindObjects (CK_SESSION_HANDLE handle, CK_OBJECT_HANDLE_PTR objects,
                   CK_ULONG max_count, CK_ULONG_PTR count)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_FindObjects (session, objects, max_count, count);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_FindObjectsFinal (CK_SESSION_HANDLE handle)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_FindObjectsFinal (session);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_EncryptInit (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
                   CK_OBJECT_HANDLE key)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_EncryptInit (session, mechanism, key);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_Encrypt (CK_SESSION_HANDLE handle, CK_BYTE_PTR data, CK_ULONG data_len,
               CK_BYTE_PTR encrypted_data, CK_ULONG_PTR encrypted_data_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_Encrypt (session, data, data_len, encrypted_data, encrypted_data_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_EncryptUpdate (CK_SESSION_HANDLE handle, CK_BYTE_PTR part,
                     CK_ULONG part_len, CK_BYTE_PTR encrypted_part,
                     CK_ULONG_PTR encrypted_part_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_EncryptUpdate (session, part, part_len, encrypted_part, encrypted_part_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_EncryptFinal (CK_SESSION_HANDLE handle, CK_BYTE_PTR last_part,
                    CK_ULONG_PTR last_part_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_EncryptFinal (session, last_part, last_part_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_DecryptInit (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
                   CK_OBJECT_HANDLE key)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_DecryptInit (session, mechanism, key);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_Decrypt (CK_SESSION_HANDLE handle, CK_BYTE_PTR enc_data,
               CK_ULONG enc_data_len, CK_BYTE_PTR data, CK_ULONG_PTR data_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_Decrypt (session, enc_data, enc_data_len, data, data_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_DecryptUpdate (CK_SESSION_HANDLE handle, CK_BYTE_PTR enc_part,
                     CK_ULONG enc_part_len, CK_BYTE_PTR part, CK_ULONG_PTR part_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_DecryptUpdate (session, enc_part, enc_part_len, part, part_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_DecryptFinal (CK_SESSION_HANDLE handle, CK_BYTE_PTR last_part,
                    CK_ULONG_PTR last_part_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_DecryptFinal (session, last_part, last_part_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_DigestInit (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_DigestInit (session, mechanism);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_Digest (CK_SESSION_HANDLE handle, CK_BYTE_PTR data, CK_ULONG data_len,
              CK_BYTE_PTR digest, CK_ULONG_PTR digest_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_Digest (session, data, data_len, digest, digest_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_DigestUpdate (CK_SESSION_HANDLE handle, CK_BYTE_PTR part, CK_ULONG part_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_DigestUpdate (session, part, part_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_DigestKey (CK_SESSION_HANDLE handle, CK_OBJECT_HANDLE key)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_DigestKey (session, key);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_DigestFinal (CK_SESSION_HANDLE handle, CK_BYTE_PTR digest,
                   CK_ULONG_PTR digest_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_DigestFinal (session, digest, digest_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_SignInit (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
                CK_OBJECT_HANDLE key)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_SignInit (session, mechanism, key);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_Sign (CK_SESSION_HANDLE handle, CK_BYTE_PTR data, CK_ULONG data_len,
            CK_BYTE_PTR signature, CK_ULONG_PTR signature_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_Sign (session, data, data_len, signature, signature_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_SignUpdate (CK_SESSION_HANDLE handle, CK_BYTE_PTR part, CK_ULONG part_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_SignUpdate (session, part, part_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_SignFinal (CK_SESSION_HANDLE handle, CK_BYTE_PTR signature,
                 CK_ULONG_PTR signature_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_SignFinal (session, signature, signature_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_SignRecoverInit (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
                       CK_OBJECT_HANDLE key)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_SignRecoverInit (session, mechanism, key);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_SignRecover (CK_SESSION_HANDLE handle, CK_BYTE_PTR data, CK_ULONG data_len,
                   CK_BYTE_PTR signature, CK_ULONG_PTR signature_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_SignRecover (session, data, data_len, signature, signature_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_VerifyInit (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
                  CK_OBJECT_HANDLE key)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_VerifyInit (session, mechanism, key);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_Verify (CK_SESSION_HANDLE handle, CK_BYTE_PTR data, CK_ULONG data_len,
              CK_BYTE_PTR signature, CK_ULONG signature_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_Verify (session, data, data_len, signature, signature_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_VerifyUpdate (CK_SESSION_HANDLE handle, CK_BYTE_PTR part, CK_ULONG part_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_VerifyUpdate (session, part, part_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_VerifyFinal (CK_SESSION_HANDLE handle, CK_BYTE_PTR signature,
                   CK_ULONG signature_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_VerifyFinal (session, signature, signature_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_VerifyRecoverInit (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
                         CK_OBJECT_HANDLE key)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_VerifyRecoverInit (session, mechanism, key);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_VerifyRecover (CK_SESSION_HANDLE handle, CK_BYTE_PTR signature,
                     CK_ULONG signature_len, CK_BYTE_PTR data, CK_ULONG_PTR data_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_VerifyRecover (session, signature, signature_len, data, data_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_DigestEncryptUpdate (CK_SESSION_HANDLE handle, CK_BYTE_PTR part,
                           CK_ULONG part_len, CK_BYTE_PTR enc_part,
                           CK_ULONG_PTR enc_part_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_DigestEncryptUpdate (session, part, part_len, enc_part, enc_part_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_DecryptDigestUpdate (CK_SESSION_HANDLE handle, CK_BYTE_PTR enc_part,
                           CK_ULONG enc_part_len, CK_BYTE_PTR part,
                           CK_ULONG_PTR part_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_DecryptDigestUpdate (session, enc_part, enc_part_len, part, part_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_SignEncryptUpdate (CK_SESSION_HANDLE handle, CK_BYTE_PTR part,
                         CK_ULONG part_len, CK_BYTE_PTR enc_part,
                         CK_ULONG_PTR enc_part_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_SignEncryptUpdate (session, part, part_len, enc_part, enc_part_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_DecryptVerifyUpdate (CK_SESSION_HANDLE handle, CK_BYTE_PTR enc_part,
                           CK_ULONG enc_part_len, CK_BYTE_PTR part,
                           CK_ULONG_PTR part_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_DecryptVerifyUpdate (session, enc_part, enc_part_len, part, part_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_GenerateKey (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
                   CK_ATTRIBUTE_PTR template, CK_ULONG count,
                   CK_OBJECT_HANDLE_PTR key)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_GenerateKey (session, mechanism, template, count, key);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_GenerateKeyPair (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
                       CK_ATTRIBUTE_PTR pub_template, CK_ULONG pub_count,
                       CK_ATTRIBUTE_PTR priv_template, CK_ULONG priv_count,
                       CK_OBJECT_HANDLE_PTR pub_key, CK_OBJECT_HANDLE_PTR priv_key)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_GenerateKeyPair (session, mechanism, pub_template, pub_count, priv_template, priv_count, pub_key, priv_key);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_WrapKey (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
               CK_OBJECT_HANDLE wrapping_key, CK_OBJECT_HANDLE key,
               CK_BYTE_PTR wrapped_key, CK_ULONG_PTR wrapped_key_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_WrapKey (session, mechanism, wrapping_key, key, wrapped_key, wrapped_key_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_UnwrapKey (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
                 CK_OBJECT_HANDLE unwrapping_key, CK_BYTE_PTR wrapped_key,
                 CK_ULONG wrapped_key_len, CK_ATTRIBUTE_PTR template,
                 CK_ULONG count, CK_OBJECT_HANDLE_PTR key)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_UnwrapKey (session, mechanism, unwrapping_key, wrapped_key, wrapped_key_len, template, count, key);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_DeriveKey (CK_SESSION_HANDLE handle, CK_MECHANISM_PTR mechanism,
                 CK_OBJECT_HANDLE base_key, CK_ATTRIBUTE_PTR template,
                 CK_ULONG count, CK_OBJECT_HANDLE_PTR key)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_DeriveKey (session, mechanism, base_key, template, count, key);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_SeedRandom (CK_SESSION_HANDLE handle, CK_BYTE_PTR seed, CK_ULONG seed_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_SeedRandom (session, seed, seed_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

static CK_RV
gkm_C_GenerateRandom (CK_SESSION_HANDLE handle, CK_BYTE_PTR random_data,
                      CK_ULONG random_len)
{
	CK_RV rv = CKR_CRYPTOKI_NOT_INITIALIZED;
	GkmSession *session;

	g_mutex_lock (&pkcs11_module_mutex);

		if (pkcs11_module != NULL) {
			session = gkm_module_lookup_session (pkcs11_module, handle);
			if (session != NULL)
				rv = gkm_session_C_GenerateRandom (session, random_data, random_len);
			else
				rv = CKR_SESSION_HANDLE_INVALID;
		}

	g_mutex_unlock (&pkcs11_module_mutex);

	return rv;
}

/* --------------------------------------------------------------------
 * MODULE ENTRY POINT
 */

/*
 * PKCS#11 is broken here. It states that Unix compilers automatically byte
 * pack structures. This is wrong. GCC on Linux aligns to 4 by default.
 *
 * This results in incompatibilities. Where this structure's first version
 * members take up too much or too little space depending on how this module
 * is compiled.
 */

static CK_FUNCTION_LIST gkm_module_function_list = {
	{ CRYPTOKI_VERSION_MAJOR, CRYPTOKI_VERSION_MINOR },  /* version */
	gkm_C_Initialize,
	gkm_C_Finalize,
	gkm_C_GetInfo,
	gkm_C_GetFunctionList,
	gkm_C_GetSlotList,
	gkm_C_GetSlotInfo,
	gkm_C_GetTokenInfo,
	gkm_C_GetMechanismList,
	gkm_C_GetMechanismInfo,
	gkm_C_InitToken,
	gkm_C_InitPIN,
	gkm_C_SetPIN,
	gkm_C_OpenSession,
	gkm_C_CloseSession,
	gkm_C_CloseAllSessions,
	gkm_C_GetSessionInfo,
	gkm_C_GetOperationState,
	gkm_C_SetOperationState,
	gkm_C_Login,
	gkm_C_Logout,
	gkm_C_CreateObject,
	gkm_C_CopyObject,
	gkm_C_DestroyObject,
	gkm_C_GetObjectSize,
	gkm_C_GetAttributeValue,
	gkm_C_SetAttributeValue,
	gkm_C_FindObjectsInit,
	gkm_C_FindObjects,
	gkm_C_FindObjectsFinal,
	gkm_C_EncryptInit,
	gkm_C_Encrypt,
	gkm_C_EncryptUpdate,
	gkm_C_EncryptFinal,
	gkm_C_DecryptInit,
	gkm_C_Decrypt,
	gkm_C_DecryptUpdate,
	gkm_C_DecryptFinal,
	gkm_C_DigestInit,
	gkm_C_Digest,
	gkm_C_DigestUpdate,
	gkm_C_DigestKey,
	gkm_C_DigestFinal,
	gkm_C_SignInit,
	gkm_C_Sign,
	gkm_C_SignUpdate,
	gkm_C_SignFinal,
	gkm_C_SignRecoverInit,
	gkm_C_SignRecover,
	gkm_C_VerifyInit,
	gkm_C_Verify,
	gkm_C_VerifyUpdate,
	gkm_C_VerifyFinal,
	gkm_C_VerifyRecoverInit,
	gkm_C_VerifyRecover,
	gkm_C_DigestEncryptUpdate,
	gkm_C_DecryptDigestUpdate,
	gkm_C_SignEncryptUpdate,
	gkm_C_DecryptVerifyUpdate,
	gkm_C_GenerateKey,
	gkm_C_GenerateKeyPair,
	gkm_C_WrapKey,
	gkm_C_UnwrapKey,
	gkm_C_DeriveKey,
	gkm_C_SeedRandom,
	gkm_C_GenerateRandom,
	gkm_C_GetFunctionStatus,
	gkm_C_CancelFunction,
	gkm_C_WaitForSlotEvent
};
