newcertificatewizard.cpp 63.7 KB
Newer Older
Marc Mutz's avatar
Marc Mutz committed
1 2 3 4
/* -*- mode: c++; c-basic-offset:4 -*-
    newcertificatewizard/newcertificatewizard.cpp

    This file is part of Kleopatra, the KDE keymanager
5
    SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
Marc Mutz's avatar
Marc Mutz committed
6

7 8
    SPDX-FileCopyrightText: 2016, 2017 Bundesamt für Sicherheit in der Informationstechnik
    SPDX-FileContributor: Intevation GmbH
Marc Mutz's avatar
Marc Mutz committed
9

10
    SPDX-License-Identifier: GPL-2.0-or-later
Marc Mutz's avatar
Marc Mutz committed
11 12 13 14
*/

#include <config-kleopatra.h>

15 16 17 18
#include "newcertificatewizard.h"

#include "ui_chooseprotocolpage.h"
#include "ui_enterdetailspage.h"
19
#include "ui_keycreationpage.h"
20 21
#include "ui_resultpage.h"

22 23
#include "ui_advancedsettingsdialog.h"

24 25 26
#include "commands/exportsecretkeycommand.h"
#include "commands/exportopenpgpcertstoservercommand.h"
#include "commands/exportcertificatecommand.h"
27

28 29
#include "kleopatraapplication.h"

30 31 32
#include "utils/validation.h"
#include "utils/filedialog.h"
#include "utils/keyparameters.h"
33

34
#include <Libkleo/GnuPG>
Laurent Montel's avatar
Laurent Montel committed
35 36
#include <Libkleo/Stl_Util>
#include <Libkleo/Dn>
Laurent Montel's avatar
Laurent Montel committed
37
#include <Libkleo/OidMap>
38 39
#include <Libkleo/KeyCache>
#include <Libkleo/Formatting>
40

41 42
#include <QGpgME/KeyGenerationJob>
#include <QGpgME/Protocol>
43
#include <QGpgME/CryptoConfig>
44

45
#include <gpgme++/global.h>
46
#include <gpgme++/keygenerationresult.h>
47
#include <gpgme++/context.h>
48
#include <gpgme++/interfaces/passphraseprovider.h>
49

50
#include <KConfigGroup>
Laurent Montel's avatar
Laurent Montel committed
51
#include <KLocalizedString>
Laurent Montel's avatar
Laurent Montel committed
52
#include "kleopatra_debug.h"
Laurent Montel's avatar
Laurent Montel committed
53
#include <QTemporaryDir>
54
#include <KMessageBox>
Laurent Montel's avatar
KIcon--  
Laurent Montel committed
55
#include <QIcon>
56 57 58

#include <QRegExpValidator>
#include <QLineEdit>
59
#include <QMetaProperty>
Marc Mutz's avatar
Marc Mutz committed
60 61
#include <QDir>
#include <QFile>
62
#include <QUrl>
63
#include <QDesktopServices>
64
#include <QUrlQuery>
65

66
#include <algorithm>
67

68
#include <KSharedConfig>
69
#include <KEMailSettings>
70
#include <KEmailAddress>
Laurent Montel's avatar
Laurent Montel committed
71
#include <QLocale>
72

73 74 75 76 77 78
#ifdef Q_OS_WIN
# include <windows.h>
# define SECURITY_WIN32
# include <secext.h> // For GetUserNameEx
#endif

79 80
using namespace Kleo;
using namespace Kleo::NewCertificateUi;
81
using namespace Kleo::Commands;
82 83
using namespace GpgME;

84 85 86
static const char RSA_KEYSIZES_ENTRY[] = "RSAKeySizes";
static const char DSA_KEYSIZES_ENTRY[] = "DSAKeySizes";
static const char ELG_KEYSIZES_ENTRY[] = "ELGKeySizes";
Marc Mutz's avatar
Marc Mutz committed
87

88 89 90
static const char RSA_KEYSIZE_LABELS_ENTRY[] = "RSAKeySizeLabels";
static const char DSA_KEYSIZE_LABELS_ENTRY[] = "DSAKeySizeLabels";
static const char ELG_KEYSIZE_LABELS_ENTRY[] = "ELGKeySizeLabels";
91

92 93
static const char PGP_KEY_TYPE_ENTRY[] = "PGPKeyType";
static const char CMS_KEY_TYPE_ENTRY[] = "CMSKeyType";
94

95 96 97 98 99 100 101 102 103 104 105 106 107 108
// 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") },
};

109 110 111 112 113 114 115 116 117 118
class EmptyPassphraseProvider: public PassphraseProvider
{
public:
    char *getPassphrase(const char * /*useridHint*/, const char * /*description*/,
                        bool /*previousWasBad*/, bool &/*canceled*/) Q_DECL_OVERRIDE
    {
        return gpgrt_strdup ("");
    }
};

