summaryrefslogtreecommitdiff
path: root/ckcapi-object.c
diff options
context:
space:
mode:
Diffstat (limited to 'ckcapi-object.c')
-rw-r--r--ckcapi-object.c655
1 files changed, 655 insertions, 0 deletions
diff --git a/ckcapi-object.c b/ckcapi-object.c
new file mode 100644
index 0000000..531ad2f
--- /dev/null
+++ b/ckcapi-object.c
@@ -0,0 +1,655 @@
+
+#include "cryptoki-capi.h"
+
+#include <wtypes.h>
+#include <wincrypt.h>
+
+enum
+{
+ DATA_UNKNOWN = 0,
+ DATA_BOOL,
+ DATA_ULONG,
+ DATA_DATE,
+ 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:
+ 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:
+ 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:
+ return DATA_BYTES;
+
+ // CK_DATE data
+ case CKA_START_DATE:
+ case CKA_END_DATE:
+ return DATA_DATE;
+
+ // Arrays are nasty
+ case CKA_WRAP_TEMPLATE:
+ case CKA_ALLOWED_MECHANISMS:
+ case CKA_UNWRAP_TEMPLATE:
+ default:
+ return DATA_UNKNOWN;
+ };
+}
+
+CK_RV
+attribute_fill(CK_ATTRIBUTE_PTR attr, CK_ATTRIBUTE_TYPE type,
+ CK_VOID_PTR* val, CK_ULONG len)
+{
+ ASSERT(attr);
+ ASSERT(val);
+ ASSERT(len);
+
+ attr->type = type;
+
+ if(attr->pValue && attr->ulValueLen < len)
+ {
+ attr->ulValueLen = len;
+ return CKR_BUFFER_TOO_SMALL;
+ }
+
+ else if(!attr->pValue)
+ {
+ attr->ulValueLen = len;
+ return CKR_OK;
+ }
+
+ else
+ {
+ memcpy(attr->pValue, &val, len);
+ attr->ulValueLen = len;
+ return CKR_OK;
+ }
+}
+
+CK_RV
+attribute_match(CK_ATTRIBUTE_PTR attr, CK_ATTRIBUTE_TYPE type,
+ CK_VOID_PTR* val, CK_ULONG len)
+{
+ ASSERT(attr);
+ ASSERT(val);
+ ASSERT(len);
+
+ if(attr->pValue && attr->ulValueLen == len &&
+ memcmp(attr->pValue, &val, len);
+ return CKR_OK;
+ return CKR_CANCEL;
+}
+
+/* --------------------------------------------------------------------------
+ * CERTIFICATES
+ */
+
+static CK_RV
+cert_bool_attribute(CK_ATTRIBUTE_TYPE type, PCCERT_CONTEXT cert,
+ CK_ATTRIBUTE_PTR attr, CK_BBOOL fill)
+{
+ CK_BBOOL val;
+ switch(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.
+ * - Use CertGetCertificateChain to build up a chain
+ * for this certificate, and then look in the CERT_CHAIN_CONTEXT
+ * TrustStatus field.
+ */
+ case CKA_TRUSTED:
+ /* TODO: Implement */
+
+ default:
+ return CK_CANCEL;
+ };
+
+ if(fill)
+ return attribute_fill(attr, type, &val, sizeof(CK_BBOOL));
+ else
+ return attribute_match(attr, type, &val, sizeof(CK_BBOOL));
+}
+
+static CK_RV
+cert_ulong_attribute(CK_ATTRIBUTE_TYPE type, PCCERT_CONTEXT cert,
+ CK_ATTRIBUTE_PTR attr, CK_BBOOL fill)
+{
+ CK_ULONG val;
+ switch(type)
+ {
+
+ /*
+ * Object class.
+ * - Always CKO_CERTIFICATE for certificates.
+ */
+ case CKA_OBJECT_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:
+ /* TODO: Implement */
+
+ default:
+ return CK_CANCEL;
+ };
+
+ if(fill)
+ return attribute_fill(attr, type, &val, sizeof(CK_ULONG));
+ else
+ return attribute_match(attr, type, &val, sizeof(CK_ULONG));
+}
+
+static CK_RV
+cert_bytes_attribute(CK_ATTRIBUTE_TYPE type, PCCERT_CONTEXT cert,
+ CK_ATTRIBUTE_PTR attr, CK_BBOOL fill)
+{
+ switch(type)
+ {
+
+ /*
+ * Description of the object.
+ * - We use CAPI's CERT_FRIENDLY_NAME_PROP_ID property,
+ * converted into UTF8.
+ */
+ case CKA_LABEL:
+ xxxxx
+ 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:
+ xxxxx
+ break;
+
+ /*
+ * DER-encoding of the certificate subject name.
+ *
+ * We use CAPI's CERT_CONTEXT pCertInfo->Subject field
+ * directly.
+ */
+ case CKA_SUBJECT:
+ xxxxx
+ break;
+
+ /*
+ * DER-encoding of the certificate issuer name.
+ *
+ * We use CAPI's CERT_CONTEXT pCertInfo->Issuer field
+ * directly.
+ */
+ case CKA_ISSUER:
+ xxxxx
+ break;
+
+ /*
+ * DER-encoding of the certificate serial number.
+ *
+ * TODO:
+ * BOOL rc = CryptEncodeObject(X509_ASN_ENCODING,
+ * X509_MULTI_BYTE_INTEGER,
+ * &certContext->pCertInfo->SerialNumber,
+ * co->derSerial,
+ * &size);
+ */
+ case CKA_SERIAL_NUMBER:
+ xxxxx
+ break;
+
+ /*
+ * BER-encoding of the full certificate.
+ *
+ * We use CAPI's CERT_CONTEXT pbCertEncoded field directly.
+ */
+ case CKA_VALUE:
+ xxxxx
+ break;
+
+ /*
+ * 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.
+ */
+ case CKA_URL:
+ break;
+
+ /*
+ * Checksum
+ * - TODO: Work out what to do here
+ */
+ case CKA_CHECKSUM:
+ break;
+
+ /*
+ * TODO: Should we support these?
+ */
+ CKA_HASH_OF_SUBJECT_PUBLIC_KEY
+ CKA_HASH_OF_ISSUER_PUBLIC_KEY
+ break;
+
+ /* Not supported */
+
+ default:
+ break
+ };
+};
+
+static xxx
+cert_date_attribute()
+{
+ switch(type)
+ {
+
+ /*
+ * Start date for the certificate.
+ * - TODO: Work out where to get this.
+ */
+ pCertInfo->NotBefore;
+ case CKA_START_DATE:
+ xxxx;
+ break;
+
+ /*
+ * End date for the certificate.
+ * - TODO: Work out where to get this.
+ */
+ pCertInfo->NotAfter;
+ case CKA_END_DATE:
+ xxxx;
+ break;
+ };
+}
+xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
+
+static CK_RV
+cert_ulong_attribute(CK_ATTRIBUTE_TYPE type, PCCERT_CONTEXT cert,
+ CK_ATTRIBUTE_PTR attr)
+{
+ CK_ATTRIBUTE_PTR ret;
+ CK_BBOOL val;
+
+ switch(type)
+ {
+ case CKA_TOKEN:
+ val = CK_TRUE;
+ break;
+ case CKA_PRIVATE:
+ case CKA_MODIFIABLE:
+ val = CK_FALSE;
+ break;
+ };
+
+ /* If attr is initialized, just do a match */
+ if(attr.type == type)
+ {
+ if(attr->pValue && attr->ulValueLen == sizeof(CK_BBOOL) &&
+ memcmp(attr->pValue, &val, sizeof(CK_BBOOL));
+ return CKR_OK;
+ return CKR_CANCEL;
+ }
+
+ /* Otherwise fill in the value */
+ else
+ {
+ return prep_attribute(attr, type, &val, sizeof(CK_BBOOL);
+ }
+}
+
+static CK_ATTRIBUTE_PTR
+cert_load_attribute(CK_ATTRIBUTE_TYPE type, PCCERT_CONTEXT cert)
+{
+ CK_BBOOL vval = CK_FALSE;
+ CK_ULONG uval = 0;
+ CK_VOID_PTR val;
+ CK_ULONG n_val;
+
+ switch(type)
+ {
+ case CKA_CLASS:
+ uval = CKO_CERTIFICATE;
+ break;
+ case CKA_TOKEN;
+
+ }
+}
+
+static CK_BBOOL
+cert_match_attr(CK_ATTRIBUTE_PTR match, PCCERT_CONTEXT cert)
+{
+ CK_ATTRIBUTE_PTR attr;
+ CK_BBOOL ret;
+
+ attr = cert_load_attribute(match->type, cert);
+ if(!attr)
+ return CK_FALSE;
+
+ ret = (attr->ulValueLen == match->ulValueLen &&
+ memcmp(attr->pValue, match->pValue, attr->ulValueLen) == 0);
+
+ free_attribute(attr);
+ return ret;
+}
+
+static CK_BBOOL
+cert_match(CK_ATTRIBUTE_PTR matches, CK_ULONG count,
+ PCCERT_CONTEXT cert)
+{
+ CK_ULONG i;
+
+ for(i = 0; i < count; ++i)
+ {
+ if(!cert_match_attr(&match[i], cert))
+ return CK_FALSE;
+ }
+
+ return CK_TRUE;
+}
+
+static CK_RV
+gather_store_certs(const char* store_name, Array* arr,
+ CK_ATTRIBUTE_PTR match, CK_ULONG count)
+{
+ PCCERT_CONTEXT cert = NULL;
+ CK_OBJECT_HANDLE obj;
+ HCERTSTORE store;
+ DWORD err;
+ CK_RV ret = CKR_OK;
+
+ store = CertOpenSystemStore((HCRYPTPROV)NULL, store_name);
+ if(store == NULL)
+ {
+ err = GetLastError();
+
+ /* Store not found, we don't care */
+ if(err == ERROR_FILE_NOT_FOUND)
+ return CKR_OK;
+
+ else
+ return ckcapi_winerr_to_ckr(err);
+ }
+
+ /* Match each certificate */
+ while((cert = CertEnumCertificatesInStore(store, cert)) != NULL)
+ {
+ if(match_cert(match, count, cert))
+ {
+ obj = register_cert(store, cert);
+ if(!obj)
+ {
+ ret = CKR_HOST_MEMORY;
+ break;
+ }
+
+ ckcapi_util_array_append(arr, obj);
+ }
+ }
+
+ ASSERT(store);
+ CertCloseStore(store, 0);
+
+ return ret;
+}
+
+/* ----------------------------------------------------------------------------
+ * FIND
+ */
+
+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;
+}
+
+
+
+
+CK_RV
+gather_objects(Array* arr, CK_ATTRIBUTE_PTR match, CK_ULONG count)
+{
+ CK_OBJECT_CLASS ocls = CK_INVALID_HANDLE;
+ CK_RV ret = CKR_OK;
+
+ get_ulong_attribute(CKA_CLASS, match, count, &ocls);
+ switch(ocls)
+ {
+ /* all different classes */
+ case CK_INVALID_HANDLE:
+ case CKO_CERTIFICATE:
+ ret = gather_certificates(arr, match, count);
+ break;
+ case CKO_PUBLIC_KEY:
+ case CKO_PRIVATE_KEY:
+ default:
+ break;
+ };
+
+ return ret;
+}
+
+void
+cleanup_find_operation(Session* sess)
+{
+ ASSERT(sess->operation_type == OPERATION_FIND);
+ if(sess->operation_data)
+ ckcapi_util_array_free((Array*)sess->operation_data, TRUE);
+ sess->operation_type = OPERATION_NONE;
+ sess->operation_data = NULL;
+ sess->operation_cancel = NULL;
+}
+
+CK_RV
+ckcapi_object_find_init(Session* sess, CK_ATTRIBUTE_PTR match,
+ CK_ULONG count)
+{
+ Array* arr;
+ CK_RV ret;
+
+ ASSERT(sess);
+ ASSERT(!count || match);
+
+ if(sess->operation_type != OPERATION_NONE)
+ return CKR_OPERATION_ACTIVE;
+
+ arr = ckcapi_util_array_new(0, 1, sizeof(CK_OBJECT_HANDLE));
+ if(!arr)
+ return CKR_HOST_MEMORY;
+
+ ret = gather_objects(arr, match, count);
+ if(ret != CKR_OK)
+ {
+ ckcapi_util_array_free(arr, TRUE);
+ return ret;
+ }
+
+ sess->operation_type = OPERATION_FIND;
+ sess->operation_data = arr;
+ sess->operation_cancel = cleanup_find_operation;
+
+ return CKR_OK;
+}
+
+CK_RV
+ckcapi_object_find(Session* sess, CK_OBJECT_HANDLE_PTR objects,
+ CK_ULONG max_object_count, CK_ULONG_PTR object_count)
+{
+ Array* 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 = (Array*)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_util_array_index(arr, CK_OBJECT_HANDLE, i);
+ ckcapi_util_array_remove_range(arr, 0, *object_count);
+
+ return CKR_OK;
+}
+
+CK_RV
+ckcapi_objects_find_final(Session* sess)
+{
+ ASSERT(sess);
+
+ if(sess->operation_type != OPERATION_FIND)
+ return CKR_OPERATION_NOT_INITIALIZED;
+
+ cleanup_find_operation(sess);
+ return CKR_OK;
+}
+