diff options
Diffstat (limited to 'pkcs11.c')
-rw-r--r-- | pkcs11.c | 3802 |
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: + */ |