Laurent Montel's avatar
Laurent Montel committed
119 120
static void set_tab_order(const QList<QWidget *> &wl)
{
121
    kdtools::for_each_adjacent_pair(wl.begin(), wl.end(), &QWidget::setTabOrder);
122 123
}

124
enum KeyAlgo { RSA, DSA, ELG, ECDSA, ECDH, EDDSA };
125

126
static bool is_algo(Subkey::PubkeyAlgo algo, KeyAlgo what)
Laurent Montel's avatar
Laurent Montel committed
127 128
{
    switch (algo) {
129 130 131 132 133 134 135 136 137 138 139 140 141
        case Subkey::AlgoRSA:
        case Subkey::AlgoRSA_E:
        case Subkey::AlgoRSA_S:
            return what == RSA;
        case Subkey::AlgoELG_E:
        case Subkey::AlgoELG:
            return what == ELG;
        case Subkey::AlgoDSA:
            return what == DSA;
        case Subkey::AlgoECDSA:
            return what == ECDSA;
        case Subkey::AlgoECDH:
            return what == ECDH;
142 143
        case Subkey::AlgoEDDSA:
            return what == EDDSA;
144 145
        default:
            break;
146 147 148 149
    }
    return false;
}

Laurent Montel's avatar
Laurent Montel committed
150 151
static bool is_rsa(unsigned int algo)
{
152
    return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), RSA);
153 154
}

Laurent Montel's avatar
Laurent Montel committed
155 156
static bool is_dsa(unsigned int algo)
{
157
    return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), DSA);
158 159
}

Laurent Montel's avatar
Laurent Montel committed
160 161
static bool is_elg(unsigned int algo)
{
162
    return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), ELG);
163 164
}

165 166
static bool is_ecdsa(unsigned int algo)
{
167
    return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), ECDSA);
168 169
}

170 171 172 173 174
static bool is_eddsa(unsigned int algo)
{
    return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), EDDSA);
}

175 176
static bool is_ecdh(unsigned int algo)
{
177
    return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), ECDH);
178 179
}

Laurent Montel's avatar
Laurent Montel committed
180 181
static void force_set_checked(QAbstractButton *b, bool on)
{
182 183
    // work around Qt bug (tested: 4.1.4, 4.2.3, 4.3.4)
    const bool autoExclusive = b->autoExclusive();
Laurent Montel's avatar
Laurent Montel committed
184 185 186
    b->setAutoExclusive(false);
    b->setChecked(b->isEnabled() && on);
    b->setAutoExclusive(autoExclusive);
187 188
}

Laurent Montel's avatar
Laurent Montel committed
189 190 191
static void set_keysize(QComboBox *cb, unsigned int strength)
{
    if (!cb) {
192
        return;
Laurent Montel's avatar
Laurent Montel committed
193 194 195
    }
    const int idx = cb->findData(static_cast<int>(strength));
    cb->setCurrentIndex(idx);
196 197
}

Laurent Montel's avatar
Laurent Montel committed
198 199 200
static unsigned int get_keysize(const QComboBox *cb)
{
    if (!cb) {
201
        return 0;
Laurent Montel's avatar
Laurent Montel committed
202
    }
203
    const int idx = cb->currentIndex();
Laurent Montel's avatar
Laurent Montel committed
204
    if (idx < 0) {
205
        return 0;
Laurent Montel's avatar
Laurent Montel committed
206 207
    }
    return cb->itemData(idx).toInt();
208 209
}

210 211 212 213 214
static void set_curve(QComboBox *cb, const QString &curve)
{
    if (!cb) {
        return;
    }
215
    const int idx = cb->findText(curve);
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230
    if (idx < 0) {
        // Can't happen as we don't have them configurable.
        qCWarning(KLEOPATRA_LOG) << "curve " << curve << " not allowed";
    }
    cb->setCurrentIndex(idx);
}

static QString get_curve(const QComboBox *cb)
{
    if (!cb) {
        return QString();
    }
    return cb->currentText();
}

