File indexing completed on 2024-06-23 05:13:44

0001 /*  commands/pivgeneratecardkeycommand.cpp
0002 
0003     This file is part of Kleopatra, the KDE keymanager
0004     SPDX-FileCopyrightText: 2020 g10 Code GmbH
0005     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "pivgeneratecardkeycommand.h"
0011 
0012 #include "cardcommand_p.h"
0013 
0014 #include "smartcard/algorithminfo.h"
0015 #include "smartcard/pivcard.h"
0016 #include "smartcard/readerstatus.h"
0017 
0018 #include "commands/authenticatepivcardapplicationcommand.h"
0019 
0020 #include "dialogs/gencardkeydialog.h"
0021 
0022 #include <Libkleo/Formatting>
0023 
0024 #include <KLocalizedString>
0025 
0026 #include <gpgme++/error.h>
0027 
0028 #include <gpg-error.h>
0029 #if GPG_ERROR_VERSION_NUMBER >= 0x12400 // 1.36
0030 #define GPG_ERROR_HAS_NO_AUTH
0031 #endif
0032 
0033 #include "kleopatra_debug.h"
0034 
0035 using namespace Kleo;
0036 using namespace Kleo::Commands;
0037 using namespace Kleo::SmartCard;
0038 using namespace GpgME;
0039 
0040 class PIVGenerateCardKeyCommand::Private : public CardCommand::Private
0041 {
0042     friend class ::Kleo::Commands::PIVGenerateCardKeyCommand;
0043     PIVGenerateCardKeyCommand *q_func() const
0044     {
0045         return static_cast<PIVGenerateCardKeyCommand *>(q);
0046     }
0047 
0048 public:
0049     explicit Private(PIVGenerateCardKeyCommand *qq, const std::string &serialNumber, QWidget *p);
0050     ~Private() override;
0051 
0052     void init();
0053 
0054 private:
0055     void slotDialogAccepted();
0056     void slotDialogRejected();
0057     void slotResult(const Error &err);
0058 
0059 private:
0060     void authenticate();
0061     void authenticationFinished();
0062     void authenticationCanceled();
0063     void generateKey();
0064     void ensureDialogCreated();
0065 
0066 private:
0067     std::string keyRef;
0068     bool overwriteExistingKey = false;
0069     std::string algorithm;
0070     QPointer<GenCardKeyDialog> dialog;
0071     bool hasBeenCanceled = false;
0072 };
0073 
0074 PIVGenerateCardKeyCommand::Private *PIVGenerateCardKeyCommand::d_func()
0075 {
0076     return static_cast<Private *>(d.get());
0077 }
0078 const PIVGenerateCardKeyCommand::Private *PIVGenerateCardKeyCommand::d_func() const
0079 {
0080     return static_cast<const Private *>(d.get());
0081 }
0082 
0083 #define d d_func()
0084 #define q q_func()
0085 
0086 PIVGenerateCardKeyCommand::Private::Private(PIVGenerateCardKeyCommand *qq, const std::string &serialNumber, QWidget *p)
0087     : CardCommand::Private(qq, serialNumber, p)
0088     , dialog()
0089 {
0090 }
0091 
0092 PIVGenerateCardKeyCommand::Private::~Private()
0093 {
0094     qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::Private::~Private()";
0095 }
0096 
0097 PIVGenerateCardKeyCommand::PIVGenerateCardKeyCommand(const std::string &serialNumber, QWidget *p)
0098     : CardCommand(new Private(this, serialNumber, p))
0099 {
0100     d->init();
0101 }
0102 
0103 void PIVGenerateCardKeyCommand::Private::init()
0104 {
0105 }
0106 
0107 PIVGenerateCardKeyCommand::~PIVGenerateCardKeyCommand()
0108 {
0109     qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::~PIVGenerateCardKeyCommand()";
0110 }
0111 
0112 void PIVGenerateCardKeyCommand::setKeyRef(const std::string &keyRef)
0113 {
0114     d->keyRef = keyRef;
0115 }
0116 
0117 void PIVGenerateCardKeyCommand::doStart()
0118 {
0119     qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::doStart()";
0120 
0121     // check if key exists
0122     auto pivCard = ReaderStatus::instance()->getCard<PIVCard>(d->serialNumber());
0123     if (!pivCard) {
0124         d->error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(d->serialNumber())));
0125         d->finished();
0126         return;
0127     }
0128 
0129     auto existingKey = pivCard->keyInfo(d->keyRef).grip;
0130     if (!existingKey.empty()) {
0131         const QString warningText = i18nc("@info",
0132                                           "<p>This card already contains a key in this slot. Continuing will <b>overwrite</b> that key.</p>"
0133                                           "<p>If there is no backup the existing key will be irrecoverably lost.</p>")
0134             + i18n("The existing key has the ID:") + QStringLiteral("<pre>%1</pre>").arg(QString::fromStdString(existingKey))
0135             + (d->keyRef == PIVCard::keyManagementKeyRef() ? i18n("It will no longer be possible to decrypt past communication encrypted for the existing key.")
0136                                                            : QString());
0137         const auto choice = KMessageBox::warningContinueCancel(d->parentWidgetOrView(),
0138                                                                warningText,
0139                                                                i18nc("@title:window", "Overwrite existing key"),
0140                                                                KStandardGuiItem::cont(),
0141                                                                KStandardGuiItem::cancel(),
0142                                                                QString(),
0143                                                                KMessageBox::Notify | KMessageBox::Dangerous);
0144         if (choice != KMessageBox::Continue) {
0145             d->finished();
0146             return;
0147         }
0148         d->overwriteExistingKey = true;
0149     }
0150 
0151     d->ensureDialogCreated();
0152     Q_ASSERT(d->dialog);
0153     d->dialog->show();
0154 }
0155 
0156 void PIVGenerateCardKeyCommand::doCancel()
0157 {
0158 }
0159 
0160 void PIVGenerateCardKeyCommand::Private::authenticate()
0161 {
0162     qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::authenticate()";
0163 
0164     auto cmd = new AuthenticatePIVCardApplicationCommand(serialNumber(), parentWidgetOrView());
0165     cmd->setAutoResetCardToOpenPGP(false);
0166     connect(cmd, &AuthenticatePIVCardApplicationCommand::finished, q, [this]() {
0167         authenticationFinished();
0168     });
0169     connect(cmd, &AuthenticatePIVCardApplicationCommand::canceled, q, [this]() {
0170         authenticationCanceled();
0171     });
0172     cmd->start();
0173 }
0174 
0175 void PIVGenerateCardKeyCommand::Private::authenticationFinished()
0176 {
0177     qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::authenticationFinished()";
0178     if (!hasBeenCanceled) {
0179         generateKey();
0180     }
0181 }
0182 
0183 void PIVGenerateCardKeyCommand::Private::authenticationCanceled()
0184 {
0185     qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::authenticationCanceled()";
0186     hasBeenCanceled = true;
0187     canceled();
0188 }
0189 
0190 void PIVGenerateCardKeyCommand::Private::generateKey()
0191 {
0192     qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::generateKey()";
0193 
0194     auto pivCard = ReaderStatus::instance()->getCard<PIVCard>(serialNumber());
0195     if (!pivCard) {
0196         error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(serialNumber())));
0197         finished();
0198         return;
0199     }
0200 
0201     QByteArrayList command;
0202     command << "SCD GENKEY";
0203     if (overwriteExistingKey) {
0204         command << "--force";
0205     }
0206     if (!algorithm.empty()) {
0207         command << "--algo=" + QByteArray::fromStdString(algorithm);
0208     }
0209     command << "--" << QByteArray::fromStdString(keyRef);
0210     ReaderStatus::mutableInstance()->startSimpleTransaction(pivCard, command.join(' '), q, [this](const GpgME::Error &err) {
0211         slotResult(err);
0212     });
0213 }
0214 
0215 void PIVGenerateCardKeyCommand::Private::slotResult(const GpgME::Error &err)
0216 {
0217     qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::slotResult():" << Formatting::errorAsString(err) << "(" << err.code() << ")";
0218     if (err) {
0219 #ifdef GPG_ERROR_HAS_NO_AUTH
0220         if (err.code() == GPG_ERR_NO_AUTH) {
0221             authenticate();
0222             return;
0223         }
0224 #endif
0225 
0226         error(i18nc("@info", "Generating key failed: %1", Formatting::errorAsString(err)));
0227     } else if (!err.isCanceled()) {
0228         success(i18nc("@info", "Key successfully generated."));
0229         ReaderStatus::mutableInstance()->updateStatus();
0230     }
0231     finished();
0232 }
0233 
0234 void PIVGenerateCardKeyCommand::Private::slotDialogAccepted()
0235 {
0236     algorithm = dialog->getKeyParams().algorithm;
0237 
0238     // assume that we are already authenticated to the card
0239     generateKey();
0240 }
0241 
0242 void PIVGenerateCardKeyCommand::Private::slotDialogRejected()
0243 {
0244     finished();
0245 }
0246 
0247 void PIVGenerateCardKeyCommand::Private::ensureDialogCreated()
0248 {
0249     if (dialog) {
0250         return;
0251     }
0252 
0253     dialog = new GenCardKeyDialog(GenCardKeyDialog::KeyAlgorithm, parentWidgetOrView());
0254     dialog->setAttribute(Qt::WA_DeleteOnClose);
0255     dialog->setSupportedAlgorithms(PIVCard::supportedAlgorithms(keyRef), "rsa2048");
0256 
0257     connect(dialog, &QDialog::accepted, q, [this]() {
0258         slotDialogAccepted();
0259     });
0260     connect(dialog, &QDialog::rejected, q, [this]() {
0261         slotDialogRejected();
0262     });
0263 }
0264 
0265 #undef d
0266 #undef q
0267 
0268 #include "moc_pivgeneratecardkeycommand.cpp"