diff --git a/src/openrct2/core/Crypt.OpenRCT2.cpp b/src/openrct2/core/Crypt.OpenRCT2.cpp index 814a402488..f3ad2d17d5 100644 --- a/src/openrct2/core/Crypt.OpenRCT2.cpp +++ b/src/openrct2/core/Crypt.OpenRCT2.cpp @@ -7,190 +7,95 @@ * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ -#ifdef DISABLE_NETWORK +#include "Crypt.h" -# include "Crypt.h" - -# include +#include +#include +#include +#include +#include using namespace Crypt; -// https://github.com/CTrabant/teeny-sha1 -static int sha1digest(uint8_t* digest, const uint8_t* data, size_t databytes) -{ -# define SHA1ROTATELEFT(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - - uint32_t W[80]; - uint32_t H[] = { 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 }; - uint32_t a; - uint32_t b; - uint32_t c; - uint32_t d; - uint32_t e; - uint32_t f = 0; - uint32_t k = 0; - - uint32_t idx; - uint32_t lidx; - uint32_t widx; - uint32_t didx = 0; - - int32_t wcount; - uint32_t temp; - uint64_t databits = (static_cast(databytes)) * 8; - auto loopcount = (databytes + 8) / 64 + 1; - auto tailbytes = 64 * loopcount - databytes; - uint8_t datatail[128] = { 0 }; - - if (!digest) - return -1; - - if (!data) - return -1; - - /* Pre-processing of data tail (includes padding to fill out 512-bit chunk): - Add bit '1' to end of message (big-endian) - Add 64-bit message length in bits at very end (big-endian) */ - datatail[0] = 0x80; - datatail[tailbytes - 8] = static_cast(databits >> 56 & 0xFF); - datatail[tailbytes - 7] = static_cast(databits >> 48 & 0xFF); - datatail[tailbytes - 6] = static_cast(databits >> 40 & 0xFF); - datatail[tailbytes - 5] = static_cast(databits >> 32 & 0xFF); - datatail[tailbytes - 4] = static_cast(databits >> 24 & 0xFF); - datatail[tailbytes - 3] = static_cast(databits >> 16 & 0xFF); - datatail[tailbytes - 2] = static_cast(databits >> 8 & 0xFF); - datatail[tailbytes - 1] = static_cast(databits >> 0 & 0xFF); - - /* Process each 512-bit chunk */ - for (lidx = 0; lidx < loopcount; lidx++) - { - /* Compute all elements in W */ - std::memset(W, 0, 80 * sizeof(uint32_t)); - - /* Break 512-bit chunk into sixteen 32-bit, big endian words */ - for (widx = 0; widx <= 15; widx++) - { - wcount = 24; - - /* Copy byte-per byte from specified buffer */ - while (didx < databytes && wcount >= 0) - { - W[widx] += (static_cast(data[didx]) << wcount); - didx++; - wcount -= 8; - } - /* Fill out W with padding as needed */ - while (wcount >= 0) - { - W[widx] += (static_cast(datatail[didx - databytes]) << wcount); - didx++; - wcount -= 8; - } - } - - /* Extend the sixteen 32-bit words into eighty 32-bit words, with potential optimization from: - "Improving the Performance of the Secure Hash Algorithm (SHA-1)" by Max Locktyukhin */ - for (widx = 16; widx <= 31; widx++) - { - W[widx] = SHA1ROTATELEFT((W[widx - 3] ^ W[widx - 8] ^ W[widx - 14] ^ W[widx - 16]), 1); - } - for (widx = 32; widx <= 79; widx++) - { - W[widx] = SHA1ROTATELEFT((W[widx - 6] ^ W[widx - 16] ^ W[widx - 28] ^ W[widx - 32]), 2); - } - - /* Main loop */ - a = H[0]; - b = H[1]; - c = H[2]; - d = H[3]; - e = H[4]; - - for (idx = 0; idx <= 79; idx++) - { - if (idx <= 19) - { - f = (b & c) | ((~b) & d); - k = 0x5A827999; - } - else if (idx >= 20 && idx <= 39) - { - f = b ^ c ^ d; - k = 0x6ED9EBA1; - } - else if (idx >= 40 && idx <= 59) - { - f = (b & c) | (b & d) | (c & d); - k = 0x8F1BBCDC; - } - else if (idx >= 60 && idx <= 79) - { - f = b ^ c ^ d; - k = 0xCA62C1D6; - } - temp = SHA1ROTATELEFT(a, 5) + f + e + k + W[idx]; - e = d; - d = c; - c = SHA1ROTATELEFT(b, 30); - b = a; - a = temp; - } - - H[0] += a; - H[1] += b; - H[2] += c; - H[3] += d; - H[4] += e; - } - - /* Store binary digest in supplied buffer */ - if (digest) - { - for (idx = 0; idx < 5; idx++) - { - digest[idx * 4 + 0] = static_cast(H[idx] >> 24); - digest[idx * 4 + 1] = static_cast(H[idx] >> 16); - digest[idx * 4 + 2] = static_cast(H[idx] >> 8); - digest[idx * 4 + 3] = static_cast(H[idx]); - } - } - return 0; -} - -class OpenRCT2Sha1Algorithm : public Sha1Algorithm +class OpenRCT2FNV1aAlgorithm : public FNV1aAlgorithm { private: - std::vector _data; + static constexpr uint64_t Offset = 0xCBF29CE484222325ULL; + static constexpr uint64_t Prime = 0x00000100000001B3ULL; + + uint64_t _data = Offset; + uint8_t _rem[8]{}; + size_t _remLen{}; + + void ProcessRemainder() + { + if (_remLen > 0) + { + uint64_t temp{}; + std::memcpy(&temp, _rem, _remLen); + _data ^= temp; + _data *= Prime; + _remLen = 0; + } + } public: HashAlgorithm* Clear() override { - _data = {}; + _data = Offset; return this; } HashAlgorithm* Update(const void* data, size_t dataLen) override { - auto src = reinterpret_cast(data); - _data.insert(_data.end(), src, src + dataLen); + if (dataLen == 0) + return this; + + auto src = reinterpret_cast(data); + if (_remLen > 0) + { + // We have remainder, so fill rest of it with bytes from src + auto fillLen = sizeof(uint64_t) - _remLen; + assert(_remLen + fillLen <= sizeof(uint64_t)); + std::memcpy(_rem + _remLen, src, fillLen); + src = reinterpret_cast(reinterpret_cast(src) + fillLen); + _remLen += fillLen; + dataLen -= fillLen; + ProcessRemainder(); + } + + // Process every block of 8 bytes + while (dataLen >= sizeof(uint64_t)) + { + auto temp = *src++; + _data ^= temp; + _data *= Prime; + dataLen -= sizeof(uint64_t); + } + + // Store the remaining data (< 8 bytes) + if (dataLen > 0) + { + _remLen = dataLen; + std::memcpy(&_rem, src, dataLen); + } return this; } Result Finish() override { - std::array digest{}; - sha1digest(digest.data(), _data.data(), _data.size()); - _data = {}; - return digest; + ProcessRemainder(); + + Result res; + std::memcpy(res.data(), &_data, sizeof(_data)); + return res; } }; namespace Crypt { - std::unique_ptr CreateSHA1() + std::unique_ptr CreateFNV1a() { - return std::make_unique(); + return std::make_unique(); } } // namespace Crypt - -#endif // DISABLE_NETWORK diff --git a/src/openrct2/core/Crypt.h b/src/openrct2/core/Crypt.h index 468ca65780..e5f4721c11 100644 --- a/src/openrct2/core/Crypt.h +++ b/src/openrct2/core/Crypt.h @@ -48,13 +48,20 @@ namespace Crypt using Sha1Algorithm = HashAlgorithm<20>; using Sha256Algorithm = HashAlgorithm<32>; + using FNV1aAlgorithm = HashAlgorithm<8>; // Factories + std::unique_ptr CreateFNV1a(); std::unique_ptr CreateSHA1(); std::unique_ptr CreateSHA256(); std::unique_ptr CreateRSA(); std::unique_ptr CreateRSAKey(); + inline FNV1aAlgorithm::Result FNV1a(const void* data, size_t dataLen) + { + return CreateFNV1a()->Update(data, dataLen)->Finish(); + } + inline Sha1Algorithm::Result SHA1(const void* data, size_t dataLen) { return CreateSHA1()->Update(data, dataLen)->Finish(); diff --git a/src/openrct2/core/OrcaStream.hpp b/src/openrct2/core/OrcaStream.hpp index 923cd96021..4773d4721b 100644 --- a/src/openrct2/core/OrcaStream.hpp +++ b/src/openrct2/core/OrcaStream.hpp @@ -48,8 +48,8 @@ namespace OpenRCT2 uint64_t UncompressedSize{}; uint32_t Compression{}; uint64_t CompressedSize{}; - std::array Sha1{}; - uint8_t padding[8]; + std::array FNV1a{}; + uint8_t padding[20]; }; static_assert(sizeof(Header) == 64, "Header should be 64 bytes"); @@ -132,7 +132,7 @@ namespace OpenRCT2 _header.NumChunks = static_cast(_chunks.size()); _header.UncompressedSize = uncompressedSize; _header.CompressedSize = uncompressedSize; - _header.Sha1 = Crypt::SHA1(uncompressedData, uncompressedSize); + _header.FNV1a = Crypt::FNV1a(uncompressedData, uncompressedSize); // Compress data std::optional> compressedBytes;