VirtualBox

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

Last change on this file since 98103 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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