231 232 233 234 235 236 237 238 239 240 241
// 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)
{
    const auto split = algoString.split(QLatin1Char('/'));
242
    bool isEncrypt = split.size() == 2 && split[1].contains(QLatin1String("enc"));
243 244 245 246 247 248 249

    // Normalize
    const auto lowered = split[0].toLower().remove(QLatin1Char('-'));
    if (!algo || !size) {
        return;
    }
    *algo = Subkey::AlgoUnknown;
Laurent Montel's avatar
Laurent Montel committed
250
    if (lowered.startsWith(QLatin1String("rsa"))) {
251
        *algo = Subkey::AlgoRSA;
Laurent Montel's avatar
Laurent Montel committed
252
    } else if (lowered.startsWith(QLatin1String("dsa"))) {
253
        *algo = Subkey::AlgoDSA;
Laurent Montel's avatar
Laurent Montel committed
254
    } else if (lowered.startsWith(QLatin1String("elg"))) {
255 256 257 258 259
        *algo = Subkey::AlgoELG;
    }

    if (*algo != Subkey::AlgoUnknown) {
        bool ok;
Laurent Montel's avatar
Laurent Montel committed
260
        *size = lowered.rightRef(lowered.size() - 3).toInt(&ok);
261 262
        if (!ok) {
            qCWarning(KLEOPATRA_LOG) << "Could not extract size from: " << lowered;
Andre Heinecke's avatar
Andre Heinecke committed
263
            *size = 3072;
264 265 266 267 268
        }
        return;
    }

    // Now the ECC Algorithms
Laurent Montel's avatar
Laurent Montel committed
269
    if (lowered.startsWith(QLatin1String("ed25519"))) {
270 271 272 273 274 275 276
        // Special handling for this as technically
        // this is a cv25519 curve used for EDDSA
        curve = split[0];
        *algo = Subkey::AlgoEDDSA;
        return;
    }

Laurent Montel's avatar
Laurent Montel committed
277 278 279 280
    if (lowered.startsWith(QLatin1String("cv25519")) ||
        lowered.startsWith(QLatin1String("nist")) ||
        lowered.startsWith(QLatin1String("brainpool")) ||
        lowered.startsWith(QLatin1String("secp"))) {
281 282 283 284 285 286 287 288
        curve = split[0];
        *algo = isEncrypt ? Subkey::AlgoECDH : Subkey::AlgoECDSA;
        return;
    }

    qCWarning(KLEOPATRA_LOG) << "Failed to parse default_pubkey_algo:" << algoString;
}

289 290 291
/* Use Windows API to query the user name and email.
   EXTENDED_NAME_FORMAT is documented in MSDN */
#ifdef Q_OS_WIN
292
static QString win_get_user_name (EXTENDED_NAME_FORMAT what)
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
{
  QString ret;
  wchar_t tmp[1];
  ULONG nSize = 1;
  if (what == NameUnknown) {
      if (GetUserNameW (tmp, &nSize)) {
          qCWarning (KLEOPATRA_LOG) << "Got empty username";
          return ret;
      }
  } else if (GetUserNameExW (what, tmp, &nSize)) {
      return ret;
  }

  /* nSize now contains the required size of the buffer */
  wchar_t *buf = new wchar_t[nSize];

  if (what == NameUnknown) {
      if (!GetUserNameW (buf, &nSize)) {
          qCWarning (KLEOPATRA_LOG) << "Failed to get username";
          delete[] buf;
          return ret;
      }
  } else if (!GetUserNameExW (what, buf, &nSize)) {
      delete[] buf;
      return ret;
  }
  ret = QString::fromWCharArray (buf, nSize);
  delete[] buf;
  return ret.trimmed();
}
#endif

325 326 327 328 329 330 331 332 333 334 335 336 337
static QString env_get_user_name (bool mbox)
{
    const auto var = qEnvironmentVariable("EMAIL");
    if (!var.isEmpty()) {
        QString name, addrspec, comment;
        const auto result = KEmailAddress::splitAddress (var, name, addrspec, comment);
        if (result == KEmailAddress::AddressOk) {
            return (mbox ? addrspec : name);
        }
    }
    return QString ();
}

338
Q_DECLARE_METATYPE(GpgME::Subkey::PubkeyAlgo)
Laurent Montel's avatar
Laurent Montel committed
339 340 341 342 343 344 345 346
namespace Kleo
{
namespace NewCertificateUi
{
class WizardPage : public QWizardPage
{
    Q_OBJECT
protected:
Laurent Montel's avatar
Laurent Montel committed
347
    explicit WizardPage(QWidget *parent = nullptr)
Laurent Montel's avatar
Laurent Montel committed
348
        : QWizardPage(parent) {}
349

Laurent Montel's avatar
Laurent Montel committed
350 351
    NewCertificateWizard *wizard() const
    {
352
        Q_ASSERT(static_cast<NewCertificateWizard *>(QWizardPage::wizard()) == qobject_cast<NewCertificateWizard *>(QWizardPage::wizard()));
Laurent Montel's avatar
Laurent Montel committed
353 354
        return static_cast<NewCertificateWizard *>(QWizardPage::wizard());
    }
355

Laurent Montel's avatar
Laurent Montel committed
356 357
    QAbstractButton *button(QWizard::WizardButton button) const
    {
Laurent Montel's avatar
Laurent Montel committed
358
        return QWizardPage::wizard() ? QWizardPage::wizard()->button(button) : nullptr;
Laurent Montel's avatar
Laurent Montel committed
359 360 361 362 363 364 365 366
    }

