75 : m_path(other.m_path)
76 , m_password(other.m_password)
77 , m_rootEntry(other.m_rootEntry ? make_unique<
NodeEntry>(*other.m_rootEntry) : nullptr)
78 , m_extendedHeader(other.m_extendedHeader)
79 , m_encryptedExtendedHeader(other.m_encryptedExtendedHeader)
80 , m_freader(BinaryReader(&m_file))
81 , m_fwriter(BinaryWriter(&m_file))
82 , m_version(other.m_version)
83 , m_openOptions(other.m_openOptions)
84 , m_saveOptions(other.m_saveOptions)
86 m_file.exceptions(ios_base::failbit | ios_base::badbit);
93 : m_path(std::move(other.m_path))
94 , m_password(std::move(other.m_password))
95 , m_rootEntry(std::move(other.m_rootEntry))
96 , m_extendedHeader(std::move(other.m_extendedHeader))
97 , m_encryptedExtendedHeader(std::move(other.m_encryptedExtendedHeader))
98 , m_file(std::move(other.m_file))
99 , m_freader(BinaryReader(&m_file))
100 , m_fwriter(BinaryWriter(&m_file))
101 , m_version(other.m_version)
102 , m_openOptions(other.m_openOptions)
103 , m_saveOptions(other.m_saveOptions)
178 if (!m_file.is_open()) {
186 if (m_freader.readUInt32LE() != 0x7770616DU) {
191 m_version = m_freader.readUInt32LE();
192 if (m_version > 0x6U) {
193 throw ParsingException(argsToString(
"Version \"", m_version,
"\" is unknown. Only versions 0 to 6 are supported."));
195 if (m_version >= 0x6U) {
198 bool decrypterUsed, ivUsed, compressionUsed;
199 if (m_version >= 0x3U) {
200 const auto flags = m_freader.readByte();
201 if ((decrypterUsed = flags & 0x80)) {
204 if ((compressionUsed = flags & 0x20)) {
207 ivUsed = flags & 0x40;
209 if ((decrypterUsed = m_version >= 0x1U)) {
212 compressionUsed =
false;
213 ivUsed = m_version == 0x2U;
219 if (m_version >= 0x4U) {
220 std::uint16_t extendedHeaderSize = m_freader.readUInt16BE();
221 m_extendedHeader = m_freader.readString(extendedHeaderSize);
223 m_extendedHeader.clear();
227 const auto headerSize =
static_cast<size_t>(m_file.tellg());
228 m_file.seekg(0, ios_base::end);
229 auto remainingSize =
static_cast<size_t>(m_file.tellg()) - headerSize;
230 m_file.seekg(
static_cast<streamoff
>(headerSize), ios_base::beg);
233 uint32_t hashCount = 0U;
235 if (remainingSize < 4) {
238 hashCount = m_freader.readUInt32BE();
244 if (decrypterUsed && ivUsed) {
251 if (!remainingSize) {
256 vector<char> rawData;
257 m_freader.read(rawData,
static_cast<streamoff
>(remainingSize));
258 vector<char> decryptedData;
260 if (remainingSize > numeric_limits<int>::max()) {
269 for (uint32_t i = 1; i < hashCount; ++i) {
277 EVP_CIPHER_CTX *ctx =
nullptr;
279 int outlen1, outlen2;
280 if ((ctx = EVP_CIPHER_CTX_new()) ==
nullptr || EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(),
nullptr,
password.data, iv) != 1
281 || EVP_DecryptUpdate(ctx,
reinterpret_cast<unsigned char *
>(decryptedData.data()), &outlen1,
282 reinterpret_cast<unsigned char *
>(rawData.data()),
static_cast<int>(remainingSize))
284 || EVP_DecryptFinal_ex(ctx,
reinterpret_cast<unsigned char *
>(decryptedData.data()) + outlen1, &outlen2) != 1) {
287 EVP_CIPHER_CTX_free(ctx);
290 auto errorCode = ERR_get_error();
295 msg += ERR_error_string(errorCode,
nullptr);
296 errorCode = ERR_get_error();
302 EVP_CIPHER_CTX_free(ctx);
304 const auto decryptedSize = outlen1 + outlen2;
305 if (decryptedSize < 0) {
308 remainingSize =
static_cast<size_t>(decryptedSize);
309 if (!remainingSize) {
315 decryptedData.swap(rawData);
319 if (compressionUsed) {
320 if (remainingSize < 8) {
323 if (remainingSize > numeric_limits<uLongf>::max()) {
326 const auto rawDecompressedSize = LE::toUInt64(decryptedData.data());
327 if (rawDecompressedSize > numeric_limits<uLongf>::max()) {
330 auto decompressedSize =
static_cast<uLongf
>(rawDecompressedSize);
331 rawData.resize(decompressedSize);
332 switch (uncompress(
reinterpret_cast<Bytef *
>(rawData.data()), &decompressedSize,
reinterpret_cast<Bytef *
>(decryptedData.data() + 8),
333 static_cast<uLongf
>(remainingSize - 8))) {
335 throw ParsingException(
"Decompressing failed. The source buffer was too small.");
337 throw ParsingException(
"Decompressing failed. The destination buffer was too small.");
339 throw ParsingException(
"Decompressing failed. The input data was corrupted or incomplete.");
341 decryptedData.swap(rawData);
342 remainingSize = decompressedSize;
345 if (!remainingSize) {
350 stringstream decryptedStream(stringstream::in | stringstream::out | stringstream::binary);
351 decryptedStream.exceptions(ios_base::failbit | ios_base::badbit);
353#if defined(__GLIBCXX__) && !defined(_LIBCPP_VERSION)
354 decryptedStream.rdbuf()->pubsetbuf(decryptedData.data(),
static_cast<streamsize
>(remainingSize));
356 decryptedStream.write(decryptedData.data(),
static_cast<streamsize
>(remainingSize));
358 if (m_version >= 0x5u) {
359 BinaryReader reader(&decryptedStream);
360 const auto extendedHeaderSize = reader.readUInt16BE();
361 m_encryptedExtendedHeader = reader.readString(extendedHeaderSize);
363 m_encryptedExtendedHeader.clear();
365 m_rootEntry.reset(
new NodeEntry(decryptedStream));
366 }
catch (
const std::ios_base::failure &failure) {
367 if (decryptedStream.eof()) {
370 throw ParsingException(argsToString(
"An IO error occurred when reading internal buffer: ", failure.what()));
446 throw runtime_error(
"Root entry has not been created.");
450 m_fwriter.writeUInt32LE(0x7770616DU);
454 m_fwriter.writeUInt32LE(
version);
457 std::uint8_t flags = 0x00;
459 flags |= 0x80 | 0x40;
464 m_fwriter.writeByte(flags);
468 if (m_extendedHeader.size() > numeric_limits<std::uint16_t>::max()) {
469 throw runtime_error(
"Extended header exceeds maximum size.");
471 m_fwriter.writeUInt16BE(
static_cast<std::uint16_t
>(m_extendedHeader.size()));
472 m_fwriter.writeString(m_extendedHeader);
476 stringstream buffstr(stringstream::in | stringstream::out | stringstream::binary);
477 buffstr.exceptions(ios_base::failbit | ios_base::badbit);
481 if (m_encryptedExtendedHeader.size() > numeric_limits<std::uint16_t>::max()) {
482 throw runtime_error(
"Encrypted extended header exceeds maximum size.");
484 BinaryWriter buffstrWriter(&buffstr);
485 buffstrWriter.writeUInt16BE(
static_cast<std::uint16_t
>(m_encryptedExtendedHeader.size()));
486 buffstrWriter.writeString(m_encryptedExtendedHeader);
488 m_rootEntry->make(buffstr);
489 buffstr.seekp(0, ios_base::end);
490 auto size =
static_cast<size_t>(buffstr.tellp());
494 vector<char> decryptedData(
size, 0);
495 buffstr.read(decryptedData.data(),
static_cast<streamoff
>(
size));
496 vector<char> encryptedData;
500 uLongf compressedSize = compressBound(
size);
501 encryptedData.resize(8 + compressedSize);
502 LE::getBytes(
static_cast<std::uint64_t
>(
size), encryptedData.data());
504 compress(
reinterpret_cast<Bytef *
>(encryptedData.data() + 8), &compressedSize,
reinterpret_cast<Bytef *
>(decryptedData.data()),
size)) {
506 throw runtime_error(
"Compressing failed. The source buffer was too small.");
508 throw runtime_error(
"Compressing failed. The destination buffer was too small.");
510 encryptedData.swap(decryptedData);
511 size = 8 + compressedSize;
515 if (
size > numeric_limits<int>::max()) {
522 m_file.write(decryptedData.data(),
static_cast<streamsize
>(
size));
533 for (uint32_t i = 1; i < hashCount; ++i) {
541 EVP_CIPHER_CTX *ctx =
nullptr;
543 int outlen1, outlen2;
545 if (RAND_bytes(iv,
aes256cbcIvSize) != 1 || (ctx = EVP_CIPHER_CTX_new()) ==
nullptr
546 || EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(),
nullptr,
password.data, iv) != 1
547 || EVP_EncryptUpdate(ctx,
reinterpret_cast<unsigned char *
>(encryptedData.data()), &outlen1,
548 reinterpret_cast<unsigned char *
>(decryptedData.data()),
static_cast<int>(
size))
550 || EVP_EncryptFinal_ex(ctx,
reinterpret_cast<unsigned char *
>(encryptedData.data()) + outlen1, &outlen2) != 1) {
553 EVP_CIPHER_CTX_free(ctx);
556 auto errorCode = ERR_get_error();
561 msg += ERR_error_string(errorCode,
nullptr);
562 errorCode = ERR_get_error();
568 EVP_CIPHER_CTX_free(ctx);
573 m_fwriter.writeUInt32BE(hashCount);
576 m_file.write(encryptedData.data(),
static_cast<streamsize
>(outlen1 + outlen2));
610 throw runtime_error(
"Root entry has not been created.");
612 NativeFileStream output;
613 output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
614 output.open(targetPath, std::ios_base::out);
615 const auto printIndention = [&output](
int level) {
616 for (
int i = 0; i < level; ++i) {
620 function<void(
const Entry *entry,
int level)> printNode;
621 printNode = [&output, &printNode, &printIndention](
const Entry *entry,
int level) {
622 printIndention(level);
623 output <<
" - " << entry->
label() << endl;
624 switch (entry->
type()) {
626 for (
const Entry *child :
static_cast<const NodeEntry *
>(entry)->children()) {
627 printNode(child, level + 1);
632 printIndention(level);
633 output <<
" " << field.name();
634 for (
auto i = field.name().length(); i < 15; ++i) {
637 output << field.value() << endl;
641 printNode(m_rootEntry.get(), 0);
The exception that is thrown when a parsing error occurs.
Instances of the Entry class form a hierarchic data structure used to store account information.
virtual EntryType type() const =0
Returns the type of the entry.
const std::string & label() const
Returns the label.
The NodeEntry class acts as parent for other entries.