/*
 * pkcs11-object.c: PKCS#11 object management and handling functions
 *
 * Copyright (C) 2002  Timo Teräs <timo.teras@iki.fi>
 *
 * 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.1 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#include "config.h"

#include <stdlib.h>
#include <string.h>

#include "sc-pkcs11.h"

static void sc_find_release(sc_pkcs11_operation_t *operation);

/* Pseudo mechanism for the Find operation */
static sc_pkcs11_mechanism_type_t find_mechanism = {
	0,		/* mech */
	{0,0,0},	/* mech_info */
	{ -1 },		/* key_types */
	sizeof(struct sc_pkcs11_find_operation),	/* obj_size */
	sc_find_release,				/* release */
	NULL,		/* md_init */
	NULL,		/* md_update */
	NULL,		/* md_final */
	NULL,		/* sign_init */
	NULL,		/* sign_update */
	NULL,		/* sign_final */
	NULL,		/* sign_size */
	NULL,		/* verif_init */
	NULL,		/* verif_update */
	NULL,		/* verif_final */
	NULL,		/* decrypt_init */
	NULL,		/* decrypt */
	NULL,		/* decrypt_update */
	NULL,		/* decrypt_final */
	NULL,		/* derive */
	NULL,		/* wrap */
	NULL,		/* unwrap */
	NULL,		/* encrypt init */
	NULL,		/* encrypt */
	NULL,		/* ecnrypt_update */
	NULL,		/* encrypt_final */
	NULL,		/* mech_data */
	NULL,		/* free_mech_data */
	NULL,		/* copy_mech_data */
};

static void
sc_find_release(sc_pkcs11_operation_t *operation)
{
	struct sc_pkcs11_find_operation *fop = (struct sc_pkcs11_find_operation *)operation;

	if (fop->handles) {
		free(fop->handles);
		fop->handles = NULL;
	}
}


static CK_RV
get_object_from_session(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
		struct sc_pkcs11_session **session, struct sc_pkcs11_object **object)
{
	struct sc_pkcs11_session *sess;
	CK_RV rv;

	rv = get_session(hSession, &sess);
	if (rv != CKR_OK)
		return rv;

	*object = list_seek(&sess->slot->objects, &hObject);
	if (!*object)
		return CKR_OBJECT_HANDLE_INVALID;
	*session = sess;
	return CKR_OK;
}

/* C_CreateObject can be called from C_DeriveKey
 * which is holding the sc_pkcs11_lock
 * So dont get the lock again. */
static
CK_RV sc_create_object_int(CK_SESSION_HANDLE hSession,	/* the session's handle */
		CK_ATTRIBUTE_PTR pTemplate,		/* the object's template */
		CK_ULONG ulCount,			/* attributes in template */
		CK_OBJECT_HANDLE_PTR phObject,		/* receives new object's handle. */
		int use_lock)
{
	CK_RV rv = CKR_OK;
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_card *card;
	CK_BBOOL is_token = FALSE;

	LOG_FUNC_CALLED(context);
	if (pTemplate == NULL_PTR || ulCount == 0)
		return CKR_ARGUMENTS_BAD;

	if (use_lock) {
	    rv = sc_pkcs11_lock();
	    if (rv != CKR_OK)
		return rv;
	}

	dump_template(SC_LOG_DEBUG_NORMAL, "C_CreateObject()", pTemplate, ulCount);

	session = list_seek(&sessions, &hSession);
	if (!session) {
		rv = CKR_SESSION_HANDLE_INVALID;
		goto out;
	}

	rv = attr_find(pTemplate, ulCount, CKA_TOKEN, &is_token, NULL);
	if (rv != CKR_TEMPLATE_INCOMPLETE && rv != CKR_OK) {
		goto out;
	}

	if (is_token == TRUE) {
		if (session->slot->token_info.flags & CKF_WRITE_PROTECTED) {
			rv = CKR_TOKEN_WRITE_PROTECTED;
			goto out;
		}
		if (!(session->flags & CKF_RW_SESSION)) {
			rv = CKR_SESSION_READ_ONLY;
			goto out;
		}
	}

	card = session->slot->p11card;
	if (card->framework->create_object == NULL)
		rv = CKR_FUNCTION_NOT_SUPPORTED;
	else
		rv = card->framework->create_object(session->slot, pTemplate, ulCount, phObject);

out:
	if (use_lock)
		sc_pkcs11_unlock();

	return rv;
}

CK_RV
C_CreateObject(CK_SESSION_HANDLE hSession,	/* the session's handle */
		CK_ATTRIBUTE_PTR pTemplate,	/* the object's template */
		CK_ULONG ulCount,		/* attributes in template */
		CK_OBJECT_HANDLE_PTR phObject)
{
	return sc_create_object_int(hSession, pTemplate, ulCount, phObject, 1);
}


CK_RV
C_CopyObject(CK_SESSION_HANDLE hSession,	/* the session's handle */
		CK_OBJECT_HANDLE hObject,	/* the object's handle */
		CK_ATTRIBUTE_PTR pTemplate,	/* template for new object */
		CK_ULONG ulCount,		/* attributes in template */
		CK_OBJECT_HANDLE_PTR phNewObject)	/* receives handle of copy */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}


CK_RV
C_DestroyObject(CK_SESSION_HANDLE hSession,	/* the session's handle */
		CK_OBJECT_HANDLE hObject)	/* the object's handle */
{
	CK_RV rv;
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;
	CK_BBOOL is_token = FALSE;
	CK_ATTRIBUTE token_attribute = {CKA_TOKEN, &is_token, sizeof(is_token)};

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	sc_log(context, "C_DestroyObject(hSession=0x%lx, hObject=0x%lx)", hSession, hObject);
	rv = get_object_from_session(hSession, hObject, &session, &object);
	if (rv != CKR_OK)
		goto out;

	object->ops->get_attribute(session, object, &token_attribute);
	if (is_token == TRUE) {
		if (session->slot->token_info.flags & CKF_WRITE_PROTECTED) {
			rv = CKR_TOKEN_WRITE_PROTECTED;
			goto out;
		}
		if (!(session->flags & CKF_RW_SESSION)) {
			rv = CKR_SESSION_READ_ONLY;
			goto out;
		}
	}

	if (object->ops->destroy_object == NULL)
		rv = CKR_FUNCTION_NOT_SUPPORTED;
	else
		rv = object->ops->destroy_object(session, object);

out:
	sc_pkcs11_unlock();
	return rv;
}


CK_RV
C_GetObjectSize(CK_SESSION_HANDLE hSession,	/* the session's handle */
		      CK_OBJECT_HANDLE hObject,	/* the object's handle */
		      CK_ULONG_PTR pulSize)	/* receives size of object */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}


