Skip to content

Commit 94f944f

Browse files
committed
Add function fileGetHash
1 parent 8b0d21b commit 94f944f

File tree

6 files changed

+158
-0
lines changed

6 files changed

+158
-0
lines changed

Client/mods/deathmatch/logic/CScriptFile.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,14 @@ long CScriptFile::Read(unsigned long ulSize, SString& outBuffer)
180180
return m_pFile->FRead(outBuffer.data(), ulSize);
181181
}
182182

183+
long CScriptFile::ReadToBuffer(unsigned char* buffer, unsigned long bufferSize)
184+
{
185+
if (!m_pFile)
186+
return -1;
187+
188+
return m_pFile->FRead(buffer, bufferSize);
189+
}
190+
183191
long CScriptFile::Write(unsigned long ulSize, const char* pData)
184192
{
185193
if (!m_pFile)

Client/mods/deathmatch/logic/CScriptFile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ class CScriptFile final : public CClientEntity
6666

6767
void Flush();
6868
long Read(unsigned long ulSize, SString& outBuffer);
69+
long ReadToBuffer(unsigned char* buffer, unsigned long bufferSize);
6970
long Write(unsigned long ulSize, const char* pData);
7071

7172
long GetContents(std::string& buffer);

Server/mods/deathmatch/logic/CScriptFile.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,14 @@ long CScriptFile::Read(unsigned long ulSize, SString& outBuffer)
200200
return fread(outBuffer.data(), 1, ulSize, m_pFile);
201201
}
202202

203+
long CScriptFile::ReadToBuffer(unsigned char* buffer, unsigned long bufferSize)
204+
{
205+
if (!m_pFile)
206+
return -1;
207+
208+
return fread(buffer, 1, bufferSize, m_pFile);
209+
}
210+
203211
long CScriptFile::Write(unsigned long ulSize, const char* pData)
204212
{
205213
if (!m_pFile)

Server/mods/deathmatch/logic/CScriptFile.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class CScriptFile final : public CElement
6060

6161
void Flush();
6262
long Read(unsigned long ulSize, SString& outBuffer);
63+
long ReadToBuffer(unsigned char* buffer, unsigned long bufferSize);
6364
long Write(unsigned long ulSize, const char* pData);
6465

6566
long GetContents(std::string& buffer);

Shared/mods/deathmatch/logic/luadefs/CLuaFileDefs.cpp

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ void CLuaFileDefs::LoadFunctions()
5454
{"fileIsEOF", fileIsEOF},
5555
{"fileSetPos", fileSetPos},
5656
{"fileGetContents", ArgumentParser<fileGetContents>},
57+
{"fileGetHash", ArgumentParser<fileGetHash>},
5758
};
5859

5960
// Add functions
@@ -89,6 +90,7 @@ void CLuaFileDefs::AddClass(lua_State* luaVM)
8990
lua_classfunction(luaVM, "getSize", "fileGetSize");
9091
lua_classfunction(luaVM, "getPath", "fileGetPath");
9192
lua_classfunction(luaVM, "getContents", "fileGetContents");
93+
lua_classfunction(luaVM, "getHash", "fileGetHash");
9294
lua_classfunction(luaVM, "isEOF", "fileIsEOF");
9395

9496
lua_classfunction(luaVM, "setPos", "fileSetPos");
@@ -858,6 +860,142 @@ std::optional<std::string> CLuaFileDefs::fileGetContents(lua_State* L, CScriptFi
858860
return {};
859861
}
860862

