crypt32: Support reading a serialized store object from memory in CryptQueryObject.
[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 "wintrust.h"
28 #include "crypt32_private.h"
29 #include "cryptres.h"
30 #include "wine/unicode.h"
31 #include "wine/debug.h"
32
33 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
34
35 static BOOL CRYPT_ReadBlobFromFile(LPCWSTR fileName, PCERT_BLOB blob)
36 {
37     BOOL ret = FALSE;
38     HANDLE file;
39
40     TRACE("%s\n", debugstr_w(fileName));
41
42     file = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL,
43      OPEN_EXISTING, 0, NULL);
44     if (file != INVALID_HANDLE_VALUE)
45     {
46         ret = TRUE;
47         blob->cbData = GetFileSize(file, NULL);
48         if (blob->cbData)
49         {
50             blob->pbData = CryptMemAlloc(blob->cbData);
51             if (blob->pbData)
52             {
53                 DWORD read;
54
55                 ret = ReadFile(file, blob->pbData, blob->cbData, &read, NULL);
56             }
57         }
58         CloseHandle(file);
59     }
60     TRACE("returning %d\n", ret);
61     return ret;
62 }
63
64 static BOOL CRYPT_QueryContextBlob(const CERT_BLOB *blob,
65  DWORD dwExpectedContentTypeFlags, HCERTSTORE store,
66  DWORD *contentType, const void **ppvContext)
67 {
68     BOOL ret = FALSE;
69
70     if (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CERT)
71     {
72         ret = pCertInterface->addEncodedToStore(store, X509_ASN_ENCODING,
73          blob->pbData, blob->cbData, CERT_STORE_ADD_ALWAYS, ppvContext);
74         if (ret && contentType)
75             *contentType = CERT_QUERY_CONTENT_CERT;
76     }
77     if (!ret && (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CRL))
78     {
79         ret = pCRLInterface->addEncodedToStore(store, X509_ASN_ENCODING,
80          blob->pbData, blob->cbData, CERT_STORE_ADD_ALWAYS, ppvContext);
81         if (ret && contentType)
82             *contentType = CERT_QUERY_CONTENT_CRL;
83     }
84     if (!ret && (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CTL))
85     {
86         ret = pCTLInterface->addEncodedToStore(store, X509_ASN_ENCODING,
87          blob->pbData, blob->cbData, CERT_STORE_ADD_ALWAYS, ppvContext);
88         if (ret && contentType)
89             *contentType = CERT_QUERY_CONTENT_CTL;
90     }
91     return ret;
92 }
93
94 static BOOL CRYPT_QueryContextObject(DWORD dwObjectType, const void *pvObject,
95  DWORD dwExpectedContentTypeFlags, DWORD dwExpectedFormatTypeFlags,
96  DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType, DWORD *pdwFormatType,
97  HCERTSTORE *phCertStore, const void **ppvContext)
98 {
99     CERT_BLOB fileBlob;
100     const CERT_BLOB *blob;
101     HCERTSTORE store;
102     BOOL ret;
103     DWORD formatType = 0;
104
105     switch (dwObjectType)
106     {
107     case CERT_QUERY_OBJECT_FILE:
108         /* Cert, CRL, and CTL contexts can't be "embedded" in a file, so
109          * just read the file directly
110          */
111         ret = CRYPT_ReadBlobFromFile(pvObject, &fileBlob);
112         blob = &fileBlob;
113         break;
114     case CERT_QUERY_OBJECT_BLOB:
115         blob = pvObject;
116         ret = TRUE;
117         break;
118     default:
119         SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */
120         ret = FALSE;
121     }
122     if (!ret)
123         return FALSE;
124
125     ret = FALSE;
126     store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
127      CERT_STORE_CREATE_NEW_FLAG, NULL);
128     if (dwExpectedFormatTypeFlags & CERT_QUERY_FORMAT_FLAG_BINARY)
129     {
130         ret = CRYPT_QueryContextBlob(blob, dwExpectedContentTypeFlags, store,
131          pdwContentType, ppvContext);
132         if (ret)
133             formatType = CERT_QUERY_FORMAT_BINARY;
134     }
135     if (!ret &&
136      (dwExpectedFormatTypeFlags & CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED))
137     {
138         CRYPT_DATA_BLOB trimmed = { blob->cbData, blob->pbData };
139         CRYPT_DATA_BLOB decoded;
140
141         while (trimmed.cbData && !trimmed.pbData[trimmed.cbData - 1])
142             trimmed.cbData--;
143         ret = CryptStringToBinaryA((LPSTR)trimmed.pbData, trimmed.cbData,
144          CRYPT_STRING_BASE64_ANY, NULL, &decoded.cbData, NULL, NULL);
145         if (ret)
146         {
147             decoded.pbData = CryptMemAlloc(decoded.cbData);
148             if (decoded.pbData)
149             {
150                 ret = CryptStringToBinaryA((LPSTR)trimmed.pbData,
151                  trimmed.cbData, CRYPT_STRING_BASE64_ANY, decoded.pbData,
152                  &decoded.cbData, NULL, NULL);
153                 if (ret)
154                 {
155                     ret = CRYPT_QueryContextBlob(&decoded,
156                      dwExpectedContentTypeFlags, store, pdwContentType,
157                      ppvContext);
158                     if (ret)
159                         formatType = CERT_QUERY_FORMAT_BASE64_ENCODED;
160                 }
161                 CryptMemFree(decoded.pbData);
162             }
163             else
164                 ret = FALSE;
165         }
166     }
167     if (ret)
168     {
169         if (pdwMsgAndCertEncodingType)
170             *pdwMsgAndCertEncodingType = X509_ASN_ENCODING;
171         if (pdwFormatType)
172             *pdwFormatType = formatType;
173         if (phCertStore)
174             *phCertStore = CertDuplicateStore(store);
175     }
176     CertCloseStore(store, 0);
177     if (blob == &fileBlob)
178         CryptMemFree(blob->pbData);
179     TRACE("returning %d\n", ret);
180     return ret;
181 }
182
183 static BOOL CRYPT_QuerySerializedContextObject(DWORD dwObjectType,
184  const void *pvObject, DWORD dwExpectedContentTypeFlags,
185  DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType,
186  HCERTSTORE *phCertStore, const void **ppvContext)
187 {
188     CERT_BLOB fileBlob;
189     const CERT_BLOB *blob;
190     const WINE_CONTEXT_INTERFACE *contextInterface = NULL;
191     const void *context;
192     DWORD contextType;
193     BOOL ret;
194
195     switch (dwObjectType)
196     {
197     case CERT_QUERY_OBJECT_FILE:
198         /* Cert, CRL, and CTL contexts can't be "embedded" in a file, so
199          * just read the file directly
200          */
201         ret = CRYPT_ReadBlobFromFile(pvObject, &fileBlob);
202         blob = &fileBlob;
203         break;
204     case CERT_QUERY_OBJECT_BLOB:
205         blob = pvObject;
206         ret = TRUE;
207         break;
208     default:
209         SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */
210         ret = FALSE;
211     }
212     if (!ret)
213         return FALSE;
214
215     ret = FALSE;
216     context = CRYPT_ReadSerializedElement(blob->pbData, blob->cbData,
217      CERT_STORE_ALL_CONTEXT_FLAG, &contextType);
218     if (context)
219     {
220         DWORD contentType, certStoreOffset;
221
222         ret = TRUE;
223         switch (contextType)
224         {
225         case CERT_STORE_CERTIFICATE_CONTEXT:
226             contextInterface = pCertInterface;
227             contentType = CERT_QUERY_CONTENT_SERIALIZED_CERT;
228             certStoreOffset = offsetof(CERT_CONTEXT, hCertStore);
229             if (!(dwExpectedContentTypeFlags &
230              CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT))
231             {
232                 SetLastError(ERROR_INVALID_DATA);
233                 ret = FALSE;
234                 goto end;
235             }
236             break;
237         case CERT_STORE_CRL_CONTEXT:
238             contextInterface = pCRLInterface;
239             contentType = CERT_QUERY_CONTENT_SERIALIZED_CRL;
240             certStoreOffset = offsetof(CRL_CONTEXT, hCertStore);
241             if (!(dwExpectedContentTypeFlags &
242              CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL))
243             {
244                 SetLastError(ERROR_INVALID_DATA);
245                 ret = FALSE;
246                 goto end;
247             }
248             break;
249         case CERT_STORE_CTL_CONTEXT:
250             contextInterface = pCTLInterface;
251             contentType = CERT_QUERY_CONTENT_SERIALIZED_CTL;
252             certStoreOffset = offsetof(CTL_CONTEXT, hCertStore);
253             if (!(dwExpectedContentTypeFlags &
254              CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL))
255             {
256                 SetLastError(ERROR_INVALID_DATA);
257                 ret = FALSE;
258                 goto end;
259             }
260             break;
261         default:
262             SetLastError(ERROR_INVALID_DATA);
263             ret = FALSE;
264             goto end;
265         }
266         if (pdwMsgAndCertEncodingType)
267             *pdwMsgAndCertEncodingType = X509_ASN_ENCODING;
268         if (pdwContentType)
269             *pdwContentType = contentType;
270         if (phCertStore)
271             *phCertStore = CertDuplicateStore(
272              *(HCERTSTORE *)((const BYTE *)context + certStoreOffset));
273         if (ppvContext)
274             *ppvContext = contextInterface->duplicate(context);
275     }
276
277 end:
278     if (contextInterface && context)
279         contextInterface->free(context);
280     if (blob == &fileBlob)
281         CryptMemFree(blob->pbData);
282     TRACE("returning %d\n", ret);
283     return ret;
284 }
285
286 static BOOL CRYPT_QuerySerializedStoreFromFile(LPCWSTR fileName,
287  DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType,
288  HCERTSTORE *phCertStore, HCRYPTMSG *phMsg)
289 {
290     HANDLE file;
291     BOOL ret = FALSE;
292
293     TRACE("%s\n", debugstr_w(fileName));
294     file = CreateFileW(fileName, GENERIC_READ, FILE_SHARE_READ, NULL,
295      OPEN_EXISTING, 0, NULL);
296     if (file != INVALID_HANDLE_VALUE)
297     {
298         HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
299          CERT_STORE_CREATE_NEW_FLAG, NULL);
300
301         ret = CRYPT_ReadSerializedStoreFromFile(file, store);
302         if (ret)
303         {
304             if (pdwMsgAndCertEncodingType)
305                 *pdwMsgAndCertEncodingType = X509_ASN_ENCODING;
306             if (pdwContentType)
307                 *pdwContentType = CERT_QUERY_CONTENT_SERIALIZED_STORE;
308             if (phCertStore)
309                 *phCertStore = CertDuplicateStore(store);
310         }
311         CertCloseStore(store, 0);
312         CloseHandle(file);
313     }
314     TRACE("returning %d\n", ret);
315     return ret;
316 }
317
318 static BOOL CRYPT_QuerySerializedStoreFromBlob(const CRYPT_DATA_BLOB *blob,
319  DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType,
320  HCERTSTORE *phCertStore, HCRYPTMSG *phMsg)
321 {
322     HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
323      CERT_STORE_CREATE_NEW_FLAG, NULL);
324     BOOL ret;
325
326     TRACE("(%d, %p)\n", blob->cbData, blob->pbData);
327
328     ret = CRYPT_ReadSerializedStoreFromBlob(blob, store);
329     if (ret)
330     {
331         if (pdwMsgAndCertEncodingType)
332             *pdwMsgAndCertEncodingType = X509_ASN_ENCODING;
333         if (pdwContentType)
334             *pdwContentType = CERT_QUERY_CONTENT_SERIALIZED_STORE;
335         if (phCertStore)
336             *phCertStore = CertDuplicateStore(store);
337     }
338     CertCloseStore(store, 0);
339     TRACE("returning %d\n", ret);
340     return ret;
341 }
342
343 static BOOL CRYPT_QuerySerializedStoreObject(DWORD dwObjectType,
344  const void *pvObject, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType,
345  HCERTSTORE *phCertStore, HCRYPTMSG *phMsg)
346 {
347     switch (dwObjectType)
348     {
349     case CERT_QUERY_OBJECT_FILE:
350         return CRYPT_QuerySerializedStoreFromFile(pvObject,
351          pdwMsgAndCertEncodingType, pdwContentType, phCertStore, phMsg);
352     case CERT_QUERY_OBJECT_BLOB:
353         return CRYPT_QuerySerializedStoreFromBlob(pvObject,
354          pdwMsgAndCertEncodingType, pdwContentType, phCertStore, phMsg);
355     default:
356         FIXME("unimplemented for type %d\n", dwObjectType);
357         SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */
358         return FALSE;
359     }
360 }
361
362 static BOOL CRYPT_QuerySignedMessage(const CRYPT_DATA_BLOB *blob,
363  DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType, HCRYPTMSG *phMsg)
364 {
365     DWORD encodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
366     BOOL ret = FALSE;
367     HCRYPTMSG msg;
368
369     if ((msg = CryptMsgOpenToDecode(encodingType, 0, 0, 0, NULL, NULL)))
370     {
371         ret = CryptMsgUpdate(msg, blob->pbData, blob->cbData, TRUE);
372         if (ret)
373         {
374             DWORD type, len = sizeof(type);
375
376             ret = CryptMsgGetParam(msg, CMSG_TYPE_PARAM, 0, &type, &len);
377             if (ret)
378             {
379                 if (type != CMSG_SIGNED)
380                 {
381                     SetLastError(ERROR_INVALID_DATA);
382                     ret = FALSE;
383                 }
384             }
385         }
386         if (!ret)
387         {
388             CryptMsgClose(msg);
389             msg = CryptMsgOpenToDecode(encodingType, 0, CMSG_SIGNED, 0, NULL,
390              NULL);
391             if (msg)
392             {
393                 ret = CryptMsgUpdate(msg, blob->pbData, blob->cbData, TRUE);
394                 if (!ret)
395                 {
396                     CryptMsgClose(msg);
397                     msg = NULL;
398                 }
399             }
400         }
401     }
402     if (ret)
403     {
404         if (pdwMsgAndCertEncodingType)
405             *pdwMsgAndCertEncodingType = encodingType;
406         if (pdwContentType)
407             *pdwContentType = CERT_QUERY_CONTENT_PKCS7_SIGNED;
408         if (phMsg)
409             *phMsg = msg;
410     }
411     return ret;
412 }
413
414 static BOOL CRYPT_QueryUnsignedMessage(const CRYPT_DATA_BLOB *blob,
415  DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType, HCRYPTMSG *phMsg)
416 {
417     DWORD encodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
418     BOOL ret = FALSE;
419     HCRYPTMSG msg;
420
421     if ((msg = CryptMsgOpenToDecode(encodingType, 0, 0, 0, NULL, NULL)))
422     {
423         ret = CryptMsgUpdate(msg, blob->pbData, blob->cbData, TRUE);
424         if (ret)
425         {
426             DWORD type, len = sizeof(type);
427
428             ret = CryptMsgGetParam(msg, CMSG_TYPE_PARAM, 0, &type, &len);
429             if (ret)
430             {
431                 if (type != CMSG_DATA)
432                 {
433                     SetLastError(ERROR_INVALID_DATA);
434                     ret = FALSE;
435                 }
436             }
437         }
438         if (!ret)
439         {
440             CryptMsgClose(msg);
441             msg = CryptMsgOpenToDecode(encodingType, 0, CMSG_DATA, 0,
442              NULL, NULL);
443             if (msg)
444             {
445                 ret = CryptMsgUpdate(msg, blob->pbData, blob->cbData, TRUE);
446                 if (!ret)
447                 {
448                     CryptMsgClose(msg);
449                     msg = NULL;
450                 }
451             }
452         }
453     }
454     if (ret)
455     {
456         if (pdwMsgAndCertEncodingType)
457             *pdwMsgAndCertEncodingType = encodingType;
458         if (pdwContentType)
459             *pdwContentType = CERT_QUERY_CONTENT_PKCS7_SIGNED;
460         if (phMsg)
461             *phMsg = msg;
462     }
463     return ret;
464 }
465
466 /* Used to decode non-embedded messages */
467 static BOOL CRYPT_QueryMessageObject(DWORD dwObjectType, const void *pvObject,
468  DWORD dwExpectedContentTypeFlags, DWORD dwExpectedFormatTypeFlags,
469  DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType, DWORD *pdwFormatType,
470  HCERTSTORE *phCertStore, HCRYPTMSG *phMsg)
471 {
472     CERT_BLOB fileBlob;
473     const CERT_BLOB *blob;
474     BOOL ret;
475     HCRYPTMSG msg = NULL;
476     DWORD encodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
477     DWORD formatType = 0;
478
479     TRACE("(%d, %p, %08x, %08x, %p, %p, %p, %p, %p)\n", dwObjectType, pvObject,
480      dwExpectedContentTypeFlags, dwExpectedFormatTypeFlags,
481      pdwMsgAndCertEncodingType, pdwContentType, pdwFormatType, phCertStore,
482      phMsg);
483
484     switch (dwObjectType)
485     {
486     case CERT_QUERY_OBJECT_FILE:
487         /* This isn't an embedded PKCS7 message, so just read the file
488          * directly
489          */
490         ret = CRYPT_ReadBlobFromFile(pvObject, &fileBlob);
491         blob = &fileBlob;
492         break;
493     case CERT_QUERY_OBJECT_BLOB:
494         blob = pvObject;
495         ret = TRUE;
496         break;
497     default:
498         SetLastError(E_INVALIDARG); /* FIXME: is this the correct error? */
499         ret = FALSE;
500     }
501     if (!ret)
502         return FALSE;
503
504     ret = FALSE;
505     if (dwExpectedFormatTypeFlags & CERT_QUERY_FORMAT_FLAG_BINARY)
506     {
507         /* Try it first as a signed message */
508         if (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED)
509             ret = CRYPT_QuerySignedMessage(blob, pdwMsgAndCertEncodingType,
510              pdwContentType, &msg);
511         /* Failing that, try as an unsigned message */
512         if (!ret &&
513          (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED))
514             ret = CRYPT_QueryUnsignedMessage(blob, pdwMsgAndCertEncodingType,
515              pdwContentType, &msg);
516         if (ret)
517             formatType = CERT_QUERY_FORMAT_BINARY;
518     }
519     if (!ret &&
520      (dwExpectedFormatTypeFlags & CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED))
521     {
522         CRYPT_DATA_BLOB trimmed = { blob->cbData, blob->pbData };
523         CRYPT_DATA_BLOB decoded;
524
525         while (trimmed.cbData && !trimmed.pbData[trimmed.cbData - 1])
526             trimmed.cbData--;
527         ret = CryptStringToBinaryA((LPSTR)trimmed.pbData, trimmed.cbData,
528          CRYPT_STRING_BASE64_ANY, NULL, &decoded.cbData, NULL, NULL);
529         if (ret)
530         {
531             decoded.pbData = CryptMemAlloc(decoded.cbData);
532             if (decoded.pbData)
533             {
534                 ret = CryptStringToBinaryA((LPSTR)trimmed.pbData,
535                  trimmed.cbData, CRYPT_STRING_BASE64_ANY, decoded.pbData,
536                  &decoded.cbData, NULL, NULL);
537                 if (ret)
538                 {
539                     /* Try it first as a signed message */
540                     if (dwExpectedContentTypeFlags &
541                      CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED)
542                         ret = CRYPT_QuerySignedMessage(&decoded,
543                          pdwMsgAndCertEncodingType, pdwContentType, &msg);
544                     /* Failing that, try as an unsigned message */
545                     if (!ret && (dwExpectedContentTypeFlags &
546                      CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED))
547                         ret = CRYPT_QueryUnsignedMessage(&decoded,
548                          pdwMsgAndCertEncodingType, pdwContentType, &msg);
549                     if (ret)
550                         formatType = CERT_QUERY_FORMAT_BASE64_ENCODED;
551                 }
552                 CryptMemFree(decoded.pbData);
553             }
554             else
555                 ret = FALSE;
556         }
557         if (!ret && !(blob->cbData % sizeof(WCHAR)))
558         {
559             CRYPT_DATA_BLOB decoded;
560             LPWSTR str = (LPWSTR)blob->pbData;
561             DWORD strLen = blob->cbData / sizeof(WCHAR);
562
563             /* Try again, assuming the input string is UTF-16 base64 */
564             while (strLen && !str[strLen - 1])
565                 strLen--;
566             ret = CryptStringToBinaryW(str, strLen, CRYPT_STRING_BASE64_ANY,
567              NULL, &decoded.cbData, NULL, NULL);
568             if (ret)
569             {
570                 decoded.pbData = CryptMemAlloc(decoded.cbData);
571                 if (decoded.pbData)
572                 {
573                     ret = CryptStringToBinaryW(str, strLen,
574                      CRYPT_STRING_BASE64_ANY, decoded.pbData, &decoded.cbData,
575                      NULL, NULL);
576                     if (ret)
577                     {
578                         /* Try it first as a signed message */
579                         if (dwExpectedContentTypeFlags &
580                          CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED)
581                             ret = CRYPT_QuerySignedMessage(&decoded,
582                              pdwMsgAndCertEncodingType, pdwContentType, &msg);
583                         /* Failing that, try as an unsigned message */
584                         if (!ret && (dwExpectedContentTypeFlags &
585                          CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED))
586                             ret = CRYPT_QueryUnsignedMessage(&decoded,
587                              pdwMsgAndCertEncodingType, pdwContentType, &msg);
588                         if (ret)
589                             formatType = CERT_QUERY_FORMAT_BASE64_ENCODED;
590                     }
591                     CryptMemFree(decoded.pbData);
592                 }
593                 else
594                     ret = FALSE;
595             }
596         }
597     }
598     if (ret)
599     {
600         if (pdwFormatType)
601             *pdwFormatType = formatType;
602         if (phMsg)
603             *phMsg = msg;
604         if (phCertStore)
605             *phCertStore = CertOpenStore(CERT_STORE_PROV_MSG, encodingType, 0,
606              0, msg);
607     }
608     if (blob == &fileBlob)
609         CryptMemFree(blob->pbData);
610     TRACE("returning %d\n", ret);
611     return ret;
612 }
613
614 static BOOL CRYPT_QueryEmbeddedMessageObject(DWORD dwObjectType,
615  const void *pvObject, DWORD dwExpectedContentTypeFlags,
616  DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType,
617  HCERTSTORE *phCertStore, HCRYPTMSG *phMsg)
618 {
619     HANDLE file;
620     GUID subject;
621     BOOL ret = FALSE;
622
623     TRACE("%s\n", debugstr_w(pvObject));
624
625     if (dwObjectType != CERT_QUERY_OBJECT_FILE)
626     {
627         WARN("don't know what to do for type %d embedded signed messages\n",
628          dwObjectType);
629         SetLastError(E_INVALIDARG);
630         return FALSE;
631     }
632     file = CreateFileW(pvObject, GENERIC_READ, FILE_SHARE_READ,
633      NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
634     if (file != INVALID_HANDLE_VALUE)
635     {
636         ret = CryptSIPRetrieveSubjectGuid(pvObject, file, &subject);
637         if (ret)
638         {
639             SIP_DISPATCH_INFO sip;
640
641             memset(&sip, 0, sizeof(sip));
642             sip.cbSize = sizeof(sip);
643             ret = CryptSIPLoad(&subject, 0, &sip);
644             if (ret)
645             {
646                 SIP_SUBJECTINFO subjectInfo;
647                 CERT_BLOB blob;
648                 DWORD encodingType;
649
650                 memset(&subjectInfo, 0, sizeof(subjectInfo));
651                 subjectInfo.cbSize = sizeof(subjectInfo);
652                 subjectInfo.pgSubjectType = &subject;
653                 subjectInfo.hFile = file;
654                 subjectInfo.pwsFileName = pvObject;
655                 ret = sip.pfGet(&subjectInfo, &encodingType, 0, &blob.cbData,
656                  NULL);
657                 if (ret)
658                 {
659                     blob.pbData = CryptMemAlloc(blob.cbData);
660                     if (blob.pbData)
661                     {
662                         ret = sip.pfGet(&subjectInfo, &encodingType, 0,
663                          &blob.cbData, blob.pbData);
664                         if (ret)
665                         {
666                             ret = CRYPT_QueryMessageObject(
667                              CERT_QUERY_OBJECT_BLOB, &blob,
668                              CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED,
669                              CERT_QUERY_FORMAT_FLAG_BINARY,
670                              pdwMsgAndCertEncodingType, NULL, NULL,
671                              phCertStore, phMsg);
672                             if (ret && pdwContentType)
673                                 *pdwContentType =
674                                  CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED;
675                         }
676                         CryptMemFree(blob.pbData);
677                     }
678                     else
679                     {
680                         SetLastError(ERROR_OUTOFMEMORY);
681                         ret = FALSE;
682                     }
683                 }
684             }
685         }
686         CloseHandle(file);
687     }
688     TRACE("returning %d\n", ret);
689     return ret;
690 }
691
692 BOOL WINAPI CryptQueryObject(DWORD dwObjectType, const void *pvObject,
693  DWORD dwExpectedContentTypeFlags, DWORD dwExpectedFormatTypeFlags,
694  DWORD dwFlags, DWORD *pdwMsgAndCertEncodingType, DWORD *pdwContentType,
695  DWORD *pdwFormatType, HCERTSTORE *phCertStore, HCRYPTMSG *phMsg,
696  const void **ppvContext)
697 {
698     static const DWORD unimplementedTypes =
699      CERT_QUERY_CONTENT_FLAG_PKCS10 | CERT_QUERY_CONTENT_FLAG_PFX |
700      CERT_QUERY_CONTENT_FLAG_CERT_PAIR;
701     BOOL ret = TRUE;
702
703     TRACE("(%08x, %p, %08x, %08x, %08x, %p, %p, %p, %p, %p, %p)\n",
704      dwObjectType, pvObject, dwExpectedContentTypeFlags,
705      dwExpectedFormatTypeFlags, dwFlags, pdwMsgAndCertEncodingType,
706      pdwContentType, pdwFormatType, phCertStore, phMsg, ppvContext);
707
708     if (dwObjectType != CERT_QUERY_OBJECT_BLOB &&
709      dwObjectType != CERT_QUERY_OBJECT_FILE)
710     {
711         WARN("unsupported type %d\n", dwObjectType);
712         SetLastError(E_INVALIDARG);
713         return FALSE;
714     }
715     if (!pvObject)
716     {
717         WARN("missing required argument\n");
718         SetLastError(E_INVALIDARG);
719         return FALSE;
720     }
721     if (dwExpectedContentTypeFlags & unimplementedTypes)
722         WARN("unimplemented for types %08x\n",
723          dwExpectedContentTypeFlags & unimplementedTypes);
724
725     if (pdwFormatType)
726         *pdwFormatType = CERT_QUERY_FORMAT_BINARY;
727     if (phCertStore)
728         *phCertStore = NULL;
729     if (phMsg)
730         *phMsg = NULL;
731     if (ppvContext)
732         *ppvContext = NULL;
733
734     ret = FALSE;
735     if ((dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CERT) ||
736      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CRL) ||
737      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_CTL))
738     {
739         ret = CRYPT_QueryContextObject(dwObjectType, pvObject,
740          dwExpectedContentTypeFlags, dwExpectedFormatTypeFlags,
741          pdwMsgAndCertEncodingType, pdwContentType, pdwFormatType, phCertStore,
742          ppvContext);
743     }
744     if (!ret &&
745      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE))
746     {
747         ret = CRYPT_QuerySerializedStoreObject(dwObjectType, pvObject,
748          pdwMsgAndCertEncodingType, pdwContentType, phCertStore, phMsg);
749     }
750     if (!ret &&
751      ((dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT) ||
752      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL) ||
753      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL)))
754     {
755         ret = CRYPT_QuerySerializedContextObject(dwObjectType, pvObject,
756          dwExpectedContentTypeFlags, pdwMsgAndCertEncodingType, pdwContentType,
757          phCertStore, ppvContext);
758     }
759     if (!ret &&
760      ((dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED) ||
761      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED)))
762     {
763         ret = CRYPT_QueryMessageObject(dwObjectType, pvObject,
764          dwExpectedContentTypeFlags, dwExpectedFormatTypeFlags,
765          pdwMsgAndCertEncodingType, pdwContentType, pdwFormatType,
766          phCertStore, phMsg);
767     }
768     if (!ret &&
769      (dwExpectedContentTypeFlags & CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED))
770     {
771         ret = CRYPT_QueryEmbeddedMessageObject(dwObjectType, pvObject,
772          dwExpectedContentTypeFlags, pdwMsgAndCertEncodingType, pdwContentType,
773          phCertStore, phMsg);
774     }
775     if (!ret)
776         SetLastError(CRYPT_E_NO_MATCH);
777     TRACE("returning %d\n", ret);
778     return ret;
779 }
780
781 static BOOL WINAPI CRYPT_FormatHexString(DWORD dwCertEncodingType,
782  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
783  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
784  DWORD *pcbFormat)
785 {
786     BOOL ret;
787     DWORD bytesNeeded;
788
789     if (cbEncoded)
790         bytesNeeded = (cbEncoded * 3) * sizeof(WCHAR);
791     else
792         bytesNeeded = sizeof(WCHAR);
793     if (!pbFormat)
794     {
795         *pcbFormat = bytesNeeded;
796         ret = TRUE;
797     }
798     else if (*pcbFormat < bytesNeeded)
799     {
800         *pcbFormat = bytesNeeded;
801         SetLastError(ERROR_MORE_DATA);
802         ret = FALSE;
803     }
804     else
805     {
806         static const WCHAR fmt[] = { '%','0','2','x',' ',0 };
807         static const WCHAR endFmt[] = { '%','0','2','x',0 };
808         DWORD i;
809         LPWSTR ptr = pbFormat;
810
811         *pcbFormat = bytesNeeded;
812         if (cbEncoded)
813         {
814             for (i = 0; i < cbEncoded; i++)
815             {
816                 if (i < cbEncoded - 1)
817                     ptr += sprintfW(ptr, fmt, pbEncoded[i]);
818                 else
819                     ptr += sprintfW(ptr, endFmt, pbEncoded[i]);
820             }
821         }
822         else
823             *ptr = 0;
824         ret = TRUE;
825     }
826     return ret;
827 }
828
829 #define MAX_STRING_RESOURCE_LEN 128
830
831 static const WCHAR commaSpace[] = { ',',' ',0 };
832
833 struct BitToString
834 {
835     BYTE bit;
836     int id;
837     WCHAR str[MAX_STRING_RESOURCE_LEN];
838 };
839
840 static BOOL CRYPT_FormatBits(BYTE bits, const struct BitToString *map,
841  DWORD mapEntries, void *pbFormat, DWORD *pcbFormat, BOOL *first)
842 {
843     DWORD bytesNeeded = sizeof(WCHAR);
844     int i;
845     BOOL ret = TRUE, localFirst = *first;
846
847     for (i = 0; i < mapEntries; i++)
848         if (bits & map[i].bit)
849         {
850             if (!localFirst)
851                 bytesNeeded += strlenW(commaSpace) * sizeof(WCHAR);
852             localFirst = FALSE;
853             bytesNeeded += strlenW(map[i].str) * sizeof(WCHAR);
854         }
855     if (!pbFormat)
856     {
857         *first = localFirst;
858         *pcbFormat = bytesNeeded;
859     }
860     else if (*pcbFormat < bytesNeeded)
861     {
862         *first = localFirst;
863         *pcbFormat = bytesNeeded;
864         SetLastError(ERROR_MORE_DATA);
865         ret = FALSE;
866     }
867     else
868     {
869         LPWSTR str = pbFormat;
870
871         localFirst = *first;
872         *pcbFormat = bytesNeeded;
873         for (i = 0; i < mapEntries; i++)
874             if (bits & map[i].bit)
875             {
876                 if (!localFirst)
877                 {
878                     strcpyW(str, commaSpace);
879                     str += strlenW(commaSpace);
880                 }
881                 localFirst = FALSE;
882                 strcpyW(str, map[i].str);
883                 str += strlenW(map[i].str);
884             }
885         *first = localFirst;
886     }
887     return ret;
888 }
889
890 static struct BitToString keyUsageByte0Map[] = {
891  { CERT_DIGITAL_SIGNATURE_KEY_USAGE, IDS_DIGITAL_SIGNATURE, { 0 } },
892  { CERT_NON_REPUDIATION_KEY_USAGE, IDS_NON_REPUDIATION, { 0 } },
893  { CERT_KEY_ENCIPHERMENT_KEY_USAGE, IDS_KEY_ENCIPHERMENT, { 0 } },
894  { CERT_DATA_ENCIPHERMENT_KEY_USAGE, IDS_DATA_ENCIPHERMENT, { 0 } },
895  { CERT_KEY_AGREEMENT_KEY_USAGE, IDS_KEY_AGREEMENT, { 0 } },
896  { CERT_KEY_CERT_SIGN_KEY_USAGE, IDS_CERT_SIGN, { 0 } },
897  { CERT_OFFLINE_CRL_SIGN_KEY_USAGE, IDS_OFFLINE_CRL_SIGN, { 0 } },
898  { CERT_CRL_SIGN_KEY_USAGE, IDS_CRL_SIGN, { 0 } },
899  { CERT_ENCIPHER_ONLY_KEY_USAGE, IDS_ENCIPHER_ONLY, { 0 } },
900 };
901 static struct BitToString keyUsageByte1Map[] = {
902  { CERT_DECIPHER_ONLY_KEY_USAGE, IDS_DECIPHER_ONLY, { 0 } },
903 };
904
905 static BOOL WINAPI CRYPT_FormatKeyUsage(DWORD dwCertEncodingType,
906  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
907  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
908  DWORD *pcbFormat)
909 {
910     DWORD size;
911     CRYPT_BIT_BLOB *bits;
912     BOOL ret;
913
914     if (!cbEncoded)
915     {
916         SetLastError(E_INVALIDARG);
917         return FALSE;
918     }
919     if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_KEY_USAGE,
920      pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &bits, &size)))
921     {
922         WCHAR infoNotAvailable[MAX_STRING_RESOURCE_LEN];
923         DWORD bytesNeeded = sizeof(WCHAR);
924
925         LoadStringW(hInstance, IDS_INFO_NOT_AVAILABLE, infoNotAvailable,
926          sizeof(infoNotAvailable) / sizeof(infoNotAvailable[0]));
927         if (!bits->cbData || bits->cbData > 2)
928         {
929             bytesNeeded += strlenW(infoNotAvailable) * sizeof(WCHAR);
930             if (!pbFormat)
931                 *pcbFormat = bytesNeeded;
932             else if (*pcbFormat < bytesNeeded)
933             {
934                 *pcbFormat = bytesNeeded;
935                 SetLastError(ERROR_MORE_DATA);
936                 ret = FALSE;
937             }
938             else
939             {
940                 LPWSTR str = pbFormat;
941
942                 *pcbFormat = bytesNeeded;
943                 strcpyW(str, infoNotAvailable);
944             }
945         }
946         else
947         {
948             static BOOL stringsLoaded = FALSE;
949             int i;
950             DWORD bitStringLen;
951             BOOL first = TRUE;
952
953             if (!stringsLoaded)
954             {
955                 for (i = 0;
956                  i < sizeof(keyUsageByte0Map) / sizeof(keyUsageByte0Map[0]);
957                  i++)
958                     LoadStringW(hInstance, keyUsageByte0Map[i].id,
959                      keyUsageByte0Map[i].str, MAX_STRING_RESOURCE_LEN);
960                 for (i = 0;
961                  i < sizeof(keyUsageByte1Map) / sizeof(keyUsageByte1Map[0]);
962                  i++)
963                     LoadStringW(hInstance, keyUsageByte1Map[i].id,
964                      keyUsageByte1Map[i].str, MAX_STRING_RESOURCE_LEN);
965                 stringsLoaded = TRUE;
966             }
967             CRYPT_FormatBits(bits->pbData[0], keyUsageByte0Map,
968              sizeof(keyUsageByte0Map) / sizeof(keyUsageByte0Map[0]),
969              NULL, &bitStringLen, &first);
970             bytesNeeded += bitStringLen;
971             if (bits->cbData == 2)
972             {
973                 CRYPT_FormatBits(bits->pbData[1], keyUsageByte1Map,
974                  sizeof(keyUsageByte1Map) / sizeof(keyUsageByte1Map[0]),
975                  NULL, &bitStringLen, &first);
976                 bytesNeeded += bitStringLen;
977             }
978             bytesNeeded += 3 * sizeof(WCHAR); /* " (" + ")" */
979             CRYPT_FormatHexString(0, 0, 0, NULL, NULL, bits->pbData,
980              bits->cbData, NULL, &size);
981             bytesNeeded += size;
982             if (!pbFormat)
983                 *pcbFormat = bytesNeeded;
984             else if (*pcbFormat < bytesNeeded)
985             {
986                 *pcbFormat = bytesNeeded;
987                 SetLastError(ERROR_MORE_DATA);
988                 ret = FALSE;
989             }
990             else
991             {
992                 LPWSTR str = pbFormat;
993
994                 bitStringLen = bytesNeeded;
995                 first = TRUE;
996                 CRYPT_FormatBits(bits->pbData[0], keyUsageByte0Map,
997                  sizeof(keyUsageByte0Map) / sizeof(keyUsageByte0Map[0]),
998                  str, &bitStringLen, &first);
999                 str += bitStringLen / sizeof(WCHAR) - 1;
1000                 if (bits->cbData == 2)
1001                 {
1002                     bitStringLen = bytesNeeded;
1003                     CRYPT_FormatBits(bits->pbData[1], keyUsageByte1Map,
1004                      sizeof(keyUsageByte1Map) / sizeof(keyUsageByte1Map[0]),
1005                      str, &bitStringLen, &first);
1006                     str += bitStringLen / sizeof(WCHAR) - 1;
1007                 }
1008                 *str++ = ' ';
1009                 *str++ = '(';
1010                 CRYPT_FormatHexString(0, 0, 0, NULL, NULL, bits->pbData,
1011                  bits->cbData, str, &size);
1012                 str += size / sizeof(WCHAR) - 1;
1013                 *str++ = ')';
1014                 *str = 0;
1015             }
1016         }
1017         LocalFree(bits);
1018     }
1019     return ret;
1020 }
1021
1022 static const WCHAR crlf[] = { '\r','\n',0 };
1023
1024 static WCHAR subjectTypeHeader[MAX_STRING_RESOURCE_LEN];
1025 static WCHAR subjectTypeCA[MAX_STRING_RESOURCE_LEN];
1026 static WCHAR subjectTypeEndCert[MAX_STRING_RESOURCE_LEN];
1027 static WCHAR pathLengthHeader[MAX_STRING_RESOURCE_LEN];
1028
1029 static BOOL WINAPI CRYPT_FormatBasicConstraints2(DWORD dwCertEncodingType,
1030  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
1031  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
1032  DWORD *pcbFormat)
1033 {
1034     DWORD size;
1035     CERT_BASIC_CONSTRAINTS2_INFO *info;
1036     BOOL ret;
1037
1038     if (!cbEncoded)
1039     {
1040         SetLastError(E_INVALIDARG);
1041         return FALSE;
1042     }
1043     if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_BASIC_CONSTRAINTS2,
1044      pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size)))
1045     {
1046         static const WCHAR pathFmt[] = { '%','d',0 };
1047         static BOOL stringsLoaded = FALSE;
1048         DWORD bytesNeeded = sizeof(WCHAR); /* space for the NULL terminator */
1049         WCHAR pathLength[MAX_STRING_RESOURCE_LEN];
1050         LPCWSTR sep, subjectType;
1051         DWORD sepLen;
1052
1053         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1054         {
1055             sep = crlf;
1056             sepLen = strlenW(crlf) * sizeof(WCHAR);
1057         }
1058         else
1059         {
1060             sep = commaSpace;
1061             sepLen = strlenW(commaSpace) * sizeof(WCHAR);
1062         }
1063
1064         if (!stringsLoaded)
1065         {
1066             LoadStringW(hInstance, IDS_SUBJECT_TYPE, subjectTypeHeader,
1067              sizeof(subjectTypeHeader) / sizeof(subjectTypeHeader[0]));
1068             LoadStringW(hInstance, IDS_SUBJECT_TYPE_CA, subjectTypeCA,
1069              sizeof(subjectTypeCA) / sizeof(subjectTypeCA[0]));
1070             LoadStringW(hInstance, IDS_SUBJECT_TYPE_END_CERT,
1071              subjectTypeEndCert,
1072              sizeof(subjectTypeEndCert) / sizeof(subjectTypeEndCert[0]));
1073             LoadStringW(hInstance, IDS_PATH_LENGTH, pathLengthHeader,
1074              sizeof(pathLengthHeader) / sizeof(pathLengthHeader[0]));
1075             stringsLoaded = TRUE;
1076         }
1077         bytesNeeded += strlenW(subjectTypeHeader) * sizeof(WCHAR);
1078         if (info->fCA)
1079             subjectType = subjectTypeCA;
1080         else
1081             subjectType = subjectTypeEndCert;
1082         bytesNeeded += strlenW(subjectType) * sizeof(WCHAR);
1083         bytesNeeded += sepLen;
1084         bytesNeeded += strlenW(pathLengthHeader) * sizeof(WCHAR);
1085         if (info->fPathLenConstraint)
1086             sprintfW(pathLength, pathFmt, info->dwPathLenConstraint);
1087         else
1088             LoadStringW(hInstance, IDS_PATH_LENGTH_NONE, pathLength,
1089              sizeof(pathLength) / sizeof(pathLength[0]));
1090         bytesNeeded += strlenW(pathLength) * sizeof(WCHAR);
1091         if (!pbFormat)
1092             *pcbFormat = bytesNeeded;
1093         else if (*pcbFormat < bytesNeeded)
1094         {
1095             *pcbFormat = bytesNeeded;
1096             SetLastError(ERROR_MORE_DATA);
1097             ret = FALSE;
1098         }
1099         else
1100         {
1101             LPWSTR str = pbFormat;
1102
1103             *pcbFormat = bytesNeeded;
1104             strcpyW(str, subjectTypeHeader);
1105             str += strlenW(subjectTypeHeader);
1106             strcpyW(str, subjectType);
1107             str += strlenW(subjectType);
1108             strcpyW(str, sep);
1109             str += sepLen / sizeof(WCHAR);
1110             strcpyW(str, pathLengthHeader);
1111             str += strlenW(pathLengthHeader);
1112             strcpyW(str, pathLength);
1113             str += strlenW(pathLength);
1114         }
1115         LocalFree(info);
1116     }
1117     return ret;
1118 }
1119
1120 static BOOL CRYPT_FormatHexStringWithPrefix(const CRYPT_DATA_BLOB *blob, int id,
1121  LPWSTR str, DWORD *pcbStr)
1122 {
1123     WCHAR buf[MAX_STRING_RESOURCE_LEN];
1124     DWORD bytesNeeded;
1125     BOOL ret;
1126
1127     LoadStringW(hInstance, id, buf, sizeof(buf) / sizeof(buf[0]));
1128     CRYPT_FormatHexString(X509_ASN_ENCODING, 0, 0, NULL, NULL,
1129      blob->pbData, blob->cbData, NULL, &bytesNeeded);
1130     bytesNeeded += strlenW(buf) * sizeof(WCHAR);
1131     if (!str)
1132     {
1133         *pcbStr = bytesNeeded;
1134         ret = TRUE;
1135     }
1136     else if (*pcbStr < bytesNeeded)
1137     {
1138         *pcbStr = bytesNeeded;
1139         SetLastError(ERROR_MORE_DATA);
1140         ret = FALSE;
1141     }
1142     else
1143     {
1144         *pcbStr = bytesNeeded;
1145         strcpyW(str, buf);
1146         str += strlenW(str);
1147         bytesNeeded -= strlenW(str) * sizeof(WCHAR);
1148         ret = CRYPT_FormatHexString(X509_ASN_ENCODING, 0, 0, NULL, NULL,
1149          blob->pbData, blob->cbData, str, &bytesNeeded);
1150     }
1151     return ret;
1152 }
1153
1154 static BOOL CRYPT_FormatKeyId(const CRYPT_DATA_BLOB *keyId, LPWSTR str,
1155  DWORD *pcbStr)
1156 {
1157     return CRYPT_FormatHexStringWithPrefix(keyId, IDS_KEY_ID, str, pcbStr);
1158 }
1159
1160 static BOOL CRYPT_FormatCertSerialNumber(const CRYPT_DATA_BLOB *serialNum, LPWSTR str,
1161  DWORD *pcbStr)
1162 {
1163     return CRYPT_FormatHexStringWithPrefix(serialNum, IDS_CERT_SERIAL_NUMBER,
1164      str, pcbStr);
1165 }
1166
1167 static const WCHAR indent[] = { ' ',' ',' ',' ',' ',0 };
1168 static const WCHAR colonCrlf[] = { ':','\r','\n',0 };
1169
1170 static BOOL CRYPT_FormatAltNameEntry(DWORD dwFormatStrType, DWORD indentLevel,
1171  CERT_ALT_NAME_ENTRY *entry, LPWSTR str, DWORD *pcbStr)
1172 {
1173     BOOL ret;
1174     WCHAR buf[MAX_STRING_RESOURCE_LEN];
1175     WCHAR mask[MAX_STRING_RESOURCE_LEN];
1176     WCHAR ipAddrBuf[32];
1177     WCHAR maskBuf[16];
1178     DWORD bytesNeeded = sizeof(WCHAR);
1179     DWORD strType = CERT_X500_NAME_STR | CERT_NAME_STR_REVERSE_FLAG;
1180
1181     if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1182         bytesNeeded += indentLevel * strlenW(indent) * sizeof(WCHAR);
1183     switch (entry->dwAltNameChoice)
1184     {
1185     case CERT_ALT_NAME_RFC822_NAME:
1186         LoadStringW(hInstance, IDS_ALT_NAME_RFC822_NAME, buf,
1187          sizeof(buf) / sizeof(buf[0]));
1188         bytesNeeded += strlenW(entry->u.pwszRfc822Name) * sizeof(WCHAR);
1189         ret = TRUE;
1190         break;
1191     case CERT_ALT_NAME_DNS_NAME:
1192         LoadStringW(hInstance, IDS_ALT_NAME_DNS_NAME, buf,
1193          sizeof(buf) / sizeof(buf[0]));
1194         bytesNeeded += strlenW(entry->u.pwszDNSName) * sizeof(WCHAR);
1195         ret = TRUE;
1196         break;
1197     case CERT_ALT_NAME_DIRECTORY_NAME:
1198     {
1199         DWORD directoryNameLen;
1200
1201         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1202             strType |= CERT_NAME_STR_CRLF_FLAG;
1203         directoryNameLen = cert_name_to_str_with_indent(X509_ASN_ENCODING,
1204          indentLevel + 1, &entry->u.DirectoryName, strType, NULL, 0);
1205         LoadStringW(hInstance, IDS_ALT_NAME_DIRECTORY_NAME, buf,
1206          sizeof(buf) / sizeof(buf[0]));
1207         bytesNeeded += (directoryNameLen - 1) * sizeof(WCHAR);
1208         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1209             bytesNeeded += strlenW(colonCrlf) * sizeof(WCHAR);
1210         else
1211             bytesNeeded += sizeof(WCHAR); /* '=' */
1212         ret = TRUE;
1213         break;
1214     }
1215     case CERT_ALT_NAME_URL:
1216         LoadStringW(hInstance, IDS_ALT_NAME_URL, buf,
1217          sizeof(buf) / sizeof(buf[0]));
1218         bytesNeeded += strlenW(entry->u.pwszURL) * sizeof(WCHAR);
1219         ret = TRUE;
1220         break;
1221     case CERT_ALT_NAME_IP_ADDRESS:
1222     {
1223         static const WCHAR ipAddrWithMaskFmt[] = { '%','d','.','%','d','.',
1224          '%','d','.','%','d','/','%','d','.','%','d','.','%','d','.','%','d',0
1225         };
1226         static const WCHAR ipAddrFmt[] = { '%','d','.','%','d','.','%','d',
1227          '.','%','d',0 };
1228
1229         LoadStringW(hInstance, IDS_ALT_NAME_IP_ADDRESS, buf,
1230          sizeof(buf) / sizeof(buf[0]));
1231         if (entry->u.IPAddress.cbData == 8)
1232         {
1233             if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1234             {
1235                 LoadStringW(hInstance, IDS_ALT_NAME_MASK, mask,
1236                  sizeof(mask) / sizeof(mask[0]));
1237                 bytesNeeded += strlenW(mask) * sizeof(WCHAR);
1238                 sprintfW(ipAddrBuf, ipAddrFmt,
1239                  entry->u.IPAddress.pbData[0],
1240                  entry->u.IPAddress.pbData[1],
1241                  entry->u.IPAddress.pbData[2],
1242                  entry->u.IPAddress.pbData[3]);
1243                 bytesNeeded += strlenW(ipAddrBuf) * sizeof(WCHAR);
1244                 /* indent again, for the mask line */
1245                 bytesNeeded += indentLevel * strlenW(indent) * sizeof(WCHAR);
1246                 sprintfW(maskBuf, ipAddrFmt,
1247                  entry->u.IPAddress.pbData[4],
1248                  entry->u.IPAddress.pbData[5],
1249                  entry->u.IPAddress.pbData[6],
1250                  entry->u.IPAddress.pbData[7]);
1251                 bytesNeeded += strlenW(maskBuf) * sizeof(WCHAR);
1252                 bytesNeeded += strlenW(crlf) * sizeof(WCHAR);
1253             }
1254             else
1255             {
1256                 sprintfW(ipAddrBuf, ipAddrWithMaskFmt,
1257                  entry->u.IPAddress.pbData[0],
1258                  entry->u.IPAddress.pbData[1],
1259                  entry->u.IPAddress.pbData[2],
1260                  entry->u.IPAddress.pbData[3],
1261                  entry->u.IPAddress.pbData[4],
1262                  entry->u.IPAddress.pbData[5],
1263                  entry->u.IPAddress.pbData[6],
1264                  entry->u.IPAddress.pbData[7]);
1265                 bytesNeeded += (strlenW(ipAddrBuf) + 1) * sizeof(WCHAR);
1266             }
1267             ret = TRUE;
1268         }
1269         else
1270         {
1271             FIXME("unknown IP address format (%d bytes)\n",
1272              entry->u.IPAddress.cbData);
1273             ret = FALSE;
1274         }
1275         break;
1276     }
1277     default:
1278         FIXME("unimplemented for %d\n", entry->dwAltNameChoice);
1279         ret = FALSE;
1280     }
1281     if (ret)
1282     {
1283         bytesNeeded += strlenW(buf) * sizeof(WCHAR);
1284         if (!str)
1285             *pcbStr = bytesNeeded;
1286         else if (*pcbStr < bytesNeeded)
1287         {
1288             *pcbStr = bytesNeeded;
1289             SetLastError(ERROR_MORE_DATA);
1290             ret = FALSE;
1291         }
1292         else
1293         {
1294             DWORD i;
1295
1296             *pcbStr = bytesNeeded;
1297             if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1298             {
1299                 for (i = 0; i < indentLevel; i++)
1300                 {
1301                     strcpyW(str, indent);
1302                     str += strlenW(indent);
1303                 }
1304             }
1305             strcpyW(str, buf);
1306             str += strlenW(str);
1307             switch (entry->dwAltNameChoice)
1308             {
1309             case CERT_ALT_NAME_RFC822_NAME:
1310             case CERT_ALT_NAME_DNS_NAME:
1311             case CERT_ALT_NAME_URL:
1312                 strcpyW(str, entry->u.pwszURL);
1313                 break;
1314             case CERT_ALT_NAME_DIRECTORY_NAME:
1315                 if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1316                 {
1317                     strcpyW(str, colonCrlf);
1318                     str += strlenW(colonCrlf);
1319                 }
1320                 else
1321                     *str++ = '=';
1322                 cert_name_to_str_with_indent(X509_ASN_ENCODING,
1323                  indentLevel + 1, &entry->u.DirectoryName, strType, str,
1324                  bytesNeeded / sizeof(WCHAR));
1325                 break;
1326             case CERT_ALT_NAME_IP_ADDRESS:
1327                 if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1328                 {
1329                     strcpyW(str, ipAddrBuf);
1330                     str += strlenW(ipAddrBuf);
1331                     strcpyW(str, crlf);
1332                     str += strlenW(crlf);
1333                     if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1334                     {
1335                         for (i = 0; i < indentLevel; i++)
1336                         {
1337                             strcpyW(str, indent);
1338                             str += strlenW(indent);
1339                         }
1340                     }
1341                     strcpyW(str, mask);
1342                     str += strlenW(mask);
1343                     strcpyW(str, maskBuf);
1344                 }
1345                 else
1346                     strcpyW(str, ipAddrBuf);
1347                 break;
1348             }
1349         }
1350     }
1351     return ret;
1352 }
1353
1354 static BOOL CRYPT_FormatAltNameInfo(DWORD dwFormatStrType, DWORD indentLevel,
1355  CERT_ALT_NAME_INFO *name, LPWSTR str, DWORD *pcbStr)
1356 {
1357     DWORD i, size, bytesNeeded = 0;
1358     BOOL ret = TRUE;
1359     LPCWSTR sep;
1360     DWORD sepLen;
1361
1362     if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1363     {
1364         sep = crlf;
1365         sepLen = strlenW(crlf) * sizeof(WCHAR);
1366     }
1367     else
1368     {
1369         sep = commaSpace;
1370         sepLen = strlenW(commaSpace) * sizeof(WCHAR);
1371     }
1372
1373     for (i = 0; ret && i < name->cAltEntry; i++)
1374     {
1375         ret = CRYPT_FormatAltNameEntry(dwFormatStrType, indentLevel,
1376          &name->rgAltEntry[i], NULL, &size);
1377         if (ret)
1378         {
1379             bytesNeeded += size - sizeof(WCHAR);
1380             if (i < name->cAltEntry - 1)
1381                 bytesNeeded += sepLen;
1382         }
1383     }
1384     if (ret)
1385     {
1386         bytesNeeded += sizeof(WCHAR);
1387         if (!str)
1388             *pcbStr = bytesNeeded;
1389         else if (*pcbStr < bytesNeeded)
1390         {
1391             *pcbStr = bytesNeeded;
1392             SetLastError(ERROR_MORE_DATA);
1393             ret = FALSE;
1394         }
1395         else
1396         {
1397             *pcbStr = bytesNeeded;
1398             for (i = 0; ret && i < name->cAltEntry; i++)
1399             {
1400                 ret = CRYPT_FormatAltNameEntry(dwFormatStrType, indentLevel,
1401                  &name->rgAltEntry[i], str, &size);
1402                 if (ret)
1403                 {
1404                     str += size / sizeof(WCHAR) - 1;
1405                     if (i < name->cAltEntry - 1)
1406                     {
1407                         strcpyW(str, sep);
1408                         str += sepLen / sizeof(WCHAR);
1409                     }
1410                 }
1411             }
1412         }
1413     }
1414     return ret;
1415 }
1416
1417 static const WCHAR colonSep[] = { ':',' ',0 };
1418
1419 static BOOL WINAPI CRYPT_FormatAltName(DWORD dwCertEncodingType,
1420  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
1421  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
1422  DWORD *pcbFormat)
1423 {
1424     BOOL ret;
1425     CERT_ALT_NAME_INFO *info;
1426     DWORD size;
1427
1428     if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_ALTERNATE_NAME,
1429      pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size)))
1430     {
1431         ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 0, info, pbFormat, pcbFormat);
1432         LocalFree(info);
1433     }
1434     return ret;
1435 }
1436
1437 static BOOL CRYPT_FormatCertIssuer(DWORD dwFormatStrType,
1438  CERT_ALT_NAME_INFO *issuer, LPWSTR str, DWORD *pcbStr)
1439 {
1440     WCHAR buf[MAX_STRING_RESOURCE_LEN];
1441     DWORD bytesNeeded, sepLen;
1442     LPCWSTR sep;
1443     BOOL ret;
1444
1445     LoadStringW(hInstance, IDS_CERT_ISSUER, buf, sizeof(buf) / sizeof(buf[0]));
1446     ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 1, issuer, NULL,
1447      &bytesNeeded);
1448     bytesNeeded += strlenW(buf) * sizeof(WCHAR);
1449     if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1450     {
1451         sep = colonCrlf;
1452         sepLen = strlenW(colonCrlf) * sizeof(WCHAR);
1453     }
1454     else
1455     {
1456         sep = colonSep;
1457         sepLen = strlenW(colonSep) * sizeof(WCHAR);
1458     }
1459     bytesNeeded += sepLen;
1460     if (ret)
1461     {
1462         if (!str)
1463             *pcbStr = bytesNeeded;
1464         else if (*pcbStr < bytesNeeded)
1465         {
1466             *pcbStr = bytesNeeded;
1467             SetLastError(ERROR_MORE_DATA);
1468             ret = FALSE;
1469         }
1470         else
1471         {
1472             *pcbStr = bytesNeeded;
1473             strcpyW(str, buf);
1474             bytesNeeded -= strlenW(str) * sizeof(WCHAR);
1475             str += strlenW(str);
1476             strcpyW(str, sep);
1477             str += sepLen / sizeof(WCHAR);
1478             ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 1, issuer, str,
1479              &bytesNeeded);
1480         }
1481     }
1482     return ret;
1483 }
1484
1485 static BOOL WINAPI CRYPT_FormatAuthorityKeyId2(DWORD dwCertEncodingType,
1486  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
1487  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
1488  DWORD *pcbFormat)
1489 {
1490     CERT_AUTHORITY_KEY_ID2_INFO *info;
1491     DWORD size;
1492     BOOL ret = FALSE;
1493
1494     if (!cbEncoded)
1495     {
1496         SetLastError(E_INVALIDARG);
1497         return FALSE;
1498     }
1499     if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_AUTHORITY_KEY_ID2,
1500      pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size)))
1501     {
1502         DWORD bytesNeeded = sizeof(WCHAR); /* space for the NULL terminator */
1503         LPCWSTR sep;
1504         DWORD sepLen;
1505         BOOL needSeparator = FALSE;
1506
1507         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1508         {
1509             sep = crlf;
1510             sepLen = strlenW(crlf) * sizeof(WCHAR);
1511         }
1512         else
1513         {
1514             sep = commaSpace;
1515             sepLen = strlenW(commaSpace) * sizeof(WCHAR);
1516         }
1517
1518         if (info->KeyId.cbData)
1519         {
1520             needSeparator = TRUE;
1521             ret = CRYPT_FormatKeyId(&info->KeyId, NULL, &size);
1522             if (ret)
1523             {
1524                 /* don't include NULL-terminator more than once */
1525                 bytesNeeded += size - sizeof(WCHAR);
1526             }
1527         }
1528         if (info->AuthorityCertIssuer.cAltEntry)
1529         {
1530             if (needSeparator)
1531                 bytesNeeded += sepLen;
1532             needSeparator = TRUE;
1533             ret = CRYPT_FormatCertIssuer(dwFormatStrType,
1534              &info->AuthorityCertIssuer, NULL, &size);
1535             if (ret)
1536             {
1537                 /* don't include NULL-terminator more than once */
1538                 bytesNeeded += size - sizeof(WCHAR);
1539             }
1540         }
1541         if (info->AuthorityCertSerialNumber.cbData)
1542         {
1543             if (needSeparator)
1544                 bytesNeeded += sepLen;
1545             ret = CRYPT_FormatCertSerialNumber(
1546              &info->AuthorityCertSerialNumber, NULL, &size);
1547             if (ret)
1548             {
1549                 /* don't include NULL-terminator more than once */
1550                 bytesNeeded += size - sizeof(WCHAR);
1551             }
1552         }
1553         if (ret)
1554         {
1555             if (!pbFormat)
1556                 *pcbFormat = bytesNeeded;
1557             else if (*pcbFormat < bytesNeeded)
1558             {
1559                 *pcbFormat = bytesNeeded;
1560                 SetLastError(ERROR_MORE_DATA);
1561                 ret = FALSE;
1562             }
1563             else
1564             {
1565                 LPWSTR str = pbFormat;
1566
1567                 *pcbFormat = bytesNeeded;
1568                 needSeparator = FALSE;
1569                 if (info->KeyId.cbData)
1570                 {
1571                     needSeparator = TRUE;
1572                     /* Overestimate size available, it's already been checked
1573                      * above.
1574                      */
1575                     size = bytesNeeded;
1576                     ret = CRYPT_FormatKeyId(&info->KeyId, str, &size);
1577                     if (ret)
1578                         str += size / sizeof(WCHAR) - 1;
1579                 }
1580                 if (info->AuthorityCertIssuer.cAltEntry)
1581                 {
1582                     if (needSeparator)
1583                     {
1584                         strcpyW(str, sep);
1585                         str += sepLen / sizeof(WCHAR);
1586                     }
1587                     needSeparator = TRUE;
1588                     /* Overestimate size available, it's already been checked
1589                      * above.
1590                      */
1591                     size = bytesNeeded;
1592                     ret = CRYPT_FormatCertIssuer(dwFormatStrType,
1593                      &info->AuthorityCertIssuer, str, &size);
1594                     if (ret)
1595                         str += size / sizeof(WCHAR) - 1;
1596                 }
1597                 if (info->AuthorityCertSerialNumber.cbData)
1598                 {
1599                     if (needSeparator)
1600                     {
1601                         strcpyW(str, sep);
1602                         str += sepLen / sizeof(WCHAR);
1603                     }
1604                     /* Overestimate size available, it's already been checked
1605                      * above.
1606                      */
1607                     size = bytesNeeded;
1608                     ret = CRYPT_FormatCertSerialNumber(
1609                      &info->AuthorityCertSerialNumber, str, &size);
1610                 }
1611             }
1612         }
1613         LocalFree(info);
1614     }
1615     return ret;
1616 }
1617
1618 static WCHAR aia[MAX_STRING_RESOURCE_LEN];
1619 static WCHAR accessMethod[MAX_STRING_RESOURCE_LEN];
1620 static WCHAR ocsp[MAX_STRING_RESOURCE_LEN];
1621 static WCHAR caIssuers[MAX_STRING_RESOURCE_LEN];
1622 static WCHAR unknown[MAX_STRING_RESOURCE_LEN];
1623 static WCHAR accessLocation[MAX_STRING_RESOURCE_LEN];
1624
1625 static BOOL WINAPI CRYPT_FormatAuthorityInfoAccess(DWORD dwCertEncodingType,
1626  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
1627  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
1628  DWORD *pcbFormat)
1629 {
1630     CERT_AUTHORITY_INFO_ACCESS *info;
1631     DWORD size;
1632     BOOL ret = FALSE;
1633
1634     if (!cbEncoded)
1635     {
1636         SetLastError(E_INVALIDARG);
1637         return FALSE;
1638     }
1639     if ((ret = CryptDecodeObjectEx(dwCertEncodingType,
1640      X509_AUTHORITY_INFO_ACCESS, pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG,
1641      NULL, &info, &size)))
1642     {
1643         DWORD bytesNeeded = sizeof(WCHAR);
1644
1645         if (!info->cAccDescr)
1646         {
1647             WCHAR infoNotAvailable[MAX_STRING_RESOURCE_LEN];
1648
1649             LoadStringW(hInstance, IDS_INFO_NOT_AVAILABLE, infoNotAvailable,
1650              sizeof(infoNotAvailable) / sizeof(infoNotAvailable[0]));
1651             bytesNeeded += strlenW(infoNotAvailable) * sizeof(WCHAR);
1652             if (!pbFormat)
1653                 *pcbFormat = bytesNeeded;
1654             else if (*pcbFormat < bytesNeeded)
1655             {
1656                 *pcbFormat = bytesNeeded;
1657                 SetLastError(ERROR_MORE_DATA);
1658                 ret = FALSE;
1659             }
1660             else
1661             {
1662                 *pcbFormat = bytesNeeded;
1663                 strcpyW(pbFormat, infoNotAvailable);
1664             }
1665         }
1666         else
1667         {
1668             static const WCHAR numFmt[] = { '%','d',0 };
1669             static const WCHAR equal[] = { '=',0 };
1670             static BOOL stringsLoaded = FALSE;
1671             DWORD i;
1672             LPCWSTR headingSep, accessMethodSep, locationSep;
1673             WCHAR accessDescrNum[11];
1674
1675             if (!stringsLoaded)
1676             {
1677                 LoadStringW(hInstance, IDS_AIA, aia,
1678                  sizeof(aia) / sizeof(aia[0]));
1679                 LoadStringW(hInstance, IDS_ACCESS_METHOD, accessMethod,
1680                  sizeof(accessMethod) / sizeof(accessMethod[0]));
1681                 LoadStringW(hInstance, IDS_ACCESS_METHOD_OCSP, ocsp,
1682                  sizeof(ocsp) / sizeof(ocsp[0]));
1683                 LoadStringW(hInstance, IDS_ACCESS_METHOD_CA_ISSUERS, caIssuers,
1684                  sizeof(caIssuers) / sizeof(caIssuers[0]));
1685                 LoadStringW(hInstance, IDS_ACCESS_METHOD_UNKNOWN, unknown,
1686                  sizeof(unknown) / sizeof(unknown[0]));
1687                 LoadStringW(hInstance, IDS_ACCESS_LOCATION, accessLocation,
1688                  sizeof(accessLocation) / sizeof(accessLocation[0]));
1689                 stringsLoaded = TRUE;
1690             }
1691             if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1692             {
1693                 headingSep = crlf;
1694                 accessMethodSep = crlf;
1695                 locationSep = colonCrlf;
1696             }
1697             else
1698             {
1699                 headingSep = colonSep;
1700                 accessMethodSep = commaSpace;
1701                 locationSep = equal;
1702             }
1703
1704             for (i = 0; ret && i < info->cAccDescr; i++)
1705             {
1706                 /* Heading */
1707                 bytesNeeded += sizeof(WCHAR); /* left bracket */
1708                 sprintfW(accessDescrNum, numFmt, i + 1);
1709                 bytesNeeded += strlenW(accessDescrNum) * sizeof(WCHAR);
1710                 bytesNeeded += sizeof(WCHAR); /* right bracket */
1711                 bytesNeeded += strlenW(aia) * sizeof(WCHAR);
1712                 bytesNeeded += strlenW(headingSep) * sizeof(WCHAR);
1713                 /* Access method */
1714                 bytesNeeded += strlenW(accessMethod) * sizeof(WCHAR);
1715                 if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1716                     bytesNeeded += strlenW(indent) * sizeof(WCHAR);
1717                 if (!strcmp(info->rgAccDescr[i].pszAccessMethod,
1718                  szOID_PKIX_OCSP))
1719                     bytesNeeded += strlenW(ocsp) * sizeof(WCHAR);
1720                 else if (!strcmp(info->rgAccDescr[i].pszAccessMethod,
1721                  szOID_PKIX_CA_ISSUERS))
1722                     bytesNeeded += strlenW(caIssuers) * sizeof(caIssuers);
1723                 else
1724                     bytesNeeded += strlenW(unknown) * sizeof(WCHAR);
1725                 bytesNeeded += sizeof(WCHAR); /* space */
1726                 bytesNeeded += sizeof(WCHAR); /* left paren */
1727                 bytesNeeded += strlen(info->rgAccDescr[i].pszAccessMethod)
1728                  * sizeof(WCHAR);
1729                 bytesNeeded += sizeof(WCHAR); /* right paren */
1730                 /* Delimiter between access method and location */
1731                 bytesNeeded += strlenW(accessMethodSep) * sizeof(WCHAR);
1732                 if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1733                     bytesNeeded += strlenW(indent) * sizeof(WCHAR);
1734                 bytesNeeded += strlenW(accessLocation) * sizeof(WCHAR);
1735                 bytesNeeded += strlenW(locationSep) * sizeof(WCHAR);
1736                 ret = CRYPT_FormatAltNameEntry(dwFormatStrType, 2,
1737                  &info->rgAccDescr[i].AccessLocation, NULL, &size);
1738                 if (ret)
1739                     bytesNeeded += size - sizeof(WCHAR);
1740                 /* Need extra delimiter between access method entries */
1741                 if (i < info->cAccDescr - 1)
1742                     bytesNeeded += strlenW(accessMethodSep) * sizeof(WCHAR);
1743             }
1744             if (ret)
1745             {
1746                 if (!pbFormat)
1747                     *pcbFormat = bytesNeeded;
1748                 else if (*pcbFormat < bytesNeeded)
1749                 {
1750                     *pcbFormat = bytesNeeded;
1751                     SetLastError(ERROR_MORE_DATA);
1752                     ret = FALSE;
1753                 }
1754                 else
1755                 {
1756                     LPWSTR str = pbFormat;
1757                     DWORD altNameEntrySize;
1758
1759                     *pcbFormat = bytesNeeded;
1760                     for (i = 0; ret && i < info->cAccDescr; i++)
1761                     {
1762                         LPCSTR oidPtr;
1763
1764                         *str++ = '[';
1765                         sprintfW(accessDescrNum, numFmt, i + 1);
1766                         strcpyW(str, accessDescrNum);
1767                         str += strlenW(accessDescrNum);
1768                         *str++ = ']';
1769                         strcpyW(str, aia);
1770                         str += strlenW(aia);
1771                         strcpyW(str, headingSep);
1772                         str += strlenW(headingSep);
1773                         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1774                         {
1775                             strcpyW(str, indent);
1776                             str += strlenW(indent);
1777                         }
1778                         strcpyW(str, accessMethod);
1779                         str += strlenW(accessMethod);
1780                         if (!strcmp(info->rgAccDescr[i].pszAccessMethod,
1781                          szOID_PKIX_OCSP))
1782                         {
1783                             strcpyW(str, ocsp);
1784                             str += strlenW(ocsp);
1785                         }
1786                         else if (!strcmp(info->rgAccDescr[i].pszAccessMethod,
1787                          szOID_PKIX_CA_ISSUERS))
1788                         {
1789                             strcpyW(str, caIssuers);
1790                             str += strlenW(caIssuers);
1791                         }
1792                         else
1793                         {
1794                             strcpyW(str, unknown);
1795                             str += strlenW(unknown);
1796                         }
1797                         *str++ = ' ';
1798                         *str++ = '(';
1799                         for (oidPtr = info->rgAccDescr[i].pszAccessMethod;
1800                          *oidPtr; oidPtr++, str++)
1801                             *str = *oidPtr;
1802                         *str++ = ')';
1803                         strcpyW(str, accessMethodSep);
1804                         str += strlenW(accessMethodSep);
1805                         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1806                         {
1807                             strcpyW(str, indent);
1808                             str += strlenW(indent);
1809                         }
1810                         strcpyW(str, accessLocation);
1811                         str += strlenW(accessLocation);
1812                         strcpyW(str, locationSep);
1813                         str += strlenW(locationSep);
1814                         /* This overestimates the size available, but that
1815                          * won't matter since we checked earlier whether enough
1816                          * space for the entire string was available.
1817                          */
1818                         altNameEntrySize = bytesNeeded;
1819                         ret = CRYPT_FormatAltNameEntry(dwFormatStrType, 2,
1820                          &info->rgAccDescr[i].AccessLocation, str,
1821                          &altNameEntrySize);
1822                         if (ret)
1823                             str += altNameEntrySize / sizeof(WCHAR) - 1;
1824                         if (i < info->cAccDescr - 1)
1825                         {
1826                             strcpyW(str, accessMethodSep);
1827                             str += strlenW(accessMethodSep);
1828                         }
1829                     }
1830                 }
1831             }
1832         }
1833         LocalFree(info);
1834     }
1835     return ret;
1836 }
1837
1838 static WCHAR keyCompromise[MAX_STRING_RESOURCE_LEN];
1839 static WCHAR caCompromise[MAX_STRING_RESOURCE_LEN];
1840 static WCHAR affiliationChanged[MAX_STRING_RESOURCE_LEN];
1841 static WCHAR superseded[MAX_STRING_RESOURCE_LEN];
1842 static WCHAR operationCeased[MAX_STRING_RESOURCE_LEN];
1843 static WCHAR certificateHold[MAX_STRING_RESOURCE_LEN];
1844
1845 struct reason_map_entry
1846 {
1847     BYTE   reasonBit;
1848     LPWSTR reason;
1849     int    id;
1850 };
1851 static struct reason_map_entry reason_map[] = {
1852  { CRL_REASON_KEY_COMPROMISE_FLAG, keyCompromise, IDS_REASON_KEY_COMPROMISE },
1853  { CRL_REASON_CA_COMPROMISE_FLAG, caCompromise, IDS_REASON_CA_COMPROMISE },
1854  { CRL_REASON_AFFILIATION_CHANGED_FLAG, affiliationChanged,
1855    IDS_REASON_AFFILIATION_CHANGED },
1856  { CRL_REASON_SUPERSEDED_FLAG, superseded, IDS_REASON_SUPERSEDED },
1857  { CRL_REASON_CESSATION_OF_OPERATION_FLAG, operationCeased,
1858    IDS_REASON_CESSATION_OF_OPERATION },
1859  { CRL_REASON_CERTIFICATE_HOLD_FLAG, certificateHold,
1860    IDS_REASON_CERTIFICATE_HOLD },
1861 };
1862
1863 static BOOL CRYPT_FormatReason(DWORD dwFormatStrType,
1864  const CRYPT_BIT_BLOB *reasonFlags, LPWSTR str, DWORD *pcbStr)
1865 {
1866     static const WCHAR sep[] = { ',',' ',0 };
1867     static const WCHAR bitsFmt[] = { ' ','(','%','0','2','x',')',0 };
1868     static BOOL stringsLoaded = FALSE;
1869     int i, numReasons = 0;
1870     BOOL ret = TRUE;
1871     DWORD bytesNeeded = sizeof(WCHAR);
1872     WCHAR bits[6];
1873
1874     if (!stringsLoaded)
1875     {
1876         for (i = 0; i < sizeof(reason_map) / sizeof(reason_map[0]); i++)
1877             LoadStringW(hInstance, reason_map[i].id, reason_map[i].reason,
1878              MAX_STRING_RESOURCE_LEN);
1879         stringsLoaded = TRUE;
1880     }
1881     /* No need to check reasonFlags->cbData, we already know it's positive.
1882      * Ignore any other bytes, as they're for undefined bits.
1883      */
1884     for (i = 0; i < sizeof(reason_map) / sizeof(reason_map[0]); i++)
1885     {
1886         if (reasonFlags->pbData[0] & reason_map[i].reasonBit)
1887         {
1888             bytesNeeded += strlenW(reason_map[i].reason) * sizeof(WCHAR);
1889             if (numReasons++)
1890                 bytesNeeded += strlenW(sep) * sizeof(WCHAR);
1891         }
1892     }
1893     sprintfW(bits, bitsFmt, reasonFlags->pbData[0]);
1894     bytesNeeded += strlenW(bits);
1895     if (!str)
1896         *pcbStr = bytesNeeded;
1897     else if (*pcbStr < bytesNeeded)
1898     {
1899         *pcbStr = bytesNeeded;
1900         SetLastError(ERROR_MORE_DATA);
1901         ret = FALSE;
1902     }
1903     else
1904     {
1905         *pcbStr = bytesNeeded;
1906         for (i = 0; i < sizeof(reason_map) / sizeof(reason_map[0]); i++)
1907         {
1908             if (reasonFlags->pbData[0] & reason_map[i].reasonBit)
1909             {
1910                 strcpyW(str, reason_map[i].reason);
1911                 str += strlenW(reason_map[i].reason);
1912                 if (i < sizeof(reason_map) / sizeof(reason_map[0]) - 1 &&
1913                  numReasons)
1914                 {
1915                     strcpyW(str, sep);
1916                     str += strlenW(sep);
1917                 }
1918             }
1919         }
1920         strcpyW(str, bits);
1921     }
1922     return ret;
1923 }
1924
1925 static WCHAR crlDistPoint[MAX_STRING_RESOURCE_LEN];
1926 static WCHAR distPointName[MAX_STRING_RESOURCE_LEN];
1927 static WCHAR fullName[MAX_STRING_RESOURCE_LEN];
1928 static WCHAR rdnName[MAX_STRING_RESOURCE_LEN];
1929 static WCHAR reason[MAX_STRING_RESOURCE_LEN];
1930 static WCHAR issuer[MAX_STRING_RESOURCE_LEN];
1931
1932 static BOOL WINAPI CRYPT_FormatCRLDistPoints(DWORD dwCertEncodingType,
1933  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
1934  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
1935  DWORD *pcbFormat)
1936 {
1937     CRL_DIST_POINTS_INFO *info;
1938     DWORD size;
1939     BOOL ret = FALSE;
1940
1941     if (!cbEncoded)
1942     {
1943         SetLastError(E_INVALIDARG);
1944         return FALSE;
1945     }
1946     if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_CRL_DIST_POINTS,
1947      pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size)))
1948     {
1949         static const WCHAR numFmt[] = { '%','d',0 };
1950         static const WCHAR colon[] = { ':',0 };
1951         static BOOL stringsLoaded = FALSE;
1952         DWORD bytesNeeded = sizeof(WCHAR); /* space for NULL terminator */
1953         BOOL haveAnEntry = FALSE;
1954         LPCWSTR headingSep, nameSep;
1955         WCHAR distPointNum[11];
1956         DWORD i;
1957
1958         if (!stringsLoaded)
1959         {
1960             LoadStringW(hInstance, IDS_CRL_DIST_POINT, crlDistPoint,
1961              sizeof(crlDistPoint) / sizeof(crlDistPoint[0]));
1962             LoadStringW(hInstance, IDS_CRL_DIST_POINT_NAME, distPointName,
1963              sizeof(distPointName) / sizeof(distPointName[0]));
1964             LoadStringW(hInstance, IDS_CRL_DIST_POINT_FULL_NAME, fullName,
1965              sizeof(fullName) / sizeof(fullName[0]));
1966             LoadStringW(hInstance, IDS_CRL_DIST_POINT_RDN_NAME, rdnName,
1967              sizeof(rdnName) / sizeof(rdnName[0]));
1968             LoadStringW(hInstance, IDS_CRL_DIST_POINT_REASON, reason,
1969              sizeof(reason) / sizeof(reason[0]));
1970             LoadStringW(hInstance, IDS_CRL_DIST_POINT_ISSUER, issuer,
1971              sizeof(issuer) / sizeof(issuer[0]));
1972             stringsLoaded = TRUE;
1973         }
1974         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
1975         {
1976             headingSep = crlf;
1977             nameSep = colonCrlf;
1978         }
1979         else
1980         {
1981             headingSep = colonSep;
1982             nameSep = colon;
1983         }
1984
1985         for (i = 0; ret && i < info->cDistPoint; i++)
1986         {
1987             CRL_DIST_POINT *distPoint = &info->rgDistPoint[i];
1988
1989             if (distPoint->DistPointName.dwDistPointNameChoice !=
1990              CRL_DIST_POINT_NO_NAME)
1991             {
1992                 bytesNeeded += strlenW(distPointName) * sizeof(WCHAR);
1993                 bytesNeeded += strlenW(nameSep) * sizeof(WCHAR);
1994                 if (distPoint->DistPointName.dwDistPointNameChoice ==
1995                  CRL_DIST_POINT_FULL_NAME)
1996                     bytesNeeded += strlenW(fullName) * sizeof(WCHAR);
1997                 else
1998                     bytesNeeded += strlenW(rdnName) * sizeof(WCHAR);
1999                 bytesNeeded += strlenW(nameSep) * sizeof(WCHAR);
2000                 if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
2001                     bytesNeeded += 2 * strlenW(indent) * sizeof(WCHAR);
2002                 /* The indent level (3) is higher than when used as the issuer,
2003                  * because the name is subordinate to the name type (full vs.
2004                  * RDN.)
2005                  */
2006                 ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 3,
2007                  &distPoint->DistPointName.u.FullName, NULL, &size);
2008                 if (ret)
2009                     bytesNeeded += size - sizeof(WCHAR);
2010                 haveAnEntry = TRUE;
2011             }
2012             else if (distPoint->ReasonFlags.cbData)
2013             {
2014                 bytesNeeded += strlenW(reason) * sizeof(WCHAR);
2015                 ret = CRYPT_FormatReason(dwFormatStrType,
2016                  &distPoint->ReasonFlags, NULL, &size);
2017                 if (ret)
2018                     bytesNeeded += size - sizeof(WCHAR);
2019                 haveAnEntry = TRUE;
2020             }
2021             else if (distPoint->CRLIssuer.cAltEntry)
2022             {
2023                 bytesNeeded += strlenW(issuer) * sizeof(WCHAR);
2024                 bytesNeeded += strlenW(nameSep) * sizeof(WCHAR);
2025                 ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 2,
2026                  &distPoint->CRLIssuer, NULL, &size);
2027                 if (ret)
2028                     bytesNeeded += size - sizeof(WCHAR);
2029                 haveAnEntry = TRUE;
2030             }
2031             if (haveAnEntry)
2032             {
2033                 bytesNeeded += sizeof(WCHAR); /* left bracket */
2034                 sprintfW(distPointNum, numFmt, i + 1);
2035                 bytesNeeded += strlenW(distPointNum) * sizeof(WCHAR);
2036                 bytesNeeded += sizeof(WCHAR); /* right bracket */
2037                 bytesNeeded += strlenW(crlDistPoint) * sizeof(WCHAR);
2038                 bytesNeeded += strlenW(headingSep) * sizeof(WCHAR);
2039                 if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
2040                     bytesNeeded += strlenW(indent) * sizeof(WCHAR);
2041             }
2042         }
2043         if (!haveAnEntry)
2044         {
2045             WCHAR infoNotAvailable[MAX_STRING_RESOURCE_LEN];
2046
2047             LoadStringW(hInstance, IDS_INFO_NOT_AVAILABLE, infoNotAvailable,
2048              sizeof(infoNotAvailable) / sizeof(infoNotAvailable[0]));
2049             bytesNeeded += strlenW(infoNotAvailable) * sizeof(WCHAR);
2050             if (!pbFormat)
2051                 *pcbFormat = bytesNeeded;
2052             else if (*pcbFormat < bytesNeeded)
2053             {
2054                 *pcbFormat = bytesNeeded;
2055                 SetLastError(ERROR_MORE_DATA);
2056                 ret = FALSE;
2057             }
2058             else
2059             {
2060                 *pcbFormat = bytesNeeded;
2061                 strcpyW(pbFormat, infoNotAvailable);
2062             }
2063         }
2064         else
2065         {
2066             if (!pbFormat)
2067                 *pcbFormat = bytesNeeded;
2068             else if (*pcbFormat < bytesNeeded)
2069             {
2070                 *pcbFormat = bytesNeeded;
2071                 SetLastError(ERROR_MORE_DATA);
2072                 ret = FALSE;
2073             }
2074             else
2075             {
2076                 LPWSTR str = pbFormat;
2077
2078                 *pcbFormat = bytesNeeded;
2079                 for (i = 0; ret && i < info->cDistPoint; i++)
2080                 {
2081                     CRL_DIST_POINT *distPoint = &info->rgDistPoint[i];
2082
2083                     *str++ = '[';
2084                     sprintfW(distPointNum, numFmt, i + 1);
2085                     strcpyW(str, distPointNum);
2086                     str += strlenW(distPointNum);
2087                     *str++ = ']';
2088                     strcpyW(str, crlDistPoint);
2089                     str += strlenW(crlDistPoint);
2090                     strcpyW(str, headingSep);
2091                     str += strlenW(headingSep);
2092                     if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
2093                     {
2094                         strcpyW(str, indent);
2095                         str += strlenW(indent);
2096                     }
2097                     if (distPoint->DistPointName.dwDistPointNameChoice !=
2098                      CRL_DIST_POINT_NO_NAME)
2099                     {
2100                         DWORD altNameSize = bytesNeeded;
2101
2102                         strcpyW(str, distPointName);
2103                         str += strlenW(distPointName);
2104                         strcpyW(str, nameSep);
2105                         str += strlenW(nameSep);
2106                         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
2107                         {
2108                             strcpyW(str, indent);
2109                             str += strlenW(indent);
2110                             strcpyW(str, indent);
2111                             str += strlenW(indent);
2112                         }
2113                         if (distPoint->DistPointName.dwDistPointNameChoice ==
2114                          CRL_DIST_POINT_FULL_NAME)
2115                         {
2116                             strcpyW(str, fullName);
2117                             str += strlenW(fullName);
2118                         }
2119                         else
2120                         {
2121                             strcpyW(str, rdnName);
2122                             str += strlenW(rdnName);
2123                         }
2124                         strcpyW(str, nameSep);
2125                         str += strlenW(nameSep);
2126                         ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 3,
2127                          &distPoint->DistPointName.u.FullName, str,
2128                          &altNameSize);
2129                         if (ret)
2130                             str += altNameSize / sizeof(WCHAR) - 1;
2131                     }
2132                     else if (distPoint->ReasonFlags.cbData)
2133                     {
2134                         DWORD reasonSize = bytesNeeded;
2135
2136                         strcpyW(str, reason);
2137                         str += strlenW(reason);
2138                         ret = CRYPT_FormatReason(dwFormatStrType,
2139                          &distPoint->ReasonFlags, str, &reasonSize);
2140                         if (ret)
2141                             str += reasonSize / sizeof(WCHAR) - 1;
2142                     }
2143                     else if (distPoint->CRLIssuer.cAltEntry)
2144                     {
2145                         DWORD crlIssuerSize = bytesNeeded;
2146
2147                         strcpyW(str, issuer);
2148                         str += strlenW(issuer);
2149                         strcpyW(str, nameSep);
2150                         str += strlenW(nameSep);
2151                         ret = CRYPT_FormatAltNameInfo(dwFormatStrType, 2,
2152                          &distPoint->CRLIssuer, str,
2153                          &crlIssuerSize);
2154                         if (ret)
2155                             str += crlIssuerSize / sizeof(WCHAR) - 1;
2156                     }
2157                 }
2158             }
2159         }
2160         LocalFree(info);
2161     }
2162     return ret;
2163 }
2164
2165 static BOOL WINAPI CRYPT_FormatEnhancedKeyUsage(DWORD dwCertEncodingType,
2166  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
2167  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
2168  DWORD *pcbFormat)
2169 {
2170     CERT_ENHKEY_USAGE *usage;
2171     DWORD size;
2172     BOOL ret = FALSE;
2173
2174     if (!cbEncoded)
2175     {
2176         SetLastError(E_INVALIDARG);
2177         return FALSE;
2178     }
2179     if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_ENHANCED_KEY_USAGE,
2180      pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &usage, &size)))
2181     {
2182         WCHAR unknown[MAX_STRING_RESOURCE_LEN];
2183         DWORD i;
2184         DWORD bytesNeeded = sizeof(WCHAR); /* space for the NULL terminator */
2185         LPCWSTR sep;
2186         DWORD sepLen;
2187
2188         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
2189         {
2190             sep = crlf;
2191             sepLen = strlenW(crlf) * sizeof(WCHAR);
2192         }
2193         else
2194         {
2195             sep = commaSpace;
2196             sepLen = strlenW(commaSpace) * sizeof(WCHAR);
2197         }
2198
2199         LoadStringW(hInstance, IDS_USAGE_UNKNOWN, unknown,
2200          sizeof(unknown) / sizeof(unknown[0]));
2201         for (i = 0; i < usage->cUsageIdentifier; i++)
2202         {
2203             PCCRYPT_OID_INFO info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
2204              usage->rgpszUsageIdentifier[i], CRYPT_ENHKEY_USAGE_OID_GROUP_ID);
2205
2206             if (info)
2207                 bytesNeeded += strlenW(info->pwszName) * sizeof(WCHAR);
2208             else
2209                 bytesNeeded += strlenW(unknown) * sizeof(WCHAR);
2210             bytesNeeded += sizeof(WCHAR); /* space */
2211             bytesNeeded += sizeof(WCHAR); /* left paren */
2212             bytesNeeded += strlen(usage->rgpszUsageIdentifier[i]) *
2213              sizeof(WCHAR);
2214             bytesNeeded += sizeof(WCHAR); /* right paren */
2215             if (i < usage->cUsageIdentifier - 1)
2216                 bytesNeeded += sepLen;
2217         }
2218         if (!pbFormat)
2219             *pcbFormat = bytesNeeded;
2220         else if (*pcbFormat < bytesNeeded)
2221         {
2222             *pcbFormat = bytesNeeded;
2223             SetLastError(ERROR_MORE_DATA);
2224             ret = FALSE;
2225         }
2226         else
2227         {
2228             LPWSTR str = pbFormat;
2229
2230             *pcbFormat = bytesNeeded;
2231             for (i = 0; i < usage->cUsageIdentifier; i++)
2232             {
2233                 PCCRYPT_OID_INFO info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
2234                  usage->rgpszUsageIdentifier[i],
2235                  CRYPT_ENHKEY_USAGE_OID_GROUP_ID);
2236                 LPCSTR oidPtr;
2237
2238                 if (info)
2239                 {
2240                     strcpyW(str, info->pwszName);
2241                     str += strlenW(info->pwszName);
2242                 }
2243                 else
2244                 {
2245                     strcpyW(str, unknown);
2246                     str += strlenW(unknown);
2247                 }
2248                 *str++ = ' ';
2249                 *str++ = '(';
2250                 for (oidPtr = usage->rgpszUsageIdentifier[i]; *oidPtr; oidPtr++)
2251                     *str++ = *oidPtr;
2252                 *str++ = ')';
2253                 *str = 0;
2254                 if (i < usage->cUsageIdentifier - 1)
2255                 {
2256                     strcpyW(str, sep);
2257                     str += sepLen / sizeof(WCHAR);
2258                 }
2259             }
2260         }
2261         LocalFree(usage);
2262     }
2263     return ret;
2264 }
2265
2266 static struct BitToString netscapeCertTypeMap[] = {
2267  { NETSCAPE_SSL_CLIENT_AUTH_CERT_TYPE, IDS_NETSCAPE_SSL_CLIENT, { 0 } },
2268  { NETSCAPE_SSL_SERVER_AUTH_CERT_TYPE, IDS_NETSCAPE_SSL_SERVER, { 0 } },
2269  { NETSCAPE_SMIME_CERT_TYPE, IDS_NETSCAPE_SMIME, { 0 } },
2270  { NETSCAPE_SIGN_CERT_TYPE, IDS_NETSCAPE_SIGN, { 0 } },
2271  { NETSCAPE_SSL_CA_CERT_TYPE, IDS_NETSCAPE_SSL_CA, { 0 } },
2272  { NETSCAPE_SMIME_CA_CERT_TYPE, IDS_NETSCAPE_SMIME_CA, { 0 } },
2273  { NETSCAPE_SIGN_CA_CERT_TYPE, IDS_NETSCAPE_SIGN_CA, { 0 } },
2274 };
2275
2276 static BOOL WINAPI CRYPT_FormatNetscapeCertType(DWORD dwCertEncodingType,
2277  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
2278  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
2279  DWORD *pcbFormat)
2280 {
2281     DWORD size;
2282     CRYPT_BIT_BLOB *bits;
2283     BOOL ret;
2284
2285     if (!cbEncoded)
2286     {
2287         SetLastError(E_INVALIDARG);
2288         return FALSE;
2289     }
2290     if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_BITS,
2291      pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &bits, &size)))
2292     {
2293         WCHAR infoNotAvailable[MAX_STRING_RESOURCE_LEN];
2294         DWORD bytesNeeded = sizeof(WCHAR);
2295
2296         LoadStringW(hInstance, IDS_INFO_NOT_AVAILABLE, infoNotAvailable,
2297          sizeof(infoNotAvailable) / sizeof(infoNotAvailable[0]));
2298         if (!bits->cbData || bits->cbData > 1)
2299         {
2300             bytesNeeded += strlenW(infoNotAvailable) * sizeof(WCHAR);
2301             if (!pbFormat)
2302                 *pcbFormat = bytesNeeded;
2303             else if (*pcbFormat < bytesNeeded)
2304             {
2305                 *pcbFormat = bytesNeeded;
2306                 SetLastError(ERROR_MORE_DATA);
2307                 ret = FALSE;
2308             }
2309             else
2310             {
2311                 LPWSTR str = pbFormat;
2312
2313                 *pcbFormat = bytesNeeded;
2314                 strcpyW(str, infoNotAvailable);
2315             }
2316         }
2317         else
2318         {
2319             static BOOL stringsLoaded = FALSE;
2320             int i;
2321             DWORD bitStringLen;
2322             BOOL first = TRUE;
2323
2324             if (!stringsLoaded)
2325             {
2326                 for (i = 0; i < sizeof(netscapeCertTypeMap) /
2327                  sizeof(netscapeCertTypeMap[0]); i++)
2328                     LoadStringW(hInstance, netscapeCertTypeMap[i].id,
2329                      netscapeCertTypeMap[i].str, MAX_STRING_RESOURCE_LEN);
2330                 stringsLoaded = TRUE;
2331             }
2332             CRYPT_FormatBits(bits->pbData[0], netscapeCertTypeMap,
2333              sizeof(netscapeCertTypeMap) / sizeof(netscapeCertTypeMap[0]),
2334              NULL, &bitStringLen, &first);
2335             bytesNeeded += bitStringLen;
2336             bytesNeeded += 3 * sizeof(WCHAR); /* " (" + ")" */
2337             CRYPT_FormatHexString(0, 0, 0, NULL, NULL, bits->pbData,
2338              bits->cbData, NULL, &size);
2339             bytesNeeded += size;
2340             if (!pbFormat)
2341                 *pcbFormat = bytesNeeded;
2342             else if (*pcbFormat < bytesNeeded)
2343             {
2344                 *pcbFormat = bytesNeeded;
2345                 SetLastError(ERROR_MORE_DATA);
2346                 ret = FALSE;
2347             }
2348             else
2349             {
2350                 LPWSTR str = pbFormat;
2351
2352                 bitStringLen = bytesNeeded;
2353                 first = TRUE;
2354                 CRYPT_FormatBits(bits->pbData[0], netscapeCertTypeMap,
2355                  sizeof(netscapeCertTypeMap) / sizeof(netscapeCertTypeMap[0]),
2356                  str, &bitStringLen, &first);
2357                 str += bitStringLen / sizeof(WCHAR) - 1;
2358                 *str++ = ' ';
2359                 *str++ = '(';
2360                 CRYPT_FormatHexString(0, 0, 0, NULL, NULL, bits->pbData,
2361                  bits->cbData, str, &size);
2362                 str += size / sizeof(WCHAR) - 1;
2363                 *str++ = ')';
2364                 *str = 0;
2365             }
2366         }
2367         LocalFree(bits);
2368     }
2369     return ret;
2370 }
2371
2372 static WCHAR financialCriteria[MAX_STRING_RESOURCE_LEN];
2373 static WCHAR available[MAX_STRING_RESOURCE_LEN];
2374 static WCHAR notAvailable[MAX_STRING_RESOURCE_LEN];
2375 static WCHAR meetsCriteria[MAX_STRING_RESOURCE_LEN];
2376 static WCHAR yes[MAX_STRING_RESOURCE_LEN];
2377 static WCHAR no[MAX_STRING_RESOURCE_LEN];
2378
2379 static BOOL WINAPI CRYPT_FormatSpcFinancialCriteria(DWORD dwCertEncodingType,
2380  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
2381  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
2382  DWORD *pcbFormat)
2383 {
2384     SPC_FINANCIAL_CRITERIA criteria;
2385     DWORD size = sizeof(criteria);
2386     BOOL ret = FALSE;
2387
2388     if (!cbEncoded)
2389     {
2390         SetLastError(E_INVALIDARG);
2391         return FALSE;
2392     }
2393     if ((ret = CryptDecodeObjectEx(dwCertEncodingType,
2394      SPC_FINANCIAL_CRITERIA_STRUCT, pbEncoded, cbEncoded, 0, NULL, &criteria,
2395      &size)))
2396     {
2397         static BOOL stringsLoaded = FALSE;
2398         DWORD bytesNeeded = sizeof(WCHAR);
2399         LPCWSTR sep;
2400         DWORD sepLen;
2401
2402         if (!stringsLoaded)
2403         {
2404             LoadStringW(hInstance, IDS_FINANCIAL_CRITERIA, financialCriteria,
2405              sizeof(financialCriteria) / sizeof(financialCriteria[0]));
2406             LoadStringW(hInstance, IDS_FINANCIAL_CRITERIA_AVAILABLE, available,
2407              sizeof(available) / sizeof(available[0]));
2408             LoadStringW(hInstance, IDS_FINANCIAL_CRITERIA_NOT_AVAILABLE,
2409              notAvailable, sizeof(notAvailable) / sizeof(notAvailable[0]));
2410             LoadStringW(hInstance, IDS_FINANCIAL_CRITERIA_MEETS_CRITERIA,
2411              meetsCriteria, sizeof(meetsCriteria) / sizeof(meetsCriteria[0]));
2412             LoadStringW(hInstance, IDS_YES, yes, sizeof(yes) / sizeof(yes[0]));
2413             LoadStringW(hInstance, IDS_NO, no, sizeof(no) / sizeof(no[0]));
2414             stringsLoaded = TRUE;
2415         }
2416         if (dwFormatStrType & CRYPT_FORMAT_STR_MULTI_LINE)
2417         {
2418             sep = crlf;
2419             sepLen = strlenW(crlf) * sizeof(WCHAR);
2420         }
2421         else
2422         {
2423             sep = commaSpace;
2424             sepLen = strlenW(commaSpace) * sizeof(WCHAR);
2425         }
2426         bytesNeeded += strlenW(financialCriteria) * sizeof(WCHAR);
2427         if (criteria.fFinancialInfoAvailable)
2428         {
2429             bytesNeeded += strlenW(available) * sizeof(WCHAR);
2430             bytesNeeded += sepLen;
2431             bytesNeeded += strlenW(meetsCriteria) * sizeof(WCHAR);
2432             if (criteria.fMeetsCriteria)
2433                 bytesNeeded += strlenW(yes) * sizeof(WCHAR);
2434             else
2435                 bytesNeeded += strlenW(no) * sizeof(WCHAR);
2436         }
2437         else
2438             bytesNeeded += strlenW(notAvailable) * sizeof(WCHAR);
2439         if (!pbFormat)
2440             *pcbFormat = bytesNeeded;
2441         else if (*pcbFormat < bytesNeeded)
2442         {
2443             *pcbFormat = bytesNeeded;
2444             SetLastError(ERROR_MORE_DATA);
2445             ret = FALSE;
2446         }
2447         else
2448         {
2449             LPWSTR str = pbFormat;
2450
2451             *pcbFormat = bytesNeeded;
2452             strcpyW(str, financialCriteria);
2453             str += strlenW(financialCriteria);
2454             if (criteria.fFinancialInfoAvailable)
2455             {
2456                 strcpyW(str, available);
2457                 str += strlenW(available);
2458                 strcpyW(str, sep);
2459                 str += sepLen / sizeof(WCHAR);
2460                 strcpyW(str, meetsCriteria);
2461                 str += strlenW(meetsCriteria);
2462                 if (criteria.fMeetsCriteria)
2463                     strcpyW(str, yes);
2464                 else
2465                     strcpyW(str, no);
2466             }
2467             else
2468             {
2469                 strcpyW(str, notAvailable);
2470                 str += strlenW(notAvailable);
2471             }
2472         }
2473     }
2474     return ret;
2475 }
2476
2477 static BOOL WINAPI CRYPT_FormatUnicodeString(DWORD dwCertEncodingType,
2478  DWORD dwFormatType, DWORD dwFormatStrType, void *pFormatStruct,
2479  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat,
2480  DWORD *pcbFormat)
2481 {
2482     CERT_NAME_VALUE *value;
2483     DWORD size;
2484     BOOL ret;
2485
2486     if (!cbEncoded)
2487     {
2488         SetLastError(E_INVALIDARG);
2489         return FALSE;
2490     }
2491     if ((ret = CryptDecodeObjectEx(dwCertEncodingType, X509_UNICODE_ANY_STRING,
2492      pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &value, &size)))
2493     {
2494         if (!pbFormat)
2495             *pcbFormat = value->Value.cbData;
2496         else if (*pcbFormat < value->Value.cbData)
2497         {
2498             *pcbFormat = value->Value.cbData;
2499             SetLastError(ERROR_MORE_DATA);
2500             ret = FALSE;
2501         }
2502         else
2503         {
2504             LPWSTR str = pbFormat;
2505
2506             *pcbFormat = value->Value.cbData;
2507             strcpyW(str, (LPWSTR)value->Value.pbData);
2508         }
2509     }
2510     return ret;
2511 }
2512
2513 typedef BOOL (WINAPI *CryptFormatObjectFunc)(DWORD, DWORD, DWORD, void *,
2514  LPCSTR, const BYTE *, DWORD, void *, DWORD *);
2515
2516 static CryptFormatObjectFunc CRYPT_GetBuiltinFormatFunction(DWORD encodingType,
2517  DWORD formatStrType, LPCSTR lpszStructType)
2518 {
2519     CryptFormatObjectFunc format = NULL;
2520
2521     if ((encodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING)
2522     {
2523         SetLastError(ERROR_FILE_NOT_FOUND);
2524         return NULL;
2525     }
2526     if (!HIWORD(lpszStructType))
2527     {
2528         switch (LOWORD(lpszStructType))
2529         {
2530         case LOWORD(X509_KEY_USAGE):
2531             format = CRYPT_FormatKeyUsage;
2532             break;
2533         case LOWORD(X509_ALTERNATE_NAME):
2534             format = CRYPT_FormatAltName;
2535             break;
2536         case LOWORD(X509_BASIC_CONSTRAINTS2):
2537             format = CRYPT_FormatBasicConstraints2;
2538             break;
2539         case LOWORD(X509_AUTHORITY_KEY_ID2):
2540             format = CRYPT_FormatAuthorityKeyId2;
2541             break;
2542         case LOWORD(X509_AUTHORITY_INFO_ACCESS):
2543             format = CRYPT_FormatAuthorityInfoAccess;
2544             break;
2545         case LOWORD(X509_CRL_DIST_POINTS):
2546             format = CRYPT_FormatCRLDistPoints;
2547             break;
2548         case LOWORD(X509_ENHANCED_KEY_USAGE):
2549             format = CRYPT_FormatEnhancedKeyUsage;
2550             break;
2551         case LOWORD(SPC_FINANCIAL_CRITERIA_STRUCT):
2552             format = CRYPT_FormatSpcFinancialCriteria;
2553             break;
2554         }
2555     }
2556     else if (!strcmp(lpszStructType, szOID_SUBJECT_ALT_NAME))
2557         format = CRYPT_FormatAltName;
2558     else if (!strcmp(lpszStructType, szOID_ISSUER_ALT_NAME))
2559         format = CRYPT_FormatAltName;
2560     else if (!strcmp(lpszStructType, szOID_KEY_USAGE))
2561         format = CRYPT_FormatKeyUsage;
2562     else if (!strcmp(lpszStructType, szOID_SUBJECT_ALT_NAME2))
2563         format = CRYPT_FormatAltName;
2564     else if (!strcmp(lpszStructType, szOID_ISSUER_ALT_NAME2))
2565         format = CRYPT_FormatAltName;
2566     else if (!strcmp(lpszStructType, szOID_BASIC_CONSTRAINTS2))
2567         format = CRYPT_FormatBasicConstraints2;
2568     else if (!strcmp(lpszStructType, szOID_AUTHORITY_INFO_ACCESS))
2569         format = CRYPT_FormatAuthorityInfoAccess;
2570     else if (!strcmp(lpszStructType, szOID_AUTHORITY_KEY_IDENTIFIER2))
2571         format = CRYPT_FormatAuthorityKeyId2;
2572     else if (!strcmp(lpszStructType, szOID_CRL_DIST_POINTS))
2573         format = CRYPT_FormatCRLDistPoints;
2574     else if (!strcmp(lpszStructType, szOID_ENHANCED_KEY_USAGE))
2575         format = CRYPT_FormatEnhancedKeyUsage;
2576     else if (!strcmp(lpszStructType, szOID_NETSCAPE_CERT_TYPE))
2577         format = CRYPT_FormatNetscapeCertType;
2578     else if (!strcmp(lpszStructType, szOID_NETSCAPE_BASE_URL) ||
2579      !strcmp(lpszStructType, szOID_NETSCAPE_REVOCATION_URL) ||
2580      !strcmp(lpszStructType, szOID_NETSCAPE_CA_REVOCATION_URL) ||
2581      !strcmp(lpszStructType, szOID_NETSCAPE_CERT_RENEWAL_URL) ||
2582      !strcmp(lpszStructType, szOID_NETSCAPE_CA_POLICY_URL) ||
2583      !strcmp(lpszStructType, szOID_NETSCAPE_SSL_SERVER_NAME) ||
2584      !strcmp(lpszStructType, szOID_NETSCAPE_COMMENT))
2585         format = CRYPT_FormatUnicodeString;
2586     else if (!strcmp(lpszStructType, SPC_FINANCIAL_CRITERIA_OBJID))
2587         format = CRYPT_FormatSpcFinancialCriteria;
2588     return format;
2589 }
2590
2591 BOOL WINAPI CryptFormatObject(DWORD dwCertEncodingType, DWORD dwFormatType,
2592  DWORD dwFormatStrType, void *pFormatStruct, LPCSTR lpszStructType,
2593  const BYTE *pbEncoded, DWORD cbEncoded, void *pbFormat, DWORD *pcbFormat)
2594 {
2595     CryptFormatObjectFunc format = NULL;
2596     HCRYPTOIDFUNCADDR hFunc = NULL;
2597     BOOL ret = FALSE;
2598
2599     TRACE("(%08x, %d, %08x, %p, %s, %p, %d, %p, %p)\n", dwCertEncodingType,
2600      dwFormatType, dwFormatStrType, pFormatStruct, debugstr_a(lpszStructType),
2601      pbEncoded, cbEncoded, pbFormat, pcbFormat);
2602
2603     if (!(format = CRYPT_GetBuiltinFormatFunction(dwCertEncodingType,
2604      dwFormatStrType, lpszStructType)))
2605     {
2606         static HCRYPTOIDFUNCSET set = NULL;
2607
2608         if (!set)
2609             set = CryptInitOIDFunctionSet(CRYPT_OID_FORMAT_OBJECT_FUNC, 0);
2610         CryptGetOIDFunctionAddress(set, dwCertEncodingType, lpszStructType, 0,
2611          (void **)&format, &hFunc);
2612     }
2613     if (!format && (dwCertEncodingType & CERT_ENCODING_TYPE_MASK) ==
2614      X509_ASN_ENCODING && !(dwFormatStrType & CRYPT_FORMAT_STR_NO_HEX))
2615         format = CRYPT_FormatHexString;
2616     if (format)
2617         ret = format(dwCertEncodingType, dwFormatType, dwFormatStrType,
2618          pFormatStruct, lpszStructType, pbEncoded, cbEncoded, pbFormat,
2619          pcbFormat);
2620     if (hFunc)
2621         CryptFreeOIDFunctionAddress(hFunc, 0);
2622     return ret;
2623 }