aboutsummaryrefslogtreecommitdiff
path: root/pkcs11.c
diff options
context:
space:
mode:
authorRob Austein <sra@hactrn.net>2015-04-28 15:29:12 -0400
committerRob Austein <sra@hactrn.net>2015-04-28 15:29:12 -0400
commit0c8d1d765783bbc09cc1ca63ffdd233f0ce31613 (patch)
tree65114ff0b424e0eb6aa8862c12c305bf26282fcb /pkcs11.c
First public commit of PKCS #11 implementation.
Diffstat (limited to 'pkcs11.c')
-rw-r--r--pkcs11.c3802
1 files changed, 3802 insertions, 0 deletions
diff --git a/pkcs11.c b/pkcs11.c
new file mode 100644
index 0000000..5b7576f
--- /dev/null
+++ b/pkcs11.c
@@ -0,0 +1,3802 @@
+/*
+ * pkcs11.c
+ * --------
+ *
+ * This is a partial implementation of PKCS #11 on top of Cryptlib on
+ * top of a HAL connecting to the Cryptech FPGA cores.
+ *
+ * This is still at a very early stage and should not (yet?) be used
+ * for any serious purpose. Among other things, it's not yet entirely
+ * clear whether this approach really is workable.
+ *
+ * Author: Rob Austein
+ * Copyright (c) 2015, SUNET
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <assert.h>
+
+#include <sqlite3.h>
+
+#include "cryptlib.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
+
+/*
+ * Placeholders for PIN length limits. Figure out real values later.
+ */
+
+#warning Figure out PIN length limits
+#define P11_MIN_PIN_LENGTH 16
+#define P11_MAX_PIN_LENGTH 4096
+
+/*
+ * Version numbers. Placeholders for now. Cryptlib has a version
+ * number, but from PKCS #11's point of view, Cryptlib is part of the
+ * "hardware", and we're probably going to need something other than
+ * Cryptlib's version number for the hardware, because we have to
+ * represent the version number of the attached Cryptech FPGA cores.
+ *
+ * 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
+
+/*
+ * A value that can't possibly be a valid Cryptlib handle.
+ */
+
+#ifndef CRYPT_HANDLE_NONE
+#define CRYPT_HANDLE_NONE (-1)
+#endif
+
+/*
+ * Whether to enable hardware (FPGA) support. This option may go away
+ * eventually, once we have enough algorithms implemented in Verilog.
+ */
+
+#ifndef ENABLE_CRYPTLIB_DEVICE
+#define ENABLE_CRYPTLIB_DEVICE 1
+#endif
+
+/*
+ * Whehter to enable software algorithms. This is not really an
+ * option at the moment, as the code won't run or even build properly
+ * if this is disabled. It's a placeholder to let us flag bits of
+ * code that probably should go away if and when we're doing all the
+ * crypto algorithms on the FPGA.
+ */
+
+#ifndef ENABLE_CRYPTLIB_SOFTWARE
+#define ENABLE_CRYPTLIB_SOFTWARE 1
+#endif
+
+#if !ENABLE_CRYPTLIB_SOFTWARE
+#error Code will not work correctly with software algorithm support disabled
+#endif
+
+/*
+ * 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
+
+/*
+ * Default name for PKCS #15 keyring. Can be overriden at runtime by
+ * setting PKCS11_KEYRING environment variable.
+ *
+ * In the long term this probably goes away, as all keys should live
+ * behind the Cryptlib hardware interface, but we need something for
+ * initial testing.
+ */
+
+#ifndef PKCS15_KEYRING
+#define PKCS15_KEYRING ".cryptech-pkcs11.p15"
+#endif
+
+
+
+/*
+ * PKCS #11 session.
+ */
+
+/*
+ * Cryptlib handles in the session structure are defined via a silly
+ * macro so that we can automate initialization and finalization
+ * without accidently missing any of the handles.
+ *
+ * Note that the encryption and decryption cases (other than raw
+ * encryption with no symmetric cipher algorithm) will need to use the
+ * enveloping API: see pp 61-62, 70-71, 190 of the Cryptlib manual.
+ * We may not really need to keep all the contexts around in this case
+ * once we've bound them into the envelope, drive off that bridge when
+ * we get to it.
+ *
+ * Syntax: One handle per line, as calls to to-be-defined macros
+ * SESSION_CRYPTLIB_CONTEXT() or SESSION_CRYPTLIB_ENVELOPE(), entries
+ * separated by semicolons, no semicolon after last entry.
+ */
+
+#define SESSION_CRYPTLIB_HANDLES \
+ SESSION_CRYPTLIB_CONTEXT(sign_key_context); \
+ SESSION_CRYPTLIB_CONTEXT(sign_digest_context); \
+ SESSION_CRYPTLIB_CONTEXT(verify_key_context); \
+ SESSION_CRYPTLIB_CONTEXT(verify_digest_context); \
+ SESSION_CRYPTLIB_CONTEXT(digest_context)
+
+#if 0
+ SESSION_CRYPTLIB_CONTEXT(encrypt_key_context);
+ SESSION_CRYPTLIB_CONTEXT(encrypt_cipher_context);
+ SESSION_CRYPTLIB_CONTEXT(decrypt_key_context);
+ SESSION_CRYPTLIB_CONTEXT(decrypt_cipher_context);
+ SESSION_CRYPTLIB_ENVELOPE(encrypt_envelope);
+ SESSION_CRYPTLIB_ENVELOPE(decrypt_envelope);
+#endif
+
+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 */
+
+#define SESSION_CRYPTLIB_CONTEXT(_ctx_) CRYPT_CONTEXT _ctx_
+#define SESSION_CRYPTLIB_ENVELOPE(_env_) CRYPT_ENVELOPE _env_
+ SESSION_CRYPTLIB_HANDLES;
+#undef SESSION_CRYPTLIB_ENVELOPE
+#undef SESSION_CRYPTLIB_CONTEXT
+
+} p11_session_t;
+
+/*
+ * PKCS #11 handle management. PKCS #11 has two kinds of handles:
+ * session handles and object handles. We subdivide object handles
+ * into token object handles (handles for objects that live on the
+ * token) and session object handles (handles for objects that live
+ * only as long as the session does), and we steal a bit of the object
+ * handle as a flag to distinguish between our two kinds of object
+ * handles, considerably simplifing the objected-related SQL code.
+ */
+
+typedef enum {
+ handle_flavor_session,
+ handle_flavor_token_object,
+ handle_flavor_session_object
+} handle_flavor_t;
+
+#define FLAG_HANDLE_TOKEN 0x80000000
+
+#define is_token_handle(_handle_) (((_handle_) & FLAG_HANDLE_TOKEN) != 0)
+
+
+
+/*
+ * Current logged-in user.
+ */
+
+static enum {
+ not_logged_in,
+ logged_in_as_user,
+ logged_in_as_so
+} logged_in_as = not_logged_in;
+
+/*
+ * PKCS #11 sessions for this application.
+ */
+
+static p11_session_t *p11_sessions;
+
+/*
+ * SQL database.
+ */
+
+static sqlite3 *sqldb = NULL;
+
+/*
+ * Saved copy of PIN (sigh).
+ *
+ * We'd like to do better than this, but as long as we're supporting
+ * software keysets which require a password every time we read or
+ * write a private key, we need this. Once we're dealing with just
+ * the hardware interface we should be able to skip this.
+ */
+
+#if ENABLE_CRYPTLIB_SOFTWARE
+static char *pin = NULL;
+#endif
+
+/*
+ * 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;
+
+/*
+ * Cryptlib handle for hardware device.
+ */
+
+#if ENABLE_CRYPTLIB_DEVICE
+static CRYPT_DEVICE cryptlib_device = CRYPT_HANDLE_NONE;
+#endif
+
+/*
+ * Filenames for SQL database and PKCS #15 keyring.
+ */
+
+static char *database_filename = NULL;
+static char *keyring_filename = NULL;
+
+
+
+/*
+ * Syntactic sugar for functions returning CK_RV complex enough to
+ * need cleanup actions on failure. Also does very basic logging
+ * for debug-by-printf().
+ */
+
+#define lose(_ck_rv_code_) \
+ do { \
+ rv = (_ck_rv_code_); \
+ fprintf(stderr, "%s:%u: %s\n", __FILE__, __LINE__, #_ck_rv_code_); \
+ goto fail; \
+ } while (0)
+
+/*
+ * Error checking for SQLite calls.
+ */
+
+#if DEBUG_SQL
+#define sql_whine(_expr_) \
+ (fprintf(stderr, "%s:%u: %s returned %s\n", \
+ __FILE__, __LINE__, #_expr_, sqlite3_errmsg(sqldb)), \
+ sql_breakpoint())
+#else
+#define sql_whine(_expr_) \
+ ((void) 0)
+#endif
+
+#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())
+
+
+
+/*
+ * Filename utilities.
+ */
+
+/*
+ * Construct name of configuration file if we don't already have it cached.
+ */
+
+static char *cf_generate(char **fn, /* Output filename */
+ const char * const env, /* Name of environment variable */
+ const char * const base) /* Filename in home directory */
+{
+ char *var;
+
+ assert(fn != NULL && env != NULL && base != NULL);
+
+ if (*fn != NULL)
+ return *fn;
+
+ if ((var = getenv(env)) != NULL && (*fn = malloc(strlen(var) + 1)) != NULL)
+ strcpy(*fn, var);
+
+ else if (var == NULL && (var = getenv("HOME")) != NULL && (*fn = malloc(strlen(var) + strlen(base) + 2)) != NULL)
+ sprintf(*fn, "%s/%s", var, base);
+
+ else if (var == NULL && (*fn = malloc(strlen(base) + 1)) != NULL)
+ strcpy(*fn, base);
+
+ return *fn;
+}
+
+/*
+ * Closures over cf_generate() for particular filenames.
+ */
+
+static char *cf_sql_database(void)
+{
+ return cf_generate(&database_filename, "PKCS11_DATABASE", SQL_DATABASE);
+}
+
+static char *cf_pkcs15_keyring(void)
+{
+ return cf_generate(&keyring_filename, "PKCS11_KEYRING", PKCS15_KEYRING);
+}
+
+
+
+/*
+ * Wrappers around some of Cryptlib's context functions, so that the
+ * rest of the code can mostly ignore whether a particular algorithm
+ * is implemented in hardware or not. In theory, we could achieve
+ * this simply by always trying cryptDeviceCreateContext() and
+ * checking its return code to see whether we should fall back to
+ * CryptCreateContext(), but for the moment I'm more comfortable with
+ * explictly coding the list of algorithms we expect to be supported
+ * here. This may change at some future date, once the HAL code is a
+ * little further along.
+ */
+
+static int cryptlib_implemented_in_hardware(const CRYPT_ALGO_TYPE algo)
+{
+#if ENABLE_CRYPTLIB_DEVICE
+ switch (algo) {
+ case CRYPT_ALGO_YOU_NEED_TO_SPECIFY_SOMETHING_HERE_BOZO:
+ return 1;
+ }
+#endif
+
+ return 0;
+}
+
+/*
+ * Create a context -- hardware if supported, software otherwise.
+ */
+
+static C_RET cryptlib_create_context(CRYPT_CONTEXT *ctx, const CRYPT_ALGO_TYPE algo)
+{
+#if ENABLE_CRYPTLIB_DEVICE
+ if (cryptlib_implemented_in_hardware(algo))
+ return cryptDeviceCreateContext(cryptlib_device, ctx, algo);
+#endif
+
+ return cryptCreateContext(ctx, CRYPT_UNUSED, algo);
+}
+
+/*
+ * Store a key. This is a no-op for hardware contexts (the hardware
+ * device functions as a key store), but requires writing to the PKCS
+ * #15 keyring for software contexts.
+ */
+
+static C_RET cryptlib_store_key(const CRYPT_CONTEXT ctx)
+{
+ CRYPT_KEYSET keyset;
+ int ret, algo;
+
+ if ((ret = cryptGetAttribute(ctx, CRYPT_CTXINFO_ALGO, &algo)) != CRYPT_OK)
+ return ret;
+
+ if (cryptlib_implemented_in_hardware(algo))
+ return CRYPT_OK;
+
+ ret = cryptKeysetOpen(&keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, cf_pkcs15_keyring(), CRYPT_KEYOPT_NONE);
+
+ if (ret == CRYPT_ERROR_OPEN || ret == CRYPT_ERROR_NOTFOUND)
+ ret = cryptKeysetOpen(&keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, cf_pkcs15_keyring(), CRYPT_KEYOPT_CREATE);
+
+ if (ret != CRYPT_OK)
+ return ret;
+
+ ret = cryptAddPrivateKey(keyset, ctx, pin);
+
+ cryptKeysetClose(keyset);
+
+ return ret;
+}
+
+/*
+ * Load a key. This creates a new context.
+ */
+
+static C_RET cryptlib_load_key(CRYPT_CONTEXT *ctx, const char *keyid)
+{
+ CRYPT_KEYSET keyset;
+ int ret;
+
+ assert(ctx != NULL);
+
+ *ctx = CRYPT_HANDLE_NONE;
+
+#if ENABLE_CRYPTLIB_DEVICE
+ if ((ret = cryptGetPrivateKey(cryptlib_device, ctx, CRYPT_KEYID_NAME, keyid, NULL)) == CRYPT_OK)
+ return ret;
+#endif
+
+ if ((ret = cryptKeysetOpen(&keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, cf_pkcs15_keyring(), CRYPT_KEYOPT_READONLY)) != CRYPT_OK)
+ return ret;
+
+ ret = cryptGetPrivateKey(keyset, ctx, CRYPT_KEYID_NAME, keyid, pin);
+
+ cryptKeysetClose(keyset);
+
+ return ret;
+}
+
+/*
+ * Delete a key.
+ */
+
+static C_RET cryptlib_delete_key(const char *keyid)
+{
+ CRYPT_KEYSET keyset;
+ int ret;
+
+#if ENABLE_CRYPTLIB_DEVICE
+ if ((ret = cryptDeleteKey(cryptlib_device, CRYPT_KEYID_NAME, keyid)) == CRYPT_OK)
+ return ret;
+#endif
+
+ if ((ret = cryptKeysetOpen(&keyset, CRYPT_UNUSED, CRYPT_KEYSET_FILE, cf_pkcs15_keyring(), CRYPT_KEYOPT_NONE)) != CRYPT_OK)
+ return ret;
+
+ ret = cryptDeleteKey(keyset, CRYPT_KEYID_NAME, keyid);
+
+ cryptKeysetClose(keyset);
+
+ return ret;
+}
+
+
+
+/*
+ * SQL utilities.
+ */
+
+/*
+ * Hook on which to hang a debugger breakpoint on SQL errors.
+ */
+
+#if DEBUG_SQL
+static void sql_breakpoint(void)
+{
+ fprintf(stderr, "[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, "[%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);
+
+ return sql_check_ok(sqlite3_open(cf_sql_database(), &sqldb)) && 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);
+}
+
+
+
+/*
+ * (Extremely) minimal ASN.1 parser, just good enough to pick a few
+ * fields out of something like a well-formed ASN.1 DER representation
+ * of a certificate.
+ */
+
+#define ASN1_UNIVERSAL 0x00
+#define ASN1_APPLICATION 0x40
+#define ASN1_CONTEXT_SPECIFIC 0x80
+#define ASN1_PRIVATE 0xC0
+
+#define ASN1_PRIMITIVE 0x00
+#define ASN1_CONSTRUCTED 0x20
+
+#define ASN1_TAG_MASK 0x1F
+
+#define ASN1_INTEGER (ASN1_PRIMITIVE | 0x02)
+#define ASN1_BIT_STRING (ASN1_PRIMITIVE | 0x03)
+#define ASN1_OCTET_STRING (ASN1_PRIMITIVE | 0x04)
+#define ASN1_NULL (ASN1_PRIMITIVE | 0x05)
+#define ASN1_OBJECT_IDENTIFIER (ASN1_PRIMITIVE | 0x06)
+#define ASN1_SEQUENCE (ASN1_CONSTRUCTED | 0x10)
+#define ASN1_SET (ASN1_CONSTRUCTED | 0x11)
+
+#define ASN1_EXPLICIT_CONTEXT (ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED)
+#define ASN1_EXPLICIT_0 (ASN1_EXPLICIT_CONTEXT + 0)
+
+/*
+ * Common setup code for asn1_dive() and asn1_skip().
+ *
+ * Check ASN.1 tag and various errors, decode length field.
+ * Outputs are length of header (tag + length) and value.
+ */
+
+static int asn1_prep(const unsigned char tag,
+ const unsigned char * const der,
+ const size_t len,
+ size_t *phlen,
+ size_t *pvlen)
+{
+ size_t i, hlen, vlen;
+
+ if (der == NULL || len < 2 || phlen == NULL || pvlen == NULL || der[0] != tag || der[1] > 0x84)
+ return 0;
+
+ if ((der[1] & 0x80) == 0) {
+ hlen = 2;
+ vlen = der[1];
+ }
+
+ else {
+ hlen = 2 + (der[1] & 0x7F);
+ vlen = 0;
+
+ if (hlen >= len)
+ return 0;
+
+ for (i = 2; i < hlen; i++)
+ vlen = (vlen << 8) + der[i];
+ }
+
+ if (hlen + vlen > len)
+ return 0;
+
+ *phlen = hlen;
+ *pvlen = vlen;
+ return 1;
+}
+
+/*
+ * Dive into an ASN.1 object.
+ *
+ * The special handling for BIT STRING is only appropriate for the
+ * intended use, where BIT STRING always encapsulates another ASN.1
+ * object like SubjectPublicKeyInfo and is thus always required to be
+ * a multiple of 8 bits in length. If we ever need to use this code
+ * to deal with real bit strings, the special handling will need to
+ * move to a separate function which we can call when appropriate.
+ */
+
+static int asn1_dive(const unsigned char tag,
+ const unsigned char **der,
+ size_t *len)
+{
+ size_t hlen, vlen;
+
+ if (der == NULL || len == NULL || !asn1_prep(tag, *der, *len, &hlen, &vlen))
+ return 0;
+
+ if (tag == ASN1_BIT_STRING) {
+ if (vlen == 0 || hlen >= *len || (*der)[hlen] != 0x00)
+ return 0;
+ hlen++, vlen--;
+ }
+
+ assert(hlen + vlen <= *len);
+ *der += hlen; /* Advance past the header */
+ *len = vlen; /* Shrink range to be just the content */
+ return 1;
+}
+
+/*
+ * Skip over an ASN.1 object.
+ */
+
+static int asn1_skip(const unsigned char tag,
+ const unsigned char **der,
+ size_t *len)
+{
+ size_t hlen, vlen;
+
+ if (der == NULL || len == NULL || !asn1_prep(tag, *der, *len, &hlen, &vlen))
+ return 0;
+
+ assert(hlen + vlen <= *len);
+ *der += hlen + vlen; /* Advance past entire object */
+ *len -= hlen + vlen; /* Reduce range by length of object */
+ return 1;
+}
+
+/*
+ * Grovel through a DER encoded X.509v3 certificate object until we
+ * find the subjectPublicKey field. See the ASN.1 in RFC 5280.
+ *
+ * This is much too simplistic for general use, but should suffice to
+ * pick the subjectPublicKey data out of a certificate generated for
+ * us by Cryptlib.
+ */
+
+static int asn1_find_x509_spki(const unsigned char **der, size_t *len)
+{
+ return (asn1_dive(ASN1_SEQUENCE, der, len) && /* Dive into certificate */
+ asn1_dive(ASN1_SEQUENCE, der, len) && /* Dive into tbsCertificate */
+ asn1_skip(ASN1_EXPLICIT_0, der, len) && /* Skip version */
+ asn1_skip(ASN1_INTEGER, der, len) && /* Skip serialNumber */
+ asn1_skip(ASN1_SEQUENCE, der, len) && /* Skip signature */
+ asn1_skip(ASN1_SEQUENCE, der, len) && /* Skip issuer */
+ asn1_skip(ASN1_SEQUENCE, der, len) && /* skip validity */
+ asn1_skip(ASN1_SEQUENCE, der, len) && /* Skip subject */
+ asn1_dive(ASN1_SEQUENCE, der, len) && /* Dive into subjectPublicKeyInfo */
+ asn1_skip(ASN1_SEQUENCE, der, len) && /* Skip algorithm */
+ asn1_dive(ASN1_BIT_STRING, der, len)); /* Dive into subjectPublicKey */
+}
+
+
+
+/*
+ * 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));
+}
+
+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));
+}
+
+static int p11_attribute_get_bbool(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, CK_BBOOL *value)
+{
+ CK_ULONG length;
+ return p11_attribute_get(object_handle, type, value, &length, sizeof(*value)) && length == sizeof(*value);
+}
+
+static int p11_attribute_get_ulong(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, CK_ULONG *value)
+{
+ CK_ULONG length;
+ return p11_attribute_get(object_handle, type, value, &length, sizeof(*value)) && length == sizeof(*value);
+}
+
+/*
+ * Find an attribute in a CK_ATTRIBUTE_PTR template. Returns index
+ * into template, or -1 if not found.
+ */
+
+static int p11_attribute_find_in_template(const CK_ATTRIBUTE_TYPE type,
+ const CK_ATTRIBUTE_PTR template,
+ const CK_ULONG length)
+{
+ int i;
+
+ if (template != NULL)
+ for (i = 0; i < length; i++)
+ if (template[i].type == type)
+ return i;
+
+ return -1;
+}
+
+/*
+ * Map a keyusage-related attribute to a keyusage bit flag.
+ *
+ * Assumes that calling code has already checked whether this
+ * attribute is legal for this object class, that attribute which
+ * should be CK_BBOOLs are of the correct length, etcetera.
+ *
+ * To handle all the possible permutations of specified and default
+ * values, it may be necessary to defer calling this method until
+ * after the default and mandatory values have been merged into the
+ * values supplied by the application-supplied template.
+ */
+
+static void p11_attribute_apply_keyusage(unsigned *keyusage, const CK_ATTRIBUTE_TYPE type, const CK_BBOOL *value)
+{
+ unsigned flag;
+
+ assert(keyusage != NULL && value != NULL);
+
+ switch (type) {
+ case CKA_SIGN: /* Generate signature */
+ case CKA_VERIFY: /* Verify signature */
+ flag = CRYPT_KEYUSAGE_DIGITALSIGNATURE;
+ break;
+ case CKA_ENCRYPT: /* Encrypt bulk data (seldom used) */
+ case CKA_DECRYPT: /* Bulk decryption (seldom used) */
+ flag = CRYPT_KEYUSAGE_DATAENCIPHERMENT;
+ break;
+ case CKA_WRAP: /* Wrap key (normal way of doing encryption) */
+ case CKA_UNWRAP: /* Unwrap key (normal way of doing decryption) */
+ flag = CRYPT_KEYUSAGE_KEYENCIPHERMENT;
+ break;
+ default:
+ return; /* Attribute not related to key usage */
+ }
+
+ if (*value)
+ *keyusage |= flag;
+ else
+ *keyusage &= ~flag;
+}
+
+
+
+/*
+ * Descriptor methods. Descriptors are generated at compile time by
+ * an auxiliary Python script, see attributes.* for details.
+ */
+
+/*
+ * Return the descriptor associated with a particular object class and
+ * key type.
+ */
+
+static const p11_descriptor_t *p11_descriptor_from_key_type(const CK_OBJECT_CLASS object_class,
+ const CK_KEY_TYPE key_type)
+{
+ int i;
+
+ for (i = 0; i < sizeof(p11_descriptor_keyclass_map)/sizeof(*p11_descriptor_keyclass_map); i++) {
+ const p11_descriptor_keyclass_map_t * const m = &p11_descriptor_keyclass_map[i];
+ if (m->object_class == object_class && m->key_type == key_type)
+ return m->descriptor;
+ }
+
+ return NULL;
+}
+
+/*
+ * Find the entry for a particular attribute in a descriptor.
+ */
+
+static const p11_attribute_descriptor_t *p11_find_attribute_in_descriptor(const p11_descriptor_t *descriptor,
+ const CK_ATTRIBUTE_TYPE type)
+{
+ int i;
+
+ if (descriptor != NULL && descriptor->attributes != NULL)
+ for (i = 0; i < descriptor->n_attributes; i++)
+ if (descriptor->attributes[i].type == type)
+ return &descriptor->attributes[i];
+
+ return NULL;
+}
+
+/*
+ * Check whether an attribute is marked as sensitive. If we don't
+ * recognize the attribute, report it as sensitive (safer than the
+ * alternative).
+ */
+
+static int p11_attribute_is_sensitive(const p11_descriptor_t *descriptor,
+ const CK_ATTRIBUTE_TYPE type)
+{
+ const p11_attribute_descriptor_t *a = p11_find_attribute_in_descriptor(descriptor, type);
+ return a == NULL || (a->flags & P11_DESCRIPTOR_SENSITIVE) != 0;
+}
+
+
+
+/*
+ * Object methods.
+ */
+
+/*
+ * Check access rights for an object.
+ */
+
+typedef enum { p11_object_access_read, p11_object_access_write } p11_object_access_t;
+
+static CK_RV p11_object_check_rights(const p11_session_t *session,
+ const CK_OBJECT_HANDLE object_handle,
+ const p11_object_access_t rights)
+{
+ static const char session_handle_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 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);
+ }
+
+ /*
+ * Session objects are only visible to the session which created them.
+ */
+
+ if (!is_token_handle(object_handle) &&
+ (!sql_check_ok(sql_prepare(&q, session_handle_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 select_format[] =
+ " WITH"
+ " private AS (SELECT session_object_id FROM session_attribute WHERE type = %u AND value <> X'00')"
+ " SELECT keyid FROM session_object WHERE keyid IS NOT NULL AND session_object_id IN private";
+
+ 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 ret, ok = 0;
+
+ if (!sql_check_ok(sql_prepare(&q, select_format, CKA_PRIVATE)))
+ goto fail;
+
+ while ((ret = sqlite3_step(q)) == SQLITE_ROW)
+ if (cryptlib_delete_key((const char *) sqlite3_column_text(q, 0)) != CRYPT_OK)
+ goto fail;
+
+ if (ret != SQLITE_DONE) {
+ sql_whine_step();
+ goto fail;
+ }
+
+ sqlite3_finalize(q);
+ q = NULL;
+
+ 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);
+
+ sqlite3_finalize(q);
+ q = NULL;
+
+ 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);
+ sqlite3_finalize(q);
+ q = NULL;
+ if (!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)))
+ goto fail;
+ sqlite3_finalize(q);
+ q = NULL;
+ if (!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);
+ sqlite3_finalize(q);
+ q = NULL;
+ if (!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)))
+ goto fail;
+ sqlite3_finalize(q);
+ q = NULL;
+ if (!sql_check_ok(sql_prepare(&q, insert_session_attribute)) ||
+ !sql_check_ok(sqlite3_bind_int64(q, 1, session_object_id)))
+ goto fail;
+ break;
+
+ default: /* Suppress GCC warning */
+ goto fail;
+ }
+
+ /*
+ * Now populate attributes, starting with the application's
+ * template, which we assume has already been blessed by the API
+ * function that called this method.
+ */
+
+ for (i = 0; i < template_length; i++) {
+ const CK_ATTRIBUTE_TYPE type = template[i].type;
+ const void * val = template[i].pValue;
+ const int len = template[i].ulValueLen;
+
+ if (!sql_check_ok(sqlite3_reset(q)) ||
+ !sql_check_ok(sqlite3_bind_int64(q, 2, type)) ||
+ !sql_check_ok(sqlite3_bind_blob( q, 3, val, len, NULL)) ||
+ !sql_check_done(sqlite3_step(q)))
+ goto fail;
+ }
+
+ /*
+ * Next, add defaults from the descriptor.
+ */
+
+ for (i = 0; i < descriptor->n_attributes; i++) {
+ const CK_ATTRIBUTE_TYPE type = descriptor->attributes[i].type;
+ const void * val = descriptor->attributes[i].value;
+ const int len = descriptor->attributes[i].length;
+ const unsigned flags = descriptor->attributes[i].flags;
+
+ if (val == NULL && (flags & P11_DESCRIPTOR_DEFAULT_VALUE) != 0)
+ val = "";
+
+ if (val == NULL || p11_attribute_find_in_template(type, template, template_length) >= 0)
+ continue;
+
+ if (!sql_check_ok(sqlite3_reset(q)) ||
+ !sql_check_ok(sqlite3_bind_int64(q, 2, type)) ||
+ !sql_check_ok(sqlite3_bind_blob( q, 3, val, len, NULL)) ||
+ !sql_check_done(sqlite3_step(q)))
+ goto fail;
+ }
+
+ /*
+ * Finally, add generation mechanism attributes as needed.
+ */
+
+ if (mechanism != NULL &&
+ (!sql_check_ok(sqlite3_reset(q)) ||
+ !sql_check_ok(sqlite3_bind_int64(q, 2, CKA_LOCAL)) ||
+ !sql_check_ok(sqlite3_bind_blob( q, 3, &const_CK_TRUE, sizeof(const_CK_TRUE), NULL)) ||
+ !sql_check_done(sqlite3_step(q)) ||
+ !sql_check_ok(sqlite3_reset(q)) ||
+ !sql_check_ok(sqlite3_bind_int64(q, 2, CKA_KEY_GEN_MECHANISM)) ||
+ !sql_check_ok(sqlite3_bind_blob( q, 3, &mechanism->mechanism, sizeof(mechanism->mechanism), NULL)) ||
+ !sql_check_done(sqlite3_step(q))))
+ goto fail;
+
+ /*
+ * If we made it past all that, we're happy.
+ */
+
+ ok = 1;
+
+ fail:
+ sqlite3_finalize(q);
+ return ok ? object_handle : CK_INVALID_HANDLE;
+}
+
+/*
+ * Get the keyid for an object.
+ *
+ * This may require calculating the keyid from the CKA_ID attribute.
+ */
+
+static int p11_object_get_keyid(const CK_OBJECT_HANDLE object_handle,
+ char *keyid,
+ const size_t maxkeyid)
+{
+ static const char select_format[] =
+ " SELECT keyid FROM %s_object NATURAL JOIN object WHERE object_handle = ?";
+
+ static const char update_format[] =
+ " UPDATE %s_object SET keyid = ?1"
+ " WHERE %s_object_id = (SELECT %s_object_id FROM object WHERE object_handle =?2)";
+
+ const char *flavor = is_token_handle(object_handle) ? "token" : "session";
+
+ sqlite3_stmt *q = NULL;
+ int ok = 0;
+
+ 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)))
+ goto fail;
+
+ if (sqlite3_column_type(q, 0) == SQLITE_NULL) {
+
+ /*
+ * No keyid set yet, have to create one. We use the CKA_ID
+ * attribute for this, zero-filling or truncating as necessary.
+ */
+
+ const CK_ULONG target_length = (CRYPT_MAX_TEXTSIZE < maxkeyid ? CRYPT_MAX_TEXTSIZE : (maxkeyid - 1)) / 2;
+ unsigned char id[CRYPT_MAX_HASHSIZE];
+ CK_ULONG len;
+ int i;
+
+ assert(target_length > 0 && target_length <= sizeof(id) && target_length * 2 < maxkeyid);
+
+ if (!p11_attribute_get(object_handle, CKA_ID, id, &len, sizeof(id)))
+ goto fail;
+
+ if (len < target_length) {
+ memmove(id + target_length - len, id, len);
+ memset(id, 0x00, target_length - len);
+ }
+
+ for (i = 0; i < target_length; i++)
+ sprintf(keyid + (2 * i), "%02x", id[i]);
+ keyid[target_length * 2] = '\0';
+
+ sqlite3_finalize(q);
+ q = NULL;
+
+ if (!sql_check_ok(sql_prepare(&q, update_format, flavor, flavor, flavor)) ||
+ !sql_check_ok(sqlite3_bind_text( q, 1, keyid, strlen(keyid), NULL)) ||
+ !sql_check_ok(sqlite3_bind_int64(q, 2, object_handle)) ||
+ !sql_check_done(sqlite3_step(q)))
+ goto fail;
+
+ } else {
+
+ /*
+ * Already had a keyid, just have to copy it.
+ */
+
+ int len = sqlite3_column_bytes(q, 0);
+
+ if (len >= maxkeyid)
+ goto fail;
+
+ memcpy(keyid, sqlite3_column_text(q, 0), len);
+ keyid[len] = '\0';
+
+ }
+
+ ok = 1;
+
+ fail:
+ sqlite3_finalize(q);
+ return ok;
+}
+
+/*
+ * Add attributes representing the SPKI value of a key we've
+ * generated.
+ *
+ * Cryptlib does such a complete job of protecting our keys that it's
+ * rather tedious to extract the raw subjectPublicKeyInfo, but the
+ * PKCS #11 client needs that information, so we have to jump through
+ * some silly hoops. This routine does most of the work, but uses a
+ * separate handler (supplied as an argument) to generate attributes
+ * based on mechanism-specific data from the subjectPublicKey.
+ *
+ * Basic approach here is to generate a temporary certificate from the
+ * key, export that as DER, parse the DER for the data we need, and
+ * destroy the temporary certificate.
+ */
+
+static int p11_object_add_spki(const CK_OBJECT_HANDLE public_handle,
+ const CK_OBJECT_HANDLE private_handle,
+ const CRYPT_CONTEXT key,
+ int (*handler)(const CK_OBJECT_HANDLE,
+ const CK_OBJECT_HANDLE,
+ const unsigned char *,
+ const size_t))
+{
+ static const char label[] = "Don't care";
+ CRYPT_CERTIFICATE cert = CRYPT_HANDLE_NONE;
+ unsigned char *buffer = NULL;
+ const unsigned char *der;
+ int ilen, ok = 0;
+ size_t ulen;
+
+ if (handler == NULL ||
+ cryptCreateCert(&cert, CRYPT_UNUSED, CRYPT_CERTTYPE_CERTIFICATE) != CRYPT_OK ||
+ cryptSetAttribute(cert, CRYPT_CERTINFO_SUBJECTPUBLICKEYINFO, key) != CRYPT_OK ||
+ cryptSetAttribute(cert, CRYPT_CERTINFO_XYZZY, 1) != CRYPT_OK ||
+ cryptSetAttributeString(cert, CRYPT_CERTINFO_COMMONNAME, label, sizeof(label)) != CRYPT_OK ||
+ cryptSignCert(cert, key) != CRYPT_OK ||
+ cryptExportCert(NULL, 0, &ilen, CRYPT_CERTFORMAT_CERTIFICATE, cert) != CRYPT_OK ||
+ (der = buffer = malloc(ulen = (size_t) ilen)) == NULL ||
+ cryptExportCert(buffer, ilen, &ilen, CRYPT_CERTFORMAT_CERTIFICATE, cert) != CRYPT_OK ||
+ !asn1_find_x509_spki(&der, &ulen) ||
+ !handler(public_handle, private_handle, der, ulen))
+ goto fail;
+
+ ok = 1;
+
+ fail:
+ if (buffer != NULL)
+ free(buffer);
+ if (cert != CRYPT_HANDLE_NONE)
+ cryptDestroyCert(cert);
+ return ok;
+}
+
+/*
+ * RSA-specific handler to go with p11_object_add_spki().
+ *
+ * Extract RSA modulus and public exponent from the subjectPublicKey
+ * and adds the appropriate attributes to the public and private keys.
+ */
+
+static int p11_object_add_spki_rsa(const CK_OBJECT_HANDLE public_handle,
+ const CK_OBJECT_HANDLE private_handle,
+ const unsigned char *der,
+ const size_t len)
+{
+ const unsigned char *modulus = der, *publicExponent = der;
+ size_t modulus_len = len, publicExponent_len = len;
+
+ /*
+ * Dig the relevant integers out of the ASN.1.
+ */
+ if (!asn1_dive(ASN1_SEQUENCE, &modulus, &modulus_len) ||
+ !asn1_dive(ASN1_INTEGER, &modulus, &modulus_len) ||
+ !asn1_dive(ASN1_SEQUENCE, &publicExponent, &publicExponent_len) ||
+ !asn1_skip(ASN1_INTEGER, &publicExponent, &publicExponent_len) ||
+ !asn1_dive(ASN1_INTEGER, &publicExponent, &publicExponent_len))
+ return 0;
+
+ /*
+ * ASN.1 INTEGERs are signed while PKCS #11 "big integers" are
+ * unsigned, so skip leading zero byte, if present.
+ */
+
+ if (modulus_len > 0 && *modulus == 0x00)
+ modulus_len--, modulus++;
+
+ if (publicExponent_len > 0 && *publicExponent == 0x00)
+ publicExponent_len--, publicExponent++;
+
+ /*
+ * Insert the attributes and we're done.
+ */
+
+ return (p11_attribute_set(public_handle, CKA_MODULUS, modulus, modulus_len) &&
+ p11_attribute_set(public_handle, CKA_PUBLIC_EXPONENT, publicExponent, publicExponent_len) &&
+ p11_attribute_set(private_handle, CKA_MODULUS, modulus, modulus_len) &&
+ p11_attribute_set(private_handle, CKA_PUBLIC_EXPONENT, publicExponent, publicExponent_len));
+}
+
+
+
+/*
+ * 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));
+
+#define SESSION_CRYPTLIB_CONTEXT(_ctx_) session->_ctx_ = CRYPT_HANDLE_NONE
+#define SESSION_CRYPTLIB_ENVELOPE(_env_) session->_env_ = CRYPT_HANDLE_NONE
+ SESSION_CRYPTLIB_HANDLES;
+#undef SESSION_CRYPTLIB_ENVELOPE
+#undef SESSION_CRYPTLIB_CONTEXT
+
+ return session;
+}
+
+/*
+ * Free a session.
+ */
+
+static void p11_session_free(p11_session_t *session)
+{
+ if (session == NULL)
+ return;
+
+ if (session->find_query != NULL)
+ sqlite3_finalize(session->find_query);
+
+#define SESSION_CRYPTLIB_CONTEXT(_ctx_) if (session->_ctx_ != CRYPT_HANDLE_NONE) cryptDestroyContext(session->_ctx_)
+#define SESSION_CRYPTLIB_ENVELOPE(_env_) if (session->_env_ != CRYPT_HANDLE_NONE) cryptDestroyEnvelope(session->_env_)
+ SESSION_CRYPTLIB_HANDLES;
+#undef SESSION_CRYPTLIB_ENVELOPE
+#undef SESSION_CRYPTLIB_CONTEXT
+
+ 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.
+ *
+ * Since this destroys all associated session objects, we also have to
+ * delete any keys we might be holding for session objects.
+ *
+ * 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 select_keyid[] =
+ " SELECT keyid FROM session NATURAL JOIN session_object"
+ " WHERE session_handle = ?1 AND keyid IS NOT NULL";
+
+ 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;
+ int ret;
+
+ if (session == NULL)
+ return CKR_SESSION_HANDLE_INVALID;
+
+ if (!sql_check_ok(sql_prepare(&q, select_keyid)) ||
+ !sql_check_ok(sqlite3_bind_int64(q, 1, session_handle)))
+ lose(CKR_FUNCTION_FAILED);
+
+ while ((ret = sqlite3_step(q)) == SQLITE_ROW)
+ if (cryptlib_delete_key((const char *) sqlite3_column_text(q, 0)) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+
+ if (ret != SQLITE_DONE) {
+ sql_whine_step();
+ lose(CKR_FUNCTION_FAILED);
+ }
+
+ sqlite3_finalize(q);
+ q = NULL;
+
+ if (!sql_check_ok(sql_prepare(&q, delete_session)) ||
+ !sql_check_ok(sqlite3_bind_int64(q, 1, session_handle)) ||
+ !sql_check_done(sqlite3_step(q)))
+ lose(CKR_FUNCTION_FAILED);
+
+ /* Check that move-to-the-front behaved as expected */
+ assert(p11_sessions == session);
+
+ p11_sessions = session->link;
+ p11_session_free(session);
+
+ fail:
+ sqlite3_finalize(q);
+ return rv;
+}
+
+/*
+ * Delete all sessions.
+ *
+ * Like p11_session_delete(), this must also delete any keys held in
+ * session objects.
+ */
+
+static CK_RV p11_session_delete_all(void)
+{
+ static const char select_keys[] =
+ " SELECT keyid FROM session_object WHERE keyid IS NOT NULL";
+
+#warning Should this also clear the object table?
+
+ static const char delete_all_sessions[] =
+ " DELETE FROM session";
+
+ p11_session_t *session;
+ sqlite3_stmt *q = NULL;
+ int ret = SQLITE_OK;
+ CK_RV rv = CKR_OK;
+
+ if (!sql_check_ok(sql_prepare(&q, select_keys)))
+ lose(CKR_FUNCTION_FAILED);
+
+ while ((ret = sqlite3_step(q)) == SQLITE_ROW)
+ if (cryptlib_delete_key((const char *) sqlite3_column_text(q, 0)) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+
+ if (ret != SQLITE_DONE) {
+ sql_whine_step();
+ lose(CKR_FUNCTION_FAILED);
+ }
+
+ sqlite3_finalize(q);
+ q = NULL;
+
+ if (!sql_exec(delete_all_sessions))
+ lose(CKR_FUNCTION_FAILED);
+
+ while (p11_sessions != NULL) {
+ session = p11_sessions;
+ p11_sessions = session->link;
+ p11_session_free(session);
+ }
+
+ fail:
+ sqlite3_finalize(q);
+ return rv;
+}
+
+/*
+ * Check session database against login state for consistency.
+ *
+ * This is mostly useful in assertions.
+ */
+
+static int p11_session_consistent_login(void)
+{
+ p11_session_t *session;
+
+ switch (logged_in_as) {
+
+ case not_logged_in:
+ for (session = p11_sessions; session != NULL; session = session->link)
+ if (session->state != CKS_RO_PUBLIC_SESSION && session->state != CKS_RW_PUBLIC_SESSION)
+ return 0;
+ return 1;
+
+ case logged_in_as_user:
+ for (session = p11_sessions; session != NULL; session = session->link)
+ if (session->state != CKS_RO_USER_FUNCTIONS && session->state != CKS_RW_USER_FUNCTIONS)
+ return 0;
+ return 1;
+
+ case logged_in_as_so:
+ for (session = p11_sessions; session != NULL; session = session->link)
+ if (session->state != CKS_RW_SO_FUNCTIONS)
+ return 0;
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+
+/*
+ * PKCS #11 likes space-padded rather than null-terminated strings.
+ */
+
+static int psnprintf(void *buffer_, size_t size, const char *format, ...)
+{
+ char *buffer = buffer_;
+ size_t i, n;
+ va_list ap;
+
+ va_start(ap, format);
+ i = n = vsnprintf(buffer, size, format, ap);
+ va_end(ap);
+
+ while (i < size)
+ buffer[i++] = ' ';
+
+ return n;
+}
+
+
+
+/*
+ * Template checking and key generation.
+ *
+ * This may need refactoring at some point, eg, when we add support
+ * for C_CreateObject().
+ */
+
+/*
+ * First pass: called once per template entry during initial pass over
+ * template to handle generic checks that apply regardless of
+ * attribute type.
+ */
+
+static CK_RV p11_check_keypair_attributes_check_template_1(const CK_ATTRIBUTE_TYPE type,
+ const void * const val,
+ const size_t len,
+ const p11_descriptor_t * const descriptor)
+{
+ const p11_attribute_descriptor_t * const atd = p11_find_attribute_in_descriptor(descriptor, type);
+ CK_RV rv;
+
+ /* Attribute not allowed or not allowed for key generation */
+ if (atd == NULL || (atd->flags & P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE) != 0)
+ lose(CKR_ATTRIBUTE_TYPE_INVALID);
+
+ /* NULL or wrong-sized attribute values */
+ if (val == NULL || (atd->size != 0 && len != atd->size))
+ lose(CKR_ATTRIBUTE_VALUE_INVALID);
+
+ /* Attributes which only the SO user is allowed to set to CK_TRUE */
+ if ((atd->flags & P11_DESCRIPTOR_ONLY_SO_USER_CAN_SET) != 0 && logged_in_as != logged_in_as_so && *(CK_BBOOL *) val)
+ lose(CKR_ATTRIBUTE_VALUE_INVALID);
+
+ /* Attributes which don't match mandatory values */
+ if (atd->value != NULL && (atd->flags & P11_DESCRIPTOR_DEFAULT_VALUE) == 0 && memcmp(val, atd->value, atd->length) != 0)
+ lose(CKR_TEMPLATE_INCONSISTENT);
+
+ rv = CKR_OK;
+
+ fail:
+ return rv;
+}
+
+/*
+ * Second pass: called once per template to check that each attribute
+ * required for that template has been specified exactly once.
+ */
+
+static CK_RV p11_check_keypair_attributes_check_template_2(const p11_session_t *session,
+ const p11_descriptor_t * const descriptor,
+ const CK_ATTRIBUTE_PTR template,
+ const CK_ULONG template_length)
+{
+ const CK_BBOOL *object_is_private;
+ CK_RV rv;
+ int i, j;
+
+ /*
+ * Some session states aren't allowed to play with private objects.
+ */
+
+ switch (session->state) {
+ case CKS_RO_PUBLIC_SESSION:
+ case CKS_RW_PUBLIC_SESSION:
+ case CKS_RW_SO_FUNCTIONS:
+ if ((i = p11_attribute_find_in_template(CKA_PRIVATE, template, template_length)) >= 0) {
+ assert(template[i].pValue != NULL);
+ object_is_private = template[i].pValue;
+ }
+ else {
+ const p11_attribute_descriptor_t * const atd = p11_find_attribute_in_descriptor(descriptor, CKA_PRIVATE);
+ assert(atd != NULL && atd->value != NULL);
+ object_is_private = atd->value;
+ }
+ if (*object_is_private)
+ lose(CKR_TEMPLATE_INCONSISTENT);
+ }
+
+ for (i = 0; i < descriptor->n_attributes; i++) {
+ const p11_attribute_descriptor_t * const atd = &descriptor->attributes[i];
+ const int required_by_api = (atd->flags & P11_DESCRIPTOR_REQUIRED_BY_GENERATE) != 0;
+ const int forbidden_by_api = (atd->flags & P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE) != 0;
+ const int in_descriptor = (atd->flags & P11_DESCRIPTOR_DEFAULT_VALUE) != 0 || atd->value != NULL;
+ const int pos_in_template = p11_attribute_find_in_template(atd->type, template, template_length);
+
+ /* Multiple entries for same attribute */
+ if (pos_in_template >= 0)
+ for (j = pos_in_template + 1; j < template_length; j++)
+ if (template[j].type == atd->type)
+ lose(CKR_TEMPLATE_INCONSISTENT);
+
+ /* Required attribute missing from template */
+ if (!forbidden_by_api && (required_by_api || !in_descriptor) && pos_in_template < 0) {
+ fprintf(stderr, "[Missing attribute 0x%lx]\n", atd->type); /* XXX */
+ 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, same as
+ * Cryptlib. We reuse Cryptlib's bit flags because they're
+ * convenient.
+ *
+ * We use the PKCS #11 CKA_ID attribute to generate the Cryptlib key
+ * label. PKCS #11 suggests but does not require CKA_ID values for
+ * public and private key to match; we do insist on this, because we
+ * really only have one key label which applies to both the public and
+ * private keys.
+ */
+
+static CK_RV p11_check_keypair_attributes(const p11_session_t *session,
+ const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+ const CK_ULONG ulPublicKeyAttributeCount,
+ const p11_descriptor_t * const public_descriptor,
+ const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+ const CK_ULONG ulPrivateKeyAttributeCount,
+ const p11_descriptor_t * const private_descriptor)
+{
+ unsigned public_keyusage = 0, private_keyusage = 0;
+ const CK_BYTE *id = NULL;
+ size_t id_len = 0;
+ CK_RV rv = CKR_OK;
+ int i;
+
+ assert(session != NULL &&
+ pPublicKeyTemplate != NULL && public_descriptor != NULL &&
+ pPrivateKeyTemplate != NULL && private_descriptor != NULL);
+
+ /*
+ * Read-only sessions can't create keys, doh.
+ */
+
+ switch (session->state) {
+ case CKS_RO_PUBLIC_SESSION:
+ case CKS_RO_USER_FUNCTIONS:
+ lose(CKR_SESSION_READ_ONLY);
+ }
+
+ /*
+ * Check values provided in the public and private templates.
+ */
+
+ for (i = 0; i < ulPublicKeyAttributeCount; i++) {
+ const CK_ATTRIBUTE_TYPE type = pPublicKeyTemplate[i].type;
+ const void * const val = pPublicKeyTemplate[i].pValue;
+ const size_t len = pPublicKeyTemplate[i].ulValueLen;
+
+ if ((rv = p11_check_keypair_attributes_check_template_1(type, val, len, public_descriptor)) != CKR_OK)
+ goto fail;
+
+ p11_attribute_apply_keyusage(&public_keyusage, type, val);
+
+ if (type == CKA_ID) {
+ id = val;
+ id_len = len;
+ }
+ }
+
+ for (i = 0; i < ulPrivateKeyAttributeCount; i++) {
+ const CK_ATTRIBUTE_TYPE type = pPrivateKeyTemplate[i].type;
+ const void * const val = pPrivateKeyTemplate[i].pValue;
+ const size_t len = pPrivateKeyTemplate[i].ulValueLen;
+
+ if ((rv = p11_check_keypair_attributes_check_template_1(type, val, len, private_descriptor)) != CKR_OK)
+ goto fail;
+
+ p11_attribute_apply_keyusage(&private_keyusage, type, val);
+
+ if (type == CKA_ID && id == NULL) {
+ id = val;
+ id_len = len;
+ }
+
+ if (type == CKA_ID && (len != id_len || memcmp(id, val, len)))
+ lose(CKR_TEMPLATE_INCONSISTENT);
+ }
+
+ /*
+ * We insist that keyusage be specified for both public and private
+ * key, and that they match. May not need to be this strict.
+ */
+
+ if (public_keyusage != private_keyusage || public_keyusage == 0)
+ lose(CKR_TEMPLATE_INCONSISTENT);
+
+ /*
+ * We require a key ID.
+ */
+
+ if (id == NULL || id_len == 0)
+ lose(CKR_TEMPLATE_INCOMPLETE);
+
+ /*
+ * Check that all required attributes have been specified.
+ */
+
+ if ((rv = p11_check_keypair_attributes_check_template_2(session,
+ public_descriptor,
+ pPublicKeyTemplate,
+ ulPublicKeyAttributeCount)) != CKR_OK ||
+ (rv = p11_check_keypair_attributes_check_template_2(session,
+ private_descriptor,
+ pPrivateKeyTemplate,
+ ulPrivateKeyAttributeCount)) != CKR_OK)
+ goto fail;
+
+ /*
+ * If we get this far, we're happy. Maybe.
+ */
+
+ rv = CKR_OK;
+
+ fail:
+ return rv;
+}
+
+/*
+ * CKM_RSA_PKCS_KEY_PAIR_GEN key pair generation implemetation.
+ *
+ * Much mechanism-independent code has already been factored out of
+ * this function, no doubt much remains that will require further
+ * refactoring once we implement other mechanisms.
+ */
+
+static CK_RV generate_keypair_rsa_pkcs(p11_session_t *session,
+ const CK_MECHANISM_PTR pMechanism,
+ const CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+ const CK_ULONG ulPublicKeyAttributeCount,
+ const CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+ const CK_ULONG ulPrivateKeyAttributeCount,
+ CK_OBJECT_HANDLE_PTR phPublicKey,
+ CK_OBJECT_HANDLE_PTR phPrivateKey)
+{
+ CK_OBJECT_HANDLE private_handle = CK_INVALID_HANDLE;
+ CK_OBJECT_HANDLE public_handle = CK_INVALID_HANDLE;
+ handle_flavor_t public_handle_flavor = handle_flavor_session_object;
+ handle_flavor_t private_handle_flavor = handle_flavor_session_object;
+ char keyid[CRYPT_MAX_HASHSIZE * 2 + 1];
+ CRYPT_CONTEXT ctx = CRYPT_HANDLE_NONE;
+ const CK_BYTE *id = NULL;
+ CK_ULONG keysize = 0;
+ size_t id_len = 0;
+ CK_RV rv;
+ int i;
+
+ /*
+ * Do mechanism-independent checks before anything else.
+ */
+
+ rv = p11_check_keypair_attributes(session,
+ pPublicKeyTemplate, ulPublicKeyAttributeCount, &p11_descriptor_rsa_public_key,
+ pPrivateKeyTemplate, ulPrivateKeyAttributeCount, &p11_descriptor_rsa_private_key);
+ if (rv != CKR_OK)
+ return rv;
+
+ assert(session != NULL && pMechanism != NULL &&
+ pPublicKeyTemplate != NULL && phPublicKey != NULL &&
+ pPrivateKeyTemplate != NULL && phPrivateKey != NULL);
+
+ memset(keyid, 0, sizeof(keyid));
+
+ /*
+ * Grab values and perform mechanism-specific checks.
+ */
+
+ for (i = 0; i < ulPublicKeyAttributeCount; i++) {
+ const CK_ATTRIBUTE_TYPE type = pPublicKeyTemplate[i].type;
+ const void * const val = pPublicKeyTemplate[i].pValue;
+ const size_t len = pPublicKeyTemplate[i].ulValueLen;
+
+ assert(val != NULL);
+
+ switch (type) {
+
+ case CKA_TOKEN: /* Object stored on token */
+ public_handle_flavor = p11_handle_flavor_from_cka_token(val);
+ continue;
+
+ case CKA_ID: /* We use PKCS #11 "ID" as Cryptlib label */
+ id = val;
+ id_len = len;
+ continue;
+
+ case CKA_MODULUS_BITS: /* Keysize in bits -- Cryptlib only allows multiples of 8 */
+ keysize = *(CK_ULONG *) val;
+ if ((keysize & 7) != 0)
+ return CKR_ATTRIBUTE_VALUE_INVALID;
+ continue;
+
+ }
+ }
+
+ for (i = 0; i < ulPrivateKeyAttributeCount; i++) {
+ const CK_ATTRIBUTE_TYPE type = pPrivateKeyTemplate[i].type;
+ const void * const val = pPrivateKeyTemplate[i].pValue;
+ const size_t len = pPrivateKeyTemplate[i].ulValueLen;
+
+ assert (val != NULL);
+
+ switch (type) {
+
+ case CKA_TOKEN: /* Object stored on token */
+ private_handle_flavor = p11_handle_flavor_from_cka_token(val);
+ continue;
+
+ case CKA_ID: /* We use PKCS #11 "ID" as Cryptlib label */
+ id = val;
+ id_len = len;
+ continue;
+
+ }
+ }
+
+ /*
+ * We require a key ID and a key size, and if either key is a token
+ * object, the other must be too.
+ */
+ if (id == NULL || id_len == 0 || keysize == 0 || public_handle_flavor != private_handle_flavor)
+ return CKR_TEMPLATE_INCOMPLETE;
+
+ /*
+ * If we got this far, create the PKCS #11 objects.
+ */
+
+ if (!sql_exec("BEGIN"))
+ lose(CKR_FUNCTION_FAILED);
+
+ public_handle = p11_object_create(session, public_handle_flavor,
+ pPublicKeyTemplate, ulPublicKeyAttributeCount,
+ &p11_descriptor_rsa_public_key, pMechanism);
+
+ private_handle = p11_object_create(session, private_handle_flavor,
+ pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
+ &p11_descriptor_rsa_private_key, pMechanism);
+
+ if (public_handle == CK_INVALID_HANDLE || private_handle == CK_INVALID_HANDLE)
+ lose(CKR_FUNCTION_FAILED);
+
+ /*
+ * Generate the keypair.
+ */
+
+ if (!p11_object_get_keyid(private_handle, keyid, sizeof(keyid)) ||
+ cryptlib_create_context(&ctx, CRYPT_ALGO_RSA) != CRYPT_OK ||
+ cryptSetAttributeString(ctx, CRYPT_CTXINFO_LABEL, keyid, strlen(keyid)) != CRYPT_OK ||
+ cryptSetAttribute(ctx, CRYPT_CTXINFO_KEYSIZE, keysize / 8) != CRYPT_OK ||
+ cryptGenerateKey(ctx) != CRYPT_OK ||
+ !p11_object_add_spki(public_handle, private_handle, ctx, p11_object_add_spki_rsa) ||
+ cryptlib_store_key(ctx) != CRYPT_OK ||
+ cryptDestroyContext(ctx) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+
+ /*
+ * Commit the SQL transaction.
+ */
+
+ if (!sql_exec("COMMIT"))
+ lose(CKR_FUNCTION_FAILED);
+
+ /*
+ * All went well, return handles and we're done.
+ */
+ *phPublicKey = public_handle;
+ *phPrivateKey = private_handle;
+ return CKR_OK;
+
+ fail:
+
+ if (ctx != CRYPT_HANDLE_NONE)
+ cryptDestroyContext(ctx);
+
+ if (ctx != CRYPT_HANDLE_NONE && keyid[0] != 0x00)
+ (void) cryptlib_delete_key(keyid);
+
+ if (!sql_exec("ROLLBACK"))
+ rv = CKR_GENERAL_ERROR;
+
+ return rv;
+}
+
+
+
+/*
+ * PKCS #11 API functions.
+ */
+
+CK_RV C_Initialize(CK_VOID_PTR pInitArgs)
+{
+ int initialized_sql = 0, initialized_cryptlib = 0;
+ CK_C_INITIALIZE_ARGS_PTR a = pInitArgs;
+ CK_RV rv;
+
+ if (a != NULL) {
+ int functions_provided = ((a->CreateMutex != NULL) +
+ (a->DestroyMutex != NULL) +
+ (a->LockMutex != NULL) +
+ (a->UnlockMutex != NULL));
+
+ /*
+ * Reserved is, um, reserved.
+ * Threading parameters must either all be present or all be absent.
+ */
+
+ if (a->pReserved != NULL || (functions_provided & 3) != 0)
+ lose(CKR_ARGUMENTS_BAD);
+
+ /*
+ * At present we don't support threads or locking. This may be a
+ * problem for OpenDNSSEC. Need to figure out what the "obvious"
+ * system threading mechanism is supposed to be, or make it
+ * configurable, or something. For the moment, just return the
+ * correct error code to report that we're lame.
+ */
+
+#warning Thread support check disabled, this needs to be fixed
+#if 0
+ if (functions_provided || (a->flags & CKF_OS_LOCKING_OK) != 0)
+ lose(CKR_CANT_LOCK);
+#endif
+ }
+
+ /*
+ * Initialize SQLite3, opening the database(s) and loading the
+ * schema and views.
+ */
+
+ if (!sql_init())
+ lose(CKR_GENERAL_ERROR);
+
+ initialized_sql = 1;
+
+ /*
+ * Initialize cryptlib and open the hardware crypto device (our FPGA).
+ *
+ * The option settings are to make sure that internal stuff like the
+ * PKCS #15 keyset code uses algorithms we like.
+ */
+
+ if (cryptInit() != CRYPT_OK)
+ lose(CKR_GENERAL_ERROR);
+
+ initialized_cryptlib = 1;
+
+ if (cryptSetAttribute(CRYPT_UNUSED, CRYPT_OPTION_ENCR_ALGO, CRYPT_ALGO_AES) != CRYPT_OK ||
+ cryptSetAttribute(CRYPT_UNUSED, CRYPT_OPTION_ENCR_HASH, CRYPT_ALGO_SHA2) != CRYPT_OK)
+ lose(CKR_GENERAL_ERROR);
+
+#if ENABLE_CRYPTLIB_DEVICE
+ if (cryptDeviceOpen(&cryptlib_device, CRYPT_UNUSED, CRYPT_DEVICE_HARDWARE, NULL) != CRYPT_OK)
+ lose(CKR_GENERAL_ERROR);
+#endif
+
+ return CKR_OK;
+
+ fail:
+
+#if ENABLE_CRYPTLIB_DEVICE
+ if (cryptlib_device != CRYPT_HANDLE_NONE) {
+ cryptDeviceClose(cryptlib_device);
+ cryptlib_device = CRYPT_HANDLE_NONE;
+ }
+#endif
+
+ if (initialized_cryptlib)
+ cryptEnd();
+
+ if (initialized_sql)
+ sql_fini();
+
+ return rv;
+}
+
+CK_RV C_Finalize(CK_VOID_PTR pReserved)
+{
+ if (pReserved != NULL)
+ return CKR_ARGUMENTS_BAD;
+
+ /*
+ * Destroy all current sessions.
+ */
+
+ p11_session_delete_all();
+
+ /*
+ * Shut down SQLite3.
+ */
+
+ if (!sql_fini())
+ return CKR_GENERAL_ERROR;
+
+ /*
+ * Shut down hardware device and exit cryptlib. Is there any point
+ * in checking error codes here?
+ */
+
+#if ENABLE_CRYPTLIB_DEVICE
+ if (cryptlib_device != CRYPT_HANDLE_NONE)
+ cryptDeviceClose(cryptlib_device);
+ cryptlib_device = CRYPT_HANDLE_NONE;
+#endif
+
+ cryptEnd();
+ return CKR_OK;
+}
+
+CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList)
+{
+ /*
+ * Use pkcs11f.h to build dispatch vector for C_GetFunctionList().
+ * This should be const, but that's not what PKCS #11 says, oh well.
+ */
+
+ 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)
+{
+ /*
+ * We only have one slot, and it's hardwired.
+ */
+
+ 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)
+{
+ 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.
+ * See cryptlib's CRYPT_DEVINFO_* attributes for some hints.
+ *
+ * pInfo->label is supposed to be set when the token is initialized.
+ * Not yet sure what that means in our context, but need something
+ * here or the libhsm test programs will bomb trying to find the
+ * right token, so hard-wire something for now.
+ */
+
+ psnprintf(pInfo->label, sizeof(pInfo->label),
+ "Cryptech Token");
+
+ psnprintf(pInfo->manufacturerID, sizeof(pInfo->manufacturerID),
+ "Cryptech Project");
+
+ psnprintf(pInfo->model, sizeof(pInfo->model),
+ "%04x%04x%04x%04x",
+ P11_VERSION_HW_MAJOR, P11_VERSION_HW_MINOR,
+ P11_VERSION_SW_MAJOR, P11_VERSION_SW_MINOR);
+
+ psnprintf(pInfo->serialNumber, sizeof(pInfo->serialNumber),
+ "007");
+
+ pInfo->flags = CKF_RNG | CKF_LOGIN_REQUIRED;
+
+#warning Have not yet sorted out token flags
+#if 0
+ CKF_RNG
+ CKF_WRITE_PROTECTED
+ CKF_LOGIN_REQUIRED
+ CKF_USER_PIN_INITIALIZED
+ CKF_RESTORE_KEY_NOT_NEEDED
+ CKF_CLOCK_ON_TOKEN
+ CKF_PROTECTED_AUTHENTICATION_PATH
+ CKF_DUAL_CRYPTO_OPERATIONS
+ CKF_TOKEN_INITIALIZED
+ CKF_SECONDARY_AUTHENTICATION
+ CKF_USER_PIN_COUNT_LOW
+ CKF_USER_PIN_FINAL_TRY
+ CKF_USER_PIN_LOCKED
+ CKF_USER_PIN_TO_BE_CHANGED
+ CKF_SO_PIN_COUNT_LOW
+ CKF_SO_PIN_FINAL_TRY
+ CKF_SO_PIN_LOCKED
+ CKF_SO_PIN_TO_BE_CHANGED
+ CKF_ERROR_STATE
+#endif
+
+#warning Much of the TOKEN_INFO we return is nonsense
+ pInfo->ulMaxSessionCount = CK_EFFECTIVELY_INFINITE;
+ pInfo->ulSessionCount = CK_UNAVAILABLE_INFORMATION;
+ pInfo->ulMaxRwSessionCount = CK_EFFECTIVELY_INFINITE;
+ pInfo->ulRwSessionCount = CK_UNAVAILABLE_INFORMATION;
+ pInfo->ulMaxPinLen = P11_MAX_PIN_LENGTH;
+ pInfo->ulMinPinLen = P11_MIN_PIN_LENGTH;
+ pInfo->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION;
+ pInfo->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION;
+ pInfo->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION;
+ pInfo->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION;
+ pInfo->hardwareVersion.major = P11_VERSION_HW_MAJOR;
+ pInfo->hardwareVersion.minor = P11_VERSION_HW_MINOR;
+ pInfo->firmwareVersion.major = P11_VERSION_SW_MAJOR;
+ pInfo->firmwareVersion.minor = P11_VERSION_SW_MINOR;
+
+#warning Need to sort out hardware clock
+#if 0
+ /*
+ * Eventually we expect cryptech devices to have their own hardware
+ * clocks. Not implemented yet.
+ */
+ pInfo->utcTime;
+#endif
+
+ return CKR_OK;
+}
+
+CK_RV C_OpenSession(CK_SLOT_ID slotID,
+ CK_FLAGS flags,
+ CK_VOID_PTR pApplication,
+ CK_NOTIFY Notify,
+ CK_SESSION_HANDLE_PTR phSession)
+{
+ 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;
+
+ 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());
+
+ *phSession = session->handle;
+ return CKR_OK;
+
+ fail:
+ p11_session_free(session);
+ return rv;
+}
+
+CK_RV C_CloseSession(CK_SESSION_HANDLE hSession)
+{
+ return p11_session_delete(hSession);
+}
+
+CK_RV C_CloseAllSessions(CK_SLOT_ID slotID)
+{
+ if (slotID != P11_ONE_AND_ONLY_SLOT)
+ return CKR_SLOT_ID_INVALID;
+
+ p11_session_delete_all();
+
+ return CKR_OK;
+}
+
+CK_RV C_Login(CK_SESSION_HANDLE hSession,
+ CK_USER_TYPE userType,
+ CK_UTF8CHAR_PTR pPin,
+ CK_ULONG ulPinLen)
+{
+ p11_session_t *session;
+ int crypt_cmd;
+
+ if (pPin == NULL)
+ return 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)
+ return CKR_SESSION_HANDLE_INVALID;
+
+ /*
+ * This is where the combination of pure-software and hardware
+ * starts to get confusing. See the CRYPT_DEVINFO_* attributes for
+ * the operations we can do during device setup (setting PINs,
+ * logging in using pins, etc).
+ *
+ * All fine, but behaves somewhat differently from the case where
+ * we're doing everything in software and using the PIN primarily as
+ * the encryption password for the PKCS #15 keyring.
+ *
+ * In the long run just want the hardware interface. This will
+ * require cleanup.
+ */
+
+ /*
+ * 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)
+ return CKR_USER_ALREADY_LOGGED_IN;
+
+ /*
+ * We don't (yet?) support CKU_CONTEXT_SPECIFIC.
+ */
+
+ switch (userType) {
+ case CKU_USER: crypt_cmd = CRYPT_DEVINFO_AUTHENT_USER; break;
+ case CKU_SO: crypt_cmd = CRYPT_DEVINFO_AUTHENT_SUPERVISOR; break;
+ case CKU_CONTEXT_SPECIFIC: return CKR_OPERATION_NOT_INITIALIZED;
+ default: return CKR_USER_TYPE_INVALID;
+ }
+
+ /*
+ * 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.
+ */
+
+ if (userType == CKU_SO)
+ for (session = p11_sessions; session != NULL; session = session->link)
+ if (session->state == CKS_RO_PUBLIC_SESSION)
+ return CKR_SESSION_READ_ONLY_EXISTS;
+
+ /*
+ * Ask Cryptlib to log us in. We may need to examine cryptlib error
+ * return more closely than this.
+ */
+
+#if ENABLE_CRYPTLIB_DEVICE
+ if (cryptSetAttributeString(cryptlib_device, crypt_cmd, pPin, ulPinLen) != CRYPT_OK)
+ return CKR_PIN_INCORRECT;
+#endif
+
+#if ENABLE_CRYPTLIB_SOFTWARE
+ {
+ char *newpin;
+ if (memchr(pPin, '\0', ulPinLen) != NULL)
+ return CKR_PIN_INCORRECT;
+ if ((newpin = malloc(ulPinLen + 1)) == NULL)
+ return CKR_HOST_MEMORY;
+ memcpy(newpin, pPin, ulPinLen);
+ newpin[ulPinLen] = '\0';
+ if (pin != NULL)
+ free(pin);
+ pin = newpin;
+ }
+#endif
+
+ /*
+ * Update global login state, then whack each session into 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());
+
+ return CKR_OK;
+}
+
+CK_RV C_Logout(CK_SESSION_HANDLE hSession)
+{
+ p11_session_t *session;
+ int crypt_cmd;
+
+ /*
+ * 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)
+ return CKR_SESSION_HANDLE_INVALID;
+
+ switch (logged_in_as) {
+ case logged_in_as_user: crypt_cmd = CRYPT_DEVINFO_AUTHENT_USER; break;
+ case logged_in_as_so: crypt_cmd = CRYPT_DEVINFO_AUTHENT_SUPERVISOR; break;
+ case not_logged_in: return CKR_USER_NOT_LOGGED_IN;
+ }
+
+ /*
+ * This is a bit problematic, because Cryptlib doesn't have a logout
+ * function per se. For lack of a better idea, construe logout as a
+ * new authentication attempt with an empty PIN. This is a little
+ * weird, but at least it's something we can use as a relatively
+ * clear signal to the HAL, and it's consistent with the way
+ * cryptlib does things like terminating digest inputs.
+ */
+
+#if ENABLE_CRYPTLIB_DEVICE
+ if (cryptSetAttributeString(cryptlib_device, crypt_cmd, "", 0) != CRYPT_OK)
+ return CKR_FUNCTION_FAILED;
+#endif
+
+#if ENABLE_CRYPTLIB_SOFTWARE
+ if (pin != NULL)
+ free(pin);
+ pin = NULL;
+#endif
+
+ /*
+ * Update global login state, then delete any private objects and
+ * whack every existing session into the right state.
+ */
+
+ assert(p11_session_consistent_login());
+
+ logged_in_as = not_logged_in;
+
+ p11_object_delete_all_private();
+
+ for (session = p11_sessions; session != NULL; session = session->link) {
+ switch (session->state) {
+
+ case CKS_RO_USER_FUNCTIONS:
+ session->state = CKS_RO_PUBLIC_SESSION;
+ continue;
+
+ case CKS_RW_USER_FUNCTIONS:
+ case CKS_RW_SO_FUNCTIONS:
+ session->state = CKS_RW_PUBLIC_SESSION;
+ continue;
+
+ }
+ }
+
+ assert(p11_session_consistent_login());
+
+ return CKR_OK;
+}
+
+CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject)
+{
+ static const char select_format[] =
+ " SELECT %s_object_id, keyid FROM object NATURAL JOIN %s_object WHERE object_handle = ?1";
+
+ 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 = ?";
+
+ const char *flavor = is_token_handle(hObject) ? "token" : "session";
+
+ p11_session_t *session = p11_session_find(hSession);
+ sqlite3_stmt *q = NULL;
+ CK_RV rv = CKR_OK;
+ sqlite3_int64 id;
+
+ if ((rv = p11_object_check_rights(session, hObject, p11_object_access_write)) != CKR_OK)
+ goto fail;
+
+ if (!sql_check_ok(sql_prepare(&q, select_format, flavor, flavor)) ||
+ !sql_check_ok(sqlite3_bind_int64(q, 1, hObject)))
+ lose(CKR_FUNCTION_FAILED);
+
+ switch (sqlite3_step(q)) {
+ case SQLITE_ROW:
+ break;
+ case SQLITE_DONE:
+ lose(CKR_OBJECT_HANDLE_INVALID);
+ default:
+ sql_whine_step();
+ lose(CKR_FUNCTION_FAILED);
+ }
+
+ id = sqlite3_column_int64(q, 0);
+
+ if (sqlite3_column_type(q, 1) == SQLITE_TEXT &&
+ cryptlib_delete_key((const char *) sqlite3_column_text(q, 1)) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+
+ sqlite3_finalize(q);
+ q = NULL;
+
+ if (is_token_handle(hObject) &&
+ (!sql_check_ok(sql_prepare(&q, delete_token_object)) ||
+ !sql_check_ok(sqlite3_bind_int64(q, 1, id)) ||
+ !sql_check_done(sqlite3_step(q))))
+ lose(CKR_FUNCTION_FAILED);
+
+ sqlite3_finalize(q);
+ q = NULL;
+
+ 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);
+ return rv;
+}
+
+CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount)
+{
+ 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 = p11_session_find(hSession);
+ 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 = CKR_OK;
+ int ret, i;
+
+ if (pTemplate == NULL)
+ lose(CKR_ARGUMENTS_BAD);
+
+ 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);
+
+ 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);
+ return rv;
+}
+
+CK_RV C_FindObjectsInit(CK_SESSION_HANDLE hSession,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount)
+{
+ 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 = p11_session_find(hSession);
+ sqlite3_stmt *q1 = NULL, *q2 = NULL;
+ CK_RV rv = CKR_OK;
+ int i, ret;
+
+ if (session == 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);
+ }
+
+ sqlite3_finalize(q1);
+ sqlite3_finalize(q2);
+ q1 = q2 = NULL;
+
+ /*
+ * 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_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)))
+ lose(CKR_FUNCTION_FAILED);
+
+ sqlite3_finalize(q1);
+ sqlite3_finalize(q2);
+ q1 = q2 = NULL;
+
+ if (!sql_check_ok(sql_prepare(&q1, delete_format, hSession)))
+ lose(CKR_FUNCTION_FAILED);
+
+ /*
+ * We only see private objects when logged in as the regular user.
+ */
+
+ 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);
+ return rv;
+}
+
+CK_RV C_FindObjects(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE_PTR phObject,
+ CK_ULONG ulMaxObjectCount,
+ CK_ULONG_PTR pulObjectCount)
+{
+ p11_session_t *session = p11_session_find(hSession);
+ int i, ret = SQLITE_OK;
+ CK_RV rv = CKR_OK;
+
+ if (session == 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:
+ return rv;
+}
+
+CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE hSession)
+{
+ static const char drop_format[] =
+ " DROP TABLE IF EXISTS findobjects_%lu";
+
+ p11_session_t *session = p11_session_find(hSession);
+ sqlite3_stmt *q = NULL;
+ CK_RV rv = CKR_OK;
+
+ if (session == NULL)
+ lose(CKR_SESSION_HANDLE_INVALID);
+
+ if (session->find_query == NULL)
+ lose(CKR_OPERATION_NOT_INITIALIZED);
+
+ /*
+ * Clean up result query and temporary table.
+ */
+
+ sqlite3_finalize(session->find_query);
+ session->find_query = NULL;
+
+ if (!sql_check_ok(sql_prepare(&q, drop_format, hSession)) ||
+ !sql_check_done(sqlite3_step(q)))
+ lose(CKR_FUNCTION_FAILED);
+
+ fail:
+ sqlite3_finalize(q);
+ return rv;
+}
+
+CK_RV C_DigestInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism)
+{
+ p11_session_t *session = p11_session_find(hSession);
+ CRYPT_ALGO_TYPE algo;
+ unsigned hash_len;
+ CK_RV rv;
+
+ if (session == NULL)
+ return CKR_SESSION_HANDLE_INVALID;
+
+ if (pMechanism == NULL)
+ return CKR_ARGUMENTS_BAD;
+
+ if (session->digest_context != CRYPT_HANDLE_NONE)
+ return CKR_OPERATION_ACTIVE;
+
+ switch (pMechanism->mechanism) {
+ case CKM_SHA_1: algo = CRYPT_ALGO_SHA1; break;
+ case CKM_SHA256: algo = CRYPT_ALGO_SHA2; hash_len = 256; break;
+ case CKM_SHA384: algo = CRYPT_ALGO_SHA2; hash_len = 384; break;
+ case CKM_SHA512: algo = CRYPT_ALGO_SHA2; hash_len = 512; break;
+ default: return CKR_MECHANISM_INVALID;
+ }
+
+ assert((hash_len & 7) == 0);
+
+ if (cryptlib_create_context(&session->digest_context, algo) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+
+ if (algo == CRYPT_ALGO_SHA2 &&
+ cryptSetAttribute(session->digest_context, CRYPT_CTXINFO_BLOCKSIZE, hash_len >> 3) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+
+ return CKR_OK;
+
+ fail:
+ if (session->digest_context != CRYPT_HANDLE_NONE)
+ cryptDestroyContext(session->digest_context);
+ session->digest_context = CRYPT_HANDLE_NONE;
+ return rv;
+}
+
+CK_RV C_Digest(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pDigest,
+ CK_ULONG_PTR pulDigestLen)
+{
+ p11_session_t *session = p11_session_find(hSession);
+ CK_RV rv;
+ int len;
+
+ if (session == NULL)
+ return CKR_SESSION_HANDLE_INVALID;
+
+ if (pData == NULL || pulDigestLen == NULL)
+ return CKR_ARGUMENTS_BAD;
+
+ if (session->digest_context == CRYPT_HANDLE_NONE)
+ return CKR_OPERATION_NOT_INITIALIZED;
+
+ if (pDigest == NULL) {
+ if (cryptGetAttribute(session->digest_context, CRYPT_CTXINFO_BLOCKSIZE, &len) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+ *pulDigestLen = len;
+ return CKR_OK;
+ }
+
+ if (cryptEncrypt(session->digest_context, pData, ulDataLen) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+
+ if (ulDataLen != 0 &&
+ cryptEncrypt(session->digest_context, pData, 0) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+
+ if (cryptGetAttributeString(session->digest_context,
+ CRYPT_CTXINFO_HASHVALUE, NULL, &len) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+
+ if (len > *pulDigestLen)
+ lose(CKR_BUFFER_TOO_SMALL);
+
+ if (cryptGetAttributeString(session->digest_context,
+ CRYPT_CTXINFO_HASHVALUE, pDigest, &len) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+
+ *pulDigestLen = len;
+
+ rv = CKR_OK; /* Fall through */
+
+ fail:
+ cryptDestroyContext(session->digest_context);
+ session->digest_context = CRYPT_HANDLE_NONE;
+ return rv;
+}
+
+CK_RV C_SignInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ p11_session_t *session = p11_session_find(hSession);
+ char keyid[CRYPT_MAX_HASHSIZE * 2 + 1];
+ CRYPT_ALGO_TYPE sign_algo, hash_algo;
+ unsigned hash_size = 0;
+ int key_algo;
+ CK_RV rv;
+
+ if (session == NULL)
+ return CKR_SESSION_HANDLE_INVALID;
+
+ if (pMechanism == NULL)
+ return CKR_ARGUMENTS_BAD;
+
+ if (session->sign_key_context != CRYPT_HANDLE_NONE ||
+ session->sign_digest_context != CRYPT_HANDLE_NONE)
+ return CKR_OPERATION_ACTIVE;
+
+ if ((rv = p11_object_check_rights(session, hKey, p11_object_access_read)) != CKR_OK)
+ goto fail;
+
+ switch (pMechanism->mechanism) {
+ case CKM_RSA_PKCS: sign_algo = CRYPT_ALGO_RSA; hash_algo = CRYPT_ALGO_NONE; break;
+ case CKM_SHA1_RSA_PKCS: sign_algo = CRYPT_ALGO_RSA; hash_algo = CRYPT_ALGO_SHA1; break;
+ case CKM_SHA256_RSA_PKCS: sign_algo = CRYPT_ALGO_RSA; hash_algo = CRYPT_ALGO_SHA2; hash_size = 256; break;
+ case CKM_SHA384_RSA_PKCS: sign_algo = CRYPT_ALGO_RSA; hash_algo = CRYPT_ALGO_SHA2; hash_size = 384; break;
+ case CKM_SHA512_RSA_PKCS: sign_algo = CRYPT_ALGO_RSA; hash_algo = CRYPT_ALGO_SHA2; hash_size = 512; break;
+ default: return CKR_MECHANISM_INVALID;
+ }
+
+ assert((hash_size & 7) == 0);
+
+ if (!p11_object_get_keyid(hKey, keyid, sizeof(keyid)) ||
+ cryptlib_load_key(&session->sign_key_context, keyid) != CRYPT_OK ||
+ cryptGetAttribute(session->sign_key_context, CRYPT_CTXINFO_ALGO, &key_algo) != CRYPT_OK)
+ lose(CKR_KEY_HANDLE_INVALID);
+
+ if (sign_algo != key_algo)
+ lose(CKR_KEY_TYPE_INCONSISTENT);
+
+ if (hash_algo != CRYPT_ALGO_NONE &&
+ cryptlib_create_context(&session->sign_digest_context, hash_algo) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+
+ if (hash_algo == CRYPT_ALGO_SHA2 &&
+ cryptSetAttribute(session->sign_digest_context,
+ CRYPT_CTXINFO_BLOCKSIZE, hash_size >> 3) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+
+ return CKR_OK;
+
+ fail:
+ if (session->sign_key_context != CRYPT_HANDLE_NONE)
+ cryptDestroyContext(session->sign_key_context);
+ session->sign_key_context = CRYPT_HANDLE_NONE;
+
+ if (session->sign_digest_context != CRYPT_HANDLE_NONE)
+ cryptDestroyContext(session->sign_digest_context);
+ session->sign_digest_context = CRYPT_HANDLE_NONE;
+
+ return rv;
+}
+
+CK_RV C_Sign(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pulSignatureLen)
+{
+ p11_session_t *session = p11_session_find(hSession);
+ int len, algo;
+ CK_RV rv;
+
+ if (session == NULL)
+ return CKR_SESSION_HANDLE_INVALID;
+
+ if (pData == NULL || pulSignatureLen == NULL)
+ return CKR_ARGUMENTS_BAD;
+
+ if (session->sign_key_context == CRYPT_HANDLE_NONE)
+ return CKR_OPERATION_NOT_INITIALIZED;
+
+ if (pSignature == NULL) {
+ /*
+ * Caller just wants to know the signature length, which we can
+ * get from cryptCreateSignature(), using a dummy hash context if
+ * necessary.
+ *
+ * There may be an easier way: at least for RSA, reading the key's
+ * CRYPT_CTXINFO_KEYSIZE would give us the answer. But the
+ * constraint that messages_size == key_size doesn't necessarily
+ * hold for all asymmetric algorithms, so best to be safe here.
+ */
+
+ CRYPT_CONTEXT ctx = session->sign_digest_context;
+
+ if (ctx == CRYPT_HANDLE_NONE && cryptCreateContext(&ctx, CRYPT_UNUSED, CRYPT_ALGO_SHA2) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+
+ if (cryptCreateSignature(NULL, 0, &len, session->sign_key_context, ctx) != CRYPT_OK)
+ len = 0;
+
+ if (session->sign_digest_context == CRYPT_HANDLE_NONE)
+ cryptDestroyContext(ctx);
+
+ if (len == 0)
+ lose(CKR_FUNCTION_FAILED);
+ }
+
+ else if (session->sign_digest_context != CRYPT_HANDLE_NONE) {
+ /*
+ * Caller wanted a hash-and-sign operation, so we can use cryptCreateSignature().
+ */
+
+ if (cryptEncrypt(session->sign_digest_context, pData, ulDataLen) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+
+ if (ulDataLen != 0 &&
+ cryptEncrypt(session->sign_digest_context, pData, 0) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+
+ if (cryptCreateSignature(pSignature, *pulSignatureLen, &len,
+ session->sign_key_context,
+ session->sign_digest_context) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+ }
+
+ else {
+
+ /*
+ * Caller wanted a pure-signature operation, have to use
+ * cryptDeCrypt() [sic].
+ *
+ * At the moment we just blindly sign this without checking that
+ * what we're signing really is (eg) a valid DigestInfo SEQUENCE.
+ * Should we bother checking the syntax here, given that we have
+ * no way of checking the digest itself (if we get here, we've
+ * never seen the original plaintext, just the purported digest)?
+ */
+
+ if (cryptGetAttribute(session->sign_key_context, CRYPT_CTXINFO_ALGO, &algo) != CRYPT_OK ||
+ cryptGetAttribute(session->sign_key_context, CRYPT_CTXINFO_KEYSIZE, &len) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+
+ switch (algo) {
+
+ case CRYPT_ALGO_RSA:
+
+ /*
+ * Congregation will now please turn to RFC 2313 8.1 as we
+ * construct a PKCS #1.5 type 01 encryption block.
+ */
+
+ if (len > *pulSignatureLen)
+ lose(CKR_BUFFER_TOO_SMALL);
+
+ if (ulDataLen > len - 11)
+ return CKR_DATA_LEN_RANGE;
+
+ pSignature[0] = 0x00;
+ pSignature[1] = 0x01;
+ memset(pSignature + 2, 0xFF, len - 3 - ulDataLen);
+ pSignature[len - ulDataLen - 1] = 0x00;
+ memcpy(pSignature + len - ulDataLen, pData, ulDataLen);
+
+#if 0
+ /* XXX */
+ {
+ int i;
+ fprintf(stderr, "[PKCS #1.5 len %lu ulDataLen %lu block ", len, ulDataLen);
+ for (i = 0; i < len; i++)
+ fprintf(stderr, "%s%02x", i == 0 ? "" : ":", pSignature[i]);
+ fprintf(stderr, "]\n");
+ }
+#endif
+
+ break;
+
+ default:
+ lose(CKR_FUNCTION_FAILED);
+ }
+
+ /*
+ * The terms "encrypt" and "decrypt" get weird when one goes this
+ * far past the API that a sane person would be using. As
+ * explained in RFC 3447, the RSASP1 (signature generation)
+ * operation is the same mathematical operation as the RSADP
+ * (decryption) operation, so we have to use cryptDecrypt(), not
+ * cryptEncrypt() here. No, really.
+ *
+ * Well, this works for RSA, anyway. ECDSA may turn out to be a
+ * whole different bucket of monkey guts.
+ */
+
+ if (cryptDecrypt(session->sign_key_context, pSignature, len) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+ }
+
+ *pulSignatureLen = len;
+
+ rv = CKR_OK; /* Fall through */
+
+ fail:
+
+ if (session->sign_digest_context != CRYPT_HANDLE_NONE)
+ cryptDestroyContext(session->sign_digest_context);
+ session->sign_digest_context = CRYPT_HANDLE_NONE;
+
+ cryptDestroyContext(session->sign_key_context);
+ session->sign_key_context = CRYPT_HANDLE_NONE;
+
+ return rv;
+}
+
+/*
+ * libhsm only uses C_GenerateKey() for DSA parameter generation.
+ * More general use presumably wants this for things like generating
+ * symmetric keys for later wrapping by asymmetric keys.
+ */
+CK_RV C_GenerateKey(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phKey)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+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)
+{
+ p11_session_t *session = p11_session_find(hSession);
+
+ if (session == NULL)
+ return CKR_SESSION_HANDLE_INVALID;
+
+ if (pMechanism == NULL ||
+ pPublicKeyTemplate == NULL || phPublicKey == NULL ||
+ pPrivateKeyTemplate == NULL || phPrivateKey == NULL)
+ return CKR_ARGUMENTS_BAD;
+
+ switch (pMechanism->mechanism) {
+ case CKM_RSA_PKCS_KEY_PAIR_GEN:
+ return generate_keypair_rsa_pkcs(session, pMechanism,
+ pPublicKeyTemplate, ulPublicKeyAttributeCount,
+ pPrivateKeyTemplate, ulPrivateKeyAttributeCount,
+ phPublicKey, phPrivateKey);
+ default:
+ return CKR_MECHANISM_INVALID;
+ }
+}
+
+CK_RV C_GenerateRandom(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR RandomData,
+ CK_ULONG ulRandomLen)
+{
+ p11_session_t *session = p11_session_find(hSession);
+ CRYPT_CONTEXT ctx = CRYPT_HANDLE_NONE;
+ CK_RV rv = CKR_OK;
+
+ if (session == NULL)
+ return CKR_SESSION_HANDLE_INVALID;
+
+ if (RandomData == NULL)
+ return CKR_ARGUMENTS_BAD;
+
+ /*
+ * Cryptlib doesn't expose the raw TRNG, but, per the manual, block
+ * cipher encryption output with a randomly generated key is good
+ * enough for most sane purposes.
+ *
+ * Not certain why the Cryptlib manual suggests using CFB mode
+ * instead of OFB mode here, but going with the manual for now.
+ */
+
+ if (cryptCreateContext(&ctx, CRYPT_UNUSED, CRYPT_ALGO_AES) != CRYPT_OK ||
+ cryptSetAttribute(ctx, CRYPT_CTXINFO_MODE, CRYPT_MODE_CFB) != CRYPT_OK ||
+ cryptGenerateKey(ctx) != CRYPT_OK ||
+ cryptEncrypt(ctx, RandomData, ulRandomLen) != CRYPT_OK)
+ lose(CKR_FUNCTION_FAILED);
+
+ fail:
+ if (ctx != CRYPT_HANDLE_NONE)
+ (void) cryptDestroyContext(ctx);
+
+ return rv;
+}
+
+
+
+/*
+ * 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_GetInfo(CK_INFO_PTR pInfo)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_GetSlotInfo(CK_SLOT_ID slotID,
+ CK_SLOT_INFO_PTR pInfo)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_GetMechanismList(CK_SLOT_ID slotID,
+ CK_MECHANISM_TYPE_PTR pMechanismList,
+ CK_ULONG_PTR pulCount)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID,
+ CK_MECHANISM_TYPE type,
+ CK_MECHANISM_INFO_PTR pInfo)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_InitToken(CK_SLOT_ID slotID,
+ CK_UTF8CHAR_PTR pPin,
+ CK_ULONG ulPinLen,
+ CK_UTF8CHAR_PTR pLabel)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_InitPIN(CK_SESSION_HANDLE hSession,
+ CK_UTF8CHAR_PTR pPin,
+ CK_ULONG ulPinLen)
+{ 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)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession,
+ CK_SESSION_INFO_PTR pInfo)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_GetOperationState(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pOperationState,
+ CK_ULONG_PTR pulOperationStateLen)
+{ 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)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_CreateObject(CK_SESSION_HANDLE hSession,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phObject)
+{ 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)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_GetObjectSize(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,
+ CK_ULONG_PTR pulSize)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_SetAttributeValue(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_EncryptInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{ 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)
+{ 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)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_EncryptFinal(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pLastEncryptedPart,
+ CK_ULONG_PTR pulLastEncryptedPartLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_DecryptInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{ 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)
+{ 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)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_DecryptFinal(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pLastPart,
+ CK_ULONG_PTR pulLastPartLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_DigestUpdate(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_DigestKey(CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hKey)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_DigestFinal(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pDigest,
+ CK_ULONG_PTR pulDigestLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_SignUpdate(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_SignFinal(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pulSignatureLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_SignRecoverInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{ 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)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_VerifyInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey )
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_Verify(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG ulSignatureLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_VerifyUpdate(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_VerifyFinal(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG ulSignatureLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{ 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)
+{ 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)
+{ 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)
+{ 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)
+{ 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)
+{ 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)
+{ 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)
+{ 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)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_SeedRandom(CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSeed,
+ CK_ULONG ulSeedLen)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_GetFunctionStatus(CK_SESSION_HANDLE hSession)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_CancelFunction(CK_SESSION_HANDLE hSession)
+{ return CKR_FUNCTION_NOT_SUPPORTED; }
+
+CK_RV C_WaitForSlotEvent(CK_FLAGS flags,
+ CK_SLOT_ID_PTR pSlot,
+ CK_VOID_PTR pRserved)
+{ 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:
+ */