/*
* 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-2016, NORDUnet A/S
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* - Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* - 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.
*
* - Neither the name of the NORDUnet nor the names of its contributors may
* be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* 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
* HOLDER 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 <sqlite3.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"
/*
* 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 0
#endif
#ifndef DEBUG_PKCS11
#define DEBUG_PKCS11 0
#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.
*
* 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 */
hal_digest_algorithm_t
digest_algorithm, /* Hash algorithm for C_Digest*() */
sign_digest_algorithm, /* Hash algorithm for C_Sign*() */
verify_digest_algorithm; /* Hash algorithm for C_Verify*() */
CK_OBJECT_HANDLE
sign_key_handle, /* Private key for C_Sign*() */
verify_key_handle; /* Public key for C_Verify() */
hal_hash_handle_t
digest_handle, /* Hash state for C_Digest*() */
sign_digest_handle, /* Hash state for C_Sign*() */
verify_digest_handle; /* Hash state 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)
/*
* Digest algorithm to use when computing a key hashes. This doesn't
* need to be particularly secure, we're just using it to generate
* reasonably unique identifier strings from public keys. We use
* SHA-1 for this because that's what most X.509 implementations use
* for this purpose.
*/
#define P11_KEY_HASH_ALGORITHM hal_digest_algorithm_sha1
/*
* 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, "\n%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, "\nEntering function %s\n", #_name_)
#else /* DEBUG_PKCS11 > 1 */
#define ENTER_PUBLIC_FUNCTION(_name_)
#endif /* DEBUG_PKCS11 > 1 */
/*
* Error checking for libhal calls.
*/
#define hal_whine(_expr_) (_hal_whine((_expr_), #_expr_, __FILE__, __LINE__, HAL_OK))
#define hal_whine_allow(_expr_, ...) (_hal_whine((_expr_), #_expr_, __FILE__, __LINE__, __VA_ARGS__, HAL_OK))
#define hal_check(_expr_) (hal_whine(_expr_) == HAL_OK)
#if DEBUG_HAL
static inline hal_error_t _hal_whine(const hal_error_t err,
const char * const expr,
const char * const file,
const unsigned line, ...)
{
va_list ap;
int ok = 0;
hal_error_t code;
va_start(ap, line);
do {
code = va_arg(ap, hal_error_t);
ok |= (err == code);
} while (code != HAL_OK);
va_end(ap);
if (!ok)
fprintf(stderr, "\n%s:%u: %s returned %s\n", file, line, expr, hal_error_string(err));
return err;
}
#else /* DEBUG_HAL */
#define _hal_whine(_expr_, ...) (_expr_)
#endif /* DEBUG_HAL */
/*
* Error translation fun for the entire family!
*/
#if DEBUG_PKCS11 || DEBUG_HAL
#define hal_p11_error_case(_hal_err_, _p11_err_) \
case _hal_err_: fprintf(stderr, "\n%s:%u: Mapping %s to %s\n", file, line, #_hal_err_, #_p11_err_); return _p11_err_;
#else
#define hal_p11_error_case(_hal_err_, _p11_err_) \
case _hal_err_: return _p11_err_;
#endif
#define p11_error_from_hal(_hal_err_) \
(_p11_error_from_hal((_hal_err_), __FILE__, __LINE__))
#define p11_whine_from_hal(_expr_) \
(_p11_error_from_hal(_hal_whine((_expr_), #_expr_, __FILE__, __LINE__, HAL_OK), __FILE__, __LINE__))
static CK_RV _p11_error_from_hal(const hal_error_t err, const char * const file, const unsigned line)
{
switch (err) {
hal_p11_error_case(HAL_ERROR_PIN_INCORRECT, CKR_PIN_INCORRECT);
hal_p11_error_case(HAL_ERROR_INVALID_SIGNATURE, CKR_SIGNATURE_INVALID);
/*
* More here later, first see if this compiles.
*/
case HAL_OK:
return CKR_OK;
default:
#if DEBUG_PKCS11 || DEBUG_HAL
fprintf(stderr, "\n%s:%u: Mapping unhandled HAL error to CKR_FUNCTION_FAILED\n", file, line);
#endif
return CKR_FUNCTION_FAILED;
}
}
#undef hal_p11_error_case
/*
* SQL utilities.
*/
/*
* Debugging control.
*/
#ifndef DEBUG_SQL
#define DEBUG_SQL 1
#endif
/*
* Default filename for SQL database lives. Can be overriden at
* runtime by setting PKCS11_DATABASE environment variable.
*/
#ifndef SQL_DATABASE
#define SQL_DATABASE ".cryptech-pkcs11.db"
#endif
/*
* SQL database.
*/
static sqlite3 *sqldb = NULL;
/*
* Error checking for SQLite calls.
*/
#if DEBUG_SQL
#define sql_whine(_expr_) \
(fprintf(stderr, "\n%s:%u: %s returned %s\n", \
__FILE__, __LINE__, #_expr_, sqlite3_errmsg(sqldb)), \
sql_breakpoint())
#else /* DEBUG_SQL */
#define sql_whine(_expr_) \
((void) 0)
#endif /* DEBUG_SQL */
#define sql_check(_good_, _expr_) \
((_expr_) == (_good_) ? 1 : (sql_whine(_expr_), 0))
#define sql_check_ok(_expr_) sql_check(SQLITE_OK, _expr_)
#define sql_check_row(_expr_) sql_check(SQLITE_ROW, _expr_)
#define sql_check_done(_expr_) sql_check(SQLITE_DONE, _expr_)
#define sql_whine_step() sql_whine(sqlite3_step())
/*
* Hook on which to hang a debugger breakpoint on SQL errors.
*/
#if DEBUG_SQL
static void sql_breakpoint(void)
{
fprintf(stderr, "\n[sql_breakpoint]\n");
}
#endif
/*
* Execute SQL code that doesn't require a prepared query.
*/
static int sql_exec(const char *cmd)
{
char *msg = NULL;
if (sql_check_ok(sqlite3_exec(sqldb, cmd, NULL, NULL, &msg)))
return 1;
#if DEBUG_SQL
if (msg != NULL)
fprintf(stderr, "\n[%s]\n", msg);
#endif
return 0;
}
/*
* Initialize SQL. This includes loading our schema, portions of
* which live in the temp (memory) database thus always need to be
* created on startup.
*/
static int sql_init(void)
{
static const char schema[] =
#include "schema.h"
;
assert(sqldb == NULL);
const char * const env = getenv("PKCS11_DATABASE");
const char * const home = getenv("HOME");
const char * const base = SQL_DATABASE;
int ok;
if (env != NULL) {
ok = sql_check_ok(sqlite3_open(env, &sqldb));
}
else if (home == NULL) {
ok = sql_check_ok(sqlite3_open(base, &sqldb));
}
else {
char fn[strlen(home) + strlen(base) + 2];
snprintf(fn, sizeof(fn), "%s/%s", home, base);
ok = sql_check_ok(sqlite3_open(fn, &sqldb));
}
return ok && sql_exec(schema);
}
/*
* Shut down SQL.
*
* Yes, this can return failure, although it's not clear what we're
* meant to do about that if the application is going to shut down
* regardless of what we do. I guess we could loop retrying a few
* times for errors like SQLITE_BUSY, but that's about it.
*/
static int sql_fini(void)
{
if (!sql_check_ok(sqlite3_close(sqldb)))
return 0;
sqldb = NULL;
return 1;
}
/*
* GCC attribute declaration to help catch format string errors,
* ignored by other compilers.
*/
#ifdef __GNUC__
static int sql_prepare(sqlite3_stmt **q,
const char *format, ...)
__attribute__ ((format (printf, 2, 3)));
#endif
/*
* Prepare an SQLite3 query, using vsnprintf() to format the query.
*
* WARNING WARNING WARNING WARNING
*
* Do not use this formatting mechanism for anything involving
* user-supplied data. It's only intended to handle things like
* selecting between two parallel table structures or queries using
* manifest constants that are only available in C header files.
*/
static int sql_prepare(sqlite3_stmt **q, const char *format, ...)
{
char buffer[2048];
va_list ap;
size_t n;
va_start(ap, format);
n = vsnprintf(buffer, sizeof(buffer), format, ap);
va_end(ap);
if (n >= sizeof(buffer))
return SQLITE_TOOBIG;
return sqlite3_prepare_v2(sqldb, buffer, -1, q, NULL);
}
/*
* This idiom occurs frequently, bundle it so we have the option of
* doing it along with the normal conditional control flow that SQL
* queries seem to follow.
*/
static int sql_finalize_and_clear(sqlite3_stmt **q)
{
assert(q != NULL);
int err = sqlite3_finalize(*q);
if (err != SQLITE_OK)
return err;
*q = NULL;
return SQLITE_OK;
}
/*
* 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 */
/*
* Translate between libhal EC curve names and OIDs.
*/
#warning Perhaps this should be a utility routine in libhal instead of here
static int ec_curve_oid_to_name(const uint8_t * const oid, const size_t oid_len, hal_curve_name_t *curve)
{
static uint8_t ec_curve_oid_p256[] = { 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 };
static uint8_t ec_curve_oid_p384[] = { 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22 };
static uint8_t ec_curve_oid_p521[] = { 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23 };
if (oid == NULL || curve == NULL)
return 0;
else if (oid_len == sizeof(ec_curve_oid_p256) && memcmp(oid, ec_curve_oid_p256, oid_len) == 0)
*curve = HAL_CURVE_P256;
else if (oid_len == sizeof(ec_curve_oid_p384) && memcmp(oid, ec_curve_oid_p384, oid_len) == 0)
*curve = HAL_CURVE_P384;
else if (oid_len == sizeof(ec_curve_oid_p521) && memcmp(oid, ec_curve_oid_p521, oid_len) == 0)
*curve = HAL_CURVE_P521;
else
return 0;
return 1;
}
/*
* Extract libhal-compatible client and session identifiers from a session.
*
* libhal's session identifiers are deliberately chosen to be in the same
* numeric range as PKCS #11's, so we can just use them directly.
*
* libhal's client identifiers are multiplexing extension handled elsewhere,
* for our purposes using constant client identifier of zero will do.
*/
static inline hal_client_handle_t p11_session_hal_client(const p11_session_t * const session)
{
hal_client_handle_t handle = {0};
return handle;
}
static inline hal_session_handle_t p11_session_hal_session(const p11_session_t * const session)
{
hal_session_handle_t handle = {session->handle};
return handle;
}
/*
* 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.
*/
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));
}
#if 0
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)
{
if (template != NULL)
for (int i = 0; i < length; i++)
if (template[i].type == type)
return i;
return -1;
}
/*
* Find an attribute in a CK_ATTRIBUTE_PTR template. Returns pointer
* to attribute value, or NULL if not found.
*/
static void *p11_attribute_find_value_in_template(const CK_ATTRIBUTE_TYPE type,
const CK_ATTRIBUTE_PTR template,
const CK_ULONG length)
{
const int i = p11_attribute_find_in_template(type, template, length);
return i < 0 ? NULL : template[i].pValue;
}
/*
* 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 so we
* can simplify things a bit by reusing libhal's flag values.
*/
static void p11_attribute_apply_keyusage(hal_key_flags_t *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 = HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE;
break;
case CKA_ENCRYPT: /* Encrypt bulk data (seldom used) */
case CKA_DECRYPT: /* Bulk decryption (seldom used) */
flag = HAL_KEY_FLAG_USAGE_DATAENCIPHERMENT;
break;
case CKA_WRAP: /* Wrap key (normal way of doing encryption) */
case CKA_UNWRAP: /* Unwrap key (normal way of doing decryption) */
flag = HAL_KEY_FLAG_USAGE_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.
*
* If the attribute is flagged as sensitive in the descriptor, we
* don't store it in SQL. Generally, this only arises for private
* key components of objects created with C_CreateObject(), but in
* theory there are some corner cases in which a user could choose
* to mark a private key as extractable and not sensitive, so we
* might have to back-fill missing values in those cases if anyone
* ever thinks up a sane reason for supporting them. For now, assume
* that private keys are bloody well supposed to be private.
*/
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 (p11_attribute_is_sensitive(descriptor, type))
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;
}
/*
* 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;
}
/*
* Bind PKCS #11 objects to keystore objects.
*
* This requires storing the pkey type (supplied) and the SKI
* (calcualted from supplied public key DER) of the key. We return
* the calculated SKI so that the caller can use it as the pkey name.
*
* We don't work with pkey handles here because that would create a
* chicken-and-egg problem, given that the values calculated and
* stored by this function are what we use to look up a pkey given
* a PKCS #11 key object.
*/
static int p11_object_bind_pkey(const p11_session_t * const session,
const hal_key_type_t pkey_type_1,
const hal_key_type_t pkey_type_2,
const CK_OBJECT_HANDLE object_handle_1,
const CK_OBJECT_HANDLE object_handle_2,
const uint8_t * const der, const size_t der_len,
uint8_t *ski, const size_t ski_len)
{
assert(session != NULL && der != NULL && ski != NULL);
static const char update_format[] =
" UPDATE %s_object SET hal_pkey_type = ?1, hal_pkey_ski = ?2"
" WHERE %s_object_id = (SELECT %s_object_id FROM object WHERE object_handle = ?3)";
const char *flavor_1 = is_token_handle(object_handle_1) ? "token" : "session";
const char *flavor_2 = is_token_handle(object_handle_2) ? "token" : "session";
hal_hash_handle_t hash = {HAL_HANDLE_NONE};
int ok = hal_check(hal_rpc_hash_initialize(p11_session_hal_client(session),
p11_session_hal_session(session),
&hash, P11_KEY_HASH_ALGORITHM, NULL, 0));
if (ok)
ok = hal_check(hal_rpc_hash_update(hash, der, der_len));
if (hash.handle != HAL_HANDLE_NONE)
ok = hal_check(hal_rpc_hash_finalize(hash, ski, ski_len)) && ok;
if (!ok)
return 0;
sqlite3_stmt *q1 = NULL, *q2 = NULL;
ok = (sql_check_ok(sql_prepare(&q1, update_format, flavor_1, flavor_1, flavor_1)) &&
sql_check_ok(sqlite3_bind_int64(q1, 1, pkey_type_1)) &&
sql_check_ok(sqlite3_bind_blob( q1, 2, ski, ski_len, NULL)) &&
sql_check_ok(sqlite3_bind_int64(q1, 3, object_handle_1)) &&
sql_check_done(sqlite3_step(q1)));
if (ok && object_handle_2 != CK_INVALID_HANDLE)
ok = (sql_check_ok(sql_prepare(&q2, update_format, flavor_2, flavor_2, flavor_2)) &&
sql_check_ok(sqlite3_bind_int64(q2, 1, pkey_type_2)) &&
sql_check_ok(sqlite3_bind_blob( q2, 2, ski, ski_len, NULL)) &&
sql_check_ok(sqlite3_bind_int64(q2, 3, object_handle_2)) &&
sql_check_done(sqlite3_step(q2)));
sqlite3_finalize(q1);
sqlite3_finalize(q2);
return ok;
}
/*
* Create pkeys to go with PKCS #11 key objects loaded by C_CreateObject().
*/
static inline int p11_object_create_rsa_public_key(const p11_session_t * const session,
const CK_OBJECT_HANDLE object_handle,
const hal_key_flags_t flags)
{
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";
hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
uint8_t keybuf[hal_rsa_key_t_size];
hal_rsa_key_t *key = NULL;
sqlite3_stmt *q = NULL;
size_t ski_len = 0;
int ok
= (hal_check(hal_rpc_hash_get_digest_length(P11_KEY_HASH_ALGORITHM, &ski_len)) &&
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, sizeof(keybuf),
sqlite3_column_blob( q, 0),
sqlite3_column_bytes(q, 0),
sqlite3_column_blob( q, 1),
sqlite3_column_bytes(q, 1))));
if (ok) {
uint8_t der[hal_rsa_public_key_to_der_len(key)], ski[ski_len];
ok = (hal_check(hal_rsa_public_key_to_der(key, der, NULL, sizeof(der))) &&
p11_object_bind_pkey(session, HAL_KEY_TYPE_RSA_PUBLIC, HAL_KEY_TYPE_NONE,
object_handle, CK_INVALID_HANDLE,
der, sizeof(der), ski, sizeof(ski)) &&
hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
p11_session_hal_session(session),
&pkey, HAL_KEY_TYPE_RSA_PUBLIC, HAL_CURVE_NONE,
ski, sizeof(ski), der, sizeof(der), flags)));
}
(void) hal_rpc_pkey_close(pkey);
sqlite3_finalize(q);
return ok;
}
static inline int p11_object_create_ec_public_key(const p11_session_t * const session,
const CK_OBJECT_HANDLE object_handle,
const hal_key_flags_t flags)
{
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";
hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
uint8_t keybuf[hal_ecdsa_key_t_size];
hal_ecdsa_key_t *key = NULL;
hal_curve_name_t curve;
sqlite3_stmt *q = NULL;
size_t ski_len = 0;
int ok
= (hal_check(hal_rpc_hash_get_digest_length(P11_KEY_HASH_ALGORITHM, &ski_len)) &&
sql_check_ok(sql_prepare(&q, select_format, flavor, CKA_EC_PARAMS, CKA_EC_POINT)) &&
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 &&
ec_curve_oid_to_name(sqlite3_column_blob( q, 0), sqlite3_column_bytes(q, 0), &curve) &&
hal_check(hal_ecdsa_key_from_ecpoint(&key, keybuf, sizeof(keybuf),
sqlite3_column_blob( q, 1),
sqlite3_column_bytes(q, 1),
curve)));
if (ok) {
uint8_t der[hal_ecdsa_public_key_to_der_len(key)], ski[ski_len];
ok = (hal_check(hal_ecdsa_public_key_to_der(key, der, NULL, sizeof(der))) &&
p11_object_bind_pkey(session, HAL_KEY_TYPE_EC_PUBLIC, HAL_KEY_TYPE_NONE,
object_handle, CK_INVALID_HANDLE,
der, sizeof(der), ski, sizeof(ski)) &&
hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
p11_session_hal_session(session),
&pkey, HAL_KEY_TYPE_EC_PUBLIC, curve,
ski, sizeof(ski), der, sizeof(der), flags)));
}
(void) hal_rpc_pkey_close(pkey);
sqlite3_finalize(q);
return ok;
}
static inline int p11_object_create_rsa_private_key(const p11_session_t * const session,
const CK_OBJECT_HANDLE object_handle,
const hal_key_flags_t flags,
const CK_ATTRIBUTE_PTR const template,
const CK_ULONG template_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";
hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
uint8_t keybuf[hal_rsa_key_t_size];
hal_rsa_key_t *key = NULL;
sqlite3_stmt *q = NULL;
size_t ski_len = 0;
const uint8_t *cka_private_exponent = NULL; size_t cka_private_exponent_len = 0;
const uint8_t *cka_prime_1 = NULL; size_t cka_prime_1_len = 0;
const uint8_t *cka_prime_2 = NULL; size_t cka_prime_2_len = 0;
const uint8_t *cka_exponent_1 = NULL; size_t cka_exponent_1_len = 0;
const uint8_t *cka_exponent_2 = NULL; size_t cka_exponent_2_len = 0;
const uint8_t *cka_coefficient = NULL; size_t cka_coefficient_len = 0;
for (int i = 0; i < template_len; i++) {
switch (template[i].type) {
case CKA_PRIVATE_EXPONENT:
cka_private_exponent = template[i].pValue; cka_private_exponent_len = template[i].ulValueLen;
break;
case CKA_PRIME_1:
cka_prime_1 = template[i].pValue; cka_prime_1_len = template[i].ulValueLen;
break;
case CKA_PRIME_2:
cka_prime_2 = template[i].pValue; cka_prime_2_len = template[i].ulValueLen;
break;
case CKA_EXPONENT_1:
cka_exponent_1 = template[i].pValue; cka_exponent_1_len = template[i].ulValueLen;
break;
case CKA_EXPONENT_2:
cka_exponent_2 = template[i].pValue; cka_exponent_2_len = template[i].ulValueLen;
break;
case CKA_COEFFICIENT:
cka_coefficient = template[i].pValue; cka_coefficient_len = template[i].ulValueLen;
break;
}
}
int ok
= (hal_check(hal_rpc_hash_get_digest_length(P11_KEY_HASH_ALGORITHM, &ski_len)) &&
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_private(&key, keybuf, sizeof(keybuf),
sqlite3_column_blob( q, 0), sqlite3_column_bytes(q, 0),
sqlite3_column_blob( q, 1), sqlite3_column_bytes(q, 1),
cka_private_exponent, cka_private_exponent_len,
cka_prime_1, cka_prime_1_len,
cka_prime_2, cka_prime_2_len,
cka_coefficient, cka_coefficient_len,
cka_exponent_1, cka_exponent_1_len,
cka_exponent_2, cka_exponent_2_len)));
if (ok) {
const size_t private_len = hal_rsa_private_key_to_der_len(key);
const size_t public_len = hal_rsa_public_key_to_der_len(key);
uint8_t der[public_len > private_len ? public_len : private_len], ski[ski_len];
ok = (hal_check(hal_rsa_public_key_to_der(key, der, NULL, sizeof(der))) &&
p11_object_bind_pkey(session, HAL_KEY_TYPE_RSA_PRIVATE, HAL_KEY_TYPE_NONE,
object_handle, CK_INVALID_HANDLE,
der, public_len, ski, sizeof(ski)) &&
hal_check(hal_rsa_private_key_to_der(key, der, NULL, sizeof(der))) &&
hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
p11_session_hal_session(session),
&pkey, HAL_KEY_TYPE_RSA_PRIVATE, HAL_CURVE_NONE,
ski, sizeof(ski), der, private_len, flags)));
memset(der, 0, sizeof(der));
}
memset(keybuf, 0, sizeof(keybuf));
(void) hal_rpc_pkey_close(pkey);
sqlite3_finalize(q);
return ok;
}
static inline int p11_object_create_ec_private_key(const p11_session_t * const session,
const CK_OBJECT_HANDLE object_handle,
const hal_key_flags_t flags,
const CK_ATTRIBUTE_PTR const template,
const CK_ULONG template_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";
hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
uint8_t keybuf[hal_ecdsa_key_t_size];
hal_ecdsa_key_t *key = NULL;
hal_curve_name_t curve;
sqlite3_stmt *q = NULL;
size_t ski_len = 0;
const uint8_t *ecpoint = NULL;
size_t ecpoint_len = 0;
const uint8_t *cka_value = NULL; size_t cka_value_len = 0;
for (int i = 0; i < template_len; i++)
if (template[i].type == CKA_VALUE)
cka_value = template[i].pValue, cka_value_len = template[i].ulValueLen;
int ok
= (hal_check(hal_rpc_hash_get_digest_length(P11_KEY_HASH_ALGORITHM, &ski_len)) &&
sql_check_ok(sql_prepare(&q, select_format, flavor, CKA_EC_PARAMS, CKA_EC_POINT)) &&
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 &&
ec_curve_oid_to_name(sqlite3_column_blob( q, 0), sqlite3_column_bytes(q, 0), &curve) &&
((ecpoint_len = sqlite3_column_bytes(q, 1)) & 1) != 0 &&
*(ecpoint = sqlite3_column_blob( q, 1)) == 0x04 &&
hal_check(hal_ecdsa_key_load_private(&key, keybuf, sizeof(keybuf), curve,
ecpoint + 1 + 0 * ecpoint_len / 2, ecpoint_len / 2,
ecpoint + 1 + 0 * ecpoint_len / 2, ecpoint_len / 2,
cka_value, cka_value_len)));
if (ok) {
const size_t private_len = hal_ecdsa_private_key_to_der_len(key);
const size_t public_len = hal_ecdsa_public_key_to_der_len(key);
uint8_t der[public_len > private_len ? public_len : private_len], ski[ski_len];
ok = (hal_check(hal_ecdsa_public_key_to_der(key, der, NULL, sizeof(der))) &&
p11_object_bind_pkey(session, HAL_KEY_TYPE_EC_PRIVATE, HAL_KEY_TYPE_NONE,
object_handle, CK_INVALID_HANDLE,
der, public_len, ski, sizeof(ski)) &&
hal_check(hal_ecdsa_private_key_to_der(key, der, NULL, sizeof(der))) &&
hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
p11_session_hal_session(session),
&pkey, HAL_KEY_TYPE_EC_PRIVATE, curve,
ski, sizeof(ski), der, private_len, flags)));
memset(der, 0, sizeof(der));
}
memset(keybuf, 0, sizeof(keybuf));
(void) hal_rpc_pkey_close(pkey);
sqlite3_finalize(q);
return ok;
}
/*
* Given a PKCS #11 object, obtain a libhal pkey handle.
*/
static int p11_object_get_pkey_handle(const p11_session_t * const session,
const CK_OBJECT_HANDLE object_handle,
hal_pkey_handle_t *pkey_handle)
{
static const char select_format[] =
" SELECT hal_pkey_type, hal_pkey_ski FROM %s_object NATURAL JOIN object WHERE object_handle = ?1";
hal_key_flags_t flags = is_token_handle(object_handle) ? 0 : HAL_KEY_FLAG_PROXIMATE;
const char *flavor = is_token_handle(object_handle) ? "token" : "session";
sqlite3_stmt *q = NULL;
int ok = 0;
assert(pkey_handle != NULL);
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_INTEGER ||
sqlite3_column_type(q, 1) != SQLITE_BLOB)
goto fail;
const hal_key_type_t pkey_type = sqlite3_column_int64(q, 0);
const uint8_t * const ski = sqlite3_column_blob( q, 1);
const size_t ski_len = sqlite3_column_bytes(q, 1);
ok = hal_check(hal_rpc_pkey_find(p11_session_hal_client(session), p11_session_hal_session(session),
pkey_handle, pkey_type, ski, ski_len, flags));
fail:
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);
(void) hal_rpc_hash_finalize(session->digest_handle, NULL, 0);
(void) hal_rpc_hash_finalize(session->sign_digest_handle, NULL, 0);
(void) hal_rpc_hash_finalize(session->verify_digest_handle, NULL, 0);
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);
/* Deleting last session also logs us out */
if (p11_sessions == NULL)
logged_in_as = not_logged_in;
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);
}
logged_in_as = not_logged_in;
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.
*/
/*
* 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_template_check_1(const CK_ATTRIBUTE_TYPE type,
const void * const val,
const size_t len,
const p11_descriptor_t * const descriptor,
unsigned long forbidden_flag)
{
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 & forbidden_flag) != 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);
#warning Add _LATCH checks here?
rv = CKR_OK;
fail:
#if DEBUG_PKCS11
if (rv != CKR_OK)
fprintf(stderr, "\np11_template_check_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_template_check_2(const p11_session_t *session,
const p11_descriptor_t * const descriptor,
const CK_ATTRIBUTE_PTR template,
const CK_ULONG template_length,
unsigned long required_flag,
unsigned long forbidden_flag)
{
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 ((object_is_private = p11_attribute_find_value_in_template(CKA_PRIVATE, template, template_length)) == NULL) {
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 & required_flag) != 0;
const int forbidden_by_api = (atd->flags & forbidden_flag) != 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, "\n[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.
*/
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,
hal_key_flags_t *public_flags,
const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
const CK_ULONG ulPrivateKeyAttributeCount,
const p11_descriptor_t * const private_descriptor,
hal_key_flags_t *private_flags)
{
CK_RV rv = CKR_OK;
int i;
assert(session != NULL &&
pPublicKeyTemplate != NULL && public_descriptor != NULL && public_flags != NULL &&
pPrivateKeyTemplate != NULL && private_descriptor != NULL && private_flags != NULL);
*public_flags = *private_flags = 0;
/*
* 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_template_check_1(type, val, len, public_descriptor,
P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE)) != CKR_OK)
goto fail;
p11_attribute_apply_keyusage(public_flags, 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_template_check_1(type, val, len, private_descriptor,
P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE)) != CKR_OK)
goto fail;
p11_attribute_apply_keyusage(private_flags, 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_flags != *private_flags || *public_flags == 0)
lose(CKR_TEMPLATE_INCONSISTENT);
/*
* Check that all required attributes have been specified.
*/
if ((rv = p11_template_check_2(session,
public_descriptor,
pPublicKeyTemplate,
ulPublicKeyAttributeCount,
P11_DESCRIPTOR_REQUIRED_BY_GENERATE,
P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE)) != CKR_OK ||
(rv = p11_template_check_2(session,
private_descriptor,
pPrivateKeyTemplate,
ulPrivateKeyAttributeCount,
P11_DESCRIPTOR_REQUIRED_BY_GENERATE,
P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE)) != 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 handler.
*/
static CK_RV generate_keypair_rsa_pkcs(p11_session_t *session,
const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
const CK_ULONG ulPublicKeyAttributeCount,
const CK_OBJECT_HANDLE public_handle,
const hal_key_flags_t public_flags,
const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
const CK_ULONG ulPrivateKeyAttributeCount,
const CK_OBJECT_HANDLE private_handle,
const hal_key_flags_t private_flags)
{
const uint8_t *public_exponent = const_0x010001;
size_t public_exponent_len = sizeof(const_0x010001);
hal_pkey_handle_t pkey1 = {HAL_HANDLE_NONE}, pkey2 = {HAL_HANDLE_NONE};
CK_ULONG keysize = 0;
size_t ski_len = 0;
CK_RV rv;
int i;
assert(session != NULL && pPublicKeyTemplate != NULL && pPrivateKeyTemplate != NULL && is_token_handle(private_handle));
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;
switch (type) {
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;
case CKA_PUBLIC_EXPONENT:
public_exponent = val;
public_exponent_len = len;
continue;
}
}
if (keysize == 0)
return CKR_TEMPLATE_INCOMPLETE;
if (!hal_check(hal_rpc_hash_get_digest_length(P11_KEY_HASH_ALGORITHM, &ski_len)))
lose(CKR_FUNCTION_FAILED);
if (!hal_check(hal_rpc_pkey_generate_rsa(p11_session_hal_client(session),
p11_session_hal_session(session),
&pkey1, (const uint8_t *) "", 0, keysize,
public_exponent, public_exponent_len,
private_flags)))
lose(CKR_FUNCTION_FAILED);
{
uint8_t der[hal_rpc_pkey_get_public_key_len(pkey1)], keybuf[hal_rsa_key_t_size], ski[ski_len];
size_t der_len, modulus_len;
hal_rsa_key_t *key = NULL;
const hal_key_type_t pkey2_type = is_token_handle(public_handle) ? HAL_KEY_TYPE_RSA_PRIVATE : HAL_KEY_TYPE_RSA_PUBLIC;
if (!hal_check(hal_rpc_pkey_get_public_key(pkey1, der, &der_len, sizeof(der))) ||
!p11_object_bind_pkey(session, HAL_KEY_TYPE_RSA_PRIVATE, pkey2_type,
private_handle, public_handle,
der, sizeof(der), ski, sizeof(ski)) ||
!hal_check(hal_rpc_pkey_rename(pkey1, ski, sizeof(ski))) ||
!hal_check(hal_rsa_public_key_from_der(&key, keybuf, sizeof(keybuf), der, der_len)) ||
!hal_check(hal_rsa_key_get_modulus(key, NULL, &modulus_len, 0)))
lose(CKR_FUNCTION_FAILED);
uint8_t modulus[modulus_len];
if (!hal_check(hal_rsa_key_get_modulus(key, modulus, NULL, 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);
if (!is_token_handle(public_handle) &&
!hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
p11_session_hal_session(session),
&pkey2, HAL_KEY_TYPE_RSA_PUBLIC, HAL_CURVE_NONE,
ski, sizeof(ski), der, der_len, public_flags)))
lose(CKR_FUNCTION_FAILED);
}
rv = CKR_OK;
fail:
hal_rpc_pkey_close(pkey1);
hal_rpc_pkey_close(pkey2);
return rv;
}
/*
* CKM_EC_KEY_PAIR_GEN key pair generation handler.
*/
static CK_RV generate_keypair_ec(p11_session_t *session,
const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
const CK_ULONG ulPublicKeyAttributeCount,
const CK_OBJECT_HANDLE public_handle,
const hal_key_flags_t public_flags,
const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
const CK_ULONG ulPrivateKeyAttributeCount,
const CK_OBJECT_HANDLE private_handle,
const hal_key_flags_t private_flags)
{
hal_pkey_handle_t pkey1 = {HAL_HANDLE_NONE}, pkey2 = {HAL_HANDLE_NONE};
const CK_BYTE *params = NULL;
hal_curve_name_t curve;
size_t params_len;
size_t ski_len = 0;
CK_RV rv;
int i;
assert(session != NULL && pPublicKeyTemplate != NULL && pPrivateKeyTemplate != NULL && is_token_handle(private_handle));
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;
switch (type) {
case CKA_EC_PARAMS:
params = val;
params_len = len;
continue;
}
}
if (!ec_curve_oid_to_name(params, params_len, &curve))
return CKR_TEMPLATE_INCOMPLETE;
if (!hal_check(hal_rpc_hash_get_digest_length(P11_KEY_HASH_ALGORITHM, &ski_len)))
lose(CKR_FUNCTION_FAILED);
if (!hal_check(hal_rpc_pkey_generate_ec(p11_session_hal_client(session),
p11_session_hal_session(session),
&pkey1, (const uint8_t *) "", 0,
curve, private_flags)) ||
!p11_attribute_set(public_handle, CKA_EC_PARAMS, params, params_len) ||
!p11_attribute_set(private_handle, CKA_EC_PARAMS, params, params_len))
lose(CKR_FUNCTION_FAILED);
{
uint8_t der[hal_rpc_pkey_get_public_key_len(pkey1)], keybuf[hal_ecdsa_key_t_size], ski[ski_len];
hal_ecdsa_key_t *key = NULL;
size_t der_len;
const hal_key_type_t pkey2_type = is_token_handle(public_handle) ? HAL_KEY_TYPE_EC_PRIVATE : HAL_KEY_TYPE_EC_PUBLIC;
if (!hal_check(hal_rpc_pkey_get_public_key(pkey1, der, &der_len, sizeof(der))) ||
!p11_object_bind_pkey(session, HAL_KEY_TYPE_EC_PRIVATE, pkey2_type,
private_handle, public_handle,
der, sizeof(der), ski, sizeof(ski)) ||
!hal_check(hal_rpc_pkey_rename(pkey1, ski, sizeof(ski))) ||
!hal_check(hal_ecdsa_public_key_from_der(&key, keybuf, sizeof(keybuf), der, der_len)))
lose(CKR_FUNCTION_FAILED);
uint8_t point[hal_ecdsa_key_to_ecpoint_len(key)];
if (!hal_check(hal_ecdsa_key_to_ecpoint(key, point, NULL, sizeof(point))) ||
!p11_attribute_set(public_handle, CKA_EC_POINT, point, sizeof(point)))
lose(CKR_FUNCTION_FAILED);
if (!is_token_handle(public_handle) &&
!hal_check(hal_rpc_pkey_load(p11_session_hal_client(session),
p11_session_hal_session(session),
&pkey2, HAL_KEY_TYPE_EC_PUBLIC, curve,
ski, sizeof(ski), der, der_len, public_flags)))
lose(CKR_FUNCTION_FAILED);
}
rv = CKR_OK;
fail:
hal_rpc_pkey_close(pkey1);
hal_rpc_pkey_close(pkey2);
return rv;
}
/*
* Key pair generation. This needs a mechanism-specific function to
* do the inner bits, but there's a lot of boilerplate.
*
* We have no real way to protect session objects, so we don't allow
* them to hold private keys. PKCS #11 wants us to report this kind
* of restriction as a template inconsistency.
*/
static CK_RV generate_keypair(p11_session_t *session,
const CK_MECHANISM_PTR pMechanism,
CK_RV (*mechanism_handler)(p11_session_t *session,
const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
const CK_ULONG ulPublicKeyAttributeCount,
const CK_OBJECT_HANDLE public_handle,
const hal_key_flags_t public_flags,
const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
const CK_ULONG ulPrivateKeyAttributeCount,
const CK_OBJECT_HANDLE private_handle,
const hal_key_flags_t private_flags),
const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
const CK_ULONG ulPublicKeyAttributeCount,
const p11_descriptor_t * const public_descriptor,
CK_OBJECT_HANDLE_PTR phPublicKey,
const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
const CK_ULONG ulPrivateKeyAttributeCount,
const p11_descriptor_t * const private_descriptor,
CK_OBJECT_HANDLE_PTR phPrivateKey)
{
CK_OBJECT_HANDLE public_handle = CK_INVALID_HANDLE;
CK_OBJECT_HANDLE private_handle = CK_INVALID_HANDLE;
handle_flavor_t public_handle_flavor = handle_flavor_session_object;
handle_flavor_t private_handle_flavor = handle_flavor_session_object;
hal_key_flags_t public_flags = 0;
hal_key_flags_t private_flags = 0;
CK_RV rv;
int i;
rv = p11_check_keypair_attributes(session,
pPublicKeyTemplate, ulPublicKeyAttributeCount, public_descriptor, &public_flags,
pPrivateKeyTemplate, ulPrivateKeyAttributeCount, private_descriptor, &private_flags);
if (rv != CKR_OK)
return rv;
assert(session != NULL && pMechanism != NULL &&
pPublicKeyTemplate != NULL && phPublicKey != NULL &&
pPrivateKeyTemplate != NULL && phPrivateKey != NULL);
for (i = 0; i < ulPublicKeyAttributeCount; i++)
if (pPublicKeyTemplate[i].type == CKA_TOKEN)
public_handle_flavor = p11_handle_flavor_from_cka_token(pPublicKeyTemplate[i].pValue);
for (i = 0; i < ulPrivateKeyAttributeCount; i++)
if (pPrivateKeyTemplate[i].type == CKA_TOKEN)
private_handle_flavor = p11_handle_flavor_from_cka_token(pPrivateKeyTemplate[i].pValue);
if (public_handle_flavor == handle_flavor_session_object)
public_flags |= HAL_KEY_FLAG_PROXIMATE;
if (private_handle_flavor == handle_flavor_session_object)
return CKR_TEMPLATE_INCONSISTENT;
if (!sql_exec("BEGIN"))
lose(CKR_FUNCTION_FAILED);
if ((public_handle = p11_object_create(session, public_handle_flavor,
pPublicKeyTemplate, ulPublicKeyAttributeCount,
public_descriptor, pMechanism)) == CK_INVALID_HANDLE)
lose(CKR_FUNCTION_FAILED);
if ((private_handle = p11_object_create(session, private_handle_flavor,
pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
private_descriptor, pMechanism)) == CK_INVALID_HANDLE)
lose(CKR_FUNCTION_FAILED);
rv = mechanism_handler(session,
pPublicKeyTemplate, ulPublicKeyAttributeCount, public_handle, public_flags,
pPrivateKeyTemplate, ulPrivateKeyAttributeCount, private_handle, private_flags);
if (rv != CKR_OK)
goto fail;
if (!sql_exec("COMMIT"))
lose(CKR_FUNCTION_FAILED);
*phPublicKey = public_handle;
*phPrivateKey = private_handle;
return CKR_OK;
fail:
if (!sql_exec("ROLLBACK"))
rv = CKR_GENERAL_ERROR;
return rv;
}
/*
* Mechanism-independent checks for templates and descriptors when
* import objects via C_CreateObject().
*
* Fun question exactly how calling code knows what descriptor to
* pass. p11_descriptor_from_key_type() will suffice for key objects.
* Drive off that bridge when we get to it.
*/
static CK_RV p11_check_create_attributes(const p11_session_t *session,
const CK_ATTRIBUTE_PTR pTemplate,
const CK_ULONG ulCount,
const p11_descriptor_t * const descriptor)
{
CK_RV rv = CKR_OK;
int i;
assert(session != NULL && pTemplate != NULL && descriptor != NULL);
/*
* Read-only sessions can't create objects, doh.
*/
switch (session->state) {
case CKS_RO_PUBLIC_SESSION:
case CKS_RO_USER_FUNCTIONS:
lose(CKR_SESSION_READ_ONLY);
}
/*
* Check values provided in the template.
*/
for (i = 0; i < ulCount; i++) {
const CK_ATTRIBUTE_TYPE type = pTemplate[i].type;
const void * const val = pTemplate[i].pValue;
const size_t len = pTemplate[i].ulValueLen;
if ((rv = p11_template_check_1(type, val, len, descriptor,
P11_DESCRIPTOR_FORBIDDEN_BY_CREATEOBJECT)) != CKR_OK)
goto fail;
}
/*
* Check that all required attributes have been specified.
*/
if ((rv = p11_template_check_2(session, descriptor, pTemplate, ulCount,
P11_DESCRIPTOR_REQUIRED_BY_CREATEOBJECT,
P11_DESCRIPTOR_FORBIDDEN_BY_CREATEOBJECT)) != CKR_OK)
goto fail;
/*
* If we get this far, we're happy. Maybe.
*/
rv = CKR_OK;
fail:
return rv;
}
/*
* Add data to a digest.
*/
static CK_RV digest_update(const p11_session_t * const session,
const hal_digest_algorithm_t algorithm,
hal_hash_handle_t *handle,
const uint8_t * const data, const size_t data_len)
{
assert(algorithm != hal_digest_algorithm_none && handle != NULL && data != NULL);
if (handle->handle == HAL_HANDLE_NONE) {
switch (hal_rpc_hash_initialize(p11_session_hal_client(session),
p11_session_hal_session(session),
handle, algorithm, NULL, 0)) {
case HAL_OK:
break;
case HAL_ERROR_ALLOCATION_FAILURE:
return CKR_HOST_MEMORY;
default:
return CKR_FUNCTION_FAILED;
}
}
if (!hal_check(hal_rpc_hash_update(*handle, data, data_len)))
return CKR_FUNCTION_FAILED;
return CKR_OK;
}
/*
* Finish using a digest context, if we haven't already.
*/
static void digest_cleanup(hal_hash_handle_t *handle)
{
assert(handle != NULL);
if (handle->handle == HAL_HANDLE_NONE)
return;
(void) hal_rpc_hash_finalize(*handle, NULL, 0);
handle->handle = HAL_HANDLE_NONE;
}
/*
* Compute the length of a signature based on the key. We could get
* this via the RPC API, but its probably faster to look in the local
* attribute database. Rewrite this later if this proves incorrect.
*/
static int get_signature_len(const CK_OBJECT_HANDLE object_handle,
const hal_pkey_handle_t pkey,
size_t *signature_len)
{
assert(signature_len != NULL);
CK_KEY_TYPE cka_key_type;
hal_curve_name_t curve;
CK_BYTE oid[20];
CK_ULONG len;
if (!p11_attribute_get_ulong(object_handle, CKA_KEY_TYPE, &cka_key_type))
return 0;
switch (cka_key_type) {
case CKK_RSA:
if (!p11_attribute_get(object_handle, CKA_MODULUS, NULL, &len, 0))
return 0;
*signature_len = len;
return 1;
case CKK_EC:
if (!p11_attribute_get(object_handle, CKA_EC_PARAMS, oid, &len, sizeof(oid)) ||
!ec_curve_oid_to_name(oid, len, &curve))
return 0;
switch (curve) {
case HAL_CURVE_P256: *signature_len = 64; return 1;
case HAL_CURVE_P384: *signature_len = 96; return 1;
case HAL_CURVE_P521: *signature_len = 132; return 1;
default: return 0;
}
}
return 0;
}
/*
* Generate a signature using the libhal RPC API.
*/
static CK_RV sign_hal_rpc(p11_session_t *session,
CK_BYTE_PTR pData,
CK_ULONG ulDataLen,
CK_BYTE_PTR pSignature,
CK_ULONG_PTR pulSignatureLen)
{
hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
size_t signature_len;
CK_RV rv;
assert(session != NULL && pulSignatureLen != NULL);
if (!p11_object_get_pkey_handle(session, session->sign_key_handle, &pkey))
lose(CKR_FUNCTION_FAILED);
if (!get_signature_len(session->sign_key_handle, pkey, &signature_len))
lose(CKR_FUNCTION_FAILED);
rv = signature_len > *pulSignatureLen ? CKR_BUFFER_TOO_SMALL : CKR_OK;
*pulSignatureLen = signature_len;
if (pSignature != NULL && rv == CKR_BUFFER_TOO_SMALL)
lose(CKR_BUFFER_TOO_SMALL);
if (pSignature == NULL)
rv = CKR_OK;
else
rv = p11_whine_from_hal(hal_rpc_pkey_sign(p11_session_hal_session(session), pkey, session->sign_digest_handle,
pData, ulDataLen, pSignature, &signature_len, signature_len));
/* Fall through */
fail:
hal_rpc_pkey_close(pkey);
return rv;
}
/*
* Verify a signature using the libhal RPC API.
*/
static CK_RV verify_hal_rpc(p11_session_t *session,
CK_BYTE_PTR pData,
CK_ULONG ulDataLen,
CK_BYTE_PTR pSignature,
CK_ULONG ulSignatureLen)
{
hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
CK_RV rv;
assert(session != NULL);
if (!p11_object_get_pkey_handle(session, session->verify_key_handle, &pkey))
lose(CKR_FUNCTION_FAILED);
rv = p11_whine_from_hal(hal_rpc_pkey_verify(p11_session_hal_session(session), pkey, session->verify_digest_handle,
pData, ulDataLen, pSignature, ulSignatureLen));
/* Fall through */
fail:
hal_rpc_pkey_close(pkey);
return rv;
}
#warning May need to do something about truncating oversized hashes for ECDSA, see PKCS11 spec
/*
* 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;
int initialized_rpc = 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 libhal RPC channel.
*/
if (!hal_check(hal_rpc_client_init()))
lose(CKR_GENERAL_ERROR);
initialized_rpc = 1;
/*
* Initialize SQLite3, opening the database(s) and loading the
* schema and views.
*/
if (!sql_init())
lose(CKR_GENERAL_ERROR);
initialized_sql = 1;
#if USE_POSIX
initialized_pid = getpid();
#endif
return CKR_OK;
fail:
if (initialized_sql)
sql_fini();
if (initialized_rpc)
hal_rpc_client_close();
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 any of the rest of this fails.
*/
hal_rpc_client_close();
rv = mutex_unlock(p11_global_mutex);
(void) mutex_destroy(p11_global_mutex);
p11_global_mutex = NULL;
#if USE_POSIX
initialized_pid = 0;
#endif
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 = (CK_ULONG) hal_rpc_min_pin_length;
pInfo->ulMinPinLen = (CK_ULONG) hal_rpc_max_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);
const hal_client_handle_t client = {HAL_HANDLE_NONE};
p11_session_t *session;
hal_user_t user = HAL_USER_NONE;
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:
user = HAL_USER_NORMAL;
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);
user = HAL_USER_SO;
break;
case CKU_CONTEXT_SPECIFIC:
lose(CKR_OPERATION_NOT_INITIALIZED);
default:
lose(CKR_USER_TYPE_INVALID);
}
/*
* Try to log in the HSM.
*/
if ((rv = p11_whine_from_hal(hal_rpc_login(client, user, (char *) pPin, ulPinLen))) != CKR_OK)
goto fail;
/*
* 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:
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_Logout(CK_SESSION_HANDLE hSession)
{
ENTER_PUBLIC_FUNCTION(C_Logout);
const hal_client_handle_t client = {HAL_HANDLE_NONE};
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());
if ((rv = p11_whine_from_hal(hal_rpc_logout(client))) != CKR_OK)
goto fail;
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_CreateObject(CK_SESSION_HANDLE hSession,
CK_ATTRIBUTE_PTR pTemplate,
CK_ULONG ulCount,
CK_OBJECT_HANDLE_PTR phObject)
{
ENTER_PUBLIC_FUNCTION(C_CreateObject);
CK_OBJECT_HANDLE handle = CK_INVALID_HANDLE;
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 (pTemplate == NULL || phObject == NULL)
lose(CKR_ARGUMENTS_BAD);
const CK_OBJECT_CLASS * const cka_class = p11_attribute_find_value_in_template(CKA_CLASS, pTemplate, ulCount);
const CK_KEY_TYPE * const cka_key_type = p11_attribute_find_value_in_template(CKA_KEY_TYPE, pTemplate, ulCount);
const CK_BBOOL * const cka_token = p11_attribute_find_value_in_template(CKA_TOKEN, pTemplate, ulCount);
if (cka_class == NULL)
lose(CKR_TEMPLATE_INCOMPLETE);
switch (*cka_class) {
case CKO_PUBLIC_KEY:
case CKO_PRIVATE_KEY:
case CKO_SECRET_KEY:
break;
default:
lose(CKR_TEMPLATE_INCONSISTENT);
}
if (cka_key_type == NULL)
lose(CKR_TEMPLATE_INCOMPLETE);
const p11_descriptor_t * const
descriptor = p11_descriptor_from_key_type(*cka_class, *cka_key_type);
if (descriptor == NULL)
lose(CKR_TEMPLATE_INCONSISTENT);
if ((rv = p11_check_create_attributes(session, pTemplate, ulCount, descriptor)) != CKR_OK)
goto fail;
const handle_flavor_t flavor
= cka_token == NULL ? handle_flavor_session_object : p11_handle_flavor_from_cka_token(cka_token);
switch (session->state) {
case CKS_RO_PUBLIC_SESSION:
case CKS_RO_USER_FUNCTIONS:
if (flavor == handle_flavor_token_object)
lose(CKR_SESSION_READ_ONLY);
}
if (flavor == handle_flavor_session_object && *cka_class == CKO_PRIVATE_KEY)
lose(CKR_TEMPLATE_INCONSISTENT);
if (!sql_exec("BEGIN"))
lose(CKR_FUNCTION_FAILED);
if ((handle = p11_object_create(session, flavor, pTemplate, ulCount, descriptor, NULL)) == CK_INVALID_HANDLE)
lose(CKR_FUNCTION_FAILED);
if (!p11_attribute_set_bbool(handle, CKA_LOCAL, CK_FALSE))
lose(CKR_FUNCTION_FAILED);
switch (*cka_class) {
case CKO_PRIVATE_KEY:
case CKO_SECRET_KEY:
if (!p11_attribute_set_bbool(handle, CKA_ALWAYS_SENSITIVE, CK_FALSE) ||
!p11_attribute_set_bbool(handle, CKA_NEVER_EXTRACTABLE, CK_FALSE))
lose(CKR_FUNCTION_FAILED);
}
hal_key_flags_t flags = flavor == handle_flavor_session_object ? HAL_KEY_FLAG_PROXIMATE : 0;
for (int i = 0; i < ulCount; i++)
p11_attribute_apply_keyusage(&flags, pTemplate[i].type, pTemplate[i].pValue);
if (*cka_class == CKO_PUBLIC_KEY && *cka_key_type == CKK_RSA &&
!p11_object_create_rsa_public_key(session, handle, flags))
lose(CKR_FUNCTION_FAILED);
if (*cka_class == CKO_PUBLIC_KEY && *cka_key_type == CKK_EC &&
!p11_object_create_ec_public_key(session, handle, flags))
lose(CKR_FUNCTION_FAILED);
if (*cka_class == CKO_PRIVATE_KEY && *cka_key_type == CKK_RSA &&
!p11_object_create_rsa_private_key(session, handle, flags, pTemplate, ulCount))
lose(CKR_FUNCTION_FAILED);
if (*cka_class == CKO_PRIVATE_KEY && *cka_key_type == CKK_EC &&
!p11_object_create_ec_private_key(session, handle, flags, pTemplate, ulCount))
lose(CKR_FUNCTION_FAILED);
if (!sql_exec("COMMIT"))
lose(CKR_FUNCTION_FAILED);
*phObject = handle;
return mutex_unlock(p11_global_mutex);
fail:
if (!sql_exec("ROLLBACK"))
rv = CKR_GENERAL_ERROR;
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 = ?)";
hal_pkey_handle_t pkey = {HAL_HANDLE_NONE};
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 (p11_object_get_pkey_handle(session, hObject, &pkey) && !hal_check(hal_rpc_pkey_delete(pkey)))
lose(CKR_FUNCTION_FAILED);
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);
hal_digest_algorithm_t algorithm;
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_algorithm != hal_digest_algorithm_none)
lose(CKR_OPERATION_ACTIVE);
switch (pMechanism->mechanism) {
case CKM_SHA_1: algorithm = hal_digest_algorithm_sha1; break;
case CKM_SHA256: algorithm = hal_digest_algorithm_sha256; break;
case CKM_SHA384: algorithm = hal_digest_algorithm_sha384; break;
case CKM_SHA512: algorithm = hal_digest_algorithm_sha512; break;
default: lose(CKR_MECHANISM_INVALID);
}
session->digest_algorithm = algorithm;
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;
size_t digest_len;
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_algorithm == hal_digest_algorithm_none)
lose(CKR_OPERATION_NOT_INITIALIZED);
if (session->digest_handle.handle != HAL_HANDLE_NONE)
lose(CKR_OPERATION_ACTIVE);
if (!hal_check(hal_rpc_hash_get_digest_length(session->digest_algorithm, &digest_len)))
lose(CKR_FUNCTION_FAILED);
rv = *pulDigestLen < digest_len ? CKR_BUFFER_TOO_SMALL : CKR_OK;
*pulDigestLen = digest_len;
if (pDigest == NULL)
return mutex_unlock(p11_global_mutex);
if (rv == CKR_BUFFER_TOO_SMALL)
lose(CKR_BUFFER_TOO_SMALL);
if ((rv = digest_update(session, session->digest_algorithm,
&session->digest_handle, pData, ulDataLen)) != CKR_OK)
goto fail;
if (!hal_check(hal_rpc_hash_finalize(session->digest_handle, pDigest, *pulDigestLen)))
lose(CKR_FUNCTION_FAILED);
rv = CKR_OK; /* Fall through */
fail:
if (session != NULL) {
digest_cleanup(&session->digest_handle);
session->digest_algorithm = hal_digest_algorithm_none;
}
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_DigestUpdate(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pPart,
CK_ULONG ulPartLen)
{
ENTER_PUBLIC_FUNCTION(C_DigestUpdate);
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 (pPart == NULL)
lose(CKR_ARGUMENTS_BAD);
if (session->digest_algorithm == hal_digest_algorithm_none)
lose(CKR_OPERATION_NOT_INITIALIZED);
if ((rv = digest_update(session, session->digest_algorithm,
&session->digest_handle, pPart, ulPartLen)) != CKR_OK)
goto fail;
return mutex_unlock(p11_global_mutex);
fail:
if (session != NULL) {
digest_cleanup(&session->digest_handle);
session->digest_algorithm = hal_digest_algorithm_none;
}
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_DigestFinal(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pDigest,
CK_ULONG_PTR pulDigestLen)
{
ENTER_PUBLIC_FUNCTION(C_DigestFinal);
p11_session_t *session;
size_t digest_len;
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 (pulDigestLen == NULL)
lose(CKR_ARGUMENTS_BAD);
if (session->digest_algorithm == hal_digest_algorithm_none || session->digest_handle.handle == HAL_HANDLE_NONE)
lose(CKR_OPERATION_NOT_INITIALIZED);
if (!hal_check(hal_rpc_hash_get_digest_length(session->digest_algorithm, &digest_len)))
lose(CKR_FUNCTION_FAILED);
rv = *pulDigestLen < digest_len ? CKR_BUFFER_TOO_SMALL : CKR_OK;
*pulDigestLen = digest_len;
if (pDigest == NULL)
return mutex_unlock(p11_global_mutex);
if (rv == CKR_BUFFER_TOO_SMALL)
lose(CKR_BUFFER_TOO_SMALL);
if (!hal_check(hal_rpc_hash_finalize(session->digest_handle, pDigest, *pulDigestLen)))
lose(CKR_FUNCTION_FAILED);
rv = CKR_OK; /* Fall through */
fail:
if (session != NULL) {
digest_cleanup(&session->digest_handle);
session->digest_algorithm = hal_digest_algorithm_none;
}
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_OBJECT_CLASS key_class;
CK_KEY_TYPE key_type;
CK_BBOOL key_sign;
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_algorithm != hal_digest_algorithm_none)
lose(CKR_OPERATION_ACTIVE);
if ((rv = p11_object_check_rights(session, hKey, p11_object_access_read)) != CKR_OK)
goto fail;
if (!p11_attribute_get_ulong(hKey, CKA_CLASS, &key_class) ||
!p11_attribute_get_ulong(hKey, CKA_KEY_TYPE, &key_type) ||
!p11_attribute_get_bbool(hKey, CKA_SIGN, &key_sign) ||
key_class != CKO_PRIVATE_KEY)
lose(CKR_KEY_HANDLE_INVALID);
if (!key_sign)
lose(CKR_KEY_FUNCTION_NOT_PERMITTED);
switch (pMechanism->mechanism) {
case CKM_RSA_PKCS:
case CKM_SHA1_RSA_PKCS:
case CKM_SHA256_RSA_PKCS:
case CKM_SHA384_RSA_PKCS:
case CKM_SHA512_RSA_PKCS:
if (key_type != CKK_RSA)
lose(CKR_KEY_TYPE_INCONSISTENT);
break;
case CKM_ECDSA:
case CKM_ECDSA_SHA256:
case CKM_ECDSA_SHA384:
case CKM_ECDSA_SHA512:
if (key_type != CKK_EC)
lose(CKR_KEY_TYPE_INCONSISTENT);
break;
default:
return CKR_MECHANISM_INVALID;
}
session->sign_key_handle = hKey;
switch (pMechanism->mechanism) {
case CKM_RSA_PKCS:
case CKM_ECDSA:
session->sign_digest_algorithm = hal_digest_algorithm_none;
break;
case CKM_SHA1_RSA_PKCS:
session->sign_digest_algorithm = hal_digest_algorithm_sha1;
break;
case CKM_SHA256_RSA_PKCS:
case CKM_ECDSA_SHA256:
session->sign_digest_algorithm = hal_digest_algorithm_sha256;
break;
case CKM_SHA384_RSA_PKCS:
case CKM_ECDSA_SHA384:
session->sign_digest_algorithm = hal_digest_algorithm_sha384;
break;
case CKM_SHA512_RSA_PKCS:
case CKM_ECDSA_SHA512:
session->sign_digest_algorithm = hal_digest_algorithm_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_algorithm = hal_digest_algorithm_none;
}
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);
p11_session_t *session;
CK_KEY_TYPE key_type;
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);
if (session->sign_digest_handle.handle != HAL_HANDLE_NONE)
lose(CKR_OPERATION_ACTIVE);
if (!p11_attribute_get_ulong(session->sign_key_handle, CKA_KEY_TYPE, &key_type))
lose(CKR_FUNCTION_FAILED);
if (session->sign_digest_algorithm != hal_digest_algorithm_none && pSignature != NULL) {
if ((rv = digest_update(session, session->sign_digest_algorithm,
&session->sign_digest_handle, pData, ulDataLen)) != CKR_OK)
goto fail;
pData = NULL;
ulDataLen = 0;
}
switch (key_type) {
case CKK_RSA:
case CKK_EC:
rv = sign_hal_rpc(session, pData, ulDataLen, pSignature, pulSignatureLen);
break;
default:
lose(CKR_FUNCTION_FAILED);
}
/* Fall through */
fail:
if (session != NULL && pSignature != NULL) {
session->sign_key_handle = CK_INVALID_HANDLE;
session->sign_digest_algorithm = hal_digest_algorithm_none;
digest_cleanup(&session->sign_digest_handle);
}
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_SignUpdate(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pPart,
CK_ULONG ulPartLen)
{
ENTER_PUBLIC_FUNCTION(C_SignUpdate);
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 (pPart == NULL)
lose(CKR_ARGUMENTS_BAD);
if (session->sign_key_handle == CK_INVALID_HANDLE)
lose(CKR_OPERATION_NOT_INITIALIZED);
if (session->sign_digest_algorithm == hal_digest_algorithm_none)
lose(CKR_FUNCTION_FAILED);
if ((rv = digest_update(session, session->sign_digest_algorithm,
&session->sign_digest_handle, pPart, ulPartLen)) != CKR_OK)
goto fail;
return mutex_unlock(p11_global_mutex);
fail:
if (session != NULL) {
session->sign_key_handle = CK_INVALID_HANDLE;
session->sign_digest_algorithm = hal_digest_algorithm_none;
digest_cleanup(&session->sign_digest_handle);
}
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_SignFinal(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pSignature,
CK_ULONG_PTR pulSignatureLen)
{
ENTER_PUBLIC_FUNCTION(C_SignFinal);
p11_session_t *session;
CK_KEY_TYPE key_type;
CK_RV rv;
mutex_lock_or_return_failure(p11_global_mutex);
if ((session = p11_session_find(hSession)) == NULL)
lose(CKR_SESSION_HANDLE_INVALID);
if (pulSignatureLen == NULL)
lose(CKR_ARGUMENTS_BAD);
if (session->sign_key_handle == CK_INVALID_HANDLE || session->sign_digest_handle.handle == HAL_HANDLE_NONE)
lose(CKR_OPERATION_NOT_INITIALIZED);
if (!p11_attribute_get_ulong(session->sign_key_handle, CKA_KEY_TYPE, &key_type))
lose(CKR_FUNCTION_FAILED);
switch (key_type) {
case CKK_RSA:
case CKK_EC:
rv = sign_hal_rpc(session, NULL, 0, pSignature, pulSignatureLen);
break;
default:
lose(CKR_FUNCTION_FAILED);
}
/* Fall through */
fail:
if (session != NULL && pSignature != NULL) {
session->sign_key_handle = CK_INVALID_HANDLE;
session->sign_digest_algorithm = hal_digest_algorithm_none;
digest_cleanup(&session->sign_digest_handle);
}
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_OBJECT_CLASS key_class;
CK_KEY_TYPE key_type;
CK_BBOOL key_verify;
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_algorithm != hal_digest_algorithm_none)
lose(CKR_OPERATION_ACTIVE);
if ((rv = p11_object_check_rights(session, hKey, p11_object_access_read)) != CKR_OK)
goto fail;
if (!p11_attribute_get_ulong(hKey, CKA_CLASS, &key_class) ||
!p11_attribute_get_ulong(hKey, CKA_KEY_TYPE, &key_type) ||
!p11_attribute_get_bbool(hKey, CKA_VERIFY, &key_verify) ||
key_class != CKO_PUBLIC_KEY)
lose(CKR_KEY_HANDLE_INVALID);
if (!key_verify)
lose(CKR_KEY_FUNCTION_NOT_PERMITTED);
switch (pMechanism->mechanism) {
case CKM_RSA_PKCS:
case CKM_SHA1_RSA_PKCS:
case CKM_SHA256_RSA_PKCS:
case CKM_SHA384_RSA_PKCS:
case CKM_SHA512_RSA_PKCS:
if (key_type != CKK_RSA)
lose(CKR_KEY_TYPE_INCONSISTENT);
break;
case CKM_ECDSA:
case CKM_ECDSA_SHA256:
case CKM_ECDSA_SHA384:
case CKM_ECDSA_SHA512:
if (key_type != CKK_EC)
lose(CKR_KEY_TYPE_INCONSISTENT);
break;
default:
return CKR_MECHANISM_INVALID;
}
session->verify_key_handle = hKey;
switch (pMechanism->mechanism) {
case CKM_RSA_PKCS:
case CKM_ECDSA:
session->verify_digest_algorithm = hal_digest_algorithm_none;
break;
case CKM_SHA1_RSA_PKCS:
session->verify_digest_algorithm = hal_digest_algorithm_sha1;
break;
case CKM_SHA256_RSA_PKCS:
case CKM_ECDSA_SHA256:
session->verify_digest_algorithm = hal_digest_algorithm_sha256;
break;
case CKM_SHA384_RSA_PKCS:
case CKM_ECDSA_SHA384:
session->verify_digest_algorithm = hal_digest_algorithm_sha384;
break;
case CKM_SHA512_RSA_PKCS:
case CKM_ECDSA_SHA512:
session->verify_digest_algorithm = hal_digest_algorithm_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_algorithm = hal_digest_algorithm_none;
}
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);
p11_session_t *session;
CK_KEY_TYPE key_type;
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 || pSignature == NULL)
lose(CKR_ARGUMENTS_BAD);
if (session->verify_key_handle == CK_INVALID_HANDLE)
lose(CKR_OPERATION_NOT_INITIALIZED);
if (session->verify_digest_algorithm != hal_digest_algorithm_none) {
if ((rv = digest_update(session, session->verify_digest_algorithm,
&session->verify_digest_handle, pData, ulDataLen)) != CKR_OK)
goto fail;
pData = NULL;
ulDataLen = 0;
}
if (!p11_attribute_get_ulong(session->verify_key_handle, CKA_KEY_TYPE, &key_type))
lose(CKR_FUNCTION_FAILED);
switch (key_type) {
case CKK_RSA:
case CKK_EC:
rv = verify_hal_rpc(session, pData, ulDataLen, pSignature, ulSignatureLen);
break;
default:
lose(CKR_FUNCTION_FAILED);
}
fail: /* Fall through */
if (session != NULL) {
session->verify_key_handle = CK_INVALID_HANDLE;
session->verify_digest_algorithm = hal_digest_algorithm_none;
digest_cleanup(&session->verify_digest_handle);
}
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_VerifyUpdate(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pPart,
CK_ULONG ulPartLen)
{
ENTER_PUBLIC_FUNCTION(C_VerifyUpdate);
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 (pPart == NULL)
lose(CKR_ARGUMENTS_BAD);
if (session->verify_key_handle == CK_INVALID_HANDLE)
lose(CKR_OPERATION_NOT_INITIALIZED);
if (session->verify_digest_algorithm == hal_digest_algorithm_none)
lose(CKR_FUNCTION_FAILED);
if ((rv = digest_update(session, session->verify_digest_algorithm,
&session->verify_digest_handle, pPart, ulPartLen)) != CKR_OK)
goto fail;
return mutex_unlock(p11_global_mutex);
fail:
if (session != NULL) {
session->verify_key_handle = CK_INVALID_HANDLE;
session->verify_digest_algorithm = hal_digest_algorithm_none;
digest_cleanup(&session->verify_digest_handle);
}
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
CK_RV C_VerifyFinal(CK_SESSION_HANDLE hSession,
CK_BYTE_PTR pSignature,
CK_ULONG ulSignatureLen)
{
ENTER_PUBLIC_FUNCTION(C_VerifyFinal);
p11_session_t *session;
CK_KEY_TYPE key_type;
CK_RV rv;
mutex_lock_or_return_failure(p11_global_mutex);
if ((session = p11_session_find(hSession)) == NULL)
lose(CKR_SESSION_HANDLE_INVALID);
if (pSignature == NULL)
lose(CKR_ARGUMENTS_BAD);
if (session->verify_key_handle == CK_INVALID_HANDLE || session->verify_digest_handle.handle == HAL_HANDLE_NONE)
lose(CKR_OPERATION_NOT_INITIALIZED);
if (!p11_attribute_get_ulong(session->verify_key_handle, CKA_KEY_TYPE, &key_type))
lose(CKR_FUNCTION_FAILED);
switch (key_type) {
case CKK_RSA:
case CKK_EC:
rv = verify_hal_rpc(session, NULL, 0, pSignature, ulSignatureLen);
break;
default:
lose(CKR_FUNCTION_FAILED);
}
fail: /* Fall through */
if (session != NULL) {
session->verify_key_handle = CK_INVALID_HANDLE;
session->verify_digest_algorithm = hal_digest_algorithm_none;
digest_cleanup(&session->verify_digest_handle);
}
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(session, pMechanism, generate_keypair_rsa_pkcs,
pPublicKeyTemplate, ulPublicKeyAttributeCount, &p11_descriptor_rsa_public_key, phPublicKey,
pPrivateKeyTemplate, ulPrivateKeyAttributeCount, &p11_descriptor_rsa_private_key, phPrivateKey);
break;
case CKM_EC_KEY_PAIR_GEN:
rv = generate_keypair(session, pMechanism, generate_keypair_ec,
pPublicKeyTemplate, ulPublicKeyAttributeCount, &p11_descriptor_ec_public_key, phPublicKey,
pPrivateKeyTemplate, ulPrivateKeyAttributeCount, &p11_descriptor_ec_private_key, 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_rpc_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 that applications we care about are 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);
const CK_ULONG rsa_key_min = 1024;
const CK_ULONG rsa_key_max = 8192;
const CK_ULONG ec_key_min = 256;
const CK_ULONG ec_key_max = 521;
/*
* 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;
#if 0
/*
* Perhaps revisit this after adding an RPC call to let us check
* which cores are available. For now, given that we now have
* software core support for these hash algorithms, this test isn't
* particularly useful.
*/
hal_digest_algorithm_t algorithm = hal_digest_algorithm_none;
CK_RV rv = CKR_OK;
switch (type) {
case CKM_SHA_1:
case CKM_SHA1_RSA_PKCS:
case CKM_SHA_1_HMAC:
case CKM_ECDSA_SHA1:
algorithm = hal_digest_algorithm_sha1;
break;
case CKM_SHA256:
case CKM_SHA256_RSA_PKCS:
case CKM_SHA256_HMAC:
case CKM_ECDSA_SHA256:
algorithm = hal_digest_algorithm_sha256;
break;
case CKM_SHA384:
case CKM_SHA384_RSA_PKCS:
case CKM_SHA384_HMAC:
case CKM_ECDSA_SHA384:
algorithm = hal_digest_algorithm_sha384;
break;
case CKM_SHA512:
case CKM_SHA512_RSA_PKCS:
case CKM_SHA512_HMAC:
case CKM_ECDSA_SHA512:
algorithm = hal_digest_algorithm_sha512;
break;
default:
break;
}
if (algorithm != hal_digest_algorithm_none && (rv = digest_available(algorithm)) != CKR_OK)
return rv;
#endif
switch (type) {
case CKM_RSA_PKCS_KEY_PAIR_GEN:
pInfo->ulMinKeySize = rsa_key_min;
pInfo->ulMaxKeySize = rsa_key_max;
pInfo->flags = CKF_HW | CKF_GENERATE_KEY_PAIR;
break;
case CKM_EC_KEY_PAIR_GEN:
pInfo->ulMinKeySize = ec_key_min;
pInfo->ulMaxKeySize = ec_key_max;
pInfo->flags = CKF_HW | CKF_GENERATE_KEY_PAIR | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS;
break;
case CKM_RSA_PKCS:
case CKM_SHA1_RSA_PKCS:
case CKM_SHA256_RSA_PKCS:
case CKM_SHA384_RSA_PKCS:
case CKM_SHA512_RSA_PKCS:
pInfo->ulMinKeySize = rsa_key_min;
pInfo->ulMaxKeySize = rsa_key_max;
pInfo->flags = CKF_HW | CKF_SIGN | CKF_VERIFY;
break;
case CKM_ECDSA:
case CKM_ECDSA_SHA256:
case CKM_ECDSA_SHA384:
case CKM_ECDSA_SHA512:
pInfo->ulMinKeySize = ec_key_min;
pInfo->ulMaxKeySize = ec_key_max;
pInfo->flags = CKF_HW | CKF_SIGN | CKF_VERIFY | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS;
break;
case CKM_SHA_1:
case CKM_SHA256:
case CKM_SHA384:
case CKM_SHA512:
pInfo->ulMinKeySize = 0;
pInfo->ulMaxKeySize = 0;
pInfo->flags = CKF_HW | CKF_DIGEST;
break;
#if 0
/*
* We have Verilog and libhal for these, but no PKCS #11 support (yet).
*/
case CKM_SHA_1_HMAC:
case CKM_SHA256_HMAC:
case CKM_SHA384_HMAC:
case CKM_SHA512_HMAC:
#endif
default:
return CKR_MECHANISM_INVALID;
}
return CKR_OK;
}
CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession,
CK_SESSION_INFO_PTR pInfo)
{
ENTER_PUBLIC_FUNCTION(C_GetSessionInfo);
p11_session_t *session;
CK_RV rv = CKR_OK;
mutex_lock_or_return_failure(p11_global_mutex);
if (pInfo == NULL)
lose(CKR_ARGUMENTS_BAD);
if ((session = p11_session_find(hSession)) == NULL)
lose(CKR_SESSION_HANDLE_INVALID);
pInfo->slotID = P11_ONE_AND_ONLY_SLOT;
pInfo->state = session->state;
pInfo->flags = CKF_SERIAL_SESSION;
pInfo->ulDeviceError = 0;
switch (session->state) {
case CKS_RW_PUBLIC_SESSION:
case CKS_RW_SO_FUNCTIONS:
case CKS_RW_USER_FUNCTIONS:
pInfo->flags |= CKF_RW_SESSION;
default:
break;
}
fail:
mutex_unlock_return_with_rv(rv, p11_global_mutex);
}
/*
* 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_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_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_DigestKey(CK_SESSION_HANDLE hSession,
CK_OBJECT_HANDLE hKey)
{
ENTER_PUBLIC_FUNCTION(C_DigestKey);
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_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:
*/