From d108e2008ec7205ef3907296fd3e5a810e45919b Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Tue, 9 Dec 2008 20:09:51 +0000 Subject: First shot at renaming the project. --- ckcapi-builtin.c | 245 --------- ckcapi-builtin.h | 30 -- ckcapi-cert.c | 775 --------------------------- ckcapi-cert.h | 55 -- ckcapi-der.c | 177 ------ ckcapi-der.h | 35 -- ckcapi-key.c | 1083 ------------------------------------- ckcapi-key.h | 34 -- ckcapi-object.c | 290 ---------- ckcapi-object.h | 100 ---- ckcapi-rsa.c | 416 --------------- ckcapi-rsa.h | 44 -- ckcapi-session.c | 982 ---------------------------------- ckcapi-session.h | 150 ------ ckcapi-token.c | 354 ------------ ckcapi-token.h | 60 --- ckcapi-trust.c | 569 -------------------- ckcapi-trust.h | 30 -- ckcapi-util.c | 547 ------------------- ckcapi-util.h | 93 ---- ckcapi.c | 1515 ---------------------------------------------------- ckcapi.dep | 69 --- ckcapi.h | 127 ----- ckcapi.vcproj | 428 --------------- p11-capi-builtin.c | 245 +++++++++ p11-capi-builtin.h | 30 ++ p11-capi-cert.c | 775 +++++++++++++++++++++++++++ p11-capi-cert.h | 37 ++ p11-capi-der.c | 177 ++++++ p11-capi-der.h | 35 ++ p11-capi-key.c | 1083 +++++++++++++++++++++++++++++++++++++ p11-capi-key.h | 34 ++ p11-capi-object.c | 290 ++++++++++ p11-capi-object.h | 100 ++++ p11-capi-rsa.c | 416 +++++++++++++++ p11-capi-rsa.h | 44 ++ p11-capi-session.c | 983 ++++++++++++++++++++++++++++++++++ p11-capi-session.h | 169 ++++++ p11-capi-token.c | 354 ++++++++++++ p11-capi-token.h | 60 +++ p11-capi-trust.c | 569 ++++++++++++++++++++ p11-capi-trust.h | 30 ++ p11-capi-util.c | 547 +++++++++++++++++++ p11-capi-util.h | 92 ++++ p11-capi.c | 1515 ++++++++++++++++++++++++++++++++++++++++++++++++++++ p11-capi.dep | 69 +++ p11-capi.h | 126 +++++ p11-capi.vcproj | 428 +++++++++++++++ 48 files changed, 8208 insertions(+), 8208 deletions(-) delete mode 100644 ckcapi-builtin.c delete mode 100644 ckcapi-builtin.h delete mode 100644 ckcapi-cert.c delete mode 100644 ckcapi-cert.h delete mode 100644 ckcapi-der.c delete mode 100644 ckcapi-der.h delete mode 100644 ckcapi-key.c delete mode 100644 ckcapi-key.h delete mode 100644 ckcapi-object.c delete mode 100644 ckcapi-object.h delete mode 100644 ckcapi-rsa.c delete mode 100644 ckcapi-rsa.h delete mode 100644 ckcapi-session.c delete mode 100644 ckcapi-session.h delete mode 100644 ckcapi-token.c delete mode 100644 ckcapi-token.h delete mode 100644 ckcapi-trust.c delete mode 100644 ckcapi-trust.h delete mode 100644 ckcapi-util.c delete mode 100644 ckcapi-util.h delete mode 100644 ckcapi.c delete mode 100644 ckcapi.dep delete mode 100644 ckcapi.h delete mode 100644 ckcapi.vcproj create mode 100644 p11-capi-builtin.c create mode 100644 p11-capi-builtin.h create mode 100644 p11-capi-cert.c create mode 100644 p11-capi-cert.h create mode 100644 p11-capi-der.c create mode 100644 p11-capi-der.h create mode 100644 p11-capi-key.c create mode 100644 p11-capi-key.h create mode 100644 p11-capi-object.c create mode 100644 p11-capi-object.h create mode 100644 p11-capi-rsa.c create mode 100644 p11-capi-rsa.h create mode 100644 p11-capi-session.c create mode 100644 p11-capi-session.h create mode 100644 p11-capi-token.c create mode 100644 p11-capi-token.h create mode 100644 p11-capi-trust.c create mode 100644 p11-capi-trust.h create mode 100644 p11-capi-util.c create mode 100644 p11-capi-util.h create mode 100644 p11-capi.c create mode 100644 p11-capi.dep create mode 100644 p11-capi.h create mode 100644 p11-capi.vcproj diff --git a/ckcapi-builtin.c b/ckcapi-builtin.c deleted file mode 100644 index 7d92f81..0000000 --- a/ckcapi-builtin.c +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2007 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "ckcapi.h" -#include "ckcapi-object.h" -#include "ckcapi-session.h" -#include "ckcapi-token.h" - -#include "pkcs11/pkcs11n.h" - -/* -------------------------------------------------------------------------- - * BUILT IN VALUES - */ - -static const CK_BBOOL ck_true = CK_TRUE; -static const CK_BBOOL ck_false = CK_FALSE; - -static const CK_OBJECT_CLASS cko_netscape_builtin_root_list = CKO_NETSCAPE_BUILTIN_ROOT_LIST; - -static const char ck_root_label[] = "Windows Certificate Roots"; - -/* -------------------------------------------------------------------------- - * BUILT IN OBJECTS - */ - -#define CK_END_LIST (CK_ULONG)-1 - -static const CK_ATTRIBUTE builtin_root[] = { - { CKA_TOKEN, (void*)&ck_true, sizeof(CK_BBOOL) }, - { CKA_CLASS, (void*)&cko_netscape_builtin_root_list, sizeof(CK_OBJECT_CLASS) }, - { CKA_PRIVATE, (void*)&ck_false, sizeof(CK_BBOOL) }, - { CKA_MODIFIABLE, (void*)&ck_false, sizeof(CK_BBOOL) }, - { CKA_LABEL, (void*)ck_root_label, sizeof(ck_root_label) }, - { CK_END_LIST, NULL, 0 } -}; - -typedef struct _BuiltinMatch -{ - CK_ATTRIBUTE_PTR attr; - CK_ULONG slot_flags; -} -BuiltinMatch; - -static const BuiltinMatch all_builtins[] = { - { (CK_ATTRIBUTE_PTR)&builtin_root, CKCAPI_SLOT_TRUSTED | CKCAPI_SLOT_CA | CKCAPI_SLOT_CERTS }, - { NULL, 0 } -}; - -/* This is filled in later */ -static CK_ULONG num_builtins = 0; - -/* -------------------------------------------------------------------------- - * IMPLEMENTATION - */ - -/* Represents a loaded builtin object */ -typedef struct _BuiltinObject -{ - CkCapiObject obj; - CK_ATTRIBUTE_PTR attr; -} -BuiltinObject; - -typedef struct _BuiltinObjectData -{ - CkCapiObjectData base; - CK_ATTRIBUTE_PTR attr; -} -BuiltinObjectData; - -static CK_RV -builtin_attribute(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) -{ - BuiltinObjectData* bdata = (BuiltinObjectData*)objdata; - CK_ATTRIBUTE_PTR builtin = bdata->attr; - - ASSERT(attr); - ASSERT(bdata); - - while(builtin->type != CK_END_LIST) - { - if(builtin->type == attr->type) - { - if(builtin->ulValueLen == 0) - return CKR_ATTRIBUTE_TYPE_INVALID; - return ckcapi_return_data(attr, builtin->pValue, builtin->ulValueLen); - } - - builtin++; - } - - return CKR_ATTRIBUTE_TYPE_INVALID; -} - -static void -builtin_data_release(void* data) -{ - BuiltinObjectData* bdata = (BuiltinObjectData*)data; - ASSERT(bdata); - free(bdata); -} - -static const CkCapiObjectDataVtable builtin_objdata_vtable = { - builtin_attribute, - builtin_attribute, - builtin_attribute, - builtin_data_release, -}; - -static CK_RV -builtin_load_data(CkCapiSession* sess, CkCapiObject* obj, CkCapiObjectData** objdata) -{ - BuiltinObject* bobj = (BuiltinObject*)obj; - BuiltinObjectData* bdata; - - ASSERT(bobj); - ASSERT(objdata); - ASSERT(num_builtins > 0); - - bdata = (BuiltinObjectData*)calloc(1, sizeof(BuiltinObjectData)); - if(!bdata) - return CKR_HOST_MEMORY; - - /* Simple, just use same data */ - bdata->attr = bobj->attr; - - bdata->base.object = obj->id; - bdata->base.data_funcs = &builtin_objdata_vtable; - - *objdata = &(bdata->base); - return CKR_OK; -} - -static unsigned int -builtin_hash_func(CkCapiObject* obj) -{ - return ckcapi_hash_pointer(((BuiltinObject*)obj)->attr); -} - -static int -builtin_equal_func(CkCapiObject* one, CkCapiObject* two) -{ - return ((BuiltinObject*)one)->attr == ((BuiltinObject*)two)->attr; -} - -static void -builtin_object_release(void* data) -{ - BuiltinObject* bobj = (BuiltinObject*)data; - ASSERT(bobj); - free(bobj); -} - -static const CkCapiObjectVtable builtin_object_vtable = { - builtin_load_data, - builtin_hash_func, - builtin_equal_func, - builtin_object_release, -}; - -static CK_RV -register_builtin_object(CkCapiSession* sess, CK_ATTRIBUTE_PTR attr, CkCapiObject** obj) -{ - BuiltinObject* bobj; - CK_RV ret; - - bobj = calloc(1, sizeof(BuiltinObject)); - if(!bobj) - return CKR_HOST_MEMORY; - - bobj->attr = attr; - - bobj->obj.id = 0; - bobj->obj.obj_funcs = &builtin_object_vtable; - - ret = ckcapi_token_register_object(sess->slot, &(bobj->obj)); - if(ret != CKR_OK) - { - free(bobj); - return ret; - } - - ASSERT(bobj->obj.id != 0); - *obj = &(bobj->obj); - return CKR_OK; -} - -CK_RV -ckcapi_builtin_find(CkCapiSession* sess, CK_OBJECT_CLASS cls, CK_ATTRIBUTE_PTR match, - CK_ULONG count, CkCapiArray* arr) -{ - CkCapiObject* obj; - BuiltinObjectData bdata; - CK_RV ret = CKR_OK; - CK_ULONG i, fl; - - /* First time around count total number */ - if(!num_builtins) - { - while(all_builtins[num_builtins].attr) - ++num_builtins; - ASSERT(num_builtins > 0); - } - - /* Match each certificate */ - for(i = 0; i < num_builtins; ++i) - { - /* Only apply built in objects to appropriate slots */ - fl = ckcapi_token_get_flags(sess->slot) & all_builtins[i].slot_flags; - if(fl != all_builtins[i].slot_flags) - continue; - - bdata.attr = all_builtins[i].attr; - bdata.base.object = 0; - bdata.base.data_funcs = &builtin_objdata_vtable; - - if(ckcapi_object_data_match(&bdata.base, match, count)) - { - ret = register_builtin_object(sess, all_builtins[i].attr, &obj); - if(ret != CKR_OK) - break; - - ckcapi_array_append(arr, obj->id); - } - } - - return ret; -} - diff --git a/ckcapi-builtin.h b/ckcapi-builtin.h deleted file mode 100644 index b7a043c..0000000 --- a/ckcapi-builtin.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2007 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef CKCAPI_BUILTIN_H -#define CKCAPI_BUILTIN_H - -#include "ckcapi.h" - -/* Find builtin objects matching criteria */ -CK_RV ckcapi_builtin_find (CkCapiSession* sess, CK_OBJECT_CLASS cls, - CK_ATTRIBUTE_PTR match, CK_ULONG count, - CkCapiArray* arr); - -#endif /* CRYPTOKI_BUILTIN_H */ diff --git a/ckcapi-cert.c b/ckcapi-cert.c deleted file mode 100644 index 5225286..0000000 --- a/ckcapi-cert.c +++ /dev/null @@ -1,775 +0,0 @@ -/* - * Copyright (C) 2007 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "ckcapi.h" -#include "ckcapi-cert.h" -#include "ckcapi-object.h" -#include "ckcapi-session.h" -#include "ckcapi-token.h" - -#include - -#ifndef CERT_FIND_KEY_IDENTIFIER -#define CERT_FIND_KEY_IDENTIFIER 983040 -#endif - -#ifndef CERT_KEY_IDENTIFIER_PROP_ID -#define CERT_KEY_IDENTIFIER_PROP_ID 20 -#endif - -typedef struct _CertObject -{ - CkCapiObject obj; - - /* Together these can uniquely identify a certificate */ - CRYPT_INTEGER_BLOB serial; - CERT_NAME_BLOB issuer; -} -CertObject; - -typedef struct _CertObjectData -{ - CkCapiObjectData base; - PCCERT_CONTEXT cert; - BOOL is_in_root; -} -CertObjectData; - -static CK_RV -parse_basic_constraints(CertObjectData* cdata, CK_ULONG* category) -{ - CERT_BASIC_CONSTRAINTS_INFO* basic; - CERT_EXTENSION* ext; - DWORD size; - BYTE bits; - CK_RV ret; - - ASSERT(cdata); - ASSERT(cdata->cert); - - *category = 0; - - ext = CertFindExtension(szOID_BASIC_CONSTRAINTS, - cdata->cert->pCertInfo->cExtension, - cdata->cert->pCertInfo->rgExtension); - - /* No key usage, don't care */ - if(!ext) - return CKR_OK; - - /* Find the size of the decoded structure */ - if(!CryptDecodeObject(CKCAPI_ENCODINGS, X509_BASIC_CONSTRAINTS, - ext->Value.pbData, ext->Value.cbData, 0, NULL, &size)) - return ckcapi_winerr_to_ckr(GetLastError()); - - /* Allocate enough memory */ - basic = (CERT_BASIC_CONSTRAINTS_INFO*)calloc(1, size); - if(!basic) - return CKR_HOST_MEMORY; - - /* And get the decoded structure */ - if(CryptDecodeObject(CKCAPI_ENCODINGS, X509_BASIC_CONSTRAINTS, - ext->Value.pbData, ext->Value.cbData, 0, basic, &size)) - { - if(basic->SubjectType.cbData != 1) - { - DBG(("basic constraints bits are of invalid size")); - ret = CKR_GENERAL_ERROR; - } - else - { - /* All of the above was for 2 bits. Lovely */ - bits = basic->SubjectType.pbData[0] & ~(0xff >> (8 - basic->SubjectType.cUnusedBits)); - if((bits & CERT_CA_SUBJECT_FLAG) == CERT_CA_SUBJECT_FLAG) - *category = 2; - else if((bits & CERT_END_ENTITY_SUBJECT_FLAG) == CERT_END_ENTITY_SUBJECT_FLAG) - *category = 3; - else - *category = 0; - ret = CKR_OK; - } - } - else - { - ret = ckcapi_winerr_to_ckr(GetLastError()); - } - - free(basic); - - return ret; -} - - -static CK_RV -cert_bool_attribute(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) -{ - CertObjectData* cdata = (CertObjectData*)objdata; - CK_BBOOL val; - - ASSERT(cdata); - - switch(attr->type) - { - /* - * Resides on the token - * - Always true for CAPI objects. - */ - case CKA_TOKEN: - val = CK_TRUE; - break; - - /* - * Private vs. Public object. - * - Always false for certificates. - */ - case CKA_PRIVATE: - val = CK_FALSE; - break; - - /* - * If object can be modified. - * - Currently always false. In the future with additional - * functionality this may change. - */ - case CKA_MODIFIABLE: - val = CK_FALSE; - break; - - /* - * Whether the certificate can be trusted for the application - * in which it was created. - * - We just report on whether the certificate is a trusted root. - */ - case CKA_TRUSTED: - val = cdata->is_in_root ? CK_TRUE : CK_FALSE; - break; - - default: - return CKR_ATTRIBUTE_TYPE_INVALID; - }; - - return ckcapi_return_data(attr, &val, sizeof(CK_BBOOL)); -} - -static CK_RV -cert_ulong_attribute(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) -{ - CertObjectData* cdata = (CertObjectData*)objdata; - CK_ULONG val; - CK_RV ret; - - ASSERT(objdata); - - switch(attr->type) - { - - /* - * Object class. - * - Always CKO_CERTIFICATE for certificates. - */ - case CKA_CLASS: - val = CKO_CERTIFICATE; - break; - - /* - * Type of certificate. - * - Always X509. - */ - case CKA_CERTIFICATE_TYPE: - val = CKC_X_509; - break; - - /* - * Whether a CA, user certificate, other. - * - Get certificate szOID_ENHANCED_KEY_USAGE - * extension or CERT_CTL_PROP_ID and look into CTL_USAGE structure. - */ - case CKA_CERTIFICATE_CATEGORY: - ret = parse_basic_constraints(cdata, &val); - if(ret != CKR_OK) - return ret; - break; - - /* - * Java MIDP security domain. - * - Have no idea what this is. Spec says default to zero. - */ - case CKA_JAVA_MIDP_SECURITY_DOMAIN: - val = 0; - break; - - default: - return CKR_ATTRIBUTE_TYPE_INVALID; - }; - - return ckcapi_return_data(attr, &val, sizeof(CK_ULONG)); -} - -static CK_RV -cert_bytes_attribute(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) -{ - CertObjectData* cdata = (CertObjectData*)objdata; - PCCERT_CONTEXT cert = cdata->cert; - - ASSERT(sizeof(CK_ULONG) == sizeof(DWORD)); - ASSERT(cdata); - - return ckcapi_cert_certificate_get_bytes(cdata->cert, attr); -} - -static void -cert_data_release(void* data) -{ - CertObjectData* cdata = (CertObjectData*)data; - ASSERT(cdata && cdata->cert); - CertFreeCertificateContext(cdata->cert); - free(cdata); -} - -static const CkCapiObjectDataVtable cert_objdata_vtable = { - cert_bool_attribute, - cert_ulong_attribute, - cert_bytes_attribute, - cert_data_release, -}; - -static CkCapiObjectData* -cert_alloc_data(CkCapiSession* sess, CkCapiObject* obj, PCCERT_CONTEXT cert) -{ - CertObjectData* cdata; - - cdata = (CertObjectData*)calloc(1, sizeof(CertObjectData)); - if(!cdata) - return NULL; - - cdata->cert = cert; - cdata->is_in_root = (ckcapi_token_get_flags(sess->slot) & CKCAPI_SLOT_CA) ? TRUE : FALSE; - - cdata->base.object = obj->id; - cdata->base.data_funcs = &cert_objdata_vtable; - - return &(cdata->base); -} - -static CK_RV -cert_load_data(CkCapiSession* sess, CkCapiObject* obj, CkCapiObjectData** objdata) -{ - CertObject* cobj = (CertObject*)obj; - CERT_INFO info; - PCCERT_CONTEXT cert; - - ASSERT(cobj); - ASSERT(objdata); - - ASSERT(cobj->issuer.pbData); - ASSERT(cobj->issuer.cbData); - ASSERT(cobj->serial.pbData); - ASSERT(cobj->serial.cbData); - - /* No store should mean no objects were loaded */ - ASSERT(sess->store); - - /* Setup our search */ - memset(&info, 0, sizeof(info)); - memcpy(&info.SerialNumber, &cobj->serial, sizeof(info.SerialNumber)); - memcpy(&info.Issuer, &cobj->issuer, sizeof(info.Issuer)); - - cert = CertGetSubjectCertificateFromStore(sess->store, CKCAPI_ENCODINGS, &info); - - if(!cert) - { - DWORD err = GetLastError(); - - /* TODO: Is this right for a deleted certificate? */ - ASSERT(err != E_INVALIDARG); - if(err == CRYPT_E_NOT_FOUND) - return CKR_OBJECT_HANDLE_INVALID; - else - return ckcapi_winerr_to_ckr(GetLastError()); - } - - *objdata = cert_alloc_data(sess, obj, cert); - if(!(*objdata)) - { - CertFreeCertificateContext(cert); - return CKR_HOST_MEMORY; - } - - return CKR_OK; -} - -static unsigned int -cert_hash_func(CkCapiObject* obj) -{ - CertObject* cobj = (CertObject*)obj; - return ckcapi_hash_data(cobj->issuer.pbData, cobj->issuer.cbData) ^ - ckcapi_hash_data(cobj->serial.pbData, cobj->serial.cbData); -} - -static int -cert_equal_func(CkCapiObject* a, CkCapiObject* b) -{ - CertObject* ca = (CertObject*)a; - CertObject* cb = (CertObject*)b; - return ca->issuer.cbData == cb->issuer.cbData && - memcmp(ca->issuer.pbData, cb->issuer.pbData, ca->issuer.cbData) == 0 && - ca->serial.cbData == cb->serial.cbData && - memcmp(ca->serial.pbData, cb->serial.pbData, ca->serial.cbData) == 0; -} - -static void -cert_object_release(void* data) -{ - CertObject* cobj = (CertObject*)data; - ASSERT(cobj); - free(cobj); -} - -static const CkCapiObjectVtable cert_object_vtable = { - cert_load_data, - cert_hash_func, - cert_equal_func, - cert_object_release, -}; - -static CK_RV -calculate_check_value(PCCERT_CONTEXT cert, CK_ATTRIBUTE_PTR attr) -{ - BYTE* buffer; - DWORD length; - CK_RV ret; - - ASSERT(cert); - ASSERT(attr); - - /* Short cut for the measuring case */ - if(!attr->pValue) - { - attr->ulValueLen = 3; - return CKR_OK; - } - - length = 0; - if(!CryptHashCertificate(0, CALG_SHA1, 0, cert->pbCertEncoded, - cert->cbCertEncoded, NULL, &length)) - return ckcapi_winerr_to_ckr(GetLastError()); - - if(length < 3) - { - DBG(("SHA1 hash length too short: %d", length)); - return CKR_DEVICE_ERROR; - } - - buffer = malloc(length); - if(!buffer) - return CKR_HOST_MEMORY; - - if(!CryptHashCertificate(0, CALG_SHA1, 0, cert->pbCertEncoded, - cert->cbCertEncoded, buffer, &length)) - { - free(buffer); - return ckcapi_winerr_to_ckr(GetLastError()); - } - - ret = ckcapi_return_data(attr, buffer, 3); - free(buffer); - return ret; -} - - -CK_RV -ckcapi_cert_certificate_get_bytes(PCCERT_CONTEXT cert, CK_ATTRIBUTE_PTR attr) -{ - DWORD err; - - ASSERT(cert); - ASSERT(attr); - - switch(attr->type) - { - - /* - * Description of the object. - * - We use CAPI's CERT_FRIENDLY_NAME_PROP_ID property, - * converted into UTF8. - * - Yes this is slow, but this is not really a property - * that's searched on or retrieved intensively. - */ - case CKA_LABEL: - { - WCHAR* utf16 = NULL; - DWORD size; - - if(!CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, NULL, &size)) - { - err = GetLastError(); - if(err == CRYPT_E_NOT_FOUND) - utf16 = L"Unnamed Certificate"; - else - return ckcapi_winerr_to_ckr(err); - } - - if(!utf16) - { - utf16 = _alloca(size); - if(!CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, utf16, &size)) - return ckcapi_winerr_to_ckr(GetLastError()); - } - - return ckcapi_return_string(attr, utf16); - } - break; - - /* - * A byte array unique to this certificate. The CKA_ID of - * matching certificates and private keys should match. - * Should match the key identifier in an X.509v3 certificate. - * - * We use CAPI's CERT_KEY_IDENTIFIER_PROP_ID property directly. - */ - case CKA_ID: - if(!CertGetCertificateContextProperty(cert, CERT_KEY_IDENTIFIER_PROP_ID, - attr->pValue, (DWORD*)&attr->ulValueLen)) - { - err = GetLastError(); - if(err == CRYPT_E_NOT_FOUND) - return CKR_ATTRIBUTE_TYPE_INVALID; - return ckcapi_winerr_to_ckr(err); - } - return CKR_OK; - - - /* - * DER-encoding of the certificate subject name. - * - * We use CAPI's CERT_CONTEXT pCertInfo->Subject field - * directly. - */ - case CKA_SUBJECT: - return ckcapi_return_data(attr, cert->pCertInfo->Subject.pbData, - cert->pCertInfo->Subject.cbData); - - /* - * DER-encoding of the certificate issuer name. - * - * We use CAPI's CERT_CONTEXT pCertInfo->Issuer field - * directly. - */ - case CKA_ISSUER: - return ckcapi_return_data(attr, cert->pCertInfo->Issuer.pbData, - cert->pCertInfo->Issuer.cbData); - - /* - * DER-encoding of the certificate serial number. - */ - case CKA_SERIAL_NUMBER: - if(!CryptEncodeObject(X509_ASN_ENCODING, X509_MULTI_BYTE_INTEGER, - &cert->pCertInfo->SerialNumber, - attr->pValue, (DWORD*)&attr->ulValueLen)) - { - err = GetLastError(); - if(err == ERROR_FILE_NOT_FOUND) - return CKR_GENERAL_ERROR; - return ckcapi_winerr_to_ckr(err); - } - return CKR_OK; - - /* - * BER-encoding of the full certificate. - * - * We use CAPI's CERT_CONTEXT pbCertEncoded field directly. - */ - case CKA_VALUE: - return ckcapi_return_data(attr, cert->pbCertEncoded, - cert->cbCertEncoded); - - /* - * If CKA_VALUE not specified, this is where the full - * certificate can be found. - * - * We don't support this. All our certificates are present - * in full. - * - * - Spec says default to empty. - */ - case CKA_URL: - return ckcapi_return_data(attr, "", 0); - - /* - * Checksum - * - This is the first 3 bytes of the SHA hash of the DER. - */ - case CKA_CHECK_VALUE: - return calculate_check_value(cert, attr); - - /* - * Various hashes for remote retrieval. - * - Spec says default to empty. - */ - case CKA_HASH_OF_SUBJECT_PUBLIC_KEY: - case CKA_HASH_OF_ISSUER_PUBLIC_KEY: - return ckcapi_return_data(attr, "", 0); - - /* - * Start date for the certificate. - */ - case CKA_START_DATE: - return ckcapi_return_filetime(attr, &cert->pCertInfo->NotBefore); - - /* - * End date for the certificate. - */ - case CKA_END_DATE: - return ckcapi_return_filetime(attr, &cert->pCertInfo->NotAfter); - - default: - return CKR_ATTRIBUTE_TYPE_INVALID; - }; -} - -PCCERT_CONTEXT -ckcapi_cert_object_data_get_certificate(CkCapiObjectData* objdata) -{ - CertObjectData* cdata; - - ASSERT(objdata); - ASSERT(objdata->data_funcs == &cert_objdata_vtable); - - cdata = (CertObjectData*)objdata; - return cdata->cert; -} - -static CK_RV -register_cert_object(CkCapiSession* sess, PCCERT_CONTEXT cert, CkCapiObject** obj) -{ - CertObject* cobj; - CK_RV ret; - size_t len; - - /* We save the Issuer and SerialNumber for identification later */ - len = cert->pCertInfo->SerialNumber.cbData + - cert->pCertInfo->Issuer.cbData; - - cobj = calloc(1, sizeof(CertObject) + len); - if(!cobj) - return CKR_HOST_MEMORY; - - cobj->obj.id = 0; - cobj->obj.obj_funcs = &cert_object_vtable; - - /* Copy Issuer data in */ - cobj->issuer.cbData = cert->pCertInfo->Issuer.cbData; - cobj->issuer.pbData = (BYTE*)(cobj + 1); - memcpy(cobj->issuer.pbData, cert->pCertInfo->Issuer.pbData, - cobj->issuer.cbData); - - /* Copy Serial Number data in */ - cobj->serial.cbData = cert->pCertInfo->SerialNumber.cbData; - cobj->serial.pbData = cobj->issuer.pbData + cobj->issuer.cbData; - memcpy(cobj->serial.pbData, cert->pCertInfo->SerialNumber.pbData, - cobj->serial.cbData); - - ret = ckcapi_token_register_object(sess->slot, &(cobj->obj)); - if(ret != CKR_OK) - { - free(cobj); - return ret; - } - - ASSERT(cobj->obj.id != 0); - *obj = &cobj->obj; - return CKR_OK; -} - -static CK_RV -find_in_store(CkCapiSession* sess, DWORD find_type, const void *find_criteria, - CK_ATTRIBUTE_PTR match, CK_ULONG count, CkCapiArray* arr) -{ - PCCERT_CONTEXT cert = NULL; - CkCapiObject* obj; - CkCapiObjectData* objdata; - CertObjectData cdata; - DWORD err; - CK_RV ret = CKR_OK; - - /* No store, no objects */ - if(!sess->store) - return CKR_OK; - - for(;;) - { - cert = CertFindCertificateInStore(sess->store, CKCAPI_ENCODINGS, 0, - find_type, find_criteria, cert); - if(cert == NULL) - { - err = GetLastError(); - - /* Certificate not found, we don't care */ - if(err == CRYPT_E_NOT_FOUND) - return CKR_OK; - else - return ckcapi_winerr_to_ckr(err); - } - - /* Match the certificate */ - cdata.cert = cert; - cdata.base.object = 0; - cdata.base.data_funcs = &cert_objdata_vtable; - - if(ckcapi_object_data_match(&cdata.base, match, count)) - { - ret = register_cert_object(sess, cert, &obj); - if(ret == CKR_OK) - { - ASSERT(obj); - - /* Store away the object data for performance reasons */ - objdata = cert_alloc_data(sess, obj, cert); - if(objdata) - { - ckcapi_session_take_object_data(sess, obj, objdata); - - /* For continuing the enumeration */ - cert = CertDuplicateCertificateContext(cert); - } - - ckcapi_array_append(arr, obj->id); - } - } - } - - if(ret != CKR_OK && cert) - CertFreeCertificateContext(cert); - - return ret; -} - -CK_RV -ckcapi_cert_find(CkCapiSession* sess, CK_OBJECT_CLASS cls, CK_ATTRIBUTE_PTR match, - CK_ULONG count, CkCapiArray* arr) -{ - CRYPT_INTEGER_BLOB* serial = NULL; - CK_RV ret; - CK_ULONG i; - DWORD size; - - CERT_INFO find_info; /* For searching by issuer and serial */ - CRYPT_HASH_BLOB find_key; /* For searching by ID */ - - /* We only have certificates here */ - if(cls != CKO_CERTIFICATE && cls != CKO_ANY) - return CKR_OK; - - /* Only work with slots that have certificates */ - if(!(ckcapi_token_get_flags (sess->slot) & CKCAPI_SLOT_CERTS)) - return CKR_OK; - - /* - * There are some better searches we can do rather than - * listing everything. - * - * CKA_ISSUER + CKA_SERIAL_NUMBER - * See if we have a issuer and serial number for a - * specific certificate to find. - * - * CKA_ID - * Search by key identifier - * - * TODO: could search by hash (use CertFindCertificateInStore - * with CERT_FIND_HASH or CERT_FIND_SHA1_HASH or CERT_FIND_MD5_HASH) - * - * TODO: could search by issuer (use CertFindCertificateInStore - * with CERT_FIND_ISSUER_NAME) - * - * TODO: could search by subject (use CertFindCertificateInStore - * with CERT_FIND_SUBJECT_NAME) - * - * TODO: could search by CKA_VALUE (use CertFindCertificateInStore - * with CERT_FIND_EXISTING) - */ - memset(&find_info, 0, sizeof(find_info)); - memset(&find_key, 0, sizeof(find_key)); - - for(i = 0; i < count; ++i) - { - if(!match[i].pValue || !match[i].ulValueLen) - continue; - - if(match[i].type == CKA_ISSUER) - { - find_info.Issuer.cbData = match[i].ulValueLen; - find_info.Issuer.pbData = match[i].pValue; - } - - else if(match[i].type == CKA_SERIAL_NUMBER && !serial) - { - if(!CryptDecodeObject(CKCAPI_ENCODINGS, X509_MULTI_BYTE_INTEGER, - match[i].pValue, match[i].ulValueLen, 0, NULL, &size)) - { - continue; - } - - serial = calloc(1, size); - if(!serial) - continue; - - if(!CryptDecodeObject(CKCAPI_ENCODINGS, X509_MULTI_BYTE_INTEGER, - match[i].pValue, match[i].ulValueLen, 0, serial, &size)) - continue; - - ASSERT(serial->cbData); - ASSERT(serial->pbData); - - find_info.SerialNumber.cbData = serial->cbData; - find_info.SerialNumber.pbData = serial->pbData; - } - - else if(match[i].type == CKA_ID) - { - find_key.cbData = match[i].ulValueLen; - find_key.pbData = match[i].pValue; - } - } - - /* Match a specific certificate */ - if(find_info.SerialNumber.cbData && find_info.Issuer.cbData) - { - ret = find_in_store(sess, CERT_FIND_SUBJECT_CERT, &find_info, - match, count, arr); - } - - /* Find all certificates with key identifier */ - else if(find_key.cbData) - { - ret = find_in_store(sess, CERT_FIND_KEY_IDENTIFIER, &find_key, - match, count, arr); - } - - /* Match any ol certificate */ - else - { - ret = find_in_store(sess, CERT_FIND_ANY, NULL, - match, count, arr); - } - - if(serial) - free(serial); - - return ret; -} diff --git a/ckcapi-cert.h b/ckcapi-cert.h deleted file mode 100644 index 28edd9d..0000000 --- a/ckcapi-cert.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2007 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef CKCAPI_CERT_H -#define CKCAPI_CERT_H - -/* -------------------------------------------------------------------- - * - * Session = CkCapiSession - * - A PKCS#11 Session - * - * Objects = CkCapiObject - * - There's a global list of objects in ckcapi-object.c indexed by - * object handle. - * - The object itself has no attributes or cached data, but knows how - * to load data when needed. - * - Each object has a unique key which guarantees we don't load the - * same object twice with two different object handles. - * - * Object Data = CkCapiObjectData - * - Object Data is owned by the Session - * - Loaded data and/or attributes for an object. - */ - -#include "ckcapi.h" -#include "ckcapi-util.h" - -/* Find certificates matching criteria */ -CK_RV ckcapi_cert_find (CkCapiSession* sess, CK_OBJECT_CLASS cls, - CK_ATTRIBUTE_PTR match, CK_ULONG count, - CkCapiArray* arr); - -/* Called by trust and key stuff */ -CK_RV ckcapi_cert_certificate_get_bytes (PCCERT_CONTEXT cert, - CK_ATTRIBUTE_PTR attr); - -PCCERT_CONTEXT ckcapi_cert_object_data_get_certificate (CkCapiObjectData* objdata); - -#endif /* CRYPTOKI_CERT_H */ diff --git a/ckcapi-der.c b/ckcapi-der.c deleted file mode 100644 index 602b831..0000000 --- a/ckcapi-der.c +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Portions derived from NSS source files: - * lib/ckfw/capi/cobject.c - * lib/ckfw/capi/crsa.c - * - * Portions of this file: - * Copyright (C) Stef Walter 2008 - * - */ - -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Netscape security libraries. - * - * The Initial Developer of the Original Code is - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 1994-2000 - * the Initial Developer. All Rights Reserved. - * Portions created by Red Hat, Inc, are Copyright (C) 2005 - * - * Contributor(s): - * Bob Relyea (rrelyea@redhat.com) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#include "ckcapi.h" -#include "ckcapi-der.h" - -/* - * unwrap a single DER value - */ -BYTE* -ckcapi_der_unwrap(BYTE* src, DWORD n_src, - DWORD* n_result, BYTE** next) -{ - BYTE* start = src; - BYTE* end = src + n_src; - DWORD len = 0; - - /* initialize error condition return values */ - *n_result = 0; - if(next) - *next = src; - - if(n_src < 2) - return start; - - src++ ; /* skip the tag -- should check it against an expected value! */ - len = (DWORD)*src++; - if(len & 0x80) - { - DWORD count = len & 0x7f; - len = 0; - - if(count + 2 > n_src) - return start; - - while(count-- > 0) - len = (len << 8) | (DWORD)*src++; - } - - if(len + (src - start) > (DWORD)n_src) - return start; - - if(next) - *next = src + len; - - *n_result = len; - return src; -} - -/* - * write a Decimal value to a string - */ - -static char* -put_decimal_string(char* cstr, DWORD value) -{ - DWORD tenpower; - BOOL first = TRUE; - - for(tenpower = 10000000; tenpower; tenpower /= 10) - { - BYTE digit = (BYTE)(value / tenpower); - value = value % tenpower; - - /* drop leading zeros */ - if(first && (0 == digit)) - continue; - - first = FALSE; - *cstr++ = digit + '0'; - } - - /* if value was zero, put one of them out */ - if(first) - *cstr++ = '0'; - - return cstr; -} - -/* - * Create a Capi OID string value from a DER OID - */ -char* -ckcapi_der_read_oid(BYTE* oid_tag, DWORD n_oid_tag) -{ - BYTE* oid; - char *oid_str; - char *cstr; - DWORD value; - DWORD n_oid; - - /* wasn't an oid */ - if(CKCAPI_DER_OBJECT_ID != *oid_tag) - return NULL; - - oid = ckcapi_der_unwrap(oid_tag, n_oid_tag, &n_oid, NULL);; - if(n_oid < 2) - return NULL; - - oid_str = malloc(n_oid * 4); - if(!oid_str) - return NULL; - - cstr = oid_str; - cstr = put_decimal_string(cstr, (*oid) / 40); - *cstr++ = '.'; - cstr = put_decimal_string(cstr, (*oid) % 40); - n_oid--; - - value = 0; - while(n_oid--) - { - oid++; - value = (value << 7) + (*oid & 0x7f); - if(0 == (*oid & 0x80)) - { - *cstr++ = '.'; - cstr = put_decimal_string(cstr, value); - value = 0; - } - } - - *cstr = 0; /* NULL terminate */ - - if(value != 0) - { - free(oid_str); - return NULL; - } - - return oid_str; -} diff --git a/ckcapi-der.h b/ckcapi-der.h deleted file mode 100644 index 9030ce3..0000000 --- a/ckcapi-der.h +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2008 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef CKCAPI_DER_H -#define CKCAPI_DER_H - -#include "ckcapi.h" - -#define CKCAPI_DER_OCTET_STRING 0x04 -#define CKCAPI_DER_OBJECT_ID 0x06 -#define CKCAPI_DER_SEQUENCE 0x10 -#define CKCAPI_DER_CONSTRUCTED 0x20 - -BYTE* ckcapi_der_unwrap (BYTE* src, DWORD n_src, - DWORD* n_result, BYTE** next); - -char* ckcapi_der_read_oid (BYTE* oid_tag, DWORD n_oid_tag); - -#endif /* CRYPTOKI_DER_H */ diff --git a/ckcapi-key.c b/ckcapi-key.c deleted file mode 100644 index 34a6ae1..0000000 --- a/ckcapi-key.c +++ /dev/null @@ -1,1083 +0,0 @@ -/* - * Copyright (C) 2008 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "ckcapi.h" -#include "ckcapi-cert.h" -#include "ckcapi-key.h" -#include "ckcapi-object.h" -#include "ckcapi-session.h" -#include "ckcapi-token.h" -#include "x509-usages.h" - -typedef struct _KeyObject -{ - CkCapiObject obj; - - /* The raw key identifier */ - CRYPT_HASH_BLOB key_identifier; - CK_OBJECT_CLASS object_class; -} -KeyObject; - -typedef struct _KeyObjectData -{ - CkCapiObjectData base; - CK_OBJECT_CLASS object_class; - CRYPT_INTEGER_BLOB key_identifier; - CRYPT_DATA_BLOB raw_public_key; - CRYPT_KEY_PROV_INFO* prov_info; -} -KeyObjectData; - -static CK_RV -load_key_handle (CkCapiObjectData* objdata, HCRYPTPROV* ret_prov, - HCRYPTKEY* ret_key) -{ - KeyObjectData* kdata = (KeyObjectData*)objdata; - HCRYPTPROV prov; - HCRYPTKEY key; - DWORD error; - - ASSERT(kdata); - ASSERT(ret_key); - ASSERT(ret_prov); - - if(!CryptAcquireContextW(&prov, kdata->prov_info->pwszContainerName, - kdata->prov_info->pwszProvName, - kdata->prov_info->dwProvType, 0)) - { - return ckcapi_winerr_to_ckr(GetLastError()); - } - - if(!CryptGetUserKey(prov, kdata->prov_info->dwKeySpec, &key)) - { - error = GetLastError(); - CryptReleaseContext(prov, 0); - return ckcapi_winerr_to_ckr(error); - } - - *ret_key = key; - *ret_prov = prov; - - return CKR_OK; -} - - -static CK_RV -load_raw_public_key(KeyObjectData* kdata) -{ - BOOL success = FALSE; - HCRYPTPROV prov; - HCRYPTKEY key; - CK_RV ret; - DWORD error; - - ASSERT(kdata); - ASSERT(!kdata->raw_public_key.pbData); - - ret = load_key_handle(&kdata->base, &prov, &key); - if(ret != CKR_OK) - return ret; - - if(CryptExportKey(key, 0, PUBLICKEYBLOB, 0, NULL, &kdata->raw_public_key.cbData)) - { - kdata->raw_public_key.pbData = malloc(kdata->raw_public_key.cbData); - if(!kdata->raw_public_key.pbData) - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - else - { - if(CryptExportKey(key, 0, PUBLICKEYBLOB, 0, kdata->raw_public_key.pbData, - &kdata->raw_public_key.cbData)) - { - success = TRUE; - } - } - } - - CryptReleaseContext(prov, 0); - CryptDestroyKey(key); - - if(success) - { - return CKR_OK; - } - else - { - error = GetLastError(); - if(error == NTE_BAD_KEY_STATE) - return CKR_ATTRIBUTE_SENSITIVE; - return ckcapi_winerr_to_ckr(error); - } -} - -static CK_RV -lookup_rsa_attribute(KeyObjectData* kdata, CK_ATTRIBUTE_PTR attr) -{ - PUBLICKEYSTRUC* header; - RSAPUBKEY* pubkey; - CK_ULONG number; - CK_RV ret; - - ASSERT(kdata); - ASSERT(attr); - - if(!kdata->raw_public_key.pbData) - { - ret = load_raw_public_key(kdata); - if(ret != CKR_OK) - return ret; - } - - header = (PUBLICKEYSTRUC*)kdata->raw_public_key.pbData; - if(!header->bType == PUBLICKEYBLOB) - return CKR_GENERAL_ERROR; - - pubkey = (RSAPUBKEY*)(header + 1); - if(!pubkey->magic == 0x31415352) - return CKR_GENERAL_ERROR; - - switch(attr->type) - { - case CKA_MODULUS_BITS: - number = pubkey->bitlen; - return ckcapi_return_data(attr, &number, sizeof(CK_ULONG)); - - case CKA_PUBLIC_EXPONENT: - return ckcapi_return_dword_as_bytes(attr, pubkey->pubexp); - - case CKA_MODULUS: - return ckcapi_return_reversed_data(attr, (pubkey + 1), - pubkey->bitlen / 8); - - case CKA_PRIVATE_EXPONENT: - case CKA_PRIME_1: - case CKA_PRIME_2: - case CKA_EXPONENT_1: - case CKA_EXPONENT_2: - case CKA_COEFFICIENT: - if(kdata->object_class == CKO_PRIVATE_KEY) - return CKR_ATTRIBUTE_SENSITIVE; - else - return CKR_ATTRIBUTE_TYPE_INVALID; - - default: - ASSERT(FALSE); - return CKR_ATTRIBUTE_TYPE_INVALID; - } -} - -static CK_RV -key_bool_attribute(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) -{ - KeyObjectData* kdata = (KeyObjectData*)objdata; - CK_BBOOL val; - CK_BBOOL is_private, is_rsa; - - ASSERT(objdata); - ASSERT(attr); - - is_private = (kdata->object_class == CKO_PRIVATE_KEY); - is_rsa = kdata->prov_info->dwProvType == PROV_RSA_FULL; - - switch(attr->type) - { - - /* - * Whether to authenticate before every use. - * - CAPI does all authentication - */ - case CKA_ALWAYS_AUTHENTICATE: - val = CK_FALSE; - break; - - /* - * Whether this key has always been sensitive. - * TODO: Can we detect this? - */ - case CKA_ALWAYS_SENSITIVE: - val = CK_FALSE; - break; - - /* - * Whether this key can be used to decrypt. - * - CKK_RSA but not CKK_DSA. - */ - case CKA_DECRYPT: - val = is_private && is_rsa; - break; - - /* - * Whether this key can be used to derive a session or - * other key. - * - Not true for CKK_RSA or CKK_DSA. - */ - case CKA_DERIVE: - val = CK_FALSE; - break; - - /* - * Whether or not this key can be used to encrypt?. - * TODO: Support for RSA public keys. - */ - case CKA_ENCRYPT: - val = CK_FALSE; - break; - - /* - * Whether this key can be exported or not. - * TODO: We may want to support this for public keys. - */ - case CKA_EXTRACTABLE: - val = CK_FALSE; - break; - - /* - * Whether this key was created on token. - * TODO: Can we implement this properly? - */ - case CKA_LOCAL: - val = CK_FALSE; - break; - - /* - * Whether this object is modifiable. - * - Keys are generally. never modifiable. - */ - case CKA_MODIFIABLE: - val = CK_FALSE; - break; - - /* - * Whether this key was ever extractable. - * TODO: Can we determine this? - */ - case CKA_NEVER_EXTRACTABLE: - val = CK_FALSE; - break; - - /* - * Whether this is a private object or not. - * - This 'private' means login before use. But maps - * well to private key use, since we're always logged in. - */ - case CKA_PRIVATE: - val = is_private; - break; - - /* - * Whether this is a sensitive object or not. - * - Private keys are sensitive, some attributes not - * readable. - */ - case CKA_SENSITIVE: - val = is_private; - break; - - /* - * Can this key sign stuff? - * - Private keys can sign. - */ - case CKA_SIGN: - val = is_private; - break; - - /* - * Can this key sign recoverable. - * TODO: Private RSA keys can sign recoverable. - */ - case CKA_SIGN_RECOVER: - val = CK_FALSE; - break; - - /* - * Is this stored on the token? - * - All CAPI objects are. - */ - case CKA_TOKEN: - val = CK_TRUE; - break; - - /* - * Is this key trusted? - * - A nebulous question. - */ - case CKA_TRUSTED: - val = CK_FALSE; - break; - - /* - * Key wrapping with public keys. - */ - case CKA_WRAP: - if(is_private) - return CKR_ATTRIBUTE_TYPE_INVALID; - val = CK_FALSE; - break; - - /* - * Key wrapping on private keys. - */ - case CKA_WRAP_WITH_TRUSTED: - if(!is_private) - return CKR_ATTRIBUTE_TYPE_INVALID; - val = CK_FALSE; - break; - - /* - * Can do a unwrap operation? - * - We don't implement this. - */ - case CKA_UNWRAP: - val = CK_FALSE; - break; - - /* - * Wrap, and unwrap stuff. - * - We don't implement this. - */ - case CKA_UNWRAP_TEMPLATE: - return CKR_ATTRIBUTE_TYPE_INVALID; - - /* - * Whether this key can be used to verify? - * TODO: Support for public keys. - */ - case CKA_VERIFY: - val = CK_FALSE; - break; - - /* - * Whether this key can be used to verify? - * TODO: Support for public keys. - */ - case CKA_VERIFY_RECOVER: - val = CK_FALSE; - break; - - default: - return CKR_ATTRIBUTE_TYPE_INVALID; - }; - - return ckcapi_return_data(attr, &val, sizeof(CK_BBOOL)); -} - -static CK_RV -key_ulong_attribute(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) -{ - KeyObjectData* kdata = (KeyObjectData*)objdata; - CK_ULONG val; - - ASSERT(kdata); - ASSERT(attr); - - switch(attr->type) - { - - /* - * Object class. - */ - case CKA_CLASS: - val = kdata->object_class; - break; - - /* - * The key type. - * - Right now we only support (and load) RSA. - */ - case CKA_KEY_TYPE: - if(kdata->prov_info->dwProvType == PROV_RSA_FULL) - val = CKK_RSA; - else - val = CK_UNAVAILABLE_INFORMATION; - break; - - /* - * The key generation mechanism. - * TODO: We don't yet support key generation. - */ - case CKA_KEY_GEN_MECHANISM: - val = CK_UNAVAILABLE_INFORMATION; - break; - - /* - * The RSA modulus bits. - */ - case CKA_MODULUS_BITS: - if(kdata->prov_info->dwProvType == PROV_RSA_FULL) - return lookup_rsa_attribute(kdata, attr); - else - return CKR_ATTRIBUTE_TYPE_INVALID; - - default: - return CKR_ATTRIBUTE_TYPE_INVALID; - }; - - return ckcapi_return_data(attr, &val, sizeof(CK_ULONG)); -} - -static CK_RV -key_bytes_attribute(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) -{ - KeyObjectData* kdata = (KeyObjectData*)objdata; - CK_MECHANISM_TYPE allowed_mechanisms[] = { CKM_RSA_PKCS }; - WCHAR* label; - - ASSERT(kdata); - ASSERT(attr); - - switch(attr->type) - { - /* - * The ID of the key. This should match the ID we - * return for any matching certificates. - */ - case CKA_ID: - return ckcapi_return_data(attr, kdata->key_identifier.pbData, - kdata->key_identifier.cbData); - - /* - * The key label. - * - We use the container name. - */ - case CKA_LABEL: - label = kdata->prov_info->pwszContainerName; - if(!label) - label = L"Unnamed Key"; - return ckcapi_return_string(attr, label); - - /* - * The subject of the related certificate. - * TODO: Implement this lookup. - */ - case CKA_SUBJECT: - return ckcapi_return_data(attr, "", 0); - - /* - * Allowed mechanisms with this key. - * - RSA used with CKM_RSA - * TODO: Needs updating when DSA implemented. - */ - case CKA_ALLOWED_MECHANISMS: - return ckcapi_return_data(attr, &allowed_mechanisms, - sizeof(allowed_mechanisms)); - - /* - * Various RSA public attributes. - */ - case CKA_MODULUS: - case CKA_PUBLIC_EXPONENT: - case CKA_PRIVATE_EXPONENT: - case CKA_PRIME_1: - case CKA_PRIME_2: - case CKA_EXPONENT_1: - case CKA_EXPONENT_2: - case CKA_COEFFICIENT: - if(kdata->prov_info->dwProvType == PROV_RSA_FULL) - return lookup_rsa_attribute(kdata, attr); - else - return CKR_ATTRIBUTE_TYPE_INVALID; - - /* - * Last date this key can be used. - * TODO: Does CAPI support this ability? - */ - case CKA_END_DATE: - case CKA_START_DATE: - return ckcapi_return_data(attr, "", 0); - - default: - return CKR_ATTRIBUTE_TYPE_INVALID; - }; -} - -static void -key_release(void* data) -{ - KeyObjectData* kdata = (KeyObjectData*)data; - ASSERT(kdata); - - ASSERT(kdata->key_identifier.pbData); - ASSERT(kdata->prov_info); - - free(kdata->key_identifier.pbData); - free(kdata->prov_info); - - free(kdata); -} - -static const CkCapiObjectDataVtable key_objdata_vtable = { - key_bool_attribute, - key_ulong_attribute, - key_bytes_attribute, - key_release, -}; - -static CRYPT_KEY_PROV_INFO* -duplicate_prov_info(CRYPT_KEY_PROV_INFO* original) -{ - DWORD container_length, prov_length; - CRYPT_KEY_PROV_INFO* result; - DWORD length, i; - BYTE* at; - BYTE* end; - - if(!original) - return NULL; - - /* Go through and calculate the length */ - length = sizeof(CRYPT_KEY_PROV_INFO); - if(original->pwszContainerName) - { - container_length = (wcslen(original->pwszContainerName) + 1) * sizeof(WCHAR); - length += container_length; - } - - if(original->pwszProvName) - { - prov_length = (wcslen(original->pwszProvName) + 1) * sizeof(WCHAR); - length += prov_length; - } - - length += sizeof(CRYPT_KEY_PROV_PARAM) * original->cProvParam; - for(i = 0; i < original->cProvParam; ++i) - length += original->rgProvParam[i].cbData; - - /* Allocate a single block of memory for everything */ - at = (BYTE*)malloc(length); - if(!at) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return NULL; - } - - /* Copy in very carefully */ - end = at + length; - - memcpy(at, original, sizeof(CRYPT_KEY_PROV_INFO)); - result = (CRYPT_KEY_PROV_INFO*)at; - at += sizeof(CRYPT_KEY_PROV_INFO); - - if(result->pwszContainerName) - { - memcpy(at, result->pwszContainerName, container_length); - result->pwszContainerName = (LPWSTR)at; - at += container_length; - } - - if(result->pwszProvName) - { - memcpy(at, result->pwszProvName, prov_length); - result->pwszProvName = (LPWSTR)at; - at += prov_length; - } - - if(original->cProvParam) - { - memcpy(at, result->rgProvParam, sizeof(CRYPT_KEY_PROV_PARAM) * result->cProvParam); - result->rgProvParam = (CRYPT_KEY_PROV_PARAM*)at; - at += sizeof(CRYPT_KEY_PROV_PARAM) * result->cProvParam; - - for(i = 0; i < result->cProvParam; ++i) - { - memcpy(at, result->rgProvParam[i].pbData, result->rgProvParam[i].cbData); - result->rgProvParam[i].pbData = (BYTE*)at; - at += result->rgProvParam[i].cbData; - } - } - - ASSERT(at == end); - return result; -} - -static CkCapiObjectData* -key_alloc_data(CkCapiSession* sess, CkCapiObject* obj, CRYPT_KEY_PROV_INFO* prov_info) -{ - KeyObject* kobj = (KeyObject*)obj; - KeyObjectData* kdata; - - kdata = (KeyObjectData*)calloc(1, sizeof(KeyObjectData)); - if(!kdata) - return NULL; - - /* Allocate memory for key identifier */ - kdata->key_identifier.pbData = malloc(kobj->key_identifier.cbData); - if(!kdata->key_identifier.pbData) - { - free(kdata); - return NULL; - } - - /* Setup the object data */ - kdata->object_class = kobj->object_class; - kdata->prov_info = prov_info; - kdata->key_identifier.cbData = kobj->key_identifier.cbData; - memcpy(kdata->key_identifier.pbData, kobj->key_identifier.pbData, - kdata->key_identifier.cbData); - kdata->raw_public_key.pbData = NULL; - kdata->raw_public_key.cbData = 0; - - kdata->base.object = obj->id; - kdata->base.data_funcs = &key_objdata_vtable; - - return &(kdata->base); -} - -static BOOL WINAPI -load_key_property_info(PCRYPT_HASH_BLOB key_identifier, DWORD flags, - void* reserved, void* arg, DWORD n_props, DWORD* props, - void** datas, DWORD* n_datas) -{ - CRYPT_KEY_PROV_INFO** prov_info = (CRYPT_KEY_PROV_INFO**)arg; - DWORD i; - - /* - * Already got a provider info. This shouldn't happen - * but can occur if the same key is present twice. - */ - if(*prov_info) - return TRUE; - - /* Find the key provider info property */ - for(i = 0; i < n_props; ++i) - { - if(props[i] == CERT_KEY_PROV_INFO_PROP_ID) - { - *prov_info = duplicate_prov_info((CRYPT_KEY_PROV_INFO*)datas[i]); - break; - } - } - - return TRUE; -} - -static CK_RV -key_load_data(CkCapiSession* sess, CkCapiObject* obj, CkCapiObjectData** objdata) -{ - KeyObject* kobj = (KeyObject*)obj; - CRYPT_KEY_PROV_INFO* prov_info = NULL; - - ASSERT(kobj); - ASSERT(objdata); - - /* Load the provider info */ - if(!CryptEnumKeyIdentifierProperties((CRYPT_HASH_BLOB*)&kobj->key_identifier, - CERT_KEY_PROV_INFO_PROP_ID, 0, NULL, NULL, - &prov_info, load_key_property_info)) - return ckcapi_winerr_to_ckr(GetLastError()); - - /* No provider info, bad news */ - if(!prov_info) - return CKR_GENERAL_ERROR; - - *objdata = key_alloc_data(sess, obj, prov_info); - if(!(*objdata)) - { - free(prov_info); - return CKR_HOST_MEMORY; - } - - return CKR_OK; -} - -static unsigned int -key_hash_func(CkCapiObject* obj) -{ - KeyObject* kobj = (KeyObject*)obj; - return ckcapi_hash_data(kobj->key_identifier.pbData, kobj->key_identifier.cbData) ^ - ckcapi_hash_integer((int)kobj->object_class); -} - -static int -key_equal_func(CkCapiObject* a, CkCapiObject* b) -{ - KeyObject* ka = (KeyObject*)a; - KeyObject* kb = (KeyObject*)b; - return ka->object_class == kb->object_class && - ka->key_identifier.cbData == kb->key_identifier.cbData && - memcmp(ka->key_identifier.pbData, kb->key_identifier.pbData, ka->key_identifier.cbData) == 0; -} - -static void -key_object_release(void* data) -{ - KeyObject* kobj = (KeyObject*)data; - ASSERT(kobj); - free(kobj); -} - -static const CkCapiObjectVtable key_object_vtable = { - key_load_data, - key_hash_func, - key_equal_func, - key_object_release, -}; - -static CK_RV -register_key_object(CkCapiSession* sess, CK_OBJECT_CLASS cls, - CRYPT_HASH_BLOB* key_identifier, CkCapiObject** obj) -{ - KeyObject* kobj; - CK_RV ret; - - ASSERT(obj); - ASSERT(key_identifier); - ASSERT(cls == CKO_PRIVATE_KEY || cls == CKO_PUBLIC_KEY); - - kobj = calloc(1, sizeof(KeyObject) + key_identifier->cbData); - if(!kobj) - return CKR_HOST_MEMORY; - - kobj->obj.id = 0; - kobj->obj.obj_funcs = &key_object_vtable; - - kobj->object_class = cls; - kobj->key_identifier.pbData = (BYTE*)(kobj + 1); - kobj->key_identifier.cbData = key_identifier->cbData; - memcpy(kobj->key_identifier.pbData, key_identifier->pbData, - kobj->key_identifier.cbData); - - ret = ckcapi_token_register_object(sess->slot, &(kobj->obj)); - if(ret != CKR_OK) - { - free(kobj); - return ret; - } - - ASSERT(kobj->obj.id != 0); - *obj = &(kobj->obj); - - return CKR_OK; -} - -typedef struct _EnumArguments -{ - CkCapiSession* sess; - CK_OBJECT_CLASS object_class; - CK_ATTRIBUTE_PTR match; - CK_ULONG count; - CkCapiArray* results; - CK_RV ret; -} -EnumArguments; - -static BOOL WINAPI -enum_key_property_info(PCRYPT_HASH_BLOB key_identifier, DWORD flags, - void* reserved, void* arg, DWORD n_props, DWORD* props, - void** datas, DWORD* n_datas) -{ - EnumArguments* args = (EnumArguments*)arg; - CRYPT_KEY_PROV_INFO* prov_info = NULL; - CkCapiObject *obj = NULL; - KeyObjectData kdata; - DWORD i; - - /* Find the key provider info property */ - for(i = 0; i < n_props; ++i) - { - if(props[i] == CERT_KEY_PROV_INFO_PROP_ID) - { - prov_info = (CRYPT_KEY_PROV_INFO*)datas[i]; - break; - } - } - - /* Strange key, skip */ - if(!prov_info) - return TRUE; - - /* Match the public key */ - kdata.prov_info = prov_info; - kdata.object_class = args->object_class; - kdata.base.object = 0; - kdata.base.data_funcs = &key_objdata_vtable; - - if(ckcapi_object_data_match(&kdata.base, args->match, args->count)) - { - args->ret = register_key_object(args->sess, args->object_class, key_identifier, &obj); - if(args->ret == CKR_OK) - { - ASSERT(obj); - ckcapi_array_append(args->results, obj->id); - } - } - - return TRUE; - -} - -static CK_RV -find_any_keys(CkCapiSession* sess, CK_OBJECT_CLASS cls, - CK_ATTRIBUTE_PTR match, CK_ULONG count, CkCapiArray* arr) -{ - CRYPT_HASH_BLOB find_id; - EnumArguments enum_args; - CK_ULONG i; - - /* Try to setup for an efficient search based on key id */ - memset(&find_id, 0, sizeof(find_id)); - for(i = 0; i < count; ++i) - { - if(!match[i].pValue || !match[i].ulValueLen) - continue; - if(match[i].type == CKA_ID) - { - find_id.cbData = match[i].ulValueLen; - find_id.pbData = match[i].pValue; - } - } - - enum_args.sess = sess; - enum_args.match = match; - enum_args.count = count; - enum_args.results = arr; - enum_args.object_class = cls; - enum_args.ret = CKR_OK; - - if(!CryptEnumKeyIdentifierProperties(find_id.cbData != 0 ? &find_id : NULL, - CERT_KEY_PROV_INFO_PROP_ID, 0, NULL, NULL, - &enum_args, enum_key_property_info)) - return ckcapi_winerr_to_ckr(GetLastError()); - - return enum_args.ret; -} - -static CK_RV -list_matching_certificates(CkCapiSession* sess, CK_ATTRIBUTE_PTR match, - CK_ULONG count, CkCapiArray* arr) -{ - CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; - CK_ATTRIBUTE search[3]; - CK_ULONG n_search = 0; - CK_ULONG i; - - /* The class */ - search[0].type = CKA_CLASS; - search[0].pValue = &cert_class; - search[0].ulValueLen = sizeof(CK_OBJECT_CLASS); - ++n_search; - - for(i = 0; i < count && n_search < 3; ++i) - { - /* - * This is the attributes that tie a certificate - * to key object, so try match certs with these - */ - if(match[i].type == CKA_ID) - { - search[n_search].type = match[i].type; - search[n_search].pValue = match[i].pValue; - search[n_search].ulValueLen = match[i].ulValueLen; - ++n_search; - } - } - - /* Do the certificate search */ - return ckcapi_cert_find(sess, CKO_CERTIFICATE, search, n_search, arr); -} - -static CK_RV -find_certificate_key(CkCapiSession* session, CK_OBJECT_CLASS cls, - CK_ATTRIBUTE_PTR match, CK_ULONG count, - PCCERT_CONTEXT cert, CkCapiArray* arr) -{ - CRYPT_KEY_PROV_INFO* prov_info; - CRYPT_HASH_BLOB key_identifier; - CkCapiObjectData* objdata; - KeyObjectData kdata; - CkCapiObject* obj; - DWORD prov_length; - DWORD error; - CK_RV ret = CKR_OK; - - /* Look up the key provider info and identifier */ - if(!CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, NULL, &prov_length) || - !CertGetCertificateContextProperty(cert, CERT_KEY_IDENTIFIER_PROP_ID, NULL, &key_identifier.cbData)) - { - error = GetLastError(); - if(error == CRYPT_E_NOT_FOUND) - return CKR_OK; - return ckcapi_winerr_to_ckr(error); - } - - /* We own the info memory */ - prov_info = malloc(prov_length); - if(!prov_info) - return CKR_HOST_MEMORY; - key_identifier.pbData = malloc(key_identifier.cbData); - if(!key_identifier.pbData) - { - free(prov_info); - return CKR_HOST_MEMORY; - } - - /* Lookup the key provider info and identifier */ - if(CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, prov_info, &prov_length) && - CertGetCertificateContextProperty(cert, CERT_KEY_IDENTIFIER_PROP_ID, key_identifier.pbData, &key_identifier.cbData)) - { - kdata.object_class = cls; - kdata.prov_info = prov_info; - kdata.key_identifier = key_identifier; - kdata.base.object = 0; - kdata.base.data_funcs = &key_objdata_vtable; - - if(ckcapi_object_data_match(&kdata.base, match, count)) - { - ret = register_key_object(session, cls, &key_identifier, &obj); - if(ret == CKR_OK) - { - ASSERT(obj); - - /* Store away the object data for performance reasons */ - objdata = key_alloc_data(session, obj, prov_info); - if(objdata) - { - ckcapi_session_take_object_data(session, obj, objdata); - - /* Note these are used, and not to be freed */ - key_identifier.pbData = NULL; - key_identifier.cbData = 0; - prov_info = NULL; - } - - ckcapi_array_append(arr, obj->id); - } - } - } - else - { - ret = ckcapi_winerr_to_ckr(GetLastError()); - } - - if(key_identifier.pbData) - free(key_identifier.pbData); - if(prov_info) - free(prov_info); - - return ret; -} - -static CK_RV -find_certificate_keys(CkCapiSession* session, CK_OBJECT_CLASS cls, - CK_ATTRIBUTE_PTR match, CK_ULONG count, CkCapiArray* arr) -{ - CK_OBJECT_HANDLE id; - CkCapiObjectData* certdata; - CkCapiArray* certarr; - PCCERT_CONTEXT cert; - CK_RV ret = CKR_OK; - CK_ULONG i; - - /* Get a list of all certificates */ - certarr = ckcapi_array_new(0, 1, sizeof(CK_OBJECT_HANDLE)); - if(!certarr) - return CKR_HOST_MEMORY; - ret = list_matching_certificates(session, match, count, certarr); - - /* Now match each of them against our criteria */ - if(ret == CKR_OK) - { - for(i = 0; i < certarr->len; ++i) - { - id = ckcapi_array_index(certarr, CK_OBJECT_HANDLE, i); - ASSERT(id); - - /* Get the certificate data for this certificate object */ - if(ckcapi_session_get_object_data_for(session, id, &certdata) != CKR_OK) - continue; - - /* Get the certificate context */ - cert = ckcapi_cert_object_data_get_certificate(certdata); - if(!cert) - continue; - - /* Remember we can have either or both keys for each certificate */ - ret = find_certificate_key(session, cls, match, count, cert, arr); - } - } - - ckcapi_array_free(certarr, TRUE); - return ret; -} - -CK_RV -ckcapi_key_find(CkCapiSession* sess, CK_OBJECT_CLASS cls, - CK_ATTRIBUTE_PTR match, CK_ULONG count, CkCapiArray* arr) -{ - CK_RV ret = CKR_OK; - - /* Is this somewhere we have all keys present? */ - if(ckcapi_token_get_flags(sess->slot) & CKCAPI_SLOT_ANYKEY) - { - if((cls == CKO_PRIVATE_KEY || cls == CKO_ANY) && ret == CKR_OK) - ret = find_any_keys(sess, CKO_PRIVATE_KEY, match, count, arr); - if((cls == CKO_PUBLIC_KEY || cls == CKO_ANY) && ret == CKR_OK) - ret = find_any_keys(sess, CKO_PUBLIC_KEY, match, count, arr); - } - - /* Otherwise we can only list the keys that have certificates */ - else - { - if((cls == CKO_PRIVATE_KEY || cls == CKO_ANY) && ret == CKR_OK) - ret = find_certificate_keys(sess, CKO_PRIVATE_KEY, match, count, arr); - if((cls == CKO_PUBLIC_KEY || cls == CKO_ANY) && ret == CKR_OK) - ret = find_certificate_keys(sess, CKO_PUBLIC_KEY, match, count, arr); - } - - return ret; -} - -DWORD -ckcapi_key_object_data_get_bits(CkCapiObjectData* objdata) -{ - KeyObjectData* kdata; - PUBLICKEYSTRUC* header; - RSAPUBKEY* pubkey; - CK_RV ret; - - ASSERT(objdata); - - kdata = (KeyObjectData*)objdata; - - if(!kdata->raw_public_key.pbData) - { - ret = load_raw_public_key(kdata); - if(ret != CKR_OK) - return ret; - } - - header = (PUBLICKEYSTRUC*)kdata->raw_public_key.pbData; - if(!header->bType == PUBLICKEYBLOB) - return 0; - - pubkey = (RSAPUBKEY*)(header + 1); - if(!pubkey->magic == 0x31415352) - return 0; - - return pubkey->bitlen; -} - -CRYPT_KEY_PROV_INFO* -ckcapi_key_object_data_get_prov_info(CkCapiObjectData* objdata) -{ - KeyObjectData* kdata; - - ASSERT(objdata); - kdata = (KeyObjectData*)objdata; - return kdata->prov_info; -} diff --git a/ckcapi-key.h b/ckcapi-key.h deleted file mode 100644 index eabe7a9..0000000 --- a/ckcapi-key.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2008 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef CKCAPI_KEY_H -#define CKCAPI_KEY_H - -#include "ckcapi.h" - -/* Find key objects matching criteria */ -CK_RV ckcapi_key_find (CkCapiSession* sess, CK_OBJECT_CLASS cls, - CK_ATTRIBUTE_PTR match, CK_ULONG count, - CkCapiArray* arr); - -DWORD ckcapi_key_object_data_get_bits (CkCapiObjectData* objdata); - -CRYPT_KEY_PROV_INFO* ckcapi_key_object_data_get_prov_info (CkCapiObjectData* objdata); - -#endif /* CRYPTOKI_KEY_H */ diff --git a/ckcapi-object.c b/ckcapi-object.c deleted file mode 100644 index 3143700..0000000 --- a/ckcapi-object.c +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (C) 2007 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "ckcapi.h" -#include "ckcapi-object.h" - -#include "pkcs11/pkcs11n.h" - -#include - -enum -{ - DATA_UNKNOWN = 0, - DATA_BOOL, - DATA_ULONG, - DATA_BYTES -}; - -int -attribute_data_type(CK_ATTRIBUTE_TYPE type) -{ - switch(type) - { - // CK_ULONG attribute types - case CKA_CLASS: - case CKA_CERTIFICATE_TYPE: - case CKA_CERTIFICATE_CATEGORY: - case CKA_KEY_TYPE: - case CKA_MODULUS_BITS: - case CKA_PRIME_BITS: - /* case CKA_SUBPRIME_BITS: */ - case CKA_SUB_PRIME_BITS: - case CKA_VALUE_BITS: - case CKA_VALUE_LEN: - case CKA_KEY_GEN_MECHANISM: - case CKA_HW_FEATURE_TYPE: - case CKA_PIXEL_X: - case CKA_PIXEL_Y: - case CKA_RESOLUTION: - case CKA_CHAR_ROWS: - case CKA_CHAR_COLUMNS: - case CKA_BITS_PER_PIXEL: - case CKA_MECHANISM_TYPE: - case CKA_JAVA_MIDP_SECURITY_DOMAIN: - case CKA_TRUST_SERVER_AUTH: - case CKA_TRUST_CLIENT_AUTH: - case CKA_TRUST_CODE_SIGNING: - case CKA_TRUST_EMAIL_PROTECTION: - case CKA_TRUST_IPSEC_END_SYSTEM: - case CKA_TRUST_IPSEC_TUNNEL: - case CKA_TRUST_IPSEC_USER: - case CKA_TRUST_TIME_STAMPING: - return DATA_ULONG; - - // CK_BBOOL attribute types - case CKA_TOKEN: - case CKA_PRIVATE: - case CKA_MODIFIABLE: - case CKA_TRUSTED: - case CKA_SENSITIVE: - case CKA_DECRYPT: - case CKA_SIGN: - case CKA_SIGN_RECOVER: - case CKA_UNWRAP: - case CKA_EXTRACTABLE: - case CKA_NEVER_EXTRACTABLE: - case CKA_ALWAYS_SENSITIVE: - case CKA_WRAP_WITH_TRUSTED: - case CKA_ALWAYS_AUTHENTICATE: - case CKA_ENCRYPT: - case CKA_WRAP: - case CKA_VERIFY: - case CKA_VERIFY_RECOVER: - case CKA_DERIVE: - case CKA_LOCAL: - case CKA_RESET_ON_INIT: - case CKA_HAS_RESET: - case CKA_COLOR: - case CKA_TRUST_STEP_UP_APPROVED: - return DATA_BOOL; - - // Raw or string data - case CKA_LABEL: - case CKA_APPLICATION: - case CKA_VALUE: - case CKA_OBJECT_ID: - case CKA_CHECK_VALUE: - case CKA_ISSUER: - case CKA_SERIAL_NUMBER: - case CKA_SUBJECT: - case CKA_ID: - case CKA_URL: - case CKA_HASH_OF_SUBJECT_PUBLIC_KEY: - case CKA_HASH_OF_ISSUER_PUBLIC_KEY: - case CKA_AC_ISSUER: - case CKA_OWNER: - case CKA_ATTR_TYPES: - case CKA_MODULUS: - case CKA_PUBLIC_EXPONENT: - case CKA_PRIVATE_EXPONENT: - case CKA_PRIME_1: - case CKA_PRIME_2: - case CKA_EXPONENT_1: - case CKA_EXPONENT_2: - case CKA_COEFFICIENT: - case CKA_PRIME: - case CKA_SUBPRIME: - case CKA_BASE: - case CKA_ECDSA_PARAMS: - /* case CKA_EC_PARAMS: */ - case CKA_EC_POINT: - case CKA_CHAR_SETS: - case CKA_ENCODING_METHODS: - case CKA_MIME_TYPES: - case CKA_REQUIRED_CMS_ATTRIBUTES: - case CKA_DEFAULT_CMS_ATTRIBUTES: - case CKA_SUPPORTED_CMS_ATTRIBUTES: - case CKA_CERT_SHA1_HASH: - case CKA_CERT_MD5_HASH: - case CKA_ALLOWED_MECHANISMS: - case CKA_START_DATE: - case CKA_END_DATE: - return DATA_BYTES; - - // Arrays are nasty - case CKA_WRAP_TEMPLATE: - case CKA_UNWRAP_TEMPLATE: - default: - DBG(("unknown attribute type: %x", type)); - return DATA_UNKNOWN; - }; -} - -CK_BBOOL -ckcapi_object_data_match_attr(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR match) -{ - CK_ATTRIBUTE attr; - CK_RV rv; - int dtype; - - ASSERT(match); - ASSERT(objdata); - ASSERT(objdata->data_funcs); - - /* Get the data type of the attribute */ - dtype = attribute_data_type(match->type); - if(dtype == DATA_UNKNOWN) - return CK_FALSE; - - /* We only do byte matching */ - if(match->pValue == NULL) - return CK_FALSE; - - /* Only load as much data as is needed */ - attr.type = match->type; - attr.pValue = _alloca(match->ulValueLen > 4 ? match->ulValueLen : 4); - attr.ulValueLen = match->ulValueLen; - - switch(dtype) - { - case DATA_BOOL: - rv = (objdata->data_funcs->get_bool)(objdata, &attr); - break; - case DATA_ULONG: - rv = (objdata->data_funcs->get_ulong)(objdata, &attr); - break; - case DATA_BYTES: - rv = (objdata->data_funcs->get_bytes)(objdata, &attr); - break; - default: - ASSERT(0 && "unrecognized type"); - break; - }; - - /* Unrecognized attribute */ - if(rv == CKR_ATTRIBUTE_TYPE_INVALID) - return CK_FALSE; - - /* Value is longer than this one */ - if(rv == CKR_BUFFER_TOO_SMALL) - return CK_FALSE; - - /* All other errors */ - if(rv != CKR_OK) - return CK_FALSE; - - return (match->ulValueLen == attr.ulValueLen && - memcmp(match->pValue, attr.pValue, attr.ulValueLen) == 0); -} - -CK_BBOOL -ckcapi_object_data_match(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR matches, - CK_ULONG count) -{ - CK_ULONG i; - - for(i = 0; i < count; ++i) - { - if(!ckcapi_object_data_match_attr(objdata, &matches[i])) - return CK_FALSE; - } - - return CK_TRUE; -} - -CK_RV -ckcapi_object_data_get_attrs(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attrs, - CK_ULONG count) -{ - CK_ULONG i; - CK_RV rv, ret = CKR_OK; - - ASSERT(objdata); - ASSERT(!count || attrs); - - for(i = 0; i < count; ++i) - { - /* Get the data type of the attribute */ - switch(attribute_data_type(attrs[i].type)) - { - case DATA_BOOL: - rv = (objdata->data_funcs->get_bool)(objdata, &attrs[i]); - break; - case DATA_ULONG: - rv = (objdata->data_funcs->get_ulong)(objdata, &attrs[i]); - break; - case DATA_BYTES: - rv = (objdata->data_funcs->get_bytes)(objdata, &attrs[i]); - break; - case DATA_UNKNOWN: - rv = CKR_ATTRIBUTE_TYPE_INVALID; - break; - default: - ASSERT(0 && "unrecognized type"); - break; - }; - - /* Not an error if they were just requesting the size */ - if(rv != CKR_OK) - { - if(rv == CKR_BUFFER_TOO_SMALL) - { - if(!attrs[i].pValue) - rv = CKR_OK; - } - - /* Attribute is sensitive */ - else if(rv == CKR_ATTRIBUTE_SENSITIVE) - { - attrs[i].ulValueLen = (CK_ULONG)-1; - } - - /* Attribute doesn't exist */ - else if(rv == CKR_ATTRIBUTE_TYPE_INVALID) - { - DBG(("O%d: attribute not found: 0x%08x", objdata->object, attrs[i].type)); - attrs[i].ulValueLen = (CK_ULONG)-1; - } - - /* A fatal error? */ - else - { - ret = rv; - break; - } - - /* Transfer any non-fatal errors outward */ - if(rv != CKR_OK && ret == CKR_OK) - ret = rv; - } - } - - return ret; -} diff --git a/ckcapi-object.h b/ckcapi-object.h deleted file mode 100644 index 2b07c30..0000000 --- a/ckcapi-object.h +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (C) 2007 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef CKCAPI_OBJECT_H -#define CKCAPI_OBJECT_H - -#include "ckcapi.h" - -/* Debug print something about an object */ -#define DBGO(obj, msg) \ - ckcapi_debug("O%d: %s", (obj) ? (obj)->id : 0, (msg)) - -/* A function to load data for an object */ -typedef CK_RV (*CkCapiLoadData)(CkCapiSession* sess, struct _CkCapiObject* obj, - CkCapiObjectData** objdata); - -/* Produce a hash code for an object */ -typedef CK_RV (*CkCapiHashObject)(struct _CkCapiObject* obj); - -/* Produce a hash code for an object */ -typedef CK_RV (*CkCapiEqualObject)(struct _CkCapiObject* one, struct _CkCapiObject* two); - -/* A function to free some data */ -typedef void (*CkCapiRelease)(void* data); - -/* Object functions */ -typedef struct _CkCapiObjectVtable -{ - CkCapiLoadData load_data; - CkCapiHashObject hash_object; - CkCapiEqualObject equal_object; - CkCapiRelease release; -} -CkCapiObjectVtable; - -/* Represents a object we've seen */ -struct _CkCapiObject -{ - CK_OBJECT_HANDLE id; - CK_SLOT_ID slot; - CK_SESSION_HANDLE session; - const CkCapiObjectVtable* obj_funcs; -}; - -/* A function to get an attribute from ObjectData */ -typedef CK_RV (*CkCapiGetAttribute)(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr); - -/* Object data functions */ -typedef struct _CkCapiObjectDataVtable -{ - CkCapiGetAttribute get_bool; - CkCapiGetAttribute get_ulong; - CkCapiGetAttribute get_bytes; - CkCapiRelease release; -} -CkCapiObjectDataVtable; - -/* - * Base class for object data. Different types of - * objects extend this with more detailed data - */ -struct _CkCapiObjectData -{ - CK_OBJECT_HANDLE object; - const CkCapiObjectDataVtable* data_funcs; -}; - -/* Match object data against all the given match attributes */ -CK_BBOOL ckcapi_object_data_match (CkCapiObjectData* objdata, - CK_ATTRIBUTE_PTR matches, CK_ULONG count); - -/* Match a single attribute against object data */ -CK_BBOOL ckcapi_object_data_match_attr (CkCapiObjectData* objdata, - CK_ATTRIBUTE_PTR match); - -/* Get a bunch of attributes from object data */ -CK_RV ckcapi_object_data_get_attrs (CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attrs, - CK_ULONG count); - -/* Debug print something about an object data */ -#define DBGOD(objdata, msg) \ - ckcapi_debug("O%d: %s", (objdata) ? (objdata)->obj : 0, (msg)) - -#endif /* CKCAPI_OBJECT_H */ diff --git a/ckcapi-rsa.c b/ckcapi-rsa.c deleted file mode 100644 index 032bf82..0000000 --- a/ckcapi-rsa.c +++ /dev/null @@ -1,416 +0,0 @@ -/* - * Copyright (C) 2008 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "ckcapi.h" - -#include "ckcapi-der.h" -#include "ckcapi-key.h" -#include "ckcapi-object.h" - -/* - * Portions derived from NSS source files: - * lib/ckfw/capi/crsa.c - */ - -/* ***** BEGIN LICENSE BLOCK ***** - * Version: MPL 1.1/GPL 2.0/LGPL 2.1 - * - * The contents of this file are subject to the Mozilla Public License Version - * 1.1 (the "License"); you may not use this file except in compliance with - * the License. You may obtain a copy of the License at - * http://www.mozilla.org/MPL/ - * - * Software distributed under the License is distributed on an "AS IS" basis, - * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License - * for the specific language governing rights and limitations under the - * License. - * - * The Original Code is the Netscape security libraries. - * - * The Initial Developer of the Original Code is - * Red Hat, Inc. - * Portions created by the Initial Developer are Copyright (C) 2005 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Bob Relyea (rrelyea@redhat.com) - * - * Alternatively, the contents of this file may be used under the terms of - * either the GNU General Public License Version 2 or later (the "GPL"), or - * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), - * in which case the provisions of the GPL or the LGPL are applicable instead - * of those above. If you wish to allow use of your version of this file only - * under the terms of either the GPL or the LGPL, and not to allow others to - * use your version of this file under the terms of the MPL, indicate your - * decision by deleting the provisions above and replace them with the notice - * and other provisions required by the GPL or the LGPL. If you do not delete - * the provisions above, a recipient may use your version of this file under - * the terms of any one of the MPL, the GPL or the LGPL. - * - * ***** END LICENSE BLOCK ***** */ - -#define SSL3_SHAMD5_HASH_SIZE 36 /* LEN_MD5 (16) + LEN_SHA1 (20) */ - -/* - * PKCS #11 sign for RSA expects to take a fully DER-encoded hash value, - * which includes the hash OID. CAPI expects to take a Hash Context. While - * CAPI does have the capability of setting a raw hash value, it does not - * have the ability to sign an arbitrary value. This function tries to - * reduce the passed in data into something that CAPI could actually sign. - */ -static CK_BYTE_PTR -parse_rsa_pkcs_der_hash(CK_BYTE_PTR input, CK_ULONG n_input, - ALG_ID* algorithm, CK_ULONG_PTR n_hash) -{ - BYTE* algid; - BYTE* oid; - BYTE* hash_data; - BYTE* oid_str; - DWORD n_oid; - DWORD n_algid; - - /* - * there are 2 types of hashes NSS typically tries to sign, regular - * RSA signature format (with encoded DER_OIDS), and SSL3 Signed hashes. - * CAPI knows not to add any oids to SSL3_Signed hashes, so if we have any - * random hash that is exactly the same size as an SSL3 hash, then we can - * just pass the data through. CAPI has know way of knowing if the value - * is really a combined hash or some other arbitrary data, so it's safe to - * handle this case first. - */ - if(SSL3_SHAMD5_HASH_SIZE == n_input) - { - *n_hash = n_input; - *algorithm = CALG_SSL3_SHAMD5; - return input; - } - - /* make sure we have a sequence tag */ - if((CKCAPI_DER_SEQUENCE | CKCAPI_DER_CONSTRUCTED) != *input) - return NULL; - - /* - * parse the input block to get 1) the hash oid, and 2) the raw hash value. - * unfortunatly CAPI doesn't have a builtin function to do this work, so - * we go ahead and do it by hand here. - * - * format is: - * SEQUENCE { - * SECQUENCE { // algid - * OID {} // oid - * ANY {} // optional params - * } - * OCTECT {} // hash - */ - - /* unwrap */ - algid = ckcapi_der_unwrap(input, n_input, &n_algid, NULL); - if(!algid) - return NULL; - - /* make sure there is not extra data at the end */ - if(algid + n_algid != input + n_input) - return NULL; - - /* wasn't an algid */ - if((CKCAPI_DER_SEQUENCE | CKCAPI_DER_CONSTRUCTED) != *algid) - return NULL; - - oid = ckcapi_der_unwrap(algid, n_algid, &n_oid, &hash_data); - if(!oid || !hash_data) - return NULL; - - if(algorithm) - { - /* - * get the real oid as a string. Again, Microsoft does not - * export anything that does this for us - */ - oid_str = ckcapi_der_read_oid(oid, n_oid); - if(!oid_str) - return NULL; - - /* look up the hash alg from the oid (fortunately CAPI does to this) */ - *algorithm = CertOIDToAlgId(oid_str); - free(oid_str); - } - - /* wasn't a hash? */ - if(CKCAPI_DER_OCTET_STRING != *hash_data) - return NULL; - - /* get the real raw hash */ - return ckcapi_der_unwrap(hash_data, n_algid - (hash_data - algid), - n_hash, NULL); -} - -CK_RV -ckcapi_rsa_pkcs_sign_init(CkCapiObjectData *keydata, void** operation) -{ - CRYPT_KEY_PROV_INFO* prov_info; - - ASSERT(keydata); - ASSERT(operation); - ASSERT(!*operation); - - prov_info = ckcapi_key_object_data_get_prov_info(keydata); - if(prov_info->dwProvType != PROV_RSA_FULL) - return CKR_KEY_TYPE_INCONSISTENT; - - *operation = keydata; - return CKR_OK; -} - -CK_RV -ckcapi_rsa_pkcs_sign_perform (CK_BYTE_PTR data, CK_ULONG n_data, - CK_BYTE_PTR signature, CK_ULONG_PTR n_signature, - void** operation) -{ - CRYPT_KEY_PROV_INFO* prov_info; - CkCapiObjectData* keydata; - ALG_ID algorithm; - BYTE* hash_data; - DWORD n_hash_data; - BOOL capifail; - DWORD len, check; - DWORD bits; - CK_RV ret; - - HCRYPTPROV prov = 0; - HCRYPTHASH hash = 0; - - - ASSERT(operation); - ASSERT(*operation); - - if(!data || !n_data) - return CKR_ARGUMENTS_BAD; - - keydata = (CkCapiObjectData*)*operation; - - prov_info = ckcapi_key_object_data_get_prov_info(keydata); - ASSERT(prov_info); - - /* Calculate the number of bits */ - bits = ckcapi_key_object_data_get_bits (keydata); - if(!bits) - return CKR_GENERAL_ERROR; - - /* Want to know the length */ - if(!signature) - { - *n_signature = bits / 8; - return CKR_OK; - } - - /* TODO: Support arbitrary input on Vista */ - - /* - * PKCS #11 sign for RSA expects to take a fully DER-encoded hash value, - * which includes the hash OID. CAPI expects to take a Hash Context. While - * CAPI does have the capability of setting a raw hash value, it does not - * have the ability to sign an arbitrary value. This function tries to - * reduce the passed in data into something that CAPI could actually sign. - */ - hash_data = parse_rsa_pkcs_der_hash(data, n_data, &algorithm, &n_hash_data); - if(!hash_data) - return CKR_DATA_INVALID; - - capifail = TRUE; - if(CryptAcquireContextW(&prov, prov_info->pwszContainerName, prov_info->pwszProvName, - prov_info->dwProvType, 0)) - { - if(CryptCreateHash(prov, algorithm, 0, 0, &hash)) - { - /* make sure the hash lens match before we set it */ - len = sizeof(DWORD); - if(CryptGetHashParam(hash, HP_HASHSIZE, (BYTE*)&check, &len, 0)) - { - if(check != n_hash_data) - { - capifail = FALSE; - ret = CKR_DATA_INVALID; - } - - /* - * we have an explicit hash, set it, note that the length is - * implicit by the hashAlg used in create - */ - if(CryptSetHashParam(hash, HP_HASHVAL, hash_data, 0)) - { - /* OK, we have the data in a hash structure, sign it! */ - if(CryptSignHash(hash, prov_info->dwKeySpec, - NULL, 0, signature, n_signature)) - { - /* - * OK, Microsoft likes to do things completely - * differently than anyone else. We need to reverse - * the data we recieved here - */ - if(signature) - ckcapi_reverse_memory(signature, *n_signature); - - capifail = FALSE; - ret = CKR_OK; - } - } - } - } - } - - if(capifail) - ret = ckcapi_winerr_to_ckr(GetLastError()); - - if(hash) - CryptDestroyHash(hash); - if(prov) - CryptReleaseContext(prov, 0); - - return ret; -} - -void -ckcapi_rsa_pkcs_sign_cleanup (void* operation) -{ - /* Nothing to do */ -} - - -CK_RV -ckcapi_rsa_pkcs_decrypt_init(CkCapiObjectData* keydata, void** operation) -{ - CRYPT_KEY_PROV_INFO* prov_info; - - ASSERT(keydata); - ASSERT(operation); - ASSERT(!*operation); - - prov_info = ckcapi_key_object_data_get_prov_info(keydata); - if(prov_info->dwProvType != PROV_RSA_FULL) - return CKR_KEY_TYPE_INCONSISTENT; - - *operation = keydata; - return CKR_OK; -} - -CK_RV -ckcapi_rsa_pkcs_decrypt_perform(CK_BYTE_PTR encdata, CK_ULONG n_encdata, - CK_BYTE_PTR result, CK_ULONG_PTR n_result, - void** operation) -{ - CRYPT_KEY_PROV_INFO* prov_info; - CkCapiObjectData* keydata; - BOOL capifail; - DWORD bits, error; - CK_RV ret; - - HCRYPTPROV prov = 0; - HCRYPTKEY key = 0; - void* buffer = NULL; - - ASSERT(operation); - ASSERT(*operation); - ASSERT(encdata); - ASSERT(n_encdata); - - keydata = (CkCapiObjectData*)*operation; - - prov_info = ckcapi_key_object_data_get_prov_info(keydata); - ASSERT(prov_info); - - /* Calculate the number of bits */ - bits = ckcapi_key_object_data_get_bits (keydata); - if(!bits) - return CKR_GENERAL_ERROR; - - /* Want to know the length */ - if(!result) - { - *n_result = bits / 8; - return CKR_OK; - } - - /* - * Copy the input, since CAPI operates in place, and - * we must also reverse it properly. - */ - buffer = malloc(n_encdata); - if(!buffer) - return CKR_HOST_MEMORY; - - memcpy(buffer, encdata, n_encdata); - ckcapi_reverse_memory(buffer, n_encdata); - - capifail = TRUE; - if(CryptAcquireContextW(&prov, prov_info->pwszContainerName, prov_info->pwszProvName, - prov_info->dwProvType, 0)) - { - if(CryptGetUserKey(prov, prov_info->dwKeySpec, &key)) - { - *n_result = n_encdata; - if(CryptDecrypt(key, 0, TRUE, 0, buffer, n_result)) - { - capifail = FALSE; - ret = CKR_OK; - } - } - } - - if(capifail) - { - error = GetLastError(); - switch(error) - { - case NTE_BAD_DATA: - ret = CKR_ENCRYPTED_DATA_INVALID; - default: - ret = ckcapi_winerr_to_ckr(error); - }; - } - - /* Copy the memory out to the result buffer */ - if(ret == CKR_OK) - ret = ckcapi_return_data_raw(result, n_result, buffer, *n_result); - - if(key) - CryptDestroyKey(key); - if(prov) - CryptReleaseContext(prov, 0); - if(buffer) - free(buffer); - - return ret; -} - -void -ckcapi_rsa_pkcs_decrypt_cleanup(void* operation) -{ - /* Nothing to do */ -} - -void -ckcapi_rsa_pkcs_get_info (CK_MECHANISM_TYPE mech, CK_MECHANISM_INFO_PTR info) -{ - ASSERT(mech == CKM_RSA_PKCS); - ASSERT(info != NULL); - - info->ulMinKeySize = 384; - info->ulMaxKeySize = 16384; - info->flags = 0; -} diff --git a/ckcapi-rsa.h b/ckcapi-rsa.h deleted file mode 100644 index d410cc1..0000000 --- a/ckcapi-rsa.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2008 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef CKCAPI_RSA_H -#define CKCAPI_RSA_H - -#include "ckcapi.h" - -CK_RV ckcapi_rsa_pkcs_sign_init (CkCapiObjectData* keydata, void** operation); - -CK_RV ckcapi_rsa_pkcs_sign_perform (CK_BYTE_PTR data, CK_ULONG data_len, - CK_BYTE_PTR signature, CK_ULONG_PTR signature_len, - void** operation); - -void ckcapi_rsa_pkcs_sign_cleanup (void* operation); - -CK_RV ckcapi_rsa_pkcs_decrypt_init (CkCapiObjectData* keydata, void** operation); - -CK_RV ckcapi_rsa_pkcs_decrypt_perform (CK_BYTE_PTR encdata, CK_ULONG n_encdata, - CK_BYTE_PTR result, CK_ULONG_PTR n_result, - void** operation); - -void ckcapi_rsa_pkcs_decrypt_cleanup (void* operation); - -void ckcapi_rsa_pkcs_get_info (CK_MECHANISM_TYPE mech, - CK_MECHANISM_INFO_PTR info); - -#endif /* CKCAPI_RSA_H */ diff --git a/ckcapi-session.c b/ckcapi-session.c deleted file mode 100644 index d1d345c..0000000 --- a/ckcapi-session.c +++ /dev/null @@ -1,982 +0,0 @@ -/* - * Copyright (C) 2007 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include - -#include "ckcapi.h" -#include "ckcapi-builtin.h" -#include "ckcapi-cert.h" -#include "ckcapi-key.h" -#include "ckcapi-object.h" -#include "ckcapi-rsa.h" -#include "ckcapi-session.h" -#include "ckcapi-token.h" -#include "ckcapi-trust.h" - -/* For operation_type in CkCapiSession */ -enum -{ - OPERATION_NONE, - OPERATION_FIND, - OPERATION_SIGN, - OPERATION_DECRYPT -}; - -static CkCapiArray* all_sessions = NULL; - -static void -object_data_release(CkCapiObjectData* objdata) -{ - ASSERT(objdata->data_funcs); - ASSERT(objdata->data_funcs->release); - (objdata->data_funcs->release)(objdata); -} - -CK_RV -ckcapi_session_create(CK_SLOT_ID slot, CkCapiSession** ret) -{ - CkCapiSession* sess; - const char *store; - DWORD err; - - sess = calloc(1, sizeof(CkCapiSession)); - if(!sess) - return CKR_HOST_MEMORY; - - sess->object_data = ckcapi_hash_new(NULL, NULL); - if(!sess->object_data) { - free(sess); - return CKR_HOST_MEMORY; - } - - sess->mutex = CreateMutex(NULL, FALSE, NULL); - if(!sess->mutex) { - ckcapi_hash_free(sess->object_data, NULL); - free(sess); - return CKR_HOST_MEMORY; - } - - store = ckcapi_token_get_store_name(slot); - if(store) - { - sess->store = CertOpenSystemStore((HCRYPTPROV)NULL, store); - if(sess->store == NULL) - { - err = GetLastError(); - - /* Store not found, we don't care */ - if(err != ERROR_FILE_NOT_FOUND) - { - ckcapi_hash_free(sess->object_data, NULL); - CloseHandle(sess->mutex); - free(sess); - return ckcapi_winerr_to_ckr(err); - } - } - } - - sess->slot = slot; - - DBGS(sess, "created"); - - *ret = sess; - return CKR_OK; -} - -CK_RV -ckcapi_session_register(CkCapiSession* sess) -{ - CkCapiSession* blank = NULL; - CK_SESSION_HANDLE id = 0; - CK_RV ret = CKR_OK; - size_t i; - - ASSERT(sess); - ASSERT(sess->id == 0 && sess->refs == 0); - - DBGS(sess, "registering new session"); - - ckcapi_lock_global(); - - /* Find a nice session identifier */ - while(id == 0) { - - /* Allocate sessions properly */ - if(!all_sessions) - { - all_sessions = ckcapi_array_new(0, 1, sizeof(CkCapiSession*)); - if(!all_sessions) - { - ret = CKR_HOST_MEMORY; - break; - } - - /* A blank entry for '0' */ - ckcapi_array_append(all_sessions, blank); - - DBG(("allocated new session list")); - } - - /* - * PKCS#11 GRAY AREA: We're assuming we can reuse session - * handles. PKCS#11 spec says they're like file handles, - * and file handles get reused :) - */ - - /* Note we never put anything in array position '0' */ - for(i = 1; i < all_sessions->len; ++i) - { - /* Any empty position will do */ - if(!ckcapi_array_index(all_sessions, CkCapiSession*, i)) - { - id = i; - break; - } - } - - /* Couldn't find a handle, append a handle */ - if(id == 0) - { - id = all_sessions->len; - ckcapi_array_append(all_sessions, blank); - } - } - - if(ret == CKR_OK) - { - ASSERT(id > 0 && id < all_sessions->len); - ASSERT(!ckcapi_array_index(all_sessions, CkCapiSession*, id)); - - - /* And assign it to the session handle */ - ckcapi_array_index(all_sessions, CkCapiSession*, i) = sess; - sess->id = id; - - /* The session list reference */ - ASSERT(sess->refs == 0); - sess->refs++; - - DBGS(sess, "registered sesson id"); - } - - ckcapi_unlock_global(); - - return ret; -} - -void -ckcapi_session_destroy(CkCapiSession* sess) -{ - ASSERT(sess); - ASSERT(sess->refs == 0); - - /* Ask any pending operations to cleanup */ - if(sess->operation_type) - { - ASSERT(sess->operation_cancel); - (sess->operation_cancel)(sess); - } - - ASSERT(sess->operation_type == 0); - ASSERT(sess->operation_data == NULL); - ASSERT(sess->operation_cancel == NULL); - - if(sess->store) - CertCloseStore(sess->store, 0); - - /* Make all the object adat go away */ - ASSERT(sess->object_data != NULL); - ckcapi_hash_free(sess->object_data, object_data_release); - - /* And make the mutex go away */ - ASSERT(sess->mutex != NULL); - CloseHandle(sess->mutex); - - DBGS(sess, "destroyed"); - free(sess); -} - -void -ckcapi_session_get_info(CkCapiSession* sess, CK_SESSION_INFO_PTR info) -{ - ASSERT(sess); - ASSERT(info); - - info->slotID = sess->slot; - info->flags = CKF_SERIAL_SESSION; - if(sess->read_write) - info->flags |= CKF_RW_SESSION; - - if(ckcapi_token_is_logged_in(sess->slot)) - info->state = sess->read_write ? CKS_RW_USER_FUNCTIONS : CKS_RO_USER_FUNCTIONS; - else - info->state = sess->read_write ? CKS_RW_PUBLIC_SESSION : CKS_RO_PUBLIC_SESSION; - - /* TODO: We could implement some use of GetLastError() here */ - info->ulDeviceError = 0; -} - -static CK_RV -lock_ref_internal(CkCapiArray* sessions, CK_SESSION_HANDLE id, - BOOL remove, BOOL writable, CkCapiSession** sess_ret) -{ - CkCapiSession *sess; - DWORD r; - - ASSERT(sessions); - ASSERT(sess_ret); - - if(id >= sessions->len) - { - DBG(("invalid session id: %d", id)); - return CKR_SESSION_HANDLE_INVALID; - } - - /* A seemingly valid id */ - ASSERT(sessions); - sess = ckcapi_array_index(sessions, CkCapiSession*, id); - - if(!sess) - { - DBG(("session does not exist: %d", id)); - return CKR_SESSION_HANDLE_INVALID; - } - - /* Make sure it's the right kind of session */ - if(writable && !sess->read_write) - return CKR_SESSION_READ_ONLY; - - ASSERT(sess->id == id); - - /* Closing takes precedence over active operations */ - if(!remove) - { - /* - * An initial check is done to make sure this session is not active. - * This is done outside of the lock. The real check is done later - * inside a lock. This is so we can return quickly without blocking - * in most cases. - */ - - if(sess->in_call) - { - DBGS(sess, ("an operation is already active in this session")); - return CKR_OPERATION_ACTIVE; - } - } - - /* Lock the CallCkCapiSession */ - r = WaitForSingleObject(sess->mutex, INFINITE); - ASSERT(r == WAIT_OBJECT_0); - - /* Do the real check */ - if(!remove && sess->in_call) - { - ReleaseMutex(sess->mutex); - DBGS(sess, ("an operation is already active in this session")); - return CKR_OPERATION_ACTIVE; - } - - /* Make sure it doesn't go away */ - ASSERT(sess->refs > 0); - sess->refs++; - - DBGS(sess, "found and locked session"); - - /* And remove it if necessary */ - if(remove) - { - ckcapi_array_index(sessions, CkCapiSession*, id) = NULL; - - /* The session list reference */ - sess->refs--; - ASSERT(sess->refs > 0); - - DBGS(sess, "removed session from list"); - } - else - { - ASSERT(!sess->in_call); - sess->in_call = 1; - } - - *sess_ret = sess; - return CKR_OK; -} - -CK_RV -ckcapi_session_get_lock_ref(CK_ULONG id, BOOL writable, CkCapiSession **sess) -{ - /* This must be called without any locks held */ - - CK_RV ret = CKR_OK; - - ASSERT(sess); - - if(id <= 0) - { - DBG(("invalid session id passed: %d", id)); - return CKR_ARGUMENTS_BAD; - } - - ckcapi_lock_global(); - - ret = lock_ref_internal (all_sessions, id, FALSE, writable, sess); - - ckcapi_unlock_global(); - - return ret; -} - -CK_RV -ckcapi_session_remove_lock_ref(CK_ULONG id, CkCapiSession **sess) -{ - /* This must be called without any locks held */ - - CK_RV ret = CKR_OK; - - ASSERT(sess); - - if(id <= 0) - { - DBG(("invalid session id passed: %d", id)); - return CKR_ARGUMENTS_BAD; - } - - ckcapi_lock_global(); - - ret = lock_ref_internal (all_sessions, id, TRUE, FALSE, sess); - - ckcapi_unlock_global(); - - return ret; -} - -void -ckcapi_session_unref_unlock(CkCapiSession* sess) -{ - /* The CallCkCapiSession must be locked at this point */ - - int refs; - BOOL r; - - ASSERT(sess); - - ASSERT(sess->refs > 0); - sess->refs--; - refs = sess->refs; - - sess->in_call = 0; - - DBGS(sess, "unlocked session"); - - r = ReleaseMutex(sess->mutex); - ASSERT(r == TRUE); - - /* - * At this point if no references are held, then we can safely - * delete. No other thread should be involved. - */ - - if(refs == 0) - ckcapi_session_destroy(sess); -} - -CK_RV -ckcapi_session_close_all(CK_SLOT_ID slot) -{ - /* This must be called without any locks held */ - - CkCapiArray* sessions; - CkCapiSession *sess; - size_t i; - CK_RV ret = CKR_OK; - - /* - * PKCS#11 GRAY AREA: What happens when this gets called - * concurrently? We don't return an error on the second call, - * because by the time it returns, all sessions should be closed. - */ - - DBG(("closing all sessions for: %d", slot)); - - if(!all_sessions) - return CKR_OK; - - ckcapi_lock_global(); - - sessions = ckcapi_array_sized_new(0, 1, sizeof(CkCapiSession*), - all_sessions->len); - if(!sessions) - ret = CKR_HOST_MEMORY; - - /* Steal all the session data */ - if(ret == CKR_OK) - { - for(i = 0; i < all_sessions->len; ++i) - { - sess = ckcapi_array_index(all_sessions, CkCapiSession*, i); - if(sess && (slot == ((CK_SLOT_ID)-1) || sess->slot == slot)) - { - /* Steal this session */ - ckcapi_array_index(all_sessions, CkCapiSession*, i) = NULL; - } - else - { - /* Not a session we're interested in */ - sess = NULL; - } - - /* Both null and normal sessions are set to preserve indexes */ - ckcapi_array_append(sessions, sess); - } - - ASSERT(sessions->len == all_sessions->len); - } - - ckcapi_unlock_global(); - - if(ret != CKR_OK) - return ret; - - /* Close each session in turn */ - for(i = 0; i < sessions->len; ++i) - { - if(!ckcapi_array_index(sessions, CkCapiSession*, i)) - continue; - - /* We need any calls in other threads to finish, so wait here */ - if(lock_ref_internal(sessions, i, TRUE, FALSE, &sess) == CKR_OK) - ckcapi_session_unref_unlock(sess); - } - - /* We stole the memory above, free it now */ - ckcapi_array_free(sessions, 1); - return CKR_OK; -} - -void -ckcapi_session_cleanup_all() -{ - ckcapi_session_close_all((CK_SLOT_ID)-1); - - ckcapi_lock_global(); - - ckcapi_array_free(all_sessions, 1); - all_sessions = NULL; - - ckcapi_unlock_global(); -} - -/* ---------------------------------------------------------------------------- - * OBJECT DATA - */ - -CK_RV -ckcapi_session_get_object_data(CkCapiSession* sess, CkCapiObject* obj, - CkCapiObjectData** objdata) -{ - CK_OBJECT_HANDLE id; - CkCapiObjectData* newdata; - CK_RV ret; - - ASSERT(sess); - ASSERT(sess->object_data); - ASSERT(obj); - ASSERT(obj->obj_funcs); - ASSERT(obj->obj_funcs->load_data); - ASSERT(objdata); - - id = obj->id; - - *objdata = ckcapi_hash_get(sess->object_data, ckcapi_hash_key(id)); - if(*objdata) - return CKR_OK; - - ret = (obj->obj_funcs->load_data)(sess, obj, &newdata); - if(ret != CKR_OK) - return ret; - - newdata->object = id; - ASSERT(newdata->data_funcs); - - if(!ckcapi_hash_set(sess->object_data, ckcapi_hash_key(id), newdata)) { - object_data_release(newdata); - return CKR_HOST_MEMORY; - } - - *objdata = newdata; - return CKR_OK; -} - -void -ckcapi_session_clear_object_data(CkCapiSession* sess, CkCapiObject* obj) -{ - CkCapiObjectData* objdata; - - ASSERT(sess); - ASSERT(sess->object_data); - ASSERT(obj); - - objdata = (CkCapiObjectData*)ckcapi_hash_rem(sess->object_data, ckcapi_hash_key(obj->id)); - if(objdata) - object_data_release(objdata); -} - -void -ckcapi_session_enum_object_data(CkCapiSession* sess, - CkCapiEnumObjectData enum_func, void* arg) -{ - CK_OBJECT_HANDLE i, max; - CkCapiObject* obj; - CkCapiObjectData* objdata; - - ASSERT(sess); - ASSERT(sess->object_data); - ASSERT(enum_func); - - max = ckcapi_token_get_max_handle(); - for(i = 0; i < max; ++i) - { - objdata = (CkCapiObjectData*)ckcapi_hash_get(sess->object_data, ckcapi_hash_key(i)); - if(!objdata) - continue; - - obj = ckcapi_token_lookup_object(sess->slot, i); - if(!obj) - continue; - - (enum_func)(sess, obj, objdata, arg); - } -} - -CK_RV -ckcapi_session_get_object_data_for(CkCapiSession* sess, CK_OBJECT_HANDLE hand, - CkCapiObjectData** objdata) -{ - CkCapiObject* obj; - - obj = ckcapi_token_lookup_object(sess->slot, hand); - if(!obj) - return CKR_OBJECT_HANDLE_INVALID; - - return ckcapi_session_get_object_data(sess, obj, objdata); -} - -void -ckcapi_session_take_object_data(CkCapiSession* sess, CkCapiObject* obj, - CkCapiObjectData* objdata) -{ - CkCapiObjectData* prev; - - ASSERT(obj); - ASSERT(sess); - ASSERT(sess->object_data); - - ASSERT(objdata); - objdata->object = obj->id; - - prev = ckcapi_hash_rem(sess->object_data, ckcapi_hash_key(obj->id)); - if(prev) - object_data_release(prev); - - if(!ckcapi_hash_set(sess->object_data, ckcapi_hash_key(obj->id), objdata)) - object_data_release(objdata); -} - - -/* ---------------------------------------------------------------------------- - * FIND OPERATION - */ - -static BOOL -get_ulong_attribute(CK_ATTRIBUTE_TYPE type, CK_ATTRIBUTE_PTR templ, - CK_ULONG count, CK_ULONG* val) -{ - CK_ULONG i; - - ASSERT(val); - ASSERT(!count || templ); - - for(i = 0; i < count; ++i) - { - if(templ[i].type == type) - { - *val = *((CK_ULONG*)templ[i].pValue); - return TRUE; - } - } - - return FALSE; -} - -static CK_RV -gather_objects(CkCapiSession* sess, CK_ATTRIBUTE_PTR match, - CK_ULONG count, CkCapiArray* arr) -{ - CK_OBJECT_CLASS ocls = CKO_ANY; - CK_RV ret = CKR_OK; - - get_ulong_attribute(CKA_CLASS, match, count, &ocls); - - /* Search for builtins */ - ret = ckcapi_builtin_find(sess, ocls, match, count, arr); - if(ret != CKR_OK) - return ret; - - /* - * Search through certificates. - * - * We always do this search first. In Windows a lots hangs off - * the certificates. For example private keys are not contained - * in the same stores that certificates are in. There are a different - * set of key containers many of which can be used together - * with a certificate stored in any store. - * - * The trust objects we expose also depend on the certificates - * loaded. - */ - ret = ckcapi_cert_find(sess, ocls, match, count, arr); - if(ret != CKR_OK) - return ret; - - /* Search through trust objects */ - ret = ckcapi_trust_find(sess, ocls, match, count, arr); - if(ret != CKR_OK) - return ret; - - /* Search through key objects */ - ret = ckcapi_key_find(sess, ocls, match, count, arr); - if(ret != CKR_OK) - return ret; - - return ret; -} - -void -cleanup_find_operation(CkCapiSession* sess) -{ - ASSERT(sess->operation_type == OPERATION_FIND); - if(sess->operation_data) - ckcapi_array_free((CkCapiArray*)sess->operation_data, TRUE); - sess->operation_type = OPERATION_NONE; - sess->operation_data = NULL; - sess->operation_cancel = NULL; -} - -void -purge_duplicate_objects(CkCapiArray* arr) -{ - CkCapiHash* checks; - CK_OBJECT_HANDLE v; - size_t i; - - checks = ckcapi_hash_new(NULL, NULL); - if(!checks) - return; - - for(i = 0; i < arr->len; ) - { - v = ckcapi_array_index(arr, CK_OBJECT_HANDLE, i); - if(ckcapi_hash_get(checks, ckcapi_hash_key(v))) - { - ckcapi_array_remove_index(arr, i); - /* Look at same i again */ - } - else - { - if(!ckcapi_hash_set(checks, ckcapi_hash_key(v), arr)) - break; - ++i; - } - } - - ckcapi_hash_free(checks, NULL); -} - -CK_RV -ckcapi_session_find_init(CkCapiSession* sess, CK_ATTRIBUTE_PTR match, - CK_ULONG count) -{ - CkCapiArray* arr; - CK_RV ret; - - ASSERT(sess); - ASSERT(!count || match); - - if(sess->operation_type != OPERATION_NONE) - return CKR_OPERATION_ACTIVE; - - arr = ckcapi_array_new(0, 1, sizeof(CK_OBJECT_HANDLE)); - if(!arr) - return CKR_HOST_MEMORY; - - ret = gather_objects(sess, match, count, arr); - if(ret != CKR_OK) - { - ckcapi_array_free(arr, TRUE); - return ret; - } - - /* Cleanup all duplicates in the array */ - purge_duplicate_objects(arr); - - sess->operation_type = OPERATION_FIND; - sess->operation_data = arr; - sess->operation_cancel = cleanup_find_operation; - - return CKR_OK; -} - -CK_RV -ckcapi_session_find(CkCapiSession* sess, CK_OBJECT_HANDLE_PTR objects, - CK_ULONG max_object_count, CK_ULONG_PTR object_count) -{ - CkCapiArray* arr; - size_t i; - - ASSERT(sess); - ASSERT(object_count); - ASSERT(!max_object_count || objects); - - if(sess->operation_type != OPERATION_FIND) - return CKR_OPERATION_NOT_INITIALIZED; - - if(!max_object_count) - { - *object_count = 0; - return CKR_OK; - } - - arr = (CkCapiArray*)sess->operation_data; - *object_count = (max_object_count > arr->len ? arr->len : max_object_count); - for(i = 0; i < *object_count; ++i) - objects[i] = ckcapi_array_index(arr, CK_OBJECT_HANDLE, i); - - ckcapi_array_remove_range(arr, 0, *object_count); - - return CKR_OK; -} - -CK_RV -ckcapi_session_find_final(CkCapiSession* sess) -{ - ASSERT(sess); - - if(sess->operation_type != OPERATION_FIND) - return CKR_OPERATION_NOT_INITIALIZED; - - cleanup_find_operation(sess); - return CKR_OK; -} - - -/* ---------------------------------------------------------------------------- - * CRYPTO OPERATIONS - */ - -typedef struct _CryptoContext -{ - CK_MECHANISM_TYPE mech_type; - CkCapiDestroyFunc mech_cleanup; - void* mech_data; -} -CryptoContext; - -void -cleanup_crypto_operation(CkCapiSession* sess) -{ - CryptoContext* ctx; - - if(sess->operation_data) - { - ctx = (CryptoContext*)sess->operation_data; - if(ctx->mech_cleanup) - (ctx->mech_cleanup)(ctx->mech_data); - free(ctx); - } - - sess->operation_type = OPERATION_NONE; - sess->operation_data = NULL; - sess->operation_cancel = NULL; -} - -CK_RV -ckcapi_session_sign_init(CkCapiSession* sess, CK_MECHANISM_PTR mech, - CkCapiObjectData *objdata) -{ - CryptoContext* ctx; - CK_RV ret; - - ASSERT(sess); - ASSERT(mech); - ASSERT(objdata); - - if(sess->operation_type != OPERATION_NONE) - return CKR_OPERATION_ACTIVE; - - ctx = calloc(1, sizeof(CryptoContext)); - if(!ctx) - return CKR_HOST_MEMORY; - - ctx->mech_type = mech->mechanism; - - switch(mech->mechanism) - { - case CKM_RSA_PKCS: - ret = ckcapi_rsa_pkcs_sign_init(objdata, &ctx->mech_data); - ctx->mech_cleanup = ckcapi_rsa_pkcs_sign_cleanup; - break; - default: - ret = CKR_MECHANISM_INVALID; - break; - }; - - if(ret != CKR_OK) - { - free(ctx); - ASSERT(!sess->operation_data); - return ret; - } - - sess->operation_type = OPERATION_SIGN; - sess->operation_data = ctx; - sess->operation_cancel = cleanup_crypto_operation; - return CKR_OK; -} - -CK_RV -ckcapi_session_sign(CkCapiSession* sess, CK_BYTE_PTR data, CK_ULONG n_data, - CK_BYTE_PTR signature, CK_ULONG_PTR n_signature) -{ - CryptoContext *ctx; - BOOL incomplete; - CK_RV ret; - - ASSERT(sess); - ASSERT(data); - ASSERT(n_data); - - if(sess->operation_type != OPERATION_SIGN) - return CKR_OPERATION_NOT_INITIALIZED; - - ctx = (CryptoContext*)sess->operation_data; - switch(ctx->mech_type) - { - case CKM_RSA_PKCS: - ret = ckcapi_rsa_pkcs_sign_perform(data, n_data, signature, n_signature, - &ctx->mech_data); - break; - - default: - ASSERT(FALSE); - ret = CKR_GENERAL_ERROR; - break; - } - - /* Buffer calculation, we don't end operation */ - incomplete = (ret == CKR_BUFFER_TOO_SMALL || (ret == CKR_OK && !signature)); - - if(!incomplete) - cleanup_crypto_operation(sess); - - return ret; -} - -CK_RV -ckcapi_session_decrypt_init(CkCapiSession* sess, CK_MECHANISM_PTR mech, - CkCapiObjectData *objdata) -{ - CryptoContext* ctx; - CK_RV ret; - - ASSERT(sess); - ASSERT(mech); - ASSERT(objdata); - - if(sess->operation_type != OPERATION_NONE) - return CKR_OPERATION_ACTIVE; - - ctx = calloc(1, sizeof(CryptoContext)); - if(!ctx) - return CKR_HOST_MEMORY; - - ctx->mech_type = mech->mechanism; - - switch(mech->mechanism) - { - case CKM_RSA_PKCS: - ret = ckcapi_rsa_pkcs_decrypt_init(objdata, &ctx->mech_data); - ctx->mech_cleanup = ckcapi_rsa_pkcs_decrypt_cleanup; - break; - default: - ret = CKR_MECHANISM_INVALID; - break; - }; - - if(ret != CKR_OK) - { - free(ctx); - ASSERT(!sess->operation_data); - return ret; - } - - sess->operation_type = OPERATION_DECRYPT; - sess->operation_data = ctx; - sess->operation_cancel = cleanup_crypto_operation; - return CKR_OK; -} - -CK_RV -ckcapi_session_decrypt(CkCapiSession* sess, CK_BYTE_PTR encdata, CK_ULONG n_encdata, - CK_BYTE_PTR result, CK_ULONG_PTR n_result) -{ - CryptoContext *ctx; - BOOL incomplete; - CK_RV ret; - - ASSERT(sess); - ASSERT(encdata); - ASSERT(n_encdata); - - if(sess->operation_type != OPERATION_DECRYPT) - return CKR_OPERATION_NOT_INITIALIZED; - - ctx = (CryptoContext*)sess->operation_data; - switch(ctx->mech_type) - { - case CKM_RSA_PKCS: - ret = ckcapi_rsa_pkcs_decrypt_perform(encdata, n_encdata, result, n_result, - &ctx->mech_data); - break; - - default: - ASSERT(FALSE); - ret = CKR_GENERAL_ERROR; - break; - } - - /* Buffer calculation, we don't end operation */ - incomplete = (ret == CKR_BUFFER_TOO_SMALL || (ret == CKR_OK && !result)); - - if(!incomplete) - cleanup_crypto_operation(sess); - - return ret; -} diff --git a/ckcapi-session.h b/ckcapi-session.h deleted file mode 100644 index f486277..0000000 --- a/ckcapi-session.h +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2007 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef CKCAPI_SESSION_H -#define CKCAPI_SESSION_H - -#include "ckcapi.h" - -/* Callback to cleanup a current operation */ -typedef void (*CkCapiSessionCancel) (struct _CkCapiSession* sess); - -/* Represents an open session */ -typedef struct _CkCapiSession -{ - CK_SESSION_HANDLE id; /* Unique ID for this session */ - CK_SLOT_ID slot; - int in_call; /* Whether this session is use in PKCS#11 function */ - - HCERTSTORE store; /* Handle to an open certificate store */ - - BOOL read_write; /* A read-write session? */ - - int operation_type; /* Whether an operation is happening or not */ - void* operation_data; /* Data for this operation */ - CkCapiSessionCancel operation_cancel; /* Callback to cancel operation when necessary */ - - CkCapiHash* object_data; - - CK_NOTIFY notify_callback; /* Application specified callback */ - CK_VOID_PTR user_data; /* Argument for above */ - - int refs; /* Reference count */ - HANDLE mutex; /* Mutex for protecting this structure */ -} -CkCapiSession; - -/* Debug print something related to a session */ -#define DBGS(sess, msg) \ - ckcapi_debug("S%d: %s", (sess) ? (sess)->id : 0, (msg)) - -/* Create a session */ -CK_RV ckcapi_session_create (CK_SLOT_ID slot, CkCapiSession** ret); - -/* Destroy a session */ -void ckcapi_session_destroy (CkCapiSession* sess); - -/* Register a new session */ -CK_RV ckcapi_session_register (CkCapiSession* sess); - -/* Get information about a session */ -void ckcapi_session_get_info (CkCapiSession* sess, - CK_SESSION_INFO_PTR info); - -/* Get a session from a handle, and lock it */ -CK_RV ckcapi_session_get_lock_ref (CK_ULONG id, BOOL writable, - CkCapiSession **sess); - -/* Get a session from a handle, remove it from list, and lock it */ -CK_RV ckcapi_session_remove_lock_ref (CK_ULONG id, CkCapiSession **sess); - -/* Unlock and unreference a session */ -void ckcapi_session_unref_unlock (CkCapiSession* sess); - -/* Close all sessions on a certain slot/token */ -CK_RV ckcapi_session_close_all (CK_SLOT_ID slot); - - - -/* Start a find operation on a session */ -CK_RV ckcapi_session_find_init (CkCapiSession* sess, - CK_ATTRIBUTE_PTR templ, - CK_ULONG count); - -/* Return results from a find operation */ -CK_RV ckcapi_session_find (CkCapiSession* sess, - CK_OBJECT_HANDLE_PTR objects, - CK_ULONG max_object_count, - CK_ULONG_PTR object_count); - -/* End a find operation */ -CK_RV ckcapi_session_find_final (CkCapiSession* sess); - - -/* Start a sign operation on a session */ -CK_RV ckcapi_session_sign_init (CkCapiSession* sess, - CK_MECHANISM_PTR mech, - CkCapiObjectData *objdata); - -/* Perform sign operation */ -CK_RV ckcapi_session_sign (CkCapiSession* sess, - CK_BYTE_PTR data, CK_ULONG n_data, - CK_BYTE_PTR sig, CK_ULONG_PTR n_sig); - -/* Start a decrypt operation on a session */ -CK_RV ckcapi_session_decrypt_init (CkCapiSession* sess, - CK_MECHANISM_PTR mech, - CkCapiObjectData *objdata); - -/* Perform decrypt operation */ -CK_RV ckcapi_session_decrypt (CkCapiSession* sess, - CK_BYTE_PTR encdata, CK_ULONG n_encdata, - CK_BYTE_PTR result, CK_ULONG_PTR n_result); - -/* Get object data for an object */ -CK_RV ckcapi_session_get_object_data (CkCapiSession* sess, - CkCapiObject* obj, - CkCapiObjectData** objdata); - -/* Get object data for an object handle */ -CK_RV ckcapi_session_get_object_data_for (CkCapiSession* sess, - CK_OBJECT_HANDLE hand, - CkCapiObjectData** objdata); - -/* Set object data for an object */ -void ckcapi_session_take_object_data (CkCapiSession* sess, - CkCapiObject* obj, - CkCapiObjectData* objdata); - -/* Clear object data for an object */ -void ckcapi_session_clear_object_data (CkCapiSession* sess, - CkCapiObject* obj); - -/* Enumerate object data for all objects */ -typedef void (*CkCapiEnumObjectData) (CkCapiSession* sess, - CkCapiObject* obj, - CkCapiObjectData* data, - void* arg); -void ckcapi_session_enum_object_data (CkCapiSession* sess, - CkCapiEnumObjectData enum_func, - void* arg); - -void ckcapi_session_cleanup_all (void); - -#endif /* CKCAPI_SESSION_H */ diff --git a/ckcapi-token.c b/ckcapi-token.c deleted file mode 100644 index c9b6f34..0000000 --- a/ckcapi-token.c +++ /dev/null @@ -1,354 +0,0 @@ -/* - * Copyright (C) 2007 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "ckcapi.h" -#include "ckcapi-object.h" -#include "ckcapi-token.h" - -static CkCapiArray* object_array = NULL; -static CkCapiHash* object_hash = NULL; -static CkCapiArray* logged_in_slots = NULL; - -typedef struct _SlotInfo -{ - const char* capi_store; - const char* display_name; - CK_ULONG slot_flags; -} -SlotInfo; - -#define SLOT_OFFSET 0x00001000 - -static SlotInfo slot_info[] = { - { "My", "Personal Certificates", CKCAPI_SLOT_TRUSTED | CKCAPI_SLOT_CERTS }, - { "AddressBook", "Address Book Certificates", CKCAPI_SLOT_CERTS }, - { "CA", "Certificate Authorities", CKCAPI_SLOT_CA | CKCAPI_SLOT_CERTS }, - { "Root", "Root Authorities", CKCAPI_SLOT_TRUSTED | CKCAPI_SLOT_CA | CKCAPI_SLOT_CERTS }, - { "Trust", "Trust", CKCAPI_SLOT_CERTS }, - { "TrustedPeople", "Trusted People", CKCAPI_SLOT_TRUSTED | CKCAPI_SLOT_CERTS }, - { "AuthRoot", "Auth Root", CKCAPI_SLOT_CERTS }, - { NULL, "All User Keys", CKCAPI_SLOT_ANYKEY } -}; - -#define SLOT_TO_OFFSET(slot) \ - ((slot) & ~(SLOT_OFFSET)) - -#define OFFSET_TO_SLOT(offset) \ - ((offset) | SLOT_OFFSET) - -unsigned int -ckcapi_token_get_count(void) -{ - return sizeof(slot_info) / sizeof(slot_info[0]); -} - -CK_SLOT_ID -ckcapi_token_get_slot_id(unsigned int offset) -{ - ASSERT(offset < ckcapi_token_get_count()); - return OFFSET_TO_SLOT(offset); -} - -CK_BBOOL -ckcapi_token_is_valid(CK_SLOT_ID slot) -{ - unsigned int offset = SLOT_TO_OFFSET(slot); - return offset >= 0 && offset < ckcapi_token_get_count(); -} - -const char* -ckcapi_token_get_display_name(CK_SLOT_ID slot) -{ - unsigned int offset = SLOT_TO_OFFSET(slot); - ASSERT(ckcapi_token_is_valid(slot)); - ASSERT(slot_info[offset].display_name); - return slot_info[offset].display_name; -} - -const char* -ckcapi_token_get_store_name(CK_SLOT_ID slot) -{ - unsigned int offset = SLOT_TO_OFFSET(slot); - ASSERT(ckcapi_token_is_valid(slot)); - return slot_info[offset].capi_store; -} - -CK_ULONG -ckcapi_token_get_flags(CK_SLOT_ID slot) -{ - unsigned int offset = SLOT_TO_OFFSET(slot); - ASSERT(ckcapi_token_is_valid(slot)); - return slot_info[offset].slot_flags; -} - -static void -object_free(CkCapiObject* obj) -{ - ASSERT(obj); - ASSERT(obj->obj_funcs); - ASSERT(obj->obj_funcs->release); - (obj->obj_funcs->release)(obj); -} - -void -ckcapi_token_cleanup_all(void) -{ - size_t i; - - ckcapi_lock_global(); - - if(object_hash) - { - ckcapi_hash_free(object_hash, NULL); - object_hash = NULL; - } - - if(object_array) - { - for(i = 1; i < object_array->len; ++i) - { - ASSERT(ckcapi_array_index(object_array, CkCapiObject*, i)); - object_free(ckcapi_array_index(object_array, CkCapiObject*, i)); - } - - ckcapi_array_free(object_array, TRUE); - object_array = NULL; - } - - if(logged_in_slots) - { - ckcapi_array_free(logged_in_slots, TRUE); - logged_in_slots = NULL; - } - - ckcapi_unlock_global(); -} - -CK_OBJECT_HANDLE -ckcapi_token_get_max_handle(void) -{ - if(!object_array) - return 0; - return object_array->len; -} - -CkCapiObject* -ckcapi_token_lookup_object(CK_SLOT_ID slot, CK_OBJECT_HANDLE obj) -{ - /* This must be called without any locks held */ - - CkCapiObject* ret = NULL; - - ASSERT(slot); - ASSERT(obj > 0); - - ckcapi_lock_global(); - - if(object_array && obj < object_array->len) - ret = ckcapi_array_index(object_array, CkCapiObject*, obj); - - ckcapi_unlock_global(); - - /* Must belong to the right slot */ - if(ret && ret->slot != slot) - ret = NULL; - - return ret; -} - -static unsigned int -object_hash_func(const void* a) -{ - CkCapiObject* obj = (CkCapiObject*)a; - unsigned int hash = ckcapi_hash_pointer(obj->obj_funcs); - hash ^= (obj->obj_funcs->hash_object)(obj); - return hash; -} - -static int -object_equal_func(const void* a, const void* b) -{ - CkCapiObject* ca = (CkCapiObject*)a; - CkCapiObject* cb = (CkCapiObject*)b; - if(ca == cb) - return 1; - if(ca->obj_funcs != cb->obj_funcs) - return 0; - return (ca->obj_funcs->equal_object)(ca, cb); -} - -CK_RV -ckcapi_token_register_object(CK_SLOT_ID slot, CkCapiObject* obj) -{ - CkCapiObject* prev; - CK_RV ret = CKR_OK; - - ASSERT(slot); - ASSERT(obj->id == 0); - - DBG(("registering object")); - - ckcapi_lock_global(); - - if(!object_array) - { - object_array = ckcapi_array_sized_new(0, 1, sizeof(CkCapiObject*), 16); - if(object_array) - { - /* A blank entry for '0' */ - CkCapiObject* blank = NULL; - ckcapi_array_append(object_array, blank); - } - - object_hash = ckcapi_hash_new(object_hash_func, object_equal_func); - - if(!object_array || !object_hash) - { - /* Allocation failed above */ - ret = CKR_HOST_MEMORY; - } - } - - if(ret == CKR_OK) - { - ASSERT(object_array); - ASSERT(object_hash); - - /* Look in the hash and find a previous object */ - prev = ckcapi_hash_get(object_hash, obj); - if(prev) - { - /* Register it in the previous object's place */ - obj->id = prev->id; - ASSERT(prev->id < object_array->len); - if(ckcapi_hash_set(object_hash, obj, obj)) - { - ckcapi_array_index(object_array, CkCapiObject*, obj->id) = obj; - object_free(prev); - DBGO(obj, "found old object id"); - } - else - { - ret = CKR_HOST_MEMORY; - } - } - else - { - /* Register it at the end of the array */ - obj->id = object_array->len; - ASSERT(obj->id > 0); - if(ckcapi_hash_set(object_hash, obj, obj)) - { - if(ckcapi_array_append(object_array, obj)) - { - DBGO(obj, "registered new object id"); - } - else - { - ret = CKR_HOST_MEMORY; - - /* Roll back our addition */ - ckcapi_hash_rem(object_hash, obj); - } - } - else - { - ret = CKR_HOST_MEMORY; - } - } - } - - if(ret == CKR_OK) - obj->slot = slot; - - ckcapi_unlock_global(); - - return ret; - -} - -CK_BBOOL -ckcapi_token_is_logged_in(CK_SLOT_ID slot) -{ - unsigned int count, offset; - - ASSERT(ckcapi_token_is_valid(slot)); - - if(!logged_in_slots) - return CK_FALSE; - - offset = SLOT_TO_OFFSET(slot); - count = ckcapi_token_get_count(); - - ASSERT(logged_in_slots->len == count && offset < count); - return ckcapi_array_index(logged_in_slots, CK_BBOOL, offset); -} - -CK_RV -ckcapi_token_login(CK_SLOT_ID slot) -{ - unsigned int i, count; - unsigned int offset; - CK_BBOOL value; - - ASSERT(ckcapi_token_is_valid(slot)); - - offset = SLOT_TO_OFFSET(slot); - count = ckcapi_token_get_count(); - - if(!logged_in_slots) - { - logged_in_slots = ckcapi_array_sized_new(0, 1, sizeof(CK_BBOOL), count); - if(!logged_in_slots) - return CKR_HOST_MEMORY; - - value = CK_FALSE; - for(i = 0; i < count; ++i) - ckcapi_array_append(logged_in_slots, value); - - } - - ASSERT(logged_in_slots->len == count && offset < count); - if(ckcapi_array_index(logged_in_slots, CK_BBOOL, offset)) - return CKR_USER_ALREADY_LOGGED_IN; - - ckcapi_array_index(logged_in_slots, CK_BBOOL, offset) = CK_TRUE; - return CKR_OK; -} - -CK_RV -ckcapi_token_logout(CK_SLOT_ID slot) -{ - unsigned int count, offset; - - ASSERT(ckcapi_token_is_valid(slot)); - - if(!logged_in_slots) - return CKR_USER_NOT_LOGGED_IN; - - offset = SLOT_TO_OFFSET(slot); - count = ckcapi_token_get_count(); - - ASSERT(logged_in_slots->len == count && offset < count); - if(!ckcapi_array_index(logged_in_slots, CK_BBOOL, offset)) - return CKR_USER_NOT_LOGGED_IN; - - ckcapi_array_index(logged_in_slots, CK_BBOOL, offset) = CK_FALSE; - return CKR_OK; -} diff --git a/ckcapi-token.h b/ckcapi-token.h deleted file mode 100644 index 1176701..0000000 --- a/ckcapi-token.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2007 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef CKCAPI_TOKEN_H -#define CKCAPI_TOKEN_H - -#include "ckcapi.h" - -#define CKCAPI_SLOT_CERTS 0x00000001 -#define CKCAPI_SLOT_ANYKEY 0x00000002 -#define CKCAPI_SLOT_CA 0x00000100 -#define CKCAPI_SLOT_TRUSTED 0x00000200 - -/* Register a new object, a handle will be assigned to obj->id */ -CK_RV ckcapi_token_register_object (CK_SLOT_ID slot, CkCapiObject* obj); - -/* Lookup an object for a given object handle */ -CkCapiObject* ckcapi_token_lookup_object (CK_SLOT_ID slot, CK_OBJECT_HANDLE obj); - -/* Clear all objects for all tokens. Only done when finalizing */ -void ckcapi_token_cleanup_all (void); - -/* Get the number of the maximum object handle currently in memory */ -CK_OBJECT_HANDLE ckcapi_token_get_max_handle (void); - -unsigned int ckcapi_token_get_count (void); - -CK_SLOT_ID ckcapi_token_get_slot_id (unsigned int index); - -CK_BBOOL ckcapi_token_is_valid (CK_SLOT_ID slot); - -const char* ckcapi_token_get_display_name (CK_SLOT_ID slot); - -const char* ckcapi_token_get_store_name (CK_SLOT_ID slot); - -CK_ULONG ckcapi_token_get_flags (CK_SLOT_ID slot); - -CK_RV ckcapi_token_login (CK_SLOT_ID slot); - -CK_RV ckcapi_token_logout (CK_SLOT_ID slot); - -CK_BBOOL ckcapi_token_is_logged_in (CK_SLOT_ID slot); - -#endif /* CKCAPI_TOKEN_H */ diff --git a/ckcapi-trust.c b/ckcapi-trust.c deleted file mode 100644 index 0449fc6..0000000 --- a/ckcapi-trust.c +++ /dev/null @@ -1,569 +0,0 @@ -/* - * Copyright (C) 2007 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "ckcapi.h" -#include "ckcapi-cert.h" -#include "ckcapi-object.h" -#include "ckcapi-session.h" -#include "ckcapi-token.h" -#include "ckcapi-trust.h" -#include "x509-usages.h" - -#include "pkcs11/pkcs11n.h" - -/* - * These are the attributes expected by NSS on a trust object: - * - * CKA_CLASS - * CKA_TOKEN - * CKA_LABEL - * CKA_CERT_SHA1_HASH - * CKA_CERT_MD5_HASH - * CKA_ISSUER - * CKA_SUBJECT - * CKA_TRUST_SERVER_AUTH - * CKA_TRUST_CLIENT_AUTH - * CKA_TRUST_EMAIL_PROTECTION - * CKA_TRUST_CODE_SIGNING - * CKA_SERIAL_NUMBER - */ - -typedef struct _TrustObject -{ - CkCapiObject obj; - CK_OBJECT_HANDLE cert_obj; -} -TrustObject; - -typedef struct _TrustObjectData -{ - CkCapiObjectData base; - - PCCERT_CONTEXT cert; - CERT_ENHKEY_USAGE* enhanced_usage; - - BOOL has_usage; - BYTE usage; -} -TrustObjectData; - -static CK_TRUST -has_usage(TrustObjectData* tdata, BYTE restriction) -{ - if(!tdata->has_usage) - CKT_NETSCAPE_TRUST_UNKNOWN; - if((tdata->usage & restriction) == restriction) - return CKT_NETSCAPE_TRUSTED; - return CKT_NETSCAPE_UNTRUSTED; - -} - -static CK_TRUST -has_enhanced_usage(TrustObjectData* tdata, const char* oid) -{ - CERT_ENHKEY_USAGE* eusage = tdata->enhanced_usage; - DWORD i; - - /* No usages, means anything goes */ - if(eusage == NULL) - return CKT_NETSCAPE_TRUSTED_DELEGATOR; - - for(i = 0; i < eusage->cUsageIdentifier; ++i) - { - if(eusage->rgpszUsageIdentifier[i] && - strcmp(oid, eusage->rgpszUsageIdentifier[i]) == 0) - return CKT_NETSCAPE_TRUSTED_DELEGATOR; - } - - return CKT_NETSCAPE_TRUST_UNKNOWN; -} - -static CK_RV -trust_bool_attribute(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) -{ - CK_BBOOL val; - - ASSERT(objdata); - ASSERT(attr); - - switch(attr->type) - { - /* - * Resides on the token - * - Always true for CAPI objects. - */ - case CKA_TOKEN: - val = CK_TRUE; - break; - - /* - * Private vs. Public object. - * - Always false for certificates. - */ - case CKA_PRIVATE: - val = CK_FALSE; - break; - - /* - * If object can be modified. - */ - case CKA_MODIFIABLE: - val = CK_TRUE; - break; - - /* - * TODO: Figure out what this is. - */ - case CKA_TRUST_STEP_UP_APPROVED: - val = CK_FALSE; - break; - - default: - return CKR_ATTRIBUTE_TYPE_INVALID; - }; - - return ckcapi_return_data(attr, &val, sizeof(CK_BBOOL)); -} - -static CK_RV -trust_ulong_attribute(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) -{ - TrustObjectData* tdata = (TrustObjectData*)objdata; - CK_ULONG val; - - ASSERT(tdata); - ASSERT(attr); - - switch(attr->type) - { - - /* - * Object class. - * - Always CKO_NETSCAPE_TRUST for netscape trust - */ - case CKA_CLASS: - val = CKO_NETSCAPE_TRUST; - break; - - /* - * Key restrictions - */ - case CKA_TRUST_DIGITAL_SIGNATURE: - val = has_usage(tdata, CERT_DIGITAL_SIGNATURE_KEY_USAGE); - break; - case CKA_TRUST_NON_REPUDIATION: - val = has_usage(tdata, CERT_NON_REPUDIATION_KEY_USAGE); - break; - case CKA_TRUST_KEY_ENCIPHERMENT: - val = has_usage(tdata, CERT_KEY_ENCIPHERMENT_KEY_USAGE); - break; - case CKA_TRUST_DATA_ENCIPHERMENT: - val = has_usage(tdata, CERT_DATA_ENCIPHERMENT_KEY_USAGE); - break; - case CKA_TRUST_KEY_AGREEMENT: - val = has_usage(tdata, CERT_KEY_AGREEMENT_KEY_USAGE); - break; - case CKA_TRUST_KEY_CERT_SIGN: - val = has_usage(tdata, CERT_KEY_CERT_SIGN_KEY_USAGE); - break; - case CKA_TRUST_CRL_SIGN: - val = has_usage(tdata, CERT_CRL_SIGN_KEY_USAGE); - break; - - /* - * Various trust flags - */ - case CKA_TRUST_SERVER_AUTH: - val = has_enhanced_usage(tdata, X509_USAGE_SERVER_AUTH); - break; - case CKA_TRUST_CLIENT_AUTH: - val = has_enhanced_usage(tdata, X509_USAGE_CLIENT_AUTH); - break; - case CKA_TRUST_CODE_SIGNING: - val = has_enhanced_usage(tdata, X509_USAGE_CODE_SIGNING); - break; - case CKA_TRUST_EMAIL_PROTECTION: - val = has_enhanced_usage(tdata, X509_USAGE_EMAIL); - break; - case CKA_TRUST_IPSEC_END_SYSTEM: - val = has_enhanced_usage(tdata, X509_USAGE_IPSEC_ENDPOINT); - break; - case CKA_TRUST_IPSEC_TUNNEL: - val = has_enhanced_usage(tdata, X509_USAGE_IPSEC_TUNNEL); - break; - case CKA_TRUST_IPSEC_USER: - val = has_enhanced_usage(tdata, X509_USAGE_IPSEC_USER); - break; - case CKA_TRUST_TIME_STAMPING: - val = has_enhanced_usage(tdata, X509_USAGE_TIME_STAMPING); - break; - - default: - return CKR_ATTRIBUTE_TYPE_INVALID; - }; - - return ckcapi_return_data(attr, &val, sizeof(CK_ULONG)); -} - -static CK_RV -trust_bytes_attribute(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) -{ - TrustObjectData* tdata = (TrustObjectData*)objdata; - - ASSERT(tdata); - ASSERT(attr); - - switch(attr->type) - { - /* - * Forward these through to the certificate itself. - */ - case CKA_SUBJECT: - case CKA_ISSUER: - case CKA_SERIAL_NUMBER: - case CKA_LABEL: - ASSERT(tdata->cert); - return ckcapi_cert_certificate_get_bytes(tdata->cert, attr); - - /* - * The hash of the DER encoded certificate. - */ - case CKA_CERT_MD5_HASH: - case CKA_CERT_SHA1_HASH: - if(!CryptHashCertificate(0, attr->type == CKA_CERT_MD5_HASH ? CALG_MD5 : CALG_SHA1, - 0, tdata->cert->pbCertEncoded, - tdata->cert->cbCertEncoded, attr->pValue, - (DWORD*)(&attr->ulValueLen))) - return ckcapi_winerr_to_ckr(GetLastError()); - return CKR_OK; - }; - - return CKR_ATTRIBUTE_TYPE_INVALID; -} - -static unsigned int -trust_hash_func(CkCapiObject* obj) -{ - return ckcapi_hash_integer(((TrustObject*)obj)->cert_obj); -} - -static int -trust_equal_func(CkCapiObject* a, CkCapiObject* b) -{ - return ((TrustObject*)a)->cert_obj == ((TrustObject*)b)->cert_obj; -} - -static void -trust_release(void* data) -{ - TrustObjectData* tdata = (TrustObjectData*)data; - ASSERT(tdata); - - ASSERT(tdata->cert); - CertFreeCertificateContext(tdata->cert); - - if(tdata->enhanced_usage) - free(tdata->enhanced_usage); - - free(tdata); -} - -static const CkCapiObjectDataVtable trust_objdata_vtable = { - trust_bool_attribute, - trust_ulong_attribute, - trust_bytes_attribute, - trust_release, -}; - -static CK_RV -parse_usage(TrustObjectData* tdata, DWORD flags) -{ - DWORD size, err; - CERT_ENHKEY_USAGE* eusage; - - ASSERT(!tdata->enhanced_usage); - - /* Get the size of the enhanced_usage */ - if(!CertGetEnhancedKeyUsage(tdata->cert, flags, NULL, &size)) - { - err = GetLastError(); - - /* No enhanced_usage data is not an error */ - if(err == CRYPT_E_NOT_FOUND) - return CKR_OK; - return ckcapi_winerr_to_ckr(err); - } - - eusage = (CERT_ENHKEY_USAGE*)calloc(1, size); - if(!eusage) - return CKR_HOST_MEMORY; - - /* Now get the actual enhanced usage property */ - if(!CertGetEnhancedKeyUsage(tdata->cert, flags, eusage, &size)) - { - err = GetLastError(); - if(err == CRYPT_E_NOT_FOUND) - return CKR_OK; - return ckcapi_winerr_to_ckr(err); - } - - tdata->enhanced_usage = eusage; - return CKR_OK; -} - -static CK_RV -parse_restrictions(TrustObjectData* tdata) -{ - CRYPT_BIT_BLOB* rst; - CERT_EXTENSION* ext; - DWORD size; - - ASSERT(tdata); - ASSERT(tdata->cert); - - tdata->has_usage = CK_FALSE; - tdata->usage = 0x00; - - ext = CertFindExtension(szOID_KEY_USAGE, - tdata->cert->pCertInfo->cExtension, - tdata->cert->pCertInfo->rgExtension); - - /* No key usage, don't care */ - if(!ext) - return CKR_OK; - - /* Find the size of the decoded structure */ - if(!CryptDecodeObject(CKCAPI_ENCODINGS, X509_KEY_USAGE, - ext->Value.pbData, ext->Value.cbData, 0, NULL, &size)) - return ckcapi_winerr_to_ckr(GetLastError()); - - /* Allocate enough memory */ - rst = (CRYPT_BIT_BLOB*)calloc(1, size); - if(!rst) - return CKR_HOST_MEMORY; - - /* And get the decoded structure */ - if(CryptDecodeObject(CKCAPI_ENCODINGS, X509_KEY_USAGE, - ext->Value.pbData, ext->Value.cbData, 0, rst, &size)) - { - if(rst->cbData != 1 && - rst->cUnusedBits != 0) - { - DBG(("key usage are of invalid size")); - } - else - { - /* A valid byte of key restricted usage flags. Yes all that for one byte */ - tdata->usage = *((BYTE*)(rst->pbData)); - tdata->has_usage = TRUE; - } - } - - free(rst); - return CKR_OK; -} - -static CK_RV -trust_load_data(CkCapiSession* sess, CkCapiObject* obj, CkCapiObjectData** objdata) -{ - TrustObject* tobj = (TrustObject*)obj; - TrustObjectData* tdata; - CkCapiObjectData* certdata; - CK_RV ret; - - ASSERT(tobj); - ASSERT(objdata); - - /* Get the raw data for the certificate */ - ret = ckcapi_session_get_object_data_for(sess, tobj->cert_obj, &certdata); - if(ret != CKR_OK) - return ret; - - tdata = (TrustObjectData*)calloc(1, sizeof(TrustObjectData)); - if(!tdata) - return CKR_HOST_MEMORY; - - tdata->cert = ckcapi_cert_object_data_get_certificate (certdata); - ASSERT(tdata->cert); - - /* Dig up the restrictions data extension */ - ret = parse_restrictions(tdata); - if(ret != CKR_OK) - { - free(tdata); - return ret; - } - - /* Dig up the enhanced usage data property, and then try the extension */ - ret = parse_usage(tdata, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG); - if(ret == CKR_OK && !tdata->enhanced_usage) - ret = parse_usage(tdata, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG); - - if(ret != CKR_OK) - { - free(tdata); - return ret; - } - - /* And keep a reference to the certificate */ - tdata->cert = CertDuplicateCertificateContext(tdata->cert); - - tdata->base.object = obj->id; - tdata->base.data_funcs = &trust_objdata_vtable; - - *objdata = &(tdata->base); - return CKR_OK; -} - - -static void -trust_object_release(void* data) -{ - TrustObject* tobj = (TrustObject*)data; - ASSERT(tobj); - free(tobj); -} - -static const CkCapiObjectVtable trust_object_vtable = { - trust_load_data, - trust_hash_func, - trust_equal_func, - trust_object_release, -}; - -static CK_RV -register_trust_object(CkCapiSession* sess, CkCapiObject* cert, CkCapiObject** obj) -{ - TrustObject* tobj; - CK_RV ret; - - tobj = calloc(1, sizeof(TrustObject)); - if(!tobj) - return CKR_HOST_MEMORY; - - tobj->cert_obj = cert->id; - - tobj->obj.id = 0; - tobj->obj.obj_funcs = &trust_object_vtable; - - ret = ckcapi_token_register_object(sess->slot, &(tobj->obj)); - if(ret != CKR_OK) - { - free(tobj); - return ret; - } - - ASSERT(tobj->obj.id != 0); - *obj = &(tobj->obj); - - return CKR_OK; -} - -static CK_RV -list_matching_certificates(CkCapiSession* sess, CK_ATTRIBUTE_PTR match, - CK_ULONG count, CkCapiArray* arr) -{ - CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; - CK_ATTRIBUTE search[3]; - CK_ULONG n_search = 0; - CK_ULONG i; - - /* The class */ - search[0].type = CKA_CLASS; - search[0].pValue = &cert_class; - search[0].ulValueLen = sizeof(CK_OBJECT_CLASS); - ++n_search; - - for(i = 0; i < count && n_search < 3; ++i) - { - /* - * These are the attributes that tie a certificate - * to trust object, so try match certs with these - */ - if(match[i].type == CKA_ISSUER || - match[i].type == CKA_SERIAL_NUMBER) - { - search[n_search].type = match[i].type; - search[n_search].pValue = match[i].pValue; - search[n_search].ulValueLen = match[i].ulValueLen; - ++n_search; - } - } - - /* Do the certificate search */ - return ckcapi_cert_find(sess, CKO_CERTIFICATE, search, n_search, arr); -} - -CK_RV -ckcapi_trust_find(CkCapiSession* sess, CK_OBJECT_CLASS cls, - CK_ATTRIBUTE_PTR match, CK_ULONG count, CkCapiArray* arr) -{ - CK_OBJECT_HANDLE id; - CkCapiObject* obj; - CkCapiObject* certobj; - CkCapiObjectData* objdata; - CkCapiArray* certarr; - CK_RV ret = CKR_OK; - CK_ULONG i; - - /* We only have trust objects in here */ - if(cls != CKO_NETSCAPE_TRUST && cls != CKO_ANY) - return CKR_OK; - - /* Only work with slots that have certificates */ - if(!(ckcapi_token_get_flags (sess->slot) & CKCAPI_SLOT_CERTS)) - return CKR_OK; - - /* Get a list of all certificates */ - certarr = ckcapi_array_new(0, 1, sizeof(CK_OBJECT_HANDLE)); - if(!certarr) - return CKR_HOST_MEMORY; - ret = list_matching_certificates(sess, match, count, certarr); - - /* Now match each of them against our criteria */ - if(ret == CKR_OK) - { - for(i = 0; i < certarr->len; ++i) - { - id = ckcapi_array_index(certarr, CK_OBJECT_HANDLE, i); - ASSERT(id); - - certobj = ckcapi_token_lookup_object(sess->slot, id); - ASSERT(certobj); - - /* We'll register a trust object for any loaded certificate */ - ret = register_trust_object(sess, certobj, &obj); - if(ret != CKR_OK) - break; - - ASSERT(obj); - - ret = ckcapi_session_get_object_data(sess, obj, &objdata); - if(ret != CKR_OK) - break; - - /* Only return new object if it matches */ - if(ckcapi_object_data_match(objdata, match, count)) - ckcapi_array_append(arr, obj->id); - } - } - - ckcapi_array_free(certarr, TRUE); - return ret; -} diff --git a/ckcapi-trust.h b/ckcapi-trust.h deleted file mode 100644 index f8d8bcc..0000000 --- a/ckcapi-trust.h +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2007 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef CKCAPI_TRUST_H -#define CKCAPI_TRUST_H - -#include "ckcapi.h" - -/* Find trust objects matching criteria */ -CK_RV ckcapi_trust_find (CkCapiSession* sess, CK_OBJECT_CLASS cls, - CK_ATTRIBUTE_PTR match, CK_ULONG count, - CkCapiArray* arr); - -#endif /* CRYPTOKI_TRUST_H */ diff --git a/ckcapi-util.c b/ckcapi-util.c deleted file mode 100644 index ae8c39b..0000000 --- a/ckcapi-util.c +++ /dev/null @@ -1,547 +0,0 @@ -/* - * Copyright (C) 2007 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include "ckcapi-util.h" - -#include -#include -#include - - -void -ckcapi_reverse_memory (void* data, size_t length) -{ - size_t end = length - 1; - size_t middle = length / 2; - unsigned char* buf = data; - size_t i; - - for (i = 0; i < middle; i++) - { - unsigned char tmp = buf[i]; - buf[i] = buf[end - i]; - buf[end - i] = tmp; - } -} - -/* - * Array code originially from Glib. - * Modified extensively by Stef Walter - */ - -/* GLIB - Library of useful routines for C programming - * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - - -#define MIN_ARRAY_SIZE 16 - -typedef struct _RealArray -{ - CkCapiArray pub; - size_t alloc; - size_t elt_size; - int zero_terminated : 1; - int clear : 1; -} -RealArray; - -#define array_elt_len(array, i) ((array)->elt_size * (i)) -#define array_elt_pos(array, i) (((char*)(array)->pub.data) + array_elt_len((array),(i))) -#define array_elt_zero(array, pos, len) \ - (memset(array_elt_pos((array), pos), 0, array_elt_len((array), len))) -#define array_zero_terminate(array) \ - { if ((array)->zero_terminated) \ - array_elt_zero((array), (array)->pub.len, 1); } - -static unsigned int -nearest_pow(unsigned int num) -{ - unsigned int n = 1; - while(n < num) - n <<= 1; - return n; -} - -static int -maybe_expand(RealArray *array, size_t len) -{ - void* mem; - size_t want_alloc = array_elt_len(array, array->pub.len + len + - array->zero_terminated); - - if(want_alloc > array->alloc) - { - want_alloc = nearest_pow(want_alloc); - want_alloc = want_alloc > MIN_ARRAY_SIZE ? want_alloc : MIN_ARRAY_SIZE; - - mem = realloc(array->pub.data, want_alloc); - if(!mem) - return 0; - array->pub.data = mem; - - memset((char*)array->pub.data + array->alloc, 0, want_alloc - array->alloc); - array->alloc = want_alloc; - } - - return 1; -} - -CkCapiArray* -ckcapi_array_new(int zero_terminated, int clear, size_t elt_size) -{ - return ckcapi_array_sized_new(zero_terminated, clear, elt_size, 0); -} - -CkCapiArray* -ckcapi_array_sized_new(int zero_terminated, int clear, size_t elt_size, - size_t reserved_size) -{ - RealArray *array = malloc(sizeof(RealArray)); - if(!array) - return NULL; - - array->pub.data = NULL; - array->pub.len = 0; - array->alloc = 0; - array->zero_terminated = (zero_terminated ? 1 : 0); - array->clear = (clear ? 1 : 0); - array->elt_size = elt_size; - - if(array->zero_terminated || reserved_size != 0) - { - maybe_expand(array, reserved_size); - array_zero_terminate(array); - } - - return (CkCapiArray*)array; -} - -void* -ckcapi_array_free(CkCapiArray* array, int free_segment) -{ - void* segment; - - if(array == NULL) - return NULL; - - if(free_segment) - { - if(array->data) - free(array->data); - segment = NULL; - } - else - segment = array->data; - - free(array); - return segment; -} - -int -ckcapi_array_append_vals(CkCapiArray* parray, const void* data, size_t len) -{ - RealArray* array = (RealArray*)parray; - if(!maybe_expand(array, len)) - return 0; - - memcpy(array_elt_pos(array, array->pub.len), data, - array_elt_len(array, len)); - - array->pub.len += len; - array_zero_terminate(array); - - return 1; -} - -void -ckcapi_array_remove_index(CkCapiArray* parray, unsigned int index) -{ - RealArray* array = (RealArray*)parray; - - if(index >= array->pub.len) - return; - - if(index != array->pub.len - 1) - memmove(array_elt_pos (array, index), - array_elt_pos (array, index + 1), - array_elt_len (array, array->pub.len - index - 1)); - - array->pub.len -= 1; - - array_elt_zero (array, array->pub.len, 1); -} - -void -ckcapi_array_remove_range(CkCapiArray* parray, unsigned int index, size_t length) -{ - RealArray *array = (RealArray*)parray; - - if(index >= array->pub.len) - return; - if(index + length > array->pub.len) - length = array->pub.len - index; - if(length == 0) - return; - - if(index + length != array->pub.len) - memmove(array_elt_pos (array, index), - array_elt_pos (array, index + length), - (array->pub.len - (index + length)) * array->elt_size); - - array->pub.len -= length; - array_elt_zero(array, array->pub.len, length); -} - - -/* - * Originally from apache 2.0 - * Extensive modifications by - */ - -/* Copyright 2000-2004 The Apache Software Foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -/* - * The internal form of a hash table. - * - * The table is an array indexed by the hash of the key; collisions - * are resolved by hanging a linked list of hash entries off each - * element of the array. Although this is a really simple design it - * isn't too bad given that pools have a low allocation overhead. - */ - -typedef struct _HashEntry -{ - struct _HashEntry* next; - unsigned int hash; - const void* key; - void* val; -} -HashEntry; - -/* - * The size of the array is always a power of two. We use the maximum - * index rather than the size so that we can use bitwise-AND for - * modular arithmetic. - * The count of hash entries may be greater depending on the chosen - * collision rate. - */ -struct _CkCapiHash -{ - HashEntry** array; - CkCapiHashFunc hash_func; - CkCapiHashEqual equal_func; - size_t count; - size_t max; -}; - - -#define INITIAL_MAX 15 /* tunable == 2^n - 1 */ - -static int -equal_default(const void* a, const void* b) -{ - return a == b; -} - -/* - * Hash creation functions. - */ - -static HashEntry** -alloc_array(CkCapiHash* ht, size_t max) -{ - return calloc(1, sizeof(*(ht->array)) * (max + 1)); -} - -CkCapiHash* -ckcapi_hash_new(CkCapiHashFunc hash_func, CkCapiHashEqual equal_func) -{ - CkCapiHash* ht = malloc(sizeof(CkCapiHash)); - if(ht) - { - ht->hash_func = hash_func ? hash_func : ckcapi_hash_pointer; - ht->equal_func = equal_func ? equal_func : equal_default; - ht->count = 0; - ht->max = INITIAL_MAX; - ht->array = alloc_array(ht, ht->max); - if(!ht->array) - { - free(ht); - ht = NULL; - } - } - return ht; -} - -void -ckcapi_hash_free(CkCapiHash* ht, CkCapiHashDestroy destroy_func) -{ - HashEntry* he; - HashEntry* next; - size_t i; - - for(i = 0; i <= ht->max; ++i) - { - for(he = ht->array[i]; he; ) - { - next = he->next; - if(destroy_func) - (destroy_func)((void*)he->val); - free(he); - he = next; - } - } - - if(ht->array) - free(ht->array); - free(ht); -} - -/* - * Expanding a hash table - */ -static int -expand_array(CkCapiHash* ht) -{ - HashEntry** new_array; - size_t new_max; - HashEntry* he; - HashEntry* next; - size_t i; - - new_max = ht->max * 2 + 1; - new_array = alloc_array(ht, new_max); - - if(!new_array) - return 0; - - for(i = 0; i <= ht->max; ++i) - { - for(he = ht->array[i], next = he ? he->next : NULL; - he != NULL; he = next, next = next ? next->next : NULL) - { - unsigned int j = he->hash & new_max; - he->next = new_array[j]; - new_array[j] = he; - } - } - - if(ht->array) - free(ht->array); - - ht->array = new_array; - ht->max = new_max; - return 1; -} - -/* - * This is where we keep the details of the hash function and control - * the maximum collision rate. - * - * If val is non-NULL it creates and initializes a new hash entry if - * there isn't already one there; it returns an updatable pointer so - * that hash entries can be removed. - */ - -static HashEntry** -find_entry(CkCapiHash* ht, const void* key, void* val) -{ - HashEntry** hep; - HashEntry* he; - unsigned int hash; - - hash = (ht->hash_func)(key); - - /* scan linked list */ - for(hep = &ht->array[hash & ht->max], he = *hep; - he; hep = &he->next, he = *hep) - { - if(he->hash == hash && (ht->equal_func)(he->key, key)) - break; - } - - if(he || !val) - return hep; - - /* add a new entry for non-NULL val */ - he = malloc(sizeof(*he)); - if(he) - { - /* Key points to external data */ - he->key = key; - he->next = NULL; - he->hash = hash; - he->val = val; - - *hep = he; - ht->count++; - } - - return hep; -} - -void* -ckcapi_hash_get(CkCapiHash* ht, const void *key) -{ - HashEntry** he = find_entry(ht, key, NULL); - if(he && *he) - return (void*)((*he)->val); - else - return NULL; -} - -int -ckcapi_hash_set(CkCapiHash* ht, const void* key, void* val) -{ - HashEntry** hep = find_entry(ht, key, val); - if(hep && *hep) - { - /* replace entry */ - (*hep)->key = key; - (*hep)->val = val; - - /* check that the collision rate isn't too high */ - if(ht->count > ht->max) - { - if(!expand_array(ht)) - return 0; - } - - return 1; - } - - return 0; -} - -void* -ckcapi_hash_rem(CkCapiHash* ht, const void* key) -{ - HashEntry** hep = find_entry(ht, key, NULL); - void* val = NULL; - - if(hep && *hep) - { - HashEntry* old = *hep; - *hep = (*hep)->next; - --ht->count; - val = (void*)old->val; - free(old); - } - - return val; -} - -size_t -ckcapi_hash_count(CkCapiHash* ht) -{ - return ht->count; -} - -unsigned int -ckcapi_hash_pointer(const void* ptr) -{ - return (unsigned int)ptr; -} - -unsigned int -ckcapi_hash_data(const void* data, size_t n_data) -{ - unsigned int hash = 0; - const unsigned char* end; - const unsigned char* p; - - /* - * This is the popular `times 33' hash algorithm which is used by - * perl and also appears in Berkeley DB. This is one of the best - * known hash functions for strings because it is both computed - * very fast and distributes very well. - * - * The originator may be Dan Bernstein but the code in Berkeley DB - * cites Chris Torek as the source. The best citation I have found - * is "Chris Torek, Hash function for text in C, Usenet message - * <27038@mimsy.umd.edu> in comp.lang.c , October, 1990." in Rich - * Salz's USENIX 1992 paper about INN which can be found at - * . - * - * The magic of number 33, i.e. why it works better than many other - * constants, prime or not, has never been adequately explained by - * anyone. So I try an explanation: if one experimentally tests all - * multipliers between 1 and 256 (as I did while writing a low-level - * data structure library some time ago) one detects that even - * numbers are not useable at all. The remaining 128 odd numbers - * (except for the number 1) work more or less all equally well. - * They all distribute in an acceptable way and this way fill a hash - * table with an average percent of approx. 86%. - * - * If one compares the chi^2 values of the variants (see - * Bob Jenkins ``Hashing Frequently Asked Questions'' at - * http://burtleburtle.net/bob/hash/hashfaq.html for a description - * of chi^2), the number 33 not even has the best value. But the - * number 33 and a few other equally good numbers like 17, 31, 63, - * 127 and 129 have nevertheless a great advantage to the remaining - * numbers in the large set of possible multipliers: their multiply - * operation can be replaced by a faster operation based on just one - * shift plus either a single addition or subtraction operation. And - * because a hash function has to both distribute good _and_ has to - * be very fast to compute, those few numbers should be preferred. - * - * -- Ralf S. Engelschall - */ - - for(p = data, end = p + n_data; p != end; ++p) - hash = hash * 33 + *p; - - return hash; -} - -unsigned int -ckcapi_hash_integer(int integer) -{ - return integer; -} diff --git a/ckcapi-util.h b/ckcapi-util.h deleted file mode 100644 index 59e660c..0000000 --- a/ckcapi-util.h +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (C) 2007 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef __CKCAPI_UTIL_H__ -#define __CKCAPI_UTIL_H__ - -#include - - -void ckcapi_reverse_memory (void* data, size_t length); - -/* -------------------------------------------------------------------------------- - * ARRAYS - */ - -typedef struct _Array -{ - void* data; - size_t len; -} -CkCapiArray; - -#define ckcapi_array_append(a,v) ckcapi_array_append_vals(a, &(v), 1) -#define ckcapi_array_index(a,t,i) (((t*) (a)->data) [(i)]) - -CkCapiArray* ckcapi_array_new (int zero_terminated, int zero, - size_t element_size); - -CkCapiArray* ckcapi_array_sized_new (int zero_terminated, int zero, - size_t element_size, size_t reserved_size); - -void* ckcapi_array_free (CkCapiArray* array, int free_segment); - -int ckcapi_array_append_vals (CkCapiArray* array, const void* data, - size_t num); - -void ckcapi_array_remove_index (CkCapiArray* array, unsigned int index); - -void ckcapi_array_remove_range (CkCapiArray* array, unsigned int index, - size_t count); - - -/* -------------------------------------------------------------------------------- - * HASHTABLE - */ - -struct _CkCapiHash; -typedef struct _CkCapiHash CkCapiHash; - -typedef unsigned int (*CkCapiHashFunc)(const void* key); - -typedef int (*CkCapiHashEqual)(const void* a, const void* b); - -typedef void (*CkCapiHashDestroy)(void* val); - -CkCapiHash* ckcapi_hash_new (CkCapiHashFunc hash_func, CkCapiHashEqual equal_func); - -void ckcapi_hash_free (CkCapiHash* ht, CkCapiHashDestroy destroy_func); - -size_t ckcapi_hash_count (CkCapiHash* ht); - -void* ckcapi_hash_get (CkCapiHash* ht, const void* key); - -int ckcapi_hash_set (CkCapiHash* ht, const void* key, void* val); - -void* ckcapi_hash_rem (CkCapiHash* ht, const void* key); - -unsigned int ckcapi_hash_pointer (const void* ptr); - -unsigned int ckcapi_hash_data (const void* data, size_t n_data); - -unsigned int ckcapi_hash_integer (int integer); - -#define ckcapi_hash_key(num) \ - (((char*)NULL) + (size_t)(num)) - -#endif /* __CKCAPI_UTIL_H__ */ \ No newline at end of file diff --git a/ckcapi.c b/ckcapi.c deleted file mode 100644 index c0e6036..0000000 --- a/ckcapi.c +++ /dev/null @@ -1,1515 +0,0 @@ -/* - * Copyright (C) 2007 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#include -#include -#include - -#include "ckcapi.h" -#include "ckcapi-object.h" -#include "ckcapi-session.h" -#include "ckcapi-rsa.h" -#include "ckcapi-token.h" - -/* Warns about all the raw string usage in this file */ -#pragma warning (disable : 4996) - -/* ------------------------------------------------------------------- - * GLOBALS / DEFINES - */ - -static int cryptoki_initialized = 0; -static HANDLE global_mutex = NULL; - -#define MANUFACTURER_ID "Cryptoki CAPI " -#define LIBRARY_DESCRIPTION "Cryptoki CAPI Provider " -#define LIBRARY_VERSION_MAJOR 1 -#define LIBRARY_VERSION_MINOR 1 -#define HARDWARE_VERSION_MAJOR 0 -#define HARDWARE_VERSION_MINOR 0 -#define FIRMWARE_VERSION_MAJOR 0 -#define FIRMWARE_VERSION_MINOR 0 -#define SLOT_TOKEN_SERIAL "1.0 " -#define SLOT_TOKEN_MODEL "1.0 " -#define MAX_PIN_LEN 256 -#define MIN_PIN_LEN 1 - -static CK_MECHANISM_TYPE all_mechanisms[] = { - CKM_RSA_PKCS -}; - -/* ------------------------------------------------------------------- - * MODULE GLOBAL FUNCTIONS - */ - -#define LINE 1024 - -void -ckcapi_debug(const char* msg, ...) -{ - char buf[LINE]; - va_list va; - size_t len; - - va_start(va, msg); - _vsnprintf(buf, 1024, msg, va); - va_end(va); - - buf[LINE - 1] = 0; - len = strlen (buf); - - strncpy(buf + len, "\n", 1024 - len); - buf[LINE - 1] = 0; - // OutputDebugStringA(buf); -} - -/* Bah humbug, MSVC doesn't have __func__ */ -#define ENTER(func) \ - char* _func = #func; \ - ckcapi_debug("%s: enter", _func) - -#define RETURN(ret) \ - return (ckcapi_debug("%s: %d", _func, ret), ret) - -#define PREREQ(cond, ret) \ - if (!(cond)) { ckcapi_debug("%s: %s failed: %d", _func, #cond, ret); return ret; } - -void -ckcapi_lock_global(void) -{ - DWORD r; - - ASSERT(global_mutex); - - r = WaitForSingleObject(global_mutex, INFINITE); - ASSERT(r == WAIT_OBJECT_0); -} - -void -ckcapi_unlock_global(void) -{ - BOOL r; - - ASSERT(global_mutex); - - r = ReleaseMutex(global_mutex); - ASSERT(r); -} - -CK_RV -ckcapi_winerr_to_ckr(DWORD werr) -{ - switch(werr) - { - case ERROR_NOT_ENOUGH_MEMORY: - case ERROR_OUTOFMEMORY: - return CKR_HOST_MEMORY; - break; - case NTE_NO_MEMORY: - return CKR_DEVICE_MEMORY; - break; - case ERROR_MORE_DATA: - return CKR_BUFFER_TOO_SMALL; - case ERROR_INVALID_PARAMETER: /* these params were derived from the */ - case ERROR_INVALID_HANDLE: /* inputs, so if they are bad, the input */ - case NTE_BAD_ALGID: /* data is bad */ - case NTE_BAD_HASH: - case NTE_BAD_TYPE: - case NTE_BAD_PUBLIC_KEY: - return CKR_DATA_INVALID; - case ERROR_BUSY: - case NTE_FAIL: - case NTE_BAD_UID: - return CKR_DEVICE_ERROR; - default: - return CKR_GENERAL_ERROR; - }; -} - -CK_RV -ckcapi_return_data_raw(CK_VOID_PTR output, CK_ULONG_PTR n_output, - CK_VOID_PTR input, CK_ULONG n_input) -{ - ASSERT(n_output); - ASSERT(input); - - /* Just asking for the length */ - if(!output) - { - *n_output = n_input; - return CKR_OK; - } - - /* Buffer is too short */ - if(n_input > *n_output) - { - *n_output = n_input; - return CKR_BUFFER_TOO_SMALL; - } - - *n_output = n_input; - memcpy(output, input, n_input); - return CKR_OK; -} - -CK_RV -ckcapi_return_data(CK_ATTRIBUTE_PTR attr, CK_VOID_PTR input, DWORD n_input) -{ - return ckcapi_return_data_raw(attr->pValue, &(attr->ulValueLen), - input, n_input); -} - -CK_RV -ckcapi_return_string(CK_ATTRIBUTE_PTR attr, WCHAR* string) -{ - CK_UTF8CHAR_PTR buffer; - int result; - - SetLastError(0); - - /* - * Sadly WideCharToMultiByte doesn't handle zero - * length strings properly. So we have to special - * case this part. - */ - if(!string[0]) - return ckcapi_return_data(attr, "", 0); - - /* The length of the string, including null termination */ - result = WideCharToMultiByte(CP_UTF8, 0, string, -1, - NULL, 0, NULL, NULL); - - if(result) - { - /* Did the caller just want the length? */ - if(!attr->pValue) - { - attr->ulValueLen = result - 1; - return CKR_OK; - } - - /* Is the callers buffer too short? */ - if((int)attr->ulValueLen < result - 1) - { - attr->ulValueLen = result - 1; - return CKR_BUFFER_TOO_SMALL; - } - - /* - * Allocate a buffer for the conversion. We have to - * do this because strings in PKCS#11 are not null - * terminated and strings returned from - * WideCharToMultiByte are always null terminated. - */ - buffer = malloc(result); - if(!buffer) - return CKR_HOST_MEMORY; - - /* Do the actual conversion */ - result = WideCharToMultiByte(CP_UTF8, 0, string, -1, - buffer, result, NULL, NULL); - - if(result) - { - attr->ulValueLen = result - 1; - memcpy(attr->pValue, buffer, attr->ulValueLen); - - free(buffer); - return CKR_OK; - } - - free(buffer); - } - - /* - * We should never have too little buffer, or - * get a zero length success code. It's a very - * strange error that arrives here. - */ - return CKR_GENERAL_ERROR; -} - -CK_RV -ckcapi_return_dword_as_bytes(CK_ATTRIBUTE_PTR attr, DWORD value) -{ - int i; - CK_ULONG count = 0; - BOOL first = TRUE; - BYTE* at = attr->pValue; - CK_RV ret = CKR_OK; - - for(i = 0; i < sizeof(DWORD); i++) - { - BYTE digit = (BYTE)((value >> (((sizeof(DWORD)-1)*8))) & 0xFF); - value = value << 8; - - /* No leading zero */ - if (first && digit == 0) - continue; - - first = FALSE; - if(at) - { - if(count > attr->ulValueLen) - ret = CKR_BUFFER_TOO_SMALL; - else - *(at++) = digit; - } - - count++; - } - - attr->ulValueLen = count; - return ret; -} - -CK_RV -ckcapi_return_reversed_data(CK_ATTRIBUTE_PTR attr, CK_VOID_PTR data, CK_ULONG length) -{ - CK_RV ret = ckcapi_return_data(attr, data, length); - if(ret != CKR_OK || !attr->pValue) - return ret; - - ckcapi_reverse_memory(attr->pValue, attr->ulValueLen); - return CKR_OK; -} - -static void -print_zero_decimal(CK_BYTE_PTR buffer, CK_ULONG length, WORD value) -{ - int i; - for(i = (int)length - 1; i >= 0; --i) - { - BYTE digit = value % 10; - buffer[i] = '0' + digit; - value /= 10; - } -} - -CK_RV -ckcapi_return_filetime(CK_ATTRIBUTE_PTR attr, FILETIME *ftime) -{ - SYSTEMTIME stime; - CK_DATE* date; - - ASSERT(attr); - ASSERT(ftime); - - if(!attr->pValue) - { - attr->ulValueLen = sizeof(CK_DATE); - return CKR_OK; - } - - if(attr->ulValueLen < sizeof(CK_DATE)) - { - attr->ulValueLen = sizeof(CK_DATE); - return CKR_BUFFER_TOO_SMALL; - } - - if(!FileTimeToSystemTime(ftime, &stime)) - { - DBG(("An invalid FILETIME was encountered")); - return CKR_GENERAL_ERROR; - } - - date = (CK_DATE*)attr->pValue; - attr->ulValueLen = sizeof(CK_DATE); - print_zero_decimal(date->year, sizeof(date->year), stime.wYear); - print_zero_decimal(date->month, sizeof(date->month), stime.wMonth); - print_zero_decimal(date->day, sizeof(date->day), stime.wDay); - - return CKR_OK; -} - -/* ---------------------------------------------------------------- */ - -static CK_RV -CC_C_Initialize(CK_VOID_PTR init_args) -{ - ENTER(C_Initialize); - PREREQ(!cryptoki_initialized, CKR_CRYPTOKI_ALREADY_INITIALIZED); - - if (init_args != NULL) { - CK_C_INITIALIZE_ARGS_PTR args; - int supplied_ok; - - /* pReserved must be NULL */ - args = init_args; - PREREQ(!args->pReserved, CKR_ARGUMENTS_BAD); - - /* ALL supplied function pointers need to have the value either NULL or non-NULL. */ - supplied_ok = (args->CreateMutex == NULL && args->DestroyMutex == NULL && - args->LockMutex == NULL && args->UnlockMutex == NULL) || - (args->CreateMutex != NULL && args->DestroyMutex != NULL && - args->LockMutex != NULL && args->UnlockMutex != NULL); - PREREQ(supplied_ok, CKR_ARGUMENTS_BAD); - - /* - * When the CKF_OS_LOCKING_OK flag isn't set and mutex function pointers are supplied - * by an application, return an error. We must be able to use our own locks. - */ - if(!(args->flags & CKF_OS_LOCKING_OK) && (args->CreateMutex != NULL)) - RETURN(CKR_CANT_LOCK); - } - - if(!global_mutex) - { - global_mutex = CreateMutex(NULL, FALSE, NULL); - if(!global_mutex) - RETURN(CKR_CANT_LOCK); - } - - cryptoki_initialized = 1; - - RETURN(CKR_OK); -} - -static CK_RV -CC_C_Finalize(CK_VOID_PTR pReserved) -{ - ENTER(C_Finalize); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - PREREQ(!pReserved, CKR_ARGUMENTS_BAD); - - cryptoki_initialized = 0; - - ckcapi_session_cleanup_all(); - ckcapi_token_cleanup_all(); - - RETURN(CKR_OK); -} - -static CK_RV -CC_C_GetInfo(CK_INFO_PTR info) -{ - ENTER(C_GetInfo); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - PREREQ(info, CKR_ARGUMENTS_BAD); - - ASSERT(strlen(MANUFACTURER_ID) == 32); - ASSERT(strlen(LIBRARY_DESCRIPTION) == 32); - - info->cryptokiVersion.major = CRYPTOKI_VERSION_MAJOR; - info->cryptokiVersion.minor = CRYPTOKI_VERSION_MINOR; - info->libraryVersion.major = LIBRARY_VERSION_MAJOR; - info->libraryVersion.minor = LIBRARY_VERSION_MINOR; - info->flags = 0; - strncpy((char*)info->manufacturerID, MANUFACTURER_ID, 32); - strncpy((char*)info->libraryDescription, LIBRARY_DESCRIPTION, 32); - - RETURN(CKR_OK); -} - -static CK_RV -CC_C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR list) -{ - /* This would be a strange call to receive */ - return C_GetFunctionList(list); -} - -static CK_RV -CC_C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR slot_list, CK_ULONG_PTR count) -{ - unsigned int n_tokens, i; - - ENTER(C_GetSlotList); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - PREREQ(count, CKR_ARGUMENTS_BAD); - - /* All tokens are always present */ - - n_tokens = ckcapi_token_get_count(); - - /* Application only wants to know the number of slots. */ - if(slot_list == NULL) - { - *count = n_tokens; - RETURN(CKR_OK); - } - - if(*count < n_tokens) - { - *count = n_tokens; - RETURN(CKR_BUFFER_TOO_SMALL); - } - - *count = n_tokens; - for(i = 0; i < n_tokens; ++i) - slot_list[i] = ckcapi_token_get_slot_id (i); - RETURN(CKR_OK); -} - -static CK_RV -CC_C_GetSlotInfo(CK_SLOT_ID id, CK_SLOT_INFO_PTR info) -{ - const char* name; - - ENTER(C_GetSlotInfo); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - PREREQ(info, CKR_ARGUMENTS_BAD); - - /* Make sure the slot ID is valid */ - if(!ckcapi_token_is_valid(id)) - RETURN(CKR_SLOT_ID_INVALID); - - ASSERT(strlen(MANUFACTURER_ID) == 32); - - /* Provide information about the slot in the provided buffer */ - strncpy((char*)info->manufacturerID, MANUFACTURER_ID, 32); - info->hardwareVersion.major = HARDWARE_VERSION_MAJOR; - info->hardwareVersion.minor = HARDWARE_VERSION_MINOR; - info->firmwareVersion.major = FIRMWARE_VERSION_MAJOR; - info->firmwareVersion.minor = FIRMWARE_VERSION_MINOR; - - /* Token is always present */ - info->flags = CKF_TOKEN_PRESENT; - - /* Slot name is blank padded, odd */ - name = ckcapi_token_get_display_name(id); - memset((char*)info->slotDescription, ' ', - sizeof(info->slotDescription)); - memcpy((char*)info->slotDescription, name, - min(strlen(name), sizeof(info->slotDescription))); - - RETURN(CKR_OK); -} - -static CK_RV -CC_C_GetTokenInfo(CK_SLOT_ID id, CK_TOKEN_INFO_PTR info) -{ - const char* name; - - ENTER(C_GetTokenInfo); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - PREREQ(info, CKR_ARGUMENTS_BAD); - - /* Make sure the slot ID is valid */ - if(!ckcapi_token_is_valid(id)) - RETURN(CKR_SLOT_ID_INVALID); - - ASSERT(strlen(MANUFACTURER_ID) == 32); - ASSERT(strlen(SLOT_TOKEN_MODEL) == 16); - ASSERT(strlen(SLOT_TOKEN_SERIAL) == 16); - - /* Provide information about a token in the provided buffer */ - strncpy((char*)info->manufacturerID, MANUFACTURER_ID, 32); - strncpy((char*)info->model, SLOT_TOKEN_MODEL, 16); - strncpy((char*)info->serialNumber, SLOT_TOKEN_SERIAL, 16); - - /* Protected authentication path: Windows prompts for it's own PINs */ - info->flags = CKF_TOKEN_INITIALIZED | CKF_PROTECTED_AUTHENTICATION_PATH; - info->ulMaxSessionCount = CK_EFFECTIVELY_INFINITE; - info->ulSessionCount = CK_EFFECTIVELY_INFINITE; - info->ulMaxRwSessionCount = CK_EFFECTIVELY_INFINITE; - info->ulRwSessionCount = CK_EFFECTIVELY_INFINITE; - info->ulMaxPinLen = MAX_PIN_LEN; - info->ulMinPinLen = MIN_PIN_LEN; - info->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION; - info->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION; - info->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION; - info->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION; - info->hardwareVersion.major = HARDWARE_VERSION_MAJOR; - info->hardwareVersion.minor = HARDWARE_VERSION_MINOR; - info->firmwareVersion.major = FIRMWARE_VERSION_MAJOR; - info->firmwareVersion.minor = FIRMWARE_VERSION_MINOR; - memset(info->utcTime, ' ', 16); - - /* Slot name is blank padded, odd */ - name = ckcapi_token_get_display_name(id); - memset((char*)info->label, ' ', sizeof(info->label)); - memcpy((char*)info->label, name, - min(strlen(name), sizeof(info->label))); - - RETURN(CKR_OK); -} - -static CK_RV -CC_C_GetMechanismList(CK_SLOT_ID id, CK_MECHANISM_TYPE_PTR mechanism_list, - CK_ULONG_PTR count) -{ - CK_ULONG n_mechs; - - ENTER(C_GetMechanismList); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - PREREQ(count, CKR_ARGUMENTS_BAD); - - if(!ckcapi_token_is_valid(id)) - RETURN(CKR_SLOT_ID_INVALID); - - n_mechs = sizeof(all_mechanisms) / sizeof(all_mechanisms[0]); - - if(mechanism_list == NULL) - { - *count = n_mechs; - RETURN(CKR_OK); - } - - if(*count < n_mechs) - { - *count = n_mechs; - RETURN(CKR_BUFFER_TOO_SMALL); - } - - memcpy(mechanism_list, all_mechanisms, - n_mechs * sizeof(CK_MECHANISM_TYPE)); - *count = n_mechs; - RETURN(CKR_OK); -} - -static CK_RV -CC_C_GetMechanismInfo(CK_SLOT_ID id, CK_MECHANISM_TYPE type, - CK_MECHANISM_INFO_PTR info) -{ - ENTER(C_GetMechanismInfo); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - PREREQ(info, CKR_ARGUMENTS_BAD); - - if(!ckcapi_token_is_valid(id)) - RETURN(CKR_SLOT_ID_INVALID); - - if(type == CKM_RSA_PKCS) - { - ckcapi_rsa_pkcs_get_info(type, info); - RETURN(CKR_OK); - } - - RETURN(CKR_MECHANISM_INVALID); -} - -static CK_RV -CC_C_InitToken(CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, CK_ULONG pin_len, - CK_UTF8CHAR_PTR label) -{ - ENTER(C_InitToken); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_WaitForSlotEvent(CK_FLAGS flags, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR pReserved) -{ - ENTER(C_WaitForSlotEvent); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* - * PKCS#11 GRAY AREA: What happens when we know we'll *never* - * have any slot events, and someone calls us without CKR_DONT_BLOCK? - * In case there's a thread dedicated to calling this function in a - * loop, we wait 1 seconds when called without CKR_DONT_BLOCK. - */ - - if(!(flags & CKF_DONT_BLOCK)) - Sleep(1000); - - RETURN(CKR_NO_EVENT); -} - -static CK_RV -CC_C_OpenSession(CK_SLOT_ID id, CK_FLAGS flags, CK_VOID_PTR application, - CK_NOTIFY notify, CK_SESSION_HANDLE_PTR session) -{ - CkCapiSession* sess; - CK_RV ret; - - ENTER(C_OpenSession); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - PREREQ(session, CKR_ARGUMENTS_BAD); - PREREQ(flags & CKF_SERIAL_SESSION, CKR_SESSION_PARALLEL_NOT_SUPPORTED); - - if(!ckcapi_token_is_valid(id)) - RETURN(CKR_SLOT_ID_INVALID); - - ret = ckcapi_session_create(id, &sess); - if(ret != CKR_OK) - RETURN(ret); - - sess->notify_callback = notify; - sess->user_data = application; - - if(flags & CKF_RW_SESSION) - sess->read_write = TRUE; - - ret = ckcapi_session_register(sess); - if(ret == CKR_OK) - { - /* ID should have been assigned when registering */ - ASSERT(sess->id > 0); - *session = sess->id; - } - else - { - ckcapi_session_destroy(sess); - } - - RETURN(ret); -} - -static CK_RV -CC_C_CloseSession(CK_SESSION_HANDLE session) -{ - CkCapiSession* sess; - CK_RV ret; - - ENTER(C_CloseSession); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - ret = ckcapi_session_remove_lock_ref(session, &sess); - if(ret == CKR_OK) - { - /* This will unref and possibly destroy the session */ - ckcapi_session_unref_unlock(sess); - } - - RETURN(ret); -} - -static CK_RV -CC_C_CloseAllSessions(CK_SLOT_ID id) -{ - ENTER(C_CloseAllSession); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - if(!ckcapi_token_is_valid(id)) - RETURN(CKR_SLOT_ID_INVALID); - - ckcapi_session_close_all(id); - RETURN(CKR_OK); -} - -static CK_RV -CC_C_GetFunctionStatus(CK_SESSION_HANDLE session) -{ - ENTER(C_GetFunctionStatus); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - RETURN(CKR_FUNCTION_NOT_PARALLEL); -} - -static CK_RV -CC_C_CancelFunction(CK_SESSION_HANDLE session) -{ - ENTER(C_CancelFunction); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - RETURN(CKR_FUNCTION_NOT_PARALLEL); -} - -static CK_RV -CC_C_GetSessionInfo(CK_SESSION_HANDLE session, CK_SESSION_INFO_PTR info) -{ - CkCapiSession* sess; - CK_RV ret; - - ENTER(C_GetSessionInfo); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - PREREQ(info, CKR_ARGUMENTS_BAD); - - ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); - if(ret == CKR_OK) - { - ckcapi_session_get_info(sess, info); - ckcapi_session_unref_unlock(sess); - } - - RETURN(ret); -} - -static CK_RV -CC_C_InitPIN(CK_SESSION_HANDLE session, CK_UTF8CHAR_PTR pin, - CK_ULONG pin_len) -{ - ENTER(C_InitPIN); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* We don't support this stuff. We don't support 'SO' logins. */ - RETURN(CKR_USER_NOT_LOGGED_IN); -} - -static CK_RV -CC_C_SetPIN(CK_SESSION_HANDLE session, CK_UTF8CHAR_PTR old_pin, - CK_ULONG old_len, CK_UTF8CHAR_PTR new_pin, CK_ULONG new_len) -{ - ENTER(C_SetPIN); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* Not supported, Windows takes care of this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_GetOperationState(CK_SESSION_HANDLE session, CK_BYTE_PTR operation_state, - CK_ULONG_PTR operation_state_len) -{ - ENTER(C_GetOperationState); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* Nasty, no sirrr */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_SetOperationState(CK_SESSION_HANDLE session, CK_BYTE_PTR operation_state, - CK_ULONG operation_state_len, CK_OBJECT_HANDLE encryption_key, - CK_OBJECT_HANDLE authentication_key) -{ - ENTER(C_SetOperationState); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* Nasty, no sirrr */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_Login(CK_SESSION_HANDLE session, CK_USER_TYPE user_type, - CK_UTF8CHAR_PTR pin, CK_ULONG pin_len) -{ - CkCapiSession* sess; - CK_RV ret; - - ENTER(C_Login); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); - if(ret == CKR_OK) - { - switch(user_type) - { - case CKU_USER: - ret = ckcapi_token_login(sess->slot); - break; - case CKU_SO: - ret = CKR_USER_TYPE_INVALID; - break; - default: - ret = CKR_USER_TYPE_INVALID; - break; - } - - ckcapi_session_unref_unlock(sess); - } - - RETURN(ret); -} - -static CK_RV -CC_C_Logout(CK_SESSION_HANDLE session) -{ - CkCapiSession* sess; - CK_RV ret; - - ENTER(C_Logout); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); - if(ret == CKR_OK) - { - ret = ckcapi_token_logout(sess->slot); - ckcapi_session_unref_unlock(sess); - } - - RETURN(ret); -} - -static CK_RV -CC_C_CreateObject(CK_SESSION_HANDLE session, CK_ATTRIBUTE_PTR templ, - CK_ULONG count, CK_OBJECT_HANDLE_PTR object) -{ - ENTER(C_CreateObject); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* TODO: See if we need to support this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_CopyObject(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, - CK_ATTRIBUTE_PTR templ, CK_ULONG count, - CK_OBJECT_HANDLE_PTR new_object) -{ - ENTER(C_CopyObject); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* TODO: See if we need to support this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - - -static CK_RV -CC_C_DestroyObject(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object) -{ - ENTER(C_DestroyObject); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* TODO: See if we need to support this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_GetObjectSize(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, - CK_ULONG_PTR size) -{ - ENTER(C_GetObjectSize); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* TODO: Implement this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_GetAttributeValue(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, - CK_ATTRIBUTE_PTR templ, CK_ULONG count) -{ - CkCapiSession* sess; - CkCapiObjectData* objdata; - CK_RV ret; - - ENTER(C_GetAttributeValue); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - PREREQ(object, CKR_OBJECT_HANDLE_INVALID); - PREREQ(!count || templ, CKR_ARGUMENTS_BAD); - - ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); - if(ret == CKR_OK) - { - ret = ckcapi_session_get_object_data_for(sess, object, &objdata); - if(ret == CKR_OK) - ret = ckcapi_object_data_get_attrs(objdata, templ, count); - - ckcapi_session_unref_unlock(sess); - } - - RETURN(ret); -} - -static CK_RV -CC_C_SetAttributeValue(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, - CK_ATTRIBUTE_PTR templ, CK_ULONG count) -{ - ENTER(C_SetAttributeValue); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* TODO: See if we need to implement this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_FindObjectsInit(CK_SESSION_HANDLE session, CK_ATTRIBUTE_PTR templ, - CK_ULONG count) -{ - CkCapiSession* sess; - CK_RV ret; - - ENTER(C_FindObjectsInit); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - PREREQ(!count || templ, CKR_ARGUMENTS_BAD); - - ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); - if(ret == CKR_OK) - { - ret = ckcapi_session_find_init(sess, templ, count); - ckcapi_session_unref_unlock(sess); - } - - RETURN(ret); -} - -static CK_RV -CC_C_FindObjects(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE_PTR objects, - CK_ULONG max_object_count, CK_ULONG_PTR object_count) -{ - CkCapiSession* sess; - CK_RV ret; - - ENTER(C_FindObjects); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - PREREQ(object_count, CKR_ARGUMENTS_BAD); - PREREQ(!max_object_count || objects, CKR_ARGUMENTS_BAD); - - ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); - if(ret == CKR_OK) - { - ret = ckcapi_session_find(sess, objects, max_object_count, object_count); - ckcapi_session_unref_unlock(sess); - } - - RETURN(ret); -} - -static CK_RV -CC_C_FindObjectsFinal(CK_SESSION_HANDLE session) -{ - CkCapiSession* sess; - CK_RV ret; - - ENTER(C_FindObjectsFinal); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); - if(ret == CKR_OK) - { - ret = ckcapi_session_find_final(sess); - ckcapi_session_unref_unlock(sess); - } - - RETURN(ret); -} - -static CK_RV -CC_C_EncryptInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, - CK_OBJECT_HANDLE key) -{ - ENTER(C_EncryptInit); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* TODO: See if we need to implement this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_Encrypt(CK_SESSION_HANDLE session, CK_BYTE_PTR data, CK_ULONG data_len, - CK_BYTE_PTR encrypted_data, CK_ULONG_PTR encrypted_data_len) -{ - ENTER(C_Encrypt); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* TODO: See if we need to implement this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_EncryptUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, - CK_ULONG part_len, CK_BYTE_PTR encrypted_part, - CK_ULONG_PTR encrypted_part_len) -{ - ENTER(C_EncryptUpdate); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* RSA/DSA mechs don't support incremental crypto operations. */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_EncryptFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR last_encrypted_part, - CK_ULONG_PTR last_encrypted_part_len) -{ - ENTER(C_EncryptFinal); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* RSA/DSA mechs don't support incremental crypto operations. */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_DecryptInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, - CK_OBJECT_HANDLE key) -{ - CkCapiObjectData* objdata; - CkCapiSession* sess; - CK_RV ret; - - ENTER(C_DecryptInit); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - PREREQ(mechanism, CKR_ARGUMENTS_BAD); - PREREQ(key, CKR_ARGUMENTS_BAD); - - ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); - if(ret == CKR_OK) - { - ret = ckcapi_session_get_object_data_for(sess, key, &objdata); - if(ret == CKR_OK) - ret = ckcapi_session_decrypt_init(sess, mechanism, objdata); - - ckcapi_session_unref_unlock(sess); - } - - RETURN(ret); -} - -static CK_RV -CC_C_Decrypt(CK_SESSION_HANDLE session, CK_BYTE_PTR encrypted_data, - CK_ULONG encrypted_data_len, CK_BYTE_PTR data, CK_ULONG_PTR data_len) -{ - CkCapiSession* sess; - CK_RV ret; - - ENTER(C_Decrypt); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - PREREQ(encrypted_data, CKR_ARGUMENTS_BAD); - PREREQ(encrypted_data_len, CKR_ARGUMENTS_BAD); - - ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); - if(ret == CKR_OK) - { - ret = ckcapi_session_decrypt(sess, encrypted_data, encrypted_data_len, - data, data_len); - ckcapi_session_unref_unlock(sess); - } - - RETURN(ret); -} - -static CK_RV -CC_C_DecryptUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR encrypted_part, - CK_ULONG encrypted_part_len, CK_BYTE_PTR part, CK_ULONG_PTR part_len) -{ - ENTER(C_DecryptUpdate); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* RSA/DSA mechs don't support incremental crypto operations. */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_DecryptFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR pLastPart, - CK_ULONG_PTR last_part_len) -{ - ENTER(C_DecryptFinal); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* RSA/DSA mechs don't support incremental crypto operations. */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_DigestInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism) -{ - ENTER(C_DigestInit); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* RSA/DSA mechs don't support digest. */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_Digest(CK_SESSION_HANDLE session, CK_BYTE_PTR data, CK_ULONG data_len, - CK_BYTE_PTR digest, CK_ULONG_PTR digest_len) -{ - ENTER(C_Digest); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* RSA/DSA mechs don't support digest. */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_DigestUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len) -{ - ENTER(C_DigestUpdate); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* RSA/DSA mechs don't support digest. */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_DigestKey(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) -{ - ENTER(C_DigestKey); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* RSA/DSA mechs don't support digest. */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_DigestFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR digest, - CK_ULONG_PTR digest_len) -{ - ENTER(C_DigestFinal); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* RSA/DSA mechs don't support digest. */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_SignInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, - CK_OBJECT_HANDLE key) -{ - CkCapiObjectData* objdata; - CkCapiSession* sess; - CK_RV ret; - - ENTER(C_SignInit); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - PREREQ(mechanism, CKR_ARGUMENTS_BAD); - PREREQ(key, CKR_ARGUMENTS_BAD); - - ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); - if(ret == CKR_OK) - { - ret = ckcapi_session_get_object_data_for(sess, key, &objdata); - if(ret == CKR_OK) - ret = ckcapi_session_sign_init(sess, mechanism, objdata); - - ckcapi_session_unref_unlock(sess); - } - - RETURN(ret); -} - -static CK_RV -CC_C_Sign(CK_SESSION_HANDLE session, CK_BYTE_PTR data, CK_ULONG data_len, - CK_BYTE_PTR signature, CK_ULONG_PTR signature_len) -{ - CkCapiSession* sess; - CK_RV ret; - - ENTER(C_Sign); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - PREREQ(data, CKR_ARGUMENTS_BAD); - PREREQ(data_len, CKR_ARGUMENTS_BAD); - - ret = ckcapi_session_get_lock_ref(session, FALSE, &sess); - if(ret == CKR_OK) - { - ret = ckcapi_session_sign(sess, data, data_len, signature, signature_len); - ckcapi_session_unref_unlock(sess); - } - - RETURN(ret); -} - -static CK_RV -CC_C_SignUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len) -{ - ENTER(C_SignUpdate); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* RSA/DSA mechs don't support incremental crypto operations. */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_SignFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR signature, - CK_ULONG_PTR signature_len) -{ - ENTER(C_SignFinal); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* RSA/DSA mechs don't support incremental crypto operations. */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_SignRecoverInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, - CK_OBJECT_HANDLE key) -{ - ENTER(C_SignRecoverInit); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* TODO: Implement this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_SignRecover(CK_SESSION_HANDLE session, CK_BYTE_PTR data, CK_ULONG data_len, - CK_BYTE_PTR signature, CK_ULONG_PTR signature_len) -{ - ENTER(C_SignRecover); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* TODO: Implement this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_VerifyInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, - CK_OBJECT_HANDLE key) -{ - ENTER(C_VerifyInit); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* TODO: See if we need to implement this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_Verify(CK_SESSION_HANDLE session, CK_BYTE_PTR data, CK_ULONG data_len, - CK_BYTE_PTR signature, CK_ULONG signature_len) -{ - ENTER(C_Verify); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* TODO: See if we need to implement this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_VerifyUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len) -{ - ENTER(C_VerifyUpdate); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* RSA/DSA mechs don't support incremental crypto operations. */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_VerifyFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR signature, - CK_ULONG signature_len) -{ - ENTER(C_VerifyFinal); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* RSA/DSA mechs don't support incremental crypto operations. */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_VerifyRecoverInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, - CK_OBJECT_HANDLE key) -{ - ENTER(C_VerifyRecoverInit); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* TODO: See if we need to implement this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_VerifyRecover(CK_SESSION_HANDLE session, CK_BYTE_PTR signature, - CK_ULONG signature_len, CK_BYTE_PTR data, CK_ULONG_PTR data_len) -{ - ENTER(C_VerifyRecover); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* TODO: See if we need to implement this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_DigestEncryptUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, - CK_ULONG part_len, CK_BYTE_PTR encrypted_part, - CK_ULONG_PTR encrypted_part_len) -{ - ENTER(C_DigestEncryptUpdate); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* RSA/DSA mechs don't support incremental crypto operations. */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_DecryptDigestUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR encrypted_part, - CK_ULONG encrypted_part_len, CK_BYTE_PTR part, - CK_ULONG_PTR part_len) -{ - ENTER(C_DecryptDigestUpdate); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* RSA/DSA mechs don't support incremental crypto operations. */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_SignEncryptUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, - CK_ULONG part_len, CK_BYTE_PTR encrypted_part, - CK_ULONG_PTR encrypted_part_len) -{ - ENTER(C_SignEncryptUpdate); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* RSA/DSA mechs don't support incremental crypto operations. */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_DecryptVerifyUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR encrypted_part, - CK_ULONG encrypted_part_len, CK_BYTE_PTR part, - CK_ULONG_PTR part_len) -{ - ENTER(C_DecryptVerifyUpdate); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* RSA/DSA mechs don't support incremental crypto operations. */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_GenerateKey(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, - CK_ATTRIBUTE_PTR templ, CK_ULONG count, - CK_OBJECT_HANDLE_PTR key) -{ - ENTER(C_GenerateKey); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* Let key generation happen via Windows interfaces */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_GenerateKeyPair(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, - CK_ATTRIBUTE_PTR public_key_template, CK_ULONG public_key_attribute_count, - CK_ATTRIBUTE_PTR private_key_template, CK_ULONG private_key_attribute_count, - CK_OBJECT_HANDLE_PTR public_key, CK_OBJECT_HANDLE_PTR private_key) -{ - ENTER(C_GenerateKeyPair); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* Let key generation happen via Windows interfaces */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_WrapKey(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, - CK_OBJECT_HANDLE wrapping_key, CK_OBJECT_HANDLE key, - CK_BYTE_PTR wrapped_key, CK_ULONG_PTR wrapped_key_len) -{ - ENTER(C_WrapKey); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* TODO: See if we need to implement this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_UnwrapKey(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, - CK_OBJECT_HANDLE unwrapping_key, CK_BYTE_PTR wrapped_key, - CK_ULONG wrapped_key_len, CK_ATTRIBUTE_PTR templ, - CK_ULONG count, CK_OBJECT_HANDLE_PTR key) -{ - ENTER(C_UnwrapKey); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* TODO: See if we need to implement this */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_DeriveKey(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, - CK_OBJECT_HANDLE base_key, CK_ATTRIBUTE_PTR templ, - CK_ULONG count, CK_OBJECT_HANDLE_PTR key) -{ - ENTER(C_DeriveKey); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* Can't do this with RSA */ - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_SeedRandom(CK_SESSION_HANDLE session, CK_BYTE_PTR seed, CK_ULONG seed_len) -{ - ENTER(C_SeedRandom); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* - * TODO: Perhaps at some point in the future we may want - * to see if we can hook into the Windows RNG - */ - - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_RV -CC_C_GenerateRandom(CK_SESSION_HANDLE session, CK_BYTE_PTR random_data, - CK_ULONG random_len) -{ - ENTER(C_GenerateRandom); - PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); - - /* - * TODO: Perhaps at some point in the future we may want - * to see if we can hook into the Windows RNG - */ - - RETURN(CKR_FUNCTION_NOT_SUPPORTED); -} - -static CK_FUNCTION_LIST functionList = { - { 2, 11 }, /* version */ - CC_C_Initialize, - CC_C_Finalize, - CC_C_GetInfo, - CC_C_GetFunctionList, - CC_C_GetSlotList, - CC_C_GetSlotInfo, - CC_C_GetTokenInfo, - CC_C_GetMechanismList, - CC_C_GetMechanismInfo, - CC_C_InitToken, - CC_C_InitPIN, - CC_C_SetPIN, - CC_C_OpenSession, - CC_C_CloseSession, - CC_C_CloseAllSessions, - CC_C_GetSessionInfo, - CC_C_GetOperationState, - CC_C_SetOperationState, - CC_C_Login, - CC_C_Logout, - CC_C_CreateObject, - CC_C_CopyObject, - CC_C_DestroyObject, - CC_C_GetObjectSize, - CC_C_GetAttributeValue, - CC_C_SetAttributeValue, - CC_C_FindObjectsInit, - CC_C_FindObjects, - CC_C_FindObjectsFinal, - CC_C_EncryptInit, - CC_C_Encrypt, - CC_C_EncryptUpdate, - CC_C_EncryptFinal, - CC_C_DecryptInit, - CC_C_Decrypt, - CC_C_DecryptUpdate, - CC_C_DecryptFinal, - CC_C_DigestInit, - CC_C_Digest, - CC_C_DigestUpdate, - CC_C_DigestKey, - CC_C_DigestFinal, - CC_C_SignInit, - CC_C_Sign, - CC_C_SignUpdate, - CC_C_SignFinal, - CC_C_SignRecoverInit, - CC_C_SignRecover, - CC_C_VerifyInit, - CC_C_Verify, - CC_C_VerifyUpdate, - CC_C_VerifyFinal, - CC_C_VerifyRecoverInit, - CC_C_VerifyRecover, - CC_C_DigestEncryptUpdate, - CC_C_DecryptDigestUpdate, - CC_C_SignEncryptUpdate, - CC_C_DecryptVerifyUpdate, - CC_C_GenerateKey, - CC_C_GenerateKeyPair, - CC_C_WrapKey, - CC_C_UnwrapKey, - CC_C_DeriveKey, - CC_C_SeedRandom, - CC_C_GenerateRandom, - CC_C_GetFunctionStatus, - CC_C_CancelFunction, - CC_C_WaitForSlotEvent -}; - -__declspec(dllexport) CK_RV -C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR list) -{ - if(!list) - return CKR_ARGUMENTS_BAD; - - *list = &functionList; - return CKR_OK; -} diff --git a/ckcapi.dep b/ckcapi.dep deleted file mode 100644 index 0b2cf80..0000000 --- a/ckcapi.dep +++ /dev/null @@ -1,69 +0,0 @@ -# Microsoft Developer Studio Generated Dependency File, included by ckcapi.mak - -".\ckcapi-builtin.c" : \ - "..\..\..\..\program files\microsoft visual studio\vc98\include\basetsd.h"\ - ".\ckcapi-util.h"\ - ".\ckcapi.h"\ - ".\pkcs11\cryptoki.h"\ - ".\pkcs11\pkcs11.h"\ - ".\pkcs11\pkcs11f.h"\ - ".\pkcs11\pkcs11n.h"\ - ".\pkcs11\pkcs11t.h"\ - - -".\ckcapi-cert.c" : \ - "..\..\..\..\program files\microsoft visual studio\vc98\include\basetsd.h"\ - ".\ckcapi-util.h"\ - ".\ckcapi.h"\ - ".\pkcs11\cryptoki.h"\ - ".\pkcs11\pkcs11.h"\ - ".\pkcs11\pkcs11f.h"\ - ".\pkcs11\pkcs11t.h"\ - - -".\ckcapi-object.c" : \ - "..\..\..\..\program files\microsoft visual studio\vc98\include\basetsd.h"\ - ".\ckcapi-util.h"\ - ".\ckcapi.h"\ - ".\pkcs11\cryptoki.h"\ - ".\pkcs11\pkcs11.h"\ - ".\pkcs11\pkcs11f.h"\ - ".\pkcs11\pkcs11n.h"\ - ".\pkcs11\pkcs11t.h"\ - - -".\ckcapi-session.c" : \ - "..\..\..\..\program files\microsoft visual studio\vc98\include\basetsd.h"\ - ".\ckcapi-util.h"\ - ".\ckcapi.h"\ - ".\pkcs11\cryptoki.h"\ - ".\pkcs11\pkcs11.h"\ - ".\pkcs11\pkcs11f.h"\ - ".\pkcs11\pkcs11t.h"\ - - -".\ckcapi-trust.c" : \ - "..\..\..\..\program files\microsoft visual studio\vc98\include\basetsd.h"\ - ".\ckcapi-util.h"\ - ".\ckcapi.h"\ - ".\pkcs11\cryptoki.h"\ - ".\pkcs11\pkcs11.h"\ - ".\pkcs11\pkcs11f.h"\ - ".\pkcs11\pkcs11n.h"\ - ".\pkcs11\pkcs11t.h"\ - ".\x509-usages.h"\ - - -".\ckcapi-util.c" : \ - ".\ckcapi-util.h"\ - - -.\ckcapi.c : \ - "..\..\..\..\program files\microsoft visual studio\vc98\include\basetsd.h"\ - ".\ckcapi-util.h"\ - ".\ckcapi.h"\ - ".\pkcs11\cryptoki.h"\ - ".\pkcs11\pkcs11.h"\ - ".\pkcs11\pkcs11f.h"\ - ".\pkcs11\pkcs11t.h"\ - diff --git a/ckcapi.h b/ckcapi.h deleted file mode 100644 index f174675..0000000 --- a/ckcapi.h +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2007 Stef Walter - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - */ - -#ifndef CKCAPI_H -#define CKCAPI_H - -/* -------------------------------------------------------------------- - * - * Session = CkCapiSession - * - A PKCS#11 Session - * - * Objects = CkCapiObject - * - There's a global list of objects in ckcapi-object.c indexed by - * object handle. - * - The object itself has no attributes or cached data, but knows how - * to load data when needed. - * - Each object has a unique key which guarantees we don't load the - * same object twice with two different object handles. - * - * Object Data = CkCapiObjectData - * - Object Data is owned by the Session - * - Loaded data and/or attributes for an object. - */ - -#define WIN32_LEAN_AND_MEAN -#define _WIN32_WINNT 0x400 -#include -#include - -#define CKCAPI_ENCODINGS (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) - -#define CRYPTOKI_EXPORTS -#include "pkcs11/pkcs11.h" - -#include "ckcapi-util.h" - -struct _CkCapiSlot; -struct _CkCapiObject; -struct _CkCapiObjectData; -struct _CkCapiSession; - -typedef struct _CkCapiSlot CkCapiSlot; -typedef struct _CkCapiObject CkCapiObject; -typedef struct _CkCapiObjectData CkCapiObjectData; -typedef struct _CkCapiSession CkCapiSession; - -/* ------------------------------------------------------------------ - * cryptoki-capi.c - * - * Module helper and logging functions. - */ - -#define DBG(args) \ - ckcapi_debug args - -void ckcapi_debug (const char* msg, ...); - -/* - * Protect global data with these. - */ -void ckcapi_lock_global (void); -void ckcapi_unlock_global (void); - -/* - * Convert a GetLastError() windows error to a - * PKCS#11 return code. - */ -CK_RV ckcapi_winerr_to_ckr (DWORD werr); - -/* - * This stores data in the output buffer with appropriate - * PKCS#11 codes when the buffer is too short, or the caller - * just wants to know the length, etc. - */ -CK_RV ckcapi_return_data (CK_ATTRIBUTE_PTR attr, - CK_VOID_PTR src, DWORD slen); - -CK_RV ckcapi_return_data_raw (CK_VOID_PTR output, CK_ULONG_PTR n_output, - CK_VOID_PTR input, CK_ULONG n_input); - -/* - * This stores a string in the output buffer with appropriate - * PKCS#11 codes when the buffer is too short, or the caller - * just wants to know the length, etc. - */ -CK_RV ckcapi_return_string (CK_ATTRIBUTE_PTR attr, - WCHAR* string); - -CK_RV ckcapi_return_dword_as_bytes (CK_ATTRIBUTE_PTR attr, - DWORD value); - -CK_RV ckcapi_return_reversed_data (CK_ATTRIBUTE_PTR attr, - CK_VOID_PTR data, CK_ULONG length); - -CK_RV ckcapi_return_filetime (CK_ATTRIBUTE_PTR attr, - FILETIME* ftime); - -/* ------------------------------------------------------------------ */ - -typedef void (*CkCapiDestroyFunc)(void* data); - -#ifndef ASSERT -#include "assert.h" -#define ASSERT assert -#endif - -/* Represents 'any' class in searches */ -#define CKO_ANY CK_INVALID_HANDLE - - -#endif /* CRYPTOKI_CAPI_H */ diff --git a/ckcapi.vcproj b/ckcapi.vcproj deleted file mode 100644 index 01ef822..0000000 --- a/ckcapi.vcproj +++ /dev/null @@ -1,428 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/p11-capi-builtin.c b/p11-capi-builtin.c new file mode 100644 index 0000000..2757cb3 --- /dev/null +++ b/p11-capi-builtin.c @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2007 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "p11-capi.h" +#include "p11-capi-object.h" +#include "p11-capi-session.h" +#include "p11-capi-token.h" + +#include "pkcs11/pkcs11n.h" + +/* -------------------------------------------------------------------------- + * BUILT IN VALUES + */ + +static const CK_BBOOL ck_true = CK_TRUE; +static const CK_BBOOL ck_false = CK_FALSE; + +static const CK_OBJECT_CLASS cko_netscape_builtin_root_list = CKO_NETSCAPE_BUILTIN_ROOT_LIST; + +static const char ck_root_label[] = "Windows Certificate Roots"; + +/* -------------------------------------------------------------------------- + * BUILT IN OBJECTS + */ + +#define CK_END_LIST (CK_ULONG)-1 + +static const CK_ATTRIBUTE builtin_root[] = { + { CKA_TOKEN, (void*)&ck_true, sizeof(CK_BBOOL) }, + { CKA_CLASS, (void*)&cko_netscape_builtin_root_list, sizeof(CK_OBJECT_CLASS) }, + { CKA_PRIVATE, (void*)&ck_false, sizeof(CK_BBOOL) }, + { CKA_MODIFIABLE, (void*)&ck_false, sizeof(CK_BBOOL) }, + { CKA_LABEL, (void*)ck_root_label, sizeof(ck_root_label) }, + { CK_END_LIST, NULL, 0 } +}; + +typedef struct _BuiltinMatch +{ + CK_ATTRIBUTE_PTR attr; + CK_ULONG slot_flags; +} +BuiltinMatch; + +static const BuiltinMatch all_builtins[] = { + { (CK_ATTRIBUTE_PTR)&builtin_root, P11c_SLOT_TRUSTED | P11c_SLOT_CA | P11c_SLOT_CERTS }, + { NULL, 0 } +}; + +/* This is filled in later */ +static CK_ULONG num_builtins = 0; + +/* -------------------------------------------------------------------------- + * IMPLEMENTATION + */ + +/* Represents a loaded builtin object */ +typedef struct _BuiltinObject +{ + P11cObject obj; + CK_ATTRIBUTE_PTR attr; +} +BuiltinObject; + +typedef struct _BuiltinObjectData +{ + P11cObjectData base; + CK_ATTRIBUTE_PTR attr; +} +BuiltinObjectData; + +static CK_RV +builtin_attribute(P11cObjectData* objdata, CK_ATTRIBUTE_PTR attr) +{ + BuiltinObjectData* bdata = (BuiltinObjectData*)objdata; + CK_ATTRIBUTE_PTR builtin = bdata->attr; + + ASSERT(attr); + ASSERT(bdata); + + while(builtin->type != CK_END_LIST) + { + if(builtin->type == attr->type) + { + if(builtin->ulValueLen == 0) + return CKR_ATTRIBUTE_TYPE_INVALID; + return p11c_return_data(attr, builtin->pValue, builtin->ulValueLen); + } + + builtin++; + } + + return CKR_ATTRIBUTE_TYPE_INVALID; +} + +static void +builtin_data_release(void* data) +{ + BuiltinObjectData* bdata = (BuiltinObjectData*)data; + ASSERT(bdata); + free(bdata); +} + +static const P11cObjectDataVtable builtin_objdata_vtable = { + builtin_attribute, + builtin_attribute, + builtin_attribute, + builtin_data_release, +}; + +static CK_RV +builtin_load_data(P11cSession* sess, P11cObject* obj, P11cObjectData** objdata) +{ + BuiltinObject* bobj = (BuiltinObject*)obj; + BuiltinObjectData* bdata; + + ASSERT(bobj); + ASSERT(objdata); + ASSERT(num_builtins > 0); + + bdata = (BuiltinObjectData*)calloc(1, sizeof(BuiltinObjectData)); + if(!bdata) + return CKR_HOST_MEMORY; + + /* Simple, just use same data */ + bdata->attr = bobj->attr; + + bdata->base.object = obj->id; + bdata->base.data_funcs = &builtin_objdata_vtable; + + *objdata = &(bdata->base); + return CKR_OK; +} + +static unsigned int +builtin_hash_func(P11cObject* obj) +{ + return p11c_hash_pointer(((BuiltinObject*)obj)->attr); +} + +static int +builtin_equal_func(P11cObject* one, P11cObject* two) +{ + return ((BuiltinObject*)one)->attr == ((BuiltinObject*)two)->attr; +} + +static void +builtin_object_release(void* data) +{ + BuiltinObject* bobj = (BuiltinObject*)data; + ASSERT(bobj); + free(bobj); +} + +static const P11cObjectVtable builtin_object_vtable = { + builtin_load_data, + builtin_hash_func, + builtin_equal_func, + builtin_object_release, +}; + +static CK_RV +register_builtin_object(P11cSession* sess, CK_ATTRIBUTE_PTR attr, P11cObject** obj) +{ + BuiltinObject* bobj; + CK_RV ret; + + bobj = calloc(1, sizeof(BuiltinObject)); + if(!bobj) + return CKR_HOST_MEMORY; + + bobj->attr = attr; + + bobj->obj.id = 0; + bobj->obj.obj_funcs = &builtin_object_vtable; + + ret = p11c_token_register_object(sess->slot, &(bobj->obj)); + if(ret != CKR_OK) + { + free(bobj); + return ret; + } + + ASSERT(bobj->obj.id != 0); + *obj = &(bobj->obj); + return CKR_OK; +} + +CK_RV +p11c_builtin_find(P11cSession* sess, CK_OBJECT_CLASS cls, CK_ATTRIBUTE_PTR match, + CK_ULONG count, P11cArray* arr) +{ + P11cObject* obj; + BuiltinObjectData bdata; + CK_RV ret = CKR_OK; + CK_ULONG i, fl; + + /* First time around count total number */ + if(!num_builtins) + { + while(all_builtins[num_builtins].attr) + ++num_builtins; + ASSERT(num_builtins > 0); + } + + /* Match each certificate */ + for(i = 0; i < num_builtins; ++i) + { + /* Only apply built in objects to appropriate slots */ + fl = p11c_token_get_flags(sess->slot) & all_builtins[i].slot_flags; + if(fl != all_builtins[i].slot_flags) + continue; + + bdata.attr = all_builtins[i].attr; + bdata.base.object = 0; + bdata.base.data_funcs = &builtin_objdata_vtable; + + if(p11c_object_data_match(&bdata.base, match, count)) + { + ret = register_builtin_object(sess, all_builtins[i].attr, &obj); + if(ret != CKR_OK) + break; + + p11c_array_append(arr, obj->id); + } + } + + return ret; +} + diff --git a/p11-capi-builtin.h b/p11-capi-builtin.h new file mode 100644 index 0000000..b398509 --- /dev/null +++ b/p11-capi-builtin.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2007 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef P11C_BUILTIN_H +#define P11C_BUILTIN_H + +#include "p11-capi.h" + +/* Find builtin objects matching criteria */ +CK_RV p11c_builtin_find (P11cSession* sess, CK_OBJECT_CLASS cls, + CK_ATTRIBUTE_PTR match, CK_ULONG count, + P11cArray* arr); + +#endif /* P11C_BUILTIN_H */ diff --git a/p11-capi-cert.c b/p11-capi-cert.c new file mode 100644 index 0000000..960073a --- /dev/null +++ b/p11-capi-cert.c @@ -0,0 +1,775 @@ +/* + * Copyright (C) 2007 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "p11-capi.h" +#include "p11-capi-cert.h" +#include "p11-capi-object.h" +#include "p11-capi-session.h" +#include "p11-capi-token.h" + +#include + +#ifndef CERT_FIND_KEY_IDENTIFIER +#define CERT_FIND_KEY_IDENTIFIER 983040 +#endif + +#ifndef CERT_KEY_IDENTIFIER_PROP_ID +#define CERT_KEY_IDENTIFIER_PROP_ID 20 +#endif + +typedef struct _CertObject +{ + P11cObject obj; + + /* Together these can uniquely identify a certificate */ + CRYPT_INTEGER_BLOB serial; + CERT_NAME_BLOB issuer; +} +CertObject; + +typedef struct _CertObjectData +{ + P11cObjectData base; + PCCERT_CONTEXT cert; + BOOL is_in_root; +} +CertObjectData; + +static CK_RV +parse_basic_constraints(CertObjectData* cdata, CK_ULONG* category) +{ + CERT_BASIC_CONSTRAINTS_INFO* basic; + CERT_EXTENSION* ext; + DWORD size; + BYTE bits; + CK_RV ret; + + ASSERT(cdata); + ASSERT(cdata->cert); + + *category = 0; + + ext = CertFindExtension(szOID_BASIC_CONSTRAINTS, + cdata->cert->pCertInfo->cExtension, + cdata->cert->pCertInfo->rgExtension); + + /* No key usage, don't care */ + if(!ext) + return CKR_OK; + + /* Find the size of the decoded structure */ + if(!CryptDecodeObject(P11c_ENCODINGS, X509_BASIC_CONSTRAINTS, + ext->Value.pbData, ext->Value.cbData, 0, NULL, &size)) + return p11c_winerr_to_ckr(GetLastError()); + + /* Allocate enough memory */ + basic = (CERT_BASIC_CONSTRAINTS_INFO*)calloc(1, size); + if(!basic) + return CKR_HOST_MEMORY; + + /* And get the decoded structure */ + if(CryptDecodeObject(P11c_ENCODINGS, X509_BASIC_CONSTRAINTS, + ext->Value.pbData, ext->Value.cbData, 0, basic, &size)) + { + if(basic->SubjectType.cbData != 1) + { + DBG(("basic constraints bits are of invalid size")); + ret = CKR_GENERAL_ERROR; + } + else + { + /* All of the above was for 2 bits. Lovely */ + bits = basic->SubjectType.pbData[0] & ~(0xff >> (8 - basic->SubjectType.cUnusedBits)); + if((bits & CERT_CA_SUBJECT_FLAG) == CERT_CA_SUBJECT_FLAG) + *category = 2; + else if((bits & CERT_END_ENTITY_SUBJECT_FLAG) == CERT_END_ENTITY_SUBJECT_FLAG) + *category = 3; + else + *category = 0; + ret = CKR_OK; + } + } + else + { + ret = p11c_winerr_to_ckr(GetLastError()); + } + + free(basic); + + return ret; +} + + +static CK_RV +cert_bool_attribute(P11cObjectData* objdata, CK_ATTRIBUTE_PTR attr) +{ + CertObjectData* cdata = (CertObjectData*)objdata; + CK_BBOOL val; + + ASSERT(cdata); + + switch(attr->type) + { + /* + * Resides on the token + * - Always true for CAPI objects. + */ + case CKA_TOKEN: + val = CK_TRUE; + break; + + /* + * Private vs. Public object. + * - Always false for certificates. + */ + case CKA_PRIVATE: + val = CK_FALSE; + break; + + /* + * If object can be modified. + * - Currently always false. In the future with additional + * functionality this may change. + */ + case CKA_MODIFIABLE: + val = CK_FALSE; + break; + + /* + * Whether the certificate can be trusted for the application + * in which it was created. + * - We just report on whether the certificate is a trusted root. + */ + case CKA_TRUSTED: + val = cdata->is_in_root ? CK_TRUE : CK_FALSE; + break; + + default: + return CKR_ATTRIBUTE_TYPE_INVALID; + }; + + return p11c_return_data(attr, &val, sizeof(CK_BBOOL)); +} + +static CK_RV +cert_ulong_attribute(P11cObjectData* objdata, CK_ATTRIBUTE_PTR attr) +{ + CertObjectData* cdata = (CertObjectData*)objdata; + CK_ULONG val; + CK_RV ret; + + ASSERT(objdata); + + switch(attr->type) + { + + /* + * Object class. + * - Always CKO_CERTIFICATE for certificates. + */ + case CKA_CLASS: + val = CKO_CERTIFICATE; + break; + + /* + * Type of certificate. + * - Always X509. + */ + case CKA_CERTIFICATE_TYPE: + val = CKC_X_509; + break; + + /* + * Whether a CA, user certificate, other. + * - Get certificate szOID_ENHANCED_KEY_USAGE + * extension or CERT_CTL_PROP_ID and look into CTL_USAGE structure. + */ + case CKA_CERTIFICATE_CATEGORY: + ret = parse_basic_constraints(cdata, &val); + if(ret != CKR_OK) + return ret; + break; + + /* + * Java MIDP security domain. + * - Have no idea what this is. Spec says default to zero. + */ + case CKA_JAVA_MIDP_SECURITY_DOMAIN: + val = 0; + break; + + default: + return CKR_ATTRIBUTE_TYPE_INVALID; + }; + + return p11c_return_data(attr, &val, sizeof(CK_ULONG)); +} + +static CK_RV +cert_bytes_attribute(P11cObjectData* objdata, CK_ATTRIBUTE_PTR attr) +{ + CertObjectData* cdata = (CertObjectData*)objdata; + PCCERT_CONTEXT cert = cdata->cert; + + ASSERT(sizeof(CK_ULONG) == sizeof(DWORD)); + ASSERT(cdata); + + return p11c_cert_certificate_get_bytes(cdata->cert, attr); +} + +static void +cert_data_release(void* data) +{ + CertObjectData* cdata = (CertObjectData*)data; + ASSERT(cdata && cdata->cert); + CertFreeCertificateContext(cdata->cert); + free(cdata); +} + +static const P11cObjectDataVtable cert_objdata_vtable = { + cert_bool_attribute, + cert_ulong_attribute, + cert_bytes_attribute, + cert_data_release, +}; + +static P11cObjectData* +cert_alloc_data(P11cSession* sess, P11cObject* obj, PCCERT_CONTEXT cert) +{ + CertObjectData* cdata; + + cdata = (CertObjectData*)calloc(1, sizeof(CertObjectData)); + if(!cdata) + return NULL; + + cdata->cert = cert; + cdata->is_in_root = (p11c_token_get_flags(sess->slot) & P11c_SLOT_CA) ? TRUE : FALSE; + + cdata->base.object = obj->id; + cdata->base.data_funcs = &cert_objdata_vtable; + + return &(cdata->base); +} + +static CK_RV +cert_load_data(P11cSession* sess, P11cObject* obj, P11cObjectData** objdata) +{ + CertObject* cobj = (CertObject*)obj; + CERT_INFO info; + PCCERT_CONTEXT cert; + + ASSERT(cobj); + ASSERT(objdata); + + ASSERT(cobj->issuer.pbData); + ASSERT(cobj->issuer.cbData); + ASSERT(cobj->serial.pbData); + ASSERT(cobj->serial.cbData); + + /* No store should mean no objects were loaded */ + ASSERT(sess->store); + + /* Setup our search */ + memset(&info, 0, sizeof(info)); + memcpy(&info.SerialNumber, &cobj->serial, sizeof(info.SerialNumber)); + memcpy(&info.Issuer, &cobj->issuer, sizeof(info.Issuer)); + + cert = CertGetSubjectCertificateFromStore(sess->store, P11c_ENCODINGS, &info); + + if(!cert) + { + DWORD err = GetLastError(); + + /* TODO: Is this right for a deleted certificate? */ + ASSERT(err != E_INVALIDARG); + if(err == CRYPT_E_NOT_FOUND) + return CKR_OBJECT_HANDLE_INVALID; + else + return p11c_winerr_to_ckr(GetLastError()); + } + + *objdata = cert_alloc_data(sess, obj, cert); + if(!(*objdata)) + { + CertFreeCertificateContext(cert); + return CKR_HOST_MEMORY; + } + + return CKR_OK; +} + +static unsigned int +cert_hash_func(P11cObject* obj) +{ + CertObject* cobj = (CertObject*)obj; + return p11c_hash_data(cobj->issuer.pbData, cobj->issuer.cbData) ^ + p11c_hash_data(cobj->serial.pbData, cobj->serial.cbData); +} + +static int +cert_equal_func(P11cObject* a, P11cObject* b) +{ + CertObject* ca = (CertObject*)a; + CertObject* cb = (CertObject*)b; + return ca->issuer.cbData == cb->issuer.cbData && + memcmp(ca->issuer.pbData, cb->issuer.pbData, ca->issuer.cbData) == 0 && + ca->serial.cbData == cb->serial.cbData && + memcmp(ca->serial.pbData, cb->serial.pbData, ca->serial.cbData) == 0; +} + +static void +cert_object_release(void* data) +{ + CertObject* cobj = (CertObject*)data; + ASSERT(cobj); + free(cobj); +} + +static const P11cObjectVtable cert_object_vtable = { + cert_load_data, + cert_hash_func, + cert_equal_func, + cert_object_release, +}; + +static CK_RV +calculate_check_value(PCCERT_CONTEXT cert, CK_ATTRIBUTE_PTR attr) +{ + BYTE* buffer; + DWORD length; + CK_RV ret; + + ASSERT(cert); + ASSERT(attr); + + /* Short cut for the measuring case */ + if(!attr->pValue) + { + attr->ulValueLen = 3; + return CKR_OK; + } + + length = 0; + if(!CryptHashCertificate(0, CALG_SHA1, 0, cert->pbCertEncoded, + cert->cbCertEncoded, NULL, &length)) + return p11c_winerr_to_ckr(GetLastError()); + + if(length < 3) + { + DBG(("SHA1 hash length too short: %d", length)); + return CKR_DEVICE_ERROR; + } + + buffer = malloc(length); + if(!buffer) + return CKR_HOST_MEMORY; + + if(!CryptHashCertificate(0, CALG_SHA1, 0, cert->pbCertEncoded, + cert->cbCertEncoded, buffer, &length)) + { + free(buffer); + return p11c_winerr_to_ckr(GetLastError()); + } + + ret = p11c_return_data(attr, buffer, 3); + free(buffer); + return ret; +} + + +CK_RV +p11c_cert_certificate_get_bytes(PCCERT_CONTEXT cert, CK_ATTRIBUTE_PTR attr) +{ + DWORD err; + + ASSERT(cert); + ASSERT(attr); + + switch(attr->type) + { + + /* + * Description of the object. + * - We use CAPI's CERT_FRIENDLY_NAME_PROP_ID property, + * converted into UTF8. + * - Yes this is slow, but this is not really a property + * that's searched on or retrieved intensively. + */ + case CKA_LABEL: + { + WCHAR* utf16 = NULL; + DWORD size; + + if(!CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, NULL, &size)) + { + err = GetLastError(); + if(err == CRYPT_E_NOT_FOUND) + utf16 = L"Unnamed Certificate"; + else + return p11c_winerr_to_ckr(err); + } + + if(!utf16) + { + utf16 = _alloca(size); + if(!CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, utf16, &size)) + return p11c_winerr_to_ckr(GetLastError()); + } + + return p11c_return_string(attr, utf16); + } + break; + + /* + * A byte array unique to this certificate. The CKA_ID of + * matching certificates and private keys should match. + * Should match the key identifier in an X.509v3 certificate. + * + * We use CAPI's CERT_KEY_IDENTIFIER_PROP_ID property directly. + */ + case CKA_ID: + if(!CertGetCertificateContextProperty(cert, CERT_KEY_IDENTIFIER_PROP_ID, + attr->pValue, (DWORD*)&attr->ulValueLen)) + { + err = GetLastError(); + if(err == CRYPT_E_NOT_FOUND) + return CKR_ATTRIBUTE_TYPE_INVALID; + return p11c_winerr_to_ckr(err); + } + return CKR_OK; + + + /* + * DER-encoding of the certificate subject name. + * + * We use CAPI's CERT_CONTEXT pCertInfo->Subject field + * directly. + */ + case CKA_SUBJECT: + return p11c_return_data(attr, cert->pCertInfo->Subject.pbData, + cert->pCertInfo->Subject.cbData); + + /* + * DER-encoding of the certificate issuer name. + * + * We use CAPI's CERT_CONTEXT pCertInfo->Issuer field + * directly. + */ + case CKA_ISSUER: + return p11c_return_data(attr, cert->pCertInfo->Issuer.pbData, + cert->pCertInfo->Issuer.cbData); + + /* + * DER-encoding of the certificate serial number. + */ + case CKA_SERIAL_NUMBER: + if(!CryptEncodeObject(X509_ASN_ENCODING, X509_MULTI_BYTE_INTEGER, + &cert->pCertInfo->SerialNumber, + attr->pValue, (DWORD*)&attr->ulValueLen)) + { + err = GetLastError(); + if(err == ERROR_FILE_NOT_FOUND) + return CKR_GENERAL_ERROR; + return p11c_winerr_to_ckr(err); + } + return CKR_OK; + + /* + * BER-encoding of the full certificate. + * + * We use CAPI's CERT_CONTEXT pbCertEncoded field directly. + */ + case CKA_VALUE: + return p11c_return_data(attr, cert->pbCertEncoded, + cert->cbCertEncoded); + + /* + * If CKA_VALUE not specified, this is where the full + * certificate can be found. + * + * We don't support this. All our certificates are present + * in full. + * + * - Spec says default to empty. + */ + case CKA_URL: + return p11c_return_data(attr, "", 0); + + /* + * Checksum + * - This is the first 3 bytes of the SHA hash of the DER. + */ + case CKA_CHECK_VALUE: + return calculate_check_value(cert, attr); + + /* + * Various hashes for remote retrieval. + * - Spec says default to empty. + */ + case CKA_HASH_OF_SUBJECT_PUBLIC_KEY: + case CKA_HASH_OF_ISSUER_PUBLIC_KEY: + return p11c_return_data(attr, "", 0); + + /* + * Start date for the certificate. + */ + case CKA_START_DATE: + return p11c_return_filetime(attr, &cert->pCertInfo->NotBefore); + + /* + * End date for the certificate. + */ + case CKA_END_DATE: + return p11c_return_filetime(attr, &cert->pCertInfo->NotAfter); + + default: + return CKR_ATTRIBUTE_TYPE_INVALID; + }; +} + +PCCERT_CONTEXT +p11c_cert_object_data_get_certificate(P11cObjectData* objdata) +{ + CertObjectData* cdata; + + ASSERT(objdata); + ASSERT(objdata->data_funcs == &cert_objdata_vtable); + + cdata = (CertObjectData*)objdata; + return cdata->cert; +} + +static CK_RV +register_cert_object(P11cSession* sess, PCCERT_CONTEXT cert, P11cObject** obj) +{ + CertObject* cobj; + CK_RV ret; + size_t len; + + /* We save the Issuer and SerialNumber for identification later */ + len = cert->pCertInfo->SerialNumber.cbData + + cert->pCertInfo->Issuer.cbData; + + cobj = calloc(1, sizeof(CertObject) + len); + if(!cobj) + return CKR_HOST_MEMORY; + + cobj->obj.id = 0; + cobj->obj.obj_funcs = &cert_object_vtable; + + /* Copy Issuer data in */ + cobj->issuer.cbData = cert->pCertInfo->Issuer.cbData; + cobj->issuer.pbData = (BYTE*)(cobj + 1); + memcpy(cobj->issuer.pbData, cert->pCertInfo->Issuer.pbData, + cobj->issuer.cbData); + + /* Copy Serial Number data in */ + cobj->serial.cbData = cert->pCertInfo->SerialNumber.cbData; + cobj->serial.pbData = cobj->issuer.pbData + cobj->issuer.cbData; + memcpy(cobj->serial.pbData, cert->pCertInfo->SerialNumber.pbData, + cobj->serial.cbData); + + ret = p11c_token_register_object(sess->slot, &(cobj->obj)); + if(ret != CKR_OK) + { + free(cobj); + return ret; + } + + ASSERT(cobj->obj.id != 0); + *obj = &cobj->obj; + return CKR_OK; +} + +static CK_RV +find_in_store(P11cSession* sess, DWORD find_type, const void *find_criteria, + CK_ATTRIBUTE_PTR match, CK_ULONG count, P11cArray* arr) +{ + PCCERT_CONTEXT cert = NULL; + P11cObject* obj; + P11cObjectData* objdata; + CertObjectData cdata; + DWORD err; + CK_RV ret = CKR_OK; + + /* No store, no objects */ + if(!sess->store) + return CKR_OK; + + for(;;) + { + cert = CertFindCertificateInStore(sess->store, P11c_ENCODINGS, 0, + find_type, find_criteria, cert); + if(cert == NULL) + { + err = GetLastError(); + + /* Certificate not found, we don't care */ + if(err == CRYPT_E_NOT_FOUND) + return CKR_OK; + else + return p11c_winerr_to_ckr(err); + } + + /* Match the certificate */ + cdata.cert = cert; + cdata.base.object = 0; + cdata.base.data_funcs = &cert_objdata_vtable; + + if(p11c_object_data_match(&cdata.base, match, count)) + { + ret = register_cert_object(sess, cert, &obj); + if(ret == CKR_OK) + { + ASSERT(obj); + + /* Store away the object data for performance reasons */ + objdata = cert_alloc_data(sess, obj, cert); + if(objdata) + { + p11c_session_take_object_data(sess, obj, objdata); + + /* For continuing the enumeration */ + cert = CertDuplicateCertificateContext(cert); + } + + p11c_array_append(arr, obj->id); + } + } + } + + if(ret != CKR_OK && cert) + CertFreeCertificateContext(cert); + + return ret; +} + +CK_RV +p11c_cert_find(P11cSession* sess, CK_OBJECT_CLASS cls, CK_ATTRIBUTE_PTR match, + CK_ULONG count, P11cArray* arr) +{ + CRYPT_INTEGER_BLOB* serial = NULL; + CK_RV ret; + CK_ULONG i; + DWORD size; + + CERT_INFO find_info; /* For searching by issuer and serial */ + CRYPT_HASH_BLOB find_key; /* For searching by ID */ + + /* We only have certificates here */ + if(cls != CKO_CERTIFICATE && cls != CKO_ANY) + return CKR_OK; + + /* Only work with slots that have certificates */ + if(!(p11c_token_get_flags (sess->slot) & P11c_SLOT_CERTS)) + return CKR_OK; + + /* + * There are some better searches we can do rather than + * listing everything. + * + * CKA_ISSUER + CKA_SERIAL_NUMBER + * See if we have a issuer and serial number for a + * specific certificate to find. + * + * CKA_ID + * Search by key identifier + * + * TODO: could search by hash (use CertFindCertificateInStore + * with CERT_FIND_HASH or CERT_FIND_SHA1_HASH or CERT_FIND_MD5_HASH) + * + * TODO: could search by issuer (use CertFindCertificateInStore + * with CERT_FIND_ISSUER_NAME) + * + * TODO: could search by subject (use CertFindCertificateInStore + * with CERT_FIND_SUBJECT_NAME) + * + * TODO: could search by CKA_VALUE (use CertFindCertificateInStore + * with CERT_FIND_EXISTING) + */ + memset(&find_info, 0, sizeof(find_info)); + memset(&find_key, 0, sizeof(find_key)); + + for(i = 0; i < count; ++i) + { + if(!match[i].pValue || !match[i].ulValueLen) + continue; + + if(match[i].type == CKA_ISSUER) + { + find_info.Issuer.cbData = match[i].ulValueLen; + find_info.Issuer.pbData = match[i].pValue; + } + + else if(match[i].type == CKA_SERIAL_NUMBER && !serial) + { + if(!CryptDecodeObject(P11c_ENCODINGS, X509_MULTI_BYTE_INTEGER, + match[i].pValue, match[i].ulValueLen, 0, NULL, &size)) + { + continue; + } + + serial = calloc(1, size); + if(!serial) + continue; + + if(!CryptDecodeObject(P11c_ENCODINGS, X509_MULTI_BYTE_INTEGER, + match[i].pValue, match[i].ulValueLen, 0, serial, &size)) + continue; + + ASSERT(serial->cbData); + ASSERT(serial->pbData); + + find_info.SerialNumber.cbData = serial->cbData; + find_info.SerialNumber.pbData = serial->pbData; + } + + else if(match[i].type == CKA_ID) + { + find_key.cbData = match[i].ulValueLen; + find_key.pbData = match[i].pValue; + } + } + + /* Match a specific certificate */ + if(find_info.SerialNumber.cbData && find_info.Issuer.cbData) + { + ret = find_in_store(sess, CERT_FIND_SUBJECT_CERT, &find_info, + match, count, arr); + } + + /* Find all certificates with key identifier */ + else if(find_key.cbData) + { + ret = find_in_store(sess, CERT_FIND_KEY_IDENTIFIER, &find_key, + match, count, arr); + } + + /* Match any ol certificate */ + else + { + ret = find_in_store(sess, CERT_FIND_ANY, NULL, + match, count, arr); + } + + if(serial) + free(serial); + + return ret; +} diff --git a/p11-capi-cert.h b/p11-capi-cert.h new file mode 100644 index 0000000..a19b740 --- /dev/null +++ b/p11-capi-cert.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2007 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef P11C_CERT_H +#define P11C_CERT_H + +#include "p11-capi.h" +#include "p11-capi-util.h" + +/* Find certificates matching criteria */ +CK_R p11c_cert_find (P11cSession* sess, CK_OBJECT_CLASS cls, + CK_ATTRIBUTE_PTR match, CK_ULONG count, + P11cArray* arr); + +/* Called by trust and key stuff */ +CK_RV p11c_cert_certificate_get_bytes (PCCERT_CONTEXT cert, + CK_ATTRIBUTE_PTR attr); + +PCCERT_CONTEXT p11c_cert_object_data_get_certificate (P11cObjectData* objdata); + +#endif /* P11C_CERT_H */ diff --git a/p11-capi-der.c b/p11-capi-der.c new file mode 100644 index 0000000..db69b89 --- /dev/null +++ b/p11-capi-der.c @@ -0,0 +1,177 @@ +/* + * Portions derived from NSS source files: + * lib/ckfw/capi/cobject.c + * lib/ckfw/capi/crsa.c + * + * Portions of this file: + * Copyright (C) Stef Walter 2008 + * + */ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1994-2000 + * the Initial Developer. All Rights Reserved. + * Portions created by Red Hat, Inc, are Copyright (C) 2005 + * + * Contributor(s): + * Bob Relyea (rrelyea@redhat.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "p11-capi.h" +#include "p11-capi-der.h" + +/* + * unwrap a single DER value + */ +BYTE* +p11c_der_unwrap(BYTE* src, DWORD n_src, + DWORD* n_result, BYTE** next) +{ + BYTE* start = src; + BYTE* end = src + n_src; + DWORD len = 0; + + /* initialize error condition return values */ + *n_result = 0; + if(next) + *next = src; + + if(n_src < 2) + return start; + + src++ ; /* skip the tag -- should check it against an expected value! */ + len = (DWORD)*src++; + if(len & 0x80) + { + DWORD count = len & 0x7f; + len = 0; + + if(count + 2 > n_src) + return start; + + while(count-- > 0) + len = (len << 8) | (DWORD)*src++; + } + + if(len + (src - start) > (DWORD)n_src) + return start; + + if(next) + *next = src + len; + + *n_result = len; + return src; +} + +/* + * write a Decimal value to a string + */ + +static char* +put_decimal_string(char* cstr, DWORD value) +{ + DWORD tenpower; + BOOL first = TRUE; + + for(tenpower = 10000000; tenpower; tenpower /= 10) + { + BYTE digit = (BYTE)(value / tenpower); + value = value % tenpower; + + /* drop leading zeros */ + if(first && (0 == digit)) + continue; + + first = FALSE; + *cstr++ = digit + '0'; + } + + /* if value was zero, put one of them out */ + if(first) + *cstr++ = '0'; + + return cstr; +} + +/* + * Create a Capi OID string value from a DER OID + */ +char* +p11c_der_read_oid(BYTE* oid_tag, DWORD n_oid_tag) +{ + BYTE* oid; + char *oid_str; + char *cstr; + DWORD value; + DWORD n_oid; + + /* wasn't an oid */ + if(P11c_DER_OBJECT_ID != *oid_tag) + return NULL; + + oid = p11c_der_unwrap(oid_tag, n_oid_tag, &n_oid, NULL);; + if(n_oid < 2) + return NULL; + + oid_str = malloc(n_oid * 4); + if(!oid_str) + return NULL; + + cstr = oid_str; + cstr = put_decimal_string(cstr, (*oid) / 40); + *cstr++ = '.'; + cstr = put_decimal_string(cstr, (*oid) % 40); + n_oid--; + + value = 0; + while(n_oid--) + { + oid++; + value = (value << 7) + (*oid & 0x7f); + if(0 == (*oid & 0x80)) + { + *cstr++ = '.'; + cstr = put_decimal_string(cstr, value); + value = 0; + } + } + + *cstr = 0; /* NULL terminate */ + + if(value != 0) + { + free(oid_str); + return NULL; + } + + return oid_str; +} diff --git a/p11-capi-der.h b/p11-capi-der.h new file mode 100644 index 0000000..0807fcd --- /dev/null +++ b/p11-capi-der.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2008 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef P11C_DER_H +#define P11C_DER_H + +#include "p11-capi.h" + +#define P11C_DER_OCTET_STRING 0x04 +#define P11C_DER_OBJECT_ID 0x06 +#define P11C_DER_SEQUENCE 0x10 +#define P11C_DER_CONSTRUCTED 0x20 + +BYTE* p11c_der_unwrap (BYTE* src, DWORD n_src, + DWORD* n_result, BYTE** next); + +char* p11c_der_read_oid (BYTE* oid_tag, DWORD n_oid_tag); + +#endif /* P11C_DER_H */ diff --git a/p11-capi-key.c b/p11-capi-key.c new file mode 100644 index 0000000..bda2baf --- /dev/null +++ b/p11-capi-key.c @@ -0,0 +1,1083 @@ +/* + * Copyright (C) 2008 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "p11-capi.h" +#include "p11-capi-cert.h" +#include "p11-capi-key.h" +#include "p11-capi-object.h" +#include "p11-capi-session.h" +#include "p11-capi-token.h" +#include "x509-usages.h" + +typedef struct _KeyObject +{ + P11cObject obj; + + /* The raw key identifier */ + CRYPT_HASH_BLOB key_identifier; + CK_OBJECT_CLASS object_class; +} +KeyObject; + +typedef struct _KeyObjectData +{ + P11cObjectData base; + CK_OBJECT_CLASS object_class; + CRYPT_INTEGER_BLOB key_identifier; + CRYPT_DATA_BLOB raw_public_key; + CRYPT_KEY_PROV_INFO* prov_info; +} +KeyObjectData; + +static CK_RV +load_key_handle(P11cObjectData* objdata, HCRYPTPROV* ret_prov, + HCRYPTKEY* ret_key) +{ + KeyObjectData* kdata = (KeyObjectData*)objdata; + HCRYPTPROV prov; + HCRYPTKEY key; + DWORD error; + + ASSERT(kdata); + ASSERT(ret_key); + ASSERT(ret_prov); + + if(!CryptAcquireContextW(&prov, kdata->prov_info->pwszContainerName, + kdata->prov_info->pwszProvName, + kdata->prov_info->dwProvType, 0)) + { + return p11c_winerr_to_ckr(GetLastError()); + } + + if(!CryptGetUserKey(prov, kdata->prov_info->dwKeySpec, &key)) + { + error = GetLastError(); + CryptReleaseContext(prov, 0); + return p11c_winerr_to_ckr(error); + } + + *ret_key = key; + *ret_prov = prov; + + return CKR_OK; +} + + +static CK_RV +load_raw_public_key(KeyObjectData* kdata) +{ + BOOL success = FALSE; + HCRYPTPROV prov; + HCRYPTKEY key; + CK_RV ret; + DWORD error; + + ASSERT(kdata); + ASSERT(!kdata->raw_public_key.pbData); + + ret = load_key_handle(&kdata->base, &prov, &key); + if(ret != CKR_OK) + return ret; + + if(CryptExportKey(key, 0, PUBLICKEYBLOB, 0, NULL, &kdata->raw_public_key.cbData)) + { + kdata->raw_public_key.pbData = malloc(kdata->raw_public_key.cbData); + if(!kdata->raw_public_key.pbData) + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + else + { + if(CryptExportKey(key, 0, PUBLICKEYBLOB, 0, kdata->raw_public_key.pbData, + &kdata->raw_public_key.cbData)) + { + success = TRUE; + } + } + } + + CryptReleaseContext(prov, 0); + CryptDestroyKey(key); + + if(success) + { + return CKR_OK; + } + else + { + error = GetLastError(); + if(error == NTE_BAD_KEY_STATE) + return CKR_ATTRIBUTE_SENSITIVE; + return p11c_winerr_to_ckr(error); + } +} + +static CK_RV +lookup_rsa_attribute(KeyObjectData* kdata, CK_ATTRIBUTE_PTR attr) +{ + PUBLICKEYSTRUC* header; + RSAPUBKEY* pubkey; + CK_ULONG number; + CK_RV ret; + + ASSERT(kdata); + ASSERT(attr); + + if(!kdata->raw_public_key.pbData) + { + ret = load_raw_public_key(kdata); + if(ret != CKR_OK) + return ret; + } + + header = (PUBLICKEYSTRUC*)kdata->raw_public_key.pbData; + if(!header->bType == PUBLICKEYBLOB) + return CKR_GENERAL_ERROR; + + pubkey = (RSAPUBKEY*)(header + 1); + if(!pubkey->magic == 0x31415352) + return CKR_GENERAL_ERROR; + + switch(attr->type) + { + case CKA_MODULUS_BITS: + number = pubkey->bitlen; + return p11c_return_data(attr, &number, sizeof(CK_ULONG)); + + case CKA_PUBLIC_EXPONENT: + return p11c_return_dword_as_bytes(attr, pubkey->pubexp); + + case CKA_MODULUS: + return p11c_return_reversed_data(attr, (pubkey + 1), + pubkey->bitlen / 8); + + case CKA_PRIVATE_EXPONENT: + case CKA_PRIME_1: + case CKA_PRIME_2: + case CKA_EXPONENT_1: + case CKA_EXPONENT_2: + case CKA_COEFFICIENT: + if(kdata->object_class == CKO_PRIVATE_KEY) + return CKR_ATTRIBUTE_SENSITIVE; + else + return CKR_ATTRIBUTE_TYPE_INVALID; + + default: + ASSERT(FALSE); + return CKR_ATTRIBUTE_TYPE_INVALID; + } +} + +static CK_RV +key_bool_attribute(P11cObjectData* objdata, CK_ATTRIBUTE_PTR attr) +{ + KeyObjectData* kdata = (KeyObjectData*)objdata; + CK_BBOOL val; + CK_BBOOL is_private, is_rsa; + + ASSERT(objdata); + ASSERT(attr); + + is_private = (kdata->object_class == CKO_PRIVATE_KEY); + is_rsa = kdata->prov_info->dwProvType == PROV_RSA_FULL; + + switch(attr->type) + { + + /* + * Whether to authenticate before every use. + * - CAPI does all authentication + */ + case CKA_ALWAYS_AUTHENTICATE: + val = CK_FALSE; + break; + + /* + * Whether this key has always been sensitive. + * TODO: Can we detect this? + */ + case CKA_ALWAYS_SENSITIVE: + val = CK_FALSE; + break; + + /* + * Whether this key can be used to decrypt. + * - CKK_RSA but not CKK_DSA. + */ + case CKA_DECRYPT: + val = is_private && is_rsa; + break; + + /* + * Whether this key can be used to derive a session or + * other key. + * - Not true for CKK_RSA or CKK_DSA. + */ + case CKA_DERIVE: + val = CK_FALSE; + break; + + /* + * Whether or not this key can be used to encrypt?. + * TODO: Support for RSA public keys. + */ + case CKA_ENCRYPT: + val = CK_FALSE; + break; + + /* + * Whether this key can be exported or not. + * TODO: We may want to support this for public keys. + */ + case CKA_EXTRACTABLE: + val = CK_FALSE; + break; + + /* + * Whether this key was created on token. + * TODO: Can we implement this properly? + */ + case CKA_LOCAL: + val = CK_FALSE; + break; + + /* + * Whether this object is modifiable. + * - Keys are generally. never modifiable. + */ + case CKA_MODIFIABLE: + val = CK_FALSE; + break; + + /* + * Whether this key was ever extractable. + * TODO: Can we determine this? + */ + case CKA_NEVER_EXTRACTABLE: + val = CK_FALSE; + break; + + /* + * Whether this is a private object or not. + * - This 'private' means login before use. But maps + * well to private key use, since we're always logged in. + */ + case CKA_PRIVATE: + val = is_private; + break; + + /* + * Whether this is a sensitive object or not. + * - Private keys are sensitive, some attributes not + * readable. + */ + case CKA_SENSITIVE: + val = is_private; + break; + + /* + * Can this key sign stuff? + * - Private keys can sign. + */ + case CKA_SIGN: + val = is_private; + break; + + /* + * Can this key sign recoverable. + * TODO: Private RSA keys can sign recoverable. + */ + case CKA_SIGN_RECOVER: + val = CK_FALSE; + break; + + /* + * Is this stored on the token? + * - All CAPI objects are. + */ + case CKA_TOKEN: + val = CK_TRUE; + break; + + /* + * Is this key trusted? + * - A nebulous question. + */ + case CKA_TRUSTED: + val = CK_FALSE; + break; + + /* + * Key wrapping with public keys. + */ + case CKA_WRAP: + if(is_private) + return CKR_ATTRIBUTE_TYPE_INVALID; + val = CK_FALSE; + break; + + /* + * Key wrapping on private keys. + */ + case CKA_WRAP_WITH_TRUSTED: + if(!is_private) + return CKR_ATTRIBUTE_TYPE_INVALID; + val = CK_FALSE; + break; + + /* + * Can do a unwrap operation? + * - We don't implement this. + */ + case CKA_UNWRAP: + val = CK_FALSE; + break; + + /* + * Wrap, and unwrap stuff. + * - We don't implement this. + */ + case CKA_UNWRAP_TEMPLATE: + return CKR_ATTRIBUTE_TYPE_INVALID; + + /* + * Whether this key can be used to verify? + * TODO: Support for public keys. + */ + case CKA_VERIFY: + val = CK_FALSE; + break; + + /* + * Whether this key can be used to verify? + * TODO: Support for public keys. + */ + case CKA_VERIFY_RECOVER: + val = CK_FALSE; + break; + + default: + return CKR_ATTRIBUTE_TYPE_INVALID; + }; + + return p11c_return_data(attr, &val, sizeof(CK_BBOOL)); +} + +static CK_RV +key_ulong_attribute(P11cObjectData* objdata, CK_ATTRIBUTE_PTR attr) +{ + KeyObjectData* kdata = (KeyObjectData*)objdata; + CK_ULONG val; + + ASSERT(kdata); + ASSERT(attr); + + switch(attr->type) + { + + /* + * Object class. + */ + case CKA_CLASS: + val = kdata->object_class; + break; + + /* + * The key type. + * - Right now we only support (and load) RSA. + */ + case CKA_KEY_TYPE: + if(kdata->prov_info->dwProvType == PROV_RSA_FULL) + val = CKK_RSA; + else + val = CK_UNAVAILABLE_INFORMATION; + break; + + /* + * The key generation mechanism. + * TODO: We don't yet support key generation. + */ + case CKA_KEY_GEN_MECHANISM: + val = CK_UNAVAILABLE_INFORMATION; + break; + + /* + * The RSA modulus bits. + */ + case CKA_MODULUS_BITS: + if(kdata->prov_info->dwProvType == PROV_RSA_FULL) + return lookup_rsa_attribute(kdata, attr); + else + return CKR_ATTRIBUTE_TYPE_INVALID; + + default: + return CKR_ATTRIBUTE_TYPE_INVALID; + }; + + return p11c_return_data(attr, &val, sizeof(CK_ULONG)); +} + +static CK_RV +key_bytes_attribute(P11cObjectData* objdata, CK_ATTRIBUTE_PTR attr) +{ + KeyObjectData* kdata = (KeyObjectData*)objdata; + CK_MECHANISM_TYPE allowed_mechanisms[] = { CKM_RSA_PKCS }; + WCHAR* label; + + ASSERT(kdata); + ASSERT(attr); + + switch(attr->type) + { + /* + * The ID of the key. This should match the ID we + * return for any matching certificates. + */ + case CKA_ID: + return p11c_return_data(attr, kdata->key_identifier.pbData, + kdata->key_identifier.cbData); + + /* + * The key label. + * - We use the container name. + */ + case CKA_LABEL: + label = kdata->prov_info->pwszContainerName; + if(!label) + label = L"Unnamed Key"; + return p11c_return_string(attr, label); + + /* + * The subject of the related certificate. + * TODO: Implement this lookup. + */ + case CKA_SUBJECT: + return p11c_return_data(attr, "", 0); + + /* + * Allowed mechanisms with this key. + * - RSA used with CKM_RSA + * TODO: Needs updating when DSA implemented. + */ + case CKA_ALLOWED_MECHANISMS: + return p11c_return_data(attr, &allowed_mechanisms, + sizeof(allowed_mechanisms)); + + /* + * Various RSA public attributes. + */ + case CKA_MODULUS: + case CKA_PUBLIC_EXPONENT: + case CKA_PRIVATE_EXPONENT: + case CKA_PRIME_1: + case CKA_PRIME_2: + case CKA_EXPONENT_1: + case CKA_EXPONENT_2: + case CKA_COEFFICIENT: + if(kdata->prov_info->dwProvType == PROV_RSA_FULL) + return lookup_rsa_attribute(kdata, attr); + else + return CKR_ATTRIBUTE_TYPE_INVALID; + + /* + * Last date this key can be used. + * TODO: Does CAPI support this ability? + */ + case CKA_END_DATE: + case CKA_START_DATE: + return p11c_return_data(attr, "", 0); + + default: + return CKR_ATTRIBUTE_TYPE_INVALID; + }; +} + +static void +key_release(void* data) +{ + KeyObjectData* kdata = (KeyObjectData*)data; + ASSERT(kdata); + + ASSERT(kdata->key_identifier.pbData); + ASSERT(kdata->prov_info); + + free(kdata->key_identifier.pbData); + free(kdata->prov_info); + + free(kdata); +} + +static const P11cObjectDataVtable key_objdata_vtable = { + key_bool_attribute, + key_ulong_attribute, + key_bytes_attribute, + key_release, +}; + +static CRYPT_KEY_PROV_INFO* +duplicate_prov_info(CRYPT_KEY_PROV_INFO* original) +{ + DWORD container_length, prov_length; + CRYPT_KEY_PROV_INFO* result; + DWORD length, i; + BYTE* at; + BYTE* end; + + if(!original) + return NULL; + + /* Go through and calculate the length */ + length = sizeof(CRYPT_KEY_PROV_INFO); + if(original->pwszContainerName) + { + container_length = (wcslen(original->pwszContainerName) + 1) * sizeof(WCHAR); + length += container_length; + } + + if(original->pwszProvName) + { + prov_length = (wcslen(original->pwszProvName) + 1) * sizeof(WCHAR); + length += prov_length; + } + + length += sizeof(CRYPT_KEY_PROV_PARAM) * original->cProvParam; + for(i = 0; i < original->cProvParam; ++i) + length += original->rgProvParam[i].cbData; + + /* Allocate a single block of memory for everything */ + at = (BYTE*)malloc(length); + if(!at) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + /* Copy in very carefully */ + end = at + length; + + memcpy(at, original, sizeof(CRYPT_KEY_PROV_INFO)); + result = (CRYPT_KEY_PROV_INFO*)at; + at += sizeof(CRYPT_KEY_PROV_INFO); + + if(result->pwszContainerName) + { + memcpy(at, result->pwszContainerName, container_length); + result->pwszContainerName = (LPWSTR)at; + at += container_length; + } + + if(result->pwszProvName) + { + memcpy(at, result->pwszProvName, prov_length); + result->pwszProvName = (LPWSTR)at; + at += prov_length; + } + + if(original->cProvParam) + { + memcpy(at, result->rgProvParam, sizeof(CRYPT_KEY_PROV_PARAM) * result->cProvParam); + result->rgProvParam = (CRYPT_KEY_PROV_PARAM*)at; + at += sizeof(CRYPT_KEY_PROV_PARAM) * result->cProvParam; + + for(i = 0; i < result->cProvParam; ++i) + { + memcpy(at, result->rgProvParam[i].pbData, result->rgProvParam[i].cbData); + result->rgProvParam[i].pbData = (BYTE*)at; + at += result->rgProvParam[i].cbData; + } + } + + ASSERT(at == end); + return result; +} + +static P11cObjectData* +key_alloc_data(P11cSession* sess, P11cObject* obj, CRYPT_KEY_PROV_INFO* prov_info) +{ + KeyObject* kobj = (KeyObject*)obj; + KeyObjectData* kdata; + + kdata = (KeyObjectData*)calloc(1, sizeof(KeyObjectData)); + if(!kdata) + return NULL; + + /* Allocate memory for key identifier */ + kdata->key_identifier.pbData = malloc(kobj->key_identifier.cbData); + if(!kdata->key_identifier.pbData) + { + free(kdata); + return NULL; + } + + /* Setup the object data */ + kdata->object_class = kobj->object_class; + kdata->prov_info = prov_info; + kdata->key_identifier.cbData = kobj->key_identifier.cbData; + memcpy(kdata->key_identifier.pbData, kobj->key_identifier.pbData, + kdata->key_identifier.cbData); + kdata->raw_public_key.pbData = NULL; + kdata->raw_public_key.cbData = 0; + + kdata->base.object = obj->id; + kdata->base.data_funcs = &key_objdata_vtable; + + return &(kdata->base); +} + +static BOOL WINAPI +load_key_property_info(PCRYPT_HASH_BLOB key_identifier, DWORD flags, + void* reserved, void* arg, DWORD n_props, DWORD* props, + void** datas, DWORD* n_datas) +{ + CRYPT_KEY_PROV_INFO** prov_info = (CRYPT_KEY_PROV_INFO**)arg; + DWORD i; + + /* + * Already got a provider info. This shouldn't happen + * but can occur if the same key is present twice. + */ + if(*prov_info) + return TRUE; + + /* Find the key provider info property */ + for(i = 0; i < n_props; ++i) + { + if(props[i] == CERT_KEY_PROV_INFO_PROP_ID) + { + *prov_info = duplicate_prov_info((CRYPT_KEY_PROV_INFO*)datas[i]); + break; + } + } + + return TRUE; +} + +static CK_RV +key_load_data(P11cSession* sess, P11cObject* obj, P11cObjectData** objdata) +{ + KeyObject* kobj = (KeyObject*)obj; + CRYPT_KEY_PROV_INFO* prov_info = NULL; + + ASSERT(kobj); + ASSERT(objdata); + + /* Load the provider info */ + if(!CryptEnumKeyIdentifierProperties((CRYPT_HASH_BLOB*)&kobj->key_identifier, + CERT_KEY_PROV_INFO_PROP_ID, 0, NULL, NULL, + &prov_info, load_key_property_info)) + return p11c_winerr_to_ckr(GetLastError()); + + /* No provider info, bad news */ + if(!prov_info) + return CKR_GENERAL_ERROR; + + *objdata = key_alloc_data(sess, obj, prov_info); + if(!(*objdata)) + { + free(prov_info); + return CKR_HOST_MEMORY; + } + + return CKR_OK; +} + +static unsigned int +key_hash_func(P11cObject* obj) +{ + KeyObject* kobj = (KeyObject*)obj; + return p11c_hash_data(kobj->key_identifier.pbData, kobj->key_identifier.cbData) ^ + p11c_hash_integer((int)kobj->object_class); +} + +static int +key_equal_func(P11cObject* a, P11cObject* b) +{ + KeyObject* ka = (KeyObject*)a; + KeyObject* kb = (KeyObject*)b; + return ka->object_class == kb->object_class && + ka->key_identifier.cbData == kb->key_identifier.cbData && + memcmp(ka->key_identifier.pbData, kb->key_identifier.pbData, ka->key_identifier.cbData) == 0; +} + +static void +key_object_release(void* data) +{ + KeyObject* kobj = (KeyObject*)data; + ASSERT(kobj); + free(kobj); +} + +static const P11cObjectVtable key_object_vtable = { + key_load_data, + key_hash_func, + key_equal_func, + key_object_release, +}; + +static CK_RV +register_key_object(P11cSession* sess, CK_OBJECT_CLASS cls, + CRYPT_HASH_BLOB* key_identifier, P11cObject** obj) +{ + KeyObject* kobj; + CK_RV ret; + + ASSERT(obj); + ASSERT(key_identifier); + ASSERT(cls == CKO_PRIVATE_KEY || cls == CKO_PUBLIC_KEY); + + kobj = calloc(1, sizeof(KeyObject) + key_identifier->cbData); + if(!kobj) + return CKR_HOST_MEMORY; + + kobj->obj.id = 0; + kobj->obj.obj_funcs = &key_object_vtable; + + kobj->object_class = cls; + kobj->key_identifier.pbData = (BYTE*)(kobj + 1); + kobj->key_identifier.cbData = key_identifier->cbData; + memcpy(kobj->key_identifier.pbData, key_identifier->pbData, + kobj->key_identifier.cbData); + + ret = p11c_token_register_object(sess->slot, &(kobj->obj)); + if(ret != CKR_OK) + { + free(kobj); + return ret; + } + + ASSERT(kobj->obj.id != 0); + *obj = &(kobj->obj); + + return CKR_OK; +} + +typedef struct _EnumArguments +{ + P11cSession* sess; + CK_OBJECT_CLASS object_class; + CK_ATTRIBUTE_PTR match; + CK_ULONG count; + P11cArray* results; + CK_RV ret; +} +EnumArguments; + +static BOOL WINAPI +enum_key_property_info(PCRYPT_HASH_BLOB key_identifier, DWORD flags, + void* reserved, void* arg, DWORD n_props, DWORD* props, + void** datas, DWORD* n_datas) +{ + EnumArguments* args = (EnumArguments*)arg; + CRYPT_KEY_PROV_INFO* prov_info = NULL; + P11cObject *obj = NULL; + KeyObjectData kdata; + DWORD i; + + /* Find the key provider info property */ + for(i = 0; i < n_props; ++i) + { + if(props[i] == CERT_KEY_PROV_INFO_PROP_ID) + { + prov_info = (CRYPT_KEY_PROV_INFO*)datas[i]; + break; + } + } + + /* Strange key, skip */ + if(!prov_info) + return TRUE; + + /* Match the public key */ + kdata.prov_info = prov_info; + kdata.object_class = args->object_class; + kdata.base.object = 0; + kdata.base.data_funcs = &key_objdata_vtable; + + if(p11c_object_data_match(&kdata.base, args->match, args->count)) + { + args->ret = register_key_object(args->sess, args->object_class, key_identifier, &obj); + if(args->ret == CKR_OK) + { + ASSERT(obj); + p11c_array_append(args->results, obj->id); + } + } + + return TRUE; + +} + +static CK_RV +find_any_keys(P11cSession* sess, CK_OBJECT_CLASS cls, + CK_ATTRIBUTE_PTR match, CK_ULONG count, P11cArray* arr) +{ + CRYPT_HASH_BLOB find_id; + EnumArguments enum_args; + CK_ULONG i; + + /* Try to setup for an efficient search based on key id */ + memset(&find_id, 0, sizeof(find_id)); + for(i = 0; i < count; ++i) + { + if(!match[i].pValue || !match[i].ulValueLen) + continue; + if(match[i].type == CKA_ID) + { + find_id.cbData = match[i].ulValueLen; + find_id.pbData = match[i].pValue; + } + } + + enum_args.sess = sess; + enum_args.match = match; + enum_args.count = count; + enum_args.results = arr; + enum_args.object_class = cls; + enum_args.ret = CKR_OK; + + if(!CryptEnumKeyIdentifierProperties(find_id.cbData != 0 ? &find_id : NULL, + CERT_KEY_PROV_INFO_PROP_ID, 0, NULL, NULL, + &enum_args, enum_key_property_info)) + return p11c_winerr_to_ckr(GetLastError()); + + return enum_args.ret; +} + +static CK_RV +list_matching_certificates(P11cSession* sess, CK_ATTRIBUTE_PTR match, + CK_ULONG count, P11cArray* arr) +{ + CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; + CK_ATTRIBUTE search[3]; + CK_ULONG n_search = 0; + CK_ULONG i; + + /* The class */ + search[0].type = CKA_CLASS; + search[0].pValue = &cert_class; + search[0].ulValueLen = sizeof(CK_OBJECT_CLASS); + ++n_search; + + for(i = 0; i < count && n_search < 3; ++i) + { + /* + * This is the attributes that tie a certificate + * to key object, so try match certs with these + */ + if(match[i].type == CKA_ID) + { + search[n_search].type = match[i].type; + search[n_search].pValue = match[i].pValue; + search[n_search].ulValueLen = match[i].ulValueLen; + ++n_search; + } + } + + /* Do the certificate search */ + return p11c_cert_find(sess, CKO_CERTIFICATE, search, n_search, arr); +} + +static CK_RV +find_certificate_key(P11cSession* session, CK_OBJECT_CLASS cls, + CK_ATTRIBUTE_PTR match, CK_ULONG count, + PCCERT_CONTEXT cert, P11cArray* arr) +{ + CRYPT_KEY_PROV_INFO* prov_info; + CRYPT_HASH_BLOB key_identifier; + P11cObjectData* objdata; + KeyObjectData kdata; + P11cObject* obj; + DWORD prov_length; + DWORD error; + CK_RV ret = CKR_OK; + + /* Look up the key provider info and identifier */ + if(!CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, NULL, &prov_length) || + !CertGetCertificateContextProperty(cert, CERT_KEY_IDENTIFIER_PROP_ID, NULL, &key_identifier.cbData)) + { + error = GetLastError(); + if(error == CRYPT_E_NOT_FOUND) + return CKR_OK; + return p11c_winerr_to_ckr(error); + } + + /* We own the info memory */ + prov_info = malloc(prov_length); + if(!prov_info) + return CKR_HOST_MEMORY; + key_identifier.pbData = malloc(key_identifier.cbData); + if(!key_identifier.pbData) + { + free(prov_info); + return CKR_HOST_MEMORY; + } + + /* Lookup the key provider info and identifier */ + if(CertGetCertificateContextProperty(cert, CERT_KEY_PROV_INFO_PROP_ID, prov_info, &prov_length) && + CertGetCertificateContextProperty(cert, CERT_KEY_IDENTIFIER_PROP_ID, key_identifier.pbData, &key_identifier.cbData)) + { + kdata.object_class = cls; + kdata.prov_info = prov_info; + kdata.key_identifier = key_identifier; + kdata.base.object = 0; + kdata.base.data_funcs = &key_objdata_vtable; + + if(p11c_object_data_match(&kdata.base, match, count)) + { + ret = register_key_object(session, cls, &key_identifier, &obj); + if(ret == CKR_OK) + { + ASSERT(obj); + + /* Store away the object data for performance reasons */ + objdata = key_alloc_data(session, obj, prov_info); + if(objdata) + { + p11c_session_take_object_data(session, obj, objdata); + + /* Note these are used, and not to be freed */ + key_identifier.pbData = NULL; + key_identifier.cbData = 0; + prov_info = NULL; + } + + p11c_array_append(arr, obj->id); + } + } + } + else + { + ret = p11c_winerr_to_ckr(GetLastError()); + } + + if(key_identifier.pbData) + free(key_identifier.pbData); + if(prov_info) + free(prov_info); + + return ret; +} + +static CK_RV +find_certificate_keys(P11cSession* session, CK_OBJECT_CLASS cls, + CK_ATTRIBUTE_PTR match, CK_ULONG count, P11cArray* arr) +{ + CK_OBJECT_HANDLE id; + P11cObjectData* certdata; + P11cArray* certarr; + PCCERT_CONTEXT cert; + CK_RV ret = CKR_OK; + CK_ULONG i; + + /* Get a list of all certificates */ + certarr = p11c_array_new(0, 1, sizeof(CK_OBJECT_HANDLE)); + if(!certarr) + return CKR_HOST_MEMORY; + ret = list_matching_certificates(session, match, count, certarr); + + /* Now match each of them against our criteria */ + if(ret == CKR_OK) + { + for(i = 0; i < certarr->len; ++i) + { + id = p11c_array_index(certarr, CK_OBJECT_HANDLE, i); + ASSERT(id); + + /* Get the certificate data for this certificate object */ + if(p11c_session_get_object_data_for(session, id, &certdata) != CKR_OK) + continue; + + /* Get the certificate context */ + cert = p11c_cert_object_data_get_certificate(certdata); + if(!cert) + continue; + + /* Remember we can have either or both keys for each certificate */ + ret = find_certificate_key(session, cls, match, count, cert, arr); + } + } + + p11c_array_free(certarr, TRUE); + return ret; +} + +CK_RV +p11c_key_find(P11cSession* sess, CK_OBJECT_CLASS cls, + CK_ATTRIBUTE_PTR match, CK_ULONG count, P11cArray* arr) +{ + CK_RV ret = CKR_OK; + + /* Is this somewhere we have all keys present? */ + if(p11c_token_get_flags(sess->slot) & P11c_SLOT_ANYKEY) + { + if((cls == CKO_PRIVATE_KEY || cls == CKO_ANY) && ret == CKR_OK) + ret = find_any_keys(sess, CKO_PRIVATE_KEY, match, count, arr); + if((cls == CKO_PUBLIC_KEY || cls == CKO_ANY) && ret == CKR_OK) + ret = find_any_keys(sess, CKO_PUBLIC_KEY, match, count, arr); + } + + /* Otherwise we can only list the keys that have certificates */ + else + { + if((cls == CKO_PRIVATE_KEY || cls == CKO_ANY) && ret == CKR_OK) + ret = find_certificate_keys(sess, CKO_PRIVATE_KEY, match, count, arr); + if((cls == CKO_PUBLIC_KEY || cls == CKO_ANY) && ret == CKR_OK) + ret = find_certificate_keys(sess, CKO_PUBLIC_KEY, match, count, arr); + } + + return ret; +} + +DWORD +p11c_key_object_data_get_bits(P11cObjectData* objdata) +{ + KeyObjectData* kdata; + PUBLICKEYSTRUC* header; + RSAPUBKEY* pubkey; + CK_RV ret; + + ASSERT(objdata); + + kdata = (KeyObjectData*)objdata; + + if(!kdata->raw_public_key.pbData) + { + ret = load_raw_public_key(kdata); + if(ret != CKR_OK) + return ret; + } + + header = (PUBLICKEYSTRUC*)kdata->raw_public_key.pbData; + if(!header->bType == PUBLICKEYBLOB) + return 0; + + pubkey = (RSAPUBKEY*)(header + 1); + if(!pubkey->magic == 0x31415352) + return 0; + + return pubkey->bitlen; +} + +CRYPT_KEY_PROV_INFO* +p11c_key_object_data_get_prov_info(P11cObjectData* objdata) +{ + KeyObjectData* kdata; + + ASSERT(objdata); + kdata = (KeyObjectData*)objdata; + return kdata->prov_info; +} diff --git a/p11-capi-key.h b/p11-capi-key.h new file mode 100644 index 0000000..b57a2d3 --- /dev/null +++ b/p11-capi-key.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2008 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef P11C_KEY_H +#define P11C_KEY_H + +#include "p11-capi.h" + +/* Find key objects matching criteria */ +CK_RV p11c_key_find (P11cSession* sess, CK_OBJECT_CLASS cls, + CK_ATTRIBUTE_PTR match, CK_ULONG count, + P11cArray* arr); + +DWORD p11c_key_object_data_get_bits (P11cObjectData* objdata); + +CRYPT_KEY_PROV_INFO* p11c_key_object_data_get_prov_info (P11cObjectData* objdata); + +#endif /* P11C_KEY_H */ diff --git a/p11-capi-object.c b/p11-capi-object.c new file mode 100644 index 0000000..6c2330e --- /dev/null +++ b/p11-capi-object.c @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2007 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "p11-capi.h" +#include "p11-capi-object.h" + +#include "pkcs11/pkcs11n.h" + +#include + +enum +{ + DATA_UNKNOWN = 0, + DATA_BOOL, + DATA_ULONG, + DATA_BYTES +}; + +int +attribute_data_type(CK_ATTRIBUTE_TYPE type) +{ + switch(type) + { + // CK_ULONG attribute types + case CKA_CLASS: + case CKA_CERTIFICATE_TYPE: + case CKA_CERTIFICATE_CATEGORY: + case CKA_KEY_TYPE: + case CKA_MODULUS_BITS: + case CKA_PRIME_BITS: + /* case CKA_SUBPRIME_BITS: */ + case CKA_SUB_PRIME_BITS: + case CKA_VALUE_BITS: + case CKA_VALUE_LEN: + case CKA_KEY_GEN_MECHANISM: + case CKA_HW_FEATURE_TYPE: + case CKA_PIXEL_X: + case CKA_PIXEL_Y: + case CKA_RESOLUTION: + case CKA_CHAR_ROWS: + case CKA_CHAR_COLUMNS: + case CKA_BITS_PER_PIXEL: + case CKA_MECHANISM_TYPE: + case CKA_JAVA_MIDP_SECURITY_DOMAIN: + case CKA_TRUST_SERVER_AUTH: + case CKA_TRUST_CLIENT_AUTH: + case CKA_TRUST_CODE_SIGNING: + case CKA_TRUST_EMAIL_PROTECTION: + case CKA_TRUST_IPSEC_END_SYSTEM: + case CKA_TRUST_IPSEC_TUNNEL: + case CKA_TRUST_IPSEC_USER: + case CKA_TRUST_TIME_STAMPING: + return DATA_ULONG; + + // CK_BBOOL attribute types + case CKA_TOKEN: + case CKA_PRIVATE: + case CKA_MODIFIABLE: + case CKA_TRUSTED: + case CKA_SENSITIVE: + case CKA_DECRYPT: + case CKA_SIGN: + case CKA_SIGN_RECOVER: + case CKA_UNWRAP: + case CKA_EXTRACTABLE: + case CKA_NEVER_EXTRACTABLE: + case CKA_ALWAYS_SENSITIVE: + case CKA_WRAP_WITH_TRUSTED: + case CKA_ALWAYS_AUTHENTICATE: + case CKA_ENCRYPT: + case CKA_WRAP: + case CKA_VERIFY: + case CKA_VERIFY_RECOVER: + case CKA_DERIVE: + case CKA_LOCAL: + case CKA_RESET_ON_INIT: + case CKA_HAS_RESET: + case CKA_COLOR: + case CKA_TRUST_STEP_UP_APPROVED: + return DATA_BOOL; + + // Raw or string data + case CKA_LABEL: + case CKA_APPLICATION: + case CKA_VALUE: + case CKA_OBJECT_ID: + case CKA_CHECK_VALUE: + case CKA_ISSUER: + case CKA_SERIAL_NUMBER: + case CKA_SUBJECT: + case CKA_ID: + case CKA_URL: + case CKA_HASH_OF_SUBJECT_PUBLIC_KEY: + case CKA_HASH_OF_ISSUER_PUBLIC_KEY: + case CKA_AC_ISSUER: + case CKA_OWNER: + case CKA_ATTR_TYPES: + case CKA_MODULUS: + case CKA_PUBLIC_EXPONENT: + case CKA_PRIVATE_EXPONENT: + case CKA_PRIME_1: + case CKA_PRIME_2: + case CKA_EXPONENT_1: + case CKA_EXPONENT_2: + case CKA_COEFFICIENT: + case CKA_PRIME: + case CKA_SUBPRIME: + case CKA_BASE: + case CKA_ECDSA_PARAMS: + /* case CKA_EC_PARAMS: */ + case CKA_EC_POINT: + case CKA_CHAR_SETS: + case CKA_ENCODING_METHODS: + case CKA_MIME_TYPES: + case CKA_REQUIRED_CMS_ATTRIBUTES: + case CKA_DEFAULT_CMS_ATTRIBUTES: + case CKA_SUPPORTED_CMS_ATTRIBUTES: + case CKA_CERT_SHA1_HASH: + case CKA_CERT_MD5_HASH: + case CKA_ALLOWED_MECHANISMS: + case CKA_START_DATE: + case CKA_END_DATE: + return DATA_BYTES; + + // Arrays are nasty + case CKA_WRAP_TEMPLATE: + case CKA_UNWRAP_TEMPLATE: + default: + DBG(("unknown attribute type: %x", type)); + return DATA_UNKNOWN; + }; +} + +CK_BBOOL +p11c_object_data_match_attr(P11cObjectData* objdata, CK_ATTRIBUTE_PTR match) +{ + CK_ATTRIBUTE attr; + CK_RV rv; + int dtype; + + ASSERT(match); + ASSERT(objdata); + ASSERT(objdata->data_funcs); + + /* Get the data type of the attribute */ + dtype = attribute_data_type(match->type); + if(dtype == DATA_UNKNOWN) + return CK_FALSE; + + /* We only do byte matching */ + if(match->pValue == NULL) + return CK_FALSE; + + /* Only load as much data as is needed */ + attr.type = match->type; + attr.pValue = _alloca(match->ulValueLen > 4 ? match->ulValueLen : 4); + attr.ulValueLen = match->ulValueLen; + + switch(dtype) + { + case DATA_BOOL: + rv = (objdata->data_funcs->get_bool)(objdata, &attr); + break; + case DATA_ULONG: + rv = (objdata->data_funcs->get_ulong)(objdata, &attr); + break; + case DATA_BYTES: + rv = (objdata->data_funcs->get_bytes)(objdata, &attr); + break; + default: + ASSERT(0 && "unrecognized type"); + break; + }; + + /* Unrecognized attribute */ + if(rv == CKR_ATTRIBUTE_TYPE_INVALID) + return CK_FALSE; + + /* Value is longer than this one */ + if(rv == CKR_BUFFER_TOO_SMALL) + return CK_FALSE; + + /* All other errors */ + if(rv != CKR_OK) + return CK_FALSE; + + return (match->ulValueLen == attr.ulValueLen && + memcmp(match->pValue, attr.pValue, attr.ulValueLen) == 0); +} + +CK_BBOOL +p11c_object_data_match(P11cObjectData* objdata, CK_ATTRIBUTE_PTR matches, + CK_ULONG count) +{ + CK_ULONG i; + + for(i = 0; i < count; ++i) + { + if(!p11c_object_data_match_attr(objdata, &matches[i])) + return CK_FALSE; + } + + return CK_TRUE; +} + +CK_RV +p11c_object_data_get_attrs(P11cObjectData* objdata, CK_ATTRIBUTE_PTR attrs, + CK_ULONG count) +{ + CK_ULONG i; + CK_RV rv, ret = CKR_OK; + + ASSERT(objdata); + ASSERT(!count || attrs); + + for(i = 0; i < count; ++i) + { + /* Get the data type of the attribute */ + switch(attribute_data_type(attrs[i].type)) + { + case DATA_BOOL: + rv = (objdata->data_funcs->get_bool)(objdata, &attrs[i]); + break; + case DATA_ULONG: + rv = (objdata->data_funcs->get_ulong)(objdata, &attrs[i]); + break; + case DATA_BYTES: + rv = (objdata->data_funcs->get_bytes)(objdata, &attrs[i]); + break; + case DATA_UNKNOWN: + rv = CKR_ATTRIBUTE_TYPE_INVALID; + break; + default: + ASSERT(0 && "unrecognized type"); + break; + }; + + /* Not an error if they were just requesting the size */ + if(rv != CKR_OK) + { + if(rv == CKR_BUFFER_TOO_SMALL) + { + if(!attrs[i].pValue) + rv = CKR_OK; + } + + /* Attribute is sensitive */ + else if(rv == CKR_ATTRIBUTE_SENSITIVE) + { + attrs[i].ulValueLen = (CK_ULONG)-1; + } + + /* Attribute doesn't exist */ + else if(rv == CKR_ATTRIBUTE_TYPE_INVALID) + { + DBG(("O%d: attribute not found: 0x%08x", objdata->object, attrs[i].type)); + attrs[i].ulValueLen = (CK_ULONG)-1; + } + + /* A fatal error? */ + else + { + ret = rv; + break; + } + + /* Transfer any non-fatal errors outward */ + if(rv != CKR_OK && ret == CKR_OK) + ret = rv; + } + } + + return ret; +} diff --git a/p11-capi-object.h b/p11-capi-object.h new file mode 100644 index 0000000..cd08ae9 --- /dev/null +++ b/p11-capi-object.h @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2007 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef P11C_OBJECT_H +#define P11C_OBJECT_H + +#include "p11-capi.h" + +/* Debug print something about an object */ +#define DBGO(obj, msg) \ + p11c_debug("O%d: %s", (obj) ? (obj)->id : 0, (msg)) + +/* A function to load data for an object */ +typedef CK_RV (*P11cLoadData)(P11cSession* sess, struct _P11cObject* obj, + P11cObjectData** objdata); + +/* Produce a hash code for an object */ +typedef CK_RV (*P11cHashObject)(struct _P11cObject* obj); + +/* Produce a hash code for an object */ +typedef CK_RV (*P11cEqualObject)(struct _P11cObject* one, struct _P11cObject* two); + +/* A function to free some data */ +typedef void (*P11cRelease)(void* data); + +/* Object functions */ +typedef struct _P11cObjectVtable +{ + P11cLoadData load_data; + P11cHashObject hash_object; + P11cEqualObject equal_object; + P11cRelease release; +} +P11cObjectVtable; + +/* Represents a object we've seen */ +struct _P11cObject +{ + CK_OBJECT_HANDLE id; + CK_SLOT_ID slot; + CK_SESSION_HANDLE session; + const P11cObjectVtable* obj_funcs; +}; + +/* A function to get an attribute from ObjectData */ +typedef CK_RV (*P11cGetAttribute)(P11cObjectData* objdata, CK_ATTRIBUTE_PTR attr); + +/* Object data functions */ +typedef struct _P11cObjectDataVtable +{ + P11cGetAttribute get_bool; + P11cGetAttribute get_ulong; + P11cGetAttribute get_bytes; + P11cRelease release; +} +P11cObjectDataVtable; + +/* + * Base class for object data. Different types of + * objects extend this with more detailed data + */ +struct _P11cObjectData +{ + CK_OBJECT_HANDLE object; + const P11cObjectDataVtable* data_funcs; +}; + +/* Match object data against all the given match attributes */ +CK_BBOO p11c_object_data_match (P11cObjectData* objdata, + CK_ATTRIBUTE_PTR matches, CK_ULONG count); + +/* Match a single attribute against object data */ +CK_BBOOL p11c_object_data_match_attr (P11cObjectData* objdata, + CK_ATTRIBUTE_PTR match); + +/* Get a bunch of attributes from object data */ +CK_RV p11c_object_data_get_attrs (P11cObjectData* objdata, CK_ATTRIBUTE_PTR attrs, + CK_ULONG count); + +/* Debug print something about an object data */ +#define DBGOD(objdata, msg) \ + p11c_debug("O%d: %s", (objdata) ? (objdata)->obj : 0, (msg)) + +#endif /* P11C_OBJECT_H */ diff --git a/p11-capi-rsa.c b/p11-capi-rsa.c new file mode 100644 index 0000000..1e22618 --- /dev/null +++ b/p11-capi-rsa.c @@ -0,0 +1,416 @@ +/* + * Copyright (C) 2008 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "p11c.h" + +#include "p11-capi-der.h" +#include "p11-capi-key.h" +#include "p11-capi-object.h" + +/* + * Portions derived from NSS source files: + * lib/ckfw/capi/crsa.c + */ + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Netscape security libraries. + * + * The Initial Developer of the Original Code is + * Red Hat, Inc. + * Portions created by the Initial Developer are Copyright (C) 2005 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bob Relyea (rrelyea@redhat.com) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#define SSL3_SHAMD5_HASH_SIZE 36 /* LEN_MD5 (16) + LEN_SHA1 (20) */ + +/* + * PKCS #11 sign for RSA expects to take a fully DER-encoded hash value, + * which includes the hash OID. CAPI expects to take a Hash Context. While + * CAPI does have the capability of setting a raw hash value, it does not + * have the ability to sign an arbitrary value. This function tries to + * reduce the passed in data into something that CAPI could actually sign. + */ +static CK_BYTE_PTR +parse_rsa_pkcs_der_hash(CK_BYTE_PTR input, CK_ULONG n_input, + ALG_ID* algorithm, CK_ULONG_PTR n_hash) +{ + BYTE* algid; + BYTE* oid; + BYTE* hash_data; + BYTE* oid_str; + DWORD n_oid; + DWORD n_algid; + + /* + * there are 2 types of hashes NSS typically tries to sign, regular + * RSA signature format (with encoded DER_OIDS), and SSL3 Signed hashes. + * CAPI knows not to add any oids to SSL3_Signed hashes, so if we have any + * random hash that is exactly the same size as an SSL3 hash, then we can + * just pass the data through. CAPI has know way of knowing if the value + * is really a combined hash or some other arbitrary data, so it's safe to + * handle this case first. + */ + if(SSL3_SHAMD5_HASH_SIZE == n_input) + { + *n_hash = n_input; + *algorithm = CALG_SSL3_SHAMD5; + return input; + } + + /* make sure we have a sequence tag */ + if((P11C_DER_SEQUENCE | P11C_DER_CONSTRUCTED) != *input) + return NULL; + + /* + * parse the input block to get 1) the hash oid, and 2) the raw hash value. + * unfortunatly CAPI doesn't have a builtin function to do this work, so + * we go ahead and do it by hand here. + * + * format is: + * SEQUENCE { + * SECQUENCE { // algid + * OID {} // oid + * ANY {} // optional params + * } + * OCTECT {} // hash + */ + + /* unwrap */ + algid = p11c_der_unwrap(input, n_input, &n_algid, NULL); + if(!algid) + return NULL; + + /* make sure there is not extra data at the end */ + if(algid + n_algid != input + n_input) + return NULL; + + /* wasn't an algid */ + if((P11C_DER_SEQUENCE | P11C_DER_CONSTRUCTED) != *algid) + return NULL; + + oid = p11c_der_unwrap(algid, n_algid, &n_oid, &hash_data); + if(!oid || !hash_data) + return NULL; + + if(algorithm) + { + /* + * get the real oid as a string. Again, Microsoft does not + * export anything that does this for us + */ + oid_str = p11c_der_read_oid(oid, n_oid); + if(!oid_str) + return NULL; + + /* look up the hash alg from the oid (fortunately CAPI does to this) */ + *algorithm = CertOIDToAlgId(oid_str); + free(oid_str); + } + + /* wasn't a hash? */ + if(P11C_DER_OCTET_STRING != *hash_data) + return NULL; + + /* get the real raw hash */ + return p11c_der_unwrap(hash_data, n_algid - (hash_data - algid), + n_hash, NULL); +} + +CK_RV +p11c_rsa_pkcs_sign_init(P11CObjectData *keydata, void** operation) +{ + CRYPT_KEY_PROV_INFO* prov_info; + + ASSERT(keydata); + ASSERT(operation); + ASSERT(!*operation); + + prov_info = p11c_key_object_data_get_prov_info(keydata); + if(prov_info->dwProvType != PROV_RSA_FULL) + return CKR_KEY_TYPE_INCONSISTENT; + + *operation = keydata; + return CKR_OK; +} + +CK_RV +p11c_rsa_pkcs_sign_perform (CK_BYTE_PTR data, CK_ULONG n_data, + CK_BYTE_PTR signature, CK_ULONG_PTR n_signature, + void** operation) +{ + CRYPT_KEY_PROV_INFO* prov_info; + P11CObjectData* keydata; + ALG_ID algorithm; + BYTE* hash_data; + DWORD n_hash_data; + BOOL capifail; + DWORD len, check; + DWORD bits; + CK_RV ret; + + HCRYPTPROV prov = 0; + HCRYPTHASH hash = 0; + + + ASSERT(operation); + ASSERT(*operation); + + if(!data || !n_data) + return CKR_ARGUMENTS_BAD; + + keydata = (P11CObjectData*)*operation; + + prov_info = p11c_key_object_data_get_prov_info(keydata); + ASSERT(prov_info); + + /* Calculate the number of bits */ + bits = p11c_key_object_data_get_bits (keydata); + if(!bits) + return CKR_GENERAL_ERROR; + + /* Want to know the length */ + if(!signature) + { + *n_signature = bits / 8; + return CKR_OK; + } + + /* TODO: Support arbitrary input on Vista */ + + /* + * PKCS #11 sign for RSA expects to take a fully DER-encoded hash value, + * which includes the hash OID. CAPI expects to take a Hash Context. While + * CAPI does have the capability of setting a raw hash value, it does not + * have the ability to sign an arbitrary value. This function tries to + * reduce the passed in data into something that CAPI could actually sign. + */ + hash_data = parse_rsa_pkcs_der_hash(data, n_data, &algorithm, &n_hash_data); + if(!hash_data) + return CKR_DATA_INVALID; + + capifail = TRUE; + if(CryptAcquireContextW(&prov, prov_info->pwszContainerName, prov_info->pwszProvName, + prov_info->dwProvType, 0)) + { + if(CryptCreateHash(prov, algorithm, 0, 0, &hash)) + { + /* make sure the hash lens match before we set it */ + len = sizeof(DWORD); + if(CryptGetHashParam(hash, HP_HASHSIZE, (BYTE*)&check, &len, 0)) + { + if(check != n_hash_data) + { + capifail = FALSE; + ret = CKR_DATA_INVALID; + } + + /* + * we have an explicit hash, set it, note that the length is + * implicit by the hashAlg used in create + */ + if(CryptSetHashParam(hash, HP_HASHVAL, hash_data, 0)) + { + /* OK, we have the data in a hash structure, sign it! */ + if(CryptSignHash(hash, prov_info->dwKeySpec, + NULL, 0, signature, n_signature)) + { + /* + * OK, Microsoft likes to do things completely + * differently than anyone else. We need to reverse + * the data we recieved here + */ + if(signature) + p11c_reverse_memory(signature, *n_signature); + + capifail = FALSE; + ret = CKR_OK; + } + } + } + } + } + + if(capifail) + ret = p11c_winerr_to_ckr(GetLastError()); + + if(hash) + CryptDestroyHash(hash); + if(prov) + CryptReleaseContext(prov, 0); + + return ret; +} + +void +p11c_rsa_pkcs_sign_cleanup (void* operation) +{ + /* Nothing to do */ +} + + +CK_RV +p11c_rsa_pkcs_decrypt_init(P11CObjectData* keydata, void** operation) +{ + CRYPT_KEY_PROV_INFO* prov_info; + + ASSERT(keydata); + ASSERT(operation); + ASSERT(!*operation); + + prov_info = p11c_key_object_data_get_prov_info(keydata); + if(prov_info->dwProvType != PROV_RSA_FULL) + return CKR_KEY_TYPE_INCONSISTENT; + + *operation = keydata; + return CKR_OK; +} + +CK_RV +p11c_rsa_pkcs_decrypt_perform(CK_BYTE_PTR encdata, CK_ULONG n_encdata, + CK_BYTE_PTR result, CK_ULONG_PTR n_result, + void** operation) +{ + CRYPT_KEY_PROV_INFO* prov_info; + P11CObjectData* keydata; + BOOL capifail; + DWORD bits, error; + CK_RV ret; + + HCRYPTPROV prov = 0; + HCRYPTKEY key = 0; + void* buffer = NULL; + + ASSERT(operation); + ASSERT(*operation); + ASSERT(encdata); + ASSERT(n_encdata); + + keydata = (P11CObjectData*)*operation; + + prov_info = p11c_key_object_data_get_prov_info(keydata); + ASSERT(prov_info); + + /* Calculate the number of bits */ + bits = p11c_key_object_data_get_bits (keydata); + if(!bits) + return CKR_GENERAL_ERROR; + + /* Want to know the length */ + if(!result) + { + *n_result = bits / 8; + return CKR_OK; + } + + /* + * Copy the input, since CAPI operates in place, and + * we must also reverse it properly. + */ + buffer = malloc(n_encdata); + if(!buffer) + return CKR_HOST_MEMORY; + + memcpy(buffer, encdata, n_encdata); + p11c_reverse_memory(buffer, n_encdata); + + capifail = TRUE; + if(CryptAcquireContextW(&prov, prov_info->pwszContainerName, prov_info->pwszProvName, + prov_info->dwProvType, 0)) + { + if(CryptGetUserKey(prov, prov_info->dwKeySpec, &key)) + { + *n_result = n_encdata; + if(CryptDecrypt(key, 0, TRUE, 0, buffer, n_result)) + { + capifail = FALSE; + ret = CKR_OK; + } + } + } + + if(capifail) + { + error = GetLastError(); + switch(error) + { + case NTE_BAD_DATA: + ret = CKR_ENCRYPTED_DATA_INVALID; + default: + ret = p11c_winerr_to_ckr(error); + }; + } + + /* Copy the memory out to the result buffer */ + if(ret == CKR_OK) + ret = p11c_return_data_raw(result, n_result, buffer, *n_result); + + if(key) + CryptDestroyKey(key); + if(prov) + CryptReleaseContext(prov, 0); + if(buffer) + free(buffer); + + return ret; +} + +void +p11c_rsa_pkcs_decrypt_cleanup(void* operation) +{ + /* Nothing to do */ +} + +void +p11c_rsa_pkcs_get_info(CK_MECHANISM_TYPE mech, CK_MECHANISM_INFO_PTR info) +{ + ASSERT(mech == CKM_RSA_PKCS); + ASSERT(info != NULL); + + info->ulMinKeySize = 384; + info->ulMaxKeySize = 16384; + info->flags = 0; +} diff --git a/p11-capi-rsa.h b/p11-capi-rsa.h new file mode 100644 index 0000000..0ce571e --- /dev/null +++ b/p11-capi-rsa.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2008 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef P11C_RSA_H +#define P11C_RSA_H + +#include "p11-capi.h" + +CK_RV p11c_rsa_pkcs_sign_init (P11cObjectData* keydata, void** operation); + +CK_RV p11c_rsa_pkcs_sign_perform (CK_BYTE_PTR data, CK_ULONG data_len, + CK_BYTE_PTR signature, CK_ULONG_PTR signature_len, + void** operation); + +void p11c_rsa_pkcs_sign_cleanup (void* operation); + +CK_RV p11c_rsa_pkcs_decrypt_init (P11cObjectData* keydata, void** operation); + +CK_RV p11c_rsa_pkcs_decrypt_perform (CK_BYTE_PTR encdata, CK_ULONG n_encdata, + CK_BYTE_PTR result, CK_ULONG_PTR n_result, + void** operation); + +void p11c_rsa_pkcs_decrypt_cleanup (void* operation); + +void p11c_rsa_pkcs_get_info (CK_MECHANISM_TYPE mech, + CK_MECHANISM_INFO_PTR info); + +#endif /* P11C_RSA_H */ diff --git a/p11-capi-session.c b/p11-capi-session.c new file mode 100644 index 0000000..e921875 --- /dev/null +++ b/p11-capi-session.c @@ -0,0 +1,983 @@ +/* + * Copyright (C) 2007 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include + +#include "p11-capi.h" +#include "p11-capi-builtin.h" +#include "p11-capi-cert.h" +#include "p11-capi-key.h" +#include "p11-capi-object.h" +#include "p11-capi-rsa.h" +#include "p11-capi-session.h" +#include "p11-capi-token.h" +#include "p11-capi-trust.h" + +/* For operation_type in P11cSession */ +enum +{ + OPERATION_NONE, + OPERATION_FIND, + OPERATION_SIGN, + OPERATION_DECRYPT +}; + +static P11cArray* all_sessions = NULL; + +static void +object_data_release(P11cObjectData* objdata) +{ + ASSERT(objdata->data_funcs); + ASSERT(objdata->data_funcs->release); + (objdata->data_funcs->release)(objdata); +} + +CK_RV +p11c_session_create(CK_SLOT_ID slot, P11cSession** ret) +{ + P11cSession* sess; + const char *store; + DWORD err; + + sess = calloc(1, sizeof(P11cSession)); + if(!sess) + return CKR_HOST_MEMORY; + + sess->object_data = p11c_hash_new(NULL, NULL); + if(!sess->object_data) { + free(sess); + return CKR_HOST_MEMORY; + } + + sess->mutex = CreateMutex(NULL, FALSE, NULL); + if(!sess->mutex) { + p11c_hash_free(sess->object_data, NULL); + free(sess); + return CKR_HOST_MEMORY; + } + + store = p11c_token_get_store_name(slot); + if(store) + { + sess->store = CertOpenSystemStore((HCRYPTPROV)NULL, store); + if(sess->store == NULL) + { + err = GetLastError(); + + /* Store not found, we don't care */ + if(err != ERROR_FILE_NOT_FOUND) + { + p11c_hash_free(sess->object_data, NULL); + CloseHandle(sess->mutex); + free(sess); + return p11c_winerr_to_ckr(err); + } + } + } + + sess->slot = slot; + + DBGS(sess, "created"); + + *ret = sess; + return CKR_OK; +} + +CK_RV +p11c_session_register(P11cSession* sess) +{ + P11cSession* blank = NULL; + CK_SESSION_HANDLE id = 0; + CK_RV ret = CKR_OK; + size_t i; + + ASSERT(sess); + ASSERT(sess->id == 0 && sess->refs == 0); + + DBGS(sess, "registering new session"); + + p11c_lock_global(); + + /* Find a nice session identifier */ + while(id == 0) { + + /* Allocate sessions properly */ + if(!all_sessions) + { + all_sessions = p11c_array_new(0, 1, sizeof(P11cSession*)); + if(!all_sessions) + { + ret = CKR_HOST_MEMORY; + break; + } + + /* A blank entry for '0' */ + p11c_array_append(all_sessions, blank); + + DBG(("allocated new session list")); + } + + /* + * PKCS#11 GRAY AREA: We're assuming we can reuse session + * handles. PKCS#11 spec says they're like file handles, + * and file handles get reused :) + */ + + /* Note we never put anything in array position '0' */ + for(i = 1; i < all_sessions->len; ++i) + { + /* Any empty position will do */ + if(!p11c_array_index(all_sessions, P11cSession*, i)) + { + id = i; + break; + } + } + + /* Couldn't find a handle, append a handle */ + if(id == 0) + { + id = all_sessions->len; + p11c_array_append(all_sessions, blank); + } + } + + if(ret == CKR_OK) + { + ASSERT(id > 0 && id < all_sessions->len); + ASSERT(!p11c_array_index(all_sessions, P11cSession*, id)); + + + /* And assign it to the session handle */ + p11c_array_index(all_sessions, P11cSession*, i) = sess; + sess->id = id; + + /* The session list reference */ + ASSERT(sess->refs == 0); + sess->refs++; + + DBGS(sess, "registered sesson id"); + } + + p11c_unlock_global(); + + return ret; +} + +void +p11c_session_destroy(P11cSession* sess) +{ + ASSERT(sess); + ASSERT(sess->refs == 0); + + /* Ask any pending operations to cleanup */ + if(sess->operation_type) + { + ASSERT(sess->operation_cancel); + (sess->operation_cancel)(sess); + } + + ASSERT(sess->operation_type == 0); + ASSERT(sess->operation_data == NULL); + ASSERT(sess->operation_cancel == NULL); + + if(sess->store) + CertCloseStore(sess->store, 0); + + /* Make all the object adat go away */ + ASSERT(sess->object_data != NULL); + p11c_hash_free(sess->object_data, object_data_release); + + /* And make the mutex go away */ + ASSERT(sess->mutex != NULL); + CloseHandle(sess->mutex); + + DBGS(sess, "destroyed"); + free(sess); +} + +void +p11c_session_get_info(P11cSession* sess, CK_SESSION_INFO_PTR info) +{ + ASSERT(sess); + ASSERT(info); + + info->slotID = sess->slot; + info->flags = CKF_SERIAL_SESSION; + if(sess->read_write) + info->flags |= CKF_RW_SESSION; + + if(p11c_token_is_logged_in(sess->slot)) + info->state = sess->read_write ? CKS_RW_USER_FUNCTIONS : CKS_RO_USER_FUNCTIONS; + else + info->state = sess->read_write ? CKS_RW_PUBLIC_SESSION : CKS_RO_PUBLIC_SESSION; + + /* TODO: We could implement some use of GetLastError() here */ + info->ulDeviceError = 0; +} + +static CK_RV +lock_ref_internal(P11cArray* sessions, CK_SESSION_HANDLE id, + BOOL remove, BOOL writable, P11cSession** sess_ret) +{ + P11cSession *sess; + DWORD r; + + ASSERT(sessions); + ASSERT(sess_ret); + + if(id >= sessions->len) + { + DBG(("invalid session id: %d", id)); + return CKR_SESSION_HANDLE_INVALID; + } + + /* A seemingly valid id */ + ASSERT(sessions); + sess = p11c_array_index(sessions, P11cSession*, id); + + if(!sess) + { + DBG(("session does not exist: %d", id)); + return CKR_SESSION_HANDLE_INVALID; + } + + /* Make sure it's the right kind of session */ + if(writable && !sess->read_write) + return CKR_SESSION_READ_ONLY; + + ASSERT(sess->id == id); + + /* Closing takes precedence over active operations */ + if(!remove) + { + /* + * An initial check is done to make sure this session is not active. + * This is done outside of the lock. The real check is done later + * inside a lock. This is so we can return quickly without blocking + * in most cases. + */ + + if(sess->in_call) + { + DBGS(sess, ("an operation is already active in this session")); + return CKR_OPERATION_ACTIVE; + } + } + + /* Lock the CallP11cSession */ + r = WaitForSingleObject(sess->mutex, INFINITE); + ASSERT(r == WAIT_OBJECT_0); + + /* Do the real check */ + if(!remove && sess->in_call) + { + ReleaseMutex(sess->mutex); + DBGS(sess, ("an operation is already active in this session")); + return CKR_OPERATION_ACTIVE; + } + + /* Make sure it doesn't go away */ + ASSERT(sess->refs > 0); + sess->refs++; + + DBGS(sess, "found and locked session"); + + /* And remove it if necessary */ + if(remove) + { + p11c_array_index(sessions, P11cSession*, id) = NULL; + + /* The session list reference */ + sess->refs--; + ASSERT(sess->refs > 0); + + DBGS(sess, "removed session from list"); + } + else + { + ASSERT(!sess->in_call); + sess->in_call = 1; + } + + *sess_ret = sess; + return CKR_OK; +} + +CK_RV +p11c_session_get_lock_ref(CK_ULONG id, BOOL writable, P11cSession **sess) +{ + /* This must be called without any locks held */ + + CK_RV ret = CKR_OK; + + ASSERT(sess); + + if(id <= 0) + { + DBG(("invalid session id passed: %d", id)); + return CKR_ARGUMENTS_BAD; + } + + p11c_lock_global(); + + ret = lock_ref_internal (all_sessions, id, FALSE, writable, sess); + + p11c_unlock_global(); + + return ret; +} + +CK_RV +p11c_session_remove_lock_ref(CK_ULONG id, P11cSession **sess) +{ + /* This must be called without any locks held */ + + CK_RV ret = CKR_OK; + + ASSERT(sess); + + if(id <= 0) + { + DBG(("invalid session id passed: %d", id)); + return CKR_ARGUMENTS_BAD; + } + + p11c_lock_global(); + + ret = lock_ref_internal (all_sessions, id, TRUE, FALSE, sess); + + p11c_unlock_global(); + + return ret; +} + +void +p11c_session_unref_unlock(P11cSession* sess) +{ + /* The CallP11cSession must be locked at this point */ + + int refs; + BOOL r; + + ASSERT(sess); + + ASSERT(sess->refs > 0); + sess->refs--; + refs = sess->refs; + + sess->in_call = 0; + + DBGS(sess, "unlocked session"); + + r = ReleaseMutex(sess->mutex); + ASSERT(r == TRUE); + + /* + * At this point if no references are held, then we can safely + * delete. No other thread should be involved. + */ + + if(refs == 0) + p11c_session_destroy(sess); +} + +CK_RV +p11c_session_close_all(CK_SLOT_ID slot) +{ + /* This must be called without any locks held */ + + P11cArray* sessions; + P11cSession *sess; + size_t i; + CK_RV ret = CKR_OK; + + /* + * PKCS#11 GRAY AREA: What happens when this gets called + * concurrently? We don't return an error on the second call, + * because by the time it returns, all sessions should be closed. + */ + + DBG(("closing all sessions for: %d", slot)); + + if(!all_sessions) + return CKR_OK; + + p11c_lock_global(); + + sessions = p11c_array_sized_new(0, 1, sizeof(P11cSession*), + all_sessions->len); + if(!sessions) + ret = CKR_HOST_MEMORY; + + /* Steal all the session data */ + if(ret == CKR_OK) + { + for(i = 0; i < all_sessions->len; ++i) + { + sess = p11c_array_index(all_sessions, P11cSession*, i); + if(sess && (slot == ((CK_SLOT_ID)-1) || sess->slot == slot)) + { + /* Steal this session */ + p11c_array_index(all_sessions, P11cSession*, i) = NULL; + } + else + { + /* Not a session we're interested in */ + sess = NULL; + } + + /* Both null and normal sessions are set to preserve indexes */ + p11c_array_append(sessions, sess); + } + + ASSERT(sessions->len == all_sessions->len); + } + + p11c_unlock_global(); + + if(ret != CKR_OK) + return ret; + + /* Close each session in turn */ + for(i = 0; i < sessions->len; ++i) + { + if(!p11c_array_index(sessions, P11cSession*, i)) + continue; + + /* We need any calls in other threads to finish, so wait here */ + if(lock_ref_internal(sessions, i, TRUE, FALSE, &sess) == CKR_OK) + p11c_session_unref_unlock(sess); + } + + /* We stole the memory above, free it now */ + p11c_array_free(sessions, 1); + return CKR_OK; +} + +void +p11c_session_cleanup_all() +{ + p11c_session_close_all((CK_SLOT_ID)-1); + + p11c_lock_global(); + + p11c_array_free(all_sessions, 1); + all_sessions = NULL; + + p11c_unlock_global(); +} + +/* ---------------------------------------------------------------------------- + * OBJECT DATA + */ + +CK_RV +p11c_session_get_object_data(P11cSession* sess, P11cObject* obj, + P11cObjectData** objdata) +{ + CK_OBJECT_HANDLE id; + P11cObjectData* newdata; + CK_RV ret; + + ASSERT(sess); + ASSERT(sess->object_data); + ASSERT(obj); + ASSERT(obj->obj_funcs); + ASSERT(obj->obj_funcs->load_data); + ASSERT(objdata); + + id = obj->id; + + *objdata = p11c_hash_get(sess->object_data, p11c_hash_key(id)); + if(*objdata) + return CKR_OK; + + ret = (obj->obj_funcs->load_data)(sess, obj, &newdata); + if(ret != CKR_OK) + return ret; + + newdata->object = id; + ASSERT(newdata->data_funcs); + + if(!p11c_hash_set(sess->object_data, p11c_hash_key(id), newdata)) + { + object_data_release(newdata); + return CKR_HOST_MEMORY; + } + + *objdata = newdata; + return CKR_OK; +} + +void +p11c_session_clear_object_data(P11cSession* sess, P11cObject* obj) +{ + P11cObjectData* objdata; + + ASSERT(sess); + ASSERT(sess->object_data); + ASSERT(obj); + + objdata = (P11cObjectData*)p11c_hash_rem(sess->object_data, p11c_hash_key(obj->id)); + if(objdata) + object_data_release(objdata); +} + +void +p11c_session_enum_object_data(P11cSession* sess, + P11cEnumObjectData enum_func, void* arg) +{ + CK_OBJECT_HANDLE i, max; + P11cObject* obj; + P11cObjectData* objdata; + + ASSERT(sess); + ASSERT(sess->object_data); + ASSERT(enum_func); + + max = p11c_token_get_max_handle(); + for(i = 0; i < max; ++i) + { + objdata = (P11cObjectData*)p11c_hash_get(sess->object_data, p11c_hash_key(i)); + if(!objdata) + continue; + + obj = p11c_token_lookup_object(sess->slot, i); + if(!obj) + continue; + + (enum_func)(sess, obj, objdata, arg); + } +} + +CK_RV +p11c_session_get_object_data_for(P11cSession* sess, CK_OBJECT_HANDLE hand, + P11cObjectData** objdata) +{ + P11cObject* obj; + + obj = p11c_token_lookup_object(sess->slot, hand); + if(!obj) + return CKR_OBJECT_HANDLE_INVALID; + + return p11c_session_get_object_data(sess, obj, objdata); +} + +void +p11c_session_take_object_data(P11cSession* sess, P11cObject* obj, + P11cObjectData* objdata) +{ + P11cObjectData* prev; + + ASSERT(obj); + ASSERT(sess); + ASSERT(sess->object_data); + + ASSERT(objdata); + objdata->object = obj->id; + + prev = p11c_hash_rem(sess->object_data, p11c_hash_key(obj->id)); + if(prev) + object_data_release(prev); + + if(!p11c_hash_set(sess->object_data, p11c_hash_key(obj->id), objdata)) + object_data_release(objdata); +} + + +/* ---------------------------------------------------------------------------- + * FIND OPERATION + */ + +static BOOL +get_ulong_attribute(CK_ATTRIBUTE_TYPE type, CK_ATTRIBUTE_PTR templ, + CK_ULONG count, CK_ULONG* val) +{ + CK_ULONG i; + + ASSERT(val); + ASSERT(!count || templ); + + for(i = 0; i < count; ++i) + { + if(templ[i].type == type) + { + *val = *((CK_ULONG*)templ[i].pValue); + return TRUE; + } + } + + return FALSE; +} + +static CK_RV +gather_objects(P11cSession* sess, CK_ATTRIBUTE_PTR match, + CK_ULONG count, P11cArray* arr) +{ + CK_OBJECT_CLASS ocls = CKO_ANY; + CK_RV ret = CKR_OK; + + get_ulong_attribute(CKA_CLASS, match, count, &ocls); + + /* Search for builtins */ + ret = p11c_builtin_find(sess, ocls, match, count, arr); + if(ret != CKR_OK) + return ret; + + /* + * Search through certificates. + * + * We always do this search first. In Windows a lots hangs off + * the certificates. For example private keys are not contained + * in the same stores that certificates are in. There are a different + * set of key containers many of which can be used together + * with a certificate stored in any store. + * + * The trust objects we expose also depend on the certificates + * loaded. + */ + ret = p11c_cert_find(sess, ocls, match, count, arr); + if(ret != CKR_OK) + return ret; + + /* Search through trust objects */ + ret = p11c_trust_find(sess, ocls, match, count, arr); + if(ret != CKR_OK) + return ret; + + /* Search through key objects */ + ret = p11c_key_find(sess, ocls, match, count, arr); + if(ret != CKR_OK) + return ret; + + return ret; +} + +void +cleanup_find_operation(P11cSession* sess) +{ + ASSERT(sess->operation_type == OPERATION_FIND); + if(sess->operation_data) + p11c_array_free((P11cArray*)sess->operation_data, TRUE); + sess->operation_type = OPERATION_NONE; + sess->operation_data = NULL; + sess->operation_cancel = NULL; +} + +void +purge_duplicate_objects(P11cArray* arr) +{ + P11cHash* checks; + CK_OBJECT_HANDLE v; + size_t i; + + checks = p11c_hash_new(NULL, NULL); + if(!checks) + return; + + for(i = 0; i < arr->len; ) + { + v = p11c_array_index(arr, CK_OBJECT_HANDLE, i); + if(p11c_hash_get(checks, p11c_hash_key(v))) + { + p11c_array_remove_index(arr, i); + /* Look at same i again */ + } + else + { + if(!p11c_hash_set(checks, p11c_hash_key(v), arr)) + break; + ++i; + } + } + + p11c_hash_free(checks, NULL); +} + +CK_RV +p11c_session_find_init(P11cSession* sess, CK_ATTRIBUTE_PTR match, + CK_ULONG count) +{ + P11cArray* arr; + CK_RV ret; + + ASSERT(sess); + ASSERT(!count || match); + + if(sess->operation_type != OPERATION_NONE) + return CKR_OPERATION_ACTIVE; + + arr = p11c_array_new(0, 1, sizeof(CK_OBJECT_HANDLE)); + if(!arr) + return CKR_HOST_MEMORY; + + ret = gather_objects(sess, match, count, arr); + if(ret != CKR_OK) + { + p11c_array_free(arr, TRUE); + return ret; + } + + /* Cleanup all duplicates in the array */ + purge_duplicate_objects(arr); + + sess->operation_type = OPERATION_FIND; + sess->operation_data = arr; + sess->operation_cancel = cleanup_find_operation; + + return CKR_OK; +} + +CK_RV +p11c_session_find(P11cSession* sess, CK_OBJECT_HANDLE_PTR objects, + CK_ULONG max_object_count, CK_ULONG_PTR object_count) +{ + P11cArray* arr; + size_t i; + + ASSERT(sess); + ASSERT(object_count); + ASSERT(!max_object_count || objects); + + if(sess->operation_type != OPERATION_FIND) + return CKR_OPERATION_NOT_INITIALIZED; + + if(!max_object_count) + { + *object_count = 0; + return CKR_OK; + } + + arr = (P11cArray*)sess->operation_data; + *object_count = (max_object_count > arr->len ? arr->len : max_object_count); + for(i = 0; i < *object_count; ++i) + objects[i] = p11c_array_index(arr, CK_OBJECT_HANDLE, i); + + p11c_array_remove_range(arr, 0, *object_count); + + return CKR_OK; +} + +CK_RV +p11c_session_find_final(P11cSession* sess) +{ + ASSERT(sess); + + if(sess->operation_type != OPERATION_FIND) + return CKR_OPERATION_NOT_INITIALIZED; + + cleanup_find_operation(sess); + return CKR_OK; +} + + +/* ---------------------------------------------------------------------------- + * CRYPTO OPERATIONS + */ + +typedef struct _CryptoContext +{ + CK_MECHANISM_TYPE mech_type; + P11cDestroyFunc mech_cleanup; + void* mech_data; +} +CryptoContext; + +void +cleanup_crypto_operation(P11cSession* sess) +{ + CryptoContext* ctx; + + if(sess->operation_data) + { + ctx = (CryptoContext*)sess->operation_data; + if(ctx->mech_cleanup) + (ctx->mech_cleanup)(ctx->mech_data); + free(ctx); + } + + sess->operation_type = OPERATION_NONE; + sess->operation_data = NULL; + sess->operation_cancel = NULL; +} + +CK_RV +p11c_session_sign_init(P11cSession* sess, CK_MECHANISM_PTR mech, + P11cObjectData *objdata) +{ + CryptoContext* ctx; + CK_RV ret; + + ASSERT(sess); + ASSERT(mech); + ASSERT(objdata); + + if(sess->operation_type != OPERATION_NONE) + return CKR_OPERATION_ACTIVE; + + ctx = calloc(1, sizeof(CryptoContext)); + if(!ctx) + return CKR_HOST_MEMORY; + + ctx->mech_type = mech->mechanism; + + switch(mech->mechanism) + { + case CKM_RSA_PKCS: + ret = p11c_rsa_pkcs_sign_init(objdata, &ctx->mech_data); + ctx->mech_cleanup = p11c_rsa_pkcs_sign_cleanup; + break; + default: + ret = CKR_MECHANISM_INVALID; + break; + }; + + if(ret != CKR_OK) + { + free(ctx); + ASSERT(!sess->operation_data); + return ret; + } + + sess->operation_type = OPERATION_SIGN; + sess->operation_data = ctx; + sess->operation_cancel = cleanup_crypto_operation; + return CKR_OK; +} + +CK_RV +p11c_session_sign(P11cSession* sess, CK_BYTE_PTR data, CK_ULONG n_data, + CK_BYTE_PTR signature, CK_ULONG_PTR n_signature) +{ + CryptoContext *ctx; + BOOL incomplete; + CK_RV ret; + + ASSERT(sess); + ASSERT(data); + ASSERT(n_data); + + if(sess->operation_type != OPERATION_SIGN) + return CKR_OPERATION_NOT_INITIALIZED; + + ctx = (CryptoContext*)sess->operation_data; + switch(ctx->mech_type) + { + case CKM_RSA_PKCS: + ret = p11c_rsa_pkcs_sign_perform(data, n_data, signature, n_signature, + &ctx->mech_data); + break; + + default: + ASSERT(FALSE); + ret = CKR_GENERAL_ERROR; + break; + } + + /* Buffer calculation, we don't end operation */ + incomplete = (ret == CKR_BUFFER_TOO_SMALL || (ret == CKR_OK && !signature)); + + if(!incomplete) + cleanup_crypto_operation(sess); + + return ret; +} + +CK_RV +p11c_session_decrypt_init(P11cSession* sess, CK_MECHANISM_PTR mech, + P11cObjectData *objdata) +{ + CryptoContext* ctx; + CK_RV ret; + + ASSERT(sess); + ASSERT(mech); + ASSERT(objdata); + + if(sess->operation_type != OPERATION_NONE) + return CKR_OPERATION_ACTIVE; + + ctx = calloc(1, sizeof(CryptoContext)); + if(!ctx) + return CKR_HOST_MEMORY; + + ctx->mech_type = mech->mechanism; + + switch(mech->mechanism) + { + case CKM_RSA_PKCS: + ret = p11c_rsa_pkcs_decrypt_init(objdata, &ctx->mech_data); + ctx->mech_cleanup = p11c_rsa_pkcs_decrypt_cleanup; + break; + default: + ret = CKR_MECHANISM_INVALID; + break; + }; + + if(ret != CKR_OK) + { + free(ctx); + ASSERT(!sess->operation_data); + return ret; + } + + sess->operation_type = OPERATION_DECRYPT; + sess->operation_data = ctx; + sess->operation_cancel = cleanup_crypto_operation; + return CKR_OK; +} + +CK_RV +p11c_session_decrypt(P11cSession* sess, CK_BYTE_PTR encdata, CK_ULONG n_encdata, + CK_BYTE_PTR result, CK_ULONG_PTR n_result) +{ + CryptoContext *ctx; + BOOL incomplete; + CK_RV ret; + + ASSERT(sess); + ASSERT(encdata); + ASSERT(n_encdata); + + if(sess->operation_type != OPERATION_DECRYPT) + return CKR_OPERATION_NOT_INITIALIZED; + + ctx = (CryptoContext*)sess->operation_data; + switch(ctx->mech_type) + { + case CKM_RSA_PKCS: + ret = p11c_rsa_pkcs_decrypt_perform(encdata, n_encdata, result, n_result, + &ctx->mech_data); + break; + + default: + ASSERT(FALSE); + ret = CKR_GENERAL_ERROR; + break; + } + + /* Buffer calculation, we don't end operation */ + incomplete = (ret == CKR_BUFFER_TOO_SMALL || (ret == CKR_OK && !result)); + + if(!incomplete) + cleanup_crypto_operation(sess); + + return ret; +} diff --git a/p11-capi-session.h b/p11-capi-session.h new file mode 100644 index 0000000..8f84026 --- /dev/null +++ b/p11-capi-session.h @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2007 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef P11C_SESSION_H +#define P11C_SESSION_H + +#include "p11-capi.h" + +/* -------------------------------------------------------------------- + * + * Session = P11cSession + * - A PKCS#11 Session + * + * Objects = P11cObject + * - There's a global list of objects in p11c-object.c indexed by + * object handle. + * - The object itself has no attributes or cached data, but knows how + * to load data when needed. + * - Each object has a unique key which guarantees we don't load the + * same object twice with two different object handles. + * + * Object Data = P11cObjectData + * - Object Data is owned by the Session + * - Loaded data and/or attributes for an object. + */ + +/* Callback to cleanup a current operation */ +typedef void (*P11cSessionCancel) (struct _P11cSession* sess); + +/* Represents an open session */ +typedef struct _P11cSession +{ + CK_SESSION_HANDLE id; /* Unique ID for this session */ + CK_SLOT_ID slot; + int in_call; /* Whether this session is use in PKCS#11 function */ + + HCERTSTORE store; /* Handle to an open certificate store */ + + BOOL read_write; /* A read-write session? */ + + int operation_type; /* Whether an operation is happening or not */ + void* operation_data; /* Data for this operation */ + P11cSessionCancel operation_cancel; /* Callback to cancel operation when necessary */ + + P11cHash* object_data; + + CK_NOTIFY notify_callback; /* Application specified callback */ + CK_VOID_PTR user_data; /* Argument for above */ + + int refs; /* Reference count */ + HANDLE mutex; /* Mutex for protecting this structure */ +} +P11cSession; + +/* Debug print something related to a session */ +#define DBGS(sess, msg) \ + p11c_debug("S%d: %s", (sess) ? (sess)->id : 0, (msg)) + +/* Create a session */ +CK_RV p11c_session_create (CK_SLOT_ID slot, P11cSession** ret); + +/* Destroy a session */ +void p11c_session_destroy (P11cSession* sess); + +/* Register a new session */ +CK_RV p11c_session_register (P11cSession* sess); + +/* Get information about a session */ +void p11c_session_get_info (P11cSession* sess, + CK_SESSION_INFO_PTR info); + +/* Get a session from a handle, and lock it */ +CK_RV p11c_session_get_lock_ref (CK_ULONG id, BOOL writable, + P11cSession **sess); + +/* Get a session from a handle, remove it from list, and lock it */ +CK_RV p11c_session_remove_lock_ref (CK_ULONG id, P11cSession **sess); + +/* Unlock and unreference a session */ +void p11c_session_unref_unlock (P11cSession* sess); + +/* Close all sessions on a certain slot/token */ +CK_RV p11c_session_close_all (CK_SLOT_ID slot); + + + +/* Start a find operation on a session */ +CK_RV p11c_session_find_init (P11cSession* sess, + CK_ATTRIBUTE_PTR templ, + CK_ULONG count); + +/* Return results from a find operation */ +CK_RV p11c_session_find (P11cSession* sess, + CK_OBJECT_HANDLE_PTR objects, + CK_ULONG max_object_count, + CK_ULONG_PTR object_count); + +/* End a find operation */ +CK_RV p11c_session_find_final (P11cSession* sess); + + +/* Start a sign operation on a session */ +CK_RV p11c_session_sign_init (P11cSession* sess, + CK_MECHANISM_PTR mech, + P11cObjectData *objdata); + +/* Perform sign operation */ +CK_RV p11c_session_sign (P11cSession* sess, + CK_BYTE_PTR data, CK_ULONG n_data, + CK_BYTE_PTR sig, CK_ULONG_PTR n_sig); + +/* Start a decrypt operation on a session */ +CK_RV p11c_session_decrypt_init (P11cSession* sess, + CK_MECHANISM_PTR mech, + P11cObjectData *objdata); + +/* Perform decrypt operation */ +CK_RV p11c_session_decrypt (P11cSession* sess, + CK_BYTE_PTR encdata, CK_ULONG n_encdata, + CK_BYTE_PTR result, CK_ULONG_PTR n_result); + +/* Get object data for an object */ +CK_RV p11c_session_get_object_data (P11cSession* sess, + P11cObject* obj, + P11cObjectData** objdata); + +/* Get object data for an object handle */ +CK_RV p11c_session_get_object_data_for (P11cSession* sess, + CK_OBJECT_HANDLE hand, + P11cObjectData** objdata); + +/* Set object data for an object */ +void p11c_session_take_object_data (P11cSession* sess, + P11cObject* obj, + P11cObjectData* objdata); + +/* Clear object data for an object */ +void p11c_session_clear_object_data (P11cSession* sess, + P11cObject* obj); + +/* Enumerate object data for all objects */ +typedef void (*P11cEnumObjectData) (P11cSession* sess, + P11cObject* obj, + P11cObjectData* data, + void* arg); + +void p11c_session_enum_object_data (P11cSession* sess, + P11cEnumObjectData enum_func, + void* arg); + +void p11c_session_cleanup_all (void); + +#endif /* P11C_SESSION_H */ diff --git a/p11-capi-token.c b/p11-capi-token.c new file mode 100644 index 0000000..13a87d6 --- /dev/null +++ b/p11-capi-token.c @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2007 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "p11-capi.h" +#include "p11-capi-object.h" +#include "p11-capi-token.h" + +static P11cArray* object_array = NULL; +static P11cHash* object_hash = NULL; +static P11cArray* logged_in_slots = NULL; + +typedef struct _SlotInfo +{ + const char* capi_store; + const char* display_name; + CK_ULONG slot_flags; +} +SlotInfo; + +#define SLOT_OFFSET 0x00001000 + +static SlotInfo slot_info[] = { + { "My", "Personal Certificates", P11c_SLOT_TRUSTED | P11c_SLOT_CERTS }, + { "AddressBook", "Address Book Certificates", P11c_SLOT_CERTS }, + { "CA", "Certificate Authorities", P11c_SLOT_CA | P11c_SLOT_CERTS }, + { "Root", "Root Authorities", P11c_SLOT_TRUSTED | P11c_SLOT_CA | P11c_SLOT_CERTS }, + { "Trust", "Trust", P11c_SLOT_CERTS }, + { "TrustedPeople", "Trusted People", P11c_SLOT_TRUSTED | P11c_SLOT_CERTS }, + { "AuthRoot", "Auth Root", P11c_SLOT_CERTS }, + { NULL, "All User Keys", P11c_SLOT_ANYKEY } +}; + +#define SLOT_TO_OFFSET(slot) \ + ((slot) & ~(SLOT_OFFSET)) + +#define OFFSET_TO_SLOT(offset) \ + ((offset) | SLOT_OFFSET) + +unsigned int +p11c_token_get_count(void) +{ + return sizeof(slot_info) / sizeof(slot_info[0]); +} + +CK_SLOT_ID +p11c_token_get_slot_id(unsigned int offset) +{ + ASSERT(offset < p11c_token_get_count()); + return OFFSET_TO_SLOT(offset); +} + +CK_BBOOL +p11c_token_is_valid(CK_SLOT_ID slot) +{ + unsigned int offset = SLOT_TO_OFFSET(slot); + return offset >= 0 && offset < p11c_token_get_count(); +} + +const char* +p11c_token_get_display_name(CK_SLOT_ID slot) +{ + unsigned int offset = SLOT_TO_OFFSET(slot); + ASSERT(p11c_token_is_valid(slot)); + ASSERT(slot_info[offset].display_name); + return slot_info[offset].display_name; +} + +const char* +p11c_token_get_store_name(CK_SLOT_ID slot) +{ + unsigned int offset = SLOT_TO_OFFSET(slot); + ASSERT(p11c_token_is_valid(slot)); + return slot_info[offset].capi_store; +} + +CK_ULONG +p11c_token_get_flags(CK_SLOT_ID slot) +{ + unsigned int offset = SLOT_TO_OFFSET(slot); + ASSERT(p11c_token_is_valid(slot)); + return slot_info[offset].slot_flags; +} + +static void +object_free(P11cObject* obj) +{ + ASSERT(obj); + ASSERT(obj->obj_funcs); + ASSERT(obj->obj_funcs->release); + (obj->obj_funcs->release)(obj); +} + +void +p11c_token_cleanup_all(void) +{ + size_t i; + + p11c_lock_global(); + + if(object_hash) + { + p11c_hash_free(object_hash, NULL); + object_hash = NULL; + } + + if(object_array) + { + for(i = 1; i < object_array->len; ++i) + { + ASSERT(p11c_array_index(object_array, P11cObject*, i)); + object_free(p11c_array_index(object_array, P11cObject*, i)); + } + + p11c_array_free(object_array, TRUE); + object_array = NULL; + } + + if(logged_in_slots) + { + p11c_array_free(logged_in_slots, TRUE); + logged_in_slots = NULL; + } + + p11c_unlock_global(); +} + +CK_OBJECT_HANDLE +p11c_token_get_max_handle(void) +{ + if(!object_array) + return 0; + return object_array->len; +} + +P11cObject* +p11c_token_lookup_object(CK_SLOT_ID slot, CK_OBJECT_HANDLE obj) +{ + /* This must be called without any locks held */ + + P11cObject* ret = NULL; + + ASSERT(slot); + ASSERT(obj > 0); + + p11c_lock_global(); + + if(object_array && obj < object_array->len) + ret = p11c_array_index(object_array, P11cObject*, obj); + + p11c_unlock_global(); + + /* Must belong to the right slot */ + if(ret && ret->slot != slot) + ret = NULL; + + return ret; +} + +static unsigned int +object_hash_func(const void* a) +{ + P11cObject* obj = (P11cObject*)a; + unsigned int hash = p11c_hash_pointer(obj->obj_funcs); + hash ^= (obj->obj_funcs->hash_object)(obj); + return hash; +} + +static int +object_equal_func(const void* a, const void* b) +{ + P11cObject* ca = (P11cObject*)a; + P11cObject* cb = (P11cObject*)b; + if(ca == cb) + return 1; + if(ca->obj_funcs != cb->obj_funcs) + return 0; + return (ca->obj_funcs->equal_object)(ca, cb); +} + +CK_RV +p11c_token_register_object(CK_SLOT_ID slot, P11cObject* obj) +{ + P11cObject* prev; + CK_RV ret = CKR_OK; + + ASSERT(slot); + ASSERT(obj->id == 0); + + DBG(("registering object")); + + p11c_lock_global(); + + if(!object_array) + { + object_array = p11c_array_sized_new(0, 1, sizeof(P11cObject*), 16); + if(object_array) + { + /* A blank entry for '0' */ + P11cObject* blank = NULL; + p11c_array_append(object_array, blank); + } + + object_hash = p11c_hash_new(object_hash_func, object_equal_func); + + if(!object_array || !object_hash) + { + /* Allocation failed above */ + ret = CKR_HOST_MEMORY; + } + } + + if(ret == CKR_OK) + { + ASSERT(object_array); + ASSERT(object_hash); + + /* Look in the hash and find a previous object */ + prev = p11c_hash_get(object_hash, obj); + if(prev) + { + /* Register it in the previous object's place */ + obj->id = prev->id; + ASSERT(prev->id < object_array->len); + if(p11c_hash_set(object_hash, obj, obj)) + { + p11c_array_index(object_array, P11cObject*, obj->id) = obj; + object_free(prev); + DBGO(obj, "found old object id"); + } + else + { + ret = CKR_HOST_MEMORY; + } + } + else + { + /* Register it at the end of the array */ + obj->id = object_array->len; + ASSERT(obj->id > 0); + if(p11c_hash_set(object_hash, obj, obj)) + { + if(p11c_array_append(object_array, obj)) + { + DBGO(obj, "registered new object id"); + } + else + { + ret = CKR_HOST_MEMORY; + + /* Roll back our addition */ + p11c_hash_rem(object_hash, obj); + } + } + else + { + ret = CKR_HOST_MEMORY; + } + } + } + + if(ret == CKR_OK) + obj->slot = slot; + + p11c_unlock_global(); + + return ret; + +} + +CK_BBOOL +p11c_token_is_logged_in(CK_SLOT_ID slot) +{ + unsigned int count, offset; + + ASSERT(p11c_token_is_valid(slot)); + + if(!logged_in_slots) + return CK_FALSE; + + offset = SLOT_TO_OFFSET(slot); + count = p11c_token_get_count(); + + ASSERT(logged_in_slots->len == count && offset < count); + return p11c_array_index(logged_in_slots, CK_BBOOL, offset); +} + +CK_RV +p11c_token_login(CK_SLOT_ID slot) +{ + unsigned int i, count; + unsigned int offset; + CK_BBOOL value; + + ASSERT(p11c_token_is_valid(slot)); + + offset = SLOT_TO_OFFSET(slot); + count = p11c_token_get_count(); + + if(!logged_in_slots) + { + logged_in_slots = p11c_array_sized_new(0, 1, sizeof(CK_BBOOL), count); + if(!logged_in_slots) + return CKR_HOST_MEMORY; + + value = CK_FALSE; + for(i = 0; i < count; ++i) + p11c_array_append(logged_in_slots, value); + + } + + ASSERT(logged_in_slots->len == count && offset < count); + if(p11c_array_index(logged_in_slots, CK_BBOOL, offset)) + return CKR_USER_ALREADY_LOGGED_IN; + + p11c_array_index(logged_in_slots, CK_BBOOL, offset) = CK_TRUE; + return CKR_OK; +} + +CK_RV +p11c_token_logout(CK_SLOT_ID slot) +{ + unsigned int count, offset; + + ASSERT(p11c_token_is_valid(slot)); + + if(!logged_in_slots) + return CKR_USER_NOT_LOGGED_IN; + + offset = SLOT_TO_OFFSET(slot); + count = p11c_token_get_count(); + + ASSERT(logged_in_slots->len == count && offset < count); + if(!p11c_array_index(logged_in_slots, CK_BBOOL, offset)) + return CKR_USER_NOT_LOGGED_IN; + + p11c_array_index(logged_in_slots, CK_BBOOL, offset) = CK_FALSE; + return CKR_OK; +} diff --git a/p11-capi-token.h b/p11-capi-token.h new file mode 100644 index 0000000..e97f547 --- /dev/null +++ b/p11-capi-token.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2007 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef P11C_TOKEN_H +#define P11C_TOKEN_H + +#include "p11-capi.h" + +#define P11C_SLOT_CERTS 0x00000001 +#define P11C_SLOT_ANYKEY 0x00000002 +#define P11C_SLOT_CA 0x00000100 +#define P11C_SLOT_TRUSTED 0x00000200 + +/* Register a new object, a handle will be assigned to obj->id */ +CK_RV p11c_token_register_object (CK_SLOT_ID slot, P11cObject* obj); + +/* Lookup an object for a given object handle */ +P11cObject* p11c_token_lookup_object (CK_SLOT_ID slot, CK_OBJECT_HANDLE obj); + +/* Clear all objects for all tokens. Only done when finalizing */ +void p11c_token_cleanup_all (void); + +/* Get the number of the maximum object handle currently in memory */ +CK_OBJECT_HANDLE p11c_token_get_max_handle (void); + +unsigned int p11c_token_get_count (void); + +CK_SLOT_ID p11c_token_get_slot_id (unsigned int index); + +CK_BBOOL p11c_token_is_valid (CK_SLOT_ID slot); + +const char* p11c_token_get_display_name (CK_SLOT_ID slot); + +const char* p11c_token_get_store_name (CK_SLOT_ID slot); + +CK_ULONG p11c_token_get_flags (CK_SLOT_ID slot); + +CK_RV p11c_token_login (CK_SLOT_ID slot); + +CK_RV p11c_token_logout (CK_SLOT_ID slot); + +CK_BBOOL p11c_token_is_logged_in (CK_SLOT_ID slot); + +#endif /* P11C_TOKEN_H */ diff --git a/p11-capi-trust.c b/p11-capi-trust.c new file mode 100644 index 0000000..e8bab5f --- /dev/null +++ b/p11-capi-trust.c @@ -0,0 +1,569 @@ +/* + * Copyright (C) 2007 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "p11-capi.h" +#include "p11-capi-cert.h" +#include "p11-capi-object.h" +#include "p11-capi-session.h" +#include "p11-capi-token.h" +#include "p11-capi-trust.h" +#include "x509-usages.h" + +#include "pkcs11/pkcs11n.h" + +/* + * These are the attributes expected by NSS on a trust object: + * + * CKA_CLASS + * CKA_TOKEN + * CKA_LABEL + * CKA_CERT_SHA1_HASH + * CKA_CERT_MD5_HASH + * CKA_ISSUER + * CKA_SUBJECT + * CKA_TRUST_SERVER_AUTH + * CKA_TRUST_CLIENT_AUTH + * CKA_TRUST_EMAIL_PROTECTION + * CKA_TRUST_CODE_SIGNING + * CKA_SERIAL_NUMBER + */ + +typedef struct _TrustObject +{ + P11cObject obj; + CK_OBJECT_HANDLE cert_obj; +} +TrustObject; + +typedef struct _TrustObjectData +{ + P11cObjectData base; + + PCCERT_CONTEXT cert; + CERT_ENHKEY_USAGE* enhanced_usage; + + BOOL has_usage; + BYTE usage; +} +TrustObjectData; + +static CK_TRUST +has_usage(TrustObjectData* tdata, BYTE restriction) +{ + if(!tdata->has_usage) + CKT_NETSCAPE_TRUST_UNKNOWN; + if((tdata->usage & restriction) == restriction) + return CKT_NETSCAPE_TRUSTED; + return CKT_NETSCAPE_UNTRUSTED; + +} + +static CK_TRUST +has_enhanced_usage(TrustObjectData* tdata, const char* oid) +{ + CERT_ENHKEY_USAGE* eusage = tdata->enhanced_usage; + DWORD i; + + /* No usages, means anything goes */ + if(eusage == NULL) + return CKT_NETSCAPE_TRUSTED_DELEGATOR; + + for(i = 0; i < eusage->cUsageIdentifier; ++i) + { + if(eusage->rgpszUsageIdentifier[i] && + strcmp(oid, eusage->rgpszUsageIdentifier[i]) == 0) + return CKT_NETSCAPE_TRUSTED_DELEGATOR; + } + + return CKT_NETSCAPE_TRUST_UNKNOWN; +} + +static CK_RV +trust_bool_attribute(P11cObjectData* objdata, CK_ATTRIBUTE_PTR attr) +{ + CK_BBOOL val; + + ASSERT(objdata); + ASSERT(attr); + + switch(attr->type) + { + /* + * Resides on the token + * - Always true for CAPI objects. + */ + case CKA_TOKEN: + val = CK_TRUE; + break; + + /* + * Private vs. Public object. + * - Always false for certificates. + */ + case CKA_PRIVATE: + val = CK_FALSE; + break; + + /* + * If object can be modified. + */ + case CKA_MODIFIABLE: + val = CK_TRUE; + break; + + /* + * TODO: Figure out what this is. + */ + case CKA_TRUST_STEP_UP_APPROVED: + val = CK_FALSE; + break; + + default: + return CKR_ATTRIBUTE_TYPE_INVALID; + }; + + return p11c_return_data(attr, &val, sizeof(CK_BBOOL)); +} + +static CK_RV +trust_ulong_attribute(P11cObjectData* objdata, CK_ATTRIBUTE_PTR attr) +{ + TrustObjectData* tdata = (TrustObjectData*)objdata; + CK_ULONG val; + + ASSERT(tdata); + ASSERT(attr); + + switch(attr->type) + { + + /* + * Object class. + * - Always CKO_NETSCAPE_TRUST for netscape trust + */ + case CKA_CLASS: + val = CKO_NETSCAPE_TRUST; + break; + + /* + * Key restrictions + */ + case CKA_TRUST_DIGITAL_SIGNATURE: + val = has_usage(tdata, CERT_DIGITAL_SIGNATURE_KEY_USAGE); + break; + case CKA_TRUST_NON_REPUDIATION: + val = has_usage(tdata, CERT_NON_REPUDIATION_KEY_USAGE); + break; + case CKA_TRUST_KEY_ENCIPHERMENT: + val = has_usage(tdata, CERT_KEY_ENCIPHERMENT_KEY_USAGE); + break; + case CKA_TRUST_DATA_ENCIPHERMENT: + val = has_usage(tdata, CERT_DATA_ENCIPHERMENT_KEY_USAGE); + break; + case CKA_TRUST_KEY_AGREEMENT: + val = has_usage(tdata, CERT_KEY_AGREEMENT_KEY_USAGE); + break; + case CKA_TRUST_KEY_CERT_SIGN: + val = has_usage(tdata, CERT_KEY_CERT_SIGN_KEY_USAGE); + break; + case CKA_TRUST_CRL_SIGN: + val = has_usage(tdata, CERT_CRL_SIGN_KEY_USAGE); + break; + + /* + * Various trust flags + */ + case CKA_TRUST_SERVER_AUTH: + val = has_enhanced_usage(tdata, X509_USAGE_SERVER_AUTH); + break; + case CKA_TRUST_CLIENT_AUTH: + val = has_enhanced_usage(tdata, X509_USAGE_CLIENT_AUTH); + break; + case CKA_TRUST_CODE_SIGNING: + val = has_enhanced_usage(tdata, X509_USAGE_CODE_SIGNING); + break; + case CKA_TRUST_EMAIL_PROTECTION: + val = has_enhanced_usage(tdata, X509_USAGE_EMAIL); + break; + case CKA_TRUST_IPSEC_END_SYSTEM: + val = has_enhanced_usage(tdata, X509_USAGE_IPSEC_ENDPOINT); + break; + case CKA_TRUST_IPSEC_TUNNEL: + val = has_enhanced_usage(tdata, X509_USAGE_IPSEC_TUNNEL); + break; + case CKA_TRUST_IPSEC_USER: + val = has_enhanced_usage(tdata, X509_USAGE_IPSEC_USER); + break; + case CKA_TRUST_TIME_STAMPING: + val = has_enhanced_usage(tdata, X509_USAGE_TIME_STAMPING); + break; + + default: + return CKR_ATTRIBUTE_TYPE_INVALID; + }; + + return p11c_return_data(attr, &val, sizeof(CK_ULONG)); +} + +static CK_RV +trust_bytes_attribute(P11cObjectData* objdata, CK_ATTRIBUTE_PTR attr) +{ + TrustObjectData* tdata = (TrustObjectData*)objdata; + + ASSERT(tdata); + ASSERT(attr); + + switch(attr->type) + { + /* + * Forward these through to the certificate itself. + */ + case CKA_SUBJECT: + case CKA_ISSUER: + case CKA_SERIAL_NUMBER: + case CKA_LABEL: + ASSERT(tdata->cert); + return p11c_cert_certificate_get_bytes(tdata->cert, attr); + + /* + * The hash of the DER encoded certificate. + */ + case CKA_CERT_MD5_HASH: + case CKA_CERT_SHA1_HASH: + if(!CryptHashCertificate(0, attr->type == CKA_CERT_MD5_HASH ? CALG_MD5 : CALG_SHA1, + 0, tdata->cert->pbCertEncoded, + tdata->cert->cbCertEncoded, attr->pValue, + (DWORD*)(&attr->ulValueLen))) + return p11c_winerr_to_ckr(GetLastError()); + return CKR_OK; + }; + + return CKR_ATTRIBUTE_TYPE_INVALID; +} + +static unsigned int +trust_hash_func(P11cObject* obj) +{ + return p11c_hash_integer(((TrustObject*)obj)->cert_obj); +} + +static int +trust_equal_func(P11cObject* a, P11cObject* b) +{ + return ((TrustObject*)a)->cert_obj == ((TrustObject*)b)->cert_obj; +} + +static void +trust_release(void* data) +{ + TrustObjectData* tdata = (TrustObjectData*)data; + ASSERT(tdata); + + ASSERT(tdata->cert); + CertFreeCertificateContext(tdata->cert); + + if(tdata->enhanced_usage) + free(tdata->enhanced_usage); + + free(tdata); +} + +static const P11cObjectDataVtable trust_objdata_vtable = { + trust_bool_attribute, + trust_ulong_attribute, + trust_bytes_attribute, + trust_release, +}; + +static CK_RV +parse_usage(TrustObjectData* tdata, DWORD flags) +{ + DWORD size, err; + CERT_ENHKEY_USAGE* eusage; + + ASSERT(!tdata->enhanced_usage); + + /* Get the size of the enhanced_usage */ + if(!CertGetEnhancedKeyUsage(tdata->cert, flags, NULL, &size)) + { + err = GetLastError(); + + /* No enhanced_usage data is not an error */ + if(err == CRYPT_E_NOT_FOUND) + return CKR_OK; + return p11c_winerr_to_ckr(err); + } + + eusage = (CERT_ENHKEY_USAGE*)calloc(1, size); + if(!eusage) + return CKR_HOST_MEMORY; + + /* Now get the actual enhanced usage property */ + if(!CertGetEnhancedKeyUsage(tdata->cert, flags, eusage, &size)) + { + err = GetLastError(); + if(err == CRYPT_E_NOT_FOUND) + return CKR_OK; + return p11c_winerr_to_ckr(err); + } + + tdata->enhanced_usage = eusage; + return CKR_OK; +} + +static CK_RV +parse_restrictions(TrustObjectData* tdata) +{ + CRYPT_BIT_BLOB* rst; + CERT_EXTENSION* ext; + DWORD size; + + ASSERT(tdata); + ASSERT(tdata->cert); + + tdata->has_usage = CK_FALSE; + tdata->usage = 0x00; + + ext = CertFindExtension(szOID_KEY_USAGE, + tdata->cert->pCertInfo->cExtension, + tdata->cert->pCertInfo->rgExtension); + + /* No key usage, don't care */ + if(!ext) + return CKR_OK; + + /* Find the size of the decoded structure */ + if(!CryptDecodeObject(P11c_ENCODINGS, X509_KEY_USAGE, + ext->Value.pbData, ext->Value.cbData, 0, NULL, &size)) + return p11c_winerr_to_ckr(GetLastError()); + + /* Allocate enough memory */ + rst = (CRYPT_BIT_BLOB*)calloc(1, size); + if(!rst) + return CKR_HOST_MEMORY; + + /* And get the decoded structure */ + if(CryptDecodeObject(P11c_ENCODINGS, X509_KEY_USAGE, + ext->Value.pbData, ext->Value.cbData, 0, rst, &size)) + { + if(rst->cbData != 1 && + rst->cUnusedBits != 0) + { + DBG(("key usage are of invalid size")); + } + else + { + /* A valid byte of key restricted usage flags. Yes all that for one byte */ + tdata->usage = *((BYTE*)(rst->pbData)); + tdata->has_usage = TRUE; + } + } + + free(rst); + return CKR_OK; +} + +static CK_RV +trust_load_data(P11cSession* sess, P11cObject* obj, P11cObjectData** objdata) +{ + TrustObject* tobj = (TrustObject*)obj; + TrustObjectData* tdata; + P11cObjectData* certdata; + CK_RV ret; + + ASSERT(tobj); + ASSERT(objdata); + + /* Get the raw data for the certificate */ + ret = p11c_session_get_object_data_for(sess, tobj->cert_obj, &certdata); + if(ret != CKR_OK) + return ret; + + tdata = (TrustObjectData*)calloc(1, sizeof(TrustObjectData)); + if(!tdata) + return CKR_HOST_MEMORY; + + tdata->cert = p11c_cert_object_data_get_certificate (certdata); + ASSERT(tdata->cert); + + /* Dig up the restrictions data extension */ + ret = parse_restrictions(tdata); + if(ret != CKR_OK) + { + free(tdata); + return ret; + } + + /* Dig up the enhanced usage data property, and then try the extension */ + ret = parse_usage(tdata, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG); + if(ret == CKR_OK && !tdata->enhanced_usage) + ret = parse_usage(tdata, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG); + + if(ret != CKR_OK) + { + free(tdata); + return ret; + } + + /* And keep a reference to the certificate */ + tdata->cert = CertDuplicateCertificateContext(tdata->cert); + + tdata->base.object = obj->id; + tdata->base.data_funcs = &trust_objdata_vtable; + + *objdata = &(tdata->base); + return CKR_OK; +} + + +static void +trust_object_release(void* data) +{ + TrustObject* tobj = (TrustObject*)data; + ASSERT(tobj); + free(tobj); +} + +static const P11cObjectVtable trust_object_vtable = { + trust_load_data, + trust_hash_func, + trust_equal_func, + trust_object_release, +}; + +static CK_RV +register_trust_object(P11cSession* sess, P11cObject* cert, P11cObject** obj) +{ + TrustObject* tobj; + CK_RV ret; + + tobj = calloc(1, sizeof(TrustObject)); + if(!tobj) + return CKR_HOST_MEMORY; + + tobj->cert_obj = cert->id; + + tobj->obj.id = 0; + tobj->obj.obj_funcs = &trust_object_vtable; + + ret = p11c_token_register_object(sess->slot, &(tobj->obj)); + if(ret != CKR_OK) + { + free(tobj); + return ret; + } + + ASSERT(tobj->obj.id != 0); + *obj = &(tobj->obj); + + return CKR_OK; +} + +static CK_RV +list_matching_certificates(P11cSession* sess, CK_ATTRIBUTE_PTR match, + CK_ULONG count, P11cArray* arr) +{ + CK_OBJECT_CLASS cert_class = CKO_CERTIFICATE; + CK_ATTRIBUTE search[3]; + CK_ULONG n_search = 0; + CK_ULONG i; + + /* The class */ + search[0].type = CKA_CLASS; + search[0].pValue = &cert_class; + search[0].ulValueLen = sizeof(CK_OBJECT_CLASS); + ++n_search; + + for(i = 0; i < count && n_search < 3; ++i) + { + /* + * These are the attributes that tie a certificate + * to trust object, so try match certs with these + */ + if(match[i].type == CKA_ISSUER || + match[i].type == CKA_SERIAL_NUMBER) + { + search[n_search].type = match[i].type; + search[n_search].pValue = match[i].pValue; + search[n_search].ulValueLen = match[i].ulValueLen; + ++n_search; + } + } + + /* Do the certificate search */ + return p11c_cert_find(sess, CKO_CERTIFICATE, search, n_search, arr); +} + +CK_RV +p11c_trust_find(P11cSession* sess, CK_OBJECT_CLASS cls, + CK_ATTRIBUTE_PTR match, CK_ULONG count, P11cArray* arr) +{ + CK_OBJECT_HANDLE id; + P11cObject* obj; + P11cObject* certobj; + P11cObjectData* objdata; + P11cArray* certarr; + CK_RV ret = CKR_OK; + CK_ULONG i; + + /* We only have trust objects in here */ + if(cls != CKO_NETSCAPE_TRUST && cls != CKO_ANY) + return CKR_OK; + + /* Only work with slots that have certificates */ + if(!(p11c_token_get_flags (sess->slot) & P11c_SLOT_CERTS)) + return CKR_OK; + + /* Get a list of all certificates */ + certarr = p11c_array_new(0, 1, sizeof(CK_OBJECT_HANDLE)); + if(!certarr) + return CKR_HOST_MEMORY; + ret = list_matching_certificates(sess, match, count, certarr); + + /* Now match each of them against our criteria */ + if(ret == CKR_OK) + { + for(i = 0; i < certarr->len; ++i) + { + id = p11c_array_index(certarr, CK_OBJECT_HANDLE, i); + ASSERT(id); + + certobj = p11c_token_lookup_object(sess->slot, id); + ASSERT(certobj); + + /* We'll register a trust object for any loaded certificate */ + ret = register_trust_object(sess, certobj, &obj); + if(ret != CKR_OK) + break; + + ASSERT(obj); + + ret = p11c_session_get_object_data(sess, obj, &objdata); + if(ret != CKR_OK) + break; + + /* Only return new object if it matches */ + if(p11c_object_data_match(objdata, match, count)) + p11c_array_append(arr, obj->id); + } + } + + p11c_array_free(certarr, TRUE); + return ret; +} diff --git a/p11-capi-trust.h b/p11-capi-trust.h new file mode 100644 index 0000000..639f2eb --- /dev/null +++ b/p11-capi-trust.h @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2007 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef P11C_TRUST_H +#define P11C_TRUST_H + +#include "p11-capi.h" + +/* Find trust objects matching criteria */ +CK_RV p11c_trust_find (P11cSession* sess, CK_OBJECT_CLASS cls, + CK_ATTRIBUTE_PTR match, CK_ULONG count, + P11cArray* arr); + +#endif /* P11C_TRUST_H */ diff --git a/p11-capi-util.c b/p11-capi-util.c new file mode 100644 index 0000000..5ebb72b --- /dev/null +++ b/p11-capi-util.c @@ -0,0 +1,547 @@ +/* + * Copyright (C) 2007 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include "p11-capi-util.h" + +#include +#include +#include + + +void +p11c_reverse_memory (void* data, size_t length) +{ + size_t end = length - 1; + size_t middle = length / 2; + unsigned char* buf = data; + size_t i; + + for (i = 0; i < middle; i++) + { + unsigned char tmp = buf[i]; + buf[i] = buf[end - i]; + buf[end - i] = tmp; + } +} + +/* + * Array code originially from Glib. + * Modified extensively by Stef Walter + */ + +/* GLIB - Library of useful routines for C programming + * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + + +#define MIN_ARRAY_SIZE 16 + +typedef struct _RealArray +{ + P11cArray pub; + size_t alloc; + size_t elt_size; + int zero_terminated : 1; + int clear : 1; +} +RealArray; + +#define array_elt_len(array, i) ((array)->elt_size * (i)) +#define array_elt_pos(array, i) (((char*)(array)->pub.data) + array_elt_len((array),(i))) +#define array_elt_zero(array, pos, len) \ + (memset(array_elt_pos((array), pos), 0, array_elt_len((array), len))) +#define array_zero_terminate(array) \ + { if ((array)->zero_terminated) \ + array_elt_zero((array), (array)->pub.len, 1); } + +static unsigned int +nearest_pow(unsigned int num) +{ + unsigned int n = 1; + while(n < num) + n <<= 1; + return n; +} + +static int +maybe_expand(RealArray *array, size_t len) +{ + void* mem; + size_t want_alloc = array_elt_len(array, array->pub.len + len + + array->zero_terminated); + + if(want_alloc > array->alloc) + { + want_alloc = nearest_pow(want_alloc); + want_alloc = want_alloc > MIN_ARRAY_SIZE ? want_alloc : MIN_ARRAY_SIZE; + + mem = realloc(array->pub.data, want_alloc); + if(!mem) + return 0; + array->pub.data = mem; + + memset((char*)array->pub.data + array->alloc, 0, want_alloc - array->alloc); + array->alloc = want_alloc; + } + + return 1; +} + +P11cArray* +p11c_array_new(int zero_terminated, int clear, size_t elt_size) +{ + return p11c_array_sized_new(zero_terminated, clear, elt_size, 0); +} + +P11cArray* +p11c_array_sized_new(int zero_terminated, int clear, size_t elt_size, + size_t reserved_size) +{ + RealArray *array = malloc(sizeof(RealArray)); + if(!array) + return NULL; + + array->pub.data = NULL; + array->pub.len = 0; + array->alloc = 0; + array->zero_terminated = (zero_terminated ? 1 : 0); + array->clear = (clear ? 1 : 0); + array->elt_size = elt_size; + + if(array->zero_terminated || reserved_size != 0) + { + maybe_expand(array, reserved_size); + array_zero_terminate(array); + } + + return (P11cArray*)array; +} + +void* +p11c_array_free(P11cArray* array, int free_segment) +{ + void* segment; + + if(array == NULL) + return NULL; + + if(free_segment) + { + if(array->data) + free(array->data); + segment = NULL; + } + else + segment = array->data; + + free(array); + return segment; +} + +int +p11c_array_append_vals(P11cArray* parray, const void* data, size_t len) +{ + RealArray* array = (RealArray*)parray; + if(!maybe_expand(array, len)) + return 0; + + memcpy(array_elt_pos(array, array->pub.len), data, + array_elt_len(array, len)); + + array->pub.len += len; + array_zero_terminate(array); + + return 1; +} + +void +p11c_array_remove_index(P11cArray* parray, unsigned int index) +{ + RealArray* array = (RealArray*)parray; + + if(index >= array->pub.len) + return; + + if(index != array->pub.len - 1) + memmove(array_elt_pos (array, index), + array_elt_pos (array, index + 1), + array_elt_len (array, array->pub.len - index - 1)); + + array->pub.len -= 1; + + array_elt_zero (array, array->pub.len, 1); +} + +void +p11c_array_remove_range(P11cArray* parray, unsigned int index, size_t length) +{ + RealArray *array = (RealArray*)parray; + + if(index >= array->pub.len) + return; + if(index + length > array->pub.len) + length = array->pub.len - index; + if(length == 0) + return; + + if(index + length != array->pub.len) + memmove(array_elt_pos (array, index), + array_elt_pos (array, index + length), + (array->pub.len - (index + length)) * array->elt_size); + + array->pub.len -= length; + array_elt_zero(array, array->pub.len, length); +} + + +/* + * Originally from apache 2.0 + * Extensive modifications by + */ + +/* Copyright 2000-2004 The Apache Software Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/* + * The internal form of a hash table. + * + * The table is an array indexed by the hash of the key; collisions + * are resolved by hanging a linked list of hash entries off each + * element of the array. Although this is a really simple design it + * isn't too bad given that pools have a low allocation overhead. + */ + +typedef struct _HashEntry +{ + struct _HashEntry* next; + unsigned int hash; + const void* key; + void* val; +} +HashEntry; + +/* + * The size of the array is always a power of two. We use the maximum + * index rather than the size so that we can use bitwise-AND for + * modular arithmetic. + * The count of hash entries may be greater depending on the chosen + * collision rate. + */ +struct _P11cHash +{ + HashEntry** array; + P11cHashFunc hash_func; + P11cHashEqual equal_func; + size_t count; + size_t max; +}; + + +#define INITIAL_MAX 15 /* tunable == 2^n - 1 */ + +static int +equal_default(const void* a, const void* b) +{ + return a == b; +} + +/* + * Hash creation functions. + */ + +static HashEntry** +alloc_array(P11cHash* ht, size_t max) +{ + return calloc(1, sizeof(*(ht->array)) * (max + 1)); +} + +P11cHash* +p11c_hash_new(P11cHashFunc hash_func, P11cHashEqual equal_func) +{ + P11cHash* ht = malloc(sizeof(P11cHash)); + if(ht) + { + ht->hash_func = hash_func ? hash_func : p11c_hash_pointer; + ht->equal_func = equal_func ? equal_func : equal_default; + ht->count = 0; + ht->max = INITIAL_MAX; + ht->array = alloc_array(ht, ht->max); + if(!ht->array) + { + free(ht); + ht = NULL; + } + } + return ht; +} + +void +p11c_hash_free(P11cHash* ht, P11cHashDestroy destroy_func) +{ + HashEntry* he; + HashEntry* next; + size_t i; + + for(i = 0; i <= ht->max; ++i) + { + for(he = ht->array[i]; he; ) + { + next = he->next; + if(destroy_func) + (destroy_func)((void*)he->val); + free(he); + he = next; + } + } + + if(ht->array) + free(ht->array); + free(ht); +} + +/* + * Expanding a hash table + */ +static int +expand_array(P11cHash* ht) +{ + HashEntry** new_array; + size_t new_max; + HashEntry* he; + HashEntry* next; + size_t i; + + new_max = ht->max * 2 + 1; + new_array = alloc_array(ht, new_max); + + if(!new_array) + return 0; + + for(i = 0; i <= ht->max; ++i) + { + for(he = ht->array[i], next = he ? he->next : NULL; + he != NULL; he = next, next = next ? next->next : NULL) + { + unsigned int j = he->hash & new_max; + he->next = new_array[j]; + new_array[j] = he; + } + } + + if(ht->array) + free(ht->array); + + ht->array = new_array; + ht->max = new_max; + return 1; +} + +/* + * This is where we keep the details of the hash function and control + * the maximum collision rate. + * + * If val is non-NULL it creates and initializes a new hash entry if + * there isn't already one there; it returns an updatable pointer so + * that hash entries can be removed. + */ + +static HashEntry** +find_entry(P11cHash* ht, const void* key, void* val) +{ + HashEntry** hep; + HashEntry* he; + unsigned int hash; + + hash = (ht->hash_func)(key); + + /* scan linked list */ + for(hep = &ht->array[hash & ht->max], he = *hep; + he; hep = &he->next, he = *hep) + { + if(he->hash == hash && (ht->equal_func)(he->key, key)) + break; + } + + if(he || !val) + return hep; + + /* add a new entry for non-NULL val */ + he = malloc(sizeof(*he)); + if(he) + { + /* Key points to external data */ + he->key = key; + he->next = NULL; + he->hash = hash; + he->val = val; + + *hep = he; + ht->count++; + } + + return hep; +} + +void* +p11c_hash_get(P11cHash* ht, const void *key) +{ + HashEntry** he = find_entry(ht, key, NULL); + if(he && *he) + return (void*)((*he)->val); + else + return NULL; +} + +int +p11c_hash_set(P11cHash* ht, const void* key, void* val) +{ + HashEntry** hep = find_entry(ht, key, val); + if(hep && *hep) + { + /* replace entry */ + (*hep)->key = key; + (*hep)->val = val; + + /* check that the collision rate isn't too high */ + if(ht->count > ht->max) + { + if(!expand_array(ht)) + return 0; + } + + return 1; + } + + return 0; +} + +void* +p11c_hash_rem(P11cHash* ht, const void* key) +{ + HashEntry** hep = find_entry(ht, key, NULL); + void* val = NULL; + + if(hep && *hep) + { + HashEntry* old = *hep; + *hep = (*hep)->next; + --ht->count; + val = (void*)old->val; + free(old); + } + + return val; +} + +size_t +p11c_hash_count(P11cHash* ht) +{ + return ht->count; +} + +unsigned int +p11c_hash_pointer(const void* ptr) +{ + return (unsigned int)ptr; +} + +unsigned int +p11c_hash_data(const void* data, size_t n_data) +{ + unsigned int hash = 0; + const unsigned char* end; + const unsigned char* p; + + /* + * This is the popular `times 33' hash algorithm which is used by + * perl and also appears in Berkeley DB. This is one of the best + * known hash functions for strings because it is both computed + * very fast and distributes very well. + * + * The originator may be Dan Bernstein but the code in Berkeley DB + * cites Chris Torek as the source. The best citation I have found + * is "Chris Torek, Hash function for text in C, Usenet message + * <27038@mimsy.umd.edu> in comp.lang.c , October, 1990." in Rich + * Salz's USENIX 1992 paper about INN which can be found at + * . + * + * The magic of number 33, i.e. why it works better than many other + * constants, prime or not, has never been adequately explained by + * anyone. So I try an explanation: if one experimentally tests all + * multipliers between 1 and 256 (as I did while writing a low-level + * data structure library some time ago) one detects that even + * numbers are not useable at all. The remaining 128 odd numbers + * (except for the number 1) work more or less all equally well. + * They all distribute in an acceptable way and this way fill a hash + * table with an average percent of approx. 86%. + * + * If one compares the chi^2 values of the variants (see + * Bob Jenkins ``Hashing Frequently Asked Questions'' at + * http://burtleburtle.net/bob/hash/hashfaq.html for a description + * of chi^2), the number 33 not even has the best value. But the + * number 33 and a few other equally good numbers like 17, 31, 63, + * 127 and 129 have nevertheless a great advantage to the remaining + * numbers in the large set of possible multipliers: their multiply + * operation can be replaced by a faster operation based on just one + * shift plus either a single addition or subtraction operation. And + * because a hash function has to both distribute good _and_ has to + * be very fast to compute, those few numbers should be preferred. + * + * -- Ralf S. Engelschall + */ + + for(p = data, end = p + n_data; p != end; ++p) + hash = hash * 33 + *p; + + return hash; +} + +unsigned int +p11c_hash_integer(int integer) +{ + return integer; +} diff --git a/p11-capi-util.h b/p11-capi-util.h new file mode 100644 index 0000000..ed3507d --- /dev/null +++ b/p11-capi-util.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2007 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __P11C_UTIL_H__ +#define __P11C_UTIL_H__ + +#include + + +void p11c_reverse_memory (void* data, size_t length); + +/* -------------------------------------------------------------------------------- + * ARRAYS + */ + +typedef struct _Array +{ + void* data; + size_t len; +} +P11cArray; + +#define p11c_array_append(a,v) p11c_array_append_vals(a, &(v), 1) +#define p11c_array_index(a,t,i) (((t*) (a)->data) [(i)]) + +P11cArray* p11c_array_new (int zero_terminated, int zero, + size_t element_size); + +P11cArray* p11c_array_sized_new (int zero_terminated, int zero, + size_t element_size, size_t reserved_size); + +void* p11c_array_free (P11cArray* array, int free_segment); + +int p11c_array_append_vals (P11cArray* array, const void* data, + size_t num); + +void p11c_array_remove_index (P11cArray* array, unsigned int index); + +void p11c_array_remove_range (P11cArray* array, unsigned int index, + size_t count); + + +/* -------------------------------------------------------------------------------- + * HASHTABLE + */ + +struct _P11cHash; +typedef struct _P11cHash P11cHash; + +typedef unsigned int (*P11cHashFunc)(const void* key); + +typedef int (*P11cHashEqual)(const void* a, const void* b); + +typedef void (*P11cHashDestroy)(void* val); + +P11cHash* p11c_hash_new (P11cHashFunc hash_func, P11cHashEqual equal_func); + +void p11c_hash_free (P11cHash* ht, P11cHashDestroy destroy_func); + +size_t p11c_hash_count (P11cHash* ht); + +void* p11c_hash_get (P11cHash* ht, const void* key); + +int p11c_hash_set (P11cHash* ht, const void* key, void* val); + +void* p11c_hash_rem (P11cHash* ht, const void* key); + +unsigned int p11c_hash_pointer (const void* ptr); + +unsigned int p11c_hash_data (const void* data, size_t n_data); + +unsigned int p11c_hash_integer (int integer); + +#define p11c_hash_key(num) (((char*)NULL) + (size_t)(num)) + +#endif /* __P11C_UTIL_H__ */ diff --git a/p11-capi.c b/p11-capi.c new file mode 100644 index 0000000..b5f7f62 --- /dev/null +++ b/p11-capi.c @@ -0,0 +1,1515 @@ +/* + * Copyright (C) 2007 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include +#include +#include + +#include "p11-capi.h" +#include "p11-capi-object.h" +#include "p11-capi-session.h" +#include "p11-capi-rsa.h" +#include "p11-capi-token.h" + +/* Warns about all the raw string usage in this file */ +#pragma warning (disable : 4996) + +/* ------------------------------------------------------------------- + * GLOBALS / DEFINES + */ + +static int cryptoki_initialized = 0; +static HANDLE global_mutex = NULL; + +#define MANUFACTURER_ID "Cryptoki CAPI " +#define LIBRARY_DESCRIPTION "Cryptoki CAPI Provider " +#define LIBRARY_VERSION_MAJOR 1 +#define LIBRARY_VERSION_MINOR 1 +#define HARDWARE_VERSION_MAJOR 0 +#define HARDWARE_VERSION_MINOR 0 +#define FIRMWARE_VERSION_MAJOR 0 +#define FIRMWARE_VERSION_MINOR 0 +#define SLOT_TOKEN_SERIAL "1.0 " +#define SLOT_TOKEN_MODEL "1.0 " +#define MAX_PIN_LEN 256 +#define MIN_PIN_LEN 1 + +static CK_MECHANISM_TYPE all_mechanisms[] = { + CKM_RSA_PKCS +}; + +/* ------------------------------------------------------------------- + * MODULE GLOBAL FUNCTIONS + */ + +#define LINE 1024 + +void +p11c_debug(const char* msg, ...) +{ + char buf[LINE]; + va_list va; + size_t len; + + va_start(va, msg); + _vsnprintf(buf, 1024, msg, va); + va_end(va); + + buf[LINE - 1] = 0; + len = strlen (buf); + + strncpy(buf + len, "\n", 1024 - len); + buf[LINE - 1] = 0; + // OutputDebugStringA(buf); +} + +/* Bah humbug, MSVC doesn't have __func__ */ +#define ENTER(func) \ + char* _func = #func; \ + p11c_debug("%s: enter", _func) + +#define RETURN(ret) \ + return (p11c_debug("%s: %d", _func, ret), ret) + +#define PREREQ(cond, ret) \ + if (!(cond)) { p11c_debug("%s: %s failed: %d", _func, #cond, ret); return ret; } + +void +p11c_lock_global(void) +{ + DWORD r; + + ASSERT(global_mutex); + + r = WaitForSingleObject(global_mutex, INFINITE); + ASSERT(r == WAIT_OBJECT_0); +} + +void +p11c_unlock_global(void) +{ + BOOL r; + + ASSERT(global_mutex); + + r = ReleaseMutex(global_mutex); + ASSERT(r); +} + +CK_RV +p11c_winerr_to_ckr(DWORD werr) +{ + switch(werr) + { + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_OUTOFMEMORY: + return CKR_HOST_MEMORY; + break; + case NTE_NO_MEMORY: + return CKR_DEVICE_MEMORY; + break; + case ERROR_MORE_DATA: + return CKR_BUFFER_TOO_SMALL; + case ERROR_INVALID_PARAMETER: /* these params were derived from the */ + case ERROR_INVALID_HANDLE: /* inputs, so if they are bad, the input */ + case NTE_BAD_ALGID: /* data is bad */ + case NTE_BAD_HASH: + case NTE_BAD_TYPE: + case NTE_BAD_PUBLIC_KEY: + return CKR_DATA_INVALID; + case ERROR_BUSY: + case NTE_FAIL: + case NTE_BAD_UID: + return CKR_DEVICE_ERROR; + default: + return CKR_GENERAL_ERROR; + }; +} + +CK_RV +p11c_return_data_raw(CK_VOID_PTR output, CK_ULONG_PTR n_output, + CK_VOID_PTR input, CK_ULONG n_input) +{ + ASSERT(n_output); + ASSERT(input); + + /* Just asking for the length */ + if(!output) + { + *n_output = n_input; + return CKR_OK; + } + + /* Buffer is too short */ + if(n_input > *n_output) + { + *n_output = n_input; + return CKR_BUFFER_TOO_SMALL; + } + + *n_output = n_input; + memcpy(output, input, n_input); + return CKR_OK; +} + +CK_RV +p11c_return_data(CK_ATTRIBUTE_PTR attr, CK_VOID_PTR input, DWORD n_input) +{ + return p11c_return_data_raw(attr->pValue, &(attr->ulValueLen), + input, n_input); +} + +CK_RV +p11c_return_string(CK_ATTRIBUTE_PTR attr, WCHAR* string) +{ + CK_UTF8CHAR_PTR buffer; + int result; + + SetLastError(0); + + /* + * Sadly WideCharToMultiByte doesn't handle zero + * length strings properly. So we have to special + * case this part. + */ + if(!string[0]) + return p11c_return_data(attr, "", 0); + + /* The length of the string, including null termination */ + result = WideCharToMultiByte(CP_UTF8, 0, string, -1, + NULL, 0, NULL, NULL); + + if(result) + { + /* Did the caller just want the length? */ + if(!attr->pValue) + { + attr->ulValueLen = result - 1; + return CKR_OK; + } + + /* Is the callers buffer too short? */ + if((int)attr->ulValueLen < result - 1) + { + attr->ulValueLen = result - 1; + return CKR_BUFFER_TOO_SMALL; + } + + /* + * Allocate a buffer for the conversion. We have to + * do this because strings in PKCS#11 are not null + * terminated and strings returned from + * WideCharToMultiByte are always null terminated. + */ + buffer = malloc(result); + if(!buffer) + return CKR_HOST_MEMORY; + + /* Do the actual conversion */ + result = WideCharToMultiByte(CP_UTF8, 0, string, -1, + buffer, result, NULL, NULL); + + if(result) + { + attr->ulValueLen = result - 1; + memcpy(attr->pValue, buffer, attr->ulValueLen); + + free(buffer); + return CKR_OK; + } + + free(buffer); + } + + /* + * We should never have too little buffer, or + * get a zero length success code. It's a very + * strange error that arrives here. + */ + return CKR_GENERAL_ERROR; +} + +CK_RV +p11c_return_dword_as_bytes(CK_ATTRIBUTE_PTR attr, DWORD value) +{ + int i; + CK_ULONG count = 0; + BOOL first = TRUE; + BYTE* at = attr->pValue; + CK_RV ret = CKR_OK; + + for(i = 0; i < sizeof(DWORD); i++) + { + BYTE digit = (BYTE)((value >> (((sizeof(DWORD)-1)*8))) & 0xFF); + value = value << 8; + + /* No leading zero */ + if (first && digit == 0) + continue; + + first = FALSE; + if(at) + { + if(count > attr->ulValueLen) + ret = CKR_BUFFER_TOO_SMALL; + else + *(at++) = digit; + } + + count++; + } + + attr->ulValueLen = count; + return ret; +} + +CK_RV +p11c_return_reversed_data(CK_ATTRIBUTE_PTR attr, CK_VOID_PTR data, CK_ULONG length) +{ + CK_RV ret = p11c_return_data(attr, data, length); + if(ret != CKR_OK || !attr->pValue) + return ret; + + p11c_reverse_memory(attr->pValue, attr->ulValueLen); + return CKR_OK; +} + +static void +print_zero_decimal(CK_BYTE_PTR buffer, CK_ULONG length, WORD value) +{ + int i; + for(i = (int)length - 1; i >= 0; --i) + { + BYTE digit = value % 10; + buffer[i] = '0' + digit; + value /= 10; + } +} + +CK_RV +p11c_return_filetime(CK_ATTRIBUTE_PTR attr, FILETIME *ftime) +{ + SYSTEMTIME stime; + CK_DATE* date; + + ASSERT(attr); + ASSERT(ftime); + + if(!attr->pValue) + { + attr->ulValueLen = sizeof(CK_DATE); + return CKR_OK; + } + + if(attr->ulValueLen < sizeof(CK_DATE)) + { + attr->ulValueLen = sizeof(CK_DATE); + return CKR_BUFFER_TOO_SMALL; + } + + if(!FileTimeToSystemTime(ftime, &stime)) + { + DBG(("An invalid FILETIME was encountered")); + return CKR_GENERAL_ERROR; + } + + date = (CK_DATE*)attr->pValue; + attr->ulValueLen = sizeof(CK_DATE); + print_zero_decimal(date->year, sizeof(date->year), stime.wYear); + print_zero_decimal(date->month, sizeof(date->month), stime.wMonth); + print_zero_decimal(date->day, sizeof(date->day), stime.wDay); + + return CKR_OK; +} + +/* ---------------------------------------------------------------- */ + +static CK_RV +PC_C_Initialize(CK_VOID_PTR init_args) +{ + ENTER(C_Initialize); + PREREQ(!cryptoki_initialized, CKR_CRYPTOKI_ALREADY_INITIALIZED); + + if (init_args != NULL) { + CK_C_INITIALIZE_ARGS_PTR args; + int supplied_ok; + + /* pReserved must be NULL */ + args = init_args; + PREREQ(!args->pReserved, CKR_ARGUMENTS_BAD); + + /* ALL supplied function pointers need to have the value either NULL or non-NULL. */ + supplied_ok = (args->CreateMutex == NULL && args->DestroyMutex == NULL && + args->LockMutex == NULL && args->UnlockMutex == NULL) || + (args->CreateMutex != NULL && args->DestroyMutex != NULL && + args->LockMutex != NULL && args->UnlockMutex != NULL); + PREREQ(supplied_ok, CKR_ARGUMENTS_BAD); + + /* + * When the CKF_OS_LOCKING_OK flag isn't set and mutex function pointers are supplied + * by an application, return an error. We must be able to use our own locks. + */ + if(!(args->flags & CKF_OS_LOCKING_OK) && (args->CreateMutex != NULL)) + RETURN(CKR_CANT_LOCK); + } + + if(!global_mutex) + { + global_mutex = CreateMutex(NULL, FALSE, NULL); + if(!global_mutex) + RETURN(CKR_CANT_LOCK); + } + + cryptoki_initialized = 1; + + RETURN(CKR_OK); +} + +static CK_RV +PC_C_Finalize(CK_VOID_PTR pReserved) +{ + ENTER(C_Finalize); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(!pReserved, CKR_ARGUMENTS_BAD); + + cryptoki_initialized = 0; + + p11c_session_cleanup_all(); + p11c_token_cleanup_all(); + + RETURN(CKR_OK); +} + +static CK_RV +PC_C_GetInfo(CK_INFO_PTR info) +{ + ENTER(C_GetInfo); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(info, CKR_ARGUMENTS_BAD); + + ASSERT(strlen(MANUFACTURER_ID) == 32); + ASSERT(strlen(LIBRARY_DESCRIPTION) == 32); + + info->cryptokiVersion.major = CRYPTOKI_VERSION_MAJOR; + info->cryptokiVersion.minor = CRYPTOKI_VERSION_MINOR; + info->libraryVersion.major = LIBRARY_VERSION_MAJOR; + info->libraryVersion.minor = LIBRARY_VERSION_MINOR; + info->flags = 0; + strncpy((char*)info->manufacturerID, MANUFACTURER_ID, 32); + strncpy((char*)info->libraryDescription, LIBRARY_DESCRIPTION, 32); + + RETURN(CKR_OK); +} + +static CK_RV +PC_C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR list) +{ + /* This would be a strange call to receive */ + return C_GetFunctionList(list); +} + +static CK_RV +PC_C_GetSlotList(CK_BBOOL tokenPresent, CK_SLOT_ID_PTR slot_list, CK_ULONG_PTR count) +{ + unsigned int n_tokens, i; + + ENTER(C_GetSlotList); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(count, CKR_ARGUMENTS_BAD); + + /* All tokens are always present */ + + n_tokens = p11c_token_get_count(); + + /* Application only wants to know the number of slots. */ + if(slot_list == NULL) + { + *count = n_tokens; + RETURN(CKR_OK); + } + + if(*count < n_tokens) + { + *count = n_tokens; + RETURN(CKR_BUFFER_TOO_SMALL); + } + + *count = n_tokens; + for(i = 0; i < n_tokens; ++i) + slot_list[i] = p11c_token_get_slot_id (i); + RETURN(CKR_OK); +} + +static CK_RV +PC_C_GetSlotInfo(CK_SLOT_ID id, CK_SLOT_INFO_PTR info) +{ + const char* name; + + ENTER(C_GetSlotInfo); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(info, CKR_ARGUMENTS_BAD); + + /* Make sure the slot ID is valid */ + if(!p11c_token_is_valid(id)) + RETURN(CKR_SLOT_ID_INVALID); + + ASSERT(strlen(MANUFACTURER_ID) == 32); + + /* Provide information about the slot in the provided buffer */ + strncpy((char*)info->manufacturerID, MANUFACTURER_ID, 32); + info->hardwareVersion.major = HARDWARE_VERSION_MAJOR; + info->hardwareVersion.minor = HARDWARE_VERSION_MINOR; + info->firmwareVersion.major = FIRMWARE_VERSION_MAJOR; + info->firmwareVersion.minor = FIRMWARE_VERSION_MINOR; + + /* Token is always present */ + info->flags = CKF_TOKEN_PRESENT; + + /* Slot name is blank padded, odd */ + name = p11c_token_get_display_name(id); + memset((char*)info->slotDescription, ' ', + sizeof(info->slotDescription)); + memcpy((char*)info->slotDescription, name, + min(strlen(name), sizeof(info->slotDescription))); + + RETURN(CKR_OK); +} + +static CK_RV +PC_C_GetTokenInfo(CK_SLOT_ID id, CK_TOKEN_INFO_PTR info) +{ + const char* name; + + ENTER(C_GetTokenInfo); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(info, CKR_ARGUMENTS_BAD); + + /* Make sure the slot ID is valid */ + if(!p11c_token_is_valid(id)) + RETURN(CKR_SLOT_ID_INVALID); + + ASSERT(strlen(MANUFACTURER_ID) == 32); + ASSERT(strlen(SLOT_TOKEN_MODEL) == 16); + ASSERT(strlen(SLOT_TOKEN_SERIAL) == 16); + + /* Provide information about a token in the provided buffer */ + strncpy((char*)info->manufacturerID, MANUFACTURER_ID, 32); + strncpy((char*)info->model, SLOT_TOKEN_MODEL, 16); + strncpy((char*)info->serialNumber, SLOT_TOKEN_SERIAL, 16); + + /* Protected authentication path: Windows prompts for it's own PINs */ + info->flags = CKF_TOKEN_INITIALIZED | CKF_PROTECTED_AUTHENTICATION_PATH; + info->ulMaxSessionCount = CK_EFFECTIVELY_INFINITE; + info->ulSessionCount = CK_EFFECTIVELY_INFINITE; + info->ulMaxRwSessionCount = CK_EFFECTIVELY_INFINITE; + info->ulRwSessionCount = CK_EFFECTIVELY_INFINITE; + info->ulMaxPinLen = MAX_PIN_LEN; + info->ulMinPinLen = MIN_PIN_LEN; + info->ulTotalPublicMemory = CK_UNAVAILABLE_INFORMATION; + info->ulFreePublicMemory = CK_UNAVAILABLE_INFORMATION; + info->ulTotalPrivateMemory = CK_UNAVAILABLE_INFORMATION; + info->ulFreePrivateMemory = CK_UNAVAILABLE_INFORMATION; + info->hardwareVersion.major = HARDWARE_VERSION_MAJOR; + info->hardwareVersion.minor = HARDWARE_VERSION_MINOR; + info->firmwareVersion.major = FIRMWARE_VERSION_MAJOR; + info->firmwareVersion.minor = FIRMWARE_VERSION_MINOR; + memset(info->utcTime, ' ', 16); + + /* Slot name is blank padded, odd */ + name = p11c_token_get_display_name(id); + memset((char*)info->label, ' ', sizeof(info->label)); + memcpy((char*)info->label, name, + min(strlen(name), sizeof(info->label))); + + RETURN(CKR_OK); +} + +static CK_RV +PC_C_GetMechanismList(CK_SLOT_ID id, CK_MECHANISM_TYPE_PTR mechanism_list, + CK_ULONG_PTR count) +{ + CK_ULONG n_mechs; + + ENTER(C_GetMechanismList); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(count, CKR_ARGUMENTS_BAD); + + if(!p11c_token_is_valid(id)) + RETURN(CKR_SLOT_ID_INVALID); + + n_mechs = sizeof(all_mechanisms) / sizeof(all_mechanisms[0]); + + if(mechanism_list == NULL) + { + *count = n_mechs; + RETURN(CKR_OK); + } + + if(*count < n_mechs) + { + *count = n_mechs; + RETURN(CKR_BUFFER_TOO_SMALL); + } + + memcpy(mechanism_list, all_mechanisms, + n_mechs * sizeof(CK_MECHANISM_TYPE)); + *count = n_mechs; + RETURN(CKR_OK); +} + +static CK_RV +PC_C_GetMechanismInfo(CK_SLOT_ID id, CK_MECHANISM_TYPE type, + CK_MECHANISM_INFO_PTR info) +{ + ENTER(C_GetMechanismInfo); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(info, CKR_ARGUMENTS_BAD); + + if(!p11c_token_is_valid(id)) + RETURN(CKR_SLOT_ID_INVALID); + + if(type == CKM_RSA_PKCS) + { + p11c_rsa_pkcs_get_info(type, info); + RETURN(CKR_OK); + } + + RETURN(CKR_MECHANISM_INVALID); +} + +static CK_RV +PC_C_InitToken(CK_SLOT_ID slot_id, CK_UTF8CHAR_PTR pin, CK_ULONG pin_len, + CK_UTF8CHAR_PTR label) +{ + ENTER(C_InitToken); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_WaitForSlotEvent(CK_FLAGS flags, CK_SLOT_ID_PTR pSlot, CK_VOID_PTR pReserved) +{ + ENTER(C_WaitForSlotEvent); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* + * PKCS#11 GRAY AREA: What happens when we know we'll *never* + * have any slot events, and someone calls us without CKR_DONT_BLOCK? + * In case there's a thread dedicated to calling this function in a + * loop, we wait 1 seconds when called without CKR_DONT_BLOCK. + */ + + if(!(flags & CKF_DONT_BLOCK)) + Sleep(1000); + + RETURN(CKR_NO_EVENT); +} + +static CK_RV +PC_C_OpenSession(CK_SLOT_ID id, CK_FLAGS flags, CK_VOID_PTR application, + CK_NOTIFY notify, CK_SESSION_HANDLE_PTR session) +{ + P11cSession* sess; + CK_RV ret; + + ENTER(C_OpenSession); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(session, CKR_ARGUMENTS_BAD); + PREREQ(flags & CKF_SERIAL_SESSION, CKR_SESSION_PARALLEL_NOT_SUPPORTED); + + if(!p11c_token_is_valid(id)) + RETURN(CKR_SLOT_ID_INVALID); + + ret = p11c_session_create(id, &sess); + if(ret != CKR_OK) + RETURN(ret); + + sess->notify_callback = notify; + sess->user_data = application; + + if(flags & CKF_RW_SESSION) + sess->read_write = TRUE; + + ret = p11c_session_register(sess); + if(ret == CKR_OK) + { + /* ID should have been assigned when registering */ + ASSERT(sess->id > 0); + *session = sess->id; + } + else + { + p11c_session_destroy(sess); + } + + RETURN(ret); +} + +static CK_RV +PC_C_CloseSession(CK_SESSION_HANDLE session) +{ + P11cSession* sess; + CK_RV ret; + + ENTER(C_CloseSession); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + ret = p11c_session_remove_lock_ref(session, &sess); + if(ret == CKR_OK) + { + /* This will unref and possibly destroy the session */ + p11c_session_unref_unlock(sess); + } + + RETURN(ret); +} + +static CK_RV +PC_C_CloseAllSessions(CK_SLOT_ID id) +{ + ENTER(C_CloseAllSession); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + if(!p11c_token_is_valid(id)) + RETURN(CKR_SLOT_ID_INVALID); + + p11c_session_close_all(id); + RETURN(CKR_OK); +} + +static CK_RV +PC_C_GetFunctionStatus(CK_SESSION_HANDLE session) +{ + ENTER(C_GetFunctionStatus); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + RETURN(CKR_FUNCTION_NOT_PARALLEL); +} + +static CK_RV +PC_C_CancelFunction(CK_SESSION_HANDLE session) +{ + ENTER(C_CancelFunction); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + RETURN(CKR_FUNCTION_NOT_PARALLEL); +} + +static CK_RV +PC_C_GetSessionInfo(CK_SESSION_HANDLE session, CK_SESSION_INFO_PTR info) +{ + P11cSession* sess; + CK_RV ret; + + ENTER(C_GetSessionInfo); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(info, CKR_ARGUMENTS_BAD); + + ret = p11c_session_get_lock_ref(session, FALSE, &sess); + if(ret == CKR_OK) + { + p11c_session_get_info(sess, info); + p11c_session_unref_unlock(sess); + } + + RETURN(ret); +} + +static CK_RV +PC_C_InitPIN(CK_SESSION_HANDLE session, CK_UTF8CHAR_PTR pin, + CK_ULONG pin_len) +{ + ENTER(C_InitPIN); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* We don't support this stuff. We don't support 'SO' logins. */ + RETURN(CKR_USER_NOT_LOGGED_IN); +} + +static CK_RV +PC_C_SetPIN(CK_SESSION_HANDLE session, CK_UTF8CHAR_PTR old_pin, + CK_ULONG old_len, CK_UTF8CHAR_PTR new_pin, CK_ULONG new_len) +{ + ENTER(C_SetPIN); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* Not supported, Windows takes care of this */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_GetOperationState(CK_SESSION_HANDLE session, CK_BYTE_PTR operation_state, + CK_ULONG_PTR operation_state_len) +{ + ENTER(C_GetOperationState); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* Nasty, no sirrr */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_SetOperationState(CK_SESSION_HANDLE session, CK_BYTE_PTR operation_state, + CK_ULONG operation_state_len, CK_OBJECT_HANDLE encryption_key, + CK_OBJECT_HANDLE authentication_key) +{ + ENTER(C_SetOperationState); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* Nasty, no sirrr */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_Login(CK_SESSION_HANDLE session, CK_USER_TYPE user_type, + CK_UTF8CHAR_PTR pin, CK_ULONG pin_len) +{ + P11cSession* sess; + CK_RV ret; + + ENTER(C_Login); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + ret = p11c_session_get_lock_ref(session, FALSE, &sess); + if(ret == CKR_OK) + { + switch(user_type) + { + case CKU_USER: + ret = p11c_token_login(sess->slot); + break; + case CKU_SO: + ret = CKR_USER_TYPE_INVALID; + break; + default: + ret = CKR_USER_TYPE_INVALID; + break; + } + + p11c_session_unref_unlock(sess); + } + + RETURN(ret); +} + +static CK_RV +PC_C_Logout(CK_SESSION_HANDLE session) +{ + P11cSession* sess; + CK_RV ret; + + ENTER(C_Logout); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + ret = p11c_session_get_lock_ref(session, FALSE, &sess); + if(ret == CKR_OK) + { + ret = p11c_token_logout(sess->slot); + p11c_session_unref_unlock(sess); + } + + RETURN(ret); +} + +static CK_RV +PC_C_CreateObject(CK_SESSION_HANDLE session, CK_ATTRIBUTE_PTR templ, + CK_ULONG count, CK_OBJECT_HANDLE_PTR object) +{ + ENTER(C_CreateObject); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* TODO: See if we need to support this */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_CopyObject(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, + CK_ATTRIBUTE_PTR templ, CK_ULONG count, + CK_OBJECT_HANDLE_PTR new_object) +{ + ENTER(C_CopyObject); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* TODO: See if we need to support this */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + + +static CK_RV +PC_C_DestroyObject(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object) +{ + ENTER(C_DestroyObject); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* TODO: See if we need to support this */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_GetObjectSize(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, + CK_ULONG_PTR size) +{ + ENTER(C_GetObjectSize); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* TODO: Implement this */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_GetAttributeValue(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, + CK_ATTRIBUTE_PTR templ, CK_ULONG count) +{ + P11cSession* sess; + P11cObjectData* objdata; + CK_RV ret; + + ENTER(C_GetAttributeValue); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(object, CKR_OBJECT_HANDLE_INVALID); + PREREQ(!count || templ, CKR_ARGUMENTS_BAD); + + ret = p11c_session_get_lock_ref(session, FALSE, &sess); + if(ret == CKR_OK) + { + ret = p11c_session_get_object_data_for(sess, object, &objdata); + if(ret == CKR_OK) + ret = p11c_object_data_get_attrs(objdata, templ, count); + + p11c_session_unref_unlock(sess); + } + + RETURN(ret); +} + +static CK_RV +PC_C_SetAttributeValue(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE object, + CK_ATTRIBUTE_PTR templ, CK_ULONG count) +{ + ENTER(C_SetAttributeValue); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* TODO: See if we need to implement this */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_FindObjectsInit(CK_SESSION_HANDLE session, CK_ATTRIBUTE_PTR templ, + CK_ULONG count) +{ + P11cSession* sess; + CK_RV ret; + + ENTER(C_FindObjectsInit); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(!count || templ, CKR_ARGUMENTS_BAD); + + ret = p11c_session_get_lock_ref(session, FALSE, &sess); + if(ret == CKR_OK) + { + ret = p11c_session_find_init(sess, templ, count); + p11c_session_unref_unlock(sess); + } + + RETURN(ret); +} + +static CK_RV +PC_C_FindObjects(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE_PTR objects, + CK_ULONG max_object_count, CK_ULONG_PTR object_count) +{ + P11cSession* sess; + CK_RV ret; + + ENTER(C_FindObjects); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(object_count, CKR_ARGUMENTS_BAD); + PREREQ(!max_object_count || objects, CKR_ARGUMENTS_BAD); + + ret = p11c_session_get_lock_ref(session, FALSE, &sess); + if(ret == CKR_OK) + { + ret = p11c_session_find(sess, objects, max_object_count, object_count); + p11c_session_unref_unlock(sess); + } + + RETURN(ret); +} + +static CK_RV +PC_C_FindObjectsFinal(CK_SESSION_HANDLE session) +{ + P11cSession* sess; + CK_RV ret; + + ENTER(C_FindObjectsFinal); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + ret = p11c_session_get_lock_ref(session, FALSE, &sess); + if(ret == CKR_OK) + { + ret = p11c_session_find_final(sess); + p11c_session_unref_unlock(sess); + } + + RETURN(ret); +} + +static CK_RV +PC_C_EncryptInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, + CK_OBJECT_HANDLE key) +{ + ENTER(C_EncryptInit); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* TODO: See if we need to implement this */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_Encrypt(CK_SESSION_HANDLE session, CK_BYTE_PTR data, CK_ULONG data_len, + CK_BYTE_PTR encrypted_data, CK_ULONG_PTR encrypted_data_len) +{ + ENTER(C_Encrypt); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* TODO: See if we need to implement this */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_EncryptUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, + CK_ULONG part_len, CK_BYTE_PTR encrypted_part, + CK_ULONG_PTR encrypted_part_len) +{ + ENTER(C_EncryptUpdate); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* RSA/DSA mechs don't support incremental crypto operations. */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_EncryptFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR last_encrypted_part, + CK_ULONG_PTR last_encrypted_part_len) +{ + ENTER(C_EncryptFinal); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* RSA/DSA mechs don't support incremental crypto operations. */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_DecryptInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, + CK_OBJECT_HANDLE key) +{ + P11cObjectData* objdata; + P11cSession* sess; + CK_RV ret; + + ENTER(C_DecryptInit); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(mechanism, CKR_ARGUMENTS_BAD); + PREREQ(key, CKR_ARGUMENTS_BAD); + + ret = p11c_session_get_lock_ref(session, FALSE, &sess); + if(ret == CKR_OK) + { + ret = p11c_session_get_object_data_for(sess, key, &objdata); + if(ret == CKR_OK) + ret = p11c_session_decrypt_init(sess, mechanism, objdata); + + p11c_session_unref_unlock(sess); + } + + RETURN(ret); +} + +static CK_RV +PC_C_Decrypt(CK_SESSION_HANDLE session, CK_BYTE_PTR encrypted_data, + CK_ULONG encrypted_data_len, CK_BYTE_PTR data, CK_ULONG_PTR data_len) +{ + P11cSession* sess; + CK_RV ret; + + ENTER(C_Decrypt); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(encrypted_data, CKR_ARGUMENTS_BAD); + PREREQ(encrypted_data_len, CKR_ARGUMENTS_BAD); + + ret = p11c_session_get_lock_ref(session, FALSE, &sess); + if(ret == CKR_OK) + { + ret = p11c_session_decrypt(sess, encrypted_data, encrypted_data_len, + data, data_len); + p11c_session_unref_unlock(sess); + } + + RETURN(ret); +} + +static CK_RV +PC_C_DecryptUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR encrypted_part, + CK_ULONG encrypted_part_len, CK_BYTE_PTR part, CK_ULONG_PTR part_len) +{ + ENTER(C_DecryptUpdate); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* RSA/DSA mechs don't support incremental crypto operations. */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_DecryptFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR pLastPart, + CK_ULONG_PTR last_part_len) +{ + ENTER(C_DecryptFinal); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* RSA/DSA mechs don't support incremental crypto operations. */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_DigestInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism) +{ + ENTER(C_DigestInit); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* RSA/DSA mechs don't support digest. */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_Digest(CK_SESSION_HANDLE session, CK_BYTE_PTR data, CK_ULONG data_len, + CK_BYTE_PTR digest, CK_ULONG_PTR digest_len) +{ + ENTER(C_Digest); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* RSA/DSA mechs don't support digest. */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_DigestUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len) +{ + ENTER(C_DigestUpdate); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* RSA/DSA mechs don't support digest. */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_DigestKey(CK_SESSION_HANDLE session, CK_OBJECT_HANDLE key) +{ + ENTER(C_DigestKey); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* RSA/DSA mechs don't support digest. */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_DigestFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR digest, + CK_ULONG_PTR digest_len) +{ + ENTER(C_DigestFinal); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* RSA/DSA mechs don't support digest. */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_SignInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, + CK_OBJECT_HANDLE key) +{ + P11cObjectData* objdata; + P11cSession* sess; + CK_RV ret; + + ENTER(C_SignInit); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(mechanism, CKR_ARGUMENTS_BAD); + PREREQ(key, CKR_ARGUMENTS_BAD); + + ret = p11c_session_get_lock_ref(session, FALSE, &sess); + if(ret == CKR_OK) + { + ret = p11c_session_get_object_data_for(sess, key, &objdata); + if(ret == CKR_OK) + ret = p11c_session_sign_init(sess, mechanism, objdata); + + p11c_session_unref_unlock(sess); + } + + RETURN(ret); +} + +static CK_RV +PC_C_Sign(CK_SESSION_HANDLE session, CK_BYTE_PTR data, CK_ULONG data_len, + CK_BYTE_PTR signature, CK_ULONG_PTR signature_len) +{ + P11cSession* sess; + CK_RV ret; + + ENTER(C_Sign); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + PREREQ(data, CKR_ARGUMENTS_BAD); + PREREQ(data_len, CKR_ARGUMENTS_BAD); + + ret = p11c_session_get_lock_ref(session, FALSE, &sess); + if(ret == CKR_OK) + { + ret = p11c_session_sign(sess, data, data_len, signature, signature_len); + p11c_session_unref_unlock(sess); + } + + RETURN(ret); +} + +static CK_RV +PC_C_SignUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len) +{ + ENTER(C_SignUpdate); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* RSA/DSA mechs don't support incremental crypto operations. */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_SignFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR signature, + CK_ULONG_PTR signature_len) +{ + ENTER(C_SignFinal); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* RSA/DSA mechs don't support incremental crypto operations. */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_SignRecoverInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, + CK_OBJECT_HANDLE key) +{ + ENTER(C_SignRecoverInit); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* TODO: Implement this */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_SignRecover(CK_SESSION_HANDLE session, CK_BYTE_PTR data, CK_ULONG data_len, + CK_BYTE_PTR signature, CK_ULONG_PTR signature_len) +{ + ENTER(C_SignRecover); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* TODO: Implement this */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_VerifyInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, + CK_OBJECT_HANDLE key) +{ + ENTER(C_VerifyInit); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* TODO: See if we need to implement this */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_Verify(CK_SESSION_HANDLE session, CK_BYTE_PTR data, CK_ULONG data_len, + CK_BYTE_PTR signature, CK_ULONG signature_len) +{ + ENTER(C_Verify); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* TODO: See if we need to implement this */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_VerifyUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, CK_ULONG part_len) +{ + ENTER(C_VerifyUpdate); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* RSA/DSA mechs don't support incremental crypto operations. */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_VerifyFinal(CK_SESSION_HANDLE session, CK_BYTE_PTR signature, + CK_ULONG signature_len) +{ + ENTER(C_VerifyFinal); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* RSA/DSA mechs don't support incremental crypto operations. */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_VerifyRecoverInit(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, + CK_OBJECT_HANDLE key) +{ + ENTER(C_VerifyRecoverInit); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* TODO: See if we need to implement this */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_VerifyRecover(CK_SESSION_HANDLE session, CK_BYTE_PTR signature, + CK_ULONG signature_len, CK_BYTE_PTR data, CK_ULONG_PTR data_len) +{ + ENTER(C_VerifyRecover); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* TODO: See if we need to implement this */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_DigestEncryptUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, + CK_ULONG part_len, CK_BYTE_PTR encrypted_part, + CK_ULONG_PTR encrypted_part_len) +{ + ENTER(C_DigestEncryptUpdate); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* RSA/DSA mechs don't support incremental crypto operations. */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_DecryptDigestUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR encrypted_part, + CK_ULONG encrypted_part_len, CK_BYTE_PTR part, + CK_ULONG_PTR part_len) +{ + ENTER(C_DecryptDigestUpdate); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* RSA/DSA mechs don't support incremental crypto operations. */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_SignEncryptUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR part, + CK_ULONG part_len, CK_BYTE_PTR encrypted_part, + CK_ULONG_PTR encrypted_part_len) +{ + ENTER(C_SignEncryptUpdate); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* RSA/DSA mechs don't support incremental crypto operations. */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_DecryptVerifyUpdate(CK_SESSION_HANDLE session, CK_BYTE_PTR encrypted_part, + CK_ULONG encrypted_part_len, CK_BYTE_PTR part, + CK_ULONG_PTR part_len) +{ + ENTER(C_DecryptVerifyUpdate); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* RSA/DSA mechs don't support incremental crypto operations. */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_GenerateKey(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, + CK_ATTRIBUTE_PTR templ, CK_ULONG count, + CK_OBJECT_HANDLE_PTR key) +{ + ENTER(C_GenerateKey); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* Let key generation happen via Windows interfaces */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_GenerateKeyPair(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, + CK_ATTRIBUTE_PTR public_key_template, CK_ULONG public_key_attribute_count, + CK_ATTRIBUTE_PTR private_key_template, CK_ULONG private_key_attribute_count, + CK_OBJECT_HANDLE_PTR public_key, CK_OBJECT_HANDLE_PTR private_key) +{ + ENTER(C_GenerateKeyPair); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* Let key generation happen via Windows interfaces */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_WrapKey(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, + CK_OBJECT_HANDLE wrapping_key, CK_OBJECT_HANDLE key, + CK_BYTE_PTR wrapped_key, CK_ULONG_PTR wrapped_key_len) +{ + ENTER(C_WrapKey); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* TODO: See if we need to implement this */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_UnwrapKey(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, + CK_OBJECT_HANDLE unwrapping_key, CK_BYTE_PTR wrapped_key, + CK_ULONG wrapped_key_len, CK_ATTRIBUTE_PTR templ, + CK_ULONG count, CK_OBJECT_HANDLE_PTR key) +{ + ENTER(C_UnwrapKey); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* TODO: See if we need to implement this */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_DeriveKey(CK_SESSION_HANDLE session, CK_MECHANISM_PTR mechanism, + CK_OBJECT_HANDLE base_key, CK_ATTRIBUTE_PTR templ, + CK_ULONG count, CK_OBJECT_HANDLE_PTR key) +{ + ENTER(C_DeriveKey); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* Can't do this with RSA */ + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_SeedRandom(CK_SESSION_HANDLE session, CK_BYTE_PTR seed, CK_ULONG seed_len) +{ + ENTER(C_SeedRandom); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* + * TODO: Perhaps at some point in the future we may want + * to see if we can hook into the Windows RNG + */ + + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_RV +PC_C_GenerateRandom(CK_SESSION_HANDLE session, CK_BYTE_PTR random_data, + CK_ULONG random_len) +{ + ENTER(C_GenerateRandom); + PREREQ(cryptoki_initialized, CKR_CRYPTOKI_NOT_INITIALIZED); + + /* + * TODO: Perhaps at some point in the future we may want + * to see if we can hook into the Windows RNG + */ + + RETURN(CKR_FUNCTION_NOT_SUPPORTED); +} + +static CK_FUNCTION_LIST functionList = { + { 2, 11 }, /* version */ + PC_C_Initialize, + PC_C_Finalize, + PC_C_GetInfo, + PC_C_GetFunctionList, + PC_C_GetSlotList, + PC_C_GetSlotInfo, + PC_C_GetTokenInfo, + PC_C_GetMechanismList, + PC_C_GetMechanismInfo, + PC_C_InitToken, + PC_C_InitPIN, + PC_C_SetPIN, + PC_C_OpenSession, + PC_C_CloseSession, + PC_C_CloseAllSessions, + PC_C_GetSessionInfo, + PC_C_GetOperationState, + PC_C_SetOperationState, + PC_C_Login, + PC_C_Logout, + PC_C_CreateObject, + PC_C_CopyObject, + PC_C_DestroyObject, + PC_C_GetObjectSize, + PC_C_GetAttributeValue, + PC_C_SetAttributeValue, + PC_C_FindObjectsInit, + PC_C_FindObjects, + PC_C_FindObjectsFinal, + PC_C_EncryptInit, + PC_C_Encrypt, + PC_C_EncryptUpdate, + PC_C_EncryptFinal, + PC_C_DecryptInit, + PC_C_Decrypt, + PC_C_DecryptUpdate, + PC_C_DecryptFinal, + PC_C_DigestInit, + PC_C_Digest, + PC_C_DigestUpdate, + PC_C_DigestKey, + PC_C_DigestFinal, + PC_C_SignInit, + PC_C_Sign, + PC_C_SignUpdate, + PC_C_SignFinal, + PC_C_SignRecoverInit, + PC_C_SignRecover, + PC_C_VerifyInit, + PC_C_Verify, + PC_C_VerifyUpdate, + PC_C_VerifyFinal, + PC_C_VerifyRecoverInit, + PC_C_VerifyRecover, + PC_C_DigestEncryptUpdate, + PC_C_DecryptDigestUpdate, + PC_C_SignEncryptUpdate, + PC_C_DecryptVerifyUpdate, + PC_C_GenerateKey, + PC_C_GenerateKeyPair, + PC_C_WrapKey, + PC_C_UnwrapKey, + PC_C_DeriveKey, + PC_C_SeedRandom, + PC_C_GenerateRandom, + PC_C_GetFunctionStatus, + PC_C_CancelFunction, + PC_C_WaitForSlotEvent +}; + +__declspec(dllexport) CK_RV +C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR list) +{ + if(!list) + return CKR_ARGUMENTS_BAD; + + *list = &functionList; + return CKR_OK; +} diff --git a/p11-capi.dep b/p11-capi.dep new file mode 100644 index 0000000..c04832a --- /dev/null +++ b/p11-capi.dep @@ -0,0 +1,69 @@ +# Microsoft Developer Studio Generated Dependency File, included by p11c.mak + +".\p11-capi-builtin.c" : \ + "..\..\..\..\program files\microsoft visual studio\vc98\include\basetsd.h"\ + ".\p11-capi-util.h"\ + ".\p11-capi.h"\ + ".\pkcs11\cryptoki.h"\ + ".\pkcs11\pkcs11.h"\ + ".\pkcs11\pkcs11f.h"\ + ".\pkcs11\pkcs11n.h"\ + ".\pkcs11\pkcs11t.h"\ + + +".\p11-capi-cert.c" : \ + "..\..\..\..\program files\microsoft visual studio\vc98\include\basetsd.h"\ + ".\p11-capi-util.h"\ + ".\p11-capi.h"\ + ".\pkcs11\cryptoki.h"\ + ".\pkcs11\pkcs11.h"\ + ".\pkcs11\pkcs11f.h"\ + ".\pkcs11\pkcs11t.h"\ + + +".\p11-capi-object.c" : \ + "..\..\..\..\program files\microsoft visual studio\vc98\include\basetsd.h"\ + ".\p11-capi-util.h"\ + ".\p11-capi.h"\ + ".\pkcs11\cryptoki.h"\ + ".\pkcs11\pkcs11.h"\ + ".\pkcs11\pkcs11f.h"\ + ".\pkcs11\pkcs11n.h"\ + ".\pkcs11\pkcs11t.h"\ + + +".\p11-capi-session.c" : \ + "..\..\..\..\program files\microsoft visual studio\vc98\include\basetsd.h"\ + ".\p11-capi-util.h"\ + ".\p11-capi.h"\ + ".\pkcs11\cryptoki.h"\ + ".\pkcs11\pkcs11.h"\ + ".\pkcs11\pkcs11f.h"\ + ".\pkcs11\pkcs11t.h"\ + + +".\p11-capi-trust.c" : \ + "..\..\..\..\program files\microsoft visual studio\vc98\include\basetsd.h"\ + ".\p11-capi-util.h"\ + ".\p11-capi.h"\ + ".\pkcs11\cryptoki.h"\ + ".\pkcs11\pkcs11.h"\ + ".\pkcs11\pkcs11f.h"\ + ".\pkcs11\pkcs11n.h"\ + ".\pkcs11\pkcs11t.h"\ + ".\x509-usages.h"\ + + +".\p11-capi-util.c" : \ + ".\p11-capi-util.h"\ + + +.\p11-capi.c : \ + "..\..\..\..\program files\microsoft visual studio\vc98\include\basetsd.h"\ + ".\p11-capi-util.h"\ + ".\p11-capi.h"\ + ".\pkcs11\cryptoki.h"\ + ".\pkcs11\pkcs11.h"\ + ".\pkcs11\pkcs11f.h"\ + ".\pkcs11\pkcs11t.h"\ + diff --git a/p11-capi.h b/p11-capi.h new file mode 100644 index 0000000..e522bfa --- /dev/null +++ b/p11-capi.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2007 Stef Walter + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef P11C_H +#define P11C_H + +/* -------------------------------------------------------------------- + * + * Session = P11cSession + * - A PKCS#11 Session + * + * Objects = P11cObject + * - There's a global list of objects in p11c-object.c indexed by + * object handle. + * - The object itself has no attributes or cached data, but knows how + * to load data when needed. + * - Each object has a unique key which guarantees we don't load the + * same object twice with two different object handles. + * + * Object Data = P11cObjectData + * - Object Data is owned by the Session + * - Loaded data and/or attributes for an object. + */ + +#define WIN32_LEAN_AND_MEAN +#define _WIN32_WINNT 0x400 +#include +#include + +#define P11c_ENCODINGS (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING) + +#define CRYPTOKI_EXPORTS +#include "pkcs11/pkcs11.h" + +#include "p11-capi-util.h" + +struct _P11cSlot; +struct _P11cObject; +struct _P11cObjectData; +struct _P11cSession; + +typedef struct _P11cSlot P11cSlot; +typedef struct _P11cObject P11cObject; +typedef struct _P11cObjectData P11cObjectData; +typedef struct _P11cSession P11cSession; + +/* ------------------------------------------------------------------ + * cryptoki-capi.c + * + * Module helper and logging functions. + */ + +#define DBG(args) p11c_debug args + +void p11c_debug (const char* msg, ...); + +/* + * Protect global data with these. + */ +void p11c_lock_global (void); +void p11c_unlock_global (void); + +/* + * Convert a GetLastError() windows error to a + * PKCS#11 return code. + */ +CK_RV p11c_winerr_to_ckr (DWORD werr); + +/* + * This stores data in the output buffer with appropriate + * PKCS#11 codes when the buffer is too short, or the caller + * just wants to know the length, etc. + */ +CK_RV p11c_return_data (CK_ATTRIBUTE_PTR attr, + CK_VOID_PTR src, DWORD slen); + +CK_RV p11c_return_data_raw (CK_VOID_PTR output, CK_ULONG_PTR n_output, + CK_VOID_PTR input, CK_ULONG n_input); + +/* + * This stores a string in the output buffer with appropriate + * PKCS#11 codes when the buffer is too short, or the caller + * just wants to know the length, etc. + */ +CK_RV p11c_return_string (CK_ATTRIBUTE_PTR attr, + WCHAR* string); + +CK_RV p11c_return_dword_as_bytes (CK_ATTRIBUTE_PTR attr, + DWORD value); + +CK_RV p11c_return_reversed_data (CK_ATTRIBUTE_PTR attr, + CK_VOID_PTR data, CK_ULONG length); + +CK_RV p11c_return_filetime (CK_ATTRIBUTE_PTR attr, + FILETIME* ftime); + +/* ------------------------------------------------------------------ */ + +typedef void (*P11cDestroyFunc)(void* data); + +#ifndef ASSERT +#include "assert.h" +#define ASSERT assert +#endif + +/* Represents 'any' class in searches */ +#define CKO_ANY CK_INVALID_HANDLE + + +#endif /* P11C_CAPI_H */ diff --git a/p11-capi.vcproj b/p11-capi.vcproj new file mode 100644 index 0000000..7c554d1 --- /dev/null +++ b/p11-capi.vcproj @@ -0,0 +1,428 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- cgit v1.2.3