From 3d8ed01d2653c45e52821ba00ac72099a12600e1 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Fri, 27 Apr 2007 03:19:46 +0000 Subject: --- ckcapi-cert.c | 520 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 520 insertions(+) create mode 100644 ckcapi-cert.c (limited to 'ckcapi-cert.c') diff --git a/ckcapi-cert.c b/ckcapi-cert.c new file mode 100644 index 0000000..d1b9b32 --- /dev/null +++ b/ckcapi-cert.c @@ -0,0 +1,520 @@ + + +typedef struct _CertRef +{ + + +} +CertRef; + +static CK_RV +copy_static_data(CK_VOID_PTR val, CK_ULONG_PTR len, + CK_VOID_PTR data, DWORD cb) +{ + if(cb == 0) + return CKR_CANCEL; + + if(!val) + { + *len = cb; + return CKR_OK; + } + + if(cb > *len) + { + *len = cb; + return CKR_BUFFER_TOO_SMALL; + } + + *len = cb; + memcpy(val, data, cb); + return CKR_OK; +} + +static CK_RV +cert_bool_attribute(void* obj, CK_ATTRIBUTE_TYPE type, + CK_VOID_PTR data, CK_ULONG_PTR len) +{ + PCCERT_CONTEXT cert = (PCCERT_CONTEXT)obj; + CK_BBOOL val; + + ASSERT(obj); + ASSERT(ret); + + 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; + }; + + return copy_static_data(data, len, &val, sizeof(CK_BBOOL)); +} + +static CK_RV +cert_ulong_attribute(void* obj, CK_ATTRIBUTE_TYPE type, + CK_VOID_PTR data, CK_ULONG_PTR len) +{ + PCCERT_CONTEXT cert = (PCCERT_CONTEXT)obj; + CK_ULONG val; + + ASSERT(obj); + ASSERT(ret); + + 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(*len < sizeof(CK_ULONG)) + { + *len = sizeof(CK_ULONG); + return CKR_BUFFER_TOO_SMALL; + } + + return copy_static_data(data, len, &val, sizeof(CK_ULONG)); +} + +static CK_RV +cert_bytes_attribute(void* obj, CK_ATTRIBUTE_TYPE type, + CkCapiAttrAction action, CK_ATTRIBUTE_PTR attr) +{ + PCCERT_CONTEXT cert = (PCCERT_CONTEXT)obj; + CK_VOID_PTR val; + CK_ULONG len; + size_t maxa; + BOOL r; + + + DWORD size = *len; + + int allocated = 0; + + ASSERT(obj); + ASSERT(ret); + + /* + * We don't need to retrieve the full value of any of + * these fields if we don't have to. Just calculate + * the max amount to retrieve, so we can compare, or + * notify caller of real length. + */ + maxa = attr->ulValueLen; + if(maxa == 0) + maxa = 4; + /* Keep dynamic allocations under a 64k. */ + if(maxa > 0xFFFF) + maxa = 0xFFFF; + + switch(type) + { + + /* + * Description of the object. + * - We use CAPI's CERT_FRIENDLY_NAME_PROP_ID property, + * converted into UTF8. + * - Yes this is slow, but this is not really a property + * that's searched on or retrieved intensively. + */ + case CKA_LABEL: + { + DWORD size = maxa * sizeof(WCHAR); + WCHAR* utf16; +xxxxxx, this is wrong, second call may be needed + /* Get the UTF16 string, a worst case of twice as long as UTF8 */ + WCHAR* utf16 = alloca(size); + r = CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, + utf16, &size); + if(!r) + { + DWORD err = GetLastError(); + if(err == ERROR_MORE_DATA) + { + /* Truncate long names */ + utf16[maxa - 1] = 0; + r = TRUE; + } + else if (err != CRYPT_E_NOT_FOUND) + { + return ckcapi_winerr_to_ckr(err); + } + } + + if(r) + { + /* Convert it */ + if(!ckcapi_util_utf16_to_utf8(utf16, &val, &len)) + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + allocated = 1; + } + else + { + /* We always like to have a decent label */ + val = "Unnamed Certificate"; + len = strlen(val); + allocated = 0; + } + } + 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: + { + BOOL r; + + val = alloca(size); + r = CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, + val, &size); + if(!r) + { + DWORD err = GetLastError(); + if(err == CRYPT_E_NOT_FOUND) + return CKR_CANCEL; + if(err != ERROR_MORE_DATA) + return ckcapi_winerr_to_ckr(err); + } + + *len = size; + } + break; + + /* + * DER-encoding of the certificate subject name. + * + * We use CAPI's CERT_CONTEXT pCertInfo->Subject field + * directly. + */ + case CKA_SUBJECT: + if(cert->pCertInfo->Subject.pbData == NULL || + cert->pCertInfo->Subject.cbData == 0) + return CKR_CANCEL; + val = cert->pCertInfo->Subject.pbData; + len = cert->pCertInfo->Subject.cbData; + break; + + /* + * DER-encoding of the certificate issuer name. + * + * We use CAPI's CERT_CONTEXT pCertInfo->Issuer field + * directly. + */ + case CKA_ISSUER: + if(cert->pCertInfo->Issuer.pbData == NULL || + cert->pCertInfo->Issuer.cbData == 0) + return CKR_CANCEL; + val = cert->pCertInfo->Issuer.pbData; + len = cert->pCertInfo->Issuer.cbData; + 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: + { + BOOL r; + DWORD size = *len; + + r = CryptEncodeObject(X509_ASN_ENCODING, X509_MULTI_BYTE_INTEGER, + &cert->pCertInfo->SerialNumber, val, *len); + if(!r) + { + DWORD err = GetLastError(); + if(err == ERROR_FILE_NOT_FOUND) + return CKR_GENERAL_ERROR; + if(err != ERROR_MORE_DATA) + return ckcapi_winerr_to_ckr(err); + } + + len = size; + } + break; + + /* + * BER-encoding of the full certificate. + * + * We use CAPI's CERT_CONTEXT pbCertEncoded field directly. + */ + case CKA_VALUE: + if(cert->pbCertEncoded == NULL || + cert->cbCertEncoded == 0) + return CKR_CANCEL; + val = cert->pbCertEncoded; + len = cert->cbCertEncoded; + break; + 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: + return CKR_CANCEL; + + /* + * 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 CK_RV +cert_bytes_attribute(void* obj, CK_ATTRIBUTE_TYPE type, + CK_VOID_PTR data, CK_ULONG_PTR len) +{ + PCCERT_CONTEXT cert = (PCCERT_CONTEXT)obj; + + ASSERT(sizeof(CK_ULONG) == sizeof(DWORD)); + ASSERT(obj); + + switch(type) + { + + /* + * Description of the object. + * - We use CAPI's CERT_FRIENDLY_NAME_PROP_ID property, + * converted into UTF8. + * - Yes this is slow, but this is not really a property + * that's searched on or retrieved intensively. + */ + case CKA_LABEL: + { + WCHAR* utf16; + + /* Get the UTF16 string, a worst case of twice as long as UTF8 */ + (*len) *= sizeof(WCHAR); + utf16 = alloca(*len); + r = CertGetCertificateContextProperty(cert, CERT_FRIENDLY_NAME_PROP_ID, + utf16, (DWORD*)len)) + { + DWORD err = GetLastError(); + if(err == CRYPT_E_NOT_FOUND) + utf16 = L"Unnamed Certificate"; + else + return ckcapi_winerr_to_ckr(err); + } + + /* Convert the data into the buffer */ + return ckcapi_util_utf16_to_utf8(utf16, data, len)); + } + 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: + { + if(!CertGetCertificateContextProperty(cert, CERT_KEY_IDENTIFIER_PROP_ID, + data, (DWORD*)len)) + { + DWORD err = GetLastError(); + if(err == CRYPT_E_NOT_FOUND) + return CKR_CANCEL; + return ckcapi_winerr_to_ckr(err); + } + } + break; + + /* + * DER-encoding of the certificate subject name. + * + * We use CAPI's CERT_CONTEXT pCertInfo->Subject field + * directly. + */ + case CKA_SUBJECT: + return copy_static_data(data, len, cert->pCertInfo->Subject.pbData, + cert->pCertInfo->Subject.cbData); + + /* + * DER-encoding of the certificate issuer name. + * + * We use CAPI's CERT_CONTEXT pCertInfo->Issuer field + * directly. + */ + case CKA_ISSUER: + return copy_static_data(data, len, cert->pCertInfo->Issuer.pbData, + cert->pCertInfo->Issuer.cbData); + + /* + * 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, + &cert->pCertInfo->SerialNumber, data, len)) + { + DWORD err = GetLastError(); + if(err == ERROR_FILE_NOT_FOUND) + return CKR_GENERAL_ERROR; + return ckcapi_winerr_to_ckr(err); + } + } + break; + + /* + * BER-encoding of the full certificate. + * + * We use CAPI's CERT_CONTEXT pbCertEncoded field directly. + */ + case CKA_VALUE: + return copy_static_data(data, len, cert->pbCertEncoded, + cert->cbCertEncoded); + + /* + * 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 + }; + + return CKR_CANCEL; +} + +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->NotBefore; + */ + case CKA_END_DATE: + xxxx; + break; + }; +} -- cgit v1.2.3