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