keepassxc

Форк
0
/
HmacBlockStream.cpp 
261 строка · 6.5 Кб
1
/*
2
 *  Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
3
 *
4
 *  This program is free software: you can redistribute it and/or modify
5
 *  it under the terms of the GNU General Public License as published by
6
 *  the Free Software Foundation, either version 2 or (at your option)
7
 *  version 3 of the License.
8
 *
9
 *  This program is distributed in the hope that it will be useful,
10
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
 *  GNU General Public License for more details.
13
 *
14
 *  You should have received a copy of the GNU General Public License
15
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
 */
17

18
#include "HmacBlockStream.h"
19

20
#include "core/Endian.h"
21
#include "crypto/CryptoHash.h"
22

23
const QSysInfo::Endian HmacBlockStream::ByteOrder = QSysInfo::LittleEndian;
24

25
HmacBlockStream::HmacBlockStream(QIODevice* baseDevice, QByteArray key)
26
    : LayeredStream(baseDevice)
27
    , m_blockSize(1024 * 1024)
28
    , m_key(std::move(key))
29
{
30
    init();
31
}
32

33
HmacBlockStream::HmacBlockStream(QIODevice* baseDevice, QByteArray key, qint32 blockSize)
34
    : LayeredStream(baseDevice)
35
    , m_blockSize(blockSize)
36
    , m_key(std::move(key))
37
{
38
    init();
39
}
40

41
HmacBlockStream::~HmacBlockStream()
42
{
43
    close();
44
}
45

46
void HmacBlockStream::init()
47
{
48
    m_buffer.clear();
49
    m_bufferPos = 0;
50
    m_blockIndex = 0;
51
    m_eof = false;
52
    m_error = false;
53
}
54

55
bool HmacBlockStream::reset()
56
{
57
    // Write final block(s) only if device is writable and we haven't
58
    // already written a final block.
59
    if (isWritable() && (!m_buffer.isEmpty() || m_blockIndex != 0)) {
60
        if (!m_buffer.isEmpty() && !writeHashedBlock()) {
61
            return false;
62
        }
63

64
        // write empty final block
65
        if (!writeHashedBlock()) {
66
            return false;
67
        }
68
    }
69

70
    init();
71

72
    return true;
73
}
74

75
void HmacBlockStream::close()
76
{
77
    // Write final block(s) only if device is writable and we haven't
78
    // already written a final block.
79
    if (isWritable() && (!m_buffer.isEmpty() || m_blockIndex != 0)) {
80
        if (!m_buffer.isEmpty()) {
81
            writeHashedBlock();
82
        }
83

84
        // write empty final block
85
        writeHashedBlock();
86
    }
87

88
    LayeredStream::close();
89
}
90

91
qint64 HmacBlockStream::readData(char* data, qint64 maxSize)
92
{
93
    if (m_error) {
94
        return -1;
95
    } else if (m_eof) {
96
        return 0;
97
    }
98

99
    qint64 bytesRemaining = maxSize;
100
    qint64 offset = 0;
101

102
    while (bytesRemaining > 0) {
103
        if (m_bufferPos == m_buffer.size()) {
104
            if (!readHashedBlock()) {
105
                if (m_error) {
106
                    return -1;
107
                }
108
                return maxSize - bytesRemaining;
109
            }
110
        }
111

112
        qint64 bytesToCopy = qMin(bytesRemaining, static_cast<qint64>(m_buffer.size() - m_bufferPos));
113

114
        memcpy(data + offset, m_buffer.constData() + m_bufferPos, static_cast<size_t>(bytesToCopy));
115

116
        offset += bytesToCopy;
117
        m_bufferPos += bytesToCopy;
118
        bytesRemaining -= bytesToCopy;
119
    }
120

121
    return maxSize;
122
}
123

