summaryrefslogtreecommitdiff
path: root/module/p11-capi-session.c
diff options
context:
space:
mode:
Diffstat (limited to 'module/p11-capi-session.c')
-rw-r--r--module/p11-capi-session.c983
1 files changed, 983 insertions, 0 deletions
diff --git a/module/p11-capi-session.c b/module/p11-capi-session.c
new file mode 100644
index 0000000..e921875
--- /dev/null
+++ b/module/p11-capi-session.c
@@ -0,0 +1,983 @@
+/*
+ * 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 <stdlib.h>
+
+#include "p11-capi.h"
+#include "p11-capi-builtin.h"
+#include "p11-capi-cert.h"
+#include "p11-capi-key.h"
+#include "p11-capi-object.h"
+#include "p11-capi-rsa.h"
+#include "p11-capi-session.h"
+#include "p11-capi-token.h"
+#include "p11-capi-trust.h"
+
+/* For operation_type in P11cSession */
+enum
+{
+ OPERATION_NONE,
+ OPERATION_FIND,
+ OPERATION_SIGN,
+ OPERATION_DECRYPT
+};
+
+static P11cArray* all_sessions = NULL;
+
+static void
+object_data_release(P11cObjectData* objdata)
+{
+ ASSERT(objdata->data_funcs);
+ ASSERT(objdata->data_funcs->release);
+ (objdata->data_funcs->release)(objdata);
+}
+
+CK_RV
+p11c_session_create(CK_SLOT_ID slot, P11cSession** ret)
+{
+ P11cSession* sess;
+ const char *store;
+ DWORD err;
+
+ sess = calloc(1, sizeof(P11cSession));
+ if(!sess)
+ return CKR_HOST_MEMORY;
+
+ sess->object_data = p11c_hash_new(NULL, NULL);
+ if(!sess->object_data) {
+ free(sess);
+ return CKR_HOST_MEMORY;
+ }
+
+ sess->mutex = CreateMutex(NULL, FALSE, NULL);
+ if(!sess->mutex) {
+ p11c_hash_free(sess->object_data, NULL);
+ free(sess);
+ return CKR_HOST_MEMORY;
+ }
+
+ store = p11c_token_get_store_name(slot);
+ if(store)
+ {
+ sess->store = CertOpenSystemStore((HCRYPTPROV)NULL, store);
+ if(sess->store == NULL)
+ {
+ err = GetLastError();
+
+ /* Store not found, we don't care */
+ if(err != ERROR_FILE_NOT_FOUND)
+ {
+ p11c_hash_free(sess->object_data, NULL);
+ CloseHandle(sess->mutex);
+ free(sess);
+ return p11c_winerr_to_ckr(err);
+ }
+ }
+ }
+
+ sess->slot = slot;
+
+ DBGS(sess, "created");
+
+ *ret = sess;
+ return CKR_OK;
+}
+
+CK_RV
+p11c_session_register(P11cSession* sess)
+{
+ P11cSession* blank = NULL;
+ CK_SESSION_HANDLE id = 0;
+ CK_RV ret = CKR_OK;
+ size_t i;
+
+ ASSERT(sess);
+ ASSERT(sess->id == 0 && sess->refs == 0);
+
+ DBGS(sess, "registering new session");
+
+ p11c_lock_global();
+
+ /* Find a nice session identifier */
+ while(id == 0) {
+
+ /* Allocate sessions properly */
+ if(!all_sessions)
+ {
+ all_sessions = p11c_array_new(0, 1, sizeof(P11cSession*));
+ if(!all_sessions)
+ {
+ ret = CKR_HOST_MEMORY;
+ break;
+ }
+
+ /* A blank entry for '0' */
+ p11c_array_append(all_sessions, blank);
+
+ DBG(("allocated new session list"));
+ }
+
+ /*
+ * PKCS#11 GRAY AREA: We're assuming we can reuse session
+ * handles. PKCS#11 spec says they're like file handles,
+ * and file handles get reused :)
+ */
+
+ /* Note we never put anything in array position '0' */
+ for(i = 1; i < all_sessions->len; ++i)
+ {
+ /* Any empty position will do */
+ if(!p11c_array_index(all_sessions, P11cSession*, i))
+ {
+ id = i;
+ break;
+ }
+ }
+
+ /* Couldn't find a handle, append a handle */
+ if(id == 0)
+ {
+ id = all_sessions->len;
+ p11c_array_append(all_sessions, blank);
+ }
+ }
+
+ if(ret == CKR_OK)
+ {
+ ASSERT(id > 0 && id < all_sessions->len);
+ ASSERT(!p11c_array_index(all_sessions, P11cSession*, id));
+
+
+ /* And assign it to the session handle */
+ p11c_array_index(all_sessions, P11cSession*, i) = sess;
+ sess->id = id;
+
+ /* The session list reference */
+ ASSERT(sess->refs == 0);
+ sess->refs++;
+
+ DBGS(sess, "registered sesson id");
+ }
+
+ p11c_unlock_global();
+
+ return ret;
+}
+
+void
+p11c_session_destroy(P11cSession* sess)
+{
+ ASSERT(sess);
+ ASSERT(sess->refs == 0);
+
+ /* Ask any pending operations to cleanup */
+ if(sess->operation_type)
+ {
+ ASSERT(sess->operation_cancel);
+ (sess->operation_cancel)(sess);
+ }
+
+ ASSERT(sess->operation_type == 0);
+ ASSERT(sess->operation_data == NULL);
+ ASSERT(sess->operation_cancel == NULL);
+
+ if(sess->store)
+ CertCloseStore(sess->store, 0);
+
+ /* Make all the object adat go away */
+ ASSERT(sess->object_data != NULL);
+ p11c_hash_free(sess->object_data, object_data_release);
+
+ /* And make the mutex go away */
+ ASSERT(sess->mutex != NULL);
+ CloseHandle(sess->mutex);
+
+ DBGS(sess, "destroyed");
+ free(sess);
+}
+
+void
+p11c_session_get_info(P11cSession* sess, CK_SESSION_INFO_PTR info)
+{
+ ASSERT(sess);
+ ASSERT(info);
+
+ info->slotID = sess->slot;
+ info->flags = CKF_SERIAL_SESSION;
+ if(sess->read_write)
+ info->flags |= CKF_RW_SESSION;
+
+ if(p11c_token_is_logged_in(sess->slot))
+ info->state = sess->read_write ? CKS_RW_USER_FUNCTIONS : CKS_RO_USER_FUNCTIONS;
+ else
+ info->state = sess->read_write ? CKS_RW_PUBLIC_SESSION : CKS_RO_PUBLIC_SESSION;
+
+ /* TODO: We could implement some use of GetLastError() here */
+ info->ulDeviceError = 0;
+}
+
+static CK_RV
+lock_ref_internal(P11cArray* sessions, CK_SESSION_HANDLE id,
+ BOOL remove, BOOL writable, P11cSession** sess_ret)
+{
+ P11cSession *sess;
+ DWORD r;
+
+ ASSERT(sessions);
+ ASSERT(sess_ret);
+
+ if(id >= sessions->len)
+ {
+ DBG(("invalid session id: %d", id));
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+
+ /* A seemingly valid id */
+ ASSERT(sessions);
+ sess = p11c_array_index(sessions, P11cSession*, id);
+
+ if(!sess)
+ {
+ DBG(("session does not exist: %d", id));
+ return CKR_SESSION_HANDLE_INVALID;
+ }
+
+ /* Make sure it's the right kind of session */
+ if(writable && !sess->read_write)
+ return CKR_SESSION_READ_ONLY;
+
+ ASSERT(sess->id == id);
+
+ /* Closing takes precedence over active operations */
+ if(!remove)
+ {
+ /*
+ * An initial check is done to make sure this session is not active.
+ * This is done outside of the lock. The real check is done later
+ * inside a lock. This is so we can return quickly without blocking
+ * in most cases.
+ */
+
+ if(sess->in_call)
+ {
+ DBGS(sess, ("an operation is already active in this session"));
+ return CKR_OPERATION_ACTIVE;
+ }
+ }
+
+ /* Lock the CallP11cSession */
+ r = WaitForSingleObject(sess->mutex, INFINITE);
+ ASSERT(r == WAIT_OBJECT_0);
+
+ /* Do the real check */
+ if(!remove && sess->in_call)
+ {
+ ReleaseMutex(sess->mutex);
+ DBGS(sess, ("an operation is already active in this session"));
+ return CKR_OPERATION_ACTIVE;
+ }
+
+ /* Make sure it doesn't go away */
+ ASSERT(sess->refs > 0);
+ sess->refs++;
+
+ DBGS(sess, "found and locked session");
+
+ /* And remove it if necessary */
+ if(remove)
+ {
+ p11c_array_index(sessions, P11cSession*, id) = NULL;
+
+ /* The session list reference */
+ sess->refs--;
+ ASSERT(sess->refs > 0);
+
+ DBGS(sess, "removed session from list");
+ }
+ else
+ {
+ ASSERT(!sess->in_call);
+ sess->in_call = 1;
+ }
+
+ *sess_ret = sess;
+ return CKR_OK;
+}
+
+CK_RV
+p11c_session_get_lock_ref(CK_ULONG id, BOOL writable, P11cSession **sess)
+{
+ /* This must be called without any locks held */
+
+ CK_RV ret = CKR_OK;
+
+ ASSERT(sess);
+
+ if(id <= 0)
+ {
+ DBG(("invalid session id passed: %d", id));
+ return CKR_ARGUMENTS_BAD;
+ }
+
+ p11c_lock_global();
+
+ ret = lock_ref_internal (all_sessions, id, FALSE, writable, sess);
+
+ p11c_unlock_global();
+
+ return ret;
+}
+
+CK_RV
+p11c_session_remove_lock_ref(CK_ULONG id, P11cSession **sess)
+{
+ /* This must be called without any locks held */
+
+ CK_RV ret = CKR_OK;
+
+ ASSERT(sess);
+
+ if(id <= 0)
+ {
+ DBG(("invalid session id passed: %d", id));
+ return CKR_ARGUMENTS_BAD;
+ }
+
+ p11c_lock_global();
+
+ ret = lock_ref_internal (all_sessions, id, TRUE, FALSE, sess);
+
+ p11c_unlock_global();
+
+ return ret;
+}
+
+void
+p11c_session_unref_unlock(P11cSession* sess)
+{
+ /* The CallP11cSession must be locked at this point */
+
+ int refs;
+ BOOL r;
+
+ ASSERT(sess);
+
+ ASSERT(sess->refs > 0);
+ sess->refs--;
+ refs = sess->refs;
+
+ sess->in_call = 0;
+
+ DBGS(sess, "unlocked session");
+
+ r = ReleaseMutex(sess->mutex);
+ ASSERT(r == TRUE);
+
+ /*
+ * At this point if no references are held, then we can safely
+ * delete. No other thread should be involved.
+ */
+
+ if(refs == 0)
+ p11c_session_destroy(sess);
+}
+
+CK_RV
+p11c_session_close_all(CK_SLOT_ID slot)
+{
+ /* This must be called without any locks held */
+
+ P11cArray* sessions;
+ P11cSession *sess;
+ size_t i;
+ CK_RV ret = CKR_OK;
+
+ /*
+ * PKCS#11 GRAY AREA: What happens when this gets called
+ * concurrently? We don't return an error on the second call,
+ * because by the time it returns, all sessions should be closed.
+ */
+
+ DBG(("closing all sessions for: %d", slot));
+
+ if(!all_sessions)
+ return CKR_OK;
+
+ p11c_lock_global();
+
+ sessions = p11c_array_sized_new(0, 1, sizeof(P11cSession*),
+ all_sessions->len);
+ if(!sessions)
+ ret = CKR_HOST_MEMORY;
+
+ /* Steal all the session data */
+ if(ret == CKR_OK)
+ {
+ for(i = 0; i < all_sessions->len; ++i)
+ {
+ sess = p11c_array_index(all_sessions, P11cSession*, i);
+ if(sess && (slot == ((CK_SLOT_ID)-1) || sess->slot == slot))
+ {
+ /* Steal this session */
+ p11c_array_index(all_sessions, P11cSession*, i) = NULL;
+ }
+ else
+ {
+ /* Not a session we're interested in */
+ sess = NULL;
+ }
+
+ /* Both null and normal sessions are set to preserve indexes */
+ p11c_array_append(sessions, sess);
+ }
+
+ ASSERT(sessions->len == all_sessions->len);
+ }
+
+ p11c_unlock_global();
+
+ if(ret != CKR_OK)
+ return ret;
+
+ /* Close each session in turn */
+ for(i = 0; i < sessions->len; ++i)
+ {
+ if(!p11c_array_index(sessions, P11cSession*, i))
+ continue;
+
+ /* We need any calls in other threads to finish, so wait here */
+ if(lock_ref_internal(sessions, i, TRUE, FALSE, &sess) == CKR_OK)
+ p11c_session_unref_unlock(sess);
+ }
+
+ /* We stole the memory above, free it now */
+ p11c_array_free(sessions, 1);
+ return CKR_OK;
+}
+
+void
+p11c_session_cleanup_all()
+{
+ p11c_session_close_all((CK_SLOT_ID)-1);
+
+ p11c_lock_global();
+
+ p11c_array_free(all_sessions, 1);
+ all_sessions = NULL;
+
+ p11c_unlock_global();
+}
+
+/* ----------------------------------------------------------------------------
+ * OBJECT DATA
+ */
+
+CK_RV
+p11c_session_get_object_data(P11cSession* sess, P11cObject* obj,
+ P11cObjectData** objdata)
+{
+ CK_OBJECT_HANDLE id;
+ P11cObjectData* newdata;
+ CK_RV ret;
+
+ ASSERT(sess);
+ ASSERT(sess->object_data);
+ ASSERT(obj);
+ ASSERT(obj->obj_funcs);
+ ASSERT(obj->obj_funcs->load_data);
+ ASSERT(objdata);
+
+ id = obj->id;
+
+ *objdata = p11c_hash_get(sess->object_data, p11c_hash_key(id));
+ if(*objdata)
+ return CKR_OK;
+
+ ret = (obj->obj_funcs->load_data)(sess, obj, &newdata);
+ if(ret != CKR_OK)
+ return ret;
+
+ newdata->object = id;
+ ASSERT(newdata->data_funcs);
+
+ if(!p11c_hash_set(sess->object_data, p11c_hash_key(id), newdata))
+ {
+ object_data_release(newdata);
+ return CKR_HOST_MEMORY;
+ }
+
+ *objdata = newdata;
+ return CKR_OK;
+}
+
+void
+p11c_session_clear_object_data(P11cSession* sess, P11cObject* obj)
+{
+ P11cObjectData* objdata;
+
+ ASSERT(sess);
+ ASSERT(sess->object_data);
+ ASSERT(obj);
+
+ objdata = (P11cObjectData*)p11c_hash_rem(sess->object_data, p11c_hash_key(obj->id));
+ if(objdata)
+ object_data_release(objdata);
+}
+
+void
+p11c_session_enum_object_data(P11cSession* sess,
+ P11cEnumObjectData enum_func, void* arg)
+{
+ CK_OBJECT_HANDLE i, max;
+ P11cObject* obj;
+ P11cObjectData* objdata;
+
+ ASSERT(sess);
+ ASSERT(sess->object_data);
+ ASSERT(enum_func);
+
+ max = p11c_token_get_max_handle();
+ for(i = 0; i < max; ++i)
+ {
+ objdata = (P11cObjectData*)p11c_hash_get(sess->object_data, p11c_hash_key(i));
+ if(!objdata)
+ continue;
+
+ obj = p11c_token_lookup_object(sess->slot, i);
+ if(!obj)
+ continue;
+
+ (enum_func)(sess, obj, objdata, arg);
+ }
+}
+
+CK_RV
+p11c_session_get_object_data_for(P11cSession* sess, CK_OBJECT_HANDLE hand,
+ P11cObjectData** objdata)
+{
+ P11cObject* obj;
+
+ obj = p11c_token_lookup_object(sess->slot, hand);
+ if(!obj)
+ return CKR_OBJECT_HANDLE_INVALID;
+
+ return p11c_session_get_object_data(sess, obj, objdata);
+}
+
+void
+p11c_session_take_object_data(P11cSession* sess, P11cObject* obj,
+ P11cObjectData* objdata)
+{
+ P11cObjectData* prev;
+
+ ASSERT(obj);
+ ASSERT(sess);
+ ASSERT(sess->object_data);
+
+ ASSERT(objdata);
+ objdata->object = obj->id;
+
+ prev = p11c_hash_rem(sess->object_data, p11c_hash_key(obj->id));
+ if(prev)
+ object_data_release(prev);
+
+ if(!p11c_hash_set(sess->object_data, p11c_hash_key(obj->id), objdata))
+ object_data_release(objdata);
+}
+
+
+/* ----------------------------------------------------------------------------
+ * FIND OPERATION
+ */
+
+static BOOL
+get_ulong_attribute(CK_ATTRIBUTE_TYPE type, CK_ATTRIBUTE_PTR templ,
+ CK_ULONG count, CK_ULONG* val)
+{
+ CK_ULONG i;
+
+ ASSERT(val);
+ ASSERT(!count || templ);
+
+ for(i = 0; i < count; ++i)
+ {
+ if(templ[i].type == type)
+ {
+ *val = *((CK_ULONG*)templ[i].pValue);
+ return TRUE;
+ }
+ }
+
+ return FALSE;
+}
+
+static CK_RV
+gather_objects(P11cSession* sess, CK_ATTRIBUTE_PTR match,
+ CK_ULONG count, P11cArray* arr)
+{
+ CK_OBJECT_CLASS ocls = CKO_ANY;
+ CK_RV ret = CKR_OK;
+
+ get_ulong_attribute(CKA_CLASS, match, count, &ocls);
+
+ /* Search for builtins */
+ ret = p11c_builtin_find(sess, ocls, match, count, arr);
+ if(ret != CKR_OK)
+ return ret;
+
+ /*
+ * Search through certificates.
+ *
+ * We always do this search first. In Windows a lots hangs off
+ * the certificates. For example private keys are not contained
+ * in the same stores that certificates are in. There are a different
+ * set of key containers many of which can be used together
+ * with a certificate stored in any store.
+ *
+ * The trust objects we expose also depend on the certificates
+ * loaded.
+ */
+ ret = p11c_cert_find(sess, ocls, match, count, arr);
+ if(ret != CKR_OK)
+ return ret;
+
+ /* Search through trust objects */
+ ret = p11c_trust_find(sess, ocls, match, count, arr);
+ if(ret != CKR_OK)
+ return ret;
+
+ /* Search through key objects */
+ ret = p11c_key_find(sess, ocls, match, count, arr);
+ if(ret != CKR_OK)
+ return ret;
+
+ return ret;
+}
+
+void
+cleanup_find_operation(P11cSession* sess)
+{
+ ASSERT(sess->operation_type == OPERATION_FIND);
+ if(sess->operation_data)
+ p11c_array_free((P11cArray*)sess->operation_data, TRUE);
+ sess->operation_type = OPERATION_NONE;
+ sess->operation_data = NULL;
+ sess->operation_cancel = NULL;
+}
+
+void
+purge_duplicate_objects(P11cArray* arr)
+{
+ P11cHash* checks;
+ CK_OBJECT_HANDLE v;
+ size_t i;
+
+ checks = p11c_hash_new(NULL, NULL);
+ if(!checks)
+ return;
+
+ for(i = 0; i < arr->len; )
+ {
+ v = p11c_array_index(arr, CK_OBJECT_HANDLE, i);
+ if(p11c_hash_get(checks, p11c_hash_key(v)))
+ {
+ p11c_array_remove_index(arr, i);
+ /* Look at same i again */
+ }
+ else
+ {
+ if(!p11c_hash_set(checks, p11c_hash_key(v), arr))
+ break;
+ ++i;
+ }
+ }
+
+ p11c_hash_free(checks, NULL);
+}
+
+CK_RV
+p11c_session_find_init(P11cSession* sess, CK_ATTRIBUTE_PTR match,
+ CK_ULONG count)
+{
+ P11cArray* arr;
+ CK_RV ret;
+
+ ASSERT(sess);
+ ASSERT(!count || match);
+
+ if(sess->operation_type != OPERATION_NONE)
+ return CKR_OPERATION_ACTIVE;
+
+ arr = p11c_array_new(0, 1, sizeof(CK_OBJECT_HANDLE));
+ if(!arr)
+ return CKR_HOST_MEMORY;
+
+ ret = gather_objects(sess, match, count, arr);
+ if(ret != CKR_OK)
+ {
+ p11c_array_free(arr, TRUE);
+ return ret;
+ }
+
+ /* Cleanup all duplicates in the array */
+ purge_duplicate_objects(arr);
+
+ sess->operation_type = OPERATION_FIND;
+ sess->operation_data = arr;
+ sess->operation_cancel = cleanup_find_operation;
+
+ return CKR_OK;
+}
+
+CK_RV
+p11c_session_find(P11cSession* sess, CK_OBJECT_HANDLE_PTR objects,
+ CK_ULONG max_object_count, CK_ULONG_PTR object_count)
+{
+ P11cArray* arr;
+ size_t i;
+
+ ASSERT(sess);
+ ASSERT(object_count);
+ ASSERT(!max_object_count || objects);
+
+ if(sess->operation_type != OPERATION_FIND)
+ return CKR_OPERATION_NOT_INITIALIZED;
+
+ if(!max_object_count)
+ {
+ *object_count = 0;
+ return CKR_OK;
+ }
+
+ arr = (P11cArray*)sess->operation_data;
+ *object_count = (max_object_count > arr->len ? arr->len : max_object_count);
+ for(i = 0; i < *object_count; ++i)
+ objects[i] = p11c_array_index(arr, CK_OBJECT_HANDLE, i);
+
+ p11c_array_remove_range(arr, 0, *object_count);
+
+ return CKR_OK;
+}
+
+CK_RV
+p11c_session_find_final(P11cSession* sess)
+{
+ ASSERT(sess);
+
+ if(sess->operation_type != OPERATION_FIND)
+ return CKR_OPERATION_NOT_INITIALIZED;
+
+ cleanup_find_operation(sess);
+ return CKR_OK;
+}
+
+
+/* ----------------------------------------------------------------------------
+ * CRYPTO OPERATIONS
+ */
+
+typedef struct _CryptoContext
+{
+ CK_MECHANISM_TYPE mech_type;
+ P11cDestroyFunc mech_cleanup;
+ void* mech_data;
+}
+CryptoContext;
+
+void
+cleanup_crypto_operation(P11cSession* sess)
+{
+ CryptoContext* ctx;
+
+ if(sess->operation_data)
+ {
+ ctx = (CryptoContext*)sess->operation_data;
+ if(ctx->mech_cleanup)
+ (ctx->mech_cleanup)(ctx->mech_data);
+ free(ctx);
+ }
+
+ sess->operation_type = OPERATION_NONE;
+ sess->operation_data = NULL;
+ sess->operation_cancel = NULL;
+}
+
+CK_RV
+p11c_session_sign_init(P11cSession* sess, CK_MECHANISM_PTR mech,
+ P11cObjectData *objdata)
+{
+ CryptoContext* ctx;
+ CK_RV ret;
+
+ ASSERT(sess);
+ ASSERT(mech);
+ ASSERT(objdata);
+
+ if(sess->operation_type != OPERATION_NONE)
+ return CKR_OPERATION_ACTIVE;
+
+ ctx = calloc(1, sizeof(CryptoContext));
+ if(!ctx)
+ return CKR_HOST_MEMORY;
+
+ ctx->mech_type = mech->mechanism;
+
+ switch(mech->mechanism)
+ {
+ case CKM_RSA_PKCS:
+ ret = p11c_rsa_pkcs_sign_init(objdata, &ctx->mech_data);
+ ctx->mech_cleanup = p11c_rsa_pkcs_sign_cleanup;
+ break;
+ default:
+ ret = CKR_MECHANISM_INVALID;
+ break;
+ };
+
+ if(ret != CKR_OK)
+ {
+ free(ctx);
+ ASSERT(!sess->operation_data);
+ return ret;
+ }
+
+ sess->operation_type = OPERATION_SIGN;
+ sess->operation_data = ctx;
+ sess->operation_cancel = cleanup_crypto_operation;
+ return CKR_OK;
+}
+
+CK_RV
+p11c_session_sign(P11cSession* sess, CK_BYTE_PTR data, CK_ULONG n_data,
+ CK_BYTE_PTR signature, CK_ULONG_PTR n_signature)
+{
+ CryptoContext *ctx;
+ BOOL incomplete;
+ CK_RV ret;
+
+ ASSERT(sess);
+ ASSERT(data);
+ ASSERT(n_data);
+
+ if(sess->operation_type != OPERATION_SIGN)
+ return CKR_OPERATION_NOT_INITIALIZED;
+
+ ctx = (CryptoContext*)sess->operation_data;
+ switch(ctx->mech_type)
+ {
+ case CKM_RSA_PKCS:
+ ret = p11c_rsa_pkcs_sign_perform(data, n_data, signature, n_signature,
+ &ctx->mech_data);
+ break;
+
+ default:
+ ASSERT(FALSE);
+ ret = CKR_GENERAL_ERROR;
+ break;
+ }
+
+ /* Buffer calculation, we don't end operation */
+ incomplete = (ret == CKR_BUFFER_TOO_SMALL || (ret == CKR_OK && !signature));
+
+ if(!incomplete)
+ cleanup_crypto_operation(sess);
+
+ return ret;
+}
+
+CK_RV
+p11c_session_decrypt_init(P11cSession* sess, CK_MECHANISM_PTR mech,
+ P11cObjectData *objdata)
+{
+ CryptoContext* ctx;
+ CK_RV ret;
+
+ ASSERT(sess);
+ ASSERT(mech);
+ ASSERT(objdata);
+
+ if(sess->operation_type != OPERATION_NONE)
+ return CKR_OPERATION_ACTIVE;
+
+ ctx = calloc(1, sizeof(CryptoContext));
+ if(!ctx)
+ return CKR_HOST_MEMORY;
+
+ ctx->mech_type = mech->mechanism;
+
+ switch(mech->mechanism)
+ {
+ case CKM_RSA_PKCS:
+ ret = p11c_rsa_pkcs_decrypt_init(objdata, &ctx->mech_data);
+ ctx->mech_cleanup = p11c_rsa_pkcs_decrypt_cleanup;
+ break;
+ default:
+ ret = CKR_MECHANISM_INVALID;
+ break;
+ };
+
+ if(ret != CKR_OK)
+ {
+ free(ctx);
+ ASSERT(!sess->operation_data);
+ return ret;
+ }
+
+ sess->operation_type = OPERATION_DECRYPT;
+ sess->operation_data = ctx;
+ sess->operation_cancel = cleanup_crypto_operation;
+ return CKR_OK;
+}
+
+CK_RV
+p11c_session_decrypt(P11cSession* sess, CK_BYTE_PTR encdata, CK_ULONG n_encdata,
+ CK_BYTE_PTR result, CK_ULONG_PTR n_result)
+{
+ CryptoContext *ctx;
+ BOOL incomplete;
+ CK_RV ret;
+
+ ASSERT(sess);
+ ASSERT(encdata);
+ ASSERT(n_encdata);
+
+ if(sess->operation_type != OPERATION_DECRYPT)
+ return CKR_OPERATION_NOT_INITIALIZED;
+
+ ctx = (CryptoContext*)sess->operation_data;
+ switch(ctx->mech_type)
+ {
+ case CKM_RSA_PKCS:
+ ret = p11c_rsa_pkcs_decrypt_perform(encdata, n_encdata, result, n_result,
+ &ctx->mech_data);
+ break;
+
+ default:
+ ASSERT(FALSE);
+ ret = CKR_GENERAL_ERROR;
+ break;
+ }
+
+ /* Buffer calculation, we don't end operation */
+ incomplete = (ret == CKR_BUFFER_TOO_SMALL || (ret == CKR_OK && !result));
+
+ if(!incomplete)
+ cleanup_crypto_operation(sess);
+
+ return ret;
+}