Coverage Report

Created: 2026-10-28 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtbase/src/plugins/tls/openssl/qssldiffiehellmanparameters_openssl.cpp
Line
Count
Source
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
6
#include "qsslsocket_openssl_symbols_p.h"
7
#include "qtlsbackend_openssl_p.h"
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
19
QT_BEGIN_NAMESPACE
20
21
#ifndef OPENSSL_NO_DEPRECATED_3_0
22
23
namespace {
24
25
bool isSafeDH(DH *dh)
26
0
{
27
0
    int status = 0;
28
0
    int bad = 0;
29
30
    // TLSTODO: check it's needed or if supportsSsl()
31
    // is enough.
32
0
    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
0
    if (q_DH_bits(dh) < 1024)
47
0
        return false;
48
49
0
    if (q_DH_check(dh, &status) != 1)
50
0
        return false;
51
52
0
    const BIGNUM *p = nullptr;
53
0
    const BIGNUM *q = nullptr;
54
0
    const BIGNUM *g = nullptr;
55
0
    q_DH_get0_pqg(dh, &p, &q, &g);
56
57
0
    if (q_BN_is_word(const_cast<BIGNUM *>(g), DH_GENERATOR_2)) {
58
0
        const unsigned long residue = q_BN_mod_word(p, 24);
59
0
        if (residue == 11 || residue == 23)
60
0
            status &= ~DH_NOT_SUITABLE_GENERATOR;
61
0
    }
62
63
0
    bad |= DH_CHECK_P_NOT_PRIME;
64
0
    bad |= DH_CHECK_P_NOT_SAFE_PRIME;
65
0
    bad |= DH_NOT_SUITABLE_GENERATOR;
66
67
0
    return !(status & bad);
68
0
}
69
70
} // unnamed namespace
71
72
#endif
73
74
int QTlsBackendOpenSSL::dhParametersFromDer(const QByteArray &der, QByteArray *derData) const
75
0
{
76
0
#ifndef OPENSSL_NO_DEPRECATED_3_0
77
0
    Q_ASSERT(derData);
78
79
0
    if (der.isEmpty())
80
0
        return DHParams::InvalidInputDataError;
81
82
0
    const unsigned char *data = reinterpret_cast<const unsigned char *>(der.data());
83
0
    const int len = der.size();
84
85
    // TLSTODO: check it's needed (loading ciphers and certs in
86
    // addition to the library!)
87
0
    QSslSocketPrivate::ensureInitialized();
88
89
0
    DH *dh = q_d2i_DHparams(nullptr, &data, len);
90
0
    if (dh) {
91
0
        const auto dhRaii = qScopeGuard([dh] {q_DH_free(dh);});
92
93
0
        if (isSafeDH(dh))
94
0
            *derData = der;
95
0
        else
96
0
            return DHParams::UnsafeParametersError;
97
0
    } else {
98
0
        return DHParams::InvalidInputDataError;
99
0
    }
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
0
    return DHParams::NoError;
106
0
}
107
108
int QTlsBackendOpenSSL::dhParametersFromPem(const QByteArray &pem, QByteArray *data) const
109
0
{
110
0
#ifndef OPENSSL_NO_DEPRECATED_3_0
111
0
    Q_ASSERT(data);
112
113
0
    if (pem.isEmpty())
114
0
        return DHParams::InvalidInputDataError;
115
116
    // TLSTODO: check it was not a cargo-cult programming in case of
117
    // DH ...
118
0
    QSslSocketPrivate::ensureInitialized();
119
120
0
    BIO *bio = q_BIO_new_mem_buf(const_cast<char *>(pem.data()), pem.size());
121
0
    if (!bio)
122
0
        return DHParams::InvalidInputDataError;
123
124
0
    const auto bioRaii = qScopeGuard([bio]
125
0
    {
126
0
        q_BIO_free(bio);
127
0
    });
128
129
0
    DH *dh = nullptr;
130
0
    q_PEM_read_bio_DHparams(bio, &dh, nullptr, nullptr);
131
132
0
    if (dh) {
133
0
        const auto dhGuard = qScopeGuard([dh]
134
0
        {
135
0
            q_DH_free(dh);
136
0
        });
137
138
0
        if (isSafeDH(dh)) {
139
0
            char *buf = nullptr;
140
0
            const int len = q_i2d_DHparams(dh, reinterpret_cast<unsigned char **>(&buf));
141
0
            const auto freeBuf = qScopeGuard([&] { q_OPENSSL_free(buf); });
142
0
            if (len > 0)
143
0
                data->assign({buf, len});
144
0
            else
145
0
                return DHParams::InvalidInputDataError;
146
0
        } else {
147
0
            return DHParams::UnsafeParametersError;
148
0
        }
149
0
    } else {
150
0
        return DHParams::InvalidInputDataError;
151
0
    }
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
0
    return DHParams::NoError;
158
0
}
159
160
QT_END_NAMESPACE