CK_RV
C_GetAttributeValue(CK_SESSION_HANDLE hSession,	/* the session's handle */
		CK_OBJECT_HANDLE hObject,	/* the object's handle */
		CK_ATTRIBUTE_PTR pTemplate,	/* specifies attributes, gets values */
		CK_ULONG ulCount)		/* attributes in template */
{
	static CK_RV precedence[] = {
		CKR_OK,
		CKR_BUFFER_TOO_SMALL,
		CKR_ATTRIBUTE_TYPE_INVALID,
		CKR_ATTRIBUTE_SENSITIVE,
		-1
	};
	char object_name[64];
	CK_RV j;
	CK_RV rv;
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;
	CK_RV res;
	CK_RV res_type;
	unsigned int i;
	const char *name;

	if (pTemplate == NULL_PTR || ulCount == 0)
		return CKR_ARGUMENTS_BAD;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_object_from_session(hSession, hObject, &session, &object);
	if (rv != CKR_OK)
		goto out;

	/* Debug printf */
	snprintf(object_name, sizeof(object_name), "Object %lu", (unsigned long)hObject);

	res_type = 0;
	for (i = 0; i < ulCount; i++) {
		res = object->ops->get_attribute(session, object, &pTemplate[i]);
		if (res != CKR_OK)
			pTemplate[i].ulValueLen = (CK_ULONG) - 1;

		dump_template(SC_LOG_DEBUG_NORMAL, object_name, &pTemplate[i], 1);

		/* the pkcs11 spec has complicated rules on
		 * what errors take precedence:
		 *      CKR_ATTRIBUTE_SENSITIVE
		 *      CKR_ATTRIBUTE_INVALID
		 *      CKR_BUFFER_TOO_SMALL
		 * It does not exactly specify how other errors
		 * should be handled - we give them highest
		 * precedence
		 */
		for (j = 0; precedence[j] != (CK_RV) -1; j++) {
			if (precedence[j] == res)
				break;
		}
		if (j > res_type) {
			res_type = j;
			rv = res;
		}
	}

out:
	name = lookup_enum (RV_T, rv );
	if (name)
		sc_log(context, "C_GetAttributeValue(hSession=0x%lx, hObject=0x%lx) = %s",
			hSession, hObject, name);
	else
		sc_log(context, "C_GetAttributeValue(hSession=0x%lx, hObject=0x%lx) = 0x%lx",
                        hSession, hObject, rv);

	sc_pkcs11_unlock();
	return rv;
}


CK_RV
C_SetAttributeValue(CK_SESSION_HANDLE hSession,	/* the session's handle */
		CK_OBJECT_HANDLE hObject,	/* the object's handle */
		CK_ATTRIBUTE_PTR pTemplate,	/* specifies attributes and values */
		CK_ULONG ulCount)		/* attributes in template */
{
	CK_RV rv;
	unsigned int i;
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;

	if (pTemplate == NULL_PTR || ulCount == 0)
		return CKR_ARGUMENTS_BAD;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	dump_template(SC_LOG_DEBUG_NORMAL, "C_SetAttributeValue", pTemplate, ulCount);

	rv = get_object_from_session(hSession, hObject, &session, &object);
	if (rv != CKR_OK)
		goto out;

	if (!(session->flags & CKF_RW_SESSION)) {
		rv = CKR_SESSION_READ_ONLY;
		goto out;
	}

	if (object->ops->set_attribute == NULL)
		rv = CKR_FUNCTION_NOT_SUPPORTED;
	else {
		for (i = 0; i < ulCount; i++) {
			rv = object->ops->set_attribute(session, object, &pTemplate[i]);
			if (rv != CKR_OK)
				break;
		}
	}

out:
	sc_pkcs11_unlock();
	return rv;
}


CK_RV
C_FindObjectsInit(CK_SESSION_HANDLE hSession,	/* the session's handle */
		CK_ATTRIBUTE_PTR pTemplate,	/* attribute values to match */
		CK_ULONG ulCount)		/* attributes in search template */
{
	CK_RV rv;
	CK_BBOOL is_private = TRUE;
	CK_ATTRIBUTE private_attribute = { CKA_PRIVATE, &is_private, sizeof(is_private) };
	int match, hide_private;
	unsigned int i, j;
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;
	struct sc_pkcs11_find_operation *operation;
	struct sc_pkcs11_slot *slot;
	struct sc_pkcs11_operation *op = NULL;

	if (pTemplate == NULL_PTR && ulCount > 0)
		return CKR_ARGUMENTS_BAD;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	sc_log(context, "C_FindObjectsInit(slot = %lu)\n", session->slot->id);
	dump_template(SC_LOG_DEBUG_NORMAL, "C_FindObjectsInit()", pTemplate, ulCount);

	rv = session_start_operation(session, SC_PKCS11_OPERATION_FIND,
				     &find_mechanism, &op);
	operation = (struct sc_pkcs11_find_operation *) op;
	if (rv != CKR_OK)
		goto out;

	operation->current_handle = 0;
	operation->num_handles = 0;
	operation->allocated_handles = 0;
	operation->handles = NULL;
	slot = session->slot;

	/* Check whether we should hide private objects */
	hide_private = 0;
	if ((slot->login_user == -1) && (slot->token_info.flags & CKF_LOGIN_REQUIRED))
		hide_private = 1;

	/* For each object in token do */
	for (i=0; i<list_size(&slot->objects); i++) {
		object = (struct sc_pkcs11_object *)list_get_at(&slot->objects, i);
		sc_log(context, "Object with handle 0x%lx", object->handle);

		/* User not logged in and private object? */
		if (hide_private) {
			if (object->ops->get_attribute(session, object, &private_attribute) != CKR_OK)
			        continue;
			if (is_private) {
				sc_log(context,
				       "Object %lu/%lu: Private object and not logged in.",
				       slot->id, object->handle);
				continue;
			}
		}

		/* Try to match every attribute */
		match = 1;
		for (j = 0; j < ulCount; j++) {
			rv = object->ops->cmp_attribute(session, object, &pTemplate[j]);
			if (rv == 0) {
				sc_log(context,
				       "Object %lu/%lu: Attribute 0x%lx does NOT match.",
				       slot->id, object->handle, pTemplate[j].type);
				match = 0;
				break;
			}

			if (context->debug >= 4) {
				sc_log(context,
				       "Object %lu/%lu: Attribute 0x%lx matches.",
				       slot->id, object->handle, pTemplate[j].type);
			}
		}

		if (match) {
			sc_log(context, "Object %lu/%lu matches\n", slot->id,
			       object->handle);
			/* Realloc handles - remove restriction on only 32 matching objects -dee */
			if (operation->num_handles >= operation->allocated_handles) {
				operation->allocated_handles += SC_PKCS11_FIND_INC_HANDLES;
				sc_log(context, "realloc for %d handles", operation->allocated_handles);
				operation->handles = realloc(operation->handles,
					sizeof(CK_OBJECT_HANDLE) * operation->allocated_handles);
				if (operation->handles == NULL) {
					rv = CKR_HOST_MEMORY;
					goto out;
				}
			}
			operation->handles[operation->num_handles++] = object->handle;
		}
	}
	rv = CKR_OK;

	sc_log(context, "%d matching objects\n", operation->num_handles);

out:
	sc_pkcs11_unlock();
	return rv;
}


CK_RV
C_FindObjects(CK_SESSION_HANDLE hSession,	/* the session's handle */
		CK_OBJECT_HANDLE_PTR phObject,	/* receives object handle array */
		CK_ULONG ulMaxObjectCount,	/* max handles to be returned */
		CK_ULONG_PTR pulObjectCount)	/* actual number returned */
{
	CK_RV rv;
	CK_ULONG to_return;
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_find_operation *operation;
	struct sc_pkcs11_operation *op = NULL;

	if (phObject == NULL_PTR || ulMaxObjectCount == 0 || pulObjectCount == NULL_PTR)
		return CKR_ARGUMENTS_BAD;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	rv = session_get_operation(session, SC_PKCS11_OPERATION_FIND, &op);
	operation = (struct sc_pkcs11_find_operation *) op;
	if (rv != CKR_OK)
		goto out;

	to_return = (CK_ULONG) operation->num_handles - operation->current_handle;
	if (to_return > ulMaxObjectCount)
		to_return = ulMaxObjectCount;

	*pulObjectCount = to_return;

	memcpy(phObject, &operation->handles[operation->current_handle], to_return * sizeof(CK_OBJECT_HANDLE));

	operation->current_handle += to_return;

out:	sc_pkcs11_unlock();
	return rv;
}


