VirtualBox

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

Last change on this file since 91692 was 86610, checked in by vboxsync, 4 years ago

SUPHArdNt,IPRT: Recognize WHQL/attestation signatures from MS. Corrected RTCRX509TBSCERTIFICATE::T3::fExtKeyUsage type (32 -> 64 bit) as it has been to small for a while now. bugref:3103

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