VirtualBox

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

Last change on this file since 74158 was 73665, checked in by vboxsync, 6 years ago

IPRT,SUP,Main: Working on new crypto key handling and rsa signing. bugref:9152

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