newcertificatewizard.cpp 61.5 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
#include "utils/userinfo.h"
34

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

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

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

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

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

67
#include <algorithm>
68

69
#include <KSharedConfig>
Laurent Montel's avatar
Laurent Montel committed
70
#include <QLocale>
71

72
73
using namespace Kleo;
using namespace Kleo::NewCertificateUi;
74
using namespace Kleo::Commands;
75
76
using namespace GpgME;

77
78
79
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
80

81
82
83
static const char RSA_KEYSIZE_LABELS_ENTRY[] = "RSAKeySizeLabels";
static const char DSA_KEYSIZE_LABELS_ENTRY[] = "DSAKeySizeLabels";
static const char ELG_KEYSIZE_LABELS_ENTRY[] = "ELGKeySizeLabels";
84

85
86
static const char PGP_KEY_TYPE_ENTRY[] = "PGPKeyType";
static const char CMS_KEY_TYPE_ENTRY[] = "CMSKeyType";
87

88
89
90
91
92
93
94
95
96
97
98
99
100
101
// 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") },
};

102
103
104
105
106
107
108
109
110
111
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
112
113
static void set_tab_order(const QList<QWidget *> &wl)
{
114
    kdtools::for_each_adjacent_pair(wl.begin(), wl.end(), &QWidget::setTabOrder);
115
116
}

117
enum KeyAlgo { RSA, DSA, ELG, ECDSA, ECDH, EDDSA };
118

119
static bool is_algo(Subkey::PubkeyAlgo algo, KeyAlgo what)
Laurent Montel's avatar
Laurent Montel committed
120
121
{
    switch (algo) {
122
123
124
125
126
127
128
129
130
131
132
133
134
        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;
135
136
        case Subkey::AlgoEDDSA:
            return what == EDDSA;
137
138
        default:
            break;
139
140
141
142
    }
    return false;
}

Laurent Montel's avatar
Laurent Montel committed
143
144
static bool is_rsa(unsigned int algo)
{
145
    return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), RSA);
146
147
}

Laurent Montel's avatar
Laurent Montel committed
148
149
static bool is_dsa(unsigned int algo)
{
150
    return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), DSA);
151
152
}

Laurent Montel's avatar
Laurent Montel committed
153
154
static bool is_elg(unsigned int algo)
{
155
    return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), ELG);
156
157
}

158
159
static bool is_ecdsa(unsigned int algo)
{
160
    return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), ECDSA);
161
162
}

163
164
165
166
167
static bool is_eddsa(unsigned int algo)
{
    return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), EDDSA);
}

168
169
static bool is_ecdh(unsigned int algo)
{
170
    return is_algo(static_cast<Subkey::PubkeyAlgo>(algo), ECDH);
171
172
}

Laurent Montel's avatar
Laurent Montel committed
173
174
static void force_set_checked(QAbstractButton *b, bool on)
{
175
176
    // 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
177
178
179
    b->setAutoExclusive(false);
    b->setChecked(b->isEnabled() && on);
    b->setAutoExclusive(autoExclusive);
180
181
}

Laurent Montel's avatar
Laurent Montel committed
182
183
184
static void set_keysize(QComboBox *cb, unsigned int strength)
{
    if (!cb) {
185
        return;
Laurent Montel's avatar
Laurent Montel committed
186
187
188
    }
    const int idx = cb->findData(static_cast<int>(strength));
    cb->setCurrentIndex(idx);
189
190
}

Laurent Montel's avatar
Laurent Montel committed
191
192
193
static unsigned int get_keysize(const QComboBox *cb)
{
    if (!cb) {
194
        return 0;
Laurent Montel's avatar
Laurent Montel committed
195
    }
196
    const int idx = cb->currentIndex();
Laurent Montel's avatar
Laurent Montel committed
197
    if (idx < 0) {
198
        return 0;
Laurent Montel's avatar
Laurent Montel committed
199
200
    }
    return cb->itemData(idx).toInt();
201
202
}

203
204
205
206
207
static void set_curve(QComboBox *cb, const QString &curve)
{
    if (!cb) {
        return;
    }
208
    const int idx = cb->findText(curve);
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
    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();
}

224
225
226
227
228
229
230
231
232
233
234
// 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('/'));
235
    bool isEncrypt = split.size() == 2 && split[1].contains(QLatin1String("enc"));
236
237
238
239
240
241
242

    // Normalize
    const auto lowered = split[0].toLower().remove(QLatin1Char('-'));
    if (!algo || !size) {
        return;
    }
    *algo = Subkey::AlgoUnknown;
