@@ -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+
861999int CLuaFileDefs::fileGetPos (lua_State* luaVM)
8621000{
8631001 // int fileGetPos ( file theFile )
0 commit comments