From 622323117a518fd7c709f2004553b65902a1012e Mon Sep 17 00:00:00 2001 From: njohnson Date: Mon, 15 Sep 2025 18:51:45 -0400 Subject: [PATCH] Refactor: Improved encryption library performance and stability. --- libs/encryption/encryption.cpp | 194 ++++++++++++++++----------------- libs/encryption/encryption.h | 3 +- 2 files changed, 94 insertions(+), 103 deletions(-) diff --git a/libs/encryption/encryption.cpp b/libs/encryption/encryption.cpp index 6be65a8..a4c5732 100644 --- a/libs/encryption/encryption.cpp +++ b/libs/encryption/encryption.cpp @@ -1,27 +1,35 @@ -#include "encryption.h" +#include #include "QtZlib/zlib.h" #include "ecrypt-sync.h" +#include "sha1.h" +#include "encryption.h" #include "compression.h" -void Encryption::Convert32BitTo8Bit(quint32 value, quint8 *array) { +static QVector ivCounter(4, 1); // start all counters at 1 + +void Encryption::Convert32BitTo8Bit(quint32 value, quint8 *array) +{ array[0] = static_cast(value >> 0); array[1] = static_cast(value >> 8); array[2] = static_cast(value >> 16); array[3] = static_cast(value >> 24); } -quint32 Encryption::ConvertArrayTo32Bit(const QByteArray &array) { +quint32 Encryption::ConvertArrayTo32Bit(const QByteArray &array) +{ return ((static_cast(static_cast(array[0])) << 0) | (static_cast(static_cast(array[1])) << 8) | (static_cast(static_cast(array[2])) << 16) | (static_cast(static_cast(array[3])) << 24)); } -quint32 Encryption::Rotate(quint32 value, quint32 numBits) { +quint32 Encryption::Rotate(quint32 value, quint32 numBits) +{ return (value << numBits) | (value >> (32 - numBits)); } -QByteArray Encryption::InitIVTable(const QByteArray &feed) { +QByteArray Encryption::InitIVTable(const QByteArray &feed) +{ const int tableSize = 0xFB0; QByteArray table; table.resize(tableSize); @@ -48,22 +56,23 @@ QByteArray Encryption::InitIVTable(const QByteArray &feed) { return table; } -int Encryption::unk(quint64 arg1, quint8 arg2) { +int Encryption::unk(quint64 arg1, quint8 arg2) +{ if (arg2 >= 0x40) return 0; return static_cast(arg1 >> arg2); } -QByteArray Encryption::GetIV(const QByteArray &table, int index) { - int num1 = 0xFA0 + index; +QByteArray Encryption::GetIV(const QByteArray &table, int index) +{ + int num1 = (4 * index % 4 + 0xFA0) + index % 4 + (index - (index % 4)); int num2 = unk(0x51EB851FLL * num1, 0x20); - int adjust = ((num2 >> 6) + (num2 >> 31)); - int startIndex = 20 * (num1 - 200 * adjust); - // Return 8 bytes from that location. + int startIndex = 20 * (num1 - 200 * ((num2 >> 6) + (num2 >> 31))); return table.mid(startIndex, 8); } -void Encryption::UpdateIVTable(QByteArray &table, int index, const QByteArray §ionHash) { +void Encryption::UpdateIVTable(QByteArray &table, int index, const QByteArray §ionHash) +{ int blockNumIndex = index % 4; int baseOffset = 0xFA0 + blockNumIndex * 4; quint32 blockNumVal = (static_cast(table.at(baseOffset)) ) | @@ -86,7 +95,8 @@ void Encryption::UpdateIVTable(QByteArray &table, int index, const QByteArray &s } } -quint32 Encryption::ToUInt32(const QByteArray &data, int offset) { +quint32 Encryption::ToUInt32(const QByteArray &data, int offset) +{ // Converts 4 bytes (starting at offset) from data into a 32-bit quint32eger (little-endian) return ((static_cast(static_cast(data[offset])) ) | (static_cast(static_cast(data[offset+1])) << 8 ) | @@ -351,96 +361,8 @@ void Encryption::generateNewIV(int index, const QByteArray &hash, QByteArray &iv ivCounter[index]++; } -QByteArray Encryption::decryptFastFile_BO2(const QByteArray &fastFileData) +QByteArray Encryption::decryptFastFile_BO3(const QByteArray &fastFileData) { - const QByteArray bo2_salsa20_key = QByteArray::fromHex("641D8A2FE31D3AA63622BBC9CE8587229D42B0F8ED9B924130BF88B65EDC50BE"); - - QByteArray fileData = fastFileData; - QByteArray finalFastFile; - - QByteArray ivTable(16000, 0); - fillIVTable(fileData, ivTable, 16000 - 1); - - QVector ivCounter(4, 1); - QDataStream stream(fileData); - stream.setByteOrder(QDataStream::LittleEndian); - stream.skipRawData(0x138); - - QByteArray sha1Hash(20, 0); - QByteArray ivPtr(8, 0); - int chunkIndex = 0; - - while (!stream.atEnd()) { - quint32 dataLength; - stream >> dataLength; - - if (dataLength == 0 || dataLength > fileData.size() - stream.device()->pos()) { - qWarning() << "Invalid data length at offset: " << stream.device()->pos(); - break; - } - - fillIV(chunkIndex % 4, ivPtr, ivTable, ivCounter); - - ECRYPT_ctx x; - ECRYPT_keysetup(&x, reinterpret_cast(bo2_salsa20_key.constData()), 256, 0); - ECRYPT_ivsetup(&x, reinterpret_cast(ivPtr.constData())); - - QByteArray encryptedBlock = fileData.mid(stream.device()->pos(), dataLength); - QByteArray decryptedBlock; - decryptedBlock.resize(dataLength); - - ECRYPT_decrypt_bytes(&x, reinterpret_cast(encryptedBlock.constData()), - reinterpret_cast(decryptedBlock.data()), dataLength); - - QCryptographicHash sha1(QCryptographicHash::Sha1); - sha1.addData(decryptedBlock); - sha1Hash = sha1.result(); - - z_stream strm = {}; - strm.zalloc = Z_NULL; - strm.zfree = Z_NULL; - strm.opaque = Z_NULL; - strm.avail_in = static_cast(decryptedBlock.size()); - strm.next_in = reinterpret_cast(decryptedBlock.data()); - - QByteArray decompressedData; - decompressedData.resize(fmax(dataLength * 2, 4096)); - strm.avail_out = decompressedData.size(); - strm.next_out = reinterpret_cast(decompressedData.data()); - - int zReturn = inflateInit2(&strm, -15); - if (zReturn != Z_OK) { - qWarning() << "inflateInit2 failed with error code" << zReturn; - break; - } - - zReturn = inflate(&strm, Z_FINISH); - inflateEnd(&strm); - - if (zReturn != Z_STREAM_END) { - qDebug() << "Error decompressing at offset: " << stream.device()->pos() << " : " << zReturn; - decompressedData.clear(); - } else { - decompressedData.resize(strm.total_out); - } - - finalFastFile.append(decompressedData); - - generateNewIV(chunkIndex % 4, sha1Hash, ivTable, ivCounter); - - if (stream.device()->pos() + static_cast(dataLength) > fileData.size()) { - qWarning() << "Skipping past file size!"; - break; - } - - stream.skipRawData(dataLength); - chunkIndex++; - } - - return finalFastFile; -} - -QByteArray Encryption::decryptFastFile_BO3(const QByteArray &fastFileData) { const QByteArray salsaKey = QByteArray::fromHex("0E50F49F412317096038665622DD091332A209BA0A05A00E1377CEDB0A3CB1D3"); QByteArray ivTable(0xFB0, 0); @@ -504,3 +426,71 @@ QByteArray Encryption::decryptFastFile_BO3(const QByteArray &fastFileData) { return finalFastFile; } + +QByteArray Encryption::DecryptFile(const QByteArray &fastFileData, const QString &aFileName, const QByteArray &aKey) +{ + Q_UNUSED(aFileName); + + const QByteArray salsaKey = QByteArray::fromHex(aKey); + + QByteArray ivTable(0xFB0, 0); + fillIVTable(fastFileData, ivTable, 0xFB0 - 1); + + QVector ivCounter(4, 1); + QDataStream stream(fastFileData); + stream.setByteOrder(QDataStream::LittleEndian); + + QByteArray finalFastFile; + QByteArray sha1Hash(20, 0); + QByteArray ivPtr(8, 0); + int chunkIndex = 0; + + while (!stream.atEnd()) { + if (stream.device()->bytesAvailable() < 4) { + qWarning() << "No sufficient data for chunk size at offset:" << stream.device()->pos(); + break; + } + + quint32 dataLength; + stream >> dataLength; + + if (dataLength == 0 || dataLength > fastFileData.size() - stream.device()->pos()) { + qWarning() << "Invalid data length at offset:" << stream.device()->pos(); + break; + } + + fillIV(chunkIndex % 4, ivPtr, ivTable, ivCounter); + + ECRYPT_ctx x; + ECRYPT_keysetup(&x, reinterpret_cast(salsaKey.constData()), 256, 0); + ECRYPT_ivsetup(&x, reinterpret_cast(ivPtr.constData())); + + QByteArray encryptedBlock = fastFileData.mid(stream.device()->pos(), dataLength); + QByteArray decryptedBlock(dataLength, Qt::Uninitialized); + + ECRYPT_decrypt_bytes(&x, + reinterpret_cast(encryptedBlock.constData()), + reinterpret_cast(decryptedBlock.data()), + dataLength); + + // SHA1 hash update + sha1Hash = QCryptographicHash::hash(decryptedBlock, QCryptographicHash::Sha1); + + // Decompress (ZLIB raw DEFLATE) + QByteArray decompressedData = Compression::DecompressDeflate(decryptedBlock); + if (decompressedData.isEmpty()) { + qWarning() << "Failed decompression at chunk index:" << chunkIndex; + return QByteArray(); + } + + finalFastFile.append(decompressedData); + + // Update IV table using SHA1 + generateNewIV(chunkIndex % 4, sha1Hash, ivTable, ivCounter); + + stream.skipRawData(dataLength); + chunkIndex++; + } + + return finalFastFile; +} diff --git a/libs/encryption/encryption.h b/libs/encryption/encryption.h index f03d85e..5ce41d3 100644 --- a/libs/encryption/encryption.h +++ b/libs/encryption/encryption.h @@ -46,8 +46,9 @@ public: static void generateNewIV(int index, const QByteArray& hash, QByteArray& ivTable, QVector& ivCounter); - static QByteArray decryptFastFile_BO2(const QByteArray& fastFileData); + //static QByteArray decryptFastFile_BO2(const QByteArray& fastFileData); static QByteArray decryptFastFile_BO3(const QByteArray& fastFileData); + static QByteArray DecryptFile(const QByteArray& fastFileData, const QString& aFileName, const QByteArray& aKey); }; #endif // ENCRYPTION_H