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