Passwordfile library: tests/passwordfiletests.cpp Source File

Passwordfile library 5.2.1 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(testAuthTagVerification);
25 CPPUNIT_TEST_SUITE_END();
26
27public:
28 void setUp() override;
29 void tearDown() override;
30
31 void testReading();
32 void testReading(const string &context, const string &testfile1path, const string &testfile1password, const string &testfile2,
33 const string &testfile2password, bool testfile2Mod, bool extendedHeaderMod, bool withHMAC = false);
34 void testBasicWriting();
35 void testExtendedWriting();
36 void testAuthTagVerification();
37};
38
39CPPUNIT_TEST_SUITE_REGISTRATION(PasswordFileTests);
40
41 void PasswordFileTests::setUp()
42{
43}
44
45 void PasswordFileTests::tearDown()
46{
47}
48
52 void PasswordFileTests::testReading()
53{
54 testReading("read", testFilePath("testfile1.pwmgr"), "123456", testFilePath("testfile2.pwmgr"), string(), false, false);
55}
56
57 void PasswordFileTests::testReading(const string &context, const string &testfile1path, const string &testfile1password, const string &testfile2,
58 const string &testfile2password, bool testfilesMod, bool extendedHeaderMod, bool withHMAC)
59{
60 PasswordFile file;
61
62 // open testfile 1 ...
63 file.setPath(testfile1path);
64 file.open(PasswordFileOpenFlags::ReadOnly);
65
66 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, !testfile1password.empty(), file.isEncryptionUsed());
67 // attempt to decrypt using a wrong password
68 file.setPassword(testfile1password + "asdf");
69 if (!testfile1password.empty()) {
70 CPPUNIT_ASSERT_THROW(file.load(), CryptoException);
71 }
72 // attempt to decrypt using the correct password
73 file.setPassword(testfile1password);
74 file.load();
75 // test root entry
76 const NodeEntry *const rootEntry = file.rootEntry();
77 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testfile1"s, rootEntry->label());
78 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 4_st, rootEntry->children().size());
79
80 // test testaccount1
81 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testaccount1"s, rootEntry->children()[0]->label());
82 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, EntryType::Account, rootEntry->children()[0]->type());
83 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "pin"s, static_cast<AccountEntry *>(rootEntry->children()[0])->fields().at(0).name());
84 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "123456"s, static_cast<AccountEntry *>(rootEntry->children()[0])->fields().at(0).value());
85 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, FieldType::Password, static_cast<AccountEntry *>(rootEntry->children()[0])->fields().at(0).type());
86 CPPUNIT_ASSERT(
87 static_cast<AccountEntry *>(rootEntry->children()[0])->fields().at(0).tiedAccount() == static_cast<AccountEntry *>(rootEntry->children()[0]));
88 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, FieldType::Normal, static_cast<AccountEntry *>(rootEntry->children()[0])->fields().at(1).type());
89 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 2_st, static_cast<AccountEntry *>(rootEntry->children()[0])->fields().size());
90
91 // test testaccount2
92 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testaccount2"s, rootEntry->children()[1]->label());
93 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, EntryType::Account, rootEntry->children()[1]->type());
94 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 0_st, static_cast<AccountEntry *>(rootEntry->children()[1])->fields().size());
95
96 // test testcategory1
97 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testcategory1"s, rootEntry->children()[2]->label());
98 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, EntryType::Node, rootEntry->children()[2]->type());
99 const NodeEntry *const category = static_cast<NodeEntry *>(rootEntry->children()[2]);
100 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 3_st, category->children().size());
101 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, EntryType::Node, category->children()[2]->type());
102 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 2_st, static_cast<NodeEntry *>(category->children()[2])->children().size());
103
104 // test testaccount3
105 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testaccount3"s, rootEntry->children()[3]->label());
106
107 if (!testfilesMod) {
108 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "encryption, compression"s, flagsToString(file.saveOptions()));
109 } else if (extendedHeaderMod) {
110 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "foo"s, file.extendedHeader());
111 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "encryption, password hashing"s, flagsToString(file.saveOptions()));
112 } else {
113 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, ""s, file.extendedHeader());
114 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "compression"s, flagsToString(file.saveOptions()));
115 }
116 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, ""s, file.encryptedExtendedHeader());
117
118 // open testfile 2
119 file.setPath(testfile2);
120 file.open(PasswordFileOpenFlags::ReadOnly);
121
122 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, !testfile2password.empty(), file.isEncryptionUsed());
123 file.setPassword(testfile2password);
124 file.load();
125 const NodeEntry *const rootEntry2 = file.rootEntry();
126 if (testfilesMod) {
127 if (withHMAC && extendedHeaderMod) {
128 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, static_cast<std::uint32_t>(6), file.version());
129 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "encryption, password hashing, HMAC"s, flagsToString(file.saveOptions()));
130 } else if (withHMAC) {
131 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, static_cast<std::uint32_t>(4), file.version());
132 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "encryption, HMAC"s, flagsToString(file.saveOptions()));
133 } else if (extendedHeaderMod) {
134 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, static_cast<std::uint32_t>(6), file.version());
135 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "encryption, password hashing"s, flagsToString(file.saveOptions()));
136 } else {
137 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, static_cast<std::uint32_t>(3), file.version());
138 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "encryption"s, flagsToString(file.saveOptions()));
139 }
140 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testfile2 - modified"s, rootEntry2->label());
141 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 2_st, rootEntry2->children().size());
142 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "newAccount"s, rootEntry2->children()[1]->label());
143 } else {
144 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, static_cast<std::uint32_t>(3), file.version());
145 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "testfile2"s, rootEntry2->label());
146 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, 1_st, rootEntry2->children().size());
147 }
148 if (extendedHeaderMod) {
149 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "foo"s, file.extendedHeader());
150 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, "bar"s, file.encryptedExtendedHeader());
151
152 } else {
153 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, ""s, file.extendedHeader());
154 CPPUNIT_ASSERT_EQUAL_MESSAGE(context, ""s, file.encryptedExtendedHeader());
155 }
156}
157
161 void PasswordFileTests::testBasicWriting()
162{
163 const string testfile1 = workingCopyPath("testfile1.pwmgr");
164 const string testfile2 = workingCopyPath("testfile2.pwmgr");
165 PasswordFile file;
166
167 // resave testfile 1
168 file.setPath(testfile1);
169 file.open();
170 file.setPassword("123456");
171 file.load();
172 file.doBackup();
173 file.save(PasswordFileSaveFlags::Compression);
174
175 // resave testfile 2
176 file.setPath(testfile2);
177 file.open();
178 file.load();
179 file.rootEntry()->setLabel("testfile2 - modified");
180 new AccountEntry("newAccount", file.rootEntry());
181 file.setPassword("654321");
182 file.doBackup();
183 file.save(PasswordFileSaveFlags::Encryption | PasswordFileSaveFlags::AuthenticationTag);
184
185 // check results using the reading test
186 testReading("basic writing", testfile1, string(), testfile2, "654321", true, false, true);
187
188 // check backup files
189 testReading("basic writing", testfile1 + ".backup", "123456", testfile2 + ".backup", string(), false, false);
190}
191
195 void PasswordFileTests::testExtendedWriting()
196{
197 const string testfile1 = workingCopyPath("testfile1.pwmgr");
198 const string testfile2 = workingCopyPath("testfile2.pwmgr");
199 PasswordFile file;
200
201 // resave testfile 1
202 file.setPath(testfile1);
203 file.open();
204 file.setPassword("123456");
205 file.load();
206 CPPUNIT_ASSERT_EQUAL(""s, file.extendedHeader());
207 CPPUNIT_ASSERT_EQUAL(""s, file.encryptedExtendedHeader());
208 file.doBackup();
209 file.extendedHeader() = "foo";
210 file.save(PasswordFileSaveFlags::Encryption | PasswordFileSaveFlags::PasswordHashing);
211
212 // resave testfile 2
213 file.setPath(testfile2);
214 file.open();
215 file.load();
216 CPPUNIT_ASSERT_EQUAL(""s, file.extendedHeader());
217 CPPUNIT_ASSERT_EQUAL(""s, file.encryptedExtendedHeader());
218 file.rootEntry()->setLabel("testfile2 - modified");
219 auto *const newAccount = new AccountEntry("newAccount", file.rootEntry());
220 file.setPassword("654321");
221 file.extendedHeader() = "foo";
222 file.encryptedExtendedHeader() = "bar";
223 file.doBackup();
224 file.save(PasswordFileSaveFlags::Encryption | PasswordFileSaveFlags::PasswordHashing | PasswordFileSaveFlags::AuthenticationTag);
225
226 // check results using the reading test
227 testReading("extended writing", testfile1, "123456", testfile2, "654321", true, true, true);
228
229 // check backup files
230 testReading("extended writing", testfile1 + ".backup", "123456", testfile2 + ".backup", string(), false, false);
231
232 // remove newAccount again to check what happens if the file size decreases
233 const auto fileSize = file.size();
234 delete newAccount;
235 file.save(PasswordFileSaveFlags::Encryption | PasswordFileSaveFlags::PasswordHashing);
236 file.close();
237 file.clearEntries();
238 file.open();
239 CPPUNIT_ASSERT_LESS(fileSize, file.size());
240 file.load();
241
242 auto path = std::list<std::string>{ "newAccount" };
243 CPPUNIT_ASSERT(file.rootEntry());
244 CPPUNIT_ASSERT(!file.rootEntry()->entryByPath(path));
245}
246
247 void PasswordFileTests::testAuthTagVerification()
248{
249 auto file = PasswordFile(workingCopyPath("testfile3-hmac.pwmgr"), "123456");
250 file.open();
251 file.load();
252 CPPUNIT_ASSERT_MESSAGE("auth tag present", file.saveOptions() & PasswordFileSaveFlags::AuthenticationTag);
253 CPPUNIT_ASSERT_EQUAL_MESSAGE("auth tag not returned as extended data", ""s, file.extendedHeader());
254
255 // corrupt file and try reloading it
256 file.fileStream().seekp(0x78);
257 file.fileStream().put(0x00);
258 file.close();
259 file.clearEntries();
260 file.open();
261 auto error = std::string();
262 try {
263 file.load();
264 } catch (const CryptoException &e) {
265 error = e.what();
266 }
267 CPPUNIT_ASSERT_EQUAL_MESSAGE("auth failed", "Authentication failed: data integrity check failed (wrong password or file corrupted)."s, error);
268}
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::AccountEntry::AccountEntry
AccountEntry()
Definition entry.cpp:433
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:41
Io::PasswordFile::rootEntry
const NodeEntry * rootEntry() const
Returns the root entry if present or nullptr otherwise.
Definition passwordfile.cpp:770
Io::PasswordFile::clearEntries
void clearEntries()
Removes the root element if one is present.
Definition passwordfile.cpp:667
Io::PasswordFile::open
void open(PasswordFileOpenFlags options=PasswordFileOpenFlags::Default)
Opens the file.
Definition passwordfile.cpp:129
Io::PasswordFile::load
void load()
Reads the contents of the file.
Definition passwordfile.cpp:191
Io::PasswordFile::close
void close()
Closes the file if currently opened.
Definition passwordfile.cpp:786
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:202
Io::PasswordFile::PasswordFile
PasswordFile()
Constructs a new password file.
Definition passwordfile.cpp:56
Io::PasswordFile::setPassword
void setPassword(const std::string &password)
Sets the current password.
Definition passwordfile.h:134
Io::PasswordFile::isEncryptionUsed
bool isEncryptionUsed()
Returns an indication whether encryption is used and the file is open; returns always false otherwise...
Definition passwordfile.cpp:813
Io::PasswordFile::size
std::size_t size()
Returns the size of the file if the file is open; otherwise returns zero.
Definition passwordfile.cpp:839
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:457
Io::PasswordFile::encryptedExtendedHeader
std::string & encryptedExtendedHeader()
Returns the encrypted extended header.
Definition passwordfile.h:185
Io::PasswordFile::setPath
void setPath(const std::string &value)
Sets the current file path.
Definition passwordfile.cpp:797
Io::PasswordFile::extendedHeader
std::string & extendedHeader()
Returns the extended header.
Definition passwordfile.h:169
Io::PasswordFile::doBackup
void doBackup()
Creates a backup of the file.
Definition passwordfile.cpp:733
Io::PasswordFile::saveOptions
PasswordFileSaveFlags saveOptions() const
Returns the save options used the last time when saving the file.
Definition passwordfile.h:218
PasswordFileTests
The PasswordFileTests class tests the Io::PasswordFile class.
Definition passwordfiletests.cpp:19
PasswordFileTests::setUp
void setUp() override
Definition passwordfiletests.cpp:41
PasswordFileTests::testAuthTagVerification
void testAuthTagVerification()
Definition passwordfiletests.cpp:247
PasswordFileTests::testBasicWriting
void testBasicWriting()
Tests writing (and reading again) using basic features.
Definition passwordfiletests.cpp:161
PasswordFileTests::testExtendedWriting
void testExtendedWriting()
Tests writing (and reading again) using extended features.
Definition passwordfiletests.cpp:195
PasswordFileTests::testReading
void testReading()
Tests reading the testfiles testfile{1,2}.pwmgr.
Definition passwordfiletests.cpp:52
PasswordFileTests::tearDown
void tearDown() override
Definition passwordfiletests.cpp:45
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:875
Io::EntryType::Account
@ Account
Definition entry.h:19
Io::EntryType::Node
@ Node
Definition entry.h:18
Io::PasswordFileSaveFlags::Compression
@ Compression
Definition passwordfile.h:31
Io::PasswordFileSaveFlags::AuthenticationTag
@ AuthenticationTag
Definition passwordfile.h:34
Io::PasswordFileSaveFlags::PasswordHashing
@ PasswordHashing
Definition passwordfile.h:32
Io::PasswordFileSaveFlags::Encryption
@ Encryption
Definition passwordfile.h:30
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