Laurent Montel's avatar
Laurent Montel committed
243
    if (lowered.startsWith(QLatin1String("rsa"))) {
244
        *algo = Subkey::AlgoRSA;
Laurent Montel's avatar
Laurent Montel committed
245
    } else if (lowered.startsWith(QLatin1String("dsa"))) {
246
        *algo = Subkey::AlgoDSA;
Laurent Montel's avatar
Laurent Montel committed
247
    } else if (lowered.startsWith(QLatin1String("elg"))) {
248
249
250
251
252
        *algo = Subkey::AlgoELG;
    }

    if (*algo != Subkey::AlgoUnknown) {
        bool ok;
Laurent Montel's avatar
Laurent Montel committed
253
        *size = lowered.rightRef(lowered.size() - 3).toInt(&ok);
254
255
        if (!ok) {
            qCWarning(KLEOPATRA_LOG) << "Could not extract size from: " << lowered;
Andre Heinecke's avatar
Andre Heinecke committed
256
            *size = 3072;
257
258
259
260
261
        }
        return;
    }

    // Now the ECC Algorithms
Laurent Montel's avatar
Laurent Montel committed
262
    if (lowered.startsWith(QLatin1String("ed25519"))) {
263
264
265
266
267
268
269
        // 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
270
271
272
273
    if (lowered.startsWith(QLatin1String("cv25519")) ||
        lowered.startsWith(QLatin1String("nist")) ||
        lowered.startsWith(QLatin1String("brainpool")) ||
        lowered.startsWith(QLatin1String("secp"))) {
274
275
276
277
278
279
280
281
        curve = split[0];
        *algo = isEncrypt ? Subkey::AlgoECDH : Subkey::AlgoECDSA;
        return;
    }

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

282
Q_DECLARE_METATYPE(GpgME::Subkey::PubkeyAlgo)
Laurent Montel's avatar
Laurent Montel committed
283
284
285
286
287
288
289
290
namespace Kleo
{
namespace NewCertificateUi
{
class WizardPage : public QWizardPage
{
    Q_OBJECT
protected:
Laurent Montel's avatar
Laurent Montel committed
291
    explicit WizardPage(QWidget *parent = nullptr)
Laurent Montel's avatar
Laurent Montel committed
292
        : QWizardPage(parent) {}
293

Laurent Montel's avatar
Laurent Montel committed
294
295
    NewCertificateWizard *wizard() const
    {
296
        Q_ASSERT(static_cast<NewCertificateWizard *>(QWizardPage::wizard()) == qobject_cast<NewCertificateWizard *>(QWizardPage::wizard()));
Laurent Montel's avatar
Laurent Montel committed
297
298
        return static_cast<NewCertificateWizard *>(QWizardPage::wizard());
    }
299

Laurent Montel's avatar
Laurent Montel committed
300
301
    QAbstractButton *button(QWizard::WizardButton button) const
    {
Laurent Montel's avatar
Laurent Montel committed
302
        return QWizardPage::wizard() ? QWizardPage::wizard()->button(button) : nullptr;
Laurent Montel's avatar
Laurent Montel committed
303
304
305
306
307
308
309
310
    }

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

Laurent Montel's avatar
Laurent Montel committed
314
    QDir tmpDir() const;
315

Laurent Montel's avatar
Laurent Montel committed
316
317
318
319
320
protected Q_SLOTS:
    void setButtonVisible(QWizard::WizardButton button, bool visible)
    {
        if (QAbstractButton *const b = this->button(button)) {
            b->setVisible(visible);
321
        }
Laurent Montel's avatar
Laurent Montel committed
322
    }
323

Laurent Montel's avatar
Laurent Montel committed
324
protected:
Laurent Montel's avatar
Laurent Montel committed
325
#define FIELD(type, name) type name() const { return field( QStringLiteral(#name) ).value<type>(); }
Laurent Montel's avatar
Laurent Montel committed
326
327
328
329
330
331
332
333
334
    FIELD(bool, pgp)
    FIELD(bool, signingAllowed)
    FIELD(bool, encryptionAllowed)
    FIELD(bool, certificationAllowed)
    FIELD(bool, authenticationAllowed)

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

337
    FIELD(Subkey::PubkeyAlgo, keyType)
Laurent Montel's avatar
Laurent Montel committed
338
    FIELD(int, keyStrength)
Laurent Montel's avatar
Laurent Montel committed
339
    FIELD(QString, keyCurve)
Laurent Montel's avatar
Laurent Montel committed
340

341
    FIELD(Subkey::PubkeyAlgo, subkeyType)
Laurent Montel's avatar
Laurent Montel committed
342
    FIELD(int, subkeyStrength)
Laurent Montel's avatar
Laurent Montel committed
343
    FIELD(QString, subkeyCurve)
Laurent Montel's avatar
Laurent Montel committed
344
345
346
347
348
349
350
351
352
353
354
355

    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)
356
#undef FIELD
Laurent Montel's avatar
Laurent Montel committed
357
};
358
359
360
361
362
} // namespace NewCertificateUi
} // namespace Kleo

using namespace Kleo::NewCertificateUi;

Laurent Montel's avatar
Laurent Montel committed
363
364
namespace
{
365

Laurent Montel's avatar
Laurent Montel committed
366
367
368
369
370
371
372
373
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)
374
    Q_PROPERTY(Subkey::PubkeyAlgo keyType READ keyType WRITE setKeyType)
