6 #include "../util/openssl.h"
7 #include "../util/opensslrandomdevice.h"
9 #include <c++utilities/conversion/stringbuilder.h>
10 #include <c++utilities/conversion/stringconversion.h>
12 #include <openssl/conf.h>
13 #include <openssl/err.h>
14 #include <openssl/evp.h>
15 #include <openssl/rand.h>
42 PasswordFile::PasswordFile()
43 : m_freader(BinaryReader(&m_file))
44 , m_fwriter(BinaryWriter(&m_file))
49 m_file.exceptions(ios_base::failbit | ios_base::badbit);
57 : m_freader(BinaryReader(&m_file))
58 , m_fwriter(BinaryWriter(&m_file))
63 m_file.exceptions(ios_base::failbit | ios_base::badbit);
72 : m_path(other.m_path)
73 , m_password(other.m_password)
74 , m_rootEntry(other.m_rootEntry ? make_unique<
NodeEntry>(*other.m_rootEntry) : nullptr)
75 , m_extendedHeader(other.m_extendedHeader)
76 , m_encryptedExtendedHeader(other.m_encryptedExtendedHeader)
77 , m_freader(BinaryReader(&m_file))
78 , m_fwriter(BinaryWriter(&m_file))
79 , m_version(other.m_version)
80 , m_openOptions(other.m_openOptions)
81 , m_saveOptions(other.m_saveOptions)
83 m_file.exceptions(ios_base::failbit | ios_base::badbit);
90 : m_path(move(other.m_path))
91 , m_password(move(other.m_password))
92 , m_rootEntry(move(other.m_rootEntry))
93 , m_extendedHeader(move(other.m_extendedHeader))
94 , m_encryptedExtendedHeader(move(other.m_encryptedExtendedHeader))
95 , m_file(move(other.m_file))
96 , m_freader(BinaryReader(&m_file))
97 , m_fwriter(BinaryWriter(&m_file))
98 , m_version(other.m_version)
99 , m_openOptions(other.m_openOptions)
100 , m_saveOptions(other.m_saveOptions)
118 if (m_path.empty()) {
119 throw std::ios_base::failure(
"Unable to open file because path is emtpy.");
123 m_openOptions = options;
134 m_file.seekg(0, ios_base::end);
135 if (m_file.tellg() == 0) {
136 throw std::ios_base::failure(
"File is empty.");
148 m_rootEntry.reset(
new NodeEntry(
"accounts"));
159 if (m_path.empty()) {
160 throw std::ios_base::failure(
"Unable to create file because path is empty.");
162 m_file.open(m_path, fstream::out | fstream::trunc | fstream::binary);
175 if (!m_file.is_open()) {
183 if (m_freader.readUInt32LE() != 0x7770616DU) {
188 m_version = m_freader.readUInt32LE();
189 if (m_version > 0x6U) {
190 throw ParsingException(argsToString(
"Version \"", m_version,
"\" is unknown. Only versions 0 to 6 are supported."));
192 if (m_version >= 0x6U) {
195 bool decrypterUsed, ivUsed, compressionUsed;
196 if (m_version >= 0x3U) {
197 const auto flags = m_freader.readByte();
198 if ((decrypterUsed = flags & 0x80)) {
201 if ((compressionUsed = flags & 0x20)) {
204 ivUsed = flags & 0x40;
206 if ((decrypterUsed = m_version >= 0x1U)) {
209 compressionUsed =
false;
210 ivUsed = m_version == 0x2U;
216 if (m_version >= 0x4U) {
217 std::uint16_t extendedHeaderSize = m_freader.readUInt16BE();
218 m_extendedHeader = m_freader.readString(extendedHeaderSize);
220 m_extendedHeader.clear();
224 const auto headerSize =
static_cast<size_t>(m_file.tellg());
225 m_file.seekg(0, ios_base::end);
226 auto remainingSize =
static_cast<size_t>(m_file.tellg()) - headerSize;
227 m_file.seekg(
static_cast<streamoff
>(headerSize), ios_base::beg);
230 uint32_t hashCount = 0U;
232 if (remainingSize < 4) {
235 hashCount = m_freader.readUInt32BE();
241 if (decrypterUsed && ivUsed) {
248 if (!remainingSize) {
253 vector<char> rawData;
254 m_freader.read(rawData,
static_cast<streamoff
>(remainingSize));
255 vector<char> decryptedData;
257 if (remainingSize > numeric_limits<int>::max()) {
266 for (uint32_t i = 1; i < hashCount; ++i) {
274 EVP_CIPHER_CTX *ctx =
nullptr;
275 decryptedData.resize(remainingSize + 32);
276 int outlen1, outlen2;
277 if ((ctx = EVP_CIPHER_CTX_new()) ==
nullptr || EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(),
nullptr,
password.data, iv) != 1
278 || EVP_DecryptUpdate(ctx,
reinterpret_cast<unsigned char *
>(decryptedData.data()), &outlen1,
279 reinterpret_cast<unsigned char *
>(rawData.data()),
static_cast<int>(remainingSize))
281 || EVP_DecryptFinal_ex(ctx,
reinterpret_cast<unsigned char *
>(decryptedData.data()) + outlen1, &outlen2) != 1) {
284 EVP_CIPHER_CTX_free(ctx);
287 auto errorCode = ERR_get_error();
292 msg += ERR_error_string(errorCode,
nullptr);
293 errorCode = ERR_get_error();
299 EVP_CIPHER_CTX_free(ctx);
301 const auto decryptedSize = outlen1 + outlen2;
302 if (decryptedSize < 0) {
305 remainingSize =
static_cast<size_t>(decryptedSize);
306 if (!remainingSize) {
312 decryptedData.swap(rawData);
316 if (compressionUsed) {
317 if (remainingSize < 8) {
320 if (remainingSize > numeric_limits<uLongf>::max()) {
323 const auto rawDecompressedSize = LE::toUInt64(decryptedData.data());
324 if (rawDecompressedSize > numeric_limits<uLongf>::max()) {
327 auto decompressedSize =
static_cast<uLongf
>(rawDecompressedSize);
328 rawData.resize(decompressedSize);
329 switch (uncompress(
reinterpret_cast<Bytef *
>(rawData.data()), &decompressedSize,
reinterpret_cast<Bytef *
>(decryptedData.data() + 8),
330 static_cast<uLongf
>(remainingSize - 8))) {
332 throw ParsingException(
"Decompressing failed. The source buffer was too small.");
334 throw ParsingException(
"Decompressing failed. The destination buffer was too small.");
336 throw ParsingException(
"Decompressing failed. The input data was corrupted or incomplete.");
338 decryptedData.swap(rawData);
339 remainingSize = decompressedSize;
342 if (!remainingSize) {
347 stringstream decryptedStream(stringstream::in | stringstream::out | stringstream::binary);
348 decryptedStream.exceptions(ios_base::failbit | ios_base::badbit);
350 #ifdef _LIBCPP_VERSION
351 decryptedStream.write(decryptedData.data(),
static_cast<streamsize
>(remainingSize));
353 decryptedStream.rdbuf()->pubsetbuf(decryptedData.data(),
static_cast<streamsize
>(remainingSize));
355 if (m_version >= 0x5u) {
356 BinaryReader reader(&decryptedStream);
357 const auto extendedHeaderSize = reader.readUInt16BE();
358 m_encryptedExtendedHeader = reader.readString(extendedHeaderSize);
360 m_encryptedExtendedHeader.clear();
362 m_rootEntry.reset(
new NodeEntry(decryptedStream));
363 }
catch (
const std::ios_base::failure &failure) {
364 if (decryptedStream.eof()) {
367 throw ParsingException(argsToString(
"An IO error occurred when reading internal buffer: ", failure.what()));
379 }
else if (!m_encryptedExtendedHeader.empty()) {
381 }
else if (!m_extendedHeader.empty()) {
397 throw runtime_error(
"Root entry has not been created.");
401 if (m_file.good() && m_file.is_open() && (m_file.flags() & ios_base::out)) {
405 if (m_file.is_open()) {
409 m_file.open(m_path, ios_base::in | ios_base::out | ios_base::trunc | ios_base::binary);
410 }
catch (
const ios_base::failure &) {
415 m_file.open(m_path, ios_base::out | ios_base::trunc | ios_base::binary);
433 throw runtime_error(
"Root entry has not been created.");
437 m_fwriter.writeUInt32LE(0x7770616DU);
441 m_fwriter.writeUInt32LE(
version);
444 std::uint8_t flags = 0x00;
446 flags |= 0x80 | 0x40;
451 m_fwriter.writeByte(flags);
455 if (m_extendedHeader.size() > numeric_limits<std::uint16_t>::max()) {
456 throw runtime_error(
"Extended header exceeds maximum size.");
458 m_fwriter.writeUInt16BE(
static_cast<std::uint16_t
>(m_extendedHeader.size()));
459 m_fwriter.writeString(m_extendedHeader);
463 stringstream buffstr(stringstream::in | stringstream::out | stringstream::binary);
464 buffstr.exceptions(ios_base::failbit | ios_base::badbit);
468 if (m_encryptedExtendedHeader.size() > numeric_limits<std::uint16_t>::max()) {
469 throw runtime_error(
"Encrypted extended header exceeds maximum size.");
471 BinaryWriter buffstrWriter(&buffstr);
472 buffstrWriter.writeUInt16BE(
static_cast<std::uint16_t
>(m_encryptedExtendedHeader.size()));
473 buffstrWriter.writeString(m_encryptedExtendedHeader);
475 m_rootEntry->make(buffstr);
476 buffstr.seekp(0, ios_base::end);
477 auto size =
static_cast<size_t>(buffstr.tellp());
481 vector<char> decryptedData(
size, 0);
482 buffstr.read(decryptedData.data(),
static_cast<streamoff
>(
size));
483 vector<char> encryptedData;
487 uLongf compressedSize = compressBound(
size);
488 encryptedData.resize(8 + compressedSize);
489 LE::getBytes(
static_cast<std::uint64_t
>(
size), encryptedData.data());
491 compress(
reinterpret_cast<Bytef *
>(encryptedData.data() + 8), &compressedSize,
reinterpret_cast<Bytef *
>(decryptedData.data()),
size)) {
493 throw runtime_error(
"Compressing failed. The source buffer was too small.");
495 throw runtime_error(
"Compressing failed. The destination buffer was too small.");
497 encryptedData.swap(decryptedData);
498 size = 8 + compressedSize;
502 if (
size > numeric_limits<int>::max()) {
509 m_file.write(decryptedData.data(),
static_cast<streamsize
>(
size));
520 for (uint32_t i = 1; i < hashCount; ++i) {
528 EVP_CIPHER_CTX *ctx =
nullptr;
530 int outlen1, outlen2;
531 encryptedData.resize(
size + 32);
532 if (RAND_bytes(iv,
aes256cbcIvSize) != 1 || (ctx = EVP_CIPHER_CTX_new()) ==
nullptr
533 || EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(),
nullptr,
password.data, iv) != 1
534 || EVP_EncryptUpdate(ctx,
reinterpret_cast<unsigned char *
>(encryptedData.data()), &outlen1,
535 reinterpret_cast<unsigned char *
>(decryptedData.data()),
static_cast<int>(
size))
537 || EVP_EncryptFinal_ex(ctx,
reinterpret_cast<unsigned char *
>(encryptedData.data()) + outlen1, &outlen2) != 1) {
540 EVP_CIPHER_CTX_free(ctx);
543 auto errorCode = ERR_get_error();
548 msg += ERR_error_string(errorCode,
nullptr);
549 errorCode = ERR_get_error();
555 EVP_CIPHER_CTX_free(ctx);
560 m_fwriter.writeUInt32BE(hashCount);
563 m_file.write(encryptedData.data(),
static_cast<streamsize
>(outlen1 + outlen2));
585 m_extendedHeader.clear();
586 m_encryptedExtendedHeader.clear();
598 throw runtime_error(
"Root entry has not been created.");
600 fstream output(targetPath.c_str(), ios_base::out);
601 const auto printIndention = [&output](
int level) {
602 for (
int i = 0; i < level; ++i) {
606 function<void(
const Entry *entry,
int level)> printNode;
607 printNode = [&output, &printNode, &printIndention](
const Entry *entry,
int level) {
608 printIndention(level);
609 output <<
" - " << entry->label() << endl;
610 switch (entry->type()) {
612 for (
const Entry *child :
static_cast<const NodeEntry *
>(entry)->children()) {
613 printNode(child, level + 1);
618 printIndention(level);
619 output <<
" " << field.name();
620 for (
auto i = field.name().length(); i < 15; ++i) {
623 output << field.value() << endl;
627 printNode(m_rootEntry.get(), 0);
647 fstream backupFile(m_path +
".backup", ios::out | ios::trunc | ios::binary);
648 backupFile.exceptions(ios_base::failbit | ios_base::badbit);
649 backupFile << m_file.rdbuf();
660 return m_rootEntry !=
nullptr;
668 return m_rootEntry.get();
676 return m_rootEntry.get();
684 if (m_file.is_open()) {
699 if (startsWith(m_path,
"file:")) {
700 m_path = m_path.substr(5);
717 if (m_freader.readUInt32LE() != 0x7770616DU) {
722 const auto version = m_freader.readUInt32LE();
726 return m_freader.readByte() & 0x80;
740 m_file.seekg(0, ios::end);
741 return static_cast<size_t>(m_file.tellg());
749 string result =
"<table>";
750 if (!m_path.empty()) {
751 result += argsToString(
"<tr><td>Path:</td><td>", m_path,
"</td></tr>");
753 result += argsToString(
"<tr><td>Version:</td><td>", m_version,
"</td></tr>");
755 if (m_version != minVersion) {
756 result += argsToString(
"<tr><td></td><td>(on disk, after saving: ", minVersion,
")</td></tr>");
758 result += argsToString(
"<tr><td>Features:</td><td>",
flagsToString(m_saveOptions),
"</td></tr>");
762 const auto stats = m_rootEntry ? m_rootEntry->computeStatistics() :
EntryStatistics();
763 result += argsToString(
"<tr><td>Number of categories:</td><td>", stats.nodeCount,
"</td></tr><tr><td>Number of accounts:</td><td>",
764 stats.accountCount,
"</td></tr><tr><td>Number of fields:</td><td>", stats.fieldCount,
"</td></tr></table>");
773 vector<string> options;
775 options.emplace_back(
"read-only");
777 if (options.empty()) {
778 options.emplace_back(
"none");
780 return joinStrings(options,
", ");
788 vector<string> options;
791 options.emplace_back(
"encryption");
794 options.emplace_back(
"compression");
797 options.emplace_back(
"password hashing");
799 if (options.empty()) {
800 options.emplace_back(
"none");
802 return joinStrings(options,
", ");