diff --git a/src/format/Kdbx3Reader.cpp b/src/format/Kdbx3Reader.cpp index 638f84063..84f7db67e 100644 --- a/src/format/Kdbx3Reader.cpp +++ b/src/format/Kdbx3Reader.cpp @@ -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); diff --git a/src/format/Kdbx3Writer.cpp b/src/format/Kdbx3Writer.cpp index b0b44c6b2..c2fefff1b 100644 --- a/src/format/Kdbx3Writer.cpp +++ b/src/format/Kdbx3Writer.cpp @@ -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(&header, KeePass2::HeaderFieldID::CipherID, db->cipher().toByteArray())); CHECK_RETURN_FALSE(writeHeaderField(&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 diff --git a/src/format/KeePass2.h b/src/format/KeePass2.h index c376ecdf2..67779121f 100644 --- a/src/format/KeePass2.h +++ b/src/format/KeePass2.h @@ -22,6 +22,7 @@ #include #include #include +#include #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> CIPHERS; - extern const QList> 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> CIPHERS; +extern const QList> 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 kdfFromParameters(const QVariantMap& p); - QVariantMap kdfToParameters(QSharedPointer kdf); - QSharedPointer uuidToKdf(const Uuid& uuid); - Uuid kdfToUuid(QSharedPointer 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 kdfFromParameters(const QVariantMap& p); +QVariantMap kdfToParameters(QSharedPointer kdf); +QSharedPointer uuidToKdf(const Uuid& uuid); +Uuid kdfToUuid(QSharedPointer kdf); +ProtectedStreamAlgo idToProtectedStreamAlgo(quint32 id); + +} // namespace KeePass2 #endif // KEEPASSX_KEEPASS2_H diff --git a/src/format/KeePass2Repair.cpp b/src/format/KeePass2Repair.cpp index e2af16cea..47ed59dc9 100644 --- a/src/format/KeePass2Repair.cpp +++ b/src/format/KeePass2Repair.cpp @@ -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 { diff --git a/src/format/KeePass2Writer.cpp b/src/format/KeePass2Writer.cpp index 67aeec98f..7be3178ba 100644 --- a/src/format/KeePass2Writer.cpp +++ b/src/format/KeePass2Writer.cpp @@ -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; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4472fc27a..5e6043609 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -105,94 +105,91 @@ if(YUBIKEY_FOUND) endif() add_unit_test(NAME testgroup SOURCES TestGroup.cpp - LIBS ${TEST_LIBRARIES}) + LIBS ${TEST_LIBRARIES}) -add_unit_test(NAME testkdbx3xmlreader SOURCES TestKeePass2XmlReader.cpp TestKdbx3XmlReader.cpp - LIBS ${TEST_LIBRARIES}) +add_unit_test(NAME testkdbx2 SOURCES TestKdbx2.cpp + LIBS ${TEST_LIBRARIES}) -add_unit_test(NAME testkdbx4xmlreader SOURCES TestKeePass2XmlReader.cpp TestKdbx4XmlReader.cpp - LIBS ${TEST_LIBRARIES}) +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}) + LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testgroupmodel SOURCES TestGroupModel.cpp - LIBS testsupport ${TEST_LIBRARIES}) + LIBS testsupport ${TEST_LIBRARIES}) add_unit_test(NAME testentrymodel SOURCES TestEntryModel.cpp - LIBS testsupport ${TEST_LIBRARIES}) + LIBS testsupport ${TEST_LIBRARIES}) add_unit_test(NAME testcryptohash SOURCES TestCryptoHash.cpp - LIBS ${TEST_LIBRARIES}) + LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testsymmetriccipher SOURCES TestSymmetricCipher.cpp - LIBS ${TEST_LIBRARIES}) + LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testhashedblockstream SOURCES TestHashedBlockStream.cpp - LIBS testsupport ${TEST_LIBRARIES}) + LIBS testsupport ${TEST_LIBRARIES}) add_unit_test(NAME testkeepass2randomstream SOURCES TestKeePass2RandomStream.cpp - LIBS ${TEST_LIBRARIES}) + LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testmodified SOURCES TestModified.cpp - LIBS ${TEST_LIBRARIES}) + LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testdeletedobjects SOURCES TestDeletedObjects.cpp - LIBS ${TEST_LIBRARIES}) + LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testkeepass1reader SOURCES TestKeePass1Reader.cpp - LIBS ${TEST_LIBRARIES}) + LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testwildcardmatcher SOURCES TestWildcardMatcher.cpp - LIBS ${TEST_LIBRARIES}) + LIBS ${TEST_LIBRARIES}) if(WITH_XC_AUTOTYPE) add_unit_test(NAME testautotype SOURCES TestAutoType.cpp - LIBS ${TEST_LIBRARIES}) + LIBS ${TEST_LIBRARIES}) set_target_properties(testautotype PROPERTIES ENABLE_EXPORTS ON) endif() if(WITH_XC_SSHAGENT) add_unit_test(NAME testopensshkey SOURCES TestOpenSSHKey.cpp - LIBS sshagent ${TEST_LIBRARIES}) + LIBS sshagent ${TEST_LIBRARIES}) endif() add_unit_test(NAME testentry SOURCES TestEntry.cpp - LIBS ${TEST_LIBRARIES}) + LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testmerge SOURCES TestMerge.cpp - LIBS ${TEST_LIBRARIES}) + LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testtotp SOURCES TestTotp.cpp - LIBS ${TEST_LIBRARIES}) + LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testbase32 SOURCES TestBase32.cpp - LIBS ${TEST_LIBRARIES}) + LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testcsvparser SOURCES TestCsvParser.cpp - LIBS ${TEST_LIBRARIES}) + LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testrandom SOURCES TestRandom.cpp - LIBS ${TEST_LIBRARIES}) + LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testentrysearcher SOURCES TestEntrySearcher.cpp - LIBS ${TEST_LIBRARIES}) + LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testcsvexporter SOURCES TestCsvExporter.cpp - LIBS ${TEST_LIBRARIES}) + LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testykchallengeresponsekey - SOURCES TestYkChallengeResponseKey.cpp TestYkChallengeResponseKey.h - LIBS ${TEST_LIBRARIES}) + SOURCES TestYkChallengeResponseKey.cpp TestYkChallengeResponseKey.h + LIBS ${TEST_LIBRARIES}) add_unit_test(NAME testdatabase SOURCES TestDatabase.cpp - LIBS ${TEST_LIBRARIES}) + LIBS ${TEST_LIBRARIES}) if(WITH_GUI_TESTS) add_subdirectory(gui) diff --git a/tests/TestDeletedObjects.cpp b/tests/TestDeletedObjects.cpp index c8236f05b..63dab0edf 100644 --- a/tests/TestDeletedObjects.cpp +++ b/tests/TestDeletedObjects.cpp @@ -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); diff --git a/tests/TestExporter.cpp b/tests/TestExporter.cpp deleted file mode 100644 index 656b76c58..000000000 --- a/tests/TestExporter.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (C) 2014 Felix Geyer - * Copyright (C) 2014 Florian Geyer - * - * 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 . - */ - -#include "TestExporter.h" - -#include - -#include "core/ToDbExporter.h" -#include "core/Group.h" -#include "core/Metadata.h" -#include "crypto/Crypto.h" - -QTEST_GUILESS_MAIN(TestExporter) - -void TestExporter::initTestCase() -{ - QVERIFY(Crypto::init()); -} - -void TestExporter::testToDbExporter() -{ - QImage iconImage(1, 1, QImage::Format_RGB32); - iconImage.setPixel(0, 0, qRgb(1, 2, 3)); - Uuid iconUuid = Uuid::random(); - - QImage iconUnusedImage(1, 1, QImage::Format_RGB32); - iconUnusedImage.setPixel(0, 0, qRgb(1, 2, 3)); - Uuid iconUnusedUuid = Uuid::random(); - - Database* dbOrg = new Database(); - Group* groupOrg = new Group(); - groupOrg->setParent(dbOrg->rootGroup()); - groupOrg->setName("GTEST"); - Entry* entryOrg = new Entry(); - entryOrg->setGroup(groupOrg); - entryOrg->setTitle("ETEST"); - dbOrg->metadata()->addCustomIcon(iconUuid, iconImage); - dbOrg->metadata()->addCustomIcon(iconUnusedUuid, iconUnusedImage); - entryOrg->setIcon(iconUuid); - entryOrg->beginUpdate(); - entryOrg->setIcon(Entry::DefaultIconNumber); - entryOrg->endUpdate(); - - Database* dbExp = ToDbExporter().exportGroup(groupOrg); - - QCOMPARE(dbExp->rootGroup()->children().size(), 1); - Group* groupExp = dbExp->rootGroup()->children().at(0); - QVERIFY(groupExp != groupOrg); - QCOMPARE(groupExp->name(), groupOrg->name()); - QCOMPARE(groupExp->entries().size(), 1); - - Entry* entryExp = groupExp->entries().at(0); - QCOMPARE(entryExp->title(), entryOrg->title()); - QCOMPARE(dbExp->metadata()->customIcons().size(), 1); - QVERIFY(dbExp->metadata()->containsCustomIcon(iconUuid)); - QCOMPARE(entryExp->iconNumber(), entryOrg->iconNumber()); - - QCOMPARE(entryExp->historyItems().size(), 1); - QCOMPARE(entryExp->historyItems().at(0)->iconUuid(), iconUuid); - - delete dbOrg; - delete dbExp; -} diff --git a/tests/TestKdbx2.cpp b/tests/TestKdbx2.cpp new file mode 100644 index 000000000..f10f10b94 --- /dev/null +++ b/tests/TestKdbx2.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 . + */ + +#include "TestKdbx2.h" +#include "crypto/Crypto.h" +#include "keys/CompositeKey.h" +#include "keys/PasswordKey.h" +#include "format/KeePass2Reader.h" +#include "format/KeePass2Writer.h" +#include "core/Entry.h" +#include "core/Group.h" +#include "core/Metadata.h" +#include "config-keepassx-tests.h" + +#include +#include + +QTEST_GUILESS_MAIN(TestKdbx2) + +void TestKdbx2::initTestCase() +{ + QVERIFY(Crypto::init()); +} + +/** + * Helper method for verifying contents of the sample KDBX 2 file. + */ +void TestKdbx2::verifyKdbx2Db(Database* db) +{ + QVERIFY(db); + 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")); +} + +void TestKdbx2::testFormat200() +{ + QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Format200.kdbx"); + CompositeKey key; + key.addKey(PasswordKey("a")); + KeePass2Reader reader; + QScopedPointer db(reader.readDatabase(filename, key)); + QCOMPARE(reader.version(), KeePass2::FILE_VERSION_2 & KeePass2::FILE_VERSION_CRITICAL_MASK); + + QVERIFY(!reader.hasError()); + verifyKdbx2Db(db.data()); +} + +void TestKdbx2::testFormat200Upgrade() +{ + QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Format200.kdbx"); + CompositeKey key; + key.addKey(PasswordKey("a")); + KeePass2Reader reader; + QScopedPointer db(reader.readDatabase(filename, key)); + QCOMPARE(reader.version(), KeePass2::FILE_VERSION_2 & KeePass2::FILE_VERSION_CRITICAL_MASK); + QCOMPARE(db->kdf()->uuid(), KeePass2::KDF_AES_KDBX3); + + QBuffer buffer; + buffer.open(QBuffer::ReadWrite); + + // write KDBX 3 to upgrade it + KeePass2Writer writer; + writer.writeDatabase(&buffer, db.data()); + if (writer.hasError()) { + QFAIL(qPrintable(QString("Error while writing database: %1").arg(writer.errorString()))); + } + + // read buffer back + buffer.seek(0); + QScopedPointer targetDb(reader.readDatabase(&buffer, key)); + if (reader.hasError()) { + QFAIL(qPrintable(QString("Error while reading database: %1").arg(reader.errorString()))); + } + + // database should now be upgraded to KDBX 3 without data loss + verifyKdbx2Db(targetDb.data()); + QCOMPARE(reader.version(), KeePass2::FILE_VERSION_3_1 & KeePass2::FILE_VERSION_CRITICAL_MASK); + QCOMPARE(targetDb->kdf()->uuid(), KeePass2::KDF_AES_KDBX3); +} diff --git a/tests/TestExporter.h b/tests/TestKdbx2.h similarity index 69% rename from tests/TestExporter.h rename to tests/TestKdbx2.h index 8c9945252..9b5f5c5ff 100644 --- a/tests/TestExporter.h +++ b/tests/TestKdbx2.h @@ -1,6 +1,5 @@ /* - * Copyright (C) 2014 Felix Geyer - * Copyright (C) 2014 Florian Geyer + * Copyright (C) 2018 KeePassXC Team * * 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 @@ -16,18 +15,24 @@ * along with this program. If not, see . */ -#ifndef KEEPASSX_TESTEXPORTER_H -#define KEEPASSX_TESTEXPORTER_H +#ifndef KEEPASSXC_TEST_KDBX2_H +#define KEEPASSXC_TEST_KDBX2_H #include -class TestExporter : public QObject +class Database; + +class TestKdbx2 : public QObject { - Q_OBJECT +Q_OBJECT private slots: void initTestCase(); - void testToDbExporter(); + void testFormat200(); + void testFormat200Upgrade(); + +private: + void verifyKdbx2Db(Database* db); }; -#endif // KEEPASSX_TESTEXPORTER_H +#endif // KEEPASSXC_TEST_KDBX2_H diff --git a/tests/TestKdbx3.cpp b/tests/TestKdbx3.cpp new file mode 100644 index 000000000..210ddc752 --- /dev/null +++ b/tests/TestKdbx3.cpp @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 . + */ + +#include "TestKdbx3.h" +#include "core/Metadata.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_GUILESS_MAIN(TestKdbx3) + +void TestKdbx3::initTestCaseImpl() +{ + +} + +Database* TestKdbx3::readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString) +{ + KdbxXmlReader reader(KeePass2::FILE_VERSION_3_1); + reader.setStrictMode(strictMode); + auto db = reader.readDatabase(path); + hasError = reader.hasError(); + errorString = reader.errorString(); + return db; +} + +Database* TestKdbx3::readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString) +{ + KdbxXmlReader reader(KeePass2::FILE_VERSION_3_1); + reader.setStrictMode(strictMode); + auto db = reader.readDatabase(buf); + hasError = reader.hasError(); + errorString = reader.errorString(); + return db; +} + +void TestKdbx3::writeXml(QBuffer* buf, Database* db, bool& hasError, QString& errorString) +{ + 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& 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& 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 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 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 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 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 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 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 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")); +} diff --git a/tests/TestKdbx3.h b/tests/TestKdbx3.h new file mode 100644 index 000000000..07c1c5503 --- /dev/null +++ b/tests/TestKdbx3.h @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 . + */ + +#ifndef KEEPASSXC_TEST_KDBX3_H +#define KEEPASSXC_TEST_KDBX3_H + +#include "TestKeePass2Format.h" + +class TestKdbx3 : public TestKeePass2Format +{ +Q_OBJECT + +private slots: + void testNonAscii(); + void testCompressed(); + void testProtectedStrings(); + void testBrokenHeaderHash(); + void testFormat300(); + void testKdbxRepair(); + +protected: + 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& db, + bool& hasError, QString& errorString) override; + void readKdbx(const QString& path, CompositeKey const& key, QScopedPointer& db, + bool& hasError, QString& errorString) override; + void writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString) override; + +}; + +#endif // KEEPASSXC_TEST_KDBX3_H diff --git a/tests/TestKdbx3XmlReader.cpp b/tests/TestKdbx3XmlReader.cpp deleted file mode 100644 index d4ce58542..000000000 --- a/tests/TestKdbx3XmlReader.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2017 KeePassXC Team - * - * 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 . - */ - -#include - -#include "TestKeePass2XmlReader.h" - -QTEST_GUILESS_MAIN(TestKdbx3XmlReader) diff --git a/tests/TestKdbx4.cpp b/tests/TestKdbx4.cpp new file mode 100644 index 000000000..0a21215ac --- /dev/null +++ b/tests/TestKdbx4.cpp @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 . + */ + +#include "TestKdbx4.h" +#include "core/Metadata.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" + +#include + +QTEST_GUILESS_MAIN(TestKdbx4) + +void TestKdbx4::initTestCaseImpl() +{ + m_xmlDb->changeKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2)); + m_kdbxSourceDb->changeKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2)); +} + +Database* TestKdbx4::readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString) +{ + KdbxXmlReader reader(KeePass2::FILE_VERSION_4); + reader.setStrictMode(strictMode); + auto db = reader.readDatabase(path); + hasError = reader.hasError(); + errorString = reader.errorString(); + return db; +} + +Database* TestKdbx4::readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString) +{ + KdbxXmlReader reader(KeePass2::FILE_VERSION_4); + reader.setStrictMode(strictMode); + auto db = reader.readDatabase(buf); + hasError = reader.hasError(); + errorString = reader.errorString(); + return db; +} + +void TestKdbx4::writeXml(QBuffer* buf, Database* db, bool& hasError, QString& errorString) +{ + 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& 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& 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 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 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 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("kdfUuid"); + QTest::addColumn("cipherUuid"); + QTest::addColumn("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; +} diff --git a/tests/TestKdbx4.h b/tests/TestKdbx4.h new file mode 100644 index 000000000..59864b70e --- /dev/null +++ b/tests/TestKdbx4.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 . + */ + +#ifndef KEEPASSXC_TEST_KDBX4_H +#define KEEPASSXC_TEST_KDBX4_H + +#include "TestKeePass2Format.h" + +class TestKdbx4 : public TestKeePass2Format +{ +Q_OBJECT + +private slots: + void testFormat400(); + void testFormat400Upgrade(); + void testFormat400Upgrade_data(); + +protected: + 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& db, + bool& hasError, QString& errorString) override; + void readKdbx(QIODevice* device, CompositeKey const& key, QScopedPointer& db, + bool& hasError, QString& errorString) override; + void writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString) override; +}; + +#endif // KEEPASSXC_TEST_KDBX4_H diff --git a/tests/TestKdbx4XmlReader.cpp b/tests/TestKdbx4XmlReader.cpp deleted file mode 100644 index c1a0b42ee..000000000 --- a/tests/TestKdbx4XmlReader.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2017 KeePassXC Team - * - * 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 . - */ - -#include - -#include "TestKeePass2XmlReader.h" - -QTEST_GUILESS_MAIN(TestKdbx4XmlReader) diff --git a/tests/TestKeePass2XmlReader.cpp b/tests/TestKeePass2Format.cpp similarity index 58% rename from tests/TestKeePass2XmlReader.cpp rename to tests/TestKeePass2Format.cpp index 4970fe927..6c3777387 100644 --- a/tests/TestKeePass2XmlReader.cpp +++ b/tests/TestKeePass2Format.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Felix Geyer + * Copyright (C) 2018 KeePassXC Team * * 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,183 +15,105 @@ * along with this program. If not, see . */ -#include "TestKeePass2XmlReader.h" +#include "TestKeePass2Format.h" -#include -#include -#include -#include - -#include "core/Database.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" -namespace QTest { - template<> - char* toString(const Uuid& uuid) - { - QByteArray ba = "Uuid("; - ba += uuid.toBase64().toLatin1().constData(); - ba += ")"; - return qstrdup(ba.constData()); - } +#include +#include - 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 TestKeePass2XmlReader::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 TestKeePass2XmlReader::strToBytes(const QString& str) -{ - QByteArray result; - - for (int i = 0; i < str.size(); i++) { - result.append(str.at(i).unicode() >> 8); - result.append(str.at(i).unicode() & 0xFF); - } - - return result; -} - -void TestKdbx3XmlReader::initTestCase() +void TestKeePass2Format::initTestCase() { QVERIFY(Crypto::init()); - KdbxXmlReader reader(KeePass2::FILE_VERSION_3); - reader.setStrictMode(true); - QString xmlFile = QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.xml"); - m_db = reader.readDatabase(xmlFile); - QVERIFY(m_db); - QVERIFY(!reader.hasError()); + // 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()); + + // 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))); + } + + // call sub class init method + initTestCaseImpl(); } -void TestKdbx4XmlReader::initTestCase() +void TestKeePass2Format::testXmlMetadata() { - QVERIFY(Crypto::init()); - - KdbxXmlReader reader(KeePass2::FILE_VERSION_3); - reader.setStrictMode(true); - QString xmlFile = QString(KEEPASSX_TEST_DATA_DIR).append("/NewDatabase.xml"); - m_db = reader.readDatabase(xmlFile); - QVERIFY(m_db); - QVERIFY(!reader.hasError()); + 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); } -void TestKdbx3XmlReader::readDatabase(QString path, bool strictMode, Database*& db, bool& hasError, QString& errorString) +void TestKeePass2Format::testXmlCustomIcons() { - KdbxXmlReader reader(KeePass2::FILE_VERSION_3); - reader.setStrictMode(strictMode); - db = reader.readDatabase(path); - hasError = reader.hasError(); - errorString = reader.errorString(); -} - -void TestKdbx3XmlReader::readDatabase(QBuffer* buf, bool strictMode, Database*& db, bool& hasError, QString& errorString) -{ - KdbxXmlReader reader(KeePass2::FILE_VERSION_3); - reader.setStrictMode(strictMode); - db = reader.readDatabase(buf); - hasError = reader.hasError(); - errorString = reader.errorString(); -} - -void TestKdbx3XmlReader::writeDatabase(QBuffer* buf, Database* db, bool& hasError, QString& errorString) -{ - KdbxXmlWriter writer(KeePass2::FILE_VERSION_3); - writer.writeDatabase(buf, db); - hasError = writer.hasError(); - errorString = writer.errorString(); -} - -void TestKdbx4XmlReader::readDatabase(QString path, bool strictMode, Database*& db, bool& hasError, QString& errorString) -{ - KdbxXmlReader reader(KeePass2::FILE_VERSION_3); - reader.setStrictMode(strictMode); - db = reader.readDatabase(path); - hasError = reader.hasError(); - errorString = reader.errorString(); -} - -void TestKdbx4XmlReader::readDatabase(QBuffer* buf, bool strictMode, Database*& db, bool& hasError, QString& errorString) -{ - KdbxXmlReader reader(KeePass2::FILE_VERSION_3); - reader.setStrictMode(strictMode); - db = reader.readDatabase(buf); - hasError = reader.hasError(); - errorString = reader.errorString(); -} - -void TestKdbx4XmlReader::writeDatabase(QBuffer* buf, Database* db, bool& hasError, QString& errorString) -{ - KdbxXmlWriter writer(KeePass2::FILE_VERSION_3); - writer.writeDatabase(buf, db); - hasError = writer.hasError(); - errorString = writer.errorString(); -} - -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); @@ -205,18 +127,18 @@ void TestKeePass2XmlReader::testCustomIcons() } } -void TestKeePass2XmlReader::testCustomData() +void TestKeePass2Format::testXmlCustomData() { - QHash customFields = m_db->metadata()->customFields(); + QHash 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")); @@ -238,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")); @@ -259,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")); @@ -279,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); @@ -341,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); @@ -389,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); { @@ -415,9 +337,9 @@ void TestKeePass2XmlReader::testEntryHistory() } } -void TestKeePass2XmlReader::testDeletedObjects() +void TestKeePass2Format::testXmlDeletedObjects() { - QList objList = m_db->deletedObjects(); + QList objList = m_xmlDb->deletedObjects(); DeletedObject delObj; delObj = objList.takeFirst(); @@ -431,72 +353,63 @@ void TestKeePass2XmlReader::testDeletedObjects() QVERIFY(objList.isEmpty()); } -void TestKeePass2XmlReader::testBroken() +void TestKeePass2Format::testXmlBroken() { QFETCH(QString, baseName); QFETCH(bool, strictMode); QFETCH(bool, expectError); - QString xmlFile = QString("%1/%2.xml").arg(KEEPASSX_TEST_DATA_DIR, baseName); QVERIFY(QFile::exists(xmlFile)); bool hasError; QString errorString; - Database* db; - readDatabase(xmlFile, strictMode, db, hasError, errorString); + QScopedPointer db(readXml(xmlFile, strictMode, hasError, errorString)); if (hasError) { qWarning("Reader error: %s", qPrintable(errorString)); } QCOMPARE(hasError, expectError); - if (db) { - delete db; - } } -void TestKeePass2XmlReader::testBroken_data() +void TestKeePass2Format::testXmlBroken_data() { QTest::addColumn("baseName"); QTest::addColumn("strictMode"); QTest::addColumn("expectError"); - // testfile strict? error? - QTest::newRow("BrokenNoGroupUuid (strict)") << "BrokenNoGroupUuid" << true << true; - QTest::newRow("BrokenNoGroupUuid (not strict)") << "BrokenNoGroupUuid" << false << false; - QTest::newRow("BrokenNoEntryUuid (strict)") << "BrokenNoEntryUuid" << true << true; - QTest::newRow("BrokenNoEntryUuid (not strict)") << "BrokenNoEntryUuid" << false << false; - QTest::newRow("BrokenNoRootGroup (strict)") << "BrokenNoRootGroup" << true << true; - QTest::newRow("BrokenNoRootGroup (not strict)") << "BrokenNoRootGroup" << false << true; - QTest::newRow("BrokenTwoRoots (strict)") << "BrokenTwoRoots" << true << true; - QTest::newRow("BrokenTwoRoots (not strict)") << "BrokenTwoRoots" << false << true; - QTest::newRow("BrokenTwoRootGroups (strict)") << "BrokenTwoRootGroups" << true << true; - QTest::newRow("BrokenTwoRootGroups (not strict)") << "BrokenTwoRootGroups" << false << true; - QTest::newRow("BrokenGroupReference (strict)") << "BrokenGroupReference" << true << false; - QTest::newRow("BrokenGroupReference (not strict)") << "BrokenGroupReference" << false << false; - QTest::newRow("BrokenDeletedObjects (strict)") << "BrokenDeletedObjects" << true << true; - QTest::newRow("BrokenDeletedObjects (not strict)") << "BrokenDeletedObjects" << false << false; - QTest::newRow("BrokenDifferentEntryHistoryUuid (strict)") << "BrokenDifferentEntryHistoryUuid" << true << true; + // testfile strict? error? + QTest::newRow("BrokenNoGroupUuid (strict)") << "BrokenNoGroupUuid" << true << true; + QTest::newRow("BrokenNoGroupUuid (not strict)") << "BrokenNoGroupUuid" << false << false; + QTest::newRow("BrokenNoEntryUuid (strict)") << "BrokenNoEntryUuid" << true << true; + QTest::newRow("BrokenNoEntryUuid (not strict)") << "BrokenNoEntryUuid" << false << false; + QTest::newRow("BrokenNoRootGroup (strict)") << "BrokenNoRootGroup" << true << true; + QTest::newRow("BrokenNoRootGroup (not strict)") << "BrokenNoRootGroup" << false << true; + QTest::newRow("BrokenTwoRoots (strict)") << "BrokenTwoRoots" << true << true; + QTest::newRow("BrokenTwoRoots (not strict)") << "BrokenTwoRoots" << false << true; + QTest::newRow("BrokenTwoRootGroups (strict)") << "BrokenTwoRootGroups" << true << true; + QTest::newRow("BrokenTwoRootGroups (not strict)") << "BrokenTwoRootGroups" << false << true; + QTest::newRow("BrokenGroupReference (strict)") << "BrokenGroupReference" << true << false; + QTest::newRow("BrokenGroupReference (not strict)") << "BrokenGroupReference" << false << false; + QTest::newRow("BrokenDeletedObjects (strict)") << "BrokenDeletedObjects" << true << true; + QTest::newRow("BrokenDeletedObjects (not strict)") << "BrokenDeletedObjects" << false << false; + QTest::newRow("BrokenDifferentEntryHistoryUuid (strict)") << "BrokenDifferentEntryHistoryUuid" << true << true; 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)); - Database* dbp; bool hasError; QString errorString; - readDatabase(xmlFile, true, dbp, hasError, errorString); + QScopedPointer dbp(readXml(xmlFile, true, hasError, errorString)); if (hasError) { qWarning("Reader error: %s", qPrintable(errorString)); } QVERIFY(!hasError); - if (dbp) { - delete dbp; - } } -void TestKeePass2XmlReader::testInvalidXmlChars() +void TestKeePass2Format::testXmlInvalidXmlChars() { QScopedPointer dbWrite(new Database()); @@ -514,9 +427,10 @@ void TestKeePass2XmlReader::testInvalidXmlChars() QString strSingleLowSurrogate2 = QString().append(QChar((0x31))).append(QChar(0xDC37)).append(QChar(0x32)); QString strLowLowSurrogate = QString().append(QChar(0xDC37)).append(QChar(0xDC37)); QString strSurrogateValid1 = QString().append(QChar(0xD801)).append(QChar(0xDC37)); - QString strSurrogateValid2 = QString().append(QChar(0x31)).append(QChar(0xD801)).append(QChar(0xDC37)).append(QChar(0x32)); + QString strSurrogateValid2 = QString().append(QChar(0x31)).append(QChar(0xD801)).append(QChar(0xDC37)) + .append(QChar(0x32)); - Entry* entry = new Entry(); + auto entry = new Entry(); entry->setUuid(Uuid::random()); entry->setGroup(dbWrite->rootGroup()); entry->attributes()->set("PlainInvalid", strPlainInvalid); @@ -534,17 +448,16 @@ 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); - Database* dbRead; - readDatabase(&buffer, true, dbRead, hasError, errorString); + QScopedPointer dbRead(readXml(&buffer, true, hasError, errorString)); if (hasError) { qWarning("Database read error: %s", qPrintable(errorString)); } QVERIFY(!hasError); - QVERIFY(dbRead); + QVERIFY(dbRead.data()); QCOMPARE(dbRead->rootGroup()->entries().size(), 1); Entry* entryRead = dbRead->rootGroup()->entries().at(0); EntryAttributes* attrRead = entryRead->attributes(); @@ -559,27 +472,20 @@ void TestKeePass2XmlReader::testInvalidXmlChars() QCOMPARE(strToBytes(attrRead->value("LowLowSurrogate")), QByteArray()); QCOMPARE(strToBytes(attrRead->value("SurrogateValid1")), strToBytes(strSurrogateValid1)); QCOMPARE(strToBytes(attrRead->value("SurrogateValid2")), strToBytes(strSurrogateValid2)); - - if (dbRead) { - delete dbRead; - } } -void TestKeePass2XmlReader::testRepairUuidHistoryItem() +void TestKeePass2Format::testXmlRepairUuidHistoryItem() { QString xmlFile = QString("%1/%2.xml").arg(KEEPASSX_TEST_DATA_DIR, "BrokenDifferentEntryHistoryUuid"); QVERIFY(QFile::exists(xmlFile)); - Database* db; bool hasError; QString errorString; - readDatabase(xmlFile, true, db, hasError, errorString); + QScopedPointer db(readXml(xmlFile, false, hasError, errorString)); if (hasError) { qWarning("Database read error: %s", qPrintable(errorString)); } QVERIFY(!hasError); - - QList entries = db->rootGroup()->entries(); QCOMPARE(entries.size(), 1); Entry* entry = entries.at(0); @@ -591,13 +497,126 @@ void TestKeePass2XmlReader::testRepairUuidHistoryItem() QVERIFY(!entry->uuid().isNull()); QVERIFY(!historyItem->uuid().isNull()); QCOMPARE(historyItem->uuid(), entry->uuid()); - - if (db) { - delete db; - } } -void TestKeePass2XmlReader::cleanupTestCase() +void TestKeePass2Format::testReadBackTargetDb() { - delete m_db; + // 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 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(i.unicode() >> 8)); + result.append(static_cast(i.unicode() & 0xFF)); + } + + return result; } diff --git a/tests/TestKeePass2Format.h b/tests/TestKeePass2Format.h new file mode 100644 index 000000000..11420bab0 --- /dev/null +++ b/tests/TestKeePass2Format.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2018 KeePassXC Team + * + * 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 . + */ + +#ifndef KEEPASSXC_TESTKEEPASS2FORMAT_H +#define KEEPASSXC_TESTKEEPASS2FORMAT_H + +#include +#include +#include +#include + +#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& db, + bool& hasError, QString& errorString) = 0; + virtual void readKdbx(const QString& path, CompositeKey const& key, QScopedPointer& 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 m_xmlDb; + QScopedPointer m_kdbxSourceDb; + QScopedPointer m_kdbxTargetDb; + +private: + QBuffer m_kdbxTargetBuffer; +}; + +#endif // KEEPASSXC_TESTKEEPASS2FORMAT_H diff --git a/tests/TestKeePass2Reader.cpp b/tests/TestKeePass2Reader.cpp deleted file mode 100644 index 86dc6db2c..000000000 --- a/tests/TestKeePass2Reader.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* - * Copyright (C) 2010 Felix Geyer - * - * 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 . - */ - -#include "TestKeePass2Reader.h" - -#include - -#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")); -} diff --git a/tests/TestKeePass2Reader.h b/tests/TestKeePass2Reader.h deleted file mode 100644 index 6ba9b0dc1..000000000 --- a/tests/TestKeePass2Reader.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (C) 2010 Felix Geyer - * - * 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 . - */ - -#ifndef KEEPASSX_TESTKEEPASS2READER_H -#define KEEPASSX_TESTKEEPASS2READER_H - -#include - -class TestKeePass2Reader : public QObject -{ - Q_OBJECT - -private slots: - void initTestCase(); - void testNonAscii(); - void testCompressed(); - void testProtectedStrings(); - void testBrokenHeaderHash(); - void testFormat200(); - void testFormat300(); - void testFormat400(); -}; - -#endif // KEEPASSX_TESTKEEPASS2READER_H diff --git a/tests/TestKeePass2Writer.cpp b/tests/TestKeePass2Writer.cpp deleted file mode 100644 index 30d0cbf5a..000000000 --- a/tests/TestKeePass2Writer.cpp +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright (C) 2010 Felix Geyer - * - * 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 . - */ - -#include "TestKeePass2Writer.h" - -#include -#include -#include - -#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; -} diff --git a/tests/TestKeePass2Writer.h b/tests/TestKeePass2Writer.h deleted file mode 100644 index 36a51dce6..000000000 --- a/tests/TestKeePass2Writer.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2010 Felix Geyer - * - * 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 . - */ - -#ifndef KEEPASSX_TESTKEEPASS2WRITER_H -#define KEEPASSX_TESTKEEPASS2WRITER_H - -#include - -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 diff --git a/tests/TestKeePass2XmlReader.h b/tests/TestKeePass2XmlReader.h deleted file mode 100644 index e07f575b3..000000000 --- a/tests/TestKeePass2XmlReader.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (C) 2010 Felix Geyer - * - * 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 . - */ - -#ifndef KEEPASSX_TESTKEEPASS2XMLREADER_H -#define KEEPASSX_TESTKEEPASS2XMLREADER_H - -#include -#include -#include - -class Database; - -class TestKeePass2XmlReader : public QObject -{ - Q_OBJECT - -protected slots: - virtual void initTestCase() = 0; - 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(); - void cleanupTestCase(); - -protected: - virtual void readDatabase(QBuffer* buf, bool strictMode, Database*& db, bool& hasError, QString& errorString) = 0; - virtual void readDatabase(QString path, bool strictMode, Database*& db, 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); - - Database* m_db; -}; - -class TestKdbx3XmlReader : public TestKeePass2XmlReader -{ - Q_OBJECT - -private slots: - virtual void initTestCase() override; - -protected: - virtual void readDatabase(QBuffer* buf, bool strictMode, Database*& db, bool& hasError, QString& errorString) override; - virtual void readDatabase(QString path, bool strictMode, Database*& db, bool& hasError, QString& errorString) override; - virtual void writeDatabase(QBuffer* buf, Database* db, bool& hasError, QString& errorString) override; -}; - -class TestKdbx4XmlReader : public TestKeePass2XmlReader -{ - Q_OBJECT - -private slots: - virtual void initTestCase() override; - -protected: - virtual void readDatabase(QBuffer* buf, bool strictMode, Database*& db, bool& hasError, QString& errorString) override; - virtual void readDatabase(QString path, bool strictMode, Database*& db, bool& hasError, QString& errorString) override; - virtual void writeDatabase(QBuffer* buf, Database* db, bool& hasError, QString& errorString) override; -}; - -#endif // KEEPASSX_TESTKEEPASS2XMLREADER_H