375
    Q_PROPERTY(QString keyCurve READ keyCurve WRITE setKeyCurve)
Laurent Montel's avatar
Laurent Montel committed
376
    Q_PROPERTY(uint subkeyStrength READ subkeyStrength WRITE setSubkeyStrength)
377
    Q_PROPERTY(QString subkeyCurve READ subkeyCurve WRITE setSubkeyCurve)
378
    Q_PROPERTY(Subkey::PubkeyAlgo subkeyType READ subkeyType WRITE setSubkeyType)
Laurent Montel's avatar
Laurent Montel committed
379
380
381
382
383
384
    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
385
    explicit AdvancedSettingsDialog(QWidget *parent = nullptr)
Laurent Montel's avatar
Laurent Montel committed
386
387
        : QDialog(parent),
          protocol(UnknownProtocol),
388
389
          pgpDefaultAlgorithm(Subkey::AlgoELG_E),
          cmsDefaultAlgorithm(Subkey::AlgoRSA),
Laurent Montel's avatar
Laurent Montel committed
390
          keyTypeImmutable(false),
391
          ui(),
392
393
          mECCSupported(engineIsVersion(2, 1, 0)),
          mEdDSASupported(engineIsVersion(2, 1, 15))
Laurent Montel's avatar
Laurent Montel committed
394
    {
395
        qRegisterMetaType<Subkey::PubkeyAlgo>("Subkey::PubkeyAlgo");
Laurent Montel's avatar
Laurent Montel committed
396
397
398
399
        ui.setupUi(this);
        const QDate today = QDate::currentDate();
        ui.expiryDE->setMinimumDate(today);
        ui.expiryDE->setDate(today.addYears(2));
400
        ui.expiryCB->setChecked(true);
Laurent Montel's avatar
Laurent Montel committed
401
402
403
404
405
406
        ui.emailLW->setDefaultValue(i18n("new email"));
        ui.dnsLW->setDefaultValue(i18n("new dns name"));
        ui.uriLW->setDefaultValue(i18n("new uri"));

        fillKeySizeComboBoxen();
    }
407

Laurent Montel's avatar
Laurent Montel committed
408
409
410
411
412
413
414
415
    void setProtocol(GpgME::Protocol proto)
    {
        if (protocol == proto) {
            return;
        }
        protocol = proto;
        loadDefaultKeyType();
    }
416

Laurent Montel's avatar
Laurent Montel committed
417
418
419
420
421
422
423
424
    void setAdditionalUserIDs(const QStringList &items)
    {
        ui.uidLW->setItems(items);
    }
    QStringList additionalUserIDs() const
    {
        return ui.uidLW->items();
    }
425

Laurent Montel's avatar
Laurent Montel committed
426
427
428
429
430
431
432
433
    void setAdditionalEMailAddresses(const QStringList &items)
    {
        ui.emailLW->setItems(items);
    }
    QStringList additionalEMailAddresses() const
    {
        return ui.emailLW->items();
    }
434

Laurent Montel's avatar
Laurent Montel committed
435
436
437
438
439
440
441
442
    void setDnsNames(const QStringList &items)
    {
        ui.dnsLW->setItems(items);
    }
    QStringList dnsNames() const
    {
        return ui.dnsLW->items();
    }
443

Laurent Montel's avatar
Laurent Montel committed
444
445
446
447
448
449
450
451
    void setUris(const QStringList &items)
    {
        ui.uriLW->setItems(items);
    }
    QStringList uris() const
    {
        return ui.uriLW->items();
    }
452

Laurent Montel's avatar
Laurent Montel committed
453
454
455
456
457
458
459
460
461
    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
462
            ui.rsaRB->isChecked() ? get_keysize(ui.rsaKeyStrengthCB) : 0;
Laurent Montel's avatar
Laurent Montel committed
463
    }
464

465
    void setKeyType(Subkey::PubkeyAlgo algo)
Laurent Montel's avatar
Laurent Montel committed
466
467
468
    {
        QRadioButton *const rb =
            is_rsa(algo) ? ui.rsaRB :
469
            is_dsa(algo) ? ui.dsaRB :
470
            is_ecdsa(algo) || is_eddsa(algo) ? ui.ecdsaRB : nullptr;
Laurent Montel's avatar
Laurent Montel committed
471
472
        if (rb) {
            rb->setChecked(true);
473
        }
Laurent Montel's avatar
Laurent Montel committed
474
    }
475
    Subkey::PubkeyAlgo keyType() const
Laurent Montel's avatar
Laurent Montel committed
476
477
    {
        return
478
479
            ui.dsaRB->isChecked() ? Subkey::AlgoDSA :
            ui.rsaRB->isChecked() ? Subkey::AlgoRSA :
480
            ui.ecdsaRB->isChecked() ?
481
                ui.ecdsaKeyCurvesCB->currentText() == QLatin1String("ed25519") ? Subkey::AlgoEDDSA :
482
                Subkey::AlgoECDSA :
483
            Subkey::AlgoUnknown;
Laurent Montel's avatar
Laurent Montel committed
484
    }
