VirtualBox

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

Last change on this file was 100442, checked in by vboxsync, 11 months ago

IPRT,OpenSSL: Support ECDSA for verficiation purposes when IPRT links with OpenSSL. This required quite a bit of cleanups, so not entirely no-risk. bugref:10479 ticketref:21621

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.1 KB
Line 
1/* $Id: pkix-verify.cpp 100442 2023-07-08 11:10:51Z 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 AssertPtrReturn(hPublicKey, VERR_INVALID_POINTER);
72 Assert(RTCrKeyHasPublicPart(hPublicKey));
73 RTCRKEYTYPE const enmKeyType = RTCrKeyGetType(hPublicKey);
74 AssertReturn(enmKeyType != RTCRKEYTYPE_INVALID, VERR_INVALID_HANDLE);
75
76 AssertPtrReturn(pSignatureValue, VERR_INVALID_POINTER);
77 AssertReturn(RTAsn1BitString_IsPresent(pSignatureValue), VERR_INVALID_POINTER);
78
79 AssertPtrReturn(pvData, VERR_INVALID_POINTER);
80 AssertReturn(cbData > 0, VERR_INVALID_PARAMETER);
81
82 /*
83 * Verify that the parameters are compatible with the key. We ASSUME the
84 * parameters are for a hash+cryption combination, like those found in
85 * RTCRX509TBSCERTIFICATE::Signature. At present, these should NULL (or
86 * absent) for the two key types we support RSA & ECDSA, which is an
87 * ASSUMPTION by the OpenSSL code below.
88 */
89 int rcIprt = RTCrKeyVerifyParameterCompatibility(hPublicKey, pParameters, true /*fForSignature*/, pAlgorithm, pErrInfo);
90 AssertRCReturn(rcIprt, rcIprt);
91
92 /*
93 * Validate using IPRT.
94 */
95 RTCRPKIXSIGNATURE hSignature;
96 rcIprt = RTCrPkixSignatureCreateByObjId(&hSignature, pAlgorithm, hPublicKey, pParameters, false /*fSigning*/);
97 if (RT_FAILURE(rcIprt))
98 return RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_CIPHER_ALGO_NOT_KNOWN,
99 "Unknown public key algorithm [IPRT %Rrc]: %s", rcIprt, pAlgorithm->szObjId);
100
101 RTCRDIGEST hDigest;
102 rcIprt = RTCrDigestCreateByObjId(&hDigest, pAlgorithm);
103 if (RT_SUCCESS(rcIprt))
104 {
105 /* Calculate the digest. */
106 rcIprt = RTCrDigestUpdate(hDigest, pvData, cbData);
107 if (RT_SUCCESS(rcIprt))
108 {
109 rcIprt = RTCrPkixSignatureVerifyBitString(hSignature, hDigest, pSignatureValue);
110 if (RT_FAILURE(rcIprt))
111 RTErrInfoSet(pErrInfo, rcIprt, "RTCrPkixSignatureVerifyBitString failed");
112 }
113 else
114 RTErrInfoSet(pErrInfo, rcIprt, "RTCrDigestUpdate failed");
115 RTCrDigestRelease(hDigest);
116 }
117 else
118 RTErrInfoSetF(pErrInfo, rcIprt, "Unknown digest algorithm [IPRT]: %s", pAlgorithm->szObjId);
119 RTCrPkixSignatureRelease(hSignature);
120
121#ifdef IPRT_WITH_OPENSSL
122 /* We don't implement digest+cipher parameters in OpenSSL (or at all),
123 RTCrKeyVerifyParameterCompatibility should ensure we don't get here
124 (ASSUMING only RSA and ECDSA keys). But, just in case, bail out if we do. */
125 AssertReturn( !pParameters
126 || pParameters->enmType == RTASN1TYPE_NULL
127 || pParameters->enmType == RTASN1TYPE_NOT_PRESENT,
128 VERR_CR_PKIX_CIPHER_ALGO_PARAMS_NOT_IMPL);
129
130 /*
131 * Validate using OpenSSL EVP.
132 */
133 /* Create an EVP public key. */
134 EVP_PKEY *pEvpPublicKey = NULL;
135 const EVP_MD *pEvpMdType = NULL;
136 int rcOssl = rtCrKeyToOpenSslKeyEx(hPublicKey, true /*fNeedPublic*/, pAlgorithm->szObjId,
137 (void **)&pEvpPublicKey, (const void **)&pEvpMdType, pErrInfo);
138 if (RT_SUCCESS(rcOssl))
139 {
140 EVP_MD_CTX *pEvpMdCtx = EVP_MD_CTX_create();
141 if (pEvpMdCtx)
142 {
143 if (EVP_VerifyInit_ex(pEvpMdCtx, pEvpMdType, NULL /*engine*/))
144 {
145 /* Digest the data. */
146 EVP_VerifyUpdate(pEvpMdCtx, pvData, cbData);
147
148 /* Verify the signature. */
149 if (EVP_VerifyFinal(pEvpMdCtx,
150 RTASN1BITSTRING_GET_BIT0_PTR(pSignatureValue),
151 RTASN1BITSTRING_GET_BYTE_SIZE(pSignatureValue),
152 pEvpPublicKey) > 0)
153 rcOssl = VINF_SUCCESS;
154 else
155 rcOssl = RTErrInfoSet(pErrInfo, VERR_CR_PKIX_OSSL_VERIFY_FINAL_FAILED, "EVP_VerifyFinal failed");
156
157 /* Cleanup and return: */
158 }
159 else
160 rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_CIPHER_ALOG_INIT_FAILED,
161 "EVP_VerifyInit_ex failed (algorithm type is %s)", pAlgorithm->szObjId);
162 EVP_MD_CTX_destroy(pEvpMdCtx);
163 }
164 else
165 rcOssl = RTErrInfoSetF(pErrInfo, VERR_NO_MEMORY, "EVP_MD_CTX_create failed");
166 EVP_PKEY_free(pEvpPublicKey);
167 }
168
169 /*
170 * Check the result.
171 */
172 if ( (RT_SUCCESS(rcIprt) && RT_SUCCESS(rcOssl))
173 || (RT_FAILURE_NP(rcIprt) && RT_FAILURE_NP(rcOssl))
174 || (RT_SUCCESS(rcIprt) && rcOssl == VERR_CR_PKIX_OSSL_CIPHER_ALGO_NOT_KNOWN_EVP) )
175 return rcIprt;
176 AssertMsgFailed(("rcIprt=%Rrc rcOssl=%Rrc\n", rcIprt, rcOssl));
177 if (RT_FAILURE_NP(rcOssl))
178 return rcOssl;
179#endif /* IPRT_WITH_OPENSSL */
180
181 return rcIprt;
182}
183
184
185RTDECL(int) RTCrPkixPubKeyVerifySignedDigest(PCRTASN1OBJID pAlgorithm, RTCRKEY hPublicKey, PCRTASN1DYNTYPE pParameters,
186 void const *pvSignedDigest, size_t cbSignedDigest, RTCRDIGEST hDigest,
187 PRTERRINFO pErrInfo)
188{
189 /*
190 * Valid input.
191 */
192 AssertPtrReturn(pAlgorithm, VERR_INVALID_POINTER);
193 AssertReturn(RTAsn1ObjId_IsPresent(pAlgorithm), VERR_INVALID_POINTER);
194
195 AssertPtrReturn(hPublicKey, VERR_INVALID_POINTER);
196 Assert(RTCrKeyHasPublicPart(hPublicKey));
197 RTCRKEYTYPE const enmKeyType = RTCrKeyGetType(hPublicKey);
198 AssertReturn(enmKeyType != RTCRKEYTYPE_INVALID, VERR_INVALID_HANDLE);
199
200 AssertPtrReturn(pvSignedDigest, VERR_INVALID_POINTER);
201 AssertReturn(cbSignedDigest, VERR_INVALID_PARAMETER);
202
203 AssertPtrReturn(hDigest, VERR_INVALID_HANDLE);
204
205 /*
206 * Verify that the parameters are compatible with the key. We ASSUME the
207 * parameters are for a hash+cryption combination, like those found in
208 * RTCRX509TBSCERTIFICATE::Signature. At present, these should NULL (or
209 * absent) for the two key types we support RSA & ECDSA, which is an
210 * ASSUMPTION by the OpenSSL code below.
211 */
212 int rcIprt = RTCrKeyVerifyParameterCompatibility(hPublicKey, pParameters, true /*fForSignature*/, pAlgorithm, pErrInfo);
213 AssertRCReturn(rcIprt, rcIprt);
214
215 /*
216 * Validate using IPRT.
217 */
218 RTCRPKIXSIGNATURE hSignature;
219 rcIprt = RTCrPkixSignatureCreateByObjId(&hSignature, pAlgorithm, hPublicKey, pParameters, false /*fSigning*/);
220 if (RT_FAILURE(rcIprt))
221 return RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_CIPHER_ALGO_NOT_KNOWN,
222 "Unknown public key algorithm [IPRT %Rrc]: %s", rcIprt, pAlgorithm->szObjId);
223
224 rcIprt = RTCrPkixSignatureVerify(hSignature, hDigest, pvSignedDigest, cbSignedDigest);
225 if (RT_FAILURE(rcIprt))
226 RTErrInfoSet(pErrInfo, rcIprt, "RTCrPkixSignatureVerifyBitString failed");
227
228 RTCrPkixSignatureRelease(hSignature);
229
230#if defined(IPRT_WITH_OPENSSL) \
231 && (OPENSSL_VERSION_NUMBER > 0x10000000L) /* 0.9.8 doesn't seem to have EVP_PKEY_CTX_set_signature_md. */
232 /*
233 * Validate using OpenSSL EVP.
234 */
235 /* Make sure the algorithm includes the digest and isn't just RSA, ECDSA or similar. */
236 const char *pszAlgObjId = RTCrX509AlgorithmIdentifier_CombineEncryptionOidAndDigestOid(pAlgorithm->szObjId,
237 RTCrDigestGetAlgorithmOid(hDigest));
238 AssertMsgStmt(pszAlgObjId, ("enc=%s hash=%s\n", pAlgorithm->szObjId, RTCrDigestGetAlgorithmOid(hDigest)),
239 pszAlgObjId = RTCrDigestGetAlgorithmOid(hDigest));
240
241 /* We don't implement digest+cipher parameters in OpenSSL (or at all),
242 RTCrKeyVerifyParameterCompatibility should ensure we don't get here
243 (ASSUMING only RSA and ECDSA keys). But, just in case, bail out if we do. */
244 AssertReturn( !pParameters
245 || pParameters->enmType == RTASN1TYPE_NULL
246 || pParameters->enmType == RTASN1TYPE_NOT_PRESENT,
247 VERR_CR_PKIX_CIPHER_ALGO_PARAMS_NOT_IMPL);
248
249 /* Create an EVP public key. */
250 EVP_PKEY *pEvpPublicKey = NULL;
251 const EVP_MD *pEvpMdType = NULL;
252 int rcOssl = rtCrKeyToOpenSslKeyEx(hPublicKey, true /*fNeedPublic*/, pszAlgObjId,
253 (void **)&pEvpPublicKey, (const void **)&pEvpMdType, pErrInfo);
254 if (RT_SUCCESS(rcOssl))
255 {
256 /* Create an EVP public key context we can use to validate the digest. */
257 EVP_PKEY_CTX *pEvpPKeyCtx = EVP_PKEY_CTX_new(pEvpPublicKey, NULL);
258 if (pEvpPKeyCtx)
259 {
260 rcOssl = EVP_PKEY_verify_init(pEvpPKeyCtx);
261 if (rcOssl > 0)
262 {
263 rcOssl = EVP_PKEY_CTX_set_signature_md(pEvpPKeyCtx, pEvpMdType);
264 if (rcOssl > 0)
265 {
266 /* Get the digest from hDigest and verify it. */
267 rcOssl = EVP_PKEY_verify(pEvpPKeyCtx,
268 (uint8_t const *)pvSignedDigest,
269 cbSignedDigest,
270 RTCrDigestGetHash(hDigest),
271 RTCrDigestGetHashSize(hDigest));
272 if (rcOssl > 0)
273 rcOssl = VINF_SUCCESS;
274 else
275 rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_VERIFY_FINAL_FAILED,
276 "EVP_PKEY_verify failed (%d)", rcOssl);
277 /* Cleanup and return: */
278 }
279 else
280 rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_TYPE_ERROR,
281 "EVP_PKEY_CTX_set_signature_md failed (%d)", rcOssl);
282 }
283 else
284 rcOssl = RTErrInfoSetF(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_TYPE_ERROR,
285 "EVP_PKEY_verify_init failed (%d)", rcOssl);
286 EVP_PKEY_CTX_free(pEvpPKeyCtx);
287 }
288 else
289 rcOssl = RTErrInfoSet(pErrInfo, VERR_CR_PKIX_OSSL_EVP_PKEY_TYPE_ERROR, "EVP_PKEY_CTX_new failed");
290 EVP_PKEY_free(pEvpPublicKey);
291 }
292
293 /*
294 * Check the result.
295 */
296 if ( (RT_SUCCESS(rcIprt) && RT_SUCCESS(rcOssl))
297 || (RT_FAILURE_NP(rcIprt) && RT_FAILURE_NP(rcOssl))
298 || (RT_SUCCESS(rcIprt) && rcOssl == VERR_CR_PKIX_OSSL_CIPHER_ALGO_NOT_KNOWN_EVP) )
299 return rcIprt;
300 AssertMsgFailed(("rcIprt=%Rrc rcOssl=%Rrc\n", rcIprt, rcOssl));
301 if (RT_FAILURE_NP(rcOssl))
302 return rcOssl;
303#endif /* IPRT_WITH_OPENSSL */
304
305 return rcIprt;
306}
307
308
309RTDECL(int) RTCrPkixPubKeyVerifySignedDigestByCertPubKeyInfo(PCRTCRX509SUBJECTPUBLICKEYINFO pCertPubKeyInfo,
310 void const *pvSignedDigest, size_t cbSignedDigest,
311 RTCRDIGEST hDigest, PRTERRINFO pErrInfo)
312{
313 RTCRKEY hPublicKey;
314 int rc = RTCrKeyCreateFromPublicAlgorithmAndBits(&hPublicKey, &pCertPubKeyInfo->Algorithm.Algorithm,
315 &pCertPubKeyInfo->Algorithm.Parameters,
316 &pCertPubKeyInfo->SubjectPublicKey, pErrInfo, NULL);
317 if (RT_SUCCESS(rc))
318 {
319 /** @todo r=bird (2023-07-06): This ASSUMES no digest+cipher parameters, which
320 * is the case for RSA and ECDSA. */
321 rc = RTCrPkixPubKeyVerifySignedDigest(&pCertPubKeyInfo->Algorithm.Algorithm, hPublicKey, NULL,
322 pvSignedDigest, cbSignedDigest, hDigest, pErrInfo);
323
324 uint32_t cRefs = RTCrKeyRelease(hPublicKey);
325 Assert(cRefs == 0); RT_NOREF(cRefs);
326 }
327 return rc;
328}
329
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use