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"