mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-06-11 07:18:11 -05:00
Move XChunk processors to ZoneCommon
This commit is contained in:
53
src/ZoneCommon/Zone/XChunk/AbstractSalsa20Processor.cpp
Normal file
53
src/ZoneCommon/Zone/XChunk/AbstractSalsa20Processor.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
#include "AbstractSalsa20Processor.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
AbstractSalsa20Processor::AbstractSalsa20Processor(const int streamCount, std::string& zoneName, const uint8_t* salsa20Key, size_t keySize)
|
||||
: m_stream_count(streamCount),
|
||||
m_stream_contexts(std::make_unique<StreamContext[]>(streamCount)),
|
||||
m_block_hashes(std::make_unique<uint8_t[]>(BLOCK_HASHES_COUNT * streamCount * SHA1_HASH_SIZE)),
|
||||
m_stream_block_indices(std::make_unique<unsigned int[]>(streamCount))
|
||||
{
|
||||
InitStreams(zoneName, salsa20Key, keySize);
|
||||
}
|
||||
|
||||
uint8_t* AbstractSalsa20Processor::GetHashBlock(const int streamNumber) const
|
||||
{
|
||||
const auto blockIndexOffset = m_stream_block_indices[streamNumber] * m_stream_count * SHA1_HASH_SIZE;
|
||||
const auto streamOffset = static_cast<size_t>(streamNumber) * SHA1_HASH_SIZE;
|
||||
|
||||
return &m_block_hashes[blockIndexOffset + streamOffset];
|
||||
}
|
||||
|
||||
void AbstractSalsa20Processor::InitStreams(std::string& zoneName, const uint8_t* salsa20Key, size_t keySize) const
|
||||
{
|
||||
const int zoneNameLength = zoneName.length();
|
||||
const size_t blockHashBufferSize = BLOCK_HASHES_COUNT * m_stream_count * SHA1_HASH_SIZE;
|
||||
|
||||
assert(blockHashBufferSize % 4 == 0);
|
||||
|
||||
size_t zoneNameOffset = 0;
|
||||
for (size_t i = 0; i < blockHashBufferSize; i += 4)
|
||||
{
|
||||
*reinterpret_cast<uint32_t*>(&m_block_hashes[i]) = 0x1010101 * zoneName[zoneNameOffset++];
|
||||
|
||||
zoneNameOffset %= zoneNameLength;
|
||||
}
|
||||
|
||||
for (auto stream = 0; stream < m_stream_count; stream++)
|
||||
{
|
||||
m_stream_block_indices[stream] = 0;
|
||||
|
||||
m_stream_contexts[stream].m_salsa20 = Crypto::CreateSalsa20(salsa20Key, keySize);
|
||||
m_stream_contexts[stream].m_sha1 = Crypto::CreateSHA1();
|
||||
}
|
||||
}
|
||||
|
||||
void AbstractSalsa20Processor::GetCapturedData(const uint8_t** pCapturedData, size_t* pSize)
|
||||
{
|
||||
assert(pCapturedData != nullptr);
|
||||
assert(pSize != nullptr);
|
||||
|
||||
*pCapturedData = m_block_hashes.get();
|
||||
*pSize = BLOCK_HASHES_COUNT * m_stream_count * SHA1_HASH_SIZE;
|
||||
}
|
45
src/ZoneCommon/Zone/XChunk/AbstractSalsa20Processor.h
Normal file
45
src/ZoneCommon/Zone/XChunk/AbstractSalsa20Processor.h
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Crypto.h"
|
||||
#include "Utils/ICapturedDataProvider.h"
|
||||
|
||||
class AbstractSalsa20Processor : public ICapturedDataProvider
|
||||
{
|
||||
protected:
|
||||
static constexpr int BLOCK_HASHES_COUNT = 200;
|
||||
static constexpr int SHA1_HASH_SIZE = 20;
|
||||
static constexpr int SALSA20_IV_SIZE = 8;
|
||||
|
||||
class StreamContext
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<IStreamCipher> m_salsa20;
|
||||
std::unique_ptr<IHashFunction> m_sha1;
|
||||
};
|
||||
|
||||
int m_stream_count;
|
||||
std::unique_ptr<StreamContext[]> m_stream_contexts;
|
||||
|
||||
// m_block_hashes[BLOCK_HASHES_COUNT][numStreams][HASH_SIZE]
|
||||
std::unique_ptr<uint8_t[]> m_block_hashes;
|
||||
std::unique_ptr<unsigned int[]> m_stream_block_indices;
|
||||
|
||||
AbstractSalsa20Processor(int streamCount, std::string& zoneName, const uint8_t* salsa20Key, size_t keySize);
|
||||
|
||||
_NODISCARD uint8_t* GetHashBlock(int streamNumber) const;
|
||||
|
||||
void InitStreams(std::string& zoneName, const uint8_t* salsa20Key, size_t keySize) const;
|
||||
|
||||
public:
|
||||
virtual ~AbstractSalsa20Processor() = default;
|
||||
AbstractSalsa20Processor(const AbstractSalsa20Processor& other) = delete;
|
||||
AbstractSalsa20Processor(AbstractSalsa20Processor&& other) noexcept = default;
|
||||
AbstractSalsa20Processor& operator=(const AbstractSalsa20Processor& other) = delete;
|
||||
AbstractSalsa20Processor& operator=(AbstractSalsa20Processor&& other) noexcept = default;
|
||||
|
||||
void GetCapturedData(const uint8_t** pCapturedData, size_t* pSize) override;
|
||||
};
|
11
src/ZoneCommon/Zone/XChunk/IXChunkProcessor.h
Normal file
11
src/ZoneCommon/Zone/XChunk/IXChunkProcessor.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
class IXChunkProcessor
|
||||
{
|
||||
public:
|
||||
virtual ~IXChunkProcessor() = default;
|
||||
virtual size_t Process(int streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) = 0;
|
||||
};
|
16
src/ZoneCommon/Zone/XChunk/XChunkException.cpp
Normal file
16
src/ZoneCommon/Zone/XChunk/XChunkException.cpp
Normal file
@ -0,0 +1,16 @@
|
||||
#include "XChunkException.h"
|
||||
|
||||
XChunkException::XChunkException(std::string message)
|
||||
: m_message(std::move(message))
|
||||
{
|
||||
}
|
||||
|
||||
char const* XChunkException::what() const
|
||||
{
|
||||
return m_message.c_str();
|
||||
}
|
||||
|
||||
const std::string& XChunkException::Message() const
|
||||
{
|
||||
return m_message;
|
||||
}
|
16
src/ZoneCommon/Zone/XChunk/XChunkException.h
Normal file
16
src/ZoneCommon/Zone/XChunk/XChunkException.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include <exception>
|
||||
#include <string>
|
||||
|
||||
#include "Utils/ClassUtils.h"
|
||||
|
||||
class XChunkException final : public std::exception
|
||||
{
|
||||
std::string m_message;
|
||||
|
||||
public:
|
||||
explicit XChunkException(std::string message);
|
||||
|
||||
_NODISCARD char const* what() const override;
|
||||
_NODISCARD const std::string& Message() const;
|
||||
};
|
33
src/ZoneCommon/Zone/XChunk/XChunkProcessorDeflate.cpp
Normal file
33
src/ZoneCommon/Zone/XChunk/XChunkProcessorDeflate.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include "XChunkProcessorDeflate.h"
|
||||
|
||||
#include <zlib.h>
|
||||
#include <zutil.h>
|
||||
|
||||
#include "XChunkException.h"
|
||||
|
||||
size_t XChunkProcessorDeflate::Process(int streamNumber, const uint8_t* input, const size_t inputLength, uint8_t* output, const size_t outputBufferSize)
|
||||
{
|
||||
z_stream stream{};
|
||||
stream.zalloc = Z_NULL;
|
||||
stream.zfree = Z_NULL;
|
||||
stream.opaque = Z_NULL;
|
||||
|
||||
auto ret = deflateInit2(&stream, Z_BEST_COMPRESSION, Z_DEFLATED, -DEF_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
|
||||
if (ret != Z_OK)
|
||||
throw XChunkException("Initializing deflate failed.");
|
||||
|
||||
stream.avail_in = inputLength;
|
||||
stream.next_in = input;
|
||||
stream.avail_out = outputBufferSize;
|
||||
stream.next_out = output;
|
||||
|
||||
ret = deflate(&stream, Z_FINISH);
|
||||
if (ret != Z_STREAM_END)
|
||||
throw XChunkException("Zone has invalid or unsupported compression. Deflate failed");
|
||||
|
||||
const size_t outputSize = stream.total_out;
|
||||
|
||||
deflateEnd(&stream);
|
||||
|
||||
return outputSize;
|
||||
}
|
8
src/ZoneCommon/Zone/XChunk/XChunkProcessorDeflate.h
Normal file
8
src/ZoneCommon/Zone/XChunk/XChunkProcessorDeflate.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "IXChunkProcessor.h"
|
||||
|
||||
class XChunkProcessorDeflate final : public IXChunkProcessor
|
||||
{
|
||||
public:
|
||||
size_t Process(int streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) override;
|
||||
};
|
33
src/ZoneCommon/Zone/XChunk/XChunkProcessorInflate.cpp
Normal file
33
src/ZoneCommon/Zone/XChunk/XChunkProcessorInflate.cpp
Normal file
@ -0,0 +1,33 @@
|
||||
#include "XChunkProcessorInflate.h"
|
||||
|
||||
#include <zlib.h>
|
||||
#include <zutil.h>
|
||||
|
||||
#include "XChunkException.h"
|
||||
|
||||
size_t XChunkProcessorInflate::Process(int streamNumber, const uint8_t* input, const size_t inputLength, uint8_t* output, const size_t outputBufferSize)
|
||||
{
|
||||
z_stream stream{};
|
||||
stream.zalloc = Z_NULL;
|
||||
stream.zfree = Z_NULL;
|
||||
stream.opaque = Z_NULL;
|
||||
|
||||
auto ret = inflateInit2(&stream, -DEF_WBITS);
|
||||
if (ret != Z_OK)
|
||||
throw XChunkException("Initializing inflate failed.");
|
||||
|
||||
stream.avail_in = inputLength;
|
||||
stream.next_in = input;
|
||||
stream.avail_out = outputBufferSize;
|
||||
stream.next_out = output;
|
||||
|
||||
ret = inflate(&stream, Z_FULL_FLUSH);
|
||||
if (ret != Z_STREAM_END)
|
||||
throw XChunkException("Zone has invalid or unsupported compression. Inflate failed");
|
||||
|
||||
const size_t outputSize = stream.total_out;
|
||||
|
||||
inflateEnd(&stream);
|
||||
|
||||
return outputSize;
|
||||
}
|
8
src/ZoneCommon/Zone/XChunk/XChunkProcessorInflate.h
Normal file
8
src/ZoneCommon/Zone/XChunk/XChunkProcessorInflate.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
#include "IXChunkProcessor.h"
|
||||
|
||||
class XChunkProcessorInflate final : public IXChunkProcessor
|
||||
{
|
||||
public:
|
||||
size_t Process(int streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) override;
|
||||
};
|
@ -0,0 +1,44 @@
|
||||
#include "XChunkProcessorSalsa20Decryption.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "Crypto.h"
|
||||
#include "AbstractSalsa20Processor.h"
|
||||
|
||||
XChunkProcessorSalsa20Decryption::XChunkProcessorSalsa20Decryption(const int streamCount, std::string& zoneName, const uint8_t* salsa20Key, size_t keySize)
|
||||
: AbstractSalsa20Processor(streamCount, zoneName, salsa20Key, keySize)
|
||||
{
|
||||
}
|
||||
|
||||
size_t XChunkProcessorSalsa20Decryption::Process(const int streamNumber, const uint8_t* input, const size_t inputLength, uint8_t* output, const size_t outputBufferSize)
|
||||
{
|
||||
assert(streamNumber >= 0 && streamNumber < m_stream_count);
|
||||
assert(input != nullptr);
|
||||
assert(output != nullptr);
|
||||
assert(inputLength <= outputBufferSize);
|
||||
|
||||
auto& streamContext = m_stream_contexts[streamNumber];
|
||||
|
||||
// Initialize Salsa20 with an IV of the first 8 bytes of the current hash block
|
||||
streamContext.m_salsa20->SetIV(GetHashBlock(streamNumber), SALSA20_IV_SIZE);
|
||||
streamContext.m_salsa20->Process(input, output, inputLength);
|
||||
|
||||
// Hash decrypted XChunk
|
||||
uint8_t blockSha1Hash[SHA1_HASH_SIZE];
|
||||
streamContext.m_sha1->Init();
|
||||
streamContext.m_sha1->Process(output, inputLength);
|
||||
streamContext.m_sha1->Finish(&blockSha1Hash);
|
||||
|
||||
// Advance index to next hash block
|
||||
m_stream_block_indices[streamNumber] = (m_stream_block_indices[streamNumber] + 1) % BLOCK_HASHES_COUNT;
|
||||
|
||||
auto* nextHashBlock = GetHashBlock(streamNumber);
|
||||
|
||||
// XOR the upcoming hash block with the hash of the XChunk utilizing the previous hash block
|
||||
for (unsigned int hashOffset = 0; hashOffset < sizeof(blockSha1Hash); hashOffset++)
|
||||
{
|
||||
nextHashBlock[hashOffset] ^= blockSha1Hash[hashOffset];
|
||||
}
|
||||
|
||||
return inputLength;
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
#include "IXChunkProcessor.h"
|
||||
#include "AbstractSalsa20Processor.h"
|
||||
|
||||
class XChunkProcessorSalsa20Decryption final : public IXChunkProcessor, public AbstractSalsa20Processor
|
||||
{
|
||||
public:
|
||||
XChunkProcessorSalsa20Decryption(int streamCount, std::string& zoneName, const uint8_t* salsa20Key, size_t keySize);
|
||||
|
||||
size_t Process(int streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) override;
|
||||
};
|
@ -0,0 +1,41 @@
|
||||
#include "XChunkProcessorSalsa20Encryption.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
XChunkProcessorSalsa20Encryption::XChunkProcessorSalsa20Encryption(const int streamCount, std::string& zoneName, const uint8_t* salsa20Key, const size_t keySize)
|
||||
: AbstractSalsa20Processor(streamCount, zoneName, salsa20Key, keySize)
|
||||
{
|
||||
}
|
||||
|
||||
size_t XChunkProcessorSalsa20Encryption::Process(const int streamNumber, const uint8_t* input, const size_t inputLength, uint8_t* output, const size_t outputBufferSize)
|
||||
{
|
||||
assert(streamNumber >= 0 && streamNumber < m_stream_count);
|
||||
assert(input != nullptr);
|
||||
assert(output != nullptr);
|
||||
assert(inputLength <= outputBufferSize);
|
||||
|
||||
auto& streamContext = m_stream_contexts[streamNumber];
|
||||
|
||||
// Hash not yet encrypted XChunk
|
||||
uint8_t blockSha1Hash[SHA1_HASH_SIZE];
|
||||
streamContext.m_sha1->Init();
|
||||
streamContext.m_sha1->Process(input, inputLength);
|
||||
streamContext.m_sha1->Finish(&blockSha1Hash);
|
||||
|
||||
// Initialize Salsa20 with an IV of the first 8 bytes of the current hash block
|
||||
streamContext.m_salsa20->SetIV(GetHashBlock(streamNumber), SALSA20_IV_SIZE);
|
||||
streamContext.m_salsa20->Process(input, output, inputLength);
|
||||
|
||||
// Advance index to next hash block
|
||||
m_stream_block_indices[streamNumber] = (m_stream_block_indices[streamNumber] + 1) % BLOCK_HASHES_COUNT;
|
||||
|
||||
auto* nextHashBlock = GetHashBlock(streamNumber);
|
||||
|
||||
// XOR the upcoming hash block with the hash of the XChunk utilizing the previous hash block
|
||||
for (unsigned int hashOffset = 0; hashOffset < sizeof(blockSha1Hash); hashOffset++)
|
||||
{
|
||||
nextHashBlock[hashOffset] ^= blockSha1Hash[hashOffset];
|
||||
}
|
||||
|
||||
return inputLength;
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "AbstractSalsa20Processor.h"
|
||||
#include "IXChunkProcessor.h"
|
||||
|
||||
class XChunkProcessorSalsa20Encryption final : public IXChunkProcessor, public AbstractSalsa20Processor
|
||||
{
|
||||
public:
|
||||
XChunkProcessorSalsa20Encryption(int streamCount, std::string& zoneName, const uint8_t* salsa20Key, size_t keySize);
|
||||
|
||||
size_t Process(int streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) override;
|
||||
};
|
Reference in New Issue
Block a user