Implement CertAddSerializedElementToStore.

diff --git a/dlls/crypt32/cert.c b/dlls/crypt32/cert.c
index a166377..8660b7c 100644
--- a/dlls/crypt32/cert.c
+++ b/dlls/crypt32/cert.c
@@ -34,6 +34,8 @@
 #include "wincrypt.h"
 #include "wine/debug.h"
 #include "wine/list.h"
+#include "excpt.h"
+#include "wine/exception.h"
 #include "crypt32_private.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
@@ -44,6 +46,77 @@
 #define CERT_CRL_PROP_ID  33
 #define CERT_CTL_PROP_ID  34
 
+/* Some typedefs that make it easier to abstract which type of context we're
+ * working with.
+ */
+typedef const void *(WINAPI *CreateContextFunc)(DWORD dwCertEncodingType,
+ const BYTE *pbCertEncoded, DWORD cbCertEncoded);
+typedef BOOL (WINAPI *AddContextToStoreFunc)(HCERTSTORE hCertStore,
+ const void *context, DWORD dwAddDisposition, const void **ppStoreContext);
+typedef BOOL (WINAPI *AddEncodedContextToStoreFunc)(HCERTSTORE hCertStore,
+ DWORD dwCertEncodingType, const BYTE *pbEncoded, DWORD cbEncoded,
+ DWORD dwAddDisposition, const void **ppContext);
+typedef const void *(WINAPI *EnumContextsInStoreFunc)(HCERTSTORE hCertStore,
+ const void *pPrevContext);
+typedef BOOL (WINAPI *GetContextPropertyFunc)(const void *context,
+ DWORD dwPropID, void *pvData, DWORD *pcbData);
+typedef BOOL (WINAPI *SetContextPropertyFunc)(const void *context,
+ DWORD dwPropID, DWORD dwFlags, const void *pvData);
+typedef BOOL (WINAPI *SerializeElementFunc)(const void *context, DWORD dwFlags,
+ BYTE *pbElement, DWORD *pcbElement);
+typedef BOOL (WINAPI *FreeContextFunc)(const void *context);
+typedef BOOL (WINAPI *DeleteContextFunc)(const void *context);
+
+/* An abstract context (certificate, CRL, or CTL) interface */
+typedef struct _WINE_CONTEXT_INTERFACE
+{
+    CreateContextFunc            create;
+    AddContextToStoreFunc        addContextToStore;
+    AddEncodedContextToStoreFunc addEncodedToStore;
+    EnumContextsInStoreFunc      enumContextsInStore;
+    GetContextPropertyFunc       getProp;
+    SetContextPropertyFunc       setProp;
+    SerializeElementFunc         serialize;
+    FreeContextFunc              free;
+    DeleteContextFunc            deleteFromStore;
+} WINE_CONTEXT_INTERFACE, *PWINE_CONTEXT_INTERFACE;
+
+static const WINE_CONTEXT_INTERFACE gCertInterface = {
+    (CreateContextFunc)CertCreateCertificateContext,
+    (AddContextToStoreFunc)CertAddCertificateContextToStore,
+    (AddEncodedContextToStoreFunc)CertAddEncodedCertificateToStore,
+    (EnumContextsInStoreFunc)CertEnumCertificatesInStore,
+    (GetContextPropertyFunc)CertGetCertificateContextProperty,
+    (SetContextPropertyFunc)CertSetCertificateContextProperty,
+    (SerializeElementFunc)CertSerializeCertificateStoreElement,
+    (FreeContextFunc)CertFreeCertificateContext,
+    (DeleteContextFunc)CertDeleteCertificateFromStore,
+};
+
+static const WINE_CONTEXT_INTERFACE gCRLInterface = {
+    (CreateContextFunc)CertCreateCRLContext,
+    (AddContextToStoreFunc)CertAddCRLContextToStore,
+    (AddEncodedContextToStoreFunc)CertAddEncodedCRLToStore,
+    (EnumContextsInStoreFunc)CertEnumCRLsInStore,
+    (GetContextPropertyFunc)CertGetCRLContextProperty,
+    (SetContextPropertyFunc)CertSetCRLContextProperty,
+    (SerializeElementFunc)CertSerializeCRLStoreElement,
+    (FreeContextFunc)CertFreeCRLContext,
+    (DeleteContextFunc)CertDeleteCRLFromStore,
+};
+
+static const WINE_CONTEXT_INTERFACE gCTLInterface = {
+    (CreateContextFunc)CertCreateCTLContext,
+    (AddContextToStoreFunc)CertAddCTLContextToStore,
+    (AddEncodedContextToStoreFunc)CertAddEncodedCTLToStore,
+    (EnumContextsInStoreFunc)CertEnumCTLsInStore,
+    (GetContextPropertyFunc)CertGetCTLContextProperty,
+    (SetContextPropertyFunc)CertSetCTLContextProperty,
+    (SerializeElementFunc)CertSerializeCTLStoreElement,
+    (FreeContextFunc)CertFreeCTLContext,
+    (DeleteContextFunc)CertDeleteCTLFromStore,
+};
+
 struct WINE_CRYPTCERTSTORE;
 
 typedef struct WINE_CRYPTCERTSTORE * (*StoreOpenFunc)(HCRYPTPROV hCryptProv,
@@ -205,6 +278,23 @@
 static BOOL WINAPI CRYPT_SetCertificateContextProperty(
  PWINE_CERT_CONTEXT context, DWORD dwPropId, DWORD dwFlags, const void *pvData);
 
+/* Helper function for store reading functions and
+ * CertAddSerializedElementToStore.  Returns a context of the appropriate type
+ * if it can, or NULL otherwise.  Doesn't validate any of the properties in
+ * the serialized context (for example, bad hashes are retained.)
+ * *pdwContentType is set to the type of the returned context.
+ */
+static const void * WINAPI CRYPT_ReadSerializedElement(const BYTE *pbElement,
+ DWORD cbElement, DWORD dwContextTypeFlags, DWORD *pdwContentType);
+
+/* filter for page-fault exceptions */
+static WINE_EXCEPTION_FILTER(page_fault)
+{
+    if (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION)
+        return EXCEPTION_EXECUTE_HANDLER;
+    return EXCEPTION_CONTINUE_SEARCH;
+}
+
 static void CRYPT_InitStore(WINECRYPT_CERTSTORE *store, HCRYPTPROV hCryptProv,
  DWORD dwFlags, CertStoreType type)
 {
@@ -1655,14 +1745,292 @@
     return ret;
 }
 
+/* Looks for the property with ID propID in the buffer buf.  Returns a pointer
+ * to its header if a valid header is found, NULL if not.  Valid means the
+ * length of thte property won't overrun buf, and the unknown field is 1.
+ */
+static const WINE_CERT_PROP_HEADER *CRYPT_findPropID(const BYTE *buf,
+ DWORD size, DWORD propID)
+{
+    const WINE_CERT_PROP_HEADER *ret = NULL;
+    BOOL done = FALSE;
+
+    while (size && !ret && !done)
+    {
+        if (size < sizeof(WINE_CERT_PROP_HEADER))
+        {
+            SetLastError(CRYPT_E_FILE_ERROR);
+            done = TRUE;
+        }
+        else
+        {
+            const WINE_CERT_PROP_HEADER *hdr =
+             (const WINE_CERT_PROP_HEADER *)buf;
+
+            size -= sizeof(WINE_CERT_PROP_HEADER);
+            buf += sizeof(WINE_CERT_PROP_HEADER);
+            if (size < hdr->cb)
+            {
+                SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
+                done = TRUE;
+            }
+            else if (!hdr->propID)
+            {
+                /* assume a zero prop ID means the data are uninitialized, so
+                 * stop looking.
+                 */
+                done = TRUE;
+            }
+            else if (hdr->unknown != 1)
+            {
+                SetLastError(ERROR_FILE_NOT_FOUND);
+                done = TRUE;
+            }
+            else if (hdr->propID == propID)
+                ret = hdr;
+            else
+            {
+                buf += hdr->cb;
+                size -= hdr->cb;
+            }
+        }
+    }
+    return ret;
+}
+
+static const void * WINAPI CRYPT_ReadSerializedElement(const BYTE *pbElement,
+ DWORD cbElement, DWORD dwContextTypeFlags, DWORD *pdwContentType)
+{
+    const void *context = NULL;
+
+    TRACE("(%p, %ld, %08lx, %p)\n", pbElement, cbElement, dwContextTypeFlags,
+     pdwContentType);
+
+    if (!cbElement)
+    {
+        SetLastError(ERROR_END_OF_MEDIA);
+        return NULL;
+    }
+
+    __TRY
+    {
+        const WINE_CONTEXT_INTERFACE *contextInterface = NULL;
+        const WINE_CERT_PROP_HEADER *hdr = NULL;
+        DWORD type = 0;
+        BOOL ret;
+
+        ret = TRUE;
+        if (dwContextTypeFlags == CERT_STORE_ALL_CONTEXT_FLAG)
+        {
+            hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CERT_PROP_ID);
+            if (hdr)
+                type = CERT_STORE_CERTIFICATE_CONTEXT;
+            else
+            {
+                hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CRL_PROP_ID);
+                if (hdr)
+                    type = CERT_STORE_CRL_CONTEXT;
+                else
+                {
+                    hdr = CRYPT_findPropID(pbElement, cbElement,
+                     CERT_CTL_PROP_ID);
+                    if (hdr)
+                        type = CERT_STORE_CTL_CONTEXT;
+                }
+            }
+        }
+        else if (dwContextTypeFlags & CERT_STORE_CERTIFICATE_CONTEXT_FLAG)
+        {
+            hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CERT_PROP_ID);
+            type = CERT_STORE_CERTIFICATE_CONTEXT;
+        }
+        else if (dwContextTypeFlags & CERT_STORE_CRL_CONTEXT_FLAG)
+        {
+            hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CRL_PROP_ID);
+            type = CERT_STORE_CRL_CONTEXT;
+        }
+        else if (dwContextTypeFlags & CERT_STORE_CTL_CONTEXT_FLAG)
+        {
+            hdr = CRYPT_findPropID(pbElement, cbElement, CERT_CTL_PROP_ID);
+            type = CERT_STORE_CTL_CONTEXT;
+        }
+
+        switch (type)
+        {
+        case CERT_STORE_CERTIFICATE_CONTEXT:
+            contextInterface = &gCertInterface;
+            break;
+        case CERT_STORE_CRL_CONTEXT:
+            contextInterface = &gCRLInterface;
+            break;
+        case CERT_STORE_CTL_CONTEXT:
+            contextInterface = &gCTLInterface;
+            break;
+        default:
+            SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
+            ret = FALSE;
+        }
+        if (!hdr)
+            ret = FALSE;
+
+        if (ret)
+            context = contextInterface->create(X509_ASN_ENCODING,
+             (BYTE *)hdr + sizeof(WINE_CERT_PROP_HEADER), hdr->cb);
+        if (ret && context)
+        {
+            BOOL noMoreProps = FALSE;
+
+            while (!noMoreProps && ret)
+            {
+                if (cbElement < sizeof(WINE_CERT_PROP_HEADER))
+                    ret = FALSE;
+                else
+                {
+                    const WINE_CERT_PROP_HEADER *hdr =
+                     (const WINE_CERT_PROP_HEADER *)pbElement;
+
+                    TRACE("prop is %ld\n", hdr->propID);
+                    cbElement -= sizeof(WINE_CERT_PROP_HEADER);
+                    pbElement += sizeof(WINE_CERT_PROP_HEADER);
+                    if (cbElement < hdr->cb)
+                    {
+                        SetLastError(HRESULT_FROM_WIN32(
+                         ERROR_INVALID_PARAMETER));
+                        ret = FALSE;
+                    }
+                    else if (!hdr->propID)
+                    {
+                        /* Like in CRYPT_findPropID, stop if the propID is zero
+                         */
+                        noMoreProps = TRUE;
+                    }
+                    else if (hdr->unknown != 1)
+                    {
+                        SetLastError(ERROR_FILE_NOT_FOUND);
+                        ret = FALSE;
+                    }
+                    else if (hdr->propID != CERT_CERT_PROP_ID &&
+                     hdr->propID != CERT_CRL_PROP_ID && hdr->propID !=
+                     CERT_CTL_PROP_ID)
+                    {
+                        /* Have to create a blob for most types, but not
+                         * for all.. arghh.
+                         */
+                        switch (hdr->propID)
+                        {
+                        case CERT_AUTO_ENROLL_PROP_ID:
+                        case CERT_CTL_USAGE_PROP_ID:
+                        case CERT_DESCRIPTION_PROP_ID:
+                        case CERT_FRIENDLY_NAME_PROP_ID:
+                        case CERT_HASH_PROP_ID:
+                        case CERT_KEY_IDENTIFIER_PROP_ID:
+                        case CERT_MD5_HASH_PROP_ID:
+                        case CERT_NEXT_UPDATE_LOCATION_PROP_ID:
+                        case CERT_PUBKEY_ALG_PARA_PROP_ID:
+                        case CERT_PVK_FILE_PROP_ID:
+                        case CERT_SIGNATURE_HASH_PROP_ID:
+                        case CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID:
+                        case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID:
+                        case CERT_ENROLLMENT_PROP_ID:
+                        case CERT_CROSS_CERT_DIST_POINTS_PROP_ID:
+                        case CERT_RENEWAL_PROP_ID:
+                        {
+                            CRYPT_DATA_BLOB blob = { hdr->cb,
+                             (LPBYTE)pbElement };
+
+                            ret = contextInterface->setProp(context,
+                             hdr->propID, 0, &blob);
+                            break;
+                        }
+                        case CERT_DATE_STAMP_PROP_ID:
+                            ret = contextInterface->setProp(context,
+                             hdr->propID, 0, pbElement);
+                            break;
+                        default:
+                            FIXME("prop ID %ld: stub\n", hdr->propID);
+                        }
+                    }
+                    pbElement += hdr->cb;
+                    cbElement -= hdr->cb;
+                    if (!cbElement)
+                        noMoreProps = TRUE;
+                }
+            }
+            if (ret)
+            {
+                if (pdwContentType)
+                    *pdwContentType = type;
+            }
+            else
+            {
+                contextInterface->free(context);
+                context = NULL;
+            }
+        }
+    }
+    __EXCEPT(page_fault)
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+        context = NULL;
+    }
+    __ENDTRY
+    return context;
+}
+
 BOOL WINAPI CertAddSerializedElementToStore(HCERTSTORE hCertStore,
  const BYTE *pbElement, DWORD cbElement, DWORD dwAddDisposition, DWORD dwFlags,
  DWORD dwContextTypeFlags, DWORD *pdwContentType, const void **ppvContext)
 {
-    FIXME("(%p, %p, %ld, %08lx, %08lx, %08lx, %p, %p): stub\n", hCertStore,
+    const void *context;
+    DWORD type;
+    BOOL ret;
+
+    TRACE("(%p, %p, %ld, %08lx, %08lx, %08lx, %p, %p)\n", hCertStore,
      pbElement, cbElement, dwAddDisposition, dwFlags, dwContextTypeFlags,
      pdwContentType, ppvContext);
-    return FALSE;
+
+    /* Call the internal function, then delete the hashes.  Tests show this
+     * function uses real hash values, not whatever's stored in the hash
+     * property.
+     */
+    context = CRYPT_ReadSerializedElement(pbElement, cbElement,
+     dwContextTypeFlags, &type);
+    if (context)
+    {
+        const WINE_CONTEXT_INTERFACE *contextInterface = NULL;
+
+        switch (type)
+        {
+        case CERT_STORE_CERTIFICATE_CONTEXT:
+            contextInterface = &gCertInterface;
+            break;
+        case CERT_STORE_CRL_CONTEXT:
+            contextInterface = &gCRLInterface;
+            break;
+        case CERT_STORE_CTL_CONTEXT:
+            contextInterface = &gCTLInterface;
+            break;
+        default:
+            SetLastError(HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER));
+        }
+        if (contextInterface)
+        {
+            contextInterface->setProp(context, CERT_HASH_PROP_ID, 0, NULL);
+            contextInterface->setProp(context, CERT_MD5_HASH_PROP_ID, 0, NULL);
+            contextInterface->setProp(context, CERT_SIGNATURE_HASH_PROP_ID, 0,
+             NULL);
+            if (pdwContentType)
+                *pdwContentType = type;
+            ret = contextInterface->addContextToStore(hCertStore, context,
+             dwAddDisposition, ppvContext);
+            contextInterface->free(context);
+        }
+        else
+            ret = FALSE;
+    }
+    else
+        ret = FALSE;
+    return ret;
 }
 
 BOOL WINAPI CertFreeCertificateContext(PCCERT_CONTEXT pCertContext)
