diff --git a/THIRDPARTY_LEGAL_NOTICES.txt b/THIRDPARTY_LEGAL_NOTICES.txt index ddab7ee58..b51c80c65 100644 --- a/THIRDPARTY_LEGAL_NOTICES.txt +++ b/THIRDPARTY_LEGAL_NOTICES.txt @@ -74,39 +74,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------- Crypto++ --------------- - -BSD 3-Clause "New" or "Revised" License - -Copyright (c) 2018, The Authors -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - --------------- emscripten --------------- MIT License @@ -266,6 +233,66 @@ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +--------------- libtomcrypt --------------- + +The Unlicense + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + + +--------------- libtommath --------------- + +The Unlicense + +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + + --------------- libwebp --------------- BSD 3-Clause "New" or "Revised" License diff --git a/cmake/AddRemoteLibrary.cmake b/cmake/AddRemoteLibrary.cmake index 3aa50d140..c8e9616c9 100644 --- a/cmake/AddRemoteLibrary.cmake +++ b/cmake/AddRemoteLibrary.cmake @@ -3,7 +3,7 @@ include_guard(GLOBAL) include(FetchContent) function(add_sourcepp_remote_library NAME REPOSITORY TAG) - cmake_parse_arguments(PARSE_ARGV 3 OPTIONS "ALLOW_NOT_PINNED;EXCLUDE_FROM_ALL;DO_NOT_USE_CMAKELISTS" "" "") + cmake_parse_arguments(PARSE_ARGV 3 OPTIONS "ALLOW_NOT_PINNED;DO_NOT_USE_CMAKELISTS;EXCLUDE_FROM_ALL;OVERRIDE_FIND_PACKAGE" "" "") if(NOT OPTIONS_ALLOW_NOT_PINNED) string(LENGTH "${TAG}" TAG_SIZE) if(NOT TAG_SIZE EQUAL 40) @@ -13,31 +13,17 @@ function(add_sourcepp_remote_library NAME REPOSITORY TAG) if(NOT TARGET NAME) message(STATUS "Fetching ${NAME} ${REPOSITORY} ${TAG}") string(REPLACE ":" "_" NAME_CLEAN "${NAME}") + set(FETCHCONTENT_DECLARE_ARGUMENTS "${NAME_CLEAN}" "GIT_REPOSITORY" "${REPOSITORY}.git" "GIT_TAG" "${TAG}") + if(OPTIONS_DO_NOT_USE_CMAKELISTS) + list(APPEND FETCHCONTENT_DECLARE_ARGUMENTS "SOURCE_SUBDIR" "THIS_DIRECTORY_DOES_NOT_EXIST") + endif() if(OPTIONS_EXCLUDE_FROM_ALL) - if(OPTIONS_DO_NOT_USE_CMAKELISTS) - FetchContent_Declare(${NAME_CLEAN} - GIT_REPOSITORY "${REPOSITORY}.git" - GIT_TAG "${TAG}" - SOURCE_SUBDIR "THIS_DIRECTORY_DOES_NOT_EXIST" - EXCLUDE_FROM_ALL) - else() - FetchContent_Declare(${NAME_CLEAN} - GIT_REPOSITORY "${REPOSITORY}.git" - GIT_TAG "${TAG}" - EXCLUDE_FROM_ALL) - endif() - else() - if(OPTIONS_DO_NOT_USE_CMAKELISTS) - FetchContent_Declare(${NAME_CLEAN} - GIT_REPOSITORY "${REPOSITORY}.git" - GIT_TAG "${TAG}" - SOURCE_SUBDIR "THIS_DIRECTORY_DOES_NOT_EXIST") - else() - FetchContent_Declare(${NAME_CLEAN} - GIT_REPOSITORY "${REPOSITORY}.git" - GIT_TAG "${TAG}") - endif() + list(APPEND FETCHCONTENT_DECLARE_ARGUMENTS "EXCLUDE_FROM_ALL") + endif() + if(OPTIONS_OVERRIDE_FIND_PACKAGE) + list(APPEND FETCHCONTENT_DECLARE_ARGUMENTS "OVERRIDE_FIND_PACKAGE") endif() + FetchContent_Declare(${FETCHCONTENT_DECLARE_ARGUMENTS}) FetchContent_MakeAvailable(${NAME_CLEAN}) endif() return(PROPAGATE "${NAME_CLEAN}_POPULATED" "${NAME_CLEAN}_SOURCE_DIR" "${NAME_CLEAN}_BINARY_DIR") diff --git a/ext/_ext.cmake b/ext/_ext.cmake index 12ffd8806..80594c5d7 100644 --- a/ext/_ext.cmake +++ b/ext/_ext.cmake @@ -1,3 +1,21 @@ +# Threads +if(SOURCEPP_BUILD_WITH_THREADS) + set(CMAKE_THREAD_PREFER_PTHREAD ON) + set(THREADS_PREFER_PTHREAD_FLAG ON) + find_package(Threads) + if(NOT Threads_FOUND) + set(SOURCEPP_BUILD_WITH_THREADS OFF CACHE INTERNAL "" FORCE) + endif() +endif() + +function(sourcepp_add_threads TARGET) + if(SOURCEPP_BUILD_WITH_THREADS) + target_compile_definitions(${TARGET} PUBLIC SOURCEPP_BUILD_WITH_THREADS) + target_link_libraries(${TARGET} PRIVATE Threads::Threads) + endif() +endfunction() + + # bcdec if(SOURCEPP_USE_VTFPP) add_sourcepp_remote_library(bcdec https://github.com/craftablescience/bcdec 59441e17ba36b7d7eef336aeedc62e01d0cdcd5a) @@ -14,19 +32,6 @@ if(SOURCEPP_USE_VTFPP AND SOURCEPP_VTFPP_BUILD_WITH_COMPRESSONATOR) endif() -# cryptopp -if(NOT TARGET cryptopp::cryptopp) - set(CRYPTOPP_BUILD_TESTING OFF CACHE INTERNAL "" FORCE) - set(CRYPTOPP_INSTALL OFF CACHE INTERNAL "" FORCE) - add_sourcepp_remote_library(cryptopp-cmake https://github.com/abdes/cryptopp-cmake 866aceb8b13b6427a3c4541288ff412ad54f11ea) - - # hack: clang on windows (NOT clang-cl) needs these to compile cryptopp - if(WIN32 AND NOT MSVC AND CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - target_compile_options(cryptopp PRIVATE -mcrc32 -mssse3) - endif() -endif() - - # half add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/half") @@ -43,6 +48,51 @@ if(SOURCEPP_USE_VCRYPTPP) endif() +# libtommath +if(NOT TARGET libtommath) + add_sourcepp_remote_library(libtommath https://github.com/libtom/libtommath ae40a87a920099a7d9d00979570e0c8d917a1fd7 OVERRIDE_FIND_PACKAGE) + + # hack: always compile with optimizations on MSVC because this library stinks + if(WIN32 AND MSVC) + get_target_property(libtommath_CURRENT_FLAGS libtommath COMPILE_FLAGS) + if(libtommath_CURRENT_FLAGS) + list(REMOVE_ITEM libtommath_CURRENT_OPTIONS "/RTC1") + list(APPEND libtommath_CURRENT_OPTIONS "/O2") + set_target_properties(libtommath PROPERTIES COMPILE_FLAGS "${libtommath_CURRENT_FLAGS}") + endif() + endif() +endif() + + +# libtomcrypt +if(NOT TARGET libtomcrypt) + if(SOURCEPP_BUILD_WITH_THREADS AND CMAKE_USE_PTHREADS_INIT) + set(WITH_PTHREAD ON CACHE INTERNAL "" FORCE) + endif() + add_sourcepp_remote_library(libtomcrypt https://github.com/libtom/libtomcrypt c80285ba04f87ee5359baf689ccc7ce8a31116dc) +endif() + + +# libwebp +if(SOURCEPP_USE_VTFPP AND SOURCEPP_VTFPP_SUPPORT_WEBP) + set(WEBP_BUILD_ANIM_UTILS OFF CACHE INTERNAL "" FORCE) + set(WEBP_BUILD_CWEBP OFF CACHE INTERNAL "" FORCE) + set(WEBP_BUILD_DWEBP OFF CACHE INTERNAL "" FORCE) + set(WEBP_BUILD_GIF2WEBP OFF CACHE INTERNAL "" FORCE) + set(WEBP_BUILD_IMG2WEBP OFF CACHE INTERNAL "" FORCE) + set(WEBP_BUILD_VWEBP OFF CACHE INTERNAL "" FORCE) + set(WEBP_BUILD_WEBPINFO OFF CACHE INTERNAL "" FORCE) + set(WEBP_BUILD_LIBWEBPMUX OFF CACHE INTERNAL "" FORCE) + set(WEBP_BUILD_WEBPMUX OFF CACHE INTERNAL "" FORCE) + set(WEBP_BUILD_EXTRAS OFF CACHE INTERNAL "" FORCE) + set(WEBP_BUILD_WEBP_JS OFF CACHE INTERNAL "" FORCE) + set(WEBP_BUILD_FUZZTEST OFF CACHE INTERNAL "" FORCE) + set(WEBP_USE_THREAD ${SOURCEPP_BUILD_WITH_THREADS} CACHE INTERNAL "" FORCE) + set(WEBP_NEAR_LOSSLESS ON CACHE INTERNAL "" FORCE) + add_sourcepp_remote_library(libwebp https://github.com/webmproject/libwebp 5003e5609eedc5680b8d838a962cbb9a6e9709ce EXCLUDE_FROM_ALL) +endif() + + # miniz if(SOURCEPP_USE_VPKPP OR SOURCEPP_USE_VTFPP) add_sourcepp_remote_library(miniz https://github.com/richgel999/miniz 5cf1e56a9c968c11fdd1a6414f3a95f84314c437) @@ -116,41 +166,3 @@ function(sourcepp_add_tbb TARGET) endif() endif() endfunction() - - -# Threads -if(SOURCEPP_BUILD_WITH_THREADS) - set(CMAKE_THREAD_PREFER_PTHREAD ON) - set(THREADS_PREFER_PTHREAD_FLAG ON) - find_package(Threads) - if(NOT Threads_FOUND) - set(SOURCEPP_BUILD_WITH_THREADS OFF CACHE INTERNAL "" FORCE) - endif() -endif() - -function(sourcepp_add_threads TARGET) - if(SOURCEPP_BUILD_WITH_THREADS) - target_compile_definitions(${TARGET} PUBLIC SOURCEPP_BUILD_WITH_THREADS) - target_link_libraries(${TARGET} PRIVATE Threads::Threads) - endif() -endfunction() - - -# webp -if(SOURCEPP_USE_VTFPP AND SOURCEPP_VTFPP_SUPPORT_WEBP) - set(WEBP_BUILD_ANIM_UTILS OFF CACHE INTERNAL "" FORCE) - set(WEBP_BUILD_CWEBP OFF CACHE INTERNAL "" FORCE) - set(WEBP_BUILD_DWEBP OFF CACHE INTERNAL "" FORCE) - set(WEBP_BUILD_GIF2WEBP OFF CACHE INTERNAL "" FORCE) - set(WEBP_BUILD_IMG2WEBP OFF CACHE INTERNAL "" FORCE) - set(WEBP_BUILD_VWEBP OFF CACHE INTERNAL "" FORCE) - set(WEBP_BUILD_WEBPINFO OFF CACHE INTERNAL "" FORCE) - set(WEBP_BUILD_LIBWEBPMUX OFF CACHE INTERNAL "" FORCE) - set(WEBP_BUILD_WEBPMUX OFF CACHE INTERNAL "" FORCE) - set(WEBP_BUILD_EXTRAS OFF CACHE INTERNAL "" FORCE) - set(WEBP_BUILD_WEBP_JS OFF CACHE INTERNAL "" FORCE) - set(WEBP_BUILD_FUZZTEST OFF CACHE INTERNAL "" FORCE) - set(WEBP_USE_THREAD ${SOURCEPP_BUILD_WITH_THREADS} CACHE INTERNAL "" FORCE) - set(WEBP_NEAR_LOSSLESS ON CACHE INTERNAL "" FORCE) - add_sourcepp_remote_library(webp https://github.com/webmproject/libwebp 5003e5609eedc5680b8d838a962cbb9a6e9709ce EXCLUDE_FROM_ALL) -endif() diff --git a/include/sourcepp/String.h b/include/sourcepp/String.h index 044390491..9f92c8f16 100644 --- a/include/sourcepp/String.h +++ b/include/sourcepp/String.h @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include #include @@ -92,4 +94,8 @@ std::from_chars_result toFloat(std::string_view number, std::floating_point auto #endif } +[[nodiscard]] std::vector decodeHex(std::string_view hex); + +[[nodiscard]] std::string encodeHex(std::span hex); + } // namespace sourcepp::string diff --git a/include/sourcepp/crypto/AES.h b/include/sourcepp/crypto/AES.h new file mode 100644 index 000000000..ab34d8717 --- /dev/null +++ b/include/sourcepp/crypto/AES.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include +#include + +#include + +namespace sourcepp::crypto { + +extern const std::array NULL_IV; + +bool decryptAES_CFB(std::span buffer, std::span key, std::span iv = NULL_IV); + +bool encryptAES_CFB(std::span buffer, std::span key, std::span iv = NULL_IV); + +} // namespace sourcepp::crypto diff --git a/include/sourcepp/crypto/Globals.h b/include/sourcepp/crypto/Globals.h new file mode 100644 index 000000000..6829ca13a --- /dev/null +++ b/include/sourcepp/crypto/Globals.h @@ -0,0 +1,11 @@ +#pragma once + +namespace sourcepp::crypto { + +extern const bool LTM_MATH; + +extern const int AES_INDEX; +extern const int SHA256_INDEX; +extern const int YARROW_INDEX; + +} // namespace sourcepp::crypto diff --git a/include/sourcepp/crypto/SHA256.h b/include/sourcepp/crypto/SHA256.h new file mode 100644 index 000000000..b69c4f6e7 --- /dev/null +++ b/include/sourcepp/crypto/SHA256.h @@ -0,0 +1,12 @@ +#pragma once + +#include +#include + +#include + +namespace sourcepp::crypto { + +std::array computeSHA256(std::span buffer); + +} // namespace sourcepp::crypto diff --git a/include/sourcepp/crypto/String.h b/include/sourcepp/crypto/String.h deleted file mode 100644 index 71b09794e..000000000 --- a/include/sourcepp/crypto/String.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -namespace sourcepp::crypto { - -std::vector decodeHexString(std::string_view hex); - -std::string encodeHexString(std::span hex); - -} // namespace sourcepp::crypto diff --git a/include/vpkpp/format/GCF.h b/include/vpkpp/format/GCF.h index bc106e47f..4231f63ec 100644 --- a/include/vpkpp/format/GCF.h +++ b/include/vpkpp/format/GCF.h @@ -57,13 +57,13 @@ class GCF : public PackFileReadOnly { [[nodiscard]] CompressionType getCompressionType() const { static constexpr std::array compressionTypeLUT = { + CompressionType::UNCOMPRESSED, CompressionType::COMPRESSED, CompressionType::COMPRESSED_AND_ENCRYPTED, CompressionType::UNCOMPRESSED, CompressionType::ENCRYPTED, }; - const auto idx = (static_cast(flags) & 7) - 1; - if (idx <= 3) { + if (const uint16_t idx = this->flags & 0x111; idx < compressionTypeLUT.size()) { return compressionTypeLUT[idx]; } return CompressionType::UNCOMPRESSED; diff --git a/src/sourcepp/String.cpp b/src/sourcepp/String.cpp index 2f6ffa635..884c6ff15 100644 --- a/src/sourcepp/String.cpp +++ b/src/sourcepp/String.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include namespace { @@ -249,3 +250,39 @@ std::from_chars_result string::toBool(std::string_view number, bool& out, int ba out = tmp; return result; } + +std::vector string::decodeHex(std::string_view hex) { + static constexpr auto hexChar2Int = [](char c) -> uint8_t { + if (c >= '0' && c <= '9') { + return c - '0'; + } + if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } + if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } + return 0; + }; + + const bool remainder = hex.size() % 2; + + std::vector hexData; + hexData.reserve(hex.size() / 2 + remainder); + if (remainder) { + hexData.push_back(static_cast(hexChar2Int(hex.front()))); + } + for (int i = remainder; i < hex.length(); i += 2) { + hexData.push_back(static_cast(hexChar2Int(hex[i]) << 4 | hexChar2Int(hex[i + 1]))); + } + return hexData; +} + +std::string string::encodeHex(std::span hex) { + std::string hexStr; + hexStr.reserve(hex.size() * 2); + for (const auto byte : hex) { + hexStr.append(std::format("{:02x}", static_cast(byte))); + } + return hexStr; +} diff --git a/src/sourcepp/crypto/AES.cpp b/src/sourcepp/crypto/AES.cpp new file mode 100644 index 000000000..315cda00e --- /dev/null +++ b/src/sourcepp/crypto/AES.cpp @@ -0,0 +1,39 @@ +#include + +#include + +#include + +using namespace sourcepp; + +namespace sourcepp::crypto { + +const std::array NULL_IV{}; + +} // namespace sourcepp::crypto + +bool crypto::decryptAES_CFB(std::span buffer, std::span key, std::span iv) { + if (!LTM_MATH || AES_INDEX < 0 || buffer.empty()) { + return {}; + } + + const std::unique_ptr cfb{new symmetric_CFB, [](symmetric_CFB* c) { cfb_done(c); delete c; }}; + if (cfb_start(AES_INDEX, reinterpret_cast(iv.data()), reinterpret_cast(key.data()), static_cast(key.size()), 0, cfb.get()) != CRYPT_OK) { + return {}; + } + const unsigned long bufferLen = buffer.size(); + return cfb_decrypt(reinterpret_cast(buffer.data()), reinterpret_cast(buffer.data()), bufferLen, cfb.get()) == CRYPT_OK; +} + +bool crypto::encryptAES_CFB(std::span buffer, std::span key, std::span iv) { + if (!LTM_MATH || AES_INDEX < 0 || buffer.empty()) { + return {}; + } + + const std::unique_ptr cfb{new symmetric_CFB, [](symmetric_CFB* c) { cfb_done(c); delete c; }}; + if (cfb_start(AES_INDEX, reinterpret_cast(iv.data()), reinterpret_cast(key.data()), static_cast(key.size()), 0, cfb.get()) != CRYPT_OK) { + return {}; + } + const unsigned long bufferLen = buffer.size(); + return cfb_encrypt(reinterpret_cast(buffer.data()), reinterpret_cast(buffer.data()), bufferLen, cfb.get()) == CRYPT_OK; +} diff --git a/src/sourcepp/crypto/Adler32.cpp b/src/sourcepp/crypto/Adler32.cpp index dfcc5dd5d..e956c9fbf 100644 --- a/src/sourcepp/crypto/Adler32.cpp +++ b/src/sourcepp/crypto/Adler32.cpp @@ -1,105 +1,23 @@ -/* - * Copyright notice: - * - * (C) 1995-2024 Jean-loup Gailly and Mark Adler - * - * This software is provided 'as-is', without any express or implied - * warranty. In no event will the authors be held liable for any damages - * arising from the use of this software. - * - * Permission is granted to anyone to use this software for any purpose, - * including commercial applications, and to alter it and redistribute it - * freely, subject to the following restrictions: - * - * 1. The origin of this software must not be misrepresented; you must not - * claim that you wrote the original software. If you use this software - * in a product, an acknowledgment in the product documentation would be - * appreciated but is not required. - * 2. Altered source versions must be plainly marked as such, and must not be - * misrepresented as being the original software. - * 3. This notice may not be removed or altered from any source distribution. - * - * Jean-loup Gailly Mark Adler - * jloup@gzip.org madler@alumni.caltech.edu - */ - #include -using namespace sourcepp; +#include +#include -constexpr uint32_t BASE = 65521u; /* largest prime smaller than 65536 */ -constexpr std::size_t NMAX = 5552u; +#include -#define DO1(buffer,i) {adler += static_cast((buffer)[i]); sum2 += adler;} -#define DO2(buffer,i) DO1(buffer,i) DO1(buffer,i+1) -#define DO4(buffer,i) DO2(buffer,i) DO2(buffer,i+2) -#define DO8(buffer,i) DO4(buffer,i) DO4(buffer,i+4) -#define DO16(buffer) DO8(buffer,0) DO8(buffer,8) +using namespace sourcepp; uint32_t crypto::computeAdler32(std::span buffer) { - const std::byte* cur = buffer.data(); - uint64_t len = buffer.size(); - - uint32_t adler = 0; - - /* split Adler-32 into component sums */ - uint32_t sum2 = (adler >> 16) & 0xffff; - adler &= 0xffff; - - /* in case user likes doing a byte at a time, keep it fast */ - if (len == 1) { - adler += static_cast(buffer[0]); - if (adler >= BASE) - adler -= BASE; - sum2 += adler; - if (sum2 >= BASE) - sum2 -= BASE; - return adler | (sum2 << 16); + if (!LTM_MATH || buffer.empty()) { + return 0; } - /* initial Adler-32 value (deferred check for len == 1 speed) */ - if (!cur) - return 1L; - - /* in case short lengths are provided, keep it somewhat fast */ - if (len < 16) { - while (len--) { - adler += static_cast(*cur++); - sum2 += adler; - } - if (adler >= BASE) - adler -= BASE; - sum2 %= BASE; /* only added so many BASE's */ - return adler | (sum2 << 16); - } - - /* do length NMAX blocks -- requires just one modulo operation */ - while (len >= NMAX) { - len -= NMAX; - uint32_t n = NMAX / 16; /* NMAX is divisible by 16 */ - do { - DO16(cur) /* 16 sums unrolled */ - cur += 16; - } while (--n); - adler %= BASE; - sum2 %= BASE; - } - - /* do remaining bytes (less than NMAX, still just one modulo) */ - if (len) { /* avoid modulos if none remaining */ - while (len >= 16) { - len -= 16; - DO16(cur) - cur += 16; - } - while (len--) { - adler += static_cast(*cur++); - sum2 += adler; - } - adler %= BASE; - sum2 %= BASE; - } + adler32_state adler32{}; + // do NOT call init! must start zeroed, init changes this (necessary for GCF) + adler32_update(&adler32, reinterpret_cast(buffer.data()), buffer.size()); - /* return recombined sums */ - return adler | (sum2 << 16); + uint32_t final; + adler32_finish(&adler32, &final, sizeof(final)); + BufferStream::swap_endian(&final); + return final; } diff --git a/src/sourcepp/crypto/CRC32.cpp b/src/sourcepp/crypto/CRC32.cpp index 16c99feee..9da199d13 100644 --- a/src/sourcepp/crypto/CRC32.cpp +++ b/src/sourcepp/crypto/CRC32.cpp @@ -1,17 +1,23 @@ #include -#include +#include +#include + +#include using namespace sourcepp; uint32_t crypto::computeCRC32(std::span buffer) { - // Make sure this is right - static_assert(CryptoPP::CRC32::DIGESTSIZE == sizeof(uint32_t)); + if (!LTM_MATH || buffer.empty()) { + return 0; + } - CryptoPP::CRC32 crc32; - crc32.Update(reinterpret_cast(buffer.data()), buffer.size()); + crc32_state crc32; + crc32_init(&crc32); + crc32_update(&crc32, reinterpret_cast(buffer.data()), buffer.size()); uint32_t final; - crc32.Final(reinterpret_cast(&final)); + crc32_finish(&crc32, &final, sizeof(final)); + BufferStream::swap_endian(&final); return final; } diff --git a/src/sourcepp/crypto/Globals.cpp b/src/sourcepp/crypto/Globals.cpp new file mode 100644 index 000000000..2b9e8b089 --- /dev/null +++ b/src/sourcepp/crypto/Globals.cpp @@ -0,0 +1,16 @@ +#include + +#include + +namespace sourcepp::crypto { + +const bool LTM_MATH = [] { + ltc_mp = ltm_desc; + return true; +}(); + +const int AES_INDEX = register_cipher(&aes_desc); +const int SHA256_INDEX = register_hash(&sha256_desc); +const int YARROW_INDEX = register_prng(&yarrow_desc); + +} // namespace sourcepp::crypto diff --git a/src/sourcepp/crypto/MD5.cpp b/src/sourcepp/crypto/MD5.cpp index d813177f8..6726d1ae7 100644 --- a/src/sourcepp/crypto/MD5.cpp +++ b/src/sourcepp/crypto/MD5.cpp @@ -1,18 +1,21 @@ #include -#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1 -#include +#include + +#include using namespace sourcepp; std::array crypto::computeMD5(std::span buffer) { - // Make sure this is right - static_assert(CryptoPP::Weak::MD5::DIGESTSIZE == sizeof(std::array)); + if (!LTM_MATH || buffer.empty()) { + return {}; + } - CryptoPP::Weak::MD5 md5; - md5.Update(reinterpret_cast(buffer.data()), buffer.size()); + hash_state md5; + md5_init(&md5); + md5_process(&md5, reinterpret_cast(buffer.data()), buffer.size()); std::array final{}; - md5.Final(reinterpret_cast(final.data())); + md5_done(&md5, reinterpret_cast(final.data())); return final; } diff --git a/src/sourcepp/crypto/RSA.cpp b/src/sourcepp/crypto/RSA.cpp index 580425390..1f3bf0ca9 100644 --- a/src/sourcepp/crypto/RSA.cpp +++ b/src/sourcepp/crypto/RSA.cpp @@ -1,45 +1,95 @@ #include -#include -#include -#include +#include +#include +#include + +#include + +#include +#include using namespace sourcepp; +namespace { + +constexpr auto SHA256_ENTROPY_SIZE = 64; + +} // namespace + std::pair crypto::computeSHA256KeyPair(uint16_t size) { - CryptoPP::AutoSeededRandomPool rng; + if (!LTM_MATH || SHA256_INDEX < 0 || YARROW_INDEX < 0 || size < 8) { + return {}; + } + size /= 8; + + const std::unique_ptr prng{new prng_state, [](prng_state* p) { yarrow_done(p); delete p; }}; + yarrow_start(prng.get()); + { + std::array entropy{}; + std::random_device rd; + std::uniform_int_distribution<> dist{0, std::numeric_limits::max()}; + for (auto& i : entropy) { + i = static_cast(dist(rd)); + } + yarrow_add_entropy(entropy.data(), entropy.size(), prng.get()); + } + yarrow_ready(prng.get()); - CryptoPP::RSAES_OAEP_SHA256_Decryptor privateKey{rng, size}; - CryptoPP::RSAES_OAEP_SHA256_Encryptor publicKey{privateKey}; + const std::unique_ptr key{new rsa_key, [](rsa_key* k) { rsa_free(k); delete k; }}; + if (rsa_make_key(prng.get(), YARROW_INDEX, size, 65537, key.get()) != CRYPT_OK) { + return {}; + } - std::vector privateKeyData; - CryptoPP::VectorSink privateKeyDataSink{privateKeyData}; - privateKey.AccessMaterial().Save(privateKeyDataSink); - std::string privateKeyStr; - CryptoPP::StringSource privateKeyStringSource{privateKeyData.data(), privateKeyData.size(), true, new CryptoPP::HexEncoder{new CryptoPP::StringSink{privateKeyStr}}}; + std::vector privateKeyData(size * 16); + unsigned long privateKeyLen = privateKeyData.size(); + if (rsa_export(privateKeyData.data(), &privateKeyLen, PK_PRIVATE, key.get()) != CRYPT_OK) { + return {}; + } + privateKeyData.resize(privateKeyLen); - std::vector publicKeyData; - CryptoPP::VectorSink publicKeyDataArraySink{publicKeyData}; - publicKey.AccessMaterial().Save(publicKeyDataArraySink); - std::string publicKeyStr; - CryptoPP::StringSource publicKeyStringSource{publicKeyData.data(), publicKeyData.size(), true, new CryptoPP::HexEncoder{new CryptoPP::StringSink{publicKeyStr}}}; + std::vector publicKeyData(size * 16); + unsigned long publicKeyLen = publicKeyData.size(); + if (rsa_export(publicKeyData.data(), &publicKeyLen, PK_PUBLIC, key.get()) != CRYPT_OK) { + return {}; + } + publicKeyData.resize(publicKeyLen); - return std::make_pair(std::move(privateKeyStr), std::move(publicKeyStr)); + return { + string::encodeHex({reinterpret_cast(privateKeyData.data()), privateKeyData.size()}), + string::encodeHex({reinterpret_cast(publicKeyData.data()), publicKeyData.size()}), + }; } bool crypto::verifySHA256PublicKey(std::span buffer, std::span publicKey, std::span signature) { - CryptoPP::ArraySource publicKeySource{reinterpret_cast(publicKey.data()), publicKey.size(), true}; - const CryptoPP::RSASS::Verifier verifier{publicKeySource}; - return verifier.VerifyMessage(reinterpret_cast(buffer.data()), buffer.size(), reinterpret_cast(signature.data()), signature.size()); + if (!LTM_MATH || SHA256_INDEX < 0 || buffer.empty()) { + return false; + } + + const std::unique_ptr key{new rsa_key, [](rsa_key* k) { rsa_free(k); delete k; }}; + if (rsa_import(reinterpret_cast(publicKey.data()), publicKey.size(), key.get()) != CRYPT_OK) { + return false; + } + + const auto sha256 = computeSHA256(buffer); + ltc_rsa_op_parameters params{ .params = { .hash_idx = SHA256_INDEX }, .padding = LTC_PKCS_1_V1_5 }; + int stat = 0; + return rsa_verify_hash_v2(reinterpret_cast(signature.data()), signature.size(), reinterpret_cast(sha256.data()), sha256.size(), ¶ms, &stat, key.get()) == CRYPT_OK && stat; } std::vector crypto::signDataWithSHA256PrivateKey(std::span buffer, std::span privateKey) { - CryptoPP::AutoSeededRandomPool rng; + if (!LTM_MATH || SHA256_INDEX < 0 || buffer.empty()) { + return {}; + } - CryptoPP::ArraySource privateKeySource{reinterpret_cast(privateKey.data()), privateKey.size(), true}; - const CryptoPP::RSASS::Signer signer{privateKeySource}; + const std::unique_ptr key{new rsa_key, [](rsa_key* k) { rsa_free(k); delete k; }}; + if (rsa_import(reinterpret_cast(privateKey.data()), privateKey.size(), key.get()) != CRYPT_OK) { + return {}; + } - std::vector out; - CryptoPP::ArraySource signData{reinterpret_cast(buffer.data()), buffer.size(), true, new CryptoPP::SignerFilter{rng, signer, new CryptoPP::VectorSink{reinterpret_cast&>(out)}}}; - return out; + const auto sha256 = computeSHA256(buffer); + unsigned long signatureLen = rsa_get_size(key.get()); + std::vector signature(signatureLen); + ltc_rsa_op_parameters params{ .params = { .hash_idx = SHA256_INDEX }, .padding = LTC_PKCS_1_V1_5 }; + return rsa_sign_hash_v2(reinterpret_cast(sha256.data()), sha256.size(), reinterpret_cast(signature.data()), &signatureLen, ¶ms, key.get()) == CRYPT_OK ? signature : std::vector{}; } diff --git a/src/sourcepp/crypto/SHA256.cpp b/src/sourcepp/crypto/SHA256.cpp new file mode 100644 index 000000000..253859254 --- /dev/null +++ b/src/sourcepp/crypto/SHA256.cpp @@ -0,0 +1,21 @@ +#include + +#include + +#include + +using namespace sourcepp; + +std::array crypto::computeSHA256(std::span buffer) { + if (!LTM_MATH || SHA256_INDEX < 0 || buffer.empty()) { + return {}; + } + + hash_state sha256; + sha256_init(&sha256); + sha256_process(&sha256, reinterpret_cast(buffer.data()), buffer.size()); + + std::array final{}; + sha256_done(&sha256, reinterpret_cast(final.data())); + return final; +} diff --git a/src/sourcepp/crypto/String.cpp b/src/sourcepp/crypto/String.cpp deleted file mode 100644 index 4de73c3f2..000000000 --- a/src/sourcepp/crypto/String.cpp +++ /dev/null @@ -1,28 +0,0 @@ -#include - -#include -#include - -#include - -using namespace sourcepp; - -std::vector crypto::decodeHexString(std::string_view hex) { - std::string hexBin; - CryptoPP::StringSource hexSource{hex.data(), true, new CryptoPP::HexDecoder{new CryptoPP::StringSink{hexBin}}}; - - std::vector out; - for (char c : hexBin) { - out.push_back(static_cast(c)); - } - return out; -} - -std::string crypto::encodeHexString(std::span hex) { - std::ostringstream oss; - oss << std::hex << std::setfill('0'); - for (auto byte : hex) { - oss << std::setw(2) << static_cast(std::to_integer(byte)); - } - return oss.str(); -} diff --git a/src/sourcepp/crypto/_crypto.cmake b/src/sourcepp/crypto/_crypto.cmake index eab77edff..1b6714cae 100644 --- a/src/sourcepp/crypto/_crypto.cmake +++ b/src/sourcepp/crypto/_crypto.cmake @@ -1,18 +1,22 @@ list(APPEND ${PROJECT_NAME}_crypto_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/include/sourcepp/crypto/Adler32.h" + "${CMAKE_CURRENT_SOURCE_DIR}/include/sourcepp/crypto/AES.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/sourcepp/crypto/CRC32.h" + "${CMAKE_CURRENT_SOURCE_DIR}/include/sourcepp/crypto/Globals.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/sourcepp/crypto/MD5.h" "${CMAKE_CURRENT_SOURCE_DIR}/include/sourcepp/crypto/RSA.h" - "${CMAKE_CURRENT_SOURCE_DIR}/include/sourcepp/crypto/String.h") + "${CMAKE_CURRENT_SOURCE_DIR}/include/sourcepp/crypto/SHA256.h") add_library(${PROJECT_NAME}_crypto STATIC ${${PROJECT_NAME}_crypto_HEADERS} "${CMAKE_CURRENT_LIST_DIR}/Adler32.cpp" + "${CMAKE_CURRENT_LIST_DIR}/AES.cpp" "${CMAKE_CURRENT_LIST_DIR}/CRC32.cpp" + "${CMAKE_CURRENT_LIST_DIR}/Globals.cpp" "${CMAKE_CURRENT_LIST_DIR}/MD5.cpp" "${CMAKE_CURRENT_LIST_DIR}/RSA.cpp" - "${CMAKE_CURRENT_LIST_DIR}/String.cpp") + "${CMAKE_CURRENT_LIST_DIR}/SHA256.cpp") target_precompile_headers(${PROJECT_NAME}_crypto PUBLIC ${${PROJECT_NAME}_crypto_HEADERS}) -target_link_libraries(${PROJECT_NAME}_crypto PUBLIC ${PROJECT_NAME} cryptopp::cryptopp) +target_link_libraries(${PROJECT_NAME}_crypto PUBLIC ${PROJECT_NAME} libtomcrypt libtommath) diff --git a/src/vpkpp/format/FGP.cpp b/src/vpkpp/format/FGP.cpp index 76dd2f7d8..41e7a7d20 100644 --- a/src/vpkpp/format/FGP.cpp +++ b/src/vpkpp/format/FGP.cpp @@ -94,7 +94,7 @@ std::unique_ptr FGP::open(const std::string& path, const EntryCallback if (crackedHashes.contains(entry.crc32)) { entryPath = crackedHashes[entry.crc32]; } else { - entryPath = fgp->cleanEntryPath(FGP_HASHED_FILEPATH_PREFIX.data() + crypto::encodeHexString({reinterpret_cast(&entry.crc32), sizeof(entry.crc32)})); + entryPath = fgp->cleanEntryPath(FGP_HASHED_FILEPATH_PREFIX.data() + string::encodeHex({reinterpret_cast(&entry.crc32), sizeof(entry.crc32)})); } if (loadingScreen > 0 && i == loadingScreen) { fgp->loadingScreenPath = entryPath; diff --git a/src/vpkpp/format/GCF.cpp b/src/vpkpp/format/GCF.cpp index 3cbbdcb7a..22e77cd60 100644 --- a/src/vpkpp/format/GCF.cpp +++ b/src/vpkpp/format/GCF.cpp @@ -4,12 +4,10 @@ #include #include -#include -#include -#include #include #include #include +#include #include using namespace sourcepp; @@ -286,7 +284,6 @@ std::optional> GCF::readEntry(const std::string& path_) c filemode = block.getCompressionType(); } if (needs_decrypt) { - CryptoPP::byte iv[16] = {}; switch (filemode) { using enum Block::CompressionType; case UNCOMPRESSED: @@ -300,10 +297,8 @@ std::optional> GCF::readEntry(const std::string& path_) c auto remaining_encrypted = filedata.size(); auto offset = 0; while (remaining_encrypted) { - CryptoPP::CFB_Mode::Decryption d; - d.SetKeyWithIV(reinterpret_cast(this->decryption_key.data()), 16, iv); auto current_batch = std::min(remaining_encrypted, static_cast(0x8000)); - d.ProcessData(reinterpret_cast(filedata.data() + offset), reinterpret_cast(filedata.data() + offset), current_batch); + crypto::decryptAES_CFB({filedata.data() + offset, current_batch}, this->decryption_key); remaining_encrypted -= current_batch; offset += static_cast(current_batch); } @@ -328,9 +323,7 @@ std::optional> GCF::readEntry(const std::string& path_) c if (buffer.size() % 10 != 0) { buffer.resize(buffer.size() + (0x10 - (buffer.size() % 10)), {}); } - CryptoPP::CFB_Mode::Decryption d; - d.SetKeyWithIV(reinterpret_cast(this->decryption_key.data()), 16, iv); - d.ProcessData(reinterpret_cast(buffer.data()), reinterpret_cast(buffer.data()), buffer.size()); + crypto::decryptAES_CFB(buffer, this->decryption_key); buffer.resize(real_size); auto start = allocate_block(processed_data, decompressed_size); diff --git a/src/vpkpp/format/VPK.cpp b/src/vpkpp/format/VPK.cpp index 681371da6..970435a7f 100644 --- a/src/vpkpp/format/VPK.cpp +++ b/src/vpkpp/format/VPK.cpp @@ -4,16 +4,14 @@ #include #include -#define CRYPTOPP_ENABLE_NAMESPACE_WEAK 1 -#include #include #include #include #include #include -#include #include #include +#include #include #ifdef VPKPP_SUPPORT_VPK_V54 @@ -315,9 +313,7 @@ bool VPK::verifyPackFileSignature() const { if (dirFileBuffer.size() <= signatureSectionSize) { return false; } - for (int i = 0; i < signatureSectionSize; i++) { - dirFileBuffer.pop_back(); - } + dirFileBuffer.resize(dirFileBuffer.size() - signatureSectionSize); return crypto::verifySHA256PublicKey(dirFileBuffer, this->footer2.publicKey, this->footer2.signature); } @@ -723,30 +719,41 @@ bool VPK::bake(const std::string& outputDir_, BakeOptions options, const EntryCa this->header2.signatureSectionSize = 0; // Calculate Footer2 - CryptoPP::Weak::MD5 wholeFileChecksumMD5; + hash_state wholeFileChecksumMD5; + md5_init(&wholeFileChecksumMD5); { // Only the tree is updated in the file right now - wholeFileChecksumMD5.Update(reinterpret_cast(&this->header1), sizeof(Header1)); - wholeFileChecksumMD5.Update(reinterpret_cast(&this->header2), sizeof(Header2)); + md5_process(&wholeFileChecksumMD5, reinterpret_cast(&this->header1), sizeof(this->header1)); + md5_process(&wholeFileChecksumMD5, reinterpret_cast(&this->header2), sizeof(this->header2)); } { outDir.seek_in(sizeof(Header1) + sizeof(Header2)); - std::vector treeData = outDir.read_bytes(this->header1.treeSize); - wholeFileChecksumMD5.Update(reinterpret_cast(treeData.data()), treeData.size()); - this->footer2.treeChecksum = crypto::computeMD5(treeData); + if (this->header1.treeSize > 0) { + std::vector treeData = outDir.read_bytes(this->header1.treeSize); + md5_process(&wholeFileChecksumMD5, reinterpret_cast(treeData.data()), treeData.size()); + this->footer2.treeChecksum = crypto::computeMD5(treeData); + } else { + this->footer2.treeChecksum = {}; + } } if (!dirVPKEntryData.empty()) { - wholeFileChecksumMD5.Update(reinterpret_cast(dirVPKEntryData.data()), dirVPKEntryData.size()); + md5_process(&wholeFileChecksumMD5, reinterpret_cast(dirVPKEntryData.data()), dirVPKEntryData.size()); } { - wholeFileChecksumMD5.Update(reinterpret_cast(this->md5Entries.data()), this->md5Entries.size() * sizeof(MD5Entry)); - CryptoPP::Weak::MD5 md5EntriesChecksumMD5; - md5EntriesChecksumMD5.Update(reinterpret_cast(this->md5Entries.data()), this->md5Entries.size() * sizeof(MD5Entry)); - md5EntriesChecksumMD5.Final(reinterpret_cast(this->footer2.md5EntriesChecksum.data())); + if (!this->md5Entries.empty()) { + md5_process(&wholeFileChecksumMD5, reinterpret_cast(this->md5Entries.data()), this->md5Entries.size() * sizeof(MD5Entry)); + this->footer2.md5EntriesChecksum = crypto::computeMD5({reinterpret_cast(this->md5Entries.data()), this->md5Entries.size() * sizeof(MD5Entry)}); + } else { + this->footer2.md5EntriesChecksum = {}; + } + } + if (!this->footer2.treeChecksum.empty()) { + md5_process(&wholeFileChecksumMD5, reinterpret_cast(this->footer2.treeChecksum.data()), this->footer2.treeChecksum.size()); + } + if (!this->footer2.md5EntriesChecksum.empty()) { + md5_process(&wholeFileChecksumMD5, reinterpret_cast(this->footer2.md5EntriesChecksum.data()), this->footer2.md5EntriesChecksum.size()); } - wholeFileChecksumMD5.Update(reinterpret_cast(this->footer2.treeChecksum.data()), this->footer2.treeChecksum.size()); - wholeFileChecksumMD5.Update(reinterpret_cast(this->footer2.md5EntriesChecksum.data()), this->footer2.md5EntriesChecksum.size()); - wholeFileChecksumMD5.Final(reinterpret_cast(this->footer2.wholeFileChecksum.data())); + md5_done(&wholeFileChecksumMD5, reinterpret_cast(this->footer2.wholeFileChecksum.data())); // We can't recalculate the signature without the private key this->footer2.publicKey.clear(); @@ -848,7 +855,7 @@ bool VPK::sign(const std::string& filename_) { return false; } - return this->sign(crypto::decodeHexString(privateKeyHex), crypto::decodeHexString(publicKeyHex)); + return this->sign(string::decodeHex(privateKeyHex), string::decodeHex(publicKeyHex)); } bool VPK::sign(const std::vector& privateKey, const std::vector& publicKey) {