keepassxc

Форк
0
/
KdbxReader.cpp 
263 строки · 6.5 Кб
1

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

19
#include "KdbxReader.h"
20
#include "core/Database.h"
21
#include "core/Endian.h"
22
#include "crypto/SymmetricCipher.h"
23
#include "streams/StoreDataStream.h"
24

25
#define UUID_LENGTH 16
26

27
/**
28
 * Read KDBX magic header numbers from a device.
29
 *
30
 * Passing a null key will only read in the unprotected headers.
31
 *
32
 * @param device input device
33
 * @param sig1 KDBX signature 1
34
 * @param sig2 KDBX signature 2
35
 * @param version KDBX version
36
 * @return true if magic numbers were read successfully
37
 */
38
bool KdbxReader::readMagicNumbers(QIODevice* device, quint32& sig1, quint32& sig2, quint32& version)
39
{
40
    bool ok;
41
    sig1 = Endian::readSizedInt<quint32>(device, KeePass2::BYTEORDER, &ok);
42
    if (!ok) {
43
        return false;
44
    }
45

46
    sig2 = Endian::readSizedInt<quint32>(device, KeePass2::BYTEORDER, &ok);
47
    if (!ok) {
48
        return false;
49
    }
50

51
    version = Endian::readSizedInt<quint32>(device, KeePass2::BYTEORDER, &ok);
52

53
    return ok;
54
}
55

56
/**
57
 * Read KDBX stream from device.
58
 * The device will automatically be reset to 0 before reading.
59
 *
60
 * Passing a null key will only read in the unprotected headers.
61
 *
62
 * @param device input device
63
 * @param key database encryption composite key
64
 * @param db database to read into
65
 * @return true on success
66
 */
67
bool KdbxReader::readDatabase(QIODevice* device, QSharedPointer<const CompositeKey> key, Database* db)
68
{
69
    device->seek(0);
70

71
    m_db = db;
72
    m_masterSeed.clear();
73
    m_encryptionIV.clear();
74
    m_streamStartBytes.clear();
75
    m_protectedStreamKey.clear();
76

77
    StoreDataStream headerStream(device);
78
    headerStream.open(QIODevice::ReadOnly);
79

80
    // read KDBX magic numbers
81
    quint32 sig1, sig2, version;
82
    if (!readMagicNumbers(&headerStream, sig1, sig2, version)) {
83
        return false;
84
    }
85
    m_kdbxSignature = qMakePair(sig1, sig2);
86
    m_db->setFormatVersion(version);
87

88
    // read header fields
89
    while (readHeaderField(headerStream, m_db) && !hasError()) {
90
    }
91

92
    headerStream.close();
93

94
    if (hasError()) {
95
        return false;
96
    }
97

98
    // No key provided - don't proceed to load payload
99
    if (key.isNull()) {
100
        return true;
101
    }
102

103
    // read payload
104
    return readDatabaseImpl(device, headerStream.storedData(), std::move(key), db);
105
}
106

107
bool KdbxReader::hasError() const
108
{
109
    return m_error;
110
}
111

112
QString KdbxReader::errorString() const
113
{
114
    return m_errorStr;
115
}
116

117
KeePass2::ProtectedStreamAlgo KdbxReader::protectedStreamAlgo() const
118
{
119
    return m_irsAlgo;
120
}
121

122
/**
123
 * @param data stream cipher UUID as bytes
124
 */
125
void KdbxReader::setCipher(const QByteArray& data)
126
{
127
    if (data.size() != UUID_LENGTH) {
128
        raiseError(tr("Invalid cipher uuid length: %1 (length=%2)").arg(QString(data)).arg(data.size()));
129
        return;
130
    }
131

132
    QUuid uuid = QUuid::fromRfc4122(data);
133
    if (uuid.isNull()) {
134
        raiseError(tr("Unable to parse UUID: %1").arg(QString(data)));
135
        return;
136
    }
137

138
    if (SymmetricCipher::cipherUuidToMode(uuid) == SymmetricCipher::InvalidMode) {
139
        raiseError(tr("Unsupported cipher"));
140
        return;
141
    }
142
    m_db->setCipher(uuid);
143
}
144

