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/pkix-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/pkix-verify.cpp')
-rw-r--r-- | src/VBox/Runtime/common/crypto/pkix-verify.cpp | 329 |
1 files changed, 329 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/crypto/pkix-verify.cpp b/src/VBox/Runtime/common/crypto/pkix-verify.cpp new file mode 100644 index 00000000..10c5bd61 --- /dev/null +++ b/src/VBox/Runtime/common/crypto/pkix-verify.cpp @@ -0,0 +1,329 @@ +/* $Id: pkix-verify.cpp $ */ +/** @file + * IPRT - Crypto - Public Key Infrastructure API, 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/pkix.h> + +#include <iprt/err.h> +#include <iprt/string.h> +#include <iprt/crypto/digest.h> +#include <iprt/crypto/key.h> + +#ifdef IPRT_WITH_OPENSSL +# include "internal/iprt-openssl.h" +# include "internal/openssl-pre.h" +# include <openssl/evp.h> +# include "internal/openssl-post.h" +# ifndef OPENSSL_VERSION_NUMBER +# error "Missing OPENSSL_VERSION_NUMBER!" +# endif +#endif + + + +RTDECL(int) RTCrPkixPubKeyVerifySignature(PCRTASN1OBJID pAlgorithm, RTCRKEY hPublicKey, PCRTASN1DYNTYPE pParameters, + PCRTASN1BITSTRING pSignatureValue, const void *pvData, size_t cbData, + PRTERRINFO pErrInfo) +{ + /* + * Valid input. + */ + AssertPtrReturn(pAlgorithm, VERR_INVALID_POINTER); + AssertReturn(RTAsn1ObjId_IsPresent(pAlgorithm), VERR_INVALID_POINTER); + + AssertPtrReturn(hPublicKey, VERR_INVALID_POINTER); + Assert(RTCrKeyHasPublicPart(hPublicKey)); + RTCRKEYTYPE const enmKeyType = RTCrKeyGetType(hPublicKey); + AssertReturn(enmKeyType != RTCRKEYTYPE_INVALID, VERR_INVALID_HANDLE); + + AssertPtrReturn(pSignatureValue, VERR_INVALID_POINTER); + AssertReturn(RTAsn1BitString_IsPresent(pSignatureValue), VERR_INVALID_POINTER); + + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData > 0, VERR_INVALID_PARAMETER); + + /* + * Verify that the parameters are compatible with the key. We ASSUME the + * parameters are for a hash+cryption combination, like those found in + * RTCRX509TBSCERTIFICATE::Signature. At present, these should NULL (or + * absent) for the two key types we support RSA & ECDSA, which is an + * ASSUMPTION by the OpenSSL code below. + */ + int rcIprt = RTCrKeyVerifyParameterCompatibility(hPublicKey, pParameters, true /*fForSignature*/, pAlgorithm, pErrInfo); + AssertRCReturn(rcIprt, rcIprt); + + /* + * Validate using IPRT. + */ + RTCRPKIXSIGNATURE hSignature; + rcIprt = RTCrPkixSignatureCreateByObjId(&hSignature, pAlgorithm, hPublicKey, pParameters, false /*fSigning*/); + if (RT_FAILURE(rcIprt)) + return RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_CIPHER_ALGO_NOT_KNOWN, + "Unknown public key algorithm [IPRT %Rrc]: %s", rcIprt, pAlgorithm->szObjId); + + RTCRDIGEST hDigest; + rcIprt = RTCrDigestCreateByObjId(&hDigest, pAlgorithm); + if (RT_SUCCESS(rcIprt)) + { + /* Calculate the digest. */ + rcIprt = RTCrDigestUpdate(hDigest, pvData, cbData); + if (RT_SUCCESS(rcIprt)) + { + rcIprt = RTCrPkixSignatureVerifyBitString(hSignature, hDigest, pSignatureValue); + if (RT_FAILURE(rcIprt)) + RTErrInfoSet(pErrInfo, rcIprt, "RTCrPkixSignatureVerifyBitString failed"); + } + else + RTErrInfoSet(pErrInfo, rcIprt, "RTCrDigestUpdate failed"); + RTCrDigestRelease(hDigest); + } + else + RTErrInfoSetF(pErrInfo, rcIprt, "Unknown digest algorithm [IPRT]: %s", pAlgorithm->szObjId); + RTCrPkixSignatureRelease(hSignature); + +#ifdef IPRT_WITH_OPENSSL + /* We don't implement digest+cipher parameters in OpenSSL (or at all), + RTCrKeyVerifyParameterCompatibility should ensure we don't get here + (ASSUMING only RSA and ECDSA keys). But, just in case, bail out if we do. */ + AssertReturn( !pParameters + || pParameters->enmType == RTASN1TYPE_NULL + || pParameters->enmType == RTASN1TYPE_NOT_PRESENT, + VERR_CR_PKIX_CIPHER_ALGO_PARAMS_NOT_IMPL); + + /* + * Validate using OpenSSL EVP. + */ + /* Create an EVP public key. */ + EVP_PKEY *pEvpPublicKey = NULL; + const EVP_MD *pEvpMdType = NULL; + int rcOssl = rtCrKeyToOpenSslKeyEx(hPublicKey, true /*fNeedPublic*/, pAlgorithm->szObjId, + (void **)&pEvpPublicKey, (const void **)&pEvpMdType, pErrInfo); + if (RT_SUCCESS(rcOssl)) + { + EVP_MD_CTX *pEvpMdCtx = EVP_MD_CTX_create(); + if (pEvpMdCtx) + { + if (EVP_VerifyInit_ex(pEvpMdCtx, pEvpMdType, NULL /*engine*/)) + { + /* Digest the data. */ + EVP_VerifyUpdate(pEvpMdCtx, pvData, cbData); + + /* Verify the signature. */ + if (EVP_VerifyFinal(pEvpMdCtx, + RTASN1BITSTRING_GET_BIT0_PTR(pSignatureValue), + RTASN1BITSTRING_GET_BYTE_SIZE(pSignatureValue), + pEvpPublicKey) > 0) + rcOssl = VINF_SUCCESS; + else + rcOssl = RTErrInfoSet(pErrInfo, VERR_CR_PKIX_OSSL_VERIFY_FINAL_FAILED, "EVP_VerifyFinal failed"); + + /* Cleanup and return: */ + } + else + rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_CIPHER_ALOG_INIT_FAILED, + "EVP_VerifyInit_ex failed (algorithm type is %s)", pAlgorithm->szObjId); + EVP_MD_CTX_destroy(pEvpMdCtx); + } + else + rcOssl = RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "EVP_MD_CTX_create failed"); + EVP_PKEY_free(pEvpPublicKey); + } + + /* + * Check the result. + */ + if ( (RT_SUCCESS(rcIprt) && RT_SUCCESS(rcOssl)) + || (RT_FAILURE_NP(rcIprt) && RT_FAILURE_NP(rcOssl)) + || (RT_SUCCESS(rcIprt) && rcOssl == VERR_CR_PKIX_OSSL_CIPHER_ALGO_NOT_KNOWN_EVP) ) + return rcIprt; + AssertMsgFailed(("rcIprt=%Rrc rcOssl=%Rrc\n", rcIprt, rcOssl)); + if (RT_FAILURE_NP(rcOssl)) + return rcOssl; +#endif /* IPRT_WITH_OPENSSL */ + + return rcIprt; +} + + +RTDECL(int) RTCrPkixPubKeyVerifySignedDigest(PCRTASN1OBJID pAlgorithm, RTCRKEY hPublicKey, PCRTASN1DYNTYPE pParameters, + void const *pvSignedDigest, size_t cbSignedDigest, RTCRDIGEST hDigest, + PRTERRINFO pErrInfo) +{ + /* + * Valid input. + */ + AssertPtrReturn(pAlgorithm, VERR_INVALID_POINTER); + AssertReturn(RTAsn1ObjId_IsPresent(pAlgorithm), VERR_INVALID_POINTER); + + AssertPtrReturn(hPublicKey, VERR_INVALID_POINTER); + Assert(RTCrKeyHasPublicPart(hPublicKey)); + RTCRKEYTYPE const enmKeyType = RTCrKeyGetType(hPublicKey); + AssertReturn(enmKeyType != RTCRKEYTYPE_INVALID, VERR_INVALID_HANDLE); + + AssertPtrReturn(pvSignedDigest, VERR_INVALID_POINTER); + AssertReturn(cbSignedDigest, VERR_INVALID_PARAMETER); + + AssertPtrReturn(hDigest, VERR_INVALID_HANDLE); + + /* + * Verify that the parameters are compatible with the key. We ASSUME the + * parameters are for a hash+cryption combination, like those found in + * RTCRX509TBSCERTIFICATE::Signature. At present, these should NULL (or + * absent) for the two key types we support RSA & ECDSA, which is an + * ASSUMPTION by the OpenSSL code below. + */ + int rcIprt = RTCrKeyVerifyParameterCompatibility(hPublicKey, pParameters, true /*fForSignature*/, pAlgorithm, pErrInfo); + AssertRCReturn(rcIprt, rcIprt); + + /* + * Validate using IPRT. + */ + RTCRPKIXSIGNATURE hSignature; + rcIprt = RTCrPkixSignatureCreateByObjId(&hSignature, pAlgorithm, hPublicKey, pParameters, false /*fSigning*/); + if (RT_FAILURE(rcIprt)) + return RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_CIPHER_ALGO_NOT_KNOWN, + "Unknown public key algorithm [IPRT %Rrc]: %s", rcIprt, pAlgorithm->szObjId); + + rcIprt = RTCrPkixSignatureVerify(hSignature, hDigest, pvSignedDigest, cbSignedDigest); + if (RT_FAILURE(rcIprt)) + RTErrInfoSet(pErrInfo, rcIprt, "RTCrPkixSignatureVerifyBitString failed"); + + RTCrPkixSignatureRelease(hSignature); + +#if defined(IPRT_WITH_OPENSSL) \ + && (OPENSSL_VERSION_NUMBER > 0x10000000L) /* 0.9.8 doesn't seem to have EVP_PKEY_CTX_set_signature_md. */ + /* + * Validate using OpenSSL EVP. + */ + /* Make sure the algorithm includes the digest and isn't just RSA, ECDSA or similar. */ + const char *pszAlgObjId = RTCrX509AlgorithmIdentifier_CombineEncryptionOidAndDigestOid(pAlgorithm->szObjId, + RTCrDigestGetAlgorithmOid(hDigest)); + AssertMsgStmt(pszAlgObjId, ("enc=%s hash=%s\n", pAlgorithm->szObjId, RTCrDigestGetAlgorithmOid(hDigest)), + pszAlgObjId = RTCrDigestGetAlgorithmOid(hDigest)); + + /* We don't implement digest+cipher parameters in OpenSSL (or at all), + RTCrKeyVerifyParameterCompatibility should ensure we don't get here + (ASSUMING only RSA and ECDSA keys). But, just in case, bail out if we do. */ + AssertReturn( !pParameters + || pParameters->enmType == RTASN1TYPE_NULL + || pParameters->enmType == RTASN1TYPE_NOT_PRESENT, + VERR_CR_PKIX_CIPHER_ALGO_PARAMS_NOT_IMPL); + + /* Create an EVP public key. */ + EVP_PKEY *pEvpPublicKey = NULL; + const EVP_MD *pEvpMdType = NULL; + int rcOssl = rtCrKeyToOpenSslKeyEx(hPublicKey, true /*fNeedPublic*/, pszAlgObjId, + (void **)&pEvpPublicKey, (const void **)&pEvpMdType, pErrInfo); + if (RT_SUCCESS(rcOssl)) + { + /* Create an EVP public key context we can use to validate the digest. */ + EVP_PKEY_CTX *pEvpPKeyCtx = EVP_PKEY_CTX_new(pEvpPublicKey, NULL); + if (pEvpPKeyCtx) + { + rcOssl = EVP_PKEY_verify_init(pEvpPKeyCtx); + if (rcOssl > 0) + { + rcOssl = EVP_PKEY_CTX_set_signature_md(pEvpPKeyCtx, pEvpMdType); + if (rcOssl > 0) + { + /* Get the digest from hDigest and verify it. */ + rcOssl = EVP_PKEY_verify(pEvpPKeyCtx, + (uint8_t const *)pvSignedDigest, + cbSignedDigest, + RTCrDigestGetHash(hDigest), + RTCrDigestGetHashSize(hDigest)); + if (rcOssl > 0) + rcOssl = VINF_SUCCESS; + else + rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_VERIFY_FINAL_FAILED, + "EVP_PKEY_verify failed (%d)", rcOssl); + /* Cleanup and return: */ + } + else + rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_TYPE_ERROR, + "EVP_PKEY_CTX_set_signature_md failed (%d)", rcOssl); + } + else + rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_TYPE_ERROR, + "EVP_PKEY_verify_init failed (%d)", rcOssl); + EVP_PKEY_CTX_free(pEvpPKeyCtx); + } + else + rcOssl = RTErrInfoSet(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_TYPE_ERROR, "EVP_PKEY_CTX_new failed"); + EVP_PKEY_free(pEvpPublicKey); + } + + /* + * Check the result. + */ + if ( (RT_SUCCESS(rcIprt) && RT_SUCCESS(rcOssl)) + || (RT_FAILURE_NP(rcIprt) && RT_FAILURE_NP(rcOssl)) + || (RT_SUCCESS(rcIprt) && rcOssl == VERR_CR_PKIX_OSSL_CIPHER_ALGO_NOT_KNOWN_EVP) ) + return rcIprt; + AssertMsgFailed(("rcIprt=%Rrc rcOssl=%Rrc\n", rcIprt, rcOssl)); + if (RT_FAILURE_NP(rcOssl)) + return rcOssl; +#endif /* IPRT_WITH_OPENSSL */ + + return rcIprt; +} + + +RTDECL(int) RTCrPkixPubKeyVerifySignedDigestByCertPubKeyInfo(PCRTCRX509SUBJECTPUBLICKEYINFO pCertPubKeyInfo, + void const *pvSignedDigest, size_t cbSignedDigest, + RTCRDIGEST hDigest, PRTERRINFO pErrInfo) +{ + RTCRKEY hPublicKey; + int rc = RTCrKeyCreateFromPublicAlgorithmAndBits(&hPublicKey, &pCertPubKeyInfo->Algorithm.Algorithm, + &pCertPubKeyInfo->Algorithm.Parameters, + &pCertPubKeyInfo->SubjectPublicKey, pErrInfo, NULL); + if (RT_SUCCESS(rc)) + { + /** @todo r=bird (2023-07-06): This ASSUMES no digest+cipher parameters, which + * is the case for RSA and ECDSA. */ + rc = RTCrPkixPubKeyVerifySignedDigest(&pCertPubKeyInfo->Algorithm.Algorithm, hPublicKey, NULL, + pvSignedDigest, cbSignedDigest, hDigest, pErrInfo); + + uint32_t cRefs = RTCrKeyRelease(hPublicKey); + Assert(cRefs == 0); RT_NOREF(cRefs); + } + return rc; +} + |