1/****************************************************************************
2**
3** Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk>
4** Copyright (C) 2016 Richard J. Moore <rich@kde.org>
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtNetwork module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "qssldiffiehellmanparameters.h"
42#include "qssldiffiehellmanparameters_p.h"
43#include "qsslsocket_openssl_symbols_p.h"
44#include "qsslsocket.h"
45#include "qsslsocket_p.h"
46
47#include "private/qssl_p.h"
48
49#include <QtCore/qatomic.h>
50#include <QtCore/qbytearray.h>
51#include <QtCore/qiodevice.h>
52#include <QtCore/qscopeguard.h>
53#ifndef QT_NO_DEBUG_STREAM
54#include <QtCore/qdebug.h>
55#endif
56
57#include <openssl/bn.h>
58#include <openssl/dh.h>
59
60QT_BEGIN_NAMESPACE
61
62#ifdef OPENSSL_NO_DEPRECATED_3_0
63
64static int q_DH_check(DH *dh, int *status)
65{
66 // DH_check was first deprecated in OpenSSL 3.0.0, as low-level
67 // API; the EVP_PKEY family of functions was advised as an alternative.
68 // As of now EVP_PKEY_params_check ends up calling ... DH_check,
69 // which is good enough.
70
71 Q_ASSERT(dh);
72 Q_ASSERT(status);
73
74 EVP_PKEY *key = q_EVP_PKEY_new();
75 if (!key) {
76 qCWarning(lcSsl, "EVP_PKEY_new failed");
77 QSslSocketBackendPrivate::logAndClearErrorQueue();
78 return 0;
79 }
80 const auto keyDeleter = qScopeGuard([key](){
81 q_EVP_PKEY_free(key);
82 });
83 if (!q_EVP_PKEY_set1_DH(key, dh)) {
84 qCWarning(lcSsl, "EVP_PKEY_set1_DH failed");
85 QSslSocketBackendPrivate::logAndClearErrorQueue();
86 return 0;
87 }
88
89 EVP_PKEY_CTX *keyCtx = q_EVP_PKEY_CTX_new(key, nullptr);
90 if (!keyCtx) {
91 qCWarning(lcSsl, "EVP_PKEY_CTX_new failed");
92 QSslSocketBackendPrivate::logAndClearErrorQueue();
93 return 0;
94 }
95 const auto ctxDeleter = qScopeGuard([keyCtx]{
96 q_EVP_PKEY_CTX_free(keyCtx);
97 });
98
99 const int result = q_EVP_PKEY_param_check(keyCtx);
100 QSslSocketBackendPrivate::logAndClearErrorQueue();
101 // Note: unlike DH_check, we cannot obtain the 'status',
102 // if the 'result' is 0 (actually the result is 1 only
103 // if this 'status' was 0). We could probably check the
104 // errors from the error queue, but it's not needed anyway
105 // - see the 'isSafeDH' below, how it returns immediately
106 // on 0.
107 Q_UNUSED(status)
108
109 return result;
110}
111#endif // OPENSSL_NO_DEPRECATED_3_0
112
113static bool isSafeDH(DH *dh)
114{
115 int status = 0;
116 int bad = 0;
117
118 QSslSocketPrivate::ensureInitialized();
119
120
121 // From https://wiki.openssl.org/index.php/Diffie-Hellman_parameters:
122 //
123 // The additional call to BN_mod_word(dh->p, 24)
124 // (and unmasking of DH_NOT_SUITABLE_GENERATOR)
125 // is performed to ensure your program accepts
126 // IETF group parameters. OpenSSL checks the prime
127 // is congruent to 11 when g = 2; while the IETF's
128 // primes are congruent to 23 when g = 2.
129 // Without the test, the IETF parameters would
130 // fail validation. For details, see Diffie-Hellman
131 // Parameter Check (when g = 2, must p mod 24 == 11?).
132 // Mark p < 1024 bits as unsafe.
133 if (q_DH_bits(dh) < 1024)
134 return false;
135
136 if (q_DH_check(dh, codes: &status) != 1)
137 return false;
138
139 const BIGNUM *p = nullptr;
140 const BIGNUM *q = nullptr;
141 const BIGNUM *g = nullptr;
142 q_DH_get0_pqg(dh, p: &p, q: &q, g: &g);
143
144 if (q_BN_is_word(a: const_cast<BIGNUM *>(g), DH_GENERATOR_2)) {
145 const unsigned long residue = q_BN_mod_word(a: p, w: 24);
146 if (residue == 11 || residue == 23)
147 status &= ~DH_NOT_SUITABLE_GENERATOR;
148 }
149
150 bad |= DH_CHECK_P_NOT_PRIME;
151 bad |= DH_CHECK_P_NOT_SAFE_PRIME;
152 bad |= DH_NOT_SUITABLE_GENERATOR;
153
154 return !(status & bad);
155}
156
157void QSslDiffieHellmanParametersPrivate::decodeDer(const QByteArray &der)
158{
159 if (der.isEmpty()) {
160 error = QSslDiffieHellmanParameters::InvalidInputDataError;
161 return;
162 }
163
164 const unsigned char *data = reinterpret_cast<const unsigned char *>(der.data());
165 int len = der.size();
166
167 QSslSocketPrivate::ensureInitialized();
168
169 DH *dh = q_d2i_DHparams(a: nullptr, pp: &data, length: len);
170 if (dh) {
171 if (isSafeDH(dh))
172 derData = der;
173 else
174 error = QSslDiffieHellmanParameters::UnsafeParametersError;
175 } else {
176 error = QSslDiffieHellmanParameters::InvalidInputDataError;
177 }
178
179 q_DH_free(dh);
180}
181
182void QSslDiffieHellmanParametersPrivate::decodePem(const QByteArray &pem)
183{
184 if (pem.isEmpty()) {
185 error = QSslDiffieHellmanParameters::InvalidInputDataError;
186 return;
187 }
188
189 if (!QSslSocket::supportsSsl()) {
190 error = QSslDiffieHellmanParameters::InvalidInputDataError;
191 return;
192 }
193
194 QSslSocketPrivate::ensureInitialized();
195
196 BIO *bio = q_BIO_new_mem_buf(a: const_cast<char *>(pem.data()), b: pem.size());
197 if (!bio) {
198 error = QSslDiffieHellmanParameters::InvalidInputDataError;
199 return;
200 }
201
202 DH *dh = nullptr;
203 q_PEM_read_bio_DHparams(a: bio, b: &dh, c: nullptr, d: nullptr);
204
205 if (dh) {
206 if (isSafeDH(dh)) {
207 char *buf = nullptr;
208 int len = q_i2d_DHparams(a: dh, p: reinterpret_cast<unsigned char **>(&buf));
209 if (len > 0)
210 derData = QByteArray(buf, len);
211 else
212 error = QSslDiffieHellmanParameters::InvalidInputDataError;
213 } else {
214 error = QSslDiffieHellmanParameters::UnsafeParametersError;
215 }
216 } else {
217 error = QSslDiffieHellmanParameters::InvalidInputDataError;
218 }
219
220 q_DH_free(dh);
221 q_BIO_free(a: bio);
222}
223
224QT_END_NAMESPACE
225

source code of qtbase/src/network/ssl/qssldiffiehellmanparameters_openssl.cpp