VirtualBox

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

Last change on this file since 76452 was 74828, checked in by vboxsync, 6 years ago

IPRT/pkcs7: scm fix. bugref:9232

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