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