CK_RV
C_FindObjectsFinal(CK_SESSION_HANDLE hSession)	/* the session's handle */
{
	CK_RV rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	rv = session_get_operation(session, SC_PKCS11_OPERATION_FIND, NULL);
	if (rv == CKR_OK)
		session_stop_operation(session, SC_PKCS11_OPERATION_FIND);

out:	sc_pkcs11_unlock();
	return rv;
}

/*
 * Below here all functions are wrappers to pass all object attribute and method
 * handling to appropriate object layer.
 */
CK_RV
C_DigestInit(CK_SESSION_HANDLE hSession,	/* the session's handle */
		CK_MECHANISM_PTR pMechanism)	/* the digesting mechanism */
{
	CK_RV rv;
	struct sc_pkcs11_session *session;

	if (pMechanism == NULL_PTR)
		return CKR_ARGUMENTS_BAD;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	sc_log(context, "C_DigestInit(hSession=0x%lx)", hSession);
	rv = get_session(hSession, &session);
	if (rv == CKR_OK)
		rv = sc_pkcs11_md_init(session, pMechanism);

	SC_LOG_RV("C_DigestInit() = %s", rv);
	sc_pkcs11_unlock();
	return rv;
}


CK_RV
C_Digest(CK_SESSION_HANDLE hSession,		/* the session's handle */
		CK_BYTE_PTR pData,		/* data to be digested */
		CK_ULONG ulDataLen,		/* bytes of data to be digested */
		CK_BYTE_PTR pDigest,		/* receives the message digest */
		CK_ULONG_PTR pulDigestLen)	/* receives byte length of digest */
{
	CK_RV rv;
	struct sc_pkcs11_session *session;
	CK_ULONG  ulBuflen = 0;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	sc_log(context, "C_Digest(hSession=0x%lx)", hSession);
	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	/* if pDigest == NULL, buffer size request */
	if (pDigest) {
	    /* As per PKCS#11 2.20 we need to check if buffer too small before update */
	    rv = sc_pkcs11_md_final(session, NULL, &ulBuflen);
	    if (rv != CKR_OK)
		goto out;

	    if (ulBuflen > *pulDigestLen) {
	        *pulDigestLen = ulBuflen;
		rv = CKR_BUFFER_TOO_SMALL;
		goto out;
	    }

	    rv = sc_pkcs11_md_update(session, pData, ulDataLen);
	}
	if (rv == CKR_OK)
		rv = sc_pkcs11_md_final(session, pDigest, pulDigestLen);

out:
	SC_LOG_RV("C_Digest = %s", rv);
	sc_pkcs11_unlock();
	return rv;
}


CK_RV
C_DigestUpdate(CK_SESSION_HANDLE hSession,	/* the session's handle */
		CK_BYTE_PTR pPart,		/* data to be digested */
		CK_ULONG ulPartLen)		/* bytes of data to be digested */
{
	CK_RV rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv == CKR_OK)
		rv = sc_pkcs11_md_update(session, pPart, ulPartLen);

	SC_LOG_RV("C_DigestUpdate() = %s", rv);
	sc_pkcs11_unlock();
	return rv;
}


CK_RV
C_DigestKey(CK_SESSION_HANDLE hSession,	/* the session's handle */
		CK_OBJECT_HANDLE hKey)	/* handle of secret key to digest */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}


CK_RV
C_DigestFinal(CK_SESSION_HANDLE hSession,	/* the session's handle */
		CK_BYTE_PTR pDigest,		/* receives the message digest */
		CK_ULONG_PTR pulDigestLen)	/* receives byte count of digest */
{
	CK_RV rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv == CKR_OK)
		rv = sc_pkcs11_md_final(session, pDigest, pulDigestLen);

	SC_LOG_RV("C_DigestFinal() = %s", rv);
	sc_pkcs11_unlock();
	return rv;
}


CK_RV
C_SignInit(CK_SESSION_HANDLE hSession,		/* the session's handle */
		CK_MECHANISM_PTR pMechanism,	/* the signature mechanism */
		CK_OBJECT_HANDLE hKey)		/* handle of the signature key */
{
	CK_BBOOL can_sign;
	CK_KEY_TYPE key_type;
	CK_ATTRIBUTE sign_attribute = { CKA_SIGN, &can_sign, sizeof(can_sign) };
	CK_ATTRIBUTE key_type_attr = { CKA_KEY_TYPE, &key_type, sizeof(key_type) };
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;
	CK_RV rv;

	if (pMechanism == NULL_PTR)
		return CKR_ARGUMENTS_BAD;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_object_from_session(hSession, hKey, &session, &object);
	if (rv != CKR_OK) {
		if (rv == CKR_OBJECT_HANDLE_INVALID)
			rv = CKR_KEY_HANDLE_INVALID;
		goto out;
	}

	if (object->ops->sign == NULL_PTR) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}

	rv = object->ops->get_attribute(session, object, &sign_attribute);
	if (rv != CKR_OK || !can_sign) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}
	rv = object->ops->get_attribute(session, object, &key_type_attr);
	if (rv != CKR_OK) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}

	rv = sc_pkcs11_sign_init(session, pMechanism, object, key_type);

out:
	SC_LOG_RV("C_SignInit() = %s", rv);
	sc_pkcs11_unlock();
	return rv;
}


CK_RV
C_Sign(CK_SESSION_HANDLE hSession,		/* the session's handle */
		CK_BYTE_PTR pData,		/* the data (digest) to be signed */
		CK_ULONG ulDataLen,		/* count of bytes to be signed */
		CK_BYTE_PTR pSignature,		/* receives the signature */
		CK_ULONG_PTR pulSignatureLen)	/* receives byte count of signature */
{
	CK_RV rv;
	struct sc_pkcs11_session *session;
	CK_ULONG length;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	/* According to the pkcs11 specs, we must not do any calls that
	 * change our crypto state if the caller is just asking for the
	 * signature buffer size, or if the result would be
	 * CKR_BUFFER_TOO_SMALL. Thus we cannot do the sign_update call
	 * below. */
	if ((rv = sc_pkcs11_sign_size(session, &length)) != CKR_OK)
		goto out;

	if (pSignature == NULL || length > *pulSignatureLen) {
		*pulSignatureLen = length;
		rv = pSignature ? CKR_BUFFER_TOO_SMALL : CKR_OK;
		goto out;
	}

	rv = sc_pkcs11_sign_update(session, pData, ulDataLen);
	if (rv == CKR_OK) {
		rv = restore_login_state(session->slot);
		if (rv == CKR_OK)
			rv = sc_pkcs11_sign_final(session, pSignature, pulSignatureLen);
		rv = reset_login_state(session->slot, rv);
	}

out:
	SC_LOG_RV("C_Sign() = %s", rv);
	sc_pkcs11_unlock();
	return rv;
}


CK_RV
C_SignUpdate(CK_SESSION_HANDLE hSession,	/* the session's handle */
		CK_BYTE_PTR pPart,		/* the data (digest) to be signed */
		CK_ULONG ulPartLen)		/* count of bytes to be signed */
{
	CK_RV rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv == CKR_OK)
		rv = sc_pkcs11_sign_update(session, pPart, ulPartLen);

	SC_LOG_RV("C_SignUpdate() = %s", rv);
	sc_pkcs11_unlock();
	return rv;
}


