VirtualBox

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

Last change on this file since 84889 was 84380, checked in by vboxsync, 5 years ago

IPRT/RTCrPkcs7: Added RTCRPKCS7VERIFY_SD_F_CHECK_TRUST_ANCHORS. Cleaned up @todo from r138008. bugref:9699

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