# Copyright (c) 2018-2025 Ribose Inc. # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. 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. # # 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 HOLDERS 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. include(GNUInstallDirs) include(GenerateExportHeader) # BZip2 is optional (tri-state): On - require, Off - do not use, Auto - use if found if (ENABLE_BZIP2) string(TOLOWER "${ENABLE_BZIP2}" ENABLE_BZIP2_LOWER) if (ENABLE_BZIP2_LOWER STREQUAL "auto") find_package(BZip2) if (BZIP2_FOUND) set(ENABLE_BZIP2 On CACHE STRING "Autodetected" FORCE) else() set(ENABLE_BZIP2 Off CACHE STRING "Autodetected" FORCE) message(STATUS "BZip2 not found - BZip2 compression support is disabled.") endif() else() find_package(BZip2 REQUIRED) endif() unset(ENABLE_BZIP2_LOWER) endif() # these could probably be optional but are currently not find_package(ZLIB REQUIRED) # required packages if(ENABLE_CRYPTO_REFRESH AND CRYPTO_BACKEND_BOTAN) find_package(Botan 3.6.0 REQUIRED) set(CRYPTO_BACKEND_BOTAN3 1) else() if (CRYPTO_BACKEND_BOTAN3) find_package(Botan 3.0.0 REQUIRED) elseif (CRYPTO_BACKEND_BOTAN) # botan-config.cmake from Botan distribution doesn't support version 2.x, # so use -DCRYPTO_BACKEND=botan3 to configure find_package(Botan 2.14.0 REQUIRED) if(Botan_VERSION VERSION_GREATER_EQUAL 3.0.0) set(CRYPTO_BACKEND_BOTAN3 1) endif() endif() endif() if (CRYPTO_BACKEND_BOTAN) # botan-config.cmake from Botan distribution sets Botan_VERSION instead of BOTAN_VERSION # like we did and doesn't set BOTAN_INCLUDE_DIRS like our built-in did. get_target_property(BOTAN_INCLUDE_DIRS Botan::Botan INTERFACE_INCLUDE_DIRECTORIES) set(CRYPTO_BACKEND_VERSION "${Botan_VERSION}") endif() if (CRYPTO_BACKEND_OPENSSL) find_package(OpenSSL 1.1.1 REQUIRED) include(FindOpenSSLFeatures) if("${OPENSSL_VERSION}" VERSION_GREATER_EQUAL "3.0.0") set(CRYPTO_BACKEND_OPENSSL3 1) endif() set(CRYPTO_BACKEND_VERSION "${OPENSSL_VERSION}") endif() if(CRYPTO_BACKEND_BOTAN3) set(CMAKE_CXX_STANDARD 20) endif() if(ENABLE_CRYPTO_REFRESH AND CRYPTO_BACKEND_BOTAN) if (NOT CRYPTO_BACKEND_BOTAN3) message(FATAL_ERROR "ENABLE_CRYPTO_REFRESH requires Botan 3 as crypto backend") endif() endif() # check that AEAD is enabled and not turned off for ENABLE_CRYPTO_REFRESH if(ENABLE_CRYPTO_REFRESH AND (NOT ENABLE_AEAD)) message(FATAL_ERROR "ENABLE_CRYPTO_REFRESH requires ENABLE_AEAD, but it's either Off or Auto and got turned off") endif() # generate a config.h include(CheckIncludeFileCXX) include(CheckCXXSymbolExists) # Some toolchain files (e.g. ohos.toolchain.cmake) set CMAKE_SYSROOT but the # try-compile doesn't always propagate it to the linker invocation. Pass # --sysroot explicitly to be safe; harmless when CMAKE_SYSROOT is empty. if(CMAKE_SYSROOT) list(APPEND CMAKE_REQUIRED_FLAGS "--sysroot=${CMAKE_SYSROOT}") endif() check_include_file_cxx(fcntl.h HAVE_FCNTL_H) check_include_file_cxx(inttypes.h HAVE_INTTYPES_H) check_include_file_cxx(limits.h HAVE_LIMITS_H) check_include_file_cxx(stdint.h HAVE_STDINT_H) check_include_file_cxx(string.h HAVE_STRING_H) check_include_file_cxx(sys/cdefs.h HAVE_SYS_CDEFS_H) check_include_file_cxx(sys/cdefs.h HAVE_SYS_MMAN_H) check_include_file_cxx(sys/resource.h HAVE_SYS_RESOURCE_H) check_include_file_cxx(sys/stat.h HAVE_SYS_STAT_H) check_include_file_cxx(sys/types.h HAVE_SYS_TYPES_H) check_include_file_cxx(sys/param.h HAVE_SYS_PARAM_H) check_include_file_cxx(unistd.h HAVE_UNISTD_H) check_include_file_cxx(sys/wait.h HAVE_SYS_WAIT_H) check_cxx_symbol_exists(mkdtemp "stdlib.h;unistd.h" HAVE_MKDTEMP) check_cxx_symbol_exists(mkstemp "stdlib.h;unistd.h" HAVE_MKSTEMP) check_cxx_symbol_exists(realpath stdlib.h HAVE_REALPATH) check_cxx_symbol_exists(O_BINARY fcntl.h HAVE_O_BINARY) check_cxx_symbol_exists(_O_BINARY fcntl.h HAVE__O_BINARY) check_cxx_symbol_exists(_tempnam stdio.h HAVE__TEMPNAM) set(HAVE_ZLIB_H "${ZLIB_FOUND}") set(HAVE_BZLIB_H "${BZIP2_FOUND}") if (WIN32) include(CheckCSourceCompiles) check_c_source_compiles(" #include #include #include int main() { struct stat s; return 0; }" HAVE_WIN_STAT) endif() # Checks feature in CRYPTO_BACKEND and puts the result into variable whose name is stored in RESULT_VARNAME variable. function(backend_has_feature FEATURE RESULT_VARNAME) if (CRYPTO_BACKEND_LOWERCASE STREQUAL "botan") check_cxx_symbol_exists("BOTAN_HAS_${FEATURE}" botan/build.h ${RESULT_VARNAME}) else() message(STATUS "Looking for OpenSSL feature ${FEATURE}") OpenSSLHasFeature(${FEATURE} ${RESULT_VARNAME}) if (${RESULT_VARNAME}) message(STATUS "Looking for OpenSSL feature ${FEATURE} - found") endif() set(${RESULT_VARNAME} "${${RESULT_VARNAME}}" PARENT_SCOPE) endif() endfunction() function(resolve_feature_state RNP_FEATURE BACKEND_FEATURES) if (NOT ${RNP_FEATURE}) # User has explicitly disabled this feature return() endif() string(TOLOWER "${${RNP_FEATURE}}" ${RNP_FEATURE}) if (${RNP_FEATURE} STREQUAL "auto") set(MESSAGE_TYPE "NOTICE") set(OUTCOME "Disabling") else() # User has explicitly enabled this feature set(MESSAGE_TYPE "FATAL_ERROR") set(OUTCOME "Aborting") endif() foreach(feature ${BACKEND_FEATURES}) backend_has_feature("${feature}" _has_${feature}) if (NOT _has_${feature}) set(${RNP_FEATURE} Off CACHE STRING "Autodetected" FORCE) message(${MESSAGE_TYPE} "${RNP_FEATURE} requires ${CRYPTO_BACKEND} feature which is missing: ${feature}. ${OUTCOME}.") return() endif() endforeach() set(${RNP_FEATURE} On CACHE STRING "Autodetected" FORCE) endfunction() function(openssl_nope RNP_FEATURE REASON) if (NOT ${RNP_FEATURE}) # User has explicitly disabled this feature return() endif() string(TOLOWER "${${RNP_FEATURE}}" ${RNP_FEATURE}) if (${RNP_FEATURE} STREQUAL "auto") set(MESSAGE_TYPE "NOTICE") set(OUTCOME "Disabling") else() # User has explicitly enabled this feature set(MESSAGE_TYPE "FATAL_ERROR") set(OUTCOME "Aborting") endif() set(${RNP_FEATURE} Off CACHE STRING "Auto -> Off as no support" FORCE) message(${MESSAGE_TYPE} "${RNP_FEATURE} doesn't work with OpenSSL backend (${REASON}). ${OUTCOME}.") endfunction() if(CRYPTO_BACKEND_BOTAN) # check botan's enabled features set(CMAKE_REQUIRED_INCLUDES "${BOTAN_INCLUDE_DIRS}") set(_botan_required_features # base BIGINT FFI HEX_CODEC PGP_S2K # symmetric ciphers BLOCK_CIPHER AES CAMELLIA DES # cipher modes MODE_CBC MODE_CFB # RNG AUTO_RNG AUTO_SEEDING_RNG HMAC HMAC_DRBG # hash CRC24 HASH MD5 SHA1 SHA2_32 SHA2_64 SHA3 # public-key core DL_GROUP ECC_GROUP ECC_PUBLIC_KEY_CRYPTO PUBLIC_KEY_CRYPTO # Botan-2: DL_PUBLIC_KEY_FAMILY Botan-3: DL_SCHEME, see switch below # public-key algs CURVE_25519 DSA ECDH ECDSA ED25519 ELGAMAL RSA # public-key operations etc EME_PKCS1v15 EMSA_PKCS1 EMSA_RAW KDF_BASE RFC3394_KEYWRAP SP800_56A ) if(Botan_VERSION VERSION_LESS 3.0.0) set(_botan_required_features ${_botan_required_features} DL_PUBLIC_KEY_FAMILY) else() set(_botan_required_features ${_botan_required_features} DL_SCHEME RAW_HASH_FN) endif() if(Botan_VERSION VERSION_GREATER_EQUAL 3.7.0) set(_botan_required_features ${_botan_required_features} # Basic curves, see below for Brainpool/SM2 PCURVES PCURVES_IMPL PCURVES_SECP192R1 PCURVES_SECP256K1 PCURVES_SECP256R1 PCURVES_SECP384R1 PCURVES_SECP521R1 ) endif() foreach(feature ${_botan_required_features}) backend_has_feature("${feature}" _botan_has_${feature}) if (NOT _botan_has_${feature}) message(FATAL_ERROR "A required botan feature is missing: ${feature}") endif() endforeach() resolve_feature_state(ENABLE_SM2 "SM2;SM3;SM4") resolve_feature_state(ENABLE_AEAD "AEAD_EAX;AEAD_OCB") resolve_feature_state(ENABLE_TWOFISH "TWOFISH") resolve_feature_state(ENABLE_IDEA "IDEA") resolve_feature_state(ENABLE_CRYPTO_REFRESH "HKDF;ED448;X448;ARGON2") # The PQC code paths use the FIPS 203/204/205 APIs (KyberMode::ML_KEM_*, # DilithiumMode::ML_DSA_*, SLH_DSA_*), which first exist in Botan 3.6.0. # The floor is 3.7.0, not 3.6.0: # - 3.6.0 signs SLH-DSA with the non-FIPS-compliant deterministic variant # (fixed upstream in 3.6.1, GH #4398); # - 3.6.0/3.6.1 crash in the BigInt/legacy-EC path on arm64 (verified: # rnp_tests.ecdsa_signverify_success and the ML-KEM/ECDH curve composites # die with SIGBUS inside Botan::BigInt::is_equal); # - the minimized-build module list (ci/botan3-pqc-modules) needs the # legacy_ec_point module, which only exists from 3.7.0. # The round-3 Kyber/Dilithium/SPHINCS+ variants of older Botan are different # algorithms, not renames: the draft-ietf-openpgp-pqc test vectors verify # only with the final FIPS parameter sets, so enabling PQC there would pass # configure (the legacy BOTAN_HAS_KYBER/DILITHIUM/SPHINCS_PLUS_* defines # exist) and then fail compiling the missing APIs. Reject it upfront. if(NOT Botan_VERSION VERSION_GREATER_EQUAL 3.7.0) if(ENABLE_PQC) string(TOLOWER "${ENABLE_PQC}" _pqc) if(NOT _pqc STREQUAL "auto") message(FATAL_ERROR "ENABLE_PQC requires Botan >= 3.7.0 (FIPS 203/204/205 APIs; 3.6.x " "has an arm64 EC crash and an SLH-DSA signing bug); older Botan " "only provides the non-interoperable round-3 variants.") endif() set(ENABLE_PQC Off CACHE STRING "Auto -> Off as no support" FORCE) message(NOTICE "ENABLE_PQC requires Botan >= 3.7.0. Disabling.") endif() else() resolve_feature_state(ENABLE_PQC "HKDF;ML_KEM;ML_DSA;SLH_DSA_WITH_SHA2;SLH_DSA_WITH_SHAKE") endif() # Even without PQC, plain ECDSA/ECDH crash on arm64 with Botan 3.6.x (same # upstream BigInt/legacy-EC bug); x86-64 3.6 builds are unaffected. if(Botan_VERSION VERSION_GREATER_EQUAL 3.6.0 AND Botan_VERSION VERSION_LESS 3.7.0 AND (CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm64|ARM64")) message(WARNING "Botan ${Botan_VERSION} has an upstream crash in the EC path on arm64 " "(SIGBUS in BigInt::is_equal) that breaks ordinary ECDSA/ECDH. " "Use Botan >= 3.7.0 on arm64 if possible.") endif() resolve_feature_state(ENABLE_BLOWFISH "BLOWFISH") resolve_feature_state(ENABLE_CAST5 "CAST_128") resolve_feature_state(ENABLE_RIPEMD160 "RIPEMD_160") if(Botan_VERSION VERSION_GREATER_EQUAL 3.7.0) # Separate SM2 curve since 3.7.0 resolve_feature_state(ENABLE_SM2 "PCURVES_SM2P256V1") # Botan supports Brainpool curves together with SECP via the ECC_GROUP define before the 3.7.0 resolve_feature_state(ENABLE_BRAINPOOL "PCURVES_BRAINPOOL256R1;PCURVES_BRAINPOOL384R1;PCURVES_BRAINPOOL512R1") endif() set(ENABLE_AEAD_EAX "${ENABLE_AEAD}") set(ENABLE_AEAD_OCB "${ENABLE_AEAD}") set(CMAKE_REQUIRED_INCLUDES) endif() if(CRYPTO_BACKEND_OPENSSL) # check OpenSSL features set(_openssl_required_features # symmetric ciphers AES-128-ECB AES-192-ECB AES-256-ECB AES-128-CBC AES-192-CBC AES-256-CBC AES-128-OCB AES-192-OCB AES-256-OCB CAMELLIA-128-ECB CAMELLIA-192-ECB CAMELLIA-256-ECB DES-EDE3 # hashes MD5 SHA1 SHA224 SHA256 SHA384 SHA512 SHA3-256 SHA3-512 # curves PRIME256V1 SECP384R1 SECP521R1 SECP256K1 # public key RSAENCRYPTION DSAENCRYPTION DHKEYAGREEMENT ID-ECPUBLICKEY X25519 ED25519 ) foreach(feature ${_openssl_required_features}) backend_has_feature("${feature}" _openssl_has_${feature}) if (NOT _openssl_has_${feature}) message(FATAL_ERROR "A required OpenSSL feature is missing: ${feature}") endif() endforeach() if (CRYPTO_BACKEND_OPENSSL3) backend_has_feature("LEGACY" CRYPTO_BACKEND_OPENSSL3_LEGACY) endif() resolve_feature_state(ENABLE_BRAINPOOL "BRAINPOOLP256R1;BRAINPOOLP384R1;BRAINPOOLP512R1") # Not all of the OpenSSL installations have legacy crypto provider resolve_feature_state(ENABLE_IDEA "IDEA-ECB;IDEA-CBC;LEGACY") resolve_feature_state(ENABLE_BLOWFISH "BF-ECB;LEGACY") resolve_feature_state(ENABLE_CAST5 "CAST5-ECB;LEGACY") if("${OPENSSL_VERSION}" VERSION_GREATER_EQUAL "3.0.7") resolve_feature_state(ENABLE_RIPEMD160 "RIPEMD160") else() resolve_feature_state(ENABLE_RIPEMD160 "RIPEMD160;LEGACY") endif() resolve_feature_state(ENABLE_AEAD "AES-128-OCB;AES-192-OCB;AES-256-OCB") openssl_nope(ENABLE_SM2 "it's on our roadmap, see https://github.com/rnpgp/rnp/issues/1877") #resolve_feature_state(ENABLE_SM2 "SM2;SM3;SM4-ECB") openssl_nope(ENABLE_TWOFISH "Twofish isn't and won't be supported by OpenSSL, see https://github.com/openssl/openssl/issues/2046") if(CRYPTO_BACKEND_OPENSSL3) # Argon2 KDFs are available since OpenSSL 3.2 resolve_feature_state(ENABLE_CRYPTO_REFRESH "HKDF;ED448;X448;ARGON2ID") else() openssl_nope(ENABLE_CRYPTO_REFRESH "requires OpenSSL 3.0+") endif() if(CRYPTO_BACKEND_OPENSSL3) resolve_feature_state(ENABLE_PQC "ML-KEM-768;ML-KEM-1024;ML-DSA-65;ML-DSA-87;SLH-DSA-SHAKE-128f;SLH-DSA-SHAKE-128s;SLH-DSA-SHAKE-256s") else() openssl_nope(ENABLE_PQC "requires OpenSSL 3.0+") endif() set(ENABLE_AEAD_EAX "Off") set(ENABLE_AEAD_OCB "${ENABLE_AEAD}") endif() configure_file(config.h.in config.h) # generate a version.h configure_file(version.h.in version.h) # append enabled features defines set(HAS_FEATURES BRAINPOOL IDEA BLOWFISH CAST5 RIPEMD160 AEAD AEAD_EAX AEAD_OCB SM2 TWOFISH) list(SORT HAS_FEATURES) set(HAS_FEATURES_MACROS "") foreach(FEATURE ${HAS_FEATURES}) if(ENABLE_${FEATURE}) set(VALUE 1) else() set(VALUE 0) endif() string(APPEND HAS_FEATURES_MACROS "#define RNP_HAS_${FEATURE} ${VALUE}\n") endforeach() file(APPEND ${CMAKE_CURRENT_BINARY_DIR}/version.h "${HAS_FEATURES_MACROS}") if(CRYPTO_BACKEND_OPENSSL) set(CRYPTO_SOURCES crypto/dsa_common.cpp crypto/dsa_ossl.cpp crypto/ec_curves.cpp crypto/ec_ossl.cpp crypto/ecdh_utils.cpp crypto/ecdh_ossl.cpp crypto/ecdsa_ossl.cpp crypto/eddsa_ossl.cpp crypto/dl_ossl.cpp crypto/elgamal_ossl.cpp crypto/hash_common.cpp crypto/hash_ossl.cpp crypto/hash_crc24.cpp crypto/mpi.cpp crypto/rng_ossl.cpp crypto/rsa_ossl.cpp crypto/s2k.cpp crypto/s2k_ossl.cpp crypto/symmetric_common.cpp crypto/symmetric_ossl.cpp crypto/signatures.cpp crypto/mem_ossl.cpp crypto/cipher.cpp crypto/cipher_ossl.cpp ) if(ENABLE_SM2) list(APPEND CRYPTO_SOURCES crypto/sm2_ossl.cpp) endif() if(ENABLE_CRYPTO_REFRESH OR ENABLE_PQC) list(APPEND CRYPTO_SOURCES crypto/secure_bytes.cpp crypto/hkdf.cpp crypto/hkdf_ossl.cpp crypto/ed25519_ed448_ossl.cpp crypto/x25519_x448_ossl.cpp crypto/exdsa_ecdhkem_ossl.cpp ) endif() if(ENABLE_PQC) list(APPEND CRYPTO_SOURCES crypto/kyber_common.cpp crypto/kyber_ossl.cpp crypto/kyber_ecdh_composite.cpp crypto/kem_combiner.cpp ) endif() if(ENABLE_PQC AND ENABLE_CRYPTO_REFRESH) list(APPEND CRYPTO_SOURCES crypto/dilithium_common.cpp crypto/dilithium_ossl.cpp crypto/sphincsplus_ossl.cpp crypto/dilithium_exdsa_composite.cpp ) endif() elseif(CRYPTO_BACKEND_BOTAN) set(CRYPTO_SOURCES crypto/dsa_common.cpp crypto/dsa.cpp crypto/ec_curves.cpp crypto/ec.cpp crypto/ecdh_utils.cpp crypto/ecdh.cpp crypto/ecdsa.cpp crypto/eddsa.cpp crypto/elgamal.cpp crypto/hash_common.cpp crypto/hash.cpp crypto/mpi.cpp crypto/rng.cpp crypto/rsa.cpp crypto/s2k.cpp crypto/symmetric_common.cpp crypto/symmetric.cpp crypto/signatures.cpp crypto/mem.cpp crypto/cipher.cpp crypto/cipher_botan.cpp ) if(ENABLE_SM2) list(APPEND CRYPTO_SOURCES crypto/sm2.cpp) endif() if(ENABLE_CRYPTO_REFRESH OR ENABLE_PQC) list(APPEND CRYPTO_SOURCES crypto/secure_bytes.cpp crypto/exdsa_ecdhkem.cpp crypto/x25519_x448.cpp crypto/ed25519_ed448.cpp crypto/hkdf.cpp crypto/hkdf_botan.cpp ) endif() if(ENABLE_PQC) list(APPEND CRYPTO_SOURCES crypto/kyber_common.cpp crypto/kyber.cpp crypto/kyber_ecdh_composite.cpp crypto/kem_combiner.cpp ) endif() if(ENABLE_PQC AND ENABLE_CRYPTO_REFRESH) list(APPEND CRYPTO_SOURCES crypto/dilithium.cpp crypto/dilithium_common.cpp crypto/sphincsplus.cpp crypto/dilithium_exdsa_composite.cpp ) endif() else() message(FATAL_ERROR "Unknown crypto backend: ${CRYPTO_BACKEND}.") endif() list(APPEND CRYPTO_SOURCES crypto/backend_version.cpp) # sha11collisiondetection sources list(APPEND CRYPTO_SOURCES crypto/hash_sha1cd.cpp crypto/sha1cd/sha1.c crypto/sha1cd/ubc_check.c) set(CRYPTO_REFRESH_SOURCES ) if(ENABLE_CRYPTO_REFRESH) list(APPEND CRYPTO_REFRESH_SOURCES ../librepgp/v2_seipd.cpp ) endif() add_library(librnp-obj OBJECT # librepgp ../librepgp/stream-armor.cpp ../librepgp/stream-common.cpp ../librepgp/stream-ctx.cpp ../librepgp/stream-dump.cpp ../librepgp/stream-key.cpp ../librepgp/stream-packet.cpp ../librepgp/stream-parse.cpp ../librepgp/stream-sig.cpp ../librepgp/stream-write.cpp ${CRYPTO_REFRESH_SOURCES} # librekey ../librekey/key_store_g10.cpp ../librekey/key_store_kbx.cpp ../librekey/key_store_pgp.cpp ../librekey/rnp_key_store.cpp # cryptography ${CRYPTO_SOURCES} # other sources sec_profile.cpp fingerprint.cpp key-provider.cpp logging.cpp json-utils.cpp utils.cpp pass-provider.cpp rawpacket.cpp sig_subpacket.cpp key_material.cpp enc_material.cpp sig_material.cpp signature.cpp userid.cpp keygen.cpp key.cpp rnp.cpp ) get_target_property(_comp_options librnp-obj COMPILE_OPTIONS) string(REGEX MATCH "\\-fsanitize=[a-z,]*undefined" _comp_sanitizers "${_comp_options}" "${CMAKE_C_FLAGS}") if (ENABLE_SANITIZERS OR _comp_sanitizers) # sha1cd attempts to use unaligned access for optimisations on intel CPUs # CFLAGS is checked as sanitizers may be enabled without CMake var set_source_files_properties(crypto/sha1cd/sha1.c PROPERTIES COMPILE_DEFINITIONS "SHA1DC_FORCE_ALIGNED_ACCESS" ) endif() set_target_properties(librnp-obj PROPERTIES POSITION_INDEPENDENT_CODE ON) target_include_directories(librnp-obj PUBLIC "$" "$" "$" "$" "$" PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" "${PROJECT_SOURCE_DIR}/src" "${SEXPP_INCLUDE_DIRS}" ) target_link_libraries(librnp-obj PRIVATE) if (CRYPTO_BACKEND_BOTAN) target_link_libraries(librnp-obj PRIVATE Botan::Botan) elseif (CRYPTO_BACKEND_OPENSSL) target_link_libraries(librnp-obj PRIVATE OpenSSL::Crypto) endif() target_link_libraries(librnp-obj PRIVATE ${SEXPP_TARGET}) set_target_properties(librnp-obj PROPERTIES CXX_VISIBILITY_PRESET hidden) if (TARGET BZip2::BZip2) target_link_libraries(librnp-obj PRIVATE BZip2::BZip2) endif() if (TARGET ZLIB::ZLIB) target_link_libraries(librnp-obj PRIVATE ZLIB::ZLIB) endif() if (BUILD_SHARED_LIBS) target_compile_definitions(librnp-obj PRIVATE librnp_EXPORTS) else() target_compile_definitions(librnp-obj PRIVATE RNP_STATIC) endif() add_library(librnp $ $) if (OpenSSL::applink) target_link_libraries(librnp PRIVATE OpenSSL::applink) endif(OpenSSL::applink) set_target_properties(librnp PROPERTIES VERSION "${RNP_VERSION}" SOVERSION "${RNP_VERSION_MAJOR}" OUTPUT_NAME "rnp" ) if (BUILD_SHARED_LIBS) add_library(librnp-static STATIC $ $) if (OpenSSL::applink) target_link_libraries(librnp-static PRIVATE OpenSSL::applink) endif(OpenSSL::applink) if (WIN32) set_target_properties(librnp-static PROPERTIES OUTPUT_NAME "rnp-static") else (WIN32) # On Unix like systems we will build/install/pack shared and static libraries librnp.so and librnp.a # On Windows we will build/install/pack dynamic, import and static libraries rnp.dll, rnp.lib and rnp-static.lib set_target_properties(librnp-static PROPERTIES OUTPUT_NAME "rnp") endif (WIN32) # Limit symbols export only to rnp_* functions. if (APPLE) # use -export_symbols_list on Apple OSs target_link_options(librnp PRIVATE -Wl,-exported_symbols_list "${CMAKE_CURRENT_SOURCE_DIR}/librnp.symbols") set_target_properties(librnp PROPERTIES LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/librnp.symbols") elseif(NOT WIN32) target_link_options(librnp PRIVATE "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/librnp.vsc") set_target_properties(librnp PROPERTIES LINK_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/librnp.vsc") endif() else() add_library(librnp-static ALIAS librnp) endif() foreach (prop LINK_LIBRARIES INTERFACE_LINK_LIBRARIES INCLUDE_DIRECTORIES INTERFACE_INCLUDE_DIRECTORIES) get_target_property(val librnp-obj ${prop}) if (BUILD_SHARED_LIBS) set_property(TARGET librnp-static PROPERTY ${prop} ${val}) list(REMOVE_ITEM val "$") set_property(TARGET librnp PROPERTY ${prop} ${val}) else() set_property(TARGET librnp PROPERTY ${prop} ${val}) endif() endforeach() set(EXPERIMENTAL_FEATURES "") if (ENABLE_CRYPTO_REFRESH) set(EXPERIMENTAL_FEATURES "${EXPERIMENTAL_FEATURES}\n#define RNP_EXPERIMENTAL_CRYPTO_REFRESH\n") endif() if(ENABLE_PQC) set(EXPERIMENTAL_FEATURES "${EXPERIMENTAL_FEATURES}\n#define RNP_EXPERIMENTAL_PQC\n") endif() if(NOT EXPERIMENTAL_FEATURES STREQUAL "") message(WARNING "One or more experimental features are enabled. Use it on your own risk.") endif() generate_export_header(librnp BASE_NAME rnp EXPORT_MACRO_NAME RNP_API EXPORT_FILE_NAME rnp/rnp_export.h STATIC_DEFINE RNP_STATIC INCLUDE_GUARD_NAME RNP_EXPORT CUSTOM_CONTENT_FROM_VARIABLE EXPERIMENTAL_FEATURES ) # This has precedence and can cause some confusion when the binary # dir one isn't actually being used. To be improved. if (NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) file(REMOVE "${CMAKE_CURRENT_SOURCE_DIR}/config.h") file(REMOVE "${CMAKE_CURRENT_SOURCE_DIR}/version.h") endif() if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.12.0") set(namelink_component NAMELINK_COMPONENT development) else() set(namelink_component) endif() # add these to the rnp-targets export # On Unix like systems we will build/install/pack either shared library librnp.so or static librnp.a # On Windows we will build/install/pack either dynamic and import libraries rnp.dll, rnp.lib or static library rnp-static.lib # If a client application uses shared rnp library, sexpp is statically linked to librnp.so and libsexpp.a is not installed # If a client application uses static rnp library, it still needs libsexpp.a and it is installed if (BUILD_SHARED_LIBS) install(TARGETS librnp EXPORT rnp-targets LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT runtime ${namelink_component} ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT development ) # install dll only for windows if (WIN32) install(TARGETS librnp RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" COMPONENT runtime ) endif(WIN32) else(BUILD_SHARED_LIBS) # static libraries # install libsexpp unless system-installed libsexpp is used if (SYSTEM_LIBSEXPP) install(TARGETS librnp EXPORT rnp-targets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT development ) else (SYSTEM_LIBSEXPP) install(TARGETS librnp sexpp EXPORT rnp-targets ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" COMPONENT development ) endif (SYSTEM_LIBSEXPP) endif(BUILD_SHARED_LIBS) # install headers install( FILES "${PROJECT_SOURCE_DIR}/include/rnp/rnp.h" COMPONENT development DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rnp" RENAME rnp.h ) install( FILES "${PROJECT_SOURCE_DIR}/include/rnp/rnp_err.h" COMPONENT development DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rnp" RENAME rnp_err.h ) install( FILES "${PROJECT_BINARY_DIR}/src/lib/rnp/rnp_export.h" COMPONENT development DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rnp" RENAME rnp_export.h ) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/version.h" COMPONENT development DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/rnp" RENAME rnp_ver.h ) # .cmake installs set(INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/rnp") install(EXPORT rnp-targets FILE rnp-targets.cmake NAMESPACE rnp:: DESTINATION "${INSTALL_CMAKEDIR}" COMPONENT development ) # Find modules shipped with rnp: the exported rnp targets reference the # imported targets they define (Botan::Botan), and rnp-config.cmake uses # them to resolve those dependencies downstream. # Some CI jobs delete the bundled modules to exercise the botan-config.cmake # path, so only install the ones that are actually present. set(_rnp_find_modules "") foreach(_module FindBotan.cmake) if(EXISTS "${PROJECT_SOURCE_DIR}/cmake/Modules/${_module}") list(APPEND _rnp_find_modules "${PROJECT_SOURCE_DIR}/cmake/Modules/${_module}") endif() endforeach() if(_rnp_find_modules) install( FILES ${_rnp_find_modules} DESTINATION "${INSTALL_CMAKEDIR}/modules" COMPONENT development ) endif() unset(_rnp_find_modules) # Dependencies that consumers resolve via find_package(rnp), emitted into # rnp-config.cmake (cmake/rnp-config.cmake.in). if (CRYPTO_BACKEND_BOTAN3) set(RNP_CONFIG_CRYPTO_DEPENDENCY "find_dependency(Botan 3.0.0)") elseif (CRYPTO_BACKEND_BOTAN) set(RNP_CONFIG_CRYPTO_DEPENDENCY "find_dependency(Botan 2.14.0)") elseif (CRYPTO_BACKEND_OPENSSL) set(RNP_CONFIG_CRYPTO_DEPENDENCY "find_dependency(OpenSSL 1.1.1)") endif() # BZip2 is optional, so consumers must resolve it only if librnp was built with it. if (BZIP2_FOUND) set(RNP_CONFIG_BZIP2_DEPENDENCY "find_dependency(BZip2)") else () set(RNP_CONFIG_BZIP2_DEPENDENCY "# BZip2 support was disabled in this build") endif () # A shared librnp has sexpp linked in, and a static librnp built with the # bundled sexpp is exported together with it (rnp::sexpp), so sexpp is an # external dependency only for a static librnp built with SYSTEM_LIBSEXPP. if (NOT BUILD_SHARED_LIBS AND SYSTEM_LIBSEXPP) set(RNP_CONFIG_SEXPP_DEPENDENCY "find_dependency(sexpp 0.8.7)") else () set(RNP_CONFIG_SEXPP_DEPENDENCY "# sexpp is linked into the shared library or exported as rnp::sexpp") endif () include(CMakePackageConfigHelpers) configure_package_config_file( "${PROJECT_SOURCE_DIR}/cmake/rnp-config.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/rnp-config.cmake" INSTALL_DESTINATION "${INSTALL_CMAKEDIR}" ) write_basic_package_version_file(rnp-config-version.cmake VERSION "${PROJECT_VERSION}" COMPATIBILITY SameMajorVersion ) install ( FILES "${CMAKE_CURRENT_BINARY_DIR}/rnp-config.cmake" "${CMAKE_CURRENT_BINARY_DIR}/rnp-config-version.cmake" DESTINATION "${INSTALL_CMAKEDIR}" COMPONENT development ) # Compute Libs.private for the pkg-config file from the link interface of # librnp, so that `pkg-config --static --libs librnp` lists the transitive # dependencies (crypto backend, zlib, bzip2 and, for a static # librnp, sexpp). set(LIBRNP_PRIVATE_LIBS "") get_target_property(_librnp_interface_libs librnp INTERFACE_LINK_LIBRARIES) foreach (_lib ${_librnp_interface_libs}) string(REGEX REPLACE "^\\$$" "\\1" _lib "${_lib}") if (TARGET ${_lib}) get_target_property(_imported ${_lib} IMPORTED) get_target_property(_location ${_lib} IMPORTED_LOCATION) if (_imported AND NOT _location) # some find modules only set a configuration-specific location get_target_property(_configs ${_lib} IMPORTED_CONFIGURATIONS) if (_configs) list(GET _configs 0 _config) get_target_property(_location ${_lib} IMPORTED_LOCATION_${_config}) endif () endif () if (_imported AND _location) get_filename_component(_libname "${_location}" NAME_WE) if (CMAKE_SHARED_LIBRARY_PREFIX) string(REGEX REPLACE "^${CMAKE_SHARED_LIBRARY_PREFIX}" "" _libname "${_libname}") endif () get_filename_component(_libdir "${_location}" DIRECTORY) string(APPEND LIBRNP_PRIVATE_LIBS " -L${_libdir} -l${_libname}") elseif (NOT _imported) # built together with librnp (bundled sexpp) and installed next to it get_target_property(_libname ${_lib} OUTPUT_NAME) if (NOT _libname) set(_libname "${_lib}") endif () string(APPEND LIBRNP_PRIVATE_LIBS " -l${_libname}") endif () else () string(APPEND LIBRNP_PRIVATE_LIBS " ${_lib}") endif () endforeach() string(STRIP "${LIBRNP_PRIVATE_LIBS}" LIBRNP_PRIVATE_LIBS) # create a pkgconfig .pc too find_package(PkgConfig) if (PKG_CONFIG_FOUND) get_target_property(LIBRNP_OUTPUT_NAME librnp OUTPUT_NAME) if(IS_ABSOLUTE "${CMAKE_INSTALL_LIBDIR}") set(PKGCONFIG_LIBDIR "${CMAKE_INSTALL_LIBDIR}") else() set(PKGCONFIG_LIBDIR "\${prefix}/${CMAKE_INSTALL_LIBDIR}") endif() if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") set(PKGCONFIG_INCLUDEDIR "${CMAKE_INSTALL_INCLUDEDIR}") else() set(PKGCONFIG_INCLUDEDIR "\${prefix}/${CMAKE_INSTALL_INCLUDEDIR}") endif() configure_file( "${PROJECT_SOURCE_DIR}/cmake/librnp.pc.in" "${PROJECT_BINARY_DIR}/librnp.pc" @ONLY ) install( FILES "${PROJECT_BINARY_DIR}/librnp.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig" COMPONENT development ) endif() # Build and install man page if (ENABLE_DOC) add_adoc_man("${CMAKE_CURRENT_SOURCE_DIR}/librnp.3.adoc" ${RNP_VERSION}) endif()