diff --git a/components/core/.clang-format b/components/core/.clang-format index 35934f594..99a0d74ce 100644 --- a/components/core/.clang-format +++ b/components/core/.clang-format @@ -75,7 +75,7 @@ IncludeCategories: # Library headers. Update when adding new libraries. # NOTE: clang-format retains leading white-space on a line in violation of the YAML spec. - Regex: "<(absl|antlr4|archive|boost|bsoncxx|catch2|curl|date|fmt|json|log_surgeon|mariadb\ -|mongocxx|msgpack|outcome|regex_utils|simdjson|spdlog|sqlite3|string_utils|yaml-cpp|zstd)" +|mongocxx|msgpack|openssl|outcome|regex_utils|simdjson|spdlog|sqlite3|string_utils|yaml-cpp|zstd)" Priority: 3 # C system headers - Regex: "^<.+\\.h>" diff --git a/components/core/CMakeLists.txt b/components/core/CMakeLists.txt index 32dfa4d99..cdc00ede3 100644 --- a/components/core/CMakeLists.txt +++ b/components/core/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.5.1) +cmake_minimum_required(VERSION 3.16.3) project(CLP LANGUAGES CXX C) if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) @@ -146,6 +146,14 @@ else() message(FATAL_ERROR "Could not find ${CLP_LIBS_STRING} libraries for CURL") endif() +# Find OpenSSL +find_package(OpenSSL REQUIRED) +if (OPENSSL_FOUND) + message(STATUS "Found OpenSSL (${OPENSSL_VERSION})") +else () + message(FATAL_ERROR "OpenSSL not found") +endif () + # Add log surgeon add_subdirectory(submodules/log-surgeon EXCLUDE_FROM_ALL) @@ -346,6 +354,8 @@ set(SOURCE_FILES_unitTest src/clp/GlobalSQLiteMetadataDB.hpp src/clp/Grep.cpp src/clp/Grep.hpp + src/clp/hash_utils.cpp + src/clp/hash_utils.hpp src/clp/ir/constants.hpp src/clp/ir/EncodedTextAst.hpp src/clp/ir/LogEvent.hpp @@ -465,6 +475,7 @@ set(SOURCE_FILES_unitTest tests/test-encoding_methods.cpp tests/test-ffi_SchemaTree.cpp tests/test-Grep.cpp + tests/test-hash_utils.cpp tests/test-ir_encoding_methods.cpp tests/test-ir_parsing.cpp tests/test-kql.cpp @@ -500,6 +511,7 @@ target_link_libraries(unitTest LibArchive::LibArchive MariaDBClient::MariaDBClient spdlog::spdlog + OpenSSL::Crypto ${sqlite_LIBRARY_DEPENDENCIES} ${STD_FS_LIBS} clp::regex_utils diff --git a/components/core/cmake/Modules/FindOpenSSL.cmake b/components/core/cmake/Modules/FindOpenSSL.cmake deleted file mode 100644 index 79c3b044b..000000000 --- a/components/core/cmake/Modules/FindOpenSSL.cmake +++ /dev/null @@ -1,534 +0,0 @@ -# NOTE: This is FindOpenSSL.cmake from cmake-3.16.0-rc3. It fixes issues with missing dependencies for the OpenSSL static library. -# This file should be deleted when cmake-3.16 is released. - -# Distributed under the OSI-approved BSD 3-Clause License. See accompanying -# file Copyright.txt or https://cmake.org/licensing for details. - -#[=======================================================================[.rst: -FindOpenSSL ------------ - -Find the OpenSSL encryption library. - -Optional COMPONENTS -^^^^^^^^^^^^^^^^^^^ - -This module supports two optional COMPONENTS: ``Crypto`` and ``SSL``. Both -components have associated imported targets, as described below. - -Imported Targets -^^^^^^^^^^^^^^^^ - -This module defines the following :prop_tgt:`IMPORTED` targets: - -``OpenSSL::SSL`` - The OpenSSL ``ssl`` library, if found. -``OpenSSL::Crypto`` - The OpenSSL ``crypto`` library, if found. - -Result Variables -^^^^^^^^^^^^^^^^ - -This module will set the following variables in your project: - -``OPENSSL_FOUND`` - System has the OpenSSL library. If no components are requested it only - requires the crypto library. -``OPENSSL_INCLUDE_DIR`` - The OpenSSL include directory. -``OPENSSL_CRYPTO_LIBRARY`` - The OpenSSL crypto library. -``OPENSSL_CRYPTO_LIBRARIES`` - The OpenSSL crypto library and its dependencies. -``OPENSSL_SSL_LIBRARY`` - The OpenSSL SSL library. -``OPENSSL_SSL_LIBRARIES`` - The OpenSSL SSL library and its dependencies. -``OPENSSL_LIBRARIES`` - All OpenSSL libraries and their dependencies. -``OPENSSL_VERSION`` - This is set to ``$major.$minor.$revision$patch`` (e.g. ``0.9.8s``). - -Hints -^^^^^ - -Set ``OPENSSL_ROOT_DIR`` to the root directory of an OpenSSL installation. -Set ``OPENSSL_USE_STATIC_LIBS`` to ``TRUE`` to look for static libraries. -Set ``OPENSSL_MSVC_STATIC_RT`` set ``TRUE`` to choose the MT version of the lib. -#]=======================================================================] - -macro(_OpenSSL_test_and_find_dependencies ssl_library crypto_library) - if((CMAKE_SYSTEM_NAME STREQUAL "Linux") AND - (("${ssl_library}" MATCHES "\\${CMAKE_STATIC_LIBRARY_SUFFIX}$") OR - ("${crypto_library}" MATCHES "\\${CMAKE_STATIC_LIBRARY_SUFFIX}$"))) - set(_OpenSSL_has_dependencies TRUE) - find_package(Threads) - else() - set(_OpenSSL_has_dependencies FALSE) - endif() -endmacro() - -function(_OpenSSL_add_dependencies libraries_var library) - if(CMAKE_THREAD_LIBS_INIT) - list(APPEND ${libraries_var} ${CMAKE_THREAD_LIBS_INIT}) - endif() - list(APPEND ${libraries_var} ${CMAKE_DL_LIBS}) - set(${libraries_var} ${${libraries_var}} PARENT_SCOPE) -endfunction() - -function(_OpenSSL_target_add_dependencies target) - if(_OpenSSL_has_dependencies) - set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES Threads::Threads ) - set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${CMAKE_DL_LIBS} ) - endif() -endfunction() - -if (UNIX) - find_package(PkgConfig QUIET) - pkg_check_modules(_OPENSSL QUIET openssl) -endif () - -# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES -if(OPENSSL_USE_STATIC_LIBS) - set(_openssl_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES}) - if(WIN32) - set(CMAKE_FIND_LIBRARY_SUFFIXES .lib .a ${CMAKE_FIND_LIBRARY_SUFFIXES}) - else() - set(CMAKE_FIND_LIBRARY_SUFFIXES .a ) - endif() -endif() - -if (WIN32) - # http://www.slproweb.com/products/Win32OpenSSL.html - set(_OPENSSL_ROOT_HINTS - ${OPENSSL_ROOT_DIR} - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (32-bit)_is1;Inno Setup: App Path]" - "[HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\OpenSSL (64-bit)_is1;Inno Setup: App Path]" - ENV OPENSSL_ROOT_DIR - ) - file(TO_CMAKE_PATH "$ENV{PROGRAMFILES}" _programfiles) - set(_OPENSSL_ROOT_PATHS - "${_programfiles}/OpenSSL" - "${_programfiles}/OpenSSL-Win32" - "${_programfiles}/OpenSSL-Win64" - "C:/OpenSSL/" - "C:/OpenSSL-Win32/" - "C:/OpenSSL-Win64/" - ) - unset(_programfiles) -else () - set(_OPENSSL_ROOT_HINTS - ${OPENSSL_ROOT_DIR} - ENV OPENSSL_ROOT_DIR - ) -endif () - -set(_OPENSSL_ROOT_HINTS_AND_PATHS - HINTS ${_OPENSSL_ROOT_HINTS} - PATHS ${_OPENSSL_ROOT_PATHS} - ) - -find_path(OPENSSL_INCLUDE_DIR - NAMES - openssl/ssl.h - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - HINTS - ${_OPENSSL_INCLUDEDIR} - PATH_SUFFIXES - include -) - -if(WIN32 AND NOT CYGWIN) - if(MSVC) - # /MD and /MDd are the standard values - if someone wants to use - # others, the libnames have to change here too - # use also ssl and ssleay32 in debug as fallback for openssl < 0.9.8b - # enable OPENSSL_MSVC_STATIC_RT to get the libs build /MT (Multithreaded no-DLL) - # In Visual C++ naming convention each of these four kinds of Windows libraries has it's standard suffix: - # * MD for dynamic-release - # * MDd for dynamic-debug - # * MT for static-release - # * MTd for static-debug - - # Implementation details: - # We are using the libraries located in the VC subdir instead of the parent directory even though : - # libeay32MD.lib is identical to ../libeay32.lib, and - # ssleay32MD.lib is identical to ../ssleay32.lib - # enable OPENSSL_USE_STATIC_LIBS to use the static libs located in lib/VC/static - - if (OPENSSL_MSVC_STATIC_RT) - set(_OPENSSL_MSVC_RT_MODE "MT") - else () - set(_OPENSSL_MSVC_RT_MODE "MD") - endif () - - # Since OpenSSL 1.1, lib names are like libcrypto32MTd.lib and libssl32MTd.lib - if( "${CMAKE_SIZEOF_VOID_P}" STREQUAL "8" ) - set(_OPENSSL_MSVC_ARCH_SUFFIX "64") - else() - set(_OPENSSL_MSVC_ARCH_SUFFIX "32") - endif() - - if(OPENSSL_USE_STATIC_LIBS) - set(_OPENSSL_PATH_SUFFIXES - "lib/VC/static" - "VC/static" - "lib" - ) - else() - set(_OPENSSL_PATH_SUFFIXES - "lib/VC" - "VC" - "lib" - ) - endif () - - find_library(LIB_EAY_DEBUG - NAMES - libcrypto${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d - libcrypto${_OPENSSL_MSVC_RT_MODE}d - libcryptod - libeay32${_OPENSSL_MSVC_RT_MODE}d - libeay32d - cryptod - NAMES_PER_DIR - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - PATH_SUFFIXES - ${_OPENSSL_PATH_SUFFIXES} - ) - - find_library(LIB_EAY_RELEASE - NAMES - libcrypto${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE} - libcrypto${_OPENSSL_MSVC_RT_MODE} - libcrypto - libeay32${_OPENSSL_MSVC_RT_MODE} - libeay32 - crypto - NAMES_PER_DIR - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - PATH_SUFFIXES - ${_OPENSSL_PATH_SUFFIXES} - ) - - find_library(SSL_EAY_DEBUG - NAMES - libssl${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE}d - libssl${_OPENSSL_MSVC_RT_MODE}d - libssld - ssleay32${_OPENSSL_MSVC_RT_MODE}d - ssleay32d - ssld - NAMES_PER_DIR - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - PATH_SUFFIXES - ${_OPENSSL_PATH_SUFFIXES} - ) - - find_library(SSL_EAY_RELEASE - NAMES - libssl${_OPENSSL_MSVC_ARCH_SUFFIX}${_OPENSSL_MSVC_RT_MODE} - libssl${_OPENSSL_MSVC_RT_MODE} - libssl - ssleay32${_OPENSSL_MSVC_RT_MODE} - ssleay32 - ssl - NAMES_PER_DIR - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - PATH_SUFFIXES - ${_OPENSSL_PATH_SUFFIXES} - ) - - set(LIB_EAY_LIBRARY_DEBUG "${LIB_EAY_DEBUG}") - set(LIB_EAY_LIBRARY_RELEASE "${LIB_EAY_RELEASE}") - set(SSL_EAY_LIBRARY_DEBUG "${SSL_EAY_DEBUG}") - set(SSL_EAY_LIBRARY_RELEASE "${SSL_EAY_RELEASE}") - - include(SelectLibraryConfigurations) - select_library_configurations(LIB_EAY) - select_library_configurations(SSL_EAY) - - mark_as_advanced(LIB_EAY_LIBRARY_DEBUG LIB_EAY_LIBRARY_RELEASE - SSL_EAY_LIBRARY_DEBUG SSL_EAY_LIBRARY_RELEASE) - set(OPENSSL_SSL_LIBRARY ${SSL_EAY_LIBRARY} ) - set(OPENSSL_CRYPTO_LIBRARY ${LIB_EAY_LIBRARY} ) - elseif(MINGW) - # same player, for MinGW - set(LIB_EAY_NAMES crypto libeay32) - set(SSL_EAY_NAMES ssl ssleay32) - find_library(LIB_EAY - NAMES - ${LIB_EAY_NAMES} - NAMES_PER_DIR - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - PATH_SUFFIXES - "lib/MinGW" - "lib" - ) - - find_library(SSL_EAY - NAMES - ${SSL_EAY_NAMES} - NAMES_PER_DIR - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - PATH_SUFFIXES - "lib/MinGW" - "lib" - ) - - mark_as_advanced(SSL_EAY LIB_EAY) - set(OPENSSL_SSL_LIBRARY ${SSL_EAY} ) - set(OPENSSL_CRYPTO_LIBRARY ${LIB_EAY} ) - unset(LIB_EAY_NAMES) - unset(SSL_EAY_NAMES) - else() - # Not sure what to pick for -say- intel, let's use the toplevel ones and hope someone report issues: - find_library(LIB_EAY - NAMES - libcrypto - libeay32 - NAMES_PER_DIR - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - HINTS - ${_OPENSSL_LIBDIR} - PATH_SUFFIXES - lib - ) - - find_library(SSL_EAY - NAMES - libssl - ssleay32 - NAMES_PER_DIR - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - HINTS - ${_OPENSSL_LIBDIR} - PATH_SUFFIXES - lib - ) - - mark_as_advanced(SSL_EAY LIB_EAY) - set(OPENSSL_SSL_LIBRARY ${SSL_EAY} ) - set(OPENSSL_CRYPTO_LIBRARY ${LIB_EAY} ) - endif() -else() - - find_library(OPENSSL_SSL_LIBRARY - NAMES - ssl - ssleay32 - ssleay32MD - NAMES_PER_DIR - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - HINTS - ${_OPENSSL_LIBDIR} - PATH_SUFFIXES - lib - ) - - find_library(OPENSSL_CRYPTO_LIBRARY - NAMES - crypto - NAMES_PER_DIR - ${_OPENSSL_ROOT_HINTS_AND_PATHS} - HINTS - ${_OPENSSL_LIBDIR} - PATH_SUFFIXES - lib - ) - - mark_as_advanced(OPENSSL_CRYPTO_LIBRARY OPENSSL_SSL_LIBRARY) - -endif() - -# compat defines -set(OPENSSL_SSL_LIBRARIES ${OPENSSL_SSL_LIBRARY}) -set(OPENSSL_CRYPTO_LIBRARIES ${OPENSSL_CRYPTO_LIBRARY}) -_OpenSSL_test_and_find_dependencies("${OPENSSL_SSL_LIBRARY}" "${OPENSSL_CRYPTO_LIBRARY}") -if(_OpenSSL_has_dependencies) - _OpenSSL_add_dependencies( OPENSSL_SSL_LIBRARIES "${OPENSSL_SSL_LIBRARY}" ) - _OpenSSL_add_dependencies( OPENSSL_CRYPTO_LIBRARIES "${OPENSSL_CRYPTO_LIBRARY}" ) -endif() - -function(from_hex HEX DEC) - string(TOUPPER "${HEX}" HEX) - set(_res 0) - string(LENGTH "${HEX}" _strlen) - - while (_strlen GREATER 0) - math(EXPR _res "${_res} * 16") - string(SUBSTRING "${HEX}" 0 1 NIBBLE) - string(SUBSTRING "${HEX}" 1 -1 HEX) - if (NIBBLE STREQUAL "A") - math(EXPR _res "${_res} + 10") - elseif (NIBBLE STREQUAL "B") - math(EXPR _res "${_res} + 11") - elseif (NIBBLE STREQUAL "C") - math(EXPR _res "${_res} + 12") - elseif (NIBBLE STREQUAL "D") - math(EXPR _res "${_res} + 13") - elseif (NIBBLE STREQUAL "E") - math(EXPR _res "${_res} + 14") - elseif (NIBBLE STREQUAL "F") - math(EXPR _res "${_res} + 15") - else() - math(EXPR _res "${_res} + ${NIBBLE}") - endif() - - string(LENGTH "${HEX}" _strlen) - endwhile() - - set(${DEC} ${_res} PARENT_SCOPE) -endfunction() - -if(OPENSSL_INCLUDE_DIR AND EXISTS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h") - file(STRINGS "${OPENSSL_INCLUDE_DIR}/openssl/opensslv.h" openssl_version_str - REGEX "^#[\t ]*define[\t ]+OPENSSL_VERSION_NUMBER[\t ]+0x([0-9a-fA-F])+.*") - - if(openssl_version_str) - # The version number is encoded as 0xMNNFFPPS: major minor fix patch status - # The status gives if this is a developer or prerelease and is ignored here. - # Major, minor, and fix directly translate into the version numbers shown in - # the string. The patch field translates to the single character suffix that - # indicates the bug fix state, which 00 -> nothing, 01 -> a, 02 -> b and so - # on. - - string(REGEX REPLACE "^.*OPENSSL_VERSION_NUMBER[\t ]+0x([0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F][0-9a-fA-F])([0-9a-fA-F]).*$" - "\\1;\\2;\\3;\\4;\\5" OPENSSL_VERSION_LIST "${openssl_version_str}") - list(GET OPENSSL_VERSION_LIST 0 OPENSSL_VERSION_MAJOR) - list(GET OPENSSL_VERSION_LIST 1 OPENSSL_VERSION_MINOR) - from_hex("${OPENSSL_VERSION_MINOR}" OPENSSL_VERSION_MINOR) - list(GET OPENSSL_VERSION_LIST 2 OPENSSL_VERSION_FIX) - from_hex("${OPENSSL_VERSION_FIX}" OPENSSL_VERSION_FIX) - list(GET OPENSSL_VERSION_LIST 3 OPENSSL_VERSION_PATCH) - - if (NOT OPENSSL_VERSION_PATCH STREQUAL "00") - from_hex("${OPENSSL_VERSION_PATCH}" _tmp) - # 96 is the ASCII code of 'a' minus 1 - math(EXPR OPENSSL_VERSION_PATCH_ASCII "${_tmp} + 96") - unset(_tmp) - # Once anyone knows how OpenSSL would call the patch versions beyond 'z' - # this should be updated to handle that, too. This has not happened yet - # so it is simply ignored here for now. - string(ASCII "${OPENSSL_VERSION_PATCH_ASCII}" OPENSSL_VERSION_PATCH_STRING) - endif () - - set(OPENSSL_VERSION "${OPENSSL_VERSION_MAJOR}.${OPENSSL_VERSION_MINOR}.${OPENSSL_VERSION_FIX}${OPENSSL_VERSION_PATCH_STRING}") - endif () -endif () - -set(OPENSSL_LIBRARIES ${OPENSSL_SSL_LIBRARIES} ${OPENSSL_CRYPTO_LIBRARIES} ) -list(REMOVE_DUPLICATES OPENSSL_LIBRARIES) - -foreach(_comp IN LISTS OpenSSL_FIND_COMPONENTS) - if(_comp STREQUAL "Crypto") - if(EXISTS "${OPENSSL_INCLUDE_DIR}" AND - (EXISTS "${OPENSSL_CRYPTO_LIBRARY}" OR - EXISTS "${LIB_EAY_LIBRARY_DEBUG}" OR - EXISTS "${LIB_EAY_LIBRARY_RELEASE}") - ) - set(OpenSSL_${_comp}_FOUND TRUE) - else() - set(OpenSSL_${_comp}_FOUND FALSE) - endif() - elseif(_comp STREQUAL "SSL") - if(EXISTS "${OPENSSL_INCLUDE_DIR}" AND - (EXISTS "${OPENSSL_SSL_LIBRARY}" OR - EXISTS "${SSL_EAY_LIBRARY_DEBUG}" OR - EXISTS "${SSL_EAY_LIBRARY_RELEASE}") - ) - set(OpenSSL_${_comp}_FOUND TRUE) - else() - set(OpenSSL_${_comp}_FOUND FALSE) - endif() - else() - message(WARNING "${_comp} is not a valid OpenSSL component") - set(OpenSSL_${_comp}_FOUND FALSE) - endif() -endforeach() -unset(_comp) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(OpenSSL - REQUIRED_VARS - OPENSSL_CRYPTO_LIBRARY - OPENSSL_INCLUDE_DIR - VERSION_VAR - OPENSSL_VERSION - HANDLE_COMPONENTS - FAIL_MESSAGE - "Could NOT find OpenSSL, try to set the path to OpenSSL root folder in the system variable OPENSSL_ROOT_DIR" -) - -mark_as_advanced(OPENSSL_INCLUDE_DIR OPENSSL_LIBRARIES) - -if(OPENSSL_FOUND) - if(NOT TARGET OpenSSL::Crypto AND - (EXISTS "${OPENSSL_CRYPTO_LIBRARY}" OR - EXISTS "${LIB_EAY_LIBRARY_DEBUG}" OR - EXISTS "${LIB_EAY_LIBRARY_RELEASE}") - ) - add_library(OpenSSL::Crypto UNKNOWN IMPORTED) - set_target_properties(OpenSSL::Crypto PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${OPENSSL_INCLUDE_DIR}") - if(EXISTS "${OPENSSL_CRYPTO_LIBRARY}") - set_target_properties(OpenSSL::Crypto PROPERTIES - IMPORTED_LINK_INTERFACE_LANGUAGES "C" - IMPORTED_LOCATION "${OPENSSL_CRYPTO_LIBRARY}") - endif() - if(EXISTS "${LIB_EAY_LIBRARY_RELEASE}") - set_property(TARGET OpenSSL::Crypto APPEND PROPERTY - IMPORTED_CONFIGURATIONS RELEASE) - set_target_properties(OpenSSL::Crypto PROPERTIES - IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" - IMPORTED_LOCATION_RELEASE "${LIB_EAY_LIBRARY_RELEASE}") - endif() - if(EXISTS "${LIB_EAY_LIBRARY_DEBUG}") - set_property(TARGET OpenSSL::Crypto APPEND PROPERTY - IMPORTED_CONFIGURATIONS DEBUG) - set_target_properties(OpenSSL::Crypto PROPERTIES - IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C" - IMPORTED_LOCATION_DEBUG "${LIB_EAY_LIBRARY_DEBUG}") - endif() - _OpenSSL_target_add_dependencies(OpenSSL::Crypto) - endif() - - if(NOT TARGET OpenSSL::SSL AND - (EXISTS "${OPENSSL_SSL_LIBRARY}" OR - EXISTS "${SSL_EAY_LIBRARY_DEBUG}" OR - EXISTS "${SSL_EAY_LIBRARY_RELEASE}") - ) - add_library(OpenSSL::SSL UNKNOWN IMPORTED) - set_target_properties(OpenSSL::SSL PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${OPENSSL_INCLUDE_DIR}") - if(EXISTS "${OPENSSL_SSL_LIBRARY}") - set_target_properties(OpenSSL::SSL PROPERTIES - IMPORTED_LINK_INTERFACE_LANGUAGES "C" - IMPORTED_LOCATION "${OPENSSL_SSL_LIBRARY}") - endif() - if(EXISTS "${SSL_EAY_LIBRARY_RELEASE}") - set_property(TARGET OpenSSL::SSL APPEND PROPERTY - IMPORTED_CONFIGURATIONS RELEASE) - set_target_properties(OpenSSL::SSL PROPERTIES - IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" - IMPORTED_LOCATION_RELEASE "${SSL_EAY_LIBRARY_RELEASE}") - endif() - if(EXISTS "${SSL_EAY_LIBRARY_DEBUG}") - set_property(TARGET OpenSSL::SSL APPEND PROPERTY - IMPORTED_CONFIGURATIONS DEBUG) - set_target_properties(OpenSSL::SSL PROPERTIES - IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C" - IMPORTED_LOCATION_DEBUG "${SSL_EAY_LIBRARY_DEBUG}") - endif() - if(TARGET OpenSSL::Crypto) - set_target_properties(OpenSSL::SSL PROPERTIES - INTERFACE_LINK_LIBRARIES OpenSSL::Crypto) - endif() - _OpenSSL_target_add_dependencies(OpenSSL::SSL) - endif() -endif() - -# Restore the original find library ordering -if(OPENSSL_USE_STATIC_LIBS) - set(CMAKE_FIND_LIBRARY_SUFFIXES ${_openssl_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES}) -endif() diff --git a/components/core/src/clp/hash_utils.cpp b/components/core/src/clp/hash_utils.cpp new file mode 100644 index 000000000..a70cd8ff6 --- /dev/null +++ b/components/core/src/clp/hash_utils.cpp @@ -0,0 +1,223 @@ +#include "hash_utils.hpp" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ErrorCode.hpp" +#include "TraceableException.hpp" + +using std::make_unique; +using std::span; +using std::string; +using std::unique_ptr; +using std::vector; + +namespace clp { +namespace { +/** + * Pops the first OpenSSL error from its error queue and gets its string representation. + * @return The string representing the first OpenSSL error from its error queue. + */ +auto get_openssl_error_string() -> string { + auto const openssl_err = ERR_get_error(); + if (0 == openssl_err) { + return {}; + } + auto* openssl_err_str = ERR_error_string(openssl_err, nullptr); + if (nullptr == openssl_err_str) { + return {"Error has no string representation, error_code: " + std::to_string(openssl_err)}; + } + return {openssl_err_str}; +} + +/** + * A C++ wrapper for OpenSSL's EVP message digest context (EVP_MD_CTX). + */ +class EvpDigestContext { +public: + // Types + class OperationFailed : public clp::TraceableException { + public: + // Constructors + OperationFailed(ErrorCode error_code, char const* const filename, int line_number) + : OperationFailed( + error_code, + filename, + line_number, + "EvpDigestContext operation failed" + ) {} + + OperationFailed( + ErrorCode error_code, + char const* const filename, + int line_number, + string message + ) + : TraceableException(error_code, filename, line_number), + m_message(std::move(message)) {} + + // Methods + [[nodiscard]] auto what() const noexcept -> char const* override { + return m_message.c_str(); + } + + private: + string m_message; + }; + + // Constructors + /** + * @param type The type of digest (hash algorithm). + * @throw EvpDigestContext::OperationFailed with ErrorCode_NoMem if `EVP_MD_CTX_create` fails. + * @throw EvpDigestContext::OperationFailed with ErrorCode_Failure if `EVP_DigestInit_ex` fails. + */ + explicit EvpDigestContext(EVP_MD const* type) + : m_md_ctx{EVP_MD_CTX_create()}, + m_digest_nid{EVP_MD_type(type)} { + if (nullptr == m_md_ctx) { + throw OperationFailed(ErrorCode_NoMem, __FILENAME__, __LINE__); + } + // Set impl to nullptr to use the default implementation of digest type + if (1 != EVP_DigestInit_ex(m_md_ctx, type, nullptr)) { + throw OperationFailed( + ErrorCode_Failure, + __FILENAME__, + __LINE__, + get_openssl_error_string() + ); + } + } + + // Disable copy constructor and assignment operator + EvpDigestContext(EvpDigestContext const&) = delete; + auto operator=(EvpDigestContext const&) -> EvpDigestContext& = delete; + + // Disable move constructor and assignment operator + EvpDigestContext(EvpDigestContext&&) = delete; + auto operator=(EvpDigestContext&&) -> EvpDigestContext& = delete; + + // Destructor + ~EvpDigestContext() { EVP_MD_CTX_destroy(m_md_ctx); } + + // Methods + /** + * Hashes `input` into the digest. + * @param input + * @return Whether `EVP_DigestUpdate` succeeded. + */ + [[nodiscard]] auto digest_update(span input) -> bool; + + /** + * Writes the digest into `hash` and clears the digest. + * @param hash Returns the hashing result. + * @return ErrorCode_Success on success. + * @return ErrorCode_Corrupt if `hash` has an unexpected length. + * @return ErrorCode_Failure if `EVP_DigestFinal_ex` fails. + * @throw EvpDigestContext::OperationFailed with ErrorCode_Failure if `EVP_DigestInit_ex` fails. + */ + [[nodiscard]] auto digest_final(vector& hash) -> ErrorCode; + +private: + EVP_MD_CTX* m_md_ctx{nullptr}; + int m_digest_nid{}; +}; + +auto EvpDigestContext::digest_update(span input) -> bool { + if (1 != EVP_DigestUpdate(m_md_ctx, input.data(), input.size())) { + return false; + } + return true; +} + +auto EvpDigestContext::digest_final(vector& hash) -> ErrorCode { + hash.resize(EVP_MD_CTX_size(m_md_ctx)); + unsigned int length{}; + if (1 != EVP_DigestFinal_ex(m_md_ctx, hash.data(), &length)) { + return ErrorCode_Failure; + } + if (hash.size() != length) { + return ErrorCode_Corrupt; + } + + if (1 != EVP_DigestInit_ex(m_md_ctx, EVP_get_digestbynid(m_digest_nid), nullptr)) { + throw OperationFailed( + ErrorCode_Failure, + __FILENAME__, + __LINE__, + get_openssl_error_string() + ); + } + return ErrorCode_Success; +} +} // namespace + +auto convert_to_hex_string(span input) -> string { + string hex_string; + for (auto const c : input) { + hex_string += fmt::format("{:02x}", c); + } + return hex_string; +} + +auto get_hmac_sha256_hash( + span input, + span key, + vector& hash +) -> ErrorCode { + if (key.size() > INT32_MAX) { + return ErrorCode_BadParam; + } + + hash.resize(SHA256_DIGEST_LENGTH); + unsigned int hash_length{0}; + auto const key_length{static_cast(key.size())}; + if (nullptr + == HMAC(EVP_sha256(), + key.data(), + key_length, + input.data(), + input.size(), + hash.data(), + &hash_length)) + { + return ErrorCode_Failure; + } + + if (hash.size() != hash_length) { + return ErrorCode_Corrupt; + } + + return ErrorCode_Success; +} + +auto get_sha256_hash(span input, vector& hash) -> ErrorCode { + unique_ptr evp_ctx_manager; + try { + evp_ctx_manager = make_unique(EVP_sha256()); + } catch (EvpDigestContext::OperationFailed const& err) { + throw HashUtilsOperationFailed(err.get_error_code(), __FILENAME__, __LINE__, err.what()); + } + + if (false == evp_ctx_manager->digest_update(input)) { + return ErrorCode_Failure; + } + + if (auto const error_code = evp_ctx_manager->digest_final(hash); + ErrorCode_Success != error_code) + { + return error_code; + } + + return ErrorCode_Success; +} +} // namespace clp diff --git a/components/core/src/clp/hash_utils.hpp b/components/core/src/clp/hash_utils.hpp new file mode 100644 index 000000000..4606aed23 --- /dev/null +++ b/components/core/src/clp/hash_utils.hpp @@ -0,0 +1,77 @@ +#ifndef CLP_HASH_UTILS_HPP +#define CLP_HASH_UTILS_HPP + +#include +#include +#include +#include + +#include "ErrorCode.hpp" +#include "TraceableException.hpp" + +namespace clp { +// Types +class HashUtilsOperationFailed : public TraceableException { +public: + // Constructors + HashUtilsOperationFailed(ErrorCode error_code, char const* const filename, int line_number) + : HashUtilsOperationFailed( + error_code, + filename, + line_number, + "clp::hash_utils operation failed" + ) {} + + HashUtilsOperationFailed( + ErrorCode error_code, + char const* const filename, + int line_number, + std::string message + ) + : TraceableException(error_code, filename, line_number), + m_message(std::move(message)) {} + + // Methods + [[nodiscard]] auto what() const noexcept -> char const* override { return m_message.c_str(); } + +private: + std::string m_message; +}; + +/** + * @param input + * @return `input` as a hex string (without the "0x" prefix). + */ +[[nodiscard]] auto convert_to_hex_string(std::span input) -> std::string; + +/** + * Gets the HMAC-SHA256 hash of `input` with `key`. + * @param input + * @param key + * @param hash Returns the HMAC. + * @return ErrorCode_Success on success. + * @return ErrorCode_BadParam if `key` is longer than `INT32_MAX`. + * @return ErrorCode_Failure if hash generation fails. + * @return ErrorCode_Corrupt if `hash` has an unexpected length. + */ +[[nodiscard]] auto get_hmac_sha256_hash( + std::span input, + std::span key, + std::vector& hash +) -> ErrorCode; + +/** + * Gets the SHA256 hash of `input`. + * @param input + * @param hash Returns the hash. + * @return ErrorCode_Success on success. + * @return ErrorCode_Failure if `EvpDigestContext::digest_update` fails. + * @return Same as `EvpDigestContext::digest_final` if `EvpDigestContext::digest_final` fails. + * @throw HashUtilsOperationFailed if an OpenSSL EVP digest couldn't be created. + */ +[[nodiscard]] auto get_sha256_hash( + std::span input, + std::vector& hash +) -> ErrorCode; +} // namespace clp +#endif // CLP_HASH_UTILS_HPP diff --git a/components/core/tests/test-hash_utils.cpp b/components/core/tests/test-hash_utils.cpp new file mode 100644 index 000000000..9e5aacd47 --- /dev/null +++ b/components/core/tests/test-hash_utils.cpp @@ -0,0 +1,51 @@ +#include +#include + +#include + +#include "../src/clp/ErrorCode.hpp" +#include "../src/clp/hash_utils.hpp" +#include "../src/clp/type_utils.hpp" + +using clp::convert_to_hex_string; +using clp::ErrorCode_Success; +using clp::get_hmac_sha256_hash; +using clp::get_sha256_hash; +using clp::size_checked_pointer_cast; +using std::string_view; +using std::vector; + +TEST_CASE("test_sha256", "[hash_utils]") { + constexpr string_view cInputString{"ThisIsARandomTestInput"}; + constexpr string_view cReferenceSha256{ + "c3a1d9f04ada1198c4c63bf51d9933fc2cc216429275cadabdcb2178775add0c" + }; + vector hash; + + REQUIRE(ErrorCode_Success + == get_sha256_hash( + {size_checked_pointer_cast(cInputString.data()), + cInputString.size()}, + hash + )); + REQUIRE(convert_to_hex_string(hash) == cReferenceSha256); +} + +TEST_CASE("test_hmac", "[hash_utils]") { + constexpr string_view cInputString{"ThisIsARandomTestInput"}; + constexpr string_view cInputKey{"ThisIsATestKey"}; + constexpr string_view cReferenceHmacSha256{ + "38373057694c1038a6895212bea46849eb7a59b73a2ec175883ae095fb91ffda" + }; + vector hmac_hash; + + REQUIRE(ErrorCode_Success + == get_hmac_sha256_hash( + {size_checked_pointer_cast(cInputString.data()), + cInputString.size()}, + {size_checked_pointer_cast(cInputKey.data()), + cInputKey.size()}, + hmac_hash + )); + REQUIRE(convert_to_hex_string(hmac_hash) == cReferenceHmacSha256); +}