485

486
487
488
489
490
491
492
493
494
495
    void setKeyCurve(const QString &curve)
    {
        set_curve(ui.ecdsaKeyCurvesCB, curve);
    }

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

496
    void setSubkeyType(Subkey::PubkeyAlgo algo)
Laurent Montel's avatar
Laurent Montel committed
497
498
499
    {
        ui.elgCB->setChecked(is_elg(algo));
        ui.rsaSubCB->setChecked(is_rsa(algo));
500
        ui.ecdhCB->setChecked(is_ecdh(algo));
Laurent Montel's avatar
Laurent Montel committed
501
    }
502
    Subkey::PubkeyAlgo subkeyType() const
Laurent Montel's avatar
Laurent Montel committed
503
504
    {
        if (ui.elgCB->isChecked()) {
505
            return Subkey::AlgoELG_E;
Laurent Montel's avatar
Laurent Montel committed
506
        } else if (ui.rsaSubCB->isChecked()) {
507
            return Subkey::AlgoRSA;
508
        } else if (ui.ecdhCB->isChecked()) {
509
            return Subkey::AlgoECDH;
510
        }
511
        return Subkey::AlgoUnknown;
Laurent Montel's avatar
Laurent Montel committed
512
    }
513

514
515
516
517
518
519
520
521
522
523
    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
524
525
    void setSubkeyStrength(unsigned int strength)
    {
526
        if (subkeyType() == Subkey::AlgoRSA) {
Laurent Montel's avatar
Laurent Montel committed
527
528
529
            set_keysize(ui.rsaKeyStrengthSubCB, strength);
        } else {
            set_keysize(ui.elgKeyStrengthCB, strength);
530
        }
Laurent Montel's avatar
Laurent Montel committed
531
532
533
    }
    unsigned int subkeyStrength() const
    {
534
        if (subkeyType() == Subkey::AlgoRSA) {
Laurent Montel's avatar
Laurent Montel committed
535
            return get_keysize(ui.rsaKeyStrengthSubCB);
536
        }
Laurent Montel's avatar
Laurent Montel committed
537
538
        return get_keysize(ui.elgKeyStrengthCB);
    }
539

Laurent Montel's avatar
Laurent Montel committed
540
541
542
543
544
545
546
547
    void setSigningAllowed(bool on)
    {
        ui.signingCB->setChecked(on);
    }
    bool signingAllowed() const
    {
        return ui.signingCB->isChecked();
    }
548

Laurent Montel's avatar
Laurent Montel committed
549
550
551
552
553
554
555
556
    void setEncryptionAllowed(bool on)
    {
        ui.encryptionCB->setChecked(on);
    }
    bool encryptionAllowed() const
    {
        return ui.encryptionCB->isChecked();
    }
557

Laurent Montel's avatar
Laurent Montel committed
558
559
560
561
562
563
564
565
    void setCertificationAllowed(bool on)
    {
        ui.certificationCB->setChecked(on);
    }
    bool certificationAllowed() const
    {
        return ui.certificationCB->isChecked();
    }
566

Laurent Montel's avatar
Laurent Montel committed
567
568
569
570
571
572
573
574
    void setAuthenticationAllowed(bool on)
    {
        ui.authenticationCB->setChecked(on);
    }
    bool authenticationAllowed() const
    {
        return ui.authenticationCB->isChecked();
    }
575

Laurent Montel's avatar
Laurent Montel committed
576
    void setExpiryDate(QDate date)
Laurent Montel's avatar
Laurent Montel committed
577
578
579
580
581
582
583
584
585
    {
        if (date.isValid()) {
            ui.expiryDE->setDate(date);
        } else {
            ui.expiryCB->setChecked(false);
        }
    }
    QDate expiryDate() const
    {
Laurent Montel's avatar
Laurent Montel committed
586
        return ui.expiryCB->isChecked() ? ui.expiryDE->date() : QDate();
Laurent Montel's avatar
Laurent Montel committed
587
    }
588

Laurent Montel's avatar
Laurent Montel committed
589
590
Q_SIGNALS:
    void changed();
591

