Passwordfile library  5.0.0
C++ library to read/write passwords from/to encrypted files
passwordfiletests.cpp
Go to the documentation of this file.
1 #include "../io/cryptoexception.h"
2 #include "../io/entry.h"
3 #include "../io/passwordfile.h"
4 
5 #include <c++utilities/tests/testutils.h>
6 
7 #include <cppunit/TestFixture.h>
8 #include <cppunit/extensions/HelperMacros.h>
9 
10 using namespace std;
11 using namespace Io;
12 using namespace CppUtilities;
13 using namespace CppUtilities::Literals;
14 using namespace CPPUNIT_NS;
15 
19 class PasswordFileTests : public TestFixture {
20  CPPUNIT_TEST_SUITE(PasswordFileTests);
21  CPPUNIT_TEST(testReading);
22  CPPUNIT_TEST(testBasicWriting);
23  CPPUNIT_TEST(testExtendedWriting);
24  CPPUNIT_TEST_SUITE_END();
25 
26 public:
27  void setUp() override;
28  void tearDown() override;
29 
30  void testReading();
31  void testReading(const string &context, const string &testfile1path, const string &testfile1password, const string &testfile2,
32  const string &testfile2password, bool testfile2Mod, bool extendedHeaderMod);
33  void testBasicWriting();
34  void testExtendedWriting();
35 };
36 
38 
40 {
41 }
42 
44 {
45 }
46 
51 {
52  testReading("read", testFilePath("testfile1.pwmgr"), "123456", testFilePath("testfile2.pwmgr"), string(), false, false);
53 }
54 
55 void PasswordFileTests::testReading(const string &context, const string &testfile1path, const string &testfile1password, const string &testfile2,
56  const string &testfile2password, bool testfilesMod, bool extendedHeaderMod)
57 {
58  PasswordFile file;
59 
60  // open testfile 1 ...
61  file.setPath(testfile1path);
62  file.open(PasswordFileOpenFlags::ReadOnly);
63 
64  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, !testfile1password.empty(), file.isEncryptionUsed());
65  // attempt to decrypt using a wrong password
66  file.setPassword(testfile1password + "asdf");
67  if (!testfile1password.empty()) {
68  CPPUNIT_ASSERT_THROW(file.load(), CryptoException);
69  }
70  // attempt to decrypt using the correct password
71  file.setPassword(testfile1password);
72  file.load();
73  // test root entry
74  const NodeEntry *const rootEntry = file.rootEntry();
75  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testfile1"s, rootEntry->label());
76  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 4_st, rootEntry->children().size());
77 
78  // test testaccount1
79  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testaccount1"s, rootEntry->children()[0]->label());
80  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, EntryType::Account, rootEntry->children()[0]->type());
81  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "pin"s, static_cast<AccountEntry *>(rootEntry->children()[0])->fields().at(0).name());
82  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "123456"s, static_cast<AccountEntry *>(rootEntry->children()[0])->fields().at(0).value());
83  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, FieldType::Password, static_cast<AccountEntry *>(rootEntry->children()[0])->fields().at(0).type());
84  CPPUNIT_ASSERT(
85  static_cast<AccountEntry *>(rootEntry->children()[0])->fields().at(0).tiedAccount() == static_cast<AccountEntry *>(rootEntry->children()[0]));
86  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, FieldType::Normal, static_cast<AccountEntry *>(rootEntry->children()[0])->fields().at(1).type());
87  CPPUNIT_ASSERT_THROW_MESSAGE(context, static_cast<AccountEntry *>(rootEntry->children()[0])->fields().at(2), out_of_range);
88 
89  // test testaccount2
90  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testaccount2"s, rootEntry->children()[1]->label());
91  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, EntryType::Account, rootEntry->children()[1]->type());
92  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 0_st, static_cast<AccountEntry *>(rootEntry->children()[1])->fields().size());
93 
94  // test testcategory1
95  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testcategory1"s, rootEntry->children()[2]->label());
96  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, EntryType::Node, rootEntry->children()[2]->type());
97  const NodeEntry *const category = static_cast<NodeEntry *>(rootEntry->children()[2]);
98  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 3_st, category->children().size());
99  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, EntryType::Node, category->children()[2]->type());
100  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 2_st, static_cast<NodeEntry *>(category->children()[2])->children().size());
101 
102  // test testaccount3
103  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testaccount3"s, rootEntry->children()[3]->label());
104 
105  if (!testfilesMod) {
106  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "encryption, compression"s, flagsToString(file.saveOptions()));
107  } else if (extendedHeaderMod) {
108  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "foo"s, file.extendedHeader());
109  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "encryption, password hashing"s, flagsToString(file.saveOptions()));
110  } else {
111  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, ""s, file.extendedHeader());
112  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "compression"s, flagsToString(file.saveOptions()));
113  }
114  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, ""s, file.encryptedExtendedHeader());
115 
116  // open testfile 2
117  file.setPath(testfile2);
118  file.open(PasswordFileOpenFlags::ReadOnly);
119 
120  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, !testfile2password.empty(), file.isEncryptionUsed());
121  file.setPassword(testfile2password);
122  file.load();
123  const NodeEntry *const rootEntry2 = file.rootEntry();
124  if (testfilesMod) {
125  if (extendedHeaderMod) {
126  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, static_cast<std::uint32_t>(6), file.version());
127  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "encryption, password hashing"s, flagsToString(file.saveOptions()));
128  } else {
129  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, static_cast<std::uint32_t>(3), file.version());
130  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "encryption"s, flagsToString(file.saveOptions()));
131  }
132  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testfile2 - modified"s, rootEntry2->label());
133  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 2_st, rootEntry2->children().size());
134  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "newAccount"s, rootEntry2->children()[1]->label());
135  } else {
136  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, static_cast<std::uint32_t>(3), file.version());
137  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testfile2"s, rootEntry2->label());
138  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 1_st, rootEntry2->children().size());
139  }
140  if (extendedHeaderMod) {
141  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "foo"s, file.extendedHeader());
142  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "bar"s, file.encryptedExtendedHeader());
143 
144  } else {
145  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, ""s, file.extendedHeader());
146  CPPUNIT_ASSERT_EQUAL_MESSAGE(context, ""s, file.encryptedExtendedHeader());
147  }
148 }
149 
154 {
155  const string testfile1 = workingCopyPath("testfile1.pwmgr");
156  const string testfile2 = workingCopyPath("testfile2.pwmgr");
157  PasswordFile file;
158 
159  // resave testfile 1
160  file.setPath(testfile1);
161  file.open();
162  file.setPassword("123456");
163  file.load();
164  file.doBackup();
165  file.save(PasswordFileSaveFlags::Compression);
166 
167  // resave testfile 2
168  file.setPath(testfile2);
169  file.open();
170  file.load();
171  file.rootEntry()->setLabel("testfile2 - modified");
172  new AccountEntry("newAccount", file.rootEntry());
173  file.setPassword("654321");
174  file.doBackup();
175  file.save(PasswordFileSaveFlags::Encryption);
176 
177  // check results using the reading test
178  testReading("basic writing", testfile1, string(), testfile2, "654321", true, false);
179 
180  // check backup files
181  testReading("basic writing", testfile1 + ".backup", "123456", testfile2 + ".backup", string(), false, false);
182 }
183 
188 {
189  const string testfile1 = workingCopyPath("testfile1.pwmgr");
190  const string testfile2 = workingCopyPath("testfile2.pwmgr");
191  PasswordFile file;
192 
193  // resave testfile 1
194  file.setPath(testfile1);
195  file.open();
196  file.setPassword("123456");
197  file.load();
198  CPPUNIT_ASSERT_EQUAL(""s, file.extendedHeader());
199  CPPUNIT_ASSERT_EQUAL(""s, file.encryptedExtendedHeader());
200  file.doBackup();
201  file.extendedHeader() = "foo";
202  file.save(PasswordFileSaveFlags::Encryption | PasswordFileSaveFlags::PasswordHashing);
203 
204  // resave testfile 2
205  file.setPath(testfile2);
206  file.open();
207  file.load();
208  CPPUNIT_ASSERT_EQUAL(""s, file.extendedHeader());
209  CPPUNIT_ASSERT_EQUAL(""s, file.encryptedExtendedHeader());
210  file.rootEntry()->setLabel("testfile2 - modified");
211  new AccountEntry("newAccount", file.rootEntry());
212  file.setPassword("654321");
213  file.extendedHeader() = "foo";
214  file.encryptedExtendedHeader() = "bar";
215  file.doBackup();
216  file.save(PasswordFileSaveFlags::Encryption | PasswordFileSaveFlags::PasswordHashing);
217 
218  // check results using the reading test
219  testReading("extended writing", testfile1, "123456", testfile2, "654321", true, true);
220 
221  // check backup files
222  testReading("extended writing", testfile1 + ".backup", "123456", testfile2 + ".backup", string(), false, false);
223 }
CPPUNIT_TEST_SUITE_REGISTRATION
CPPUNIT_TEST_SUITE_REGISTRATION(PasswordFileTests)
Io::NodeEntry
The NodeEntry class acts as parent for other entries.
Definition: entry.h:114
Io::CryptoException
The exception that is thrown when an encryption/decryption error occurs.
Definition: cryptoexception.h:11
Io::PasswordFile
The PasswordFile class holds account information in the form of Entry and Field instances and provide...
Definition: passwordfile.h:40
PasswordFileTests::tearDown
void tearDown() override
Definition: passwordfiletests.cpp:43
Io::PasswordFile::saveOptions
PasswordFileSaveFlags saveOptions() const
Returns the save options used the last time when saving the file.
Definition: passwordfile.h:214
Io::PasswordFile::encryptedExtendedHeader
std::string & encryptedExtendedHeader()
Returns the encrypted extended header.
Definition: passwordfile.h:181
Io::PasswordFile::save
void save(PasswordFileSaveFlags options=PasswordFileSaveFlags::Default)
Writes the current root entry to the file under path() replacing its previous contents.
Definition: passwordfile.cpp:394
PasswordFileTests
The PasswordFileTests class tests the Io::PasswordFile class.
Definition: passwordfiletests.cpp:19
Io::NodeEntry::children
const std::vector< Entry * > & children() const
Definition: entry.h:145
Io::Entry::label
const std::string & label() const
Returns the label.
Definition: entry.h:70
Io::PasswordFile::rootEntry
const NodeEntry * rootEntry() const
Returns the root entry if present or nullptr otherwise.
Definition: passwordfile.cpp:664
Io::PasswordFile::load
void load()
Reads the contents of the file.
Definition: passwordfile.cpp:173
Io::PasswordFile::setPassword
void setPassword(const std::string &password)
Sets the current password.
Definition: passwordfile.h:133
Io::PasswordFile::setPath
void setPath(const std::string &value)
Sets the current file path.
Definition: passwordfile.cpp:691
Io
Contains all IO related classes.
Definition: cryptoexception.h:9
PasswordFileTests::setUp
void setUp() override
Definition: passwordfiletests.cpp:39
Io::PasswordFile::version
std::uint32_t version() const
Returns the file version used the last time when saving the file (the version of the file as it is on...
Definition: passwordfile.h:198
CppUtilities
Definition: utils.h:12
Io::PasswordFile::doBackup
void doBackup()
Creates a backup of the file.
Definition: passwordfile.cpp:633
PasswordFileTests::testExtendedWriting
void testExtendedWriting()
Tests writing (and reading again) using extended features.
Definition: passwordfiletests.cpp:187
Io::PasswordFile::extendedHeader
std::string & extendedHeader()
Returns the extended header.
Definition: passwordfile.h:165
Io::PasswordFile::open
void open(PasswordFileOpenFlags options=PasswordFileOpenFlags::Default)
Opens the file.
Definition: passwordfile.cpp:115
PasswordFileTests::testBasicWriting
void testBasicWriting()
Tests writing (and reading again) using basic features.
Definition: passwordfiletests.cpp:153
PasswordFileTests::testReading
void testReading()
Tests reading the testfiles testfile{1,2}.pwmgr.
Definition: passwordfiletests.cpp:50
Io::flagsToString
std::string PASSWORD_FILE_EXPORT flagsToString(PasswordFileOpenFlags flags)
Returns a comma-separated string for the specified flags.
Definition: passwordfile.cpp:769
Io::PasswordFile::isEncryptionUsed
bool isEncryptionUsed()
Returns an indication whether encryption is used and the file is open; returns always false otherwise...
Definition: passwordfile.cpp:707
Io::AccountEntry
The exception that is thrown when a parsing error occurs.
Definition: entry.h:170
Io::Entry::setLabel
void setLabel(const std::string &label)
Sets the label.
Definition: entry.h:80