VirtualBox

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

Last change on this file since 65796 was 64883, checked in by vboxsync, 8 years ago

IPRT/ASN.1: Refactored array handling (SET OF, SEQUENCE OF) to use a pointer array instead of an object instance array. The old approach would move objects around in memory after they'd be initialized/decoded, making certain core optimziations involving pointers to object members impossible, as well as causing potentially causing trouble when modifying structures that takes down pointers after decoding. Fixed validation bug in rtCrX509Name_CheckSanityExtra where it didn't check that the RDNs had subitems but instead checked the parent twice (slight risk).

  • 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 64883 2016-12-15 15:26:20Z vboxsync $ */
2/** @file
3 * IPRT - Crypto - PKCS \#7, Verification
4 */
5
6/*
7 * Copyright (C) 2006-2016 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