Passwordfile library: tests/passwordfiletests.cpp Source File

Passwordfile library 5.1.0 C++ library to read/write passwords from/to encrypted files

Loading...
Searching...
No Matches
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 "./utils.h"
6
7#include <cppunit/TestFixture.h>
8#include <cppunit/extensions/HelperMacros.h>
9
10using namespace std;
11using namespace Io;
12using namespace CppUtilities;
13using namespace CppUtilities::Literals;
14using 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
26public:
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
37CPPUNIT_TEST_SUITE_REGISTRATION(PasswordFileTests);
38
39 void PasswordFileTests::setUp()
40{
41}
42
43 void PasswordFileTests::tearDown()
44{
45}
46
50 void PasswordFileTests::testReading()
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_EQUAL_MESSAGE(context, 2_st, static_cast<AccountEntry *>(rootEntry->children()[0])->fields().size());
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
153 void PasswordFileTests::testBasicWriting()
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
187 void PasswordFileTests::testExtendedWriting()
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 auto *const newAccount = 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
224 // remove newAccount again to check what happens if the file size decreases
225 const auto fileSize = file.size();
226 delete newAccount;
227 file.save(PasswordFileSaveFlags::Encryption | PasswordFileSaveFlags::PasswordHashing);
228 file.close();
229 file.clearEntries();
230 file.open();
231 CPPUNIT_ASSERT_LESS(fileSize, file.size());
232 file.load();
233
234 auto path = std::list<std::string>{ "newAccount" };
235 CPPUNIT_ASSERT(file.rootEntry());
236 CPPUNIT_ASSERT(!file.rootEntry()->entryByPath(path));
237}
Io::AccountEntry
The exception that is thrown when a parsing error occurs.
Definition entry.h:170
Io::AccountEntry::fields
const std::vector< Field > & fields() const
Definition entry.h:194
Io::CryptoException
The exception that is thrown when an encryption/decryption error occurs.
Definition cryptoexception.h:11
Io::Entry::setLabel
void setLabel(const std::string &label)
Sets the label.
Definition entry.h:80
Io::Entry::label
const std::string & label() const
Returns the label.
Definition entry.h:70
Io::NodeEntry
The NodeEntry class acts as parent for other entries.
Definition entry.h:114
Io::NodeEntry::entryByPath
Entry * entryByPath(std::list< std::string > &path, bool includeThis=true, const EntryType *creationType=nullptr)
Returns an entry specified by the provided path.
Definition entry.cpp:347
Io::NodeEntry::children
const std::vector< Entry * > & children() const
Definition entry.h:145
Io::PasswordFile
The PasswordFile class holds account information in the form of Entry and Field instances and provide...
Definition passwordfile.h:38
Io::PasswordFile::rootEntry
const NodeEntry * rootEntry() const
Returns the root entry if present or nullptr otherwise.
Definition passwordfile.cpp:685
Io::PasswordFile::clearEntries
void clearEntries()
Removes the root element if one is present.
Definition passwordfile.cpp:588
Io::PasswordFile::open
void open(PasswordFileOpenFlags options=PasswordFileOpenFlags::Default)
Opens the file.
Definition passwordfile.cpp:119
Io::PasswordFile::load
void load()
Reads the contents of the file.
Definition passwordfile.cpp:177
Io::PasswordFile::close
void close()
Closes the file if currently opened.
Definition passwordfile.cpp:701
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:196
Io::PasswordFile::setPassword
void setPassword(const std::string &password)
Sets the current password.
Definition passwordfile.h:131
Io::PasswordFile::isEncryptionUsed
bool isEncryptionUsed()
Returns an indication whether encryption is used and the file is open; returns always false otherwise...
Definition passwordfile.cpp:728
Io::PasswordFile::size
std::size_t size()
Returns the size of the file if the file is open; otherwise returns zero.
Definition passwordfile.cpp:754
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:399
Io::PasswordFile::encryptedExtendedHeader
std::string & encryptedExtendedHeader()
Returns the encrypted extended header.
Definition passwordfile.h:179
Io::PasswordFile::setPath
void setPath(const std::string &value)
Sets the current file path.
Definition passwordfile.cpp:712
Io::PasswordFile::extendedHeader
std::string & extendedHeader()
Returns the extended header.
Definition passwordfile.h:163
Io::PasswordFile::doBackup
void doBackup()
Creates a backup of the file.
Definition passwordfile.cpp:654
Io::PasswordFile::saveOptions
PasswordFileSaveFlags saveOptions() const
Returns the save options used the last time when saving the file.
Definition passwordfile.h:212
PasswordFileTests
The PasswordFileTests class tests the Io::PasswordFile class.
Definition passwordfiletests.cpp:19
PasswordFileTests::setUp
void setUp() override
Definition passwordfiletests.cpp:39
PasswordFileTests::testBasicWriting
void testBasicWriting()
Tests writing (and reading again) using basic features.
Definition passwordfiletests.cpp:153
PasswordFileTests::testExtendedWriting
void testExtendedWriting()
Tests writing (and reading again) using extended features.
Definition passwordfiletests.cpp:187
PasswordFileTests::testReading
void testReading()
Tests reading the testfiles testfile{1,2}.pwmgr.
Definition passwordfiletests.cpp:50
PasswordFileTests::tearDown
void tearDown() override
Definition passwordfiletests.cpp:43
cryptoexception.h
entry.h
CppUtilities
Definition openssl.h:13
Io
Contains all IO related classes.
Definition cryptoexception.h:9
Io::flagsToString
PASSWORD_FILE_EXPORT std::string flagsToString(PasswordFileOpenFlags flags)
Returns a comma-separated string for the specified flags.
Definition passwordfile.cpp:790
Io::EntryType::Account
@ Account
Definition entry.h:19
Io::EntryType::Node
@ Node
Definition entry.h:18
Io::PasswordFileSaveFlags::Compression
@ Compression
Definition passwordfile.h:30
Io::PasswordFileSaveFlags::PasswordHashing
@ PasswordHashing
Definition passwordfile.h:31
Io::PasswordFileSaveFlags::Encryption
@ Encryption
Definition passwordfile.h:29
Io::FieldType::Normal
@ Normal
Definition field.h:11
Io::FieldType::Password
@ Password
Definition field.h:11
Io::PasswordFileOpenFlags::ReadOnly
@ ReadOnly
Definition passwordfile.h:21
passwordfile.h
CPPUNIT_TEST_SUITE_REGISTRATION
CPPUNIT_TEST_SUITE_REGISTRATION(PasswordFileTests)
utils.h