diff options
Diffstat (limited to 'src/corelib/tools/qcryptographichash.cpp')
-rw-r--r-- | src/corelib/tools/qcryptographichash.cpp | 216 |
1 files changed, 168 insertions, 48 deletions
diff --git a/src/corelib/tools/qcryptographichash.cpp b/src/corelib/tools/qcryptographichash.cpp index 2c4a2b276b..d0ed17eba2 100644 --- a/src/corelib/tools/qcryptographichash.cpp +++ b/src/corelib/tools/qcryptographichash.cpp @@ -25,12 +25,13 @@ #include "../../3rdparty/rfc6234/sha.h" #ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 -#if !QT_CONFIG(opensslv30) || !QT_CONFIG(openssl_linked) +#if !QT_CONFIG(openssl_hash) // qdoc and qmake only need SHA-1 #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; @@ -71,6 +72,7 @@ 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' @@ -114,7 +116,7 @@ static inline int SHA384_512AddLength(SHA512Context *context, unsigned int lengt #endif #endif // QT_CRYPTOGRAPHICHASH_ONLY_SHA1 -#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(opensslv30) && QT_CONFIG(openssl_linked) +#if !defined(QT_BOOTSTRAPPED) && QT_CONFIG(openssl_hash) #define USING_OPENSSL30 #include <openssl/evp.h> #include <openssl/provider.h> @@ -266,10 +268,6 @@ static constexpr const char * methodToName(QCryptographicHash::Algorithm method) CASE(RealSha3_256, "SHA3-256"); CASE(RealSha3_384, "SHA3-384"); CASE(RealSha3_512, "SHA3-512"); - CASE(Keccak_224, "SHA3-224"); - CASE(Keccak_256, "SHA3-256"); - CASE(Keccak_384, "SHA3-384"); - CASE(Keccak_512, "SHA3-512"); CASE(Blake2b_512, "BLAKE2B512"); CASE(Blake2s_256, "BLAKE2S256"); #undef CASE @@ -283,7 +281,9 @@ static constexpr const char * methodToName(QCryptographicHash::Algorithm method) */ static constexpr bool useNonOpenSSLFallback(QCryptographicHash::Algorithm method) noexcept { - if (method == QCryptographicHash::Blake2b_160 || method == QCryptographicHash::Blake2b_256 || + if (method == QCryptographicHash::Keccak_224 || method == QCryptographicHash::Keccak_256 || + method == QCryptographicHash::Keccak_384 || method == QCryptographicHash::Keccak_512 || + method == QCryptographicHash::Blake2b_160 || method == QCryptographicHash::Blake2b_256 || method == QCryptographicHash::Blake2b_384 || method == QCryptographicHash::Blake2s_128 || method == QCryptographicHash::Blake2s_160 || method == QCryptographicHash::Blake2s_224) return true; @@ -326,11 +326,20 @@ public: EVP_MD_free(md); } }; + struct OSSL_PROVIDER_deleter { + void operator()(OSSL_PROVIDER *provider) const noexcept { + OSSL_PROVIDER_unload(provider); + } + }; + 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>; + using OSSL_PROVIDER_ptr = std::unique_ptr<OSSL_PROVIDER, OSSL_PROVIDER_deleter>; struct EVP { EVP_MD_ptr algorithm; EVP_MD_CTX_ptr context; + OSSL_PROVIDER_ptr defaultProvider; + OSSL_PROVIDER_ptr legacyProvider; bool initializationFailed; explicit EVP(QCryptographicHash::Algorithm method); @@ -361,11 +370,11 @@ public: SHA256Context sha256Context; SHA384Context sha384Context; SHA512Context sha512Context; +#endif SHA3Context sha3Context; enum class Sha3Variant { Sha3, Keccak }; void sha3Finish(HashResult &result, int bitCount, Sha3Variant sha3Variant); -#endif blake2b_state blake2bContext; blake2s_state blake2sContext; #endif @@ -378,7 +387,6 @@ public: }; #ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 -#ifndef USING_OPENSSL30 void QCryptographicHashPrivate::State::sha3Finish(HashResult &result, int bitCount, Sha3Variant sha3Variant) { @@ -418,7 +426,6 @@ void QCryptographicHashPrivate::State::sha3Finish(HashResult &result, int bitCou sha3Final(©, result.data()); } -#endif // !QT_CONFIG(opensslv30) #endif /*! @@ -548,9 +555,15 @@ QCryptographicHash::Algorithm QCryptographicHash::algorithm() const noexcept QCryptographicHashPrivate::State::State(QCryptographicHash::Algorithm method) { - if (method == QCryptographicHash::Blake2b_160 || - method == QCryptographicHash::Blake2b_256 || - method == QCryptographicHash::Blake2b_384) { + if (method == QCryptographicHash::Keccak_224 || + method == QCryptographicHash::Keccak_256 || + method == QCryptographicHash::Keccak_384 || + method == QCryptographicHash::Keccak_512) { + new (&sha3Context) SHA3Context; + reset(method); + } else if (method == QCryptographicHash::Blake2b_160 || + method == QCryptographicHash::Blake2b_256 || + method == QCryptographicHash::Blake2b_384) { new (&blake2bContext) blake2b_state; reset(method); } else if (method == QCryptographicHash::Blake2s_128 || @@ -565,7 +578,11 @@ QCryptographicHashPrivate::State::State(QCryptographicHash::Algorithm method) void QCryptographicHashPrivate::State::destroy(QCryptographicHash::Algorithm method) { - if (method != QCryptographicHash::Blake2b_160 && + if (method != QCryptographicHash::Keccak_224 && + method != QCryptographicHash::Keccak_256 && + method != QCryptographicHash::Keccak_384 && + method != QCryptographicHash::Keccak_512 && + method != QCryptographicHash::Blake2b_160 && method != QCryptographicHash::Blake2b_256 && method != QCryptographicHash::Blake2b_384 && method != QCryptographicHash::Blake2s_128 && @@ -583,12 +600,16 @@ QCryptographicHashPrivate::EVP::EVP(QCryptographicHash::Algorithm method) * We need to load the legacy provider in order to have the MD4 * algorithm available. */ - if (!OSSL_PROVIDER_load(nullptr, "legacy")) - return; - if (!OSSL_PROVIDER_load(nullptr, "default")) + legacyProvider = OSSL_PROVIDER_ptr(OSSL_PROVIDER_load(nullptr, "legacy")); + + if (!legacyProvider) return; } + defaultProvider = OSSL_PROVIDER_ptr(OSSL_PROVIDER_load(nullptr, "default")); + if (!defaultProvider) + return; + context = EVP_MD_CTX_ptr(EVP_MD_CTX_new()); if (!context) { @@ -685,9 +706,14 @@ void QCryptographicHashPrivate::reset() noexcept void QCryptographicHashPrivate::State::reset(QCryptographicHash::Algorithm method) noexcept { - if (method == QCryptographicHash::Blake2b_160 || - method == QCryptographicHash::Blake2b_256 || - method == QCryptographicHash::Blake2b_384) { + if (method == QCryptographicHash::Keccak_224 || + method == QCryptographicHash::Keccak_256 || + method == QCryptographicHash::Keccak_384 || + method == QCryptographicHash::Keccak_512) { + sha3Init(&sha3Context, hashLengthInternal(method) * 8); + } else if (method == QCryptographicHash::Blake2b_160 || + method == QCryptographicHash::Blake2b_256 || + method == QCryptographicHash::Blake2b_384) { blake2b_init(&blake2bContext, hashLengthInternal(method)); } else if (method == QCryptographicHash::Blake2s_128 || method == QCryptographicHash::Blake2s_160 || @@ -813,9 +839,14 @@ void QCryptographicHashPrivate::State::addData(QCryptographicHash::Algorithm met auto length = bytes.size(); // all functions take size_t length, so we don't need to loop around them: { - if (method == QCryptographicHash::Blake2b_160 || - method == QCryptographicHash::Blake2b_256 || - method == QCryptographicHash::Blake2b_384) { + if (method == QCryptographicHash::Keccak_224 || + method == QCryptographicHash::Keccak_256 || + method == QCryptographicHash::Keccak_384 || + method == QCryptographicHash::Keccak_512) { + sha3Update(&sha3Context, reinterpret_cast<const BitSequence *>(data), uint64_t(length) * 8); + } else if (method == QCryptographicHash::Blake2b_160 || + method == QCryptographicHash::Blake2b_256 || + method == QCryptographicHash::Blake2b_384) { blake2b_update(&blake2bContext, reinterpret_cast<const uint8_t *>(data), length); } else if (method == QCryptographicHash::Blake2s_128 || method == QCryptographicHash::Blake2s_160 || @@ -987,9 +1018,14 @@ void QCryptographicHashPrivate::finalizeUnchecked() noexcept void QCryptographicHashPrivate::State::finalizeUnchecked(QCryptographicHash::Algorithm method, HashResult &result) noexcept { - if (method == QCryptographicHash::Blake2b_160 || - method == QCryptographicHash::Blake2b_256 || - method == QCryptographicHash::Blake2b_384) { + if (method == QCryptographicHash::Keccak_224 || + method == QCryptographicHash::Keccak_256 || + method == QCryptographicHash::Keccak_384 || + method == QCryptographicHash::Keccak_512) { + sha3Finish(result, 8 * hashLengthInternal(method), Sha3Variant::Keccak); + } else if (method == QCryptographicHash::Blake2b_160 || + method == QCryptographicHash::Blake2b_256 || + method == QCryptographicHash::Blake2b_384) { const auto length = hashLengthInternal(method); blake2b_state copy = blake2bContext; result.resizeForOverwrite(length); @@ -1117,13 +1153,49 @@ void QCryptographicHashPrivate::State::finalizeUnchecked(QCryptographicHash::Alg \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 +{ QCryptographicHashPrivate hash(method); - hash.addData(data); + for (QByteArrayView part : data) + hash.addData(part); hash.finalizeUnchecked(); // no mutex needed: no-one but us has access to 'hash' - return hash.resultView().toByteArray(); + auto result = hash.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()); } /*! @@ -1163,8 +1235,8 @@ bool QCryptographicHashPrivate::supportsAlgorithm(QCryptographicHash::Algorithm if (useNonOpenSSLFallback(method)) return true; - OSSL_PROVIDER_load(nullptr, "legacy"); - OSSL_PROVIDER_load(nullptr, "default"); + auto legacyProvider = OSSL_PROVIDER_ptr(OSSL_PROVIDER_load(nullptr, "legacy")); + auto defaultProvider = OSSL_PROVIDER_ptr(OSSL_PROVIDER_load(nullptr, "default")); const char *restriction = "-fips"; EVP_MD_ptr algorithm = EVP_MD_ptr(EVP_MD_fetch(nullptr, methodToName(method), restriction)); @@ -1246,7 +1318,7 @@ static constexpr int qt_hash_block_size(QCryptographicHash::Algorithm method) return BLAKE2S_BLOCKBYTES; #endif // QT_CRYPTOGRAPHICHASH_ONLY_SHA1 case QCryptographicHash::NumAlgorithms: -#if !defined(Q_GCC_ONLY) || Q_CC_GCC >= 900 +#if !defined(Q_CC_GNU_ONLY) || Q_CC_GNU >= 900 // GCC 8 has trouble with Q_UNREACHABLE() in constexpr functions Q_UNREACHABLE(); #endif @@ -1289,9 +1361,9 @@ using HashBlock = QSmallByteArray<maxHashBlockSize()>; static HashBlock xored(const HashBlock &block, quint8 val) noexcept { // some hints for the optimizer: - Q_ASSUME(block.size() >= minHashBlockSize()); - Q_ASSUME(block.size() <= maxHashBlockSize()); - Q_ASSUME(block.size() % gcdHashBlockSize() == 0); + Q_ASSERT(block.size() >= minHashBlockSize()); + Q_ASSERT(block.size() <= maxHashBlockSize()); + Q_ASSERT(block.size() % gcdHashBlockSize() == 0); HashBlock result; result.resizeForOverwrite(block.size()); for (qsizetype i = 0; i < block.size(); ++i) @@ -1302,7 +1374,7 @@ static HashBlock xored(const HashBlock &block, quint8 val) noexcept class QMessageAuthenticationCodePrivate { public: - QMessageAuthenticationCodePrivate(QCryptographicHash::Algorithm m) + explicit QMessageAuthenticationCodePrivate(QCryptographicHash::Algorithm m) noexcept : messageHash(m) { } @@ -1381,21 +1453,32 @@ void QMessageAuthenticationCodePrivate::initMessageHash() noexcept \ingroup tools \reentrant - QMessageAuthenticationCode supports all cryptographic hashes which are supported by - QCryptographicHash. + 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: - To generate message authentication code, pass hash algorithm QCryptographicHash::Algorithm - to constructor, then set key and message by setKey() and addData() functions. Result - can be acquired by result() function. \snippet qmessageauthenticationcode/main.cpp 0 \dots \snippet qmessageauthenticationcode/main.cpp 1 - Alternatively, this effect can be achieved by providing message, - key and method to hash() method. + For simple cases like above, you can also use the static + \l hash() function: + \snippet qmessageauthenticationcode/main.cpp 2 - \sa QCryptographicHash + + \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 */ /*! @@ -1460,7 +1543,7 @@ QMessageAuthenticationCode::~QMessageAuthenticationCode() */ /*! - Resets message data. Calling this method doesn't affect the key. + Resets message data. Calling this function doesn't affect the key. */ void QMessageAuthenticationCode::reset() noexcept { @@ -1469,9 +1552,9 @@ void QMessageAuthenticationCode::reset() noexcept } /*! - Sets secret \a key. Calling this method automatically resets the object state. + Sets secret \a key. Calling this function automatically resets the object state. - For optimal performance, call this method only to \e change the active key, + For optimal performance, call this function only to \e change the active key, not to set an \e initial key, as in \code @@ -1588,15 +1671,52 @@ void QMessageAuthenticationCodePrivate::finalizeUnchecked() noexcept 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 +{ QMessageAuthenticationCodePrivate mac(method); mac.setKey(key); - mac.messageHash.addData(message); + for (QByteArrayView part : messageParts) + mac.messageHash.addData(part); mac.finalizeUnchecked(); - return mac.messageHash.resultView().toByteArray(); + 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 |