Merge pull request #2055 from heinrich5991/pr_sha256

Use more secure hash function for map downloads
This commit is contained in:
oy 2019-03-08 19:10:56 +01:00 committed by GitHub
commit ac6f1ac506
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 560 additions and 44 deletions

View file

@ -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 $<$<CONFIG:Debug>: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})

19
cmake/FindCrypto.cmake Normal file
View file

@ -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()

41
src/base/hash.c Normal file
View file

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

43
src/base/hash.h Normal file
View file

@ -0,0 +1,43 @@
#ifndef BASE_HASH_H
#define BASE_HASH_H
#include <stddef.h>
#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

35
src/base/hash_ctxt.h Normal file
View file

@ -0,0 +1,35 @@
#ifndef BASE_HASH_CTXT_H
#define BASE_HASH_CTXT_H
#include "hash.h"
#include <stdint.h>
#if defined(CONF_OPENSSL)
#include <openssl/sha.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
#if defined(CONF_OPENSSL)
// SHA256_CTX is defined in <openssl/sha.h>
#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

197
src/base/hash_libtomcrypt.c Executable file
View file

@ -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 <stddef.h>
#include <stdint.h>
#include <string.h>
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

20
src/base/hash_openssl.c Normal file
View file

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

View file

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

View file

@ -3,6 +3,8 @@
#ifndef ENGINE_CLIENT_CLIENT_H
#define ENGINE_CLIENT_CLIENT_H
#include <base/hash.h>
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);

View file

@ -3,6 +3,7 @@
#ifndef ENGINE_MAP_H
#define ENGINE_MAP_H
#include <base/hash.h>
#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;
};

View file

@ -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)

View file

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

View file

@ -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 <base/hash_ctxt.h>
#include <base/math.h>
#include <base/system.h>
#include <engine/storage.h>
@ -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;

View file

@ -3,6 +3,8 @@
#ifndef ENGINE_SHARED_DATAFILE_H
#define ENGINE_SHARED_DATAFILE_H
#include <base/hash.h>
// raw datafile access
class CDataFileReader
{
@ -30,6 +32,7 @@ public:
int NumData() const;
void Unload();
SHA256_DIGEST Sha256() const;
unsigned Crc() const;
};

View file

@ -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)

View file

@ -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();

View file

@ -86,6 +86,11 @@ public:
return m_DataFile.IsOpen();
}
virtual SHA256_DIGEST Sha256()
{
return m_DataFile.Sha256();
}
virtual unsigned Crc()
{
return m_DataFile.Crc();

View file

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

View file

@ -3,6 +3,8 @@
#ifndef ENGINE_SHARED_MAPCHECKER_H
#define ENGINE_SHARED_MAPCHECKER_H
#include <base/hash.h>
#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);
};

View file

@ -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 <base/hash_ctxt.h>
#include <base/system.h>
#include <engine/storage.h>
#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;

View file

@ -3,6 +3,7 @@
#ifndef ENGINE_STORAGE_H
#define ENGINE_STORAGE_H
#include <base/hash.h>
#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);

42
src/test/hash.cpp Normal file
View file

@ -0,0 +1,42 @@
#include <gtest/gtest.h>
#include <base/hash_ctxt.h>
#include <base/system.h>
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));
}

View file

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