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