From f9f82e970568181803c02a82aae876f170a26085 Mon Sep 17 00:00:00 2001 From: Pat Long Date: Sat, 18 Feb 2023 16:38:39 -0500 Subject: [PATCH] Add optional support for Botan3 (#8994) --- CMakeLists.txt | 35 ++++++-- cmake/FindBotan3.cmake | 106 +++++++++++++++++++++++ share/translations/keepassxc_en.ts | 2 +- src/CMakeLists.txt | 2 +- src/browser/CMakeLists.txt | 2 +- src/config-keepassx.h.cmake | 1 + src/crypto/Crypto.cpp | 14 ++- src/fdosecrets/CMakeLists.txt | 2 +- src/fdosecrets/objects/SessionCipher.cpp | 17 ++++ src/keeshare/CMakeLists.txt | 2 +- src/proxy/CMakeLists.txt | 2 +- 11 files changed, 171 insertions(+), 14 deletions(-) create mode 100644 cmake/FindBotan3.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 06eba07a..869e3205 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -61,6 +61,7 @@ if(UNIX AND NOT APPLE) option(WITH_XC_FDOSECRETS "Implement freedesktop.org Secret Storage Spec server side API." OFF) endif() option(WITH_XC_DOCS "Enable building of documentation" ON) +option(KPXC_DEV_BOTAN3 "Build against Botan3" OFF) set(WITH_XC_X11 ON CACHE BOOL "Enable building with X11 deps") @@ -298,6 +299,10 @@ if(CMAKE_BUILD_TYPE_LOWER STREQUAL "debug") check_add_gcc_compiler_flag("-Wshadow-compatible-local") check_add_gcc_compiler_flag("-Wshadow-local") add_gcc_compiler_flags("-Werror") + # This is needed since compiling aginst Botan3 requires compiling against C++20 + if(KPXC_DEV_BOTAN3) + add_gcc_compiler_cxxflags("-Wno-error=deprecated-enum-enum-conversion -Wno-error=deprecated") + endif() endif() if (NOT HAIKU) @@ -343,7 +348,11 @@ if(UNIX AND NOT APPLE) endif() set(CMAKE_C_STANDARD 99) -set(CMAKE_CXX_STANDARD 17) +if(KPXC_DEV_BOTAN3) + set(CMAKE_CXX_STANDARD 20) +else() + set(CMAKE_CXX_STANDARD 17) +endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) check_cxx_compiler_flag("-fsized-deallocation" CXX_HAS_fsized_deallocation) @@ -529,12 +538,26 @@ endif() # Make sure we don't enable asserts there. set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_NONE QT_NO_DEBUG) -# Find Botan2 -find_package(Botan2 REQUIRED) -if(BOTAN2_VERSION VERSION_LESS "2.11.0") - message(FATAL_ERROR "Botan2 2.11.0 or higher is required") +if(KPXC_DEV_BOTAN3) + # Find Botan3 + find_package(Botan3 REQUIRED) + if(BOTAN3_VERSION VERSION_LESS "3.0.0") + message(FATAL_ERROR "Botan3 3.0.0 or higher is required") + endif() + set(BOTAN_VERSION "${BOTAN3_VERSION}") + set(BOTAN_INCLUDE_DIR "${BOTAN3_INCLUDE_DIR}") + set(BOTAN_LIBRARIES "${BOTAN3_LIBRARIES}") +else() + # Find Botan2 + find_package(Botan2 REQUIRED) + if(BOTAN2_VERSION VERSION_LESS "2.11.0") + message(FATAL_ERROR "Botan2 2.11.0 or higher is required") + endif() + set(BOTAN_VERSION "${BOTAN2_VERSION}") + set(BOTAN_INCLUDE_DIR "${BOTAN2_INCLUDE_DIR}") + set(BOTAN_LIBRARIES "${BOTAN2_LIBRARIES}") endif() -include_directories(SYSTEM ${BOTAN2_INCLUDE_DIR}) +include_directories(SYSTEM ${BOTAN_INCLUDE_DIR}) # Find Argon2 -- Botan 2.18 and below does not support threaded Argon2 find_library(ARGON2_LIBRARIES NAMES argon2) find_path(ARGON2_INCLUDE_DIR NAMES argon2.h PATH_SUFFIXES local/include) diff --git a/cmake/FindBotan3.cmake b/cmake/FindBotan3.cmake new file mode 100644 index 00000000..f59eb414 --- /dev/null +++ b/cmake/FindBotan3.cmake @@ -0,0 +1,106 @@ +# Copyright (c) 2018 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. + +#.rst: +# FindBotan3 +# ----------- +# +# Find the botan-3 library. +# +# IMPORTED Targets +# ^^^^^^^^^^^^^^^^ +# +# This module defines :prop_tgt:`IMPORTED` targets: +# +# ``Botan3::Botan3`` +# The botan-3 library, if found. +# +# Result variables +# ^^^^^^^^^^^^^^^^ +# +# This module defines the following variables: +# +# :: +# +# BOTAN3_FOUND - true if the headers and library were found +# BOTAN3_INCLUDE_DIRS - where to find headers +# BOTAN3_LIBRARIES - list of libraries to link +# BOTAN3_VERSION - library version that was found, if any + +# find the headers +find_path(BOTAN3_INCLUDE_DIR + NAMES botan/version.h + PATH_SUFFIXES botan-3 +) + +# find the library +find_library(BOTAN3_LIBRARY NAMES botan-3 libbotan-3 botan) + +# determine the version +if(BOTAN3_INCLUDE_DIR AND EXISTS "${BOTAN3_INCLUDE_DIR}/botan/build.h") + file(STRINGS "${BOTAN3_INCLUDE_DIR}/botan/build.h" botan3_version_str + REGEX "^#define[\t ]+(BOTAN_VERSION_[A-Z]+)[\t ]+[0-9]+") + + string(REGEX REPLACE ".*#define[\t ]+BOTAN_VERSION_MAJOR[\t ]+([0-9]+).*" + "\\1" _botan3_version_major "${botan3_version_str}") + string(REGEX REPLACE ".*#define[\t ]+BOTAN_VERSION_MINOR[\t ]+([0-9]+).*" + "\\1" _botan3_version_minor "${botan3_version_str}") + string(REGEX REPLACE ".*#define[\t ]+BOTAN_VERSION_PATCH[\t ]+([0-9]+).*" + "\\1" _botan3_version_patch "${botan3_version_str}") + set(BOTAN3_VERSION "${_botan3_version_major}.${_botan3_version_minor}.${_botan3_version_patch}" + CACHE INTERNAL "The version of Botan which was detected") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Botan3 + REQUIRED_VARS BOTAN3_LIBRARY BOTAN3_INCLUDE_DIR + VERSION_VAR BOTAN3_VERSION +) + +if(BOTAN3_FOUND) + set(BOTAN3_INCLUDE_DIRS ${BOTAN3_INCLUDE_DIR} ${PC_BOTAN3_INCLUDE_DIRS}) + set(BOTAN3_LIBRARIES ${BOTAN3_LIBRARY}) +endif() + +if(BOTAN3_FOUND AND NOT TARGET Botan3::Botan3) + # create the new library target + add_library(Botan3::Botan3 UNKNOWN IMPORTED) + # set the required include dirs for the target + if(BOTAN3_INCLUDE_DIRS) + set_target_properties(Botan3::Botan3 + PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${BOTAN3_INCLUDE_DIRS}" + ) + endif() + # set the required libraries for the target + if(EXISTS "${BOTAN3_LIBRARY}") + set_target_properties(Botan3::Botan3 + PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${BOTAN3_LIBRARY}" + ) + endif() +endif() + +mark_as_advanced(BOTAN3_INCLUDE_DIR BOTAN3_LIBRARY) diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index 62db8cf5..af7b9de6 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -7594,7 +7594,7 @@ Kernel: %3 %4 - Botan library must be at least 2.11.x, found %1.%2.%3 + Botan library must be at least %1, found %2.%3.%4 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3a589842..8b5c4b58 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -338,7 +338,7 @@ target_link_libraries(keepassx_core Qt5::Concurrent Qt5::Network Qt5::Widgets - ${BOTAN2_LIBRARIES} + ${BOTAN_LIBRARIES} ${PCSC_LIBRARIES} ${ZXCVBN_LIBRARIES} ${ZLIB_LIBRARIES} diff --git a/src/browser/CMakeLists.txt b/src/browser/CMakeLists.txt index 98715cb1..9bd05385 100755 --- a/src/browser/CMakeLists.txt +++ b/src/browser/CMakeLists.txt @@ -33,5 +33,5 @@ if(WITH_XC_BROWSER) ) add_library(keepassxcbrowser STATIC ${keepassxcbrowser_SOURCES}) - target_link_libraries(keepassxcbrowser Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network ${BOTAN2_LIBRARIES}) + target_link_libraries(keepassxcbrowser Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network ${BOTAN_LIBRARIES}) endif() diff --git a/src/config-keepassx.h.cmake b/src/config-keepassx.h.cmake index a67b1b3a..92605588 100644 --- a/src/config-keepassx.h.cmake +++ b/src/config-keepassx.h.cmake @@ -14,6 +14,7 @@ #cmakedefine WITH_XC_AUTOTYPE #cmakedefine WITH_XC_NETWORKING +#cmakedefine KPXC_DEV_BOTAN3 #cmakedefine WITH_XC_BROWSER #cmakedefine WITH_XC_YUBIKEY #cmakedefine WITH_XC_SSHAGENT diff --git a/src/crypto/Crypto.cpp b/src/crypto/Crypto.cpp index 85d84649..c5972d82 100644 --- a/src/crypto/Crypto.cpp +++ b/src/crypto/Crypto.cpp @@ -18,6 +18,8 @@ #include "Crypto.h" +#include "config-keepassx.h" + #include "crypto/CryptoHash.h" #include "crypto/SymmetricCipher.h" @@ -237,8 +239,16 @@ namespace Crypto { bool init() { - if (Botan::version_major() != 2 || Botan::version_minor() < 11) { - g_cryptoError = QObject::tr("Botan library must be at least 2.11.x, found %1.%2.%3") +#ifdef KPXC_DEV_BOTAN3 + unsigned int version_major = 3, min_version_minor = 0; + QString versionString = "3.x"; +#else + unsigned int version_major = 2, min_version_minor = 11; + QString versionString = "2.11.x"; +#endif + if (Botan::version_major() != version_major || Botan::version_minor() < min_version_minor) { + g_cryptoError = QObject::tr("Botan library must be at least %1, found %2.%3.%4") + .arg(versionString) .arg(Botan::version_major()) .arg(Botan::version_minor()) .arg(Botan::version_patch()); diff --git a/src/fdosecrets/CMakeLists.txt b/src/fdosecrets/CMakeLists.txt index fec3f99d..ecdac8dc 100644 --- a/src/fdosecrets/CMakeLists.txt +++ b/src/fdosecrets/CMakeLists.txt @@ -31,5 +31,5 @@ if(WITH_XC_FDOSECRETS) objects/Prompt.cpp dbus/DBusTypes.cpp ) - target_link_libraries(fdosecrets Qt5::Core Qt5::Widgets Qt5::DBus ${BOTAN2_LIBRARIES}) + target_link_libraries(fdosecrets Qt5::Core Qt5::Widgets Qt5::DBus ${BOTAN_LIBRARIES}) endif() diff --git a/src/fdosecrets/objects/SessionCipher.cpp b/src/fdosecrets/objects/SessionCipher.cpp index 147e820b..8bc526a1 100644 --- a/src/fdosecrets/objects/SessionCipher.cpp +++ b/src/fdosecrets/objects/SessionCipher.cpp @@ -17,12 +17,19 @@ #include "SessionCipher.h" +#include "config-keepassx.h" + #include "crypto/Random.h" #include "crypto/SymmetricCipher.h" #include #include + +#ifdef KPXC_DEV_BOTAN3 +#include +#else #include +#endif namespace FdoSecrets { @@ -50,6 +57,15 @@ namespace FdoSecrets try { Botan::secure_vector salt(32, '\0'); +#ifdef KPXC_DEV_BOTAN3 + Botan::PK_Key_Agreement dhka(*m_privateKey, *randomGen()->getRng(), "HKDF(SHA-256)", ""); + auto aesKey = dhka.derive_key(16, + reinterpret_cast(clientPublicKey.constData()), + clientPublicKey.size(), + salt.data(), + salt.size()); + m_aesKey = QByteArray(reinterpret_cast(aesKey.begin()), aesKey.size()); +#else auto dhka = m_privateKey->create_key_agreement_op(*randomGen()->getRng(), "HKDF(SHA-256)", ""); auto aesKey = dhka->agree(16, reinterpret_cast(clientPublicKey.constData()), @@ -57,6 +73,7 @@ namespace FdoSecrets salt.data(), salt.size()); m_aesKey = QByteArray(reinterpret_cast(aesKey.data()), aesKey.size()); +#endif return true; } catch (std::exception& e) { qCritical("Failed to update client public key: %s", e.what()); diff --git a/src/keeshare/CMakeLists.txt b/src/keeshare/CMakeLists.txt index a108b784..3adf272a 100644 --- a/src/keeshare/CMakeLists.txt +++ b/src/keeshare/CMakeLists.txt @@ -16,6 +16,6 @@ if(WITH_XC_KEESHARE) find_package(Minizip REQUIRED) add_library(keeshare STATIC ${keeshare_SOURCES}) - target_link_libraries(keeshare PUBLIC Qt5::Core Qt5::Widgets ${BOTAN2_LIBRARIES} ${ZLIB_LIBRARIES} PRIVATE ${MINIZIP_LIBRARIES}) + target_link_libraries(keeshare PUBLIC Qt5::Core Qt5::Widgets ${BOTAN_LIBRARIES} ${ZLIB_LIBRARIES} PRIVATE ${MINIZIP_LIBRARIES}) include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) endif(WITH_XC_KEESHARE) diff --git a/src/proxy/CMakeLists.txt b/src/proxy/CMakeLists.txt index 15137f5b..be756672 100755 --- a/src/proxy/CMakeLists.txt +++ b/src/proxy/CMakeLists.txt @@ -21,7 +21,7 @@ if(WITH_XC_BROWSER) # Alloc must be defined in a static library to prevent clashing with clang ASAN definitions add_library(proxy_alloc STATIC ../core/Alloc.cpp) - target_link_libraries(proxy_alloc PRIVATE Qt5::Core ${BOTAN2_LIBRARIES}) + target_link_libraries(proxy_alloc PRIVATE Qt5::Core ${BOTAN_LIBRARIES}) add_executable(keepassxc-proxy ${proxy_SOURCES}) target_link_libraries(keepassxc-proxy proxy_alloc Qt5::Core Qt5::Network)