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"