145
/**
146
 * @param data compression flags as bytes
147
 */
148
void KdbxReader::setCompressionFlags(const QByteArray& data)
149
{
150
    if (data.size() != 4) {
151
        raiseError(tr("Invalid compression flags length"));
152
        return;
153
    }
154
    auto id = Endian::bytesToSizedInt<quint32>(data, KeePass2::BYTEORDER);
155

156
    if (id > Database::CompressionAlgorithmMax) {
157
        raiseError(tr("Unsupported compression algorithm"));
158
        return;
159
    }
160
    m_db->setCompressionAlgorithm(static_cast<Database::CompressionAlgorithm>(id));
161
}
162

163
/**
164
 * @param data master seed as bytes
165
 */
166
void KdbxReader::setMasterSeed(const QByteArray& data)
167
{
168
    if (data.size() != 32) {
169
        raiseError(tr("Invalid master seed size"));
170
        return;
171
    }
172
    m_masterSeed = data;
173
}
174

175
/**
176
 * @param data KDF seed as bytes
177
 */
178
void KdbxReader::setTransformSeed(const QByteArray& data)
179
{
180
    if (data.size() != 32) {
181
        raiseError(tr("Invalid transform seed size"));
182
        return;
183
    }
184

185
    auto kdf = m_db->kdf();
186
    if (!kdf.isNull()) {
187
        kdf->setSeed(data);
188
    }
189
}
190

191
/**
192
 * @param data KDF transform rounds as bytes
193
 */
194
void KdbxReader::setTransformRounds(const QByteArray& data)
195
{
196
    if (data.size() != 8) {
197
        raiseError(tr("Invalid transform rounds size"));
198
        return;
199
    }
200

201
    auto rounds = Endian::bytesToSizedInt<quint64>(data, KeePass2::BYTEORDER);
202
    auto kdf = m_db->kdf();
203
    if (!kdf.isNull()) {
204
        kdf->setRounds(static_cast<int>(rounds));
205
    }
206
}
207

208
/**
209
 * @param data cipher stream IV as bytes
210
 */
211
void KdbxReader::setEncryptionIV(const QByteArray& data)
212
{
213
    m_encryptionIV = data;
214
}
215

216
/**
217
 * @param data key for random (inner) stream as bytes
218
 */
219
void KdbxReader::setProtectedStreamKey(const QByteArray& data)
220
{
221
    m_protectedStreamKey = data;
222
}
223

224
/**
225
 * @param data start bytes for cipher stream
226
 */
227
void KdbxReader::setStreamStartBytes(const QByteArray& data)
228
{
229
    if (data.size() != 32) {
230
        raiseError(tr("Invalid start bytes size"));
231
        return;
232
    }
233
    m_streamStartBytes = data;
234
}
235

236
/**
237
 * @param data id of inner cipher stream algorithm
238
 */
239
void KdbxReader::setInnerRandomStreamID(const QByteArray& data)
240
{
241
    if (data.size() != 4) {
242
        raiseError(tr("Invalid random stream id size"));
243
        return;
244
    }
245
    auto id = Endian::bytesToSizedInt<quint32>(data, KeePass2::BYTEORDER);
246
    KeePass2::ProtectedStreamAlgo irsAlgo = KeePass2::idToProtectedStreamAlgo(id);
247
    if (irsAlgo == KeePass2::ProtectedStreamAlgo::InvalidProtectedStreamAlgo
248
        || irsAlgo == KeePass2::ProtectedStreamAlgo::ArcFourVariant) {
249
        raiseError(tr("Invalid inner random stream cipher"));
250
        return;
251
    }
252
    m_irsAlgo = irsAlgo;
253
}
254

255
/**
256
 * Raise an error. Use in case of an unexpected read error.
257
 *
258
 * @param errorMessage error message
259
 */
260
void KdbxReader::raiseError(const QString& errorMessage)
261
{
262
    m_error = true;
263
    m_errorStr = errorMessage;
264
}
265

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

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

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

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