diff --git a/tests/TestKdbx4.cpp b/tests/TestKdbx4.cpp index a000b2707..4521140dc 100644 --- a/tests/TestKdbx4.cpp +++ b/tests/TestKdbx4.cpp @@ -37,7 +37,9 @@ int main(int argc, char* argv[]) TestKdbx4Argon2 argon2Test; TestKdbx4AesKdf aesKdfTest; - return QTest::qExec(&argon2Test, argc, argv) | QTest::qExec(&aesKdfTest, argc, argv); + TestKdbx4Format kdbx4Test; + return QTest::qExec(&argon2Test, argc, argv) || QTest::qExec(&aesKdfTest, argc, argv) + || QTest::qExec(&kdbx4Test, argc, argv); } void TestKdbx4Argon2::initTestCaseImpl() @@ -119,8 +121,14 @@ void TestKdbx4Argon2::writeKdbx(QIODevice* device, Database* db, bool& hasError, QCOMPARE(writer.version(), KeePass2::FILE_VERSION_4); } +void TestKdbx4AesKdf::initTestCaseImpl() +{ + m_xmlDb->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_AES_KDBX4))); + m_kdbxSourceDb->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_AES_KDBX4))); +} + Q_DECLARE_METATYPE(QUuid) -void TestKdbx4Argon2::testFormat400() +void TestKdbx4Format::testFormat400() { QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Format400.kdbx"); auto key = QSharedPointer::create(); @@ -145,7 +153,7 @@ void TestKdbx4Argon2::testFormat400() QCOMPARE(entry->attachments()->value("Format400"), QByteArray("Format400\n")); } -void TestKdbx4Argon2::testFormat400Upgrade() +void TestKdbx4Format::testFormat400Upgrade() { QFETCH(QUuid, kdfUuid); QFETCH(QUuid, cipherUuid); @@ -203,7 +211,7 @@ void TestKdbx4Argon2::testFormat400Upgrade() } // clang-format off -void TestKdbx4Argon2::testFormat400Upgrade_data() +void TestKdbx4Format::testFormat400Upgrade_data() { QTest::addColumn("kdfUuid"); QTest::addColumn("cipherUuid"); @@ -242,7 +250,68 @@ void TestKdbx4Argon2::testFormat400Upgrade_data() } // clang-format on -void TestKdbx4Argon2::testUpgradeMasterKeyIntegrity() +void TestKdbx4Format::testFormat410Upgrade() +{ + Database db; + db.changeKdf(fastKdf(db.kdf())); + QCOMPARE(db.kdf()->uuid(), KeePass2::KDF_AES_KDBX3); + QVERIFY(!KeePass2Writer::implicitKDBXUpgradeNeeded(&db)); + + auto group1 = new Group(); + group1->setUuid(QUuid::createUuid()); + group1->setParent(db.rootGroup()); + + auto group2 = new Group(); + group2->setUuid(QUuid::createUuid()); + group2->setParent(db.rootGroup()); + + auto entry = new Entry(); + entry->setUuid(QUuid::createUuid()); + entry->setGroup(group1); + + // Groups with tags + group1->setTags("tag"); + QVERIFY(KeePass2Writer::implicitKDBXUpgradeNeeded(&db)); + group1->setTags(""); + QVERIFY(!KeePass2Writer::implicitKDBXUpgradeNeeded(&db)); + + // PasswordQuality flag set + entry->setExcludeFromReports(true); + QVERIFY(KeePass2Writer::implicitKDBXUpgradeNeeded(&db)); + entry->setExcludeFromReports(false); + QVERIFY(!KeePass2Writer::implicitKDBXUpgradeNeeded(&db)); + + // Previous parent group set on group + group1->setPreviousParentGroup(group2); + QCOMPARE(group1->previousParentGroup(), group2); + QVERIFY(KeePass2Writer::implicitKDBXUpgradeNeeded(&db)); + group1->setPreviousParentGroup(nullptr); + QVERIFY(!KeePass2Writer::implicitKDBXUpgradeNeeded(&db)); + + // Previous parent group set on entry + entry->setPreviousParentGroup(group2); + QCOMPARE(entry->previousParentGroup(), group2); + QVERIFY(KeePass2Writer::implicitKDBXUpgradeNeeded(&db)); + entry->setPreviousParentGroup(nullptr); + QVERIFY(!KeePass2Writer::implicitKDBXUpgradeNeeded(&db)); + + // Custom icons with name or modification date + Metadata::CustomIconData customIcon; + auto iconUuid = QUuid::createUuid(); + db.metadata()->addCustomIcon(iconUuid, customIcon); + QVERIFY(!KeePass2Writer::implicitKDBXUpgradeNeeded(&db)); + customIcon.name = "abc"; + db.metadata()->removeCustomIcon(iconUuid); + db.metadata()->addCustomIcon(iconUuid, customIcon); + QVERIFY(KeePass2Writer::implicitKDBXUpgradeNeeded(&db)); + customIcon.name.clear(); + customIcon.lastModified = Clock::currentDateTimeUtc(); + db.metadata()->removeCustomIcon(iconUuid); + db.metadata()->addCustomIcon(iconUuid, customIcon); + QVERIFY(KeePass2Writer::implicitKDBXUpgradeNeeded(&db)); +} + +void TestKdbx4Format::testUpgradeMasterKeyIntegrity() { QFETCH(QString, upgradeAction); QFETCH(quint32, expectedVersion); @@ -331,7 +400,7 @@ void TestKdbx4Argon2::testUpgradeMasterKeyIntegrity() } } -void TestKdbx4Argon2::testUpgradeMasterKeyIntegrity_data() +void TestKdbx4Format::testUpgradeMasterKeyIntegrity_data() { QTest::addColumn("upgradeAction"); QTest::addColumn("expectedVersion"); @@ -350,7 +419,7 @@ void TestKdbx4Argon2::testUpgradeMasterKeyIntegrity_data() QTest::newRow("Upgrade (implicit): entry-customdata") << QString("entry-customdata") << KeePass2::FILE_VERSION_4; } -void TestKdbx4Argon2::testCustomData() +void TestKdbx4Format::testCustomData() { Database db; @@ -443,9 +512,3 @@ void TestKdbx4Argon2::testCustomData() QCOMPARE(newEntry->customData()->value(customDataKey1), customData1); QCOMPARE(newEntry->customData()->value(customDataKey2), customData2); } - -void TestKdbx4AesKdf::initTestCaseImpl() -{ - m_xmlDb->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_AES_KDBX4))); - m_kdbxSourceDb->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_AES_KDBX4))); -} diff --git a/tests/TestKdbx4.h b/tests/TestKdbx4.h index 53f2295e0..0eddef6c0 100644 --- a/tests/TestKdbx4.h +++ b/tests/TestKdbx4.h @@ -24,14 +24,6 @@ class TestKdbx4Argon2 : public TestKeePass2Format { Q_OBJECT -private slots: - void testFormat400(); - void testFormat400Upgrade(); - void testFormat400Upgrade_data(); - void testUpgradeMasterKeyIntegrity(); - void testUpgradeMasterKeyIntegrity_data(); - void testCustomData(); - protected: void initTestCaseImpl() override; @@ -61,4 +53,21 @@ protected: void initTestCaseImpl() override; }; +/** + * KDF-independent KDBX4 format tests. + */ +class TestKdbx4Format : public QObject +{ + Q_OBJECT + +private slots: + void testFormat400(); + void testFormat400Upgrade(); + void testFormat400Upgrade_data(); + void testFormat410Upgrade(); + void testUpgradeMasterKeyIntegrity(); + void testUpgradeMasterKeyIntegrity_data(); + void testCustomData(); +}; + #endif // KEEPASSXC_TEST_KDBX4_H diff --git a/tests/TestKeePass2Format.cpp b/tests/TestKeePass2Format.cpp index 8829af0b7..dc928fa14 100644 --- a/tests/TestKeePass2Format.cpp +++ b/tests/TestKeePass2Format.cpp @@ -795,13 +795,13 @@ void TestKeePass2Format::testDuplicateAttachments() } /** - * @return fast "dummy" KDF + * Fast "dummy" KDF */ -QSharedPointer TestKeePass2Format::fastKdf(QSharedPointer kdf) const +QSharedPointer fastKdf(QSharedPointer kdf) { kdf->setRounds(1); - if (kdf->uuid() == KeePass2::KDF_ARGON2D) { + if (kdf->uuid() == KeePass2::KDF_ARGON2D or kdf->uuid() == KeePass2::KDF_ARGON2ID) { kdf->processParameters({{KeePass2::KDFPARAM_ARGON2_MEMORY, 1024}, {KeePass2::KDFPARAM_ARGON2_PARALLELISM, 1}}); } diff --git a/tests/TestKeePass2Format.h b/tests/TestKeePass2Format.h index e332d372d..759172ce6 100644 --- a/tests/TestKeePass2Format.h +++ b/tests/TestKeePass2Format.h @@ -23,7 +23,7 @@ #include "core/Database.h" /** - * Abstract base class for KeePass2 file format tests. + * Abstract base class for version-dependent KDBX file format tests. */ class TestKeePass2Format : public QObject { @@ -83,8 +83,6 @@ protected: QString& errorString) = 0; virtual void writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString) = 0; - QSharedPointer fastKdf(QSharedPointer kdf) const; - QSharedPointer m_xmlDb; QSharedPointer m_kdbxSourceDb; QSharedPointer m_kdbxTargetDb; @@ -93,4 +91,6 @@ private: QBuffer m_kdbxTargetBuffer; }; +QSharedPointer fastKdf(QSharedPointer kdf); + #endif // KEEPASSXC_TESTKEEPASS2FORMAT_H