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"