18
#include "HmacBlockStream.h"
20
#include "core/Endian.h"
21
#include "crypto/CryptoHash.h"
23
const QSysInfo::Endian HmacBlockStream::ByteOrder = QSysInfo::LittleEndian;
25
HmacBlockStream::HmacBlockStream(QIODevice* baseDevice, QByteArray key)
26
: LayeredStream(baseDevice)
27
, m_blockSize(1024 * 1024)
28
, m_key(std::move(key))
33
HmacBlockStream::HmacBlockStream(QIODevice* baseDevice, QByteArray key, qint32 blockSize)
34
: LayeredStream(baseDevice)
35
, m_blockSize(blockSize)
36
, m_key(std::move(key))
41
HmacBlockStream::~HmacBlockStream()
46
void HmacBlockStream::init()
55
bool HmacBlockStream::reset()
59
if (isWritable() && (!m_buffer.isEmpty() || m_blockIndex != 0)) {
60
if (!m_buffer.isEmpty() && !writeHashedBlock()) {
65
if (!writeHashedBlock()) {
75
void HmacBlockStream::close()
79
if (isWritable() && (!m_buffer.isEmpty() || m_blockIndex != 0)) {
80
if (!m_buffer.isEmpty()) {
88
LayeredStream::close();
91
qint64 HmacBlockStream::readData(char* data, qint64 maxSize)
99
qint64 bytesRemaining = maxSize;
102
while (bytesRemaining > 0) {
103
if (m_bufferPos == m_buffer.size()) {
104
if (!readHashedBlock()) {
108
return maxSize - bytesRemaining;
112
qint64 bytesToCopy = qMin(bytesRemaining, static_cast<qint64>(m_buffer.size() - m_bufferPos));
114
memcpy(data + offset, m_buffer.constData() + m_bufferPos, static_cast<size_t>(bytesToCopy));
116
offset += bytesToCopy;
117
m_bufferPos += bytesToCopy;
118
bytesRemaining -= bytesToCopy;
124
bool HmacBlockStream::readHashedBlock()
129
QByteArray hmac = m_baseDevice->read(32);
130
if (hmac.size() != 32) {
132
setErrorString("Invalid HMAC size.");
136
QByteArray blockSizeBytes = m_baseDevice->read(4);
137
if (blockSizeBytes.size() != 4) {
139
setErrorString("Invalid block size size.");
142
auto blockSize = Endian::bytesToSizedInt<qint32>(blockSizeBytes, ByteOrder);
145
setErrorString("Invalid block size.");
149
m_buffer = m_baseDevice->read(blockSize);
150
if (m_buffer.size() != blockSize) {
152
setErrorString("Block too short.");
156
CryptoHash hasher(CryptoHash::Sha256, true);
157
hasher.setKey(getCurrentHmacKey());
158
hasher.addData(Endian::sizedIntToBytes<quint64>(m_blockIndex, ByteOrder));
159
hasher.addData(blockSizeBytes);
160
hasher.addData(m_buffer);
162
if (hmac != hasher.result()) {
164
setErrorString("Mismatch between hash and data.");
171
if (blockSize == 0) {
179
qint64 HmacBlockStream::writeData(const char* data, qint64 maxSize)
181
Q_ASSERT(maxSize >= 0);
187
qint64 bytesRemaining = maxSize;
190
while (bytesRemaining > 0) {
191
qint64 bytesToCopy = qMin(bytesRemaining, static_cast<qint64>(m_blockSize - m_buffer.size()));
193
m_buffer.append(data + offset, static_cast<int>(bytesToCopy));
195
offset += bytesToCopy;
196
bytesRemaining -= bytesToCopy;
198
if (m_buffer.size() == m_blockSize && !writeHashedBlock()) {
202
return maxSize - bytesRemaining;
209
bool HmacBlockStream::writeHashedBlock()
211
CryptoHash hasher(CryptoHash::Sha256, true);
212
hasher.setKey(getCurrentHmacKey());
213
hasher.addData(Endian::sizedIntToBytes<quint64>(m_blockIndex, ByteOrder));
214
hasher.addData(Endian::sizedIntToBytes<qint32>(m_buffer.size(), ByteOrder));
215
hasher.addData(m_buffer);
216
QByteArray hash = hasher.result();
218
if (m_baseDevice->write(hash) != hash.size()) {
220
setErrorString(m_baseDevice->errorString());
224
if (!Endian::writeSizedInt<qint32>(m_buffer.size(), m_baseDevice, ByteOrder)) {
226
setErrorString(m_baseDevice->errorString());
230
if (!m_buffer.isEmpty()) {
231
if (m_baseDevice->write(m_buffer) != m_buffer.size()) {
233
setErrorString(m_baseDevice->errorString());
243
QByteArray HmacBlockStream::getCurrentHmacKey() const
245
return getHmacKey(m_blockIndex, m_key);
248
QByteArray HmacBlockStream::getHmacKey(quint64 blockIndex, const QByteArray& key)
250
Q_ASSERT(key.size() == 64);
251
QByteArray indexBytes = Endian::sizedIntToBytes<quint64>(blockIndex, ByteOrder);
252
CryptoHash hasher(CryptoHash::Sha512);
253
hasher.addData(indexBytes);
255
return hasher.result();
258
bool HmacBlockStream::atEnd() const