From ae56e5c788e3ed6c60d98d445574d3042aaae24f Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Tue, 9 Dec 2008 20:59:55 +0000 Subject: Move into module folder --- module/p11-capi-key.c | 1083 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1083 insertions(+) create mode 100644 module/p11-capi-key.c (limited to 'module/p11-capi-key.c') diff --git a/module/p11-capi-key.c b/module/p11-capi-key.c new file mode 100644 index 0000000..ed306d4 --- /dev/null +++ b/module/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; +} -- cgit v1.2.3