diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-11 08:17:27 +0000 |
commit | f215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch) | |
tree | 6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Runtime/common/crypto/pkcs7-verify.cpp | |
parent | Initial commit. (diff) | |
download | virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip |
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Runtime/common/crypto/pkcs7-verify.cpp')
-rw-r--r-- | src/VBox/Runtime/common/crypto/pkcs7-verify.cpp | 866 |
1 files changed, 866 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/crypto/pkcs7-verify.cpp b/src/VBox/Runtime/common/crypto/pkcs7-verify.cpp new file mode 100644 index 00000000..121ca442 --- /dev/null +++ b/src/VBox/Runtime/common/crypto/pkcs7-verify.cpp @@ -0,0 +1,866 @@ +/* $Id: pkcs7-verify.cpp $ */ +/** @file + * IPRT - Crypto - PKCS \#7, Verification + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "internal/iprt.h" +#include <iprt/crypto/pkcs7.h> + +#include <iprt/err.h> +#include <iprt/mem.h> +#include <iprt/string.h> +#include <iprt/crypto/digest.h> +#include <iprt/crypto/key.h> +#include <iprt/crypto/pkix.h> +#include <iprt/crypto/store.h> +#include <iprt/crypto/x509.h> + +#ifdef IPRT_WITH_OPENSSL +# include "internal/iprt-openssl.h" +# include "internal/openssl-pre.h" +# include <openssl/pkcs7.h> +# include <openssl/x509.h> +# include <openssl/err.h> +# include "internal/openssl-post.h" +#endif + + + +#ifdef IPRT_WITH_OPENSSL +static int rtCrPkcs7VerifySignedDataUsingOpenSsl(PCRTCRPKCS7CONTENTINFO pContentInfo, uint32_t fFlags, + RTCRSTORE hAdditionalCerts, RTCRSTORE hTrustedCerts, + void const *pvContent, size_t cbContent, PRTERRINFO pErrInfo) +{ + RT_NOREF_PV(fFlags); + + /* + * Verify using OpenSSL. ERR_PUT_error + */ + unsigned char const *pbRawContent; + uint32_t cbRawContent; + void *pvFree; + int rcOssl = RTAsn1EncodeQueryRawBits(RTCrPkcs7ContentInfo_GetAsn1Core(pContentInfo), + (const uint8_t **)&pbRawContent, &cbRawContent, &pvFree, pErrInfo); + AssertRCReturn(rcOssl, rcOssl); + + PKCS7 *pOsslPkcs7 = NULL; + PKCS7 *pOsslPkcs7Ret = d2i_PKCS7(&pOsslPkcs7, &pbRawContent, cbRawContent); + + RTMemTmpFree(pvFree); + + if (pOsslPkcs7 != NULL && pOsslPkcs7Ret == pOsslPkcs7) + { + STACK_OF(X509) *pAddCerts = NULL; + if (hAdditionalCerts != NIL_RTCRSTORE) + rcOssl = RTCrStoreConvertToOpenSslCertStack(hAdditionalCerts, 0, (void **)&pAddCerts, pErrInfo); + else + { + pAddCerts = sk_X509_new_null(); + rcOssl = RT_LIKELY(pAddCerts != NULL) ? VINF_SUCCESS : VERR_NO_MEMORY; + } + if (RT_SUCCESS(rcOssl)) + { + PCRTCRPKCS7SETOFCERTS pCerts = &pContentInfo->u.pSignedData->Certificates; + for (uint32_t i = 0; i < pCerts->cItems; i++) + if (pCerts->papItems[i]->enmChoice == RTCRPKCS7CERTCHOICE_X509) + rtCrOpenSslAddX509CertToStack(pAddCerts, pCerts->papItems[i]->u.pX509Cert, NULL); + + X509_STORE *pTrustedCerts = NULL; + if (hTrustedCerts != NIL_RTCRSTORE) + rcOssl = RTCrStoreConvertToOpenSslCertStore(hTrustedCerts, 0, (void **)&pTrustedCerts, pErrInfo); + if (RT_SUCCESS(rcOssl)) + { + rtCrOpenSslInit(); + + BIO *pBioContent = BIO_new_mem_buf((void *)pvContent, (int)cbContent); + if (pBioContent) + { + uint32_t fOsslFlags = PKCS7_NOCHAIN; + fOsslFlags |= PKCS7_NOVERIFY; // temporary hack. + if (PKCS7_verify(pOsslPkcs7, pAddCerts, pTrustedCerts, pBioContent, NULL /*out*/, fOsslFlags)) + rcOssl = VINF_SUCCESS; + else + { + rcOssl = RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_OSSL_VERIFY_FAILED, "PKCS7_verify failed: "); + if (pErrInfo) + ERR_print_errors_cb(rtCrOpenSslErrInfoCallback, pErrInfo); + } + BIO_free(pBioContent); + } + if (pTrustedCerts) + X509_STORE_free(pTrustedCerts); + } + else + rcOssl = RTErrInfoSet(pErrInfo, rcOssl, "RTCrStoreConvertToOpenSslCertStore failed"); +#include "internal/openssl-pre.h" /* Need to disable C5039 warning here. */ + if (pAddCerts) + sk_X509_pop_free(pAddCerts, X509_free); +#include "internal/openssl-post.h" + } + else + rcOssl = RTErrInfoSet(pErrInfo, rcOssl, "RTCrStoreConvertToOpenSslCertStack failed"); + PKCS7_free(pOsslPkcs7); + } + else + { + rcOssl = RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_OSSL_D2I_FAILED, "d2i_PKCS7 failed"); + if (pErrInfo) + ERR_print_errors_cb(rtCrOpenSslErrInfoCallback, pErrInfo); + } + + return rcOssl; +} +#endif /* IPRT_WITH_OPENSSL */ + + + +static int rtCrPkcs7VerifyCertUsageTimstamping(PCRTCRX509CERTIFICATE pCert, PRTERRINFO pErrInfo) +{ + if (!(pCert->TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_EXT_KEY_USAGE)) + return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_KEY_USAGE_MISMATCH, "No extended key usage certificate attribute."); + if (!(pCert->TbsCertificate.T3.fExtKeyUsage & (RTCRX509CERT_EKU_F_TIMESTAMPING | RTCRX509CERT_EKU_F_MS_TIMESTAMP_SIGNING))) + return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_KEY_USAGE_MISMATCH, "fExtKeyUsage=%#x, missing %#x (time stamping)", + pCert->TbsCertificate.T3.fExtKeyUsage, + RTCRX509CERT_EKU_F_TIMESTAMPING | RTCRX509CERT_EKU_F_MS_TIMESTAMP_SIGNING); + return VINF_SUCCESS; +} + + +static int rtCrPkcs7VerifyCertUsageDigitalSignature(PCRTCRX509CERTIFICATE pCert, PRTERRINFO pErrInfo) +{ + if ( (pCert->TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_KEY_USAGE) + && !(pCert->TbsCertificate.T3.fKeyUsage & RTCRX509CERT_KEY_USAGE_F_DIGITAL_SIGNATURE)) + return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_KEY_USAGE_MISMATCH, "fKeyUsage=%#x, missing %#x", + pCert->TbsCertificate.T3.fKeyUsage, RTCRX509CERT_KEY_USAGE_F_DIGITAL_SIGNATURE); + return VINF_SUCCESS; +} + + +/** + * @callback_method_impl{RTCRPKCS7VERIFYCERTCALLBACK, + * Default implementation that checks for the DigitalSignature KeyUsage bit.} + */ +RTDECL(int) RTCrPkcs7VerifyCertCallbackDefault(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths, uint32_t fFlags, + void *pvUser, PRTERRINFO pErrInfo) +{ + RT_NOREF_PV(hCertPaths); RT_NOREF_PV(pvUser); + int rc = VINF_SUCCESS; + + if (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA) + rc = rtCrPkcs7VerifyCertUsageDigitalSignature(pCert, pErrInfo); + + if ( (fFlags & RTCRPKCS7VCC_F_TIMESTAMP) + && RT_SUCCESS(rc)) + rc = rtCrPkcs7VerifyCertUsageTimstamping(pCert, pErrInfo); + + return rc; +} + + +/** + * @callback_method_impl{RTCRPKCS7VERIFYCERTCALLBACK, + * Standard code signing. Use this for Microsoft SPC.} + */ +RTDECL(int) RTCrPkcs7VerifyCertCallbackCodeSigning(PCRTCRX509CERTIFICATE pCert, RTCRX509CERTPATHS hCertPaths, uint32_t fFlags, + void *pvUser, PRTERRINFO pErrInfo) +{ + RT_NOREF_PV(hCertPaths); RT_NOREF_PV(pvUser); + int rc = VINF_SUCCESS; + if (fFlags & RTCRPKCS7VCC_F_SIGNED_DATA) + { + /* + * If KeyUsage is present it must include digital signature. + */ + rc = rtCrPkcs7VerifyCertUsageDigitalSignature(pCert, pErrInfo); + if (RT_SUCCESS(rc)) + { + /* + * The extended usage 'code signing' must be present. + */ + if (!(pCert->TbsCertificate.T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_EXT_KEY_USAGE)) + return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_KEY_USAGE_MISMATCH, "No extended key usage certificate attribute."); + if (!(pCert->TbsCertificate.T3.fExtKeyUsage & RTCRX509CERT_EKU_F_CODE_SIGNING)) + return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_KEY_USAGE_MISMATCH, "fExtKeyUsage=%#RX64, missing CODE_SIGNING (%#RX64)", + pCert->TbsCertificate.T3.fExtKeyUsage, RTCRX509CERT_EKU_F_CODE_SIGNING); + } + } + + /* + * Timestamping too? + */ + if ( (fFlags & RTCRPKCS7VCC_F_TIMESTAMP) + && RT_SUCCESS(rc)) + rc = rtCrPkcs7VerifyCertUsageTimstamping(pCert, pErrInfo); + + return rc; +} + + +/** + * Deals with authenticated attributes. + * + * When authenticated attributes are present (checked by caller) we must: + * - fish out the content type and check it against the content inof, + * - fish out the message digest among and check it against *phDigest, + * - compute the message digest of the authenticated attributes and + * replace *phDigest with this for the signature verification. + * + * @returns IPRT status code. + * @param pSignerInfo The signer info being verified. + * @param pSignedData The signed data. + * @param phDigest On input this is the digest of the content. On + * output it will (on success) be a reference to + * the message digest of the authenticated + * attributes. The input reference is consumed. + * The caller shall release the output reference. + * @param fFlags Flags. + * @param pErrInfo Extended error info, optional. + */ +static int rtCrPkcs7VerifySignerInfoAuthAttribs(PCRTCRPKCS7SIGNERINFO pSignerInfo, PCRTCRPKCS7SIGNEDDATA pSignedData, + PRTCRDIGEST phDigest, uint32_t fFlags, PRTERRINFO pErrInfo) +{ + /* + * Scan the attributes and validate the two required attributes + * (RFC-2315, chapter 9.2, fourth bullet). Checking that we've got exactly + * one of each of them is checked by the santiy checker function, so we'll + * just assert that it did it's job here. + */ + uint32_t cContentTypes = 0; + uint32_t cMessageDigests = 0; + uint32_t i = pSignerInfo->AuthenticatedAttributes.cItems; + while (i-- > 0) + { + PCRTCRPKCS7ATTRIBUTE pAttrib = pSignerInfo->AuthenticatedAttributes.papItems[i]; + + if (RTAsn1ObjId_CompareWithString(&pAttrib->Type, RTCR_PKCS9_ID_CONTENT_TYPE_OID) == 0) + { + AssertReturn(!cContentTypes, VERR_CR_PKCS7_INTERNAL_ERROR); + AssertReturn(pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_OBJ_IDS, VERR_CR_PKCS7_INTERNAL_ERROR); + AssertReturn(pAttrib->uValues.pObjIds->cItems == 1, VERR_CR_PKCS7_INTERNAL_ERROR); + + if ( !(fFlags & RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE) /* See note about microsoft below. */ + && RTAsn1ObjId_Compare(pAttrib->uValues.pObjIds->papItems[0], &pSignedData->ContentInfo.ContentType) != 0) + return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_CONTENT_TYPE_ATTRIB_MISMATCH, + "Expected content-type %s, found %s", pAttrib->uValues.pObjIds->papItems[0]->szObjId, + pSignedData->ContentInfo.ContentType.szObjId); + cContentTypes++; + } + else if (RTAsn1ObjId_CompareWithString(&pAttrib->Type, RTCR_PKCS9_ID_MESSAGE_DIGEST_OID) == 0) + { + AssertReturn(!cMessageDigests, VERR_CR_PKCS7_INTERNAL_ERROR); + AssertReturn(pAttrib->enmType == RTCRPKCS7ATTRIBUTETYPE_OCTET_STRINGS, VERR_CR_PKCS7_INTERNAL_ERROR); + AssertReturn(pAttrib->uValues.pOctetStrings->cItems == 1, VERR_CR_PKCS7_INTERNAL_ERROR); + + if (!RTCrDigestMatch(*phDigest, + pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.uData.pv, + pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb)) + { + size_t cbHash = RTCrDigestGetHashSize(*phDigest); + if (cbHash != pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb) + return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_MESSAGE_DIGEST_ATTRIB_MISMATCH, + "Authenticated message-digest attribute mismatch: cbHash=%#zx cbValue=%#x", + cbHash, pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.cb); + return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_MESSAGE_DIGEST_ATTRIB_MISMATCH, + "Authenticated message-digest attribute mismatch (cbHash=%#zx):\n" + "signed: %.*Rhxs\n" + "our: %.*Rhxs\n", + cbHash, + cbHash, pAttrib->uValues.pOctetStrings->papItems[0]->Asn1Core.uData.pv, + cbHash, RTCrDigestGetHash(*phDigest)); + } + cMessageDigests++; + } + } + + /* + * Full error reporting here as we don't currently extensively santiy check + * counter signatures. + * Note! Microsoft includes content info in their timestamp counter signatures, + * at least for vista, despite the RFC-3852 stating counter signatures + * "MUST NOT contain a content-type". + */ + if (RT_UNLIKELY( cContentTypes != 1 + && !(fFlags & RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE))) + return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_MISSING_CONTENT_TYPE_ATTRIB, + "Missing authenticated content-type attribute."); + if (RT_UNLIKELY(cMessageDigests != 1)) + return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_MISSING_MESSAGE_DIGEST_ATTRIB, + "Missing authenticated message-digest attribute."); + + /* + * Calculate the digest of the authenticated attributes for use in the + * signature validation. + */ + if ( pSignerInfo->DigestAlgorithm.Parameters.enmType != RTASN1TYPE_NULL + && pSignerInfo->DigestAlgorithm.Parameters.enmType != RTASN1TYPE_NOT_PRESENT) + return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_DIGEST_PARAMS_NOT_IMPL, "Digest algorithm has unsupported parameters"); + + RTCRDIGEST hDigest; + int rc = RTCrDigestCreateByObjId(&hDigest, &pSignerInfo->DigestAlgorithm.Algorithm); + if (RT_SUCCESS(rc)) + { + RTCrDigestRelease(*phDigest); + *phDigest = hDigest; + + /** @todo The encoding step modifies the data, contradicting the const-ness + * of the parameter. */ + rc = RTCrPkcs7Attributes_HashAttributes((PRTCRPKCS7ATTRIBUTES)&pSignerInfo->AuthenticatedAttributes, hDigest, pErrInfo); + } + return rc; +} + + +/** + * Find the handle to the digest given by the specified SignerInfo. + * + * @returns IPRT status code + * @param phDigest Where to return a referenced digest handle on + * success. + * @param pSignedData The signed data structure. + * @param pSignerInfo The signer info. + * @param pahDigests Array of content digests that runs parallel to + * pSignedData->DigestAlgorithms. + * @param pErrInfo Where to store additional error details, + * optional. + */ +static int rtCrPkcs7VerifyFindDigest(PRTCRDIGEST phDigest, PCRTCRPKCS7SIGNEDDATA pSignedData, + PCRTCRPKCS7SIGNERINFO pSignerInfo, PRTCRDIGEST pahDigests, PRTERRINFO pErrInfo) +{ + uint32_t iDigest = pSignedData->DigestAlgorithms.cItems; + while (iDigest-- > 0) + if (RTCrX509AlgorithmIdentifier_Compare(pSignedData->DigestAlgorithms.papItems[iDigest], + &pSignerInfo->DigestAlgorithm) == 0) + { + RTCRDIGEST hDigest = pahDigests[iDigest]; + uint32_t cRefs = RTCrDigestRetain(hDigest); + AssertReturn(cRefs != UINT32_MAX, VERR_CR_PKCS7_INTERNAL_ERROR); + *phDigest = hDigest; + return VINF_SUCCESS; + } + *phDigest = NIL_RTCRDIGEST; /* Make gcc happy. */ + return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_DIGEST_ALGO_NOT_FOUND_IN_LIST, + "SignerInfo.DigestAlgorithm %s not found.", + pSignerInfo->DigestAlgorithm.Algorithm.szObjId); +} + + +/** + * Verifies one signature on a PKCS \#7 SignedData. + * + * @returns IPRT status code. + * @param pSignerInfo The signature. + * @param pSignedData The SignedData. + * @param hDigests The digest corresponding to + * pSignerInfo->DigestAlgorithm. + * @param fFlags Verification flags. + * @param hAdditionalCerts Store containing optional certificates, + * optional. + * @param hTrustedCerts Store containing trusted certificates, required. + * @param pValidationTime The time we're supposed to validate the + * certificates chains at. + * @param pfnVerifyCert Signing certificate verification callback. + * @param fVccFlags Signing certificate verification callback flags. + * @param pvUser Callback parameter. + * @param pErrInfo Where to store additional error details, + * optional. + */ +static int rtCrPkcs7VerifySignerInfo(PCRTCRPKCS7SIGNERINFO pSignerInfo, PCRTCRPKCS7SIGNEDDATA pSignedData, + RTCRDIGEST hDigest, uint32_t fFlags, RTCRSTORE hAdditionalCerts, RTCRSTORE hTrustedCerts, + PCRTTIMESPEC pValidationTime, PFNRTCRPKCS7VERIFYCERTCALLBACK pfnVerifyCert, + uint32_t fVccFlags, void *pvUser, PRTERRINFO pErrInfo) +{ + /* + * Locate the certificate used for signing. + */ + PCRTCRCERTCTX pSignerCertCtx = NULL; + PCRTCRX509CERTIFICATE pSignerCert = NULL; + if (hTrustedCerts != NIL_RTCRSTORE) + pSignerCertCtx = RTCrStoreCertByIssuerAndSerialNo(hTrustedCerts, &pSignerInfo->IssuerAndSerialNumber.Name, + &pSignerInfo->IssuerAndSerialNumber.SerialNumber); + if (!pSignerCertCtx && hAdditionalCerts != NIL_RTCRSTORE) + pSignerCertCtx = RTCrStoreCertByIssuerAndSerialNo(hAdditionalCerts, &pSignerInfo->IssuerAndSerialNumber.Name, + &pSignerInfo->IssuerAndSerialNumber.SerialNumber); + if (pSignerCertCtx) + pSignerCert = pSignerCertCtx->pCert; + else + { + pSignerCert = RTCrPkcs7SetOfCerts_FindX509ByIssuerAndSerialNumber(&pSignedData->Certificates, + &pSignerInfo->IssuerAndSerialNumber.Name, + &pSignerInfo->IssuerAndSerialNumber.SerialNumber); + if (!pSignerCert) + return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_SIGNED_DATA_CERT_NOT_FOUND, + "Certificate not found: serial=%.*Rhxs", + pSignerInfo->IssuerAndSerialNumber.SerialNumber.Asn1Core.cb, + pSignerInfo->IssuerAndSerialNumber.SerialNumber.Asn1Core.uData.pv); + } + + /* + * Unless caller requesed all certificates to be trusted fully, we always + * pass it on to the certificate path builder so it can do the requested + * checks on trust anchors. (We didn't used to do this as the path + * builder could handle trusted targets. A benefit here is that + * pfnVerifyCert can assume a hCertPaths now, and get the validation time + * from it if it wants it.) + * + * If no valid paths are found, this step will fail. + */ + int rc; + if (!(fFlags & RTCRPKCS7VERIFY_SD_F_TRUST_ALL_CERTS)) + { + RTCRX509CERTPATHS hCertPaths; + rc = RTCrX509CertPathsCreate(&hCertPaths, pSignerCert); + if (RT_SUCCESS(rc)) + { + rc = RTCrX509CertPathsSetValidTimeSpec(hCertPaths, pValidationTime); + if (hTrustedCerts != NIL_RTCRSTORE && RT_SUCCESS(rc)) + rc = RTCrX509CertPathsSetTrustedStore(hCertPaths, hTrustedCerts); + if (hAdditionalCerts != NIL_RTCRSTORE && RT_SUCCESS(rc)) + rc = RTCrX509CertPathsSetUntrustedStore(hCertPaths, hAdditionalCerts); + if (pSignedData->Certificates.cItems > 0 && RT_SUCCESS(rc)) + rc = RTCrX509CertPathsSetUntrustedSet(hCertPaths, &pSignedData->Certificates); + if ((fFlags & RTCRPKCS7VERIFY_SD_F_CHECK_TRUST_ANCHORS) && RT_SUCCESS(rc)) + rc = RTCrX509CertPathsSetTrustAnchorChecks(hCertPaths, true /*fEnable*/); + if (RT_SUCCESS(rc)) + { + rc = RTCrX509CertPathsBuild(hCertPaths, pErrInfo); + if (RT_SUCCESS(rc)) + rc = RTCrX509CertPathsValidateAll(hCertPaths, NULL, pErrInfo); + + /* + * Check that the certificate purpose and whatnot matches what + * is being signed. + */ + if (RT_SUCCESS(rc)) + rc = pfnVerifyCert(pSignerCert, hCertPaths, fVccFlags, pvUser, pErrInfo); + } + else + RTErrInfoSetF(pErrInfo, rc, "Error configuring path builder: %Rrc", rc); + RTCrX509CertPathsRelease(hCertPaths); + } + } + /* + * Check that the certificate purpose matches what is signed. + */ + else + rc = pfnVerifyCert(pSignerCert, NIL_RTCRX509CERTPATHS, fVccFlags, pvUser, pErrInfo); + + /* + * Reference the digest so we can safely replace with one on the + * authenticated attributes below. + */ + if ( RT_SUCCESS(rc) + && RTCrDigestRetain(hDigest) != UINT32_MAX) + { + /* + * If there are authenticated attributes, we've got more work before we + * can verify the signature. + */ + if ( RT_SUCCESS(rc) + && RTCrPkcs7Attributes_IsPresent(&pSignerInfo->AuthenticatedAttributes)) + rc = rtCrPkcs7VerifySignerInfoAuthAttribs(pSignerInfo, pSignedData, &hDigest, fFlags, pErrInfo); + + /* + * Verify the signature. + */ + if (RT_SUCCESS(rc)) + { + RTCRKEY hKey; + rc = RTCrKeyCreateFromSubjectPublicKeyInfo(&hKey, &pSignerCert->TbsCertificate.SubjectPublicKeyInfo, + pErrInfo, "pkcs7"); + if (RT_SUCCESS(rc)) + { + RTCRPKIXSIGNATURE hSignature; + rc = RTCrPkixSignatureCreateByObjId(&hSignature, &pSignerInfo->DigestEncryptionAlgorithm.Algorithm, + hKey, &pSignerInfo->DigestEncryptionAlgorithm.Parameters, false /*fSigning*/); + RTCrKeyRelease(hKey); + if (RT_SUCCESS(rc)) + { + /** @todo Check that DigestEncryptionAlgorithm is compatible with hSignature + * (this is not vital). */ + rc = RTCrPkixSignatureVerifyOctetString(hSignature, hDigest, &pSignerInfo->EncryptedDigest); + if (RT_FAILURE(rc)) + rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_SIGNATURE_VERIFICATION_FAILED, + "Signature verification failed: %Rrc", rc); + RTCrPkixSignatureRelease(hSignature); + } + else + rc = RTErrInfoSetF(pErrInfo, rc, "Failure to instantiate public key algorithm [IPRT]: %s (%s)", + pSignerCert->TbsCertificate.SubjectPublicKeyInfo.Algorithm.Algorithm.szObjId, + pSignerInfo->DigestEncryptionAlgorithm.Algorithm.szObjId); + } + } + + RTCrDigestRelease(hDigest); + } + else if (RT_SUCCESS(rc)) + rc = VERR_CR_PKCS7_INTERNAL_ERROR; + RTCrCertCtxRelease(pSignerCertCtx); + return rc; +} + + +/** + * Verifies a counter signature. + * + * @returns IPRT status code. + * @param pCounterSignerInfo The counter signature. + * @param pPrimarySignerInfo The primary signature (can be a counter + * signature too if nested). + * @param pSignedData The SignedData. + * @param fFlags Verification flags. + * @param hAdditionalCerts Store containing optional certificates, + * optional. + * @param hTrustedCerts Store containing trusted certificates, required. + * @param pValidationTime The time we're supposed to validate the + * certificates chains at. + * @param pfnVerifyCert Signing certificate verification callback. + * @param fVccFlags Signing certificate verification callback flags. + * @param pvUser Callback parameter. + * @param pErrInfo Where to store additional error details, + * optional. + */ +static int rtCrPkcs7VerifyCounterSignerInfo(PCRTCRPKCS7SIGNERINFO pCounterSignerInfo, PCRTCRPKCS7SIGNERINFO pPrimarySignerInfo, + PCRTCRPKCS7SIGNEDDATA pSignedData, uint32_t fFlags, + RTCRSTORE hAdditionalCerts, RTCRSTORE hTrustedCerts, PCRTTIMESPEC pValidationTime, + PFNRTCRPKCS7VERIFYCERTCALLBACK pfnVerifyCert, uint32_t fVccFlags, + void *pvUser, PRTERRINFO pErrInfo) +{ + /* + * Calculate the digest we need to verify. + */ + RTCRDIGEST hDigest; + int rc = RTCrDigestCreateByObjId(&hDigest, &pCounterSignerInfo->DigestAlgorithm.Algorithm); + if (RT_SUCCESS(rc)) + { + rc = RTCrDigestUpdate(hDigest, + pPrimarySignerInfo->EncryptedDigest.Asn1Core.uData.pv, + pPrimarySignerInfo->EncryptedDigest.Asn1Core.cb); + if (RT_SUCCESS(rc)) + rc = RTCrDigestFinal(hDigest, NULL, 0); + if (RT_SUCCESS(rc)) + { + /* + * Pass it on to the common SignerInfo verifier function. + */ + rc = rtCrPkcs7VerifySignerInfo(pCounterSignerInfo, pSignedData, hDigest, + fFlags | RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE, + hAdditionalCerts, hTrustedCerts, pValidationTime, + pfnVerifyCert, fVccFlags, pvUser, pErrInfo); + + } + else + rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_DIGEST_CALC_ERROR, + "Hashing for counter signature failed unexpectedly: %Rrc", rc); + RTCrDigestRelease(hDigest); + } + else + rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_DIGEST_CREATE_ERROR, "Error creating digest for '%s': %Rrc", + pCounterSignerInfo->DigestAlgorithm.Algorithm.szObjId, rc); + + return rc; +} + + +/** + * Worker. + */ +static int rtCrPkcs7VerifySignedDataEx(PCRTCRPKCS7CONTENTINFO pContentInfo, uint32_t fFlags, + RTCRSTORE hAdditionalCerts, RTCRSTORE hTrustedCerts, + PCRTTIMESPEC pValidationTime, + PFNRTCRPKCS7VERIFYCERTCALLBACK pfnVerifyCert, void *pvUser, + void const *pvContent, size_t cbContent, PRTERRINFO pErrInfo) +{ + /* + * Check and adjust the input. + */ + if (pfnVerifyCert) + AssertPtrReturn(pfnVerifyCert, VERR_INVALID_POINTER); + else + pfnVerifyCert = RTCrPkcs7VerifyCertCallbackDefault; + + if (!RTCrPkcs7ContentInfo_IsSignedData(pContentInfo)) + return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_NOT_SIGNED_DATA, "Not PKCS #7 SignedData."); + PCRTCRPKCS7SIGNEDDATA pSignedData = pContentInfo->u.pSignedData; + int rc = RTCrPkcs7SignedData_CheckSanity(pSignedData, 0, pErrInfo, ""); + if (RT_FAILURE(rc)) + return rc; + + /* + * Hash the content info. + */ + /* Check that there aren't too many or too few hash algorithms for our + implementation and purposes. */ + RTCRDIGEST ahDigests[2]; + uint32_t const cDigests = pSignedData->DigestAlgorithms.cItems; + if (!cDigests) /** @todo we might have to support this... */ + return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_NO_DIGEST_ALGORITHMS, "No digest algorithms"); + + if (cDigests > RT_ELEMENTS(ahDigests)) + return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_TOO_MANY_DIGEST_ALGORITHMS, + "Too many digest algorithm: cAlgorithms=%u", cDigests); + + /* Create the message digest calculators. */ + rc = VERR_CR_PKCS7_NO_DIGEST_ALGORITHMS; + uint32_t i; + for (i = 0; i < cDigests; i++) + { + rc = RTCrDigestCreateByObjId(&ahDigests[i], &pSignedData->DigestAlgorithms.papItems[i]->Algorithm); + if (RT_FAILURE(rc)) + { + rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_DIGEST_CREATE_ERROR, "Error creating digest for '%s': %Rrc", + pSignedData->DigestAlgorithms.papItems[i]->Algorithm.szObjId, rc); + break; + } + } + if (RT_SUCCESS(rc)) + { + /* Hash the content. */ + for (i = 0; i < cDigests && RT_SUCCESS(rc); i++) + { + rc = RTCrDigestUpdate(ahDigests[i], pvContent, cbContent); + if (RT_SUCCESS(rc)) + rc = RTCrDigestFinal(ahDigests[i], NULL, 0); + } + if (RT_SUCCESS(rc)) + { + /* + * Validate the signed infos. The flags may select one particular entry. + */ + RTTIMESPEC const GivenValidationTime = *pValidationTime; + uint32_t fPrimaryVccFlags = !(fFlags & RTCRPKCS7VERIFY_SD_F_USAGE_TIMESTAMPING) + ? RTCRPKCS7VCC_F_SIGNED_DATA : RTCRPKCS7VCC_F_TIMESTAMP; + uint32_t cItems = pSignedData->SignerInfos.cItems; + i = 0; + if (fFlags & RTCRPKCS7VERIFY_SD_F_HAS_SIGNER_INDEX) + { + i = (fFlags & RTCRPKCS7VERIFY_SD_F_SIGNER_INDEX_MASK) >> RTCRPKCS7VERIFY_SD_F_SIGNER_INDEX_SHIFT; + cItems = RT_MIN(cItems, i + 1); + } + rc = VERR_CR_PKCS7_NO_SIGNER_INFOS; + for (; i < cItems; i++) + { + PCRTCRPKCS7SIGNERINFO pSignerInfo = pSignedData->SignerInfos.papItems[i]; + RTCRDIGEST hThisDigest = NIL_RTCRDIGEST; /* (gcc maybe incredible stupid.) */ + rc = rtCrPkcs7VerifyFindDigest(&hThisDigest, pSignedData, pSignerInfo, ahDigests, pErrInfo); + if (RT_FAILURE(rc)) + break; + + /* + * See if we can find a trusted signing time. + * (Note that while it would make sense splitting up this function, + * we need to carry a lot of arguments around, so better not.) + */ + bool fDone = false; + PCRTCRPKCS7SIGNERINFO pSigningTimeSigner = NULL; + PCRTASN1TIME pSignedTime; + while ( !fDone + && (pSignedTime = RTCrPkcs7SignerInfo_GetSigningTime(pSignerInfo, &pSigningTimeSigner)) != NULL) + { + RTTIMESPEC ThisValidationTime; + if (RT_LIKELY(RTTimeImplode(&ThisValidationTime, &pSignedTime->Time))) + { + if (pSigningTimeSigner == pSignerInfo) + { + if (fFlags & RTCRPKCS7VERIFY_SD_F_COUNTER_SIGNATURE_SIGNING_TIME_ONLY) + continue; + rc = rtCrPkcs7VerifySignerInfo(pSignerInfo, pSignedData, hThisDigest, fFlags, + hAdditionalCerts, hTrustedCerts, &ThisValidationTime, + pfnVerifyCert, fPrimaryVccFlags | RTCRPKCS7VCC_F_TIMESTAMP, + pvUser, pErrInfo); + } + else + { + rc = VINF_SUCCESS; + if (!(fFlags & RTCRPKCS7VERIFY_SD_F_USE_SIGNING_TIME_UNVERIFIED)) + rc = rtCrPkcs7VerifyCounterSignerInfo(pSigningTimeSigner, pSignerInfo, pSignedData, + fFlags & ~RTCRPKCS7VERIFY_SD_F_UPDATE_VALIDATION_TIME, + hAdditionalCerts, hTrustedCerts, &ThisValidationTime, + pfnVerifyCert, RTCRPKCS7VCC_F_TIMESTAMP, pvUser, pErrInfo); + if (RT_SUCCESS(rc)) + rc = rtCrPkcs7VerifySignerInfo(pSignerInfo, pSignedData, hThisDigest, fFlags, hAdditionalCerts, + hTrustedCerts, &ThisValidationTime, + pfnVerifyCert, fPrimaryVccFlags, pvUser, pErrInfo); + } + fDone = RT_SUCCESS(rc) + || (fFlags & RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_SIGNING_TIME_IF_PRESENT); + if ((fFlags & RTCRPKCS7VERIFY_SD_F_UPDATE_VALIDATION_TIME) && fDone) + *(PRTTIMESPEC)pValidationTime = ThisValidationTime; + } + else + { + rc = RTErrInfoSet(pErrInfo, VERR_INTERNAL_ERROR_3, "RTTimeImplode failed"); + fDone = true; + } + } + + /* + * If not luck, check for microsoft timestamp counter signatures. + */ + if (!fDone && !(fFlags & RTCRPKCS7VERIFY_SD_F_IGNORE_MS_TIMESTAMP)) + { + PCRTCRPKCS7CONTENTINFO pSignedTimestamp = NULL; + pSignedTime = RTCrPkcs7SignerInfo_GetMsTimestamp(pSignerInfo, &pSignedTimestamp); + if (pSignedTime) + { + RTTIMESPEC ThisValidationTime; + if (RT_LIKELY(RTTimeImplode(&ThisValidationTime, &pSignedTime->Time))) + { + rc = VINF_SUCCESS; + if (!(fFlags & RTCRPKCS7VERIFY_SD_F_USE_MS_TIMESTAMP_UNVERIFIED)) + rc = RTCrPkcs7VerifySignedData(pSignedTimestamp, + fFlags | RTCRPKCS7VERIFY_SD_F_IGNORE_MS_TIMESTAMP + | RTCRPKCS7VERIFY_SD_F_USAGE_TIMESTAMPING, + hAdditionalCerts, hTrustedCerts, &ThisValidationTime, + pfnVerifyCert, pvUser, pErrInfo); + + if (RT_SUCCESS(rc)) + rc = rtCrPkcs7VerifySignerInfo(pSignerInfo, pSignedData, hThisDigest, fFlags, hAdditionalCerts, + hTrustedCerts, &ThisValidationTime, + pfnVerifyCert, fPrimaryVccFlags, pvUser, pErrInfo); + fDone = RT_SUCCESS(rc) + || (fFlags & RTCRPKCS7VERIFY_SD_F_ALWAYS_USE_MS_TIMESTAMP_IF_PRESENT); + if ((fFlags & RTCRPKCS7VERIFY_SD_F_UPDATE_VALIDATION_TIME) && fDone) + *(PRTTIMESPEC)pValidationTime = ThisValidationTime; + } + else + { + rc = RTErrInfoSet(pErrInfo, VERR_INTERNAL_ERROR_3, "RTTimeImplode failed"); + fDone = true; + } + + } + } + + /* + * No valid signing time found, use the one specified instead. + */ + if (!fDone) + rc = rtCrPkcs7VerifySignerInfo(pSignerInfo, pSignedData, hThisDigest, fFlags, hAdditionalCerts, hTrustedCerts, + &GivenValidationTime, pfnVerifyCert, fPrimaryVccFlags, pvUser, pErrInfo); + RTCrDigestRelease(hThisDigest); + if (RT_FAILURE(rc)) + break; + } + } + else + rc = RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_DIGEST_CALC_ERROR, + "Hashing content failed unexpectedly (i=%u): %Rrc", i, rc); + + /* Clean up digests. */ + i = cDigests; + } + while (i-- > 0) + { + int rc2 = RTCrDigestRelease(ahDigests[i]); + AssertRC(rc2); + } + + +#ifdef IPRT_WITH_OPENSSL + /* + * Verify using OpenSSL and combine the results (should be identical). + */ + /** @todo figure out how to verify MS timstamp signatures using OpenSSL. */ + if (fFlags & RTCRPKCS7VERIFY_SD_F_USAGE_TIMESTAMPING) + return rc; + /** @todo figure out if we can verify just one signer info item using OpenSSL. */ + if (!(fFlags & RTCRPKCS7VERIFY_SD_F_HAS_SIGNER_INDEX) && pSignedData->SignerInfos.cItems > 1) + return rc; + + int rcOssl = rtCrPkcs7VerifySignedDataUsingOpenSsl(pContentInfo, fFlags, hAdditionalCerts, hTrustedCerts, + pvContent, cbContent, RT_SUCCESS(rc) ? pErrInfo : NULL); + if (RT_SUCCESS(rcOssl) && RT_SUCCESS(rc)) + return rc; +// AssertMsg(RT_FAILURE_NP(rcOssl) && RT_FAILURE_NP(rc), ("%Rrc, %Rrc\n", rcOssl, rc)); + if (RT_FAILURE(rc)) + return rc; + return rcOssl; +#else + return rc; +#endif +} + + +RTDECL(int) RTCrPkcs7VerifySignedData(PCRTCRPKCS7CONTENTINFO pContentInfo, uint32_t fFlags, + RTCRSTORE hAdditionalCerts, RTCRSTORE hTrustedCerts, + PCRTTIMESPEC pValidationTime, PFNRTCRPKCS7VERIFYCERTCALLBACK pfnVerifyCert, void *pvUser, + PRTERRINFO pErrInfo) +{ + /* + * Find the content and pass it on to common worker. + */ + if (!RTCrPkcs7ContentInfo_IsSignedData(pContentInfo)) + return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_NOT_SIGNED_DATA, "Not PKCS #7 SignedData."); + + /* Exactly what the content is, is for some stupid reason unnecessarily complicated. */ + PCRTCRPKCS7SIGNEDDATA pSignedData = pContentInfo->u.pSignedData; + void const *pvContent = pSignedData->ContentInfo.Content.Asn1Core.uData.pv; + uint32_t cbContent = pSignedData->ContentInfo.Content.Asn1Core.cb; + if (pSignedData->ContentInfo.Content.pEncapsulated) + { + pvContent = pSignedData->ContentInfo.Content.pEncapsulated->uData.pv; + cbContent = pSignedData->ContentInfo.Content.pEncapsulated->cb; + } + + return rtCrPkcs7VerifySignedDataEx(pContentInfo, fFlags, hAdditionalCerts, hTrustedCerts, pValidationTime, + pfnVerifyCert, pvUser, pvContent, cbContent, pErrInfo); +} + + +RTDECL(int) RTCrPkcs7VerifySignedDataWithExternalData(PCRTCRPKCS7CONTENTINFO pContentInfo, uint32_t fFlags, + RTCRSTORE hAdditionalCerts, RTCRSTORE hTrustedCerts, + PCRTTIMESPEC pValidationTime, + PFNRTCRPKCS7VERIFYCERTCALLBACK pfnVerifyCert, void *pvUser, + void const *pvData, size_t cbData, PRTERRINFO pErrInfo) +{ + /* + * Require 'data' as inner content type. + */ + if (!RTCrPkcs7ContentInfo_IsSignedData(pContentInfo)) + return RTErrInfoSet(pErrInfo, VERR_CR_PKCS7_NOT_SIGNED_DATA, "Not PKCS #7 SignedData."); + PCRTCRPKCS7SIGNEDDATA pSignedData = pContentInfo->u.pSignedData; + + if (RTAsn1ObjId_CompareWithString(&pSignedData->ContentInfo.ContentType, RTCR_PKCS7_DATA_OID) != 0) + return RTErrInfoSetF(pErrInfo, VERR_CR_PKCS7_NOT_DATA, + "The signedData content type is %s, expected 'data' (%s)", + pSignedData->ContentInfo.ContentType.szObjId, RTCR_PKCS7_DATA_OID); + + return rtCrPkcs7VerifySignedDataEx(pContentInfo, fFlags, hAdditionalCerts, hTrustedCerts, pValidationTime, + pfnVerifyCert, pvUser, pvData, cbData, pErrInfo); +} + |