[CRYPT32] Sync with Wine Staging 4.0. CORE-15682
[reactos.git] / dll / win32 / crypt32 / cert.c
index 695b792..0065ba6 100644 (file)
  *
  */
 
-#define WIN32_NO_STATUS
-#define _INC_WINDOWS
-#define COM_NO_WINDOWS_H
-
 #include <assert.h>
 #include <stdarg.h>
 
 #define NONAMELESSUNION
-#include <windef.h>
-#include <winbase.h>
-#include <wincrypt.h>
-//#include "winnls.h"
-#include <rpc.h>
-#include <wine/debug.h>
-#include <wine/unicode.h>
+#include "ntstatus.h"
+#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "wine/winternl.h"
+#define CRYPT_OID_INFO_HAS_EXTRA_FIELDS
+#include "wincrypt.h"
+#include "snmp.h"
+#include "bcrypt.h"
+#include "winnls.h"
+#include "rpc.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
 #include "crypt32_private.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
@@ -42,7 +44,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(crypt);
  * CertGetCertificateContextProperty, and are particular to the store in which
  * the property exists (which is separate from the context.)
  */
-static BOOL CertContext_GetProperty(void *context, DWORD dwPropId,
+static BOOL CertContext_GetProperty(cert_t *cert, DWORD dwPropId,
  void *pvData, DWORD *pcbData);
 
 /* Internal version of CertSetCertificateContextProperty that sets properties
@@ -50,7 +52,7 @@ static BOOL CertContext_GetProperty(void *context, DWORD dwPropId,
  * type.) Doesn't handle special cases, since they're handled by
  * CertSetCertificateContextProperty anyway.
  */
-static BOOL CertContext_SetProperty(void *context, DWORD dwPropId,
+static BOOL CertContext_SetProperty(cert_t *cert, DWORD dwPropId,
  DWORD dwFlags, const void *pvData);
 
 BOOL WINAPI CertAddEncodedCertificateToStore(HCERTSTORE hCertStore,
@@ -113,12 +115,190 @@ BOOL WINAPI CertAddEncodedCertificateToSystemStoreW(LPCWSTR pszCertStoreName,
     return ret;
 }
 
+static const context_vtbl_t cert_vtbl;
+
+static void Cert_free(context_t *context)
+{
+    cert_t *cert = (cert_t*)context;
+
+    CryptMemFree(cert->ctx.pbCertEncoded);
+    LocalFree(cert->ctx.pCertInfo);
+}
+
+static context_t *Cert_clone(context_t *context, WINECRYPT_CERTSTORE *store, BOOL use_link)
+{
+    cert_t *cert;
+
+    if(use_link) {
+        cert = (cert_t*)Context_CreateLinkContext(sizeof(CERT_CONTEXT), context, store);
+        if(!cert)
+            return NULL;
+    }else {
+        const cert_t *cloned = (const cert_t*)context;
+        DWORD size = 0;
+        BOOL res;
+
+        cert = (cert_t*)Context_CreateDataContext(sizeof(CERT_CONTEXT), &cert_vtbl, store);
+        if(!cert)
+            return NULL;
+
+        Context_CopyProperties(&cert->ctx, &cloned->ctx);
+
+        cert->ctx.dwCertEncodingType = cloned->ctx.dwCertEncodingType;
+        cert->ctx.pbCertEncoded = CryptMemAlloc(cloned->ctx.cbCertEncoded);
+        memcpy(cert->ctx.pbCertEncoded, cloned->ctx.pbCertEncoded, cloned->ctx.cbCertEncoded);
+        cert->ctx.cbCertEncoded = cloned->ctx.cbCertEncoded;
+
+        /* FIXME: We don't need to decode the object here, we could just clone cert info. */
+        res = CryptDecodeObjectEx(cert->ctx.dwCertEncodingType, X509_CERT_TO_BE_SIGNED,
+         cert->ctx.pbCertEncoded, cert->ctx.cbCertEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL,
+         &cert->ctx.pCertInfo, &size);
+        if(!res) {
+            CertFreeCertificateContext(&cert->ctx);
+            return NULL;
+        }
+    }
+
+    cert->ctx.hCertStore = store;
+    return &cert->base;
+}
+
+static const context_vtbl_t cert_vtbl = {
+    Cert_free,
+    Cert_clone
+};
+
+static BOOL add_cert_to_store(WINECRYPT_CERTSTORE *store, const CERT_CONTEXT *cert,
+ DWORD add_disposition, BOOL use_link, PCCERT_CONTEXT *ret_context)
+{
+    const CERT_CONTEXT *existing = NULL;
+    BOOL ret = TRUE, inherit_props = FALSE;
+    context_t *new_context = NULL;
+
+    switch (add_disposition)
+    {
+    case CERT_STORE_ADD_ALWAYS:
+        break;
+    case CERT_STORE_ADD_NEW:
+    case CERT_STORE_ADD_REPLACE_EXISTING:
+    case CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES:
+    case CERT_STORE_ADD_USE_EXISTING:
+    case CERT_STORE_ADD_NEWER:
+    case CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES:
+    {
+        BYTE hashToAdd[20];
+        DWORD size = sizeof(hashToAdd);
+
+        ret = CertGetCertificateContextProperty(cert, CERT_HASH_PROP_ID,
+         hashToAdd, &size);
+        if (ret)
+        {
+            CRYPT_HASH_BLOB blob = { sizeof(hashToAdd), hashToAdd };
+
+            existing = CertFindCertificateInStore(store, cert->dwCertEncodingType, 0,
+             CERT_FIND_SHA1_HASH, &blob, NULL);
+        }
+        break;
+    }
+    default:
+        FIXME("Unimplemented add disposition %d\n", add_disposition);
+        SetLastError(E_INVALIDARG);
+        return FALSE;
+    }
+
+    switch (add_disposition)
+    {
+    case CERT_STORE_ADD_ALWAYS:
+        break;
+    case CERT_STORE_ADD_NEW:
+        if (existing)
+        {
+            TRACE("found matching certificate, not adding\n");
+            SetLastError(CRYPT_E_EXISTS);
+            return FALSE;
+        }
+        break;
+    case CERT_STORE_ADD_REPLACE_EXISTING:
+        break;
+    case CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES:
+        if (use_link)
+            FIXME("CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES: semi-stub for links\n");
+        if (existing)
+            inherit_props = TRUE;
+        break;
+    case CERT_STORE_ADD_USE_EXISTING:
+        if(use_link)
+            FIXME("CERT_STORE_ADD_USE_EXISTING: semi-stub for links\n");
+        if (existing)
+        {
+            Context_CopyProperties(existing, cert);
+            if (ret_context)
+                *ret_context = CertDuplicateCertificateContext(existing);
+            return TRUE;
+        }
+        break;
+    case CERT_STORE_ADD_NEWER:
+        if (existing && CompareFileTime(&existing->pCertInfo->NotBefore, &cert->pCertInfo->NotBefore) >= 0)
+        {
+            TRACE("existing certificate is newer, not adding\n");
+            SetLastError(CRYPT_E_EXISTS);
+            return FALSE;
+        }
+        break;
+    case CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES:
+        if (existing)
+        {
+            if (CompareFileTime(&existing->pCertInfo->NotBefore, &cert->pCertInfo->NotBefore) >= 0)
+            {
+                TRACE("existing certificate is newer, not adding\n");
+                SetLastError(CRYPT_E_EXISTS);
+                return FALSE;
+            }
+            inherit_props = TRUE;
+        }
+        break;
+    }
+
+    /* FIXME: We have tests that this works, but what should we really do in this case? */
+    if(!store) {
+        if(ret_context)
+            *ret_context = CertDuplicateCertificateContext(cert);
+        return TRUE;
+    }
+
+    ret = store->vtbl->certs.addContext(store, context_from_ptr(cert), existing ? context_from_ptr(existing) : NULL,
+     (ret_context || inherit_props) ? &new_context : NULL, use_link);
+    if(!ret)
+        return FALSE;
+
+    if(inherit_props)
+        Context_CopyProperties(context_ptr(new_context), existing);
+
+    if(ret_context)
+        *ret_context = context_ptr(new_context);
+    else if(new_context)
+        Context_Release(new_context);
+
+    TRACE("returning %d\n", ret);
+    return ret;
+}
+
+BOOL WINAPI CertAddCertificateContextToStore(HCERTSTORE hCertStore, PCCERT_CONTEXT pCertContext,
+ DWORD dwAddDisposition, PCCERT_CONTEXT *ppStoreContext)
+{
+    WINECRYPT_CERTSTORE *store = hCertStore;
+
+    TRACE("(%p, %p, %08x, %p)\n", hCertStore, pCertContext, dwAddDisposition, ppStoreContext);
+
+    return add_cert_to_store(store, pCertContext, dwAddDisposition, FALSE, ppStoreContext);
+}
+
 BOOL WINAPI CertAddCertificateLinkToStore(HCERTSTORE hCertStore,
  PCCERT_CONTEXT pCertContext, DWORD dwAddDisposition,
  PCCERT_CONTEXT *ppCertContext)
 {
     static int calls;
-    PWINECRYPT_CERTSTORE store = (PWINECRYPT_CERTSTORE)hCertStore;
+    WINECRYPT_CERTSTORE *store = (WINECRYPT_CERTSTORE*)hCertStore;
 
     if (!(calls++))
         FIXME("(%p, %p, %08x, %p): semi-stub\n", hCertStore, pCertContext,
@@ -130,14 +310,14 @@ BOOL WINAPI CertAddCertificateLinkToStore(HCERTSTORE hCertStore,
         SetLastError(E_INVALIDARG);
         return FALSE;
     }
-    return CertAddCertificateContextToStore(hCertStore, pCertContext,
-     dwAddDisposition, ppCertContext);
+    return add_cert_to_store(hCertStore, pCertContext, dwAddDisposition, TRUE, ppCertContext);
 }
 
 PCCERT_CONTEXT WINAPI CertCreateCertificateContext(DWORD dwCertEncodingType,
  const BYTE *pbCertEncoded, DWORD cbCertEncoded)
 {
-    PCERT_CONTEXT cert = NULL;
+    cert_t *cert = NULL;
+    BYTE *data = NULL;
     BOOL ret;
     PCERT_INFO certInfo = NULL;
     DWORD size = 0;
@@ -154,81 +334,65 @@ PCCERT_CONTEXT WINAPI CertCreateCertificateContext(DWORD dwCertEncodingType,
     ret = CryptDecodeObjectEx(dwCertEncodingType, X509_CERT_TO_BE_SIGNED,
      pbCertEncoded, cbCertEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL,
      &certInfo, &size);
-    if (ret)
-    {
-        BYTE *data = NULL;
+    if (!ret)
+        return NULL;
 
-        cert = Context_CreateDataContext(sizeof(CERT_CONTEXT));
-        if (!cert)
-            goto end;
-        data = CryptMemAlloc(cbCertEncoded);
-        if (!data)
-        {
-            CertFreeCertificateContext(cert);
-            cert = NULL;
-            goto end;
-        }
-        memcpy(data, pbCertEncoded, cbCertEncoded);
-        cert->dwCertEncodingType = dwCertEncodingType;
-        cert->pbCertEncoded      = data;
-        cert->cbCertEncoded      = cbCertEncoded;
-        cert->pCertInfo          = certInfo;
-        cert->hCertStore         = 0;
+    cert = (cert_t*)Context_CreateDataContext(sizeof(CERT_CONTEXT), &cert_vtbl, &empty_store);
+    if (!cert)
+        return NULL;
+    data = CryptMemAlloc(cbCertEncoded);
+    if (!data)
+    {
+        Context_Release(&cert->base);
+        return NULL;
     }
 
-end:
-    return cert;
+    memcpy(data, pbCertEncoded, cbCertEncoded);
+    cert->ctx.dwCertEncodingType = dwCertEncodingType;
+    cert->ctx.pbCertEncoded      = data;
+    cert->ctx.cbCertEncoded      = cbCertEncoded;
+    cert->ctx.pCertInfo          = certInfo;
+    cert->ctx.hCertStore         = &empty_store;
+
+    return &cert->ctx;
 }
 
-PCCERT_CONTEXT WINAPI CertDuplicateCertificateContext(
- PCCERT_CONTEXT pCertContext)
+PCCERT_CONTEXT WINAPI CertDuplicateCertificateContext(PCCERT_CONTEXT pCertContext)
 {
     TRACE("(%p)\n", pCertContext);
 
     if (!pCertContext)
         return NULL;
 
-    Context_AddRef((void *)pCertContext, sizeof(CERT_CONTEXT));
+    Context_AddRef(&cert_from_ptr(pCertContext)->base);
     return pCertContext;
 }
 
-static void CertDataContext_Free(void *context)
-{
-    PCERT_CONTEXT certContext = context;
-
-    CryptMemFree(certContext->pbCertEncoded);
-    LocalFree(certContext->pCertInfo);
-}
-
 BOOL WINAPI CertFreeCertificateContext(PCCERT_CONTEXT pCertContext)
 {
-    BOOL ret = TRUE;
-
     TRACE("(%p)\n", pCertContext);
 
     if (pCertContext)
-        ret = Context_Release((void *)pCertContext, sizeof(CERT_CONTEXT),
-         CertDataContext_Free);
-    return ret;
+        Context_Release(&cert_from_ptr(pCertContext)->base);
+    return TRUE;
 }
 
 DWORD WINAPI CertEnumCertificateContextProperties(PCCERT_CONTEXT pCertContext,
  DWORD dwPropId)
 {
-    PCONTEXT_PROPERTY_LIST properties = Context_GetProperties(
-     pCertContext, sizeof(CERT_CONTEXT));
+    cert_t *cert = cert_from_ptr(pCertContext);
     DWORD ret;
 
     TRACE("(%p, %d)\n", pCertContext, dwPropId);
 
-    if (properties)
-        ret = ContextPropertyList_EnumPropIDs(properties, dwPropId);
+    if (cert->base.properties)
+        ret = ContextPropertyList_EnumPropIDs(cert->base.properties, dwPropId);
     else
         ret = 0;
     return ret;
 }
 
-static BOOL CertContext_GetHashProp(void *context, DWORD dwPropId,
+static BOOL CertContext_GetHashProp(cert_t *cert, DWORD dwPropId,
  ALG_ID algID, const BYTE *toHash, DWORD toHashLen, void *pvData,
  DWORD *pcbData)
 {
@@ -238,7 +402,7 @@ static BOOL CertContext_GetHashProp(void *context, DWORD dwPropId,
     {
         CRYPT_DATA_BLOB blob = { *pcbData, pvData };
 
-        ret = CertContext_SetProperty(context, dwPropId, 0, &blob);
+        ret = CertContext_SetProperty(cert, dwPropId, 0, &blob);
     }
     return ret;
 }
@@ -264,19 +428,16 @@ static BOOL CertContext_CopyParam(void *pvData, DWORD *pcbData, const void *pb,
     return ret;
 }
 
-static BOOL CertContext_GetProperty(void *context, DWORD dwPropId,
+static BOOL CertContext_GetProperty(cert_t *cert, DWORD dwPropId,
  void *pvData, DWORD *pcbData)
 {
-    PCCERT_CONTEXT pCertContext = context;
-    PCONTEXT_PROPERTY_LIST properties =
-     Context_GetProperties(context, sizeof(CERT_CONTEXT));
     BOOL ret;
     CRYPT_DATA_BLOB blob;
 
-    TRACE("(%p, %d, %p, %p)\n", context, dwPropId, pvData, pcbData);
+    TRACE("(%p, %d, %p, %p)\n", cert, dwPropId, pvData, pcbData);
 
-    if (properties)
-        ret = ContextPropertyList_FindProperty(properties, dwPropId, &blob);
+    if (cert->base.properties)
+        ret = ContextPropertyList_FindProperty(cert->base.properties, dwPropId, &blob);
     else
         ret = FALSE;
     if (ret)
@@ -287,49 +448,49 @@ static BOOL CertContext_GetProperty(void *context, DWORD dwPropId,
         switch (dwPropId)
         {
         case CERT_SHA1_HASH_PROP_ID:
-            ret = CertContext_GetHashProp(context, dwPropId, CALG_SHA1,
-             pCertContext->pbCertEncoded, pCertContext->cbCertEncoded, pvData,
+            ret = CertContext_GetHashProp(cert, dwPropId, CALG_SHA1,
+             cert->ctx.pbCertEncoded, cert->ctx.cbCertEncoded, pvData,
              pcbData);
             break;
         case CERT_MD5_HASH_PROP_ID:
-            ret = CertContext_GetHashProp(context, dwPropId, CALG_MD5,
-             pCertContext->pbCertEncoded, pCertContext->cbCertEncoded, pvData,
+            ret = CertContext_GetHashProp(cert, dwPropId, CALG_MD5,
+             cert->ctx.pbCertEncoded, cert->ctx.cbCertEncoded, pvData,
              pcbData);
             break;
         case CERT_SUBJECT_NAME_MD5_HASH_PROP_ID:
-            ret = CertContext_GetHashProp(context, dwPropId, CALG_MD5,
-             pCertContext->pCertInfo->Subject.pbData,
-             pCertContext->pCertInfo->Subject.cbData,
+            ret = CertContext_GetHashProp(cert, dwPropId, CALG_MD5,
+             cert->ctx.pCertInfo->Subject.pbData,
+             cert->ctx.pCertInfo->Subject.cbData,
              pvData, pcbData);
             break;
         case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID:
-            ret = CertContext_GetHashProp(context, dwPropId, CALG_MD5,
-             pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,
-             pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData,
+            ret = CertContext_GetHashProp(cert, dwPropId, CALG_MD5,
+             cert->ctx.pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,
+             cert->ctx.pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData,
              pvData, pcbData);
             break;
         case CERT_ISSUER_SERIAL_NUMBER_MD5_HASH_PROP_ID:
-            ret = CertContext_GetHashProp(context, dwPropId, CALG_MD5,
-             pCertContext->pCertInfo->SerialNumber.pbData,
-             pCertContext->pCertInfo->SerialNumber.cbData,
+            ret = CertContext_GetHashProp(cert, dwPropId, CALG_MD5,
+             cert->ctx.pCertInfo->SerialNumber.pbData,
+             cert->ctx.pCertInfo->SerialNumber.cbData,
              pvData, pcbData);
             break;
         case CERT_SIGNATURE_HASH_PROP_ID:
-            ret = CryptHashToBeSigned(0, pCertContext->dwCertEncodingType,
-             pCertContext->pbCertEncoded, pCertContext->cbCertEncoded, pvData,
+            ret = CryptHashToBeSigned(0, cert->ctx.dwCertEncodingType,
+             cert->ctx.pbCertEncoded, cert->ctx.cbCertEncoded, pvData,
              pcbData);
             if (ret && pvData)
             {
                 CRYPT_DATA_BLOB blob = { *pcbData, pvData };
 
-                ret = CertContext_SetProperty(context, dwPropId, 0, &blob);
+                ret = CertContext_SetProperty(cert, dwPropId, 0, &blob);
             }
             break;
         case CERT_KEY_IDENTIFIER_PROP_ID:
         {
             PCERT_EXTENSION ext = CertFindExtension(
-             szOID_SUBJECT_KEY_IDENTIFIER, pCertContext->pCertInfo->cExtension,
-             pCertContext->pCertInfo->rgExtension);
+             szOID_SUBJECT_KEY_IDENTIFIER, cert->ctx.pCertInfo->cExtension,
+             cert->ctx.pCertInfo->rgExtension);
 
             if (ext)
             {
@@ -344,7 +505,7 @@ static BOOL CertContext_GetProperty(void *context, DWORD dwPropId,
                 {
                     ret = CertContext_CopyParam(pvData, pcbData, value.pbData,
                      value.cbData);
-                    CertContext_SetProperty(context, dwPropId, 0, &value);
+                    CertContext_SetProperty(cert, dwPropId, 0, &value);
                 }
             }
             else
@@ -372,19 +533,25 @@ void CRYPT_FixKeyProvInfoPointers(PCRYPT_KEY_PROV_INFO info)
     provNameLen = (lstrlenW(info->pwszProvName) + 1) * sizeof(WCHAR);
     data += provNameLen;
 
-    info->rgProvParam = (PCRYPT_KEY_PROV_PARAM)data;
-    data += info->cProvParam * sizeof(CRYPT_KEY_PROV_PARAM);
-
-    for (i = 0; i < info->cProvParam; i++)
+    if (info->cProvParam)
     {
-        info->rgProvParam[i].pbData = data;
-        data += info->rgProvParam[i].cbData;
+        info->rgProvParam = (PCRYPT_KEY_PROV_PARAM)data;
+        data += info->cProvParam * sizeof(CRYPT_KEY_PROV_PARAM);
+
+        for (i = 0; i < info->cProvParam; i++)
+        {
+            info->rgProvParam[i].pbData = data;
+            data += info->rgProvParam[i].cbData;
+        }
     }
+    else
+        info->rgProvParam = NULL;
 }
 
 BOOL WINAPI CertGetCertificateContextProperty(PCCERT_CONTEXT pCertContext,
  DWORD dwPropId, void *pvData, DWORD *pcbData)
 {
+    cert_t *cert = cert_from_ptr(pCertContext);
     BOOL ret;
 
     TRACE("(%p, %d, %p, %p)\n", pCertContext, dwPropId, pvData, pcbData);
@@ -399,22 +566,14 @@ BOOL WINAPI CertGetCertificateContextProperty(PCCERT_CONTEXT pCertContext,
         ret = FALSE;
         break;
     case CERT_ACCESS_STATE_PROP_ID:
-        if (pCertContext->hCertStore)
-            ret = CertGetStoreProperty(pCertContext->hCertStore, dwPropId,
-             pvData, pcbData);
-        else
-        {
-            DWORD state = 0;
-
-            ret = CertContext_CopyParam(pvData, pcbData, &state, sizeof(state));
-        }
+        ret = CertGetStoreProperty(cert->ctx.hCertStore, dwPropId, pvData, pcbData);
         break;
     case CERT_KEY_PROV_HANDLE_PROP_ID:
     {
         CERT_KEY_CONTEXT keyContext;
         DWORD size = sizeof(keyContext);
 
-        ret = CertContext_GetProperty((void *)pCertContext,
+        ret = CertContext_GetProperty(cert,
          CERT_KEY_CONTEXT_PROP_ID, &keyContext, &size);
         if (ret)
             ret = CertContext_CopyParam(pvData, pcbData, &keyContext.hCryptProv,
@@ -422,13 +581,13 @@ BOOL WINAPI CertGetCertificateContextProperty(PCCERT_CONTEXT pCertContext,
         break;
     }
     case CERT_KEY_PROV_INFO_PROP_ID:
-        ret = CertContext_GetProperty((void *)pCertContext, dwPropId, pvData,
+        ret = CertContext_GetProperty(cert, dwPropId, pvData,
          pcbData);
         if (ret && pvData)
             CRYPT_FixKeyProvInfoPointers(pvData);
         break;
     default:
-        ret = CertContext_GetProperty((void *)pCertContext, dwPropId, pvData,
+        ret = CertContext_GetProperty(cert, dwPropId, pvData,
          pcbData);
     }
 
@@ -485,7 +644,7 @@ static void CRYPT_CopyKeyProvInfo(PCRYPT_KEY_PROV_INFO to,
     }
 }
 
-static BOOL CertContext_SetKeyProvInfoProperty(PCONTEXT_PROPERTY_LIST properties,
+static BOOL CertContext_SetKeyProvInfoProperty(CONTEXT_PROPERTY_LIST *properties,
  const CRYPT_KEY_PROV_INFO *info)
 {
     BOOL ret;
@@ -516,16 +675,14 @@ static BOOL CertContext_SetKeyProvInfoProperty(PCONTEXT_PROPERTY_LIST properties
     return ret;
 }
 
-static BOOL CertContext_SetProperty(void *context, DWORD dwPropId,
+static BOOL CertContext_SetProperty(cert_t *cert, DWORD dwPropId,
  DWORD dwFlags, const void *pvData)
 {
-    PCONTEXT_PROPERTY_LIST properties =
-     Context_GetProperties(context, sizeof(CERT_CONTEXT));
     BOOL ret;
 
-    TRACE("(%p, %d, %08x, %p)\n", context, dwPropId, dwFlags, pvData);
+    TRACE("(%p, %d, %08x, %p)\n", cert, dwPropId, dwFlags, pvData);
 
-    if (!properties)
+    if (!cert->base.properties)
         ret = FALSE;
     else
     {
@@ -548,29 +705,30 @@ static BOOL CertContext_SetProperty(void *context, DWORD dwPropId,
         case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID:
         case CERT_ENROLLMENT_PROP_ID:
         case CERT_CROSS_CERT_DIST_POINTS_PROP_ID:
+        case CERT_OCSP_RESPONSE_PROP_ID:
         case CERT_RENEWAL_PROP_ID:
         {
             if (pvData)
             {
                 const CRYPT_DATA_BLOB *blob = pvData;
 
-                ret = ContextPropertyList_SetProperty(properties, dwPropId,
+                ret = ContextPropertyList_SetProperty(cert->base.properties, dwPropId,
                  blob->pbData, blob->cbData);
             }
             else
             {
-                ContextPropertyList_RemoveProperty(properties, dwPropId);
+                ContextPropertyList_RemoveProperty(cert->base.properties, dwPropId);
                 ret = TRUE;
             }
             break;
         }
         case CERT_DATE_STAMP_PROP_ID:
             if (pvData)
-                ret = ContextPropertyList_SetProperty(properties, dwPropId,
+                ret = ContextPropertyList_SetProperty(cert->base.properties, dwPropId,
                  pvData, sizeof(FILETIME));
             else
             {
-                ContextPropertyList_RemoveProperty(properties, dwPropId);
+                ContextPropertyList_RemoveProperty(cert->base.properties, dwPropId);
                 ret = TRUE;
             }
             break;
@@ -586,22 +744,22 @@ static BOOL CertContext_SetProperty(void *context, DWORD dwPropId,
                     ret = FALSE;
                 }
                 else
-                    ret = ContextPropertyList_SetProperty(properties, dwPropId,
+                    ret = ContextPropertyList_SetProperty(cert->base.properties, dwPropId,
                      (const BYTE *)keyContext, keyContext->cbSize);
             }
             else
             {
-                ContextPropertyList_RemoveProperty(properties, dwPropId);
+                ContextPropertyList_RemoveProperty(cert->base.properties, dwPropId);
                 ret = TRUE;
             }
             break;
         }
         case CERT_KEY_PROV_INFO_PROP_ID:
             if (pvData)
-                ret = CertContext_SetKeyProvInfoProperty(properties, pvData);
+                ret = CertContext_SetKeyProvInfoProperty(cert->base.properties, pvData);
             else
             {
-                ContextPropertyList_RemoveProperty(properties, dwPropId);
+                ContextPropertyList_RemoveProperty(cert->base.properties, dwPropId);
                 ret = TRUE;
             }
             break;
@@ -610,7 +768,7 @@ static BOOL CertContext_SetProperty(void *context, DWORD dwPropId,
             CERT_KEY_CONTEXT keyContext;
             DWORD size = sizeof(keyContext);
 
-            ret = CertContext_GetProperty(context, CERT_KEY_CONTEXT_PROP_ID,
+            ret = CertContext_GetProperty(cert, CERT_KEY_CONTEXT_PROP_ID,
              &keyContext, &size);
             if (ret)
             {
@@ -625,7 +783,7 @@ static BOOL CertContext_SetProperty(void *context, DWORD dwPropId,
                 keyContext.hCryptProv = 0;
                 keyContext.dwKeySpec = AT_SIGNATURE;
             }
-            ret = CertContext_SetProperty(context, CERT_KEY_CONTEXT_PROP_ID,
+            ret = CertContext_SetProperty(cert, CERT_KEY_CONTEXT_PROP_ID,
              0, &keyContext);
             break;
         }
@@ -658,7 +816,7 @@ BOOL WINAPI CertSetCertificateContextProperty(PCCERT_CONTEXT pCertContext,
         SetLastError(E_INVALIDARG);
         return FALSE;
     }
-    ret = CertContext_SetProperty((void *)pCertContext, dwPropId, dwFlags,
+    ret = CertContext_SetProperty(cert_from_ptr(pCertContext), dwPropId, dwFlags,
      pvData);
     TRACE("returning %d\n", ret);
     return ret;
@@ -838,10 +996,9 @@ static BOOL container_matches_cert(PCCERT_CONTEXT pCert, LPCSTR container,
 {
     CRYPT_KEY_PROV_INFO copy;
     WCHAR containerW[MAX_PATH];
-    BOOL matches = FALSE;
+    BOOL matches;
 
-    MultiByteToWideChar(CP_ACP, 0, container, -1,
-     containerW, sizeof(containerW) / sizeof(containerW[0]));
+    MultiByteToWideChar(CP_ACP, 0, container, -1, containerW, ARRAY_SIZE(containerW));
     /* We make a copy of the CRYPT_KEY_PROV_INFO because the caller expects
      * keyProvInfo->pwszContainerName to be NULL or a heap-allocated container
      * name.
@@ -996,7 +1153,7 @@ static BOOL cert_prov_info_matches_cert(PCCERT_CONTEXT pCert)
 BOOL WINAPI CryptFindCertificateKeyProvInfo(PCCERT_CONTEXT pCert,
  DWORD dwFlags, void *pvReserved)
 {
-    BOOL matches = FALSE;
+    BOOL matches;
 
     TRACE("(%p, %08x, %p)\n", pCert, dwFlags, pvReserved);
 
@@ -1091,6 +1248,12 @@ BOOL WINAPI CertComparePublicKeyInfo(DWORD dwCertEncodingType,
 
     TRACE("(%08x, %p, %p)\n", dwCertEncodingType, pPublicKey1, pPublicKey2);
 
+    /* RSA public key data should start with ASN_SEQUENCE,
+     * otherwise it's not a RSA_CSP_PUBLICKEYBLOB.
+     */
+    if (!pPublicKey1->PublicKey.cbData || pPublicKey1->PublicKey.pbData[0] != ASN_SEQUENCE)
+        dwCertEncodingType = 0;
+
     switch (GET_CERT_ENCODING_TYPE(dwCertEncodingType))
     {
     case 0:    /* Seems to mean "raw binary bits" */
@@ -1116,32 +1279,21 @@ BOOL WINAPI CertComparePublicKeyInfo(DWORD dwCertEncodingType,
         ret = FALSE;
         if (CryptDecodeObject(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB,
                     pPublicKey1->PublicKey.pbData, pPublicKey1->PublicKey.cbData,
-                    0, NULL, &length))
+                    CRYPT_DECODE_ALLOC_FLAG, &pblob1, &length))
         {
-            pblob1 = CryptMemAlloc(length);
             if (CryptDecodeObject(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB,
-                    pPublicKey1->PublicKey.pbData, pPublicKey1->PublicKey.cbData,
-                    0, pblob1, &length))
+                        pPublicKey2->PublicKey.pbData, pPublicKey2->PublicKey.cbData,
+                        CRYPT_DECODE_ALLOC_FLAG, &pblob2, &length))
             {
-                if (CryptDecodeObject(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB,
-                            pPublicKey2->PublicKey.pbData, pPublicKey2->PublicKey.cbData,
-                            0, NULL, &length))
-                {
-                    pblob2 = CryptMemAlloc(length);
-                    if (CryptDecodeObject(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB,
-                            pPublicKey2->PublicKey.pbData, pPublicKey2->PublicKey.cbData,
-                            0, pblob2, &length))
-                    {
-                        /* The RSAPUBKEY structure directly follows the BLOBHEADER */
-                        RSAPUBKEY *pk1 = (LPVOID)(pblob1 + 1),
-                                  *pk2 = (LPVOID)(pblob2 + 1);
-                        ret = (pk1->bitlen == pk2->bitlen) && (pk1->pubexp == pk2->pubexp)
-                                 && !memcmp(pk1 + 1, pk2 + 1, pk1->bitlen/8);
-                    }
-                    CryptMemFree(pblob2);
-                }
+                /* The RSAPUBKEY structure directly follows the BLOBHEADER */
+                RSAPUBKEY *pk1 = (LPVOID)(pblob1 + 1),
+                          *pk2 = (LPVOID)(pblob2 + 1);
+                ret = (pk1->bitlen == pk2->bitlen) && (pk1->pubexp == pk2->pubexp)
+                         && !memcmp(pk1 + 1, pk2 + 1, pk1->bitlen/8);
+
+                LocalFree(pblob2);
             }
-            CryptMemFree(pblob1);
+            LocalFree(pblob1);
         }
 
         break;
@@ -1170,9 +1322,30 @@ DWORD WINAPI CertGetPublicKeyLength(DWORD dwCertEncodingType,
     }
     else
     {
+        PCCRYPT_OID_INFO info;
         DWORD size;
         PBYTE buf;
-        BOOL ret = CryptDecodeObjectEx(dwCertEncodingType,
+        BOOL ret;
+
+        info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, pPublicKey->Algorithm.pszObjId, 0);
+        if (info)
+        {
+            HCRYPTKEY key;
+
+            TRACE("public key algid %#x (%s)\n", info->u.Algid, debugstr_a(pPublicKey->Algorithm.pszObjId));
+
+            ret = CryptImportPublicKeyInfo(I_CryptGetDefaultCryptProv(info->u.Algid), dwCertEncodingType, pPublicKey, &key);
+            if (ret)
+            {
+                size = sizeof(len);
+                ret = CryptGetKeyParam(key, KP_KEYLEN, (BYTE *)&len, &size, 0);
+                CryptDestroyKey(key);
+                return len;
+            }
+            /* fallback to RSA */
+        }
+
+        ret = CryptDecodeObjectEx(dwCertEncodingType,
          RSA_CSP_PUBLICKEYBLOB, pPublicKey->PublicKey.pbData,
          pPublicKey->PublicKey.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &buf,
          &size);
@@ -1320,6 +1493,8 @@ static BOOL compare_cert_by_cert_id(PCCERT_CONTEXT pCertContext, DWORD dwType,
                 ret = !memcmp(buf, id->u.KeyId.pbData, size);
                 CryptMemFree(buf);
             }
+            else
+                ret = FALSE;
         }
         else
             ret = FALSE;
@@ -1360,6 +1535,8 @@ static BOOL compare_cert_by_signature_hash(PCCERT_CONTEXT pCertContext, DWORD dw
             ret = !memcmp(buf, hash->pbData, size);
             CryptMemFree(buf);
         }
+        else
+            ret = FALSE;
     }
     else
         ret = FALSE;
@@ -1639,7 +1816,7 @@ PCCERT_CONTEXT WINAPI CertFindCertificateInStore(HCERTSTORE hCertStore,
     }
 
     if (find)
-        ret = find(hCertStore, dwFlags, dwType, pvPara, pPrevCertContext);
+        ret = find(hCertStore, dwType, dwFlags, pvPara, pPrevCertContext);
     else if (compare)
         ret = cert_compare_certs_in_store(hCertStore, pPrevCertContext,
          compare, dwType, dwFlags, pvPara);
@@ -1733,6 +1910,12 @@ PCCERT_CONTEXT WINAPI CertGetIssuerCertificateFromStore(HCERTSTORE hCertStore,
             CertFreeCertificateContext(ret);
             ret = NULL;
         }
+        if (CRYPT_IsCertificateSelfSigned(pSubjectContext))
+        {
+            CertFreeCertificateContext(ret);
+            ret = NULL;
+            SetLastError(CRYPT_E_SELF_SIGNED);
+        }
     }
     TRACE("returning %p\n", ret);
     return ret;
@@ -1743,7 +1926,7 @@ typedef struct _OLD_CERT_REVOCATION_STATUS {
     DWORD dwIndex;
     DWORD dwError;
     DWORD dwReason;
-} OLD_CERT_REVOCATION_STATUS, *POLD_CERT_REVOCATION_STATUS;
+} OLD_CERT_REVOCATION_STATUS;
 
 typedef BOOL (WINAPI *CertVerifyRevocationFunc)(DWORD, DWORD, DWORD,
  void **, DWORD, PCERT_REVOCATION_PARA, PCERT_REVOCATION_STATUS);
@@ -2012,7 +2195,7 @@ BOOL WINAPI CryptHashCertificate(HCRYPTPROV_LEGACY hCryptProv, ALG_ID Algid,
      pbEncoded, cbEncoded, pbComputedHash, pcbComputedHash);
 
     if (!hCryptProv)
-        hCryptProv = CRYPT_GetDefaultProvider();
+        hCryptProv = I_CryptGetDefaultCryptProv(0);
     if (!Algid)
         Algid = CALG_SHA1;
     if (ret)
@@ -2041,7 +2224,7 @@ BOOL WINAPI CryptHashPublicKeyInfo(HCRYPTPROV_LEGACY hCryptProv, ALG_ID Algid,
      dwCertEncodingType, pInfo, pbComputedHash, pcbComputedHash);
 
     if (!hCryptProv)
-        hCryptProv = CRYPT_GetDefaultProvider();
+        hCryptProv = I_CryptGetDefaultCryptProv(0);
     if (!Algid)
         Algid = CALG_MD5;
     if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING)
@@ -2093,7 +2276,7 @@ BOOL WINAPI CryptHashToBeSigned(HCRYPTPROV_LEGACY hCryptProv,
         HCRYPTHASH hHash;
 
         if (!hCryptProv)
-            hCryptProv = CRYPT_GetDefaultProvider();
+            hCryptProv = I_CryptGetDefaultCryptProv(0);
         oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
          info->SignatureAlgorithm.pszObjId, 0);
         if (!oidInfo)
@@ -2142,7 +2325,7 @@ BOOL WINAPI CryptSignCertificate(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProv,
     if (info->dwGroupId == CRYPT_HASH_ALG_OID_GROUP_ID)
     {
         if (!hCryptProv)
-            hCryptProv = CRYPT_GetDefaultProvider();
+            hCryptProv = I_CryptGetDefaultCryptProv(0);
         ret = CryptCreateHash(hCryptProv, info->u.Algid, 0, 0, &hHash);
         if (ret)
         {
@@ -2229,10 +2412,14 @@ BOOL WINAPI CryptSignAndEncodeCertificate(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCrypt
                         }
                         CryptMemFree(hash);
                     }
+                    else
+                        ret = FALSE;
                 }
             }
             CryptMemFree(encoded);
         }
+        else
+            ret = FALSE;
     }
     return ret;
 }
@@ -2248,22 +2435,13 @@ BOOL WINAPI CryptVerifyCertificateSignature(HCRYPTPROV_LEGACY hCryptProv,
      CRYPT_VERIFY_CERT_SIGN_ISSUER_PUBKEY, pPublicKey, 0, NULL);
 }
 
-static BOOL CRYPT_VerifyCertSignatureFromPublicKeyInfo(HCRYPTPROV_LEGACY hCryptProv,
- DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pubKeyInfo,
- const CERT_SIGNED_CONTENT_INFO *signedCert)
+static BOOL CRYPT_VerifySignature(HCRYPTPROV_LEGACY hCryptProv, DWORD dwCertEncodingType,
+    CERT_PUBLIC_KEY_INFO *pubKeyInfo, const CERT_SIGNED_CONTENT_INFO *signedCert, const CRYPT_OID_INFO *info)
 {
     BOOL ret;
     HCRYPTKEY key;
-    PCCRYPT_OID_INFO info;
     ALG_ID pubKeyID, hashID;
 
-    info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
-     signedCert->SignatureAlgorithm.pszObjId, 0);
-    if (!info || info->dwGroupId != CRYPT_SIGN_ALG_OID_GROUP_ID)
-    {
-        SetLastError(NTE_BAD_ALGID);
-        return FALSE;
-    }
     hashID = info->u.Algid;
     if (info->ExtraInfo.cbData >= sizeof(ALG_ID))
         pubKeyID = *(ALG_ID *)info->ExtraInfo.pbData;
@@ -2271,7 +2449,7 @@ static BOOL CRYPT_VerifyCertSignatureFromPublicKeyInfo(HCRYPTPROV_LEGACY hCryptP
         pubKeyID = hashID;
     /* Load the default provider if necessary */
     if (!hCryptProv)
-        hCryptProv = CRYPT_GetDefaultProvider();
+        hCryptProv = I_CryptGetDefaultCryptProv(0);
     ret = CryptImportPublicKeyInfoEx(hCryptProv, dwCertEncodingType,
      pubKeyInfo, pubKeyID, 0, NULL, &key);
     if (ret)
@@ -2293,6 +2471,249 @@ static BOOL CRYPT_VerifyCertSignatureFromPublicKeyInfo(HCRYPTPROV_LEGACY hCryptP
     return ret;
 }
 
+static BOOL CNG_CalcHash(const WCHAR *algorithm, const CERT_SIGNED_CONTENT_INFO *signedCert,
+        BYTE **hash_value, DWORD *hash_len)
+{
+    BCRYPT_HASH_HANDLE hash = NULL;
+    BCRYPT_ALG_HANDLE alg = NULL;
+    NTSTATUS status;
+    DWORD size;
+
+    if ((status = BCryptOpenAlgorithmProvider(&alg, algorithm, NULL, 0)))
+        goto done;
+
+    if ((status = BCryptCreateHash(alg, &hash, NULL, 0, NULL, 0, 0)))
+        goto done;
+
+    if ((status = BCryptHashData(hash, signedCert->ToBeSigned.pbData, signedCert->ToBeSigned.cbData, 0)))
+        goto done;
+
+    if ((status = BCryptGetProperty(hash, BCRYPT_HASH_LENGTH, (BYTE *)hash_len, sizeof(*hash_len), &size, 0)))
+        goto done;
+
+    if (!(*hash_value = CryptMemAlloc(*hash_len)))
+    {
+        status = STATUS_NO_MEMORY;
+        goto done;
+    }
+
+    if ((status = BCryptFinishHash(hash, *hash_value, *hash_len, 0)))
+    {
+        CryptMemFree(*hash_value);
+        goto done;
+    }
+
+done:
+    if (hash) BCryptDestroyHash(hash);
+    if (alg)  BCryptCloseAlgorithmProvider(alg, 0);
+    if (status) SetLastError(RtlNtStatusToDosError(status));
+    return status == 0;
+}
+
+static BOOL CNG_ImportECCPubKey(CERT_PUBLIC_KEY_INFO *pubKeyInfo, BCRYPT_KEY_HANDLE *key)
+{
+    DWORD blob_magic, ecckey_len, size;
+    BCRYPT_ALG_HANDLE alg = NULL;
+    BCRYPT_ECCKEY_BLOB *ecckey;
+    const WCHAR *sign_algo;
+    char **ecc_curve;
+    NTSTATUS status;
+
+    if (!pubKeyInfo->PublicKey.cbData)
+    {
+        SetLastError(NTE_BAD_ALGID);
+        return FALSE;
+    }
+
+    if (pubKeyInfo->PublicKey.pbData[0] != 0x4)
+    {
+        FIXME("Compressed ECC curves (%02x) not yet supported\n", pubKeyInfo->PublicKey.pbData[0]);
+        SetLastError(NTE_BAD_ALGID);
+        return FALSE;
+    }
+
+    if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_OBJECT_IDENTIFIER, pubKeyInfo->Algorithm.Parameters.pbData,
+            pubKeyInfo->Algorithm.Parameters.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &ecc_curve, &size))
+        return FALSE;
+
+    if (!strcmp(*ecc_curve, szOID_ECC_CURVE_P256))
+    {
+        sign_algo = BCRYPT_ECDSA_P256_ALGORITHM;
+        blob_magic = BCRYPT_ECDSA_PUBLIC_P256_MAGIC;
+    }
+    else if (!strcmp(*ecc_curve, szOID_ECC_CURVE_P384))
+    {
+        sign_algo = BCRYPT_ECDSA_P384_ALGORITHM;
+        blob_magic = BCRYPT_ECDSA_PUBLIC_P384_MAGIC;
+    }
+    else
+    {
+        FIXME("Unsupported ecc curve type: %s\n", *ecc_curve);
+        sign_algo = NULL;
+        blob_magic = 0;
+    }
+    LocalFree(ecc_curve);
+
+    if (!sign_algo)
+    {
+        SetLastError(NTE_BAD_ALGID);
+        return FALSE;
+    }
+
+    if ((status = BCryptOpenAlgorithmProvider(&alg, sign_algo, NULL, 0)))
+        goto done;
+
+    ecckey_len = sizeof(BCRYPT_ECCKEY_BLOB) + pubKeyInfo->PublicKey.cbData - 1;
+    if (!(ecckey = CryptMemAlloc(ecckey_len)))
+    {
+        status = STATUS_NO_MEMORY;
+        goto done;
+    }
+
+    ecckey->dwMagic = blob_magic;
+    ecckey->cbKey = (pubKeyInfo->PublicKey.cbData - 1) / 2;
+    memcpy(ecckey + 1, pubKeyInfo->PublicKey.pbData + 1, pubKeyInfo->PublicKey.cbData - 1);
+
+    status = BCryptImportKeyPair(alg, NULL, BCRYPT_ECCPUBLIC_BLOB, key, (BYTE*)ecckey, ecckey_len, 0);
+    CryptMemFree(ecckey);
+
+done:
+    if (alg) BCryptCloseAlgorithmProvider(alg, 0);
+    if (status) SetLastError(RtlNtStatusToDosError(status));
+    return !status;
+}
+
+static BOOL CNG_ImportPubKey(CERT_PUBLIC_KEY_INFO *pubKeyInfo, BCRYPT_KEY_HANDLE *key)
+{
+    if (!strcmp(pubKeyInfo->Algorithm.pszObjId, szOID_ECC_PUBLIC_KEY))
+        return CNG_ImportECCPubKey(pubKeyInfo, key);
+
+    FIXME("Unsupported public key type: %s\n", debugstr_a(pubKeyInfo->Algorithm.pszObjId));
+    SetLastError(NTE_BAD_ALGID);
+    return FALSE;
+}
+
+static BOOL CNG_PrepareSignatureECC(BYTE *encoded_sig, DWORD encoded_size, BYTE **sig_value, DWORD *sig_len)
+{
+    CERT_ECC_SIGNATURE *ecc_sig;
+    DWORD size;
+    int i;
+
+    if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ECC_SIGNATURE, encoded_sig, encoded_size,
+            CRYPT_DECODE_ALLOC_FLAG, NULL, &ecc_sig, &size))
+        return FALSE;
+
+    if (!ecc_sig->r.cbData || !ecc_sig->s.cbData)
+    {
+        LocalFree(ecc_sig);
+        SetLastError(ERROR_INVALID_DATA);
+        return FALSE;
+    }
+
+    *sig_len = ecc_sig->r.cbData + ecc_sig->s.cbData;
+    if (!(*sig_value = CryptMemAlloc(*sig_len)))
+    {
+        LocalFree(ecc_sig);
+        SetLastError(ERROR_OUTOFMEMORY);
+        return FALSE;
+    }
+
+    for (i = 0; i < ecc_sig->r.cbData; i++)
+        (*sig_value)[i] = ecc_sig->r.pbData[ecc_sig->r.cbData - i - 1];
+    for (i = 0; i < ecc_sig->s.cbData; i++)
+        (*sig_value)[ecc_sig->r.cbData + i] = ecc_sig->s.pbData[ecc_sig->s.cbData - i - 1];
+
+    LocalFree(ecc_sig);
+    return TRUE;
+}
+
+static BOOL CNG_PrepareSignature(CERT_PUBLIC_KEY_INFO *pubKeyInfo, const CERT_SIGNED_CONTENT_INFO *signedCert,
+    BYTE **sig_value, DWORD *sig_len)
+{
+    BYTE *encoded_sig;
+    BOOL ret = FALSE;
+    int i;
+
+    if (!signedCert->Signature.cbData)
+    {
+        SetLastError(ERROR_INVALID_DATA);
+        return FALSE;
+    }
+
+    if (!(encoded_sig = CryptMemAlloc(signedCert->Signature.cbData)))
+    {
+        SetLastError(ERROR_OUTOFMEMORY);
+        return FALSE;
+    }
+
+    for (i = 0; i < signedCert->Signature.cbData; i++)
+        encoded_sig[i] = signedCert->Signature.pbData[signedCert->Signature.cbData - i - 1];
+
+    if (!strcmp(pubKeyInfo->Algorithm.pszObjId, szOID_ECC_PUBLIC_KEY))
+        ret = CNG_PrepareSignatureECC(encoded_sig, signedCert->Signature.cbData, sig_value, sig_len);
+    else
+    {
+        FIXME("Unsupported public key type: %s\n", debugstr_a(pubKeyInfo->Algorithm.pszObjId));
+        SetLastError(NTE_BAD_ALGID);
+    }
+
+    CryptMemFree(encoded_sig);
+    return ret;
+}
+
+static BOOL CNG_VerifySignature(HCRYPTPROV_LEGACY hCryptProv, DWORD dwCertEncodingType,
+    CERT_PUBLIC_KEY_INFO *pubKeyInfo, const CERT_SIGNED_CONTENT_INFO *signedCert, const CRYPT_OID_INFO *info)
+{
+    BCRYPT_KEY_HANDLE key = NULL;
+    BYTE *hash_value = NULL, *sig_value;
+    DWORD hash_len, sig_len;
+    NTSTATUS status;
+    BOOL ret;
+
+    ret = CNG_ImportPubKey(pubKeyInfo, &key);
+    if (ret)
+    {
+        ret = CNG_CalcHash(info->pwszCNGAlgid, signedCert, &hash_value, &hash_len);
+        if (ret)
+        {
+            ret = CNG_PrepareSignature(pubKeyInfo, signedCert, &sig_value, &sig_len);
+            if (ret)
+            {
+                status = BCryptVerifySignature(key, NULL, hash_value, hash_len, sig_value, sig_len, 0);
+                if (status)
+                {
+                    FIXME("Failed to verify signature: %08x\n", status);
+                    SetLastError(RtlNtStatusToDosError(status));
+                    ret = FALSE;
+                }
+                CryptMemFree(sig_value);
+            }
+            CryptMemFree(hash_value);
+        }
+        BCryptDestroyKey(key);
+    }
+
+    return ret;
+}
+
+static BOOL CRYPT_VerifyCertSignatureFromPublicKeyInfo(HCRYPTPROV_LEGACY hCryptProv, DWORD dwCertEncodingType,
+    CERT_PUBLIC_KEY_INFO *pubKeyInfo, const CERT_SIGNED_CONTENT_INFO *signedCert)
+{
+    CCRYPT_OID_INFO *info;
+
+    info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, signedCert->SignatureAlgorithm.pszObjId, 0);
+    if (!info || info->dwGroupId != CRYPT_SIGN_ALG_OID_GROUP_ID)
+    {
+        SetLastError(NTE_BAD_ALGID);
+        return FALSE;
+    }
+
+    if (info->u.Algid == CALG_OID_INFO_CNG_ONLY)
+        return CNG_VerifySignature(hCryptProv, dwCertEncodingType, pubKeyInfo, signedCert, info);
+    else
+        return CRYPT_VerifySignature(hCryptProv, dwCertEncodingType, pubKeyInfo, signedCert, info);
+}
+
 BOOL WINAPI CryptVerifyCertificateSignatureEx(HCRYPTPROV_LEGACY hCryptProv,
  DWORD dwCertEncodingType, DWORD dwSubjectType, void *pvSubject,
  DWORD dwIssuerType, void *pvIssuer, DWORD dwFlags, void *pvReserved)
@@ -2727,7 +3148,7 @@ static void CRYPT_SetBitInField(struct BitField *field, DWORD bit)
 
 static BOOL CRYPT_IsBitInFieldSet(const struct BitField *field, DWORD bit)
 {
-    BOOL set = FALSE;
+    BOOL set;
     DWORD indexIndex = bit / BITS_PER_DWORD;
 
     assert(field->cIndexes);
@@ -2939,7 +3360,6 @@ static void CertContext_SetKeyProvInfo(PCCERT_CONTEXT context,
                 CryptMemFree(szProvider);
             }
         }
-        size = sizeof(info.dwKeySpec);
         /* in case no CRYPT_KEY_PROV_INFO given,
          *  we always use AT_SIGNATURE key spec
          */
@@ -3287,3 +3707,11 @@ const void * WINAPI CertCreateContext(DWORD dwContextType, DWORD dwEncodingType,
         return NULL;
     }
 }
+
+BOOL WINAPI CryptSetKeyIdentifierProperty(const CRYPT_HASH_BLOB *pKeyIdentifier, DWORD dwPropId,
+    DWORD dwFlags, LPCWSTR pwszComputerName, void *pvReserved, const void *pvData)
+{
+    FIXME("(%p, 0x%x, 0x%x, %s, %p, %p): stub\n", pKeyIdentifier, dwPropId, dwFlags,
+        debugstr_w(pwszComputerName), pvReserved, pvData);
+    return FALSE;
+}