From 04c6a5fc8135974a286b127e2baedaa34cb74061 Mon Sep 17 00:00:00 2001 From: Ruslan Isaev Date: Tue, 18 Aug 2026 06:17:41 +0300 Subject: [PATCH] engine: add static GOST engine support with explicit OpenSSL binding ENGINE_load_gost() previously created and added the GOST engine to OpenSSL, but did not explicitly register its PKEY ASN.1 methods. This was sufficient for the dynamic engine loading path, but incomplete for direct library/static use where the program calls ENGINE_load_gost() itself. As a result, the engine could be found with ENGINE_by_id("gost"), while GOST-2012 key ASN.1 methods were still unavailable to libcrypto. This also caused GOST TLS cipher suites to be rejected by SSL_CTX_set_cipher_list(). Register the engine PKEY ASN.1 methods explicitly after ENGINE_add() so static and library builds expose GOST-2012 keys correctly and can use GOST TLS cipher suites. Signed-off-by: Ruslan Isaev --- CMakeLists.txt | 42 +++++++++++++++----------- cmake/engine.cmake | 74 ++++++++++++++++++++++++++++++---------------- gost_eng.c | 29 ++++++++++-------- 3 files changed, 91 insertions(+), 54 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 11d977296..08b85cd85 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,17 +7,28 @@ include(CheckFunctionExists) include(CheckCSourceRuns) include(CheckCSourceCompiles) +if(NOT DEFINED OPENSSL_ROOT_DIR) + message(FATAL_ERROR "OPENSSL_ROOT_DIR is required") +endif() + find_package(OpenSSL 3.4 REQUIRED) +get_filename_component(GOST_OPENSSL_ROOT "${OPENSSL_ROOT_DIR}" REALPATH) +get_filename_component(GOST_OPENSSL_INCLUDE "${OPENSSL_INCLUDE_DIR}" REALPATH) +get_filename_component(GOST_OPENSSL_CRYPTO "${OPENSSL_CRYPTO_LIBRARY}" REALPATH) +get_filename_component(GOST_OPENSSL_SSL "${OPENSSL_SSL_LIBRARY}" REALPATH) +foreach(path IN ITEMS GOST_OPENSSL_INCLUDE GOST_OPENSSL_CRYPTO GOST_OPENSSL_SSL) + string(FIND "${${path}}/" "${GOST_OPENSSL_ROOT}/" pos) + if(NOT pos EQUAL 0) + message(FATAL_ERROR "OpenSSL path escapes OPENSSL_ROOT_DIR: ${${path}}") + endif() +endforeach() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'RelWithDebInfo' as none was specified.") set(CMAKE_BUILD_TYPE "RelWithDebInfo" CACHE STRING "Choose the type of build." FORCE) endif() -if (NOT DEFINED OPENSSL_ROOT_DIR) - get_filename_component(OPENSSL_ROOT_DIR ${OPENSSL_INCLUDE_DIR} DIRECTORY) - message(STATUS "Setting OpenSSL root: ${OPENSSL_ROOT_DIR}") -endif() find_program(OPENSSL_PROGRAM openssl PATHS ${OPENSSL_ROOT_DIR} PATH_SUFFIXES apps bin NO_DEFAULT_PATH) message(STATUS "Found OpenSSL application: ${OPENSSL_PROGRAM}") @@ -27,16 +38,17 @@ set(OPENSSL_MODULES_DIR ${CMAKE_INSTALL_LIBDIR}/ossl-modules) include("cmake/utils.cmake") check_have_engine_api(HAVE_ENGINE_API) -option(GOST_BUILD_ENGINE "Build gost engine module" ${HAVE_ENGINE_API}) -option(GOST_BUILD_PROVIDER "Build gost provider module" ON) +option(GOST_BUILD_ENGINE "Build gost engine module" ${HAVE_ENGINE_API}) +option(GOST_BUILD_STATIC_ENGINE "Build gost engine static library" OFF) +option(GOST_BUILD_PROVIDER "Build gost provider module" ON) -if(GOST_BUILD_ENGINE AND NOT HAVE_ENGINE_API) +if((GOST_BUILD_ENGINE OR GOST_BUILD_STATIC_ENGINE) AND NOT HAVE_ENGINE_API) message(FATAL_ERROR - "GOST_BUILD_ENGINE=ON, but libcrypto has no ENGINE API. " - "Rebuild OpenSSL without no-engine, or pass -DGOST_BUILD_ENGINE=OFF.") + "GOST engine requested, but libcrypto has no ENGINE API. " + "Rebuild OpenSSL without no-engine, or disable GOST engine builds.") endif() -if(NOT GOST_BUILD_ENGINE) +if(NOT GOST_BUILD_ENGINE AND NOT GOST_BUILD_STATIC_ENGINE) # We may depend on ENGINE API only if there is an engine target add_compile_definitions(OPENSSL_NO_ENGINE) set(HAVE_ENGINE_API FALSE) @@ -130,12 +142,8 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIRECTORY}) # Remove when https://gitlab.kitware.com/cmake/cmake/issues/18525 is addressed set(OPENSSL_ENGINES_DIR "" CACHE PATH "OpenSSL Engines Directory") -if ("${OPENSSL_ENGINES_DIR}" STREQUAL "") - include(FindPkgConfig) - pkg_get_variable(OPENSSL_ENGINES_DIR libcrypto enginesdir) - if ("${OPENSSL_ENGINES_DIR}" STREQUAL "") - message( FATAL_ERROR "Unable to discover the OpenSSL engines directory. Provide the path using -DOPENSSL_ENGINES_DIR" ) - endif() +if("${OPENSSL_ENGINES_DIR}" STREQUAL "") + message(FATAL_ERROR "OPENSSL_ENGINES_DIR is required") endif() set(GOST_89_SOURCE_FILES @@ -274,7 +282,7 @@ add_custom_target(tags WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}) list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake) -if(GOST_BUILD_ENGINE) +if(GOST_BUILD_ENGINE OR GOST_BUILD_STATIC_ENGINE) include(engine) endif() if(GOST_BUILD_PROVIDER) diff --git a/cmake/engine.cmake b/cmake/engine.cmake index f28d05d39..6e2a7e3ec 100644 --- a/cmake/engine.cmake +++ b/cmake/engine.cmake @@ -9,38 +9,62 @@ set(GOST_ENGINE_SOURCE_FILES gost_eng_pmeth.c ) -# The GOST engine in module form -add_library(gost_engine MODULE ${GOST_ENGINE_SOURCE_FILES}) -# Set the suffix explicitly to adapt to OpenSSL's idea of what a -# module suffix should be -set_target_properties(gost_engine PROPERTIES -PREFIX "" OUTPUT_NAME "gost" SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) -target_link_libraries(gost_engine PRIVATE gost_core gost_err) - -if (NOT MSVC) - # The GOST engine in library form - add_library(lib_gost_engine SHARED ${GOST_ENGINE_SOURCE_FILES}) - set_target_properties(lib_gost_engine PROPERTIES - COMPILE_DEFINITIONS "BUILDING_ENGINE_AS_LIBRARY" - PUBLIC_HEADER gost-engine.h - OUTPUT_NAME "gost") - target_link_libraries(lib_gost_engine PRIVATE gost_core gost_err) +if(GOST_BUILD_ENGINE) + # The GOST engine in module form + add_library(gost_engine MODULE ${GOST_ENGINE_SOURCE_FILES}) + set_target_properties(gost_engine PROPERTIES + PREFIX "" OUTPUT_NAME "gost" SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) + target_link_libraries(gost_engine PRIVATE gost_core gost_err) + + if(NOT MSVC) + # The GOST engine in shared-library form + add_library(lib_gost_engine SHARED ${GOST_ENGINE_SOURCE_FILES}) + set_target_properties(lib_gost_engine PROPERTIES + COMPILE_DEFINITIONS "BUILDING_ENGINE_AS_LIBRARY" + PUBLIC_HEADER gost-engine.h + OUTPUT_NAME "gost") + target_link_libraries(lib_gost_engine PRIVATE gost_core gost_err) + endif() + + install(TARGETS gost_engine EXPORT GostEngineConfig + LIBRARY DESTINATION ${OPENSSL_ENGINES_DIR} + RUNTIME DESTINATION ${OPENSSL_ENGINES_DIR}) + + if(NOT MSVC) + install(TARGETS lib_gost_engine EXPORT GostEngineConfig + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + endif() endif() -install(TARGETS gost_engine EXPORT GostEngineConfig - LIBRARY DESTINATION ${OPENSSL_ENGINES_DIR} - RUNTIME DESTINATION ${OPENSSL_ENGINES_DIR}) +if(GOST_BUILD_STATIC_ENGINE) + set(GOST_ENGINE_STATIC_SOURCE_FILES + ${GOST_ENGINE_SOURCE_FILES} + ${GOST_89_SOURCE_FILES} + ${GOST_HASH_SOURCE_FILES} + ${GOST_HASH_2012_SOURCE_FILES} + ${GOST_TLS12_ADDITIONAL_SOURCE_FILES} + ${GOST_LIB_SOURCE_FILES} + ${GOST_ERR_SOURCE_FILES}) + list(REMOVE_DUPLICATES GOST_ENGINE_STATIC_SOURCE_FILES) + + add_library(gost_engine_static STATIC ${GOST_ENGINE_STATIC_SOURCE_FILES}) + set_target_properties(gost_engine_static PROPERTIES + COMPILE_DEFINITIONS "BUILDING_ENGINE_AS_LIBRARY" + OUTPUT_NAME "gost") + target_link_libraries(gost_engine_static PUBLIC OpenSSL::Crypto) -if (NOT MSVC) - install(TARGETS lib_gost_engine EXPORT GostEngineConfig - LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(TARGETS gost_engine_static EXPORT GostEngineConfig + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}) + install(FILES gost-engine.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) endif() -if (MSVC) +if(GOST_BUILD_ENGINE OR GOST_BUILD_STATIC_ENGINE) + install(EXPORT GostEngineConfig DESTINATION share/cmake/GostEngine) +endif() + +if(MSVC AND GOST_BUILD_ENGINE) install(FILES $ $ EXPORT GostEngineConfig DESTINATION ${CMAKE_INSTALL_BINDIR} OPTIONAL) install(FILES $ EXPORT GostEngineConfig DESTINATION ${OPENSSL_ENGINES_DIR} OPTIONAL) endif() - -install(EXPORT GostEngineConfig DESTINATION share/cmake/GostEngine) diff --git a/gost_eng.c b/gost_eng.c index 41018c75f..bcc6dbc3e 100644 --- a/gost_eng.c +++ b/gost_eng.c @@ -534,20 +534,25 @@ IMPLEMENT_DYNAMIC_CHECK_FN() #else /* - * When building gost-engine as a shared library, the application that uses - * it must manually call ENGINE_load_gost() for it to bind itself into the - * libcrypto libraries. + * When building gost-engine as a library, the application that uses it must + * manually call ENGINE_load_gost() for it to bind itself into libcrypto. */ -void ENGINE_load_gost(void) { - ENGINE* toadd; - int ret = 0; +void ENGINE_load_gost(void) +{ + ENGINE *e; + + e = ENGINE_new(); + if (e == NULL) { + return; + } + if (!make_gost_engine(e, engine_gost_id) || + !ENGINE_add(e) || !ENGINE_register_pkey_asn1_meths(e)) { + goto out; + } + ERR_clear_error(); - if ((toadd = ENGINE_new()) != NULL - && (ret = make_gost_engine(toadd, engine_gost_id)) > 0) - ENGINE_add(toadd); - ENGINE_free(toadd); - if (ret > 0) - ERR_clear_error(); +out: + ENGINE_free(e); } #endif #endif