crypt32: Detached hash messages don't contain the content, so don't make a copy of it.
[wine/wine64.git] / dlls / crypt32 / msg.c
blob36194a3ea27edf30e611a6c94e24aebd0f9e006f
1 /*
2 * Copyright 2007 Juan Lang
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.
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.
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
18 #include <stdarg.h>
19 #include "windef.h"
20 #include "winbase.h"
21 #include "wincrypt.h"
22 #include "snmp.h"
24 #include "wine/debug.h"
25 #include "wine/exception.h"
26 #include "crypt32_private.h"
28 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
30 /* Called when a message's ref count reaches zero. Free any message-specific
31 * data here.
33 typedef void (*CryptMsgCloseFunc)(HCRYPTMSG msg);
35 typedef BOOL (*CryptMsgGetParamFunc)(HCRYPTMSG hCryptMsg, DWORD dwParamType,
36 DWORD dwIndex, void *pvData, DWORD *pcbData);
38 typedef BOOL (*CryptMsgUpdateFunc)(HCRYPTMSG hCryptMsg, const BYTE *pbData,
39 DWORD cbData, BOOL fFinal);
41 typedef struct _CryptMsgBase
43 LONG ref;
44 DWORD open_flags;
45 BOOL streamed;
46 CMSG_STREAM_INFO stream_info;
47 BOOL finalized;
48 CryptMsgCloseFunc close;
49 CryptMsgUpdateFunc update;
50 CryptMsgGetParamFunc get_param;
51 } CryptMsgBase;
53 static inline void CryptMsgBase_Init(CryptMsgBase *msg, DWORD dwFlags,
54 PCMSG_STREAM_INFO pStreamInfo, CryptMsgCloseFunc close,
55 CryptMsgGetParamFunc get_param, CryptMsgUpdateFunc update)
57 msg->ref = 1;
58 msg->open_flags = dwFlags;
59 if (pStreamInfo)
61 msg->streamed = TRUE;
62 memcpy(&msg->stream_info, pStreamInfo, sizeof(msg->stream_info));
64 else
66 msg->streamed = FALSE;
67 memset(&msg->stream_info, 0, sizeof(msg->stream_info));
69 msg->close = close;
70 msg->get_param = get_param;
71 msg->update = update;
72 msg->finalized = FALSE;
75 typedef struct _CDataEncodeMsg
77 CryptMsgBase base;
78 DWORD bare_content_len;
79 LPBYTE bare_content;
80 BOOL begun;
81 } CDataEncodeMsg;
83 static const BYTE empty_data_content[] = { 0x04,0x00 };
85 static void CDataEncodeMsg_Close(HCRYPTMSG hCryptMsg)
87 CDataEncodeMsg *msg = (CDataEncodeMsg *)hCryptMsg;
89 if (msg->bare_content != empty_data_content)
90 LocalFree(msg->bare_content);
93 static WINAPI BOOL CRYPT_EncodeContentLength(DWORD dwCertEncodingType,
94 LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
95 PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
97 const CDataEncodeMsg *msg = (const CDataEncodeMsg *)pvStructInfo;
98 DWORD lenBytes;
99 BOOL ret = TRUE;
101 /* Trick: report bytes needed based on total message length, even though
102 * the message isn't available yet. The caller will use the length
103 * reported here to encode its length.
105 CRYPT_EncodeLen(msg->base.stream_info.cbContent, NULL, &lenBytes);
106 if (!pbEncoded)
107 *pcbEncoded = 1 + lenBytes + msg->base.stream_info.cbContent;
108 else
110 if ((ret = CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded,
111 pcbEncoded, 1 + lenBytes)))
113 if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG)
114 pbEncoded = *(BYTE **)pbEncoded;
115 *pbEncoded++ = ASN_OCTETSTRING;
116 CRYPT_EncodeLen(msg->base.stream_info.cbContent, pbEncoded,
117 &lenBytes);
120 return ret;
123 static BOOL CRYPT_EncodeDataContentInfoHeader(CDataEncodeMsg *msg,
124 CRYPT_DATA_BLOB *header)
126 BOOL ret;
128 if (msg->base.streamed && msg->base.stream_info.cbContent == 0xffffffff)
130 FIXME("unimplemented for indefinite-length encoding\n");
131 header->cbData = 0;
132 header->pbData = NULL;
133 ret = TRUE;
135 else
137 struct AsnConstructedItem constructed = { 0, msg,
138 CRYPT_EncodeContentLength };
139 struct AsnEncodeSequenceItem items[2] = {
140 { szOID_RSA_data, CRYPT_AsnEncodeOid, 0 },
141 { &constructed, CRYPT_AsnEncodeConstructed, 0 },
144 ret = CRYPT_AsnEncodeSequence(X509_ASN_ENCODING, items,
145 sizeof(items) / sizeof(items[0]), CRYPT_ENCODE_ALLOC_FLAG, NULL,
146 (LPBYTE)&header->pbData, &header->cbData);
147 if (ret)
149 /* Trick: subtract the content length from the reported length,
150 * as the actual content hasn't come yet.
152 header->cbData -= msg->base.stream_info.cbContent;
155 return ret;
158 static BOOL CDataEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
159 DWORD cbData, BOOL fFinal)
161 CDataEncodeMsg *msg = (CDataEncodeMsg *)hCryptMsg;
162 BOOL ret = FALSE;
164 if (msg->base.finalized)
165 SetLastError(CRYPT_E_MSG_ERROR);
166 else if (msg->base.streamed)
168 if (fFinal)
169 msg->base.finalized = TRUE;
170 __TRY
172 if (!msg->begun)
174 CRYPT_DATA_BLOB header;
176 msg->begun = TRUE;
177 ret = CRYPT_EncodeDataContentInfoHeader(msg, &header);
178 if (ret)
180 ret = msg->base.stream_info.pfnStreamOutput(
181 msg->base.stream_info.pvArg, header.pbData, header.cbData,
182 FALSE);
183 LocalFree(header.pbData);
186 if (!fFinal)
187 ret = msg->base.stream_info.pfnStreamOutput(
188 msg->base.stream_info.pvArg, (BYTE *)pbData, cbData,
189 FALSE);
190 else
192 if (msg->base.stream_info.cbContent == 0xffffffff)
194 BYTE indefinite_trailer[6] = { 0 };
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);
204 else
205 ret = msg->base.stream_info.pfnStreamOutput(
206 msg->base.stream_info.pvArg, (BYTE *)pbData, cbData, TRUE);
209 __EXCEPT_PAGE_FAULT
211 SetLastError(STATUS_ACCESS_VIOLATION);
213 __ENDTRY;
215 else
217 if (!fFinal)
219 if (msg->base.open_flags & CMSG_DETACHED_FLAG)
220 SetLastError(E_INVALIDARG);
221 else
222 SetLastError(CRYPT_E_MSG_ERROR);
224 else
226 msg->base.finalized = TRUE;
227 if (!cbData)
228 SetLastError(E_INVALIDARG);
229 else
231 CRYPT_DATA_BLOB blob = { cbData, (LPBYTE)pbData };
233 /* non-streamed data messages don't allow non-final updates,
234 * don't bother checking whether data already exist, they can't.
236 ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_OCTET_STRING,
237 &blob, CRYPT_ENCODE_ALLOC_FLAG, NULL, &msg->bare_content,
238 &msg->bare_content_len);
242 return ret;
245 static BOOL CRYPT_CopyParam(void *pvData, DWORD *pcbData, const BYTE *src,
246 DWORD len)
248 BOOL ret = TRUE;
250 if (!pvData)
251 *pcbData = len;
252 else if (*pcbData < len)
254 *pcbData = len;
255 SetLastError(ERROR_MORE_DATA);
256 ret = FALSE;
258 else
260 *pcbData = len;
261 memcpy(pvData, src, len);
263 return ret;
266 static BOOL CDataEncodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
267 DWORD dwIndex, void *pvData, DWORD *pcbData)
269 CDataEncodeMsg *msg = (CDataEncodeMsg *)hCryptMsg;
270 BOOL ret = FALSE;
272 switch (dwParamType)
274 case CMSG_CONTENT_PARAM:
275 if (msg->base.streamed)
276 SetLastError(E_INVALIDARG);
277 else
279 CRYPT_CONTENT_INFO info;
280 char rsa_data[] = "1.2.840.113549.1.7.1";
282 info.pszObjId = rsa_data;
283 info.Content.cbData = msg->bare_content_len;
284 info.Content.pbData = msg->bare_content;
285 ret = CryptEncodeObject(X509_ASN_ENCODING, PKCS_CONTENT_INFO, &info,
286 pvData, pcbData);
288 break;
289 case CMSG_BARE_CONTENT_PARAM:
290 if (msg->base.streamed)
291 SetLastError(E_INVALIDARG);
292 else
293 ret = CRYPT_CopyParam(pvData, pcbData, msg->bare_content,
294 msg->bare_content_len);
295 break;
296 default:
297 SetLastError(CRYPT_E_INVALID_MSG_TYPE);
299 return ret;
302 static HCRYPTMSG CDataEncodeMsg_Open(DWORD dwFlags, const void *pvMsgEncodeInfo,
303 LPSTR pszInnerContentObjID, PCMSG_STREAM_INFO pStreamInfo)
305 CDataEncodeMsg *msg;
307 if (pvMsgEncodeInfo)
309 SetLastError(E_INVALIDARG);
310 return NULL;
312 msg = CryptMemAlloc(sizeof(CDataEncodeMsg));
313 if (msg)
315 CryptMsgBase_Init((CryptMsgBase *)msg, dwFlags, pStreamInfo,
316 CDataEncodeMsg_Close, CDataEncodeMsg_GetParam, CDataEncodeMsg_Update);
317 msg->bare_content_len = sizeof(empty_data_content);
318 msg->bare_content = (LPBYTE)empty_data_content;
319 msg->begun = FALSE;
321 return (HCRYPTMSG)msg;
324 typedef struct _CHashEncodeMsg
326 CryptMsgBase base;
327 HCRYPTPROV prov;
328 HCRYPTHASH hash;
329 CRYPT_DATA_BLOB data;
330 } CHashEncodeMsg;
332 static void CHashEncodeMsg_Close(HCRYPTMSG hCryptMsg)
334 CHashEncodeMsg *msg = (CHashEncodeMsg *)hCryptMsg;
336 CryptMemFree(msg->data.pbData);
337 CryptDestroyHash(msg->hash);
338 if (msg->base.open_flags & CMSG_CRYPT_RELEASE_CONTEXT_FLAG)
339 CryptReleaseContext(msg->prov, 0);
342 static BOOL CHashEncodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
343 DWORD dwIndex, void *pvData, DWORD *pcbData)
345 CHashEncodeMsg *msg = (CHashEncodeMsg *)hCryptMsg;
346 BOOL ret = FALSE;
348 TRACE("(%p, %d, %d, %p, %p)\n", hCryptMsg, dwParamType, dwIndex,
349 pvData, pcbData);
351 switch (dwParamType)
353 case CMSG_COMPUTED_HASH_PARAM:
354 ret = CryptGetHashParam(msg->hash, HP_HASHVAL, (BYTE *)pvData, pcbData,
356 break;
357 case CMSG_VERSION_PARAM:
358 if (!msg->base.finalized)
359 SetLastError(CRYPT_E_MSG_ERROR);
360 else
362 /* FIXME: under what circumstances is this CMSG_HASHED_DATA_V2? */
363 DWORD version = CMSG_HASHED_DATA_PKCS_1_5_VERSION;
365 ret = CRYPT_CopyParam(pvData, pcbData, (const BYTE *)&version,
366 sizeof(version));
368 break;
369 default:
370 FIXME("%d: stub\n", dwParamType);
371 ret = FALSE;
373 return ret;
376 static BOOL CHashEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
377 DWORD cbData, BOOL fFinal)
379 CHashEncodeMsg *msg = (CHashEncodeMsg *)hCryptMsg;
380 BOOL ret = FALSE;
382 TRACE("(%p, %p, %d, %d)\n", hCryptMsg, pbData, cbData, fFinal);
384 if (msg->base.finalized)
385 SetLastError(CRYPT_E_MSG_ERROR);
386 else
388 if (fFinal)
389 msg->base.finalized = TRUE;
390 if (msg->base.streamed || (msg->base.open_flags & CMSG_DETACHED_FLAG))
392 /* Doesn't do much, as stream output is never called, and you
393 * can't get the content.
395 ret = CryptHashData(msg->hash, pbData, cbData, 0);
397 else
399 if (!fFinal)
400 SetLastError(CRYPT_E_MSG_ERROR);
401 else
403 ret = CryptHashData(msg->hash, pbData, cbData, 0);
404 if (ret)
406 msg->data.pbData = CryptMemAlloc(cbData);
407 if (msg->data.pbData)
409 memcpy(msg->data.pbData + msg->data.cbData, pbData,
410 cbData);
411 msg->data.cbData += cbData;
413 else
414 ret = FALSE;
419 return ret;
422 static HCRYPTMSG CHashEncodeMsg_Open(DWORD dwFlags, const void *pvMsgEncodeInfo,
423 LPSTR pszInnerContentObjID, PCMSG_STREAM_INFO pStreamInfo)
425 CHashEncodeMsg *msg;
426 const CMSG_HASHED_ENCODE_INFO *info =
427 (const CMSG_HASHED_ENCODE_INFO *)pvMsgEncodeInfo;
428 HCRYPTPROV prov;
429 ALG_ID algID;
431 if (info->cbSize != sizeof(CMSG_HASHED_ENCODE_INFO))
433 SetLastError(E_INVALIDARG);
434 return NULL;
436 if (!(algID = CertOIDToAlgId(info->HashAlgorithm.pszObjId)))
438 SetLastError(CRYPT_E_UNKNOWN_ALGO);
439 return NULL;
441 if (info->hCryptProv)
442 prov = info->hCryptProv;
443 else
445 prov = CRYPT_GetDefaultProvider();
446 dwFlags &= ~CMSG_CRYPT_RELEASE_CONTEXT_FLAG;
448 msg = CryptMemAlloc(sizeof(CHashEncodeMsg));
449 if (msg)
451 CryptMsgBase_Init((CryptMsgBase *)msg, dwFlags, pStreamInfo,
452 CHashEncodeMsg_Close, CHashEncodeMsg_GetParam, CHashEncodeMsg_Update);
453 msg->prov = prov;
454 msg->data.cbData = 0;
455 msg->data.pbData = NULL;
456 if (!CryptCreateHash(prov, algID, 0, 0, &msg->hash))
458 CryptMsgClose(msg);
459 msg = NULL;
462 return (HCRYPTMSG)msg;
465 static inline const char *MSG_TYPE_STR(DWORD type)
467 switch (type)
469 #define _x(x) case (x): return #x
470 _x(CMSG_DATA);
471 _x(CMSG_SIGNED);
472 _x(CMSG_ENVELOPED);
473 _x(CMSG_SIGNED_AND_ENVELOPED);
474 _x(CMSG_HASHED);
475 _x(CMSG_ENCRYPTED);
476 #undef _x
477 default:
478 return wine_dbg_sprintf("unknown (%d)", type);
482 HCRYPTMSG WINAPI CryptMsgOpenToEncode(DWORD dwMsgEncodingType, DWORD dwFlags,
483 DWORD dwMsgType, const void *pvMsgEncodeInfo, LPSTR pszInnerContentObjID,
484 PCMSG_STREAM_INFO pStreamInfo)
486 HCRYPTMSG msg = NULL;
488 TRACE("(%08x, %08x, %08x, %p, %s, %p)\n", dwMsgEncodingType, dwFlags,
489 dwMsgType, pvMsgEncodeInfo, debugstr_a(pszInnerContentObjID), pStreamInfo);
491 if (GET_CMSG_ENCODING_TYPE(dwMsgEncodingType) != PKCS_7_ASN_ENCODING)
493 SetLastError(E_INVALIDARG);
494 return NULL;
496 switch (dwMsgType)
498 case CMSG_DATA:
499 msg = CDataEncodeMsg_Open(dwFlags, pvMsgEncodeInfo,
500 pszInnerContentObjID, pStreamInfo);
501 break;
502 case CMSG_HASHED:
503 msg = CHashEncodeMsg_Open(dwFlags, pvMsgEncodeInfo,
504 pszInnerContentObjID, pStreamInfo);
505 break;
506 case CMSG_SIGNED:
507 case CMSG_ENVELOPED:
508 FIXME("unimplemented for type %s\n", MSG_TYPE_STR(dwMsgType));
509 break;
510 case CMSG_SIGNED_AND_ENVELOPED:
511 case CMSG_ENCRYPTED:
512 /* defined but invalid, fall through */
513 default:
514 SetLastError(CRYPT_E_INVALID_MSG_TYPE);
516 return msg;
519 typedef struct _CDecodeMsg
521 CryptMsgBase base;
522 DWORD type;
523 HCRYPTPROV crypt_prov;
524 } CDecodeMsg;
526 static void CDecodeMsg_Close(HCRYPTMSG hCryptMsg)
528 CDecodeMsg *msg = (CDecodeMsg *)hCryptMsg;
530 if (msg->base.open_flags & CMSG_CRYPT_RELEASE_CONTEXT_FLAG)
531 CryptReleaseContext(msg->crypt_prov, 0);
534 static BOOL CDecodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
535 DWORD cbData, BOOL fFinal)
537 FIXME("(%p, %p, %d, %d): stub\n", hCryptMsg, pbData, cbData, fFinal);
538 return FALSE;
541 static BOOL CDecodeMsg_GetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
542 DWORD dwIndex, void *pvData, DWORD *pcbData)
544 CDecodeMsg *msg = (CDecodeMsg *)hCryptMsg;
545 BOOL ret = FALSE;
547 switch (dwParamType)
549 case CMSG_TYPE_PARAM:
550 ret = CRYPT_CopyParam(pvData, pcbData, (const BYTE *)&msg->type,
551 sizeof(msg->type));
552 break;
553 default:
554 FIXME("unimplemented for parameter %d\n", dwParamType);
555 SetLastError(CRYPT_E_INVALID_MSG_TYPE);
557 return ret;
560 HCRYPTMSG WINAPI CryptMsgOpenToDecode(DWORD dwMsgEncodingType, DWORD dwFlags,
561 DWORD dwMsgType, HCRYPTPROV hCryptProv, PCERT_INFO pRecipientInfo,
562 PCMSG_STREAM_INFO pStreamInfo)
564 CDecodeMsg *msg;
566 TRACE("(%08x, %08x, %08x, %08lx, %p, %p)\n", dwMsgEncodingType,
567 dwFlags, dwMsgType, hCryptProv, pRecipientInfo, pStreamInfo);
569 if (GET_CMSG_ENCODING_TYPE(dwMsgEncodingType) != PKCS_7_ASN_ENCODING)
571 SetLastError(E_INVALIDARG);
572 return NULL;
574 msg = CryptMemAlloc(sizeof(CDecodeMsg));
575 if (msg)
577 CryptMsgBase_Init((CryptMsgBase *)msg, dwFlags, pStreamInfo,
578 CDecodeMsg_Close, CDecodeMsg_GetParam, CDecodeMsg_Update);
579 msg->type = dwMsgType;
581 return msg;
584 HCRYPTMSG WINAPI CryptMsgDuplicate(HCRYPTMSG hCryptMsg)
586 TRACE("(%p)\n", hCryptMsg);
588 if (hCryptMsg)
590 CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
592 InterlockedIncrement(&msg->ref);
594 return hCryptMsg;
597 BOOL WINAPI CryptMsgClose(HCRYPTMSG hCryptMsg)
599 TRACE("(%p)\n", hCryptMsg);
601 if (hCryptMsg)
603 CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
605 if (InterlockedDecrement(&msg->ref) == 0)
607 TRACE("freeing %p\n", msg);
608 if (msg->close)
609 msg->close(msg);
610 CryptMemFree(msg);
613 return TRUE;
616 BOOL WINAPI CryptMsgUpdate(HCRYPTMSG hCryptMsg, const BYTE *pbData,
617 DWORD cbData, BOOL fFinal)
619 CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
621 TRACE("(%p, %p, %d, %d)\n", hCryptMsg, pbData, cbData, fFinal);
622 return msg->update(hCryptMsg, pbData, cbData, fFinal);
625 BOOL WINAPI CryptMsgGetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
626 DWORD dwIndex, void *pvData, DWORD *pcbData)
628 CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
630 TRACE("(%p, %d, %d, %p, %p)\n", hCryptMsg, dwParamType, dwIndex,
631 pvData, pcbData);
632 return msg->get_param(hCryptMsg, dwParamType, dwIndex, pvData, pcbData);