crypt32: Implement CryptFormatObject for szOID_AUTHORITY_KEY_IDENTIFIER2.
[wine] / dlls / crypt32 / object.c
1 /*
2  * crypt32 Crypt*Object functions
3  *
4  * Copyright 2007 Juan Lang
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 #include <stdarg.h>
21 #define NONAMELESSUNION
22 #include "windef.h"
23 #include "winbase.h"
24 #include "wincrypt.h"
25 #include "mssip.h"
26 #include "winuser.h"
27 #include "crypt32_private.h"
28 #include "cryptres.h"
29 #include "wine/unicode.h"
30 #include "wine/debug.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
33
34 static BOOL CRYPT_ReadBlobFromFile(LPCWSTR fileName, PCERT_BLOB blob)
35 {
36     BOOL ret = FALSE;
37     HANDLE file;
38
39     TRACE("%s\n", debugstr_w(fileName));
40
41     file = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL,
42      OPEN_EXISTING, 0, NULL);
43     if (file != INVALID_HANDLE_VALUE)
44     {
45         ret = TRUE;
46         blob->cbData = GetFileSize(file, NULL);
47         if (blob->cbData)
48         {
49             blob->pbData = CryptMemAlloc(blob->cbData);
50             if (blob->pbData)
51             {
52                 DWORD read;
53
54                 ret = ReadFile(file, blob->pbData, blob->cbData, &read, NULL);
55             }
56         }
57         CloseHandle(file);
58     }
59     TRACE("returning %d\n", ret);
60     return ret;
61 }
62
63 static BOOL CRYPT_QueryContextObject(DWORD dwObjectType, const void *pvObject,
64  DWORD dwExpectedContentTypeFlags, DWORD *pdwMsgAndCertEncodingType,
65  DWORD *pdwContentType, HCERTSTORE *phCertStore, const void **ppvContext)
66 {
67     CERT_BLOB fileBlob;
68     const CERT_BLOB *blob;
69     HCERTSTORE store;
70     DWORD contentType;
71     BOOL ret;
72
73     switch (dwObjectType)
74     {
75     case CERT_QUERY_OBJECT_FILE:
76         /* Cert, CRL, and CTL contexts can't be "embedded" in a file, so
77          * just read the file directly
78          */
79         ret = CRYPT_ReadBlobFromFile((LPCWSTR)pvObject, &fileBlob);
80         blob = &fileBlob;
81         break;
82     case CERT_QUERY_OBJECT_BLOB:
83         blob = (const CERT_BLOB *)pvObject;
84         ret = TRUE;
85         break;
86     default:
87         SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */
88         ret = FALSE;
89     }
90     if (!ret)
91         return FALSE;
92
93     store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
94      CERT_STORE_CREATE_NEW_FLAG, NULL);
95     ret = FALSE;
96     if (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CERT)
97     {
98         ret = pCertInterface->addEncodedToStore(store, X509_ASN_ENCODING,
99          blob->pbData, blob->cbData, CERT_STORE_ADD_ALWAYS, ppvContext);
100         if (ret)
101             contentType = CERT_QUERY_CONTENT_CERT;
102     }
103     if (!ret && (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CRL))
104     {
105         ret = pCRLInterface->addEncodedToStore(store, X509_ASN_ENCODING,
106          blob->pbData, blob->cbData, CERT_STORE_ADD_ALWAYS, ppvContext);
107         if (ret)
108             contentType = CERT_QUERY_CONTENT_CRL;
109     }
110     if (!ret && (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CTL))
111     {
112         ret = pCTLInterface->addEncodedToStore(store, X509_ASN_ENCODING,
113          blob->pbData, blob->cbData, CERT_STORE_ADD_ALWAYS, ppvContext);
114         if (ret)
115             contentType = CERT_QUERY_CONTENT_CTL;
116     }
117     if (ret)
118     {
119         if (pdwMsgAndCertEncodingType)
120             *pdwMsgAndCertEncodingType = X509_ASN_ENCODING;
121         if (pdwContentType)
122             *pdwContentType = contentType;
123         if (phCertStore)
124             *phCertStore = CertDuplicateStore(store);
125     }
126     CertCloseStore(store, 0);
127     if (blob == &fileBlob)
128         CryptMemFree(blob->pbData);
129     TRACE("returning %d\n", ret);
130     return ret;
131 }
132
133 static BOOL CRYPT_QuerySerializedContextObject(DWORD dwObjectType,
134  const void *pvObject, DWORD dwExpectedContentTypeFlags,
135  DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType,
136  HCERTSTORE *phCertStore, const void **ppvContext)
137 {
138     CERT_BLOB fileBlob;
139     const CERT_BLOB *blob;
140     const WINE_CONTEXT_INTERFACE *contextInterface = NULL;
141     const void *context;
142     DWORD contextType;
143     BOOL ret;
144
145     switch (dwObjectType)
146     {
147     case CERT_QUERY_OBJECT_FILE:
148         /* Cert, CRL, and CTL contexts can't be "embedded" in a file, so
149          * just read the file directly
150          */
151         ret = CRYPT_ReadBlobFromFile((LPCWSTR)pvObject, &fileBlob);
152         blob = &fileBlob;
153         break;
154     case CERT_QUERY_OBJECT_BLOB:
155         blob = (const CERT_BLOB *)pvObject;
156         ret = TRUE;
157         break;
158     default:
159         SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */
160         ret = FALSE;
161     }
162     if (!ret)
163         return FALSE;
164
165     context = CRYPT_ReadSerializedElement(blob->pbData, blob->cbData,
166      CERT_STORE_ALL_CONTEXT_FLAG, &contextType);
167     if (context)
168     {
169         DWORD contentType, certStoreOffset;
170
171         ret = TRUE;
172         switch (contextType)
173         {
174         case CERT_STORE_CERTIFICATE_CONTEXT:
175             contextInterface = pCertInterface;
176             contentType = CERT_QUERY_CONTENT_SERIALIZED_CERT;
177             certStoreOffset = offsetof(CERT_CONTEXT, hCertStore);
178             if (!(dwExpectedContentTypeFlags &
179              CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT))
180             {
181                 SetLastError(ERROR_INVALID_DATA);
182                 ret = FALSE;
183                 goto end;
184             }
185             break;
186         case CERT_STORE_CRL_CONTEXT:
187             contextInterface = pCRLInterface;
188             contentType = CERT_QUERY_CONTENT_SERIALIZED_CRL;
189             certStoreOffset = offsetof(CRL_CONTEXT, hCertStore);
190             if (!(dwExpectedContentTypeFlags &
191              CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL))
192             {
193                 SetLastError(ERROR_INVALID_DATA);
194                 ret = FALSE;
195                 goto end;
196             }
197             break;
198         case CERT_STORE_CTL_CONTEXT:
199             contextInterface = pCTLInterface;
200             contentType = CERT_QUERY_CONTENT_SERIALIZED_CTL;
201             certStoreOffset = offsetof(CTL_CONTEXT, hCertStore);
202             if (!(dwExpectedContentTypeFlags &
203              CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL))
204             {
205                 SetLastError(ERROR_INVALID_DATA);
206                 ret = FALSE;
207                 goto end;
208             }
209             break;
210         default:
211             SetLastError(ERROR_INVALID_DATA);
212             ret = FALSE;
213             goto end;
214         }
215         if (pdwMsgAndCertEncodingType)
216             *pdwMsgAndCertEncodingType = X509_ASN_ENCODING;
217         if (pdwContentType)
218             *pdwContentType = contentType;
219         if (phCertStore)
220             *phCertStore = CertDuplicateStore(
221              *(HCERTSTORE *)((const BYTE *)context + certStoreOffset));
222         if (ppvContext)
223             *ppvContext = contextInterface->duplicate(context);
224     }
225
226 end:
227     if (contextInterface && context)
228         contextInterface->free(context);
229     if (blob == &fileBlob)
230         CryptMemFree(blob->pbData);
231     TRACE("returning %d\n", ret);
232     return ret;
233 }
234
235 static BOOL CRYPT_QuerySerializedStoreObject(DWORD dwObjectType,
236  const void *pvObject, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType,
237  HCERTSTORE *phCertStore, HCRYPTMSG *phMsg)
238 {
239     LPCWSTR fileName = (LPCWSTR)pvObject;
240     HANDLE file;
241     BOOL ret = FALSE;
242
243     if (dwObjectType != CERT_QUERY_OBJECT_FILE)
244     {
245         FIXME("unimplemented for non-file type %d\n", dwObjectType);
246         SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */
247         return FALSE;
248     }
249     TRACE("%s\n", debugstr_w(fileName));
250     file = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL,
251      OPEN_EXISTING, 0, NULL);
252     if (file != INVALID_HANDLE_VALUE)
253     {
254         HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
255          CERT_STORE_CREATE_NEW_FLAG, NULL);
256
257         ret = CRYPT_ReadSerializedStoreFromFile(file, store);
258         if (ret)
259         {
260             if (pdwMsgAndCertEncodingType)
261                 *pdwMsgAndCertEncodingType = X509_ASN_ENCODING;
262             if (pdwContentType)
263                 *pdwContentType = CERT_QUERY_CONTENT_SERIALIZED_STORE;
264             if (phCertStore)
265                 *phCertStore = CertDuplicateStore(store);
266         }
267         CertCloseStore(store, 0);
268         CloseHandle(file);
269     }
270     TRACE("returning %d\n", ret);
271     return ret;
272 }
273
274 /* Used to decode non-embedded messages */
275 static BOOL CRYPT_QueryMessageObject(DWORD dwObjectType, const void *pvObject,
276  DWORD dwExpectedContentTypeFlags, DWORD *pdwMsgAndCertEncodingType,
277  DWORD *pdwContentType, HCERTSTORE *phCertStore, HCRYPTMSG *phMsg)
278 {
279     CERT_BLOB fileBlob;
280     const CERT_BLOB *blob;
281     BOOL ret;
282     HCRYPTMSG msg = NULL;
283     DWORD encodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
284
285     switch (dwObjectType)
286     {
287     case CERT_QUERY_OBJECT_FILE:
288         /* This isn't an embedded PKCS7 message, so just read the file
289          * directly
290          */
291         ret = CRYPT_ReadBlobFromFile((LPCWSTR)pvObject, &fileBlob);
292         blob = &fileBlob;
293         break;
294     case CERT_QUERY_OBJECT_BLOB:
295         blob = (const CERT_BLOB *)pvObject;
296         ret = TRUE;
297         break;
298     default:
299         SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */
300         ret = FALSE;
301     }
302     if (!ret)
303         return FALSE;
304
305     ret = FALSE;
306     /* Try it first as a PKCS content info */
307     if ((dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED) ||
308      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED))
309     {
310         msg = CryptMsgOpenToDecode(encodingType, 0, 0, 0, NULL, NULL);
311         if (msg)
312         {
313             ret = CryptMsgUpdate(msg, blob->pbData, blob->cbData, TRUE);
314             if (ret)
315             {
316                 DWORD type, len = sizeof(type);
317
318                 ret = CryptMsgGetParam(msg, CMSG_TYPE_PARAM, 0, &type, &len);
319                 if (ret)
320                 {
321                     if ((dwExpectedContentTypeFlags &
322                      CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED))
323                     {
324                         if (type != CMSG_SIGNED)
325                         {
326                             SetLastError(ERROR_INVALID_DATA);
327                             ret = FALSE;
328                         }
329                         else if (pdwContentType)
330                             *pdwContentType = CERT_QUERY_CONTENT_PKCS7_SIGNED;
331                     }
332                     else if ((dwExpectedContentTypeFlags &
333                      CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED))
334                     {
335                         if (type != CMSG_DATA)
336                         {
337                             SetLastError(ERROR_INVALID_DATA);
338                             ret = FALSE;
339                         }
340                         else if (pdwContentType)
341                             *pdwContentType = CERT_QUERY_CONTENT_PKCS7_UNSIGNED;
342                     }
343                 }
344             }
345             if (!ret)
346             {
347                 CryptMsgClose(msg);
348                 msg = NULL;
349             }
350         }
351     }
352     /* Failing that, try explicitly typed messages */
353     if (!ret &&
354      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED))
355     {
356         msg = CryptMsgOpenToDecode(encodingType, 0, CMSG_SIGNED, 0, NULL, NULL);
357         if (msg)
358         {
359             ret = CryptMsgUpdate(msg, blob->pbData, blob->cbData, TRUE);
360             if (!ret)
361             {
362                 CryptMsgClose(msg);
363                 msg = NULL;
364             }
365         }
366         if (msg && pdwContentType)
367             *pdwContentType = CERT_QUERY_CONTENT_PKCS7_SIGNED;
368     }
369     if (!ret &&
370      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED))
371     {
372         msg = CryptMsgOpenToDecode(encodingType, 0, CMSG_DATA, 0, NULL, NULL);
373         if (msg)
374         {
375             ret = CryptMsgUpdate(msg, blob->pbData, blob->cbData, TRUE);
376             if (!ret)
377             {
378                 CryptMsgClose(msg);
379                 msg = NULL;
380             }
381         }
382         if (msg && pdwContentType)
383             *pdwContentType = CERT_QUERY_CONTENT_PKCS7_UNSIGNED;
384     }
385     if (pdwMsgAndCertEncodingType)
386         *pdwMsgAndCertEncodingType = encodingType;
387     if (msg)
388     {
389         if (phMsg)
390             *phMsg = msg;
391         if (phCertStore)
392             *phCertStore = CertOpenStore(CERT_STORE_PROV_MSG, encodingType, 0,
393              0, msg);
394     }
395     if (blob == &fileBlob)
396         CryptMemFree(blob->pbData);
397     TRACE("returning %d\n", ret);
398     return ret;
399 }
400
401 static BOOL CRYPT_QueryEmbeddedMessageObject(DWORD dwObjectType,
402  const void *pvObject, DWORD dwExpectedContentTypeFlags,
403  DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType,
404  HCERTSTORE *phCertStore, HCRYPTMSG *phMsg)
405 {
406     HANDLE file;
407     GUID subject;
408     BOOL ret = FALSE;
409
410     TRACE("%s\n", debugstr_w((LPCWSTR)pvObject));
411
412     if (dwObjectType != CERT_QUERY_OBJECT_FILE)
413     {
414         FIXME("don't know what to do for type %d embedded signed messages\n",
415          dwObjectType);
416         SetLastError(E_INVALIDARG);
417         return FALSE;
418     }
419     file = CreateFileW((LPCWSTR)pvObject, GENERIC_READ, FILE_SHARE_READ,
420      NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
421     if (file != INVALID_HANDLE_VALUE)
422     {
423         ret = CryptSIPRetrieveSubjectGuid((LPCWSTR)pvObject, file, &subject);
424         if (ret)
425         {
426             SIP_DISPATCH_INFO sip;
427
428             memset(&sip, 0, sizeof(sip));
429             sip.cbSize = sizeof(sip);
430             ret = CryptSIPLoad(&subject, 0, &sip);
431             if (ret)
432             {
433                 SIP_SUBJECTINFO subjectInfo;
434                 CERT_BLOB blob;
435                 DWORD encodingType;
436
437                 memset(&subjectInfo, 0, sizeof(subjectInfo));
438                 subjectInfo.cbSize = sizeof(subjectInfo);
439                 subjectInfo.pgSubjectType = &subject;
440                 subjectInfo.hFile = file;
441                 subjectInfo.pwsFileName = (LPCWSTR)pvObject;
442                 ret = sip.pfGet(&subjectInfo, &encodingType, 0, &blob.cbData,
443                  NULL);
444                 if (ret)
445                 {
446                     blob.pbData = CryptMemAlloc(blob.cbData);
447                     if (blob.pbData)
448                     {
449                         ret = sip.pfGet(&subjectInfo, &encodingType, 0,
450                          &blob.cbData, blob.pbData);
451                         if (ret)
452                         {
453                             ret = CRYPT_QueryMessageObject(
454                              CERT_QUERY_OBJECT_BLOB, &blob,
455                              CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED,
456                              pdwMsgAndCertEncodingType, NULL, phCertStore,
457                              phMsg);
458                             if (ret && pdwContentType)
459                                 *pdwContentType =
460                                  CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED;
461                         }
462                         CryptMemFree(blob.pbData);
463                     }
464                     else
465                     {
466                         SetLastError(ERROR_OUTOFMEMORY);
467                         ret = FALSE;
468                     }
469                 }
470             }
471         }
472         CloseHandle(file);
473     }
474     TRACE("returning %d\n", ret);
475     return ret;
476 }
477
478 BOOL WINAPI CryptQueryObject(DWORD dwObjectType, const void *pvObject,
479  DWORD dwExpectedContentTypeFlags, DWORD dwExpectedFormatTypeFlags,
480  DWORD dwFlags, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType,
481  DWORD *pdwFormatType, HCERTSTORE *phCertStore, HCRYPTMSG *phMsg,
482  const void **ppvContext)
483 {
484     static const DWORD unimplementedTypes =
485      CERT_QUERY_CONTENT_FLAG_PKCS10 | CERT_QUERY_CONTENT_FLAG_PFX |
486      CERT_QUERY_CONTENT_FLAG_CERT_PAIR;
487     BOOL ret = TRUE;
488
489     TRACE("(%08x, %p, %08x, %08x, %08x, %p, %p, %p, %p, %p, %p)\n",
490      dwObjectType, pvObject, dwExpectedContentTypeFlags,
491      dwExpectedFormatTypeFlags, dwFlags, pdwMsgAndCertEncodingType,
492      pdwContentType, pdwFormatType, phCertStore, phMsg, ppvContext);
493
494     if (dwExpectedContentTypeFlags & unimplementedTypes)
495         WARN("unimplemented for types %08x\n",
496          dwExpectedContentTypeFlags & unimplementedTypes);
497     if (!(dwExpectedFormatTypeFlags & CERT_QUERY_FORMAT_FLAG_BINARY))
498     {
499         FIXME("unimplemented for anything but binary\n");
500         SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
501         return FALSE;
502     }
503     if (pdwFormatType)
504         *pdwFormatType = CERT_QUERY_FORMAT_BINARY;
505
506     if (phCertStore)
507         *phCertStore = NULL;
508     if (phMsg)
509         *phMsg = NULL;
510     if (ppvContext)
511         *ppvContext = NULL;
512
513     ret = FALSE;
514     if ((dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CERT) ||
515      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CRL) ||
516      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CTL))
517     {
518         ret = CRYPT_QueryContextObject(dwObjectType, pvObject,
519          dwExpectedContentTypeFlags, pdwMsgAndCertEncodingType, pdwContentType,
520          phCertStore, ppvContext);
521     }
522     if (!ret &&
523      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE))
524     {
525         ret = CRYPT_QuerySerializedStoreObject(dwObjectType, pvObject,
526          pdwMsgAndCertEncodingType, pdwContentType, phCertStore, phMsg);
527     }
528     if (!ret &&
529      ((dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT) ||
530      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL) ||
531      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL)))
532     {
533         ret = CRYPT_QuerySerializedContextObject(dwObjectType, pvObject,
534          dwExpectedContentTypeFlags, pdwMsgAndCertEncodingType, pdwContentType,
535          phCertStore, ppvContext);
536     }
537     if (!ret &&
538      ((dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED) ||
539      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED)))
540     {
541         ret = CRYPT_QueryMessageObject(dwObjectType, pvObject,
542          dwExpectedContentTypeFlags, pdwMsgAndCertEncodingType, pdwContentType,
543          phCertStore, phMsg);
544     }
545     if (!ret &&
546      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED))
547     {
548         ret = CRYPT_QueryEmbeddedMessageObject(dwObjectType, pvObject,
549          dwExpectedContentTypeFlags, pdwMsgAndCertEncodingType, pdwContentType,
550          phCertStore, phMsg);
551     }
552     TRACE("returning %d\n", ret);
553     return ret;
554 }
555
556 static BOOL WINAPI CRYPT_FormatHexString(DWORD dwCertEncodingType,
557  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
558  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
559  DWORD *pcbFormat)
560 {
561     BOOL ret;
562     DWORD bytesNeeded;
563
564     if (cbEncoded)
565         bytesNeeded = (cbEncoded * 3) * sizeof(WCHAR);
566     else
567         bytesNeeded = sizeof(WCHAR);
568     if (!pbFormat)
569     {
570         *pcbFormat = bytesNeeded;
571         ret = TRUE;
572     }
573     else if (*pcbFormat < bytesNeeded)
574     {
575         *pcbFormat = bytesNeeded;
576         SetLastError(ERROR_MORE_DATA);
577         ret = FALSE;
578     }
579     else
580     {
581         static const WCHAR fmt[] = { '%','0','2','x',' ',0 };
582         static const WCHAR endFmt[] = { '%','0','2','x',0 };
583         DWORD i;
584         LPWSTR ptr = pbFormat;
585
586         *pcbFormat = bytesNeeded;
587         if (cbEncoded)
588         {
589             for (i = 0; i < cbEncoded; i++)
590             {
591                 if (i < cbEncoded - 1)
592                     ptr += sprintfW(ptr, fmt, pbEncoded[i]);
593                 else
594                     ptr += sprintfW(ptr, endFmt, pbEncoded[i]);
595             }
596         }
597         else
598             *ptr = 0;
599         ret = TRUE;
600     }
601     return ret;
602 }
603
604 #define MAX_STRING_RESOURCE_LEN 128
605
606 static BOOL CRYPT_FormatHexStringWithPrefix(CRYPT_DATA_BLOB *blob, int id,
607  LPWSTR str, DWORD *pcbStr)
608 {
609     WCHAR buf[MAX_STRING_RESOURCE_LEN];
610     DWORD bytesNeeded;
611     BOOL ret;
612
613     LoadStringW(hInstance, id, buf, sizeof(buf) / sizeof(buf[0]));
614     CRYPT_FormatHexString(X509_ASN_ENCODING, 0, 0, NULL, NULL,
615      blob->pbData, blob->cbData, NULL, &bytesNeeded);
616     bytesNeeded += strlenW(buf) * sizeof(WCHAR);
617     if (!str)
618     {
619         *pcbStr = bytesNeeded;
620         ret = TRUE;
621     }
622     else if (*pcbStr < bytesNeeded)
623     {
624         *pcbStr = bytesNeeded;
625         SetLastError(ERROR_MORE_DATA);
626         ret = FALSE;
627     }
628     else
629     {
630         *pcbStr = bytesNeeded;
631         strcpyW(str, buf);
632         str += strlenW(str);
633         bytesNeeded -= strlenW(str) * sizeof(WCHAR);
634         ret = CRYPT_FormatHexString(X509_ASN_ENCODING, 0, 0, NULL, NULL,
635          blob->pbData, blob->cbData, str, &bytesNeeded);
636     }
637     return ret;
638 }
639
640 static BOOL CRYPT_FormatKeyId(CRYPT_DATA_BLOB *keyId, LPWSTR str,
641  DWORD *pcbStr)
642 {
643     return CRYPT_FormatHexStringWithPrefix(keyId, IDS_KEY_ID, str, pcbStr);
644 }
645
646 static BOOL CRYPT_FormatCertSerialNumber(CRYPT_DATA_BLOB *serialNum, LPWSTR str,
647  DWORD *pcbStr)
648 {
649     return CRYPT_FormatHexStringWithPrefix(serialNum, IDS_CERT_SERIAL_NUMBER,
650      str, pcbStr);
651 }
652
653 static const WCHAR crlf[] = { '\r','\n',0 };
654
655 static BOOL CRYPT_FormatAltNameEntry(DWORD dwFormatStrType,
656  CERT_ALT_NAME_ENTRY *entry, LPWSTR str, DWORD *pcbStr)
657 {
658     BOOL ret;
659     WCHAR buf[MAX_STRING_RESOURCE_LEN];
660     WCHAR mask[MAX_STRING_RESOURCE_LEN];
661     WCHAR ipAddrBuf[32];
662     WCHAR maskBuf[16];
663     DWORD bytesNeeded = sizeof(WCHAR);
664
665     switch (entry->dwAltNameChoice)
666     {
667     case CERT_ALT_NAME_RFC822_NAME:
668         LoadStringW(hInstance, IDS_ALT_NAME_RFC822_NAME, buf,
669          sizeof(buf) / sizeof(buf[0]));
670         bytesNeeded += strlenW(entry->u.pwszRfc822Name) * sizeof(WCHAR);
671         ret = TRUE;
672         break;
673     case CERT_ALT_NAME_DNS_NAME:
674         LoadStringW(hInstance, IDS_ALT_NAME_DNS_NAME, buf,
675          sizeof(buf) / sizeof(buf[0]));
676         bytesNeeded += strlenW(entry->u.pwszDNSName) * sizeof(WCHAR);
677         ret = TRUE;
678         break;
679     case CERT_ALT_NAME_URL:
680         LoadStringW(hInstance, IDS_ALT_NAME_URL, buf,
681          sizeof(buf) / sizeof(buf[0]));
682         bytesNeeded += strlenW(entry->u.pwszURL) * sizeof(WCHAR);
683         ret = TRUE;
684         break;
685     case CERT_ALT_NAME_IP_ADDRESS:
686     {
687         static const WCHAR ipAddrWithMaskFmt[] = { '%','d','.','%','d','.',
688          '%','d','.','%','d','/','%','d','.','%','d','.','%','d','.','%','d',0
689         };
690         static const WCHAR ipAddrFmt[] = { '%','d','.','%','d','.','%','d',
691          '.','%','d',0 };
692
693         LoadStringW(hInstance, IDS_ALT_NAME_IP_ADDRESS, buf,
694          sizeof(buf) / sizeof(buf[0]));
695         if (entry->u.IPAddress.cbData == 8)
696         {
697             if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
698             {
699                 LoadStringW(hInstance, IDS_ALT_NAME_MASK, mask,
700                  sizeof(mask) / sizeof(mask[0]));
701                 bytesNeeded += strlenW(mask) * sizeof(WCHAR);
702                 sprintfW(ipAddrBuf, ipAddrFmt,
703                  entry->u.IPAddress.pbData[0],
704                  entry->u.IPAddress.pbData[1],
705                  entry->u.IPAddress.pbData[2],
706                  entry->u.IPAddress.pbData[3]);
707                 bytesNeeded += strlenW(ipAddrBuf) * sizeof(WCHAR);
708                 sprintfW(maskBuf, ipAddrFmt,
709                  entry->u.IPAddress.pbData[4],
710                  entry->u.IPAddress.pbData[5],
711                  entry->u.IPAddress.pbData[6],
712                  entry->u.IPAddress.pbData[7]);
713                 bytesNeeded += strlenW(maskBuf) * sizeof(WCHAR);
714                 bytesNeeded += strlenW(crlf) * sizeof(WCHAR);
715             }
716             else
717             {
718                 sprintfW(ipAddrBuf, ipAddrWithMaskFmt,
719                  entry->u.IPAddress.pbData[0],
720                  entry->u.IPAddress.pbData[1],
721                  entry->u.IPAddress.pbData[2],
722                  entry->u.IPAddress.pbData[3],
723                  entry->u.IPAddress.pbData[4],
724                  entry->u.IPAddress.pbData[5],
725                  entry->u.IPAddress.pbData[6],
726                  entry->u.IPAddress.pbData[7]);
727                 bytesNeeded += (strlenW(ipAddrBuf) + 1) * sizeof(WCHAR);
728             }
729             ret = TRUE;
730         }
731         else
732         {
733             FIXME("unknown IP address format (%d bytes)\n",
734              entry->u.IPAddress.cbData);
735             ret = FALSE;
736         }
737         break;
738     }
739     default:
740         FIXME("unimplemented for %d\n", entry->dwAltNameChoice);
741         ret = FALSE;
742     }
743     if (ret)
744     {
745         bytesNeeded += strlenW(buf) * sizeof(WCHAR);
746         if (!str)
747             *pcbStr = bytesNeeded;
748         else if (*pcbStr < bytesNeeded)
749         {
750             *pcbStr = bytesNeeded;
751             SetLastError(ERROR_MORE_DATA);
752             ret = FALSE;
753         }
754         else
755         {
756             *pcbStr = bytesNeeded;
757             strcpyW(str, buf);
758             str += strlenW(str);
759             switch (entry->dwAltNameChoice)
760             {
761             case CERT_ALT_NAME_RFC822_NAME:
762             case CERT_ALT_NAME_DNS_NAME:
763             case CERT_ALT_NAME_URL:
764                 strcpyW(str, entry->u.pwszURL);
765                 break;
766             case CERT_ALT_NAME_IP_ADDRESS:
767                 if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
768                 {
769                     strcpyW(str, ipAddrBuf);
770                     str += strlenW(ipAddrBuf);
771                     strcpyW(str, crlf);
772                     str += strlenW(crlf);
773                     strcpyW(str, mask);
774                     str += strlenW(mask);
775                     strcpyW(str, maskBuf);
776                 }
777                 else
778                     strcpyW(str, ipAddrBuf);
779                 break;
780             }
781         }
782     }
783     return ret;
784 }
785
786 static const WCHAR commaSpace[] = { ',',' ',0 };
787
788 static BOOL CRYPT_FormatAltNameInfo(DWORD dwFormatStrType,
789  CERT_ALT_NAME_INFO *name, LPWSTR str, DWORD *pcbStr)
790 {
791     DWORD i, size, bytesNeeded = 0;
792     BOOL ret = TRUE;
793     LPCWSTR sep;
794     DWORD sepLen;
795
796     if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
797     {
798         sep = crlf;
799         sepLen = strlenW(crlf) * sizeof(WCHAR);
800     }
801     else
802     {
803         sep = commaSpace;
804         sepLen = strlenW(commaSpace) * sizeof(WCHAR);
805     }
806
807     for (i = 0; ret && i < name->cAltEntry; i++)
808     {
809         ret = CRYPT_FormatAltNameEntry(dwFormatStrType, &name->rgAltEntry[i],
810          NULL, &size);
811         if (ret)
812         {
813             bytesNeeded += size - sizeof(WCHAR);
814             if (i < name->cAltEntry - 1)
815                 bytesNeeded += sepLen;
816         }
817     }
818     if (ret)
819     {
820         bytesNeeded += sizeof(WCHAR);
821         if (!str)
822             *pcbStr = bytesNeeded;
823         else if (*pcbStr < bytesNeeded)
824         {
825             *pcbStr = bytesNeeded;
826             SetLastError(ERROR_MORE_DATA);
827             ret = FALSE;
828         }
829         else
830         {
831             *pcbStr = bytesNeeded;
832             for (i = 0; ret && i < name->cAltEntry; i++)
833             {
834                 ret = CRYPT_FormatAltNameEntry(dwFormatStrType,
835                  &name->rgAltEntry[i], str, &size);
836                 if (ret)
837                 {
838                     str += size / sizeof(WCHAR) - 1;
839                     if (i < name->cAltEntry - 1)
840                     {
841                         strcpyW(str, sep);
842                         str += sepLen / sizeof(WCHAR);
843                     }
844                 }
845             }
846         }
847     }
848     return ret;
849 }
850
851 static BOOL CRYPT_FormatCertIssuer(DWORD dwFormatStrType,
852  CERT_ALT_NAME_INFO *issuer, LPWSTR str, DWORD *pcbStr)
853 {
854     WCHAR buf[MAX_STRING_RESOURCE_LEN];
855     DWORD bytesNeeded;
856     BOOL ret;
857
858     LoadStringW(hInstance, IDS_CERT_ISSUER, buf, sizeof(buf) / sizeof(buf[0]));
859     ret = CRYPT_FormatAltNameInfo(dwFormatStrType, issuer, NULL, &bytesNeeded);
860     bytesNeeded += strlenW(buf) * sizeof(WCHAR);
861     if (ret)
862     {
863         if (!str)
864             *pcbStr = bytesNeeded;
865         else if (*pcbStr < bytesNeeded)
866         {
867             *pcbStr = bytesNeeded;
868             SetLastError(ERROR_MORE_DATA);
869             ret = FALSE;
870         }
871         else
872         {
873             *pcbStr = bytesNeeded;
874             strcpyW(str, buf);
875             str += strlenW(str);
876             bytesNeeded -= strlenW(str) * sizeof(WCHAR);
877             ret = CRYPT_FormatAltNameInfo(dwFormatStrType, issuer, str,
878              &bytesNeeded);
879         }
880     }
881     return ret;
882 }
883
884 static BOOL WINAPI CRYPT_FormatAuthorityKeyId2(DWORD dwCertEncodingType,
885  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
886  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
887  DWORD *pcbFormat)
888 {
889     CERT_AUTHORITY_KEY_ID2_INFO *info;
890     DWORD size;
891     BOOL ret = FALSE;
892
893     if (!cbEncoded)
894     {
895         SetLastError(E_INVALIDARG);
896         return FALSE;
897     }
898     if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_AUTHORITY_KEY_ID2,
899      pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size)))
900     {
901         DWORD bytesNeeded = sizeof(WCHAR); /* space for the NULL terminator */
902         LPCWSTR sep;
903         DWORD sepLen;
904         BOOL needSeparator = FALSE;
905
906         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
907         {
908             sep = crlf;
909             sepLen = strlenW(crlf) * sizeof(WCHAR);
910         }
911         else
912         {
913             sep = commaSpace;
914             sepLen = strlenW(commaSpace) * sizeof(WCHAR);
915         }
916
917         if (info->KeyId.cbData)
918         {
919             needSeparator = TRUE;
920             ret = CRYPT_FormatKeyId(&info->KeyId, NULL, &size);
921             if (ret)
922             {
923                 /* don't include NULL-terminator more than once */
924                 bytesNeeded += size - sizeof(WCHAR);
925             }
926         }
927         if (info->AuthorityCertIssuer.cAltEntry)
928         {
929             if (needSeparator)
930                 bytesNeeded += sepLen;
931             needSeparator = TRUE;
932             ret = CRYPT_FormatCertIssuer(dwFormatStrType,
933              &info->AuthorityCertIssuer, NULL, &size);
934             if (ret)
935             {
936                 /* don't include NULL-terminator more than once */
937                 bytesNeeded += size - sizeof(WCHAR);
938             }
939         }
940         if (info->AuthorityCertSerialNumber.cbData)
941         {
942             if (needSeparator)
943                 bytesNeeded += sepLen;
944             ret = CRYPT_FormatCertSerialNumber(
945              &info->AuthorityCertSerialNumber, NULL, &size);
946             if (ret)
947             {
948                 /* don't include NULL-terminator more than once */
949                 bytesNeeded += size - sizeof(WCHAR);
950             }
951         }
952         if (ret)
953         {
954             if (!pbFormat)
955                 *pcbFormat = bytesNeeded;
956             else if (*pcbFormat < bytesNeeded)
957             {
958                 *pcbFormat = bytesNeeded;
959                 SetLastError(ERROR_MORE_DATA);
960                 ret = FALSE;
961             }
962             else
963             {
964                 LPWSTR str = pbFormat;
965
966                 *pcbFormat = bytesNeeded;
967                 needSeparator = FALSE;
968                 if (info->KeyId.cbData)
969                 {
970                     needSeparator = TRUE;
971                     ret = CRYPT_FormatKeyId(&info->KeyId, str, &size);
972                     if (ret)
973                         str += size / sizeof(WCHAR);
974                 }
975                 if (info->AuthorityCertIssuer.cAltEntry)
976                 {
977                     if (needSeparator)
978                     {
979                         strcpyW(str, sep);
980                         str += sepLen / sizeof(WCHAR);
981                     }
982                     needSeparator = TRUE;
983                     ret = CRYPT_FormatCertIssuer(dwFormatStrType,
984                      &info->AuthorityCertIssuer, str, &size);
985                     if (ret)
986                         str += size / sizeof(WCHAR);
987                 }
988                 if (info->AuthorityCertSerialNumber.cbData)
989                 {
990                     if (needSeparator)
991                     {
992                         strcpyW(str, sep);
993                         str += sepLen / sizeof(WCHAR);
994                     }
995                     ret = CRYPT_FormatCertSerialNumber(
996                      &info->AuthorityCertSerialNumber, str, &size);
997                 }
998             }
999         }
1000         LocalFree(info);
1001     }
1002     return ret;
1003 }
1004
1005 typedef BOOL (WINAPI *CryptFormatObjectFunc)(DWORD, DWORD, DWORD, void *,
1006  LPCSTR, const BYTE *, DWORD, void *, DWORD *);
1007
1008 static CryptFormatObjectFunc CRYPT_GetBuiltinFormatFunction(DWORD encodingType,
1009  DWORD formatStrType, LPCSTR lpszStructType)
1010 {
1011     CryptFormatObjectFunc format = NULL;
1012
1013     if ((encodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING)
1014     {
1015         SetLastError(ERROR_FILE_NOT_FOUND);
1016         return NULL;
1017     }
1018     if (!HIWORD(lpszStructType))
1019     {
1020         switch (LOWORD(lpszStructType))
1021         {
1022         case LOWORD(X509_AUTHORITY_KEY_ID2):
1023             format = CRYPT_FormatAuthorityKeyId2;
1024             break;
1025         }
1026     }
1027     else if (!strcmp(lpszStructType, szOID_AUTHORITY_KEY_IDENTIFIER2))
1028         format = CRYPT_FormatAuthorityKeyId2;
1029     if (!format && !(formatStrType & CRYPT_FORMAT_STR_NO_HEX))
1030         format = CRYPT_FormatHexString;
1031     return format;
1032 }
1033
1034 BOOL WINAPI CryptFormatObject(DWORD dwCertEncodingType, DWORD dwFormatType,
1035  DWORD dwFormatStrType, void *pFormatStruct, LPCSTR lpszStructType,
1036  const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat, DWORD *pcbFormat)
1037 {
1038     CryptFormatObjectFunc format = NULL;
1039     HCRYPTOIDFUNCADDR hFunc = NULL;
1040     BOOL ret = FALSE;
1041
1042     TRACE("(%08x, %d, %08x, %p, %s, %p, %d, %p, %p)\n", dwCertEncodingType,
1043      dwFormatType, dwFormatStrType, pFormatStruct, debugstr_a(lpszStructType),
1044      pbEncoded, cbEncoded, pbFormat, pcbFormat);
1045
1046     if (!(format = CRYPT_GetBuiltinFormatFunction(dwCertEncodingType,
1047      dwFormatStrType, lpszStructType)))
1048     {
1049         static HCRYPTOIDFUNCSET set = NULL;
1050
1051         if (!set)
1052             set = CryptInitOIDFunctionSet(CRYPT_OID_FORMAT_OBJECT_FUNC, 0);
1053         CryptGetOIDFunctionAddress(set, dwCertEncodingType, lpszStructType, 0,
1054          (void **)&format, &hFunc);
1055     }
1056     if (format)
1057         ret = format(dwCertEncodingType, dwFormatType, dwFormatStrType,
1058          pFormatStruct, lpszStructType, pbEncoded, cbEncoded, pbFormat,
1059          pcbFormat);
1060     if (hFunc)
1061         CryptFreeOIDFunctionAddress(hFunc, 0);
1062     return ret;
1063 }