Laurent Montel's avatar
Laurent Montel committed
592
593
594
595
596
private Q_SLOTS:
    void slotKeyMaterialSelectionChanged()
    {
        const unsigned int algo = keyType();
        const unsigned int sk_algo = subkeyType();
597

Laurent Montel's avatar
Laurent Montel committed
598
599
600
        if (protocol == OpenPGP) {
            if (!keyTypeImmutable) {
                ui.elgCB->setEnabled(is_dsa(algo));
601
                ui.rsaSubCB->setEnabled(is_rsa(algo));
602
                ui.ecdhCB->setEnabled(is_ecdsa(algo) || is_eddsa(algo));
603
                if (sender() == ui.dsaRB || sender() == ui.rsaRB || sender() == ui.ecdsaRB) {
Laurent Montel's avatar
Laurent Montel committed
604
                    ui.elgCB->setChecked(is_dsa(algo));
605
                    ui.ecdhCB->setChecked(is_ecdsa(algo) || is_eddsa(algo));
606
                    ui.rsaSubCB->setChecked(is_rsa(algo));
Thomas McGuire's avatar
Thomas McGuire committed
607
                }
Laurent Montel's avatar
Laurent Montel committed
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
                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);
624
                    } else {
Laurent Montel's avatar
Laurent Montel committed
625
                        ui.encryptionCB->setChecked(false);
626
                    }
627
                } else if (is_ecdsa(algo) || is_eddsa(algo)) {
628
629
630
631
632
                    ui.signingCB->setEnabled(true);
                    ui.signingCB->setChecked(true);
                    ui.authenticationCB->setEnabled(true);
                    ui.encryptionCB->setEnabled(false);
                    ui.encryptionCB->setChecked(is_ecdh(sk_algo));
633
634
                }
            }
Laurent Montel's avatar
Laurent Montel committed
635
636
        } else {
            //assert( is_rsa( keyType() ) ); // it can happen through misconfiguration by the admin that no key type is selectable at all
637
        }
Laurent Montel's avatar
Laurent Montel committed
638
    }
639

Laurent Montel's avatar
Laurent Montel committed
640
641
642
643
    void slotSigningAllowedToggled(bool on)
    {
        if (!on && protocol == CMS && !encryptionAllowed()) {
            setEncryptionAllowed(true);
644
        }
Laurent Montel's avatar
Laurent Montel committed
645
646
647
648
649
    }
    void slotEncryptionAllowedToggled(bool on)
    {
        if (!on && protocol == CMS && !signingAllowed()) {
            setSigningAllowed(true);
650
        }
Laurent Montel's avatar
Laurent Montel committed
651
    }
652

Laurent Montel's avatar
Laurent Montel committed
653
654
655
private:
    void fillKeySizeComboBoxen();
    void loadDefaultKeyType();
656
    void loadDefaultGnuPGKeyType();
Laurent Montel's avatar
Laurent Montel committed
657
    void updateWidgetVisibility();
658

Laurent Montel's avatar
Laurent Montel committed
659
660
661
662
663
664
private:
    GpgME::Protocol protocol;
    unsigned int pgpDefaultAlgorithm;
    unsigned int cmsDefaultAlgorithm;
    bool keyTypeImmutable;
    Ui_AdvancedSettingsDialog ui;
665
    bool mECCSupported;
666
    bool mEdDSASupported;
Laurent Montel's avatar
Laurent Montel committed
667
};
668

Laurent Montel's avatar
Laurent Montel committed
669
670
671
672
class ChooseProtocolPage : public WizardPage
{
    Q_OBJECT
public:
Laurent Montel's avatar
Laurent Montel committed
673
    explicit ChooseProtocolPage(QWidget *p = nullptr)
Laurent Montel's avatar
Laurent Montel committed
674
675
676
677
678
        : WizardPage(p),
          initialized(false),
          ui()
    {
        ui.setupUi(this);
Laurent Montel's avatar
Laurent Montel committed
679
        registerField(QStringLiteral("pgp"), ui.pgpCLB);
Laurent Montel's avatar
Laurent Montel committed
680
    }
681

Laurent Montel's avatar
Laurent Montel committed
682
683
684
685
686
687
688
689
690
    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);
691
        }
Laurent Montel's avatar
Laurent Montel committed
692
    }
693

Laurent Montel's avatar
Laurent Montel committed
694
695
696
697
    Protocol protocol() const
    {
        return
            ui.pgpCLB->isChecked()  ? OpenPGP :
Laurent Montel's avatar
Laurent Montel committed
698
            ui.x509CLB->isChecked() ? CMS : UnknownProtocol;
Laurent Montel's avatar
Laurent Montel committed
699
    }
700

701
    void initializePage() override {
Laurent Montel's avatar
Laurent Montel committed
702
703
        if (!initialized)
        {
704
705
            connect(ui.pgpCLB,  &QAbstractButton::clicked, wizard(), &QWizard::next, Qt::QueuedConnection);
            connect(ui.x509CLB, &QAbstractButton::clicked, wizard(), &QWizard::next, Qt::QueuedConnection);
706
        }
Laurent Montel's avatar
Laurent Montel committed
707
708
        initialized = true;
    }
709

710
    bool isComplete() const override
Laurent Montel's avatar
Laurent Montel committed
711
    {
Laurent Montel's avatar
Laurent Montel committed
712
        return protocol() != UnknownProtocol;
Laurent Montel's avatar
Laurent Montel committed
713
    }
714

Laurent Montel's avatar
Laurent Montel committed
715
716
717
718
private:
    bool initialized : 1;
    Ui_ChooseProtocolPage ui;
};
719

Laurent Montel's avatar
Laurent Montel committed
720
721
722
723
724
725
struct Line {
    QString attr;
    QString label;
    QString regex;
    QLineEdit *edit;
};
726