124
bool HmacBlockStream::readHashedBlock()
125
{
126
    if (m_eof) {
127
        return false;
128
    }
129
    QByteArray hmac = m_baseDevice->read(32);
130
    if (hmac.size() != 32) {
131
        m_error = true;
132
        setErrorString("Invalid HMAC size.");
133
        return false;
134
    }
135

136
    QByteArray blockSizeBytes = m_baseDevice->read(4);
137
    if (blockSizeBytes.size() != 4) {
138
        m_error = true;
139
        setErrorString("Invalid block size size.");
140
        return false;
141
    }
142
    auto blockSize = Endian::bytesToSizedInt<qint32>(blockSizeBytes, ByteOrder);
143
    if (blockSize < 0) {
144
        m_error = true;
145
        setErrorString("Invalid block size.");
146
        return false;
147
    }
148

149
    m_buffer = m_baseDevice->read(blockSize);
150
    if (m_buffer.size() != blockSize) {
151
        m_error = true;
152
        setErrorString("Block too short.");
153
        return false;
154
    }
155

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);
161

162
    if (hmac != hasher.result()) {
163
        m_error = true;
164
        setErrorString("Mismatch between hash and data.");
165
        return false;
166
    }
167

168
    m_bufferPos = 0;
169
    ++m_blockIndex;
170

171
    if (blockSize == 0) {
172
        m_eof = true;
173
        return false;
174
    }
175

176
    return true;
177
}
178

179
qint64 HmacBlockStream::writeData(const char* data, qint64 maxSize)
180
{
181
    Q_ASSERT(maxSize >= 0);
182

183
    if (m_error) {
184
        return 0;
185
    }
186

187
    qint64 bytesRemaining = maxSize;
188
    qint64 offset = 0;
189

190
    while (bytesRemaining > 0) {
191
        qint64 bytesToCopy = qMin(bytesRemaining, static_cast<qint64>(m_blockSize - m_buffer.size()));
192

193
        m_buffer.append(data + offset, static_cast<int>(bytesToCopy));
194

195
        offset += bytesToCopy;
196
        bytesRemaining -= bytesToCopy;
197

198
        if (m_buffer.size() == m_blockSize && !writeHashedBlock()) {
199
            if (m_error) {
200
                return -1;
201
            }
202
            return maxSize - bytesRemaining;
203
        }
204
    }
205

206
    return maxSize;
207
}
208

209
bool HmacBlockStream::writeHashedBlock()
210
{
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();
217

218
    if (m_baseDevice->write(hash) != hash.size()) {
219
        m_error = true;
220
        setErrorString(m_baseDevice->errorString());
221
        return false;
222
    }
223

224
    if (!Endian::writeSizedInt<qint32>(m_buffer.size(), m_baseDevice, ByteOrder)) {
225
        m_error = true;
226
        setErrorString(m_baseDevice->errorString());
227
        return false;
228
    }
229

230
    if (!m_buffer.isEmpty()) {
231
        if (m_baseDevice->write(m_buffer) != m_buffer.size()) {
232
            m_error = true;
233
            setErrorString(m_baseDevice->errorString());
234
            return false;
235
        }
236

237
        m_buffer.clear();
238
    }
239
    ++m_blockIndex;
240
    return true;
241
}
242

243
QByteArray HmacBlockStream::getCurrentHmacKey() const
244
{
245
    return getHmacKey(m_blockIndex, m_key);
246
}
247

248
QByteArray HmacBlockStream::getHmacKey(quint64 blockIndex, const QByteArray& key)
249
{
250
    Q_ASSERT(key.size() == 64);
251
    QByteArray indexBytes = Endian::sizedIntToBytes<quint64>(blockIndex, ByteOrder);
252
    CryptoHash hasher(CryptoHash::Sha512);
253
    hasher.addData(indexBytes);
254
    hasher.addData(key);
255
    return hasher.result();
256
}
257

258
bool HmacBlockStream::atEnd() const
259
{
260
    return m_eof;
261
}
262

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.