    bool isButtonVisible(QWizard::WizardButton button) const
    {
        if (const QAbstractButton *const b = this->button(button)) {
            return b->isVisible();
        } else {
            return false;
367
        }
Laurent Montel's avatar
Laurent Montel committed
368
    }
369

Laurent Montel's avatar
Laurent Montel committed
370
    QDir tmpDir() const;
371

Laurent Montel's avatar
Laurent Montel committed
372 373 374 375 376
protected Q_SLOTS:
    void setButtonVisible(QWizard::WizardButton button, bool visible)
    {
        if (QAbstractButton *const b = this->button(button)) {
            b->setVisible(visible);
377
        }
Laurent Montel's avatar
Laurent Montel committed
378
    }
379

Laurent Montel's avatar
Laurent Montel committed
380
protected:
Laurent Montel's avatar
Laurent Montel committed
381
#define FIELD(type, name) type name() const { return field( QStringLiteral(#name) ).value<type>(); }
Laurent Montel's avatar
Laurent Montel committed
382 383 384 385 386 387 388 389 390
    FIELD(bool, pgp)
    FIELD(bool, signingAllowed)
    FIELD(bool, encryptionAllowed)
    FIELD(bool, certificationAllowed)
    FIELD(bool, authenticationAllowed)

    FIELD(QString, name)
    FIELD(QString, email)
    FIELD(QString, dn)
391
    FIELD(bool, protectedKey)
Laurent Montel's avatar
Laurent Montel committed
392

393
    FIELD(Subkey::PubkeyAlgo, keyType)
Laurent Montel's avatar
Laurent Montel committed
394
    FIELD(int, keyStrength)
Laurent Montel's avatar
Laurent Montel committed
395
    FIELD(QString, keyCurve)
Laurent Montel's avatar
Laurent Montel committed
396

397
    FIELD(Subkey::PubkeyAlgo, subkeyType)
Laurent Montel's avatar
Laurent Montel committed
398
    FIELD(int, subkeyStrength)
Laurent Montel's avatar
Laurent Montel committed
399
    FIELD(QString, subkeyCurve)
Laurent Montel's avatar
Laurent Montel committed
400 401 402 403 404 405 406 407 408 409 410 411

    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)
412
#undef FIELD
Laurent Montel's avatar
Laurent Montel committed
413
};
414 415 416 417 418
} // namespace NewCertificateUi
} // namespace Kleo

using namespace Kleo::NewCertificateUi;

Laurent Montel's avatar
Laurent Montel committed
419 420
namespace
{
421

Laurent Montel's avatar
Laurent Montel committed
422 423 424 425 426 427 428 429
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)
430
    Q_PROPERTY(Subkey::PubkeyAlgo keyType READ keyType WRITE setKeyType)
431
    Q_PROPERTY(QString keyCurve READ keyCurve WRITE setKeyCurve)
Laurent Montel's avatar
Laurent Montel committed
432
    Q_PROPERTY(uint subkeyStrength READ subkeyStrength WRITE setSubkeyStrength)
433
    Q_PROPERTY(QString subkeyCurve READ subkeyCurve WRITE setSubkeyCurve)
434
    Q_PROPERTY(Subkey::PubkeyAlgo subkeyType READ subkeyType WRITE setSubkeyType)
Laurent Montel's avatar
Laurent Montel committed
435 436 437 438 439 440
    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:
Laurent Montel's avatar
Laurent Montel committed
441
    explicit AdvancedSettingsDialog(QWidget *parent = nullptr)
Laurent Montel's avatar
Laurent Montel committed
442 443
        : QDialog(parent),
          protocol(UnknownProtocol),
444 445
          pgpDefaultAlgorithm(Subkey::AlgoELG_E),
          cmsDefaultAlgorithm(Subkey::AlgoRSA),
Laurent Montel's avatar
Laurent Montel committed
446
          keyTypeImmutable(false),
447
          ui(),
448 449
          mECCSupported(engineIsVersion(2, 1, 0)),
          mEdDSASupported(engineIsVersion(2, 1, 15))
Laurent Montel's avatar
Laurent Montel committed
450
    {
451
        qRegisterMetaType<Subkey::PubkeyAlgo>("Subkey::PubkeyAlgo");
Laurent Montel's avatar
Laurent Montel committed
452 453 454 455
        ui.setupUi(this);
        const QDate today = QDate::currentDate();
        ui.expiryDE->setMinimumDate(today);
        ui.expiryDE->setDate(today.addYears(2));
456
        ui.expiryCB->setChecked(true);
Laurent Montel's avatar
Laurent Montel committed
457 458 459 460 461 462
        ui.emailLW->setDefaultValue(i18n("new email"));
        ui.dnsLW->setDefaultValue(i18n("new dns name"));
        ui.uriLW->setDefaultValue(i18n("new uri"));

        fillKeySizeComboBoxen();
    }
463

Laurent Montel's avatar
Laurent Montel committed
464 465 466 467 468 469 470 471
    void setProtocol(GpgME::Protocol proto)
    {
        if (protocol == proto) {
            return;
        }
        protocol = proto;
        loadDefaultKeyType();
    }
472

Laurent Montel's avatar
Laurent Montel committed
473 474 475 476 477 478 479 480
    void setAdditionalUserIDs(const QStringList &items)
    {
        ui.uidLW->setItems(items);
    }
    QStringList additionalUserIDs() const
    {
        return ui.uidLW->items();
    }
