pivcardwidget.cpp 14.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
/*  view/pivcardwiget.cpp

    This file is part of Kleopatra, the KDE keymanager
    SPDX-FileCopyrightText: 2020 g10 Code GmbH
    SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>

    SPDX-License-Identifier: GPL-2.0-or-later
*/

#include "pivcardwidget.h"

12
13
#include "tooltippreferences.h"

14
#include "commands/certificatetopivcardcommand.h"
15
#include "commands/changepincommand.h"
16
#include "commands/createopenpgpkeyfromcardkeyscommand.h"
17
#include "commands/importcertificatefrompivcardcommand.h"
18
#include "commands/keytocardcommand.h"
19
#include "commands/pivgeneratecardkeycommand.h"
20
#include "commands/setpivcardapplicationadministrationkeycommand.h"
21

22
#include "smartcard/pivcard.h"
23
#include "smartcard/readerstatus.h"
24

25
26
27
28
29
30
#include <Libkleo/Dn>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>

#include <KLocalizedString>

31
32
33
#include <QFrame>
#include <QGridLayout>
#include <QLabel>
34
#include <QPushButton>
35
36
37
#include <QScrollArea>
#include <QVBoxLayout>

38
using namespace GpgME;
39
using namespace Kleo;
40
using namespace Kleo::Commands;
41
42
43
using namespace Kleo::SmartCard;

namespace {
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
static void layoutKeyWidgets(QGridLayout *grid, const QString &keyName, const PIVCardWidget::KeyWidgets &keyWidgets)
{
    int row = grid->rowCount();
    grid->addWidget(new QLabel(keyName), row, 0);
    grid->addWidget(keyWidgets.certificateInfo, row, 1, 1, 2);
    grid->addWidget(keyWidgets.generateButton, row, 3);
    if (keyWidgets.writeKeyButton) {
        grid->addWidget(keyWidgets.writeKeyButton, row, 4);
    }
    row++;
    grid->addWidget(keyWidgets.keyGrip, row, 1);
    grid->addWidget(keyWidgets.keyAlgorithm, row, 2);
    grid->addWidget(keyWidgets.writeCertificateButton, row, 3);
    grid->addWidget(keyWidgets.importCertificateButton, row, 4);
}

static int toolTipOptions()
{
    using namespace Kleo::Formatting;
    static const int validityFlags = Validity | Issuer | ExpiryDates | CertificateUsage;
    static const int ownerFlags = Subject | UserIDs | OwnerTrust;
    static const int detailsFlags = StorageLocation | CertificateType | SerialNumber | Fingerprint;

    const TooltipPreferences prefs;

    int flags = KeyID;
    flags |= prefs.showValidity() ? validityFlags : 0;
    flags |= prefs.showOwnerInformation() ? ownerFlags : 0;
    flags |= prefs.showCertificateDetails() ? detailsFlags : 0;
    return flags;
}
75
}
76

77
78
79
80
81
PIVCardWidget::PIVCardWidget(QWidget *parent)
    : QWidget(parent)
    , mSerialNumber(new QLabel(this))
    , mVersionLabel(new QLabel(this))
    , mKeyForCardKeysButton(new QPushButton(this))
82
{
83
84
    // Set up the scroll area
    auto myLayout = new QVBoxLayout(this);
85
    myLayout->setContentsMargins(0, 0, 0, 0);
86
87
88
89

    auto area = new QScrollArea;
    area->setFrameShape(QFrame::NoFrame);
    area->setWidgetResizable(true);
90
91
    myLayout->addWidget(area);

92
    auto areaWidget = new QWidget;
93
94
    area->setWidget(areaWidget);

95
    auto areaVLay = new QVBoxLayout(areaWidget);
96
97

    auto grid = new QGridLayout;
98
99
    areaVLay->addLayout(grid);
    areaVLay->addStretch(1);
100
101
102

    const int columnCount = 5;
    int row = 0;
103
104
105
106
107

    // Version and Serialnumber
    grid->addWidget(mVersionLabel, row++, 0, 1, 2);
    mVersionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);

108
    grid->addWidget(new QLabel(i18n("Serial number:")), row, 0);
109
110
111
    grid->addWidget(mSerialNumber, row++, 1);
    mSerialNumber->setTextInteractionFlags(Qt::TextBrowserInteraction);

112
113
114
115
116
117
    {
        auto line = new QFrame();
        line->setFrameShape(QFrame::HLine);
        grid->addWidget(line, row++, 0, 1, columnCount);
    }

