1 /*  commands/pivgeneratecardkeycommand.cpp
2 
3     This file is part of Kleopatra, the KDE keymanager
4     SPDX-FileCopyrightText: 2020 g10 Code GmbH
5     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
6 
7     SPDX-License-Identifier: GPL-2.0-or-later
8 */
9 
10 #include "pivgeneratecardkeycommand.h"
11 
12 #include "cardcommand_p.h"
13 
14 #include "smartcard/pivcard.h"
15 #include "smartcard/readerstatus.h"
16 
17 #include "commands/authenticatepivcardapplicationcommand.h"
18 
19 #include "dialogs/gencardkeydialog.h"
20 
21 #include <KLocalizedString>
22 
23 #include <gpgme++/error.h>
24 
25 #include <gpg-error.h>
26 #if GPG_ERROR_VERSION_NUMBER >= 0x12400 // 1.36
27 # define GPG_ERROR_HAS_NO_AUTH
28 #endif
29 
30 #include "kleopatra_debug.h"
31 
32 using namespace Kleo;
33 using namespace Kleo::Commands;
34 using namespace Kleo::SmartCard;
35 using namespace GpgME;
36 
37 class PIVGenerateCardKeyCommand::Private : public CardCommand::Private
38 {
39     friend class ::Kleo::Commands::PIVGenerateCardKeyCommand;
q_func() const40     PIVGenerateCardKeyCommand *q_func() const
41     {
42         return static_cast<PIVGenerateCardKeyCommand *>(q);
43     }
44 public:
45     explicit Private(PIVGenerateCardKeyCommand *qq, const std::string &serialNumber, QWidget *p);
46     ~Private() override;
47 
48     void init();
49 
50 private:
51     void slotDialogAccepted();
52     void slotDialogRejected();
53     void slotResult(const Error &err);
54 
55 private:
56     void authenticate();
57     void authenticationFinished();
58     void authenticationCanceled();
59     void generateKey();
60     void ensureDialogCreated();
61 
62 private:
63     std::string keyRef;
64     bool overwriteExistingKey = false;
65     std::string algorithm;
66     QPointer<GenCardKeyDialog> dialog;
67     bool hasBeenCanceled = false;
68 };
69 
d_func()70 PIVGenerateCardKeyCommand::Private *PIVGenerateCardKeyCommand::d_func()
71 {
72     return static_cast<Private *>(d.get());
73 }
d_func() const74 const PIVGenerateCardKeyCommand::Private *PIVGenerateCardKeyCommand::d_func() const
75 {
76     return static_cast<const Private *>(d.get());
77 }
78 
79 #define d d_func()
80 #define q q_func()
81 
Private(PIVGenerateCardKeyCommand * qq,const std::string & serialNumber,QWidget * p)82 PIVGenerateCardKeyCommand::Private::Private(PIVGenerateCardKeyCommand *qq, const std::string &serialNumber, QWidget *p)
83     : CardCommand::Private(qq, serialNumber, p)
84     , dialog()
85 {
86 }
87 
~Private()88 PIVGenerateCardKeyCommand::Private::~Private()
89 {
90     qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::Private::~Private()";
91 }
92 
PIVGenerateCardKeyCommand(const std::string & serialNumber,QWidget * p)93 PIVGenerateCardKeyCommand::PIVGenerateCardKeyCommand(const std::string &serialNumber, QWidget *p)
94     : CardCommand(new Private(this, serialNumber, p))
95 {
96     d->init();
97 }
98 
init()99 void PIVGenerateCardKeyCommand::Private::init()
100 {
101 }
102 
~PIVGenerateCardKeyCommand()103 PIVGenerateCardKeyCommand::~PIVGenerateCardKeyCommand()
104 {
105     qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::~PIVGenerateCardKeyCommand()";
106 }
107 
setKeyRef(const std::string & keyRef)108 void PIVGenerateCardKeyCommand::setKeyRef(const std::string &keyRef)
109 {
110     d->keyRef = keyRef;
111 }
112 
doStart()113 void PIVGenerateCardKeyCommand::doStart()
114 {
115     qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::doStart()";
116 
117     // check if key exists
118     auto pivCard = ReaderStatus::instance()->getCard<PIVCard>(d->serialNumber());
119     if (!pivCard) {
120         d->error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(d->serialNumber())));
121         d->finished();
122         return;
123     }
124 
125     auto existingKey = pivCard->keyInfo(d->keyRef).grip;
126     if (!existingKey.empty()) {
127         const QString warningText = i18nc("@info",
128             "<p>This card already contains a key in this slot. Continuing will <b>overwrite</b> that key.</p>"
129             "<p>If there is no backup the existing key will be irrecoverably lost.</p>") +
130             i18n("The existing key has the ID:") + QStringLiteral("<pre>%1</pre>").arg(QString::fromStdString(existingKey)) +
131             (d->keyRef == PIVCard::keyManagementKeyRef() ?
132              i18n("It will no longer be possible to decrypt past communication encrypted for the existing key.") :
133              QString());
134         const auto choice = KMessageBox::warningContinueCancel(d->parentWidgetOrView(), warningText,
135             i18nc("@title:window", "Overwrite existing key"),
136             KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous);
137         if (choice != KMessageBox::Continue) {
138             d->finished();
139             return;
140         }
141         d->overwriteExistingKey = true;
142     }
143 
144     d->ensureDialogCreated();
145     Q_ASSERT(d->dialog);
146     d->dialog->show();
147 }
148 
doCancel()149 void PIVGenerateCardKeyCommand::doCancel()
150 {
151 }
152 
authenticate()153 void PIVGenerateCardKeyCommand::Private::authenticate()
154 {
155     qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::authenticate()";
156 
157     auto cmd = new AuthenticatePIVCardApplicationCommand(serialNumber(), parentWidgetOrView());
158     connect(cmd, &AuthenticatePIVCardApplicationCommand::finished,
159             q, [this]() { authenticationFinished(); });
160     connect(cmd, &AuthenticatePIVCardApplicationCommand::canceled,
161             q, [this]() { authenticationCanceled(); });
162     cmd->start();
163 }
164 
authenticationFinished()165 void PIVGenerateCardKeyCommand::Private::authenticationFinished()
166 {
167     qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::authenticationFinished()";
168     if (!hasBeenCanceled) {
169         generateKey();
170     }
171 }
172 
authenticationCanceled()173 void PIVGenerateCardKeyCommand::Private::authenticationCanceled()
174 {
175     qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::authenticationCanceled()";
176     hasBeenCanceled = true;
177     canceled();
178 }
179 
generateKey()180 void PIVGenerateCardKeyCommand::Private::generateKey()
181 {
182     qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::generateKey()";
183 
184     auto pivCard = ReaderStatus::instance()->getCard<PIVCard>(serialNumber());
185     if (!pivCard) {
186         error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(serialNumber())));
187         finished();
188         return;
189     }
190 
191     QByteArrayList command;
192     command << "SCD GENKEY";
193     if (overwriteExistingKey) {
194         command << "--force";
195     }
196     if (!algorithm.empty()) {
197         command << "--algo=" + QByteArray::fromStdString(algorithm);
198     }
199     command << "--" << QByteArray::fromStdString(keyRef);
200     ReaderStatus::mutableInstance()->startSimpleTransaction(pivCard, command.join(' '), q, "slotResult");
201 }
202 
slotResult(const GpgME::Error & err)203 void PIVGenerateCardKeyCommand::Private::slotResult(const GpgME::Error& err)
204 {
205     qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::slotResult():"
206                            << err.asString() << "(" << err.code() << ")";
207     if (err) {
208 #ifdef GPG_ERROR_HAS_NO_AUTH
209         if (err.code() == GPG_ERR_NO_AUTH) {
210             authenticate();
211             return;
212         }
213 #endif
214 
215         error(i18nc("@info", "Generating key failed: %1", QString::fromLatin1(err.asString())),
216               i18nc("@title", "Error"));
217     } else if (!err.isCanceled()) {
218         information(i18nc("@info", "Key successfully generated."), i18nc("@title", "Success"));
219         ReaderStatus::mutableInstance()->updateStatus();
220     }
221     finished();
222 }
223 
slotDialogAccepted()224 void PIVGenerateCardKeyCommand::Private::slotDialogAccepted()
225 {
226     algorithm = dialog->getKeyParams().algorithm;
227 
228     // assume that we are already authenticated to the card
229     generateKey();
230 }
231 
slotDialogRejected()232 void PIVGenerateCardKeyCommand::Private::slotDialogRejected()
233 {
234     finished();
235 }
236 
ensureDialogCreated()237 void PIVGenerateCardKeyCommand::Private::ensureDialogCreated()
238 {
239     if (dialog) {
240         return;
241     }
242 
243     dialog = new GenCardKeyDialog(GenCardKeyDialog::KeyAlgorithm, parentWidgetOrView());
244     dialog->setAttribute(Qt::WA_DeleteOnClose);
245     dialog->setSupportedAlgorithms(PIVCard::supportedAlgorithms(keyRef), "rsa2048");
246 
247     connect(dialog, SIGNAL(accepted()), q, SLOT(slotDialogAccepted()));
248     connect(dialog, SIGNAL(rejected()), q, SLOT(slotDialogRejected()));
249 }
250 
251 #undef d
252 #undef q
253 
254 #include "moc_pivgeneratecardkeycommand.cpp"
255