#include "cryptoki-capi.h" #include #include 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; }