VirtualBox

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

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

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.7 KB
Line 
1/* $Id: pkix-verify.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - Crypto - Public Key Infrastructure API, Verification.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include "internal/iprt.h"
42#include <iprt/crypto/pkix.h>
43
44#include <iprt/err.h>
45#include <iprt/string.h>
46#include <iprt/crypto/digest.h>
47#include <iprt/crypto/key.h>
48
49#ifdef IPRT_WITH_OPENSSL
50# include "internal/iprt-openssl.h"
51# include "internal/openssl-pre.h"
52# include <openssl/evp.h>
53# include "internal/openssl-post.h"
54# ifndef OPENSSL_VERSION_NUMBER
55# error "Missing OPENSSL_VERSION_NUMBER!"
56# endif
57#endif
58
59
60
61RTDECL(int) RTCrPkixPubKeyVerifySignature(PCRTASN1OBJID pAlgorithm, RTCRKEY hPublicKey, PCRTASN1DYNTYPE pParameters,
62 PCRTASN1BITSTRING pSignatureValue, const void *pvData, size_t cbData,
63 PRTERRINFO pErrInfo)
64{
65 /*
66 * Valid input.
67 */
68 AssertPtrReturn(pAlgorithm, VERR_INVALID_POINTER);
69 AssertReturn(RTAsn1ObjId_IsPresent(pAlgorithm), VERR_INVALID_POINTER);
70
71 if (pParameters)
72 {
73 AssertPtrReturn(pParameters, VERR_INVALID_POINTER);
74 if (pParameters->enmType == RTASN1TYPE_NULL)
75 pParameters = NULL;
76 }
77
78 AssertPtrReturn(hPublicKey, VERR_INVALID_POINTER);
79 Assert(RTCrKeyHasPublicPart(hPublicKey));
80
81 AssertPtrReturn(pSignatureValue, VERR_INVALID_POINTER);
82 AssertReturn(RTAsn1BitString_IsPresent(pSignatureValue), VERR_INVALID_POINTER);
83
84 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
85 AssertReturn(cbData > 0, VERR_INVALID_PARAMETER);
86
87 /*
88 * Parameters are not currently supported (openssl code path).
89 */
90 if (pParameters)
91 return RTErrInfoSet(pErrInfo, VERR_CR_PKIX_CIPHER_ALGO_PARAMS_NOT_IMPL,
92 "Cipher algorithm parameters are not yet supported.");
93
94 /*
95 * Validate using IPRT.
96 */
97 RTCRPKIXSIGNATURE hSignature;
98 int rcIprt = RTCrPkixSignatureCreateByObjId(&hSignature, pAlgorithm, hPublicKey, pParameters, false /*fSigning*/);
99 if (RT_FAILURE(rcIprt))
100 return RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_CIPHER_ALGO_NOT_KNOWN,
101 "Unknown public key algorithm [IPRT]: %s", pAlgorithm->szObjId);
102
103 RTCRDIGEST hDigest;
104 rcIprt = RTCrDigestCreateByObjId(&hDigest, pAlgorithm);
105 if (RT_SUCCESS(rcIprt))
106 {
107 /* Calculate the digest. */
108 rcIprt = RTCrDigestUpdate(hDigest, pvData, cbData);
109 if (RT_SUCCESS(rcIprt))
110 {
111 rcIprt = RTCrPkixSignatureVerifyBitString(hSignature, hDigest, pSignatureValue);
112 if (RT_FAILURE(rcIprt))
113 RTErrInfoSet(pErrInfo, rcIprt, "RTCrPkixSignatureVerifyBitString failed");
114 }
115 else
116 RTErrInfoSet(pErrInfo, rcIprt, "RTCrDigestUpdate failed");
117 RTCrDigestRelease(hDigest);
118 }
119 else
120 RTErrInfoSetF(pErrInfo, rcIprt, "Unknown digest algorithm [IPRT]: %s", pAlgorithm->szObjId);
121 RTCrPkixSignatureRelease(hSignature);
122
123#ifdef IPRT_WITH_OPENSSL
124 /*
125 * Validate using OpenSSL EVP.
126 */
127 /* Create an EVP public key. */
128 EVP_PKEY *pEvpPublicKey = NULL;
129 const EVP_MD *pEvpMdType = NULL;
130 int rcOssl = rtCrKeyToOpenSslKeyEx(hPublicKey, true /*fNeedPublic*/, pAlgorithm->szObjId,
131 (void **)&pEvpPublicKey, (const void **)&pEvpMdType, pErrInfo);
132 if (RT_SUCCESS(rcOssl))
133 {
134 EVP_MD_CTX *pEvpMdCtx = EVP_MD_CTX_create();
135 if (pEvpMdCtx)
136 {
137 if (EVP_VerifyInit_ex(pEvpMdCtx, pEvpMdType, NULL /*engine*/))
138 {
139 /* Digest the data. */
140 EVP_VerifyUpdate(pEvpMdCtx, pvData, cbData);
141
142 /* Verify the signature. */
143 if (EVP_VerifyFinal(pEvpMdCtx,
144 RTASN1BITSTRING_GET_BIT0_PTR(pSignatureValue),
145 RTASN1BITSTRING_GET_BYTE_SIZE(pSignatureValue),
146 pEvpPublicKey) > 0)
147 rcOssl = VINF_SUCCESS;
148 else
149 rcOssl = RTErrInfoSet(pErrInfo, VERR_CR_PKIX_OSSL_VERIFY_FINAL_FAILED, "EVP_VerifyFinal failed");
150
151 /* Cleanup and return: */
152 }
153 else
154 rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_CIPHER_ALOG_INIT_FAILED,
155 "EVP_VerifyInit_ex failed (algorithm type is %s)", pAlgorithm->szObjId);
156 EVP_MD_CTX_destroy(pEvpMdCtx);
157 }
158 else
159 rcOssl = RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "EVP_MD_CTX_create failed");
160 EVP_PKEY_free(pEvpPublicKey);
161 }
162
163 /*
164 * Check the result.
165 */
166 if ( (RT_SUCCESS(rcIprt) && RT_SUCCESS(rcOssl))
167 || (RT_FAILURE_NP(rcIprt) && RT_FAILURE_NP(rcOssl))
168 || (RT_SUCCESS(rcIprt) && rcOssl == VERR_CR_PKIX_OSSL_CIPHER_ALGO_NOT_KNOWN_EVP) )
169 return rcIprt;
170 AssertMsgFailed(("rcIprt=%Rrc rcOssl=%Rrc\n", rcIprt, rcOssl));
171 if (RT_FAILURE_NP(rcOssl))
172 return rcOssl;
173#endif /* IPRT_WITH_OPENSSL */
174
175 return rcIprt;
176}
177
178
179RTDECL(int) RTCrPkixPubKeyVerifySignedDigest(PCRTASN1OBJID pAlgorithm, RTCRKEY hPublicKey, PCRTASN1DYNTYPE pParameters,
180 void const *pvSignedDigest, size_t cbSignedDigest, RTCRDIGEST hDigest,
181 PRTERRINFO pErrInfo)
182{
183 /*
184 * Valid input.
185 */
186 AssertPtrReturn(pAlgorithm, VERR_INVALID_POINTER);
187 AssertReturn(RTAsn1ObjId_IsPresent(pAlgorithm), VERR_INVALID_POINTER);
188
189 if (pParameters)
190 {
191 AssertPtrReturn(pParameters, VERR_INVALID_POINTER);
192 if (pParameters->enmType == RTASN1TYPE_NULL)
193 pParameters = NULL;
194 }
195
196 AssertPtrReturn(hPublicKey, VERR_INVALID_POINTER);
197 Assert(RTCrKeyHasPublicPart(hPublicKey));
198
199 AssertPtrReturn(pvSignedDigest, VERR_INVALID_POINTER);
200 AssertReturn(cbSignedDigest, VERR_INVALID_PARAMETER);
201
202 AssertPtrReturn(hDigest, VERR_INVALID_HANDLE);
203
204 /*
205 * Parameters are not currently supported (openssl code path).
206 */
207 if (pParameters)
208 return RTErrInfoSet(pErrInfo, VERR_CR_PKIX_CIPHER_ALGO_PARAMS_NOT_IMPL,
209 "Cipher algorithm parameters are not yet supported.");
210
211 /*
212 * Validate using IPRT.
213 */
214 RTCRPKIXSIGNATURE hSignature;
215 int rcIprt = RTCrPkixSignatureCreateByObjId(&hSignature, pAlgorithm, hPublicKey, pParameters, false /*fSigning*/);
216 if (RT_FAILURE(rcIprt))
217 return RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_CIPHER_ALGO_NOT_KNOWN,
218 "Unknown public key algorithm [IPRT]: %s", pAlgorithm->szObjId);
219
220 rcIprt = RTCrPkixSignatureVerify(hSignature, hDigest, pvSignedDigest, cbSignedDigest);
221 if (RT_FAILURE(rcIprt))
222 RTErrInfoSet(pErrInfo, rcIprt, "RTCrPkixSignatureVerifyBitString failed");
223
224 RTCrPkixSignatureRelease(hSignature);
225
226#if defined(IPRT_WITH_OPENSSL) \
227 && (OPENSSL_VERSION_NUMBER > 0x10000000L) /* 0.9.8 doesn't seem to have EVP_PKEY_CTX_set_signature_md. */
228 /*
229 * Validate using OpenSSL EVP.
230 */
231 /* Combine encryption and digest if the algorithm doesn't specify the digest type. */
232 const char *pszAlgObjId = pAlgorithm->szObjId;
233 if (!strcmp(pszAlgObjId, RTCRX509ALGORITHMIDENTIFIERID_RSA))
234 {
235 pszAlgObjId = RTCrX509AlgorithmIdentifier_CombineEncryptionOidAndDigestOid(pszAlgObjId,
236 RTCrDigestGetAlgorithmOid(hDigest));
237 AssertMsgStmt(pszAlgObjId, ("enc=%s hash=%s\n", pAlgorithm->szObjId, RTCrDigestGetAlgorithmOid(hDigest)),
238 pszAlgObjId = RTCrDigestGetAlgorithmOid(hDigest));
239 }
240
241 /* Create an EVP public key. */
242 EVP_PKEY *pEvpPublicKey = NULL;
243 const EVP_MD *pEvpMdType = NULL;
244 int rcOssl = rtCrKeyToOpenSslKeyEx(hPublicKey, true /*fNeedPublic*/, pszAlgObjId,
245 (void **)&pEvpPublicKey, (const void **)&pEvpMdType, pErrInfo);
246 if (RT_SUCCESS(rcOssl))
247 {
248 /* Create an EVP public key context we can use to validate the digest. */
249 EVP_PKEY_CTX *pEvpPKeyCtx = EVP_PKEY_CTX_new(pEvpPublicKey, NULL);
250 if (pEvpPKeyCtx)
251 {
252 rcOssl = EVP_PKEY_verify_init(pEvpPKeyCtx);
253 if (rcOssl > 0)
254 {
255 rcOssl = EVP_PKEY_CTX_set_signature_md(pEvpPKeyCtx, pEvpMdType);
256 if (rcOssl > 0)
257 {
258 /* Get the digest from hDigest and verify it. */
259 rcOssl = EVP_PKEY_verify(pEvpPKeyCtx,
260 (uint8_t const *)pvSignedDigest,
261 cbSignedDigest,
262 RTCrDigestGetHash(hDigest),
263 RTCrDigestGetHashSize(hDigest));
264 if (rcOssl > 0)
265 rcOssl = VINF_SUCCESS;
266 else
267 rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_VERIFY_FINAL_FAILED,
268 "EVP_PKEY_verify failed (%d)", rcOssl);
269 /* Cleanup and return: */
270 }
271 else
272 rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_TYPE_ERROR,
273 "EVP_PKEY_CTX_set_signature_md failed (%d)", rcOssl);
274 }
275 else
276 rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_TYPE_ERROR,
277 "EVP_PKEY_verify_init failed (%d)", rcOssl);
278 EVP_PKEY_CTX_free(pEvpPKeyCtx);
279 }
280 else
281 rcOssl = RTErrInfoSet(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_TYPE_ERROR, "EVP_PKEY_CTX_new failed");
282 EVP_PKEY_free(pEvpPublicKey);
283 }
284
285 /*
286 * Check the result.
287 */
288 if ( (RT_SUCCESS(rcIprt) && RT_SUCCESS(rcOssl))
289 || (RT_FAILURE_NP(rcIprt) && RT_FAILURE_NP(rcOssl))
290 || (RT_SUCCESS(rcIprt) && rcOssl == VERR_CR_PKIX_OSSL_CIPHER_ALGO_NOT_KNOWN_EVP) )
291 return rcIprt;
292 AssertMsgFailed(("rcIprt=%Rrc rcOssl=%Rrc\n", rcIprt, rcOssl));
293 if (RT_FAILURE_NP(rcOssl))
294 return rcOssl;
295#endif /* IPRT_WITH_OPENSSL */
296
297 return rcIprt;
298}
299
300
301RTDECL(int) RTCrPkixPubKeyVerifySignedDigestByCertPubKeyInfo(PCRTCRX509SUBJECTPUBLICKEYINFO pCertPubKeyInfo,
302 void const *pvSignedDigest, size_t cbSignedDigest,
303 RTCRDIGEST hDigest, PRTERRINFO pErrInfo)
304{
305 RTCRKEY hPublicKey;
306 int rc = RTCrKeyCreateFromPublicAlgorithmAndBits(&hPublicKey, &pCertPubKeyInfo->Algorithm.Algorithm,
307 &pCertPubKeyInfo->SubjectPublicKey, pErrInfo, NULL);
308 if (RT_SUCCESS(rc))
309 {
310 rc = RTCrPkixPubKeyVerifySignedDigest(&pCertPubKeyInfo->Algorithm.Algorithm, hPublicKey,
311 &pCertPubKeyInfo->Algorithm.Parameters, pvSignedDigest, cbSignedDigest,
312 hDigest, pErrInfo);
313
314 uint32_t cRefs = RTCrKeyRelease(hPublicKey);
315 Assert(cRefs == 0); RT_NOREF(cRefs);
316 }
317 return rc;
318}
319
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