1 | // Copyright (C) 2023 The Qt Company Ltd. |
2 | // Copyright (C) 2013 Ruslan Nigmatullin <euroelessar@yandex.ru> |
3 | // Copyright (C) 2013 Richard J. Moore <rich@kde.org>. |
4 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
5 | |
6 | #include <qcryptographichash.h> |
7 | #include <qmessageauthenticationcode.h> |
8 | |
9 | #include <QtCore/private/qsmallbytearray_p.h> |
10 | #include <qiodevice.h> |
11 | #include <qmutex.h> |
12 | #include <private/qlocking_p.h> |
13 | |
14 | #include <array> |
15 | #include <climits> |
16 | #include <numeric> |
17 | |
18 | #include "../../3rdparty/sha1/sha1.cpp" |
19 | |
20 | #if defined(QT_BOOTSTRAPPED) && !defined(QT_CRYPTOGRAPHICHASH_ONLY_SHA1) |
21 | # error "Are you sure you need the other hashing algorithms besides SHA-1?" |
22 | #endif |
23 | |
24 | // Header from rfc6234 |
25 | #include "../../3rdparty/rfc6234/sha.h" |
26 | |
27 | #ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 |
28 | #if !QT_CONFIG(openssl_hash) |
29 | // qdoc and qmake only need SHA-1 |
30 | #include "../../3rdparty/md5/md5.h" |
31 | #include "../../3rdparty/md5/md5.cpp" |
32 | #include "../../3rdparty/md4/md4.h" |
33 | #include "../../3rdparty/md4/md4.cpp" |
34 | #endif // !QT_CONFIG(openssl_hash) |
35 | |
36 | typedef unsigned char BitSequence; |
37 | typedef unsigned long long DataLength; |
38 | typedef enum { SUCCESS = 0, FAIL = 1, BAD_HASHLEN = 2 } HashReturn; |
39 | |
40 | #ifdef Q_OS_RTEMS |
41 | # undef ALIGN |
42 | #endif |
43 | |
44 | #include "../../3rdparty/sha3/KeccakSponge.c" |
45 | typedef spongeState hashState; |
46 | |
47 | #include "../../3rdparty/sha3/KeccakNISTInterface.c" |
48 | |
49 | /* |
50 | This lets us choose between SHA3 implementations at build time. |
51 | */ |
52 | typedef spongeState SHA3Context; |
53 | typedef HashReturn (SHA3Init)(hashState *state, int hashbitlen); |
54 | typedef HashReturn (SHA3Update)(hashState *state, const BitSequence *data, DataLength databitlen); |
55 | typedef HashReturn (SHA3Final)(hashState *state, BitSequence *hashval); |
56 | |
57 | #if Q_PROCESSOR_WORDSIZE == 8 // 64 bit version |
58 | |
59 | #include "../../3rdparty/sha3/KeccakF-1600-opt64.c" |
60 | |
61 | Q_CONSTINIT static SHA3Init * const sha3Init = Init; |
62 | Q_CONSTINIT static SHA3Update * const sha3Update = Update; |
63 | Q_CONSTINIT static SHA3Final * const sha3Final = Final; |
64 | |
65 | #else // 32 bit optimised fallback |
66 | |
67 | #include "../../3rdparty/sha3/KeccakF-1600-opt32.c" |
68 | |
69 | Q_CONSTINIT static SHA3Init * const sha3Init = Init; |
70 | Q_CONSTINIT static SHA3Update * const sha3Update = Update; |
71 | Q_CONSTINIT static SHA3Final * const sha3Final = Final; |
72 | |
73 | #endif |
74 | |
75 | #if !QT_CONFIG(openssl_hash) |
76 | /* |
77 | These 2 functions replace macros of the same name in sha224-256.c and |
78 | sha384-512.c. Originally, these macros relied on a global static 'addTemp' |
79 | variable. We do not want this for 2 reasons: |
80 | |
81 | 1. since we are including the sources directly, the declaration of the 2 conflict |
82 | |
83 | 2. static variables are not thread-safe, we do not want multiple threads |
84 | computing a hash to corrupt one another |
85 | */ |
86 | static int SHA224_256AddLength(SHA256Context *context, unsigned int length); |
87 | static int SHA384_512AddLength(SHA512Context *context, unsigned int length); |
88 | |
89 | // Sources from rfc6234, with 4 modifications: |
90 | // sha224-256.c - commented out 'static uint32_t addTemp;' on line 68 |
91 | // sha224-256.c - appended 'M' to the SHA224_256AddLength macro on line 70 |
92 | #include "../../3rdparty/rfc6234/sha224-256.c" |
93 | // sha384-512.c - commented out 'static uint64_t addTemp;' on line 302 |
94 | // sha384-512.c - appended 'M' to the SHA224_256AddLength macro on line 304 |
95 | #include "../../3rdparty/rfc6234/sha384-512.c" |
96 | |
97 | static inline int SHA224_256AddLength(SHA256Context *context, unsigned int length) |
98 | { |
99 | uint32_t addTemp; |
100 | return SHA224_256AddLengthM(context, length); |
101 | } |
102 | static inline int SHA384_512AddLength(SHA512Context *context, unsigned int length) |
103 | { |
104 | uint64_t addTemp; |
105 | return SHA384_512AddLengthM(context, length); |
106 | } |
107 | #endif // !QT_CONFIG(opensslv30) |
108 | |
109 | #include "qtcore-config_p.h" |
110 | |
111 | #if QT_CONFIG(system_libb2) |
112 | #include <blake2.h> |
113 | #else |
114 | QT_WARNING_PUSH |
115 | QT_WARNING_DISABLE_CLANG("-Wunused-function" ) |
116 | QT_WARNING_DISABLE_GCC("-Wunused-function" ) |
117 | QT_WARNING_DISABLE_MSVC(4505) |
118 | #include "../../3rdparty/blake2/src/blake2b-ref.c" |
119 | #include "../../3rdparty/blake2/src/blake2s-ref.c" |
120 | QT_WARNING_POP |
121 | #endif |
122 | #endif // QT_CRYPTOGRAPHICHASH_ONLY_SHA1 |
123 | |
124 | #if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(openssl_hash) |
125 | #define USING_OPENSSL30 |
126 | #include <openssl/evp.h> |
127 | #include <openssl/provider.h> |
128 | #endif |
129 | |
130 | QT_BEGIN_NAMESPACE |
131 | |
132 | static constexpr int hashLengthInternal(QCryptographicHash::Algorithm method) noexcept |
133 | { |
134 | switch (method) { |
135 | #define CASE(Enum, Size) \ |
136 | case QCryptographicHash:: Enum : \ |
137 | return Size \ |
138 | /*end*/ |
139 | CASE(Sha1, 20); |
140 | #ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 |
141 | CASE(Md4, 16); |
142 | CASE(Md5, 16); |
143 | CASE(Sha224, SHA224HashSize); |
144 | CASE(Sha256, SHA256HashSize); |
145 | CASE(Sha384, SHA384HashSize); |
146 | CASE(Sha512, SHA512HashSize); |
147 | CASE(Blake2s_128, 128 / 8); |
148 | case QCryptographicHash::Blake2b_160: |
149 | case QCryptographicHash::Blake2s_160: |
150 | return 160 / 8; |
151 | case QCryptographicHash::RealSha3_224: |
152 | case QCryptographicHash::Keccak_224: |
153 | case QCryptographicHash::Blake2s_224: |
154 | return 224 / 8; |
155 | case QCryptographicHash::RealSha3_256: |
156 | case QCryptographicHash::Keccak_256: |
157 | case QCryptographicHash::Blake2b_256: |
158 | case QCryptographicHash::Blake2s_256: |
159 | return 256 / 8; |
160 | case QCryptographicHash::RealSha3_384: |
161 | case QCryptographicHash::Keccak_384: |
162 | case QCryptographicHash::Blake2b_384: |
163 | return 384 / 8; |
164 | case QCryptographicHash::RealSha3_512: |
165 | case QCryptographicHash::Keccak_512: |
166 | case QCryptographicHash::Blake2b_512: |
167 | return 512 / 8; |
168 | #endif |
169 | #undef CASE |
170 | case QCryptographicHash::NumAlgorithms: ; |
171 | // fall through |
172 | // Q_UNREACHABLE() would be BiC here, as hashLength(~~invalid~~) worked in 6.4 |
173 | } |
174 | return 0; |
175 | } |
176 | |
177 | static constexpr int maxHashLength() |
178 | { |
179 | int result = 0; |
180 | using A = QCryptographicHash::Algorithm; |
181 | for (int i = 0; i < A::NumAlgorithms; ++i) |
182 | result = std::max(a: result, b: hashLengthInternal(method: A(i))); |
183 | return result; |
184 | } |
185 | |
186 | using HashResult = QSmallByteArray<maxHashLength()>; |
187 | |
188 | #ifdef USING_OPENSSL30 |
189 | static constexpr const char * methodToName(QCryptographicHash::Algorithm method) noexcept |
190 | { |
191 | switch (method) { |
192 | #define CASE(Enum, Name) \ |
193 | case QCryptographicHash:: Enum : \ |
194 | return Name \ |
195 | /*end*/ |
196 | CASE(Sha1, "SHA1" ); |
197 | CASE(Md4, "MD4" ); |
198 | CASE(Md5, "MD5" ); |
199 | CASE(Sha224, "SHA224" ); |
200 | CASE(Sha256, "SHA256" ); |
201 | CASE(Sha384, "SHA384" ); |
202 | CASE(Sha512, "SHA512" ); |
203 | CASE(RealSha3_224, "SHA3-224" ); |
204 | CASE(RealSha3_256, "SHA3-256" ); |
205 | CASE(RealSha3_384, "SHA3-384" ); |
206 | CASE(RealSha3_512, "SHA3-512" ); |
207 | CASE(Blake2b_512, "BLAKE2B512" ); |
208 | CASE(Blake2s_256, "BLAKE2S256" ); |
209 | // not supported by OpenSSL: |
210 | CASE(Keccak_224, nullptr); |
211 | CASE(Keccak_256, nullptr); |
212 | CASE(Keccak_384, nullptr); |
213 | CASE(Keccak_512, nullptr); |
214 | CASE(Blake2b_160, nullptr); |
215 | CASE(Blake2b_256, nullptr); |
216 | CASE(Blake2b_384, nullptr); |
217 | CASE(Blake2s_128, nullptr); |
218 | CASE(Blake2s_160, nullptr); |
219 | CASE(Blake2s_224, nullptr); |
220 | CASE(NumAlgorithms, nullptr); |
221 | #undef CASE |
222 | } |
223 | return nullptr; |
224 | } |
225 | |
226 | /* |
227 | Checks whether given method is not provided by OpenSSL and whether we will |
228 | have a fallback to non-OpenSSL implementation. |
229 | */ |
230 | static constexpr bool useNonOpenSSLFallback(QCryptographicHash::Algorithm method) noexcept |
231 | { |
232 | if (method == QCryptographicHash::Keccak_224 || method == QCryptographicHash::Keccak_256 || |
233 | method == QCryptographicHash::Keccak_384 || method == QCryptographicHash::Keccak_512 || |
234 | method == QCryptographicHash::Blake2b_160 || method == QCryptographicHash::Blake2b_256 || |
235 | method == QCryptographicHash::Blake2b_384 || method == QCryptographicHash::Blake2s_128 || |
236 | method == QCryptographicHash::Blake2s_160 || method == QCryptographicHash::Blake2s_224) |
237 | return true; |
238 | |
239 | return false; |
240 | } |
241 | #endif // USING_OPENSSL30 |
242 | |
243 | class QCryptographicHashPrivate |
244 | { |
245 | public: |
246 | explicit QCryptographicHashPrivate(QCryptographicHash::Algorithm method) noexcept |
247 | : state(method), method(method) |
248 | { |
249 | } |
250 | ~QCryptographicHashPrivate() |
251 | { |
252 | state.destroy(method); |
253 | } |
254 | |
255 | void reset() noexcept; |
256 | void addData(QByteArrayView bytes) noexcept; |
257 | bool addData(QIODevice *dev); |
258 | void finalize() noexcept; |
259 | // when not called from the static hash() function, this function needs to be |
260 | // called with finalizeMutex held (finalize() will do that): |
261 | void finalizeUnchecked() noexcept; |
262 | // END functions that need to be called with finalizeMutex held |
263 | QByteArrayView resultView() const noexcept { return result.toByteArrayView(); } |
264 | static bool supportsAlgorithm(QCryptographicHash::Algorithm method); |
265 | |
266 | #ifdef USING_OPENSSL30 |
267 | struct EVP_MD_CTX_deleter { |
268 | void operator()(EVP_MD_CTX *ctx) const noexcept { |
269 | EVP_MD_CTX_free(ctx); |
270 | } |
271 | }; |
272 | struct EVP_MD_deleter { |
273 | void operator()(EVP_MD *md) const noexcept { |
274 | EVP_MD_free(md); |
275 | } |
276 | }; |
277 | struct OSSL_PROVIDER_deleter { |
278 | void operator()(OSSL_PROVIDER *provider) const noexcept { |
279 | OSSL_PROVIDER_unload(provider); |
280 | } |
281 | }; |
282 | |
283 | using EVP_MD_CTX_ptr = std::unique_ptr<EVP_MD_CTX, EVP_MD_CTX_deleter>; |
284 | using EVP_MD_ptr = std::unique_ptr<EVP_MD, EVP_MD_deleter>; |
285 | using OSSL_PROVIDER_ptr = std::unique_ptr<OSSL_PROVIDER, OSSL_PROVIDER_deleter>; |
286 | struct EVP { |
287 | EVP_MD_ptr algorithm; |
288 | EVP_MD_CTX_ptr context; |
289 | OSSL_PROVIDER_ptr defaultProvider; |
290 | OSSL_PROVIDER_ptr legacyProvider; |
291 | bool initializationFailed; |
292 | |
293 | explicit EVP(QCryptographicHash::Algorithm method); |
294 | void reset() noexcept; |
295 | void finalizeUnchecked(HashResult &result) noexcept; |
296 | }; |
297 | #endif |
298 | |
299 | union State { |
300 | explicit State(QCryptographicHash::Algorithm method); |
301 | void destroy(QCryptographicHash::Algorithm method); |
302 | #ifdef USING_OPENSSL30 |
303 | ~State() {} |
304 | #endif |
305 | |
306 | void reset(QCryptographicHash::Algorithm method) noexcept; |
307 | void addData(QCryptographicHash::Algorithm method, QByteArrayView data) noexcept; |
308 | void finalizeUnchecked(QCryptographicHash::Algorithm method, HashResult &result) noexcept; |
309 | |
310 | Sha1State sha1Context; |
311 | #ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 |
312 | #ifdef USING_OPENSSL30 |
313 | EVP evp; |
314 | #else |
315 | MD5Context md5Context; |
316 | md4_context md4Context; |
317 | SHA224Context sha224Context; |
318 | SHA256Context sha256Context; |
319 | SHA384Context sha384Context; |
320 | SHA512Context sha512Context; |
321 | #endif |
322 | SHA3Context sha3Context; |
323 | |
324 | enum class Sha3Variant { Sha3, Keccak }; |
325 | static void sha3Finish(SHA3Context &ctx, HashResult &result, int bitCount, Sha3Variant sha3Variant); |
326 | blake2b_state blake2bContext; |
327 | blake2s_state blake2sContext; |
328 | #endif |
329 | } state; |
330 | // protects result in finalize() |
331 | QBasicMutex finalizeMutex; |
332 | HashResult result; |
333 | |
334 | const QCryptographicHash::Algorithm method; |
335 | }; |
336 | |
337 | #ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 |
338 | void QCryptographicHashPrivate::State::sha3Finish(SHA3Context &ctx, HashResult &result, |
339 | int bitCount, Sha3Variant sha3Variant) |
340 | { |
341 | /* |
342 | FIPS 202 §6.1 defines SHA-3 in terms of calculating the Keccak function |
343 | over the original message with the two-bit suffix "01" appended to it. |
344 | This variable stores that suffix (and it's fed into the calculations |
345 | when the hash is returned to users). |
346 | |
347 | Only 2 bits of this variable are actually used (see the call to sha3Update |
348 | below). The Keccak implementation we're using will actually use the |
349 | *leftmost* 2 bits, and interpret them right-to-left. In other words, the |
350 | bits must appear in order of *increasing* significance; and as the two most |
351 | significant bits of the byte -- the rightmost 6 are ignored. (Yes, this |
352 | seems self-contradictory, but it's the way it is...) |
353 | |
354 | Overall, this means: |
355 | * the leftmost two bits must be "10" (not "01"!); |
356 | * we don't care what the other six bits are set to (they can be set to |
357 | any value), but we arbitrarily set them to 0; |
358 | |
359 | and for an unsigned char this gives us 0b10'00'00'00, or 0x80. |
360 | */ |
361 | static const unsigned char sha3FinalSuffix = 0x80; |
362 | |
363 | result.resizeForOverwrite(s: bitCount / 8); |
364 | |
365 | switch (sha3Variant) { |
366 | case Sha3Variant::Sha3: |
367 | sha3Update(&ctx, reinterpret_cast<const BitSequence *>(&sha3FinalSuffix), 2); |
368 | break; |
369 | case Sha3Variant::Keccak: |
370 | break; |
371 | } |
372 | |
373 | sha3Final(&ctx, result.data()); |
374 | } |
375 | #endif |
376 | |
377 | /*! |
378 | \class QCryptographicHash |
379 | \inmodule QtCore |
380 | |
381 | \brief The QCryptographicHash class provides a way to generate cryptographic hashes. |
382 | |
383 | \since 4.3 |
384 | |
385 | \ingroup tools |
386 | \reentrant |
387 | |
388 | QCryptographicHash can be used to generate cryptographic hashes of binary or text data. |
389 | |
390 | Refer to the documentation of the \l QCryptographicHash::Algorithm enum for a |
391 | list of the supported algorithms. |
392 | */ |
393 | |
394 | /*! |
395 | \enum QCryptographicHash::Algorithm |
396 | |
397 | \note In Qt versions before 5.9, when asked to generate a SHA3 hash sum, |
398 | QCryptographicHash actually calculated Keccak. If you need compatibility with |
399 | SHA-3 hashes produced by those versions of Qt, use the \c{Keccak_} |
400 | enumerators. Alternatively, if source compatibility is required, define the |
401 | macro \c QT_SHA3_KECCAK_COMPAT. |
402 | |
403 | \value Md4 Generate an MD4 hash sum |
404 | \value Md5 Generate an MD5 hash sum |
405 | \value Sha1 Generate an SHA-1 hash sum |
406 | \value Sha224 Generate an SHA-224 hash sum (SHA-2). Introduced in Qt 5.0 |
407 | \value Sha256 Generate an SHA-256 hash sum (SHA-2). Introduced in Qt 5.0 |
408 | \value Sha384 Generate an SHA-384 hash sum (SHA-2). Introduced in Qt 5.0 |
409 | \value Sha512 Generate an SHA-512 hash sum (SHA-2). Introduced in Qt 5.0 |
410 | \value Sha3_224 Generate an SHA3-224 hash sum. Introduced in Qt 5.1 |
411 | \value Sha3_256 Generate an SHA3-256 hash sum. Introduced in Qt 5.1 |
412 | \value Sha3_384 Generate an SHA3-384 hash sum. Introduced in Qt 5.1 |
413 | \value Sha3_512 Generate an SHA3-512 hash sum. Introduced in Qt 5.1 |
414 | \value Keccak_224 Generate a Keccak-224 hash sum. Introduced in Qt 5.9.2 |
415 | \value Keccak_256 Generate a Keccak-256 hash sum. Introduced in Qt 5.9.2 |
416 | \value Keccak_384 Generate a Keccak-384 hash sum. Introduced in Qt 5.9.2 |
417 | \value Keccak_512 Generate a Keccak-512 hash sum. Introduced in Qt 5.9.2 |
418 | \value Blake2b_160 Generate a BLAKE2b-160 hash sum. Introduced in Qt 6.0 |
419 | \value Blake2b_256 Generate a BLAKE2b-256 hash sum. Introduced in Qt 6.0 |
420 | \value Blake2b_384 Generate a BLAKE2b-384 hash sum. Introduced in Qt 6.0 |
421 | \value Blake2b_512 Generate a BLAKE2b-512 hash sum. Introduced in Qt 6.0 |
422 | \value Blake2s_128 Generate a BLAKE2s-128 hash sum. Introduced in Qt 6.0 |
423 | \value Blake2s_160 Generate a BLAKE2s-160 hash sum. Introduced in Qt 6.0 |
424 | \value Blake2s_224 Generate a BLAKE2s-224 hash sum. Introduced in Qt 6.0 |
425 | \value Blake2s_256 Generate a BLAKE2s-256 hash sum. Introduced in Qt 6.0 |
426 | \omitvalue RealSha3_224 |
427 | \omitvalue RealSha3_256 |
428 | \omitvalue RealSha3_384 |
429 | \omitvalue RealSha3_512 |
430 | \omitvalue NumAlgorithms |
431 | */ |
432 | |
433 | /*! |
434 | Constructs an object that can be used to create a cryptographic hash from data using \a method. |
435 | */ |
436 | QCryptographicHash::QCryptographicHash(Algorithm method) |
437 | : d(new QCryptographicHashPrivate{method}) |
438 | { |
439 | } |
440 | |
441 | /*! |
442 | \fn QCryptographicHash::QCryptographicHash(QCryptographicHash &&other) |
443 | |
444 | Move-constructs a new QCryptographicHash from \a other. |
445 | |
446 | \note The moved-from object \a other is placed in a |
447 | partially-formed state, in which the only valid operations are |
448 | destruction and assignment of a new value. |
449 | |
450 | \since 6.5 |
451 | */ |
452 | |
453 | /*! |
454 | Destroys the object. |
455 | */ |
456 | QCryptographicHash::~QCryptographicHash() |
457 | { |
458 | delete d; |
459 | } |
460 | |
461 | /*! |
462 | \fn QCryptographicHash &QCryptographicHash::operator=(QCryptographicHash &&other) |
463 | |
464 | Move-assigns \a other to this QCryptographicHash instance. |
465 | |
466 | \note The moved-from object \a other is placed in a |
467 | partially-formed state, in which the only valid operations are |
468 | destruction and assignment of a new value. |
469 | |
470 | \since 6.5 |
471 | */ |
472 | |
473 | /*! |
474 | \fn void QCryptographicHash::swap(QCryptographicHash &other) |
475 | \memberswap{cryptographic hash} |
476 | \since 6.5 |
477 | */ |
478 | |
479 | /*! |
480 | Resets the object. |
481 | */ |
482 | void QCryptographicHash::reset() noexcept |
483 | { |
484 | d->reset(); |
485 | } |
486 | |
487 | /*! |
488 | Returns the algorithm used to generate the cryptographic hash. |
489 | |
490 | \since 6.5 |
491 | */ |
492 | QCryptographicHash::Algorithm QCryptographicHash::algorithm() const noexcept |
493 | { |
494 | return d->method; |
495 | } |
496 | |
497 | #ifdef USING_OPENSSL30 |
498 | |
499 | QCryptographicHashPrivate::State::State(QCryptographicHash::Algorithm method) |
500 | { |
501 | if (method == QCryptographicHash::Keccak_224 || |
502 | method == QCryptographicHash::Keccak_256 || |
503 | method == QCryptographicHash::Keccak_384 || |
504 | method == QCryptographicHash::Keccak_512) { |
505 | new (&sha3Context) SHA3Context; |
506 | reset(method); |
507 | } else if (method == QCryptographicHash::Blake2b_160 || |
508 | method == QCryptographicHash::Blake2b_256 || |
509 | method == QCryptographicHash::Blake2b_384) { |
510 | new (&blake2bContext) blake2b_state; |
511 | reset(method); |
512 | } else if (method == QCryptographicHash::Blake2s_128 || |
513 | method == QCryptographicHash::Blake2s_160 || |
514 | method == QCryptographicHash::Blake2s_224) { |
515 | new (&blake2sContext) blake2s_state; |
516 | reset(method); |
517 | } else { |
518 | new (&evp) EVP(method); |
519 | } |
520 | } |
521 | |
522 | void QCryptographicHashPrivate::State::destroy(QCryptographicHash::Algorithm method) |
523 | { |
524 | if (method != QCryptographicHash::Keccak_224 && |
525 | method != QCryptographicHash::Keccak_256 && |
526 | method != QCryptographicHash::Keccak_384 && |
527 | method != QCryptographicHash::Keccak_512 && |
528 | method != QCryptographicHash::Blake2b_160 && |
529 | method != QCryptographicHash::Blake2b_256 && |
530 | method != QCryptographicHash::Blake2b_384 && |
531 | method != QCryptographicHash::Blake2s_128 && |
532 | method != QCryptographicHash::Blake2s_160 && |
533 | method != QCryptographicHash::Blake2s_224) { |
534 | evp.~EVP(); |
535 | } |
536 | } |
537 | |
538 | QCryptographicHashPrivate::EVP::EVP(QCryptographicHash::Algorithm method) |
539 | : initializationFailed{true} |
540 | { |
541 | if (method == QCryptographicHash::Md4) { |
542 | /* |
543 | * We need to load the legacy provider in order to have the MD4 |
544 | * algorithm available. |
545 | */ |
546 | legacyProvider = OSSL_PROVIDER_ptr(OSSL_PROVIDER_load(nullptr, "legacy" )); |
547 | |
548 | if (!legacyProvider) |
549 | return; |
550 | } |
551 | |
552 | defaultProvider = OSSL_PROVIDER_ptr(OSSL_PROVIDER_load(nullptr, "default" )); |
553 | if (!defaultProvider) |
554 | return; |
555 | |
556 | context = EVP_MD_CTX_ptr(EVP_MD_CTX_new()); |
557 | |
558 | if (!context) { |
559 | return; |
560 | } |
561 | |
562 | /* |
563 | * Using the "-fips" option will disable the global "fips=yes" for |
564 | * this one lookup and the algorithm can be fetched from any provider |
565 | * that implements the algorithm (including the FIPS provider). |
566 | */ |
567 | algorithm = EVP_MD_ptr(EVP_MD_fetch(nullptr, methodToName(method), "-fips" )); |
568 | if (!algorithm) { |
569 | return; |
570 | } |
571 | |
572 | initializationFailed = !EVP_DigestInit_ex(context.get(), algorithm.get(), nullptr); |
573 | } |
574 | |
575 | #else // USING_OPENSSL30 |
576 | |
577 | QCryptographicHashPrivate::State::State(QCryptographicHash::Algorithm method) |
578 | { |
579 | switch (method) { |
580 | case QCryptographicHash::Sha1: |
581 | new (&sha1Context) Sha1State; |
582 | break; |
583 | #ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 |
584 | default: |
585 | Q_ASSERT_X(false, "QCryptographicHash" , "Method not compiled in" ); |
586 | Q_UNREACHABLE(); |
587 | break; |
588 | #else |
589 | case QCryptographicHash::Md4: |
590 | new (&md4Context) md4_context; |
591 | break; |
592 | case QCryptographicHash::Md5: |
593 | new (&md5Context) MD5Context; |
594 | break; |
595 | case QCryptographicHash::Sha224: |
596 | new (&sha224Context) SHA224Context; |
597 | break; |
598 | case QCryptographicHash::Sha256: |
599 | new (&sha256Context) SHA256Context; |
600 | break; |
601 | case QCryptographicHash::Sha384: |
602 | new (&sha384Context) SHA384Context; |
603 | break; |
604 | case QCryptographicHash::Sha512: |
605 | new (&sha512Context) SHA512Context; |
606 | break; |
607 | case QCryptographicHash::RealSha3_224: |
608 | case QCryptographicHash::Keccak_224: |
609 | case QCryptographicHash::RealSha3_256: |
610 | case QCryptographicHash::Keccak_256: |
611 | case QCryptographicHash::RealSha3_384: |
612 | case QCryptographicHash::Keccak_384: |
613 | case QCryptographicHash::RealSha3_512: |
614 | case QCryptographicHash::Keccak_512: |
615 | new (&sha3Context) SHA3Context; |
616 | break; |
617 | case QCryptographicHash::Blake2b_160: |
618 | case QCryptographicHash::Blake2b_256: |
619 | case QCryptographicHash::Blake2b_384: |
620 | case QCryptographicHash::Blake2b_512: |
621 | new (&blake2bContext) blake2b_state; |
622 | break; |
623 | case QCryptographicHash::Blake2s_128: |
624 | case QCryptographicHash::Blake2s_160: |
625 | case QCryptographicHash::Blake2s_224: |
626 | case QCryptographicHash::Blake2s_256: |
627 | new (&blake2sContext) blake2s_state; |
628 | break; |
629 | #endif |
630 | case QCryptographicHash::NumAlgorithms: |
631 | Q_UNREACHABLE(); |
632 | } |
633 | reset(method); |
634 | } |
635 | |
636 | void QCryptographicHashPrivate::State::destroy(QCryptographicHash::Algorithm) |
637 | { |
638 | static_assert(std::is_trivially_destructible_v<State>); // so nothing to do here |
639 | } |
640 | #endif // !USING_OPENSSL30 |
641 | |
642 | void QCryptographicHashPrivate::reset() noexcept |
643 | { |
644 | result.clear(); |
645 | state.reset(method); |
646 | } |
647 | |
648 | #ifdef USING_OPENSSL30 |
649 | |
650 | void QCryptographicHashPrivate::State::reset(QCryptographicHash::Algorithm method) noexcept |
651 | { |
652 | if (method == QCryptographicHash::Keccak_224 || |
653 | method == QCryptographicHash::Keccak_256 || |
654 | method == QCryptographicHash::Keccak_384 || |
655 | method == QCryptographicHash::Keccak_512) { |
656 | sha3Init(&sha3Context, hashLengthInternal(method) * 8); |
657 | } else if (method == QCryptographicHash::Blake2b_160 || |
658 | method == QCryptographicHash::Blake2b_256 || |
659 | method == QCryptographicHash::Blake2b_384) { |
660 | blake2b_init(&blake2bContext, hashLengthInternal(method)); |
661 | } else if (method == QCryptographicHash::Blake2s_128 || |
662 | method == QCryptographicHash::Blake2s_160 || |
663 | method == QCryptographicHash::Blake2s_224) { |
664 | blake2s_init(&blake2sContext, hashLengthInternal(method)); |
665 | } else { |
666 | evp.reset(); |
667 | } |
668 | } |
669 | |
670 | void QCryptographicHashPrivate::EVP::reset() noexcept |
671 | { |
672 | if (!initializationFailed) { |
673 | Q_ASSERT(context); |
674 | Q_ASSERT(algorithm); |
675 | // everything already set up - just reset the context |
676 | EVP_MD_CTX_reset(context.get()); |
677 | initializationFailed = !EVP_DigestInit_ex(context.get(), algorithm.get(), nullptr); |
678 | } |
679 | // if initializationFailed first time around, it will not succeed this time, either |
680 | } |
681 | |
682 | #else // USING_OPENSSL30 |
683 | |
684 | void QCryptographicHashPrivate::State::reset(QCryptographicHash::Algorithm method) noexcept |
685 | { |
686 | switch (method) { |
687 | case QCryptographicHash::Sha1: |
688 | sha1InitState(state: &sha1Context); |
689 | break; |
690 | #ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 |
691 | default: |
692 | Q_ASSERT_X(false, "QCryptographicHash" , "Method not compiled in" ); |
693 | Q_UNREACHABLE(); |
694 | break; |
695 | #else |
696 | case QCryptographicHash::Md4: |
697 | md4_init(ctx: &md4Context); |
698 | break; |
699 | case QCryptographicHash::Md5: |
700 | MD5Init(ctx: &md5Context); |
701 | break; |
702 | case QCryptographicHash::Sha224: |
703 | SHA224Reset(context: &sha224Context); |
704 | break; |
705 | case QCryptographicHash::Sha256: |
706 | SHA256Reset(context: &sha256Context); |
707 | break; |
708 | case QCryptographicHash::Sha384: |
709 | SHA384Reset(context: &sha384Context); |
710 | break; |
711 | case QCryptographicHash::Sha512: |
712 | SHA512Reset(context: &sha512Context); |
713 | break; |
714 | case QCryptographicHash::RealSha3_224: |
715 | case QCryptographicHash::Keccak_224: |
716 | case QCryptographicHash::RealSha3_256: |
717 | case QCryptographicHash::Keccak_256: |
718 | case QCryptographicHash::RealSha3_384: |
719 | case QCryptographicHash::Keccak_384: |
720 | case QCryptographicHash::RealSha3_512: |
721 | case QCryptographicHash::Keccak_512: |
722 | sha3Init(&sha3Context, hashLengthInternal(method) * 8); |
723 | break; |
724 | case QCryptographicHash::Blake2b_160: |
725 | case QCryptographicHash::Blake2b_256: |
726 | case QCryptographicHash::Blake2b_384: |
727 | case QCryptographicHash::Blake2b_512: |
728 | blake2b_init(S: &blake2bContext, outlen: hashLengthInternal(method)); |
729 | break; |
730 | case QCryptographicHash::Blake2s_128: |
731 | case QCryptographicHash::Blake2s_160: |
732 | case QCryptographicHash::Blake2s_224: |
733 | case QCryptographicHash::Blake2s_256: |
734 | blake2s_init(S: &blake2sContext, outlen: hashLengthInternal(method)); |
735 | break; |
736 | #endif |
737 | case QCryptographicHash::NumAlgorithms: |
738 | Q_UNREACHABLE(); |
739 | } |
740 | } |
741 | |
742 | #endif // USING_OPENSSL30 |
743 | |
744 | #if QT_DEPRECATED_SINCE(6, 4) |
745 | /*! |
746 | Adds the first \a length chars of \a data to the cryptographic |
747 | hash. |
748 | |
749 | \obsolete |
750 | Use the QByteArrayView overload instead. |
751 | */ |
752 | void QCryptographicHash::addData(const char *data, qsizetype length) |
753 | { |
754 | Q_ASSERT(length >= 0); |
755 | addData(data: QByteArrayView{data, length}); |
756 | } |
757 | #endif |
758 | |
759 | /*! |
760 | Adds the characters in \a bytes to the cryptographic hash. |
761 | |
762 | \note In Qt versions prior to 6.3, this function took QByteArray, |
763 | not QByteArrayView. |
764 | */ |
765 | void QCryptographicHash::addData(QByteArrayView bytes) noexcept |
766 | { |
767 | d->addData(bytes); |
768 | } |
769 | |
770 | void QCryptographicHashPrivate::addData(QByteArrayView bytes) noexcept |
771 | { |
772 | state.addData(method, data: bytes); |
773 | result.clear(); |
774 | } |
775 | |
776 | #ifdef USING_OPENSSL30 |
777 | |
778 | void QCryptographicHashPrivate::State::addData(QCryptographicHash::Algorithm method, |
779 | QByteArrayView bytes) noexcept |
780 | { |
781 | const char *data = bytes.data(); |
782 | auto length = bytes.size(); |
783 | // all functions take size_t length, so we don't need to loop around them: |
784 | { |
785 | if (method == QCryptographicHash::Keccak_224 || |
786 | method == QCryptographicHash::Keccak_256 || |
787 | method == QCryptographicHash::Keccak_384 || |
788 | method == QCryptographicHash::Keccak_512) { |
789 | sha3Update(&sha3Context, reinterpret_cast<const BitSequence *>(data), uint64_t(length) * 8); |
790 | } else if (method == QCryptographicHash::Blake2b_160 || |
791 | method == QCryptographicHash::Blake2b_256 || |
792 | method == QCryptographicHash::Blake2b_384) { |
793 | blake2b_update(&blake2bContext, reinterpret_cast<const uint8_t *>(data), length); |
794 | } else if (method == QCryptographicHash::Blake2s_128 || |
795 | method == QCryptographicHash::Blake2s_160 || |
796 | method == QCryptographicHash::Blake2s_224) { |
797 | blake2s_update(&blake2sContext, reinterpret_cast<const uint8_t *>(data), length); |
798 | } else if (!evp.initializationFailed) { |
799 | EVP_DigestUpdate(evp.context.get(), (const unsigned char *)data, length); |
800 | } |
801 | } |
802 | } |
803 | |
804 | #else // USING_OPENSSL30 |
805 | |
806 | void QCryptographicHashPrivate::State::addData(QCryptographicHash::Algorithm method, |
807 | QByteArrayView bytes) noexcept |
808 | { |
809 | const char *data = bytes.data(); |
810 | auto length = bytes.size(); |
811 | |
812 | #if QT_POINTER_SIZE == 8 |
813 | // feed the data UINT_MAX bytes at a time, as some of the methods below |
814 | // take a uint (of course, feeding more than 4G of data into the hashing |
815 | // functions will be pretty slow anyway) |
816 | for (auto remaining = length; remaining; remaining -= length, data += length) { |
817 | length = qMin(a: qsizetype(std::numeric_limits<uint>::max()), b: remaining); |
818 | #else |
819 | { |
820 | #endif |
821 | switch (method) { |
822 | case QCryptographicHash::Sha1: |
823 | sha1Update(state: &sha1Context, data: (const unsigned char *)data, len: length); |
824 | break; |
825 | #ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 |
826 | default: |
827 | Q_ASSERT_X(false, "QCryptographicHash" , "Method not compiled in" ); |
828 | Q_UNREACHABLE(); |
829 | break; |
830 | #else |
831 | case QCryptographicHash::Md4: |
832 | md4_update(ctx: &md4Context, data: (const unsigned char *)data, size: length); |
833 | break; |
834 | case QCryptographicHash::Md5: |
835 | MD5Update(ctx: &md5Context, buf: (const unsigned char *)data, len: length); |
836 | break; |
837 | case QCryptographicHash::Sha224: |
838 | SHA224Input(context: &sha224Context, message_array: reinterpret_cast<const unsigned char *>(data), length); |
839 | break; |
840 | case QCryptographicHash::Sha256: |
841 | SHA256Input(context: &sha256Context, message_array: reinterpret_cast<const unsigned char *>(data), length); |
842 | break; |
843 | case QCryptographicHash::Sha384: |
844 | SHA384Input(context: &sha384Context, message_array: reinterpret_cast<const unsigned char *>(data), length); |
845 | break; |
846 | case QCryptographicHash::Sha512: |
847 | SHA512Input(context: &sha512Context, message_array: reinterpret_cast<const unsigned char *>(data), length); |
848 | break; |
849 | case QCryptographicHash::RealSha3_224: |
850 | case QCryptographicHash::Keccak_224: |
851 | case QCryptographicHash::RealSha3_256: |
852 | case QCryptographicHash::Keccak_256: |
853 | case QCryptographicHash::RealSha3_384: |
854 | case QCryptographicHash::Keccak_384: |
855 | case QCryptographicHash::RealSha3_512: |
856 | case QCryptographicHash::Keccak_512: |
857 | sha3Update(&sha3Context, reinterpret_cast<const BitSequence *>(data), uint64_t(length) * 8); |
858 | break; |
859 | case QCryptographicHash::Blake2b_160: |
860 | case QCryptographicHash::Blake2b_256: |
861 | case QCryptographicHash::Blake2b_384: |
862 | case QCryptographicHash::Blake2b_512: |
863 | blake2b_update(S: &blake2bContext, pin: reinterpret_cast<const uint8_t *>(data), inlen: length); |
864 | break; |
865 | case QCryptographicHash::Blake2s_128: |
866 | case QCryptographicHash::Blake2s_160: |
867 | case QCryptographicHash::Blake2s_224: |
868 | case QCryptographicHash::Blake2s_256: |
869 | blake2s_update(S: &blake2sContext, pin: reinterpret_cast<const uint8_t *>(data), inlen: length); |
870 | break; |
871 | #endif |
872 | case QCryptographicHash::NumAlgorithms: |
873 | Q_UNREACHABLE(); |
874 | } |
875 | } |
876 | } |
877 | #endif // !USING_OPENSSL30 |
878 | |
879 | /*! |
880 | Reads the data from the open QIODevice \a device until it ends |
881 | and hashes it. Returns \c true if reading was successful. |
882 | \since 5.0 |
883 | */ |
884 | bool QCryptographicHash::addData(QIODevice *device) |
885 | { |
886 | return d->addData(dev: device); |
887 | } |
888 | |
889 | bool QCryptographicHashPrivate::addData(QIODevice *device) |
890 | { |
891 | if (!device->isReadable()) |
892 | return false; |
893 | |
894 | if (!device->isOpen()) |
895 | return false; |
896 | |
897 | char buffer[1024]; |
898 | qint64 length; |
899 | |
900 | while ((length = device->read(data: buffer, maxlen: sizeof(buffer))) > 0) |
901 | addData(bytes: {buffer, qsizetype(length)}); // length always <= 1024 |
902 | |
903 | return device->atEnd(); |
904 | } |
905 | |
906 | |
907 | /*! |
908 | Returns the final hash value. |
909 | |
910 | \sa resultView(), QByteArray::toHex() |
911 | */ |
912 | QByteArray QCryptographicHash::result() const |
913 | { |
914 | return resultView().toByteArray(); |
915 | } |
916 | |
917 | /*! |
918 | \since 6.3 |
919 | |
920 | Returns the final hash value. |
921 | |
922 | Note that the returned view remains valid only as long as the QCryptographicHash object is |
923 | not modified by other means. |
924 | |
925 | \sa result() |
926 | */ |
927 | QByteArrayView QCryptographicHash::resultView() const noexcept |
928 | { |
929 | // resultView() is a const function, so concurrent calls are allowed; protect: |
930 | d->finalize(); |
931 | // resultView() remains(!) valid even after we dropped the mutex in finalize() |
932 | return d->resultView(); |
933 | } |
934 | |
935 | /*! |
936 | \internal |
937 | |
938 | Calls finalizeUnchecked(), if needed, under finalizeMutex protection. |
939 | */ |
940 | void QCryptographicHashPrivate::finalize() noexcept |
941 | { |
942 | const auto lock = qt_scoped_lock(mutex&: finalizeMutex); |
943 | // check that no other thread already finalizeUnchecked()'ed before us: |
944 | if (!result.isEmpty()) |
945 | return; |
946 | finalizeUnchecked(); |
947 | } |
948 | |
949 | /*! |
950 | \internal |
951 | |
952 | Must be called with finalizeMutex held (except from static hash() function, |
953 | where no sharing can take place). |
954 | */ |
955 | void QCryptographicHashPrivate::finalizeUnchecked() noexcept |
956 | { |
957 | state.finalizeUnchecked(method, result); |
958 | } |
959 | |
960 | #ifdef USING_OPENSSL30 |
961 | void QCryptographicHashPrivate::State::finalizeUnchecked(QCryptographicHash::Algorithm method, |
962 | HashResult &result) noexcept |
963 | { |
964 | if (method == QCryptographicHash::Keccak_224 || |
965 | method == QCryptographicHash::Keccak_256 || |
966 | method == QCryptographicHash::Keccak_384 || |
967 | method == QCryptographicHash::Keccak_512) { |
968 | SHA3Context copy = sha3Context; |
969 | sha3Finish(copy, result, 8 * hashLengthInternal(method), Sha3Variant::Keccak); |
970 | } else if (method == QCryptographicHash::Blake2b_160 || |
971 | method == QCryptographicHash::Blake2b_256 || |
972 | method == QCryptographicHash::Blake2b_384) { |
973 | const auto length = hashLengthInternal(method); |
974 | blake2b_state copy = blake2bContext; |
975 | result.resizeForOverwrite(length); |
976 | blake2b_final(©, result.data(), length); |
977 | } else if (method == QCryptographicHash::Blake2s_128 || |
978 | method == QCryptographicHash::Blake2s_160 || |
979 | method == QCryptographicHash::Blake2s_224) { |
980 | const auto length = hashLengthInternal(method); |
981 | blake2s_state copy = blake2sContext; |
982 | result.resizeForOverwrite(length); |
983 | blake2s_final(©, result.data(), length); |
984 | } else { |
985 | evp.finalizeUnchecked(result); |
986 | } |
987 | } |
988 | |
989 | void QCryptographicHashPrivate::EVP::finalizeUnchecked(HashResult &result) noexcept |
990 | { |
991 | if (!initializationFailed) { |
992 | EVP_MD_CTX_ptr copy = EVP_MD_CTX_ptr(EVP_MD_CTX_new()); |
993 | EVP_MD_CTX_copy_ex(copy.get(), context.get()); |
994 | result.resizeForOverwrite(EVP_MD_get_size(algorithm.get())); |
995 | EVP_DigestFinal_ex(copy.get(), result.data(), nullptr); |
996 | } |
997 | } |
998 | |
999 | #else // USING_OPENSSL30 |
1000 | |
1001 | void QCryptographicHashPrivate::State::finalizeUnchecked(QCryptographicHash::Algorithm method, |
1002 | HashResult &result) noexcept |
1003 | { |
1004 | switch (method) { |
1005 | case QCryptographicHash::Sha1: { |
1006 | Sha1State copy = sha1Context; |
1007 | result.resizeForOverwrite(s: 20); |
1008 | sha1FinalizeState(state: ©); |
1009 | sha1ToHash(state: ©, buffer: result.data()); |
1010 | break; |
1011 | } |
1012 | #ifdef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 |
1013 | default: |
1014 | Q_ASSERT_X(false, "QCryptographicHash" , "Method not compiled in" ); |
1015 | Q_UNREACHABLE(); |
1016 | break; |
1017 | #else |
1018 | case QCryptographicHash::Md4: { |
1019 | md4_context copy = md4Context; |
1020 | result.resizeForOverwrite(MD4_RESULTLEN); |
1021 | md4_final(ctx: ©, result: result.data()); |
1022 | break; |
1023 | } |
1024 | case QCryptographicHash::Md5: { |
1025 | MD5Context copy = md5Context; |
1026 | result.resizeForOverwrite(s: 16); |
1027 | MD5Final(ctx: ©, digest: result.data()); |
1028 | break; |
1029 | } |
1030 | case QCryptographicHash::Sha224: { |
1031 | SHA224Context copy = sha224Context; |
1032 | result.resizeForOverwrite(s: SHA224HashSize); |
1033 | SHA224Result(context: ©, Message_Digest: result.data()); |
1034 | break; |
1035 | } |
1036 | case QCryptographicHash::Sha256: { |
1037 | SHA256Context copy = sha256Context; |
1038 | result.resizeForOverwrite(s: SHA256HashSize); |
1039 | SHA256Result(context: ©, Message_Digest: result.data()); |
1040 | break; |
1041 | } |
1042 | case QCryptographicHash::Sha384: { |
1043 | SHA384Context copy = sha384Context; |
1044 | result.resizeForOverwrite(s: SHA384HashSize); |
1045 | SHA384Result(context: ©, Message_Digest: result.data()); |
1046 | break; |
1047 | } |
1048 | case QCryptographicHash::Sha512: { |
1049 | SHA512Context copy = sha512Context; |
1050 | result.resizeForOverwrite(s: SHA512HashSize); |
1051 | SHA512Result(context: ©, Message_Digest: result.data()); |
1052 | break; |
1053 | } |
1054 | case QCryptographicHash::RealSha3_224: |
1055 | case QCryptographicHash::RealSha3_256: |
1056 | case QCryptographicHash::RealSha3_384: |
1057 | case QCryptographicHash::RealSha3_512: { |
1058 | SHA3Context copy = sha3Context; |
1059 | sha3Finish(ctx&: copy, result, bitCount: 8 * hashLengthInternal(method), sha3Variant: Sha3Variant::Sha3); |
1060 | break; |
1061 | } |
1062 | case QCryptographicHash::Keccak_224: |
1063 | case QCryptographicHash::Keccak_256: |
1064 | case QCryptographicHash::Keccak_384: |
1065 | case QCryptographicHash::Keccak_512: { |
1066 | SHA3Context copy = sha3Context; |
1067 | sha3Finish(ctx&: copy, result, bitCount: 8 * hashLengthInternal(method), sha3Variant: Sha3Variant::Keccak); |
1068 | break; |
1069 | } |
1070 | case QCryptographicHash::Blake2b_160: |
1071 | case QCryptographicHash::Blake2b_256: |
1072 | case QCryptographicHash::Blake2b_384: |
1073 | case QCryptographicHash::Blake2b_512: { |
1074 | const auto length = hashLengthInternal(method); |
1075 | blake2b_state copy = blake2bContext; |
1076 | result.resizeForOverwrite(s: length); |
1077 | blake2b_final(S: ©, out: result.data(), outlen: length); |
1078 | break; |
1079 | } |
1080 | case QCryptographicHash::Blake2s_128: |
1081 | case QCryptographicHash::Blake2s_160: |
1082 | case QCryptographicHash::Blake2s_224: |
1083 | case QCryptographicHash::Blake2s_256: { |
1084 | const auto length = hashLengthInternal(method); |
1085 | blake2s_state copy = blake2sContext; |
1086 | result.resizeForOverwrite(s: length); |
1087 | blake2s_final(S: ©, out: result.data(), outlen: length); |
1088 | break; |
1089 | } |
1090 | #endif |
1091 | case QCryptographicHash::NumAlgorithms: |
1092 | Q_UNREACHABLE(); |
1093 | } |
1094 | } |
1095 | #endif // !USING_OPENSSL30 |
1096 | |
1097 | /*! |
1098 | Returns the hash of \a data using \a method. |
1099 | |
1100 | \note In Qt versions prior to 6.3, this function took QByteArray, |
1101 | not QByteArrayView. |
1102 | |
1103 | \sa hashInto() |
1104 | */ |
1105 | QByteArray QCryptographicHash::hash(QByteArrayView data, Algorithm method) |
1106 | { |
1107 | QByteArray ba(hashLengthInternal(method), Qt::Uninitialized); |
1108 | [[maybe_unused]] const auto r = hashInto(buffer: ba, data, method); |
1109 | Q_ASSERT(r.size() == ba.size()); |
1110 | return ba; |
1111 | } |
1112 | |
1113 | /*! |
1114 | \since 6.8 |
1115 | \fn QCryptographicHash::hashInto(QSpan<char> buffer, QSpan<const QByteArrayView> data, Algorithm method); |
1116 | \fn QCryptographicHash::hashInto(QSpan<uchar> buffer, QSpan<const QByteArrayView> data, Algorithm method); |
1117 | \fn QCryptographicHash::hashInto(QSpan<std::byte> buffer, QSpan<const QByteArrayView> data, Algorithm method); |
1118 | \fn QCryptographicHash::hashInto(QSpan<char> buffer, QByteArrayView data, Algorithm method); |
1119 | \fn QCryptographicHash::hashInto(QSpan<uchar> buffer, QByteArrayView data, Algorithm method); |
1120 | \fn QCryptographicHash::hashInto(QSpan<std::byte> buffer, QByteArrayView data, Algorithm method); |
1121 | |
1122 | Returns the hash of \a data using \a method, using \a buffer to store the result. |
1123 | |
1124 | If \a data is a span, adds all the byte array views to the hash, in the order given. |
1125 | |
1126 | The return value will be a sub-span of \a buffer, unless \a buffer is of |
1127 | insufficient size, in which case a null QByteArrayView is returned. |
1128 | |
1129 | \sa hash() |
1130 | */ |
1131 | QByteArrayView QCryptographicHash::hashInto(QSpan<std::byte> buffer, |
1132 | QSpan<const QByteArrayView> data, |
1133 | Algorithm method) noexcept |
1134 | { |
1135 | if (buffer.size() < hashLengthInternal(method)) |
1136 | return {}; // buffer too small |
1137 | |
1138 | QCryptographicHashPrivate hash(method); |
1139 | for (QByteArrayView part : data) |
1140 | hash.addData(bytes: part); |
1141 | hash.finalizeUnchecked(); // no mutex needed: no-one but us has access to 'hash' |
1142 | auto result = hash.resultView(); |
1143 | Q_ASSERT(buffer.size() >= result.size()); |
1144 | // ### optimize: have the method directly write into `buffer` |
1145 | memcpy(dest: buffer.data(), src: result.data(), n: result.size()); |
1146 | return buffer.first(n: result.size()); |
1147 | } |
1148 | |
1149 | /*! |
1150 | Returns the size of the output of the selected hash \a method in bytes. |
1151 | |
1152 | \since 5.12 |
1153 | */ |
1154 | int QCryptographicHash::hashLength(QCryptographicHash::Algorithm method) |
1155 | { |
1156 | return hashLengthInternal(method); |
1157 | } |
1158 | |
1159 | /*! |
1160 | Returns whether the selected algorithm \a method is supported and if |
1161 | result() will return a value when the \a method is used. |
1162 | |
1163 | \note OpenSSL will be responsible for providing this information when |
1164 | used as a provider, otherwise \c true will be returned as the non-OpenSSL |
1165 | implementation doesn't have any restrictions. |
1166 | We return \c false if we fail to query OpenSSL. |
1167 | |
1168 | \since 6.5 |
1169 | */ |
1170 | |
1171 | |
1172 | bool QCryptographicHash::supportsAlgorithm(QCryptographicHash::Algorithm method) |
1173 | { |
1174 | return QCryptographicHashPrivate::supportsAlgorithm(method); |
1175 | } |
1176 | |
1177 | bool QCryptographicHashPrivate::supportsAlgorithm(QCryptographicHash::Algorithm method) |
1178 | { |
1179 | #ifdef USING_OPENSSL30 |
1180 | // OpenSSL doesn't support Blake2b{60,236,384} and Blake2s{128,160,224} |
1181 | // and these would automatically return FALSE in that case, while they are |
1182 | // actually supported by our non-OpenSSL implementation. |
1183 | if (useNonOpenSSLFallback(method)) |
1184 | return true; |
1185 | |
1186 | auto legacyProvider = OSSL_PROVIDER_ptr(OSSL_PROVIDER_load(nullptr, "legacy" )); |
1187 | auto defaultProvider = OSSL_PROVIDER_ptr(OSSL_PROVIDER_load(nullptr, "default" )); |
1188 | |
1189 | const char *restriction = "-fips" ; |
1190 | EVP_MD_ptr algorithm = EVP_MD_ptr(EVP_MD_fetch(nullptr, methodToName(method), restriction)); |
1191 | |
1192 | return algorithm != nullptr; |
1193 | #else |
1194 | switch (method) { |
1195 | case QCryptographicHash::Sha1: |
1196 | #ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 |
1197 | case QCryptographicHash::Md4: |
1198 | case QCryptographicHash::Md5: |
1199 | case QCryptographicHash::Sha224: |
1200 | case QCryptographicHash::Sha256: |
1201 | case QCryptographicHash::Sha384: |
1202 | case QCryptographicHash::Sha512: |
1203 | case QCryptographicHash::RealSha3_224: |
1204 | case QCryptographicHash::Keccak_224: |
1205 | case QCryptographicHash::RealSha3_256: |
1206 | case QCryptographicHash::Keccak_256: |
1207 | case QCryptographicHash::RealSha3_384: |
1208 | case QCryptographicHash::Keccak_384: |
1209 | case QCryptographicHash::RealSha3_512: |
1210 | case QCryptographicHash::Keccak_512: |
1211 | case QCryptographicHash::Blake2b_160: |
1212 | case QCryptographicHash::Blake2b_256: |
1213 | case QCryptographicHash::Blake2b_384: |
1214 | case QCryptographicHash::Blake2b_512: |
1215 | case QCryptographicHash::Blake2s_128: |
1216 | case QCryptographicHash::Blake2s_160: |
1217 | case QCryptographicHash::Blake2s_224: |
1218 | case QCryptographicHash::Blake2s_256: |
1219 | #endif |
1220 | return true; |
1221 | case QCryptographicHash::NumAlgorithms: ; |
1222 | }; |
1223 | return false; |
1224 | #endif // !USING_OPENSSL3 |
1225 | } |
1226 | |
1227 | static constexpr int qt_hash_block_size(QCryptographicHash::Algorithm method) |
1228 | { |
1229 | switch (method) { |
1230 | case QCryptographicHash::Sha1: |
1231 | return SHA1_Message_Block_Size; |
1232 | #ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 |
1233 | case QCryptographicHash::Md4: |
1234 | return 64; |
1235 | case QCryptographicHash::Md5: |
1236 | return 64; |
1237 | case QCryptographicHash::Sha224: |
1238 | return SHA224_Message_Block_Size; |
1239 | case QCryptographicHash::Sha256: |
1240 | return SHA256_Message_Block_Size; |
1241 | case QCryptographicHash::Sha384: |
1242 | return SHA384_Message_Block_Size; |
1243 | case QCryptographicHash::Sha512: |
1244 | return SHA512_Message_Block_Size; |
1245 | case QCryptographicHash::RealSha3_224: |
1246 | case QCryptographicHash::Keccak_224: |
1247 | return 144; |
1248 | case QCryptographicHash::RealSha3_256: |
1249 | case QCryptographicHash::Keccak_256: |
1250 | return 136; |
1251 | case QCryptographicHash::RealSha3_384: |
1252 | case QCryptographicHash::Keccak_384: |
1253 | return 104; |
1254 | case QCryptographicHash::RealSha3_512: |
1255 | case QCryptographicHash::Keccak_512: |
1256 | return 72; |
1257 | case QCryptographicHash::Blake2b_160: |
1258 | case QCryptographicHash::Blake2b_256: |
1259 | case QCryptographicHash::Blake2b_384: |
1260 | case QCryptographicHash::Blake2b_512: |
1261 | return BLAKE2B_BLOCKBYTES; |
1262 | case QCryptographicHash::Blake2s_128: |
1263 | case QCryptographicHash::Blake2s_160: |
1264 | case QCryptographicHash::Blake2s_224: |
1265 | case QCryptographicHash::Blake2s_256: |
1266 | return BLAKE2S_BLOCKBYTES; |
1267 | #endif // QT_CRYPTOGRAPHICHASH_ONLY_SHA1 |
1268 | case QCryptographicHash::NumAlgorithms: |
1269 | #if !defined(Q_CC_GNU_ONLY) || Q_CC_GNU >= 900 |
1270 | // GCC 8 has trouble with Q_UNREACHABLE() in constexpr functions |
1271 | Q_UNREACHABLE(); |
1272 | #endif |
1273 | break; |
1274 | } |
1275 | return 0; |
1276 | } |
1277 | |
1278 | constexpr int maxHashBlockSize() |
1279 | { |
1280 | int result = 0; |
1281 | using A = QCryptographicHash::Algorithm; |
1282 | for (int i = 0; i < A::NumAlgorithms ; ++i) |
1283 | result = std::max(a: result, b: qt_hash_block_size(method: A(i))); |
1284 | return result; |
1285 | } |
1286 | |
1287 | [[maybe_unused]] |
1288 | constexpr int minHashBlockSize() |
1289 | { |
1290 | int result = INT_MAX; |
1291 | using A = QCryptographicHash::Algorithm; |
1292 | for (int i = 0; i < A::NumAlgorithms ; ++i) |
1293 | result = std::min(a: result, b: qt_hash_block_size(method: A(i))); |
1294 | return result; |
1295 | } |
1296 | |
1297 | [[maybe_unused]] |
1298 | constexpr int gcdHashBlockSize() |
1299 | { |
1300 | int result = 0; |
1301 | using A = QCryptographicHash::Algorithm; |
1302 | for (int i = 0; i < A::NumAlgorithms ; ++i) |
1303 | result = std::gcd(m: result, n: qt_hash_block_size(method: A(i))); |
1304 | return result; |
1305 | } |
1306 | |
1307 | using HashBlock = QSmallByteArray<maxHashBlockSize()>; |
1308 | |
1309 | static HashBlock xored(const HashBlock &block, quint8 val) noexcept |
1310 | { |
1311 | // some hints for the optimizer: |
1312 | Q_ASSERT(block.size() >= minHashBlockSize()); |
1313 | Q_ASSERT(block.size() <= maxHashBlockSize()); |
1314 | Q_ASSERT(block.size() % gcdHashBlockSize() == 0); |
1315 | HashBlock result; |
1316 | result.resizeForOverwrite(s: block.size()); |
1317 | for (qsizetype i = 0; i < block.size(); ++i) |
1318 | result[i] = block[i] ^ val; |
1319 | return result; |
1320 | } |
1321 | |
1322 | class QMessageAuthenticationCodePrivate |
1323 | { |
1324 | public: |
1325 | explicit QMessageAuthenticationCodePrivate(QCryptographicHash::Algorithm m) noexcept |
1326 | : messageHash(m) |
1327 | { |
1328 | } |
1329 | |
1330 | HashBlock key; |
1331 | QCryptographicHashPrivate messageHash; |
1332 | |
1333 | void setKey(QByteArrayView k) noexcept; |
1334 | void initMessageHash() noexcept; |
1335 | void finalize(); |
1336 | |
1337 | // when not called from the static hash() function, this function needs to be |
1338 | // called with messageHash.finalizeMutex held: |
1339 | void finalizeUnchecked() noexcept; |
1340 | // END functions that need to be called with finalizeMutex held |
1341 | }; |
1342 | |
1343 | /*! |
1344 | \internal |
1345 | |
1346 | Transforms key \a newKey into a block-sized format and stores it in member |
1347 | \c key. |
1348 | |
1349 | This function assumes it can use messageHash (i.e. it's in its initial |
1350 | state (reset() has been called)). |
1351 | */ |
1352 | void QMessageAuthenticationCodePrivate::setKey(QByteArrayView newKey) noexcept |
1353 | { |
1354 | const int blockSize = qt_hash_block_size(method: messageHash.method); |
1355 | |
1356 | if (newKey.size() > blockSize) { |
1357 | messageHash.addData(bytes: newKey); |
1358 | messageHash.finalizeUnchecked(); |
1359 | static_assert([] { |
1360 | using A = QCryptographicHash::Algorithm; |
1361 | for (int i = 0; i < A::NumAlgorithms; ++i) { |
1362 | if (hashLengthInternal(method: A(i)) > qt_hash_block_size(method: A(i))) |
1363 | return false; |
1364 | } |
1365 | return true; |
1366 | }(), "this code assumes that a hash's result always fits into that hash's block size" ); |
1367 | key = messageHash.result; |
1368 | messageHash.reset(); |
1369 | } else { |
1370 | key.assign(c: newKey); |
1371 | } |
1372 | |
1373 | if (key.size() < blockSize) |
1374 | key.resize(s: blockSize, v: '\0'); |
1375 | |
1376 | initMessageHash(); |
1377 | } |
1378 | |
1379 | /*! |
1380 | \internal |
1381 | |
1382 | Seeds messageHash from \c key. |
1383 | |
1384 | This function assumes that messageHash is in its initial state (reset() has |
1385 | been called). |
1386 | */ |
1387 | void QMessageAuthenticationCodePrivate::initMessageHash() noexcept |
1388 | { |
1389 | messageHash.addData(bytes: xored(block: key, val: 0x36)); |
1390 | } |
1391 | |
1392 | /*! |
1393 | \class QMessageAuthenticationCode |
1394 | \inmodule QtCore |
1395 | |
1396 | \brief The QMessageAuthenticationCode class provides a way to generate |
1397 | hash-based message authentication codes. |
1398 | |
1399 | \since 5.1 |
1400 | |
1401 | \ingroup tools |
1402 | \reentrant |
1403 | |
1404 | Use the QMessageAuthenticationCode class to generate hash-based message |
1405 | authentication codes (HMACs). The class supports all cryptographic |
1406 | hash algorithms from \l QCryptographicHash (see also |
1407 | \l{QCryptographicHash::Algorithm}). |
1408 | |
1409 | To generate a message authentication code, pass a suitable hash |
1410 | algorithm and secret key to the constructor. Then process the message |
1411 | data by calling \l addData() one or more times. After the full |
1412 | message has been processed, get the final authentication code |
1413 | via the \l result() function: |
1414 | |
1415 | \snippet qmessageauthenticationcode/main.cpp 0 |
1416 | \dots |
1417 | \snippet qmessageauthenticationcode/main.cpp 1 |
1418 | |
1419 | For simple cases like above, you can also use the static |
1420 | \l hash() function: |
1421 | |
1422 | \snippet qmessageauthenticationcode/main.cpp 2 |
1423 | |
1424 | |
1425 | \note The cryptographic strength of the HMAC depends upon the |
1426 | size of the secret key, and the security of the |
1427 | underlying hash function. |
1428 | |
1429 | \sa QCryptographicHash, QCryptographicHash::Algorithm |
1430 | */ |
1431 | |
1432 | /*! |
1433 | Constructs an object that can be used to create a cryptographic hash from data |
1434 | using method \a method and key \a key. |
1435 | |
1436 | //! [qba-to-qbav-6.6] |
1437 | \note In Qt versions prior to 6.6, this function took its arguments as |
1438 | QByteArray, not QByteArrayView. If you experience compile errors, it's |
1439 | because your code is passing objects that are implicitly convertible to |
1440 | QByteArray, but not QByteArrayView. Wrap the corresponding argument in |
1441 | \c{QByteArray{~~~}} to make the cast explicit. This is backwards-compatible |
1442 | with old Qt versions. |
1443 | //! [qba-to-qbav-6.6] |
1444 | */ |
1445 | QMessageAuthenticationCode::QMessageAuthenticationCode(QCryptographicHash::Algorithm method, |
1446 | QByteArrayView key) |
1447 | : d(new QMessageAuthenticationCodePrivate(method)) |
1448 | { |
1449 | d->setKey(key); |
1450 | } |
1451 | |
1452 | /*! |
1453 | Destroys the object. |
1454 | */ |
1455 | QMessageAuthenticationCode::~QMessageAuthenticationCode() |
1456 | { |
1457 | delete d; |
1458 | } |
1459 | |
1460 | /*! |
1461 | \fn QMessageAuthenticationCode::QMessageAuthenticationCode(QMessageAuthenticationCode &&other) |
1462 | |
1463 | Move-constructs a new QMessageAuthenticationCode from \a other. |
1464 | |
1465 | \note The moved-from object \a other is placed in a |
1466 | partially-formed state, in which the only valid operations are |
1467 | destruction and assignment of a new object. |
1468 | |
1469 | \since 6.6 |
1470 | */ |
1471 | |
1472 | /*! |
1473 | \fn QMessageAuthenticationCode &QMessageAuthenticationCode::operator=(QMessageAuthenticationCode &&other) |
1474 | |
1475 | Move-assigns \a other to this QMessageAuthenticationCode instance. |
1476 | |
1477 | \note The moved-from object \a other is placed in a |
1478 | partially-formed state, in which the only valid operations are |
1479 | destruction and assignment of a new object. |
1480 | |
1481 | \since 6.6 |
1482 | */ |
1483 | |
1484 | /*! |
1485 | \fn void QMessageAuthenticationCode::swap(QMessageAuthenticationCode &other) |
1486 | \memberswap{message authentication code} |
1487 | \since 6.6 |
1488 | */ |
1489 | |
1490 | /*! |
1491 | Resets message data. Calling this function doesn't affect the key. |
1492 | */ |
1493 | void QMessageAuthenticationCode::reset() noexcept |
1494 | { |
1495 | d->messageHash.reset(); |
1496 | d->initMessageHash(); |
1497 | } |
1498 | |
1499 | /*! |
1500 | Sets secret \a key. Calling this function automatically resets the object state. |
1501 | |
1502 | For optimal performance, call this function only to \e change the active key, |
1503 | not to set an \e initial key, as in |
1504 | |
1505 | \code |
1506 | QMessageAuthenticationCode mac(method); |
1507 | mac.setKey(key); // does extra work |
1508 | use(mac); |
1509 | \endcode |
1510 | |
1511 | Prefer to pass initial keys as the constructor argument: |
1512 | |
1513 | \code |
1514 | QMessageAuthenticationCode mac(method, key); // OK, optimal |
1515 | use(mac); |
1516 | \endcode |
1517 | |
1518 | You can use std::optional to delay construction of a |
1519 | QMessageAuthenticationCode until you know the key: |
1520 | |
1521 | \code |
1522 | std::optional<QMessageAuthenticationCode> mac; |
1523 | ~~~ |
1524 | key = ~~~; |
1525 | mac.emplace(method, key); |
1526 | use(*mac); |
1527 | \endcode |
1528 | |
1529 | \include qcryptographichash.cpp {qba-to-qbav-6.6} |
1530 | */ |
1531 | void QMessageAuthenticationCode::setKey(QByteArrayView key) noexcept |
1532 | { |
1533 | d->messageHash.reset(); |
1534 | d->setKey(key); |
1535 | } |
1536 | |
1537 | /*! |
1538 | \overload |
1539 | Adds the first \a length chars of \a data to the message. |
1540 | */ |
1541 | void QMessageAuthenticationCode::addData(const char *data, qsizetype length) |
1542 | { |
1543 | d->messageHash.addData(bytes: {data, length}); |
1544 | } |
1545 | |
1546 | /*! |
1547 | Adds \a data to the message. |
1548 | |
1549 | \include qcryptographichash.cpp {qba-to-qbav-6.6} |
1550 | |
1551 | \sa resultView(), result() |
1552 | */ |
1553 | void QMessageAuthenticationCode::addData(QByteArrayView data) noexcept |
1554 | { |
1555 | d->messageHash.addData(bytes: data); |
1556 | } |
1557 | |
1558 | /*! |
1559 | Reads the data from the open QIODevice \a device until it ends |
1560 | and adds it to message. Returns \c true if reading was successful. |
1561 | |
1562 | \note \a device must be already opened. |
1563 | */ |
1564 | bool QMessageAuthenticationCode::addData(QIODevice *device) |
1565 | { |
1566 | return d->messageHash.addData(device); |
1567 | } |
1568 | |
1569 | /*! |
1570 | \since 6.6 |
1571 | |
1572 | Returns the final hash value. |
1573 | |
1574 | Note that the returned view remains valid only as long as the |
1575 | QMessageAuthenticationCode object is not modified by other means. |
1576 | |
1577 | \sa result() |
1578 | */ |
1579 | QByteArrayView QMessageAuthenticationCode::resultView() const noexcept |
1580 | { |
1581 | d->finalize(); |
1582 | return d->messageHash.resultView(); |
1583 | } |
1584 | |
1585 | /*! |
1586 | Returns the final authentication code. |
1587 | |
1588 | \sa resultView(), QByteArray::toHex() |
1589 | */ |
1590 | QByteArray QMessageAuthenticationCode::result() const |
1591 | { |
1592 | return resultView().toByteArray(); |
1593 | } |
1594 | |
1595 | void QMessageAuthenticationCodePrivate::finalize() |
1596 | { |
1597 | const auto lock = qt_scoped_lock(mutex&: messageHash.finalizeMutex); |
1598 | if (!messageHash.result.isEmpty()) |
1599 | return; |
1600 | finalizeUnchecked(); |
1601 | } |
1602 | |
1603 | void QMessageAuthenticationCodePrivate::finalizeUnchecked() noexcept |
1604 | { |
1605 | messageHash.finalizeUnchecked(); |
1606 | const HashResult hashedMessage = messageHash.result; |
1607 | |
1608 | messageHash.reset(); |
1609 | messageHash.addData(bytes: xored(block: key, val: 0x5c)); |
1610 | messageHash.addData(bytes: hashedMessage); |
1611 | messageHash.finalizeUnchecked(); |
1612 | } |
1613 | |
1614 | /*! |
1615 | Returns the authentication code for the message \a message using |
1616 | the key \a key and the method \a method. |
1617 | |
1618 | \include qcryptographichash.cpp {qba-to-qbav-6.6} |
1619 | |
1620 | \sa hashInto() |
1621 | */ |
1622 | QByteArray QMessageAuthenticationCode::hash(QByteArrayView message, QByteArrayView key, |
1623 | QCryptographicHash::Algorithm method) |
1624 | { |
1625 | QByteArray ba(hashLengthInternal(method), Qt::Uninitialized); |
1626 | [[maybe_unused]] const auto r = hashInto(buffer: ba, message, key, method); |
1627 | Q_ASSERT(r.size() == ba.size()); |
1628 | return ba; |
1629 | } |
1630 | |
1631 | /*! |
1632 | \since 6.8 |
1633 | \fn QMessageAuthenticationCode::hashInto(QSpan<char> buffer, QSpan<const QByteArrayView> messageParts, QByteArrayView key, QCryptographicHash::Algorithm method); |
1634 | \fn QMessageAuthenticationCode::hashInto(QSpan<uchar> buffer, QSpan<const QByteArrayView> messageParts, QByteArrayView key, QCryptographicHash::Algorithm method); |
1635 | \fn QMessageAuthenticationCode::hashInto(QSpan<std::byte> buffer, QSpan<const QByteArrayView> messageParts, QByteArrayView key, QCryptographicHash::Algorithm method); |
1636 | \fn QMessageAuthenticationCode::hashInto(QSpan<char> buffer, QByteArrayView message, QByteArrayView key, QCryptographicHash::Algorithm method); |
1637 | \fn QMessageAuthenticationCode::hashInto(QSpan<uchar> buffer, QByteArrayView message, QByteArrayView key, QCryptographicHash::Algorithm method); |
1638 | \fn QMessageAuthenticationCode::hashInto(QSpan<std::byte> buffer, QByteArrayView message, QByteArrayView key, QCryptographicHash::Algorithm method); |
1639 | |
1640 | Returns the authentication code for the message (\a message or, for the |
1641 | QSpan overloads, the concatenation of \a messageParts) using the key \a key |
1642 | and the method \a method. |
1643 | |
1644 | The return value will be a sub-span of \a buffer, unless \a buffer is of |
1645 | insufficient size, in which case a null QByteArrayView is returned. |
1646 | |
1647 | \sa hash() |
1648 | */ |
1649 | QByteArrayView QMessageAuthenticationCode::hashInto(QSpan<std::byte> buffer, |
1650 | QSpan<const QByteArrayView> messageParts, |
1651 | QByteArrayView key, |
1652 | QCryptographicHash::Algorithm method) noexcept |
1653 | { |
1654 | QMessageAuthenticationCodePrivate mac(method); |
1655 | mac.setKey(key); |
1656 | for (QByteArrayView part : messageParts) |
1657 | mac.messageHash.addData(bytes: part); |
1658 | mac.finalizeUnchecked(); |
1659 | auto result = mac.messageHash.resultView(); |
1660 | if (buffer.size() < result.size()) |
1661 | return {}; // buffer too small |
1662 | // ### optimize: have the method directly write into `buffer` |
1663 | memcpy(dest: buffer.data(), src: result.data(), n: result.size()); |
1664 | return buffer.first(n: result.size()); |
1665 | } |
1666 | |
1667 | QT_END_NAMESPACE |
1668 | |
1669 | #ifndef QT_NO_QOBJECT |
1670 | #include "moc_qcryptographichash.cpp" |
1671 | #endif |
1672 | |