Passwordfile library: io/passwordfile.cpp Source File

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

Loading...
Searching...
No Matches
passwordfile.cpp
Go to the documentation of this file.
1#include "./passwordfile.h"
2#include "./cryptoexception.h"
3#include "./entry.h"
4#include "./parsingexception.h"
5
6#include "../util/openssl.h"
7
8#include <c++utilities/conversion/stringbuilder.h>
9#include <c++utilities/conversion/stringconversion.h>
10#include <c++utilities/io/path.h>
11
12#include <openssl/conf.h>
13#include <openssl/err.h>
14#include <openssl/evp.h>
15#include <openssl/rand.h>
16
17#include <zlib.h>
18
19#include <cstring>
20#include <filesystem>
21#include <fstream>
22#include <functional>
23#include <iostream>
24#include <limits>
25#include <memory>
26#include <sstream>
27
28using namespace std;
29using namespace CppUtilities;
30
31namespace Io {
32
33 constexpr unsigned int aes256cbcIvSize = 16U;
34 constexpr unsigned int aes256blockSize = 32U;
35 constexpr unsigned int aes256additionalBufferSize = aes256blockSize * 2;
36
42
46PasswordFile::PasswordFile()
47 : m_freader(BinaryReader(&m_file))
48 , m_fwriter(BinaryWriter(&m_file))
49 , m_version(0)
50 , m_openOptions(PasswordFileOpenFlags::None)
51 , m_saveOptions(PasswordFileSaveFlags::None)
52{
53 m_file.exceptions(ios_base::failbit | ios_base::badbit);
54 clearPassword();
55}
56
60 PasswordFile::PasswordFile(const string &path, const string &password)
61 : m_freader(BinaryReader(&m_file))
62 , m_fwriter(BinaryWriter(&m_file))
63 , m_version(0)
64 , m_openOptions(PasswordFileOpenFlags::None)
65 , m_saveOptions(PasswordFileSaveFlags::None)
66{
67 m_file.exceptions(ios_base::failbit | ios_base::badbit);
68 setPath(path);
69 setPassword(password);
70}
71
75 PasswordFile::PasswordFile(const PasswordFile &other)
76 : m_path(other.m_path)
77 , m_password(other.m_password)
78 , m_rootEntry(other.m_rootEntry ? make_unique<NodeEntry>(*other.m_rootEntry) : nullptr)
79 , m_extendedHeader(other.m_extendedHeader)
80 , m_encryptedExtendedHeader(other.m_encryptedExtendedHeader)
81 , m_freader(BinaryReader(&m_file))
82 , m_fwriter(BinaryWriter(&m_file))
83 , m_version(other.m_version)
84 , m_openOptions(other.m_openOptions)
85 , m_saveOptions(other.m_saveOptions)
86{
87 m_file.exceptions(ios_base::failbit | ios_base::badbit);
88}
89
93 PasswordFile::PasswordFile(PasswordFile &&other)
94 : m_path(std::move(other.m_path))
95 , m_password(std::move(other.m_password))
96 , m_rootEntry(std::move(other.m_rootEntry))
97 , m_extendedHeader(std::move(other.m_extendedHeader))
98 , m_encryptedExtendedHeader(std::move(other.m_encryptedExtendedHeader))
99 , m_file(std::move(other.m_file))
100 , m_freader(BinaryReader(&m_file))
101 , m_fwriter(BinaryWriter(&m_file))
102 , m_version(other.m_version)
103 , m_openOptions(other.m_openOptions)
104 , m_saveOptions(other.m_saveOptions)
105{
106}
107
111PasswordFile::~PasswordFile()
112{
113}
114
119 void PasswordFile::open(PasswordFileOpenFlags options)
120{
121 close();
122 if (m_path.empty()) {
123 throw std::ios_base::failure("Unable to open file because path is empty.");
124 }
125 m_file.open(
126 m_path, options & PasswordFileOpenFlags::ReadOnly ? ios_base::in | ios_base::binary : ios_base::in | ios_base::out | ios_base::binary);
127 m_openOptions = options;
128 opened();
129}
130
136 void PasswordFile::opened()
137{
138 m_file.seekg(0, ios_base::end);
139 if (m_file.tellg() == 0) {
140 throw std::ios_base::failure("File is empty.");
141 } else {
142 m_file.seekg(0);
143 }
144}
145
149 void PasswordFile::generateRootEntry()
150{
151 if (!m_rootEntry) {
152 m_rootEntry.reset(new NodeEntry("accounts"));
153 }
154}
155
160 void PasswordFile::create()
161{
162 close();
163 if (m_path.empty()) {
164 throw std::ios_base::failure("Unable to create file because path is empty.");
165 }
166 m_file.open(m_path, fstream::out | fstream::trunc | fstream::binary);
167}
168
177 void PasswordFile::load()
178{
179 if (!m_file.is_open()) {
180 open();
181 }
182 m_file.seekg(0);
183 m_version = 0;
184 m_saveOptions = PasswordFileSaveFlags::None;
185
186 // check magic number
187 if (m_freader.readUInt32LE() != 0x7770616DU) {
188 throw ParsingException("Signature not present.");
189 }
190
191 // check version and flags (used in version 0x3 only)
192 m_version = m_freader.readUInt32LE();
193 if (m_version > 0x6U) {
194 throw ParsingException(argsToString("Version \"", m_version, "\" is unknown. Only versions 0 to 6 are supported."));
195 }
196 if (m_version >= 0x6U) {
197 m_saveOptions |= PasswordFileSaveFlags::PasswordHashing;
198 }
199 bool decrypterUsed, ivUsed, compressionUsed;
200 if (m_version >= 0x3U) {
201 const auto flags = m_freader.readByte();
202 if ((decrypterUsed = flags & 0x80)) {
203 m_saveOptions |= PasswordFileSaveFlags::Encryption;
204 }
205 if ((compressionUsed = flags & 0x20)) {
206 m_saveOptions |= PasswordFileSaveFlags::Compression;
207 }
208 ivUsed = flags & 0x40;
209 } else {
210 if ((decrypterUsed = m_version >= 0x1U)) {
211 m_saveOptions |= PasswordFileSaveFlags::Encryption;
212 }
213 compressionUsed = false;
214 ivUsed = m_version == 0x2U;
215 }
216
217 // skip extended header
218 // (the extended header might be used in further versions to
219 // add additional information without breaking compatibility)
220 if (m_version >= 0x4U) {
221 std::uint16_t extendedHeaderSize = m_freader.readUInt16BE();
222 m_extendedHeader = m_freader.readString(extendedHeaderSize);
223 } else {
224 m_extendedHeader.clear();
225 }
226
227 // get length
228 const auto headerSize = static_cast<size_t>(m_file.tellg());
229 m_file.seekg(0, ios_base::end);
230 auto remainingSize = static_cast<size_t>(m_file.tellg()) - headerSize;
231 m_file.seekg(static_cast<streamoff>(headerSize), ios_base::beg);
232
233 // read hash count
234 uint32_t hashCount = 0U;
235 if ((m_saveOptions & PasswordFileSaveFlags::PasswordHashing) && decrypterUsed) {
236 if (remainingSize < 4) {
237 throw ParsingException("Hash count truncated.");
238 }
239 hashCount = m_freader.readUInt32BE();
240 remainingSize -= 4;
241 }
242
243 // read IV
244 unsigned char iv[aes256cbcIvSize] = { 0 };
245 if (decrypterUsed && ivUsed) {
246 if (remainingSize < aes256cbcIvSize) {
247 throw ParsingException("Initiation vector is truncated.");
248 }
249 m_file.read(reinterpret_cast<char *>(iv), aes256cbcIvSize);
250 remainingSize -= aes256cbcIvSize;
251 }
252 if (!remainingSize) {
253 throw ParsingException("No contents found.");
254 }
255
256 // decrypt contents
257 vector<char> rawData;
258 m_freader.read(rawData, static_cast<streamoff>(remainingSize));
259 vector<char> decryptedData;
260 if (decrypterUsed) {
261 if (remainingSize > numeric_limits<int>::max()) {
262 throw CryptoException("Size exceeds limit.");
263 }
264
265 // prepare password
266 Util::OpenSsl::Sha256Sum password;
267 if (hashCount) {
268 // hash the password as often as it has been hashed when writing the file
269 password = Util::OpenSsl::computeSha256Sum(reinterpret_cast<unsigned const char *>(m_password.data()), m_password.size());
270 for (uint32_t i = 1; i < hashCount; ++i) {
271 password = Util::OpenSsl::computeSha256Sum(password.data, Util::OpenSsl::Sha256Sum::size);
272 }
273 } else {
274 m_password.copy(reinterpret_cast<char *>(password.data), Util::OpenSsl::Sha256Sum::size);
275 }
276
277 // initiate ctx, decrypt data
278 EVP_CIPHER_CTX *ctx = nullptr;
279 decryptedData.resize(remainingSize + aes256additionalBufferSize);
280 int outlen1, outlen2;
281 if ((ctx = EVP_CIPHER_CTX_new()) == nullptr || EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, password.data, iv) != 1
282 || EVP_DecryptUpdate(ctx, reinterpret_cast<unsigned char *>(decryptedData.data()), &outlen1,
283 reinterpret_cast<unsigned char *>(rawData.data()), static_cast<int>(remainingSize))
284 != 1
285 || EVP_DecryptFinal_ex(ctx, reinterpret_cast<unsigned char *>(decryptedData.data()) + outlen1, &outlen2) != 1) {
286 // handle decryption error
287 if (ctx) {
288 EVP_CIPHER_CTX_free(ctx);
289 }
290 string msg;
291 auto errorCode = ERR_get_error();
292 while (errorCode) {
293 if (!msg.empty()) {
294 msg += "\n";
295 }
296 msg += ERR_error_string(errorCode, nullptr);
297 errorCode = ERR_get_error();
298 }
299 throw CryptoException(std::move(msg));
300 }
301
302 if (ctx) {
303 EVP_CIPHER_CTX_free(ctx);
304 }
305 const auto decryptedSize = outlen1 + outlen2;
306 if (decryptedSize < 0) {
307 throw CryptoException("Decrypted size is negative.");
308 }
309 remainingSize = static_cast<size_t>(decryptedSize);
310 if (!remainingSize) {
311 throw ParsingException("Decrypted buffer is empty.");
312 }
313
314 } else {
315 // use raw data directly if not encrypted
316 decryptedData.swap(rawData);
317 }
318
319 // decompress
320 if (compressionUsed) {
321 if (remainingSize < 8) {
322 throw ParsingException("File is truncated (decompressed size expected).");
323 }
324 if (remainingSize > numeric_limits<uLongf>::max()) {
325 throw CryptoException("Size exceeds limit.");
326 }
327 const auto rawDecompressedSize = LE::toUInt64(decryptedData.data());
328 if (rawDecompressedSize > numeric_limits<uLongf>::max()) {
329 throw ParsingException("Decompressed size exceeds limit.");
330 }
331 auto decompressedSize = static_cast<uLongf>(rawDecompressedSize);
332 rawData.resize(decompressedSize);
333 switch (uncompress(reinterpret_cast<Bytef *>(rawData.data()), &decompressedSize, reinterpret_cast<Bytef *>(decryptedData.data() + 8),
334 static_cast<uLongf>(remainingSize - 8))) {
335 case Z_MEM_ERROR:
336 throw ParsingException("Decompressing failed. The source buffer was too small.");
337 case Z_BUF_ERROR:
338 throw ParsingException("Decompressing failed. The destination buffer was too small.");
339 case Z_DATA_ERROR:
340 throw ParsingException("Decompressing failed. The input data was corrupted or incomplete.");
341 case Z_OK:
342 decryptedData.swap(rawData);
343 remainingSize = decompressedSize;
344 }
345 }
346 if (!remainingSize) {
347 throw ParsingException("Decompressed buffer is empty.");
348 }
349
350 // parse contents
351 stringstream decryptedStream(stringstream::in | stringstream::out | stringstream::binary);
352 decryptedStream.exceptions(ios_base::failbit | ios_base::badbit);
353 try {
354#if defined(__GLIBCXX__) && !defined(_LIBCPP_VERSION)
355 decryptedStream.rdbuf()->pubsetbuf(decryptedData.data(), static_cast<streamsize>(remainingSize));
356#else
357 decryptedStream.write(decryptedData.data(), static_cast<streamsize>(remainingSize));
358#endif
359 if (m_version >= 0x5u) {
360 BinaryReader reader(&decryptedStream);
361 const auto extendedHeaderSize = reader.readUInt16BE();
362 m_encryptedExtendedHeader = reader.readString(extendedHeaderSize);
363 } else {
364 m_encryptedExtendedHeader.clear();
365 }
366 m_rootEntry.reset(new NodeEntry(decryptedStream));
367 } catch (const std::ios_base::failure &failure) {
368 if (decryptedStream.eof()) {
369 throw ParsingException("The file seems to be truncated.");
370 }
371 throw ParsingException(argsToString("An IO error occurred when reading internal buffer: ", failure.what()));
372 }
373}
374
379std::uint32_t PasswordFile::mininumVersion(PasswordFileSaveFlags options) const
380{
381 if (options & PasswordFileSaveFlags::PasswordHashing) {
382 return 0x6U; // password hashing requires at least version 6
383 } else if (!m_encryptedExtendedHeader.empty()) {
384 return 0x5U; // encrypted extended header requires at least version 5
385 } else if (!m_extendedHeader.empty()) {
386 return 0x4U; // regular extended header requires at least version 4
387 }
388 return 0x3U; // lowest supported version by the serializer
389}
390
399 void PasswordFile::save(PasswordFileSaveFlags options)
400{
401 if (!m_rootEntry) {
402 throw runtime_error("Root entry has not been created.");
403 }
404
405 // use already opened and writable file; otherwise re-open the file
406 auto fileSize = std::size_t();
407 if (m_file.good() && m_file.is_open() && !(m_openOptions & PasswordFileOpenFlags::ReadOnly)) {
408 fileSize = size();
409 m_file.seekp(0);
410 } else {
411 m_file.clear();
412 if (m_file.is_open()) {
413 m_file.close();
414 }
415 try {
416 m_file.open(m_path, ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary);
417 } catch (const ios_base::failure &) {
418 // try to create a new file if configured via \a options
419 if (!(options & PasswordFileSaveFlags::AllowToCreateNewFile)) {
420 throw;
421 }
422 m_file.open(m_path, ios_base::out | ios_base::trunc | ios_base::binary);
423 }
424 }
425
426 // write entries
427 write(options);
428 m_file.flush();
429
430 // truncate file if it became smaller
431 const auto newSize = static_cast<std::size_t>(m_file.tellp());
432 if (fileSize && newSize < fileSize) {
433 m_file.close();
434 std::filesystem::resize_file(makeNativePath(m_path), newSize);
435 }
436}
437
445 void PasswordFile::write(PasswordFileSaveFlags options)
446{
447 if (!m_rootEntry) {
448 throw runtime_error("Root entry has not been created.");
449 }
450
451 // write magic number
452 m_fwriter.writeUInt32LE(0x7770616DU);
453
454 // write version
455 const auto version = mininumVersion(options);
456 m_fwriter.writeUInt32LE(version);
457
458 // write flags
459 std::uint8_t flags = 0x00;
460 if (options & PasswordFileSaveFlags::Encryption) {
461 flags |= 0x80 | 0x40;
462 }
463 if (options & PasswordFileSaveFlags::Compression) {
464 flags |= 0x20;
465 }
466 m_fwriter.writeByte(flags);
467
468 // write extended header
469 if (version >= 0x4U) {
470 if (m_extendedHeader.size() > numeric_limits<std::uint16_t>::max()) {
471 throw runtime_error("Extended header exceeds maximum size.");
472 }
473 m_fwriter.writeUInt16BE(static_cast<std::uint16_t>(m_extendedHeader.size()));
474 m_fwriter.writeString(m_extendedHeader);
475 }
476
477 // serialize root entry and descendants
478 stringstream buffstr(stringstream::in | stringstream::out | stringstream::binary);
479 buffstr.exceptions(ios_base::failbit | ios_base::badbit);
480
481 // write encrypted extended header
482 if (version >= 0x5U) {
483 if (m_encryptedExtendedHeader.size() > numeric_limits<std::uint16_t>::max()) {
484 throw runtime_error("Encrypted extended header exceeds maximum size.");
485 }
486 BinaryWriter buffstrWriter(&buffstr);
487 buffstrWriter.writeUInt16BE(static_cast<std::uint16_t>(m_encryptedExtendedHeader.size()));
488 buffstrWriter.writeString(m_encryptedExtendedHeader);
489 }
490 m_rootEntry->make(buffstr);
491 buffstr.seekp(0, ios_base::end);
492 auto size = static_cast<std::size_t>(buffstr.tellp());
493 if (size > std::numeric_limits<uLong>::max()) {
494 throw std::runtime_error("File size exceeds maximum size.");
495 }
496
497 // write the data to a buffer
498 buffstr.seekg(0);
499 vector<char> decryptedData(size, 0);
500 buffstr.read(decryptedData.data(), static_cast<streamoff>(size));
501 vector<char> encryptedData;
502
503 // compress data
504 if (options & PasswordFileSaveFlags::Compression) {
505 auto compressedSize = static_cast<uLongf>(compressBound(static_cast<uLong>(size)));
506 encryptedData.resize(8 + compressedSize);
507 LE::getBytes(static_cast<std::uint64_t>(size), encryptedData.data());
508 switch (compress(reinterpret_cast<Bytef *>(encryptedData.data() + 8), &compressedSize, reinterpret_cast<Bytef *>(decryptedData.data()),
509 static_cast<uLong>(size))) {
510 case Z_MEM_ERROR:
511 throw runtime_error("Compressing failed. The source buffer was too small.");
512 case Z_BUF_ERROR:
513 throw runtime_error("Compressing failed. The destination buffer was too small.");
514 case Z_OK:
515 encryptedData.swap(decryptedData); // compression successful
516 size = 8 + compressedSize;
517 }
518 }
519
520 if (size > numeric_limits<int>::max()) {
521 throw CryptoException("size exceeds limit");
522 }
523
524 // write data without encryption
525 if (!(options & PasswordFileSaveFlags::Encryption)) {
526 // write data to file
527 m_file.write(decryptedData.data(), static_cast<streamsize>(size));
528 m_file.flush();
529 return;
530 }
531
532 // prepare password
533 Util::OpenSsl::Sha256Sum password;
534 const uint32_t hashCount = (options & PasswordFileSaveFlags::PasswordHashing) ? Util::OpenSsl::generateRandomNumber(1, 100) : 0u;
535 if (hashCount) {
536 // hash password a few times
537 password = Util::OpenSsl::computeSha256Sum(reinterpret_cast<unsigned const char *>(m_password.data()), m_password.size());
538 for (uint32_t i = 1; i < hashCount; ++i) {
539 password = Util::OpenSsl::computeSha256Sum(password.data, Util::OpenSsl::Sha256Sum::size);
540 }
541 } else {
542 m_password.copy(reinterpret_cast<char *>(password.data), Util::OpenSsl::Sha256Sum::size);
543 }
544
545 // initiate ctx, encrypt data
546 EVP_CIPHER_CTX *ctx = nullptr;
547 unsigned char iv[aes256cbcIvSize];
548 int outlen1, outlen2;
549 encryptedData.resize(size + aes256additionalBufferSize);
550 if (RAND_bytes(iv, aes256cbcIvSize) != 1 || (ctx = EVP_CIPHER_CTX_new()) == nullptr
551 || EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, password.data, iv) != 1
552 || EVP_EncryptUpdate(ctx, reinterpret_cast<unsigned char *>(encryptedData.data()), &outlen1,
553 reinterpret_cast<unsigned char *>(decryptedData.data()), static_cast<int>(size))
554 != 1
555 || EVP_EncryptFinal_ex(ctx, reinterpret_cast<unsigned char *>(encryptedData.data()) + outlen1, &outlen2) != 1) {
556 // handle encryption error
557 if (ctx) {
558 EVP_CIPHER_CTX_free(ctx);
559 }
560 string msg;
561 auto errorCode = ERR_get_error();
562 while (errorCode) {
563 if (!msg.empty()) {
564 msg += "\n";
565 }
566 msg += ERR_error_string(errorCode, nullptr);
567 errorCode = ERR_get_error();
568 }
569 throw CryptoException(std::move(msg));
570 }
571
572 if (ctx) {
573 EVP_CIPHER_CTX_free(ctx);
574 }
575
576 // write encrypted data to file
577 if (version >= 0x6U) {
578 m_fwriter.writeUInt32BE(hashCount);
579 }
580 m_file.write(reinterpret_cast<char *>(iv), aes256cbcIvSize);
581 m_file.write(encryptedData.data(), static_cast<streamsize>(outlen1 + outlen2));
582 m_file.flush();
583}
584
588 void PasswordFile::clearEntries()
589{
590 m_rootEntry.reset();
591}
592
596 void PasswordFile::clear()
597{
598 close();
599 clearPath();
600 clearPassword();
601 clearEntries();
602 m_openOptions = PasswordFileOpenFlags::None;
603 m_extendedHeader.clear();
604 m_encryptedExtendedHeader.clear();
605}
606
612 void PasswordFile::exportToTextfile(const string &targetPath) const
613{
614 if (!m_rootEntry) {
615 throw runtime_error("Root entry has not been created.");
616 }
617 NativeFileStream output;
618 output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
619 output.open(targetPath, std::ios_base::out);
620 const auto printIndention = [&output](int level) {
621 for (int i = 0; i < level; ++i) {
622 output << " ";
623 }
624 };
625 function<void(const Entry *entry, int level)> printNode;
626 printNode = [&output, &printNode, &printIndention](const Entry *entry, int level) {
627 printIndention(level);
628 output << " - " << entry->label() << endl;
629 switch (entry->type()) {
630 case EntryType::Node:
631 for (const Entry *child : static_cast<const NodeEntry *>(entry)->children()) {
632 printNode(child, level + 1);
633 }
634 break;
635 case EntryType::Account:
636 for (const Field &field : static_cast<const AccountEntry *>(entry)->fields()) {
637 printIndention(level);
638 output << " " << field.name();
639 for (auto i = field.name().length(); i < 15; ++i) {
640 output << ' ';
641 }
642 output << field.value() << endl;
643 }
644 }
645 };
646 printNode(m_rootEntry.get(), 0);
647 output.close();
648}
649
654 void PasswordFile::doBackup()
655{
656 if (!isOpen()) {
657 open();
658 }
659
660 // skip if the current file is empty anyways
661 if (!size()) {
662 return;
663 }
664
665 m_file.seekg(0);
666 fstream backupFile(m_path + ".backup", ios::out | ios::trunc | ios::binary);
667 backupFile.exceptions(ios_base::failbit | ios_base::badbit);
668 backupFile << m_file.rdbuf();
669 backupFile.close();
670}
671
677 bool PasswordFile::hasRootEntry() const
678{
679 return m_rootEntry != nullptr;
680}
681
685 const NodeEntry *PasswordFile::rootEntry() const
686{
687 return m_rootEntry.get();
688}
689
693 NodeEntry *PasswordFile::rootEntry()
694{
695 return m_rootEntry.get();
696}
697
701 void PasswordFile::close()
702{
703 if (m_file.is_open()) {
704 m_file.close();
705 }
706 m_file.clear();
707}
708
712 void PasswordFile::setPath(const string &value)
713{
714 close();
715 m_path = value;
716
717 // support "file://" protocol
718 if (startsWith(m_path, "file:")) {
719 m_path = m_path.substr(5);
720 }
721}
722
728 bool PasswordFile::isEncryptionUsed()
729{
730 if (!isOpen()) {
731 return false;
732 }
733 m_file.seekg(0);
734
735 // check magic number
736 if (m_freader.readUInt32LE() != 0x7770616DU) {
737 return false;
738 }
739
740 // check version
741 const auto version = m_freader.readUInt32LE();
742 if (version == 0x1U || version == 0x2U) {
743 return true;
744 } else if (version >= 0x3U) {
745 return m_freader.readByte() & 0x80;
746 } else {
747 return false;
748 }
749}
750
754 size_t PasswordFile::size()
755{
756 if (!isOpen()) {
757 return 0;
758 }
759 m_file.seekg(0, ios::end);
760 return static_cast<size_t>(m_file.tellg());
761}
762
766 string PasswordFile::summary(PasswordFileSaveFlags saveOptions) const
767{
768 string result = "<table>";
769 if (!m_path.empty()) {
770 result += argsToString("<tr><td>Path:</td><td>", m_path, "</td></tr>");
771 }
772 result += argsToString("<tr><td>Version:</td><td>", m_version, "</td></tr>");
773 const auto minVersion = mininumVersion(saveOptions);
774 if (m_version != minVersion) {
775 result += argsToString("<tr><td></td><td>(on disk, after saving: ", minVersion, ")</td></tr>");
776 }
777 result += argsToString("<tr><td>Features:</td><td>", flagsToString(m_saveOptions), "</td></tr>");
778 if (m_saveOptions != saveOptions) {
779 result += argsToString("<tr><td></td><td>(on disk, after saving: ", flagsToString(saveOptions), ")</td></tr>");
780 }
781 const auto stats = m_rootEntry ? m_rootEntry->computeStatistics() : EntryStatistics();
782 result += argsToString("<tr><td>Number of categories:</td><td>", stats.nodeCount, "</td></tr><tr><td>Number of accounts:</td><td>",
783 stats.accountCount, "</td></tr><tr><td>Number of fields:</td><td>", stats.fieldCount, "</td></tr></table>");
784 return result;
785}
786
790 string flagsToString(PasswordFileOpenFlags flags)
791{
792 vector<string> options;
793 if (flags & PasswordFileOpenFlags::ReadOnly) {
794 options.emplace_back("read-only");
795 }
796 if (options.empty()) {
797 options.emplace_back("none");
798 }
799 return joinStrings(options, ", ");
800}
801
805 string flagsToString(PasswordFileSaveFlags flags)
806{
807 vector<string> options;
808 options.reserve(3);
809 if (flags & PasswordFileSaveFlags::Encryption) {
810 options.emplace_back("encryption");
811 }
812 if (flags & PasswordFileSaveFlags::Compression) {
813 options.emplace_back("compression");
814 }
815 if (flags & PasswordFileSaveFlags::PasswordHashing) {
816 options.emplace_back("password hashing");
817 }
818 if (options.empty()) {
819 options.emplace_back("none");
820 }
821 return joinStrings(options, ", ");
822}
823
824} // namespace Io
Io::AccountEntry
The exception that is thrown when a parsing error occurs.
Definition entry.h:170
Io::CryptoException
The exception that is thrown when an encryption/decryption error occurs.
Definition cryptoexception.h:11
Io::Entry
Instances of the Entry class form a hierarchic data structure used to store account information.
Definition entry.h:30
Io::Entry::label
const std::string & label() const
Returns the label.
Definition entry.h:70
Io::Entry::type
virtual EntryType type() const =0
Returns the type of the entry.
Io::Field
The Field class holds field information which consists of a name and a value and is able to serialize...
Definition field.h:15
Io::NodeEntry
The NodeEntry class acts as parent for other entries.
Definition entry.h:114
Io::ParsingException
The exception that is thrown when a parsing error occurs.
Definition parsingexception.h:11
Io::PasswordFile::rootEntry
const NodeEntry * rootEntry() const
Returns the root entry if present or nullptr otherwise.
Definition passwordfile.cpp:685
Io::PasswordFile::isOpen
bool isOpen() const
Returns an indication whether the file is open.
Definition passwordfile.h:155
Io::PasswordFile::clear
void clear()
Closes the file if opened.
Definition passwordfile.cpp:596
Io::PasswordFile::opened
void opened()
Handles the file being opened.
Definition passwordfile.cpp:136
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::hasRootEntry
bool hasRootEntry() const
Returns an indication whether a root entry is present.
Definition passwordfile.cpp:677
Io::PasswordFile::create
void create()
Creates the file.
Definition passwordfile.cpp:160
Io::PasswordFile::exportToTextfile
void exportToTextfile(const std::string &targetPath) const
Writes the current root entry to a plain text file.
Definition passwordfile.cpp:612
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::clearPassword
void clearPassword()
Clears the current password.
Definition passwordfile.h:147
Io::PasswordFile::summary
std::string summary(PasswordFileSaveFlags saveOptions) const
Returns a summary about the file (version, used features, statistics).
Definition passwordfile.cpp:766
Io::PasswordFile::PasswordFile
PasswordFile()
Constructs a new password file.
Definition passwordfile.cpp:46
Io::PasswordFile::password
const std::string & password() const
Returns the current password.
Definition passwordfile.h:123
Io::PasswordFile::setPassword
void setPassword(const std::string &password)
Sets the current password.
Definition passwordfile.h:131
Io::PasswordFile::path
const std::string & path() const
Returns the current file path.
Definition passwordfile.h:106
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::~PasswordFile
~PasswordFile()
Closes the file if still opened and destroys the instance.
Definition passwordfile.cpp:111
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::setPath
void setPath(const std::string &value)
Sets the current file path.
Definition passwordfile.cpp:712
Io::PasswordFile::mininumVersion
std::uint32_t mininumVersion(PasswordFileSaveFlags options) const
Returns the minimum file version required to write the current instance with the specified options.
Definition passwordfile.cpp:379
Io::PasswordFile::write
void write(PasswordFileSaveFlags options=PasswordFileSaveFlags::Default)
Writes the current root entry to the file which is assumed to be opened and writeable.
Definition passwordfile.cpp:445
Io::PasswordFile::generateRootEntry
void generateRootEntry()
Generates a new root entry for the file.
Definition passwordfile.cpp:149
Io::PasswordFile::clearPath
void clearPath()
Clears the current path.
Definition passwordfile.h:114
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
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::aes256additionalBufferSize
constexpr unsigned int aes256additionalBufferSize
Definition passwordfile.cpp:35
Io::EntryType::Account
@ Account
Definition entry.h:19
Io::EntryType::Node
@ Node
Definition entry.h:18
Io::aes256cbcIvSize
constexpr unsigned int aes256cbcIvSize
Definition passwordfile.cpp:33
Io::PasswordFileSaveFlags
PasswordFileSaveFlags
Definition passwordfile.h:27
Io::PasswordFileSaveFlags::None
@ None
Definition passwordfile.h:28
Io::PasswordFileSaveFlags::Compression
@ Compression
Definition passwordfile.h:30
Io::PasswordFileSaveFlags::AllowToCreateNewFile
@ AllowToCreateNewFile
Definition passwordfile.h:32
Io::PasswordFileSaveFlags::PasswordHashing
@ PasswordHashing
Definition passwordfile.h:31
Io::PasswordFileSaveFlags::Encryption
@ Encryption
Definition passwordfile.h:29
Io::aes256blockSize
constexpr unsigned int aes256blockSize
Definition passwordfile.cpp:34
Io::PasswordFileOpenFlags
PasswordFileOpenFlags
Definition passwordfile.h:19
Io::PasswordFileOpenFlags::ReadOnly
@ ReadOnly
Definition passwordfile.h:21
Io::PasswordFileOpenFlags::None
@ None
Definition passwordfile.h:20
Util::OpenSsl::generateRandomNumber
PASSWORD_FILE_EXPORT std::uint32_t generateRandomNumber(std::uint32_t min, std::uint32_t max)
Generates a random number using OpenSSL.
Definition openssl.cpp:140
Util::OpenSsl::computeSha256Sum
PASSWORD_FILE_EXPORT Sha256Sum computeSha256Sum(const unsigned char *buffer, std::size_t size)
Computes a SHA-256 sum using OpenSSL.
Definition openssl.cpp:130
openssl.h
parsingexception.h
passwordfile.h
Io::EntryStatistics
Definition entry.h:22
Util::OpenSsl::Sha256Sum
Definition openssl.h:21
Util::OpenSsl::Sha256Sum::size
static constexpr std::size_t size
Definition openssl.h:22