86 : m_path(other.m_path)
87 , m_password(other.m_password)
88 , m_rootEntry(other.m_rootEntry ? make_unique<
NodeEntry>(*other.m_rootEntry) : nullptr)
89 , m_extendedHeader(other.m_extendedHeader)
90 , m_encryptedExtendedHeader(other.m_encryptedExtendedHeader)
91 , m_freader(BinaryReader(&m_file))
92 , m_fwriter(BinaryWriter(&m_file))
93 , m_version(other.m_version)
94 , m_openOptions(other.m_openOptions)
95 , m_saveOptions(other.m_saveOptions)
97 m_file.exceptions(ios_base::failbit | ios_base::badbit);
104 : m_path(std::move(other.m_path))
105 , m_password(std::move(other.m_password))
106 , m_rootEntry(std::move(other.m_rootEntry))
107 , m_extendedHeader(std::move(other.m_extendedHeader))
108 , m_encryptedExtendedHeader(std::move(other.m_encryptedExtendedHeader))
109 , m_file(std::move(other.m_file))
110 , m_freader(BinaryReader(&m_file))
111 , m_fwriter(BinaryWriter(&m_file))
112 , m_version(other.m_version)
113 , m_openOptions(other.m_openOptions)
114 , m_saveOptions(other.m_saveOptions)
193 if (!m_file.is_open()) {
201 if (m_freader.readUInt32LE() != 0x7770616DU) {
206 m_version = m_freader.readUInt32LE();
207 if (m_version > 0x6U) {
208 throw ParsingException(argsToString(
"Version \"", m_version,
"\" is unknown. Only versions 0 to 6 are supported."));
210 auto decrypterUsed =
false, ivUsed =
false, compressionUsed =
false, hmacUsed =
false;
211 if (m_version >= 0x3U) {
212 const auto flags = m_freader.readByte();
213 if ((decrypterUsed = flags & 0x80)) {
216 if ((compressionUsed = flags & 0x20)) {
219 if ((hmacUsed = flags & 0x10)) {
222 ivUsed = flags & 0x40;
224 if ((decrypterUsed = m_version >= 0x1U)) {
227 ivUsed = m_version == 0x2U;
231 auto authTag = std::vector<char>();
232 if (m_version >= 0x4U) {
233 auto extendedHeaderSize =
static_cast<std::size_t
>(m_freader.readUInt16BE());
256 m_extendedHeader = m_freader.readString(extendedHeaderSize);
258 m_extendedHeader.clear();
262 if (hmacUsed && authTag.empty()) {
267 const auto headerSize =
static_cast<size_t>(m_file.tellg());
268 m_file.seekg(0, ios_base::end);
269 auto remainingSize =
static_cast<size_t>(m_file.tellg()) - headerSize;
270 m_file.seekg(
static_cast<streamoff
>(headerSize), ios_base::beg);
273 auto hashCount = std::uint32_t();
274 if (m_version >= 0x6U && decrypterUsed) {
275 if (remainingSize < 4) {
278 hashCount = m_freader.readUInt32BE();
287 if (decrypterUsed && ivUsed) {
294 if (!remainingSize) {
299 auto rawData = std::vector<char>();
300 m_freader.read(rawData,
static_cast<streamoff
>(remainingSize));
301 auto decryptedData = std::vector<char>();
304 if (remainingSize > numeric_limits<int>::max()) {
313 for (uint32_t i = 1; i < hashCount; ++i) {
322 auto hmacInput = std::vector<unsigned char>();
323 hmacInput.reserve(
static_cast<std::size_t
>(
aes256cbcIvSize) + rawData.size());
325 hmacInput.insert(hmacInput.end(), rawData.begin(), rawData.end());
328 if (CRYPTO_memcmp(authTag.data(), expectedTag.data,
authTagSize) != 0) {
329 throw CryptoException(
"Authentication failed: data integrity check failed (wrong password or file corrupted).");
334 EVP_CIPHER_CTX *ctx =
nullptr;
336 auto outlen1 = int(), outlen2 = int();
337 if ((ctx = EVP_CIPHER_CTX_new()) ==
nullptr || EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(),
nullptr,
password.data, iv) != 1
338 || EVP_DecryptUpdate(ctx,
reinterpret_cast<unsigned char *
>(decryptedData.data()), &outlen1,
339 reinterpret_cast<unsigned char *
>(rawData.data()),
static_cast<int>(remainingSize))
341 || EVP_DecryptFinal_ex(ctx,
reinterpret_cast<unsigned char *
>(decryptedData.data()) + outlen1, &outlen2) != 1) {
344 EVP_CIPHER_CTX_free(ctx);
346 auto msg = std::string();
347 auto errorCode = ERR_get_error();
352 msg += ERR_error_string(errorCode,
nullptr);
353 errorCode = ERR_get_error();
359 EVP_CIPHER_CTX_free(ctx);
361 const auto decryptedSize = outlen1 + outlen2;
362 if (decryptedSize < 0) {
365 remainingSize =
static_cast<size_t>(decryptedSize);
366 if (!remainingSize) {
372 decryptedData.swap(rawData);
376 if (compressionUsed) {
377 if (remainingSize < 8) {
380 if (remainingSize > numeric_limits<uLongf>::max()) {
383 const auto rawDecompressedSize = LE::toUInt64(decryptedData.data());
384 if (rawDecompressedSize > numeric_limits<uLongf>::max()) {
387 auto decompressedSize =
static_cast<uLongf
>(rawDecompressedSize);
388 rawData.resize(decompressedSize);
389 switch (uncompress(
reinterpret_cast<Bytef *
>(rawData.data()), &decompressedSize,
reinterpret_cast<Bytef *
>(decryptedData.data() + 8),
390 static_cast<uLongf
>(remainingSize - 8))) {
392 throw ParsingException(
"Decompressing failed. The source buffer was too small.");
394 throw ParsingException(
"Decompressing failed. The destination buffer was too small.");
396 throw ParsingException(
"Decompressing failed. The input data was corrupted or incomplete.");
398 decryptedData.swap(rawData);
399 remainingSize = decompressedSize;
402 if (!remainingSize) {
407 auto decryptedStream = std::stringstream(stringstream::in | stringstream::out | stringstream::binary);
408 decryptedStream.exceptions(ios_base::failbit | ios_base::badbit);
410#if defined(__GLIBCXX__) && !defined(_LIBCPP_VERSION)
411 decryptedStream.rdbuf()->pubsetbuf(decryptedData.data(),
static_cast<streamsize
>(remainingSize));
413 decryptedStream.write(decryptedData.data(),
static_cast<streamsize
>(remainingSize));
415 if (m_version >= 0x5u) {
416 BinaryReader reader(&decryptedStream);
417 const auto extendedHeaderSize = reader.readUInt16BE();
418 m_encryptedExtendedHeader = reader.readString(extendedHeaderSize);
420 m_encryptedExtendedHeader.clear();
422 m_rootEntry.reset(
new NodeEntry(decryptedStream));
423 }
catch (
const std::ios_base::failure &failure) {
424 if (decryptedStream.eof()) {
427 throw ParsingException(argsToString(
"An IO error occurred when reading internal buffer: ", failure.what()));
506 throw runtime_error(
"Root entry has not been created.");
510 m_fwriter.writeUInt32LE(0x7770616DU);
514 m_fwriter.writeUInt32LE(
version);
517 auto flags = std::uint8_t{0x00};
519 flags |= 0x80 | 0x40;
527 m_fwriter.writeByte(flags);
530 stringstream buffstr(stringstream::in | stringstream::out | stringstream::binary);
531 buffstr.exceptions(ios_base::failbit | ios_base::badbit);
535 if (m_encryptedExtendedHeader.size() > numeric_limits<std::uint16_t>::max()) {
536 throw runtime_error(
"Encrypted extended header exceeds maximum size.");
538 BinaryWriter buffstrWriter(&buffstr);
539 buffstrWriter.writeUInt16BE(
static_cast<std::uint16_t
>(m_encryptedExtendedHeader.size()));
540 buffstrWriter.writeString(m_encryptedExtendedHeader);
542 m_rootEntry->make(buffstr);
543 buffstr.seekp(0, ios_base::end);
544 auto size =
static_cast<std::size_t
>(buffstr.tellp());
545 if (
size > std::numeric_limits<uLong>::max()) {
546 throw std::runtime_error(
"File size exceeds maximum size.");
551 auto decryptedData = std::vector<char>(
size, 0);
552 buffstr.read(decryptedData.data(),
static_cast<streamoff
>(
size));
553 auto encryptedData = std::vector<char>();
557 auto compressedSize =
static_cast<uLongf
>(compressBound(
static_cast<uLong
>(
size)));
558 encryptedData.resize(8 + compressedSize);
559 LE::getBytes(
static_cast<std::uint64_t
>(
size), encryptedData.data());
560 switch (compress(
reinterpret_cast<Bytef *
>(encryptedData.data() + 8), &compressedSize,
reinterpret_cast<Bytef *
>(decryptedData.data()),
561 static_cast<uLong
>(
size))) {
563 throw runtime_error(
"Compressing failed. The source buffer was too small.");
565 throw runtime_error(
"Compressing failed. The destination buffer was too small.");
567 encryptedData.swap(decryptedData);
568 size = 8 + compressedSize;
572 if (
size > numeric_limits<int>::max()) {
579 EVP_CIPHER_CTX *ctx =
nullptr;
581 auto ciphertextSize = std::size_t();
588 for (uint32_t i = 1; i < hashCount; ++i) {
596 int outlen1, outlen2;
598 if (RAND_bytes(iv,
aes256cbcIvSize) != 1 || (ctx = EVP_CIPHER_CTX_new()) ==
nullptr
599 || EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(),
nullptr,
password.data, iv) != 1
600 || EVP_EncryptUpdate(ctx,
reinterpret_cast<unsigned char *
>(encryptedData.data()), &outlen1,
601 reinterpret_cast<unsigned char *
>(decryptedData.data()),
static_cast<int>(
size))
603 || EVP_EncryptFinal_ex(ctx,
reinterpret_cast<unsigned char *
>(encryptedData.data()) + outlen1, &outlen2) != 1) {
606 EVP_CIPHER_CTX_free(ctx);
609 auto errorCode = ERR_get_error();
614 msg += ERR_error_string(errorCode,
nullptr);
615 errorCode = ERR_get_error();
620 EVP_CIPHER_CTX_free(ctx);
622 ciphertextSize =
static_cast<std::size_t
>(outlen1 + outlen2);
627 auto extendedHeaderSize = m_extendedHeader.size();
631 if (extendedHeaderSize > numeric_limits<std::uint16_t>::max()) {
632 throw runtime_error(
"Extended header exceeds maximum size.");
634 m_fwriter.writeUInt16BE(
static_cast<std::uint16_t
>(extendedHeaderSize));
638 vector<unsigned char> hmacInput;
639 hmacInput.reserve(
static_cast<std::size_t
>(
aes256cbcIvSize) + ciphertextSize);
641 hmacInput.insert(hmacInput.end(),
reinterpret_cast<const unsigned char *
>(encryptedData.data()),
642 reinterpret_cast<const unsigned char *
>(encryptedData.data()) + ciphertextSize);
645 m_file.write(
reinterpret_cast<const char *
>(hmacTag.data),
authTagSize);
648 m_fwriter.writeString(m_extendedHeader);
654 m_fwriter.writeUInt32BE(hashCount);
657 m_file.write(
reinterpret_cast<const char *
>(encryptedData.data()),
static_cast<streamsize
>(ciphertextSize));
659 m_file.write(decryptedData.data(),
static_cast<streamsize
>(
size));
694 throw runtime_error(
"Root entry has not been created.");
696 NativeFileStream output;
697 output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
698 output.open(targetPath, std::ios_base::out);
699 const auto printIndention = [&output](
int level) {
700 for (
int i = 0; i < level; ++i) {
704 auto printNode = std::function<void(const Entry *entry, int level)>();
705 printNode = [&output, &printNode, &printIndention](
const Entry *entry,
int level) {
706 printIndention(level);
707 output <<
" - " << entry->label() << endl;
708 switch (entry->type()) {
710 for (
const Entry *child :
static_cast<const NodeEntry *
>(entry)->children()) {
711 printNode(child, level + 1);
716 printIndention(level);
717 output <<
" " << field.name();
718 for (
auto i = field.name().length(); i < 15; ++i) {
721 output << field.value() << endl;
725 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.
The NodeEntry class acts as parent for other entries.