TestKdbx3.cpp 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. /*
  2. * Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 2 or (at your option)
  7. * version 3 of the License.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #include "TestKdbx3.h"
  18. #include "config-keepassx-tests.h"
  19. #include "core/Metadata.h"
  20. #include "format/KdbxXmlReader.h"
  21. #include "format/KdbxXmlWriter.h"
  22. #include "format/KeePass2.h"
  23. #include "format/KeePass2Reader.h"
  24. #include "format/KeePass2Writer.h"
  25. #include "keys/PasswordKey.h"
  26. #include <QTest>
  27. QTEST_GUILESS_MAIN(TestKdbx3)
  28. void TestKdbx3::initTestCaseImpl()
  29. {
  30. m_xmlDb->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_AES_KDBX3)));
  31. m_kdbxSourceDb->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_AES_KDBX3)));
  32. }
  33. QSharedPointer<Database> TestKdbx3::readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString)
  34. {
  35. KdbxXmlReader reader(KeePass2::FILE_VERSION_3_1);
  36. reader.setStrictMode(strictMode);
  37. auto db = reader.readDatabase(path);
  38. hasError = reader.hasError();
  39. errorString = reader.errorString();
  40. return db;
  41. }
  42. QSharedPointer<Database> TestKdbx3::readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString)
  43. {
  44. KdbxXmlReader reader(KeePass2::FILE_VERSION_3_1);
  45. reader.setStrictMode(strictMode);
  46. auto db = reader.readDatabase(buf);
  47. hasError = reader.hasError();
  48. errorString = reader.errorString();
  49. return db;
  50. }
  51. void TestKdbx3::writeXml(QBuffer* buf, Database* db, bool& hasError, QString& errorString)
  52. {
  53. KdbxXmlWriter writer(KeePass2::FILE_VERSION_3_1);
  54. writer.writeDatabase(buf, db);
  55. hasError = writer.hasError();
  56. errorString = writer.errorString();
  57. }
  58. void TestKdbx3::readKdbx(QIODevice* device,
  59. QSharedPointer<const CompositeKey> key,
  60. QSharedPointer<Database> db,
  61. bool& hasError,
  62. QString& errorString)
  63. {
  64. KeePass2Reader reader;
  65. reader.readDatabase(device, key, db.data());
  66. hasError = reader.hasError();
  67. if (hasError) {
  68. errorString = reader.errorString();
  69. }
  70. QCOMPARE(reader.version(), KeePass2::FILE_VERSION_3_1);
  71. }
  72. void TestKdbx3::writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString)
  73. {
  74. KeePass2Writer writer;
  75. hasError = writer.writeDatabase(device, db);
  76. hasError = writer.hasError();
  77. if (hasError) {
  78. errorString = writer.errorString();
  79. }
  80. QCOMPARE(writer.version(), KeePass2::FILE_VERSION_3_1);
  81. }
  82. void TestKdbx3::testFormat300()
  83. {
  84. QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Format300.kdbx");
  85. auto key = QSharedPointer<CompositeKey>::create();
  86. key->addKey(QSharedPointer<PasswordKey>::create("a"));
  87. KeePass2Reader reader;
  88. auto db = QSharedPointer<Database>::create();
  89. QVERIFY(reader.readDatabase(filename, key, db.data()));
  90. QCOMPARE(reader.version(), KeePass2::FILE_VERSION_3);
  91. QVERIFY(db.data());
  92. QVERIFY(!reader.hasError());
  93. QCOMPARE(db->rootGroup()->name(), QString("Format300"));
  94. QCOMPARE(db->metadata()->name(), QString("Test Database Format 0x00030000"));
  95. }
  96. void TestKdbx3::testNonAscii()
  97. {
  98. QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/NonAscii.kdbx");
  99. auto key = QSharedPointer<CompositeKey>::create();
  100. key->addKey(QSharedPointer<PasswordKey>::create(QString::fromUtf8("\xce\x94\xc3\xb6\xd8\xb6")));
  101. KeePass2Reader reader;
  102. auto db = QSharedPointer<Database>::create();
  103. QVERIFY(db->open(filename, key, nullptr));
  104. QVERIFY(db.data());
  105. QVERIFY(!reader.hasError());
  106. QCOMPARE(db->metadata()->name(), QString("NonAsciiTest"));
  107. QCOMPARE(db->compressionAlgorithm(), Database::CompressionNone);
  108. }
  109. void TestKdbx3::testCompressed()
  110. {
  111. QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Compressed.kdbx");
  112. auto key = QSharedPointer<CompositeKey>::create();
  113. key->addKey(QSharedPointer<PasswordKey>::create(""));
  114. KeePass2Reader reader;
  115. auto db = QSharedPointer<Database>::create();
  116. QVERIFY(db->open(filename, key, nullptr));
  117. QVERIFY(db.data());
  118. QVERIFY(!reader.hasError());
  119. QCOMPARE(db->metadata()->name(), QString("Compressed"));
  120. QCOMPARE(db->compressionAlgorithm(), Database::CompressionGZip);
  121. }
  122. void TestKdbx3::testProtectedStrings()
  123. {
  124. QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/ProtectedStrings.kdbx");
  125. auto key = QSharedPointer<CompositeKey>::create();
  126. key->addKey(QSharedPointer<PasswordKey>::create("masterpw"));
  127. KeePass2Reader reader;
  128. auto db = QSharedPointer<Database>::create();
  129. QVERIFY(db->open(filename, key, nullptr));
  130. QVERIFY(db.data());
  131. QVERIFY(!reader.hasError());
  132. QCOMPARE(db->metadata()->name(), QString("Protected Strings Test"));
  133. Entry* entry = db->rootGroup()->entries().at(0);
  134. QCOMPARE(entry->title(), QString("Sample Entry"));
  135. QCOMPARE(entry->username(), QString("Protected User Name"));
  136. QCOMPARE(entry->password(), QString("ProtectedPassword"));
  137. QCOMPARE(entry->attributes()->value("TestProtected"), QString("ABC"));
  138. QCOMPARE(entry->attributes()->value("TestUnprotected"), QString("DEF"));
  139. QVERIFY(db->metadata()->protectPassword());
  140. QVERIFY(entry->attributes()->isProtected("TestProtected"));
  141. QVERIFY(!entry->attributes()->isProtected("TestUnprotected"));
  142. }
  143. void TestKdbx3::testBrokenHeaderHash()
  144. {
  145. // The protected stream key has been modified in the header.
  146. // Make sure the database won't open.
  147. QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/BrokenHeaderHash.kdbx");
  148. auto key = QSharedPointer<CompositeKey>::create();
  149. key->addKey(QSharedPointer<PasswordKey>::create(""));
  150. auto db = QSharedPointer<Database>::create();
  151. QVERIFY(!db->open(filename, key, nullptr));
  152. }
  153. void TestKdbx3::testAttachmentIndexStability()
  154. {
  155. QScopedPointer<Database> db(new Database());
  156. db->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_AES_KDBX3)));
  157. auto compositeKey = QSharedPointer<CompositeKey>::create();
  158. db->setKey(compositeKey);
  159. QVERIFY(!db->uuid().isNull());
  160. auto root = db->rootGroup();
  161. auto group1 = new Group();
  162. group1->setUuid(QUuid::createUuid());
  163. QVERIFY(!group1->uuid().isNull());
  164. group1->setParent(root);
  165. auto attachment1 = QByteArray("qwerty");
  166. auto attachment2 = QByteArray("asdf");
  167. auto attachment3 = QByteArray("zxcv");
  168. auto entry1 = new Entry();
  169. entry1->setUuid(QUuid::createUuid());
  170. QVERIFY(!entry1->uuid().isNull());
  171. auto uuid1 = entry1->uuid();
  172. entry1->attachments()->set("a", attachment1);
  173. QCOMPARE(entry1->attachments()->keys().size(), 1);
  174. QCOMPARE(entry1->attachments()->values().size(), 1);
  175. entry1->setGroup(root);
  176. auto entry2 = new Entry();
  177. entry2->setUuid(QUuid::createUuid());
  178. QVERIFY(!entry2->uuid().isNull());
  179. auto uuid2 = entry2->uuid();
  180. entry2->attachments()->set("a", attachment1);
  181. entry2->attachments()->set("b", attachment2);
  182. QCOMPARE(entry2->attachments()->keys().size(), 2);
  183. QCOMPARE(entry2->attachments()->values().size(), 2);
  184. entry2->setGroup(group1);
  185. auto entry3 = new Entry();
  186. entry3->setUuid(QUuid::createUuid());
  187. QVERIFY(!entry3->uuid().isNull());
  188. auto uuid3 = entry3->uuid();
  189. entry3->attachments()->set("a", attachment1);
  190. entry3->attachments()->set("b", attachment2);
  191. entry3->attachments()->set("x", attachment3);
  192. entry3->attachments()->set("y", attachment3);
  193. QCOMPARE(entry3->attachments()->keys().size(), 4);
  194. QCOMPARE(entry3->attachments()->values().size(), 3);
  195. entry3->setGroup(group1);
  196. QBuffer buffer;
  197. buffer.open(QBuffer::ReadWrite);
  198. KeePass2Writer writer;
  199. QVERIFY(writer.writeDatabase(&buffer, db.data()));
  200. QCOMPARE(writer.version(), KeePass2::FILE_VERSION_3_1);
  201. buffer.seek(0);
  202. KeePass2Reader reader;
  203. // Re-read database and check that all attachments are still correctly assigned
  204. auto db2 = QSharedPointer<Database>::create();
  205. reader.readDatabase(&buffer, QSharedPointer<CompositeKey>::create(), db2.data());
  206. QVERIFY(!reader.hasError());
  207. QVERIFY(!db->uuid().isNull());
  208. auto a1 = db2->rootGroup()->findEntryByUuid(uuid1)->attachments();
  209. QCOMPARE(a1->keys().size(), 1);
  210. QCOMPARE(a1->values().size(), 1);
  211. QCOMPARE(a1->value("a"), attachment1);
  212. auto a2 = db2->rootGroup()->findEntryByUuid(uuid2)->attachments();
  213. QCOMPARE(a2->keys().size(), 2);
  214. QCOMPARE(a2->values().size(), 2);
  215. QCOMPARE(a2->value("a"), attachment1);
  216. QCOMPARE(a2->value("b"), attachment2);
  217. auto a3 = db2->rootGroup()->findEntryByUuid(uuid3)->attachments();
  218. QCOMPARE(a3->keys().size(), 4);
  219. QCOMPARE(a3->values().size(), 3);
  220. QCOMPARE(a3->value("a"), attachment1);
  221. QCOMPARE(a3->value("b"), attachment2);
  222. QCOMPARE(a3->value("x"), attachment3);
  223. QCOMPARE(a3->value("y"), attachment3);
  224. }