crypt32: Implement CryptMsgUpdate for data messages opened to encode.
[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
23 #include "wine/debug.h"
24
25 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
26
27 /* Called when a message's ref count reaches zero.  Free any message-specific
28  * data here.
29  */
30 typedef void (*CryptMsgCloseFunc)(HCRYPTMSG msg);
31
32 typedef BOOL (*CryptMsgGetParamFunc)(HCRYPTMSG hCryptMsg, DWORD dwParamType,
33  DWORD dwIndex, void *pvData, DWORD *pcbData);
34
35 typedef BOOL (*CryptMsgUpdateFunc)(HCRYPTMSG hCryptMsg, const BYTE *pbData,
36  DWORD cbData, BOOL fFinal);
37
38 typedef struct _CryptMsgBase
39 {
40     LONG                 ref;
41     DWORD                open_flags;
42     PCMSG_STREAM_INFO    stream_info;
43     BOOL                 finalized;
44     CryptMsgCloseFunc    close;
45     CryptMsgUpdateFunc   update;
46     CryptMsgGetParamFunc get_param;
47 } CryptMsgBase;
48
49 static inline void CryptMsgBase_Init(CryptMsgBase *msg, DWORD dwFlags,
50  PCMSG_STREAM_INFO pStreamInfo)
51 {
52     msg->ref = 1;
53     msg->open_flags = dwFlags;
54     msg->stream_info = pStreamInfo;
55     msg->finalized = FALSE;
56 }
57
58 typedef struct _CDataEncodeMsg
59 {
60     CryptMsgBase base;
61     DWORD        bare_content_len;
62     LPBYTE       bare_content;
63 } CDataEncodeMsg;
64
65 static const BYTE empty_data_content[] = { 0x04,0x00 };
66
67 static void CDataEncodeMsg_Close(HCRYPTMSG hCryptMsg)
68 {
69     CDataEncodeMsg *msg = (CDataEncodeMsg *)hCryptMsg;
70
71     if (msg->bare_content != empty_data_content)
72         LocalFree(msg->bare_content);
73 }
74
75 static BOOL CDataEncodeMsg_Update(HCRYPTMSG hCryptMsg, const BYTE *pbData,
76  DWORD cbData, BOOL fFinal)
77 {
78     CDataEncodeMsg *msg = (CDataEncodeMsg *)hCryptMsg;
79     BOOL ret = FALSE;
80
81     if (msg->base.finalized)
82         SetLastError(CRYPT_E_MSG_ERROR);
83     else if (!fFinal)
84     {
85         if (msg->base.open_flags & CMSG_DETACHED_FLAG)
86             SetLastError(E_INVALIDARG);
87         else
88             SetLastError(CRYPT_E_MSG_ERROR);
89     }
90     else
91     {
92         msg->base.finalized = TRUE;
93         if (!cbData)
94             SetLastError(E_INVALIDARG);
95         else
96         {
97             CRYPT_DATA_BLOB blob = { cbData, (LPBYTE)pbData };
98
99             /* data messages don't allow non-final updates, don't bother
100              * checking whether data already exist, they can't.
101              */
102             ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_OCTET_STRING,
103              &blob, CRYPT_ENCODE_ALLOC_FLAG, NULL, &msg->bare_content,
104              &msg->bare_content_len);
105             if (ret && msg->base.stream_info)
106                 FIXME("stream info unimplemented\n");
107         }
108     }
109     return ret;
110 }
111
112 static HCRYPTMSG CDataEncodeMsg_Open(DWORD dwFlags, const void *pvMsgEncodeInfo,
113  LPSTR pszInnerContentObjID, PCMSG_STREAM_INFO pStreamInfo)
114 {
115     CDataEncodeMsg *msg;
116
117     if (pvMsgEncodeInfo)
118     {
119         SetLastError(E_INVALIDARG);
120         return NULL;
121     }
122     FIXME("semi-stub\n");
123     msg = CryptMemAlloc(sizeof(CDataEncodeMsg));
124     if (msg)
125     {
126         CryptMsgBase_Init((CryptMsgBase *)msg, dwFlags, pStreamInfo);
127         msg->base.close = CDataEncodeMsg_Close;
128         msg->base.update = CDataEncodeMsg_Update;
129         msg->bare_content_len = sizeof(empty_data_content);
130         msg->bare_content = (LPBYTE)empty_data_content;
131     }
132     return (HCRYPTMSG)msg;
133 }
134
135 static inline const char *MSG_TYPE_STR(DWORD type)
136 {
137     switch (type)
138     {
139 #define _x(x) case (x): return #x
140         _x(CMSG_DATA);
141         _x(CMSG_SIGNED);
142         _x(CMSG_ENVELOPED);
143         _x(CMSG_SIGNED_AND_ENVELOPED);
144         _x(CMSG_HASHED);
145         _x(CMSG_ENCRYPTED);
146 #undef _x
147         default:
148             return wine_dbg_sprintf("unknown (%d)", type);
149     }
150 }
151
152 HCRYPTMSG WINAPI CryptMsgOpenToEncode(DWORD dwMsgEncodingType, DWORD dwFlags,
153  DWORD dwMsgType, const void *pvMsgEncodeInfo, LPSTR pszInnerContentObjID,
154  PCMSG_STREAM_INFO pStreamInfo)
155 {
156     HCRYPTMSG msg = NULL;
157
158     TRACE("(%08x, %08x, %08x, %p, %s, %p)\n", dwMsgEncodingType, dwFlags,
159      dwMsgType, pvMsgEncodeInfo, debugstr_a(pszInnerContentObjID), pStreamInfo);
160
161     if (GET_CMSG_ENCODING_TYPE(dwMsgEncodingType) != PKCS_7_ASN_ENCODING)
162     {
163         SetLastError(E_INVALIDARG);
164         return NULL;
165     }
166     switch (dwMsgType)
167     {
168     case CMSG_DATA:
169         msg = CDataEncodeMsg_Open(dwFlags, pvMsgEncodeInfo,
170          pszInnerContentObjID, pStreamInfo);
171         break;
172     case CMSG_SIGNED:
173     case CMSG_ENVELOPED:
174     case CMSG_HASHED:
175         FIXME("unimplemented for type %s\n", MSG_TYPE_STR(dwMsgType));
176         break;
177     case CMSG_SIGNED_AND_ENVELOPED:
178     case CMSG_ENCRYPTED:
179         /* defined but invalid, fall through */
180     default:
181         SetLastError(CRYPT_E_INVALID_MSG_TYPE);
182     }
183     return msg;
184 }
185
186 HCRYPTMSG WINAPI CryptMsgOpenToDecode(DWORD dwMsgEncodingType, DWORD dwFlags,
187  DWORD dwMsgType, HCRYPTPROV hCryptProv, PCERT_INFO pRecipientInfo,
188  PCMSG_STREAM_INFO pStreamInfo)
189 {
190     FIXME("(%08x, %08x, %08x, %08lx, %p, %p): stub\n", dwMsgEncodingType,
191      dwFlags, dwMsgType, hCryptProv, pRecipientInfo, pStreamInfo);
192
193     if (GET_CMSG_ENCODING_TYPE(dwMsgEncodingType) != PKCS_7_ASN_ENCODING)
194     {
195         SetLastError(E_INVALIDARG);
196         return NULL;
197     }
198     return NULL;
199 }
200
201 HCRYPTMSG WINAPI CryptMsgDuplicate(HCRYPTMSG hCryptMsg)
202 {
203     TRACE("(%p)\n", hCryptMsg);
204
205     if (hCryptMsg)
206     {
207         CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
208
209         InterlockedIncrement(&msg->ref);
210     }
211     return hCryptMsg;
212 }
213
214 BOOL WINAPI CryptMsgClose(HCRYPTMSG hCryptMsg)
215 {
216     TRACE("(%p)\n", hCryptMsg);
217
218     if (hCryptMsg)
219     {
220         CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
221
222         if (InterlockedDecrement(&msg->ref) == 0)
223         {
224             TRACE("freeing %p\n", msg);
225             if (msg->close)
226                 msg->close(msg);
227             CryptMemFree(msg);
228         }
229     }
230     return TRUE;
231 }
232
233 BOOL WINAPI CryptMsgUpdate(HCRYPTMSG hCryptMsg, const BYTE *pbData,
234  DWORD cbData, BOOL fFinal)
235 {
236     CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
237     BOOL ret = FALSE;
238
239     TRACE("(%p, %p, %d, %d)\n", hCryptMsg, pbData, cbData, fFinal);
240     if (msg && msg->update)
241         ret = msg->update(hCryptMsg, pbData, cbData, fFinal);
242     return ret;
243 }
244
245 BOOL WINAPI CryptMsgGetParam(HCRYPTMSG hCryptMsg, DWORD dwParamType,
246  DWORD dwIndex, void *pvData, DWORD *pcbData)
247 {
248     CryptMsgBase *msg = (CryptMsgBase *)hCryptMsg;
249     BOOL ret = FALSE;
250
251     TRACE("(%p, %d, %d, %p, %p)\n", hCryptMsg, dwParamType, dwIndex,
252      pvData, pcbData);
253     if (msg && msg->get_param)
254         ret = msg->get_param(hCryptMsg, dwParamType, dwIndex, pvData, pcbData);
255     return ret;
256 }