24
#include "config-keepassx-tests.h"
26
#include "core/Database.h"
27
#include "core/Metadata.h"
28
#include "crypto/Crypto.h"
29
#include "crypto/CryptoHash.h"
30
#include "crypto/kdf/AesKdf.h"
31
#include "format/KeePass2Reader.h"
32
#include "format/KeePass2Writer.h"
33
#include "keys/CompositeKey.h"
34
#include "keys/FileKey.h"
35
#include "keys/PasswordKey.h"
36
#include "mock/MockChallengeResponseKey.h"
38
QTEST_GUILESS_MAIN(TestKeys)
39
Q_DECLARE_METATYPE(FileKey::Type);
41
void TestKeys::initTestCase()
43
QVERIFY(Crypto::init());
46
void TestKeys::testComposite()
48
auto compositeKey1 = QSharedPointer<CompositeKey>::create();
49
auto passwordKey1 = QSharedPointer<PasswordKey>::create();
50
auto passwordKey2 = QSharedPointer<PasswordKey>::create("test");
53
compositeKey1->addKey(passwordKey1);
54
compositeKey1->addKey(passwordKey2);
58
QByteArray transformed1;
59
QVERIFY(compositeKey1->transform(kdf, transformed1));
60
QCOMPARE(transformed1.size(), 32);
62
QScopedPointer<CompositeKey> compositeKey3(new CompositeKey());
63
QScopedPointer<CompositeKey> compositeKey4(new CompositeKey());
66
compositeKey3->addKey(QSharedPointer<PasswordKey>::create("test"));
67
compositeKey3->clear();
68
QCOMPARE(compositeKey3->rawKey(), compositeKey4->rawKey());
71
auto data = compositeKey1->serialize();
72
compositeKey3->deserialize(data);
73
QCOMPARE(compositeKey1->rawKey(), compositeKey3->rawKey());
76
void TestKeys::testFileKey()
78
QFETCH(FileKey::Type, type);
79
QFETCH(QString, keyExt);
80
QFETCH(bool, fileKeyOk);
82
QString name = QString("FileKey").append(QTest::currentDataTag());
84
KeePass2Reader reader;
86
QString dbFilename = QString("%1/%2.kdbx").arg(QString(KEEPASSX_TEST_DATA_DIR), name);
87
QString keyFilename = QString("%1/%2.%3").arg(QString(KEEPASSX_TEST_DATA_DIR), name, keyExt);
89
auto compositeKey = QSharedPointer<CompositeKey>::create();
90
auto fileKey = QSharedPointer<FileKey>::create();
92
QVERIFY(fileKey->load(keyFilename, &error) == fileKeyOk);
93
QVERIFY(error.isEmpty() == fileKeyOk);
94
QCOMPARE(fileKey->type(), type);
97
auto fileKeyNoErrorParam = QSharedPointer<FileKey>::create();
98
QVERIFY(fileKeyNoErrorParam->load(keyFilename) == fileKeyOk);
99
QCOMPARE(fileKeyNoErrorParam->type(), type);
101
QCOMPARE(fileKey->rawKey(), fileKeyNoErrorParam->rawKey());
107
QCOMPARE(fileKey->rawKey().size(), 32);
109
compositeKey->addKey(fileKey);
111
auto db = QSharedPointer<Database>::create();
112
QVERIFY(db->open(dbFilename, compositeKey, nullptr));
113
QVERIFY(!reader.hasError());
114
QCOMPARE(db->metadata()->name(), QString("%1 Database").arg(name));
118
void TestKeys::testFileKey_data()
120
QTest::addColumn<FileKey::Type>("type");
121
QTest::addColumn<QString>("keyExt");
122
QTest::addColumn<bool>("fileKeyOk");
123
QTest::newRow("Xml") << FileKey::KeePass2XML << QString("key") << true;
124
QTest::newRow("XmlBrokenBase64") << FileKey::KeePass2XML << QString("key") << false;
125
QTest::newRow("XmlV2") << FileKey::KeePass2XMLv2 << QString("keyx") << true;
126
QTest::newRow("XmlV2HashFail") << FileKey::KeePass2XMLv2 << QString("keyx") << false;
127
QTest::newRow("XmlV2BrokenHex") << FileKey::KeePass2XMLv2 << QString("keyx") << false;
128
QTest::newRow("Binary") << FileKey::FixedBinary << QString("key") << true;
129
QTest::newRow("Hex") << FileKey::FixedBinaryHex << QString("key") << true;
130
QTest::newRow("Hashed") << FileKey::Hashed << QString("key") << true;
134
void TestKeys::testCreateFileKey()
137
keyBuffer1.open(QBuffer::ReadWrite);
139
FileKey::createRandom(&keyBuffer1, 128);
140
QCOMPARE(keyBuffer1.size(), 128);
143
keyBuffer2.open(QBuffer::ReadWrite);
144
FileKey::createRandom(&keyBuffer2, 64);
145
QCOMPARE(keyBuffer2.size(), 64);
148
void TestKeys::testCreateAndOpenFileKey()
150
const QString dbName("testCreateFileKey database");
153
keyBuffer.open(QBuffer::ReadWrite);
155
FileKey::createRandom(&keyBuffer);
158
auto fileKey = QSharedPointer<FileKey>::create();
159
QVERIFY(fileKey->load(&keyBuffer));
160
auto compositeKey = QSharedPointer<CompositeKey>::create();
161
compositeKey->addKey(fileKey);
163
QScopedPointer<Database> dbOrg(new Database());
164
QVERIFY(dbOrg->setKey(compositeKey));
165
dbOrg->metadata()->setName(dbName);
168
dbBuffer.open(QBuffer::ReadWrite);
170
KeePass2Writer writer;
171
writer.writeDatabase(&dbBuffer, dbOrg.data());
172
bool writeSuccess = writer.writeDatabase(&dbBuffer, dbOrg.data());
173
if (writer.hasError()) {
174
QFAIL(writer.errorString().toUtf8().constData());
176
QVERIFY(writeSuccess);
179
KeePass2Reader reader;
180
auto dbRead = QSharedPointer<Database>::create();
181
reader.readDatabase(&dbBuffer, compositeKey, dbRead.data());
182
if (reader.hasError()) {
183
QFAIL(reader.errorString().toUtf8().constData());
186
QCOMPARE(dbRead->metadata()->name(), dbName);
189
void TestKeys::testFileKeyHash()
192
keyBuffer.open(QBuffer::ReadWrite);
194
FileKey::createRandom(&keyBuffer);
196
CryptoHash cryptoHash(CryptoHash::Sha256);
197
cryptoHash.addData(keyBuffer.data());
200
fileKey.load(&keyBuffer);
202
QCOMPARE(fileKey.rawKey(), cryptoHash.result());
205
void TestKeys::testFileKeyError()
209
const QString fileName(QString(KEEPASSX_TEST_DATA_DIR).append("/does/not/exist"));
212
result = fileKey.load(fileName, &errorMsg);
214
QVERIFY(!errorMsg.isEmpty());
217
result = FileKey::create(fileName, &errorMsg);
219
QVERIFY(!errorMsg.isEmpty());
223
void TestKeys::benchmarkTransformKey()
225
QByteArray env = qgetenv("BENCHMARK");
227
if (env.isEmpty() || env == "0" || env == "no") {
228
QSKIP("Benchmark skipped. Set env variable BENCHMARK=1 to enable.");
231
auto pwKey = QSharedPointer<PasswordKey>::create();
232
pwKey->setPassword("password");
233
auto compositeKey = QSharedPointer<CompositeKey>::create();
234
compositeKey->addKey(pwKey);
236
QByteArray seed(32, '\x4B');
245
Q_UNUSED(!compositeKey->transform(kdf, result));
249
void TestKeys::testCompositeKeyComponents()
251
auto passwordKeyEnc = QSharedPointer<PasswordKey>::create("password");
252
auto fileKeyEnc = QSharedPointer<FileKey>::create();
254
fileKeyEnc->load(QString("%1/%2").arg(QString(KEEPASSX_TEST_DATA_DIR), "FileKeyHashed.key"), &error);
255
if (!error.isEmpty()) {
256
QFAIL(qPrintable(error));
258
auto challengeResponseKeyEnc = QSharedPointer<MockChallengeResponseKey>::create(QByteArray(16, 0x10));
260
auto compositeKeyEnc = QSharedPointer<CompositeKey>::create();
261
compositeKeyEnc->addKey(passwordKeyEnc);
262
compositeKeyEnc->addKey(fileKeyEnc);
263
compositeKeyEnc->addChallengeResponseKey(challengeResponseKeyEnc);
265
auto db1 = QSharedPointer<Database>::create();
266
db1->setKey(compositeKeyEnc);
268
KeePass2Writer writer;
270
buffer.open(QBuffer::ReadWrite);
271
QVERIFY(writer.writeDatabase(&buffer, db1.data()));
274
auto db2 = QSharedPointer<Database>::create();
275
KeePass2Reader reader;
276
auto compositeKeyDec1 = QSharedPointer<CompositeKey>::create();
279
QVERIFY(!reader.readDatabase(&buffer, compositeKeyDec1, db2.data()));
280
QVERIFY(reader.hasError());
282
compositeKeyDec1->addKey(passwordKeyEnc);
284
QVERIFY(!reader.readDatabase(&buffer, compositeKeyDec1, db2.data()));
285
QVERIFY(reader.hasError());
287
compositeKeyDec1->addKey(fileKeyEnc);
289
QVERIFY(!reader.readDatabase(&buffer, compositeKeyDec1, db2.data()));
290
QVERIFY(reader.hasError());
292
compositeKeyDec1->addChallengeResponseKey(challengeResponseKeyEnc);
294
QVERIFY(reader.readDatabase(&buffer, compositeKeyDec1, db2.data()));
296
if (reader.hasError()) {
297
QFAIL(qPrintable(reader.errorString()));
301
auto compositeKeyDec2 = QSharedPointer<CompositeKey>::create();
302
compositeKeyDec2->addKey(QSharedPointer<PasswordKey>::create("wrong password"));
303
compositeKeyDec2->addKey(fileKeyEnc);
304
compositeKeyDec2->addChallengeResponseKey(challengeResponseKeyEnc);
306
QVERIFY(!reader.readDatabase(&buffer, compositeKeyDec2, db2.data()));
307
QVERIFY(reader.hasError());
309
auto compositeKeyDec3 = QSharedPointer<CompositeKey>::create();
310
compositeKeyDec3->addKey(passwordKeyEnc);
311
auto fileKeyWrong = QSharedPointer<FileKey>::create();
312
fileKeyWrong->load(QString("%1/%2").arg(QString(KEEPASSX_TEST_DATA_DIR), "FileKeyHashed2.key"), &error);
313
if (!error.isEmpty()) {
314
QFAIL(qPrintable(error));
316
compositeKeyDec3->addKey(fileKeyWrong);
317
compositeKeyDec3->addChallengeResponseKey(challengeResponseKeyEnc);
319
QVERIFY(!reader.readDatabase(&buffer, compositeKeyDec3, db2.data()));
320
QVERIFY(reader.hasError());
322
auto compositeKeyDec4 = QSharedPointer<CompositeKey>::create();
323
compositeKeyDec4->addKey(passwordKeyEnc);
324
compositeKeyDec4->addKey(fileKeyEnc);
325
compositeKeyDec4->addChallengeResponseKey(QSharedPointer<MockChallengeResponseKey>::create(QByteArray(16, 0x20)));
327
QVERIFY(!reader.readDatabase(&buffer, compositeKeyDec4, db2.data()));
328
QVERIFY(reader.hasError());