/* * pkcs11.c * -------- * * This is a partial implementation of PKCS #11 on top of the Cryptech * libhal library connecting to the Cryptech FPGA cores. * * Author: Rob Austein * Copyright (c) 2015-2016, NORDUnet A/S * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of the NORDUnet nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include /* * Magic PKCS #11 macros that must be defined before including * pkcs11.h. For now these are only the Unix versions, add others * later (which may require minor refactoring). */ #define CK_PTR * #define CK_DEFINE_FUNCTION(returnType, name) returnType name #define CK_DECLARE_FUNCTION(returnType, name) returnType name #define CK_DECLARE_FUNCTION_POINTER(returnType, name) returnType (* name) #define CK_CALLBACK_FUNCTION(returnType, name) returnType (* name) #ifndef NULL_PTR #define NULL_PTR NULL #endif #include "pkcs11.h" #include "attributes.h" /* * This PKCS #11 implementation is hardwired with one slot, the token * for which is always present (so we return the same answer * regardless of the value of tokenPresent). */ #define P11_ONE_AND_ONLY_SLOT 0 /* * Version numbers. Placeholders for now. * * Software version number is just the version of this PKCS #11 * implementation. Probably. */ #warning Figure out hardware and software version numbers #define P11_VERSION_SW_MAJOR 0 #define P11_VERSION_SW_MINOR 1 #define P11_VERSION_HW_MAJOR 0 #define P11_VERSION_HW_MINOR 1 /* * Debugging control. */ #ifndef DEBUG_HAL #define DEBUG_HAL 0 #endif #ifndef DEBUG_PKCS11 #define DEBUG_PKCS11 0 #endif /* * Whether to include POSIX-specific features. */ #ifndef USE_POSIX #define USE_POSIX 1 #endif /* * Whether to use POSIX threads. */ #ifndef USE_PTHREADS #define USE_PTHREADS USE_POSIX #endif #if USE_PTHREADS && !USE_POSIX #error Can not use POSIX threads without using POSIX #endif #if USE_POSIX #include #include #endif #if USE_PTHREADS #include #endif /* * PKCS #11 session. */ /* * At present we have no concept of encryption or signature algorithms * in libhal, as we only support RSA and AES. For PKCS #11 purposes * we can figure out what kind of key we're looking at from attributes * like CKA_KEY_TYPE, so it's just something we look up given the key * object handle. * * General idea is that we have separate descriptors/handles/state for * each operation that we're allowed to do in parallel, so sign, * verify, digest, encrypt, decrypt, wrapkey, and unwrapkey all need * separate slots in the session structure. Add these as we go. */ typedef struct p11_session { CK_SESSION_HANDLE handle; /* Session handle */ struct p11_session *link; /* Next session in list */ CK_STATE state; /* State (CKS_*) of this session */ CK_NOTIFY notify; /* Notification callback */ CK_VOID_PTR application; /* Application data */ sqlite3_stmt *find_query; /* FindObject*() query state */ int find_query_done; /* find_query has terminated */ hal_digest_algorithm_t digest_algorithm, /* Hash algorithm for C_Digest*() */ sign_digest_algorithm, /* Hash algorithm for C_Sign*() */ verify_digest_algorithm; /* Hash algorithm for C_Verify*() */ CK_OBJECT_HANDLE sign_key_handle, /* Private key for C_Sign*() */ verify_key_handle; /* Public key for C_Verify() */ hal_hash_handle_t digest_handle, /* Hash state for C_Digest*() */ sign_digest_handle, /* Hash state for C_Sign*() */ verify_digest_handle; /* Hash state for C_Verify*() */ } p11_session_t; /* * PKCS #11 handle management. PKCS #11 has two kinds of handles: * session handles and object handles. We subdivide object handles * into token object handles (handles for objects that live on the * token) and session object handles (handles for objects that live * only as long as the session does), and we steal a bit of the object * handle as a flag to distinguish between our two kinds of object * handles, considerably simplifing the objected-related SQL code. */ typedef enum { handle_flavor_session, handle_flavor_token_object, handle_flavor_session_object } handle_flavor_t; #define FLAG_HANDLE_TOKEN 0x80000000 #define is_token_handle(_handle_) (((_handle_) & FLAG_HANDLE_TOKEN) != 0) /* * Digest algorithm to use when computing a key hashes. This doesn't * need to be particularly secure, we're just using it to generate * reasonably unique identifier strings from public keys. We use * SHA-1 for this because that's what most X.509 implementations use * for this purpose. */ #define P11_KEY_HASH_ALGORITHM HAL_DIGEST_ALGORITHM_SHA1 /* * Current logged-in user. */ static enum { not_logged_in, logged_in_as_user, logged_in_as_so } logged_in_as = not_logged_in; /* * PKCS #11 sessions for this application. */ static p11_session_t *p11_sessions; /* * Next PKCS #11 handle to allocate. We use a single handle space for * both session and object handles, and we just keep incrementing * until it wraps, to reduce the amount of time we have to spend * on SQL probes to avoid handle conflicts. */ static CK_ULONG next_handle; /* * Mutex callbacks. */ static CK_CREATEMUTEX mutex_cb_create; static CK_DESTROYMUTEX mutex_cb_destroy; static CK_LOCKMUTEX mutex_cb_lock; static CK_UNLOCKMUTEX mutex_cb_unlock; /* * Global mutex. We may want something finer grained later, but this * will suffice to comply with the API requirements. */ static CK_VOID_PTR p11_global_mutex; /* * (POSIX-specific) process which last called C_Initialize(). */ #if USE_POSIX static pid_t initialized_pid; #endif /* * Syntactic sugar for functions returning CK_RV complex enough to * need cleanup actions on failure. Also does very basic logging for * debug-by-printf(). * * NB: This uses a variable ("rv") and a goto target ("fail") which * must be defined in the calling environment. We could make these * arguments to the macro, but doing so would make the code less * readable without significantly reducing the voodoo factor. */ #if DEBUG_PKCS11 #define lose(_ck_rv_code_) \ do { \ rv = (_ck_rv_code_); \ fprintf(stderr, "\n%s:%u: %s\n", __FILE__, __LINE__, #_ck_rv_code_); \ goto fail; \ } while (0) #else /* DEBUG_PKCS11 */ #define lose(_ck_rv_code_) \ do { \ rv = (_ck_rv_code_); \ goto fail; \ } while (0) #endif /* DEBUG_PKCS11 */ /* * More debug-by-printf() support. One would like to consider this a * relic of the previous millenium, but, sadly, broken debugging * environments are still all too common. */ #if DEBUG_PKCS11 > 1 #define ENTER_PUBLIC_FUNCTION(_name_) \ fprintf(stderr, "\nEntering function %s\n", #_name_) #else /* DEBUG_PKCS11 > 1 */ #define ENTER_PUBLIC_FUNCTION(_name_) #endif /* DEBUG_PKCS11 > 1 */ /* * Error checking for libhal calls. */ #define hal_whine(_expr_) (_hal_whine((_expr_), #_expr_, __FILE__, __LINE__, HAL_OK)) #define hal_whine_allow(_expr_, ...) (_hal_whine((_expr_), #_expr_, __FILE__, __LINE__, __VA_ARGS__, HAL_OK)) #define hal_check(_expr_) (hal_whine(_expr_) == HAL_OK) #if DEBUG_HAL static inline hal_error_t _hal_whine(const hal_error_t err, const char * const expr, const char * const file, const unsigned line, ...) { va_list ap; int ok = 0; hal_error_t code; va_start(ap, line); do { code = va_arg(ap, hal_error_t); ok |= (err == code); } while (code != HAL_OK); va_end(ap); if (!ok) fprintf(stderr, "\n%s:%u: %s returned %s\n", file, line, expr, hal_error_string(err)); return err; } #else /* DEBUG_HAL */ #define _hal_whine(_expr_, ...) (_expr_) #endif /* DEBUG_HAL */ /* * Error translation fun for the entire family! */ #if DEBUG_PKCS11 || DEBUG_HAL #define hal_p11_error_case(_hal_err_, _p11_err_) \ case _hal_err_: fprintf(stderr, "\n%s:%u: Mapping %s to %s\n", file, line, #_hal_err_, #_p11_err_); return _p11_err_; #else #define hal_p11_error_case(_hal_err_, _p11_err_) \ case _hal_err_: return _p11_err_; #endif #define p11_error_from_hal(_hal_err_) \ (_p11_error_from_hal((_hal_err_), __FILE__, __LINE__)) #define p11_whine_from_hal(_expr_) \ (_p11_error_from_hal(_hal_whine((_expr_), #_expr_, __FILE__, __LINE__, HAL_OK), __FILE__, __LINE__)) static CK_RV _p11_error_from_hal(const hal_error_t err, const char * const file, const unsigned line) { switch (err) { hal_p11_error_case(HAL_ERROR_PIN_INCORRECT, CKR_PIN_INCORRECT); hal_p11_error_case(HAL_ERROR_INVALID_SIGNATURE, CKR_SIGNATURE_INVALID); /* * More here later, first see if this compiles. */ case HAL_OK: return CKR_OK; default: #if DEBUG_PKCS11 || DEBUG_HAL fprintf(stderr, "\n%s:%u: Mapping unhandled HAL error to CKR_FUNCTION_FAILED\n", file, line); #endif return CKR_FUNCTION_FAILED; } } #undef hal_p11_error_case /* * All (?) public functions should test whether we've been initialized or not. * Where practical, we bury this check in other boilerplate. */ #if USE_POSIX #define p11_uninitialized() (!initialized_pid) #else #define p11_uninitialized() (0) #endif /* * Handle unsupported functions. */ #define UNSUPPORTED_FUNCTION(_name_) \ do { \ ENTER_PUBLIC_FUNCTION(_name_); \ if (p11_uninitialized()) \ return CKR_CRYPTOKI_NOT_INITIALIZED; \ return CKR_FUNCTION_NOT_SUPPORTED; \ } while (0) /* * SQL utilities. */ /* * Debugging control. */ #ifndef DEBUG_SQL #define DEBUG_SQL 1 #endif /* * Default filename for SQL database lives. Can be overriden at * runtime by setting PKCS11_DATABASE environment variable. */ #ifndef SQL_DATABASE #define SQL_DATABASE ".cryptech-pkcs11.db" #endif /* * SQL database. */ static sqlite3 *sqldb = NULL; /* * Error checking for SQLite calls. */ #if DEBUG_SQL #define sql_whine(_expr_) \ (fprintf(stderr, "\n%s:%u: %s returned %s\n", \ __FILE__, __LINE__, #_expr_, sqlite3_errmsg(sqldb)), \ sql_breakpoint()) #else /* DEBUG_SQL */ #define sql_whine(_expr_) \ ((void) 0) #endif /* DEBUG_SQL */ #define sql_check(_good_, _expr_) \ ((_expr_) == (_good_) ? 1 : (sql_whine(_expr_), 0)) #define sql_check_ok(_expr_) sql_check(SQLITE_OK, _expr_) #define sql_check_row(_expr_) sql_check(SQLITE_ROW, _expr_) #define sql_check_done(_expr_) sql_check(SQLITE_DONE, _expr_) #define sql_whine_step() sql_whine(sqlite3_step()) /* * Hook on which to hang a debugger breakpoint on SQL errors. */ #if DEBUG_SQL static void sql_breakpoint(void) { fprintf(stderr, "\n[sql_breakpoint]\n"); } #endif /* * Execute SQL code that doesn't require a prepared query. */ static int sql_exec(const char *cmd) { char *msg = NULL; if (sql_check_ok(sqlite3_exec(sqldb, cmd, NULL, NULL, &msg))) return 1; #if DEBUG_SQL if (msg != NULL) fprintf(stderr, "\n[%s]\n", msg); #endif return 0; } /* * Initialize SQL. This includes loading our schema, portions of * which live in the temp (memory) database thus always need to be * created on startup. */ static int sql_init(void) { static const char schema[] = #include "schema.h" ; assert(sqldb == NULL); const char * const env = getenv("CRYPTECH_PKCS11_DATABASE"); const char * const home = getenv("HOME"); const char * const base = SQL_DATABASE; int ok; if (env != NULL) { ok = sql_check_ok(sqlite3_open(env, &sqldb)); } else if (home == NULL) { ok = sql_check_ok(sqlite3_open(base, &sqldb)); } else { char fn[strlen(home) + strlen(base) + 2]; snprintf(fn, sizeof(fn), "%s/%s", home, base); ok = sql_check_ok(sqlite3_open(fn, &sqldb)); } return ok && sql_exec(schema); } /* * Shut down SQL. * * Yes, this can return failure, although it's not clear what we're * meant to do about that if the application is going to shut down * regardless of what we do. I guess we could loop retrying a few * times for errors like SQLITE_BUSY, but that's about it. */ static int sql_fini(void) { if (!sql_check_ok(sqlite3_close(sqldb))) return 0; sqldb = NULL; return 1; } /* * GCC attribute declaration to help catch format string errors, * ignored by other compilers. */ #ifdef __GNUC__ static int sql_prepare(sqlite3_stmt **q, const char *format, ...) __attribute__ ((format (printf, 2, 3))); #endif /* * Prepare an SQLite3 query, using vsnprintf() to format the query. * * WARNING WARNING WARNING WARNING * * Do not use this formatting mechanism for anything involving * user-supplied data. It's only intended to handle things like * selecting between two parallel table structures or queries using * manifest constants that are only available in C header files. */ static int sql_prepare(sqlite3_stmt **q, const char *format, ...) { char buffer[2048]; va_list ap; size_t n; va_start(ap, format); n = vsnprintf(buffer, sizeof(buffer), format, ap); va_end(ap); if (n >= sizeof(buffer)) return SQLITE_TOOBIG; return sqlite3_prepare_v2(sqldb, buffer, -1, q, NULL); } /* * This idiom occurs frequently, bundle it so we have the option of * doing it along with the normal conditional control flow that SQL * queries seem to follow. */ static int sql_finalize_and_clear(sqlite3_stmt **q) { assert(q != NULL); int err = sqlite3_finalize(*q); if (err != SQLITE_OK) return err; *q = NULL; return SQLITE_OK; } /* * Thread mutex utilities. We need to handle three separate cases: * * 1) User doesn't care about mutexes; * 2) User wants us to use "OS" mutexes; * 3) User wants us to use user-specified mutexs. * * For "OS" mutexes, read POSIX Threads mutexes, at least for now. * * PKCS #11 sort of has a fourth case, but it's really just license * for us to pick either the second or third case at whim. * * To simplify the rest of the API, we provide a POSIX-based * implementation which uses the same API an user-provided mutex * implementation would be required to use, use null function pointers * to represent the case where the user doesn't need mutexes at all, * and wrap the whole thing in trivial macros to insulate the rest of * the code from the grotty details. */ /* * Basic macros. */ #define mutex_create(_m_) (mutex_cb_create == NULL ? CKR_OK : mutex_cb_create(_m_)) #define mutex_destroy(_m_) (mutex_cb_destroy == NULL ? CKR_OK : mutex_cb_destroy(_m_)) #define mutex_lock(_m_) (mutex_cb_lock == NULL ? CKR_OK : mutex_cb_lock(_m_)) #define mutex_unlock(_m_) (mutex_cb_unlock == NULL ? CKR_OK : mutex_cb_unlock(_m_)) /* * Slightly higher-level macros for common operations. * * Since the locking code depends on initialization, * attempting to lock anything when not initialized * is a failure, by definition. */ #define mutex_lock_or_return_failure(_m_) \ do { \ if (p11_uninitialized()) \ return CKR_CRYPTOKI_NOT_INITIALIZED; \ CK_RV _rv = mutex_lock(_m_); \ if (_rv != CKR_OK) \ return _rv; \ } while (0) #define mutex_unlock_return_with_rv(_rv_, _m_) \ do { \ CK_RV _rv1 = _rv_; \ CK_RV _rv2 = mutex_unlock(_m_); \ return _rv1 == CKR_OK ? _rv2 : _rv1; \ } while (0) /* * Mutex implementation using POSIX mutexes. */ #if USE_PTHREADS static CK_RV posix_mutex_create(CK_VOID_PTR_PTR ppMutex) { pthread_mutex_t *m = NULL; CK_RV rv; if (ppMutex == NULL) lose(CKR_GENERAL_ERROR); if ((m = malloc(sizeof(*m))) == NULL) lose(CKR_HOST_MEMORY); switch (pthread_mutex_init(m, NULL)) { case 0: *ppMutex = m; return CKR_OK; case ENOMEM: lose(CKR_HOST_MEMORY); default: lose(CKR_GENERAL_ERROR); } fail: if (m != NULL) free(m); return rv; } static CK_RV posix_mutex_destroy(CK_VOID_PTR pMutex) { CK_RV rv; if (pMutex == NULL) lose(CKR_MUTEX_BAD); switch (pthread_mutex_destroy(pMutex)) { case 0: free(pMutex); return CKR_OK; case EINVAL: lose(CKR_MUTEX_BAD); case EBUSY: /* * PKCS #11 mutex semantics are a bad match for POSIX here, * leaving us only the nuclear option. Feh. Fall through. */ default: lose(CKR_GENERAL_ERROR); } fail: return rv; } static CK_RV posix_mutex_lock(CK_VOID_PTR pMutex) { CK_RV rv; if (pMutex == NULL) lose(CKR_MUTEX_BAD); switch (pthread_mutex_lock(pMutex)) { case 0: return CKR_OK; case EINVAL: lose(CKR_MUTEX_BAD); default: lose(CKR_GENERAL_ERROR); } fail: return rv; } static CK_RV posix_mutex_unlock(CK_VOID_PTR pMutex) { CK_RV rv; if (pMutex == NULL) lose(CKR_MUTEX_BAD); switch (pthread_mutex_unlock(pMutex)) { case 0: return CKR_OK; case EINVAL: lose(CKR_MUTEX_BAD); case EPERM: lose(CKR_MUTEX_NOT_LOCKED); default: lose(CKR_GENERAL_ERROR); } fail: return rv; } #endif /* USE_PTHREADS */ /* * Translate between libhal EC curve names and OIDs. */ #warning Perhaps this should be a utility routine in libhal instead of here static int ec_curve_oid_to_name(const uint8_t * const oid, const size_t oid_len, hal_curve_name_t *curve) { static uint8_t ec_curve_oid_p256[] = { 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 }; static uint8_t ec_curve_oid_p384[] = { 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22 }; static uint8_t ec_curve_oid_p521[] = { 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23 }; if (oid == NULL || curve == NULL) return 0; else if (oid_len == sizeof(ec_curve_oid_p256) && memcmp(oid, ec_curve_oid_p256, oid_len) == 0) *curve = HAL_CURVE_P256; else if (oid_len == sizeof(ec_curve_oid_p384) && memcmp(oid, ec_curve_oid_p384, oid_len) == 0) *curve = HAL_CURVE_P384; else if (oid_len == sizeof(ec_curve_oid_p521) && memcmp(oid, ec_curve_oid_p521, oid_len) == 0) *curve = HAL_CURVE_P521; else return 0; return 1; } /* * Extract libhal-compatible client and session identifiers from a session. * * libhal's session identifiers are deliberately chosen to be in the same * numeric range as PKCS #11's, so we can just use them directly. * * libhal's client identifiers are multiplexing extension handled elsewhere, * for our purposes using constant client identifier of zero will do. */ static inline hal_client_handle_t p11_session_hal_client(const p11_session_t * const session) { hal_client_handle_t handle = {0}; return handle; } static inline hal_session_handle_t p11_session_hal_session(const p11_session_t * const session) { hal_session_handle_t handle = {session->handle}; return handle; } /* * Find an unused handle. * * Note that zero is an excluded value (CK_INVALID_HANDLE), hence the * slightly odd arithmetic. * * For object handles, we steal the high-order bit to flag whether the * handle represents a session object or token object. */ static CK_ULONG p11_allocate_unused_handle(const handle_flavor_t flavor) { static const char select_format[] = " SELECT %s_id FROM %s WHERE %s_handle = ?"; const char *table = flavor == handle_flavor_session ? "session" : "object"; sqlite3_stmt *q = NULL; CK_ULONG handle; int ret; if (!sql_check_ok(sql_prepare(&q, select_format, table, table, table))) goto fail; for (;;) { handle = ++next_handle; next_handle %= 0xFFFFFFFF; switch (flavor) { case handle_flavor_session: break; case handle_flavor_token_object: handle |= FLAG_HANDLE_TOKEN; break; case handle_flavor_session_object: handle &= ~FLAG_HANDLE_TOKEN; break; } assert(handle != CK_INVALID_HANDLE); if (!sql_check_ok(sqlite3_reset(q)) || !sql_check_ok(sqlite3_bind_int64(q, 1, handle))) goto fail; if ((ret = sqlite3_step(q)) == SQLITE_ROW) continue; if (ret == SQLITE_DONE) break; sql_whine_step(); goto fail; } sqlite3_finalize(q); return handle; fail: sqlite3_finalize(q); return CK_INVALID_HANDLE; } /* * Translate CKA_TOKEN value to handle flavor. */ static handle_flavor_t p11_handle_flavor_from_cka_token(const CK_BBOOL *bbool) { assert(bbool != NULL); return *bbool ? handle_flavor_token_object : handle_flavor_session_object; } /* * Attribute methods. */ /* * Set an attribute for a given object. * * It would be trivial to generalize this to take a CK_ATTRIBUTE_PTR * template instead of a single attribute, at the cost of losing the * const specifiers (CK_ATTRIBUTE_PTR has an internal non-const void*). */ static int p11_attribute_set(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, const void * const value, const CK_ULONG length) { static const char insert_format[] = " INSERT OR REPLACE INTO %s_attribute (%s_object_id, type, value)" " VALUES ((SELECT %s_object_id FROM object WHERE object_handle = ?1), ?2, ?3)"; const char *flavor = is_token_handle(object_handle) ? "token" : "session"; sqlite3_stmt *q = NULL; int ok = 0; if (!sql_check_ok(sql_prepare(&q, insert_format, flavor, flavor, flavor)) || !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle)) || !sql_check_ok(sqlite3_bind_int64(q, 2, type)) || !sql_check_ok(sqlite3_bind_blob( q, 3, value, length, NULL)) || !sql_check_done(sqlite3_step(q))) goto fail; ok = 1; fail: sqlite3_finalize(q); return ok; } /* * Get a single attribute from a given object. * * This could easily be generalized to take a CK_ATTRIBUTE_PTR, at the * cost of more complicated error semantics. */ static int p11_attribute_get(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, void *value, CK_ULONG *length, const CK_ULONG maxlength) { static const char select_format[] = " SELECT value FROM %s_attribute NATURAL JOIN object" " WHERE object_handle = ?1 AND type = ?2"; const char *flavor = is_token_handle(object_handle) ? "token" : "session"; sqlite3_stmt *q = NULL; int ret, ok = 0; CK_ULONG len; if (!sql_check_ok(sql_prepare(&q, select_format, flavor)) || !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle)) || !sql_check_ok(sqlite3_bind_int64(q, 2, type))) goto fail; ret = sqlite3_step(q); if (ret == SQLITE_DONE) goto fail; if (ret != SQLITE_ROW) { sql_whine_step(); goto fail; } len = sqlite3_column_bytes(q, 0); if (length != NULL) *length = len; if (value != NULL && maxlength < len) goto fail; if (value != NULL) memcpy(value, sqlite3_column_blob(q, 0), len); ok = 1; fail: sqlite3_finalize(q); return ok; } /* * Wrappers to set and get CK_BBOOL and CK_ULONG values. */ static int p11_attribute_set_bbool(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, const CK_BBOOL value) { return p11_attribute_set(object_handle, type, &value, sizeof(value)); } #if 0 static int p11_attribute_set_ulong(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, const CK_ULONG value) { return p11_attribute_set(object_handle, type, &value, sizeof(value)); } #endif static int p11_attribute_get_bbool(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, CK_BBOOL *value) { CK_ULONG length; return p11_attribute_get(object_handle, type, value, &length, sizeof(*value)) && length == sizeof(*value); } static int p11_attribute_get_ulong(const CK_OBJECT_HANDLE object_handle, const CK_ATTRIBUTE_TYPE type, CK_ULONG *value) { CK_ULONG length; return p11_attribute_get(object_handle, type, value, &length, sizeof(*value)) && length == sizeof(*value); } /* * Find an attribute in a CK_ATTRIBUTE_PTR template. Returns index * into template, or -1 if not found. */ static int p11_attribute_find_in_template(const CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE_PTR template, const CK_ULONG length) { if (template != NULL) for (int i = 0; i < length; i++) if (template[i].type == type) return i; return -1; } /* * Find an attribute in a CK_ATTRIBUTE_PTR template. Returns pointer * to attribute value, or NULL if not found. */ static void *p11_attribute_find_value_in_template(const CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE_PTR template, const CK_ULONG length) { const int i = p11_attribute_find_in_template(type, template, length); return i < 0 ? NULL : template[i].pValue; } /* * Map a keyusage-related attribute to a keyusage bit flag. * * Assumes that calling code has already checked whether this * attribute is legal for this object class, that attribute which * should be CK_BBOOLs are of the correct length, etcetera. * * To handle all the possible permutations of specified and default * values, it may be necessary to defer calling this method until * after the default and mandatory values have been merged into the * values supplied by the application-supplied template. * * Semantics of the flags follow RFC 5280 4.2.1.3. Numeric values * don't matter particularly as we only use them internally so we * can simplify things a bit by reusing libhal's flag values. */ static void p11_attribute_apply_keyusage(hal_key_flags_t *keyusage, const CK_ATTRIBUTE_TYPE type, const CK_BBOOL *value) { unsigned flag; assert(keyusage != NULL && value != NULL); switch (type) { case CKA_SIGN: /* Generate signature */ case CKA_VERIFY: /* Verify signature */ flag = HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE; break; case CKA_ENCRYPT: /* Encrypt bulk data (seldom used) */ case CKA_DECRYPT: /* Bulk decryption (seldom used) */ flag = HAL_KEY_FLAG_USAGE_DATAENCIPHERMENT; break; case CKA_WRAP: /* Wrap key (normal way of doing encryption) */ case CKA_UNWRAP: /* Unwrap key (normal way of doing decryption) */ flag = HAL_KEY_FLAG_USAGE_KEYENCIPHERMENT; break; default: return; /* Attribute not related to key usage */ } if (*value) *keyusage |= flag; else *keyusage &= ~flag; } /* * Descriptor methods. Descriptors are generated at compile time by * an auxiliary Python script, see attributes.* for details. */ /* * Return the descriptor associated with a particular object class and * key type. */ static const p11_descriptor_t *p11_descriptor_from_key_type(const CK_OBJECT_CLASS object_class, const CK_KEY_TYPE key_type) { int i; for (i = 0; i < sizeof(p11_descriptor_keyclass_map)/sizeof(*p11_descriptor_keyclass_map); i++) { const p11_descriptor_keyclass_map_t * const m = &p11_descriptor_keyclass_map[i]; if (m->object_class == object_class && m->key_type == key_type) return m->descriptor; } return NULL; } /* * Find the entry for a particular attribute in a descriptor. */ static const p11_attribute_descriptor_t *p11_find_attribute_in_descriptor(const p11_descriptor_t *descriptor, const CK_ATTRIBUTE_TYPE type) { int i; if (descriptor != NULL && descriptor->attributes != NULL) for (i = 0; i < descriptor->n_attributes; i++) if (descriptor->attributes[i].type == type) return &descriptor->attributes[i]; return NULL; } /* * Check whether an attribute is marked as sensitive. If we don't * recognize the attribute, report it as sensitive (safer than the * alternative). */ static int p11_attribute_is_sensitive(const p11_descriptor_t *descriptor, const CK_ATTRIBUTE_TYPE type) { const p11_attribute_descriptor_t *a = p11_find_attribute_in_descriptor(descriptor, type); return a == NULL || (a->flags & P11_DESCRIPTOR_SENSITIVE) != 0; } /* * Object methods. */ /* * Check access rights for an object. */ typedef enum { p11_object_access_read, p11_object_access_write } p11_object_access_t; static CK_RV p11_object_check_rights(const p11_session_t *session, const CK_OBJECT_HANDLE object_handle, const p11_object_access_t rights) { static const char object_exists_query[] = " SELECT count(*) FROM object WHERE object_handle = ?1"; static const char session_object_query[] = " SELECT session_handle FROM session NATURAL JOIN object WHERE object_handle = ?1"; sqlite3_stmt *q = NULL; CK_BBOOL cka_private; CK_BBOOL cka_token; CK_RV rv; if (session == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (!p11_attribute_get_bbool(object_handle, CKA_PRIVATE, &cka_private)) cka_private = CK_TRUE; if (!p11_attribute_get_bbool(object_handle, CKA_TOKEN, &cka_token)) cka_token = CK_FALSE; /* * Read-only sessions are, um, read-only. Well, except, in PKCS #11, * read-only only sort of means what you might expect. */ if (rights == p11_object_access_write) { switch (session->state) { case CKS_RO_PUBLIC_SESSION: if (cka_private) lose(CKR_SESSION_READ_ONLY); /* Fall through */ case CKS_RO_USER_FUNCTIONS: if (cka_token) lose(CKR_SESSION_READ_ONLY); /* Fall through */ } } /* * Private objects don't exist for sessions in the wrong state. */ switch (session->state) { case CKS_RO_PUBLIC_SESSION: case CKS_RW_PUBLIC_SESSION: case CKS_RW_SO_FUNCTIONS: if (cka_private) lose(CKR_OBJECT_HANDLE_INVALID); } /* * Does the object even exist? */ if (!sql_check_ok(sql_prepare(&q, object_exists_query)) || !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle)) || !sql_check_row(sqlite3_step(q)) || !sqlite3_column_int(q, 0)) lose(CKR_OBJECT_HANDLE_INVALID); /* * Session objects are only visible to the session which created them. */ if (!is_token_handle(object_handle) && (!sql_check_ok(sql_finalize_and_clear(&q)) || !sql_check_ok(sql_prepare(&q, session_object_query)) || !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle)) || !sql_check_row(sqlite3_step(q)) || sqlite3_column_int64(q, 0) != session->handle)) lose(CKR_OBJECT_HANDLE_INVALID); /* * Ran out of reasons to reject, guess we should allow it. */ rv = CKR_OK; fail: sqlite3_finalize(q); return rv; } /* * Delete all private objects, probably because user logged out. * * In the case of token objects, the object itself remains in the * token, we're just deleting our handle for the object. * * In the case of session objects, the object itself goes away. */ static int p11_object_delete_all_private(void) { static const char delete_format[] = " WITH" " s AS (SELECT session_object_id FROM session_attribute WHERE type = %u AND value <> X'00')," " t AS (SELECT token_object_id FROM token_attribute WHERE type = %u AND value <> X'00')" " DELETE FROM object WHERE token_object_id IN t OR session_object_id IN s"; sqlite3_stmt *q = NULL; int ok = 0; if (!sql_check_ok(sql_prepare(&q, delete_format, CKA_PRIVATE, CKA_PRIVATE)) || !sql_check_done(sqlite3_step(q))) goto fail; ok = 1; fail: sqlite3_finalize(q); return ok; } /* * Create a new object. * * This is a bit nasty due to the SQL foreign key constraints and the * different handling required for session and token objects. */ static CK_OBJECT_HANDLE p11_object_create(const p11_session_t *session, const handle_flavor_t flavor, const CK_ATTRIBUTE_PTR template, const CK_ULONG template_length, const p11_descriptor_t * const descriptor, const CK_MECHANISM_PTR mechanism) { static const char insert_object[] = " INSERT INTO object (object_handle)" " VALUES (?)"; static const char insert_token_object[] = " INSERT INTO token_object DEFAULT VALUES"; static const char insert_session_object[] = " INSERT INTO session_object (object_id) VALUES (?)"; static const char update_object_session_object[] = " UPDATE object SET" " session_id = (SELECT session_id FROM session WHERE session_handle = ?1)," " session_object_id = ?2" " WHERE object_id = ?3"; static const char update_object_token_object[] = " UPDATE object SET token_object_id = ?1 WHERE object_id = ?2"; static const char insert_token_attribute[] = " INSERT OR REPLACE INTO token_attribute (token_object_id, type, value)" " VALUES (?1, ?2, ?3)"; static const char insert_session_attribute[] = " INSERT OR REPLACE INTO session_attribute (session_object_id, type, value)" " VALUES (?1, ?2, ?3)"; CK_OBJECT_HANDLE object_handle = p11_allocate_unused_handle(flavor);; sqlite3_int64 object_id, session_object_id, token_object_id; sqlite3_stmt *q = NULL; int i, ok = 0; assert(session != NULL && template != NULL && descriptor != NULL && (flavor == handle_flavor_token_object || flavor == handle_flavor_session_object)); if (!sql_check_ok(sql_prepare(&q, insert_object)) || !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle)) || !sql_check_done(sqlite3_step(q))) goto fail; object_id = sqlite3_last_insert_rowid(sqldb); if (!sql_check_ok(sql_finalize_and_clear(&q))) goto fail; switch (flavor) { case handle_flavor_token_object: if (!sql_check_ok(sql_prepare(&q, insert_token_object)) || !sql_check_done(sqlite3_step(q))) goto fail; token_object_id = sqlite3_last_insert_rowid(sqldb); if (!sql_check_ok(sql_finalize_and_clear(&q)) || !sql_check_ok(sql_prepare(&q, update_object_token_object)) || !sql_check_ok(sqlite3_bind_int64(q, 1, token_object_id)) || !sql_check_ok(sqlite3_bind_int64(q, 2, object_id)) || !sql_check_done(sqlite3_step(q)) || !sql_check_ok(sql_finalize_and_clear(&q)) || !sql_check_ok(sql_prepare(&q, insert_token_attribute)) || !sql_check_ok(sqlite3_bind_int64(q, 1, token_object_id))) goto fail; break; case handle_flavor_session_object: if (!sql_check_ok(sql_prepare(&q, insert_session_object)) || !sql_check_ok(sqlite3_bind_int64(q, 1, object_id)) || !sql_check_done(sqlite3_step(q))) goto fail; session_object_id = sqlite3_last_insert_rowid(sqldb); if (!sql_check_ok(sql_finalize_and_clear(&q)) || !sql_check_ok(sql_prepare(&q, update_object_session_object)) || !sql_check_ok(sqlite3_bind_int64(q, 1, session->handle)) || !sql_check_ok(sqlite3_bind_int64(q, 2, session_object_id)) || !sql_check_ok(sqlite3_bind_int64(q, 3, object_id)) || !sql_check_done(sqlite3_step(q)) || !sql_check_ok(sql_finalize_and_clear(&q)) || !sql_check_ok(sql_prepare(&q, insert_session_attribute)) || !sql_check_ok(sqlite3_bind_int64(q, 1, session_object_id))) goto fail; break; default: /* Suppress GCC warning */ goto fail; } /* * Now populate attributes, starting with the application's * template, which we assume has already been blessed by the API * function that called this method. * * If the attribute is flagged as sensitive in the descriptor, we * don't store it in SQL. Generally, this only arises for private * key components of objects created with C_CreateObject(), but in * theory there are some corner cases in which a user could choose * to mark a private key as extractable and not sensitive, so we * might have to back-fill missing values in those cases if anyone * ever thinks up a sane reason for supporting them. For now, assume * that private keys are bloody well supposed to be private. */ for (i = 0; i < template_length; i++) { const CK_ATTRIBUTE_TYPE type = template[i].type; const void * val = template[i].pValue; const int len = template[i].ulValueLen; if (p11_attribute_is_sensitive(descriptor, type)) continue; if (!sql_check_ok(sqlite3_reset(q)) || !sql_check_ok(sqlite3_bind_int64(q, 2, type)) || !sql_check_ok(sqlite3_bind_blob( q, 3, val, len, NULL)) || !sql_check_done(sqlite3_step(q))) goto fail; } /* * Next, add defaults from the descriptor. */ for (i = 0; i < descriptor->n_attributes; i++) { const CK_ATTRIBUTE_TYPE type = descriptor->attributes[i].type; const void * val = descriptor->attributes[i].value; const int len = descriptor->attributes[i].length; const unsigned flags = descriptor->attributes[i].flags; if (val == NULL && (flags & P11_DESCRIPTOR_DEFAULT_VALUE) != 0) val = ""; if (val == NULL || p11_attribute_find_in_template(type, template, template_length) >= 0) continue; if (!sql_check_ok(sqlite3_reset(q)) || !sql_check_ok(sqlite3_bind_int64(q, 2, type)) || !sql_check_ok(sqlite3_bind_blob( q, 3, val, len, NULL)) || !sql_check_done(sqlite3_step(q))) goto fail; } /* * Finally, add generation mechanism attributes as needed. */ if (mechanism != NULL && (!sql_check_ok(sqlite3_reset(q)) || !sql_check_ok(sqlite3_bind_int64(q, 2, CKA_LOCAL)) || !sql_check_ok(sqlite3_bind_blob( q, 3, &const_CK_TRUE, sizeof(const_CK_TRUE), NULL)) || !sql_check_done(sqlite3_step(q)) || !sql_check_ok(sqlite3_reset(q)) || !sql_check_ok(sqlite3_bind_int64(q, 2, CKA_KEY_GEN_MECHANISM)) || !sql_check_ok(sqlite3_bind_blob( q, 3, &mechanism->mechanism, sizeof(mechanism->mechanism), NULL)) || !sql_check_done(sqlite3_step(q)))) goto fail; /* * If we made it past all that, we're happy. */ ok = 1; fail: sqlite3_finalize(q); return ok ? object_handle : CK_INVALID_HANDLE; } /* * Bind PKCS #11 objects to keystore objects, via the key's pkey type * and UUID. */ static int p11_object_bind_pkey(const p11_session_t * const session, const CK_OBJECT_HANDLE object_handle, const hal_uuid_t * const pkey_uuid) { assert(session != NULL && pkey_uuid != NULL); static const char update_format[] = " UPDATE %s_object SET hal_pkey_uuid = ?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 = (sql_check_ok(sql_prepare(&q, update_format, flavor, flavor, flavor)) && sql_check_ok(sqlite3_bind_blob( q, 1, pkey_uuid, sizeof(*pkey_uuid), NULL)) && sql_check_ok(sqlite3_bind_int64(q, 2, object_handle)) && sql_check_done(sqlite3_step(q))); sqlite3_finalize(q); return ok; } /* * Create pkeys to go with PKCS #11 key objects loaded by C_CreateObject(). */ static inline int p11_object_create_rsa_public_key(const p11_session_t * const session, const CK_OBJECT_HANDLE object_handle, const hal_key_flags_t flags) { static const char select_format[] = " WITH a (type, value) " " AS (SELECT type, value FROM %s_attribute NATURAL JOIN object WHERE object_handle = ?1)" " SELECT a1.value, a2.value FROM a AS a1, a AS a2 WHERE a1.type = %u AND a2.type = %u"; const char *flavor = is_token_handle(object_handle) ? "token" : "session"; hal_pkey_handle_t pkey = {HAL_HANDLE_NONE}; uint8_t keybuf[hal_rsa_key_t_size]; hal_rsa_key_t *key = NULL; sqlite3_stmt *q = NULL; int ok = (sql_check_ok(sql_prepare(&q, select_format, flavor, CKA_MODULUS, CKA_PUBLIC_EXPONENT)) && sql_check_ok(sqlite3_bind_int64(q, 1, object_handle)) && sql_check_row(sqlite3_step(q)) && sqlite3_column_type(q, 0) == SQLITE_BLOB && sqlite3_column_type(q, 1) == SQLITE_BLOB && hal_check(hal_rsa_key_load_public(&key, keybuf, sizeof(keybuf), sqlite3_column_blob( q, 0), sqlite3_column_bytes(q, 0), sqlite3_column_blob( q, 1), sqlite3_column_bytes(q, 1)))); if (ok) { uint8_t der[hal_rsa_public_key_to_der_len(key)]; hal_uuid_t uuid; ok = (hal_check(hal_rsa_public_key_to_der(key, der, NULL, sizeof(der))) && hal_check(hal_rpc_pkey_load(p11_session_hal_client(session), p11_session_hal_session(session), &pkey, HAL_KEY_TYPE_RSA_PUBLIC, HAL_CURVE_NONE, &uuid, der, sizeof(der), flags)) && p11_object_bind_pkey(session, object_handle, &uuid)); } if (!ok && pkey.handle != HAL_HANDLE_NONE) (void) hal_rpc_pkey_delete(pkey); else (void) hal_rpc_pkey_close(pkey); sqlite3_finalize(q); return ok; } static inline int p11_object_create_ec_public_key(const p11_session_t * const session, const CK_OBJECT_HANDLE object_handle, const hal_key_flags_t flags) { static const char select_format[] = " WITH a (type, value) " " AS (SELECT type, value FROM %s_attribute NATURAL JOIN object WHERE object_handle = ?1)" " SELECT a1.value, a2.value FROM a AS a1, a AS a2 WHERE a1.type = %u AND a2.type = %u"; const char *flavor = is_token_handle(object_handle) ? "token" : "session"; hal_pkey_handle_t pkey = {HAL_HANDLE_NONE}; uint8_t keybuf[hal_ecdsa_key_t_size]; hal_ecdsa_key_t *key = NULL; hal_curve_name_t curve; sqlite3_stmt *q = NULL; int ok = (sql_check_ok(sql_prepare(&q, select_format, flavor, CKA_EC_PARAMS, CKA_EC_POINT)) && sql_check_ok(sqlite3_bind_int64(q, 1, object_handle)) && sql_check_row(sqlite3_step(q)) && sqlite3_column_type(q, 0) == SQLITE_BLOB && sqlite3_column_type(q, 1) == SQLITE_BLOB && ec_curve_oid_to_name(sqlite3_column_blob( q, 0), sqlite3_column_bytes(q, 0), &curve) && hal_check(hal_ecdsa_key_from_ecpoint(&key, keybuf, sizeof(keybuf), sqlite3_column_blob( q, 1), sqlite3_column_bytes(q, 1), curve))); if (ok) { uint8_t der[hal_ecdsa_public_key_to_der_len(key)]; hal_uuid_t uuid; ok = (hal_check(hal_ecdsa_public_key_to_der(key, der, NULL, sizeof(der))) && hal_check(hal_rpc_pkey_load(p11_session_hal_client(session), p11_session_hal_session(session), &pkey, HAL_KEY_TYPE_EC_PUBLIC, curve, &uuid, der, sizeof(der), flags)) && p11_object_bind_pkey(session, object_handle, &uuid)); } if (!ok && pkey.handle != HAL_HANDLE_NONE) (void) hal_rpc_pkey_delete(pkey); else (void) hal_rpc_pkey_close(pkey); sqlite3_finalize(q); return ok; } static inline int p11_object_create_rsa_private_key(const p11_session_t * const session, const CK_OBJECT_HANDLE object_handle, const hal_key_flags_t flags, const CK_ATTRIBUTE_PTR template, const CK_ULONG template_len) { static const char select_format[] = " WITH a (type, value) " " AS (SELECT type, value FROM %s_attribute NATURAL JOIN object WHERE object_handle = ?1)" " SELECT a1.value, a2.value FROM a AS a1, a AS a2 WHERE a1.type = %u AND a2.type = %u"; const char *flavor = is_token_handle(object_handle) ? "token" : "session"; hal_pkey_handle_t pkey = {HAL_HANDLE_NONE}; uint8_t keybuf[hal_rsa_key_t_size]; hal_rsa_key_t *key = NULL; sqlite3_stmt *q = NULL; const uint8_t *cka_private_exponent = NULL; size_t cka_private_exponent_len = 0; const uint8_t *cka_prime_1 = NULL; size_t cka_prime_1_len = 0; const uint8_t *cka_prime_2 = NULL; size_t cka_prime_2_len = 0; const uint8_t *cka_exponent_1 = NULL; size_t cka_exponent_1_len = 0; const uint8_t *cka_exponent_2 = NULL; size_t cka_exponent_2_len = 0; const uint8_t *cka_coefficient = NULL; size_t cka_coefficient_len = 0; for (int i = 0; i < template_len; i++) { switch (template[i].type) { case CKA_PRIVATE_EXPONENT: cka_private_exponent = template[i].pValue; cka_private_exponent_len = template[i].ulValueLen; break; case CKA_PRIME_1: cka_prime_1 = template[i].pValue; cka_prime_1_len = template[i].ulValueLen; break; case CKA_PRIME_2: cka_prime_2 = template[i].pValue; cka_prime_2_len = template[i].ulValueLen; break; case CKA_EXPONENT_1: cka_exponent_1 = template[i].pValue; cka_exponent_1_len = template[i].ulValueLen; break; case CKA_EXPONENT_2: cka_exponent_2 = template[i].pValue; cka_exponent_2_len = template[i].ulValueLen; break; case CKA_COEFFICIENT: cka_coefficient = template[i].pValue; cka_coefficient_len = template[i].ulValueLen; break; } } int ok = (sql_check_ok(sql_prepare(&q, select_format, flavor, CKA_MODULUS, CKA_PUBLIC_EXPONENT)) && sql_check_ok(sqlite3_bind_int64(q, 1, object_handle)) && sql_check_row(sqlite3_step(q)) && sqlite3_column_type(q, 0) == SQLITE_BLOB && sqlite3_column_type(q, 1) == SQLITE_BLOB && hal_check(hal_rsa_key_load_private(&key, keybuf, sizeof(keybuf), sqlite3_column_blob( q, 0), sqlite3_column_bytes(q, 0), sqlite3_column_blob( q, 1), sqlite3_column_bytes(q, 1), cka_private_exponent, cka_private_exponent_len, cka_prime_1, cka_prime_1_len, cka_prime_2, cka_prime_2_len, cka_coefficient, cka_coefficient_len, cka_exponent_1, cka_exponent_1_len, cka_exponent_2, cka_exponent_2_len))); if (ok) { uint8_t der[hal_rsa_private_key_to_der_len(key)]; hal_uuid_t uuid; ok = (hal_check(hal_rsa_private_key_to_der(key, der, NULL, sizeof(der))) && hal_check(hal_rpc_pkey_load(p11_session_hal_client(session), p11_session_hal_session(session), &pkey, HAL_KEY_TYPE_RSA_PRIVATE, HAL_CURVE_NONE, &uuid, der, sizeof(der), flags)) && p11_object_bind_pkey(session, object_handle, &uuid)); memset(der, 0, sizeof(der)); } memset(keybuf, 0, sizeof(keybuf)); if (!ok && pkey.handle != HAL_HANDLE_NONE) (void) hal_rpc_pkey_delete(pkey); else (void) hal_rpc_pkey_close(pkey); sqlite3_finalize(q); return ok; } static inline int p11_object_create_ec_private_key(const p11_session_t * const session, const CK_OBJECT_HANDLE object_handle, const hal_key_flags_t flags, const CK_ATTRIBUTE_PTR template, const CK_ULONG template_len) { static const char select_format[] = " WITH a (type, value) " " AS (SELECT type, value FROM %s_attribute NATURAL JOIN object WHERE object_handle = ?1)" " SELECT a1.value, a2.value FROM a AS a1, a AS a2 WHERE a1.type = %u AND a2.type = %u"; const char *flavor = is_token_handle(object_handle) ? "token" : "session"; hal_pkey_handle_t pkey = {HAL_HANDLE_NONE}; uint8_t keybuf[hal_ecdsa_key_t_size]; hal_ecdsa_key_t *key = NULL; hal_curve_name_t curve; sqlite3_stmt *q = NULL; const uint8_t *ecpoint = NULL; size_t ecpoint_len = 0; const uint8_t *cka_value = NULL; size_t cka_value_len = 0; for (int i = 0; i < template_len; i++) if (template[i].type == CKA_VALUE) cka_value = template[i].pValue, cka_value_len = template[i].ulValueLen; int ok = (sql_check_ok(sql_prepare(&q, select_format, flavor, CKA_EC_PARAMS, CKA_EC_POINT)) && sql_check_ok(sqlite3_bind_int64(q, 1, object_handle)) && sql_check_row(sqlite3_step(q)) && sqlite3_column_type(q, 0) == SQLITE_BLOB && sqlite3_column_type(q, 1) == SQLITE_BLOB && ec_curve_oid_to_name(sqlite3_column_blob( q, 0), sqlite3_column_bytes(q, 0), &curve) && ((ecpoint_len = sqlite3_column_bytes(q, 1)) & 1) != 0 && *(ecpoint = sqlite3_column_blob( q, 1)) == 0x04 && hal_check(hal_ecdsa_key_load_private(&key, keybuf, sizeof(keybuf), curve, ecpoint + 1 + 0 * ecpoint_len / 2, ecpoint_len / 2, ecpoint + 1 + 0 * ecpoint_len / 2, ecpoint_len / 2, cka_value, cka_value_len))); if (ok) { uint8_t der[hal_ecdsa_private_key_to_der_len(key)]; hal_uuid_t uuid; ok = (hal_check(hal_ecdsa_private_key_to_der(key, der, NULL, sizeof(der))) && hal_check(hal_rpc_pkey_load(p11_session_hal_client(session), p11_session_hal_session(session), &pkey, HAL_KEY_TYPE_EC_PRIVATE, curve, &uuid, der, sizeof(der), flags)) && p11_object_bind_pkey(session, object_handle, &uuid)); memset(der, 0, sizeof(der)); } memset(keybuf, 0, sizeof(keybuf)); if (!ok && pkey.handle != HAL_HANDLE_NONE) (void) hal_rpc_pkey_delete(pkey); else (void) hal_rpc_pkey_close(pkey); sqlite3_finalize(q); return ok; } /* * Given a PKCS #11 object, obtain a libhal pkey handle. */ static int p11_object_get_pkey_handle(const p11_session_t * const session, const CK_OBJECT_HANDLE object_handle, hal_pkey_handle_t *pkey_handle) { static const char select_format[] = " SELECT hal_pkey_uuid FROM %s_object NATURAL JOIN object WHERE object_handle = ?1"; hal_key_flags_t flags = is_token_handle(object_handle) ? HAL_KEY_FLAG_TOKEN : 0; const char *flavor = is_token_handle(object_handle) ? "token" : "session"; sqlite3_stmt *q = NULL; int ok = 0; assert(pkey_handle != NULL); if (!sql_check_ok(sql_prepare(&q, select_format, flavor)) || !sql_check_ok(sqlite3_bind_int64(q, 1, object_handle)) || !sql_check_row(sqlite3_step(q)) || sqlite3_column_type(q, 0) != SQLITE_BLOB || sqlite3_column_bytes(q, 0) != sizeof(hal_uuid_t)) goto fail; const hal_uuid_t * const pkey_uuid = sqlite3_column_blob(q, 0); ok = hal_check(hal_rpc_pkey_find(p11_session_hal_client(session), p11_session_hal_session(session), pkey_handle, pkey_uuid, flags)); fail: sqlite3_finalize(q); return ok; } /* * Session methods. */ /* * Create a new session. */ static p11_session_t *p11_session_new(void) { p11_session_t *session = malloc(sizeof(*session)); if (session == NULL) return NULL; memset(session, 0, sizeof(*session)); return session; } /* * Free a session. */ static void p11_session_free(p11_session_t *session) { if (session == NULL) return; sql_finalize_and_clear(&session->find_query); (void) hal_rpc_hash_finalize(session->digest_handle, NULL, 0); (void) hal_rpc_hash_finalize(session->sign_digest_handle, NULL, 0); (void) hal_rpc_hash_finalize(session->verify_digest_handle, NULL, 0); free(session); } /* * Assign a handle to a session and add the session to SQL. */ static int p11_session_add(p11_session_t *session) { static const char insert_session[] = " INSERT INTO session (session_handle) VALUES (?)"; sqlite3_stmt *q = NULL; int ok = 0; assert(session != NULL); session->handle = p11_allocate_unused_handle(handle_flavor_session); if (!sql_check_ok(sql_prepare(&q, insert_session)) || !sql_check_ok(sqlite3_bind_int64(q, 1, session->handle)) || !sql_check_done(sqlite3_step(q))) goto fail; session->link = p11_sessions; p11_sessions = session; ok = 1; fail: sqlite3_finalize(q); return ok; } /* * Find a session. * * Since we don't expect the total number of sessions to be all that * high, we use a linked list with a move-to-the-front search. Some * of the other session methods assume this behavior, so be careful if * you decide to change it. */ static p11_session_t *p11_session_find(const CK_SESSION_HANDLE session_handle) { p11_session_t **link, *session; for (link = &p11_sessions; (session = *link) != NULL && session->handle != session_handle; link = &session->link) ; if (session != NULL && link != &p11_sessions) { *link = session->link; session->link = p11_sessions; p11_sessions = session; } return session; } /* * Delete a session: remove it from SQL and free the session data * structure. * * This method assumes the move-to-the-front behavior of * p11_session_find(). */ static CK_RV p11_session_delete(const CK_SESSION_HANDLE session_handle) { static const char delete_session[] = " DELETE FROM session WHERE session_handle = ?"; p11_session_t *session = p11_session_find(session_handle); sqlite3_stmt *q = NULL; CK_RV rv = CKR_OK; if (session == NULL) return CKR_SESSION_HANDLE_INVALID; if (!sql_check_ok(sql_prepare(&q, delete_session)) || !sql_check_ok(sqlite3_bind_int64(q, 1, session_handle)) || !sql_check_done(sqlite3_step(q))) lose(CKR_FUNCTION_FAILED); /* Check that move-to-the-front behaved as expected */ assert(p11_sessions == session); p11_sessions = session->link; p11_session_free(session); /* Deleting last session also logs us out */ if (p11_sessions == NULL) logged_in_as = not_logged_in; fail: sqlite3_finalize(q); return rv; } /* * Delete all sessions. */ #warning Should this also clear the object table? static CK_RV p11_session_delete_all(void) { static const char delete_all_sessions[] = " DELETE FROM session"; p11_session_t *session; CK_RV rv = CKR_OK; if (!sql_exec(delete_all_sessions)) lose(CKR_FUNCTION_FAILED); while (p11_sessions != NULL) { session = p11_sessions; p11_sessions = session->link; p11_session_free(session); } logged_in_as = not_logged_in; fail: return rv; } /* * Check session database against login state for consistency. * * This is mostly useful in assertions. */ static int p11_session_consistent_login(void) { p11_session_t *session; switch (logged_in_as) { case not_logged_in: for (session = p11_sessions; session != NULL; session = session->link) if (session->state != CKS_RO_PUBLIC_SESSION && session->state != CKS_RW_PUBLIC_SESSION) return 0; return 1; case logged_in_as_user: for (session = p11_sessions; session != NULL; session = session->link) if (session->state != CKS_RO_USER_FUNCTIONS && session->state != CKS_RW_USER_FUNCTIONS) return 0; return 1; case logged_in_as_so: for (session = p11_sessions; session != NULL; session = session->link) if (session->state != CKS_RW_SO_FUNCTIONS) return 0; return 1; default: return 0; } } /* * PKCS #11 likes space-padded rather than null-terminated strings. * This requires minor antics so that we can use a printf()-like API * while neither overflowing the caller's buffer nor truncating the * output if it happens to be exactly the target length. */ static int psnprintf(void *buffer_, size_t size, const char *format, ...) { char buffer[size + 1]; size_t i, n; va_list ap; va_start(ap, format); i = n = vsnprintf(buffer, sizeof(buffer), format, ap); va_end(ap); while (i < size) buffer[i++] = ' '; memcpy(buffer_, buffer, size); return n; } /* * Template checking and key generation. */ /* * First pass: called once per template entry during initial pass over * template to handle generic checks that apply regardless of * attribute type. */ static CK_RV p11_template_check_1(const CK_ATTRIBUTE_TYPE type, const void * const val, const size_t len, const p11_descriptor_t * const descriptor, unsigned long forbidden_flag) { const p11_attribute_descriptor_t * const atd = p11_find_attribute_in_descriptor(descriptor, type); CK_RV rv; /* Attribute not allowed or not allowed for key generation */ if (atd == NULL || (atd->flags & forbidden_flag) != 0) lose(CKR_ATTRIBUTE_TYPE_INVALID); /* NULL or wrong-sized attribute values */ if (val == NULL || (atd->size != 0 && len != atd->size)) lose(CKR_ATTRIBUTE_VALUE_INVALID); /* Attributes which only the SO user is allowed to set to CK_TRUE */ if ((atd->flags & P11_DESCRIPTOR_ONLY_SO_USER_CAN_SET) != 0 && logged_in_as != logged_in_as_so && *(CK_BBOOL *) val) lose(CKR_ATTRIBUTE_VALUE_INVALID); /* Attributes which don't match mandatory values */ if (atd->value != NULL && (atd->flags & P11_DESCRIPTOR_DEFAULT_VALUE) == 0 && memcmp(val, atd->value, atd->length) != 0) lose(CKR_TEMPLATE_INCONSISTENT); #warning Add _LATCH checks here? rv = CKR_OK; fail: #if DEBUG_PKCS11 if (rv != CKR_OK) fprintf(stderr, "\np11_template_check_1() rejected attribute 0x%08lx\n", (unsigned long) type); #endif return rv; } /* * Second pass: called once per template to check that each attribute * required for that template has been specified exactly once. */ static CK_RV p11_template_check_2(const p11_session_t *session, const p11_descriptor_t * const descriptor, const CK_ATTRIBUTE_PTR template, const CK_ULONG template_length, unsigned long required_flag, unsigned long forbidden_flag) { const CK_BBOOL *object_is_private; CK_RV rv; int i, j; /* * Some session states aren't allowed to play with private objects. */ switch (session->state) { case CKS_RO_PUBLIC_SESSION: case CKS_RW_PUBLIC_SESSION: case CKS_RW_SO_FUNCTIONS: if ((object_is_private = p11_attribute_find_value_in_template(CKA_PRIVATE, template, template_length)) == NULL) { const p11_attribute_descriptor_t * const atd = p11_find_attribute_in_descriptor(descriptor, CKA_PRIVATE); assert(atd != NULL && atd->value != NULL); object_is_private = atd->value; } if (*object_is_private) lose(CKR_TEMPLATE_INCONSISTENT); } for (i = 0; i < descriptor->n_attributes; i++) { const p11_attribute_descriptor_t * const atd = &descriptor->attributes[i]; const int required_by_api = (atd->flags & required_flag) != 0; const int forbidden_by_api = (atd->flags & forbidden_flag) != 0; const int in_descriptor = (atd->flags & P11_DESCRIPTOR_DEFAULT_VALUE) != 0 || atd->value != NULL; const int pos_in_template = p11_attribute_find_in_template(atd->type, template, template_length); /* Multiple entries for same attribute */ if (pos_in_template >= 0) for (j = pos_in_template + 1; j < template_length; j++) if (template[j].type == atd->type) lose(CKR_TEMPLATE_INCONSISTENT); /* Required attribute missing from template */ if (!forbidden_by_api && (required_by_api || !in_descriptor) && pos_in_template < 0) { #if DEBUG_PKCS11 fprintf(stderr, "\n[Missing attribute 0x%lx]\n", atd->type); #endif lose(CKR_TEMPLATE_INCOMPLETE); } } rv = CKR_OK; fail: return rv; } /* * Mechanism-independent checks for templates and descriptors when * generating new keypairs. * * PKCS #11 gives the application far too much rope (including but not * limited to the ability to supply completely unrelated templates for * public and private keys in a keypair), so we need to do a fair * amount of checking. We automate as much of the dumb stuff as * possible through the object descriptor. * * Key usage handling here is based on RFC 5280 4.2.1.3. */ static CK_RV p11_check_keypair_attributes(const p11_session_t *session, const CK_ATTRIBUTE_PTR pPublicKeyTemplate, const CK_ULONG ulPublicKeyAttributeCount, const p11_descriptor_t * const public_descriptor, hal_key_flags_t *public_flags, const CK_ATTRIBUTE_PTR pPrivateKeyTemplate, const CK_ULONG ulPrivateKeyAttributeCount, const p11_descriptor_t * const private_descriptor, hal_key_flags_t *private_flags) { CK_RV rv = CKR_OK; int i; assert(session != NULL && pPublicKeyTemplate != NULL && public_descriptor != NULL && public_flags != NULL && pPrivateKeyTemplate != NULL && private_descriptor != NULL && private_flags != NULL); *public_flags = *private_flags = 0; const CK_BBOOL * public_cka_private = NULL, * public_cka_token = NULL; const CK_BBOOL *private_cka_private = NULL, *private_cka_token = NULL; /* * Check values provided in the public and private templates. */ for (i = 0; i < ulPublicKeyAttributeCount; i++) { const CK_ATTRIBUTE_TYPE type = pPublicKeyTemplate[i].type; const void * const val = pPublicKeyTemplate[i].pValue; const size_t len = pPublicKeyTemplate[i].ulValueLen; if ((rv = p11_template_check_1(type, val, len, public_descriptor, P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE)) != CKR_OK) goto fail; if (type == CKA_TOKEN) public_cka_token = val; if (type == CKA_PRIVATE) public_cka_private = val; p11_attribute_apply_keyusage(public_flags, type, val); } for (i = 0; i < ulPrivateKeyAttributeCount; i++) { const CK_ATTRIBUTE_TYPE type = pPrivateKeyTemplate[i].type; const void * const val = pPrivateKeyTemplate[i].pValue; const size_t len = pPrivateKeyTemplate[i].ulValueLen; if ((rv = p11_template_check_1(type, val, len, private_descriptor, P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE)) != CKR_OK) goto fail; if (type == CKA_TOKEN) public_cka_token = val; if (type == CKA_PRIVATE) public_cka_private = val; p11_attribute_apply_keyusage(private_flags, type, val); } /* * We insist that keyusage be specified for both public and private * key, and that they match. May not need to be this strict. */ if (*public_flags != *private_flags || *public_flags == 0) lose(CKR_TEMPLATE_INCONSISTENT); /* * Check that all required attributes have been specified. */ if ((rv = p11_template_check_2(session, public_descriptor, pPublicKeyTemplate, ulPublicKeyAttributeCount, P11_DESCRIPTOR_REQUIRED_BY_GENERATE, P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE)) != CKR_OK || (rv = p11_template_check_2(session, private_descriptor, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, P11_DESCRIPTOR_REQUIRED_BY_GENERATE, P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE)) != CKR_OK) goto fail; /* * Read-only sessions can't create objects, doh. * Well, except when they can, thanks, PKCS #11. */ switch (session->state) { case CKS_RO_PUBLIC_SESSION: if ((public_cka_private == NULL || *public_cka_private) || (private_cka_private == NULL || *private_cka_private)) lose(CKR_SESSION_READ_ONLY); /* Fall through */ case CKS_RO_USER_FUNCTIONS: if ((public_cka_token != NULL && *public_cka_token) || (private_cka_token != NULL && *private_cka_token)) lose(CKR_SESSION_READ_ONLY); /* Fall through */ } /* * If we get this far, we're happy. Maybe. */ rv = CKR_OK; fail: return rv; } /* * CKM_RSA_PKCS_KEY_PAIR_GEN key pair generation handler. */ static CK_RV generate_keypair_rsa_pkcs(p11_session_t *session, const CK_ATTRIBUTE_PTR pPublicKeyTemplate, const CK_ULONG ulPublicKeyAttributeCount, const CK_OBJECT_HANDLE public_handle, const hal_key_flags_t public_flags, const CK_ATTRIBUTE_PTR pPrivateKeyTemplate, const CK_ULONG ulPrivateKeyAttributeCount, const CK_OBJECT_HANDLE private_handle, const hal_key_flags_t private_flags) { const int same_keystore = is_token_handle(public_handle) == is_token_handle(private_handle); const uint8_t *public_exponent = const_0x010001; size_t public_exponent_len = sizeof(const_0x010001); hal_pkey_handle_t pkey1 = {HAL_HANDLE_NONE}, pkey2 = {HAL_HANDLE_NONE}; CK_ULONG keysize = 0; hal_uuid_t uuid; CK_RV rv; int i; assert(session != NULL && pPublicKeyTemplate != NULL && pPrivateKeyTemplate != NULL); for (i = 0; i < ulPublicKeyAttributeCount; i++) { const CK_ATTRIBUTE_TYPE type = pPublicKeyTemplate[i].type; const void * const val = pPublicKeyTemplate[i].pValue; const size_t len = pPublicKeyTemplate[i].ulValueLen; switch (type) { case CKA_MODULUS_BITS: /* Keysize in bits -- only allow multiples of 8 */ keysize = *(CK_ULONG *) val; if ((keysize & 7) != 0) return CKR_ATTRIBUTE_VALUE_INVALID; continue; case CKA_PUBLIC_EXPONENT: public_exponent = val; public_exponent_len = len; continue; } } if (keysize == 0) return CKR_TEMPLATE_INCOMPLETE; if (!hal_check(hal_rpc_pkey_generate_rsa(p11_session_hal_client(session), p11_session_hal_session(session), &pkey1, &uuid, keysize, public_exponent, public_exponent_len, private_flags)) || !p11_object_bind_pkey(session, private_handle, &uuid)) lose(CKR_FUNCTION_FAILED); { uint8_t der[hal_rpc_pkey_get_public_key_len(pkey1)], keybuf[hal_rsa_key_t_size]; size_t der_len, modulus_len; hal_rsa_key_t *key = NULL; if (!hal_check(hal_rpc_pkey_get_public_key(pkey1, der, &der_len, sizeof(der))) || !hal_check(hal_rsa_public_key_from_der(&key, keybuf, sizeof(keybuf), der, der_len)) || !hal_check(hal_rsa_key_get_modulus(key, NULL, &modulus_len, 0))) lose(CKR_FUNCTION_FAILED); uint8_t modulus[modulus_len]; if (!hal_check(hal_rsa_key_get_modulus(key, modulus, NULL, sizeof(modulus))) || !p11_attribute_set(public_handle, CKA_MODULUS, modulus, modulus_len) || !p11_attribute_set(private_handle, CKA_MODULUS, modulus, modulus_len)) lose(CKR_FUNCTION_FAILED); if (!same_keystore && !hal_check(hal_rpc_pkey_load(p11_session_hal_client(session), p11_session_hal_session(session), &pkey2, HAL_KEY_TYPE_RSA_PUBLIC, HAL_CURVE_NONE, &uuid, der, der_len, public_flags))) lose(CKR_FUNCTION_FAILED); } if (!p11_object_bind_pkey(session, public_handle, &uuid)) lose(CKR_FUNCTION_FAILED); rv = CKR_OK; fail: hal_rpc_pkey_close(pkey1); hal_rpc_pkey_close(pkey2); return rv; } /* * CKM_EC_KEY_PAIR_GEN key pair generation handler. */ static CK_RV generate_keypair_ec(p11_session_t *session, const CK_ATTRIBUTE_PTR pPublicKeyTemplate, const CK_ULONG ulPublicKeyAttributeCount, const CK_OBJECT_HANDLE public_handle, const hal_key_flags_t public_flags, const CK_ATTRIBUTE_PTR pPrivateKeyTemplate, const CK_ULONG ulPrivateKeyAttributeCount, const CK_OBJECT_HANDLE private_handle, const hal_key_flags_t private_flags) { const int same_keystore = is_token_handle(public_handle) == is_token_handle(private_handle); hal_pkey_handle_t pkey1 = {HAL_HANDLE_NONE}, pkey2 = {HAL_HANDLE_NONE}; const CK_BYTE *params = NULL; hal_curve_name_t curve; size_t params_len; hal_uuid_t uuid; CK_RV rv; int i; assert(session != NULL && pPublicKeyTemplate != NULL && pPrivateKeyTemplate != NULL); for (i = 0; i < ulPublicKeyAttributeCount; i++) { const CK_ATTRIBUTE_TYPE type = pPublicKeyTemplate[i].type; const void * const val = pPublicKeyTemplate[i].pValue; const size_t len = pPublicKeyTemplate[i].ulValueLen; switch (type) { case CKA_EC_PARAMS: params = val; params_len = len; continue; } } if (!ec_curve_oid_to_name(params, params_len, &curve)) return CKR_TEMPLATE_INCOMPLETE; if (!hal_check(hal_rpc_pkey_generate_ec(p11_session_hal_client(session), p11_session_hal_session(session), &pkey1, &uuid, curve, private_flags)) || !p11_object_bind_pkey(session, private_handle, &uuid) || !p11_attribute_set(public_handle, CKA_EC_PARAMS, params, params_len) || !p11_attribute_set(private_handle, CKA_EC_PARAMS, params, params_len)) lose(CKR_FUNCTION_FAILED); { uint8_t der[hal_rpc_pkey_get_public_key_len(pkey1)], keybuf[hal_ecdsa_key_t_size]; hal_ecdsa_key_t *key = NULL; size_t der_len; if (!hal_check(hal_rpc_pkey_get_public_key(pkey1, der, &der_len, sizeof(der))) || !hal_check(hal_ecdsa_public_key_from_der(&key, keybuf, sizeof(keybuf), der, der_len))) lose(CKR_FUNCTION_FAILED); uint8_t point[hal_ecdsa_key_to_ecpoint_len(key)]; if (!hal_check(hal_ecdsa_key_to_ecpoint(key, point, NULL, sizeof(point))) || !p11_attribute_set(public_handle, CKA_EC_POINT, point, sizeof(point))) lose(CKR_FUNCTION_FAILED); if (!same_keystore && !hal_check(hal_rpc_pkey_load(p11_session_hal_client(session), p11_session_hal_session(session), &pkey2, HAL_KEY_TYPE_EC_PUBLIC, curve, &uuid, der, der_len, public_flags))) lose(CKR_FUNCTION_FAILED); } if (!p11_object_bind_pkey(session, public_handle, &uuid)) lose(CKR_FUNCTION_FAILED); rv = CKR_OK; fail: hal_rpc_pkey_close(pkey1); hal_rpc_pkey_close(pkey2); return rv; } /* * Key pair generation. This needs a mechanism-specific function to * do the inner bits, but there's a lot of boilerplate. */ static CK_RV generate_keypair(p11_session_t *session, const CK_MECHANISM_PTR pMechanism, CK_RV (*mechanism_handler)(p11_session_t *session, const CK_ATTRIBUTE_PTR pPublicKeyTemplate, const CK_ULONG ulPublicKeyAttributeCount, const CK_OBJECT_HANDLE public_handle, const hal_key_flags_t public_flags, const CK_ATTRIBUTE_PTR pPrivateKeyTemplate, const CK_ULONG ulPrivateKeyAttributeCount, const CK_OBJECT_HANDLE private_handle, const hal_key_flags_t private_flags), const CK_ATTRIBUTE_PTR pPublicKeyTemplate, const CK_ULONG ulPublicKeyAttributeCount, const p11_descriptor_t * const public_descriptor, CK_OBJECT_HANDLE_PTR phPublicKey, const CK_ATTRIBUTE_PTR pPrivateKeyTemplate, const CK_ULONG ulPrivateKeyAttributeCount, const p11_descriptor_t * const private_descriptor, CK_OBJECT_HANDLE_PTR phPrivateKey) { CK_OBJECT_HANDLE public_handle = CK_INVALID_HANDLE; CK_OBJECT_HANDLE private_handle = CK_INVALID_HANDLE; handle_flavor_t public_handle_flavor = handle_flavor_session_object; handle_flavor_t private_handle_flavor = handle_flavor_session_object; hal_key_flags_t public_flags = 0; hal_key_flags_t private_flags = 0; int in_transaction = 0; CK_RV rv; int i; rv = p11_check_keypair_attributes(session, pPublicKeyTemplate, ulPublicKeyAttributeCount, public_descriptor, &public_flags, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, private_descriptor, &private_flags); if (rv != CKR_OK) return rv; assert(session != NULL && pMechanism != NULL && pPublicKeyTemplate != NULL && phPublicKey != NULL && pPrivateKeyTemplate != NULL && phPrivateKey != NULL); for (i = 0; i < ulPublicKeyAttributeCount; i++) if (pPublicKeyTemplate[i].type == CKA_TOKEN) public_handle_flavor = p11_handle_flavor_from_cka_token(pPublicKeyTemplate[i].pValue); for (i = 0; i < ulPrivateKeyAttributeCount; i++) if (pPrivateKeyTemplate[i].type == CKA_TOKEN) private_handle_flavor = p11_handle_flavor_from_cka_token(pPrivateKeyTemplate[i].pValue); if (public_handle_flavor == handle_flavor_token_object) public_flags |= HAL_KEY_FLAG_TOKEN; if (private_handle_flavor == handle_flavor_token_object) private_flags |= HAL_KEY_FLAG_TOKEN; if (!sql_exec("BEGIN")) lose(CKR_FUNCTION_FAILED); in_transaction = 1; if ((public_handle = p11_object_create(session, public_handle_flavor, pPublicKeyTemplate, ulPublicKeyAttributeCount, public_descriptor, pMechanism)) == CK_INVALID_HANDLE) lose(CKR_FUNCTION_FAILED); if ((private_handle = p11_object_create(session, private_handle_flavor, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, private_descriptor, pMechanism)) == CK_INVALID_HANDLE) lose(CKR_FUNCTION_FAILED); rv = mechanism_handler(session, pPublicKeyTemplate, ulPublicKeyAttributeCount, public_handle, public_flags, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, private_handle, private_flags); if (rv != CKR_OK) goto fail; if (!sql_exec("COMMIT")) lose(CKR_FUNCTION_FAILED); *phPublicKey = public_handle; *phPrivateKey = private_handle; return CKR_OK; fail: if (in_transaction && !sql_exec("ROLLBACK")) rv = CKR_GENERAL_ERROR; return rv; } /* * Mechanism-independent checks for templates and descriptors when * import objects via C_CreateObject(). * * Fun question exactly how calling code knows what descriptor to * pass. p11_descriptor_from_key_type() will suffice for key objects. * Drive off that bridge when we get to it. */ static CK_RV p11_check_create_attributes(const p11_session_t *session, const CK_ATTRIBUTE_PTR pTemplate, const CK_ULONG ulCount, const p11_descriptor_t * const descriptor) { const CK_BBOOL *cka_private = NULL; const CK_BBOOL *cka_token = NULL; CK_RV rv = CKR_OK; int i; assert(session != NULL && pTemplate != NULL && descriptor != NULL); /* * Check values provided in the template. */ for (i = 0; i < ulCount; i++) { const CK_ATTRIBUTE_TYPE type = pTemplate[i].type; const void * const val = pTemplate[i].pValue; const size_t len = pTemplate[i].ulValueLen; if ((rv = p11_template_check_1(type, val, len, descriptor, P11_DESCRIPTOR_FORBIDDEN_BY_CREATEOBJECT)) != CKR_OK) goto fail; if (type == CKA_TOKEN) cka_token = val; if (type == CKA_PRIVATE) cka_private = val; } /* * Check that all required attributes have been specified. */ if ((rv = p11_template_check_2(session, descriptor, pTemplate, ulCount, P11_DESCRIPTOR_REQUIRED_BY_CREATEOBJECT, P11_DESCRIPTOR_FORBIDDEN_BY_CREATEOBJECT)) != CKR_OK) goto fail; /* * Read-only sessions can't create objects, doh. * Well, except when they can, thanks, PKCS #11. */ switch (session->state) { case CKS_RO_PUBLIC_SESSION: if (cka_private == NULL || *cka_private) lose(CKR_SESSION_READ_ONLY); /* Fall through */ case CKS_RO_USER_FUNCTIONS: if (cka_token != NULL && *cka_token) lose(CKR_SESSION_READ_ONLY); /* Fall through */ } /* * If we get this far, we're happy. Maybe. */ rv = CKR_OK; fail: return rv; } /* * Add data to a digest. */ static CK_RV digest_update(const p11_session_t * const session, const hal_digest_algorithm_t algorithm, hal_hash_handle_t *handle, const uint8_t * const data, const size_t data_len) { assert(algorithm != HAL_DIGEST_ALGORITHM_NONE && handle != NULL && data != NULL); if (handle->handle == HAL_HANDLE_NONE) { switch (hal_rpc_hash_initialize(p11_session_hal_client(session), p11_session_hal_session(session), handle, algorithm, NULL, 0)) { case HAL_OK: break; case HAL_ERROR_ALLOCATION_FAILURE: return CKR_HOST_MEMORY; default: return CKR_FUNCTION_FAILED; } } if (!hal_check(hal_rpc_hash_update(*handle, data, data_len))) return CKR_FUNCTION_FAILED; return CKR_OK; } /* * Finish using a digest context, if we haven't already. */ static void digest_cleanup(hal_hash_handle_t *handle) { assert(handle != NULL); if (handle->handle == HAL_HANDLE_NONE) return; (void) hal_rpc_hash_finalize(*handle, NULL, 0); handle->handle = HAL_HANDLE_NONE; } /* * Compute the length of a signature based on the key. We could get * this via the RPC API, but its probably faster to look in the local * attribute database. Rewrite this later if this proves incorrect. */ static int get_signature_len(const CK_OBJECT_HANDLE object_handle, const hal_pkey_handle_t pkey, size_t *signature_len) { assert(signature_len != NULL); CK_KEY_TYPE cka_key_type; hal_curve_name_t curve; CK_BYTE oid[20]; CK_ULONG len; if (!p11_attribute_get_ulong(object_handle, CKA_KEY_TYPE, &cka_key_type)) return 0; switch (cka_key_type) { case CKK_RSA: if (!p11_attribute_get(object_handle, CKA_MODULUS, NULL, &len, 0)) return 0; *signature_len = len; return 1; case CKK_EC: if (!p11_attribute_get(object_handle, CKA_EC_PARAMS, oid, &len, sizeof(oid)) || !ec_curve_oid_to_name(oid, len, &curve)) return 0; switch (curve) { case HAL_CURVE_P256: *signature_len = 64; return 1; case HAL_CURVE_P384: *signature_len = 96; return 1; case HAL_CURVE_P521: *signature_len = 132; return 1; default: return 0; } } return 0; } /* * Generate a signature using the libhal RPC API. */ static CK_RV sign_hal_rpc(p11_session_t *session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { hal_pkey_handle_t pkey = {HAL_HANDLE_NONE}; size_t signature_len; CK_RV rv; assert(session != NULL && pulSignatureLen != NULL); if (!p11_object_get_pkey_handle(session, session->sign_key_handle, &pkey)) lose(CKR_FUNCTION_FAILED); if (!get_signature_len(session->sign_key_handle, pkey, &signature_len)) lose(CKR_FUNCTION_FAILED); rv = pSignature != NULL && signature_len > *pulSignatureLen ? CKR_BUFFER_TOO_SMALL : CKR_OK; *pulSignatureLen = signature_len; if (pSignature != NULL && rv == CKR_OK) rv = p11_whine_from_hal(hal_rpc_pkey_sign(pkey, session->sign_digest_handle, pData, ulDataLen, pSignature, &signature_len, signature_len)); /* Fall through */ fail: hal_rpc_pkey_close(pkey); return rv; } /* * Verify a signature using the libhal RPC API. */ static CK_RV verify_hal_rpc(p11_session_t *session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { hal_pkey_handle_t pkey = {HAL_HANDLE_NONE}; CK_RV rv; assert(session != NULL); if (!p11_object_get_pkey_handle(session, session->verify_key_handle, &pkey)) lose(CKR_FUNCTION_FAILED); rv = p11_whine_from_hal(hal_rpc_pkey_verify(pkey, session->verify_digest_handle, pData, ulDataLen, pSignature, ulSignatureLen)); /* Fall through */ fail: hal_rpc_pkey_close(pkey); return rv; } #warning May need to do something about truncating oversized hashes for ECDSA, see PKCS11 spec /* * PKCS #11 API functions. */ CK_RV C_Initialize(CK_VOID_PTR pInitArgs) { ENTER_PUBLIC_FUNCTION(C_Initialize); CK_C_INITIALIZE_ARGS_PTR a = pInitArgs; int initialized_sql = 0; int initialized_rpc = 0; CK_RV rv; /* * We'd like to detect the error of calling this method more than * once in a single process without an intervening call to * C_Finalize(), but there's no completely portable way to do that * when faced with things like the POSIX fork() system call. For * the moment, we use a POSIX-specific check, but may need to * generalize this for other platforms. */ #if USE_POSIX if (initialized_pid == getpid()) lose(CKR_CRYPTOKI_ALREADY_INITIALIZED); #endif /* * Sort out what the user wants to do about mutexes. Default is not * to use mutexes at all. * * There's a chicken and egg problem here: setting up the global * mutex and mutex function pointers creates a race condition, and * there's no obvious action we can take which is robust in the face * of pathological behavior by the caller such as simultaneous calls * to this method with incompatible mutex primitives. * * Given that (a) it's an error to call this method more than once * in the same process without an intervening F_Finalize() call, and * given that (b) we haven't actually promised to do any kind of * locking at all until this method returns CKR_OK, we punt * responsibility for this pathological case back to the caller. */ mutex_cb_create = NULL; mutex_cb_destroy = NULL; mutex_cb_lock = NULL; mutex_cb_unlock = NULL; if (a != NULL) { const int functions_provided = ((a->CreateMutex != NULL) + (a->DestroyMutex != NULL) + (a->LockMutex != NULL) + (a->UnlockMutex != NULL)); /* * Reserved is, um, reserved. * Mutex parameters must either all be present or all be absent. */ if (a->pReserved != NULL || (functions_provided & 3) != 0) lose(CKR_ARGUMENTS_BAD); /* * If the user provided mutex functions, use them. Otherwise, if * the user wants locking, use POSIX mutexes or return an error * depending on whether we have POSIX mutexes available. * Otherwise, we don't need to use mutexes. */ if (functions_provided) { mutex_cb_create = a->CreateMutex; mutex_cb_destroy = a->DestroyMutex; mutex_cb_lock = a->LockMutex; mutex_cb_unlock = a->UnlockMutex; } else if ((a->flags & CKF_OS_LOCKING_OK) != 0) { #if USE_PTHREADS mutex_cb_create = posix_mutex_create; mutex_cb_destroy = posix_mutex_destroy; mutex_cb_lock = posix_mutex_lock; mutex_cb_unlock = posix_mutex_unlock; #else lose(CKR_CANT_LOCK); #endif } } /* * Now that we know which mutex implementation to use, set up a * global mutex. We may want something finer grained later, but * this is enough to preserve the basic API semantics. * * Open question whether we should lock at this point, given that * until we return we haven't promised to do locking. Skip for now * as it's simpler, fix later if it turns out to be a problem. */ if ((rv = mutex_create(&p11_global_mutex)) != CKR_OK) goto fail; /* * Initialize libhal RPC channel. */ if (!hal_check(hal_rpc_client_init())) lose(CKR_GENERAL_ERROR); initialized_rpc = 1; /* * Initialize SQLite3, opening the database(s) and loading the * schema and views. */ if (!sql_init()) lose(CKR_GENERAL_ERROR); initialized_sql = 1; #if USE_POSIX initialized_pid = getpid(); #endif return CKR_OK; fail: if (initialized_sql) sql_fini(); if (initialized_rpc) hal_rpc_client_close(); return rv; } CK_RV C_Finalize(CK_VOID_PTR pReserved) { ENTER_PUBLIC_FUNCTION(C_Finalize); CK_RV rv = CKR_OK; if (pReserved != NULL) return CKR_ARGUMENTS_BAD; mutex_lock_or_return_failure(p11_global_mutex); /* * Destroy all current sessions. */ p11_session_delete_all(); /* * Shut down SQLite3. */ if (!sql_fini()) lose(CKR_GENERAL_ERROR); /* * By this point we're pretty well committed to shutting down, so * there's not much to be done if any of the rest of this fails. */ hal_rpc_client_close(); rv = mutex_unlock(p11_global_mutex); (void) mutex_destroy(p11_global_mutex); p11_global_mutex = NULL; #if USE_POSIX initialized_pid = 0; #endif return rv; fail: (void) mutex_unlock(p11_global_mutex); return rv; } CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) { ENTER_PUBLIC_FUNCTION(C_GetFunctionList); /* * Use pkcs11f.h to build dispatch vector for C_GetFunctionList(). * This should be const, but that's not what PKCS #11 says, oh well. * * This doesn't touch anything requiring locks, nor should it. */ static CK_FUNCTION_LIST ck_function_list = { { CRYPTOKI_VERSION_MAJOR, CRYPTOKI_VERSION_MINOR }, #define CK_PKCS11_FUNCTION_INFO(name) name, #include "pkcs11f.h" #undef CK_PKCS11_FUNCTION_INFO }; if (ppFunctionList == NULL) return CKR_ARGUMENTS_BAD; *ppFunctionList = &ck_function_list; return CKR_OK; } CK_RV C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount) { ENTER_PUBLIC_FUNCTION(C_GetSlotList); /* * We only have one slot, and it's hardwired. * No locking required here as long as this holds. */ if (pulCount == NULL) return CKR_ARGUMENTS_BAD; if (pSlotList != NULL && *pulCount < 1) return CKR_BUFFER_TOO_SMALL; if (p11_uninitialized()) return CKR_CRYPTOKI_NOT_INITIALIZED; *pulCount = 1; if (pSlotList != NULL) pSlotList[0] = P11_ONE_AND_ONLY_SLOT; return CKR_OK; } CK_RV C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) { ENTER_PUBLIC_FUNCTION(C_GetTokenInfo); /* * No locking required here as long as we're just returning constants. */ if (pInfo == NULL) return CKR_ARGUMENTS_BAD; if (slotID != P11_ONE_AND_ONLY_SLOT) return CKR_SLOT_ID_INVALID; if (p11_uninitialized()) return CKR_CRYPTOKI_NOT_INITIALIZED; memset(pInfo, 0, sizeof(*pInfo)); /* * No real idea (yet) how we get many of the following parameters. * * pInfo->label is supposed to be set when the token is initialized. * Not yet sure what that means in our context, but need something * here or the libhsm test programs will bomb trying to find the * right token, so hard-wire something for now. */ psnprintf(pInfo->label, sizeof(pInfo->label), "Cryptech Token"); psnprintf(pInfo->manufacturerID, sizeof(pInfo->manufacturerID), "Cryptech Project"); psnprintf(pInfo->model, sizeof(pInfo->model), "%04x%04x%04x%04x", P11_VERSION_HW_MAJOR, P11_VERSION_HW_MINOR, P11_VERSION_SW_MAJOR, P11_VERSION_SW_MINOR); psnprintf(pInfo->serialNumber, sizeof(pInfo->serialNumber), "007"); pInfo->flags = CKF_RNG | CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED | CKF_TOKEN_INITIALIZED; #warning Have not yet sorted out token flags #if 0 CKF_RNG CKF_WRITE_PROTECTED CKF_LOGIN_REQUIRED CKF_USER_PIN_INITIALIZED CKF_RESTORE_KEY_NOT_NEEDED CKF_CLOCK_ON_TOKEN CKF_PROTECTED_AUTHENTICATION_PATH CKF_DUAL_CRYPTO_OPERATIONS CKF_TOKEN_INITIALIZED CKF_SECONDARY_AUTHENTICATION CKF_USER_PIN_COUNT_LOW CKF_USER_PIN_FINAL_TRY CKF_USER_PIN_LOCKED CKF_USER_PIN_TO_BE_CHANGED CKF_SO_PIN_COUNT_LOW CKF_SO_PIN_FINAL_TRY CKF_SO_PIN_LOCKED CKF_SO_PIN_TO_BE_CHANGED CKF_ERROR_STATE #endif #warning Much of the TOKEN_INFO we return is nonsense pInfo->ulMaxSessionCount = CK_EFFECTIVELY_INFINITE; pInfo->ulSessionCount = CK_UNAVAILABLE_INFORMATION; pInfo->ulMaxRwSessionCount = CK_EFFECTIVELY_INFINITE; pInfo->ulRwSessionCount = CK_UNAVAILABLE_INFORMATION; pInfo->ulMaxPinLen = (CK_ULONG) hal_rpc_min_pin_length; pInfo->ulMinPinLen = (CK_ULONG) hal_rpc_max_pin_length; pInfo->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION; pInfo->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION; pInfo->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION; pInfo->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION; pInfo->hardwareVersion.major = P11_VERSION_HW_MAJOR; pInfo->hardwareVersion.minor = P11_VERSION_HW_MINOR; pInfo->firmwareVersion.major = P11_VERSION_SW_MAJOR; pInfo->firmwareVersion.minor = P11_VERSION_SW_MINOR; #warning Need to sort out hardware clock #if 0 /* * Eventually we expect cryptech devices to have their own hardware * clocks. Not implemented yet. */ pInfo->utcTime; #endif return CKR_OK; } CK_RV C_OpenSession(CK_SLOT_ID slotID, CK_FLAGS flags, CK_VOID_PTR pApplication, CK_NOTIFY Notify, CK_SESSION_HANDLE_PTR phSession) { ENTER_PUBLIC_FUNCTION(C_OpenSession); const int parallel_session = (flags & CKF_SERIAL_SESSION) == 0; const int read_only_session = (flags & CKF_RW_SESSION) == 0; p11_session_t *session = NULL; CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); if (slotID != P11_ONE_AND_ONLY_SLOT) lose(CKR_SLOT_ID_INVALID); if (phSession == NULL) lose(CKR_ARGUMENTS_BAD); if (parallel_session) lose(CKR_SESSION_PARALLEL_NOT_SUPPORTED); if ((session = p11_session_new()) == NULL) lose(CKR_HOST_MEMORY); switch (logged_in_as) { case not_logged_in: session->state = read_only_session ? CKS_RO_PUBLIC_SESSION : CKS_RW_PUBLIC_SESSION; break; case logged_in_as_user: session->state = read_only_session ? CKS_RO_USER_FUNCTIONS : CKS_RW_USER_FUNCTIONS; break; case logged_in_as_so: if (read_only_session) lose(CKR_SESSION_READ_WRITE_SO_EXISTS); session->state = CKS_RW_SO_FUNCTIONS; break; } session->notify = Notify; session->application = pApplication; if (!p11_session_add(session)) lose(CKR_FUNCTION_FAILED); assert(p11_session_consistent_login()); if ((rv = mutex_unlock(p11_global_mutex)) != CKR_OK) goto fail; *phSession = session->handle; return CKR_OK; fail: p11_session_free(session); (void) mutex_unlock(p11_global_mutex); return rv; } CK_RV C_CloseSession(CK_SESSION_HANDLE hSession) { ENTER_PUBLIC_FUNCTION(C_CloseSession); CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); rv = p11_session_delete(hSession); mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_CloseAllSessions(CK_SLOT_ID slotID) { ENTER_PUBLIC_FUNCTION(C_CloseAllSessions); if (slotID != P11_ONE_AND_ONLY_SLOT) return CKR_SLOT_ID_INVALID; mutex_lock_or_return_failure(p11_global_mutex); p11_session_delete_all(); return mutex_unlock(p11_global_mutex); } CK_RV C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) { ENTER_PUBLIC_FUNCTION(C_Login); const hal_client_handle_t client = {HAL_HANDLE_NONE}; p11_session_t *session; hal_user_t user = HAL_USER_NONE; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); if (pPin == NULL) lose(CKR_ARGUMENTS_BAD); /* * Mind, I don't really know why this function takes a session * handle, given that the semantics don't seem to call upon us to do * anything special for "this" session. */ if (p11_session_find(hSession) == NULL) lose(CKR_SESSION_HANDLE_INVALID); /* * Figure out which PIN we're checking. * We don't (yet?) support CKU_CONTEXT_SPECIFIC. * * We don't currently support re-login without an intervening * logout, so reject the login attempt if we're already logged in. * * Read-only SO is an illegal state, so reject the login attempt if * we have any read-only sessions and we're trying to log in as SO. */ switch (userType) { case CKU_USER: switch (logged_in_as) { case not_logged_in: break; case logged_in_as_user: lose(CKR_USER_ALREADY_LOGGED_IN); case logged_in_as_so: lose(CKR_USER_ANOTHER_ALREADY_LOGGED_IN); } user = HAL_USER_NORMAL; break; case CKU_SO: switch (logged_in_as) { case not_logged_in: break; case logged_in_as_so: lose(CKR_USER_ALREADY_LOGGED_IN); case logged_in_as_user: lose(CKR_USER_ANOTHER_ALREADY_LOGGED_IN); } for (session = p11_sessions; session != NULL; session = session->link) if (session->state == CKS_RO_PUBLIC_SESSION) lose(CKR_SESSION_READ_ONLY_EXISTS); user = HAL_USER_SO; break; case CKU_CONTEXT_SPECIFIC: lose(CKR_OPERATION_NOT_INITIALIZED); default: lose(CKR_USER_TYPE_INVALID); } /* * Try to log in the HSM. */ if ((rv = p11_whine_from_hal(hal_rpc_login(client, user, (char *) pPin, ulPinLen))) != CKR_OK) goto fail; /* * If we get here, the PIN was OK. Update global login state, then * whack every session into the correct new state. */ assert(p11_session_consistent_login()); logged_in_as = userType == CKU_SO ? logged_in_as_so : logged_in_as_user; for (session = p11_sessions; session != NULL; session = session->link) { switch (session->state) { case CKS_RO_PUBLIC_SESSION: assert(userType == CKU_USER); session->state = CKS_RO_USER_FUNCTIONS; continue; case CKS_RW_PUBLIC_SESSION: session->state = userType == CKU_SO ? CKS_RW_SO_FUNCTIONS : CKS_RW_USER_FUNCTIONS; continue; } } assert(p11_session_consistent_login()); fail: mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_Logout(CK_SESSION_HANDLE hSession) { ENTER_PUBLIC_FUNCTION(C_Logout); const hal_client_handle_t client = {HAL_HANDLE_NONE}; p11_session_t *session; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); /* * Mind, I don't really know why this function takes a session * handle, given that the semantics don't seem to call upon us to do * anything special for "this" session. */ if (p11_session_find(hSession) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (logged_in_as == not_logged_in) lose(CKR_USER_NOT_LOGGED_IN); /* * Update global login state, then delete any private objects and * whack every existing session into the right state. */ assert(p11_session_consistent_login()); if ((rv = p11_whine_from_hal(hal_rpc_logout(client))) != CKR_OK) goto fail; logged_in_as = not_logged_in; p11_object_delete_all_private(); for (session = p11_sessions; session != NULL; session = session->link) { switch (session->state) { case CKS_RO_USER_FUNCTIONS: session->state = CKS_RO_PUBLIC_SESSION; continue; case CKS_RW_USER_FUNCTIONS: case CKS_RW_SO_FUNCTIONS: session->state = CKS_RW_PUBLIC_SESSION; continue; } } assert(p11_session_consistent_login()); fail: mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject) { ENTER_PUBLIC_FUNCTION(C_CreateObject); CK_OBJECT_HANDLE handle = CK_INVALID_HANDLE; int in_transaction = 0; p11_session_t *session; CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pTemplate == NULL || phObject == NULL) lose(CKR_ARGUMENTS_BAD); const CK_OBJECT_CLASS * const cka_class = p11_attribute_find_value_in_template(CKA_CLASS, pTemplate, ulCount); const CK_KEY_TYPE * const cka_key_type = p11_attribute_find_value_in_template(CKA_KEY_TYPE, pTemplate, ulCount); const CK_BBOOL * const cka_token = p11_attribute_find_value_in_template(CKA_TOKEN, pTemplate, ulCount); if (cka_class == NULL) lose(CKR_TEMPLATE_INCOMPLETE); switch (*cka_class) { case CKO_PUBLIC_KEY: case CKO_PRIVATE_KEY: case CKO_SECRET_KEY: break; default: lose(CKR_TEMPLATE_INCONSISTENT); } if (cka_key_type == NULL) lose(CKR_TEMPLATE_INCOMPLETE); const p11_descriptor_t * const descriptor = p11_descriptor_from_key_type(*cka_class, *cka_key_type); if (descriptor == NULL) lose(CKR_TEMPLATE_INCONSISTENT); if ((rv = p11_check_create_attributes(session, pTemplate, ulCount, descriptor)) != CKR_OK) goto fail; const handle_flavor_t flavor = cka_token == NULL ? handle_flavor_session_object : p11_handle_flavor_from_cka_token(cka_token); switch (session->state) { case CKS_RO_PUBLIC_SESSION: case CKS_RO_USER_FUNCTIONS: if (flavor == handle_flavor_token_object) lose(CKR_SESSION_READ_ONLY); } if (!sql_exec("BEGIN")) lose(CKR_FUNCTION_FAILED); in_transaction = 1; if ((handle = p11_object_create(session, flavor, pTemplate, ulCount, descriptor, NULL)) == CK_INVALID_HANDLE) lose(CKR_FUNCTION_FAILED); if (!p11_attribute_set_bbool(handle, CKA_LOCAL, CK_FALSE)) lose(CKR_FUNCTION_FAILED); switch (*cka_class) { case CKO_PRIVATE_KEY: case CKO_SECRET_KEY: if (!p11_attribute_set_bbool(handle, CKA_ALWAYS_SENSITIVE, CK_FALSE) || !p11_attribute_set_bbool(handle, CKA_NEVER_EXTRACTABLE, CK_FALSE)) lose(CKR_FUNCTION_FAILED); } hal_key_flags_t flags = flavor == handle_flavor_token_object ? HAL_KEY_FLAG_TOKEN : 0; for (int i = 0; i < ulCount; i++) p11_attribute_apply_keyusage(&flags, pTemplate[i].type, pTemplate[i].pValue); if (*cka_class == CKO_PUBLIC_KEY && *cka_key_type == CKK_RSA && !p11_object_create_rsa_public_key(session, handle, flags)) lose(CKR_FUNCTION_FAILED); if (*cka_class == CKO_PUBLIC_KEY && *cka_key_type == CKK_EC && !p11_object_create_ec_public_key(session, handle, flags)) lose(CKR_FUNCTION_FAILED); if (*cka_class == CKO_PRIVATE_KEY && *cka_key_type == CKK_RSA && !p11_object_create_rsa_private_key(session, handle, flags, pTemplate, ulCount)) lose(CKR_FUNCTION_FAILED); if (*cka_class == CKO_PRIVATE_KEY && *cka_key_type == CKK_EC && !p11_object_create_ec_private_key(session, handle, flags, pTemplate, ulCount)) lose(CKR_FUNCTION_FAILED); if (!sql_exec("COMMIT")) lose(CKR_FUNCTION_FAILED); *phObject = handle; return mutex_unlock(p11_global_mutex); fail: if (in_transaction && !sql_exec("ROLLBACK")) rv = CKR_GENERAL_ERROR; mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_DestroyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject) { ENTER_PUBLIC_FUNCTION(C_DestroyObject); static const char delete_object[] = " DELETE FROM object WHERE object_handle = ?"; static const char delete_token_object[] = " DELETE FROM token_object" " WHERE token_object_id = (SELECT token_object_id FROM object WHERE object_handle = ?)"; hal_pkey_handle_t pkey = {HAL_HANDLE_NONE}; p11_session_t *session; sqlite3_stmt *q = NULL; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); session = p11_session_find(hSession); if ((rv = p11_object_check_rights(session, hObject, p11_object_access_write)) != CKR_OK) goto fail; if (p11_object_get_pkey_handle(session, hObject, &pkey) && !hal_check(hal_rpc_pkey_delete(pkey))) lose(CKR_FUNCTION_FAILED); if (is_token_handle(hObject) && (!sql_check_ok(sql_prepare(&q, delete_token_object)) || !sql_check_ok(sqlite3_bind_int64(q, 1, hObject)) || !sql_check_done(sqlite3_step(q)) || !sql_check_ok(sql_finalize_and_clear(&q)))) lose(CKR_FUNCTION_FAILED); if (!sql_check_ok(sql_prepare(&q, delete_object)) || !sql_check_ok(sqlite3_bind_int64(q, 1, hObject)) || !sql_check_done(sqlite3_step(q))) lose(CKR_FUNCTION_FAILED); fail: sqlite3_finalize(q); mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_GetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { ENTER_PUBLIC_FUNCTION(C_GetAttributeValue); static const char select_format[] = " SELECT value FROM %s_attribute NATURAL JOIN object" " WHERE object_handle = ?1 AND type = ?2"; const char *flavor = is_token_handle(hObject) ? "token" : "session"; p11_session_t *session; const p11_descriptor_t *descriptor = NULL; CK_BBOOL cka_sensitive, cka_extractable; CK_OBJECT_CLASS cka_class; CK_KEY_TYPE cka_key_type; int sensitive_object = 0; sqlite3_stmt *q = NULL; CK_RV rv; int ret, i; mutex_lock_or_return_failure(p11_global_mutex); if (pTemplate == NULL) lose(CKR_ARGUMENTS_BAD); session = p11_session_find(hSession); if ((rv = p11_object_check_rights(session, hObject, p11_object_access_read)) != CKR_OK) goto fail; if (!p11_attribute_get_ulong(hObject, CKA_CLASS, &cka_class)) lose(CKR_OBJECT_HANDLE_INVALID); switch (cka_class) { case CKO_PRIVATE_KEY: case CKO_SECRET_KEY: if (!p11_attribute_get_bbool(hObject, CKA_EXTRACTABLE, &cka_extractable) || !p11_attribute_get_bbool(hObject, CKA_SENSITIVE, &cka_sensitive)) lose(CKR_OBJECT_HANDLE_INVALID); sensitive_object = cka_sensitive || !cka_extractable; /* Fall through */ case CKO_PUBLIC_KEY: if (!p11_attribute_get_ulong(hObject, CKA_KEY_TYPE, &cka_key_type)) lose(CKR_OBJECT_HANDLE_INVALID); descriptor = p11_descriptor_from_key_type(cka_class, cka_key_type); } if (!sql_check_ok(sql_prepare(&q, select_format, flavor)) || !sql_check_ok(sqlite3_bind_int64(q, 1, hObject))) lose(CKR_FUNCTION_FAILED); rv = CKR_OK; for (i = 0; i < ulCount; i++) { if (sensitive_object && p11_attribute_is_sensitive(descriptor, pTemplate[i].type)) { pTemplate[i].ulValueLen = -1; rv = CKR_ATTRIBUTE_SENSITIVE; } else if (!sql_check_ok(sqlite3_reset(q)) || !sql_check_ok(sqlite3_bind_int64(q, 2, pTemplate[i].type)) || (ret = sqlite3_step(q)) != SQLITE_ROW) { if (ret != SQLITE_DONE) sql_whine_step(); pTemplate[i].ulValueLen = -1; rv = CKR_ATTRIBUTE_TYPE_INVALID; } else if (pTemplate[i].pValue == NULL) { pTemplate[i].ulValueLen = sqlite3_column_bytes(q, 0); } else if (pTemplate[i].ulValueLen >= sqlite3_column_bytes(q, 0)) { pTemplate[i].ulValueLen = sqlite3_column_bytes(q, 0); memcpy(pTemplate[i].pValue, sqlite3_column_blob(q, 0), pTemplate[i].ulValueLen); } else { pTemplate[i].ulValueLen = -1; rv = CKR_BUFFER_TOO_SMALL; } } fail: sqlite3_finalize(q); mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_FindObjectsInit(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { ENTER_PUBLIC_FUNCTION(C_FindObjectsInit); static const char select_missing[] = " WITH" " known AS (SELECT token_object_id FROM object WHERE token_object_id IS NOT NULL)" " SELECT token_object_id FROM token_object WHERE token_object_id NOT IN known"; static const char insert_missing[] = " INSERT INTO object (object_handle, token_object_id) VALUES (?1, ?2)"; static const char create_format[] = " CREATE TEMPORARY TABLE findobjects_%lu AS" " SELECT object_id FROM object NATURAL LEFT JOIN session" " WHERE session_handle IS NULL OR session_handle = ?1"; static const char drop_format[] = " DROP TABLE IF EXISTS findobjects_%lu"; static const char delete_format[] = " WITH" " matches AS (SELECT object_id" " FROM object NATURAL JOIN session_attribute" " WHERE type = ?1 AND value = ?2" " UNION" " SELECT object_id" " FROM object NATURAL JOIN token_attribute" " WHERE type = ?1 AND value = ?2)" " DELETE FROM findobjects_%lu WHERE object_id NOT IN matches"; static const char select_format[] = " SELECT object_handle FROM findobjects_%lu NATURAL JOIN object ORDER BY object_id"; p11_session_t *session; sqlite3_stmt *q1 = NULL, *q2 = NULL; CK_RV rv = CKR_OK; int i, ret; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (ulCount > 0 && pTemplate == NULL) lose(CKR_ARGUMENTS_BAD); if (session->find_query != NULL) lose(CKR_OPERATION_ACTIVE); /* * Assign handles to any token objects that don't have them yet. */ if (!sql_check_ok(sql_prepare(&q1, select_missing)) || !sql_check_ok(sql_prepare(&q2, insert_missing))) lose(CKR_FUNCTION_FAILED); while ((ret = sqlite3_step(q1)) == SQLITE_ROW) { sqlite3_int64 token_object_id = sqlite3_column_int64(q1, 0); CK_OBJECT_HANDLE object_handle = p11_allocate_unused_handle(handle_flavor_token_object); if (!sql_check_ok(sqlite3_reset(q2)) || !sql_check_ok(sqlite3_bind_int64(q2, 1, object_handle)) || !sql_check_ok(sqlite3_bind_int64(q2, 2, token_object_id)) || !sql_check_done(sqlite3_step(q2))) lose(CKR_FUNCTION_FAILED); } if (ret != SQLITE_DONE) { sql_whine_step(); lose(CKR_FUNCTION_FAILED); } /* * Create a temporary table to hold this session's FindObjects * state. Populate this with every object this session knows about, * then prune based on login status and whatever filter attributes * the caller supplied. */ if (!sql_check_ok(sql_finalize_and_clear(&q1)) || !sql_check_ok(sql_finalize_and_clear(&q2)) || !sql_check_ok(sql_prepare(&q1, drop_format, hSession)) || !sql_check_done(sqlite3_step(q1)) || !sql_check_ok(sql_prepare(&q2, create_format, hSession)) || !sql_check_ok(sqlite3_bind_int64(q2, 1, hSession)) || !sql_check_done(sqlite3_step(q2)) || !sql_check_ok(sql_finalize_and_clear(&q1)) || !sql_check_ok(sql_finalize_and_clear(&q2)) || !sql_check_ok(sql_prepare(&q1, delete_format, hSession))) lose(CKR_FUNCTION_FAILED); /* * If we're not logged in as the regular user, run an extra filter * cycle to remove all private objects before we get to the * caller-supplied template. */ if (logged_in_as != logged_in_as_user) { if (!sql_check_ok(sqlite3_bind_int64(q1, 1, CKA_PRIVATE)) || !sql_check_ok(sqlite3_bind_blob( q1, 2, &const_CK_FALSE, sizeof(const_CK_FALSE), NULL)) || !sql_check_done(sqlite3_step(q1))) lose(CKR_FUNCTION_FAILED); } /* * Filter through the caller-supplied template. * * NB: This doesn't support some of the more obscure searches, such * as searches for sessions or hardware features. Too much rope * already, worry about those if we ever really need them. */ for (i = 0; i < ulCount; i++) if (!sql_check_ok(sqlite3_reset(q1)) || !sql_check_ok(sqlite3_bind_int64(q1, 1, pTemplate[i].type)) || !sql_check_ok(sqlite3_bind_blob( q1, 2, pTemplate[i].pValue, pTemplate[i].ulValueLen, NULL)) || !sql_check_done(sqlite3_step(q1))) lose(CKR_FUNCTION_FAILED); /* * Stash a prepared query in the session object which will return * whatever object handles survived all that filtering. */ if (!sql_check_ok(sql_prepare(&session->find_query, select_format, hSession))) lose(CKR_FUNCTION_FAILED); session->find_query_done = 0; fail: sqlite3_finalize(q1); sqlite3_finalize(q2); mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_FindObjects(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE_PTR phObject, CK_ULONG ulMaxObjectCount, CK_ULONG_PTR pulObjectCount) { ENTER_PUBLIC_FUNCTION(C_FindObjects); p11_session_t *session; int i, ret = SQLITE_OK; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (session->find_query == NULL) lose(CKR_OPERATION_NOT_INITIALIZED); if (phObject == NULL || pulObjectCount == NULL) lose(CKR_ARGUMENTS_BAD); /* * C_FindObjectsInit() did all the heavy lifting, we just have to * return the resulting handles. */ i = 0; if (!session->find_query_done) while (i < ulMaxObjectCount && (ret = sqlite3_step(session->find_query)) == SQLITE_ROW) phObject[i++] = (CK_OBJECT_HANDLE) sqlite3_column_int64(session->find_query, 0); switch (ret) { case SQLITE_DONE: session->find_query_done = 1; break; case SQLITE_OK: case SQLITE_ROW: break; default: sql_whine_step(); lose(CKR_FUNCTION_FAILED); } *pulObjectCount = i; fail: mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE hSession) { ENTER_PUBLIC_FUNCTION(C_FindObjectsFinal); static const char drop_format[] = " DROP TABLE IF EXISTS findobjects_%lu"; p11_session_t *session; sqlite3_stmt *q = NULL; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (session->find_query == NULL) lose(CKR_OPERATION_NOT_INITIALIZED); /* * Clean up result query and temporary table. */ if (!sql_check_ok(sql_finalize_and_clear(&session->find_query)) || !sql_check_ok(sql_prepare(&q, drop_format, hSession)) || !sql_check_done(sqlite3_step(q))) lose(CKR_FUNCTION_FAILED); fail: sqlite3_finalize(q); mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_DigestInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism) { ENTER_PUBLIC_FUNCTION(C_DigestInit); hal_digest_algorithm_t algorithm; p11_session_t *session; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pMechanism == NULL) lose(CKR_ARGUMENTS_BAD); if (session->digest_algorithm != HAL_DIGEST_ALGORITHM_NONE) lose(CKR_OPERATION_ACTIVE); switch (pMechanism->mechanism) { case CKM_SHA_1: algorithm = HAL_DIGEST_ALGORITHM_SHA1; break; case CKM_SHA224: algorithm = HAL_DIGEST_ALGORITHM_SHA224; break; case CKM_SHA256: algorithm = HAL_DIGEST_ALGORITHM_SHA256; break; case CKM_SHA384: algorithm = HAL_DIGEST_ALGORITHM_SHA384; break; case CKM_SHA512: algorithm = HAL_DIGEST_ALGORITHM_SHA512; break; default: lose(CKR_MECHANISM_INVALID); } session->digest_algorithm = algorithm; return mutex_unlock(p11_global_mutex); fail: mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_Digest(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) { ENTER_PUBLIC_FUNCTION(C_Digest); p11_session_t *session; size_t digest_len; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pData == NULL || pulDigestLen == NULL) lose(CKR_ARGUMENTS_BAD); if (session->digest_algorithm == HAL_DIGEST_ALGORITHM_NONE) lose(CKR_OPERATION_NOT_INITIALIZED); if (session->digest_handle.handle != HAL_HANDLE_NONE) lose(CKR_OPERATION_ACTIVE); if (!hal_check(hal_rpc_hash_get_digest_length(session->digest_algorithm, &digest_len))) lose(CKR_FUNCTION_FAILED); rv = pDigest != NULL && *pulDigestLen < digest_len ? CKR_BUFFER_TOO_SMALL : CKR_OK; *pulDigestLen = digest_len; if (pDigest == NULL || rv == CKR_BUFFER_TOO_SMALL) mutex_unlock_return_with_rv(rv, p11_global_mutex); if ((rv = digest_update(session, session->digest_algorithm, &session->digest_handle, pData, ulDataLen)) != CKR_OK) goto fail; if (!hal_check(hal_rpc_hash_finalize(session->digest_handle, pDigest, *pulDigestLen))) lose(CKR_FUNCTION_FAILED); rv = CKR_OK; /* Fall through */ fail: if (session != NULL) { digest_cleanup(&session->digest_handle); session->digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_DigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { ENTER_PUBLIC_FUNCTION(C_DigestUpdate); p11_session_t *session; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pPart == NULL) lose(CKR_ARGUMENTS_BAD); if (session->digest_algorithm == HAL_DIGEST_ALGORITHM_NONE) lose(CKR_OPERATION_NOT_INITIALIZED); if ((rv = digest_update(session, session->digest_algorithm, &session->digest_handle, pPart, ulPartLen)) != CKR_OK) goto fail; return mutex_unlock(p11_global_mutex); fail: if (session != NULL) { digest_cleanup(&session->digest_handle); session->digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_DigestFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) { ENTER_PUBLIC_FUNCTION(C_DigestFinal); p11_session_t *session; size_t digest_len; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pulDigestLen == NULL) lose(CKR_ARGUMENTS_BAD); if (session->digest_algorithm == HAL_DIGEST_ALGORITHM_NONE || session->digest_handle.handle == HAL_HANDLE_NONE) lose(CKR_OPERATION_NOT_INITIALIZED); if (!hal_check(hal_rpc_hash_get_digest_length(session->digest_algorithm, &digest_len))) lose(CKR_FUNCTION_FAILED); rv = pDigest != NULL && *pulDigestLen < digest_len ? CKR_BUFFER_TOO_SMALL : CKR_OK; *pulDigestLen = digest_len; if (pDigest == NULL || rv == CKR_BUFFER_TOO_SMALL) mutex_unlock_return_with_rv(rv, p11_global_mutex); if (!hal_check(hal_rpc_hash_finalize(session->digest_handle, pDigest, *pulDigestLen))) lose(CKR_FUNCTION_FAILED); rv = CKR_OK; /* Fall through */ fail: if (session != NULL) { digest_cleanup(&session->digest_handle); session->digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_SignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { ENTER_PUBLIC_FUNCTION(C_SignInit); p11_session_t *session; CK_OBJECT_CLASS key_class; CK_KEY_TYPE key_type; CK_BBOOL key_sign; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pMechanism == NULL) lose(CKR_ARGUMENTS_BAD); if (session->sign_key_handle != CK_INVALID_HANDLE || session->sign_digest_algorithm != HAL_DIGEST_ALGORITHM_NONE) lose(CKR_OPERATION_ACTIVE); if ((rv = p11_object_check_rights(session, hKey, p11_object_access_read)) != CKR_OK) goto fail; if (!p11_attribute_get_ulong(hKey, CKA_CLASS, &key_class) || !p11_attribute_get_ulong(hKey, CKA_KEY_TYPE, &key_type) || !p11_attribute_get_bbool(hKey, CKA_SIGN, &key_sign) || key_class != CKO_PRIVATE_KEY) lose(CKR_KEY_HANDLE_INVALID); if (!key_sign) lose(CKR_KEY_FUNCTION_NOT_PERMITTED); switch (pMechanism->mechanism) { case CKM_RSA_PKCS: case CKM_SHA1_RSA_PKCS: case CKM_SHA224_RSA_PKCS: case CKM_SHA256_RSA_PKCS: case CKM_SHA384_RSA_PKCS: case CKM_SHA512_RSA_PKCS: if (key_type != CKK_RSA) lose(CKR_KEY_TYPE_INCONSISTENT); break; case CKM_ECDSA: case CKM_ECDSA_SHA224: case CKM_ECDSA_SHA256: case CKM_ECDSA_SHA384: case CKM_ECDSA_SHA512: if (key_type != CKK_EC) lose(CKR_KEY_TYPE_INCONSISTENT); break; default: return CKR_MECHANISM_INVALID; } session->sign_key_handle = hKey; switch (pMechanism->mechanism) { case CKM_RSA_PKCS: case CKM_ECDSA: session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; break; case CKM_SHA1_RSA_PKCS: session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA1; break; case CKM_SHA224_RSA_PKCS: case CKM_ECDSA_SHA224: session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA224; break; case CKM_SHA256_RSA_PKCS: case CKM_ECDSA_SHA256: session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA256; break; case CKM_SHA384_RSA_PKCS: case CKM_ECDSA_SHA384: session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA384; break; case CKM_SHA512_RSA_PKCS: case CKM_ECDSA_SHA512: session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA512; break; default: return CKR_MECHANISM_INVALID; } return mutex_unlock(p11_global_mutex); fail: if (session != NULL) { session->sign_key_handle = CK_INVALID_HANDLE; session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_Sign(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { ENTER_PUBLIC_FUNCTION(C_Sign); p11_session_t *session; CK_KEY_TYPE key_type; CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pData == NULL || pulSignatureLen == NULL) lose(CKR_ARGUMENTS_BAD); if (session->sign_key_handle == CK_INVALID_HANDLE) lose(CKR_OPERATION_NOT_INITIALIZED); if (session->sign_digest_handle.handle != HAL_HANDLE_NONE) lose(CKR_OPERATION_ACTIVE); if (!p11_attribute_get_ulong(session->sign_key_handle, CKA_KEY_TYPE, &key_type)) lose(CKR_FUNCTION_FAILED); if (session->sign_digest_algorithm != HAL_DIGEST_ALGORITHM_NONE && pSignature != NULL) { if ((rv = digest_update(session, session->sign_digest_algorithm, &session->sign_digest_handle, pData, ulDataLen)) != CKR_OK) goto fail; pData = NULL; ulDataLen = 0; } switch (key_type) { case CKK_RSA: case CKK_EC: rv = sign_hal_rpc(session, pData, ulDataLen, pSignature, pulSignatureLen); break; default: lose(CKR_FUNCTION_FAILED); } /* Fall through */ fail: if (session != NULL && pSignature != NULL && rv != CKR_BUFFER_TOO_SMALL) { session->sign_key_handle = CK_INVALID_HANDLE; session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; digest_cleanup(&session->sign_digest_handle); } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_SignUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { ENTER_PUBLIC_FUNCTION(C_SignUpdate); p11_session_t *session; CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pPart == NULL) lose(CKR_ARGUMENTS_BAD); if (session->sign_key_handle == CK_INVALID_HANDLE) lose(CKR_OPERATION_NOT_INITIALIZED); if (session->sign_digest_algorithm == HAL_DIGEST_ALGORITHM_NONE) lose(CKR_FUNCTION_FAILED); if ((rv = digest_update(session, session->sign_digest_algorithm, &session->sign_digest_handle, pPart, ulPartLen)) != CKR_OK) goto fail; return mutex_unlock(p11_global_mutex); fail: if (session != NULL) { session->sign_key_handle = CK_INVALID_HANDLE; session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; digest_cleanup(&session->sign_digest_handle); } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_SignFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { ENTER_PUBLIC_FUNCTION(C_SignFinal); p11_session_t *session; CK_KEY_TYPE key_type; CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pulSignatureLen == NULL) lose(CKR_ARGUMENTS_BAD); if (session->sign_key_handle == CK_INVALID_HANDLE || session->sign_digest_handle.handle == HAL_HANDLE_NONE) lose(CKR_OPERATION_NOT_INITIALIZED); if (!p11_attribute_get_ulong(session->sign_key_handle, CKA_KEY_TYPE, &key_type)) lose(CKR_FUNCTION_FAILED); switch (key_type) { case CKK_RSA: case CKK_EC: rv = sign_hal_rpc(session, NULL, 0, pSignature, pulSignatureLen); break; default: lose(CKR_FUNCTION_FAILED); } /* Fall through */ fail: if (session != NULL && pSignature != NULL && rv != CKR_BUFFER_TOO_SMALL) { session->sign_key_handle = CK_INVALID_HANDLE; session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; digest_cleanup(&session->sign_digest_handle); } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_VerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey ) { ENTER_PUBLIC_FUNCTION(C_VerifyInit); p11_session_t *session; CK_OBJECT_CLASS key_class; CK_KEY_TYPE key_type; CK_BBOOL key_verify; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pMechanism == NULL) lose(CKR_ARGUMENTS_BAD); if (session->verify_key_handle != CK_INVALID_HANDLE || session->verify_digest_algorithm != HAL_DIGEST_ALGORITHM_NONE) lose(CKR_OPERATION_ACTIVE); if ((rv = p11_object_check_rights(session, hKey, p11_object_access_read)) != CKR_OK) goto fail; if (!p11_attribute_get_ulong(hKey, CKA_CLASS, &key_class) || !p11_attribute_get_ulong(hKey, CKA_KEY_TYPE, &key_type) || !p11_attribute_get_bbool(hKey, CKA_VERIFY, &key_verify) || key_class != CKO_PUBLIC_KEY) lose(CKR_KEY_HANDLE_INVALID); if (!key_verify) lose(CKR_KEY_FUNCTION_NOT_PERMITTED); switch (pMechanism->mechanism) { case CKM_RSA_PKCS: case CKM_SHA1_RSA_PKCS: case CKM_SHA224_RSA_PKCS: case CKM_SHA256_RSA_PKCS: case CKM_SHA384_RSA_PKCS: case CKM_SHA512_RSA_PKCS: if (key_type != CKK_RSA) lose(CKR_KEY_TYPE_INCONSISTENT); break; case CKM_ECDSA: case CKM_ECDSA_SHA224: case CKM_ECDSA_SHA256: case CKM_ECDSA_SHA384: case CKM_ECDSA_SHA512: if (key_type != CKK_EC) lose(CKR_KEY_TYPE_INCONSISTENT); break; default: return CKR_MECHANISM_INVALID; } session->verify_key_handle = hKey; switch (pMechanism->mechanism) { case CKM_RSA_PKCS: case CKM_ECDSA: session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; break; case CKM_SHA1_RSA_PKCS: session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA1; break; case CKM_SHA224_RSA_PKCS: case CKM_ECDSA_SHA224: session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA224; break; case CKM_SHA256_RSA_PKCS: case CKM_ECDSA_SHA256: session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA256; break; case CKM_SHA384_RSA_PKCS: case CKM_ECDSA_SHA384: session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA384; break; case CKM_SHA512_RSA_PKCS: case CKM_ECDSA_SHA512: session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA512; break; default: return CKR_MECHANISM_INVALID; } return mutex_unlock(p11_global_mutex); fail: if (session != NULL) { session->verify_key_handle = CK_INVALID_HANDLE; session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { ENTER_PUBLIC_FUNCTION(C_Verify); p11_session_t *session; CK_KEY_TYPE key_type; CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pData == NULL || pSignature == NULL) lose(CKR_ARGUMENTS_BAD); if (session->verify_key_handle == CK_INVALID_HANDLE) lose(CKR_OPERATION_NOT_INITIALIZED); if (session->verify_digest_algorithm != HAL_DIGEST_ALGORITHM_NONE) { if ((rv = digest_update(session, session->verify_digest_algorithm, &session->verify_digest_handle, pData, ulDataLen)) != CKR_OK) goto fail; pData = NULL; ulDataLen = 0; } if (!p11_attribute_get_ulong(session->verify_key_handle, CKA_KEY_TYPE, &key_type)) lose(CKR_FUNCTION_FAILED); switch (key_type) { case CKK_RSA: case CKK_EC: rv = verify_hal_rpc(session, pData, ulDataLen, pSignature, ulSignatureLen); break; default: lose(CKR_FUNCTION_FAILED); } fail: /* Fall through */ if (session != NULL) { session->verify_key_handle = CK_INVALID_HANDLE; session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; digest_cleanup(&session->verify_digest_handle); } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_VerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { ENTER_PUBLIC_FUNCTION(C_VerifyUpdate); p11_session_t *session; CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pPart == NULL) lose(CKR_ARGUMENTS_BAD); if (session->verify_key_handle == CK_INVALID_HANDLE) lose(CKR_OPERATION_NOT_INITIALIZED); if (session->verify_digest_algorithm == HAL_DIGEST_ALGORITHM_NONE) lose(CKR_FUNCTION_FAILED); if ((rv = digest_update(session, session->verify_digest_algorithm, &session->verify_digest_handle, pPart, ulPartLen)) != CKR_OK) goto fail; return mutex_unlock(p11_global_mutex); fail: if (session != NULL) { session->verify_key_handle = CK_INVALID_HANDLE; session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; digest_cleanup(&session->verify_digest_handle); } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_VerifyFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { ENTER_PUBLIC_FUNCTION(C_VerifyFinal); p11_session_t *session; CK_KEY_TYPE key_type; CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pSignature == NULL) lose(CKR_ARGUMENTS_BAD); if (session->verify_key_handle == CK_INVALID_HANDLE || session->verify_digest_handle.handle == HAL_HANDLE_NONE) lose(CKR_OPERATION_NOT_INITIALIZED); if (!p11_attribute_get_ulong(session->verify_key_handle, CKA_KEY_TYPE, &key_type)) lose(CKR_FUNCTION_FAILED); switch (key_type) { case CKK_RSA: case CKK_EC: rv = verify_hal_rpc(session, NULL, 0, pSignature, ulSignatureLen); break; default: lose(CKR_FUNCTION_FAILED); } fail: /* Fall through */ if (session != NULL) { session->verify_key_handle = CK_INVALID_HANDLE; session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; digest_cleanup(&session->verify_digest_handle); } mutex_unlock_return_with_rv(rv, p11_global_mutex); } /* * If there's any method in this entire package which really needs a * more complex mutex structure than the single global mutex, it's * probably this one. Key generation can take a looooong time. * Drive off that bridge when we get to it. */ CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPublicKeyTemplate, CK_ULONG ulPublicKeyAttributeCount, CK_ATTRIBUTE_PTR pPrivateKeyTemplate, CK_ULONG ulPrivateKeyAttributeCount, CK_OBJECT_HANDLE_PTR phPublicKey, CK_OBJECT_HANDLE_PTR phPrivateKey) { ENTER_PUBLIC_FUNCTION(C_GenerateKeyPair); p11_session_t *session; CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pMechanism == NULL || pPublicKeyTemplate == NULL || phPublicKey == NULL || pPrivateKeyTemplate == NULL || phPrivateKey == NULL) lose(CKR_ARGUMENTS_BAD); switch (pMechanism->mechanism) { case CKM_RSA_PKCS_KEY_PAIR_GEN: rv = generate_keypair(session, pMechanism, generate_keypair_rsa_pkcs, pPublicKeyTemplate, ulPublicKeyAttributeCount, &p11_descriptor_rsa_public_key, phPublicKey, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, &p11_descriptor_rsa_private_key, phPrivateKey); break; case CKM_EC_KEY_PAIR_GEN: rv = generate_keypair(session, pMechanism, generate_keypair_ec, pPublicKeyTemplate, ulPublicKeyAttributeCount, &p11_descriptor_ec_public_key, phPublicKey, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, &p11_descriptor_ec_private_key, phPrivateKey); break; default: lose(CKR_MECHANISM_INVALID); } fail: mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_GenerateRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR RandomData, CK_ULONG ulRandomLen) { ENTER_PUBLIC_FUNCTION(C_GenerateRandom); p11_session_t *session; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (RandomData == NULL) lose(CKR_ARGUMENTS_BAD); if (!hal_check(hal_rpc_get_random(RandomData, ulRandomLen))) lose(CKR_FUNCTION_FAILED); fail: mutex_unlock_return_with_rv(rv, p11_global_mutex); } /* * Supply information about a particular mechanism. We may want a * more generic structure for this, for the moment, just answer the * questions that applications we care about are asking. * * Not really sure whether I should be setting CKF_HW here or not, RSA * is a mix of hardware and software at the moment, but I'm also a * little unclear on what "the device" means in this context, so let's * just say that if it's implemented by libhal or the Verilog hiding * behind libhal, it's implemented in hardware. */ CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR pInfo) { ENTER_PUBLIC_FUNCTION(C_GetMechanismInfo); const CK_ULONG rsa_key_min = 1024; const CK_ULONG rsa_key_max = 8192; const CK_ULONG ec_key_min = 256; const CK_ULONG ec_key_max = 521; /* * No locking here, no obvious need for it. */ if (pInfo == NULL) return CKR_ARGUMENTS_BAD; if (slotID != P11_ONE_AND_ONLY_SLOT) return CKR_SLOT_ID_INVALID; if (p11_uninitialized()) return CKR_CRYPTOKI_NOT_INITIALIZED; #if 0 /* * Perhaps revisit this after adding an RPC call to let us check * which cores are available. For now, given that we now have * software core support for these hash algorithms, this test isn't * particularly useful. */ hal_digest_algorithm_t algorithm = HAL_DIGEST_ALGORITHM_NONE; CK_RV rv = CKR_OK; switch (type) { case CKM_SHA_1: case CKM_SHA1_RSA_PKCS: case CKM_SHA_1_HMAC: algorithm = HAL_DIGEST_ALGORITHM_SHA1; break; case CKM_SHA224: case CKM_SHA224_RSA_PKCS: case CKM_SHA224_HMAC: case CKM_ECDSA_SHA224: algorithm = HAL_DIGEST_ALGORITHM_SHA224; break; case CKM_SHA256: case CKM_SHA256_RSA_PKCS: case CKM_SHA256_HMAC: case CKM_ECDSA_SHA256: algorithm = HAL_DIGEST_ALGORITHM_SHA256; break; case CKM_SHA384: case CKM_SHA384_RSA_PKCS: case CKM_SHA384_HMAC: case CKM_ECDSA_SHA384: algorithm = HAL_DIGEST_ALGORITHM_SHA384; break; case CKM_SHA512: case CKM_SHA512_RSA_PKCS: case CKM_SHA512_HMAC: case CKM_ECDSA_SHA512: algorithm = HAL_DIGEST_ALGORITHM_SHA512; break; default: break; } if (algorithm != HAL_DIGEST_ALGORITHM_NONE && (rv = digest_available(algorithm)) != CKR_OK) return rv; #endif switch (type) { case CKM_RSA_PKCS_KEY_PAIR_GEN: pInfo->ulMinKeySize = rsa_key_min; pInfo->ulMaxKeySize = rsa_key_max; pInfo->flags = CKF_HW | CKF_GENERATE_KEY_PAIR; break; case CKM_EC_KEY_PAIR_GEN: pInfo->ulMinKeySize = ec_key_min; pInfo->ulMaxKeySize = ec_key_max; pInfo->flags = CKF_HW | CKF_GENERATE_KEY_PAIR | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS; break; case CKM_RSA_PKCS: case CKM_SHA1_RSA_PKCS: case CKM_SHA224_RSA_PKCS: case CKM_SHA256_RSA_PKCS: case CKM_SHA384_RSA_PKCS: case CKM_SHA512_RSA_PKCS: pInfo->ulMinKeySize = rsa_key_min; pInfo->ulMaxKeySize = rsa_key_max; pInfo->flags = CKF_HW | CKF_SIGN | CKF_VERIFY; break; case CKM_ECDSA: case CKM_ECDSA_SHA224: case CKM_ECDSA_SHA256: case CKM_ECDSA_SHA384: case CKM_ECDSA_SHA512: pInfo->ulMinKeySize = ec_key_min; pInfo->ulMaxKeySize = ec_key_max; pInfo->flags = CKF_HW | CKF_SIGN | CKF_VERIFY | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS; break; case CKM_SHA_1: case CKM_SHA224: case CKM_SHA256: case CKM_SHA384: case CKM_SHA512: pInfo->ulMinKeySize = 0; pInfo->ulMaxKeySize = 0; pInfo->flags = CKF_HW | CKF_DIGEST; break; #if 0 /* * We have Verilog and libhal for these, but no PKCS #11 support (yet). */ case CKM_SHA_1_HMAC: case CKM_SHA224_HMAC: case CKM_SHA256_HMAC: case CKM_SHA384_HMAC: case CKM_SHA512_HMAC: #endif default: return CKR_MECHANISM_INVALID; } return CKR_OK; } CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo) { ENTER_PUBLIC_FUNCTION(C_GetSessionInfo); p11_session_t *session; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); if (pInfo == NULL) lose(CKR_ARGUMENTS_BAD); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); pInfo->slotID = P11_ONE_AND_ONLY_SLOT; pInfo->state = session->state; pInfo->flags = CKF_SERIAL_SESSION; pInfo->ulDeviceError = 0; switch (session->state) { case CKS_RW_PUBLIC_SESSION: case CKS_RW_SO_FUNCTIONS: case CKS_RW_USER_FUNCTIONS: pInfo->flags |= CKF_RW_SESSION; default: break; } fail: mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_GetInfo(CK_INFO_PTR pInfo) { ENTER_PUBLIC_FUNCTION(C_GetInfo); if (pInfo == NULL) return CKR_ARGUMENTS_BAD; if (p11_uninitialized()) return CKR_CRYPTOKI_NOT_INITIALIZED; memset(pInfo, 0, sizeof(*pInfo)); pInfo->cryptokiVersion.major = 2; pInfo->cryptokiVersion.minor = 30; psnprintf(pInfo->manufacturerID, sizeof(pInfo->manufacturerID), "cryptech.is project"); psnprintf(pInfo->libraryDescription, sizeof(pInfo->libraryDescription), "cryptech.is pkcs11"); pInfo->libraryVersion.major = P11_VERSION_SW_MAJOR; pInfo->libraryVersion.minor = P11_VERSION_SW_MINOR; return CKR_OK; } CK_RV C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) { ENTER_PUBLIC_FUNCTION(C_GetSlotInfo); if (pInfo == NULL) return CKR_ARGUMENTS_BAD; if (slotID != P11_ONE_AND_ONLY_SLOT) return CKR_SLOT_ID_INVALID; if (p11_uninitialized()) return CKR_CRYPTOKI_NOT_INITIALIZED; memset(pInfo, 0, sizeof(*pInfo)); psnprintf(pInfo->slotDescription, sizeof(pInfo->slotDescription), "cryptech.is slot on alpha"); psnprintf(pInfo->manufacturerID, sizeof(pInfo->manufacturerID), "cryptech.is project"); pInfo->flags = CKF_TOKEN_PRESENT | CKF_HW_SLOT; pInfo->hardwareVersion.major = 0; pInfo->hardwareVersion.minor = 2; pInfo->firmwareVersion.major = P11_VERSION_HW_MAJOR; pInfo->firmwareVersion.minor = P11_VERSION_HW_MINOR; return CKR_OK; } CK_RV C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount) { static const CK_MECHANISM_TYPE mechanisms[] = { CKM_ECDSA_SHA224, CKM_ECDSA_SHA256, CKM_ECDSA_SHA384, CKM_ECDSA_SHA512, CKM_ECDSA, CKM_EC_KEY_PAIR_GEN, CKM_SHA1_RSA_PKCS, CKM_SHA224_RSA_PKCS, CKM_SHA256_RSA_PKCS, CKM_SHA384_RSA_PKCS, CKM_SHA512_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_PKCS_KEY_PAIR_GEN, CKM_SHA_1, CKM_SHA224, CKM_SHA256, CKM_SHA384, CKM_SHA512, #if 0 /* libhal support these but pkcs11 doesn't, yet */ CKM_SHA_1_HMAC, CKM_SHA224_HMAC, CKM_SHA256_HMAC, CKM_SHA384_HMAC, CKM_SHA512_HMAC, #endif }; const CK_ULONG mechanisms_len = sizeof(mechanisms)/sizeof(*mechanisms); ENTER_PUBLIC_FUNCTION(C_GetMechanismList); if (pulCount == NULL) return CKR_ARGUMENTS_BAD; if (slotID != P11_ONE_AND_ONLY_SLOT) return CKR_SLOT_ID_INVALID; if (p11_uninitialized()) return CKR_CRYPTOKI_NOT_INITIALIZED; CK_RV rv = CKR_OK; if (pMechanismList != NULL && *pulCount < mechanisms_len) rv = CKR_BUFFER_TOO_SMALL; else if (pMechanismList != NULL) memcpy(pMechanismList, mechanisms, sizeof(mechanisms)); *pulCount = mechanisms_len; return rv; } CK_RV C_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, CK_ULONG ulSeedLen) { ENTER_PUBLIC_FUNCTION(C_SeedRandom); if (p11_uninitialized()) return CKR_CRYPTOKI_NOT_INITIALIZED; return CKR_RANDOM_SEED_NOT_SUPPORTED; } /* * Legacy functions. These are basically just unimplemented functions * which return a different error code to keep test suites happy. */ CK_RV C_GetFunctionStatus(CK_SESSION_HANDLE hSession) { ENTER_PUBLIC_FUNCTION(C_GetFunctionStatus); if (p11_uninitialized()) return CKR_CRYPTOKI_NOT_INITIALIZED; return CKR_FUNCTION_NOT_PARALLEL; } CK_RV C_CancelFunction(CK_SESSION_HANDLE hSession) { ENTER_PUBLIC_FUNCTION(C_CancelFunction); if (p11_uninitialized()) return CKR_CRYPTOKI_NOT_INITIALIZED; return CKR_FUNCTION_NOT_PARALLEL; } /* * Stubs for unsupported functions below here. Per the PKCS #11 * specification, it's OK to skip implementing almost any function in * the API, but if one does so, one must provide a stub which returns * CKR_FUNCTION_NOT_SUPPORTED, because every slot in the dispatch * vector must be populated. We could reuse a single stub for all the * unimplemented slots, but the type signatures wouldn't match, which * would require some nasty casts I'd rather avoid. * * Many of these functions would be straightforward to implement, but * there are enough bald yaks in this saga already. */ CK_RV C_GenerateKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phKey) { UNSUPPORTED_FUNCTION(C_GenerateKey); } CK_RV C_InitToken(CK_SLOT_ID slotID, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, CK_UTF8CHAR_PTR pLabel) { UNSUPPORTED_FUNCTION(C_InitToken); } CK_RV C_InitPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) { UNSUPPORTED_FUNCTION(C_InitPIN); } CK_RV C_SetPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pOldPin, CK_ULONG ulOldLen, CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewLen) { UNSUPPORTED_FUNCTION(C_SetPIN); } CK_RV C_GetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, CK_ULONG_PTR pulOperationStateLen) { UNSUPPORTED_FUNCTION(C_GetOperationState); } CK_RV C_SetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey) { UNSUPPORTED_FUNCTION(C_SetOperationState); } CK_RV C_CopyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phNewObject) { UNSUPPORTED_FUNCTION(C_CopyObject); } CK_RV C_GetObjectSize(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ULONG_PTR pulSize) { UNSUPPORTED_FUNCTION(C_GetObjectSize); } CK_RV C_SetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { UNSUPPORTED_FUNCTION(C_SetAttributeValue); } CK_RV C_EncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { UNSUPPORTED_FUNCTION(C_EncryptInit); } CK_RV C_Encrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) { UNSUPPORTED_FUNCTION(C_Encrypt); } CK_RV C_EncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) { UNSUPPORTED_FUNCTION(C_EncryptUpdate); } CK_RV C_EncryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastEncryptedPart, CK_ULONG_PTR pulLastEncryptedPartLen) { UNSUPPORTED_FUNCTION(C_EncryptFinal); } CK_RV C_DecryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { UNSUPPORTED_FUNCTION(C_DecryptInit); } CK_RV C_Decrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { UNSUPPORTED_FUNCTION(C_Decrypt); } CK_RV C_DecryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) { UNSUPPORTED_FUNCTION(C_DecryptUpdate); } CK_RV C_DecryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastPart, CK_ULONG_PTR pulLastPartLen) { UNSUPPORTED_FUNCTION(C_DecryptFinal); } CK_RV C_DigestKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey) { UNSUPPORTED_FUNCTION(C_DigestKey); } CK_RV C_SignRecoverInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { UNSUPPORTED_FUNCTION(C_SignRecoverInit); } CK_RV C_SignRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { UNSUPPORTED_FUNCTION(C_SignRecover); } CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { UNSUPPORTED_FUNCTION(C_VerifyRecoverInit); } CK_RV C_VerifyRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { UNSUPPORTED_FUNCTION(C_VerifyRecover); } CK_RV C_DigestEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) { UNSUPPORTED_FUNCTION(C_DigestEncryptUpdate); } CK_RV C_DecryptDigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) { UNSUPPORTED_FUNCTION(C_DecryptDigestUpdate); } CK_RV C_SignEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) { UNSUPPORTED_FUNCTION(C_SignEncryptUpdate); } CK_RV C_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) { UNSUPPORTED_FUNCTION(C_DecryptVerifyUpdate); } 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) { UNSUPPORTED_FUNCTION(C_WrapKey); } 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) { UNSUPPORTED_FUNCTION(C_UnwrapKey); } 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) { UNSUPPORTED_FUNCTION(C_DeriveKey); } CK_RV C_WaitForSlotEvent(CK_FLAGS flags, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR pRserved) { UNSUPPORTED_FUNCTION(C_WaitForSlotEvent); } /* * "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: */