/*
* pkcs11.c
* --------
*
* This is a partial implementation of PKCS #11 on top of the Cryptech
* libhal library connecting to the Cryptech FPGA cores.
*
* Author: Rob Austein
* Copyright (c) 2015, SUNET
*
* Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <assert.h>
#include <hal.h>
/*
* Magic PKCS #11 macros that must be defined before including
* pkcs11.h. For now these are only the Unix versions, add others
* later (which may require minor refactoring).
*/
#define CK_PTR *
#define CK_DEFINE_FUNCTION(returnType, name) returnType name
#define CK_DECLARE_FUNCTION(returnType, name) returnType name
#define CK_DECLARE_FUNCTION_POINTER(returnType, name) returnType (* name)
#define CK_CALLBACK_FUNCTION(returnType, name) returnType (* name)
#ifndef NULL_PTR
#define NULL_PTR NULL
#endif
#include "pkcs11.h"
#include "attributes.h"
#include "sql_common.h"
/*
* This PKCS #11 implementation is hardwired with one slot, the token
* for which is always present (so we return the same answer
* regardless of the value of tokenPresent).
*/
#define P11_ONE_AND_ONLY_SLOT 0
/*
* Version numbers. Placeholders for now.
*
* Software version number is just the version of this PKCS #11
* implementation. Probably.
*/
#warning Figure out hardware and software version numbers
#define P11_VERSION_SW_MAJOR 0
#define P11_VERSION_SW_MINOR 0
#define P11_VERSION_HW_MAJOR 0
#define P11_VERSION_HW_MINOR 0
/*
* Debugging control.
*/
#ifndef DEBUG_HAL
#define DEBUG_HAL 1
#endif
#ifndef DEBUG_PKCS11
#define DEBUG_PKCS11 2
#endif
/*
* Whether to include POSIX-specific features.
*/
#ifndef USE_POSIX
#define USE_POSIX 1
#endif
/*
* Whether to use POSIX threads.
*/
#ifndef USE_PTHREADS
#define USE_PTHREADS USE_POSIX
#endif
#if USE_PTHREADS && !USE_POSIX
#error Can not use POSIX threads without using POSIX
#endif
#if USE_POSIX
#include <unistd.h>
#include <errno.h>
#endif
#if USE_PTHREADS
#include <pthread.h>
#endif
/*
* PKCS #11 session.
*/
/*
* At present we have no concept of encryption or signature algorithms
* in libhal, as we only support RSA and AES. For PKCS #11 purposes
* we can figure out what kind of key we're looking at from attributes
* like CKA_KEY_TYPE, so it's just something we look up given the key
* object handle.
*
* At the moment we don't need to keep any signature or digest state
* in the session structure, which is good since the current hash
* cores don't allow us to extract state anyway. This makes it
* impossible to implement the incremental operations
* (C_DigestUpdate(), C_SignUpdate()) but also simplifies our current
* task.
*
* General idea is that we have separate descriptors/handles/state for
* each operation that we're allowed to do in parallel, so sign,
* verify, digest, encrypt, decrypt, wrapkey, and unwrapkey all need
* separate slots in the session structure. Add these as we go.
*/
typedef struct p11_session {
CK_SESSION_HANDLE handle; /* Session handle */
struct p11_session *link; /* Next session in list */
CK_STATE state; /* State (CKS_*) of this session */
CK_NOTIFY notify; /* Notification callback */
CK_VOID_PTR application; /* Application data */
sqlite3_stmt *find_query; /* FindObject*() query state */
int find_query_done; /* find_query has terminated */
const hal_hash_descriptor_t
*digest_descriptor, /* Hash for C_Digest*() */
*sign_digest_descriptor, /* Hash for C_Sign*() */
*verify_digest_descriptor; /* Hash for C_Verify*() */
CK_OBJECT_HANDLE
sign_key_handle, /* Key for C_Sign*() */
verify_key_handle; /* Key for C_Verify() */
} p11_session_t;
/*
* PKCS #11 handle management. PKCS #11 has two kinds of handles:
* session handles and object handles. We subdivide object handles
* into token object handles (handles for objects that live on the
* token) and session object handles (handles for objects that live
* only as long as the session does), and we steal a bit of the object
* handle as a flag to distinguish between our two kinds of object
* handles, considerably simplifing the objected-related SQL code.
*/
typedef enum {
handle_flavor_session,
handle_flavor_token_object,
handle_flavor_session_object
} handle_flavor_t;
#define FLAG_HANDLE_TOKEN 0x80000000
#define is_token_handle(_handle_) (((_handle_) & FLAG_HANDLE_TOKEN) != 0)
/*
* Current logged-in user.
*/
static enum {
not_logged_in,
logged_in_as_user,
logged_in_as_so
} logged_in_as = not_logged_in;
/*
* PKCS #11 sessions for this application.
*/
static p11_session_t *p11_sessions;
/*
* Next PKCS #11 handle to allocate. We use a single handle space for
* both session and object handles, and we just keep incrementing
* until it wraps, to reduce the amount of time we have to spend
* on SQL probes to avoid handle conflicts.
*/
static CK_ULONG next_handle;
/*
* Mutex callbacks.
*/
static CK_CREATEMUTEX mutex_cb_create;
static CK_DESTROYMUTEX mutex_cb_destroy;
static CK_LOCKMUTEX mutex_cb_lock;
static CK_UNLOCKMUTEX mutex_cb_unlock;
/*
* Global mutex. We may want something finer grained later, but this
* will suffice to comply with the API requirements.
*/
static CK_VOID_PTR p11_global_mutex;
/*
* (POSIX-specific) process which last called C_Initialize().
*/
#if USE_POSIX
static pid_t initialized_pid;
#endif
/*
* Syntactic sugar for functions returning CK_RV complex enough to
* need cleanup actions on failure. Also does very basic logging for
* debug-by-printf().
*
* NB: This uses a variable ("rv") and a goto target ("fail") which
* must be defined in the calling environment. We could make these
* arguments to the macro, but doing so would make the code less
* readable without significantly reducing the voodoo factor.
*/
#if DEBUG_PKCS11
#define lose(_ck_rv_code_) \
do { \
rv = (_ck_rv_code_); \
fprintf(stderr, "%s:%u: %s\n", __FILE__, __LINE__, #_ck_rv_code_); \
goto fail; \
} while (0)
#else /* DEBUG_PKCS11 */
#define lose(_ck_rv_code_) \
do { \
rv = (_ck_rv_code_); \
goto fail; \
} while (0)
#endif /* DEBUG_PKCS11 */
/*
* More debug-by-printf() support. One would like to consider this a
* relic of the previous millenium, but, sadly, broken debugging
* environments are still all too common.
*/
#if DEBUG_PKCS11 > 1
#define ENTER_PUBLIC_FUNCTION(_name_) \
fprintf(stderr, "Entering function %s\n", #_name_)
#else /* DEBUG_PKCS11 > 1 */
#define ENTER_PUBLIC_FUNCTION(_name_)
#endif /* DEBUG_PKCS11 > 1 */
/*
* Error checking for libhal calls.
*/
#if DEBUG_HAL
static int _hal_check(const hal_error_t err, const char * const expr, const char * const file, const unsigned line)
{
if (err == HAL_OK)
return 1;
fprintf(stderr, "%s:%u: %s returned %s\n", file, line, expr, hal_error_string(err));
return 0;
}
#define hal_check(_expr_) (_hal_check((_expr_), #_expr_, __FILE__, __LINE__))
#else /* DEBUG_HAL */
#define hal_check(_expr_) ((_expr_) == HAL_OK)
#endif /* DEBUG_HAL */
/*
* Thread mutex utilities. We need to handle three separate cases:
*
* 1) User doesn't care about mutexes;
* 2) User wants us to use "OS" mutexes;
* 3) User wants us to use user-specified mutexs.
*
* For "OS" mutexes, read POSIX Threads mutexes, at least for now.
*
* PKCS #11 sort of has a fourth case, but it's really just license
* for us to pick either the second or third case at whim.
*
* To simplify the rest of the API, we provide a POSIX-based
* implementation which uses the same API an user-provided mutex
* implementation would be required to use, use null function pointers
* to represent the case where the user doesn't need mutexes at all,
* and wrap the whole thing in trivial macros to insulate the rest of
* the code from the grotty details.
*/
/*
* Basic macros.
*/
#define mutex_create(_m_) (mutex_cb_create == NULL ? CKR_OK : mutex_cb_create(_m_))
#define mutex_destroy(_m_) (mutex_cb_destroy == NULL ? CKR_OK : mutex_cb_destroy(_m_))
#define mutex_lock(_m_) (mutex_cb_lock == NULL ? CKR_OK : mutex_cb_lock(_m_))
#define mutex_unlock(_m_) (mutex_cb_unlock == NULL ? CKR_OK : mutex_cb_unlock(_m_))
/*
* Slightly higher-level macros for common operations.
*/
#define mutex_lock_or_return_failure(_m_) \
do { \
CK_RV _rv = mutex_lock(_m_); \
if (_rv != CKR_OK) \
return _rv; \
} while (0)
#define mutex_unlock_return_with_rv(_rv_, _m_) \
do { \
CK_RV _rv1 = _rv_; \
CK_RV _rv2 = mutex_unlock(_m_); \
return _rv1 == CKR_OK ? _rv2 : _rv1; \
} while (0)
/*
* Mutex implementation using POSIX mutexes.
*/
#if USE_PTHREADS
static CK_RV posix_mutex_create(CK_VOID_PTR_PTR ppMutex)
{
pthread_mutex_t *m = NULL;
CK_RV rv;
if (ppMutex == NULL)
lose(CKR_GENERAL_ERROR);
if ((m = malloc(sizeof(*m))) == NULL)
lose(CKR_HOST_MEMORY);
switch (pthread_mutex_init(m, NULL)) {
case 0:
*ppMutex = m;
return CKR_OK;
case ENOMEM:
lose(CKR_HOST_MEMORY);
default:
lose(CKR_GENERAL_ERROR);
}
fail:
if (m != NULL)
free(m);
return rv;
}
static CK_RV posix_mutex_destroy(CK_VOID_PTR pMutex)
{
CK_RV rv;
if (pMutex == NULL)
lose(CKR_MUTEX_BAD);
switch (pthread_mutex_destroy(pMutex)) {
case 0:
free(pMutex);
return CKR_OK;
case EINVAL:
lose(CKR_MUTEX_BAD);
case EBUSY:
/*
* PKCS #11 mutex semantics are a bad match for POSIX here,
* leaving us only the nuclear option. Feh. Fall through.
*/
default:
lose(CKR_GENERAL_ERROR);
}
fail:
return rv;
}
static CK_RV posix_mutex_lock(CK_VOID_PTR pMutex)
{
CK_RV rv;
if (pMutex == NULL)
lose(CKR_MUTEX_BAD);
switch (pthread_mutex_lock(pMutex)) {
case 0:
return CKR_OK;
case EINVAL:
lose(CKR_MUTEX_BAD);
default:
lose(CKR_GENERAL_ERROR);
}
fail:
return rv;
}
static CK_RV posix_mutex_unlock(CK_VOID_PTR pMutex)
{
CK_RV rv;
if (pMutex == NULL)
lose(CKR_MUTEX_BAD);
switch (pthread_mutex_unlock(pMutex)) {
case 0:
return CKR_OK;
case EINVAL:
lose(CKR_MUTEX_BAD);
case EPERM:
lose(CKR_MUTEX_NOT_LOCKED);
default:
lose(CKR_GENERAL_ERROR);
}
fail:
return rv;
}
#endif /* USE_PTHREADS */
/*
* Initialize KEK. If we had proper hardware support the KEK would be
* living in special RAM where we could wipe it if anything triggered
* our tamper circuitry. But we have no such at the moment, so we
* have no good place to store the KEK.
*
* So we store it in the SQL database, which kind of defeats the point
* of wrapping private keys that live in the same database -- except
* that we're trying to get all the other bits right so that we can
* just move the KEK to secure memory once we have it.
*/
static int kek_init(void)
{
static const char test_kek[] =
" SELECT kek IS NULL FROM global";
static const char set_kek[] =
" UPDATE global SET kek = ?1";
sqlite3_stmt *q = NULL;
int ok = (sql_check_ok(sql_prepare(&q, test_kek)) &&
sql_check_row(sqlite3_step(q)));
if (ok && sqlite3_column_int(q, 0)) {
uint8_t kekbuf[bitsToBytes(256)];
ok = (hal_check(hal_get_random(kekbuf, sizeof(kekbuf))) &&
sql_check_ok(sql_finalize_and_clear(&q)) &&
sql_check_ok(sql_prepare(&q, set_kek)) &&
sql_check_ok(sqlite3_bind_blob(q, 1, kekbuf,
sizeof(kekbuf),
NULL)) &&
sql_check_done(sqlite3_step(q)));
memset(kekbuf, 0, sizeof(kekbuf));
}
sqlite3_finalize(q);
return ok;
}
/*
* Find an unused handle.
*
* Note that zero is an excluded value (CK_INVALID_HANDLE), hence the
* slightly odd arithmetic.
*
* For object handles, we steal the high-order bit to flag whether the
* handle represents a session object or token object.
*/
static CK_ULONG p11_allocate_unused_handle(const handle_flavor_t flavor)
{
static const char select_format[] =
" SELECT %s_id FROM %s WHERE %s_handle = ?";
const char *table = flavor == handle_flavor_session ? "session" : "object";
sqlite3_stmt *q = NULL;
CK_ULONG handle;
int ret;
if (!sql_check_ok(sql_prepare(&q, select_format, table, table, table)))
goto fail;
for (;;) {
handle = ++next_handle;
next_handle %= 0xFFFFFFFF;
switch (flavor) {
case handle_flavor_session:
break;
case handle_flavor_token_object:
handle |= FLAG_HANDLE_TOKEN;
break;
case handle_flavor_session_object:
handle &= ~FLAG_HANDLE_TOKEN;
break;
}
assert(handle != CK_INVALID_HANDLE);
if (!sql_check_ok(sqlite3_reset(q)) ||
!sql_check_ok(sqlite3_bind_int64(q, 1, handle)))
goto fail;
if ((ret = sqlite3_step(q)) == SQLITE_ROW)
continue;
if (ret == SQLITE_DONE)
break;
sql_whine_step();
goto fail;
}
sqlite3_finalize(q);
return handle;
fail:
sqlite3_finalize(q);
return CK_INVALID_HANDLE;
}
/*
* Translate CKA_TOKEN value to handle flavor.
*/
static handle_flavor_t p11_handle_flavor_from_cka_token(const CK_BBOOL *bbool)
{
assert(bbool != NULL);
return *bbool ? handle_flavor_token_object : handle_flavor_session_object;
}
/*
* Attribute methods.
*/
/*
* Set an attribute for a given object.
*
* It would be trivial to generalize this to take a CK_ATTRIBUTE_PTR
* template instead of a single attribute, at the cost of losing the
* const specifiers (CK_ATTRIBUTE_PTR has an internal non-const void*).
*/
static int p11_attribute_set(const CK_OBJECT_HANDLE object_handle,
const CK_ATTRIBUTE_TYPE type,
const void * const value,
const CK_ULONG length)
{
static const char insert_format[] =
" INSERT OR REPLACE INTO %s_attribute (%s_object_id, type, value)"
" VALUES ((SELECT %s_object_id FROM object WHERE object_handle = ?1), ?2, ?3)";
const char *flavor = is_token_handle(object_handle) ? "token" : "session";
sqlite3_stmt *q = NULL;
int ok = 0;
if (!sql_check_ok(sql_prepare(&q, insert_format, flavor, flavor, flavor)) ||
!sql_check_ok(sqlite3_bind_int64(q, 1, object_handle)) ||
!sql_check_ok(sqlite3_bind_int64(q, 2, type)) ||
!sql_check_ok(sqlite3_bind_blob( q, 3, value, length, NULL)) ||
!sql_check_done(sqlite3_step(q)))
goto fail;
ok = 1;
fail:
sqlite3_finalize(q);
return ok;
}
/*
* Get a single attribute from a given object.
*
* This could easily be generalized to take a CK_ATTRIBUTE_PTR, at the
* cost of more complicated error semantics.
*/
static int p11_attribute_get(const CK_OBJECT_HANDLE object_handle,
const CK_ATTRIBUTE_TYPE type,
void *value,
CK_ULONG *length,
const CK_ULONG maxlength)
{
static const char select_format[] =
" SELECT value FROM %s_attribute NATURAL JOIN object"
" WHERE object_handle = ?1 AND type = ?2";
const char *flavor = is_token_handle(object_handle) ? "token" : "session";
sqlite3_stmt *q = NULL;
int ret, ok = 0;
CK_ULONG len;
if (!sql_check_ok(sql_prepare(&q, select_format, flavor)) ||
!sql_check_ok(sqlite3_bind_int64(q, 1, object_handle)) ||
!sql_check_ok(sqlite3_bind_int64(q, 2, type)))
goto fail;
ret = sqlite3_step(q);
if (ret == SQLITE_DONE)
goto fail;
if (ret != SQLITE_ROW) {
sql_whine_step();
goto fail;
}
len = sqlite3_column_bytes(q, 0);
if (length != NULL)
*length = len;
if (value != NULL && maxlength < len)
goto fail;
if (value != NULL)
memcpy(value, sqlite3_column_blob(q, 0), len);
ok = 1;
fail:
sqlite3_finalize(q);
return ok;
}
/*
* Wrappers to set and get CK_BBOOL and CK_ULONG values.
*/
#if 0
static int p11_attribute_set_bbool(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, const CK_BBOOL value)
{
return p11_attribute_set(object_handle, type, &value, sizeof(value));
}
static int p11_attribute_set_ulong(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, const CK_ULONG value)
{
return p11_attribute_set(object_handle, type, &value, sizeof(value));
}
#endif
static int p11_attribute_get_bbool(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, CK_BBOOL *value)
{
CK_ULONG length;
return p11_attribute_get(object_handle, type, value, &length, sizeof(*value)) && length == sizeof(*value);
}
static int p11_attribute_get_ulong(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, CK_ULONG *value)
{
CK_ULONG length;
return p11_attribute_get(object_handle, type, value, &length, sizeof(*value)) && length == sizeof(*value);
}
/*
* Find an attribute in a CK_ATTRIBUTE_PTR template. Returns index
* into template, or -1 if not found.
*/
static int p11_attribute_find_in_template(const CK_ATTRIBUTE_TYPE type,
const CK_ATTRIBUTE_PTR template,
const CK_ULONG length)
{
int i;
if (template != NULL)
for (i = 0; i < length; i++)
if (template[i].type == type)
return i;
return -1;
}
/*
* Map a keyusage-related attribute to a keyusage bit flag.
*
* Assumes that calling code has already checked whether this
* attribute is legal for this object class, that attribute which
* should be CK_BBOOLs are of the correct length, etcetera.
*
* To handle all the possible permutations of specified and default
* values, it may be necessary to defer calling this method until
* after the default and mandatory values have been merged into the
* values supplied by the application-supplied template.
*
* Semantics of the flags follow RFC 5280 4.2.1.3, numeric values
* don't matter particularly as we only use them internally.
*/
#define KEYUSAGE_DIGITALSIGNATURE (1 << 0)
#define KEYUSAGE_KEYENCIPHERMENT (1 << 1)
#define KEYUSAGE_DATAENCIPHERMENT (1 << 2)
static void p11_attribute_apply_keyusage(unsigned *keyusage, const CK_ATTRIBUTE_TYPE type, const CK_BBOOL *value)
{
unsigned flag;
assert(keyusage != NULL && value != NULL);
switch (type) {
case CKA_SIGN: /* Generate signature */
case CKA_VERIFY: /* Verify signature */
flag = KEYUSAGE_DIGITALSIGNATURE;
break;
case CKA_ENCRYPT: /* Encrypt bulk data (seldom used) */
case CKA_DECRYPT: /* Bulk decryption (seldom used) */
flag = KEYUSAGE_DATAENCIPHERMENT;
break;
case CKA_WRAP: /* Wrap key (normal way of doing encryption) */
case CKA_UNWRAP: /* Unwrap key (normal way of doing decryption) */
flag = KEYUSAGE_KEYENCIPHERMENT;
break;
default:
return; /* Attribute not related to key usage */
}
if (*value)
*keyusage |= flag;
else
*keyusage &= ~flag;
}
/*
* Descriptor methods. Descriptors are generated at compile time by
* an auxiliary Python script, see attributes.* for details.
*/
/*
* Return the descriptor associated with a particular object class and
* key type.
*/
static const p11_descriptor_t *p11_descriptor_from_key_type(const CK_OBJECT_CLASS object_class,
const CK_KEY_TYPE key_type)
{
int i;
for (i = 0; i < sizeof(p11_descriptor_keyclass_map)/sizeof(*p11_descriptor_keyclass_map); i++) {
const p11_descriptor_keyclass_map_t * const m = &p11_descriptor_keyclass_map[i];
if (m->object_class == object_class && m->key_type == key_type)
return m->descriptor;
}
return NULL;
}
/*
* Find the entry for a particular attribute in a descriptor.
*/
static const p11_attribute_descriptor_t *p11_find_attribute_in_descriptor(const p11_descriptor_t *descriptor,
const CK_ATTRIBUTE_TYPE type)
{
int i;
if (descriptor != NULL && descriptor->attributes != NULL)
for (i = 0; i < descriptor->n_attributes; i++)
if (descriptor->attributes[i].type == type)
return &descriptor->attributes[i];
return NULL;
}
/*
* Check whether an attribute is marked as sensitive. If we don't
* recognize the attribute, report it as sensitive (safer than the
* alternative).
*/
static int p11_attribute_is_sensitive(const p11_descriptor_t *descriptor,
const CK_ATTRIBUTE_TYPE type)
{
const p11_attribute_descriptor_t *a = p11_find_attribute_in_descriptor(descriptor, type);
return a == NULL || (a->flags & P11_DESCRIPTOR_SENSITIVE) != 0;
}
/*
* Object methods.
*/
/*
* Check access rights for an object.
*/
typedef enum { p11_object_access_read, p11_object_access_write } p11_object_access_t;
static CK_RV p11_object_check_rights(const p11_session_t *session,
const CK_OBJECT_HANDLE object_handle,
const p11_object_access_t rights)
{
static const char object_exists_query[] =
" SELECT count(*) FROM object WHERE object_handle = ?1";
static const char session_object_query[] =
" SELECT session_handle FROM session NATURAL JOIN object WHERE object_handle = ?1";
CK_BBOOL object_is_private;
sqlite3_stmt *q = NULL;
CK_RV rv;
if (session == NULL)
lose(CKR_SESSION_HANDLE_INVALID);
/*
* Read-only sessions are, um, read-only.
*/
switch (session->state) {
case CKS_RO_PUBLIC_SESSION:
case CKS_RO_USER_FUNCTIONS:
if (rights == p11_object_access_write)
lose(CKR_SESSION_READ_ONLY);
}
/*
* Private objects don't exist for sessions in the wrong state.
*/
switch (session->state) {
case CKS_RO_PUBLIC_SESSION:
case CKS_RW_PUBLIC_SESSION:
case CKS_RW_SO_FUNCTIONS:
if (!p11_attribute_get_bbool(object_handle, CKA_PRIVATE, &object_is_private) || object_is_private)
lose(CKR_OBJECT_HANDLE_INVALID);
}
/*
* Does the object even exist?
*/
if (!sql_check_ok(sql_prepare(&q, object_exists_query)) ||
!sql_check_ok(sqlite3_bind_int64(q, 1, object_handle)) ||
!sql_check_row(sqlite3_step(q)) ||
!sqlite3_column_int(q, 0))
lose(CKR_OBJECT_HANDLE_INVALID);
/*
* Session objects are only visible to the session which created them.
*/
if (!is_token_handle(object_handle) &&
(!sql_check_ok(sql_finalize_and_clear(&q)) ||
!sql_check_ok(sql_prepare(&q, session_object_query)) ||
!sql_check_ok(sqlite3_bind_int64(q, 1, object_handle)) ||
!sql_check_row(sqlite3_step(q)) ||
sqlite3_column_int64(q, 0) != session->handle))
lose(CKR_OBJECT_HANDLE_INVALID);
/*
* Ran out of reasons to reject, guess we should allow it.
*/
rv = CKR_OK;
fail:
sqlite3_finalize(q);
return rv;
}
/*
* Delete all private objects, probably because user logged out.
*
* In the case of token objects, the object itself remains in the
* token, we're just deleting our handle for the object.
*
* In the case of session objects, the object itself goes away.
*/
static int p11_object_delete_all_private(void)
{
static const char delete_format[] =
" WITH"
" s AS (SELECT session_object_id FROM session_attribute WHERE type = %u AND value <> X'00'),"
" t AS (SELECT token_object_id FROM token_attribute WHERE type = %u AND value <> X'00')"
" DELETE FROM object WHERE token_object_id IN t OR session_object_id IN s";
sqlite3_stmt *q = NULL;
int ok = 0;
if (!sql_check_ok(sql_prepare(&q, delete_format, CKA_PRIVATE, CKA_PRIVATE)) ||
!sql_check_done(sqlite3_step(q)))
goto fail;
ok = 1;
fail:
sqlite3_finalize(q);
return ok;
}
/*
* Create a new object.
*
* This is a bit nasty due to the SQL foreign key constraints and the
* different handling required for session and token objects.
*/
static CK_OBJECT_HANDLE p11_object_create(const p11_session_t *session,
const handle_flavor_t flavor,
const CK_ATTRIBUTE_PTR template,
const CK_ULONG template_length,
const p11_descriptor_t * const descriptor,
const CK_MECHANISM_PTR mechanism)
{
static const char insert_object[] =
" INSERT INTO object (object_handle)"
" VALUES (?)";
static const char insert_token_object[] =
" INSERT INTO token_object DEFAULT VALUES";
static const char insert_session_object[] =
" INSERT INTO session_object (object_id) VALUES (?)";
static const char update_object_session_object[] =
" UPDATE object SET"
" session_id = (SELECT session_id FROM session WHERE session_handle = ?1),"
" session_object_id = ?2"
" WHERE object_id = ?3";
static const char update_object_token_object[] =
" UPDATE object SET token_object_id = ?1 WHERE object_id = ?2";
static const char insert_token_attribute[] =
" INSERT OR REPLACE INTO token_attribute (token_object_id, type, value)"
" VALUES (?1, ?2, ?3)";
static const char insert_session_attribute[] =
" INSERT OR REPLACE INTO session_attribute (session_object_id, type, value)"
" VALUES (?1, ?2, ?3)";
CK_OBJECT_HANDLE object_handle = p11_allocate_unused_handle(flavor);;
sqlite3_int64 object_id, session_object_id, token_object_id;
sqlite3_stmt *q = NULL;
int i, ok = 0;
assert(session != NULL && template != NULL && descriptor != NULL &&
(flavor == handle_flavor_token_object ||
flavor == handle_flavor_session_object));
if (!sql_check_ok(sql_prepare(&q, insert_object)) ||
!sql_check_ok(sqlite3_bind_int64(q, 1, object_handle)) ||
!sql_check_done(sqlite3_step(q)))
goto fail;
object_id = sqlite3_last_insert_rowid(sqldb);
if (!sql_check_ok(sql_finalize_and_clear(&q)))
goto fail;
switch (flavor) {
case handle_flavor_token_object:
if (!sql_check_ok(sql_prepare(&q, insert_token_object)) ||
!sql_check_done(sqlite3_step(q)))
goto fail;
token_object_id = sqlite3_last_insert_rowid(sqldb);
if (!sql_check_ok(sql_finalize_and_clear(&q)) ||
!sql_check_ok(sql_prepare(&q, update_object_token_object)) ||
!sql_check_ok(sqlite3_bind_int64(q, 1, token_object_id)) ||
!sql_check_ok(sqlite3_bind_int64(q, 2, object_id)) ||
!sql_check_done(sqlite3_step(q)) ||
!sql_check_ok(sql_finalize_and_clear(&q)) ||
!sql_check_ok(sql_prepare(&q, insert_token_attribute)) ||
!sql_check_ok(sqlite3_bind_int64(q, 1, token_object_id)))
goto fail;
break;
case handle_flavor_session_object:
if (!sql_check_ok(sql_prepare(&q, insert_session_object)) ||
!sql_check_ok(sqlite3_bind_int64(q, 1, object_id)) ||
!sql_check_done(sqlite3_step(q)))
goto fail;
session_object_id = sqlite3_last_insert_rowid(sqldb);
if (!sql_check_ok(sql_finalize_and_clear(&q)) ||
!sql_check_ok(sql_prepare(&q, update_object_session_object)) ||
!sql_check_ok(sqlite3_bind_int64(q, 1, session->handle)) ||
!sql_check_ok(sqlite3_bind_int64(q, 2, session_object_id)) ||
!sql_check_ok(sqlite3_bind_int64(q, 3, object_id)) ||
!sql_check_done(sqlite3_step(q)) ||
!sql_check_ok(sql_finalize_and_clear(&q)) ||
!sql_check_ok(sql_prepare(&q, insert_session_attribute)) ||
!sql_check_ok(sqlite3_bind_int64(q, 1, session_object_id)))
goto fail;
break;
default: /* Suppress GCC warning */
goto fail;
}
/*
* Now populate attributes, starting with the application's
* template, which we assume has already been blessed by the API
* function that called this method.
*/
for (i = 0; i < template_length; i++) {
const CK_ATTRIBUTE_TYPE type = template[i].type;
const void * val = template[i].pValue;
const int len = template[i].ulValueLen;
if (!sql_check_ok(sqlite3_reset(q)) ||
!sql_check_ok(sqlite3_bind_int64(q, 2, type)) ||
!sql_check_ok(sqlite3_bind_blob( q, 3, val, len, NULL)) ||
!sql_check_done(sqlite3_step(q)))
goto fail;
}
/*
* Next, add defaults from the descriptor.
*/
for (i = 0; i < descriptor->n_attributes; i++) {
const CK_ATTRIBUTE_TYPE type = descriptor->attributes[i].type;
const void * val = descriptor->attributes[i].value;
const int len = descriptor->attributes[i].length;
const unsigned flags = descriptor->attributes[i].flags;
if (val == NULL && (flags & P11_DESCRIPTOR_DEFAULT_VALUE) != 0)
val = "";
if (val == NULL || p11_attribute_find_in_template(type, template, template_length) >= 0)
continue;
if (!sql_check_ok(sqlite3_reset(q)) ||
!sql_check_ok(sqlite3_bind_int64(q, 2, type)) ||
!sql_check_ok(sqlite3_bind_blob( q, 3, val, len, NULL)) ||
!sql_check_done(sqlite3_step(q)))
goto fail;
}
/*
* Finally, add generation mechanism attributes as needed.
*/
if (mechanism != NULL &&
(!sql_check_ok(sqlite3_reset(q)) ||
!sql_check_ok(sqlite3_bind_int64(q, 2, CKA_LOCAL)) ||
!sql_check_ok(sqlite3_bind_blob( q, 3, &const_CK_TRUE, sizeof(const_CK_TRUE), NULL)) ||
!sql_check_done(sqlite3_step(q)) ||
!sql_check_ok(sqlite3_reset(q)) ||
!sql_check_ok(sqlite3_bind_int64(q, 2, CKA_KEY_GEN_MECHANISM)) ||
!sql_check_ok(sqlite3_bind_blob( q, 3, &mechanism->mechanism, sizeof(mechanism->mechanism), NULL)) ||
!sql_check_done(sqlite3_step(q))))
goto fail;
/*
* If we made it past all that, we're happy.
*/
ok = 1;
fail:
sqlite3_finalize(q);
return ok ? object_handle : CK_INVALID_HANDLE;
}
/*
* Store an RSA private key.
*
* Write the key as PKCS #1.5 RSAPrivateKey DER, encrypt that using
* AES key wrap, and store the result as an SQL blob.
*
* We jump through a few minor hoops to let us do all the encoding and
* wrapping in place in a single buffer.
*/
static int p11_object_set_rsa_private_key(const CK_OBJECT_HANDLE object_handle,
const hal_rsa_key_t key)
{
static const char select_kek[] =
" SELECT kek FROM global";
static const char update_format[] =
" UPDATE %s_object SET private_key = ?1"
" WHERE %s_object_id = (SELECT %s_object_id FROM object WHERE object_handle = ?2)";
uint8_t wrapbuf[hal_aes_keywrap_ciphertext_length(hal_rsa_key_to_der_len(key))];
const char *flavor = is_token_handle(object_handle) ? "token" : "session";
size_t der_len, wrapbuf_len = sizeof(wrapbuf);
sqlite3_stmt *q = NULL;
int ok = 0;
if (!sql_check_ok(sql_prepare(&q, select_kek)) ||
!sql_check_row(sqlite3_step(q)) ||
sqlite3_column_type(q, 0) == SQLITE_NULL ||
!hal_check(hal_rsa_key_to_der(key, wrapbuf+8, &der_len,
sizeof(wrapbuf)-8)) ||
!hal_check(hal_aes_keywrap(sqlite3_column_blob(q, 0),
sqlite3_column_bytes(q, 0),
wrapbuf+8, der_len, wrapbuf, &wrapbuf_len)) ||
!sql_check_ok(sql_finalize_and_clear(&q)) ||
!sql_check_ok(sql_prepare(&q, update_format, flavor, flavor, flavor)) ||
!sql_check_ok(sqlite3_bind_blob( q, 1, wrapbuf, wrapbuf_len, NULL)) ||
!sql_check_ok(sqlite3_bind_int64(q, 2, object_handle)) ||
!sql_check_done(sqlite3_step(q)))
goto fail;
ok = 1;
fail:
memset(wrapbuf, 0, sizeof(wrapbuf));
sqlite3_finalize(q);
return ok;
}
/*
* Fetch an RSA private key.
*
* Retrieve SQL blob from the object, unwrap that to get the DER
* encoding of a PKCS #1.5 RSAPrivateKey object, load the key from
* that.
*
* If the key isn't set, we return success with null key.
*/
static int p11_object_get_rsa_private_key(const CK_OBJECT_HANDLE object_handle,
hal_rsa_key_t *key,
uint8_t *keybuf, const size_t keybuf_len)
{
static const char select_format[] =
" SELECT kek, private_key FROM global, %s_object NATURAL JOIN object WHERE object_handle = ?1";
const char *flavor = is_token_handle(object_handle) ? "token" : "session";
sqlite3_stmt *q = NULL;
int ok;
assert(key != NULL && keybuf != NULL);
/*
* Pull everything we need from the database.
*/
if (!sql_check_ok(sql_prepare(&q, select_format, flavor)) ||
!sql_check_ok(sqlite3_bind_int64(q, 1, object_handle)) ||
!sql_check_row(sqlite3_step(q)) ||
sqlite3_column_type(q, 0) == SQLITE_NULL) {
ok = 0;
}
else if (sqlite3_column_type(q, 1) == SQLITE_NULL) {
key->key = NULL;
ok = 1;
}
else {
const uint8_t * const kek = sqlite3_column_blob(q, 0);
const uint8_t * const pkey = sqlite3_column_blob(q, 1);
const size_t kek_len = sqlite3_column_bytes(q, 0);
const size_t pkey_len = sqlite3_column_bytes(q, 1);
size_t wrapbuf_len = pkey_len;
uint8_t wrapbuf[pkey_len];
ok = (hal_check(hal_aes_keyunwrap(kek, kek_len, pkey, pkey_len, wrapbuf, &wrapbuf_len)) &&
hal_check(hal_rsa_key_from_der(key, keybuf, keybuf_len, wrapbuf, wrapbuf_len)));
memset(wrapbuf, 0, sizeof(wrapbuf));
}
if (!ok || key->key == NULL)
memset(keybuf, 0, keybuf_len);
sqlite3_finalize(q);
return ok;
}
#warning Revisit return semantics of p11_object_get_rsa_private_key() and p11_object_get_rsa_public_key()
/*
* Fetch an RSA public key.
*
* Public keys aren't stored separately the way that private keys are,
* so we're looking for the public components so we can load them into
* a key objet.
*/
static int p11_object_get_rsa_public_key(const CK_OBJECT_HANDLE object_handle,
hal_rsa_key_t *key,
uint8_t *keybuf, const size_t keybuf_len)
{
static const char select_format[] =
" WITH a (type, value) "
" AS (SELECT type, value FROM %s_attribute NATURAL JOIN object WHERE object_handle = ?1)"
" SELECT a1.value, a2.value FROM a AS a1, a AS a2 WHERE a1.type = %u AND a2.type = %u";
const char *flavor = is_token_handle(object_handle) ? "token" : "session";
sqlite3_stmt *q = NULL;
assert(key != NULL && keybuf != NULL);
const int ok = (sql_check_ok(sql_prepare(&q, select_format, flavor,
CKA_MODULUS, CKA_PUBLIC_EXPONENT)) &&
sql_check_ok(sqlite3_bind_int64(q, 1, object_handle)) &&
sql_check_row(sqlite3_step(q)) &&
sqlite3_column_type(q, 0) == SQLITE_BLOB &&
sqlite3_column_type(q, 1) == SQLITE_BLOB &&
hal_check(hal_rsa_key_load_public(key, keybuf, keybuf_len,
sqlite3_column_blob( q, 0),
sqlite3_column_bytes(q, 0),
sqlite3_column_blob( q, 1),
sqlite3_column_bytes(q, 1))));
sqlite3_finalize(q);
return ok;
}
/*
* Session methods.
*/
/*
* Create a new session.
*/
static p11_session_t *p11_session_new(void)
{
p11_session_t *session = malloc(sizeof(*session));
if (session == NULL)
return NULL;
memset(session, 0, sizeof(*session));
return session;
}
/*
* Free a session.
*/
static void p11_session_free(p11_session_t *session)
{
if (session == NULL)
return;
sql_finalize_and_clear(&session->find_query);
free(session);
}
/*
* Assign a handle to a session and add the session to SQL.
*/
static int p11_session_add(p11_session_t *session)
{
static const char insert_session[] =
" INSERT INTO session (session_handle) VALUES (?)";
sqlite3_stmt *q = NULL;
int ok = 0;
assert(session != NULL);
session->handle = p11_allocate_unused_handle(handle_flavor_session);
if (!sql_check_ok(sql_prepare(&q, insert_session)) ||
!sql_check_ok(sqlite3_bind_int64(q, 1, session->handle)) ||
!sql_check_done(sqlite3_step(q)))
goto fail;
session->link = p11_sessions;
p11_sessions = session;
ok = 1;
fail:
sqlite3_finalize(q);
return ok;
}
/*
* Find a session.
*
* Since we don't expect the total number of sessions to be all that
* high, we use a linked list with a move-to-the-front search. Some
* of the other session methods assume this behavior, so be careful if
* you decide to change it.
*/
static p11_session_t *p11_session_find(const CK_SESSION_HANDLE session_handle)
{
p11_session_t **link, *session;
for (link = &p11_sessions;
(session = *link) != NULL && session->handle != session_handle;
link = &session->link)
;
if (session != NULL && link != &p11_sessions) {
*link = session->link;
session->link = p11_sessions;
p11_sessions = session;
}
return session;
}
/*
* Delete a session: remove it from SQL and free the session data
* structure.
*
* This method assumes the move-to-the-front behavior of
* p11_session_find().
*/
static CK_RV p11_session_delete(const CK_SESSION_HANDLE session_handle)
{
static const char delete_session[] =
" DELETE FROM session WHERE session_handle = ?";
p11_session_t *session = p11_session_find(session_handle);
sqlite3_stmt *q = NULL;
CK_RV rv = CKR_OK;
if (session == NULL)
return CKR_SESSION_HANDLE_INVALID;
if (!sql_check_ok(sql_prepare(&q, delete_session)) ||
!sql_check_ok(sqlite3_bind_int64(q, 1, session_handle)) ||
!sql_check_done(sqlite3_step(q)))
lose(CKR_FUNCTION_FAILED);
/* Check that move-to-the-front behaved as expected */
assert(p11_sessions == session);
p11_sessions = session->link;
p11_session_free(session);
fail:
sqlite3_finalize(q);
return rv;
}
/*
* Delete all sessions.
*/
#warning Should this also clear the object table?
static CK_RV p11_session_delete_all(void)
{
static const char delete_all_sessions[] =
" DELETE FROM session";
p11_session_t *session;
CK_RV rv = CKR_OK;
if (!sql_exec(delete_all_sessions))
lose(CKR_FUNCTION_FAILED);
while (p11_sessions != NULL) {
session = p11_sessions;
p11_sessions = session->link;
p11_session_free(session);
}
fail:
return rv;
}
/*
* Check session database against login state for consistency.
*
* This is mostly useful in assertions.
*/
static int p11_session_consistent_login(void)
{
p11_session_t *session;
switch (logged_in_as) {
case not_logged_in:
for (session = p11_sessions; session != NULL; session = session->link)
if (session->state != CKS_RO_PUBLIC_SESSION && session->state != CKS_RW_PUBLIC_SESSION)
return 0;
return 1;
case logged_in_as_user:
for (session = p11_sessions; session != NULL; session = session->link)
if (session->state != CKS_RO_USER_FUNCTIONS && session->state != CKS_RW_USER_FUNCTIONS)
return 0;
return 1;
case logged_in_as_so:
for (session = p11_sessions; session != NULL; session = session->link)
if (session->state != CKS_RW_SO_FUNCTIONS)
return 0;
return 1;
default:
return 0;
}
}
/*
* PKCS #11 likes space-padded rather than null-terminated strings.
*/
static int psnprintf(void *buffer_, size_t size, const char *format, ...)
{
char *buffer = buffer_;
size_t i, n;
va_list ap;
va_start(ap, format);
i = n = vsnprintf(buffer, size, format, ap);
va_end(ap);
while (i < size)
buffer[i++] = ' ';
return n;
}
/*
* Template checking and key generation.
*
* This may need refactoring at some point, eg, when we add support
* for C_CreateObject().
*/
/*
* First pass: called once per template entry during initial pass over
* template to handle generic checks that apply regardless of
* attribute type.
*/
static CK_RV p11_check_keypair_attributes_check_template_1(const CK_ATTRIBUTE_TYPE type,
const void * const val,
const size_t len,
const p11_descriptor_t * const descriptor)
{
const p11_attribute_descriptor_t * const atd = p11_find_attribute_in_descriptor(descriptor, type);
CK_RV rv;
/* Attribute not allowed or not allowed for key generation */
if (atd == NULL || (atd->flags & P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE) != 0)
lose(CKR_ATTRIBUTE_TYPE_INVALID);
/* NULL or wrong-sized attribute values */
if (val == NULL || (atd->size != 0 && len != atd->size))
lose(CKR_ATTRIBUTE_VALUE_INVALID);
/* Attributes which only the SO user is allowed to set to CK_TRUE */
if ((atd->flags & P11_DESCRIPTOR_ONLY_SO_USER_CAN_SET) != 0 && logged_in_as != logged_in_as_so && *(CK_BBOOL *) val)
lose(CKR_ATTRIBUTE_VALUE_INVALID);
/* Attributes which don't match mandatory values */
if (atd->value != NULL && (atd->flags & P11_DESCRIPTOR_DEFAULT_VALUE) == 0 && memcmp(val, atd->value, atd->length) != 0)
lose(CKR_TEMPLATE_INCONSISTENT);
rv = CKR_OK;
fail:
#if DEBUG_PKCS11
if (rv != CKR_OK)
fprintf(stderr, "p11_check_keypair_attributes_check_template_1() rejected attribute 0x%08lx\n",
(unsigned long) type);
#endif
return rv;
}
/*
* Second pass: called once per template to check that each attribute
* required for that template has been specified exactly once.
*/
static CK_RV p11_check_keypair_attributes_check_template_2(const p11_session_t *session,
const p11_descriptor_t * const descriptor,
const CK_ATTRIBUTE_PTR template,
const CK_ULONG template_length)
{
const CK_BBOOL *object_is_private;
CK_RV rv;
int i, j;
/*
* Some session states aren't allowed to play with private objects.
*/
switch (session->state) {
case CKS_RO_PUBLIC_SESSION:
case CKS_RW_PUBLIC_SESSION:
case CKS_RW_SO_FUNCTIONS:
if ((i = p11_attribute_find_in_template(CKA_PRIVATE, template, template_length)) >= 0) {
assert(template[i].pValue != NULL);
object_is_private = template[i].pValue;
}
else {
const p11_attribute_descriptor_t * const atd = p11_find_attribute_in_descriptor(descriptor, CKA_PRIVATE);
assert(atd != NULL && atd->value != NULL);
object_is_private = atd->value;
}
if (*object_is_private)
lose(CKR_TEMPLATE_INCONSISTENT);
}
for (i = 0; i < descriptor->n_attributes; i++) {
const p11_attribute_descriptor_t * const atd = &descriptor->attributes[i];
const int required_by_api = (atd->flags & P11_DESCRIPTOR_REQUIRED_BY_GENERATE) != 0;
const int forbidden_by_api = (atd->flags & P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE) != 0;
const int in_descriptor = (atd->flags & P11_DESCRIPTOR_DEFAULT_VALUE) != 0 || atd->value != NULL;
const int pos_in_template = p11_attribute_find_in_template(atd->type, template, template_length);
/* Multiple entries for same attribute */
if (pos_in_template >= 0)
for (j = pos_in_template + 1; j < template_length; j++)
if (template[j].type == atd->type)
lose(CKR_TEMPLATE_INCONSISTENT);
/* Required attribute missing from template */
if (!forbidden_by_api && (required_by_api || !in_descriptor) && pos_in_template < 0) {
#if DEBUG_PKCS11
fprintf(stderr, "[Missing attribute 0x%lx]\n", atd->type);
#endif
lose(CKR_TEMPLATE_INCOMPLETE);
}
}
rv = CKR_OK;
fail:
return rv;
}
/*
* Mechanism-independent checks for templates and descriptors when
* generating new keypairs.
*
* PKCS #11 gives the application far too much rope (including but not
* limited to the ability to supply completely unrelated templates for
* public and private keys in a keypair), so we need to do a fair
* amount of checking. We automate as much of the dumb stuff as
* possible through the object descriptor.
*
* Key usage handling here is based on RFC 5280 4.2.1.3.
*
* PKCS #11 suggests but does not require CKA_ID values for public and
* private key to match.
*/
static CK_RV p11_check_keypair_attributes(const p11_session_t *session,
const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
const CK_ULONG ulPublicKeyAttributeCount,
const p11_descriptor_t * const public_descriptor,
const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
const CK_ULONG ulPrivateKeyAttributeCount,
const p11_descriptor_t * const private_descriptor)
{
unsigned public_keyusage = 0, private_keyusage = 0;
CK_RV rv = CKR_OK;
int i;
assert(session != NULL &&
pPublicKeyTemplate != NULL && public_descriptor != NULL &&
pPrivateKeyTemplate != NULL && private_descriptor != NULL);
/*
* Read-only sessions can't create keys, doh.
*/
switch (session->state) {
case CKS_RO_PUBLIC_SESSION:
case CKS_RO_USER_FUNCTIONS:
lose(CKR_SESSION_READ_ONLY);
}
/*
* Check values provided in the public and private templates.
*/
for (i = 0; i < ulPublicKeyAttributeCount; i++) {
const CK_ATTRIBUTE_TYPE type = pPublicKeyTemplate[i].type;
const void * const val = pPublicKeyTemplate[i].pValue;
const size_t len = pPublicKeyTemplate[i].ulValueLen;
if ((rv = p11_check_keypair_attributes_check_template_1(type, val, len, public_descriptor)) != CKR_OK)
goto fail;
p11_attribute_apply_keyusage(&public_keyusage, type, val);
}
for (i = 0; i < ulPrivateKeyAttributeCount; i++) {
const CK_ATTRIBUTE_TYPE type = pPrivateKeyTemplate[i].type;
const void * const val = pPrivateKeyTemplate[i].pValue;
const size_t len = pPrivateKeyTemplate[i].ulValueLen;
if ((rv = p11_check_keypair_attributes_check_template_1(type, val, len, private_descriptor)) != CKR_OK)
goto fail;
p11_attribute_apply_keyusage(&private_keyusage, type, val);
}
/*
* We insist that keyusage be specified for both public and private
* key, and that they match. May not need to be this strict.
*/
if (public_keyusage != private_keyusage || public_keyusage == 0)
lose(CKR_TEMPLATE_INCONSISTENT);
/*
* Check that all required attributes have been specified.
*/
if ((rv = p11_check_keypair_attributes_check_template_2(session,
public_descriptor,
pPublicKeyTemplate,
ulPublicKeyAttributeCount)) != CKR_OK ||
(rv = p11_check_keypair_attributes_check_template_2(session,
private_descriptor,
pPrivateKeyTemplate,
ulPrivateKeyAttributeCount)) != CKR_OK)
goto fail;
/*
* If we get this far, we're happy. Maybe.
*/
rv = CKR_OK;
fail:
return rv;
}
/*
* CKM_RSA_PKCS_KEY_PAIR_GEN key pair generation implemetation.
*
* Much mechanism-independent code has already been factored out of
* this function, no doubt much remains that will require further
* refactoring once we implement other mechanisms.
*/
static CK_RV generate_keypair_rsa_pkcs(p11_session_t *session,
const CK_MECHANISM_PTR pMechanism,
const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
const CK_ULONG ulPublicKeyAttributeCount,
const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
const CK_ULONG ulPrivateKeyAttributeCount,
CK_OBJECT_HANDLE_PTR phPublicKey,
CK_OBJECT_HANDLE_PTR phPrivateKey)
{
CK_OBJECT_HANDLE private_handle = CK_INVALID_HANDLE;
CK_OBJECT_HANDLE public_handle = CK_INVALID_HANDLE;
handle_flavor_t public_handle_flavor = handle_flavor_session_object;
handle_flavor_t private_handle_flavor = handle_flavor_session_object;
uint8_t
keybuf[hal_rsa_key_t_size],
modulus[hal_rsa_key_t_size/8],
public_exponent[hal_rsa_key_t_size/8];
size_t modulus_len;
CK_ULONG public_exponent_len;
hal_rsa_key_t key = { NULL };
CK_ULONG keysize = 0;
CK_RV rv;
int i;
/*
* Do mechanism-independent checks before anything else.
*/
rv = p11_check_keypair_attributes(session,
pPublicKeyTemplate, ulPublicKeyAttributeCount, &p11_descriptor_rsa_public_key,
pPrivateKeyTemplate, ulPrivateKeyAttributeCount, &p11_descriptor_rsa_private_key);
if (rv != CKR_OK)
return rv;
assert(session != NULL && pMechanism != NULL &&
pPublicKeyTemplate != NULL && phPublicKey != NULL &&
pPrivateKeyTemplate != NULL && phPrivateKey != NULL);
/*
* Grab values and perform mechanism-specific checks.
*/
for (i = 0; i < ulPublicKeyAttributeCount; i++) {
const CK_ATTRIBUTE_TYPE type = pPublicKeyTemplate[i].type;
const void * const val = pPublicKeyTemplate[i].pValue;
#if 0
const size_t len = pPublicKeyTemplate[i].ulValueLen;
#endif
assert(val != NULL);
switch (type) {
case CKA_TOKEN: /* Object stored on token */
public_handle_flavor = p11_handle_flavor_from_cka_token(val);
continue;
case CKA_MODULUS_BITS: /* Keysize in bits -- only allow multiples of 8 */
keysize = *(CK_ULONG *) val;
if ((keysize & 7) != 0)
return CKR_ATTRIBUTE_VALUE_INVALID;
continue;
}
}
for (i = 0; i < ulPrivateKeyAttributeCount; i++) {
const CK_ATTRIBUTE_TYPE type = pPrivateKeyTemplate[i].type;
const void * const val = pPrivateKeyTemplate[i].pValue;
#if 0
const size_t len = pPrivateKeyTemplate[i].ulValueLen;
#endif
assert (val != NULL);
switch (type) {
case CKA_TOKEN: /* Object stored on token */
private_handle_flavor = p11_handle_flavor_from_cka_token(val);
continue;
}
}
/*
* We require a key size, and if either key is a token object, the
* other must be too.
*/
if (keysize == 0 || public_handle_flavor != private_handle_flavor)
return CKR_TEMPLATE_INCOMPLETE;
/*
* Create the PKCS #11 objects and generate the keypair.
*/
if (!sql_exec("BEGIN") ||
(public_handle = p11_object_create(session, public_handle_flavor,
pPublicKeyTemplate, ulPublicKeyAttributeCount,
&p11_descriptor_rsa_public_key,
pMechanism)) == CK_INVALID_HANDLE ||
(private_handle = p11_object_create(session, private_handle_flavor,
pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
&p11_descriptor_rsa_private_key,
pMechanism)) == CK_INVALID_HANDLE ||
!p11_attribute_get(public_handle, CKA_PUBLIC_EXPONENT,
public_exponent, &public_exponent_len, sizeof(public_exponent)) ||
!hal_check(hal_rsa_key_gen(&key, keybuf, sizeof(keybuf), keysize/8,
public_exponent, (size_t) public_exponent_len)) ||
!p11_object_set_rsa_private_key(private_handle, key) ||
!hal_check(hal_rsa_key_get_modulus(key, modulus, &modulus_len, sizeof(modulus))) ||
!p11_attribute_set(public_handle, CKA_MODULUS, modulus, modulus_len) ||
!p11_attribute_set(private_handle, CKA_MODULUS, modulus, modulus_len))
lose(CKR_FUNCTION_FAILED);
hal_rsa_key_clear(key);
/*
* Commit the SQL transaction.
*/
if (!sql_exec("COMMIT"))
lose(CKR_FUNCTION_FAILED);
/*
* All went well, return handles and we're done.
*/
*phPublicKey = public_handle;
*phPrivateKey = private_handle;
return CKR_OK;
fail:
memset(keybuf, 0, sizeof(keybuf));
if (!sql_exec("ROLLBACK"))
rv = CKR_GENERAL_ERROR;
return rv;
}
/*
* Construct a PKCS #1 DigestInfo object. This requires some (very
* basic) ASN.1 encoding, which we perform inline.
*/
static int pkcs1_construct_digestinfo(const hal_hash_descriptor_t * const desc,
const uint8_t * const data, const size_t data_len,
uint8_t *digest_info, const size_t digest_info_len)
{
uint8_t statebuf[desc->hash_state_length];
hal_hash_state_t state = { NULL };
uint8_t *d = digest_info;
/*
* Make sure size of output buffer is right. Caller is responsible
* for supplying the right length, the check here is just paranoia.
*
* This encoder will fail if the DigestInfo object is more than
* 129 octets long. Rewrite if and when we need to support
* digests or OIDs long enough for that to be an issue.
*/
assert(digest_info_len == desc->digest_length + desc->digest_algorithm_id_length + 4);
assert(digest_info_len < 130);
*d++ = 0x30; /* SEQUENCE */
*d++ = (uint8_t) (digest_info_len - 2);
memcpy(d, desc->digest_algorithm_id, desc->digest_algorithm_id_length);
d += desc->digest_algorithm_id_length;
*d++ = 0x04; /* OCTET STRING */
*d++ = (uint8_t) desc->digest_length;
assert(digest_info + digest_info_len == d + desc->digest_length);
const int ok = (hal_check(hal_hash_initialize(desc, &state, statebuf, sizeof(statebuf))) &&
hal_check(hal_hash_update(state, data, data_len)) &&
hal_check(hal_hash_finalize(state, d, desc->digest_length)));
memset(statebuf, 0, sizeof(statebuf));
if (!ok)
memset(digest_info, 0, digest_info_len);
return ok;
}
/*
* Pad an octet string with PKCS #1.5 padding for use with RSA.
*
* For the moment, this only handles type 01 encryption blocks, thus
* is only suitable for use with signature and verification. If and
* when we add support for encryption and decryption, this function
* should be extended to take an argument specifying the block type
* and include support for generating type 02 encryption blocks.
* Other than the block type code, the only difference is the padding
* value: for type 01 it's constant (0xFF), for type 02 it should be
* non-zero random bytes from the CSPRNG.
*/
static int pkcs1_5_pad(const uint8_t * const data, const size_t data_len,
uint8_t *block, const size_t block_len)
{
assert(data != NULL && block != NULL);
/*
* Congregation will now please turn to RFC 2313 8.1 as we
* construct a PKCS #1.5 type 01 encryption block.
*/
if (data_len > block_len - 11)
return 0;
block[0] = 0x00;
block[1] = 0x01;
/* This is where we'd use non-zero random bytes if constructing a type 02 block. */
memset(block + 2, 0xFF, block_len - 3 - data_len);
block[block_len - data_len - 1] = 0x00;
memcpy(block + block_len - data_len, data, data_len);
#if DEBUG_PKCS11 > 1
fprintf(stderr, "[PKCS #1.5 block_len %lu data_len %lu block ",
(unsigned long) block_len, (unsigned long) data_len);
for (int i = 0; i < block_len; i++)
fprintf(stderr, "%s%02x", i == 0 ? "" : ":", block[i]);
fprintf(stderr, "]\n");
#endif
return 1;
}
/*
* Sign a PKCS #1 DigestInfo using an RSA key and PKCS #1.5 padding.
*
* As explained in RFC 3447, the RSASP1 (signature generation)
* operation is the same mathematical operation as the RSADP
* (decryption) operation (both use the private key as exponent).
*/
static CK_RV sign_rsa_pkcs(hal_rsa_key_t key,
const uint8_t * const digest_info, const size_t digest_info_len,
uint8_t *signature, const size_t signature_len)
{
CK_RV rv;
assert(digest_info != NULL && signature != NULL);
if (!pkcs1_5_pad(digest_info, digest_info_len, signature, signature_len))
lose(CKR_DATA_LEN_RANGE);
if (!hal_check(hal_rsa_decrypt(key, signature, signature_len, signature, signature_len)))
lose(CKR_FUNCTION_FAILED);
return CKR_OK;
fail:
memset(signature, 0, signature_len);
return rv;
}
/*
* Verify a PKCS #1.5 padded RSA signature.
*
* We don't bother decoding the ASN.1, we just generate the type 01
* encryption block we expect and compare it with what we got.
*
* Using constant-time comparision code for this is probably
* unnecessary, but it's also harmless.
*/
static CK_RV verify_rsa_pkcs(hal_rsa_key_t key,
const uint8_t * const digest_info, const size_t digest_info_len,
const uint8_t * const signature, const size_t signature_len)
{
assert(digest_info != NULL && signature != NULL);
uint8_t expected[signature_len], received[signature_len];
unsigned diff = 0;
CK_RV rv;
if (!pkcs1_5_pad(digest_info, digest_info_len, expected, sizeof(expected)))
lose(CKR_DATA_LEN_RANGE);
if (!hal_check(hal_rsa_encrypt(key, signature, signature_len, received, sizeof(received))))
lose(CKR_FUNCTION_FAILED);
for (int i = 0; i < signature_len; i++)
diff |= expected[i] ^ received[i];
if (diff != 0)
lose(CKR_SIGNATURE_INVALID);
rv = CKR_OK;
fail:
memset(expected, 0, sizeof(expected));
memset(received, 0, sizeof(received));
return rv;
}
/*
* PKCS #11 API functions.
*/
CK_RV C_Initialize(CK_VOID_PTR pInitArgs)
{
ENTER_PUBLIC_FUNCTION(C_Initialize);
CK_C_INITIALIZE_ARGS_PTR a = pInitArgs;
int initialized_sql = 0;
CK_RV rv;
/*
* We'd like to detect the error of calling this method more than
* once in a single process without an intervening call to
* C_Finalize(), but there's no completely portable way to do that
* when faced with things like the POSIX fork() system call. For
* the moment, we use a POSIX-specific check, but may need to
* generalize this for other platforms.
*/
#if USE_POSIX
if (initialized_pid == getpid())
lose(CKR_CRYPTOKI_ALREADY_INITIALIZED);
#endif
/*
* Sort out what the user wants to do about mutexes. Default is not
* to use mutexes at all.
*
* There's a chicken and egg problem here: setting up the global
* mutex and mutex function pointers creates a race condition, and
* there's no obvious action we can take which is robust in the face
* of pathological behavior by the caller such as simultaneous calls
* to this method with incompatible mutex primitives.
*
* Given that (a) it's an error to call this method more than once
* in the same process without an intervening F_Finalize() call, and
* given that (b) we haven't actually promised to do any kind of
* locking at all until this method returns CKR_OK, we punt
* responsibility for this pathological case back to the caller.
*/
mutex_cb_create = NULL;
mutex_cb_destroy = NULL;
mutex_cb_lock = NULL;
mutex_cb_unlock = NULL;
if (a != NULL) {
const int functions_provided = ((a->CreateMutex != NULL) +
(a->DestroyMutex != NULL) +
(a->LockMutex != NULL) +
(a->UnlockMutex != NULL));
/*
* Reserved is, um, reserved.
* Mutex parameters must either all be present or all be absent.
*/
if (a->pReserved != NULL || (functions_provided & 3) != 0)
lose(CKR_ARGUMENTS_BAD);
/*
* If the user provided mutex functions, use them. Otherwise, if
* the user wants locking, use POSIX mutexes or return an error
* depending on whether we have POSIX mutexes available.
* Otherwise, we don't need to use mutexes.
*/
if (functions_provided) {
mutex_cb_create = a->CreateMutex;
mutex_cb_destroy = a->DestroyMutex;
mutex_cb_lock = a->LockMutex;
mutex_cb_unlock = a->UnlockMutex;
}
else if ((a->flags & CKF_OS_LOCKING_OK) != 0) {
#if USE_PTHREADS
mutex_cb_create = posix_mutex_create;
mutex_cb_destroy = posix_mutex_destroy;
mutex_cb_lock = posix_mutex_lock;
mutex_cb_unlock = posix_mutex_unlock;
#else
lose(CKR_CANT_LOCK);
#endif
}
}
/*
* Now that we know which mutex implementation to use, set up a
* global mutex. We may want something finer grained later, but
* this is enough to preserve the basic API semantics.
*
* Open question whether we should lock at this point, given that
* until we return we haven't promised to do locking. Skip for now
* as it's simpler, fix later if it turns out to be a problem.
*/
if ((rv = mutex_create(&p11_global_mutex)) != CKR_OK)
goto fail;
/*
* Initialize SQLite3, opening the database(s) and loading the
* schema and views.
*/
if (!sql_init())
lose(CKR_GENERAL_ERROR);
initialized_sql = 1;
if (!kek_init())
lose(CKR_GENERAL_ERROR);
#if USE_POSIX
initialized_pid = getpid();
#endif
return CKR_OK;
fail:
if (initialized_sql)
sql_fini();
return rv;
}
CK_RV C_Finalize(CK_VOID_PTR pReserved)
{
ENTER_PUBLIC_FUNCTION(C_Finalize);
CK_RV rv = CKR_OK;
if (pReserved != NULL)
return CKR_ARGUMENTS_BAD;
mutex_lock_or_return_failure(p11_global_mutex);
/*
* Destroy all current sessions.
*/
p11_session_delete_all();
/*
* Shut down SQLite3.
*/
if (!sql_fini())
lose(CKR_GENERAL_ERROR);
/*
* By this point we're pretty well committed to shutting down, so
* there's not much to be done if these mutex operations fail.
*/
rv = mutex_unlock(p11_global_mutex);
(void) mutex_destroy(p11_global_mutex);
p11_global_mutex = NULL;
return rv;
fail:
(void) mutex_unlock(p11_global_mutex);
return rv;
}
CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList)
{
ENTER_PUBLIC_FUNCTION(C_GetFunctionList);
/*
* Use pkcs11f.h to build dispatch vector for C_GetFunctionList().
* This should be const, but that's not what PKCS #11 says, oh well.
*
* This doesn't touch anything requiring locks, nor should it.
*/
static CK_FUNCTION_LIST ck_function_list = {
{ CRYPTOKI_VERSION_MAJOR, CRYPTOKI_VERSION_MINOR },
#define CK_PKCS11_FUNCTION_INFO(name) name,
#include "pkcs11f.h"
#undef CK_PKCS11_FUNCTION_INFO
};
if (ppFunctionList == NULL)
return CKR_ARGUMENTS_BAD;
*ppFunctionList = &ck_function_list;
return CKR_OK;
}
CK_RV C_GetSlotList(CK_BBOOL tokenPresent,
CK_SLOT_ID_PTR pSlotList,
CK_ULONG_PTR pulCount)
{
ENTER_PUBLIC_FUNCTION(C_GetSlotList);
/*
* We only have one slot, and it's hardwired.
* No locking required here as long as this holds.
*/
if (pulCount == NULL)
return CKR_ARGUMENTS_BAD;
if (pSlotList != NULL && *pulCount < 1)
return CKR_BUFFER_TOO_SMALL;
*pulCount = 1;
if (pSlotList != NULL)
pSlotList[0] = P11_ONE_AND_ONLY_SLOT;
return CKR_OK;
}
CK_RV C_GetTokenInfo(CK_SLOT_ID slotID,
CK_TOKEN_INFO_PTR pInfo)
{
ENTER_PUBLIC_FUNCTION(C_GetTokenInfo);
/*
* No locking required here as long as we're just returning constants.
*/
if (pInfo == NULL)
return CKR_ARGUMENTS_BAD;
if (slotID != P11_ONE_AND_ONLY_SLOT)
return CKR_SLOT_ID_INVALID;
memset(pInfo, 0, sizeof(*pInfo));
/*
* No real idea (yet) how we get many of the following parameters.
*
* pInfo->label is supposed to be set when the token is initialized.
* Not yet sure what that means in our context, but need something
* here or the libhsm test programs will bomb trying to find the
* right token, so hard-wire something for now.
*/
psnprintf(pInfo->label, sizeof(pInfo->label),
"Cryptech Token");
psnprintf(pInfo->manufacturerID, sizeof(pInfo->manufacturerID),
"Cryptech Project");
psnprintf(pInfo->model, sizeof(pInfo->model),
"%04x%04x%04x%04x",
P11_VERSION_HW_MAJOR, P11_VERSION_HW_MINOR,
P11_VERSION_SW_MAJOR, P11_VERSION_SW_MINOR);
psnprintf(pInfo->serialNumber, sizeof(pInfo->serialNumber),
"007");
pInfo->flags = CKF_RNG | CKF_LOGIN_REQUIRED;
#warning Have not yet sorted out token flags
#if 0
CKF_RNG
CKF_WRITE_PROTECTED
CKF_LOGIN_REQUIRED
CKF_USER_PIN_INITIALIZED
CKF_RESTORE_KEY_NOT_NEEDED
CKF_CLOCK_ON_TOKEN
CKF_PROTECTED_AUTHENTICATION_PATH
CKF_DUAL_CRYPTO_OPERATIONS
CKF_TOKEN_INITIALIZED
CKF_SECONDARY_AUTHENTICATION
CKF_USER_PIN_COUNT_LOW
CKF_USER_PIN_FINAL_TRY
CKF_USER_PIN_LOCKED
CKF_USER_PIN_TO_BE_CHANGED
CKF_SO_PIN_COUNT_LOW
CKF_SO_PIN_FINAL_TRY
CKF_SO_PIN_LOCKED
CKF_SO_PIN_TO_BE_CHANGED
CKF_ERROR_STATE
#endif
#warning Much of the TOKEN_INFO we return is nonsense
pInfo->ulMaxSessionCount = CK_EFFECTIVELY_INFINITE;
pInfo->ulSessionCount = CK_UNAVAILABLE_INFORMATION;
pInfo->ulMaxRwSessionCount = CK_EFFECTIVELY_INFINITE;
pInfo->ulRwSessionCount = CK_UNAVAILABLE_INFORMATION;
pInfo->ulMaxPinLen = P11_MAX_PIN_LENGTH;
pInfo->ulMinPinLen = P11_MIN_PIN_LENGTH;
pInfo->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION;
pInfo->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION;
pInfo->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION;
pInfo->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION;
pInfo->hardwareVersion.major = P11_VERSION_HW_MAJOR;
pInfo->hardwareVersion.minor = P11_VERSION_HW_MINOR;
pInfo->firmwareVersion.major = P11_VERSION_SW_MAJOR;
pInfo->firmwareVersion.minor = P11_VERSION_SW_MINOR;
#warning Need to sort out hardware clock
#if 0
/*
* Eventually we expect cryptech devices to have their own hardware
* clocks. Not implemented yet.
*/
pInfo->utcTime;
#endif
return CKR_OK;
}
CK_RV C_OpenSession(CK_SLOT_ID slotID,
CK_FLAGS flags,
CK_VOID_PTR pApplication,
CK_NOTIFY Notify,
CK_SESSION_HANDLE_PTR phSession)
{
ENTER_PUBLIC_FUNCTION(C_OpenSession);
const int parallel_session = (flags & CKF_SERIAL_SESSION) == 0;
const int read_only_session = (flags & CKF_RW_SESSION) == 0;
p11_session_t *session = NULL;
CK_RV rv;
mutex_lock_or_return_failure(p11_global_mutex);
if (slotID != P11_ONE_AND_ONLY_SLOT)
lose(CKR_SLOT_ID_INVALID);
if (phSession == NULL)
lose(CKR_ARGUMENTS_BAD);
if (parallel_session)
lose(CKR_SESSION_PARALLEL_NOT_SUPPORTED);
if ((session = p11_session_new()) == NULL)
lose(CKR_HOST_MEMORY);
switch (logged_in_as) {
case not_logged_in:
session->state = read_only_session ? CKS_RO_PUBLIC_SESSION : CKS_RW_PUBLIC_SESSION;
break;
case logged_in_as_user:
session->state = read_only_session ? CKS_RO_USER_FUNCTIONS : CKS_RW_USER_FUNCTIONS;
break;
case logged_in_as_so:
if (read_only_session)
lose(CKR_SESSION_READ_WRITE_SO_EXISTS);
session->state = CKS_RW_SO_FUNCTIONS;
break;
}
session->notify = Notify;
session->application = pApplication;
if (!p11_session_add(session))
lose(CKR_FUNCTION_FAILED);
assert(p11_session_consistent_login());
if ((rv = mutex_unlock(p11_global_mutex)) != CKR_OK)
goto fail;
*phSession = session->handle;
return CKR_OK;
fail:
p11_session_free(session);
(void) mutex_unlock(p11_global_mutex);
return rv;
}
CK_RV C_CloseSession(CK_SESSION_HANDLE hSession)
{
ENTER_PUBLIC_FUNCTION(C_CloseSession);
CK_RV rv;
mutex_lock_or_return_failure(p11_global_mutex);
rv = p11_session_delete(hSession);
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_CloseAllSessions(CK_SLOT_ID slotID)
{
ENTER_PUBLIC_FUNCTION(C_CloseAllSessions);
if (slotID != P11_ONE_AND_ONLY_SLOT)
return CKR_SLOT_ID_INVALID;
mutex_lock_or_return_failure(p11_global_mutex);
p11_session_delete_all();
return mutex_unlock(p11_global_mutex);
}
CK_RV C_Login(CK_SESSION_HANDLE hSession,
CK_USER_TYPE userType,
CK_UTF8CHAR_PTR pPin,
CK_ULONG ulPinLen)
{
ENTER_PUBLIC_FUNCTION(C_Login);
static const char pin_query_format[] =
" SELECT pbkdf2_iterations, %s_pin, %s_pin_salt FROM global";
p11_session_t *session;
sqlite3_stmt *q = NULL;
const char *pin_type;
CK_RV rv = CKR_OK;
mutex_lock_or_return_failure(p11_global_mutex);
if (pPin == NULL)
lose(CKR_ARGUMENTS_BAD);
/*
* Mind, I don't really know why this function takes a session
* handle, given that the semantics don't seem to call upon us to do
* anything special for "this" session.
*/
if (p11_session_find(hSession) == NULL)
lose(CKR_SESSION_HANDLE_INVALID);
/*
* We don't currently support re-login without an intervening
* logout, so reject the login attempt if we're already logged in.
*/
if (logged_in_as != not_logged_in)
lose(CKR_USER_ALREADY_LOGGED_IN);
/*
* Figure out which PIN we're checking.
* We don't (yet?) support CKU_CONTEXT_SPECIFIC.
*
* Read-only SO is an illegal state, so reject the login attempt if
* we have any read-only sessions and we're trying to log in as SO.
*/
switch (userType) {
case CKU_USER:
pin_type = "user";
break;
case CKU_SO:
for (session = p11_sessions; session != NULL; session = session->link)
if (session->state == CKS_RO_PUBLIC_SESSION)
lose(CKR_SESSION_READ_ONLY_EXISTS);
pin_type = "so";
break;
case CKU_CONTEXT_SPECIFIC:
lose(CKR_OPERATION_NOT_INITIALIZED);
default:
lose(CKR_USER_TYPE_INVALID);
}
/*
* Look up the PIN and make sure it's set.
*
* Not obvious what error we should return if SO PIN isn't set, for
* now consider this state "locked" (because it hasn't been set yet).
*/
if (!sql_check_ok(sql_prepare(&q, pin_query_format, pin_type, pin_type)) ||
!sql_check_row(sqlite3_step(q)))
lose(CKR_FUNCTION_FAILED);
if (sqlite3_column_type(q, 1) == SQLITE_NULL ||
sqlite3_column_type(q, 2) == SQLITE_NULL) {
switch (userType) {
case CKU_USER:
lose(CKR_USER_PIN_NOT_INITIALIZED);
case CKU_SO:
lose(CKR_PIN_LOCKED);
default:
lose(CKR_USER_TYPE_INVALID);
}
}
/*
* Run PBKDF2 over the supplied PIN and compare results.
*
* Probably not really necessary to use constant-time string
* comparison, but it's harmless and cheap, so we might as well.
*/
{
const unsigned iterations = sqlite3_column_int(q, 0);
const uint8_t * const pin = sqlite3_column_blob(q, 1);
const uint8_t * const salt = sqlite3_column_blob(q, 2);
const size_t pin_len = sqlite3_column_bytes(q, 1);
const size_t salt_len = sqlite3_column_bytes(q, 2);
uint8_t pinbuf[pin_len];
unsigned diff = 0;
if (!hal_check(hal_pbkdf2(hal_hash_sha256, pPin, ulPinLen, salt, salt_len,
pinbuf, sizeof(pinbuf), iterations)))
lose(CKR_FUNCTION_FAILED);
for (int i = 0; i < pin_len; i++)
diff |= pin[i] ^ pinbuf[i];
if (diff != 0)
lose(CKR_PIN_INCORRECT);
}
/*
* If we get here, the PIN was OK. Update global login state, then
* whack every session into the correct new state.
*/
assert(p11_session_consistent_login());
logged_in_as = userType == CKU_SO ? logged_in_as_so : logged_in_as_user;
for (session = p11_sessions; session != NULL; session = session->link) {
switch (session->state) {
case CKS_RO_PUBLIC_SESSION:
assert(userType == CKU_USER);
session->state = CKS_RO_USER_FUNCTIONS;
continue;
case CKS_RW_PUBLIC_SESSION:
session->state = userType == CKU_SO ? CKS_RW_SO_FUNCTIONS : CKS_RW_USER_FUNCTIONS;
continue;
}
}
assert(p11_session_consistent_login());
fail:
sqlite3_finalize(q);
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_Logout(CK_SESSION_HANDLE hSession)
{
ENTER_PUBLIC_FUNCTION(C_Logout);
p11_session_t *session;
CK_RV rv = CKR_OK;
mutex_lock_or_return_failure(p11_global_mutex);
/*
* Mind, I don't really know why this function takes a session
* handle, given that the semantics don't seem to call upon us to do
* anything special for "this" session.
*/
if (p11_session_find(hSession) == NULL)
lose(CKR_SESSION_HANDLE_INVALID);
if (logged_in_as == not_logged_in)
lose(CKR_USER_NOT_LOGGED_IN);
/*
* Update global login state, then delete any private objects and
* whack every existing session into the right state.
*/
assert(p11_session_consistent_login());
logged_in_as = not_logged_in;
p11_object_delete_all_private();
for (session = p11_sessions; session != NULL; session = session->link) {
switch (session->state) {
case CKS_RO_USER_FUNCTIONS:
session->state = CKS_RO_PUBLIC_SESSION;
continue;
case CKS_RW_USER_FUNCTIONS:
case CKS_RW_SO_FUNCTIONS:
session->state = CKS_RW_PUBLIC_SESSION;
continue;
}
}
assert(p11_session_consistent_login());
fail:
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession,
CK_OBJECT_HANDLE hObject)
{
ENTER_PUBLIC_FUNCTION(C_DestroyObject);
static const char delete_object[] =
" DELETE FROM object WHERE object_handle = ?";
static const char delete_token_object[] =
" DELETE FROM token_object"
" WHERE token_object_id = (SELECT token_object_id FROM object WHERE object_handle = ?)";
p11_session_t *session;
sqlite3_stmt *q = NULL;
CK_RV rv = CKR_OK;
mutex_lock_or_return_failure(p11_global_mutex);
session = p11_session_find(hSession);
if ((rv = p11_object_check_rights(session, hObject, p11_object_access_write)) != CKR_OK)
goto fail;
if (is_token_handle(hObject) &&
(!sql_check_ok(sql_prepare(&q, delete_token_object)) ||
!sql_check_ok(sqlite3_bind_int64(q, 1, hObject)) ||
!sql_check_done(sqlite3_step(q)) ||
!sql_check_ok(sql_finalize_and_clear(&q))))
lose(CKR_FUNCTION_FAILED);
if (!sql_check_ok(sql_prepare(&q, delete_object)) ||
!sql_check_ok(sqlite3_bind_int64(q, 1, hObject)) ||
!sql_check_done(sqlite3_step(q)))
lose(CKR_FUNCTION_FAILED);
fail:
sqlite3_finalize(q);
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession,
CK_OBJECT_HANDLE hObject,
CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulCount)
{
ENTER_PUBLIC_FUNCTION(C_GetAttributeValue);
static const char select_format[] =
" SELECT value FROM %s_attribute NATURAL JOIN object"
" WHERE object_handle = ?1 AND type = ?2";
const char *flavor = is_token_handle(hObject) ? "token" : "session";
p11_session_t *session;
const p11_descriptor_t *descriptor = NULL;
CK_BBOOL cka_sensitive, cka_extractable;
CK_OBJECT_CLASS cka_class;
CK_KEY_TYPE cka_key_type;
int sensitive_object = 0;
sqlite3_stmt *q = NULL;
CK_RV rv;
int ret, i;
mutex_lock_or_return_failure(p11_global_mutex);
if (pTemplate == NULL)
lose(CKR_ARGUMENTS_BAD);
session = p11_session_find(hSession);
if ((rv = p11_object_check_rights(session, hObject, p11_object_access_read)) != CKR_OK)
goto fail;
if (!p11_attribute_get_ulong(hObject, CKA_CLASS, &cka_class))
lose(CKR_OBJECT_HANDLE_INVALID);
switch (cka_class) {
case CKO_PRIVATE_KEY:
case CKO_SECRET_KEY:
if (!p11_attribute_get_bbool(hObject, CKA_EXTRACTABLE, &cka_extractable) ||
!p11_attribute_get_bbool(hObject, CKA_SENSITIVE, &cka_sensitive))
lose(CKR_OBJECT_HANDLE_INVALID);
sensitive_object = cka_sensitive || !cka_extractable;
/* Fall through */
case CKO_PUBLIC_KEY:
if (!p11_attribute_get_ulong(hObject, CKA_KEY_TYPE, &cka_key_type))
lose(CKR_OBJECT_HANDLE_INVALID);
descriptor = p11_descriptor_from_key_type(cka_class, cka_key_type);
}
if (!sql_check_ok(sql_prepare(&q, select_format, flavor)) ||
!sql_check_ok(sqlite3_bind_int64(q, 1, hObject)))
lose(CKR_FUNCTION_FAILED);
rv = CKR_OK;
for (i = 0; i < ulCount; i++) {
if (sensitive_object && p11_attribute_is_sensitive(descriptor, pTemplate[i].type)) {
pTemplate[i].ulValueLen = -1;
rv = CKR_ATTRIBUTE_SENSITIVE;
}
else if (!sql_check_ok(sqlite3_reset(q)) ||
!sql_check_ok(sqlite3_bind_int64(q, 2, pTemplate[i].type)) ||
(ret = sqlite3_step(q)) != SQLITE_ROW) {
if (ret != SQLITE_DONE)
sql_whine_step();
pTemplate[i].ulValueLen = -1;
rv = CKR_ATTRIBUTE_TYPE_INVALID;
}
else if (pTemplate[i].pValue == NULL) {
pTemplate[i].ulValueLen = sqlite3_column_bytes(q, 0);
}
else if (pTemplate[i].ulValueLen >= sqlite3_column_bytes(q, 0)) {
pTemplate[i].ulValueLen = sqlite3_column_bytes(q, 0);
memcpy(pTemplate[i].pValue, sqlite3_column_blob(q, 0), pTemplate[i].ulValueLen);
}
else {
pTemplate[i].ulValueLen = -1;
rv = CKR_BUFFER_TOO_SMALL;
}
}
fail:
sqlite3_finalize(q);
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_FindObjectsInit(CK_SESSION_HANDLE hSession,
CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulCount)
{
ENTER_PUBLIC_FUNCTION(C_FindObjectsInit);
static const char select_missing[] =
" WITH"
" known AS (SELECT token_object_id FROM object WHERE token_object_id IS NOT NULL)"
" SELECT token_object_id FROM token_object WHERE token_object_id NOT IN known";
static const char insert_missing[] =
" INSERT INTO object (object_handle, token_object_id) VALUES (?1, ?2)";
static const char create_format[] =
" CREATE TEMPORARY TABLE findobjects_%lu AS"
" SELECT object_id FROM object NATURAL LEFT JOIN session"
" WHERE session_handle IS NULL OR session_handle = ?1";
static const char drop_format[] =
" DROP TABLE IF EXISTS findobjects_%lu";
static const char delete_format[] =
" WITH"
" matches AS (SELECT object_id"
" FROM object NATURAL JOIN session_attribute"
" WHERE type = ?1 AND value = ?2"
" UNION"
" SELECT object_id"
" FROM object NATURAL JOIN token_attribute"
" WHERE type = ?1 AND value = ?2)"
" DELETE FROM findobjects_%lu WHERE object_id NOT IN matches";
static const char select_format[] =
" SELECT object_handle FROM findobjects_%lu NATURAL JOIN object ORDER BY object_id";
p11_session_t *session;
sqlite3_stmt *q1 = NULL, *q2 = NULL;
CK_RV rv = CKR_OK;
int i, ret;
mutex_lock_or_return_failure(p11_global_mutex);
if ((session = p11_session_find(hSession)) == NULL)
lose(CKR_SESSION_HANDLE_INVALID);
if (ulCount > 0 && pTemplate == NULL)
lose(CKR_ARGUMENTS_BAD);
if (session->find_query != NULL)
lose(CKR_OPERATION_ACTIVE);
/*
* Assign handles to any token objects that don't have them yet.
*/
if (!sql_check_ok(sql_prepare(&q1, select_missing)) ||
!sql_check_ok(sql_prepare(&q2, insert_missing)))
lose(CKR_FUNCTION_FAILED);
while ((ret = sqlite3_step(q1)) == SQLITE_ROW) {
sqlite3_int64 token_object_id = sqlite3_column_int64(q1, 0);
CK_OBJECT_HANDLE object_handle = p11_allocate_unused_handle(handle_flavor_token_object);
if (!sql_check_ok(sqlite3_reset(q2)) ||
!sql_check_ok(sqlite3_bind_int64(q2, 1, object_handle)) ||
!sql_check_ok(sqlite3_bind_int64(q2, 2, token_object_id)) ||
!sql_check_done(sqlite3_step(q2)))
lose(CKR_FUNCTION_FAILED);
}
if (ret != SQLITE_DONE) {
sql_whine_step();
lose(CKR_FUNCTION_FAILED);
}
/*
* Create a temporary table to hold this session's FindObjects
* state. Populate this with every object this session knows about,
* then prune based on login status and whatever filter attributes
* the caller supplied.
*/
if (!sql_check_ok(sql_finalize_and_clear(&q1)) ||
!sql_check_ok(sql_finalize_and_clear(&q2)) ||
!sql_check_ok(sql_prepare(&q1, drop_format, hSession)) ||
!sql_check_done(sqlite3_step(q1)) ||
!sql_check_ok(sql_prepare(&q2, create_format, hSession)) ||
!sql_check_ok(sqlite3_bind_int64(q2, 1, hSession)) ||
!sql_check_done(sqlite3_step(q2)) ||
!sql_check_ok(sql_finalize_and_clear(&q1)) ||
!sql_check_ok(sql_finalize_and_clear(&q2)) ||
!sql_check_ok(sql_prepare(&q1, delete_format, hSession)))
lose(CKR_FUNCTION_FAILED);
/*
* If we're not logged in as the regular user, run an extra filter
* cycle to remove all private objects before we get to the
* caller-supplied template.
*/
if (logged_in_as != logged_in_as_user) {
if (!sql_check_ok(sqlite3_bind_int64(q1, 1, CKA_PRIVATE)) ||
!sql_check_ok(sqlite3_bind_blob( q1, 2,
&const_CK_FALSE,
sizeof(const_CK_FALSE),
NULL)) ||
!sql_check_done(sqlite3_step(q1)))
lose(CKR_FUNCTION_FAILED);
}
/*
* Filter through the caller-supplied template.
*
* NB: This doesn't support some of the more obscure searches, such
* as searches for sessions or hardware features. Too much rope
* already, worry about those if we ever really need them.
*/
for (i = 0; i < ulCount; i++)
if (!sql_check_ok(sqlite3_reset(q1)) ||
!sql_check_ok(sqlite3_bind_int64(q1, 1, pTemplate[i].type)) ||
!sql_check_ok(sqlite3_bind_blob( q1, 2, pTemplate[i].pValue,
pTemplate[i].ulValueLen, NULL)) ||
!sql_check_done(sqlite3_step(q1)))
lose(CKR_FUNCTION_FAILED);
/*
* Stash a prepared query in the session object which will return
* whatever object handles survived all that filtering.
*/
if (!sql_check_ok(sql_prepare(&session->find_query, select_format, hSession)))
lose(CKR_FUNCTION_FAILED);
session->find_query_done = 0;
fail:
sqlite3_finalize(q1);
sqlite3_finalize(q2);
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_FindObjects(CK_SESSION_HANDLE hSession,
CK_OBJECT_HANDLE_PTR phObject,
CK_ULONG ulMaxObjectCount,
CK_ULONG_PTR pulObjectCount)
{
ENTER_PUBLIC_FUNCTION(C_FindObjects);
p11_session_t *session;
int i, ret = SQLITE_OK;
CK_RV rv = CKR_OK;
mutex_lock_or_return_failure(p11_global_mutex);
if ((session = p11_session_find(hSession)) == NULL)
lose(CKR_SESSION_HANDLE_INVALID);
if (session->find_query == NULL)
lose(CKR_OPERATION_NOT_INITIALIZED);
if (phObject == NULL || pulObjectCount == NULL)
lose(CKR_ARGUMENTS_BAD);
/*
* C_FindObjectsInit() did all the heavy lifting, we just have to
* return the resulting handles.
*/
i = 0;
if (!session->find_query_done)
while (i < ulMaxObjectCount && (ret = sqlite3_step(session->find_query)) == SQLITE_ROW)
phObject[i++] = (CK_OBJECT_HANDLE) sqlite3_column_int64(session->find_query, 0);
switch (ret) {
case SQLITE_DONE:
session->find_query_done = 1;
break;
case SQLITE_OK:
case SQLITE_ROW:
break;
default:
sql_whine_step();
lose(CKR_FUNCTION_FAILED);
}
*pulObjectCount = i;
fail:
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE hSession)
{
ENTER_PUBLIC_FUNCTION(C_FindObjectsFinal);
static const char drop_format[] =
" DROP TABLE IF EXISTS findobjects_%lu";
p11_session_t *session;
sqlite3_stmt *q = NULL;
CK_RV rv = CKR_OK;
mutex_lock_or_return_failure(p11_global_mutex);
if ((session = p11_session_find(hSession)) == NULL)
lose(CKR_SESSION_HANDLE_INVALID);
if (session->find_query == NULL)
lose(CKR_OPERATION_NOT_INITIALIZED);
/*
* Clean up result query and temporary table.
*/
if (!sql_check_ok(sql_finalize_and_clear(&session->find_query)) ||
!sql_check_ok(sql_prepare(&q, drop_format, hSession)) ||
!sql_check_done(sqlite3_step(q)))
lose(CKR_FUNCTION_FAILED);
fail:
sqlite3_finalize(q);
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_DigestInit(CK_SESSION_HANDLE hSession,
CK_MECHANISM_PTR pMechanism)
{
ENTER_PUBLIC_FUNCTION(C_DigestInit);
p11_session_t *session;
CK_RV rv = CKR_OK;
mutex_lock_or_return_failure(p11_global_mutex);
if ((session = p11_session_find(hSession)) == NULL)
lose(CKR_SESSION_HANDLE_INVALID);
if (pMechanism == NULL)
lose(CKR_ARGUMENTS_BAD);
if (session->digest_descriptor != NULL)
lose(CKR_OPERATION_ACTIVE);
switch (pMechanism->mechanism) {
case CKM_SHA_1: session->digest_descriptor = hal_hash_sha1; break;
case CKM_SHA256: session->digest_descriptor = hal_hash_sha256; break;
case CKM_SHA384: session->digest_descriptor = hal_hash_sha384; break;
case CKM_SHA512: session->digest_descriptor = hal_hash_sha512; break;
default: lose(CKR_MECHANISM_INVALID);
}
if (!hal_check(hal_hash_core_present(session->digest_descriptor))) {
session->digest_descriptor = NULL;
lose(CKR_MECHANISM_INVALID);
}
return mutex_unlock(p11_global_mutex);
fail:
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_Digest(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pData,
CK_ULONG ulDataLen,
CK_BYTE_PTR pDigest,
CK_ULONG_PTR pulDigestLen)
{
ENTER_PUBLIC_FUNCTION(C_Digest);
p11_session_t *session;
CK_RV rv = CKR_OK;
mutex_lock_or_return_failure(p11_global_mutex);
if ((session = p11_session_find(hSession)) == NULL)
lose(CKR_SESSION_HANDLE_INVALID);
if (pData == NULL || pulDigestLen == NULL)
lose(CKR_ARGUMENTS_BAD);
if (session->digest_descriptor == NULL)
lose(CKR_OPERATION_NOT_INITIALIZED);
rv = *pulDigestLen < session->digest_descriptor->digest_length ? CKR_BUFFER_TOO_SMALL : CKR_OK;
*pulDigestLen = session->digest_descriptor->digest_length;
if (pDigest == NULL)
return mutex_unlock(p11_global_mutex);
if (rv == CKR_BUFFER_TOO_SMALL)
lose(CKR_BUFFER_TOO_SMALL);
{
uint8_t statebuf[session->digest_descriptor->hash_state_length];
hal_hash_state_t state = { NULL };
if (!hal_check(hal_hash_initialize(session->digest_descriptor,
&state, statebuf, sizeof(statebuf))) ||
!hal_check(hal_hash_update(state, pData, ulDataLen)) ||
!hal_check(hal_hash_finalize(state, pDigest, *pulDigestLen)))
lose(CKR_FUNCTION_FAILED);
}
rv = CKR_OK; /* Fall through */
fail:
if (session != NULL)
session->digest_descriptor = NULL;
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_SignInit(CK_SESSION_HANDLE hSession,
CK_MECHANISM_PTR pMechanism,
CK_OBJECT_HANDLE hKey)
{
ENTER_PUBLIC_FUNCTION(C_SignInit);
p11_session_t *session;
CK_RV rv = CKR_OK;
mutex_lock_or_return_failure(p11_global_mutex);
if ((session = p11_session_find(hSession)) == NULL)
lose(CKR_SESSION_HANDLE_INVALID);
if (pMechanism == NULL)
lose(CKR_ARGUMENTS_BAD);
if (session->sign_key_handle != CK_INVALID_HANDLE || session->sign_digest_descriptor != NULL)
lose(CKR_OPERATION_ACTIVE);
if ((rv = p11_object_check_rights(session, hKey, p11_object_access_read)) != CKR_OK)
goto fail;
/*
* Will need to check key algorithm type here once we add support
* for signature algorithms other than RSA.
*/
session->sign_key_handle = hKey;
switch (pMechanism->mechanism) {
case CKM_RSA_PKCS: session->sign_digest_descriptor = NULL; break;
case CKM_SHA1_RSA_PKCS: session->sign_digest_descriptor = hal_hash_sha1; break;
case CKM_SHA256_RSA_PKCS: session->sign_digest_descriptor = hal_hash_sha256; break;
case CKM_SHA384_RSA_PKCS: session->sign_digest_descriptor = hal_hash_sha384; break;
case CKM_SHA512_RSA_PKCS: session->sign_digest_descriptor = hal_hash_sha512; break;
default: return CKR_MECHANISM_INVALID;
}
return mutex_unlock(p11_global_mutex);
fail:
if (session != NULL) {
session->sign_key_handle = CK_INVALID_HANDLE;
session->sign_digest_descriptor = NULL;
}
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_Sign(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pData,
CK_ULONG ulDataLen,
CK_BYTE_PTR pSignature,
CK_ULONG_PTR pulSignatureLen)
{
ENTER_PUBLIC_FUNCTION(C_Sign);
uint8_t keybuf[hal_rsa_key_t_size];
hal_rsa_key_t key = { NULL };
p11_session_t *session;
size_t signature_len;
CK_RV rv;
mutex_lock_or_return_failure(p11_global_mutex);
if ((session = p11_session_find(hSession)) == NULL)
lose(CKR_SESSION_HANDLE_INVALID);
if (pData == NULL || pulSignatureLen == NULL)
lose(CKR_ARGUMENTS_BAD);
if (session->sign_key_handle == CK_INVALID_HANDLE)
lose(CKR_OPERATION_NOT_INITIALIZED);
/*
* From here down this function is RSA-specific, and will need
* rewriting when we add support for other algorithms.
*/
if (!p11_object_get_rsa_private_key(session->sign_key_handle,
&key, keybuf, sizeof(keybuf)))
lose(CKR_FUNCTION_FAILED);
/*
* Retrieve signature length. For RSA this is just the modulus
* length, other algorithms will need a more generic solution.
*/
if (!hal_check(hal_rsa_key_get_modulus(key, NULL, &signature_len, 0)))
lose(CKR_FUNCTION_FAILED);
rv = signature_len > *pulSignatureLen ? CKR_BUFFER_TOO_SMALL : CKR_OK;
*pulSignatureLen = signature_len;
if (pSignature == NULL) {
hal_rsa_key_clear(key);
return mutex_unlock(p11_global_mutex);
}
if (rv == CKR_BUFFER_TOO_SMALL)
lose(CKR_BUFFER_TOO_SMALL);
if (session->sign_digest_descriptor != NULL) {
uint8_t digest_info[session->sign_digest_descriptor->digest_length + 4 +
session->sign_digest_descriptor->digest_algorithm_id_length];
if (!pkcs1_construct_digestinfo(session->sign_digest_descriptor,
pData, ulDataLen, digest_info, sizeof(digest_info)))
lose(CKR_FUNCTION_FAILED);
rv = sign_rsa_pkcs(key, digest_info, sizeof(digest_info), pSignature, signature_len);
memset(digest_info, 0, sizeof(digest_info));
if (rv != CKR_OK)
goto fail;
}
else {
if ((rv = sign_rsa_pkcs(key, pData, ulDataLen, pSignature, signature_len)) != CKR_OK)
goto fail;
}
rv = CKR_OK; /* Fall through */
fail:
if (session != NULL) {
session->sign_key_handle = CK_INVALID_HANDLE;
session->sign_digest_descriptor = NULL;
}
hal_rsa_key_clear(key);
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_VerifyInit(CK_SESSION_HANDLE hSession,
CK_MECHANISM_PTR pMechanism,
CK_OBJECT_HANDLE hKey )
{
ENTER_PUBLIC_FUNCTION(C_VerifyInit);
p11_session_t *session;
CK_RV rv = CKR_OK;
mutex_lock_or_return_failure(p11_global_mutex);
if ((session = p11_session_find(hSession)) == NULL)
lose(CKR_SESSION_HANDLE_INVALID);
if (pMechanism == NULL)
lose(CKR_ARGUMENTS_BAD);
if (session->verify_key_handle != CK_INVALID_HANDLE || session->verify_digest_descriptor != NULL)
lose(CKR_OPERATION_ACTIVE);
if ((rv = p11_object_check_rights(session, hKey, p11_object_access_read)) != CKR_OK)
goto fail;
/*
* Will need to check key algorithm type here once we add support
* for signature algorithms other than RSA.
*/
session->verify_key_handle = hKey;
switch (pMechanism->mechanism) {
case CKM_RSA_PKCS: session->verify_digest_descriptor = NULL; break;
case CKM_SHA1_RSA_PKCS: session->verify_digest_descriptor = hal_hash_sha1; break;
case CKM_SHA256_RSA_PKCS: session->verify_digest_descriptor = hal_hash_sha256; break;
case CKM_SHA384_RSA_PKCS: session->verify_digest_descriptor = hal_hash_sha384; break;
case CKM_SHA512_RSA_PKCS: session->verify_digest_descriptor = hal_hash_sha512; break;
default: return CKR_MECHANISM_INVALID;
}
return mutex_unlock(p11_global_mutex);
fail:
if (session != NULL) {
session->verify_key_handle = CK_INVALID_HANDLE;
session->verify_digest_descriptor = NULL;
}
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_Verify(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pData,
CK_ULONG ulDataLen,
CK_BYTE_PTR pSignature,
CK_ULONG ulSignatureLen)
{
ENTER_PUBLIC_FUNCTION(C_Verify);
uint8_t keybuf[hal_rsa_key_t_size];
hal_rsa_key_t key = { NULL };
p11_session_t *session;
CK_RV rv;
mutex_lock_or_return_failure(p11_global_mutex);
if ((session = p11_session_find(hSession)) == NULL)
lose(CKR_SESSION_HANDLE_INVALID);
if (pData == NULL)
lose(CKR_ARGUMENTS_BAD);
if (session->verify_key_handle == CK_INVALID_HANDLE)
lose(CKR_OPERATION_NOT_INITIALIZED);
/*
* From here down this function is RSA-specific, and will need
* rewriting when we add support for other algorithms.
*/
if (!p11_object_get_rsa_public_key(session->verify_key_handle,
&key, keybuf, sizeof(keybuf)))
lose(CKR_FUNCTION_FAILED);
if (session->verify_digest_descriptor != NULL) {
uint8_t digest_info[session->verify_digest_descriptor->digest_length + 4 +
session->verify_digest_descriptor->digest_algorithm_id_length];
if (!pkcs1_construct_digestinfo(session->verify_digest_descriptor,
pData, ulDataLen, digest_info, sizeof(digest_info)))
lose(CKR_FUNCTION_FAILED);
rv = verify_rsa_pkcs(key, digest_info, sizeof(digest_info), pSignature, ulSignatureLen);
memset(digest_info, 0, sizeof(digest_info));
if (rv != CKR_OK)
goto fail;
}
else {
if ((rv = verify_rsa_pkcs(key, pData, ulDataLen, pSignature, ulSignatureLen)) != CKR_OK)
goto fail;
}
rv = CKR_OK; /* Fall through */
fail:
if (session != NULL) {
session->verify_key_handle = CK_INVALID_HANDLE;
session->verify_digest_descriptor = NULL;
}
hal_rsa_key_clear(key);
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
/*
* If there's any method in this entire package which really needs a
* more complex mutex structure than the single global mutex, it's
* probably this one. Key generation can take a looooong time.
* Drive off that bridge when we get to it.
*/
CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession,
CK_MECHANISM_PTR pMechanism,
CK_ATTRIBUTE_PTR pPublicKeyTemplate,
CK_ULONG ulPublicKeyAttributeCount,
CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
CK_ULONG ulPrivateKeyAttributeCount,
CK_OBJECT_HANDLE_PTR phPublicKey,
CK_OBJECT_HANDLE_PTR phPrivateKey)
{
ENTER_PUBLIC_FUNCTION(C_GenerateKeyPair);
p11_session_t *session;
CK_RV rv;
mutex_lock_or_return_failure(p11_global_mutex);
if ((session = p11_session_find(hSession)) == NULL)
lose(CKR_SESSION_HANDLE_INVALID);
if (pMechanism == NULL ||
pPublicKeyTemplate == NULL || phPublicKey == NULL ||
pPrivateKeyTemplate == NULL || phPrivateKey == NULL)
lose(CKR_ARGUMENTS_BAD);
switch (pMechanism->mechanism) {
case CKM_RSA_PKCS_KEY_PAIR_GEN:
rv = generate_keypair_rsa_pkcs(session, pMechanism,
pPublicKeyTemplate, ulPublicKeyAttributeCount,
pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
phPublicKey, phPrivateKey);
break;
default:
lose(CKR_MECHANISM_INVALID);
}
fail:
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_GenerateRandom(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR RandomData,
CK_ULONG ulRandomLen)
{
ENTER_PUBLIC_FUNCTION(C_GenerateRandom);
p11_session_t *session;
CK_RV rv = CKR_OK;
mutex_lock_or_return_failure(p11_global_mutex);
if ((session = p11_session_find(hSession)) == NULL)
lose(CKR_SESSION_HANDLE_INVALID);
if (RandomData == NULL)
lose(CKR_ARGUMENTS_BAD);
if (!hal_check(hal_get_random(RandomData, ulRandomLen)))
lose(CKR_FUNCTION_FAILED);
fail:
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
/*
* Supply information about a particular mechanism. We may want a
* more generic structure for this, for the moment, just answer the
* questions hsmbully is asking.
*
* Not really sure whether I should be setting CKF_HW here or not, RSA
* is a mix of hardware and software at the moment, but I'm also a
* little unclear on what "the device" means in this context, so let's
* just say that if it's implemented by libhal or the Verilog hiding
* behind libhal, it's implemented in hardware.
*/
CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID,
CK_MECHANISM_TYPE type,
CK_MECHANISM_INFO_PTR pInfo)
{
ENTER_PUBLIC_FUNCTION(C_GetMechanismInfo);
/*
* No locking here, no obvious need for it.
*/
if (pInfo == NULL)
return CKR_ARGUMENTS_BAD;
if (slotID != P11_ONE_AND_ONLY_SLOT)
return CKR_SLOT_ID_INVALID;
switch (type) {
case CKM_RSA_PKCS_KEY_PAIR_GEN:
pInfo->ulMinKeySize = 1024;
pInfo->ulMaxKeySize = 8192;
pInfo->flags = CKF_HW | CKF_GENERATE_KEY_PAIR;
break;
case CKM_RSA_PKCS:
pInfo->ulMinKeySize = 1024;
pInfo->ulMaxKeySize = 8192;
pInfo->flags = CKF_HW | CKF_SIGN;
break;
default:
return CKR_MECHANISM_INVALID;
}
return CKR_OK;
}
/*
* Stubs for unsupported functions below here. Per the PKCS #11
* specification, it's OK to skip implementing almost any function in
* the API, but if one does so, one must provide a stub which returns
* CKR_FUNCTION_NOT_SUPPORTED, because every slot in the dispatch
* vector must be populated. We could reuse a single stub for all the
* unimplemented slots, but the type signatures wouldn't match, which
* would require some nasty casts I'd rather avoid.
*
* Many of these functions would be straightforward to implement, but
* there are enough bald yaks in this saga already.
*/
CK_RV C_GenerateKey(CK_SESSION_HANDLE hSession,
CK_MECHANISM_PTR pMechanism,
CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulCount,
CK_OBJECT_HANDLE_PTR phKey)
{
ENTER_PUBLIC_FUNCTION(C_GenerateKey);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_GetInfo(CK_INFO_PTR pInfo)
{
ENTER_PUBLIC_FUNCTION(C_GetInfo);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_GetSlotInfo(CK_SLOT_ID slotID,
CK_SLOT_INFO_PTR pInfo)
{
ENTER_PUBLIC_FUNCTION(C_GetSlotInfo);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_GetMechanismList(CK_SLOT_ID slotID,
CK_MECHANISM_TYPE_PTR pMechanismList,
CK_ULONG_PTR pulCount)
{
ENTER_PUBLIC_FUNCTION(C_GetMechanismList);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_InitToken(CK_SLOT_ID slotID,
CK_UTF8CHAR_PTR pPin,
CK_ULONG ulPinLen,
CK_UTF8CHAR_PTR pLabel)
{
ENTER_PUBLIC_FUNCTION(C_InitToken);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_InitPIN(CK_SESSION_HANDLE hSession,
CK_UTF8CHAR_PTR pPin,
CK_ULONG ulPinLen)
{
ENTER_PUBLIC_FUNCTION(C_InitPIN);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_SetPIN(CK_SESSION_HANDLE hSession,
CK_UTF8CHAR_PTR pOldPin,
CK_ULONG ulOldLen,
CK_UTF8CHAR_PTR pNewPin,
CK_ULONG ulNewLen)
{
ENTER_PUBLIC_FUNCTION(C_SetPIN);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession,
CK_SESSION_INFO_PTR pInfo)
{
ENTER_PUBLIC_FUNCTION(C_GetSessionInfo);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_GetOperationState(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pOperationState,
CK_ULONG_PTR pulOperationStateLen)
{
ENTER_PUBLIC_FUNCTION(C_GetOperationState);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_SetOperationState(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pOperationState,
CK_ULONG ulOperationStateLen,
CK_OBJECT_HANDLE hEncryptionKey,
CK_OBJECT_HANDLE hAuthenticationKey)
{
ENTER_PUBLIC_FUNCTION(C_SetOperationState);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_CreateObject(CK_SESSION_HANDLE hSession,
CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulCount,
CK_OBJECT_HANDLE_PTR phObject)
{
ENTER_PUBLIC_FUNCTION(C_CreateObject);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_CopyObject(CK_SESSION_HANDLE hSession,
CK_OBJECT_HANDLE hObject,
CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulCount,
CK_OBJECT_HANDLE_PTR phNewObject)
{
ENTER_PUBLIC_FUNCTION(C_CopyObject);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_GetObjectSize(CK_SESSION_HANDLE hSession,
CK_OBJECT_HANDLE hObject,
CK_ULONG_PTR pulSize)
{
ENTER_PUBLIC_FUNCTION(C_GetObjectSize);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_SetAttributeValue(CK_SESSION_HANDLE hSession,
CK_OBJECT_HANDLE hObject,
CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulCount)
{
ENTER_PUBLIC_FUNCTION(C_SetAttributeValue);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_EncryptInit(CK_SESSION_HANDLE hSession,
CK_MECHANISM_PTR pMechanism,
CK_OBJECT_HANDLE hKey)
{
ENTER_PUBLIC_FUNCTION(C_EncryptInit);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_Encrypt(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pData,
CK_ULONG ulDataLen,
CK_BYTE_PTR pEncryptedData,
CK_ULONG_PTR pulEncryptedDataLen)
{
ENTER_PUBLIC_FUNCTION(C_Encrypt);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_EncryptUpdate(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pPart,
CK_ULONG ulPartLen,
CK_BYTE_PTR pEncryptedPart,
CK_ULONG_PTR pulEncryptedPartLen)
{
ENTER_PUBLIC_FUNCTION(C_EncryptUpdate);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_EncryptFinal(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pLastEncryptedPart,
CK_ULONG_PTR pulLastEncryptedPartLen)
{
ENTER_PUBLIC_FUNCTION(C_EncryptFinal);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_DecryptInit(CK_SESSION_HANDLE hSession,
CK_MECHANISM_PTR pMechanism,
CK_OBJECT_HANDLE hKey)
{
ENTER_PUBLIC_FUNCTION(C_DecryptInit);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_Decrypt(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pEncryptedData,
CK_ULONG ulEncryptedDataLen,
CK_BYTE_PTR pData,
CK_ULONG_PTR pulDataLen)
{
ENTER_PUBLIC_FUNCTION(C_Decrypt);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_DecryptUpdate(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pEncryptedPart,
CK_ULONG ulEncryptedPartLen,
CK_BYTE_PTR pPart,
CK_ULONG_PTR pulPartLen)
{
ENTER_PUBLIC_FUNCTION(C_DecryptUpdate);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_DecryptFinal(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pLastPart,
CK_ULONG_PTR pulLastPartLen)
{
ENTER_PUBLIC_FUNCTION(C_DecryptFinal);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_DigestUpdate(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pPart,
CK_ULONG ulPartLen)
{
ENTER_PUBLIC_FUNCTION(C_DigestUpdate);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_DigestKey(CK_SESSION_HANDLE hSession,
CK_OBJECT_HANDLE hKey)
{
ENTER_PUBLIC_FUNCTION(C_DigestKey);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_DigestFinal(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pDigest,
CK_ULONG_PTR pulDigestLen)
{
ENTER_PUBLIC_FUNCTION(C_DigestFinal);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_SignUpdate(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pPart,
CK_ULONG ulPartLen)
{
ENTER_PUBLIC_FUNCTION(C_SignUpdate);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_SignFinal(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pSignature,
CK_ULONG_PTR pulSignatureLen)
{
ENTER_PUBLIC_FUNCTION(C_SignFinal);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_SignRecoverInit(CK_SESSION_HANDLE hSession,
CK_MECHANISM_PTR pMechanism,
CK_OBJECT_HANDLE hKey)
{
ENTER_PUBLIC_FUNCTION(C_SignRecoverInit);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_SignRecover(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pData,
CK_ULONG ulDataLen,
CK_BYTE_PTR pSignature,
CK_ULONG_PTR pulSignatureLen)
{
ENTER_PUBLIC_FUNCTION(C_SignRecover);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_VerifyUpdate(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pPart,
CK_ULONG ulPartLen)
{
ENTER_PUBLIC_FUNCTION(C_VerifyUpdate);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_VerifyFinal(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pSignature,
CK_ULONG ulSignatureLen)
{
ENTER_PUBLIC_FUNCTION(C_VerifyFinal);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE hSession,
CK_MECHANISM_PTR pMechanism,
CK_OBJECT_HANDLE hKey)
{
ENTER_PUBLIC_FUNCTION(C_VerifyRecoverInit);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_VerifyRecover(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pSignature,
CK_ULONG ulSignatureLen,
CK_BYTE_PTR pData,
CK_ULONG_PTR pulDataLen)
{
ENTER_PUBLIC_FUNCTION(C_VerifyRecover);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_DigestEncryptUpdate(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pPart,
CK_ULONG ulPartLen,
CK_BYTE_PTR pEncryptedPart,
CK_ULONG_PTR pulEncryptedPartLen)
{
ENTER_PUBLIC_FUNCTION(C_DigestEncryptUpdate);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_DecryptDigestUpdate(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pEncryptedPart,
CK_ULONG ulEncryptedPartLen,
CK_BYTE_PTR pPart,
CK_ULONG_PTR pulPartLen)
{
ENTER_PUBLIC_FUNCTION(C_DecryptDigestUpdate);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_SignEncryptUpdate(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pPart,
CK_ULONG ulPartLen,
CK_BYTE_PTR pEncryptedPart,
CK_ULONG_PTR pulEncryptedPartLen)
{
ENTER_PUBLIC_FUNCTION(C_SignEncryptUpdate);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pEncryptedPart,
CK_ULONG ulEncryptedPartLen,
CK_BYTE_PTR pPart,
CK_ULONG_PTR pulPartLen)
{
ENTER_PUBLIC_FUNCTION(C_DecryptVerifyUpdate);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_WrapKey(CK_SESSION_HANDLE hSession,
CK_MECHANISM_PTR pMechanism,
CK_OBJECT_HANDLE hWrappingKey,
CK_OBJECT_HANDLE hKey,
CK_BYTE_PTR pWrappedKey,
CK_ULONG_PTR pulWrappedKeyLen)
{
ENTER_PUBLIC_FUNCTION(C_WrapKey);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_UnwrapKey(CK_SESSION_HANDLE hSession,
CK_MECHANISM_PTR pMechanism,
CK_OBJECT_HANDLE hUnwrappingKey,
CK_BYTE_PTR pWrappedKey,
CK_ULONG ulWrappedKeyLen,
CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulAttributeCount,
CK_OBJECT_HANDLE_PTR phKey)
{
ENTER_PUBLIC_FUNCTION(C_UnwrapKey);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession,
CK_MECHANISM_PTR pMechanism,
CK_OBJECT_HANDLE hBaseKey,
CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulAttributeCount,
CK_OBJECT_HANDLE_PTR phKey)
{
ENTER_PUBLIC_FUNCTION(C_DeriveKey);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_SeedRandom(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pSeed,
CK_ULONG ulSeedLen)
{
ENTER_PUBLIC_FUNCTION(C_SeedRandom);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_GetFunctionStatus(CK_SESSION_HANDLE hSession)
{
ENTER_PUBLIC_FUNCTION(C_GetFunctionStatus);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_CancelFunction(CK_SESSION_HANDLE hSession)
{
ENTER_PUBLIC_FUNCTION(C_CancelFunction);
return CKR_FUNCTION_NOT_SUPPORTED;
}
CK_RV C_WaitForSlotEvent(CK_FLAGS flags,
CK_SLOT_ID_PTR pSlot,
CK_VOID_PTR pRserved)
{
ENTER_PUBLIC_FUNCTION(C_WaitForSlotEvent);
return CKR_FUNCTION_NOT_SUPPORTED;
}
/*
* "Any programmer who fails to comply with the standard naming, formatting,
* or commenting conventions should be shot. If it so happens that it is
* inconvenient to shoot him, then he is to be politely requested to recode
* his program in adherence to the above standard."
* -- Michael Spier, Digital Equipment Corporation
*
* Local variables:
* indent-tabs-mode: nil
* End:
*/