Page MenuHome GnuPG

No OneTemporary

diff --git a/src/ui/openpgpcertificatecreationdialog.cpp b/src/ui/openpgpcertificatecreationdialog.cpp
index abc2ebc5..d096fc32 100644
--- a/src/ui/openpgpcertificatecreationdialog.cpp
+++ b/src/ui/openpgpcertificatecreationdialog.cpp
@@ -1,452 +1,480 @@
/* -*- mode: c++; c-basic-offset:4 -*-
This file is part of Libkleo.
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "openpgpcertificatecreationdialog.h"
#include "animatedexpander_p.h"
#include "nameandemailwidget.h"
#include "openpgpcertificatecreationconfig.h"
#include "utils/compat.h"
#include "utils/compliance.h"
+#include "utils/cryptoconfig.h"
#include "utils/expiration.h"
#include "utils/gnupg.h"
#include "utils/keyparameters.h"
#include "utils/keyusage.h"
#include <KAdjustingScrollArea>
#include <KConfigGroup>
#include <KDateComboBox>
#include <KLocalizedString>
#include <KMessageBox>
#include <KSeparator>
#include <KSharedConfig>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QLabel>
#include <QPushButton>
#include <QVBoxLayout>
#include <QGpgME/CryptoConfig>
#include <QGpgME/Protocol>
+#include <gpgme++/key.h>
+
#include "libkleo_debug.h"
using namespace Kleo;
+using namespace GpgME;
using namespace Qt::Literals::StringLiterals;
static bool unlimitedValidityIsAllowed()
{
return !Kleo::Expiration::maximumExpirationDate().isValid();
}
class OpenPGPCertificateCreationDialog::Private
{
friend class ::Kleo::OpenPGPCertificateCreationDialog;
OpenPGPCertificateCreationDialog *const q;
struct UI {
QLabel *infoLabel;
KAdjustingScrollArea *scrollArea;
NameAndEmailWidget *nameAndEmail;
QCheckBox *withPassCheckBox;
QDialogButtonBox *buttonBox;
QCheckBox *expiryCB;
QLabel *expiryLabel;
KDateComboBox *expiryDE;
QComboBox *keyAlgoCB;
QLabel *keyAlgoLabel;
AnimatedExpander *expander;
UI(QWidget *dialog)
{
auto mainLayout = new QVBoxLayout{dialog};
infoLabel = new QLabel{dialog};
infoLabel->setWordWrap(true);
mainLayout->addWidget(infoLabel);
mainLayout->addWidget(new KSeparator{Qt::Horizontal, dialog});
scrollArea = new KAdjustingScrollArea{dialog};
scrollArea->setFocusPolicy(Qt::NoFocus);
scrollArea->setFrameStyle(QFrame::NoFrame);
scrollArea->setBackgroundRole(dialog->backgroundRole());
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
scrollArea->setSizeAdjustPolicy(QScrollArea::AdjustToContents);
auto widget = new QWidget;
scrollArea->setWidget(widget);
auto scrollAreaLayout = new QVBoxLayout(widget);
scrollAreaLayout->setContentsMargins(0, 0, 0, 0);
nameAndEmail = new NameAndEmailWidget{dialog};
nameAndEmail->layout()->setContentsMargins(0, 0, 0, 0);
scrollAreaLayout->addWidget(nameAndEmail);
withPassCheckBox = new QCheckBox{i18n("Protect the generated key with a passphrase."), dialog};
withPassCheckBox->setToolTip(
i18n("Encrypts the secret key with an unrecoverable passphrase. You will be asked for the passphrase during key generation."));
scrollAreaLayout->addWidget(withPassCheckBox);
expander = new AnimatedExpander(i18n("Advanced options"), {}, dialog);
scrollAreaLayout->addWidget(expander);
auto advancedLayout = new QVBoxLayout;
expander->setContentLayout(advancedLayout);
keyAlgoLabel = new QLabel(dialog);
keyAlgoLabel->setText(i18nc("The algorithm and strength of encryption key", "Key Material"));
auto font = keyAlgoLabel->font();
font.setBold(true);
keyAlgoLabel->setFont(font);
advancedLayout->addWidget(keyAlgoLabel);
keyAlgoCB = new QComboBox(dialog);
keyAlgoLabel->setBuddy(keyAlgoCB);
advancedLayout->addWidget(keyAlgoCB);
{
auto hbox = new QHBoxLayout;
expiryCB = new QCheckBox{dialog};
expiryCB->setAccessibleName(Expiration::validUntilLabel());
hbox->addWidget(expiryCB);
expiryLabel = new QLabel{Expiration::validUntilLabel(), dialog};
hbox->addWidget(expiryLabel);
expiryDE = new KDateComboBox(dialog);
hbox->addWidget(expiryDE, 1);
advancedLayout->addLayout(hbox);
}
scrollAreaLayout->addStretch(1);
mainLayout->addWidget(scrollArea);
mainLayout->addWidget(new KSeparator{Qt::Horizontal, dialog});
buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok | QDialogButtonBox::Cancel, dialog};
mainLayout->addWidget(buttonBox);
}
} ui;
public:
explicit Private(OpenPGPCertificateCreationDialog *qq)
: q{qq}
, ui{qq}
, technicalParameters{KeyParameters::OpenPGP}
{
q->setWindowTitle(i18nc("title:window", "Create OpenPGP Certificate"));
OpenPGPCertificateCreationConfig settings;
const auto requiredFields = settings.requiredFields();
const auto nameIsRequired = requiredFields.contains(QLatin1StringView{"NAME!"}, Qt::CaseInsensitive);
const auto emailIsRequired = requiredFields.contains(QLatin1StringView{"EMAIL!"}, Qt::CaseInsensitive);
ui.infoLabel->setText(nameIsRequired || emailIsRequired //
? i18n("Enter a name and an email address to use for the certificate.")
: i18n("Enter a name and/or an email address to use for the certificate."));
ui.nameAndEmail->setNameIsRequired(nameIsRequired);
ui.nameAndEmail->setNameLabel(settings.nameLabel());
const auto nameHint = settings.nameHint();
ui.nameAndEmail->setNameHint(nameHint.isEmpty() ? settings.namePlaceholder() : nameHint);
ui.nameAndEmail->setNamePattern(settings.nameRegex());
ui.nameAndEmail->setEmailIsRequired(emailIsRequired);
ui.nameAndEmail->setEmailLabel(settings.emailLabel());
const auto emailHint = settings.emailHint();
ui.nameAndEmail->setEmailHint(emailHint.isEmpty() ? settings.emailPlaceholder() : emailHint);
ui.nameAndEmail->setEmailPattern(settings.emailRegex());
ui.expander->setVisible(!settings.hideAdvanced());
const auto conf = QGpgME::cryptoConfig();
const auto entry = getCryptoConfigEntry(conf, "gpg-agent", "enforce-passphrase-constraints");
if (entry && entry->boolValue()) {
qCDebug(LIBKLEO_LOG) << "Disabling passphrase check box because of agent config.";
ui.withPassCheckBox->setEnabled(false);
ui.withPassCheckBox->setChecked(true);
} else {
ui.withPassCheckBox->setChecked(settings.withPassphrase());
ui.withPassCheckBox->setEnabled(!settings.isWithPassphraseImmutable());
}
connect(ui.buttonBox, &QDialogButtonBox::accepted, q, [this]() {
checkAccept();
});
connect(ui.buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject);
for (const auto &algorithm : DeVSCompliance::isActive() ? DeVSCompliance::compliantAlgorithms() : availableAlgorithms()) {
ui.keyAlgoCB->addItem(QString::fromStdString(algorithm), QString::fromStdString(algorithm));
}
auto cryptoConfig = QGpgME::cryptoConfig();
if (cryptoConfig) {
auto pubkeyEntry = getCryptoConfigEntry(QGpgME::cryptoConfig(), "gpg", "default_pubkey_algo");
if (pubkeyEntry) {
auto algo = pubkeyEntry->stringValue().split(QLatin1Char('/'))[0];
if (algo == QLatin1StringView("ed25519")) {
algo = QStringLiteral("curve25519");
} else if (algo == QLatin1StringView("ed448")) {
algo = QStringLiteral("curve448");
}
auto index = ui.keyAlgoCB->findData(algo);
if (index != -1) {
ui.keyAlgoCB->setCurrentIndex(index);
} else {
ui.keyAlgoCB->setCurrentIndex(0);
}
} else {
ui.keyAlgoCB->setCurrentIndex(0);
}
} else {
ui.keyAlgoCB->setCurrentIndex(0);
}
Kleo::Expiration::setUpExpirationDateComboBox(ui.expiryDE);
ui.expiryCB->setEnabled(true);
setExpiryDate(defaultExpirationDate(Kleo::Expiration::ExpirationOnUnlimitedValidity::InternalDefaultExpiration));
if (unlimitedValidityIsAllowed()) {
ui.expiryLabel->setEnabled(ui.expiryCB->isChecked());
ui.expiryDE->setEnabled(ui.expiryCB->isChecked());
} else {
ui.expiryCB->setEnabled(false);
ui.expiryCB->setVisible(false);
}
connect(ui.expiryCB, &QAbstractButton::toggled, q, [this](bool checked) {
ui.expiryLabel->setEnabled(checked);
ui.expiryDE->setEnabled(checked);
if (checked && !ui.expiryDE->isValid()) {
setExpiryDate(defaultExpirationDate(Kleo::Expiration::ExpirationOnUnlimitedValidity::InternalDefaultExpiration));
}
updateTechnicalParameters();
});
connect(ui.expiryDE, &KDateComboBox::dateChanged, q, [this]() {
updateTechnicalParameters();
});
connect(ui.keyAlgoCB, &QComboBox::currentIndexChanged, q, [this]() {
updateTechnicalParameters();
});
updateTechnicalParameters(); // set key parameters to default values for OpenPGP
connect(ui.expander, &AnimatedExpander::startExpanding, q, [this]() {
q->resize(std::max(q->sizeHint().width(), ui.expander->contentWidth()) + 20, q->sizeHint().height() + ui.expander->contentHeight() + 20);
});
+
+ KConfigGroup config(KSharedConfig::openConfig(u"kleopatrarc"_s), u"CertificateCreationWizard"_s);
+ // This is a legacy config option. "RSA" and any unknown value set the algorithm to RSA; all other options are no longer supported by kleo (dsa, elg).
+ // So we just have to check whether the value is set to *anything*.
+ if (!config.readEntry("PGPKeyType", QString()).isEmpty()) {
+ for (auto i = 0; i < ui.keyAlgoCB->count(); ++i) {
+ if (ui.keyAlgoCB->itemData(i).toString().startsWith(u"rsa"_s)) {
+ ui.keyAlgoCB->setCurrentIndex(i);
+ break;
+ }
+ }
+ }
+
+ if (config.hasKey("RSAKeySizes")) {
+ for (const auto size : config.readEntry("RSAKeySizes", QList<int>())) {
+ if (size < 0) {
+ auto index = ui.keyAlgoCB->findData(u"rsa%1"_s.arg(std::abs(size)));
+ if (index != -1) {
+ ui.keyAlgoCB->setCurrentIndex(index);
+ }
+ break;
+ }
+ }
+ }
}
private:
void updateTechnicalParameters()
{
technicalParameters = KeyParameters{KeyParameters::OpenPGP};
auto keyType = GpgME::Subkey::AlgoUnknown;
auto subkeyType = GpgME::Subkey::AlgoUnknown;
auto algoString = ui.keyAlgoCB->currentData().toString();
if (algoString.startsWith(QStringLiteral("rsa"))) {
keyType = GpgME::Subkey::AlgoRSA;
subkeyType = GpgME::Subkey::AlgoRSA;
const auto strength = algoString.mid(3).toInt();
technicalParameters.setKeyLength(strength);
technicalParameters.setSubkeyLength(strength);
} else if (algoString == QLatin1StringView("curve25519") || algoString == QLatin1StringView("curve448")) {
keyType = GpgME::Subkey::AlgoEDDSA;
subkeyType = GpgME::Subkey::AlgoECDH;
if (algoString.endsWith(QStringLiteral("25519"))) {
technicalParameters.setKeyCurve(QStringLiteral("ed25519"));
technicalParameters.setSubkeyCurve(QStringLiteral("cv25519"));
} else {
technicalParameters.setKeyCurve(QStringLiteral("ed448"));
technicalParameters.setSubkeyCurve(QStringLiteral("cv448"));
}
#if GPGMEPP_SUPPORTS_KYBER
} else if (algoString == "ky768_bp256"_L1) {
keyType = GpgME::Subkey::AlgoECDSA;
subkeyType = GpgME::Subkey::AlgoKyber;
technicalParameters.setKeyCurve(u"brainpoolP256r1"_s);
technicalParameters.setSubkeyCurve(u"brainpoolP256r1"_s);
technicalParameters.setSubkeyLength(768);
} else if (algoString == "ky1024_bp384"_L1) {
keyType = GpgME::Subkey::AlgoECDSA;
subkeyType = GpgME::Subkey::AlgoKyber;
technicalParameters.setKeyCurve(u"brainpoolP384r1"_s);
technicalParameters.setSubkeyCurve(u"brainpoolP384r1"_s);
technicalParameters.setSubkeyLength(1024);
#endif
} else {
keyType = GpgME::Subkey::AlgoECDSA;
subkeyType = GpgME::Subkey::AlgoECDH;
technicalParameters.setKeyCurve(algoString);
technicalParameters.setSubkeyCurve(algoString);
}
technicalParameters.setKeyType(keyType);
technicalParameters.setSubkeyType(subkeyType);
technicalParameters.setKeyUsage(KeyUsage(KeyUsage::Certify | KeyUsage::Sign));
technicalParameters.setSubkeyUsage(KeyUsage(KeyUsage::Encrypt));
technicalParameters.setExpirationDate(expiryDate());
// name and email are set later
}
QDate expiryDate() const
{
return ui.expiryCB->isChecked() ? ui.expiryDE->date() : QDate{};
}
void setTechnicalParameters(const KeyParameters &parameters)
{
int index = -1;
if (parameters.keyType() == GpgME::Subkey::AlgoRSA) {
index = ui.keyAlgoCB->findData(QStringLiteral("rsa%1").arg(parameters.keyLength()));
} else if (parameters.keyCurve() == QLatin1StringView("ed25519")) {
index = ui.keyAlgoCB->findData(QStringLiteral("curve25519"));
} else if (parameters.keyCurve() == QLatin1StringView("ed448")) {
index = ui.keyAlgoCB->findData(QStringLiteral("curve448"));
#if GPGMEPP_SUPPORTS_KYBER
} else if (parameters.subkeyType() == GpgME::Subkey::AlgoKyber) {
if (parameters.subkeyLength() == 768 && parameters.keyCurve() == "brainpoolP256r1"_L1) {
index = ui.keyAlgoCB->findData("ky768_bp256"_L1);
} else if (parameters.subkeyLength() == 1024 && parameters.keyCurve() == "brainpoolP384r1"_L1) {
index = ui.keyAlgoCB->findData("ky1024_bp384"_L1);
} else {
qCDebug(LIBKLEO_LOG) << __func__ << "Unsupported Kyber parameters" << parameters.subkeyLength() << parameters.keyCurve();
}
#endif
} else {
index = ui.keyAlgoCB->findData(parameters.keyCurve());
}
if (index >= 0) {
ui.keyAlgoCB->setCurrentIndex(index);
}
setExpiryDate(parameters.expirationDate());
}
void checkAccept()
{
QStringList errors;
if (ui.nameAndEmail->userID().isEmpty() && !ui.nameAndEmail->nameIsRequired() && !ui.nameAndEmail->emailIsRequired()) {
errors.push_back(i18n("Enter a name or an email address."));
}
const auto nameError = ui.nameAndEmail->nameError();
if (!nameError.isEmpty()) {
errors.push_back(nameError);
}
const auto emailError = ui.nameAndEmail->emailError();
if (!emailError.isEmpty()) {
errors.push_back(emailError);
}
if (!Expiration::isValidExpirationDate(expiryDate())) {
errors.push_back(Expiration::validityPeriodHint());
}
if (errors.size() > 1) {
KMessageBox::errorList(q, i18n("There is a problem."), errors);
} else if (!errors.empty()) {
KMessageBox::error(q, errors.first());
} else {
q->accept();
}
}
QDate forceDateIntoAllowedRange(QDate date) const
{
const auto minDate = ui.expiryDE->minimumDate();
if (minDate.isValid() && date < minDate) {
date = minDate;
}
const auto maxDate = ui.expiryDE->maximumDate();
if (maxDate.isValid() && date > maxDate) {
date = maxDate;
}
return date;
}
void setExpiryDate(QDate date)
{
if (date.isValid()) {
ui.expiryDE->setDate(forceDateIntoAllowedRange(date));
} else {
// check if unlimited validity is allowed
if (unlimitedValidityIsAllowed()) {
ui.expiryDE->setDate(date);
}
}
if (ui.expiryCB->isEnabled()) {
ui.expiryCB->setChecked(ui.expiryDE->isValid());
}
}
private:
KeyParameters technicalParameters;
};
OpenPGPCertificateCreationDialog::OpenPGPCertificateCreationDialog(QWidget *parent, Qt::WindowFlags f)
: QDialog{parent, f}
, d(new Private{this})
{
resize(std::max(sizeHint().width(), d->ui.expander->contentWidth()) + 20, sizeHint().height() + 20);
}
OpenPGPCertificateCreationDialog::~OpenPGPCertificateCreationDialog() = default;
void OpenPGPCertificateCreationDialog::setName(const QString &name)
{
d->ui.nameAndEmail->setName(name);
}
QString OpenPGPCertificateCreationDialog::name() const
{
return d->ui.nameAndEmail->name();
}
void OpenPGPCertificateCreationDialog::setEmail(const QString &email)
{
d->ui.nameAndEmail->setEmail(email);
}
QString OpenPGPCertificateCreationDialog::email() const
{
return d->ui.nameAndEmail->email();
}
void Kleo::OpenPGPCertificateCreationDialog::setKeyParameters(const Kleo::KeyParameters &parameters)
{
setName(parameters.name());
const auto emails = parameters.emails();
if (!emails.empty()) {
setEmail(emails.front());
}
d->setTechnicalParameters(parameters);
}
KeyParameters OpenPGPCertificateCreationDialog::keyParameters() const
{
// set name and email on a copy of the technical parameters
auto parameters = d->technicalParameters;
if (!name().isEmpty()) {
parameters.setName(name());
}
if (!email().isEmpty()) {
parameters.setEmail(email());
}
return parameters;
}
void Kleo::OpenPGPCertificateCreationDialog::setProtectKeyWithPassword(bool protectKey)
{
d->ui.withPassCheckBox->setChecked(protectKey);
}
bool OpenPGPCertificateCreationDialog::protectKeyWithPassword() const
{
return d->ui.withPassCheckBox->isChecked();
}
#include "moc_openpgpcertificatecreationdialog.cpp"
diff --git a/src/utils/compliance.cpp b/src/utils/compliance.cpp
index f8e6e963..36cea7b5 100644
--- a/src/utils/compliance.cpp
+++ b/src/utils/compliance.cpp
@@ -1,197 +1,210 @@
/* -*- mode: c++; c-basic-offset:4 -*-
utils/compliance.cpp
This file is part of libkleopatra
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "compliance.h"
#include "algorithm.h"
#include "cryptoconfig.h"
#include "gnupg.h"
#include "keyhelpers.h"
#include "stringutils.h"
#include "systeminfo.h"
#include <libkleo/debug.h>
#include <libkleo/keyfiltermanager.h>
#include <libkleo_debug.h>
#include <KColorScheme>
+#include <KConfigGroup>
#include <KLocalizedString>
+#include <KSharedConfig>
#include <QPushButton>
#include <gpgme++/key.h>
+using namespace Qt::Literals::StringLiterals;
using namespace Kleo;
bool Kleo::DeVSCompliance::isActive()
{
return getCryptoConfigStringValue("gpg", "compliance") == QLatin1StringView{"de-vs"};
}
bool Kleo::DeVSCompliance::isCompliant()
{
if (!isActive()) {
return false;
}
// The pseudo option compliance_de_vs was fully added in 2.2.34;
// For versions between 2.2.28 and 2.2.33 there was a broken config
// value with a wrong type. So for them we add an extra check. This
// can be removed in future versions because for GnuPG we could assume
// non-compliance for older versions as versions of Kleopatra for
// which this matters are bundled with new enough versions of GnuPG anyway.
if (engineIsVersion(2, 2, 28) && !engineIsVersion(2, 2, 34)) {
return true;
}
return getCryptoConfigIntValue("gpg", "compliance_de_vs", 0) != 0;
}
bool Kleo::DeVSCompliance::isBetaCompliance()
{
if (!isActive()) {
return false;
}
// compliance_de_vs > 2000: GnuPG has not yet been approved for VS-NfD or is beta, but we shall assume approval
return getCryptoConfigIntValue("gpg", "compliance_de_vs", 0) > 2000;
}
bool Kleo::DeVSCompliance::algorithmIsCompliant(std::string_view algo)
{
return !isActive() || Kleo::contains(compliantAlgorithms(), algo);
}
bool Kleo::DeVSCompliance::allSubkeysAreCompliant(const GpgME::Key &key)
{
if (!isActive()) {
return true;
}
// there is at least one usable subkey
const auto usableSubkeys = Kleo::count_if(key.subkeys(), [](const auto &sub) {
return !sub.isExpired() && !sub.isRevoked();
});
if (usableSubkeys == 0) {
qCDebug(LIBKLEO_LOG) << __func__ << "No usable subkeys found for key" << key;
return false;
}
// and all usable subkeys are compliant
return Kleo::all_of(key.subkeys(), [](const auto &sub) {
return sub.isDeVs() || sub.isExpired() || sub.isRevoked() || (!sub.canSign() && !sub.canEncrypt() && !sub.canCertify() && sub.canAuthenticate());
});
}
bool Kleo::DeVSCompliance::userIDIsCompliant(const GpgME::UserID &id)
{
if (!isActive()) {
return true;
}
return (id.parent().keyListMode() & GpgME::Validate) //
&& !id.isRevoked() //
&& id.validity() >= GpgME::UserID::Full //
&& allSubkeysAreCompliant(id.parent());
}
bool Kleo::DeVSCompliance::keyIsCompliant(const GpgME::Key &key)
{
if (!isActive()) {
return true;
}
return (key.keyListMode() & GpgME::Validate) //
&& allUserIDsHaveFullValidity(key) //
&& allSubkeysAreCompliant(key);
}
const std::vector<std::string> &Kleo::DeVSCompliance::compliantAlgorithms()
{
static std::vector<std::string> compliantAlgos;
if (!isActive()) {
return Kleo::availableAlgorithms();
}
if (compliantAlgos.empty()) {
- compliantAlgos.reserve(7);
- compliantAlgos = {
- "brainpoolP256r1",
- "brainpoolP384r1",
- "brainpoolP512r1",
- "rsa3072",
- "rsa4096",
- };
+ KConfigGroup config(KSharedConfig::openConfig(u"kleopatrarc"_s), u"CertificateCreationWizard"_s);
+
+ if (!config.isEntryImmutable("PGPKeyType")) {
+ compliantAlgos.reserve(7);
+ compliantAlgos = {
+ "brainpoolP256r1",
+ "brainpoolP384r1",
+ "brainpoolP512r1",
+ };
+ }
+
+ for (const auto size : config.readEntry("RSAKeySizes", QList<int>() << -3072 << 4096)) {
+ const auto abs = std::abs(size);
+ if (abs == 3072 || abs == 4096) {
+ compliantAlgos.push_back("rsa" + std::to_string(abs));
+ }
+ }
+
#if GPGMEPP_SUPPORTS_KYBER
- if (engineIsVersion(2, 5, 2)) {
+ if (engineIsVersion(2, 5, 2) && !config.isEntryImmutable("PGPKeyType")) {
compliantAlgos.insert(compliantAlgos.end(),
{
"ky768_bp256",
"ky1024_bp384",
});
}
#endif
};
return compliantAlgos;
}
const std::vector<std::string> &Kleo::DeVSCompliance::preferredCompliantAlgorithms()
{
static std::vector<std::string> result;
if (result.empty()) {
const auto &preferredAlgos = Kleo::preferredAlgorithms();
result.reserve(preferredAlgos.size());
Kleo::copy_if(preferredAlgos, std::back_inserter(result), Kleo::DeVSCompliance::algorithmIsCompliant);
}
return result;
}
void Kleo::DeVSCompliance::decorate(QPushButton *button)
{
decorate(button, isCompliant());
}
void Kleo::DeVSCompliance::decorate(QPushButton *button, bool compliant)
{
if (!button) {
return;
}
if (compliant) {
button->setIcon(QIcon::fromTheme(QStringLiteral("security-high")));
auto buttonPalette = button->palette();
KColorScheme::adjustBackground(buttonPalette, KColorScheme::PositiveBackground, button->backgroundRole(), KColorScheme::Button);
button->setPalette(buttonPalette);
} else {
button->setIcon(QIcon::fromTheme(QStringLiteral("security-medium")));
auto buttonPalette = button->palette();
KColorScheme::adjustBackground(buttonPalette, KColorScheme::NegativeBackground, button->backgroundRole(), KColorScheme::Button);
button->setPalette(buttonPalette);
}
}
QString Kleo::DeVSCompliance::name()
{
return name(isCompliant());
}
static QString complianceName(bool compliant)
{
const auto filterId = compliant ? QStringLiteral("de-vs-filter") : QStringLiteral("not-de-vs-filter");
if (auto filter = KeyFilterManager::instance()->keyFilterByID(filterId)) {
return filter->name();
}
return compliant ? i18n("VS-NfD compliant") : i18n("Not VS-NfD compliant");
}
QString Kleo::DeVSCompliance::name(bool compliant)
{
if (!isActive()) {
return {};
}
if (compliant && isBetaCompliance()) {
return i18nc("@info append beta-marker to compliance", "%1 (beta)", complianceName(compliant));
}
return complianceName(compliant);
}
diff --git a/src/utils/gnupg.cpp b/src/utils/gnupg.cpp
index 34403232..bce7bfe6 100644
--- a/src/utils/gnupg.cpp
+++ b/src/utils/gnupg.cpp
@@ -1,748 +1,760 @@
/* -*- mode: c++; c-basic-offset:4 -*-
utils/gnupg.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-FileCopyrightText: 2020-2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-libkleo.h>
#include "gnupg.h"
#include "assuan.h"
#include "compat.h"
#include "compliance.h"
#include "cryptoconfig.h"
#include "hex.h"
#include <libkleo_debug.h>
#include <KAboutComponent>
+#include <KConfigGroup>
#include <KLocalizedString>
+#include <KSharedConfig>
#include <QGpgME/CryptoConfig>
#include <QGpgME/Protocol>
#include <QByteArray>
#include <QCoreApplication>
#include <QDateTime>
#include <QDir>
#include <QFile>
#include <QPointer>
#include <QProcess>
#include <QRegularExpression>
#include <QStandardPaths>
#include <QString>
#include <QThread>
#include <gpgme++/engineinfo.h>
#include <gpgme++/error.h>
#include <gpgme++/key.h>
#include <gpg-error.h>
#ifdef Q_OS_WIN
#include "gnupg-registry.h"
#endif // Q_OS_WIN
#include <algorithm>
#include <array>
+using namespace Qt::Literals::StringLiterals;
using namespace GpgME;
QString Kleo::gnupgHomeDirectory()
{
static const QString homeDir = QString::fromUtf8(GpgME::dirInfo("homedir"));
return homeDir;
}
QString Kleo::gnupgPrivateKeysDirectory()
{
static const QString dir = QDir{gnupgHomeDirectory()}.filePath(QStringLiteral("private-keys-v1.d"));
return dir;
}
int Kleo::makeGnuPGError(int code)
{
return gpg_error(static_cast<gpg_err_code_t>(code));
}
static QString findGpgExe(GpgME::Engine engine, const QString &exe)
{
const GpgME::EngineInfo info = GpgME::engineInfo(engine);
return info.fileName() ? QFile::decodeName(info.fileName()) : QStandardPaths::findExecutable(exe);
}
QString Kleo::gpgConfPath()
{
static const auto path = findGpgExe(GpgME::GpgConfEngine, QStringLiteral("gpgconf"));
return path;
}
QString Kleo::gpgSmPath()
{
static const auto path = findGpgExe(GpgME::GpgSMEngine, QStringLiteral("gpgsm"));
return path;
}
QString Kleo::gpgPath()
{
static const auto path = findGpgExe(GpgME::GpgEngine, QStringLiteral("gpg"));
return path;
}
QStringList Kleo::gnupgFileWhitelist()
{
return {
// The obvious pubring
QStringLiteral("pubring.gpg"),
// GnuPG 2.1 pubring
QStringLiteral("pubring.kbx"),
// Trust in X509 Certificates
QStringLiteral("trustlist.txt"),
// Trustdb controls ownertrust and thus WOT validity
QStringLiteral("trustdb.gpg"),
// We want to update when smartcard status changes
QStringLiteral("reader*.status"),
// No longer used in 2.1 but for 2.0 we want this
QStringLiteral("secring.gpg"),
// Secret keys (living under private-keys-v1.d/)
QStringLiteral("*.key"),
// the keyboxd database
QStringLiteral("pubring.db"),
// Changes to the trustmodel / compliance mode might
// affect validity so we check this, too.
// Globbing for gpg.conf* here will trigger too often
// as gpgconf creates files like gpg.conf.bak or
// gpg.conf.tmp12312.gpgconf that should not trigger
// a change.
QStringLiteral("gpg.conf"),
QStringLiteral("gpg.conf-?"),
QStringLiteral("gpg.conf-?.?"),
};
}
QStringList Kleo::gnupgFolderWhitelist()
{
static const QDir gnupgHome{gnupgHomeDirectory()};
return {
gnupgHome.path(),
gnupgPrivateKeysDirectory(),
// for the keyboxd database
gnupgHome.filePath(QStringLiteral("public-keys.d")),
};
}
QString Kleo::gpg4winInstallPath()
{
#ifdef Q_OS_WIN
// QApplication::applicationDirPath is only used as a fallback
// to support the case where Kleopatra is not installed from
// Gpg4win but Gpg4win is also installed.
char *instDir = read_w32_registry_string("HKEY_LOCAL_MACHINE", "Software\\GPG4Win", "Install Directory");
if (!instDir) {
// Fallback to HKCU
instDir = read_w32_registry_string("HKEY_CURRENT_USER", "Software\\GPG4Win", "Install Directory");
}
if (instDir) {
QString ret = QString::fromLocal8Bit(instDir) + QStringLiteral("/bin");
free(instDir);
return ret;
}
qCDebug(LIBKLEO_LOG) << "Gpg4win not found. Falling back to Kleopatra instdir.";
#endif
return QCoreApplication::applicationDirPath();
}
QString Kleo::gnupgInstallPath()
{
#ifdef Q_OS_WIN
// QApplication::applicationDirPath is only used as a fallback
// to support the case where Kleopatra is not installed from
// Gpg4win but Gpg4win is also installed.
char *instDir = read_w32_registry_string("HKEY_LOCAL_MACHINE", "Software\\GnuPG", "Install Directory");
if (!instDir) {
// Fallback to HKCU
instDir = read_w32_registry_string("HKEY_CURRENT_USER", "Software\\GnuPG", "Install Directory");
}
if (instDir) {
QString ret = QString::fromLocal8Bit(instDir) + QStringLiteral("/bin");
free(instDir);
return ret;
}
qCDebug(LIBKLEO_LOG) << "GnuPG not found. Falling back to gpgconf list dir.";
#endif
return gpgConfListDir("bindir");
}
QString Kleo::gpgConfListDir(const char *which)
{
if (!which || !*which) {
return QString();
}
const QString gpgConfPath = Kleo::gpgConfPath();
if (gpgConfPath.isEmpty()) {
return QString();
}
QProcess gpgConf;
qCDebug(LIBKLEO_LOG) << "gpgConfListDir: starting " << qPrintable(gpgConfPath) << " --list-dirs";
gpgConf.start(gpgConfPath, QStringList() << QStringLiteral("--list-dirs"));
if (!gpgConf.waitForFinished()) {
qCDebug(LIBKLEO_LOG) << "gpgConfListDir(): failed to execute gpgconf: " << qPrintable(gpgConf.errorString());
qCDebug(LIBKLEO_LOG) << "output was:\n" << gpgConf.readAllStandardError().constData();
return QString();
}
const QList<QByteArray> lines = gpgConf.readAllStandardOutput().split('\n');
for (const QByteArray &line : lines) {
if (line.startsWith(which) && line[qstrlen(which)] == ':') {
const int begin = qstrlen(which) + 1;
int end = line.size();
while (end && (line[end - 1] == '\n' || line[end - 1] == '\r')) {
--end;
}
const QString result = QDir::fromNativeSeparators(QFile::decodeName(hexdecode(line.mid(begin, end - begin))));
qCDebug(LIBKLEO_LOG) << "gpgConfListDir: found " << qPrintable(result) << " for '" << which << "'entry";
return result;
}
}
qCDebug(LIBKLEO_LOG) << "gpgConfListDir(): didn't find '" << which << "'"
<< "entry in output:\n"
<< gpgConf.readAllStandardError().constData();
return QString();
}
static std::array<int, 3> getVersionFromString(const char *actual, bool &ok)
{
std::array<int, 3> ret{-1, -1, -1};
ok = false;
if (!actual) {
return ret;
}
QString versionString = QString::fromLatin1(actual);
// Try to fix it up
QRegularExpression rx(QRegularExpression::anchoredPattern(QLatin1StringView(R"((\d+)\.(\d+)\.(\d+)(?:-svn\d+)?.*)")));
QRegularExpressionMatch match;
for (int i = 0; i < 3; i++) {
match = rx.match(versionString);
if (!match.hasMatch()) {
versionString += QStringLiteral(".0");
} else {
ok = true;
break;
}
}
if (!ok) {
qCDebug(LIBKLEO_LOG) << "Can't parse version " << actual;
return ret;
}
for (int i = 0; i < 3; ++i) {
ret[i] = match.capturedView(i + 1).toUInt(&ok);
if (!ok) {
return ret;
}
}
ok = true;
return ret;
}
bool Kleo::versionIsAtLeast(const char *minimum, const char *actual)
{
if (!minimum || !actual) {
return false;
}
bool ok;
const auto minimum_version = getVersionFromString(minimum, ok);
if (!ok) {
return false;
}
const auto actual_version = getVersionFromString(actual, ok);
if (!ok) {
return false;
}
return !std::lexicographical_compare(std::begin(actual_version), std::end(actual_version), std::begin(minimum_version), std::end(minimum_version));
}
bool Kleo::engineIsVersion(int major, int minor, int patch, GpgME::Engine engine)
{
static QMap<Engine, std::array<int, 3>> cachedVersions;
const int required_version[] = {major, minor, patch};
// Gpgconf means spawning processes which is expensive on windows.
std::array<int, 3> actual_version;
if (!cachedVersions.contains(engine)) {
const Error err = checkEngine(engine);
if (err.code() == GPG_ERR_INV_ENGINE) {
qCDebug(LIBKLEO_LOG) << "isVersion: invalid engine. '";
return false;
}
const char *actual = GpgME::engineInfo(engine).version();
bool ok;
actual_version = getVersionFromString(actual, ok);
qCDebug(LIBKLEO_LOG) << "Parsed" << actual << "as: " << actual_version[0] << '.' << actual_version[1] << '.' << actual_version[2] << '.';
if (!ok) {
return false;
}
cachedVersions.insert(engine, actual_version);
} else {
actual_version = cachedVersions.value(engine);
}
// return ! ( actual_version < required_version )
return !std::lexicographical_compare(std::begin(actual_version), std::end(actual_version), std::begin(required_version), std::end(required_version));
}
const QString &Kleo::paperKeyInstallPath()
{
static const QString pkPath = (QStandardPaths::findExecutable(QStringLiteral("paperkey"), QStringList() << QCoreApplication::applicationDirPath()).isEmpty()
? QStandardPaths::findExecutable(QStringLiteral("paperkey"))
: QStandardPaths::findExecutable(QStringLiteral("paperkey"), QStringList() << QCoreApplication::applicationDirPath()));
return pkPath;
}
bool Kleo::haveKeyserverConfigured()
{
if (engineIsVersion(2, 4, 4) //
|| (engineIsVersion(2, 2, 42) && !engineIsVersion(2, 3, 0))) {
return Kleo::keyserver() != QLatin1StringView{"none"};
}
if (engineIsVersion(2, 1, 19)) {
// since 2.1.19 there is a builtin keyserver
return true;
}
return !Kleo::keyserver().isEmpty();
}
QString Kleo::keyserver()
{
QString result = getCryptoConfigStringValue("gpg", "keyserver");
if (result.isEmpty()) {
result = getCryptoConfigStringValue("dirmngr", "keyserver");
}
if (result.endsWith(QLatin1StringView{"://none"})) {
// map hkps://none, etc., to "none"; see https://dev.gnupg.org/T6708
result = QStringLiteral("none");
}
return result;
}
bool Kleo::haveX509DirectoryServerConfigured()
{
return !getCryptoConfigUrlList("dirmngr", "ldapserver").empty() //
|| !getCryptoConfigUrlList("dirmngr", "LDAP Server").empty() //
|| !getCryptoConfigUrlList("gpgsm", "keyserver").empty();
}
bool Kleo::gpgComplianceP(const char *mode)
{
const auto conf = QGpgME::cryptoConfig();
const auto entry = getCryptoConfigEntry(conf, "gpg", "compliance");
return entry && entry->stringValue() == QString::fromLatin1(mode);
}
bool Kleo::gnupgUsesDeVsCompliance()
{
return DeVSCompliance::isActive();
}
bool Kleo::gnupgIsDeVsCompliant()
{
return DeVSCompliance::isCompliant();
}
#ifdef Q_OS_WIN
static unsigned int guessConsoleOutputCodePage()
{
/* Qt on Windows uses GetACP while GnuPG prefers
* GetConsoleOutputCP.
*
* As we are not a console application GetConsoleOutputCP
* usually returns 0.
* From experience the closest thing that let's us guess
* what GetConsoleOutputCP returns for a console application
* it appears to be the OEMCP.
*/
unsigned int cpno = GetConsoleOutputCP();
if (!cpno) {
cpno = GetOEMCP();
}
if (!cpno) {
cpno = GetACP();
}
if (!cpno) {
qCDebug(LIBKLEO_LOG) << __func__ << "Failed to find native codepage";
}
qCDebug(LIBKLEO_LOG) << __func__ << "returns" << cpno;
return cpno;
}
static QString fromEncoding(unsigned int src_encoding, const char *data)
{
if (!data || !*data) {
return {};
}
// returns necessary buffer size including the terminating null character
int n = MultiByteToWideChar(src_encoding, 0, data, -1, NULL, 0);
if (n <= 0) {
qCDebug(LIBKLEO_LOG) << __func__ << "determining necessary buffer size failed with error code" << GetLastError();
return QString();
}
wchar_t *result = (wchar_t *)malloc((n + 1) * sizeof *result);
n = MultiByteToWideChar(src_encoding, 0, data, -1, result, n);
if (n <= 0) {
free(result);
qCDebug(LIBKLEO_LOG) << __func__ << "conversion failed with error code" << GetLastError();
return QString();
}
const auto ret = QString::fromWCharArray(result, n - 1);
free(result);
return ret;
}
static QString stringFromGpgOutput_legacy(const QByteArray &ba)
{
static const unsigned int cpno = guessConsoleOutputCodePage();
if (cpno) {
qCDebug(LIBKLEO_LOG) << __func__ << "trying to decode" << ba << "using codepage" << cpno;
const auto rawData = QByteArray{ba}.replace("\r\n", "\n");
const auto s = fromEncoding(cpno, rawData.constData());
if (!s.isEmpty() || ba.isEmpty()) {
return s;
}
qCDebug(LIBKLEO_LOG) << __func__ << "decoding output failed; falling back to QString::fromLocal8Bit()";
}
qCDebug(LIBKLEO_LOG) << __func__ << "decoding from local encoding:" << ba;
return QString::fromLocal8Bit(ba);
}
#endif
QString Kleo::stringFromGpgOutput(const QByteArray &ba)
{
#ifdef Q_OS_WIN
// since 2.2.28, GnuPG always uses UTF-8 for console output (and input)
if (Kleo::engineIsVersion(2, 2, 28, GpgME::GpgEngine)) {
return QString::fromUtf8(ba);
} else {
return stringFromGpgOutput_legacy(ba);
}
#else
return QString::fromLocal8Bit(ba);
#endif
}
QStringList Kleo::backendVersionInfo()
{
const auto components = backendComponents();
QStringList versions;
for (const auto &component : components) {
versions.push_back(component.name() + u' ' + component.version());
}
return versions;
}
QList<KAboutComponent> Kleo::backendComponents()
{
QList<KAboutComponent> components;
if (Kleo::engineIsVersion(2, 2, 24, GpgME::GpgConfEngine)) {
QProcess p;
qCDebug(LIBKLEO_LOG) << "Running gpgconf --show-versions ...";
p.start(Kleo::gpgConfPath(), {QStringLiteral("--show-versions")});
// wait at most 1 second
if (!p.waitForFinished(1000)) {
qCDebug(LIBKLEO_LOG) << "Running gpgconf --show-versions timed out after 1 second.";
} else if (p.exitStatus() != QProcess::NormalExit || p.exitCode() != 0) {
qCDebug(LIBKLEO_LOG) << "Running gpgconf --show-versions failed:" << p.errorString();
qCDebug(LIBKLEO_LOG) << "gpgconf stderr:" << p.readAllStandardError();
qCDebug(LIBKLEO_LOG) << "gpgconf stdout:" << p.readAllStandardOutput();
} else {
const QByteArray output = p.readAllStandardOutput().replace("\r\n", "\n");
qCDebug(LIBKLEO_LOG) << "gpgconf stdout:" << output;
const auto lines = output.split('\n');
for (const auto &line : lines) {
if (line.startsWith("* GnuPG")) {
const auto componentsLine = line.split(' ');
components.append(KAboutComponent(QStringLiteral("GnuPG"),
i18nc("@info", "GnuPG provides support for OpenPGP/LibrePGP and S/MIME."),
QString::fromLatin1(componentsLine.value(2)),
QStringLiteral("https://gnupg.org"),
KAboutLicense::GPL_V3));
}
if (line.startsWith("* Libgcrypt")) {
const auto componentsLine = line.split(' ');
components.append(KAboutComponent(QStringLiteral("Libgcrypt"),
i18nc("@info", "Libgcrypt is a general purpose cryptographic library."),
QString::fromLatin1(componentsLine.value(2)),
QStringLiteral("https://www.gnupg.org/software/libgcrypt/index.html"),
KAboutLicense::LGPL_V2_1));
}
}
}
}
return components;
}
namespace
{
void startGpgConfDetached(const QStringList &arguments)
{
const QString gpgconf = Kleo::gpgConfPath();
qCDebug(LIBKLEO_LOG) << "Starting" << gpgconf << arguments.join(QLatin1Char(' ')) << " ...";
if (QProcess::startDetached(gpgconf, arguments)) {
qCDebug(LIBKLEO_LOG) << "gpgconf was started successfully";
} else {
qCDebug(LIBKLEO_LOG) << "gpgconf failed to start";
}
}
template<typename Function1, typename Function2>
auto startGpgConf(const QStringList &arguments, Function1 onSuccess, Function2 onFailure)
{
auto process = new QProcess;
process->setProgram(Kleo::gpgConfPath());
process->setArguments(arguments);
QObject::connect(process, &QProcess::started, process, [process]() {
qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") was started successfully";
});
QObject::connect(process, &QProcess::errorOccurred, process, [process, onFailure](auto error) {
qCDebug(LIBKLEO_LOG).nospace() << "Error while running gpgconf (" << process << "): " << error;
process->deleteLater();
onFailure();
});
QObject::connect(process, &QProcess::readyReadStandardError, process, [process]() {
for (const auto &line : process->readAllStandardError().trimmed().split('\n')) {
qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") stderr: " << line;
}
});
QObject::connect(process, &QProcess::readyReadStandardOutput, process, [process]() {
(void)process->readAllStandardOutput(); /* ignore stdout */
});
QObject::connect(process, &QProcess::finished, process, [process, onSuccess, onFailure](int exitCode, QProcess::ExitStatus exitStatus) {
if (exitStatus == QProcess::NormalExit) {
qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") exited (exit code: " << exitCode << ")";
if (exitCode == 0) {
onSuccess();
} else {
onFailure();
}
} else {
qCDebug(LIBKLEO_LOG).nospace() << "gpgconf (" << process << ") crashed (exit code: " << exitCode << ")";
onFailure();
}
process->deleteLater();
});
qCDebug(LIBKLEO_LOG).nospace() << "Starting gpgconf (" << process << ") with arguments " << process->arguments().join(QLatin1Char(' ')) << " ...";
process->start();
return process;
}
static void launchGpgAgentWithEventLoop()
{
static thread_local QProcess *process = nullptr;
static thread_local qint64 mSecsSinceEpochOfLastLaunch = 0;
static thread_local int numberOfFailedLaunches = 0;
if (process) {
qCDebug(LIBKLEO_LOG) << __func__ << ": gpg-agent is already being launched";
return;
}
const auto now = QDateTime::currentMSecsSinceEpoch();
if (now - mSecsSinceEpochOfLastLaunch < 1000) {
// reduce attempts to launch the agent to 1 attempt per second
return;
}
mSecsSinceEpochOfLastLaunch = now;
if (numberOfFailedLaunches > 5) {
qCWarning(LIBKLEO_LOG) << __func__ << ": Launching gpg-agent failed" << numberOfFailedLaunches << "times in a row. Giving up.";
return;
}
process = startGpgConf(
{QStringLiteral("--launch"), QStringLiteral("gpg-agent")},
[]() {
numberOfFailedLaunches = 0;
process = nullptr;
},
[]() {
numberOfFailedLaunches++;
process = nullptr;
});
}
}
void Kleo::launchGpgAgent(Kleo::LaunchGpgAgentOptions options)
{
if ((options == CheckForRunningAgent) && Kleo::Assuan::agentIsRunning()) {
qCDebug(LIBKLEO_LOG) << __func__ << ": gpg-agent is already running";
return;
}
if (QThread::currentThread()->loopLevel() > 0) {
launchGpgAgentWithEventLoop();
} else {
startGpgConfDetached({QStringLiteral("--launch"), QStringLiteral("gpg-agent")});
}
}
void Kleo::restartGpgAgent()
{
static QPointer<QProcess> process;
if (process) {
qCDebug(LIBKLEO_LOG) << __func__ << ": gpg-agent is already being restarted";
return;
}
auto startAgent = []() {
Kleo::launchGpgAgent(SkipCheckForRunningAgent);
};
process = startGpgConf({QStringLiteral("--kill"), QStringLiteral("all")}, startAgent, startAgent);
}
const std::vector<std::string> &Kleo::availableAlgorithms()
{
static std::vector<std::string> algos;
if (algos.empty()) {
- algos.reserve(13);
- algos = {
- "brainpoolP256r1",
- "brainpoolP384r1",
- "brainpoolP512r1",
- "curve25519",
- "curve448",
- "nistp256",
- "nistp384",
- "nistp521",
- "rsa2048",
- "rsa3072",
- "rsa4096",
- // "secp256k1", // Curve secp256k1 is explicitly ignored
- };
+ KConfigGroup config(KSharedConfig::openConfig(u"kleopatrarc"_s), u"CertificateCreationWizard"_s);
+
+ if (!config.isEntryImmutable("PGPKeyType")) {
+ algos.reserve(13);
+ algos = {
+ "brainpoolP256r1",
+ "brainpoolP384r1",
+ "brainpoolP512r1",
+ "curve25519",
+ "curve448",
+ "nistp256",
+ "nistp384",
+ "nistp521",
+ // "secp256k1", // Curve secp256k1 is explicitly ignored
+ };
+ }
+
+ for (const auto size : config.readEntry("RSAKeySizes", QList<int>() << 2048 << -3072 << 4096)) {
+ const auto abs = std::abs(size);
+ if (abs == 2048 || abs == 3072 || abs == 4096) {
+ algos.push_back("rsa" + std::to_string(abs));
+ }
+ }
+
#if GPGMEPP_SUPPORTS_KYBER
- if (engineIsVersion(2, 5, 2)) {
+ if (engineIsVersion(2, 5, 2) && !config.isEntryImmutable("PGPKeyType")) {
algos.insert(algos.end(),
{
"ky768_bp256",
"ky1024_bp384",
});
}
#endif
};
return algos;
}
const std::vector<std::string> &Kleo::preferredAlgorithms()
{
static const std::vector<std::string> algos = {
"curve25519",
"brainpoolP256r1",
"rsa3072",
"rsa2048",
};
return algos;
}
const std::vector<std::string> &Kleo::ignoredAlgorithms()
{
static const std::vector<std::string> algos = {
"secp256k1", // Curve secp256k1 is not useful
};
return algos;
}
bool Kleo::gpgvVerify(const QString &filePath, const QString &sigPath, const QString &keyring, const QStringList &additionalSearchPaths)
{
const QFileInfo verifyFi(filePath);
if (!verifyFi.isReadable()) {
return false;
} else {
qCDebug(LIBKLEO_LOG) << "Verifying" << filePath;
}
const auto gpgvPath = QStandardPaths::findExecutable(QStringLiteral("gpgv"), additionalSearchPaths);
if (gpgvPath.isEmpty()) {
qCDebug(LIBKLEO_LOG) << "Could not find gpgv";
return false;
}
QFileInfo sigFi;
if (!sigPath.isEmpty()) {
sigFi.setFile(sigPath);
} else {
sigFi.setFile(filePath + QStringLiteral(".sig"));
}
if (!sigFi.isReadable()) {
qCDebug(LIBKLEO_LOG) << "No signature found at" << sigFi.absoluteFilePath();
return false;
}
auto process = QProcess();
process.setProgram(gpgvPath);
QStringList args;
if (!keyring.isEmpty()) {
args << QStringLiteral("--keyring") << keyring;
}
args << QStringLiteral("--") << sigFi.absoluteFilePath() << verifyFi.absoluteFilePath();
process.setArguments(args);
qCDebug(LIBKLEO_LOG).nospace() << "Starting gpgv (" << gpgvPath << ") with arguments " << args.join(QLatin1Char(' ')) << " ...";
process.start();
if (!process.waitForFinished(-1)) {
qCDebug(LIBKLEO_LOG) << "Failed to execute gpgv" << process.errorString();
}
bool ret = (process.exitStatus() == QProcess::NormalExit && process.exitCode() == 0);
if (!ret) {
qCDebug(LIBKLEO_LOG) << "Failed to verify file";
qCDebug(LIBKLEO_LOG) << "gpgv stdout:" << QString::fromUtf8(process.readAllStandardOutput());
qCDebug(LIBKLEO_LOG) << "gpgv stderr:" << QString::fromUtf8(process.readAllStandardError());
}
return ret;
}
std::vector<QByteArray> Kleo::readSecretKeyFile(const QString &keyGrip)
{
const auto filename = QStringLiteral("%1.key").arg(keyGrip);
const auto path = QDir{Kleo::gnupgPrivateKeysDirectory()}.filePath(filename);
QFile file{path};
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qCDebug(LIBKLEO_LOG) << "Cannot open the private key file" << path << "for reading";
return {};
}
std::vector<QByteArray> lines;
while (!file.atEnd()) {
lines.push_back(file.readLine());
}
if (lines.empty()) {
qCDebug(LIBKLEO_LOG) << "The private key file" << path << "is empty";
}
return lines;
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Oct 17, 7:58 AM (1 d, 17 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
8d/cb/2c223e6299b696cf4f2cbb9c08f6

Event Timeline