CK_RV
C_SignFinal(CK_SESSION_HANDLE hSession,		/* the session's handle */
		CK_BYTE_PTR pSignature,		/* receives the signature */
		CK_ULONG_PTR pulSignatureLen)	/* receives byte count of signature */
{
	struct sc_pkcs11_session *session;
	CK_ULONG length;
	CK_RV rv;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	/* According to the pkcs11 specs, we must not do any calls that
	 * change our crypto state if the caller is just asking for the
	 * signature buffer size, or if the result would be
	 * CKR_BUFFER_TOO_SMALL.
	 */
	if ((rv = sc_pkcs11_sign_size(session, &length)) != CKR_OK)
		goto out;

	if (pSignature == NULL || length > *pulSignatureLen) {
		*pulSignatureLen = length;
		rv = pSignature ? CKR_BUFFER_TOO_SMALL : CKR_OK;
	} else {
		rv = restore_login_state(session->slot);
		if (rv == CKR_OK)
			rv = sc_pkcs11_sign_final(session, pSignature, pulSignatureLen);
		rv = reset_login_state(session->slot, rv);
	}

out:
	SC_LOG_RV("C_SignFinal() = %s", rv);
	sc_pkcs11_unlock();
	return rv;
}


CK_RV
C_SignRecoverInit(CK_SESSION_HANDLE hSession,	/* the session's handle */
		CK_MECHANISM_PTR pMechanism,	/* the signature mechanism */
		CK_OBJECT_HANDLE hKey)		/* handle of the signature key */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}


CK_RV
C_SignRecover(CK_SESSION_HANDLE hSession,	/* the session's handle */
		CK_BYTE_PTR pData,		/* the data (digest) to be signed */
		CK_ULONG ulDataLen,		/* count of bytes to be signed */
		CK_BYTE_PTR pSignature,		/* receives the signature */
		CK_ULONG_PTR pulSignatureLen)	/* receives byte count of signature */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}


CK_RV
C_EncryptInit(CK_SESSION_HANDLE hSession,	/* the session's handle */
		CK_MECHANISM_PTR pMechanism,	/* the encryption mechanism */
		CK_OBJECT_HANDLE hKey)		/* handle of encryption key */
{
	CK_BBOOL can_encrypt;
	CK_KEY_TYPE key_type;
	CK_ATTRIBUTE encrypt_attribute = {CKA_ENCRYPT, &can_encrypt, sizeof(can_encrypt)};
	CK_ATTRIBUTE key_type_attr = {CKA_KEY_TYPE, &key_type, sizeof(key_type)};
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;
	CK_RV rv;

	if (pMechanism == NULL_PTR)
		return CKR_ARGUMENTS_BAD;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_object_from_session(hSession, hKey, &session, &object);
	if (rv != CKR_OK) {
		if (rv == CKR_OBJECT_HANDLE_INVALID)
			rv = CKR_KEY_HANDLE_INVALID;
		goto out;
	}

	if (object->ops->encrypt == NULL_PTR) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}

	rv = object->ops->get_attribute(session, object, &encrypt_attribute);
	if (rv != CKR_OK || !can_encrypt) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}
	rv = object->ops->get_attribute(session, object, &key_type_attr);
	if (rv != CKR_OK) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}
	rv = sc_pkcs11_encr_init(session, pMechanism, object, key_type);
out:
	SC_LOG_RV("C_EncryptInit() = %s", rv);
	sc_pkcs11_unlock();
	return rv;
}


CK_RV C_Encrypt(CK_SESSION_HANDLE hSession,	/* the session's handle */
		CK_BYTE_PTR pData,	/* the plaintext data */
		CK_ULONG ulDataLen,	/* bytes of plaintext data */
		CK_BYTE_PTR pEncryptedData,	/* receives encrypted data */
		CK_ULONG_PTR pulEncryptedDataLen)
{				/* receives encrypted byte count */
	CK_RV rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv == CKR_OK) {
		rv = restore_login_state(session->slot);
		if (rv == CKR_OK)
			rv = sc_pkcs11_encr(session, pData, ulDataLen, pEncryptedData, pulEncryptedDataLen);
		rv = reset_login_state(session->slot, rv);
	}

	SC_LOG_RV("C_Encrypt() = %s", rv);
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_EncryptUpdate(CK_SESSION_HANDLE hSession,	/* the session's handle */
		      CK_BYTE_PTR pPart,	/* the plaintext data */
		      CK_ULONG ulPartLen,	/* bytes of plaintext data */
		      CK_BYTE_PTR pEncryptedPart,	/* receives encrypted data */
		      CK_ULONG_PTR pulEncryptedPartLen)
{				/* receives encrypted byte count */
	CK_RV rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv == CKR_OK)
		rv = sc_pkcs11_encr_update(session, pPart, ulPartLen,
				pEncryptedPart, pulEncryptedPartLen);

	SC_LOG_RV("C_EncryptUpdate() = %s", rv);
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_EncryptFinal(CK_SESSION_HANDLE hSession,	/* the session's handle */
		     CK_BYTE_PTR pLastEncryptedPart,	/* receives encrypted last part */
		     CK_ULONG_PTR pulLastEncryptedPartLen)
{				/* receives byte count */
	CK_RV rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv == CKR_OK) {
		rv = restore_login_state(session->slot);
		if (rv == CKR_OK)
			rv = sc_pkcs11_encr_final(session, pLastEncryptedPart, pulLastEncryptedPartLen);
		rv = reset_login_state(session->slot, rv);
	}

	SC_LOG_RV("C_EncryptFinal() = %s", rv);
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_DecryptInit(CK_SESSION_HANDLE hSession,	/* the session's handle */
		    CK_MECHANISM_PTR pMechanism,	/* the decryption mechanism */
		    CK_OBJECT_HANDLE hKey)
{				/* handle of the decryption key */
	CK_BBOOL can_decrypt, can_unwrap;
	CK_KEY_TYPE key_type;
	CK_ATTRIBUTE decrypt_attribute = { CKA_DECRYPT,	&can_decrypt,	sizeof(can_decrypt) };
	CK_ATTRIBUTE key_type_attr = { CKA_KEY_TYPE,	&key_type,	sizeof(key_type) };
	CK_ATTRIBUTE unwrap_attribute = { CKA_UNWRAP,	&can_unwrap,	sizeof(can_unwrap) };
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;
	CK_RV rv;

	if (pMechanism == NULL_PTR)
		return CKR_ARGUMENTS_BAD;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_object_from_session(hSession, hKey, &session, &object);
	if (rv != CKR_OK) {
		if (rv == CKR_OBJECT_HANDLE_INVALID)
			rv = CKR_KEY_HANDLE_INVALID;
		goto out;
	}

	if (object->ops->decrypt == NULL_PTR) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}

	rv = object->ops->get_attribute(session, object, &decrypt_attribute);
	if (rv != CKR_OK || !can_decrypt) {
		/* Also accept UNWRAP - apps call Decrypt when they mean Unwrap */
		rv = object->ops->get_attribute(session, object, &unwrap_attribute);
		if (rv != CKR_OK || !can_unwrap) {
			rv = CKR_KEY_TYPE_INCONSISTENT;
			goto out;
		}
	}
	rv = object->ops->get_attribute(session, object, &key_type_attr);
	if (rv != CKR_OK) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}

	rv = sc_pkcs11_decr_init(session, pMechanism, object, key_type);

out:
	SC_LOG_RV("C_DecryptInit() = %s", rv);
	sc_pkcs11_unlock();
	return rv;
}

