1 /*  commands/authenticatepivcardapplicationcommand.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 "authenticatepivcardapplicationcommand.h"
11 
12 #include "cardcommand_p.h"
13 
14 #include "smartcard/pivcard.h"
15 #include "smartcard/readerstatus.h"
16 
17 #include "dialogs/pivcardapplicationadministrationkeyinputdialog.h"
18 
19 #include <KLocalizedString>
20 
21 #include <gpgme++/error.h>
22 
23 #include <gpg-error.h>
24 #if GPG_ERROR_VERSION_NUMBER >= 0x12400 // 1.36
25 # define GPG_ERROR_HAS_BAD_AUTH
26 #endif
27 
28 #include "kleopatra_debug.h"
29 
30 using namespace Kleo;
31 using namespace Kleo::Commands;
32 using namespace Kleo::Dialogs;
33 using namespace Kleo::SmartCard;
34 using namespace GpgME;
35 
36 class AuthenticatePIVCardApplicationCommand::Private : public CardCommand::Private
37 {
38     friend class ::Kleo::Commands::AuthenticatePIVCardApplicationCommand;
q_func() const39     AuthenticatePIVCardApplicationCommand *q_func() const
40     {
41         return static_cast<AuthenticatePIVCardApplicationCommand *>(q);
42     }
43 public:
44     explicit Private(AuthenticatePIVCardApplicationCommand *qq, const std::string &serialNumber, QWidget *p);
45     ~Private() override;
46 
47     void init();
48 
49 private:
50     void slotResult(const Error &err);
51     void slotDialogAccepted();
52     void slotDialogRejected();
53 
54 private:
55     void authenticate(const QByteArray& adminKey);
56     void retryAskingForKey();
57     void ensureDialogCreated();
58 
59 private:
60     QString prompt;
61     QPointer<PIVCardApplicationAdministrationKeyInputDialog> dialog;
62 };
63 
d_func()64 AuthenticatePIVCardApplicationCommand::Private *AuthenticatePIVCardApplicationCommand::d_func()
65 {
66     return static_cast<Private *>(d.get());
67 }
d_func() const68 const AuthenticatePIVCardApplicationCommand::Private *AuthenticatePIVCardApplicationCommand::d_func() const
69 {
70     return static_cast<const Private *>(d.get());
71 }
72 
73 #define d d_func()
74 #define q q_func()
75 
Private(AuthenticatePIVCardApplicationCommand * qq,const std::string & serialNumber,QWidget * p)76 AuthenticatePIVCardApplicationCommand::Private::Private(AuthenticatePIVCardApplicationCommand *qq, const std::string &serialNumber, QWidget *p)
77     : CardCommand::Private(qq, serialNumber, p)
78     , dialog()
79 {
80 }
81 
~Private()82 AuthenticatePIVCardApplicationCommand::Private::~Private()
83 {
84     qCDebug(KLEOPATRA_LOG) << "AuthenticatePIVCardApplicationCommand::Private::~Private()";
85 }
86 
AuthenticatePIVCardApplicationCommand(const std::string & serialNumber,QWidget * p)87 AuthenticatePIVCardApplicationCommand::AuthenticatePIVCardApplicationCommand(const std::string &serialNumber, QWidget *p)
88     : CardCommand(new Private(this, serialNumber, p))
89 {
90     d->init();
91 }
92 
init()93 void AuthenticatePIVCardApplicationCommand::Private::init()
94 {
95 }
96 
~AuthenticatePIVCardApplicationCommand()97 AuthenticatePIVCardApplicationCommand::~AuthenticatePIVCardApplicationCommand()
98 {
99     qCDebug(KLEOPATRA_LOG) << "AuthenticatePIVCardApplicationCommand::~AuthenticatePIVCardApplicationCommand()";
100 }
101 
setPrompt(const QString & prompt)102 void AuthenticatePIVCardApplicationCommand::setPrompt(const QString& prompt)
103 {
104     d->prompt = prompt;
105 }
106 
doStart()107 void AuthenticatePIVCardApplicationCommand::doStart()
108 {
109     qCDebug(KLEOPATRA_LOG) << "AuthenticatePIVCardApplicationCommand::doStart()";
110 
111     // at first, try to authenticate using the default application administration key
112     d->authenticate(QByteArray::fromHex("010203040506070801020304050607080102030405060708"));
113 }
114 
doCancel()115 void AuthenticatePIVCardApplicationCommand::doCancel()
116 {
117 }
118 
authenticate(const QByteArray & adminKey)119 void AuthenticatePIVCardApplicationCommand::Private::authenticate(const QByteArray& adminKey)
120 {
121     qCDebug(KLEOPATRA_LOG) << "AuthenticatePIVCardApplicationCommand::authenticate()";
122 
123     const auto pivCard = SmartCard::ReaderStatus::instance()->getCard<PIVCard>(serialNumber());
124     if (!pivCard) {
125         error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(serialNumber())));
126         finished();
127         return;
128     }
129 
130     const QByteArray plusPercentEncodedAdminKey = adminKey.toPercentEncoding().replace(' ', '+');
131     const QByteArray command = QByteArray("SCD SETATTR AUTH-ADM-KEY ") + plusPercentEncodedAdminKey;
132     ReaderStatus::mutableInstance()->startSimpleTransaction(pivCard, command, q, "slotResult");
133 }
134 
slotResult(const Error & err)135 void AuthenticatePIVCardApplicationCommand::Private::slotResult(const Error &err)
136 {
137     qCDebug(KLEOPATRA_LOG) << "AuthenticatePIVCardApplicationCommand::slotResult():"
138                            << err.asString() << "(" << err.code() << ")";
139     if (err.isCanceled()) {
140         canceled();
141         return;
142     }
143     if (err) {
144 #ifdef GPG_ERROR_HAS_BAD_AUTH
145         if (err.code() == GPG_ERR_BAD_AUTH) {
146             retryAskingForKey();
147             return;
148         }
149 #endif
150         error(i18nc("@info", "Authenticating to the card failed: %1", QString::fromLatin1(err.asString())),
151               i18nc("@title", "Error"));
152     }
153     finished();
154 }
155 
retryAskingForKey()156 void AuthenticatePIVCardApplicationCommand::Private::retryAskingForKey()
157 {
158     ensureDialogCreated();
159     Q_ASSERT(dialog);
160     dialog->show();
161 }
162 
ensureDialogCreated()163 void AuthenticatePIVCardApplicationCommand::Private::ensureDialogCreated()
164 {
165     if (dialog) {
166         return;
167     }
168 
169     dialog = new PIVCardApplicationAdministrationKeyInputDialog(parentWidgetOrView());
170     dialog->setAttribute(Qt::WA_DeleteOnClose);
171     dialog->setLabelText(prompt.isEmpty() ?
172                          i18n("Please enter the PIV Card Application Administration Key in hex-encoded form.") :
173                          prompt);
174 
175     connect(dialog, SIGNAL(accepted()), q, SLOT(slotDialogAccepted()));
176     connect(dialog, SIGNAL(rejected()), q, SLOT(slotDialogRejected()));
177 }
178 
slotDialogAccepted()179 void AuthenticatePIVCardApplicationCommand::Private::slotDialogAccepted()
180 {
181     authenticate(dialog->adminKey());
182 }
183 
slotDialogRejected()184 void AuthenticatePIVCardApplicationCommand::Private::slotDialogRejected()
185 {
186     canceled();
187 }
188 
189 #undef d
190 #undef q
191 
192 #include "moc_authenticatepivcardapplicationcommand.cpp"
193