summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2008-12-09 01:51:54 +0000
committerStef Walter <stef@memberwebs.com>2008-12-09 01:51:54 +0000
commita71df0e849b4e286f29ae6e26973961d3412cd83 (patch)
tree71fff8c8e760561287ddae96ffdfb051e5f6a220
parentf84cec479d41fb143487af7e78a6b3056f6b8823 (diff)
Complete the certificate support so most of the tests clear.
-rw-r--r--ckcapi-cert.c182
-rw-r--r--ckcapi-trust.c1
-rw-r--r--ckcapi.c48
-rw-r--r--ckcapi.h3
-rw-r--r--ckcapi.vcproj18
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,10 +53,76 @@ 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)
{
CertObjectData* cdata = (CertObjectData*)objdata;
@@ -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 @@
</FileConfiguration>
</File>
<File
- RelativePath="ckcapi-trust.c"
+ RelativePath=".\ckcapi-trust.c"
>
- <FileConfiguration
- Name="Debug|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- PreprocessorDefinitions=""
- />
- </FileConfiguration>
- <FileConfiguration
- Name="Release|Win32"
- >
- <Tool
- Name="VCCLCompilerTool"
- PreprocessorDefinitions=""
- />
- </FileConfiguration>
</File>
<File
RelativePath="ckcapi-util.c"