VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/crypto/pkcs7-verify.cpp@ 72367

Last change on this file since 72367 was 69111, checked in by vboxsync, 7 years ago

(C) year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 36.6 KB
Line 
1/* $Id: pkcs7-verify.cpp 69111 2017-10-17 14:26:02Z vboxsync $ */
2/** @file
3 * IPRT - Crypto - PKCS \#7, Verification
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include "internal/iprt.h"
32#include <iprt/crypto/pkcs7.h>
33
34#include <iprt/err.h>
35#include <iprt/string.h>
36#include <iprt/crypto/digest.h>
37#include <iprt/crypto/pkix.h>
38#include <iprt/crypto/store.h>
39#include <iprt/crypto/x509.h>
40
41#ifdef IPRT_WITH_OPENSSL
42# include "internal/iprt-openssl.h"
43# include <openssl/pkcs7.h>
44# include <openssl/x509.h>
45# include <openssl/err.h>
46#endif
47
48
49
50#ifdef IPRT_WITH_OPENSSL
51static int rtCrPkcs7VerifySignedDataUsingOpenSsl(PCRTCRPKCS7CONTENTINFO pContentInfo, uint32_t fFlags,
52 RTCRSTORE hAdditionalCerts, RTCRSTORE hTrustedCerts,
53 void const *pvContent, uint32_t cbContent, PRTERRINFO pErrInfo)
54{
55 RT_NOREF_PV(fFlags);
56
57 /*
58 * Verify using OpenSSL.
59 */
60 int rcOssl;
61 unsigned char const *pbRawContent = RTASN1CORE_GET_RAW_ASN1_PTR(&pContentInfo->SeqCore.Asn1Core);
62 PKCS7 *pOsslPkcs7 = NULL;
63 if (d2i_PKCS7(&pOsslPkcs7, &pbRawContent, RTASN1CORE_GET_RAW_ASN1_SIZE(&pContentInfo->SeqCore.Asn1Core)) == pOsslPkcs7)
64 {
65 STACK_OF(X509) *pAddCerts = NULL;
66 if (hAdditionalCerts != NIL_RTCRSTORE)
67 rcOssl = RTCrStoreConvertToOpenSslCertStack(hAdditionalCerts, 0, (void **)&pAddCerts);
68 else
69 {
70 pAddCerts = sk_X509_new_null();
71 rcOssl = RT_LIKELY(pAddCerts != NULL) ? VINF_SUCCESS : VERR_NO_MEMORY;
72 }
73 if (RT_SUCCESS(rcOssl))
74 {
75 PCRTCRPKCS7SETOFCERTS pCerts = &pContentInfo->u.pSignedData->Certificates;
76 for (uint32_t i = 0; i < pCerts->cItems; i++)
77 if (pCerts->papItems[i]->enmChoice == RTCRPKCS7CERTCHOICE_X509)
78 rtCrOpenSslAddX509CertToStack(pAddCerts, pCerts->papItems[i]->u.pX509Cert);
79
80
81 X509_STORE *pTrustedCerts = NULL;
82 if (hTrustedCerts != NIL_RTCRSTORE)
83 rcOssl = RTCrStoreConvertToOpenSslCertStore(hTrustedCerts, 0, (void **)&pTrustedCerts);
84 if (RT_SUCCESS(rcOssl))
85 {
86 rtCrOpenSslInit();
87
88 BIO *pBioContent = BIO_new_mem_buf((void *)pvContent, cbContent);
89 if (pBioContent)
90 {
91 uint32_t fOsslFlags = PKCS7_NOCHAIN;
92 fOsslFlags |= PKCS7_NOVERIFY; // temporary hack.
93 if (PKCS7_verify(pOsslPkcs7, pAddCerts, pTrustedCerts, pBioContent, NULL /*out*/, fOsslFlags))
94 rcOssl = VINF_SUCCESS;
95 else
96 {
97 rcOssl = RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_OSSL_VERIFY_FAILED, "PKCS7_verify failed: ");
98 if (pErrInfo)
99 ERR_print_errors_cb(rtCrOpenSslErrInfoCallback, pErrInfo);
100 }
101 BIO_free(pBioContent);
102 }
103 if (pTrustedCerts)
104 X509_STORE_free(pTrustedCerts);
105 }
106 else
107 rcOssl = RTErrInfoSet(pErrInfo, rcOssl, "RTCrStoreConvertToOpenSslCertStack failed");
108 if (pAddCerts)
109 sk_X509_pop_free(pAddCerts, X509_free);
110 }
111 else
112 rcOssl = RTErrInfoSet(pErrInfo, rcOssl, "RTCrStoreConvertToOpenSslCertStack failed");
113 PKCS7_free(pOsslPkcs7);
114 }
115 else
116 rcOssl = RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_OSSL_D2I_FAILED, "d2i_PKCS7 failed");
117
118 return rcOssl;
119}
120#endif /* IPRT_WITH_OPENSSL */
121
122
123
124static int rtCrPkcs7VerifyCertUsageTimstamping(PCRTCRX509CERTIFICATE pCert, PRTERRINFO pErrInfo)
125{
126 if (!(pCert->TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_EXT_KEY_USAGE))
127 return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_KEY_USAGE_MISMATCH, "No extended key usage certificate attribute.");
128 if (!(pCert->TbsCertificate.T3.fExtKeyUsage & (RTCRX509CERT_EKU_F_TIMESTAMPING | RTCRX509CERT_EKU_F_MS_TIMESTAMP_SIGNING)))
129 return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_KEY_USAGE_MISMATCH, "fExtKeyUsage=%#x, missing %#x (time stamping)",
130 pCert->TbsCertificate.T3.fExtKeyUsage,
131 RTCRX509CERT_EKU_F_TIMESTAMPING | RTCRX509CERT_EKU_F_MS_TIMESTAMP_SIGNING);
132 return VINF_SUCCESS;
133}
134
135
136static int rtCrPkcs7VerifyCertUsageDigitalSignature(PCRTCRX509CERTIFICATE pCert, PRTERRINFO pErrInfo)
137{
138 if ( (pCert->TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_KEY_USAGE)
139 && !(pCert->TbsCertificate.T3.fKeyUsage & RTCRX509CERT_KEY_USAGE_F_DIGITAL_SIGNATURE))
140 return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_KEY_USAGE_MISMATCH, "fKeyUsage=%#x, missing %#x",
141 pCert->TbsCertificate.T3.fKeyUsage, RTCRX509CERT_KEY_USAGE_F_DIGITAL_SIGNATURE);
142 return VINF_SUCCESS;
143}
144
145
146/**
147 * @callback_method_impl{RTCRPKCS7VERIFYCERTCALLBACK,
148 * Default implementation that checks for the DigitalSignature KeyUsage bit.}
149 */
150RTDECL(int) RTCrPkcs7VerifyCertCallbackDefault(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths, uint32_t fFlags,
151 void *pvUser, PRTERRINFO pErrInfo)
152{
153 RT_NOREF_PV(hCertPaths); RT_NOREF_PV(pvUser);
154 int rc = VINF_SUCCESS;
155
156 if (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA)
157 rc = rtCrPkcs7VerifyCertUsageDigitalSignature(pCert, pErrInfo);
158
159 if ( (fFlags & RTCRPKCS7VCC_F_TIMESTAMP)
160 && RT_SUCCESS(rc))
161 rc = rtCrPkcs7VerifyCertUsageTimstamping(pCert, pErrInfo);
162
163 return rc;
164}
165
166
167/**
168 * @callback_method_impl{RTCRPKCS7VERIFYCERTCALLBACK,
169 * Standard code signing. Use this for Microsoft SPC.}
170 */
171RTDECL(int) RTCrPkcs7VerifyCertCallbackCodeSigning(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths, uint32_t fFlags,
172 void *pvUser, PRTERRINFO pErrInfo)
173{
174 RT_NOREF_PV(hCertPaths); RT_NOREF_PV(pvUser);
175 int rc = VINF_SUCCESS;
176 if (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA)
177 {
178 /*
179 * If KeyUsage is present it must include digital signature.
180 */
181 rc = rtCrPkcs7VerifyCertUsageDigitalSignature(pCert, pErrInfo);
182 if (RT_SUCCESS(rc))
183 {
184 /*
185 * The extended usage 'code signing' must be present.
186 */
187 if (!(pCert->TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_EXT_KEY_USAGE))
188 return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_KEY_USAGE_MISMATCH, "No extended key usage certificate attribute.");
189 if (!(pCert->TbsCertificate.T3.fExtKeyUsage & RTCRX509CERT_EKU_F_CODE_SIGNING))
190 return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_KEY_USAGE_MISMATCH, "fExtKeyUsage=%#x, missing %#x",
191 pCert->TbsCertificate.T3.fExtKeyUsage, RTCRX509CERT_EKU_F_CODE_SIGNING);
192 }
193 }
194
195 /*
196 * Timestamping too?
197 */
198 if ( (fFlags & RTCRPKCS7VCC_F_TIMESTAMP)
199 && RT_SUCCESS(rc))
200 rc = rtCrPkcs7VerifyCertUsageTimstamping(pCert, pErrInfo);
201
202 return rc;
203}
204
205
206/**
207 * Deals with authenticated attributes.
208 *
209 * When authenticated attributes are present (checked by caller) we must:
210 * - fish out the content type and check it against the content inof,
211 * - fish out the message digest among and check it against *phDigest,
212 * - compute the message digest of the authenticated attributes and
213 * replace *phDigest with this for the signature verification.
214 *
215 * @returns IPRT status code.
216 * @param pSignerInfo The signer info being verified.
217 * @param pSignedData The signed data.
218 * @param phDigest On input this is the digest of the content. On
219 * output it will (on success) be a reference to
220 * the message digest of the authenticated
221 * attributes. The input reference is consumed.
222 * The caller shall release the output reference.
223 * @param fFlags Flags.
224 * @param pErrInfo Extended error info, optional.
225 */
226static int rtCrPkcs7VerifySignerInfoAuthAttribs(PCRTCRPKCS7SIGNERINFO pSignerInfo, PCRTCRPKCS7SIGNEDDATA pSignedData,
227 PRTCRDIGEST phDigest, uint32_t fFlags, PRTERRINFO pErrInfo)
228{
229 /*
230 * Scan the attributes and validate the two required attributes
231 * (RFC-2315, chapter 9.2, fourth bullet). Checking that we've got exactly
232 * one of each of them is checked by the santiy checker function, so we'll
233 * just assert that it did it's job here.
234 */
235 uint32_t cContentTypes = 0;
236 uint32_t cMessageDigests = 0;
237 uint32_t i = pSignerInfo->AuthenticatedAttributes.cItems;
238 while (i-- > 0)
239 {
240 PCRTCRPKCS7ATTRIBUTE pAttrib = pSignerInfo->AuthenticatedAttributes.papItems[i];
241
242 if (RTAsn1ObjId_CompareWithString(&pAttrib->Type, RTCR_PKCS9_ID_CONTENT_TYPE_OID) == 0)
243 {
244 AssertReturn(!cContentTypes, VERR_CR_PKCS7_INTERNAL_ERROR);
245 AssertReturn(pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_OBJ_IDS, VERR_CR_PKCS7_INTERNAL_ERROR);
246 AssertReturn(pAttrib->uValues.pObjIds->cItems == 1, VERR_CR_PKCS7_INTERNAL_ERROR);
247
248 if ( !(fFlags & RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE) /* See note about microsoft below. */
249 && RTAsn1ObjId_Compare(pAttrib->uValues.pObjIds->papItems[0], &pSignedData->ContentInfo.ContentType) != 0)
250 return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_CONTENT_TYPE_ATTRIB_MISMATCH,
251 "Expected content-type %s, found %s", pAttrib->uValues.pObjIds->papItems[0]->szObjId,
252 pSignedData->ContentInfo.ContentType.szObjId);
253 cContentTypes++;
254 }
255 else if (RTAsn1ObjId_CompareWithString(&pAttrib->Type, RTCR_PKCS9_ID_MESSAGE_DIGEST_OID) == 0)
256 {
257 AssertReturn(!cMessageDigests, VERR_CR_PKCS7_INTERNAL_ERROR);
258 AssertReturn(pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_OCTET_STRINGS, VERR_CR_PKCS7_INTERNAL_ERROR);
259 AssertReturn(pAttrib->uValues.pOctetStrings->cItems == 1, VERR_CR_PKCS7_INTERNAL_ERROR);
260
261 if (!RTCrDigestMatch(*phDigest,
262 pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.uData.pv,
263 pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb))
264 {
265 size_t cbHash = RTCrDigestGetHashSize(*phDigest);
266 if (cbHash != pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb)
267 return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_MESSAGE_DIGEST_ATTRIB_MISMATCH,
268 "Authenticated message-digest attribute mismatch: cbHash=%#zx cbValue=%#x",
269 cbHash, pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb);
270 return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_MESSAGE_DIGEST_ATTRIB_MISMATCH,
271 "Authenticated message-digest attribute mismatch (cbHash=%#zx):\n"
272 "signed: %.*Rhxs\n"
273 "our: %.*Rhxs\n",
274 cbHash,
275 cbHash, pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.uData.pv,
276 cbHash, RTCrDigestGetHash(*phDigest));
277 }
278 cMessageDigests++;
279 }
280 }
281
282 /*
283 * Full error reporting here as we don't currently extensively santiy check
284 * counter signatures.
285 * Note! Microsoft includes content info in their timestamp counter signatures,
286 * at least for vista, despite the RFC-3852 stating counter signatures
287 * "MUST NOT contain a content-type".
288 */
289 if (RT_UNLIKELY( cContentTypes != 1
290 && !(fFlags & RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE)))
291 return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_MISSING_CONTENT_TYPE_ATTRIB,
292 "Missing authenticated content-type attribute.");
293 if (RT_UNLIKELY(cMessageDigests != 1))
294 return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_MISSING_MESSAGE_DIGEST_ATTRIB,
295 "Missing authenticated message-digest attribute.");
296
297 /*
298 * Calculate the digest of the authenticated attributes for use in the
299 * signature validation.
300 */
301 if ( pSignerInfo->DigestAlgorithm.Parameters.enmType != RTASN1TYPE_NULL
302 && pSignerInfo->DigestAlgorithm.Parameters.enmType != RTASN1TYPE_NOT_PRESENT)
303 return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_DIGEST_PARAMS_NOT_IMPL, "Digest algorithm has unsupported parameters");
304
305 RTCRDIGEST hDigest;
306 int rc = RTCrDigestCreateByObjId(&hDigest, &pSignerInfo->DigestAlgorithm.Algorithm);
307 if (RT_SUCCESS(rc))
308 {
309 RTCrDigestRelease(*phDigest);
310 *phDigest = hDigest;
311
312 /* ASSUMES that the attributes are encoded according to DER. */
313 uint8_t const *pbData = (uint8_t const *)RTASN1CORE_GET_RAW_ASN1_PTR(&pSignerInfo->AuthenticatedAttributes.SetCore.Asn1Core);
314 uint32_t cbData = RTASN1CORE_GET_RAW_ASN1_SIZE(&pSignerInfo->AuthenticatedAttributes.SetCore.Asn1Core);
315 uint8_t bSetOfTag = ASN1_TAG_SET | ASN1_TAGCLASS_UNIVERSAL | ASN1_TAGFLAG_CONSTRUCTED;
316 rc = RTCrDigestUpdate(hDigest, &bSetOfTag, sizeof(bSetOfTag)); /* Replace the implict tag with a SET-OF tag. */
317 if (RT_SUCCESS(rc))
318 rc = RTCrDigestUpdate(hDigest, pbData + sizeof(bSetOfTag), cbData - sizeof(bSetOfTag)); /* Skip the implicit tag. */
319 if (RT_SUCCESS(rc))
320 rc = RTCrDigestFinal(hDigest, NULL, 0);
321 }
322 return rc;
323}
324
325
326/**
327 * Find the handle to the digest given by the specified SignerInfo.
328 *
329 * @returns IPRT status code
330 * @param phDigest Where to return a referenced digest handle on
331 * success.
332 * @param pSignedData The signed data structure.
333 * @param pSignerInfo The signer info.
334 * @param pahDigests Array of content digests that runs parallel to
335 * pSignedData->DigestAlgorithms.
336 * @param pErrInfo Where to store additional error details,
337 * optional.
338 */
339static int rtCrPkcs7VerifyFindDigest(PRTCRDIGEST phDigest, PCRTCRPKCS7SIGNEDDATA pSignedData,
340 PCRTCRPKCS7SIGNERINFO pSignerInfo, PRTCRDIGEST pahDigests, PRTERRINFO pErrInfo)
341{
342 uint32_t iDigest = pSignedData->DigestAlgorithms.cItems;
343 while (iDigest-- > 0)
344 if (RTCrX509AlgorithmIdentifier_Compare(pSignedData->DigestAlgorithms.papItems[iDigest],
345 &pSignerInfo->DigestAlgorithm) == 0)
346 {
347 RTCRDIGEST hDigest = pahDigests[iDigest];
348 uint32_t cRefs = RTCrDigestRetain(hDigest);
349 AssertReturn(cRefs != UINT32_MAX, VERR_CR_PKCS7_INTERNAL_ERROR);
350 *phDigest = hDigest;
351 return VINF_SUCCESS;
352 }
353 *phDigest = NIL_RTCRDIGEST; /* Make gcc happy. */
354 return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_DIGEST_ALGO_NOT_FOUND_IN_LIST,
355 "SignerInfo.DigestAlgorithm %s not found.",
356 pSignerInfo->DigestAlgorithm.Algorithm.szObjId);
357}
358
359
360/**
361 * Verifies one signature on a PKCS \#7 SignedData.
362 *
363 * @returns IPRT status code.
364 * @param pSignerInfo The signature.
365 * @param pSignedData The SignedData.
366 * @param hDigests The digest corresponding to
367 * pSignerInfo->DigestAlgorithm.
368 * @param fFlags Verficiation flags.
369 * @param hAdditionalCerts Store containing optional certificates,
370 * optional.
371 * @param hTrustedCerts Store containing trusted certificates, required.
372 * @param pValidationTime The time we're supposed to validate the
373 * certificates chains at.
374 * @param pfnVerifyCert Signing certificate verification callback.
375 * @param fVccFlags Signing certificate verification callback flags.
376 * @param pvUser Callback parameter.
377 * @param pErrInfo Where to store additional error details,
378 * optional.
379 */
380static int rtCrPkcs7VerifySignerInfo(PCRTCRPKCS7SIGNERINFO pSignerInfo, PCRTCRPKCS7SIGNEDDATA pSignedData,
381 RTCRDIGEST hDigest, uint32_t fFlags, RTCRSTORE hAdditionalCerts, RTCRSTORE hTrustedCerts,
382 PCRTTIMESPEC pValidationTime, PFNRTCRPKCS7VERIFYCERTCALLBACK pfnVerifyCert,
383 uint32_t fVccFlags, void *pvUser, PRTERRINFO pErrInfo)
384{
385 /*
386 * Locate the certificate used for signing.
387 */
388 PCRTCRCERTCTX pSignerCertCtx = NULL;
389 PCRTCRX509CERTIFICATE pSignerCert = NULL;
390 RTCRSTORE hSignerCertSrc = hTrustedCerts;
391 if (hSignerCertSrc != NIL_RTCRSTORE)
392 pSignerCertCtx = RTCrStoreCertByIssuerAndSerialNo(hSignerCertSrc, &pSignerInfo->IssuerAndSerialNumber.Name,
393 &pSignerInfo->IssuerAndSerialNumber.SerialNumber);
394 if (!pSignerCertCtx)
395 {
396 hSignerCertSrc = hAdditionalCerts;
397 if (hSignerCertSrc != NIL_RTCRSTORE)
398 pSignerCertCtx = RTCrStoreCertByIssuerAndSerialNo(hSignerCertSrc, &pSignerInfo->IssuerAndSerialNumber.Name,
399 &pSignerInfo->IssuerAndSerialNumber.SerialNumber);
400 }
401 if (pSignerCertCtx)
402 pSignerCert = pSignerCertCtx->pCert;
403 else
404 {
405 hSignerCertSrc = NULL;
406 pSignerCert = RTCrPkcs7SetOfCerts_FindX509ByIssuerAndSerialNumber(&pSignedData->Certificates,
407 &pSignerInfo->IssuerAndSerialNumber.Name,
408 &pSignerInfo->IssuerAndSerialNumber.SerialNumber);
409 if (!pSignerCert)
410 return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_SIGNED_DATA_CERT_NOT_FOUND,
411 "Certificate not found: serial=%.*Rhxs",
412 pSignerInfo->IssuerAndSerialNumber.SerialNumber.Asn1Core.cb,
413 pSignerInfo->IssuerAndSerialNumber.SerialNumber.Asn1Core.uData.pv);
414 }
415
416 /*
417 * If not a trusted certificate, we'll have to build certificate paths
418 * and verify them. If no valid paths are found, this step will fail.
419 */
420 int rc = VINF_SUCCESS;
421 if ( hSignerCertSrc == NIL_RTCRSTORE
422 || hSignerCertSrc != hTrustedCerts)
423 {
424 RTCRX509CERTPATHS hCertPaths;
425 rc = RTCrX509CertPathsCreate(&hCertPaths, pSignerCert);
426 if (RT_SUCCESS(rc))
427 {
428 rc = RTCrX509CertPathsSetValidTimeSpec(hCertPaths, pValidationTime);
429 if (hTrustedCerts != NIL_RTCRSTORE && RT_SUCCESS(rc))
430 rc = RTCrX509CertPathsSetTrustedStore(hCertPaths, hTrustedCerts);
431 if (hAdditionalCerts != NIL_RTCRSTORE && RT_SUCCESS(rc))
432 rc = RTCrX509CertPathsSetUntrustedStore(hCertPaths, hAdditionalCerts);
433 if (pSignedData->Certificates.cItems > 0 && RT_SUCCESS(rc))
434 rc = RTCrX509CertPathsSetUntrustedSet(hCertPaths, &pSignedData->Certificates);
435 if (RT_SUCCESS(rc))
436 {
437 rc = RTCrX509CertPathsBuild(hCertPaths, pErrInfo);
438 if (RT_SUCCESS(rc))
439 rc = RTCrX509CertPathsValidateAll(hCertPaths, NULL, pErrInfo);
440
441 /*
442 * Check that the certificate purpose and whatnot matches what
443 * is being signed.
444 */
445 if (RT_SUCCESS(rc))
446 rc = pfnVerifyCert(pSignerCert, hCertPaths, fVccFlags, pvUser, pErrInfo);
447 }
448 else
449 RTErrInfoSetF(pErrInfo, rc, "Error configuring path builder: %Rrc", rc);
450 RTCrX509CertPathsRelease(hCertPaths);
451 }
452 }
453 /*
454 * Check that the certificate purpose matches what is signed.
455 */
456 else
457 rc = pfnVerifyCert(pSignerCert, NIL_RTCRX509CERTPATHS, fVccFlags, pvUser, pErrInfo);
458
459 /*
460 * Reference the digest so we can safely replace with one on the
461 * authenticated attributes below.
462 */
463 if ( RT_SUCCESS(rc)
464 && RTCrDigestRetain(hDigest) != UINT32_MAX)
465 {
466 /*
467 * If there are authenticated attributes, we've got more work before we
468 * can verify the signature.
469 */
470 if ( RT_SUCCESS(rc)
471 && RTCrPkcs7Attributes_IsPresent(&pSignerInfo->AuthenticatedAttributes))
472 rc = rtCrPkcs7VerifySignerInfoAuthAttribs(pSignerInfo, pSignedData, &hDigest, fFlags, pErrInfo);
473
474 /*
475 * Verify the signature.
476 */
477 if (RT_SUCCESS(rc))
478 {
479 RTCRPKIXSIGNATURE hSignature;
480 rc = RTCrPkixSignatureCreateByObjId(&hSignature,
481 &pSignerCert->TbsCertificate.SubjectPublicKeyInfo.Algorithm.Algorithm,
482 false /*fSigning*/,
483 &pSignerCert->TbsCertificate.SubjectPublicKeyInfo.SubjectPublicKey,
484 &pSignerInfo->DigestEncryptionAlgorithm.Parameters);
485 if (RT_SUCCESS(rc))
486 {
487 /** @todo Check that DigestEncryptionAlgorithm is compatible with hSignature
488 * (this is not vital). */
489 rc = RTCrPkixSignatureVerifyOctetString(hSignature, hDigest, &pSignerInfo->EncryptedDigest);
490 if (RT_FAILURE(rc))
491 rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_SIGNATURE_VERIFICATION_FAILED,
492 "Signature verficiation failed: %Rrc", rc);
493 RTCrPkixSignatureRelease(hSignature);
494 }
495 else
496 rc = RTErrInfoSetF(pErrInfo, rc, "Failure to instantiate public key algorithm [IPRT]: %s (%s)",
497 pSignerCert->TbsCertificate.SubjectPublicKeyInfo.Algorithm.Algorithm.szObjId,
498 pSignerInfo->DigestEncryptionAlgorithm.Algorithm.szObjId);
499 }
500
501 RTCrDigestRelease(hDigest);
502 }
503 else if (RT_SUCCESS(rc))
504 rc = VERR_CR_PKCS7_INTERNAL_ERROR;
505 RTCrCertCtxRelease(pSignerCertCtx);
506 return rc;
507}
508
509
510/**
511 * Verifies a counter signature.
512 *
513 * @returns IPRT status code.
514 * @param pCounterSignerInfo The counter signature.
515 * @param pPrimarySignerInfo The primary signature (can be a counter
516 * signature too if nested).
517 * @param pSignedData The SignedData.
518 * @param fFlags Verficiation flags.
519 * @param hAdditionalCerts Store containing optional certificates,
520 * optional.
521 * @param hTrustedCerts Store containing trusted certificates, required.
522 * @param pValidationTime The time we're supposed to validate the
523 * certificates chains at.
524 * @param pfnVerifyCert Signing certificate verification callback.
525 * @param fVccFlags Signing certificate verification callback flags.
526 * @param pvUser Callback parameter.
527 * @param pErrInfo Where to store additional error details,
528 * optional.
529 */
530static int rtCrPkcs7VerifyCounterSignerInfo(PCRTCRPKCS7SIGNERINFO pCounterSignerInfo, PCRTCRPKCS7SIGNERINFO pPrimarySignerInfo,
531 PCRTCRPKCS7SIGNEDDATA pSignedData, uint32_t fFlags,
532 RTCRSTORE hAdditionalCerts, RTCRSTORE hTrustedCerts, PCRTTIMESPEC pValidationTime,
533 PFNRTCRPKCS7VERIFYCERTCALLBACK pfnVerifyCert, uint32_t fVccFlags,
534 void *pvUser, PRTERRINFO pErrInfo)
535{
536 /*
537 * Calculate the digest we need to verify.
538 */
539 RTCRDIGEST hDigest;
540 int rc = RTCrDigestCreateByObjId(&hDigest, &pCounterSignerInfo->DigestAlgorithm.Algorithm);
541 if (RT_SUCCESS(rc))
542 {
543 rc = RTCrDigestUpdate(hDigest,
544 pPrimarySignerInfo->EncryptedDigest.Asn1Core.uData.pv,
545 pPrimarySignerInfo->EncryptedDigest.Asn1Core.cb);
546 if (RT_SUCCESS(rc))
547 rc = RTCrDigestFinal(hDigest, NULL, 0);
548 if (RT_SUCCESS(rc))
549 {
550 /*
551 * Pass it on to the common SignerInfo verifier function.
552 */
553 rc = rtCrPkcs7VerifySignerInfo(pCounterSignerInfo, pSignedData, hDigest,
554 fFlags | RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE,
555 hAdditionalCerts, hTrustedCerts, pValidationTime,
556 pfnVerifyCert, fVccFlags, pvUser, pErrInfo);
557
558 }
559 else
560 rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_DIGEST_CALC_ERROR,
561 "Hashing for counter signature failed unexpectedly: %Rrc", rc);
562 RTCrDigestRelease(hDigest);
563 }
564 else
565 rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_DIGEST_CREATE_ERROR, "Error creating digest for '%s': %Rrc",
566 pCounterSignerInfo->DigestAlgorithm.Algorithm.szObjId, rc);
567
568 return rc;
569}
570
571
572RTDECL(int) RTCrPkcs7VerifySignedData(PCRTCRPKCS7CONTENTINFO pContentInfo, uint32_t fFlags,
573 RTCRSTORE hAdditionalCerts, RTCRSTORE hTrustedCerts,
574 PCRTTIMESPEC pValidationTime, PFNRTCRPKCS7VERIFYCERTCALLBACK pfnVerifyCert, void *pvUser,
575 PRTERRINFO pErrInfo)
576{
577 /*
578 * Check the input.
579 */
580 if (pfnVerifyCert)
581 AssertPtrReturn(pfnVerifyCert, VERR_INVALID_POINTER);
582 else
583 pfnVerifyCert = RTCrPkcs7VerifyCertCallbackDefault;
584
585 if (!RTCrPkcs7ContentInfo_IsSignedData(pContentInfo))
586 return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_NOT_SIGNED_DATA, "Not PKCS #7 SignedData.");
587 PCRTCRPKCS7SIGNEDDATA pSignedData = pContentInfo->u.pSignedData;
588 int rc = RTCrPkcs7SignedData_CheckSanity(pSignedData, 0, pErrInfo, "");
589 if (RT_FAILURE(rc))
590 return rc;
591
592 /*
593 * Hash the content info.
594 */
595 /* Exactly what the content is, for some stupid reason unnecessarily
596 complicated. Figure it out here as we'll need it for the OpenSSL code
597 path as well. */
598 void const *pvContent = pSignedData->ContentInfo.Content.Asn1Core.uData.pv;
599 uint32_t cbContent = pSignedData->ContentInfo.Content.Asn1Core.cb;
600 if (pSignedData->ContentInfo.Content.pEncapsulated)
601 {
602 pvContent = pSignedData->ContentInfo.Content.pEncapsulated->uData.pv;
603 cbContent = pSignedData->ContentInfo.Content.pEncapsulated->cb;
604 }
605
606 /* Check that there aren't too many or too few hash algorithms for our
607 implementation and purposes. */
608 RTCRDIGEST ahDigests[2];
609 uint32_t const cDigests = pSignedData->DigestAlgorithms.cItems;
610 if (!cDigests) /** @todo we might have to support this... */
611 return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_NO_DIGEST_ALGORITHMS, "No digest algorithms");
612
613 if (cDigests > RT_ELEMENTS(ahDigests))
614 return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_TOO_MANY_DIGEST_ALGORITHMS,
615 "Too many digest algorithm: cAlgorithms=%u", cDigests);
616
617 /* Create the message digest calculators. */
618 rc = VERR_CR_PKCS7_NO_DIGEST_ALGORITHMS;
619 uint32_t i;
620 for (i = 0; i < cDigests; i++)
621 {
622 rc = RTCrDigestCreateByObjId(&ahDigests[i], &pSignedData->DigestAlgorithms.papItems[i]->Algorithm);
623 if (RT_FAILURE(rc))
624 {
625 rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_DIGEST_CREATE_ERROR, "Error creating digest for '%s': %Rrc",
626 pSignedData->DigestAlgorithms.papItems[i]->Algorithm.szObjId, rc);
627 break;
628 }
629 }
630 if (RT_SUCCESS(rc))
631 {
632 /* Hash the content. */
633 for (i = 0; i < cDigests && RT_SUCCESS(rc); i++)
634 {
635 rc = RTCrDigestUpdate(ahDigests[i], pvContent, cbContent);
636 if (RT_SUCCESS(rc))
637 rc = RTCrDigestFinal(ahDigests[i], NULL, 0);
638 }
639 if (RT_SUCCESS(rc))
640 {
641 /*
642 * Validate the signed infos.
643 */
644 uint32_t fPrimaryVccFlags = !(fFlags & RTCRPKCS7VERIFY_SD_F_USAGE_TIMESTAMPING)
645 ? RTCRPKCS7VCC_F_SIGNED_DATA : RTCRPKCS7VCC_F_TIMESTAMP;
646 rc = VERR_CR_PKCS7_NO_SIGNER_INFOS;
647 for (i = 0; i < pSignedData->SignerInfos.cItems; i++)
648 {
649 PCRTCRPKCS7SIGNERINFO pSignerInfo = pSignedData->SignerInfos.papItems[i];
650 RTCRDIGEST hThisDigest = NIL_RTCRDIGEST; /* (gcc maybe incredible stupid.) */
651 rc = rtCrPkcs7VerifyFindDigest(&hThisDigest, pSignedData, pSignerInfo, ahDigests, pErrInfo);
652 if (RT_FAILURE(rc))
653 break;
654
655 /*
656 * See if we can find a trusted signing time.
657 * (Note that while it would make sense splitting up this function,
658 * we need to carry a lot of arguments around, so better not.)
659 */
660 bool fDone = false;
661 PCRTCRPKCS7SIGNERINFO pSigningTimeSigner = NULL;
662 PCRTASN1TIME pSignedTime;
663 while ( !fDone
664 && (pSignedTime = RTCrPkcs7SignerInfo_GetSigningTime(pSignerInfo, &pSigningTimeSigner)) != NULL)
665 {
666 RTTIMESPEC ThisValidationTime;
667 if (RT_LIKELY(RTTimeImplode(&ThisValidationTime, &pSignedTime->Time)))
668 {
669 if (pSigningTimeSigner == pSignerInfo)
670 {
671 if (fFlags & RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY)
672 continue;
673 rc = rtCrPkcs7VerifySignerInfo(pSignerInfo, pSignedData, hThisDigest, fFlags,
674 hAdditionalCerts, hTrustedCerts, &ThisValidationTime,
675 pfnVerifyCert, fPrimaryVccFlags | RTCRPKCS7VCC_F_TIMESTAMP,
676 pvUser, pErrInfo);
677 }
678 else
679 {
680 rc = VINF_SUCCESS;
681 if (!(fFlags & RTCRPKCS7VERIFY_SD_F_USE_SIGNING_TIME_UNVERIFIED))
682 rc = rtCrPkcs7VerifyCounterSignerInfo(pSigningTimeSigner, pSignerInfo, pSignedData, fFlags,
683 hAdditionalCerts, hTrustedCerts, &ThisValidationTime,
684 pfnVerifyCert, RTCRPKCS7VCC_F_TIMESTAMP, pvUser, pErrInfo);
685 if (RT_SUCCESS(rc))
686 rc = rtCrPkcs7VerifySignerInfo(pSignerInfo, pSignedData, hThisDigest, fFlags, hAdditionalCerts,
687 hTrustedCerts, &ThisValidationTime,
688 pfnVerifyCert, fPrimaryVccFlags, pvUser, pErrInfo);
689 }
690 fDone = RT_SUCCESS(rc)
691 || (fFlags & RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT);
692 }
693 else
694 {
695 rc = RTErrInfoSet(pErrInfo, VERR_INTERNAL_ERROR_3, "RTTimeImplode failed");
696 fDone = true;
697 }
698 }
699
700 /*
701 * If not luck, check for microsoft timestamp counter signatures.
702 */
703 if (!fDone && !(fFlags & RTCRPKCS7VERIFY_SD_F_IGNORE_MS_TIMESTAMP))
704 {
705 PCRTCRPKCS7CONTENTINFO pSignedTimestamp = NULL;
706 pSignedTime = RTCrPkcs7SignerInfo_GetMsTimestamp(pSignerInfo, &pSignedTimestamp);
707 if (pSignedTime)
708 {
709 RTTIMESPEC ThisValidationTime;
710 if (RT_LIKELY(RTTimeImplode(&ThisValidationTime, &pSignedTime->Time)))
711 {
712 rc = VINF_SUCCESS;
713 if (!(fFlags & RTCRPKCS7VERIFY_SD_F_USE_MS_TIMESTAMP_UNVERIFIED))
714 rc = RTCrPkcs7VerifySignedData(pSignedTimestamp,
715 fFlags | RTCRPKCS7VERIFY_SD_F_IGNORE_MS_TIMESTAMP
716 | RTCRPKCS7VERIFY_SD_F_USAGE_TIMESTAMPING,
717 hAdditionalCerts, hTrustedCerts, &ThisValidationTime,
718 pfnVerifyCert, pvUser, pErrInfo);
719
720 if (RT_SUCCESS(rc))
721 rc = rtCrPkcs7VerifySignerInfo(pSignerInfo, pSignedData, hThisDigest, fFlags, hAdditionalCerts,
722 hTrustedCerts, &ThisValidationTime,
723 pfnVerifyCert, fPrimaryVccFlags, pvUser, pErrInfo);
724 fDone = RT_SUCCESS(rc)
725 || (fFlags & RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT);
726 }
727 else
728 {
729 rc = RTErrInfoSet(pErrInfo, VERR_INTERNAL_ERROR_3, "RTTimeImplode failed");
730 fDone = true;
731 }
732
733 }
734 }
735
736 /*
737 * No valid signing time found, use the one specified instead.
738 */
739 if (!fDone)
740 rc = rtCrPkcs7VerifySignerInfo(pSignerInfo, pSignedData, hThisDigest, fFlags, hAdditionalCerts, hTrustedCerts,
741 pValidationTime, pfnVerifyCert, fPrimaryVccFlags, pvUser, pErrInfo);
742 RTCrDigestRelease(hThisDigest);
743 if (RT_FAILURE(rc))
744 break;
745 }
746 }
747 else
748 rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_DIGEST_CALC_ERROR,
749 "Hashing content failed unexpectedly (i=%u): %Rrc", i, rc);
750
751 /* Clean up digests. */
752 i = cDigests;
753 }
754 while (i-- > 0)
755 {
756 int rc2 = RTCrDigestRelease(ahDigests[i]);
757 AssertRC(rc2);
758 }
759
760
761#ifdef IPRT_WITH_OPENSSL
762 /*
763 * Verify using OpenSSL and combine the results (should be identical).
764 */
765 /** @todo figure out how to verify MS timstamp signatures using OpenSSL. */
766 if (fFlags & RTCRPKCS7VERIFY_SD_F_USAGE_TIMESTAMPING)
767 return rc;
768 int rcOssl = rtCrPkcs7VerifySignedDataUsingOpenSsl(pContentInfo, fFlags, hAdditionalCerts, hTrustedCerts,
769 pvContent, cbContent, RT_SUCCESS(rc) ? pErrInfo : NULL);
770 if (RT_SUCCESS(rcOssl) && RT_SUCCESS(rc))
771 return rc;
772// AssertMsg(RT_FAILURE_NP(rcOssl) && RT_FAILURE_NP(rc), ("%Rrc, %Rrc\n", rcOssl, rc));
773 if (RT_FAILURE(rc))
774 return rc;
775 return rcOssl;
776#else
777 return rc;
778#endif
779}
780
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette