mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Refactor and extend file format tests
This commit is contained in:
parent
cdefc7ea9b
commit
a595239624
@ -32,7 +32,7 @@
|
||||
Database* Kdbx3Reader::readDatabaseImpl(QIODevice* device, const QByteArray& headerData,
|
||||
const CompositeKey& key, bool keepDatabase)
|
||||
{
|
||||
Q_ASSERT(m_kdbxVersion <= KeePass2::FILE_VERSION_3);
|
||||
Q_ASSERT(m_kdbxVersion <= KeePass2::FILE_VERSION_3_1);
|
||||
|
||||
if (hasError()) {
|
||||
return nullptr;
|
||||
@ -118,7 +118,7 @@ Database* Kdbx3Reader::readDatabaseImpl(QIODevice* device, const QByteArray& hea
|
||||
|
||||
Q_ASSERT(xmlDevice);
|
||||
|
||||
KdbxXmlReader xmlReader(KeePass2::FILE_VERSION_3);
|
||||
KdbxXmlReader xmlReader(KeePass2::FILE_VERSION_3_1);
|
||||
xmlReader.readDatabase(xmlDevice, m_db.data(), &randomStream);
|
||||
|
||||
if (xmlReader.hasError()) {
|
||||
@ -129,7 +129,7 @@ Database* Kdbx3Reader::readDatabaseImpl(QIODevice* device, const QByteArray& hea
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Q_ASSERT(!xmlReader.headerHash().isEmpty() || m_kdbxVersion < KeePass2::FILE_VERSION_3);
|
||||
Q_ASSERT(!xmlReader.headerHash().isEmpty() || m_kdbxVersion < KeePass2::FILE_VERSION_3_1);
|
||||
|
||||
if (!xmlReader.headerHash().isEmpty()) {
|
||||
QByteArray headerHash = CryptoHash::hash(headerData, CryptoHash::Sha256);
|
||||
|
@ -63,7 +63,7 @@ bool Kdbx3Writer::writeDatabase(QIODevice* device, Database* db)
|
||||
QBuffer header;
|
||||
header.open(QIODevice::WriteOnly);
|
||||
|
||||
writeMagicNumbers(&header, KeePass2::SIGNATURE_1, KeePass2::SIGNATURE_2, KeePass2::FILE_VERSION_3);
|
||||
writeMagicNumbers(&header, KeePass2::SIGNATURE_1, KeePass2::SIGNATURE_2, KeePass2::FILE_VERSION_3_1);
|
||||
|
||||
CHECK_RETURN_FALSE(writeHeaderField<quint16>(&header, KeePass2::HeaderFieldID::CipherID, db->cipher().toByteArray()));
|
||||
CHECK_RETURN_FALSE(writeHeaderField<quint16>(&header, KeePass2::HeaderFieldID::CompressionFlags,
|
||||
@ -131,7 +131,7 @@ bool Kdbx3Writer::writeDatabase(QIODevice* device, Database* db)
|
||||
return false;
|
||||
}
|
||||
|
||||
KdbxXmlWriter xmlWriter(KeePass2::FILE_VERSION_3);
|
||||
KdbxXmlWriter xmlWriter(KeePass2::FILE_VERSION_3_1);
|
||||
xmlWriter.writeDatabase(outputDevice, db, &randomStream, headerHash);
|
||||
|
||||
// Explicitly close/reset streams so they are flushed and we can detect
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <QMap>
|
||||
#include <QVariantMap>
|
||||
#include <QList>
|
||||
#include <QSharedPointer>
|
||||
|
||||
#include "crypto/SymmetricCipher.h"
|
||||
#include "crypto/kdf/Kdf.h"
|
||||
@ -29,104 +30,108 @@
|
||||
|
||||
namespace KeePass2
|
||||
{
|
||||
const quint32 SIGNATURE_1 = 0x9AA2D903;
|
||||
const quint32 SIGNATURE_2 = 0xB54BFB67;
|
||||
|
||||
const quint32 FILE_VERSION_MIN = 0x00020000;
|
||||
const quint32 FILE_VERSION_CRITICAL_MASK = 0xFFFF0000;
|
||||
const quint32 FILE_VERSION_4 = 0x00040000;
|
||||
const quint32 FILE_VERSION_3 = 0x00030001;
|
||||
constexpr quint32 SIGNATURE_1 = 0x9AA2D903;
|
||||
constexpr quint32 SIGNATURE_2 = 0xB54BFB67;
|
||||
|
||||
const quint16 VARIANTMAP_VERSION = 0x0100;
|
||||
const quint16 VARIANTMAP_CRITICAL_MASK = 0xFF00;
|
||||
constexpr quint32 FILE_VERSION_CRITICAL_MASK = 0xFFFF0000;
|
||||
constexpr quint32 FILE_VERSION_4 = 0x00040000;
|
||||
constexpr quint32 FILE_VERSION_3_1 = 0x00030001;
|
||||
constexpr quint32 FILE_VERSION_3 = 0x00030000;
|
||||
constexpr quint32 FILE_VERSION_2 = 0x00020000;
|
||||
constexpr quint32 FILE_VERSION_MIN = FILE_VERSION_2;
|
||||
|
||||
const QSysInfo::Endian BYTEORDER = QSysInfo::LittleEndian;
|
||||
constexpr quint16 VARIANTMAP_VERSION = 0x0100;
|
||||
constexpr quint16 VARIANTMAP_CRITICAL_MASK = 0xFF00;
|
||||
|
||||
extern const Uuid CIPHER_AES;
|
||||
extern const Uuid CIPHER_TWOFISH;
|
||||
extern const Uuid CIPHER_CHACHA20;
|
||||
const QSysInfo::Endian BYTEORDER = QSysInfo::LittleEndian;
|
||||
|
||||
extern const Uuid KDF_AES_KDBX3;
|
||||
extern const Uuid KDF_AES_KDBX4;
|
||||
extern const Uuid KDF_ARGON2;
|
||||
extern const Uuid CIPHER_AES;
|
||||
extern const Uuid CIPHER_TWOFISH;
|
||||
extern const Uuid CIPHER_CHACHA20;
|
||||
|
||||
extern const QByteArray INNER_STREAM_SALSA20_IV;
|
||||
extern const Uuid KDF_AES_KDBX3;
|
||||
extern const Uuid KDF_AES_KDBX4;
|
||||
extern const Uuid KDF_ARGON2;
|
||||
|
||||
extern const QString KDFPARAM_UUID;
|
||||
extern const QString KDFPARAM_AES_ROUNDS;
|
||||
extern const QString KDFPARAM_AES_SEED;
|
||||
extern const QString KDFPARAM_ARGON2_SALT;
|
||||
extern const QString KDFPARAM_ARGON2_PARALLELISM;
|
||||
extern const QString KDFPARAM_ARGON2_MEMORY;
|
||||
extern const QString KDFPARAM_ARGON2_ITERATIONS;
|
||||
extern const QString KDFPARAM_ARGON2_VERSION;
|
||||
extern const QString KDFPARAM_ARGON2_SECRET;
|
||||
extern const QString KDFPARAM_ARGON2_ASSOCDATA;
|
||||
extern const QByteArray INNER_STREAM_SALSA20_IV;
|
||||
|
||||
extern const QList<QPair<Uuid, QString>> CIPHERS;
|
||||
extern const QList<QPair<Uuid, QString>> KDFS;
|
||||
extern const QString KDFPARAM_UUID;
|
||||
extern const QString KDFPARAM_AES_ROUNDS;
|
||||
extern const QString KDFPARAM_AES_SEED;
|
||||
extern const QString KDFPARAM_ARGON2_SALT;
|
||||
extern const QString KDFPARAM_ARGON2_PARALLELISM;
|
||||
extern const QString KDFPARAM_ARGON2_MEMORY;
|
||||
extern const QString KDFPARAM_ARGON2_ITERATIONS;
|
||||
extern const QString KDFPARAM_ARGON2_VERSION;
|
||||
extern const QString KDFPARAM_ARGON2_SECRET;
|
||||
extern const QString KDFPARAM_ARGON2_ASSOCDATA;
|
||||
|
||||
enum class HeaderFieldID
|
||||
{
|
||||
EndOfHeader = 0,
|
||||
Comment = 1,
|
||||
CipherID = 2,
|
||||
CompressionFlags = 3,
|
||||
MasterSeed = 4,
|
||||
TransformSeed = 5,
|
||||
TransformRounds = 6,
|
||||
EncryptionIV = 7,
|
||||
ProtectedStreamKey = 8,
|
||||
StreamStartBytes = 9,
|
||||
InnerRandomStreamID = 10,
|
||||
KdfParameters = 11,
|
||||
PublicCustomData = 12
|
||||
};
|
||||
extern const QList<QPair<Uuid, QString>> CIPHERS;
|
||||
extern const QList<QPair<Uuid, QString>> KDFS;
|
||||
|
||||
enum class InnerHeaderFieldID : quint8
|
||||
{
|
||||
End = 0,
|
||||
InnerRandomStreamID = 1,
|
||||
InnerRandomStreamKey = 2,
|
||||
Binary = 3
|
||||
};
|
||||
enum class HeaderFieldID
|
||||
{
|
||||
EndOfHeader = 0,
|
||||
Comment = 1,
|
||||
CipherID = 2,
|
||||
CompressionFlags = 3,
|
||||
MasterSeed = 4,
|
||||
TransformSeed = 5,
|
||||
TransformRounds = 6,
|
||||
EncryptionIV = 7,
|
||||
ProtectedStreamKey = 8,
|
||||
StreamStartBytes = 9,
|
||||
InnerRandomStreamID = 10,
|
||||
KdfParameters = 11,
|
||||
PublicCustomData = 12
|
||||
};
|
||||
|
||||
enum class ProtectedStreamAlgo
|
||||
{
|
||||
ArcFourVariant = 1,
|
||||
Salsa20 = 2,
|
||||
ChaCha20 = 3,
|
||||
InvalidProtectedStreamAlgo = -1
|
||||
};
|
||||
enum class InnerHeaderFieldID : quint8
|
||||
{
|
||||
End = 0,
|
||||
InnerRandomStreamID = 1,
|
||||
InnerRandomStreamKey = 2,
|
||||
Binary = 3
|
||||
};
|
||||
|
||||
enum class VariantMapFieldType : quint8
|
||||
{
|
||||
End = 0,
|
||||
// Byte = 0x02,
|
||||
// UInt16 = 0x03,
|
||||
UInt32 = 0x04,
|
||||
UInt64 = 0x05,
|
||||
// Signed mask: 0x08
|
||||
Bool = 0x08,
|
||||
// SByte = 0x0A,
|
||||
// Int16 = 0x0B,
|
||||
Int32 = 0x0C,
|
||||
Int64 = 0x0D,
|
||||
// Float = 0x10,
|
||||
// Double = 0x11,
|
||||
// Decimal = 0x12,
|
||||
// Char = 0x17, // 16-bit Unicode character
|
||||
String = 0x18,
|
||||
// Array mask: 0x40
|
||||
ByteArray = 0x42
|
||||
};
|
||||
enum class ProtectedStreamAlgo
|
||||
{
|
||||
ArcFourVariant = 1,
|
||||
Salsa20 = 2,
|
||||
ChaCha20 = 3,
|
||||
InvalidProtectedStreamAlgo = -1
|
||||
};
|
||||
|
||||
QByteArray hmacKey(QByteArray masterSeed, QByteArray transformedMasterKey);
|
||||
QSharedPointer<Kdf> kdfFromParameters(const QVariantMap& p);
|
||||
QVariantMap kdfToParameters(QSharedPointer<Kdf> kdf);
|
||||
QSharedPointer<Kdf> uuidToKdf(const Uuid& uuid);
|
||||
Uuid kdfToUuid(QSharedPointer<Kdf> kdf);
|
||||
ProtectedStreamAlgo idToProtectedStreamAlgo(quint32 id);
|
||||
}
|
||||
enum class VariantMapFieldType : quint8
|
||||
{
|
||||
End = 0,
|
||||
// Byte = 0x02,
|
||||
// UInt16 = 0x03,
|
||||
UInt32 = 0x04,
|
||||
UInt64 = 0x05,
|
||||
// Signed mask: 0x08
|
||||
Bool = 0x08,
|
||||
// SByte = 0x0A,
|
||||
// Int16 = 0x0B,
|
||||
Int32 = 0x0C,
|
||||
Int64 = 0x0D,
|
||||
// Float = 0x10,
|
||||
// Double = 0x11,
|
||||
// Decimal = 0x12,
|
||||
// Char = 0x17, // 16-bit Unicode character
|
||||
String = 0x18,
|
||||
// Array mask: 0x40
|
||||
ByteArray = 0x42
|
||||
};
|
||||
|
||||
QByteArray hmacKey(QByteArray masterSeed, QByteArray transformedMasterKey);
|
||||
QSharedPointer<Kdf> kdfFromParameters(const QVariantMap& p);
|
||||
QVariantMap kdfToParameters(QSharedPointer<Kdf> kdf);
|
||||
QSharedPointer<Kdf> uuidToKdf(const Uuid& uuid);
|
||||
Uuid kdfToUuid(QSharedPointer<Kdf> kdf);
|
||||
ProtectedStreamAlgo idToProtectedStreamAlgo(quint32 id);
|
||||
|
||||
} // namespace KeePass2
|
||||
|
||||
#endif // KEEPASSX_KEEPASS2_H
|
||||
|
@ -79,7 +79,7 @@ KeePass2Repair::RepairOutcome KeePass2Repair::repairDatabase(QIODevice* device,
|
||||
QBuffer buffer(&xmlData);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
if ((reader.version() & KeePass2::FILE_VERSION_CRITICAL_MASK) < KeePass2::FILE_VERSION_4) {
|
||||
KdbxXmlReader xmlReader(KeePass2::FILE_VERSION_3);
|
||||
KdbxXmlReader xmlReader(KeePass2::FILE_VERSION_3_1);
|
||||
xmlReader.readDatabase(&buffer, db.data(), &randomStream);
|
||||
hasError = xmlReader.hasError();
|
||||
} else {
|
||||
|
@ -54,7 +54,7 @@ bool KeePass2Writer::writeDatabase(QIODevice* device, Database* db) {
|
||||
|
||||
// determine KDBX3 vs KDBX4
|
||||
if (db->kdf()->uuid() == KeePass2::KDF_AES_KDBX3 && db->publicCustomData().isEmpty()) {
|
||||
m_version = KeePass2::FILE_VERSION_3;
|
||||
m_version = KeePass2::FILE_VERSION_3_1;
|
||||
m_writer.reset(new Kdbx3Writer());
|
||||
} else {
|
||||
m_version = KeePass2::FILE_VERSION_4;
|
||||
|
@ -107,21 +107,18 @@ endif()
|
||||
add_unit_test(NAME testgroup SOURCES TestGroup.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
|
||||
add_unit_test(NAME testkdbx3 SOURCES TestKeePass2XmlReader.cpp TestKdbx3.cpp
|
||||
add_unit_test(NAME testkdbx2 SOURCES TestKdbx2.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
|
||||
add_unit_test(NAME testkdbx4 SOURCES TestKeePass2XmlReader.cpp TestKdbx4.cpp
|
||||
add_unit_test(NAME testkdbx3 SOURCES TestKeePass2Format.cpp FailDevice.cpp TestKdbx3.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
|
||||
add_unit_test(NAME testkdbx4 SOURCES TestKeePass2Format.cpp FailDevice.cpp TestKdbx4.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
|
||||
add_unit_test(NAME testkeys SOURCES TestKeys.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
|
||||
add_unit_test(NAME testkeepass2reader SOURCES TestKeePass2Reader.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
|
||||
add_unit_test(NAME testkeepass2writer SOURCES TestKeePass2Writer.cpp
|
||||
LIBS testsupport ${TEST_LIBRARIES})
|
||||
|
||||
add_unit_test(NAME testgroupmodel SOURCES TestGroupModel.cpp
|
||||
LIBS testsupport ${TEST_LIBRARIES})
|
||||
|
||||
|
@ -89,7 +89,7 @@ void TestDeletedObjects::createAndDelete(Database* db, int delObjectsSize)
|
||||
|
||||
void TestDeletedObjects::testDeletedObjectsFromFile()
|
||||
{
|
||||
KdbxXmlReader reader(KeePass2::FILE_VERSION_3);
|
||||
KdbxXmlReader reader(KeePass2::FILE_VERSION_3_1);
|
||||
reader.setStrictMode(true);
|
||||
QString xmlFile = QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.xml");
|
||||
Database* db = reader.readDatabase(xmlFile);
|
||||
|
68
tests/TestKdbx2.cpp
Normal file
68
tests/TestKdbx2.cpp
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TestKdbx2.h"
|
||||
#include "crypto/Crypto.h"
|
||||
#include "keys/CompositeKey.h"
|
||||
#include "keys/PasswordKey.h"
|
||||
#include "format/KeePass2Reader.h"
|
||||
#include "core/Entry.h"
|
||||
#include "core/Group.h"
|
||||
#include "core/Metadata.h"
|
||||
#include "config-keepassx-tests.h"
|
||||
|
||||
#include <QTest>
|
||||
|
||||
QTEST_GUILESS_MAIN(TestKdbx2)
|
||||
|
||||
void TestKdbx2::initTestCase()
|
||||
{
|
||||
QVERIFY(Crypto::init());
|
||||
}
|
||||
|
||||
void TestKdbx2::testFormat200()
|
||||
{
|
||||
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Format200.kdbx");
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey("a"));
|
||||
KeePass2Reader reader;
|
||||
QScopedPointer<Database> db(reader.readDatabase(filename, key));
|
||||
QCOMPARE(reader.version(), KeePass2::FILE_VERSION_2);
|
||||
QVERIFY(db.data());
|
||||
QVERIFY(!reader.hasError());
|
||||
|
||||
QCOMPARE(db->rootGroup()->name(), QString("Format200"));
|
||||
QVERIFY(!db->metadata()->protectTitle());
|
||||
QVERIFY(db->metadata()->protectUsername());
|
||||
QVERIFY(!db->metadata()->protectPassword());
|
||||
QVERIFY(db->metadata()->protectUrl());
|
||||
QVERIFY(!db->metadata()->protectNotes());
|
||||
|
||||
QCOMPARE(db->rootGroup()->entries().size(), 1);
|
||||
auto entry = db->rootGroup()->entries().at(0);
|
||||
|
||||
QCOMPARE(entry->title(), QString("Sample Entry"));
|
||||
QCOMPARE(entry->username(), QString("User Name"));
|
||||
QCOMPARE(entry->attachments()->keys().size(), 2);
|
||||
QCOMPARE(entry->attachments()->value("myattach.txt"), QByteArray("abcdefghijk"));
|
||||
QCOMPARE(entry->attachments()->value("test.txt"), QByteArray("this is a test"));
|
||||
|
||||
QCOMPARE(entry->historyItems().size(), 2);
|
||||
QCOMPARE(entry->historyItems().at(0)->attachments()->keys().size(), 0);
|
||||
QCOMPARE(entry->historyItems().at(1)->attachments()->keys().size(), 1);
|
||||
QCOMPARE(entry->historyItems().at(1)->attachments()->value("myattach.txt"), QByteArray("abcdefghijk"));
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
|
||||
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -15,24 +15,18 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSX_TESTKEEPASS2READER_H
|
||||
#define KEEPASSX_TESTKEEPASS2READER_H
|
||||
#ifndef KEEPASSXC_TEST_KDBX2_H
|
||||
#define KEEPASSXC_TEST_KDBX2_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class TestKeePass2Reader : public QObject
|
||||
class TestKdbx2 : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void testNonAscii();
|
||||
void testCompressed();
|
||||
void testProtectedStrings();
|
||||
void testBrokenHeaderHash();
|
||||
void testFormat200();
|
||||
void testFormat300();
|
||||
void testFormat400();
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_TESTKEEPASS2READER_H
|
||||
#endif // KEEPASSXC_TEST_KDBX2_H
|
@ -17,31 +17,27 @@
|
||||
|
||||
#include "TestKdbx3.h"
|
||||
#include "core/Metadata.h"
|
||||
#include "crypto/Crypto.h"
|
||||
#include "keys/PasswordKey.h"
|
||||
#include "format/KeePass2.h"
|
||||
#include "format/KeePass2Reader.h"
|
||||
#include "format/KeePass2Writer.h"
|
||||
#include "format/KdbxXmlReader.h"
|
||||
#include "format/KdbxXmlWriter.h"
|
||||
#include "format/KeePass2Repair.h"
|
||||
#include "config-keepassx-tests.h"
|
||||
|
||||
#include <QTest>
|
||||
|
||||
QTEST_GUILESS_MAIN(TestKdbx3)
|
||||
|
||||
void TestKdbx3::initTestCase()
|
||||
void TestKdbx3::initTestCaseImpl()
|
||||
{
|
||||
QVERIFY(Crypto::init());
|
||||
|
||||
KdbxXmlReader reader(KeePass2::FILE_VERSION_3);
|
||||
reader.setStrictMode(true);
|
||||
QString xmlFile = QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.xml");
|
||||
m_db.reset(reader.readDatabase(xmlFile));
|
||||
QVERIFY(m_db.data());
|
||||
QVERIFY(!reader.hasError());
|
||||
}
|
||||
|
||||
Database* TestKdbx3::readDatabase(QString path, bool strictMode, bool& hasError, QString& errorString)
|
||||
Database* TestKdbx3::readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString)
|
||||
{
|
||||
KdbxXmlReader reader(KeePass2::FILE_VERSION_3);
|
||||
KdbxXmlReader reader(KeePass2::FILE_VERSION_3_1);
|
||||
reader.setStrictMode(strictMode);
|
||||
auto db = reader.readDatabase(path);
|
||||
hasError = reader.hasError();
|
||||
@ -49,9 +45,9 @@ Database* TestKdbx3::readDatabase(QString path, bool strictMode, bool& hasError,
|
||||
return db;
|
||||
}
|
||||
|
||||
Database* TestKdbx3::readDatabase(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString)
|
||||
Database* TestKdbx3::readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString)
|
||||
{
|
||||
KdbxXmlReader reader(KeePass2::FILE_VERSION_3);
|
||||
KdbxXmlReader reader(KeePass2::FILE_VERSION_3_1);
|
||||
reader.setStrictMode(strictMode);
|
||||
auto db = reader.readDatabase(buf);
|
||||
hasError = reader.hasError();
|
||||
@ -59,10 +55,155 @@ Database* TestKdbx3::readDatabase(QBuffer* buf, bool strictMode, bool& hasError,
|
||||
return db;
|
||||
}
|
||||
|
||||
void TestKdbx3::writeDatabase(QBuffer* buf, Database* db, bool& hasError, QString& errorString)
|
||||
void TestKdbx3::writeXml(QBuffer* buf, Database* db, bool& hasError, QString& errorString)
|
||||
{
|
||||
KdbxXmlWriter writer(KeePass2::FILE_VERSION_3);
|
||||
KdbxXmlWriter writer(KeePass2::FILE_VERSION_3_1);
|
||||
writer.writeDatabase(buf, db);
|
||||
hasError = writer.hasError();
|
||||
errorString = writer.errorString();
|
||||
}
|
||||
|
||||
void TestKdbx3::readKdbx(QIODevice* device, CompositeKey const& key, QScopedPointer<Database>& db,
|
||||
bool& hasError, QString& errorString)
|
||||
{
|
||||
KeePass2Reader reader;
|
||||
db.reset(reader.readDatabase(device, key));
|
||||
hasError = reader.hasError();
|
||||
if (hasError) {
|
||||
errorString = reader.errorString();
|
||||
}
|
||||
QCOMPARE(reader.version(), KeePass2::FILE_VERSION_3_1 & KeePass2::FILE_VERSION_CRITICAL_MASK);
|
||||
}
|
||||
|
||||
void TestKdbx3::readKdbx(const QString& path, CompositeKey const& key, QScopedPointer<Database>& db,
|
||||
bool& hasError, QString& errorString)
|
||||
{
|
||||
KeePass2Reader reader;
|
||||
db.reset(reader.readDatabase(path, key));
|
||||
hasError = reader.hasError();
|
||||
if (hasError) {
|
||||
errorString = reader.errorString();
|
||||
}
|
||||
QCOMPARE(reader.version(), KeePass2::FILE_VERSION_3_1 & KeePass2::FILE_VERSION_CRITICAL_MASK);
|
||||
}
|
||||
|
||||
void TestKdbx3::writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString)
|
||||
{
|
||||
KeePass2Writer writer;
|
||||
hasError = writer.writeDatabase(device, db);
|
||||
hasError = writer.hasError();
|
||||
if (hasError) {
|
||||
errorString = writer.errorString();
|
||||
}
|
||||
QCOMPARE(writer.version(), KeePass2::FILE_VERSION_3_1);
|
||||
}
|
||||
|
||||
void TestKdbx3::testFormat300()
|
||||
{
|
||||
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Format300.kdbx");
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey("a"));
|
||||
KeePass2Reader reader;
|
||||
QScopedPointer<Database> db(reader.readDatabase(filename, key));
|
||||
QCOMPARE(reader.version(), KeePass2::FILE_VERSION_3);
|
||||
QVERIFY(db.data());
|
||||
QVERIFY(!reader.hasError());
|
||||
|
||||
QCOMPARE(db->rootGroup()->name(), QString("Format300"));
|
||||
QCOMPARE(db->metadata()->name(), QString("Test Database Format 0x00030000"));
|
||||
}
|
||||
|
||||
void TestKdbx3::testNonAscii()
|
||||
{
|
||||
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/NonAscii.kdbx");
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey(QString::fromUtf8("\xce\x94\xc3\xb6\xd8\xb6")));
|
||||
KeePass2Reader reader;
|
||||
QScopedPointer<Database> db(reader.readDatabase(filename, key));
|
||||
QVERIFY(db.data());
|
||||
QVERIFY(!reader.hasError());
|
||||
QCOMPARE(db->metadata()->name(), QString("NonAsciiTest"));
|
||||
QCOMPARE(db->compressionAlgo(), Database::CompressionNone);
|
||||
}
|
||||
|
||||
void TestKdbx3::testCompressed()
|
||||
{
|
||||
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Compressed.kdbx");
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey(""));
|
||||
KeePass2Reader reader;
|
||||
QScopedPointer<Database> db(reader.readDatabase(filename, key));
|
||||
QVERIFY(db.data());
|
||||
QVERIFY(!reader.hasError());
|
||||
QCOMPARE(db->metadata()->name(), QString("Compressed"));
|
||||
QCOMPARE(db->compressionAlgo(), Database::CompressionGZip);
|
||||
}
|
||||
|
||||
void TestKdbx3::testProtectedStrings()
|
||||
{
|
||||
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/ProtectedStrings.kdbx");
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey("masterpw"));
|
||||
KeePass2Reader reader;
|
||||
QScopedPointer<Database> db(reader.readDatabase(filename, key));
|
||||
QVERIFY(db.data());
|
||||
QVERIFY(!reader.hasError());
|
||||
QCOMPARE(db->metadata()->name(), QString("Protected Strings Test"));
|
||||
|
||||
Entry* entry = db->rootGroup()->entries().at(0);
|
||||
|
||||
QCOMPARE(entry->title(), QString("Sample Entry"));
|
||||
QCOMPARE(entry->username(), QString("Protected User Name"));
|
||||
QCOMPARE(entry->password(), QString("ProtectedPassword"));
|
||||
QCOMPARE(entry->attributes()->value("TestProtected"), QString("ABC"));
|
||||
QCOMPARE(entry->attributes()->value("TestUnprotected"), QString("DEF"));
|
||||
|
||||
QVERIFY(db->metadata()->protectPassword());
|
||||
QVERIFY(entry->attributes()->isProtected("TestProtected"));
|
||||
QVERIFY(!entry->attributes()->isProtected("TestUnprotected"));
|
||||
}
|
||||
|
||||
void TestKdbx3::testBrokenHeaderHash()
|
||||
{
|
||||
// The protected stream key has been modified in the header.
|
||||
// Make sure the database won't open.
|
||||
|
||||
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/BrokenHeaderHash.kdbx");
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey(""));
|
||||
KeePass2Reader reader;
|
||||
QScopedPointer<Database> db(reader.readDatabase(filename, key));
|
||||
QVERIFY(!db.data());
|
||||
QVERIFY(reader.hasError());
|
||||
}
|
||||
|
||||
void TestKdbx3::testKdbxRepair()
|
||||
{
|
||||
QString brokenDbFilename = QString(KEEPASSX_TEST_DATA_DIR).append("/bug392.kdbx");
|
||||
// master password = test
|
||||
// entry username: testuser\x10\x20AC
|
||||
// entry password: testpw
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey("test"));
|
||||
|
||||
// test that we can't open the broken database
|
||||
bool hasError;
|
||||
QString errorString;
|
||||
QScopedPointer<Database> dbBroken;
|
||||
readKdbx(brokenDbFilename, key, dbBroken, hasError, errorString);
|
||||
QVERIFY(!dbBroken.data());
|
||||
QVERIFY(hasError);
|
||||
|
||||
// test if we can repair the database
|
||||
KeePass2Repair repair;
|
||||
QFile file(brokenDbFilename);
|
||||
file.open(QIODevice::ReadOnly);
|
||||
auto result = repair.repairDatabase(&file, key);
|
||||
QCOMPARE(result.first, KeePass2Repair::RepairSuccess);
|
||||
QScopedPointer<Database> dbRepaired(result.second);
|
||||
QVERIFY(dbRepaired);
|
||||
|
||||
QCOMPARE(dbRepaired->rootGroup()->entries().size(), 1);
|
||||
QCOMPARE(dbRepaired->rootGroup()->entries().at(0)->username(), QString("testuser").append(QChar(0x20AC)));
|
||||
QCOMPARE(dbRepaired->rootGroup()->entries().at(0)->password(), QString("testpw"));
|
||||
}
|
||||
|
@ -18,19 +18,33 @@
|
||||
#ifndef KEEPASSXC_TEST_KDBX3_H
|
||||
#define KEEPASSXC_TEST_KDBX3_H
|
||||
|
||||
#include "TestKeePass2XmlReader.h"
|
||||
#include "TestKeePass2Format.h"
|
||||
|
||||
class TestKdbx3 : public TestKeePass2XmlReader
|
||||
class TestKdbx3 : public TestKeePass2Format
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
virtual void initTestCase() override;
|
||||
void testNonAscii();
|
||||
void testCompressed();
|
||||
void testProtectedStrings();
|
||||
void testBrokenHeaderHash();
|
||||
void testFormat300();
|
||||
void testKdbxRepair();
|
||||
|
||||
protected:
|
||||
virtual Database* readDatabase(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString) override;
|
||||
virtual Database* readDatabase(QString path, bool strictMode, bool& hasError, QString& errorString) override;
|
||||
virtual void writeDatabase(QBuffer* buf, Database* db, bool& hasError, QString& errorString) override;
|
||||
void initTestCaseImpl() override;
|
||||
|
||||
Database* readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString) override;
|
||||
Database* readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString) override;
|
||||
void writeXml(QBuffer* buf, Database* db, bool& hasError, QString& errorString) override;
|
||||
|
||||
void readKdbx(QIODevice* device, CompositeKey const& key, QScopedPointer<Database>& db,
|
||||
bool& hasError, QString& errorString) override;
|
||||
void readKdbx(const QString& path, CompositeKey const& key, QScopedPointer<Database>& db,
|
||||
bool& hasError, QString& errorString) override;
|
||||
void writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString) override;
|
||||
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_TEST_KDBX3_H
|
||||
|
@ -17,8 +17,10 @@
|
||||
|
||||
#include "TestKdbx4.h"
|
||||
#include "core/Metadata.h"
|
||||
#include "crypto/Crypto.h"
|
||||
#include "keys/PasswordKey.h"
|
||||
#include "format/KeePass2.h"
|
||||
#include "format/KeePass2Reader.h"
|
||||
#include "format/KeePass2Writer.h"
|
||||
#include "format/KdbxXmlReader.h"
|
||||
#include "format/KdbxXmlWriter.h"
|
||||
#include "config-keepassx-tests.h"
|
||||
@ -27,21 +29,15 @@
|
||||
|
||||
QTEST_GUILESS_MAIN(TestKdbx4)
|
||||
|
||||
void TestKdbx4::initTestCase()
|
||||
void TestKdbx4::initTestCaseImpl()
|
||||
{
|
||||
QVERIFY(Crypto::init());
|
||||
|
||||
KdbxXmlReader reader(KeePass2::FILE_VERSION_3);
|
||||
reader.setStrictMode(true);
|
||||
QString xmlFile = QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.xml");
|
||||
m_db.reset(reader.readDatabase(xmlFile));
|
||||
QVERIFY(m_db.data());
|
||||
QVERIFY(!reader.hasError());
|
||||
m_xmlDb->changeKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2));
|
||||
m_kdbxSourceDb->changeKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2));
|
||||
}
|
||||
|
||||
Database* TestKdbx4::readDatabase(QString path, bool strictMode, bool& hasError, QString& errorString)
|
||||
Database* TestKdbx4::readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString)
|
||||
{
|
||||
KdbxXmlReader reader(KeePass2::FILE_VERSION_3);
|
||||
KdbxXmlReader reader(KeePass2::FILE_VERSION_4);
|
||||
reader.setStrictMode(strictMode);
|
||||
auto db = reader.readDatabase(path);
|
||||
hasError = reader.hasError();
|
||||
@ -49,9 +45,9 @@ Database* TestKdbx4::readDatabase(QString path, bool strictMode, bool& hasError,
|
||||
return db;
|
||||
}
|
||||
|
||||
Database* TestKdbx4::readDatabase(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString)
|
||||
Database* TestKdbx4::readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString)
|
||||
{
|
||||
KdbxXmlReader reader(KeePass2::FILE_VERSION_3);
|
||||
KdbxXmlReader reader(KeePass2::FILE_VERSION_4);
|
||||
reader.setStrictMode(strictMode);
|
||||
auto db = reader.readDatabase(buf);
|
||||
hasError = reader.hasError();
|
||||
@ -59,10 +55,135 @@ Database* TestKdbx4::readDatabase(QBuffer* buf, bool strictMode, bool& hasError,
|
||||
return db;
|
||||
}
|
||||
|
||||
void TestKdbx4::writeDatabase(QBuffer* buf, Database* db, bool& hasError, QString& errorString)
|
||||
void TestKdbx4::writeXml(QBuffer* buf, Database* db, bool& hasError, QString& errorString)
|
||||
{
|
||||
KdbxXmlWriter writer(KeePass2::FILE_VERSION_3);
|
||||
KdbxXmlWriter writer(KeePass2::FILE_VERSION_4);
|
||||
writer.writeDatabase(buf, db);
|
||||
hasError = writer.hasError();
|
||||
errorString = writer.errorString();
|
||||
}
|
||||
|
||||
void TestKdbx4::readKdbx(QIODevice* device, CompositeKey const& key, QScopedPointer<Database>& db,
|
||||
bool& hasError, QString& errorString)
|
||||
{
|
||||
KeePass2Reader reader;
|
||||
db.reset(reader.readDatabase(device, key));
|
||||
hasError = reader.hasError();
|
||||
if (hasError) {
|
||||
errorString = reader.errorString();
|
||||
}
|
||||
QCOMPARE(reader.version(), KeePass2::FILE_VERSION_4);
|
||||
}
|
||||
|
||||
void TestKdbx4::readKdbx(const QString& path, CompositeKey const& key, QScopedPointer<Database>& db,
|
||||
bool& hasError, QString& errorString)
|
||||
{
|
||||
KeePass2Reader reader;
|
||||
db.reset(reader.readDatabase(path, key));
|
||||
hasError = reader.hasError();
|
||||
if (hasError) {
|
||||
errorString = reader.errorString();
|
||||
}
|
||||
QCOMPARE(reader.version(), KeePass2::FILE_VERSION_4);
|
||||
}
|
||||
|
||||
void TestKdbx4::writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString)
|
||||
{
|
||||
if (db->kdf()->uuid() == KeePass2::KDF_AES_KDBX3) {
|
||||
db->changeKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2));
|
||||
}
|
||||
KeePass2Writer writer;
|
||||
hasError = writer.writeDatabase(device, db);
|
||||
hasError = writer.hasError();
|
||||
if (hasError) {
|
||||
errorString = writer.errorString();
|
||||
}
|
||||
QCOMPARE(writer.version(), KeePass2::FILE_VERSION_4);
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(Uuid);
|
||||
void TestKdbx4::testFormat400()
|
||||
{
|
||||
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Format400.kdbx");
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey("t"));
|
||||
KeePass2Reader reader;
|
||||
QScopedPointer<Database> db(reader.readDatabase(filename, key));
|
||||
QCOMPARE(reader.version(), KeePass2::FILE_VERSION_4);
|
||||
QVERIFY(db.data());
|
||||
QVERIFY(!reader.hasError());
|
||||
|
||||
QCOMPARE(db->rootGroup()->name(), QString("Format400"));
|
||||
QCOMPARE(db->metadata()->name(), QString("Format400"));
|
||||
QCOMPARE(db->rootGroup()->entries().size(), 1);
|
||||
auto entry = db->rootGroup()->entries().at(0);
|
||||
|
||||
QCOMPARE(entry->title(), QString("Format400"));
|
||||
QCOMPARE(entry->username(), QString("Format400"));
|
||||
QCOMPARE(entry->attributes()->keys().size(), 6);
|
||||
QCOMPARE(entry->attributes()->value("Format400"), QString("Format400"));
|
||||
QCOMPARE(entry->attachments()->keys().size(), 1);
|
||||
QCOMPARE(entry->attachments()->value("Format400"), QByteArray("Format400\n"));
|
||||
}
|
||||
|
||||
void TestKdbx4::testFormat400Upgrade()
|
||||
{
|
||||
QFETCH(Uuid, kdfUuid);
|
||||
QFETCH(Uuid, cipherUuid);
|
||||
QFETCH(quint32, expectedVersion);
|
||||
|
||||
QScopedPointer<Database> sourceDb(new Database());
|
||||
sourceDb->metadata()->setName("Wubba lubba dub dub");
|
||||
QCOMPARE(sourceDb->kdf()->uuid(), KeePass2::KDF_AES_KDBX3); // default is legacy AES-KDF
|
||||
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey("I am in great pain, please help me!"));
|
||||
sourceDb->setKey(key, true, true);
|
||||
|
||||
QBuffer buffer;
|
||||
buffer.open(QBuffer::ReadWrite);
|
||||
|
||||
// upgrade to KDBX 4 by changing KDF and Cipher
|
||||
sourceDb->changeKdf(KeePass2::uuidToKdf(kdfUuid));
|
||||
sourceDb->setCipher(cipherUuid);
|
||||
KeePass2Writer writer;
|
||||
writer.writeDatabase(&buffer, sourceDb.data());
|
||||
if (writer.hasError()) {
|
||||
QFAIL(qPrintable(QString("Error while writing database: %1").arg(writer.errorString())));
|
||||
}
|
||||
|
||||
// read buffer back
|
||||
buffer.seek(0);
|
||||
KeePass2Reader reader;
|
||||
QScopedPointer<Database> targetDb(reader.readDatabase(&buffer, key));
|
||||
if (reader.hasError()) {
|
||||
QFAIL(qPrintable(QString("Error while reading database: %1").arg(reader.errorString())));
|
||||
}
|
||||
|
||||
QVERIFY(targetDb->rootGroup());
|
||||
QCOMPARE(targetDb->metadata()->name(), sourceDb->metadata()->name());
|
||||
|
||||
QCOMPARE(reader.version(), expectedVersion);
|
||||
QCOMPARE(targetDb->kdf()->uuid(), sourceDb->kdf()->uuid());
|
||||
QCOMPARE(targetDb->cipher(), cipherUuid);
|
||||
}
|
||||
|
||||
void TestKdbx4::testFormat400Upgrade_data()
|
||||
{
|
||||
QTest::addColumn<Uuid>("kdfUuid");
|
||||
QTest::addColumn<Uuid>("cipherUuid");
|
||||
QTest::addColumn<quint32>("expectedVersion");
|
||||
|
||||
auto constexpr kdbx3 = KeePass2::FILE_VERSION_3_1 & KeePass2::FILE_VERSION_CRITICAL_MASK;
|
||||
auto constexpr kdbx4 = KeePass2::FILE_VERSION_4 & KeePass2::FILE_VERSION_CRITICAL_MASK;
|
||||
|
||||
QTest::newRow("Argon2 + AES") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_AES << kdbx4;
|
||||
QTest::newRow("AES-KDF + AES") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_AES << kdbx4;
|
||||
QTest::newRow("AES-KDF (legacy) + AES") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_AES << kdbx3;
|
||||
QTest::newRow("Argon2 + ChaCha20") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_CHACHA20 << kdbx4;
|
||||
QTest::newRow("AES-KDF + ChaCha20") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_CHACHA20 << kdbx4;
|
||||
QTest::newRow("AES-KDF (legacy) + ChaCha20") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_CHACHA20 << kdbx3;
|
||||
QTest::newRow("Argon2 + Twofish") << KeePass2::KDF_ARGON2 << KeePass2::CIPHER_TWOFISH << kdbx4;
|
||||
QTest::newRow("AES-KDF + Twofish") << KeePass2::KDF_AES_KDBX4 << KeePass2::CIPHER_TWOFISH << kdbx4;
|
||||
QTest::newRow("AES-KDF (legacy) + Twofish") << KeePass2::KDF_AES_KDBX3 << KeePass2::CIPHER_TWOFISH << kdbx3;
|
||||
}
|
||||
|
@ -18,19 +18,29 @@
|
||||
#ifndef KEEPASSXC_TEST_KDBX4_H
|
||||
#define KEEPASSXC_TEST_KDBX4_H
|
||||
|
||||
#include "TestKeePass2XmlReader.h"
|
||||
#include "TestKeePass2Format.h"
|
||||
|
||||
class TestKdbx4 : public TestKeePass2XmlReader
|
||||
class TestKdbx4 : public TestKeePass2Format
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
virtual void initTestCase() override;
|
||||
void testFormat400();
|
||||
void testFormat400Upgrade();
|
||||
void testFormat400Upgrade_data();
|
||||
|
||||
protected:
|
||||
virtual Database* readDatabase(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString) override;
|
||||
virtual Database* readDatabase(QString path, bool strictMode, bool& hasError, QString& errorString) override;
|
||||
virtual void writeDatabase(QBuffer* buf, Database* db, bool& hasError, QString& errorString) override;
|
||||
void initTestCaseImpl() override;
|
||||
|
||||
Database* readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString) override;
|
||||
Database* readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString) override;
|
||||
void writeXml(QBuffer* buf, Database* db, bool& hasError, QString& errorString) override;
|
||||
|
||||
void readKdbx(const QString& path, CompositeKey const& key, QScopedPointer<Database>& db,
|
||||
bool& hasError, QString& errorString) override;
|
||||
void readKdbx(QIODevice* device, CompositeKey const& key, QScopedPointer<Database>& db,
|
||||
bool& hasError, QString& errorString) override;
|
||||
void writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString) override;
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_TEST_KDBX4_H
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
|
||||
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@ -15,101 +15,105 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TestKeePass2XmlReader.h"
|
||||
#include "TestKeePass2Format.h"
|
||||
|
||||
#include "core/Group.h"
|
||||
#include "core/Metadata.h"
|
||||
#include "crypto/Crypto.h"
|
||||
#include "keys/PasswordKey.h"
|
||||
#include "format/KdbxXmlReader.h"
|
||||
#include "format/KdbxXmlWriter.h"
|
||||
|
||||
#include "FailDevice.h"
|
||||
#include "config-keepassx-tests.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <QTest>
|
||||
|
||||
namespace QTest {
|
||||
template<>
|
||||
char* toString(const Uuid& uuid)
|
||||
void TestKeePass2Format::initTestCase()
|
||||
{
|
||||
QByteArray ba = "Uuid(";
|
||||
ba += uuid.toBase64().toLatin1().constData();
|
||||
ba += ")";
|
||||
return qstrdup(ba.constData());
|
||||
}
|
||||
QVERIFY(Crypto::init());
|
||||
|
||||
template<>
|
||||
char* toString(const Group::TriState& triState)
|
||||
{
|
||||
QString value;
|
||||
// read raw XML database
|
||||
bool hasError;
|
||||
QString errorString;
|
||||
m_xmlDb.reset(readXml(QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.xml"), true, hasError, errorString));
|
||||
if (hasError) {
|
||||
QFAIL(qPrintable(QString("Error while reading XML: ").append(errorString)));
|
||||
}
|
||||
QVERIFY(m_xmlDb.data());
|
||||
|
||||
if (triState == Group::Inherit) {
|
||||
value = "null";
|
||||
} else if (triState == Group::Enable) {
|
||||
value = "true";
|
||||
} else {
|
||||
value = "false";
|
||||
// construct and write KDBX to buffer
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey("test"));
|
||||
|
||||
m_kdbxSourceDb.reset(new Database());
|
||||
m_kdbxSourceDb->setKey(key);
|
||||
m_kdbxSourceDb->metadata()->setName("TESTDB");
|
||||
Group* group = m_kdbxSourceDb->rootGroup();
|
||||
group->setUuid(Uuid::random());
|
||||
group->setNotes("I'm a note!");
|
||||
auto entry = new Entry();
|
||||
entry->setPassword(QString::fromUtf8("\xc3\xa4\xa3\xb6\xc3\xbc\xe9\x9b\xbb\xe7\xb4\x85"));
|
||||
entry->setUuid(Uuid::random());
|
||||
entry->attributes()->set("test", "protectedTest", true);
|
||||
QVERIFY(entry->attributes()->isProtected("test"));
|
||||
entry->attachments()->set("myattach.txt", QByteArray("this is an attachment"));
|
||||
entry->attachments()->set("aaa.txt", QByteArray("also an attachment"));
|
||||
entry->setGroup(group);
|
||||
auto groupNew = new Group();
|
||||
groupNew->setUuid(Uuid::random());
|
||||
groupNew->setName("TESTGROUP");
|
||||
groupNew->setNotes("I'm a sub group note!");
|
||||
groupNew->setParent(group);
|
||||
|
||||
m_kdbxTargetBuffer.open(QBuffer::ReadWrite);
|
||||
writeKdbx(&m_kdbxTargetBuffer, m_kdbxSourceDb.data(), hasError, errorString);
|
||||
if (hasError) {
|
||||
QFAIL(qPrintable(QString("Error while writing database: ").append(errorString)));
|
||||
}
|
||||
|
||||
return qstrdup(value.toLocal8Bit().constData());
|
||||
}
|
||||
// call sub class init method
|
||||
initTestCaseImpl();
|
||||
}
|
||||
|
||||
QDateTime TestKeePass2XmlReader::genDT(int year, int month, int day, int hour, int min, int second)
|
||||
void TestKeePass2Format::testXmlMetadata()
|
||||
{
|
||||
QDate date(year, month, day);
|
||||
QTime time(hour, min, second);
|
||||
return QDateTime(date, time, Qt::UTC);
|
||||
QCOMPARE(m_xmlDb->metadata()->generator(), QString("KeePass"));
|
||||
QCOMPARE(m_xmlDb->metadata()->name(), QString("ANAME"));
|
||||
QCOMPARE(m_xmlDb->metadata()->nameChanged(), genDT(2010, 8, 8, 17, 24, 53));
|
||||
QCOMPARE(m_xmlDb->metadata()->description(), QString("ADESC"));
|
||||
QCOMPARE(m_xmlDb->metadata()->descriptionChanged(), genDT(2010, 8, 8, 17, 27, 12));
|
||||
QCOMPARE(m_xmlDb->metadata()->defaultUserName(), QString("DEFUSERNAME"));
|
||||
QCOMPARE(m_xmlDb->metadata()->defaultUserNameChanged(), genDT(2010, 8, 8, 17, 27, 45));
|
||||
QCOMPARE(m_xmlDb->metadata()->maintenanceHistoryDays(), 127);
|
||||
QCOMPARE(m_xmlDb->metadata()->color(), QColor(0xff, 0xef, 0x00));
|
||||
QCOMPARE(m_xmlDb->metadata()->masterKeyChanged(), genDT(2012, 4, 5, 17, 9, 34));
|
||||
QCOMPARE(m_xmlDb->metadata()->masterKeyChangeRec(), 101);
|
||||
QCOMPARE(m_xmlDb->metadata()->masterKeyChangeForce(), -1);
|
||||
QCOMPARE(m_xmlDb->metadata()->protectTitle(), false);
|
||||
QCOMPARE(m_xmlDb->metadata()->protectUsername(), true);
|
||||
QCOMPARE(m_xmlDb->metadata()->protectPassword(), false);
|
||||
QCOMPARE(m_xmlDb->metadata()->protectUrl(), true);
|
||||
QCOMPARE(m_xmlDb->metadata()->protectNotes(), false);
|
||||
QCOMPARE(m_xmlDb->metadata()->recycleBinEnabled(), true);
|
||||
QVERIFY(m_xmlDb->metadata()->recycleBin() != nullptr);
|
||||
QCOMPARE(m_xmlDb->metadata()->recycleBin()->name(), QString("Recycle Bin"));
|
||||
QCOMPARE(m_xmlDb->metadata()->recycleBinChanged(), genDT(2010, 8, 25, 16, 12, 57));
|
||||
QVERIFY(m_xmlDb->metadata()->entryTemplatesGroup() == nullptr);
|
||||
QCOMPARE(m_xmlDb->metadata()->entryTemplatesGroupChanged(), genDT(2010, 8, 8, 17, 24, 19));
|
||||
QVERIFY(m_xmlDb->metadata()->lastSelectedGroup() != nullptr);
|
||||
QCOMPARE(m_xmlDb->metadata()->lastSelectedGroup()->name(), QString("NewDatabase"));
|
||||
QVERIFY(m_xmlDb->metadata()->lastTopVisibleGroup() == m_xmlDb->metadata()->lastSelectedGroup());
|
||||
QCOMPARE(m_xmlDb->metadata()->historyMaxItems(), -1);
|
||||
QCOMPARE(m_xmlDb->metadata()->historyMaxSize(), 5242880);
|
||||
}
|
||||
|
||||
QByteArray TestKeePass2XmlReader::strToBytes(const QString& str)
|
||||
void TestKeePass2Format::testXmlCustomIcons()
|
||||
{
|
||||
QByteArray result;
|
||||
|
||||
for (auto i : str) {
|
||||
result.append(static_cast<char>(i.unicode() >> 8));
|
||||
result.append(static_cast<char>(i.unicode() & 0xFF));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void TestKeePass2XmlReader::testMetadata()
|
||||
{
|
||||
QCOMPARE(m_db->metadata()->generator(), QString("KeePass"));
|
||||
QCOMPARE(m_db->metadata()->name(), QString("ANAME"));
|
||||
QCOMPARE(m_db->metadata()->nameChanged(), genDT(2010, 8, 8, 17, 24, 53));
|
||||
QCOMPARE(m_db->metadata()->description(), QString("ADESC"));
|
||||
QCOMPARE(m_db->metadata()->descriptionChanged(), genDT(2010, 8, 8, 17, 27, 12));
|
||||
QCOMPARE(m_db->metadata()->defaultUserName(), QString("DEFUSERNAME"));
|
||||
QCOMPARE(m_db->metadata()->defaultUserNameChanged(), genDT(2010, 8, 8, 17, 27, 45));
|
||||
QCOMPARE(m_db->metadata()->maintenanceHistoryDays(), 127);
|
||||
QCOMPARE(m_db->metadata()->color(), QColor(0xff, 0xef, 0x00));
|
||||
QCOMPARE(m_db->metadata()->masterKeyChanged(), genDT(2012, 4, 5, 17, 9, 34));
|
||||
QCOMPARE(m_db->metadata()->masterKeyChangeRec(), 101);
|
||||
QCOMPARE(m_db->metadata()->masterKeyChangeForce(), -1);
|
||||
QCOMPARE(m_db->metadata()->protectTitle(), false);
|
||||
QCOMPARE(m_db->metadata()->protectUsername(), true);
|
||||
QCOMPARE(m_db->metadata()->protectPassword(), false);
|
||||
QCOMPARE(m_db->metadata()->protectUrl(), true);
|
||||
QCOMPARE(m_db->metadata()->protectNotes(), false);
|
||||
QCOMPARE(m_db->metadata()->recycleBinEnabled(), true);
|
||||
QVERIFY(m_db->metadata()->recycleBin() != nullptr);
|
||||
QCOMPARE(m_db->metadata()->recycleBin()->name(), QString("Recycle Bin"));
|
||||
QCOMPARE(m_db->metadata()->recycleBinChanged(), genDT(2010, 8, 25, 16, 12, 57));
|
||||
QVERIFY(m_db->metadata()->entryTemplatesGroup() == nullptr);
|
||||
QCOMPARE(m_db->metadata()->entryTemplatesGroupChanged(), genDT(2010, 8, 8, 17, 24, 19));
|
||||
QVERIFY(m_db->metadata()->lastSelectedGroup() != nullptr);
|
||||
QCOMPARE(m_db->metadata()->lastSelectedGroup()->name(), QString("NewDatabase"));
|
||||
QVERIFY(m_db->metadata()->lastTopVisibleGroup() == m_db->metadata()->lastSelectedGroup());
|
||||
QCOMPARE(m_db->metadata()->historyMaxItems(), -1);
|
||||
QCOMPARE(m_db->metadata()->historyMaxSize(), 5242880);
|
||||
}
|
||||
|
||||
void TestKeePass2XmlReader::testCustomIcons()
|
||||
{
|
||||
QCOMPARE(m_db->metadata()->customIcons().size(), 1);
|
||||
QCOMPARE(m_xmlDb->metadata()->customIcons().size(), 1);
|
||||
Uuid uuid = Uuid::fromBase64("++vyI+daLk6omox4a6kQGA==");
|
||||
QVERIFY(m_db->metadata()->customIcons().contains(uuid));
|
||||
QImage icon = m_db->metadata()->customIcon(uuid);
|
||||
QVERIFY(m_xmlDb->metadata()->customIcons().contains(uuid));
|
||||
QImage icon = m_xmlDb->metadata()->customIcon(uuid);
|
||||
QCOMPARE(icon.width(), 16);
|
||||
QCOMPARE(icon.height(), 16);
|
||||
|
||||
@ -123,18 +127,18 @@ void TestKeePass2XmlReader::testCustomIcons()
|
||||
}
|
||||
}
|
||||
|
||||
void TestKeePass2XmlReader::testCustomData()
|
||||
void TestKeePass2Format::testXmlCustomData()
|
||||
{
|
||||
QHash<QString, QString> customFields = m_db->metadata()->customFields();
|
||||
QHash<QString, QString> customFields = m_xmlDb->metadata()->customFields();
|
||||
|
||||
QCOMPARE(customFields.size(), 2);
|
||||
QCOMPARE(customFields.value("A Sample Test Key"), QString("valu"));
|
||||
QCOMPARE(customFields.value("custom key"), QString("blub"));
|
||||
}
|
||||
|
||||
void TestKeePass2XmlReader::testGroupRoot()
|
||||
void TestKeePass2Format::testXmlGroupRoot()
|
||||
{
|
||||
const Group* group = m_db->rootGroup();
|
||||
const Group* group = m_xmlDb->rootGroup();
|
||||
QVERIFY(group);
|
||||
QCOMPARE(group->uuid().toBase64(), QString("lmU+9n0aeESKZvcEze+bRg=="));
|
||||
QCOMPARE(group->name(), QString("NewDatabase"));
|
||||
@ -156,14 +160,14 @@ void TestKeePass2XmlReader::testGroupRoot()
|
||||
QCOMPARE(group->lastTopVisibleEntry()->uuid().toBase64(), QString("+wSUOv6qf0OzW8/ZHAs2sA=="));
|
||||
|
||||
QCOMPARE(group->children().size(), 3);
|
||||
QVERIFY(m_db->metadata()->recycleBin() == m_db->rootGroup()->children().at(2));
|
||||
QVERIFY(m_xmlDb->metadata()->recycleBin() == m_xmlDb->rootGroup()->children().at(2));
|
||||
|
||||
QCOMPARE(group->entries().size(), 2);
|
||||
}
|
||||
|
||||
void TestKeePass2XmlReader::testGroup1()
|
||||
void TestKeePass2Format::testXmlGroup1()
|
||||
{
|
||||
const Group* group = m_db->rootGroup()->children().at(0);
|
||||
const Group* group = m_xmlDb->rootGroup()->children().at(0);
|
||||
|
||||
QCOMPARE(group->uuid().toBase64(), QString("AaUYVdXsI02h4T1RiAlgtg=="));
|
||||
QCOMPARE(group->name(), QString("General"));
|
||||
@ -177,9 +181,9 @@ void TestKeePass2XmlReader::testGroup1()
|
||||
QVERIFY(!group->lastTopVisibleEntry());
|
||||
}
|
||||
|
||||
void TestKeePass2XmlReader::testGroup2()
|
||||
void TestKeePass2Format::testXmlGroup2()
|
||||
{
|
||||
const Group* group = m_db->rootGroup()->children().at(1);
|
||||
const Group* group = m_xmlDb->rootGroup()->children().at(1);
|
||||
|
||||
QCOMPARE(group->uuid().toBase64(), QString("1h4NtL5DK0yVyvaEnN//4A=="));
|
||||
QCOMPARE(group->name(), QString("Windows"));
|
||||
@ -197,9 +201,9 @@ void TestKeePass2XmlReader::testGroup2()
|
||||
QCOMPARE(entry->title(), QString("Subsub Entry"));
|
||||
}
|
||||
|
||||
void TestKeePass2XmlReader::testEntry1()
|
||||
void TestKeePass2Format::testXmlEntry1()
|
||||
{
|
||||
const Entry* entry = m_db->rootGroup()->entries().at(0);
|
||||
const Entry* entry = m_xmlDb->rootGroup()->entries().at(0);
|
||||
|
||||
QCOMPARE(entry->uuid().toBase64(), QString("+wSUOv6qf0OzW8/ZHAs2sA=="));
|
||||
QCOMPARE(entry->historyItems().size(), 2);
|
||||
@ -259,9 +263,9 @@ void TestKeePass2XmlReader::testEntry1()
|
||||
QCOMPARE(assoc1.sequence, QString(""));
|
||||
}
|
||||
|
||||
void TestKeePass2XmlReader::testEntry2()
|
||||
void TestKeePass2Format::testXmlEntry2()
|
||||
{
|
||||
const Entry* entry = m_db->rootGroup()->entries().at(1);
|
||||
const Entry* entry = m_xmlDb->rootGroup()->entries().at(1);
|
||||
|
||||
QCOMPARE(entry->uuid().toBase64(), QString("4jbADG37hkiLh2O0qUdaOQ=="));
|
||||
QCOMPARE(entry->iconNumber(), 0);
|
||||
@ -307,9 +311,9 @@ void TestKeePass2XmlReader::testEntry2()
|
||||
QCOMPARE(assoc2.sequence, QString("{Title}{UserName} test"));
|
||||
}
|
||||
|
||||
void TestKeePass2XmlReader::testEntryHistory()
|
||||
void TestKeePass2Format::testXmlEntryHistory()
|
||||
{
|
||||
const Entry* entryMain = m_db->rootGroup()->entries().at(0);
|
||||
const Entry* entryMain = m_xmlDb->rootGroup()->entries().at(0);
|
||||
QCOMPARE(entryMain->historyItems().size(), 2);
|
||||
|
||||
{
|
||||
@ -333,9 +337,9 @@ void TestKeePass2XmlReader::testEntryHistory()
|
||||
}
|
||||
}
|
||||
|
||||
void TestKeePass2XmlReader::testDeletedObjects()
|
||||
void TestKeePass2Format::testXmlDeletedObjects()
|
||||
{
|
||||
QList<DeletedObject> objList = m_db->deletedObjects();
|
||||
QList<DeletedObject> objList = m_xmlDb->deletedObjects();
|
||||
DeletedObject delObj;
|
||||
|
||||
delObj = objList.takeFirst();
|
||||
@ -349,7 +353,7 @@ void TestKeePass2XmlReader::testDeletedObjects()
|
||||
QVERIFY(objList.isEmpty());
|
||||
}
|
||||
|
||||
void TestKeePass2XmlReader::testBroken()
|
||||
void TestKeePass2Format::testXmlBroken()
|
||||
{
|
||||
QFETCH(QString, baseName);
|
||||
QFETCH(bool, strictMode);
|
||||
@ -359,14 +363,14 @@ void TestKeePass2XmlReader::testBroken()
|
||||
QVERIFY(QFile::exists(xmlFile));
|
||||
bool hasError;
|
||||
QString errorString;
|
||||
QScopedPointer<Database> db(readDatabase(xmlFile, strictMode, hasError, errorString));
|
||||
QScopedPointer<Database> db(readXml(xmlFile, strictMode, hasError, errorString));
|
||||
if (hasError) {
|
||||
qWarning("Reader error: %s", qPrintable(errorString));
|
||||
}
|
||||
QCOMPARE(hasError, expectError);
|
||||
}
|
||||
|
||||
void TestKeePass2XmlReader::testBroken_data()
|
||||
void TestKeePass2Format::testXmlBroken_data()
|
||||
{
|
||||
QTest::addColumn<QString>("baseName");
|
||||
QTest::addColumn<bool>("strictMode");
|
||||
@ -391,21 +395,21 @@ void TestKeePass2XmlReader::testBroken_data()
|
||||
QTest::newRow("BrokenDifferentEntryHistoryUuid (not strict)") << "BrokenDifferentEntryHistoryUuid" << false << false;
|
||||
}
|
||||
|
||||
void TestKeePass2XmlReader::testEmptyUuids()
|
||||
void TestKeePass2Format::testXmlEmptyUuids()
|
||||
{
|
||||
|
||||
QString xmlFile = QString("%1/%2.xml").arg(KEEPASSX_TEST_DATA_DIR, "EmptyUuids");
|
||||
QVERIFY(QFile::exists(xmlFile));
|
||||
bool hasError;
|
||||
QString errorString;
|
||||
QScopedPointer<Database> dbp(readDatabase(xmlFile, true, hasError, errorString));
|
||||
QScopedPointer<Database> dbp(readXml(xmlFile, true, hasError, errorString));
|
||||
if (hasError) {
|
||||
qWarning("Reader error: %s", qPrintable(errorString));
|
||||
}
|
||||
QVERIFY(!hasError);
|
||||
}
|
||||
|
||||
void TestKeePass2XmlReader::testInvalidXmlChars()
|
||||
void TestKeePass2Format::testXmlInvalidXmlChars()
|
||||
{
|
||||
QScopedPointer<Database> dbWrite(new Database());
|
||||
|
||||
@ -444,11 +448,11 @@ void TestKeePass2XmlReader::testInvalidXmlChars()
|
||||
buffer.open(QIODevice::ReadWrite);
|
||||
bool hasError;
|
||||
QString errorString;
|
||||
writeDatabase(&buffer, dbWrite.data(), hasError, errorString);
|
||||
writeXml(&buffer, dbWrite.data(), hasError, errorString);
|
||||
QVERIFY(!hasError);
|
||||
buffer.seek(0);
|
||||
|
||||
QScopedPointer<Database> dbRead(readDatabase(&buffer, true, hasError, errorString));
|
||||
QScopedPointer<Database> dbRead(readXml(&buffer, true, hasError, errorString));
|
||||
if (hasError) {
|
||||
qWarning("Database read error: %s", qPrintable(errorString));
|
||||
}
|
||||
@ -470,13 +474,13 @@ void TestKeePass2XmlReader::testInvalidXmlChars()
|
||||
QCOMPARE(strToBytes(attrRead->value("SurrogateValid2")), strToBytes(strSurrogateValid2));
|
||||
}
|
||||
|
||||
void TestKeePass2XmlReader::testRepairUuidHistoryItem()
|
||||
void TestKeePass2Format::testXmlRepairUuidHistoryItem()
|
||||
{
|
||||
QString xmlFile = QString("%1/%2.xml").arg(KEEPASSX_TEST_DATA_DIR, "BrokenDifferentEntryHistoryUuid");
|
||||
QVERIFY(QFile::exists(xmlFile));
|
||||
bool hasError;
|
||||
QString errorString;
|
||||
QScopedPointer<Database> db(readDatabase(xmlFile, false, hasError, errorString));
|
||||
QScopedPointer<Database> db(readXml(xmlFile, false, hasError, errorString));
|
||||
if (hasError) {
|
||||
qWarning("Database read error: %s", qPrintable(errorString));
|
||||
}
|
||||
@ -494,3 +498,125 @@ void TestKeePass2XmlReader::testRepairUuidHistoryItem()
|
||||
QVERIFY(!historyItem->uuid().isNull());
|
||||
QCOMPARE(historyItem->uuid(), entry->uuid());
|
||||
}
|
||||
|
||||
void TestKeePass2Format::testReadBackTargetDb()
|
||||
{
|
||||
// read back previously constructed KDBX
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey("test"));
|
||||
|
||||
bool hasError;
|
||||
QString errorString;
|
||||
|
||||
m_kdbxTargetBuffer.seek(0);
|
||||
readKdbx(&m_kdbxTargetBuffer, key, m_kdbxTargetDb, hasError, errorString);
|
||||
if (hasError) {
|
||||
QFAIL(qPrintable(QString("Error while reading database: ").append(errorString)));
|
||||
}
|
||||
QVERIFY(m_kdbxTargetDb.data());
|
||||
}
|
||||
|
||||
void TestKeePass2Format::testKdbxBasic()
|
||||
{
|
||||
QCOMPARE(m_kdbxTargetDb->metadata()->name(), m_kdbxSourceDb->metadata()->name());
|
||||
QVERIFY(m_kdbxTargetDb->rootGroup());
|
||||
QCOMPARE(m_kdbxTargetDb->rootGroup()->children()[0]->name(), m_kdbxSourceDb->rootGroup()->children()[0]->name());
|
||||
QCOMPARE(m_kdbxTargetDb->rootGroup()->notes(), m_kdbxSourceDb->rootGroup()->notes());
|
||||
QCOMPARE(m_kdbxTargetDb->rootGroup()->children()[0]->notes(), m_kdbxSourceDb->rootGroup()->children()[0]->notes());
|
||||
}
|
||||
|
||||
void TestKeePass2Format::testKdbxProtectedAttributes()
|
||||
{
|
||||
QCOMPARE(m_kdbxTargetDb->rootGroup()->entries().size(), 1);
|
||||
Entry* entry = m_kdbxTargetDb->rootGroup()->entries().at(0);
|
||||
QCOMPARE(entry->attributes()->value("test"), QString("protectedTest"));
|
||||
QCOMPARE(entry->attributes()->isProtected("test"), true);
|
||||
}
|
||||
|
||||
void TestKeePass2Format::testKdbxAttachments()
|
||||
{
|
||||
Entry* entry = m_kdbxTargetDb->rootGroup()->entries().at(0);
|
||||
QCOMPARE(entry->attachments()->keys().size(), 2);
|
||||
QCOMPARE(entry->attachments()->value("myattach.txt"), QByteArray("this is an attachment"));
|
||||
QCOMPARE(entry->attachments()->value("aaa.txt"), QByteArray("also an attachment"));
|
||||
}
|
||||
|
||||
void TestKeePass2Format::testKdbxNonAsciiPasswords()
|
||||
{
|
||||
QCOMPARE(m_kdbxTargetDb->rootGroup()->entries()[0]->password(), m_kdbxSourceDb->rootGroup()->entries()[0]->password());
|
||||
}
|
||||
|
||||
void TestKeePass2Format::testKdbxDeviceFailure()
|
||||
{
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey("test"));
|
||||
QScopedPointer<Database> db(new Database());
|
||||
db->setKey(key);
|
||||
// Disable compression so we write a predictable number of bytes.
|
||||
db->setCompressionAlgo(Database::CompressionNone);
|
||||
|
||||
auto entry = new Entry();
|
||||
entry->setParent(db->rootGroup());
|
||||
QByteArray attachment(4096, 'Z');
|
||||
entry->attachments()->set("test", attachment);
|
||||
|
||||
FailDevice failDevice(512);
|
||||
QVERIFY(failDevice.open(QIODevice::WriteOnly));
|
||||
bool hasError;
|
||||
QString errorString;
|
||||
writeKdbx(&failDevice, db.data(), hasError, errorString);
|
||||
QVERIFY(hasError);
|
||||
QCOMPARE(errorString, QString("FAILDEVICE"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
// ====================================================================================================
|
||||
// Helper functions
|
||||
// ====================================================================================================
|
||||
|
||||
namespace QTest {
|
||||
template<>
|
||||
char* toString(const Uuid& uuid)
|
||||
{
|
||||
QByteArray ba = "Uuid(";
|
||||
ba += uuid.toBase64().toLatin1().constData();
|
||||
ba += ")";
|
||||
return qstrdup(ba.constData());
|
||||
}
|
||||
|
||||
template<>
|
||||
char* toString(const Group::TriState& triState)
|
||||
{
|
||||
QString value;
|
||||
|
||||
if (triState == Group::Inherit) {
|
||||
value = "null";
|
||||
} else if (triState == Group::Enable) {
|
||||
value = "true";
|
||||
} else {
|
||||
value = "false";
|
||||
}
|
||||
|
||||
return qstrdup(value.toLocal8Bit().constData());
|
||||
}
|
||||
}
|
||||
|
||||
QDateTime TestKeePass2Format::genDT(int year, int month, int day, int hour, int min, int second)
|
||||
{
|
||||
QDate date(year, month, day);
|
||||
QTime time(hour, min, second);
|
||||
return QDateTime(date, time, Qt::UTC);
|
||||
}
|
||||
|
||||
QByteArray TestKeePass2Format::strToBytes(const QString& str)
|
||||
{
|
||||
QByteArray result;
|
||||
|
||||
for (auto i : str) {
|
||||
result.append(static_cast<char>(i.unicode() >> 8));
|
||||
result.append(static_cast<char>(i.unicode() & 0xFF));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
91
tests/TestKeePass2Format.h
Normal file
91
tests/TestKeePass2Format.h
Normal file
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_TESTKEEPASS2FORMAT_H
|
||||
#define KEEPASSXC_TESTKEEPASS2FORMAT_H
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QBuffer>
|
||||
#include <QScopedPointer>
|
||||
|
||||
#include "core/Database.h"
|
||||
|
||||
/**
|
||||
* Abstract base class for KeePass2 file format tests.
|
||||
*/
|
||||
class TestKeePass2Format : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
|
||||
/**
|
||||
* XML Reader / writer tests.
|
||||
*/
|
||||
void testXmlMetadata();
|
||||
void testXmlCustomIcons();
|
||||
void testXmlCustomData();
|
||||
void testXmlGroupRoot();
|
||||
void testXmlGroup1();
|
||||
void testXmlGroup2();
|
||||
void testXmlEntry1();
|
||||
void testXmlEntry2();
|
||||
void testXmlEntryHistory();
|
||||
void testXmlDeletedObjects();
|
||||
void testXmlBroken();
|
||||
void testXmlBroken_data();
|
||||
void testXmlEmptyUuids();
|
||||
void testXmlInvalidXmlChars();
|
||||
void testXmlRepairUuidHistoryItem();
|
||||
|
||||
/**
|
||||
* KDBX binary format tests.
|
||||
*/
|
||||
void testReadBackTargetDb();
|
||||
void testKdbxBasic();
|
||||
void testKdbxProtectedAttributes();
|
||||
void testKdbxAttachments();
|
||||
void testKdbxNonAsciiPasswords();
|
||||
void testKdbxDeviceFailure();
|
||||
|
||||
protected:
|
||||
virtual void initTestCaseImpl() = 0;
|
||||
|
||||
virtual Database* readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString) = 0;
|
||||
virtual Database* readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString) = 0;
|
||||
virtual void writeXml(QBuffer* buf, Database* db, bool& hasError, QString& errorString) = 0;
|
||||
|
||||
virtual void readKdbx(QIODevice* device, CompositeKey const& key, QScopedPointer<Database>& db,
|
||||
bool& hasError, QString& errorString) = 0;
|
||||
virtual void readKdbx(const QString& path, CompositeKey const& key, QScopedPointer<Database>& db,
|
||||
bool& hasError, QString& errorString) = 0;
|
||||
virtual void writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString) = 0;
|
||||
|
||||
static QDateTime genDT(int year, int month, int day, int hour, int min, int second);
|
||||
static QByteArray strToBytes(const QString& str);
|
||||
|
||||
QScopedPointer<Database> m_xmlDb;
|
||||
QScopedPointer<Database> m_kdbxSourceDb;
|
||||
QScopedPointer<Database> m_kdbxTargetDb;
|
||||
|
||||
private:
|
||||
QBuffer m_kdbxTargetBuffer;
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_TESTKEEPASS2FORMAT_H
|
@ -1,180 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TestKeePass2Reader.h"
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include "config-keepassx-tests.h"
|
||||
#include "core/Database.h"
|
||||
#include "core/Group.h"
|
||||
#include "core/Metadata.h"
|
||||
#include "crypto/Crypto.h"
|
||||
#include "format/KeePass2Reader.h"
|
||||
#include "keys/PasswordKey.h"
|
||||
|
||||
QTEST_GUILESS_MAIN(TestKeePass2Reader)
|
||||
|
||||
void TestKeePass2Reader::initTestCase()
|
||||
{
|
||||
QVERIFY(Crypto::init());
|
||||
}
|
||||
|
||||
void TestKeePass2Reader::testNonAscii()
|
||||
{
|
||||
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/NonAscii.kdbx");
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey(QString::fromUtf8("\xce\x94\xc3\xb6\xd8\xb6")));
|
||||
KeePass2Reader reader;
|
||||
Database* db = reader.readDatabase(filename, key);
|
||||
QVERIFY(db);
|
||||
QVERIFY(!reader.hasError());
|
||||
QCOMPARE(db->metadata()->name(), QString("NonAsciiTest"));
|
||||
QCOMPARE(db->compressionAlgo(), Database::CompressionNone);
|
||||
|
||||
delete db;
|
||||
}
|
||||
|
||||
void TestKeePass2Reader::testCompressed()
|
||||
{
|
||||
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Compressed.kdbx");
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey(""));
|
||||
KeePass2Reader reader;
|
||||
Database* db = reader.readDatabase(filename, key);
|
||||
QVERIFY(db);
|
||||
QVERIFY(!reader.hasError());
|
||||
QCOMPARE(db->metadata()->name(), QString("Compressed"));
|
||||
QCOMPARE(db->compressionAlgo(), Database::CompressionGZip);
|
||||
|
||||
delete db;
|
||||
}
|
||||
|
||||
void TestKeePass2Reader::testProtectedStrings()
|
||||
{
|
||||
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/ProtectedStrings.kdbx");
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey("masterpw"));
|
||||
KeePass2Reader reader;
|
||||
Database* db = reader.readDatabase(filename, key);
|
||||
QVERIFY(db);
|
||||
QVERIFY(!reader.hasError());
|
||||
QCOMPARE(db->metadata()->name(), QString("Protected Strings Test"));
|
||||
|
||||
Entry* entry = db->rootGroup()->entries().at(0);
|
||||
|
||||
QCOMPARE(entry->title(), QString("Sample Entry"));
|
||||
QCOMPARE(entry->username(), QString("Protected User Name"));
|
||||
QCOMPARE(entry->password(), QString("ProtectedPassword"));
|
||||
QCOMPARE(entry->attributes()->value("TestProtected"), QString("ABC"));
|
||||
QCOMPARE(entry->attributes()->value("TestUnprotected"), QString("DEF"));
|
||||
|
||||
QVERIFY(db->metadata()->protectPassword());
|
||||
QVERIFY(entry->attributes()->isProtected("TestProtected"));
|
||||
QVERIFY(!entry->attributes()->isProtected("TestUnprotected"));
|
||||
|
||||
delete db;
|
||||
}
|
||||
|
||||
void TestKeePass2Reader::testBrokenHeaderHash()
|
||||
{
|
||||
// The protected stream key has been modified in the header.
|
||||
// Make sure the database won't open.
|
||||
|
||||
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/BrokenHeaderHash.kdbx");
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey(""));
|
||||
KeePass2Reader reader;
|
||||
Database* db = reader.readDatabase(filename, key);
|
||||
QVERIFY(!db);
|
||||
QVERIFY(reader.hasError());
|
||||
|
||||
delete db;
|
||||
}
|
||||
|
||||
void TestKeePass2Reader::testFormat200()
|
||||
{
|
||||
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Format200.kdbx");
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey("a"));
|
||||
KeePass2Reader reader;
|
||||
Database* db = reader.readDatabase(filename, key);
|
||||
QVERIFY(db);
|
||||
QVERIFY(!reader.hasError());
|
||||
|
||||
QCOMPARE(db->rootGroup()->name(), QString("Format200"));
|
||||
QVERIFY(!db->metadata()->protectTitle());
|
||||
QVERIFY(db->metadata()->protectUsername());
|
||||
QVERIFY(!db->metadata()->protectPassword());
|
||||
QVERIFY(db->metadata()->protectUrl());
|
||||
QVERIFY(!db->metadata()->protectNotes());
|
||||
|
||||
QCOMPARE(db->rootGroup()->entries().size(), 1);
|
||||
Entry* entry = db->rootGroup()->entries().at(0);
|
||||
|
||||
QCOMPARE(entry->title(), QString("Sample Entry"));
|
||||
QCOMPARE(entry->username(), QString("User Name"));
|
||||
QCOMPARE(entry->attachments()->keys().size(), 2);
|
||||
QCOMPARE(entry->attachments()->value("myattach.txt"), QByteArray("abcdefghijk"));
|
||||
QCOMPARE(entry->attachments()->value("test.txt"), QByteArray("this is a test"));
|
||||
|
||||
QCOMPARE(entry->historyItems().size(), 2);
|
||||
QCOMPARE(entry->historyItems().at(0)->attachments()->keys().size(), 0);
|
||||
QCOMPARE(entry->historyItems().at(1)->attachments()->keys().size(), 1);
|
||||
QCOMPARE(entry->historyItems().at(1)->attachments()->value("myattach.txt"), QByteArray("abcdefghijk"));
|
||||
|
||||
delete db;
|
||||
}
|
||||
|
||||
void TestKeePass2Reader::testFormat300()
|
||||
{
|
||||
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Format300.kdbx");
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey("a"));
|
||||
KeePass2Reader reader;
|
||||
Database* db = reader.readDatabase(filename, key);
|
||||
QVERIFY(db);
|
||||
QVERIFY(!reader.hasError());
|
||||
|
||||
QCOMPARE(db->rootGroup()->name(), QString("Format300"));
|
||||
QCOMPARE(db->metadata()->name(), QString("Test Database Format 0x00030000"));
|
||||
|
||||
delete db;
|
||||
}
|
||||
|
||||
void TestKeePass2Reader::testFormat400()
|
||||
{
|
||||
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Format400.kdbx");
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey("t"));
|
||||
KeePass2Reader reader;
|
||||
Database* db = reader.readDatabase(filename, key);
|
||||
QVERIFY(db);
|
||||
QVERIFY(!reader.hasError());
|
||||
|
||||
QCOMPARE(db->rootGroup()->name(), QString("Format400"));
|
||||
QCOMPARE(db->metadata()->name(), QString("Format400"));
|
||||
QCOMPARE(db->rootGroup()->entries().size(), 1);
|
||||
Entry* entry = db->rootGroup()->entries().at(0);
|
||||
|
||||
QCOMPARE(entry->title(), QString("Format400"));
|
||||
QCOMPARE(entry->username(), QString("Format400"));
|
||||
QCOMPARE(entry->attributes()->keys().size(), 6);
|
||||
QCOMPARE(entry->attributes()->value("Format400"), QString("Format400"));
|
||||
QCOMPARE(entry->attachments()->keys().size(), 1);
|
||||
QCOMPARE(entry->attachments()->value("Format400"), QByteArray("Format400\n"));
|
||||
}
|
@ -1,168 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TestKeePass2Writer.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QFile>
|
||||
#include <QTest>
|
||||
|
||||
#include "config-keepassx-tests.h"
|
||||
#include "FailDevice.h"
|
||||
#include "core/Database.h"
|
||||
#include "core/Group.h"
|
||||
#include "core/Metadata.h"
|
||||
#include "crypto/Crypto.h"
|
||||
#include "format/KeePass2Reader.h"
|
||||
#include "format/KeePass2Repair.h"
|
||||
#include "format/KeePass2Writer.h"
|
||||
#include "keys/PasswordKey.h"
|
||||
|
||||
QTEST_GUILESS_MAIN(TestKeePass2Writer)
|
||||
|
||||
void TestKeePass2Writer::initTestCase()
|
||||
{
|
||||
QVERIFY(Crypto::init());
|
||||
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey("test"));
|
||||
|
||||
m_dbOrg = new Database();
|
||||
m_dbOrg->setKey(key);
|
||||
m_dbOrg->metadata()->setName("TESTDB");
|
||||
Group* group = m_dbOrg->rootGroup();
|
||||
group->setUuid(Uuid::random());
|
||||
group->setNotes("I'm a note!");
|
||||
Entry* entry = new Entry();
|
||||
entry->setPassword(QString::fromUtf8("\xc3\xa4\xa3\xb6\xc3\xbc\xe9\x9b\xbb\xe7\xb4\x85"));
|
||||
entry->setUuid(Uuid::random());
|
||||
entry->attributes()->set("test", "protectedTest", true);
|
||||
QVERIFY(entry->attributes()->isProtected("test"));
|
||||
entry->attachments()->set("myattach.txt", QByteArray("this is an attachment"));
|
||||
entry->attachments()->set("aaa.txt", QByteArray("also an attachment"));
|
||||
entry->setGroup(group);
|
||||
Group* groupNew = new Group();
|
||||
groupNew->setUuid(Uuid::random());
|
||||
groupNew->setName("TESTGROUP");
|
||||
groupNew->setNotes("I'm a sub group note!");
|
||||
groupNew->setParent(group);
|
||||
|
||||
QBuffer buffer;
|
||||
buffer.open(QBuffer::ReadWrite);
|
||||
|
||||
KeePass2Writer writer;
|
||||
bool writeSuccess = writer.writeDatabase(&buffer, m_dbOrg);
|
||||
QVERIFY(writeSuccess);
|
||||
QVERIFY(!writer.hasError());
|
||||
buffer.seek(0);
|
||||
KeePass2Reader reader;
|
||||
m_dbTest = reader.readDatabase(&buffer, key);
|
||||
if (reader.hasError()) {
|
||||
QFAIL(reader.errorString().toUtf8().constData());
|
||||
}
|
||||
QVERIFY(m_dbTest);
|
||||
}
|
||||
|
||||
void TestKeePass2Writer::testBasic()
|
||||
{
|
||||
QCOMPARE(m_dbTest->metadata()->name(), m_dbOrg->metadata()->name());
|
||||
QVERIFY(m_dbTest->rootGroup());
|
||||
QCOMPARE(m_dbTest->rootGroup()->children()[0]->name(), m_dbOrg->rootGroup()->children()[0]->name());
|
||||
QCOMPARE(m_dbTest->rootGroup()->notes(), m_dbOrg->rootGroup()->notes());
|
||||
QCOMPARE(m_dbTest->rootGroup()->children()[0]->notes(), m_dbOrg->rootGroup()->children()[0]->notes());
|
||||
}
|
||||
|
||||
void TestKeePass2Writer::testProtectedAttributes()
|
||||
{
|
||||
QCOMPARE(m_dbTest->rootGroup()->entries().size(), 1);
|
||||
Entry* entry = m_dbTest->rootGroup()->entries().at(0);
|
||||
QCOMPARE(entry->attributes()->value("test"), QString("protectedTest"));
|
||||
QCOMPARE(entry->attributes()->isProtected("test"), true);
|
||||
}
|
||||
|
||||
void TestKeePass2Writer::testAttachments()
|
||||
{
|
||||
Entry* entry = m_dbTest->rootGroup()->entries().at(0);
|
||||
QCOMPARE(entry->attachments()->keys().size(), 2);
|
||||
QCOMPARE(entry->attachments()->value("myattach.txt"), QByteArray("this is an attachment"));
|
||||
QCOMPARE(entry->attachments()->value("aaa.txt"), QByteArray("also an attachment"));
|
||||
}
|
||||
|
||||
void TestKeePass2Writer::testNonAsciiPasswords()
|
||||
{
|
||||
QCOMPARE(m_dbTest->rootGroup()->entries()[0]->password(), m_dbOrg->rootGroup()->entries()[0]->password());
|
||||
}
|
||||
|
||||
void TestKeePass2Writer::testDeviceFailure()
|
||||
{
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey("test"));
|
||||
Database* db = new Database();
|
||||
db->setKey(key);
|
||||
// Disable compression so we write a predictable number of bytes.
|
||||
db->setCompressionAlgo(Database::CompressionNone);
|
||||
|
||||
Entry* entry = new Entry();
|
||||
entry->setParent(db->rootGroup());
|
||||
QByteArray attachment(4096, 'Z');
|
||||
entry->attachments()->set("test", attachment);
|
||||
|
||||
FailDevice failDevice(512);
|
||||
QVERIFY(failDevice.open(QIODevice::WriteOnly));
|
||||
KeePass2Writer writer;
|
||||
writer.writeDatabase(&failDevice, db);
|
||||
QVERIFY(writer.hasError());
|
||||
QCOMPARE(writer.errorString(), QString("FAILDEVICE"));
|
||||
|
||||
delete db;
|
||||
}
|
||||
|
||||
void TestKeePass2Writer::testRepair()
|
||||
{
|
||||
QString brokenDbFilename = QString(KEEPASSX_TEST_DATA_DIR).append("/bug392.kdbx");
|
||||
// master password = test
|
||||
// entry username: testuser\x10\x20AC
|
||||
// entry password: testpw
|
||||
CompositeKey key;
|
||||
key.addKey(PasswordKey("test"));
|
||||
|
||||
// test that we can't open the broken database
|
||||
KeePass2Reader reader;
|
||||
Database* dbBroken = reader.readDatabase(brokenDbFilename, key);
|
||||
QVERIFY(!dbBroken);
|
||||
QVERIFY(reader.hasError());
|
||||
|
||||
// test if we can repair the database
|
||||
KeePass2Repair repair;
|
||||
QFile file(brokenDbFilename);
|
||||
file.open(QIODevice::ReadOnly);
|
||||
auto result = repair.repairDatabase(&file, key);
|
||||
QCOMPARE(result.first, KeePass2Repair::RepairSuccess);
|
||||
Database* dbRepaired = result.second;
|
||||
QVERIFY(dbRepaired);
|
||||
|
||||
QCOMPARE(dbRepaired->rootGroup()->entries().size(), 1);
|
||||
QCOMPARE(dbRepaired->rootGroup()->entries().at(0)->username(), QString("testuser").append(QChar(0x20AC)));
|
||||
QCOMPARE(dbRepaired->rootGroup()->entries().at(0)->password(), QString("testpw"));
|
||||
delete dbRepaired;
|
||||
}
|
||||
|
||||
void TestKeePass2Writer::cleanupTestCase()
|
||||
{
|
||||
delete m_dbOrg;
|
||||
delete m_dbTest;
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSX_TESTKEEPASS2WRITER_H
|
||||
#define KEEPASSX_TESTKEEPASS2WRITER_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class Database;
|
||||
|
||||
class TestKeePass2Writer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void testBasic();
|
||||
void testProtectedAttributes();
|
||||
void testAttachments();
|
||||
void testNonAsciiPasswords();
|
||||
void testDeviceFailure();
|
||||
void testRepair();
|
||||
void cleanupTestCase();
|
||||
|
||||
private:
|
||||
Database* m_dbOrg;
|
||||
Database* m_dbTest;
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_TESTKEEPASS2WRITER_H
|
@ -1,64 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 or (at your option)
|
||||
* version 3 of the License.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSX_TESTKEEPASS2XMLREADER_H
|
||||
#define KEEPASSX_TESTKEEPASS2XMLREADER_H
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QBuffer>
|
||||
#include <QScopedPointer>
|
||||
|
||||
#include "core/Database.h"
|
||||
|
||||
|
||||
class TestKeePass2XmlReader : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
protected slots:
|
||||
virtual void initTestCase() = 0;
|
||||
|
||||
private slots:
|
||||
void testMetadata();
|
||||
void testCustomIcons();
|
||||
void testCustomData();
|
||||
void testGroupRoot();
|
||||
void testGroup1();
|
||||
void testGroup2();
|
||||
void testEntry1();
|
||||
void testEntry2();
|
||||
void testEntryHistory();
|
||||
void testDeletedObjects();
|
||||
void testBroken();
|
||||
void testBroken_data();
|
||||
void testEmptyUuids();
|
||||
void testInvalidXmlChars();
|
||||
void testRepairUuidHistoryItem();
|
||||
|
||||
protected:
|
||||
virtual Database* readDatabase(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString) = 0;
|
||||
virtual Database* readDatabase(QString path, bool strictMode, bool& hasError, QString& errorString) = 0;
|
||||
virtual void writeDatabase(QBuffer* buf, Database* db, bool& hasError, QString& errorString) = 0;
|
||||
static QDateTime genDT(int year, int month, int day, int hour, int min, int second);
|
||||
static QByteArray strToBytes(const QString& str);
|
||||
|
||||
QScopedPointer<Database> m_db;
|
||||
};
|
||||
|
||||
|
||||
#endif // KEEPASSX_TESTKEEPASS2XMLREADER_H
|
Loading…
Reference in New Issue
Block a user