mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
KeeShare: Remove checking signed container
* Remove QuaZip dependency in favor of minizip * Remove signature checks, but maintain signatures for backwards compatibility * Remove UI components related to certificates except for personal certificate for backwards compatibility * Default to unsigned containers (*.kdbx)
This commit is contained in:
parent
c88d8c870f
commit
12990e59ad
@ -50,7 +50,7 @@ option(WITH_XC_NETWORKING "Include networking code (e.g. for downloading website
|
||||
option(WITH_XC_BROWSER "Include browser integration with keepassxc-browser." OFF)
|
||||
option(WITH_XC_YUBIKEY "Include YubiKey support." OFF)
|
||||
option(WITH_XC_SSHAGENT "Include SSH agent support." OFF)
|
||||
option(WITH_XC_KEESHARE "Sharing integration with KeeShare (requires quazip5 for secure containers)" OFF)
|
||||
option(WITH_XC_KEESHARE "Sharing integration with KeeShare" OFF)
|
||||
option(WITH_XC_UPDATECHECK "Include automatic update checks; disable for controlled distributions" ON)
|
||||
if(UNIX AND NOT APPLE)
|
||||
option(WITH_XC_FDOSECRETS "Implement freedesktop.org Secret Storage Spec server side API." OFF)
|
||||
|
@ -102,7 +102,6 @@ These steps place the compiled KeePassXC binary inside the `./build/src/` direct
|
||||
-DWITH_XC_TOUCHID=[ON|OFF] (macOS Only) Enable/Disable Touch ID unlock (default:OFF)
|
||||
-DWITH_XC_FDOSECRETS=[ON|OFF] (Linux Only) Enable/Disable Freedesktop.org Secrets Service support (default:OFF)
|
||||
-DWITH_XC_KEESHARE=[ON|OFF] Enable/Disable KeeShare group synchronization extension (default: OFF)
|
||||
-DWITH_XC_KEESHARE_SECURE=[ON|OFF] Enable/Disable KeeShare signed containers, requires libquazip5 (default: OFF)
|
||||
-DWITH_XC_ALL=[ON|OFF] Enable/Disable compiling all plugins above (default: OFF)
|
||||
|
||||
-DWITH_XC_UPDATECHECK=[ON|OFF] Enable/Disable automatic updating checking (requires WITH_XC_NETWORKING) (default: ON)
|
||||
|
9
cmake/FindMinizip.cmake
Normal file
9
cmake/FindMinizip.cmake
Normal file
@ -0,0 +1,9 @@
|
||||
# MINIZIP_FOUND - Minizip library was found
|
||||
# MINIZIP_INCLUDE_DIR - Path to Minizip include dir
|
||||
# MINIZIP_LIBRARIES - List of Minizip libraries
|
||||
|
||||
find_library(MINIZIP_LIBRARIES NAMES minizip libminizip)
|
||||
find_path(MINIZIP_INCLUDE_DIR zip.h PATH_SUFFIXES minizip)
|
||||
|
||||
include(FindPackageHandleStandardArgs)
|
||||
find_package_handle_standard_args(Minizip DEFAULT_MSG MINIZIP_LIBRARIES MINIZIP_INCLUDE_DIR)
|
@ -1,24 +0,0 @@
|
||||
# QUAZIP_FOUND - QuaZip library was found
|
||||
# QUAZIP_INCLUDE_DIR - Path to QuaZip include dir
|
||||
# QUAZIP_INCLUDE_DIRS - Path to QuaZip and zlib include dir (combined from QUAZIP_INCLUDE_DIR + ZLIB_INCLUDE_DIR)
|
||||
# QUAZIP_LIBRARIES - List of QuaZip libraries
|
||||
# QUAZIP_ZLIB_INCLUDE_DIR - The include dir of zlib headers
|
||||
|
||||
if(WIN32)
|
||||
find_library(QUAZIP_LIBRARIES libquazip5)
|
||||
find_path(QUAZIP_INCLUDE_DIR quazip.h PATH_SUFFIXES quazip5)
|
||||
find_path(QUAZIP_ZLIB_INCLUDE_DIR zlib.h)
|
||||
else()
|
||||
find_library(QUAZIP_LIBRARIES
|
||||
NAMES quazip5 quazip
|
||||
PATHS /usr/lib /usr/lib64 /usr/local/lib
|
||||
)
|
||||
find_path(QUAZIP_INCLUDE_DIR quazip.h
|
||||
PATHS /usr/include /usr/local/include
|
||||
PATH_SUFFIXES quazip5 quazip
|
||||
)
|
||||
find_path(QUAZIP_ZLIB_INCLUDE_DIR zlib.h PATHS /usr/include /usr/local/include)
|
||||
endif()
|
||||
include(FindPackageHandleStandardArgs)
|
||||
set(QUAZIP_INCLUDE_DIRS ${QUAZIP_INCLUDE_DIR} ${QUAZIP_ZLIB_INCLUDE_DIR})
|
||||
find_package_handle_standard_args(QUAZIP DEFAULT_MSG QUAZIP_LIBRARIES QUAZIP_INCLUDE_DIR QUAZIP_ZLIB_INCLUDE_DIR QUAZIP_INCLUDE_DIRS)
|
@ -3083,10 +3083,6 @@ Would you like to correct it?</source>
|
||||
<source>Inactive</source>
|
||||
<translation>Inactive</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeeShare unsigned container</source>
|
||||
<translation>KeeShare unsigned container</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeeShare signed container</source>
|
||||
<translation>KeeShare signed container</translation>
|
||||
@ -3173,6 +3169,10 @@ Supported extensions are: %1.</source>
|
||||
<source>Browse…</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeeShare container</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>EditGroupWidgetMain</name>
|
||||
@ -6742,18 +6742,6 @@ Kernel: %3 %4</source>
|
||||
<source>Auto-Type</source>
|
||||
<translation type="unfinished">Auto-Type</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeeShare (signed and unsigned sharing)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeeShare (only signed sharing)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeeShare (only unsigned sharing)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>YubiKey</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@ -7641,6 +7629,10 @@ Please consider generating a new key file.</source>
|
||||
<source>%1 characters</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeeShare</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QtIOCompressor</name>
|
||||
@ -8243,91 +8235,14 @@ Please consider generating a new key file.</source>
|
||||
<source>Fingerprint:</source>
|
||||
<translation>Fingerprint:</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Signer</source>
|
||||
<translation>Signer</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Generate</source>
|
||||
<translation>Generate</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Import</source>
|
||||
<translation>Import</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export</source>
|
||||
<translation>Export</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Imported certificates</source>
|
||||
<translation>Imported certificates</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trust</source>
|
||||
<translation>Trust</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ask</source>
|
||||
<translation>Ask</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Untrust</source>
|
||||
<translation>Untrust</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove</source>
|
||||
<translation>Remove</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Path</source>
|
||||
<translation>Path</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Status</source>
|
||||
<translation>Status</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Fingerprint</source>
|
||||
<translation>Fingerprint</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trusted</source>
|
||||
<translation>Trusted</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Untrusted</source>
|
||||
<translation>Untrusted</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unknown</source>
|
||||
<translation>Unknown</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>key.share</source>
|
||||
<comment>Filetype for KeeShare key</comment>
|
||||
<translation>key.share</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeeShare key file</source>
|
||||
<translation>KeeShare key file</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>All files</source>
|
||||
<translation>All files</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Select path</source>
|
||||
<translation>Select path</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Exporting changed certificate</source>
|
||||
<translation>Exporting changed certificate</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The exported certificate is not the same as the one in use. Do you want to export the current certificate?</source>
|
||||
<translation>The exported certificate is not the same as the one in use. Do you want to export the current certificate?</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Signer:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
@ -8352,132 +8267,18 @@ Please consider generating a new key file.</source>
|
||||
<source>Generate new certificate</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Import existing certificate</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Export own certificate</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Known shares</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Trust selected certificate</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Ask whether to trust the selected certificate every time</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Untrust selected certificate</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Remove selected certificate</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ShareExport</name>
|
||||
<message>
|
||||
<source>Overwriting signed share container is not supported - export prevented</source>
|
||||
<translation type="unfinished">Overwriting signed share container is not supported - export prevented</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Could not write export container (%1)</source>
|
||||
<translation type="unfinished">Could not write export container (%1)</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Could not embed signature: Could not open file to write (%1)</source>
|
||||
<source>Could not write export container.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Could not embed signature: Could not write file (%1)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Could not embed database: Could not open file to write (%1)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Could not embed database: Could not write file (%1)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Overwriting unsigned share container is not supported - export prevented</source>
|
||||
<translation type="unfinished">Overwriting unsigned share container is not supported - export prevented</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Could not write export container</source>
|
||||
<translation type="unfinished">Could not write export container</translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>ShareImport</name>
|
||||
<message>
|
||||
<source>Not this time</source>
|
||||
<translation type="unfinished">Not this time</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Never</source>
|
||||
<translation type="unfinished">Never</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Always</source>
|
||||
<translation type="unfinished">Always</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Just this time</source>
|
||||
<translation type="unfinished">Just this time</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Signed share container are not supported - import prevented</source>
|
||||
<translation type="unfinished">Signed share container are not supported - import prevented</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>File is not readable</source>
|
||||
<translation type="unfinished">File is not readable</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Invalid sharing container</source>
|
||||
<translation type="unfinished">Invalid sharing container</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Untrusted import prevented</source>
|
||||
<translation type="unfinished">Untrusted import prevented</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Successful signed import</source>
|
||||
<translation type="unfinished">Successful signed import</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Unsigned share container are not supported - import prevented</source>
|
||||
<translation type="unfinished">Unsigned share container are not supported - import prevented</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Successful unsigned import</source>
|
||||
<translation type="unfinished">Successful unsigned import</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>File does not exist</source>
|
||||
<translation type="unfinished">File does not exist</translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>KeeShare Import</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>The source of the shared container cannot be verified because it is not signed. Do you really want to import from %1?</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<source>Do you want to trust %1 with certificate fingerprint:
|
||||
%2
|
||||
%3</source>
|
||||
<source>Successful import</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
|
@ -53,7 +53,6 @@ parts:
|
||||
- -DKEEPASSXC_BUILD_TYPE=Release
|
||||
- -DWITH_TESTS=OFF
|
||||
- -DWITH_XC_ALL=ON
|
||||
- -DWITH_XC_KEESHARE_SECURE=ON
|
||||
build-packages:
|
||||
- g++
|
||||
- libgcrypt20-dev
|
||||
@ -71,7 +70,7 @@ parts:
|
||||
- libsodium-dev
|
||||
- libargon2-0-dev
|
||||
- libqrencode-dev
|
||||
- libquazip5-dev
|
||||
- libminizip-dev
|
||||
- asciidoctor
|
||||
stage-packages:
|
||||
- dbus
|
||||
|
@ -226,7 +226,7 @@ add_feature_info(Auto-Type WITH_XC_AUTOTYPE "Automatic password typing")
|
||||
add_feature_info(Networking WITH_XC_NETWORKING "Compile KeePassXC with network access code (e.g. for downloading website icons)")
|
||||
add_feature_info(KeePassXC-Browser WITH_XC_BROWSER "Browser integration with KeePassXC-Browser")
|
||||
add_feature_info(SSHAgent WITH_XC_SSHAGENT "SSH agent integration compatible with KeeAgent")
|
||||
add_feature_info(KeeShare WITH_XC_KEESHARE "Sharing integration with KeeShare (requires quazip5 for secure containers)")
|
||||
add_feature_info(KeeShare WITH_XC_KEESHARE "Sharing integration with KeeShare")
|
||||
add_feature_info(YubiKey WITH_XC_YUBIKEY "YubiKey HMAC-SHA1 challenge-response")
|
||||
add_feature_info(UpdateCheck WITH_XC_UPDATECHECK "Automatic update checking")
|
||||
if(UNIX AND NOT APPLE)
|
||||
|
@ -18,8 +18,6 @@
|
||||
#cmakedefine WITH_XC_YUBIKEY
|
||||
#cmakedefine WITH_XC_SSHAGENT
|
||||
#cmakedefine WITH_XC_KEESHARE
|
||||
#cmakedefine WITH_XC_KEESHARE_INSECURE
|
||||
#cmakedefine WITH_XC_KEESHARE_SECURE
|
||||
#cmakedefine WITH_XC_UPDATECHECK
|
||||
#cmakedefine WITH_XC_TOUCHID
|
||||
#cmakedefine WITH_XC_FDOSECRETS
|
||||
|
@ -92,12 +92,8 @@ namespace Tools
|
||||
#ifdef WITH_XC_SSHAGENT
|
||||
extensions += "\n- " + QObject::tr("SSH Agent");
|
||||
#endif
|
||||
#if defined(WITH_XC_KEESHARE_SECURE) && defined(WITH_XC_KEESHARE_INSECURE)
|
||||
extensions += "\n- " + QObject::tr("KeeShare (signed and unsigned sharing)");
|
||||
#elif defined(WITH_XC_KEESHARE_SECURE)
|
||||
extensions += "\n- " + QObject::tr("KeeShare (only signed sharing)");
|
||||
#elif defined(WITH_XC_KEESHARE_INSECURE)
|
||||
extensions += "\n- " + QObject::tr("KeeShare (only unsigned sharing)");
|
||||
#ifdef WITH_XC_KEESHARE
|
||||
extensions += "\n- " + QObject::tr("KeeShare");
|
||||
#endif
|
||||
#ifdef WITH_XC_YUBIKEY
|
||||
extensions += "\n- " + QObject::tr("YubiKey");
|
||||
|
@ -1,8 +1,4 @@
|
||||
if(WITH_XC_KEESHARE)
|
||||
set(WITH_XC_KEESHARE_INSECURE ON PARENT_SCOPE)
|
||||
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(keeshare_SOURCES
|
||||
SettingsPageKeeShare.cpp
|
||||
SettingsWidgetKeeShare.cpp
|
||||
@ -15,23 +11,12 @@ if(WITH_XC_KEESHARE)
|
||||
ShareImport.cpp
|
||||
ShareExport.cpp
|
||||
ShareObserver.cpp
|
||||
Signature.cpp
|
||||
)
|
||||
)
|
||||
|
||||
find_package(Minizip REQUIRED)
|
||||
|
||||
add_library(keeshare STATIC ${keeshare_SOURCES})
|
||||
target_link_libraries(keeshare PUBLIC Qt5::Core Qt5::Widgets ${BOTAN2_LIBRARIES})
|
||||
|
||||
# Try to find libquazip5, if found, enable secure sharing
|
||||
find_package(QuaZip)
|
||||
if(QUAZIP_FOUND)
|
||||
set(WITH_XC_KEESHARE_SECURE ON PARENT_SCOPE)
|
||||
target_include_directories(keeshare SYSTEM PRIVATE ${QUAZIP_INCLUDE_DIR})
|
||||
target_link_libraries(keeshare PRIVATE ${QUAZIP_LIBRARIES})
|
||||
else()
|
||||
set(WITH_XC_KEESHARE_SECURE OFF PARENT_SCOPE)
|
||||
message(STATUS "KeeShare: Secure container support is DISABLED; quazip library not found")
|
||||
endif()
|
||||
else(WITH_XC_KEESHARE)
|
||||
set(WITH_XC_KEESHARE_INSECURE OFF PARENT_SCOPE)
|
||||
set(WITH_XC_KEESHARE_SECURE OFF PARENT_SCOPE)
|
||||
target_link_libraries(keeshare PUBLIC Qt5::Core Qt5::Widgets ${BOTAN2_LIBRARIES} ${ZLIB_LIBRARIES} PRIVATE ${MINIZIP_LIBRARIES})
|
||||
target_include_directories(keeshare SYSTEM PRIVATE ${MINIZIP_INCLUDE_DIR})
|
||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif(WITH_XC_KEESHARE)
|
||||
|
@ -53,7 +53,13 @@ void KeeShare::init(QObject* parent)
|
||||
|
||||
KeeShareSettings::Own KeeShare::own()
|
||||
{
|
||||
return KeeShareSettings::Own::deserialize(config()->get(Config::KeeShare_Own).toString());
|
||||
// Read existing own certificate or generate a new one if none available
|
||||
auto own = KeeShareSettings::Own::deserialize(config()->get(Config::KeeShare_Own).toString());
|
||||
if (own.key.isNull()) {
|
||||
own = KeeShareSettings::Own::generate();
|
||||
setOwn(own);
|
||||
}
|
||||
return own;
|
||||
}
|
||||
|
||||
KeeShareSettings::Active KeeShare::active()
|
||||
@ -61,16 +67,6 @@ KeeShareSettings::Active KeeShare::active()
|
||||
return KeeShareSettings::Active::deserialize(config()->get(Config::KeeShare_Active).toString());
|
||||
}
|
||||
|
||||
KeeShareSettings::Foreign KeeShare::foreign()
|
||||
{
|
||||
return KeeShareSettings::Foreign::deserialize(config()->get(Config::KeeShare_Foreign).toString());
|
||||
}
|
||||
|
||||
void KeeShare::setForeign(const KeeShareSettings::Foreign& foreign)
|
||||
{
|
||||
config()->set(Config::KeeShare_Foreign, KeeShareSettings::Foreign::serialize(foreign));
|
||||
}
|
||||
|
||||
void KeeShare::setActive(const KeeShareSettings::Active& active)
|
||||
{
|
||||
config()->set(Config::KeeShare_Active, KeeShareSettings::Active::serialize(active));
|
||||
@ -117,16 +113,6 @@ void KeeShare::setReferenceTo(Group* group, const KeeShareSettings::Reference& r
|
||||
bool KeeShare::isEnabled(const Group* group)
|
||||
{
|
||||
const auto reference = KeeShare::referenceOf(group);
|
||||
#if !defined(WITH_XC_KEESHARE_SECURE)
|
||||
if (reference.path.endsWith(signedContainerFileType(), Qt::CaseInsensitive)) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
#if !defined(WITH_XC_KEESHARE_INSECURE)
|
||||
if (reference.path.endsWith(unsignedContainerFileType(), Qt::CaseInsensitive)) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
const auto active = KeeShare::active();
|
||||
return (reference.isImporting() && active.in) || (reference.isExporting() && active.out);
|
||||
}
|
||||
|
@ -55,12 +55,11 @@ public:
|
||||
static QString sharingLabel(const Group* group);
|
||||
|
||||
static KeeShareSettings::Own own();
|
||||
static KeeShareSettings::Active active();
|
||||
static KeeShareSettings::Foreign foreign();
|
||||
static void setForeign(const KeeShareSettings::Foreign& foreign);
|
||||
static void setActive(const KeeShareSettings::Active& active);
|
||||
static void setOwn(const KeeShareSettings::Own& own);
|
||||
|
||||
static KeeShareSettings::Active active();
|
||||
static void setActive(const KeeShareSettings::Active& active);
|
||||
|
||||
static KeeShareSettings::Reference referenceOf(const Group* group);
|
||||
static void setReferenceTo(Group* group, const KeeShareSettings::Reference& reference);
|
||||
static QString referenceTypeLabel(const KeeShareSettings::Reference& reference);
|
||||
|
@ -23,8 +23,8 @@
|
||||
#include "core/Metadata.h"
|
||||
#include "crypto/Random.h"
|
||||
#include "gui/DatabaseIcons.h"
|
||||
#include "keeshare/Signature.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QTextCodec>
|
||||
#include <QXmlStreamWriter>
|
||||
|
||||
@ -261,82 +261,6 @@ namespace KeeShareSettings
|
||||
return own;
|
||||
}
|
||||
|
||||
bool ScopedCertificate::operator==(const ScopedCertificate& other) const
|
||||
{
|
||||
return trust == other.trust && path == other.path && certificate == other.certificate;
|
||||
}
|
||||
|
||||
bool ScopedCertificate::operator!=(const ScopedCertificate& other) const
|
||||
{
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
void ScopedCertificate::serialize(QXmlStreamWriter& writer, const ScopedCertificate& scopedCertificate)
|
||||
{
|
||||
writer.writeAttribute("Path", scopedCertificate.path);
|
||||
QString trust = "Ask";
|
||||
if (scopedCertificate.trust == KeeShareSettings::Trust::Trusted) {
|
||||
trust = "Trusted";
|
||||
}
|
||||
if (scopedCertificate.trust == KeeShareSettings::Trust::Untrusted) {
|
||||
trust = "Untrusted";
|
||||
}
|
||||
writer.writeAttribute("Trust", trust);
|
||||
Certificate::serialize(writer, scopedCertificate.certificate);
|
||||
}
|
||||
|
||||
ScopedCertificate ScopedCertificate::deserialize(QXmlStreamReader& reader)
|
||||
{
|
||||
ScopedCertificate scopedCertificate;
|
||||
scopedCertificate.path = reader.attributes().value("Path").toString();
|
||||
scopedCertificate.trust = KeeShareSettings::Trust::Ask;
|
||||
auto trust = reader.attributes().value("Trust").toString();
|
||||
if (trust.compare("Trusted", Qt::CaseInsensitive) == 0) {
|
||||
scopedCertificate.trust = KeeShareSettings::Trust::Trusted;
|
||||
}
|
||||
if (trust.compare("Untrusted", Qt::CaseInsensitive) == 0) {
|
||||
scopedCertificate.trust = KeeShareSettings::Trust::Untrusted;
|
||||
}
|
||||
scopedCertificate.certificate = Certificate::deserialize(reader);
|
||||
return scopedCertificate;
|
||||
}
|
||||
|
||||
QString Foreign::serialize(const Foreign& foreign)
|
||||
{
|
||||
return xmlSerialize([&](QXmlStreamWriter& writer) {
|
||||
writer.writeStartElement("Foreign");
|
||||
for (const ScopedCertificate& scopedCertificate : foreign.certificates) {
|
||||
writer.writeStartElement("Certificate");
|
||||
ScopedCertificate::serialize(writer, scopedCertificate);
|
||||
writer.writeEndElement();
|
||||
}
|
||||
writer.writeEndElement();
|
||||
});
|
||||
}
|
||||
|
||||
Foreign Foreign::deserialize(const QString& raw)
|
||||
{
|
||||
Foreign foreign;
|
||||
xmlDeserialize(raw, [&](QXmlStreamReader& reader) {
|
||||
while (!reader.error() && reader.readNextStartElement()) {
|
||||
if (reader.name() == "Foreign") {
|
||||
while (!reader.error() && reader.readNextStartElement()) {
|
||||
if (reader.name() == "Certificate") {
|
||||
foreign.certificates << ScopedCertificate::deserialize(reader);
|
||||
} else {
|
||||
qWarning("Unknown Certificates element %s", qPrintable(reader.name().toString()));
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
qWarning("Unknown KeeShareSettings element %s", qPrintable(reader.name().toString()));
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
});
|
||||
return foreign;
|
||||
}
|
||||
|
||||
Reference::Reference()
|
||||
: type(Inactive)
|
||||
, uuid(QUuid::createUuid())
|
||||
@ -433,31 +357,38 @@ namespace KeeShareSettings
|
||||
|
||||
QString Sign::serialize(const Sign& sign)
|
||||
{
|
||||
if (sign.certificate.isNull()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Extract RSA key data to serialize an ssh-rsa public key.
|
||||
// ssh-rsa keys are currently not built into Botan
|
||||
const auto rsaKey = static_cast<Botan::RSA_PrivateKey*>(sign.certificate.key.data());
|
||||
|
||||
std::vector<uint8_t> rsaE(rsaKey->get_e().bytes());
|
||||
rsaKey->get_e().binary_encode(rsaE.data());
|
||||
|
||||
std::vector<uint8_t> rsaN(rsaKey->get_n().bytes());
|
||||
rsaKey->get_n().binary_encode(rsaN.data());
|
||||
|
||||
QByteArray rsaKeySerialized;
|
||||
QDataStream stream(&rsaKeySerialized, QIODevice::WriteOnly);
|
||||
stream.writeBytes("ssh-rsa", 7);
|
||||
stream.writeBytes(reinterpret_cast<const char*>(rsaE.data()), rsaE.size());
|
||||
stream.writeBytes(reinterpret_cast<const char*>(rsaN.data()), rsaN.size());
|
||||
|
||||
return xmlSerialize([&](QXmlStreamWriter& writer) {
|
||||
writer.writeStartElement("Signature");
|
||||
writer.writeCharacters(sign.signature);
|
||||
writer.writeEndElement();
|
||||
writer.writeStartElement("Certificate");
|
||||
Certificate::serialize(writer, sign.certificate);
|
||||
writer.writeStartElement("Signer");
|
||||
writer.writeCharacters(sign.certificate.signer);
|
||||
writer.writeEndElement();
|
||||
writer.writeStartElement("Key");
|
||||
writer.writeCharacters(rsaKeySerialized.toBase64());
|
||||
writer.writeEndElement();
|
||||
writer.writeEndElement();
|
||||
});
|
||||
}
|
||||
|
||||
Sign Sign::deserialize(const QString& raw)
|
||||
{
|
||||
Sign sign;
|
||||
xmlDeserialize(raw, [&](QXmlStreamReader& reader) {
|
||||
while (!reader.error() && reader.readNextStartElement()) {
|
||||
if (reader.name() == "Signature") {
|
||||
sign.signature = reader.readElementText();
|
||||
} else if (reader.name() == "Certificate") {
|
||||
sign.certificate = KeeShareSettings::Certificate::deserialize(reader);
|
||||
} else {
|
||||
qWarning("Unknown Sign element %s", qPrintable(reader.name().toString()));
|
||||
reader.skipCurrentElement();
|
||||
}
|
||||
}
|
||||
});
|
||||
return sign;
|
||||
}
|
||||
} // namespace KeeShareSettings
|
||||
|
@ -55,7 +55,6 @@ namespace KeeShareSettings
|
||||
bool operator!=(const Key& other) const;
|
||||
|
||||
bool isNull() const;
|
||||
QString privateKey() const;
|
||||
|
||||
static void serialize(QXmlStreamWriter& writer, const Key& key);
|
||||
static Key deserialize(QXmlStreamReader& reader);
|
||||
@ -99,42 +98,6 @@ namespace KeeShareSettings
|
||||
static Own generate();
|
||||
};
|
||||
|
||||
enum class Trust
|
||||
{
|
||||
Ask,
|
||||
Untrusted,
|
||||
Trusted
|
||||
};
|
||||
struct ScopedCertificate
|
||||
{
|
||||
QString path;
|
||||
Certificate certificate;
|
||||
Trust trust;
|
||||
|
||||
bool operator==(const ScopedCertificate& other) const;
|
||||
bool operator!=(const ScopedCertificate& other) const;
|
||||
|
||||
bool isUnknown() const
|
||||
{
|
||||
return certificate.isNull();
|
||||
}
|
||||
bool isKnown() const
|
||||
{
|
||||
return !certificate.isNull();
|
||||
}
|
||||
|
||||
static void serialize(QXmlStreamWriter& writer, const ScopedCertificate& certificate);
|
||||
static ScopedCertificate deserialize(QXmlStreamReader& reader);
|
||||
};
|
||||
|
||||
struct Foreign
|
||||
{
|
||||
QList<ScopedCertificate> certificates;
|
||||
|
||||
static QString serialize(const Foreign& foreign);
|
||||
static Foreign deserialize(const QString& raw);
|
||||
};
|
||||
|
||||
struct Sign
|
||||
{
|
||||
QString signature;
|
||||
@ -146,7 +109,6 @@ namespace KeeShareSettings
|
||||
}
|
||||
|
||||
static QString serialize(const Sign& sign);
|
||||
static Sign deserialize(const QString& raw);
|
||||
};
|
||||
|
||||
enum TypeFlag
|
||||
|
@ -33,21 +33,8 @@ SettingsWidgetKeeShare::SettingsWidgetKeeShare(QWidget* parent)
|
||||
{
|
||||
m_ui->setupUi(this);
|
||||
|
||||
#if !defined(WITH_XC_KEESHARE_SECURE)
|
||||
// Setting does not help the user of Version without signed export
|
||||
m_ui->ownCertificateGroupBox->setVisible(false);
|
||||
#endif
|
||||
|
||||
connect(m_ui->ownCertificateSignerEdit, SIGNAL(textChanged(QString)), SLOT(setVerificationExporter(QString)));
|
||||
|
||||
connect(m_ui->generateOwnCerticateButton, SIGNAL(clicked(bool)), SLOT(generateCertificate()));
|
||||
connect(m_ui->importOwnCertificateButton, SIGNAL(clicked(bool)), SLOT(importCertificate()));
|
||||
connect(m_ui->exportOwnCertificateButton, SIGNAL(clicked(bool)), SLOT(exportCertificate()));
|
||||
|
||||
connect(m_ui->trustImportedCertificateButton, SIGNAL(clicked(bool)), SLOT(trustSelectedCertificates()));
|
||||
connect(m_ui->askImportedCertificateButton, SIGNAL(clicked(bool)), SLOT(askSelectedCertificates()));
|
||||
connect(m_ui->untrustImportedCertificateButton, SIGNAL(clicked(bool)), SLOT(untrustSelectedCertificates()));
|
||||
connect(m_ui->removeImportedCertificateButton, SIGNAL(clicked(bool)), SLOT(removeSelectedCertificates()));
|
||||
}
|
||||
|
||||
SettingsWidgetKeeShare::~SettingsWidgetKeeShare()
|
||||
@ -62,46 +49,6 @@ void SettingsWidgetKeeShare::loadSettings()
|
||||
|
||||
m_own = KeeShare::own();
|
||||
updateOwnCertificate();
|
||||
|
||||
m_foreign = KeeShare::foreign();
|
||||
updateForeignCertificates();
|
||||
}
|
||||
|
||||
void SettingsWidgetKeeShare::updateForeignCertificates()
|
||||
{
|
||||
auto headers = QStringList() << tr("Path") << tr("Status");
|
||||
#if defined(WITH_XC_KEESHARE_SECURE)
|
||||
headers << tr("Signer") << tr("Fingerprint");
|
||||
#endif
|
||||
|
||||
m_importedCertificateModel.reset(new QStandardItemModel());
|
||||
m_importedCertificateModel->setHorizontalHeaderLabels(headers);
|
||||
|
||||
for (const auto& scopedCertificate : m_foreign.certificates) {
|
||||
QList<QStandardItem*> 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)
|
||||
items << new QStandardItem(scopedCertificate.isKnown() ? scopedCertificate.certificate.signer : tr("Unknown"));
|
||||
items << new QStandardItem(scopedCertificate.certificate.fingerprint());
|
||||
#endif
|
||||
m_importedCertificateModel->appendRow(items);
|
||||
}
|
||||
|
||||
m_ui->importedCertificateTableView->setModel(m_importedCertificateModel.data());
|
||||
m_ui->importedCertificateTableView->resizeColumnsToContents();
|
||||
}
|
||||
|
||||
void SettingsWidgetKeeShare::updateOwnCertificate()
|
||||
@ -119,7 +66,6 @@ void SettingsWidgetKeeShare::saveSettings()
|
||||
// store changes to the settings in a temporary object and check on the final values
|
||||
// of this object (similar scheme to Entry) - this way we could validate the settings before save
|
||||
KeeShare::setOwn(m_own);
|
||||
KeeShare::setForeign(m_foreign);
|
||||
KeeShare::setActive(active);
|
||||
|
||||
config()->set(Config::KeeShare_QuietSuccess, m_ui->quietSuccessCheckBox->isChecked());
|
||||
@ -137,99 +83,3 @@ void SettingsWidgetKeeShare::generateCertificate()
|
||||
m_ui->ownCertificateSignerEdit->setText(m_own.certificate.signer);
|
||||
m_ui->ownCertificateFingerprintEdit->setText(m_own.certificate.fingerprint());
|
||||
}
|
||||
|
||||
void SettingsWidgetKeeShare::importCertificate()
|
||||
{
|
||||
auto defaultDirPath = FileDialog::getLastDir("keeshare_key");
|
||||
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 = fileDialog()->getOpenFileName(this, tr("Select path"), defaultDirPath, filters);
|
||||
if (filename.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
QFile file(filename);
|
||||
file.open(QIODevice::ReadOnly);
|
||||
QTextStream stream(&file);
|
||||
m_own = KeeShareSettings::Own::deserialize(stream.readAll());
|
||||
file.close();
|
||||
FileDialog::saveLastDir("keeshare_key", filename);
|
||||
|
||||
updateOwnCertificate();
|
||||
}
|
||||
|
||||
void SettingsWidgetKeeShare::exportCertificate()
|
||||
{
|
||||
if (KeeShare::own() != m_own) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
auto defaultDirPath = FileDialog::getLastDir("keeshare_key");
|
||||
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);
|
||||
if (filename.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
QFile file(filename);
|
||||
file.open(QIODevice::Truncate | QIODevice::WriteOnly);
|
||||
QTextStream stream(&file);
|
||||
stream << KeeShareSettings::Own::serialize(m_own);
|
||||
stream.flush();
|
||||
file.close();
|
||||
FileDialog::saveLastDir("keeshare_key", filename);
|
||||
}
|
||||
|
||||
void SettingsWidgetKeeShare::trustSelectedCertificates()
|
||||
{
|
||||
const auto* selectionModel = m_ui->importedCertificateTableView->selectionModel();
|
||||
Q_ASSERT(selectionModel);
|
||||
for (const auto& index : selectionModel->selectedRows()) {
|
||||
m_foreign.certificates[index.row()].trust = KeeShareSettings::Trust::Trusted;
|
||||
}
|
||||
|
||||
updateForeignCertificates();
|
||||
}
|
||||
|
||||
void SettingsWidgetKeeShare::askSelectedCertificates()
|
||||
{
|
||||
const auto* selectionModel = m_ui->importedCertificateTableView->selectionModel();
|
||||
Q_ASSERT(selectionModel);
|
||||
for (const auto& index : selectionModel->selectedRows()) {
|
||||
m_foreign.certificates[index.row()].trust = KeeShareSettings::Trust::Ask;
|
||||
}
|
||||
|
||||
updateForeignCertificates();
|
||||
}
|
||||
|
||||
void SettingsWidgetKeeShare::untrustSelectedCertificates()
|
||||
{
|
||||
const auto* selectionModel = m_ui->importedCertificateTableView->selectionModel();
|
||||
Q_ASSERT(selectionModel);
|
||||
for (const auto& index : selectionModel->selectedRows()) {
|
||||
m_foreign.certificates[index.row()].trust = KeeShareSettings::Trust::Untrusted;
|
||||
}
|
||||
|
||||
updateForeignCertificates();
|
||||
}
|
||||
|
||||
void SettingsWidgetKeeShare::removeSelectedCertificates()
|
||||
{
|
||||
auto certificates = m_foreign.certificates;
|
||||
const auto* selectionModel = m_ui->importedCertificateTableView->selectionModel();
|
||||
Q_ASSERT(selectionModel);
|
||||
for (const auto& index : selectionModel->selectedRows()) {
|
||||
certificates.removeOne(m_foreign.certificates[index.row()]);
|
||||
}
|
||||
m_foreign.certificates = certificates;
|
||||
|
||||
updateForeignCertificates();
|
||||
}
|
||||
|
@ -50,23 +50,13 @@ private slots:
|
||||
void setVerificationExporter(const QString& signer);
|
||||
|
||||
void generateCertificate();
|
||||
void importCertificate();
|
||||
void exportCertificate();
|
||||
|
||||
void trustSelectedCertificates();
|
||||
void askSelectedCertificates();
|
||||
void untrustSelectedCertificates();
|
||||
void removeSelectedCertificates();
|
||||
|
||||
private:
|
||||
void updateOwnCertificate();
|
||||
void updateForeignCertificates();
|
||||
|
||||
QScopedPointer<Ui::SettingsWidgetKeeShare> m_ui;
|
||||
|
||||
KeeShareSettings::Own m_own;
|
||||
KeeShareSettings::Foreign m_foreign;
|
||||
QScopedPointer<QStandardItemModel> m_importedCertificateModel;
|
||||
};
|
||||
|
||||
#endif // KEEPASSXC_SETTINGSWIDGETKEESHARE_H
|
||||
|
@ -6,11 +6,11 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>378</width>
|
||||
<height>511</height>
|
||||
<width>425</width>
|
||||
<height>251</height>
|
||||
</rect>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0,0">
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,0">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
@ -70,67 +70,55 @@
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="ownCertificateGroupBox">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string>Own certificate</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2" columnstretch="0,0,0">
|
||||
<property name="horizontalSpacing">
|
||||
<number>10</number>
|
||||
</property>
|
||||
<item row="0" column="0" colspan="3">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="generateOwnCerticateButton">
|
||||
<property name="accessibleName">
|
||||
<string>Generate new certificate</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Generate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="importOwnCertificateButton">
|
||||
<property name="accessibleName">
|
||||
<string>Import existing certificate</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Import</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="exportOwnCertificateButton">
|
||||
<property name="accessibleName">
|
||||
<string>Export own certificate</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Export</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="ownCertificateSignerLabel">
|
||||
<property name="text">
|
||||
<string>Signer:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" colspan="2">
|
||||
<item row="2" column="1">
|
||||
<widget class="QPushButton" name="generateOwnCerticateButton">
|
||||
<property name="accessibleName">
|
||||
<string>Generate new certificate</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Generate</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="ownCertificateFingerprintLabel">
|
||||
<property name="text">
|
||||
<string>Fingerprint:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="1" colspan="2">
|
||||
<widget class="QLineEdit" name="ownCertificateSignerEdit">
|
||||
<property name="accessibleName">
|
||||
<string>Signer name field</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="2">
|
||||
<widget class="QLineEdit" name="ownCertificateFingerprintEdit">
|
||||
<property name="accessibleName">
|
||||
<string>Fingerprint</string>
|
||||
@ -140,133 +128,6 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" colspan="2">
|
||||
<widget class="QLineEdit" name="ownCertificateSignerEdit">
|
||||
<property name="accessibleName">
|
||||
<string>Signer name field</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="ownCertificateSignerLabel">
|
||||
<property name="text">
|
||||
<string>Signer:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="ownCertificateFingerprintLabel">
|
||||
<property name="text">
|
||||
<string>Fingerprint:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="importedCertificatesGroupBox">
|
||||
<property name="title">
|
||||
<string>Imported certificates</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="1" column="0" rowspan="2">
|
||||
<widget class="QTableView" name="importedCertificateTableView">
|
||||
<property name="accessibleName">
|
||||
<string>Known shares</string>
|
||||
</property>
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="showDropIndicator" stdset="0">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="dragDropOverwriteMode">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="alternatingRowColors">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::MultiSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="certificatesActionLayout">
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="trustImportedCertificateButton">
|
||||
<property name="accessibleName">
|
||||
<string>Trust selected certificate</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Trust</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="askImportedCertificateButton">
|
||||
<property name="accessibleName">
|
||||
<string>Ask whether to trust the selected certificate every time</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Ask</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="untrustImportedCertificateButton">
|
||||
<property name="accessibleName">
|
||||
<string>Untrust selected certificate</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Untrust</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="removeImportedCertificateButton">
|
||||
<property name="accessibleName">
|
||||
<string>Remove selected certificate</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
@ -289,16 +150,8 @@
|
||||
<tabstop>enableImportCheckBox</tabstop>
|
||||
<tabstop>quietSuccessCheckBox</tabstop>
|
||||
<tabstop>enableExportCheckBox</tabstop>
|
||||
<tabstop>generateOwnCerticateButton</tabstop>
|
||||
<tabstop>importOwnCertificateButton</tabstop>
|
||||
<tabstop>exportOwnCertificateButton</tabstop>
|
||||
<tabstop>ownCertificateSignerEdit</tabstop>
|
||||
<tabstop>ownCertificateFingerprintEdit</tabstop>
|
||||
<tabstop>trustImportedCertificateButton</tabstop>
|
||||
<tabstop>askImportedCertificateButton</tabstop>
|
||||
<tabstop>untrustImportedCertificateButton</tabstop>
|
||||
<tabstop>removeImportedCertificateButton</tabstop>
|
||||
<tabstop>importedCertificateTableView</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
|
@ -14,23 +14,20 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "ShareExport.h"
|
||||
#include "config-keepassx.h"
|
||||
#include "core/Group.h"
|
||||
#include "core/Metadata.h"
|
||||
#include "crypto/Random.h"
|
||||
#include "format/KeePass2Writer.h"
|
||||
#include "gui/Icons.h"
|
||||
#include "gui/MessageBox.h"
|
||||
#include "keeshare/KeeShare.h"
|
||||
#include "keeshare/Signature.h"
|
||||
#include "keys/PasswordKey.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QTextStream>
|
||||
|
||||
#if defined(WITH_XC_KEESHARE_SECURE)
|
||||
#include <botan/pk_keys.h>
|
||||
#include <quazipfile.h>
|
||||
#endif
|
||||
#include <botan/pubkey.h>
|
||||
#include <zip.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -64,8 +61,6 @@ namespace
|
||||
auto* targetDb = new Database();
|
||||
auto* targetMetadata = targetDb->metadata();
|
||||
targetMetadata->setRecycleBinEnabled(false);
|
||||
auto key = QSharedPointer<CompositeKey>::create();
|
||||
key->addKey(QSharedPointer<PasswordKey>::create(reference.password));
|
||||
|
||||
// Copy the source root as the root of the export database, memory manage the old root node
|
||||
auto* targetRoot = sourceRoot->clone(Entry::CloneNoFlags, Group::CloneNoFlags);
|
||||
@ -86,7 +81,10 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
auto key = QSharedPointer<CompositeKey>::create();
|
||||
key->addKey(QSharedPointer<PasswordKey>::create(reference.password));
|
||||
targetDb->setKey(key);
|
||||
|
||||
auto* obsoleteRoot = targetDb->rootGroup();
|
||||
targetDb->setRootGroup(targetRoot);
|
||||
delete obsoleteRoot;
|
||||
@ -108,124 +106,106 @@ namespace
|
||||
return targetDb;
|
||||
}
|
||||
|
||||
ShareObserver::Result
|
||||
intoSignedContainer(const QString& resolvedPath, const KeeShareSettings::Reference& reference, Database* targetDb)
|
||||
bool writeZipFile(void* zf, const QString& fileName, const QByteArray& data)
|
||||
{
|
||||
#if !defined(WITH_XC_KEESHARE_SECURE)
|
||||
Q_UNUSED(targetDb);
|
||||
Q_UNUSED(resolvedPath);
|
||||
return {reference.path,
|
||||
ShareObserver::Result::Warning,
|
||||
ShareExport::tr("Overwriting signed share container is not supported - export prevented")};
|
||||
#else
|
||||
QByteArray bytes;
|
||||
{
|
||||
QBuffer buffer(&bytes);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
KeePass2Writer writer;
|
||||
writer.writeDatabase(&buffer, targetDb);
|
||||
if (writer.hasError()) {
|
||||
qWarning("Serializing export dabase failed: %s.", writer.errorString().toLatin1().data());
|
||||
return {reference.path, ShareObserver::Result::Error, writer.errorString()};
|
||||
}
|
||||
}
|
||||
const auto own = KeeShare::own();
|
||||
QuaZip zip(resolvedPath);
|
||||
zip.setFileNameCodec("UTF-8");
|
||||
const bool zipOpened = zip.open(QuaZip::mdCreate);
|
||||
if (!zipOpened) {
|
||||
::qWarning("Opening export file failed: %d", zip.getZipError());
|
||||
return {reference.path,
|
||||
ShareObserver::Result::Error,
|
||||
ShareExport::tr("Could not write export container (%1)").arg(zip.getZipError())};
|
||||
}
|
||||
{
|
||||
QuaZipFile file(&zip);
|
||||
const auto signatureOpened = file.open(QIODevice::WriteOnly, QuaZipNewInfo(KeeShare::signatureFileName()));
|
||||
if (!signatureOpened) {
|
||||
::qWarning("Embedding signature failed: Could not open file to write (%d)", zip.getZipError());
|
||||
return {reference.path,
|
||||
ShareObserver::Result::Error,
|
||||
ShareExport::tr("Could not embed signature: Could not open file to write (%1)")
|
||||
.arg(file.getZipError())};
|
||||
}
|
||||
QTextStream stream(&file);
|
||||
KeeShareSettings::Sign sign;
|
||||
// TODO: check for false return
|
||||
Signature::create(bytes, own.key.key, sign.signature);
|
||||
sign.certificate = own.certificate;
|
||||
stream << KeeShareSettings::Sign::serialize(sign);
|
||||
stream.flush();
|
||||
if (file.getZipError() != ZIP_OK) {
|
||||
::qWarning("Embedding signature failed: Could not write file (%d)", zip.getZipError());
|
||||
return {
|
||||
reference.path,
|
||||
ShareObserver::Result::Error,
|
||||
ShareExport::tr("Could not embed signature: Could not write file (%1)").arg(file.getZipError())};
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
{
|
||||
QuaZipFile file(&zip);
|
||||
const auto dbOpened = file.open(QIODevice::WriteOnly, QuaZipNewInfo(KeeShare::containerFileName()));
|
||||
if (!dbOpened) {
|
||||
::qWarning("Embedding database failed: Could not open file to write (%d)", zip.getZipError());
|
||||
return {reference.path,
|
||||
ShareObserver::Result::Error,
|
||||
ShareExport::tr("Could not embed database: Could not open file to write (%1)")
|
||||
.arg(file.getZipError())};
|
||||
}
|
||||
file.write(bytes);
|
||||
if (file.getZipError() != ZIP_OK) {
|
||||
::qWarning("Embedding database failed: Could not write file (%d)", zip.getZipError());
|
||||
return {reference.path,
|
||||
ShareObserver::Result::Error,
|
||||
ShareExport::tr("Could not embed database: Could not write file (%1)").arg(file.getZipError())};
|
||||
}
|
||||
file.close();
|
||||
}
|
||||
zip.close();
|
||||
return {reference.path};
|
||||
#endif
|
||||
zipOpenNewFileInZip64(zf,
|
||||
fileName.toLatin1().data(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
Z_DEFLATED,
|
||||
Z_BEST_COMPRESSION,
|
||||
1);
|
||||
int pos = 0;
|
||||
do {
|
||||
auto len = qMin(data.size() - pos, 8192);
|
||||
zipWriteInFileInZip(zf, data.data() + pos, len);
|
||||
pos += len;
|
||||
} while (pos < data.size());
|
||||
|
||||
zipCloseFileInZip(zf);
|
||||
return true;
|
||||
}
|
||||
|
||||
ShareObserver::Result
|
||||
intoUnsignedContainer(const QString& resolvedPath, const KeeShareSettings::Reference& reference, Database* targetDb)
|
||||
bool signData(const QByteArray& data, const KeeShareSettings::Key& key, QString& signature)
|
||||
{
|
||||
#if !defined(WITH_XC_KEESHARE_INSECURE)
|
||||
Q_UNUSED(targetDb);
|
||||
Q_UNUSED(resolvedPath);
|
||||
return {reference.path,
|
||||
ShareObserver::Result::Warning,
|
||||
ShareExport::tr("Overwriting unsigned share container is not supported - export prevented")};
|
||||
#else
|
||||
QFile file(resolvedPath);
|
||||
const bool fileOpened = file.open(QIODevice::WriteOnly);
|
||||
if (!fileOpened) {
|
||||
::qWarning("Opening export file failed");
|
||||
return {reference.path, ShareObserver::Result::Error, ShareExport::tr("Could not write export container")};
|
||||
}
|
||||
KeePass2Writer writer;
|
||||
writer.writeDatabase(&file, targetDb);
|
||||
if (writer.hasError()) {
|
||||
qWarning("Exporting dabase failed: %s.", writer.errorString().toLatin1().data());
|
||||
return {reference.path, ShareObserver::Result::Error, writer.errorString()};
|
||||
}
|
||||
file.close();
|
||||
#endif
|
||||
return {reference.path};
|
||||
}
|
||||
if (key.key->algo_name() == "RSA") {
|
||||
try {
|
||||
Botan::PK_Signer signer(*key.key, "EMSA3(SHA-256)");
|
||||
signer.update(reinterpret_cast<const uint8_t*>(data.constData()), data.size());
|
||||
auto s = signer.signature(*randomGen()->getRng());
|
||||
|
||||
auto hex = QByteArray(reinterpret_cast<char*>(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;
|
||||
}
|
||||
}
|
||||
qWarning("Unsupported Public/Private key format");
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
ShareObserver::Result ShareExport::intoContainer(const QString& resolvedPath,
|
||||
const KeeShareSettings::Reference& reference,
|
||||
const Group* group)
|
||||
{
|
||||
QScopedPointer<Database> targetDb(extractIntoDatabase(reference, group));
|
||||
const QFileInfo info(resolvedPath);
|
||||
if (KeeShare::isContainerType(info, KeeShare::signedContainerFileType())) {
|
||||
return intoSignedContainer(resolvedPath, reference, targetDb.data());
|
||||
QFile file(resolvedPath);
|
||||
const bool fileOpened = file.open(QIODevice::WriteOnly);
|
||||
if (!fileOpened) {
|
||||
qWarning("Opening export file failed");
|
||||
return {resolvedPath, ShareObserver::Result::Error, file.errorString()};
|
||||
}
|
||||
return intoUnsignedContainer(resolvedPath, reference, targetDb.data());
|
||||
|
||||
QScopedPointer<Database> targetDb(extractIntoDatabase(reference, group));
|
||||
if (resolvedPath.endsWith(".kdbx.share")) {
|
||||
// Write database to memory and sign it
|
||||
QByteArray dbData, signatureData;
|
||||
QBuffer buffer;
|
||||
|
||||
buffer.setBuffer(&dbData);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
|
||||
KeePass2Writer writer;
|
||||
if (!writer.writeDatabase(&buffer, targetDb.data())) {
|
||||
qWarning("Serializing export dabase failed: %s.", writer.errorString().toLatin1().data());
|
||||
return {reference.path, ShareObserver::Result::Error, writer.errorString()};
|
||||
}
|
||||
|
||||
buffer.close();
|
||||
|
||||
// Get Own Certificate for signing
|
||||
const auto own = KeeShare::own();
|
||||
Q_ASSERT(!own.isNull());
|
||||
|
||||
// Sign the database data
|
||||
KeeShareSettings::Sign sign;
|
||||
sign.certificate = own.certificate;
|
||||
signData(dbData, own.key, sign.signature);
|
||||
|
||||
signatureData = KeeShareSettings::Sign::serialize(sign).toLatin1();
|
||||
|
||||
auto zf = zipOpen64(resolvedPath.toLatin1().data(), 0);
|
||||
if (!zf) {
|
||||
return {reference.path, ShareObserver::Result::Error, ShareExport::tr("Could not write export container.")};
|
||||
}
|
||||
|
||||
writeZipFile(zf, KeeShare::signatureFileName().toLatin1().data(), signatureData);
|
||||
writeZipFile(zf, KeeShare::containerFileName().toLatin1().data(), dbData);
|
||||
|
||||
zipClose(zf, nullptr);
|
||||
} else {
|
||||
QString error;
|
||||
if (!targetDb->saveAs(resolvedPath, Database::Atomic, {}, &error)) {
|
||||
qWarning("Exporting dabase failed: %s.", error.toLatin1().data());
|
||||
return {resolvedPath, ShareObserver::Result::Error, error};
|
||||
}
|
||||
}
|
||||
|
||||
return {resolvedPath};
|
||||
}
|
||||
|
@ -15,324 +15,87 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "ShareImport.h"
|
||||
#include "config-keepassx.h"
|
||||
#include "core/Merger.h"
|
||||
#include "format/KeePass2Reader.h"
|
||||
#include "keeshare/KeeShare.h"
|
||||
#include "keeshare/Signature.h"
|
||||
#include "keys/PasswordKey.h"
|
||||
|
||||
#include <QBuffer>
|
||||
#include <QMessageBox>
|
||||
#include <QPushButton>
|
||||
#include <QTextStream>
|
||||
|
||||
#if defined(WITH_XC_KEESHARE_SECURE)
|
||||
#include <botan/pk_keys.h>
|
||||
#include <quazipfile.h>
|
||||
#endif
|
||||
#include <unzip.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
enum Trust
|
||||
QByteArray readZipFile(void* uf)
|
||||
{
|
||||
Invalid,
|
||||
Own,
|
||||
UntrustedForever,
|
||||
UntrustedOnce,
|
||||
TrustedOnce,
|
||||
TrustedForever,
|
||||
};
|
||||
|
||||
QPair<Trust, KeeShareSettings::Certificate>
|
||||
check(QByteArray& data,
|
||||
const KeeShareSettings::Reference& reference,
|
||||
const KeeShareSettings::Certificate& ownCertificate,
|
||||
const QList<KeeShareSettings::ScopedCertificate>& knownCertificates,
|
||||
const KeeShareSettings::Sign& sign)
|
||||
{
|
||||
KeeShareSettings::Certificate certificate;
|
||||
if (!sign.signature.isEmpty()) {
|
||||
certificate = sign.certificate;
|
||||
if (!Signature::verify(data, sign.certificate.key, sign.signature)) {
|
||||
qCritical("Invalid signature for shared container %s.", qPrintable(reference.path));
|
||||
return {Invalid, KeeShareSettings::Certificate()};
|
||||
QByteArray data;
|
||||
int bytes, bytesRead = 0;
|
||||
unzOpenCurrentFile(uf);
|
||||
do {
|
||||
data.resize(data.size() + 8192);
|
||||
bytes = unzReadCurrentFile(uf, data.data() + bytesRead, 8192);
|
||||
if (bytes > 0) {
|
||||
bytesRead += bytes;
|
||||
}
|
||||
|
||||
// Automatically trust your own certificate
|
||||
if (ownCertificate == sign.certificate) {
|
||||
return {Own, ownCertificate};
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& scopedCertificate : knownCertificates) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 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.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.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);
|
||||
auto trustedForever = warning.addButton(ShareImport::tr("Always"), QMessageBox::ButtonRole::YesRole);
|
||||
auto trustedOnce = warning.addButton(ShareImport::tr("Just this time"), QMessageBox::ButtonRole::YesRole);
|
||||
warning.setDefaultButton(untrustedOnce);
|
||||
warning.exec();
|
||||
if (warning.clickedButton() == trustedForever) {
|
||||
return {TrustedForever, certificate};
|
||||
}
|
||||
if (warning.clickedButton() == trustedOnce) {
|
||||
return {TrustedOnce, certificate};
|
||||
}
|
||||
if (warning.clickedButton() == untrustedOnce) {
|
||||
return {UntrustedOnce, certificate};
|
||||
}
|
||||
if (warning.clickedButton() == untrustedForever) {
|
||||
return {UntrustedForever, certificate};
|
||||
}
|
||||
return {UntrustedOnce, certificate};
|
||||
} while (bytes > 0);
|
||||
unzCloseCurrentFile(uf);
|
||||
data.truncate(bytesRead);
|
||||
return data;
|
||||
}
|
||||
|
||||
ShareObserver::Result
|
||||
signedContainerInto(const QString& resolvedPath, const KeeShareSettings::Reference& reference, Group* targetGroup)
|
||||
{
|
||||
#if !defined(WITH_XC_KEESHARE_SECURE)
|
||||
Q_UNUSED(targetGroup);
|
||||
Q_UNUSED(resolvedPath);
|
||||
return {reference.path,
|
||||
ShareObserver::Result::Warning,
|
||||
ShareImport::tr("Signed share container are not supported - import prevented")};
|
||||
#else
|
||||
QuaZip zip(resolvedPath);
|
||||
if (!zip.open(QuaZip::mdUnzip)) {
|
||||
qCritical("Unable to open file %s.", qPrintable(reference.path));
|
||||
return {reference.path, ShareObserver::Result::Error, ShareImport::tr("File is not readable")};
|
||||
}
|
||||
const auto expected = QSet<QString>() << KeeShare::signatureFileName() << KeeShare::containerFileName();
|
||||
const auto files = zip.getFileInfoList();
|
||||
QSet<QString> actual;
|
||||
for (const auto& file : files) {
|
||||
actual << file.name;
|
||||
}
|
||||
if (expected != actual) {
|
||||
qCritical("Invalid sharing container %s.", qPrintable(reference.path));
|
||||
return {reference.path, ShareObserver::Result::Error, ShareImport::tr("Invalid sharing container")};
|
||||
}
|
||||
|
||||
zip.setCurrentFile(KeeShare::signatureFileName());
|
||||
QuaZipFile signatureFile(&zip);
|
||||
signatureFile.open(QuaZipFile::ReadOnly);
|
||||
QTextStream stream(&signatureFile);
|
||||
|
||||
const auto sign = KeeShareSettings::Sign::deserialize(stream.readAll());
|
||||
signatureFile.close();
|
||||
|
||||
zip.setCurrentFile(KeeShare::containerFileName());
|
||||
QuaZipFile databaseFile(&zip);
|
||||
databaseFile.open(QuaZipFile::ReadOnly);
|
||||
auto payload = databaseFile.readAll();
|
||||
databaseFile.close();
|
||||
QBuffer buffer(&payload);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
|
||||
KeePass2Reader reader;
|
||||
auto key = QSharedPointer<CompositeKey>::create();
|
||||
key->addKey(QSharedPointer<PasswordKey>::create(reference.password));
|
||||
auto sourceDb = QSharedPointer<Database>::create();
|
||||
if (!reader.readDatabase(&buffer, key, sourceDb.data())) {
|
||||
qCritical("Error while parsing the database: %s", qPrintable(reader.errorString()));
|
||||
return {reference.path, ShareObserver::Result::Error, reader.errorString()};
|
||||
}
|
||||
|
||||
auto foreign = KeeShare::foreign();
|
||||
auto own = KeeShare::own();
|
||||
auto trust = check(payload, reference, own.certificate, foreign.certificates, sign);
|
||||
switch (trust.first) {
|
||||
case Invalid:
|
||||
qWarning("Prevent untrusted import");
|
||||
return {reference.path, ShareObserver::Result::Error, ShareImport::tr("Untrusted import prevented")};
|
||||
|
||||
case UntrustedForever:
|
||||
case TrustedForever: {
|
||||
bool found = false;
|
||||
const auto trusted =
|
||||
trust.first == TrustedForever ? KeeShareSettings::Trust::Trusted : KeeShareSettings::Trust::Untrusted;
|
||||
for (KeeShareSettings::ScopedCertificate& scopedCertificate : foreign.certificates) {
|
||||
if (scopedCertificate.certificate == trust.second && scopedCertificate.path == reference.path) {
|
||||
scopedCertificate.certificate.signer = trust.second.signer;
|
||||
scopedCertificate.path = reference.path;
|
||||
scopedCertificate.trust = trusted;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
foreign.certificates << KeeShareSettings::ScopedCertificate{reference.path, trust.second, trusted};
|
||||
}
|
||||
// update foreign certificates with new settings
|
||||
KeeShare::setForeign(foreign);
|
||||
|
||||
if (trust.first == TrustedForever) {
|
||||
qDebug("Synchronize %s %s with %s",
|
||||
qPrintable(reference.path),
|
||||
qPrintable(targetGroup->name()),
|
||||
qPrintable(sourceDb->rootGroup()->name()));
|
||||
Merger merger(sourceDb->rootGroup(), targetGroup);
|
||||
merger.setForcedMergeMode(Group::Synchronize);
|
||||
auto changelist = merger.merge();
|
||||
if (!changelist.isEmpty()) {
|
||||
return {
|
||||
reference.path, ShareObserver::Result::Success, ShareImport::tr("Successful signed import")};
|
||||
}
|
||||
}
|
||||
// Silent ignore of untrusted import or unchanging import
|
||||
return {};
|
||||
}
|
||||
case TrustedOnce:
|
||||
case Own: {
|
||||
qDebug("Synchronize %s %s with %s",
|
||||
qPrintable(reference.path),
|
||||
qPrintable(targetGroup->name()),
|
||||
qPrintable(sourceDb->rootGroup()->name()));
|
||||
Merger merger(sourceDb->rootGroup(), targetGroup);
|
||||
merger.setForcedMergeMode(Group::Synchronize);
|
||||
auto changelist = merger.merge();
|
||||
if (!changelist.isEmpty()) {
|
||||
return {reference.path, ShareObserver::Result::Success, ShareImport::tr("Successful signed import")};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
default:
|
||||
qWarning("Prevented untrusted import of signed KeeShare database %s", qPrintable(reference.path));
|
||||
return {reference.path, ShareObserver::Result::Warning, ShareImport::tr("Untrusted import prevented")};
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
ShareObserver::Result
|
||||
unsignedContainerInto(const QString& resolvedPath, const KeeShareSettings::Reference& reference, Group* targetGroup)
|
||||
{
|
||||
#if !defined(WITH_XC_KEESHARE_INSECURE)
|
||||
Q_UNUSED(targetGroup);
|
||||
Q_UNUSED(resolvedPath);
|
||||
return {reference.path,
|
||||
ShareObserver::Result::Warning,
|
||||
ShareImport::tr("Unsigned share container are not supported - import prevented")};
|
||||
#else
|
||||
QFile file(resolvedPath);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qCritical("Unable to open file %s.", qPrintable(reference.path));
|
||||
return {reference.path, ShareObserver::Result::Error, ShareImport::tr("File is not readable")};
|
||||
}
|
||||
auto payload = file.readAll();
|
||||
file.close();
|
||||
QBuffer buffer(&payload);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
|
||||
KeePass2Reader reader;
|
||||
auto key = QSharedPointer<CompositeKey>::create();
|
||||
key->addKey(QSharedPointer<PasswordKey>::create(reference.password));
|
||||
auto sourceDb = QSharedPointer<Database>::create();
|
||||
if (!reader.readDatabase(&buffer, key, sourceDb.data())) {
|
||||
qCritical("Error while parsing the database: %s", qPrintable(reader.errorString()));
|
||||
return {reference.path, ShareObserver::Result::Error, reader.errorString()};
|
||||
}
|
||||
|
||||
auto foreign = KeeShare::foreign();
|
||||
const auto own = KeeShare::own();
|
||||
const auto sign = KeeShareSettings::Sign(); // invalid sign
|
||||
auto trust = check(payload, reference, own.certificate, foreign.certificates, sign);
|
||||
switch (trust.first) {
|
||||
case UntrustedForever:
|
||||
case TrustedForever: {
|
||||
bool found = false;
|
||||
const auto trusted =
|
||||
trust.first == TrustedForever ? KeeShareSettings::Trust::Trusted : KeeShareSettings::Trust::Untrusted;
|
||||
for (KeeShareSettings::ScopedCertificate& scopedCertificate : foreign.certificates) {
|
||||
if (scopedCertificate.certificate == trust.second && scopedCertificate.path == reference.path) {
|
||||
scopedCertificate.certificate.signer = trust.second.signer;
|
||||
scopedCertificate.path = reference.path;
|
||||
scopedCertificate.trust = trusted;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
foreign.certificates << KeeShareSettings::ScopedCertificate{reference.path, trust.second, trusted};
|
||||
}
|
||||
// update foreign certificates with new settings
|
||||
KeeShare::setForeign(foreign);
|
||||
|
||||
if (trust.first == TrustedForever) {
|
||||
qDebug("Synchronize %s %s with %s",
|
||||
qPrintable(reference.path),
|
||||
qPrintable(targetGroup->name()),
|
||||
qPrintable(sourceDb->rootGroup()->name()));
|
||||
Merger merger(sourceDb->rootGroup(), targetGroup);
|
||||
merger.setForcedMergeMode(Group::Synchronize);
|
||||
auto changelist = merger.merge();
|
||||
if (!changelist.isEmpty()) {
|
||||
return {
|
||||
reference.path, ShareObserver::Result::Success, ShareImport::tr("Successful signed import")};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
case TrustedOnce: {
|
||||
qDebug("Synchronize %s %s with %s",
|
||||
qPrintable(reference.path),
|
||||
qPrintable(targetGroup->name()),
|
||||
qPrintable(sourceDb->rootGroup()->name()));
|
||||
Merger merger(sourceDb->rootGroup(), targetGroup);
|
||||
merger.setForcedMergeMode(Group::Synchronize);
|
||||
auto changelist = merger.merge();
|
||||
if (!changelist.isEmpty()) {
|
||||
return {reference.path, ShareObserver::Result::Success, ShareImport::tr("Successful unsigned import")};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
default:
|
||||
qWarning("Prevented untrusted import of unsigned KeeShare database %s", qPrintable(reference.path));
|
||||
return {reference.path, ShareObserver::Result::Warning, ShareImport::tr("Untrusted import prevented")};
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ShareObserver::Result ShareImport::containerInto(const QString& resolvedPath,
|
||||
const KeeShareSettings::Reference& reference,
|
||||
Group* targetGroup)
|
||||
{
|
||||
const QFileInfo info(resolvedPath);
|
||||
if (!info.exists()) {
|
||||
qCritical("File %s does not exist.", qPrintable(info.absoluteFilePath()));
|
||||
return {reference.path, ShareObserver::Result::Warning, tr("File does not exist")};
|
||||
// TODO: Read signing certificate as well, but don't check validity
|
||||
QByteArray dbData;
|
||||
|
||||
auto uf = unzOpen64(resolvedPath.toLatin1().constData());
|
||||
if (uf) {
|
||||
// Open zip share, extract database portion, ignore signature file
|
||||
char zipFileName[256];
|
||||
auto err = unzGoToFirstFile(uf);
|
||||
while (err == UNZ_OK) {
|
||||
unzGetCurrentFileInfo64(uf, nullptr, zipFileName, sizeof(zipFileName), nullptr, 0, nullptr, 0);
|
||||
if (QString(zipFileName).compare(KeeShare::containerFileName()) == 0) {
|
||||
dbData = readZipFile(uf);
|
||||
}
|
||||
err = unzGoToNextFile(uf);
|
||||
}
|
||||
unzClose(uf);
|
||||
} else {
|
||||
// Open KDBX file directly
|
||||
QFile file(resolvedPath);
|
||||
if (!file.open(QIODevice::ReadOnly)) {
|
||||
qCritical("Unable to open file %s.", qPrintable(reference.path));
|
||||
return {reference.path, ShareObserver::Result::Error, file.errorString()};
|
||||
}
|
||||
dbData = file.readAll();
|
||||
}
|
||||
|
||||
if (KeeShare::isContainerType(info, KeeShare::signedContainerFileType())) {
|
||||
return signedContainerInto(resolvedPath, reference, targetGroup);
|
||||
QBuffer buffer(&dbData);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
|
||||
KeePass2Reader reader;
|
||||
auto key = QSharedPointer<CompositeKey>::create();
|
||||
key->addKey(QSharedPointer<PasswordKey>::create(reference.password));
|
||||
auto sourceDb = QSharedPointer<Database>::create();
|
||||
if (!reader.readDatabase(&buffer, key, sourceDb.data())) {
|
||||
qCritical("Error while parsing the database: %s", qPrintable(reader.errorString()));
|
||||
return {reference.path, ShareObserver::Result::Error, reader.errorString()};
|
||||
}
|
||||
return unsignedContainerInto(resolvedPath, reference, targetGroup);
|
||||
|
||||
qDebug("Synchronize %s %s with %s",
|
||||
qPrintable(reference.path),
|
||||
qPrintable(targetGroup->name()),
|
||||
qPrintable(sourceDb->rootGroup()->name()));
|
||||
|
||||
Merger merger(sourceDb->rootGroup(), targetGroup);
|
||||
merger.setForcedMergeMode(Group::Synchronize);
|
||||
auto changelist = merger.merge();
|
||||
if (!changelist.isEmpty()) {
|
||||
return {reference.path, ShareObserver::Result::Success, ShareImport::tr("Successful import")};
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
@ -278,13 +278,14 @@ QList<ShareObserver::Result> ShareObserver::exportShares()
|
||||
}
|
||||
|
||||
for (auto it = references.cbegin(); it != references.cend(); ++it) {
|
||||
const auto& reference = it.value().first();
|
||||
auto reference = it.value().first();
|
||||
const QString resolvedPath = resolvePath(reference.config.path, m_db);
|
||||
auto watcher = m_fileWatchers.value(resolvedPath);
|
||||
if (watcher) {
|
||||
watcher->stop();
|
||||
}
|
||||
|
||||
// TODO: save new path into group settings if not saving to signed container anymore
|
||||
results << ShareExport::intoContainer(resolvedPath, reference.config, reference.group);
|
||||
|
||||
if (watcher) {
|
||||
|
@ -1,70 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "Signature.h"
|
||||
|
||||
#include "crypto/Random.h"
|
||||
|
||||
#include <botan/pubkey.h>
|
||||
|
||||
bool Signature::create(const QByteArray& data, QSharedPointer<Botan::Private_Key> 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->algo_name() == "RSA") {
|
||||
try {
|
||||
Botan::PK_Signer signer(*key, "EMSA3(SHA-256)");
|
||||
signer.update(reinterpret_cast<const uint8_t*>(data.constData()), data.size());
|
||||
auto s = signer.signature(*randomGen()->getRng());
|
||||
|
||||
auto hex = QByteArray(reinterpret_cast<char*>(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;
|
||||
}
|
||||
}
|
||||
qWarning("Unsupported Public/Private key format");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Signature::verify(const QByteArray& data, QSharedPointer<Botan::Public_Key> 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<const uint8_t*>(data.constData()), data.size());
|
||||
return verifier.check_signature(reinterpret_cast<const uint8_t*>(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;
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_SIGNATURE_H
|
||||
#define KEEPASSXC_SIGNATURE_H
|
||||
|
||||
#include <QSharedPointer>
|
||||
#include <QString>
|
||||
#include <botan/pubkey.h>
|
||||
|
||||
namespace Signature
|
||||
{
|
||||
bool create(const QByteArray& data, QSharedPointer<Botan::Private_Key> key, QString& signature);
|
||||
bool verify(const QByteArray& data, QSharedPointer<Botan::Public_Key> key, const QString& signature);
|
||||
}; // namespace Signature
|
||||
|
||||
#endif // KEEPASSXC_SIGNATURE_H
|
@ -103,13 +103,8 @@ void EditGroupWidgetKeeShare::updateSharingState()
|
||||
return;
|
||||
}
|
||||
|
||||
auto supportedExtensions = QStringList();
|
||||
#if defined(WITH_XC_KEESHARE_INSECURE)
|
||||
supportedExtensions << KeeShare::unsignedContainerFileType();
|
||||
#endif
|
||||
#if defined(WITH_XC_KEESHARE_SECURE)
|
||||
supportedExtensions << KeeShare::signedContainerFileType();
|
||||
#endif
|
||||
QStringList supportedExtensions;
|
||||
supportedExtensions << KeeShare::unsignedContainerFileType() << KeeShare::signedContainerFileType();
|
||||
|
||||
// Custom message for active KeeShare reference
|
||||
const auto reference = KeeShare::referenceOf(m_temporaryGroup);
|
||||
@ -225,26 +220,15 @@ void EditGroupWidgetKeeShare::launchPathSelectionDialog()
|
||||
return;
|
||||
}
|
||||
auto reference = KeeShare::referenceOf(m_temporaryGroup);
|
||||
QString defaultFiletype = "";
|
||||
auto supportedExtensions = QStringList();
|
||||
auto unsupportedExtensions = QStringList();
|
||||
auto knownFilters = QStringList() << QString("%1 (*)").arg("All files");
|
||||
#if defined(WITH_XC_KEESHARE_INSECURE)
|
||||
defaultFiletype = KeeShare::unsignedContainerFileType();
|
||||
supportedExtensions << KeeShare::unsignedContainerFileType();
|
||||
knownFilters.prepend(
|
||||
QString("%1 (*.%2)").arg(tr("KeeShare unsigned container"), KeeShare::unsignedContainerFileType()));
|
||||
#else
|
||||
unsupportedExtensions << KeeShare::unsignedContainerFileType();
|
||||
#endif
|
||||
#if defined(WITH_XC_KEESHARE_SECURE)
|
||||
defaultFiletype = KeeShare::signedContainerFileType();
|
||||
supportedExtensions << KeeShare::signedContainerFileType();
|
||||
knownFilters.prepend(
|
||||
QString("%1 (*.%2)").arg(tr("KeeShare signed container"), KeeShare::signedContainerFileType()));
|
||||
#else
|
||||
unsupportedExtensions << KeeShare::signedContainerFileType();
|
||||
#endif
|
||||
QString defaultFiletype = KeeShare::unsignedContainerFileType();
|
||||
|
||||
QStringList supportedExtensions;
|
||||
supportedExtensions << KeeShare::unsignedContainerFileType() << KeeShare::signedContainerFileType();
|
||||
|
||||
QStringList knownFilters;
|
||||
knownFilters << QString("%1 (*.%2)").arg(tr("KeeShare container"), KeeShare::unsignedContainerFileType());
|
||||
knownFilters << QString("%1 (*.%2)").arg(tr("KeeShare signed container"), KeeShare::signedContainerFileType());
|
||||
knownFilters << QString("%1 (*)").arg("All files");
|
||||
|
||||
const auto filters = knownFilters.join(";;");
|
||||
auto defaultDirPath = FileDialog::getLastDir("keeshare");
|
||||
@ -269,7 +253,7 @@ void EditGroupWidgetKeeShare::launchPathSelectionDialog()
|
||||
return;
|
||||
}
|
||||
bool validFilename = false;
|
||||
for (const auto& extension : supportedExtensions + unsupportedExtensions) {
|
||||
for (const auto& extension : supportedExtensions) {
|
||||
if (filename.endsWith(extension, Qt::CaseInsensitive)) {
|
||||
validFilename = true;
|
||||
break;
|
||||
|
@ -125,11 +125,6 @@ add_unit_test(NAME testcryptohash SOURCES TestCryptoHash.cpp
|
||||
add_unit_test(NAME testsymmetriccipher SOURCES TestSymmetricCipher.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
|
||||
if(WITH_XC_KEESHARE)
|
||||
add_unit_test(NAME testsignature SOURCES TestSignature.cpp
|
||||
LIBS ${TEST_LIBRARIES})
|
||||
endif()
|
||||
|
||||
add_unit_test(NAME testhashedblockstream SOURCES TestHashedBlockStream.cpp
|
||||
LIBS testsupport ${TEST_LIBRARIES})
|
||||
|
||||
|
@ -31,9 +31,6 @@ QTEST_GUILESS_MAIN(TestSharing)
|
||||
Q_DECLARE_METATYPE(KeeShareSettings::Type)
|
||||
Q_DECLARE_METATYPE(KeeShareSettings::Key)
|
||||
Q_DECLARE_METATYPE(KeeShareSettings::Certificate)
|
||||
Q_DECLARE_METATYPE(KeeShareSettings::Trust)
|
||||
Q_DECLARE_METATYPE(KeeShareSettings::ScopedCertificate)
|
||||
Q_DECLARE_METATYPE(QList<KeeShareSettings::ScopedCertificate>)
|
||||
|
||||
void TestSharing::initTestCase()
|
||||
{
|
||||
@ -65,55 +62,12 @@ void TestSharing::testNullObjects()
|
||||
const auto xmlActive = KeeShareSettings::Active::deserialize(empty);
|
||||
QVERIFY(xmlActive.isNull());
|
||||
|
||||
const auto foreign = KeeShareSettings::Foreign();
|
||||
QVERIFY(foreign.certificates.isEmpty());
|
||||
const auto xmlForeign = KeeShareSettings::Foreign::deserialize(empty);
|
||||
QVERIFY(xmlForeign.certificates.isEmpty());
|
||||
|
||||
const auto reference = KeeShareSettings::Reference();
|
||||
QVERIFY(reference.isNull());
|
||||
const auto xmlReference = KeeShareSettings::Reference::deserialize(empty);
|
||||
QVERIFY(xmlReference.isNull());
|
||||
}
|
||||
|
||||
void TestSharing::testCertificateSerialization()
|
||||
{
|
||||
QFETCH(KeeShareSettings::Trust, trusted);
|
||||
auto key = stubkey();
|
||||
KeeShareSettings::ScopedCertificate original;
|
||||
original.path = "/path";
|
||||
original.certificate = KeeShareSettings::Certificate{key, "Some <!> &#_\"\" weird string"};
|
||||
original.trust = trusted;
|
||||
|
||||
QString buffer;
|
||||
QXmlStreamWriter writer(&buffer);
|
||||
writer.writeStartDocument();
|
||||
writer.writeStartElement("Certificate");
|
||||
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->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.key->public_key_bits(), key->public_key_bits());
|
||||
}
|
||||
|
||||
void TestSharing::testCertificateSerialization_data()
|
||||
{
|
||||
QTest::addColumn<KeeShareSettings::Trust>("trusted");
|
||||
QTest::newRow("Ask") << KeeShareSettings::Trust::Ask;
|
||||
QTest::newRow("Trusted") << KeeShareSettings::Trust::Trusted;
|
||||
QTest::newRow("Untrusted") << KeeShareSettings::Trust::Untrusted;
|
||||
}
|
||||
|
||||
void TestSharing::testKeySerialization()
|
||||
{
|
||||
auto key = stubkey();
|
||||
@ -177,16 +131,13 @@ void TestSharing::testSettingsSerialization()
|
||||
QFETCH(bool, exporting);
|
||||
QFETCH(KeeShareSettings::Certificate, ownCertificate);
|
||||
QFETCH(KeeShareSettings::Key, ownKey);
|
||||
QFETCH(QList<KeeShareSettings::ScopedCertificate>, foreignCertificates);
|
||||
|
||||
KeeShareSettings::Own originalOwn;
|
||||
KeeShareSettings::Foreign originalForeign;
|
||||
KeeShareSettings::Active originalActive;
|
||||
originalActive.in = importing;
|
||||
originalActive.out = exporting;
|
||||
originalOwn.certificate = ownCertificate;
|
||||
originalOwn.key = ownKey;
|
||||
originalForeign.certificates = foreignCertificates;
|
||||
|
||||
const QString serializedActive = KeeShareSettings::Active::serialize(originalActive);
|
||||
KeeShareSettings::Active restoredActive = KeeShareSettings::Active::deserialize(serializedActive);
|
||||
@ -194,9 +145,6 @@ void TestSharing::testSettingsSerialization()
|
||||
const QString serializedOwn = KeeShareSettings::Own::serialize(originalOwn);
|
||||
KeeShareSettings::Own restoredOwn = KeeShareSettings::Own::deserialize(serializedOwn);
|
||||
|
||||
const QString serializedForeign = KeeShareSettings::Foreign::serialize(originalForeign);
|
||||
KeeShareSettings::Foreign restoredForeign = KeeShareSettings::Foreign::deserialize(serializedForeign);
|
||||
|
||||
QCOMPARE(restoredActive.in, importing);
|
||||
QCOMPARE(restoredActive.out, exporting);
|
||||
if (ownCertificate.key) {
|
||||
@ -205,44 +153,29 @@ void TestSharing::testSettingsSerialization()
|
||||
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, foreignCertificates[i].certificate);
|
||||
}
|
||||
}
|
||||
|
||||
void TestSharing::testSettingsSerialization_data()
|
||||
{
|
||||
auto sshKey0 = stubkey(0);
|
||||
KeeShareSettings::ScopedCertificate certificate0;
|
||||
certificate0.path = "/path/0";
|
||||
certificate0.certificate = KeeShareSettings::Certificate{sshKey0, "Some <!> &#_\"\" weird string"};
|
||||
certificate0.trust = KeeShareSettings::Trust::Trusted;
|
||||
|
||||
KeeShareSettings::Certificate certificate0;
|
||||
certificate0.key = sshKey0;
|
||||
certificate0.signer = "signer";
|
||||
|
||||
KeeShareSettings::Key key0;
|
||||
key0.key = sshKey0;
|
||||
|
||||
auto sshKey1 = stubkey(1);
|
||||
KeeShareSettings::ScopedCertificate certificate1;
|
||||
certificate1.path = "/path/1";
|
||||
certificate1.certificate = KeeShareSettings::Certificate{sshKey1, "Another "};
|
||||
certificate1.trust = KeeShareSettings::Trust::Untrusted;
|
||||
|
||||
QTest::addColumn<bool>("importing");
|
||||
QTest::addColumn<bool>("exporting");
|
||||
QTest::addColumn<KeeShareSettings::Certificate>("ownCertificate");
|
||||
QTest::addColumn<KeeShareSettings::Key>("ownKey");
|
||||
QTest::addColumn<QList<KeeShareSettings::ScopedCertificate>>("foreignCertificates");
|
||||
QTest::newRow("1") << false << false << KeeShareSettings::Certificate() << KeeShareSettings::Key()
|
||||
<< QList<KeeShareSettings::ScopedCertificate>();
|
||||
QTest::newRow("2") << true << false << KeeShareSettings::Certificate() << KeeShareSettings::Key()
|
||||
<< QList<KeeShareSettings::ScopedCertificate>();
|
||||
QTest::newRow("3") << true << true << KeeShareSettings::Certificate() << KeeShareSettings::Key()
|
||||
<< QList<KeeShareSettings::ScopedCertificate>({certificate0, certificate1});
|
||||
QTest::newRow("4") << false << true << certificate0.certificate << key0
|
||||
<< QList<KeeShareSettings::ScopedCertificate>();
|
||||
QTest::newRow("5") << false << false << certificate0.certificate << key0
|
||||
<< QList<KeeShareSettings::ScopedCertificate>({certificate1});
|
||||
|
||||
QTest::newRow("1") << false << false << KeeShareSettings::Certificate() << KeeShareSettings::Key();
|
||||
QTest::newRow("2") << true << false << KeeShareSettings::Certificate() << KeeShareSettings::Key();
|
||||
QTest::newRow("3") << true << true << KeeShareSettings::Certificate() << KeeShareSettings::Key();
|
||||
QTest::newRow("4") << false << true << certificate0 << key0;
|
||||
QTest::newRow("5") << false << false << certificate0 << key0;
|
||||
}
|
||||
|
||||
const QSharedPointer<Botan::RSA_PrivateKey> TestSharing::stubkey(int index)
|
||||
|
@ -31,8 +31,6 @@ class TestSharing : public QObject
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void testNullObjects();
|
||||
void testCertificateSerialization();
|
||||
void testCertificateSerialization_data();
|
||||
void testKeySerialization();
|
||||
void testReferenceSerialization();
|
||||
void testReferenceSerialization_data();
|
||||
|
@ -1,214 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
|
||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "TestSignature.h"
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include "crypto/Crypto.h"
|
||||
#include "crypto/Random.h"
|
||||
#include "keeshare/Signature.h"
|
||||
|
||||
#include <botan/pem.h>
|
||||
#include <botan/rsa.h>
|
||||
|
||||
QTEST_GUILESS_MAIN(TestSignature)
|
||||
static const char* rsa_2_private = "-----BEGIN RSA PRIVATE KEY-----\n"
|
||||
"MIIEowIBAAKCAQEAwGdladnqFfcDy02Gubx4sdBT8NYEg2YKXfcKLSwca5gV4X7I\n"
|
||||
"z/+QR51LAfPCkj+QjWpj3DD1/6P7s6jOJ4BNd6CSDukv18DOsIsFn2D+zLmVoir2\n"
|
||||
"lki3sTsmiEz65KvHE8EnQ8IzZCqZDC40tZOcz2bnkZrmcsEibKoxYsmQJk95NwdR\n"
|
||||
"teFymp1qH3zq85xdNWw2u6c5CKzLgI5BjInttO98iSxL0KuY/JmzMx0gTbRiqc0x\n"
|
||||
"22EODtdVsBoNL/pt8v6Q+WLpRg4/Yq7YurAngxk4h38NWvufj2vJvbcRqX4cupcu\n"
|
||||
"92T9SWwSwZmd4Xy3bt+AUlq4XRMa1MlKfPvXmwIDAQABAoIBAGbWnRD/xaup1OBU\n"
|
||||
"dr9N6qD3/fXLHqxw3PeudEUCv8oOhxt43bK3IZH1k8LeXFA5I3VCuU9W6BWUu5Mi\n"
|
||||
"ldXtMPrQ22CW6NiEGLWqCP5QJMCeLUl5d0WKZoyXVhgiNTQGUKjRY8BGy5stXZJy\n"
|
||||
"HAA1fuooUXu09Jm/ezvjl/P6Uk722nZns4g6cc8aUSQDSVoeuCvwDaix5o4Z4RGY\n"
|
||||
"4biIKGj5qYxoe+rbgYH/2zlEcAiSJIuqjYY+Xk4IdB89DYZBYnO/xPkRaDeiY2xl\n"
|
||||
"QM7Inr7PQC8PWJc9zYYvlGnnmIRCkO15mWau70N1Y1rUAsyW61g2GyFhdsIIODH/\n"
|
||||
"878Kc9ECgYEA+2JaUqRWr6dqE+uVPpGkbGiAaRQ79olTcRmxXCnM+Y3c88z9G7kC\n"
|
||||
"2S5UKPRDl7EzwmMJqqb8BZbdSWoAxO4++F6ylSz7TqowPw+13Wxwm3wApvr2Q1Mo\n"
|
||||
"rkq4ltgyHMR+iXvKqOYa2GqZNmRwh7JGLIJ7Y0Z77nwBkkgDc/3ey8MCgYEAw+/N\n"
|
||||
"fxv2t+r6VKxEtjdy3sfn8LLjiWqghPngJzcYH9NdB8fmMN1WHqX075hbKjI9TyJw\n"
|
||||
"77p8onjZI0opLexHHUmepEa6Ijo1zynJJ7XPXnyruiTXXqz49io6lFOLcXi/i+DZ\n"
|
||||
"B2vQcMGWA4qwJxz7KA6EZ/HimjuysV1guvlKf0kCgYA6+JGTvXWQc0eRMLysFuJp\n"
|
||||
"hAJLpDGE3iYy7AINSskI6dyhXL8rl7UxWYroqJSKq0knGrCT1eRdM0zqAfH4QKOJ\n"
|
||||
"BD4EfK7ff1EeGgNh1CR+dRJ6GXlXxdRPPrwattDaqsW8Xsvl30UA69DRT7KOQqXv\n"
|
||||
"nxRu74P3KCP+OuKEfVOcnQKBgQC+/2r1Zj/5huBhW9BbQ/ABBSOuueMeGEfDeIUu\n"
|
||||
"FQG6PGKqbA2TQp9pnuMGECGGH5UuJ+epeMN36Y/ZW7iKoJGuFg7EGoHlTZMYj6Yb\n"
|
||||
"xJoRhDwuZy1eiATkicOyxUHf6hHme9dz6YA1+i+O4knWxuR5ZrVhUiRPrrQBO4JI\n"
|
||||
"oSwiqQKBgHblgOVfOJrG3HDg6bo+qmxQsGFRCD0mehsg9YpokuZVX0UJtGx/RJHU\n"
|
||||
"vIBL00An6YcNfPTSlNJeG83APtk/tdgsXvQd3pmIkeY78x6xWKSieZXv4lyDv7lX\n"
|
||||
"r28lCTj2Ez2gEzEohZgf4V1uzBvTdJefarpQ00ep34UZ9FsNfUwD\n"
|
||||
"-----END RSA PRIVATE KEY-----\n";
|
||||
static const char* rsa_2_public = "-----BEGIN RSA PUBLIC KEY-----\n"
|
||||
"MIIBCgKCAQEAwGdladnqFfcDy02Gubx4sdBT8NYEg2YKXfcKLSwca5gV4X7Iz/+Q\n"
|
||||
"R51LAfPCkj+QjWpj3DD1/6P7s6jOJ4BNd6CSDukv18DOsIsFn2D+zLmVoir2lki3\n"
|
||||
"sTsmiEz65KvHE8EnQ8IzZCqZDC40tZOcz2bnkZrmcsEibKoxYsmQJk95NwdRteFy\n"
|
||||
"mp1qH3zq85xdNWw2u6c5CKzLgI5BjInttO98iSxL0KuY/JmzMx0gTbRiqc0x22EO\n"
|
||||
"DtdVsBoNL/pt8v6Q+WLpRg4/Yq7YurAngxk4h38NWvufj2vJvbcRqX4cupcu92T9\n"
|
||||
"SWwSwZmd4Xy3bt+AUlq4XRMa1MlKfPvXmwIDAQAB\n"
|
||||
"-----END RSA PUBLIC KEY-----\n";
|
||||
static const char* rsa_2_sign = "4fda1b39f93f174cdc79ac2bd6118155359830c90e2c39b60a1a548852f2c87a"
|
||||
"cd61b2a378259a38befad35dbf208a2c1332ab74faf2cee2ff2e8be49c4c5f41"
|
||||
"dc10e5a5fafb53d3c54e2b7640d7bfee6bb0f24c5a1fb934150a144c2b465fe4"
|
||||
"8a1579e666a097fb1609ae9abc5760f6e6d6e73acb610fb11dd1c409ca284a72"
|
||||
"0be64dd56a28ab257e8721f5bade58816382581ac08d932098dd200d836fe897"
|
||||
"f78a5f02095ac3b21cca2a47b2afd282ce075c6450cba8c85b08b58c5bacb75d"
|
||||
"e1a73bdec4321193d4a3ce653d8e3aa8a4f2beac6a44497328f8855f7e28e15d"
|
||||
"f63b21f8bc7204bf6e202c9cb08be050379be5ad88d8e695a38440a50e75dfdf";
|
||||
static const char* rsa_1_private = "-----BEGIN RSA PRIVATE KEY-----\n"
|
||||
"MIIEpAIBAAKCAQEAsCHtJicDPWnvHSIKbnTZaJkIB9vgE0pmLdK580JUqBuonVbB\n"
|
||||
"y1QTy0ZQ7/TtqvLPgwPK88TR46OLO/QGCzo2+XxgJ85uy0xfuyUYRmSuw0drsErN\n"
|
||||
"mH8vU91lSBxsGDp9LtBbgHKoR23vMWZ34IxFRc55XphrIH48ijsMaL6bXBwF/3tD\n"
|
||||
"9T3lm2MpP1huyVNnIY9+GRRWCy4f9LMj/UGu/n4RtwwfpOZBBRwYkq5QkzA9lPm/\n"
|
||||
"VzF3MP1rKTMkvAw+Nfb383mkmc6MRnsa6uh6iDa9aVB7naegM13UJQX/PY1Ks6pO\n"
|
||||
"XDpy/MQ7iCh+HmYNq5dRmARyaNl9xIXJNhz1cQIDAQABAoIBAQCnEUc1LUQxeM5K\n"
|
||||
"wANNCqE+SgoIClPdeHC7fmrLh1ttqe6ib6ybBUFRS31yXs0hnfefunVEDKlaV8K2\n"
|
||||
"N52UAMAsngFHQNRvGh6kEWeZPd9Xc+N98TZbNCjcT+DGKc+Om8wqH5DrodZlCq4c\n"
|
||||
"GaoT4HnE4TjWtZTH2XXrWF9I66PKFWf070R44nvyVcvaZi4pC2YmURRPuGF6K1iK\n"
|
||||
"dH8zM6HHG1UGu2W6hLNn+K01IulG0Lb8eWNaNYMmtQWaxyp7I2IWkkecUs3nCuiR\n"
|
||||
"byFOoomCjdh8r9yZFvwxjGUhgtkALN9GCU0Mwve+s11IB2gevruN+q9/Qejbyfdm\n"
|
||||
"IlgLAeTRAoGBANRcVzW9CYeobCf+U9hKJFEOur8XO+J2mTMaELA0EjWpTJFAeIT7\n"
|
||||
"KeRpCRG4/vOSklxxRF6vP1EACA4Z+5BlN+FTipHHs+bSEgqkPZiiANDH7Zot5Iqv\n"
|
||||
"1q0fRyldNRZNZK7DWp08BPNVWGA/EnEuKJiURxnxBaxNXbUyMCdjxvMvAoGBANRT\n"
|
||||
"utbrqS/bAa/DcHKn3V6DRqBl3TDOfvCNjiKC84a67F2uXgzLIdMktr4d1NyCZVJd\n"
|
||||
"7/zVgWORLIdg1eAi6rYGoOvNV39wwga7CF+m9sBY0wAaKYCELe6L26r4aQHVCX6n\n"
|
||||
"rnIgUv+4o4itmU2iP0r3wlmDC9pDRQP82vfvQPlfAoGASwhleANW/quvq2HdViq8\n"
|
||||
"Mje2HBalfhrRfpDTHK8JUBSFjTzuWG42GxJRtgVbb8x2ElujAKGDCaetMO5VSGu7\n"
|
||||
"Fs5hw6iAFCpdXY0yhl+XUi2R8kwM2EPQ4lKO3jqkq0ClNmqn9a5jQWcCVt9yMLNS\n"
|
||||
"fLbHeI8EpiCf34ngIcrLXNkCgYEAzlcEZuKkC46xB+dNew8pMTUwSKZVm53BfPKD\n"
|
||||
"44QRN6imFbBjU9mAaJnwQbfp6dWKs834cGPolyM4++MeVfB42iZ88ksesgmZdUMD\n"
|
||||
"szkl6O0pOJs0I+HQZVdjRbadDZvD22MHQ3+oST1dJ3FVXz3Cdo9qPuT8esMO6f4r\n"
|
||||
"qfDH2s8CgYAXC/lWWHQ//PGP0pH4oiEXisx1K0X1u0xMGgrChxBRGRiKZUwNMIvJ\n"
|
||||
"TqUu7IKizK19cLHF/NBvxHYHFw+m7puNjn6T1RtRCUjRZT7Dx1VHfVosL9ih5DA8\n"
|
||||
"tpbZA5KGKcvHtB5DDgT0MHwzBZnb4Q//Rhovzn+HXZPsJTTgHHy3NQ==\n"
|
||||
"-----END RSA PRIVATE KEY-----\n";
|
||||
static const char* rsa_1_public = "-----BEGIN RSA PUBLIC KEY-----\n"
|
||||
"MIIBCgKCAQEAsCHtJicDPWnvHSIKbnTZaJkIB9vgE0pmLdK580JUqBuonVbBy1QT\n"
|
||||
"y0ZQ7/TtqvLPgwPK88TR46OLO/QGCzo2+XxgJ85uy0xfuyUYRmSuw0drsErNmH8v\n"
|
||||
"U91lSBxsGDp9LtBbgHKoR23vMWZ34IxFRc55XphrIH48ijsMaL6bXBwF/3tD9T3l\n"
|
||||
"m2MpP1huyVNnIY9+GRRWCy4f9LMj/UGu/n4RtwwfpOZBBRwYkq5QkzA9lPm/VzF3\n"
|
||||
"MP1rKTMkvAw+Nfb383mkmc6MRnsa6uh6iDa9aVB7naegM13UJQX/PY1Ks6pOXDpy\n"
|
||||
"/MQ7iCh+HmYNq5dRmARyaNl9xIXJNhz1cQIDAQAB\n"
|
||||
"-----END RSA PUBLIC KEY-----\n";
|
||||
|
||||
static QByteArray data("Some trivial test with a longer .... ................................. longer text");
|
||||
|
||||
QSharedPointer<Botan::Private_Key> 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<Botan::Private_Key>(key);
|
||||
} catch (std::exception& e) {
|
||||
qWarning("Failed to load key: %s", e.what());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
QSharedPointer<Botan::Public_Key> 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<uint8_t>(der.begin(), der.end()));
|
||||
return QSharedPointer<Botan::Public_Key>(key);
|
||||
} catch (std::exception& e) {
|
||||
qWarning("Failed to load key: %s", e.what());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void TestSignature::initTestCase()
|
||||
{
|
||||
QVERIFY(Crypto::init());
|
||||
}
|
||||
|
||||
void TestSignature::testSigningOpenSSH_RSA_PrivateOnly()
|
||||
{
|
||||
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)));
|
||||
|
||||
const bool verified = Signature::verify(data, rsaKey, sign);
|
||||
QCOMPARE(verified, true);
|
||||
}
|
||||
|
||||
void TestSignature::testSigningOpenSSH_RSA()
|
||||
{
|
||||
auto privateKey = loadPrivateKey(rsa_2_private);
|
||||
QVERIFY(privateKey);
|
||||
|
||||
QString sign;
|
||||
Signature::create(data, privateKey, sign);
|
||||
QVERIFY(!sign.isEmpty());
|
||||
|
||||
auto publicKey = loadPublicKey(rsa_2_public);
|
||||
QVERIFY(publicKey);
|
||||
|
||||
const bool verified = Signature::verify(data, publicKey, sign);
|
||||
QCOMPARE(verified, true);
|
||||
}
|
||||
|
||||
void TestSignature::testSigningGenerated_RSA_PrivateOnly()
|
||||
{
|
||||
QSharedPointer<Botan::Private_Key> key(new Botan::RSA_PrivateKey(*randomGen()->getRng(), 2048));
|
||||
|
||||
QString sign;
|
||||
Signature::create(data, key, sign);
|
||||
QVERIFY(!sign.isEmpty());
|
||||
|
||||
const bool verified = Signature::verify(data, key, sign);
|
||||
QCOMPARE(verified, true);
|
||||
}
|
||||
|
||||
void TestSignature::testSigningTest_RSA_PrivateOnly()
|
||||
{
|
||||
auto rsaKey = loadPrivateKey(rsa_2_private);
|
||||
QVERIFY(rsaKey);
|
||||
|
||||
QString sign;
|
||||
Signature::create(data, rsaKey, sign);
|
||||
QVERIFY(!sign.isEmpty());
|
||||
|
||||
const bool verified = Signature::verify(data, rsaKey, sign);
|
||||
QCOMPARE(verified, true);
|
||||
}
|
||||
|
||||
void TestSignature::testSigningTest_RSA()
|
||||
{
|
||||
auto privateKey = loadPrivateKey(rsa_1_private);
|
||||
QVERIFY(privateKey);
|
||||
|
||||
QString sign;
|
||||
Signature::create(data, privateKey, sign);
|
||||
QVERIFY(!sign.isEmpty());
|
||||
|
||||
auto publicKey = loadPublicKey(rsa_1_public);
|
||||
QVERIFY(publicKey);
|
||||
|
||||
const bool verified = Signature::verify(data, publicKey, sign);
|
||||
QCOMPARE(verified, true);
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
|
||||
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
|
||||
*
|
||||
* 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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef KEEPASSXC_TESTSIGNATURE_H
|
||||
#define KEEPASSXC_TESTSIGNATURE_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class TestSignature : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
|
||||
void testSigningOpenSSH_RSA_PrivateOnly();
|
||||
void testSigningOpenSSH_RSA();
|
||||
|
||||
void testSigningGenerated_RSA_PrivateOnly();
|
||||
|
||||
void testSigningTest_RSA_PrivateOnly();
|
||||
void testSigningTest_RSA();
|
||||
};
|
||||
|
||||
#endif // KEEPASSX_TESTSIGNATURE_H
|
Loading…
Reference in New Issue
Block a user