diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index fe0c97324..0fb780464 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -49,6 +49,8 @@ jobs: env: CC: gcc-7 CXX: g++-7 + # Test fallback SHA implementations + CMAKE_FLAGS: '-DENABLE_OPENSSL=0' - name: Test run: | diff --git a/doc/compiling/README.md b/doc/compiling/README.md index 55357adf6..16167977b 100644 --- a/doc/compiling/README.md +++ b/doc/compiling/README.md @@ -30,6 +30,7 @@ General options and their default values: ENABLE_POSTGRESQL=ON - Build with libpq; Enables use of PostgreSQL map backend (PostgreSQL 9.5 or greater recommended) ENABLE_REDIS=ON - Build with libhiredis; Enables use of Redis map backend ENABLE_SPATIAL=ON - Build with LibSpatial; Speeds up AreaStores + ENABLE_OPENSSL=ON - Build with OpenSSL; Speeds up SHA1 and SHA2 hashing ENABLE_SOUND=ON - Build with OpenAL, libogg & libvorbis; in-game sounds ENABLE_LTO= - Build with IPO/LTO optimizations (smaller and more efficient than regular build) ENABLE_LUAJIT=ON - Build with LuaJIT (much faster than non-JIT Lua) diff --git a/doc/compiling/linux.md b/doc/compiling/linux.md index 54f7de2a0..54a44d501 100644 --- a/doc/compiling/linux.md +++ b/doc/compiling/linux.md @@ -18,6 +18,7 @@ | JsonCPP | 1.0.0+ | Bundled JsonCPP is used if not present | | Curl | 7.56.0+ | Optional | | gettext | - | Optional | +| OpenSSL | 3.0+ | Optional (only libcrypto used) | For Debian/Ubuntu users: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index d764e186e..692651049 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -249,6 +249,19 @@ if(ENABLE_SPATIAL) endif(SPATIAL_LIBRARY AND SPATIAL_INCLUDE_DIR) endif(ENABLE_SPATIAL) +option(ENABLE_OPENSSL "Use OpenSSL's libcrypto for faster SHA implementations" TRUE) +set(USE_OPENSSL FALSE) + +if(ENABLE_OPENSSL) + find_package(OpenSSL 3.0) + if(OPENSSL_FOUND) + set(USE_OPENSSL TRUE) + message(STATUS "OpenSSL's libcrypto SHA enabled.") + else() + message(STATUS "OpenSSL not found!") + endif() +endif(ENABLE_OPENSSL) + find_package(ZLIB REQUIRED) find_package(Zstd REQUIRED) @@ -593,6 +606,9 @@ add_dependencies(EngineCommon GenerateVersion) target_link_libraries(EngineCommon sha256 ) +if(USE_OPENSSL) + target_link_libraries(EngineCommon OpenSSL::Crypto) +endif() get_target_property( IRRLICHT_INCLUDES IrrlichtMt::IrrlichtMt INTERFACE_INCLUDE_DIRECTORIES) target_include_directories(EngineCommon PRIVATE ${IRRLICHT_INCLUDES}) @@ -726,6 +742,9 @@ if(BUILD_CLIENT) if (USE_SPATIAL) target_link_libraries(${PROJECT_NAME} ${SPATIAL_LIBRARY}) endif() + if (USE_OPENSSL) + target_link_libraries(${PROJECT_NAME} OpenSSL::Crypto) + endif() if(BUILD_UNITTESTS OR BUILD_BENCHMARKS) target_link_libraries(${PROJECT_NAME} Catch2::Catch2) endif() @@ -795,6 +814,9 @@ if(BUILD_SERVER) if (USE_SPATIAL) target_link_libraries(${PROJECT_NAME}server ${SPATIAL_LIBRARY}) endif() + if (USE_OPENSSL) + target_link_libraries(${PROJECT_NAME}server OpenSSL::Crypto) + endif() if(USE_CURL) target_link_libraries( ${PROJECT_NAME}server diff --git a/src/benchmark/CMakeLists.txt b/src/benchmark/CMakeLists.txt index f79fcf1ef..e8150848a 100644 --- a/src/benchmark/CMakeLists.txt +++ b/src/benchmark/CMakeLists.txt @@ -5,6 +5,7 @@ set (BENCHMARK_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_serialize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_mapblock.cpp ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_mapmodify.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_sha.cpp PARENT_SCOPE) set (BENCHMARK_CLIENT_SRCS diff --git a/src/benchmark/benchmark_sha.cpp b/src/benchmark/benchmark_sha.cpp new file mode 100644 index 000000000..616606f55 --- /dev/null +++ b/src/benchmark/benchmark_sha.cpp @@ -0,0 +1,23 @@ +// SPDX-FileCopyrightText: 2024 Luanti Contributors +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "catch.h" +#include "util/hashing.h" +#include + +TEST_CASE("benchmark_sha") +{ + std::string input; + input.resize(100000); + + BENCHMARK("sha1_input100000", i) { + input[0] = (char)i; + return hashing::sha1(input); + }; + + BENCHMARK("sha256_input100000", i) { + input[0] = (char)i; + return hashing::sha256(input); + }; +} diff --git a/src/client/clientmedia.cpp b/src/client/clientmedia.cpp index 2c6cb69b3..2ee0d747b 100644 --- a/src/client/clientmedia.cpp +++ b/src/client/clientmedia.cpp @@ -13,7 +13,7 @@ #include "settings.h" #include "util/hex.h" #include "util/serialize.h" -#include "util/sha1.h" +#include "util/hashing.h" #include "util/string.h" #include @@ -537,12 +537,7 @@ bool IClientMediaDownloader::checkAndLoad( std::string sha1_hex = hex_encode(sha1); // Compute actual checksum of data - std::string data_sha1; - { - SHA1 ctx; - ctx.addBytes(data); - data_sha1 = ctx.getDigest(); - } + std::string data_sha1 = hashing::sha1(data); // Check that received file matches announced checksum if (data_sha1 != sha1) { diff --git a/src/cmake_config.h.in b/src/cmake_config.h.in index 5dc6e4b74..2ec91dfd1 100644 --- a/src/cmake_config.h.in +++ b/src/cmake_config.h.in @@ -29,6 +29,7 @@ #cmakedefine01 USE_SYSTEM_GMP #cmakedefine01 USE_SYSTEM_JSONCPP #cmakedefine01 USE_REDIS +#cmakedefine01 USE_OPENSSL #cmakedefine01 HAVE_ENDIAN_H #cmakedefine01 HAVE_STRLCPY #cmakedefine01 HAVE_MALLOC_TRIM diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 4b899e598..1024988bd 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -28,7 +28,7 @@ #include "script/scripting_client.h" #include "util/serialize.h" #include "util/srp.h" -#include "util/sha1.h" +#include "util/hashing.h" #include "tileanimation.h" #include "gettext.h" #include "skyparams.h" @@ -1645,12 +1645,7 @@ void Client::handleCommand_MediaPush(NetworkPacket *pkt) if (!filedata.empty()) { // LEGACY CODEPATH // Compute and check checksum of data - std::string computed_hash; - { - SHA1 ctx; - ctx.addBytes(filedata); - computed_hash = ctx.getDigest(); - } + std::string computed_hash = hashing::sha1(filedata); if (raw_hash != computed_hash) { verbosestream << "Hash of file data mismatches, ignoring." << std::endl; return; diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index cfea974b3..f84835e8b 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -27,8 +27,7 @@ #include "config.h" #include "version.h" #include "util/hex.h" -#include "util/sha1.h" -#include "my_sha256.h" +#include "util/hashing.h" #include "util/png.h" #include "player.h" #include "daynightratio.h" @@ -540,12 +539,7 @@ int ModApiUtil::l_sha1(lua_State *L) bool hex = !lua_isboolean(L, 2) || !readParam(L, 2); // Compute actual checksum of data - std::string data_sha1; - { - SHA1 ctx; - ctx.addBytes(data); - data_sha1 = ctx.getDigest(); - } + std::string data_sha1 = hashing::sha1(data); if (hex) { std::string sha1_hex = hex_encode(data_sha1); @@ -564,10 +558,7 @@ int ModApiUtil::l_sha256(lua_State *L) auto data = readParam(L, 1); bool hex = !lua_isboolean(L, 2) || !readParam(L, 2); - std::string data_sha256; - data_sha256.resize(SHA256_DIGEST_LENGTH); - SHA256(reinterpret_cast(data.data()), data.size(), - reinterpret_cast(data_sha256.data())); + std::string data_sha256 = hashing::sha256(data); if (hex) { lua_pushstring(L, hex_encode(data_sha256).c_str()); diff --git a/src/server.cpp b/src/server.cpp index b21955170..0a5300f5f 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -44,7 +44,7 @@ #include "defaultsettings.h" #include "server/mods.h" #include "util/base64.h" -#include "util/sha1.h" +#include "util/hashing.h" #include "util/hex.h" #include "database/database.h" #include "chatmessage.h" @@ -2548,14 +2548,11 @@ bool Server::addMediaFile(const std::string &filename, return false; } - SHA1 sha1; - sha1.addBytes(filedata); - - std::string digest = sha1.getDigest(); - std::string sha1_base64 = base64_encode(digest); - std::string sha1_hex = hex_encode(digest); + std::string sha1 = hashing::sha1(filedata); + std::string sha1_base64 = base64_encode(sha1); + std::string sha1_hex = hex_encode(sha1); if (digest_to) - *digest_to = digest; + *digest_to = sha1; // Put in list m_media[filename] = MediaInfo(filepath, sha1_base64); diff --git a/src/unittest/test_map_settings_manager.cpp b/src/unittest/test_map_settings_manager.cpp index 234b40cda..8fb074e17 100644 --- a/src/unittest/test_map_settings_manager.cpp +++ b/src/unittest/test_map_settings_manager.cpp @@ -7,7 +7,7 @@ #include "noise.h" #include "settings.h" #include "mapgen/mapgen_v5.h" -#include "util/sha1.h" +#include "util/hashing.h" #include "map_settings_manager.h" class TestMapSettingsManager : public TestBase { @@ -171,11 +171,9 @@ void TestMapSettingsManager::testMapSettingsManager() 0x78, 0x56, 0x95, 0x2d, 0xdc, 0x6a, 0xf7, 0x61, 0x36, 0x5f }; - SHA1 ctx; std::string metafile_contents; UASSERT(fs::ReadFile(test_mapmeta_path, metafile_contents)); - ctx.addBytes(metafile_contents); - std::string sha1_result = ctx.getDigest(); + std::string sha1_result = hashing::sha1(metafile_contents); int resultdiff = memcmp(sha1_result.data(), expected_contents_hash, 20); UASSERT(!resultdiff); diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 761e51a4a..ec88a33c2 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -1,10 +1,11 @@ set(util_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/areastore.cpp ${CMAKE_CURRENT_SOURCE_DIR}/auth.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/colorize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/base64.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/colorize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/directiontables.cpp ${CMAKE_CURRENT_SOURCE_DIR}/enriched_string.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/hashing.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ieee_float.cpp ${CMAKE_CURRENT_SOURCE_DIR}/metricsbackend.cpp ${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp diff --git a/src/util/auth.cpp b/src/util/auth.cpp index 3c8e5763a..040d15bf9 100644 --- a/src/util/auth.cpp +++ b/src/util/auth.cpp @@ -6,7 +6,7 @@ #include #include "auth.h" #include "base64.h" -#include "sha1.h" +#include "util/hashing.h" #include "srp.h" #include "util/string.h" #include "debug.h" @@ -23,9 +23,7 @@ std::string translate_password(const std::string &name, return ""; std::string slt = name + password; - SHA1 sha1; - sha1.addBytes(slt); - std::string digest = sha1.getDigest(); + std::string digest = hashing::sha1(slt); std::string pwd = base64_encode(digest); return pwd; } diff --git a/src/util/hashing.cpp b/src/util/hashing.cpp new file mode 100644 index 000000000..452cd6818 --- /dev/null +++ b/src/util/hashing.cpp @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2024 Luanti Contributors +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "hashing.h" + +#include "debug.h" +#include "config.h" +#if USE_OPENSSL +#include +#include +#else +#include "util/sha1.h" +#include "my_sha256.h" +#endif + +namespace hashing +{ + +std::string sha1(std::string_view data) +{ +#if USE_OPENSSL + std::string digest(SHA1_DIGEST_SIZE, '\000'); + auto src = reinterpret_cast(data.data()); + auto dst = reinterpret_cast(digest.data()); + SHA1(src, data.size(), dst); + return digest; +#else + SHA1 sha1; + sha1.addBytes(data); + return sha1.getDigest(); +#endif +} + +std::string sha256(std::string_view data) +{ + std::string digest(SHA256_DIGEST_SIZE, '\000'); + auto src = reinterpret_cast(data.data()); + auto dst = reinterpret_cast(digest.data()); +#if USE_OPENSSL + // can't call SHA256(), because it's defined by our sha256.c fallback + auto success = EVP_Digest(src, data.size(), dst, nullptr, EVP_sha256(), nullptr) == 1; + FATAL_ERROR_IF(!success, "sha256 failed"); +#else + SHA256(src, data.size(), dst); +#endif + return digest; +} + +} diff --git a/src/util/hashing.h b/src/util/hashing.h new file mode 100644 index 000000000..3e9a0fee7 --- /dev/null +++ b/src/util/hashing.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: 2024 Luanti Contributors +// +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include + +namespace hashing +{ + +// Size of raw digest in bytes +constexpr size_t SHA1_DIGEST_SIZE = 20; +constexpr size_t SHA256_DIGEST_SIZE = 32; + +// Returns the digest of data +std::string sha1(std::string_view data); +std::string sha256(std::string_view data); + +} diff --git a/util/ci/common.sh b/util/ci/common.sh index 201b182f2..4d4fe1195 100644 --- a/util/ci/common.sh +++ b/util/ci/common.sh @@ -7,6 +7,7 @@ install_linux_deps() { libpng-dev libjpeg-dev libgl1-mesa-dev libsdl2-dev libfreetype-dev libsqlite3-dev libhiredis-dev libogg-dev libgmp-dev libvorbis-dev libopenal-dev libpq-dev libleveldb-dev libcurl4-openssl-dev libzstd-dev + libssl-dev ) sudo apt-get update