From a71df0e849b4e286f29ae6e26973961d3412cd83 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Tue, 9 Dec 2008 01:51:54 +0000 Subject: Complete the certificate support so most of the tests clear. --- ckcapi-cert.c | 182 ++++++++++++++++++++++++++++++++++++++++++++++++--------- ckcapi-trust.c | 1 + ckcapi.c | 48 +++++++++++++++ ckcapi.h | 3 + ckcapi.vcproj | 18 +----- 5 files changed, 208 insertions(+), 44 deletions(-) diff --git a/ckcapi-cert.c b/ckcapi-cert.c index 6d2dd53..fb3603a 100644 --- a/ckcapi-cert.c +++ b/ckcapi-cert.c @@ -46,7 +46,6 @@ typedef struct _CertObject * key, together with the data that runs off the end. */ int otype; - BYTE cert_data[1]; } CertObject; @@ -54,9 +53,75 @@ 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(size, 1); + 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) { @@ -95,12 +160,11 @@ cert_bool_attribute(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) /* * 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. + * - We just report on whether the certificate is a trusted root. */ case CKA_TRUSTED: - /* TODO: Implement */ + val = cdata->is_in_root ? CK_TRUE : CK_FALSE; + break; default: return CKR_ATTRIBUTE_TYPE_INVALID; @@ -114,6 +178,7 @@ cert_ulong_attribute(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) { CertObjectData* cdata = (CertObjectData*)objdata; CK_ULONG val; + CK_RV ret; ASSERT(objdata); @@ -142,7 +207,18 @@ cert_ulong_attribute(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) * extension or CERT_CTL_PROP_ID and look into CTL_USAGE structure. */ case CKA_CERTIFICATE_CATEGORY: - /* TODO: Implement */ + 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; @@ -166,27 +242,34 @@ cert_bytes_attribute(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) static CK_RV cert_date_attribute(CkCapiObjectData* objdata, CK_ATTRIBUTE_PTR attr) { + CertObjectData* cdata = (CertObjectData*)objdata; + FILETIME* ftime; + + ASSERT(cdata); + ASSERT(cdata->cert); + ASSERT(attr); + switch(attr->type) { - /* * Start date for the certificate. - * - TODO: Work out where to get this. - * pCertInfo->NotBefore; */ case CKA_START_DATE: + ftime = &cdata->cert->pCertInfo->NotBefore; break; /* * End date for the certificate. - * - TODO: Work out where to get this. - * pCertInfo->NotBefore; */ case CKA_END_DATE: + ftime = &cdata->cert->pCertInfo->NotAfter; break; + + default: + return CKR_ATTRIBUTE_TYPE_INVALID; }; - return CKR_ATTRIBUTE_TYPE_INVALID; + return ckcapi_return_filetime(attr, ftime); } static void @@ -216,6 +299,7 @@ cert_alloc_data(CkCapiSession* sess, CkCapiObject* obj, PCCERT_CONTEXT cert) 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; @@ -283,6 +367,51 @@ static const CkCapiObjectVtable cert_object_vtable = { 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) { @@ -367,13 +496,6 @@ ckcapi_cert_certificate_get_bytes(PCCERT_CONTEXT cert, CK_ATTRIBUTE_PTR attr) /* * 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: if(!CryptEncodeObject(X509_ASN_ENCODING, X509_MULTI_BYTE_INTEGER, @@ -402,23 +524,26 @@ ckcapi_cert_certificate_get_bytes(PCCERT_CONTEXT cert, CK_ATTRIBUTE_PTR attr) * * We don't support this. All our certificates are present * in full. + * + * - Spec says default to empty. */ case CKA_URL: - return CKR_ATTRIBUTE_TYPE_INVALID; + return ckcapi_return_data(attr, "", 0); /* * Checksum - * - TODO: Work out what to do here + * - This is the first 3 bytes of the SHA hash of the DER. */ case CKA_CHECK_VALUE: - return CKR_ATTRIBUTE_TYPE_INVALID; + return calculate_check_value(cert, attr); /* - * TODO: Should we support these? + * 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 CKR_ATTRIBUTE_TYPE_INVALID; + return ckcapi_return_data(attr, "", 0); /* Not supported */ @@ -461,18 +586,18 @@ register_cert_object(CkCapiSession* sess, PCCERT_CONTEXT cert, CkCapiObject** ob cobj->obj.id = 0; cobj->obj.unique_key = UNIQUE_KEY_AT(cobj, otype); - cobj->obj.unique_len = UNIQUE_KEY_VAR_LEN(cobj, otype, cert_data, len); + cobj->obj.unique_len = UNIQUE_KEY_VAR_LEN(cobj, otype, otype, len); cobj->obj.obj_funcs = &cert_object_vtable; /* Copy Issuer data in */ cobj->issuer.cbData = cert->pCertInfo->Issuer.cbData; - cobj->issuer.pbData = cobj->cert_data; + 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->cert_data + cobj->issuer.cbData; + cobj->serial.pbData = cobj->issuer.pbData + cobj->issuer.cbData; memcpy(cobj->serial.pbData, cert->pCertInfo->SerialNumber.pbData, cobj->serial.cbData); @@ -617,6 +742,9 @@ ckcapi_cert_find(CkCapiSession* sess, CK_OBJECT_CLASS cls, CK_ATTRIBUTE_PTR matc } 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; diff --git a/ckcapi-trust.c b/ckcapi-trust.c index 6107ac5..8519464 100644 --- a/ckcapi-trust.c +++ b/ckcapi-trust.c @@ -373,6 +373,7 @@ parse_restrictions(TrustObjectData* tdata) } } + free(rst); return CKR_OK; } diff --git a/ckcapi.c b/ckcapi.c index 50df4d7..c0e6036 100644 --- a/ckcapi.c +++ b/ckcapi.c @@ -290,6 +290,54 @@ ckcapi_return_reversed_data(CK_ATTRIBUTE_PTR attr, CK_VOID_PTR data, CK_ULONG le 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 diff --git a/ckcapi.h b/ckcapi.h index 54929bc..f174675 100644 --- a/ckcapi.h +++ b/ckcapi.h @@ -108,6 +108,9 @@ CK_RV ckcapi_return_dword_as_bytes (CK_ATTRIBUTE_PTR attr, 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); diff --git a/ckcapi.vcproj b/ckcapi.vcproj index 8ca8b36..01ef822 100644 --- a/ckcapi.vcproj +++ b/ckcapi.vcproj @@ -324,24 +324,8 @@ - - - - - -