diff --git a/CMakeLists.txt b/CMakeLists.txt index d8c0acc1b..048d30323 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/INSTALL.md b/INSTALL.md index ca2c5ba5c..b25dd38b8 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -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) diff --git a/cmake/FindMinizip.cmake b/cmake/FindMinizip.cmake new file mode 100644 index 000000000..a3817c960 --- /dev/null +++ b/cmake/FindMinizip.cmake @@ -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) diff --git a/cmake/FindQuaZip.cmake b/cmake/FindQuaZip.cmake deleted file mode 100644 index 2242baca4..000000000 --- a/cmake/FindQuaZip.cmake +++ /dev/null @@ -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) diff --git a/share/translations/keepassxc_en.ts b/share/translations/keepassxc_en.ts index dc58e114d..320296028 100644 --- a/share/translations/keepassxc_en.ts +++ b/share/translations/keepassxc_en.ts @@ -3083,10 +3083,6 @@ Would you like to correct it? Inactive Inactive - - KeeShare unsigned container - KeeShare unsigned container - KeeShare signed container KeeShare signed container @@ -3173,6 +3169,10 @@ Supported extensions are: %1. Browse… + + KeeShare container + + EditGroupWidgetMain @@ -6742,18 +6742,6 @@ Kernel: %3 %4 Auto-Type Auto-Type - - KeeShare (signed and unsigned sharing) - - - - KeeShare (only signed sharing) - - - - KeeShare (only unsigned sharing) - - YubiKey @@ -7641,6 +7629,10 @@ Please consider generating a new key file. %1 characters + + KeeShare + + QtIOCompressor @@ -8243,91 +8235,14 @@ Please consider generating a new key file. Fingerprint: Fingerprint: - - Signer - Signer - Generate Generate - - Import - Import - - - Export - Export - - - Imported certificates - Imported certificates - - - Trust - Trust - - - Ask - Ask - - - Untrust - Untrust - - - Remove - Remove - - - Path - Path - - - Status - Status - Fingerprint Fingerprint - - Trusted - Trusted - - - Untrusted - Untrusted - - - Unknown - Unknown - - - key.share - Filetype for KeeShare key - key.share - - - KeeShare key file - KeeShare key file - - - All files - All files - - - Select path - Select path - - - Exporting changed certificate - Exporting changed certificate - - - The exported certificate is not the same as the one in use. Do you want to export the current certificate? - The exported certificate is not the same as the one in use. Do you want to export the current certificate? - Signer: @@ -8352,132 +8267,18 @@ Please consider generating a new key file. Generate new certificate - - Import existing certificate - - - - Export own certificate - - - - Known shares - - - - Trust selected certificate - - - - Ask whether to trust the selected certificate every time - - - - Untrust selected certificate - - - - Remove selected certificate - - ShareExport - Overwriting signed share container is not supported - export prevented - Overwriting signed share container is not supported - export prevented - - - Could not write export container (%1) - Could not write export container (%1) - - - Could not embed signature: Could not open file to write (%1) + Could not write export container. - - Could not embed signature: Could not write file (%1) - - - - Could not embed database: Could not open file to write (%1) - - - - Could not embed database: Could not write file (%1) - - - - Overwriting unsigned share container is not supported - export prevented - Overwriting unsigned share container is not supported - export prevented - - - Could not write export container - Could not write export container - ShareImport - Not this time - Not this time - - - Never - Never - - - Always - Always - - - Just this time - Just this time - - - Signed share container are not supported - import prevented - Signed share container are not supported - import prevented - - - File is not readable - File is not readable - - - Invalid sharing container - Invalid sharing container - - - Untrusted import prevented - Untrusted import prevented - - - Successful signed import - Successful signed import - - - Unsigned share container are not supported - import prevented - Unsigned share container are not supported - import prevented - - - Successful unsigned import - Successful unsigned import - - - File does not exist - File does not exist - - - KeeShare Import - - - - The source of the shared container cannot be verified because it is not signed. Do you really want to import from %1? - - - - Do you want to trust %1 with certificate fingerprint: -%2 -%3 + Successful import diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml index fc58dfda3..6ca739e85 100644 --- a/snap/snapcraft.yaml +++ b/snap/snapcraft.yaml @@ -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 diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e9a07073f..1b08910d7 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -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) diff --git a/src/config-keepassx.h.cmake b/src/config-keepassx.h.cmake index 6aceaa2a0..53c3f03cb 100644 --- a/src/config-keepassx.h.cmake +++ b/src/config-keepassx.h.cmake @@ -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 diff --git a/src/core/Tools.cpp b/src/core/Tools.cpp index 61c8cb6e2..59ddf9e30 100644 --- a/src/core/Tools.cpp +++ b/src/core/Tools.cpp @@ -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"); diff --git a/src/keeshare/CMakeLists.txt b/src/keeshare/CMakeLists.txt index af4bc61a8..9fcc55dc2 100644 --- a/src/keeshare/CMakeLists.txt +++ b/src/keeshare/CMakeLists.txt @@ -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) diff --git a/src/keeshare/KeeShare.cpp b/src/keeshare/KeeShare.cpp index 7f512c6d9..2dec75502 100644 --- a/src/keeshare/KeeShare.cpp +++ b/src/keeshare/KeeShare.cpp @@ -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); } diff --git a/src/keeshare/KeeShare.h b/src/keeshare/KeeShare.h index d9d580e01..606947da3 100644 --- a/src/keeshare/KeeShare.h +++ b/src/keeshare/KeeShare.h @@ -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); diff --git a/src/keeshare/KeeShareSettings.cpp b/src/keeshare/KeeShareSettings.cpp index 601b05310..4f53fc25f 100644 --- a/src/keeshare/KeeShareSettings.cpp +++ b/src/keeshare/KeeShareSettings.cpp @@ -23,8 +23,8 @@ #include "core/Metadata.h" #include "crypto/Random.h" #include "gui/DatabaseIcons.h" -#include "keeshare/Signature.h" +#include #include #include @@ -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(sign.certificate.key.data()); + + std::vector rsaE(rsaKey->get_e().bytes()); + rsaKey->get_e().binary_encode(rsaE.data()); + + std::vector 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(rsaE.data()), rsaE.size()); + stream.writeBytes(reinterpret_cast(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 diff --git a/src/keeshare/KeeShareSettings.h b/src/keeshare/KeeShareSettings.h index ad6ca9c5b..b81dc15af 100644 --- a/src/keeshare/KeeShareSettings.h +++ b/src/keeshare/KeeShareSettings.h @@ -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 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 diff --git a/src/keeshare/SettingsWidgetKeeShare.cpp b/src/keeshare/SettingsWidgetKeeShare.cpp index 1dcb4ad00..7462aa5f3 100644 --- a/src/keeshare/SettingsWidgetKeeShare.cpp +++ b/src/keeshare/SettingsWidgetKeeShare.cpp @@ -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 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(); -} diff --git a/src/keeshare/SettingsWidgetKeeShare.h b/src/keeshare/SettingsWidgetKeeShare.h index bb29e8937..fcc920bab 100644 --- a/src/keeshare/SettingsWidgetKeeShare.h +++ b/src/keeshare/SettingsWidgetKeeShare.h @@ -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 m_ui; KeeShareSettings::Own m_own; - KeeShareSettings::Foreign m_foreign; - QScopedPointer m_importedCertificateModel; }; #endif // KEEPASSXC_SETTINGSWIDGETKEESHARE_H diff --git a/src/keeshare/SettingsWidgetKeeShare.ui b/src/keeshare/SettingsWidgetKeeShare.ui index bd0b98a85..48a79d8d3 100644 --- a/src/keeshare/SettingsWidgetKeeShare.ui +++ b/src/keeshare/SettingsWidgetKeeShare.ui @@ -6,11 +6,11 @@ 0 0 - 378 - 511 + 425 + 251 - + 0 @@ -70,67 +70,55 @@ - - - 0 - 0 - - Own certificate - - 10 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Generate new certificate - - - Generate - - - - - - - Import existing certificate - - - Import - - - - - - - Export own certificate - - - Export - - - - + + + + Signer: + + - + + + + Generate new certificate + + + Generate + + + + + + + Fingerprint: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Signer name field + + + + Fingerprint @@ -140,133 +128,6 @@ - - - - Signer name field - - - - - - - Signer: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Fingerprint: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - Imported certificates - - - - - - Known shares - - - QAbstractItemView::NoEditTriggers - - - false - - - false - - - true - - - QAbstractItemView::MultiSelection - - - QAbstractItemView::SelectRows - - - true - - - true - - - false - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Trust selected certificate - - - Trust - - - - - - - Ask whether to trust the selected certificate every time - - - Ask - - - - - - - Untrust selected certificate - - - Untrust - - - - - - - Remove selected certificate - - - Remove - - - - - @@ -289,16 +150,8 @@ enableImportCheckBox quietSuccessCheckBox enableExportCheckBox - generateOwnCerticateButton - importOwnCertificateButton - exportOwnCertificateButton ownCertificateSignerEdit ownCertificateFingerprintEdit - trustImportedCertificateButton - askImportedCertificateButton - untrustImportedCertificateButton - removeImportedCertificateButton - importedCertificateTableView diff --git a/src/keeshare/ShareExport.cpp b/src/keeshare/ShareExport.cpp index 31f7fece3..55c4ca711 100644 --- a/src/keeshare/ShareExport.cpp +++ b/src/keeshare/ShareExport.cpp @@ -14,23 +14,20 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + #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 -#include - -#if defined(WITH_XC_KEESHARE_SECURE) -#include -#include -#endif +#include +#include namespace { @@ -64,8 +61,6 @@ namespace auto* targetDb = new Database(); auto* targetMetadata = targetDb->metadata(); targetMetadata->setRecycleBinEnabled(false); - auto key = QSharedPointer::create(); - key->addKey(QSharedPointer::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::create(); + key->addKey(QSharedPointer::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(data.constData()), data.size()); + auto s = signer.signature(*randomGen()->getRng()); + auto hex = QByteArray(reinterpret_cast(s.data()), s.size()).toHex(); + signature = QString("rsa|%1").arg(QString::fromLatin1(hex)); + return true; + } catch (std::exception& e) { + qWarning("KeeShare: Failed to sign data: %s", e.what()); + return false; + } + } + qWarning("Unsupported Public/Private key format"); + return false; + } } // namespace ShareObserver::Result ShareExport::intoContainer(const QString& resolvedPath, const KeeShareSettings::Reference& reference, const Group* group) { - QScopedPointer 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 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}; } diff --git a/src/keeshare/ShareImport.cpp b/src/keeshare/ShareImport.cpp index 309ff29af..71d02b74f 100644 --- a/src/keeshare/ShareImport.cpp +++ b/src/keeshare/ShareImport.cpp @@ -15,324 +15,87 @@ * along with this program. If not, see . */ #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 -#include -#include -#include - -#if defined(WITH_XC_KEESHARE_SECURE) -#include -#include -#endif +#include namespace { - enum Trust + QByteArray readZipFile(void* uf) { - Invalid, - Own, - UntrustedForever, - UntrustedOnce, - TrustedOnce, - TrustedForever, - }; - - QPair - check(QByteArray& data, - const KeeShareSettings::Reference& reference, - const KeeShareSettings::Certificate& ownCertificate, - const QList& 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() << KeeShare::signatureFileName() << KeeShare::containerFileName(); - const auto files = zip.getFileInfoList(); - QSet 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::create(); - key->addKey(QSharedPointer::create(reference.password)); - auto sourceDb = QSharedPointer::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::create(); - key->addKey(QSharedPointer::create(reference.password)); - auto sourceDb = QSharedPointer::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::create(); + key->addKey(QSharedPointer::create(reference.password)); + auto sourceDb = QSharedPointer::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 {}; } diff --git a/src/keeshare/ShareObserver.cpp b/src/keeshare/ShareObserver.cpp index a30cced2d..ac1c44baa 100644 --- a/src/keeshare/ShareObserver.cpp +++ b/src/keeshare/ShareObserver.cpp @@ -278,13 +278,14 @@ QList 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) { diff --git a/src/keeshare/Signature.cpp b/src/keeshare/Signature.cpp deleted file mode 100644 index 029ca97ef..000000000 --- a/src/keeshare/Signature.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2018 KeePassXC Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "Signature.h" - -#include "crypto/Random.h" - -#include - -bool Signature::create(const QByteArray& data, QSharedPointer key, QString& signature) -{ - // TODO HNH: currently we publish the signature in our own non-standard format - it would - // be better to use a standard format (like ASN1 - but this would be more easy - // when we integrate a proper library) - // Even more, we could publish standard self signed certificates with the container - // instead of the custom certificates - if (key->algo_name() == "RSA") { - try { - Botan::PK_Signer signer(*key, "EMSA3(SHA-256)"); - signer.update(reinterpret_cast(data.constData()), data.size()); - auto s = signer.signature(*randomGen()->getRng()); - - auto hex = QByteArray(reinterpret_cast(s.data()), s.size()).toHex(); - signature = QString("rsa|%1").arg(QString::fromLatin1(hex)); - return true; - } catch (std::exception& e) { - qWarning("KeeShare: Failed to sign data: %s", e.what()); - return false; - } - } - qWarning("Unsupported Public/Private key format"); - return false; -} - -bool Signature::verify(const QByteArray& data, QSharedPointer key, const QString& signature) -{ - if (key && key->algo_name() == "RSA") { - QRegExp extractor("rsa\\|([a-f0-9]+)", Qt::CaseInsensitive); - if (!extractor.exactMatch(signature) || extractor.captureCount() != 1) { - qWarning("Could not unpack signature parts"); - return false; - } - const QByteArray sig_s = QByteArray::fromHex(extractor.cap(1).toLatin1()); - - try { - Botan::PK_Verifier verifier(*key, "EMSA3(SHA-256)"); - verifier.update(reinterpret_cast(data.constData()), data.size()); - return verifier.check_signature(reinterpret_cast(sig_s.constData()), sig_s.size()); - } catch (std::exception& e) { - qWarning("KeeShare: Failed to verify signature: %s", e.what()); - return false; - } - } - qWarning("Unsupported Public/Private key format"); - return false; -} diff --git a/src/keeshare/Signature.h b/src/keeshare/Signature.h deleted file mode 100644 index 9c4f83bba..000000000 --- a/src/keeshare/Signature.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright (C) 2018 KeePassXC Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef KEEPASSXC_SIGNATURE_H -#define KEEPASSXC_SIGNATURE_H - -#include -#include -#include - -namespace Signature -{ - bool create(const QByteArray& data, QSharedPointer key, QString& signature); - bool verify(const QByteArray& data, QSharedPointer key, const QString& signature); -}; // namespace Signature - -#endif // KEEPASSXC_SIGNATURE_H diff --git a/src/keeshare/group/EditGroupWidgetKeeShare.cpp b/src/keeshare/group/EditGroupWidgetKeeShare.cpp index 6067ec412..82f8dd1c0 100644 --- a/src/keeshare/group/EditGroupWidgetKeeShare.cpp +++ b/src/keeshare/group/EditGroupWidgetKeeShare.cpp @@ -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; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 88e907ebc..325ff880e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -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}) diff --git a/tests/TestSharing.cpp b/tests/TestSharing.cpp index 865ce9a41..14a72b142 100644 --- a/tests/TestSharing.cpp +++ b/tests/TestSharing.cpp @@ -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) 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("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, 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("importing"); QTest::addColumn("exporting"); QTest::addColumn("ownCertificate"); QTest::addColumn("ownKey"); - QTest::addColumn>("foreignCertificates"); - QTest::newRow("1") << false << false << KeeShareSettings::Certificate() << KeeShareSettings::Key() - << QList(); - QTest::newRow("2") << true << false << KeeShareSettings::Certificate() << KeeShareSettings::Key() - << QList(); - QTest::newRow("3") << true << true << KeeShareSettings::Certificate() << KeeShareSettings::Key() - << QList({certificate0, certificate1}); - QTest::newRow("4") << false << true << certificate0.certificate << key0 - << QList(); - QTest::newRow("5") << false << false << certificate0.certificate << key0 - << QList({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 TestSharing::stubkey(int index) diff --git a/tests/TestSharing.h b/tests/TestSharing.h index bba627f64..cfb521e02 100644 --- a/tests/TestSharing.h +++ b/tests/TestSharing.h @@ -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(); diff --git a/tests/TestSignature.cpp b/tests/TestSignature.cpp deleted file mode 100644 index 1c454504e..000000000 --- a/tests/TestSignature.cpp +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Copyright (C) 2010 Felix Geyer - * Copyright (C) 2017 KeePassXC Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "TestSignature.h" - -#include - -#include "crypto/Crypto.h" -#include "crypto/Random.h" -#include "keeshare/Signature.h" - -#include -#include - -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 loadPrivateKey(const QString& pem) -{ - try { - std::string label; - auto der = Botan::PEM_Code::decode(pem.toStdString(), label); - auto key = new Botan::RSA_PrivateKey( - Botan::AlgorithmIdentifier("RSA", Botan::AlgorithmIdentifier::USE_NULL_PARAM), der); - return QSharedPointer(key); - } catch (std::exception& e) { - qWarning("Failed to load key: %s", e.what()); - return {}; - } -} - -QSharedPointer loadPublicKey(const QString& pem) -{ - try { - std::string label; - auto der = Botan::PEM_Code::decode(pem.toStdString(), label); - auto key = - new Botan::RSA_PublicKey(Botan::AlgorithmIdentifier("RSA", Botan::AlgorithmIdentifier::USE_NULL_PARAM), - std::vector(der.begin(), der.end())); - return QSharedPointer(key); - } catch (std::exception& e) { - qWarning("Failed to load key: %s", e.what()); - return {}; - } -} - -void TestSignature::initTestCase() -{ - QVERIFY(Crypto::init()); -} - -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 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); -} diff --git a/tests/TestSignature.h b/tests/TestSignature.h deleted file mode 100644 index 38e576462..000000000 --- a/tests/TestSignature.h +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2010 Felix Geyer - * Copyright (C) 2017 KeePassXC Team - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 2 or (at your option) - * version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#ifndef KEEPASSXC_TESTSIGNATURE_H -#define KEEPASSXC_TESTSIGNATURE_H - -#include - -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