Laurent Montel's avatar
Laurent Montel committed
727
728
729
730
class EnterDetailsPage : public WizardPage
{
    Q_OBJECT
public:
Laurent Montel's avatar
Laurent Montel committed
731
    explicit EnterDetailsPage(QWidget *p = nullptr)
Laurent Montel's avatar
Laurent Montel committed
732
733
734
735
736
        : WizardPage(p), dialog(this), ui()
    {
        ui.setupUi(this);

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

741
742
        connect(ui.resultLE, &QLineEdit::textChanged,
                this, &QWizardPage::completeChanged);
Laurent Montel's avatar
Laurent Montel committed
743
        // The email doesn't necessarily show up in ui.resultLE:
744
745
        connect(ui.emailLE, &QLineEdit::textChanged,
                this, &QWizardPage::completeChanged);
Laurent Montel's avatar
Laurent Montel committed
746
        registerDialogPropertiesAsFields();
Laurent Montel's avatar
Laurent Montel committed
747
748
749
        registerField(QStringLiteral("dn"), ui.resultLE);
        registerField(QStringLiteral("name"), ui.nameLE);
        registerField(QStringLiteral("email"), ui.emailLE);
750
        registerField(QStringLiteral("protectedKey"), ui.withPassCB);
Laurent Montel's avatar
Laurent Montel committed
751
        updateForm();
752
753
        setCommitPage(true);
        setButtonText(QWizard::CommitButton, i18nc("@action", "Create"));
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771

        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"));
        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
772
    }
773

774
775
    bool isComplete() const override;
    void initializePage() override {
Laurent Montel's avatar
Laurent Montel committed
776
777
778
        updateForm();
        dialog.setProtocol(pgp() ? OpenPGP : CMS);
    }
779
    void cleanupPage() override {
Laurent Montel's avatar
Laurent Montel committed
780
781
        saveValues();
    }
782

Laurent Montel's avatar
Laurent Montel committed
783
784
785
786
787
private:
    void updateForm();
    void clearForm();
    void saveValues();
    void registerDialogPropertiesAsFields();
Marc Mutz's avatar
Marc Mutz committed
788

Laurent Montel's avatar
Laurent Montel committed
789
790
791
private:
    QString pgpUserID() const;
    QString cmsDN() const;
Marc Mutz's avatar
Marc Mutz committed
792

Laurent Montel's avatar
Laurent Montel committed
793
794
795
796
797
private Q_SLOTS:
    void slotAdvancedSettingsClicked();
    void slotUpdateResultLabel()
    {
        ui.resultLE->setText(pgp() ? pgpUserID() : cmsDN());
798
        ui.withPassCB->setVisible(pgp());
Laurent Montel's avatar
Laurent Montel committed
799
    }
800

Laurent Montel's avatar
Laurent Montel committed
801
802
803
804
805
806
807
private:
    QVector<Line> lineList;
    QList<QWidget *> dynamicWidgets;
    QMap<QString, QString> savedValues;
    AdvancedSettingsDialog dialog;
    Ui_EnterDetailsPage ui;
};
808

