crypt32: Implement CertCompare functions, with tests.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17  *
18  */
19
20 #include <assert.h>
21 #include <stdarg.h>
22 #include "windef.h"
23 #include "winbase.h"
24 #include "wincrypt.h"
25 #include "winnls.h"
26 #include "rpc.h"
27 #include "wine/debug.h"
28 #include "crypt32_private.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
31
32 BOOL WINAPI CertCompareCertificate(DWORD dwCertEncodingType,
33  PCERT_INFO pCertId1, PCERT_INFO pCertId2)
34 {
35     TRACE("(%08lx, %p, %p)\n", dwCertEncodingType, pCertId1, pCertId2);
36
37     return CertCompareCertificateName(dwCertEncodingType, &pCertId1->Issuer,
38      &pCertId2->Issuer) && CertCompareIntegerBlob(&pCertId1->SerialNumber,
39      &pCertId2->SerialNumber);
40 }
41
42 BOOL WINAPI CertCompareCertificateName(DWORD dwCertEncodingType,
43  PCERT_NAME_BLOB pCertName1, PCERT_NAME_BLOB pCertName2)
44 {
45     BOOL ret;
46
47     TRACE("(%08lx, %p, %p)\n", dwCertEncodingType, pCertName1, pCertName2);
48
49     if (pCertName1->cbData == pCertName2->cbData)
50     {
51         if (pCertName1->cbData)
52             ret = !memcmp(pCertName1->pbData, pCertName2->pbData,
53              pCertName1->cbData);
54         else
55             ret = TRUE;
56     }
57     else
58         ret = FALSE;
59     return ret;
60 }
61
62 /* Returns the number of significant bytes in pInt, where a byte is
63  * insignificant if it's a leading 0 for positive numbers or a leading 0xff
64  * for negative numbers.  pInt is assumed to be little-endian.
65  */
66 static DWORD CRYPT_significantBytes(PCRYPT_INTEGER_BLOB pInt)
67 {
68     DWORD ret = pInt->cbData;
69
70     while (ret > 1)
71     {
72         if (pInt->pbData[ret - 2] <= 0x7f && pInt->pbData[ret - 1] == 0)
73             ret--;
74         else if (pInt->pbData[ret - 2] >= 0x80 && pInt->pbData[ret - 1] == 0xff)
75             ret--;
76         else
77             break;
78     }
79     return ret;
80 }
81
82 BOOL WINAPI CertCompareIntegerBlob(PCRYPT_INTEGER_BLOB pInt1,
83  PCRYPT_INTEGER_BLOB pInt2)
84 {
85     BOOL ret;
86     DWORD cb1, cb2;
87
88     TRACE("(%p, %p)\n", pInt1, pInt2);
89
90     cb1 = CRYPT_significantBytes(pInt1);
91     cb2 = CRYPT_significantBytes(pInt2);
92     if (cb1 == cb2)
93         ret = !memcmp(pInt1->pbData, pInt1->pbData, cb1);
94     else
95         ret = FALSE;
96     return ret;
97 }
98
99 BOOL WINAPI CertComparePublicKeyInfo(DWORD dwCertEncodingType,
100  PCERT_PUBLIC_KEY_INFO pPublicKey1, PCERT_PUBLIC_KEY_INFO pPublicKey2)
101 {
102     BOOL ret;
103
104     TRACE("(%08lx, %p, %p)\n", dwCertEncodingType, pPublicKey1, pPublicKey2);
105
106     if (pPublicKey1->PublicKey.cbData == pPublicKey2->PublicKey.cbData &&
107      pPublicKey1->PublicKey.cUnusedBits == pPublicKey2->PublicKey.cUnusedBits)
108     {
109         if (pPublicKey2->PublicKey.cbData)
110             ret = !memcmp(pPublicKey1->PublicKey.pbData,
111              pPublicKey2->PublicKey.pbData, pPublicKey1->PublicKey.cbData);
112         else
113             ret = TRUE;
114     }
115     else
116         ret = FALSE;
117     return ret;
118 }
119
120 PCRYPT_ATTRIBUTE WINAPI CertFindAttribute(LPCSTR pszObjId, DWORD cAttr,
121  CRYPT_ATTRIBUTE rgAttr[])
122 {
123     PCRYPT_ATTRIBUTE ret = NULL;
124     DWORD i;
125
126     TRACE("%s %ld %p\n", debugstr_a(pszObjId), cAttr, rgAttr);
127
128     if (!cAttr)
129         return NULL;
130     if (!pszObjId)
131     {
132         SetLastError(ERROR_INVALID_PARAMETER);
133         return NULL;
134     }
135
136     for (i = 0; !ret && i < cAttr; i++)
137         if (rgAttr[i].pszObjId && !strcmp(pszObjId, rgAttr[i].pszObjId))
138             ret = &rgAttr[i];
139     return ret;
140 }
141
142 PCERT_EXTENSION WINAPI CertFindExtension(LPCSTR pszObjId, DWORD cExtensions,
143  CERT_EXTENSION rgExtensions[])
144 {
145     PCERT_EXTENSION ret = NULL;
146     DWORD i;
147
148     TRACE("%s %ld %p\n", debugstr_a(pszObjId), cExtensions, rgExtensions);
149
150     if (!cExtensions)
151         return NULL;
152     if (!pszObjId)
153     {
154         SetLastError(ERROR_INVALID_PARAMETER);
155         return NULL;
156     }
157
158     for (i = 0; !ret && i < cExtensions; i++)
159         if (rgExtensions[i].pszObjId && !strcmp(pszObjId,
160          rgExtensions[i].pszObjId))
161             ret = &rgExtensions[i];
162     return ret;
163 }
164
165 PCERT_RDN_ATTR WINAPI CertFindRDNAttr(LPCSTR pszObjId, PCERT_NAME_INFO pName)
166 {
167     PCERT_RDN_ATTR ret = NULL;
168     DWORD i, j;
169
170     TRACE("%s %p\n", debugstr_a(pszObjId), pName);
171
172     if (!pszObjId)
173     {
174         SetLastError(ERROR_INVALID_PARAMETER);
175         return NULL;
176     }
177
178     for (i = 0; !ret && i < pName->cRDN; i++)
179         for (j = 0; !ret && j < pName->rgRDN[i].cRDNAttr; j++)
180             if (pName->rgRDN[i].rgRDNAttr[j].pszObjId && !strcmp(pszObjId,
181              pName->rgRDN[i].rgRDNAttr[j].pszObjId))
182                 ret = &pName->rgRDN[i].rgRDNAttr[j];
183     return ret;
184 }
185
186 LONG WINAPI CertVerifyTimeValidity(LPFILETIME pTimeToVerify,
187  PCERT_INFO pCertInfo)
188 {
189     FILETIME fileTime;
190     LONG ret;
191
192     if (!pTimeToVerify)
193     {
194         SYSTEMTIME sysTime;
195
196         GetSystemTime(&sysTime);
197         SystemTimeToFileTime(&sysTime, &fileTime);
198         pTimeToVerify = &fileTime;
199     }
200     if ((ret = CompareFileTime(pTimeToVerify, &pCertInfo->NotBefore)) >= 0)
201     {
202         ret = CompareFileTime(pTimeToVerify, &pCertInfo->NotAfter);
203         if (ret < 0)
204             ret = 0;
205     }
206     return ret;
207 }
208
209 BOOL WINAPI CryptHashCertificate(HCRYPTPROV hCryptProv, ALG_ID Algid,
210  DWORD dwFlags, const BYTE *pbEncoded, DWORD cbEncoded, BYTE *pbComputedHash,
211  DWORD *pcbComputedHash)
212 {
213     BOOL ret = TRUE;
214     HCRYPTHASH hHash = 0;
215
216     TRACE("(%ld, %d, %08lx, %p, %ld, %p, %p)\n", hCryptProv, Algid, dwFlags,
217      pbEncoded, cbEncoded, pbComputedHash, pcbComputedHash);
218
219     if (!hCryptProv)
220         hCryptProv = CRYPT_GetDefaultProvider();
221     if (!Algid)
222         Algid = CALG_SHA1;
223     if (ret)
224     {
225         ret = CryptCreateHash(hCryptProv, Algid, 0, 0, &hHash);
226         if (ret)
227         {
228             ret = CryptHashData(hHash, pbEncoded, cbEncoded, 0);
229             if (ret)
230                 ret = CryptGetHashParam(hHash, HP_HASHVAL, pbComputedHash,
231                  pcbComputedHash, 0);
232             CryptDestroyHash(hHash);
233         }
234     }
235     return ret;
236 }
237
238 BOOL WINAPI CryptSignCertificate(HCRYPTPROV hCryptProv, DWORD dwKeySpec,
239  DWORD dwCertEncodingType, const BYTE *pbEncodedToBeSigned,
240  DWORD cbEncodedToBeSigned, PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm,
241  const void *pvHashAuxInfo, BYTE *pbSignature, DWORD *pcbSignature)
242 {
243     BOOL ret;
244     ALG_ID algID;
245     HCRYPTHASH hHash;
246
247     TRACE("(%08lx, %ld, %ld, %p, %ld, %p, %p, %p, %p)\n", hCryptProv,
248      dwKeySpec, dwCertEncodingType, pbEncodedToBeSigned, cbEncodedToBeSigned,
249      pSignatureAlgorithm, pvHashAuxInfo, pbSignature, pcbSignature);
250
251     algID = CertOIDToAlgId(pSignatureAlgorithm->pszObjId);
252     if (!algID)
253     {
254         SetLastError(NTE_BAD_ALGID);
255         return FALSE;
256     }
257     if (!hCryptProv)
258     {
259         SetLastError(ERROR_INVALID_PARAMETER);
260         return FALSE;
261     }
262
263     ret = CryptCreateHash(hCryptProv, algID, 0, 0, &hHash);
264     if (ret)
265     {
266         ret = CryptHashData(hHash, pbEncodedToBeSigned, cbEncodedToBeSigned, 0);
267         if (ret)
268             ret = CryptSignHashW(hHash, dwKeySpec, NULL, 0, pbSignature,
269              pcbSignature);
270         CryptDestroyHash(hHash);
271     }
272     return ret;
273 }
274
275 BOOL WINAPI CryptVerifyCertificateSignature(HCRYPTPROV hCryptProv,
276  DWORD dwCertEncodingType, const BYTE *pbEncoded, DWORD cbEncoded,
277  PCERT_PUBLIC_KEY_INFO pPublicKey)
278 {
279     return CryptVerifyCertificateSignatureEx(hCryptProv, dwCertEncodingType,
280      CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB, (void *)pbEncoded,
281      CRYPT_VERIFY_CERT_SIGN_ISSUER_PUBKEY, pPublicKey, 0, NULL);
282 }
283
284 static BOOL CRYPT_VerifyCertSignatureFromPublicKeyInfo(HCRYPTPROV hCryptProv,
285  DWORD dwCertEncodingType, PCERT_PUBLIC_KEY_INFO pubKeyInfo,
286  PCERT_SIGNED_CONTENT_INFO signedCert)
287 {
288     BOOL ret;
289     ALG_ID algID = CertOIDToAlgId(pubKeyInfo->Algorithm.pszObjId);
290     HCRYPTKEY key;
291
292     /* Load the default provider if necessary */
293     if (!hCryptProv)
294         hCryptProv = CRYPT_GetDefaultProvider();
295     ret = CryptImportPublicKeyInfoEx(hCryptProv, dwCertEncodingType,
296      pubKeyInfo, algID, 0, NULL, &key);
297     if (ret)
298     {
299         HCRYPTHASH hash;
300
301         /* Some key algorithms aren't hash algorithms, so map them */
302         if (algID == CALG_RSA_SIGN || algID == CALG_RSA_KEYX)
303             algID = CALG_SHA1;
304         ret = CryptCreateHash(hCryptProv, algID, 0, 0, &hash);
305         if (ret)
306         {
307             ret = CryptHashData(hash, signedCert->ToBeSigned.pbData,
308              signedCert->ToBeSigned.cbData, 0);
309             if (ret)
310                 ret = CryptVerifySignatureW(hash, signedCert->Signature.pbData,
311                  signedCert->Signature.cbData, key, NULL, 0);
312             CryptDestroyHash(hash);
313         }
314         CryptDestroyKey(key);
315     }
316     return ret;
317 }
318
319 BOOL WINAPI CryptVerifyCertificateSignatureEx(HCRYPTPROV hCryptProv,
320  DWORD dwCertEncodingType, DWORD dwSubjectType, void *pvSubject,
321  DWORD dwIssuerType, void *pvIssuer, DWORD dwFlags, void *pvReserved)
322 {
323     BOOL ret = TRUE;
324     CRYPT_DATA_BLOB subjectBlob;
325
326     TRACE("(%08lx, %ld, %ld, %p, %ld, %p, %08lx, %p)\n", hCryptProv,
327      dwCertEncodingType, dwSubjectType, pvSubject, dwIssuerType, pvIssuer,
328      dwFlags, pvReserved);
329
330     switch (dwSubjectType)
331     {
332     case CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB:
333     {
334         PCRYPT_DATA_BLOB blob = (PCRYPT_DATA_BLOB)pvSubject;
335
336         subjectBlob.pbData = blob->pbData;
337         subjectBlob.cbData = blob->cbData;
338         break;
339     }
340     case CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT:
341     {
342         PCERT_CONTEXT context = (PCERT_CONTEXT)pvSubject;
343
344         subjectBlob.pbData = context->pbCertEncoded;
345         subjectBlob.cbData = context->cbCertEncoded;
346         break;
347     }
348     case CRYPT_VERIFY_CERT_SIGN_SUBJECT_CRL:
349     {
350         PCRL_CONTEXT context = (PCRL_CONTEXT)pvSubject;
351
352         subjectBlob.pbData = context->pbCrlEncoded;
353         subjectBlob.cbData = context->cbCrlEncoded;
354         break;
355     }
356     default:
357         SetLastError(E_INVALIDARG);
358         ret = FALSE;
359     }
360
361     if (ret)
362     {
363         PCERT_SIGNED_CONTENT_INFO signedCert = NULL;
364         DWORD size = 0;
365
366         ret = CryptDecodeObjectEx(dwCertEncodingType, X509_CERT,
367          subjectBlob.pbData, subjectBlob.cbData,
368          CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
369          (BYTE *)&signedCert, &size);
370         if (ret)
371         {
372             switch (dwIssuerType)
373             {
374             case CRYPT_VERIFY_CERT_SIGN_ISSUER_PUBKEY:
375                 ret = CRYPT_VerifyCertSignatureFromPublicKeyInfo(hCryptProv,
376                  dwCertEncodingType, (PCERT_PUBLIC_KEY_INFO)pvIssuer,
377                  signedCert);
378                 break;
379             case CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT:
380                 ret = CRYPT_VerifyCertSignatureFromPublicKeyInfo(hCryptProv,
381                  dwCertEncodingType,
382                  &((PCCERT_CONTEXT)pvIssuer)->pCertInfo->SubjectPublicKeyInfo,
383                  signedCert);
384                 break;
385             case CRYPT_VERIFY_CERT_SIGN_ISSUER_CHAIN:
386                 FIXME("CRYPT_VERIFY_CERT_SIGN_ISSUER_CHAIN: stub\n");
387                 ret = FALSE;
388                 break;
389             case CRYPT_VERIFY_CERT_SIGN_ISSUER_NULL:
390                 if (pvIssuer)
391                 {
392                     SetLastError(E_INVALIDARG);
393                     ret = FALSE;
394                 }
395                 else
396                 {
397                     FIXME("unimplemented for NULL signer\n");
398                     SetLastError(E_INVALIDARG);
399                     ret = FALSE;
400                 }
401                 break;
402             default:
403                 SetLastError(E_INVALIDARG);
404                 ret = FALSE;
405             }
406             LocalFree(signedCert);
407         }
408     }
409     return ret;
410 }
411
412 BOOL WINAPI CertGetEnhancedKeyUsage(PCCERT_CONTEXT pCertContext, DWORD dwFlags,
413  PCERT_ENHKEY_USAGE pUsage, DWORD *pcbUsage)
414 {
415     PCERT_ENHKEY_USAGE usage = NULL;
416     DWORD bytesNeeded;
417     BOOL ret = TRUE;
418
419     if (!pCertContext || !pcbUsage)
420     {
421         SetLastError(ERROR_INVALID_PARAMETER);
422         return FALSE;
423     }
424
425     TRACE("(%p, %08lx, %p, %ld)\n", pCertContext, dwFlags, pUsage, *pcbUsage);
426
427     if (!(dwFlags & CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG))
428     {
429         DWORD propSize = 0;
430
431         if (CertGetCertificateContextProperty(pCertContext,
432          CERT_ENHKEY_USAGE_PROP_ID, NULL, &propSize))
433         {
434             LPBYTE buf = CryptMemAlloc(propSize);
435
436             if (buf)
437             {
438                 if (CertGetCertificateContextProperty(pCertContext,
439                  CERT_ENHKEY_USAGE_PROP_ID, buf, &propSize))
440                 {
441                     ret = CryptDecodeObjectEx(pCertContext->dwCertEncodingType,
442                      X509_ENHANCED_KEY_USAGE, buf, propSize,
443                      CRYPT_ENCODE_ALLOC_FLAG, NULL, &usage, &bytesNeeded);
444                 }
445                 CryptMemFree(buf);
446             }
447         }
448     }
449     if (!usage && !(dwFlags & CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG))
450     {
451         PCERT_EXTENSION ext = CertFindExtension(szOID_ENHANCED_KEY_USAGE,
452          pCertContext->pCertInfo->cExtension,
453          pCertContext->pCertInfo->rgExtension);
454
455         if (ext)
456         {
457             ret = CryptDecodeObjectEx(pCertContext->dwCertEncodingType,
458              X509_ENHANCED_KEY_USAGE, ext->Value.pbData, ext->Value.cbData,
459              CRYPT_ENCODE_ALLOC_FLAG, NULL, &usage, &bytesNeeded);
460         }
461     }
462     if (!usage)
463     {
464         /* If a particular location is specified, this should fail.  Otherwise
465          * it should succeed with an empty usage.  (This is true on Win2k and
466          * later, which we emulate.)
467          */
468         if (dwFlags)
469         {
470             SetLastError(CRYPT_E_NOT_FOUND);
471             ret = FALSE;
472         }
473         else
474             bytesNeeded = sizeof(CERT_ENHKEY_USAGE);
475     }
476
477     if (ret)
478     {
479         if (!pUsage)
480             *pcbUsage = bytesNeeded;
481         else if (*pcbUsage < bytesNeeded)
482         {
483             SetLastError(ERROR_MORE_DATA);
484             *pcbUsage = bytesNeeded;
485             ret = FALSE;
486         }
487         else
488         {
489             *pcbUsage = bytesNeeded;
490             if (usage)
491             {
492                 DWORD i;
493                 LPSTR nextOID = (LPSTR)((LPBYTE)pUsage +
494                  sizeof(CERT_ENHKEY_USAGE) +
495                  usage->cUsageIdentifier * sizeof(LPSTR));
496
497                 pUsage->cUsageIdentifier = usage->cUsageIdentifier;
498                 pUsage->rgpszUsageIdentifier = (LPSTR *)((LPBYTE)pUsage +
499                  sizeof(CERT_ENHKEY_USAGE));
500                 for (i = 0; i < usage->cUsageIdentifier; i++)
501                 {
502                     pUsage->rgpszUsageIdentifier[i] = nextOID;
503                     strcpy(nextOID, usage->rgpszUsageIdentifier[i]);
504                     nextOID += strlen(nextOID) + 1;
505                 }
506             }
507             else
508                 pUsage->cUsageIdentifier = 0;
509         }
510     }
511     if (usage)
512         LocalFree(usage);
513     TRACE("returning %d\n", ret);
514     return ret;
515 }
516
517 BOOL WINAPI CertSetEnhancedKeyUsage(PCCERT_CONTEXT pCertContext,
518  PCERT_ENHKEY_USAGE pUsage)
519 {
520     BOOL ret;
521
522     TRACE("(%p, %p)\n", pCertContext, pUsage);
523
524     if (pUsage)
525     {
526         CRYPT_DATA_BLOB blob = { 0, NULL };
527
528         ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE,
529          pUsage, CRYPT_ENCODE_ALLOC_FLAG, NULL, &blob.pbData, &blob.cbData);
530         if (ret)
531         {
532             ret = CertSetCertificateContextProperty(pCertContext,
533              CERT_ENHKEY_USAGE_PROP_ID, 0, &blob);
534             LocalFree(blob.pbData);
535         }
536     }
537     else
538         ret = CertSetCertificateContextProperty(pCertContext,
539          CERT_ENHKEY_USAGE_PROP_ID, 0, NULL);
540     return ret;
541 }
542
543 BOOL WINAPI CertAddEnhancedKeyUsageIdentifier(PCCERT_CONTEXT pCertContext,
544  LPCSTR pszUsageIdentifier)
545 {
546     BOOL ret;
547     DWORD size;
548
549     TRACE("(%p, %s)\n", pCertContext, debugstr_a(pszUsageIdentifier));
550
551     if (CertGetEnhancedKeyUsage(pCertContext,
552      CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, NULL, &size))
553     {
554         PCERT_ENHKEY_USAGE usage = CryptMemAlloc(size);
555
556         if (usage)
557         {
558             ret = CertGetEnhancedKeyUsage(pCertContext,
559              CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, usage, &size);
560             if (ret)
561             {
562                 PCERT_ENHKEY_USAGE newUsage = CryptMemAlloc(size +
563                  sizeof(LPSTR) + strlen(pszUsageIdentifier) + 1);
564
565                 if (newUsage)
566                 {
567                     LPSTR nextOID;
568                     DWORD i;
569
570                     newUsage->rgpszUsageIdentifier =
571                      (LPSTR *)((LPBYTE)newUsage + sizeof(CERT_ENHKEY_USAGE));
572                     nextOID = (LPSTR)((LPBYTE)newUsage->rgpszUsageIdentifier +
573                      (usage->cUsageIdentifier + 1) * sizeof(LPSTR));
574                     for (i = 0; i < usage->cUsageIdentifier; i++)
575                     {
576                         newUsage->rgpszUsageIdentifier[i] = nextOID;
577                         strcpy(nextOID, usage->rgpszUsageIdentifier[i]);
578                         nextOID += strlen(nextOID) + 1;
579                     }
580                     newUsage->rgpszUsageIdentifier[i] = nextOID;
581                     strcpy(nextOID, pszUsageIdentifier);
582                     newUsage->cUsageIdentifier = i + 1;
583                     ret = CertSetEnhancedKeyUsage(pCertContext, newUsage);
584                     CryptMemFree(newUsage);
585                 }
586             }
587             CryptMemFree(usage);
588         }
589         else
590             ret = FALSE;
591     }
592     else
593     {
594         PCERT_ENHKEY_USAGE usage = CryptMemAlloc(sizeof(CERT_ENHKEY_USAGE) +
595          sizeof(LPSTR) + strlen(pszUsageIdentifier) + 1);
596
597         if (usage)
598         {
599             usage->rgpszUsageIdentifier =
600              (LPSTR *)((LPBYTE)usage + sizeof(CERT_ENHKEY_USAGE));
601             usage->rgpszUsageIdentifier[0] = (LPSTR)((LPBYTE)usage +
602              sizeof(CERT_ENHKEY_USAGE) + sizeof(LPSTR));
603             strcpy(usage->rgpszUsageIdentifier[0], pszUsageIdentifier);
604             usage->cUsageIdentifier = 1;
605             ret = CertSetEnhancedKeyUsage(pCertContext, usage);
606             CryptMemFree(usage);
607         }
608         else
609             ret = FALSE;
610     }
611     return ret;
612 }
613
614 BOOL WINAPI CertRemoveEnhancedKeyUsageIdentifier(PCCERT_CONTEXT pCertContext,
615  LPCSTR pszUsageIdentifier)
616 {
617     BOOL ret;
618     DWORD size;
619     CERT_ENHKEY_USAGE usage;
620
621     TRACE("(%p, %s)\n", pCertContext, debugstr_a(pszUsageIdentifier));
622
623     size = sizeof(usage);
624     ret = CertGetEnhancedKeyUsage(pCertContext,
625      CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, &usage, &size);
626     if (!ret && GetLastError() == ERROR_MORE_DATA)
627     {
628         PCERT_ENHKEY_USAGE pUsage = CryptMemAlloc(size);
629
630         if (pUsage)
631         {
632             ret = CertGetEnhancedKeyUsage(pCertContext,
633              CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG, pUsage, &size);
634             if (ret)
635             {
636                 if (pUsage->cUsageIdentifier)
637                 {
638                     DWORD i;
639                     BOOL found = FALSE;
640
641                     for (i = 0; i < pUsage->cUsageIdentifier; i++)
642                     {
643                         if (!strcmp(pUsage->rgpszUsageIdentifier[i],
644                          pszUsageIdentifier))
645                             found = TRUE;
646                         if (found && i < pUsage->cUsageIdentifier - 1)
647                             pUsage->rgpszUsageIdentifier[i] =
648                              pUsage->rgpszUsageIdentifier[i + 1];
649                     }
650                     pUsage->cUsageIdentifier--;
651                     /* Remove the usage if it's empty */
652                     if (pUsage->cUsageIdentifier)
653                         ret = CertSetEnhancedKeyUsage(pCertContext, pUsage);
654                     else
655                         ret = CertSetEnhancedKeyUsage(pCertContext, NULL);
656                 }
657             }
658             CryptMemFree(pUsage);
659         }
660         else
661             ret = FALSE;
662     }
663     else
664     {
665         /* it fit in an empty usage, therefore there's nothing to remove */
666         ret = TRUE;
667     }
668     return ret;
669 }
670
671 BOOL WINAPI CertGetValidUsages(DWORD cCerts, PCCERT_CONTEXT *rghCerts,
672  int *cNumOIDSs, LPSTR *rghOIDs, DWORD *pcbOIDs)
673 {
674     BOOL ret = TRUE;
675     DWORD i, cbOIDs = 0;
676     BOOL allUsagesValid = TRUE;
677     CERT_ENHKEY_USAGE validUsages = { 0, NULL };
678
679     TRACE("(%ld, %p, %p, %p, %ld)\n", cCerts, *rghCerts, cNumOIDSs,
680      rghOIDs, *pcbOIDs);
681
682     for (i = 0; ret && i < cCerts; i++)
683     {
684         CERT_ENHKEY_USAGE usage;
685         DWORD size = sizeof(usage);
686
687         ret = CertGetEnhancedKeyUsage(rghCerts[i], 0, &usage, &size);
688         /* Success is deliberately ignored: it implies all usages are valid */
689         if (!ret && GetLastError() == ERROR_MORE_DATA)
690         {
691             PCERT_ENHKEY_USAGE pUsage = CryptMemAlloc(size);
692
693             allUsagesValid = FALSE;
694             if (pUsage)
695             {
696                 ret = CertGetEnhancedKeyUsage(rghCerts[i], 0, pUsage, &size);
697                 if (ret)
698                 {
699                     if (!validUsages.cUsageIdentifier)
700                     {
701                         DWORD j;
702
703                         cbOIDs = pUsage->cUsageIdentifier * sizeof(LPSTR);
704                         validUsages.cUsageIdentifier = pUsage->cUsageIdentifier;
705                         for (j = 0; j < validUsages.cUsageIdentifier; j++)
706                             cbOIDs += lstrlenA(pUsage->rgpszUsageIdentifier[j])
707                              + 1;
708                         validUsages.rgpszUsageIdentifier =
709                          CryptMemAlloc(cbOIDs);
710                         if (validUsages.rgpszUsageIdentifier)
711                         {
712                             LPSTR nextOID = (LPSTR)
713                              ((LPBYTE)validUsages.rgpszUsageIdentifier +
714                              validUsages.cUsageIdentifier * sizeof(LPSTR));
715
716                             for (j = 0; j < validUsages.cUsageIdentifier; j++)
717                             {
718                                 validUsages.rgpszUsageIdentifier[j] = nextOID;
719                                 lstrcpyA(validUsages.rgpszUsageIdentifier[j],
720                                  pUsage->rgpszUsageIdentifier[j]);
721                                 nextOID += lstrlenA(nextOID) + 1;
722                             }
723                         }
724                         else
725                             ret = FALSE;
726                     }
727                     else
728                     {
729                         DWORD j, k, validIndexes = 0, numRemoved = 0;
730
731                         /* Merge: build a bitmap of all the indexes of
732                          * validUsages.rgpszUsageIdentifier that are in pUsage.
733                          */
734                         for (j = 0; j < pUsage->cUsageIdentifier; j++)
735                         {
736                             for (k = 0; k < validUsages.cUsageIdentifier; k++)
737                             {
738                                 if (!strcmp(pUsage->rgpszUsageIdentifier[j],
739                                  validUsages.rgpszUsageIdentifier[k]))
740                                 {
741                                     validIndexes |= (1 << k);
742                                     break;
743                                 }
744                             }
745                         }
746                         /* Merge by removing from validUsages those that are
747                          * not in the bitmap.
748                          */
749                         for (j = 0; j < validUsages.cUsageIdentifier; j++)
750                         {
751                             if (!(validIndexes & (1 << j)))
752                             {
753                                 if (j < validUsages.cUsageIdentifier - 1)
754                                 {
755                                     memcpy(&validUsages.rgpszUsageIdentifier[j],
756                                      &validUsages.rgpszUsageIdentifier[j +
757                                      numRemoved + 1],
758                                      (validUsages.cUsageIdentifier - numRemoved
759                                      - j - 1) * sizeof(LPSTR));
760                                     cbOIDs -= lstrlenA(
761                                      validUsages.rgpszUsageIdentifier[j]) + 1 +
762                                      sizeof(LPSTR);
763                                     numRemoved++;
764                                 }
765                                 else
766                                     validUsages.cUsageIdentifier--;
767                             }
768                         }
769                     }
770                 }
771                 CryptMemFree(pUsage);
772             }
773             else
774                 ret = FALSE;
775         }
776     }
777     if (ret)
778     {
779         if (allUsagesValid)
780         {
781             *cNumOIDSs = -1;
782             *pcbOIDs = 0;
783         }
784         else
785         {
786             if (!rghOIDs || *pcbOIDs < cbOIDs)
787             {
788                 *pcbOIDs = cbOIDs;
789                 SetLastError(ERROR_MORE_DATA);
790                 ret = FALSE;
791             }
792             else
793             {
794                 LPSTR nextOID = (LPSTR)((LPBYTE)rghOIDs +
795                  validUsages.cUsageIdentifier * sizeof(LPSTR));
796
797                 *pcbOIDs = cbOIDs;
798                 *cNumOIDSs = validUsages.cUsageIdentifier;
799                 for (i = 0; i < validUsages.cUsageIdentifier; i++)
800                 {
801                     rghOIDs[i] = nextOID;
802                     lstrcpyA(nextOID, validUsages.rgpszUsageIdentifier[i]);
803                     nextOID += lstrlenA(nextOID) + 1;
804                 }
805             }
806         }
807     }
808     CryptMemFree(validUsages.rgpszUsageIdentifier);
809     return ret;
810 }
811
812 /* Sets the CERT_KEY_PROV_INFO_PROP_ID property of context from pInfo, or, if
813  * pInfo is NULL, from the attributes of hProv.
814  */
815 static void CertContext_SetKeyProvInfo(PCCERT_CONTEXT context,
816  PCRYPT_KEY_PROV_INFO pInfo, HCRYPTPROV hProv)
817 {
818     CRYPT_KEY_PROV_INFO info = { 0 };
819     BOOL ret;
820
821     if (!pInfo)
822     {
823         DWORD size;
824         int len;
825
826         ret = CryptGetProvParam(hProv, PP_CONTAINER, NULL, &size, 0);
827         if (ret)
828         {
829             LPSTR szContainer = CryptMemAlloc(size);
830
831             if (szContainer)
832             {
833                 ret = CryptGetProvParam(hProv, PP_CONTAINER,
834                  (BYTE *)szContainer, &size, 0);
835                 if (ret)
836                 {
837                     len = MultiByteToWideChar(CP_ACP, 0, szContainer, -1,
838                      NULL, 0);
839                     if (len)
840                     {
841                         info.pwszContainerName = CryptMemAlloc(len *
842                          sizeof(WCHAR));
843                         len = MultiByteToWideChar(CP_ACP, 0, szContainer, -1,
844                          info.pwszContainerName, len);
845                     }
846                 }
847                 CryptMemFree(szContainer);
848             }
849         }
850         ret = CryptGetProvParam(hProv, PP_NAME, NULL, &size, 0);
851         if (ret)
852         {
853             LPSTR szProvider = CryptMemAlloc(size);
854
855             if (szProvider)
856             {
857                 ret = CryptGetProvParam(hProv, PP_NAME, (BYTE *)szProvider,
858                  &size, 0);
859                 if (ret)
860                 {
861                     len = MultiByteToWideChar(CP_ACP, 0, szProvider, -1,
862                      NULL, 0);
863                     if (len)
864                     {
865                         info.pwszProvName = CryptMemAlloc(len *
866                          sizeof(WCHAR));
867                         len = MultiByteToWideChar(CP_ACP, 0, szProvider, -1,
868                          info.pwszProvName, len);
869                     }
870                 }
871                 CryptMemFree(szProvider);
872             }
873         }
874         size = sizeof(info.dwKeySpec);
875         ret = CryptGetProvParam(hProv, PP_KEYSPEC, (LPBYTE)&info.dwKeySpec,
876          &size, 0);
877         if (!ret)
878             info.dwKeySpec = AT_SIGNATURE;
879         size = sizeof(info.dwProvType);
880         ret = CryptGetProvParam(hProv, PP_PROVTYPE, (LPBYTE)&info.dwProvType,
881          &size, 0);
882         if (!ret)
883             info.dwProvType = PROV_RSA_FULL;
884         pInfo = &info;
885     }
886
887     ret = CertSetCertificateContextProperty(context, CERT_KEY_PROV_INFO_PROP_ID,
888      0, pInfo);
889
890     if (pInfo == &info)
891     {
892         CryptMemFree(info.pwszContainerName);
893         CryptMemFree(info.pwszProvName);
894     }
895 }
896
897 /* Creates a signed certificate context from the unsigned, encoded certificate
898  * in blob, using the crypto provider hProv and the signature algorithm sigAlgo.
899  */
900 static PCCERT_CONTEXT CRYPT_CreateSignedCert(PCRYPT_DER_BLOB blob,
901  HCRYPTPROV hProv, PCRYPT_ALGORITHM_IDENTIFIER sigAlgo)
902 {
903     PCCERT_CONTEXT context = NULL;
904     BOOL ret;
905     DWORD sigSize = 0;
906
907     ret = CryptSignCertificate(hProv, AT_SIGNATURE, X509_ASN_ENCODING,
908      blob->pbData, blob->cbData, sigAlgo, NULL, NULL, &sigSize);
909     if (ret)
910     {
911         LPBYTE sig = CryptMemAlloc(sigSize);
912
913         ret = CryptSignCertificate(hProv, AT_SIGNATURE, X509_ASN_ENCODING,
914          blob->pbData, blob->cbData, sigAlgo, NULL, sig, &sigSize);
915         if (ret)
916         {
917             CERT_SIGNED_CONTENT_INFO signedInfo;
918             BYTE *encodedSignedCert = NULL;
919             DWORD encodedSignedCertSize = 0;
920
921             signedInfo.ToBeSigned.cbData = blob->cbData;
922             signedInfo.ToBeSigned.pbData = blob->pbData;
923             memcpy(&signedInfo.SignatureAlgorithm, sigAlgo,
924              sizeof(signedInfo.SignatureAlgorithm));
925             signedInfo.Signature.cbData = sigSize;
926             signedInfo.Signature.pbData = sig;
927             signedInfo.Signature.cUnusedBits = 0;
928             ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_CERT,
929              &signedInfo, CRYPT_ENCODE_ALLOC_FLAG, NULL,
930              (BYTE *)&encodedSignedCert, &encodedSignedCertSize);
931             if (ret)
932             {
933                 context = CertCreateCertificateContext(X509_ASN_ENCODING,
934                  encodedSignedCert, encodedSignedCertSize);
935                 LocalFree(encodedSignedCert);
936             }
937         }
938         CryptMemFree(sig);
939     }
940     return context;
941 }
942
943 /* Copies data from the parameters into info, where:
944  * pSubjectIssuerBlob: Specifies both the subject and issuer for info.
945  *                     Must not be NULL
946  * pSignatureAlgorithm: Optional.
947  * pStartTime: The starting time of the certificate.  If NULL, the current
948  *             system time is used.
949  * pEndTime: The ending time of the certificate.  If NULL, one year past the
950  *           starting time is used.
951  * pubKey: The public key of the certificate.  Must not be NULL.
952  * pExtensions: Extensions to be included with the certificate.  Optional.
953  */
954 static void CRYPT_MakeCertInfo(PCERT_INFO info,
955  PCERT_NAME_BLOB pSubjectIssuerBlob,
956  PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, PSYSTEMTIME pStartTime,
957  PSYSTEMTIME pEndTime, PCERT_PUBLIC_KEY_INFO pubKey,
958  PCERT_EXTENSIONS pExtensions)
959 {
960     /* FIXME: what serial number to use? */
961     static const BYTE serialNum[] = { 1 };
962
963     assert(info);
964     assert(pSubjectIssuerBlob);
965     assert(pubKey);
966
967     info->dwVersion = CERT_V3;
968     info->SerialNumber.cbData = sizeof(serialNum);
969     info->SerialNumber.pbData = (LPBYTE)serialNum;
970     if (pSignatureAlgorithm)
971         memcpy(&info->SignatureAlgorithm, pSignatureAlgorithm,
972          sizeof(info->SignatureAlgorithm));
973     else
974     {
975         info->SignatureAlgorithm.pszObjId = szOID_RSA_SHA1RSA;
976         info->SignatureAlgorithm.Parameters.cbData = 0;
977         info->SignatureAlgorithm.Parameters.pbData = NULL;
978     }
979     info->Issuer.cbData = pSubjectIssuerBlob->cbData;
980     info->Issuer.pbData = pSubjectIssuerBlob->pbData;
981     if (pStartTime)
982         SystemTimeToFileTime(pStartTime, &info->NotBefore);
983     else
984         GetSystemTimeAsFileTime(&info->NotBefore);
985     if (pEndTime)
986         SystemTimeToFileTime(pStartTime, &info->NotAfter);
987     else
988     {
989         SYSTEMTIME endTime;
990
991         if (FileTimeToSystemTime(&info->NotBefore, &endTime))
992         {
993             endTime.wYear++;
994             SystemTimeToFileTime(&endTime, &info->NotAfter);
995         }
996     }
997     info->Subject.cbData = pSubjectIssuerBlob->cbData;
998     info->Subject.pbData = pSubjectIssuerBlob->pbData;
999     memcpy(&info->SubjectPublicKeyInfo, pubKey,
1000      sizeof(info->SubjectPublicKeyInfo));
1001     if (pExtensions)
1002     {
1003         info->cExtension = pExtensions->cExtension;
1004         info->rgExtension = pExtensions->rgExtension;
1005     }
1006     else
1007     {
1008         info->cExtension = 0;
1009         info->rgExtension = NULL;
1010     }
1011 }
1012  
1013 typedef RPC_STATUS (RPC_ENTRY *UuidCreateFunc)(UUID *);
1014 typedef RPC_STATUS (RPC_ENTRY *UuidToStringFunc)(UUID *, unsigned char **);
1015 typedef RPC_STATUS (RPC_ENTRY *RpcStringFreeFunc)(unsigned char **);
1016
1017 static HCRYPTPROV CRYPT_CreateKeyProv(void)
1018 {
1019     HCRYPTPROV hProv = 0;
1020     HMODULE rpcrt = LoadLibraryA("rpcrt4");
1021
1022     if (rpcrt)
1023     {
1024         UuidCreateFunc uuidCreate = (UuidCreateFunc)GetProcAddress(rpcrt,
1025          "UuidCreate");
1026         UuidToStringFunc uuidToString = (UuidToStringFunc)GetProcAddress(rpcrt,
1027          "UuidToString");
1028         RpcStringFreeFunc rpcStringFree = (RpcStringFreeFunc)GetProcAddress(
1029          rpcrt, "RpcStringFree");
1030
1031         if (uuidCreate && uuidToString && rpcStringFree)
1032         {
1033             UUID uuid;
1034             RPC_STATUS status = uuidCreate(&uuid);
1035
1036             if (status == RPC_S_OK || status == RPC_S_UUID_LOCAL_ONLY)
1037             {
1038                 unsigned char *uuidStr;
1039
1040                 status = uuidToString(&uuid, &uuidStr);
1041                 if (status == RPC_S_OK)
1042                 {
1043                     BOOL ret = CryptAcquireContextA(&hProv, (LPCSTR)uuidStr,
1044                      MS_DEF_PROV_A, PROV_RSA_FULL, CRYPT_NEWKEYSET);
1045
1046                     if (ret)
1047                     {
1048                         HCRYPTKEY key;
1049
1050                         ret = CryptGenKey(hProv, AT_SIGNATURE, 0, &key);
1051                         if (ret)
1052                             CryptDestroyKey(key);
1053                     }
1054                     rpcStringFree(&uuidStr);
1055                 }
1056             }
1057         }
1058         FreeLibrary(rpcrt);
1059     }
1060     return hProv;
1061 }
1062
1063 PCCERT_CONTEXT WINAPI CertCreateSelfSignCertificate(HCRYPTPROV hProv,
1064  PCERT_NAME_BLOB pSubjectIssuerBlob, DWORD dwFlags,
1065  PCRYPT_KEY_PROV_INFO pKeyProvInfo,
1066  PCRYPT_ALGORITHM_IDENTIFIER pSignatureAlgorithm, PSYSTEMTIME pStartTime,
1067  PSYSTEMTIME pEndTime, PCERT_EXTENSIONS pExtensions)
1068 {
1069     PCCERT_CONTEXT context = NULL;
1070     BOOL ret, releaseContext = FALSE;
1071     PCERT_PUBLIC_KEY_INFO pubKey = NULL;
1072     DWORD pubKeySize = 0;
1073
1074     TRACE("(0x%08lx, %p, %08lx, %p, %p, %p, %p, %p)\n", hProv,
1075      pSubjectIssuerBlob, dwFlags, pKeyProvInfo, pSignatureAlgorithm, pStartTime,
1076      pExtensions, pExtensions);
1077
1078     if (!hProv)
1079     {
1080         hProv = CRYPT_CreateKeyProv();
1081         releaseContext = TRUE;
1082     }
1083
1084     CryptExportPublicKeyInfo(hProv, AT_SIGNATURE, X509_ASN_ENCODING, NULL,
1085      &pubKeySize);
1086     pubKey = CryptMemAlloc(pubKeySize);
1087     if (pubKey)
1088     {
1089         ret = CryptExportPublicKeyInfo(hProv, AT_SIGNATURE, X509_ASN_ENCODING,
1090          pubKey, &pubKeySize);
1091         if (ret)
1092         {
1093             CERT_INFO info = { 0 };
1094             CRYPT_DER_BLOB blob = { 0, NULL };
1095             BOOL ret;
1096
1097             CRYPT_MakeCertInfo(&info, pSubjectIssuerBlob, pSignatureAlgorithm,
1098              pStartTime, pEndTime, pubKey, pExtensions);
1099             ret = CryptEncodeObjectEx(X509_ASN_ENCODING, X509_CERT_TO_BE_SIGNED,
1100              &info, CRYPT_ENCODE_ALLOC_FLAG, NULL, (BYTE *)&blob.pbData,
1101              &blob.cbData);
1102             if (ret)
1103             {
1104                 if (!(dwFlags & CERT_CREATE_SELFSIGN_NO_SIGN))
1105                     context = CRYPT_CreateSignedCert(&blob, hProv,
1106                      &info.SignatureAlgorithm);
1107                 else
1108                     context = CertCreateCertificateContext(X509_ASN_ENCODING,
1109                      blob.pbData, blob.cbData);
1110                 if (context && !(dwFlags & CERT_CREATE_SELFSIGN_NO_KEY_INFO))
1111                     CertContext_SetKeyProvInfo(context, pKeyProvInfo, hProv);
1112                 LocalFree(blob.pbData);
1113             }
1114         }
1115         CryptMemFree(pubKey);
1116     }
1117     if (releaseContext)
1118         CryptReleaseContext(hProv, 0);
1119     return context;
1120 }