VirtualBox

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

Last change on this file since 95841 was 95668, checked in by vboxsync, 2 years ago

IPRT/RTCrPkcs7: Split out RTCrPkcs7Attributes_HashAttributes from verification. bugref:8691

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