481

Laurent Montel's avatar
Laurent Montel committed
482 483 484 485 486 487 488 489
    void setAdditionalEMailAddresses(const QStringList &items)
    {
        ui.emailLW->setItems(items);
    }
    QStringList additionalEMailAddresses() const
    {
        return ui.emailLW->items();
    }
490

Laurent Montel's avatar
Laurent Montel committed
491 492 493 494 495 496 497 498
    void setDnsNames(const QStringList &items)
    {
        ui.dnsLW->setItems(items);
    }
    QStringList dnsNames() const
    {
        return ui.dnsLW->items();
    }
499

Laurent Montel's avatar
Laurent Montel committed
500 501 502 503 504 505 506 507
    void setUris(const QStringList &items)
    {
        ui.uriLW->setItems(items);
    }
    QStringList uris() const
    {
        return ui.uriLW->items();
    }
508

Laurent Montel's avatar
Laurent Montel committed
509 510 511 512 513 514 515 516 517
    void setKeyStrength(unsigned int strength)
    {
        set_keysize(ui.rsaKeyStrengthCB, strength);
        set_keysize(ui.dsaKeyStrengthCB, strength);
    }
    unsigned int keyStrength() const
    {
        return
            ui.dsaRB->isChecked() ? get_keysize(ui.dsaKeyStrengthCB) :
Laurent Montel's avatar
Laurent Montel committed
518
            ui.rsaRB->isChecked() ? get_keysize(ui.rsaKeyStrengthCB) : 0;
Laurent Montel's avatar
Laurent Montel committed
519
    }
520

521
    void setKeyType(Subkey::PubkeyAlgo algo)
Laurent Montel's avatar
Laurent Montel committed
522 523 524
    {
        QRadioButton *const rb =
            is_rsa(algo) ? ui.rsaRB :
525
            is_dsa(algo) ? ui.dsaRB :
526
            is_ecdsa(algo) || is_eddsa(algo) ? ui.ecdsaRB : nullptr;
Laurent Montel's avatar
Laurent Montel committed
527 528
        if (rb) {
            rb->setChecked(true);
529
        }
Laurent Montel's avatar
Laurent Montel committed
530
    }
531
    Subkey::PubkeyAlgo keyType() const
Laurent Montel's avatar
Laurent Montel committed
532 533
    {
        return
534 535
            ui.dsaRB->isChecked() ? Subkey::AlgoDSA :
            ui.rsaRB->isChecked() ? Subkey::AlgoRSA :
536
            ui.ecdsaRB->isChecked() ?
537
                ui.ecdsaKeyCurvesCB->currentText() == QLatin1String("ed25519") ? Subkey::AlgoEDDSA :
538
                Subkey::AlgoECDSA :
539
            Subkey::AlgoUnknown;
Laurent Montel's avatar
Laurent Montel committed
540
    }
541

542 543 544 545 546 547 548 549 550 551
    void setKeyCurve(const QString &curve)
    {
        set_curve(ui.ecdsaKeyCurvesCB, curve);
    }

    QString keyCurve() const
    {
        return get_curve(ui.ecdsaKeyCurvesCB);
    }

552
    void setSubkeyType(Subkey::PubkeyAlgo algo)
Laurent Montel's avatar
Laurent Montel committed
553 554 555
    {
        ui.elgCB->setChecked(is_elg(algo));
        ui.rsaSubCB->setChecked(is_rsa(algo));
556
        ui.ecdhCB->setChecked(is_ecdh(algo));
Laurent Montel's avatar
Laurent Montel committed
557
    }
558
    Subkey::PubkeyAlgo subkeyType() const
Laurent Montel's avatar
Laurent Montel committed
559 560
    {
        if (ui.elgCB->isChecked()) {
561
            return Subkey::AlgoELG_E;
Laurent Montel's avatar
Laurent Montel committed
562
        } else if (ui.rsaSubCB->isChecked()) {
563
            return Subkey::AlgoRSA;
564
        } else if (ui.ecdhCB->isChecked()) {
565
            return Subkey::AlgoECDH;
566
        }
567
        return Subkey::AlgoUnknown;
Laurent Montel's avatar
Laurent Montel committed
568
    }
569

570 571 572 573 574 575 576 577 578 579
    void setSubkeyCurve(const QString &curve)
    {
        set_curve(ui.ecdhKeyCurvesCB, curve);
    }

    QString subkeyCurve() const
    {
        return get_curve(ui.ecdhKeyCurvesCB);
    }