CK_RV
C_Decrypt(CK_SESSION_HANDLE hSession,	     /* the session's handle */
		CK_BYTE_PTR pEncryptedData,  /* input encrypted data */
		CK_ULONG ulEncryptedDataLen, /* count of bytes of input */
		CK_BYTE_PTR pData,	     /* receives decrypted output */
		CK_ULONG_PTR pulDataLen)     /* receives decrypted byte count */
{
	CK_RV rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv == CKR_OK) {
		rv = restore_login_state(session->slot);
		if (rv == CKR_OK) {
			rv = sc_pkcs11_decr(session, pEncryptedData,
					ulEncryptedDataLen, pData, pulDataLen);
		}
		rv = reset_login_state(session->slot, rv);
	}

	/* do not log error code to prevent side channel attack */
	SC_LOG("C_Decrypt()");
	sc_pkcs11_unlock();
	return rv;
}

CK_RV
C_DecryptUpdate(CK_SESSION_HANDLE hSession,  /* the session's handle */
		CK_BYTE_PTR pEncryptedPart,  /* input encrypted data */
		CK_ULONG ulEncryptedPartLen, /* count of bytes of input */
		CK_BYTE_PTR pPart,	     /* receives decrypted output */
		CK_ULONG_PTR pulPartLen)     /* receives decrypted byte count */
{
	CK_RV rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv == CKR_OK)
		rv = sc_pkcs11_decr_update(session, pEncryptedPart, ulEncryptedPartLen,
				pPart, pulPartLen);

	/* do not log error code to prevent side channel attack */
	SC_LOG("C_DecryptUpdate()");
	sc_pkcs11_unlock();
	return rv;
}

CK_RV
C_DecryptFinal(CK_SESSION_HANDLE hSession,   /* the session's handle */
		CK_BYTE_PTR pLastPart,	     /* receives decrypted output */
		CK_ULONG_PTR pulLastPartLen) /* receives decrypted byte count */
{
	CK_RV rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv == CKR_OK) {
		rv = restore_login_state(session->slot);
		if (rv == CKR_OK) {
			rv = sc_pkcs11_decr_final(session,
					pLastPart,
					pulLastPartLen);
		}
		rv = reset_login_state(session->slot, rv);
	}

	/* do not log error code to prevent side channel attack */
	SC_LOG("C_DecryptFinal()");
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_DigestEncryptUpdate(CK_SESSION_HANDLE hSession,	/* the session's handle */
			    CK_BYTE_PTR pPart,	/* the plaintext data */
			    CK_ULONG ulPartLen,	/* bytes of plaintext data */
			    CK_BYTE_PTR pEncryptedPart,	/* receives encrypted data */
			    CK_ULONG_PTR pulEncryptedPartLen)
{				/* receives encrypted byte count */
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DecryptDigestUpdate(CK_SESSION_HANDLE hSession,	/* the session's handle */
			    CK_BYTE_PTR pEncryptedPart,	/* input encrypted data */
			    CK_ULONG ulEncryptedPartLen,	/* count of bytes of input */
			    CK_BYTE_PTR pPart,	/* receives decrypted output */
			    CK_ULONG_PTR pulPartLen)
{				/* receives decrypted byte count */
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_SignEncryptUpdate(CK_SESSION_HANDLE hSession,	/* the session's handle */
			  CK_BYTE_PTR pPart,	/* the plaintext data */
			  CK_ULONG ulPartLen,	/* bytes of plaintext data */
			  CK_BYTE_PTR pEncryptedPart,	/* receives encrypted data */
			  CK_ULONG_PTR pulEncryptedPartLen)
{				/* receives encrypted byte count */
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession,	/* the session's handle */
			    CK_BYTE_PTR pEncryptedPart,	/* input encrypted data */
			    CK_ULONG ulEncryptedPartLen,	/* count of byes of input */
			    CK_BYTE_PTR pPart,	/* receives decrypted output */
			    CK_ULONG_PTR pulPartLen)
{				/* receives decrypted byte count */
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_GenerateKey(CK_SESSION_HANDLE hSession,	/* the session's handle */
		    CK_MECHANISM_PTR pMechanism,	/* the key generation mechanism */
		    CK_ATTRIBUTE_PTR pTemplate,	/* template for the new key */
		    CK_ULONG ulCount,	/* number of attributes in template */
		    CK_OBJECT_HANDLE_PTR phKey)
{				/* receives handle of new key */
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession,	/* the session's handle */
			CK_MECHANISM_PTR pMechanism,	/* the key gen. mech. */
			CK_ATTRIBUTE_PTR pPublicKeyTemplate,	/* pub. attr. template */
			CK_ULONG ulPublicKeyAttributeCount,	/* # of pub. attrs. */
			CK_ATTRIBUTE_PTR pPrivateKeyTemplate,	/* priv. attr. template */
			CK_ULONG ulPrivateKeyAttributeCount,	/* # of priv. attrs. */
			CK_OBJECT_HANDLE_PTR phPublicKey,	/* gets pub. key handle */
			CK_OBJECT_HANDLE_PTR phPrivateKey)
{				/* gets priv. key handle */
	CK_RV rv;
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_slot *slot;

	if (pMechanism == NULL_PTR
			|| (pPublicKeyTemplate == NULL_PTR && ulPublicKeyAttributeCount > 0)
			|| (pPrivateKeyTemplate == NULL_PTR && ulPrivateKeyAttributeCount > 0))
		return CKR_ARGUMENTS_BAD;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	dump_template(SC_LOG_DEBUG_NORMAL, "C_GenerateKeyPair(), PrivKey attrs", pPrivateKeyTemplate, ulPrivateKeyAttributeCount);
	dump_template(SC_LOG_DEBUG_NORMAL, "C_GenerateKeyPair(), PubKey attrs", pPublicKeyTemplate, ulPublicKeyAttributeCount);

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	if (!(session->flags & CKF_RW_SESSION)) {
		rv = CKR_SESSION_READ_ONLY;
		goto out;
	}

	slot = session->slot;
	if (slot == NULL || slot->p11card == NULL || slot->p11card->framework == NULL
			|| slot->p11card->framework->gen_keypair == NULL)
		rv = CKR_FUNCTION_NOT_SUPPORTED;
	else {
		rv = restore_login_state(slot);
		if (rv == CKR_OK)
			rv = slot->p11card->framework->gen_keypair(slot, pMechanism,
					pPublicKeyTemplate, ulPublicKeyAttributeCount,
					pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
					phPublicKey, phPrivateKey);
		rv = reset_login_state(session->slot, rv);
	}

out:
	sc_pkcs11_unlock();
	return rv;
}


CK_RV C_WrapKey(CK_SESSION_HANDLE hSession,	/* the session's handle */
		CK_MECHANISM_PTR pMechanism,	/* the wrapping mechanism */
		CK_OBJECT_HANDLE hWrappingKey,	/* handle of the wrapping key */
		CK_OBJECT_HANDLE hKey,	/* handle of the key to be wrapped */
		CK_BYTE_PTR pWrappedKey,	/* receives the wrapped key */
		CK_ULONG_PTR pulWrappedKeyLen)
{				/* receives byte size of wrapped key */
	CK_RV rv;
	CK_BBOOL can_wrap,
			 can_be_wrapped;
	CK_KEY_TYPE key_type;
	CK_ATTRIBUTE wrap_attribute = { CKA_WRAP, &can_wrap, sizeof(can_wrap) };
	CK_ATTRIBUTE extractable_attribute = { CKA_EXTRACTABLE, &can_be_wrapped, sizeof(can_be_wrapped) };
	CK_ATTRIBUTE key_type_attr = { CKA_KEY_TYPE, &key_type, sizeof(key_type) };
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *wrapping_object;
	struct sc_pkcs11_object *key_object;

	if (pMechanism == NULL_PTR)
		return CKR_ARGUMENTS_BAD;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	/* Check if the wrapping key is OK to do wrapping */
	rv = get_object_from_session(hSession, hWrappingKey, &session, &wrapping_object);
	if (rv != CKR_OK) {
		if (rv == CKR_OBJECT_HANDLE_INVALID)
			rv = CKR_KEY_HANDLE_INVALID;
		goto out;
	}
	if (wrapping_object->ops->wrap_key == NULL_PTR) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}

	rv = wrapping_object->ops->get_attribute(session, wrapping_object, &wrap_attribute);
	if (rv != CKR_OK || !can_wrap) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}
	rv = wrapping_object->ops->get_attribute(session, wrapping_object, &key_type_attr);
	if (rv != CKR_OK) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}

	/* Check if the key to be wrapped exists and is extractable*/
	rv = get_object_from_session(hSession, hKey, &session, &key_object);
	if (rv != CKR_OK) {
		if (rv == CKR_OBJECT_HANDLE_INVALID)
			rv = CKR_KEY_HANDLE_INVALID;
		goto out;
	}

	rv = key_object->ops->get_attribute(session, key_object, &extractable_attribute);
	if (rv != CKR_OK || !can_be_wrapped) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}

	rv = restore_login_state(session->slot);
	if (rv == CKR_OK)
		rv = sc_pkcs11_wrap(session, pMechanism, wrapping_object, key_type,
				key_object, pWrappedKey, pulWrappedKeyLen);

	rv = reset_login_state(session->slot, rv);