118
119
120
    // The keys
    grid->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("Keys:"))), row++, 0);

121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
    {
        KeyWidgets keyWidgets = createKeyWidgets(PIVCard::pivAuthenticationKeyRef());
        layoutKeyWidgets(grid, i18n("PIV authentication:"), keyWidgets);
    }
    {
        KeyWidgets keyWidgets = createKeyWidgets(PIVCard::cardAuthenticationKeyRef());
        layoutKeyWidgets(grid, i18n("Card authentication:"), keyWidgets);
    }
    {
        KeyWidgets keyWidgets = createKeyWidgets(PIVCard::digitalSignatureKeyRef());
        layoutKeyWidgets(grid, i18n("Digital signature:"), keyWidgets);
    }
    {
        KeyWidgets keyWidgets = createKeyWidgets(PIVCard::keyManagementKeyRef());
        layoutKeyWidgets(grid, i18n("Key management:"), keyWidgets);
    }
    row = grid->rowCount();
138

139
140
141
142
143
    {
        auto line = new QFrame();
        line->setFrameShape(QFrame::HLine);
        grid->addWidget(line, row++, 0, 1, columnCount);
    }
144

145
146
    auto actionLayout = new QHBoxLayout;

147
148
149
150
151
    mKeyForCardKeysButton->setText(i18nc("@action:button", "Create OpenPGP Key"));
    mKeyForCardKeysButton->setToolTip(i18nc("@info:tooltip", "Create an OpenPGP key for the keys stored on the card."));
    actionLayout->addWidget(mKeyForCardKeysButton);
    connect(mKeyForCardKeysButton, &QPushButton::clicked, this, &PIVCardWidget::createKeyFromCardKeys);

152
    {
153
154
155
        auto button = new QPushButton(i18nc("@action:button", "Change PIN"));
        button->setToolTip(i18nc("@info:tooltip", "Change the PIV Card Application PIN that activates the PIV Card "
                                 "and enables private key operations using the stored keys."));
156
157
158
159
        actionLayout->addWidget(button);
        connect(button, &QPushButton::clicked, this, [this] () { changePin(PIVCard::pinKeyRef()); });
    }
    {
160
161
        auto button = new QPushButton(i18nc("@action:button", "Change PUK"));
        button->setToolTip(i18nc("@info:tooltip", "Change the PIN Unblocking Key that enables a reset of the PIN."));
162
163
164
165
        actionLayout->addWidget(button);
        connect(button, &QPushButton::clicked, this, [this] () { changePin(PIVCard::pukKeyRef()); });
    }
    {
166
167
168
169
        auto button = new QPushButton(i18nc("@action:button", "Change Admin Key"));
        button->setToolTip(i18nc("@info:tooltip", "Change the PIV Card Application Administration Key that is used by the "
                                 "PIV Card Application to authenticate the PIV Card Application Administrator and by the "
                                 "administrator (resp. Kleopatra) to authenticate the PIV Card Application."));
170
171
172
        actionLayout->addWidget(button);
        connect(button, &QPushButton::clicked, this, [this] () { setAdminKey(); });
    }
173
174

    actionLayout->addStretch(-1);
175
    grid->addLayout(actionLayout, row++, 0, 1, columnCount);
176

177
178
179
    grid->setColumnStretch(4, -1);
}

