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
qpassworddigestor.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:critical reason:cryptography
4
6
7#include <QtCore/QDebug>
8#include <QtCore/QMessageAuthenticationCode>
9#include <QtCore/QtEndian>
10#include <QtCore/QList>
11
12#include "qtcore-config_p.h"
13
14#include <limits>
15
16#if QT_CONFIG(opensslv30) && QT_CONFIG(openssl_linked)
17#define USING_OPENSSL30
18#include <openssl/core_names.h>
19#include <openssl/kdf.h>
20#include <openssl/params.h>
21#include <openssl/provider.h>
22#endif
23
24QT_BEGIN_NAMESPACE
26
27/*!
28 \namespace QPasswordDigestor
29 \inmodule QtNetwork
30
31 \brief The QPasswordDigestor namespace contains functions which you can use
32 to generate hashes or keys.
33*/
34
35/*!
36 \since 5.12
37
38 Returns a hash computed using the PBKDF1-algorithm as defined in
39 \l {RFC 8018, section 5.1}.
40
41 The function takes the \a data and \a salt, and then hashes it repeatedly
42 for \a iterations iterations using the specified hash \a algorithm. If the
43 resulting hash is longer than \a dkLen then it is truncated before it is
44 returned.
45
46 This function only supports SHA-1 and MD5! The max output size is 160 bits
47 (20 bytes) when using SHA-1, or 128 bits (16 bytes) when using MD5.
48 Specifying a value for \a dkLen which is greater than this results in a
49 warning and an empty QByteArray is returned. To programmatically check this
50 limit you can use \l {QCryptographicHash::hashLength}. Furthermore: the
51 \a salt must always be 8 bytes long!
52
53 \note This function is provided for use with legacy applications and all
54 new applications are recommended to use \l {deriveKeyPbkdf2} {PBKDF2}.
55
56 \sa deriveKeyPbkdf2, QCryptographicHash, QCryptographicHash::hashLength
57*/
59 const QByteArray &data, const QByteArray &salt,
61{
62 // https://tools.ietf.org/html/rfc8018#section-5.1
63
66 ) {
67 qWarning("The only supported algorithms for pbkdf1 are SHA-1 and MD5!");
68 return QByteArray();
69 }
70
71 if (salt.size() != 8) {
72 qWarning("The salt must be 8 bytes long!");
73 return QByteArray();
74 }
75 if (iterations < 1 || dkLen < 1)
76 return QByteArray();
77
79 qWarning() << "Derived key too long:\n"
80 << algorithm << "was chosen which produces output of length"
82 << "was requested.";
83 return QByteArray();
84 }
85
90
91 for (int i = 1; i < iterations; i++) {
92 hash.reset();
94 key = hash.result();
95 }
96 return key.left(dkLen);
97}
98
99#ifdef USING_OPENSSL30
100// Copied from QCryptographicHashPrivate
101static constexpr const char * methodToName(QCryptographicHash::Algorithm method) noexcept
102{
103 switch (method) {
104#define CASE(Enum, Name)
105 case QCryptographicHash:: Enum :
106 return Name
107 /*end*/
108 CASE(Sha1, "SHA1");
109 CASE(Md4, "MD4");
110 CASE(Md5, "MD5");
111 CASE(Sha224, "SHA224");
112 CASE(Sha256, "SHA256");
113 CASE(Sha384, "SHA384");
114 CASE(Sha512, "SHA512");
115 CASE(RealSha3_224, "SHA3-224");
116 CASE(RealSha3_256, "SHA3-256");
117 CASE(RealSha3_384, "SHA3-384");
118 CASE(RealSha3_512, "SHA3-512");
119 CASE(Keccak_224, "SHA3-224");
120 CASE(Keccak_256, "SHA3-256");
121 CASE(Keccak_384, "SHA3-384");
122 CASE(Keccak_512, "SHA3-512");
123 CASE(Blake2b_512, "BLAKE2B512");
124 CASE(Blake2s_256, "BLAKE2S256");
125#undef CASE
126 default: return nullptr;
127 }
128}
129
131 const QByteArray &data, const QByteArray &salt,
133{
134 EVP_KDF *kdf = EVP_KDF_fetch(nullptr, "PBKDF2", nullptr);
135
136 if (!kdf)
137 return QByteArray();
138
139 auto cleanUpKdf = qScopeGuard([kdf] {
141 });
142
144
145 if (!ctx)
146 return QByteArray();
147
148 auto cleanUpCtx = qScopeGuard([ctx] {
150 });
151
152 // Do not enable SP800-132 compliance check, otherwise we will require:
153 // - the iteration count is at least 1000
154 // - the salt length is at least 128 bits
155 // - the derived key length is at least 112 bits
156 // This would be a different behavior from the original implementation.
157 int checkDisabled = 1;
165
167 return QByteArray();
168
169 QByteArray derived(dkLen, '\0');
170
171 if (!EVP_KDF_derive(ctx, reinterpret_cast<unsigned char*>(derived.data()), derived.size(), nullptr))
172 return QByteArray();
173
174 return derived;
175}
176#endif
177
178/*!
179 \since 5.12
180
181 Derive a key using the PBKDF2-algorithm as defined in
182 \l {RFC 8018, section 5.2}.
183
184 This function takes the \a data and \a salt, and then applies HMAC-X, where
185 the X is \a algorithm, repeatedly. It internally concatenates intermediate
186 results to the final output until at least \a dkLen amount of bytes have
187 been computed and it will execute HMAC-X \a iterations times each time a
188 concatenation is required. The total number of times it will execute HMAC-X
189 depends on \a iterations, \a dkLen and \a algorithm and can be calculated
190 as
191 \c{iterations * ceil(dkLen / QCryptographicHash::hashLength(algorithm))}.
192
193 \sa deriveKeyPbkdf1, QMessageAuthenticationCode, QCryptographicHash
194*/
196 const QByteArray &data, const QByteArray &salt,
198{
199 // The RFC recommends checking that 'dkLen' is not greater than '(2^32 - 1) * hLen'
202 if (dkLen > maxLen) {
203 qWarning().nospace() << "Derived key too long:\n"
204 << algorithm << " was chosen which produces output of length "
205 << maxLen << " but " << dkLen << " was requested.";
206 return QByteArray();
207 }
208
209 if (iterations < 1 || dkLen < 1)
210 return QByteArray();
211
212#ifdef USING_OPENSSL30
215#endif
216
217 // https://tools.ietf.org/html/rfc8018#section-5.2
222 while (quint64(key.size()) < dkLen) {
224
227
229 hmac.reset();
230 QByteArray tkey = u;
231 for (int iter = 1; iter < iterations; iter++) {
232 hmac.addData(u);
233 u = hmac.result();
234 hmac.reset();
236 std::bit_xor<char>());
237 }
238 key += tkey;
240 }
241 return key.left(dkLen);
242}
243} // namespace QPasswordDigestor
244QT_END_NAMESPACE
\inmodule QtNetwork