File indexing completed on 2024-06-16 04:56:17

0001 /*  view/pivcardwiget.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 "pivcardwidget.h"
0011 
0012 #include "tooltippreferences.h"
0013 
0014 #include "commands/certificatetopivcardcommand.h"
0015 #include "commands/changepincommand.h"
0016 #include "commands/createcsrforcardkeycommand.h"
0017 #include "commands/createopenpgpkeyfromcardkeyscommand.h"
0018 #include "commands/importcertificatefrompivcardcommand.h"
0019 #include "commands/keytocardcommand.h"
0020 #include "commands/pivgeneratecardkeycommand.h"
0021 #include "commands/setpivcardapplicationadministrationkeycommand.h"
0022 
0023 #include "smartcard/pivcard.h"
0024 #include "smartcard/readerstatus.h"
0025 
0026 #include <Libkleo/Compliance>
0027 #include <Libkleo/Dn>
0028 #include <Libkleo/Formatting>
0029 #include <Libkleo/KeyCache>
0030 
0031 #include <KLocalizedString>
0032 #include <KSeparator>
0033 
0034 #include <QFrame>
0035 #include <QGridLayout>
0036 #include <QLabel>
0037 #include <QPushButton>
0038 #include <QScrollArea>
0039 #include <QVBoxLayout>
0040 
0041 using namespace GpgME;
0042 using namespace Kleo;
0043 using namespace Kleo::Commands;
0044 using namespace Kleo::SmartCard;
0045 
0046 namespace
0047 {
0048 static void layoutKeyWidgets(QGridLayout *grid, const QString &keyName, const PIVCardWidget::KeyWidgets &keyWidgets)
0049 {
0050     int row = grid->rowCount();
0051     grid->addWidget(new QLabel(keyName), row, 0);
0052     grid->addWidget(keyWidgets.keyGrip, row, 1);
0053     grid->addWidget(keyWidgets.keyAlgorithm, row, 2);
0054     grid->addWidget(keyWidgets.generateButton, row, 3);
0055     if (keyWidgets.writeKeyButton) {
0056         grid->addWidget(keyWidgets.writeKeyButton, row, 4);
0057     }
0058 
0059     row++;
0060     grid->addWidget(keyWidgets.certificateInfo, row, 1, 1, 2);
0061     grid->addWidget(keyWidgets.writeCertificateButton, row, 3);
0062     grid->addWidget(keyWidgets.importCertificateButton, row, 4);
0063     if (keyWidgets.createCSRButton) {
0064         grid->addWidget(keyWidgets.createCSRButton, row, 5);
0065     }
0066 }
0067 
0068 static int toolTipOptions()
0069 {
0070     using namespace Kleo::Formatting;
0071     static const int validityFlags = Validity | Issuer | ExpiryDates | CertificateUsage;
0072     static const int ownerFlags = Subject | UserIDs | OwnerTrust;
0073     static const int detailsFlags = StorageLocation | CertificateType | SerialNumber | Fingerprint;
0074 
0075     const TooltipPreferences prefs;
0076 
0077     int flags = KeyID;
0078     flags |= prefs.showValidity() ? validityFlags : 0;
0079     flags |= prefs.showOwnerInformation() ? ownerFlags : 0;
0080     flags |= prefs.showCertificateDetails() ? detailsFlags : 0;
0081     return flags;
0082 }
0083 }
0084 
0085 PIVCardWidget::PIVCardWidget(QWidget *parent)
0086     : QWidget(parent)
0087     , mSerialNumber(new QLabel(this))
0088     , mVersionLabel(new QLabel(this))
0089 {
0090     // Set up the scroll area
0091     auto myLayout = new QVBoxLayout(this);
0092     myLayout->setContentsMargins(0, 0, 0, 0);
0093 
0094     auto area = new QScrollArea;
0095     area->setFrameShape(QFrame::NoFrame);
0096     area->setWidgetResizable(true);
0097     myLayout->addWidget(area);
0098 
0099     auto areaWidget = new QWidget;
0100     area->setWidget(areaWidget);
0101 
0102     auto areaVLay = new QVBoxLayout(areaWidget);
0103 
0104     auto cardInfoGrid = new QGridLayout;
0105     {
0106         int row = 0;
0107 
0108         // Version and Serialnumber
0109         cardInfoGrid->addWidget(mVersionLabel, row++, 0, 1, 2);
0110         mVersionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
0111 
0112         cardInfoGrid->addWidget(new QLabel(i18n("Serial number:")), row, 0);
0113         cardInfoGrid->addWidget(mSerialNumber, row++, 1);
0114         mSerialNumber->setTextInteractionFlags(Qt::TextBrowserInteraction);
0115 
0116         cardInfoGrid->setColumnStretch(cardInfoGrid->columnCount(), 1);
0117     }
0118     areaVLay->addLayout(cardInfoGrid);
0119 
0120     areaVLay->addWidget(new KSeparator(Qt::Horizontal));
0121 
0122     areaVLay->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("Keys:"))));
0123 
0124     auto keysGrid = new QGridLayout;
0125     for (const auto &keyInfo : PIVCard::supportedKeys()) {
0126         KeyWidgets keyWidgets = createKeyWidgets(keyInfo);
0127         layoutKeyWidgets(keysGrid, PIVCard::keyDisplayName(keyInfo.keyRef), keyWidgets);
0128     }
0129     areaVLay->addLayout(keysGrid);
0130 
0131     areaVLay->addWidget(new KSeparator(Qt::Horizontal));
0132 
0133     auto actionLayout = new QHBoxLayout;
0134 
0135     if (CreateOpenPGPKeyFromCardKeysCommand::isSupported()) {
0136         mKeyForCardKeysButton = new QPushButton(this);
0137         mKeyForCardKeysButton->setText(i18nc("@action:button", "Create OpenPGP Key"));
0138         mKeyForCardKeysButton->setToolTip(i18nc("@info:tooltip", "Create an OpenPGP key for the keys stored on the card."));
0139         actionLayout->addWidget(mKeyForCardKeysButton);
0140         connect(mKeyForCardKeysButton, &QPushButton::clicked, this, &PIVCardWidget::createKeyFromCardKeys);
0141     }
0142 
0143     {
0144         auto button = new QPushButton(i18nc("@action:button", "Change PIN"));
0145         button->setToolTip(i18nc("@info:tooltip",
0146                                  "Change the PIV Card Application PIN that activates the PIV Card "
0147                                  "and enables private key operations using the stored keys."));
0148         actionLayout->addWidget(button);
0149         connect(button, &QPushButton::clicked, this, [this]() {
0150             changePin(PIVCard::pinKeyRef());
0151         });
0152     }
0153     {
0154         auto button = new QPushButton(i18nc("@action:button", "Change PUK"));
0155         button->setToolTip(i18nc("@info:tooltip", "Change the PIN Unblocking Key that enables a reset of the PIN."));
0156         actionLayout->addWidget(button);
0157         connect(button, &QPushButton::clicked, this, [this]() {
0158             changePin(PIVCard::pukKeyRef());
0159         });
0160     }
0161     {
0162         auto button = new QPushButton(i18nc("@action:button", "Change Admin Key"));
0163         button->setToolTip(i18nc("@info:tooltip",
0164                                  "Change the PIV Card Application Administration Key that is used by the "
0165                                  "PIV Card Application to authenticate the PIV Card Application Administrator and by the "
0166                                  "administrator (resp. Kleopatra) to authenticate the PIV Card Application."));
0167         actionLayout->addWidget(button);
0168         connect(button, &QPushButton::clicked, this, [this]() {
0169             setAdminKey();
0170         });
0171     }
0172 
0173     actionLayout->addStretch(-1);
0174     areaVLay->addLayout(actionLayout);
0175 
0176     areaVLay->addStretch(1);
0177 }
0178 
0179 PIVCardWidget::KeyWidgets PIVCardWidget::createKeyWidgets(const KeyPairInfo &keyInfo)
0180 {
0181     const std::string keyRef = keyInfo.keyRef;
0182     KeyWidgets keyWidgets;
0183     keyWidgets.keyGrip = new QLabel(this);
0184     keyWidgets.keyGrip->setTextInteractionFlags(Qt::TextBrowserInteraction);
0185     keyWidgets.keyAlgorithm = new QLabel(this);
0186     keyWidgets.keyAlgorithm->setTextInteractionFlags(Qt::TextSelectableByMouse);
0187     keyWidgets.certificateInfo = new QLabel(this);
0188     keyWidgets.certificateInfo->setTextInteractionFlags(Qt::TextBrowserInteraction);
0189     keyWidgets.generateButton = new QPushButton(i18nc("@action:button", "Generate"), this);
0190     keyWidgets.generateButton->setEnabled(false);
0191     connect(keyWidgets.generateButton, &QPushButton::clicked, this, [this, keyRef]() {
0192         generateKey(keyRef);
0193     });
0194     if (keyInfo.canSign() || keyInfo.canEncrypt()) {
0195         keyWidgets.createCSRButton = new QPushButton(i18nc("@action:button", "Create CSR"), this);
0196         keyWidgets.createCSRButton->setToolTip(i18nc("@info:tooltip", "Create a certificate signing request for this key"));
0197         keyWidgets.createCSRButton->setEnabled(false);
0198         connect(keyWidgets.createCSRButton, &QPushButton::clicked, this, [this, keyRef]() {
0199             createCSR(keyRef);
0200         });
0201     }
0202     keyWidgets.writeCertificateButton = new QPushButton(i18nc("@action:button", "Write Certificate"));
0203     keyWidgets.writeCertificateButton->setToolTip(i18nc("@info:tooltip", "Write the certificate corresponding to this key to the card"));
0204     keyWidgets.writeCertificateButton->setEnabled(false);
0205     connect(keyWidgets.writeCertificateButton, &QPushButton::clicked, this, [this, keyRef]() {
0206         writeCertificateToCard(keyRef);
0207     });
0208     keyWidgets.importCertificateButton = new QPushButton(i18nc("@action:button", "Import Certificate"));
0209     keyWidgets.importCertificateButton->setToolTip(i18nc("@info:tooltip", "Import the certificate stored on the card"));
0210     keyWidgets.importCertificateButton->setEnabled(false);
0211     connect(keyWidgets.importCertificateButton, &QPushButton::clicked, this, [this, keyRef]() {
0212         importCertificateFromCard(keyRef);
0213     });
0214     if (keyRef == PIVCard::cardAuthenticationKeyRef() || keyRef == PIVCard::keyManagementKeyRef()) {
0215         keyWidgets.writeKeyButton = new QPushButton(i18nc("@action:button", "Write Key"));
0216         keyWidgets.writeKeyButton->setToolTip(i18nc("@info:tooltip", "Write the key pair of a certificate to the card"));
0217         keyWidgets.writeKeyButton->setEnabled(true);
0218         connect(keyWidgets.writeKeyButton, &QPushButton::clicked, this, [this, keyRef]() {
0219             writeKeyToCard(keyRef);
0220         });
0221     }
0222     mKeyWidgets.insert({keyRef, keyWidgets});
0223     return keyWidgets;
0224 }
0225 
0226 PIVCardWidget::~PIVCardWidget()
0227 {
0228 }
0229 
0230 void PIVCardWidget::setCard(const PIVCard *card)
0231 {
0232     mCardSerialNumber = card->serialNumber();
0233     mVersionLabel->setText(i18nc("%1 version number", "PIV v%1 card", card->displayAppVersion()));
0234 
0235     mSerialNumber->setText(card->displaySerialNumber());
0236     mSerialNumber->setToolTip(QString::fromStdString(card->serialNumber()));
0237 
0238     if (card) {
0239         updateCachedValues(PIVCard::pivAuthenticationKeyRef(), card);
0240         updateCachedValues(PIVCard::cardAuthenticationKeyRef(), card);
0241         updateCachedValues(PIVCard::digitalSignatureKeyRef(), card);
0242         updateCachedValues(PIVCard::keyManagementKeyRef(), card);
0243     }
0244     updateKeyWidgets(PIVCard::pivAuthenticationKeyRef());
0245     updateKeyWidgets(PIVCard::cardAuthenticationKeyRef());
0246     updateKeyWidgets(PIVCard::digitalSignatureKeyRef());
0247     updateKeyWidgets(PIVCard::keyManagementKeyRef());
0248 
0249     if (mKeyForCardKeysButton) {
0250         mKeyForCardKeysButton->setEnabled(card->hasSigningKey() //
0251                                           && card->hasEncryptionKey() //
0252                                           && DeVSCompliance::algorithmIsCompliant(card->keyInfo(card->signingKeyRef()).algorithm)
0253                                           && DeVSCompliance::algorithmIsCompliant(card->keyInfo(card->encryptionKeyRef()).algorithm));
0254     }
0255 }
0256 
0257 void PIVCardWidget::updateCachedValues(const std::string &keyRef, const SmartCard::PIVCard *card)
0258 {
0259     KeyWidgets &widgets = mKeyWidgets.at(keyRef);
0260     widgets.keyInfo = card->keyInfo(keyRef);
0261     widgets.certificateData = card->certificateData(keyRef);
0262 }
0263 
0264 void PIVCardWidget::updateKeyWidgets(const std::string &keyRef)
0265 {
0266     const KeyWidgets &widgets = mKeyWidgets.at(keyRef);
0267     const std::string grip = widgets.keyInfo.grip;
0268     if (grip.empty()) {
0269         widgets.certificateInfo->setText(i18nc("@info", "<em>slot empty</em>"));
0270         widgets.certificateInfo->setToolTip(QString());
0271         widgets.keyGrip->setText(QString());
0272         widgets.keyAlgorithm->setText(QString());
0273         widgets.generateButton->setText(i18nc("@action:button", "Generate"));
0274         widgets.generateButton->setToolTip(i18nc("@info:tooltip %1 display name of a key", "Generate %1", PIVCard::keyDisplayName(keyRef)));
0275         if (widgets.createCSRButton) {
0276             widgets.createCSRButton->setEnabled(false);
0277         }
0278         widgets.writeCertificateButton->setEnabled(false);
0279         widgets.importCertificateButton->setEnabled(false);
0280     } else {
0281         const Key certificate = KeyCache::instance()->findSubkeyByKeyGrip(grip, GpgME::CMS).parent();
0282         if (!certificate.isNull()) {
0283             widgets.certificateInfo->setText(i18nc("X.509 certificate DN (validity, created: date)",
0284                                                    "%1 (%2, created: %3)",
0285                                                    DN(certificate.userID(0).id()).prettyDN(),
0286                                                    Formatting::complianceStringShort(certificate),
0287                                                    Formatting::creationDateString(certificate)));
0288             widgets.certificateInfo->setToolTip(Formatting::toolTip(certificate, toolTipOptions()));
0289             widgets.writeCertificateButton->setEnabled(true);
0290         } else {
0291             widgets.certificateInfo->setText(i18nc("@info", "<em>no matching certificate</em>"));
0292             widgets.certificateInfo->setToolTip(QString());
0293             widgets.writeCertificateButton->setEnabled(false);
0294         }
0295         widgets.keyGrip->setText(QString::fromStdString(grip));
0296         const std::string algo = widgets.keyInfo.algorithm;
0297         widgets.keyAlgorithm->setText(algo.empty() ? i18nc("@info unknown key algorithm", "unknown") : QString::fromStdString(algo));
0298         widgets.importCertificateButton->setEnabled(!widgets.certificateData.empty());
0299 
0300         widgets.generateButton->setText(i18nc("@action:button", "Replace"));
0301         widgets.generateButton->setToolTip(i18nc("@info:tooltip %1 display name of a key", "Replace %1 with new key", PIVCard::keyDisplayName(keyRef)));
0302         if (widgets.createCSRButton) {
0303             widgets.createCSRButton->setEnabled(DeVSCompliance::algorithmIsCompliant(algo));
0304         }
0305     }
0306 
0307     widgets.generateButton->setEnabled(true);
0308 }
0309 
0310 void PIVCardWidget::generateKey(const std::string &keyref)
0311 {
0312     auto cmd = new PIVGenerateCardKeyCommand(mCardSerialNumber, this);
0313     this->setEnabled(false);
0314     connect(cmd, &PIVGenerateCardKeyCommand::finished, this, [this]() {
0315         this->setEnabled(true);
0316     });
0317     cmd->setKeyRef(keyref);
0318     cmd->start();
0319 }
0320 
0321 void PIVCardWidget::createCSR(const std::string &keyref)
0322 {
0323     auto cmd = new CreateCSRForCardKeyCommand(keyref, mCardSerialNumber, PIVCard::AppName, this);
0324     this->setEnabled(false);
0325     connect(cmd, &CreateCSRForCardKeyCommand::finished, this, [this]() {
0326         this->setEnabled(true);
0327     });
0328     cmd->start();
0329 }
0330 
0331 void PIVCardWidget::writeCertificateToCard(const std::string &keyref)
0332 {
0333     auto cmd = new CertificateToPIVCardCommand(keyref, mCardSerialNumber);
0334     this->setEnabled(false);
0335     connect(cmd, &CertificateToPIVCardCommand::finished, this, [this]() {
0336         this->setEnabled(true);
0337     });
0338     cmd->setParentWidget(this);
0339     cmd->start();
0340 }
0341 
0342 void PIVCardWidget::importCertificateFromCard(const std::string &keyref)
0343 {
0344     auto cmd = new ImportCertificateFromPIVCardCommand(keyref, mCardSerialNumber);
0345     this->setEnabled(false);
0346     connect(cmd, &ImportCertificateFromPIVCardCommand::finished, this, [this, keyref]() {
0347         this->updateKeyWidgets(keyref);
0348         this->setEnabled(true);
0349     });
0350     cmd->setParentWidget(this);
0351     cmd->start();
0352 }
0353 
0354 void PIVCardWidget::writeKeyToCard(const std::string &keyref)
0355 {
0356     auto cmd = new KeyToCardCommand(keyref, mCardSerialNumber, PIVCard::AppName);
0357     this->setEnabled(false);
0358     connect(cmd, &KeyToCardCommand::finished, this, [this]() {
0359         this->setEnabled(true);
0360     });
0361     cmd->setParentWidget(this);
0362     cmd->start();
0363 }
0364 
0365 void PIVCardWidget::createKeyFromCardKeys()
0366 {
0367     auto cmd = new CreateOpenPGPKeyFromCardKeysCommand(mCardSerialNumber, PIVCard::AppName, this);
0368     this->setEnabled(false);
0369     connect(cmd, &CreateOpenPGPKeyFromCardKeysCommand::finished, this, [this]() {
0370         this->setEnabled(true);
0371     });
0372     cmd->start();
0373 }
0374 
0375 void PIVCardWidget::changePin(const std::string &keyRef)
0376 {
0377     auto cmd = new ChangePinCommand(mCardSerialNumber, PIVCard::AppName, this);
0378     this->setEnabled(false);
0379     connect(cmd, &ChangePinCommand::finished, this, [this]() {
0380         this->setEnabled(true);
0381     });
0382     cmd->setKeyRef(keyRef);
0383     cmd->start();
0384 }
0385 
0386 void PIVCardWidget::setAdminKey()
0387 {
0388     auto cmd = new SetPIVCardApplicationAdministrationKeyCommand(mCardSerialNumber, this);
0389     this->setEnabled(false);
0390     connect(cmd, &SetPIVCardApplicationAdministrationKeyCommand::finished, this, [this]() {
0391         this->setEnabled(true);
0392     });
0393     cmd->start();
0394 }
0395 
0396 #include "moc_pivcardwidget.cpp"