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
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
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
46 CMSG_STREAM_INFO stream_info
;
48 CryptMsgCloseFunc close
;
49 CryptMsgUpdateFunc update
;
50 CryptMsgGetParamFunc get_param
;
53 static inline void CryptMsgBase_Init(CryptMsgBase
*msg
, DWORD dwFlags
,
54 PCMSG_STREAM_INFO pStreamInfo
, CryptMsgCloseFunc close
,
55 CryptMsgGetParamFunc get_param
, CryptMsgUpdateFunc update
)
58 msg
->open_flags
= dwFlags
;
62 memcpy(&msg
->stream_info
, pStreamInfo
, sizeof(msg
->stream_info
));
66 msg
->streamed
= FALSE
;
67 memset(&msg
->stream_info
, 0, sizeof(msg
->stream_info
));
70 msg
->get_param
= get_param
;
72 msg
->finalized
= FALSE
;
75 typedef struct _CDataEncodeMsg
78 DWORD bare_content_len
;
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
;
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
);
107 *pcbEncoded
= 1 + lenBytes
+ msg
->base
.stream_info
.cbContent
;
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
,
123 static BOOL
CRYPT_EncodeDataContentInfoHeader(CDataEncodeMsg
*msg
,
124 CRYPT_DATA_BLOB
*header
)
128 if (msg
->base
.streamed
&& msg
->base
.stream_info
.cbContent
== 0xffffffff)
130 FIXME("unimplemented for indefinite-length encoding\n");
132 header
->pbData
= NULL
;
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
);
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
;
158 static BOOL
CDataEncodeMsg_Update(HCRYPTMSG hCryptMsg
, const BYTE
*pbData
,
159 DWORD cbData
, BOOL fFinal
)
161 CDataEncodeMsg
*msg
= (CDataEncodeMsg
*)hCryptMsg
;
164 if (msg
->base
.finalized
)
165 SetLastError(CRYPT_E_MSG_ERROR
);
166 else if (msg
->base
.streamed
)
169 msg
->base
.finalized
= TRUE
;
174 CRYPT_DATA_BLOB header
;
177 ret
= CRYPT_EncodeDataContentInfoHeader(msg
, &header
);
180 ret
= msg
->base
.stream_info
.pfnStreamOutput(
181 msg
->base
.stream_info
.pvArg
, header
.pbData
, header
.cbData
,
183 LocalFree(header
.pbData
);
187 ret
= msg
->base
.stream_info
.pfnStreamOutput(
188 msg
->base
.stream_info
.pvArg
, (BYTE
*)pbData
, cbData
,
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
,
200 ret
= msg
->base
.stream_info
.pfnStreamOutput(
201 msg
->base
.stream_info
.pvArg
, indefinite_trailer
,
202 sizeof(indefinite_trailer
), TRUE
);
205 ret
= msg
->base
.stream_info
.pfnStreamOutput(
206 msg
->base
.stream_info
.pvArg
, (BYTE
*)pbData
, cbData
, TRUE
);
211 SetLastError(STATUS_ACCESS_VIOLATION
);
219 if (msg
->base
.open_flags
& CMSG_DETACHED_FLAG
)
220 SetLastError(E_INVALIDARG
);
222 SetLastError(CRYPT_E_MSG_ERROR
);
226 msg
->base
.finalized
= TRUE
;
228 SetLastError(E_INVALIDARG
);
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
);
245 static BOOL
CRYPT_CopyParam(void *pvData
, DWORD
*pcbData
, const BYTE
*src
,
252 else if (*pcbData
< len
)
255 SetLastError(ERROR_MORE_DATA
);
261 memcpy(pvData
, src
, len
);
266 static BOOL
CDataEncodeMsg_GetParam(HCRYPTMSG hCryptMsg
, DWORD dwParamType
,
267 DWORD dwIndex
, void *pvData
, DWORD
*pcbData
)
269 CDataEncodeMsg
*msg
= (CDataEncodeMsg
*)hCryptMsg
;
274 case CMSG_CONTENT_PARAM
:
275 if (msg
->base
.streamed
)
276 SetLastError(E_INVALIDARG
);
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
,
289 case CMSG_BARE_CONTENT_PARAM
:
290 if (msg
->base
.streamed
)
291 SetLastError(E_INVALIDARG
);
293 ret
= CRYPT_CopyParam(pvData
, pcbData
, msg
->bare_content
,
294 msg
->bare_content_len
);
297 SetLastError(CRYPT_E_INVALID_MSG_TYPE
);
302 static HCRYPTMSG
CDataEncodeMsg_Open(DWORD dwFlags
, const void *pvMsgEncodeInfo
,
303 LPSTR pszInnerContentObjID
, PCMSG_STREAM_INFO pStreamInfo
)
309 SetLastError(E_INVALIDARG
);
312 msg
= CryptMemAlloc(sizeof(CDataEncodeMsg
));
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
;
321 return (HCRYPTMSG
)msg
;
324 typedef struct _CHashEncodeMsg
329 CRYPT_DATA_BLOB data
;
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
;
348 TRACE("(%p, %d, %d, %p, %p)\n", hCryptMsg
, dwParamType
, dwIndex
,
353 case CMSG_COMPUTED_HASH_PARAM
:
354 ret
= CryptGetHashParam(msg
->hash
, HP_HASHVAL
, (BYTE
*)pvData
, pcbData
,
357 case CMSG_VERSION_PARAM
:
358 if (!msg
->base
.finalized
)
359 SetLastError(CRYPT_E_MSG_ERROR
);
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
,
370 FIXME("%d: stub\n", dwParamType
);
376 static BOOL
CHashEncodeMsg_Update(HCRYPTMSG hCryptMsg
, const BYTE
*pbData
,
377 DWORD cbData
, BOOL fFinal
)
379 CHashEncodeMsg
*msg
= (CHashEncodeMsg
*)hCryptMsg
;
382 TRACE("(%p, %p, %d, %d)\n", hCryptMsg
, pbData
, cbData
, fFinal
);
384 if (msg
->base
.finalized
)
385 SetLastError(CRYPT_E_MSG_ERROR
);
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);
400 SetLastError(CRYPT_E_MSG_ERROR
);
403 ret
= CryptHashData(msg
->hash
, pbData
, cbData
, 0);
406 msg
->data
.pbData
= CryptMemAlloc(cbData
);
407 if (msg
->data
.pbData
)
409 memcpy(msg
->data
.pbData
+ msg
->data
.cbData
, pbData
,
411 msg
->data
.cbData
+= cbData
;
422 static HCRYPTMSG
CHashEncodeMsg_Open(DWORD dwFlags
, const void *pvMsgEncodeInfo
,
423 LPSTR pszInnerContentObjID
, PCMSG_STREAM_INFO pStreamInfo
)
426 const CMSG_HASHED_ENCODE_INFO
*info
=
427 (const CMSG_HASHED_ENCODE_INFO
*)pvMsgEncodeInfo
;
431 if (info
->cbSize
!= sizeof(CMSG_HASHED_ENCODE_INFO
))
433 SetLastError(E_INVALIDARG
);
436 if (!(algID
= CertOIDToAlgId(info
->HashAlgorithm
.pszObjId
)))
438 SetLastError(CRYPT_E_UNKNOWN_ALGO
);
441 if (info
->hCryptProv
)
442 prov
= info
->hCryptProv
;
445 prov
= CRYPT_GetDefaultProvider();
446 dwFlags
&= ~CMSG_CRYPT_RELEASE_CONTEXT_FLAG
;
448 msg
= CryptMemAlloc(sizeof(CHashEncodeMsg
));
451 CryptMsgBase_Init((CryptMsgBase
*)msg
, dwFlags
, pStreamInfo
,
452 CHashEncodeMsg_Close
, CHashEncodeMsg_GetParam
, CHashEncodeMsg_Update
);
454 msg
->data
.cbData
= 0;
455 msg
->data
.pbData
= NULL
;
456 if (!CryptCreateHash(prov
, algID
, 0, 0, &msg
->hash
))
462 return (HCRYPTMSG
)msg
;
465 static inline const char *MSG_TYPE_STR(DWORD type
)
469 #define _x(x) case (x): return #x
473 _x(CMSG_SIGNED_AND_ENVELOPED
);
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
);
499 msg
= CDataEncodeMsg_Open(dwFlags
, pvMsgEncodeInfo
,
500 pszInnerContentObjID
, pStreamInfo
);
503 msg
= CHashEncodeMsg_Open(dwFlags
, pvMsgEncodeInfo
,
504 pszInnerContentObjID
, pStreamInfo
);
508 FIXME("unimplemented for type %s\n", MSG_TYPE_STR(dwMsgType
));
510 case CMSG_SIGNED_AND_ENVELOPED
:
512 /* defined but invalid, fall through */
514 SetLastError(CRYPT_E_INVALID_MSG_TYPE
);
519 typedef struct _CDecodeMsg
523 HCRYPTPROV crypt_prov
;
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
);
541 static BOOL
CDecodeMsg_GetParam(HCRYPTMSG hCryptMsg
, DWORD dwParamType
,
542 DWORD dwIndex
, void *pvData
, DWORD
*pcbData
)
544 CDecodeMsg
*msg
= (CDecodeMsg
*)hCryptMsg
;
549 case CMSG_TYPE_PARAM
:
550 ret
= CRYPT_CopyParam(pvData
, pcbData
, (const BYTE
*)&msg
->type
,
554 FIXME("unimplemented for parameter %d\n", dwParamType
);
555 SetLastError(CRYPT_E_INVALID_MSG_TYPE
);
560 HCRYPTMSG WINAPI
CryptMsgOpenToDecode(DWORD dwMsgEncodingType
, DWORD dwFlags
,
561 DWORD dwMsgType
, HCRYPTPROV hCryptProv
, PCERT_INFO pRecipientInfo
,
562 PCMSG_STREAM_INFO pStreamInfo
)
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
);
574 msg
= CryptMemAlloc(sizeof(CDecodeMsg
));
577 CryptMsgBase_Init((CryptMsgBase
*)msg
, dwFlags
, pStreamInfo
,
578 CDecodeMsg_Close
, CDecodeMsg_GetParam
, CDecodeMsg_Update
);
579 msg
->type
= dwMsgType
;
584 HCRYPTMSG WINAPI
CryptMsgDuplicate(HCRYPTMSG hCryptMsg
)
586 TRACE("(%p)\n", hCryptMsg
);
590 CryptMsgBase
*msg
= (CryptMsgBase
*)hCryptMsg
;
592 InterlockedIncrement(&msg
->ref
);
597 BOOL WINAPI
CryptMsgClose(HCRYPTMSG hCryptMsg
)
599 TRACE("(%p)\n", hCryptMsg
);
603 CryptMsgBase
*msg
= (CryptMsgBase
*)hCryptMsg
;
605 if (InterlockedDecrement(&msg
->ref
) == 0)
607 TRACE("freeing %p\n", msg
);
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
,
632 return msg
->get_param(hCryptMsg
, dwParamType
, dwIndex
, pvData
, pcbData
);