wininet/tests: Skip tests if functions are not implemented.
[wine] / dlls / crypt32 / msg.c
index 9eab762..a16eef1 100644 (file)
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
+
+#include "config.h"
+#include "wine/port.h"
+
 #include <stdarg.h>
+#define NONAMELESSUNION
 #include "windef.h"
 #include "winbase.h"
 #include "wincrypt.h"
@@ -41,7 +46,7 @@ typedef BOOL (*CryptMsgUpdateFunc)(HCRYPTMSG hCryptMsg, const BYTE *pbData,
 typedef BOOL (*CryptMsgControlFunc)(HCRYPTMSG hCryptMsg, DWORD dwFlags,
  DWORD dwCtrlType, const void *pvCtrlPara);
 
-BOOL CRYPT_DefaultMsgControl(HCRYPTMSG hCryptMsg, DWORD dwFlags,
+static BOOL CRYPT_DefaultMsgControl(HCRYPTMSG hCryptMsg, DWORD dwFlags,
  DWORD dwCtrlType, const void *pvCtrlPara)
 {
     TRACE("(%p, %08x, %d, %p)\n", hCryptMsg, dwFlags, dwCtrlType, pvCtrlPara);
@@ -52,6 +57,7 @@ BOOL CRYPT_DefaultMsgControl(HCRYPTMSG hCryptMsg, DWORD dwFlags,
 typedef enum _CryptMsgState {
     MsgStateInit,
     MsgStateUpdated,
+    MsgStateDataFinalized,
     MsgStateFinalized
 } CryptMsgState;
 
@@ -78,7 +84,7 @@ static inline void CryptMsgBase_Init(CryptMsgBase *msg, DWORD dwFlags,
     if (pStreamInfo)
     {
         msg->streamed = TRUE;
-        memcpy(&msg->stream_info, pStreamInfo, sizeof(msg->stream_info));
+        msg->stream_info = *pStreamInfo;
     }
     else
     {
@@ -103,17 +109,17 @@ static const BYTE empty_data_content[] = { 0x04,0x00 };
 
 static void CDataEncodeMsg_Close(HCRYPTMSG hCryptMsg)
 {
-    CDataEncodeMsg *msg = (CDataEncodeMsg *)hCryptMsg;
+    CDataEncodeMsg *msg = hCryptMsg;
 
     if (msg->bare_content != empty_data_content)
         LocalFree(msg->bare_content);
 }
 
-static WINAPI BOOL CRYPT_EncodeContentLength(DWORD dwCertEncodingType,
+static BOOL WINAPI CRYPT_EncodeContentLength(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
  PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
 {
-    const CDataEncodeMsg *msg = (const CDataEncodeMsg *)pvStructInfo;
+    DWORD dataLen = *(DWORD *)pvStructInfo;
     DWORD lenBytes;
     BOOL ret = TRUE;
 
@@ -121,9 +127,9 @@ static WINAPI BOOL CRYPT_EncodeContentLength(DWORD dwCertEncodingType,
      * the message isn't available yet.  The caller will use the length
      * reported here to encode its length.
      */
-    CRYPT_EncodeLen(msg->base.stream_info.cbContent, NULL, &lenBytes);
+    CRYPT_EncodeLen(dataLen, NULL, &lenBytes);
     if (!pbEncoded)
-        *pcbEncoded = 1 + lenBytes + msg->base.stream_info.cbContent;
+        *pcbEncoded = 1 + lenBytes + dataLen;
     else
     {
         if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded,
@@ -132,7 +138,7 @@ static WINAPI BOOL CRYPT_EncodeContentLength(DWORD dwCertEncodingType,
             if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
                 pbEncoded = *(BYTE **)pbEncoded;
             *pbEncoded++ = ASN_OCTETSTRING;
-            CRYPT_EncodeLen(msg->base.stream_info.cbContent, pbEncoded,
+            CRYPT_EncodeLen(dataLen, pbEncoded,
              &lenBytes);
         }
     }
@@ -146,15 +152,23 @@ static BOOL CRYPT_EncodeDataContentInfoHeader(CDataEncodeMsg *msg,
 
     if (msg->base.streamed && msg->base.stream_info.cbContent == 0xffffffff)
     {
-        FIXME("unimplemented for indefinite-length encoding\n");
-        header->cbData = 0;
-        header->pbData = NULL;
-        ret = TRUE;
+        static const BYTE headerValue[] = { 0x30,0x80,0x06,0x09,0x2a,0x86,0x48,
+         0x86,0xf7,0x0d,0x01,0x07,0x01,0xa0,0x80,0x24,0x80 };
+
+        header->pbData = LocalAlloc(0, sizeof(headerValue));
+        if (header->pbData)
+        {
+            header->cbData = sizeof(headerValue);
+            memcpy(header->pbData, headerValue, sizeof(headerValue));
+            ret = TRUE;
+        }
+        else
+            ret = FALSE;
     }
     else
     {
-        struct AsnConstructedItem constructed = { 0, msg,
-         CRYPT_EncodeContentLength };
+        struct AsnConstructedItem constructed = { 0,
+         &msg->base.stream_info.cbContent, CRYPT_EncodeContentLength };
         struct AsnEncodeSequenceItem items[2] = {
          { szOID_RSA_data, CRYPT_AsnEncodeOid, 0 },
          { &constructed,   CRYPT_AsnEncodeConstructed, 0 },
@@ -177,10 +191,12 @@ static BOOL CRYPT_EncodeDataContentInfoHeader(CDataEncodeMsg *msg,
 static BOOL CDataEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
  DWORD cbData, BOOL fFinal)
 {
-    CDataEncodeMsg *msg = (CDataEncodeMsg *)hCryptMsg;
+    CDataEncodeMsg *msg = hCryptMsg;
     BOOL ret = FALSE;
 
-    if (msg->base.streamed)
+    if (msg->base.state == MsgStateFinalized)
+        SetLastError(CRYPT_E_MSG_ERROR);
+    else if (msg->base.streamed)
     {
         __TRY
         {
@@ -197,12 +213,35 @@ static BOOL CDataEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
                     LocalFree(header.pbData);
                 }
             }
+            /* Curiously, every indefinite-length streamed update appears to
+             * get its own tag and length, regardless of fFinal.
+             */
+            if (msg->base.stream_info.cbContent == 0xffffffff)
+            {
+                BYTE *header;
+                DWORD headerLen;
+
+                ret = CRYPT_EncodeContentLength(X509_ASN_ENCODING, NULL,
+                 &cbData, CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&header,
+                 &headerLen);
+                if (ret)
+                {
+                    ret = msg->base.stream_info.pfnStreamOutput(
+                     msg->base.stream_info.pvArg, header, headerLen,
+                     FALSE);
+                    LocalFree(header);
+                }
+            }
             if (!fFinal)
+            {
                 ret = msg->base.stream_info.pfnStreamOutput(
                  msg->base.stream_info.pvArg, (BYTE *)pbData, cbData,
                  FALSE);
+                msg->base.state = MsgStateUpdated;
+            }
             else
             {
+                msg->base.state = MsgStateFinalized;
                 if (msg->base.stream_info.cbContent == 0xffffffff)
                 {
                     BYTE indefinite_trailer[6] = { 0 };
@@ -238,6 +277,7 @@ static BOOL CDataEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
         }
         else
         {
+            msg->base.state = MsgStateFinalized;
             if (!cbData)
                 SetLastError(E_INVALIDARG);
             else
@@ -280,7 +320,7 @@ static BOOL CRYPT_CopyParam(void *pvData, DWORD *pcbData, const void *src,
 static BOOL CDataEncodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
  DWORD dwIndex, void *pvData, DWORD *pcbData)
 {
-    CDataEncodeMsg *msg = (CDataEncodeMsg *)hCryptMsg;
+    CDataEncodeMsg *msg = hCryptMsg;
     BOOL ret = FALSE;
 
     switch (dwParamType)
@@ -332,7 +372,7 @@ static HCRYPTMSG CDataEncodeMsg_Open(DWORD dwFlags, const void *pvMsgEncodeInfo,
         msg->bare_content_len = sizeof(empty_data_content);
         msg->bare_content = (LPBYTE)empty_data_content;
     }
-    return (HCRYPTMSG)msg;
+    return msg;
 }
 
 typedef struct _CHashEncodeMsg
@@ -345,7 +385,7 @@ typedef struct _CHashEncodeMsg
 
 static void CHashEncodeMsg_Close(HCRYPTMSG hCryptMsg)
 {
-    CHashEncodeMsg *msg = (CHashEncodeMsg *)hCryptMsg;
+    CHashEncodeMsg *msg = hCryptMsg;
 
     CryptMemFree(msg->data.pbData);
     CryptDestroyHash(msg->hash);
@@ -404,7 +444,7 @@ static BOOL CRYPT_EncodePKCSDigestedData(CHashEncodeMsg *msg, void *pvData,
 static BOOL CHashEncodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
  DWORD dwIndex, void *pvData, DWORD *pcbData)
 {
-    CHashEncodeMsg *msg = (CHashEncodeMsg *)hCryptMsg;
+    CHashEncodeMsg *msg = hCryptMsg;
     BOOL ret = FALSE;
 
     TRACE("(%p, %d, %d, %p, %p)\n", hCryptMsg, dwParamType, dwIndex,
@@ -447,8 +487,7 @@ static BOOL CHashEncodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
         break;
     }
     case CMSG_COMPUTED_HASH_PARAM:
-        ret = CryptGetHashParam(msg->hash, HP_HASHVAL, (BYTE *)pvData, pcbData,
-         0);
+        ret = CryptGetHashParam(msg->hash, HP_HASHVAL, pvData, pcbData, 0);
         break;
     case CMSG_VERSION_PARAM:
         if (msg->base.state != MsgStateFinalized)
@@ -472,17 +511,20 @@ static BOOL CHashEncodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
 static BOOL CHashEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
  DWORD cbData, BOOL fFinal)
 {
-    CHashEncodeMsg *msg = (CHashEncodeMsg *)hCryptMsg;
+    CHashEncodeMsg *msg = hCryptMsg;
     BOOL ret = FALSE;
 
     TRACE("(%p, %p, %d, %d)\n", hCryptMsg, pbData, cbData, fFinal);
 
-    if (msg->base.streamed || (msg->base.open_flags & CMSG_DETACHED_FLAG))
+    if (msg->base.state == MsgStateFinalized)
+        SetLastError(CRYPT_E_MSG_ERROR);
+    else if (msg->base.streamed || (msg->base.open_flags & CMSG_DETACHED_FLAG))
     {
         /* Doesn't do much, as stream output is never called, and you
          * can't get the content.
          */
         ret = CryptHashData(msg->hash, pbData, cbData, 0);
+        msg->base.state = fFinal ? MsgStateFinalized : MsgStateUpdated;
     }
     else
     {
@@ -502,6 +544,7 @@ static BOOL CHashEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
                 else
                     ret = FALSE;
             }
+            msg->base.state = MsgStateFinalized;
         }
     }
     return ret;
@@ -511,8 +554,7 @@ static HCRYPTMSG CHashEncodeMsg_Open(DWORD dwFlags, const void *pvMsgEncodeInfo,
  LPSTR pszInnerContentObjID, PCMSG_STREAM_INFO pStreamInfo)
 {
     CHashEncodeMsg *msg;
-    const CMSG_HASHED_ENCODE_INFO *info =
-     (const CMSG_HASHED_ENCODE_INFO *)pvMsgEncodeInfo;
+    const CMSG_HASHED_ENCODE_INFO *info = pvMsgEncodeInfo;
     HCRYPTPROV prov;
     ALG_ID algID;
 
@@ -548,7 +590,7 @@ static HCRYPTMSG CHashEncodeMsg_Open(DWORD dwFlags, const void *pvMsgEncodeInfo,
             msg = NULL;
         }
     }
-    return (HCRYPTMSG)msg;
+    return msg;
 }
 
 typedef struct _CMSG_SIGNER_ENCODE_INFO_WITH_CMS
@@ -589,20 +631,62 @@ static BOOL CRYPT_IsValidSigner(CMSG_SIGNER_ENCODE_INFO_WITH_CMS *signer)
         SetLastError(E_INVALIDARG);
         return FALSE;
     }
-    if (signer->cbSize == sizeof(CMSG_SIGNER_ENCODE_INFO_WITH_CMS))
+    if (signer->cbSize == sizeof(CMSG_SIGNER_ENCODE_INFO))
     {
-        FIXME("CMSG_SIGNER_ENCODE_INFO with CMS fields unsupported\n");
-        return FALSE;
-    }
-    if (!signer->pCertInfo->SerialNumber.cbData)
-    {
-        SetLastError(E_INVALIDARG);
-        return FALSE;
+        if (!signer->pCertInfo->SerialNumber.cbData)
+        {
+            SetLastError(E_INVALIDARG);
+            return FALSE;
+        }
+        if (!signer->pCertInfo->Issuer.cbData)
+        {
+            SetLastError(E_INVALIDARG);
+            return FALSE;
+        }
     }
-    if (!signer->pCertInfo->Issuer.cbData)
+    else if (signer->cbSize == sizeof(CMSG_SIGNER_ENCODE_INFO_WITH_CMS))
     {
-        SetLastError(E_INVALIDARG);
-        return FALSE;
+        switch (signer->SignerId.dwIdChoice)
+        {
+        case 0:
+            if (!signer->pCertInfo->SerialNumber.cbData)
+            {
+                SetLastError(E_INVALIDARG);
+                return FALSE;
+            }
+            if (!signer->pCertInfo->Issuer.cbData)
+            {
+                SetLastError(E_INVALIDARG);
+                return FALSE;
+            }
+            break;
+        case CERT_ID_ISSUER_SERIAL_NUMBER:
+            if (!signer->SignerId.u.IssuerSerialNumber.SerialNumber.cbData)
+            {
+                SetLastError(E_INVALIDARG);
+                return FALSE;
+            }
+            if (!signer->SignerId.u.IssuerSerialNumber.Issuer.cbData)
+            {
+                SetLastError(E_INVALIDARG);
+                return FALSE;
+            }
+            break;
+        case CERT_ID_KEY_IDENTIFIER:
+            if (!signer->SignerId.u.KeyId.cbData)
+            {
+                SetLastError(E_INVALIDARG);
+                return FALSE;
+            }
+            break;
+        default:
+            SetLastError(E_INVALIDARG);
+        }
+        if (signer->HashEncryptionAlgorithm.pszObjId)
+        {
+            FIXME("CMSG_SIGNER_ENCODE_INFO with CMS fields unsupported\n");
+            return FALSE;
+        }
     }
     if (!signer->hCryptProv)
     {
@@ -617,14 +701,6 @@ static BOOL CRYPT_IsValidSigner(CMSG_SIGNER_ENCODE_INFO_WITH_CMS *signer)
     return TRUE;
 }
 
-typedef struct _CSignerHandles
-{
-    HCRYPTPROV       prov;
-    HCRYPTHASH       contentHash;
-    HCRYPTHASH       authAttrHash;
-    HCRYPTKEY        key;
-} CSignerHandles;
-
 static BOOL CRYPT_ConstructBlob(CRYPT_DATA_BLOB *out, const CRYPT_DATA_BLOB *in)
 {
     BOOL ret = TRUE;
@@ -722,56 +798,88 @@ static BOOL CRYPT_ConstructAttributes(CRYPT_ATTRIBUTES *out,
     return ret;
 }
 
-/* Constructs both a CSignerHandles and a CMSG_SIGNER_INFO from a
- * CMSG_SIGNER_ENCODE_INFO_WITH_CMS.
- */
-static BOOL CSignerInfo_Construct(CSignerHandles *handles,
- CMSG_SIGNER_INFO *info, CMSG_SIGNER_ENCODE_INFO_WITH_CMS *in, DWORD open_flags)
+/* Constructs a CMSG_CMS_SIGNER_INFO from a CMSG_SIGNER_ENCODE_INFO_WITH_CMS. */
+static BOOL CSignerInfo_Construct(CMSG_CMS_SIGNER_INFO *info,
+ const CMSG_SIGNER_ENCODE_INFO_WITH_CMS *in)
 {
-    ALG_ID algID;
     BOOL ret;
 
-    handles->prov = in->hCryptProv;
-    if (!(open_flags & CMSG_CRYPT_RELEASE_CONTEXT_FLAG))
-        CryptContextAddRef(handles->prov, NULL, 0);
-    algID = CertOIDToAlgId(in->HashAlgorithm.pszObjId);
-    ret = CryptCreateHash(handles->prov, algID, 0, 0, &handles->contentHash);
-    if (ret && in->cAuthAttr)
-        ret = CryptCreateHash(handles->prov, algID, 0, 0,
-         &handles->authAttrHash);
-    if (ret)
+    if (in->cbSize == sizeof(CMSG_SIGNER_ENCODE_INFO))
     {
-        /* Note: needs to change if CMS fields are supported */
         info->dwVersion = CMSG_SIGNER_INFO_V1;
-        ret = CRYPT_ConstructBlob(&info->Issuer, &in->pCertInfo->Issuer);
+        ret = CRYPT_ConstructBlob(&info->SignerId.u.IssuerSerialNumber.Issuer,
+         &in->pCertInfo->Issuer);
         if (ret)
-            ret = CRYPT_ConstructBlob(&info->SerialNumber,
+            ret = CRYPT_ConstructBlob(
+             &info->SignerId.u.IssuerSerialNumber.SerialNumber,
              &in->pCertInfo->SerialNumber);
-        /* Assumption:  algorithm IDs will point to static strings, not
-         * stack-based ones, so copying the pointer values is safe.
+        info->SignerId.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
+    }
+    else
+    {
+        /* Implicitly in->cbSize == sizeof(CMSG_SIGNER_ENCODE_INFO_WITH_CMS).
+         * See CRYPT_IsValidSigner.
          */
-        info->HashAlgorithm.pszObjId = in->HashAlgorithm.pszObjId;
-        if (ret)
-            ret = CRYPT_ConstructBlob(&info->HashAlgorithm.Parameters,
-             &in->HashAlgorithm.Parameters);
-        memset(&info->HashEncryptionAlgorithm, 0,
-         sizeof(info->HashEncryptionAlgorithm));
-        if (ret)
-            ret = CRYPT_ConstructAttributes(&info->AuthAttrs,
-             (CRYPT_ATTRIBUTES *)&in->cAuthAttr);
-        if (ret)
-            ret = CRYPT_ConstructAttributes(&info->UnauthAttrs,
-             (CRYPT_ATTRIBUTES *)&in->cUnauthAttr);
+        if (!in->SignerId.dwIdChoice)
+        {
+            info->dwVersion = CMSG_SIGNER_INFO_V1;
+            ret = CRYPT_ConstructBlob(&info->SignerId.u.IssuerSerialNumber.Issuer,
+             &in->pCertInfo->Issuer);
+            if (ret)
+                ret = CRYPT_ConstructBlob(
+                 &info->SignerId.u.IssuerSerialNumber.SerialNumber,
+                 &in->pCertInfo->SerialNumber);
+            info->SignerId.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
+        }
+        else if (in->SignerId.dwIdChoice == CERT_ID_ISSUER_SERIAL_NUMBER)
+        {
+            info->dwVersion = CMSG_SIGNER_INFO_V1;
+            info->SignerId.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
+            ret = CRYPT_ConstructBlob(&info->SignerId.u.IssuerSerialNumber.Issuer,
+             &in->SignerId.u.IssuerSerialNumber.Issuer);
+            if (ret)
+                ret = CRYPT_ConstructBlob(
+                 &info->SignerId.u.IssuerSerialNumber.SerialNumber,
+                 &in->SignerId.u.IssuerSerialNumber.SerialNumber);
+        }
+        else
+        {
+            /* Implicitly dwIdChoice == CERT_ID_KEY_IDENTIFIER */
+            info->dwVersion = CMSG_SIGNER_INFO_V3;
+            info->SignerId.dwIdChoice = CERT_ID_KEY_IDENTIFIER;
+            ret = CRYPT_ConstructBlob(&info->SignerId.u.KeyId,
+             &in->SignerId.u.KeyId);
+        }
     }
+    /* Assumption:  algorithm IDs will point to static strings, not
+     * stack-based ones, so copying the pointer values is safe.
+     */
+    info->HashAlgorithm.pszObjId = in->HashAlgorithm.pszObjId;
+    if (ret)
+        ret = CRYPT_ConstructBlob(&info->HashAlgorithm.Parameters,
+         &in->HashAlgorithm.Parameters);
+    memset(&info->HashEncryptionAlgorithm, 0,
+     sizeof(info->HashEncryptionAlgorithm));
+    if (ret)
+        ret = CRYPT_ConstructAttributes(&info->AuthAttrs,
+         (CRYPT_ATTRIBUTES *)&in->cAuthAttr);
+    if (ret)
+        ret = CRYPT_ConstructAttributes(&info->UnauthAttrs,
+         (CRYPT_ATTRIBUTES *)&in->cUnauthAttr);
     return ret;
 }
 
-static void CSignerInfo_Free(CMSG_SIGNER_INFO *info)
+static void CSignerInfo_Free(CMSG_CMS_SIGNER_INFO *info)
 {
     DWORD i, j;
 
-    CryptMemFree(info->Issuer.pbData);
-    CryptMemFree(info->SerialNumber.pbData);
+    if (info->SignerId.dwIdChoice == CERT_ID_ISSUER_SERIAL_NUMBER)
+    {
+        CryptMemFree(info->SignerId.u.IssuerSerialNumber.Issuer.pbData);
+        CryptMemFree(info->SignerId.u.IssuerSerialNumber.SerialNumber.pbData);
+    }
+    else
+        CryptMemFree(info->SignerId.u.KeyId.pbData);
     CryptMemFree(info->HashAlgorithm.Parameters.pbData);
     CryptMemFree(info->EncryptedHash.pbData);
     for (i = 0; i < info->AuthAttrs.cAttr; i++)
@@ -792,31 +900,84 @@ static void CSignerInfo_Free(CMSG_SIGNER_INFO *info)
     CryptMemFree(info->UnauthAttrs.rgAttr);
 }
 
+typedef struct _CSignerHandles
+{
+    HCRYPTHASH contentHash;
+    HCRYPTHASH authAttrHash;
+} CSignerHandles;
+
 typedef struct _CSignedMsgData
 {
     CRYPT_SIGNED_INFO *info;
+    DWORD              cSignerHandle;
     CSignerHandles    *signerHandles;
 } CSignedMsgData;
 
-typedef struct _CSignedEncodeMsg
+/* Constructs the signer handles for the signerIndex'th signer of msg_data.
+ * Assumes signerIndex is a valid idnex, and that msg_data's info has already
+ * been constructed.
+ */
+static BOOL CSignedMsgData_ConstructSignerHandles(CSignedMsgData *msg_data,
+ DWORD signerIndex, HCRYPTPROV crypt_prov)
 {
-    CryptMsgBase    base;
-    CRYPT_DATA_BLOB data;
-    CSignedMsgData  msg_data;
-} CSignedEncodeMsg;
+    ALG_ID algID;
+    BOOL ret;
+
+    algID = CertOIDToAlgId(
+     msg_data->info->rgSignerInfo[signerIndex].HashAlgorithm.pszObjId);
+    ret = CryptCreateHash(crypt_prov, algID, 0, 0,
+     &msg_data->signerHandles->contentHash);
+    if (ret && msg_data->info->rgSignerInfo[signerIndex].AuthAttrs.cAttr > 0)
+        ret = CryptCreateHash(crypt_prov, algID, 0, 0,
+         &msg_data->signerHandles->authAttrHash);
+    return ret;
+}
+
+/* Allocates a CSignedMsgData's handles.  Assumes its info has already been
+ * constructed.
+ */
+static BOOL CSignedMsgData_AllocateHandles(CSignedMsgData *msg_data)
+{
+    BOOL ret = TRUE;
+
+    if (msg_data->info->cSignerInfo)
+    {
+        msg_data->signerHandles =
+         CryptMemAlloc(msg_data->info->cSignerInfo * sizeof(CSignerHandles));
+        if (msg_data->signerHandles)
+        {
+            msg_data->cSignerHandle = msg_data->info->cSignerInfo;
+            memset(msg_data->signerHandles, 0,
+             msg_data->info->cSignerInfo * sizeof(CSignerHandles));
+        }
+        else
+        {
+            msg_data->cSignerHandle = 0;
+            ret = FALSE;
+        }
+    }
+    else
+    {
+        msg_data->cSignerHandle = 0;
+        msg_data->signerHandles = NULL;
+    }
+    return ret;
+}
 
 static void CSignedMsgData_CloseHandles(CSignedMsgData *msg_data)
 {
     DWORD i;
 
-    for (i = 0; i < msg_data->info->cSignerInfo; i++)
+    for (i = 0; i < msg_data->cSignerHandle; i++)
     {
-        CryptDestroyKey(msg_data->signerHandles[i].key);
-        CryptDestroyHash(msg_data->signerHandles[i].contentHash);
-        CryptDestroyHash(msg_data->signerHandles[i].authAttrHash);
-        CryptReleaseContext(msg_data->signerHandles[i].prov, 0);
+        if (msg_data->signerHandles[i].contentHash)
+            CryptDestroyHash(msg_data->signerHandles[i].contentHash);
+        if (msg_data->signerHandles[i].authAttrHash)
+            CryptDestroyHash(msg_data->signerHandles[i].authAttrHash);
     }
     CryptMemFree(msg_data->signerHandles);
+    msg_data->signerHandles = NULL;
+    msg_data->cSignerHandle = 0;
 }
 
 static BOOL CSignedMsgData_UpdateHash(CSignedMsgData *msg_data,
@@ -825,121 +986,12 @@ static BOOL CSignedMsgData_UpdateHash(CSignedMsgData *msg_data,
     DWORD i;
     BOOL ret = TRUE;
 
-    for (i = 0; ret && i < msg_data->info->cSignerInfo; i++)
+    for (i = 0; ret && i < msg_data->cSignerHandle; i++)
         ret = CryptHashData(msg_data->signerHandles[i].contentHash, pbData,
          cbData, 0);
     return ret;
 }
 
-static void CSignedEncodeMsg_Close(HCRYPTMSG hCryptMsg)
-{
-    CSignedEncodeMsg *msg = (CSignedEncodeMsg *)hCryptMsg;
-    DWORD i;
-
-    CryptMemFree(msg->data.pbData);
-    CRYPT_FreeBlobArray((BlobArray *)&msg->msg_data.info->cCertEncoded);
-    CRYPT_FreeBlobArray((BlobArray *)&msg->msg_data.info->cCrlEncoded);
-    for (i = 0; i < msg->msg_data.info->cSignerInfo; i++)
-        CSignerInfo_Free(&msg->msg_data.info->rgSignerInfo[i]);
-    CSignedMsgData_CloseHandles(&msg->msg_data);
-    CryptMemFree(msg->msg_data.info->rgSignerInfo);
-    CryptMemFree(msg->msg_data.info);
-}
-
-static BOOL CSignedEncodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
- DWORD dwIndex, void *pvData, DWORD *pcbData)
-{
-    CSignedEncodeMsg *msg = (CSignedEncodeMsg *)hCryptMsg;
-    BOOL ret = FALSE;
-
-    switch (dwParamType)
-    {
-    case CMSG_CONTENT_PARAM:
-    {
-        CRYPT_CONTENT_INFO info;
-
-        ret = CryptMsgGetParam(hCryptMsg, CMSG_BARE_CONTENT_PARAM, 0, NULL,
-         &info.Content.cbData);
-        if (ret)
-        {
-            info.Content.pbData = CryptMemAlloc(info.Content.cbData);
-            if (info.Content.pbData)
-            {
-                ret = CryptMsgGetParam(hCryptMsg, CMSG_BARE_CONTENT_PARAM, 0,
-                 info.Content.pbData, &info.Content.cbData);
-                if (ret)
-                {
-                    char oid_rsa_signed[] = szOID_RSA_signedData;
-
-                    info.pszObjId = oid_rsa_signed;
-                    ret = CryptEncodeObjectEx(X509_ASN_ENCODING,
-                     PKCS_CONTENT_INFO, &info, 0, NULL, pvData, pcbData);
-                }
-                CryptMemFree(info.Content.pbData);
-            }
-            else
-                ret = FALSE;
-        }
-        break;
-    }
-    case CMSG_BARE_CONTENT_PARAM:
-    {
-        CRYPT_SIGNED_INFO info;
-        char oid_rsa_data[] = szOID_RSA_data;
-
-        memcpy(&info, msg->msg_data.info, sizeof(info));
-        /* Quirk:  OID is only encoded messages if an update has happened */
-        if (msg->base.state != MsgStateInit)
-            info.content.pszObjId = oid_rsa_data;
-        else
-            info.content.pszObjId = NULL;
-        if (msg->data.cbData)
-        {
-            CRYPT_DATA_BLOB blob = { msg->data.cbData, msg->data.pbData };
-
-            ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_OCTET_STRING,
-             &blob, CRYPT_ENCODE_ALLOC_FLAG, NULL,
-             &info.content.Content.pbData, &info.content.Content.cbData);
-        }
-        else
-        {
-            info.content.Content.cbData = 0;
-            info.content.Content.pbData = NULL;
-            ret = TRUE;
-        }
-        if (ret)
-        {
-            ret = CRYPT_AsnEncodePKCSSignedInfo(&info, pvData, pcbData);
-            LocalFree(info.content.Content.pbData);
-        }
-        break;
-    }
-    case CMSG_COMPUTED_HASH_PARAM:
-        if (dwIndex >= msg->msg_data.info->cSignerInfo)
-            SetLastError(CRYPT_E_INVALID_INDEX);
-        else
-            ret = CryptGetHashParam(
-             msg->msg_data.signerHandles[dwIndex].contentHash, HP_HASHVAL,
-             pvData, pcbData, 0);
-        break;
-    case CMSG_ENCODED_SIGNER:
-        if (dwIndex >= msg->msg_data.info->cSignerInfo)
-            SetLastError(CRYPT_E_INVALID_INDEX);
-        else
-            ret = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
-             PKCS7_SIGNER_INFO, &msg->msg_data.info->rgSignerInfo[dwIndex], 0,
-             NULL, pvData, pcbData);
-        break;
-    case CMSG_VERSION_PARAM:
-        ret = CRYPT_CopyParam(pvData, pcbData, &msg->msg_data.info->version,
-         sizeof(msg->msg_data.info->version));
-        break;
-    default:
-        SetLastError(CRYPT_E_INVALID_MSG_TYPE);
-    }
-    return ret;
-}
-
 static BOOL CRYPT_AppendAttribute(CRYPT_ATTRIBUTES *out,
  const CRYPT_ATTRIBUTE *in)
 {
@@ -992,8 +1044,13 @@ static BOOL CSignedMsgData_AppendMessageDigestAttribute(
     return ret;
 }
 
+typedef enum {
+    Sign,
+    Verify
+} SignOrVerify;
+
 static BOOL CSignedMsgData_UpdateAuthenticatedAttributes(
- CSignedMsgData *msg_data)
+ CSignedMsgData *msg_data, SignOrVerify flag)
 {
     DWORD i;
     BOOL ret = TRUE;
@@ -1004,18 +1061,22 @@ static BOOL CSignedMsgData_UpdateAuthenticatedAttributes(
     {
         if (msg_data->info->rgSignerInfo[i].AuthAttrs.cAttr)
         {
-            BYTE oid_rsa_data_encoded[] = { 0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,
-             0x0d,0x01,0x07,0x01 };
-            CRYPT_DATA_BLOB content = { sizeof(oid_rsa_data_encoded),
-             oid_rsa_data_encoded };
-            char contentType[] = szOID_RSA_contentType;
-            CRYPT_ATTRIBUTE contentTypeAttr = { contentType, 1, &content };
-
-            /* FIXME: does this depend on inner OID? */
-            ret = CRYPT_AppendAttribute(
-             &msg_data->info->rgSignerInfo[i].AuthAttrs, &contentTypeAttr);
-            if (ret)
-                ret = CSignedMsgData_AppendMessageDigestAttribute(msg_data, i);
+            if (flag == Sign)
+            {
+                BYTE oid_rsa_data_encoded[] = { 0x06,0x09,0x2a,0x86,0x48,0x86,
+                 0xf7,0x0d,0x01,0x07,0x01 };
+                CRYPT_DATA_BLOB content = { sizeof(oid_rsa_data_encoded),
+                 oid_rsa_data_encoded };
+                char contentType[] = szOID_RSA_contentType;
+                CRYPT_ATTRIBUTE contentTypeAttr = { contentType, 1, &content };
+
+                /* FIXME: does this depend on inner OID? */
+                ret = CRYPT_AppendAttribute(
+                 &msg_data->info->rgSignerInfo[i].AuthAttrs, &contentTypeAttr);
+                if (ret)
+                    ret = CSignedMsgData_AppendMessageDigestAttribute(msg_data,
+                     i);
+            }
             if (ret)
             {
                 LPBYTE encodedAttrs;
@@ -1023,7 +1084,7 @@ static BOOL CSignedMsgData_UpdateAuthenticatedAttributes(
 
                 ret = CryptEncodeObjectEx(X509_ASN_ENCODING, PKCS_ATTRIBUTES,
                  &msg_data->info->rgSignerInfo[i].AuthAttrs,
-                 CRYPT_ENCODE_ALLOC_FLAG, NULL, (LPBYTE)&encodedAttrs, &size);
+                 CRYPT_ENCODE_ALLOC_FLAG, NULL, &encodedAttrs, &size);
                 if (ret)
                 {
                     ret = CryptHashData(
@@ -1090,32 +1151,168 @@ static BOOL CSignedMsgData_Sign(CSignedMsgData *msg_data)
 }
 
 static BOOL CSignedMsgData_Update(CSignedMsgData *msg_data,
- const BYTE *pbData, DWORD cbData, BOOL fFinal)
+ const BYTE *pbData, DWORD cbData, BOOL fFinal, SignOrVerify flag)
 {
     BOOL ret = CSignedMsgData_UpdateHash(msg_data, pbData, cbData);
 
     if (ret && fFinal)
     {
-        ret = CSignedMsgData_UpdateAuthenticatedAttributes(msg_data);
-        if (ret)
+        ret = CSignedMsgData_UpdateAuthenticatedAttributes(msg_data, flag);
+        if (ret && flag == Sign)
             ret = CSignedMsgData_Sign(msg_data);
     }
     return ret;
 }
 
-static BOOL CSignedEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
- DWORD cbData, BOOL fFinal)
+typedef struct _CSignedEncodeMsg
+{
+    CryptMsgBase    base;
+    LPSTR           innerOID;
+    CRYPT_DATA_BLOB data;
+    CSignedMsgData  msg_data;
+} CSignedEncodeMsg;
+
+static void CSignedEncodeMsg_Close(HCRYPTMSG hCryptMsg)
+{
+    CSignedEncodeMsg *msg = hCryptMsg;
+    DWORD i;
+
+    CryptMemFree(msg->innerOID);
+    CryptMemFree(msg->data.pbData);
+    CRYPT_FreeBlobArray((BlobArray *)&msg->msg_data.info->cCertEncoded);
+    CRYPT_FreeBlobArray((BlobArray *)&msg->msg_data.info->cCrlEncoded);
+    for (i = 0; i < msg->msg_data.info->cSignerInfo; i++)
+        CSignerInfo_Free(&msg->msg_data.info->rgSignerInfo[i]);
+    CSignedMsgData_CloseHandles(&msg->msg_data);
+    CryptMemFree(msg->msg_data.info->rgSignerInfo);
+    CryptMemFree(msg->msg_data.info);
+}
+
+static BOOL CSignedEncodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
+ DWORD dwIndex, void *pvData, DWORD *pcbData)
 {
-    CSignedEncodeMsg *msg = (CSignedEncodeMsg *)hCryptMsg;
+    CSignedEncodeMsg *msg = hCryptMsg;
     BOOL ret = FALSE;
 
-    if (msg->base.streamed || (msg->base.open_flags & CMSG_DETACHED_FLAG))
+    switch (dwParamType)
     {
-        ret = CSignedMsgData_Update(&msg->msg_data, pbData, cbData, fFinal);
-        if (msg->base.streamed)
-            FIXME("streamed partial stub\n");
-    }
-    else
+    case CMSG_CONTENT_PARAM:
+    {
+        CRYPT_CONTENT_INFO info;
+
+        ret = CryptMsgGetParam(hCryptMsg, CMSG_BARE_CONTENT_PARAM, 0, NULL,
+         &info.Content.cbData);
+        if (ret)
+        {
+            info.Content.pbData = CryptMemAlloc(info.Content.cbData);
+            if (info.Content.pbData)
+            {
+                ret = CryptMsgGetParam(hCryptMsg, CMSG_BARE_CONTENT_PARAM, 0,
+                 info.Content.pbData, &info.Content.cbData);
+                if (ret)
+                {
+                    char oid_rsa_signed[] = szOID_RSA_signedData;
+
+                    info.pszObjId = oid_rsa_signed;
+                    ret = CryptEncodeObjectEx(X509_ASN_ENCODING,
+                     PKCS_CONTENT_INFO, &info, 0, NULL, pvData, pcbData);
+                }
+                CryptMemFree(info.Content.pbData);
+            }
+            else
+                ret = FALSE;
+        }
+        break;
+    }
+    case CMSG_BARE_CONTENT_PARAM:
+    {
+        CRYPT_SIGNED_INFO info;
+        BOOL freeContent = FALSE;
+
+        info = *msg->msg_data.info;
+        if (!msg->innerOID || !strcmp(msg->innerOID, szOID_RSA_data))
+        {
+            char oid_rsa_data[] = szOID_RSA_data;
+
+            /* Quirk:  OID is only encoded messages if an update has happened */
+            if (msg->base.state != MsgStateInit)
+                info.content.pszObjId = oid_rsa_data;
+            else
+                info.content.pszObjId = NULL;
+            if (msg->data.cbData)
+            {
+                CRYPT_DATA_BLOB blob = { msg->data.cbData, msg->data.pbData };
+
+                ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_OCTET_STRING,
+                 &blob, CRYPT_ENCODE_ALLOC_FLAG, NULL,
+                 &info.content.Content.pbData, &info.content.Content.cbData);
+                freeContent = TRUE;
+            }
+            else
+            {
+                info.content.Content.cbData = 0;
+                info.content.Content.pbData = NULL;
+                ret = TRUE;
+            }
+        }
+        else
+        {
+            info.content.pszObjId = msg->innerOID;
+            info.content.Content.cbData = msg->data.cbData;
+            info.content.Content.pbData = msg->data.pbData;
+            ret = TRUE;
+        }
+        if (ret)
+        {
+            ret = CRYPT_AsnEncodeCMSSignedInfo(&info, pvData, pcbData);
+            if (freeContent)
+                LocalFree(info.content.Content.pbData);
+        }
+        break;
+    }
+    case CMSG_COMPUTED_HASH_PARAM:
+        if (dwIndex >= msg->msg_data.cSignerHandle)
+            SetLastError(CRYPT_E_INVALID_INDEX);
+        else
+            ret = CryptGetHashParam(
+             msg->msg_data.signerHandles[dwIndex].contentHash, HP_HASHVAL,
+             pvData, pcbData, 0);
+        break;
+    case CMSG_ENCODED_SIGNER:
+        if (dwIndex >= msg->msg_data.info->cSignerInfo)
+            SetLastError(CRYPT_E_INVALID_INDEX);
+        else
+            ret = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
+             CMS_SIGNER_INFO, &msg->msg_data.info->rgSignerInfo[dwIndex], 0,
+             NULL, pvData, pcbData);
+        break;
+    case CMSG_VERSION_PARAM:
+        ret = CRYPT_CopyParam(pvData, pcbData, &msg->msg_data.info->version,
+         sizeof(msg->msg_data.info->version));
+        break;
+    default:
+        SetLastError(CRYPT_E_INVALID_MSG_TYPE);
+    }
+    return ret;
+}
+
+static BOOL CSignedEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
+ DWORD cbData, BOOL fFinal)
+{
+    CSignedEncodeMsg *msg = hCryptMsg;
+    BOOL ret = FALSE;
+
+    if (msg->base.state == MsgStateFinalized)
+        SetLastError(CRYPT_E_MSG_ERROR);
+    else if (msg->base.streamed || (msg->base.open_flags & CMSG_DETACHED_FLAG))
+    {
+        ret = CSignedMsgData_Update(&msg->msg_data, pbData, cbData, fFinal,
+         Sign);
+        if (msg->base.streamed)
+            FIXME("streamed partial stub\n");
+        msg->base.state = fFinal ? MsgStateFinalized : MsgStateUpdated;
+    }
+    else
     {
         if (!fFinal)
             SetLastError(CRYPT_E_MSG_ERROR);
@@ -1135,7 +1332,8 @@ static BOOL CSignedEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
                 ret = TRUE;
             if (ret)
                 ret = CSignedMsgData_Update(&msg->msg_data, pbData, cbData,
-                 fFinal);
+                 fFinal, Sign);
+            msg->base.state = MsgStateFinalized;
         }
     }
     return ret;
@@ -1145,8 +1343,7 @@ static HCRYPTMSG CSignedEncodeMsg_Open(DWORD dwFlags,
  const void *pvMsgEncodeInfo, LPSTR pszInnerContentObjID,
  PCMSG_STREAM_INFO pStreamInfo)
 {
-    const CMSG_SIGNED_ENCODE_INFO_WITH_CMS *info =
-     (const CMSG_SIGNED_ENCODE_INFO_WITH_CMS *)pvMsgEncodeInfo;
+    const CMSG_SIGNED_ENCODE_INFO_WITH_CMS *info = pvMsgEncodeInfo;
     DWORD i;
     CSignedEncodeMsg *msg;
 
@@ -1156,7 +1353,8 @@ static HCRYPTMSG CSignedEncodeMsg_Open(DWORD dwFlags,
         SetLastError(E_INVALIDARG);
         return NULL;
     }
-    if (info->cbSize == sizeof(CMSG_SIGNED_ENCODE_INFO_WITH_CMS))
+    if (info->cbSize == sizeof(CMSG_SIGNED_ENCODE_INFO_WITH_CMS) &&
+     info->cAttrCertEncoded)
     {
         FIXME("CMSG_SIGNED_ENCODE_INFO with CMS fields unsupported\n");
         return NULL;
@@ -1172,9 +1370,22 @@ static HCRYPTMSG CSignedEncodeMsg_Open(DWORD dwFlags,
         CryptMsgBase_Init((CryptMsgBase *)msg, dwFlags, pStreamInfo,
          CSignedEncodeMsg_Close, CSignedEncodeMsg_GetParam,
          CSignedEncodeMsg_Update, CRYPT_DefaultMsgControl);
+        if (pszInnerContentObjID)
+        {
+            msg->innerOID = CryptMemAlloc(strlen(pszInnerContentObjID) + 1);
+            if (msg->innerOID)
+                strcpy(msg->innerOID, pszInnerContentObjID);
+            else
+                ret = FALSE;
+        }
+        else
+            msg->innerOID = NULL;
         msg->data.cbData = 0;
         msg->data.pbData = NULL;
-        msg->msg_data.info = CryptMemAlloc(sizeof(CRYPT_SIGNED_INFO));
+        if (ret)
+            msg->msg_data.info = CryptMemAlloc(sizeof(CRYPT_SIGNED_INFO));
+        else
+            msg->msg_data.info = NULL;
         if (msg->msg_data.info)
         {
             memset(msg->msg_data.info, 0, sizeof(CRYPT_SIGNED_INFO));
@@ -1182,32 +1393,46 @@ static HCRYPTMSG CSignedEncodeMsg_Open(DWORD dwFlags,
         }
         else
             ret = FALSE;
-        if (ret && info->cSigners)
+        if (ret)
         {
-            msg->msg_data.signerHandles =
-             CryptMemAlloc(info->cSigners * sizeof(CSignerHandles));
-            if (msg->msg_data.signerHandles)
-                msg->msg_data.info->rgSignerInfo =
-                 CryptMemAlloc(info->cSigners * sizeof(CMSG_SIGNER_INFO));
-            else
+            if (info->cSigners)
             {
-                ret = FALSE;
-                msg->msg_data.info->rgSignerInfo = NULL;
+                msg->msg_data.info->rgSignerInfo =
+                 CryptMemAlloc(info->cSigners * sizeof(CMSG_CMS_SIGNER_INFO));
+                if (msg->msg_data.info->rgSignerInfo)
+                {
+                    msg->msg_data.info->cSignerInfo = info->cSigners;
+                    memset(msg->msg_data.info->rgSignerInfo, 0,
+                     msg->msg_data.info->cSignerInfo *
+                     sizeof(CMSG_CMS_SIGNER_INFO));
+                    ret = CSignedMsgData_AllocateHandles(&msg->msg_data);
+                    for (i = 0; ret && i < msg->msg_data.info->cSignerInfo; i++)
+                    {
+                        if (info->rgSigners[i].SignerId.dwIdChoice ==
+                         CERT_ID_KEY_IDENTIFIER)
+                            msg->msg_data.info->version = CMSG_SIGNED_DATA_V3;
+                        ret = CSignerInfo_Construct(
+                         &msg->msg_data.info->rgSignerInfo[i],
+                         &info->rgSigners[i]);
+                        if (ret)
+                        {
+                            ret = CSignedMsgData_ConstructSignerHandles(
+                             &msg->msg_data, i, info->rgSigners[i].hCryptProv);
+                            if (dwFlags & CMSG_CRYPT_RELEASE_CONTEXT_FLAG)
+                                CryptReleaseContext(info->rgSigners[i].hCryptProv,
+                                 0);
+                        }
+                    }
+                }
+                else
+                    ret = FALSE;
             }
-            if (msg->msg_data.info->rgSignerInfo)
+            else
             {
-                msg->msg_data.info->cSignerInfo = info->cSigners;
-                memset(msg->msg_data.signerHandles, 0,
-                 msg->msg_data.info->cSignerInfo * sizeof(CSignerHandles));
-                memset(msg->msg_data.info->rgSignerInfo, 0,
-                 msg->msg_data.info->cSignerInfo * sizeof(CMSG_SIGNER_INFO));
-                for (i = 0; ret && i < msg->msg_data.info->cSignerInfo; i++)
-                    ret = CSignerInfo_Construct(&msg->msg_data.signerHandles[i],
-                     &msg->msg_data.info->rgSignerInfo[i],
-                     &info->rgSigners[i], dwFlags);
+                msg->msg_data.info->cSignerInfo = 0;
+                msg->msg_data.signerHandles = NULL;
+                msg->msg_data.cSignerHandle = 0;
             }
-            else
-                ret = FALSE;
         }
         if (ret)
             ret = CRYPT_ConstructBlobArray(
@@ -1226,23 +1451,6 @@ static HCRYPTMSG CSignedEncodeMsg_Open(DWORD dwFlags,
     return msg;
 }
 
-static inline const char *MSG_TYPE_STR(DWORD type)
-{
-    switch (type)
-    {
-#define _x(x) case (x): return #x
-        _x(CMSG_DATA);
-        _x(CMSG_SIGNED);
-        _x(CMSG_ENVELOPED);
-        _x(CMSG_SIGNED_AND_ENVELOPED);
-        _x(CMSG_HASHED);
-        _x(CMSG_ENCRYPTED);
-#undef _x
-        default:
-            return wine_dbg_sprintf("unknown (%d)", type);
-    }
-}
-
 HCRYPTMSG WINAPI CryptMsgOpenToEncode(DWORD dwMsgEncodingType, DWORD dwFlags,
  DWORD dwMsgType, const void *pvMsgEncodeInfo, LPSTR pszInnerContentObjID,
  PCMSG_STREAM_INFO pStreamInfo)
@@ -1272,7 +1480,7 @@ HCRYPTMSG WINAPI CryptMsgOpenToEncode(DWORD dwMsgEncodingType, DWORD dwFlags,
          pszInnerContentObjID, pStreamInfo);
         break;
     case CMSG_ENVELOPED:
-        FIXME("unimplemented for type %s\n", MSG_TYPE_STR(dwMsgType));
+        FIXME("unimplemented for type CMSG_ENVELOPED\n");
         break;
     case CMSG_SIGNED_AND_ENVELOPED:
     case CMSG_ENCRYPTED:
@@ -1293,12 +1501,13 @@ typedef struct _CDecodeMsg
         CSignedMsgData signed_data;
     } u;
     CRYPT_DATA_BLOB        msg_data;
+    CRYPT_DATA_BLOB        detached_data;
     PCONTEXT_PROPERTY_LIST properties;
 } CDecodeMsg;
 
 static void CDecodeMsg_Close(HCRYPTMSG hCryptMsg)
 {
-    CDecodeMsg *msg = (CDecodeMsg *)hCryptMsg;
+    CDecodeMsg *msg = hCryptMsg;
 
     if (msg->base.open_flags & CMSG_CRYPT_RELEASE_CONTEXT_FLAG)
         CryptReleaseContext(msg->crypt_prov, 0);
@@ -1310,29 +1519,33 @@ static void CDecodeMsg_Close(HCRYPTMSG hCryptMsg)
         break;
     case CMSG_SIGNED:
         if (msg->u.signed_data.info)
+        {
             LocalFree(msg->u.signed_data.info);
+            CSignedMsgData_CloseHandles(&msg->u.signed_data);
+        }
         break;
     }
     CryptMemFree(msg->msg_data.pbData);
+    CryptMemFree(msg->detached_data.pbData);
     ContextPropertyList_Free(msg->properties);
 }
 
-static BOOL CDecodeMsg_CopyData(CDecodeMsg *msg, const BYTE *pbData,
+static BOOL CDecodeMsg_CopyData(CRYPT_DATA_BLOB *blob, const BYTE *pbData,
  DWORD cbData)
 {
     BOOL ret = TRUE;
 
     if (cbData)
     {
-        if (msg->msg_data.cbData)
-            msg->msg_data.pbData = CryptMemRealloc(msg->msg_data.pbData,
-             msg->msg_data.cbData + cbData);
+        if (blob->cbData)
+            blob->pbData = CryptMemRealloc(blob->pbData,
+             blob->cbData + cbData);
         else
-            msg->msg_data.pbData = CryptMemAlloc(cbData);
-        if (msg->msg_data.pbData)
+            blob->pbData = CryptMemAlloc(cbData);
+        if (blob->pbData)
         {
-            memcpy(msg->msg_data.pbData + msg->msg_data.cbData, pbData, cbData);
-            msg->msg_data.cbData += cbData;
+            memcpy(blob->pbData + blob->cbData, pbData, cbData);
+            blob->cbData += cbData;
         }
         else
             ret = FALSE;
@@ -1347,8 +1560,7 @@ static BOOL CDecodeMsg_DecodeDataContent(CDecodeMsg *msg, CRYPT_DER_BLOB *blob)
     DWORD size;
 
     ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_OCTET_STRING,
-     blob->pbData, blob->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, (LPBYTE)&data,
-     &size);
+     blob->pbData, blob->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &data, &size);
     if (ret)
     {
         ret = ContextPropertyList_SetProperty(msg->properties,
@@ -1421,12 +1633,15 @@ static BOOL CDecodeMsg_DecodeHashedContent(CDecodeMsg *msg,
          (const BYTE *)digestedData->ContentInfo.pszObjId,
          digestedData->ContentInfo.pszObjId ?
          strlen(digestedData->ContentInfo.pszObjId) + 1 : 0);
-        if (digestedData->ContentInfo.Content.cbData)
-            CDecodeMsg_DecodeDataContent(msg,
-             &digestedData->ContentInfo.Content);
-        else
-            ContextPropertyList_SetProperty(msg->properties,
-             CMSG_CONTENT_PARAM, NULL, 0);
+        if (!(msg->base.open_flags & CMSG_DETACHED_FLAG))
+        {
+            if (digestedData->ContentInfo.Content.cbData)
+                CDecodeMsg_DecodeDataContent(msg,
+                 &digestedData->ContentInfo.Content);
+            else
+                ContextPropertyList_SetProperty(msg->properties,
+                 CMSG_CONTENT_PARAM, NULL, 0);
+        }
         ContextPropertyList_SetProperty(msg->properties, CMSG_HASH_DATA_PARAM,
          digestedData->hash.pbData, digestedData->hash.cbData);
         LocalFree(digestedData);
@@ -1441,13 +1656,14 @@ static BOOL CDecodeMsg_DecodeSignedContent(CDecodeMsg *msg,
     CRYPT_SIGNED_INFO *signedInfo;
     DWORD size;
 
-    ret = CRYPT_AsnDecodePKCSSignedInfo(blob->pbData, blob->cbData,
+    ret = CRYPT_AsnDecodeCMSSignedInfo(blob->pbData, blob->cbData,
      CRYPT_DECODE_ALLOC_FLAG, NULL, (CRYPT_SIGNED_INFO *)&signedInfo,
      &size);
     if (ret)
         msg->u.signed_data.info = signedInfo;
     return ret;
 }
+
 /* Decodes the content in blob as the type given, and updates the value
  * (type, parameters, etc.) of msg based on what blob contains.
  * It doesn't just use msg's type, to allow a recursive call from an implicitly
@@ -1469,7 +1685,7 @@ static BOOL CDecodeMsg_DecodeContent(CDecodeMsg *msg, CRYPT_DER_BLOB *blob,
             msg->type = CMSG_HASHED;
         break;
     case CMSG_ENVELOPED:
-        FIXME("unimplemented for type %s\n", MSG_TYPE_STR(type));
+        FIXME("unimplemented for type CMSG_ENVELOPED\n");
         ret = TRUE;
         break;
     case CMSG_SIGNED:
@@ -1483,7 +1699,7 @@ static BOOL CDecodeMsg_DecodeContent(CDecodeMsg *msg, CRYPT_DER_BLOB *blob,
 
         ret = CryptDecodeObjectEx(X509_ASN_ENCODING, PKCS_CONTENT_INFO,
          msg->msg_data.pbData, msg->msg_data.cbData, CRYPT_DECODE_ALLOC_FLAG,
-         NULL, (LPBYTE)&info, &size);
+         NULL, &info, &size);
         if (ret)
         {
             if (!strcmp(info->pszObjId, szOID_RSA_data))
@@ -1509,19 +1725,160 @@ static BOOL CDecodeMsg_DecodeContent(CDecodeMsg *msg, CRYPT_DER_BLOB *blob,
     return ret;
 }
 
+static BOOL CDecodeMsg_FinalizeHashedContent(CDecodeMsg *msg,
+ CRYPT_DER_BLOB *blob)
+{
+    CRYPT_ALGORITHM_IDENTIFIER *hashAlgoID = NULL;
+    DWORD size = 0;
+    ALG_ID algID = 0;
+    BOOL ret;
+
+    CryptMsgGetParam(msg, CMSG_HASH_ALGORITHM_PARAM, 0, NULL, &size);
+    hashAlgoID = CryptMemAlloc(size);
+    ret = CryptMsgGetParam(msg, CMSG_HASH_ALGORITHM_PARAM, 0, hashAlgoID,
+     &size);
+    if (ret)
+        algID = CertOIDToAlgId(hashAlgoID->pszObjId);
+    ret = CryptCreateHash(msg->crypt_prov, algID, 0, 0, &msg->u.hash);
+    if (ret)
+    {
+        CRYPT_DATA_BLOB content;
+
+        if (msg->base.open_flags & CMSG_DETACHED_FLAG)
+        {
+            /* Unlike for non-detached messages, the data were never stored as
+             * the content param, but were saved in msg->detached_data instead.
+             */
+            content.pbData = msg->detached_data.pbData;
+            content.cbData = msg->detached_data.cbData;
+        }
+        else
+            ret = ContextPropertyList_FindProperty(msg->properties,
+             CMSG_CONTENT_PARAM, &content);
+        if (ret)
+            ret = CryptHashData(msg->u.hash, content.pbData, content.cbData, 0);
+    }
+    CryptMemFree(hashAlgoID);
+    return ret;
+}
+
+static BOOL CDecodeMsg_FinalizeSignedContent(CDecodeMsg *msg,
+ CRYPT_DER_BLOB *blob)
+{
+    BOOL ret;
+    DWORD i, size;
+
+    ret = CSignedMsgData_AllocateHandles(&msg->u.signed_data);
+    for (i = 0; ret && i < msg->u.signed_data.info->cSignerInfo; i++)
+        ret = CSignedMsgData_ConstructSignerHandles(&msg->u.signed_data, i,
+         msg->crypt_prov);
+    if (ret)
+    {
+        CRYPT_DATA_BLOB *content;
+
+        /* Now that we have all the content, update the hash handles with
+         * it.  If the message is a detached message, the content is stored
+         * in msg->detached_data rather than in the signed message's
+         * content.
+         */
+        if (msg->base.open_flags & CMSG_DETACHED_FLAG)
+            content = &msg->detached_data;
+        else
+            content = &msg->u.signed_data.info->content.Content;
+        if (content->cbData)
+        {
+            /* If the message is not detached, have to decode the message's
+             * content if the type is szOID_RSA_data.
+             */
+            if (!(msg->base.open_flags & CMSG_DETACHED_FLAG) &&
+             !strcmp(msg->u.signed_data.info->content.pszObjId,
+             szOID_RSA_data))
+            {
+                CRYPT_DATA_BLOB *blob;
+
+                ret = CryptDecodeObjectEx(X509_ASN_ENCODING,
+                 X509_OCTET_STRING, content->pbData, content->cbData,
+                 CRYPT_DECODE_ALLOC_FLAG, NULL, &blob, &size);
+                if (ret)
+                {
+                    ret = CSignedMsgData_Update(&msg->u.signed_data,
+                     blob->pbData, blob->cbData, TRUE, Verify);
+                    LocalFree(blob);
+                }
+            }
+            else
+                ret = CSignedMsgData_Update(&msg->u.signed_data,
+                 content->pbData, content->cbData, TRUE, Verify);
+        }
+    }
+    return ret;
+}
+
+static BOOL CDecodeMsg_FinalizeContent(CDecodeMsg *msg, CRYPT_DER_BLOB *blob)
+{
+    BOOL ret = FALSE;
+
+    switch (msg->type)
+    {
+    case CMSG_HASHED:
+        ret = CDecodeMsg_FinalizeHashedContent(msg, blob);
+        break;
+    case CMSG_SIGNED:
+        ret = CDecodeMsg_FinalizeSignedContent(msg, blob);
+        break;
+    default:
+        ret = TRUE;
+    }
+    return ret;
+}
+
 static BOOL CDecodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
  DWORD cbData, BOOL fFinal)
 {
-    CDecodeMsg *msg = (CDecodeMsg *)hCryptMsg;
+    CDecodeMsg *msg = hCryptMsg;
     BOOL ret = FALSE;
 
     TRACE("(%p, %p, %d, %d)\n", hCryptMsg, pbData, cbData, fFinal);
 
-    if (msg->base.streamed)
+    if (msg->base.state == MsgStateFinalized)
+        SetLastError(CRYPT_E_MSG_ERROR);
+    else if (msg->base.streamed)
     {
-        ret = CDecodeMsg_CopyData(msg, pbData, cbData);
         FIXME("(%p, %p, %d, %d): streamed update stub\n", hCryptMsg, pbData,
          cbData, fFinal);
+        switch (msg->base.state)
+        {
+        case MsgStateInit:
+            ret = CDecodeMsg_CopyData(&msg->msg_data, pbData, cbData);
+            if (fFinal)
+            {
+                if (msg->base.open_flags & CMSG_DETACHED_FLAG)
+                    msg->base.state = MsgStateDataFinalized;
+                else
+                    msg->base.state = MsgStateFinalized;
+            }
+            else
+                msg->base.state = MsgStateUpdated;
+            break;
+        case MsgStateUpdated:
+            ret = CDecodeMsg_CopyData(&msg->msg_data, pbData, cbData);
+            if (fFinal)
+            {
+                if (msg->base.open_flags & CMSG_DETACHED_FLAG)
+                    msg->base.state = MsgStateDataFinalized;
+                else
+                    msg->base.state = MsgStateFinalized;
+            }
+            break;
+        case MsgStateDataFinalized:
+            ret = CDecodeMsg_CopyData(&msg->detached_data, pbData, cbData);
+            if (fFinal)
+                msg->base.state = MsgStateFinalized;
+            break;
+        default:
+            SetLastError(CRYPT_E_MSG_ERROR);
+            break;
+        }
     }
     else
     {
@@ -1529,12 +1886,32 @@ static BOOL CDecodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
             SetLastError(CRYPT_E_MSG_ERROR);
         else
         {
-            ret = CDecodeMsg_CopyData(msg, pbData, cbData);
-            if (ret)
-                ret = CDecodeMsg_DecodeContent(msg, &msg->msg_data, msg->type);
-
+            switch (msg->base.state)
+            {
+            case MsgStateInit:
+                ret = CDecodeMsg_CopyData(&msg->msg_data, pbData, cbData);
+                if (msg->base.open_flags & CMSG_DETACHED_FLAG)
+                    msg->base.state = MsgStateDataFinalized;
+                else
+                    msg->base.state = MsgStateFinalized;
+                break;
+            case MsgStateDataFinalized:
+                ret = CDecodeMsg_CopyData(&msg->detached_data, pbData, cbData);
+                msg->base.state = MsgStateFinalized;
+                break;
+            default:
+                SetLastError(CRYPT_E_MSG_ERROR);
+            }
         }
     }
+    if (ret && fFinal &&
+     ((msg->base.open_flags & CMSG_DETACHED_FLAG && msg->base.state ==
+     MsgStateDataFinalized) ||
+     (!(msg->base.open_flags & CMSG_DETACHED_FLAG) && msg->base.state ==
+     MsgStateFinalized)))
+        ret = CDecodeMsg_DecodeContent(msg, &msg->msg_data, msg->type);
+    if (ret && msg->base.state == MsgStateFinalized)
+        ret = CDecodeMsg_FinalizeContent(msg, &msg->msg_data);
     return ret;
 }
 
@@ -1558,43 +1935,14 @@ static BOOL CDecodeHashMsg_GetParam(CDecodeMsg *msg, DWORD dwParamType,
         {
             ret = CRYPT_CopyParam(pvData, pcbData, blob.pbData, blob.cbData);
             if (ret && pvData)
-                CRYPT_FixUpAlgorithmID((CRYPT_ALGORITHM_IDENTIFIER *)pvData);
+                CRYPT_FixUpAlgorithmID(pvData);
         }
         else
             SetLastError(CRYPT_E_INVALID_MSG_TYPE);
         break;
     }
     case CMSG_COMPUTED_HASH_PARAM:
-        if (!msg->u.hash)
-        {
-            CRYPT_ALGORITHM_IDENTIFIER *hashAlgoID = NULL;
-            DWORD size = 0;
-            ALG_ID algID = 0;
-
-            CryptMsgGetParam(msg, CMSG_HASH_ALGORITHM_PARAM, 0, NULL, &size);
-            hashAlgoID = CryptMemAlloc(size);
-            ret = CryptMsgGetParam(msg, CMSG_HASH_ALGORITHM_PARAM, 0,
-             hashAlgoID, &size);
-            if (ret)
-                algID = CertOIDToAlgId(hashAlgoID->pszObjId);
-            ret = CryptCreateHash(msg->crypt_prov, algID, 0, 0, &msg->u.hash);
-            if (ret)
-            {
-                CRYPT_DATA_BLOB content;
-
-                ret = ContextPropertyList_FindProperty(msg->properties,
-                 CMSG_CONTENT_PARAM, &content);
-                if (ret)
-                    ret = CryptHashData(msg->u.hash, content.pbData,
-                     content.cbData, 0);
-            }
-            CryptMemFree(hashAlgoID);
-        }
-        else
-            ret = TRUE;
-        if (ret)
-            ret = CryptGetHashParam(msg->u.hash, HP_HASHVAL, pvData, pcbData,
-             0);
+        ret = CryptGetHashParam(msg->u.hash, HP_HASHVAL, pvData, pcbData, 0);
         break;
     default:
     {
@@ -1647,8 +1995,7 @@ static inline void CRYPT_CopyAttributes(CRYPT_ATTRIBUTES *out,
     {
         DWORD i;
 
-        if ((*nextData - (LPBYTE)0) % sizeof(DWORD))
-            *nextData += (*nextData - (LPBYTE)0) % sizeof(DWORD);
+        *nextData = POINTER_ALIGN_DWORD_PTR(*nextData);
         out->rgAttr = (CRYPT_ATTRIBUTE *)*nextData;
         *nextData += in->cAttr * sizeof(CRYPT_ATTRIBUTE);
         for (i = 0; i < in->cAttr; i++)
@@ -1664,9 +2011,9 @@ static inline void CRYPT_CopyAttributes(CRYPT_ATTRIBUTES *out,
                 DWORD j;
 
                 out->rgAttr[i].cValue = in->rgAttr[i].cValue;
-                if ((*nextData - (LPBYTE)0) % sizeof(DWORD))
-                    *nextData += (*nextData - (LPBYTE)0) % sizeof(DWORD);
+                *nextData = POINTER_ALIGN_DWORD_PTR(*nextData);
                 out->rgAttr[i].rgValue = (PCRYPT_DATA_BLOB)*nextData;
+                *nextData += in->rgAttr[i].cValue * sizeof(CRYPT_DATA_BLOB);
                 for (j = 0; j < in->rgAttr[i].cValue; j++)
                     CRYPT_CopyBlob(&out->rgAttr[i].rgValue[j],
                      &in->rgAttr[i].rgValue[j], nextData);
@@ -1684,23 +2031,82 @@ static DWORD CRYPT_SizeOfAttributes(const CRYPT_ATTRIBUTES *attr)
         if (attr->rgAttr[i].pszObjId)
             size += strlen(attr->rgAttr[i].pszObjId) + 1;
         /* align pointer */
-        if (size % sizeof(DWORD))
-            size += size % sizeof(DWORD);
+        size = ALIGN_DWORD_PTR(size);
         size += attr->rgAttr[i].cValue * sizeof(CRYPT_DATA_BLOB);
         for (j = 0; j < attr->rgAttr[i].cValue; j++)
             size += attr->rgAttr[i].rgValue[j].cbData;
     }
+    /* align pointer again to be conservative */
+    size = ALIGN_DWORD_PTR(size);
     return size;
 }
 
+static DWORD CRYPT_SizeOfKeyIdAsIssuerAndSerial(const CRYPT_DATA_BLOB *keyId)
+{
+    static char oid_key_rdn[] = szOID_KEYID_RDN;
+    DWORD size = 0;
+    CERT_RDN_ATTR attr;
+    CERT_RDN rdn = { 1, &attr };
+    CERT_NAME_INFO name = { 1, &rdn };
+
+    attr.pszObjId = oid_key_rdn;
+    attr.dwValueType = CERT_RDN_OCTET_STRING;
+    attr.Value.cbData = keyId->cbData;
+    attr.Value.pbData = keyId->pbData;
+    if (CryptEncodeObject(X509_ASN_ENCODING, X509_NAME, &name, NULL, &size))
+        size++; /* Only include size of special zero serial number on success */
+    return size;
+}
+
+static BOOL CRYPT_CopyKeyIdAsIssuerAndSerial(CERT_NAME_BLOB *issuer,
+ CRYPT_INTEGER_BLOB *serialNumber, const CRYPT_DATA_BLOB *keyId, DWORD encodedLen,
+ LPBYTE *nextData)
+{
+    static char oid_key_rdn[] = szOID_KEYID_RDN;
+    CERT_RDN_ATTR attr;
+    CERT_RDN rdn = { 1, &attr };
+    CERT_NAME_INFO name = { 1, &rdn };
+    BOOL ret;
+
+    /* Encode special zero serial number */
+    serialNumber->cbData = 1;
+    serialNumber->pbData = *nextData;
+    **nextData = 0;
+    (*nextData)++;
+    /* Encode issuer */
+    issuer->pbData = *nextData;
+    attr.pszObjId = oid_key_rdn;
+    attr.dwValueType = CERT_RDN_OCTET_STRING;
+    attr.Value.cbData = keyId->cbData;
+    attr.Value.pbData = keyId->pbData;
+    ret = CryptEncodeObject(X509_ASN_ENCODING, X509_NAME, &name, *nextData,
+     &encodedLen);
+    if (ret)
+    {
+        *nextData += encodedLen;
+        issuer->cbData = encodedLen;
+    }
+    return ret;
+}
+
 static BOOL CRYPT_CopySignerInfo(void *pvData, DWORD *pcbData,
- const CMSG_SIGNER_INFO *in)
+ const CMSG_CMS_SIGNER_INFO *in)
 {
-    DWORD size = sizeof(CMSG_SIGNER_INFO);
+    DWORD size = sizeof(CMSG_SIGNER_INFO), rdnSize = 0;
     BOOL ret;
 
-    size += in->Issuer.cbData;
-    size += in->SerialNumber.cbData;
+    TRACE("(%p, %d, %p)\n", pvData, pvData ? *pcbData : 0, in);
+
+    if (in->SignerId.dwIdChoice == CERT_ID_ISSUER_SERIAL_NUMBER)
+    {
+        size += in->SignerId.u.IssuerSerialNumber.Issuer.cbData;
+        size += in->SignerId.u.IssuerSerialNumber.SerialNumber.cbData;
+    }
+    else
+    {
+        rdnSize = CRYPT_SizeOfKeyIdAsIssuerAndSerial(&in->SignerId.u.KeyId);
+        size += rdnSize;
+    }
     if (in->HashAlgorithm.pszObjId)
         size += strlen(in->HashAlgorithm.pszObjId) + 1;
     size += in->HashAlgorithm.Parameters.cbData;
@@ -1709,8 +2115,7 @@ static BOOL CRYPT_CopySignerInfo(void *pvData, DWORD *pcbData,
     size += in->HashEncryptionAlgorithm.Parameters.cbData;
     size += in->EncryptedHash.cbData;
     /* align pointer */
-    if (size % sizeof(DWORD))
-        size += size % sizeof(DWORD);
+    size = ALIGN_DWORD_PTR(size);
     size += CRYPT_SizeOfAttributes(&in->AuthAttrs);
     size += CRYPT_SizeOfAttributes(&in->UnauthAttrs);
     if (!pvData)
@@ -1727,34 +2132,121 @@ static BOOL CRYPT_CopySignerInfo(void *pvData, DWORD *pcbData,
     else
     {
         LPBYTE nextData = (BYTE *)pvData + sizeof(CMSG_SIGNER_INFO);
-        CMSG_SIGNER_INFO *out = (CMSG_SIGNER_INFO *)pvData;
+        CMSG_SIGNER_INFO *out = pvData;
 
+        ret = TRUE;
         out->dwVersion = in->dwVersion;
-        CRYPT_CopyBlob(&out->Issuer, &in->Issuer, &nextData);
-        CRYPT_CopyBlob(&out->SerialNumber, &in->SerialNumber, &nextData);
+        if (in->SignerId.dwIdChoice == CERT_ID_ISSUER_SERIAL_NUMBER)
+        {
+            CRYPT_CopyBlob(&out->Issuer,
+             &in->SignerId.u.IssuerSerialNumber.Issuer, &nextData);
+            CRYPT_CopyBlob(&out->SerialNumber,
+             &in->SignerId.u.IssuerSerialNumber.SerialNumber, &nextData);
+        }
+        else
+            ret = CRYPT_CopyKeyIdAsIssuerAndSerial(&out->Issuer, &out->SerialNumber,
+             &in->SignerId.u.KeyId, rdnSize, &nextData);
+        if (ret)
+        {
+            CRYPT_CopyAlgorithmId(&out->HashAlgorithm, &in->HashAlgorithm,
+             &nextData);
+            CRYPT_CopyAlgorithmId(&out->HashEncryptionAlgorithm,
+             &in->HashEncryptionAlgorithm, &nextData);
+            CRYPT_CopyBlob(&out->EncryptedHash, &in->EncryptedHash, &nextData);
+            nextData = POINTER_ALIGN_DWORD_PTR(nextData);
+            CRYPT_CopyAttributes(&out->AuthAttrs, &in->AuthAttrs, &nextData);
+            CRYPT_CopyAttributes(&out->UnauthAttrs, &in->UnauthAttrs, &nextData);
+        }
+    }
+    TRACE("returning %d\n", ret);
+    return ret;
+}
+
+static BOOL CRYPT_CopyCMSSignerInfo(void *pvData, DWORD *pcbData,
+ const CMSG_CMS_SIGNER_INFO *in)
+{
+    DWORD size = sizeof(CMSG_CMS_SIGNER_INFO);
+    BOOL ret;
+
+    TRACE("(%p, %d, %p)\n", pvData, pvData ? *pcbData : 0, in);
+
+    if (in->SignerId.dwIdChoice == CERT_ID_ISSUER_SERIAL_NUMBER)
+    {
+        size += in->SignerId.u.IssuerSerialNumber.Issuer.cbData;
+        size += in->SignerId.u.IssuerSerialNumber.SerialNumber.cbData;
+    }
+    else
+        size += in->SignerId.u.KeyId.cbData;
+    if (in->HashAlgorithm.pszObjId)
+        size += strlen(in->HashAlgorithm.pszObjId) + 1;
+    size += in->HashAlgorithm.Parameters.cbData;
+    if (in->HashEncryptionAlgorithm.pszObjId)
+        size += strlen(in->HashEncryptionAlgorithm.pszObjId) + 1;
+    size += in->HashEncryptionAlgorithm.Parameters.cbData;
+    size += in->EncryptedHash.cbData;
+    /* align pointer */
+    size = ALIGN_DWORD_PTR(size);
+    size += CRYPT_SizeOfAttributes(&in->AuthAttrs);
+    size += CRYPT_SizeOfAttributes(&in->UnauthAttrs);
+    if (!pvData)
+    {
+        *pcbData = size;
+        ret = TRUE;
+    }
+    else if (*pcbData < size)
+    {
+        *pcbData = size;
+        SetLastError(ERROR_MORE_DATA);
+        ret = FALSE;
+    }
+    else
+    {
+        LPBYTE nextData = (BYTE *)pvData + sizeof(CMSG_CMS_SIGNER_INFO);
+        CMSG_CMS_SIGNER_INFO *out = pvData;
+
+        out->dwVersion = in->dwVersion;
+        out->SignerId.dwIdChoice = in->SignerId.dwIdChoice;
+        if (in->SignerId.dwIdChoice == CERT_ID_ISSUER_SERIAL_NUMBER)
+        {
+            CRYPT_CopyBlob(&out->SignerId.u.IssuerSerialNumber.Issuer,
+             &in->SignerId.u.IssuerSerialNumber.Issuer, &nextData);
+            CRYPT_CopyBlob(&out->SignerId.u.IssuerSerialNumber.SerialNumber,
+             &in->SignerId.u.IssuerSerialNumber.SerialNumber, &nextData);
+        }
+        else
+            CRYPT_CopyBlob(&out->SignerId.u.KeyId, &in->SignerId.u.KeyId, &nextData);
         CRYPT_CopyAlgorithmId(&out->HashAlgorithm, &in->HashAlgorithm,
          &nextData);
         CRYPT_CopyAlgorithmId(&out->HashEncryptionAlgorithm,
          &in->HashEncryptionAlgorithm, &nextData);
         CRYPT_CopyBlob(&out->EncryptedHash, &in->EncryptedHash, &nextData);
-        /* align pointer */
-        if ((nextData - (LPBYTE)0) % sizeof(DWORD))
-            nextData += (nextData - (LPBYTE)0) % sizeof(DWORD);
+        nextData = POINTER_ALIGN_DWORD_PTR(nextData);
         CRYPT_CopyAttributes(&out->AuthAttrs, &in->AuthAttrs, &nextData);
         CRYPT_CopyAttributes(&out->UnauthAttrs, &in->UnauthAttrs, &nextData);
         ret = TRUE;
     }
+    TRACE("returning %d\n", ret);
     return ret;
 }
 
 static BOOL CRYPT_CopySignerCertInfo(void *pvData, DWORD *pcbData,
- const CMSG_SIGNER_INFO *in)
+ const CMSG_CMS_SIGNER_INFO *in)
 {
-    DWORD size = sizeof(CERT_INFO);
+    DWORD size = sizeof(CERT_INFO), rdnSize = 0;
     BOOL ret;
 
-    size += in->Issuer.cbData;
-    size += in->SerialNumber.cbData;
+    TRACE("(%p, %d, %p)\n", pvData, pvData ? *pcbData : 0, in);
+
+    if (in->SignerId.dwIdChoice == CERT_ID_ISSUER_SERIAL_NUMBER)
+    {
+        size += in->SignerId.u.IssuerSerialNumber.Issuer.cbData;
+        size += in->SignerId.u.IssuerSerialNumber.SerialNumber.cbData;
+    }
+    else
+    {
+        rdnSize = CRYPT_SizeOfKeyIdAsIssuerAndSerial(&in->SignerId.u.KeyId);
+        size += rdnSize;
+    }
     if (!pvData)
     {
         *pcbData = size;
@@ -1769,13 +2261,22 @@ static BOOL CRYPT_CopySignerCertInfo(void *pvData, DWORD *pcbData,
     else
     {
         LPBYTE nextData = (BYTE *)pvData + sizeof(CERT_INFO);
-        CERT_INFO *out = (CERT_INFO *)pvData;
+        CERT_INFO *out = pvData;
 
         memset(out, 0, sizeof(CERT_INFO));
-        CRYPT_CopyBlob(&out->Issuer, &in->Issuer, &nextData);
-        CRYPT_CopyBlob(&out->SerialNumber, &in->SerialNumber, &nextData);
-        ret = TRUE;
+        if (in->SignerId.dwIdChoice == CERT_ID_ISSUER_SERIAL_NUMBER)
+        {
+            CRYPT_CopyBlob(&out->Issuer,
+             &in->SignerId.u.IssuerSerialNumber.Issuer, &nextData);
+            CRYPT_CopyBlob(&out->SerialNumber,
+             &in->SignerId.u.IssuerSerialNumber.SerialNumber, &nextData);
+            ret = TRUE;
+        }
+        else
+            ret = CRYPT_CopyKeyIdAsIssuerAndSerial(&out->Issuer, &out->SerialNumber,
+             &in->SignerId.u.KeyId, rdnSize, &nextData);
     }
+    TRACE("returning %d\n", ret);
     return ret;
 }
 
@@ -1801,7 +2302,7 @@ static BOOL CDecodeSignedMsg_GetParam(CDecodeMsg *msg, DWORD dwParamType,
                 ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_OCTET_STRING,
                  msg->u.signed_data.info->content.Content.pbData,
                  msg->u.signed_data.info->content.Content.cbData,
-                 CRYPT_DECODE_ALLOC_FLAG, NULL, (LPBYTE)&blob, &size);
+                 CRYPT_DECODE_ALLOC_FLAG, NULL, &blob, &size);
                 if (ret)
                 {
                     ret = CRYPT_CopyParam(pvData, pcbData, blob->pbData,
@@ -1896,6 +2397,19 @@ static BOOL CDecodeSignedMsg_GetParam(CDecodeMsg *msg, DWORD dwParamType,
         else
             SetLastError(CRYPT_E_INVALID_MSG_TYPE);
         break;
+    case CMSG_COMPUTED_HASH_PARAM:
+        if (msg->u.signed_data.info)
+        {
+            if (dwIndex >= msg->u.signed_data.cSignerHandle)
+                SetLastError(CRYPT_E_INVALID_INDEX);
+            else
+                ret = CryptGetHashParam(
+                 msg->u.signed_data.signerHandles[dwIndex].contentHash,
+                 HP_HASHVAL, pvData, pcbData, 0);
+        }
+        else
+            SetLastError(CRYPT_E_INVALID_MSG_TYPE);
+        break;
     case CMSG_ATTR_CERT_COUNT_PARAM:
         if (msg->u.signed_data.info)
         {
@@ -1913,6 +2427,18 @@ static BOOL CDecodeSignedMsg_GetParam(CDecodeMsg *msg, DWORD dwParamType,
         else
             SetLastError(CRYPT_E_INVALID_MSG_TYPE);
         break;
+    case CMSG_CMS_SIGNER_INFO_PARAM:
+        if (msg->u.signed_data.info)
+        {
+            if (dwIndex >= msg->u.signed_data.info->cSignerInfo)
+                SetLastError(CRYPT_E_INVALID_INDEX);
+            else
+                ret = CRYPT_CopyCMSSignerInfo(pvData, pcbData,
+                 &msg->u.signed_data.info->rgSignerInfo[dwIndex]);
+        }
+        else
+            SetLastError(CRYPT_E_INVALID_MSG_TYPE);
+        break;
     default:
         FIXME("unimplemented for %d\n", dwParamType);
         SetLastError(CRYPT_E_INVALID_MSG_TYPE);
@@ -1923,7 +2449,7 @@ static BOOL CDecodeSignedMsg_GetParam(CDecodeMsg *msg, DWORD dwParamType,
 static BOOL CDecodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
  DWORD dwIndex, void *pvData, DWORD *pcbData)
 {
-    CDecodeMsg *msg = (CDecodeMsg *)hCryptMsg;
+    CDecodeMsg *msg = hCryptMsg;
     BOOL ret = FALSE;
 
     switch (msg->type)
@@ -1982,11 +2508,135 @@ static BOOL CDecodeHashMsg_VerifyHash(CDecodeMsg *msg)
                 ret = CDecodeHashMsg_GetParam(msg, CMSG_COMPUTED_HASH_PARAM, 0,
                  computedHash, &computedHashSize);
                 if (ret)
-                    ret = !memcmp(hashBlob.pbData, computedHash,
-                     hashBlob.cbData);
+                {
+                    if (memcmp(hashBlob.pbData, computedHash, hashBlob.cbData))
+                    {
+                        SetLastError(CRYPT_E_HASH_VALUE);
+                        ret = FALSE;
+                    }
+                }
+                CryptMemFree(computedHash);
             }
             else
+            {
+                SetLastError(ERROR_OUTOFMEMORY);
                 ret = FALSE;
+            }
+        }
+        else
+        {
+            SetLastError(CRYPT_E_HASH_VALUE);
+            ret = FALSE;
+        }
+    }
+    return ret;
+}
+
+static BOOL CDecodeSignedMsg_VerifySignatureWithKey(CDecodeMsg *msg,
+ HCRYPTPROV prov, DWORD signerIndex, PCERT_PUBLIC_KEY_INFO keyInfo)
+{
+    HCRYPTKEY key;
+    BOOL ret;
+
+    if (!prov)
+        prov = msg->crypt_prov;
+    ret = CryptImportPublicKeyInfo(prov, X509_ASN_ENCODING, keyInfo, &key);
+    if (ret)
+    {
+        HCRYPTHASH hash;
+        CRYPT_HASH_BLOB reversedHash;
+
+        if (msg->u.signed_data.info->rgSignerInfo[signerIndex].AuthAttrs.cAttr)
+            hash = msg->u.signed_data.signerHandles[signerIndex].authAttrHash;
+        else
+            hash = msg->u.signed_data.signerHandles[signerIndex].contentHash;
+        ret = CRYPT_ConstructBlob(&reversedHash,
+         &msg->u.signed_data.info->rgSignerInfo[signerIndex].EncryptedHash);
+        if (ret)
+        {
+            CRYPT_ReverseBytes(&reversedHash);
+            ret = CryptVerifySignatureW(hash, reversedHash.pbData,
+             reversedHash.cbData, key, NULL, 0);
+            CryptMemFree(reversedHash.pbData);
+        }
+        CryptDestroyKey(key);
+    }
+    return ret;
+}
+
+static BOOL CDecodeSignedMsg_VerifySignature(CDecodeMsg *msg, PCERT_INFO info)
+{
+    BOOL ret = FALSE;
+    DWORD i;
+
+    if (!msg->u.signed_data.signerHandles)
+    {
+        SetLastError(NTE_BAD_SIGNATURE);
+        return FALSE;
+    }
+    for (i = 0; !ret && i < msg->u.signed_data.info->cSignerInfo; i++)
+    {
+        PCMSG_CMS_SIGNER_INFO signerInfo =
+         &msg->u.signed_data.info->rgSignerInfo[i];
+
+        if (signerInfo->SignerId.dwIdChoice == CERT_ID_ISSUER_SERIAL_NUMBER)
+        {
+            ret = CertCompareCertificateName(X509_ASN_ENCODING,
+             &signerInfo->SignerId.u.IssuerSerialNumber.Issuer,
+             &info->Issuer);
+            if (ret)
+            {
+                ret = CertCompareIntegerBlob(
+                 &signerInfo->SignerId.u.IssuerSerialNumber.SerialNumber,
+                 &info->SerialNumber);
+                if (ret)
+                    break;
+            }
+        }
+        else
+        {
+            FIXME("signer %d: unimplemented for key id\n", i);
+        }
+    }
+    if (ret)
+        ret = CDecodeSignedMsg_VerifySignatureWithKey(msg, 0, i,
+         &info->SubjectPublicKeyInfo);
+    else
+        SetLastError(CRYPT_E_SIGNER_NOT_FOUND);
+
+    return ret;
+}
+
+static BOOL CDecodeSignedMsg_VerifySignatureEx(CDecodeMsg *msg,
+ PCMSG_CTRL_VERIFY_SIGNATURE_EX_PARA para)
+{
+    BOOL ret = FALSE;
+
+    if (para->cbSize != sizeof(CMSG_CTRL_VERIFY_SIGNATURE_EX_PARA))
+        SetLastError(ERROR_INVALID_PARAMETER);
+    else if (para->dwSignerIndex >= msg->u.signed_data.info->cSignerInfo)
+        SetLastError(CRYPT_E_SIGNER_NOT_FOUND);
+    else if (!msg->u.signed_data.signerHandles)
+        SetLastError(NTE_BAD_SIGNATURE);
+    else
+    {
+        switch (para->dwSignerType)
+        {
+        case CMSG_VERIFY_SIGNER_PUBKEY:
+            ret = CDecodeSignedMsg_VerifySignatureWithKey(msg,
+             para->hCryptProv, para->dwSignerIndex, para->pvSigner);
+            break;
+        case CMSG_VERIFY_SIGNER_CERT:
+        {
+            PCCERT_CONTEXT cert = para->pvSigner;
+
+            ret = CDecodeSignedMsg_VerifySignatureWithKey(msg, para->hCryptProv,
+             para->dwSignerIndex, &cert->pCertInfo->SubjectPublicKeyInfo);
+            break;
+        }
+        default:
+            FIXME("unimplemented for signer type %d\n", para->dwSignerType);
+            SetLastError(CRYPT_E_SIGNER_NOT_FOUND);
         }
     }
     return ret;
@@ -1995,7 +2645,7 @@ static BOOL CDecodeHashMsg_VerifyHash(CDecodeMsg *msg)
 static BOOL CDecodeMsg_Control(HCRYPTMSG hCryptMsg, DWORD dwFlags,
  DWORD dwCtrlType, const void *pvCtrlPara)
 {
-    CDecodeMsg *msg = (CDecodeMsg *)hCryptMsg;
+    CDecodeMsg *msg = hCryptMsg;
     BOOL ret = FALSE;
 
     switch (dwCtrlType)
@@ -2004,7 +2654,7 @@ static BOOL CDecodeMsg_Control(HCRYPTMSG hCryptMsg, DWORD dwFlags,
         switch (msg->type)
         {
         case CMSG_SIGNED:
-            FIXME("CMSG_CTRL_VERIFY_SIGNATURE: stub\n");
+            ret = CDecodeSignedMsg_VerifySignature(msg, (PCERT_INFO)pvCtrlPara);
             break;
         default:
             SetLastError(CRYPT_E_INVALID_MSG_TYPE);
@@ -2027,6 +2677,17 @@ static BOOL CDecodeMsg_Control(HCRYPTMSG hCryptMsg, DWORD dwFlags,
             SetLastError(CRYPT_E_INVALID_MSG_TYPE);
         }
         break;
+    case CMSG_CTRL_VERIFY_SIGNATURE_EX:
+        switch (msg->type)
+        {
+        case CMSG_SIGNED:
+            ret = CDecodeSignedMsg_VerifySignatureEx(msg,
+             (PCMSG_CTRL_VERIFY_SIGNATURE_EX_PARA)pvCtrlPara);
+            break;
+        default:
+            SetLastError(CRYPT_E_INVALID_MSG_TYPE);
+        }
+        break;
     default:
         SetLastError(CRYPT_E_CONTROL_TYPE);
     }
@@ -2064,6 +2725,8 @@ HCRYPTMSG WINAPI CryptMsgOpenToDecode(DWORD dwMsgEncodingType, DWORD dwFlags,
         memset(&msg->u, 0, sizeof(msg->u));
         msg->msg_data.cbData = 0;
         msg->msg_data.pbData = NULL;
+        msg->detached_data.cbData = 0;
+        msg->detached_data.pbData = NULL;
         msg->properties = ContextPropertyList_Create();
     }
     return msg;
@@ -2075,7 +2738,7 @@ HCRYPTMSG WINAPI CryptMsgDuplicate(HCRYPTMSG hCryptMsg)
 
     if (hCryptMsg)
     {
-        CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
+        CryptMsgBase *msg = hCryptMsg;
 
         InterlockedIncrement(&msg->ref);
     }
@@ -2088,7 +2751,7 @@ BOOL WINAPI CryptMsgClose(HCRYPTMSG hCryptMsg)
 
     if (hCryptMsg)
     {
-        CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
+        CryptMsgBase *msg = hCryptMsg;
 
         if (InterlockedDecrement(&msg->ref) == 0)
         {
@@ -2104,27 +2767,17 @@ BOOL WINAPI CryptMsgClose(HCRYPTMSG hCryptMsg)
 BOOL WINAPI CryptMsgUpdate(HCRYPTMSG hCryptMsg, const BYTE *pbData,
  DWORD cbData, BOOL fFinal)
 {
-    CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
-    BOOL ret = FALSE;
+    CryptMsgBase *msg = hCryptMsg;
 
     TRACE("(%p, %p, %d, %d)\n", hCryptMsg, pbData, cbData, fFinal);
 
-    if (msg->state == MsgStateFinalized)
-        SetLastError(CRYPT_E_MSG_ERROR);
-    else
-    {
-        ret = msg->update(hCryptMsg, pbData, cbData, fFinal);
-        msg->state = MsgStateUpdated;
-        if (fFinal)
-            msg->state = MsgStateFinalized;
-    }
-    return ret;
+    return msg->update(hCryptMsg, pbData, cbData, fFinal);
 }
 
 BOOL WINAPI CryptMsgGetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
  DWORD dwIndex, void *pvData, DWORD *pcbData)
 {
-    CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
+    CryptMsgBase *msg = hCryptMsg;
 
     TRACE("(%p, %d, %d, %p, %p)\n", hCryptMsg, dwParamType, dwIndex,
      pvData, pcbData);
@@ -2134,9 +2787,193 @@ BOOL WINAPI CryptMsgGetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
 BOOL WINAPI CryptMsgControl(HCRYPTMSG hCryptMsg, DWORD dwFlags,
  DWORD dwCtrlType, const void *pvCtrlPara)
 {
-    CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
+    CryptMsgBase *msg = hCryptMsg;
 
     TRACE("(%p, %08x, %d, %p)\n", hCryptMsg, dwFlags, dwCtrlType,
      pvCtrlPara);
     return msg->control(hCryptMsg, dwFlags, dwCtrlType, pvCtrlPara);
 }
+
+static CERT_INFO *CRYPT_GetSignerCertInfoFromMsg(HCRYPTMSG msg,
+ DWORD dwSignerIndex)
+{
+    CERT_INFO *certInfo = NULL;
+    DWORD size;
+
+    if (CryptMsgGetParam(msg, CMSG_SIGNER_CERT_INFO_PARAM, dwSignerIndex, NULL,
+     &size))
+    {
+        certInfo = CryptMemAlloc(size);
+        if (certInfo)
+        {
+            if (!CryptMsgGetParam(msg, CMSG_SIGNER_CERT_INFO_PARAM,
+             dwSignerIndex, certInfo, &size))
+            {
+                CryptMemFree(certInfo);
+                certInfo = NULL;
+            }
+        }
+    }
+    return certInfo;
+}
+
+BOOL WINAPI CryptMsgGetAndVerifySigner(HCRYPTMSG hCryptMsg, DWORD cSignerStore,
+ HCERTSTORE *rghSignerStore, DWORD dwFlags, PCCERT_CONTEXT *ppSigner,
+ DWORD *pdwSignerIndex)
+{
+    HCERTSTORE store;
+    DWORD i, signerIndex = 0;
+    PCCERT_CONTEXT signerCert = NULL;
+    BOOL ret = FALSE;
+
+    TRACE("(%p, %d, %p, %08x, %p, %p)\n", hCryptMsg, cSignerStore,
+     rghSignerStore, dwFlags, ppSigner, pdwSignerIndex);
+
+    /* Clear output parameters */
+    if (ppSigner)
+        *ppSigner = NULL;
+    if (pdwSignerIndex && !(dwFlags & CMSG_USE_SIGNER_INDEX_FLAG))
+        *pdwSignerIndex = 0;
+
+    /* Create store to search for signer certificates */
+    store = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
+     CERT_STORE_CREATE_NEW_FLAG, NULL);
+    if (!(dwFlags & CMSG_TRUSTED_SIGNER_FLAG))
+    {
+        HCERTSTORE msgStore = CertOpenStore(CERT_STORE_PROV_MSG, 0, 0, 0,
+         hCryptMsg);
+
+        CertAddStoreToCollection(store, msgStore, 0, 0);
+        CertCloseStore(msgStore, 0);
+    }
+    for (i = 0; i < cSignerStore; i++)
+        CertAddStoreToCollection(store, rghSignerStore[i], 0, 0);
+
+    /* Find signer cert */
+    if (dwFlags & CMSG_USE_SIGNER_INDEX_FLAG)
+    {
+        CERT_INFO *signer = CRYPT_GetSignerCertInfoFromMsg(hCryptMsg,
+         *pdwSignerIndex);
+
+        if (signer)
+        {
+            signerIndex = *pdwSignerIndex;
+            signerCert = CertFindCertificateInStore(store, X509_ASN_ENCODING,
+             0, CERT_FIND_SUBJECT_CERT, signer, NULL);
+            CryptMemFree(signer);
+        }
+    }
+    else
+    {
+        DWORD count, size = sizeof(count);
+
+        if (CryptMsgGetParam(hCryptMsg, CMSG_SIGNER_COUNT_PARAM, 0, &count,
+         &size))
+        {
+            for (i = 0; !signerCert && i < count; i++)
+            {
+                CERT_INFO *signer = CRYPT_GetSignerCertInfoFromMsg(hCryptMsg,
+                 i);
+
+                if (signer)
+                {
+                    signerCert = CertFindCertificateInStore(store,
+                     X509_ASN_ENCODING, 0, CERT_FIND_SUBJECT_CERT, signer,
+                     NULL);
+                    if (signerCert)
+                        signerIndex = i;
+                    CryptMemFree(signer);
+                }
+            }
+        }
+        if (!signerCert)
+            SetLastError(CRYPT_E_NO_TRUSTED_SIGNER);
+    }
+    if (signerCert)
+    {
+        if (!(dwFlags & CMSG_SIGNER_ONLY_FLAG))
+            ret = CryptMsgControl(hCryptMsg, 0, CMSG_CTRL_VERIFY_SIGNATURE,
+             signerCert->pCertInfo);
+        else
+            ret = TRUE;
+        if (ret)
+        {
+            if (ppSigner)
+                *ppSigner = CertDuplicateCertificateContext(signerCert);
+            if (pdwSignerIndex)
+                *pdwSignerIndex = signerIndex;
+        }
+        CertFreeCertificateContext(signerCert);
+    }
+
+    CertCloseStore(store, 0);
+    return ret;
+}
+
+BOOL WINAPI CryptMsgVerifyCountersignatureEncodedEx(HCRYPTPROV_LEGACY hCryptProv,
+ DWORD dwEncodingType, PBYTE pbSignerInfo, DWORD cbSignerInfo,
+ PBYTE pbSignerInfoCountersignature, DWORD cbSignerInfoCountersignature,
+ DWORD dwSignerType, void *pvSigner, DWORD dwFlags, void *pvReserved)
+{
+    FIXME("(%08lx, %08x, %p, %d, %p, %d, %d, %p, %08x, %p): stub\n", hCryptProv,
+     dwEncodingType, pbSignerInfo, cbSignerInfo, pbSignerInfoCountersignature,
+     cbSignerInfoCountersignature, dwSignerType, pvSigner, dwFlags, pvReserved);
+    return FALSE;
+}
+
+BOOL WINAPI CryptMsgEncodeAndSignCTL(DWORD dwMsgEncodingType,
+ PCTL_INFO pCtlInfo, PCMSG_SIGNED_ENCODE_INFO pSignInfo, DWORD dwFlags,
+ BYTE *pbEncoded, DWORD *pcbEncoded)
+{
+    BOOL ret;
+    BYTE *pbCtlContent;
+    DWORD cbCtlContent;
+
+    TRACE("(%08x, %p, %p, %08x, %p, %p)\n", dwMsgEncodingType, pCtlInfo,
+     pSignInfo, dwFlags, pbEncoded, pcbEncoded);
+
+    if (dwFlags)
+    {
+        FIXME("unimplemented for flags %08x\n", dwFlags);
+        return FALSE;
+    }
+    if ((ret = CryptEncodeObjectEx(dwMsgEncodingType, PKCS_CTL, pCtlInfo,
+     CRYPT_ENCODE_ALLOC_FLAG, NULL, &pbCtlContent, &cbCtlContent)))
+    {
+        ret = CryptMsgSignCTL(dwMsgEncodingType, pbCtlContent, cbCtlContent,
+         pSignInfo, dwFlags, pbEncoded, pcbEncoded);
+        LocalFree(pbCtlContent);
+    }
+    return ret;
+}
+
+BOOL WINAPI CryptMsgSignCTL(DWORD dwMsgEncodingType, BYTE *pbCtlContent,
+ DWORD cbCtlContent, PCMSG_SIGNED_ENCODE_INFO pSignInfo, DWORD dwFlags,
+ BYTE *pbEncoded, DWORD *pcbEncoded)
+{
+    static char oid_ctl[] = szOID_CTL;
+    BOOL ret;
+    HCRYPTMSG msg;
+
+    TRACE("(%08x, %p, %d, %p, %08x, %p, %p)\n", dwMsgEncodingType,
+     pbCtlContent, cbCtlContent, pSignInfo, dwFlags, pbEncoded, pcbEncoded);
+
+    if (dwFlags)
+    {
+        FIXME("unimplemented for flags %08x\n", dwFlags);
+        return FALSE;
+    }
+    msg = CryptMsgOpenToEncode(dwMsgEncodingType, 0, CMSG_SIGNED, pSignInfo,
+     oid_ctl, NULL);
+    if (msg)
+    {
+        ret = CryptMsgUpdate(msg, pbCtlContent, cbCtlContent, TRUE);
+        if (ret)
+            ret = CryptMsgGetParam(msg, CMSG_CONTENT_PARAM, 0, pbEncoded,
+             pcbEncoded);
+        CryptMsgClose(msg);
+    }
+    else
+        ret = FALSE;
+    return ret;
+}