diff --git a/io/passwordfile.cpp b/io/passwordfile.cpp index 9b53815..0ad0988 100644 --- a/io/passwordfile.cpp +++ b/io/passwordfile.cpp @@ -15,10 +15,10 @@ #include #include +#include #include #include #include -#include using namespace std; using namespace IoUtilities; @@ -159,20 +159,20 @@ void PasswordFile::load() open(); } m_file.seekg(0); + // check magic number if (m_freader.readUInt32LE() != 0x7770616DU) { throw ParsingException("Signature not present."); } + // check version and flags (used in version 0x3 only) - uint32 version = m_freader.readUInt32LE(); + const auto version = m_freader.readUInt32LE(); if (version != 0x0U && version != 0x1U && version != 0x2U && version != 0x3U && version != 0x4U && version != 0x5U) { throw ParsingException("Version is unknown."); } - bool decrypterUsed; - bool ivUsed; - bool compressionUsed; + bool decrypterUsed, ivUsed, compressionUsed; if (version == 0x3U) { - byte flags = m_freader.readByte(); + const auto flags = m_freader.readByte(); decrypterUsed = flags & 0x80; ivUsed = flags & 0x40; compressionUsed = flags & 0x20; @@ -181,50 +181,54 @@ void PasswordFile::load() ivUsed = version == 0x2U; compressionUsed = false; } + // skip extended header - // the extended header might be used in further versions to - // add additional information without breaking compatibility + // (the extended header might be used in further versions to + // add additional information without breaking compatibility) if (version >= 0x4U) { uint16 extendedHeaderSize = m_freader.readUInt16BE(); m_extendedHeader = m_freader.readString(extendedHeaderSize); } + // get length - auto headerSize = static_cast(m_file.tellg()); + const auto headerSize = static_cast(m_file.tellg()); m_file.seekg(0, ios_base::end); - auto size = static_cast(m_file.tellg()); + auto remainingSize = static_cast(m_file.tellg()) - headerSize; m_file.seekg(static_cast(headerSize), ios_base::beg); - size -= headerSize; + // read file unsigned char iv[aes256cbcIvSize] = { 0 }; if (decrypterUsed && ivUsed) { - if (size < aes256cbcIvSize) { - throw ParsingException("Initiation vector not present."); + if (remainingSize < aes256cbcIvSize) { + throw ParsingException("Initiation vector is truncated."); } m_file.read(reinterpret_cast(iv), aes256cbcIvSize); - size -= aes256cbcIvSize; + remainingSize -= aes256cbcIvSize; } - if (size <= 0) { + if (!remainingSize) { throw ParsingException("No contents found."); } + // decrypt contents - vector rawbuff; - m_freader.read(rawbuff, static_cast(size)); - vector decbuff; + vector rawData; + m_freader.read(rawData, static_cast(remainingSize)); + vector decryptedData; if (decrypterUsed) { - if (size > numeric_limits::max()) { + if (remainingSize > numeric_limits::max()) { throw CryptoException("Size exceeds limit."); } - // initiate ctx + // initiate ctx, decrypt data EVP_CIPHER_CTX *ctx = nullptr; - decbuff.resize(size + 32); + decryptedData.resize(remainingSize + 32); int outlen1, outlen2; if ((ctx = EVP_CIPHER_CTX_new()) == nullptr || EVP_DecryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, reinterpret_cast(m_password), iv) != 1 - || EVP_DecryptUpdate( - ctx, reinterpret_cast(decbuff.data()), &outlen1, reinterpret_cast(rawbuff.data()), static_cast(size)) + || EVP_DecryptUpdate(ctx, reinterpret_cast(decryptedData.data()), &outlen1, + reinterpret_cast(rawData.data()), static_cast(remainingSize)) != 1 - || EVP_DecryptFinal_ex(ctx, reinterpret_cast(decbuff.data()) + outlen1, &outlen2) != 1) { + || EVP_DecryptFinal_ex(ctx, reinterpret_cast(decryptedData.data()) + outlen1, &outlen2) != 1) { + // handle decryption error if (ctx) { EVP_CIPHER_CTX_free(ctx); } @@ -238,28 +242,31 @@ void PasswordFile::load() errorCode = ERR_get_error(); } throw CryptoException(msg); - } else { // decryption suceeded - if (ctx) { - EVP_CIPHER_CTX_free(ctx); - } - const auto decryptedSize = outlen1 + outlen2; - if (decryptedSize < 0) { - throw CryptoException("Decrypted size is negative."); - } - size = static_cast(decryptedSize); } - } else { // file is not crypted - decbuff.swap(rawbuff); + + if (ctx) { + EVP_CIPHER_CTX_free(ctx); + } + const auto decryptedSize = outlen1 + outlen2; + if (decryptedSize < 0) { + throw CryptoException("Decrypted size is negative."); + } + remainingSize = static_cast(decryptedSize); + + } else { + // use raw data directly if not encrypted + decryptedData.swap(rawData); } + // decompress if (compressionUsed) { - if (size < 8) { + if (remainingSize < 8) { throw ParsingException("File is truncated (decompressed size expected)."); } - uLongf decompressedSize = ConversionUtilities::LE::toUInt64(decbuff.data()); - rawbuff.resize(decompressedSize); - switch (uncompress(reinterpret_cast(rawbuff.data()), &decompressedSize, reinterpret_cast(decbuff.data() + 8), - size - 8)) { + uLongf decompressedSize = ConversionUtilities::LE::toUInt64(decryptedData.data()); + rawData.resize(decompressedSize); + switch (uncompress( + reinterpret_cast(rawData.data()), &decompressedSize, reinterpret_cast(decryptedData.data() + 8), remainingSize - 8)) { case Z_MEM_ERROR: throw ParsingException("Decompressing failed. The source buffer was too small."); case Z_BUF_ERROR: @@ -267,20 +274,21 @@ void PasswordFile::load() case Z_DATA_ERROR: throw ParsingException("Decompressing failed. The input data was corrupted or incomplete."); case Z_OK: - decbuff.swap(rawbuff); // decompression successful - size = decompressedSize; + decryptedData.swap(rawData); + remainingSize = decompressedSize; } } + // parse contents - stringstream buffstr(stringstream::in | stringstream::out | stringstream::binary); - buffstr.write(decbuff.data(), static_cast(size)); - decbuff.resize(0); - buffstr.seekg(0, ios_base::beg); + stringstream decryptedStream(stringstream::in | stringstream::out | stringstream::binary); + decryptedStream.write(decryptedData.data(), static_cast(remainingSize)); + decryptedData.resize(0); + decryptedStream.seekg(0, ios_base::beg); if (version >= 0x5u) { - uint16 extendedHeaderSize = m_freader.readUInt16BE(); + const auto extendedHeaderSize = m_freader.readUInt16BE(); m_encryptedExtendedHeader = m_freader.readString(extendedHeaderSize); } - m_rootEntry.reset(new NodeEntry(buffstr)); + m_rootEntry.reset(new NodeEntry(decryptedStream)); } /*! @@ -359,22 +367,23 @@ void PasswordFile::write(bool useEncryption, bool useCompression) // write the data to a buffer buffstr.seekg(0); - vector decbuff(size, 0); - buffstr.read(decbuff.data(), static_cast(size)); - vector encbuff; + vector decryptedData(size, 0); + buffstr.read(decryptedData.data(), static_cast(size)); + vector encryptedData; // compress data if (useCompression) { uLongf compressedSize = compressBound(size); - encbuff.resize(8 + compressedSize); - ConversionUtilities::LE::getBytes(static_cast(size), encbuff.data()); - switch (compress(reinterpret_cast(encbuff.data() + 8), &compressedSize, reinterpret_cast(decbuff.data()), size)) { + encryptedData.resize(8 + compressedSize); + ConversionUtilities::LE::getBytes(static_cast(size), encryptedData.data()); + switch ( + compress(reinterpret_cast(encryptedData.data() + 8), &compressedSize, reinterpret_cast(decryptedData.data()), size)) { case Z_MEM_ERROR: throw runtime_error("Compressing failed. The source buffer was too small."); case Z_BUF_ERROR: throw runtime_error("Compressing failed. The destination buffer was too small."); case Z_OK: - encbuff.swap(decbuff); // decompression successful + encryptedData.swap(decryptedData); // decompression successful size = 8 + compressedSize; } } @@ -386,7 +395,7 @@ void PasswordFile::write(bool useEncryption, bool useCompression) // write data without encryption if (!useEncryption) { // write data to file - m_file.write(decbuff.data(), static_cast(size)); + m_file.write(decryptedData.data(), static_cast(size)); return; } @@ -394,13 +403,13 @@ void PasswordFile::write(bool useEncryption, bool useCompression) EVP_CIPHER_CTX *ctx = nullptr; unsigned char iv[aes256cbcIvSize]; int outlen1, outlen2; - encbuff.resize(size + 32); + encryptedData.resize(size + 32); if (RAND_bytes(iv, aes256cbcIvSize) != 1 || (ctx = EVP_CIPHER_CTX_new()) == nullptr || EVP_EncryptInit_ex(ctx, EVP_aes_256_cbc(), nullptr, reinterpret_cast(m_password), iv) != 1 - || EVP_EncryptUpdate( - ctx, reinterpret_cast(encbuff.data()), &outlen1, reinterpret_cast(decbuff.data()), static_cast(size)) + || EVP_EncryptUpdate(ctx, reinterpret_cast(encryptedData.data()), &outlen1, + reinterpret_cast(decryptedData.data()), static_cast(size)) != 1 - || EVP_EncryptFinal_ex(ctx, reinterpret_cast(encbuff.data()) + outlen1, &outlen2) != 1) { + || EVP_EncryptFinal_ex(ctx, reinterpret_cast(encryptedData.data()) + outlen1, &outlen2) != 1) { // handle encryption error if (ctx) { EVP_CIPHER_CTX_free(ctx); @@ -423,7 +432,7 @@ void PasswordFile::write(bool useEncryption, bool useCompression) // write encrypted data to file m_file.write(reinterpret_cast(iv), aes256cbcIvSize); - m_file.write(encbuff.data(), static_cast(outlen1 + outlen2)); + m_file.write(encryptedData.data(), static_cast(outlen1 + outlen2)); } /*!