diff --git a/CMakeLists.txt b/CMakeLists.txt index b679cb254..5e82f452f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -86,12 +86,6 @@ if(WITH_XC_ALL) endif() endif() -if(WITH_XC_SSHAGENT OR WITH_XC_KEESHARE) - set(WITH_XC_CRYPTO_SSH ON) -else() - set(WITH_XC_CRYPTO_SSH OFF) -endif() - # Prefer WITH_XC_NETWORKING setting over WITH_XC_UPDATECHECK if(NOT WITH_XC_NETWORKING AND WITH_XC_UPDATECHECK) message(STATUS "Disabling WITH_XC_UPDATECHECK because WITH_XC_NETWORKING is disabled") @@ -250,6 +244,7 @@ if(WITH_APP_BUNDLE) endif() add_gcc_compiler_flags("-fno-common") +check_add_gcc_compiler_flag("-fopenmp") add_gcc_compiler_flags("-Wall -Wextra -Wundef -Wpointer-arith -Wno-long-long") add_gcc_compiler_flags("-Wformat=2 -Wmissing-format-attribute") add_gcc_compiler_flags("-fvisibility=hidden") @@ -269,7 +264,6 @@ else() endif() endif() -add_gcc_compiler_cxxflags("-fno-exceptions -fno-rtti") add_gcc_compiler_cxxflags("-Wnon-virtual-dtor -Wold-style-cast -Woverloaded-virtual") add_gcc_compiler_cflags("-Wchar-subscripts -Wwrite-strings") @@ -324,7 +318,7 @@ if(APPLE AND CMAKE_COMPILER_IS_CLANGXX) endif() if(WITH_DEV_BUILD) - add_definitions(-DQT_DEPRECATED_WARNINGS -DGCRYPT_NO_DEPRECATED) + add_definitions(-DQT_DEPRECATED_WARNINGS) else() add_definitions(-DQT_NO_DEPRECATED_WARNINGS) add_gcc_compiler_cxxflags("-Wno-deprecated-declarations") @@ -447,20 +441,22 @@ endif() # Make sure we don't enable asserts there. set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_NONE QT_NO_DEBUG) -find_package(LibGPGError REQUIRED) -find_package(Gcrypt 1.7.0 REQUIRED) -find_package(Argon2 REQUIRED) +# 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() +include_directories(SYSTEM ${BOTAN2_INCLUDE_DIR}) + +# Find zlib find_package(ZLIB REQUIRED) -find_package(QREncode REQUIRED) -find_package(sodium 1.0.12 REQUIRED) - -set(CMAKE_REQUIRED_INCLUDES ${ZLIB_INCLUDE_DIR}) - if(ZLIB_VERSION_STRING VERSION_LESS "1.2.0") message(FATAL_ERROR "zlib 1.2.0 or higher is required to use the gzip format") endif() +include_directories(SYSTEM ${ZLIB_INCLUDE_DIR}) -include_directories(SYSTEM ${ARGON2_INCLUDE_DIR} ${sodium_INCLUDE_DIR}) +# QREncode required for TOTP +find_package(QREncode REQUIRED) # Optional if(WITH_XC_YUBIKEY) @@ -499,7 +495,7 @@ if(UNIX) endif() endif() -include_directories(SYSTEM ${GCRYPT_INCLUDE_DIR} ${ZLIB_INCLUDE_DIR}) +include_directories(SYSTEM ${ZLIB_INCLUDE_DIR}) add_subdirectory(src) add_subdirectory(share) diff --git a/COPYING b/COPYING index c18b82201..1ec6980e1 100644 --- a/COPYING +++ b/COPYING @@ -46,6 +46,10 @@ Files: cmake/FindYubiKey.cmake Copyright: 2014 Kyle Manna License: GPL-2 or GPL-3 +Files: cmake/FindBotan2.cmake +Copyright: 2018 Ribose Inc. +License: BSD-2-clause + Files: cmake/GenerateProductVersion.cmake Copyright: 2015 halex2005 License: MIT diff --git a/cmake/FindBotan2.cmake b/cmake/FindBotan2.cmake new file mode 100644 index 000000000..a7564f542 --- /dev/null +++ b/cmake/FindBotan2.cmake @@ -0,0 +1,121 @@ +# 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: +# FindBotan2 +# ----------- +# +# Find the botan-2 library. +# +# IMPORTED Targets +# ^^^^^^^^^^^^^^^^ +# +# This module defines :prop_tgt:`IMPORTED` targets: +# +# ``Botan2::Botan2`` +# The botan-2 library, if found. +# +# Result variables +# ^^^^^^^^^^^^^^^^ +# +# This module defines the following variables: +# +# :: +# +# BOTAN2_FOUND - true if the headers and library were found +# BOTAN2_INCLUDE_DIRS - where to find headers +# BOTAN2_LIBRARIES - list of libraries to link +# BOTAN2_VERSION - library version that was found, if any + +# use pkg-config to get the directories and then use these values +# in the find_path() and find_library() calls +find_package(PkgConfig QUIET) +pkg_check_modules(PC_BOTAN2 QUIET botan-2) + +# find the headers +find_path(BOTAN2_INCLUDE_DIR + NAMES botan/version.h + HINTS + ${PC_BOTAN2_INCLUDEDIR} + ${PC_BOTAN2_INCLUDE_DIRS} + PATH_SUFFIXES botan-2 +) + +# find the library +find_library(BOTAN2_LIBRARY + NAMES botan-2 libbotan-2 + HINTS + ${PC_BOTAN2_LIBDIR} + ${PC_BOTAN2_LIBRARY_DIRS} +) + +# determine the version +if(PC_BOTAN2_VERSION) + set(BOTAN2_VERSION ${PC_BOTAN2_VERSION}) +elseif(BOTAN2_INCLUDE_DIR AND EXISTS "${BOTAN2_INCLUDE_DIR}/botan/build.h") + file(STRINGS "${BOTAN2_INCLUDE_DIR}/botan/build.h" botan2_version_str + REGEX "^#define[\t ]+(BOTAN_VERSION_[A-Z]+)[\t ]+[0-9]+") + + string(REGEX REPLACE ".*#define[\t ]+BOTAN_VERSION_MAJOR[\t ]+([0-9]+).*" + "\\1" _botan2_version_major "${botan2_version_str}") + string(REGEX REPLACE ".*#define[\t ]+BOTAN_VERSION_MINOR[\t ]+([0-9]+).*" + "\\1" _botan2_version_minor "${botan2_version_str}") + string(REGEX REPLACE ".*#define[\t ]+BOTAN_VERSION_PATCH[\t ]+([0-9]+).*" + "\\1" _botan2_version_patch "${botan2_version_str}") + set(BOTAN2_VERSION "${_botan2_version_major}.${_botan2_version_minor}.${_botan2_version_patch}" + CACHE INTERNAL "The version of Botan which was detected") +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Botan2 + REQUIRED_VARS BOTAN2_LIBRARY BOTAN2_INCLUDE_DIR + VERSION_VAR BOTAN2_VERSION +) + +if (BOTAN2_FOUND) + set(BOTAN2_INCLUDE_DIRS ${BOTAN2_INCLUDE_DIR} ${PC_BOTAN2_INCLUDE_DIRS}) + set(BOTAN2_LIBRARIES ${BOTAN2_LIBRARY}) +endif() + +if (BOTAN2_FOUND AND NOT TARGET Botan2::Botan2) + # create the new library target + add_library(Botan2::Botan2 UNKNOWN IMPORTED) + # set the required include dirs for the target + if (BOTAN2_INCLUDE_DIRS) + set_target_properties(Botan2::Botan2 + PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${BOTAN2_INCLUDE_DIRS}" + ) + endif() + # set the required libraries for the target + if (EXISTS "${BOTAN2_LIBRARY}") + set_target_properties(Botan2::Botan2 + PROPERTIES + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${BOTAN2_LIBRARY}" + ) + endif() +endif() + +mark_as_advanced(BOTAN2_INCLUDE_DIR BOTAN2_LIBRARY) diff --git a/share/translations/keepassx_en_US.ts b/share/translations/keepassx_en_US.ts index 7fb1fed78..9daa32271 100644 --- a/share/translations/keepassx_en_US.ts +++ b/share/translations/keepassx_en_US.ts @@ -7477,7 +7477,7 @@ Kernel: %3 %4 Do you want to trust %1 with the fingerprint of %2 from %3? - Do you want to trust %1 with the fingerprint of %2 from %3? {1 ?} {2 ?} + Do you want to trust %1 with the fingerprint of %2 from %3? Not this time diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e3c764e0c..8b98f21c3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -67,7 +67,6 @@ set(keepassx_SOURCES crypto/CryptoHash.cpp crypto/Random.cpp crypto/SymmetricCipher.cpp - crypto/SymmetricCipherGcrypt.cpp crypto/kdf/Kdf.cpp crypto/kdf/AesKdf.cpp crypto/kdf/Argon2Kdf.cpp @@ -182,7 +181,6 @@ set(keepassx_SOURCES keys/FileKey.cpp keys/PasswordKey.cpp keys/YkChallengeResponseKey.cpp - keys/YkChallengeResponseKeyCLI.cpp streams/HashedBlockStream.cpp streams/HmacBlockStream.cpp streams/LayeredStream.cpp @@ -247,11 +245,6 @@ add_subdirectory(cli) add_subdirectory(qrcode) set(qrcode_LIB qrcode) -add_subdirectory(crypto/ssh) -if(WITH_XC_CRYPTO_SSH) - set(crypto_ssh_LIB crypto_ssh) -endif() - add_subdirectory(keeshare) if(WITH_XC_KEESHARE) set(keeshare_LIB keeshare) @@ -322,10 +315,9 @@ target_link_libraries(keepassx_core Qt5::Concurrent Qt5::Network Qt5::Widgets - ${sodium_LIBRARY_RELEASE} + ${BOTAN2_LIBRARIES} ${YUBIKEY_LIBRARIES} ${ZXCVBN_LIBRARIES} - ${ARGON2_LIBRARIES} ${ZLIB_LIBRARIES} ) @@ -370,7 +362,6 @@ if(MINGW) endif() add_executable(${PROGNAME} WIN32 ${keepassx_SOURCES_MAINEXE} ${WIN32_ProductVersionFiles}) -target_link_libraries(keepassx_core ${GCRYPT_LIBRARIES} ${GPGERROR_LIBRARIES}) target_link_libraries(${PROGNAME} keepassx_core) set_target_properties(${PROGNAME} PROPERTIES ENABLE_EXPORTS ON) @@ -497,7 +488,7 @@ if(MINGW) find_file(CRYPTO_DLL NAMES libcrypto-1_1.dll libcrypto-1_1-x64.dll) if (NOT CRYPTO_DLL) - message(FATAL_ERROR "Cannot find libcrypto dll, ensure libgcrypt is properly installed.") + message(FATAL_ERROR "Cannot find libcrypto dll, ensure openssl is properly installed.") endif() install(FILES ${OPENSSL_DLL} ${CRYPTO_DLL} DESTINATION ".") diff --git a/src/browser/BrowserAction.cpp b/src/browser/BrowserAction.cpp index 65fe4cb7b..aa8fafa1f 100644 --- a/src/browser/BrowserAction.cpp +++ b/src/browser/BrowserAction.cpp @@ -24,9 +24,9 @@ #include #include -#include -#include -#include +#include + +using namespace Botan::Sodium; namespace { @@ -283,7 +283,7 @@ QJsonObject BrowserAction::handleGetLogins(const QJsonObject& json, const QStrin const QString id = decrypted.value("id").toString(); const QString formUrl = decrypted.value("submitUrl").toString(); const QString auth = decrypted.value("httpAuth").toString(); - const bool httpAuth = auth.compare(TRUE_STR, Qt::CaseSensitive) == 0 ? true : false; + const bool httpAuth = auth.compare(TRUE_STR, Qt::CaseSensitive) == 0; const QJsonArray users = browserService()->findMatchingEntries(id, siteUrl, formUrl, "", keyList, httpAuth); if (users.isEmpty()) { diff --git a/src/browser/BrowserHost.cpp b/src/browser/BrowserHost.cpp index ac4e996c3..ee3bc5c57 100644 --- a/src/browser/BrowserHost.cpp +++ b/src/browser/BrowserHost.cpp @@ -25,7 +25,6 @@ #include #include -#include "sodium.h" #include #ifdef Q_OS_WIN @@ -53,11 +52,6 @@ BrowserHost::~BrowserHost() void BrowserHost::start() { - if (sodium_init() == -1) { - qWarning() << "Failed to start browser service: libsodium failed to initialize!"; - return; - } - if (!m_localServer->isListening()) { m_localServer->listen(BrowserShared::localServerPath()); } diff --git a/src/browser/CMakeLists.txt b/src/browser/CMakeLists.txt index bb92511bc..d5833cfc4 100755 --- a/src/browser/CMakeLists.txt +++ b/src/browser/CMakeLists.txt @@ -32,5 +32,5 @@ if(WITH_XC_BROWSER) Variant.cpp) add_library(keepassxcbrowser STATIC ${keepassxcbrowser_SOURCES}) - target_link_libraries(keepassxcbrowser Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network ${sodium_LIBRARY_RELEASE}) + target_link_libraries(keepassxcbrowser Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Network ${BOTAN2_LIBRARIES}) endif() diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index 10507b0ac..7f058312d 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -54,13 +54,7 @@ add_executable(keepassxc-cli keepassxc-cli.cpp) target_link_libraries(keepassxc-cli ${GPGERROR_LIBRARIES} cli - keepassx_core - Qt5::Core - ${GCRYPT_LIBRARIES} - ${sodium_LIBRARY_RELEASE} - ${ARGON2_LIBRARIES} - ${ZLIB_LIBRARIES} - ${ZXCVBN_LIBRARIES}) + keepassx_core) install(TARGETS keepassxc-cli BUNDLE DESTINATION . COMPONENT Runtime diff --git a/src/cli/Utils.cpp b/src/cli/Utils.cpp index ca440a393..a41649873 100644 --- a/src/cli/Utils.cpp +++ b/src/cli/Utils.cpp @@ -18,7 +18,7 @@ #include "Utils.h" #ifdef WITH_XC_YUBIKEY -#include "keys/YkChallengeResponseKeyCLI.h" +#include "keys/YkChallengeResponseKey.h" #endif #ifdef Q_OS_WIN @@ -165,9 +165,14 @@ namespace Utils } } - auto key = QSharedPointer(new YkChallengeResponseKeyCLI( - {serial, slot}, QObject::tr("Please touch the button on your YubiKey to continue…"), err)); + auto conn = QObject::connect(YubiKey::instance(), &YubiKey::userInteractionRequest, [&] { + err << QObject::tr("Please touch the button on your YubiKey to continue…") << "\n\n" << flush; + }); + + auto key = QSharedPointer(new YkChallengeResponseKey({serial, slot})); compositeKey->addChallengeResponseKey(key); + + QObject::disconnect(conn); } #else Q_UNUSED(yubiKeySlot); diff --git a/src/core/Alloc.cpp b/src/core/Alloc.cpp index 6c798e728..525a7e5c6 100644 --- a/src/core/Alloc.cpp +++ b/src/core/Alloc.cpp @@ -16,9 +16,9 @@ */ #include +#include #include #include -#include #if defined(Q_OS_MACOS) #include #elif defined(Q_OS_FREEBSD) @@ -43,7 +43,7 @@ void operator delete(void* ptr, std::size_t size) noexcept return; } - sodium_memzero(ptr, size); + Botan::secure_scrub_memory(ptr, size); std::free(ptr); } diff --git a/src/core/Tools.cpp b/src/core/Tools.cpp index 7e2b65bcd..d92d1b4a8 100644 --- a/src/core/Tools.cpp +++ b/src/core/Tools.cpp @@ -352,30 +352,4 @@ namespace Tools return subbed; } - - Buffer::Buffer() - : raw(nullptr) - , size(0) - { - } - - Buffer::~Buffer() - { - clear(); - } - - void Buffer::clear() - { - if (size > 0) { - free(raw); - } - raw = nullptr; - size = 0; - } - - QByteArray Buffer::content() const - { - return QByteArray(reinterpret_cast(raw), size); - } - } // namespace Tools diff --git a/src/core/Tools.h b/src/core/Tools.h index e56a25189..c7bf5093f 100644 --- a/src/core/Tools.h +++ b/src/core/Tools.h @@ -64,34 +64,6 @@ namespace Tools } } - template struct Map - { - QMap values; - Value& operator[](const Key index) - { - return values[index]; - } - - ~Map() - { - for (Value m : values) { - deleter(m); - } - } - }; - - struct Buffer - { - unsigned char* raw; - size_t size; - - Buffer(); - ~Buffer(); - - void clear(); - QByteArray content() const; - }; - inline int qtRuntimeVersion() { // Cache the result since the Qt version can't change during diff --git a/src/crypto/Crypto.cpp b/src/crypto/Crypto.cpp index fb9f28bd9..85d846499 100644 --- a/src/crypto/Crypto.cpp +++ b/src/crypto/Crypto.cpp @@ -1,4 +1,5 @@ /* + * Copyright (C) 2021 KeePassXC Team * Copyright (C) 2010 Felix Geyer * * This program is free software: you can redistribute it and/or modify @@ -17,328 +18,249 @@ #include "Crypto.h" -#include - -#include - -#include "config-keepassx.h" #include "crypto/CryptoHash.h" #include "crypto/SymmetricCipher.h" -bool Crypto::m_initialized(false); -QString Crypto::m_errorStr; -QString Crypto::m_backendVersion; +#include -Crypto::Crypto() +namespace { -} + QString g_cryptoError; + + bool testSha256() + { + if (CryptoHash::hash("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", CryptoHash::Sha256) + != QByteArray::fromHex("248D6A61D20638B8E5C026930C3E6039A33CE45964FF2167F6ECEDD419DB06C1")) { + g_cryptoError = "SHA-256 mismatch."; + return false; + } -bool Crypto::init() -{ - if (m_initialized) { - qWarning("Crypto::init: already initialized"); return true; } - m_backendVersion = QString::fromLocal8Bit(gcry_check_version(0)); - gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0); + bool testSha512() + { + if (CryptoHash::hash("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", CryptoHash::Sha512) + != QByteArray::fromHex("204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c335" + "96fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445")) { + g_cryptoError = "SHA-512 mismatch."; + return false; + } - if (!checkAlgorithms()) { - return false; + return true; } - // has to be set before testing Crypto classes - m_initialized = true; + bool testAes256Cbc() + { + QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d7781" + "1f352c073b6108d72d9810a30914dff4"); + QByteArray iv = QByteArray::fromHex("000102030405060708090a0b0c0d0e0f"); + QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51"); + QByteArray cipherText = QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd6" + "9cfc4e967edb808d679f777bc6702c7d"); - if (!backendSelfTest() || !selfTest()) { - m_initialized = false; - return false; + QByteArray data = plainText; + SymmetricCipher aes256; + if (!aes256.init(SymmetricCipher::Aes256_CBC, SymmetricCipher::Encrypt, key, iv)) { + g_cryptoError = aes256.errorString(); + return false; + } + if (!aes256.process(data)) { + g_cryptoError = aes256.errorString(); + return false; + } + if (data != cipherText) { + g_cryptoError = "AES-256 CBC encryption mismatch."; + return false; + } + + if (!aes256.init(SymmetricCipher::Aes256_CBC, SymmetricCipher::Decrypt, key, iv)) { + g_cryptoError = aes256.errorString(); + return false; + } + if (!aes256.process(data)) { + g_cryptoError = aes256.errorString(); + return false; + } + if (data != plainText) { + g_cryptoError = "AES-256 CBC decryption mismatch."; + return false; + } + + return true; } - return true; -} + bool testAesKdf() + { + QByteArray key = QByteArray::fromHex("000102030405060708090A0B0C0D0E0F" + "101112131415161718191A1B1C1D1E1F"); + QByteArray plainText = QByteArray::fromHex("00112233445566778899AABBCCDDEEFF"); + QByteArray cipherText = QByteArray::fromHex("8EA2B7CA516745BFEAFC49904B496089"); -bool Crypto::initialized() + if (!SymmetricCipher::aesKdf(key, 1, plainText)) { + g_cryptoError = "AES KDF Failed."; + } + + if (plainText != cipherText) { + g_cryptoError = "AES KDF encryption mismatch."; + return false; + } + + return true; + } + + bool testTwofish() + { + QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d7781" + "1f352c073b6108d72d9810a30914dff4"); + QByteArray iv = QByteArray::fromHex("000102030405060708090a0b0c0d0e0f"); + QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a" + "ae2d8a571e03ac9c9eb76fac45af8e51"); + QByteArray cipherText = QByteArray::fromHex("e0227c3cc80f3cb1b2ed847cc6f57d3c" + "657b1e7960b30fb7c8d62e72ae37c3a0"); + QByteArray data = plainText; + SymmetricCipher twofish; + if (!twofish.init(SymmetricCipher::Twofish_CBC, SymmetricCipher::Encrypt, key, iv)) { + g_cryptoError = twofish.errorString(); + return false; + } + if (!twofish.process(data)) { + g_cryptoError = twofish.errorString(); + return false; + } + if (data != cipherText) { + g_cryptoError = "Twofish encryption mismatch."; + return false; + } + + if (!twofish.init(SymmetricCipher::Twofish_CBC, SymmetricCipher::Decrypt, key, iv)) { + g_cryptoError = twofish.errorString(); + return false; + } + if (!twofish.process(data)) { + g_cryptoError = twofish.errorString(); + return false; + } + if (data != plainText) { + g_cryptoError = "Twofish encryption mismatch."; + return false; + } + + return true; + } + + bool testSalsa20() + { + QByteArray salsa20Key = QByteArray::fromHex("F3F4F5F6F7F8F9FAFBFCFDFEFF000102" + "030405060708090A0B0C0D0E0F101112"); + QByteArray salsa20iv = QByteArray::fromHex("0000000000000000"); + QByteArray salsa20Plain = QByteArray::fromHex("00000000000000000000000000000000"); + QByteArray salsa20Cipher = QByteArray::fromHex("B4C0AFA503BE7FC29A62058166D56F8F"); + + QByteArray data = salsa20Plain; + SymmetricCipher salsa20Stream; + if (!salsa20Stream.init(SymmetricCipher::Salsa20, SymmetricCipher::Encrypt, salsa20Key, salsa20iv)) { + g_cryptoError = salsa20Stream.errorString(); + return false; + } + if (!salsa20Stream.process(data)) { + g_cryptoError = salsa20Stream.errorString(); + return false; + } + if (data != salsa20Cipher) { + g_cryptoError = "Salsa20 stream cipher encrypt mismatch."; + return false; + } + + if (!salsa20Stream.init(SymmetricCipher::Salsa20, SymmetricCipher::Decrypt, salsa20Key, salsa20iv)) { + g_cryptoError = salsa20Stream.errorString(); + return false; + } + if (!salsa20Stream.process(data)) { + g_cryptoError = salsa20Stream.errorString(); + return false; + } + if (data != salsa20Plain) { + g_cryptoError = "Salsa20 stream cipher decrypt mismatch."; + return false; + } + + return true; + } + + bool testChaCha20() + { + QByteArray chacha20Key = QByteArray::fromHex("00000000000000000000000000000000" + "00000000000000000000000000000000"); + QByteArray chacha20iv = QByteArray::fromHex("0000000000000000"); + QByteArray chacha20Plain = + QByteArray::fromHex("0000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000000000000"); + QByteArray chacha20Cipher = + QByteArray::fromHex("76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7" + "da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"); + + QByteArray data = chacha20Plain; + SymmetricCipher chacha20Stream; + if (!chacha20Stream.init(SymmetricCipher::ChaCha20, SymmetricCipher::Encrypt, chacha20Key, chacha20iv)) { + g_cryptoError = chacha20Stream.errorString(); + return false; + } + if (!chacha20Stream.process(data)) { + g_cryptoError = chacha20Stream.errorString(); + return false; + } + if (data != chacha20Cipher) { + g_cryptoError = "ChaCha20 stream cipher encrypt mismatch."; + return false; + } + + if (!chacha20Stream.init(SymmetricCipher::ChaCha20, SymmetricCipher::Decrypt, chacha20Key, chacha20iv)) { + g_cryptoError = chacha20Stream.errorString(); + return false; + } + if (!chacha20Stream.process(data)) { + g_cryptoError = chacha20Stream.errorString(); + return false; + } + if (data != chacha20Plain) { + g_cryptoError = "ChaCha20 stream cipher decrypt mismatch."; + return false; + } + + return true; + } +} // namespace + +namespace Crypto { - return m_initialized; -} + 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") + .arg(Botan::version_major()) + .arg(Botan::version_minor()) + .arg(Botan::version_patch()); + return false; + } -QString Crypto::errorString() -{ - return m_errorStr; -} - -QString Crypto::debugInfo() -{ - Q_ASSERT(Crypto::initialized()); - - QString debugInfo = QObject::tr("Cryptographic libraries:").append("\n"); - debugInfo.append("- libgcrypt ").append(m_backendVersion).append("\n"); - return debugInfo; -} - -bool Crypto::backendSelfTest() -{ - return (gcry_control(GCRYCTL_SELFTEST) == 0); -} - -bool Crypto::checkAlgorithms() -{ - if (gcry_cipher_algo_info(GCRY_CIPHER_AES256, GCRYCTL_TEST_ALGO, nullptr, nullptr) != 0) { - m_errorStr = "GCRY_CIPHER_AES256 not found."; - qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr)); - return false; - } - if (gcry_cipher_algo_info(GCRY_CIPHER_TWOFISH, GCRYCTL_TEST_ALGO, nullptr, nullptr) != 0) { - m_errorStr = "GCRY_CIPHER_TWOFISH not found."; - qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr)); - return false; - } - if (gcry_cipher_algo_info(GCRY_CIPHER_SALSA20, GCRYCTL_TEST_ALGO, nullptr, nullptr) != 0) { - m_errorStr = "GCRY_CIPHER_SALSA20 not found."; - qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr)); - return false; - } - if (gcry_cipher_algo_info(GCRY_CIPHER_CHACHA20, GCRYCTL_TEST_ALGO, nullptr, nullptr) != 0) { - m_errorStr = "GCRY_CIPHER_CHACHA20 not found."; - qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr)); - return false; - } - if (gcry_md_test_algo(GCRY_MD_SHA256) != 0) { - m_errorStr = "GCRY_MD_SHA256 not found."; - qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr)); - return false; - } - if (gcry_md_test_algo(GCRY_MD_SHA512) != 0) { - m_errorStr = "GCRY_MD_SHA512 not found."; - qWarning("Crypto::checkAlgorithms: %s", qPrintable(m_errorStr)); - return false; + return testSha256() && testSha512() && testAes256Cbc() && testAesKdf() && testTwofish() && testSalsa20() + && testChaCha20(); } - return true; -} - -bool Crypto::selfTest() -{ - return testSha256() && testSha512() && testAes256Cbc() && testAes256Ecb() && testTwofish() && testSalsa20() - && testChaCha20(); -} - -void Crypto::raiseError(const QString& str) -{ - m_errorStr = str; - qWarning("Crypto::selfTest: %s", qPrintable(m_errorStr)); -} - -bool Crypto::testSha256() -{ - QByteArray sha256Test = - CryptoHash::hash("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", CryptoHash::Sha256); - - if (sha256Test != QByteArray::fromHex("248D6A61D20638B8E5C026930C3E6039A33CE45964FF2167F6ECEDD419DB06C1")) { - raiseError("SHA-256 mismatch."); - return false; + QString errorString() + { + return g_cryptoError; } - return true; -} - -bool Crypto::testSha512() -{ - QByteArray sha512Test = - CryptoHash::hash("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", CryptoHash::Sha512); - - if (sha512Test - != QByteArray::fromHex("204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b" - "07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445")) { - raiseError("SHA-512 mismatch."); - return false; + QString debugInfo() + { + QString debugInfo = QObject::tr("Cryptographic libraries:").append("\n"); + debugInfo.append(QString("- Botan %1.%2.%3\n") + .arg(Botan::version_major()) + .arg(Botan::version_minor()) + .arg(Botan::version_patch())); + return debugInfo; } - - return true; -} - -bool Crypto::testAes256Cbc() -{ - QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"); - QByteArray iv = QByteArray::fromHex("000102030405060708090a0b0c0d0e0f"); - QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a"); - plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51")); - QByteArray cipherText = QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd6"); - cipherText.append(QByteArray::fromHex("9cfc4e967edb808d679f777bc6702c7d")); - bool ok; - - SymmetricCipher aes256Encrypt(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt); - if (!aes256Encrypt.init(key, iv)) { - raiseError(aes256Encrypt.errorString()); - return false; - } - QByteArray encryptedText = aes256Encrypt.process(plainText, &ok); - if (!ok) { - raiseError(aes256Encrypt.errorString()); - return false; - } - if (encryptedText != cipherText) { - raiseError("AES-256 CBC encryption mismatch."); - return false; - } - - SymmetricCipher aes256Decrypt(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt); - if (!aes256Decrypt.init(key, iv)) { - raiseError(aes256Decrypt.errorString()); - return false; - } - QByteArray decryptedText = aes256Decrypt.process(cipherText, &ok); - if (!ok) { - raiseError(aes256Decrypt.errorString()); - return false; - } - if (decryptedText != plainText) { - raiseError("AES-256 CBC decryption mismatch."); - return false; - } - - return true; -} - -bool Crypto::testAes256Ecb() -{ - QByteArray key = QByteArray::fromHex("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F"); - QByteArray iv = QByteArray::fromHex("00000000000000000000000000000000"); - QByteArray plainText = QByteArray::fromHex("00112233445566778899AABBCCDDEEFF"); - plainText.append(QByteArray::fromHex("00112233445566778899AABBCCDDEEFF")); - QByteArray cipherText = QByteArray::fromHex("8EA2B7CA516745BFEAFC49904B496089"); - cipherText.append(QByteArray::fromHex("8EA2B7CA516745BFEAFC49904B496089")); - bool ok; - - SymmetricCipher aes256Encrypt(SymmetricCipher::Aes256, SymmetricCipher::Ecb, SymmetricCipher::Encrypt); - if (!aes256Encrypt.init(key, iv)) { - raiseError(aes256Encrypt.errorString()); - return false; - } - QByteArray encryptedText = aes256Encrypt.process(plainText, &ok); - if (!ok) { - raiseError(aes256Encrypt.errorString()); - return false; - } - if (encryptedText != cipherText) { - raiseError("AES-256 ECB encryption mismatch."); - return false; - } - - SymmetricCipher aes256Decrypt(SymmetricCipher::Aes256, SymmetricCipher::Ecb, SymmetricCipher::Decrypt); - if (!aes256Decrypt.init(key, iv)) { - raiseError(aes256Decrypt.errorString()); - return false; - } - QByteArray decryptedText = aes256Decrypt.process(cipherText, &ok); - if (!ok) { - raiseError(aes256Decrypt.errorString()); - return false; - } - if (decryptedText != plainText) { - raiseError("AES-256 ECB decryption mismatch."); - return false; - } - - return true; -} - -bool Crypto::testTwofish() -{ - QByteArray key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"); - QByteArray iv = QByteArray::fromHex("000102030405060708090a0b0c0d0e0f"); - QByteArray plainText = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a"); - plainText.append(QByteArray::fromHex("ae2d8a571e03ac9c9eb76fac45af8e51")); - QByteArray cipherText = QByteArray::fromHex("e0227c3cc80f3cb1b2ed847cc6f57d3c"); - cipherText.append(QByteArray::fromHex("657b1e7960b30fb7c8d62e72ae37c3a0")); - bool ok; - - SymmetricCipher twofishEncrypt(SymmetricCipher::Twofish, SymmetricCipher::Cbc, SymmetricCipher::Encrypt); - if (!twofishEncrypt.init(key, iv)) { - raiseError(twofishEncrypt.errorString()); - return false; - } - QByteArray encryptedText = twofishEncrypt.process(plainText, &ok); - if (!ok) { - raiseError(twofishEncrypt.errorString()); - return false; - } - if (encryptedText != cipherText) { - raiseError("Twofish encryption mismatch."); - return false; - } - - SymmetricCipher twofishDecrypt(SymmetricCipher::Twofish, SymmetricCipher::Cbc, SymmetricCipher::Decrypt); - if (!twofishDecrypt.init(key, iv)) { - raiseError(twofishEncrypt.errorString()); - return false; - } - QByteArray decryptedText = twofishDecrypt.process(cipherText, &ok); - if (!ok) { - raiseError(twofishDecrypt.errorString()); - return false; - } - if (decryptedText != plainText) { - raiseError("Twofish encryption mismatch."); - return false; - } - - return true; -} - -bool Crypto::testSalsa20() -{ - QByteArray salsa20Key = QByteArray::fromHex("F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112"); - QByteArray salsa20iv = QByteArray::fromHex("0000000000000000"); - QByteArray salsa20Plain = QByteArray::fromHex("00000000000000000000000000000000"); - QByteArray salsa20Cipher = QByteArray::fromHex("B4C0AFA503BE7FC29A62058166D56F8F"); - bool ok; - - SymmetricCipher salsa20Stream(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt); - if (!salsa20Stream.init(salsa20Key, salsa20iv)) { - raiseError(salsa20Stream.errorString()); - return false; - } - - QByteArray salsaProcessed = salsa20Stream.process(salsa20Plain, &ok); - if (!ok) { - raiseError(salsa20Stream.errorString()); - return false; - } - if (salsaProcessed != salsa20Cipher) { - raiseError("Salsa20 stream cipher mismatch."); - return false; - } - - return true; -} - -bool Crypto::testChaCha20() -{ - QByteArray chacha20Key = QByteArray::fromHex("0000000000000000000000000000000000000000000000000000000000000000"); - QByteArray chacha20iv = QByteArray::fromHex("0000000000000000"); - QByteArray chacha20Plain = QByteArray::fromHex("0000000000000000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000000000"); - QByteArray chacha20Cipher = QByteArray::fromHex("76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da" - "41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"); - bool ok; - - SymmetricCipher chacha20Stream(SymmetricCipher::ChaCha20, SymmetricCipher::Stream, SymmetricCipher::Encrypt); - if (!chacha20Stream.init(chacha20Key, chacha20iv)) { - raiseError(chacha20Stream.errorString()); - return false; - } - - QByteArray chacha20Processed = chacha20Stream.process(chacha20Plain, &ok); - if (!ok) { - raiseError(chacha20Stream.errorString()); - return false; - } - if (chacha20Processed != chacha20Cipher) { - raiseError("ChaCha20 stream cipher mismatch."); - return false; - } - - return true; -} +} // namespace Crypto diff --git a/src/crypto/Crypto.h b/src/crypto/Crypto.h index 4346f5055..0f6a56f92 100644 --- a/src/crypto/Crypto.h +++ b/src/crypto/Crypto.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2021 KeePassXC Team * Copyright (C) 2010 Felix Geyer * * This program is free software: you can redistribute it and/or modify @@ -15,36 +16,16 @@ * along with this program. If not, see . */ -#ifndef KEEPASSX_CRYPTO_H -#define KEEPASSX_CRYPTO_H +#ifndef KEEPASSXC_CRYPTO_H +#define KEEPASSXC_CRYPTO_H #include -class Crypto +namespace Crypto { -public: - static bool init(); - static bool initialized(); - static bool backendSelfTest(); - static QString errorString(); - static QString debugInfo(); + bool init(); + QString errorString(); + QString debugInfo(); +}; // namespace Crypto -private: - Crypto(); - static bool checkAlgorithms(); - static bool selfTest(); - static void raiseError(const QString& str); - static bool testSha256(); - static bool testSha512(); - static bool testAes256Cbc(); - static bool testAes256Ecb(); - static bool testTwofish(); - static bool testSalsa20(); - static bool testChaCha20(); - - static bool m_initialized; - static QString m_errorStr; - static QString m_backendVersion; -}; - -#endif // KEEPASSX_CRYPTO_H +#endif // KEEPASSXC_CRYPTO_H diff --git a/src/crypto/CryptoHash.cpp b/src/crypto/CryptoHash.cpp index 5eab8567a..d3d5ee9fe 100644 --- a/src/crypto/CryptoHash.cpp +++ b/src/crypto/CryptoHash.cpp @@ -1,4 +1,5 @@ /* + * Copyright (C) 2021 KeePassXC Team * Copyright (C) 2010 Felix Geyer * * This program is free software: you can redistribute it and/or modify @@ -17,15 +18,17 @@ #include "CryptoHash.h" -#include - #include "crypto/Crypto.h" +#include +#include +#include + class CryptoHashPrivate { public: - gcry_md_hd_t ctx; - int hashLen; + QScopedPointer hashFunction; + QScopedPointer hmacFunction; }; CryptoHash::CryptoHash(Algorithm algo, bool hmac) @@ -33,45 +36,31 @@ CryptoHash::CryptoHash(Algorithm algo, bool hmac) { Q_D(CryptoHash); - Q_ASSERT(Crypto::initialized()); - - int algoGcrypt = -1; - unsigned int flagsGcrypt = GCRY_MD_FLAG_SECURE; - switch (algo) { case CryptoHash::Sha256: - algoGcrypt = GCRY_MD_SHA256; + if (hmac) { + d->hmacFunction.reset(Botan::MessageAuthenticationCode::create("HMAC(SHA-256)").release()); + } else { + d->hashFunction.reset(Botan::HashFunction::create("SHA-256").release()); + } break; - case CryptoHash::Sha512: - algoGcrypt = GCRY_MD_SHA512; + if (hmac) { + d->hmacFunction.reset(Botan::MessageAuthenticationCode::create("HMAC(SHA-512)").release()); + } else { + d->hashFunction.reset(Botan::HashFunction::create("SHA-512").release()); + } break; - default: Q_ASSERT(false); break; } - - if (hmac) { - flagsGcrypt |= GCRY_MD_FLAG_HMAC; - } - - gcry_error_t error = gcry_md_open(&d->ctx, algoGcrypt, flagsGcrypt); - if (error != GPG_ERR_NO_ERROR) { - qWarning("Gcrypt error (ctor): %s\n %s", gcry_strerror(error), gcry_strsource(error)); - } - Q_ASSERT(error == 0); // TODO: error handling - - d->hashLen = gcry_md_get_algo_dlen(algoGcrypt); } CryptoHash::~CryptoHash() { Q_D(CryptoHash); - - gcry_md_close(d->ctx); - - delete d_ptr; + delete d; } void CryptoHash::addData(const QByteArray& data) @@ -82,31 +71,47 @@ void CryptoHash::addData(const QByteArray& data) return; } - gcry_md_write(d->ctx, data.constData(), static_cast(data.size())); + try { + if (d->hmacFunction) { + d->hmacFunction->update(reinterpret_cast(data.data()), data.size()); + } else if (d->hashFunction) { + d->hashFunction->update(reinterpret_cast(data.data()), data.size()); + } + } catch (std::exception& e) { + qWarning("CryptoHash::update failed to add data: %s", e.what()); + Q_ASSERT(false); + } } void CryptoHash::setKey(const QByteArray& data) { Q_D(CryptoHash); - gcry_error_t error = gcry_md_setkey(d->ctx, data.constData(), static_cast(data.size())); - if (error) { - qWarning("Gcrypt error (setKey): %s\n %s", gcry_strerror(error), gcry_strsource(error)); + if (d->hmacFunction) { + try { + d->hmacFunction->set_key(reinterpret_cast(data.data()), data.size()); + } catch (std::exception& e) { + qWarning("CryptoHash::setKey failed to set HMAC key: %s", e.what()); + Q_ASSERT(false); + } } - Q_ASSERT(error == 0); } QByteArray CryptoHash::result() const { Q_D(const CryptoHash); - const auto result = reinterpret_cast(gcry_md_read(d->ctx, 0)); - return QByteArray(result, d->hashLen); + Botan::secure_vector result; + if (d->hmacFunction) { + result = d->hmacFunction->final(); + } else if (d->hashFunction) { + result = d->hashFunction->final(); + } + return QByteArray(reinterpret_cast(result.data()), result.size()); } QByteArray CryptoHash::hash(const QByteArray& data, Algorithm algo) { - // replace with gcry_md_hash_buffer()? CryptoHash cryptoHash(algo); cryptoHash.addData(data); return cryptoHash.result(); @@ -114,7 +119,6 @@ QByteArray CryptoHash::hash(const QByteArray& data, Algorithm algo) QByteArray CryptoHash::hmac(const QByteArray& data, const QByteArray& key, Algorithm algo) { - // replace with gcry_md_hash_buffer()? CryptoHash cryptoHash(algo, true); cryptoHash.setKey(key); cryptoHash.addData(data); diff --git a/src/crypto/CryptoHash.h b/src/crypto/CryptoHash.h index 02f90eb4d..75ac2d7c3 100644 --- a/src/crypto/CryptoHash.h +++ b/src/crypto/CryptoHash.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2021 KeePassXC Team * Copyright (C) 2010 Felix Geyer * * This program is free software: you can redistribute it and/or modify diff --git a/src/crypto/Random.cpp b/src/crypto/Random.cpp index 024a82f91..430a413d1 100644 --- a/src/crypto/Random.cpp +++ b/src/crypto/Random.cpp @@ -17,46 +17,64 @@ #include "Random.h" -#include - #include "core/Global.h" #include "crypto/Crypto.h" -class RandomBackendGcrypt : public RandomBackend -{ -public: - void randomize(void* data, int len) override; -}; +#include +#include + +#include +#include QSharedPointer Random::m_instance; +QSharedPointer Random::instance() +{ + if (!m_instance) { + m_instance.reset(new Random()); + } + return m_instance; +} + +Random::Random() +{ +#ifdef BOTAN_HAS_SYSTEM_RNG + m_rng.reset(new Botan::System_RNG); +#else + m_rng.reset(new Botan::Autoseeded_RNG); +#endif +} + +QSharedPointer Random::getRng() +{ + return m_rng; +} + void Random::randomize(QByteArray& ba) { - m_backend->randomize(ba.data(), ba.size()); + m_rng->randomize(reinterpret_cast(ba.data()), ba.size()); } QByteArray Random::randomArray(int len) { - QByteArray ba; - ba.resize(len); - + QByteArray ba(len, '\0'); randomize(ba); - return ba; } quint32 Random::randomUInt(quint32 limit) { - Q_ASSERT(limit != 0); Q_ASSERT(limit <= QUINT32_MAX); + if (limit == 0) { + return 0; + } quint32 rand; const quint32 ceil = QUINT32_MAX - (QUINT32_MAX % limit) - 1; - // To avoid modulo bias: - // Make sure rand is below the largest number where rand%limit==0 + // To avoid modulo bias make sure rand is below the largest number where rand%limit==0 do { - m_backend->randomize(&rand, 4); + m_rng->randomize(reinterpret_cast(&rand), 4); } while (rand > ceil); return (rand % limit); @@ -66,38 +84,3 @@ quint32 Random::randomUIntRange(quint32 min, quint32 max) { return min + randomUInt(max - min); } - -Random* Random::instance() -{ - if (!m_instance) { - m_instance.reset(new Random(new RandomBackendGcrypt())); - } - - return m_instance.data(); -} - -void Random::resetInstance() -{ - m_instance.reset(); -} - -void Random::setInstance(RandomBackend* backend) -{ - m_instance.reset(new Random(backend)); -} - -Random::Random(RandomBackend* backend) - : m_backend(backend) -{ -} - -void RandomBackendGcrypt::randomize(void* data, int len) -{ - Q_ASSERT(Crypto::initialized()); - - gcry_randomize(data, len, GCRY_STRONG_RANDOM); -} - -RandomBackend::~RandomBackend() -{ -} diff --git a/src/crypto/Random.h b/src/crypto/Random.h index bdf7b9aca..2a9445a92 100644 --- a/src/crypto/Random.h +++ b/src/crypto/Random.h @@ -22,16 +22,13 @@ #include #include -class RandomBackend -{ -public: - virtual void randomize(void* data, int len) = 0; - virtual ~RandomBackend(); -}; +#include class Random { public: + static QSharedPointer instance(); + void randomize(QByteArray& ba); QByteArray randomArray(int len); @@ -45,23 +42,17 @@ public: */ quint32 randomUIntRange(quint32 min, quint32 max); - static Random* instance(); - -protected: - static void resetInstance(); - static void setInstance(RandomBackend* backend); + QSharedPointer getRng(); private: + explicit Random(); + Q_DISABLE_COPY(Random); + static QSharedPointer m_instance; - - explicit Random(RandomBackend* backend); - - QScopedPointer m_backend; - - Q_DISABLE_COPY(Random) + QSharedPointer m_rng; }; -inline Random* randomGen() +static inline QSharedPointer randomGen() { return Random::instance(); } diff --git a/src/crypto/SymmetricCipher.cpp b/src/crypto/SymmetricCipher.cpp index ee4295eec..c894ca305 100644 --- a/src/crypto/SymmetricCipher.cpp +++ b/src/crypto/SymmetricCipher.cpp @@ -16,143 +16,247 @@ */ #include "SymmetricCipher.h" +#include #include "config-keepassx.h" -#include "crypto/SymmetricCipherGcrypt.h" -SymmetricCipher::SymmetricCipher(Algorithm algo, Mode mode, Direction direction) - : m_backend(createBackend(algo, mode, direction)) - , m_initialized(false) - , m_algo(algo) -{ -} +#include +#include -SymmetricCipher::~SymmetricCipher() +bool SymmetricCipher::init(Mode mode, Direction direction, const QByteArray& key, const QByteArray& iv) { -} - -bool SymmetricCipher::init(const QByteArray& key, const QByteArray& iv) -{ - if (!m_backend->init()) { + m_mode = mode; + if (mode == InvalidMode) { + m_error = QObject::tr("SymmetricCipher::init: Invalid cipher mode."); return false; } - if (!m_backend->setKey(key)) { + try { + auto botanMode = modeToString(mode); + auto botanDirection = (direction == SymmetricCipher::Encrypt ? Botan::ENCRYPTION : Botan::DECRYPTION); + + auto cipher = Botan::Cipher_Mode::create_or_throw(botanMode.toStdString(), botanDirection); + m_cipher.reset(cipher.release()); + m_cipher->set_key(reinterpret_cast(key.data()), key.size()); + + if (!m_cipher->valid_nonce_length(iv.size())) { + m_mode = InvalidMode; + m_cipher.reset(); + m_error = QObject::tr("SymmetricCipher::init: Invalid IV size of %1 for %2.").arg(iv.size()).arg(botanMode); + return false; + } + m_cipher->start(reinterpret_cast(iv.data()), iv.size()); + } catch (std::exception& e) { + m_mode = InvalidMode; + m_cipher.reset(); + + m_error = e.what(); + reset(); return false; } - if (!m_backend->setIv(iv)) { - return false; - } - - m_initialized = true; return true; } bool SymmetricCipher::isInitalized() const { - return m_initialized; + return m_cipher; } -SymmetricCipherBackend* SymmetricCipher::createBackend(Algorithm algo, Mode mode, Direction direction) +bool SymmetricCipher::process(char* data, int len) { - switch (algo) { - case Aes128: - case Aes256: - case Twofish: - case Salsa20: - case ChaCha20: - return new SymmetricCipherGcrypt(algo, mode, direction); + Q_ASSERT(isInitalized()); + if (!isInitalized()) { + m_error = QObject::tr("Cipher not initialized prior to use."); + return false; + } + if (len == 0) { + m_error = QObject::tr("Cannot process 0 length data."); + return false; + } - default: - Q_ASSERT(false); - return nullptr; + try { + // Block size is checked by Botan, an exception is thrown if invalid + m_cipher->process(reinterpret_cast(data), len); + return true; + } catch (std::exception& e) { + m_error = e.what(); + return false; } } -bool SymmetricCipher::reset() +bool SymmetricCipher::process(QByteArray& data) { - return m_backend->reset(); + return process(data.data(), data.size()); } -int SymmetricCipher::keySize() const +bool SymmetricCipher::finish(QByteArray& data) { - return m_backend->keySize(); + Q_ASSERT(isInitalized()); + if (!isInitalized()) { + m_error = QObject::tr("Cipher not initialized prior to use."); + return false; + } + + try { + // Error checking is done by Botan, an exception is thrown if invalid + Botan::secure_vector input(data.begin(), data.end()); + m_cipher->finish(input); + // Post-finished data may be larger than before due to padding + data.resize(input.size()); + // Direct copy the finished data back into the QByteArray + std::copy(input.begin(), input.end(), data.begin()); + return true; + } catch (std::exception& e) { + m_error = e.what(); + return false; + } } -int SymmetricCipher::blockSize() const +void SymmetricCipher::reset() { - return m_backend->blockSize(); + m_error.clear(); + if (isInitalized()) { + m_cipher.reset(); + } +} + +SymmetricCipher::Mode SymmetricCipher::mode() +{ + return m_mode; +} + +bool SymmetricCipher::aesKdf(const QByteArray& key, int rounds, QByteArray& data) +{ + try { + std::unique_ptr cipher(Botan::BlockCipher::create("AES-256")); + cipher->set_key(reinterpret_cast(key.data()), key.size()); + + Botan::secure_vector out(data.begin(), data.end()); + for (int i = 0; i < rounds; ++i) { + cipher->encrypt(out); + } + std::copy(out.begin(), out.end(), data.begin()); + return true; + } catch (std::exception& e) { + qWarning("SymmetricCipher::aesKdf: Could not process: %s", e.what()); + return false; + } } QString SymmetricCipher::errorString() const { - return m_backend->error(); + return m_error; } -SymmetricCipher::Algorithm SymmetricCipher::cipherToAlgorithm(const QUuid& cipher) +SymmetricCipher::Mode SymmetricCipher::cipherUuidToMode(const QUuid& uuid) { - if (cipher == KeePass2::CIPHER_AES256) { - return Aes256; - } else if (cipher == KeePass2::CIPHER_CHACHA20) { + if (uuid == KeePass2::CIPHER_AES128) { + return Aes128_CBC; + } else if (uuid == KeePass2::CIPHER_AES256) { + return Aes256_CBC; + } else if (uuid == KeePass2::CIPHER_CHACHA20) { return ChaCha20; - } else if (cipher == KeePass2::CIPHER_TWOFISH) { - return Twofish; + } else if (uuid == KeePass2::CIPHER_TWOFISH) { + return Twofish_CBC; } - qWarning("SymmetricCipher::cipherToAlgorithm: invalid UUID %s", cipher.toString().toLatin1().data()); - return InvalidAlgorithm; + qWarning("SymmetricCipher: Invalid KeePass2 Cipher UUID %s", uuid.toString().toLatin1().data()); + return InvalidMode; } -QUuid SymmetricCipher::algorithmToCipher(Algorithm algo) +SymmetricCipher::Mode SymmetricCipher::stringToMode(const QString& cipher) { - switch (algo) { - case Aes128: - return KeePass2::CIPHER_AES128; - case Aes256: - return KeePass2::CIPHER_AES256; - case ChaCha20: - return KeePass2::CIPHER_CHACHA20; - case Twofish: - return KeePass2::CIPHER_TWOFISH; - default: - qWarning("SymmetricCipher::algorithmToCipher: invalid algorithm %d", algo); - return {}; - } -} - -int SymmetricCipher::algorithmIvSize(Algorithm algo) -{ - switch (algo) { - case ChaCha20: - return 12; - case Aes128: - return 16; - case Aes256: - return 16; - case Twofish: - return 16; - default: - qWarning("SymmetricCipher::algorithmIvSize: invalid algorithm %d", algo); - return -1; - } -} - -SymmetricCipher::Mode SymmetricCipher::algorithmMode(Algorithm algo) -{ - switch (algo) { - case ChaCha20: - return Stream; - case Aes256: - case Twofish: - return Cbc; - default: - qWarning("SymmetricCipher::algorithmMode: invalid algorithm %d", algo); + auto cs = Qt::CaseInsensitive; + if (cipher.compare("aes-128-cbc", cs) == 0 || cipher.compare("aes128-cbc", cs) == 0) { + return Aes128_CBC; + } else if (cipher.compare("aes-256-cbc", cs) == 0 || cipher.compare("aes256-cbc", cs) == 0) { + return Aes256_CBC; + } else if (cipher.compare("aes-128-ctr", cs) == 0 || cipher.compare("aes128-ctr", cs) == 0) { + return Aes128_CTR; + } else if (cipher.compare("aes-256-ctr", cs) == 0 || cipher.compare("aes256-ctr", cs) == 0) { + return Aes256_CTR; + } else if (cipher.startsWith("twofish", cs)) { + return Twofish_CBC; + } else if (cipher.startsWith("salsa", cs)) { + return Salsa20; + } else if (cipher.startsWith("chacha", cs)) { + return ChaCha20; + } else { return InvalidMode; } } -SymmetricCipher::Algorithm SymmetricCipher::algorithm() const +QString SymmetricCipher::modeToString(const Mode mode) { - return m_algo; + switch (mode) { + case Aes128_CBC: + return QStringLiteral("AES-128/CBC"); + case Aes256_CBC: + return QStringLiteral("AES-256/CBC"); + case Aes128_CTR: + return QStringLiteral("CTR(AES-128)"); + case Aes256_CTR: + return QStringLiteral("CTR(AES-256)"); + case Twofish_CBC: + return QStringLiteral("Twofish/CBC"); + case Salsa20: + return QStringLiteral("Salsa20"); + case ChaCha20: + return QStringLiteral("ChaCha20"); + default: + Q_ASSERT_X(false, "SymmetricCipher::modeToString", "Invalid Mode Specified"); + return {}; + } +} + +int SymmetricCipher::defaultIvSize(Mode mode) +{ + switch (mode) { + case Aes128_CBC: + case Aes256_CBC: + case Aes128_CTR: + case Aes256_CTR: + case Twofish_CBC: + return 16; + case Salsa20: + case ChaCha20: + return 12; + default: + return -1; + } +} + +int SymmetricCipher::keySize(Mode mode) +{ + switch (mode) { + case Aes128_CBC: + case Aes128_CTR: + return 16; + case Aes256_CBC: + case Aes256_CTR: + case Twofish_CBC: + case Salsa20: + case ChaCha20: + return 32; + default: + return 0; + } +} + +int SymmetricCipher::blockSize(Mode mode) +{ + switch (mode) { + case Aes128_CBC: + case Aes256_CBC: + case Twofish_CBC: + return 16; + case Aes128_CTR: + case Aes256_CTR: + case Salsa20: + case ChaCha20: + return 1; + default: + return 0; + } } diff --git a/src/crypto/SymmetricCipher.h b/src/crypto/SymmetricCipher.h index e0e91aff1..5bc2caab9 100644 --- a/src/crypto/SymmetricCipher.h +++ b/src/crypto/SymmetricCipher.h @@ -23,29 +23,26 @@ #include #include -#include "crypto/SymmetricCipherBackend.h" #include "format/KeePass2.h" +namespace Botan +{ + class Cipher_Mode; +} + class SymmetricCipher { public: - enum Algorithm - { - Aes128, - Aes256, - Twofish, - Salsa20, - ChaCha20, - InvalidAlgorithm = -1 - }; - enum Mode { - Cbc, - Ctr, - Ecb, - Stream, - InvalidMode = -1 + Aes128_CBC, + Aes256_CBC, + Aes128_CTR, + Aes256_CTR, + Twofish_CBC, + ChaCha20, + Salsa20, + InvalidMode = -1, }; enum Direction @@ -54,46 +51,36 @@ public: Encrypt }; - SymmetricCipher(Algorithm algo, Mode mode, Direction direction); - ~SymmetricCipher(); - Q_DISABLE_COPY(SymmetricCipher) + explicit SymmetricCipher() = default; + ~SymmetricCipher() = default; - bool init(const QByteArray& key, const QByteArray& iv); bool isInitalized() const; + Q_REQUIRED_RESULT bool init(Mode mode, Direction direction, const QByteArray& key, const QByteArray& iv); + Q_REQUIRED_RESULT bool process(char* data, int len); + Q_REQUIRED_RESULT bool process(QByteArray& data); + Q_REQUIRED_RESULT bool finish(QByteArray& data); - inline QByteArray process(const QByteArray& data, bool* ok) - { - return m_backend->process(data, ok); - } + static bool aesKdf(const QByteArray& key, int rounds, QByteArray& data); - Q_REQUIRED_RESULT inline bool processInPlace(QByteArray& data) - { - return m_backend->processInPlace(data); - } + void reset(); + Mode mode(); - Q_REQUIRED_RESULT inline bool processInPlace(QByteArray& data, quint64 rounds) - { - Q_ASSERT(rounds > 0); - return m_backend->processInPlace(data, rounds); - } - - bool reset(); - int keySize() const; - int blockSize() const; QString errorString() const; - Algorithm algorithm() const; - static Algorithm cipherToAlgorithm(const QUuid& cipher); - static QUuid algorithmToCipher(Algorithm algo); - static int algorithmIvSize(Algorithm algo); - static Mode algorithmMode(Algorithm algo); + static Mode cipherUuidToMode(const QUuid& uuid); + static Mode stringToMode(const QString& cipher); + static int defaultIvSize(Mode mode); + static int keySize(Mode mode); + static int blockSize(Mode mode); private: - static SymmetricCipherBackend* createBackend(Algorithm algo, Mode mode, Direction direction); + static QString modeToString(const Mode mode); - const QScopedPointer m_backend; - bool m_initialized; - Algorithm m_algo; + QString m_error; + Mode m_mode; + QSharedPointer m_cipher; + + Q_DISABLE_COPY(SymmetricCipher) }; #endif // KEEPASSX_SYMMETRICCIPHER_H diff --git a/src/crypto/SymmetricCipherBackend.h b/src/crypto/SymmetricCipherBackend.h deleted file mode 100644 index c8e98cfa4..000000000 --- a/src/crypto/SymmetricCipherBackend.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2010 Felix Geyer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef KEEPASSX_SYMMETRICCIPHERBACKEND_H -#define KEEPASSX_SYMMETRICCIPHERBACKEND_H - -#include - -class SymmetricCipherBackend -{ -public: - virtual ~SymmetricCipherBackend() - { - } - virtual bool init() = 0; - virtual bool setKey(const QByteArray& key) = 0; - virtual bool setIv(const QByteArray& iv) = 0; - - virtual QByteArray process(const QByteArray& data, bool* ok) = 0; - Q_REQUIRED_RESULT virtual bool processInPlace(QByteArray& data) = 0; - Q_REQUIRED_RESULT virtual bool processInPlace(QByteArray& data, quint64 rounds) = 0; - - virtual bool reset() = 0; - virtual int keySize() const = 0; - virtual int blockSize() const = 0; - - virtual QString error() const = 0; -}; - -#endif // KEEPASSX_SYMMETRICCIPHERBACKEND_H diff --git a/src/crypto/SymmetricCipherGcrypt.cpp b/src/crypto/SymmetricCipherGcrypt.cpp deleted file mode 100644 index 4d12d25a9..000000000 --- a/src/crypto/SymmetricCipherGcrypt.cpp +++ /dev/null @@ -1,261 +0,0 @@ -/* - * Copyright (C) 2010 Felix Geyer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "SymmetricCipherGcrypt.h" - -#include "config-keepassx.h" -#include "crypto/Crypto.h" - -SymmetricCipherGcrypt::SymmetricCipherGcrypt(SymmetricCipher::Algorithm algo, - SymmetricCipher::Mode mode, - SymmetricCipher::Direction direction) - : m_ctx(nullptr) - , m_algo(gcryptAlgo(algo)) - , m_mode(gcryptMode(mode)) - , m_direction(direction) -{ -} - -SymmetricCipherGcrypt::~SymmetricCipherGcrypt() -{ - gcry_cipher_close(m_ctx); -} - -int SymmetricCipherGcrypt::gcryptAlgo(SymmetricCipher::Algorithm algo) -{ - switch (algo) { - case SymmetricCipher::Aes128: - return GCRY_CIPHER_AES128; - - case SymmetricCipher::Aes256: - return GCRY_CIPHER_AES256; - - case SymmetricCipher::Twofish: - return GCRY_CIPHER_TWOFISH; - - case SymmetricCipher::Salsa20: - return GCRY_CIPHER_SALSA20; - - case SymmetricCipher::ChaCha20: - return GCRY_CIPHER_CHACHA20; - - default: - Q_ASSERT(false); - return -1; - } -} - -int SymmetricCipherGcrypt::gcryptMode(SymmetricCipher::Mode mode) -{ - switch (mode) { - case SymmetricCipher::Ecb: - return GCRY_CIPHER_MODE_ECB; - - case SymmetricCipher::Cbc: - return GCRY_CIPHER_MODE_CBC; - - case SymmetricCipher::Ctr: - return GCRY_CIPHER_MODE_CTR; - - case SymmetricCipher::Stream: - return GCRY_CIPHER_MODE_STREAM; - - default: - Q_ASSERT(false); - return -1; - } -} - -void SymmetricCipherGcrypt::setError(const gcry_error_t& err) -{ - const char* gcryptError = gcry_strerror(err); - const char* gcryptErrorSource = gcry_strsource(err); - - m_error = QString("%1/%2").arg(QString::fromLocal8Bit(gcryptErrorSource), QString::fromLocal8Bit(gcryptError)); -} - -bool SymmetricCipherGcrypt::init() -{ - Q_ASSERT(Crypto::initialized()); - - gcry_error_t error; - - if (m_ctx != nullptr) - gcry_cipher_close(m_ctx); - error = gcry_cipher_open(&m_ctx, m_algo, m_mode, 0); - if (error != 0) { - setError(error); - return false; - } - - return true; -} - -bool SymmetricCipherGcrypt::setKey(const QByteArray& key) -{ - m_key = key; - gcry_error_t error = gcry_cipher_setkey(m_ctx, m_key.constData(), m_key.size()); - - if (error != 0) { - setError(error); - return false; - } - - return true; -} - -bool SymmetricCipherGcrypt::setIv(const QByteArray& iv) -{ - m_iv = iv; - gcry_error_t error; - - if (m_mode == GCRY_CIPHER_MODE_CTR) { - error = gcry_cipher_setctr(m_ctx, m_iv.constData(), m_iv.size()); - } else { - error = gcry_cipher_setiv(m_ctx, m_iv.constData(), m_iv.size()); - } - - if (error != 0) { - setError(error); - return false; - } - - return true; -} - -QByteArray SymmetricCipherGcrypt::process(const QByteArray& data, bool* ok) -{ - // TODO: check block size - - QByteArray result; - result.resize(data.size()); - - gcry_error_t error; - - if (m_direction == SymmetricCipher::Decrypt) { - error = gcry_cipher_decrypt(m_ctx, result.data(), data.size(), data.constData(), data.size()); - } else { - error = gcry_cipher_encrypt(m_ctx, result.data(), data.size(), data.constData(), data.size()); - } - - if (error != 0) { - setError(error); - *ok = false; - } else { - *ok = true; - } - - return result; -} - -bool SymmetricCipherGcrypt::processInPlace(QByteArray& data) -{ - // TODO: check block size - - gcry_error_t error; - - if (m_direction == SymmetricCipher::Decrypt) { - error = gcry_cipher_decrypt(m_ctx, data.data(), data.size(), nullptr, 0); - } else { - error = gcry_cipher_encrypt(m_ctx, data.data(), data.size(), nullptr, 0); - } - - if (error != 0) { - setError(error); - return false; - } - - return true; -} - -bool SymmetricCipherGcrypt::processInPlace(QByteArray& data, quint64 rounds) -{ - gcry_error_t error; - - char* rawData = data.data(); - int size = data.size(); - - if (m_direction == SymmetricCipher::Decrypt) { - for (quint64 i = 0; i != rounds; ++i) { - error = gcry_cipher_decrypt(m_ctx, rawData, size, nullptr, 0); - - if (error != 0) { - setError(error); - return false; - } - } - } else { - for (quint64 i = 0; i != rounds; ++i) { - error = gcry_cipher_encrypt(m_ctx, rawData, size, nullptr, 0); - - if (error != 0) { - setError(error); - return false; - } - } - } - - return true; -} - -bool SymmetricCipherGcrypt::reset() -{ - gcry_error_t error; - - error = gcry_cipher_reset(m_ctx); - if (error != 0) { - setError(error); - return false; - } - - error = gcry_cipher_setiv(m_ctx, m_iv.constData(), m_iv.size()); - if (error != 0) { - setError(error); - return false; - } - - return true; -} - -int SymmetricCipherGcrypt::keySize() const -{ - gcry_error_t error; - size_t keySizeT; - - error = gcry_cipher_algo_info(m_algo, GCRYCTL_GET_KEYLEN, nullptr, &keySizeT); - if (error != 0) - return -1; - - return keySizeT; -} - -int SymmetricCipherGcrypt::blockSize() const -{ - gcry_error_t error; - size_t blockSizeT; - - error = gcry_cipher_algo_info(m_algo, GCRYCTL_GET_BLKLEN, nullptr, &blockSizeT); - if (error != 0) - return -1; - - return blockSizeT; -} - -QString SymmetricCipherGcrypt::error() const -{ - return m_error; -} diff --git a/src/crypto/SymmetricCipherGcrypt.h b/src/crypto/SymmetricCipherGcrypt.h deleted file mode 100644 index 2f886999c..000000000 --- a/src/crypto/SymmetricCipherGcrypt.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2010 Felix Geyer - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef KEEPASSX_SYMMETRICCIPHERGCRYPT_H -#define KEEPASSX_SYMMETRICCIPHERGCRYPT_H - -#include - -#include "crypto/SymmetricCipher.h" -#include "crypto/SymmetricCipherBackend.h" - -class SymmetricCipherGcrypt : public SymmetricCipherBackend -{ -public: - SymmetricCipherGcrypt(SymmetricCipher::Algorithm algo, - SymmetricCipher::Mode mode, - SymmetricCipher::Direction direction); - ~SymmetricCipherGcrypt(); - - bool init(); - bool setKey(const QByteArray& key); - bool setIv(const QByteArray& iv); - - QByteArray process(const QByteArray& data, bool* ok); - Q_REQUIRED_RESULT bool processInPlace(QByteArray& data); - Q_REQUIRED_RESULT bool processInPlace(QByteArray& data, quint64 rounds); - - bool reset(); - int keySize() const; - int blockSize() const; - - QString error() const; - -private: - static int gcryptAlgo(SymmetricCipher::Algorithm algo); - static int gcryptMode(SymmetricCipher::Mode mode); - void setError(const gcry_error_t& err); - - gcry_cipher_hd_t m_ctx; - const int m_algo; - const int m_mode; - const SymmetricCipher::Direction m_direction; - QByteArray m_key; - QByteArray m_iv; - QString m_error; -}; - -#endif // KEEPASSX_SYMMETRICCIPHERGCRYPT_H diff --git a/src/crypto/argon2/argon2.h b/src/crypto/argon2/argon2.h deleted file mode 100644 index 8eba85a6f..000000000 --- a/src/crypto/argon2/argon2.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2018 KeePassXC Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef KEEPASSXC_CRYPTO_ARGON2_H -#define KEEPASSXC_CRYPTO_ARGON2_H - -/* - Argon2 wrapper header with redefined symbols to be used with the - patched libargon2 binary which is generated by the build system. - This is to avoid link-time definition clashes with libsodium on Windows. - */ - -#ifdef Q_OS_WIN -#define argon2_hash libargon2_argon2_hash -#define argon2_error_message libargon2_argon2_error_message -#endif - -#include - -#endif // KEEPASSXC_CRYPTO_ARGON2_H diff --git a/src/crypto/kdf/AesKdf.cpp b/src/crypto/kdf/AesKdf.cpp index d1daf1e5d..481b7e461 100644 --- a/src/crypto/kdf/AesKdf.cpp +++ b/src/crypto/kdf/AesKdf.cpp @@ -20,6 +20,7 @@ #include #include "crypto/CryptoHash.h" +#include "crypto/SymmetricCipher.h" #include "format/KeePass2.h" AesKdf::AesKdf() @@ -83,22 +84,8 @@ bool AesKdf::transform(const QByteArray& raw, QByteArray& result) const bool AesKdf::transformKeyRaw(const QByteArray& key, const QByteArray& seed, int rounds, QByteArray* result) { - QByteArray iv(16, 0); - SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Ecb, SymmetricCipher::Encrypt); - if (!cipher.init(seed, iv)) { - qWarning("AesKdf::transformKeyRaw: error in SymmetricCipher::init: %s", cipher.errorString().toUtf8().data()); - return false; - } - *result = key; - - if (!cipher.processInPlace(*result, rounds)) { - qWarning("AesKdf::transformKeyRaw: error in SymmetricCipher::processInPlace: %s", - cipher.errorString().toUtf8().data()); - return false; - } - - return true; + return SymmetricCipher::aesKdf(seed, rounds, *result); } QSharedPointer AesKdf::clone() const @@ -106,24 +93,24 @@ QSharedPointer AesKdf::clone() const return QSharedPointer::create(*this); } -int AesKdf::benchmarkImpl(int msec) const +int AesKdf::benchmark(int msec) const { - QByteArray key = QByteArray(16, '\x7E'); - QByteArray seed = QByteArray(32, '\x4B'); - QByteArray iv(16, 0); + QByteArray key(16, '\x7E'); + QByteArray seed(32, '\x4B'); - SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Ecb, SymmetricCipher::Encrypt); - cipher.init(seed, iv); + int trials = 3; + int rounds = 1000000; - quint64 rounds = 1000000; QElapsedTimer timer; timer.start(); - - if (!cipher.processInPlace(key, rounds)) { - return -1; + for (int i = 0; i < trials; ++i) { + QByteArray result; + if (!transformKeyRaw(key, seed, rounds, &result)) { + return rounds; + } } - return static_cast(rounds * (static_cast(msec) / timer.elapsed())); + return static_cast(rounds * trials * static_cast(msec) / timer.elapsed()); } QString AesKdf::toString() const diff --git a/src/crypto/kdf/AesKdf.h b/src/crypto/kdf/AesKdf.h index d71fbb1d1..072dc9527 100644 --- a/src/crypto/kdf/AesKdf.h +++ b/src/crypto/kdf/AesKdf.h @@ -32,8 +32,7 @@ public: QSharedPointer clone() const override; QString toString() const override; -protected: - int benchmarkImpl(int msec) const override; + int benchmark(int msec) const override; private: Q_REQUIRED_RESULT static bool diff --git a/src/crypto/kdf/Argon2Kdf.cpp b/src/crypto/kdf/Argon2Kdf.cpp index a477a038a..979a6825c 100644 --- a/src/crypto/kdf/Argon2Kdf.cpp +++ b/src/crypto/kdf/Argon2Kdf.cpp @@ -18,8 +18,8 @@ #include "Argon2Kdf.h" #include +#include -#include "crypto/argon2/argon2.h" #include "format/KeePass2.h" /** @@ -163,39 +163,20 @@ bool Argon2Kdf::transform(const QByteArray& raw, QByteArray& result) const { result.clear(); result.resize(32); - return transformKeyRaw(raw, seed(), version(), type(), rounds(), memory(), parallelism(), result); -} - -bool Argon2Kdf::transformKeyRaw(const QByteArray& key, - const QByteArray& seed, - quint32 version, - Type type, - quint32 rounds, - quint64 memory, - quint32 parallelism, - QByteArray& result) -{ - // Time Cost, Mem Cost, Threads/Lanes, Password, length, Salt, length, out, length - - int rc = argon2_hash(rounds, - memory, - parallelism, - key.data(), - key.size(), - seed.data(), - seed.size(), - result.data(), - result.size(), - nullptr, - 0, - type == Type::Argon2d ? Argon2_d : Argon2_id, - version); - if (rc != ARGON2_OK) { - qWarning("Argon2 error: %s", argon2_error_message(rc)); + try { + auto algo = type() == Type::Argon2d ? "Argon2d" : "Argon2id"; + auto pwhash = Botan::PasswordHashFamily::create_or_throw(algo)->from_params(memory(), rounds(), parallelism()); + pwhash->derive_key(reinterpret_cast(result.data()), + result.size(), + raw.constData(), + raw.size(), + reinterpret_cast(seed().constData()), + seed().size()); + return true; + } catch (std::exception& e) { + qWarning("Argon2 error: %s", e.what()); return false; } - - return true; } QSharedPointer Argon2Kdf::clone() const @@ -203,20 +184,16 @@ QSharedPointer Argon2Kdf::clone() const return QSharedPointer::create(*this); } -int Argon2Kdf::benchmarkImpl(int msec) const +int Argon2Kdf::benchmark(int msec) const { - QByteArray key = QByteArray(16, '\x7E'); - QByteArray seed = QByteArray(32, '\x4B'); - - QElapsedTimer timer; - timer.start(); - - int rounds = 4; - if (transformKeyRaw(key, seed, version(), type(), rounds, memory(), parallelism(), key)) { - return static_cast(rounds * (static_cast(msec) / timer.elapsed())); + try { + auto algo = type() == Type::Argon2d ? "Argon2d" : "Argon2id"; + auto pwhash = Botan::PasswordHashFamily::create_or_throw(algo)->tune( + 32, std::chrono::milliseconds(msec), memory() / 1024); + return qMax(static_cast(1), pwhash->iterations()); + } catch (std::exception& e) { + return 1; } - - return 1; } QString Argon2Kdf::toString() const diff --git a/src/crypto/kdf/Argon2Kdf.h b/src/crypto/kdf/Argon2Kdf.h index cc8e5f07f..b5881b45b 100644 --- a/src/crypto/kdf/Argon2Kdf.h +++ b/src/crypto/kdf/Argon2Kdf.h @@ -45,22 +45,11 @@ public: bool setParallelism(quint32 threads); QString toString() const override; -protected: - int benchmarkImpl(int msec) const override; + int benchmark(int msec) const override; quint32 m_version; quint64 m_memory; quint32 m_parallelism; - -private: - Q_REQUIRED_RESULT static bool transformKeyRaw(const QByteArray& key, - const QByteArray& seed, - quint32 version, - Type type, - quint32 rounds, - quint64 memory, - quint32 parallelism, - QByteArray& result); }; #endif // KEEPASSX_ARGON2KDF_H diff --git a/src/crypto/kdf/Kdf.cpp b/src/crypto/kdf/Kdf.cpp index 6a4c7fc62..7c803a12f 100644 --- a/src/crypto/kdf/Kdf.cpp +++ b/src/crypto/kdf/Kdf.cpp @@ -16,7 +16,6 @@ */ #include "Kdf.h" -#include "Kdf_p.h" #include @@ -68,37 +67,3 @@ void Kdf::randomizeSeed() { setSeed(randomGen()->randomArray(m_seed.size())); } - -int Kdf::benchmark(int msec) const -{ - // Run the benchmark twice using half the time for each run - BenchmarkThread thread(msec / 2, this); - int rounds = 0; - - thread.start(); - thread.wait(); - rounds += thread.rounds(); - - thread.start(); - thread.wait(); - rounds += thread.rounds(); - - return qMax(1, rounds); -} - -Kdf::BenchmarkThread::BenchmarkThread(int msec, const Kdf* kdf) - : m_rounds(1) - , m_msec(msec) - , m_kdf(kdf) -{ -} - -int Kdf::BenchmarkThread::rounds() -{ - return m_rounds; -} - -void Kdf::BenchmarkThread::run() -{ - m_rounds = m_kdf->benchmarkImpl(m_msec); -} diff --git a/src/crypto/kdf/Kdf.h b/src/crypto/kdf/Kdf.h index da9a2526c..3acfc2c19 100644 --- a/src/crypto/kdf/Kdf.h +++ b/src/crypto/kdf/Kdf.h @@ -46,7 +46,7 @@ public: virtual QString toString() const = 0; - int benchmark(int msec) const; + virtual int benchmark(int msec) const = 0; /* * Default target encryption time, in MS. @@ -62,13 +62,10 @@ public: static const int MAX_ENCRYPTION_TIME = 5000; protected: - virtual int benchmarkImpl(int msec) const = 0; - int m_rounds; QByteArray m_seed; private: - class BenchmarkThread; const QUuid m_uuid; }; #endif // KEEPASSX_KDF_H diff --git a/src/crypto/kdf/Kdf_p.h b/src/crypto/kdf/Kdf_p.h deleted file mode 100644 index 55ad2401b..000000000 --- a/src/crypto/kdf/Kdf_p.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright (C) 2017 KeePassXC Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "Kdf.h" - -#include - -#ifndef KEEPASSXC_KDF_P_H -#define KEEPASSXC_KDF_P_H - -class Kdf::BenchmarkThread : public QThread -{ - Q_OBJECT - -public: - explicit BenchmarkThread(int msec, const Kdf* kdf); - - int rounds(); - -protected: - void run(); - -private: - int m_rounds; - int m_msec; - const Kdf* m_kdf; -}; - -#endif // KEEPASSXC_KDF_P_H diff --git a/src/crypto/ssh/CMakeLists.txt b/src/crypto/ssh/CMakeLists.txt deleted file mode 100644 index fdaf68e41..000000000 --- a/src/crypto/ssh/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -if(WITH_XC_CRYPTO_SSH) - include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) - - set(crypto_ssh_SOURCES - bcrypt_pbkdf.cpp - blowfish.c - ASN1Key.cpp - BinaryStream.cpp - OpenSSHKey.cpp - ) - - add_library(crypto_ssh STATIC ${crypto_ssh_SOURCES}) - target_link_libraries(crypto_ssh Qt5::Core ${GCRYPT_LIBRARIES}) -endif() diff --git a/src/crypto/ssh/bcrypt_pbkdf.cpp b/src/crypto/ssh/bcrypt_pbkdf.cpp deleted file mode 100644 index a57e2dab1..000000000 --- a/src/crypto/ssh/bcrypt_pbkdf.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/* $OpenBSD: bcrypt_pbkdf.c,v 1.13 2015/01/12 03:20:04 tedu Exp $ */ -/* - * Copyright (c) 2013 Ted Unangst - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#include - -extern "C" { -#include "blf.h" -} - -#define MINIMUM(a,b) (((a) < (b)) ? (a) : (b)) - -/* - * pkcs #5 pbkdf2 implementation using the "bcrypt" hash - * - * The bcrypt hash function is derived from the bcrypt password hashing - * function with the following modifications: - * 1. The input password and salt are preprocessed with SHA512. - * 2. The output length is expanded to 256 bits. - * 3. Subsequently the magic string to be encrypted is lengthened and modifed - * to "OxychromaticBlowfishSwatDynamite" - * 4. The hash function is defined to perform 64 rounds of initial state - * expansion. (More rounds are performed by iterating the hash.) - * - * Note that this implementation pulls the SHA512 operations into the caller - * as a performance optimization. - * - * One modification from official pbkdf2. Instead of outputting key material - * linearly, we mix it. pbkdf2 has a known weakness where if one uses it to - * generate (e.g.) 512 bits of key material for use as two 256 bit keys, an - * attacker can merely run once through the outer loop, but the user - * always runs it twice. Shuffling output bytes requires computing the - * entirety of the key material to assemble any subkey. This is something a - * wise caller could do; we just do it for you. - */ - -#define BCRYPT_WORDS 8 -#define BCRYPT_HASHSIZE (BCRYPT_WORDS * 4) -#define SHA512_DIGEST_LENGTH 64 - -// FIXME: explicit_bzero exists to ensure bzero is not optimized out -#define explicit_bzero bzero - -static void -bcrypt_hash(const quint8* sha2pass, const quint8* sha2salt, quint8* out) -{ - blf_ctx state; - quint8 ciphertext[BCRYPT_HASHSIZE] = // "OxychromaticBlowfishSwatDynamite" - { 0x4f, 0x78, 0x79, 0x63, 0x68, 0x72, 0x6f, 0x6d, - 0x61, 0x74, 0x69, 0x63, 0x42, 0x6c, 0x6f, 0x77, - 0x66, 0x69, 0x73, 0x68, 0x53, 0x77, 0x61, 0x74, - 0x44, 0x79, 0x6e, 0x61, 0x6d, 0x69, 0x74, 0x65 }; - quint32 cdata[BCRYPT_WORDS]; - int i; - quint16 j; - size_t shalen = SHA512_DIGEST_LENGTH; - - /* key expansion */ - Blowfish_initstate(&state); - Blowfish_expandstate(&state, sha2salt, shalen, sha2pass, shalen); - for (i = 0; i < 64; i++) { - Blowfish_expand0state(&state, sha2salt, shalen); - Blowfish_expand0state(&state, sha2pass, shalen); - } - - /* encryption */ - j = 0; - for (i = 0; i < BCRYPT_WORDS; i++) - cdata[i] = Blowfish_stream2word(ciphertext, sizeof(ciphertext), - &j); - for (i = 0; i < 64; i++) - blf_enc(&state, cdata, BCRYPT_WORDS / 2); - - /* copy out */ - for (i = 0; i < BCRYPT_WORDS; i++) { - out[4 * i + 3] = (cdata[i] >> 24) & 0xff; - out[4 * i + 2] = (cdata[i] >> 16) & 0xff; - out[4 * i + 1] = (cdata[i] >> 8) & 0xff; - out[4 * i + 0] = cdata[i] & 0xff; - } - - /* zap */ - explicit_bzero(ciphertext, sizeof(ciphertext)); - explicit_bzero(cdata, sizeof(cdata)); - explicit_bzero(&state, sizeof(state)); -} - -int bcrypt_pbkdf(const QByteArray& pass, const QByteArray& salt, QByteArray& key, quint32 rounds) -{ - QCryptographicHash ctx(QCryptographicHash::Sha512); - QByteArray sha2pass; - QByteArray sha2salt; - quint8 out[BCRYPT_HASHSIZE]; - quint8 tmpout[BCRYPT_HASHSIZE]; - quint8 countsalt[4]; - - /* nothing crazy */ - if (rounds < 1) { - return -1; - } - - if (pass.isEmpty() || salt.isEmpty() || key.isEmpty() || - static_cast(key.length()) > sizeof(out) * sizeof(out)) { - return -1; - } - - quint32 stride = (key.length() + sizeof(out) - 1) / sizeof(out); - quint32 amt = (key.length() + stride - 1) / stride; - - /* collapse password */ - ctx.reset(); - ctx.addData(pass); - sha2pass = ctx.result(); - - /* generate key, sizeof(out) at a time */ - for (quint32 count = 1, keylen = key.length(); keylen > 0; count++) { - countsalt[0] = (count >> 24) & 0xff; - countsalt[1] = (count >> 16) & 0xff; - countsalt[2] = (count >> 8) & 0xff; - countsalt[3] = count & 0xff; - - /* first round, salt is salt */ - ctx.reset(); - ctx.addData(salt); - ctx.addData(reinterpret_cast(countsalt), sizeof(countsalt)); - sha2salt = ctx.result(); - - bcrypt_hash(reinterpret_cast(sha2pass.data()), reinterpret_cast(sha2salt.data()), tmpout); - memcpy(out, tmpout, sizeof(out)); - - for (quint32 i = 1; i < rounds; i++) { - /* subsequent rounds, salt is previous output */ - ctx.reset(); - ctx.addData(reinterpret_cast(tmpout), sizeof(tmpout)); - sha2salt = ctx.result(); - bcrypt_hash(reinterpret_cast(sha2pass.data()), reinterpret_cast(sha2salt.data()), tmpout); - for (quint32 j = 0; j < sizeof(out); j++) - out[j] ^= tmpout[j]; - } - - /* - * pbkdf2 deviation: output the key material non-linearly. - */ - amt = MINIMUM(amt, keylen); - quint32 i; - for (i = 0; i < amt; i++) { - int dest = i * stride + (count - 1); - if (dest >= key.length()) - break; - key.data()[dest] = out[i]; - } - keylen -= i; - } - - /* zap */ - explicit_bzero(out, sizeof(out)); - - return 0; -} diff --git a/src/crypto/ssh/blf.h b/src/crypto/ssh/blf.h deleted file mode 100644 index f1ac5a5c2..000000000 --- a/src/crypto/ssh/blf.h +++ /dev/null @@ -1,88 +0,0 @@ -/* $OpenBSD: blf.h,v 1.7 2007/03/14 17:59:41 grunk Exp $ */ -/* - * Blowfish - a fast block cipher designed by Bruce Schneier - * - * Copyright 1997 Niels Provos - * 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. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Niels Provos. - * 4. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. - */ - -#ifndef _BLF_H_ -#define _BLF_H_ - -#include "includes.h" - -#if !defined(HAVE_BCRYPT_PBKDF) && !defined(HAVE_BLH_H) - -/* Schneier specifies a maximum key length of 56 bytes. - * This ensures that every key bit affects every cipher - * bit. However, the subkeys can hold up to 72 bytes. - * Warning: For normal blowfish encryption only 56 bytes - * of the key affect all cipherbits. - */ - -#define BLF_N 16 /* Number of Subkeys */ -#define BLF_MAXKEYLEN ((BLF_N-2)*4) /* 448 bits */ -#define BLF_MAXUTILIZED ((BLF_N+2)*4) /* 576 bits */ - -/* Blowfish context */ -typedef struct BlowfishContext { - u_int32_t S[4][256]; /* S-Boxes */ - u_int32_t P[BLF_N + 2]; /* Subkeys */ -} blf_ctx; - -/* Raw access to customized Blowfish - * blf_key is just: - * Blowfish_initstate( state ) - * Blowfish_expand0state( state, key, keylen ) - */ - -void Blowfish_encipher(blf_ctx *, u_int32_t *, u_int32_t *); -void Blowfish_decipher(blf_ctx *, u_int32_t *, u_int32_t *); -void Blowfish_initstate(blf_ctx *); -void Blowfish_expand0state(blf_ctx *, const u_int8_t *, u_int16_t); -void Blowfish_expandstate -(blf_ctx *, const u_int8_t *, u_int16_t, const u_int8_t *, u_int16_t); - -/* Standard Blowfish */ - -void blf_key(blf_ctx *, const u_int8_t *, u_int16_t); -void blf_enc(blf_ctx *, u_int32_t *, u_int16_t); -void blf_dec(blf_ctx *, u_int32_t *, u_int16_t); - -void blf_ecb_encrypt(blf_ctx *, u_int8_t *, u_int32_t); -void blf_ecb_decrypt(blf_ctx *, u_int8_t *, u_int32_t); - -void blf_cbc_encrypt(blf_ctx *, u_int8_t *, u_int8_t *, u_int32_t); -void blf_cbc_decrypt(blf_ctx *, u_int8_t *, u_int8_t *, u_int32_t); - -/* Converts u_int8_t to u_int32_t */ -u_int32_t Blowfish_stream2word(const u_int8_t *, u_int16_t , u_int16_t *); - -#endif /* !defined(HAVE_BCRYPT_PBKDF) && !defined(HAVE_BLH_H) */ -#endif /* _BLF_H */ - diff --git a/src/crypto/ssh/blowfish.c b/src/crypto/ssh/blowfish.c deleted file mode 100644 index e10f7e7d9..000000000 --- a/src/crypto/ssh/blowfish.c +++ /dev/null @@ -1,696 +0,0 @@ -/* $OpenBSD: blowfish.c,v 1.18 2004/11/02 17:23:26 hshoexer Exp $ */ -/* - * Blowfish block cipher for OpenBSD - * Copyright 1997 Niels Provos - * All rights reserved. - * - * Implementation advice by David Mazieres . - * - * 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. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Niels Provos. - * 4. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. - */ - -/* - * This code is derived from section 14.3 and the given source - * in section V of Applied Cryptography, second edition. - * Blowfish is an unpatented fast block cipher designed by - * Bruce Schneier. - */ - -#include "includes.h" - -#if !defined(HAVE_BCRYPT_PBKDF) && (!defined(HAVE_BLOWFISH_INITSTATE) || \ - !defined(HAVE_BLOWFISH_EXPAND0STATE) || !defined(HAVE_BLF_ENC)) - -#if 0 -#include /* used for debugging */ -#include -#endif - -#include -#ifdef HAVE_BLF_H -#include -#endif - -#undef inline -#ifdef __GNUC__ -#define inline __inline -#else /* !__GNUC__ */ -#define inline -#endif /* !__GNUC__ */ - -/* Function for Feistel Networks */ - -#define F(s, x) ((((s)[ (((x)>>24)&0xFF)] \ - + (s)[0x100 + (((x)>>16)&0xFF)]) \ - ^ (s)[0x200 + (((x)>> 8)&0xFF)]) \ - + (s)[0x300 + ( (x) &0xFF)]) - -#define BLFRND(s,p,i,j,n) (i ^= F(s,j) ^ (p)[n]) - -void -Blowfish_encipher(blf_ctx *c, u_int32_t *xl, u_int32_t *xr) -{ - u_int32_t Xl; - u_int32_t Xr; - u_int32_t *s = c->S[0]; - u_int32_t *p = c->P; - - Xl = *xl; - Xr = *xr; - - Xl ^= p[0]; - BLFRND(s, p, Xr, Xl, 1); BLFRND(s, p, Xl, Xr, 2); - BLFRND(s, p, Xr, Xl, 3); BLFRND(s, p, Xl, Xr, 4); - BLFRND(s, p, Xr, Xl, 5); BLFRND(s, p, Xl, Xr, 6); - BLFRND(s, p, Xr, Xl, 7); BLFRND(s, p, Xl, Xr, 8); - BLFRND(s, p, Xr, Xl, 9); BLFRND(s, p, Xl, Xr, 10); - BLFRND(s, p, Xr, Xl, 11); BLFRND(s, p, Xl, Xr, 12); - BLFRND(s, p, Xr, Xl, 13); BLFRND(s, p, Xl, Xr, 14); - BLFRND(s, p, Xr, Xl, 15); BLFRND(s, p, Xl, Xr, 16); - - *xl = Xr ^ p[17]; - *xr = Xl; -} - -void -Blowfish_decipher(blf_ctx *c, u_int32_t *xl, u_int32_t *xr) -{ - u_int32_t Xl; - u_int32_t Xr; - u_int32_t *s = c->S[0]; - u_int32_t *p = c->P; - - Xl = *xl; - Xr = *xr; - - Xl ^= p[17]; - BLFRND(s, p, Xr, Xl, 16); BLFRND(s, p, Xl, Xr, 15); - BLFRND(s, p, Xr, Xl, 14); BLFRND(s, p, Xl, Xr, 13); - BLFRND(s, p, Xr, Xl, 12); BLFRND(s, p, Xl, Xr, 11); - BLFRND(s, p, Xr, Xl, 10); BLFRND(s, p, Xl, Xr, 9); - BLFRND(s, p, Xr, Xl, 8); BLFRND(s, p, Xl, Xr, 7); - BLFRND(s, p, Xr, Xl, 6); BLFRND(s, p, Xl, Xr, 5); - BLFRND(s, p, Xr, Xl, 4); BLFRND(s, p, Xl, Xr, 3); - BLFRND(s, p, Xr, Xl, 2); BLFRND(s, p, Xl, Xr, 1); - - *xl = Xr ^ p[0]; - *xr = Xl; -} - -void -Blowfish_initstate(blf_ctx *c) -{ - /* P-box and S-box tables initialized with digits of Pi */ - - static const blf_ctx initstate = - { { - { - 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, - 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, - 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, - 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, - 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, - 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, - 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, - 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, - 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, - 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, - 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, - 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, - 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, - 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, - 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, - 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, - 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, - 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, - 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, - 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, - 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, - 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, - 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, - 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, - 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, - 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, - 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, - 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, - 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, - 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, - 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, - 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, - 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, - 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, - 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, - 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, - 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, - 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, - 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, - 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, - 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, - 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, - 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, - 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, - 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, - 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, - 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, - 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, - 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, - 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, - 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, - 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, - 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, - 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, - 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, - 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, - 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, - 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, - 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, - 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, - 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, - 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, - 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, - 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a}, - { - 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, - 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, - 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, - 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, - 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, - 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, - 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, - 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, - 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, - 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, - 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, - 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, - 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, - 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, - 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, - 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, - 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, - 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, - 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, - 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, - 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, - 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, - 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, - 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, - 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, - 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, - 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, - 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, - 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, - 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, - 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, - 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, - 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, - 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, - 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, - 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, - 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, - 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, - 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, - 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, - 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, - 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, - 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, - 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, - 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, - 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, - 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, - 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, - 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, - 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, - 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, - 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, - 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, - 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, - 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, - 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, - 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, - 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, - 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, - 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, - 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, - 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, - 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, - 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7}, - { - 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, - 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, - 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, - 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, - 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, - 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, - 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, - 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, - 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, - 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, - 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, - 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, - 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, - 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, - 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, - 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, - 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, - 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, - 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, - 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, - 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, - 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, - 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, - 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, - 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, - 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, - 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, - 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, - 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, - 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, - 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, - 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, - 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, - 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, - 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, - 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, - 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, - 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, - 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, - 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, - 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, - 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, - 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, - 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, - 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, - 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, - 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, - 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, - 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, - 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, - 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, - 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, - 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, - 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, - 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, - 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, - 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, - 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, - 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, - 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, - 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, - 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, - 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, - 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0}, - { - 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, - 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, - 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, - 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, - 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, - 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, - 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, - 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, - 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, - 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, - 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, - 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, - 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, - 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, - 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, - 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, - 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, - 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, - 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, - 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, - 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, - 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, - 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, - 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, - 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, - 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, - 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, - 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, - 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, - 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, - 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, - 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, - 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, - 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, - 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, - 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, - 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, - 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, - 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, - 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, - 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, - 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, - 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, - 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, - 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, - 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, - 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, - 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, - 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, - 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, - 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, - 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, - 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, - 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, - 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, - 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, - 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, - 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, - 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, - 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, - 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, - 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, - 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, - 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6} - }, - { - 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, - 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, - 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, - 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, - 0x9216d5d9, 0x8979fb1b - } }; - - *c = initstate; -} - -u_int32_t -Blowfish_stream2word(const u_int8_t *data, u_int16_t databytes, - u_int16_t *current) -{ - u_int8_t i; - u_int16_t j; - u_int32_t temp; - - temp = 0x00000000; - j = *current; - - for (i = 0; i < 4; i++, j++) { - if (j >= databytes) - j = 0; - temp = (temp << 8) | data[j]; - } - - *current = j; - return temp; -} - -void -Blowfish_expand0state(blf_ctx *c, const u_int8_t *key, u_int16_t keybytes) -{ - u_int16_t i; - u_int16_t j; - u_int16_t k; - u_int32_t temp; - u_int32_t datal; - u_int32_t datar; - - j = 0; - for (i = 0; i < BLF_N + 2; i++) { - /* Extract 4 int8 to 1 int32 from keystream */ - temp = Blowfish_stream2word(key, keybytes, &j); - c->P[i] = c->P[i] ^ temp; - } - - j = 0; - datal = 0x00000000; - datar = 0x00000000; - for (i = 0; i < BLF_N + 2; i += 2) { - Blowfish_encipher(c, &datal, &datar); - - c->P[i] = datal; - c->P[i + 1] = datar; - } - - for (i = 0; i < 4; i++) { - for (k = 0; k < 256; k += 2) { - Blowfish_encipher(c, &datal, &datar); - - c->S[i][k] = datal; - c->S[i][k + 1] = datar; - } - } -} - - -void -Blowfish_expandstate(blf_ctx *c, const u_int8_t *data, u_int16_t databytes, - const u_int8_t *key, u_int16_t keybytes) -{ - u_int16_t i; - u_int16_t j; - u_int16_t k; - u_int32_t temp; - u_int32_t datal; - u_int32_t datar; - - j = 0; - for (i = 0; i < BLF_N + 2; i++) { - /* Extract 4 int8 to 1 int32 from keystream */ - temp = Blowfish_stream2word(key, keybytes, &j); - c->P[i] = c->P[i] ^ temp; - } - - j = 0; - datal = 0x00000000; - datar = 0x00000000; - for (i = 0; i < BLF_N + 2; i += 2) { - datal ^= Blowfish_stream2word(data, databytes, &j); - datar ^= Blowfish_stream2word(data, databytes, &j); - Blowfish_encipher(c, &datal, &datar); - - c->P[i] = datal; - c->P[i + 1] = datar; - } - - for (i = 0; i < 4; i++) { - for (k = 0; k < 256; k += 2) { - datal ^= Blowfish_stream2word(data, databytes, &j); - datar ^= Blowfish_stream2word(data, databytes, &j); - Blowfish_encipher(c, &datal, &datar); - - c->S[i][k] = datal; - c->S[i][k + 1] = datar; - } - } - -} - -void -blf_key(blf_ctx *c, const u_int8_t *k, u_int16_t len) -{ - /* Initialize S-boxes and subkeys with Pi */ - Blowfish_initstate(c); - - /* Transform S-boxes and subkeys with key */ - Blowfish_expand0state(c, k, len); -} - -void -blf_enc(blf_ctx *c, u_int32_t *data, u_int16_t blocks) -{ - u_int32_t *d; - u_int16_t i; - - d = data; - for (i = 0; i < blocks; i++) { - Blowfish_encipher(c, d, d + 1); - d += 2; - } -} - -void -blf_dec(blf_ctx *c, u_int32_t *data, u_int16_t blocks) -{ - u_int32_t *d; - u_int16_t i; - - d = data; - for (i = 0; i < blocks; i++) { - Blowfish_decipher(c, d, d + 1); - d += 2; - } -} - -void -blf_ecb_encrypt(blf_ctx *c, u_int8_t *data, u_int32_t len) -{ - u_int32_t l, r; - u_int32_t i; - - for (i = 0; i < len; i += 8) { - l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; - r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; - Blowfish_encipher(c, &l, &r); - data[0] = l >> 24 & 0xff; - data[1] = l >> 16 & 0xff; - data[2] = l >> 8 & 0xff; - data[3] = l & 0xff; - data[4] = r >> 24 & 0xff; - data[5] = r >> 16 & 0xff; - data[6] = r >> 8 & 0xff; - data[7] = r & 0xff; - data += 8; - } -} - -void -blf_ecb_decrypt(blf_ctx *c, u_int8_t *data, u_int32_t len) -{ - u_int32_t l, r; - u_int32_t i; - - for (i = 0; i < len; i += 8) { - l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; - r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; - Blowfish_decipher(c, &l, &r); - data[0] = l >> 24 & 0xff; - data[1] = l >> 16 & 0xff; - data[2] = l >> 8 & 0xff; - data[3] = l & 0xff; - data[4] = r >> 24 & 0xff; - data[5] = r >> 16 & 0xff; - data[6] = r >> 8 & 0xff; - data[7] = r & 0xff; - data += 8; - } -} - -void -blf_cbc_encrypt(blf_ctx *c, u_int8_t *iv, u_int8_t *data, u_int32_t len) -{ - u_int32_t l, r; - u_int32_t i, j; - - for (i = 0; i < len; i += 8) { - for (j = 0; j < 8; j++) - data[j] ^= iv[j]; - l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; - r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; - Blowfish_encipher(c, &l, &r); - data[0] = l >> 24 & 0xff; - data[1] = l >> 16 & 0xff; - data[2] = l >> 8 & 0xff; - data[3] = l & 0xff; - data[4] = r >> 24 & 0xff; - data[5] = r >> 16 & 0xff; - data[6] = r >> 8 & 0xff; - data[7] = r & 0xff; - iv = data; - data += 8; - } -} - -void -blf_cbc_decrypt(blf_ctx *c, u_int8_t *iva, u_int8_t *data, u_int32_t len) -{ - u_int32_t l, r; - u_int8_t *iv; - u_int32_t i, j; - - iv = data + len - 16; - data = data + len - 8; - for (i = len - 8; i >= 8; i -= 8) { - l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; - r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; - Blowfish_decipher(c, &l, &r); - data[0] = l >> 24 & 0xff; - data[1] = l >> 16 & 0xff; - data[2] = l >> 8 & 0xff; - data[3] = l & 0xff; - data[4] = r >> 24 & 0xff; - data[5] = r >> 16 & 0xff; - data[6] = r >> 8 & 0xff; - data[7] = r & 0xff; - for (j = 0; j < 8; j++) - data[j] ^= iv[j]; - iv -= 8; - data -= 8; - } - l = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3]; - r = data[4] << 24 | data[5] << 16 | data[6] << 8 | data[7]; - Blowfish_decipher(c, &l, &r); - data[0] = l >> 24 & 0xff; - data[1] = l >> 16 & 0xff; - data[2] = l >> 8 & 0xff; - data[3] = l & 0xff; - data[4] = r >> 24 & 0xff; - data[5] = r >> 16 & 0xff; - data[6] = r >> 8 & 0xff; - data[7] = r & 0xff; - for (j = 0; j < 8; j++) - data[j] ^= iva[j]; -} - -#if 0 -void -report(u_int32_t data[], u_int16_t len) -{ - u_int16_t i; - for (i = 0; i < len; i += 2) - printf("Block %0hd: %08lx %08lx.\n", - i / 2, data[i], data[i + 1]); -} -void -main(void) -{ - - blf_ctx c; - char key[] = "AAAAA"; - char key2[] = "abcdefghijklmnopqrstuvwxyz"; - - u_int32_t data[10]; - u_int32_t data2[] = - {0x424c4f57l, 0x46495348l}; - - u_int16_t i; - - /* First test */ - for (i = 0; i < 10; i++) - data[i] = i; - - blf_key(&c, (u_int8_t *) key, 5); - blf_enc(&c, data, 5); - blf_dec(&c, data, 1); - blf_dec(&c, data + 2, 4); - printf("Should read as 0 - 9.\n"); - report(data, 10); - - /* Second test */ - blf_key(&c, (u_int8_t *) key2, strlen(key2)); - blf_enc(&c, data2, 1); - printf("\nShould read as: 0x324ed0fe 0xf413a203.\n"); - report(data2, 2); - blf_dec(&c, data2, 1); - report(data2, 2); -} -#endif - -#endif /* !defined(HAVE_BCRYPT_PBKDF) && (!defined(HAVE_BLOWFISH_INITSTATE) || \ - !defined(HAVE_BLOWFISH_EXPAND0STATE) || !defined(HAVE_BLF_ENC)) */ - diff --git a/src/crypto/ssh/includes.h b/src/crypto/ssh/includes.h deleted file mode 100644 index ab29d77dd..000000000 --- a/src/crypto/ssh/includes.h +++ /dev/null @@ -1,19 +0,0 @@ -// mimic openSSH-portable's includes.h file to be able to use -// its unmodified blowfish code - -#define HAVE_BLF_H - -#ifndef _GNU_SOURCE -#define _GNU_SOURCE /* activate extra prototypes for glibc */ -#endif -#include - -#if defined(_WIN32) || defined(__HAIKU__) -#include - -typedef uint32_t u_int32_t; -typedef uint16_t u_int16_t; -typedef uint8_t u_int8_t; - -#define bzero(p, s) memset(p, 0, s) -#endif diff --git a/src/fdosecrets/CMakeLists.txt b/src/fdosecrets/CMakeLists.txt index 35728a179..fec3f99d0 100644 --- a/src/fdosecrets/CMakeLists.txt +++ b/src/fdosecrets/CMakeLists.txt @@ -18,9 +18,6 @@ if(WITH_XC_FDOSECRETS) # setting storage FdoSecretsSettings.cpp - # gcrypt MPI wrapper - GcryptMPI.cpp - # dbus objects dbus/DBusClient.cpp dbus/DBusMgr.cpp @@ -34,5 +31,5 @@ if(WITH_XC_FDOSECRETS) objects/Prompt.cpp dbus/DBusTypes.cpp ) - target_link_libraries(fdosecrets Qt5::Core Qt5::Widgets Qt5::DBus ${GCRYPT_LIBRARIES}) + target_link_libraries(fdosecrets Qt5::Core Qt5::Widgets Qt5::DBus ${BOTAN2_LIBRARIES}) endif() diff --git a/src/fdosecrets/GcryptMPI.cpp b/src/fdosecrets/GcryptMPI.cpp deleted file mode 100644 index 4fc792deb..000000000 --- a/src/fdosecrets/GcryptMPI.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2019 Aetf - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "GcryptMPI.h" - -GcryptMPI MpiFromBytes(const QByteArray& bytes, bool secure, gcry_mpi_format format) -{ - auto bufLen = static_cast(bytes.size()); - - const char* buf = nullptr; - - // gcry_mpi_scan uses secure memory only if input buffer is secure memory, so we have to make a copy - GcryptMemPtr secureBuf; - if (secure) { - secureBuf.reset(static_cast(gcry_malloc_secure(bufLen))); - memcpy(secureBuf.get(), bytes.data(), bufLen); - buf = secureBuf.get(); - } else { - buf = bytes.data(); - } - - gcry_mpi_t rawMpi; - auto err = gcry_mpi_scan(&rawMpi, format, buf, format == GCRYMPI_FMT_HEX ? 0 : bufLen, nullptr); - GcryptMPI mpi(rawMpi); - - if (gcry_err_code(err) != GPG_ERR_NO_ERROR) { - mpi.reset(); - } - - return mpi; -} - -GcryptMPI MpiFromHex(const char* hex, bool secure) -{ - return MpiFromBytes(QByteArray::fromRawData(hex, static_cast(strlen(hex) + 1)), secure, GCRYMPI_FMT_HEX); -} - -QByteArray MpiToBytes(const GcryptMPI& mpi) -{ - unsigned char* buf = nullptr; - size_t buflen = 0; - gcry_mpi_aprint(GCRYMPI_FMT_USG, &buf, &buflen, mpi.get()); - - QByteArray bytes(reinterpret_cast(buf), static_cast(buflen)); - - gcry_free(buf); - - return bytes; -} diff --git a/src/fdosecrets/GcryptMPI.h b/src/fdosecrets/GcryptMPI.h deleted file mode 100644 index bbc3d0439..000000000 --- a/src/fdosecrets/GcryptMPI.h +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (C) 2019 Aetf - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef KEEPASSXC_GCRYPTMPI_H -#define KEEPASSXC_GCRYPTMPI_H - -#include - -#include - -#include -#include - -template using deleter_from_fn = std::integral_constant; - -/** - * A simple RAII wrapper for gcry_mpi_t - */ -using GcryptMPI = std::unique_ptr>; - -/** - * A simple RAII wrapper for libgcrypt allocated memory - */ -using GcryptMemPtr = std::unique_ptr>; - -/** - * Parse a QByteArray as MPI - * @param bytes - * @param secure - * @param format - * @return - */ -GcryptMPI MpiFromBytes(const QByteArray& bytes, bool secure = true, gcry_mpi_format format = GCRYMPI_FMT_USG); - -/** - * Parse MPI from hex data - * @param hex - * @param secure - * @return - */ -GcryptMPI MpiFromHex(const char* hex, bool secure = true); - -/** - * Dump MPI to bytes in USG format - * @param mpi - * @return - */ -QByteArray MpiToBytes(const GcryptMPI& mpi); - -#endif // KEEPASSXC_GCRYPTMPI_H diff --git a/src/fdosecrets/objects/SessionCipher.cpp b/src/fdosecrets/objects/SessionCipher.cpp index efb6da1fe..b7129911b 100644 --- a/src/fdosecrets/objects/SessionCipher.cpp +++ b/src/fdosecrets/objects/SessionCipher.cpp @@ -17,29 +17,11 @@ #include "SessionCipher.h" -#include "crypto/CryptoHash.h" #include "crypto/Random.h" #include "crypto/SymmetricCipher.h" -#include - -#include - -namespace -{ - constexpr const auto IETF1024_SECOND_OAKLEY_GROUP_P_HEX = "FFFFFFFFFFFFFFFFC90FDAA22168C234" - "C4C6628B80DC1CD129024E088A67CC74" - "020BBEA63B139B22514A08798E3404DD" - "EF9519B3CD3A431B302B0A6DF25F1437" - "4FE1356D6D51C245E485B576625E7EC6" - "F44C42E9A637ED6B0BFF5CB6F406B7ED" - "EE386BFB5A899FA5AE9F24117C4B1FE6" - "49286651ECE65381FFFFFFFFFFFFFFFF"; - constexpr const size_t KEY_SIZE_BYTES = 128; - constexpr const int AES_KEY_LEN = 16; // 128 bits - - const auto IETF1024_SECOND_OAKLEY_GROUP_P = MpiFromHex(IETF1024_SECOND_OAKLEY_GROUP_P_HEX, false); -} // namespace +#include +#include namespace FdoSecrets { @@ -47,128 +29,56 @@ namespace FdoSecrets constexpr char PlainCipher::Algorithm[]; constexpr char DhIetf1024Sha256Aes128CbcPkcs7::Algorithm[]; - DhIetf1024Sha256Aes128CbcPkcs7::DhIetf1024Sha256Aes128CbcPkcs7(const QByteArray& clientPublicKeyBytes) - : m_valid(false) + DhIetf1024Sha256Aes128CbcPkcs7::DhIetf1024Sha256Aes128CbcPkcs7(const QByteArray& clientPublicKey) { - // read client public key - auto clientPub = MpiFromBytes(clientPublicKeyBytes, false); - - // generate server side private, 128 bytes - GcryptMPI serverPrivate = nullptr; - if (NextPrivKey) { - serverPrivate = std::move(NextPrivKey); - } else { - serverPrivate.reset(gcry_mpi_snew(KEY_SIZE_BYTES * 8)); - gcry_mpi_randomize(serverPrivate.get(), KEY_SIZE_BYTES * 8, GCRY_STRONG_RANDOM); + try { + m_privateKey.reset(new Botan::DH_PrivateKey(*randomGen()->getRng(), Botan::DL_Group("modp/ietf/1024"))); + m_valid = updateClientPublicKey(clientPublicKey); + } catch (std::exception& e) { + qCritical("Failed to derive DH key: %s", e.what()); + m_privateKey.reset(); + m_valid = false; } - - // generate server side public key - GcryptMPI serverPublic = nullptr; - if (NextPubKey) { - serverPublic = std::move(NextPubKey); - } else { - serverPublic.reset(gcry_mpi_snew(KEY_SIZE_BYTES * 8)); - // the generator of Second Oakley Group is 2 - gcry_mpi_powm( - serverPublic.get(), GCRYMPI_CONST_TWO, serverPrivate.get(), IETF1024_SECOND_OAKLEY_GROUP_P.get()); - } - - initialize(std::move(clientPub), std::move(serverPublic), std::move(serverPrivate)); } - bool - DhIetf1024Sha256Aes128CbcPkcs7::initialize(GcryptMPI clientPublic, GcryptMPI serverPublic, GcryptMPI serverPrivate) + bool DhIetf1024Sha256Aes128CbcPkcs7::updateClientPublicKey(const QByteArray& clientPublicKey) { - QByteArray commonSecretBytes; - if (!diffieHullman(clientPublic, serverPrivate, commonSecretBytes)) { + if (!m_privateKey) { return false; } - m_privateKey = MpiToBytes(serverPrivate); - m_publicKey = MpiToBytes(serverPublic); - - m_aesKey = hkdf(commonSecretBytes); - - m_valid = true; - return true; - } - - bool DhIetf1024Sha256Aes128CbcPkcs7::diffieHullman(const GcryptMPI& clientPub, - const GcryptMPI& serverPrivate, - QByteArray& commonSecretBytes) - { - if (!clientPub || !serverPrivate) { + try { + Botan::secure_vector salt(32, '\0'); + auto dhka = m_privateKey->create_key_agreement_op(*randomGen()->getRng(), "HKDF(SHA-256)", ""); + auto aesKey = dhka->agree(16, + reinterpret_cast(clientPublicKey.constData()), + clientPublicKey.size(), + salt.data(), + salt.size()); + m_aesKey = QByteArray(reinterpret_cast(aesKey.data()), aesKey.size()); + return true; + } catch (std::exception& e) { + qCritical("Failed to update client public key: %s", e.what()); return false; } - - // calc common secret - GcryptMPI commonSecret(gcry_mpi_snew(KEY_SIZE_BYTES * 8)); - gcry_mpi_powm(commonSecret.get(), clientPub.get(), serverPrivate.get(), IETF1024_SECOND_OAKLEY_GROUP_P.get()); - commonSecretBytes = MpiToBytes(commonSecret); - - return true; - } - - QByteArray DhIetf1024Sha256Aes128CbcPkcs7::hkdf(const QByteArray& IKM) - { - // HKDF-Extract(salt, IKM) -> PRK - // PRK = HMAC-Hash(salt, IKM) - - // we use NULL salt as per spec - auto PRK = CryptoHash::hmac(IKM, - QByteArrayLiteral("\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0" - "\0\0\0\0\0\0\0\0"), - CryptoHash::Sha256); - - // HKDF-Expand(PRK, info, L) -> OKM - // N = ceil(L/HashLen) - // T = T(1) | T(2) | T(3) | ... | T(N) - // OKM = first L octets of T - // where: - // T(0) = empty string (zero length) - // T(1) = HMAC-Hash(PRK, T(0) | info | 0x01) - // T(2) = HMAC-Hash(PRK, T(1) | info | 0x02) - // T(3) = HMAC-Hash(PRK, T(2) | info | 0x03) - // ... - // - // (where the constant concatenated to the end of each T(n) is a - // single octet.) - - // we use empty info as per spec - // HashLen = 32 (sha256) - // L = 16 (16 * 8 = 128 bits) - // N = ceil(16/32) = 1 - - auto T1 = CryptoHash::hmac(QByteArrayLiteral("\x01"), PRK, CryptoHash::Sha256); - - // resulting AES key is first 128 bits - Q_ASSERT(T1.size() >= AES_KEY_LEN); - auto OKM = T1.left(AES_KEY_LEN); - return OKM; } Secret DhIetf1024Sha256Aes128CbcPkcs7::encrypt(const Secret& input) { Secret output = input; - output.value.clear(); output.parameters.clear(); + output.value.clear(); - SymmetricCipher encrypter(SymmetricCipher::Aes128, SymmetricCipher::Cbc, SymmetricCipher::Encrypt); - - auto IV = randomGen()->randomArray(SymmetricCipher::algorithmIvSize(SymmetricCipher::Aes128)); - if (!encrypter.init(m_aesKey, IV)) { + SymmetricCipher encrypter; + auto IV = randomGen()->randomArray(SymmetricCipher::defaultIvSize(SymmetricCipher::Aes128_CBC)); + if (!encrypter.init(SymmetricCipher::Aes128_CBC, SymmetricCipher::Encrypt, m_aesKey, IV)) { qWarning() << "Error encrypt: " << encrypter.errorString(); return output; } output.parameters = IV; - - bool ok; output.value = input.value; - output.value = encrypter.process(padPkcs7(output.value, encrypter.blockSize()), &ok); - if (!ok) { + if (!encrypter.finish(output.value)) { qWarning() << "Error encrypt: " << encrypter.errorString(); return output; } @@ -176,50 +86,23 @@ namespace FdoSecrets return output; } - QByteArray& DhIetf1024Sha256Aes128CbcPkcs7::padPkcs7(QByteArray& input, int blockSize) - { - // blockSize must be a power of 2. - Q_ASSERT_X(blockSize > 0 && !(blockSize & (blockSize - 1)), "padPkcs7", "blockSize must be a power of 2"); - - int padLen = blockSize - (input.size() & (blockSize - 1)); - - input.append(QByteArray(padLen, static_cast(padLen))); - return input; - } - Secret DhIetf1024Sha256Aes128CbcPkcs7::decrypt(const Secret& input) { - auto IV = input.parameters; - SymmetricCipher decrypter(SymmetricCipher::Aes128, SymmetricCipher::Cbc, SymmetricCipher::Decrypt); - if (!decrypter.init(m_aesKey, IV)) { + SymmetricCipher decrypter; + if (!decrypter.init(SymmetricCipher::Aes128_CBC, SymmetricCipher::Decrypt, m_aesKey, input.parameters)) { qWarning() << "Error decoding: " << decrypter.errorString(); return input; } - bool ok; + Secret output = input; output.parameters.clear(); - output.value = decrypter.process(input.value, &ok); - - if (!ok) { + if (!decrypter.finish(output.value)) { qWarning() << "Error decoding: " << decrypter.errorString(); return input; } - - unpadPkcs7(output.value); return output; } - QByteArray& DhIetf1024Sha256Aes128CbcPkcs7::unpadPkcs7(QByteArray& input) - { - if (input.isEmpty()) { - return input; - } - - int padLen = input[input.size() - 1]; - input.chop(padLen); - return input; - } - bool DhIetf1024Sha256Aes128CbcPkcs7::isValid() const { return m_valid; @@ -227,16 +110,10 @@ namespace FdoSecrets QVariant DhIetf1024Sha256Aes128CbcPkcs7::negotiationOutput() const { - return m_publicKey; + if (m_valid) { + auto pubkey = m_privateKey->public_value(); + return QByteArray(reinterpret_cast(pubkey.data()), pubkey.size()); + } + return {}; } - - void DhIetf1024Sha256Aes128CbcPkcs7::fixNextServerKeys(GcryptMPI priv, GcryptMPI pub) - { - NextPrivKey = std::move(priv); - NextPubKey = std::move(pub); - } - - GcryptMPI DhIetf1024Sha256Aes128CbcPkcs7::NextPrivKey = nullptr; - GcryptMPI DhIetf1024Sha256Aes128CbcPkcs7::NextPubKey = nullptr; - } // namespace FdoSecrets diff --git a/src/fdosecrets/objects/SessionCipher.h b/src/fdosecrets/objects/SessionCipher.h index e1450784b..e9b6262e5 100644 --- a/src/fdosecrets/objects/SessionCipher.h +++ b/src/fdosecrets/objects/SessionCipher.h @@ -18,15 +18,15 @@ #ifndef KEEPASSXC_FDOSECRETS_SESSIONCIPHER_H #define KEEPASSXC_FDOSECRETS_SESSIONCIPHER_H -#include "fdosecrets/GcryptMPI.h" #include "fdosecrets/objects/Session.h" -class TestFdoSecrets; -class TestGuiFdoSecrets; +namespace Botan +{ + class DH_PrivateKey; +} namespace FdoSecrets { - class CipherPair { Q_DISABLE_COPY(CipherPair) @@ -69,77 +69,24 @@ namespace FdoSecrets class DhIetf1024Sha256Aes128CbcPkcs7 : public CipherPair { - bool m_valid; - QByteArray m_privateKey; - QByteArray m_publicKey; - QByteArray m_aesKey; - - /** - * Diffie Hullman Key Exchange - * Given client public key, generate server private/public key pair and common secret. - * This also sets m_publicKey to server's public key - * @param clientPublicKey client public key - * @param serverPrivate server private key - * @param commonSecretBytes output common secret - * @return true on success. - */ - bool - diffieHullman(const GcryptMPI& clientPublicKey, const GcryptMPI& serverPrivate, QByteArray& commonSecretBytes); - - /** - * Perform HKDF defined in RFC5869, using sha256 as hash function - * @param IKM input keying material - * @return derived 128-bit key suitable for AES - */ - QByteArray hkdf(const QByteArray& IKM); - - /** - * Add PKCS#7 style padding to input inplace - * @param input - * @param blockSize the block size to use, must be 2's power - * @return reference to input for chaining - */ - QByteArray& padPkcs7(QByteArray& input, int blockSize); - - /** - * Remove PKCS#7 style padding from input inplace - * @param input - * @return reference to input for chaining - */ - QByteArray& unpadPkcs7(QByteArray& input); - - bool initialize(GcryptMPI clientPublic, GcryptMPI serverPublic, GcryptMPI serverPrivate); - - DhIetf1024Sha256Aes128CbcPkcs7() - : m_valid(false) - { - } - public: static constexpr const char Algorithm[] = "dh-ietf1024-sha256-aes128-cbc-pkcs7"; - explicit DhIetf1024Sha256Aes128CbcPkcs7(const QByteArray& clientPublicKeyBytes); + explicit DhIetf1024Sha256Aes128CbcPkcs7(const QByteArray& clientPublicKey); Secret encrypt(const Secret& input) override; - Secret decrypt(const Secret& input) override; - bool isValid() const override; - QVariant negotiationOutput() const override; - private: - /** - * For test only, fix the server side private and public key. - */ - static void fixNextServerKeys(GcryptMPI priv, GcryptMPI pub); - static GcryptMPI NextPrivKey; - static GcryptMPI NextPubKey; + bool updateClientPublicKey(const QByteArray& clientPublicKey); private: Q_DISABLE_COPY(DhIetf1024Sha256Aes128CbcPkcs7); - friend class ::TestFdoSecrets; - friend class ::TestGuiFdoSecrets; + + bool m_valid = false; + QSharedPointer m_privateKey; + QByteArray m_aesKey; }; } // namespace FdoSecrets diff --git a/src/format/Kdbx3Reader.cpp b/src/format/Kdbx3Reader.cpp index 35244f618..752e3943d 100644 --- a/src/format/Kdbx3Reader.cpp +++ b/src/format/Kdbx3Reader.cpp @@ -65,10 +65,9 @@ bool Kdbx3Reader::readDatabaseImpl(QIODevice* device, hash.addData(db->transformedDatabaseKey()); QByteArray finalKey = hash.result(); - SymmetricCipher::Algorithm cipher = SymmetricCipher::cipherToAlgorithm(db->cipher()); - SymmetricCipherStream cipherStream( - device, cipher, SymmetricCipher::algorithmMode(cipher), SymmetricCipher::Decrypt); - if (!cipherStream.init(finalKey, m_encryptionIV)) { + auto mode = SymmetricCipher::cipherUuidToMode(db->cipher()); + SymmetricCipherStream cipherStream(device); + if (!cipherStream.init(mode, SymmetricCipher::Decrypt, finalKey, m_encryptionIV)) { raiseError(cipherStream.errorString()); return false; } @@ -106,8 +105,8 @@ bool Kdbx3Reader::readDatabaseImpl(QIODevice* device, xmlDevice = ioCompressor.data(); } - KeePass2RandomStream randomStream(KeePass2::ProtectedStreamAlgo::Salsa20); - if (!randomStream.init(m_protectedStreamKey)) { + KeePass2RandomStream randomStream; + if (!randomStream.init(SymmetricCipher::Salsa20, m_protectedStreamKey)) { raiseError(randomStream.errorString()); return false; } diff --git a/src/format/Kdbx3Writer.cpp b/src/format/Kdbx3Writer.cpp index b1c1b820a..fae213234 100644 --- a/src/format/Kdbx3Writer.cpp +++ b/src/format/Kdbx3Writer.cpp @@ -35,8 +35,15 @@ bool Kdbx3Writer::writeDatabase(QIODevice* device, Database* db) m_error = false; m_errorStr.clear(); + auto mode = SymmetricCipher::cipherUuidToMode(db->cipher()); + int ivSize = SymmetricCipher::defaultIvSize(mode); + if (ivSize < 0) { + raiseError(tr("Invalid symmetric cipher IV size.", "IV = Initialization Vector for symmetric cipher")); + return false; + } + QByteArray masterSeed = randomGen()->randomArray(32); - QByteArray encryptionIV = randomGen()->randomArray(16); + QByteArray encryptionIV = randomGen()->randomArray(ivSize); QByteArray protectedStreamKey = randomGen()->randomArray(32); QByteArray startBytes = randomGen()->randomArray(32); QByteArray endOfHeader = "\r\n\r\n"; @@ -95,9 +102,8 @@ bool Kdbx3Writer::writeDatabase(QIODevice* device, Database* db) const QByteArray headerHash = CryptoHash::hash(header.data(), CryptoHash::Sha256); // write cipher stream - SymmetricCipher::Algorithm algo = SymmetricCipher::cipherToAlgorithm(db->cipher()); - SymmetricCipherStream cipherStream(device, algo, SymmetricCipher::algorithmMode(algo), SymmetricCipher::Encrypt); - cipherStream.init(finalKey, encryptionIV); + SymmetricCipherStream cipherStream(device); + cipherStream.init(mode, SymmetricCipher::Encrypt, finalKey, encryptionIV); if (!cipherStream.open(QIODevice::WriteOnly)) { raiseError(cipherStream.errorString()); return false; @@ -127,8 +133,8 @@ bool Kdbx3Writer::writeDatabase(QIODevice* device, Database* db) Q_ASSERT(outputDevice); - KeePass2RandomStream randomStream(KeePass2::ProtectedStreamAlgo::Salsa20); - if (!randomStream.init(protectedStreamKey)) { + KeePass2RandomStream randomStream; + if (!randomStream.init(SymmetricCipher::Salsa20, protectedStreamKey)) { raiseError(randomStream.errorString()); return false; } diff --git a/src/format/Kdbx4Reader.cpp b/src/format/Kdbx4Reader.cpp index baeab903c..7d583f9fb 100644 --- a/src/format/Kdbx4Reader.cpp +++ b/src/format/Kdbx4Reader.cpp @@ -83,13 +83,13 @@ bool Kdbx4Reader::readDatabaseImpl(QIODevice* device, return false; } - SymmetricCipher::Algorithm cipher = SymmetricCipher::cipherToAlgorithm(db->cipher()); - if (cipher == SymmetricCipher::InvalidAlgorithm) { + auto mode = SymmetricCipher::cipherUuidToMode(db->cipher()); + if (mode == SymmetricCipher::InvalidMode) { raiseError(tr("Unknown cipher")); return false; } - SymmetricCipherStream cipherStream(&hmacStream, cipher, SymmetricCipher::algorithmMode(cipher), SymmetricCipher::Decrypt); - if (!cipherStream.init(finalKey, m_encryptionIV)) { + SymmetricCipherStream cipherStream(&hmacStream); + if (!cipherStream.init(mode, SymmetricCipher::Decrypt, finalKey, m_encryptionIV)) { raiseError(cipherStream.errorString()); return false; } @@ -121,8 +121,20 @@ bool Kdbx4Reader::readDatabaseImpl(QIODevice* device, return false; } - KeePass2RandomStream randomStream(m_irsAlgo); - if (!randomStream.init(m_protectedStreamKey)) { + // TODO: Convert m_irsAlgo to Mode + switch (m_irsAlgo) { + case KeePass2::ProtectedStreamAlgo::Salsa20: + mode = SymmetricCipher::Salsa20; + break; + case KeePass2::ProtectedStreamAlgo::ChaCha20: + mode = SymmetricCipher::ChaCha20; + break; + default: + mode = SymmetricCipher::InvalidMode; + } + + KeePass2RandomStream randomStream; + if (!randomStream.init(mode, m_protectedStreamKey)) { raiseError(randomStream.errorString()); return false; } diff --git a/src/format/Kdbx4Writer.cpp b/src/format/Kdbx4Writer.cpp index 1810d9aa7..9961e021d 100644 --- a/src/format/Kdbx4Writer.cpp +++ b/src/format/Kdbx4Writer.cpp @@ -36,12 +36,12 @@ bool Kdbx4Writer::writeDatabase(QIODevice* device, Database* db) m_error = false; m_errorStr.clear(); - SymmetricCipher::Algorithm algo = SymmetricCipher::cipherToAlgorithm(db->cipher()); - if (algo == SymmetricCipher::InvalidAlgorithm) { + auto mode = SymmetricCipher::cipherUuidToMode(db->cipher()); + if (mode == SymmetricCipher::InvalidMode) { raiseError(tr("Invalid symmetric cipher algorithm.")); return false; } - int ivSize = SymmetricCipher::algorithmIvSize(algo); + int ivSize = SymmetricCipher::defaultIvSize(mode); if (ivSize < 0) { raiseError(tr("Invalid symmetric cipher IV size.", "IV = Initialization Vector for symmetric cipher")); return false; @@ -124,10 +124,9 @@ bool Kdbx4Writer::writeDatabase(QIODevice* device, Database* db) return false; } - cipherStream.reset(new SymmetricCipherStream( - hmacBlockStream.data(), algo, SymmetricCipher::algorithmMode(algo), SymmetricCipher::Encrypt)); + cipherStream.reset(new SymmetricCipherStream(hmacBlockStream.data())); - if (!cipherStream->init(finalKey, encryptionIV)) { + if (!cipherStream->init(mode, SymmetricCipher::Encrypt, finalKey, encryptionIV)) { raiseError(cipherStream->errorString()); return false; } @@ -165,8 +164,8 @@ bool Kdbx4Writer::writeDatabase(QIODevice* device, Database* db) CHECK_RETURN_FALSE(writeInnerHeaderField(outputDevice, KeePass2::InnerHeaderFieldID::End, QByteArray())); - KeePass2RandomStream randomStream(KeePass2::ProtectedStreamAlgo::ChaCha20); - if (!randomStream.init(protectedStreamKey)) { + KeePass2RandomStream randomStream; + if (!randomStream.init(SymmetricCipher::ChaCha20, protectedStreamKey)) { raiseError(randomStream.errorString()); return false; } @@ -207,7 +206,7 @@ bool Kdbx4Writer::writeDatabase(QIODevice* device, Database* db) bool Kdbx4Writer::writeInnerHeaderField(QIODevice* device, KeePass2::InnerHeaderFieldID fieldId, const QByteArray& data) { QByteArray fieldIdArr; - fieldIdArr[0] = static_cast(fieldId); + fieldIdArr.append(static_cast(fieldId)); CHECK_RETURN_FALSE(writeData(device, fieldIdArr)); CHECK_RETURN_FALSE( writeData(device, Endian::sizedIntToBytes(static_cast(data.size()), KeePass2::BYTEORDER))); @@ -294,7 +293,7 @@ bool Kdbx4Writer::serializeVariantMap(const QVariantMap& map, QByteArray& output return false; } QByteArray typeBytes; - typeBytes[0] = static_cast(fieldType); + typeBytes.append(static_cast(fieldType)); QByteArray nameBytes = k.toUtf8(); QByteArray nameLenBytes = Endian::sizedIntToBytes(nameBytes.size(), KeePass2::BYTEORDER); QByteArray dataLenBytes = Endian::sizedIntToBytes(data.size(), KeePass2::BYTEORDER); @@ -307,7 +306,7 @@ bool Kdbx4Writer::serializeVariantMap(const QVariantMap& map, QByteArray& output } QByteArray endBytes; - endBytes[0] = static_cast(KeePass2::VariantMapFieldType::End); + endBytes.append(static_cast(KeePass2::VariantMapFieldType::End)); CHECK_RETURN_FALSE(buf.write(endBytes) == 1); return true; } diff --git a/src/format/KdbxReader.cpp b/src/format/KdbxReader.cpp index 6ae77662c..14966bcd2 100644 --- a/src/format/KdbxReader.cpp +++ b/src/format/KdbxReader.cpp @@ -129,7 +129,7 @@ void KdbxReader::setCipher(const QByteArray& data) return; } - if (SymmetricCipher::cipherToAlgorithm(uuid) == SymmetricCipher::InvalidAlgorithm) { + if (SymmetricCipher::cipherUuidToMode(uuid) == SymmetricCipher::InvalidMode) { raiseError(tr("Unsupported cipher")); return; } diff --git a/src/format/KeePass1Reader.cpp b/src/format/KeePass1Reader.cpp index 96e9c1914..4d372b575 100644 --- a/src/format/KeePass1Reader.cpp +++ b/src/format/KeePass1Reader.cpp @@ -332,15 +332,14 @@ KeePass1Reader::testKeys(const QString& password, const QByteArray& keyfileData, if (finalKey.isEmpty()) { return nullptr; } - if (m_encryptionFlags & KeePass1::Rijndael) { - cipherStream.reset(new SymmetricCipherStream( - m_device, SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt)); - } else { - cipherStream.reset(new SymmetricCipherStream( - m_device, SymmetricCipher::Twofish, SymmetricCipher::Cbc, SymmetricCipher::Decrypt)); - } - if (!cipherStream->init(finalKey, m_encryptionIV)) { + cipherStream.reset(new SymmetricCipherStream(m_device)); + + auto mode = SymmetricCipher::Aes256_CBC; + if (m_encryptionFlags & KeePass1::Twofish) { + mode = SymmetricCipher::Twofish_CBC; + } + if (!cipherStream->init(mode, SymmetricCipher::Decrypt, finalKey, m_encryptionIV)) { raiseError(cipherStream->errorString()); return nullptr; } @@ -362,9 +361,13 @@ KeePass1Reader::testKeys(const QString& password, const QByteArray& keyfileData, return nullptr; } - cipherStream->open(QIODevice::ReadOnly); if (success) { + if (!cipherStream->init(mode, SymmetricCipher::Decrypt, finalKey, m_encryptionIV)) { + raiseError(cipherStream->errorString()); + return nullptr; + } + cipherStream->open(QIODevice::ReadOnly); break; } else { cipherStream.reset(); diff --git a/src/format/KeePass2RandomStream.cpp b/src/format/KeePass2RandomStream.cpp index 2f8c8340d..509e32513 100644 --- a/src/format/KeePass2RandomStream.cpp +++ b/src/format/KeePass2RandomStream.cpp @@ -20,23 +20,21 @@ #include "crypto/CryptoHash.h" #include "format/KeePass2.h" -KeePass2RandomStream::KeePass2RandomStream(KeePass2::ProtectedStreamAlgo algo) - : m_cipher(mapAlgo(algo), SymmetricCipher::Stream, SymmetricCipher::Encrypt) - , m_offset(0) +bool KeePass2RandomStream::init(SymmetricCipher::Mode mode, const QByteArray& key) { -} - -bool KeePass2RandomStream::init(const QByteArray& key) -{ - switch (m_cipher.algorithm()) { - case SymmetricCipher::Salsa20: - return m_cipher.init(CryptoHash::hash(key, CryptoHash::Sha256), KeePass2::INNER_STREAM_SALSA20_IV); + switch (mode) { + case SymmetricCipher::Salsa20: { + return m_cipher.init(mode, + SymmetricCipher::Encrypt, + CryptoHash::hash(key, CryptoHash::Sha256), + KeePass2::INNER_STREAM_SALSA20_IV); + } case SymmetricCipher::ChaCha20: { QByteArray keyIv = CryptoHash::hash(key, CryptoHash::Sha512); - return m_cipher.init(keyIv.left(32), keyIv.mid(32, 12)); + return m_cipher.init(mode, SymmetricCipher::Encrypt, keyIv.left(32), keyIv.mid(32, 12)); } default: - qWarning("Invalid stream algorithm (%d)", m_cipher.algorithm()); + qWarning("Invalid stream cipher mode (%d)", mode); break; } return false; @@ -111,23 +109,11 @@ bool KeePass2RandomStream::loadBlock() { Q_ASSERT(m_offset == m_buffer.size()); - m_buffer.fill('\0', m_cipher.blockSize()); - if (!m_cipher.processInPlace(m_buffer)) { + m_buffer.fill('\0', m_cipher.blockSize(m_cipher.mode())); + if (!m_cipher.process(m_buffer)) { return false; } m_offset = 0; return true; } - -SymmetricCipher::Algorithm KeePass2RandomStream::mapAlgo(KeePass2::ProtectedStreamAlgo algo) -{ - switch (algo) { - case KeePass2::ProtectedStreamAlgo::ChaCha20: - return SymmetricCipher::ChaCha20; - case KeePass2::ProtectedStreamAlgo::Salsa20: - return SymmetricCipher::Salsa20; - default: - return SymmetricCipher::InvalidAlgorithm; - } -} \ No newline at end of file diff --git a/src/format/KeePass2RandomStream.h b/src/format/KeePass2RandomStream.h index e41f5a5d6..d8d8dd0f9 100644 --- a/src/format/KeePass2RandomStream.h +++ b/src/format/KeePass2RandomStream.h @@ -26,9 +26,9 @@ class KeePass2RandomStream { public: - KeePass2RandomStream(KeePass2::ProtectedStreamAlgo algo); + KeePass2RandomStream() = default; - bool init(const QByteArray& key); + bool init(SymmetricCipher::Mode mode, const QByteArray& key); QByteArray randomBytes(int size, bool* ok); QByteArray process(const QByteArray& data, bool* ok); Q_REQUIRED_RESULT bool processInPlace(QByteArray& data); @@ -39,9 +39,7 @@ private: SymmetricCipher m_cipher; QByteArray m_buffer; - int m_offset; - - static SymmetricCipher::Algorithm mapAlgo(KeePass2::ProtectedStreamAlgo algo); + int m_offset = 0; }; #endif // KEEPASSX_KEEPASS2RANDOMSTREAM_H diff --git a/src/format/OpData01.cpp b/src/format/OpData01.cpp index d373e5814..76e729d66 100644 --- a/src/format/OpData01.cpp +++ b/src/format/OpData01.cpp @@ -69,8 +69,8 @@ bool OpData01::decode(const QByteArray& data, const QByteArray& key, const QByte return false; } - SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt); - if (!cipher.init(key, iv)) { + SymmetricCipher cipher; + if (!cipher.init(SymmetricCipher::Aes256_CBC, SymmetricCipher::Decrypt, key, iv)) { m_errorStr = tr("Unable to init cipher for opdata01: %1").arg(cipher.errorString()); return false; } @@ -82,7 +82,7 @@ bool OpData01::decode(const QByteArray& data, const QByteArray& key, const QByte * to the plaintext. Otherwise, between 1 and 15 (inclusive) bytes of random data are prepended to the plaintext * to achieve an even multiple of blocks. */ - const int blockSize = cipher.blockSize(); + const int blockSize = cipher.blockSize(cipher.mode()); int randomBytes = blockSize - (len % blockSize); if (randomBytes == 0) { // add random block @@ -111,7 +111,7 @@ bool OpData01::decode(const QByteArray& data, const QByteArray& key, const QByte return false; } - if (!cipher.processInPlace(qbaCT)) { + if (!cipher.process(qbaCT)) { m_errorStr = tr("Unable to process clearText in place"); return false; } diff --git a/src/format/OpVaultReader.cpp b/src/format/OpVaultReader.cpp index dab08bd82..c2337fdaf 100644 --- a/src/format/OpVaultReader.cpp +++ b/src/format/OpVaultReader.cpp @@ -28,7 +28,8 @@ #include #include #include -#include + +#include OpVaultReader::OpVaultReader(QObject* parent) : QObject(parent) @@ -366,30 +367,12 @@ OpVaultReader::decodeB64CompositeKeys(const QString& b64, const QByteArray& encK */ OpVaultReader::DerivedKeyHMAC* OpVaultReader::decodeCompositeKeys(const QByteArray& keyKey) { - const int encKeySize = 256 / 8; - const int hmacKeySize = 256 / 8; - const int digestSize = encKeySize + hmacKeySize; - auto result = new DerivedKeyHMAC; result->error = false; - result->encrypt = QByteArray(encKeySize, '\0'); - result->hmac = QByteArray(hmacKeySize, '\0'); - - const char* buffer_vp = keyKey.data(); - auto buf_len = size_t(keyKey.size()); - - const int algo = GCRY_MD_SHA512; - unsigned char digest[digestSize]; - gcry_md_hash_buffer(algo, digest, buffer_vp, buf_len); - - unsigned char* cp = digest; - for (int i = 0, len = encKeySize; i < len; ++i) { - result->encrypt[i] = *(cp++); - } - for (int i = 0, len = hmacKeySize; i < len; ++i) { - result->hmac[i] = *(cp++); - } + auto digest = CryptoHash::hash(keyKey, CryptoHash::Sha512); + result->encrypt = digest.left(32); + result->hmac = digest.right(32); return result; } @@ -403,43 +386,27 @@ OpVaultReader::DerivedKeyHMAC* OpVaultReader::decodeCompositeKeys(const QByteArr OpVaultReader::DerivedKeyHMAC* OpVaultReader::deriveKeysFromPassPhrase(QByteArray& salt, const QString& password, unsigned long iterations) { - const int derivedEncKeySize = 256 / 8; - const int derivedMACSize = 256 / 8; - const int keysize = derivedEncKeySize + derivedMACSize; - auto result = new DerivedKeyHMAC; result->error = false; - QByteArray keybuffer(keysize, '\0'); - auto err = gcry_kdf_derive(password.toUtf8().constData(), - password.size(), - GCRY_KDF_PBKDF2, - GCRY_MD_SHA512, - salt.constData(), - salt.size(), - iterations, - keysize, - keybuffer.data()); - if (err != 0) { + QByteArray out(64, '\0'); + try { + auto pwhash = Botan::PasswordHashFamily::create_or_throw("PBKDF2(SHA-512)")->from_iterations(iterations); + pwhash->derive_key(reinterpret_cast(out.data()), + out.size(), + password.toUtf8().constData(), + password.size(), + reinterpret_cast(salt.constData()), + salt.size()); + } catch (std::exception& e) { result->error = true; - result->errorStr = tr("Unable to derive master key: %1").arg(gcry_strerror(err)); + result->errorStr = tr("Unable to derive master key: %1").arg(e.what()); return result; } - if (keysize != keybuffer.size()) { - qWarning() << "Calling PBKDF2(keysize=" << keysize << "yielded" << keybuffer.size() << "bytes"; - } - QByteArray::const_iterator it = keybuffer.cbegin(); + result->encrypt = out.left(32); + result->hmac = out.right(32); - result->encrypt = QByteArray(derivedEncKeySize, '\0'); - for (int i = 0, len = derivedEncKeySize; i < len && it != keybuffer.cend(); ++i, ++it) { - result->encrypt[i] = *it; - } - - result->hmac = QByteArray(derivedMACSize, '\0'); - for (int i = 0; i < derivedMACSize && it != keybuffer.cend(); ++i, ++it) { - result->hmac[i] = *it; - } return result; } diff --git a/src/format/OpVaultReaderBandEntry.cpp b/src/format/OpVaultReaderBandEntry.cpp index f684561b8..c9f2045b3 100644 --- a/src/format/OpVaultReaderBandEntry.cpp +++ b/src/format/OpVaultReaderBandEntry.cpp @@ -80,12 +80,12 @@ bool OpVaultReader::decryptBandEntry(const QJsonObject& bandEntry, QByteArray iv = kBA.mid(0, 16); QByteArray keyAndMacKey = kBA.mid(iv.size(), 64); - SymmetricCipher cipher(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt); - if (!cipher.init(m_masterKey, iv)) { + SymmetricCipher cipher; + if (!cipher.init(SymmetricCipher::Aes256_CBC, SymmetricCipher::Decrypt, m_masterKey, iv)) { qCritical() << "Unable to init cipher using masterKey in UUID " << uuid; return false; } - if (!cipher.processInPlace(keyAndMacKey)) { + if (!cipher.process(keyAndMacKey)) { qCritical() << "Unable to decipher \"k\"(key+hmac) in UUID " << uuid; return false; } diff --git a/src/gui/DatabaseOpenWidget.cpp b/src/gui/DatabaseOpenWidget.cpp index 8ab2237f9..f65de6f5e 100644 --- a/src/gui/DatabaseOpenWidget.cpp +++ b/src/gui/DatabaseOpenWidget.cpp @@ -280,13 +280,14 @@ QSharedPointer DatabaseOpenWidget::buildDatabaseKey() databaseKey->clear(); // try to get, decrypt and use PasswordKey - QSharedPointer passwordKey = TouchID::getInstance().getKey(m_filename); - if (passwordKey != NULL) { + QByteArray passwordKey; + if (TouchID::getInstance().getKey(m_filename, passwordKey)) { // check if the user cancelled the operation - if (passwordKey.isNull()) + if (passwordKey.isNull()) { return QSharedPointer(); + } - databaseKey->addKey(PasswordKey::fromRawKey(*passwordKey)); + databaseKey->addKey(PasswordKey::fromRawKey(passwordKey)); } } #endif diff --git a/src/gui/entry/EditEntryWidget.cpp b/src/gui/entry/EditEntryWidget.cpp index b4339034a..b474ebaca 100644 --- a/src/gui/entry/EditEntryWidget.cpp +++ b/src/gui/entry/EditEntryWidget.cpp @@ -46,8 +46,8 @@ #include "core/TimeDelta.h" #include "core/Tools.h" #ifdef WITH_XC_SSHAGENT -#include "crypto/ssh/OpenSSHKey.h" #include "sshagent/KeeAgentSettings.h" +#include "sshagent/OpenSSHKey.h" #include "sshagent/SSHAgent.h" #endif #ifdef WITH_XC_BROWSER diff --git a/src/keeshare/CMakeLists.txt b/src/keeshare/CMakeLists.txt index 333f273a3..af4bc61a8 100644 --- a/src/keeshare/CMakeLists.txt +++ b/src/keeshare/CMakeLists.txt @@ -19,7 +19,7 @@ if(WITH_XC_KEESHARE) ) add_library(keeshare STATIC ${keeshare_SOURCES}) - target_link_libraries(keeshare PUBLIC Qt5::Core Qt5::Widgets ${GCRYPT_LIBRARIES} ${crypto_ssh_LIB}) + target_link_libraries(keeshare PUBLIC Qt5::Core Qt5::Widgets ${BOTAN2_LIBRARIES}) # Try to find libquazip5, if found, enable secure sharing find_package(QuaZip) diff --git a/src/keeshare/KeeShare.cpp b/src/keeshare/KeeShare.cpp index 31136a5c4..1ae19c9da 100644 --- a/src/keeshare/KeeShare.cpp +++ b/src/keeshare/KeeShare.cpp @@ -21,7 +21,6 @@ #include "core/DatabaseIcons.h" #include "core/Group.h" #include "core/Metadata.h" -#include "crypto/ssh/OpenSSHKey.h" #include "keeshare/ShareObserver.h" #include "keeshare/Signature.h" diff --git a/src/keeshare/KeeShare.h b/src/keeshare/KeeShare.h index 952711dbf..3a847fd0e 100644 --- a/src/keeshare/KeeShare.h +++ b/src/keeshare/KeeShare.h @@ -18,6 +18,7 @@ #ifndef KEEPASSXC_KEESHARE_H #define KEEPASSXC_KEESHARE_H +#include #include #include diff --git a/src/keeshare/KeeShareSettings.cpp b/src/keeshare/KeeShareSettings.cpp index a640cdaed..9cd399030 100644 --- a/src/keeshare/KeeShareSettings.cpp +++ b/src/keeshare/KeeShareSettings.cpp @@ -16,77 +16,41 @@ */ #include "KeeShareSettings.h" + #include "core/CustomData.h" #include "core/Database.h" #include "core/DatabaseIcons.h" #include "core/Group.h" #include "core/Metadata.h" -#include "crypto/ssh/OpenSSHKey.h" +#include "crypto/Random.h" #include "keeshare/Signature.h" #include #include #include +#include +#include +#include +#include +#include +#include #include namespace KeeShareSettings { namespace { - Certificate packCertificate(const OpenSSHKey& key, const QString& signer) - { - KeeShareSettings::Certificate extracted; - extracted.signer = signer; - Q_ASSERT(key.type() == "ssh-rsa"); - extracted.key = OpenSSHKey::serializeToBinary(OpenSSHKey::Public, key); - return extracted; - } - - Key packKey(const OpenSSHKey& key) - { - KeeShareSettings::Key extracted; - Q_ASSERT(key.type() == "ssh-rsa"); - extracted.key = OpenSSHKey::serializeToBinary(OpenSSHKey::Private, key); - return extracted; - } - - OpenSSHKey unpackKey(const Key& sign) - { - if (sign.key.isEmpty()) { - return OpenSSHKey(); - } - OpenSSHKey key = OpenSSHKey::restoreFromBinary(OpenSSHKey::Private, sign.key); - Q_ASSERT(key.type() == "ssh-rsa"); - return key; - } - - OpenSSHKey unpackCertificate(const Certificate& certificate) - { - if (certificate.key.isEmpty()) { - return OpenSSHKey(); - } - OpenSSHKey key = OpenSSHKey::restoreFromBinary(OpenSSHKey::Public, certificate.key); - Q_ASSERT(key.type() == "ssh-rsa"); - return key; - } - QString xmlSerialize(std::function specific) { QString buffer; QXmlStreamWriter writer(&buffer); writer.setCodec(QTextCodec::codecForName("UTF-8")); - writer.setAutoFormatting(true); - writer.setAutoFormattingIndent(2); - writer.writeStartDocument(); writer.writeStartElement("KeeShare"); - writer.writeAttribute("xmlns:xsd", "http://www.w3.org/2001/XMLSchema"); - writer.writeAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance"); specific(writer); writer.writeEndElement(); - writer.writeEndElement(); writer.writeEndDocument(); return buffer; } @@ -106,17 +70,23 @@ namespace KeeShareSettings if (certificate.isNull()) { return; } + auto berKey = Botan::PKCS8::BER_encode(*certificate.key); + auto baKey = QByteArray::fromRawData(reinterpret_cast(berKey.data()), berKey.size()); + writer.writeStartElement("Signer"); writer.writeCharacters(certificate.signer); writer.writeEndElement(); writer.writeStartElement("Key"); - writer.writeCharacters(certificate.key.toBase64()); + writer.writeCharacters(baKey.toBase64()); writer.writeEndElement(); } bool Certificate::operator==(const Certificate& other) const { - return key == other.key && signer == other.signer; + if (isNull() || other.isNull()) { + return isNull() == other.isNull(); + } + return key->private_key_bits() == other.key->private_key_bits() && signer == other.signer; } bool Certificate::operator!=(const Certificate& other) const @@ -126,7 +96,7 @@ namespace KeeShareSettings bool Certificate::isNull() const { - return key.isEmpty() && signer.isEmpty(); + return !key || signer.isEmpty(); } QString Certificate::fingerprint() const @@ -134,20 +104,7 @@ namespace KeeShareSettings if (isNull()) { return {}; } - return sshKey().fingerprint(); - } - - OpenSSHKey Certificate::sshKey() const - { - return unpackCertificate(*this); - } - - QString Certificate::publicKey() const - { - if (isNull()) { - return {}; - } - return sshKey().publicKey(); + return QString::fromStdString(key->fingerprint_public()); } Certificate Certificate::deserialize(QXmlStreamReader& reader) @@ -157,7 +114,16 @@ namespace KeeShareSettings if (reader.name() == "Signer") { certificate.signer = reader.readElementText(); } else if (reader.name() == "Key") { - certificate.key = QByteArray::fromBase64(reader.readElementText().toLatin1()); + auto rawKey = QByteArray::fromBase64(reader.readElementText().toLatin1()); + if (!rawKey.isEmpty()) { + try { + Botan::DataSource_Memory dataSource(reinterpret_cast(rawKey.constData()), + rawKey.size()); + certificate.key.reset(Botan::PKCS8::load_key(dataSource).release()); + } catch (std::exception& e) { + qWarning("KeeShare: Failed to deserialize key data: %s", e.what()); + } + } } } return certificate; @@ -165,7 +131,10 @@ namespace KeeShareSettings bool Key::operator==(const Key& other) const { - return key == other.key; + if (isNull() || other.isNull()) { + return isNull() == other.isNull(); + } + return key->private_key_bits() == other.key->private_key_bits(); } bool Key::operator!=(const Key& other) const @@ -175,20 +144,7 @@ namespace KeeShareSettings bool Key::isNull() const { - return key.isEmpty(); - } - - QString Key::privateKey() const - { - if (isNull()) { - return {}; - } - return sshKey().privateKey(); - } - - OpenSSHKey Key::sshKey() const - { - return unpackKey(*this); + return !key; } void Key::serialize(QXmlStreamWriter& writer, const Key& key) @@ -196,24 +152,37 @@ namespace KeeShareSettings if (key.isNull()) { return; } - writer.writeCharacters(key.key.toBase64()); + auto berKey = Botan::PKCS8::BER_encode(*key.key); + auto baKey = QByteArray::fromRawData(reinterpret_cast(berKey.data()), berKey.size()); + writer.writeCharacters(baKey.toBase64()); } Key Key::deserialize(QXmlStreamReader& reader) { Key key; - key.key = QByteArray::fromBase64(reader.readElementText().toLatin1()); + auto rawKey = QByteArray::fromBase64(reader.readElementText().toLatin1()); + if (!rawKey.isEmpty()) { + try { + Botan::DataSource_Memory dataSource(reinterpret_cast(rawKey.constData()), + rawKey.size()); + key.key.reset(Botan::PKCS8::load_key(dataSource).release()); + } catch (std::exception& e) { + qWarning("KeeShare: Failed to deserialize key data: %s", e.what()); + } + } return key; } Own Own::generate() { - OpenSSHKey key = OpenSSHKey::generate(false); - key.openKey(QString()); Own own; - own.key = packKey(key); - const QString name = qgetenv("USER"); // + "@" + QHostInfo::localHostName(); - own.certificate = packCertificate(key, name); + own.key.key.reset(new Botan::RSA_PrivateKey(*randomGen()->getRng(), 2048)); + auto name = qgetenv("USER"); + if (name.isEmpty()) { + name = qgetenv("USERNAME"); + } + own.certificate.signer = name; + own.certificate.key = own.key.key; return own; } @@ -249,7 +218,7 @@ namespace KeeShareSettings } } } else { - ::qWarning() << "Unknown KeeShareSettings element" << reader.name(); + qWarning("Unknown KeeShareSettings element %s", qPrintable(reader.name().toString())); reader.skipCurrentElement(); } } @@ -289,7 +258,7 @@ namespace KeeShareSettings } else if (reader.name() == "PublicKey") { own.certificate = Certificate::deserialize(reader); } else { - ::qWarning() << "Unknown KeeShareSettings element" << reader.name(); + qWarning("Unknown KeeShareSettings element %s", qPrintable(reader.name().toString())); reader.skipCurrentElement(); } } @@ -360,12 +329,12 @@ namespace KeeShareSettings if (reader.name() == "Certificate") { foreign.certificates << ScopedCertificate::deserialize(reader); } else { - ::qWarning() << "Unknown Cerificates element" << reader.name(); + qWarning("Unknown Certificates element %s", qPrintable(reader.name().toString())); reader.skipCurrentElement(); } } } else { - ::qWarning() << "Unknown KeeShareSettings element" << reader.name(); + qWarning("Unknown KeeShareSettings element %s", qPrintable(reader.name().toString())); reader.skipCurrentElement(); } } @@ -459,7 +428,7 @@ namespace KeeShareSettings } else if (reader.name() == "Password") { reference.password = QString::fromUtf8(QByteArray::fromBase64(reader.readElementText().toLatin1())); } else { - ::qWarning() << "Unknown Reference element" << reader.name(); + qWarning("Unknown Reference element %s", qPrintable(reader.name().toString())); reader.skipCurrentElement(); } } @@ -489,7 +458,7 @@ namespace KeeShareSettings } else if (reader.name() == "Certificate") { sign.certificate = KeeShareSettings::Certificate::deserialize(reader); } else { - ::qWarning() << "Unknown Sign element" << reader.name(); + qWarning("Unknown Sign element %s", qPrintable(reader.name().toString())); reader.skipCurrentElement(); } } diff --git a/src/keeshare/KeeShareSettings.h b/src/keeshare/KeeShareSettings.h index 4605d24aa..80500fdf2 100644 --- a/src/keeshare/KeeShareSettings.h +++ b/src/keeshare/KeeShareSettings.h @@ -20,8 +20,13 @@ #include #include +#include +#include -#include "crypto/ssh/OpenSSHKey.h" +namespace Botan +{ + class Private_Key; +} class CustomData; class QXmlStreamWriter; @@ -31,7 +36,7 @@ namespace KeeShareSettings { struct Certificate { - QByteArray key; + QSharedPointer key; QString signer; bool operator==(const Certificate& other) const; @@ -39,8 +44,6 @@ namespace KeeShareSettings bool isNull() const; QString fingerprint() const; - QString publicKey() const; - OpenSSHKey sshKey() const; static void serialize(QXmlStreamWriter& writer, const Certificate& certificate); static Certificate deserialize(QXmlStreamReader& reader); @@ -48,14 +51,13 @@ namespace KeeShareSettings struct Key { - QByteArray key; + QSharedPointer key; bool operator==(const Key& other) const; bool operator!=(const Key& other) const; bool isNull() const; QString privateKey() const; - OpenSSHKey sshKey() const; static void serialize(QXmlStreamWriter& writer, const Key& key); static Key deserialize(QXmlStreamReader& reader); diff --git a/src/keeshare/SettingsWidgetKeeShare.cpp b/src/keeshare/SettingsWidgetKeeShare.cpp index 424e78c4b..429655b5b 100644 --- a/src/keeshare/SettingsWidgetKeeShare.cpp +++ b/src/keeshare/SettingsWidgetKeeShare.cpp @@ -22,11 +22,13 @@ #include "core/Group.h" #include "core/Metadata.h" #include "gui/FileDialog.h" +#include "gui/MessageBox.h" #include "keeshare/KeeShare.h" #include "keeshare/KeeShareSettings.h" -#include #include +#include +#include SettingsWidgetKeeShare::SettingsWidgetKeeShare(QWidget* parent) : QWidget(parent) @@ -72,27 +74,32 @@ void SettingsWidgetKeeShare::updateForeignCertificates() { auto headers = QStringList() << tr("Path") << tr("Status"); #if defined(WITH_XC_KEESHARE_SECURE) - headers << tr("Signer") << tr("Fingerprint") << tr("Certificate"); + headers << tr("Signer") << tr("Fingerprint"); #endif m_importedCertificateModel.reset(new QStandardItemModel()); m_importedCertificateModel->setHorizontalHeaderLabels(headers); for (const auto& scopedCertificate : m_foreign.certificates) { - const auto items = QList() - << new QStandardItem(scopedCertificate.path) - << new QStandardItem(scopedCertificate.trust == KeeShareSettings::Trust::Ask - ? tr("Ask") - : (scopedCertificate.trust == KeeShareSettings::Trust::Trusted - ? tr("Trusted") - : tr("Untrusted"))) + QList items; + items << new QStandardItem(scopedCertificate.path); + + switch (scopedCertificate.trust) { + case KeeShareSettings::Trust::Ask: + items << new QStandardItem(tr("Ask")); + break; + case KeeShareSettings::Trust::Trusted: + items << new QStandardItem(tr("Trusted")); + break; + case KeeShareSettings::Trust::Untrusted: + items << new QStandardItem(tr("Untrusted")); + break; + } + #if defined(WITH_XC_KEESHARE_SECURE) - << new QStandardItem(scopedCertificate.isKnown() ? scopedCertificate.certificate.signer - : tr("Unknown")) - << new QStandardItem(scopedCertificate.certificate.fingerprint()) - << new QStandardItem(scopedCertificate.certificate.publicKey()) + items << new QStandardItem(scopedCertificate.isKnown() ? scopedCertificate.certificate.signer : tr("Unknown")); + items << new QStandardItem(scopedCertificate.certificate.fingerprint()); #endif - ; m_importedCertificateModel->appendRow(items); } @@ -103,8 +110,6 @@ void SettingsWidgetKeeShare::updateForeignCertificates() void SettingsWidgetKeeShare::updateOwnCertificate() { m_ui->ownCertificateSignerEdit->setText(m_own.certificate.signer); - m_ui->ownCertificatePublicKeyEdit->setText(m_own.certificate.publicKey()); - m_ui->ownCertificatePrivateKeyEdit->setText(m_own.key.privateKey()); m_ui->ownCertificateFingerprintEdit->setText(m_own.certificate.fingerprint()); } @@ -133,8 +138,6 @@ void SettingsWidgetKeeShare::generateCertificate() { m_own = KeeShareSettings::Own::generate(); m_ui->ownCertificateSignerEdit->setText(m_own.certificate.signer); - m_ui->ownCertificatePublicKeyEdit->setText(m_own.certificate.publicKey()); - m_ui->ownCertificatePrivateKeyEdit->setText(m_own.key.privateKey()); m_ui->ownCertificateFingerprintEdit->setText(m_own.certificate.fingerprint()); } @@ -165,16 +168,14 @@ void SettingsWidgetKeeShare::importCertificate() void SettingsWidgetKeeShare::exportCertificate() { if (KeeShare::own() != m_own) { - QMessageBox warning; - warning.setIcon(QMessageBox::Warning); - warning.setWindowTitle(tr("Exporting changed certificate")); - warning.setText(tr("The exported certificate is not the same as the one in use. Do you want to export the " - "current certificate?")); - auto yes = warning.addButton(QMessageBox::StandardButton::Yes); - auto no = warning.addButton(QMessageBox::StandardButton::No); - warning.setDefaultButton(no); - warning.exec(); - if (warning.clickedButton() != yes) { + auto ans = MessageBox::warning( + this, + tr("Exporting changed certificate"), + tr("The exported certificate is not the same as the one in use. Do you want to export the " + "current certificate?"), + MessageBox::Yes | MessageBox::No, + MessageBox::No); + if (ans != MessageBox::Yes) { return; } } @@ -186,8 +187,7 @@ void SettingsWidgetKeeShare::exportCertificate() const auto filetype = tr("key.share", "Filetype for KeeShare key"); const auto filters = QString("%1 (*." + filetype + ");;%2 (*)").arg(tr("KeeShare key file"), tr("All files")); QString filename = QString("%1.%2").arg(m_own.certificate.signer).arg(filetype); - filename = fileDialog()->getSaveFileName( - this, tr("Select path"), defaultDirPath, filters, nullptr, QFileDialog::Options(0)); + filename = fileDialog()->getSaveFileName(this, tr("Select path"), defaultDirPath, filters); if (filename.isEmpty()) { return; } diff --git a/src/keeshare/SettingsWidgetKeeShare.ui b/src/keeshare/SettingsWidgetKeeShare.ui index 44fb9f1da..bd0b98a85 100644 --- a/src/keeshare/SettingsWidgetKeeShare.ui +++ b/src/keeshare/SettingsWidgetKeeShare.ui @@ -79,87 +79,10 @@ Own certificate - + 10 - - - - Fingerprint: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Key - - - true - - - - - - - Certificate: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Signer: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Key: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Certificate - - - true - - - - - - - Signer name field - - - - - - - Fingerprint - - - true - - - @@ -207,6 +130,43 @@ + + + + Fingerprint + + + true + + + + + + + Signer name field + + + + + + + Signer: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Fingerprint: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + @@ -333,8 +293,6 @@ importOwnCertificateButton exportOwnCertificateButton ownCertificateSignerEdit - ownCertificatePrivateKeyEdit - ownCertificatePublicKeyEdit ownCertificateFingerprintEdit trustImportedCertificateButton askImportedCertificateButton diff --git a/src/keeshare/ShareExport.cpp b/src/keeshare/ShareExport.cpp index aedbc04af..fc34dff9f 100644 --- a/src/keeshare/ShareExport.cpp +++ b/src/keeshare/ShareExport.cpp @@ -23,6 +23,9 @@ #include "keeshare/Signature.h" #include "keys/PasswordKey.h" +#include +#include + #if defined(WITH_XC_KEESHARE_SECURE) #include #include @@ -147,9 +150,8 @@ namespace } QTextStream stream(&file); KeeShareSettings::Sign sign; - auto sshKey = own.key.sshKey(); - sshKey.openKey(QString()); - sign.signature = Signature::create(bytes, sshKey); + // TODO: check for false return + Signature::create(bytes, own.key.key, sign.signature); sign.certificate = own.certificate; stream << KeeShareSettings::Sign::serialize(sign); stream.flush(); diff --git a/src/keeshare/ShareExport.h b/src/keeshare/ShareExport.h index ff2abdc81..db54b2258 100644 --- a/src/keeshare/ShareExport.h +++ b/src/keeshare/ShareExport.h @@ -17,6 +17,8 @@ #ifndef KEEPASSXC_SHAREEXPORT_H #define KEEPASSXC_SHAREEXPORT_H +#include + #include "keeshare/KeeShareSettings.h" #include "keeshare/ShareObserver.h" diff --git a/src/keeshare/ShareImport.cpp b/src/keeshare/ShareImport.cpp index 38a477aaf..88ef8374c 100644 --- a/src/keeshare/ShareImport.cpp +++ b/src/keeshare/ShareImport.cpp @@ -22,8 +22,10 @@ #include "keeshare/Signature.h" #include "keys/PasswordKey.h" +#include #include #include +#include #if defined(WITH_XC_KEESHARE_SECURE) #include @@ -52,55 +54,43 @@ namespace KeeShareSettings::Certificate certificate; if (!sign.signature.isEmpty()) { certificate = sign.certificate; - auto key = sign.certificate.sshKey(); - key.openKey(QString()); - const auto signer = Signature(); - if (!signer.verify(data, sign.signature, key)) { + if (!Signature::verify(data, sign.certificate.key, sign.signature)) { qCritical("Invalid signature for shared container %s.", qPrintable(reference.path)); return {Invalid, KeeShareSettings::Certificate()}; } - if (ownCertificate.key == sign.certificate.key) { + // Automatically trust your own certificate + if (ownCertificate == sign.certificate) { return {Own, ownCertificate}; } } - enum Scope - { - Invalid, - Global, - Local - }; - Scope scope = Invalid; - KeeShareSettings::Trust trusted = KeeShareSettings::Trust::Ask; + for (const auto& scopedCertificate : knownCertificates) { - if (scopedCertificate.certificate.key == certificate.key && scopedCertificate.path == reference.path) { - // Global scope is overwritten by local scope - scope = Global; - trusted = scopedCertificate.trust; - } - if (scopedCertificate.certificate.key == certificate.key && scopedCertificate.path == reference.path) { - scope = Local; - trusted = scopedCertificate.trust; + if (scopedCertificate.certificate == certificate && scopedCertificate.path == reference.path) { + if (scopedCertificate.trust == KeeShareSettings::Trust::Trusted) { + return {TrustedForever, certificate}; + } else if (scopedCertificate.trust == KeeShareSettings::Trust::Untrusted) { + return {UntrustedForever, certificate}; + } + // Default to ask break; } } - if (scope != Invalid && trusted != KeeShareSettings::Trust::Ask) { - // we introduce now scopes if there is a global - return {trusted == KeeShareSettings::Trust::Trusted ? TrustedForever : UntrustedForever, certificate}; - } + // Ask the user if they want to trust the certificate QMessageBox warning; + warning.setWindowTitle(ShareImport::tr("KeeShare Import")); if (sign.signature.isEmpty()) { warning.setIcon(QMessageBox::Warning); - warning.setWindowTitle(ShareImport::tr("Import from container without signature")); - warning.setText(ShareImport::tr("We cannot verify the source of the shared container because it is not " + warning.setText(ShareImport::tr("The source of the shared container cannot be verified because it is not " "signed. Do you really want to import from %1?") .arg(reference.path)); } else { warning.setIcon(QMessageBox::Question); - warning.setWindowTitle(ShareImport::tr("Import from container with certificate")); - warning.setText(ShareImport::tr("Do you want to trust %1 with the fingerprint of %2 from %3?") - .arg(certificate.signer, certificate.fingerprint(), reference.path)); + warning.setText(ShareImport::tr("Do you want to trust %1 with certificate fingerprint:\n%2\n%3") + .arg(reference.path) + .arg(certificate.signer) + .arg(certificate.fingerprint())); } auto untrustedOnce = warning.addButton(ShareImport::tr("Not this time"), QMessageBox::ButtonRole::NoRole); auto untrustedForever = warning.addButton(ShareImport::tr("Never"), QMessageBox::ButtonRole::NoRole); @@ -188,7 +178,7 @@ namespace const auto trusted = trust.first == TrustedForever ? KeeShareSettings::Trust::Trusted : KeeShareSettings::Trust::Untrusted; for (KeeShareSettings::ScopedCertificate& scopedCertificate : foreign.certificates) { - if (scopedCertificate.certificate.key == trust.second.key && scopedCertificate.path == reference.path) { + if (scopedCertificate.certificate == trust.second && scopedCertificate.path == reference.path) { scopedCertificate.certificate.signer = trust.second.signer; scopedCertificate.path = reference.path; scopedCertificate.trust = trusted; @@ -279,7 +269,7 @@ namespace const auto trusted = trust.first == TrustedForever ? KeeShareSettings::Trust::Trusted : KeeShareSettings::Trust::Untrusted; for (KeeShareSettings::ScopedCertificate& scopedCertificate : foreign.certificates) { - if (scopedCertificate.certificate.key == trust.second.key && scopedCertificate.path == reference.path) { + if (scopedCertificate.certificate == trust.second && scopedCertificate.path == reference.path) { scopedCertificate.certificate.signer = trust.second.signer; scopedCertificate.path = reference.path; scopedCertificate.trust = trusted; diff --git a/src/keeshare/ShareImport.h b/src/keeshare/ShareImport.h index 96cb67c69..739e2979e 100644 --- a/src/keeshare/ShareImport.h +++ b/src/keeshare/ShareImport.h @@ -17,6 +17,8 @@ #ifndef KEEPASSXC_SHAREIMPORT_H #define KEEPASSXC_SHAREIMPORT_H +#include + #include "keeshare/ShareObserver.h" class ShareImport diff --git a/src/keeshare/ShareObserver.cpp b/src/keeshare/ShareObserver.cpp index f74e800a0..5d5bc7e22 100644 --- a/src/keeshare/ShareObserver.cpp +++ b/src/keeshare/ShareObserver.cpp @@ -25,6 +25,8 @@ #include "keeshare/ShareExport.h" #include "keeshare/ShareImport.h" +#include + namespace { QString resolvePath(const QString& path, QSharedPointer database) diff --git a/src/keeshare/Signature.cpp b/src/keeshare/Signature.cpp index fdc0481fb..8143d943f 100644 --- a/src/keeshare/Signature.cpp +++ b/src/keeshare/Signature.cpp @@ -16,245 +16,53 @@ */ #include "Signature.h" -#include "core/Tools.h" -#include "crypto/Crypto.h" -#include "crypto/CryptoHash.h" -#include "crypto/ssh/OpenSSHKey.h" -#include -#include +#include "crypto/Random.h" -struct RSASigner -{ - gcry_error_t rc; - QString error; - - void raiseError(const QString& message = QString()) - { - if (message.isEmpty()) { - error = QString("%1/%2").arg(QString::fromLocal8Bit(gcry_strsource(rc)), - QString::fromLocal8Bit(gcry_strerror(rc))); - } else { - error = message; - } - } - - RSASigner() - : rc(GPG_ERR_NO_ERROR) - { - } - - QString sign(const QByteArray& data, const OpenSSHKey& key) - { - enum Index - { - N, - E, - D, - P, - Q, - U, // private key - R, - S, // signature - - Data, - Key, - Sig - }; - - const QList parts = key.privateParts(); - if (parts.count() != 6) { - raiseError("Unsupported signing key"); - return QString(); - } - - const QByteArray block = CryptoHash::hash(data, CryptoHash::Sha256); - - Tools::Map mpi; - Tools::Map sexp; - const gcry_mpi_format format = GCRYMPI_FMT_USG; - rc = gcry_mpi_scan(&mpi[N], format, parts[0].data(), parts[0].size(), nullptr); - if (rc != GPG_ERR_NO_ERROR) { - raiseError(); - return QString(); - } - rc = gcry_mpi_scan(&mpi[E], format, parts[1].data(), parts[1].size(), nullptr); - if (rc != GPG_ERR_NO_ERROR) { - raiseError(); - return QString(); - } - rc = gcry_mpi_scan(&mpi[D], format, parts[2].data(), parts[2].size(), nullptr); - if (rc != GPG_ERR_NO_ERROR) { - raiseError(); - return QString(); - } - rc = gcry_mpi_scan(&mpi[U], format, parts[3].data(), parts[3].size(), nullptr); - if (rc != GPG_ERR_NO_ERROR) { - raiseError(); - return QString(); - } - rc = gcry_mpi_scan(&mpi[P], format, parts[4].data(), parts[4].size(), nullptr); - if (rc != GPG_ERR_NO_ERROR) { - raiseError(); - return QString(); - } - rc = gcry_mpi_scan(&mpi[Q], format, parts[5].data(), parts[5].size(), nullptr); - if (rc != GPG_ERR_NO_ERROR) { - raiseError(); - return QString(); - } - if (gcry_mpi_cmp(mpi[P], mpi[Q]) > 0) { - // see https://www.gnupg.org/documentation/manuals/gcrypt/RSA-key-parameters.html#RSA-key-parameters - gcry_mpi_swap(mpi[P], mpi[Q]); - gcry_mpi_invm(mpi[U], mpi[P], mpi[Q]); - } - rc = gcry_sexp_build(&sexp[Key], - NULL, - "(private-key (rsa (n %m) (e %m) (d %m) (p %m) (q %m) (u %m)))", - mpi[N], - mpi[E], - mpi[D], - mpi[P], - mpi[Q], - mpi[U]); - if (rc != GPG_ERR_NO_ERROR) { - raiseError(); - return QString(); - } - - rc = gcry_pk_testkey(sexp[Key]); - if (rc != GPG_ERR_NO_ERROR) { - raiseError(); - return QString(); - } - - rc = gcry_sexp_build(&sexp[Data], NULL, "(data (flags pkcs1) (hash sha256 %b))", block.size(), block.data()); - // rc = gcry_sexp_build(&sexp[Data], NULL, "(data (flags raw) (value %b))", data.size(), data.data()); - if (rc != GPG_ERR_NO_ERROR) { - raiseError(); - return QString(); - } - rc = gcry_pk_sign(&sexp[Sig], sexp[Data], sexp[Key]); - if (rc != GPG_ERR_NO_ERROR) { - raiseError(); - return QString(); - } - sexp[S] = gcry_sexp_find_token(sexp[Sig], "s", 1); - mpi[S] = gcry_sexp_nth_mpi(sexp[S], 1, GCRYMPI_FMT_USG); - Tools::Buffer buffer; - rc = gcry_mpi_aprint(GCRYMPI_FMT_STD, &buffer.raw, &buffer.size, mpi[S]); - if (rc != GPG_ERR_NO_ERROR) { - raiseError(); - return QString(); - } - return QString("rsa|%1").arg(QString::fromLatin1(buffer.content().toHex())); - } - - bool verify(const QByteArray& data, const OpenSSHKey& key, const QString& signature) - { - const gcry_mpi_format format = GCRYMPI_FMT_USG; - enum MPI - { - N, - E, // public key - R, - S // signature - }; - enum SEXP - { - Data, - Key, - Sig - }; - - const QList parts = key.publicParts(); - if (parts.count() != 2) { - raiseError("Unsupported verification key"); - return false; - } - - const QByteArray block = CryptoHash::hash(data, CryptoHash::Sha256); - - Tools::Map mpi; - Tools::Map sexp; - - rc = gcry_mpi_scan(&mpi[E], format, parts[0].data(), parts[0].size(), nullptr); - if (rc != GPG_ERR_NO_ERROR) { - raiseError(); - return false; - } - rc = gcry_mpi_scan(&mpi[N], format, parts[1].data(), parts[1].size(), nullptr); - if (rc != GPG_ERR_NO_ERROR) { - raiseError(); - return false; - } - rc = gcry_sexp_build(&sexp[Key], NULL, "(public-key (rsa (n %m) (e %m)))", mpi[N], mpi[E]); - if (rc != GPG_ERR_NO_ERROR) { - raiseError(); - return false; - } - - QRegExp extractor("rsa\\|([a-f0-9]+)", Qt::CaseInsensitive); - if (!extractor.exactMatch(signature) || extractor.captureCount() != 1) { - raiseError("Could not unpack signature parts"); - return false; - } - const QByteArray sig_s = QByteArray::fromHex(extractor.cap(1).toLatin1()); - - rc = gcry_mpi_scan(&mpi[S], GCRYMPI_FMT_STD, sig_s.data(), sig_s.size(), nullptr); - if (rc != GPG_ERR_NO_ERROR) { - raiseError(); - return false; - } - rc = gcry_sexp_build(&sexp[Sig], NULL, "(sig-val (rsa (s %m)))", mpi[S]); - if (rc != GPG_ERR_NO_ERROR) { - raiseError(); - return false; - } - rc = gcry_sexp_build(&sexp[Data], NULL, "(data (flags pkcs1) (hash sha256 %b))", block.size(), block.data()); - // rc = gcry_sexp_build(&sexp[Data], NULL, "(data (flags raw) (value %b))", data.size(), data.data()); - if (rc != GPG_ERR_NO_ERROR) { - raiseError(); - return false; - } - rc = gcry_pk_verify(sexp[Sig], sexp[Data], sexp[Key]); - if (rc != GPG_ERR_NO_ERROR && rc != GPG_ERR_BAD_SIGNATURE) { - raiseError(); - return false; - } - return rc != GPG_ERR_BAD_SIGNATURE; - } -}; - -QString Signature::create(const QByteArray& data, const OpenSSHKey& key) +bool Signature::create(const QByteArray& data, QSharedPointer key, QString& signature) { // TODO HNH: currently we publish the signature in our own non-standard format - it would // be better to use a standard format (like ASN1 - but this would be more easy // when we integrate a proper library) // Even more, we could publish standard self signed certificates with the container // instead of the custom certificates - if (key.type() == "ssh-rsa") { - RSASigner signer; - QString result = signer.sign(data, key); - if (signer.rc != GPG_ERR_NO_ERROR) { - ::qWarning() << signer.error; - } - return result; - } - ::qWarning() << "Unsupported Public/Private key format"; - return QString(); -} + if (key->algo_name() == "RSA") { + try { + Botan::PK_Signer signer(*key, "EMSA3(SHA-256)"); + signer.update(reinterpret_cast(data.constData()), data.size()); + auto s = signer.signature(*randomGen()->getRng()); -bool Signature::verify(const QByteArray& data, const QString& signature, const OpenSSHKey& key) -{ - if (key.type() == "ssh-rsa") { - RSASigner signer; - bool result = signer.verify(data, key, signature); - if (signer.rc != GPG_ERR_NO_ERROR) { - ::qWarning() << signer.error; + auto hex = QByteArray(reinterpret_cast(s.data()), s.size()).toHex(); + signature = QString("rsa|%1").arg(QString::fromLatin1(hex)); + return true; + } catch (std::exception& e) { + qWarning("KeeShare: Failed to sign data: %s", e.what()); + return false; } - return result; } - ::qWarning() << "Unsupported Public/Private key format"; + qWarning("Unsupported Public/Private key format"); + return false; +} + +bool Signature::verify(const QByteArray& data, QSharedPointer key, const QString& signature) +{ + if (key && key->algo_name() == "RSA") { + QRegExp extractor("rsa\\|([a-f0-9]+)", Qt::CaseInsensitive); + if (!extractor.exactMatch(signature) || extractor.captureCount() != 1) { + qWarning("Could not unpack signature parts"); + return false; + } + const QByteArray sig_s = QByteArray::fromHex(extractor.cap(1).toLatin1()); + + try { + Botan::PK_Verifier verifier(*key, "EMSA3(SHA-256)"); + verifier.update(reinterpret_cast(data.constData()), data.size()); + return verifier.check_signature(reinterpret_cast(sig_s.constData()), sig_s.size()); + } catch (std::exception& e) { + qWarning("KeeShare: Failed to verify signature: %s", e.what()); + return false; + } + } + qWarning("Unsupported Public/Private key format"); return false; } diff --git a/src/keeshare/Signature.h b/src/keeshare/Signature.h index 59c32339f..9c4f83bba 100644 --- a/src/keeshare/Signature.h +++ b/src/keeshare/Signature.h @@ -18,17 +18,14 @@ #ifndef KEEPASSXC_SIGNATURE_H #define KEEPASSXC_SIGNATURE_H +#include #include -#include +#include -class QByteArray; -class OpenSSHKey; - -class Signature +namespace Signature { -public: - static QString create(const QByteArray& data, const OpenSSHKey& key); - static bool verify(const QByteArray& data, const QString& signature, const OpenSSHKey& key); -}; + bool create(const QByteArray& data, QSharedPointer key, QString& signature); + bool verify(const QByteArray& data, QSharedPointer key, const QString& signature); +}; // namespace Signature #endif // KEEPASSXC_SIGNATURE_H diff --git a/src/keeshare/group/EditGroupWidgetKeeShare.cpp b/src/keeshare/group/EditGroupWidgetKeeShare.cpp index a3b71220f..4e2e6eeb2 100644 --- a/src/keeshare/group/EditGroupWidgetKeeShare.cpp +++ b/src/keeshare/group/EditGroupWidgetKeeShare.cpp @@ -23,7 +23,6 @@ #include "core/Group.h" #include "core/Metadata.h" #include "core/Resources.h" -#include "crypto/ssh/OpenSSHKey.h" #include "gui/FileDialog.h" #include "keeshare/KeeShare.h" diff --git a/src/keys/ChallengeResponseKey.h b/src/keys/ChallengeResponseKey.h index 263b507e0..d772ce7d4 100644 --- a/src/keys/ChallengeResponseKey.h +++ b/src/keys/ChallengeResponseKey.h @@ -21,6 +21,7 @@ #include #include +#include class ChallengeResponseKey { @@ -31,9 +32,13 @@ public: } virtual ~ChallengeResponseKey() = default; - virtual QByteArray rawKey() const = 0; virtual bool challenge(const QByteArray& challenge) = 0; - virtual QUuid uuid() const + + Botan::secure_vector& rawKey() + { + return m_key; + } + QUuid uuid() const { return m_uuid; } @@ -44,6 +49,7 @@ public: protected: QString m_error; + Botan::secure_vector m_key; private: Q_DISABLE_COPY(ChallengeResponseKey); diff --git a/src/keys/CompositeKey.cpp b/src/keys/CompositeKey.cpp index 23c830f9f..10aef6c9f 100644 --- a/src/keys/CompositeKey.cpp +++ b/src/keys/CompositeKey.cpp @@ -143,7 +143,7 @@ bool CompositeKey::challenge(const QByteArray& seed, QByteArray& result, QString qWarning() << "Failed to issue challenge: " << key->error(); return false; } - cryptoHash.addData(key->rawKey()); + cryptoHash.addData(key->rawKey().data()); } result = cryptoHash.result(); diff --git a/src/keys/FileKey.cpp b/src/keys/FileKey.cpp index 2ac52ae69..a9ee27626 100644 --- a/src/keys/FileKey.cpp +++ b/src/keys/FileKey.cpp @@ -24,29 +24,16 @@ #include -#include -#include -#include -#include - QUuid FileKey::UUID("a584cbc4-c9b4-437e-81bb-362ca9709273"); constexpr int FileKey::SHA256_SIZE; FileKey::FileKey() : Key(UUID) - , m_key(static_cast(gcry_malloc_secure(SHA256_SIZE))) + , m_key(SHA256_SIZE) { } -FileKey::~FileKey() -{ - if (m_key) { - gcry_free(m_key); - m_key = nullptr; - } -} - /** * Read key file from device while trying to detect its file format. * @@ -169,10 +156,7 @@ bool FileKey::load(const QString& fileName, QString* errorMsg) */ QByteArray FileKey::rawKey() const { - if (!m_key) { - return {}; - } - return QByteArray::fromRawData(m_key, SHA256_SIZE); + return QByteArray(m_key.data(), m_key.size()); } /** @@ -225,7 +209,7 @@ void FileKey::createXMLv2(QIODevice* device, int size) } w.writeCharacters(QChar(key[i])); } - sodium_memzero(key.data(), static_cast(key.capacity())); + Botan::secure_scrub_memory(key.data(), static_cast(key.capacity())); w.writeCharacters("\n "); w.writeEndElement(); @@ -315,12 +299,12 @@ bool FileKey::loadXml(QIODevice* device, QString* errorMsg) while (!xmlReader.error() && xmlReader.readNextStartElement()) { if (xmlReader.name() == "Data") { keyFileData.hash = QByteArray::fromHex(xmlReader.attributes().value("Hash").toLatin1()); - QByteArray rawData = xmlReader.readElementText().simplified().replace(" ", "").toLatin1(); + keyFileData.data = xmlReader.readElementText().simplified().replace(" ", "").toLatin1(); - if (keyFileData.version.startsWith("1.0") && Tools::isBase64(rawData)) { - keyFileData.data = QByteArray::fromBase64(rawData); - } else if (keyFileData.version == "2.0" && Tools::isHex(rawData)) { - keyFileData.data = QByteArray::fromHex(rawData); + if (keyFileData.version.startsWith("1.0") && Tools::isBase64(keyFileData.data)) { + keyFileData.data = QByteArray::fromBase64(keyFileData.data); + } else if (keyFileData.version == "2.0" && Tools::isHex(keyFileData.data)) { + keyFileData.data = QByteArray::fromHex(keyFileData.data); CryptoHash hash(CryptoHash::Sha256); hash.addData(keyFileData.data); @@ -337,8 +321,6 @@ bool FileKey::loadXml(QIODevice* device, QString* errorMsg) } return false; } - - sodium_memzero(rawData.data(), static_cast(rawData.capacity())); } } } @@ -346,11 +328,11 @@ bool FileKey::loadXml(QIODevice* device, QString* errorMsg) bool ok = false; if (!xmlReader.error() && !keyFileData.data.isEmpty()) { - std::memcpy(m_key, keyFileData.data.data(), std::min(SHA256_SIZE, keyFileData.data.size())); + std::memcpy(m_key.data(), keyFileData.data.data(), std::min(SHA256_SIZE, keyFileData.data.size())); ok = true; } - sodium_memzero(keyFileData.data.data(), static_cast(keyFileData.data.capacity())); + Botan::secure_scrub_memory(keyFileData.data.data(), static_cast(keyFileData.data.capacity())); return ok; } @@ -368,13 +350,12 @@ bool FileKey::loadBinary(QIODevice* device) return false; } - QByteArray data; - if (!Tools::readAllFromDevice(device, data) || data.size() != 32) { + Botan::secure_vector data(32); + if (device->read(data.data(), 32) != 32 || !device->atEnd()) { return false; } - std::memcpy(m_key, data.data(), std::min(SHA256_SIZE, data.size())); - sodium_memzero(data.data(), static_cast(data.capacity())); + m_key = data; m_type = FixedBinary; return true; } @@ -401,15 +382,13 @@ bool FileKey::loadHex(QIODevice* device) return false; } - QByteArray key = QByteArray::fromHex(data); - sodium_memzero(data.data(), static_cast(data.capacity())); - - if (key.size() != 32) { + data = QByteArray::fromHex(data); + if (data.size() != 32) { return false; } - std::memcpy(m_key, key.data(), std::min(SHA256_SIZE, key.size())); - sodium_memzero(key.data(), static_cast(key.capacity())); + std::memcpy(m_key.data(), data.data(), std::min(SHA256_SIZE, data.size())); + Botan::secure_scrub_memory(data.data(), static_cast(data.capacity())); m_type = FixedBinaryHex; return true; @@ -433,9 +412,9 @@ bool FileKey::loadHashed(QIODevice* device) cryptoHash.addData(buffer); } while (!buffer.isEmpty()); - auto result = cryptoHash.result(); - std::memcpy(m_key, result.data(), std::min(SHA256_SIZE, result.size())); - sodium_memzero(result.data(), static_cast(result.capacity())); + buffer = cryptoHash.result(); + std::memcpy(m_key.data(), buffer.data(), std::min(SHA256_SIZE, buffer.size())); + Botan::secure_scrub_memory(buffer.data(), static_cast(buffer.capacity())); m_type = Hashed; return true; diff --git a/src/keys/FileKey.h b/src/keys/FileKey.h index 540dde7d2..2e104ba5a 100644 --- a/src/keys/FileKey.h +++ b/src/keys/FileKey.h @@ -20,6 +20,7 @@ #define KEEPASSX_FILEKEY_H #include +#include #include "keys/Key.h" @@ -41,7 +42,7 @@ public: }; FileKey(); - ~FileKey() override; + ~FileKey() override = default; bool load(QIODevice* device, QString* errorMsg = nullptr); bool load(const QString& fileName, QString* errorMsg = nullptr); QByteArray rawKey() const override; @@ -58,7 +59,7 @@ private: bool loadHex(QIODevice* device); bool loadHashed(QIODevice* device); - char* m_key = nullptr; + Botan::secure_vector m_key; Type m_type = None; }; diff --git a/src/keys/PasswordKey.cpp b/src/keys/PasswordKey.cpp index 4393a1780..ae60786ef 100644 --- a/src/keys/PasswordKey.cpp +++ b/src/keys/PasswordKey.cpp @@ -19,9 +19,9 @@ #include "core/Tools.h" #include "crypto/CryptoHash.h" + #include #include -#include QUuid PasswordKey::UUID("77e90411-303a-43f2-b773-853b05635ead"); @@ -29,31 +29,23 @@ constexpr int PasswordKey::SHA256_SIZE; PasswordKey::PasswordKey() : Key(UUID) - , m_key(static_cast(gcry_malloc_secure(SHA256_SIZE))) + , m_key(SHA256_SIZE) { } PasswordKey::PasswordKey(const QString& password) : Key(UUID) - , m_key(static_cast(gcry_malloc_secure(SHA256_SIZE))) + , m_key(SHA256_SIZE) { setPassword(password); } -PasswordKey::~PasswordKey() -{ - if (m_key) { - gcry_free(m_key); - m_key = nullptr; - } -} - QByteArray PasswordKey::rawKey() const { if (!m_isInitialized) { return {}; } - return QByteArray::fromRawData(m_key, SHA256_SIZE); + return QByteArray(m_key.data(), m_key.size()); } void PasswordKey::setPassword(const QString& password) @@ -64,7 +56,7 @@ void PasswordKey::setPassword(const QString& password) void PasswordKey::setHash(const QByteArray& hash) { Q_ASSERT(hash.size() == SHA256_SIZE); - std::memcpy(m_key, hash.data(), std::min(SHA256_SIZE, hash.size())); + std::memcpy(m_key.data(), hash.data(), std::min(SHA256_SIZE, hash.size())); m_isInitialized = true; } diff --git a/src/keys/PasswordKey.h b/src/keys/PasswordKey.h index b84506673..e8542d88b 100644 --- a/src/keys/PasswordKey.h +++ b/src/keys/PasswordKey.h @@ -18,6 +18,8 @@ #ifndef KEEPASSX_PASSWORDKEY_H #define KEEPASSX_PASSWORDKEY_H +#include + #include #include @@ -30,7 +32,7 @@ public: PasswordKey(); explicit PasswordKey(const QString& password); - ~PasswordKey() override; + ~PasswordKey() override = default; QByteArray rawKey() const override; void setPassword(const QString& password); void setHash(const QByteArray& hash); @@ -40,7 +42,7 @@ public: private: static constexpr int SHA256_SIZE = 32; - char* m_key = nullptr; + Botan::secure_vector m_key; bool m_isInitialized = false; }; diff --git a/src/keys/YkChallengeResponseKey.cpp b/src/keys/YkChallengeResponseKey.cpp index 4bf2ab196..714f6b68f 100644 --- a/src/keys/YkChallengeResponseKey.cpp +++ b/src/keys/YkChallengeResponseKey.cpp @@ -31,10 +31,6 @@ #include #include -#include -#include -#include - QUuid YkChallengeResponseKey::UUID("e092495c-e77d-498b-84a1-05ae0d955508"); YkChallengeResponseKey::YkChallengeResponseKey(YubiKeySlot keySlot) @@ -43,37 +39,15 @@ YkChallengeResponseKey::YkChallengeResponseKey(YubiKeySlot keySlot) { } -YkChallengeResponseKey::~YkChallengeResponseKey() -{ - if (m_key) { - gcry_free(m_key); - m_keySize = 0; - m_key = nullptr; - } -} - -QByteArray YkChallengeResponseKey::rawKey() const -{ - return QByteArray::fromRawData(m_key, static_cast(m_keySize)); -} - bool YkChallengeResponseKey::challenge(const QByteArray& challenge) { m_error.clear(); - QByteArray key; auto result = - AsyncTask::runAndWaitForFuture([&] { return YubiKey::instance()->challenge(m_keySlot, challenge, key); }); + AsyncTask::runAndWaitForFuture([&] { return YubiKey::instance()->challenge(m_keySlot, challenge, m_key); }); - if (result == YubiKey::SUCCESS) { - if (m_key) { - gcry_free(m_key); - } - m_keySize = static_cast(key.size()); - m_key = static_cast(gcry_malloc_secure(m_keySize)); - std::memcpy(m_key, key.data(), m_keySize); - sodium_memzero(key.data(), static_cast(key.capacity())); - } else { + if (result != YubiKey::SUCCESS) { // Record the error message + m_key.clear(); m_error = YubiKey::instance()->errorMessage(); } diff --git a/src/keys/YkChallengeResponseKey.h b/src/keys/YkChallengeResponseKey.h index ba213f489..5ea38603c 100644 --- a/src/keys/YkChallengeResponseKey.h +++ b/src/keys/YkChallengeResponseKey.h @@ -22,24 +22,17 @@ #include "keys/ChallengeResponseKey.h" #include "keys/drivers/YubiKey.h" -#include - -class YkChallengeResponseKey : public QObject, public ChallengeResponseKey +class YkChallengeResponseKey : public ChallengeResponseKey { - Q_OBJECT - public: static QUuid UUID; explicit YkChallengeResponseKey(YubiKeySlot keySlot = {}); - ~YkChallengeResponseKey() override; + ~YkChallengeResponseKey() override = default; - QByteArray rawKey() const override; bool challenge(const QByteArray& challenge) override; private: - char* m_key = nullptr; - std::size_t m_keySize = 0; YubiKeySlot m_keySlot; }; diff --git a/src/keys/YkChallengeResponseKeyCLI.cpp b/src/keys/YkChallengeResponseKeyCLI.cpp deleted file mode 100644 index 4c78e4d0a..000000000 --- a/src/keys/YkChallengeResponseKeyCLI.cpp +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright (C) 2019 KeePassXC Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "keys/YkChallengeResponseKeyCLI.h" -#include "keys/drivers/YubiKey.h" - -#include "core/Tools.h" -#include "crypto/CryptoHash.h" -#include "crypto/Random.h" - -#include - -QUuid YkChallengeResponseKeyCLI::UUID("e2be77c0-c810-417a-8437-32f41d00bd1d"); - -YkChallengeResponseKeyCLI::YkChallengeResponseKeyCLI(YubiKeySlot keySlot, QString interactionMessage, QTextStream& out) - : ChallengeResponseKey(UUID) - , m_keySlot(keySlot) - , m_interactionMessage(interactionMessage) - , m_out(out.device()) -{ - connect(YubiKey::instance(), SIGNAL(userInteractionRequest()), SLOT(showInteractionMessage())); -} - -void YkChallengeResponseKeyCLI::showInteractionMessage() -{ - m_out << m_interactionMessage << "\n\n" << flush; -} - -QByteArray YkChallengeResponseKeyCLI::rawKey() const -{ - return m_key; -} - -bool YkChallengeResponseKeyCLI::challenge(const QByteArray& challenge) -{ - auto result = YubiKey::instance()->challenge(m_keySlot, challenge, m_key); - return result == YubiKey::SUCCESS; -} diff --git a/src/keys/YkChallengeResponseKeyCLI.h b/src/keys/YkChallengeResponseKeyCLI.h deleted file mode 100644 index 56025e7e1..000000000 --- a/src/keys/YkChallengeResponseKeyCLI.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2019 KeePassXC Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef KEEPASSX_YK_CHALLENGERESPONSEKEYCLI_H -#define KEEPASSX_YK_CHALLENGERESPONSEKEYCLI_H - -#include "core/Global.h" -#include "keys/ChallengeResponseKey.h" -#include "keys/drivers/YubiKey.h" - -#include -#include -#include - -class YkChallengeResponseKeyCLI : public QObject, public ChallengeResponseKey -{ - Q_OBJECT - -public: - static QUuid UUID; - - explicit YkChallengeResponseKeyCLI(YubiKeySlot keySlot, QString interactionMessage, QTextStream& out); - - QByteArray rawKey() const override; - bool challenge(const QByteArray& challenge) override; - -private slots: - void showInteractionMessage(); - -private: - QByteArray m_key; - YubiKeySlot m_keySlot; - QString m_interactionMessage; - QTextStream m_out; -}; - -#endif // KEEPASSX_YK_CHALLENGERESPONSEKEYCLI_H diff --git a/src/keys/drivers/YubiKey.cpp b/src/keys/drivers/YubiKey.cpp index 07b287c86..b3911ff8b 100644 --- a/src/keys/drivers/YubiKey.cpp +++ b/src/keys/drivers/YubiKey.cpp @@ -265,7 +265,7 @@ bool YubiKey::testChallenge(YubiKeySlot slot, bool* wouldBlock) bool YubiKey::performTestChallenge(void* key, int slot, bool* wouldBlock) { auto chall = randomGen()->randomArray(1); - QByteArray resp; + Botan::secure_vector resp; auto ret = performChallenge(static_cast(key), slot, false, chall, resp); if (ret == SUCCESS || ret == WOULDBLOCK) { if (wouldBlock) { @@ -285,7 +285,8 @@ bool YubiKey::performTestChallenge(void* key, int slot, bool* wouldBlock) * @param response response output from YubiKey * @return challenge result */ -YubiKey::ChallengeResult YubiKey::challenge(YubiKeySlot slot, const QByteArray& challenge, QByteArray& response) +YubiKey::ChallengeResult +YubiKey::challenge(YubiKeySlot slot, const QByteArray& challenge, Botan::secure_vector& response) { m_error.clear(); if (!m_initialized) { @@ -318,8 +319,11 @@ YubiKey::ChallengeResult YubiKey::challenge(YubiKeySlot slot, const QByteArray& return ret; } -YubiKey::ChallengeResult -YubiKey::performChallenge(void* key, int slot, bool mayBlock, const QByteArray& challenge, QByteArray& response) +YubiKey::ChallengeResult YubiKey::performChallenge(void* key, + int slot, + bool mayBlock, + const QByteArray& challenge, + Botan::secure_vector& response) { m_error.clear(); int yk_cmd = (slot == 1) ? SLOT_CHAL_HMAC1 : SLOT_CHAL_HMAC2; diff --git a/src/keys/drivers/YubiKey.h b/src/keys/drivers/YubiKey.h index 67eff5954..8a3744b8d 100644 --- a/src/keys/drivers/YubiKey.h +++ b/src/keys/drivers/YubiKey.h @@ -23,6 +23,7 @@ #include #include #include +#include typedef QPair YubiKeySlot; Q_DECLARE_METATYPE(YubiKeySlot); @@ -50,7 +51,7 @@ public: QList foundKeys(); QString getDisplayName(YubiKeySlot slot); - ChallengeResult challenge(YubiKeySlot slot, const QByteArray& challenge, QByteArray& response); + ChallengeResult challenge(YubiKeySlot slot, const QByteArray& challenge, Botan::secure_vector& response); bool testChallenge(YubiKeySlot slot, bool* wouldBlock = nullptr); QString errorMessage(); @@ -86,8 +87,11 @@ private: static YubiKey* m_instance; - ChallengeResult - performChallenge(void* key, int slot, bool mayBlock, const QByteArray& challenge, QByteArray& response); + ChallengeResult performChallenge(void* key, + int slot, + bool mayBlock, + const QByteArray& challenge, + Botan::secure_vector& response); bool performTestChallenge(void* key, int slot, bool* wouldBlock); QHash>> m_foundKeys; diff --git a/src/proxy/CMakeLists.txt b/src/proxy/CMakeLists.txt index 86c6af964..d013b5307 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 ${sodium_LIBRARY_RELEASE}) + target_link_libraries(proxy_alloc PRIVATE Qt5::Core ${BOTAN2_LIBRARIES}) add_executable(keepassxc-proxy ${proxy_SOURCES}) target_link_libraries(keepassxc-proxy proxy_alloc Qt5::Core Qt5::Network) diff --git a/src/crypto/ssh/ASN1Key.cpp b/src/sshagent/ASN1Key.cpp similarity index 71% rename from src/crypto/ssh/ASN1Key.cpp rename to src/sshagent/ASN1Key.cpp index b585d3abd..7fafbeb2b 100644 --- a/src/crypto/ssh/ASN1Key.cpp +++ b/src/sshagent/ASN1Key.cpp @@ -17,9 +17,7 @@ */ #include "ASN1Key.h" -#include "crypto/ssh/BinaryStream.h" - -#include +#include "BinaryStream.h" namespace { @@ -55,16 +53,6 @@ namespace return true; } - bool parsePublicHeader(BinaryStream& stream) - { - quint8 tag; - quint32 len; - - nextTag(stream, tag, len); - - return (tag == TAG_SEQUENCE); - } - bool parsePrivateHeader(BinaryStream& stream, quint8 wantedType) { quint8 tag; @@ -103,27 +91,6 @@ namespace stream.read(target); return true; } - - QByteArray calculateIqmp(QByteArray& bap, QByteArray& baq) - { - gcry_mpi_t u, p, q; - QByteArray iqmp_hex; - - u = gcry_mpi_snew(bap.length() * 8); - gcry_mpi_scan(&p, GCRYMPI_FMT_HEX, bap.toHex().data(), 0, nullptr); - gcry_mpi_scan(&q, GCRYMPI_FMT_HEX, baq.toHex().data(), 0, nullptr); - - mpi_invm(u, q, p); - - iqmp_hex.resize(bap.length() * 2); - gcry_mpi_print(GCRYMPI_FMT_HEX, reinterpret_cast(iqmp_hex.data()), iqmp_hex.size(), nullptr, u); - - gcry_mpi_release(u); - gcry_mpi_release(p); - gcry_mpi_release(q); - - return QByteArray::fromHex(QString(iqmp_hex).toLatin1()); - } } // namespace bool ASN1Key::parseDSA(QByteArray& ba, OpenSSHKey& key) @@ -161,34 +128,7 @@ bool ASN1Key::parseDSA(QByteArray& ba, OpenSSHKey& key) return true; } -bool ASN1Key::parsePublicRSA(QByteArray& ba, OpenSSHKey& key) -{ - BinaryStream stream(&ba); - - if (!parsePublicHeader(stream)) { - return false; - } - - QByteArray n, e; - readInt(stream, n); - readInt(stream, e); - - QList publicData; - publicData.append(e); - publicData.append(n); - - QList privateData; - privateData.append(n); - privateData.append(e); - - key.setType("ssh-rsa"); - key.setPublicData(publicData); - key.setPrivateData(privateData); - key.setComment(""); - return true; -} - -bool ASN1Key::parsePrivateRSA(QByteArray& ba, OpenSSHKey& key) +bool ASN1Key::parseRSA(QByteArray& ba, OpenSSHKey& key) { BinaryStream stream(&ba); @@ -206,6 +146,7 @@ bool ASN1Key::parsePrivateRSA(QByteArray& ba, OpenSSHKey& key) readInt(stream, dq); readInt(stream, qinv); + // Note: To properly calculate the key fingerprint, e and n are reversed per RFC 4253 QList publicData; publicData.append(e); publicData.append(n); @@ -214,7 +155,7 @@ bool ASN1Key::parsePrivateRSA(QByteArray& ba, OpenSSHKey& key) privateData.append(n); privateData.append(e); privateData.append(d); - privateData.append(calculateIqmp(p, q)); + privateData.append(qinv); privateData.append(p); privateData.append(q); diff --git a/src/crypto/ssh/ASN1Key.h b/src/sshagent/ASN1Key.h similarity index 89% rename from src/crypto/ssh/ASN1Key.h rename to src/sshagent/ASN1Key.h index 0a199d357..58dbcd4df 100644 --- a/src/crypto/ssh/ASN1Key.h +++ b/src/sshagent/ASN1Key.h @@ -25,8 +25,7 @@ namespace ASN1Key { bool parseDSA(QByteArray& ba, OpenSSHKey& key); - bool parsePrivateRSA(QByteArray& ba, OpenSSHKey& key); - bool parsePublicRSA(QByteArray& ba, OpenSSHKey& key); + bool parseRSA(QByteArray& ba, OpenSSHKey& key); } // namespace ASN1Key #endif // KEEPASSXC_ASN1KEY_H diff --git a/src/crypto/ssh/BinaryStream.cpp b/src/sshagent/BinaryStream.cpp similarity index 100% rename from src/crypto/ssh/BinaryStream.cpp rename to src/sshagent/BinaryStream.cpp diff --git a/src/crypto/ssh/BinaryStream.h b/src/sshagent/BinaryStream.h similarity index 100% rename from src/crypto/ssh/BinaryStream.h rename to src/sshagent/BinaryStream.h diff --git a/src/sshagent/CMakeLists.txt b/src/sshagent/CMakeLists.txt index d42059a5d..969467415 100644 --- a/src/sshagent/CMakeLists.txt +++ b/src/sshagent/CMakeLists.txt @@ -4,10 +4,13 @@ if(WITH_XC_SSHAGENT) set(sshagent_SOURCES AgentSettingsPage.cpp AgentSettingsWidget.cpp + ASN1Key.cpp + BinaryStream.cpp KeeAgentSettings.cpp + OpenSSHKey.cpp SSHAgent.cpp ) add_library(sshagent STATIC ${sshagent_SOURCES}) - target_link_libraries(sshagent Qt5::Core Qt5::Widgets Qt5::Network ${GCRYPT_LIBRARIES} ${crypto_ssh_LIB}) + target_link_libraries(sshagent Qt5::Core Qt5::Widgets Qt5::Network) endif() diff --git a/src/sshagent/KeeAgentSettings.cpp b/src/sshagent/KeeAgentSettings.cpp index e9f6e4bdd..b5cf0f426 100644 --- a/src/sshagent/KeeAgentSettings.cpp +++ b/src/sshagent/KeeAgentSettings.cpp @@ -455,7 +455,7 @@ bool KeeAgentSettings::toOpenSSHKey(const QString& username, return false; } - if (key.encrypted() && (decrypt || key.publicParts().isEmpty())) { + if (key.encrypted() && decrypt) { if (!key.openKey(password)) { m_error = key.errorString(); return false; diff --git a/src/sshagent/KeeAgentSettings.h b/src/sshagent/KeeAgentSettings.h index 3cc701666..c76ac3b16 100644 --- a/src/sshagent/KeeAgentSettings.h +++ b/src/sshagent/KeeAgentSettings.h @@ -19,9 +19,9 @@ #ifndef KEEAGENTSETTINGS_H #define KEEAGENTSETTINGS_H +#include "OpenSSHKey.h" #include "core/Entry.h" #include "core/EntryAttachments.h" -#include "crypto/ssh/OpenSSHKey.h" #include #include diff --git a/src/crypto/ssh/OpenSSHKey.cpp b/src/sshagent/OpenSSHKey.cpp similarity index 51% rename from src/crypto/ssh/OpenSSHKey.cpp rename to src/sshagent/OpenSSHKey.cpp index 7cabf38c6..4160bf7e7 100644 --- a/src/crypto/ssh/OpenSSHKey.cpp +++ b/src/sshagent/OpenSSHKey.cpp @@ -18,193 +18,21 @@ #include "OpenSSHKey.h" +#include "ASN1Key.h" +#include "BinaryStream.h" #include "core/Tools.h" #include "crypto/SymmetricCipher.h" -#include "crypto/ssh/ASN1Key.h" -#include "crypto/ssh/BinaryStream.h" #include #include #include -#include +#include const QString OpenSSHKey::TYPE_DSA_PRIVATE = "DSA PRIVATE KEY"; const QString OpenSSHKey::TYPE_RSA_PRIVATE = "RSA PRIVATE KEY"; -const QString OpenSSHKey::TYPE_RSA_PUBLIC = "RSA PUBLIC KEY"; const QString OpenSSHKey::TYPE_OPENSSH_PRIVATE = "OPENSSH PRIVATE KEY"; -namespace -{ - QPair> binaryDeserialize(const QByteArray& serialized) - { - if (serialized.isEmpty()) { - return {}; - } - QBuffer buffer; - buffer.setData(serialized); - buffer.open(QBuffer::ReadOnly); - BinaryStream stream(&buffer); - QString type; - stream.readString(type); - QByteArray temp; - QList data; - while (stream.readString(temp)) { - data << temp; - } - return ::qMakePair(type, data); - } - - QByteArray binarySerialize(const QString& type, const QList& data) - { - if (type.isEmpty() && data.isEmpty()) { - return {}; - } - QByteArray buffer; - BinaryStream stream(&buffer); - stream.writeString(type); - for (const QByteArray& part : data) { - stream.writeString(part); - } - return buffer; - } -} // namespace - -// bcrypt_pbkdf.cpp -int bcrypt_pbkdf(const QByteArray& pass, const QByteArray& salt, QByteArray& key, quint32 rounds); - -OpenSSHKey OpenSSHKey::generate(bool secure) -{ - enum Index - { - Params, - CombinedKey, - PrivateKey, - PublicKey, - - Private_N, - Private_E, - Private_D, - Private_P, - Private_Q, - Private_U, // private key - Public_N, - Public_E, - }; - - Tools::Map mpi; - Tools::Map sexp; - gcry_error_t rc = GPG_ERR_NO_ERROR; - rc = gcry_sexp_build(&sexp[Params], - NULL, - secure ? "(genkey (rsa (nbits 4:2048)))" : "(genkey (rsa (transient-key) (nbits 4:2048)))"); - if (rc != GPG_ERR_NO_ERROR) { - qWarning() << "Could not create ssh key" << gcry_err_code(rc); - return OpenSSHKey(); - } - - rc = gcry_pk_genkey(&sexp[CombinedKey], sexp[Params]); - if (rc != GPG_ERR_NO_ERROR) { - qWarning() << "Could not create ssh key" << gcry_err_code(rc); - return OpenSSHKey(); - } - - sexp[PrivateKey] = gcry_sexp_find_token(sexp[CombinedKey], "private-key", 0); - sexp[PublicKey] = gcry_sexp_find_token(sexp[CombinedKey], "public-key", 0); - - sexp[Private_N] = gcry_sexp_find_token(sexp[PrivateKey], "n", 1); - mpi[Private_N] = gcry_sexp_nth_mpi(sexp[Private_N], 1, GCRYMPI_FMT_USG); - sexp[Private_E] = gcry_sexp_find_token(sexp[PrivateKey], "e", 1); - mpi[Private_E] = gcry_sexp_nth_mpi(sexp[Private_E], 1, GCRYMPI_FMT_USG); - sexp[Private_D] = gcry_sexp_find_token(sexp[PrivateKey], "d", 1); - mpi[Private_D] = gcry_sexp_nth_mpi(sexp[Private_D], 1, GCRYMPI_FMT_USG); - sexp[Private_Q] = gcry_sexp_find_token(sexp[PrivateKey], "q", 1); - mpi[Private_Q] = gcry_sexp_nth_mpi(sexp[Private_Q], 1, GCRYMPI_FMT_USG); - sexp[Private_P] = gcry_sexp_find_token(sexp[PrivateKey], "p", 1); - mpi[Private_P] = gcry_sexp_nth_mpi(sexp[Private_P], 1, GCRYMPI_FMT_USG); - sexp[Private_U] = gcry_sexp_find_token(sexp[PrivateKey], "u", 1); - mpi[Private_U] = gcry_sexp_nth_mpi(sexp[Private_U], 1, GCRYMPI_FMT_USG); - - sexp[Public_N] = gcry_sexp_find_token(sexp[PublicKey], "n", 1); - mpi[Public_N] = gcry_sexp_nth_mpi(sexp[Public_N], 1, GCRYMPI_FMT_USG); - sexp[Public_E] = gcry_sexp_find_token(sexp[PublicKey], "e", 1); - mpi[Public_E] = gcry_sexp_nth_mpi(sexp[Public_E], 1, GCRYMPI_FMT_USG); - - QList publicParts; - QList privateParts; - Tools::Buffer buffer; - gcry_mpi_format format = GCRYMPI_FMT_USG; - rc = gcry_mpi_aprint(format, &buffer.raw, &buffer.size, mpi[Private_N]); - if (rc != GPG_ERR_NO_ERROR) { - qWarning() << "Could not extract private key part" << gcry_err_code(rc); - return OpenSSHKey(); - } - privateParts << buffer.content(); - - buffer.clear(); - rc = gcry_mpi_aprint(format, &buffer.raw, &buffer.size, mpi[Private_E]); - if (rc != GPG_ERR_NO_ERROR) { - qWarning() << "Could not extract private key part" << gcry_err_code(rc); - return OpenSSHKey(); - } - privateParts << buffer.content(); - - buffer.clear(); - rc = gcry_mpi_aprint(format, &buffer.raw, &buffer.size, mpi[Private_D]); - if (rc != GPG_ERR_NO_ERROR) { - qWarning() << "Could not extract private key part" << gcry_err_code(rc); - return OpenSSHKey(); - } - privateParts << buffer.content(); - - buffer.clear(); - rc = gcry_mpi_aprint(format, &buffer.raw, &buffer.size, mpi[Private_U]); - if (rc != GPG_ERR_NO_ERROR) { - qWarning() << "Could not extract private key part" << gcry_err_code(rc); - return OpenSSHKey(); - } - privateParts << buffer.content(); - - buffer.clear(); - rc = gcry_mpi_aprint(format, &buffer.raw, &buffer.size, mpi[Private_P]); - if (rc != GPG_ERR_NO_ERROR) { - qWarning() << "Could not extract private key part" << gcry_err_code(rc); - return OpenSSHKey(); - } - privateParts << buffer.content(); - - buffer.clear(); - rc = gcry_mpi_aprint(format, &buffer.raw, &buffer.size, mpi[Private_Q]); - if (rc != GPG_ERR_NO_ERROR) { - qWarning() << "Could not extract private key part" << gcry_err_code(rc); - return OpenSSHKey(); - } - privateParts << buffer.content(); - - buffer.clear(); - rc = gcry_mpi_aprint(format, &buffer.raw, &buffer.size, mpi[Public_E]); - if (rc != GPG_ERR_NO_ERROR) { - qWarning() << "Could not extract public key part" << gcry_err_code(rc); - return OpenSSHKey(); - } - publicParts << buffer.content(); - - buffer.clear(); - rc = gcry_mpi_aprint(format, &buffer.raw, &buffer.size, mpi[Public_N]); - if (rc != GPG_ERR_NO_ERROR) { - qWarning() << "Could not extract public key part" << gcry_err_code(rc); - return OpenSSHKey(); - } - publicParts << buffer.content(); - OpenSSHKey key; - key.m_rawType = OpenSSHKey::TYPE_RSA_PRIVATE; - key.setType("ssh-rsa"); - key.setPublicData(publicParts); - key.setPrivateData(privateParts); - key.setComment(""); - return key; -} - OpenSSHKey::OpenSSHKey(QObject* parent) : QObject(parent) , m_type(QString()) @@ -251,20 +79,6 @@ const QString OpenSSHKey::type() const return m_type; } -int OpenSSHKey::keyLength() const -{ - if (m_type == "ssh-dss" && m_rawPublicData.length() == 4) { - return (m_rawPublicData[0].length() - 1) * 8; - } else if (m_type == "ssh-rsa" && m_rawPublicData.length() == 2) { - return (m_rawPublicData[1].length() - 1) * 8; - } else if (m_type.startsWith("ecdsa-sha2-") && m_rawPublicData.length() == 2) { - return (m_rawPublicData[1].length() - 1) * 4; - } else if (m_type == "ssh-ed25519" && m_rawPublicData.length() == 1) { - return m_rawPublicData[0].length() * 8; - } - return 0; -} - const QString OpenSSHKey::fingerprint(QCryptographicHash::Algorithm algo) const { if (m_rawPublicData.isEmpty()) { @@ -301,24 +115,6 @@ const QString OpenSSHKey::comment() const return m_comment; } -const QString OpenSSHKey::privateKey() const -{ - if (m_rawPrivateData.isEmpty()) { - return {}; - } - - QByteArray privateKey; - BinaryStream stream(&privateKey); - - stream.writeString(m_type); - - for (QByteArray ba : m_rawPrivateData) { - stream.writeString(ba); - } - - return m_type + " " + QString::fromLatin1(privateKey.toBase64()) + " " + m_comment; -} - const QString OpenSSHKey::publicKey() const { if (m_rawPublicData.isEmpty()) { @@ -438,7 +234,7 @@ bool OpenSSHKey::parsePKCS1PEM(const QByteArray& in) return false; } - if (m_rawType == TYPE_DSA_PRIVATE || m_rawType == TYPE_RSA_PRIVATE || m_rawType == TYPE_RSA_PUBLIC) { + if (m_rawType == TYPE_DSA_PRIVATE || m_rawType == TYPE_RSA_PRIVATE) { m_rawData = data; } else if (m_rawType == TYPE_OPENSSH_PRIVATE) { BinaryStream stream(&data); @@ -508,7 +304,7 @@ bool OpenSSHKey::encrypted() const bool OpenSSHKey::openKey(const QString& passphrase) { - QScopedPointer cipher; + QScopedPointer cipher(new SymmetricCipher()); if (!m_rawPrivateData.isEmpty()) { return true; @@ -519,94 +315,89 @@ bool OpenSSHKey::openKey(const QString& passphrase) return false; } - if (m_cipherName.compare("aes-128-cbc", Qt::CaseInsensitive) == 0) { - cipher.reset(new SymmetricCipher(SymmetricCipher::Aes128, SymmetricCipher::Cbc, SymmetricCipher::Decrypt)); - } else if (m_cipherName == "aes256-cbc" || m_cipherName.compare("aes-256-cbc", Qt::CaseInsensitive) == 0) { - cipher.reset(new SymmetricCipher(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt)); - } else if (m_cipherName == "aes256-ctr" || m_cipherName.compare("aes-256-ctr", Qt::CaseInsensitive) == 0) { - cipher.reset(new SymmetricCipher(SymmetricCipher::Aes256, SymmetricCipher::Ctr, SymmetricCipher::Decrypt)); - } else if (m_cipherName != "none") { - m_error = tr("Unknown cipher: %1").arg(m_cipherName); - return false; - } + QByteArray rawData = m_rawData; - if (m_kdfName == "bcrypt") { - if (!cipher) { - m_error = tr("Trying to run KDF without cipher"); - return false; - } - - if (passphrase.isEmpty()) { - m_error = tr("Passphrase is required to decrypt this key"); - return false; - } - - BinaryStream optionStream(&m_kdfOptions); - - QByteArray salt; - quint32 rounds; - - optionStream.readString(salt); - optionStream.read(rounds); - - QByteArray decryptKey; - decryptKey.fill(0, cipher->keySize() + cipher->blockSize()); - - QByteArray phraseData = passphrase.toUtf8(); - if (bcrypt_pbkdf(phraseData, salt, decryptKey, rounds) < 0) { - m_error = tr("Key derivation failed, key file corrupted?"); + if (m_cipherName != "none") { + auto cipherMode = SymmetricCipher::stringToMode(m_cipherName); + if (cipherMode == SymmetricCipher::InvalidMode) { + m_error = tr("Unknown cipher: %1").arg(m_cipherName); return false; } QByteArray keyData, ivData; - keyData.setRawData(decryptKey.data(), cipher->keySize()); - ivData.setRawData(decryptKey.data() + cipher->keySize(), cipher->blockSize()); - cipher->init(keyData, ivData); + if (m_kdfName == "bcrypt") { + if (passphrase.isEmpty()) { + m_error = tr("Passphrase is required to decrypt this key"); + return false; + } - if (!cipher->init(keyData, ivData)) { - m_error = cipher->errorString(); - return false; - } - } else if (m_kdfName == "md5") { - if (m_cipherIV.length() < 8) { - m_error = tr("Cipher IV is too short for MD5 kdf"); + int keySize = cipher->keySize(cipherMode); + int blockSize = 16; + + BinaryStream optionStream(&m_kdfOptions); + + QByteArray salt; + quint32 rounds; + + optionStream.readString(salt); + optionStream.read(rounds); + + QByteArray decryptKey(keySize + blockSize, '\0'); + try { + auto baPass = passphrase.toUtf8(); + auto pwhash = Botan::PasswordHashFamily::create_or_throw("Bcrypt-PBKDF")->from_iterations(rounds); + pwhash->derive_key(reinterpret_cast(decryptKey.data()), + decryptKey.size(), + baPass.constData(), + baPass.size(), + reinterpret_cast(salt.constData()), + salt.size()); + } catch (std::exception& e) { + m_error = tr("Key derivation failed: %1").arg(e.what()); + return false; + } + + keyData = decryptKey.left(keySize); + ivData = decryptKey.right(blockSize); + } else if (m_kdfName == "md5") { + if (m_cipherIV.length() < 8) { + m_error = tr("Cipher IV is too short for MD5 kdf"); + return false; + } + + int keySize = cipher->keySize(cipherMode); + + QByteArray mdBuf; + do { + QCryptographicHash hash(QCryptographicHash::Md5); + hash.addData(mdBuf); + hash.addData(passphrase.toUtf8()); + hash.addData(m_cipherIV.data(), 8); + mdBuf = hash.result(); + keyData.append(mdBuf); + } while (keyData.size() < keySize); + + if (keyData.size() > keySize) { + // If our key size isn't a multiple of 16 (e.g. AES-192 or something), + // then we will need to truncate it. + keyData.resize(keySize); + } + + ivData = m_cipherIV; + } else if (m_kdfName != "none") { + m_error = tr("Unknown KDF: %1").arg(m_kdfName); return false; } - QByteArray keyData; - QByteArray mdBuf; - do { - QCryptographicHash hash(QCryptographicHash::Md5); - hash.addData(mdBuf); - hash.addData(passphrase.toUtf8()); - hash.addData(m_cipherIV.data(), 8); - mdBuf = hash.result(); - keyData.append(mdBuf); - } while (keyData.size() < cipher->keySize()); - - if (keyData.size() > cipher->keySize()) { - // If our key size isn't a multiple of 16 (e.g. AES-192 or something), - // then we will need to truncate it. - keyData.resize(cipher->keySize()); - } - - if (!cipher->init(keyData, m_cipherIV)) { - m_error = cipher->errorString(); + // Initialize the cipher using the processed key and iv data + if (!cipher->init(cipherMode, SymmetricCipher::Decrypt, keyData, ivData)) { + m_error = tr("Failed to initialize cipher: %1").arg(cipher->errorString()); return false; } - } else if (m_kdfName != "none") { - m_error = tr("Unknown KDF: %1").arg(m_kdfName); - return false; - } - - QByteArray rawData = m_rawData; - - if (cipher && cipher->isInitalized()) { - bool ok = false; - rawData = cipher->process(rawData, &ok); - if (!ok) { - m_error = tr("Decryption failed, wrong passphrase?"); + // Decrypt the raw data, we do not use finish because padding is handled separately + if (!cipher->process(rawData)) { + m_error = tr("Decryption failed: %1").arg(cipher->errorString()); return false; } } @@ -619,13 +410,7 @@ bool OpenSSHKey::openKey(const QString& passphrase) return true; } else if (m_rawType == TYPE_RSA_PRIVATE) { - if (!ASN1Key::parsePrivateRSA(rawData, *this)) { - m_error = tr("Decryption failed, wrong passphrase?"); - return false; - } - return true; - } else if (m_rawType == TYPE_RSA_PUBLIC) { - if (!ASN1Key::parsePublicRSA(rawData, *this)) { + if (!ASN1Key::parseRSA(rawData, *this)) { m_error = tr("Decryption failed, wrong passphrase?"); return false; } @@ -779,49 +564,6 @@ bool OpenSSHKey::writePrivate(BinaryStream& stream) return true; } -QList OpenSSHKey::publicParts() const -{ - return m_rawPublicData; -} - -QList OpenSSHKey::privateParts() const -{ - return m_rawPrivateData; -} - -const QString& OpenSSHKey::privateType() const -{ - return m_rawType; -} - -OpenSSHKey OpenSSHKey::restoreFromBinary(Type type, const QByteArray& serialized) -{ - OpenSSHKey key; - auto data = binaryDeserialize(serialized); - key.setType(data.first); - switch (type) { - case Public: - key.setPublicData(data.second); - break; - case Private: - key.setPrivateData(data.second); - break; - } - return key; -} - -QByteArray OpenSSHKey::serializeToBinary(Type type, const OpenSSHKey& key) -{ - Q_ASSERT(!key.encrypted()); - switch (type) { - case Public: - return binarySerialize(key.type(), key.publicParts()); - case Private: - return binarySerialize(key.type(), key.privateParts()); - } - return {}; -} - uint qHash(const OpenSSHKey& key) { return qHash(key.fingerprint()); diff --git a/src/crypto/ssh/OpenSSHKey.h b/src/sshagent/OpenSSHKey.h similarity index 82% rename from src/crypto/ssh/OpenSSHKey.h rename to src/sshagent/OpenSSHKey.h index 85c288b9f..99689da5b 100644 --- a/src/crypto/ssh/OpenSSHKey.h +++ b/src/sshagent/OpenSSHKey.h @@ -19,7 +19,8 @@ #ifndef KEEPASSXC_OPENSSHKEY_H #define KEEPASSXC_OPENSSHKEY_H -#include +#include +#include class BinaryStream; @@ -27,8 +28,6 @@ class OpenSSHKey : public QObject { Q_OBJECT public: - static OpenSSHKey generate(bool secure = true); - explicit OpenSSHKey(QObject* parent = nullptr); OpenSSHKey(const OpenSSHKey& other); bool operator==(const OpenSSHKey& other) const; @@ -39,11 +38,9 @@ public: const QString cipherName() const; const QString type() const; - int keyLength() const; const QString fingerprint(QCryptographicHash::Algorithm algo = QCryptographicHash::Sha256) const; const QString comment() const; const QString publicKey() const; - const QString privateKey() const; const QString errorString() const; void setType(const QString& type); @@ -58,24 +55,10 @@ public: bool writePublic(BinaryStream& stream); bool writePrivate(BinaryStream& stream); - QList publicParts() const; - QList privateParts() const; - const QString& privateType() const; - static const QString TYPE_DSA_PRIVATE; static const QString TYPE_RSA_PRIVATE; - static const QString TYPE_RSA_PUBLIC; static const QString TYPE_OPENSSH_PRIVATE; - enum Type - { - Public, - Private - }; - - static OpenSSHKey restoreFromBinary(Type eType, const QByteArray& serialized); - static QByteArray serializeToBinary(Type eType, const OpenSSHKey& key); - private: bool extractPEM(const QByteArray& in, QByteArray& out); diff --git a/src/sshagent/SSHAgent.cpp b/src/sshagent/SSHAgent.cpp index 1e67ae3c8..81610e7d4 100644 --- a/src/sshagent/SSHAgent.cpp +++ b/src/sshagent/SSHAgent.cpp @@ -18,12 +18,12 @@ #include "SSHAgent.h" +#include "BinaryStream.h" +#include "OpenSSHKey.h" #include "core/Config.h" #include "core/Database.h" #include "core/Group.h" #include "core/Metadata.h" -#include "crypto/ssh/BinaryStream.h" -#include "crypto/ssh/OpenSSHKey.h" #include "sshagent/KeeAgentSettings.h" #include diff --git a/src/sshagent/SSHAgent.h b/src/sshagent/SSHAgent.h index d18cd4770..2318fdffd 100644 --- a/src/sshagent/SSHAgent.h +++ b/src/sshagent/SSHAgent.h @@ -23,7 +23,7 @@ #include #include -#include "crypto/ssh/OpenSSHKey.h" +#include "OpenSSHKey.h" #include "sshagent/KeeAgentSettings.h" class SSHAgent : public QObject diff --git a/src/streams/SymmetricCipherStream.cpp b/src/streams/SymmetricCipherStream.cpp index b930d8023..afe6c1b53 100644 --- a/src/streams/SymmetricCipherStream.cpp +++ b/src/streams/SymmetricCipherStream.cpp @@ -17,12 +17,9 @@ #include "SymmetricCipherStream.h" -SymmetricCipherStream::SymmetricCipherStream(QIODevice* baseDevice, - SymmetricCipher::Algorithm algo, - SymmetricCipher::Mode mode, - SymmetricCipher::Direction direction) +SymmetricCipherStream::SymmetricCipherStream(QIODevice* baseDevice) : LayeredStream(baseDevice) - , m_cipher(new SymmetricCipher(algo, mode, direction)) + , m_cipher(new SymmetricCipher()) , m_bufferPos(0) , m_bufferFilling(false) , m_error(false) @@ -37,14 +34,18 @@ SymmetricCipherStream::~SymmetricCipherStream() close(); } -bool SymmetricCipherStream::init(const QByteArray& key, const QByteArray& iv) +bool SymmetricCipherStream::init(SymmetricCipher::Mode mode, + SymmetricCipher::Direction direction, + const QByteArray& key, + const QByteArray& iv) { - m_isInitialized = m_cipher->init(key, iv); + m_isInitialized = m_cipher->init(mode, direction, key, iv); if (!m_isInitialized) { setErrorString(m_cipher->errorString()); + return false; } - m_streamCipher = m_cipher->blockSize() == 1; - return m_isInitialized; + m_streamCipher = m_cipher->blockSize(m_cipher->mode()) == 1; + return true; } void SymmetricCipherStream::resetInternalState() @@ -145,41 +146,23 @@ bool SymmetricCipherStream::readBlock() m_bufferFilling = true; return false; } else { - if (!m_cipher->processInPlace(m_buffer)) { - m_error = true; - setErrorString(m_cipher->errorString()); - return false; - } m_bufferPos = 0; m_bufferFilling = false; - if (m_baseDevice->atEnd()) { - if (!m_streamCipher) { - // PKCS7 padding - quint8 padLength = m_buffer.at(m_buffer.size() - 1); - - if (padLength == blockSize()) { - Q_ASSERT(m_buffer == QByteArray(blockSize(), blockSize())); - // full block with just padding: discard - m_buffer.clear(); - return false; - } else if (padLength > blockSize()) { - // invalid padding - m_error = true; - setErrorString("Invalid padding."); - return false; - } else { - Q_ASSERT(m_buffer.right(padLength) == QByteArray(padLength, padLength)); - // resize buffer to strip padding - m_buffer.resize(blockSize() - padLength); - return true; - } - } else { - return m_buffer.size() > 0; + if (!m_streamCipher && m_baseDevice->atEnd()) { + if (!m_cipher->finish(m_buffer)) { + m_error = true; + setErrorString(m_cipher->errorString()); + return false; + } + } else if (m_buffer.size() > 0) { + if (!m_cipher->process(m_buffer)) { + m_error = true; + setErrorString(m_cipher->errorString()); + return false; } - } else { - return true; } + return m_buffer.size() > 0; } } @@ -222,14 +205,13 @@ bool SymmetricCipherStream::writeBlock(bool lastBlock) Q_ASSERT(m_streamCipher || lastBlock || (m_buffer.size() == blockSize())); if (lastBlock && !m_streamCipher) { - // PKCS7 padding - int padLen = blockSize() - m_buffer.size(); - for (int i = 0; i < padLen; i++) { - m_buffer.append(static_cast(padLen)); + QByteArray end; + if (!m_cipher->finish(m_buffer)) { + m_error = true; + setErrorString(m_cipher->errorString()); + return false; } - } - - if (!m_cipher->processInPlace(m_buffer)) { + } else if (!m_cipher->process(m_buffer)) { m_error = true; setErrorString(m_cipher->errorString()); return false; @@ -250,5 +232,5 @@ int SymmetricCipherStream::blockSize() const if (m_streamCipher) { return 1024; } - return m_cipher->blockSize(); + return m_cipher->blockSize(m_cipher->mode()); } diff --git a/src/streams/SymmetricCipherStream.h b/src/streams/SymmetricCipherStream.h index 4f8feaa33..3f11dc8bd 100644 --- a/src/streams/SymmetricCipherStream.h +++ b/src/streams/SymmetricCipherStream.h @@ -29,12 +29,10 @@ class SymmetricCipherStream : public LayeredStream Q_OBJECT public: - SymmetricCipherStream(QIODevice* baseDevice, - SymmetricCipher::Algorithm algo, - SymmetricCipher::Mode mode, - SymmetricCipher::Direction direction); + SymmetricCipherStream(QIODevice* baseDevice); ~SymmetricCipherStream(); - bool init(const QByteArray& key, const QByteArray& iv); + bool + init(SymmetricCipher::Mode mode, SymmetricCipher::Direction direction, const QByteArray& key, const QByteArray& iv); bool open(QIODevice::OpenMode mode) override; bool reset() override; void close() override; diff --git a/src/touchid/TouchID.h b/src/touchid/TouchID.h index d37522054..b33075886 100644 --- a/src/touchid/TouchID.h +++ b/src/touchid/TouchID.h @@ -33,7 +33,7 @@ public: bool storeKey(const QString& databasePath, const QByteArray& passwordKey); - QSharedPointer getKey(const QString& databasePath) const; + bool getKey(const QString& databasePath, QByteArray& passwordKey) const; bool isAvailable(); diff --git a/src/touchid/TouchID.mm b/src/touchid/TouchID.mm index 7df5ad556..350276131 100644 --- a/src/touchid/TouchID.mm +++ b/src/touchid/TouchID.mm @@ -57,28 +57,25 @@ bool TouchID::storeKey(const QString& databasePath, const QByteArray& passwordKe } // generate random AES 256bit key and IV - Random* random = randomGen(); - QByteArray randomKey = random->randomArray(32); - QByteArray randomIV = random->randomArray(16); + QByteArray randomKey = randomGen()->randomArray(32); + QByteArray randomIV = randomGen()->randomArray(16); - bool ok; - SymmetricCipher aes256Encrypt(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt); - - if (!aes256Encrypt.init(randomKey, randomIV)) { + SymmetricCipher aes256Encrypt; + if (!aes256Encrypt.init(SymmetricCipher::Aes256_CBC, SymmetricCipher::Encrypt, randomKey, randomIV)) { debug("TouchID::storeKey - Error initializing encryption: %s", aes256Encrypt.errorString().toUtf8().constData()); return false; } // encrypt and keep result in memory - QByteArray encryptedMasterKey = aes256Encrypt.process(passwordKey, &ok); - if (!ok) { + QByteArray encryptedMasterKey = passwordKey; + if (!aes256Encrypt.process(encryptedMasterKey)) { debug("TouchID::storeKey - Error encrypting: %s", aes256Encrypt.errorString().toUtf8().constData()); return false; } // memorize which database the stored key is for - this->m_encryptedMasterKeys.insert(databasePath, encryptedMasterKey); + m_encryptedMasterKeys.insert(databasePath, encryptedMasterKey); NSString* accountName = (SECURITY_ACCOUNT_PREFIX + hash(databasePath)).toNSString(); // autoreleased @@ -145,18 +142,19 @@ bool TouchID::storeKey(const QString& databasePath, const QByteArray& passwordKe * Checks if an encrypted PasswordKey is available for the given database, tries to * decrypt it using the KeyChain and if successful, returns it. */ -QSharedPointer TouchID::getKey(const QString& databasePath) const +bool TouchID::getKey(const QString& databasePath, QByteArray& passwordKey) const { + passwordKey.clear(); if (databasePath.isEmpty()) { // illegal arguments debug("TouchID::storeKey - Illegal argument: databasePath = %s", databasePath.toUtf8().constData()); - return NULL; + return false; } // checks if encrypted PasswordKey is available and is stored for the given database if (!this->m_encryptedMasterKeys.contains(databasePath)) { debug("TouchID::getKey - No stored key found"); - return NULL; + return false; } // query the KeyChain for the AES key @@ -179,12 +177,12 @@ QSharedPointer TouchID::getKey(const QString& databasePath) const CFRelease(query); if (status == errSecUserCanceled) { - // user canceled the authentication, need special return value + // user canceled the authentication, return true with empty key debug("TouchID::getKey - User canceled authentication"); - return QSharedPointer::create(); + return true; } else if (status != errSecSuccess || dataTypeRef == NULL) { debug("TouchID::getKey - Error retrieving result: %d", status); - return NULL; + return false; } CFDataRef valueData = static_cast(dataTypeRef); @@ -196,22 +194,20 @@ QSharedPointer TouchID::getKey(const QString& databasePath) const QByteArray key = dataBytes.left(32); QByteArray iv = dataBytes.right(16); - bool ok; - SymmetricCipher aes256Decrypt(SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt); - - if (!aes256Decrypt.init(key, iv)) { + SymmetricCipher aes256Decrypt; + if (!aes256Decrypt.init(SymmetricCipher::Aes256_CBC, SymmetricCipher::Decrypt, key, iv)) { debug("TouchID::getKey - Error initializing decryption: %s", aes256Decrypt.errorString().toUtf8().constData()); - return NULL; + return false; } // decrypt PasswordKey from memory using AES - QByteArray result = aes256Decrypt.process(this->m_encryptedMasterKeys[databasePath], &ok); - if (!ok) { + passwordKey = m_encryptedMasterKeys[databasePath]; + if (!aes256Decrypt.process(passwordKey)) { debug("TouchID::getKey - Error decryption: %s", aes256Decrypt.errorString().toUtf8().constData()); - return NULL; + return false; } - return QSharedPointer::create(result); + return true; } /** diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 845bb8593..b2385f1ec 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -94,8 +94,7 @@ set(testsupport_SOURCES modeltest.cpp FailDevice.cpp mock/MockClock.cpp - util/TemporaryFile.cpp - stub/TestRandom.cpp) + util/TemporaryFile.cpp) add_library(testsupport STATIC ${testsupport_SOURCES}) target_link_libraries(testsupport Qt5::Core Qt5::Concurrent Qt5::Widgets Qt5::Test) @@ -162,9 +161,9 @@ if(WITH_XC_AUTOTYPE) set_target_properties(testautotype PROPERTIES ENABLE_EXPORTS ON) endif() -if(WITH_XC_CRYPTO_SSH) +if(WITH_XC_SSHAGENT) add_unit_test(NAME testopensshkey SOURCES TestOpenSSHKey.cpp - LIBS ${TEST_LIBRARIES}) + LIBS sshagent ${TEST_LIBRARIES}) if(NOT WIN32) add_unit_test(NAME testsshagent SOURCES TestSSHAgent.cpp LIBS ${TEST_LIBRARIES}) diff --git a/tests/TestBrowser.cpp b/tests/TestBrowser.cpp index 577b08fe5..1d95edd67 100644 --- a/tests/TestBrowser.cpp +++ b/tests/TestBrowser.cpp @@ -21,9 +21,11 @@ #include "browser/BrowserSettings.h" #include "core/Tools.h" #include "crypto/Crypto.h" -#include "sodium/crypto_box.h" #include +#include + +using namespace Botan::Sodium; QTEST_GUILESS_MAIN(TestBrowser) diff --git a/tests/TestCli.cpp b/tests/TestCli.cpp index 838d761a6..fad5b98aa 100644 --- a/tests/TestCli.cpp +++ b/tests/TestCli.cpp @@ -1837,7 +1837,7 @@ void TestCli::testYubiKeyOption() bool wouldBlock = false; QByteArray challenge("CLITest"); - QByteArray response; + Botan::secure_vector response; QByteArray expected("\xA2\x3B\x94\x00\xBE\x47\x9A\x30\xA9\xEB\x50\x9B\x85\x56\x5B\x6B\x30\x25\xB4\x8E", 20); // Find a key that as configured for this test @@ -1845,7 +1845,7 @@ void TestCli::testYubiKeyOption() for (auto key : keys) { if (YubiKey::instance()->testChallenge(key, &wouldBlock) && !wouldBlock) { YubiKey::instance()->challenge(key, challenge, response); - if (response == expected) { + if (std::memcmp(response.data(), expected.data(), expected.size())) { pKey = key; break; } diff --git a/tests/TestFdoSecrets.cpp b/tests/TestFdoSecrets.cpp index eba97f67c..a7528f6ab 100644 --- a/tests/TestFdoSecrets.cpp +++ b/tests/TestFdoSecrets.cpp @@ -21,7 +21,7 @@ #include "core/EntrySearcher.h" #include "crypto/Crypto.h" -#include "fdosecrets/GcryptMPI.h" +#include "crypto/Random.h" #include "fdosecrets/dbus/DBusMgr.h" #include "fdosecrets/objects/Collection.h" #include "fdosecrets/objects/Item.h" @@ -29,69 +29,10 @@ QTEST_GUILESS_MAIN(TestFdoSecrets) -void TestFdoSecrets::initTestCase() -{ - QVERIFY(Crypto::init()); -} - -void TestFdoSecrets::cleanupTestCase() -{ -} - -void TestFdoSecrets::testGcryptMPI() -{ - auto bytes = QByteArray::fromHex(QByteArrayLiteral("DEADBEEF")); - - auto mpi = MpiFromBytes(bytes); - auto another = MpiFromHex("DEADBEEF"); - - // verify it can parse the bytes in USG mode - QVERIFY(mpi.get()); - QVERIFY(another.get()); - - // verify the number is of the correct value - QCOMPARE(gcry_mpi_cmp_ui(mpi.get(), 0xdeadbeef), 0); - QCOMPARE(gcry_mpi_cmp_ui(another.get(), 0xdeadbeef), 0); - - // verify it can convert back - QCOMPARE(MpiToBytes(mpi), bytes); - QCOMPARE(MpiToBytes(another), bytes); -} - void TestFdoSecrets::testDhIetf1024Sha256Aes128CbcPkcs7() { - auto clientPublic = MpiFromHex("40a0c8d27012c651bf270ebd96890a538" - "396fae3852aef69c0c19bae420d667577" - "ed471cd8ba5a49ef0ec91b568b95f87f0" - "9ec31d271f1699ed140c5b38644c42f60" - "ef84b5a6c406e17c07cd3208e5a605626" - "a5266153b447529946be2394dd43e5638" - "5ffbc4322902c2942391d1a36e8d125dc" - "809e3e406a2f5c2dcf39d3da2"); - auto serverPublic = MpiFromHex("e407997e8b918419cf851cf3345358fdf" - "ffb9564a220ac9c3934efd277cea20d17" - "467ecdc56e817f75ac39501f38a4a04ff" - "64d627e16c09981c7ad876da255b61c8e" - "6a8408236c2a4523cfe6961c26dbdfc77" - "c1a27a5b425ca71a019e829fae32c0b42" - "0e1b3096b48bc2ce9ccab1d1ff13a5eb4" - "b263cee30bdb1a57af9bfa93f"); - auto serverPrivate = MpiFromHex("013f4f3381ef0ca11c4c7363079577b56" - "99b238644e0aba47e24bdba6173590216" - "4f1e12dd0944800a373e090e63192f53b" - "93583e9a9e50bb9d792aafaa3a0f5ae77" - "de0c3423f5820848d88ee3bdd01c889f2" - "7af58a02f5b6693d422b9d189b300d7b1" - "be5076b5795cf8808c31e2e2898368d18" - "ab5c26b0ea3480c9aba8154cf"); - - std::unique_ptr cipher{new FdoSecrets::DhIetf1024Sha256Aes128CbcPkcs7}; - - cipher->initialize(std::move(clientPublic), std::move(serverPublic), std::move(serverPrivate)); - - QVERIFY(cipher->isValid()); - - QCOMPARE(cipher->m_aesKey.toHex(), QByteArrayLiteral("6b8f5ee55138eac37118508be21e7834")); + FdoSecrets::DhIetf1024Sha256Aes128CbcPkcs7 cipher(randomGen()->randomArray(128)); + QVERIFY(cipher.isValid()); } void TestFdoSecrets::testCrazyAttributeKey() diff --git a/tests/TestFdoSecrets.h b/tests/TestFdoSecrets.h index c41a6578f..05caf588c 100644 --- a/tests/TestFdoSecrets.h +++ b/tests/TestFdoSecrets.h @@ -25,10 +25,6 @@ class TestFdoSecrets : public QObject Q_OBJECT private slots: - void initTestCase(); - void cleanupTestCase(); - - void testGcryptMPI(); void testDhIetf1024Sha256Aes128CbcPkcs7(); void testCrazyAttributeKey(); void testSpecialCharsInAttributeValue(); diff --git a/tests/TestKeePass2RandomStream.cpp b/tests/TestKeePass2RandomStream.cpp index 3f5443532..bc7e16b99 100644 --- a/tests/TestKeePass2RandomStream.cpp +++ b/tests/TestKeePass2RandomStream.cpp @@ -35,8 +35,11 @@ void TestKeePass2RandomStream::test() const QByteArray key("\x11\x22\x33\x44\x55\x66\x77\x88"); const int Size = 128; - SymmetricCipher cipher(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt); - QVERIFY(cipher.init(CryptoHash::hash(key, CryptoHash::Sha256), KeePass2::INNER_STREAM_SALSA20_IV)); + SymmetricCipher cipher; + QVERIFY(cipher.init(SymmetricCipher::Salsa20, + SymmetricCipher::Encrypt, + CryptoHash::hash(key, CryptoHash::Sha256), + KeePass2::INNER_STREAM_SALSA20_IV)); const QByteArray data(QByteArray::fromHex("601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c5" "2b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6" @@ -45,7 +48,7 @@ void TestKeePass2RandomStream::test() QByteArray cipherPad; cipherPad.fill('\0', Size); - QVERIFY(cipher.processInPlace(cipherPad)); + QVERIFY(cipher.process(cipherPad)); QByteArray cipherData; cipherData.resize(Size); @@ -54,9 +57,9 @@ void TestKeePass2RandomStream::test() cipherData[i] = data[i] ^ cipherPad[i]; } - KeePass2RandomStream randomStream(KeePass2::ProtectedStreamAlgo::Salsa20); + KeePass2RandomStream randomStream; bool ok; - QVERIFY(randomStream.init(key)); + QVERIFY(randomStream.init(SymmetricCipher::Salsa20, key)); QByteArray randomStreamData; randomStreamData.append(randomStream.process(data.mid(0, 7), &ok)); QVERIFY(ok); @@ -70,10 +73,13 @@ void TestKeePass2RandomStream::test() randomStreamData.append(randomStream.process(data.mid(64, 64), &ok)); QVERIFY(ok); - SymmetricCipher cipherEncrypt(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt); - QVERIFY(cipherEncrypt.init(CryptoHash::hash(key, CryptoHash::Sha256), KeePass2::INNER_STREAM_SALSA20_IV)); - QByteArray cipherDataEncrypt = cipherEncrypt.process(data, &ok); - QVERIFY(ok); + SymmetricCipher cipherEncrypt; + QVERIFY(cipherEncrypt.init(SymmetricCipher::Salsa20, + SymmetricCipher::Encrypt, + CryptoHash::hash(key, CryptoHash::Sha256), + KeePass2::INNER_STREAM_SALSA20_IV)); + QByteArray cipherDataEncrypt = data; + QVERIFY(cipherEncrypt.process(cipherDataEncrypt)); QCOMPARE(randomStreamData.size(), Size); QCOMPARE(cipherData, cipherDataEncrypt); diff --git a/tests/TestOpenSSHKey.cpp b/tests/TestOpenSSHKey.cpp index 956dfc64a..2a880ab1c 100644 --- a/tests/TestOpenSSHKey.cpp +++ b/tests/TestOpenSSHKey.cpp @@ -18,8 +18,8 @@ #include "TestOpenSSHKey.h" #include "TestGlobal.h" #include "crypto/Crypto.h" -#include "crypto/ssh/BinaryStream.h" -#include "crypto/ssh/OpenSSHKey.h" +#include "sshagent/BinaryStream.h" +#include "sshagent/OpenSSHKey.h" QTEST_GUILESS_MAIN(TestOpenSSHKey) @@ -434,12 +434,3 @@ void TestOpenSSHKey::testDecryptUTF8() QCOMPARE(key.type(), QString("ssh-ed25519")); QCOMPARE(key.comment(), QString("opensshkey-test-utf8@keepassxc")); } - -void TestOpenSSHKey::testGenerateRSA() -{ - OpenSSHKey key = OpenSSHKey::generate(false); - QVERIFY(!key.encrypted()); - QCOMPARE(key.cipherName(), QString("none")); - QCOMPARE(key.type(), QString("ssh-rsa")); - QCOMPARE(key.comment(), QString("")); -} diff --git a/tests/TestOpenSSHKey.h b/tests/TestOpenSSHKey.h index 64516b204..214de8942 100644 --- a/tests/TestOpenSSHKey.h +++ b/tests/TestOpenSSHKey.h @@ -38,7 +38,6 @@ private slots: void testDecryptOpenSSHAES256CTR(); void testDecryptRSAAES256CTR(); void testDecryptUTF8(); - void testGenerateRSA(); }; #endif // TESTOPENSSHKEY_H diff --git a/tests/TestRandomGenerator.cpp b/tests/TestRandomGenerator.cpp index 02f183d07..ae6dfb4e8 100644 --- a/tests/TestRandomGenerator.cpp +++ b/tests/TestRandomGenerator.cpp @@ -17,64 +17,46 @@ #include "TestRandomGenerator.h" #include "TestGlobal.h" + #include "core/Endian.h" #include "core/Global.h" -#include "stub/TestRandom.h" +#include "crypto/Random.h" #include QTEST_GUILESS_MAIN(TestRandomGenerator) -void TestRandomGenerator::initTestCase() +void TestRandomGenerator::testArray() { - m_backend = new RandomBackendPreset(); + auto ba = randomGen()->randomArray(10); + QCOMPARE(ba.size(), 10); + QVERIFY(ba != QByteArray(10, '\0')); - TestRandom::setup(m_backend); -} - -void TestRandomGenerator::cleanupTestCase() -{ - TestRandom::teardown(); - - m_backend = nullptr; + auto ba2 = ba; + randomGen()->randomize(ba2); + QVERIFY(ba2 != ba); } void TestRandomGenerator::testUInt() { - QByteArray nextBytes; + QVERIFY(randomGen()->randomUInt(0) == 0); + QVERIFY(randomGen()->randomUInt(1) == 0); - nextBytes = Endian::sizedIntToBytes(42, QSysInfo::ByteOrder); - m_backend->setNextBytes(nextBytes); - QCOMPARE(randomGen()->randomUInt(100), 42U); - - nextBytes = Endian::sizedIntToBytes(117, QSysInfo::ByteOrder); - m_backend->setNextBytes(nextBytes); - QCOMPARE(randomGen()->randomUInt(100), 17U); - - nextBytes = Endian::sizedIntToBytes(1001, QSysInfo::ByteOrder); - m_backend->setNextBytes(nextBytes); - QCOMPARE(randomGen()->randomUInt(1), 0U); - - nextBytes.clear(); - nextBytes.append(Endian::sizedIntToBytes(QUINT32_MAX, QSysInfo::ByteOrder)); - nextBytes.append(Endian::sizedIntToBytes(QUINT32_MAX - 70000U, QSysInfo::ByteOrder)); - m_backend->setNextBytes(nextBytes); - QCOMPARE(randomGen()->randomUInt(100000U), (QUINT32_MAX - 70000U) % 100000U); - - nextBytes.clear(); - for (int i = 0; i < 10000; i++) { - nextBytes.append(Endian::sizedIntToBytes((QUINT32_MAX / 2U) + 1U + i, QSysInfo::ByteOrder)); + // Run a bunch of trials creating random numbers to ensure we meet the standard + for (int i = 0; i < 100; ++i) { + QVERIFY(randomGen()->randomUInt(5) < 5); + QVERIFY(randomGen()->randomUInt(100) < 100); + QVERIFY(randomGen()->randomUInt(100000U) < 100000U); + QVERIFY(randomGen()->randomUInt((QUINT32_MAX / 2U) + 1U) < QUINT32_MAX / 2U + 1U); } - nextBytes.append(Endian::sizedIntToBytes(QUINT32_MAX / 2U, QSysInfo::ByteOrder)); - m_backend->setNextBytes(nextBytes); - QCOMPARE(randomGen()->randomUInt((QUINT32_MAX / 2U) + 1U), QUINT32_MAX / 2U); } void TestRandomGenerator::testUIntRange() { - QByteArray nextBytes; - - nextBytes = Endian::sizedIntToBytes(42, QSysInfo::ByteOrder); - m_backend->setNextBytes(nextBytes); - QCOMPARE(randomGen()->randomUIntRange(100, 200), 142U); + // Run a bunch of trials to ensure we stay within the range + for (int i = 0; i < 100; ++i) { + auto rand = randomGen()->randomUIntRange(100, 200); + QVERIFY(rand >= 100); + QVERIFY(rand < 200); + } } diff --git a/tests/TestRandomGenerator.h b/tests/TestRandomGenerator.h index addcb0250..6f9c335e4 100644 --- a/tests/TestRandomGenerator.h +++ b/tests/TestRandomGenerator.h @@ -18,24 +18,16 @@ #ifndef KEEPASSX_TESTRANDOMGENERATOR_H #define KEEPASSX_TESTRANDOMGENERATOR_H -#include "crypto/Random.h" - #include -class RandomBackendPreset; - class TestRandomGenerator : public QObject { Q_OBJECT private slots: - void initTestCase(); - void cleanupTestCase(); + void testArray(); void testUInt(); void testUIntRange(); - -private: - RandomBackendPreset* m_backend; }; #endif // KEEPASSX_TESTRANDOMGENERATOR_H diff --git a/tests/TestSSHAgent.h b/tests/TestSSHAgent.h index 13e8076e7..bb9fa5459 100644 --- a/tests/TestSSHAgent.h +++ b/tests/TestSSHAgent.h @@ -18,10 +18,11 @@ #ifndef TESTSSHAGENT_H #define TESTSSHAGENT_H -#include "crypto/ssh/OpenSSHKey.h" +#include "sshagent/OpenSSHKey.h" #include #include #include +#include class TestSSHAgent : public QObject { diff --git a/tests/TestSharing.cpp b/tests/TestSharing.cpp index c1188ed14..b54076ed5 100644 --- a/tests/TestSharing.cpp +++ b/tests/TestSharing.cpp @@ -17,7 +17,6 @@ #include "TestSharing.h" #include "TestGlobal.h" -#include "stub/TestRandom.h" #include #include @@ -29,11 +28,14 @@ #include "core/Metadata.h" #include "crypto/Crypto.h" #include "crypto/Random.h" -#include "crypto/ssh/OpenSSHKey.h" #include "format/KeePass2Writer.h" #include "keeshare/KeeShareSettings.h" #include "keys/PasswordKey.h" +#include +#include +#include + #include QTEST_GUILESS_MAIN(TestSharing) @@ -50,59 +52,6 @@ void TestSharing::initTestCase() QVERIFY(Crypto::init()); } -void TestSharing::cleanupTestCase() -{ - TestRandom::teardown(); -} - -void TestSharing::testIdempotentDatabaseWriting() -{ - QScopedPointer db(new Database()); - auto key = QSharedPointer::create(); - key->addKey(QSharedPointer::create("password")); - db->setKey(key); - - Group* sharingGroup = new Group(); - sharingGroup->setName("SharingGroup"); - sharingGroup->setUuid(QUuid::createUuid()); - sharingGroup->setParent(db->rootGroup()); - - Entry* entry1 = new Entry(); - entry1->setUuid(QUuid::createUuid()); - entry1->beginUpdate(); - entry1->setTitle("Entry1"); - entry1->endUpdate(); - entry1->setGroup(sharingGroup); - - Entry* entry2 = new Entry(); - entry2->setUuid(QUuid::createUuid()); - entry2->beginUpdate(); - entry2->setTitle("Entry2"); - entry2->endUpdate(); - entry2->setGroup(sharingGroup); - - // prevent from changes introduced by randomization - TestRandom::setup(new RandomBackendNull()); - - QByteArray bufferOriginal; - { - QBuffer device(&bufferOriginal); - device.open(QIODevice::ReadWrite); - KeePass2Writer writer; - writer.writeDatabase(&device, db.data()); - } - - QByteArray bufferCopy; - { - QBuffer device(&bufferCopy); - device.open(QIODevice::ReadWrite); - KeePass2Writer writer; - writer.writeDatabase(&device, db.data()); - } - - QCOMPARE(bufferCopy, bufferOriginal); -} - void TestSharing::testNullObjects() { const QString empty; @@ -142,11 +91,10 @@ void TestSharing::testNullObjects() void TestSharing::testCertificateSerialization() { QFETCH(KeeShareSettings::Trust, trusted); - const OpenSSHKey& key = stubkey(); + auto key = stubkey(); KeeShareSettings::ScopedCertificate original; original.path = "/path"; - original.certificate = KeeShareSettings::Certificate{OpenSSHKey::serializeToBinary(OpenSSHKey::Public, key), - "Some &#_\"\" weird string"}; + original.certificate = KeeShareSettings::Certificate{key, "Some &#_\"\" weird string"}; original.trust = trusted; QString buffer; @@ -156,17 +104,18 @@ void TestSharing::testCertificateSerialization() KeeShareSettings::ScopedCertificate::serialize(writer, original); writer.writeEndElement(); writer.writeEndDocument(); + QXmlStreamReader reader(buffer); reader.readNextStartElement(); QVERIFY(reader.name() == "Certificate"); KeeShareSettings::ScopedCertificate restored = KeeShareSettings::ScopedCertificate::deserialize(reader); - QCOMPARE(restored.certificate.key, original.certificate.key); + QCOMPARE(restored.certificate.key->private_key_bits(), original.certificate.key->private_key_bits()); QCOMPARE(restored.certificate.signer, original.certificate.signer); QCOMPARE(restored.trust, original.trust); QCOMPARE(restored.path, original.path); - QCOMPARE(restored.certificate.sshKey().publicParts(), key.publicParts()); + QCOMPARE(restored.certificate.key->public_key_bits(), key->public_key_bits()); } void TestSharing::testCertificateSerialization_data() @@ -179,9 +128,9 @@ void TestSharing::testCertificateSerialization_data() void TestSharing::testKeySerialization() { - const OpenSSHKey& key = stubkey(); + auto key = stubkey(); KeeShareSettings::Key original; - original.key = OpenSSHKey::serializeToBinary(OpenSSHKey::Private, key); + original.key = key; QString buffer; QXmlStreamWriter writer(&buffer); @@ -195,9 +144,8 @@ void TestSharing::testKeySerialization() QVERIFY(reader.name() == "Key"); KeeShareSettings::Key restored = KeeShareSettings::Key::deserialize(reader); - QCOMPARE(restored.key, original.key); - QCOMPARE(restored.sshKey().privateParts(), key.privateParts()); - QCOMPARE(restored.sshKey().type(), key.type()); + QCOMPARE(restored.key->private_key_bits(), original.key->private_key_bits()); + QCOMPARE(restored.key->algo_name(), original.key->algo_name()); } void TestSharing::testReferenceSerialization() @@ -263,31 +211,33 @@ void TestSharing::testSettingsSerialization() QCOMPARE(restoredActive.in, importing); QCOMPARE(restoredActive.out, exporting); - QCOMPARE(restoredOwn.certificate.key, ownCertificate.key); - QCOMPARE(restoredOwn.key.key, ownKey.key); + if (ownCertificate.key) { + QCOMPARE(restoredOwn.certificate, ownCertificate); + } + if (ownKey.key) { + QCOMPARE(restoredOwn.key, ownKey); + } QCOMPARE(restoredForeign.certificates.count(), foreignCertificates.count()); for (int i = 0; i < foreignCertificates.count(); ++i) { - QCOMPARE(restoredForeign.certificates[i].certificate.key, foreignCertificates[i].certificate.key); + QCOMPARE(restoredForeign.certificates[i].certificate, foreignCertificates[i].certificate); } } void TestSharing::testSettingsSerialization_data() { - const OpenSSHKey& sshKey0 = stubkey(0); + auto sshKey0 = stubkey(0); KeeShareSettings::ScopedCertificate certificate0; certificate0.path = "/path/0"; - certificate0.certificate = KeeShareSettings::Certificate{OpenSSHKey::serializeToBinary(OpenSSHKey::Public, sshKey0), - "Some &#_\"\" weird string"}; + certificate0.certificate = KeeShareSettings::Certificate{sshKey0, "Some &#_\"\" weird string"}; certificate0.trust = KeeShareSettings::Trust::Trusted; KeeShareSettings::Key key0; - key0.key = OpenSSHKey::serializeToBinary(OpenSSHKey::Private, sshKey0); + key0.key = sshKey0; - const OpenSSHKey& sshKey1 = stubkey(1); + auto sshKey1 = stubkey(1); KeeShareSettings::ScopedCertificate certificate1; certificate1.path = "/path/1"; - certificate1.certificate = - KeeShareSettings::Certificate{OpenSSHKey::serializeToBinary(OpenSSHKey::Public, sshKey1), "Another "}; + certificate1.certificate = KeeShareSettings::Certificate{sshKey1, "Another "}; certificate1.trust = KeeShareSettings::Trust::Untrusted; QTest::addColumn("importing"); @@ -307,13 +257,12 @@ void TestSharing::testSettingsSerialization_data() << QList({certificate1}); } -const OpenSSHKey& TestSharing::stubkey(int index) +const QSharedPointer TestSharing::stubkey(int index) { - static QMap keys; + static QMap> keys; if (!keys.contains(index)) { - OpenSSHKey* key = new OpenSSHKey(OpenSSHKey::generate(false)); - key->setParent(this); - keys[index] = key; + keys.insert(index, + QSharedPointer(new Botan::RSA_PrivateKey(*randomGen()->getRng(), 2048))); } - return *keys[index]; + return keys[index]; } diff --git a/tests/TestSharing.h b/tests/TestSharing.h index ebf85fd4e..f3e45faf2 100644 --- a/tests/TestSharing.h +++ b/tests/TestSharing.h @@ -21,16 +21,16 @@ #include #include -class OpenSSHKey; - +namespace Botan +{ + class RSA_PrivateKey; +} class TestSharing : public QObject { Q_OBJECT private slots: void initTestCase(); - void cleanupTestCase(); - void testIdempotentDatabaseWriting(); void testNullObjects(); void testCertificateSerialization(); void testCertificateSerialization_data(); @@ -41,7 +41,7 @@ private slots: void testSettingsSerialization_data(); private: - const OpenSSHKey& stubkey(int iIndex = 0); + const QSharedPointer stubkey(int index = 0); }; #endif // KEEPASSXC_TESTSHARING_H diff --git a/tests/TestSignature.cpp b/tests/TestSignature.cpp index 887143a85..f4a3dbe59 100644 --- a/tests/TestSignature.cpp +++ b/tests/TestSignature.cpp @@ -22,9 +22,13 @@ #include #include "crypto/Crypto.h" -#include "crypto/ssh/OpenSSHKey.h" +#include "crypto/Random.h" #include "keeshare/Signature.h" +#include +#include +#include + QTEST_GUILESS_MAIN(TestSignature) static const char* rsa_2_private = "-----BEGIN RSA PRIVATE KEY-----\n" "MIIEowIBAAKCAQEAwGdladnqFfcDy02Gubx4sdBT8NYEg2YKXfcKLSwca5gV4X7I\n" @@ -107,6 +111,35 @@ static const char* rsa_1_public = "-----BEGIN RSA PUBLIC KEY-----\n" static QByteArray data("Some trivial test with a longer .... ................................. longer text"); +QSharedPointer loadPrivateKey(const QString& pem) +{ + try { + std::string label; + auto der = Botan::PEM_Code::decode(pem.toStdString(), label); + auto key = new Botan::RSA_PrivateKey( + Botan::AlgorithmIdentifier("RSA", Botan::AlgorithmIdentifier::USE_NULL_PARAM), der); + return QSharedPointer(key); + } catch (std::exception& e) { + qWarning("Failed to load key: %s", e.what()); + return {}; + } +} + +QSharedPointer loadPublicKey(const QString& pem) +{ + try { + std::string label; + auto der = Botan::PEM_Code::decode(pem.toStdString(), label); + auto key = + new Botan::RSA_PublicKey(Botan::AlgorithmIdentifier("RSA", Botan::AlgorithmIdentifier::USE_NULL_PARAM), + std::vector(der.begin(), der.end())); + return QSharedPointer(key); + } catch (std::exception& e) { + qWarning("Failed to load key: %s", e.what()); + return {}; + } +} + void TestSignature::initTestCase() { QVERIFY(Crypto::init()); @@ -114,85 +147,70 @@ void TestSignature::initTestCase() void TestSignature::testSigningOpenSSH_RSA_PrivateOnly() { - OpenSSHKey privateKey; - privateKey.parsePKCS1PEM(rsa_2_private); - privateKey.openKey(QString()); - QCOMPARE(privateKey.fingerprint(), QString("SHA256:ZAQ/W1QdW59OaIh/0hs3ePl2og5TjXnGX5L0iN7WtNA")); - Signature signer; - const QString sign = signer.create(data, privateKey); - QVERIFY(!sign.isEmpty()); + auto rsaKey = loadPrivateKey(rsa_2_private); + QVERIFY(rsaKey); + QString sign; + Signature::create(data, rsaKey, sign); QCOMPARE(sign, QString("rsa|%1").arg(QString::fromLatin1(rsa_2_sign))); - Signature verifier; - const bool verified = verifier.verify(data, sign, privateKey); + const bool verified = Signature::verify(data, rsaKey, sign); QCOMPARE(verified, true); } void TestSignature::testSigningOpenSSH_RSA() { - OpenSSHKey privateKey; - privateKey.parsePKCS1PEM(rsa_2_private); - privateKey.openKey(QString()); - QCOMPARE(privateKey.fingerprint(), QString("SHA256:ZAQ/W1QdW59OaIh/0hs3ePl2og5TjXnGX5L0iN7WtNA")); - Signature signer; - const QString sign = signer.create(data, privateKey); + auto privateKey = loadPrivateKey(rsa_2_private); + QVERIFY(privateKey); + + QString sign; + Signature::create(data, privateKey, sign); QVERIFY(!sign.isEmpty()); - OpenSSHKey publicKey; - publicKey.parsePKCS1PEM(rsa_2_public); - publicKey.openKey(QString()); - QCOMPARE(publicKey.fingerprint(), QString("SHA256:ZAQ/W1QdW59OaIh/0hs3ePl2og5TjXnGX5L0iN7WtNA")); + auto publicKey = loadPublicKey(rsa_2_public); + QVERIFY(publicKey); - Signature verifier; - const bool verified = verifier.verify(data, sign, publicKey); + const bool verified = Signature::verify(data, publicKey, sign); QCOMPARE(verified, true); } void TestSignature::testSigningGenerated_RSA_PrivateOnly() { - OpenSSHKey privateKey = OpenSSHKey::generate(false); - privateKey.openKey(QString()); + QSharedPointer key(new Botan::RSA_PrivateKey(*randomGen()->getRng(), 2048)); - Signature signer; - const QString sign = signer.create(data, privateKey); + QString sign; + Signature::create(data, key, sign); QVERIFY(!sign.isEmpty()); - Signature verifier; - const bool verified = verifier.verify(data, sign, privateKey); + const bool verified = Signature::verify(data, key, sign); QCOMPARE(verified, true); } void TestSignature::testSigningTest_RSA_PrivateOnly() { - OpenSSHKey privateKey; - privateKey.parsePKCS1PEM(rsa_1_private); - privateKey.openKey(QString()); - QCOMPARE(privateKey.fingerprint(), QString("SHA256:DYdaZciYNxCejr+/8x+OKYxeTU1D5UsuIFUG4PWRFkk")); - Signature signer; - const QString sign = signer.create(data, privateKey); + auto rsaKey = loadPrivateKey(rsa_2_private); + QVERIFY(rsaKey); + + QString sign; + Signature::create(data, rsaKey, sign); QVERIFY(!sign.isEmpty()); - Signature verifier; - const bool verified = verifier.verify(data, sign, privateKey); + const bool verified = Signature::verify(data, rsaKey, sign); QCOMPARE(verified, true); } void TestSignature::testSigningTest_RSA() { - OpenSSHKey privateKey; - privateKey.parsePKCS1PEM(rsa_1_private); - privateKey.openKey(QString()); - QCOMPARE(privateKey.fingerprint(), QString("SHA256:DYdaZciYNxCejr+/8x+OKYxeTU1D5UsuIFUG4PWRFkk")); - Signature signer; - const QString sign = signer.create(data, privateKey); + auto privateKey = loadPrivateKey(rsa_1_private); + QVERIFY(privateKey); + + QString sign; + Signature::create(data, privateKey, sign); QVERIFY(!sign.isEmpty()); - OpenSSHKey publicKey; - publicKey.parsePKCS1PEM(rsa_1_public); - publicKey.openKey(QString()); - QCOMPARE(publicKey.fingerprint(), QString("SHA256:DYdaZciYNxCejr+/8x+OKYxeTU1D5UsuIFUG4PWRFkk")); - Signature verifier; - const bool verified = verifier.verify(data, sign, publicKey); + auto publicKey = loadPublicKey(rsa_1_public); + QVERIFY(publicKey); + + const bool verified = Signature::verify(data, publicKey, sign); QCOMPARE(verified, true); } diff --git a/tests/TestSymmetricCipher.cpp b/tests/TestSymmetricCipher.cpp index bc872a510..de6b2c1c0 100644 --- a/tests/TestSymmetricCipher.cpp +++ b/tests/TestSymmetricCipher.cpp @@ -26,7 +26,6 @@ #include "streams/SymmetricCipherStream.h" QTEST_GUILESS_MAIN(TestSymmetricCipher) -Q_DECLARE_METATYPE(SymmetricCipher::Algorithm); Q_DECLARE_METATYPE(SymmetricCipher::Mode); Q_DECLARE_METATYPE(SymmetricCipher::Direction); @@ -35,19 +34,18 @@ void TestSymmetricCipher::initTestCase() QVERIFY(Crypto::init()); } -void TestSymmetricCipher::testAlgorithmToCipher() +void TestSymmetricCipher::testCipherUuidToMode() { - QCOMPARE(SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm::Aes128), KeePass2::CIPHER_AES128); - QCOMPARE(SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm::Aes256), KeePass2::CIPHER_AES256); - QCOMPARE(SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm::Twofish), KeePass2::CIPHER_TWOFISH); - QCOMPARE(SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm::ChaCha20), KeePass2::CIPHER_CHACHA20); - QCOMPARE(SymmetricCipher::algorithmToCipher(SymmetricCipher::Algorithm::InvalidAlgorithm), QUuid()); + QCOMPARE(SymmetricCipher::cipherUuidToMode(KeePass2::CIPHER_AES128), SymmetricCipher::Aes128_CBC); + QCOMPARE(SymmetricCipher::cipherUuidToMode(KeePass2::CIPHER_AES256), SymmetricCipher::Aes256_CBC); + QCOMPARE(SymmetricCipher::cipherUuidToMode(KeePass2::CIPHER_TWOFISH), SymmetricCipher::Twofish_CBC); + QCOMPARE(SymmetricCipher::cipherUuidToMode(KeePass2::CIPHER_CHACHA20), SymmetricCipher::ChaCha20); + QCOMPARE(SymmetricCipher::cipherUuidToMode(QUuid()), SymmetricCipher::InvalidMode); } // clang-format off void TestSymmetricCipher::testEncryptionDecryption_data() { - QTest::addColumn("algorithm"); QTest::addColumn("mode"); QTest::addColumn("direction"); QTest::addColumn("key"); @@ -57,64 +55,57 @@ void TestSymmetricCipher::testEncryptionDecryption_data() // http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf QTest::newRow("AES128-CBC Encryption") - << SymmetricCipher::Aes128 - << SymmetricCipher::Cbc + << SymmetricCipher::Aes128_CBC << SymmetricCipher::Encrypt << QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c") << QByteArray::fromHex("000102030405060708090a0b0c0d0e0f") - << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51") + << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a") << QByteArray::fromHex("7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b2"); QTest::newRow("AES128-CBC Decryption") - << SymmetricCipher::Aes128 - << SymmetricCipher::Cbc + << SymmetricCipher::Aes128_CBC << SymmetricCipher::Decrypt << QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c") << QByteArray::fromHex("000102030405060708090a0b0c0d0e0f") << QByteArray::fromHex("7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b2") - << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"); + << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a"); QTest::newRow("AES256-CBC Encryption") - << SymmetricCipher::Aes256 - << SymmetricCipher::Cbc + << SymmetricCipher::Aes256_CBC << SymmetricCipher::Encrypt << QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4") << QByteArray::fromHex("000102030405060708090a0b0c0d0e0f") - << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51") + << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a") << QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d"); QTest::newRow("AES256-CBC Decryption") - << SymmetricCipher::Aes256 - << SymmetricCipher::Cbc + << SymmetricCipher::Aes256_CBC << SymmetricCipher::Decrypt << QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4") << QByteArray::fromHex("000102030405060708090a0b0c0d0e0f") << QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d") - << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"); + << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a"); QTest::newRow("AES256-CTR Encryption") - << SymmetricCipher::Aes256 - << SymmetricCipher::Ctr + << SymmetricCipher::Aes256_CTR << SymmetricCipher::Encrypt << QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4") << QByteArray::fromHex("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff") - << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51") + << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a") << QByteArray::fromHex("601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c5"); QTest::newRow("AES256-CTR Decryption") - << SymmetricCipher::Aes256 - << SymmetricCipher::Ctr + << SymmetricCipher::Aes256_CTR << SymmetricCipher::Decrypt << QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4") << QByteArray::fromHex("f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff") << QByteArray::fromHex("601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c5") - << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"); + << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a"); } // clang-format on void TestSymmetricCipher::testEncryptionDecryption() { - QFETCH(SymmetricCipher::Algorithm, algorithm); QFETCH(SymmetricCipher::Mode, mode); QFETCH(SymmetricCipher::Direction, direction); QFETCH(QByteArray, key); @@ -122,66 +113,51 @@ void TestSymmetricCipher::testEncryptionDecryption() QFETCH(QByteArray, plainText); QFETCH(QByteArray, cipherText); - bool ok; - SymmetricCipher cipher(algorithm, mode, direction); - QVERIFY(cipher.init(key, iv)); - QCOMPARE(cipher.blockSize(), 16); - QCOMPARE(cipher.process(plainText, &ok), cipherText); - QVERIFY(ok); + QByteArray data = plainText; + SymmetricCipher cipher; + QVERIFY(cipher.init(mode, direction, key, iv)); + QVERIFY(cipher.process(data)); + QCOMPARE(data.left(16), cipherText.left(16)); - if (mode == SymmetricCipher::Cbc) { - QBuffer buffer; - SymmetricCipherStream stream(&buffer, algorithm, mode, direction); - QVERIFY(stream.init(key, iv)); - buffer.open(QIODevice::WriteOnly); - QVERIFY(stream.open(QIODevice::WriteOnly)); - QVERIFY(stream.reset()); - - buffer.reset(); - buffer.buffer().clear(); - QCOMPARE(stream.write(plainText.left(16)), qint64(16)); - QCOMPARE(buffer.data(), cipherText.left(16)); - QVERIFY(stream.reset()); - // make sure padding is written - QCOMPARE(buffer.data().size(), 32); - - buffer.reset(); - buffer.buffer().clear(); - QCOMPARE(stream.write(plainText.left(10)), qint64(10)); - QVERIFY(buffer.data().isEmpty()); - - QVERIFY(stream.reset()); - buffer.reset(); - buffer.buffer().clear(); - QCOMPARE(stream.write(plainText.left(10)), qint64(10)); - stream.close(); - QCOMPARE(buffer.data().size(), 16); - } + QBuffer buffer; + buffer.open(QIODevice::WriteOnly); + SymmetricCipherStream stream(&buffer); + QVERIFY(stream.init(mode, direction, key, iv)); + QVERIFY(stream.open(QIODevice::WriteOnly)); + QCOMPARE(stream.write(plainText.left(16)), qint64(16)); + stream.close(); + QCOMPARE(buffer.data().left(16), cipherText.left(16)); } void TestSymmetricCipher::testAesCbcPadding_data() { + QTest::addColumn("mode"); QTest::addColumn("key"); QTest::addColumn("iv"); QTest::addColumn("cipherText"); QTest::addColumn("plainText"); QTest::addColumn("padding"); - QTest::newRow("AES128") << QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c") + // clang-format off + QTest::newRow("AES128") << SymmetricCipher::Aes128_CBC + << QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c") << QByteArray::fromHex("000102030405060708090a0b0c0d0e0f") << QByteArray::fromHex("7649abac8119b246cee98e9b12e9197d5086cb9b507219ee95db113a917678b2") << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51") << QByteArray::fromHex("55e21d7100b988ffec32feeafaf23538"); - QTest::newRow("AES256") << QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4") + QTest::newRow("AES256") << SymmetricCipher::Aes256_CBC + << QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4") << QByteArray::fromHex("000102030405060708090a0b0c0d0e0f") << QByteArray::fromHex("f58c4c04d6e5f1ba779eabfb5f7bfbd69cfc4e967edb808d679f777bc6702c7d") << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51") << QByteArray::fromHex("3a3aa5e0213db1a9901f9036cf5102d2"); + // clang-format on } void TestSymmetricCipher::testAesCbcPadding() { + QFETCH(SymmetricCipher::Mode, mode); QFETCH(QByteArray, key); QFETCH(QByteArray, iv); QFETCH(QByteArray, cipherText); @@ -192,64 +168,36 @@ void TestSymmetricCipher::testAesCbcPadding() QByteArray cipherTextPadded = cipherText + padding; QBuffer buffer(&cipherTextPadded); - SymmetricCipherStream stream(&buffer, SymmetricCipher::Aes128, SymmetricCipher::Cbc, SymmetricCipher::Decrypt); - QVERIFY(stream.init(key, iv)); + SymmetricCipherStream stream(&buffer); + QVERIFY(stream.init(mode, SymmetricCipher::Decrypt, key, iv)); buffer.open(QIODevice::ReadOnly); QVERIFY(stream.open(QIODevice::ReadOnly)); QCOMPARE(stream.read(10), plainText.left(10)); buffer.reset(); QVERIFY(stream.reset()); + QVERIFY(stream.init(mode, SymmetricCipher::Decrypt, key, iv)); QCOMPARE(stream.read(20), plainText.left(20)); buffer.reset(); QVERIFY(stream.reset()); + QVERIFY(stream.init(mode, SymmetricCipher::Decrypt, key, iv)); QCOMPARE(stream.read(16), plainText.left(16)); buffer.reset(); QVERIFY(stream.reset()); + QVERIFY(stream.init(mode, SymmetricCipher::Decrypt, key, iv)); QCOMPARE(stream.read(100), plainText); } -void TestSymmetricCipher::testInplaceEcb_data() +void TestSymmetricCipher::testAesKdf() { - QTest::addColumn("key"); - QTest::addColumn("plainText"); - QTest::addColumn("cipherText"); + auto key = QByteArray::fromHex("603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4"); + auto data = QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a"); + auto result = QByteArray::fromHex("f3eed1bdb5d2a03c064b5a7e3db181f8"); - QTest::newRow("AES128") << QByteArray::fromHex("2b7e151628aed2a6abf7158809cf4f3c") - << QByteArray::fromHex("6bc1bee22e409f96e93d7e117393172a") - << QByteArray::fromHex("3ad77bb40d7a3660a89ecaf32466ef97"); -} + QVERIFY(SymmetricCipher::aesKdf(key, 1, data)); + QCOMPARE(data, result); -void TestSymmetricCipher::testInplaceEcb() -{ - QFETCH(QByteArray, key); - QFETCH(QByteArray, plainText); - QFETCH(QByteArray, cipherText); - - SymmetricCipher cipherInPlaceEnc(SymmetricCipher::Aes128, SymmetricCipher::Ecb, SymmetricCipher::Encrypt); - QVERIFY(cipherInPlaceEnc.init(key, QByteArray(16, 0))); - QCOMPARE(cipherInPlaceEnc.blockSize(), 16); - auto data = QByteArray(plainText); - QVERIFY(cipherInPlaceEnc.processInPlace(data)); - QCOMPARE(data, cipherText); - - SymmetricCipher cipherInPlaceDec(SymmetricCipher::Aes128, SymmetricCipher::Ecb, SymmetricCipher::Decrypt); - QVERIFY(cipherInPlaceDec.init(key, QByteArray(16, 0))); - QCOMPARE(cipherInPlaceDec.blockSize(), 16); - QVERIFY(cipherInPlaceDec.processInPlace(data)); - QCOMPARE(data, plainText); - - SymmetricCipher cipherInPlaceEnc2(SymmetricCipher::Aes128, SymmetricCipher::Ecb, SymmetricCipher::Encrypt); - QVERIFY(cipherInPlaceEnc2.init(key, QByteArray(16, 0))); - QCOMPARE(cipherInPlaceEnc2.blockSize(), 16); - data = QByteArray(plainText); - QVERIFY(cipherInPlaceEnc2.processInPlace(data, 100)); - - SymmetricCipher cipherInPlaceDec2(SymmetricCipher::Aes128, SymmetricCipher::Ecb, SymmetricCipher::Decrypt); - QVERIFY(cipherInPlaceDec2.init(key, QByteArray(16, 0))); - QCOMPARE(cipherInPlaceDec2.blockSize(), 16); - QVERIFY(cipherInPlaceDec2.processInPlace(data, 100)); - QCOMPARE(data, plainText); + // TODO: Test multiple rounds of AES KDF } void TestSymmetricCipher::testTwofish256CbcEncryption() @@ -278,32 +226,30 @@ void TestSymmetricCipher::testTwofish256CbcEncryption() QByteArray::fromHex("957108025BFD57125B40057BC2DE4FE2"), QByteArray::fromHex("6F725C5950133F82EF021A94CADC8508")}; - SymmetricCipher cipher(SymmetricCipher::Twofish, SymmetricCipher::Cbc, SymmetricCipher::Encrypt); + SymmetricCipher cipher; for (int i = 0; i < keys.size(); ++i) { - QVERIFY(cipher.init(keys[i], ivs[i])); + QVERIFY(cipher.init(SymmetricCipher::Twofish_CBC, SymmetricCipher::Encrypt, keys[i], ivs[i])); QByteArray ptNext = plainTexts[i]; QByteArray ctPrev = ivs[i]; QByteArray ctCur; - QCOMPARE(cipher.blockSize(), 16); - bool ok = false; + QCOMPARE(cipher.blockSize(cipher.mode()), 16); for (int j = 0; j < 5000; ++j) { - ctCur = cipher.process(ptNext, &ok); - if (!ok) { + if (!cipher.process(ptNext)) { break; } + ctCur = ptNext; ptNext = ctPrev; ctPrev = ctCur; - ctCur = cipher.process(ptNext, &ok); - if (!ok) { + if (!cipher.process(ptNext)) { break; } + ctCur = ptNext; ptNext = ctPrev; ctPrev = ctCur; } - QVERIFY(ok); QCOMPARE(ctCur, cipherTexts[i]); } } @@ -334,30 +280,24 @@ void TestSymmetricCipher::testTwofish256CbcDecryption() QByteArray::fromHex("A792AC61E7110C434BC2BBCAB6E53CAE"), QByteArray::fromHex("4C81F5BDC1081170FF96F50B1F76A566")}; - SymmetricCipher cipher(SymmetricCipher::Twofish, SymmetricCipher::Cbc, SymmetricCipher::Decrypt); + SymmetricCipher cipher; for (int i = 0; i < keys.size(); ++i) { - cipher.init(keys[i], ivs[i]); + QVERIFY(cipher.init(SymmetricCipher::Twofish_CBC, SymmetricCipher::Decrypt, keys[i], ivs[i])); QByteArray ctNext = cipherTexts[i]; QByteArray ptCur; - QCOMPARE(cipher.blockSize(), 16); - bool ok = false; + QCOMPARE(cipher.blockSize(cipher.mode()), 16); for (int j = 0; j < 5000; ++j) { - ptCur = cipher.process(ctNext, &ok); - if (!ok) { + if (!cipher.process(ctNext)) { break; } - ctNext = ptCur; - ptCur = cipher.process(ctNext, &ok); - if (!ok) { + if (!cipher.process(ctNext)) { break; } - ctNext = ptCur; } - QVERIFY(ok); - QCOMPARE(ptCur, plainTexts[i]); + QCOMPARE(ctNext, plainTexts[i]); } } @@ -367,20 +307,22 @@ void TestSymmetricCipher::testSalsa20() QByteArray key = QByteArray::fromHex("F3F4F5F6F7F8F9FAFBFCFDFEFF000102030405060708090A0B0C0D0E0F101112"); QByteArray iv = QByteArray::fromHex("0000000000000000"); - bool ok; - SymmetricCipher cipher(SymmetricCipher::Salsa20, SymmetricCipher::Stream, SymmetricCipher::Encrypt); - QVERIFY(cipher.init(key, iv)); + SymmetricCipher cipher; + QVERIFY(cipher.init(SymmetricCipher::Salsa20, SymmetricCipher::Encrypt, key, iv)); QByteArray cipherTextA; for (int i = 0; i < 8; i++) { - cipherTextA.append(cipher.process(QByteArray(64, '\0'), &ok)); - QVERIFY(ok); + QByteArray data(64, '\0'); + QVERIFY(cipher.process(data)); + cipherTextA.append(data); } - cipher.reset(); - QByteArray cipherTextB = cipher.process(QByteArray(512, '\0'), &ok); - QVERIFY(ok); + // Re-initialize + QVERIFY(cipher.init(SymmetricCipher::Salsa20, SymmetricCipher::Encrypt, key, iv)); + + QByteArray cipherTextB(512, '\0'); + QVERIFY(cipher.process(cipherTextB)); cipher.reset(); QByteArray expectedCipherText1; @@ -421,50 +363,52 @@ void TestSymmetricCipher::testSalsa20() void TestSymmetricCipher::testChaCha20() { // https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04#section-7 - bool ok; - { QByteArray key = QByteArray::fromHex("0000000000000000000000000000000000000000000000000000000000000000"); QByteArray iv = QByteArray::fromHex("0000000000000000"); - SymmetricCipher cipher(SymmetricCipher::ChaCha20, SymmetricCipher::Stream, SymmetricCipher::Encrypt); - QVERIFY(cipher.init(key, iv)); - QCOMPARE(cipher.process(QByteArray(64, 0), &ok), + SymmetricCipher cipher; + QByteArray data(64, 0); + QVERIFY(cipher.init(SymmetricCipher::ChaCha20, SymmetricCipher::Encrypt, key, iv)); + QVERIFY(cipher.process(data)); + QCOMPARE(data, QByteArray::fromHex("76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7" "724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586")); - QVERIFY(ok); } { QByteArray key = QByteArray::fromHex("0000000000000000000000000000000000000000000000000000000000000001"); QByteArray iv = QByteArray::fromHex("0000000000000000"); - SymmetricCipher cipher(SymmetricCipher::ChaCha20, SymmetricCipher::Stream, SymmetricCipher::Encrypt); - QVERIFY(cipher.init(key, iv)); - QCOMPARE(cipher.process(QByteArray(64, 0), &ok), + SymmetricCipher cipher; + QByteArray data(64, 0); + QVERIFY(cipher.init(SymmetricCipher::ChaCha20, SymmetricCipher::Encrypt, key, iv)); + QVERIFY(cipher.process(data)); + QCOMPARE(data, QByteArray::fromHex("4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a" "5d1e7e20d42af2c53d792b1c43fea817e9ad275ae546963")); - QVERIFY(ok); } { QByteArray key = QByteArray::fromHex("0000000000000000000000000000000000000000000000000000000000000000"); QByteArray iv = QByteArray::fromHex("0000000000000001"); - SymmetricCipher cipher(SymmetricCipher::ChaCha20, SymmetricCipher::Stream, SymmetricCipher::Encrypt); - QVERIFY(cipher.init(key, iv)); - QCOMPARE(cipher.process(QByteArray(60, 0), &ok), + SymmetricCipher cipher; + QByteArray data(60, 0); + QVERIFY(cipher.init(SymmetricCipher::ChaCha20, SymmetricCipher::Encrypt, key, iv)); + QVERIFY(cipher.process(data)); + QCOMPARE(data, QByteArray::fromHex("de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031e85a050278a708452" "7214f73efc7fa5b5277062eb7a0433e445f41e3")); - QVERIFY(ok); } { QByteArray key = QByteArray::fromHex("0000000000000000000000000000000000000000000000000000000000000000"); QByteArray iv = QByteArray::fromHex("0100000000000000"); - SymmetricCipher cipher(SymmetricCipher::ChaCha20, SymmetricCipher::Stream, SymmetricCipher::Encrypt); - QVERIFY(cipher.init(key, iv)); - QCOMPARE(cipher.process(QByteArray(64, 0), &ok), + SymmetricCipher cipher; + QByteArray data(64, 0); + QVERIFY(cipher.init(SymmetricCipher::ChaCha20, SymmetricCipher::Encrypt, key, iv)); + QVERIFY(cipher.process(data)); + QCOMPARE(data, QByteArray::fromHex("ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca" "8ad6426194a88545ddc497a0b466e7d6bbdb0041b2f586b")); - QVERIFY(ok); } } @@ -477,8 +421,8 @@ void TestSymmetricCipher::testPadding() QBuffer buffer; buffer.open(QIODevice::ReadWrite); - SymmetricCipherStream streamEnc(&buffer, SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt); - QVERIFY(streamEnc.init(key, iv)); + SymmetricCipherStream streamEnc(&buffer); + QVERIFY(streamEnc.init(SymmetricCipher::Aes256_CBC, SymmetricCipher::Encrypt, key, iv)); streamEnc.open(QIODevice::WriteOnly); streamEnc.write(plainText); streamEnc.close(); @@ -486,8 +430,8 @@ void TestSymmetricCipher::testPadding() // make sure padding is written QCOMPARE(buffer.buffer().size(), 16); - SymmetricCipherStream streamDec(&buffer, SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Decrypt); - QVERIFY(streamDec.init(key, iv)); + SymmetricCipherStream streamDec(&buffer); + QVERIFY(streamDec.init(SymmetricCipher::Aes256_CBC, SymmetricCipher::Decrypt, key, iv)); streamDec.open(QIODevice::ReadOnly); QByteArray decrypted = streamDec.readAll(); QCOMPARE(decrypted, plainText); @@ -500,8 +444,8 @@ void TestSymmetricCipher::testStreamReset() QBuffer buffer; QVERIFY(buffer.open(QIODevice::WriteOnly)); - SymmetricCipherStream writer(&buffer, SymmetricCipher::Aes256, SymmetricCipher::Cbc, SymmetricCipher::Encrypt); - QVERIFY(writer.init(key, iv)); + SymmetricCipherStream writer(&buffer); + QVERIFY(writer.init(SymmetricCipher::Aes256_CBC, SymmetricCipher::Encrypt, key, iv)); QVERIFY(writer.open(QIODevice::WriteOnly)); QCOMPARE(writer.write(QByteArray(4, 'Z')), qint64(4)); // test if reset() and close() write only one block diff --git a/tests/TestSymmetricCipher.h b/tests/TestSymmetricCipher.h index 5eede0953..28ec432b6 100644 --- a/tests/TestSymmetricCipher.h +++ b/tests/TestSymmetricCipher.h @@ -27,13 +27,12 @@ class TestSymmetricCipher : public QObject private slots: void initTestCase(); - void testAlgorithmToCipher(); + void testCipherUuidToMode(); void testEncryptionDecryption_data(); void testEncryptionDecryption(); void testAesCbcPadding_data(); void testAesCbcPadding(); - void testInplaceEcb_data(); - void testInplaceEcb(); + void testAesKdf(); void testTwofish256CbcEncryption(); void testTwofish256CbcDecryption(); void testSalsa20(); diff --git a/tests/TestYkChallengeResponseKey.cpp b/tests/TestYkChallengeResponseKey.cpp index 05161544b..e165ca8ce 100644 --- a/tests/TestYkChallengeResponseKey.cpp +++ b/tests/TestYkChallengeResponseKey.cpp @@ -88,5 +88,5 @@ void TestYubiKeyChallengeResponse::testKeyChallenge() QByteArray ba("UnitTest"); QVERIFY(key->challenge(ba)); - QCOMPARE(key->rawKey().size(), 20); + QCOMPARE(key->rawKey().size(), 20UL); } diff --git a/tests/gui/TestGuiFdoSecrets.cpp b/tests/gui/TestGuiFdoSecrets.cpp index 555021274..1e8834e97 100644 --- a/tests/gui/TestGuiFdoSecrets.cpp +++ b/tests/gui/TestGuiFdoSecrets.cpp @@ -32,6 +32,7 @@ #include "core/Config.h" #include "core/Tools.h" #include "crypto/Crypto.h" +#include "crypto/Random.h" #include "gui/Application.h" #include "gui/DatabaseTabWidget.h" #include "gui/DatabaseWidget.h" @@ -160,49 +161,22 @@ void TestGuiFdoSecrets::initTestCase() VERIFY(m_plugin); m_mainWindow->show(); + auto key = QByteArray::fromHex("e407997e8b918419cf851cf3345358fdf" + "ffb9564a220ac9c3934efd277cea20d17" + "467ecdc56e817f75ac39501f38a4a04ff" + "64d627e16c09981c7ad876da255b61c8e" + "6a8408236c2a4523cfe6961c26dbdfc77" + "c1a27a5b425ca71a019e829fae32c0b42" + "0e1b3096b48bc2ce9ccab1d1ff13a5eb4" + "b263cee30bdb1a57af9bfa93f"); + m_clientCipher.reset(new DhIetf1024Sha256Aes128CbcPkcs7(key)); + // Load the NewDatabase.kdbx file into temporary storage QFile sourceDbFile(QStringLiteral(KEEPASSX_TEST_DATA_DIR "/NewDatabase.kdbx")); VERIFY(sourceDbFile.open(QIODevice::ReadOnly)); VERIFY(Tools::readAllFromDevice(&sourceDbFile, m_dbData)); sourceDbFile.close(); - // set keys for session encryption - m_serverPublic = MpiFromHex("e407997e8b918419cf851cf3345358fdf" - "ffb9564a220ac9c3934efd277cea20d17" - "467ecdc56e817f75ac39501f38a4a04ff" - "64d627e16c09981c7ad876da255b61c8e" - "6a8408236c2a4523cfe6961c26dbdfc77" - "c1a27a5b425ca71a019e829fae32c0b42" - "0e1b3096b48bc2ce9ccab1d1ff13a5eb4" - "b263cee30bdb1a57af9bfa93f"); - m_serverPrivate = MpiFromHex("013f4f3381ef0ca11c4c7363079577b56" - "99b238644e0aba47e24bdba6173590216" - "4f1e12dd0944800a373e090e63192f53b" - "93583e9a9e50bb9d792aafaa3a0f5ae77" - "de0c3423f5820848d88ee3bdd01c889f2" - "7af58a02f5b6693d422b9d189b300d7b1" - "be5076b5795cf8808c31e2e2898368d18" - "ab5c26b0ea3480c9aba8154cf"); - // use the same cipher to do the client side encryption, but exchange the position of client/server keys - m_cipher.reset(new DhIetf1024Sha256Aes128CbcPkcs7); - VERIFY(m_cipher->initialize(MpiFromBytes(MpiToBytes(m_serverPublic)), - MpiFromHex("30d18c6b328bac970c05bda6af2e708b9" - "d6bbbb6dc136c1a2d96e870fabc86ad74" - "1846a26a4197f32f65ea2e7580ad2afe3" - "dd5d6c1224b8368b0df2cd75d520a9ff9" - "7fe894cc7da71b7bd285b4633359c16c8" - "d341f822fa4f0fdf59b5d3448658c46a2" - "a86dbb14ff85823873f8a259ccc52bbb8" - "2b5a4c2a75447982553b42221"), - MpiFromHex("84aafe9c9f356f7762307f4d791acb59e" - "8e3fd562abdbb481d0587f8400ad6c51d" - "af561a1beb9a22c8cd4d2807367c5787b" - "2e06d631ccbb5194b6bb32211583ce688" - "f9c2cebc22a9e4d494d12ebdd570c61a1" - "62a94e88561d25ccd0415339d1f59e1b0" - "6bc6b6b5fde46e23b2410eb034be390d3" - "2407ec7ae90f0831f24afd5ac"))); - // set a fake dbus client all the time so we can freely access DBusMgr anywhere m_client.reset(new FakeClient(m_plugin->dbus().data())); m_plugin->dbus()->overrideClient(m_client); @@ -875,7 +849,7 @@ void TestGuiFdoSecrets::testItemCreate() // secrets { DBUS_GET(ss, item->GetSecret(QDBusObjectPath(sess->path()))); - auto decrypted = m_cipher->decrypt(ss.unmarshal(m_plugin->dbus())); + auto decrypted = m_clientCipher->decrypt(ss.unmarshal(m_plugin->dbus())); COMPARE(decrypted.value, QByteArrayLiteral("Password")); } @@ -1075,7 +1049,7 @@ void TestGuiFdoSecrets::testItemSecret() // plain text secret { DBUS_GET(encrypted, item->GetSecret(QDBusObjectPath(sess->path()))); - auto ss = m_cipher->decrypt(encrypted.unmarshal(m_plugin->dbus())); + auto ss = m_clientCipher->decrypt(encrypted.unmarshal(m_plugin->dbus())); COMPARE(ss.contentType, TEXT_PLAIN); COMPARE(ss.value, entry->password().toUtf8()); } @@ -1087,7 +1061,7 @@ void TestGuiFdoSecrets::testItemSecret() VERIFY(spyShowNotification.isValid()); DBUS_GET(encrypted, item->GetSecret(QDBusObjectPath(sess->path()))); - auto ss = m_cipher->decrypt(encrypted.unmarshal(m_plugin->dbus())); + auto ss = m_clientCipher->decrypt(encrypted.unmarshal(m_plugin->dbus())); COMPARE(ss.contentType, TEXT_PLAIN); COMPARE(ss.value, entry->password().toUtf8()); @@ -1107,7 +1081,7 @@ void TestGuiFdoSecrets::testItemSecret() ss.contentType = TEXT_PLAIN; ss.value = "NewPassword"; ss.session = QDBusObjectPath(sess->path()); - auto encrypted = m_cipher->encrypt(ss.unmarshal(m_plugin->dbus())); + auto encrypted = m_clientCipher->encrypt(ss.unmarshal(m_plugin->dbus())); DBUS_VERIFY(item->SetSecret(encrypted.marshal())); COMPARE(entry->password().toUtf8(), ss.value); @@ -1119,12 +1093,12 @@ void TestGuiFdoSecrets::testItemSecret() expected.contentType = APPLICATION_OCTET_STREAM; expected.value = QByteArrayLiteral("NewPasswordBinary"); expected.session = QDBusObjectPath(sess->path()); - DBUS_VERIFY(item->SetSecret(m_cipher->encrypt(expected.unmarshal(m_plugin->dbus())).marshal())); + DBUS_VERIFY(item->SetSecret(m_clientCipher->encrypt(expected.unmarshal(m_plugin->dbus())).marshal())); COMPARE(entry->password(), QStringLiteral("")); DBUS_GET(encrypted, item->GetSecret(QDBusObjectPath(sess->path()))); - auto ss = m_cipher->decrypt(encrypted.unmarshal(m_plugin->dbus())); + auto ss = m_clientCipher->decrypt(encrypted.unmarshal(m_plugin->dbus())); COMPARE(ss.contentType, expected.contentType); COMPARE(ss.value, expected.value); } @@ -1195,7 +1169,7 @@ void TestGuiFdoSecrets::testItemLockState() "text/plain", } .unmarshal(m_plugin->dbus()); - auto encrypted = m_cipher->encrypt(secret).marshal(); + auto encrypted = m_clientCipher->encrypt(secret).marshal(); // when access confirmation is disabled, item is unlocked when the collection is unlocked FdoSecrets::settings()->setConfirmAccessItem(false); @@ -1364,13 +1338,8 @@ QSharedPointer TestGuiFdoSecrets::openSession(const QSharedPointer return getProxy(sessPath); } else if (algo == DhIetf1024Sha256Aes128CbcPkcs7::Algorithm) { - - DhIetf1024Sha256Aes128CbcPkcs7::fixNextServerKeys(MpiFromBytes(MpiToBytes(m_serverPrivate)), - MpiFromBytes(MpiToBytes(m_serverPublic))); - - DBUS_GET2(output, sessPath, service->OpenSession(algo, QDBusVariant(m_cipher->m_publicKey))); - - COMPARE(qvariant_cast(output.variant()), MpiToBytes(m_serverPublic)); + DBUS_GET2(output, sessPath, service->OpenSession(algo, QDBusVariant(m_clientCipher->negotiationOutput()))); + m_clientCipher->updateClientPublicKey(output.variant().toByteArray()); return getProxy(sessPath); } QTest::qFail("Unsupported algorithm", __FILE__, __LINE__); @@ -1412,7 +1381,7 @@ QSharedPointer TestGuiFdoSecrets::createItem(const QSharedPointerpath()); ss.value = pass.toLocal8Bit(); ss.contentType = "plain/text"; - auto encrypted = m_cipher->encrypt(ss.unmarshal(m_plugin->dbus())).marshal(); + auto encrypted = m_clientCipher->encrypt(ss.unmarshal(m_plugin->dbus())).marshal(); DBUS_GET2(itemPath, promptPath, coll->CreateItem(properties, encrypted, replace)); diff --git a/tests/gui/TestGuiFdoSecrets.h b/tests/gui/TestGuiFdoSecrets.h index 8ded86586..e25472ae0 100644 --- a/tests/gui/TestGuiFdoSecrets.h +++ b/tests/gui/TestGuiFdoSecrets.h @@ -25,7 +25,6 @@ #include #include -#include "fdosecrets/GcryptMPI.h" #include "fdosecrets/dbus/DBusTypes.h" class MainWindow; @@ -139,10 +138,7 @@ private: QPointer m_plugin; QSharedPointer m_client; - // For DH session tests - GcryptMPI m_serverPrivate; - GcryptMPI m_serverPublic; - std::unique_ptr m_cipher; + QScopedPointer m_clientCipher; QByteArray m_dbData; QScopedPointer m_dbFile; diff --git a/tests/mock/MockChallengeResponseKey.cpp b/tests/mock/MockChallengeResponseKey.cpp index 1fa79dc84..3ca387bb3 100644 --- a/tests/mock/MockChallengeResponseKey.cpp +++ b/tests/mock/MockChallengeResponseKey.cpp @@ -27,13 +27,13 @@ MockChallengeResponseKey::~MockChallengeResponseKey() { } -QByteArray MockChallengeResponseKey::rawKey() const -{ - return m_challenge + m_secret; -} - bool MockChallengeResponseKey::challenge(const QByteArray& challenge) { m_challenge = challenge; + + auto response = m_challenge + m_secret; + m_key.resize(response.size()); + std::copy(response.begin(), response.end(), m_key.data()); + return true; } diff --git a/tests/mock/MockChallengeResponseKey.h b/tests/mock/MockChallengeResponseKey.h index a9aeeb412..0a3656bb5 100644 --- a/tests/mock/MockChallengeResponseKey.h +++ b/tests/mock/MockChallengeResponseKey.h @@ -30,7 +30,6 @@ public: explicit MockChallengeResponseKey(const QByteArray& secret); Q_DISABLE_COPY(MockChallengeResponseKey); ~MockChallengeResponseKey() override; - QByteArray rawKey() const override; bool challenge(const QByteArray& challenge) override; private: diff --git a/tests/stub/TestRandom.cpp b/tests/stub/TestRandom.cpp deleted file mode 100644 index d33b1c9b8..000000000 --- a/tests/stub/TestRandom.cpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (C) 2018 KeePassXC Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "TestRandom.h" -#include "TestGlobal.h" - -RandomBackendPreset::RandomBackendPreset() - : m_bytesIndex(0) -{ -} - -void RandomBackendPreset::randomize(void* data, int len) -{ - QVERIFY(len <= (m_nextBytes.size() - m_bytesIndex)); - - char* charData = reinterpret_cast(data); - - for (int i = 0; i < len; i++) { - charData[i] = m_nextBytes[m_bytesIndex + i]; - } - - m_bytesIndex += len; -} - -void RandomBackendPreset::setNextBytes(const QByteArray& nextBytes) -{ - m_nextBytes = nextBytes; - m_bytesIndex = 0; -} - -void TestRandom::setup(RandomBackend* backend) -{ - Random::setInstance(backend); -} - -void TestRandom::teardown() -{ - Random::resetInstance(); -} - -void RandomBackendNull::randomize(void* data, int len) -{ - char* charData = reinterpret_cast(data); - - for (int i = 0; i < len; i++) { - charData[i] = '\0'; - } -} diff --git a/tests/stub/TestRandom.h b/tests/stub/TestRandom.h deleted file mode 100644 index ec5afb59d..000000000 --- a/tests/stub/TestRandom.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (C) 2018 KeePassXC Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef KEEPASSXC_TESTRANDOM_H -#define KEEPASSXC_TESTRANDOM_H - -#include "crypto/Random.h" - -class RandomBackendPreset : public RandomBackend -{ -public: - RandomBackendPreset(); - void randomize(void* data, int len) override; - void setNextBytes(const QByteArray& nextBytes); - -private: - QByteArray m_nextBytes; - int m_bytesIndex; -}; - -class RandomBackendNull : public RandomBackend -{ -public: - void randomize(void* data, int len) override; -}; - -class TestRandom : public Random -{ -public: - static void setup(RandomBackend* backend); - static void teardown(); -}; - -#endif // KEEPASSXC_TESTRANDOM_H