crypt32: Store decoded signed content in message.
[wine] / dlls / crypt32 / msg.c
1 /*
2  * Copyright 2007 Juan Lang
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18 #include <stdarg.h>
19 #include "windef.h"
20 #include "winbase.h"
21 #include "wincrypt.h"
22 #include "snmp.h"
23
24 #include "wine/debug.h"
25 #include "wine/exception.h"
26 #include "crypt32_private.h"
27
28 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
29
30 /* Called when a message's ref count reaches zero.  Free any message-specific
31  * data here.
32  */
33 typedef void (*CryptMsgCloseFunc)(HCRYPTMSG msg);
34
35 typedef BOOL (*CryptMsgGetParamFunc)(HCRYPTMSG hCryptMsg, DWORD dwParamType,
36  DWORD dwIndex, void *pvData, DWORD *pcbData);
37
38 typedef BOOL (*CryptMsgUpdateFunc)(HCRYPTMSG hCryptMsg, const BYTE *pbData,
39  DWORD cbData, BOOL fFinal);
40
41 typedef enum _CryptMsgState {
42     MsgStateInit,
43     MsgStateUpdated,
44     MsgStateFinalized
45 } CryptMsgState;
46
47 typedef struct _CryptMsgBase
48 {
49     LONG                 ref;
50     DWORD                open_flags;
51     BOOL                 streamed;
52     CMSG_STREAM_INFO     stream_info;
53     CryptMsgState        state;
54     CryptMsgCloseFunc    close;
55     CryptMsgUpdateFunc   update;
56     CryptMsgGetParamFunc get_param;
57 } CryptMsgBase;
58
59 static inline void CryptMsgBase_Init(CryptMsgBase *msg, DWORD dwFlags,
60  PCMSG_STREAM_INFO pStreamInfo, CryptMsgCloseFunc close,
61  CryptMsgGetParamFunc get_param, CryptMsgUpdateFunc update)
62 {
63     msg->ref = 1;
64     msg->open_flags = dwFlags;
65     if (pStreamInfo)
66     {
67         msg->streamed = TRUE;
68         memcpy(&msg->stream_info, pStreamInfo, sizeof(msg->stream_info));
69     }
70     else
71     {
72         msg->streamed = FALSE;
73         memset(&msg->stream_info, 0, sizeof(msg->stream_info));
74     }
75     msg->close = close;
76     msg->get_param = get_param;
77     msg->update = update;
78     msg->state = MsgStateInit;
79 }
80
81 typedef struct _CDataEncodeMsg
82 {
83     CryptMsgBase base;
84     DWORD        bare_content_len;
85     LPBYTE       bare_content;
86 } CDataEncodeMsg;
87
88 static const BYTE empty_data_content[] = { 0x04,0x00 };
89
90 static void CDataEncodeMsg_Close(HCRYPTMSG hCryptMsg)
91 {
92     CDataEncodeMsg *msg = (CDataEncodeMsg *)hCryptMsg;
93
94     if (msg->bare_content != empty_data_content)
95         LocalFree(msg->bare_content);
96 }
97
98 static WINAPI BOOL CRYPT_EncodeContentLength(DWORD dwCertEncodingType,
99  LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
100  PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
101 {
102     const CDataEncodeMsg *msg = (const CDataEncodeMsg *)pvStructInfo;
103     DWORD lenBytes;
104     BOOL ret = TRUE;
105
106     /* Trick:  report bytes needed based on total message length, even though
107      * the message isn't available yet.  The caller will use the length
108      * reported here to encode its length.
109      */
110     CRYPT_EncodeLen(msg->base.stream_info.cbContent, NULL, &lenBytes);
111     if (!pbEncoded)
112         *pcbEncoded = 1 + lenBytes + msg->base.stream_info.cbContent;
113     else
114     {
115         if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded,
116          pcbEncoded, 1 + lenBytes)))
117         {
118             if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
119                 pbEncoded = *(BYTE **)pbEncoded;
120             *pbEncoded++ = ASN_OCTETSTRING;
121             CRYPT_EncodeLen(msg->base.stream_info.cbContent, pbEncoded,
122              &lenBytes);
123         }
124     }
125     return ret;
126 }
127
128 static BOOL CRYPT_EncodeDataContentInfoHeader(CDataEncodeMsg *msg,
129  CRYPT_DATA_BLOB *header)
130 {
131     BOOL ret;
132
133     if (msg->base.streamed && msg->base.stream_info.cbContent == 0xffffffff)
134     {
135         FIXME("unimplemented for indefinite-length encoding\n");
136         header->cbData = 0;
137         header->pbData = NULL;
138         ret = TRUE;
139     }
140     else
141     {
142         struct AsnConstructedItem constructed = { 0, msg,
143          CRYPT_EncodeContentLength };
144         struct AsnEncodeSequenceItem items[2] = {
145          { szOID_RSA_data, CRYPT_AsnEncodeOid, 0 },
146          { &constructed,   CRYPT_AsnEncodeConstructed, 0 },
147         };
148
149         ret = CRYPT_AsnEncodeSequence(X509_ASN_ENCODING, items,
150          sizeof(items) / sizeof(items[0]), CRYPT_ENCODE_ALLOC_FLAG, NULL,
151          (LPBYTE)&header->pbData, &header->cbData);
152         if (ret)
153         {
154             /* Trick:  subtract the content length from the reported length,
155              * as the actual content hasn't come yet.
156              */
157             header->cbData -= msg->base.stream_info.cbContent;
158         }
159     }
160     return ret;
161 }
162
163 static BOOL CDataEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
164  DWORD cbData, BOOL fFinal)
165 {
166     CDataEncodeMsg *msg = (CDataEncodeMsg *)hCryptMsg;
167     BOOL ret = FALSE;
168
169     if (msg->base.streamed)
170     {
171         __TRY
172         {
173             if (msg->base.state != MsgStateUpdated)
174             {
175                 CRYPT_DATA_BLOB header;
176
177                 ret = CRYPT_EncodeDataContentInfoHeader(msg, &header);
178                 if (ret)
179                 {
180                     ret = msg->base.stream_info.pfnStreamOutput(
181                      msg->base.stream_info.pvArg, header.pbData, header.cbData,
182                      FALSE);
183                     LocalFree(header.pbData);
184                 }
185             }
186             if (!fFinal)
187                 ret = msg->base.stream_info.pfnStreamOutput(
188                  msg->base.stream_info.pvArg, (BYTE *)pbData, cbData,
189                  FALSE);
190             else
191             {
192                 if (msg->base.stream_info.cbContent == 0xffffffff)
193                 {
194                     BYTE indefinite_trailer[6] = { 0 };
195
196                     ret = msg->base.stream_info.pfnStreamOutput(
197                      msg->base.stream_info.pvArg, (BYTE *)pbData, cbData,
198                      FALSE);
199                     if (ret)
200                         ret = msg->base.stream_info.pfnStreamOutput(
201                          msg->base.stream_info.pvArg, indefinite_trailer,
202                          sizeof(indefinite_trailer), TRUE);
203                 }
204                 else
205                     ret = msg->base.stream_info.pfnStreamOutput(
206                      msg->base.stream_info.pvArg, (BYTE *)pbData, cbData, TRUE);
207             }
208         }
209         __EXCEPT_PAGE_FAULT
210         {
211             SetLastError(STATUS_ACCESS_VIOLATION);
212         }
213         __ENDTRY;
214     }
215     else
216     {
217         if (!fFinal)
218         {
219             if (msg->base.open_flags & CMSG_DETACHED_FLAG)
220                 SetLastError(E_INVALIDARG);
221             else
222                 SetLastError(CRYPT_E_MSG_ERROR);
223         }
224         else
225         {
226             if (!cbData)
227                 SetLastError(E_INVALIDARG);
228             else
229             {
230                 CRYPT_DATA_BLOB blob = { cbData, (LPBYTE)pbData };
231
232                 /* non-streamed data messages don't allow non-final updates,
233                  * don't bother checking whether data already exist, they can't.
234                  */
235                 ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_OCTET_STRING,
236                  &blob, CRYPT_ENCODE_ALLOC_FLAG, NULL, &msg->bare_content,
237                  &msg->bare_content_len);
238             }
239         }
240     }
241     return ret;
242 }
243
244 static BOOL CRYPT_CopyParam(void *pvData, DWORD *pcbData, const BYTE *src,
245  DWORD len)
246 {
247     BOOL ret = TRUE;
248
249     if (!pvData)
250         *pcbData = len;
251     else if (*pcbData < len)
252     {
253         *pcbData = len;
254         SetLastError(ERROR_MORE_DATA);
255         ret = FALSE;
256     }
257     else
258     {
259         *pcbData = len;
260         memcpy(pvData, src, len);
261     }
262     return ret;
263 }
264
265 static BOOL CDataEncodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
266  DWORD dwIndex, void *pvData, DWORD *pcbData)
267 {
268     CDataEncodeMsg *msg = (CDataEncodeMsg *)hCryptMsg;
269     BOOL ret = FALSE;
270
271     switch (dwParamType)
272     {
273     case CMSG_CONTENT_PARAM:
274         if (msg->base.streamed)
275             SetLastError(E_INVALIDARG);
276         else
277         {
278             CRYPT_CONTENT_INFO info;
279             char rsa_data[] = "1.2.840.113549.1.7.1";
280
281             info.pszObjId = rsa_data;
282             info.Content.cbData = msg->bare_content_len;
283             info.Content.pbData = msg->bare_content;
284             ret = CryptEncodeObject(X509_ASN_ENCODING, PKCS_CONTENT_INFO, &info,
285              pvData, pcbData);
286         }
287         break;
288     case CMSG_BARE_CONTENT_PARAM:
289         if (msg->base.streamed)
290             SetLastError(E_INVALIDARG);
291         else
292             ret = CRYPT_CopyParam(pvData, pcbData, msg->bare_content,
293              msg->bare_content_len);
294         break;
295     default:
296         SetLastError(CRYPT_E_INVALID_MSG_TYPE);
297     }
298     return ret;
299 }
300
301 static HCRYPTMSG CDataEncodeMsg_Open(DWORD dwFlags, const void *pvMsgEncodeInfo,
302  LPSTR pszInnerContentObjID, PCMSG_STREAM_INFO pStreamInfo)
303 {
304     CDataEncodeMsg *msg;
305
306     if (pvMsgEncodeInfo)
307     {
308         SetLastError(E_INVALIDARG);
309         return NULL;
310     }
311     msg = CryptMemAlloc(sizeof(CDataEncodeMsg));
312     if (msg)
313     {
314         CryptMsgBase_Init((CryptMsgBase *)msg, dwFlags, pStreamInfo,
315          CDataEncodeMsg_Close, CDataEncodeMsg_GetParam, CDataEncodeMsg_Update);
316         msg->bare_content_len = sizeof(empty_data_content);
317         msg->bare_content = (LPBYTE)empty_data_content;
318     }
319     return (HCRYPTMSG)msg;
320 }
321
322 typedef struct _CHashEncodeMsg
323 {
324     CryptMsgBase    base;
325     HCRYPTPROV      prov;
326     HCRYPTHASH      hash;
327     CRYPT_DATA_BLOB data;
328 } CHashEncodeMsg;
329
330 static void CHashEncodeMsg_Close(HCRYPTMSG hCryptMsg)
331 {
332     CHashEncodeMsg *msg = (CHashEncodeMsg *)hCryptMsg;
333
334     CryptMemFree(msg->data.pbData);
335     CryptDestroyHash(msg->hash);
336     if (msg->base.open_flags & CMSG_CRYPT_RELEASE_CONTEXT_FLAG)
337         CryptReleaseContext(msg->prov, 0);
338 }
339
340 static BOOL CRYPT_EncodePKCSDigestedData(CHashEncodeMsg *msg, void *pvData,
341  DWORD *pcbData)
342 {
343     BOOL ret;
344     ALG_ID algID;
345     DWORD size = sizeof(algID);
346
347     ret = CryptGetHashParam(msg->hash, HP_ALGID, (BYTE *)&algID, &size, 0);
348     if (ret)
349     {
350         CRYPT_DIGESTED_DATA digestedData = { 0 };
351         char oid_rsa_data[] = szOID_RSA_data;
352
353         digestedData.version = CMSG_HASHED_DATA_PKCS_1_5_VERSION;
354         digestedData.DigestAlgorithm.pszObjId = (LPSTR)CertAlgIdToOID(algID);
355         /* FIXME: what about digestedData.DigestAlgorithm.Parameters? */
356         /* Quirk:  OID is only encoded messages if an update has happened */
357         if (msg->base.state != MsgStateInit)
358             digestedData.ContentInfo.pszObjId = oid_rsa_data;
359         if (!(msg->base.open_flags & CMSG_DETACHED_FLAG) && msg->data.cbData)
360         {
361             ret = CRYPT_AsnEncodeOctets(0, NULL, &msg->data,
362              CRYPT_ENCODE_ALLOC_FLAG, NULL,
363              (LPBYTE)&digestedData.ContentInfo.Content.pbData,
364              &digestedData.ContentInfo.Content.cbData);
365         }
366         if (msg->base.state == MsgStateFinalized)
367         {
368             size = sizeof(DWORD);
369             ret = CryptGetHashParam(msg->hash, HP_HASHSIZE,
370              (LPBYTE)&digestedData.hash.cbData, &size, 0);
371             if (ret)
372             {
373                 digestedData.hash.pbData = CryptMemAlloc(
374                  digestedData.hash.cbData);
375                 ret = CryptGetHashParam(msg->hash, HP_HASHVAL,
376                  digestedData.hash.pbData, &digestedData.hash.cbData, 0);
377             }
378         }
379         if (ret)
380             ret = CRYPT_AsnEncodePKCSDigestedData(&digestedData, pvData,
381              pcbData);
382         CryptMemFree(digestedData.hash.pbData);
383         LocalFree(digestedData.ContentInfo.Content.pbData);
384     }
385     return ret;
386 }
387
388 static BOOL CHashEncodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
389  DWORD dwIndex, void *pvData, DWORD *pcbData)
390 {
391     CHashEncodeMsg *msg = (CHashEncodeMsg *)hCryptMsg;
392     BOOL ret = FALSE;
393
394     TRACE("(%p, %d, %d, %p, %p)\n", hCryptMsg, dwParamType, dwIndex,
395      pvData, pcbData);
396
397     switch (dwParamType)
398     {
399     case CMSG_BARE_CONTENT_PARAM:
400         if (msg->base.streamed)
401             SetLastError(E_INVALIDARG);
402         else
403             ret = CRYPT_EncodePKCSDigestedData(msg, pvData, pcbData);
404         break;
405     case CMSG_CONTENT_PARAM:
406     {
407         CRYPT_CONTENT_INFO info;
408
409         ret = CryptMsgGetParam(hCryptMsg, CMSG_BARE_CONTENT_PARAM, 0, NULL,
410          &info.Content.cbData);
411         if (ret)
412         {
413             info.Content.pbData = CryptMemAlloc(info.Content.cbData);
414             if (info.Content.pbData)
415             {
416                 ret = CryptMsgGetParam(hCryptMsg, CMSG_BARE_CONTENT_PARAM, 0,
417                  info.Content.pbData, &info.Content.cbData);
418                 if (ret)
419                 {
420                     char oid_rsa_hashed[] = szOID_RSA_hashedData;
421
422                     info.pszObjId = oid_rsa_hashed;
423                     ret = CryptEncodeObjectEx(X509_ASN_ENCODING,
424                      PKCS_CONTENT_INFO, &info, 0, NULL, pvData, pcbData);
425                 }
426                 CryptMemFree(info.Content.pbData);
427             }
428             else
429                 ret = FALSE;
430         }
431         break;
432     }
433     case CMSG_COMPUTED_HASH_PARAM:
434         ret = CryptGetHashParam(msg->hash, HP_HASHVAL, (BYTE *)pvData, pcbData,
435          0);
436         break;
437     case CMSG_VERSION_PARAM:
438         if (msg->base.state != MsgStateFinalized)
439             SetLastError(CRYPT_E_MSG_ERROR);
440         else
441         {
442             DWORD version = CMSG_HASHED_DATA_PKCS_1_5_VERSION;
443
444             /* Since the data are always encoded as octets, the version is
445              * always 0 (see rfc3852, section 7)
446              */
447             ret = CRYPT_CopyParam(pvData, pcbData, (const BYTE *)&version,
448              sizeof(version));
449         }
450         break;
451     default:
452         ret = FALSE;
453     }
454     return ret;
455 }
456
457 static BOOL CHashEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
458  DWORD cbData, BOOL fFinal)
459 {
460     CHashEncodeMsg *msg = (CHashEncodeMsg *)hCryptMsg;
461     BOOL ret = FALSE;
462
463     TRACE("(%p, %p, %d, %d)\n", hCryptMsg, pbData, cbData, fFinal);
464
465     if (msg->base.streamed || (msg->base.open_flags & CMSG_DETACHED_FLAG))
466     {
467         /* Doesn't do much, as stream output is never called, and you
468          * can't get the content.
469          */
470         ret = CryptHashData(msg->hash, pbData, cbData, 0);
471     }
472     else
473     {
474         if (!fFinal)
475             SetLastError(CRYPT_E_MSG_ERROR);
476         else
477         {
478             ret = CryptHashData(msg->hash, pbData, cbData, 0);
479             if (ret)
480             {
481                 msg->data.pbData = CryptMemAlloc(cbData);
482                 if (msg->data.pbData)
483                 {
484                     memcpy(msg->data.pbData + msg->data.cbData, pbData, cbData);
485                     msg->data.cbData += cbData;
486                 }
487                 else
488                     ret = FALSE;
489             }
490         }
491     }
492     return ret;
493 }
494
495 static HCRYPTMSG CHashEncodeMsg_Open(DWORD dwFlags, const void *pvMsgEncodeInfo,
496  LPSTR pszInnerContentObjID, PCMSG_STREAM_INFO pStreamInfo)
497 {
498     CHashEncodeMsg *msg;
499     const CMSG_HASHED_ENCODE_INFO *info =
500      (const CMSG_HASHED_ENCODE_INFO *)pvMsgEncodeInfo;
501     HCRYPTPROV prov;
502     ALG_ID algID;
503
504     if (info->cbSize != sizeof(CMSG_HASHED_ENCODE_INFO))
505     {
506         SetLastError(E_INVALIDARG);
507         return NULL;
508     }
509     if (!(algID = CertOIDToAlgId(info->HashAlgorithm.pszObjId)))
510     {
511         SetLastError(CRYPT_E_UNKNOWN_ALGO);
512         return NULL;
513     }
514     if (info->hCryptProv)
515         prov = info->hCryptProv;
516     else
517     {
518         prov = CRYPT_GetDefaultProvider();
519         dwFlags &= ~CMSG_CRYPT_RELEASE_CONTEXT_FLAG;
520     }
521     msg = CryptMemAlloc(sizeof(CHashEncodeMsg));
522     if (msg)
523     {
524         CryptMsgBase_Init((CryptMsgBase *)msg, dwFlags, pStreamInfo,
525          CHashEncodeMsg_Close, CHashEncodeMsg_GetParam, CHashEncodeMsg_Update);
526         msg->prov = prov;
527         msg->data.cbData = 0;
528         msg->data.pbData = NULL;
529         if (!CryptCreateHash(prov, algID, 0, 0, &msg->hash))
530         {
531             CryptMsgClose(msg);
532             msg = NULL;
533         }
534     }
535     return (HCRYPTMSG)msg;
536 }
537
538 typedef struct _CMSG_SIGNER_ENCODE_INFO_WITH_CMS
539 {
540     DWORD                      cbSize;
541     PCERT_INFO                 pCertInfo;
542     HCRYPTPROV                 hCryptProv;
543     DWORD                      dwKeySpec;
544     CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
545     void                      *pvHashAuxInfo;
546     DWORD                      cAuthAttr;
547     PCRYPT_ATTRIBUTE           rgAuthAttr;
548     DWORD                      cUnauthAttr;
549     PCRYPT_ATTRIBUTE           rgUnauthAttr;
550     CERT_ID                    SignerId;
551     CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm;
552     void                      *pvHashEncryptionAuxInfo;
553 } CMSG_SIGNER_ENCODE_INFO_WITH_CMS, *PCMSG_SIGNER_ENCODE_INFO_WITH_CMS;
554
555 typedef struct _CMSG_SIGNED_ENCODE_INFO_WITH_CMS
556 {
557     DWORD                             cbSize;
558     DWORD                             cSigners;
559     PCMSG_SIGNER_ENCODE_INFO_WITH_CMS rgSigners;
560     DWORD                             cCertEncoded;
561     PCERT_BLOB                        rgCertEncoded;
562     DWORD                             cCrlEncoded;
563     PCRL_BLOB                         rgCrlEncoded;
564     DWORD                             cAttrCertEncoded;
565     PCERT_BLOB                        rgAttrCertEncoded;
566 } CMSG_SIGNED_ENCODE_INFO_WITH_CMS, *PCMSG_SIGNED_ENCODE_INFO_WITH_CMS;
567
568 static BOOL CRYPT_IsValidSigner(CMSG_SIGNER_ENCODE_INFO_WITH_CMS *signer)
569 {
570     if (signer->cbSize != sizeof(CMSG_SIGNER_ENCODE_INFO) &&
571      signer->cbSize != sizeof(CMSG_SIGNER_ENCODE_INFO_WITH_CMS))
572     {
573         SetLastError(E_INVALIDARG);
574         return FALSE;
575     }
576     if (signer->cbSize == sizeof(CMSG_SIGNER_ENCODE_INFO_WITH_CMS))
577     {
578         FIXME("CMSG_SIGNER_ENCODE_INFO with CMS fields unsupported\n");
579         return FALSE;
580     }
581     if (!signer->pCertInfo->SerialNumber.cbData)
582     {
583         SetLastError(E_INVALIDARG);
584         return FALSE;
585     }
586     if (!signer->pCertInfo->Issuer.cbData)
587     {
588         SetLastError(E_INVALIDARG);
589         return FALSE;
590     }
591     if (!signer->hCryptProv)
592     {
593         SetLastError(E_INVALIDARG);
594         return FALSE;
595     }
596     if (!CertOIDToAlgId(signer->HashAlgorithm.pszObjId))
597     {
598         SetLastError(CRYPT_E_UNKNOWN_ALGO);
599         return FALSE;
600     }
601     return TRUE;
602 }
603
604 typedef struct _CSignerHandles
605 {
606     HCRYPTPROV       prov;
607     HCRYPTHASH       contentHash;
608     HCRYPTHASH       authAttrHash;
609     HCRYPTKEY        key;
610 } CSignerHandles;
611
612 static BOOL CRYPT_CopyBlob(CRYPT_DATA_BLOB *out, const CRYPT_DATA_BLOB *in)
613 {
614     BOOL ret = TRUE;
615
616     out->cbData = in->cbData;
617     if (out->cbData)
618     {
619         out->pbData = CryptMemAlloc(out->cbData);
620         if (out->pbData)
621             memcpy(out->pbData, in->pbData, out->cbData);
622         else
623             ret = FALSE;
624     }
625     else
626         out->pbData = NULL;
627     return ret;
628 }
629
630 typedef struct _BlobArray
631 {
632     DWORD            cBlobs;
633     PCRYPT_DATA_BLOB blobs;
634 } BlobArray;
635
636 static BOOL CRYPT_CopyBlobArray(BlobArray *out, const BlobArray *in)
637 {
638     BOOL ret = TRUE;
639
640     out->cBlobs = in->cBlobs;
641     if (out->cBlobs)
642     {
643         out->blobs = CryptMemAlloc(out->cBlobs * sizeof(CRYPT_DATA_BLOB));
644         if (out->blobs)
645         {
646             DWORD i;
647
648             memset(out->blobs, 0, out->cBlobs * sizeof(CRYPT_DATA_BLOB));
649             for (i = 0; ret && i < out->cBlobs; i++)
650                 ret = CRYPT_CopyBlob(&out->blobs[i], &in->blobs[i]);
651         }
652         else
653             ret = FALSE;
654     }
655     return ret;
656 }
657
658 static void CRYPT_FreeBlobArray(BlobArray *array)
659 {
660     DWORD i;
661
662     for (i = 0; i < array->cBlobs; i++)
663         CryptMemFree(array->blobs[i].pbData);
664     CryptMemFree(array->blobs);
665 }
666
667 static BOOL CRYPT_CopyAttribute(CRYPT_ATTRIBUTE *out, const CRYPT_ATTRIBUTE *in)
668 {
669     BOOL ret;
670
671     out->pszObjId = CryptMemAlloc(strlen(in->pszObjId) + 1);
672     if (out->pszObjId)
673     {
674         strcpy(out->pszObjId, in->pszObjId);
675         ret = CRYPT_CopyBlobArray((BlobArray *)&out->cValue,
676          (const BlobArray *)&in->cValue);
677     }
678     else
679         ret = FALSE;
680     return ret;
681 }
682
683 static BOOL CRYPT_CopyAttributes(CRYPT_ATTRIBUTES *out,
684  const CRYPT_ATTRIBUTES *in)
685 {
686     BOOL ret = TRUE;
687
688     out->cAttr = in->cAttr;
689     if (out->cAttr)
690     {
691         out->rgAttr = CryptMemAlloc(out->cAttr * sizeof(CRYPT_ATTRIBUTE));
692         if (out->rgAttr)
693         {
694             DWORD i;
695
696             memset(out->rgAttr, 0, out->cAttr * sizeof(CRYPT_ATTRIBUTE));
697             for (i = 0; ret && i < out->cAttr; i++)
698                 ret = CRYPT_CopyAttribute(&out->rgAttr[i], &in->rgAttr[i]);
699         }
700         else
701             ret = FALSE;
702     }
703     else
704         out->rgAttr = NULL;
705     return ret;
706 }
707
708 /* Constructs both a CSignerHandles and a CMSG_SIGNER_INFO from a
709  * CMSG_SIGNER_ENCODE_INFO_WITH_CMS.
710  */
711 static BOOL CSignerInfo_Construct(CSignerHandles *handles,
712  CMSG_SIGNER_INFO *info, CMSG_SIGNER_ENCODE_INFO_WITH_CMS *in, DWORD open_flags)
713 {
714     ALG_ID algID;
715     BOOL ret;
716
717     handles->prov = in->hCryptProv;
718     if (!(open_flags & CMSG_CRYPT_RELEASE_CONTEXT_FLAG))
719         CryptContextAddRef(handles->prov, NULL, 0);
720     algID = CertOIDToAlgId(in->HashAlgorithm.pszObjId);
721     ret = CryptCreateHash(handles->prov, algID, 0, 0, &handles->contentHash);
722     if (ret && in->cAuthAttr)
723         ret = CryptCreateHash(handles->prov, algID, 0, 0,
724          &handles->authAttrHash);
725     if (ret)
726     {
727         /* Note: needs to change if CMS fields are supported */
728         info->dwVersion = CMSG_SIGNER_INFO_V1;
729         ret = CRYPT_CopyBlob(&info->Issuer, &in->pCertInfo->Issuer);
730         if (ret)
731             ret = CRYPT_CopyBlob(&info->SerialNumber,
732              &in->pCertInfo->SerialNumber);
733         /* Assumption:  algorithm IDs will point to static strings, not
734          * stack-based ones, so copying the pointer values is safe.
735          */
736         info->HashAlgorithm.pszObjId = in->HashAlgorithm.pszObjId;
737         if (ret)
738             ret = CRYPT_CopyBlob(&info->HashAlgorithm.Parameters,
739              &in->HashAlgorithm.Parameters);
740         memset(&info->HashEncryptionAlgorithm, 0,
741          sizeof(info->HashEncryptionAlgorithm));
742         if (ret)
743             ret = CRYPT_CopyAttributes(&info->AuthAttrs,
744              (CRYPT_ATTRIBUTES *)&in->cAuthAttr);
745         if (ret)
746             ret = CRYPT_CopyAttributes(&info->UnauthAttrs,
747              (CRYPT_ATTRIBUTES *)&in->cUnauthAttr);
748     }
749     return ret;
750 }
751
752 static void CSignerInfo_Free(CMSG_SIGNER_INFO *info)
753 {
754     DWORD i, j;
755
756     CryptMemFree(info->Issuer.pbData);
757     CryptMemFree(info->SerialNumber.pbData);
758     CryptMemFree(info->HashAlgorithm.Parameters.pbData);
759     CryptMemFree(info->EncryptedHash.pbData);
760     for (i = 0; i < info->AuthAttrs.cAttr; i++)
761     {
762         for (j = 0; j < info->AuthAttrs.rgAttr[i].cValue; j++)
763             CryptMemFree(info->AuthAttrs.rgAttr[i].rgValue[j].pbData);
764         CryptMemFree(info->AuthAttrs.rgAttr[i].rgValue);
765         CryptMemFree(info->AuthAttrs.rgAttr[i].pszObjId);
766     }
767     CryptMemFree(info->AuthAttrs.rgAttr);
768     for (i = 0; i < info->UnauthAttrs.cAttr; i++)
769     {
770         for (j = 0; j < info->UnauthAttrs.rgAttr[i].cValue; j++)
771             CryptMemFree(info->UnauthAttrs.rgAttr[i].rgValue[j].pbData);
772         CryptMemFree(info->UnauthAttrs.rgAttr[i].rgValue);
773         CryptMemFree(info->UnauthAttrs.rgAttr[i].pszObjId);
774     }
775     CryptMemFree(info->UnauthAttrs.rgAttr);
776 }
777
778 typedef struct _CSignedEncodeMsg
779 {
780     CryptMsgBase      base;
781     CRYPT_DATA_BLOB   data;
782     CRYPT_SIGNED_INFO info;
783     CSignerHandles   *signerHandles;
784 } CSignedEncodeMsg;
785
786 static void CSignedEncodeMsg_Close(HCRYPTMSG hCryptMsg)
787 {
788     CSignedEncodeMsg *msg = (CSignedEncodeMsg *)hCryptMsg;
789     DWORD i;
790
791     CryptMemFree(msg->data.pbData);
792     CRYPT_FreeBlobArray((BlobArray *)&msg->info.cCertEncoded);
793     CRYPT_FreeBlobArray((BlobArray *)&msg->info.cCrlEncoded);
794     for (i = 0; i < msg->info.cSignerInfo; i++)
795     {
796         CSignerInfo_Free(&msg->info.rgSignerInfo[i]);
797         CryptDestroyKey(msg->signerHandles[i].key);
798         CryptDestroyHash(msg->signerHandles[i].contentHash);
799         CryptDestroyHash(msg->signerHandles[i].authAttrHash);
800         CryptReleaseContext(msg->signerHandles[i].prov, 0);
801     }
802     CryptMemFree(msg->signerHandles);
803     CryptMemFree(msg->info.rgSignerInfo);
804 }
805
806 static BOOL CSignedEncodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
807  DWORD dwIndex, void *pvData, DWORD *pcbData)
808 {
809     CSignedEncodeMsg *msg = (CSignedEncodeMsg *)hCryptMsg;
810     BOOL ret = FALSE;
811
812     switch (dwParamType)
813     {
814     case CMSG_CONTENT_PARAM:
815     {
816         CRYPT_CONTENT_INFO info;
817
818         ret = CryptMsgGetParam(hCryptMsg, CMSG_BARE_CONTENT_PARAM, 0, NULL,
819          &info.Content.cbData);
820         if (ret)
821         {
822             info.Content.pbData = CryptMemAlloc(info.Content.cbData);
823             if (info.Content.pbData)
824             {
825                 ret = CryptMsgGetParam(hCryptMsg, CMSG_BARE_CONTENT_PARAM, 0,
826                  info.Content.pbData, &info.Content.cbData);
827                 if (ret)
828                 {
829                     char oid_rsa_signed[] = szOID_RSA_signedData;
830
831                     info.pszObjId = oid_rsa_signed;
832                     ret = CryptEncodeObjectEx(X509_ASN_ENCODING,
833                      PKCS_CONTENT_INFO, &info, 0, NULL, pvData, pcbData);
834                 }
835                 CryptMemFree(info.Content.pbData);
836             }
837             else
838                 ret = FALSE;
839         }
840         break;
841     }
842     case CMSG_BARE_CONTENT_PARAM:
843     {
844         CRYPT_SIGNED_INFO info;
845         char oid_rsa_data[] = szOID_RSA_data;
846
847         memcpy(&info, &msg->info, sizeof(info));
848         /* Quirk:  OID is only encoded messages if an update has happened */
849         if (msg->base.state != MsgStateInit)
850             info.content.pszObjId = oid_rsa_data;
851         else
852             info.content.pszObjId = NULL;
853         if (msg->data.cbData)
854         {
855             CRYPT_DATA_BLOB blob = { msg->data.cbData, msg->data.pbData };
856
857             ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_OCTET_STRING,
858              &blob, CRYPT_ENCODE_ALLOC_FLAG, NULL,
859              &info.content.Content.pbData, &info.content.Content.cbData);
860         }
861         else
862         {
863             info.content.Content.cbData = 0;
864             info.content.Content.pbData = NULL;
865             ret = TRUE;
866         }
867         if (ret)
868         {
869             ret = CRYPT_AsnEncodePKCSSignedInfo(&info, pvData, pcbData);
870             LocalFree(info.content.Content.pbData);
871         }
872         break;
873     }
874     case CMSG_COMPUTED_HASH_PARAM:
875         if (dwIndex >= msg->info.cSignerInfo)
876             SetLastError(CRYPT_E_INVALID_INDEX);
877         else
878             ret = CryptGetHashParam(msg->signerHandles[dwIndex].contentHash,
879              HP_HASHVAL, pvData, pcbData, 0);
880         break;
881     case CMSG_ENCODED_SIGNER:
882         if (dwIndex >= msg->info.cSignerInfo)
883             SetLastError(CRYPT_E_INVALID_INDEX);
884         else
885             ret = CryptEncodeObjectEx(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
886              PKCS7_SIGNER_INFO, &msg->info.rgSignerInfo[dwIndex], 0, NULL,
887              pvData, pcbData);
888         break;
889     case CMSG_VERSION_PARAM:
890         ret = CRYPT_CopyParam(pvData, pcbData, (const BYTE *)&msg->info.version,
891          sizeof(msg->info.version));
892         break;
893     default:
894         SetLastError(CRYPT_E_INVALID_MSG_TYPE);
895     }
896     return ret;
897 }
898
899 static BOOL CSignedEncodeMsg_UpdateHash(CSignedEncodeMsg *msg,
900  const BYTE *pbData, DWORD cbData)
901 {
902     DWORD i;
903     BOOL ret = TRUE;
904
905     TRACE("(%p, %p, %d)\n", msg, pbData, cbData);
906
907     for (i = 0; ret && i < msg->info.cSignerInfo; i++)
908         ret = CryptHashData(msg->signerHandles[i].contentHash, pbData, cbData,
909          0);
910     return ret;
911 }
912
913 static BOOL CRYPT_AppendAttribute(CRYPT_ATTRIBUTES *out,
914  const CRYPT_ATTRIBUTE *in)
915 {
916     BOOL ret = FALSE;
917
918     out->rgAttr = CryptMemRealloc(out->rgAttr,
919      (out->cAttr + 1) * sizeof(CRYPT_ATTRIBUTE));
920     if (out->rgAttr)
921     {
922         ret = CRYPT_CopyAttribute(&out->rgAttr[out->cAttr], in);
923         if (ret)
924             out->cAttr++;
925     }
926     return ret;
927 }
928
929 static BOOL CSignedEncodeMsg_AppendMessageDigestAttribute(CSignedEncodeMsg *msg,
930  DWORD signerIndex)
931 {
932     BOOL ret;
933     DWORD size;
934     CRYPT_HASH_BLOB hash = { 0, NULL }, encodedHash = { 0, NULL };
935     char messageDigest[] = szOID_RSA_messageDigest;
936     CRYPT_ATTRIBUTE messageDigestAttr = { messageDigest, 1, &encodedHash };
937
938     size = sizeof(DWORD);
939     ret = CryptGetHashParam(msg->signerHandles[signerIndex].contentHash,
940      HP_HASHSIZE, (LPBYTE)&hash.cbData, &size, 0);
941     if (ret)
942     {
943         hash.pbData = CryptMemAlloc(hash.cbData);
944         ret = CryptGetHashParam(msg->signerHandles[signerIndex].contentHash,
945          HP_HASHVAL, hash.pbData, &hash.cbData, 0);
946         if (ret)
947         {
948             ret = CRYPT_AsnEncodeOctets(0, NULL, &hash, CRYPT_ENCODE_ALLOC_FLAG,
949              NULL, (LPBYTE)&encodedHash.pbData, &encodedHash.cbData);
950             if (ret)
951             {
952                 ret = CRYPT_AppendAttribute(
953                  &msg->info.rgSignerInfo[signerIndex].AuthAttrs,
954                  &messageDigestAttr);
955                 LocalFree(encodedHash.pbData);
956             }
957         }
958         CryptMemFree(hash.pbData);
959     }
960     return ret;
961 }
962
963 static BOOL CSignedEncodeMsg_UpdateAuthenticatedAttributes(
964  CSignedEncodeMsg *msg)
965 {
966     DWORD i;
967     BOOL ret = TRUE;
968
969     TRACE("(%p)\n", msg);
970
971     for (i = 0; ret && i < msg->info.cSignerInfo; i++)
972     {
973         if (msg->info.rgSignerInfo[i].AuthAttrs.cAttr)
974         {
975             BYTE oid_rsa_data_encoded[] = { 0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,
976              0x0d,0x01,0x07,0x01 };
977             CRYPT_DATA_BLOB content = { sizeof(oid_rsa_data_encoded),
978              oid_rsa_data_encoded };
979             char contentType[] = szOID_RSA_contentType;
980             CRYPT_ATTRIBUTE contentTypeAttr = { contentType, 1, &content };
981
982             /* FIXME: does this depend on inner OID? */
983             ret = CRYPT_AppendAttribute(&msg->info.rgSignerInfo[i].AuthAttrs,
984              &contentTypeAttr);
985             if (ret)
986                 ret = CSignedEncodeMsg_AppendMessageDigestAttribute(msg, i);
987             if (ret)
988             {
989                 LPBYTE encodedAttrs;
990                 DWORD size;
991
992                 ret = CryptEncodeObjectEx(X509_ASN_ENCODING, PKCS_ATTRIBUTES,
993                  &msg->info.rgSignerInfo[i].AuthAttrs, CRYPT_ENCODE_ALLOC_FLAG,
994                  NULL, (LPBYTE)&encodedAttrs, &size);
995                 if (ret)
996                 {
997                     ret = CryptHashData(msg->signerHandles[i].authAttrHash,
998                      encodedAttrs, size, 0);
999                     LocalFree(encodedAttrs);
1000                 }
1001             }
1002         }
1003     }
1004     TRACE("returning %d\n", ret);
1005     return ret;
1006 }
1007
1008 static void CRYPT_ReverseBytes(CRYPT_HASH_BLOB *hash)
1009 {
1010     DWORD i;
1011     BYTE tmp;
1012
1013     for (i = 0; i < hash->cbData / 2; i++)
1014     {
1015         tmp = hash->pbData[hash->cbData - i - 1];
1016         hash->pbData[hash->cbData - i - 1] = hash->pbData[i];
1017         hash->pbData[i] = tmp;
1018     }
1019 }
1020
1021 static BOOL CSignedEncodeMsg_Sign(CSignedEncodeMsg *msg)
1022 {
1023     DWORD i;
1024     BOOL ret = TRUE;
1025
1026     TRACE("(%p)\n", msg);
1027
1028     for (i = 0; ret && i < msg->info.cSignerInfo; i++)
1029     {
1030         HCRYPTHASH hash;
1031
1032         if (msg->info.rgSignerInfo[i].AuthAttrs.cAttr)
1033             hash = msg->signerHandles[i].authAttrHash;
1034         else
1035             hash = msg->signerHandles[i].contentHash;
1036         ret = CryptSignHashW(hash, AT_SIGNATURE, NULL, 0, NULL,
1037          &msg->info.rgSignerInfo[i].EncryptedHash.cbData);
1038         if (ret)
1039         {
1040             msg->info.rgSignerInfo[i].EncryptedHash.pbData =
1041              CryptMemAlloc(msg->info.rgSignerInfo[i].EncryptedHash.cbData);
1042             if (msg->info.rgSignerInfo[i].EncryptedHash.pbData)
1043             {
1044                 ret = CryptSignHashW(hash, AT_SIGNATURE, NULL, 0,
1045                  msg->info.rgSignerInfo[i].EncryptedHash.pbData,
1046                  &msg->info.rgSignerInfo[i].EncryptedHash.cbData);
1047                 if (ret)
1048                     CRYPT_ReverseBytes(&msg->info.rgSignerInfo[i].EncryptedHash);
1049             }
1050             else
1051                 ret = FALSE;
1052         }
1053     }
1054     return ret;
1055 }
1056
1057 static BOOL CSignedEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
1058  DWORD cbData, BOOL fFinal)
1059 {
1060     CSignedEncodeMsg *msg = (CSignedEncodeMsg *)hCryptMsg;
1061     BOOL ret = FALSE;
1062
1063     if (msg->base.streamed || (msg->base.open_flags & CMSG_DETACHED_FLAG))
1064     {
1065         ret = CSignedEncodeMsg_UpdateHash(msg, pbData, cbData);
1066         if (ret && fFinal)
1067         {
1068             ret = CSignedEncodeMsg_UpdateAuthenticatedAttributes(msg);
1069             if (ret)
1070                 ret = CSignedEncodeMsg_Sign(msg);
1071         }
1072         if (msg->base.streamed)
1073             FIXME("streamed partial stub\n");
1074     }
1075     else
1076     {
1077         if (!fFinal)
1078             SetLastError(CRYPT_E_MSG_ERROR);
1079         else
1080         {
1081             if (cbData)
1082             {
1083                 msg->data.pbData = CryptMemAlloc(cbData);
1084                 if (msg->data.pbData)
1085                 {
1086                     memcpy(msg->data.pbData, pbData, cbData);
1087                     msg->data.cbData = cbData;
1088                     ret = TRUE;
1089                 }
1090             }
1091             else
1092                 ret = TRUE;
1093             if (ret)
1094                 ret = CSignedEncodeMsg_UpdateHash(msg, pbData, cbData);
1095             if (ret)
1096                 ret = CSignedEncodeMsg_UpdateAuthenticatedAttributes(msg);
1097             if (ret)
1098                 ret = CSignedEncodeMsg_Sign(msg);
1099         }
1100     }
1101     return ret;
1102 }
1103
1104 static HCRYPTMSG CSignedEncodeMsg_Open(DWORD dwFlags,
1105  const void *pvMsgEncodeInfo, LPSTR pszInnerContentObjID,
1106  PCMSG_STREAM_INFO pStreamInfo)
1107 {
1108     const CMSG_SIGNED_ENCODE_INFO_WITH_CMS *info =
1109      (const CMSG_SIGNED_ENCODE_INFO_WITH_CMS *)pvMsgEncodeInfo;
1110     DWORD i;
1111     CSignedEncodeMsg *msg;
1112
1113     if (info->cbSize != sizeof(CMSG_SIGNED_ENCODE_INFO) &&
1114      info->cbSize != sizeof(CMSG_SIGNED_ENCODE_INFO_WITH_CMS))
1115     {
1116         SetLastError(E_INVALIDARG);
1117         return NULL;
1118     }
1119     if (info->cbSize == sizeof(CMSG_SIGNED_ENCODE_INFO_WITH_CMS))
1120     {
1121         FIXME("CMSG_SIGNED_ENCODE_INFO with CMS fields unsupported\n");
1122         return NULL;
1123     }
1124     for (i = 0; i < info->cSigners; i++)
1125         if (!CRYPT_IsValidSigner(&info->rgSigners[i]))
1126             return NULL;
1127     msg = CryptMemAlloc(sizeof(CSignedEncodeMsg));
1128     if (msg)
1129     {
1130         BOOL ret = TRUE;
1131
1132         CryptMsgBase_Init((CryptMsgBase *)msg, dwFlags, pStreamInfo,
1133          CSignedEncodeMsg_Close, CSignedEncodeMsg_GetParam,
1134          CSignedEncodeMsg_Update);
1135         msg->data.cbData = 0;
1136         msg->data.pbData = NULL;
1137         memset(&msg->info, 0, sizeof(msg->info));
1138         msg->info.version = CMSG_SIGNED_DATA_V1;
1139         if (info->cSigners)
1140         {
1141             msg->signerHandles =
1142              CryptMemAlloc(info->cSigners * sizeof(CSignerHandles));
1143             if (msg->signerHandles)
1144                 msg->info.rgSignerInfo =
1145                  CryptMemAlloc(info->cSigners * sizeof(CMSG_SIGNER_INFO));
1146             else
1147             {
1148                 ret = FALSE;
1149                 msg->info.rgSignerInfo = NULL;
1150             }
1151             if (msg->info.rgSignerInfo)
1152             {
1153                 msg->info.cSignerInfo = info->cSigners;
1154                 memset(msg->signerHandles, 0,
1155                  msg->info.cSignerInfo * sizeof(CSignerHandles));
1156                 memset(msg->info.rgSignerInfo, 0,
1157                  msg->info.cSignerInfo * sizeof(CMSG_SIGNER_INFO));
1158                 for (i = 0; ret && i < msg->info.cSignerInfo; i++)
1159                     ret = CSignerInfo_Construct(&msg->signerHandles[i],
1160                      &msg->info.rgSignerInfo[i], &info->rgSigners[i], dwFlags);
1161             }
1162             else
1163                 ret = FALSE;
1164         }
1165         if (ret)
1166             ret = CRYPT_CopyBlobArray((BlobArray *)&msg->info.cCertEncoded,
1167              (const BlobArray *)&info->cCertEncoded);
1168         if (ret)
1169             ret = CRYPT_CopyBlobArray((BlobArray *)&msg->info.cCrlEncoded,
1170              (const BlobArray *)&info->cCrlEncoded);
1171         if (!ret)
1172         {
1173             CSignedEncodeMsg_Close(msg);
1174             msg = NULL;
1175         }
1176     }
1177     return msg;
1178 }
1179
1180 static inline const char *MSG_TYPE_STR(DWORD type)
1181 {
1182     switch (type)
1183     {
1184 #define _x(x) case (x): return #x
1185         _x(CMSG_DATA);
1186         _x(CMSG_SIGNED);
1187         _x(CMSG_ENVELOPED);
1188         _x(CMSG_SIGNED_AND_ENVELOPED);
1189         _x(CMSG_HASHED);
1190         _x(CMSG_ENCRYPTED);
1191 #undef _x
1192         default:
1193             return wine_dbg_sprintf("unknown (%d)", type);
1194     }
1195 }
1196
1197 HCRYPTMSG WINAPI CryptMsgOpenToEncode(DWORD dwMsgEncodingType, DWORD dwFlags,
1198  DWORD dwMsgType, const void *pvMsgEncodeInfo, LPSTR pszInnerContentObjID,
1199  PCMSG_STREAM_INFO pStreamInfo)
1200 {
1201     HCRYPTMSG msg = NULL;
1202
1203     TRACE("(%08x, %08x, %08x, %p, %s, %p)\n", dwMsgEncodingType, dwFlags,
1204      dwMsgType, pvMsgEncodeInfo, debugstr_a(pszInnerContentObjID), pStreamInfo);
1205
1206     if (GET_CMSG_ENCODING_TYPE(dwMsgEncodingType) != PKCS_7_ASN_ENCODING)
1207     {
1208         SetLastError(E_INVALIDARG);
1209         return NULL;
1210     }
1211     switch (dwMsgType)
1212     {
1213     case CMSG_DATA:
1214         msg = CDataEncodeMsg_Open(dwFlags, pvMsgEncodeInfo,
1215          pszInnerContentObjID, pStreamInfo);
1216         break;
1217     case CMSG_HASHED:
1218         msg = CHashEncodeMsg_Open(dwFlags, pvMsgEncodeInfo,
1219          pszInnerContentObjID, pStreamInfo);
1220         break;
1221     case CMSG_SIGNED:
1222         msg = CSignedEncodeMsg_Open(dwFlags, pvMsgEncodeInfo,
1223          pszInnerContentObjID, pStreamInfo);
1224         break;
1225     case CMSG_ENVELOPED:
1226         FIXME("unimplemented for type %s\n", MSG_TYPE_STR(dwMsgType));
1227         break;
1228     case CMSG_SIGNED_AND_ENVELOPED:
1229     case CMSG_ENCRYPTED:
1230         /* defined but invalid, fall through */
1231     default:
1232         SetLastError(CRYPT_E_INVALID_MSG_TYPE);
1233     }
1234     return msg;
1235 }
1236
1237 typedef struct _CDecodeMsg
1238 {
1239     CryptMsgBase           base;
1240     DWORD                  type;
1241     HCRYPTPROV             crypt_prov;
1242     union {
1243         HCRYPTHASH             hash;
1244         CRYPT_SIGNED_INFO     *signedInfo;
1245     } u;
1246     CRYPT_DATA_BLOB        msg_data;
1247     PCONTEXT_PROPERTY_LIST properties;
1248 } CDecodeMsg;
1249
1250 static void CDecodeMsg_Close(HCRYPTMSG hCryptMsg)
1251 {
1252     CDecodeMsg *msg = (CDecodeMsg *)hCryptMsg;
1253
1254     if (msg->base.open_flags & CMSG_CRYPT_RELEASE_CONTEXT_FLAG)
1255         CryptReleaseContext(msg->crypt_prov, 0);
1256     switch (msg->type)
1257     {
1258     case CMSG_HASHED:
1259         CryptDestroyHash(msg->u.hash);
1260         break;
1261     case CMSG_SIGNED:
1262         LocalFree(msg->u.signedInfo);
1263         break;
1264     }
1265     CryptMemFree(msg->msg_data.pbData);
1266     ContextPropertyList_Free(msg->properties);
1267 }
1268
1269 static BOOL CDecodeMsg_CopyData(CDecodeMsg *msg, const BYTE *pbData,
1270  DWORD cbData)
1271 {
1272     BOOL ret = TRUE;
1273
1274     if (cbData)
1275     {
1276         if (msg->msg_data.cbData)
1277             msg->msg_data.pbData = CryptMemRealloc(msg->msg_data.pbData,
1278              msg->msg_data.cbData + cbData);
1279         else
1280             msg->msg_data.pbData = CryptMemAlloc(cbData);
1281         if (msg->msg_data.pbData)
1282         {
1283             memcpy(msg->msg_data.pbData + msg->msg_data.cbData, pbData, cbData);
1284             msg->msg_data.cbData += cbData;
1285         }
1286         else
1287             ret = FALSE;
1288     }
1289     return ret;
1290 }
1291
1292 static BOOL CDecodeMsg_DecodeDataContent(CDecodeMsg *msg, CRYPT_DER_BLOB *blob)
1293 {
1294     BOOL ret;
1295     CRYPT_DATA_BLOB *data;
1296     DWORD size;
1297
1298     ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_OCTET_STRING,
1299      blob->pbData, blob->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, (LPBYTE)&data,
1300      &size);
1301     if (ret)
1302     {
1303         ret = ContextPropertyList_SetProperty(msg->properties,
1304          CMSG_CONTENT_PARAM, data->pbData, data->cbData);
1305         LocalFree(data);
1306     }
1307     return ret;
1308 }
1309
1310 static void CDecodeMsg_SaveAlgorithmID(CDecodeMsg *msg, DWORD param,
1311  const CRYPT_ALGORITHM_IDENTIFIER *id)
1312 {
1313     static const BYTE nullParams[] = { ASN_NULL, 0 };
1314     CRYPT_ALGORITHM_IDENTIFIER *copy;
1315     DWORD len = sizeof(CRYPT_ALGORITHM_IDENTIFIER);
1316
1317     /* Linearize algorithm id */
1318     len += strlen(id->pszObjId) + 1;
1319     len += id->Parameters.cbData;
1320     copy = CryptMemAlloc(len);
1321     if (copy)
1322     {
1323         copy->pszObjId =
1324          (LPSTR)((BYTE *)copy + sizeof(CRYPT_ALGORITHM_IDENTIFIER));
1325         strcpy(copy->pszObjId, id->pszObjId);
1326         copy->Parameters.pbData = (BYTE *)copy->pszObjId + strlen(id->pszObjId)
1327          + 1;
1328         /* Trick:  omit NULL parameters */
1329         if (id->Parameters.cbData == sizeof(nullParams) &&
1330          !memcmp(id->Parameters.pbData, nullParams, sizeof(nullParams)))
1331         {
1332             copy->Parameters.cbData = 0;
1333             len -= sizeof(nullParams);
1334         }
1335         else
1336             copy->Parameters.cbData = id->Parameters.cbData;
1337         if (copy->Parameters.cbData)
1338             memcpy(copy->Parameters.pbData, id->Parameters.pbData,
1339              id->Parameters.cbData);
1340         ContextPropertyList_SetProperty(msg->properties, param, (BYTE *)copy,
1341          len);
1342         CryptMemFree(copy);
1343     }
1344 }
1345
1346 static inline void CRYPT_FixUpAlgorithmID(CRYPT_ALGORITHM_IDENTIFIER *id)
1347 {
1348     id->pszObjId = (LPSTR)((BYTE *)id + sizeof(CRYPT_ALGORITHM_IDENTIFIER));
1349     id->Parameters.pbData = (BYTE *)id->pszObjId + strlen(id->pszObjId) + 1;
1350 }
1351
1352 static BOOL CDecodeMsg_DecodeHashedContent(CDecodeMsg *msg,
1353  CRYPT_DER_BLOB *blob)
1354 {
1355     BOOL ret;
1356     CRYPT_DIGESTED_DATA *digestedData;
1357     DWORD size;
1358
1359     ret = CRYPT_AsnDecodePKCSDigestedData(blob->pbData, blob->cbData,
1360      CRYPT_DECODE_ALLOC_FLAG, NULL, (CRYPT_DIGESTED_DATA *)&digestedData,
1361      &size);
1362     if (ret)
1363     {
1364         ContextPropertyList_SetProperty(msg->properties, CMSG_VERSION_PARAM,
1365          (const BYTE *)&digestedData->version, sizeof(digestedData->version));
1366         CDecodeMsg_SaveAlgorithmID(msg, CMSG_HASH_ALGORITHM_PARAM,
1367          &digestedData->DigestAlgorithm);
1368         ContextPropertyList_SetProperty(msg->properties,
1369          CMSG_INNER_CONTENT_TYPE_PARAM,
1370          (const BYTE *)digestedData->ContentInfo.pszObjId,
1371          digestedData->ContentInfo.pszObjId ?
1372          strlen(digestedData->ContentInfo.pszObjId) + 1 : 0);
1373         if (digestedData->ContentInfo.Content.cbData)
1374             CDecodeMsg_DecodeDataContent(msg,
1375              &digestedData->ContentInfo.Content);
1376         else
1377             ContextPropertyList_SetProperty(msg->properties,
1378              CMSG_CONTENT_PARAM, NULL, 0);
1379         ContextPropertyList_SetProperty(msg->properties, CMSG_HASH_DATA_PARAM,
1380          digestedData->hash.pbData, digestedData->hash.cbData);
1381         LocalFree(digestedData);
1382     }
1383     return ret;
1384 }
1385
1386 static BOOL CDecodeMsg_DecodeSignedContent(CDecodeMsg *msg,
1387  CRYPT_DER_BLOB *blob)
1388 {
1389     BOOL ret;
1390     CRYPT_SIGNED_INFO *signedInfo;
1391     DWORD size;
1392
1393     ret = CRYPT_AsnDecodePKCSSignedInfo(blob->pbData, blob->cbData,
1394      CRYPT_DECODE_ALLOC_FLAG, NULL, (CRYPT_SIGNED_INFO *)&signedInfo,
1395      &size);
1396     if (ret)
1397         msg->u.signedInfo = signedInfo;
1398     return ret;
1399 }
1400 /* Decodes the content in blob as the type given, and updates the value
1401  * (type, parameters, etc.) of msg based on what blob contains.
1402  * It doesn't just use msg's type, to allow a recursive call from an implicitly
1403  * typed message once the outer content info has been decoded.
1404  */
1405 static BOOL CDecodeMsg_DecodeContent(CDecodeMsg *msg, CRYPT_DER_BLOB *blob,
1406  DWORD type)
1407 {
1408     BOOL ret;
1409
1410     switch (type)
1411     {
1412     case CMSG_DATA:
1413         if ((ret = CDecodeMsg_DecodeDataContent(msg, blob)))
1414             msg->type = CMSG_DATA;
1415         break;
1416     case CMSG_HASHED:
1417         if ((ret = CDecodeMsg_DecodeHashedContent(msg, blob)))
1418             msg->type = CMSG_HASHED;
1419         break;
1420     case CMSG_ENVELOPED:
1421         FIXME("unimplemented for type %s\n", MSG_TYPE_STR(type));
1422         ret = TRUE;
1423         break;
1424     case CMSG_SIGNED:
1425         if ((ret = CDecodeMsg_DecodeSignedContent(msg, blob)))
1426             msg->type = CMSG_SIGNED;
1427         break;
1428     default:
1429     {
1430         CRYPT_CONTENT_INFO *info;
1431         DWORD size;
1432
1433         ret = CryptDecodeObjectEx(X509_ASN_ENCODING, PKCS_CONTENT_INFO,
1434          msg->msg_data.pbData, msg->msg_data.cbData, CRYPT_DECODE_ALLOC_FLAG,
1435          NULL, (LPBYTE)&info, &size);
1436         if (ret)
1437         {
1438             if (!strcmp(info->pszObjId, szOID_RSA_data))
1439                 ret = CDecodeMsg_DecodeContent(msg, &info->Content, CMSG_DATA);
1440             else if (!strcmp(info->pszObjId, szOID_RSA_digestedData))
1441                 ret = CDecodeMsg_DecodeContent(msg, &info->Content,
1442                  CMSG_HASHED);
1443             else if (!strcmp(info->pszObjId, szOID_RSA_envelopedData))
1444                 ret = CDecodeMsg_DecodeContent(msg, &info->Content,
1445                  CMSG_ENVELOPED);
1446             else if (!strcmp(info->pszObjId, szOID_RSA_signedData))
1447                 ret = CDecodeMsg_DecodeContent(msg, &info->Content,
1448                  CMSG_SIGNED);
1449             else
1450             {
1451                 SetLastError(CRYPT_E_INVALID_MSG_TYPE);
1452                 ret = FALSE;
1453             }
1454             LocalFree(info);
1455         }
1456     }
1457     }
1458     return ret;
1459 }
1460
1461 static BOOL CDecodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
1462  DWORD cbData, BOOL fFinal)
1463 {
1464     CDecodeMsg *msg = (CDecodeMsg *)hCryptMsg;
1465     BOOL ret = FALSE;
1466
1467     TRACE("(%p, %p, %d, %d)\n", hCryptMsg, pbData, cbData, fFinal);
1468
1469     if (msg->base.streamed)
1470     {
1471         ret = CDecodeMsg_CopyData(msg, pbData, cbData);
1472         FIXME("(%p, %p, %d, %d): streamed update stub\n", hCryptMsg, pbData,
1473          cbData, fFinal);
1474     }
1475     else
1476     {
1477         if (!fFinal)
1478             SetLastError(CRYPT_E_MSG_ERROR);
1479         else
1480         {
1481             ret = CDecodeMsg_CopyData(msg, pbData, cbData);
1482             if (ret)
1483                 ret = CDecodeMsg_DecodeContent(msg, &msg->msg_data, msg->type);
1484
1485         }
1486     }
1487     return ret;
1488 }
1489
1490 static BOOL CDecodeHashMsg_GetParam(CDecodeMsg *msg, DWORD dwParamType,
1491  DWORD dwIndex, void *pvData, DWORD *pcbData)
1492 {
1493     BOOL ret = FALSE;
1494
1495     switch (dwParamType)
1496     {
1497     case CMSG_TYPE_PARAM:
1498         ret = CRYPT_CopyParam(pvData, pcbData, (const BYTE *)&msg->type,
1499          sizeof(msg->type));
1500         break;
1501     case CMSG_HASH_ALGORITHM_PARAM:
1502     {
1503         CRYPT_DATA_BLOB blob;
1504
1505         ret = ContextPropertyList_FindProperty(msg->properties, dwParamType,
1506          &blob);
1507         if (ret)
1508         {
1509             ret = CRYPT_CopyParam(pvData, pcbData, blob.pbData, blob.cbData);
1510             if (ret && pvData)
1511                 CRYPT_FixUpAlgorithmID((CRYPT_ALGORITHM_IDENTIFIER *)pvData);
1512         }
1513         else
1514             SetLastError(CRYPT_E_INVALID_MSG_TYPE);
1515         break;
1516     }
1517     case CMSG_COMPUTED_HASH_PARAM:
1518         if (!msg->u.hash)
1519         {
1520             CRYPT_ALGORITHM_IDENTIFIER *hashAlgoID = NULL;
1521             DWORD size = 0;
1522             ALG_ID algID = 0;
1523
1524             CryptMsgGetParam(msg, CMSG_HASH_ALGORITHM_PARAM, 0, NULL, &size);
1525             hashAlgoID = CryptMemAlloc(size);
1526             ret = CryptMsgGetParam(msg, CMSG_HASH_ALGORITHM_PARAM, 0,
1527              hashAlgoID, &size);
1528             if (ret)
1529                 algID = CertOIDToAlgId(hashAlgoID->pszObjId);
1530             ret = CryptCreateHash(msg->crypt_prov, algID, 0, 0, &msg->u.hash);
1531             if (ret)
1532             {
1533                 CRYPT_DATA_BLOB content;
1534
1535                 ret = ContextPropertyList_FindProperty(msg->properties,
1536                  CMSG_CONTENT_PARAM, &content);
1537                 if (ret)
1538                     ret = CryptHashData(msg->u.hash, content.pbData,
1539                      content.cbData, 0);
1540             }
1541             CryptMemFree(hashAlgoID);
1542         }
1543         else
1544             ret = TRUE;
1545         if (ret)
1546             ret = CryptGetHashParam(msg->u.hash, HP_HASHVAL, pvData, pcbData,
1547              0);
1548         break;
1549     default:
1550     {
1551         CRYPT_DATA_BLOB blob;
1552
1553         ret = ContextPropertyList_FindProperty(msg->properties, dwParamType,
1554          &blob);
1555         if (ret)
1556             ret = CRYPT_CopyParam(pvData, pcbData, blob.pbData, blob.cbData);
1557         else
1558             SetLastError(CRYPT_E_INVALID_MSG_TYPE);
1559     }
1560     }
1561     return ret;
1562 }
1563
1564 static BOOL CDecodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
1565  DWORD dwIndex, void *pvData, DWORD *pcbData)
1566 {
1567     CDecodeMsg *msg = (CDecodeMsg *)hCryptMsg;
1568     BOOL ret = FALSE;
1569
1570     switch (msg->type)
1571     {
1572     case CMSG_HASHED:
1573         ret = CDecodeHashMsg_GetParam(msg, dwParamType, dwIndex, pvData,
1574          pcbData);
1575         break;
1576     default:
1577         switch (dwParamType)
1578         {
1579         case CMSG_TYPE_PARAM:
1580             ret = CRYPT_CopyParam(pvData, pcbData, (const BYTE *)&msg->type,
1581              sizeof(msg->type));
1582             break;
1583         default:
1584         {
1585             CRYPT_DATA_BLOB blob;
1586
1587             ret = ContextPropertyList_FindProperty(msg->properties, dwParamType,
1588              &blob);
1589             if (ret)
1590                 ret = CRYPT_CopyParam(pvData, pcbData, blob.pbData,
1591                  blob.cbData);
1592             else
1593                 SetLastError(CRYPT_E_INVALID_MSG_TYPE);
1594         }
1595         }
1596     }
1597     return ret;
1598 }
1599
1600 HCRYPTMSG WINAPI CryptMsgOpenToDecode(DWORD dwMsgEncodingType, DWORD dwFlags,
1601  DWORD dwMsgType, HCRYPTPROV hCryptProv, PCERT_INFO pRecipientInfo,
1602  PCMSG_STREAM_INFO pStreamInfo)
1603 {
1604     CDecodeMsg *msg;
1605
1606     TRACE("(%08x, %08x, %08x, %08lx, %p, %p)\n", dwMsgEncodingType,
1607      dwFlags, dwMsgType, hCryptProv, pRecipientInfo, pStreamInfo);
1608
1609     if (GET_CMSG_ENCODING_TYPE(dwMsgEncodingType) != PKCS_7_ASN_ENCODING)
1610     {
1611         SetLastError(E_INVALIDARG);
1612         return NULL;
1613     }
1614     msg = CryptMemAlloc(sizeof(CDecodeMsg));
1615     if (msg)
1616     {
1617         CryptMsgBase_Init((CryptMsgBase *)msg, dwFlags, pStreamInfo,
1618          CDecodeMsg_Close, CDecodeMsg_GetParam, CDecodeMsg_Update);
1619         msg->type = dwMsgType;
1620         if (hCryptProv)
1621             msg->crypt_prov = hCryptProv;
1622         else
1623         {
1624             msg->crypt_prov = CRYPT_GetDefaultProvider();
1625             msg->base.open_flags &= ~CMSG_CRYPT_RELEASE_CONTEXT_FLAG;
1626         }
1627         memset(&msg->u, 0, sizeof(msg->u));
1628         msg->msg_data.cbData = 0;
1629         msg->msg_data.pbData = NULL;
1630         msg->properties = ContextPropertyList_Create();
1631     }
1632     return msg;
1633 }
1634
1635 HCRYPTMSG WINAPI CryptMsgDuplicate(HCRYPTMSG hCryptMsg)
1636 {
1637     TRACE("(%p)\n", hCryptMsg);
1638
1639     if (hCryptMsg)
1640     {
1641         CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
1642
1643         InterlockedIncrement(&msg->ref);
1644     }
1645     return hCryptMsg;
1646 }
1647
1648 BOOL WINAPI CryptMsgClose(HCRYPTMSG hCryptMsg)
1649 {
1650     TRACE("(%p)\n", hCryptMsg);
1651
1652     if (hCryptMsg)
1653     {
1654         CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
1655
1656         if (InterlockedDecrement(&msg->ref) == 0)
1657         {
1658             TRACE("freeing %p\n", msg);
1659             if (msg->close)
1660                 msg->close(msg);
1661             CryptMemFree(msg);
1662         }
1663     }
1664     return TRUE;
1665 }
1666
1667 BOOL WINAPI CryptMsgUpdate(HCRYPTMSG hCryptMsg, const BYTE *pbData,
1668  DWORD cbData, BOOL fFinal)
1669 {
1670     CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
1671     BOOL ret = FALSE;
1672
1673     TRACE("(%p, %p, %d, %d)\n", hCryptMsg, pbData, cbData, fFinal);
1674
1675     if (msg->state == MsgStateFinalized)
1676         SetLastError(CRYPT_E_MSG_ERROR);
1677     else
1678     {
1679         ret = msg->update(hCryptMsg, pbData, cbData, fFinal);
1680         msg->state = MsgStateUpdated;
1681         if (fFinal)
1682             msg->state = MsgStateFinalized;
1683     }
1684     return ret;
1685 }
1686
1687 BOOL WINAPI CryptMsgGetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
1688  DWORD dwIndex, void *pvData, DWORD *pcbData)
1689 {
1690     CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
1691
1692     TRACE("(%p, %d, %d, %p, %p)\n", hCryptMsg, dwParamType, dwIndex,
1693      pvData, pcbData);
1694     return msg->get_param(hCryptMsg, dwParamType, dwIndex, pvData, pcbData);
1695 }