diff --git a/dlls/crypt32/tests/cert.c b/dlls/crypt32/tests/cert.c
index d2133cb..9d96939 100644
--- a/dlls/crypt32/tests/cert.c
+++ b/dlls/crypt32/tests/cert.c
@@ -28,6 +28,18 @@
 
 #include "wine/test.h"
 
+/* The following aren't defined in wincrypt.h, as they're "reserved" */
+#define CERT_CERT_PROP_ID 32
+#define CERT_CRL_PROP_ID  33
+#define CERT_CTL_PROP_ID  34
+
+struct CertPropIDHeader
+{
+    DWORD propID;
+    DWORD unknown1;
+    DWORD cb;
+};
+
 static void testCryptHashCert(void)
 {
     static const BYTE emptyHash[] = { 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b,
@@ -667,6 +679,169 @@
     }
 }
 
+static void testAddSerialized(void)
+{
+    BOOL ret;
+    HCERTSTORE store;
+    BYTE buf[sizeof(struct CertPropIDHeader) * 2 + 20 + sizeof(bigCert) - 1] =
+     { 0 };
+    BYTE hash[20];
+    struct CertPropIDHeader *hdr;
+    PCCERT_CONTEXT context;
+
+    ret = CertAddSerializedElementToStore(0, NULL, 0, 0, 0, 0, NULL, NULL);
+    ok(!ret && GetLastError() == ERROR_END_OF_MEDIA,
+     "Expected ERROR_END_OF_MEDIA, got %08lx\n", GetLastError());
+
+    store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
+     CERT_STORE_CREATE_NEW_FLAG, NULL);
+    ok(store != 0, "CertOpenStore failed: %08lx\n", GetLastError());
+
+    ret = CertAddSerializedElementToStore(store, NULL, 0, 0, 0, 0, NULL, NULL);
+    ok(!ret && GetLastError() == ERROR_END_OF_MEDIA,
+     "Expected ERROR_END_OF_MEDIA, got %08lx\n", GetLastError());
+
+    /* Test with an empty property */
+    hdr = (struct CertPropIDHeader *)buf;
+    hdr->propID = CERT_CERT_PROP_ID;
+    hdr->unknown1 = 1;
+    hdr->cb = 0;
+    ret = CertAddSerializedElementToStore(store, buf, sizeof(buf), 0, 0, 0,
+     NULL, NULL);
+    ok(!ret && GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
+     "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %08lx\n",
+     GetLastError());
+    /* Test with a bad size in property header */
+    hdr->cb = sizeof(bigCert) - 2;
+    memcpy(buf + sizeof(struct CertPropIDHeader), bigCert, sizeof(bigCert) - 1);
+    ret = CertAddSerializedElementToStore(store, buf, sizeof(buf), 0, 0, 0,
+     NULL, NULL);
+    ok(!ret && GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
+     "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %08lx\n",
+     GetLastError());
+    ret = CertAddSerializedElementToStore(store, buf,
+     sizeof(struct CertPropIDHeader) + sizeof(bigCert) - 1, 0, 0, 0, NULL,
+     NULL);
+    ok(!ret && GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
+     "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %08lx\n",
+     GetLastError());
+    ret = CertAddSerializedElementToStore(store, buf,
+     sizeof(struct CertPropIDHeader) + sizeof(bigCert) - 1, CERT_STORE_ADD_NEW,
+     0, 0, NULL, NULL);
+    ok(!ret && GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
+     "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %08lx\n",
+     GetLastError());
+    /* Kosher size in property header, but no context type */
+    hdr->cb = sizeof(bigCert) - 1;
+    ret = CertAddSerializedElementToStore(store, buf, sizeof(buf), 0, 0, 0,
+     NULL, NULL);
+    ok(!ret && GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
+     "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %08lx\n",
+     GetLastError());
+    ret = CertAddSerializedElementToStore(store, buf,
+     sizeof(struct CertPropIDHeader) + sizeof(bigCert) - 1, 0, 0, 0, NULL,
+     NULL);
+    ok(!ret && GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
+     "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %08lx\n",
+     GetLastError());
+    ret = CertAddSerializedElementToStore(store, buf,
+     sizeof(struct CertPropIDHeader) + sizeof(bigCert) - 1, CERT_STORE_ADD_NEW,
+     0, 0, NULL, NULL);
+    ok(!ret && GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
+     "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %08lx\n",
+     GetLastError());
+    /* With a bad context type */
+    ret = CertAddSerializedElementToStore(store, buf, sizeof(buf), 0, 0, 
+     CERT_STORE_CRL_CONTEXT_FLAG, NULL, NULL);
+    ok(!ret && GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
+     "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %08lx\n",
+     GetLastError());
+    ret = CertAddSerializedElementToStore(store, buf,
+     sizeof(struct CertPropIDHeader) + sizeof(bigCert) - 1, 0, 0, 
+     CERT_STORE_CRL_CONTEXT_FLAG, NULL, NULL);
+    ok(!ret && GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
+     "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %08lx\n",
+     GetLastError());
+    ret = CertAddSerializedElementToStore(store, buf,
+     sizeof(struct CertPropIDHeader) + sizeof(bigCert) - 1, CERT_STORE_ADD_NEW,
+     0, CERT_STORE_CRL_CONTEXT_FLAG, NULL, NULL);
+    ok(!ret && GetLastError() == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER),
+     "Expected HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER), got %08lx\n",
+     GetLastError());
+    /* Bad unknown field, good type */
+    hdr->unknown1 = 2;
+    ret = CertAddSerializedElementToStore(store, buf, sizeof(buf), 0, 0, 
+     CERT_STORE_CERTIFICATE_CONTEXT_FLAG, NULL, NULL);
+    ok(!ret && GetLastError() == ERROR_FILE_NOT_FOUND,
+     "Expected ERROR_FILE_NOT_FOUND got %08lx\n", GetLastError());
+    ret = CertAddSerializedElementToStore(store, buf,
+     sizeof(struct CertPropIDHeader) + sizeof(bigCert) - 1, 0, 0, 
+     CERT_STORE_CERTIFICATE_CONTEXT_FLAG, NULL, NULL);
+    ok(!ret && GetLastError() == ERROR_FILE_NOT_FOUND,
+     "Expected ERROR_FILE_NOT_FOUND got %08lx\n", GetLastError());
+    ret = CertAddSerializedElementToStore(store, buf,
+     sizeof(struct CertPropIDHeader) + sizeof(bigCert) - 1, CERT_STORE_ADD_NEW,
+     0, CERT_STORE_CERTIFICATE_CONTEXT_FLAG, NULL, NULL);
+    ok(!ret && GetLastError() == ERROR_FILE_NOT_FOUND,
+     "Expected ERROR_FILE_NOT_FOUND got %08lx\n", GetLastError());
+    /* Most everything okay, but bad add disposition */
+    hdr->unknown1 = 1;
+    /* This crashes
+    ret = CertAddSerializedElementToStore(store, buf, sizeof(buf), 0, 0, 
+     CERT_STORE_CERTIFICATE_CONTEXT_FLAG, NULL, NULL);
+     * as does this
+    ret = CertAddSerializedElementToStore(store, buf,
+     sizeof(struct CertPropIDHeader) + sizeof(bigCert) - 1, 0, 0, 
+     CERT_STORE_CERTIFICATE_CONTEXT_FLAG, NULL, NULL);
+     */
+    /* Everything okay, but buffer's too big */
+    ret = CertAddSerializedElementToStore(store, buf, sizeof(buf),
+     CERT_STORE_ADD_NEW, 0, CERT_STORE_CERTIFICATE_CONTEXT_FLAG, NULL, NULL);
+    ok(ret, "CertAddSerializedElementToStore failed: %08lx\n", GetLastError());
+    /* Everything okay, check it's not re-added */
+    ret = CertAddSerializedElementToStore(store, buf,
+     sizeof(struct CertPropIDHeader) + sizeof(bigCert) - 1, CERT_STORE_ADD_NEW,
+     0, CERT_STORE_CERTIFICATE_CONTEXT_FLAG, NULL, NULL);
+    ok(!ret && GetLastError() == CRYPT_E_EXISTS,
+     "Expected CRYPT_E_EXISTS, got %08lx\n", GetLastError());
+
+    context = CertEnumCertificatesInStore(store, NULL);
+    ok(context != NULL, "Expected a cert\n");
+    if (context)
+        CertDeleteCertificateFromStore(context);
+
+    /* Try adding with a bogus hash.  Oddly enough, it succeeds, and the hash,
+     * when queried, is the real hash rather than the bogus hash.
+     */
+    hdr = (struct CertPropIDHeader *)(buf + sizeof(struct CertPropIDHeader) +
+     sizeof(bigCert) - 1);
+    hdr->propID = CERT_HASH_PROP_ID;
+    hdr->unknown1 = 1;
+    hdr->cb = sizeof(hash);
+    memset(hash, 0xc, sizeof(hash));
+    memcpy((LPBYTE)hdr + sizeof(struct CertPropIDHeader), hash, sizeof(hash));
+    ret = CertAddSerializedElementToStore(store, buf, sizeof(buf),
+     CERT_STORE_ADD_NEW, 0, CERT_STORE_CERTIFICATE_CONTEXT_FLAG, NULL,
+     (const void **)&context);
+    ok(ret, "CertAddSerializedElementToStore failed: %08lx\n", GetLastError());
+    if (context)
+    {
+        BYTE hashVal[20], realHash[20];
+        DWORD size = sizeof(hashVal);
+
+        ret = CryptHashCertificate(0, 0, 0, bigCert, sizeof(bigCert) - 1,
+         realHash, &size);
+        ok(ret, "CryptHashCertificate failed: %08lx\n", GetLastError());
+        ret = CertGetCertificateContextProperty(context, CERT_HASH_PROP_ID,
+         hashVal, &size);
+        ok(ret, "CertGetCertificateContextProperty failed: %08lx\n",
+         GetLastError());
+        ok(!memcmp(hashVal, realHash, size), "Unexpected hash\n");
+    }
+
+    CertCloseStore(store, 0);
+}
+
 START_TEST(cert)
 {
     testCryptHashCert();
@@ -676,4 +851,5 @@
     testCollectionStore();
 
     testCertProperties();
+    testAddSerialized();
 }