diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 99572c1bc3d..1e611fa7e96 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -285,7 +285,7 @@ qt_internal_add_module(Core tools/qmakearray_p.h tools/qmap.h tools/qmargins.cpp tools/qmargins.h - tools/qmessageauthenticationcode.cpp tools/qmessageauthenticationcode.h + tools/qmessageauthenticationcode.h tools/qoffsetstringarray_p.h tools/qpair.h tools/qpoint.cpp tools/qpoint.h diff --git a/src/corelib/tools/qcryptographichash.cpp b/src/corelib/tools/qcryptographichash.cpp index 3b1208be7ac..dbb4e0c0906 100644 --- a/src/corelib/tools/qcryptographichash.cpp +++ b/src/corelib/tools/qcryptographichash.cpp @@ -1,10 +1,14 @@ -// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2023 The Qt Company Ltd. +// Copyright (C) 2013 Ruslan Nigmatullin // Copyright (C) 2013 Richard J. Moore . // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include +#include + #include #include +#include #include #include @@ -15,6 +19,9 @@ # error "Are you sure you need the other hashing algorithms besides SHA-1?" #endif +// Header from rfc6234 +#include "../../3rdparty/rfc6234/sha.h" + #ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 #if !QT_CONFIG(opensslv30) || !QT_CONFIG(openssl_linked) // qdoc and qmake only need SHA-1 @@ -62,9 +69,6 @@ Q_CONSTINIT static SHA3Final * const sha3Final = Final; #endif -// Header from rfc6234 -#include "../../3rdparty/rfc6234/sha.h" - /* 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' @@ -925,6 +929,253 @@ bool QCryptographicHashPrivate::supportsAlgorithm(QCryptographicHash::Algorithm #endif } +static int qt_hash_block_size(QCryptographicHash::Algorithm method) +{ + switch (method) { + case QCryptographicHash::Sha1: + return SHA1_Message_Block_Size; +#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1 + 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; +#endif // QT_CRYPTOGRAPHICHASH_ONLY_SHA1 + } + return 0; +} + +class QMessageAuthenticationCodePrivate +{ +public: + QMessageAuthenticationCodePrivate(QCryptographicHash::Algorithm m) + : messageHash(m), method(m), messageHashInited(false) + { + } + + QByteArray key; + QByteArray result; + QBasicMutex finalizeMutex; + QCryptographicHash messageHash; + QCryptographicHash::Algorithm method; + bool messageHashInited; + + void initMessageHash(); + void finalize(); + + // when not called from the static hash() function, this function needs to be + // called with finalizeMutex held: + void finalizeUnchecked(); + // END functions that need to be called with finalizeMutex held +}; + +void QMessageAuthenticationCodePrivate::initMessageHash() +{ + if (messageHashInited) + return; + messageHashInited = true; + + const int blockSize = qt_hash_block_size(method); + + if (key.size() > blockSize) { + key = QCryptographicHash::hash(key, method); + } + + if (key.size() < blockSize) { + const int size = key.size(); + key.resize(blockSize); + memset(key.data() + size, 0, blockSize - size); + } + + QVarLengthArray iKeyPad(blockSize); + const char * const keyData = key.constData(); + + for (int i = 0; i < blockSize; ++i) + iKeyPad[i] = keyData[i] ^ 0x36; + + messageHash.addData(iKeyPad); +} + +/*! + \class QMessageAuthenticationCode + \inmodule QtCore + + \brief The QMessageAuthenticationCode class provides a way to generate + hash-based message authentication codes. + + \since 5.1 + + \ingroup tools + \reentrant + + QMessageAuthenticationCode supports all cryptographic hashes which are supported by + QCryptographicHash. + + 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. + \snippet qmessageauthenticationcode/main.cpp 2 + + \sa QCryptographicHash +*/ + +/*! + Constructs an object that can be used to create a cryptographic hash from data + using method \a method and key \a key. +*/ +QMessageAuthenticationCode::QMessageAuthenticationCode(QCryptographicHash::Algorithm method, + const QByteArray &key) + : d(new QMessageAuthenticationCodePrivate(method)) +{ + d->key = key; +} + +/*! + Destroys the object. +*/ +QMessageAuthenticationCode::~QMessageAuthenticationCode() +{ + delete d; +} + +/*! + Resets message data. Calling this method doesn't affect the key. +*/ +void QMessageAuthenticationCode::reset() +{ + d->result.clear(); + d->messageHash.reset(); + d->messageHashInited = false; +} + +/*! + Sets secret \a key. Calling this method automatically resets the object state. +*/ +void QMessageAuthenticationCode::setKey(const QByteArray &key) +{ + reset(); + d->key = key; +} + +/*! + Adds the first \a length chars of \a data to the message. +*/ +void QMessageAuthenticationCode::addData(const char *data, qsizetype length) +{ + d->initMessageHash(); + d->messageHash.addData({data, length}); +} + +/*! + \overload addData() +*/ +void QMessageAuthenticationCode::addData(const QByteArray &data) +{ + d->initMessageHash(); + 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) +{ + d->initMessageHash(); + return d->messageHash.addData(device); +} + +/*! + Returns the final authentication code. + + \sa QByteArray::toHex() +*/ +QByteArray QMessageAuthenticationCode::result() const +{ + d->finalize(); + return d->result; +} + +void QMessageAuthenticationCodePrivate::finalize() +{ + const auto lock = qt_scoped_lock(finalizeMutex); + if (!result.isEmpty()) + return; + initMessageHash(); + finalizeUnchecked(); +} + +void QMessageAuthenticationCodePrivate::finalizeUnchecked() +{ + const int blockSize = qt_hash_block_size(method); + + QByteArrayView hashedMessage = messageHash.resultView(); + + QVarLengthArray oKeyPad(blockSize); + const char * const keyData = key.constData(); + + for (int i = 0; i < blockSize; ++i) + oKeyPad[i] = keyData[i] ^ 0x5c; + + QCryptographicHash hash(method); + hash.addData(oKeyPad); + hash.addData(hashedMessage); + + result = hash.result(); +} + +/*! + Returns the authentication code for the message \a message using + the key \a key and the method \a method. +*/ +QByteArray QMessageAuthenticationCode::hash(const QByteArray &message, const QByteArray &key, + QCryptographicHash::Algorithm method) +{ + QMessageAuthenticationCodePrivate mac(method); + mac.key = key; + mac.initMessageHash(); + mac.messageHash.addData(message); + mac.finalizeUnchecked(); + return mac.result; +} + QT_END_NAMESPACE #ifndef QT_NO_QOBJECT diff --git a/src/corelib/tools/qmessageauthenticationcode.cpp b/src/corelib/tools/qmessageauthenticationcode.cpp deleted file mode 100644 index 4b8017953bf..00000000000 --- a/src/corelib/tools/qmessageauthenticationcode.cpp +++ /dev/null @@ -1,268 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// Copyright (C) 2013 Ruslan Nigmatullin -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only - -#include "qmessageauthenticationcode.h" -#include "qvarlengtharray.h" -#include "qmutex.h" -#include "private/qlocking_p.h" - -#include "qtcore-config_p.h" - -// Header from rfc6234 -#include "../../3rdparty/rfc6234/sha.h" - -#if QT_CONFIG(system_libb2) -#include -#else -#include "../../3rdparty/blake2/src/blake2.h" -#endif - -QT_BEGIN_NAMESPACE - -static int qt_hash_block_size(QCryptographicHash::Algorithm method) -{ - switch (method) { - case QCryptographicHash::Md4: - return 64; - case QCryptographicHash::Md5: - return 64; - case QCryptographicHash::Sha1: - return SHA1_Message_Block_Size; - 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; - } - return 0; -} - -class QMessageAuthenticationCodePrivate -{ -public: - QMessageAuthenticationCodePrivate(QCryptographicHash::Algorithm m) - : messageHash(m), method(m), messageHashInited(false) - { - } - - QByteArray key; - QByteArray result; - QBasicMutex finalizeMutex; - QCryptographicHash messageHash; - QCryptographicHash::Algorithm method; - bool messageHashInited; - - void initMessageHash(); - void finalize(); - - // when not called from the static hash() function, this function needs to be - // called with finalizeMutex held: - void finalizeUnchecked(); - // END functions that need to be called with finalizeMutex held -}; - -void QMessageAuthenticationCodePrivate::initMessageHash() -{ - if (messageHashInited) - return; - messageHashInited = true; - - const int blockSize = qt_hash_block_size(method); - - if (key.size() > blockSize) { - key = QCryptographicHash::hash(key, method); - } - - if (key.size() < blockSize) { - const int size = key.size(); - key.resize(blockSize); - memset(key.data() + size, 0, blockSize - size); - } - - QVarLengthArray iKeyPad(blockSize); - const char * const keyData = key.constData(); - - for (int i = 0; i < blockSize; ++i) - iKeyPad[i] = keyData[i] ^ 0x36; - - messageHash.addData(iKeyPad); -} - -/*! - \class QMessageAuthenticationCode - \inmodule QtCore - - \brief The QMessageAuthenticationCode class provides a way to generate - hash-based message authentication codes. - - \since 5.1 - - \ingroup tools - \reentrant - - QMessageAuthenticationCode supports all cryptographic hashes which are supported by - QCryptographicHash. - - 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. - \snippet qmessageauthenticationcode/main.cpp 2 - - \sa QCryptographicHash -*/ - -/*! - Constructs an object that can be used to create a cryptographic hash from data - using method \a method and key \a key. -*/ -QMessageAuthenticationCode::QMessageAuthenticationCode(QCryptographicHash::Algorithm method, - const QByteArray &key) - : d(new QMessageAuthenticationCodePrivate(method)) -{ - d->key = key; -} - -/*! - Destroys the object. -*/ -QMessageAuthenticationCode::~QMessageAuthenticationCode() -{ - delete d; -} - -/*! - Resets message data. Calling this method doesn't affect the key. -*/ -void QMessageAuthenticationCode::reset() -{ - d->result.clear(); - d->messageHash.reset(); - d->messageHashInited = false; -} - -/*! - Sets secret \a key. Calling this method automatically resets the object state. -*/ -void QMessageAuthenticationCode::setKey(const QByteArray &key) -{ - reset(); - d->key = key; -} - -/*! - Adds the first \a length chars of \a data to the message. -*/ -void QMessageAuthenticationCode::addData(const char *data, qsizetype length) -{ - d->initMessageHash(); - d->messageHash.addData({data, length}); -} - -/*! - \overload addData() -*/ -void QMessageAuthenticationCode::addData(const QByteArray &data) -{ - d->initMessageHash(); - 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) -{ - d->initMessageHash(); - return d->messageHash.addData(device); -} - -/*! - Returns the final authentication code. - - \sa QByteArray::toHex() -*/ -QByteArray QMessageAuthenticationCode::result() const -{ - d->finalize(); - return d->result; -} - -void QMessageAuthenticationCodePrivate::finalize() -{ - const auto lock = qt_scoped_lock(finalizeMutex); - if (!result.isEmpty()) - return; - initMessageHash(); - finalizeUnchecked(); -} - -void QMessageAuthenticationCodePrivate::finalizeUnchecked() -{ - const int blockSize = qt_hash_block_size(method); - - QByteArrayView hashedMessage = messageHash.resultView(); - - QVarLengthArray oKeyPad(blockSize); - const char * const keyData = key.constData(); - - for (int i = 0; i < blockSize; ++i) - oKeyPad[i] = keyData[i] ^ 0x5c; - - QCryptographicHash hash(method); - hash.addData(oKeyPad); - hash.addData(hashedMessage); - - result = hash.result(); -} - -/*! - Returns the authentication code for the message \a message using - the key \a key and the method \a method. -*/ -QByteArray QMessageAuthenticationCode::hash(const QByteArray &message, const QByteArray &key, - QCryptographicHash::Algorithm method) -{ - QMessageAuthenticationCodePrivate mac(method); - mac.key = key; - mac.initMessageHash(); - mac.messageHash.addData(message); - mac.finalizeUnchecked(); - return mac.result; -} - -QT_END_NAMESPACE diff --git a/src/corelib/tools/qmessageauthenticationcode.h b/src/corelib/tools/qmessageauthenticationcode.h index c1e50b56688..3951a6e5fab 100644 --- a/src/corelib/tools/qmessageauthenticationcode.h +++ b/src/corelib/tools/qmessageauthenticationcode.h @@ -12,6 +12,7 @@ QT_BEGIN_NAMESPACE class QMessageAuthenticationCodePrivate; class QIODevice; +// implemented in qcryptographichash.cpp class Q_CORE_EXPORT QMessageAuthenticationCode { public: