Revision control

++ // Copyright (C) 2023 The Qt Company Ltd.
++ // Copyright (C) 2013 Ruslan Nigmatullin <euroelessar@yandex.ru>
++ // Copyright (C) 2013 Richard J. Moore <rich@kde.org>.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:critical reason:cryptography
++
++ #include <qcryptographichash.h>
++ #include <qmessageauthenticationcode.h>
++
++ #include <QtCore/private/qsmallbytearray_p.h>
++ #include <qiodevice.h>
++ #include <qmutex.h>
++ #include <private/qlocking_p.h>
++
++ #include <array>
++ #include <climits>
++ #include <numeric>
++
++ #include "../../3rdparty/sha1/sha1.cpp"
++
++ // Header from rfc6234
++ #include "../../3rdparty/rfc6234/sha.h"
++
++ #if !QT_CONFIG(openssl_hash)
++ #include "../../3rdparty/md5/md5.h"
++ #include "../../3rdparty/md5/md5.cpp"
++ #include "../../3rdparty/md4/md4.h"
++ #include "../../3rdparty/md4/md4.cpp"
++ #endif // !QT_CONFIG(openssl_hash)
++
++ typedef unsigned char BitSequence;
++ typedef unsigned long long DataLength;
++ typedef enum { SUCCESS = 0, FAIL = 1, BAD_HASHLEN = 2 } HashReturn;
++
++ #ifdef Q_OS_RTEMS
++ # undef ALIGN
++ #endif
++
++ #include "../../3rdparty/sha3/KeccakSponge.c"
++ typedef spongeState hashState;
++
++ #include "../../3rdparty/sha3/KeccakNISTInterface.c"
++
++ /*
++ This lets us choose between SHA3 implementations at build time.
++ */
++ typedef spongeState SHA3Context;
++ typedef HashReturn (SHA3Init)(hashState *state, int hashbitlen);
++ typedef HashReturn (SHA3Update)(hashState *state, const BitSequence *data, DataLength databitlen);
++ typedef HashReturn (SHA3Final)(hashState *state, BitSequence *hashval);
++
++ #if Q_PROCESSOR_WORDSIZE == 8 // 64 bit version
++
++ #include "../../3rdparty/sha3/KeccakF-1600-opt64.c"
++
++ Q_CONSTINIT static SHA3Init * const sha3Init = Init;
++ Q_CONSTINIT static SHA3Update * const sha3Update = Update;
++ Q_CONSTINIT static SHA3Final * const sha3Final = Final;
++
++ #else // 32 bit optimised fallback
++
++ #include "../../3rdparty/sha3/KeccakF-1600-opt32.c"
++
++ Q_CONSTINIT static SHA3Init * const sha3Init = Init;
++ Q_CONSTINIT static SHA3Update * const sha3Update = Update;
++ Q_CONSTINIT static SHA3Final * const sha3Final = Final;
++
++ #endif
++
++ #if !QT_CONFIG(openssl_hash)
++ /*
++ These 2 functions replace macros of the same name in sha224-256.c and
++ sha384-512.c. Originally, these macros relied on a global static 'addTemp'
++ variable. We do not want this for 2 reasons:
++
++ 1. since we are including the sources directly, the declaration of the 2 conflict
++
++ 2. static variables are not thread-safe, we do not want multiple threads
++ computing a hash to corrupt one another
++ */
++ static int SHA224_256AddLength(SHA256Context *context, unsigned int length);
++ static int SHA384_512AddLength(SHA512Context *context, unsigned int length);
++
++ // Sources from rfc6234, with 4 modifications:
++ // sha224-256.c - commented out 'static uint32_t addTemp;' on line 68
++ // sha224-256.c - appended 'M' to the SHA224_256AddLength macro on line 70
++ #include "../../3rdparty/rfc6234/sha224-256.c"
++ // sha384-512.c - commented out 'static uint64_t addTemp;' on line 302
++ // sha384-512.c - appended 'M' to the SHA224_256AddLength macro on line 304
++ #include "../../3rdparty/rfc6234/sha384-512.c"
++
++ static inline int SHA224_256AddLength(SHA256Context *context, unsigned int length)
++ {
++ uint32_t addTemp;
++ return SHA224_256AddLengthM(context, length);
++ }
++ static inline int SHA384_512AddLength(SHA512Context *context, unsigned int length)
++ {
++ uint64_t addTemp;
++ return SHA384_512AddLengthM(context, length);
++ }
++ #endif // !QT_CONFIG(opensslv30)
++
++ #if QT_CONFIG(system_libb2)
++ #include <blake2.h>
++ #else
++ QT_WARNING_PUSH
++ QT_WARNING_DISABLE_CLANG("-Wunused-function")
++ QT_WARNING_DISABLE_GCC("-Wunused-function")
++ QT_WARNING_DISABLE_MSVC(4505)
++ #include "../../3rdparty/blake2/src/blake2b-ref.c"
++ #include "../../3rdparty/blake2/src/blake2s-ref.c"
++ QT_WARNING_POP
++ #endif
++
++ #if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(openssl_hash)
++ #define USING_OPENSSL30
++ #include <openssl/crypto.h>
++ #include <openssl/evp.h>
++ #include <openssl/provider.h>
++ #endif
++
++ QT_BEGIN_NAMESPACE
++
++ static constexpr int hashLengthInternal(QCryptographicHash::Algorithm method) noexcept
++ {
++ switch (method) {
++ #define CASE(Enum, Size) \
++ case QCryptographicHash:: Enum : \
++ return Size \
++ /*end*/
++ CASE(Sha1, 20);
++ CASE(Md4, 16);
++ CASE(Md5, 16);
++ CASE(Sha224, SHA224HashSize);
++ CASE(Sha256, SHA256HashSize);
++ CASE(Sha384, SHA384HashSize);
++ CASE(Sha512, SHA512HashSize);
++ CASE(Blake2s_128, 128 / 8);
++ case QCryptographicHash::Blake2b_160:
++ case QCryptographicHash::Blake2s_160:
++ return 160 / 8;
++ case QCryptographicHash::RealSha3_224:
++ case QCryptographicHash::Keccak_224:
++ case QCryptographicHash::Blake2s_224:
++ return 224 / 8;
++ case QCryptographicHash::RealSha3_256:
++ case QCryptographicHash::Keccak_256:
++ case QCryptographicHash::Blake2b_256:
++ case QCryptographicHash::Blake2s_256:
++ return 256 / 8;
++ case QCryptographicHash::RealSha3_384:
++ case QCryptographicHash::Keccak_384:
++ case QCryptographicHash::Blake2b_384:
++ return 384 / 8;
++ case QCryptographicHash::RealSha3_512:
++ case QCryptographicHash::Keccak_512:
++ case QCryptographicHash::Blake2b_512:
++ return 512 / 8;
++ #undef CASE
++ case QCryptographicHash::NumAlgorithms: ;
++ // fall through
++ // Q_UNREACHABLE() would be BiC here, as hashLength(~~invalid~~) worked in 6.4
++ }
++ return 0;
++ }
++
++ static constexpr int maxHashLength()
++ {
++ int result = 0;
++ using A = QCryptographicHash::Algorithm;
++ for (int i = 0; i < A::NumAlgorithms; ++i)
++ result = std::max(result, hashLengthInternal(A(i)));
++ return result;
++ }
++
++ using HashResult = QSmallByteArray<maxHashLength()>;
++
++ #ifdef USING_OPENSSL30
++ static constexpr const char * methodToName(QCryptographicHash::Algorithm method) noexcept
++ {
++ switch (method) {
++ #define CASE(Enum, Name) \
++ case QCryptographicHash:: Enum : \
++ return Name \
++ /*end*/
++ CASE(Sha1, "SHA1");
++ CASE(Md4, "MD4");
++ CASE(Md5, "MD5");
++ CASE(Sha224, "SHA224");
++ CASE(Sha256, "SHA256");
++ CASE(Sha384, "SHA384");
++ CASE(Sha512, "SHA512");
++ CASE(RealSha3_224, "SHA3-224");
++ CASE(RealSha3_256, "SHA3-256");
++ CASE(RealSha3_384, "SHA3-384");
++ CASE(RealSha3_512, "SHA3-512");
++ CASE(Blake2b_512, "BLAKE2B512");
++ CASE(Blake2s_256, "BLAKE2S256");
++ // not supported by OpenSSL:
++ CASE(Keccak_224, nullptr);
++ CASE(Keccak_256, nullptr);
++ CASE(Keccak_384, nullptr);
++ CASE(Keccak_512, nullptr);
++ CASE(Blake2b_160, nullptr);
++ CASE(Blake2b_256, nullptr);
++ CASE(Blake2b_384, nullptr);
++ CASE(Blake2s_128, nullptr);
++ CASE(Blake2s_160, nullptr);
++ CASE(Blake2s_224, nullptr);
++ CASE(NumAlgorithms, nullptr);
++ #undef CASE
++ }
++ return nullptr;
++ }
++
++ /*
++ Never unloaded: it is the unload that trips OpenSSL up, freeing a stale
++ provider-store entry a second time at exit (QTBUG-148575;
++ openssl/openssl#32079 has the exact sequence, and is where to look before
++ removing any of this). retain_fallbacks=1 leaves OpenSSL's own fallback in
++ place for everyone else in the process, e.g. the TLS backend (QTBUG-136223).
++
++ Whether the load succeeded is of no interest to the callers: the
++ EVP_MD_fetch() that follows is the actual availability test, and it can
++ still succeed from the fallback or from another provider (e.g. FIPS).
++ */
++ static void ensureDefaultProviderLoaded()
++ {
++ static const bool loaded = OSSL_PROVIDER_try_load(nullptr, "default", /*retain_fallbacks=*/1);
++ Q_UNUSED(loaded);
++ }
++
++ namespace {
++ struct OSSL_LIB_CTX_deleter {
++ void operator()(OSSL_LIB_CTX *ctx) const noexcept {
++ OSSL_LIB_CTX_free(ctx);
++ }
++ };
++ using OSSL_LIB_CTX_ptr = std::unique_ptr<OSSL_LIB_CTX, OSSL_LIB_CTX_deleter>;
++ } // unnamed namespace
++
++ /*
++ MD4 only exists in OpenSSL's "legacy" provider, which we don't want loaded
++ process-wide - other OpenSSL users in the process could then fetch other,
++ deliberately-weak algorithms from it too. Give it its own context instead:
++ load it (and its config, since OSSL_LIB_CTX_new() doesn't do that on its
++ own) once and keep it alive like ensureDefaultProviderLoaded() above.
++ */
++ static OSSL_LIB_CTX *legacyProviderContext()
++ {
++ static const OSSL_LIB_CTX_ptr ctx = [] {
++ OSSL_LIB_CTX_ptr ctx(OSSL_LIB_CTX_new());
++ if (ctx) {
++ // Unlike the global context's automatic init, this fails when
++ // there is no readable openssl.cnf.
++ const bool configLoaded = OSSL_LIB_CTX_load_config(ctx.get(), nullptr);
++ Q_UNUSED(configLoaded);
++ if (!OSSL_PROVIDER_load(ctx.get(), "legacy"))
++ ctx.reset();
++ }
++ return ctx;
++ }();
++ return ctx.get();
++ }
++ #endif // USING_OPENSSL30
++
++ class QCryptographicHashPrivate
++ {
++ public:
++ explicit QCryptographicHashPrivate(QCryptographicHash::Algorithm method) noexcept
++ : state(method), method(method)
++ {
++ }
++ ~QCryptographicHashPrivate()
++ {
++ state.destroy(method);
++ }
++
++ void reset() noexcept;
++ void addData(QByteArrayView bytes) noexcept;
++ bool addData(QIODevice *dev);
++ void finalize() noexcept;
++ // when not called from the static hash() function, this function needs to be
++ // called with finalizeMutex held (finalize() will do that):
++ void finalizeUnchecked() noexcept;
++ QSpan<uchar> finalizeUnchecked(QSpan<uchar> buffer) noexcept;
++
++ // END functions that need to be called with finalizeMutex held
++ QByteArrayView resultView() const noexcept { return result.toByteArrayView(); }
++ static bool supportsAlgorithm(QCryptographicHash::Algorithm method);
++
++ #ifdef USING_OPENSSL30
++ struct EVP_MD_CTX_deleter {
++ void operator()(EVP_MD_CTX *ctx) const noexcept {
++ EVP_MD_CTX_free(ctx);
++ }
++ };
++ struct EVP_MD_deleter {
++ void operator()(EVP_MD *md) const noexcept {
++ EVP_MD_free(md);
++ }
++ };
++ using EVP_MD_CTX_ptr = std::unique_ptr<EVP_MD_CTX, EVP_MD_CTX_deleter>;
++ using EVP_MD_ptr = std::unique_ptr<EVP_MD, EVP_MD_deleter>;
++ struct EVP {
++ EVP_MD_ptr algorithm;
++ EVP_MD_CTX_ptr context;
++ bool initializationFailed;
++
++ explicit EVP(QCryptographicHash::Algorithm method);
++ void reset() noexcept;
++ void finalizeUnchecked(QSpan<uchar> buffer) noexcept;
++ };
++ #endif
++
++ union State {
++ explicit State(QCryptographicHash::Algorithm method);
++ void destroy(QCryptographicHash::Algorithm method);
++ #ifdef USING_OPENSSL30
++ ~State() {}
++ #endif
++
++ void reset(QCryptographicHash::Algorithm method) noexcept;
++ void addData(QCryptographicHash::Algorithm method, QByteArrayView data) noexcept;
++ void finalizeUnchecked(QCryptographicHash::Algorithm method, QSpan<uchar> buffer) noexcept;
++
++ Sha1State sha1Context;
++ #ifdef USING_OPENSSL30
++ EVP evp;
++ #else
++ MD5Context md5Context;
++ md4_context md4Context;
++ SHA224Context sha224Context;
++ SHA256Context sha256Context;
++ SHA384Context sha384Context;
++ SHA512Context sha512Context;
++ #endif
++ SHA3Context sha3Context;
++
++ enum class Sha3Variant { Sha3, Keccak };
++ static void sha3Finish(SHA3Context &ctx, QSpan<uchar> result, Sha3Variant sha3Variant);
++ blake2b_state blake2bContext;
++ blake2s_state blake2sContext;
++ } state;
++ // protects result in finalize()
++ QBasicMutex finalizeMutex;
++ HashResult result;
++
++ const QCryptographicHash::Algorithm method;
++ };
++
++ void QCryptographicHashPrivate::State::sha3Finish(SHA3Context &ctx, QSpan<uchar> result,
++ Sha3Variant sha3Variant)
++ {
++ /*
++ FIPS 202 §6.1 defines SHA-3 in terms of calculating the Keccak function
++ over the original message with the two-bit suffix "01" appended to it.
++ This variable stores that suffix (and it's fed into the calculations
++ when the hash is returned to users).
++
++ Only 2 bits of this variable are actually used (see the call to sha3Update
++ below). The Keccak implementation we're using will actually use the
++ *leftmost* 2 bits, and interpret them right-to-left. In other words, the
++ bits must appear in order of *increasing* significance; and as the two most
++ significant bits of the byte -- the rightmost 6 are ignored. (Yes, this
++ seems self-contradictory, but it's the way it is...)
++
++ Overall, this means:
++ * the leftmost two bits must be "10" (not "01"!);
++ * we don't care what the other six bits are set to (they can be set to
++ any value), but we arbitrarily set them to 0;
++
++ and for an unsigned char this gives us 0b10'00'00'00, or 0x80.
++ */
++ static const unsigned char sha3FinalSuffix = 0x80;
++
++ switch (sha3Variant) {
++ case Sha3Variant::Sha3:
++ sha3Update(&ctx, reinterpret_cast<const BitSequence *>(&sha3FinalSuffix), 2);
++ break;
++ case Sha3Variant::Keccak:
++ break;
++ }
++
++ sha3Final(&ctx, result.data());
++ }
++
++ /*!
++ \class QCryptographicHash
++ \inmodule QtCore
++
++ \brief The QCryptographicHash class provides a way to generate cryptographic hashes.
++
++ \since 4.3
++
++ \ingroup tools
++ \reentrant
++
++ QCryptographicHash can be used to generate cryptographic hashes of binary or text data.
++
++ Refer to the documentation of the \l QCryptographicHash::Algorithm enum for a
++ list of the supported algorithms.
++ */
++
++ /*!
++ \enum QCryptographicHash::Algorithm
++
++ \note In Qt versions before 5.9, when asked to generate a SHA3 hash sum,
++ QCryptographicHash actually calculated Keccak. If you need compatibility with
++ SHA-3 hashes produced by those versions of Qt, use the \c{Keccak_}
++ enumerators. Alternatively, if source compatibility is required, define the
++ macro \c QT_SHA3_KECCAK_COMPAT.
++
++ \value Md4 Generate an MD4 hash sum
++ \value Md5 Generate an MD5 hash sum
++ \value Sha1 Generate an SHA-1 hash sum
++ \value Sha224 Generate an SHA-224 hash sum (SHA-2). Introduced in Qt 5.0
++ \value Sha256 Generate an SHA-256 hash sum (SHA-2). Introduced in Qt 5.0
++ \value Sha384 Generate an SHA-384 hash sum (SHA-2). Introduced in Qt 5.0
++ \value Sha512 Generate an SHA-512 hash sum (SHA-2). Introduced in Qt 5.0
++ \value Sha3_224 Generate an SHA3-224 hash sum. Introduced in Qt 5.1
++ \value Sha3_256 Generate an SHA3-256 hash sum. Introduced in Qt 5.1
++ \value Sha3_384 Generate an SHA3-384 hash sum. Introduced in Qt 5.1
++ \value Sha3_512 Generate an SHA3-512 hash sum. Introduced in Qt 5.1
++ \value Keccak_224 Generate a Keccak-224 hash sum. Introduced in Qt 5.9.2
++ \value Keccak_256 Generate a Keccak-256 hash sum. Introduced in Qt 5.9.2
++ \value Keccak_384 Generate a Keccak-384 hash sum. Introduced in Qt 5.9.2
++ \value Keccak_512 Generate a Keccak-512 hash sum. Introduced in Qt 5.9.2
++ \value Blake2b_160 Generate a BLAKE2b-160 hash sum. Introduced in Qt 6.0
++ \value Blake2b_256 Generate a BLAKE2b-256 hash sum. Introduced in Qt 6.0
++ \value Blake2b_384 Generate a BLAKE2b-384 hash sum. Introduced in Qt 6.0
++ \value Blake2b_512 Generate a BLAKE2b-512 hash sum. Introduced in Qt 6.0
++ \value Blake2s_128 Generate a BLAKE2s-128 hash sum. Introduced in Qt 6.0
++ \value Blake2s_160 Generate a BLAKE2s-160 hash sum. Introduced in Qt 6.0
++ \value Blake2s_224 Generate a BLAKE2s-224 hash sum. Introduced in Qt 6.0
++ \value Blake2s_256 Generate a BLAKE2s-256 hash sum. Introduced in Qt 6.0
++ \omitvalue RealSha3_224
++ \omitvalue RealSha3_256
++ \omitvalue RealSha3_384
++ \omitvalue RealSha3_512
++ \omitvalue NumAlgorithms
++ */
++
++ /*!
++ Constructs an object that can be used to create a cryptographic hash from data using \a method.
++ */
++ QCryptographicHash::QCryptographicHash(Algorithm method)
++ : d(new QCryptographicHashPrivate{method})
++ {
++ }
++
++ /*!
++ \fn QCryptographicHash::QCryptographicHash(QCryptographicHash &&other)
++
++ Move-constructs a new QCryptographicHash from \a other.
++
++ \note The moved-from object \a other is placed in a
++ partially-formed state, in which the only valid operations are
++ destruction and assignment of a new value.
++
++ \since 6.5
++ */
++
++ /*!
++ Destroys the object.
++ */
++ QCryptographicHash::~QCryptographicHash()
++ {
++ delete d;
++ }
++
++ /*!
++ \fn QCryptographicHash &QCryptographicHash::operator=(QCryptographicHash &&other)
++
++ Move-assigns \a other to this QCryptographicHash instance.
++
++ \note The moved-from object \a other is placed in a
++ partially-formed state, in which the only valid operations are
++ destruction and assignment of a new value.
++
++ \since 6.5
++ */
++
++ /*!
++ \fn void QCryptographicHash::swap(QCryptographicHash &other)
++ \memberswap{cryptographic hash}
++ \since 6.5
++ */
++
++ /*!
++ Resets the object.
++ */
++ void QCryptographicHash::reset() noexcept
++ {
++ d->reset();
++ }
++
++ /*!
++ Returns the algorithm used to generate the cryptographic hash.
++
++ \since 6.5
++ */
++ QCryptographicHash::Algorithm QCryptographicHash::algorithm() const noexcept
++ {
++ return d->method;
++ }
++
++ #ifdef USING_OPENSSL30
++
++ QCryptographicHashPrivate::State::State(QCryptographicHash::Algorithm method)
++ {
++ switch (method) {
++ case QCryptographicHash::Keccak_224:
++ case QCryptographicHash::Keccak_256:
++ case QCryptographicHash::Keccak_384:
++ case QCryptographicHash::Keccak_512:
++ new (&sha3Context) SHA3Context;
++ reset(method);
++ break;
++ case QCryptographicHash::Blake2b_160:
++ case QCryptographicHash::Blake2b_256:
++ case QCryptographicHash::Blake2b_384:
++ new (&blake2bContext) blake2b_state;
++ reset(method);
++ break;
++ case QCryptographicHash::Blake2s_128:
++ case QCryptographicHash::Blake2s_160:
++ case QCryptographicHash::Blake2s_224:
++ new (&blake2sContext) blake2s_state;
++ reset(method);
++ break;
++ case QCryptographicHash::Sha1:
++ case QCryptographicHash::Md4:
++ case QCryptographicHash::Md5:
++ case QCryptographicHash::Sha224:
++ case QCryptographicHash::Sha256:
++ case QCryptographicHash::Sha384:
++ case QCryptographicHash::Sha512:
++ case QCryptographicHash::RealSha3_224:
++ case QCryptographicHash::RealSha3_256:
++ case QCryptographicHash::RealSha3_384:
++ case QCryptographicHash::RealSha3_512:
++ case QCryptographicHash::Blake2b_512:
++ case QCryptographicHash::Blake2s_256:
++ new (&evp) EVP(method);
++ break;
++ case QCryptographicHash::NumAlgorithms:
++ Q_UNREACHABLE();
++ }
++ }
++
++ void QCryptographicHashPrivate::State::destroy(QCryptographicHash::Algorithm method)
++ {
++ switch (method) {
++ case QCryptographicHash::Keccak_224:
++ case QCryptographicHash::Keccak_256:
++ case QCryptographicHash::Keccak_384:
++ case QCryptographicHash::Keccak_512:
++ case QCryptographicHash::Blake2b_160:
++ case QCryptographicHash::Blake2b_256:
++ case QCryptographicHash::Blake2b_384:
++ case QCryptographicHash::Blake2s_128:
++ case QCryptographicHash::Blake2s_160:
++ case QCryptographicHash::Blake2s_224:
++ return;
++ case QCryptographicHash::Sha1:
++ case QCryptographicHash::Md4:
++ case QCryptographicHash::Md5:
++ case QCryptographicHash::Sha224:
++ case QCryptographicHash::Sha256:
++ case QCryptographicHash::Sha384:
++ case QCryptographicHash::Sha512:
++ case QCryptographicHash::RealSha3_224:
++ case QCryptographicHash::RealSha3_256:
++ case QCryptographicHash::RealSha3_384:
++ case QCryptographicHash::RealSha3_512:
++ case QCryptographicHash::Blake2b_512:
++ case QCryptographicHash::Blake2s_256:
++ evp.~EVP();
++ break;
++ case QCryptographicHash::NumAlgorithms:
++ Q_UNREACHABLE();
++ }
++ }
++
++ QCryptographicHashPrivate::EVP::EVP(QCryptographicHash::Algorithm method)
++ : initializationFailed{true}
++ {
++ OSSL_LIB_CTX *fetchCtx = nullptr;
++ if (method == QCryptographicHash::Md4) {
++ fetchCtx = legacyProviderContext();
++ if (!fetchCtx)
++ return;
++ } else {
++ ensureDefaultProviderLoaded();
++ }
++
++ context = EVP_MD_CTX_ptr(EVP_MD_CTX_new());
++
++ if (!context) {
++ return;
++ }
++
++ /*
++ * Using the "-fips" option will disable the global "fips=yes" for
++ * this one lookup and the algorithm can be fetched from any provider
++ * that implements the algorithm (including the FIPS provider). For Md4
++ * that is moot: fetchCtx only ever holds the legacy provider.
++ */
++ algorithm = EVP_MD_ptr{EVP_MD_fetch(fetchCtx, methodToName(method), "-fips")};
++ if (!algorithm) {
++ return;
++ }
++
++ initializationFailed = !EVP_DigestInit_ex(context.get(), algorithm.get(), nullptr);
++ }
++
++ #else // USING_OPENSSL30
++
++ QCryptographicHashPrivate::State::State(QCryptographicHash::Algorithm method)
++ {
++ switch (method) {
++ case QCryptographicHash::Sha1:
++ new (&sha1Context) Sha1State;
++ break;
++ case QCryptographicHash::Md4:
++ new (&md4Context) md4_context;
++ break;
++ case QCryptographicHash::Md5:
++ new (&md5Context) MD5Context;
++ break;
++ case QCryptographicHash::Sha224:
++ new (&sha224Context) SHA224Context;
++ break;
++ case QCryptographicHash::Sha256:
++ new (&sha256Context) SHA256Context;
++ break;
++ case QCryptographicHash::Sha384:
++ new (&sha384Context) SHA384Context;
++ break;
++ case QCryptographicHash::Sha512:
++ new (&sha512Context) SHA512Context;
++ break;
++ case QCryptographicHash::RealSha3_224:
++ case QCryptographicHash::Keccak_224:
++ case QCryptographicHash::RealSha3_256:
++ case QCryptographicHash::Keccak_256:
++ case QCryptographicHash::RealSha3_384:
++ case QCryptographicHash::Keccak_384:
++ case QCryptographicHash::RealSha3_512:
++ case QCryptographicHash::Keccak_512:
++ new (&sha3Context) SHA3Context;
++ break;
++ case QCryptographicHash::Blake2b_160:
++ case QCryptographicHash::Blake2b_256:
++ case QCryptographicHash::Blake2b_384:
++ case QCryptographicHash::Blake2b_512:
++ new (&blake2bContext) blake2b_state;
++ break;
++ case QCryptographicHash::Blake2s_128:
++ case QCryptographicHash::Blake2s_160:
++ case QCryptographicHash::Blake2s_224:
++ case QCryptographicHash::Blake2s_256:
++ new (&blake2sContext) blake2s_state;
++ break;
++ case QCryptographicHash::NumAlgorithms:
++ Q_UNREACHABLE();
++ }
++ reset(method);
++ }
++
++ void QCryptographicHashPrivate::State::destroy(QCryptographicHash::Algorithm)
++ {
++ static_assert(std::is_trivially_destructible_v<State>); // so nothing to do here
++ }
++ #endif // !USING_OPENSSL30
++
++ void QCryptographicHashPrivate::reset() noexcept
++ {
++ result.clear();
++ state.reset(method);
++ }
++
++ #ifdef USING_OPENSSL30
++
++ void QCryptographicHashPrivate::State::reset(QCryptographicHash::Algorithm method) noexcept
++ {
++ switch (method) {
++ case QCryptographicHash::Keccak_224:
++ case QCryptographicHash::Keccak_256:
++ case QCryptographicHash::Keccak_384:
++ case QCryptographicHash::Keccak_512:
++ sha3Init(&sha3Context, hashLengthInternal(method) * 8);
++ break;
++ case QCryptographicHash::Blake2b_160:
++ case QCryptographicHash::Blake2b_256:
++ case QCryptographicHash::Blake2b_384:
++ blake2b_init(&blake2bContext, hashLengthInternal(method));
++ break;
++ case QCryptographicHash::Blake2s_128:
++ case QCryptographicHash::Blake2s_160:
++ case QCryptographicHash::Blake2s_224:
++ blake2s_init(&blake2sContext, hashLengthInternal(method));
++ break;
++ case QCryptographicHash::Sha1:
++ case QCryptographicHash::Md4:
++ case QCryptographicHash::Md5:
++ case QCryptographicHash::Sha224:
++ case QCryptographicHash::Sha256:
++ case QCryptographicHash::Sha384:
++ case QCryptographicHash::Sha512:
++ case QCryptographicHash::RealSha3_224:
++ case QCryptographicHash::RealSha3_256:
++ case QCryptographicHash::RealSha3_384:
++ case QCryptographicHash::RealSha3_512:
++ case QCryptographicHash::Blake2b_512:
++ case QCryptographicHash::Blake2s_256:
++ evp.reset();
++ break;
++ case QCryptographicHash::NumAlgorithms:
++ Q_UNREACHABLE();
++ }
++ }
++
++ void QCryptographicHashPrivate::EVP::reset() noexcept
++ {
++ if (!initializationFailed) {
++ Q_ASSERT(context);
++ Q_ASSERT(algorithm);
++ // everything already set up - just reset the context
++ EVP_MD_CTX_reset(context.get());
++ initializationFailed = !EVP_DigestInit_ex(context.get(), algorithm.get(), nullptr);
++ }
++ // if initializationFailed first time around, it will not succeed this time, either
++ }
++
++ #else // USING_OPENSSL30
++
++ void QCryptographicHashPrivate::State::reset(QCryptographicHash::Algorithm method) noexcept
++ {
++ switch (method) {
++ case QCryptographicHash::Sha1:
++ sha1InitState(&sha1Context);
++ break;
++ case QCryptographicHash::Md4:
++ md4_init(&md4Context);
++ break;
++ case QCryptographicHash::Md5:
++ MD5Init(&md5Context);
++ break;
++ case QCryptographicHash::Sha224:
++ SHA224Reset(&sha224Context);
++ break;
++ case QCryptographicHash::Sha256:
++ SHA256Reset(&sha256Context);
++ break;
++ case QCryptographicHash::Sha384:
++ SHA384Reset(&sha384Context);
++ break;
++ case QCryptographicHash::Sha512:
++ SHA512Reset(&sha512Context);
++ break;
++ case QCryptographicHash::RealSha3_224:
++ case QCryptographicHash::Keccak_224:
++ case QCryptographicHash::RealSha3_256:
++ case QCryptographicHash::Keccak_256:
++ case QCryptographicHash::RealSha3_384:
++ case QCryptographicHash::Keccak_384:
++ case QCryptographicHash::RealSha3_512:
++ case QCryptographicHash::Keccak_512:
++ sha3Init(&sha3Context, hashLengthInternal(method) * 8);
++ break;
++ case QCryptographicHash::Blake2b_160:
++ case QCryptographicHash::Blake2b_256:
++ case QCryptographicHash::Blake2b_384:
++ case QCryptographicHash::Blake2b_512:
++ blake2b_init(&blake2bContext, hashLengthInternal(method));
++ break;
++ case QCryptographicHash::Blake2s_128:
++ case QCryptographicHash::Blake2s_160:
++ case QCryptographicHash::Blake2s_224:
++ case QCryptographicHash::Blake2s_256:
++ blake2s_init(&blake2sContext, hashLengthInternal(method));
++ break;
++ case QCryptographicHash::NumAlgorithms:
++ Q_UNREACHABLE();
++ }
++ }
++
++ #endif // USING_OPENSSL30
++
++ #if QT_DEPRECATED_SINCE(6, 4)
++ /*!
++ Adds the first \a length chars of \a data to the cryptographic
++ hash.
++
++ \deprecated [6.4]
++ Use the QByteArrayView overload instead.
++ */
++ void QCryptographicHash::addData(const char *data, qsizetype length)
++ {
++ Q_ASSERT(length >= 0);
++ addData(QByteArrayView{data, length});
++ }
++ #endif
++
++ /*!
++ Adds the characters in \a bytes to the cryptographic hash.
++
++ \note In Qt versions prior to 6.3, this function took QByteArray,
++ not QByteArrayView.
++ */
++ void QCryptographicHash::addData(QByteArrayView bytes) noexcept
++ {
++ d->addData(bytes);
++ }
++
++ void QCryptographicHashPrivate::addData(QByteArrayView bytes) noexcept
++ {
++ state.addData(method, bytes);
++ result.clear();
++ }
++
++ #ifdef USING_OPENSSL30
++
++ void QCryptographicHashPrivate::State::addData(QCryptographicHash::Algorithm method,
++ QByteArrayView bytes) noexcept
++ {
++ const char *data = bytes.data();
++ auto length = bytes.size();
++ // all functions take size_t length, so we don't need to loop around them:
++ switch (method) {
++ case QCryptographicHash::Keccak_224:
++ case QCryptographicHash::Keccak_256:
++ case QCryptographicHash::Keccak_384:
++ case QCryptographicHash::Keccak_512:
++ sha3Update(&sha3Context, reinterpret_cast<const BitSequence *>(data), uint64_t(length) * 8);
++ break;
++ case QCryptographicHash::Blake2b_160:
++ case QCryptographicHash::Blake2b_256:
++ case QCryptographicHash::Blake2b_384:
++ blake2b_update(&blake2bContext, reinterpret_cast<const uint8_t *>(data), length);
++ break;
++ case QCryptographicHash::Blake2s_128:
++ case QCryptographicHash::Blake2s_160:
++ case QCryptographicHash::Blake2s_224:
++ blake2s_update(&blake2sContext, reinterpret_cast<const uint8_t *>(data), length);
++ break;
++ case QCryptographicHash::Sha1:
++ case QCryptographicHash::Md4:
++ case QCryptographicHash::Md5:
++ case QCryptographicHash::Sha224:
++ case QCryptographicHash::Sha256:
++ case QCryptographicHash::Sha384:
++ case QCryptographicHash::Sha512:
++ case QCryptographicHash::RealSha3_224:
++ case QCryptographicHash::RealSha3_256:
++ case QCryptographicHash::RealSha3_384:
++ case QCryptographicHash::RealSha3_512:
++ case QCryptographicHash::Blake2b_512:
++ case QCryptographicHash::Blake2s_256:
++ if (!evp.initializationFailed)
++ EVP_DigestUpdate(evp.context.get(), (const unsigned char *)data, length);
++ break;
++ case QCryptographicHash::NumAlgorithms:
++ Q_UNREACHABLE();
++ }
++ }
++
++ #else // USING_OPENSSL30
++
++ void QCryptographicHashPrivate::State::addData(QCryptographicHash::Algorithm method,
++ QByteArrayView bytes) noexcept
++ {
++ const char *data = bytes.data();
++ auto length = bytes.size();
++
++ #if QT_POINTER_SIZE == 8
++ // feed the data UINT_MAX bytes at a time, as some of the methods below
++ // take a uint (of course, feeding more than 4G of data into the hashing
++ // functions will be pretty slow anyway)
++ for (auto remaining = length; remaining; remaining -= length, data += length) {
++ length = qMin(qsizetype(std::numeric_limits<uint>::max()), remaining);
++ #else
++ {
++ #endif
++ switch (method) {
++ case QCryptographicHash::Sha1:
++ sha1Update(&sha1Context, (const unsigned char *)data, length);
++ break;
++ case QCryptographicHash::Md4:
++ md4_update(&md4Context, (const unsigned char *)data, length);
++ break;
++ case QCryptographicHash::Md5:
++ MD5Update(&md5Context, (const unsigned char *)data, length);
++ break;
++ case QCryptographicHash::Sha224:
++ SHA224Input(&sha224Context, reinterpret_cast<const unsigned char *>(data), length);
++ break;
++ case QCryptographicHash::Sha256:
++ SHA256Input(&sha256Context, reinterpret_cast<const unsigned char *>(data), length);
++ break;
++ case QCryptographicHash::Sha384:
++ SHA384Input(&sha384Context, reinterpret_cast<const unsigned char *>(data), length);
++ break;
++ case QCryptographicHash::Sha512:
++ SHA512Input(&sha512Context, reinterpret_cast<const unsigned char *>(data), length);
++ break;
++ case QCryptographicHash::RealSha3_224:
++ case QCryptographicHash::Keccak_224:
++ case QCryptographicHash::RealSha3_256:
++ case QCryptographicHash::Keccak_256:
++ case QCryptographicHash::RealSha3_384:
++ case QCryptographicHash::Keccak_384:
++ case QCryptographicHash::RealSha3_512:
++ case QCryptographicHash::Keccak_512:
++ sha3Update(&sha3Context, reinterpret_cast<const BitSequence *>(data), uint64_t(length) * 8);
++ break;
++ case QCryptographicHash::Blake2b_160:
++ case QCryptographicHash::Blake2b_256:
++ case QCryptographicHash::Blake2b_384:
++ case QCryptographicHash::Blake2b_512:
++ blake2b_update(&blake2bContext, reinterpret_cast<const uint8_t *>(data), length);
++ break;
++ case QCryptographicHash::Blake2s_128:
++ case QCryptographicHash::Blake2s_160:
++ case QCryptographicHash::Blake2s_224:
++ case QCryptographicHash::Blake2s_256:
++ blake2s_update(&blake2sContext, reinterpret_cast<const uint8_t *>(data), length);
++ break;
++ case QCryptographicHash::NumAlgorithms:
++ Q_UNREACHABLE();
++ }
++ }
++ }
++ #endif // !USING_OPENSSL30
++
++ /*!
++ Reads the data from the open QIODevice \a device until it ends
++ and hashes it. Returns \c true if reading was successful.
++ \since 5.0
++ */
++ bool QCryptographicHash::addData(QIODevice *device)
++ {
++ return d->addData(device);
++ }
++
++ bool QCryptographicHashPrivate::addData(QIODevice *device)
++ {
++ if (!device->isReadable())
++ return false;
++
++ if (!device->isOpen())
++ return false;
++
++ Q_DECL_UNINITIALIZED
++ char buffer[1024];
++ qint64 length;
++
++ while ((length = device->read(buffer, sizeof(buffer))) > 0)
++ addData({buffer, qsizetype(length)}); // length always <= 1024
++
++ return device->atEnd();
++ }
++
++
++ /*!
++ Returns the final hash value.
++
++ \sa resultView(), QByteArray::toHex()
++ */
++ QByteArray QCryptographicHash::result() const
++ {
++ return resultView().toByteArray();
++ }
++
++ /*!
++ \since 6.3
++
++ Returns the final hash value.
++
++ Note that the returned view remains valid only as long as the QCryptographicHash object is
++ not modified by other means.
++
++ \sa result()
++ */
++ QByteArrayView QCryptographicHash::resultView() const noexcept
++ {
++ // resultView() is a const function, so concurrent calls are allowed; protect:
++ d->finalize();
++ // resultView() remains(!) valid even after we dropped the mutex in finalize()
++ return d->resultView();
++ }
++
++ /*!
++ \internal
++
++ Calls finalizeUnchecked(), if needed, under finalizeMutex protection.
++ */
++ void QCryptographicHashPrivate::finalize() noexcept
++ {
++ const auto lock = qt_scoped_lock(finalizeMutex);
++ // check that no other thread already finalizeUnchecked()'ed before us:
++ if (!result.isEmpty())
++ return;
++ finalizeUnchecked();
++ }
++
++ /*!
++ \internal
++
++ Must be called with finalizeMutex held (except from static hash() function,
++ where no sharing can take place).
++ */
++ void QCryptographicHashPrivate::finalizeUnchecked() noexcept
++ {
++ result.resizeForOverwrite(hashLengthInternal(method));
++ state.finalizeUnchecked(method, result);
++ }
++
++ /*!
++ \internal
++
++ Must be called with finalizeMutex held, except when called from the static
++ hash() function, where no sharing can take place.
++ */
++ QSpan<uchar> QCryptographicHashPrivate::finalizeUnchecked(QSpan<uchar> buffer) noexcept
++ {
++ buffer = buffer.first(hashLengthInternal(method));
++ state.finalizeUnchecked(method, buffer);
++ Q_ASSERT(result.size() == 0); // internal buffer wasn't used
++ return buffer;
++ }
++
++ #ifdef USING_OPENSSL30
++ void QCryptographicHashPrivate::State::finalizeUnchecked(QCryptographicHash::Algorithm method,
++ QSpan<uchar> result) noexcept
++ {
++ switch (method) {
++ case QCryptographicHash::Keccak_224:
++ case QCryptographicHash::Keccak_256:
++ case QCryptographicHash::Keccak_384:
++ case QCryptographicHash::Keccak_512: {
++ SHA3Context copy = sha3Context;
++ sha3Finish(copy, result, Sha3Variant::Keccak);
++ break;
++ }
++ case QCryptographicHash::Blake2b_160:
++ case QCryptographicHash::Blake2b_256:
++ case QCryptographicHash::Blake2b_384: {
++ const auto length = hashLengthInternal(method);
++ blake2b_state copy = blake2bContext;
++ blake2b_final(&copy, result.data(), length);
++ break;
++ }
++ case QCryptographicHash::Blake2s_128:
++ case QCryptographicHash::Blake2s_160:
++ case QCryptographicHash::Blake2s_224: {
++ const auto length = hashLengthInternal(method);
++ blake2s_state copy = blake2sContext;
++ blake2s_final(&copy, result.data(), length);
++ break;
++ }
++ case QCryptographicHash::Sha1:
++ case QCryptographicHash::Md4:
++ case QCryptographicHash::Md5:
++ case QCryptographicHash::Sha224:
++ case QCryptographicHash::Sha256:
++ case QCryptographicHash::Sha384:
++ case QCryptographicHash::Sha512:
++ case QCryptographicHash::RealSha3_224:
++ case QCryptographicHash::RealSha3_256:
++ case QCryptographicHash::RealSha3_384:
++ case QCryptographicHash::RealSha3_512:
++ case QCryptographicHash::Blake2b_512:
++ case QCryptographicHash::Blake2s_256:
++ evp.finalizeUnchecked(result);
++ break;
++ case QCryptographicHash::NumAlgorithms:
++ Q_UNREACHABLE();
++ }
++ }
++
++ void QCryptographicHashPrivate::EVP::finalizeUnchecked(QSpan<uchar> result) noexcept
++ {
++ if (!initializationFailed) {
++ EVP_MD_CTX_ptr copy = EVP_MD_CTX_ptr(EVP_MD_CTX_new());
++ EVP_MD_CTX_copy_ex(copy.get(), context.get());
++ Q_ASSERT(result.size() == EVP_MD_get_size(algorithm.get()));
++ EVP_DigestFinal_ex(copy.get(), result.data(), nullptr);
++ }
++ }
++
++ #else // USING_OPENSSL30
++
++ void QCryptographicHashPrivate::State::finalizeUnchecked(QCryptographicHash::Algorithm method,
++ QSpan<uchar> result) noexcept
++ {
++ switch (method) {
++ case QCryptographicHash::Sha1: {
++ Sha1State copy = sha1Context;
++ sha1FinalizeState(&copy);
++ sha1ToHash(&copy, result.data());
++ break;
++ }
++ case QCryptographicHash::Md4: {
++ md4_context copy = md4Context;
++ md4_final(&copy, result.data());
++ break;
++ }
++ case QCryptographicHash::Md5: {
++ MD5Context copy = md5Context;
++ MD5Final(&copy, result.data());
++ break;
++ }
++ case QCryptographicHash::Sha224: {
++ SHA224Context copy = sha224Context;
++ SHA224Result(&copy, result.data());
++ break;
++ }
++ case QCryptographicHash::Sha256: {
++ SHA256Context copy = sha256Context;
++ SHA256Result(&copy, result.data());
++ break;
++ }
++ case QCryptographicHash::Sha384: {
++ SHA384Context copy = sha384Context;
++ SHA384Result(&copy, result.data());
++ break;
++ }
++ case QCryptographicHash::Sha512: {
++ SHA512Context copy = sha512Context;
++ SHA512Result(&copy, result.data());
++ break;
++ }
++ case QCryptographicHash::RealSha3_224:
++ case QCryptographicHash::RealSha3_256:
++ case QCryptographicHash::RealSha3_384:
++ case QCryptographicHash::RealSha3_512: {
++ SHA3Context copy = sha3Context;
++ sha3Finish(copy, result, Sha3Variant::Sha3);
++ break;
++ }
++ case QCryptographicHash::Keccak_224:
++ case QCryptographicHash::Keccak_256:
++ case QCryptographicHash::Keccak_384:
++ case QCryptographicHash::Keccak_512: {
++ SHA3Context copy = sha3Context;
++ sha3Finish(copy, result, Sha3Variant::Keccak);
++ break;
++ }
++ case QCryptographicHash::Blake2b_160:
++ case QCryptographicHash::Blake2b_256:
++ case QCryptographicHash::Blake2b_384:
++ case QCryptographicHash::Blake2b_512: {
++ const auto length = hashLengthInternal(method);
++ blake2b_state copy = blake2bContext;
++ blake2b_final(&copy, result.data(), length);
++ break;
++ }
++ case QCryptographicHash::Blake2s_128:
++ case QCryptographicHash::Blake2s_160:
++ case QCryptographicHash::Blake2s_224:
++ case QCryptographicHash::Blake2s_256: {
++ const auto length = hashLengthInternal(method);
++ blake2s_state copy = blake2sContext;
++ blake2s_final(&copy, result.data(), length);
++ break;
++ }
++ case QCryptographicHash::NumAlgorithms:
++ Q_UNREACHABLE();
++ }
++ }
++ #endif // !USING_OPENSSL30
++
++ /*!
++ Returns the hash of \a data using \a method.
++
++ \note In Qt versions prior to 6.3, this function took QByteArray,
++ not QByteArrayView.
++
++ \sa hashInto()
++ */
++ QByteArray QCryptographicHash::hash(QByteArrayView data, Algorithm method)
++ {
++ QByteArray ba(hashLengthInternal(method), Qt::Uninitialized);
++ [[maybe_unused]] const auto r = hashInto(ba, data, method);
++ Q_ASSERT(r.size() == ba.size());
++ return ba;
++ }
++
++ /*!
++ \since 6.8
++ \fn QCryptographicHash::hashInto(QSpan<char> buffer, QSpan<const QByteArrayView> data, Algorithm method);
++ \fn QCryptographicHash::hashInto(QSpan<uchar> buffer, QSpan<const QByteArrayView> data, Algorithm method);
++ \fn QCryptographicHash::hashInto(QSpan<std::byte> buffer, QSpan<const QByteArrayView> data, Algorithm method);
++ \fn QCryptographicHash::hashInto(QSpan<char> buffer, QByteArrayView data, Algorithm method);
++ \fn QCryptographicHash::hashInto(QSpan<uchar> buffer, QByteArrayView data, Algorithm method);
++ \fn QCryptographicHash::hashInto(QSpan<std::byte> buffer, QByteArrayView data, Algorithm method);
++
++ Returns the hash of \a data using \a method, using \a buffer to store the result.
++
++ If \a data is a span, adds all the byte array views to the hash, in the order given.
++
++ The return value will be a sub-span of \a buffer, unless \a buffer is of
++ insufficient size, in which case a null QByteArrayView is returned.
++
++ \sa hash()
++ */
++ QByteArrayView QCryptographicHash::hashInto(QSpan<std::byte> buffer,
++ QSpan<const QByteArrayView> data,
++ Algorithm method) noexcept
++ {
++ if (buffer.size() < hashLengthInternal(method))
++ return {}; // buffer too small
++
++ Q_DECL_UNINITIALIZED
++ QCryptographicHashPrivate hash(method);
++ for (QByteArrayView part : data)
++ hash.addData(part);
++ auto span = QSpan{reinterpret_cast<uchar *>(buffer.data()), buffer.size()};
++ return hash.finalizeUnchecked(span); // no mutex needed: no-one but us has access to 'hash'
++ }
++
++ /*!
++ Returns the size of the output of the selected hash \a method in bytes.
++
++ \since 5.12
++ */
++ int QCryptographicHash::hashLength(QCryptographicHash::Algorithm method)
++ {
++ return hashLengthInternal(method);
++ }
++
++ /*!
++ Returns whether the selected algorithm \a method is supported and if
++ result() will return a value when the \a method is used.
++
++ \note OpenSSL will be responsible for providing this information when
++ used as a provider, otherwise \c true will be returned as the non-OpenSSL
++ implementation doesn't have any restrictions.
++ We return \c false if we fail to query OpenSSL.
++
++ \since 6.5
++ */
++
++
++ bool QCryptographicHash::supportsAlgorithm(QCryptographicHash::Algorithm method)
++ {
++ return QCryptographicHashPrivate::supportsAlgorithm(method);
++ }
++
++ #ifdef USING_OPENSSL30
++ bool QCryptographicHashPrivate::supportsAlgorithm(QCryptographicHash::Algorithm method)
++ {
++ // OpenSSL doesn't support Keccak*, Blake2b{160,256,384} and Blake2s{128,160,224},
++ // and these would automatically return FALSE in that case, while they are
++ // actually supported by our non-OpenSSL implementation.
++ switch (method) {
++ case QCryptographicHash::Keccak_224:
++ case QCryptographicHash::Keccak_256:
++ case QCryptographicHash::Keccak_384:
++ case QCryptographicHash::Keccak_512:
++ case QCryptographicHash::Blake2b_160:
++ case QCryptographicHash::Blake2b_256:
++ case QCryptographicHash::Blake2b_384:
++ case QCryptographicHash::Blake2s_128:
++ case QCryptographicHash::Blake2s_160:
++ case QCryptographicHash::Blake2s_224:
++ return true;
++ case QCryptographicHash::Md4:
++ if (OSSL_LIB_CTX *legacyLibCtx = legacyProviderContext()) {
++ return EVP_MD_ptr{EVP_MD_fetch(legacyLibCtx, methodToName(method), "-fips")}
++ != nullptr;
++ }
++ return false;
++ case QCryptographicHash::Sha1:
++ case QCryptographicHash::Md5:
++ case QCryptographicHash::Sha224:
++ case QCryptographicHash::Sha256:
++ case QCryptographicHash::Sha384:
++ case QCryptographicHash::Sha512:
++ case QCryptographicHash::RealSha3_224:
++ case QCryptographicHash::RealSha3_256:
++ case QCryptographicHash::RealSha3_384:
++ case QCryptographicHash::RealSha3_512:
++ case QCryptographicHash::Blake2b_512:
++ case QCryptographicHash::Blake2s_256: {
++ ensureDefaultProviderLoaded();
++
++ const char *restriction = "-fips";
++ EVP_MD_ptr algorithm = EVP_MD_ptr(EVP_MD_fetch(nullptr, methodToName(method), restriction));
++
++ return algorithm != nullptr;
++
++ }
++ case QCryptographicHash::NumAlgorithms:
++ ;
++ }
++ return false;
++
++ }
++ #else
++ bool QCryptographicHashPrivate::supportsAlgorithm(QCryptographicHash::Algorithm method)
++ {
++ switch (method) {
++ case QCryptographicHash::Sha1:
++ case QCryptographicHash::Md4:
++ case QCryptographicHash::Md5:
++ case QCryptographicHash::Sha224:
++ case QCryptographicHash::Sha256:
++ case QCryptographicHash::Sha384:
++ case QCryptographicHash::Sha512:
++ case QCryptographicHash::RealSha3_224:
++ case QCryptographicHash::Keccak_224:
++ case QCryptographicHash::RealSha3_256:
++ case QCryptographicHash::Keccak_256:
++ case QCryptographicHash::RealSha3_384:
++ case QCryptographicHash::Keccak_384:
++ case QCryptographicHash::RealSha3_512:
++ case QCryptographicHash::Keccak_512:
++ case QCryptographicHash::Blake2b_160:
++ case QCryptographicHash::Blake2b_256:
++ case QCryptographicHash::Blake2b_384:
++ case QCryptographicHash::Blake2b_512:
++ case QCryptographicHash::Blake2s_128:
++ case QCryptographicHash::Blake2s_160:
++ case QCryptographicHash::Blake2s_224:
++ case QCryptographicHash::Blake2s_256:
++ return true;
++ case QCryptographicHash::NumAlgorithms: ;
++ };
++ return false;
++ }
++ #endif // !USING_OPENSSL3
++
++ static constexpr int qt_hash_block_size(QCryptographicHash::Algorithm method)
++ {
++ switch (method) {
++ case QCryptographicHash::Sha1:
++ return SHA1_Message_Block_Size;
++ case QCryptographicHash::Md4:
++ return 64;
++ case QCryptographicHash::Md5:
++ return 64;
++ case QCryptographicHash::Sha224:
++ return SHA224_Message_Block_Size;
++ case QCryptographicHash::Sha256:
++ return SHA256_Message_Block_Size;
++ case QCryptographicHash::Sha384:
++ return SHA384_Message_Block_Size;
++ case QCryptographicHash::Sha512:
++ return SHA512_Message_Block_Size;
++ case QCryptographicHash::RealSha3_224:
++ case QCryptographicHash::Keccak_224:
++ return 144;
++ case QCryptographicHash::RealSha3_256:
++ case QCryptographicHash::Keccak_256:
++ return 136;
++ case QCryptographicHash::RealSha3_384:
++ case QCryptographicHash::Keccak_384:
++ return 104;
++ case QCryptographicHash::RealSha3_512:
++ case QCryptographicHash::Keccak_512:
++ return 72;
++ case QCryptographicHash::Blake2b_160:
++ case QCryptographicHash::Blake2b_256:
++ case QCryptographicHash::Blake2b_384:
++ case QCryptographicHash::Blake2b_512:
++ return BLAKE2B_BLOCKBYTES;
++ case QCryptographicHash::Blake2s_128:
++ case QCryptographicHash::Blake2s_160:
++ case QCryptographicHash::Blake2s_224:
++ case QCryptographicHash::Blake2s_256:
++ return BLAKE2S_BLOCKBYTES;
++ case QCryptographicHash::NumAlgorithms:
++ #if !defined(Q_CC_GNU_ONLY) || Q_CC_GNU >= 900
++ // GCC 8 has trouble with Q_UNREACHABLE() in constexpr functions
++ Q_UNREACHABLE();
++ #endif
++ break;
++ }
++ return 0;
++ }
++
++ constexpr int maxHashBlockSize()
++ {
++ int result = 0;
++ using A = QCryptographicHash::Algorithm;
++ for (int i = 0; i < A::NumAlgorithms ; ++i)
++ result = std::max(result, qt_hash_block_size(A(i)));
++ return result;
++ }
++
++ [[maybe_unused]]
++ constexpr int minHashBlockSize()
++ {
++ int result = INT_MAX;
++ using A = QCryptographicHash::Algorithm;
++ for (int i = 0; i < A::NumAlgorithms ; ++i)
++ result = std::min(result, qt_hash_block_size(A(i)));
++ return result;
++ }
++
++ [[maybe_unused]]
++ constexpr int gcdHashBlockSize()
++ {
++ int result = 0;
++ using A = QCryptographicHash::Algorithm;
++ for (int i = 0; i < A::NumAlgorithms ; ++i)
++ result = std::gcd(result, qt_hash_block_size(A(i)));
++ return result;
++ }
++
++ using HashBlock = QSmallByteArray<maxHashBlockSize()>;
++
++ static HashBlock xored(const HashBlock &block, quint8 val) noexcept
++ {
++ // some hints for the optimizer:
++ Q_ASSERT(block.size() >= minHashBlockSize());
++ Q_ASSERT(block.size() <= maxHashBlockSize());
++ Q_ASSERT(block.size() % gcdHashBlockSize() == 0);
++
++ Q_DECL_UNINITIALIZED
++ HashBlock result;
++ result.resizeForOverwrite(block.size());
++ for (qsizetype i = 0; i < block.size(); ++i)
++ result[i] = block[i] ^ val;
++ return result;
++ }
++
++ class QMessageAuthenticationCodePrivate
++ {
++ public:
++ explicit QMessageAuthenticationCodePrivate(QCryptographicHash::Algorithm m) noexcept
++ : messageHash(m)
++ {
++ }
++
++ HashBlock key;
++ QCryptographicHashPrivate messageHash;
++
++ void setKey(QByteArrayView k) noexcept;
++ void initMessageHash() noexcept;
++ void finalize();
++
++ // when not called from the static hash() function, this function needs to be
++ // called with messageHash.finalizeMutex held:
++ void finalizeUnchecked() noexcept;
++ // END functions that need to be called with finalizeMutex held
++ };
++
++ /*!
++ \internal
++
++ Transforms key \a newKey into a block-sized format and stores it in member
++ \c key.
++
++ This function assumes it can use messageHash (i.e. it's in its initial
++ state (reset() has been called)).
++ */
++ void QMessageAuthenticationCodePrivate::setKey(QByteArrayView newKey) noexcept
++ {
++ const int blockSize = qt_hash_block_size(messageHash.method);
++
++ if (newKey.size() > blockSize) {
++ messageHash.addData(newKey);
++ messageHash.finalizeUnchecked();
++ static_assert([] {
++ using A = QCryptographicHash::Algorithm;
++ for (int i = 0; i < A::NumAlgorithms; ++i) {
++ if (hashLengthInternal(A(i)) > qt_hash_block_size(A(i)))
++ return false;
++ }
++ return true;
++ }(), "this code assumes that a hash's result always fits into that hash's block size");
++ key = messageHash.result;
++ messageHash.reset();
++ } else {
++ key.assign(newKey);
++ }
++
++ if (key.size() < blockSize)
++ key.resize(blockSize, '\0');
++
++ initMessageHash();
++ }
++
++ /*!
++ \internal
++
++ Seeds messageHash from \c key.
++
++ This function assumes that messageHash is in its initial state (reset() has
++ been called).
++ */
++ void QMessageAuthenticationCodePrivate::initMessageHash() noexcept
++ {
++ messageHash.addData(xored(key, 0x36));
++ }
++
++ /*!
++ \class QMessageAuthenticationCode
++ \inmodule QtCore
++
++ \brief The QMessageAuthenticationCode class provides a way to generate
++ hash-based message authentication codes.
++
++ \since 5.1
++
++ \ingroup tools
++ \reentrant
++
++ Use the QMessageAuthenticationCode class to generate hash-based message
++ authentication codes (HMACs). The class supports all cryptographic
++ hash algorithms from \l QCryptographicHash (see also
++ \l{QCryptographicHash::Algorithm}).
++
++ To generate a message authentication code, pass a suitable hash
++ algorithm and secret key to the constructor. Then process the message
++ data by calling \l addData() one or more times. After the full
++ message has been processed, get the final authentication code
++ via the \l result() function:
++
++ \snippet qmessageauthenticationcode/main.cpp 0
++ \dots
++ \snippet qmessageauthenticationcode/main.cpp 1
++
++ For simple cases like above, you can also use the static
++ \l hash() function:
++
++ \snippet qmessageauthenticationcode/main.cpp 2
++
++
++ \note The cryptographic strength of the HMAC depends upon the
++ size of the secret key, and the security of the
++ underlying hash function.
++
++ \sa QCryptographicHash, QCryptographicHash::Algorithm
++ */
++
++ /*!
++ Constructs an object that can be used to create a cryptographic hash from data
++ using method \a method and key \a key.
++
++ //! [qba-to-qbav-6.6]
++ \note In Qt versions prior to 6.6, this function took its arguments as
++ QByteArray, not QByteArrayView. If you experience compile errors, it's
++ because your code is passing objects that are implicitly convertible to
++ QByteArray, but not QByteArrayView. Wrap the corresponding argument in
++ \c{QByteArray{~~~}} to make the cast explicit. This is backwards-compatible
++ with old Qt versions.
++ //! [qba-to-qbav-6.6]
++ */
++ QMessageAuthenticationCode::QMessageAuthenticationCode(QCryptographicHash::Algorithm method,
++ QByteArrayView key)
++ : d(new QMessageAuthenticationCodePrivate(method))
++ {
++ d->setKey(key);
++ }
++
++ /*!
++ Destroys the object.
++ */
++ QMessageAuthenticationCode::~QMessageAuthenticationCode()
++ {
++ delete d;
++ }
++
++ /*!
++ \fn QMessageAuthenticationCode::QMessageAuthenticationCode(QMessageAuthenticationCode &&other)
++
++ Move-constructs a new QMessageAuthenticationCode from \a other.
++
++ \note The moved-from object \a other is placed in a
++ partially-formed state, in which the only valid operations are
++ destruction and assignment of a new object.
++
++ \since 6.6
++ */
++
++ /*!
++ \fn QMessageAuthenticationCode &QMessageAuthenticationCode::operator=(QMessageAuthenticationCode &&other)
++
++ Move-assigns \a other to this QMessageAuthenticationCode instance.
++
++ \note The moved-from object \a other is placed in a
++ partially-formed state, in which the only valid operations are
++ destruction and assignment of a new object.
++
++ \since 6.6
++ */
++
++ /*!
++ \fn void QMessageAuthenticationCode::swap(QMessageAuthenticationCode &other)
++ \memberswap{message authentication code}
++ \since 6.6
++ */
++
++ /*!
++ Resets message data. Calling this function doesn't affect the key.
++ */
++ void QMessageAuthenticationCode::reset() noexcept
++ {
++ d->messageHash.reset();
++ d->initMessageHash();
++ }
++
++ /*!
++ Sets secret \a key. Calling this function automatically resets the object state.
++
++ For optimal performance, call this function only to \e change the active key,
++ not to set an \e initial key, as in
++
++ \code
++ QMessageAuthenticationCode mac(method);
++ mac.setKey(key); // does extra work
++ use(mac);
++ \endcode
++
++ Prefer to pass initial keys as the constructor argument:
++
++ \code
++ QMessageAuthenticationCode mac(method, key); // OK, optimal
++ use(mac);
++ \endcode
++
++ You can use std::optional to delay construction of a
++ QMessageAuthenticationCode until you know the key:
++
++ \code
++ std::optional<QMessageAuthenticationCode> mac;
++ ~~~
++ key = ~~~;
++ mac.emplace(method, key);
++ use(*mac);
++ \endcode
++
++ \include qcryptographichash.cpp {qba-to-qbav-6.6}
++ */
++ void QMessageAuthenticationCode::setKey(QByteArrayView key) noexcept
++ {
++ d->messageHash.reset();
++ d->setKey(key);
++ }
++
++ /*!
++ \overload
++ Adds the first \a length chars of \a data to the message.
++ */
++ void QMessageAuthenticationCode::addData(const char *data, qsizetype length)
++ {
++ d->messageHash.addData({data, length});
++ }
++
++ /*!
++ Adds \a data to the message.
++
++ \include qcryptographichash.cpp {qba-to-qbav-6.6}
++
++ \sa resultView(), result()
++ */
++ void QMessageAuthenticationCode::addData(QByteArrayView data) noexcept
++ {
++ d->messageHash.addData(data);
++ }
++
++ /*!
++ Reads the data from the open QIODevice \a device until it ends
++ and adds it to message. Returns \c true if reading was successful.
++
++ \note \a device must be already opened.
++ */
++ bool QMessageAuthenticationCode::addData(QIODevice *device)
++ {
++ return d->messageHash.addData(device);
++ }
++
++ /*!
++ \since 6.6
++
++ Returns the final hash value.
++
++ Note that the returned view remains valid only as long as the
++ QMessageAuthenticationCode object is not modified by other means.
++
++ \sa result()
++ */
++ QByteArrayView QMessageAuthenticationCode::resultView() const noexcept
++ {
++ d->finalize();
++ return d->messageHash.resultView();
++ }
++
++ /*!
++ Returns the final authentication code.
++
++ \sa resultView(), QByteArray::toHex()
++ */
++ QByteArray QMessageAuthenticationCode::result() const
++ {
++ return resultView().toByteArray();
++ }
++
++ void QMessageAuthenticationCodePrivate::finalize()
++ {
++ const auto lock = qt_scoped_lock(messageHash.finalizeMutex);
++ if (!messageHash.result.isEmpty())
++ return;
++ finalizeUnchecked();
++ }
++
++ void QMessageAuthenticationCodePrivate::finalizeUnchecked() noexcept
++ {
++ messageHash.finalizeUnchecked();
++ const HashResult hashedMessage = messageHash.result;
++
++ messageHash.reset();
++ messageHash.addData(xored(key, 0x5c));
++ messageHash.addData(hashedMessage);
++ messageHash.finalizeUnchecked();
++ }
++
++ /*!
++ Returns the authentication code for the message \a message using
++ the key \a key and the method \a method.
++
++ \include qcryptographichash.cpp {qba-to-qbav-6.6}
++
++ \sa hashInto()
++ */
++ QByteArray QMessageAuthenticationCode::hash(QByteArrayView message, QByteArrayView key,
++ QCryptographicHash::Algorithm method)
++ {
++ QByteArray ba(hashLengthInternal(method), Qt::Uninitialized);
++ [[maybe_unused]] const auto r = hashInto(ba, message, key, method);
++ Q_ASSERT(r.size() == ba.size());
++ return ba;
++ }
++
++ /*!
++ \since 6.8
++ \fn QMessageAuthenticationCode::hashInto(QSpan<char> buffer, QSpan<const QByteArrayView> messageParts, QByteArrayView key, QCryptographicHash::Algorithm method);
++ \fn QMessageAuthenticationCode::hashInto(QSpan<uchar> buffer, QSpan<const QByteArrayView> messageParts, QByteArrayView key, QCryptographicHash::Algorithm method);
++ \fn QMessageAuthenticationCode::hashInto(QSpan<std::byte> buffer, QSpan<const QByteArrayView> messageParts, QByteArrayView key, QCryptographicHash::Algorithm method);
++ \fn QMessageAuthenticationCode::hashInto(QSpan<char> buffer, QByteArrayView message, QByteArrayView key, QCryptographicHash::Algorithm method);
++ \fn QMessageAuthenticationCode::hashInto(QSpan<uchar> buffer, QByteArrayView message, QByteArrayView key, QCryptographicHash::Algorithm method);
++ \fn QMessageAuthenticationCode::hashInto(QSpan<std::byte> buffer, QByteArrayView message, QByteArrayView key, QCryptographicHash::Algorithm method);
++
++ Returns the authentication code for the message (\a message or, for the
++ QSpan overloads, the concatenation of \a messageParts) using the key \a key
++ and the method \a method.
++
++ The return value will be a sub-span of \a buffer, unless \a buffer is of
++ insufficient size, in which case a null QByteArrayView is returned.
++
++ \sa hash()
++ */
++ QByteArrayView QMessageAuthenticationCode::hashInto(QSpan<std::byte> buffer,
++ QSpan<const QByteArrayView> messageParts,
++ QByteArrayView key,
++ QCryptographicHash::Algorithm method) noexcept
++ {
++ Q_DECL_UNINITIALIZED
++ QMessageAuthenticationCodePrivate mac(method);
++ mac.setKey(key);
++ for (QByteArrayView part : messageParts)
++ mac.messageHash.addData(part);
++ mac.finalizeUnchecked();
++ auto result = mac.messageHash.resultView();
++ if (buffer.size() < result.size())
++ return {}; // buffer too small
++ // ### optimize: have the method directly write into `buffer`
++ memcpy(buffer.data(), result.data(), result.size());
++ return buffer.first(result.size());
++ }
++
++ QT_END_NAMESPACE
++
++ #ifndef QT_NO_QOBJECT
++ #include "moc_qcryptographichash.cpp"
++ #endif