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