/* * pkcs11.c * -------- * * This is a partial implementation of PKCS #11 on top of the Cryptech * libhal library connecting to the Cryptech FPGA cores. * * Author: Rob Austein * Copyright (c) 2015-2016, NORDUnet A/S * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * - Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of the NORDUnet nor the names of its contributors may * be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include /* * Magic PKCS #11 macros that must be defined before including * pkcs11.h. For now these are only the Unix versions, add others * later (which may require minor refactoring). */ #define CK_PTR * #define CK_DEFINE_FUNCTION(returnType, name) returnType name #define CK_DECLARE_FUNCTION(returnType, name) returnType name #define CK_DECLARE_FUNCTION_POINTER(returnType, name) returnType (* name) #define CK_CALLBACK_FUNCTION(returnType, name) returnType (* name) #ifndef NULL_PTR #define NULL_PTR NULL #endif #include "pkcs11.h" #include "attributes.h" /* * This PKCS #11 implementation is hardwired with one slot, the token * for which is always present (so we return the same answer * regardless of the value of tokenPresent). */ #define P11_ONE_AND_ONLY_SLOT 0 /* * How many sessions and object handles to allow. We could do this * with dynamic memory, but static arrays are simpler and faster. We * don't expect all that many sessions, and slots in the object table * are cheap. */ #ifndef P11_MAX_SESSION_HANDLES #define P11_MAX_SESSION_HANDLES (64) #endif #ifndef P11_MAX_OBJECT_HANDLES #define P11_MAX_OBJECT_HANDLES (4096) #endif /* * Manufacturer ID, version numbers (hardware, firmware, software), etc. * Some of this really should be coming from RPC queries. */ #define P11_MANUFACTURER_ID "Cryptech Project" #define P11_TOKEN_LABEL "Cryptech Token" #define P11_BOARD_MODEL "Alpha Board" #define P11_BOARD_SERIAL "007" #define P11_LIBRARY_DESCRIPTION "libcryptech-pkcs11.so" #define P11_SLOT_DESCRIPTION "Cryptech Alpha slot" #define P11_VERSION_HW_MAJOR 0 #define P11_VERSION_HW_MINOR 3 #define P11_VERSION_FW_MAJOR 3 #define P11_VERSION_FW_MINOR 0 #define P11_VERSION_SW_MAJOR 3 #define P11_VERSION_SW_MINOR 0 /* * Debugging control. */ #ifndef DEBUG_HAL #define DEBUG_HAL 0 #endif #ifndef DEBUG_PKCS11 #define DEBUG_PKCS11 0 #endif /* * Whether to include POSIX-specific features. */ #ifndef USE_POSIX #define USE_POSIX 1 #endif /* * Whether to use POSIX threads. */ #ifndef USE_PTHREADS #define USE_PTHREADS USE_POSIX #endif #if USE_PTHREADS && !USE_POSIX #error Can not use POSIX threads without using POSIX #endif #if USE_POSIX #include #include #endif #if USE_PTHREADS #include #endif /* * PKCS #11 sessions. 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 */ CK_STATE state; /* State (CKS_*) of this session */ CK_NOTIFY notify; /* Notification callback */ CK_VOID_PTR application; /* Application data */ hal_pkey_attribute_t *find_query; /* FindObject*() query state */ unsigned find_query_token : 1; /* Find query for token objects in progress */ unsigned find_query_session : 1; /* Find query for session objects in progress */ unsigned find_query_n : 30; /* Number of entries in find_query */ hal_uuid_t find_query_previous_uuid; /* Previous UUID for find queries */ unsigned find_query_state; /* hal_rpc_pkey_match() internal state */ hal_digest_algorithm_t digest_algorithm, /* Hash algorithm for C_Digest*() */ sign_digest_algorithm, /* Hash algorithm for C_Sign*() */ verify_digest_algorithm; /* Hash algorithm for C_Verify*() */ CK_OBJECT_HANDLE sign_key_handle, /* Private key for C_Sign*() */ verify_key_handle; /* Public key for C_Verify() */ hal_hash_handle_t digest_handle, /* Hash state for C_Digest*() */ sign_digest_handle, /* Hash state for C_Sign*() */ verify_digest_handle; /* Hash state for C_Verify*() */ } p11_session_t; /* * PKCS #11 objects. These are pretty simple, as they're really just * mappings from PKCS #11's naming scheme to libhal UUIDs, with a little * extra fun for PKCS #11 "session" objects. */ typedef struct p11_object { CK_OBJECT_HANDLE handle; /* Object handle */ CK_SESSION_HANDLE session; /* Associated session (if any) */ hal_uuid_t uuid; /* libhal key UUID */ } p11_object_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 which live on the * token, ie, in non-volatile storage) and session object handles * (handles for objects which live only as long as the session does), * and we steal two bits of the handle as as flags to distinguish * between these three kinds handles. We sub-divide the rest of a * handle into a nonce (well, a lame one -- for now this is just a * counter, if this becomes an issue we could do better) and an array * index into the relevant table. */ typedef enum { handle_flavor_none = 0, /* Matches CK_INVALID_HANDLE */ handle_flavor_session = 1, handle_flavor_token_object = 2, handle_flavor_session_object = 3 } handle_flavor_t; #define HANDLE_MASK_FLAVOR (0xc0000000) #define HANDLE_MASK_NONCE (0x3fff0000) #define HANDLE_MASK_INDEX (0x0000ffff) /* * 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 and object handles for this application. */ static p11_session_t p11_sessions [P11_MAX_SESSION_HANDLES]; static p11_object_t p11_objects [P11_MAX_OBJECT_HANDLES]; static unsigned p11_object_uuids [P11_MAX_OBJECT_HANDLES]; static unsigned p11_sessions_in_use, p11_objects_in_use; /* * Mutex callbacks. */ static CK_CREATEMUTEX mutex_cb_create; static CK_DESTROYMUTEX mutex_cb_destroy; static CK_LOCKMUTEX mutex_cb_lock; static CK_UNLOCKMUTEX mutex_cb_unlock; /* * Global mutex. We may want something finer grained later, but this * will suffice to comply with the API requirements. */ static CK_VOID_PTR p11_global_mutex; /* * (POSIX-specific) process which last called C_Initialize(). */ #if USE_POSIX static pid_t initialized_pid; #endif /* * Syntactic sugar for functions returning CK_RV complex enough to * need cleanup actions on failure. Also does very basic logging for * debug-by-printf(). * * NB: This uses a variable ("rv") and a goto target ("fail") which * must be defined in the calling environment. We could make these * arguments to the macro, but doing so would make the code less * readable without significantly reducing the voodoo factor. */ #if DEBUG_PKCS11 #define lose(_ck_rv_code_) \ do { \ rv = (_ck_rv_code_); \ fprintf(stderr, "\n%s:%u: %s\n", __FILE__, __LINE__, #_ck_rv_code_); \ goto fail; \ } while (0) #else /* DEBUG_PKCS11 */ #define lose(_ck_rv_code_) \ do { \ rv = (_ck_rv_code_); \ goto fail; \ } while (0) #endif /* DEBUG_PKCS11 */ /* * More debug-by-printf() support. One would like to consider this a * relic of the previous millenium, but, sadly, broken debugging * environments are still all too common. */ #if DEBUG_PKCS11 > 1 #define ENTER_PUBLIC_FUNCTION(_name_) \ fprintf(stderr, "\nEntering function %s\n", #_name_) #else /* DEBUG_PKCS11 > 1 */ #define ENTER_PUBLIC_FUNCTION(_name_) #endif /* DEBUG_PKCS11 > 1 */ /* * Error checking for libhal calls. */ #define hal_whine(_expr_) (_hal_whine((_expr_), #_expr_, __FILE__, __LINE__, HAL_OK)) #define hal_whine_allow(_expr_, ...) (_hal_whine((_expr_), #_expr_, __FILE__, __LINE__, __VA_ARGS__, HAL_OK)) #define hal_check(_expr_) (hal_whine(_expr_) == HAL_OK) #if DEBUG_HAL static inline hal_error_t _hal_whine(const hal_error_t err, const char * const expr, const char * const file, const unsigned line, ...) { va_list ap; int ok = 0; hal_error_t code; va_start(ap, line); do { code = va_arg(ap, hal_error_t); ok |= (err == code); } while (code != HAL_OK); va_end(ap); if (!ok) fprintf(stderr, "\n%s:%u: %s returned %s\n", file, line, expr, hal_error_string(err)); return err; } #else /* DEBUG_HAL */ #define _hal_whine(_expr_, ...) (_expr_) #endif /* DEBUG_HAL */ /* * Error translation fun for the entire family! */ #if DEBUG_PKCS11 || DEBUG_HAL #define hal_p11_error_case(_hal_err_, _p11_err_) \ case _hal_err_: fprintf(stderr, "\n%s:%u: Mapping %s to %s\n", file, line, #_hal_err_, #_p11_err_); return _p11_err_; #else #define hal_p11_error_case(_hal_err_, _p11_err_) \ case _hal_err_: return _p11_err_; #endif #define p11_error_from_hal(_hal_err_) \ (_p11_error_from_hal((_hal_err_), __FILE__, __LINE__)) #define p11_whine_from_hal(_expr_) \ (_p11_error_from_hal(_hal_whine((_expr_), #_expr_, __FILE__, __LINE__, HAL_OK), __FILE__, __LINE__)) static CK_RV _p11_error_from_hal(const hal_error_t err, const char * const file, const unsigned line) { switch (err) { hal_p11_error_case(HAL_ERROR_PIN_INCORRECT, CKR_PIN_INCORRECT); hal_p11_error_case(HAL_ERROR_INVALID_SIGNATURE, CKR_SIGNATURE_INVALID); /* * More here later, first see if this compiles. */ case HAL_OK: return CKR_OK; default: #if DEBUG_PKCS11 || DEBUG_HAL fprintf(stderr, "\n%s:%u: Mapping unhandled HAL error to CKR_FUNCTION_FAILED\n", file, line); #endif return CKR_FUNCTION_FAILED; } } #undef hal_p11_error_case /* * All (?) public functions should test whether we've been initialized or not. * Where practical, we bury this check in other boilerplate. */ #if USE_POSIX #define p11_uninitialized() (!initialized_pid) #else #define p11_uninitialized() (0) #endif /* * Handle unsupported functions. */ #define UNSUPPORTED_FUNCTION(_name_) \ do { \ ENTER_PUBLIC_FUNCTION(_name_); \ if (p11_uninitialized()) \ return CKR_CRYPTOKI_NOT_INITIALIZED; \ return CKR_FUNCTION_NOT_SUPPORTED; \ } while (0) /* * Thread mutex utilities. We need to handle three separate cases: * * 1) User doesn't care about mutexes; * 2) User wants us to use "OS" mutexes; * 3) User wants us to use user-specified mutexs. * * For "OS" mutexes, read POSIX Threads mutexes, at least for now. * * PKCS #11 sort of has a fourth case, but it's really just license * for us to pick either the second or third case at whim. * * To simplify the rest of the API, we provide a POSIX-based * implementation which uses the same API an user-provided mutex * implementation would be required to use, use null function pointers * to represent the case where the user doesn't need mutexes at all, * and wrap the whole thing in trivial macros to insulate the rest of * the code from the grotty details. */ /* * Basic macros. */ #define mutex_create(_m_) (mutex_cb_create == NULL ? CKR_OK : mutex_cb_create(_m_)) #define mutex_destroy(_m_) (mutex_cb_destroy == NULL ? CKR_OK : mutex_cb_destroy(_m_)) #define mutex_lock(_m_) (mutex_cb_lock == NULL ? CKR_OK : mutex_cb_lock(_m_)) #define mutex_unlock(_m_) (mutex_cb_unlock == NULL ? CKR_OK : mutex_cb_unlock(_m_)) /* * Slightly higher-level macros for common operations. * * Since the locking code depends on initialization, * attempting to lock anything when not initialized * is a failure, by definition. */ #define mutex_lock_or_return_failure(_m_) \ do { \ if (p11_uninitialized()) \ return CKR_CRYPTOKI_NOT_INITIALIZED; \ CK_RV _rv = mutex_lock(_m_); \ if (_rv != CKR_OK) \ return _rv; \ } while (0) #define mutex_unlock_return_with_rv(_rv_, _m_) \ do { \ CK_RV _rv1 = _rv_; \ CK_RV _rv2 = mutex_unlock(_m_); \ return _rv1 == CKR_OK ? _rv2 : _rv1; \ } while (0) /* * Mutex implementation using POSIX mutexes. */ #if USE_PTHREADS static CK_RV posix_mutex_create(CK_VOID_PTR_PTR ppMutex) { pthread_mutex_t *m = NULL; CK_RV rv; if (ppMutex == NULL) lose(CKR_GENERAL_ERROR); if ((m = malloc(sizeof(*m))) == NULL) lose(CKR_HOST_MEMORY); switch (pthread_mutex_init(m, NULL)) { case 0: *ppMutex = m; return CKR_OK; case ENOMEM: lose(CKR_HOST_MEMORY); default: lose(CKR_GENERAL_ERROR); } fail: if (m != NULL) free(m); return rv; } static CK_RV posix_mutex_destroy(CK_VOID_PTR pMutex) { CK_RV rv; if (pMutex == NULL) lose(CKR_MUTEX_BAD); switch (pthread_mutex_destroy(pMutex)) { case 0: free(pMutex); return CKR_OK; case EINVAL: lose(CKR_MUTEX_BAD); case EBUSY: /* * PKCS #11 mutex semantics are a bad match for POSIX here, * leaving us only the nuclear option. Feh. Fall through. */ default: lose(CKR_GENERAL_ERROR); } fail: return rv; } static CK_RV posix_mutex_lock(CK_VOID_PTR pMutex) { CK_RV rv; if (pMutex == NULL) lose(CKR_MUTEX_BAD); switch (pthread_mutex_lock(pMutex)) { case 0: return CKR_OK; case EINVAL: lose(CKR_MUTEX_BAD); default: lose(CKR_GENERAL_ERROR); } fail: return rv; } static CK_RV posix_mutex_unlock(CK_VOID_PTR pMutex) { CK_RV rv; if (pMutex == NULL) lose(CKR_MUTEX_BAD); switch (pthread_mutex_unlock(pMutex)) { case 0: return CKR_OK; case EINVAL: lose(CKR_MUTEX_BAD); case EPERM: lose(CKR_MUTEX_NOT_LOCKED); default: lose(CKR_GENERAL_ERROR); } fail: return rv; } #endif /* USE_PTHREADS */ /* * Bit mask twiddling utilities. */ static inline CK_ULONG mask_pos(const CK_ULONG mask) { return mask & ~(mask - 1); /* Finds least significant bit set in mask */ } static inline CK_ULONG mask_ldb(const CK_ULONG mask, const CK_ULONG value) { return (value & mask) / mask_pos(mask); } static inline CK_ULONG mask_dpb(const CK_ULONG mask, const CK_ULONG value) { return (value * mask_pos(mask)) & mask; } /* * Translate between libhal EC curve names and OIDs. */ #warning Perhaps this should be a utility routine in libhal instead of here static int ec_curve_oid_to_name(const uint8_t * const oid, const size_t oid_len, hal_curve_name_t *curve) { static uint8_t ec_curve_oid_p256[] = { 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07 }; static uint8_t ec_curve_oid_p384[] = { 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22 }; static uint8_t ec_curve_oid_p521[] = { 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23 }; if (oid == NULL || curve == NULL) return 0; else if (oid_len == sizeof(ec_curve_oid_p256) && memcmp(oid, ec_curve_oid_p256, oid_len) == 0) *curve = HAL_CURVE_P256; else if (oid_len == sizeof(ec_curve_oid_p384) && memcmp(oid, ec_curve_oid_p384, oid_len) == 0) *curve = HAL_CURVE_P384; else if (oid_len == sizeof(ec_curve_oid_p521) && memcmp(oid, ec_curve_oid_p521, oid_len) == 0) *curve = HAL_CURVE_P521; else return 0; return 1; } /* * Extract libhal-compatible client and session identifiers from a session. * * libhal's session identifiers are deliberately chosen to be in the same * numeric range as PKCS #11's, so we can just use them directly. * * libhal's client identifiers are multiplexing extension handled elsewhere, * for our purposes using constant client identifier of zero will do. */ static inline hal_client_handle_t p11_session_hal_client(const p11_session_t * const session) { hal_client_handle_t handle = {0}; return handle; } static inline hal_session_handle_t p11_session_hal_session(const p11_session_t * const session) { hal_session_handle_t handle = {session->handle}; return handle; } /* * Handle utilities. */ static inline CK_ULONG handle_compose(const handle_flavor_t flavor, const unsigned nonce, const unsigned index) { return (mask_dpb(HANDLE_MASK_FLAVOR, flavor) | mask_dpb(HANDLE_MASK_NONCE, nonce) | mask_dpb(HANDLE_MASK_INDEX, index)); } static inline handle_flavor_t handle_flavor(const CK_ULONG handle) { return (handle_flavor_t) mask_ldb(HANDLE_MASK_FLAVOR, handle); } static inline unsigned handle_index(const CK_ULONG handle) { return mask_ldb(HANDLE_MASK_INDEX, handle); } /* * 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; } /* * Attribute methods. */ /* * Find an attribute in a CK_ATTRIBUTE_PTR template. Returns index * into template, or -1 if not found. */ static int p11_attribute_find_in_template(const CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE_PTR template, const CK_ULONG length) { if (template != NULL) for (int i = 0; i < length; i++) if (template[i].type == type) return i; return -1; } /* * Find an attribute in a CK_ATTRIBUTE_PTR template. Returns pointer * to attribute value, or NULL if not found. */ static void *p11_attribute_find_value_in_template(const CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE_PTR template, const CK_ULONG length) { const int i = p11_attribute_find_in_template(type, template, length); return i < 0 ? NULL : template[i].pValue; } /* * Idiom for combination of p11_attribute_find_value_in_template() and * p11_find_attribute_in_descriptor(). */ static const void * p11_attribute_find_value_in_template_or_descriptor(const p11_descriptor_t *descriptor, const CK_ATTRIBUTE_TYPE type, const CK_ATTRIBUTE_PTR template, const CK_ULONG length) { const int i = p11_attribute_find_in_template(type, template, length); if (i >= 0) return template[i].pValue; const p11_attribute_descriptor_t * const atd = p11_find_attribute_in_descriptor(descriptor, type); assert(atd != NULL); return atd->value; } /* * Set attributes for a newly-created or newly-uploaded HSM key. */ static int p11_attributes_set(const hal_pkey_handle_t pkey, const CK_ATTRIBUTE_PTR template, const CK_ULONG template_length, const p11_descriptor_t * const descriptor, const hal_pkey_attribute_t *extra, const unsigned extra_length) { assert(template != NULL && descriptor != NULL && (extra_length == 0 || extra != NULL)); /* * Populate attributes, starting with the application's template, * which we assume has already been blessed by the API function that * called this method. * * If the attribute is flagged as sensitive in the descriptor, we * don't store it as an attribute. Generally, this only arises for * private key components of objects created with C_CreateObject(), * but in theory there are some corner cases in which a user could * choose to mark a private key as extractable and not sensitive, so * we might have to back-fill missing values in those cases if * anyone ever thinks up a sane reason for supporting them. For * now, assume that private keys are bloody well supposed to be * private. */ hal_pkey_attribute_t attributes[template_length + descriptor->n_attributes + extra_length]; unsigned n = 0; for (int i = 0; i < template_length; i++) { const CK_ATTRIBUTE_TYPE type = template[i].type; const void * val = template[i].pValue; const int len = template[i].ulValueLen; if (p11_attribute_is_sensitive(descriptor, type)) continue; if (n >= sizeof(attributes) / sizeof(*attributes)) return 0; attributes[n].type = type; attributes[n].value = val; attributes[n].length = len; n++; } /* * Next, add defaults from the descriptor. */ for (int 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 (n >= sizeof(attributes) / sizeof(*attributes)) return 0; attributes[n].type = type; attributes[n].value = val; attributes[n].length = len; n++; } /* * Finally, add any attributes provided by the calling function itself. */ for (int i = 0; i < extra_length; i++) { if (n >= sizeof(attributes) / sizeof(*attributes)) return 0; attributes[n].type = extra[i].type; attributes[n].value = extra[i].value; attributes[n].length = extra[i].length; n++; } /* * Set all those attributes. */ if (!hal_check(hal_rpc_pkey_set_attributes(pkey, attributes, n))) return 0; 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 so we * can simplify things a bit by reusing libhal's flag values. */ static void p11_attribute_apply_keyusage(hal_key_flags_t *keyusage, const CK_ATTRIBUTE_TYPE type, const CK_BBOOL *value) { unsigned flag; assert(keyusage != NULL && value != NULL); switch (type) { case CKA_SIGN: /* Generate signature */ case CKA_VERIFY: /* Verify signature */ flag = HAL_KEY_FLAG_USAGE_DIGITALSIGNATURE; break; case CKA_ENCRYPT: /* Encrypt bulk data (seldom used) */ case CKA_DECRYPT: /* Bulk decryption (seldom used) */ flag = HAL_KEY_FLAG_USAGE_DATAENCIPHERMENT; break; case CKA_WRAP: /* Wrap key (normal way of doing encryption) */ case CKA_UNWRAP: /* Unwrap key (normal way of doing decryption) */ flag = HAL_KEY_FLAG_USAGE_KEYENCIPHERMENT; break; default: return; /* Attribute not related to key usage */ } if (*value) *keyusage |= flag; else *keyusage &= ~flag; } /* * Access rights. */ static CK_RV p11_check_read_access(const p11_session_t *session, const CK_BBOOL cka_private, const CK_BBOOL cka_token) { if (session == NULL) return CKR_SESSION_HANDLE_INVALID; switch (session->state) { case CKS_RO_PUBLIC_SESSION: /* RO access to public token objects, RW access to public session objects */ return (cka_private) ? CKR_OBJECT_HANDLE_INVALID : CKR_OK; case CKS_RO_USER_FUNCTIONS: /* RO access to all token objects, RW access to all session objects */ return CKR_OK; case CKS_RW_PUBLIC_SESSION: /* RW access all public objects */ return (cka_private) ? CKR_OBJECT_HANDLE_INVALID : CKR_OK; case CKS_RW_USER_FUNCTIONS: /* RW acess to all objects */ return CKR_OK; case CKS_RW_SO_FUNCTIONS: /* RW access to public token objects only */ return (cka_private || ! cka_token) ? CKR_OBJECT_HANDLE_INVALID : CKR_OK; } return CKR_SESSION_HANDLE_INVALID; } static CK_RV p11_check_write_access(const p11_session_t *session, const CK_BBOOL cka_private, const CK_BBOOL cka_token) { if (session == NULL) return CKR_SESSION_HANDLE_INVALID; switch (session->state) { case CKS_RO_PUBLIC_SESSION: /* RO access to public token objects, RW access to public session objects */ return (cka_private || cka_token) ? CKR_USER_NOT_LOGGED_IN : CKR_OK; case CKS_RO_USER_FUNCTIONS: /* RO access to all token objects, RW access to all session objects */ return (cka_token) ? CKR_SESSION_READ_ONLY : CKR_OK; case CKS_RW_PUBLIC_SESSION: /* RW access all public objects */ return (cka_private) ? CKR_USER_NOT_LOGGED_IN : CKR_OK; case CKS_RW_USER_FUNCTIONS: /* RW acess to all objects */ return CKR_OK; case CKS_RW_SO_FUNCTIONS: /* RW access to public token objects only */ return (cka_private || ! cka_token) ? CKR_USER_NOT_LOGGED_IN : CKR_OK; } return CKR_SESSION_HANDLE_INVALID; } /* * Object methods. */ /* * Look up an object's UUID in the object index table, return * indication of whether it's present or not and the position it * should occupy within the index table in either case. * * NB: *where is a position in p11_object_uuids[], not p11_objects[]. */ static int p11_object_uuid_bsearch(const hal_uuid_t * const uuid, int *where) { assert(uuid != NULL && where != NULL); int lo = -1; int hi = p11_objects_in_use; for (;;) { int m = (lo + hi) / 2; if (hi == 0 || m == lo) { *where = hi; return 0; } const int cmp = hal_uuid_cmp(uuid, &p11_objects[p11_object_uuids[m]].uuid); if (cmp < 0) hi = m; else if (cmp > 0) lo = m; else { *where = m; return 1; } } } /* * Allocate a new object. */ static CK_OBJECT_HANDLE p11_object_allocate(const handle_flavor_t flavor, const hal_uuid_t *uuid, const p11_session_t *session) { if (uuid == NULL) return CK_INVALID_HANDLE; if (flavor != handle_flavor_token_object && flavor != handle_flavor_session_object) return CK_INVALID_HANDLE; int where; if (p11_object_uuid_bsearch(uuid, &where)) { assert(where >= 0 && where < p11_objects_in_use); const CK_OBJECT_HANDLE handle = p11_objects[p11_object_uuids[where]].handle; return handle_flavor(handle) == flavor ? handle : CK_INVALID_HANDLE; } if (p11_objects_in_use >= sizeof(p11_objects) / sizeof(*p11_objects)) return CK_INVALID_HANDLE; static unsigned next_index, nonce; const unsigned last_index = next_index; p11_object_t *object = NULL; do { next_index = (next_index + 1) % (sizeof(p11_objects) / sizeof(*p11_objects)); if (next_index == last_index) return CK_INVALID_HANDLE; if (next_index == 0) ++nonce; object = &p11_objects[next_index]; } while (object->handle != CK_INVALID_HANDLE); object->handle = handle_compose(flavor, nonce, next_index); object->uuid = *uuid; object->session = flavor == handle_flavor_session_object ? session->handle : CK_INVALID_HANDLE; if (where < p11_objects_in_use) memmove(&p11_object_uuids[where + 1], &p11_object_uuids[where], (p11_objects_in_use - where) * sizeof(*p11_object_uuids)); p11_object_uuids[where] = next_index; p11_objects_in_use++; return object->handle; } /* * Free an object. */ static void p11_object_free(p11_object_t *object) { if (object == NULL) return; int where; if (p11_objects_in_use > 0 && p11_object_uuid_bsearch(&object->uuid, &where) && --p11_objects_in_use > where) memmove(&p11_object_uuids[where], &p11_object_uuids[where + 1], (p11_objects_in_use - where) * sizeof(*p11_object_uuids)); memset(object, 0, sizeof(*object)); } /* * Find an object given its UUID. */ static p11_object_t *p11_object_by_uuid(const hal_uuid_t * const uuid) { int where; if (uuid == NULL || !p11_object_uuid_bsearch(uuid, &where)) return NULL; assert(where >= 0 && where < p11_objects_in_use); p11_object_t *object = &p11_objects[p11_object_uuids[where]]; if (handle_flavor(object->handle) != handle_flavor_session_object && handle_flavor(object->handle) != handle_flavor_token_object) return NULL; return object; } /* * Find an object given its handle. */ static p11_object_t *p11_object_by_handle(const CK_OBJECT_HANDLE object_handle) { if (handle_flavor(object_handle) != handle_flavor_session_object && handle_flavor(object_handle) != handle_flavor_token_object) return NULL; const unsigned index = handle_index(object_handle); if (index >= sizeof(p11_objects) / sizeof(*p11_objects)) return NULL; p11_object_t *object = &p11_objects[index]; if (object->handle != object_handle) return NULL; return object; } /* * Translate CKA_TOKEN value to handle flavor. */ static inline handle_flavor_t p11_object_flavor_from_cka_token(const CK_BBOOL *bbool) { return (bbool != NULL && *bbool) ? handle_flavor_token_object : handle_flavor_session_object; } /* * Open the HSM pkey object (if any) corresponding to the PKCS #11 handle. */ static int p11_object_pkey_open(const p11_session_t *session, const CK_OBJECT_HANDLE object_handle, hal_pkey_handle_t *pkey) { const p11_object_t *object = p11_object_by_handle(object_handle); return (session != NULL && pkey != NULL && object != NULL && hal_check(hal_rpc_pkey_open(p11_session_hal_client(session), p11_session_hal_session(session), pkey, &object->uuid))); } /* * Create pkeys to go with PKCS #11 key objects loaded by C_CreateObject(). */ static int p11_object_create_rsa_public_key(const p11_session_t * const session, const handle_flavor_t flavor, const CK_ATTRIBUTE_PTR template, const CK_ULONG template_len, const p11_descriptor_t * const descriptor, CK_OBJECT_HANDLE_PTR phObject, const hal_key_flags_t flags) { const hal_pkey_attribute_t extra[] = { {.type = CKA_LOCAL, .value = &const_CK_FALSE, .length = sizeof(const_CK_FALSE)} }; hal_pkey_handle_t pkey = {HAL_HANDLE_NONE}; uint8_t keybuf[hal_rsa_key_t_size]; hal_rsa_key_t *key = NULL; hal_uuid_t uuid; const uint8_t *cka_modulus = NULL; size_t cka_modulus_len = 0; const uint8_t *cka_public_exponent = const_0x010001; size_t cka_public_exponent_len = sizeof(const_0x010001); for (int i = 0; i < template_len; i++) { const void * const val = template[i].pValue; const size_t len = template[i].ulValueLen; switch (template[i].type) { case CKA_MODULUS: cka_modulus = val; cka_modulus_len = len; break; case CKA_PUBLIC_EXPONENT: cka_public_exponent = val; cka_public_exponent_len = len; break; } } int ok = hal_check(hal_rsa_key_load_public(&key, keybuf, sizeof(keybuf), cka_modulus, cka_modulus_len, cka_public_exponent, cka_public_exponent_len)); if (ok) { uint8_t der[hal_rsa_public_key_to_der_len(key)]; ok = (hal_check(hal_rsa_public_key_to_der(key, der, NULL, sizeof(der))) && hal_check(hal_rpc_pkey_load(p11_session_hal_client(session), p11_session_hal_session(session), &pkey, &uuid, der, sizeof(der), flags))); } if (ok) ok = p11_attributes_set(pkey, template, template_len, descriptor, extra, sizeof(extra)/sizeof(*extra)); if (ok) { *phObject = p11_object_allocate(flavor, &uuid, session); ok = *phObject != CK_INVALID_HANDLE; } if (!ok && pkey.handle != HAL_HANDLE_NONE) (void) hal_rpc_pkey_delete(pkey); else (void) hal_rpc_pkey_close(pkey); return ok; } static int p11_object_create_ec_public_key(const p11_session_t * const session, const handle_flavor_t flavor, const CK_ATTRIBUTE_PTR template, const CK_ULONG template_len, const p11_descriptor_t * const descriptor, CK_OBJECT_HANDLE_PTR phObject, const hal_key_flags_t flags) { const hal_pkey_attribute_t extra[] = { {.type = CKA_LOCAL, .value = &const_CK_FALSE, .length = sizeof(const_CK_FALSE)} }; hal_pkey_handle_t pkey = {HAL_HANDLE_NONE}; uint8_t keybuf[hal_ecdsa_key_t_size]; hal_ecdsa_key_t *key = NULL; hal_curve_name_t curve; hal_uuid_t uuid; const uint8_t *cka_ec_point = NULL; size_t cka_ec_point_len = 0; const uint8_t *cka_ec_params = NULL; size_t cka_ec_params_len = 0; for (int i = 0; i < template_len; i++) { const void * const val = template[i].pValue; const size_t len = template[i].ulValueLen; switch (template[i].type) { case CKA_EC_POINT: cka_ec_point = val; cka_ec_point_len = len; break; case CKA_EC_PARAMS: cka_ec_params = val; cka_ec_params_len = len; break; } } int ok = (ec_curve_oid_to_name(cka_ec_params, cka_ec_params_len, &curve) && hal_check(hal_ecdsa_key_from_ecpoint(&key, keybuf, sizeof(keybuf), cka_ec_point, cka_ec_point_len, curve))); if (ok) { uint8_t der[hal_ecdsa_public_key_to_der_len(key)]; ok = (hal_check(hal_ecdsa_public_key_to_der(key, der, NULL, sizeof(der))) && hal_check(hal_rpc_pkey_load(p11_session_hal_client(session), p11_session_hal_session(session), &pkey, &uuid, der, sizeof(der), flags))); } if (ok) ok = p11_attributes_set(pkey, template, template_len, descriptor, extra, sizeof(extra)/sizeof(*extra)); if (ok) { *phObject = p11_object_allocate(flavor, &uuid, session); ok = *phObject != CK_INVALID_HANDLE; } if (!ok && pkey.handle != HAL_HANDLE_NONE) (void) hal_rpc_pkey_delete(pkey); else (void) hal_rpc_pkey_close(pkey); return ok; } static int p11_object_create_rsa_private_key(const p11_session_t * const session, const handle_flavor_t flavor, const CK_ATTRIBUTE_PTR template, const CK_ULONG template_len, const p11_descriptor_t * const descriptor, CK_OBJECT_HANDLE_PTR phObject, const hal_key_flags_t flags) { const hal_pkey_attribute_t extra[] = { {.type = CKA_LOCAL, .value = &const_CK_FALSE, .length = sizeof(const_CK_FALSE)}, {.type = CKA_ALWAYS_SENSITIVE, .value = &const_CK_FALSE, .length = sizeof(const_CK_FALSE)}, {.type = CKA_NEVER_EXTRACTABLE, .value = &const_CK_FALSE, .length = sizeof(const_CK_FALSE)} }; hal_pkey_handle_t pkey = {HAL_HANDLE_NONE}; uint8_t keybuf[hal_rsa_key_t_size]; hal_rsa_key_t *key = NULL; hal_uuid_t uuid; const uint8_t *cka_modulus = NULL; size_t cka_modulus_len = 0; const uint8_t *cka_private_exponent = NULL; size_t cka_private_exponent_len = 0; const uint8_t *cka_prime_1 = NULL; size_t cka_prime_1_len = 0; const uint8_t *cka_prime_2 = NULL; size_t cka_prime_2_len = 0; const uint8_t *cka_exponent_1 = NULL; size_t cka_exponent_1_len = 0; const uint8_t *cka_exponent_2 = NULL; size_t cka_exponent_2_len = 0; const uint8_t *cka_coefficient = NULL; size_t cka_coefficient_len = 0; const uint8_t *cka_public_exponent = const_0x010001; size_t cka_public_exponent_len = sizeof(const_0x010001); for (int i = 0; i < template_len; i++) { const void * const val = template[i].pValue; const size_t len = template[i].ulValueLen; switch (template[i].type) { case CKA_MODULUS: cka_modulus = val; cka_modulus_len = len; break; case CKA_PUBLIC_EXPONENT: cka_public_exponent = val; cka_public_exponent_len = len; break; case CKA_PRIVATE_EXPONENT: cka_private_exponent = val; cka_private_exponent_len = len; break; case CKA_PRIME_1: cka_prime_1 = val; cka_prime_1_len = len; break; case CKA_PRIME_2: cka_prime_2 = val; cka_prime_2_len = len; break; case CKA_EXPONENT_1: cka_exponent_1 = val; cka_exponent_1_len = len; break; case CKA_EXPONENT_2: cka_exponent_2 = val; cka_exponent_2_len = len; break; case CKA_COEFFICIENT: cka_coefficient = val; cka_coefficient_len = len; break; } } int ok = hal_check(hal_rsa_key_load_private(&key, keybuf, sizeof(keybuf), cka_modulus, cka_modulus_len, cka_public_exponent, cka_public_exponent_len, cka_private_exponent, cka_private_exponent_len, cka_prime_1, cka_prime_1_len, cka_prime_2, cka_prime_2_len, cka_coefficient, cka_coefficient_len, cka_exponent_1, cka_exponent_1_len, cka_exponent_2, cka_exponent_2_len)); if (ok) { uint8_t der[hal_rsa_private_key_to_der_len(key)]; ok = (hal_check(hal_rsa_private_key_to_der(key, der, NULL, sizeof(der))) && hal_check(hal_rpc_pkey_load(p11_session_hal_client(session), p11_session_hal_session(session), &pkey, &uuid, der, sizeof(der), flags))); memset(der, 0, sizeof(der)); } memset(keybuf, 0, sizeof(keybuf)); if (ok) ok = p11_attributes_set(pkey, template, template_len, descriptor, extra, sizeof(extra)/sizeof(*extra)); if (ok) { *phObject = p11_object_allocate(flavor, &uuid, session); ok = *phObject != CK_INVALID_HANDLE; } if (!ok && pkey.handle != HAL_HANDLE_NONE) (void) hal_rpc_pkey_delete(pkey); else (void) hal_rpc_pkey_close(pkey); return ok; } static int p11_object_create_ec_private_key(const p11_session_t * const session, const handle_flavor_t flavor, const CK_ATTRIBUTE_PTR template, const CK_ULONG template_len, const p11_descriptor_t * const descriptor, CK_OBJECT_HANDLE_PTR phObject, const hal_key_flags_t flags) { const hal_pkey_attribute_t extra[] = { {.type = CKA_LOCAL, .value = &const_CK_FALSE, .length = sizeof(const_CK_FALSE)}, {.type = CKA_ALWAYS_SENSITIVE, .value = &const_CK_FALSE, .length = sizeof(const_CK_FALSE)}, {.type = CKA_NEVER_EXTRACTABLE, .value = &const_CK_FALSE, .length = sizeof(const_CK_FALSE)} }; hal_pkey_handle_t pkey = {HAL_HANDLE_NONE}; uint8_t keybuf[hal_ecdsa_key_t_size]; hal_ecdsa_key_t *key = NULL; hal_curve_name_t curve; hal_uuid_t uuid; const uint8_t *cka_value = NULL; size_t cka_value_len = 0; const uint8_t *cka_ec_point = NULL; size_t cka_ec_point_len = 0; const uint8_t *cka_ec_params = NULL; size_t cka_ec_params_len = 0; for (int i = 0; i < template_len; i++) { const void * const val = template[i].pValue; const size_t len = template[i].ulValueLen; switch (template[i].type) { case CKA_VALUE: cka_value = val; cka_value_len = len; break; case CKA_EC_POINT: cka_ec_point = val; cka_ec_point_len = len; break; case CKA_EC_PARAMS: cka_ec_params = val; cka_ec_params_len = len; break; } } int ok = (ec_curve_oid_to_name(cka_ec_params, cka_ec_params_len, &curve) && hal_check(hal_ecdsa_key_load_private(&key, keybuf, sizeof(keybuf), curve, cka_ec_point + 1 + 0 * cka_ec_point_len / 2, cka_ec_point_len / 2, cka_ec_point + 1 + 1 * cka_ec_point_len / 2, cka_ec_point_len / 2, cka_value, cka_value_len))); if (ok) { uint8_t der[hal_ecdsa_private_key_to_der_len(key)]; ok = (hal_check(hal_ecdsa_private_key_to_der(key, der, NULL, sizeof(der))) && hal_check(hal_rpc_pkey_load(p11_session_hal_client(session), p11_session_hal_session(session), &pkey, &uuid, der, sizeof(der), flags))); memset(der, 0, sizeof(der)); } memset(keybuf, 0, sizeof(keybuf)); if (ok) ok = p11_attributes_set(pkey, template, template_len, descriptor, extra, sizeof(extra)/sizeof(*extra)); if (ok) { *phObject = p11_object_allocate(flavor, &uuid, session); ok = *phObject != CK_INVALID_HANDLE; } if (!ok && pkey.handle != HAL_HANDLE_NONE) (void) hal_rpc_pkey_delete(pkey); else (void) hal_rpc_pkey_close(pkey); return ok; } /* * Session methods. */ /* * Create a new session. */ static p11_session_t *p11_session_allocate(void) { static unsigned next_index, nonce; const unsigned last_index = next_index; p11_session_t *session = NULL; if (p11_sessions_in_use >= sizeof(p11_sessions) / sizeof(*p11_sessions)) return NULL; do { next_index = (next_index + 1) % (sizeof(p11_sessions) / sizeof(*p11_sessions)); if (next_index == last_index) return NULL; if (next_index == 0) ++nonce; session = &p11_sessions[next_index]; } while (session->handle != CK_INVALID_HANDLE); memset(session, 0, sizeof(*session)); session->handle = handle_compose(handle_flavor_session, nonce, next_index); p11_sessions_in_use++; return session; } /* * Free a session. */ static void p11_session_free(p11_session_t *session) { if (session == NULL) return; assert(p11_sessions_in_use > 0); if (session->find_query) free(session->find_query); (void) hal_rpc_hash_finalize(session->digest_handle, NULL, 0); (void) hal_rpc_hash_finalize(session->sign_digest_handle, NULL, 0); (void) hal_rpc_hash_finalize(session->verify_digest_handle, NULL, 0); memset(session, 0, sizeof(*session)); if (--p11_sessions_in_use == 0) logged_in_as = not_logged_in; } /* * Find a session. */ static p11_session_t *p11_session_find(const CK_SESSION_HANDLE session_handle) { if (handle_flavor(session_handle) != handle_flavor_session) return NULL; const unsigned index = handle_index(session_handle); if (index >= sizeof(p11_sessions) / sizeof(*p11_sessions)) return NULL; p11_session_t *session = &p11_sessions[index]; if (session->handle != session_handle) return NULL; return session; } /* * Iterate over session handles. Start with CK_INVALID_HANDLE, * returns CK_INVALID_HANDLE when done. * * This does not verify the provided session handle, because we want * to be able to modify the sessions this finds, including deleting * them (which invalidates the session handle). Don't trust the * returned handle until it has been blessed by p11_session_find(). */ static CK_SESSION_HANDLE p11_session_handle_iterate(const CK_SESSION_HANDLE session_handle) { unsigned index; if (session_handle == CK_INVALID_HANDLE) index = 0; else if (handle_flavor(session_handle) == handle_flavor_session) index = handle_index(session_handle) + 1; else return CK_INVALID_HANDLE; for (; index < sizeof(p11_sessions) / sizeof(*p11_sessions); index++) if (handle_flavor(p11_sessions[index].handle) == handle_flavor_session) return p11_sessions[index].handle; return CK_INVALID_HANDLE; } /* * Same thing, but return session objects instead of session handles. * This is just syntactic sugar around a common idiom. */ static p11_session_t *p11_session_iterate(p11_session_t *session) { const CK_SESSION_HANDLE handle = session == NULL ? CK_INVALID_HANDLE : session->handle; return p11_session_find(p11_session_handle_iterate(handle)); } /* * Delete all sessions. Have to use p11_session_handle_iterate() here. */ static void p11_session_free_all(void) { for (CK_SESSION_HANDLE handle = p11_session_handle_iterate(CK_INVALID_HANDLE); handle != CK_INVALID_HANDLE; handle = p11_session_handle_iterate(handle)) p11_session_free(p11_session_find(handle)); } /* * Check session database against login state for consistency. * * This is mostly useful in assertions. */ static int p11_session_consistent_login(void) { switch (logged_in_as) { case not_logged_in: for (p11_session_t *session = p11_session_iterate(NULL); session != NULL; session = p11_session_iterate(session)) if (session->state != CKS_RO_PUBLIC_SESSION && session->state != CKS_RW_PUBLIC_SESSION) return 0; return 1; case logged_in_as_user: for (p11_session_t *session = p11_session_iterate(NULL); session != NULL; session = p11_session_iterate(session)) if (session->state != CKS_RO_USER_FUNCTIONS && session->state != CKS_RW_USER_FUNCTIONS) return 0; return 1; case logged_in_as_so: for (p11_session_t *session = p11_session_iterate(NULL); session != NULL; session = p11_session_iterate(session)) if (session->state != CKS_RW_SO_FUNCTIONS) return 0; return 1; default: return 0; } } /* * PKCS #11 likes space-padded rather than null-terminated strings. * This requires minor antics so that we can use a printf()-like API * while neither overflowing the caller's buffer nor truncating the * output if it happens to be exactly the target length. */ static int psnprintf(void *buffer_, size_t size, const char *format, ...) { char buffer[size + 1]; size_t n; va_list ap; va_start(ap, format); n = vsnprintf(buffer, sizeof(buffer), format, ap); va_end(ap); for (size_t i = n; i < size; i++) buffer[i] = ' '; memcpy(buffer_, buffer, size); return n; } /* * Template checking and key generation. */ /* * First pass: called once per template entry during initial pass over * template to handle generic checks that apply regardless of * attribute type. */ static CK_RV p11_template_check_1(const CK_ATTRIBUTE_TYPE type, const void * const val, const size_t len, const p11_descriptor_t * const descriptor, unsigned long forbidden_flag) { const p11_attribute_descriptor_t * const atd = p11_find_attribute_in_descriptor(descriptor, type); CK_RV rv; /* Attribute not allowed or not allowed for key generation */ if (atd == NULL || (atd->flags & forbidden_flag) != 0) lose(CKR_ATTRIBUTE_TYPE_INVALID); /* NULL or wrong-sized attribute values */ if (val == NULL || (atd->size != 0 && len != atd->size)) lose(CKR_ATTRIBUTE_VALUE_INVALID); /* Attributes which only the SO user is allowed to set to CK_TRUE */ if ((atd->flags & P11_DESCRIPTOR_ONLY_SO_USER_CAN_SET) != 0 && logged_in_as != logged_in_as_so && *(CK_BBOOL *) val) lose(CKR_ATTRIBUTE_VALUE_INVALID); /* Attributes which don't match mandatory values */ if (atd->value != NULL && (atd->flags & P11_DESCRIPTOR_DEFAULT_VALUE) == 0 && memcmp(val, atd->value, atd->length) != 0) lose(CKR_TEMPLATE_INCONSISTENT); #warning Add _LATCH checks here? rv = CKR_OK; fail: #if DEBUG_PKCS11 if (rv != CKR_OK) fprintf(stderr, "\np11_template_check_1() rejected attribute 0x%08lx\n", (unsigned long) type); #endif return rv; } /* * Second pass: called once per template to check that each attribute * required for that template has been specified exactly once and that * the session's current login state allows access with this template. */ static CK_RV p11_template_check_2(const p11_session_t *session, const p11_descriptor_t * const descriptor, const CK_ATTRIBUTE_PTR template, const CK_ULONG template_length, unsigned long required_flag, unsigned long forbidden_flag) { const CK_BBOOL * const cka_private = p11_attribute_find_value_in_template_or_descriptor(descriptor, CKA_PRIVATE, template, template_length); const CK_BBOOL * const cka_token = p11_attribute_find_value_in_template_or_descriptor(descriptor, CKA_TOKEN, template, template_length); CK_RV rv; assert(cka_private != NULL && cka_token != NULL); /* * Morass of session-state-specific restrictions on which objects we * can even see, much less modify. Callers of this function need RW * acecss to the object in question, which simplifies this a bit. */ if ((rv = p11_check_write_access(session, *cka_private, *cka_token)) != CKR_OK) goto fail; for (int i = 0; i < descriptor->n_attributes; i++) { const p11_attribute_descriptor_t * const atd = &descriptor->attributes[i]; const int required_by_api = (atd->flags & required_flag) != 0; const int forbidden_by_api = (atd->flags & forbidden_flag) != 0; const int in_descriptor = (atd->flags & P11_DESCRIPTOR_DEFAULT_VALUE) != 0 || atd->value != NULL; const int pos_in_template = p11_attribute_find_in_template(atd->type, template, template_length); /* Multiple entries for same attribute */ if (pos_in_template >= 0) for (int j = pos_in_template + 1; j < template_length; j++) if (template[j].type == atd->type) lose(CKR_TEMPLATE_INCONSISTENT); /* Required attribute missing from template */ if (!forbidden_by_api && (required_by_api || !in_descriptor) && pos_in_template < 0) { #if DEBUG_PKCS11 fprintf(stderr, "\n[Missing attribute 0x%lx]\n", atd->type); #endif lose(CKR_TEMPLATE_INCOMPLETE); } } rv = CKR_OK; fail: return rv; } /* * Mechanism-independent checks for templates and descriptors when * generating new keypairs. * * PKCS #11 gives the application far too much rope (including but not * limited to the ability to supply completely unrelated templates for * public and private keys in a keypair), so we need to do a fair * amount of checking. We automate as much of the dumb stuff as * possible through the object descriptor. * * Key usage handling here is based on RFC 5280 4.2.1.3. */ static CK_RV p11_check_keypair_attributes(const p11_session_t *session, const CK_ATTRIBUTE_PTR pPublicKeyTemplate, const CK_ULONG ulPublicKeyAttributeCount, const p11_descriptor_t * const public_descriptor, hal_key_flags_t *public_flags, const CK_ATTRIBUTE_PTR pPrivateKeyTemplate, const CK_ULONG ulPrivateKeyAttributeCount, const p11_descriptor_t * const private_descriptor, hal_key_flags_t *private_flags) { CK_RV rv = CKR_OK; int i; assert(session != NULL && pPublicKeyTemplate != NULL && public_descriptor != NULL && public_flags != NULL && pPrivateKeyTemplate != NULL && private_descriptor != NULL && private_flags != NULL); *public_flags = *private_flags = 0; const CK_BBOOL * public_cka_private = NULL; const CK_BBOOL *private_cka_private = NULL; const CK_BBOOL *private_cka_extractable = NULL; /* * Check values provided in the public and private templates. */ for (i = 0; i < ulPublicKeyAttributeCount; i++) { const CK_ATTRIBUTE_TYPE type = pPublicKeyTemplate[i].type; const void * const val = pPublicKeyTemplate[i].pValue; const size_t len = pPublicKeyTemplate[i].ulValueLen; if ((rv = p11_template_check_1(type, val, len, public_descriptor, P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE)) != CKR_OK) goto fail; if (type == CKA_PRIVATE) public_cka_private = val; p11_attribute_apply_keyusage(public_flags, type, val); } for (i = 0; i < ulPrivateKeyAttributeCount; i++) { const CK_ATTRIBUTE_TYPE type = pPrivateKeyTemplate[i].type; const void * const val = pPrivateKeyTemplate[i].pValue; const size_t len = pPrivateKeyTemplate[i].ulValueLen; if ((rv = p11_template_check_1(type, val, len, private_descriptor, P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE)) != CKR_OK) goto fail; if (type == CKA_PRIVATE) private_cka_private = val; if (type == CKA_EXTRACTABLE) private_cka_extractable = val; p11_attribute_apply_keyusage(private_flags, type, val); } /* * We insist that keyusage be specified for both public and private * key, and that they match. May not need to be this strict. */ if (*public_flags != *private_flags || *public_flags == 0) lose(CKR_TEMPLATE_INCONSISTENT); /* * Pass PKCS #11's weird notion of "public" objects through to HSM. */ if (public_cka_private != NULL && ! *public_cka_private) *public_flags |= HAL_KEY_FLAG_PUBLIC; if (private_cka_private != NULL && ! *private_cka_private) *private_flags |= HAL_KEY_FLAG_PUBLIC; /* * Pass extractability through to HSM. Public keys are always extractable. */ *public_flags |= HAL_KEY_FLAG_EXPORTABLE; if (private_cka_extractable != NULL && *private_cka_extractable) *private_flags |= HAL_KEY_FLAG_EXPORTABLE; /* * Check that all required attributes have been specified, * and that our current session state allows this access. */ if ((rv = p11_template_check_2(session, public_descriptor, pPublicKeyTemplate, ulPublicKeyAttributeCount, P11_DESCRIPTOR_REQUIRED_BY_GENERATE, P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE)) != CKR_OK || (rv = p11_template_check_2(session, private_descriptor, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, P11_DESCRIPTOR_REQUIRED_BY_GENERATE, P11_DESCRIPTOR_FORBIDDEN_BY_GENERATE)) != CKR_OK) goto fail; /* * If we get this far, we're happy. Maybe. */ rv = CKR_OK; fail: return rv; } /* * CKM_RSA_PKCS_KEY_PAIR_GEN key pair generation handler. */ static CK_RV generate_keypair_rsa_pkcs(p11_session_t *session, const handle_flavor_t public_handle_flavor, const CK_ATTRIBUTE_PTR pPublicKeyTemplate, const CK_ULONG ulPublicKeyAttributeCount, const p11_descriptor_t *public_descriptor, CK_OBJECT_HANDLE_PTR phPublicKey, const hal_key_flags_t public_flags, const handle_flavor_t private_handle_flavor, const CK_ATTRIBUTE_PTR pPrivateKeyTemplate, const CK_ULONG ulPrivateKeyAttributeCount, const p11_descriptor_t *private_descriptor, CK_OBJECT_HANDLE_PTR phPrivateKey, const hal_key_flags_t private_flags, const CK_MECHANISM_PTR pMechanism) { hal_pkey_handle_t public_pkey = {HAL_HANDLE_NONE}, private_pkey = {HAL_HANDLE_NONE}; const uint8_t *public_exponent = const_0x010001; size_t public_exponent_len = sizeof(const_0x010001); hal_uuid_t public_uuid, private_uuid; CK_ULONG keysize = 0; CK_RV rv; assert(pPublicKeyTemplate != NULL && pPrivateKeyTemplate != NULL && public_descriptor != NULL && private_descriptor != NULL && phPublicKey != NULL && phPrivateKey != NULL && session != NULL && pMechanism != NULL); for (int i = 0; i < ulPublicKeyAttributeCount; i++) { const CK_ATTRIBUTE_TYPE type = pPublicKeyTemplate[i].type; const void * const val = pPublicKeyTemplate[i].pValue; const size_t len = pPublicKeyTemplate[i].ulValueLen; switch (type) { case CKA_MODULUS_BITS: /* Keysize in bits -- only allow multiples of 8 */ keysize = *(CK_ULONG *) val; if ((keysize & 7) != 0) return CKR_ATTRIBUTE_VALUE_INVALID; continue; case CKA_PUBLIC_EXPONENT: public_exponent = val; public_exponent_len = len; continue; } } if (keysize == 0) return CKR_TEMPLATE_INCOMPLETE; { if (!hal_check(hal_rpc_pkey_generate_rsa(p11_session_hal_client(session), p11_session_hal_session(session), &private_pkey, &private_uuid, keysize, public_exponent, public_exponent_len, private_flags))) lose(CKR_FUNCTION_FAILED); uint8_t der[hal_rpc_pkey_get_public_key_len(private_pkey)], keybuf[hal_rsa_key_t_size]; size_t der_len, modulus_len; hal_rsa_key_t *key = NULL; if (!hal_check(hal_rpc_pkey_get_public_key(private_pkey, der, &der_len, sizeof(der))) || !hal_check(hal_rsa_public_key_from_der(&key, keybuf, sizeof(keybuf), der, der_len)) || !hal_check(hal_rpc_pkey_load(p11_session_hal_client(session), p11_session_hal_session(session), &public_pkey, &public_uuid, der, der_len, public_flags)) || !hal_check(hal_rsa_key_get_modulus(key, NULL, &modulus_len, 0))) lose(CKR_FUNCTION_FAILED); uint8_t modulus[modulus_len]; if (!hal_check(hal_rsa_key_get_modulus(key, modulus, NULL, sizeof(modulus)))) lose(CKR_FUNCTION_FAILED); const hal_pkey_attribute_t extra[] = { {.type = CKA_LOCAL, .value = &const_CK_TRUE, .length = sizeof(const_CK_TRUE)}, {.type = CKA_KEY_GEN_MECHANISM, .value = &pMechanism->mechanism, .length = sizeof(pMechanism->mechanism)}, {.type = CKA_MODULUS, .value = modulus, .length = modulus_len} }; if (!p11_attributes_set(private_pkey, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, private_descriptor, extra, sizeof(extra)/sizeof(*extra)) || !p11_attributes_set(public_pkey, pPublicKeyTemplate, ulPublicKeyAttributeCount, public_descriptor, extra, sizeof(extra)/sizeof(*extra))) lose(CKR_FUNCTION_FAILED); *phPrivateKey = p11_object_allocate(private_handle_flavor, &private_uuid, session); *phPublicKey = p11_object_allocate(public_handle_flavor, &public_uuid, session); if (*phPrivateKey == CK_INVALID_HANDLE || *phPublicKey == CK_INVALID_HANDLE) lose(CKR_FUNCTION_FAILED); } rv = CKR_OK; fail: hal_rpc_pkey_close(private_pkey); hal_rpc_pkey_close(public_pkey); return rv; } /* * CKM_EC_KEY_PAIR_GEN key pair generation handler. */ static CK_RV generate_keypair_ec(p11_session_t *session, const handle_flavor_t public_handle_flavor, const CK_ATTRIBUTE_PTR pPublicKeyTemplate, const CK_ULONG ulPublicKeyAttributeCount, const p11_descriptor_t *public_descriptor, CK_OBJECT_HANDLE_PTR phPublicKey, const hal_key_flags_t public_flags, const handle_flavor_t private_handle_flavor, const CK_ATTRIBUTE_PTR pPrivateKeyTemplate, const CK_ULONG ulPrivateKeyAttributeCount, const p11_descriptor_t *private_descriptor, CK_OBJECT_HANDLE_PTR phPrivateKey, const hal_key_flags_t private_flags, const CK_MECHANISM_PTR pMechanism) { hal_pkey_handle_t public_pkey = {HAL_HANDLE_NONE}, private_pkey = {HAL_HANDLE_NONE}; const CK_BYTE *params = NULL; hal_curve_name_t curve; size_t params_len; hal_uuid_t public_uuid, private_uuid; CK_RV rv; assert(session != NULL && pPublicKeyTemplate != NULL && pPrivateKeyTemplate != NULL); for (int i = 0; i < ulPublicKeyAttributeCount; i++) { const CK_ATTRIBUTE_TYPE type = pPublicKeyTemplate[i].type; const void * const val = pPublicKeyTemplate[i].pValue; const size_t len = pPublicKeyTemplate[i].ulValueLen; switch (type) { case CKA_EC_PARAMS: params = val; params_len = len; continue; } } if (!ec_curve_oid_to_name(params, params_len, &curve)) return CKR_TEMPLATE_INCOMPLETE; { if (!hal_check(hal_rpc_pkey_generate_ec(p11_session_hal_client(session), p11_session_hal_session(session), &private_pkey, &private_uuid, curve, private_flags))) lose(CKR_FUNCTION_FAILED); uint8_t der[hal_rpc_pkey_get_public_key_len(private_pkey)], keybuf[hal_ecdsa_key_t_size]; hal_ecdsa_key_t *key = NULL; size_t der_len; if (!hal_check(hal_rpc_pkey_get_public_key(private_pkey, der, &der_len, sizeof(der))) || !hal_check(hal_ecdsa_public_key_from_der(&key, keybuf, sizeof(keybuf), der, der_len)) || !hal_check(hal_rpc_pkey_load(p11_session_hal_client(session), p11_session_hal_session(session), &public_pkey, &public_uuid, der, der_len, public_flags))) lose(CKR_FUNCTION_FAILED); uint8_t point[hal_ecdsa_key_to_ecpoint_len(key)]; if (!hal_check(hal_ecdsa_key_to_ecpoint(key, point, NULL, sizeof(point)))) lose(CKR_FUNCTION_FAILED); const hal_pkey_attribute_t extra[] = { {.type = CKA_LOCAL, .value = &const_CK_TRUE, .length = sizeof(const_CK_TRUE)}, {.type = CKA_KEY_GEN_MECHANISM, .value = &pMechanism->mechanism, .length = sizeof(pMechanism->mechanism)}, {.type = CKA_EC_PARAMS, .value = params, .length = params_len}, {.type = CKA_EC_POINT, .value = point, .length = sizeof(point)} }; if (!p11_attributes_set(private_pkey, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, private_descriptor, extra, sizeof(extra)/sizeof(*extra) - 1) || !p11_attributes_set(public_pkey, pPublicKeyTemplate, ulPublicKeyAttributeCount, public_descriptor, extra, sizeof(extra)/sizeof(*extra))) lose(CKR_FUNCTION_FAILED); *phPrivateKey = p11_object_allocate(private_handle_flavor, &private_uuid, session); *phPublicKey = p11_object_allocate(public_handle_flavor, &public_uuid, session); if (*phPrivateKey == CK_INVALID_HANDLE || *phPublicKey == CK_INVALID_HANDLE) lose(CKR_FUNCTION_FAILED); } rv = CKR_OK; fail: hal_rpc_pkey_close(private_pkey); hal_rpc_pkey_close(public_pkey); return rv; } /* * Key pair generation. This needs a mechanism-specific function to * do the inner bits, but there's a lot of boilerplate. */ static CK_RV generate_keypair(p11_session_t *session, const CK_MECHANISM_PTR pMechanism, CK_RV (*mechanism_handler)(p11_session_t *session, const handle_flavor_t public_handle_flavor, const CK_ATTRIBUTE_PTR pPublicKeyTemplate, const CK_ULONG ulPublicKeyAttributeCount, const p11_descriptor_t *public_descriptor, CK_OBJECT_HANDLE_PTR phPublicKey, const hal_key_flags_t public_flags, const handle_flavor_t private_handle_flavor, const CK_ATTRIBUTE_PTR pPrivateKeyTemplate, const CK_ULONG ulPrivateKeyAttributeCount, const p11_descriptor_t *private_descriptor, CK_OBJECT_HANDLE_PTR phPrivateKey, const hal_key_flags_t private_flags, const CK_MECHANISM_PTR pMechanism), const CK_ATTRIBUTE_PTR pPublicKeyTemplate, const CK_ULONG ulPublicKeyAttributeCount, const p11_descriptor_t * const public_descriptor, CK_OBJECT_HANDLE_PTR phPublicKey, const CK_ATTRIBUTE_PTR pPrivateKeyTemplate, const CK_ULONG ulPrivateKeyAttributeCount, const p11_descriptor_t * const private_descriptor, CK_OBJECT_HANDLE_PTR phPrivateKey) { handle_flavor_t public_handle_flavor = handle_flavor_session_object; handle_flavor_t private_handle_flavor = handle_flavor_session_object; hal_key_flags_t public_flags = 0; hal_key_flags_t private_flags = 0; CK_RV rv; rv = p11_check_keypair_attributes(session, pPublicKeyTemplate, ulPublicKeyAttributeCount, public_descriptor, &public_flags, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, private_descriptor, &private_flags); if (rv != CKR_OK) return rv; assert(session != NULL && pMechanism != NULL && pPublicKeyTemplate != NULL && phPublicKey != NULL && pPrivateKeyTemplate != NULL && phPrivateKey != NULL); for (int i = 0; i < ulPublicKeyAttributeCount; i++) if (pPublicKeyTemplate[i].type == CKA_TOKEN) public_handle_flavor = p11_object_flavor_from_cka_token(pPublicKeyTemplate[i].pValue); for (int i = 0; i < ulPrivateKeyAttributeCount; i++) if (pPrivateKeyTemplate[i].type == CKA_TOKEN) private_handle_flavor = p11_object_flavor_from_cka_token(pPrivateKeyTemplate[i].pValue); if (public_handle_flavor == handle_flavor_token_object) public_flags |= HAL_KEY_FLAG_TOKEN; if (private_handle_flavor == handle_flavor_token_object) private_flags |= HAL_KEY_FLAG_TOKEN; return mechanism_handler(session, public_handle_flavor, pPublicKeyTemplate, ulPublicKeyAttributeCount, public_descriptor, phPublicKey, public_flags, private_handle_flavor, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, private_descriptor, phPrivateKey, private_flags, pMechanism); } /* * Mechanism-independent checks for templates and descriptors when * import objects via C_CreateObject(). * * Fun question exactly how calling code knows what descriptor to * pass. p11_descriptor_from_key_type() will suffice for key objects. * Drive off that bridge when we get to it. */ static CK_RV p11_check_create_attributes(const p11_session_t *session, const CK_ATTRIBUTE_PTR pTemplate, const CK_ULONG ulCount, const p11_descriptor_t * const descriptor) { CK_RV rv = CKR_OK; int i; assert(session != NULL && pTemplate != NULL && descriptor != NULL); /* * Check values provided in the template. */ for (i = 0; i < ulCount; i++) { const CK_ATTRIBUTE_TYPE type = pTemplate[i].type; const void * const val = pTemplate[i].pValue; const size_t len = pTemplate[i].ulValueLen; if ((rv = p11_template_check_1(type, val, len, descriptor, P11_DESCRIPTOR_FORBIDDEN_BY_CREATEOBJECT)) != CKR_OK) goto fail; } /* * Check that all required attributes have been specified, * and that our current session state allows this access. */ if ((rv = p11_template_check_2(session, descriptor, pTemplate, ulCount, P11_DESCRIPTOR_REQUIRED_BY_CREATEOBJECT, P11_DESCRIPTOR_FORBIDDEN_BY_CREATEOBJECT)) != CKR_OK) goto fail; /* * If we get this far, we're happy. Maybe. */ rv = CKR_OK; fail: return rv; } /* * Add data to a digest. */ static CK_RV digest_update(const p11_session_t * const session, const hal_digest_algorithm_t algorithm, hal_hash_handle_t *handle, const uint8_t * const data, const size_t data_len) { assert(algorithm != HAL_DIGEST_ALGORITHM_NONE && handle != NULL && data != NULL); if (handle->handle == HAL_HANDLE_NONE) { switch (hal_rpc_hash_initialize(p11_session_hal_client(session), p11_session_hal_session(session), handle, algorithm, NULL, 0)) { case HAL_OK: break; case HAL_ERROR_ALLOCATION_FAILURE: return CKR_HOST_MEMORY; default: return CKR_FUNCTION_FAILED; } } if (!hal_check(hal_rpc_hash_update(*handle, data, data_len))) return CKR_FUNCTION_FAILED; return CKR_OK; } /* * Finish using a digest context, if we haven't already. */ static void digest_cleanup(hal_hash_handle_t *handle) { assert(handle != NULL); if (handle->handle == HAL_HANDLE_NONE) return; (void) hal_rpc_hash_finalize(*handle, NULL, 0); handle->handle = HAL_HANDLE_NONE; } /* * Compute the length of a signature based on the key. */ static int get_signature_len(const hal_pkey_handle_t pkey, size_t *signature_len) { assert(signature_len != NULL); hal_pkey_attribute_t attribute; uint8_t attribute_buffer[sizeof(CK_KEY_TYPE)]; hal_curve_name_t curve; CK_BYTE oid[20]; attribute.type = CKA_KEY_TYPE; if (!hal_check(hal_rpc_pkey_get_attributes(pkey, &attribute, 1, attribute_buffer, sizeof(attribute_buffer)))) return 0; switch (*(CK_KEY_TYPE*)attribute.value) { case CKK_RSA: attribute.type = CKA_MODULUS; if (!hal_check(hal_rpc_pkey_get_attributes(pkey, &attribute, 1, NULL, 0)) || attribute.length == HAL_PKEY_ATTRIBUTE_NIL) return 0; *signature_len = attribute.length; return 1; case CKK_EC: attribute.type = CKA_EC_PARAMS; if (!hal_check(hal_rpc_pkey_get_attributes(pkey, &attribute, 1, oid, sizeof(oid))) || !ec_curve_oid_to_name(attribute.value, attribute.length, &curve)) return 0; switch (curve) { case HAL_CURVE_P256: *signature_len = 64; return 1; case HAL_CURVE_P384: *signature_len = 96; return 1; case HAL_CURVE_P521: *signature_len = 132; return 1; default: return 0; } } return 0; } /* * Generate a signature using the libhal RPC API. */ static CK_RV sign_hal_rpc(p11_session_t *session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { hal_pkey_handle_t pkey = {HAL_HANDLE_NONE}; size_t signature_len; CK_RV rv; assert(session != NULL && pulSignatureLen != NULL); if (!p11_object_pkey_open(session, session->sign_key_handle, &pkey)) lose(CKR_FUNCTION_FAILED); if (!get_signature_len(pkey, &signature_len)) lose(CKR_FUNCTION_FAILED); rv = pSignature != NULL && signature_len > *pulSignatureLen ? CKR_BUFFER_TOO_SMALL : CKR_OK; *pulSignatureLen = signature_len; if (pSignature != NULL && rv == CKR_OK) rv = p11_whine_from_hal(hal_rpc_pkey_sign(pkey, session->sign_digest_handle, pData, ulDataLen, pSignature, &signature_len, signature_len)); /* Fall through */ fail: hal_rpc_pkey_close(pkey); return rv; } /* * Verify a signature using the libhal RPC API. */ static CK_RV verify_hal_rpc(p11_session_t *session, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { hal_pkey_handle_t pkey = {HAL_HANDLE_NONE}; CK_RV rv; assert(session != NULL); if (!p11_object_pkey_open(session, session->verify_key_handle, &pkey)) lose(CKR_FUNCTION_FAILED); rv = p11_whine_from_hal(hal_rpc_pkey_verify(pkey, session->verify_digest_handle, pData, ulDataLen, pSignature, ulSignatureLen)); /* Fall through */ fail: hal_rpc_pkey_close(pkey); return rv; } #warning May need to do something about truncating oversized hashes for ECDSA, see PKCS11 spec /* * PKCS #11 API functions. */ CK_RV C_Initialize(CK_VOID_PTR pInitArgs) { ENTER_PUBLIC_FUNCTION(C_Initialize); CK_C_INITIALIZE_ARGS_PTR a = pInitArgs; CK_RV rv; /* * We'd like to detect the error of calling this method more than * once in a single process without an intervening call to * C_Finalize(), but there's no completely portable way to do that * when faced with things like the POSIX fork() system call. For * the moment, we use a POSIX-specific check, but may need to * generalize this for other platforms. */ #if USE_POSIX if (initialized_pid == getpid()) lose(CKR_CRYPTOKI_ALREADY_INITIALIZED); #endif /* * Sort out what the user wants to do about mutexes. Default is not * to use mutexes at all. * * There's a chicken and egg problem here: setting up the global * mutex and mutex function pointers creates a race condition, and * there's no obvious action we can take which is robust in the face * of pathological behavior by the caller such as simultaneous calls * to this method with incompatible mutex primitives. * * Given that (a) it's an error to call this method more than once * in the same process without an intervening F_Finalize() call, and * given that (b) we haven't actually promised to do any kind of * locking at all until this method returns CKR_OK, we punt * responsibility for this pathological case back to the caller. */ mutex_cb_create = NULL; mutex_cb_destroy = NULL; mutex_cb_lock = NULL; mutex_cb_unlock = NULL; if (a != NULL) { const int functions_provided = ((a->CreateMutex != NULL) + (a->DestroyMutex != NULL) + (a->LockMutex != NULL) + (a->UnlockMutex != NULL)); /* * Reserved is, um, reserved. * Mutex parameters must either all be present or all be absent. */ if (a->pReserved != NULL || (functions_provided & 3) != 0) lose(CKR_ARGUMENTS_BAD); /* * If the user provided mutex functions, use them. Otherwise, if * the user wants locking, use POSIX mutexes or return an error * depending on whether we have POSIX mutexes available. * Otherwise, we don't need to use mutexes. */ if (functions_provided) { mutex_cb_create = a->CreateMutex; mutex_cb_destroy = a->DestroyMutex; mutex_cb_lock = a->LockMutex; mutex_cb_unlock = a->UnlockMutex; } else if ((a->flags & CKF_OS_LOCKING_OK) != 0) { #if USE_PTHREADS mutex_cb_create = posix_mutex_create; mutex_cb_destroy = posix_mutex_destroy; mutex_cb_lock = posix_mutex_lock; mutex_cb_unlock = posix_mutex_unlock; #else lose(CKR_CANT_LOCK); #endif } } /* * Now that we know which mutex implementation to use, set up a * global mutex. We may want something finer grained later, but * this is enough to preserve the basic API semantics. * * Open question whether we should lock at this point, given that * until we return we haven't promised to do locking. Skip for now * as it's simpler, fix later if it turns out to be a problem. */ if ((rv = mutex_create(&p11_global_mutex)) != CKR_OK) goto fail; /* * Initialize libhal RPC channel. */ if (!hal_check(hal_rpc_client_init())) lose(CKR_GENERAL_ERROR); #if USE_POSIX initialized_pid = getpid(); #endif return CKR_OK; fail: 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_free_all(); /* * At this point we're pretty well committed to shutting down, so * there's not much to be done if any of the rest of this fails. */ hal_rpc_client_close(); rv = mutex_unlock(p11_global_mutex); (void) mutex_destroy(p11_global_mutex); p11_global_mutex = NULL; #if USE_POSIX initialized_pid = 0; #endif return rv; } CK_RV C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList) { ENTER_PUBLIC_FUNCTION(C_GetFunctionList); /* * Use pkcs11f.h to build dispatch vector for C_GetFunctionList(). * This should be const, but that's not what PKCS #11 says, oh well. * * This doesn't touch anything requiring locks, nor should it. */ static CK_FUNCTION_LIST ck_function_list = { { CRYPTOKI_VERSION_MAJOR, CRYPTOKI_VERSION_MINOR }, #define CK_PKCS11_FUNCTION_INFO(name) name, #include "pkcs11f.h" #undef CK_PKCS11_FUNCTION_INFO }; if (ppFunctionList == NULL) return CKR_ARGUMENTS_BAD; *ppFunctionList = &ck_function_list; return CKR_OK; } CK_RV C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount) { ENTER_PUBLIC_FUNCTION(C_GetSlotList); /* * We only have one slot, and it's hardwired. * No locking required here as long as this holds. */ if (pulCount == NULL) return CKR_ARGUMENTS_BAD; if (pSlotList != NULL && *pulCount < 1) return CKR_BUFFER_TOO_SMALL; if (p11_uninitialized()) return CKR_CRYPTOKI_NOT_INITIALIZED; *pulCount = 1; if (pSlotList != NULL) pSlotList[0] = P11_ONE_AND_ONLY_SLOT; return CKR_OK; } CK_RV C_GetTokenInfo(CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo) { ENTER_PUBLIC_FUNCTION(C_GetTokenInfo); /* * No locking required here as long as we're just returning constants. * * Some of the values below are nonsensical, because they don't map * particularly well to what the HSM is really doing. In some cases * (particularly for some of the flags) we hard-wire whatever client * software insists that we say before it will talk to us. Feh. * * Eventually we expect Cryptech devices to have their own hardware * clocks, in which case we'd set CKF_CLOCK_ON_TOKEN and * pInfo->utcTime. Hardware not implemented yet, so not here either. */ if (pInfo == NULL) return CKR_ARGUMENTS_BAD; if (slotID != P11_ONE_AND_ONLY_SLOT) return CKR_SLOT_ID_INVALID; if (p11_uninitialized()) return CKR_CRYPTOKI_NOT_INITIALIZED; memset(pInfo, 0, sizeof(*pInfo)); psnprintf(pInfo->label, sizeof(pInfo->label), P11_TOKEN_LABEL); psnprintf(pInfo->manufacturerID, sizeof(pInfo->manufacturerID), P11_MANUFACTURER_ID); psnprintf(pInfo->model, sizeof(pInfo->model), P11_BOARD_MODEL); psnprintf(pInfo->serialNumber, sizeof(pInfo->serialNumber), P11_BOARD_SERIAL); pInfo->flags = CKF_RNG | CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED | CKF_TOKEN_INITIALIZED; pInfo->ulMaxSessionCount = CK_EFFECTIVELY_INFINITE; pInfo->ulSessionCount = CK_UNAVAILABLE_INFORMATION; pInfo->ulMaxRwSessionCount = CK_EFFECTIVELY_INFINITE; pInfo->ulRwSessionCount = CK_UNAVAILABLE_INFORMATION; pInfo->ulMaxPinLen = (CK_ULONG) hal_rpc_min_pin_length; pInfo->ulMinPinLen = (CK_ULONG) hal_rpc_max_pin_length; pInfo->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION; pInfo->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION; pInfo->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION; pInfo->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION; pInfo->hardwareVersion.major = P11_VERSION_HW_MAJOR; pInfo->hardwareVersion.minor = P11_VERSION_HW_MINOR; pInfo->firmwareVersion.major = P11_VERSION_FW_MAJOR; pInfo->firmwareVersion.minor = P11_VERSION_FW_MINOR; 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_allocate()) == 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; 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); 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); p11_session_free(session); fail: 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_free_all(); return mutex_unlock(p11_global_mutex); } CK_RV C_Login(CK_SESSION_HANDLE hSession, CK_USER_TYPE userType, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) { ENTER_PUBLIC_FUNCTION(C_Login); const hal_client_handle_t client = {HAL_HANDLE_NONE}; hal_user_t user = HAL_USER_NONE; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); if (pPin == NULL) lose(CKR_ARGUMENTS_BAD); /* * Mind, I don't really know why this function takes a session * handle, given that the semantics don't seem to call upon us to do * anything special for "this" session. */ if (p11_session_find(hSession) == NULL) lose(CKR_SESSION_HANDLE_INVALID); /* * Figure out which PIN we're checking. * We don't (yet?) support CKU_CONTEXT_SPECIFIC. * * We don't currently support re-login without an intervening * logout, so reject the login attempt if we're already logged in. * * Read-only SO is an illegal state, so reject the login attempt if * we have any read-only sessions and we're trying to log in as SO. */ switch (userType) { case CKU_USER: switch (logged_in_as) { case not_logged_in: break; case logged_in_as_user: lose(CKR_USER_ALREADY_LOGGED_IN); case logged_in_as_so: lose(CKR_USER_ANOTHER_ALREADY_LOGGED_IN); } user = HAL_USER_NORMAL; break; case CKU_SO: switch (logged_in_as) { case not_logged_in: break; case logged_in_as_so: lose(CKR_USER_ALREADY_LOGGED_IN); case logged_in_as_user: lose(CKR_USER_ANOTHER_ALREADY_LOGGED_IN); } for (p11_session_t *session = p11_session_iterate(NULL); session != NULL; session = p11_session_iterate(session)) if (session->state == CKS_RO_PUBLIC_SESSION) lose(CKR_SESSION_READ_ONLY_EXISTS); user = HAL_USER_SO; break; case CKU_CONTEXT_SPECIFIC: lose(CKR_OPERATION_NOT_INITIALIZED); default: lose(CKR_USER_TYPE_INVALID); } /* * Try to log in the HSM. */ if ((rv = p11_whine_from_hal(hal_rpc_login(client, user, (char *) pPin, ulPinLen))) != CKR_OK) goto fail; /* * If we get here, the PIN was OK. Update global login state, then * whack every session into the correct new state. */ assert(p11_session_consistent_login()); logged_in_as = userType == CKU_SO ? logged_in_as_so : logged_in_as_user; for (p11_session_t *session = p11_session_iterate(NULL); session != NULL; session = p11_session_iterate(session)) { switch (session->state) { case CKS_RO_PUBLIC_SESSION: assert(userType == CKU_USER); session->state = CKS_RO_USER_FUNCTIONS; continue; case CKS_RW_PUBLIC_SESSION: session->state = userType == CKU_SO ? CKS_RW_SO_FUNCTIONS : CKS_RW_USER_FUNCTIONS; continue; } } assert(p11_session_consistent_login()); fail: mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_Logout(CK_SESSION_HANDLE hSession) { ENTER_PUBLIC_FUNCTION(C_Logout); const hal_client_handle_t client = {HAL_HANDLE_NONE}; p11_session_t *session = NULL; 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 ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (logged_in_as == not_logged_in) lose(CKR_USER_NOT_LOGGED_IN); /* * Delete any private session objects, clear handles for all private * objects, and whack every existing session into the right state. */ { assert(p11_session_consistent_login()); const hal_pkey_attribute_t attrs[] = { {.type = CKA_PRIVATE, .value = &const_CK_TRUE, .length = sizeof(const_CK_TRUE)} }; hal_uuid_t uuids[64]; unsigned n, state; for (p11_session_t *session = p11_session_iterate(NULL); session != NULL; session = p11_session_iterate(session)) { memset(uuids, 0, sizeof(uuids)); state = 0; do { rv = p11_whine_from_hal(hal_rpc_pkey_match(p11_session_hal_client(session), p11_session_hal_session(session), HAL_KEY_TYPE_NONE, HAL_CURVE_NONE, HAL_KEY_FLAG_TOKEN, 0, attrs, sizeof(attrs)/sizeof(*attrs), &state, uuids, &n, sizeof(uuids)/sizeof(*uuids), &uuids[sizeof(uuids)/sizeof(*uuids) - 1])); if (rv != CKR_OK) goto fail; for (int i = 0; i < n; i++) { p11_object_free(p11_object_by_uuid(&uuids[i])); hal_pkey_handle_t pkey; rv = p11_whine_from_hal(hal_rpc_pkey_open(p11_session_hal_client(session), p11_session_hal_session(session), &pkey, &uuids[i])); if (rv != CKR_OK) goto fail; if ((rv = p11_whine_from_hal(hal_rpc_pkey_delete(pkey))) != CKR_OK) { (void) hal_rpc_pkey_close(pkey); goto fail; } } } while (n == sizeof(uuids)/sizeof(*uuids)); } memset(uuids, 0, sizeof(uuids)); state = 0; do { rv = p11_whine_from_hal(hal_rpc_pkey_match(p11_session_hal_client(session), p11_session_hal_session(session), HAL_KEY_TYPE_NONE, HAL_CURVE_NONE, HAL_KEY_FLAG_TOKEN, HAL_KEY_FLAG_TOKEN, attrs, sizeof(attrs)/sizeof(*attrs), &state, uuids, &n, sizeof(uuids)/sizeof(*uuids), &uuids[sizeof(uuids)/sizeof(*uuids) - 1])); if (rv != CKR_OK) goto fail; for (int i = 0; i < n; i++) p11_object_free(p11_object_by_uuid(&uuids[i])); } while (n == sizeof(uuids)/sizeof(*uuids)); for (p11_session_t *session = p11_session_iterate(NULL); session != NULL; session = p11_session_iterate(session)) { 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; } } if ((rv = p11_whine_from_hal(hal_rpc_logout(client))) != CKR_OK) goto fail; logged_in_as = not_logged_in; assert(p11_session_consistent_login()); } fail: mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_CreateObject(CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phObject) { ENTER_PUBLIC_FUNCTION(C_CreateObject); p11_session_t *session; CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pTemplate == NULL || phObject == NULL) lose(CKR_ARGUMENTS_BAD); const CK_OBJECT_CLASS * const cka_class = p11_attribute_find_value_in_template(CKA_CLASS, pTemplate, ulCount); const CK_KEY_TYPE * const cka_key_type = p11_attribute_find_value_in_template(CKA_KEY_TYPE, pTemplate, ulCount); const CK_BBOOL * const cka_token = p11_attribute_find_value_in_template(CKA_TOKEN, pTemplate, ulCount); const CK_BBOOL * const cka_private = p11_attribute_find_value_in_template(CKA_PRIVATE, pTemplate, ulCount); const CK_BBOOL * const cka_extractable = p11_attribute_find_value_in_template(CKA_EXTRACTABLE, pTemplate, ulCount); if (cka_class == NULL) lose(CKR_TEMPLATE_INCOMPLETE); switch (*cka_class) { case CKO_PUBLIC_KEY: case CKO_PRIVATE_KEY: case CKO_SECRET_KEY: break; default: lose(CKR_TEMPLATE_INCONSISTENT); } if (cka_key_type == NULL) lose(CKR_TEMPLATE_INCOMPLETE); const p11_descriptor_t * const descriptor = p11_descriptor_from_key_type(*cka_class, *cka_key_type); if (descriptor == NULL) lose(CKR_TEMPLATE_INCONSISTENT); if ((rv = p11_check_create_attributes(session, pTemplate, ulCount, descriptor)) != CKR_OK) goto fail; const handle_flavor_t flavor = p11_object_flavor_from_cka_token(cka_token); switch (session->state) { case CKS_RO_PUBLIC_SESSION: case CKS_RO_USER_FUNCTIONS: if (flavor == handle_flavor_token_object) lose(CKR_SESSION_READ_ONLY); } hal_key_flags_t flags = flavor == handle_flavor_token_object ? HAL_KEY_FLAG_TOKEN : 0; for (int i = 0; i < ulCount; i++) p11_attribute_apply_keyusage(&flags, pTemplate[i].type, pTemplate[i].pValue); if (cka_private != NULL && ! *cka_private) flags |= HAL_KEY_FLAG_PUBLIC; if (*cka_class == CKO_PUBLIC_KEY || (cka_extractable != NULL && *cka_extractable)) flags |= HAL_KEY_FLAG_EXPORTABLE; int (*handler)(const p11_session_t *session, const handle_flavor_t flavor, const CK_ATTRIBUTE_PTR pTemplate, const CK_ULONG ulCount, const p11_descriptor_t * const descriptor, CK_OBJECT_HANDLE_PTR phObject, const hal_key_flags_t flags) = NULL; if (*cka_class == CKO_PUBLIC_KEY && *cka_key_type == CKK_RSA) handler = p11_object_create_rsa_public_key; if (*cka_class == CKO_PUBLIC_KEY && *cka_key_type == CKK_EC) handler = p11_object_create_ec_public_key; if (*cka_class == CKO_PRIVATE_KEY && *cka_key_type == CKK_RSA) handler = p11_object_create_rsa_private_key; if (*cka_class == CKO_PRIVATE_KEY && *cka_key_type == CKK_EC) handler = p11_object_create_ec_private_key; if (handler == NULL) lose(CKR_FUNCTION_FAILED); if (!handler(session, flavor, pTemplate, ulCount, descriptor, phObject, flags)) lose(CKR_FUNCTION_FAILED); return mutex_unlock(p11_global_mutex); 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); uint8_t attributes_buffer[2 * sizeof(CK_BBOOL)]; hal_pkey_handle_t pkey = {HAL_HANDLE_NONE}; hal_pkey_attribute_t attributes[] = { [0].type = CKA_PRIVATE, [1].type = CKA_TOKEN }; CK_BBOOL cka_private; CK_BBOOL cka_token; p11_session_t *session; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); session = p11_session_find(hSession); if (!p11_object_pkey_open(session, hObject, &pkey)) lose(CKR_FUNCTION_FAILED); if (!hal_check(hal_rpc_pkey_get_attributes(pkey, attributes, sizeof(attributes)/sizeof(*attributes), attributes_buffer, sizeof(attributes_buffer)))) lose(CKR_KEY_HANDLE_INVALID); cka_private = *(CK_BBOOL*) attributes[0].value; cka_token = *(CK_BBOOL*) attributes[1].value; if ((rv = p11_check_write_access(session, cka_private, cka_token)) != CKR_OK) goto fail; if (!hal_check(hal_rpc_pkey_delete(pkey))) lose(CKR_FUNCTION_FAILED); p11_object_free(p11_object_by_handle(hObject)); fail: if (pkey.handle != HAL_HANDLE_NONE) (void) hal_rpc_pkey_close(pkey); 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); hal_pkey_handle_t pkey = {HAL_HANDLE_NONE}; const p11_descriptor_t *descriptor = NULL; CK_BBOOL cka_extractable, cka_sensitive; CK_OBJECT_CLASS cka_class; CK_KEY_TYPE cka_key_type; CK_BBOOL cka_private; CK_BBOOL cka_token; int sensitive_object = 0; p11_session_t *session; CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); if (pTemplate == NULL) lose(CKR_ARGUMENTS_BAD); session = p11_session_find(hSession); if (!p11_object_pkey_open(session, hObject, &pkey)) lose(CKR_OBJECT_HANDLE_INVALID); { hal_pkey_attribute_t attributes[] = { [0].type = CKA_CLASS, [1].type = CKA_PRIVATE, [2].type = CKA_TOKEN, [3].type = CKA_KEY_TYPE }; uint8_t attributes_buffer[sizeof(CK_OBJECT_CLASS) + 2 * sizeof(CK_BBOOL) + sizeof(CK_KEY_TYPE)]; if (!hal_check(hal_rpc_pkey_get_attributes(pkey, attributes, sizeof(attributes)/sizeof(*attributes), attributes_buffer, sizeof(attributes_buffer)))) lose(CKR_OBJECT_HANDLE_INVALID); cka_class = *(CK_OBJECT_CLASS*) attributes[0].value; cka_private = *(CK_BBOOL*) attributes[1].value; cka_token = *(CK_BBOOL*) attributes[2].value; cka_key_type = *(CK_KEY_TYPE*) attributes[3].value; if ((rv = p11_check_read_access(session, cka_private, cka_token)) != CKR_OK) goto fail; descriptor = p11_descriptor_from_key_type(cka_class, cka_key_type); } if (cka_class == CKO_PRIVATE_KEY || cka_class == CKO_SECRET_KEY) { hal_pkey_attribute_t attributes[] = { [0].type = CKA_EXTRACTABLE, [1].type = CKA_SENSITIVE }; uint8_t attributes_buffer[sizeof(CK_OBJECT_CLASS) + sizeof(CK_KEY_TYPE)]; if (!hal_check(hal_rpc_pkey_get_attributes(pkey, attributes, sizeof(attributes)/sizeof(*attributes), attributes_buffer, sizeof(attributes_buffer)))) lose(CKR_OBJECT_HANDLE_INVALID); cka_extractable = *(CK_BBOOL*) attributes[0].value; cka_sensitive = *(CK_BBOOL*) attributes[1].value; sensitive_object = cka_sensitive || !cka_extractable; } { hal_pkey_attribute_t attributes[ulCount]; memset(attributes, 0, sizeof(attributes)); for (int i = 0; i < ulCount; i++) attributes[i].type = pTemplate[i].type; if (!hal_check(hal_rpc_pkey_get_attributes(pkey, attributes, sizeof(attributes)/sizeof(*attributes), NULL, 0))) lose(CKR_OBJECT_HANDLE_INVALID); rv = CKR_OK; size_t attributes_buffer_len = 0; for (int i = 0; i < ulCount; i++) { if (sensitive_object && p11_attribute_is_sensitive(descriptor, pTemplate[i].type)) { pTemplate[i].ulValueLen = -1; rv = CKR_ATTRIBUTE_SENSITIVE; continue; } if (attributes[i].length == HAL_PKEY_ATTRIBUTE_NIL) { pTemplate[i].ulValueLen = -1; rv = CKR_ATTRIBUTE_TYPE_INVALID; continue; } if (pTemplate[i].pValue == NULL) { pTemplate[i].ulValueLen = attributes[i].length; continue; } if (pTemplate[i].ulValueLen < attributes[i].length) { pTemplate[i].ulValueLen = -1; rv = CKR_BUFFER_TOO_SMALL; continue; } attributes_buffer_len += attributes[i].length; } if (attributes_buffer_len == 0) goto fail; uint8_t attributes_buffer[attributes_buffer_len]; unsigned n = 0; for (int i = 0; i < ulCount; i++) if (pTemplate[i].pValue != NULL && pTemplate[i].ulValueLen != -1) attributes[n++].type = pTemplate[i].type; if (!hal_check(hal_rpc_pkey_get_attributes(pkey, attributes, n, attributes_buffer, sizeof(attributes_buffer)))) lose(CKR_OBJECT_HANDLE_INVALID); for (int i = 0; i < n; i++) { int j = p11_attribute_find_in_template(attributes[i].type, pTemplate, ulCount); if (j < 0 || pTemplate[j].ulValueLen == -1 || pTemplate[j].ulValueLen < attributes[i].length) lose(CKR_FUNCTION_FAILED); memcpy(pTemplate[j].pValue, attributes[i].value, attributes[i].length); pTemplate[j].ulValueLen = attributes[i].length; } } fail: if (pkey.handle != HAL_HANDLE_NONE) { if (rv == CKR_OK) rv = p11_whine_from_hal(hal_rpc_pkey_close(pkey)); else (void) hal_rpc_pkey_close(pkey); } 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); const size_t attributes_len = sizeof(hal_pkey_attribute_t) * (ulCount + 1); size_t len = attributes_len; CK_BBOOL *cka_private = NULL; CK_BBOOL *cka_token = NULL; p11_session_t *session; CK_RV rv = CKR_OK; uint8_t *mem; 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); assert(!session->find_query_token && !session->find_query_session && !session->find_query_state); for (int i = 0; i < ulCount; i++) { if (pTemplate[i].pValue == NULL || pTemplate[i].ulValueLen == 0) lose(CKR_ARGUMENTS_BAD); len += pTemplate[i].ulValueLen; } if ((mem = malloc(len)) == NULL) lose(CKR_HOST_MEMORY); session->find_query = (hal_pkey_attribute_t *) mem; mem += attributes_len; for (int i = 0; i < ulCount; i++) { len = pTemplate[i].ulValueLen; session->find_query[i].type = pTemplate[i].type; session->find_query[i].value = mem; session->find_query[i].length = len; memcpy(mem, pTemplate[i].pValue, len); mem += len; } cka_private = p11_attribute_find_value_in_template(CKA_PRIVATE, pTemplate, ulCount); cka_token = p11_attribute_find_value_in_template(CKA_TOKEN, pTemplate, ulCount); session->find_query_n = ulCount; session->find_query_token = cka_token == NULL || *cka_token; session->find_query_session = cka_token == NULL || !*cka_token; session->find_query_state = 0; memset(&session->find_query_previous_uuid, 0, sizeof(session->find_query_previous_uuid)); /* * Quietly enforce object privacy even if template tries to bypass, * per PCKS #11 specification. */ if (logged_in_as != logged_in_as_user && cka_private == NULL) { session->find_query[ulCount].type = CKA_PRIVATE; session->find_query[ulCount].value = &const_CK_FALSE; session->find_query[ulCount].length = sizeof(const_CK_FALSE); session->find_query_n++; } if (logged_in_as != logged_in_as_user && cka_private != NULL && *cka_private) { int i = p11_attribute_find_in_template(CKA_PRIVATE, pTemplate, ulCount); assert(i >= 0 && i < ulCount); session->find_query[i].value = &const_CK_FALSE; session->find_query[i].length = sizeof(const_CK_FALSE); } fail: 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; 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); *pulObjectCount = 0; while (*pulObjectCount < ulMaxObjectCount && (session->find_query_token || session->find_query_session)) { hal_uuid_t uuids[ulMaxObjectCount - *pulObjectCount]; handle_flavor_t flavor; hal_key_flags_t flags; unsigned n; if (session->find_query_token) { flavor = handle_flavor_token_object; flags = HAL_KEY_FLAG_TOKEN; } else { flavor = handle_flavor_session_object; flags = 0; } rv = p11_whine_from_hal(hal_rpc_pkey_match(p11_session_hal_client(session), p11_session_hal_session(session), HAL_KEY_TYPE_NONE, HAL_CURVE_NONE, HAL_KEY_FLAG_TOKEN, flags, session->find_query, session->find_query_n, &session->find_query_state, uuids, &n, sizeof(uuids)/sizeof(*uuids), &session->find_query_previous_uuid)); if (rv != CKR_OK) goto fail; for (int i = 0; i < n; i++) { phObject[*pulObjectCount] = p11_object_allocate(flavor, &uuids[i], session); if (phObject[*pulObjectCount] == CK_INVALID_HANDLE) lose(CKR_FUNCTION_FAILED); ++*pulObjectCount; } if (n == sizeof(uuids)/sizeof(*uuids)) { memcpy(&session->find_query_previous_uuid, &uuids[n - 1], sizeof(session->find_query_previous_uuid)); } else { memset(&session->find_query_previous_uuid, 0, sizeof(session->find_query_previous_uuid)); session->find_query_state = 0; if (session->find_query_token) session->find_query_token = 0; else session->find_query_session = 0; } } fail: mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_FindObjectsFinal(CK_SESSION_HANDLE hSession) { ENTER_PUBLIC_FUNCTION(C_FindObjectsFinal); 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 (session->find_query == NULL) lose(CKR_OPERATION_NOT_INITIALIZED); free(session->find_query); session->find_query = NULL; session->find_query_n = 0; session->find_query_token = 0; session->find_query_session = 0; session->find_query_state = 0; memset(&session->find_query_previous_uuid, 0, sizeof(session->find_query_previous_uuid)); fail: mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_DigestInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism) { ENTER_PUBLIC_FUNCTION(C_DigestInit); hal_digest_algorithm_t algorithm; p11_session_t *session; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pMechanism == NULL) lose(CKR_ARGUMENTS_BAD); if (session->digest_algorithm != HAL_DIGEST_ALGORITHM_NONE) lose(CKR_OPERATION_ACTIVE); switch (pMechanism->mechanism) { case CKM_SHA_1: algorithm = HAL_DIGEST_ALGORITHM_SHA1; break; case CKM_SHA224: algorithm = HAL_DIGEST_ALGORITHM_SHA224; break; case CKM_SHA256: algorithm = HAL_DIGEST_ALGORITHM_SHA256; break; case CKM_SHA384: algorithm = HAL_DIGEST_ALGORITHM_SHA384; break; case CKM_SHA512: algorithm = HAL_DIGEST_ALGORITHM_SHA512; break; default: lose(CKR_MECHANISM_INVALID); } session->digest_algorithm = algorithm; return mutex_unlock(p11_global_mutex); fail: mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_Digest(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) { ENTER_PUBLIC_FUNCTION(C_Digest); p11_session_t *session; size_t digest_len; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pData == NULL || pulDigestLen == NULL) lose(CKR_ARGUMENTS_BAD); if (session->digest_algorithm == HAL_DIGEST_ALGORITHM_NONE) lose(CKR_OPERATION_NOT_INITIALIZED); if (session->digest_handle.handle != HAL_HANDLE_NONE) lose(CKR_OPERATION_ACTIVE); if (!hal_check(hal_rpc_hash_get_digest_length(session->digest_algorithm, &digest_len))) lose(CKR_FUNCTION_FAILED); rv = pDigest != NULL && *pulDigestLen < digest_len ? CKR_BUFFER_TOO_SMALL : CKR_OK; *pulDigestLen = digest_len; if (pDigest == NULL || rv == CKR_BUFFER_TOO_SMALL) mutex_unlock_return_with_rv(rv, p11_global_mutex); if ((rv = digest_update(session, session->digest_algorithm, &session->digest_handle, pData, ulDataLen)) != CKR_OK) goto fail; if (!hal_check(hal_rpc_hash_finalize(session->digest_handle, pDigest, *pulDigestLen))) lose(CKR_FUNCTION_FAILED); rv = CKR_OK; /* Fall through */ fail: if (session != NULL) { digest_cleanup(&session->digest_handle); session->digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_DigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { ENTER_PUBLIC_FUNCTION(C_DigestUpdate); p11_session_t *session; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pPart == NULL) lose(CKR_ARGUMENTS_BAD); if (session->digest_algorithm == HAL_DIGEST_ALGORITHM_NONE) lose(CKR_OPERATION_NOT_INITIALIZED); if ((rv = digest_update(session, session->digest_algorithm, &session->digest_handle, pPart, ulPartLen)) != CKR_OK) goto fail; return mutex_unlock(p11_global_mutex); fail: if (session != NULL) { digest_cleanup(&session->digest_handle); session->digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_DigestFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) { ENTER_PUBLIC_FUNCTION(C_DigestFinal); p11_session_t *session; size_t digest_len; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pulDigestLen == NULL) lose(CKR_ARGUMENTS_BAD); if (session->digest_algorithm == HAL_DIGEST_ALGORITHM_NONE || session->digest_handle.handle == HAL_HANDLE_NONE) lose(CKR_OPERATION_NOT_INITIALIZED); if (!hal_check(hal_rpc_hash_get_digest_length(session->digest_algorithm, &digest_len))) lose(CKR_FUNCTION_FAILED); rv = pDigest != NULL && *pulDigestLen < digest_len ? CKR_BUFFER_TOO_SMALL : CKR_OK; *pulDigestLen = digest_len; if (pDigest == NULL || rv == CKR_BUFFER_TOO_SMALL) mutex_unlock_return_with_rv(rv, p11_global_mutex); if (!hal_check(hal_rpc_hash_finalize(session->digest_handle, pDigest, *pulDigestLen))) lose(CKR_FUNCTION_FAILED); rv = CKR_OK; /* Fall through */ fail: if (session != NULL) { digest_cleanup(&session->digest_handle); session->digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_SignInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { ENTER_PUBLIC_FUNCTION(C_SignInit); uint8_t attributes_buffer[sizeof(CK_OBJECT_CLASS) + sizeof(CK_KEY_TYPE) + 3 * sizeof(CK_BBOOL)]; hal_pkey_handle_t pkey = {HAL_HANDLE_NONE}; hal_pkey_attribute_t attributes[] = { [0].type = CKA_KEY_TYPE, [1].type = CKA_SIGN, [2].type = CKA_PRIVATE, [3].type = CKA_TOKEN }; CK_KEY_TYPE cka_key_type; CK_BBOOL cka_sign; CK_BBOOL cka_private; CK_BBOOL cka_token; 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_algorithm != HAL_DIGEST_ALGORITHM_NONE) lose(CKR_OPERATION_ACTIVE); if (!p11_object_pkey_open(session, hKey, &pkey)) lose(CKR_KEY_HANDLE_INVALID); if (!hal_check(hal_rpc_pkey_get_attributes(pkey, attributes, sizeof(attributes)/sizeof(*attributes), attributes_buffer, sizeof(attributes_buffer)))) lose(CKR_KEY_HANDLE_INVALID); cka_key_type = *(CK_KEY_TYPE*) attributes[0].value; cka_sign = *(CK_BBOOL*) attributes[1].value; cka_private = *(CK_BBOOL*) attributes[2].value; cka_token = *(CK_BBOOL*) attributes[3].value; if ((rv = p11_check_read_access(session, cka_private, cka_token)) != CKR_OK) goto fail; if (!cka_sign) lose(CKR_KEY_FUNCTION_NOT_PERMITTED); switch (pMechanism->mechanism) { case CKM_RSA_PKCS: case CKM_SHA1_RSA_PKCS: case CKM_SHA224_RSA_PKCS: case CKM_SHA256_RSA_PKCS: case CKM_SHA384_RSA_PKCS: case CKM_SHA512_RSA_PKCS: if (cka_key_type != CKK_RSA) lose(CKR_KEY_TYPE_INCONSISTENT); break; case CKM_ECDSA: case CKM_ECDSA_SHA224: case CKM_ECDSA_SHA256: case CKM_ECDSA_SHA384: case CKM_ECDSA_SHA512: if (cka_key_type != CKK_EC) lose(CKR_KEY_TYPE_INCONSISTENT); break; default: return CKR_MECHANISM_INVALID; } session->sign_key_handle = hKey; switch (pMechanism->mechanism) { case CKM_RSA_PKCS: case CKM_ECDSA: session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; break; case CKM_SHA1_RSA_PKCS: session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA1; break; case CKM_SHA224_RSA_PKCS: case CKM_ECDSA_SHA224: session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA224; break; case CKM_SHA256_RSA_PKCS: case CKM_ECDSA_SHA256: session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA256; break; case CKM_SHA384_RSA_PKCS: case CKM_ECDSA_SHA384: session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA384; break; case CKM_SHA512_RSA_PKCS: case CKM_ECDSA_SHA512: session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA512; break; default: return CKR_MECHANISM_INVALID; } rv = CKR_OK; fail: if (pkey.handle != HAL_HANDLE_NONE) (void) hal_rpc_pkey_close(pkey); if (rv != CKR_OK && session != NULL) { session->sign_key_handle = CK_INVALID_HANDLE; session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_Sign(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { ENTER_PUBLIC_FUNCTION(C_Sign); p11_session_t *session; CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pData == NULL || pulSignatureLen == NULL) lose(CKR_ARGUMENTS_BAD); if (session->sign_key_handle == CK_INVALID_HANDLE) lose(CKR_OPERATION_NOT_INITIALIZED); if (session->sign_digest_handle.handle != HAL_HANDLE_NONE) lose(CKR_OPERATION_ACTIVE); if (session->sign_digest_algorithm != HAL_DIGEST_ALGORITHM_NONE && pSignature != NULL) { if ((rv = digest_update(session, session->sign_digest_algorithm, &session->sign_digest_handle, pData, ulDataLen)) != CKR_OK) goto fail; pData = NULL; ulDataLen = 0; } rv = sign_hal_rpc(session, pData, ulDataLen, pSignature, pulSignatureLen); /* Fall through */ fail: if (session != NULL && pSignature != NULL && rv != CKR_BUFFER_TOO_SMALL) { session->sign_key_handle = CK_INVALID_HANDLE; session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; digest_cleanup(&session->sign_digest_handle); } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_SignUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { ENTER_PUBLIC_FUNCTION(C_SignUpdate); p11_session_t *session; CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pPart == NULL) lose(CKR_ARGUMENTS_BAD); if (session->sign_key_handle == CK_INVALID_HANDLE) lose(CKR_OPERATION_NOT_INITIALIZED); if (session->sign_digest_algorithm == HAL_DIGEST_ALGORITHM_NONE) lose(CKR_FUNCTION_FAILED); if ((rv = digest_update(session, session->sign_digest_algorithm, &session->sign_digest_handle, pPart, ulPartLen)) != CKR_OK) goto fail; return mutex_unlock(p11_global_mutex); fail: if (session != NULL) { session->sign_key_handle = CK_INVALID_HANDLE; session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; digest_cleanup(&session->sign_digest_handle); } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_SignFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { ENTER_PUBLIC_FUNCTION(C_SignFinal); p11_session_t *session; CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pulSignatureLen == NULL) lose(CKR_ARGUMENTS_BAD); if (session->sign_key_handle == CK_INVALID_HANDLE || session->sign_digest_handle.handle == HAL_HANDLE_NONE) lose(CKR_OPERATION_NOT_INITIALIZED); rv = sign_hal_rpc(session, NULL, 0, pSignature, pulSignatureLen); /* Fall through */ fail: if (session != NULL && pSignature != NULL && rv != CKR_BUFFER_TOO_SMALL) { session->sign_key_handle = CK_INVALID_HANDLE; session->sign_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; digest_cleanup(&session->sign_digest_handle); } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_VerifyInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey ) { ENTER_PUBLIC_FUNCTION(C_VerifyInit); uint8_t attributes_buffer[sizeof(CK_OBJECT_CLASS) + sizeof(CK_KEY_TYPE) + 3 * sizeof(CK_BBOOL)]; hal_pkey_handle_t pkey = {HAL_HANDLE_NONE}; hal_pkey_attribute_t attributes[] = { [0].type = CKA_KEY_TYPE, [1].type = CKA_VERIFY, [2].type = CKA_PRIVATE, [3].type = CKA_TOKEN }; CK_KEY_TYPE cka_key_type; CK_BBOOL cka_verify; CK_BBOOL cka_private; CK_BBOOL cka_token; 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_algorithm != HAL_DIGEST_ALGORITHM_NONE) lose(CKR_OPERATION_ACTIVE); if (!p11_object_pkey_open(session, hKey, &pkey)) lose(CKR_KEY_HANDLE_INVALID); if (!hal_check(hal_rpc_pkey_get_attributes(pkey, attributes, sizeof(attributes)/sizeof(*attributes), attributes_buffer, sizeof(attributes_buffer)))) lose(CKR_KEY_HANDLE_INVALID); cka_key_type = *(CK_KEY_TYPE*) attributes[0].value; cka_verify = *(CK_BBOOL*) attributes[1].value; cka_private = *(CK_BBOOL*) attributes[2].value; cka_token = *(CK_BBOOL*) attributes[3].value; if ((rv = p11_check_read_access(session, cka_private, cka_token)) != CKR_OK) goto fail; if (!cka_verify) lose(CKR_KEY_FUNCTION_NOT_PERMITTED); switch (pMechanism->mechanism) { case CKM_RSA_PKCS: case CKM_SHA1_RSA_PKCS: case CKM_SHA224_RSA_PKCS: case CKM_SHA256_RSA_PKCS: case CKM_SHA384_RSA_PKCS: case CKM_SHA512_RSA_PKCS: if (cka_key_type != CKK_RSA) lose(CKR_KEY_TYPE_INCONSISTENT); break; case CKM_ECDSA: case CKM_ECDSA_SHA224: case CKM_ECDSA_SHA256: case CKM_ECDSA_SHA384: case CKM_ECDSA_SHA512: if (cka_key_type != CKK_EC) lose(CKR_KEY_TYPE_INCONSISTENT); break; default: return CKR_MECHANISM_INVALID; } session->verify_key_handle = hKey; switch (pMechanism->mechanism) { case CKM_RSA_PKCS: case CKM_ECDSA: session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; break; case CKM_SHA1_RSA_PKCS: session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA1; break; case CKM_SHA224_RSA_PKCS: case CKM_ECDSA_SHA224: session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA224; break; case CKM_SHA256_RSA_PKCS: case CKM_ECDSA_SHA256: session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA256; break; case CKM_SHA384_RSA_PKCS: case CKM_ECDSA_SHA384: session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA384; break; case CKM_SHA512_RSA_PKCS: case CKM_ECDSA_SHA512: session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_SHA512; break; default: return CKR_MECHANISM_INVALID; } rv = CKR_OK; fail: if (pkey.handle != HAL_HANDLE_NONE) (void) hal_rpc_pkey_close(pkey); if (rv != CKR_OK && session != NULL) { session->verify_key_handle = CK_INVALID_HANDLE; session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_Verify(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { ENTER_PUBLIC_FUNCTION(C_Verify); p11_session_t *session; CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pData == NULL || pSignature == NULL) lose(CKR_ARGUMENTS_BAD); if (session->verify_key_handle == CK_INVALID_HANDLE) lose(CKR_OPERATION_NOT_INITIALIZED); if (session->verify_digest_algorithm != HAL_DIGEST_ALGORITHM_NONE) { if ((rv = digest_update(session, session->verify_digest_algorithm, &session->verify_digest_handle, pData, ulDataLen)) != CKR_OK) goto fail; pData = NULL; ulDataLen = 0; } rv = verify_hal_rpc(session, pData, ulDataLen, pSignature, ulSignatureLen); fail: /* Fall through */ if (session != NULL) { session->verify_key_handle = CK_INVALID_HANDLE; session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; digest_cleanup(&session->verify_digest_handle); } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_VerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen) { ENTER_PUBLIC_FUNCTION(C_VerifyUpdate); p11_session_t *session; CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pPart == NULL) lose(CKR_ARGUMENTS_BAD); if (session->verify_key_handle == CK_INVALID_HANDLE) lose(CKR_OPERATION_NOT_INITIALIZED); if (session->verify_digest_algorithm == HAL_DIGEST_ALGORITHM_NONE) lose(CKR_FUNCTION_FAILED); if ((rv = digest_update(session, session->verify_digest_algorithm, &session->verify_digest_handle, pPart, ulPartLen)) != CKR_OK) goto fail; return mutex_unlock(p11_global_mutex); fail: if (session != NULL) { session->verify_key_handle = CK_INVALID_HANDLE; session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; digest_cleanup(&session->verify_digest_handle); } mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_VerifyFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen) { ENTER_PUBLIC_FUNCTION(C_VerifyFinal); p11_session_t *session; CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pSignature == NULL) lose(CKR_ARGUMENTS_BAD); if (session->verify_key_handle == CK_INVALID_HANDLE || session->verify_digest_handle.handle == HAL_HANDLE_NONE) lose(CKR_OPERATION_NOT_INITIALIZED); rv = verify_hal_rpc(session, NULL, 0, pSignature, ulSignatureLen); fail: /* Fall through */ if (session != NULL) { session->verify_key_handle = CK_INVALID_HANDLE; session->verify_digest_algorithm = HAL_DIGEST_ALGORITHM_NONE; digest_cleanup(&session->verify_digest_handle); } mutex_unlock_return_with_rv(rv, p11_global_mutex); } /* * If there's any method in this entire package which really needs a * more complex mutex structure than the single global mutex, it's * probably this one. Key generation can take a looooong time. * Drive off that bridge when we get to it. */ CK_RV C_GenerateKeyPair(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pPublicKeyTemplate, CK_ULONG ulPublicKeyAttributeCount, CK_ATTRIBUTE_PTR pPrivateKeyTemplate, CK_ULONG ulPrivateKeyAttributeCount, CK_OBJECT_HANDLE_PTR phPublicKey, CK_OBJECT_HANDLE_PTR phPrivateKey) { ENTER_PUBLIC_FUNCTION(C_GenerateKeyPair); p11_session_t *session; CK_RV rv; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (pMechanism == NULL || pPublicKeyTemplate == NULL || phPublicKey == NULL || pPrivateKeyTemplate == NULL || phPrivateKey == NULL) lose(CKR_ARGUMENTS_BAD); switch (pMechanism->mechanism) { case CKM_RSA_PKCS_KEY_PAIR_GEN: rv = generate_keypair(session, pMechanism, generate_keypair_rsa_pkcs, pPublicKeyTemplate, ulPublicKeyAttributeCount, &p11_descriptor_rsa_public_key, phPublicKey, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, &p11_descriptor_rsa_private_key, phPrivateKey); break; case CKM_EC_KEY_PAIR_GEN: rv = generate_keypair(session, pMechanism, generate_keypair_ec, pPublicKeyTemplate, ulPublicKeyAttributeCount, &p11_descriptor_ec_public_key, phPublicKey, pPrivateKeyTemplate, ulPrivateKeyAttributeCount, &p11_descriptor_ec_private_key, phPrivateKey); break; default: lose(CKR_MECHANISM_INVALID); } fail: mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_GenerateRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR RandomData, CK_ULONG ulRandomLen) { ENTER_PUBLIC_FUNCTION(C_GenerateRandom); p11_session_t *session; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); if (RandomData == NULL) lose(CKR_ARGUMENTS_BAD); if (!hal_check(hal_rpc_get_random(RandomData, ulRandomLen))) lose(CKR_FUNCTION_FAILED); fail: mutex_unlock_return_with_rv(rv, p11_global_mutex); } /* * Supply information about a particular mechanism. We may want a * more generic structure for this, for the moment, just answer the * questions that applications we care about are asking. */ CK_RV C_GetMechanismInfo(CK_SLOT_ID slotID, CK_MECHANISM_TYPE type, CK_MECHANISM_INFO_PTR pInfo) { ENTER_PUBLIC_FUNCTION(C_GetMechanismInfo); const CK_ULONG rsa_key_min = 1024; const CK_ULONG rsa_key_max = 8192; const CK_ULONG ec_key_min = 256; const CK_ULONG ec_key_max = 521; /* * No locking here, no obvious need for it. */ if (pInfo == NULL) return CKR_ARGUMENTS_BAD; if (slotID != P11_ONE_AND_ONLY_SLOT) return CKR_SLOT_ID_INVALID; if (p11_uninitialized()) return CKR_CRYPTOKI_NOT_INITIALIZED; switch (type) { case CKM_RSA_PKCS_KEY_PAIR_GEN: pInfo->ulMinKeySize = rsa_key_min; pInfo->ulMaxKeySize = rsa_key_max; pInfo->flags = CKF_HW | CKF_GENERATE_KEY_PAIR; break; case CKM_EC_KEY_PAIR_GEN: pInfo->ulMinKeySize = ec_key_min; pInfo->ulMaxKeySize = ec_key_max; pInfo->flags = CKF_HW | CKF_GENERATE_KEY_PAIR | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS; break; case CKM_RSA_PKCS: case CKM_SHA1_RSA_PKCS: case CKM_SHA224_RSA_PKCS: case CKM_SHA256_RSA_PKCS: case CKM_SHA384_RSA_PKCS: case CKM_SHA512_RSA_PKCS: pInfo->ulMinKeySize = rsa_key_min; pInfo->ulMaxKeySize = rsa_key_max; pInfo->flags = CKF_HW | CKF_SIGN | CKF_VERIFY; break; case CKM_ECDSA: case CKM_ECDSA_SHA224: case CKM_ECDSA_SHA256: case CKM_ECDSA_SHA384: case CKM_ECDSA_SHA512: pInfo->ulMinKeySize = ec_key_min; pInfo->ulMaxKeySize = ec_key_max; pInfo->flags = CKF_HW | CKF_SIGN | CKF_VERIFY | CKF_EC_F_P | CKF_EC_NAMEDCURVE | CKF_EC_UNCOMPRESS; break; case CKM_SHA_1: case CKM_SHA224: case CKM_SHA256: case CKM_SHA384: case CKM_SHA512: pInfo->ulMinKeySize = 0; pInfo->ulMaxKeySize = 0; pInfo->flags = CKF_HW | CKF_DIGEST; break; #if 0 /* * libhal supports HMAC, but we have no PKCS #11 HMAC support (yet). * * HMAC in PKCS #11 is a bit weird (what a surprise). It uses the * C_Sign*()/C_Verify*() API, with "generic secret key" objects * (CKO_SECRET_KEY, CKK_GENERIC_SECRET): these can be created with * C_CreateObject() (user-supplied HMAC key) or C_GenerateKey() * (HSM-generated HMAC key, probably from TRNG). The CKM_*_HMAC * mechanisms have fixed-length output; the CKM_*_HMAC_GENERAL * mechanisms are variable-width output. */ case CKM_SHA_1_HMAC: case CKM_SHA224_HMAC: case CKM_SHA256_HMAC: case CKM_SHA384_HMAC: case CKM_SHA512_HMAC: #endif default: return CKR_MECHANISM_INVALID; } return CKR_OK; } CK_RV C_GetSessionInfo(CK_SESSION_HANDLE hSession, CK_SESSION_INFO_PTR pInfo) { ENTER_PUBLIC_FUNCTION(C_GetSessionInfo); p11_session_t *session; CK_RV rv = CKR_OK; mutex_lock_or_return_failure(p11_global_mutex); if (pInfo == NULL) lose(CKR_ARGUMENTS_BAD); if ((session = p11_session_find(hSession)) == NULL) lose(CKR_SESSION_HANDLE_INVALID); pInfo->slotID = P11_ONE_AND_ONLY_SLOT; pInfo->state = session->state; pInfo->flags = CKF_SERIAL_SESSION; pInfo->ulDeviceError = 0; switch (session->state) { case CKS_RW_PUBLIC_SESSION: case CKS_RW_SO_FUNCTIONS: case CKS_RW_USER_FUNCTIONS: pInfo->flags |= CKF_RW_SESSION; default: break; } fail: mutex_unlock_return_with_rv(rv, p11_global_mutex); } CK_RV C_GetInfo(CK_INFO_PTR pInfo) { ENTER_PUBLIC_FUNCTION(C_GetInfo); if (pInfo == NULL) return CKR_ARGUMENTS_BAD; if (p11_uninitialized()) return CKR_CRYPTOKI_NOT_INITIALIZED; memset(pInfo, 0, sizeof(*pInfo)); pInfo->cryptokiVersion.major = 2; pInfo->cryptokiVersion.minor = 30; psnprintf(pInfo->manufacturerID, sizeof(pInfo->manufacturerID), P11_MANUFACTURER_ID); psnprintf(pInfo->libraryDescription, sizeof(pInfo->libraryDescription), P11_LIBRARY_DESCRIPTION); pInfo->libraryVersion.major = P11_VERSION_SW_MAJOR; pInfo->libraryVersion.minor = P11_VERSION_SW_MINOR; return CKR_OK; } CK_RV C_GetSlotInfo(CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo) { ENTER_PUBLIC_FUNCTION(C_GetSlotInfo); if (pInfo == NULL) return CKR_ARGUMENTS_BAD; if (slotID != P11_ONE_AND_ONLY_SLOT) return CKR_SLOT_ID_INVALID; if (p11_uninitialized()) return CKR_CRYPTOKI_NOT_INITIALIZED; memset(pInfo, 0, sizeof(*pInfo)); psnprintf(pInfo->slotDescription, sizeof(pInfo->slotDescription), P11_SLOT_DESCRIPTION); psnprintf(pInfo->manufacturerID, sizeof(pInfo->manufacturerID), P11_MANUFACTURER_ID); pInfo->flags = CKF_TOKEN_PRESENT | CKF_HW_SLOT; pInfo->hardwareVersion.major = P11_VERSION_HW_MAJOR; pInfo->hardwareVersion.minor = P11_VERSION_HW_MINOR; pInfo->firmwareVersion.major = P11_VERSION_FW_MAJOR; pInfo->firmwareVersion.minor = P11_VERSION_FW_MINOR; return CKR_OK; } CK_RV C_GetMechanismList(CK_SLOT_ID slotID, CK_MECHANISM_TYPE_PTR pMechanismList, CK_ULONG_PTR pulCount) { static const CK_MECHANISM_TYPE mechanisms[] = { CKM_ECDSA_SHA224, CKM_ECDSA_SHA256, CKM_ECDSA_SHA384, CKM_ECDSA_SHA512, CKM_ECDSA, CKM_EC_KEY_PAIR_GEN, CKM_SHA1_RSA_PKCS, CKM_SHA224_RSA_PKCS, CKM_SHA256_RSA_PKCS, CKM_SHA384_RSA_PKCS, CKM_SHA512_RSA_PKCS, CKM_RSA_PKCS, CKM_RSA_PKCS_KEY_PAIR_GEN, CKM_SHA_1, CKM_SHA224, CKM_SHA256, CKM_SHA384, CKM_SHA512, #if 0 /* libhal support these but pkcs11 doesn't, yet */ CKM_SHA_1_HMAC, CKM_SHA224_HMAC, CKM_SHA256_HMAC, CKM_SHA384_HMAC, CKM_SHA512_HMAC, #endif }; const CK_ULONG mechanisms_len = sizeof(mechanisms)/sizeof(*mechanisms); ENTER_PUBLIC_FUNCTION(C_GetMechanismList); if (pulCount == NULL) return CKR_ARGUMENTS_BAD; if (slotID != P11_ONE_AND_ONLY_SLOT) return CKR_SLOT_ID_INVALID; if (p11_uninitialized()) return CKR_CRYPTOKI_NOT_INITIALIZED; CK_RV rv = CKR_OK; if (pMechanismList != NULL && *pulCount < mechanisms_len) rv = CKR_BUFFER_TOO_SMALL; else if (pMechanismList != NULL) memcpy(pMechanismList, mechanisms, sizeof(mechanisms)); *pulCount = mechanisms_len; return rv; } CK_RV C_SeedRandom(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSeed, CK_ULONG ulSeedLen) { ENTER_PUBLIC_FUNCTION(C_SeedRandom); if (p11_uninitialized()) return CKR_CRYPTOKI_NOT_INITIALIZED; return CKR_RANDOM_SEED_NOT_SUPPORTED; } /* * Legacy functions. These are basically just unimplemented functions * which return a different error code to keep test suites happy. */ CK_RV C_GetFunctionStatus(CK_SESSION_HANDLE hSession) { ENTER_PUBLIC_FUNCTION(C_GetFunctionStatus); if (p11_uninitialized()) return CKR_CRYPTOKI_NOT_INITIALIZED; return CKR_FUNCTION_NOT_PARALLEL; } CK_RV C_CancelFunction(CK_SESSION_HANDLE hSession) { ENTER_PUBLIC_FUNCTION(C_CancelFunction); if (p11_uninitialized()) return CKR_CRYPTOKI_NOT_INITIALIZED; return CKR_FUNCTION_NOT_PARALLEL; } /* * Stubs for unsupported functions below here. Per the PKCS #11 * specification, it's OK to skip implementing almost any function in * the API, but if one does so, one must provide a stub which returns * CKR_FUNCTION_NOT_SUPPORTED, because every slot in the dispatch * vector must be populated. We could reuse a single stub for all the * unimplemented slots, but the type signatures wouldn't match, which * would require some nasty casts I'd rather avoid. * * Many of these functions would be straightforward to implement, but * there are enough bald yaks in this saga already. */ CK_RV C_GenerateKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phKey) { UNSUPPORTED_FUNCTION(C_GenerateKey); } CK_RV C_InitToken(CK_SLOT_ID slotID, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen, CK_UTF8CHAR_PTR pLabel) { UNSUPPORTED_FUNCTION(C_InitToken); } CK_RV C_InitPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin, CK_ULONG ulPinLen) { UNSUPPORTED_FUNCTION(C_InitPIN); } CK_RV C_SetPIN(CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pOldPin, CK_ULONG ulOldLen, CK_UTF8CHAR_PTR pNewPin, CK_ULONG ulNewLen) { UNSUPPORTED_FUNCTION(C_SetPIN); } CK_RV C_GetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, CK_ULONG_PTR pulOperationStateLen) { UNSUPPORTED_FUNCTION(C_GetOperationState); } CK_RV C_SetOperationState(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pOperationState, CK_ULONG ulOperationStateLen, CK_OBJECT_HANDLE hEncryptionKey, CK_OBJECT_HANDLE hAuthenticationKey) { UNSUPPORTED_FUNCTION(C_SetOperationState); } CK_RV C_CopyObject(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount, CK_OBJECT_HANDLE_PTR phNewObject) { UNSUPPORTED_FUNCTION(C_CopyObject); } CK_RV C_GetObjectSize(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ULONG_PTR pulSize) { UNSUPPORTED_FUNCTION(C_GetObjectSize); } CK_RV C_SetAttributeValue(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount) { UNSUPPORTED_FUNCTION(C_SetAttributeValue); } CK_RV C_EncryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { UNSUPPORTED_FUNCTION(C_EncryptInit); } CK_RV C_Encrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pEncryptedData, CK_ULONG_PTR pulEncryptedDataLen) { UNSUPPORTED_FUNCTION(C_Encrypt); } CK_RV C_EncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) { UNSUPPORTED_FUNCTION(C_EncryptUpdate); } CK_RV C_EncryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastEncryptedPart, CK_ULONG_PTR pulLastEncryptedPartLen) { UNSUPPORTED_FUNCTION(C_EncryptFinal); } CK_RV C_DecryptInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { UNSUPPORTED_FUNCTION(C_DecryptInit); } CK_RV C_Decrypt(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedData, CK_ULONG ulEncryptedDataLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { UNSUPPORTED_FUNCTION(C_Decrypt); } CK_RV C_DecryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) { UNSUPPORTED_FUNCTION(C_DecryptUpdate); } CK_RV C_DecryptFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pLastPart, CK_ULONG_PTR pulLastPartLen) { UNSUPPORTED_FUNCTION(C_DecryptFinal); } CK_RV C_DigestKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey) { UNSUPPORTED_FUNCTION(C_DigestKey); } CK_RV C_SignRecoverInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { UNSUPPORTED_FUNCTION(C_SignRecoverInit); } CK_RV C_SignRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, CK_BYTE_PTR pSignature, CK_ULONG_PTR pulSignatureLen) { UNSUPPORTED_FUNCTION(C_SignRecover); } CK_RV C_VerifyRecoverInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hKey) { UNSUPPORTED_FUNCTION(C_VerifyRecoverInit); } CK_RV C_VerifyRecover(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pSignature, CK_ULONG ulSignatureLen, CK_BYTE_PTR pData, CK_ULONG_PTR pulDataLen) { UNSUPPORTED_FUNCTION(C_VerifyRecover); } CK_RV C_DigestEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) { UNSUPPORTED_FUNCTION(C_DigestEncryptUpdate); } CK_RV C_DecryptDigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) { UNSUPPORTED_FUNCTION(C_DecryptDigestUpdate); } CK_RV C_SignEncryptUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, CK_ULONG ulPartLen, CK_BYTE_PTR pEncryptedPart, CK_ULONG_PTR pulEncryptedPartLen) { UNSUPPORTED_FUNCTION(C_SignEncryptUpdate); } CK_RV C_DecryptVerifyUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pEncryptedPart, CK_ULONG ulEncryptedPartLen, CK_BYTE_PTR pPart, CK_ULONG_PTR pulPartLen) { UNSUPPORTED_FUNCTION(C_DecryptVerifyUpdate); } CK_RV C_WrapKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hWrappingKey, CK_OBJECT_HANDLE hKey, CK_BYTE_PTR pWrappedKey, CK_ULONG_PTR pulWrappedKeyLen) { UNSUPPORTED_FUNCTION(C_WrapKey); } CK_RV C_UnwrapKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hUnwrappingKey, CK_BYTE_PTR pWrappedKey, CK_ULONG ulWrappedKeyLen, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey) { UNSUPPORTED_FUNCTION(C_UnwrapKey); } CK_RV C_DeriveKey(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism, CK_OBJECT_HANDLE hBaseKey, CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulAttributeCount, CK_OBJECT_HANDLE_PTR phKey) { UNSUPPORTED_FUNCTION(C_DeriveKey); } CK_RV C_WaitForSlotEvent(CK_FLAGS flags, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR pRserved) { UNSUPPORTED_FUNCTION(C_WaitForSlotEvent); } /* * "Any programmer who fails to comply with the standard naming, formatting, * or commenting conventions should be shot. If it so happens that it is * inconvenient to shoot him, then he is to be politely requested to recode * his program in adherence to the above standard." * -- Michael Spier, Digital Equipment Corporation * * Local variables: * indent-tabs-mode: nil * End: */