diff --git a/CMakeLists.txt b/CMakeLists.txt index fd12f0a53..44d156efa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -294,6 +294,7 @@ if(NOT CMAKE_CROSSCOMPILING) find_package(PkgConfig) endif() find_package(ZLIB) +find_package(Crypto) find_package(Freetype) find_package(Git) find_package(GTest) @@ -342,6 +343,7 @@ show_dependency_status("Freetype" FREETYPE) if(TARGET_OS AND TARGET_OS STREQUAL "mac") show_dependency_status("Hdiutil" HDIUTIL) endif() +show_dependency_status("OpenSSL Crypto" CRYPTO) show_dependency_status("Pnglite" PNGLITE) show_dependency_status("PythonInterp" PYTHONINTERP) show_dependency_status("SDL2" SDL2) @@ -1124,6 +1126,11 @@ generate_source("src/generated/server_data.h" "server_content_header") set_src(BASE GLOB_RECURSE src/base color.h detect.h + hash.c + hash.h + hash_ctxt.h + hash_libtomcrypt.c + hash_openssl.c math.h system.c system.h @@ -1231,7 +1238,7 @@ set(GAME_GENERATED_SHARED set(DEPS ${DEP_MD5} ${ZLIB_DEP}) # Libraries -set(LIBS ${CMAKE_THREAD_LIBS_INIT} ${ZLIB_LIBRARIES} ${PLATFORM_LIBS}) +set(LIBS ${CMAKE_THREAD_LIBS_INIT} ${ZLIB_LIBRARIES} ${CRYPTO_LIBRARIES} ${PLATFORM_LIBS}) # Targets add_library(engine-shared EXCLUDE_FROM_ALL OBJECT ${ENGINE_INTERFACE} ${ENGINE_SHARED} ${ENGINE_GENERATED_SHARED} ${BASE}) @@ -1561,6 +1568,7 @@ if(GTEST_FOUND OR DOWNLOAD_GTEST) set_src(TESTS GLOB src/test fs.cpp git_revision.cpp + hash.cpp storage.cpp str.cpp test.cpp @@ -1951,6 +1959,9 @@ foreach(target ${TARGETS_OWN}) target_compile_definitions(${target} PRIVATE $<$:CONF_DEBUG>) target_include_directories(${target} PRIVATE ${CURL_INCLUDE_DIRS}) target_include_directories(${target} PRIVATE ${ZLIB_INCLUDE_DIRS}) + if(CRYPTO_FOUND) + target_compile_definitions(${target} PRIVATE CONF_OPENSSL) + endif() endforeach() foreach(target ${TARGETS_DEP}) diff --git a/cmake/FindCrypto.cmake b/cmake/FindCrypto.cmake new file mode 100644 index 000000000..f765e7157 --- /dev/null +++ b/cmake/FindCrypto.cmake @@ -0,0 +1,19 @@ +if(NOT PREFER_BUNDLED_LIBS) + find_package(OpenSSL) + if(OPENSSL_FOUND) + set(CRYPTO_FOUND ON) + set(CRYPTO_BUNDLED OFF) + set(CRYPTO_LIBRARY ${OPENSSL_CRYPTO_LIBRARY}) + set(CRYPTO_INCLUDEDIR ${OPENSSL_INCLUDE_DIR}) + endif() +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Crypto DEFAULT_MSG CRYPTO_LIBRARY CRYPTO_INCLUDEDIR) + +mark_as_advanced(CRYPTO_LIBRARY CRYPTO_INCLUDEDIR) + +if(CRYPTO_FOUND) + set(CRYPTO_LIBRARIES ${CRYPTO_LIBRARY}) + set(CRYPTO_INCLUDE_DIRS ${CRYPTO_INCLUDEDIR}) +endif() diff --git a/src/base/hash.c b/src/base/hash.c new file mode 100644 index 000000000..eb2f20587 --- /dev/null +++ b/src/base/hash.c @@ -0,0 +1,41 @@ +#include "hash.h" +#include "hash_ctxt.h" + +#include "system.h" + +SHA256_DIGEST sha256(const void *message, size_t message_len) +{ + SHA256_CTX ctxt; + sha256_init(&ctxt); + sha256_update(&ctxt, message, message_len); + return sha256_finish(&ctxt); +} + +void sha256_str(SHA256_DIGEST digest, char *str, size_t max_len) +{ + unsigned i; + if(max_len > SHA256_MAXSTRSIZE) + { + max_len = SHA256_MAXSTRSIZE; + } + str[max_len - 1] = 0; + max_len -= 1; + for(i = 0; i < max_len; i++) + { + static const char HEX[] = "0123456789abcdef"; + int index = i / 2; + if(i % 2 == 0) + { + str[i] = HEX[digest.data[index] >> 4]; + } + else + { + str[i] = HEX[digest.data[index] & 0xf]; + } + } +} + +int sha256_comp(SHA256_DIGEST digest1, SHA256_DIGEST digest2) +{ + return mem_comp(digest1.data, digest2.data, sizeof(digest1.data)); +} diff --git a/src/base/hash.h b/src/base/hash.h new file mode 100644 index 000000000..cdb947bd1 --- /dev/null +++ b/src/base/hash.h @@ -0,0 +1,43 @@ +#ifndef BASE_HASH_H +#define BASE_HASH_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + SHA256_DIGEST_LENGTH=256/8, + SHA256_MAXSTRSIZE=2*SHA256_DIGEST_LENGTH+1, +}; + +typedef struct +{ + unsigned char data[SHA256_DIGEST_LENGTH]; +} SHA256_DIGEST; + +SHA256_DIGEST sha256(const void *message, size_t message_len); +void sha256_str(SHA256_DIGEST digest, char *str, size_t max_len); +int sha256_from_str(SHA256_DIGEST *out, const char *str); +int sha256_comp(SHA256_DIGEST digest1, SHA256_DIGEST digest2); + +static const SHA256_DIGEST SHA256_ZEROED = {{0}}; + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +inline bool operator==(const SHA256_DIGEST &that, const SHA256_DIGEST &other) +{ + return sha256_comp(that, other) == 0; +} +inline bool operator!=(const SHA256_DIGEST &that, const SHA256_DIGEST &other) +{ + return !(that == other); +} +#endif + +#endif // BASE_HASH_H diff --git a/src/base/hash_ctxt.h b/src/base/hash_ctxt.h new file mode 100644 index 000000000..636ed6741 --- /dev/null +++ b/src/base/hash_ctxt.h @@ -0,0 +1,35 @@ +#ifndef BASE_HASH_CTXT_H +#define BASE_HASH_CTXT_H + +#include "hash.h" +#include + +#if defined(CONF_OPENSSL) +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(CONF_OPENSSL) +// SHA256_CTX is defined in +#else +typedef struct +{ + uint64_t length; + uint32_t state[8]; + uint32_t curlen; + unsigned char buf[64]; +} SHA256_CTX; +#endif + +void sha256_init(SHA256_CTX *ctxt); +void sha256_update(SHA256_CTX *ctxt, const void *data, size_t data_len); +SHA256_DIGEST sha256_finish(SHA256_CTX *ctxt); + +#ifdef __cplusplus +} +#endif + +#endif // BASE_HASH_CTXT_H diff --git a/src/base/hash_libtomcrypt.c b/src/base/hash_libtomcrypt.c new file mode 100755 index 000000000..b249f27e0 --- /dev/null +++ b/src/base/hash_libtomcrypt.c @@ -0,0 +1,197 @@ +// SHA-256. Adapted from https://github.com/kalven/sha-2, which was adapted +// from LibTomCrypt. This code is Public Domain. + +#if !defined(CONF_OPENSSL) + +#include "hash_ctxt.h" + +#include +#include +#include + +typedef uint32_t u32; +typedef uint64_t u64; +typedef SHA256_CTX sha256_state; + +static const u32 K[64] = +{ + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, + 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, + 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, + 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, + 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, + 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, + 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, + 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, + 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + +static u32 min(u32 x, u32 y) +{ + return x < y ? x : y; +} + +static u32 load32(const unsigned char* y) +{ + return ((u32)y[0] << 24) | ((u32)y[1] << 16) | ((u32)y[2] << 8) | ((u32)y[3] << 0); +} + +static void store64(u64 x, unsigned char* y) +{ + for(int i = 0; i != 8; ++i) + y[i] = (x >> ((7-i) * 8)) & 255; +} + +static void store32(u32 x, unsigned char* y) +{ + for(int i = 0; i != 4; ++i) + y[i] = (x >> ((3-i) * 8)) & 255; +} + +static u32 Ch(u32 x, u32 y, u32 z) { return z ^ (x & (y ^ z)); } +static u32 Maj(u32 x, u32 y, u32 z) { return ((x | y) & z) | (x & y); } +static u32 Rot(u32 x, u32 n) { return (x >> (n & 31)) | (x << (32 - (n & 31))); } +static u32 Sh(u32 x, u32 n) { return x >> n; } +static u32 Sigma0(u32 x) { return Rot(x, 2) ^ Rot(x, 13) ^ Rot(x, 22); } +static u32 Sigma1(u32 x) { return Rot(x, 6) ^ Rot(x, 11) ^ Rot(x, 25); } +static u32 Gamma0(u32 x) { return Rot(x, 7) ^ Rot(x, 18) ^ Sh(x, 3); } +static u32 Gamma1(u32 x) { return Rot(x, 17) ^ Rot(x, 19) ^ Sh(x, 10); } + +static void sha_compress(sha256_state* md, const unsigned char* buf) +{ + u32 S[8], W[64], t0, t1, t; + + // Copy state into S + for(int i = 0; i < 8; i++) + S[i] = md->state[i]; + + // Copy the state into 512-bits into W[0..15] + for(int i = 0; i < 16; i++) + W[i] = load32(buf + (4*i)); + + // Fill W[16..63] + for(int i = 16; i < 64; i++) + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + W[i - 16]; + + // Compress + #define RND(a, b, c, d, e, f, g, h, i) \ + { \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; \ + } + + for(int i = 0; i < 64; ++i) + { + RND(S[0],S[1],S[2],S[3],S[4],S[5],S[6],S[7],i); + t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; + S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; + } + + // Feedback + for(int i = 0; i < 8; i++) + md->state[i] = md->state[i] + S[i]; +} + +// Public interface + +static void sha_init(sha256_state* md) +{ + md->curlen = 0; + md->length = 0; + md->state[0] = 0x6A09E667UL; + md->state[1] = 0xBB67AE85UL; + md->state[2] = 0x3C6EF372UL; + md->state[3] = 0xA54FF53AUL; + md->state[4] = 0x510E527FUL; + md->state[5] = 0x9B05688CUL; + md->state[6] = 0x1F83D9ABUL; + md->state[7] = 0x5BE0CD19UL; +} + +static void sha_process(sha256_state* md, const void* src, u32 inlen) +{ + const u32 block_size = 64; + const unsigned char* in = src; + + while(inlen > 0) + { + if(md->curlen == 0 && inlen >= block_size) + { + sha_compress(md, in); + md->length += block_size * 8; + in += block_size; + inlen -= block_size; + } + else + { + u32 n = min(inlen, (block_size - md->curlen)); + memcpy(md->buf + md->curlen, in, n); + md->curlen += n; + in += n; + inlen -= n; + + if(md->curlen == block_size) + { + sha_compress(md, md->buf); + md->length += 8*block_size; + md->curlen = 0; + } + } + } +} + +static void sha_done(sha256_state* md, void* out) +{ + // Increase the length of the message + md->length += md->curlen * 8; + + // Append the '1' bit + md->buf[md->curlen++] = (unsigned char)0x80; + + // If the length is currently above 56 bytes we append zeros then compress. + // Then we can fall back to padding zeros and length encoding like normal. + if(md->curlen > 56) + { + while(md->curlen < 64) + md->buf[md->curlen++] = 0; + sha_compress(md, md->buf); + md->curlen = 0; + } + + // Pad upto 56 bytes of zeroes + while(md->curlen < 56) + md->buf[md->curlen++] = 0; + + // Store length + store64(md->length, md->buf+56); + sha_compress(md, md->buf); + + // Copy output + for(int i = 0; i < 8; i++) + store32(md->state[i], (unsigned char *)out+(4*i)); +} + +void sha256_init(SHA256_CTX *ctxt) +{ + sha_init(ctxt); +} + +void sha256_update(SHA256_CTX *ctxt, const void *data, size_t data_len) +{ + sha_process(ctxt, data, data_len); +} + +SHA256_DIGEST sha256_finish(SHA256_CTX *ctxt) +{ + SHA256_DIGEST result; + sha_done(ctxt, result.data); + return result; +} + +#endif diff --git a/src/base/hash_openssl.c b/src/base/hash_openssl.c new file mode 100644 index 000000000..6d2240abb --- /dev/null +++ b/src/base/hash_openssl.c @@ -0,0 +1,20 @@ +#if defined(CONF_OPENSSL) +#include "hash_ctxt.h" + +void sha256_init(SHA256_CTX *ctxt) +{ + SHA256_Init(ctxt); +} + +void sha256_update(SHA256_CTX *ctxt, const void *data, size_t data_len) +{ + SHA256_Update(ctxt, data, data_len); +} + +SHA256_DIGEST sha256_finish(SHA256_CTX *ctxt) +{ + SHA256_DIGEST result; + SHA256_Final(result.data, ctxt); + return result; +} +#endif diff --git a/src/engine/client/client.cpp b/src/engine/client/client.cpp index f1fca781d..ef4d4f7f5 100644 --- a/src/engine/client/client.cpp +++ b/src/engine/client/client.cpp @@ -277,6 +277,7 @@ CClient::CClient() : m_DemoPlayer(&m_SnapshotDelta), m_DemoRecorder(&m_SnapshotD // m_aCurrentMap[0] = 0; + m_CurrentMapSha256 = SHA256_ZEROED; m_CurrentMapCrc = 0; // @@ -287,6 +288,8 @@ CClient::CClient() : m_DemoPlayer(&m_SnapshotDelta), m_DemoRecorder(&m_SnapshotD m_aMapdownloadName[0] = 0; m_MapdownloadFile = 0; m_MapdownloadChunk = 0; + m_MapdownloadSha256 = SHA256_ZEROED; + m_MapdownloadSha256Present = false; m_MapdownloadCrc = 0; m_MapdownloadAmount = -1; m_MapdownloadTotalsize = -1; @@ -545,6 +548,8 @@ void CClient::DisconnectWithReason(const char *pReason) if(m_MapdownloadFile) io_close(m_MapdownloadFile); m_MapdownloadFile = 0; + m_MapdownloadSha256 = SHA256_ZEROED; + m_MapdownloadSha256Present = false; m_MapdownloadCrc = 0; m_MapdownloadTotalsize = -1; m_MapdownloadAmount = 0; @@ -756,9 +761,9 @@ void CClient::Render() DebugRender(); } -const char *CClient::LoadMap(const char *pName, const char *pFilename, unsigned WantedCrc) +const char *CClient::LoadMap(const char *pName, const char *pFilename, const SHA256_DIGEST *pWantedSha256, unsigned WantedCrc) { - static char aErrorMsg[128]; + static char aErrorMsg[512]; SetState(IClient::STATE_LOADING); @@ -768,6 +773,18 @@ const char *CClient::LoadMap(const char *pName, const char *pFilename, unsigned return aErrorMsg; } + if(pWantedSha256 && m_pMap->Sha256() != *pWantedSha256) + { + char aSha256[SHA256_MAXSTRSIZE]; + char aWantedSha256[SHA256_MAXSTRSIZE]; + sha256_str(m_pMap->Sha256(), aSha256, sizeof(aSha256)); + sha256_str(*pWantedSha256, aWantedSha256, sizeof(aWantedSha256)); + str_format(aErrorMsg, sizeof(aErrorMsg), "map differs from the server. %s != %s", aSha256, aWantedSha256); + m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client", aErrorMsg); + m_pMap->Unload(); + return aErrorMsg; + } + // get the crc of the map if(m_pMap->Crc() != WantedCrc) { @@ -786,38 +803,70 @@ const char *CClient::LoadMap(const char *pName, const char *pFilename, unsigned m_RecivedSnapshots = 0; str_copy(m_aCurrentMap, pName, sizeof(m_aCurrentMap)); + m_CurrentMapSha256 = m_pMap->Sha256(); m_CurrentMapCrc = m_pMap->Crc(); return 0x0; } +static void FormatMapDownloadFilename(const char *pName, const SHA256_DIGEST *pSha256, int Crc, char *pBuffer, int BufferSize) +{ + if(pSha256) + { + char aSha256[SHA256_MAXSTRSIZE]; + sha256_str(*pSha256, aSha256, sizeof(aSha256)); + str_format(pBuffer, BufferSize, "downloadedmaps/%s_%s.map", pName, aSha256); + } + else + { + str_format(pBuffer, BufferSize, "downloadedmaps/%s_%08x.map", pName, Crc); + } +} -const char *CClient::LoadMapSearch(const char *pMapName, int WantedCrc) + +const char *CClient::LoadMapSearch(const char *pMapName, const SHA256_DIGEST *pWantedSha256, int WantedCrc) { const char *pError = 0; char aBuf[512]; - str_format(aBuf, sizeof(aBuf), "loading map, map=%s wanted crc=%08x", pMapName, WantedCrc); + char aWanted[SHA256_MAXSTRSIZE + 16]; + aWanted[0] = 0; + if(pWantedSha256) + { + char aWantedSha256[SHA256_MAXSTRSIZE]; + sha256_str(*pWantedSha256, aWantedSha256, sizeof(aWantedSha256)); + str_format(aWanted, sizeof(aWanted), "sha256=%s ", aWantedSha256); + } + str_format(aBuf, sizeof(aBuf), "loading map, map=%s wanted %scrc=%08x", pMapName, aWanted, WantedCrc); m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client", aBuf); SetState(IClient::STATE_LOADING); // try the normal maps folder str_format(aBuf, sizeof(aBuf), "maps/%s.map", pMapName); - pError = LoadMap(pMapName, aBuf, WantedCrc); + pError = LoadMap(pMapName, aBuf, pWantedSha256, WantedCrc); if(!pError) return pError; // try the downloaded maps - str_format(aBuf, sizeof(aBuf), "downloadedmaps/%s_%08x.map", pMapName, WantedCrc); - pError = LoadMap(pMapName, aBuf, WantedCrc); + FormatMapDownloadFilename(pMapName, pWantedSha256, WantedCrc, aBuf, sizeof(aBuf)); + pError = LoadMap(pMapName, aBuf, pWantedSha256, WantedCrc); if(!pError) return pError; + // backward compatibility with old names + if(pWantedSha256) + { + FormatMapDownloadFilename(pMapName, 0, WantedCrc, aBuf, sizeof(aBuf)); + pError = LoadMap(pMapName, aBuf, 0, WantedCrc); + if(!pError) + return pError; + } + // search for the map within subfolders char aFilename[128]; str_format(aFilename, sizeof(aFilename), "%s.map", pMapName); if(Storage()->FindFile(aFilename, "maps", IStorage::TYPE_ALL, aBuf, sizeof(aBuf))) - pError = LoadMap(pMapName, aBuf, WantedCrc); + pError = LoadMap(pMapName, aBuf, pWantedSha256, WantedCrc); return pError; } @@ -1023,13 +1072,13 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) int MapSize = Unpacker.GetInt(); int MapChunkNum = Unpacker.GetInt(); int MapChunkSize = Unpacker.GetInt(); - const char *pError = 0; - if(Unpacker.Error()) return; + const SHA256_DIGEST *pMapSha256 = (const SHA256_DIGEST *)Unpacker.GetRaw(sizeof(*pMapSha256)); + const char *pError = 0; // check for valid standard map - if(!m_MapChecker.IsMapValid(pMap, MapCrc, MapSize)) + if(!m_MapChecker.IsMapValid(pMap, pMapSha256, MapCrc, MapSize)) pError = "invalid standard map"; // protect the player from nasty map names @@ -1046,7 +1095,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) DisconnectWithReason(pError); else { - pError = LoadMapSearch(pMap, MapCrc); + pError = LoadMapSearch(pMap, pMapSha256, MapCrc); if(!pError) { @@ -1056,7 +1105,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) else { // start map download - str_format(m_aMapdownloadFilename, sizeof(m_aMapdownloadFilename), "downloadedmaps/%s_%08x.map", pMap, MapCrc); + FormatMapDownloadFilename(pMap, pMapSha256, MapCrc, m_aMapdownloadFilename, sizeof(m_aMapdownloadFilename)); char aBuf[256]; str_format(aBuf, sizeof(aBuf), "starting to download map to '%s'", m_aMapdownloadFilename); @@ -1069,6 +1118,8 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) m_MapdownloadChunk = 0; m_MapdownloadChunkNum = MapChunkNum; m_MapDownloadChunkSize = MapChunkSize; + m_MapdownloadSha256 = pMapSha256 ? *pMapSha256 : SHA256_ZEROED; + m_MapdownloadSha256Present = pMapSha256; m_MapdownloadCrc = MapCrc; m_MapdownloadTotalsize = MapSize; m_MapdownloadAmount = 0; @@ -1108,7 +1159,7 @@ void CClient::ProcessServerPacket(CNetChunk *pPacket) m_MapdownloadTotalsize = -1; // load map - const char *pError = LoadMap(m_aMapdownloadName, m_aMapdownloadFilename, m_MapdownloadCrc); + const char *pError = LoadMap(m_aMapdownloadName, m_aMapdownloadFilename, m_MapdownloadSha256Present ? &m_MapdownloadSha256 : 0, m_MapdownloadCrc); if(!pError) { m_pConsole->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "client/network", "loading done"); @@ -2184,7 +2235,7 @@ const char *CClient::DemoPlayer_Play(const char *pFilename, int StorageType) (m_DemoPlayer.Info()->m_Header.m_aMapCrc[1]<<16)| (m_DemoPlayer.Info()->m_Header.m_aMapCrc[2]<<8)| (m_DemoPlayer.Info()->m_Header.m_aMapCrc[3]); - pError = LoadMapSearch(m_DemoPlayer.Info()->m_Header.m_aMapName, Crc); + pError = LoadMapSearch(m_DemoPlayer.Info()->m_Header.m_aMapName, 0, Crc); if(pError) { DisconnectWithReason(pError); @@ -2239,7 +2290,7 @@ void CClient::DemoRecorder_Start(const char *pFilename, bool WithTimestamp) } else str_format(aFilename, sizeof(aFilename), "demos/%s.demo", pFilename); - m_DemoRecorder.Start(Storage(), m_pConsole, aFilename, GameClient()->NetVersion(), m_aCurrentMap, m_CurrentMapCrc, "client"); + m_DemoRecorder.Start(Storage(), m_pConsole, aFilename, GameClient()->NetVersion(), m_aCurrentMap, m_CurrentMapSha256, m_CurrentMapCrc, "client"); } } diff --git a/src/engine/client/client.h b/src/engine/client/client.h index c6609df52..6888c0fa2 100644 --- a/src/engine/client/client.h +++ b/src/engine/client/client.h @@ -3,6 +3,8 @@ #ifndef ENGINE_CLIENT_CLIENT_H #define ENGINE_CLIENT_CLIENT_H +#include + class CGraph { public: @@ -114,6 +116,7 @@ class CClient : public IClient, public CDemoPlayer::IListner // char m_aCurrentMap[256]; + SHA256_DIGEST m_CurrentMapSha256; unsigned m_CurrentMapCrc; // @@ -126,6 +129,8 @@ class CClient : public IClient, public CDemoPlayer::IListner int m_MapdownloadChunk; int m_MapdownloadChunkNum; int m_MapDownloadChunkSize; + SHA256_DIGEST m_MapdownloadSha256; + bool m_MapdownloadSha256Present; int m_MapdownloadCrc; int m_MapdownloadAmount; int m_MapdownloadTotalsize; @@ -252,8 +257,8 @@ public: virtual const char *ErrorString() const; - const char *LoadMap(const char *pName, const char *pFilename, unsigned WantedCrc); - const char *LoadMapSearch(const char *pMapName, int WantedCrc); + const char *LoadMap(const char *pName, const char *pFilename, const SHA256_DIGEST *pWantedSha256, unsigned WantedCrc); + const char *LoadMapSearch(const char *pMapName, const SHA256_DIGEST *pWantedSha256, int WantedCrc); int UnpackServerInfo(CUnpacker *pUnpacker, CServerInfo *pInfo, int *pToken); void ProcessConnlessPacket(CNetChunk *pPacket); diff --git a/src/engine/map.h b/src/engine/map.h index 5e0809831..bdc3cc68f 100644 --- a/src/engine/map.h +++ b/src/engine/map.h @@ -3,6 +3,7 @@ #ifndef ENGINE_MAP_H #define ENGINE_MAP_H +#include #include "kernel.h" class IMap : public IInterface @@ -26,6 +27,7 @@ public: virtual bool Load(const char *pMapName, class IStorage *pStorage=0) = 0; virtual bool IsLoaded() = 0; virtual void Unload() = 0; + virtual SHA256_DIGEST Sha256() = 0; virtual unsigned Crc() = 0; }; diff --git a/src/engine/server/server.cpp b/src/engine/server/server.cpp index f2be35f73..613dc6b99 100644 --- a/src/engine/server/server.cpp +++ b/src/engine/server/server.cpp @@ -714,6 +714,7 @@ void CServer::SendMap(int ClientID) Msg.AddInt(m_CurrentMapSize); Msg.AddInt(m_MapChunksPerRequest); Msg.AddInt(MAP_CHUNK_SIZE); + Msg.AddRaw(&m_CurrentMapSha256, sizeof(m_CurrentMapSha256)); SendMsg(&Msg, MSGFLAG_VITAL|MSGFLAG_FLUSH, ClientID); } @@ -1242,9 +1243,14 @@ int CServer::LoadMap(const char *pMapName) // reinit snapshot ids m_IDPool.TimeoutIDs(); - // get the crc of the map + // get the sha256 and crc of the map + m_CurrentMapSha256 = m_pMap->Sha256(); m_CurrentMapCrc = m_pMap->Crc(); + char aSha256[SHA256_MAXSTRSIZE]; + sha256_str(m_CurrentMapSha256, aSha256, sizeof(aSha256)); char aBufMsg[256]; + str_format(aBufMsg, sizeof(aBufMsg), "%s sha256 is %s", aBuf, aSha256); + Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBufMsg); str_format(aBufMsg, sizeof(aBufMsg), "%s crc is %08x", aBuf, m_CurrentMapCrc); Console()->Print(IConsole::OUTPUT_LEVEL_ADDINFO, "server", aBufMsg); @@ -1560,7 +1566,7 @@ void CServer::DemoRecorder_HandleAutoStart() char aDate[20]; str_timestamp(aDate, sizeof(aDate)); str_format(aFilename, sizeof(aFilename), "demos/%s_%s.demo", "auto/autorecord", aDate); - m_DemoRecorder.Start(Storage(), m_pConsole, aFilename, GameServer()->NetVersion(), m_aCurrentMap, m_CurrentMapCrc, "server"); + m_DemoRecorder.Start(Storage(), m_pConsole, aFilename, GameServer()->NetVersion(), m_aCurrentMap, m_CurrentMapSha256, m_CurrentMapCrc, "server"); if(g_Config.m_SvAutoDemoMax) { // clean up auto recorded demos @@ -1587,7 +1593,7 @@ void CServer::ConRecord(IConsole::IResult *pResult, void *pUser) str_timestamp(aDate, sizeof(aDate)); str_format(aFilename, sizeof(aFilename), "demos/demo_%s.demo", aDate); } - pServer->m_DemoRecorder.Start(pServer->Storage(), pServer->Console(), aFilename, pServer->GameServer()->NetVersion(), pServer->m_aCurrentMap, pServer->m_CurrentMapCrc, "server"); + pServer->m_DemoRecorder.Start(pServer->Storage(), pServer->Console(), aFilename, pServer->GameServer()->NetVersion(), pServer->m_aCurrentMap, pServer->m_CurrentMapSha256, pServer->m_CurrentMapCrc, "server"); } void CServer::ConStopRecord(IConsole::IResult *pResult, void *pUser) diff --git a/src/engine/server/server.h b/src/engine/server/server.h index f792d9744..9ff0d2c6c 100644 --- a/src/engine/server/server.h +++ b/src/engine/server/server.h @@ -163,6 +163,7 @@ public: MAP_CHUNK_SIZE=NET_MAX_PAYLOAD-NET_MAX_CHUNKHEADERSIZE-4, // msg type }; char m_aCurrentMap[64]; + SHA256_DIGEST m_CurrentMapSha256; unsigned m_CurrentMapCrc; unsigned char *m_pCurrentMapData; int m_CurrentMapSize; diff --git a/src/engine/shared/datafile.cpp b/src/engine/shared/datafile.cpp index aaa6e9f39..1310559c3 100644 --- a/src/engine/shared/datafile.cpp +++ b/src/engine/shared/datafile.cpp @@ -1,5 +1,6 @@ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ +#include #include #include #include @@ -58,6 +59,7 @@ struct CDatafileInfo struct CDatafile { IOHANDLE m_File; + SHA256_DIGEST m_Sha256; unsigned m_Crc; CDatafileInfo m_Info; CDatafileHeader m_Header; @@ -78,7 +80,9 @@ bool CDataFileReader::Open(class IStorage *pStorage, const char *pFilename, int } - // take the CRC of the file and store it + // take the hashes of the file and store them + SHA256_CTX Sha256Ctx; + sha256_init(&Sha256Ctx); unsigned Crc = crc32(0L, 0x0, 0); { enum @@ -93,13 +97,13 @@ bool CDataFileReader::Open(class IStorage *pStorage, const char *pFilename, int unsigned Bytes = io_read(File, aBuffer, BUFFER_SIZE); if(Bytes <= 0) break; + sha256_update(&Sha256Ctx, aBuffer, Bytes); Crc = crc32(Crc, aBuffer, Bytes); // ignore_convention } io_seek(File, 0, IOSEEK_START); } - // TODO: change this header CDatafileHeader Header; io_read(File, &Header, sizeof(Header)); @@ -141,6 +145,7 @@ bool CDataFileReader::Open(class IStorage *pStorage, const char *pFilename, int pTmpDataFile->m_ppDataPtrs = (char**)(pTmpDataFile+1); pTmpDataFile->m_pData = (char *)(pTmpDataFile+1)+Header.m_NumRawData*sizeof(char *); pTmpDataFile->m_File = File; + pTmpDataFile->m_Sha256 = sha256_finish(&Sha256Ctx); pTmpDataFile->m_Crc = Crc; // clear the data pointers @@ -401,6 +406,12 @@ bool CDataFileReader::Close() return true; } +SHA256_DIGEST CDataFileReader::Sha256() const +{ + if(!m_pDataFile) return SHA256_ZEROED; + return m_pDataFile->m_Sha256; +} + unsigned CDataFileReader::Crc() const { if(!m_pDataFile) return 0xFFFFFFFF; diff --git a/src/engine/shared/datafile.h b/src/engine/shared/datafile.h index 2e8044d61..72286cf7e 100644 --- a/src/engine/shared/datafile.h +++ b/src/engine/shared/datafile.h @@ -3,6 +3,8 @@ #ifndef ENGINE_SHARED_DATAFILE_H #define ENGINE_SHARED_DATAFILE_H +#include + // raw datafile access class CDataFileReader { @@ -30,6 +32,7 @@ public: int NumData() const; void Unload(); + SHA256_DIGEST Sha256() const; unsigned Crc() const; }; diff --git a/src/engine/shared/demo.cpp b/src/engine/shared/demo.cpp index c1a3162f9..dda2e444c 100644 --- a/src/engine/shared/demo.cpp +++ b/src/engine/shared/demo.cpp @@ -25,8 +25,10 @@ CDemoRecorder::CDemoRecorder(class CSnapshotDelta *pSnapshotDelta) m_pSnapshotDelta = pSnapshotDelta; } +// TODO: fix demo map loading (looks broken) + // Record -int CDemoRecorder::Start(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, const char *pNetVersion, const char *pMap, unsigned Crc, const char *pType) +int CDemoRecorder::Start(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, const char *pNetVersion, const char *pMap, SHA256_DIGEST Sha256, unsigned Crc, const char *pType) { CDemoHeader Header; if(m_File) diff --git a/src/engine/shared/demo.h b/src/engine/shared/demo.h index 1bdc3f9a0..8d3671dad 100644 --- a/src/engine/shared/demo.h +++ b/src/engine/shared/demo.h @@ -25,7 +25,7 @@ class CDemoRecorder : public IDemoRecorder public: CDemoRecorder(class CSnapshotDelta *pSnapshotDelta); - int Start(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, const char *pNetversion, const char *pMap, unsigned MapCrc, const char *pType); + int Start(class IStorage *pStorage, class IConsole *pConsole, const char *pFilename, const char *pNetversion, const char *pMap, SHA256_DIGEST MapSha256, unsigned MapCrc, const char *pType); int Stop(); void AddDemoMarker(); diff --git a/src/engine/shared/map.cpp b/src/engine/shared/map.cpp index f49e682d2..d38bab669 100644 --- a/src/engine/shared/map.cpp +++ b/src/engine/shared/map.cpp @@ -86,6 +86,11 @@ public: return m_DataFile.IsOpen(); } + virtual SHA256_DIGEST Sha256() + { + return m_DataFile.Sha256(); + } + virtual unsigned Crc() { return m_DataFile.Crc(); diff --git a/src/engine/shared/mapchecker.cpp b/src/engine/shared/mapchecker.cpp index 24098715c..9eaf0ffd3 100644 --- a/src/engine/shared/mapchecker.cpp +++ b/src/engine/shared/mapchecker.cpp @@ -46,7 +46,7 @@ void CMapChecker::AddMaplist(CMapVersion *pMaplist, int Num) } } -bool CMapChecker::IsMapValid(const char *pMapName, unsigned MapCrc, unsigned MapSize) +bool CMapChecker::IsMapValid(const char *pMapName, const SHA256_DIGEST *pMapSha256, unsigned MapCrc, unsigned MapSize) { return true; /*bool StandardMap = false; diff --git a/src/engine/shared/mapchecker.h b/src/engine/shared/mapchecker.h index 8f6d24e8e..72668b9d5 100644 --- a/src/engine/shared/mapchecker.h +++ b/src/engine/shared/mapchecker.h @@ -3,6 +3,8 @@ #ifndef ENGINE_SHARED_MAPCHECKER_H #define ENGINE_SHARED_MAPCHECKER_H +#include + #include "memheap.h" class CMapChecker @@ -15,6 +17,7 @@ class CMapChecker struct CWhitelistEntry { char m_aMapName[MAX_MAP_LENGTH]; + SHA256_DIGEST m_MapSha256; unsigned m_MapCrc; unsigned m_MapSize; CWhitelistEntry *m_pNext; @@ -31,7 +34,7 @@ class CMapChecker public: CMapChecker(); void AddMaplist(struct CMapVersion *pMaplist, int Num); - bool IsMapValid(const char *pMapName, unsigned MapCrc, unsigned MapSize); + bool IsMapValid(const char *pMapName, const SHA256_DIGEST *pMapSha256, unsigned MapCrc, unsigned MapSize); bool ReadAndValidateMap(class IStorage *pStorage, const char *pFilename, int StorageType); }; diff --git a/src/engine/shared/storage.cpp b/src/engine/shared/storage.cpp index 4f60342ba..db010980b 100644 --- a/src/engine/shared/storage.cpp +++ b/src/engine/shared/storage.cpp @@ -1,5 +1,6 @@ /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */ /* If you are missing that file, acquire a complete release at teeworlds.com. */ +#include #include #include #include "linereader.h" @@ -342,9 +343,10 @@ public: const char *m_pPath; char *m_pBuffer; int m_BufferSize; + const SHA256_DIGEST *m_pWantedSha256; unsigned m_WantedCrc; unsigned m_WantedSize; - bool m_CheckCrcSize; + bool m_CheckHashAndSize; }; static int FindFileCallback(const char *pName, int IsDir, int Type, void *pUser) @@ -369,12 +371,13 @@ public: // found the file str_format(Data.m_pBuffer, Data.m_BufferSize, "%s/%s", Data.m_pPath, Data.m_pFilename); - if(Data.m_CheckCrcSize) + if(Data.m_CheckHashAndSize) { // check crc and size + SHA256_DIGEST Sha256; unsigned Crc = 0; unsigned Size = 0; - if(!Data.m_pStorage->GetCrcSize(Data.m_pBuffer, Type, &Crc, &Size) || Crc != Data.m_WantedCrc || Size != Data.m_WantedSize) + if(!Data.m_pStorage->GetHashAndSize(Data.m_pBuffer, Type, &Sha256, &Crc, &Size) || (Data.m_pWantedSha256 && Sha256 != *Data.m_pWantedSha256) || Crc != Data.m_WantedCrc || Size != Data.m_WantedSize) { Data.m_pBuffer[0] = 0; return 0; @@ -423,13 +426,14 @@ public: Data.m_pPath = pPath; Data.m_pBuffer = pBuffer; Data.m_BufferSize = BufferSize; + Data.m_pWantedSha256 = 0; Data.m_WantedCrc = 0; Data.m_WantedSize = 0; - Data.m_CheckCrcSize = false; + Data.m_CheckHashAndSize = false; return FindFileImpl(Type, &Data); } - virtual bool FindFile(const char *pFilename, const char *pPath, int Type, char *pBuffer, int BufferSize, unsigned WantedCrc, unsigned WantedSize) + virtual bool FindFile(const char *pFilename, const char *pPath, int Type, char *pBuffer, int BufferSize, const SHA256_DIGEST *pWantedSha256, unsigned WantedCrc, unsigned WantedSize) { CFindCBData Data; Data.m_pStorage = this; @@ -437,9 +441,10 @@ public: Data.m_pPath = pPath; Data.m_pBuffer = pBuffer; Data.m_BufferSize = BufferSize; + Data.m_pWantedSha256 = pWantedSha256; Data.m_WantedCrc = WantedCrc; Data.m_WantedSize = WantedSize; - Data.m_CheckCrcSize = true; + Data.m_CheckHashAndSize = true; return FindFileImpl(Type, &Data); } @@ -482,13 +487,15 @@ public: GetPath(Type, pDir, pBuffer, BufferSize); } - virtual bool GetCrcSize(const char *pFilename, int StorageType, unsigned *pCrc, unsigned *pSize) + virtual bool GetHashAndSize(const char *pFilename, int StorageType, SHA256_DIGEST *pSha256, unsigned *pCrc, unsigned *pSize) { IOHANDLE File = OpenFile(pFilename, IOFLAG_READ, StorageType); if(!File) return false; - // get crc and size + // get hash and size + SHA256_CTX Sha256Ctx; + sha256_init(&Sha256Ctx); unsigned Crc = 0; unsigned Size = 0; unsigned char aBuffer[64*1024]; @@ -497,12 +504,14 @@ public: unsigned Bytes = io_read(File, aBuffer, sizeof(aBuffer)); if(Bytes <= 0) break; + sha256_update(&Sha256Ctx, aBuffer, Bytes); Crc = crc32(Crc, aBuffer, Bytes); // ignore_convention Size += Bytes; } io_close(File); + *pSha256 = sha256_finish(&Sha256Ctx); *pCrc = Crc; *pSize = Size; return true; diff --git a/src/engine/storage.h b/src/engine/storage.h index e9a9f71aa..2833de069 100644 --- a/src/engine/storage.h +++ b/src/engine/storage.h @@ -3,6 +3,7 @@ #ifndef ENGINE_STORAGE_H #define ENGINE_STORAGE_H +#include #include "kernel.h" class IStorage : public IInterface @@ -22,12 +23,12 @@ public: virtual void ListDirectory(int Type, const char *pPath, FS_LISTDIR_CALLBACK pfnCallback, void *pUser) = 0; virtual IOHANDLE OpenFile(const char *pFilename, int Flags, int Type, char *pBuffer = 0, int BufferSize = 0) = 0; virtual bool FindFile(const char *pFilename, const char *pPath, int Type, char *pBuffer, int BufferSize) = 0; - virtual bool FindFile(const char *pFilename, const char *pPath, int Type, char *pBuffer, int BufferSize, unsigned WantedCrc, unsigned WantedSize) = 0; + virtual bool FindFile(const char *pFilename, const char *pPath, int Type, char *pBuffer, int BufferSize, const SHA256_DIGEST *pWantedSha256, unsigned WantedCrc, unsigned WantedSize) = 0; virtual bool RemoveFile(const char *pFilename, int Type) = 0; virtual bool RenameFile(const char* pOldFilename, const char* pNewFilename, int Type) = 0; virtual bool CreateFolder(const char *pFoldername, int Type) = 0; virtual void GetCompletePath(int Type, const char *pDir, char *pBuffer, unsigned BufferSize) = 0; - virtual bool GetCrcSize(const char *pFilename, int StorageType, unsigned *pCrc, unsigned *pSize) = 0; + virtual bool GetHashAndSize(const char *pFilename, int StorageType, SHA256_DIGEST *pSha256, unsigned *pCrc, unsigned *pSize) = 0; }; IStorage *CreateStorage(const char *pApplicationName, int StorageType, int NumArgs, const char **ppArguments); diff --git a/src/test/hash.cpp b/src/test/hash.cpp new file mode 100644 index 000000000..7a018e215 --- /dev/null +++ b/src/test/hash.cpp @@ -0,0 +1,42 @@ +#include + +#include +#include + +static void Expect(SHA256_DIGEST Actual, const char *pWanted) +{ + char aActual[SHA256_MAXSTRSIZE]; + sha256_str(Actual, aActual, sizeof(aActual)); + EXPECT_STREQ(aActual, pWanted); +} + +TEST(Hash, Sha256) +{ + // https://en.wikipedia.org/w/index.php?title=SHA-2&oldid=840187620#Test_vectors + Expect(sha256("", 0), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + SHA256_CTX ctxt; + + sha256_init(&ctxt); + Expect(sha256_finish(&ctxt), "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"); + + // printf 'The quick brown fox jumps over the lazy dog.' | sha256sum + char QUICK_BROWN_FOX[] = "The quick brown fox jumps over the lazy dog."; + Expect(sha256(QUICK_BROWN_FOX, str_length(QUICK_BROWN_FOX)), "ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c"); + + sha256_init(&ctxt); + sha256_update(&ctxt, "The ", 4); + sha256_update(&ctxt, "quick ", 6); + sha256_update(&ctxt, "brown ", 6); + sha256_update(&ctxt, "fox ", 4); + sha256_update(&ctxt, "jumps ", 6); + sha256_update(&ctxt, "over ", 5); + sha256_update(&ctxt, "the ", 4); + sha256_update(&ctxt, "lazy ", 5); + sha256_update(&ctxt, "dog.", 4); + Expect(sha256_finish(&ctxt), "ef537f25c895bfa782526529a9b63d97aa631564d5d789c2b765448c8635fb6c"); +} + +TEST(Hash, Sha256Eq) +{ + EXPECT_EQ(sha256("", 0), sha256("", 0)); +} diff --git a/src/test/storage.cpp b/src/test/storage.cpp index 992137909..a42906284 100644 --- a/src/test/storage.cpp +++ b/src/test/storage.cpp @@ -16,17 +16,26 @@ TEST(Storage, FindFile) EXPECT_EQ(io_write(File, "test\n", 5), 5); EXPECT_FALSE(io_close(File)); + SHA256_DIGEST Sha256 = sha256("test\n", 5); + SHA256_DIGEST WrongSha256 = sha256("", 0); + char aFound[128]; EXPECT_TRUE(pStorage->FindFile(Info.m_aFilename, ".", IStorage::TYPE_ALL, aFound, sizeof(aFound))); EXPECT_STREQ(aFound, aFilenameWithDot); - EXPECT_TRUE(pStorage->FindFile(Info.m_aFilename, ".", IStorage::TYPE_ALL, aFound, sizeof(aFound), 0x3bb935c6, 5)); + EXPECT_TRUE(pStorage->FindFile(Info.m_aFilename, ".", IStorage::TYPE_ALL, aFound, sizeof(aFound), 0, 0x3bb935c6, 5)); EXPECT_STREQ(aFound, aFilenameWithDot); - EXPECT_FALSE(pStorage->FindFile(Info.m_aFilename, ".", IStorage::TYPE_ALL, aFound, sizeof(aFound), 0, 0)); - EXPECT_FALSE(pStorage->FindFile(Info.m_aFilename, ".", IStorage::TYPE_ALL, aFound, sizeof(aFound), 0x3bb935c6, 0)); - EXPECT_FALSE(pStorage->FindFile(Info.m_aFilename, ".", IStorage::TYPE_ALL, aFound, sizeof(aFound), 0, 5)); - EXPECT_FALSE(pStorage->FindFile(Info.m_aFilename, ".", IStorage::TYPE_ALL, aFound, sizeof(aFound), 0x3bb935c5, 5)); - EXPECT_FALSE(pStorage->FindFile(Info.m_aFilename, ".", IStorage::TYPE_ALL, aFound, sizeof(aFound), 0x3bb935c6, 6)); + EXPECT_TRUE(pStorage->FindFile(Info.m_aFilename, ".", IStorage::TYPE_ALL, aFound, sizeof(aFound), &Sha256, 0x3bb935c6, 5)); + EXPECT_STREQ(aFound, aFilenameWithDot); + + EXPECT_FALSE(pStorage->FindFile(Info.m_aFilename, ".", IStorage::TYPE_ALL, aFound, sizeof(aFound), 0, 0, 0)); + EXPECT_FALSE(pStorage->FindFile(Info.m_aFilename, ".", IStorage::TYPE_ALL, aFound, sizeof(aFound), 0, 0x3bb935c6, 0)); + EXPECT_FALSE(pStorage->FindFile(Info.m_aFilename, ".", IStorage::TYPE_ALL, aFound, sizeof(aFound), 0, 0, 5)); + EXPECT_FALSE(pStorage->FindFile(Info.m_aFilename, ".", IStorage::TYPE_ALL, aFound, sizeof(aFound), 0, 0x3bb935c5, 5)); + EXPECT_FALSE(pStorage->FindFile(Info.m_aFilename, ".", IStorage::TYPE_ALL, aFound, sizeof(aFound), 0, 0x3bb935c6, 6)); + + EXPECT_FALSE(pStorage->FindFile(Info.m_aFilename, ".", IStorage::TYPE_ALL, aFound, sizeof(aFound), &WrongSha256, 0x3bb935c6, 5)); + EXPECT_FALSE(pStorage->FindFile(Info.m_aFilename, ".", IStorage::TYPE_ALL, aFound, sizeof(aFound), &SHA256_ZEROED, 0x3bb935c6, 5)); }