out:
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_UnwrapKey(CK_SESSION_HANDLE hSession,	/* the session's handle */
		  CK_MECHANISM_PTR pMechanism,	/* the unwrapping mechanism */
		  CK_OBJECT_HANDLE hUnwrappingKey,	/* handle of the unwrapping key */
		  CK_BYTE_PTR pWrappedKey,	/* the wrapped key */
		  CK_ULONG ulWrappedKeyLen,	/* bytes length of wrapped key */
		  CK_ATTRIBUTE_PTR pTemplate,	/* template for the new key */
		  CK_ULONG ulAttributeCount,	/* # of attributes in template */
		  CK_OBJECT_HANDLE_PTR phKey)
{				/* gets handle of recovered key */
	CK_RV rv;
	CK_BBOOL can_unwrap;
	CK_KEY_TYPE key_type;
	CK_ATTRIBUTE unwrap_attribute = { CKA_UNWRAP, &can_unwrap, sizeof(can_unwrap) };
	CK_ATTRIBUTE key_type_attr = { CKA_KEY_TYPE, &key_type, sizeof(key_type) };
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;
	struct sc_pkcs11_object *key_object;

	if (pMechanism == NULL_PTR)
		return CKR_ARGUMENTS_BAD;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_object_from_session(hSession, hUnwrappingKey, &session, &object);
	if (rv != CKR_OK) {
		if (rv == CKR_OBJECT_HANDLE_INVALID)
			rv = CKR_KEY_HANDLE_INVALID;
		goto out;
	}
	if (object->ops->unwrap_key == NULL_PTR) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}

	rv = object->ops->get_attribute(session, object, &unwrap_attribute);
	if (rv != CKR_OK || !can_unwrap) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}
	rv = object->ops->get_attribute(session, object, &key_type_attr);
	if (rv != CKR_OK) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}

	/* Create the target object in memory */
	rv = sc_create_object_int(hSession, pTemplate, ulAttributeCount, phKey, 0);

	if (rv != CKR_OK)
	    goto out;

	rv = get_object_from_session(hSession, *phKey, &session, &key_object);
	if (rv != CKR_OK) {
		if (rv == CKR_OBJECT_HANDLE_INVALID)
			rv = CKR_KEY_HANDLE_INVALID;
		goto out;
	}

	rv = restore_login_state(session->slot);
	if (rv == CKR_OK)
		rv = sc_pkcs11_unwrap(session, pMechanism, object, key_type,
				pWrappedKey, ulWrappedKeyLen, key_object);
	/* TODO if (rv != CK_OK) need to destroy the object */
	rv = reset_login_state(session->slot, rv);

out:
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession,	/* the session's handle */
		  CK_MECHANISM_PTR pMechanism,	/* the key derivation mechanism */
		  CK_OBJECT_HANDLE hBaseKey,	/* handle of the base key */
		  CK_ATTRIBUTE_PTR pTemplate,	/* template for the new key */
		  CK_ULONG ulAttributeCount,	/* # of attributes in template */
		  CK_OBJECT_HANDLE_PTR phKey)	/* gets handle of derived key */
{
/* TODO: -DEE ECDH with Cofactor  on PIV is an example */
/* TODO: need to do a lot of checking, will only support ECDH for now.*/
	CK_RV rv;
	CK_BBOOL can_derive;
	CK_KEY_TYPE key_type;
	CK_ATTRIBUTE derive_attribute = { CKA_DERIVE, &can_derive, sizeof(can_derive) };
	CK_ATTRIBUTE key_type_attr = { CKA_KEY_TYPE, &key_type, sizeof(key_type) };
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;
	struct sc_pkcs11_object *key_object;

	if (pMechanism == NULL_PTR)
		return CKR_ARGUMENTS_BAD;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_object_from_session(hSession, hBaseKey, &session, &object);
	if (rv != CKR_OK) {
		if (rv == CKR_OBJECT_HANDLE_INVALID)
			rv = CKR_KEY_HANDLE_INVALID;
		goto out;
	}

	if (object->ops->derive == NULL_PTR) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}

	rv = object->ops->get_attribute(session, object, &derive_attribute);
	if (rv != CKR_OK || !can_derive) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}
	rv = object->ops->get_attribute(session, object, &key_type_attr);
	if (rv != CKR_OK) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}
	/* TODO DEE Should also check SENSITIVE, ALWAYS_SENSITIVE, EXTRACTABLE,
	   NEVER_EXTRACTABLE of the BaseKey against the template for the newkey.
	*/

	switch(key_type) {
	    case CKK_EC:
	    case CKK_EC_MONTGOMERY:

		rv = sc_create_object_int(hSession, pTemplate, ulAttributeCount, phKey, 0);
		if (rv != CKR_OK)
		    goto out;

		rv = get_object_from_session(hSession, *phKey, &session, &key_object);
		if (rv != CKR_OK) {
			if (rv == CKR_OBJECT_HANDLE_INVALID)
				rv = CKR_KEY_HANDLE_INVALID;
			goto out;
		}

		rv = restore_login_state(session->slot);
		if (rv == CKR_OK)
			rv = sc_pkcs11_deri(session, pMechanism, object, key_type,
					hSession, *phKey, key_object);
		/* TODO if (rv != CK_OK) need to destroy the object */
		rv = reset_login_state(session->slot, rv);

		break;
	    default:
		rv = CKR_KEY_TYPE_INCONSISTENT;
	}