863+
template <typename, typename = std::void_t<>>
864+
struct HasSetKeyMethod : std::false_type
865+
{
866+
};
867+
868+
template <typename T>
869+
struct HasSetKeyMethod<T, std::void_t<decltype(std::declval<T>().SetKey(std::declval<const CryptoPP::byte*>(), std::declval<size_t>()))>> : std::true_type
870+
{
871+
};
872+
873+
template <typename HashAlgorithmT>
874+
static std::string ComputeScriptFileHash(CScriptFile* scriptFile, std::string_view key = {})
875+
{
876+
HashAlgorithmT hash{};
877+
878+
if constexpr (HasSetKeyMethod<HashAlgorithmT>::value)
879+
{
880+
if (!key.empty())
881+
hash.SetKey(reinterpret_cast<const CryptoPP::byte*>(key.data()), key.size());
882+
}
883+
884+
std::array<unsigned char, 4096> buffer{};
885+
886+
while (!scriptFile->IsEOF())
887+
{
888+
const long bytesRead = scriptFile->ReadToBuffer(buffer.data(), static_cast<unsigned long>(buffer.size()));
889+
890+
if (bytesRead < 1)
891+
break;
892+
893+
hash.Update(buffer.data(), static_cast<size_t>(bytesRead));
894+
}
895+
896+
std::string digest;
897+
digest.resize(hash.DigestSize());
898+
hash.Final(reinterpret_cast<CryptoPP::byte*>(digest.data()));
899+
900+
SString result;
901+
CryptoPP::StringSource source(digest, true, new CryptoPP::HexEncoder(new CryptoPP::StringSink(result)));
902+
903+
return result.ToLower();
904+
}
905+
906+
std::optional<std::string> CLuaFileDefs::fileGetHash(lua_State* const luaVM, CScriptFile* scriptFile, HashFunctionType hashFunction,
907+
std::optional<std::unordered_map<std::string, std::string>> options)
908+
{
909+
// string|nil fileGetHash ( file theFile, string algorithm [, table options ] )
910+
911+
std::string result;
912+
913+
const long oldPosition = scriptFile->GetPointer();
914+
scriptFile->SetPointer(0);
915+
916+
try
917+
{
918+
switch (hashFunction)
919+
{
920+
case HashFunctionType::MD5:
921+
result = ComputeScriptFileHash<CryptoPP::MD5>(scriptFile);
922+
break;
923+
case HashFunctionType::SHA1:
924+
result = ComputeScriptFileHash<CryptoPP::SHA1>(scriptFile);
925+
break;
926+
case HashFunctionType::SHA224:
927+
result = ComputeScriptFileHash<CryptoPP::SHA224>(scriptFile);
928+
break;
929+
case HashFunctionType::SHA256:
930+
result = ComputeScriptFileHash<CryptoPP::SHA256>(scriptFile);
931+
break;
932+
case HashFunctionType::SHA384:
933+
result = ComputeScriptFileHash<CryptoPP::SHA384>(scriptFile);
934+
break;
935+
case HashFunctionType::SHA512:
936+
result = ComputeScriptFileHash<CryptoPP::SHA512>(scriptFile);
937+
break;
938+
case HashFunctionType::HMAC:
939+
{
940+
if (!options.has_value())
941+
throw std::invalid_argument("Invalid value for fields 'key' and 'algorithm'");
942+
943+
std::unordered_map<std::string, std::string>& optionsMap = options.value();
944+
945+
std::string& key = optionsMap["key"];
946+
std::string& algorithm = optionsMap["algorithm"];
947+
HmacAlgorithm hmacAlgorithm;
948+
949+
if (key.empty())
950+
throw std::invalid_argument("Invalid value for field 'key'");
951+
952+
if (algorithm.empty() || !StringToEnum(algorithm, hmacAlgorithm))
953+
throw std::invalid_argument("Invalid value for field 'algorithm'");
954+
955+
switch (hmacAlgorithm)
956+
{
957+
case HmacAlgorithm::MD5:
958+
result = ComputeScriptFileHash<CryptoPP::HMAC<CryptoPP::MD5>>(scriptFile, key);
959+
break;
960+
case HmacAlgorithm::SHA1:
961+
result = ComputeScriptFileHash<CryptoPP::HMAC<CryptoPP::SHA1>>(scriptFile, key);
962+
break;
963+
case HmacAlgorithm::SHA224:
964+
result = ComputeScriptFileHash<CryptoPP::HMAC<CryptoPP::SHA224>>(scriptFile, key);
965+
break;
966+
case HmacAlgorithm::SHA256:
967+
result = ComputeScriptFileHash<CryptoPP::HMAC<CryptoPP::SHA256>>(scriptFile, key);
968+
break;
969+
case HmacAlgorithm::SHA384:
970+
result = ComputeScriptFileHash<CryptoPP::HMAC<CryptoPP::SHA384>>(scriptFile, key);
971+
break;
972+
case HmacAlgorithm::SHA512:
973+
result = ComputeScriptFileHash<CryptoPP::HMAC<CryptoPP::SHA512>>(scriptFile, key);
974+
break;
975+
default:
976+
throw std::invalid_argument("Invalid hmac algorithm");
977+
}
978+
979+
break;
980+
}
981+
default:
982+
throw std::invalid_argument("Unknown hash algorithm");
983+
}
984+
}
985+
catch (std::exception& ex)
986+
{
987+
m_pScriptDebugging->LogWarning(luaVM, ex.what());
988+
result.clear();
989+
}
990+
991+
scriptFile->SetPointer(oldPosition);
992+
993+
if (result.empty())
994+
return {};
995+
996+
return result;
997+
}
998+
861999
int CLuaFileDefs::fileGetPos(lua_State* luaVM)
8621000
{
8631001
// int fileGetPos ( file theFile )

Shared/mods/deathmatch/logic/luadefs/CLuaFileDefs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ class CLuaFileDefs : public CLuaDefs
3131
LUA_DECLARE(fileRead);
3232
LUA_DECLARE(fileWrite);
3333
static std::optional<std::string> fileGetContents(lua_State* L, CScriptFile* scriptFile, std::optional<bool> maybeVerifyContents);
34+
static std::optional<std::string> fileGetHash(lua_State* const luaVM, CScriptFile* scriptFile, HashFunctionType hashFunction,
35+
std::optional<std::unordered_map<std::string, std::string>> options);
3436

3537
LUA_DECLARE(fileGetPos);
3638
LUA_DECLARE(fileGetSize);

0 commit comments

Comments
 (0)