Laurent Montel's avatar
Laurent Montel committed
580 581
    void setSubkeyStrength(unsigned int strength)
    {
582
        if (subkeyType() == Subkey::AlgoRSA) {
Laurent Montel's avatar
Laurent Montel committed
583 584 585
            set_keysize(ui.rsaKeyStrengthSubCB, strength);
        } else {
            set_keysize(ui.elgKeyStrengthCB, strength);
586
        }
Laurent Montel's avatar
Laurent Montel committed
587 588 589
    }
    unsigned int subkeyStrength() const
    {
590
        if (subkeyType() == Subkey::AlgoRSA) {
Laurent Montel's avatar
Laurent Montel committed
591
            return get_keysize(ui.rsaKeyStrengthSubCB);
592
        }
Laurent Montel's avatar
Laurent Montel committed
593 594
        return get_keysize(ui.elgKeyStrengthCB);
    }
595

Laurent Montel's avatar
Laurent Montel committed
596 597 598 599 600 601 602 603
    void setSigningAllowed(bool on)
    {
        ui.signingCB->setChecked(on);
    }
    bool signingAllowed() const
    {
        return ui.signingCB->isChecked();
    }
604

Laurent Montel's avatar
Laurent Montel committed
605 606 607 608 609 610 611 612
    void setEncryptionAllowed(bool on)
    {
        ui.encryptionCB->setChecked(on);
    }
    bool encryptionAllowed() const
    {
        return ui.encryptionCB->isChecked();
    }
613

Laurent Montel's avatar
Laurent Montel committed
614 615 616 617 618 619 620 621
    void setCertificationAllowed(bool on)
    {
        ui.certificationCB->setChecked(on);
    }
    bool certificationAllowed() const
    {
        return ui.certificationCB->isChecked();
    }
622

Laurent Montel's avatar
Laurent Montel committed
623 624 625 626 627 628 629 630
    void setAuthenticationAllowed(bool on)
    {
        ui.authenticationCB->setChecked(on);
    }
    bool authenticationAllowed() const
    {
        return ui.authenticationCB->isChecked();
    }
631

Laurent Montel's avatar
Laurent Montel committed
632
    void setExpiryDate(QDate date)
Laurent Montel's avatar
Laurent Montel committed
633 634 635 636 637 638 639 640 641
    {
        if (date.isValid()) {
            ui.expiryDE->setDate(date);
        } else {
            ui.expiryCB->setChecked(false);
        }
    }
    QDate expiryDate() const
    {
Laurent Montel's avatar
Laurent Montel committed
642
        return ui.expiryCB->isChecked() ? ui.expiryDE->date() : QDate();
Laurent Montel's avatar
Laurent Montel committed
643
    }
644

Laurent Montel's avatar
Laurent Montel committed
645 646
Q_SIGNALS:
    void changed();
647

Laurent Montel's avatar
Laurent Montel committed
648 649 650 651 652
private Q_SLOTS:
    void slotKeyMaterialSelectionChanged()
    {
        const unsigned int algo = keyType();
        const unsigned int sk_algo = subkeyType();
653

Laurent Montel's avatar
Laurent Montel committed
654 655 656
        if (protocol == OpenPGP) {
            if (!keyTypeImmutable) {
                ui.elgCB->setEnabled(is_dsa(algo));
657
                ui.rsaSubCB->setEnabled(is_rsa(algo));
658
                ui.ecdhCB->setEnabled(is_ecdsa(algo) || is_eddsa(algo));
659
                if (sender() == ui.dsaRB || sender() == ui.rsaRB || sender() == ui.ecdsaRB) {
Laurent Montel's avatar
Laurent Montel committed
660
                    ui.elgCB->setChecked(is_dsa(algo));
661
                    ui.ecdhCB->setChecked(is_ecdsa(algo) || is_eddsa(algo));
662
                    ui.rsaSubCB->setChecked(is_rsa(algo));
Thomas McGuire's avatar
Thomas McGuire committed
663
                }
Laurent Montel's avatar
Laurent Montel committed
664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679
                if (is_rsa(algo)) {
                    ui.encryptionCB->setEnabled(true);
                    ui.encryptionCB->setChecked(true);
                    ui.signingCB->setEnabled(true);
                    ui.signingCB->setChecked(true);
                    ui.authenticationCB->setEnabled(true);
                    if (is_rsa(sk_algo)) {
                        ui.encryptionCB->setEnabled(false);
                        ui.encryptionCB->setChecked(true);
                    } else {
                        ui.encryptionCB->setEnabled(true);
                    }
                } else if (is_dsa(algo)) {
                    ui.encryptionCB->setEnabled(false);
                    if (is_elg(sk_algo)) {
                        ui.encryptionCB->setChecked(true);
680
                    } else {
Laurent Montel's avatar
Laurent Montel committed
681
                        ui.encryptionCB->setChecked(false);
682
                    }
683
                } else if (is_ecdsa(algo) || is_eddsa(algo)) {
684 685 686 687 688
                    ui.signingCB->setEnabled(true);
                    ui.signingCB->setChecked(true);
                    ui.authenticationCB->setEnabled(true);
                    ui.encryptionCB->setEnabled(false);
                    ui.encryptionCB->setChecked(is_ecdh(sk_algo));
689 690
                }
            }
Laurent Montel's avatar
Laurent Montel committed
691 692
        } else {
            //assert( is_rsa( keyType() ) ); // it can happen through misconfiguration by the admin that no key type is selectable at all
693
        }
Laurent Montel's avatar
Laurent Montel committed
694
    }