out:
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_SeedRandom(CK_SESSION_HANDLE hSession,	/* the session's handle */
		   CK_BYTE_PTR pSeed,	/* the seed material */
		   CK_ULONG ulSeedLen)
{				/* count of bytes of seed material */
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_GenerateRandom(CK_SESSION_HANDLE hSession,	/* the session's handle */
		       CK_BYTE_PTR RandomData,	/* receives the random data */
		       CK_ULONG ulRandomLen)
{				/* number of bytes to be generated */
	CK_RV rv;
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_slot *slot;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv == CKR_OK) {
		slot = session->slot;
		if (slot == NULL || slot->p11card == NULL || slot->p11card->framework == NULL
				|| slot->p11card->framework->get_random == NULL)
			rv = CKR_RANDOM_NO_RNG;
		else
			rv = slot->p11card->framework->get_random(slot, RandomData, ulRandomLen);
	}

	SC_LOG_RV("C_GenerateRandom() = %s", rv);
	sc_pkcs11_unlock();
	return rv;
}

CK_RV C_GetFunctionStatus(CK_SESSION_HANDLE hSession)
{				/* the session's handle */
	return CKR_FUNCTION_NOT_PARALLEL;
}

CK_RV C_CancelFunction(CK_SESSION_HANDLE hSession)
{				/* the session's handle */
	return CKR_FUNCTION_NOT_PARALLEL;
}

CK_RV C_VerifyInit(CK_SESSION_HANDLE hSession,	/* the session's handle */
		   CK_MECHANISM_PTR pMechanism,	/* the verification mechanism */
		   CK_OBJECT_HANDLE hKey)
{				/* handle of the verification key */
#ifndef ENABLE_OPENSSL
	return CKR_FUNCTION_NOT_SUPPORTED;
#else
	CK_KEY_TYPE key_type;
	CK_ATTRIBUTE key_type_attr = { CKA_KEY_TYPE, &key_type, sizeof(key_type) };
	CK_RV rv;
	struct sc_pkcs11_session *session;
	struct sc_pkcs11_object *object;

	if (pMechanism == NULL_PTR)
		return CKR_ARGUMENTS_BAD;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;


	rv = get_object_from_session(hSession, hKey, &session, &object);
	if (rv != CKR_OK) {
		if (rv == CKR_OBJECT_HANDLE_INVALID)
			rv = CKR_KEY_HANDLE_INVALID;
		goto out;
	}
	rv = object->ops->get_attribute(session, object, &key_type_attr);
	if (rv != CKR_OK) {
		rv = CKR_KEY_TYPE_INCONSISTENT;
		goto out;
	}

	rv = sc_pkcs11_verif_init(session, pMechanism, object, key_type);

out:
	SC_LOG_RV("C_VerifyInit() = %s", rv);
	sc_pkcs11_unlock();
	return rv;
#endif
}

CK_RV C_Verify(CK_SESSION_HANDLE hSession,	/* the session's handle */
	       CK_BYTE_PTR pData,	/* plaintext data (digest) to compare */
	       CK_ULONG ulDataLen,	/* length of data (digest) in bytes */
	       CK_BYTE_PTR pSignature,	/* the signature to be verified */
	       CK_ULONG ulSignatureLen)
{				/* count of bytes of signature */
#ifndef ENABLE_OPENSSL
	return CKR_FUNCTION_NOT_SUPPORTED;
#else
	CK_RV rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv != CKR_OK)
		goto out;

	rv = sc_pkcs11_verif_update(session, pData, ulDataLen);
	if (rv == CKR_OK) {
		rv = restore_login_state(session->slot);
		if (rv == CKR_OK)
			rv = sc_pkcs11_verif_final(session, pSignature, ulSignatureLen);
		rv = reset_login_state(session->slot, rv);
	}

out:
	SC_LOG_RV("C_Verify() = %s", rv);
	sc_pkcs11_unlock();
	return rv;
#endif
}

CK_RV C_VerifyUpdate(CK_SESSION_HANDLE hSession,	/* the session's handle */
		     CK_BYTE_PTR pPart,	/* plaintext data (digest) to compare */
		     CK_ULONG ulPartLen)
{				/* length of data (digest) in bytes */
#ifndef ENABLE_OPENSSL
	return CKR_FUNCTION_NOT_SUPPORTED;
#else
	CK_RV rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv == CKR_OK)
		rv = sc_pkcs11_verif_update(session, pPart, ulPartLen);

	SC_LOG_RV("C_VerifyUpdate() = %s", rv);
	sc_pkcs11_unlock();
	return rv;
#endif
}

CK_RV C_VerifyFinal(CK_SESSION_HANDLE hSession,	/* the session's handle */
		    CK_BYTE_PTR pSignature,	/* the signature to be verified */
		    CK_ULONG ulSignatureLen)
{				/* count of bytes of signature */
#ifndef ENABLE_OPENSSL
	return CKR_FUNCTION_NOT_SUPPORTED;
#else
	CK_RV rv;
	struct sc_pkcs11_session *session;

	rv = sc_pkcs11_lock();
	if (rv != CKR_OK)
		return rv;

	rv = get_session(hSession, &session);
	if (rv == CKR_OK) {
		rv = restore_login_state(session->slot);
		if (rv == CKR_OK)
			rv = sc_pkcs11_verif_final(session, pSignature, ulSignatureLen);
		rv = reset_login_state(session->slot, rv);
	}

	SC_LOG_RV("C_VerifyFinal() = %s", rv);
	sc_pkcs11_unlock();
	return rv;
#endif
}

CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE hSession,	/* the session's handle */
			  CK_MECHANISM_PTR pMechanism,	/* the verification mechanism */
			  CK_OBJECT_HANDLE hKey)
{				/* handle of the verification key */
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_VerifyRecover(CK_SESSION_HANDLE hSession,	/* the session's handle */
		      CK_BYTE_PTR pSignature,	/* the signature to be verified */
		      CK_ULONG ulSignatureLen,	/* count of bytes of signature */
		      CK_BYTE_PTR pData,	/* receives decrypted data (digest) */
		      CK_ULONG_PTR pulDataLen)
{				/* receives byte count of data */
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/* PKCS #11 3.0 only */
CK_RV C_MessageEncryptInit(CK_SESSION_HANDLE hSession,    /* the session's handle */
			   CK_MECHANISM_PTR pMechanism,  /* the encryption mechanism */
			   CK_OBJECT_HANDLE hKey)         /* handle of encryption key */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_EncryptMessage(CK_SESSION_HANDLE hSession,   /* the session's handle */
		       CK_VOID_PTR pParameter,       /* message specific parameter */
		       CK_ULONG ulParameterLen,      /* length of message specific parameter */
		       CK_BYTE_PTR pAssociatedData,  /* AEAD Associated data */
		       CK_ULONG ulAssociatedDataLen, /* AEAD Associated data length */
		       CK_BYTE_PTR pPlaintext,       /* plain text  */
		       CK_ULONG ulPlaintextLen,      /* plain text length */
		       CK_BYTE_PTR pCiphertext,      /* gets cipher text */
		       CK_ULONG_PTR pulCiphertextLen) /* gets cipher text length */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_EncryptMessageBegin(CK_SESSION_HANDLE hSession,   /* the session's handle */
			    CK_VOID_PTR pParameter,       /* message specific parameter */
			    CK_ULONG ulParameterLen,      /* length of message specific parameter */
			    CK_BYTE_PTR pAssociatedData,  /* AEAD Associated data */
			    CK_ULONG ulAssociatedDataLen)  /* AEAD Associated data length */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_EncryptMessageNext(CK_SESSION_HANDLE hSession,        /* the session's handle */
			   CK_VOID_PTR pParameter,            /* message specific parameter */
			   CK_ULONG ulParameterLen,           /* length of message specific parameter */
			   CK_BYTE_PTR pPlaintextPart,        /* plain text */
			   CK_ULONG ulPlaintextPartLen,       /* plain text length */
			   CK_BYTE_PTR pCiphertextPart,       /* gets cipher text */
			   CK_ULONG_PTR pulCiphertextPartLen, /* gets cipher text length */
			   CK_FLAGS flags)                     /* multi mode flag */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_MessageEncryptFinal(CK_SESSION_HANDLE hSession)        /* the session's handle */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_MessageDecryptInit(CK_SESSION_HANDLE hSession,    /* the session's handle */
			   CK_MECHANISM_PTR pMechanism,  /* the decryption mechanism */
			   CK_OBJECT_HANDLE hKey)         /* handle of decryption key */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DecryptMessage(CK_SESSION_HANDLE hSession,    /* the session's handle */
		       CK_VOID_PTR pParameter,       /* message specific parameter */
		       CK_ULONG ulParameterLen,      /* length of message specific parameter */
		       CK_BYTE_PTR pAssociatedData,  /* AEAD Associated data */
		       CK_ULONG ulAssociatedDataLen, /* AEAD Associated data length */
		       CK_BYTE_PTR pCiphertext,      /* cipher text */
		       CK_ULONG ulCiphertextLen,     /* cipher text length */
		       CK_BYTE_PTR pPlaintext,       /* gets plain text */
		       CK_ULONG_PTR pulPlaintextLen)  /* gets plain text length */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DecryptMessageBegin(CK_SESSION_HANDLE hSession,    /* the session's handle */
			    CK_VOID_PTR pParameter,       /* message specific parameter */
			    CK_ULONG ulParameterLen,      /* length of message specific parameter */
			    CK_BYTE_PTR pAssociatedData,  /* AEAD Associated data */
			    CK_ULONG ulAssociatedDataLen)  /* AEAD Associated data length */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_DecryptMessageNext(CK_SESSION_HANDLE hSession,    /* the session's handle */
			   CK_VOID_PTR pParameter,            /* message specific parameter */
			   CK_ULONG ulParameterLen,           /* length of message specific parameter */
			   CK_BYTE_PTR pCiphertextPart,       /* cipher text */
			   CK_ULONG ulCiphertextPartLen,      /* cipher text length */
			   CK_BYTE_PTR pPlaintextPart,        /* gets plain text */
			   CK_ULONG_PTR pulPlaintextPartLen,  /* gets plain text length */
			   CK_FLAGS flags)                     /* multi mode flag */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_MessageDecryptFinal(CK_SESSION_HANDLE hSession)    /* the session's handle */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_MessageSignInit(CK_SESSION_HANDLE hSession,    /* the session's handle */
			CK_MECHANISM_PTR pMechanism,  /* the signing mechanism */
			CK_OBJECT_HANDLE hKey)         /* handle of signing key */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_SignMessage(CK_SESSION_HANDLE hSession,    /* the session's handle */
		    CK_VOID_PTR pParameter,       /* message specific parameter */
		    CK_ULONG ulParameterLen,      /* length of message specific parameter */
		    CK_BYTE_PTR pData,            /* data to sign */
		    CK_ULONG ulDataLen,           /* data to sign length */
		    CK_BYTE_PTR pSignature,       /* gets signature */
		    CK_ULONG_PTR pulSignatureLen)  /* gets signature length */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_SignMessageBegin(CK_SESSION_HANDLE hSession,    /* the session's handle */
			 CK_VOID_PTR pParameter,       /* message specific parameter */
			 CK_ULONG ulParameterLen)      /* length of message specific parameter */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_SignMessageNext(CK_SESSION_HANDLE hSession,    /* the session's handle */
			CK_VOID_PTR pParameter,       /* message specific parameter */
			CK_ULONG ulParameterLen,      /* length of message specific parameter */
			CK_BYTE_PTR pData,            /* data to sign */
			CK_ULONG ulDataLen,           /* data to sign length */
			CK_BYTE_PTR pSignature,       /* gets signature */
			CK_ULONG_PTR pulSignatureLen)  /* gets signature length */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_MessageSignFinal(CK_SESSION_HANDLE hSession)    /* the session's handle */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_MessageVerifyInit(CK_SESSION_HANDLE hSession,    /* the session's handle */
			  CK_MECHANISM_PTR  pMechanism,  /* the signing mechanism */
			  CK_OBJECT_HANDLE  hKey)         /* handle of signing key */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_VerifyMessage(CK_SESSION_HANDLE hSession,    /* the session's handle */
		      CK_VOID_PTR pParameter,       /* message specific parameter */
		      CK_ULONG ulParameterLen,      /* length of message specific parameter */
		      CK_BYTE_PTR pData,            /* data to sign */
		      CK_ULONG ulDataLen,           /* data to sign length */
		      CK_BYTE_PTR pSignature,       /* signature */
		      CK_ULONG ulSignatureLen)       /* signature length */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_VerifyMessageBegin(CK_SESSION_HANDLE hSession,    /* the session's handle */
			   CK_VOID_PTR pParameter,       /* message specific parameter */
			   CK_ULONG ulParameterLen)      /* length of message specific parameter */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_VerifyMessageNext(CK_SESSION_HANDLE hSession,    /* the session's handle */
			  CK_VOID_PTR pParameter,       /* message specific parameter */
			  CK_ULONG ulParameterLen,      /* length of message specific parameter */
			  CK_BYTE_PTR pData,            /* data to sign */
			  CK_ULONG ulDataLen,           /* data to sign length */
			  CK_BYTE_PTR pSignature,       /* signature */
			  CK_ULONG ulSignatureLen)       /* signature length */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

CK_RV C_MessageVerifyFinal(CK_SESSION_HANDLE hSession)    /* the session's handle */
{
	return CKR_FUNCTION_NOT_SUPPORTED;
}

/*
 * Helper function to compare attributes on any sort of object
 */
CK_RV sc_pkcs11_any_cmp_attribute(struct sc_pkcs11_session *session, void *ptr, CK_ATTRIBUTE_PTR attr)
{
	CK_RV rv;
	struct sc_pkcs11_object *object;
	u8 temp1[1024];
	u8 *temp2 = NULL;	/* dynamic allocation for large attributes */
	CK_ATTRIBUTE temp_attr;

	object = (struct sc_pkcs11_object *)ptr;
	temp_attr.type = attr->type;
	temp_attr.pValue = NULL;
	temp_attr.ulValueLen = 0;

	/* Get the length of the attribute */
	rv = object->ops->get_attribute(session, object, &temp_attr);
	if (rv != CKR_OK || temp_attr.ulValueLen != attr->ulValueLen)
		return 0;

	if (temp_attr.ulValueLen <= sizeof(temp1))
		temp_attr.pValue = temp1;
	else {
		temp2 = calloc(1, temp_attr.ulValueLen);
		if (temp2 == NULL)
			return 0;
		temp_attr.pValue = temp2;
	}

	/* Get the attribute */
	rv = object->ops->get_attribute(session, object, &temp_attr);
	if (rv != CKR_OK) {
		rv = 0;
		goto done;
	}
#ifdef DEBUG
	{
		char foo[64];

		snprintf(foo, sizeof(foo), "Object %p (slot 0x%lx)", object, session->slot->id);
		dump_template(SC_LOG_DEBUG_NORMAL, foo, &temp_attr, 1);
	}
#endif
	rv = temp_attr.ulValueLen == attr->ulValueLen
	    && !memcmp(temp_attr.pValue, attr->pValue, attr->ulValueLen);

      done:
	if (temp2 != NULL)
		free(temp2);

	return rv;
}
