diff --git a/src/newcertificatewizard/advancedsettingsdialog.cpp b/src/newcertificatewizard/advancedsettingsdialog.cpp index 7c69932b5..aad747844 100644 --- a/src/newcertificatewizard/advancedsettingsdialog.cpp +++ b/src/newcertificatewizard/advancedsettingsdialog.cpp @@ -1,1017 +1,439 @@ /* -*- mode: c++; c-basic-offset:4 -*- newcertificatewizard/advancedsettingsdialog.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-FileCopyrightText: 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "advancedsettingsdialog_p.h" -#include "keyalgo_p.h" #include "listwidget.h" -#include "utils/expiration.h" #include "utils/gui-helper.h" #include "utils/scrollarea.h" #include #include #include #include -#include #include -#include #include #include #include +#include #include #include #include #include +#include #include -#include #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::NewCertificateUi; using namespace GpgME; static const char RSA_KEYSIZES_ENTRY[] = "RSAKeySizes"; -static const char DSA_KEYSIZES_ENTRY[] = "DSAKeySizes"; -static const char ELG_KEYSIZES_ENTRY[] = "ELGKeySizes"; - static const char RSA_KEYSIZE_LABELS_ENTRY[] = "RSAKeySizeLabels"; -static const char DSA_KEYSIZE_LABELS_ENTRY[] = "DSAKeySizeLabels"; -static const char ELG_KEYSIZE_LABELS_ENTRY[] = "ELGKeySizeLabels"; - -static const char PGP_KEY_TYPE_ENTRY[] = "PGPKeyType"; static const char CMS_KEY_TYPE_ENTRY[] = "CMSKeyType"; -// This should come from gpgme in the future -// For now we only support the basic 2.1 curves and check -// for GnuPG 2.1. The whole subkey / usage generation needs -// new api and a reworked dialog. (ah 10.3.16) -// EDDSA should be supported, too. -static const QStringList curveNames{ - {QStringLiteral("brainpoolP256r1")}, - {QStringLiteral("brainpoolP384r1")}, - {QStringLiteral("brainpoolP512r1")}, - {QStringLiteral("NIST P-256")}, - {QStringLiteral("NIST P-384")}, - {QStringLiteral("NIST P-521")}, -}; - namespace { static void set_keysize(QComboBox *cb, unsigned int strength) { if (!cb) { return; } const int idx = cb->findData(static_cast(strength)); if (idx >= 0) { cb->setCurrentIndex(idx); } } static unsigned int get_keysize(const QComboBox *cb) { if (!cb) { return 0; } const int idx = cb->currentIndex(); if (idx < 0) { return 0; } return cb->itemData(idx).toInt(); } -static void set_curve(QComboBox *cb, const QString &curve) -{ - if (!cb) { - return; - } - const int idx = cb->findText(curve, Qt::MatchFixedString); - if (idx >= 0) { - cb->setCurrentIndex(idx); - } -} - -static QString get_curve(const QComboBox *cb) -{ - if (!cb) { - return QString(); - } - return cb->currentText(); -} - -// Extract the algo information from default_pubkey_algo format -// -// and put it into the return values size, algo and curve. -// -// Values look like: -// RSA-2048 -// rsa2048/cert,sign+rsa2048/enc -// brainpoolP256r1+brainpoolP256r1 -static void parseAlgoString(const QString &algoString, int *size, Subkey::PubkeyAlgo *algo, QString &curve) +static int parseSize(const QString &algoString) { const auto split = algoString.split(QLatin1Char('/')); - bool isEncrypt = split.size() == 2 && split[1].contains(QLatin1StringView("enc")); - - // Normalize const auto lowered = split[0].toLower().remove(QLatin1Char('-')); - if (!algo || !size) { - return; - } - *algo = Subkey::AlgoUnknown; - if (lowered.startsWith(QLatin1StringView("rsa"))) { - *algo = Subkey::AlgoRSA; - } else if (lowered.startsWith(QLatin1StringView("dsa"))) { - *algo = Subkey::AlgoDSA; - } else if (lowered.startsWith(QLatin1StringView("elg"))) { - *algo = Subkey::AlgoELG; - } - - if (*algo != Subkey::AlgoUnknown) { - bool ok; - *size = QStringView(lowered).right(lowered.size() - 3).toInt(&ok); - if (!ok) { - qCWarning(KLEOPATRA_LOG) << "Could not extract size from: " << lowered; - *size = 3072; - } - return; - } - // Now the ECC Algorithms - if (lowered.startsWith(QLatin1StringView("ed25519"))) { - // Special handling for this as technically - // this is a cv25519 curve used for EDDSA - if (isEncrypt) { - curve = QLatin1StringView("cv25519"); - *algo = Subkey::AlgoECDH; - } else { - curve = split[0]; - *algo = Subkey::AlgoEDDSA; - } - return; + bool ok; + auto size = lowered.right(lowered.size() - 3).toInt(&ok); + if (ok) { + return size; } - if (lowered.startsWith(QLatin1StringView("cv25519")) // - || lowered.startsWith(QLatin1StringView("nist")) // - || lowered.startsWith(QLatin1StringView("brainpool")) // - || lowered.startsWith(QLatin1StringView("secp"))) { - curve = split[0]; - *algo = isEncrypt ? Subkey::AlgoECDH : Subkey::AlgoECDSA; - return; - } - - qCWarning(KLEOPATRA_LOG) << "Failed to parse default_pubkey_algo:" << algoString; + qCWarning(KLEOPATRA_LOG) << "Could not extract size from: " << lowered; + return 3072; } } struct AdvancedSettingsDialog::UI { QTabWidget *tabWidget = nullptr; - QRadioButton *rsaRB = nullptr; QComboBox *rsaKeyStrengthCB = nullptr; - QCheckBox *rsaSubCB = nullptr; - QComboBox *rsaKeyStrengthSubCB = nullptr; - QRadioButton *dsaRB = nullptr; - QComboBox *dsaKeyStrengthCB = nullptr; - QCheckBox *elgCB = nullptr; - QComboBox *elgKeyStrengthCB = nullptr; - QRadioButton *ecdsaRB = nullptr; - QComboBox *ecdsaKeyCurvesCB = nullptr; - QCheckBox *ecdhCB = nullptr; - QComboBox *ecdhKeyCurvesCB = nullptr; - QCheckBox *certificationCB = nullptr; QCheckBox *signingCB = nullptr; QCheckBox *encryptionCB = nullptr; - QCheckBox *authenticationCB = nullptr; - QCheckBox *expiryCB = nullptr; - KDateComboBox *expiryDE = nullptr; ScrollArea *personalTab = nullptr; - QGroupBox *uidGB = nullptr; - Kleo::NewCertificateUi::ListWidget *uidLW = nullptr; QGroupBox *emailGB = nullptr; Kleo::NewCertificateUi::ListWidget *emailLW = nullptr; QGroupBox *dnsGB = nullptr; Kleo::NewCertificateUi::ListWidget *dnsLW = nullptr; QGroupBox *uriGB = nullptr; Kleo::NewCertificateUi::ListWidget *uriLW = nullptr; QDialogButtonBox *buttonBox = nullptr; UI(QDialog *parent) { parent->setWindowTitle(i18nc("@title:window", "Advanced Settings")); auto mainLayout = new QVBoxLayout{parent}; tabWidget = new QTabWidget{parent}; + tabWidget->setDocumentMode(true); { auto technicalTab = new ScrollArea{tabWidget}; technicalTab->setFocusPolicy(Qt::NoFocus); technicalTab->setFrameStyle(QFrame::NoFrame); technicalTab->setBackgroundRole(parent->backgroundRole()); technicalTab->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); technicalTab->setSizeAdjustPolicy(QScrollArea::AdjustToContents); auto tabLayout = qobject_cast(technicalTab->widget()->layout()); { auto groupBox = new QGroupBox{i18nc("@title:group", "Key Material"), technicalTab}; auto groupBoxGrid = new QGridLayout{groupBox}; int row = 0; - rsaRB = new QRadioButton{i18nc("@option:radio", "RSA"), groupBox}; - rsaRB->setChecked(false); - groupBoxGrid->addWidget(rsaRB, row, 0, 1, 2); + auto label = new QLabel(i18nc("@info:label", "RSA key strength:")); + groupBoxGrid->addWidget(label, row, 0, 1, 1); rsaKeyStrengthCB = new QComboBox{groupBox}; - rsaKeyStrengthCB->setEnabled(false); + rsaKeyStrengthCB->setEnabled(true); groupBoxGrid->addWidget(rsaKeyStrengthCB, row, 2, 1, 1); row++; auto subKeyIndentation = new QSpacerItem(13, 13, QSizePolicy::Fixed, QSizePolicy::Minimum); groupBoxGrid->addItem(subKeyIndentation, row, 0, 1, 1); - rsaSubCB = new QCheckBox{i18nc("@option:check", "+ RSA"), groupBox}; - rsaSubCB->setEnabled(true); - groupBoxGrid->addWidget(rsaSubCB, row, 1, 1, 1); - - rsaKeyStrengthSubCB = new QComboBox{groupBox}; - rsaKeyStrengthSubCB->setEnabled(false); - groupBoxGrid->addWidget(rsaKeyStrengthSubCB, row, 2, 1, 1); - - row++; - dsaRB = new QRadioButton{i18nc("@option:radio", "DSA"), groupBox}; - groupBoxGrid->addWidget(dsaRB, row, 0, 1, 2); - - dsaKeyStrengthCB = new QComboBox{groupBox}; - dsaKeyStrengthCB->setEnabled(false); - groupBoxGrid->addWidget(dsaKeyStrengthCB, row, 2, 1, 1); - - row++; - elgCB = new QCheckBox{i18nc("@option:check", "+ Elgamal"), groupBox}; - elgCB->setToolTip(i18nc("@info:tooltip", "This subkey is required for encryption.")); - elgCB->setEnabled(true); - groupBoxGrid->addWidget(elgCB, row, 1, 1, 1); - - elgKeyStrengthCB = new QComboBox{groupBox}; - elgKeyStrengthCB->setEnabled(false); - groupBoxGrid->addWidget(elgKeyStrengthCB, row, 2, 1, 1); - - row++; - ecdsaRB = new QRadioButton{i18nc("@option:radio", "ECDSA"), groupBox}; - groupBoxGrid->addWidget(ecdsaRB, row, 0, 1, 2); - - ecdsaKeyCurvesCB = new QComboBox{groupBox}; - ecdsaKeyCurvesCB->setEnabled(false); - groupBoxGrid->addWidget(ecdsaKeyCurvesCB, row, 2, 1, 1); - - row++; - ecdhCB = new QCheckBox{i18nc("@option:check", "+ ECDH"), groupBox}; - ecdhCB->setToolTip(i18nc("@info:tooltip", "This subkey is required for encryption.")); - ecdhCB->setEnabled(true); - groupBoxGrid->addWidget(ecdhCB, row, 1, 1, 1); - - ecdhKeyCurvesCB = new QComboBox{groupBox}; - ecdhKeyCurvesCB->setEnabled(false); - groupBoxGrid->addWidget(ecdhKeyCurvesCB, row, 2, 1, 1); - groupBoxGrid->setColumnStretch(3, 1); tabLayout->addWidget(groupBox); } { auto groupBox = new QGroupBox{i18nc("@title:group", "Certificate Usage"), technicalTab}; auto groupBoxGrid = new QGridLayout{groupBox}; int row = 0; signingCB = new QCheckBox{i18nc("@option:check", "Signing"), groupBox}; signingCB->setChecked(true); groupBoxGrid->addWidget(signingCB, row, 0, 1, 1); - certificationCB = new QCheckBox{i18nc("@option:check", "Certification"), groupBox}; - groupBoxGrid->addWidget(certificationCB, row, 1, 1, 1); - row++; encryptionCB = new QCheckBox{i18nc("@option:check", "Encryption"), groupBox}; encryptionCB->setChecked(true); groupBoxGrid->addWidget(encryptionCB, row, 0, 1, 1); - authenticationCB = new QCheckBox{i18nc("@option:check", "Authentication"), groupBox}; - groupBoxGrid->addWidget(authenticationCB, row, 1, 1, 1); - row++; { auto hbox = new QHBoxLayout; - - expiryCB = new QCheckBox{i18nc("@option:check", "Valid until:"), groupBox}; - hbox->addWidget(expiryCB); - - expiryDE = new KDateComboBox(groupBox); - hbox->addWidget(expiryDE, 1); - groupBoxGrid->addLayout(hbox, row, 0, 1, 2); } tabLayout->addWidget(groupBox); } tabLayout->addStretch(1); tabWidget->addTab(technicalTab, i18nc("@title:tab", "Technical Details")); } { personalTab = new ScrollArea{tabWidget}; personalTab->setFocusPolicy(Qt::NoFocus); personalTab->setFrameStyle(QFrame::NoFrame); personalTab->setBackgroundRole(parent->backgroundRole()); personalTab->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); personalTab->setSizeAdjustPolicy(QScrollArea::AdjustToContents); auto scrollAreaLayout = qobject_cast(personalTab->widget()->layout()); auto tabGrid = new QGridLayout; - uidGB = new QGroupBox{i18nc("@title:group", "Additional User IDs"), personalTab}; - { - auto layout = new QVBoxLayout{uidGB}; - uidLW = new Kleo::NewCertificateUi::ListWidget{uidGB}; - layout->addWidget(uidLW); - } - tabGrid->addWidget(uidGB, 0, 0, 1, 2); - emailGB = new QGroupBox{i18nc("@title:group", "EMail Addresses"), personalTab}; { auto layout = new QVBoxLayout{emailGB}; emailLW = new Kleo::NewCertificateUi::ListWidget{emailGB}; layout->addWidget(emailLW); } tabGrid->addWidget(emailGB, 2, 0, 2, 1); dnsGB = new QGroupBox{i18nc("@title:group", "DNS Names"), personalTab}; { auto layout = new QVBoxLayout{dnsGB}; dnsLW = new Kleo::NewCertificateUi::ListWidget{dnsGB}; layout->addWidget(dnsLW); } tabGrid->addWidget(dnsGB, 2, 1, 1, 1); uriGB = new QGroupBox{i18nc("@title:group", "URIs"), personalTab}; { auto layout = new QVBoxLayout{uriGB}; uriLW = new Kleo::NewCertificateUi::ListWidget{uriGB}; layout->addWidget(uriLW); } tabGrid->addWidget(uriGB, 3, 1, 1, 1); scrollAreaLayout->addLayout(tabGrid); tabWidget->addTab(personalTab, i18nc("@title:tab", "Personal Details")); } mainLayout->addWidget(tabWidget); buttonBox = new QDialogButtonBox{parent}; buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok); mainLayout->addWidget(buttonBox); } }; AdvancedSettingsDialog::AdvancedSettingsDialog(QWidget *parent) : QDialog{parent} , ui{new UI{this}} - , mECCSupported{engineIsVersion(2, 1, 0)} - , mEdDSASupported{engineIsVersion(2, 1, 15)} { qRegisterMetaType("Subkey::PubkeyAlgo"); - Kleo::setUpExpirationDateComboBox(ui->expiryDE); - if (unlimitedValidityIsAllowed()) { - ui->expiryDE->setEnabled(ui->expiryCB->isChecked()); - } else { - ui->expiryCB->setEnabled(false); - ui->expiryCB->setChecked(true); - } ui->emailLW->setDefaultValue(i18n("new email")); ui->dnsLW->setDefaultValue(i18n("new dns name")); ui->uriLW->setDefaultValue(i18n("new uri")); fillKeySizeComboBoxen(); - connect(ui->rsaRB, &QAbstractButton::toggled, ui->rsaKeyStrengthCB, &QWidget::setEnabled); - connect(ui->rsaRB, &QAbstractButton::toggled, this, &AdvancedSettingsDialog::slotKeyMaterialSelectionChanged); - connect(ui->rsaSubCB, &QAbstractButton::toggled, ui->rsaKeyStrengthSubCB, &QWidget::setEnabled); - connect(ui->rsaSubCB, &QAbstractButton::toggled, this, &AdvancedSettingsDialog::slotKeyMaterialSelectionChanged); - - connect(ui->dsaRB, &QAbstractButton::toggled, ui->dsaKeyStrengthCB, &QWidget::setEnabled); - connect(ui->dsaRB, &QAbstractButton::toggled, this, &AdvancedSettingsDialog::slotKeyMaterialSelectionChanged); - connect(ui->elgCB, &QAbstractButton::toggled, ui->elgKeyStrengthCB, &QWidget::setEnabled); - connect(ui->elgCB, &QAbstractButton::toggled, this, &AdvancedSettingsDialog::slotKeyMaterialSelectionChanged); - - connect(ui->ecdsaRB, &QAbstractButton::toggled, ui->ecdsaKeyCurvesCB, &QWidget::setEnabled); - connect(ui->ecdsaRB, &QAbstractButton::toggled, this, &AdvancedSettingsDialog::slotKeyMaterialSelectionChanged); - connect(ui->ecdhCB, &QAbstractButton::toggled, ui->ecdhKeyCurvesCB, &QWidget::setEnabled); - connect(ui->ecdhCB, &QAbstractButton::toggled, this, &AdvancedSettingsDialog::slotKeyMaterialSelectionChanged); - connect(ui->signingCB, &QAbstractButton::toggled, this, &AdvancedSettingsDialog::slotSigningAllowedToggled); connect(ui->encryptionCB, &QAbstractButton::toggled, this, &AdvancedSettingsDialog::slotEncryptionAllowedToggled); - connect(ui->expiryCB, &QAbstractButton::toggled, this, [this](bool checked) { - ui->expiryDE->setEnabled(checked); - if (checked && !ui->expiryDE->isValid()) { - setExpiryDate(defaultExpirationDate(ExpirationOnUnlimitedValidity::InternalDefaultExpiration)); - } - }); - connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &AdvancedSettingsDialog::accept); connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); -} - -AdvancedSettingsDialog::~AdvancedSettingsDialog() = default; -bool AdvancedSettingsDialog::unlimitedValidityIsAllowed() const -{ - return !Kleo::maximumExpirationDate().isValid(); -} - -void AdvancedSettingsDialog::setProtocol(GpgME::Protocol proto) -{ - if (protocol == proto) { - return; - } - protocol = proto; loadDefaults(); } -void AdvancedSettingsDialog::setAdditionalUserIDs(const QStringList &items) -{ - ui->uidLW->setItems(items); -} - -QStringList AdvancedSettingsDialog::additionalUserIDs() const -{ - return ui->uidLW->items(); -} +AdvancedSettingsDialog::~AdvancedSettingsDialog() = default; void AdvancedSettingsDialog::setAdditionalEMailAddresses(const QStringList &items) { ui->emailLW->setItems(items); } QStringList AdvancedSettingsDialog::additionalEMailAddresses() const { return ui->emailLW->items(); } void AdvancedSettingsDialog::setDnsNames(const QStringList &items) { ui->dnsLW->setItems(items); } QStringList AdvancedSettingsDialog::dnsNames() const { return ui->dnsLW->items(); } void AdvancedSettingsDialog::setUris(const QStringList &items) { ui->uriLW->setItems(items); } QStringList AdvancedSettingsDialog::uris() const { return ui->uriLW->items(); } void AdvancedSettingsDialog::setKeyStrength(unsigned int strength) { set_keysize(ui->rsaKeyStrengthCB, strength); - set_keysize(ui->dsaKeyStrengthCB, strength); } unsigned int AdvancedSettingsDialog::keyStrength() const { - return ui->dsaRB->isChecked() ? get_keysize(ui->dsaKeyStrengthCB) : ui->rsaRB->isChecked() ? get_keysize(ui->rsaKeyStrengthCB) : 0; -} - -void AdvancedSettingsDialog::setKeyType(Subkey::PubkeyAlgo algo) -{ - QRadioButton *const rb = is_rsa(algo) ? ui->rsaRB : is_dsa(algo) ? ui->dsaRB : is_ecdsa(algo) || is_eddsa(algo) ? ui->ecdsaRB : nullptr; - if (rb) { - rb->setChecked(true); - } + return get_keysize(ui->rsaKeyStrengthCB); } Subkey::PubkeyAlgo AdvancedSettingsDialog::keyType() const { - return ui->dsaRB->isChecked() ? Subkey::AlgoDSA - : ui->rsaRB->isChecked() ? Subkey::AlgoRSA - : ui->ecdsaRB->isChecked() ? ui->ecdsaKeyCurvesCB->currentText() == QLatin1StringView("ed25519") ? Subkey::AlgoEDDSA : Subkey::AlgoECDSA - : Subkey::AlgoUnknown; -} - -void AdvancedSettingsDialog::setKeyCurve(const QString &curve) -{ - set_curve(ui->ecdsaKeyCurvesCB, curve); -} - -QString AdvancedSettingsDialog::keyCurve() const -{ - return get_curve(ui->ecdsaKeyCurvesCB); -} - -void AdvancedSettingsDialog::setSubkeyType(Subkey::PubkeyAlgo algo) -{ - ui->elgCB->setChecked(is_elg(algo)); - ui->rsaSubCB->setChecked(is_rsa(algo)); - ui->ecdhCB->setChecked(is_ecdh(algo)); -} - -Subkey::PubkeyAlgo AdvancedSettingsDialog::subkeyType() const -{ - if (ui->elgCB->isChecked()) { - return Subkey::AlgoELG_E; - } else if (ui->rsaSubCB->isChecked()) { - return Subkey::AlgoRSA; - } else if (ui->ecdhCB->isChecked()) { - return Subkey::AlgoECDH; - } - return Subkey::AlgoUnknown; -} - -void AdvancedSettingsDialog::setSubkeyCurve(const QString &curve) -{ - set_curve(ui->ecdhKeyCurvesCB, curve); -} - -QString AdvancedSettingsDialog::subkeyCurve() const -{ - return get_curve(ui->ecdhKeyCurvesCB); -} - -void AdvancedSettingsDialog::setSubkeyStrength(unsigned int strength) -{ - if (subkeyType() == Subkey::AlgoRSA) { - set_keysize(ui->rsaKeyStrengthSubCB, strength); - } else { - set_keysize(ui->elgKeyStrengthCB, strength); - } -} - -unsigned int AdvancedSettingsDialog::subkeyStrength() const -{ - if (subkeyType() == Subkey::AlgoRSA) { - return get_keysize(ui->rsaKeyStrengthSubCB); - } - return get_keysize(ui->elgKeyStrengthCB); + return Subkey::AlgoRSA; } void AdvancedSettingsDialog::setSigningAllowed(bool on) { ui->signingCB->setChecked(on); } bool AdvancedSettingsDialog::signingAllowed() const { return ui->signingCB->isChecked(); } void AdvancedSettingsDialog::setEncryptionAllowed(bool on) { ui->encryptionCB->setChecked(on); } bool AdvancedSettingsDialog::encryptionAllowed() const { return ui->encryptionCB->isChecked(); } -void AdvancedSettingsDialog::setCertificationAllowed(bool on) -{ - ui->certificationCB->setChecked(on); -} - -bool AdvancedSettingsDialog::certificationAllowed() const -{ - return ui->certificationCB->isChecked(); -} - -void AdvancedSettingsDialog::setAuthenticationAllowed(bool on) -{ - ui->authenticationCB->setChecked(on); -} - -bool AdvancedSettingsDialog::authenticationAllowed() const -{ - return ui->authenticationCB->isChecked(); -} - -QDate AdvancedSettingsDialog::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 AdvancedSettingsDialog::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()); - } -} - -QDate AdvancedSettingsDialog::expiryDate() const -{ - return ui->expiryCB->isChecked() ? ui->expiryDE->date() : QDate{}; -} - -void AdvancedSettingsDialog::slotKeyMaterialSelectionChanged() -{ - const unsigned int algo = keyType(); - const unsigned int sk_algo = subkeyType(); - - if (protocol == OpenPGP) { - // first update the enabled state, but only if key type is not forced - if (!keyTypeImmutable) { - ui->elgCB->setEnabled(is_dsa(algo)); - ui->rsaSubCB->setEnabled(is_rsa(algo)); - ui->ecdhCB->setEnabled(is_ecdsa(algo) || is_eddsa(algo)); - if (is_rsa(algo)) { - ui->encryptionCB->setEnabled(true); - ui->signingCB->setEnabled(true); - ui->authenticationCB->setEnabled(true); - if (is_rsa(sk_algo)) { - ui->encryptionCB->setEnabled(false); - } else { - ui->encryptionCB->setEnabled(true); - } - } else if (is_dsa(algo)) { - ui->encryptionCB->setEnabled(false); - } else if (is_ecdsa(algo) || is_eddsa(algo)) { - ui->signingCB->setEnabled(true); - ui->authenticationCB->setEnabled(true); - ui->encryptionCB->setEnabled(false); - } - } - // then update the checked state - if (sender() == ui->dsaRB || sender() == ui->rsaRB || sender() == ui->ecdsaRB) { - ui->elgCB->setChecked(is_dsa(algo)); - ui->ecdhCB->setChecked(is_ecdsa(algo) || is_eddsa(algo)); - ui->rsaSubCB->setChecked(is_rsa(algo)); - } - if (is_rsa(algo)) { - ui->encryptionCB->setChecked(true); - ui->signingCB->setChecked(true); - if (is_rsa(sk_algo)) { - ui->encryptionCB->setChecked(true); - } - } else if (is_dsa(algo)) { - if (is_elg(sk_algo)) { - ui->encryptionCB->setChecked(true); - } else { - ui->encryptionCB->setChecked(false); - } - } else if (is_ecdsa(algo) || is_eddsa(algo)) { - ui->signingCB->setChecked(true); - ui->encryptionCB->setChecked(is_ecdh(sk_algo)); - } - } else { - // assert( is_rsa( keyType() ) ); // it can happen through misconfiguration by the admin that no key type is selectable at all - } -} - void AdvancedSettingsDialog::slotSigningAllowedToggled(bool on) { - if (!on && protocol == CMS && !encryptionAllowed()) { + if (!on && !encryptionAllowed()) { setEncryptionAllowed(true); } } void AdvancedSettingsDialog::slotEncryptionAllowedToggled(bool on) { - if (!on && protocol == CMS && !signingAllowed()) { + if (!on && !signingAllowed()) { setSigningAllowed(true); } } static void fill_combobox(QComboBox &cb, const QList &sizes, const QStringList &labels) { cb.clear(); for (int i = 0, end = sizes.size(); i != end; ++i) { const int size = std::abs(sizes[i]); /* As we respect the defaults configurable in GnuPG, and we also have configurable * defaults in Kleopatra its difficult to print out "default" here. To avoid confusion * about that its better not to show any default indication. */ cb.addItem(i < labels.size() && !labels[i].trimmed().isEmpty() ? i18ncp("%2: some admin-supplied text, %1: key size in bits", "%2 (1 bit)", "%2 (%1 bits)", size, labels[i].trimmed()) : i18ncp("%1: key size in bits", "1 bit", "%1 bits", size), size); if (sizes[i] < 0) { cb.setCurrentIndex(cb.count() - 1); } } } void AdvancedSettingsDialog::fillKeySizeComboBoxen() { const KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("CertificateCreationWizard")); QList rsaKeySizes = config.readEntry(RSA_KEYSIZES_ENTRY, QList() << 2048 << -3072 << 4096); if (DeVSCompliance::isActive()) { rsaKeySizes = config.readEntry(RSA_KEYSIZES_ENTRY, QList() << -3072 << 4096); } - const QList dsaKeySizes = config.readEntry(DSA_KEYSIZES_ENTRY, QList() << -2048); - const QList elgKeySizes = config.readEntry(ELG_KEYSIZES_ENTRY, QList() << -2048 << 3072 << 4096); const QStringList rsaKeySizeLabels = config.readEntry(RSA_KEYSIZE_LABELS_ENTRY, QStringList()); - const QStringList dsaKeySizeLabels = config.readEntry(DSA_KEYSIZE_LABELS_ENTRY, QStringList()); - const QStringList elgKeySizeLabels = config.readEntry(ELG_KEYSIZE_LABELS_ENTRY, QStringList()); fill_combobox(*ui->rsaKeyStrengthCB, rsaKeySizes, rsaKeySizeLabels); - fill_combobox(*ui->rsaKeyStrengthSubCB, rsaKeySizes, rsaKeySizeLabels); - fill_combobox(*ui->dsaKeyStrengthCB, dsaKeySizes, dsaKeySizeLabels); - fill_combobox(*ui->elgKeyStrengthCB, elgKeySizes, elgKeySizeLabels); - if (mEdDSASupported) { - // If supported we recommend cv25519 - ui->ecdsaKeyCurvesCB->addItem(QStringLiteral("ed25519")); - ui->ecdhKeyCurvesCB->addItem(QStringLiteral("cv25519")); - } - ui->ecdhKeyCurvesCB->addItems(curveNames); - ui->ecdsaKeyCurvesCB->addItems(curveNames); } // Try to load the default key type from GnuPG void AdvancedSettingsDialog::loadDefaultGnuPGKeyType() { const auto conf = QGpgME::cryptoConfig(); if (!conf) { qCWarning(KLEOPATRA_LOG) << "Failed to obtain cryptoConfig."; return; } - const auto entry = getCryptoConfigEntry(conf, protocol == CMS ? "gpgsm" : "gpg", "default_pubkey_algo"); + const auto entry = getCryptoConfigEntry(conf, "gpgsm", "default_pubkey_algo"); if (!entry) { qCDebug(KLEOPATRA_LOG) << "GnuPG does not have default key type. Fallback to RSA"; - setKeyType(Subkey::AlgoRSA); - setSubkeyType(Subkey::AlgoRSA); return; } qCDebug(KLEOPATRA_LOG) << "Have default key type: " << entry->stringValue(); // Format is [/usage]+[/usage] const auto split = entry->stringValue().split(QLatin1Char('+')); - int size = 0; - Subkey::PubkeyAlgo algo = Subkey::AlgoUnknown; - QString curve; - - parseAlgoString(split[0], &size, &algo, curve); - if (algo == Subkey::AlgoUnknown) { - setSubkeyType(Subkey::AlgoRSA); - return; - } - - setKeyType(algo); - - if (is_rsa(algo) || is_elg(algo) || is_dsa(algo)) { - setKeyStrength(size); - } else { - setKeyCurve(curve); - } - - { - auto algoString = (split.size() == 2) ? split[1] : split[0]; - // If it has no usage we assume encrypt subkey - if (!algoString.contains(QLatin1Char('/'))) { - algoString += QStringLiteral("/enc"); - } - - parseAlgoString(algoString, &size, &algo, curve); - - if (algo == Subkey::AlgoUnknown) { - setSubkeyType(Subkey::AlgoRSA); - return; - } - - setSubkeyType(algo); - - if (is_rsa(algo) || is_elg(algo)) { - setSubkeyStrength(size); - } else { - setSubkeyCurve(curve); - } - } + setKeyStrength(parseSize(split[0])); } void AdvancedSettingsDialog::loadDefaultKeyType() { - if (protocol != CMS && protocol != OpenPGP) { - return; - } - const KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("CertificateCreationWizard")); - const QString entry = protocol == CMS ? QLatin1StringView(CMS_KEY_TYPE_ENTRY) : QLatin1String(PGP_KEY_TYPE_ENTRY); + const QString entry = QLatin1StringView(CMS_KEY_TYPE_ENTRY); const QString keyType = config.readEntry(entry).trimmed().toUpper(); - if (protocol == OpenPGP && keyType == QLatin1StringView("DSA")) { - setKeyType(Subkey::AlgoDSA); - setSubkeyType(Subkey::AlgoUnknown); - } else if (protocol == OpenPGP && keyType == QLatin1StringView("DSA+ELG")) { - setKeyType(Subkey::AlgoDSA); - setSubkeyType(Subkey::AlgoELG_E); - } else if (keyType.isEmpty() && engineIsVersion(2, 1, 17)) { + if (keyType.isEmpty() && engineIsVersion(2, 1, 17)) { loadDefaultGnuPGKeyType(); } else { if (!keyType.isEmpty() && keyType != QLatin1StringView("RSA")) qCWarning(KLEOPATRA_LOG) << "invalid value \"" << qPrintable(keyType) << "\" for entry \"[CertificateCreationWizard]" << qPrintable(entry) << "\""; - setKeyType(Subkey::AlgoRSA); - setSubkeyType(Subkey::AlgoRSA); } keyTypeImmutable = config.isEntryImmutable(entry); } -void AdvancedSettingsDialog::loadDefaultExpiration() -{ - if (protocol != OpenPGP) { - return; - } - - if (unlimitedValidityIsAllowed()) { - setExpiryDate(defaultExpirationDate(ExpirationOnUnlimitedValidity::NoExpiration)); - } else { - setExpiryDate(defaultExpirationDate(ExpirationOnUnlimitedValidity::InternalDefaultExpiration)); - } -} - void AdvancedSettingsDialog::loadDefaults() { loadDefaultKeyType(); - loadDefaultExpiration(); - updateWidgetVisibility(); } void AdvancedSettingsDialog::updateWidgetVisibility() { - // Personal Details Page - if (protocol == OpenPGP) { // ### hide until multi-uid is implemented - if (ui->tabWidget->indexOf(ui->personalTab) != -1) { - ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->personalTab)); - } - } else { - if (ui->tabWidget->indexOf(ui->personalTab) == -1) { - ui->tabWidget->addTab(ui->personalTab, i18nc("@title:tab", "Personal Details")); - } - } - ui->uidGB->setVisible(protocol == OpenPGP); - ui->uidGB->setEnabled(false); - ui->uidGB->setToolTip(i18nc("@info:tooltip", "Adding more than one user ID is not yet implemented.")); - ui->emailGB->setVisible(protocol == CMS); - ui->dnsGB->setVisible(protocol == CMS); - ui->uriGB->setVisible(protocol == CMS); - - // Technical Details Page - ui->ecdhCB->setVisible(mECCSupported); - ui->ecdhKeyCurvesCB->setVisible(mECCSupported); - ui->ecdsaKeyCurvesCB->setVisible(mECCSupported); - ui->ecdsaRB->setVisible(mECCSupported); - if (mEdDSASupported) { - // We use the same radio button for EdDSA as we use for - // ECDSA GnuPG does the same and this is really super technical - // land. - ui->ecdsaRB->setText(QStringLiteral("ECDSA/EdDSA")); - } - - const bool deVsHack = DeVSCompliance::isActive(); - - if (deVsHack) { - // GnuPG Provides no API to query which keys are compliant for - // a mode. If we request a different one it will error out so - // we have to remove the options. - // - // Does anyone want to use NIST anyway? - int i; - while ((i = ui->ecdsaKeyCurvesCB->findText(QStringLiteral("NIST"), Qt::MatchStartsWith)) != -1 - || (i = ui->ecdsaKeyCurvesCB->findText(QStringLiteral("25519"), Qt::MatchEndsWith)) != -1) { - ui->ecdsaKeyCurvesCB->removeItem(i); - } - while ((i = ui->ecdhKeyCurvesCB->findText(QStringLiteral("NIST"), Qt::MatchStartsWith)) != -1 - || (i = ui->ecdhKeyCurvesCB->findText(QStringLiteral("25519"), Qt::MatchEndsWith)) != -1) { - ui->ecdhKeyCurvesCB->removeItem(i); - } - } - ui->certificationCB->setVisible(protocol == OpenPGP); // gpgsm limitation? - ui->authenticationCB->setVisible(protocol == OpenPGP); - if (keyTypeImmutable) { - ui->rsaRB->setEnabled(false); - ui->rsaSubCB->setEnabled(false); - ui->dsaRB->setEnabled(false); - ui->elgCB->setEnabled(false); - ui->ecdsaRB->setEnabled(false); - ui->ecdhCB->setEnabled(false); - // force usage if key type is forced - ui->certificationCB->setEnabled(false); ui->signingCB->setEnabled(false); ui->encryptionCB->setEnabled(false); - ui->authenticationCB->setEnabled(false); } else { - ui->rsaRB->setEnabled(true); - ui->rsaSubCB->setEnabled(protocol == OpenPGP); - ui->dsaRB->setEnabled(protocol == OpenPGP && !deVsHack); - ui->elgCB->setEnabled(protocol == OpenPGP && !deVsHack); - ui->ecdsaRB->setEnabled(protocol == OpenPGP); - ui->ecdhCB->setEnabled(protocol == OpenPGP); - - if (protocol == OpenPGP) { - // OpenPGP keys must have certify capability - ui->certificationCB->setEnabled(false); - } - if (protocol == CMS) { - ui->encryptionCB->setEnabled(true); - ui->rsaKeyStrengthSubCB->setEnabled(false); - } + ui->encryptionCB->setEnabled(true); } - if (protocol == OpenPGP) { - // OpenPGP keys must have certify capability - ui->certificationCB->setChecked(true); - } - if (protocol == CMS) { - ui->rsaSubCB->setChecked(false); - } - - ui->expiryDE->setVisible(protocol == OpenPGP); - ui->expiryCB->setVisible(protocol == OpenPGP); - - slotKeyMaterialSelectionChanged(); } void AdvancedSettingsDialog::setInitialFocus() { - // first try the key type radio buttons - if (focusFirstCheckedButton({ui->rsaRB, ui->dsaRB, ui->ecdsaRB})) { - return; - } // then try the usage check boxes and the expiration check box - if (focusFirstEnabledButton({ui->signingCB, ui->certificationCB, ui->encryptionCB, ui->authenticationCB, ui->expiryCB})) { + if (focusFirstEnabledButton({ui->signingCB, ui->encryptionCB})) { return; } // finally, focus the OK button ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus(); } void AdvancedSettingsDialog::accept() { - if (!Kleo::isValidExpirationDate(expiryDate())) { - KMessageBox::error(this, i18nc("@info", "Error: %1", Kleo::validityPeriodHint())); - return; - } - QDialog::accept(); } void AdvancedSettingsDialog::showEvent(QShowEvent *event) { if (isFirstShowEvent) { setInitialFocus(); isFirstShowEvent = false; } QDialog::showEvent(event); } #include "moc_advancedsettingsdialog_p.cpp" diff --git a/src/newcertificatewizard/advancedsettingsdialog_p.h b/src/newcertificatewizard/advancedsettingsdialog_p.h index 4bfa95e29..7d7f106f4 100644 --- a/src/newcertificatewizard/advancedsettingsdialog_p.h +++ b/src/newcertificatewizard/advancedsettingsdialog_p.h @@ -1,129 +1,84 @@ /* -*- mode: c++; c-basic-offset:4 -*- newcertificatewizard/advancedsettingsdialog_p.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-FileCopyrightText: 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "utils/metatypes_for_gpgmepp_key.h" #include #include #include class AdvancedSettingsDialog : public QDialog { Q_OBJECT - Q_PROPERTY(QStringList additionalUserIDs READ additionalUserIDs WRITE setAdditionalUserIDs) Q_PROPERTY(QStringList additionalEMailAddresses READ additionalEMailAddresses WRITE setAdditionalEMailAddresses) Q_PROPERTY(QStringList dnsNames READ dnsNames WRITE setDnsNames) Q_PROPERTY(QStringList uris READ uris WRITE setUris) Q_PROPERTY(uint keyStrength READ keyStrength WRITE setKeyStrength) - Q_PROPERTY(GpgME::Subkey::PubkeyAlgo keyType READ keyType WRITE setKeyType) - Q_PROPERTY(QString keyCurve READ keyCurve WRITE setKeyCurve) - Q_PROPERTY(uint subkeyStrength READ subkeyStrength WRITE setSubkeyStrength) - Q_PROPERTY(QString subkeyCurve READ subkeyCurve WRITE setSubkeyCurve) - Q_PROPERTY(GpgME::Subkey::PubkeyAlgo subkeyType READ subkeyType WRITE setSubkeyType) + Q_PROPERTY(GpgME::Subkey::PubkeyAlgo keyType READ keyType) Q_PROPERTY(bool signingAllowed READ signingAllowed WRITE setSigningAllowed) Q_PROPERTY(bool encryptionAllowed READ encryptionAllowed WRITE setEncryptionAllowed) - Q_PROPERTY(bool certificationAllowed READ certificationAllowed WRITE setCertificationAllowed) - Q_PROPERTY(bool authenticationAllowed READ authenticationAllowed WRITE setAuthenticationAllowed) - Q_PROPERTY(QDate expiryDate READ expiryDate WRITE setExpiryDate) public: explicit AdvancedSettingsDialog(QWidget *parent = nullptr); ~AdvancedSettingsDialog() override; - bool unlimitedValidityIsAllowed() const; - - void setProtocol(GpgME::Protocol proto); - - void setAdditionalUserIDs(const QStringList &items); - QStringList additionalUserIDs() const; - void setAdditionalEMailAddresses(const QStringList &items); QStringList additionalEMailAddresses() const; void setDnsNames(const QStringList &items); QStringList dnsNames() const; void setUris(const QStringList &items); QStringList uris() const; void setKeyStrength(unsigned int strength); unsigned int keyStrength() const; - void setKeyType(GpgME::Subkey::PubkeyAlgo algo); GpgME::Subkey::PubkeyAlgo keyType() const; - void setKeyCurve(const QString &curve); - QString keyCurve() const; - - void setSubkeyType(GpgME::Subkey::PubkeyAlgo algo); - GpgME::Subkey::PubkeyAlgo subkeyType() const; - - void setSubkeyCurve(const QString &curve); - QString subkeyCurve() const; - - void setSubkeyStrength(unsigned int strength); - unsigned int subkeyStrength() const; - void setSigningAllowed(bool on); bool signingAllowed() const; void setEncryptionAllowed(bool on); bool encryptionAllowed() const; - void setCertificationAllowed(bool on); - bool certificationAllowed() const; - - void setAuthenticationAllowed(bool on); - bool authenticationAllowed() const; - - QDate forceDateIntoAllowedRange(QDate date) const; - - void setExpiryDate(QDate date); - QDate expiryDate() const; - Q_SIGNALS: void changed(); private Q_SLOTS: - void slotKeyMaterialSelectionChanged(); void slotSigningAllowedToggled(bool on); void slotEncryptionAllowedToggled(bool on); private: void fillKeySizeComboBoxen(); void loadDefaultKeyType(); - void loadDefaultExpiration(); void loadDefaultGnuPGKeyType(); void loadDefaults(); void updateWidgetVisibility(); void setInitialFocus(); protected: void accept() override; void showEvent(QShowEvent *event) override; private: struct UI; std::unique_ptr ui; - GpgME::Protocol protocol = GpgME::UnknownProtocol; - unsigned int pgpDefaultAlgorithm = GpgME::Subkey::AlgoELG_E; unsigned int cmsDefaultAlgorithm = GpgME::Subkey::AlgoRSA; bool keyTypeImmutable = false; - bool mECCSupported; - bool mEdDSASupported; bool isFirstShowEvent = true; }; diff --git a/src/newcertificatewizard/enterdetailspage.cpp b/src/newcertificatewizard/enterdetailspage.cpp index e31cfcfc2..ea2c43549 100644 --- a/src/newcertificatewizard/enterdetailspage.cpp +++ b/src/newcertificatewizard/enterdetailspage.cpp @@ -1,538 +1,529 @@ /* -*- mode: c++; c-basic-offset:4 -*- newcertificatewizard/enterdetailspage.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-FileCopyrightText: 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "enterdetailspage_p.h" #include "advancedsettingsdialog_p.h" #include "utils/scrollarea.h" #include "utils/userinfo.h" #include "utils/validation.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::NewCertificateUi; using namespace GpgME; static void set_tab_order(const QList &wl) { kdtools::for_each_adjacent_pair(wl.begin(), wl.end(), [](QWidget *w1, QWidget *w2) { QWidget::setTabOrder(w1, w2); }); } static QString pgpLabel(const QString &attr) { if (attr == QLatin1StringView("NAME")) { return i18n("Name"); } if (attr == QLatin1StringView("EMAIL")) { return i18n("EMail"); } return QString(); } static QString attributeLabel(const QString &attr, bool pgp) { if (attr.isEmpty()) { return QString(); } const QString label = pgp ? pgpLabel(attr) : Kleo::DN::attributeNameToLabel(attr); if (!label.isEmpty()) if (pgp) { return label; } else return i18nc("Format string for the labels in the \"Your Personal Data\" page", "%1 (%2)", label, attr); else { return attr; } } static QString attributeFromKey(QString key) { return key.remove(QLatin1Char('!')); } struct EnterDetailsPage::UI { QGridLayout *gridLayout = nullptr; QLabel *nameLB = nullptr; QLineEdit *nameLE = nullptr; QLabel *nameRequiredLB = nullptr; QLabel *emailLB = nullptr; QLineEdit *emailLE = nullptr; QLabel *emailRequiredLB = nullptr; QCheckBox *withPassCB = nullptr; QLineEdit *resultLE = nullptr; QLabel *errorLB = nullptr; QPushButton *advancedPB = nullptr; UI(QWizardPage *parent) { parent->setTitle(i18nc("@title", "Enter Details")); auto mainLayout = new QVBoxLayout{parent}; const auto margins = mainLayout->contentsMargins(); mainLayout->setContentsMargins(margins.left(), 0, margins.right(), 0); auto scrollArea = new ScrollArea{parent}; scrollArea->setFocusPolicy(Qt::NoFocus); scrollArea->setFrameStyle(QFrame::NoFrame); scrollArea->setBackgroundRole(parent->backgroundRole()); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); scrollArea->setSizeAdjustPolicy(QScrollArea::AdjustToContents); auto scrollAreaLayout = qobject_cast(scrollArea->widget()->layout()); scrollAreaLayout->setContentsMargins(0, margins.top(), 0, margins.bottom()); gridLayout = new QGridLayout; int row = 0; nameLB = new QLabel{i18n("Real name:"), parent}; nameLE = new QLineEdit{parent}; nameRequiredLB = new QLabel{i18n("(required)"), parent}; gridLayout->addWidget(nameLB, row, 0, 1, 1); gridLayout->addWidget(nameLE, row, 1, 1, 1); gridLayout->addWidget(nameRequiredLB, row, 2, 1, 1); row++; emailLB = new QLabel{i18n("EMail address:"), parent}; emailLE = new QLineEdit{parent}; emailRequiredLB = new QLabel{i18n("(required)"), parent}; gridLayout->addWidget(emailLB, row, 0, 1, 1); gridLayout->addWidget(emailLE, row, 1, 1, 1); gridLayout->addWidget(emailRequiredLB, row, 2, 1, 1); row++; withPassCB = new QCheckBox{i18n("Protect the generated key with a passphrase."), parent}; withPassCB->setToolTip(i18n("Encrypts the secret key with an unrecoverable passphrase. You will be asked for the passphrase during key generation.")); gridLayout->addWidget(withPassCB, row, 1, 1, 2); scrollAreaLayout->addLayout(gridLayout); auto verticalSpacer = new QSpacerItem{20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding}; scrollAreaLayout->addItem(verticalSpacer); resultLE = new QLineEdit{parent}; resultLE->setFrame(false); resultLE->setAlignment(Qt::AlignCenter); resultLE->setReadOnly(true); scrollAreaLayout->addWidget(resultLE); auto horizontalLayout = new QHBoxLayout; errorLB = new QLabel{parent}; QSizePolicy sizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Preferred); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); sizePolicy.setHeightForWidth(errorLB->sizePolicy().hasHeightForWidth()); errorLB->setSizePolicy(sizePolicy); QPalette palette; QBrush brush(QColor(255, 0, 0, 255)); brush.setStyle(Qt::SolidPattern); palette.setBrush(QPalette::Active, QPalette::WindowText, brush); palette.setBrush(QPalette::Inactive, QPalette::WindowText, brush); QBrush brush1(QColor(114, 114, 114, 255)); brush1.setStyle(Qt::SolidPattern); palette.setBrush(QPalette::Disabled, QPalette::WindowText, brush1); errorLB->setPalette(palette); errorLB->setTextFormat(Qt::RichText); horizontalLayout->addWidget(errorLB); advancedPB = new QPushButton{i18n("Advanced Settings..."), parent}; advancedPB->setAutoDefault(false); horizontalLayout->addWidget(advancedPB); scrollAreaLayout->addLayout(horizontalLayout); mainLayout->addWidget(scrollArea); } }; EnterDetailsPage::EnterDetailsPage(QWidget *p) : WizardPage{p} , ui{new UI{this}} , dialog{new AdvancedSettingsDialog{this}} { setObjectName(QLatin1StringView("Kleo__NewCertificateUi__EnterDetailsPage")); Settings settings; if (settings.hideAdvanced()) { setSubTitle(i18n("Please enter your personal details below.")); } else { setSubTitle(i18n("Please enter your personal details below. If you want more control over the parameters, click on the Advanced Settings button.")); } ui->advancedPB->setVisible(!settings.hideAdvanced()); ui->resultLE->setFocusPolicy(Qt::NoFocus); // set errorLB to have a fixed height of two lines: ui->errorLB->setText(QStringLiteral("2
1")); ui->errorLB->setFixedHeight(ui->errorLB->minimumSizeHint().height()); ui->errorLB->clear(); connect(ui->advancedPB, &QPushButton::clicked, this, &EnterDetailsPage::slotAdvancedSettingsClicked); connect(ui->resultLE, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); // The email doesn't necessarily show up in ui->resultLE: connect(ui->emailLE, &QLineEdit::textChanged, this, &QWizardPage::completeChanged); registerDialogPropertiesAsFields(); registerField(QStringLiteral("dn"), ui->resultLE); registerField(QStringLiteral("name"), ui->nameLE); registerField(QStringLiteral("email"), ui->emailLE); registerField(QStringLiteral("protectedKey"), ui->withPassCB); setCommitPage(true); setButtonText(QWizard::CommitButton, i18nc("@action", "Create")); const auto conf = QGpgME::cryptoConfig(); if (!conf) { qCWarning(KLEOPATRA_LOG) << "Failed to obtain cryptoConfig."; return; } const auto entry = getCryptoConfigEntry(conf, "gpg-agent", "enforce-passphrase-constraints"); if (entry && entry->boolValue()) { qCDebug(KLEOPATRA_LOG) << "Disabling passphrace cb because of agent config."; ui->withPassCB->setEnabled(false); ui->withPassCB->setChecked(true); } else { const KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("CertificateCreationWizard")); ui->withPassCB->setChecked(config.readEntry("WithPassphrase", false)); ui->withPassCB->setEnabled(!config.isEntryImmutable("WithPassphrase")); } } EnterDetailsPage::~EnterDetailsPage() = default; void EnterDetailsPage::initializePage() { updateForm(); - ui->withPassCB->setVisible(pgp()); - dialog->setProtocol(pgp() ? OpenPGP : CMS); + ui->withPassCB->setVisible(false); } void EnterDetailsPage::cleanupPage() { saveValues(); } void EnterDetailsPage::registerDialogPropertiesAsFields() { const QMetaObject *const mo = dialog->metaObject(); for (unsigned int i = mo->propertyOffset(), end = i + mo->propertyCount(); i != end; ++i) { const QMetaProperty mp = mo->property(i); if (mp.isValid()) { registerField(QLatin1StringView(mp.name()), dialog, mp.name(), SIGNAL(accepted())); } } } void EnterDetailsPage::saveValues() { for (const Line &line : std::as_const(lineList)) { savedValues[attributeFromKey(line.attr)] = line.edit->text().trimmed(); } } void EnterDetailsPage::clearForm() { qDeleteAll(dynamicWidgets); dynamicWidgets.clear(); lineList.clear(); ui->nameLE->hide(); ui->nameLE->clear(); ui->nameLB->hide(); ui->nameRequiredLB->hide(); ui->emailLE->hide(); ui->emailLE->clear(); ui->emailLB->hide(); ui->emailRequiredLB->hide(); } static int row_index_of(QWidget *w, QGridLayout *l) { const int idx = l->indexOf(w); int r, c, rs, cs; l->getItemPosition(idx, &r, &c, &rs, &cs); return r; } static QLineEdit * adjust_row(QGridLayout *l, int row, const QString &label, const QString &preset, const std::shared_ptr &validator, bool readonly, bool required) { Q_ASSERT(l); Q_ASSERT(row >= 0); Q_ASSERT(row < l->rowCount()); auto lb = qobject_cast(l->itemAtPosition(row, 0)->widget()); Q_ASSERT(lb); auto le = qobject_cast(l->itemAtPosition(row, 1)->widget()); Q_ASSERT(le); lb->setBuddy(le); // For better accessibility auto reqLB = qobject_cast(l->itemAtPosition(row, 2)->widget()); Q_ASSERT(reqLB); lb->setText(i18nc("interpunctation for labels", "%1:", label)); le->setText(preset); reqLB->setText(required ? i18n("(required)") : i18n("(optional)")); if (validator) { le->setValidator(validator.get()); } le->setReadOnly(readonly && le->hasAcceptableInput()); lb->show(); le->show(); reqLB->show(); return le; } static int add_row(QGridLayout *l, QList *wl) { Q_ASSERT(l); Q_ASSERT(wl); const int row = l->rowCount(); QWidget *w1, *w2, *w3; l->addWidget(w1 = new QLabel(l->parentWidget()), row, 0); l->addWidget(w2 = new QLineEdit(l->parentWidget()), row, 1); l->addWidget(w3 = new QLabel(l->parentWidget()), row, 2); wl->push_back(w1); wl->push_back(w2); wl->push_back(w3); return row; } void EnterDetailsPage::updateForm() { clearForm(); const auto settings = Kleo::Settings{}; const KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("CertificateCreationWizard")); - QStringList attrOrder = config.readEntry(pgp() ? "OpenPGPAttributeOrder" : "DNAttributeOrder", QStringList()); + QStringList attrOrder = config.readEntry("DNAttributeOrder", QStringList()); if (attrOrder.empty()) { - if (pgp()) { - attrOrder << QStringLiteral("NAME") << QStringLiteral("EMAIL"); - } else { - attrOrder << QStringLiteral("CN!") << QStringLiteral("L") << QStringLiteral("OU") << QStringLiteral("O") << QStringLiteral("C") - << QStringLiteral("EMAIL!"); - } + attrOrder << QStringLiteral("CN!") << QStringLiteral("L") << QStringLiteral("OU") << QStringLiteral("O") << QStringLiteral("C") + << QStringLiteral("EMAIL!"); } QList widgets; widgets.push_back(ui->nameLE); widgets.push_back(ui->emailLE); QMap lines; for (const QString &rawKey : std::as_const(attrOrder)) { const QString key = rawKey.trimmed().toUpper(); const QString attr = attributeFromKey(key); if (attr.isEmpty()) { continue; } const QString preset = savedValues.value(attr, config.readEntry(attr, QString())); const bool required = key.endsWith(QLatin1Char('!')); const bool readonly = config.isEntryImmutable(attr); - const QString label = config.readEntry(attr + QLatin1StringView("_label"), attributeLabel(attr, pgp())); + const QString label = config.readEntry(attr + QLatin1StringView("_label"), attributeLabel(attr, false)); const QString regex = config.readEntry(attr + QLatin1StringView("_regex")); const QString placeholder = config.readEntry(attr + QLatin1StringView{"_placeholder"}); int row; bool known = true; std::shared_ptr validator; if (attr == QLatin1StringView("EMAIL")) { row = row_index_of(ui->emailLE, ui->gridLayout); validator = regex.isEmpty() ? Validation::email() : Validation::email(regex); } else if (attr == QLatin1StringView("NAME") || attr == QLatin1String("CN")) { - if ((pgp() && attr == QLatin1StringView("CN")) || (!pgp() && attr == QLatin1String("NAME"))) { + if (attr == QLatin1String("NAME")) { continue; } - if (pgp()) { - validator = regex.isEmpty() ? Validation::pgpName() : Validation::pgpName(regex); - } row = row_index_of(ui->nameLE, ui->gridLayout); } else { known = false; row = add_row(ui->gridLayout, &dynamicWidgets); } if (!validator && !regex.isEmpty()) { validator = std::make_shared(QRegularExpression{regex}); } QLineEdit *le = adjust_row(ui->gridLayout, row, label, preset, validator, readonly, required); le->setPlaceholderText(placeholder); const Line line = {key, label, regex, le, validator}; lines[row] = line; if (!known) { widgets.push_back(le); } // don't connect twice: disconnect(le, &QLineEdit::textChanged, this, &EnterDetailsPage::slotUpdateResultLabel); connect(le, &QLineEdit::textChanged, this, &EnterDetailsPage::slotUpdateResultLabel); } // create lineList in visual order, so requirementsAreMet() // complains from top to bottom: lineList.reserve(lines.count()); std::copy(lines.cbegin(), lines.cend(), std::back_inserter(lineList)); widgets.push_back(ui->withPassCB); widgets.push_back(ui->advancedPB); - const bool prefillName = (pgp() && settings.prefillName()) || (!pgp() && settings.prefillCN()); - if (ui->nameLE->text().isEmpty() && prefillName) { + if (ui->nameLE->text().isEmpty() && settings.prefillCN()) { ui->nameLE->setText(userFullName()); } if (ui->emailLE->text().isEmpty() && settings.prefillEmail()) { ui->emailLE->setText(userEmailAddress()); } slotUpdateResultLabel(); set_tab_order(widgets); } QString EnterDetailsPage::cmsDN() const { DN dn; for (QList::const_iterator it = lineList.begin(), end = lineList.end(); it != end; ++it) { const QString text = it->edit->text().trimmed(); if (text.isEmpty()) { continue; } QString attr = attributeFromKey(it->attr); if (attr == QLatin1StringView("EMAIL")) { continue; } if (const char *const oid = oidForAttributeName(attr)) { attr = QString::fromUtf8(oid); } dn.append(DN::Attribute(attr, text)); } return dn.dn(); } QString EnterDetailsPage::pgpUserID() const { return Formatting::prettyNameAndEMail(OpenPGP, QString(), ui->nameLE->text().trimmed(), ui->emailLE->text().trimmed(), QString()); } static bool has_intermediate_input(const QLineEdit *le) { QString text = le->text(); int pos = le->cursorPosition(); const QValidator *const v = le->validator(); return v && v->validate(text, pos) == QValidator::Intermediate; } static bool requirementsAreMet(const QList &list, QString &error) { bool allEmpty = true; for (const auto &line : list) { const QLineEdit *le = line.edit; if (!le) { continue; } const QString key = line.attr; qCDebug(KLEOPATRA_LOG) << "requirementsAreMet(): checking" << key << "against" << le->text() << ":"; if (le->text().trimmed().isEmpty()) { if (key.endsWith(QLatin1Char('!'))) { if (line.regex.isEmpty()) { error = xi18nc("@info", "%1 is required, but empty.", line.label); } else error = xi18nc("@info", "%1 is required, but empty." "Local Admin rule: %2", line.label, line.regex); return false; } } else if (has_intermediate_input(le)) { if (line.regex.isEmpty()) { error = xi18nc("@info", "%1 is incomplete.", line.label); } else error = xi18nc("@info", "%1 is incomplete." "Local Admin rule: %2", line.label, line.regex); return false; } else if (!le->hasAcceptableInput()) { if (line.regex.isEmpty()) { error = xi18nc("@info", "%1 is invalid.", line.label); } else error = xi18nc("@info", "%1 is invalid." "Local Admin rule: %2", line.label, line.regex); return false; } else { allEmpty = false; } } // Ensure that at least one value is acceptable return !allEmpty; } bool EnterDetailsPage::isComplete() const { QString error; const bool ok = requirementsAreMet(lineList, error); ui->errorLB->setText(error); return ok; } void EnterDetailsPage::slotAdvancedSettingsClicked() { dialog->exec(); } void EnterDetailsPage::slotUpdateResultLabel() { - ui->resultLE->setText(pgp() ? pgpUserID() : cmsDN()); + ui->resultLE->setText(cmsDN()); } #include "moc_enterdetailspage_p.cpp" diff --git a/src/newcertificatewizard/keycreationpage.cpp b/src/newcertificatewizard/keycreationpage.cpp index 92697652e..1da1718c2 100644 --- a/src/newcertificatewizard/keycreationpage.cpp +++ b/src/newcertificatewizard/keycreationpage.cpp @@ -1,249 +1,175 @@ /* -*- mode: c++; c-basic-offset:4 -*- newcertificatewizard/keycreationpage.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-FileCopyrightText: 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "keycreationpage_p.h" -#include "keyalgo_p.h" - #include "kleopatraapplication.h" #include "utils/keyparameters.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::NewCertificateUi; using namespace GpgME; struct KeyCreationPage::UI { UI(QWizardPage *parent) { parent->setTitle(i18nc("@title", "Creating Key Pair...")); auto mainLayout = new QVBoxLayout{parent}; auto label = new QLabel{i18n("The process of creating a key requires large amounts of random numbers. This may require several minutes..."), parent}; label->setWordWrap(true); mainLayout->addWidget(label); } }; KeyCreationPage::KeyCreationPage(QWidget *p) : WizardPage{p} , ui{new UI{this}} { setObjectName(QString::fromUtf8("Kleo__NewCertificateUi__KeyCreationPage")); } KeyCreationPage::~KeyCreationPage() = default; bool KeyCreationPage::isComplete() const { return !job; } void KeyCreationPage::initializePage() { startJob(); } void KeyCreationPage::startJob() { - const auto proto = pgp() ? QGpgME::openpgp() : QGpgME::smime(); - if (!proto) { - return; - } - QGpgME::KeyGenerationJob *const j = proto->keyGenerationJob(); + auto j = QGpgME::smime()->keyGenerationJob(); if (!j) { return; } - if (!protectedKey() && pgp()) { - auto ctx = QGpgME::Job::context(j); - ctx->setPassphraseProvider(&mEmptyPassphraseProvider); - ctx->setPinentryMode(Context::PinentryLoopback); - } connect(j, &QGpgME::KeyGenerationJob::result, this, &KeyCreationPage::slotResult); if (const Error err = j->start(createGnupgKeyParms())) setField(QStringLiteral("error"), i18n("Could not start key pair creation: %1", Formatting::errorAsString(err))); else { job = j; } } KeyUsage KeyCreationPage::keyUsage() const { KeyUsage usage; if (signingAllowed()) { usage.setCanSign(true); } - if (encryptionAllowed() && !is_ecdh(subkeyType()) && !is_dsa(keyType()) && !is_rsa(subkeyType())) { - usage.setCanEncrypt(true); - } - if (authenticationAllowed()) { - usage.setCanAuthenticate(true); - } - if (!usage.value() && certificationAllowed()) { - /* Empty usages cause an error so we need to - * add at least certify if nothing else is selected */ - usage.setCanCertify(true); - } - return usage; -} - -KeyUsage KeyCreationPage::subkeyUsage() const -{ - KeyUsage usage; - if (encryptionAllowed() && (is_dsa(keyType()) || is_rsa(subkeyType()) || is_ecdh(subkeyType()))) { - Q_ASSERT(subkeyType()); + if (encryptionAllowed()) { usage.setCanEncrypt(true); } return usage; } QString KeyCreationPage::createGnupgKeyParms() const { - KeyParameters keyParameters(pgp() ? KeyParameters::OpenPGP : KeyParameters::CMS); + KeyParameters keyParameters(KeyParameters::CMS); keyParameters.setKeyType(keyType()); - if (is_ecdsa(keyType()) || is_eddsa(keyType())) { - keyParameters.setKeyCurve(keyCurve()); - } else if (const unsigned int strength = keyStrength()) { + if (const unsigned int strength = keyStrength()) { keyParameters.setKeyLength(strength); } keyParameters.setKeyUsage(keyUsage()); - if (subkeyType()) { - keyParameters.setSubkeyType(subkeyType()); - if (is_ecdh(subkeyType())) { - keyParameters.setSubkeyCurve(subkeyCurve()); - } else if (const unsigned int strength = subkeyStrength()) { - keyParameters.setSubkeyLength(strength); - } - keyParameters.setSubkeyUsage(subkeyUsage()); + keyParameters.setDN(dn()); + keyParameters.setEmail(email()); + const auto addesses{additionalEMailAddresses()}; + for (const QString &email : addesses) { + keyParameters.addEmail(email); } - - if (pgp()) { - if (expiryDate().isValid()) { - keyParameters.setExpirationDate(expiryDate()); - } - if (!name().isEmpty()) { - keyParameters.setName(name()); - } - if (!email().isEmpty()) { - keyParameters.setEmail(email()); - } - } else { - keyParameters.setDN(dn()); - keyParameters.setEmail(email()); - const auto addesses{additionalEMailAddresses()}; - for (const QString &email : addesses) { - keyParameters.addEmail(email); - } - const auto dnsN{dnsNames()}; - for (const QString &dns : dnsN) { - keyParameters.addDomainName(dns); - } - const auto urisList{uris()}; - for (const QString &uri : urisList) { - keyParameters.addURI(uri); - } + const auto dnsN{dnsNames()}; + for (const QString &dns : dnsN) { + keyParameters.addDomainName(dns); + } + const auto urisList{uris()}; + for (const QString &uri : urisList) { + keyParameters.addURI(uri); } const QString result = keyParameters.toString(); qCDebug(KLEOPATRA_LOG) << '\n' << result; return result; } void KeyCreationPage::slotResult(const GpgME::KeyGenerationResult &result, const QByteArray &request, const QString &auditLog) { Q_UNUSED(auditLog) - if (result.error().code() || (pgp() && !result.fingerprint())) { + if (result.error().code()) { setField(QStringLiteral("error"), result.error().isCanceled() ? i18n("Operation canceled.") : i18n("Could not create key pair: %1", Formatting::errorAsString(result.error()))); setField(QStringLiteral("url"), QString()); setField(QStringLiteral("result"), QString()); - } else if (pgp()) { - setField(QStringLiteral("error"), QString()); - setField(QStringLiteral("url"), QString()); - setField(QStringLiteral("result"), - i18n("Key pair created successfully.\n" - "Fingerprint: %1", - Formatting::prettyID(result.fingerprint()))); } else { QFile file(tmpDir().absoluteFilePath(QStringLiteral("request.p10"))); if (!file.open(QIODevice::WriteOnly)) { setField(QStringLiteral("error"), i18n("Could not write output file %1: %2", file.fileName(), file.errorString())); setField(QStringLiteral("url"), QString()); setField(QStringLiteral("result"), QString()); } else { file.write(request); setField(QStringLiteral("error"), QString()); setField(QStringLiteral("url"), QUrl::fromLocalFile(file.fileName()).toString()); setField(QStringLiteral("result"), i18n("Key pair created successfully.")); } } - // Ensure that we have the key in the keycache - if (pgp() && !result.error().code() && result.fingerprint()) { - auto ctx = Context::createForProtocol(OpenPGP); - if (ctx) { - // Check is pretty useless something very buggy in that case. - Error e; - const auto key = ctx->key(result.fingerprint(), e, true); - if (!key.isNull()) { - KeyCache::mutableInstance()->insert(key); - } else { - qCDebug(KLEOPATRA_LOG) << "Failed to find newly generated key."; - } - delete ctx; - } - } + setField(QStringLiteral("fingerprint"), result.fingerprint() ? QString::fromLatin1(result.fingerprint()) : QString()); job = nullptr; Q_EMIT completeChanged(); const KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("CertificateCreationWizard")); if (config.readEntry("SkipResultPage", false)) { if (result.fingerprint()) { KleopatraApplication::instance()->slotActivateRequested(QStringList() << QStringLiteral("kleopatra") << QStringLiteral("--query") << QLatin1StringView(result.fingerprint()), QString()); QMetaObject::invokeMethod(wizard(), "close", Qt::QueuedConnection); } else { QMetaObject::invokeMethod(wizard(), "next", Qt::QueuedConnection); } } else { QMetaObject::invokeMethod(wizard(), "next", Qt::QueuedConnection); } } #include "moc_keycreationpage_p.cpp" diff --git a/src/newcertificatewizard/keycreationpage_p.h b/src/newcertificatewizard/keycreationpage_p.h index 41b086ab1..db69bd1b9 100644 --- a/src/newcertificatewizard/keycreationpage_p.h +++ b/src/newcertificatewizard/keycreationpage_p.h @@ -1,59 +1,58 @@ /* -*- mode: c++; c-basic-offset:4 -*- newcertificatewizard/keycreationpage_p.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-FileCopyrightText: 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "wizardpage_p.h" #include "utils/emptypassphraseprovider.h" namespace GpgME { class KeyGenerationResult; } namespace QGpgME { class KeyGenerationJob; } namespace Kleo { class KeyUsage; } class KeyCreationPage : public Kleo::NewCertificateUi::WizardPage { Q_OBJECT public: explicit KeyCreationPage(QWidget *p = nullptr); ~KeyCreationPage() override; bool isComplete() const override; void initializePage() override; private: void startJob(); Kleo::KeyUsage keyUsage() const; - Kleo::KeyUsage subkeyUsage() const; QString createGnupgKeyParms() const; private Q_SLOTS: void slotResult(const GpgME::KeyGenerationResult &result, const QByteArray &request, const QString &auditLog); private: struct UI; std::unique_ptr ui; EmptyPassphraseProvider mEmptyPassphraseProvider; QPointer job; }; diff --git a/src/newcertificatewizard/resultpage.cpp b/src/newcertificatewizard/resultpage.cpp index 5f8abd98d..ae773d3b6 100644 --- a/src/newcertificatewizard/resultpage.cpp +++ b/src/newcertificatewizard/resultpage.cpp @@ -1,369 +1,286 @@ /* -*- mode: c++; c-basic-offset:4 -*- newcertificatewizard/resultpage.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-FileCopyrightText: 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "resultpage_p.h" #include "commands/exportcertificatecommand.h" #include "commands/exportopenpgpcertstoservercommand.h" #include "commands/exportsecretkeycommand.h" #include "utils/dragqueen.h" #include "utils/email.h" #include "utils/filedialog.h" #include "utils/scrollarea.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::NewCertificateUi; using namespace GpgME; struct ResultPage::UI { QTextBrowser *resultTB = nullptr; QTextBrowser *errorTB = nullptr; DragQueen *dragQueen = nullptr; QPushButton *restartWizardPB = nullptr; QGroupBox *nextStepsGB = nullptr; QPushButton *saveRequestToFilePB = nullptr; QPushButton *sendRequestByEMailPB = nullptr; - QPushButton *makeBackupPB = nullptr; - QPushButton *sendCertificateByEMailPB = nullptr; - QPushButton *uploadToKeyserverPB = nullptr; - QPushButton *createRevocationRequestPB = nullptr; QPushButton *createSigningCertificatePB = nullptr; QPushButton *createEncryptionCertificatePB = nullptr; UI(QWizardPage *parent) { auto mainLayout = new QVBoxLayout{parent}; const auto margins = mainLayout->contentsMargins(); mainLayout->setContentsMargins(margins.left(), 0, margins.right(), 0); auto scrollArea = new ScrollArea{parent}; scrollArea->setFocusPolicy(Qt::NoFocus); scrollArea->setFrameStyle(QFrame::NoFrame); scrollArea->setBackgroundRole(parent->backgroundRole()); scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); scrollArea->setSizeAdjustPolicy(QScrollArea::AdjustToContents); auto scrollAreaLayout = qobject_cast(scrollArea->widget()->layout()); scrollAreaLayout->setContentsMargins(0, margins.top(), 0, margins.bottom()); auto resultGB = new QGroupBox{i18nc("@title:group", "Result"), parent}; auto resultGBLayout = new QHBoxLayout{resultGB}; resultTB = new QTextBrowser{resultGB}; resultGBLayout->addWidget(resultTB); errorTB = new QTextBrowser{resultGB}; resultGBLayout->addWidget(errorTB); dragQueen = new Kleo::DragQueen{resultGB}; dragQueen->setToolTip(i18n("Drag this icon to your mail application's composer to attach the request to a mail.")); dragQueen->setAlignment(Qt::AlignCenter); resultGBLayout->addWidget(dragQueen); scrollAreaLayout->addWidget(resultGB); restartWizardPB = new QPushButton{i18n("Restart This Wizard (Keeps Your Parameters)"), parent}; scrollAreaLayout->addWidget(restartWizardPB); nextStepsGB = new QGroupBox{i18nc("@title:group", "Next Steps"), parent}; auto nextStepsGBLayout = new QVBoxLayout{nextStepsGB}; saveRequestToFilePB = new QPushButton{i18n("Save Certificate Request To File..."), nextStepsGB}; nextStepsGBLayout->addWidget(saveRequestToFilePB); sendRequestByEMailPB = new QPushButton{i18n("Send Certificate Request By EMail..."), nextStepsGB}; nextStepsGBLayout->addWidget(sendRequestByEMailPB); - makeBackupPB = new QPushButton{i18n("Make a Backup Of Your Key Pair..."), nextStepsGB}; - nextStepsGBLayout->addWidget(makeBackupPB); - - sendCertificateByEMailPB = new QPushButton{i18n("Send Public Key By EMail..."), nextStepsGB}; - nextStepsGBLayout->addWidget(sendCertificateByEMailPB); - - uploadToKeyserverPB = new QPushButton{i18n("Upload Public Key To Directory Service..."), nextStepsGB}; - nextStepsGBLayout->addWidget(uploadToKeyserverPB); - - createRevocationRequestPB = new QPushButton{i18n("Create Revocation Request..."), nextStepsGB}; - nextStepsGBLayout->addWidget(createRevocationRequestPB); - createSigningCertificatePB = new QPushButton{i18n("Create Signing Certificate With Same Parameters"), nextStepsGB}; nextStepsGBLayout->addWidget(createSigningCertificatePB); createEncryptionCertificatePB = new QPushButton{i18n("Create Encryption Certificate With Same Parameters"), nextStepsGB}; nextStepsGBLayout->addWidget(createEncryptionCertificatePB); scrollAreaLayout->addWidget(nextStepsGB); mainLayout->addWidget(scrollArea); } }; ResultPage::ResultPage(QWidget *p) : WizardPage{p} , ui{new UI{this}} , initialized{false} , successfullyCreatedSigningCertificate{false} , successfullyCreatedEncryptionCertificate{false} { setObjectName(QString::fromUtf8("Kleo__NewCertificateUi__ResultPage")); connect(ui->saveRequestToFilePB, &QPushButton::clicked, this, &ResultPage::slotSaveRequestToFile); connect(ui->sendRequestByEMailPB, &QPushButton::clicked, this, &ResultPage::slotSendRequestByEMail); - connect(ui->sendCertificateByEMailPB, &QPushButton::clicked, this, &ResultPage::slotSendCertificateByEMail); - connect(ui->uploadToKeyserverPB, &QPushButton::clicked, this, &ResultPage::slotUploadCertificateToDirectoryServer); - connect(ui->makeBackupPB, &QPushButton::clicked, this, &ResultPage::slotBackupCertificate); - connect(ui->createRevocationRequestPB, &QPushButton::clicked, this, &ResultPage::slotCreateRevocationRequest); connect(ui->createSigningCertificatePB, &QPushButton::clicked, this, &ResultPage::slotCreateSigningCertificate); connect(ui->createEncryptionCertificatePB, &QPushButton::clicked, this, &ResultPage::slotCreateEncryptionCertificate); ui->dragQueen->setPixmap(QIcon::fromTheme(QStringLiteral("kleopatra")).pixmap(64, 64)); registerField(QStringLiteral("error"), ui->errorTB, "plainText"); registerField(QStringLiteral("result"), ui->resultTB, "plainText"); registerField(QStringLiteral("url"), ui->dragQueen, "url"); // hidden field, since QWizard can't deal with non-widget-backed fields... auto le = new QLineEdit(this); le->hide(); registerField(QStringLiteral("fingerprint"), le); } ResultPage::~ResultPage() = default; void ResultPage::initializePage() { const bool error = isError(); if (error) { setTitle(i18nc("@title", "Key Creation Failed")); setSubTitle(i18n("Key pair creation failed. Please find details about the failure below.")); } else { setTitle(i18nc("@title", "Key Pair Successfully Created")); setSubTitle(i18n("Your new key pair was created successfully. Please find details on the result and some suggested next steps below.")); } ui->resultTB->setVisible(!error); ui->errorTB->setVisible(error); - ui->dragQueen->setVisible(!error && !pgp()); + ui->dragQueen->setVisible(!error); ui->restartWizardPB->setVisible(error); ui->nextStepsGB->setVisible(!error); - ui->saveRequestToFilePB->setVisible(!pgp()); - ui->makeBackupPB->setVisible(pgp()); - ui->createRevocationRequestPB->setVisible(pgp() && false); // not implemented - - ui->sendCertificateByEMailPB->setVisible(pgp()); - ui->sendRequestByEMailPB->setVisible(!pgp()); - ui->uploadToKeyserverPB->setVisible(pgp()); + ui->saveRequestToFilePB->setVisible(true); + ui->sendRequestByEMailPB->setVisible(true); - if (!error && !pgp()) { + if (!error) { if (signingAllowed() && !encryptionAllowed()) { successfullyCreatedSigningCertificate = true; } else if (!signingAllowed() && encryptionAllowed()) { successfullyCreatedEncryptionCertificate = true; } else { successfullyCreatedEncryptionCertificate = successfullyCreatedSigningCertificate = true; } } ui->createSigningCertificatePB->setVisible(successfullyCreatedEncryptionCertificate && !successfullyCreatedSigningCertificate); ui->createEncryptionCertificatePB->setVisible(successfullyCreatedSigningCertificate && !successfullyCreatedEncryptionCertificate); if (error) { wizard()->setOptions(wizard()->options() & ~QWizard::NoCancelButtonOnLastPage); } else { wizard()->setOptions(wizard()->options() | QWizard::NoCancelButtonOnLastPage); } if (!initialized) { connect(ui->restartWizardPB, &QAbstractButton::clicked, this, [this]() { restartAtEnterDetailsPage(); }); } initialized = true; } bool ResultPage::isError() const { return !ui->errorTB->document()->isEmpty(); } bool ResultPage::isComplete() const { return !isError(); } Key ResultPage::key() const { return KeyCache::instance()->findByFingerprint(fingerprint().toLatin1().constData()); } void ResultPage::slotSaveRequestToFile() { QString fileName = FileDialog::getSaveFileName(this, i18nc("@title", "Save Request"), QStringLiteral("imp"), i18n("PKCS#10 Requests (*.p10)")); if (fileName.isEmpty()) { return; } if (!fileName.endsWith(QLatin1StringView(".p10"), Qt::CaseInsensitive)) { fileName += QLatin1StringView(".p10"); } QFile src(QUrl(url()).toLocalFile()); if (!src.copy(fileName)) KMessageBox::error(this, xi18nc("@info", "Could not copy temporary file %1 " "to file %2: %3", src.fileName(), fileName, src.errorString()), i18nc("@title", "Error Saving Request")); else KMessageBox::information(this, xi18nc("@info", "Successfully wrote request to %1." "You should now send the request to the Certification Authority (CA).", fileName), i18nc("@title", "Request Saved")); } void ResultPage::slotSendRequestByEMail() { - if (pgp()) { - return; - } const KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("CertificateCreationWizard")); invokeMailer(config.readEntry("CAEmailAddress"), // to i18n("Please process this certificate."), // subject i18n("Please process this certificate and inform the sender about the location to fetch the resulting certificate.\n\nThanks,\n"), // body QFileInfo{QUrl(url()).toLocalFile()}); // attachment KMessageBox::information(this, xi18nc("@info", "Kleopatra tried to send a mail via your default mail client." "Some mail clients are known not to support attachments when invoked this way." "If your mail client does not have an attachment, then drag the Kleopatra icon and drop " "it on the message compose window of your mail client." "If that does not work, either, save the request to a file, and then attach that."), i18nc("@title", "Sending Mail"), QStringLiteral("newcertificatewizard-mailto-troubles")); } -void ResultPage::slotSendCertificateByEMail() -{ - if (!pgp() || exportCertificateCommand) { - return; - } - auto cmd = new ExportCertificateCommand(key()); - connect(cmd, &ExportCertificateCommand::finished, this, &ResultPage::slotSendCertificateByEMailContinuation); - cmd->setOpenPGPFileName(tmpDir().absoluteFilePath(fingerprint() + QLatin1StringView(".asc"))); - cmd->start(); - exportCertificateCommand = cmd; -} - -void ResultPage::slotSendCertificateByEMailContinuation() -{ - if (!exportCertificateCommand) { - return; - } - // ### better error handling? - const QString fileName = exportCertificateCommand->openPGPFileName(); - qCDebug(KLEOPATRA_LOG) << "fileName" << fileName; - exportCertificateCommand = nullptr; - if (fileName.isEmpty()) { - return; - } - invokeMailer(i18n("My new public OpenPGP key"), // subject - i18n("Please find attached my new public OpenPGP key."), // body - QFileInfo{fileName}); - KMessageBox::information(this, - xi18nc("@info", - "Kleopatra tried to send a mail via your default mail client." - "Some mail clients are known not to support attachments when invoked this way." - "If your mail client does not have an attachment, then attach the file %1 manually.", - fileName), - i18nc("@title", "Sending Mail"), - QStringLiteral("newcertificatewizard-openpgp-mailto-troubles")); -} - -void ResultPage::slotUploadCertificateToDirectoryServer() -{ - if (pgp()) { - (new ExportOpenPGPCertsToServerCommand(key()))->start(); - } -} - -void ResultPage::slotBackupCertificate() -{ - if (pgp()) { - (new ExportSecretKeyCommand(key()))->start(); - } -} - -void ResultPage::slotCreateRevocationRequest() -{ -} - void ResultPage::slotCreateSigningCertificate() { if (successfullyCreatedSigningCertificate) { return; } toggleSignEncryptAndRestart(); } void ResultPage::slotCreateEncryptionCertificate() { if (successfullyCreatedEncryptionCertificate) { return; } toggleSignEncryptAndRestart(); } void ResultPage::toggleSignEncryptAndRestart() { if (!wizard()) { return; } if (KMessageBox::warningContinueCancel(this, i18nc("@info", "This operation will delete the certification request. " "Please make sure that you have sent or saved it before proceeding."), i18nc("@title", "Certification Request About To Be Deleted")) != KMessageBox::Continue) { return; } const bool sign = signingAllowed(); const bool encr = encryptionAllowed(); setField(QStringLiteral("signingAllowed"), !sign); setField(QStringLiteral("encryptionAllowed"), !encr); restartAtEnterDetailsPage(); } #include "moc_resultpage_p.cpp" diff --git a/src/newcertificatewizard/resultpage_p.h b/src/newcertificatewizard/resultpage_p.h index c155595ad..2ee757140 100644 --- a/src/newcertificatewizard/resultpage_p.h +++ b/src/newcertificatewizard/resultpage_p.h @@ -1,67 +1,62 @@ /* -*- mode: c++; c-basic-offset:4 -*- newcertificatewizard/resultpage_p.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-FileCopyrightText: 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "wizardpage_p.h" namespace GpgME { class Key; } namespace Kleo { class ExportCertificateCommand; } namespace Kleo::NewCertificateUi { class ResultPage : public WizardPage { Q_OBJECT public: explicit ResultPage(QWidget *p = nullptr); ~ResultPage() override; void initializePage() override; bool isError() const; bool isComplete() const override; private: GpgME::Key key() const; private Q_SLOTS: void slotSaveRequestToFile(); void slotSendRequestByEMail(); - void slotSendCertificateByEMail(); - void slotSendCertificateByEMailContinuation(); - void slotUploadCertificateToDirectoryServer(); - void slotBackupCertificate(); - void slotCreateRevocationRequest(); void slotCreateSigningCertificate(); void slotCreateEncryptionCertificate(); private: void toggleSignEncryptAndRestart(); private: struct UI; std::unique_ptr ui; bool initialized : 1; bool successfullyCreatedSigningCertificate : 1; bool successfullyCreatedEncryptionCertificate : 1; QPointer exportCertificateCommand; }; } diff --git a/src/newcertificatewizard/wizardpage_p.h b/src/newcertificatewizard/wizardpage_p.h index 1096cb7ea..a0543f040 100644 --- a/src/newcertificatewizard/wizardpage_p.h +++ b/src/newcertificatewizard/wizardpage_p.h @@ -1,100 +1,85 @@ /* -*- mode: c++; c-basic-offset:4 -*- newcertificatewizard/wizardpage_p.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "newcertificatewizard.h" #include "utils/metatypes_for_gpgmepp_key.h" #include #include #include #include #include namespace Kleo { namespace NewCertificateUi { class WizardPage : public QWizardPage { Q_OBJECT protected: explicit WizardPage(QWidget *parent = nullptr) : QWizardPage(parent) { } NewCertificateWizard *wizard() const { Q_ASSERT(static_cast(QWizardPage::wizard()) == qobject_cast(QWizardPage::wizard())); return static_cast(QWizardPage::wizard()); } - bool pgp() const - { - return wizard()->protocol() == GpgME::OpenPGP; - } - void restartAtEnterDetailsPage() { wizard()->restartAtEnterDetailsPage(); } QDir tmpDir() const { return wizard()->tmpDir(); } protected: #define FIELD(type, name) \ type name() const \ { \ return field(QStringLiteral(#name)).value(); \ } FIELD(bool, signingAllowed) FIELD(bool, encryptionAllowed) - FIELD(bool, certificationAllowed) - FIELD(bool, authenticationAllowed) FIELD(QString, name) FIELD(QString, email) FIELD(QString, dn) FIELD(bool, protectedKey) FIELD(GpgME::Subkey::PubkeyAlgo, keyType) FIELD(int, keyStrength) - FIELD(QString, keyCurve) - - FIELD(GpgME::Subkey::PubkeyAlgo, subkeyType) - FIELD(int, subkeyStrength) - FIELD(QString, subkeyCurve) - - FIELD(QDate, expiryDate) - FIELD(QStringList, additionalUserIDs) FIELD(QStringList, additionalEMailAddresses) FIELD(QStringList, dnsNames) FIELD(QStringList, uris) FIELD(QString, url) FIELD(QString, error) FIELD(QString, result) FIELD(QString, fingerprint) #undef FIELD }; } // namespace NewCertificateUi } // namespace Kleo