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