summaryrefslogtreecommitdiff
path: root/p11-capi-key.c
diff options
context:
space:
mode:
Diffstat (limited to 'p11-capi-key.c')
-rw-r--r--p11-capi-key.c1083
1 files changed, 1083 insertions, 0 deletions
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;
+}