wininet/tests: Skip tests if functions are not implemented.
[wine] / dlls / crypt32 / msg.c
index be12218..a16eef1 100644 (file)
@@ -20,6 +20,7 @@
 #include "wine/port.h"
 
 #include <stdarg.h>
+#define NONAMELESSUNION
 #include "windef.h"
 #include "winbase.h"
 #include "wincrypt.h"
@@ -45,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);
@@ -108,7 +109,7 @@ 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);
@@ -190,7 +191,7 @@ 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.state == MsgStateFinalized)
@@ -319,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)
@@ -371,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
@@ -384,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);
@@ -443,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,
@@ -486,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)
@@ -511,7 +511,7 @@ 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);
@@ -554,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;
 
@@ -591,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
@@ -662,19 +661,19 @@ static BOOL CRYPT_IsValidSigner(CMSG_SIGNER_ENCODE_INFO_WITH_CMS *signer)
             }
             break;
         case CERT_ID_ISSUER_SERIAL_NUMBER:
-            if (!signer->SignerId.IssuerSerialNumber.SerialNumber.cbData)
+            if (!signer->SignerId.u.IssuerSerialNumber.SerialNumber.cbData)
             {
                 SetLastError(E_INVALIDARG);
                 return FALSE;
             }
-            if (!signer->SignerId.IssuerSerialNumber.Issuer.cbData)
+            if (!signer->SignerId.u.IssuerSerialNumber.Issuer.cbData)
             {
                 SetLastError(E_INVALIDARG);
                 return FALSE;
             }
             break;
         case CERT_ID_KEY_IDENTIFIER:
