keepassxc

Форк
0
/
HashedBlockStream.cpp 
257 строк · 6.1 Кб
1
/*
2
 *  Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
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 "HashedBlockStream.h"
19

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

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

25
HashedBlockStream::HashedBlockStream(QIODevice* baseDevice)
26
    : LayeredStream(baseDevice)
27
    , m_blockSize(1024 * 1024)
28
{
29
    init();
30
}
31

32
HashedBlockStream::HashedBlockStream(QIODevice* baseDevice, qint32 blockSize)
33
    : LayeredStream(baseDevice)
34
    , m_blockSize(blockSize)
35
{
36
    init();
37
}
38

39
HashedBlockStream::~HashedBlockStream()
40
{
41
    close();
42
}
43

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

53
bool HashedBlockStream::reset()
54
{
55
    // Write final block(s) only if device is writable and we haven't
56
    // already written a final block.
57
    if (isWritable() && (!m_buffer.isEmpty() || m_blockIndex != 0)) {
58
        if (!m_buffer.isEmpty()) {
59
            if (!writeHashedBlock()) {
60
                return false;
61
            }
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 HashedBlockStream::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 HashedBlockStream::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
                } else {
108
                    return maxSize - bytesRemaining;
109
                }
110
            }
111
        }
112

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

115
        memcpy(data + offset, m_buffer.constData() + m_bufferPos, bytesToCopy);
116

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

122
    return maxSize;
123
}
124

125
bool HashedBlockStream::readHashedBlock()
126
{
127
    bool ok;
128

129
    auto index = Endian::readSizedInt<quint32>(m_baseDevice, ByteOrder, &ok);
130
    if (!ok || index != m_blockIndex) {
131
        m_error = true;
132
        setErrorString("Invalid block index.");
133
        return false;
134
    }
135

136
    QByteArray hash = m_baseDevice->read(32);
137
    if (hash.size() != 32) {
138
        m_error = true;
139
        setErrorString("Invalid hash size.");
140
        return false;
141
    }
142

143
    m_blockSize = Endian::readSizedInt<qint32>(m_baseDevice, ByteOrder, &ok);
144
    if (!ok || m_blockSize < 0) {
145
        m_error = true;
146
        setErrorString("Invalid block size.");
147
        return false;
148
    }
149

150
    if (m_blockSize == 0) {
151
        if (hash.count('\0') != 32) {
152
            m_error = true;
153
            setErrorString("Invalid hash of final block.");
154
            return false;
155
        }
156

157
        m_eof = true;
158
        return false;
159
    }
160

161
    m_buffer = m_baseDevice->read(m_blockSize);
162
    if (m_buffer.size() != m_blockSize) {
163
        m_error = true;
164
        setErrorString("Block too short.");
165
        return false;
166
    }
167

168
    if (hash != CryptoHash::hash(m_buffer, CryptoHash::Sha256)) {
169
        m_error = true;
170
        setErrorString("Mismatch between hash and data.");
171
        return false;
172
    }
173

174
    m_bufferPos = 0;
175
    m_blockIndex++;
176

177
    return true;
178
}
179

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

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

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

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

194
        m_buffer.append(data + offset, bytesToCopy);
195

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

199
        if (m_buffer.size() == m_blockSize) {
200
            if (!writeHashedBlock()) {
201
                if (m_error) {
202
                    return -1;
203
                } else {
204
                    return maxSize - bytesRemaining;
205
                }
206
            }
207
        }
208
    }
209

210
    return maxSize;
211
}
212

213
bool HashedBlockStream::writeHashedBlock()
214
{
215
    if (!Endian::writeSizedInt<qint32>(m_blockIndex, m_baseDevice, ByteOrder)) {
216
        m_error = true;
217
        setErrorString(m_baseDevice->errorString());
218
        return false;
219
    }
220
    m_blockIndex++;
221

222
    QByteArray hash;
223
    if (!m_buffer.isEmpty()) {
224
        hash = CryptoHash::hash(m_buffer, CryptoHash::Sha256);
225
    } else {
226
        hash.fill(0, 32);
227
    }
228

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

235
    if (!Endian::writeSizedInt<qint32>(m_buffer.size(), m_baseDevice, ByteOrder)) {
236
        m_error = true;
237
        setErrorString(m_baseDevice->errorString());
238
        return false;
239
    }
240

241
    if (!m_buffer.isEmpty()) {
242
        if (m_baseDevice->write(m_buffer) != m_buffer.size()) {
243
            m_error = true;
244
            setErrorString(m_baseDevice->errorString());
245
            return false;
246
        }
247

248
        m_buffer.clear();
249
    }
250

251
    return true;
252
}
253

254
bool HashedBlockStream::atEnd() const
255
{
256
    return m_eof;
257
}
258

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

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

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

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