File indexing completed on 2024-06-23 05:14:09

0001 /* -*- mode: c++; c-basic-offset:4 -*-
0002     newcertificatewizard/keycreationpage.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 "keycreationpage_p.h"
0017 
0018 #include "keyalgo_p.h"
0019 
0020 #include "kleopatraapplication.h"
0021 
0022 #include "utils/keyparameters.h"
0023 
0024 #include <Libkleo/Formatting>
0025 #include <Libkleo/KeyCache>
0026 #include <Libkleo/KeyUsage>
0027 
0028 #include <KConfigGroup>
0029 #include <KLocalizedString>
0030 #include <KSharedConfig>
0031 
0032 #include <QGpgME/KeyGenerationJob>
0033 #include <QGpgME/Protocol>
0034 
0035 #include <QLabel>
0036 #include <QUrl>
0037 #include <QVBoxLayout>
0038 
0039 #include <gpgme++/context.h>
0040 #include <gpgme++/keygenerationresult.h>
0041 
0042 #include "kleopatra_debug.h"
0043 
0044 using namespace Kleo;
0045 using namespace Kleo::NewCertificateUi;
0046 using namespace GpgME;
0047 
0048 struct KeyCreationPage::UI {
0049     UI(QWizardPage *parent)
0050     {
0051         parent->setTitle(i18nc("@title", "Creating Key Pair..."));
0052 
0053         auto mainLayout = new QVBoxLayout{parent};
0054 
0055         auto label = new QLabel{i18n("The process of creating a key requires large amounts of random numbers. This may require several minutes..."), parent};
0056         label->setWordWrap(true);
0057         mainLayout->addWidget(label);
0058     }
0059 };
0060 
0061 KeyCreationPage::KeyCreationPage(QWidget *p)
0062     : WizardPage{p}
0063     , ui{new UI{this}}
0064 {
0065     setObjectName(QString::fromUtf8("Kleo__NewCertificateUi__KeyCreationPage"));
0066 }
0067 
0068 KeyCreationPage::~KeyCreationPage() = default;
0069 
0070 bool KeyCreationPage::isComplete() const
0071 {
0072     return !job;
0073 }
0074 
0075 void KeyCreationPage::initializePage()
0076 {
0077     startJob();
0078 }
0079 
0080 void KeyCreationPage::startJob()
0081 {
0082     const auto proto = pgp() ? QGpgME::openpgp() : QGpgME::smime();
0083     if (!proto) {
0084         return;
0085     }
0086     QGpgME::KeyGenerationJob *const j = proto->keyGenerationJob();
0087     if (!j) {
0088         return;
0089     }
0090     if (!protectedKey() && pgp()) {
0091         auto ctx = QGpgME::Job::context(j);
0092         ctx->setPassphraseProvider(&mEmptyPassphraseProvider);
0093         ctx->setPinentryMode(Context::PinentryLoopback);
0094     }
0095     connect(j, &QGpgME::KeyGenerationJob::result, this, &KeyCreationPage::slotResult);
0096     if (const Error err = j->start(createGnupgKeyParms()))
0097         setField(QStringLiteral("error"), i18n("Could not start key pair creation: %1", Formatting::errorAsString(err)));
0098     else {
0099         job = j;
0100     }
0101 }
0102 
0103 KeyUsage KeyCreationPage::keyUsage() const
0104 {
0105     KeyUsage usage;
0106     if (signingAllowed()) {
0107         usage.setCanSign(true);
0108     }
0109     if (encryptionAllowed() && !is_ecdh(subkeyType()) && !is_dsa(keyType()) && !is_rsa(subkeyType())) {
0110         usage.setCanEncrypt(true);
0111     }
0112     if (authenticationAllowed()) {
0113         usage.setCanAuthenticate(true);
0114     }
0115     if (!usage.value() && certificationAllowed()) {
0116         /* Empty usages cause an error so we need to
0117          * add at least certify if nothing else is selected */
0118         usage.setCanCertify(true);
0119     }
0120     return usage;
0121 }
0122 
0123 KeyUsage KeyCreationPage::subkeyUsage() const
0124 {
0125     KeyUsage usage;
0126     if (encryptionAllowed() && (is_dsa(keyType()) || is_rsa(subkeyType()) || is_ecdh(subkeyType()))) {
0127         Q_ASSERT(subkeyType());
0128         usage.setCanEncrypt(true);
0129     }
0130     return usage;
0131 }
0132 
0133 QString KeyCreationPage::createGnupgKeyParms() const
0134 {
0135     KeyParameters keyParameters(pgp() ? KeyParameters::OpenPGP : KeyParameters::CMS);
0136 
0137     keyParameters.setKeyType(keyType());
0138     if (is_ecdsa(keyType()) || is_eddsa(keyType())) {
0139         keyParameters.setKeyCurve(keyCurve());
0140     } else if (const unsigned int strength = keyStrength()) {
0141         keyParameters.setKeyLength(strength);
0142     }
0143     keyParameters.setKeyUsage(keyUsage());
0144 
0145     if (subkeyType()) {
0146         keyParameters.setSubkeyType(subkeyType());
0147         if (is_ecdh(subkeyType())) {
0148             keyParameters.setSubkeyCurve(subkeyCurve());
0149         } else if (const unsigned int strength = subkeyStrength()) {
0150             keyParameters.setSubkeyLength(strength);
0151         }
0152         keyParameters.setSubkeyUsage(subkeyUsage());
0153     }
0154 
0155     if (pgp()) {
0156         if (expiryDate().isValid()) {
0157             keyParameters.setExpirationDate(expiryDate());
0158         }
0159         if (!name().isEmpty()) {
0160             keyParameters.setName(name());
0161         }
0162         if (!email().isEmpty()) {
0163             keyParameters.setEmail(email());
0164         }
0165     } else {
0166         keyParameters.setDN(dn());
0167         keyParameters.setEmail(email());
0168         const auto addesses{additionalEMailAddresses()};
0169         for (const QString &email : addesses) {
0170             keyParameters.addEmail(email);
0171         }
0172         const auto dnsN{dnsNames()};
0173         for (const QString &dns : dnsN) {
0174             keyParameters.addDomainName(dns);
0175         }
0176         const auto urisList{uris()};
0177         for (const QString &uri : urisList) {
0178             keyParameters.addURI(uri);
0179         }
0180     }
0181 
0182     const QString result = keyParameters.toString();
0183     qCDebug(KLEOPATRA_LOG) << '\n' << result;
0184     return result;
0185 }
0186 
0187 void KeyCreationPage::slotResult(const GpgME::KeyGenerationResult &result, const QByteArray &request, const QString &auditLog)
0188 {
0189     Q_UNUSED(auditLog)
0190     if (result.error().code() || (pgp() && !result.fingerprint())) {
0191         setField(QStringLiteral("error"),
0192                  result.error().isCanceled() ? i18n("Operation canceled.") : i18n("Could not create key pair: %1", Formatting::errorAsString(result.error())));
0193         setField(QStringLiteral("url"), QString());
0194         setField(QStringLiteral("result"), QString());
0195     } else if (pgp()) {
0196         setField(QStringLiteral("error"), QString());
0197         setField(QStringLiteral("url"), QString());
0198         setField(QStringLiteral("result"),
0199                  i18n("Key pair created successfully.\n"
0200                       "Fingerprint: %1",
0201                       Formatting::prettyID(result.fingerprint())));
0202     } else {
0203         QFile file(tmpDir().absoluteFilePath(QStringLiteral("request.p10")));
0204 
0205         if (!file.open(QIODevice::WriteOnly)) {
0206             setField(QStringLiteral("error"), i18n("Could not write output file %1: %2", file.fileName(), file.errorString()));
0207             setField(QStringLiteral("url"), QString());
0208             setField(QStringLiteral("result"), QString());
0209         } else {
0210             file.write(request);
0211             setField(QStringLiteral("error"), QString());
0212             setField(QStringLiteral("url"), QUrl::fromLocalFile(file.fileName()).toString());
0213             setField(QStringLiteral("result"), i18n("Key pair created successfully."));
0214         }
0215     }
0216     // Ensure that we have the key in the keycache
0217     if (pgp() && !result.error().code() && result.fingerprint()) {
0218         auto ctx = Context::createForProtocol(OpenPGP);
0219         if (ctx) {
0220             // Check is pretty useless something very buggy in that case.
0221             Error e;
0222             const auto key = ctx->key(result.fingerprint(), e, true);
0223             if (!key.isNull()) {
0224                 KeyCache::mutableInstance()->insert(key);
0225             } else {
0226                 qCDebug(KLEOPATRA_LOG) << "Failed to find newly generated key.";
0227             }
0228             delete ctx;
0229         }
0230     }
0231     setField(QStringLiteral("fingerprint"), result.fingerprint() ? QString::fromLatin1(result.fingerprint()) : QString());
0232     job = nullptr;
0233     Q_EMIT completeChanged();
0234     const KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("CertificateCreationWizard"));
0235     if (config.readEntry("SkipResultPage", false)) {
0236         if (result.fingerprint()) {
0237             KleopatraApplication::instance()->slotActivateRequested(QStringList() << QStringLiteral("kleopatra") << QStringLiteral("--query")
0238                                                                                   << QLatin1StringView(result.fingerprint()),
0239                                                                     QString());
0240             QMetaObject::invokeMethod(wizard(), "close", Qt::QueuedConnection);
0241         } else {
0242             QMetaObject::invokeMethod(wizard(), "next", Qt::QueuedConnection);
0243         }
0244     } else {
0245         QMetaObject::invokeMethod(wizard(), "next", Qt::QueuedConnection);
0246     }
0247 }
0248 
0249 #include "moc_keycreationpage_p.cpp"