QMessageAuthenticationCode: use QSmallByteArray for the key

Add more API to QSmallByteArray:

- const op[] overload
- copy construction and -assignment from QSmallByteArrays with smaller N
  (for implicitly converting a HashResult into a HashBlock, incl. a
  static assertion that it always fits
- STL-style assign()

This allows us to use HashBlock for the key, instead of a QByteArray.

To make-it-cool®, add a method that xor's all bytes in a
QSmallByteArray with a given value and returns the result. We use that
algorithm twice in the implementation, and the code simplification at
the call sites is amazing.

Pick-to: 6.5
Change-Id: I7fbc11538544b4a8d1771dc8ad025be154d446df
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Marc Mutz 2023-02-23 18:45:47 +01:00
parent 18aa3309a4
commit 1fb0dca140

View File

@ -127,7 +127,29 @@ class QSmallByteArray
static_assert(N <= std::numeric_limits<std::uint8_t>::max());
quint8 m_size = 0;
public:
// all SMFs are ok!
QSmallByteArray() = default;
// all compiler-generated SMFs are ok!
template <std::size_t M, std::enable_if_t<M < N, bool> = true> // M == N is for copy ctor!
QSmallByteArray(const QSmallByteArray<M> &other) noexcept
{
assign(other);
}
template <std::size_t M, std::enable_if_t<M < N, bool> = true> // M == N is for copy-assignment op!
QSmallByteArray &operator=(const QSmallByteArray<M> &other) noexcept
{
assign(other);
return *this;
}
template <typename Container> // ### underconstrained
void assign(const Container &c)
{
const size_t otherSize = size_t(std::size(c));
Q_ASSERT(otherSize < N);
memcpy(data(), std::data(c), otherSize);
m_size = quint8(otherSize);
}
quint8 *data() noexcept { return m_data.data(); }
const quint8 *data() const noexcept { return m_data.data(); }
qsizetype size() const noexcept { return qsizetype{m_size}; }
@ -136,6 +158,11 @@ public:
Q_ASSERT(n < size());
return data()[n];
}
const quint8 &operator[](qsizetype n) const
{
Q_ASSERT(n < size());
return data()[n];
}
bool isEmpty() const noexcept { return size() == 0; }
void clear() noexcept { m_size = 0; }
void resizeForOverwrite(qsizetype s)
@ -1074,6 +1101,15 @@ constexpr int maxHashBlockSize()
using HashBlock = QSmallByteArray<maxHashBlockSize()>;
static HashBlock xored(const HashBlock &block, quint8 val) noexcept
{
HashBlock result;
result.resizeForOverwrite(block.size());
for (qsizetype i = 0; i < block.size(); ++i)
result[i] = block[i] ^ val;
return result;
}
class QMessageAuthenticationCodePrivate
{
public:
@ -1082,7 +1118,7 @@ public:
{
}
QByteArray key;
HashBlock key;
HashResult result;
QBasicMutex finalizeMutex;
QCryptographicHashPrivate messageHash;
@ -1115,10 +1151,10 @@ void QMessageAuthenticationCodePrivate::setKey(const QByteArray &newKey)
messageHash.addData(newKey);
messageHash.finalizeUnchecked();
static_assert(maxHashLength() <= maxHashBlockSize());
key = messageHash.resultView().toByteArray();
key = messageHash.result;
messageHash.reset();
} else {
key = newKey;
key.assign(newKey);
}
if (key.size() < blockSize)
@ -1135,15 +1171,7 @@ void QMessageAuthenticationCodePrivate::setKey(const QByteArray &newKey)
*/
void QMessageAuthenticationCodePrivate::initMessageHash()
{
const int blockSize = qt_hash_block_size(method);
QVarLengthArray<char> iKeyPad(blockSize);
const char * const keyData = key.constData();
for (int i = 0; i < blockSize; ++i)
iKeyPad[i] = keyData[i] ^ 0x36;
messageHash.addData(iKeyPad);
messageHash.addData(xored(key, 0x36));
}
/*!
@ -1291,20 +1319,11 @@ void QMessageAuthenticationCodePrivate::finalize()
void QMessageAuthenticationCodePrivate::finalizeUnchecked() noexcept
{
const int blockSize = qt_hash_block_size(method);
messageHash.finalizeUnchecked();
const HashResult hashedMessage = messageHash.result;
HashBlock oKeyPad;
oKeyPad.resizeForOverwrite(blockSize);
const char * const keyData = key.constData();
for (int i = 0; i < blockSize; ++i)
oKeyPad[i] = keyData[i] ^ 0x5c;
messageHash.reset();
messageHash.addData(oKeyPad);
messageHash.addData(xored(key, 0x5c));
messageHash.addData(hashedMessage);
messageHash.finalizeUnchecked();