Laurent Montel's avatar
Laurent Montel committed
809
810
811
812
class KeyCreationPage : public WizardPage
{
    Q_OBJECT
public:
Laurent Montel's avatar
Laurent Montel committed
813
    explicit KeyCreationPage(QWidget *p = nullptr)
Laurent Montel's avatar
Laurent Montel committed
814
815
816
817
818
819
        : WizardPage(p),
          ui()
    {
        ui.setupUi(this);
    }

820
    bool isComplete() const override
Laurent Montel's avatar
Laurent Montel committed
821
822
823
824
    {
        return !job;
    }

825
    void initializePage() override {
Laurent Montel's avatar
Laurent Montel committed
826
827
828
829
830
831
        startJob();
    }

private:
    void startJob()
    {
Andre Heinecke's avatar
Andre Heinecke committed
832
        const auto proto = pgp() ? QGpgME::openpgp() : QGpgME::smime();
Laurent Montel's avatar
Laurent Montel committed
833
834
835
        if (!proto) {
            return;
        }
836
        QGpgME::KeyGenerationJob *const j = proto->keyGenerationJob();
Laurent Montel's avatar
Laurent Montel committed
837
838
        if (!j) {
            return;
839
        }
840
        if (!protectedKey() && pgp()) {
841
842
843
844
            auto ctx = QGpgME::Job::context(j);
            ctx->setPassphraseProvider(&mEmptyPWProvider);
            ctx->setPinentryMode(Context::PinentryLoopback);
        }
Laurent Montel's avatar
Laurent Montel committed
845
846
        connect(j, &QGpgME::KeyGenerationJob::result,
                this, &KeyCreationPage::slotResult);
Laurent Montel's avatar
Laurent Montel committed
847
        if (const Error err = j->start(createGnupgKeyParms()))
848
            setField(QStringLiteral("error"), i18n("Could not start key pair creation: %1",
Laurent Montel's avatar
Laurent Montel committed
849
                                                   QString::fromLocal8Bit(err.asString())));
Laurent Montel's avatar
Laurent Montel committed
850
851
852
853
854
855
856
        else {
            job = j;
        }
    }
    QStringList keyUsages() const;
    QStringList subkeyUsages() const;
    QString createGnupgKeyParms() const;
857
    EmptyPassphraseProvider mEmptyPWProvider;
858

Laurent Montel's avatar
Laurent Montel committed
859
860
861
private Q_SLOTS:
    void slotResult(const GpgME::KeyGenerationResult &result, const QByteArray &request, const QString &auditLog)
    {
Laurent Montel's avatar
Laurent Montel committed
862
        Q_UNUSED(auditLog)
Andre Heinecke's avatar
Andre Heinecke committed
863
        if (result.error().code() || (pgp() && !result.fingerprint())) {
Laurent Montel's avatar
Laurent Montel committed
864
            setField(QStringLiteral("error"), result.error().isCanceled()
Laurent Montel's avatar
Laurent Montel committed
865
                     ? i18n("Operation canceled.")
866
                     : i18n("Could not create key pair: %1",
Laurent Montel's avatar
Laurent Montel committed
867
                            QString::fromLocal8Bit(result.error().asString())));
868
869
            setField(QStringLiteral("url"), QString());
            setField(QStringLiteral("result"), QString());
Laurent Montel's avatar
Laurent Montel committed
870
        } else if (pgp()) {
871
872
            setField(QStringLiteral("error"), QString());
            setField(QStringLiteral("url"), QString());
873
            setField(QStringLiteral("result"), i18n("Key pair created successfully.\n"
Laurent Montel's avatar
Laurent Montel committed
874
                                                    "Fingerprint: %1", QLatin1String(result.fingerprint())));
Laurent Montel's avatar
Laurent Montel committed
875
        } else {
876
            QFile file(tmpDir().absoluteFilePath(QStringLiteral("request.p10")));
877

Laurent Montel's avatar
Laurent Montel committed
878
            if (!file.open(QIODevice::WriteOnly)) {
879
                setField(QStringLiteral("error"), i18n("Could not write output file %1: %2",
Laurent Montel's avatar
Laurent Montel committed
880
                                                       file.fileName(), file.errorString()));
881
882
                setField(QStringLiteral("url"), QString());
                setField(QStringLiteral("result"), QString());
883
            } else {
Laurent Montel's avatar
Laurent Montel committed
884
                file.write(request);
885
886
                setField(QStringLiteral("error"), QString());
                setField(QStringLiteral("url"), QUrl::fromLocalFile(file.fileName()).toString());
887
                setField(QStringLiteral("result"), i18n("Key pair created successfully."));
888
            }
Laurent Montel's avatar
Laurent Montel committed
889
        }
890
        // Ensure that we have the key in the keycache
Andre Heinecke's avatar
Andre Heinecke committed
891
        if (pgp() && !result.error().code() && result.fingerprint()) {
892
893
894
895
            auto ctx = Context::createForProtocol(OpenPGP);
            if (ctx) {
                // Check is pretty useless something very buggy in that case.
                Error e;
Andre Heinecke's avatar
Andre Heinecke committed
896
897
898
899
900
901
                const auto key = ctx->key(result.fingerprint(), e, true);
                if (!key.isNull()) {
                    KeyCache::mutableInstance()->insert(key);
                } else {
                    qCDebug(KLEOPATRA_LOG) << "Failed to find newly generated key.";
                }
902
903
                delete ctx;
            }
904
        }
Andre Heinecke's avatar
Andre Heinecke committed
905
906
        setField(QStringLiteral("fingerprint"), result.fingerprint() ?
                 QString::fromLatin1(result.fingerprint()) : QString());
Laurent Montel's avatar
Laurent Montel committed
907
        job = nullptr;
Laurent Montel's avatar
Laurent Montel committed
908
        Q_EMIT completeChanged();
909
910
911
912
913
914
915
916
917
918
919
920
        const KConfigGroup config(KSharedConfig::openConfig(), "CertificateCreationWizard");
        if (config.readEntry("SkipResultPage", false)) {
            if (result.fingerprint()) {
                KleopatraApplication::instance()->slotActivateRequested(QStringList() <<
                       QStringLiteral("kleopatra") << QStringLiteral("--query") << QLatin1String(result.fingerprint()), QString());
                QMetaObject::invokeMethod(wizard(), "close", Qt::QueuedConnection);
            } else {
                QMetaObject::invokeMethod(wizard(), "next", Qt::QueuedConnection);
            }
        } else {
            QMetaObject::invokeMethod(wizard(), "next", Qt::QueuedConnection);
        }
Laurent Montel's avatar
Laurent Montel committed
921
922
923
    }

private:
924
    QPointer<QGpgME::KeyGenerationJob> job;
Laurent Montel's avatar
Laurent Montel committed
925
926
    Ui_KeyCreationPage ui;
};
927