695

Laurent Montel's avatar
Laurent Montel committed
696 697 698 699
    void slotSigningAllowedToggled(bool on)
    {
        if (!on && protocol == CMS && !encryptionAllowed()) {
            setEncryptionAllowed(true);
700
        }
Laurent Montel's avatar
Laurent Montel committed
701 702 703 704 705
    }
    void slotEncryptionAllowedToggled(bool on)
    {
        if (!on && protocol == CMS && !signingAllowed()) {
            setSigningAllowed(true);
706
        }
Laurent Montel's avatar
Laurent Montel committed
707
    }
708

Laurent Montel's avatar
Laurent Montel committed
709 710 711
private:
    void fillKeySizeComboBoxen();
    void loadDefaultKeyType();
712
    void loadDefaultGnuPGKeyType();
Laurent Montel's avatar
Laurent Montel committed
713
    void updateWidgetVisibility();
714

Laurent Montel's avatar
Laurent Montel committed
715 716 717 718 719 720
private:
    GpgME::Protocol protocol;
    unsigned int pgpDefaultAlgorithm;
    unsigned int cmsDefaultAlgorithm;
    bool keyTypeImmutable;
    Ui_AdvancedSettingsDialog ui;
721
    bool mECCSupported;
722
    bool mEdDSASupported;
Laurent Montel's avatar
Laurent Montel committed
723
};
724

Laurent Montel's avatar
Laurent Montel committed
725 726 727 728
class ChooseProtocolPage : public WizardPage
{
    Q_OBJECT
public:
Laurent Montel's avatar
Laurent Montel committed
729
    explicit ChooseProtocolPage(QWidget *p = nullptr)
Laurent Montel's avatar
Laurent Montel committed
730 731 732 733 734
        : WizardPage(p),
          initialized(false),
          ui()
    {
        ui.setupUi(this);
Laurent Montel's avatar
Laurent Montel committed
735
        registerField(QStringLiteral("pgp"), ui.pgpCLB);
Laurent Montel's avatar
Laurent Montel committed
736
    }
737

Laurent Montel's avatar
Laurent Montel committed
738 739 740 741 742 743 744 745 746
    void setProtocol(Protocol proto)
    {
        if (proto == OpenPGP) {
            ui.pgpCLB->setChecked(true);
        } else if (proto == CMS) {
            ui.x509CLB->setChecked(true);
        } else {
            force_set_checked(ui.pgpCLB,  false);
            force_set_checked(ui.x509CLB, false);
747
        }
Laurent Montel's avatar
Laurent Montel committed
748
    }
749

Laurent Montel's avatar
Laurent Montel committed
750 751 752 753
    Protocol protocol() const
    {
        return
            ui.pgpCLB->isChecked()  ? OpenPGP :
Laurent Montel's avatar
Laurent Montel committed
754
            ui.x509CLB->isChecked() ? CMS : UnknownProtocol;
Laurent Montel's avatar
Laurent Montel committed
755
    }
756

757
    void initializePage() override {
Laurent Montel's avatar
Laurent Montel committed
758 759
        if (!initialized)
        {
760 761
            connect(ui.pgpCLB,  &QAbstractButton::clicked, wizard(), &QWizard::next, Qt::QueuedConnection);
            connect(ui.x509CLB, &QAbstractButton::clicked, wizard(), &QWizard::next, Qt::QueuedConnection);
762
        }
Laurent Montel's avatar
Laurent Montel committed
763 764
        initialized = true;
    }
765

766
    bool isComplete() const override
Laurent Montel's avatar
Laurent Montel committed
767
    {
Laurent Montel's avatar
Laurent Montel committed
768
        return protocol() != UnknownProtocol;
Laurent Montel's avatar
Laurent Montel committed
769
    }
770

Laurent Montel's avatar
Laurent Montel committed
771 772 773 774
private:
    bool initialized : 1;
    Ui_ChooseProtocolPage ui;
};
775

Laurent Montel's avatar
Laurent Montel committed
776 777 778 779 780 781
struct Line {
    QString attr;
    QString label;
    QString regex;
    QLineEdit *edit;
};
782

