Warning, file /pim/kleopatra/src/newcertificatewizard/advancedsettingsdialog.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     newcertificatewizard/advancedsettingsdialog.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
0006     SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik
0007     SPDX-FileContributor: Intevation GmbH
0008     SPDX-FileCopyrightText: 2022 g10 Code GmbH
0009     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
0010 
0011     SPDX-License-Identifier: GPL-2.0-or-later
0012 */
0013 
0014 #include <config-kleopatra.h>
0015 
0016 #include "advancedsettingsdialog_p.h"
0017 #include "keyalgo_p.h"
0018 #include "listwidget.h"
0019 
0020 #include "utils/expiration.h"
0021 #include "utils/gui-helper.h"
0022 #include "utils/scrollarea.h"
0023 
0024 #include <settings.h>
0025 
0026 #include <Libkleo/Compat>
0027 #include <Libkleo/Compliance>
0028 #include <Libkleo/GnuPG>
0029 
0030 #include <KDateComboBox>
0031 #include <KLocalizedString>
0032 #include <KMessageBox>
0033 
0034 #include <QGpgME/CryptoConfig>
0035 #include <QGpgME/Protocol>
0036 
0037 #include <QCheckBox>
0038 #include <QDialogButtonBox>
0039 #include <QGridLayout>
0040 #include <QGroupBox>
0041 #include <QHBoxLayout>
0042 #include <QPushButton>
0043 #include <QRadioButton>
0044 #include <QTabWidget>
0045 #include <QVBoxLayout>
0046 
0047 #include "kleopatra_debug.h"
0048 
0049 using namespace Kleo;
0050 using namespace Kleo::NewCertificateUi;
0051 using namespace GpgME;
0052 
0053 static const char RSA_KEYSIZES_ENTRY[] = "RSAKeySizes";
0054 static const char DSA_KEYSIZES_ENTRY[] = "DSAKeySizes";
0055 static const char ELG_KEYSIZES_ENTRY[] = "ELGKeySizes";
0056 
0057 static const char RSA_KEYSIZE_LABELS_ENTRY[] = "RSAKeySizeLabels";
0058 static const char DSA_KEYSIZE_LABELS_ENTRY[] = "DSAKeySizeLabels";
0059 static const char ELG_KEYSIZE_LABELS_ENTRY[] = "ELGKeySizeLabels";
0060 
0061 static const char PGP_KEY_TYPE_ENTRY[] = "PGPKeyType";
0062 static const char CMS_KEY_TYPE_ENTRY[] = "CMSKeyType";
0063 
0064 // This should come from gpgme in the future
0065 // For now we only support the basic 2.1 curves and check
0066 // for GnuPG 2.1. The whole subkey / usage generation needs
0067 // new api and a reworked dialog. (ah 10.3.16)
0068 // EDDSA should be supported, too.
0069 static const QStringList curveNames{
0070     {QStringLiteral("brainpoolP256r1")},
0071     {QStringLiteral("brainpoolP384r1")},
0072     {QStringLiteral("brainpoolP512r1")},
0073     {QStringLiteral("NIST P-256")},
0074     {QStringLiteral("NIST P-384")},
0075     {QStringLiteral("NIST P-521")},
0076 };
0077 
0078 namespace
0079 {
0080 
0081 static void set_keysize(QComboBox *cb, unsigned int strength)
0082 {
0083     if (!cb) {
0084         return;
0085     }
0086     const int idx = cb->findData(static_cast<int>(strength));
0087     if (idx >= 0) {
0088         cb->setCurrentIndex(idx);
0089     }
0090 }
0091 
0092 static unsigned int get_keysize(const QComboBox *cb)
0093 {
0094     if (!cb) {
0095         return 0;
0096     }
0097     const int idx = cb->currentIndex();
0098     if (idx < 0) {
0099         return 0;
0100     }
0101     return cb->itemData(idx).toInt();
0102 }
0103 
0104 static void set_curve(QComboBox *cb, const QString &curve)
0105 {
0106     if (!cb) {
0107         return;
0108     }
0109     const int idx = cb->findText(curve, Qt::MatchFixedString);
0110     if (idx >= 0) {
0111         cb->setCurrentIndex(idx);
0112     }
0113 }
0114 
0115 static QString get_curve(const QComboBox *cb)
0116 {
0117     if (!cb) {
0118         return QString();
0119     }
0120     return cb->currentText();
0121 }
0122 
0123 // Extract the algo information from default_pubkey_algo format
0124 //
0125 // and put it into the return values size, algo and curve.
0126 //
0127 // Values look like:
0128 // RSA-2048
0129 // rsa2048/cert,sign+rsa2048/enc
0130 // brainpoolP256r1+brainpoolP256r1
0131 static void parseAlgoString(const QString &algoString, int *size, Subkey::PubkeyAlgo *algo, QString &curve)
0132 {
0133     const auto split = algoString.split(QLatin1Char('/'));
0134     bool isEncrypt = split.size() == 2 && split[1].contains(QLatin1StringView("enc"));
0135 
0136     // Normalize
0137     const auto lowered = split[0].toLower().remove(QLatin1Char('-'));
0138     if (!algo || !size) {
0139         return;
0140     }
0141     *algo = Subkey::AlgoUnknown;
0142     if (lowered.startsWith(QLatin1StringView("rsa"))) {
0143         *algo = Subkey::AlgoRSA;
0144     } else if (lowered.startsWith(QLatin1StringView("dsa"))) {
0145         *algo = Subkey::AlgoDSA;
0146     } else if (lowered.startsWith(QLatin1StringView("elg"))) {
0147         *algo = Subkey::AlgoELG;
0148     }
0149 
0150     if (*algo != Subkey::AlgoUnknown) {
0151         bool ok;
0152         *size = QStringView(lowered).right(lowered.size() - 3).toInt(&ok);
0153         if (!ok) {
0154             qCWarning(KLEOPATRA_LOG) << "Could not extract size from: " << lowered;
0155             *size = 3072;
0156         }
0157         return;
0158     }
0159 
0160     // Now the ECC Algorithms
0161     if (lowered.startsWith(QLatin1StringView("ed25519"))) {
0162         // Special handling for this as technically
0163         // this is a cv25519 curve used for EDDSA
0164         if (isEncrypt) {
0165             curve = QLatin1StringView("cv25519");
0166             *algo = Subkey::AlgoECDH;
0167         } else {
0168             curve = split[0];
0169             *algo = Subkey::AlgoEDDSA;
0170         }
0171         return;
0172     }
0173 
0174     if (lowered.startsWith(QLatin1StringView("cv25519")) //
0175         || lowered.startsWith(QLatin1StringView("nist")) //
0176         || lowered.startsWith(QLatin1StringView("brainpool")) //
0177         || lowered.startsWith(QLatin1StringView("secp"))) {
0178         curve = split[0];
0179         *algo = isEncrypt ? Subkey::AlgoECDH : Subkey::AlgoECDSA;
0180         return;
0181     }
0182 
0183     qCWarning(KLEOPATRA_LOG) << "Failed to parse default_pubkey_algo:" << algoString;
0184 }
0185 
0186 }
0187 
0188 struct AdvancedSettingsDialog::UI {
0189     QTabWidget *tabWidget = nullptr;
0190     QRadioButton *rsaRB = nullptr;
0191     QComboBox *rsaKeyStrengthCB = nullptr;
0192     QCheckBox *rsaSubCB = nullptr;
0193     QComboBox *rsaKeyStrengthSubCB = nullptr;
0194     QRadioButton *dsaRB = nullptr;
0195     QComboBox *dsaKeyStrengthCB = nullptr;
0196     QCheckBox *elgCB = nullptr;
0197     QComboBox *elgKeyStrengthCB = nullptr;
0198     QRadioButton *ecdsaRB = nullptr;
0199     QComboBox *ecdsaKeyCurvesCB = nullptr;
0200     QCheckBox *ecdhCB = nullptr;
0201     QComboBox *ecdhKeyCurvesCB = nullptr;
0202     QCheckBox *certificationCB = nullptr;
0203     QCheckBox *signingCB = nullptr;
0204     QCheckBox *encryptionCB = nullptr;
0205     QCheckBox *authenticationCB = nullptr;
0206     QCheckBox *expiryCB = nullptr;
0207     KDateComboBox *expiryDE = nullptr;
0208     ScrollArea *personalTab = nullptr;
0209     QGroupBox *uidGB = nullptr;
0210     Kleo::NewCertificateUi::ListWidget *uidLW = nullptr;
0211     QGroupBox *emailGB = nullptr;
0212     Kleo::NewCertificateUi::ListWidget *emailLW = nullptr;
0213     QGroupBox *dnsGB = nullptr;
0214     Kleo::NewCertificateUi::ListWidget *dnsLW = nullptr;
0215     QGroupBox *uriGB = nullptr;
0216     Kleo::NewCertificateUi::ListWidget *uriLW = nullptr;
0217     QDialogButtonBox *buttonBox = nullptr;
0218 
0219     UI(QDialog *parent)
0220     {
0221         parent->setWindowTitle(i18nc("@title:window", "Advanced Settings"));
0222 
0223         auto mainLayout = new QVBoxLayout{parent};
0224 
0225         tabWidget = new QTabWidget{parent};
0226 
0227         {
0228             auto technicalTab = new ScrollArea{tabWidget};
0229             technicalTab->setFocusPolicy(Qt::NoFocus);
0230             technicalTab->setFrameStyle(QFrame::NoFrame);
0231             technicalTab->setBackgroundRole(parent->backgroundRole());
0232             technicalTab->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0233             technicalTab->setSizeAdjustPolicy(QScrollArea::AdjustToContents);
0234             auto tabLayout = qobject_cast<QVBoxLayout *>(technicalTab->widget()->layout());
0235 
0236             {
0237                 auto groupBox = new QGroupBox{i18nc("@title:group", "Key Material"), technicalTab};
0238                 auto groupBoxGrid = new QGridLayout{groupBox};
0239 
0240                 int row = 0;
0241                 rsaRB = new QRadioButton{i18nc("@option:radio", "RSA"), groupBox};
0242                 rsaRB->setChecked(false);
0243                 groupBoxGrid->addWidget(rsaRB, row, 0, 1, 2);
0244 
0245                 rsaKeyStrengthCB = new QComboBox{groupBox};
0246                 rsaKeyStrengthCB->setEnabled(false);
0247                 groupBoxGrid->addWidget(rsaKeyStrengthCB, row, 2, 1, 1);
0248 
0249                 row++;
0250                 auto subKeyIndentation = new QSpacerItem(13, 13, QSizePolicy::Fixed, QSizePolicy::Minimum);
0251                 groupBoxGrid->addItem(subKeyIndentation, row, 0, 1, 1);
0252 
0253                 rsaSubCB = new QCheckBox{i18nc("@option:check", "+ RSA"), groupBox};
0254                 rsaSubCB->setEnabled(true);
0255                 groupBoxGrid->addWidget(rsaSubCB, row, 1, 1, 1);
0256 
0257                 rsaKeyStrengthSubCB = new QComboBox{groupBox};
0258                 rsaKeyStrengthSubCB->setEnabled(false);
0259                 groupBoxGrid->addWidget(rsaKeyStrengthSubCB, row, 2, 1, 1);
0260 
0261                 row++;
0262                 dsaRB = new QRadioButton{i18nc("@option:radio", "DSA"), groupBox};
0263                 groupBoxGrid->addWidget(dsaRB, row, 0, 1, 2);
0264 
0265                 dsaKeyStrengthCB = new QComboBox{groupBox};
0266                 dsaKeyStrengthCB->setEnabled(false);
0267                 groupBoxGrid->addWidget(dsaKeyStrengthCB, row, 2, 1, 1);
0268 
0269                 row++;
0270                 elgCB = new QCheckBox{i18nc("@option:check", "+ Elgamal"), groupBox};
0271                 elgCB->setToolTip(i18nc("@info:tooltip", "This subkey is required for encryption."));
0272                 elgCB->setEnabled(true);
0273                 groupBoxGrid->addWidget(elgCB, row, 1, 1, 1);
0274 
0275                 elgKeyStrengthCB = new QComboBox{groupBox};
0276                 elgKeyStrengthCB->setEnabled(false);
0277                 groupBoxGrid->addWidget(elgKeyStrengthCB, row, 2, 1, 1);
0278 
0279                 row++;
0280                 ecdsaRB = new QRadioButton{i18nc("@option:radio", "ECDSA"), groupBox};
0281                 groupBoxGrid->addWidget(ecdsaRB, row, 0, 1, 2);
0282 
0283                 ecdsaKeyCurvesCB = new QComboBox{groupBox};
0284                 ecdsaKeyCurvesCB->setEnabled(false);
0285                 groupBoxGrid->addWidget(ecdsaKeyCurvesCB, row, 2, 1, 1);
0286 
0287                 row++;
0288                 ecdhCB = new QCheckBox{i18nc("@option:check", "+ ECDH"), groupBox};
0289                 ecdhCB->setToolTip(i18nc("@info:tooltip", "This subkey is required for encryption."));
0290                 ecdhCB->setEnabled(true);
0291                 groupBoxGrid->addWidget(ecdhCB, row, 1, 1, 1);
0292 
0293                 ecdhKeyCurvesCB = new QComboBox{groupBox};
0294                 ecdhKeyCurvesCB->setEnabled(false);
0295                 groupBoxGrid->addWidget(ecdhKeyCurvesCB, row, 2, 1, 1);
0296 
0297                 groupBoxGrid->setColumnStretch(3, 1);
0298 
0299                 tabLayout->addWidget(groupBox);
0300             }
0301             {
0302                 auto groupBox = new QGroupBox{i18nc("@title:group", "Certificate Usage"), technicalTab};
0303                 auto groupBoxGrid = new QGridLayout{groupBox};
0304 
0305                 int row = 0;
0306                 signingCB = new QCheckBox{i18nc("@option:check", "Signing"), groupBox};
0307                 signingCB->setChecked(true);
0308                 groupBoxGrid->addWidget(signingCB, row, 0, 1, 1);
0309 
0310                 certificationCB = new QCheckBox{i18nc("@option:check", "Certification"), groupBox};
0311                 groupBoxGrid->addWidget(certificationCB, row, 1, 1, 1);
0312 
0313                 row++;
0314                 encryptionCB = new QCheckBox{i18nc("@option:check", "Encryption"), groupBox};
0315                 encryptionCB->setChecked(true);
0316                 groupBoxGrid->addWidget(encryptionCB, row, 0, 1, 1);
0317 
0318                 authenticationCB = new QCheckBox{i18nc("@option:check", "Authentication"), groupBox};
0319                 groupBoxGrid->addWidget(authenticationCB, row, 1, 1, 1);
0320 
0321                 row++;
0322                 {
0323                     auto hbox = new QHBoxLayout;
0324 
0325                     expiryCB = new QCheckBox{i18nc("@option:check", "Valid until:"), groupBox};
0326                     hbox->addWidget(expiryCB);
0327 
0328                     expiryDE = new KDateComboBox(groupBox);
0329                     hbox->addWidget(expiryDE, 1);
0330 
0331                     groupBoxGrid->addLayout(hbox, row, 0, 1, 2);
0332                 }
0333 
0334                 tabLayout->addWidget(groupBox);
0335             }
0336 
0337             tabLayout->addStretch(1);
0338 
0339             tabWidget->addTab(technicalTab, i18nc("@title:tab", "Technical Details"));
0340         }
0341 
0342         {
0343             personalTab = new ScrollArea{tabWidget};
0344             personalTab->setFocusPolicy(Qt::NoFocus);
0345             personalTab->setFrameStyle(QFrame::NoFrame);
0346             personalTab->setBackgroundRole(parent->backgroundRole());
0347             personalTab->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0348             personalTab->setSizeAdjustPolicy(QScrollArea::AdjustToContents);
0349             auto scrollAreaLayout = qobject_cast<QVBoxLayout *>(personalTab->widget()->layout());
0350 
0351             auto tabGrid = new QGridLayout;
0352 
0353             uidGB = new QGroupBox{i18nc("@title:group", "Additional User IDs"), personalTab};
0354             {
0355                 auto layout = new QVBoxLayout{uidGB};
0356                 uidLW = new Kleo::NewCertificateUi::ListWidget{uidGB};
0357                 layout->addWidget(uidLW);
0358             }
0359             tabGrid->addWidget(uidGB, 0, 0, 1, 2);
0360 
0361             emailGB = new QGroupBox{i18nc("@title:group", "EMail Addresses"), personalTab};
0362             {
0363                 auto layout = new QVBoxLayout{emailGB};
0364                 emailLW = new Kleo::NewCertificateUi::ListWidget{emailGB};
0365                 layout->addWidget(emailLW);
0366             }
0367             tabGrid->addWidget(emailGB, 2, 0, 2, 1);
0368 
0369             dnsGB = new QGroupBox{i18nc("@title:group", "DNS Names"), personalTab};
0370             {
0371                 auto layout = new QVBoxLayout{dnsGB};
0372                 dnsLW = new Kleo::NewCertificateUi::ListWidget{dnsGB};
0373                 layout->addWidget(dnsLW);
0374             }
0375             tabGrid->addWidget(dnsGB, 2, 1, 1, 1);
0376 
0377             uriGB = new QGroupBox{i18nc("@title:group", "URIs"), personalTab};
0378             {
0379                 auto layout = new QVBoxLayout{uriGB};
0380                 uriLW = new Kleo::NewCertificateUi::ListWidget{uriGB};
0381                 layout->addWidget(uriLW);
0382             }
0383             tabGrid->addWidget(uriGB, 3, 1, 1, 1);
0384 
0385             scrollAreaLayout->addLayout(tabGrid);
0386 
0387             tabWidget->addTab(personalTab, i18nc("@title:tab", "Personal Details"));
0388         }
0389 
0390         mainLayout->addWidget(tabWidget);
0391 
0392         buttonBox = new QDialogButtonBox{parent};
0393         buttonBox->setStandardButtons(QDialogButtonBox::Cancel | QDialogButtonBox::Ok);
0394 
0395         mainLayout->addWidget(buttonBox);
0396     }
0397 };
0398 
0399 AdvancedSettingsDialog::AdvancedSettingsDialog(QWidget *parent)
0400     : QDialog{parent}
0401     , ui{new UI{this}}
0402     , mECCSupported{engineIsVersion(2, 1, 0)}
0403     , mEdDSASupported{engineIsVersion(2, 1, 15)}
0404 {
0405     qRegisterMetaType<Subkey::PubkeyAlgo>("Subkey::PubkeyAlgo");
0406 
0407     Kleo::setUpExpirationDateComboBox(ui->expiryDE);
0408     if (unlimitedValidityIsAllowed()) {
0409         ui->expiryDE->setEnabled(ui->expiryCB->isChecked());
0410     } else {
0411         ui->expiryCB->setEnabled(false);
0412         ui->expiryCB->setChecked(true);
0413     }
0414     ui->emailLW->setDefaultValue(i18n("new email"));
0415     ui->dnsLW->setDefaultValue(i18n("new dns name"));
0416     ui->uriLW->setDefaultValue(i18n("new uri"));
0417 
0418     fillKeySizeComboBoxen();
0419 
0420     connect(ui->rsaRB, &QAbstractButton::toggled, ui->rsaKeyStrengthCB, &QWidget::setEnabled);
0421     connect(ui->rsaRB, &QAbstractButton::toggled, this, &AdvancedSettingsDialog::slotKeyMaterialSelectionChanged);
0422     connect(ui->rsaSubCB, &QAbstractButton::toggled, ui->rsaKeyStrengthSubCB, &QWidget::setEnabled);
0423     connect(ui->rsaSubCB, &QAbstractButton::toggled, this, &AdvancedSettingsDialog::slotKeyMaterialSelectionChanged);
0424 
0425     connect(ui->dsaRB, &QAbstractButton::toggled, ui->dsaKeyStrengthCB, &QWidget::setEnabled);
0426     connect(ui->dsaRB, &QAbstractButton::toggled, this, &AdvancedSettingsDialog::slotKeyMaterialSelectionChanged);
0427     connect(ui->elgCB, &QAbstractButton::toggled, ui->elgKeyStrengthCB, &QWidget::setEnabled);
0428     connect(ui->elgCB, &QAbstractButton::toggled, this, &AdvancedSettingsDialog::slotKeyMaterialSelectionChanged);
0429 
0430     connect(ui->ecdsaRB, &QAbstractButton::toggled, ui->ecdsaKeyCurvesCB, &QWidget::setEnabled);
0431     connect(ui->ecdsaRB, &QAbstractButton::toggled, this, &AdvancedSettingsDialog::slotKeyMaterialSelectionChanged);
0432     connect(ui->ecdhCB, &QAbstractButton::toggled, ui->ecdhKeyCurvesCB, &QWidget::setEnabled);
0433     connect(ui->ecdhCB, &QAbstractButton::toggled, this, &AdvancedSettingsDialog::slotKeyMaterialSelectionChanged);
0434 
0435     connect(ui->signingCB, &QAbstractButton::toggled, this, &AdvancedSettingsDialog::slotSigningAllowedToggled);
0436     connect(ui->encryptionCB, &QAbstractButton::toggled, this, &AdvancedSettingsDialog::slotEncryptionAllowedToggled);
0437 
0438     connect(ui->expiryCB, &QAbstractButton::toggled, this, [this](bool checked) {
0439         ui->expiryDE->setEnabled(checked);
0440         if (checked && !ui->expiryDE->isValid()) {
0441             setExpiryDate(defaultExpirationDate(ExpirationOnUnlimitedValidity::InternalDefaultExpiration));
0442         }
0443     });
0444 
0445     connect(ui->buttonBox, &QDialogButtonBox::accepted, this, &AdvancedSettingsDialog::accept);
0446     connect(ui->buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
0447 }
0448 
0449 AdvancedSettingsDialog::~AdvancedSettingsDialog() = default;
0450 
0451 bool AdvancedSettingsDialog::unlimitedValidityIsAllowed() const
0452 {
0453     return !Kleo::maximumExpirationDate().isValid();
0454 }
0455 
0456 void AdvancedSettingsDialog::setProtocol(GpgME::Protocol proto)
0457 {
0458     if (protocol == proto) {
0459         return;
0460     }
0461     protocol = proto;
0462     loadDefaults();
0463 }
0464 
0465 void AdvancedSettingsDialog::setAdditionalUserIDs(const QStringList &items)
0466 {
0467     ui->uidLW->setItems(items);
0468 }
0469 
0470 QStringList AdvancedSettingsDialog::additionalUserIDs() const
0471 {
0472     return ui->uidLW->items();
0473 }
0474 
0475 void AdvancedSettingsDialog::setAdditionalEMailAddresses(const QStringList &items)
0476 {
0477     ui->emailLW->setItems(items);
0478 }
0479 
0480 QStringList AdvancedSettingsDialog::additionalEMailAddresses() const
0481 {
0482     return ui->emailLW->items();
0483 }
0484 
0485 void AdvancedSettingsDialog::setDnsNames(const QStringList &items)
0486 {
0487     ui->dnsLW->setItems(items);
0488 }
0489 
0490 QStringList AdvancedSettingsDialog::dnsNames() const
0491 {
0492     return ui->dnsLW->items();
0493 }
0494 
0495 void AdvancedSettingsDialog::setUris(const QStringList &items)
0496 {
0497     ui->uriLW->setItems(items);
0498 }
0499 
0500 QStringList AdvancedSettingsDialog::uris() const
0501 {
0502     return ui->uriLW->items();
0503 }
0504 
0505 void AdvancedSettingsDialog::setKeyStrength(unsigned int strength)
0506 {
0507     set_keysize(ui->rsaKeyStrengthCB, strength);
0508     set_keysize(ui->dsaKeyStrengthCB, strength);
0509 }
0510 
0511 unsigned int AdvancedSettingsDialog::keyStrength() const
0512 {
0513     return ui->dsaRB->isChecked() ? get_keysize(ui->dsaKeyStrengthCB) : ui->rsaRB->isChecked() ? get_keysize(ui->rsaKeyStrengthCB) : 0;
0514 }
0515 
0516 void AdvancedSettingsDialog::setKeyType(Subkey::PubkeyAlgo algo)
0517 {
0518     QRadioButton *const rb = is_rsa(algo) ? ui->rsaRB : is_dsa(algo) ? ui->dsaRB : is_ecdsa(algo) || is_eddsa(algo) ? ui->ecdsaRB : nullptr;
0519     if (rb) {
0520         rb->setChecked(true);
0521     }
0522 }
0523 
0524 Subkey::PubkeyAlgo AdvancedSettingsDialog::keyType() const
0525 {
0526     return ui->dsaRB->isChecked()  ? Subkey::AlgoDSA
0527         : ui->rsaRB->isChecked()   ? Subkey::AlgoRSA
0528         : ui->ecdsaRB->isChecked() ? ui->ecdsaKeyCurvesCB->currentText() == QLatin1StringView("ed25519") ? Subkey::AlgoEDDSA : Subkey::AlgoECDSA
0529                                    : Subkey::AlgoUnknown;
0530 }
0531 
0532 void AdvancedSettingsDialog::setKeyCurve(const QString &curve)
0533 {
0534     set_curve(ui->ecdsaKeyCurvesCB, curve);
0535 }
0536 
0537 QString AdvancedSettingsDialog::keyCurve() const
0538 {
0539     return get_curve(ui->ecdsaKeyCurvesCB);
0540 }
0541 
0542 void AdvancedSettingsDialog::setSubkeyType(Subkey::PubkeyAlgo algo)
0543 {
0544     ui->elgCB->setChecked(is_elg(algo));
0545     ui->rsaSubCB->setChecked(is_rsa(algo));
0546     ui->ecdhCB->setChecked(is_ecdh(algo));
0547 }
0548 
0549 Subkey::PubkeyAlgo AdvancedSettingsDialog::subkeyType() const
0550 {
0551     if (ui->elgCB->isChecked()) {
0552         return Subkey::AlgoELG_E;
0553     } else if (ui->rsaSubCB->isChecked()) {
0554         return Subkey::AlgoRSA;
0555     } else if (ui->ecdhCB->isChecked()) {
0556         return Subkey::AlgoECDH;
0557     }
0558     return Subkey::AlgoUnknown;
0559 }
0560 
0561 void AdvancedSettingsDialog::setSubkeyCurve(const QString &curve)
0562 {
0563     set_curve(ui->ecdhKeyCurvesCB, curve);
0564 }
0565 
0566 QString AdvancedSettingsDialog::subkeyCurve() const
0567 {
0568     return get_curve(ui->ecdhKeyCurvesCB);
0569 }
0570 
0571 void AdvancedSettingsDialog::setSubkeyStrength(unsigned int strength)
0572 {
0573     if (subkeyType() == Subkey::AlgoRSA) {
0574         set_keysize(ui->rsaKeyStrengthSubCB, strength);
0575     } else {
0576         set_keysize(ui->elgKeyStrengthCB, strength);
0577     }
0578 }
0579 
0580 unsigned int AdvancedSettingsDialog::subkeyStrength() const
0581 {
0582     if (subkeyType() == Subkey::AlgoRSA) {
0583         return get_keysize(ui->rsaKeyStrengthSubCB);
0584     }
0585     return get_keysize(ui->elgKeyStrengthCB);
0586 }
0587 
0588 void AdvancedSettingsDialog::setSigningAllowed(bool on)
0589 {
0590     ui->signingCB->setChecked(on);
0591 }
0592 
0593 bool AdvancedSettingsDialog::signingAllowed() const
0594 {
0595     return ui->signingCB->isChecked();
0596 }
0597 
0598 void AdvancedSettingsDialog::setEncryptionAllowed(bool on)
0599 {
0600     ui->encryptionCB->setChecked(on);
0601 }
0602 
0603 bool AdvancedSettingsDialog::encryptionAllowed() const
0604 {
0605     return ui->encryptionCB->isChecked();
0606 }
0607 
0608 void AdvancedSettingsDialog::setCertificationAllowed(bool on)
0609 {
0610     ui->certificationCB->setChecked(on);
0611 }
0612 
0613 bool AdvancedSettingsDialog::certificationAllowed() const
0614 {
0615     return ui->certificationCB->isChecked();
0616 }
0617 
0618 void AdvancedSettingsDialog::setAuthenticationAllowed(bool on)
0619 {
0620     ui->authenticationCB->setChecked(on);
0621 }
0622 
0623 bool AdvancedSettingsDialog::authenticationAllowed() const
0624 {
0625     return ui->authenticationCB->isChecked();
0626 }
0627 
0628 QDate AdvancedSettingsDialog::forceDateIntoAllowedRange(QDate date) const
0629 {
0630     const auto minDate = ui->expiryDE->minimumDate();
0631     if (minDate.isValid() && date < minDate) {
0632         date = minDate;
0633     }
0634     const auto maxDate = ui->expiryDE->maximumDate();
0635     if (maxDate.isValid() && date > maxDate) {
0636         date = maxDate;
0637     }
0638     return date;
0639 }
0640 
0641 void AdvancedSettingsDialog::setExpiryDate(QDate date)
0642 {
0643     if (date.isValid()) {
0644         ui->expiryDE->setDate(forceDateIntoAllowedRange(date));
0645     } else {
0646         // check if unlimited validity is allowed
0647         if (unlimitedValidityIsAllowed()) {
0648             ui->expiryDE->setDate(date);
0649         }
0650     }
0651     if (ui->expiryCB->isEnabled()) {
0652         ui->expiryCB->setChecked(ui->expiryDE->isValid());
0653     }
0654 }
0655 
0656 QDate AdvancedSettingsDialog::expiryDate() const
0657 {
0658     return ui->expiryCB->isChecked() ? ui->expiryDE->date() : QDate{};
0659 }
0660 
0661 void AdvancedSettingsDialog::slotKeyMaterialSelectionChanged()
0662 {
0663     const unsigned int algo = keyType();
0664     const unsigned int sk_algo = subkeyType();
0665 
0666     if (protocol == OpenPGP) {
0667         // first update the enabled state, but only if key type is not forced
0668         if (!keyTypeImmutable) {
0669             ui->elgCB->setEnabled(is_dsa(algo));
0670             ui->rsaSubCB->setEnabled(is_rsa(algo));
0671             ui->ecdhCB->setEnabled(is_ecdsa(algo) || is_eddsa(algo));
0672             if (is_rsa(algo)) {
0673                 ui->encryptionCB->setEnabled(true);
0674                 ui->signingCB->setEnabled(true);
0675                 ui->authenticationCB->setEnabled(true);
0676                 if (is_rsa(sk_algo)) {
0677                     ui->encryptionCB->setEnabled(false);
0678                 } else {
0679                     ui->encryptionCB->setEnabled(true);
0680                 }
0681             } else if (is_dsa(algo)) {
0682                 ui->encryptionCB->setEnabled(false);
0683             } else if (is_ecdsa(algo) || is_eddsa(algo)) {
0684                 ui->signingCB->setEnabled(true);
0685                 ui->authenticationCB->setEnabled(true);
0686                 ui->encryptionCB->setEnabled(false);
0687             }
0688         }
0689         // then update the checked state
0690         if (sender() == ui->dsaRB || sender() == ui->rsaRB || sender() == ui->ecdsaRB) {
0691             ui->elgCB->setChecked(is_dsa(algo));
0692             ui->ecdhCB->setChecked(is_ecdsa(algo) || is_eddsa(algo));
0693             ui->rsaSubCB->setChecked(is_rsa(algo));
0694         }
0695         if (is_rsa(algo)) {
0696             ui->encryptionCB->setChecked(true);
0697             ui->signingCB->setChecked(true);
0698             if (is_rsa(sk_algo)) {
0699                 ui->encryptionCB->setChecked(true);
0700             }
0701         } else if (is_dsa(algo)) {
0702             if (is_elg(sk_algo)) {
0703                 ui->encryptionCB->setChecked(true);
0704             } else {
0705                 ui->encryptionCB->setChecked(false);
0706             }
0707         } else if (is_ecdsa(algo) || is_eddsa(algo)) {
0708             ui->signingCB->setChecked(true);
0709             ui->encryptionCB->setChecked(is_ecdh(sk_algo));
0710         }
0711     } else {
0712         // assert( is_rsa( keyType() ) ); // it can happen through misconfiguration by the admin that no key type is selectable at all
0713     }
0714 }
0715 
0716 void AdvancedSettingsDialog::slotSigningAllowedToggled(bool on)
0717 {
0718     if (!on && protocol == CMS && !encryptionAllowed()) {
0719         setEncryptionAllowed(true);
0720     }
0721 }
0722 
0723 void AdvancedSettingsDialog::slotEncryptionAllowedToggled(bool on)
0724 {
0725     if (!on && protocol == CMS && !signingAllowed()) {
0726         setSigningAllowed(true);
0727     }
0728 }
0729 
0730 static void fill_combobox(QComboBox &cb, const QList<int> &sizes, const QStringList &labels)
0731 {
0732     cb.clear();
0733     for (int i = 0, end = sizes.size(); i != end; ++i) {
0734         const int size = std::abs(sizes[i]);
0735         /* As we respect the defaults configurable in GnuPG, and we also have configurable
0736          * defaults in Kleopatra its difficult to print out "default" here. To avoid confusion
0737          * about that its better not to show any default indication.  */
0738         cb.addItem(i < labels.size() && !labels[i].trimmed().isEmpty()
0739                        ? i18ncp("%2: some admin-supplied text, %1: key size in bits", "%2 (1 bit)", "%2 (%1 bits)", size, labels[i].trimmed())
0740                        : i18ncp("%1: key size in bits", "1 bit", "%1 bits", size),
0741                    size);
0742         if (sizes[i] < 0) {
0743             cb.setCurrentIndex(cb.count() - 1);
0744         }
0745     }
0746 }
0747 
0748 void AdvancedSettingsDialog::fillKeySizeComboBoxen()
0749 {
0750     const KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("CertificateCreationWizard"));
0751 
0752     QList<int> rsaKeySizes = config.readEntry(RSA_KEYSIZES_ENTRY, QList<int>() << 2048 << -3072 << 4096);
0753     if (DeVSCompliance::isActive()) {
0754         rsaKeySizes = config.readEntry(RSA_KEYSIZES_ENTRY, QList<int>() << -3072 << 4096);
0755     }
0756     const QList<int> dsaKeySizes = config.readEntry(DSA_KEYSIZES_ENTRY, QList<int>() << -2048);
0757     const QList<int> elgKeySizes = config.readEntry(ELG_KEYSIZES_ENTRY, QList<int>() << -2048 << 3072 << 4096);
0758 
0759     const QStringList rsaKeySizeLabels = config.readEntry(RSA_KEYSIZE_LABELS_ENTRY, QStringList());
0760     const QStringList dsaKeySizeLabels = config.readEntry(DSA_KEYSIZE_LABELS_ENTRY, QStringList());
0761     const QStringList elgKeySizeLabels = config.readEntry(ELG_KEYSIZE_LABELS_ENTRY, QStringList());
0762 
0763     fill_combobox(*ui->rsaKeyStrengthCB, rsaKeySizes, rsaKeySizeLabels);
0764     fill_combobox(*ui->rsaKeyStrengthSubCB, rsaKeySizes, rsaKeySizeLabels);
0765     fill_combobox(*ui->dsaKeyStrengthCB, dsaKeySizes, dsaKeySizeLabels);
0766     fill_combobox(*ui->elgKeyStrengthCB, elgKeySizes, elgKeySizeLabels);
0767     if (mEdDSASupported) {
0768         // If supported we recommend cv25519
0769         ui->ecdsaKeyCurvesCB->addItem(QStringLiteral("ed25519"));
0770         ui->ecdhKeyCurvesCB->addItem(QStringLiteral("cv25519"));
0771     }
0772     ui->ecdhKeyCurvesCB->addItems(curveNames);
0773     ui->ecdsaKeyCurvesCB->addItems(curveNames);
0774 }
0775 
0776 // Try to load the default key type from GnuPG
0777 void AdvancedSettingsDialog::loadDefaultGnuPGKeyType()
0778 {
0779     const auto conf = QGpgME::cryptoConfig();
0780     if (!conf) {
0781         qCWarning(KLEOPATRA_LOG) << "Failed to obtain cryptoConfig.";
0782         return;
0783     }
0784     const auto entry = getCryptoConfigEntry(conf, protocol == CMS ? "gpgsm" : "gpg", "default_pubkey_algo");
0785     if (!entry) {
0786         qCDebug(KLEOPATRA_LOG) << "GnuPG does not have default key type. Fallback to RSA";
0787         setKeyType(Subkey::AlgoRSA);
0788         setSubkeyType(Subkey::AlgoRSA);
0789         return;
0790     }
0791 
0792     qCDebug(KLEOPATRA_LOG) << "Have default key type: " << entry->stringValue();
0793 
0794     // Format is <primarytype>[/usage]+<subkeytype>[/usage]
0795     const auto split = entry->stringValue().split(QLatin1Char('+'));
0796     int size = 0;
0797     Subkey::PubkeyAlgo algo = Subkey::AlgoUnknown;
0798     QString curve;
0799 
0800     parseAlgoString(split[0], &size, &algo, curve);
0801     if (algo == Subkey::AlgoUnknown) {
0802         setSubkeyType(Subkey::AlgoRSA);
0803         return;
0804     }
0805 
0806     setKeyType(algo);
0807 
0808     if (is_rsa(algo) || is_elg(algo) || is_dsa(algo)) {
0809         setKeyStrength(size);
0810     } else {
0811         setKeyCurve(curve);
0812     }
0813 
0814     {
0815         auto algoString = (split.size() == 2) ? split[1] : split[0];
0816         // If it has no usage we assume encrypt subkey
0817         if (!algoString.contains(QLatin1Char('/'))) {
0818             algoString += QStringLiteral("/enc");
0819         }
0820 
0821         parseAlgoString(algoString, &size, &algo, curve);
0822 
0823         if (algo == Subkey::AlgoUnknown) {
0824             setSubkeyType(Subkey::AlgoRSA);
0825             return;
0826         }
0827 
0828         setSubkeyType(algo);
0829 
0830         if (is_rsa(algo) || is_elg(algo)) {
0831             setSubkeyStrength(size);
0832         } else {
0833             setSubkeyCurve(curve);
0834         }
0835     }
0836 }
0837 
0838 void AdvancedSettingsDialog::loadDefaultKeyType()
0839 {
0840     if (protocol != CMS && protocol != OpenPGP) {
0841         return;
0842     }
0843 
0844     const KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("CertificateCreationWizard"));
0845 
0846     const QString entry = protocol == CMS ? QLatin1StringView(CMS_KEY_TYPE_ENTRY) : QLatin1String(PGP_KEY_TYPE_ENTRY);
0847     const QString keyType = config.readEntry(entry).trimmed().toUpper();
0848 
0849     if (protocol == OpenPGP && keyType == QLatin1StringView("DSA")) {
0850         setKeyType(Subkey::AlgoDSA);
0851         setSubkeyType(Subkey::AlgoUnknown);
0852     } else if (protocol == OpenPGP && keyType == QLatin1StringView("DSA+ELG")) {
0853         setKeyType(Subkey::AlgoDSA);
0854         setSubkeyType(Subkey::AlgoELG_E);
0855     } else if (keyType.isEmpty() && engineIsVersion(2, 1, 17)) {
0856         loadDefaultGnuPGKeyType();
0857     } else {
0858         if (!keyType.isEmpty() && keyType != QLatin1StringView("RSA"))
0859             qCWarning(KLEOPATRA_LOG) << "invalid value \"" << qPrintable(keyType) << "\" for entry \"[CertificateCreationWizard]" << qPrintable(entry) << "\"";
0860         setKeyType(Subkey::AlgoRSA);
0861         setSubkeyType(Subkey::AlgoRSA);
0862     }
0863 
0864     keyTypeImmutable = config.isEntryImmutable(entry);
0865 }
0866 
0867 void AdvancedSettingsDialog::loadDefaultExpiration()
0868 {
0869     if (protocol != OpenPGP) {
0870         return;
0871     }
0872 
0873     if (unlimitedValidityIsAllowed()) {
0874         setExpiryDate(defaultExpirationDate(ExpirationOnUnlimitedValidity::NoExpiration));
0875     } else {
0876         setExpiryDate(defaultExpirationDate(ExpirationOnUnlimitedValidity::InternalDefaultExpiration));
0877     }
0878 }
0879 
0880 void AdvancedSettingsDialog::loadDefaults()
0881 {
0882     loadDefaultKeyType();
0883     loadDefaultExpiration();
0884 
0885     updateWidgetVisibility();
0886 }
0887 
0888 void AdvancedSettingsDialog::updateWidgetVisibility()
0889 {
0890     // Personal Details Page
0891     if (protocol == OpenPGP) { // ### hide until multi-uid is implemented
0892         if (ui->tabWidget->indexOf(ui->personalTab) != -1) {
0893             ui->tabWidget->removeTab(ui->tabWidget->indexOf(ui->personalTab));
0894         }
0895     } else {
0896         if (ui->tabWidget->indexOf(ui->personalTab) == -1) {
0897             ui->tabWidget->addTab(ui->personalTab, i18nc("@title:tab", "Personal Details"));
0898         }
0899     }
0900     ui->uidGB->setVisible(protocol == OpenPGP);
0901     ui->uidGB->setEnabled(false);
0902     ui->uidGB->setToolTip(i18nc("@info:tooltip", "Adding more than one user ID is not yet implemented."));
0903     ui->emailGB->setVisible(protocol == CMS);
0904     ui->dnsGB->setVisible(protocol == CMS);
0905     ui->uriGB->setVisible(protocol == CMS);
0906 
0907     // Technical Details Page
0908     ui->ecdhCB->setVisible(mECCSupported);
0909     ui->ecdhKeyCurvesCB->setVisible(mECCSupported);
0910     ui->ecdsaKeyCurvesCB->setVisible(mECCSupported);
0911     ui->ecdsaRB->setVisible(mECCSupported);
0912     if (mEdDSASupported) {
0913         // We use the same radio button for EdDSA as we use for
0914         // ECDSA GnuPG does the same and this is really super technical
0915         // land.
0916         ui->ecdsaRB->setText(QStringLiteral("ECDSA/EdDSA"));
0917     }
0918 
0919     const bool deVsHack = DeVSCompliance::isActive();
0920 
0921     if (deVsHack) {
0922         // GnuPG Provides no API to query which keys are compliant for
0923         // a mode. If we request a different one it will error out so
0924         // we have to remove the options.
0925         //
0926         // Does anyone want to use NIST anyway?
0927         int i;
0928         while ((i = ui->ecdsaKeyCurvesCB->findText(QStringLiteral("NIST"), Qt::MatchStartsWith)) != -1
0929                || (i = ui->ecdsaKeyCurvesCB->findText(QStringLiteral("25519"), Qt::MatchEndsWith)) != -1) {
0930             ui->ecdsaKeyCurvesCB->removeItem(i);
0931         }
0932         while ((i = ui->ecdhKeyCurvesCB->findText(QStringLiteral("NIST"), Qt::MatchStartsWith)) != -1
0933                || (i = ui->ecdhKeyCurvesCB->findText(QStringLiteral("25519"), Qt::MatchEndsWith)) != -1) {
0934             ui->ecdhKeyCurvesCB->removeItem(i);
0935         }
0936     }
0937     ui->certificationCB->setVisible(protocol == OpenPGP); // gpgsm limitation?
0938     ui->authenticationCB->setVisible(protocol == OpenPGP);
0939 
0940     if (keyTypeImmutable) {
0941         ui->rsaRB->setEnabled(false);
0942         ui->rsaSubCB->setEnabled(false);
0943         ui->dsaRB->setEnabled(false);
0944         ui->elgCB->setEnabled(false);
0945         ui->ecdsaRB->setEnabled(false);
0946         ui->ecdhCB->setEnabled(false);
0947 
0948         // force usage if key type is forced
0949         ui->certificationCB->setEnabled(false);
0950         ui->signingCB->setEnabled(false);
0951         ui->encryptionCB->setEnabled(false);
0952         ui->authenticationCB->setEnabled(false);
0953     } else {
0954         ui->rsaRB->setEnabled(true);
0955         ui->rsaSubCB->setEnabled(protocol == OpenPGP);
0956         ui->dsaRB->setEnabled(protocol == OpenPGP && !deVsHack);
0957         ui->elgCB->setEnabled(protocol == OpenPGP && !deVsHack);
0958         ui->ecdsaRB->setEnabled(protocol == OpenPGP);
0959         ui->ecdhCB->setEnabled(protocol == OpenPGP);
0960 
0961         if (protocol == OpenPGP) {
0962             // OpenPGP keys must have certify capability
0963             ui->certificationCB->setEnabled(false);
0964         }
0965         if (protocol == CMS) {
0966             ui->encryptionCB->setEnabled(true);
0967             ui->rsaKeyStrengthSubCB->setEnabled(false);
0968         }
0969     }
0970     if (protocol == OpenPGP) {
0971         // OpenPGP keys must have certify capability
0972         ui->certificationCB->setChecked(true);
0973     }
0974     if (protocol == CMS) {
0975         ui->rsaSubCB->setChecked(false);
0976     }
0977 
0978     ui->expiryDE->setVisible(protocol == OpenPGP);
0979     ui->expiryCB->setVisible(protocol == OpenPGP);
0980 
0981     slotKeyMaterialSelectionChanged();
0982 }
0983 
0984 void AdvancedSettingsDialog::setInitialFocus()
0985 {
0986     // first try the key type radio buttons
0987     if (focusFirstCheckedButton({ui->rsaRB, ui->dsaRB, ui->ecdsaRB})) {
0988         return;
0989     }
0990     // then try the usage check boxes and the expiration check box
0991     if (focusFirstEnabledButton({ui->signingCB, ui->certificationCB, ui->encryptionCB, ui->authenticationCB, ui->expiryCB})) {
0992         return;
0993     }
0994     // finally, focus the OK button
0995     ui->buttonBox->button(QDialogButtonBox::Ok)->setFocus();
0996 }
0997 
0998 void AdvancedSettingsDialog::accept()
0999 {
1000     if (!Kleo::isValidExpirationDate(expiryDate())) {
1001         KMessageBox::error(this, i18nc("@info", "Error: %1", Kleo::validityPeriodHint()));
1002         return;
1003     }
1004 
1005     QDialog::accept();
1006 }
1007 
1008 void AdvancedSettingsDialog::showEvent(QShowEvent *event)
1009 {
1010     if (isFirstShowEvent) {
1011         setInitialFocus();
1012         isFirstShowEvent = false;
1013     }
1014     QDialog::showEvent(event);
1015 }
1016 
1017 #include "moc_advancedsettingsdialog_p.cpp"