xref: /reactos/dll/win32/crypt32/cert.c (revision 1734f297)
1 /*
2  * Copyright 2004-2006 Juan Lang
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  *
18  */
19 
20 #include <assert.h>
21 #include <stdarg.h>
22 
23 #define NONAMELESSUNION
24 #include "ntstatus.h"
25 #define WIN32_NO_STATUS
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wine/winternl.h"
29 #define CRYPT_OID_INFO_HAS_EXTRA_FIELDS
30 #include "wincrypt.h"
31 #include "snmp.h"
32 #include "bcrypt.h"
33 #include "winnls.h"
34 #include "rpc.h"
35 #include "wine/debug.h"
36 #include "wine/unicode.h"
37 #include "crypt32_private.h"
38 
39 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
40 
41 /* Internal version of CertGetCertificateContextProperty that gets properties
42  * directly from the context (or the context it's linked to, depending on its
43  * type.) Doesn't handle special-case properties, since they are handled by
44  * CertGetCertificateContextProperty, and are particular to the store in which
45  * the property exists (which is separate from the context.)
46  */
47 static BOOL CertContext_GetProperty(cert_t *cert, DWORD dwPropId,
48  void *pvData, DWORD *pcbData);
49 
50 /* Internal version of CertSetCertificateContextProperty that sets properties
51  * directly on the context (or the context it's linked to, depending on its
52  * type.) Doesn't handle special cases, since they're handled by
53  * CertSetCertificateContextProperty anyway.
54  */
55 static BOOL CertContext_SetProperty(cert_t *cert, DWORD dwPropId,
56  DWORD dwFlags, const void *pvData);
57 
58 BOOL WINAPI CertAddEncodedCertificateToStore(HCERTSTORE hCertStore,
59  DWORD dwCertEncodingType, const BYTE *pbCertEncoded, DWORD cbCertEncoded,
60  DWORD dwAddDisposition, PCCERT_CONTEXT *ppCertContext)
61 {
62     PCCERT_CONTEXT cert = CertCreateCertificateContext(dwCertEncodingType,
63      pbCertEncoded, cbCertEncoded);
64     BOOL ret;
65 
66     TRACE("(%p, %08x, %p, %d, %08x, %p)\n", hCertStore, dwCertEncodingType,
67      pbCertEncoded, cbCertEncoded, dwAddDisposition, ppCertContext);
68 
69     if (cert)
70     {
71         ret = CertAddCertificateContextToStore(hCertStore, cert,
72          dwAddDisposition, ppCertContext);
73         CertFreeCertificateContext(cert);
74     }
75     else
76         ret = FALSE;
77     return ret;
78 }
79 
80 BOOL WINAPI CertAddEncodedCertificateToSystemStoreA(LPCSTR pszCertStoreName,
81  const BYTE *pbCertEncoded, DWORD cbCertEncoded)
82 {
83     HCERTSTORE store;
84     BOOL ret = FALSE;
85 
86     TRACE("(%s, %p, %d)\n", debugstr_a(pszCertStoreName), pbCertEncoded,
87      cbCertEncoded);
88 
89     store = CertOpenSystemStoreA(0, pszCertStoreName);
90     if (store)
91     {
92         ret = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING,
93          pbCertEncoded, cbCertEncoded, CERT_STORE_ADD_USE_EXISTING, NULL);
94         CertCloseStore(store, 0);
95     }
96     return ret;
97 }
98 
99 BOOL WINAPI CertAddEncodedCertificateToSystemStoreW(LPCWSTR pszCertStoreName,
100  const BYTE *pbCertEncoded, DWORD cbCertEncoded)
101 {
102     HCERTSTORE store;
103     BOOL ret = FALSE;
104 
105     TRACE("(%s, %p, %d)\n", debugstr_w(pszCertStoreName), pbCertEncoded,
106      cbCertEncoded);
107 
108     store = CertOpenSystemStoreW(0, pszCertStoreName);
109     if (store)
110     {
111         ret = CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING,
112          pbCertEncoded, cbCertEncoded, CERT_STORE_ADD_USE_EXISTING, NULL);
113         CertCloseStore(store, 0);
114     }
115     return ret;
116 }
117 
118 static const context_vtbl_t cert_vtbl;
119 
120 static void Cert_free(context_t *context)
121 {
122     cert_t *cert = (cert_t*)context;
123 
124     CryptMemFree(cert->ctx.pbCertEncoded);
125     LocalFree(cert->ctx.pCertInfo);
126 }
127 
128 static context_t *Cert_clone(context_t *context, WINECRYPT_CERTSTORE *store, BOOL use_link)
129 {
130     cert_t *cert;
131 
132     if(use_link) {
133         cert = (cert_t*)Context_CreateLinkContext(sizeof(CERT_CONTEXT), context, store);
134         if(!cert)
135             return NULL;
136     }else {
137         const cert_t *cloned = (const cert_t*)context;
138         DWORD size = 0;
139         BOOL res;
140 
141         cert = (cert_t*)Context_CreateDataContext(sizeof(CERT_CONTEXT), &cert_vtbl, store);
142         if(!cert)
143             return NULL;
144 
145         Context_CopyProperties(&cert->ctx, &cloned->ctx);
146 
147         cert->ctx.dwCertEncodingType = cloned->ctx.dwCertEncodingType;
148         cert->ctx.pbCertEncoded = CryptMemAlloc(cloned->ctx.cbCertEncoded);
149         memcpy(cert->ctx.pbCertEncoded, cloned->ctx.pbCertEncoded, cloned->ctx.cbCertEncoded);
150         cert->ctx.cbCertEncoded = cloned->ctx.cbCertEncoded;
151 
152         /* FIXME: We don't need to decode the object here, we could just clone cert info. */
153         res = CryptDecodeObjectEx(cert->ctx.dwCertEncodingType, X509_CERT_TO_BE_SIGNED,
154          cert->ctx.pbCertEncoded, cert->ctx.cbCertEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL,
155          &cert->ctx.pCertInfo, &size);
156         if(!res) {
157             CertFreeCertificateContext(&cert->ctx);
158             return NULL;
159         }
160     }
161 
162     cert->ctx.hCertStore = store;
163     return &cert->base;
164 }
165 
166 static const context_vtbl_t cert_vtbl = {
167     Cert_free,
168     Cert_clone
169 };
170 
171 static BOOL add_cert_to_store(WINECRYPT_CERTSTORE *store, const CERT_CONTEXT *cert,
172  DWORD add_disposition, BOOL use_link, PCCERT_CONTEXT *ret_context)
173 {
174     const CERT_CONTEXT *existing = NULL;
175     BOOL ret = TRUE, inherit_props = FALSE;
176     context_t *new_context = NULL;
177 
178     switch (add_disposition)
179     {
180     case CERT_STORE_ADD_ALWAYS:
181         break;
182     case CERT_STORE_ADD_NEW:
183     case CERT_STORE_ADD_REPLACE_EXISTING:
184     case CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES:
185     case CERT_STORE_ADD_USE_EXISTING:
186     case CERT_STORE_ADD_NEWER:
187     case CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES:
188     {
189         BYTE hashToAdd[20];
190         DWORD size = sizeof(hashToAdd);
191 
192         ret = CertGetCertificateContextProperty(cert, CERT_HASH_PROP_ID,
193          hashToAdd, &size);
194         if (ret)
195         {
196             CRYPT_HASH_BLOB blob = { sizeof(hashToAdd), hashToAdd };
197 
198             existing = CertFindCertificateInStore(store, cert->dwCertEncodingType, 0,
199              CERT_FIND_SHA1_HASH, &blob, NULL);
200         }
201         break;
202     }
203     default:
204         FIXME("Unimplemented add disposition %d\n", add_disposition);
205         SetLastError(E_INVALIDARG);
206         return FALSE;
207     }
208 
209     switch (add_disposition)
210     {
211     case CERT_STORE_ADD_ALWAYS:
212         break;
213     case CERT_STORE_ADD_NEW:
214         if (existing)
215         {
216             TRACE("found matching certificate, not adding\n");
217             SetLastError(CRYPT_E_EXISTS);
218             return FALSE;
219         }
220         break;
221     case CERT_STORE_ADD_REPLACE_EXISTING:
222         break;
223     case CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES:
224         if (use_link)
225             FIXME("CERT_STORE_ADD_REPLACE_EXISTING_INHERIT_PROPERTIES: semi-stub for links\n");
226         if (existing)
227             inherit_props = TRUE;
228         break;
229     case CERT_STORE_ADD_USE_EXISTING:
230         if(use_link)
231             FIXME("CERT_STORE_ADD_USE_EXISTING: semi-stub for links\n");
232         if (existing)
233         {
234             Context_CopyProperties(existing, cert);
235             if (ret_context)
236                 *ret_context = CertDuplicateCertificateContext(existing);
237             return TRUE;
238         }
239         break;
240     case CERT_STORE_ADD_NEWER:
241         if (existing && CompareFileTime(&existing->pCertInfo->NotBefore, &cert->pCertInfo->NotBefore) >= 0)
242         {
243             TRACE("existing certificate is newer, not adding\n");
244             SetLastError(CRYPT_E_EXISTS);
245             return FALSE;
246         }
247         break;
248     case CERT_STORE_ADD_NEWER_INHERIT_PROPERTIES:
249         if (existing)
250         {
251             if (CompareFileTime(&existing->pCertInfo->NotBefore, &cert->pCertInfo->NotBefore) >= 0)
252             {
253                 TRACE("existing certificate is newer, not adding\n");
254                 SetLastError(CRYPT_E_EXISTS);
255                 return FALSE;
256             }
257             inherit_props = TRUE;
258         }
259         break;
260     }
261 
262     /* FIXME: We have tests that this works, but what should we really do in this case? */
263     if(!store) {
264         if(ret_context)
265             *ret_context = CertDuplicateCertificateContext(cert);
266         return TRUE;
267     }
268 
269     ret = store->vtbl->certs.addContext(store, context_from_ptr(cert), existing ? context_from_ptr(existing) : NULL,
270      (ret_context || inherit_props) ? &new_context : NULL, use_link);
271     if(!ret)
272         return FALSE;
273 
274     if(inherit_props)
275         Context_CopyProperties(context_ptr(new_context), existing);
276 
277     if(ret_context)
278         *ret_context = context_ptr(new_context);
279     else if(new_context)
280         Context_Release(new_context);
281 
282     TRACE("returning %d\n", ret);
283     return ret;
284 }
285 
286 BOOL WINAPI CertAddCertificateContextToStore(HCERTSTORE hCertStore, PCCERT_CONTEXT pCertContext,
287  DWORD dwAddDisposition, PCCERT_CONTEXT *ppStoreContext)
288 {
289     WINECRYPT_CERTSTORE *store = hCertStore;
290 
291     TRACE("(%p, %p, %08x, %p)\n", hCertStore, pCertContext, dwAddDisposition, ppStoreContext);
292 
293     return add_cert_to_store(store, pCertContext, dwAddDisposition, FALSE, ppStoreContext);
294 }
295 
296 BOOL WINAPI CertAddCertificateLinkToStore(HCERTSTORE hCertStore,
297  PCCERT_CONTEXT pCertContext, DWORD dwAddDisposition,
298  PCCERT_CONTEXT *ppCertContext)
299 {
300     static int calls;
301     WINECRYPT_CERTSTORE *store = (WINECRYPT_CERTSTORE*)hCertStore;
302 
303     if (!(calls++))
304         FIXME("(%p, %p, %08x, %p): semi-stub\n", hCertStore, pCertContext,
305          dwAddDisposition, ppCertContext);
306     if (store->dwMagic != WINE_CRYPTCERTSTORE_MAGIC)
307         return FALSE;
308     if (store->type == StoreTypeCollection)
309     {
310         SetLastError(E_INVALIDARG);
311         return FALSE;
312     }
313     return add_cert_to_store(hCertStore, pCertContext, dwAddDisposition, TRUE, ppCertContext);
314 }
315 
316 PCCERT_CONTEXT WINAPI CertCreateCertificateContext(DWORD dwCertEncodingType,
317  const BYTE *pbCertEncoded, DWORD cbCertEncoded)
318 {
319     cert_t *cert = NULL;
320     BYTE *data = NULL;
321     BOOL ret;
322     PCERT_INFO certInfo = NULL;
323     DWORD size = 0;
324 
325     TRACE("(%08x, %p, %d)\n", dwCertEncodingType, pbCertEncoded,
326      cbCertEncoded);
327 
328     if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING)
329     {
330         SetLastError(E_INVALIDARG);
331         return NULL;
332     }
333 
334     ret = CryptDecodeObjectEx(dwCertEncodingType, X509_CERT_TO_BE_SIGNED,
335      pbCertEncoded, cbCertEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL,
336      &certInfo, &size);
337     if (!ret)
338         return NULL;
339 
340     cert = (cert_t*)Context_CreateDataContext(sizeof(CERT_CONTEXT), &cert_vtbl, &empty_store);
341     if (!cert)
342         return NULL;
343     data = CryptMemAlloc(cbCertEncoded);
344     if (!data)
345     {
346         Context_Release(&cert->base);
347         return NULL;
348     }
349 
350     memcpy(data, pbCertEncoded, cbCertEncoded);
351     cert->ctx.dwCertEncodingType = dwCertEncodingType;
352     cert->ctx.pbCertEncoded      = data;
353     cert->ctx.cbCertEncoded      = cbCertEncoded;
354     cert->ctx.pCertInfo          = certInfo;
355     cert->ctx.hCertStore         = &empty_store;
356 
357     return &cert->ctx;
358 }
359 
360 PCCERT_CONTEXT WINAPI CertDuplicateCertificateContext(PCCERT_CONTEXT pCertContext)
361 {
362     TRACE("(%p)\n", pCertContext);
363 
364     if (!pCertContext)
365         return NULL;
366 
367     Context_AddRef(&cert_from_ptr(pCertContext)->base);
368     return pCertContext;
369 }
370 
371 BOOL WINAPI CertFreeCertificateContext(PCCERT_CONTEXT pCertContext)
372 {
373     TRACE("(%p)\n", pCertContext);
374 
375     if (pCertContext)
376         Context_Release(&cert_from_ptr(pCertContext)->base);
377     return TRUE;
378 }
379 
380 DWORD WINAPI CertEnumCertificateContextProperties(PCCERT_CONTEXT pCertContext,
381  DWORD dwPropId)
382 {
383     cert_t *cert = cert_from_ptr(pCertContext);
384     DWORD ret;
385 
386     TRACE("(%p, %d)\n", pCertContext, dwPropId);
387 
388     if (cert->base.properties)
389         ret = ContextPropertyList_EnumPropIDs(cert->base.properties, dwPropId);
390     else
391         ret = 0;
392     return ret;
393 }
394 
395 static BOOL CertContext_GetHashProp(cert_t *cert, DWORD dwPropId,
396  ALG_ID algID, const BYTE *toHash, DWORD toHashLen, void *pvData,
397  DWORD *pcbData)
398 {
399     BOOL ret = CryptHashCertificate(0, algID, 0, toHash, toHashLen, pvData,
400      pcbData);
401     if (ret && pvData)
402     {
403         CRYPT_DATA_BLOB blob = { *pcbData, pvData };
404 
405         ret = CertContext_SetProperty(cert, dwPropId, 0, &blob);
406     }
407     return ret;
408 }
409 
410 static BOOL CertContext_CopyParam(void *pvData, DWORD *pcbData, const void *pb,
411  DWORD cb)
412 {
413     BOOL ret = TRUE;
414 
415     if (!pvData)
416         *pcbData = cb;
417     else if (*pcbData < cb)
418     {
419         SetLastError(ERROR_MORE_DATA);
420         *pcbData = cb;
421         ret = FALSE;
422     }
423     else
424     {
425         memcpy(pvData, pb, cb);
426         *pcbData = cb;
427     }
428     return ret;
429 }
430 
431 static BOOL CertContext_GetProperty(cert_t *cert, DWORD dwPropId,
432  void *pvData, DWORD *pcbData)
433 {
434     BOOL ret;
435     CRYPT_DATA_BLOB blob;
436 
437     TRACE("(%p, %d, %p, %p)\n", cert, dwPropId, pvData, pcbData);
438 
439     if (cert->base.properties)
440         ret = ContextPropertyList_FindProperty(cert->base.properties, dwPropId, &blob);
441     else
442         ret = FALSE;
443     if (ret)
444         ret = CertContext_CopyParam(pvData, pcbData, blob.pbData, blob.cbData);
445     else
446     {
447         /* Implicit properties */
448         switch (dwPropId)
449         {
450         case CERT_SHA1_HASH_PROP_ID:
451             ret = CertContext_GetHashProp(cert, dwPropId, CALG_SHA1,
452              cert->ctx.pbCertEncoded, cert->ctx.cbCertEncoded, pvData,
453              pcbData);
454             break;
455         case CERT_MD5_HASH_PROP_ID:
456             ret = CertContext_GetHashProp(cert, dwPropId, CALG_MD5,
457              cert->ctx.pbCertEncoded, cert->ctx.cbCertEncoded, pvData,
458              pcbData);
459             break;
460         case CERT_SUBJECT_NAME_MD5_HASH_PROP_ID:
461             ret = CertContext_GetHashProp(cert, dwPropId, CALG_MD5,
462              cert->ctx.pCertInfo->Subject.pbData,
463              cert->ctx.pCertInfo->Subject.cbData,
464              pvData, pcbData);
465             break;
466         case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID:
467             ret = CertContext_GetHashProp(cert, dwPropId, CALG_MD5,
468              cert->ctx.pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,
469              cert->ctx.pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData,
470              pvData, pcbData);
471             break;
472         case CERT_ISSUER_SERIAL_NUMBER_MD5_HASH_PROP_ID:
473             ret = CertContext_GetHashProp(cert, dwPropId, CALG_MD5,
474              cert->ctx.pCertInfo->SerialNumber.pbData,
475              cert->ctx.pCertInfo->SerialNumber.cbData,
476              pvData, pcbData);
477             break;
478         case CERT_SIGNATURE_HASH_PROP_ID:
479             ret = CryptHashToBeSigned(0, cert->ctx.dwCertEncodingType,
480              cert->ctx.pbCertEncoded, cert->ctx.cbCertEncoded, pvData,
481              pcbData);
482             if (ret && pvData)
483             {
484                 CRYPT_DATA_BLOB blob = { *pcbData, pvData };
485 
486                 ret = CertContext_SetProperty(cert, dwPropId, 0, &blob);
487             }
488             break;
489         case CERT_KEY_IDENTIFIER_PROP_ID:
490         {
491             PCERT_EXTENSION ext = CertFindExtension(
492              szOID_SUBJECT_KEY_IDENTIFIER, cert->ctx.pCertInfo->cExtension,
493              cert->ctx.pCertInfo->rgExtension);
494 
495             if (ext)
496             {
497                 CRYPT_DATA_BLOB value;
498                 DWORD size = sizeof(value);
499 
500                 ret = CryptDecodeObjectEx(X509_ASN_ENCODING,
501                  szOID_SUBJECT_KEY_IDENTIFIER, ext->Value.pbData,
502                  ext->Value.cbData, CRYPT_DECODE_NOCOPY_FLAG, NULL, &value,
503                  &size);
504                 if (ret)
505                 {
506                     ret = CertContext_CopyParam(pvData, pcbData, value.pbData,
507                      value.cbData);
508                     CertContext_SetProperty(cert, dwPropId, 0, &value);
509                 }
510             }
511             else
512                 SetLastError(ERROR_INVALID_DATA);
513             break;
514         }
515         default:
516             SetLastError(CRYPT_E_NOT_FOUND);
517         }
518     }
519     TRACE("returning %d\n", ret);
520     return ret;
521 }
522 
523 void CRYPT_FixKeyProvInfoPointers(PCRYPT_KEY_PROV_INFO info)
524 {
525     DWORD i, containerLen, provNameLen;
526     LPBYTE data = (LPBYTE)info + sizeof(CRYPT_KEY_PROV_INFO);
527 
528     info->pwszContainerName = (LPWSTR)data;
529     containerLen = (lstrlenW(info->pwszContainerName) + 1) * sizeof(WCHAR);
530     data += containerLen;
531 
532     info->pwszProvName = (LPWSTR)data;
533     provNameLen = (lstrlenW(info->pwszProvName) + 1) * sizeof(WCHAR);
534     data += provNameLen;
535 
536     if (info->cProvParam)
537     {
538         info->rgProvParam = (PCRYPT_KEY_PROV_PARAM)data;
539         data += info->cProvParam * sizeof(CRYPT_KEY_PROV_PARAM);
540 
541         for (i = 0; i < info->cProvParam; i++)
542         {
543             info->rgProvParam[i].pbData = data;
544             data += info->rgProvParam[i].cbData;
545         }
546     }
547     else
548         info->rgProvParam = NULL;
549 }
550 
551 BOOL WINAPI CertGetCertificateContextProperty(PCCERT_CONTEXT pCertContext,
552  DWORD dwPropId, void *pvData, DWORD *pcbData)
553 {
554     cert_t *cert = cert_from_ptr(pCertContext);
555     BOOL ret;
556 
557     TRACE("(%p, %d, %p, %p)\n", pCertContext, dwPropId, pvData, pcbData);
558 
559     switch (dwPropId)
560     {
561     case 0:
562     case CERT_CERT_PROP_ID:
563     case CERT_CRL_PROP_ID:
564     case CERT_CTL_PROP_ID:
565         SetLastError(E_INVALIDARG);
566         ret = FALSE;
567         break;
568     case CERT_ACCESS_STATE_PROP_ID:
569         ret = CertGetStoreProperty(cert->ctx.hCertStore, dwPropId, pvData, pcbData);
570         break;
571     case CERT_KEY_PROV_HANDLE_PROP_ID:
572     {
573         CERT_KEY_CONTEXT keyContext;
574         DWORD size = sizeof(keyContext);
575 
576         ret = CertContext_GetProperty(cert,
577          CERT_KEY_CONTEXT_PROP_ID, &keyContext, &size);
578         if (ret)
579             ret = CertContext_CopyParam(pvData, pcbData, &keyContext.hCryptProv,
580              sizeof(keyContext.hCryptProv));
581         break;
582     }
583     case CERT_KEY_PROV_INFO_PROP_ID:
584         ret = CertContext_GetProperty(cert, dwPropId, pvData,
585          pcbData);
586         if (ret && pvData)
587             CRYPT_FixKeyProvInfoPointers(pvData);
588         break;
589     default:
590         ret = CertContext_GetProperty(cert, dwPropId, pvData,
591          pcbData);
592     }
593 
594     TRACE("returning %d\n", ret);
595     return ret;
596 }
597 
598 /* Copies key provider info from from into to, where to is assumed to be a
599  * contiguous buffer of memory large enough for from and all its associated
600  * data, but whose pointers are uninitialized.
601  * Upon return, to contains a contiguous copy of from, packed in the following
602  * order:
603  * - CRYPT_KEY_PROV_INFO
604  * - pwszContainerName
605  * - pwszProvName
606  * - rgProvParam[0]...
607  */
608 static void CRYPT_CopyKeyProvInfo(PCRYPT_KEY_PROV_INFO to,
609  const CRYPT_KEY_PROV_INFO *from)
610 {
611     DWORD i;
612     LPBYTE nextData = (LPBYTE)to + sizeof(CRYPT_KEY_PROV_INFO);
613 
614     if (from->pwszContainerName)
615     {
616         to->pwszContainerName = (LPWSTR)nextData;
617         lstrcpyW(to->pwszContainerName, from->pwszContainerName);
618         nextData += (lstrlenW(from->pwszContainerName) + 1) * sizeof(WCHAR);
619     }
620     else
621         to->pwszContainerName = NULL;
622     if (from->pwszProvName)
623     {
624         to->pwszProvName = (LPWSTR)nextData;
625         lstrcpyW(to->pwszProvName, from->pwszProvName);
626         nextData += (lstrlenW(from->pwszProvName) + 1) * sizeof(WCHAR);
627     }
628     else
629         to->pwszProvName = NULL;
630     to->dwProvType = from->dwProvType;
631     to->dwFlags = from->dwFlags;
632     to->cProvParam = from->cProvParam;
633     to->rgProvParam = (PCRYPT_KEY_PROV_PARAM)nextData;
634     nextData += to->cProvParam * sizeof(CRYPT_KEY_PROV_PARAM);
635     to->dwKeySpec = from->dwKeySpec;
636     for (i = 0; i < to->cProvParam; i++)
637     {
638         memcpy(&to->rgProvParam[i], &from->rgProvParam[i],
639          sizeof(CRYPT_KEY_PROV_PARAM));
640         to->rgProvParam[i].pbData = nextData;
641         memcpy(to->rgProvParam[i].pbData, from->rgProvParam[i].pbData,
642          from->rgProvParam[i].cbData);
643         nextData += from->rgProvParam[i].cbData;
644     }
645 }
646 
647 static BOOL CertContext_SetKeyProvInfoProperty(CONTEXT_PROPERTY_LIST *properties,
648  const CRYPT_KEY_PROV_INFO *info)
649 {
650     BOOL ret;
651     LPBYTE buf = NULL;
652     DWORD size = sizeof(CRYPT_KEY_PROV_INFO), i, containerSize, provNameSize;
653 
654     if (info->pwszContainerName)
655         containerSize = (lstrlenW(info->pwszContainerName) + 1) * sizeof(WCHAR);
656     else
657         containerSize = 0;
658     if (info->pwszProvName)
659         provNameSize = (lstrlenW(info->pwszProvName) + 1) * sizeof(WCHAR);
660     else
661         provNameSize = 0;
662     size += containerSize + provNameSize;
663     for (i = 0; i < info->cProvParam; i++)
664         size += sizeof(CRYPT_KEY_PROV_PARAM) + info->rgProvParam[i].cbData;
665     buf = CryptMemAlloc(size);
666     if (buf)
667     {
668         CRYPT_CopyKeyProvInfo((PCRYPT_KEY_PROV_INFO)buf, info);
669         ret = ContextPropertyList_SetProperty(properties,
670          CERT_KEY_PROV_INFO_PROP_ID, buf, size);
671         CryptMemFree(buf);
672     }
673     else
674         ret = FALSE;
675     return ret;
676 }
677 
678 static BOOL CertContext_SetProperty(cert_t *cert, DWORD dwPropId,
679  DWORD dwFlags, const void *pvData)
680 {
681     BOOL ret;
682 
683     TRACE("(%p, %d, %08x, %p)\n", cert, dwPropId, dwFlags, pvData);
684 
685     if (!cert->base.properties)
686         ret = FALSE;
687     else
688     {
689         switch (dwPropId)
690         {
691         case CERT_AUTO_ENROLL_PROP_ID:
692         case CERT_CTL_USAGE_PROP_ID: /* same as CERT_ENHKEY_USAGE_PROP_ID */
693         case CERT_DESCRIPTION_PROP_ID:
694         case CERT_FRIENDLY_NAME_PROP_ID:
695         case CERT_HASH_PROP_ID:
696         case CERT_KEY_IDENTIFIER_PROP_ID:
697         case CERT_MD5_HASH_PROP_ID:
698         case CERT_NEXT_UPDATE_LOCATION_PROP_ID:
699         case CERT_PUBKEY_ALG_PARA_PROP_ID:
700         case CERT_PVK_FILE_PROP_ID:
701         case CERT_SIGNATURE_HASH_PROP_ID:
702         case CERT_ISSUER_PUBLIC_KEY_MD5_HASH_PROP_ID:
703         case CERT_SUBJECT_NAME_MD5_HASH_PROP_ID:
704         case CERT_EXTENDED_ERROR_INFO_PROP_ID:
705         case CERT_SUBJECT_PUBLIC_KEY_MD5_HASH_PROP_ID:
706         case CERT_ENROLLMENT_PROP_ID:
707         case CERT_CROSS_CERT_DIST_POINTS_PROP_ID:
708         case CERT_OCSP_RESPONSE_PROP_ID:
709         case CERT_RENEWAL_PROP_ID:
710         {
711             if (pvData)
712             {
713                 const CRYPT_DATA_BLOB *blob = pvData;
714 
715                 ret = ContextPropertyList_SetProperty(cert->base.properties, dwPropId,
716                  blob->pbData, blob->cbData);
717             }
718             else
719             {
720                 ContextPropertyList_RemoveProperty(cert->base.properties, dwPropId);
721                 ret = TRUE;
722             }
723             break;
724         }
725         case CERT_DATE_STAMP_PROP_ID:
726             if (pvData)
727                 ret = ContextPropertyList_SetProperty(cert->base.properties, dwPropId,
728                  pvData, sizeof(FILETIME));
729             else
730             {
731                 ContextPropertyList_RemoveProperty(cert->base.properties, dwPropId);
732                 ret = TRUE;
733             }
734             break;
735         case CERT_KEY_CONTEXT_PROP_ID:
736         {
737             if (pvData)
738             {
739                 const CERT_KEY_CONTEXT *keyContext = pvData;
740 
741                 if (keyContext->cbSize != sizeof(CERT_KEY_CONTEXT))
742                 {
743                     SetLastError(E_INVALIDARG);
744                     ret = FALSE;
745                 }
746                 else
747                     ret = ContextPropertyList_SetProperty(cert->base.properties, dwPropId,
748                      (const BYTE *)keyContext, keyContext->cbSize);
749             }
750             else
751             {
752                 ContextPropertyList_RemoveProperty(cert->base.properties, dwPropId);
753                 ret = TRUE;
754             }
755             break;
756         }
757         case CERT_KEY_PROV_INFO_PROP_ID:
758             if (pvData)
759                 ret = CertContext_SetKeyProvInfoProperty(cert->base.properties, pvData);
760             else
761             {
762                 ContextPropertyList_RemoveProperty(cert->base.properties, dwPropId);
763                 ret = TRUE;
764             }
765             break;
766         case CERT_KEY_PROV_HANDLE_PROP_ID:
767         {
768             CERT_KEY_CONTEXT keyContext;
769             DWORD size = sizeof(keyContext);
770 
771             ret = CertContext_GetProperty(cert, CERT_KEY_CONTEXT_PROP_ID,
772              &keyContext, &size);
773             if (ret)
774             {
775                 if (!(dwFlags & CERT_STORE_NO_CRYPT_RELEASE_FLAG))
776                     CryptReleaseContext(keyContext.hCryptProv, 0);
777             }
778             keyContext.cbSize = sizeof(keyContext);
779             if (pvData)
780                 keyContext.hCryptProv = *(const HCRYPTPROV *)pvData;
781             else
782             {
783                 keyContext.hCryptProv = 0;
784                 keyContext.dwKeySpec = AT_SIGNATURE;
785             }
786             ret = CertContext_SetProperty(cert, CERT_KEY_CONTEXT_PROP_ID,
787              0, &keyContext);
788             break;
789         }
790         default:
791             FIXME("%d: stub\n", dwPropId);
792             ret = FALSE;
793         }
794     }
795     TRACE("returning %d\n", ret);
796     return ret;
797 }
798 
799 BOOL WINAPI CertSetCertificateContextProperty(PCCERT_CONTEXT pCertContext,
800  DWORD dwPropId, DWORD dwFlags, const void *pvData)
801 {
802     BOOL ret;
803 
804     TRACE("(%p, %d, %08x, %p)\n", pCertContext, dwPropId, dwFlags, pvData);
805 
806     /* Handle special cases for "read-only"/invalid prop IDs.  Windows just
807      * crashes on most of these, I'll be safer.
808      */
809     switch (dwPropId)
810     {
811     case 0:
812     case CERT_ACCESS_STATE_PROP_ID:
813     case CERT_CERT_PROP_ID:
814     case CERT_CRL_PROP_ID:
815     case CERT_CTL_PROP_ID:
816         SetLastError(E_INVALIDARG);
817         return FALSE;
818     }
819     ret = CertContext_SetProperty(cert_from_ptr(pCertContext), dwPropId, dwFlags,
820      pvData);
821     TRACE("returning %d\n", ret);
822     return ret;
823 }
824 
825 /* Acquires the private key using the key provider info, retrieving info from
826  * the certificate if info is NULL.  The acquired provider is returned in
827  * *phCryptProv, and the key spec for the provider is returned in *pdwKeySpec.
828  */
829 static BOOL CRYPT_AcquirePrivateKeyFromProvInfo(PCCERT_CONTEXT pCert,
830  PCRYPT_KEY_PROV_INFO info, HCRYPTPROV *phCryptProv, DWORD *pdwKeySpec)
831 {
832     DWORD size = 0;
833     BOOL allocated = FALSE, ret = TRUE;
834 
835     if (!info)
836     {
837         ret = CertGetCertificateContextProperty(pCert,
838          CERT_KEY_PROV_INFO_PROP_ID, 0, &size);
839         if (ret)
840         {
841             info = HeapAlloc(GetProcessHeap(), 0, size);
842             if (info)
843             {
844                 ret = CertGetCertificateContextProperty(pCert,
845                  CERT_KEY_PROV_INFO_PROP_ID, info, &size);
846                 allocated = TRUE;
847             }
848             else
849             {
850                 SetLastError(ERROR_OUTOFMEMORY);
851                 ret = FALSE;
852             }
853         }
854         else
855             SetLastError(CRYPT_E_NO_KEY_PROPERTY);
856     }
857     if (ret)
858     {
859         ret = CryptAcquireContextW(phCryptProv, info->pwszContainerName,
860          info->pwszProvName, info->dwProvType, 0);
861         if (ret)
862         {
863             DWORD i;
864 
865             for (i = 0; i < info->cProvParam; i++)
866             {
867                 CryptSetProvParam(*phCryptProv,
868                  info->rgProvParam[i].dwParam, info->rgProvParam[i].pbData,
869                  info->rgProvParam[i].dwFlags);
870             }
871             *pdwKeySpec = info->dwKeySpec;
872         }
873         else
874             SetLastError(CRYPT_E_NO_KEY_PROPERTY);
875     }
876     if (allocated)
877         HeapFree(GetProcessHeap(), 0, info);
878     return ret;
879 }
880 
881 BOOL WINAPI CryptAcquireCertificatePrivateKey(PCCERT_CONTEXT pCert,
882  DWORD dwFlags, void *pvReserved, HCRYPTPROV_OR_NCRYPT_KEY_HANDLE *phCryptProv,
883  DWORD *pdwKeySpec, BOOL *pfCallerFreeProv)
884 {
885     BOOL ret = FALSE, cache = FALSE;
886     PCRYPT_KEY_PROV_INFO info = NULL;
887     CERT_KEY_CONTEXT keyContext;
888     DWORD size;
889 
890     TRACE("(%p, %08x, %p, %p, %p, %p)\n", pCert, dwFlags, pvReserved,
891      phCryptProv, pdwKeySpec, pfCallerFreeProv);
892 
893     if (dwFlags & CRYPT_ACQUIRE_USE_PROV_INFO_FLAG)
894     {
895         DWORD size = 0;
896 
897         ret = CertGetCertificateContextProperty(pCert,
898          CERT_KEY_PROV_INFO_PROP_ID, 0, &size);
899         if (ret)
900         {
901             info = HeapAlloc(GetProcessHeap(), 0, size);
902             ret = CertGetCertificateContextProperty(pCert,
903              CERT_KEY_PROV_INFO_PROP_ID, info, &size);
904             if (ret)
905                 cache = info->dwFlags & CERT_SET_KEY_CONTEXT_PROP_ID;
906         }
907     }
908     else if (dwFlags & CRYPT_ACQUIRE_CACHE_FLAG)
909         cache = TRUE;
910     *phCryptProv = 0;
911     if (cache)
912     {
913         size = sizeof(keyContext);
914         ret = CertGetCertificateContextProperty(pCert, CERT_KEY_CONTEXT_PROP_ID,
915          &keyContext, &size);
916         if (ret)
917         {
918             *phCryptProv = keyContext.hCryptProv;
919             if (pdwKeySpec)
920                 *pdwKeySpec = keyContext.dwKeySpec;
921             if (pfCallerFreeProv)
922                 *pfCallerFreeProv = !cache;
923         }
924     }
925     if (!*phCryptProv)
926     {
927         ret = CRYPT_AcquirePrivateKeyFromProvInfo(pCert, info,
928          &keyContext.hCryptProv, &keyContext.dwKeySpec);
929         if (ret)
930         {
931             *phCryptProv = keyContext.hCryptProv;
932             if (pdwKeySpec)
933                 *pdwKeySpec = keyContext.dwKeySpec;
934             if (cache)
935             {
936                 keyContext.cbSize = sizeof(keyContext);
937                 if (CertSetCertificateContextProperty(pCert,
938                  CERT_KEY_CONTEXT_PROP_ID, 0, &keyContext))
939                 {
940                     if (pfCallerFreeProv)
941                         *pfCallerFreeProv = FALSE;
942                 }
943             }
944             else
945             {
946                 if (pfCallerFreeProv)
947                     *pfCallerFreeProv = TRUE;
948             }
949         }
950     }
951     HeapFree(GetProcessHeap(), 0, info);
952     return ret;
953 }
954 
955 static BOOL key_prov_info_matches_cert(PCCERT_CONTEXT pCert,
956  const CRYPT_KEY_PROV_INFO *keyProvInfo)
957 {
958     HCRYPTPROV csp;
959     BOOL matches = FALSE;
960 
961     if (CryptAcquireContextW(&csp, keyProvInfo->pwszContainerName,
962      keyProvInfo->pwszProvName, keyProvInfo->dwProvType, keyProvInfo->dwFlags))
963     {
964         DWORD size;
965 
966         /* Need to sign something to verify the sig.  What to sign?  Why not
967          * the certificate itself?
968          */
969         if (CryptSignAndEncodeCertificate(csp, AT_SIGNATURE,
970          pCert->dwCertEncodingType, X509_CERT_TO_BE_SIGNED, pCert->pCertInfo,
971          &pCert->pCertInfo->SignatureAlgorithm, NULL, NULL, &size))
972         {
973             BYTE *certEncoded = CryptMemAlloc(size);
974 
975             if (certEncoded)
976             {
977                 if (CryptSignAndEncodeCertificate(csp, AT_SIGNATURE,
978                  pCert->dwCertEncodingType, X509_CERT_TO_BE_SIGNED,
979                  pCert->pCertInfo, &pCert->pCertInfo->SignatureAlgorithm,
980                  NULL, certEncoded, &size))
981                 {
982                     if (size == pCert->cbCertEncoded &&
983                      !memcmp(certEncoded, pCert->pbCertEncoded, size))
984                         matches = TRUE;
985                 }
986                 CryptMemFree(certEncoded);
987             }
988         }
989         CryptReleaseContext(csp, 0);
990     }
991     return matches;
992 }
993 
994 static BOOL container_matches_cert(PCCERT_CONTEXT pCert, LPCSTR container,
995  CRYPT_KEY_PROV_INFO *keyProvInfo)
996 {
997     CRYPT_KEY_PROV_INFO copy;
998     WCHAR containerW[MAX_PATH];
999     BOOL matches;
1000 
1001     MultiByteToWideChar(CP_ACP, 0, container, -1, containerW, ARRAY_SIZE(containerW));
1002     /* We make a copy of the CRYPT_KEY_PROV_INFO because the caller expects
1003      * keyProvInfo->pwszContainerName to be NULL or a heap-allocated container
1004      * name.
1005      */
1006     copy = *keyProvInfo;
1007     copy.pwszContainerName = containerW;
1008     matches = key_prov_info_matches_cert(pCert, &copy);
1009     if (matches)
1010     {
1011         keyProvInfo->pwszContainerName =
1012          CryptMemAlloc((strlenW(containerW) + 1) * sizeof(WCHAR));
1013         if (keyProvInfo->pwszContainerName)
1014         {
1015             strcpyW(keyProvInfo->pwszContainerName, containerW);
1016             keyProvInfo->dwKeySpec = AT_SIGNATURE;
1017         }
1018         else
1019             matches = FALSE;
1020     }
1021     return matches;
1022 }
1023 
1024 /* Searches the provider named keyProvInfo.pwszProvName for a container whose
1025  * private key matches pCert's public key.  Upon success, updates keyProvInfo
1026  * with the matching container's info (free keyProvInfo.pwszContainerName upon
1027  * success.)
1028  * Returns TRUE if found, FALSE if not.
1029  */
1030 static BOOL find_key_prov_info_in_provider(PCCERT_CONTEXT pCert,
1031  CRYPT_KEY_PROV_INFO *keyProvInfo)
1032 {
1033     HCRYPTPROV defProvider;
1034     BOOL ret, found = FALSE;
1035     char containerA[MAX_PATH];
1036 
1037     assert(keyProvInfo->pwszContainerName == NULL);
1038     if ((ret = CryptAcquireContextW(&defProvider, NULL,
1039      keyProvInfo->pwszProvName, keyProvInfo->dwProvType,
1040      keyProvInfo->dwFlags | CRYPT_VERIFYCONTEXT)))
1041     {
1042         DWORD enumFlags = keyProvInfo->dwFlags | CRYPT_FIRST;
1043 
1044         while (ret && !found)
1045         {
1046             DWORD size = sizeof(containerA);
1047 
1048             ret = CryptGetProvParam(defProvider, PP_ENUMCONTAINERS,
1049              (BYTE *)containerA, &size, enumFlags);
1050             if (ret)
1051                 found = container_matches_cert(pCert, containerA, keyProvInfo);
1052             if (enumFlags & CRYPT_FIRST)
1053             {
1054                 enumFlags &= ~CRYPT_FIRST;
1055                 enumFlags |= CRYPT_NEXT;
1056             }
1057         }
1058         CryptReleaseContext(defProvider, 0);
1059     }
1060     return found;
1061 }
1062 
1063 static BOOL find_matching_provider(PCCERT_CONTEXT pCert, DWORD dwFlags)
1064 {
1065     BOOL found = FALSE, ret = TRUE;
1066     DWORD index = 0, cbProvName = 0;
1067     CRYPT_KEY_PROV_INFO keyProvInfo;
1068 
1069     TRACE("(%p, %08x)\n", pCert, dwFlags);
1070 
1071     memset(&keyProvInfo, 0, sizeof(keyProvInfo));
1072     while (ret && !found)
1073     {
1074         DWORD size = 0;
1075 
1076         ret = CryptEnumProvidersW(index, NULL, 0, &keyProvInfo.dwProvType,
1077          NULL, &size);
1078         if (ret)
1079         {
1080             if (size <= cbProvName)
1081                 ret = CryptEnumProvidersW(index, NULL, 0,
1082                  &keyProvInfo.dwProvType, keyProvInfo.pwszProvName, &size);
1083             else
1084             {
1085                 CryptMemFree(keyProvInfo.pwszProvName);
1086                 keyProvInfo.pwszProvName = CryptMemAlloc(size);
1087                 if (keyProvInfo.pwszProvName)
1088                 {
1089                     cbProvName = size;
1090                     ret = CryptEnumProvidersW(index, NULL, 0,
1091                      &keyProvInfo.dwProvType, keyProvInfo.pwszProvName, &size);
1092                     if (ret)
1093                     {
1094                         if (dwFlags & CRYPT_FIND_SILENT_KEYSET_FLAG)
1095                             keyProvInfo.dwFlags |= CRYPT_SILENT;
1096                         if (dwFlags & CRYPT_FIND_USER_KEYSET_FLAG ||
1097                          !(dwFlags & (CRYPT_FIND_USER_KEYSET_FLAG |
1098                          CRYPT_FIND_MACHINE_KEYSET_FLAG)))
1099                         {
1100                             keyProvInfo.dwFlags |= CRYPT_USER_KEYSET;
1101                             found = find_key_prov_info_in_provider(pCert,
1102                              &keyProvInfo);
1103                         }
1104                         if (!found)
1105                         {
1106                             if (dwFlags & CRYPT_FIND_MACHINE_KEYSET_FLAG ||
1107                              !(dwFlags & (CRYPT_FIND_USER_KEYSET_FLAG |
1108                              CRYPT_FIND_MACHINE_KEYSET_FLAG)))
1109                             {
1110                                 keyProvInfo.dwFlags &= ~CRYPT_USER_KEYSET;
1111                                 keyProvInfo.dwFlags |= CRYPT_MACHINE_KEYSET;
1112                                 found = find_key_prov_info_in_provider(pCert,
1113                                  &keyProvInfo);
1114                             }
1115                         }
1116                     }
1117                 }
1118                 else
1119                     ret = FALSE;
1120             }
1121             index++;
1122         }
1123     }
1124     if (found)
1125         CertSetCertificateContextProperty(pCert, CERT_KEY_PROV_INFO_PROP_ID,
1126          0, &keyProvInfo);
1127     CryptMemFree(keyProvInfo.pwszProvName);
1128     CryptMemFree(keyProvInfo.pwszContainerName);
1129     return found;
1130 }
1131 
1132 static BOOL cert_prov_info_matches_cert(PCCERT_CONTEXT pCert)
1133 {
1134     BOOL matches = FALSE;
1135     DWORD size;
1136 
1137     if (CertGetCertificateContextProperty(pCert, CERT_KEY_PROV_INFO_PROP_ID,
1138      NULL, &size))
1139     {
1140         CRYPT_KEY_PROV_INFO *keyProvInfo = CryptMemAlloc(size);
1141 
1142         if (keyProvInfo)
1143         {
1144             if (CertGetCertificateContextProperty(pCert,
1145              CERT_KEY_PROV_INFO_PROP_ID, keyProvInfo, &size))
1146                 matches = key_prov_info_matches_cert(pCert, keyProvInfo);
1147             CryptMemFree(keyProvInfo);
1148         }
1149     }
1150     return matches;
1151 }
1152 
1153 BOOL WINAPI CryptFindCertificateKeyProvInfo(PCCERT_CONTEXT pCert,
1154  DWORD dwFlags, void *pvReserved)
1155 {
1156     BOOL matches;
1157 
1158     TRACE("(%p, %08x, %p)\n", pCert, dwFlags, pvReserved);
1159 
1160     matches = cert_prov_info_matches_cert(pCert);
1161     if (!matches)
1162         matches = find_matching_provider(pCert, dwFlags);
1163     return matches;
1164 }
1165 
1166 BOOL WINAPI CertCompareCertificate(DWORD dwCertEncodingType,
1167  PCERT_INFO pCertId1, PCERT_INFO pCertId2)
1168 {
1169     BOOL ret;
1170 
1171     TRACE("(%08x, %p, %p)\n", dwCertEncodingType, pCertId1, pCertId2);
1172 
1173     ret = CertCompareCertificateName(dwCertEncodingType, &pCertId1->Issuer,
1174      &pCertId2->Issuer) && CertCompareIntegerBlob(&pCertId1->SerialNumber,
1175      &pCertId2->SerialNumber);
1176     TRACE("returning %d\n", ret);
1177     return ret;
1178 }
1179 
1180 BOOL WINAPI CertCompareCertificateName(DWORD dwCertEncodingType,
1181  PCERT_NAME_BLOB pCertName1, PCERT_NAME_BLOB pCertName2)
1182 {
1183     BOOL ret;
1184 
1185     TRACE("(%08x, %p, %p)\n", dwCertEncodingType, pCertName1, pCertName2);
1186 
1187     if (pCertName1->cbData == pCertName2->cbData)
1188     {
1189         if (pCertName1->cbData)
1190             ret = !memcmp(pCertName1->pbData, pCertName2->pbData,
1191              pCertName1->cbData);
1192         else
1193             ret = TRUE;
1194     }
1195     else
1196         ret = FALSE;
1197     TRACE("returning %d\n", ret);
1198     return ret;
1199 }
1200 
1201 /* Returns the number of significant bytes in pInt, where a byte is
1202  * insignificant if it's a leading 0 for positive numbers or a leading 0xff
1203  * for negative numbers.  pInt is assumed to be little-endian.
1204  */
1205 static DWORD CRYPT_significantBytes(const CRYPT_INTEGER_BLOB *pInt)
1206 {
1207     DWORD ret = pInt->cbData;
1208 
1209     while (ret > 1)
1210     {
1211         if (pInt->pbData[ret - 2] <= 0x7f && pInt->pbData[ret - 1] == 0)
1212             ret--;
1213         else if (pInt->pbData[ret - 2] >= 0x80 && pInt->pbData[ret - 1] == 0xff)
1214             ret--;
1215         else
1216             break;
1217     }
1218     return ret;
1219 }
1220 
1221 BOOL WINAPI CertCompareIntegerBlob(PCRYPT_INTEGER_BLOB pInt1,
1222  PCRYPT_INTEGER_BLOB pInt2)
1223 {
1224     BOOL ret;
1225     DWORD cb1, cb2;
1226 
1227     TRACE("(%p, %p)\n", pInt1, pInt2);
1228 
1229     cb1 = CRYPT_significantBytes(pInt1);
1230     cb2 = CRYPT_significantBytes(pInt2);
1231     if (cb1 == cb2)
1232     {
1233         if (cb1)
1234             ret = !memcmp(pInt1->pbData, pInt2->pbData, cb1);
1235         else
1236             ret = TRUE;
1237     }
1238     else
1239         ret = FALSE;
1240     TRACE("returning %d\n", ret);
1241     return ret;
1242 }
1243 
1244 BOOL WINAPI CertComparePublicKeyInfo(DWORD dwCertEncodingType,
1245  PCERT_PUBLIC_KEY_INFO pPublicKey1, PCERT_PUBLIC_KEY_INFO pPublicKey2)
1246 {
1247     BOOL ret;
1248 
1249     TRACE("(%08x, %p, %p)\n", dwCertEncodingType, pPublicKey1, pPublicKey2);
1250 
1251     /* RSA public key data should start with ASN_SEQUENCE,
1252      * otherwise it's not a RSA_CSP_PUBLICKEYBLOB.
1253      */
1254     if (!pPublicKey1->PublicKey.cbData || pPublicKey1->PublicKey.pbData[0] != ASN_SEQUENCE)
1255         dwCertEncodingType = 0;
1256 
1257     switch (GET_CERT_ENCODING_TYPE(dwCertEncodingType))
1258     {
1259     case 0:	/* Seems to mean "raw binary bits" */
1260         if (pPublicKey1->PublicKey.cbData == pPublicKey2->PublicKey.cbData &&
1261          pPublicKey1->PublicKey.cUnusedBits == pPublicKey2->PublicKey.cUnusedBits)
1262         {
1263           if (pPublicKey2->PublicKey.cbData)
1264               ret = !memcmp(pPublicKey1->PublicKey.pbData,
1265                pPublicKey2->PublicKey.pbData, pPublicKey1->PublicKey.cbData);
1266           else
1267               ret = TRUE;
1268         }
1269         else
1270             ret = FALSE;
1271         break;
1272     default:
1273         WARN("Unknown encoding type %08x\n", dwCertEncodingType);
1274         /* FALLTHROUGH */
1275     case X509_ASN_ENCODING:
1276     {
1277         BLOBHEADER *pblob1, *pblob2;
1278         DWORD length;
1279         ret = FALSE;
1280         if (CryptDecodeObject(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB,
1281                     pPublicKey1->PublicKey.pbData, pPublicKey1->PublicKey.cbData,
1282                     CRYPT_DECODE_ALLOC_FLAG, &pblob1, &length))
1283         {
1284             if (CryptDecodeObject(dwCertEncodingType, RSA_CSP_PUBLICKEYBLOB,
1285                         pPublicKey2->PublicKey.pbData, pPublicKey2->PublicKey.cbData,
1286                         CRYPT_DECODE_ALLOC_FLAG, &pblob2, &length))
1287             {
1288                 /* The RSAPUBKEY structure directly follows the BLOBHEADER */
1289                 RSAPUBKEY *pk1 = (LPVOID)(pblob1 + 1),
1290                           *pk2 = (LPVOID)(pblob2 + 1);
1291                 ret = (pk1->bitlen == pk2->bitlen) && (pk1->pubexp == pk2->pubexp)
1292                          && !memcmp(pk1 + 1, pk2 + 1, pk1->bitlen/8);
1293 
1294                 LocalFree(pblob2);
1295             }
1296             LocalFree(pblob1);
1297         }
1298 
1299         break;
1300     }
1301     }
1302     return ret;
1303 }
1304 
1305 DWORD WINAPI CertGetPublicKeyLength(DWORD dwCertEncodingType,
1306  PCERT_PUBLIC_KEY_INFO pPublicKey)
1307 {
1308     DWORD len = 0;
1309 
1310     TRACE("(%08x, %p)\n", dwCertEncodingType, pPublicKey);
1311 
1312     if (GET_CERT_ENCODING_TYPE(dwCertEncodingType) != X509_ASN_ENCODING)
1313     {
1314         SetLastError(ERROR_FILE_NOT_FOUND);
1315         return 0;
1316     }
1317     if (pPublicKey->Algorithm.pszObjId &&
1318      !strcmp(pPublicKey->Algorithm.pszObjId, szOID_RSA_DH))
1319     {
1320         FIXME("unimplemented for DH public keys\n");
1321         SetLastError(CRYPT_E_ASN1_BADTAG);
1322     }
1323     else
1324     {
1325         PCCRYPT_OID_INFO info;
1326         DWORD size;
1327         PBYTE buf;
1328         BOOL ret;
1329 
1330         info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, pPublicKey->Algorithm.pszObjId, 0);
1331         if (info)
1332         {
1333             HCRYPTKEY key;
1334 
1335             TRACE("public key algid %#x (%s)\n", info->u.Algid, debugstr_a(pPublicKey->Algorithm.pszObjId));
1336 
1337             ret = CryptImportPublicKeyInfo(I_CryptGetDefaultCryptProv(info->u.Algid), dwCertEncodingType, pPublicKey, &key);
1338             if (ret)
1339             {
1340                 size = sizeof(len);
1341                 ret = CryptGetKeyParam(key, KP_KEYLEN, (BYTE *)&len, &size, 0);
1342                 CryptDestroyKey(key);
1343                 return len;
1344             }
1345             /* fallback to RSA */
1346         }
1347 
1348         ret = CryptDecodeObjectEx(dwCertEncodingType,
1349          RSA_CSP_PUBLICKEYBLOB, pPublicKey->PublicKey.pbData,
1350          pPublicKey->PublicKey.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &buf,
1351          &size);
1352 
1353         if (ret)
1354         {
1355             RSAPUBKEY *rsaPubKey = (RSAPUBKEY *)(buf + sizeof(BLOBHEADER));
1356 
1357             len = rsaPubKey->bitlen;
1358             LocalFree(buf);
1359         }
1360     }
1361     return len;
1362 }
1363 
1364 typedef BOOL (*CertCompareFunc)(PCCERT_CONTEXT pCertContext, DWORD dwType,
1365  DWORD dwFlags, const void *pvPara);
1366 
1367 static BOOL compare_cert_by_md5_hash(PCCERT_CONTEXT pCertContext, DWORD dwType,
1368  DWORD dwFlags, const void *pvPara)
1369 {
1370     BOOL ret;
1371     BYTE hash[16];
1372     DWORD size = sizeof(hash);
1373 
1374     ret = CertGetCertificateContextProperty(pCertContext,
1375      CERT_MD5_HASH_PROP_ID, hash, &size);
1376     if (ret)
1377     {
1378         const CRYPT_HASH_BLOB *pHash = pvPara;
1379 
1380         if (size == pHash->cbData)
1381             ret = !memcmp(pHash->pbData, hash, size);
1382         else
1383             ret = FALSE;
1384     }
1385     return ret;
1386 }
1387 
1388 static BOOL compare_cert_by_sha1_hash(PCCERT_CONTEXT pCertContext, DWORD dwType,
1389  DWORD dwFlags, const void *pvPara)
1390 {
1391     BOOL ret;
1392     BYTE hash[20];
1393     DWORD size = sizeof(hash);
1394 
1395     ret = CertGetCertificateContextProperty(pCertContext,
1396      CERT_SHA1_HASH_PROP_ID, hash, &size);
1397     if (ret)
1398     {
1399         const CRYPT_HASH_BLOB *pHash = pvPara;
1400 
1401         if (size == pHash->cbData)
1402             ret = !memcmp(pHash->pbData, hash, size);
1403         else
1404             ret = FALSE;
1405     }
1406     return ret;
1407 }
1408 
1409 static BOOL compare_cert_by_name(PCCERT_CONTEXT pCertContext, DWORD dwType,
1410  DWORD dwFlags, const void *pvPara)
1411 {
1412     CERT_NAME_BLOB *blob = (CERT_NAME_BLOB *)pvPara, *toCompare;
1413     BOOL ret;
1414 
1415     if (dwType & CERT_INFO_SUBJECT_FLAG)
1416         toCompare = &pCertContext->pCertInfo->Subject;
1417     else
1418         toCompare = &pCertContext->pCertInfo->Issuer;
1419     ret = CertCompareCertificateName(pCertContext->dwCertEncodingType,
1420      toCompare, blob);
1421     return ret;
1422 }
1423 
1424 static BOOL compare_cert_by_public_key(PCCERT_CONTEXT pCertContext,
1425  DWORD dwType, DWORD dwFlags, const void *pvPara)
1426 {
1427     CERT_PUBLIC_KEY_INFO *publicKey = (CERT_PUBLIC_KEY_INFO *)pvPara;
1428     BOOL ret;
1429 
1430     ret = CertComparePublicKeyInfo(pCertContext->dwCertEncodingType,
1431      &pCertContext->pCertInfo->SubjectPublicKeyInfo, publicKey);
1432     return ret;
1433 }
1434 
1435 static BOOL compare_cert_by_subject_cert(PCCERT_CONTEXT pCertContext,
1436  DWORD dwType, DWORD dwFlags, const void *pvPara)
1437 {
1438     CERT_INFO *pCertInfo = (CERT_INFO *)pvPara;
1439     BOOL ret;
1440 
1441     /* Matching serial number and subject match.. */
1442     ret = CertCompareCertificateName(pCertContext->dwCertEncodingType,
1443      &pCertContext->pCertInfo->Subject, &pCertInfo->Issuer);
1444     if (ret)
1445         ret = CertCompareIntegerBlob(&pCertContext->pCertInfo->SerialNumber,
1446          &pCertInfo->SerialNumber);
1447     else
1448     {
1449         /* failing that, if the serial number and issuer match, we match */
1450         ret = CertCompareIntegerBlob(&pCertContext->pCertInfo->SerialNumber,
1451          &pCertInfo->SerialNumber);
1452         if (ret)
1453             ret = CertCompareCertificateName(pCertContext->dwCertEncodingType,
1454              &pCertContext->pCertInfo->Issuer, &pCertInfo->Issuer);
1455     }
1456     TRACE("returning %d\n", ret);
1457     return ret;
1458 }
1459 
1460 static BOOL compare_cert_by_cert_id(PCCERT_CONTEXT pCertContext, DWORD dwType,
1461  DWORD dwFlags, const void *pvPara)
1462 {
1463     CERT_ID *id = (CERT_ID *)pvPara;
1464     BOOL ret;
1465 
1466     switch (id->dwIdChoice)
1467     {
1468     case CERT_ID_ISSUER_SERIAL_NUMBER:
1469         ret = CertCompareCertificateName(pCertContext->dwCertEncodingType,
1470          &pCertContext->pCertInfo->Issuer, &id->u.IssuerSerialNumber.Issuer);
1471         if (ret)
1472             ret = CertCompareIntegerBlob(&pCertContext->pCertInfo->SerialNumber,
1473              &id->u.IssuerSerialNumber.SerialNumber);
1474         break;
1475     case CERT_ID_SHA1_HASH:
1476         ret = compare_cert_by_sha1_hash(pCertContext, dwType, dwFlags,
1477          &id->u.HashId);
1478         break;
1479     case CERT_ID_KEY_IDENTIFIER:
1480     {
1481         DWORD size = 0;
1482 
1483         ret = CertGetCertificateContextProperty(pCertContext,
1484          CERT_KEY_IDENTIFIER_PROP_ID, NULL, &size);
1485         if (ret && size == id->u.KeyId.cbData)
1486         {
1487             LPBYTE buf = CryptMemAlloc(size);
1488 
1489             if (buf)
1490             {
1491                 CertGetCertificateContextProperty(pCertContext,
1492                  CERT_KEY_IDENTIFIER_PROP_ID, buf, &size);
1493                 ret = !memcmp(buf, id->u.KeyId.pbData, size);
1494                 CryptMemFree(buf);
1495             }
1496             else
1497                 ret = FALSE;
1498         }
1499         else
1500             ret = FALSE;
1501         break;
1502     }
1503     default:
1504         ret = FALSE;
1505         break;
1506     }
1507     return ret;
1508 }
1509 
1510 static BOOL compare_existing_cert(PCCERT_CONTEXT pCertContext, DWORD dwType,
1511  DWORD dwFlags, const void *pvPara)
1512 {
1513     PCCERT_CONTEXT toCompare = pvPara;
1514     return CertCompareCertificate(pCertContext->dwCertEncodingType,
1515      pCertContext->pCertInfo, toCompare->pCertInfo);
1516 }
1517 
1518 static BOOL compare_cert_by_signature_hash(PCCERT_CONTEXT pCertContext, DWORD dwType,
1519  DWORD dwFlags, const void *pvPara)
1520 {
1521     const CRYPT_HASH_BLOB *hash = pvPara;
1522     DWORD size = 0;
1523     BOOL ret;
1524 
1525     ret = CertGetCertificateContextProperty(pCertContext,
1526      CERT_SIGNATURE_HASH_PROP_ID, NULL, &size);
1527     if (ret && size == hash->cbData)
1528     {
1529         LPBYTE buf = CryptMemAlloc(size);
1530 
1531         if (buf)
1532         {
1533             CertGetCertificateContextProperty(pCertContext,
1534              CERT_SIGNATURE_HASH_PROP_ID, buf, &size);
1535             ret = !memcmp(buf, hash->pbData, size);
1536             CryptMemFree(buf);
1537         }
1538         else
1539             ret = FALSE;
1540     }
1541     else
1542         ret = FALSE;
1543     return ret;
1544 }
1545 
1546 static inline PCCERT_CONTEXT cert_compare_certs_in_store(HCERTSTORE store,
1547  PCCERT_CONTEXT prev, CertCompareFunc compare, DWORD dwType, DWORD dwFlags,
1548  const void *pvPara)
1549 {
1550     BOOL matches = FALSE;
1551     PCCERT_CONTEXT ret;
1552 
1553     ret = prev;
1554     do {
1555         ret = CertEnumCertificatesInStore(store, ret);
1556         if (ret)
1557             matches = compare(ret, dwType, dwFlags, pvPara);
1558     } while (ret != NULL && !matches);
1559     return ret;
1560 }
1561 
1562 typedef PCCERT_CONTEXT (*CertFindFunc)(HCERTSTORE store, DWORD dwType,
1563  DWORD dwFlags, const void *pvPara, PCCERT_CONTEXT prev);
1564 
1565 static PCCERT_CONTEXT find_cert_any(HCERTSTORE store, DWORD dwType,
1566  DWORD dwFlags, const void *pvPara, PCCERT_CONTEXT prev)
1567 {
1568     return CertEnumCertificatesInStore(store, prev);
1569 }
1570 
1571 static PCCERT_CONTEXT find_cert_by_issuer(HCERTSTORE store, DWORD dwType,
1572  DWORD dwFlags, const void *pvPara, PCCERT_CONTEXT prev)
1573 {
1574     BOOL ret;
1575     PCCERT_CONTEXT found = NULL, subject = pvPara;
1576     PCERT_EXTENSION ext;
1577     DWORD size;
1578 
1579     if ((ext = CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER,
1580      subject->pCertInfo->cExtension, subject->pCertInfo->rgExtension)))
1581     {
1582         CERT_AUTHORITY_KEY_ID_INFO *info;
1583 
1584         ret = CryptDecodeObjectEx(subject->dwCertEncodingType,
1585          X509_AUTHORITY_KEY_ID, ext->Value.pbData, ext->Value.cbData,
1586          CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
1587          &info, &size);
1588         if (ret)
1589         {
1590             CERT_ID id;
1591 
1592             if (info->CertIssuer.cbData && info->CertSerialNumber.cbData)
1593             {
1594                 id.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
1595                 memcpy(&id.u.IssuerSerialNumber.Issuer, &info->CertIssuer,
1596                  sizeof(CERT_NAME_BLOB));
1597                 memcpy(&id.u.IssuerSerialNumber.SerialNumber,
1598                  &info->CertSerialNumber, sizeof(CRYPT_INTEGER_BLOB));
1599             }
1600             else if (info->KeyId.cbData)
1601             {
1602                 id.dwIdChoice = CERT_ID_KEY_IDENTIFIER;
1603                 memcpy(&id.u.KeyId, &info->KeyId, sizeof(CRYPT_HASH_BLOB));
1604             }
1605             else
1606                 ret = FALSE;
1607             if (ret)
1608                 found = cert_compare_certs_in_store(store, prev,
1609                  compare_cert_by_cert_id, dwType, dwFlags, &id);
1610             LocalFree(info);
1611         }
1612     }
1613     else if ((ext = CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER2,
1614      subject->pCertInfo->cExtension, subject->pCertInfo->rgExtension)))
1615     {
1616         CERT_AUTHORITY_KEY_ID2_INFO *info;
1617 
1618         ret = CryptDecodeObjectEx(subject->dwCertEncodingType,
1619          X509_AUTHORITY_KEY_ID2, ext->Value.pbData, ext->Value.cbData,
1620          CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
1621          &info, &size);
1622         if (ret)
1623         {
1624             CERT_ID id;
1625 
1626             if (info->AuthorityCertIssuer.cAltEntry &&
1627              info->AuthorityCertSerialNumber.cbData)
1628             {
1629                 PCERT_ALT_NAME_ENTRY directoryName = NULL;
1630                 DWORD i;
1631 
1632                 for (i = 0; !directoryName &&
1633                  i < info->AuthorityCertIssuer.cAltEntry; i++)
1634                     if (info->AuthorityCertIssuer.rgAltEntry[i].dwAltNameChoice
1635                      == CERT_ALT_NAME_DIRECTORY_NAME)
1636                         directoryName =
1637                          &info->AuthorityCertIssuer.rgAltEntry[i];
1638                 if (directoryName)
1639                 {
1640                     id.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
1641                     memcpy(&id.u.IssuerSerialNumber.Issuer,
1642                      &directoryName->u.DirectoryName, sizeof(CERT_NAME_BLOB));
1643                     memcpy(&id.u.IssuerSerialNumber.SerialNumber,
1644                      &info->AuthorityCertSerialNumber,
1645                      sizeof(CRYPT_INTEGER_BLOB));
1646                 }
1647                 else
1648                 {
1649                     FIXME("no supported name type in authority key id2\n");
1650                     ret = FALSE;
1651                 }
1652             }
1653             else if (info->KeyId.cbData)
1654             {
1655                 id.dwIdChoice = CERT_ID_KEY_IDENTIFIER;
1656                 memcpy(&id.u.KeyId, &info->KeyId, sizeof(CRYPT_HASH_BLOB));
1657             }
1658             else
1659                 ret = FALSE;
1660             if (ret)
1661                 found = cert_compare_certs_in_store(store, prev,
1662                  compare_cert_by_cert_id, dwType, dwFlags, &id);
1663             LocalFree(info);
1664         }
1665     }
1666     else
1667        found = cert_compare_certs_in_store(store, prev,
1668         compare_cert_by_name, CERT_COMPARE_NAME | CERT_COMPARE_SUBJECT_CERT,
1669         dwFlags, &subject->pCertInfo->Issuer);
1670     return found;
1671 }
1672 
1673 static BOOL compare_cert_by_name_str(PCCERT_CONTEXT pCertContext,
1674  DWORD dwType, DWORD dwFlags, const void *pvPara)
1675 {
1676     PCERT_NAME_BLOB name;
1677     DWORD len;
1678     BOOL ret = FALSE;
1679 
1680     if (dwType & CERT_INFO_SUBJECT_FLAG)
1681         name = &pCertContext->pCertInfo->Subject;
1682     else
1683         name = &pCertContext->pCertInfo->Issuer;
1684     len = CertNameToStrW(pCertContext->dwCertEncodingType, name,
1685      CERT_SIMPLE_NAME_STR, NULL, 0);
1686     if (len)
1687     {
1688         LPWSTR str = CryptMemAlloc(len * sizeof(WCHAR));
1689 
1690         if (str)
1691         {
1692             LPWSTR ptr;
1693 
1694             CertNameToStrW(pCertContext->dwCertEncodingType, name,
1695              CERT_SIMPLE_NAME_STR, str, len);
1696             for (ptr = str; *ptr; ptr++)
1697                 *ptr = tolowerW(*ptr);
1698             if (strstrW(str, pvPara))
1699                 ret = TRUE;
1700             CryptMemFree(str);
1701         }
1702     }
1703     return ret;
1704 }
1705 
1706 static PCCERT_CONTEXT find_cert_by_name_str_a(HCERTSTORE store, DWORD dwType,
1707  DWORD dwFlags, const void *pvPara, PCCERT_CONTEXT prev)
1708 {
1709     PCCERT_CONTEXT found = NULL;
1710 
1711     TRACE("%s\n", debugstr_a(pvPara));
1712 
1713     if (pvPara)
1714     {
1715         int len = MultiByteToWideChar(CP_ACP, 0, pvPara, -1, NULL, 0);
1716         LPWSTR str = CryptMemAlloc(len * sizeof(WCHAR));
1717 
1718         if (str)
1719         {
1720             LPWSTR ptr;
1721 
1722             MultiByteToWideChar(CP_ACP, 0, pvPara, -1, str, len);
1723             for (ptr = str; *ptr; ptr++)
1724                 *ptr = tolowerW(*ptr);
1725             found = cert_compare_certs_in_store(store, prev,
1726              compare_cert_by_name_str, dwType, dwFlags, str);
1727             CryptMemFree(str);
1728         }
1729     }
1730     else
1731         found = find_cert_any(store, dwType, dwFlags, NULL, prev);
1732     return found;
1733 }
1734 
1735 static PCCERT_CONTEXT find_cert_by_name_str_w(HCERTSTORE store, DWORD dwType,
1736  DWORD dwFlags, const void *pvPara, PCCERT_CONTEXT prev)
1737 {
1738     PCCERT_CONTEXT found = NULL;
1739 
1740     TRACE("%s\n", debugstr_w(pvPara));
1741 
1742     if (pvPara)
1743     {
1744         DWORD len = strlenW(pvPara);
1745         LPWSTR str = CryptMemAlloc((len + 1) * sizeof(WCHAR));
1746 
1747         if (str)
1748         {
1749             LPCWSTR src;
1750             LPWSTR dst;
1751 
1752             for (src = pvPara, dst = str; *src; src++, dst++)
1753                 *dst = tolowerW(*src);
1754             *dst = 0;
1755            found = cert_compare_certs_in_store(store, prev,
1756             compare_cert_by_name_str, dwType, dwFlags, str);
1757            CryptMemFree(str);
1758         }
1759     }
1760     else
1761         found = find_cert_any(store, dwType, dwFlags, NULL, prev);
1762     return found;
1763 }
1764 
1765 PCCERT_CONTEXT WINAPI CertFindCertificateInStore(HCERTSTORE hCertStore,
1766  DWORD dwCertEncodingType, DWORD dwFlags, DWORD dwType, const void *pvPara,
1767  PCCERT_CONTEXT pPrevCertContext)
1768 {
1769     PCCERT_CONTEXT ret;
1770     CertFindFunc find = NULL;
1771     CertCompareFunc compare = NULL;
1772 
1773     TRACE("(%p, %08x, %08x, %08x, %p, %p)\n", hCertStore, dwCertEncodingType,
1774 	 dwFlags, dwType, pvPara, pPrevCertContext);
1775 
1776     switch (dwType >> CERT_COMPARE_SHIFT)
1777     {
1778     case CERT_COMPARE_ANY:
1779         find = find_cert_any;
1780         break;
1781     case CERT_COMPARE_MD5_HASH:
1782         compare = compare_cert_by_md5_hash;
1783         break;
1784     case CERT_COMPARE_SHA1_HASH:
1785         compare = compare_cert_by_sha1_hash;
1786         break;
1787     case CERT_COMPARE_NAME:
1788         compare = compare_cert_by_name;
1789         break;
1790     case CERT_COMPARE_PUBLIC_KEY:
1791         compare = compare_cert_by_public_key;
1792         break;
1793     case CERT_COMPARE_NAME_STR_A:
1794         find = find_cert_by_name_str_a;
1795         break;
1796     case CERT_COMPARE_NAME_STR_W:
1797         find = find_cert_by_name_str_w;
1798         break;
1799     case CERT_COMPARE_SUBJECT_CERT:
1800         compare = compare_cert_by_subject_cert;
1801         break;
1802     case CERT_COMPARE_CERT_ID:
1803         compare = compare_cert_by_cert_id;
1804         break;
1805     case CERT_COMPARE_ISSUER_OF:
1806         find = find_cert_by_issuer;
1807         break;
1808     case CERT_COMPARE_EXISTING:
1809         compare = compare_existing_cert;
1810         break;
1811     case CERT_COMPARE_SIGNATURE_HASH:
1812         compare = compare_cert_by_signature_hash;
1813         break;
1814     default:
1815         FIXME("find type %08x unimplemented\n", dwType);
1816     }
1817 
1818     if (find)
1819         ret = find(hCertStore, dwType, dwFlags, pvPara, pPrevCertContext);
1820     else if (compare)
1821         ret = cert_compare_certs_in_store(hCertStore, pPrevCertContext,
1822          compare, dwType, dwFlags, pvPara);
1823     else
1824         ret = NULL;
1825     if (!ret)
1826         SetLastError(CRYPT_E_NOT_FOUND);
1827     TRACE("returning %p\n", ret);
1828     return ret;
1829 }
1830 
1831 PCCERT_CONTEXT WINAPI CertGetSubjectCertificateFromStore(HCERTSTORE hCertStore,
1832  DWORD dwCertEncodingType, PCERT_INFO pCertId)
1833 {
1834     TRACE("(%p, %08x, %p)\n", hCertStore, dwCertEncodingType, pCertId);
1835 
1836     if (!pCertId)
1837     {
1838         SetLastError(E_INVALIDARG);
1839         return NULL;
1840     }
1841     return CertFindCertificateInStore(hCertStore, dwCertEncodingType, 0,
1842      CERT_FIND_SUBJECT_CERT, pCertId, NULL);
1843 }
1844 
1845 BOOL WINAPI CertVerifySubjectCertificateContext(PCCERT_CONTEXT pSubject,
1846  PCCERT_CONTEXT pIssuer, DWORD *pdwFlags)
1847 {
1848     static const DWORD supportedFlags = CERT_STORE_REVOCATION_FLAG |
1849      CERT_STORE_SIGNATURE_FLAG | CERT_STORE_TIME_VALIDITY_FLAG;
1850 
1851     if (*pdwFlags & ~supportedFlags)
1852     {
1853         SetLastError(E_INVALIDARG);
1854         return FALSE;
1855     }
1856     if (*pdwFlags & CERT_STORE_REVOCATION_FLAG)
1857     {
1858         DWORD flags = 0;
1859         PCCRL_CONTEXT crl = CertGetCRLFromStore(pSubject->hCertStore, pSubject,
1860          NULL, &flags);
1861 
1862         /* FIXME: what if the CRL has expired? */
1863         if (crl)
1864         {
1865             if (CertVerifyCRLRevocation(pSubject->dwCertEncodingType,
1866              pSubject->pCertInfo, 1, (PCRL_INFO *)&crl->pCrlInfo))
1867                 *pdwFlags &= CERT_STORE_REVOCATION_FLAG;
1868         }
1869         else
1870             *pdwFlags |= CERT_STORE_NO_CRL_FLAG;
1871     }
1872     if (*pdwFlags & CERT_STORE_TIME_VALIDITY_FLAG)
1873     {
1874         if (0 == CertVerifyTimeValidity(NULL, pSubject->pCertInfo))
1875             *pdwFlags &= ~CERT_STORE_TIME_VALIDITY_FLAG;
1876     }
1877     if (*pdwFlags & CERT_STORE_SIGNATURE_FLAG)
1878     {
1879         if (CryptVerifyCertificateSignatureEx(0, pSubject->dwCertEncodingType,
1880          CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, (void *)pSubject,
1881          CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *)pIssuer, 0, NULL))
1882             *pdwFlags &= ~CERT_STORE_SIGNATURE_FLAG;
1883     }
1884     return TRUE;
1885 }
1886 
1887 PCCERT_CONTEXT WINAPI CertGetIssuerCertificateFromStore(HCERTSTORE hCertStore,
1888  PCCERT_CONTEXT pSubjectContext, PCCERT_CONTEXT pPrevIssuerContext,
1889  DWORD *pdwFlags)
1890 {
1891     PCCERT_CONTEXT ret;
1892 
1893     TRACE("(%p, %p, %p, %08x)\n", hCertStore, pSubjectContext,
1894      pPrevIssuerContext, *pdwFlags);
1895 
1896     if (!pSubjectContext)
1897     {
1898         SetLastError(E_INVALIDARG);
1899         return NULL;
1900     }
1901 
1902     ret = CertFindCertificateInStore(hCertStore,
1903      pSubjectContext->dwCertEncodingType, 0, CERT_FIND_ISSUER_OF,
1904      pSubjectContext, pPrevIssuerContext);
1905     if (ret)
1906     {
1907         if (!CertVerifySubjectCertificateContext(pSubjectContext, ret,
1908          pdwFlags))
1909         {
1910             CertFreeCertificateContext(ret);
1911             ret = NULL;
1912         }
1913         if (CRYPT_IsCertificateSelfSigned(pSubjectContext))
1914         {
1915             CertFreeCertificateContext(ret);
1916             ret = NULL;
1917             SetLastError(CRYPT_E_SELF_SIGNED);
1918         }
1919     }
1920     TRACE("returning %p\n", ret);
1921     return ret;
1922 }
1923 
1924 typedef struct _OLD_CERT_REVOCATION_STATUS {
1925     DWORD cbSize;
1926     DWORD dwIndex;
1927     DWORD dwError;
1928     DWORD dwReason;
1929 } OLD_CERT_REVOCATION_STATUS;
1930 
1931 typedef BOOL (WINAPI *CertVerifyRevocationFunc)(DWORD, DWORD, DWORD,
1932  void **, DWORD, PCERT_REVOCATION_PARA, PCERT_REVOCATION_STATUS);
1933 
1934 BOOL WINAPI CertVerifyRevocation(DWORD dwEncodingType, DWORD dwRevType,
1935  DWORD cContext, PVOID rgpvContext[], DWORD dwFlags,
1936  PCERT_REVOCATION_PARA pRevPara, PCERT_REVOCATION_STATUS pRevStatus)
1937 {
1938     BOOL ret;
1939 
1940     TRACE("(%08x, %d, %d, %p, %08x, %p, %p)\n", dwEncodingType, dwRevType,
1941      cContext, rgpvContext, dwFlags, pRevPara, pRevStatus);
1942 
1943     if (pRevStatus->cbSize != sizeof(OLD_CERT_REVOCATION_STATUS) &&
1944      pRevStatus->cbSize != sizeof(CERT_REVOCATION_STATUS))
1945     {
1946         SetLastError(E_INVALIDARG);
1947         return FALSE;
1948     }
1949     if (cContext)
1950     {
1951         static HCRYPTOIDFUNCSET set = NULL;
1952         DWORD size;
1953 
1954         if (!set)
1955             set = CryptInitOIDFunctionSet(CRYPT_OID_VERIFY_REVOCATION_FUNC, 0);
1956         ret = CryptGetDefaultOIDDllList(set, dwEncodingType, NULL, &size);
1957         if (ret)
1958         {
1959             if (size == 1)
1960             {
1961                 /* empty list */
1962                 SetLastError(CRYPT_E_NO_REVOCATION_DLL);
1963                 ret = FALSE;
1964             }
1965             else
1966             {
1967                 LPWSTR dllList = CryptMemAlloc(size * sizeof(WCHAR)), ptr;
1968 
1969                 if (dllList)
1970                 {
1971                     ret = CryptGetDefaultOIDDllList(set, dwEncodingType,
1972                      dllList, &size);
1973                     if (ret)
1974                     {
1975                         for (ptr = dllList; ret && *ptr;
1976                          ptr += lstrlenW(ptr) + 1)
1977                         {
1978                             CertVerifyRevocationFunc func;
1979                             HCRYPTOIDFUNCADDR hFunc;
1980 
1981                             ret = CryptGetDefaultOIDFunctionAddress(set,
1982                              dwEncodingType, ptr, 0, (void **)&func, &hFunc);
1983                             if (ret)
1984                             {
1985                                 ret = func(dwEncodingType, dwRevType, cContext,
1986                                  rgpvContext, dwFlags, pRevPara, pRevStatus);
1987                                 CryptFreeOIDFunctionAddress(hFunc, 0);
1988                             }
1989                         }
1990                     }
1991                     CryptMemFree(dllList);
1992                 }
1993                 else
1994                 {
1995                     SetLastError(ERROR_OUTOFMEMORY);
1996                     ret = FALSE;
1997                 }
1998             }
1999         }
2000     }
2001     else
2002         ret = TRUE;
2003     return ret;
2004 }
2005 
2006 PCRYPT_ATTRIBUTE WINAPI CertFindAttribute(LPCSTR pszObjId, DWORD cAttr,
2007  CRYPT_ATTRIBUTE rgAttr[])
2008 {
2009     PCRYPT_ATTRIBUTE ret = NULL;
2010     DWORD i;
2011 
2012     TRACE("%s %d %p\n", debugstr_a(pszObjId), cAttr, rgAttr);
2013 
2014     if (!cAttr)
2015         return NULL;
2016     if (!pszObjId)
2017     {
2018         SetLastError(ERROR_INVALID_PARAMETER);
2019         return NULL;
2020     }
2021 
2022     for (i = 0; !ret && i < cAttr; i++)
2023         if (rgAttr[i].pszObjId && !strcmp(pszObjId, rgAttr[i].pszObjId))
2024             ret = &rgAttr[i];
2025     return ret;
2026 }
2027 
2028 PCERT_EXTENSION WINAPI CertFindExtension(LPCSTR pszObjId, DWORD cExtensions,
2029  CERT_EXTENSION rgExtensions[])
2030 {
2031     PCERT_EXTENSION ret = NULL;
2032     DWORD i;
2033 
2034     TRACE("%s %d %p\n", debugstr_a(pszObjId), cExtensions, rgExtensions);
2035 
2036     if (!cExtensions)
2037         return NULL;
2038     if (!pszObjId)
2039     {
2040         SetLastError(ERROR_INVALID_PARAMETER);
2041         return NULL;
2042     }
2043 
2044     for (i = 0; !ret && i < cExtensions; i++)
2045         if (rgExtensions[i].pszObjId && !strcmp(pszObjId,
2046          rgExtensions[i].pszObjId))
2047             ret = &rgExtensions[i];
2048     return ret;
2049 }
2050 
2051 PCERT_RDN_ATTR WINAPI CertFindRDNAttr(LPCSTR pszObjId, PCERT_NAME_INFO pName)
2052 {
2053     PCERT_RDN_ATTR ret = NULL;
2054     DWORD i, j;
2055 
2056     TRACE("%s %p\n", debugstr_a(pszObjId), pName);
2057 
2058     if (!pszObjId)
2059     {
2060         SetLastError(ERROR_INVALID_PARAMETER);
2061         return NULL;
2062     }
2063 
2064     for (i = 0; !ret && i < pName->cRDN; i++)
2065         for (j = 0; !ret && j < pName->rgRDN[i].cRDNAttr; j++)
2066             if (pName->rgRDN[i].rgRDNAttr[j].pszObjId && !strcmp(pszObjId,
2067              pName->rgRDN[i].rgRDNAttr[j].pszObjId))
2068                 ret = &pName->rgRDN[i].rgRDNAttr[j];
2069     return ret;
2070 }
2071 
2072 static BOOL find_matching_rdn_attr(DWORD dwFlags, const CERT_NAME_INFO *name,
2073  const CERT_RDN_ATTR *attr)
2074 {
2075     DWORD i, j;
2076     BOOL match = FALSE;
2077 
2078     for (i = 0; !match && i < name->cRDN; i++)
2079     {
2080         for (j = 0; j < name->rgRDN[i].cRDNAttr; j++)
2081         {
2082             if (!strcmp(name->rgRDN[i].rgRDNAttr[j].pszObjId,
2083              attr->pszObjId) &&
2084              name->rgRDN[i].rgRDNAttr[j].dwValueType ==
2085              attr->dwValueType)
2086             {
2087                 if (dwFlags & CERT_UNICODE_IS_RDN_ATTRS_FLAG)
2088                 {
2089                     LPCWSTR nameStr =
2090                      (LPCWSTR)name->rgRDN[i].rgRDNAttr[j].Value.pbData;
2091                     LPCWSTR attrStr = (LPCWSTR)attr->Value.pbData;
2092 
2093                     if (attr->Value.cbData !=
2094                      name->rgRDN[i].rgRDNAttr[j].Value.cbData)
2095                         match = FALSE;
2096                     else if (dwFlags & CERT_CASE_INSENSITIVE_IS_RDN_ATTRS_FLAG)
2097                         match = !strncmpiW(nameStr, attrStr,
2098                          attr->Value.cbData / sizeof(WCHAR));
2099                     else
2100                         match = !strncmpW(nameStr, attrStr,
2101                          attr->Value.cbData / sizeof(WCHAR));
2102                     TRACE("%s : %s => %d\n",
2103                      debugstr_wn(nameStr, attr->Value.cbData / sizeof(WCHAR)),
2104                      debugstr_wn(attrStr, attr->Value.cbData / sizeof(WCHAR)),
2105                      match);
2106                 }
2107                 else
2108                 {
2109                     LPCSTR nameStr =
2110                      (LPCSTR)name->rgRDN[i].rgRDNAttr[j].Value.pbData;
2111                     LPCSTR attrStr = (LPCSTR)attr->Value.pbData;
2112 
2113                     if (attr->Value.cbData !=
2114                      name->rgRDN[i].rgRDNAttr[j].Value.cbData)
2115                         match = FALSE;
2116                     else if (dwFlags & CERT_CASE_INSENSITIVE_IS_RDN_ATTRS_FLAG)
2117                         match = !strncasecmp(nameStr, attrStr,
2118                          attr->Value.cbData);
2119                     else
2120                         match = !strncmp(nameStr, attrStr, attr->Value.cbData);
2121                     TRACE("%s : %s => %d\n",
2122                      debugstr_an(nameStr, attr->Value.cbData),
2123                      debugstr_an(attrStr, attr->Value.cbData), match);
2124                 }
2125             }
2126         }
2127     }
2128     return match;
2129 }
2130 
2131 BOOL WINAPI CertIsRDNAttrsInCertificateName(DWORD dwCertEncodingType,
2132  DWORD dwFlags, PCERT_NAME_BLOB pCertName, PCERT_RDN pRDN)
2133 {
2134     CERT_NAME_INFO *name;
2135     LPCSTR type;
2136     DWORD size;
2137     BOOL ret;
2138 
2139     TRACE("(%08x, %08x, %p, %p)\n", dwCertEncodingType, dwFlags, pCertName,
2140      pRDN);
2141 
2142     type = dwFlags & CERT_UNICODE_IS_RDN_ATTRS_FLAG ? X509_UNICODE_NAME :
2143      X509_NAME;
2144     if ((ret = CryptDecodeObjectEx(dwCertEncodingType, type, pCertName->pbData,
2145      pCertName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &name, &size)))
2146     {
2147         DWORD i;
2148 
2149         for (i = 0; ret && i < pRDN->cRDNAttr; i++)
2150             ret = find_matching_rdn_attr(dwFlags, name, &pRDN->rgRDNAttr[i]);
2151         if (!ret)
2152             SetLastError(CRYPT_E_NO_MATCH);
2153         LocalFree(name);
2154     }
2155     return ret;
2156 }
2157 
2158 LONG WINAPI CertVerifyTimeValidity(LPFILETIME pTimeToVerify,
2159  PCERT_INFO pCertInfo)
2160 {
2161     FILETIME fileTime;
2162     LONG ret;
2163 
2164     if (!pTimeToVerify)
2165     {
2166         GetSystemTimeAsFileTime(&fileTime);
2167         pTimeToVerify = &fileTime;
2168     }
2169     if ((ret = CompareFileTime(pTimeToVerify, &pCertInfo->NotBefore)) >= 0)
2170     {
2171         ret = CompareFileTime(pTimeToVerify, &pCertInfo->NotAfter);
2172         if (ret < 0)
2173             ret = 0;
2174     }
2175     return ret;
2176 }
2177 
2178 BOOL WINAPI CertVerifyValidityNesting(PCERT_INFO pSubjectInfo,
2179  PCERT_INFO pIssuerInfo)
2180 {
2181     TRACE("(%p, %p)\n", pSubjectInfo, pIssuerInfo);
2182 
2183     return CertVerifyTimeValidity(&pSubjectInfo->NotBefore, pIssuerInfo) == 0
2184      && CertVerifyTimeValidity(&pSubjectInfo->NotAfter, pIssuerInfo) == 0;
2185 }
2186 
2187 BOOL WINAPI CryptHashCertificate(HCRYPTPROV_LEGACY hCryptProv, ALG_ID Algid,
2188  DWORD dwFlags, const BYTE *pbEncoded, DWORD cbEncoded, BYTE *pbComputedHash,
2189  DWORD *pcbComputedHash)
2190 {
2191     BOOL ret = TRUE;
2192     HCRYPTHASH hHash = 0;
2193 
2194     TRACE("(%08lx, %d, %08x, %p, %d, %p, %p)\n", hCryptProv, Algid, dwFlags,
2195      pbEncoded, cbEncoded, pbComputedHash, pcbComputedHash);
2196 
2197     if (!hCryptProv)
2198         hCryptProv = I_CryptGetDefaultCryptProv(0);
2199     if (!Algid)
2200         Algid = CALG_SHA1;
2201     if (ret)
2202     {
2203         ret = CryptCreateHash(hCryptProv, Algid, 0, 0, &hHash);
2204         if (ret)
2205         {
2206             ret = CryptHashData(hHash, pbEncoded, cbEncoded, 0);
2207             if (ret)
2208                 ret = CryptGetHashParam(hHash, HP_HASHVAL, pbComputedHash,
2209                  pcbComputedHash, 0);
2210             CryptDestroyHash(hHash);
2211         }
2212     }
2213     return ret;
2214 }
2215 
2216 BOOL WINAPI CryptHashPublicKeyInfo(HCRYPTPROV_LEGACY hCryptProv, ALG_ID Algid,
2217  DWORD dwFlags, DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pInfo,
2218  BYTE *pbComputedHash, DWORD *pcbComputedHash)
2219 {
2220     BOOL ret = TRUE;
2221     HCRYPTHASH hHash = 0;
2222 
2223     TRACE("(%08lx, %d, %08x, %d, %p, %p, %p)\n", hCryptProv, Algid, dwFlags,
2224      dwCertEncodingType, pInfo, pbComputedHash, pcbComputedHash);
2225 
2226     if (!hCryptProv)
2227         hCryptProv = I_CryptGetDefaultCryptProv(0);
2228     if (!Algid)
2229         Algid = CALG_MD5;
2230     if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING)
2231     {
2232         SetLastError(ERROR_FILE_NOT_FOUND);
2233         return FALSE;
2234     }
2235     if (ret)
2236     {
2237         BYTE *buf;
2238         DWORD size = 0;
2239 
2240         ret = CRYPT_AsnEncodePubKeyInfoNoNull(dwCertEncodingType,
2241          X509_PUBLIC_KEY_INFO, pInfo, CRYPT_ENCODE_ALLOC_FLAG, NULL,
2242          (LPBYTE)&buf, &size);
2243         if (ret)
2244         {
2245             ret = CryptCreateHash(hCryptProv, Algid, 0, 0, &hHash);
2246             if (ret)
2247             {
2248                 ret = CryptHashData(hHash, buf, size, 0);
2249                 if (ret)
2250                     ret = CryptGetHashParam(hHash, HP_HASHVAL, pbComputedHash,
2251                      pcbComputedHash, 0);
2252                 CryptDestroyHash(hHash);
2253             }
2254             LocalFree(buf);
2255         }
2256     }
2257     return ret;
2258 }
2259 
2260 BOOL WINAPI CryptHashToBeSigned(HCRYPTPROV_LEGACY hCryptProv,
2261  DWORD dwCertEncodingType, const BYTE *pbEncoded, DWORD cbEncoded,
2262  BYTE *pbComputedHash, DWORD *pcbComputedHash)
2263 {
2264     BOOL ret;
2265     CERT_SIGNED_CONTENT_INFO *info;
2266     DWORD size;
2267 
2268     TRACE("(%08lx, %08x, %p, %d, %p, %d)\n", hCryptProv, dwCertEncodingType,
2269      pbEncoded, cbEncoded, pbComputedHash, *pcbComputedHash);
2270 
2271     ret = CryptDecodeObjectEx(dwCertEncodingType, X509_CERT,
2272      pbEncoded, cbEncoded, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size);
2273     if (ret)
2274     {
2275         PCCRYPT_OID_INFO oidInfo;
2276         HCRYPTHASH hHash;
2277 
2278         if (!hCryptProv)
2279             hCryptProv = I_CryptGetDefaultCryptProv(0);
2280         oidInfo = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
2281          info->SignatureAlgorithm.pszObjId, 0);
2282         if (!oidInfo)
2283         {
2284             SetLastError(NTE_BAD_ALGID);
2285             ret = FALSE;
2286         }
2287         else
2288         {
2289             ret = CryptCreateHash(hCryptProv, oidInfo->u.Algid, 0, 0, &hHash);
2290             if (ret)
2291             {
2292                 ret = CryptHashData(hHash, info->ToBeSigned.pbData,
2293                  info->ToBeSigned.cbData, 0);
2294                 if (ret)
2295                     ret = CryptGetHashParam(hHash, HP_HASHVAL, pbComputedHash,
2296                      pcbComputedHash, 0);
2297                 CryptDestroyHash(hHash);
2298             }
2299         }
2300         LocalFree(info);
2301     }
2302     return ret;
2303 }
2304 
2305 BOOL WINAPI CryptSignCertificate(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProv,
2306  DWORD dwKeySpec, DWORD dwCertEncodingType, const BYTE *pbEncodedToBeSigned,
2307  DWORD cbEncodedToBeSigned, PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm,
2308  const void *pvHashAuxInfo, BYTE *pbSignature, DWORD *pcbSignature)
2309 {
2310     BOOL ret;
2311     PCCRYPT_OID_INFO info;
2312     HCRYPTHASH hHash;
2313 
2314     TRACE("(%08lx, %d, %d, %p, %d, %p, %p, %p, %p)\n", hCryptProv,
2315      dwKeySpec, dwCertEncodingType, pbEncodedToBeSigned, cbEncodedToBeSigned,
2316      pSignatureAlgorithm, pvHashAuxInfo, pbSignature, pcbSignature);
2317 
2318     info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY,
2319      pSignatureAlgorithm->pszObjId, 0);
2320     if (!info)
2321     {
2322         SetLastError(NTE_BAD_ALGID);
2323         return FALSE;
2324     }
2325     if (info->dwGroupId == CRYPT_HASH_ALG_OID_GROUP_ID)
2326     {
2327         if (!hCryptProv)
2328             hCryptProv = I_CryptGetDefaultCryptProv(0);
2329         ret = CryptCreateHash(hCryptProv, info->u.Algid, 0, 0, &hHash);
2330         if (ret)
2331         {
2332             ret = CryptHashData(hHash, pbEncodedToBeSigned,
2333              cbEncodedToBeSigned, 0);
2334             if (ret)
2335                 ret = CryptGetHashParam(hHash, HP_HASHVAL, pbSignature,
2336                  pcbSignature, 0);
2337             CryptDestroyHash(hHash);
2338         }
2339     }
2340     else
2341     {
2342         if (!hCryptProv)
2343         {
2344             SetLastError(ERROR_INVALID_PARAMETER);
2345             ret = FALSE;
2346         }
2347         else
2348         {
2349             ret = CryptCreateHash(hCryptProv, info->u.Algid, 0, 0, &hHash);
2350             if (ret)
2351             {
2352                 ret = CryptHashData(hHash, pbEncodedToBeSigned,
2353                  cbEncodedToBeSigned, 0);
2354                 if (ret)
2355                     ret = CryptSignHashW(hHash, dwKeySpec, NULL, 0, pbSignature,
2356                      pcbSignature);
2357                 CryptDestroyHash(hHash);
2358             }
2359         }
2360     }
2361     return ret;
2362 }
2363 
2364 BOOL WINAPI CryptSignAndEncodeCertificate(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hCryptProv,
2365  DWORD dwKeySpec, DWORD dwCertEncodingType, LPCSTR lpszStructType,
2366  const void *pvStructInfo, PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm,
2367  const void *pvHashAuxInfo, BYTE *pbEncoded, DWORD *pcbEncoded)
2368 {
2369     BOOL ret;
2370     DWORD encodedSize, hashSize;
2371 
2372     TRACE("(%08lx, %d, %d, %s, %p, %p, %p, %p, %p)\n", hCryptProv, dwKeySpec,
2373      dwCertEncodingType, debugstr_a(lpszStructType), pvStructInfo,
2374      pSignatureAlgorithm, pvHashAuxInfo, pbEncoded, pcbEncoded);
2375 
2376     ret = CryptEncodeObject(dwCertEncodingType, lpszStructType, pvStructInfo,
2377      NULL, &encodedSize);
2378     if (ret)
2379     {
2380         PBYTE encoded = CryptMemAlloc(encodedSize);
2381 
2382         if (encoded)
2383         {
2384             ret = CryptEncodeObject(dwCertEncodingType, lpszStructType,
2385              pvStructInfo, encoded, &encodedSize);
2386             if (ret)
2387             {
2388                 ret = CryptSignCertificate(hCryptProv, dwKeySpec,
2389                  dwCertEncodingType, encoded, encodedSize, pSignatureAlgorithm,
2390                  pvHashAuxInfo, NULL, &hashSize);
2391                 if (ret)
2392                 {
2393                     PBYTE hash = CryptMemAlloc(hashSize);
2394 
2395                     if (hash)
2396                     {
2397                         ret = CryptSignCertificate(hCryptProv, dwKeySpec,
2398                          dwCertEncodingType, encoded, encodedSize,
2399                          pSignatureAlgorithm, pvHashAuxInfo, hash, &hashSize);
2400                         if (ret)
2401                         {
2402                             CERT_SIGNED_CONTENT_INFO info = { { 0 } };
2403 
2404                             info.ToBeSigned.cbData = encodedSize;
2405                             info.ToBeSigned.pbData = encoded;
2406                             info.SignatureAlgorithm = *pSignatureAlgorithm;
2407                             info.Signature.cbData = hashSize;
2408                             info.Signature.pbData = hash;
2409                             info.Signature.cUnusedBits = 0;
2410                             ret = CryptEncodeObject(dwCertEncodingType,
2411                              X509_CERT, &info, pbEncoded, pcbEncoded);
2412                         }
2413                         CryptMemFree(hash);
2414                     }
2415                     else
2416                         ret = FALSE;
2417                 }
2418             }
2419             CryptMemFree(encoded);
2420         }
2421         else
2422             ret = FALSE;
2423     }
2424     return ret;
2425 }
2426 
2427 BOOL WINAPI CryptVerifyCertificateSignature(HCRYPTPROV_LEGACY hCryptProv,
2428  DWORD dwCertEncodingType, const BYTE *pbEncoded, DWORD cbEncoded,
2429  PCERT_PUBLIC_KEY_INFO pPublicKey)
2430 {
2431     CRYPT_DATA_BLOB blob = { cbEncoded, (BYTE *)pbEncoded };
2432 
2433     return CryptVerifyCertificateSignatureEx(hCryptProv, dwCertEncodingType,
2434      CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB, &blob,
2435      CRYPT_VERIFY_CERT_SIGN_ISSUER_PUBKEY, pPublicKey, 0, NULL);
2436 }
2437 
2438 static BOOL CRYPT_VerifySignature(HCRYPTPROV_LEGACY hCryptProv, DWORD dwCertEncodingType,
2439     CERT_PUBLIC_KEY_INFO *pubKeyInfo, const CERT_SIGNED_CONTENT_INFO *signedCert, const CRYPT_OID_INFO *info)
2440 {
2441     BOOL ret;
2442     HCRYPTKEY key;
2443     ALG_ID pubKeyID, hashID;
2444 
2445     hashID = info->u.Algid;
2446     if (info->ExtraInfo.cbData >= sizeof(ALG_ID))
2447         pubKeyID = *(ALG_ID *)info->ExtraInfo.pbData;
2448     else
2449         pubKeyID = hashID;
2450     /* Load the default provider if necessary */
2451     if (!hCryptProv)
2452         hCryptProv = I_CryptGetDefaultCryptProv(0);
2453     ret = CryptImportPublicKeyInfoEx(hCryptProv, dwCertEncodingType,
2454      pubKeyInfo, pubKeyID, 0, NULL, &key);
2455     if (ret)
2456     {
2457         HCRYPTHASH hash;
2458 
2459         ret = CryptCreateHash(hCryptProv, hashID, 0, 0, &hash);
2460         if (ret)
2461         {
2462             ret = CryptHashData(hash, signedCert->ToBeSigned.pbData,
2463              signedCert->ToBeSigned.cbData, 0);
2464             if (ret)
2465                 ret = CryptVerifySignatureW(hash, signedCert->Signature.pbData,
2466                  signedCert->Signature.cbData, key, NULL, 0);
2467             CryptDestroyHash(hash);
2468         }
2469         CryptDestroyKey(key);
2470     }
2471     return ret;
2472 }
2473 
2474 static BOOL CNG_CalcHash(const WCHAR *algorithm, const CERT_SIGNED_CONTENT_INFO *signedCert,
2475         BYTE **hash_value, DWORD *hash_len)
2476 {
2477     BCRYPT_HASH_HANDLE hash = NULL;
2478     BCRYPT_ALG_HANDLE alg = NULL;
2479     NTSTATUS status;
2480     DWORD size;
2481 
2482     if ((status = BCryptOpenAlgorithmProvider(&alg, algorithm, NULL, 0)))
2483         goto done;
2484 
2485     if ((status = BCryptCreateHash(alg, &hash, NULL, 0, NULL, 0, 0)))
2486         goto done;
2487 
2488     if ((status = BCryptHashData(hash, signedCert->ToBeSigned.pbData, signedCert->ToBeSigned.cbData, 0)))
2489         goto done;
2490 
2491     if ((status = BCryptGetProperty(hash, BCRYPT_HASH_LENGTH, (BYTE *)hash_len, sizeof(*hash_len), &size, 0)))
2492         goto done;
2493 
2494     if (!(*hash_value = CryptMemAlloc(*hash_len)))
2495     {
2496         status = STATUS_NO_MEMORY;
2497         goto done;
2498     }
2499 
2500     if ((status = BCryptFinishHash(hash, *hash_value, *hash_len, 0)))
2501     {
2502         CryptMemFree(*hash_value);
2503         goto done;
2504     }
2505 
2506 done:
2507     if (hash) BCryptDestroyHash(hash);
2508     if (alg)  BCryptCloseAlgorithmProvider(alg, 0);
2509     if (status) SetLastError(RtlNtStatusToDosError(status));
2510     return status == 0;
2511 }
2512 
2513 static BOOL CNG_ImportECCPubKey(CERT_PUBLIC_KEY_INFO *pubKeyInfo, BCRYPT_KEY_HANDLE *key)
2514 {
2515     DWORD blob_magic, ecckey_len, size;
2516     BCRYPT_ALG_HANDLE alg = NULL;
2517     BCRYPT_ECCKEY_BLOB *ecckey;
2518     const WCHAR *sign_algo;
2519     char **ecc_curve;
2520     NTSTATUS status;
2521 
2522     if (!pubKeyInfo->PublicKey.cbData)
2523     {
2524         SetLastError(NTE_BAD_ALGID);
2525         return FALSE;
2526     }
2527 
2528     if (pubKeyInfo->PublicKey.pbData[0] != 0x4)
2529     {
2530         FIXME("Compressed ECC curves (%02x) not yet supported\n", pubKeyInfo->PublicKey.pbData[0]);
2531         SetLastError(NTE_BAD_ALGID);
2532         return FALSE;
2533     }
2534 
2535     if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_OBJECT_IDENTIFIER, pubKeyInfo->Algorithm.Parameters.pbData,
2536             pubKeyInfo->Algorithm.Parameters.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &ecc_curve, &size))
2537         return FALSE;
2538 
2539     if (!strcmp(*ecc_curve, szOID_ECC_CURVE_P256))
2540     {
2541         sign_algo = BCRYPT_ECDSA_P256_ALGORITHM;
2542         blob_magic = BCRYPT_ECDSA_PUBLIC_P256_MAGIC;
2543     }
2544     else if (!strcmp(*ecc_curve, szOID_ECC_CURVE_P384))
2545     {
2546         sign_algo = BCRYPT_ECDSA_P384_ALGORITHM;
2547         blob_magic = BCRYPT_ECDSA_PUBLIC_P384_MAGIC;
2548     }
2549     else
2550     {
2551         FIXME("Unsupported ecc curve type: %s\n", *ecc_curve);
2552         sign_algo = NULL;
2553         blob_magic = 0;
2554     }
2555     LocalFree(ecc_curve);
2556 
2557     if (!sign_algo)
2558     {
2559         SetLastError(NTE_BAD_ALGID);
2560         return FALSE;
2561     }
2562 
2563     if ((status = BCryptOpenAlgorithmProvider(&alg, sign_algo, NULL, 0)))
2564         goto done;
2565 
2566     ecckey_len = sizeof(BCRYPT_ECCKEY_BLOB) + pubKeyInfo->PublicKey.cbData - 1;
2567     if (!(ecckey = CryptMemAlloc(ecckey_len)))
2568     {
2569         status = STATUS_NO_MEMORY;
2570         goto done;
2571     }
2572 
2573     ecckey->dwMagic = blob_magic;
2574     ecckey->cbKey = (pubKeyInfo->PublicKey.cbData - 1) / 2;
2575     memcpy(ecckey + 1, pubKeyInfo->PublicKey.pbData + 1, pubKeyInfo->PublicKey.cbData - 1);
2576 
2577     status = BCryptImportKeyPair(alg, NULL, BCRYPT_ECCPUBLIC_BLOB, key, (BYTE*)ecckey, ecckey_len, 0);
2578     CryptMemFree(ecckey);
2579 
2580 done:
2581     if (alg) BCryptCloseAlgorithmProvider(alg, 0);
2582     if (status) SetLastError(RtlNtStatusToDosError(status));
2583     return !status;
2584 }
2585 
2586 static BOOL CNG_ImportPubKey(CERT_PUBLIC_KEY_INFO *pubKeyInfo, BCRYPT_KEY_HANDLE *key)
2587 {
2588     if (!strcmp(pubKeyInfo->Algorithm.pszObjId, szOID_ECC_PUBLIC_KEY))
2589         return CNG_ImportECCPubKey(pubKeyInfo, key);
2590 
2591     FIXME("Unsupported public key type: %s\n", debugstr_a(pubKeyInfo->Algorithm.pszObjId));
2592     SetLastError(NTE_BAD_ALGID);
2593     return FALSE;
2594 }
2595 
2596 static BOOL CNG_PrepareSignatureECC(BYTE *encoded_sig, DWORD encoded_size, BYTE **sig_value, DWORD *sig_len)
2597 {
2598     CERT_ECC_SIGNATURE *ecc_sig;
2599     DWORD size;
2600     int i;
2601 
2602     if (!CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ECC_SIGNATURE, encoded_sig, encoded_size,
2603             CRYPT_DECODE_ALLOC_FLAG, NULL, &ecc_sig, &size))
2604         return FALSE;
2605 
2606     if (!ecc_sig->r.cbData || !ecc_sig->s.cbData)
2607     {
2608         LocalFree(ecc_sig);
2609         SetLastError(ERROR_INVALID_DATA);
2610         return FALSE;
2611     }
2612 
2613     *sig_len = ecc_sig->r.cbData + ecc_sig->s.cbData;
2614     if (!(*sig_value = CryptMemAlloc(*sig_len)))
2615     {
2616         LocalFree(ecc_sig);
2617         SetLastError(ERROR_OUTOFMEMORY);
2618         return FALSE;
2619     }
2620 
2621     for (i = 0; i < ecc_sig->r.cbData; i++)
2622         (*sig_value)[i] = ecc_sig->r.pbData[ecc_sig->r.cbData - i - 1];
2623     for (i = 0; i < ecc_sig->s.cbData; i++)
2624         (*sig_value)[ecc_sig->r.cbData + i] = ecc_sig->s.pbData[ecc_sig->s.cbData - i - 1];
2625 
2626     LocalFree(ecc_sig);
2627     return TRUE;
2628 }
2629 
2630 static BOOL CNG_PrepareSignature(CERT_PUBLIC_KEY_INFO *pubKeyInfo, const CERT_SIGNED_CONTENT_INFO *signedCert,
2631     BYTE **sig_value, DWORD *sig_len)
2632 {
2633     BYTE *encoded_sig;
2634     BOOL ret = FALSE;
2635     int i;
2636 
2637     if (!signedCert->Signature.cbData)
2638     {
2639         SetLastError(ERROR_INVALID_DATA);
2640         return FALSE;
2641     }
2642 
2643     if (!(encoded_sig = CryptMemAlloc(signedCert->Signature.cbData)))
2644     {
2645         SetLastError(ERROR_OUTOFMEMORY);
2646         return FALSE;
2647     }
2648 
2649     for (i = 0; i < signedCert->Signature.cbData; i++)
2650         encoded_sig[i] = signedCert->Signature.pbData[signedCert->Signature.cbData - i - 1];
2651 
2652     if (!strcmp(pubKeyInfo->Algorithm.pszObjId, szOID_ECC_PUBLIC_KEY))
2653         ret = CNG_PrepareSignatureECC(encoded_sig, signedCert->Signature.cbData, sig_value, sig_len);
2654     else
2655     {
2656         FIXME("Unsupported public key type: %s\n", debugstr_a(pubKeyInfo->Algorithm.pszObjId));
2657         SetLastError(NTE_BAD_ALGID);
2658     }
2659 
2660     CryptMemFree(encoded_sig);
2661     return ret;
2662 }
2663 
2664 static BOOL CNG_VerifySignature(HCRYPTPROV_LEGACY hCryptProv, DWORD dwCertEncodingType,
2665     CERT_PUBLIC_KEY_INFO *pubKeyInfo, const CERT_SIGNED_CONTENT_INFO *signedCert, const CRYPT_OID_INFO *info)
2666 {
2667     BCRYPT_KEY_HANDLE key = NULL;
2668     BYTE *hash_value = NULL, *sig_value;
2669     DWORD hash_len, sig_len;
2670     NTSTATUS status;
2671     BOOL ret;
2672 
2673     ret = CNG_ImportPubKey(pubKeyInfo, &key);
2674     if (ret)
2675     {
2676         ret = CNG_CalcHash(info->pwszCNGAlgid, signedCert, &hash_value, &hash_len);
2677         if (ret)
2678         {
2679             ret = CNG_PrepareSignature(pubKeyInfo, signedCert, &sig_value, &sig_len);
2680             if (ret)
2681             {
2682                 status = BCryptVerifySignature(key, NULL, hash_value, hash_len, sig_value, sig_len, 0);
2683                 if (status)
2684                 {
2685                     FIXME("Failed to verify signature: %08x\n", status);
2686                     SetLastError(RtlNtStatusToDosError(status));
2687                     ret = FALSE;
2688                 }
2689                 CryptMemFree(sig_value);
2690             }
2691             CryptMemFree(hash_value);
2692         }
2693         BCryptDestroyKey(key);
2694     }
2695 
2696     return ret;
2697 }
2698 
2699 static BOOL CRYPT_VerifyCertSignatureFromPublicKeyInfo(HCRYPTPROV_LEGACY hCryptProv, DWORD dwCertEncodingType,
2700     CERT_PUBLIC_KEY_INFO *pubKeyInfo, const CERT_SIGNED_CONTENT_INFO *signedCert)
2701 {
2702     CCRYPT_OID_INFO *info;
2703 
2704     info = CryptFindOIDInfo(CRYPT_OID_INFO_OID_KEY, signedCert->SignatureAlgorithm.pszObjId, 0);
2705     if (!info || info->dwGroupId != CRYPT_SIGN_ALG_OID_GROUP_ID)
2706     {
2707         SetLastError(NTE_BAD_ALGID);
2708         return FALSE;
2709     }
2710 
2711     if (info->u.Algid == CALG_OID_INFO_CNG_ONLY)
2712         return CNG_VerifySignature(hCryptProv, dwCertEncodingType, pubKeyInfo, signedCert, info);
2713     else
2714         return CRYPT_VerifySignature(hCryptProv, dwCertEncodingType, pubKeyInfo, signedCert, info);
2715 }
2716 
2717 BOOL WINAPI CryptVerifyCertificateSignatureEx(HCRYPTPROV_LEGACY hCryptProv,
2718  DWORD dwCertEncodingType, DWORD dwSubjectType, void *pvSubject,
2719  DWORD dwIssuerType, void *pvIssuer, DWORD dwFlags, void *pvReserved)
2720 {
2721     BOOL ret = TRUE;
2722     CRYPT_DATA_BLOB subjectBlob;
2723 
2724     TRACE("(%08lx, %d, %d, %p, %d, %p, %08x, %p)\n", hCryptProv,
2725      dwCertEncodingType, dwSubjectType, pvSubject, dwIssuerType, pvIssuer,
2726      dwFlags, pvReserved);
2727 
2728     switch (dwSubjectType)
2729     {
2730     case CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB:
2731     {
2732         PCRYPT_DATA_BLOB blob = pvSubject;
2733 
2734         subjectBlob.pbData = blob->pbData;
2735         subjectBlob.cbData = blob->cbData;
2736         break;
2737     }
2738     case CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT:
2739     {
2740         PCERT_CONTEXT context = pvSubject;
2741 
2742         subjectBlob.pbData = context->pbCertEncoded;
2743         subjectBlob.cbData = context->cbCertEncoded;
2744         break;
2745     }
2746     case CRYPT_VERIFY_CERT_SIGN_SUBJECT_CRL:
2747     {
2748         PCRL_CONTEXT context = pvSubject;
2749 
2750         subjectBlob.pbData = context->pbCrlEncoded;
2751         subjectBlob.cbData = context->cbCrlEncoded;
2752         break;
2753     }
2754     default:
2755         SetLastError(E_INVALIDARG);
2756         ret = FALSE;
2757     }
2758 
2759     if (ret)
2760     {
2761         PCERT_SIGNED_CONTENT_INFO signedCert = NULL;
2762         DWORD size = 0;
2763 
2764         ret = CryptDecodeObjectEx(dwCertEncodingType, X509_CERT,
2765          subjectBlob.pbData, subjectBlob.cbData,
2766          CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
2767          &signedCert, &size);
2768         if (ret)
2769         {
2770             switch (dwIssuerType)
2771             {
2772             case CRYPT_VERIFY_CERT_SIGN_ISSUER_PUBKEY:
2773                 ret = CRYPT_VerifyCertSignatureFromPublicKeyInfo(hCryptProv,
2774                  dwCertEncodingType, pvIssuer,
2775                  signedCert);
2776                 break;
2777             case CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT:
2778                 ret = CRYPT_VerifyCertSignatureFromPublicKeyInfo(hCryptProv,
2779                  dwCertEncodingType,
2780                  &((PCCERT_CONTEXT)pvIssuer)->pCertInfo->SubjectPublicKeyInfo,
2781                  signedCert);
2782                 break;
2783             case CRYPT_VERIFY_CERT_SIGN_ISSUER_CHAIN:
2784                 FIXME("CRYPT_VERIFY_CERT_SIGN_ISSUER_CHAIN: stub\n");
2785                 ret = FALSE;
2786                 break;
2787             case CRYPT_VERIFY_CERT_SIGN_ISSUER_NULL:
2788                 if (pvIssuer)
2789                 {
2790                     SetLastError(E_INVALIDARG);
2791                     ret = FALSE;
2792                 }
2793                 else
2794                 {
2795                     FIXME("unimplemented for NULL signer\n");
2796                     SetLastError(E_INVALIDARG);
2797                     ret = FALSE;
2798                 }
2799                 break;
2800             default:
2801                 SetLastError(E_INVALIDARG);
2802                 ret = FALSE;
2803             }
2804             LocalFree(signedCert);
2805         }
2806     }
2807     return ret;
2808 }
2809 
2810 BOOL WINAPI CertGetIntendedKeyUsage(DWORD dwCertEncodingType,
2811  PCERT_INFO pCertInfo, BYTE *pbKeyUsage, DWORD cbKeyUsage)
2812 {
2813     PCERT_EXTENSION ext;
2814     BOOL ret = FALSE;
2815 
2816     TRACE("(%08x, %p, %p, %d)\n", dwCertEncodingType, pCertInfo, pbKeyUsage,
2817      cbKeyUsage);
2818 
2819     ext = CertFindExtension(szOID_KEY_USAGE, pCertInfo->cExtension,
2820      pCertInfo->rgExtension);
2821     if (ext)
2822     {
2823         CRYPT_BIT_BLOB usage;
2824         DWORD size = sizeof(usage);
2825 
2826         ret = CryptDecodeObjectEx(dwCertEncodingType, X509_BITS,
2827          ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_NOCOPY_FLAG, NULL,
2828          &usage, &size);
2829         if (ret)
2830         {
2831             if (cbKeyUsage < usage.cbData)
2832                 ret = FALSE;
2833             else
2834             {
2835                 memcpy(pbKeyUsage, usage.pbData, usage.cbData);
2836                 if (cbKeyUsage > usage.cbData)
2837                     memset(pbKeyUsage + usage.cbData, 0,
2838                      cbKeyUsage - usage.cbData);
2839             }
2840         }
2841     }
2842     else
2843         SetLastError(0);
2844     return ret;
2845 }
2846 
2847 BOOL WINAPI CertGetEnhancedKeyUsage(PCCERT_CONTEXT pCertContext, DWORD dwFlags,
2848  PCERT_ENHKEY_USAGE pUsage, DWORD *pcbUsage)
2849 {
2850     PCERT_ENHKEY_USAGE usage = NULL;
2851     DWORD bytesNeeded;
2852     BOOL ret = TRUE;
2853 
2854     if (!pCertContext || !pcbUsage)
2855     {
2856         SetLastError(ERROR_INVALID_PARAMETER);
2857         return FALSE;
2858     }
2859 
2860     TRACE("(%p, %08x, %p, %d)\n", pCertContext, dwFlags, pUsage, *pcbUsage);
2861 
2862     if (!(dwFlags & CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG))
2863     {
2864         DWORD propSize = 0;
2865 
2866         if (CertGetCertificateContextProperty(pCertContext,
2867          CERT_ENHKEY_USAGE_PROP_ID, NULL, &propSize))
2868         {
2869             LPBYTE buf = CryptMemAlloc(propSize);
2870 
2871             if (buf)
2872             {
2873                 if (CertGetCertificateContextProperty(pCertContext,
2874                  CERT_ENHKEY_USAGE_PROP_ID, buf, &propSize))
2875                 {
2876                     ret = CryptDecodeObjectEx(pCertContext->dwCertEncodingType,
2877                      X509_ENHANCED_KEY_USAGE, buf, propSize,
2878                      CRYPT_ENCODE_ALLOC_FLAG, NULL, &usage, &bytesNeeded);
2879                 }
2880                 CryptMemFree(buf);
2881             }
2882         }
2883     }
2884     if (!usage && !(dwFlags & CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG))
2885     {
2886         PCERT_EXTENSION ext = CertFindExtension(szOID_ENHANCED_KEY_USAGE,
2887          pCertContext->pCertInfo->cExtension,
2888          pCertContext->pCertInfo->rgExtension);
2889 
2890         if (ext)
2891         {
2892             ret = CryptDecodeObjectEx(pCertContext->dwCertEncodingType,
2893              X509_ENHANCED_KEY_USAGE, ext->Value.pbData, ext->Value.cbData,
2894              CRYPT_ENCODE_ALLOC_FLAG, NULL, &usage, &bytesNeeded);
2895         }
2896     }
2897     if (!usage)
2898     {
2899         /* If a particular location is specified, this should fail.  Otherwise
2900          * it should succeed with an empty usage.  (This is true on Win2k and
2901          * later, which we emulate.)
2902          */
2903         if (dwFlags)
2904         {
2905             SetLastError(CRYPT_E_NOT_FOUND);
2906             ret = FALSE;
2907         }
2908         else
2909             bytesNeeded = sizeof(CERT_ENHKEY_USAGE);
2910     }
2911 
2912     if (ret)
2913     {
2914         if (!pUsage)
2915             *pcbUsage = bytesNeeded;
2916         else if (*pcbUsage < bytesNeeded)
2917         {
2918             SetLastError(ERROR_MORE_DATA);
2919             *pcbUsage = bytesNeeded;
2920             ret = FALSE;
2921         }
2922         else
2923         {
2924             *pcbUsage = bytesNeeded;
2925             if (usage)
2926             {
2927                 DWORD i;
2928                 LPSTR nextOID = (LPSTR)((LPBYTE)pUsage +
2929                  sizeof(CERT_ENHKEY_USAGE) +
2930                  usage->cUsageIdentifier * sizeof(LPSTR));
2931 
2932                 pUsage->cUsageIdentifier = usage->cUsageIdentifier;
2933                 pUsage->rgpszUsageIdentifier = (LPSTR *)((LPBYTE)pUsage +
2934                  sizeof(CERT_ENHKEY_USAGE));
2935                 for (i = 0; i < usage->cUsageIdentifier; i++)
2936                 {
2937                     pUsage->rgpszUsageIdentifier[i] = nextOID;
2938                     strcpy(nextOID, usage->rgpszUsageIdentifier[i]);
2939                     nextOID += strlen(nextOID) + 1;
2940                 }
2941             }
2942             else
2943                 pUsage->cUsageIdentifier = 0;
2944         }
2945     }
2946     if (usage)
2947         LocalFree(usage);
2948     TRACE("returning %d\n", ret);
2949     return ret;
2950 }
2951 
2952 BOOL WINAPI CertSetEnhancedKeyUsage(PCCERT_CONTEXT pCertContext,
2953  PCERT_ENHKEY_USAGE pUsage)
2954 {
2955     BOOL ret;
2956 
2957     TRACE("(%p, %p)\n", pCertContext, pUsage);
2958 
2959     if (pUsage)
2960     {
2961         CRYPT_DATA_BLOB blob = { 0, NULL };
2962 
2963         ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE,
2964          pUsage, CRYPT_ENCODE_ALLOC_FLAG, NULL, &blob.pbData, &blob.cbData);
2965         if (ret)
2966         {
2967             ret = CertSetCertificateContextProperty(pCertContext,
2968              CERT_ENHKEY_USAGE_PROP_ID, 0, &blob);
2969             LocalFree(blob.pbData);
2970         }
2971     }
2972     else
2973         ret = CertSetCertificateContextProperty(pCertContext,
2974          CERT_ENHKEY_USAGE_PROP_ID, 0, NULL);
2975     return ret;
2976 }
2977 
2978 BOOL WINAPI CertAddEnhancedKeyUsageIdentifier(PCCERT_CONTEXT pCertContext,
2979  LPCSTR pszUsageIdentifier)
2980 {
2981     BOOL ret;
2982     DWORD size;
2983 
2984     TRACE("(%p, %s)\n", pCertContext, debugstr_a(pszUsageIdentifier));
2985 
2986     if (CertGetEnhancedKeyUsage(pCertContext,
2987      CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, NULL, &size))
2988     {
2989         PCERT_ENHKEY_USAGE usage = CryptMemAlloc(size);
2990 
2991         if (usage)
2992         {
2993             ret = CertGetEnhancedKeyUsage(pCertContext,
2994              CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, usage, &size);
2995             if (ret)
2996             {
2997                 DWORD i;
2998                 BOOL exists = FALSE;
2999 
3000                 /* Make sure usage doesn't already exist */
3001                 for (i = 0; !exists && i < usage->cUsageIdentifier; i++)
3002                 {
3003                     if (!strcmp(usage->rgpszUsageIdentifier[i],
3004                      pszUsageIdentifier))
3005                         exists = TRUE;
3006                 }
3007                 if (!exists)
3008                 {
3009                     PCERT_ENHKEY_USAGE newUsage = CryptMemAlloc(size +
3010                      sizeof(LPSTR) + strlen(pszUsageIdentifier) + 1);
3011 
3012                     if (newUsage)
3013                     {
3014                         LPSTR nextOID;
3015 
3016                         newUsage->rgpszUsageIdentifier = (LPSTR *)
3017                          ((LPBYTE)newUsage + sizeof(CERT_ENHKEY_USAGE));
3018                         nextOID = (LPSTR)((LPBYTE)newUsage->rgpszUsageIdentifier
3019                           + (usage->cUsageIdentifier + 1) * sizeof(LPSTR));
3020                         for (i = 0; i < usage->cUsageIdentifier; i++)
3021                         {
3022                             newUsage->rgpszUsageIdentifier[i] = nextOID;
3023                             strcpy(nextOID, usage->rgpszUsageIdentifier[i]);
3024                             nextOID += strlen(nextOID) + 1;
3025                         }
3026                         newUsage->rgpszUsageIdentifier[i] = nextOID;
3027                         strcpy(nextOID, pszUsageIdentifier);
3028                         newUsage->cUsageIdentifier = i + 1;
3029                         ret = CertSetEnhancedKeyUsage(pCertContext, newUsage);
3030                         CryptMemFree(newUsage);
3031                     }
3032                     else
3033                         ret = FALSE;
3034                 }
3035             }
3036             CryptMemFree(usage);
3037         }
3038         else
3039             ret = FALSE;
3040     }
3041     else
3042     {
3043         PCERT_ENHKEY_USAGE usage = CryptMemAlloc(sizeof(CERT_ENHKEY_USAGE) +
3044          sizeof(LPSTR) + strlen(pszUsageIdentifier) + 1);
3045 
3046         if (usage)
3047         {
3048             usage->rgpszUsageIdentifier =
3049              (LPSTR *)((LPBYTE)usage + sizeof(CERT_ENHKEY_USAGE));
3050             usage->rgpszUsageIdentifier[0] = (LPSTR)((LPBYTE)usage +
3051              sizeof(CERT_ENHKEY_USAGE) + sizeof(LPSTR));
3052             strcpy(usage->rgpszUsageIdentifier[0], pszUsageIdentifier);
3053             usage->cUsageIdentifier = 1;
3054             ret = CertSetEnhancedKeyUsage(pCertContext, usage);
3055             CryptMemFree(usage);
3056         }
3057         else
3058             ret = FALSE;
3059     }
3060     return ret;
3061 }
3062 
3063 BOOL WINAPI CertRemoveEnhancedKeyUsageIdentifier(PCCERT_CONTEXT pCertContext,
3064  LPCSTR pszUsageIdentifier)
3065 {
3066     BOOL ret;
3067     DWORD size;
3068     CERT_ENHKEY_USAGE usage;
3069 
3070     TRACE("(%p, %s)\n", pCertContext, debugstr_a(pszUsageIdentifier));
3071 
3072     size = sizeof(usage);
3073     ret = CertGetEnhancedKeyUsage(pCertContext,
3074      CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, &usage, &size);
3075     if (!ret && GetLastError() == ERROR_MORE_DATA)
3076     {
3077         PCERT_ENHKEY_USAGE pUsage = CryptMemAlloc(size);
3078 
3079         if (pUsage)
3080         {
3081             ret = CertGetEnhancedKeyUsage(pCertContext,
3082              CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, pUsage, &size);
3083             if (ret)
3084             {
3085                 if (pUsage->cUsageIdentifier)
3086                 {
3087                     DWORD i;
3088                     BOOL found = FALSE;
3089 
3090                     for (i = 0; i < pUsage->cUsageIdentifier; i++)
3091                     {
3092                         if (!strcmp(pUsage->rgpszUsageIdentifier[i],
3093                          pszUsageIdentifier))
3094                             found = TRUE;
3095                         if (found && i < pUsage->cUsageIdentifier - 1)
3096                             pUsage->rgpszUsageIdentifier[i] =
3097                              pUsage->rgpszUsageIdentifier[i + 1];
3098                     }
3099                     pUsage->cUsageIdentifier--;
3100                     /* Remove the usage if it's empty */
3101                     if (pUsage->cUsageIdentifier)
3102                         ret = CertSetEnhancedKeyUsage(pCertContext, pUsage);
3103                     else
3104                         ret = CertSetEnhancedKeyUsage(pCertContext, NULL);
3105                 }
3106             }
3107             CryptMemFree(pUsage);
3108         }
3109         else
3110             ret = FALSE;
3111     }
3112     else
3113     {
3114         /* it fit in an empty usage, therefore there's nothing to remove */
3115         ret = TRUE;
3116     }
3117     return ret;
3118 }
3119 
3120 struct BitField
3121 {
3122     DWORD  cIndexes;
3123     DWORD *indexes;
3124 };
3125 
3126 #define BITS_PER_DWORD (sizeof(DWORD) * 8)
3127 
3128 static void CRYPT_SetBitInField(struct BitField *field, DWORD bit)
3129 {
3130     DWORD indexIndex = bit / BITS_PER_DWORD;
3131 
3132     if (indexIndex + 1 > field->cIndexes)
3133     {
3134         if (field->cIndexes)
3135             field->indexes = CryptMemRealloc(field->indexes,
3136              (indexIndex + 1) * sizeof(DWORD));
3137         else
3138             field->indexes = CryptMemAlloc(sizeof(DWORD));
3139         if (field->indexes)
3140         {
3141             field->indexes[indexIndex] = 0;
3142             field->cIndexes = indexIndex + 1;
3143         }
3144     }
3145     if (field->indexes)
3146         field->indexes[indexIndex] |= 1 << (bit % BITS_PER_DWORD);
3147 }
3148 
3149 static BOOL CRYPT_IsBitInFieldSet(const struct BitField *field, DWORD bit)
3150 {
3151     BOOL set;
3152     DWORD indexIndex = bit / BITS_PER_DWORD;
3153 
3154     assert(field->cIndexes);
3155     set = field->indexes[indexIndex] & (1 << (bit % BITS_PER_DWORD));
3156     return set;
3157 }
3158 
3159 BOOL WINAPI CertGetValidUsages(DWORD cCerts, PCCERT_CONTEXT *rghCerts,
3160  int *cNumOIDs, LPSTR *rghOIDs, DWORD *pcbOIDs)
3161 {
3162     BOOL ret = TRUE;
3163     DWORD i, cbOIDs = 0;
3164     BOOL allUsagesValid = TRUE;
3165     CERT_ENHKEY_USAGE validUsages = { 0, NULL };
3166 
3167     TRACE("(%d, %p, %d, %p, %d)\n", cCerts, rghCerts, *cNumOIDs,
3168      rghOIDs, *pcbOIDs);
3169 
3170     for (i = 0; i < cCerts; i++)
3171     {
3172         CERT_ENHKEY_USAGE usage;
3173         DWORD size = sizeof(usage);
3174 
3175         ret = CertGetEnhancedKeyUsage(rghCerts[i], 0, &usage, &size);
3176         /* Success is deliberately ignored: it implies all usages are valid */
3177         if (!ret && GetLastError() == ERROR_MORE_DATA)
3178         {
3179             PCERT_ENHKEY_USAGE pUsage = CryptMemAlloc(size);
3180 
3181             allUsagesValid = FALSE;
3182             if (pUsage)
3183             {
3184                 ret = CertGetEnhancedKeyUsage(rghCerts[i], 0, pUsage, &size);
3185                 if (ret)
3186                 {
3187                     if (!validUsages.cUsageIdentifier)
3188                     {
3189                         DWORD j;
3190 
3191                         cbOIDs = pUsage->cUsageIdentifier * sizeof(LPSTR);
3192                         validUsages.cUsageIdentifier = pUsage->cUsageIdentifier;
3193                         for (j = 0; j < validUsages.cUsageIdentifier; j++)
3194                             cbOIDs += lstrlenA(pUsage->rgpszUsageIdentifier[j])
3195                              + 1;
3196                         validUsages.rgpszUsageIdentifier =
3197                          CryptMemAlloc(cbOIDs);
3198                         if (validUsages.rgpszUsageIdentifier)
3199                         {
3200                             LPSTR nextOID = (LPSTR)
3201                              ((LPBYTE)validUsages.rgpszUsageIdentifier +
3202                              validUsages.cUsageIdentifier * sizeof(LPSTR));
3203 
3204                             for (j = 0; j < validUsages.cUsageIdentifier; j++)
3205                             {
3206                                 validUsages.rgpszUsageIdentifier[j] = nextOID;
3207                                 lstrcpyA(validUsages.rgpszUsageIdentifier[j],
3208                                  pUsage->rgpszUsageIdentifier[j]);
3209                                 nextOID += lstrlenA(nextOID) + 1;
3210                             }
3211                         }
3212                     }
3213                     else
3214                     {
3215                         struct BitField validIndexes = { 0, NULL };
3216                         DWORD j, k, numRemoved = 0;
3217 
3218                         /* Merge: build a bitmap of all the indexes of
3219                          * validUsages.rgpszUsageIdentifier that are in pUsage.
3220                          */
3221                         for (j = 0; j < pUsage->cUsageIdentifier; j++)
3222                         {
3223                             for (k = 0; k < validUsages.cUsageIdentifier; k++)
3224                             {
3225                                 if (!strcmp(pUsage->rgpszUsageIdentifier[j],
3226                                  validUsages.rgpszUsageIdentifier[k]))
3227                                 {
3228                                     CRYPT_SetBitInField(&validIndexes, k);
3229                                     break;
3230                                 }
3231                             }
3232                         }
3233                         /* Merge by removing from validUsages those that are
3234                          * not in the bitmap.
3235                          */
3236                         for (j = 0; j < validUsages.cUsageIdentifier; j++)
3237                         {
3238                             if (!CRYPT_IsBitInFieldSet(&validIndexes, j))
3239                             {
3240                                 if (j < validUsages.cUsageIdentifier - 1)
3241                                 {
3242                                     memmove(&validUsages.rgpszUsageIdentifier[j],
3243                                      &validUsages.rgpszUsageIdentifier[j +
3244                                      numRemoved + 1],
3245                                      (validUsages.cUsageIdentifier - numRemoved
3246                                      - j - 1) * sizeof(LPSTR));
3247                                     cbOIDs -= lstrlenA(
3248                                      validUsages.rgpszUsageIdentifier[j]) + 1 +
3249                                      sizeof(LPSTR);
3250                                     validUsages.cUsageIdentifier--;
3251                                     numRemoved++;
3252                                 }
3253                                 else
3254                                     validUsages.cUsageIdentifier--;
3255                             }
3256                         }
3257                         CryptMemFree(validIndexes.indexes);
3258                     }
3259                 }
3260                 CryptMemFree(pUsage);
3261             }
3262         }
3263     }
3264     ret = TRUE;
3265     if (allUsagesValid)
3266     {
3267         *cNumOIDs = -1;
3268         *pcbOIDs = 0;
3269     }
3270     else
3271     {
3272         *cNumOIDs = validUsages.cUsageIdentifier;
3273         if (!rghOIDs)
3274             *pcbOIDs = cbOIDs;
3275         else if (*pcbOIDs < cbOIDs)
3276         {
3277             *pcbOIDs = cbOIDs;
3278             SetLastError(ERROR_MORE_DATA);
3279             ret = FALSE;
3280         }
3281         else
3282         {
3283             LPSTR nextOID = (LPSTR)((LPBYTE)rghOIDs +
3284              validUsages.cUsageIdentifier * sizeof(LPSTR));
3285 
3286             *pcbOIDs = cbOIDs;
3287             for (i = 0; i < validUsages.cUsageIdentifier; i++)
3288             {
3289                 rghOIDs[i] = nextOID;
3290                 lstrcpyA(nextOID, validUsages.rgpszUsageIdentifier[i]);
3291                 nextOID += lstrlenA(nextOID) + 1;
3292             }
3293         }
3294     }
3295     CryptMemFree(validUsages.rgpszUsageIdentifier);
3296     TRACE("cNumOIDs: %d\n", *cNumOIDs);
3297     TRACE("returning %d\n", ret);
3298     return ret;
3299 }
3300 
3301 /* Sets the CERT_KEY_PROV_INFO_PROP_ID property of context from pInfo, or, if
3302  * pInfo is NULL, from the attributes of hProv.
3303  */
3304 static void CertContext_SetKeyProvInfo(PCCERT_CONTEXT context,
3305  const CRYPT_KEY_PROV_INFO *pInfo, HCRYPTPROV hProv)
3306 {
3307     CRYPT_KEY_PROV_INFO info = { 0 };
3308     BOOL ret;
3309 
3310     if (!pInfo)
3311     {
3312         DWORD size;
3313         int len;
3314 
3315         ret = CryptGetProvParam(hProv, PP_CONTAINER, NULL, &size, 0);
3316         if (ret)
3317         {
3318             LPSTR szContainer = CryptMemAlloc(size);
3319 
3320             if (szContainer)
3321             {
3322                 ret = CryptGetProvParam(hProv, PP_CONTAINER,
3323                  (BYTE *)szContainer, &size, 0);
3324                 if (ret)
3325                 {
3326                     len = MultiByteToWideChar(CP_ACP, 0, szContainer, -1,
3327                      NULL, 0);
3328                     if (len)
3329                     {
3330                         info.pwszContainerName = CryptMemAlloc(len *
3331                          sizeof(WCHAR));
3332                         MultiByteToWideChar(CP_ACP, 0, szContainer, -1,
3333                          info.pwszContainerName, len);
3334                     }
3335                 }
3336                 CryptMemFree(szContainer);
3337             }
3338         }
3339         ret = CryptGetProvParam(hProv, PP_NAME, NULL, &size, 0);
3340         if (ret)
3341         {
3342             LPSTR szProvider = CryptMemAlloc(size);
3343 
3344             if (szProvider)
3345             {
3346                 ret = CryptGetProvParam(hProv, PP_NAME, (BYTE *)szProvider,
3347                  &size, 0);
3348                 if (ret)
3349                 {
3350                     len = MultiByteToWideChar(CP_ACP, 0, szProvider, -1,
3351                      NULL, 0);
3352                     if (len)
3353                     {
3354                         info.pwszProvName = CryptMemAlloc(len *
3355                          sizeof(WCHAR));
3356                         MultiByteToWideChar(CP_ACP, 0, szProvider, -1,
3357                          info.pwszProvName, len);
3358                     }
3359                 }
3360                 CryptMemFree(szProvider);
3361             }
3362         }
3363         /* in case no CRYPT_KEY_PROV_INFO given,
3364          *  we always use AT_SIGNATURE key spec
3365          */
3366         info.dwKeySpec = AT_SIGNATURE;
3367         size = sizeof(info.dwProvType);
3368         ret = CryptGetProvParam(hProv, PP_PROVTYPE, (LPBYTE)&info.dwProvType,
3369          &size, 0);
3370         if (!ret)
3371             info.dwProvType = PROV_RSA_FULL;
3372         pInfo = &info;
3373     }
3374 
3375     CertSetCertificateContextProperty(context, CERT_KEY_PROV_INFO_PROP_ID,
3376      0, pInfo);
3377 
3378     if (pInfo == &info)
3379     {
3380         CryptMemFree(info.pwszContainerName);
3381         CryptMemFree(info.pwszProvName);
3382     }
3383 }
3384 
3385 /* Creates a signed certificate context from the unsigned, encoded certificate
3386  * in blob, using the crypto provider hProv and the signature algorithm sigAlgo.
3387  */
3388 static PCCERT_CONTEXT CRYPT_CreateSignedCert(const CRYPT_DER_BLOB *blob,
3389  HCRYPTPROV hProv, DWORD dwKeySpec, PCRYPT_ALGORITHM_IDENTIFIER sigAlgo)
3390 {
3391     PCCERT_CONTEXT context = NULL;
3392     BOOL ret;
3393     DWORD sigSize = 0;
3394 
3395     ret = CryptSignCertificate(hProv, dwKeySpec, X509_ASN_ENCODING,
3396      blob->pbData, blob->cbData, sigAlgo, NULL, NULL, &sigSize);
3397     if (ret)
3398     {
3399         LPBYTE sig = CryptMemAlloc(sigSize);
3400 
3401         ret = CryptSignCertificate(hProv, dwKeySpec, X509_ASN_ENCODING,
3402          blob->pbData, blob->cbData, sigAlgo, NULL, sig, &sigSize);
3403         if (ret)
3404         {
3405             CERT_SIGNED_CONTENT_INFO signedInfo;
3406             BYTE *encodedSignedCert = NULL;
3407             DWORD encodedSignedCertSize = 0;
3408 
3409             signedInfo.ToBeSigned.cbData = blob->cbData;
3410             signedInfo.ToBeSigned.pbData = blob->pbData;
3411             signedInfo.SignatureAlgorithm = *sigAlgo;
3412             signedInfo.Signature.cbData = sigSize;
3413             signedInfo.Signature.pbData = sig;
3414             signedInfo.Signature.cUnusedBits = 0;
3415             ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_CERT,
3416              &signedInfo, CRYPT_ENCODE_ALLOC_FLAG, NULL,
3417              &encodedSignedCert, &encodedSignedCertSize);
3418             if (ret)
3419             {
3420                 context = CertCreateCertificateContext(X509_ASN_ENCODING,
3421                  encodedSignedCert, encodedSignedCertSize);
3422                 LocalFree(encodedSignedCert);
3423             }
3424         }
3425         CryptMemFree(sig);
3426     }
3427     return context;
3428 }
3429 
3430 /* Copies data from the parameters into info, where:
3431  * pSerialNumber: The serial number.  Must not be NULL.
3432  * pSubjectIssuerBlob: Specifies both the subject and issuer for info.
3433  *                     Must not be NULL
3434  * pSignatureAlgorithm: Optional.
3435  * pStartTime: The starting time of the certificate.  If NULL, the current
3436  *             system time is used.
3437  * pEndTime: The ending time of the certificate.  If NULL, one year past the
3438  *           starting time is used.
3439  * pubKey: The public key of the certificate.  Must not be NULL.
3440  * pExtensions: Extensions to be included with the certificate.  Optional.
3441  */
3442 static void CRYPT_MakeCertInfo(PCERT_INFO info, const CRYPT_DATA_BLOB *pSerialNumber,
3443  const CERT_NAME_BLOB *pSubjectIssuerBlob,
3444  const CRYPT_ALGORITHM_IDENTIFIER *pSignatureAlgorithm, const SYSTEMTIME *pStartTime,
3445  const SYSTEMTIME *pEndTime, const CERT_PUBLIC_KEY_INFO *pubKey,
3446  const CERT_EXTENSIONS *pExtensions)
3447 {
3448     static CHAR oid[] = szOID_RSA_SHA1RSA;
3449 
3450     assert(info);
3451     assert(pSerialNumber);
3452     assert(pSubjectIssuerBlob);
3453     assert(pubKey);
3454 
3455     if (pExtensions && pExtensions->cExtension)
3456         info->dwVersion = CERT_V3;
3457     else
3458         info->dwVersion = CERT_V1;
3459     info->SerialNumber.cbData = pSerialNumber->cbData;
3460     info->SerialNumber.pbData = pSerialNumber->pbData;
3461     if (pSignatureAlgorithm)
3462         info->SignatureAlgorithm = *pSignatureAlgorithm;
3463     else
3464     {
3465         info->SignatureAlgorithm.pszObjId = oid;
3466         info->SignatureAlgorithm.Parameters.cbData = 0;
3467         info->SignatureAlgorithm.Parameters.pbData = NULL;
3468     }
3469     info->Issuer.cbData = pSubjectIssuerBlob->cbData;
3470     info->Issuer.pbData = pSubjectIssuerBlob->pbData;
3471     if (pStartTime)
3472         SystemTimeToFileTime(pStartTime, &info->NotBefore);
3473     else
3474         GetSystemTimeAsFileTime(&info->NotBefore);
3475     if (pEndTime)
3476         SystemTimeToFileTime(pEndTime, &info->NotAfter);
3477     else
3478     {
3479         SYSTEMTIME endTime;
3480 
3481         if (FileTimeToSystemTime(&info->NotBefore, &endTime))
3482         {
3483             endTime.wYear++;
3484             SystemTimeToFileTime(&endTime, &info->NotAfter);
3485         }
3486     }
3487     info->Subject.cbData = pSubjectIssuerBlob->cbData;
3488     info->Subject.pbData = pSubjectIssuerBlob->pbData;
3489     info->SubjectPublicKeyInfo = *pubKey;
3490     if (pExtensions)
3491     {
3492         info->cExtension = pExtensions->cExtension;
3493         info->rgExtension = pExtensions->rgExtension;
3494     }
3495     else
3496     {
3497         info->cExtension = 0;
3498         info->rgExtension = NULL;
3499     }
3500 }
3501 
3502 typedef RPC_STATUS (RPC_ENTRY *UuidCreateFunc)(UUID *);
3503 typedef RPC_STATUS (RPC_ENTRY *UuidToStringFunc)(UUID *, unsigned char **);
3504 typedef RPC_STATUS (RPC_ENTRY *RpcStringFreeFunc)(unsigned char **);
3505 
3506 static HCRYPTPROV CRYPT_CreateKeyProv(void)
3507 {
3508     HCRYPTPROV hProv = 0;
3509     HMODULE rpcrt = LoadLibraryA("rpcrt4");
3510 
3511     if (rpcrt)
3512     {
3513         UuidCreateFunc uuidCreate = (UuidCreateFunc)GetProcAddress(rpcrt,
3514          "UuidCreate");
3515         UuidToStringFunc uuidToString = (UuidToStringFunc)GetProcAddress(rpcrt,
3516          "UuidToStringA");
3517         RpcStringFreeFunc rpcStringFree = (RpcStringFreeFunc)GetProcAddress(
3518          rpcrt, "RpcStringFreeA");
3519 
3520         if (uuidCreate && uuidToString && rpcStringFree)
3521         {
3522             UUID uuid;
3523             RPC_STATUS status = uuidCreate(&uuid);
3524 
3525             if (status == RPC_S_OK || status == RPC_S_UUID_LOCAL_ONLY)
3526             {
3527                 unsigned char *uuidStr;
3528 
3529                 status = uuidToString(&uuid, &uuidStr);
3530                 if (status == RPC_S_OK)
3531                 {
3532                     BOOL ret = CryptAcquireContextA(&hProv, (LPCSTR)uuidStr,
3533                      MS_DEF_PROV_A, PROV_RSA_FULL, CRYPT_NEWKEYSET);
3534 
3535                     if (ret)
3536                     {
3537                         HCRYPTKEY key;
3538 
3539                         ret = CryptGenKey(hProv, AT_SIGNATURE, 0, &key);
3540                         if (ret)
3541                             CryptDestroyKey(key);
3542                     }
3543                     rpcStringFree(&uuidStr);
3544                 }
3545             }
3546         }
3547         FreeLibrary(rpcrt);
3548     }
3549     return hProv;
3550 }
3551 
3552 PCCERT_CONTEXT WINAPI CertCreateSelfSignCertificate(HCRYPTPROV_OR_NCRYPT_KEY_HANDLE hProv,
3553  PCERT_NAME_BLOB pSubjectIssuerBlob, DWORD dwFlags,
3554  PCRYPT_KEY_PROV_INFO pKeyProvInfo,
3555  PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, PSYSTEMTIME pStartTime,
3556  PSYSTEMTIME pEndTime, PCERT_EXTENSIONS pExtensions)
3557 {
3558     PCCERT_CONTEXT context = NULL;
3559     BOOL ret, releaseContext = FALSE;
3560     PCERT_PUBLIC_KEY_INFO pubKey = NULL;
3561     DWORD pubKeySize = 0, dwKeySpec;
3562 
3563     TRACE("(%08lx, %p, %08x, %p, %p, %p, %p, %p)\n", hProv,
3564      pSubjectIssuerBlob, dwFlags, pKeyProvInfo, pSignatureAlgorithm, pStartTime,
3565      pExtensions, pExtensions);
3566 
3567     if(!pSubjectIssuerBlob)
3568     {
3569         SetLastError(ERROR_INVALID_PARAMETER);
3570         return NULL;
3571     }
3572 
3573     dwKeySpec = pKeyProvInfo ? pKeyProvInfo->dwKeySpec : AT_SIGNATURE;
3574     if (!hProv)
3575     {
3576         if (!pKeyProvInfo)
3577         {
3578             hProv = CRYPT_CreateKeyProv();
3579             releaseContext = TRUE;
3580         }
3581         else if (pKeyProvInfo->dwFlags & CERT_SET_KEY_PROV_HANDLE_PROP_ID)
3582         {
3583             SetLastError(NTE_BAD_FLAGS);
3584             return NULL;
3585         }
3586         else
3587         {
3588             HCRYPTKEY hKey = 0;
3589             /* acquire the context using the given information*/
3590             ret = CryptAcquireContextW(&hProv,pKeyProvInfo->pwszContainerName,
3591                     pKeyProvInfo->pwszProvName,pKeyProvInfo->dwProvType,
3592                     pKeyProvInfo->dwFlags);
3593             if (!ret)
3594             {
3595 	        if(GetLastError() != NTE_BAD_KEYSET)
3596                     return NULL;
3597                 /* create the key set */
3598                 ret = CryptAcquireContextW(&hProv,pKeyProvInfo->pwszContainerName,
3599                     pKeyProvInfo->pwszProvName,pKeyProvInfo->dwProvType,
3600                     pKeyProvInfo->dwFlags|CRYPT_NEWKEYSET);
3601                 if (!ret)
3602                     return NULL;
3603 	    }
3604             /* check if the key is here */
3605             ret = CryptGetUserKey(hProv,dwKeySpec,&hKey);
3606             if(!ret)
3607             {
3608                 if (NTE_NO_KEY == GetLastError())
3609                 { /* generate the key */
3610                     ret = CryptGenKey(hProv,dwKeySpec,0,&hKey);
3611                 }
3612                 if (!ret)
3613                 {
3614                     CryptReleaseContext(hProv,0);
3615                     SetLastError(NTE_BAD_KEYSET);
3616                     return NULL;
3617                 }
3618             }
3619             CryptDestroyKey(hKey);
3620             releaseContext = TRUE;
3621         }
3622     }
3623 
3624     ret = CryptExportPublicKeyInfo(hProv, dwKeySpec, X509_ASN_ENCODING, NULL,
3625      &pubKeySize);
3626     if (!ret)
3627         goto end;
3628     pubKey = CryptMemAlloc(pubKeySize);
3629     if (pubKey)
3630     {
3631         ret = CryptExportPublicKeyInfo(hProv, dwKeySpec, X509_ASN_ENCODING,
3632          pubKey, &pubKeySize);
3633         if (ret)
3634         {
3635             CERT_INFO info = { 0 };
3636             CRYPT_DER_BLOB blob = { 0, NULL };
3637             BYTE serial[16];
3638             CRYPT_DATA_BLOB serialBlob = { sizeof(serial), serial };
3639 
3640             CryptGenRandom(hProv, sizeof(serial), serial);
3641             CRYPT_MakeCertInfo(&info, &serialBlob, pSubjectIssuerBlob,
3642              pSignatureAlgorithm, pStartTime, pEndTime, pubKey, pExtensions);
3643             ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED,
3644              &info, CRYPT_ENCODE_ALLOC_FLAG, NULL, &blob.pbData,
3645              &blob.cbData);
3646             if (ret)
3647             {
3648                 if (!(dwFlags & CERT_CREATE_SELFSIGN_NO_SIGN))
3649                     context = CRYPT_CreateSignedCert(&blob, hProv,dwKeySpec,
3650                      &info.SignatureAlgorithm);
3651                 else
3652                     context = CertCreateCertificateContext(X509_ASN_ENCODING,
3653                      blob.pbData, blob.cbData);
3654                 if (context && !(dwFlags & CERT_CREATE_SELFSIGN_NO_KEY_INFO))
3655                     CertContext_SetKeyProvInfo(context, pKeyProvInfo, hProv);
3656                 LocalFree(blob.pbData);
3657             }
3658         }
3659         CryptMemFree(pubKey);
3660     }
3661 end:
3662     if (releaseContext)
3663         CryptReleaseContext(hProv, 0);
3664     return context;
3665 }
3666 
3667 BOOL WINAPI CertVerifyCTLUsage(DWORD dwEncodingType, DWORD dwSubjectType,
3668                                void *pvSubject, PCTL_USAGE pSubjectUsage, DWORD dwFlags,
3669                                PCTL_VERIFY_USAGE_PARA pVerifyUsagePara,
3670                                PCTL_VERIFY_USAGE_STATUS pVerifyUsageStatus)
3671 {
3672     FIXME("(0x%x, %d, %p, %p, 0x%x, %p, %p): stub\n", dwEncodingType,
3673           dwSubjectType, pvSubject, pSubjectUsage, dwFlags, pVerifyUsagePara,
3674           pVerifyUsageStatus);
3675     SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
3676     return FALSE;
3677 }
3678 
3679 const void * WINAPI CertCreateContext(DWORD dwContextType, DWORD dwEncodingType,
3680                                       const BYTE *pbEncoded, DWORD cbEncoded,
3681                                       DWORD dwFlags, PCERT_CREATE_CONTEXT_PARA pCreatePara)
3682 {
3683     TRACE("(0x%x, 0x%x, %p, %d, 0x%08x, %p)\n", dwContextType, dwEncodingType,
3684           pbEncoded, cbEncoded, dwFlags, pCreatePara);
3685 
3686     if (dwFlags)
3687     {
3688         FIXME("dwFlags 0x%08x not handled\n", dwFlags);
3689         return NULL;
3690     }
3691     if (pCreatePara)
3692     {
3693         FIXME("pCreatePara not handled\n");
3694         return NULL;
3695     }
3696 
3697     switch (dwContextType)
3698     {
3699     case CERT_STORE_CERTIFICATE_CONTEXT:
3700         return CertCreateCertificateContext(dwEncodingType, pbEncoded, cbEncoded);
3701     case CERT_STORE_CRL_CONTEXT:
3702         return CertCreateCRLContext(dwEncodingType, pbEncoded, cbEncoded);
3703     case CERT_STORE_CTL_CONTEXT:
3704         return CertCreateCTLContext(dwEncodingType, pbEncoded, cbEncoded);
3705     default:
3706         WARN("unknown context type: 0x%x\n", dwContextType);
3707         return NULL;
3708     }
3709 }
3710 
3711 BOOL WINAPI CryptSetKeyIdentifierProperty(const CRYPT_HASH_BLOB *pKeyIdentifier, DWORD dwPropId,
3712     DWORD dwFlags, LPCWSTR pwszComputerName, void *pvReserved, const void *pvData)
3713 {
3714     FIXME("(%p, 0x%x, 0x%x, %s, %p, %p): stub\n", pKeyIdentifier, dwPropId, dwFlags,
3715         debugstr_w(pwszComputerName), pvReserved, pvData);
3716     return FALSE;
3717 }
3718