Laurent Montel's avatar
Laurent Montel committed
928
929
930
931
class ResultPage : public WizardPage
{
    Q_OBJECT
public:
Laurent Montel's avatar
Laurent Montel committed
932
    explicit ResultPage(QWidget *p = nullptr)
Laurent Montel's avatar
Laurent Montel committed
933
934
935
936
937
938
939
        : WizardPage(p),
          initialized(false),
          successfullyCreatedSigningCertificate(false),
          successfullyCreatedEncryptionCertificate(false),
          ui()
    {
        ui.setupUi(this);
Laurent Montel's avatar
Laurent Montel committed
940
        ui.dragQueen->setPixmap(QIcon::fromTheme(QStringLiteral("kleopatra")).pixmap(64, 64));
Laurent Montel's avatar
Laurent Montel committed
941
942
943
        registerField(QStringLiteral("error"),  ui.errorTB,   "plainText");
        registerField(QStringLiteral("result"), ui.resultTB,  "plainText");
        registerField(QStringLiteral("url"),    ui.dragQueen, "url");
Laurent Montel's avatar
Laurent Montel committed
944
945
946
        // hidden field, since QWizard can't deal with non-widget-backed fields...
        QLineEdit *le = new QLineEdit(this);
        le->hide();
Laurent Montel's avatar
Laurent Montel committed
947
        registerField(QStringLiteral("fingerprint"), le);
Laurent Montel's avatar
Laurent Montel committed
948
949
    }

950
    void initializePage() override {
Laurent Montel's avatar
Laurent Montel committed
951
952
        const bool error = isError();

Laurent Montel's avatar
Laurent Montel committed
953
954
        if (error)
        {
Laurent Montel's avatar
Laurent Montel committed
955
956
957
958
959
960
961
962
963
            setTitle(i18nc("@title", "Key Creation Failed"));
            setSubTitle(i18n("Key pair creation failed. Please find details about the failure below."));
        } else {
            setTitle(i18nc("@title", "Key Pair Successfully Created"));
            setSubTitle(i18n("Your new key pair was created successfully. Please find details on the result and some suggested next steps below."));
        }

        ui.resultTB                 ->setVisible(!error);
        ui.errorTB                  ->setVisible(error);
Laurent Montel's avatar
Laurent Montel committed
964
        ui.dragQueen                ->setVisible(!error &&!pgp());
Laurent Montel's avatar
Laurent Montel committed
965
966
967
968
        ui.restartWizardPB          ->setVisible(error);
        ui.nextStepsGB              ->setVisible(!error);
        ui.saveRequestToFilePB      ->setVisible(!pgp());
        ui.makeBackupPB             ->setVisible(pgp());
Laurent Montel's avatar
Laurent Montel committed
969
        ui.createRevocationRequestPB->setVisible(pgp() &&false);     // not implemented
970

Laurent Montel's avatar
Laurent Montel committed
971
972
973
        ui.sendCertificateByEMailPB ->setVisible(pgp());
        ui.sendRequestByEMailPB     ->setVisible(!pgp());
        ui.uploadToKeyserverPB      ->setVisible(pgp());
974

Laurent Montel's avatar
Laurent Montel committed
975
976
        if (!error && !pgp())
        {
Laurent Montel's avatar
Laurent Montel committed
977
978
979
980
981
982
            if (signingAllowed() && !encryptionAllowed()) {
                successfullyCreatedSigningCertificate = true;
            } else if (!signingAllowed() && encryptionAllowed()) {
                successfullyCreatedEncryptionCertificate = true;
            } else {
                successfullyCreatedEncryptionCertificate = successfullyCreatedSigningCertificate = true;
983
            }
Laurent Montel's avatar
Laurent Montel committed
984
        }
985

Laurent Montel's avatar
Laurent Montel committed
986
987
        ui.createSigningCertificatePB->setVisible(successfullyCreatedEncryptionCertificate &&!successfullyCreatedSigningCertificate);
        ui.createEncryptionCertificatePB->setVisible(successfullyCreatedSigningCertificate &&!successfullyCreatedEncryptionCertificate);
988

Laurent Montel's avatar
Laurent Montel committed
989
        setButtonVisible(QWizard::CancelButton, error);
990

Laurent Montel's avatar
Laurent Montel committed
991
        if (!initialized)
992
993
            connect(ui.restartWizardPB, &QAbstractButton::clicked,
                    wizard(), &QWizard::restart);
Laurent Montel's avatar
Laurent Montel committed
994
995
        initialized = true;
    }
996

997
    void cleanupPage() override {
Laurent Montel's avatar
Laurent Montel committed
998
999
        setButtonVisible(QWizard::CancelButton, true);
    }
1000

Laurent Montel's avatar
Laurent Montel committed
1001
1002
    bool isError() const
    {
Laurent Montel's avatar
Laurent Montel committed
1003
        return !ui.errorTB->document()->isEmpty();
Laurent Montel's avatar
Laurent Montel committed
1004
    }