summaryrefslogtreecommitdiff
path: root/ckcapi-cert.c
diff options
context:
space:
mode:
Diffstat (limited to 'ckcapi-cert.c')
-rw-r--r--ckcapi-cert.c182
1 files changed, 155 insertions, 27 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;