Laurent Montel's avatar
Laurent Montel committed
783 784 785 786
class EnterDetailsPage : public WizardPage
{
    Q_OBJECT
public:
Laurent Montel's avatar
Laurent Montel committed
787
    explicit EnterDetailsPage(QWidget *p = nullptr)
Laurent Montel's avatar
Laurent Montel committed
788 789 790 791 792
        : WizardPage(p), dialog(this), ui()
    {
        ui.setupUi(this);

        // set errorLB to have a fixed height of two lines:
793
        ui.errorLB->setText(QStringLiteral("2<br>1"));
Laurent Montel's avatar
Laurent Montel committed
794 795 796
        ui.errorLB->setFixedHeight(ui.errorLB->minimumSizeHint().height());
        ui.errorLB->clear();

797 798
        connect(ui.resultLE, &QLineEdit::textChanged,
                this, &QWizardPage::completeChanged);
Laurent Montel's avatar
Laurent Montel committed
799
        // The email doesn't necessarily show up in ui.resultLE:
800 801
        connect(ui.emailLE, &QLineEdit::textChanged,
                this, &QWizardPage::completeChanged);
Laurent Montel's avatar
Laurent Montel committed
802
        registerDialogPropertiesAsFields();
Laurent Montel's avatar
Laurent Montel committed
803 804 805
        registerField(QStringLiteral("dn"), ui.resultLE);
        registerField(QStringLiteral("name"), ui.nameLE);
        registerField(QStringLiteral("email"), ui.emailLE);
806
        registerField(QStringLiteral("protectedKey"), ui.withPassCB);
Laurent Montel's avatar
Laurent Montel committed
807
        updateForm();
808 809
        setCommitPage(true);
        setButtonText(QWizard::CommitButton, i18nc("@action", "Create"));
810 811 812 813 814 815 816 817 818

        const auto conf = QGpgME::cryptoConfig();
        if (!conf) {
            qCWarning(KLEOPATRA_LOG) << "Failed to obtain cryptoConfig.";
            return;
        }
        const auto entry = conf->entry(QStringLiteral("gpg-agent"),
                                   QStringLiteral("Passphrase policy"),
                                   QStringLiteral("enforce-passphrase-constraints"));
819 820 821 822 823
        if (!pgp()) {
            // GnuPG / GPGME as of 2.2.27 do not support
            // pinentry mode and passphrase setting for S/MIME
            ui.withPassCB->setVisible(false);
        }
824 825 826 827 828 829 830 831 832
        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(), "CertificateCreationWizard");
            ui.withPassCB->setChecked(config.readEntry("WithPassphrase", false));
            ui.withPassCB->setEnabled(!config.isEntryImmutable("WithPassphrase"));
        }
Laurent Montel's avatar
Laurent Montel committed
833
    }
834

835 836
    bool isComplete() const override;
    void initializePage() override {
Laurent Montel's avatar
Laurent Montel committed
837 838 839
        updateForm();
        dialog.setProtocol(pgp() ? OpenPGP : CMS);
    }
840
    void cleanupPage() override {
Laurent Montel's avatar
Laurent Montel committed
841 842
        saveValues();
    }
843

Laurent Montel's avatar
Laurent Montel committed
844 845 846 847 848
private:
    void updateForm();
    void clearForm();
    void saveValues();
    void registerDialogPropertiesAsFields();
Marc Mutz's avatar
Marc Mutz committed
849

Laurent Montel's avatar
Laurent Montel committed
850 851 852
private:
    QString pgpUserID() const;
    QString cmsDN() const;
Marc Mutz's avatar
Marc Mutz committed
853

Laurent Montel's avatar
Laurent Montel committed
854 855 856 857 858 859
private Q_SLOTS:
    void slotAdvancedSettingsClicked();
    void slotUpdateResultLabel()
    {
        ui.resultLE->setText(pgp() ? pgpUserID() : cmsDN());
    }
860

Laurent Montel's avatar
Laurent Montel committed
861 862 863 864 865 866 867
private:
    QVector<Line> lineList;
    QList<QWidget *> dynamicWidgets;
    QMap<QString, QString> savedValues;
    AdvancedSettingsDialog dialog;
    Ui_EnterDetailsPage ui;
};
868

Laurent Montel's avatar
Laurent Montel committed
869 870 871 872
class KeyCreationPage : public WizardPage
{
    Q_OBJECT
public:
Laurent Montel's avatar
Laurent Montel committed
873
    explicit KeyCreationPage(QWidget *p = nullptr)
Laurent Montel's avatar
Laurent Montel committed
874 875 876 877 878 879
        : WizardPage(p),
          ui()
    {
        ui.setupUi(this);
    }

880
    bool isComplete() const override
Laurent Montel's avatar
Laurent Montel committed
881 882 883 884
    {
        return !job;
    }

885
    void initializePage() override {
Laurent Montel's avatar
Laurent Montel committed
886 887 888 889 890 891
        startJob();
    }

private:
    void startJob()
    {
Andre Heinecke's avatar
Andre Heinecke committed
892
        const auto proto = pgp() ? QGpgME::openpgp() : QGpgME::smime();
Laurent Montel's avatar
Laurent Montel committed
893 894 895
        if (!proto) {
            return;
        }
896
        QGpgME::KeyGenerationJob *const j = proto->keyGenerationJob();
Laurent Montel's avatar
Laurent Montel committed
897 898
        if (!j) {
            return;
899
        }
900
        if (!protectedKey() && pgp()) {
901 902 903 904
            auto ctx = QGpgME::Job::context(j);
            ctx->setPassphraseProvider(&mEmptyPWProvider);