-            if (!signer->SignerId.KeyId.cbData)
+            if (!signer->SignerId.u.KeyId.cbData)
             {
                 SetLastError(E_INVALIDARG);
                 return FALSE;
@@ -808,11 +807,11 @@ static BOOL CSignerInfo_Construct(CMSG_CMS_SIGNER_INFO *info,
     if (in->cbSize == sizeof(CMSG_SIGNER_ENCODE_INFO))
     {
         info->dwVersion = CMSG_SIGNER_INFO_V1;
-        ret = CRYPT_ConstructBlob(&info->SignerId.IssuerSerialNumber.Issuer,
+        ret = CRYPT_ConstructBlob(&info->SignerId.u.IssuerSerialNumber.Issuer,
          &in->pCertInfo->Issuer);
         if (ret)
             ret = CRYPT_ConstructBlob(
-             &info->SignerId.IssuerSerialNumber.SerialNumber,
+             &info->SignerId.u.IssuerSerialNumber.SerialNumber,
              &in->pCertInfo->SerialNumber);
         info->SignerId.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
     }
@@ -824,11 +823,11 @@ static BOOL CSignerInfo_Construct(CMSG_CMS_SIGNER_INFO *info,
         if (!in->SignerId.dwIdChoice)
         {
             info->dwVersion = CMSG_SIGNER_INFO_V1;
-            ret = CRYPT_ConstructBlob(&info->SignerId.IssuerSerialNumber.Issuer,
+            ret = CRYPT_ConstructBlob(&info->SignerId.u.IssuerSerialNumber.Issuer,
              &in->pCertInfo->Issuer);
             if (ret)
                 ret = CRYPT_ConstructBlob(
-                 &info->SignerId.IssuerSerialNumber.SerialNumber,
+                 &info->SignerId.u.IssuerSerialNumber.SerialNumber,
                  &in->pCertInfo->SerialNumber);
             info->SignerId.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
         }
@@ -836,20 +835,20 @@ static BOOL CSignerInfo_Construct(CMSG_CMS_SIGNER_INFO *info,
         {
             info->dwVersion = CMSG_SIGNER_INFO_V1;
             info->SignerId.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
-            ret = CRYPT_ConstructBlob(&info->SignerId.IssuerSerialNumber.Issuer,
-             &in->SignerId.IssuerSerialNumber.Issuer);
+            ret = CRYPT_ConstructBlob(&info->SignerId.u.IssuerSerialNumber.Issuer,
+             &in->SignerId.u.IssuerSerialNumber.Issuer);
             if (ret)
                 ret = CRYPT_ConstructBlob(
-                 &info->SignerId.IssuerSerialNumber.SerialNumber,
-                 &in->SignerId.IssuerSerialNumber.SerialNumber);
+                 &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.KeyId,
-             &in->SignerId.KeyId);
+            ret = CRYPT_ConstructBlob(&info->SignerId.u.KeyId,
+             &in->SignerId.u.KeyId);
         }
     }
     /* Assumption:  algorithm IDs will point to static strings, not
@@ -876,11 +875,11 @@ static void CSignerInfo_Free(CMSG_CMS_SIGNER_INFO *info)
 
     if (info->SignerId.dwIdChoice == CERT_ID_ISSUER_SERIAL_NUMBER)
     {
-        CryptMemFree(info->SignerId.IssuerSerialNumber.Issuer.pbData);
-        CryptMemFree(info->SignerId.IssuerSerialNumber.SerialNumber.pbData);
+        CryptMemFree(info->SignerId.u.IssuerSerialNumber.Issuer.pbData);
+        CryptMemFree(info->SignerId.u.IssuerSerialNumber.SerialNumber.pbData);
     }
     else
-        CryptMemFree(info->SignerId.KeyId.pbData);
+        CryptMemFree(info->SignerId.u.KeyId.pbData);
     CryptMemFree(info->HashAlgorithm.Parameters.pbData);
     CryptMemFree(info->EncryptedHash.pbData);
     for (i = 0; i < info->AuthAttrs.cAttr; i++)
@@ -1085,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(
@@ -1168,15 +1167,17 @@ static BOOL CSignedMsgData_Update(CSignedMsgData *msg_data,
 typedef struct _CSignedEncodeMsg
 {
     CryptMsgBase    base;
+    LPSTR           innerOID;
     CRYPT_DATA_BLOB data;
     CSignedMsgData  msg_data;
 } CSignedEncodeMsg;
 
 static void CSignedEncodeMsg_Close(HCRYPTMSG hCryptMsg)
 {
-    CSignedEncodeMsg *msg = (CSignedEncodeMsg *)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);
@@ -1190,7 +1191,7 @@ static void CSignedEncodeMsg_Close(HCRYPTMSG hCryptMsg)
 static BOOL CSignedEncodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
  DWORD dwIndex, void *pvData, DWORD *pcbData)
 {
-    CSignedEncodeMsg *msg = (CSignedEncodeMsg *)hCryptMsg;
+    CSignedEncodeMsg *msg = hCryptMsg;
     BOOL ret = FALSE;
 
     switch (dwParamType)
@@ -1226,32 +1227,46 @@ static BOOL CSignedEncodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
     case CMSG_BARE_CONTENT_PARAM:
     {
         CRYPT_SIGNED_INFO info;
-        char oid_rsa_data[] = szOID_RSA_data;
+        BOOL freeContent = FALSE;
 
         info = *msg->msg_data.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)
+        if (!msg->innerOID || !strcmp(msg->innerOID, szOID_RSA_data))
         {
-            CRYPT_DATA_BLOB blob = { msg->data.cbData, msg->data.pbData };
+            char oid_rsa_data[] = szOID_RSA_data;
 
-            ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_OCTET_STRING,
-             &blob, CRYPT_ENCODE_ALLOC_FLAG, NULL,
-             &info.content.Content.pbData, &info.content.Content.cbData);
+            /* 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.Content.cbData = 0;
-            info.content.Content.pbData = NULL;
+            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);
-            LocalFree(info.content.Content.pbData);
+            if (freeContent)
+                LocalFree(info.content.Content.pbData);
         }
         break;
     }
@@ -1284,7 +1299,7 @@ static BOOL CSignedEncodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
 static BOOL CSignedEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
  DWORD cbData, BOOL fFinal)
 {
-    CSignedEncodeMsg *msg = (CSignedEncodeMsg *)hCryptMsg;
+    CSignedEncodeMsg *msg = hCryptMsg;
     BOOL ret = FALSE;
 
     if (msg->base.state == MsgStateFinalized)
@@ -1328,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;
 
@@ -1340,7 +1354,7 @@ static HCRYPTMSG CSignedEncodeMsg_Open(DWORD dwFlags,
         return NULL;
     }
     if (info->cbSize == sizeof(CMSG_SIGNED_ENCODE_INFO_WITH_CMS) &&
-     info->rgAttrCertEncoded)
+     info->cAttrCertEncoded)
     {
         FIXME("CMSG_SIGNED_ENCODE_INFO with CMS fields unsupported\n");
         return NULL;
@@ -1356,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));
@@ -1424,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)
@@ -1470,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:
@@ -1491,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);
@@ -1515,6 +1526,7 @@ static void CDecodeMsg_Close(HCRYPTMSG hCryptMsg)
         break;
     }
     CryptMemFree(msg->msg_data.pbData);
+    CryptMemFree(msg->detached_data.pbData);
     ContextPropertyList_Free(msg->properties);
 }
 
@@ -1548,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,
@@ -1622,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);
@@ -1646,48 +1660,10 @@ static BOOL CDecodeMsg_DecodeSignedContent(CDecodeMsg *msg,
      CRYPT_DECODE_ALLOC_FLAG, NULL, (CRYPT_SIGNED_INFO *)&signedInfo,
      &size);
     if (ret)
-    {
-        DWORD i;
-
         msg->u.signed_data.info = signedInfo;
-        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)
-        {
-            /* Now that we have all the content, update the hash handles with
-             * it.  Have to decode it if the type is szOID_RSA_data.
-             */
-            if (msg->u.signed_data.info->content.Content.cbData)
-            {
-                if (!strcmp(msg->u.signed_data.info->content.pszObjId,
-                 szOID_RSA_data))
-                {
-                    CRYPT_DATA_BLOB *blob;
-
-                    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);
-                    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,
-                     msg->u.signed_data.info->content.Content.pbData,
-                     msg->u.signed_data.info->content.Content.cbData, TRUE,
-                     Verify);
-            }
-        }
-    }
     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
@@ -1709,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:
@@ -1723,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))
@@ -1749,10 +1725,117 @@ 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);
@@ -1763,30 +1846,38 @@ static BOOL CDecodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
     {
         FIXME("(%p, %p, %d, %d): streamed update stub\n", hCryptMsg, pbData,
          cbData, fFinal);
-        if (fFinal)
+        switch (msg->base.state)
         {
-            if (msg->base.open_flags & CMSG_DETACHED_FLAG &&
-             msg->base.state != MsgStateDataFinalized)
+        case MsgStateInit:
+            ret = CDecodeMsg_CopyData(&msg->msg_data, pbData, cbData);
+            if (fFinal)
             {
-                ret = CDecodeMsg_CopyData(&msg->msg_data, pbData, cbData);
-                msg->base.state = MsgStateDataFinalized;
-                if (ret)
-                    ret = CDecodeMsg_DecodeContent(msg, &msg->msg_data,
-                     msg->type);
+                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)
             {
-                FIXME("(%p, %p, %d, %d): detached update stub\n", hCryptMsg,
-                 pbData, cbData, fFinal);
-                ret = TRUE;
-                msg->base.state = MsgStateFinalized;
+                if (msg->base.open_flags & CMSG_DETACHED_FLAG)
+                    msg->base.state = MsgStateDataFinalized;
+                else
+                    msg->base.state = MsgStateFinalized;
             }
-        }
-        else
-        {
-            ret = CDecodeMsg_CopyData(&msg->msg_data, pbData, cbData);
-            if (msg->base.state == MsgStateInit)
-                msg->base.state = MsgStateUpdated;
+            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
@@ -1795,26 +1886,32 @@ static BOOL CDecodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
             SetLastError(CRYPT_E_MSG_ERROR);
         else
         {
-            if (msg->base.state == MsgStateInit)
+            switch (msg->base.state)
             {
+            case MsgStateInit:
                 ret = CDecodeMsg_CopyData(&msg->msg_data, pbData, cbData);
-                if (ret)
-                    ret = CDecodeMsg_DecodeContent(msg, &msg->msg_data,
-                     msg->type);
                 if (msg->base.open_flags & CMSG_DETACHED_FLAG)
                     msg->base.state = MsgStateDataFinalized;
                 else
                     msg->base.state = MsgStateFinalized;
-            }
-            else if (msg->base.state == MsgStateDataFinalized)
-            {
-                FIXME("(%p, %p, %d, %d): detached update stub\n", hCryptMsg,
-                 pbData, cbData, fFinal);
-                ret = TRUE;
+                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;
 }
 
@@ -1838,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:
     {
@@ -1927,8 +1995,7 @@ static inline void CRYPT_CopyAttributes(CRYPT_ATTRIBUTES *out,
     {
         DWORD i;
 
-        if ((*nextData - (LPBYTE)0) % sizeof(DWORD_PTR))
-            *nextData += (*nextData - (LPBYTE)0) % sizeof(DWORD_PTR);
+        *nextData = POINTER_ALIGN_DWORD_PTR(*nextData);
         out->rgAttr = (CRYPT_ATTRIBUTE *)*nextData;
         *nextData += in->cAttr * sizeof(CRYPT_ATTRIBUTE);
         for (i = 0; i < in->cAttr; i++)
@@ -1944,8 +2011,7 @@ static inline void CRYPT_CopyAttributes(CRYPT_ATTRIBUTES *out,
                 DWORD j;
 
                 out->rgAttr[i].cValue = in->rgAttr[i].cValue;
-                if ((*nextData - (LPBYTE)0) % sizeof(DWORD_PTR))
-                    *nextData += (*nextData - (LPBYTE)0) % sizeof(DWORD_PTR);
+                *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++)
@@ -1965,15 +2031,13 @@ 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_PTR))
-            size += size % sizeof(DWORD_PTR);
+        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 */
-    if (size % sizeof(DWORD_PTR))
-        size += size % sizeof(DWORD_PTR);
+    size = ALIGN_DWORD_PTR(size);
     return size;
 }
 
@@ -2028,19 +2092,19 @@ static BOOL CRYPT_CopyKeyIdAsIssuerAndSerial(CERT_NAME_BLOB *issuer,
 static BOOL CRYPT_CopySignerInfo(void *pvData, DWORD *pcbData,
  const CMSG_CMS_SIGNER_INFO *in)
 {
-    DWORD size = sizeof(CMSG_SIGNER_INFO), rdnSize;
+    DWORD size = sizeof(CMSG_SIGNER_INFO), rdnSize = 0;
     BOOL ret;
 
     TRACE("(%p, %d, %p)\n", pvData, pvData ? *pcbData : 0, in);
 
     if (in->SignerId.dwIdChoice == CERT_ID_ISSUER_SERIAL_NUMBER)
     {
-        size += in->SignerId.IssuerSerialNumber.Issuer.cbData;
-        size += in->SignerId.IssuerSerialNumber.SerialNumber.cbData;
+        size += in->SignerId.u.IssuerSerialNumber.Issuer.cbData;
+        size += in->SignerId.u.IssuerSerialNumber.SerialNumber.cbData;
     }
     else
     {
-        rdnSize = CRYPT_SizeOfKeyIdAsIssuerAndSerial(&in->SignerId.KeyId);
+        rdnSize = CRYPT_SizeOfKeyIdAsIssuerAndSerial(&in->SignerId.u.KeyId);
         size += rdnSize;
     }
     if (in->HashAlgorithm.pszObjId)
@@ -2051,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_PTR))
-        size += size % sizeof(DWORD_PTR);
+    size = ALIGN_DWORD_PTR(size);
     size += CRYPT_SizeOfAttributes(&in->AuthAttrs);
     size += CRYPT_SizeOfAttributes(&in->UnauthAttrs);
     if (!pvData)
@@ -2069,20 +2132,20 @@ 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;
         if (in->SignerId.dwIdChoice == CERT_ID_ISSUER_SERIAL_NUMBER)
         {
             CRYPT_CopyBlob(&out->Issuer,
-             &in->SignerId.IssuerSerialNumber.Issuer, &nextData);
+             &in->SignerId.u.IssuerSerialNumber.Issuer, &nextData);
             CRYPT_CopyBlob(&out->SerialNumber,
-             &in->SignerId.IssuerSerialNumber.SerialNumber, &nextData);
+             &in->SignerId.u.IssuerSerialNumber.SerialNumber, &nextData);
         }
         else
             ret = CRYPT_CopyKeyIdAsIssuerAndSerial(&out->Issuer, &out->SerialNumber,
-             &in->SignerId.KeyId, rdnSize, &nextData);
+             &in->SignerId.u.KeyId, rdnSize, &nextData);
         if (ret)
         {
             CRYPT_CopyAlgorithmId(&out->HashAlgorithm, &in->HashAlgorithm,
@@ -2090,9 +2153,7 @@ static BOOL CRYPT_CopySignerInfo(void *pvData, DWORD *pcbData,
             CRYPT_CopyAlgorithmId(&out->HashEncryptionAlgorithm,
              &in->HashEncryptionAlgorithm, &nextData);
             CRYPT_CopyBlob(&out->EncryptedHash, &in->EncryptedHash, &nextData);
-            /* align pointer */
-            if ((nextData - (LPBYTE)0) % sizeof(DWORD_PTR))
-                nextData += (nextData - (LPBYTE)0) % sizeof(DWORD_PTR);
+            nextData = POINTER_ALIGN_DWORD_PTR(nextData);
             CRYPT_CopyAttributes(&out->AuthAttrs, &in->AuthAttrs, &nextData);
             CRYPT_CopyAttributes(&out->UnauthAttrs, &in->UnauthAttrs, &nextData);
         }
@@ -2111,11 +2172,11 @@ static BOOL CRYPT_CopyCMSSignerInfo(void *pvData, DWORD *pcbData,
 
     if (in->SignerId.dwIdChoice == CERT_ID_ISSUER_SERIAL_NUMBER)
     {
-        size += in->SignerId.IssuerSerialNumber.Issuer.cbData;
-        size += in->SignerId.IssuerSerialNumber.SerialNumber.cbData;
+        size += in->SignerId.u.IssuerSerialNumber.Issuer.cbData;
+        size += in->SignerId.u.IssuerSerialNumber.SerialNumber.cbData;
     }
     else
-        size += in->SignerId.KeyId.cbData;
+        size += in->SignerId.u.KeyId.cbData;
     if (in->HashAlgorithm.pszObjId)
         size += strlen(in->HashAlgorithm.pszObjId) + 1;
     size += in->HashAlgorithm.Parameters.cbData;
@@ -2124,8 +2185,7 @@ static BOOL CRYPT_CopyCMSSignerInfo(void *pvData, DWORD *pcbData,
     size += in->HashEncryptionAlgorithm.Parameters.cbData;
     size += in->EncryptedHash.cbData;
     /* align pointer */
-    if (size % sizeof(DWORD_PTR))
-        size += size % sizeof(DWORD_PTR);
+    size = ALIGN_DWORD_PTR(size);
     size += CRYPT_SizeOfAttributes(&in->AuthAttrs);
     size += CRYPT_SizeOfAttributes(&in->UnauthAttrs);
     if (!pvData)
@@ -2142,27 +2202,25 @@ static BOOL CRYPT_CopyCMSSignerInfo(void *pvData, DWORD *pcbData,
     else
     {
         LPBYTE nextData = (BYTE *)pvData + sizeof(CMSG_CMS_SIGNER_INFO);
-        CMSG_CMS_SIGNER_INFO *out = (CMSG_CMS_SIGNER_INFO *)pvData;
+        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.IssuerSerialNumber.Issuer,
-             &in->SignerId.IssuerSerialNumber.Issuer, &nextData);
-            CRYPT_CopyBlob(&out->SignerId.IssuerSerialNumber.SerialNumber,
-             &in->SignerId.IssuerSerialNumber.SerialNumber, &nextData);
+            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.KeyId, &in->SignerId.KeyId, &nextData);
+            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_PTR))
-            nextData += (nextData - (LPBYTE)0) % sizeof(DWORD_PTR);
+        nextData = POINTER_ALIGN_DWORD_PTR(nextData);
         CRYPT_CopyAttributes(&out->AuthAttrs, &in->AuthAttrs, &nextData);
         CRYPT_CopyAttributes(&out->UnauthAttrs, &in->UnauthAttrs, &nextData);
         ret = TRUE;
@@ -2174,19 +2232,19 @@ static BOOL CRYPT_CopyCMSSignerInfo(void *pvData, DWORD *pcbData,
 static BOOL CRYPT_CopySignerCertInfo(void *pvData, DWORD *pcbData,
  const CMSG_CMS_SIGNER_INFO *in)
 {
-    DWORD size = sizeof(CERT_INFO), rdnSize;
+    DWORD size = sizeof(CERT_INFO), rdnSize = 0;
     BOOL ret;
 
     TRACE("(%p, %d, %p)\n", pvData, pvData ? *pcbData : 0, in);
 
     if (in->SignerId.dwIdChoice == CERT_ID_ISSUER_SERIAL_NUMBER)
     {
-        size += in->SignerId.IssuerSerialNumber.Issuer.cbData;
-        size += in->SignerId.IssuerSerialNumber.SerialNumber.cbData;
+        size += in->SignerId.u.IssuerSerialNumber.Issuer.cbData;
+        size += in->SignerId.u.IssuerSerialNumber.SerialNumber.cbData;
     }
     else
     {
-        rdnSize = CRYPT_SizeOfKeyIdAsIssuerAndSerial(&in->SignerId.KeyId);
+        rdnSize = CRYPT_SizeOfKeyIdAsIssuerAndSerial(&in->SignerId.u.KeyId);
         size += rdnSize;
     }
     if (!pvData)
@@ -2203,20 +2261,20 @@ 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));
         if (in->SignerId.dwIdChoice == CERT_ID_ISSUER_SERIAL_NUMBER)
         {
             CRYPT_CopyBlob(&out->Issuer,
-             &in->SignerId.IssuerSerialNumber.Issuer, &nextData);
+             &in->SignerId.u.IssuerSerialNumber.Issuer, &nextData);
             CRYPT_CopyBlob(&out->SerialNumber,
-             &in->SignerId.IssuerSerialNumber.SerialNumber, &nextData);
+             &in->SignerId.u.IssuerSerialNumber.SerialNumber, &nextData);
             ret = TRUE;
         }
         else
             ret = CRYPT_CopyKeyIdAsIssuerAndSerial(&out->Issuer, &out->SerialNumber,
-             &in->SignerId.KeyId, rdnSize, &nextData);
+             &in->SignerId.u.KeyId, rdnSize, &nextData);
     }
     TRACE("returning %d\n", ret);
     return ret;
@@ -2244,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,
@@ -2391,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)
@@ -2450,12 +2508,25 @@ 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;
@@ -2498,6 +2569,11 @@ 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 =
@@ -2506,12 +2582,12 @@ static BOOL CDecodeSignedMsg_VerifySignature(CDecodeMsg *msg, PCERT_INFO info)
         if (signerInfo->SignerId.dwIdChoice == CERT_ID_ISSUER_SERIAL_NUMBER)
         {
             ret = CertCompareCertificateName(X509_ASN_ENCODING,
-             &signerInfo->SignerId.IssuerSerialNumber.Issuer,
+             &signerInfo->SignerId.u.IssuerSerialNumber.Issuer,
              &info->Issuer);
             if (ret)
             {
                 ret = CertCompareIntegerBlob(
-                 &signerInfo->SignerId.IssuerSerialNumber.SerialNumber,
+                 &signerInfo->SignerId.u.IssuerSerialNumber.SerialNumber,
                  &info->SerialNumber);
                 if (ret)
                     break;
@@ -2540,18 +2616,19 @@ static BOOL CDecodeSignedMsg_VerifySignatureEx(CDecodeMsg *msg,
         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,
-             (PCERT_PUBLIC_KEY_INFO)para->pvSigner);
+             para->hCryptProv, para->dwSignerIndex, para->pvSigner);
             break;
         case CMSG_VERIFY_SIGNER_CERT:
         {
-            PCCERT_CONTEXT cert = (PCCERT_CONTEXT)para->pvSigner;
+            PCCERT_CONTEXT cert = para->pvSigner;
 
             ret = CDecodeSignedMsg_VerifySignatureWithKey(msg, para->hCryptProv,
              para->dwSignerIndex, &cert->pCertInfo->SubjectPublicKeyInfo);
@@ -2568,7 +2645,7 @@ static BOOL CDecodeSignedMsg_VerifySignatureEx(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)
@@ -2648,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;
@@ -2659,7 +2738,7 @@ HCRYPTMSG WINAPI CryptMsgDuplicate(HCRYPTMSG hCryptMsg)
 
     if (hCryptMsg)
     {
-        CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
+        CryptMsgBase *msg = hCryptMsg;
 
         InterlockedIncrement(&msg->ref);
     }
@@ -2672,7 +2751,7 @@ BOOL WINAPI CryptMsgClose(HCRYPTMSG hCryptMsg)
 
     if (hCryptMsg)
     {
-        CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
+        CryptMsgBase *msg = hCryptMsg;
 
         if (InterlockedDecrement(&msg->ref) == 0)
         {
@@ -2688,7 +2767,7 @@ BOOL WINAPI CryptMsgClose(HCRYPTMSG hCryptMsg)
 BOOL WINAPI CryptMsgUpdate(HCRYPTMSG hCryptMsg, const BYTE *pbData,
  DWORD cbData, BOOL fFinal)
 {
-    CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
+    CryptMsgBase *msg = hCryptMsg;
 
     TRACE("(%p, %p, %d, %d)\n", hCryptMsg, pbData, cbData, fFinal);
 
@@ -2698,7 +2777,7 @@ BOOL WINAPI CryptMsgUpdate(HCRYPTMSG hCryptMsg, const BYTE *pbData,
 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);
@@ -2708,7 +2787,7 @@ 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);
@@ -2743,7 +2822,7 @@ BOOL WINAPI CryptMsgGetAndVerifySigner(HCRYPTMSG hCryptMsg, DWORD cSignerStore,
  DWORD *pdwSignerIndex)
 {
     HCERTSTORE store;
-    DWORD i, signerIndex;
+    DWORD i, signerIndex = 0;
     PCCERT_CONTEXT signerCert = NULL;
     BOOL ret = FALSE;
 
@@ -2841,3 +2920,60 @@ BOOL WINAPI CryptMsgVerifyCountersignatureEncodedEx(HCRYPTPROV_LEGACY hCryptProv
      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;
+}