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; }; }