Move XChunk processors to ZoneCommon

This commit is contained in:
Jan
2021-03-16 20:42:48 +01:00
parent ca1329323b
commit f22012d282
33 changed files with 383 additions and 317 deletions

View 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;
}

View 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;
};

View 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;
};

View 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;
}

View 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;
};

View 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;
}

View 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;
};

View 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;
}

View 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;
};

View File

@ -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;
}

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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;
};