1// Copyright (C) 2015 Mikkel Krautz <mikkel@krautz.dk>
2// Copyright (C) 2016 Richard J. Moore <rich@kde.org>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qsslsocket_openssl_symbols_p.h"
6#include "qtlsbackend_openssl_p.h"
7
8#include <QtNetwork/private/qsslsocket_p.h>
9
10#include <QtCore/qscopeguard.h>
11#include <QtCore/qbytearray.h>
12#include <QtCore/qiodevice.h>
13#include <QtCore/qdebug.h>
14
15#include <openssl/bn.h>
16#include <openssl/dh.h>
17
18QT_BEGIN_NAMESPACE
19
20#ifndef OPENSSL_NO_DEPRECATED_3_0
21
22namespace {
23
24bool isSafeDH(DH *dh)
25{
26 int status = 0;
27 int bad = 0;
28
29 // TLSTODO: check it's needed or if supportsSsl()
30 // is enough.
31 QSslSocketPrivate::ensureInitialized();
32
33 // From https://wiki.openssl.org/index.php/Diffie-Hellman_parameters:
34 //
35 // The additional call to BN_mod_word(dh->p, 24)
36 // (and unmasking of DH_NOT_SUITABLE_GENERATOR)
37 // is performed to ensure your program accepts
38 // IETF group parameters. OpenSSL checks the prime
39 // is congruent to 11 when g = 2; while the IETF's
40 // primes are congruent to 23 when g = 2.
41 // Without the test, the IETF parameters would
42 // fail validation. For details, see Diffie-Hellman
43 // Parameter Check (when g = 2, must p mod 24 == 11?).
44 // Mark p < 1024 bits as unsafe.
45 if (q_DH_bits(dh) < 1024)
46 return false;
47
48 if (q_DH_check(dh, codes: &status) != 1)
49 return false;
50
51 const BIGNUM *p = nullptr;
52 const BIGNUM *q = nullptr;
53 const BIGNUM *g = nullptr;
54 q_DH_get0_pqg(dh, p: &p, q: &q, g: &g);
55
56 if (q_BN_is_word(a: const_cast<BIGNUM *>(g), DH_GENERATOR_2)) {
57 const unsigned long residue = q_BN_mod_word(a: p, w: 24);
58 if (residue == 11 || residue == 23)
59 status &= ~DH_NOT_SUITABLE_GENERATOR;
60 }
61
62 bad |= DH_CHECK_P_NOT_PRIME;
63 bad |= DH_CHECK_P_NOT_SAFE_PRIME;
64 bad |= DH_NOT_SUITABLE_GENERATOR;
65
66 return !(status & bad);
67}
68
69} // unnamed namespace
70
71#endif
72
73int QTlsBackendOpenSSL::dhParametersFromDer(const QByteArray &der, QByteArray *derData) const
74{
75#ifndef OPENSSL_NO_DEPRECATED_3_0
76 Q_ASSERT(derData);
77
78 if (der.isEmpty())
79 return DHParams::InvalidInputDataError;
80
81 const unsigned char *data = reinterpret_cast<const unsigned char *>(der.data());
82 const int len = der.size();
83
84 // TLSTODO: check it's needed (loading ciphers and certs in
85 // addition to the library!)
86 QSslSocketPrivate::ensureInitialized();
87
88 DH *dh = q_d2i_DHparams(a: nullptr, pp: &data, length: len);
89 if (dh) {
90 const auto dhRaii = qScopeGuard(f: [dh] {q_DH_free(dh);});
91
92 if (isSafeDH(dh))
93 *derData = der;
94 else
95 return DHParams::UnsafeParametersError;
96 } else {
97 return DHParams::InvalidInputDataError;
98 }
99#else
100 Q_UNUSED(der);
101 Q_UNUSED(derData);
102 qCWarning(lcTlsBackend, "Diffie-Hellman parameters are not supported, because OpenSSL v3 was built with deprecated API removed");
103#endif
104 return DHParams::NoError;
105}
106
107int QTlsBackendOpenSSL::dhParametersFromPem(const QByteArray &pem, QByteArray *data) const
108{
109#ifndef OPENSSL_NO_DEPRECATED_3_0
110 Q_ASSERT(data);
111
112 if (pem.isEmpty())
113 return DHParams::InvalidInputDataError;
114
115 // TLSTODO: check it was not a cargo-cult programming in case of
116 // DH ...
117 QSslSocketPrivate::ensureInitialized();
118
119 BIO *bio = q_BIO_new_mem_buf(a: const_cast<char *>(pem.data()), b: pem.size());
120 if (!bio)
121 return DHParams::InvalidInputDataError;
122
123 const auto bioRaii = qScopeGuard(f: [bio]
124 {
125 q_BIO_free(a: bio);
126 });
127
128 DH *dh = nullptr;
129 q_PEM_read_bio_DHparams(a: bio, b: &dh, c: nullptr, d: nullptr);
130
131 if (dh) {
132 const auto dhGuard = qScopeGuard(f: [dh]
133 {
134 q_DH_free(dh);
135 });
136
137 if (isSafeDH(dh)) {
138 char *buf = nullptr;
139 const int len = q_i2d_DHparams(a: dh, p: reinterpret_cast<unsigned char **>(&buf));
140 const auto freeBuf = qScopeGuard(f: [&] { q_OPENSSL_free(buf); });
141 if (len > 0)
142 data->assign(v: {buf, len});
143 else
144 return DHParams::InvalidInputDataError;
145 } else {
146 return DHParams::UnsafeParametersError;
147 }
148 } else {
149 return DHParams::InvalidInputDataError;
150 }
151#else
152 Q_UNUSED(pem);
153 Q_UNUSED(data);
154 qCWarning(lcTlsBackend, "Diffie-Hellman parameters are not supported, because OpenSSL v3 was built with deprecated API removed");
155#endif
156 return DHParams::NoError;
157}
158
159QT_END_NAMESPACE
160

source code of qtbase/src/plugins/tls/openssl/qssldiffiehellmanparameters_openssl.cpp