180
181
182
183
184
PIVCardWidget::KeyWidgets PIVCardWidget::createKeyWidgets(const std::string &keyRef)
{
    KeyWidgets keyWidgets;
    keyWidgets.keyGrip = new QLabel(this);
    keyWidgets.keyGrip->setTextInteractionFlags(Qt::TextBrowserInteraction);
185
    keyWidgets.keyAlgorithm = new QLabel(this);
186
187
188
    keyWidgets.keyAlgorithm->setTextInteractionFlags(Qt::TextSelectableByMouse);
    keyWidgets.certificateInfo = new QLabel(this);
    keyWidgets.certificateInfo->setTextInteractionFlags(Qt::TextBrowserInteraction);
189
190
    keyWidgets.generateButton = new QPushButton(i18nc("@action:button", "Generate"), this);
    keyWidgets.generateButton->setEnabled(false);
191
192
    connect(keyWidgets.generateButton, &QPushButton::clicked,
            this, [this, keyRef] () { generateKey(keyRef); });
193
194
195
    keyWidgets.writeCertificateButton = new QPushButton(i18nc("@action:button", "Write Certificate"));
    keyWidgets.writeCertificateButton->setToolTip(i18nc("@info:tooltip", "Write the certificate corresponding to this key to the card"));
    keyWidgets.writeCertificateButton->setEnabled(false);
196
197
198
199
200
201
202
    connect(keyWidgets.writeCertificateButton, &QPushButton::clicked,
            this, [this, keyRef] () { writeCertificateToCard(keyRef); });
    keyWidgets.importCertificateButton = new QPushButton(i18nc("@action:button", "Import Certificate"));
    keyWidgets.importCertificateButton->setToolTip(i18nc("@info:tooltip", "Import the certificate stored on the card"));
    keyWidgets.importCertificateButton->setEnabled(false);
    connect(keyWidgets.importCertificateButton, &QPushButton::clicked,
            this, [this, keyRef] () { importCertificateFromCard(keyRef); });
203
    if (keyRef == PIVCard::cardAuthenticationKeyRef() || keyRef == PIVCard::keyManagementKeyRef()) {
204
205
206
        keyWidgets.writeKeyButton = new QPushButton(i18nc("@action:button", "Write Key"));
        keyWidgets.writeKeyButton->setToolTip(i18nc("@info:tooltip", "Write the key pair of a certificate to the card"));
        keyWidgets.writeKeyButton->setEnabled(true);
207
208
        connect(keyWidgets.writeKeyButton, &QPushButton::clicked,
                this, [this, keyRef] () { writeKeyToCard(keyRef); });
209
    }
210
    mKeyWidgets.insert(keyRef, keyWidgets);
211
212
213
    return keyWidgets;
}

214
215
216
217
PIVCardWidget::~PIVCardWidget()
{
}

218
219
void PIVCardWidget::setCard(const PIVCard *card)
{
220
    mCardSerialNumber = card->serialNumber();
221
    mVersionLabel->setText(i18nc("%1 version number", "PIV v%1 card", card->displayAppVersion()));
222

223
224
    mSerialNumber->setText(card->displaySerialNumber());
    mSerialNumber->setToolTip(QString::fromStdString(card->serialNumber()));
225

226
227
228
229
    updateKeyWidgets(PIVCard::pivAuthenticationKeyRef(), card);
    updateKeyWidgets(PIVCard::cardAuthenticationKeyRef(), card);
    updateKeyWidgets(PIVCard::digitalSignatureKeyRef(), card);
    updateKeyWidgets(PIVCard::keyManagementKeyRef(), card);
230
231
232
233

    const bool signingKeyAvailable = !card->keyGrip(PIVCard::digitalSignatureKeyRef()).empty();
    const bool encryptionKeyAvailable = !card->keyGrip(PIVCard::keyManagementKeyRef()).empty();
    mKeyForCardKeysButton->setEnabled(signingKeyAvailable && encryptionKeyAvailable);
234
235
}

236
void PIVCardWidget::updateKeyWidgets(const std::string &keyRef, const PIVCard *card)
237
{
238
239
    KeyWidgets widgets = mKeyWidgets.value(keyRef);
    const std::string grip = card ? card->keyGrip(keyRef) : widgets.keyGrip->text().toStdString();
240
    if (grip.empty()) {
241
242
243
        widgets.certificateInfo->setText(i18nc("@info", "<em>slot empty</em>"));
        widgets.certificateInfo->setToolTip(QString());
        widgets.keyGrip->setText(QString());
244
245
246
247
        widgets.keyAlgorithm->setText(QString());
        widgets.generateButton->setText(i18nc("@action:button", "Generate"));
        widgets.generateButton->setToolTip(
            i18nc("@info:tooltip %1 display name of a key", "Generate %1", PIVCard::keyDisplayName(keyRef)));
248
249
        widgets.writeCertificateButton->setEnabled(false);
        widgets.importCertificateButton->setEnabled(false);
250
    } else {
251
252
        const Key certificate = KeyCache::instance()->findSubkeyByKeyGrip(grip, GpgME::CMS).parent();
        if (!certificate.isNull()) {
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
            widgets.certificateInfo->setText(
                i18nc("X.509 certificate DN (validity, created: date)", "%1 (%2, created: %3)",
                      DN(certificate.userID(0).id()).prettyDN(),
                      Formatting::complianceStringShort(certificate),
                      Formatting::creationDateString(certificate)));
            widgets.certificateInfo->setToolTip(Formatting::toolTip(certificate, toolTipOptions()));
            widgets.writeCertificateButton->setEnabled(true);
        } else {
            widgets.certificateInfo->setText(i18nc("@info", "<em>no matching certificate</em>"));
            widgets.certificateInfo->setToolTip(QString());
            widgets.writeCertificateButton->setEnabled(false);
        }
        if (card) {
            // update information if called with card
            widgets.keyGrip->setText(QString::fromStdString(grip));
            const std::string algo = card->keyAlgorithm(keyRef);
            widgets.keyAlgorithm->setText(algo.empty() ? i18nc("@info unknown key algorithm", "unknown") : QString::fromStdString(algo));
            widgets.importCertificateButton->setEnabled(!card->certificateData(keyRef).empty());
        }
272
273
274
275
        widgets.generateButton->setText(i18nc("@action:button", "Replace"));
        widgets.generateButton->setToolTip(
            i18nc("@info:tooltip %1 display name of a key", "Replace %1 with new key", PIVCard::keyDisplayName(keyRef)));
    }
276

277
    widgets.generateButton->setEnabled(true);
278
279
}

