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:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user