summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools/qcryptographichash.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/tools/qcryptographichash.cpp')
-rw-r--r--src/corelib/tools/qcryptographichash.cpp216
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(&copy, 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