280
void PIVCardWidget::generateKey(const std::string &keyref)
281
{
282
    auto cmd = new PIVGenerateCardKeyCommand(mCardSerialNumber, this);
283
284
285
286
287
288
289
290
    this->setEnabled(false);
    connect(cmd, &PIVGenerateCardKeyCommand::finished,
            this, [this]() {
                this->setEnabled(true);
            });
    cmd->setKeyRef(keyref);
    cmd->start();
}
291

292
void PIVCardWidget::writeCertificateToCard(const std::string &keyref)
293
{
294
    auto cmd = new CertificateToPIVCardCommand(keyref, mCardSerialNumber);
295
    this->setEnabled(false);
296
    connect(cmd, &CertificateToPIVCardCommand::finished,
297
298
299
300
301
            this, [this]() {
                this->setEnabled(true);
            });
    cmd->setParentWidget(this);
    cmd->start();
302
303
304
305
306
307
308
}

void PIVCardWidget::importCertificateFromCard(const std::string &keyref)
{
    auto cmd = new ImportCertificateFromPIVCardCommand(keyref, mCardSerialNumber);
    this->setEnabled(false);
    connect(cmd, &ImportCertificateFromPIVCardCommand::finished,
309
310
            this, [this, keyref] () {
                this->updateKeyWidgets(keyref, nullptr);
311
312
313
314
                this->setEnabled(true);
            });
    cmd->setParentWidget(this);
    cmd->start();
315
316
}

317
318
void PIVCardWidget::writeKeyToCard(const std::string &keyref)
{
319
    auto cmd = new KeyToCardCommand(keyref, mCardSerialNumber, PIVCard::AppName);
320
321
322
323
324
325
326
327
328
    this->setEnabled(false);
    connect(cmd, &KeyToCardCommand::finished,
            this, [this]() {
                this->setEnabled(true);
            });
    cmd->setParentWidget(this);
    cmd->start();
}

329
330
331
332
333
334
335
336
337
338
339
void PIVCardWidget::createKeyFromCardKeys()
{
    auto cmd = new CreateOpenPGPKeyFromCardKeysCommand(mCardSerialNumber, PIVCard::AppName, this);
    this->setEnabled(false);
    connect(cmd, &CreateOpenPGPKeyFromCardKeysCommand::finished,
            this, [this]() {
                this->setEnabled(true);
            });
    cmd->start();
}

340
341
void PIVCardWidget::changePin(const std::string &keyRef)
{
342
    auto cmd = new ChangePinCommand(mCardSerialNumber, PIVCard::AppName, this);
343
344
345
346
347
348
349
350
    this->setEnabled(false);
    connect(cmd, &ChangePinCommand::finished,
            this, [this]() {
                this->setEnabled(true);
            });
    cmd->setKeyRef(keyRef);
    cmd->start();
}
351
352
353
354
355
356
357
358
359
360
361

void PIVCardWidget::setAdminKey()
{
    auto cmd = new SetPIVCardApplicationAdministrationKeyCommand(mCardSerialNumber, this);
    this->setEnabled(false);
    connect(cmd, &SetPIVCardApplicationAdministrationKeyCommand::finished,
            this, [this]() {
                this->setEnabled(true);
            });
    cmd->start();
}