File indexing completed on 2024-06-16 04:56:16
0001 /* view/netkeywidget.cpp 0002 0003 This file is part of Kleopatra, the KDE keymanager 0004 SPDX-FileCopyrightText: 2017 Intevation GmbH 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 #include "netkeywidget.h" 0009 0010 #include "keytreeview.h" 0011 #include "kleopatraapplication.h" 0012 #include "nullpinwidget.h" 0013 #include "systrayicon.h" 0014 0015 #include "kleopatra_debug.h" 0016 0017 #include "smartcard/netkeycard.h" 0018 #include "smartcard/readerstatus.h" 0019 0020 #include "commands/changepincommand.h" 0021 #include "commands/createcsrforcardkeycommand.h" 0022 #include "commands/createopenpgpkeyfromcardkeyscommand.h" 0023 #include "commands/detailscommand.h" 0024 #include "commands/learncardkeyscommand.h" 0025 0026 #include <Libkleo/Algorithm> 0027 #include <Libkleo/Compliance> 0028 #include <Libkleo/KeyListModel> 0029 0030 #include <KConfigGroup> 0031 #include <KSharedConfig> 0032 0033 #include <QHBoxLayout> 0034 #include <QInputDialog> 0035 #include <QLabel> 0036 #include <QPushButton> 0037 #include <QScrollArea> 0038 #include <QTreeView> 0039 #include <QVBoxLayout> 0040 0041 #include <KLocalizedString> 0042 #include <KMessageBox> 0043 0044 #include <gpgme++/context.h> 0045 #include <gpgme++/engineinfo.h> 0046 0047 using namespace Kleo; 0048 using namespace Kleo::SmartCard; 0049 using namespace Kleo::Commands; 0050 0051 NetKeyWidget::NetKeyWidget(QWidget *parent) 0052 : QWidget(parent) 0053 , mSerialNumberLabel(new QLabel(this)) 0054 , mVersionLabel(new QLabel(this)) 0055 , mLearnKeysLabel(new QLabel(this)) 0056 , mErrorLabel(new QLabel(this)) 0057 , mNullPinWidget(new NullPinWidget(this)) 0058 , mLearnKeysBtn(new QPushButton(this)) 0059 , mChangeNKSPINBtn(new QPushButton(this)) 0060 , mChangeSigGPINBtn(new QPushButton(this)) 0061 , mTreeView(new KeyTreeView(this)) 0062 , mArea(new QScrollArea) 0063 { 0064 auto vLay = new QVBoxLayout; 0065 0066 // Set up the scroll are 0067 mArea->setFrameShape(QFrame::NoFrame); 0068 mArea->setWidgetResizable(true); 0069 auto mAreaWidget = new QWidget; 0070 mAreaWidget->setLayout(vLay); 0071 mArea->setWidget(mAreaWidget); 0072 auto scrollLay = new QVBoxLayout(this); 0073 scrollLay->setContentsMargins(0, 0, 0, 0); 0074 scrollLay->addWidget(mArea); 0075 0076 // Add general widgets 0077 mVersionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); 0078 vLay->addWidget(mVersionLabel, 0, Qt::AlignLeft); 0079 0080 mSerialNumberLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); 0081 0082 auto hLay1 = new QHBoxLayout; 0083 hLay1->addWidget(new QLabel(i18n("Serial number:"))); 0084 hLay1->addWidget(mSerialNumberLabel); 0085 hLay1->addStretch(1); 0086 vLay->addLayout(hLay1); 0087 0088 vLay->addWidget(mNullPinWidget); 0089 0090 auto line1 = new QFrame(); 0091 line1->setFrameShape(QFrame::HLine); 0092 vLay->addWidget(line1); 0093 vLay->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("Certificates:"))), 0, Qt::AlignLeft); 0094 0095 mLearnKeysLabel = new QLabel(i18n("There are unknown certificates on this card.")); 0096 mLearnKeysBtn->setText(i18nc("@action", "Load Certificates")); 0097 connect(mLearnKeysBtn, &QPushButton::clicked, this, [this]() { 0098 mLearnKeysBtn->setEnabled(false); 0099 auto cmd = new LearnCardKeysCommand(GpgME::CMS); 0100 cmd->setParentWidget(this); 0101 cmd->start(); 0102 0103 auto icon = KleopatraApplication::instance()->sysTrayIcon(); 0104 if (icon) { 0105 icon->setLearningInProgress(true); 0106 } 0107 0108 connect(cmd, &Command::finished, this, [icon]() { 0109 ReaderStatus::mutableInstance()->updateStatus(); 0110 icon->setLearningInProgress(false); 0111 }); 0112 }); 0113 0114 auto hLay2 = new QHBoxLayout; 0115 hLay2->addWidget(mLearnKeysLabel); 0116 hLay2->addWidget(mLearnKeysBtn); 0117 hLay2->addStretch(1); 0118 vLay->addLayout(hLay2); 0119 0120 mErrorLabel->setVisible(false); 0121 vLay->addWidget(mErrorLabel); 0122 0123 // The certificate view 0124 mTreeView->setHierarchicalModel(AbstractKeyListModel::createHierarchicalKeyListModel(mTreeView)); 0125 mTreeView->setHierarchicalView(true); 0126 0127 connect(mTreeView->view(), &QAbstractItemView::doubleClicked, this, [this](const QModelIndex &idx) { 0128 const auto klm = dynamic_cast<KeyListModelInterface *>(mTreeView->view()->model()); 0129 if (!klm) { 0130 qCDebug(KLEOPATRA_LOG) << "Unhandled Model: " << mTreeView->view()->model()->metaObject()->className(); 0131 return; 0132 } 0133 auto cmd = new DetailsCommand(klm->key(idx)); 0134 cmd->setParentWidget(this); 0135 cmd->start(); 0136 }); 0137 vLay->addWidget(mTreeView); 0138 0139 // The action area 0140 auto line2 = new QFrame(); 0141 line2->setFrameShape(QFrame::HLine); 0142 vLay->addWidget(line2); 0143 vLay->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("Actions:"))), 0, Qt::AlignLeft); 0144 0145 auto actionLayout = new QHBoxLayout(); 0146 0147 if (CreateOpenPGPKeyFromCardKeysCommand::isSupported()) { 0148 mKeyForCardKeysButton = new QPushButton(this); 0149 mKeyForCardKeysButton->setText(i18nc("@action:button", "Create OpenPGP Key")); 0150 mKeyForCardKeysButton->setToolTip(i18nc("@info:tooltip", "Create an OpenPGP key for the keys stored on the card.")); 0151 actionLayout->addWidget(mKeyForCardKeysButton); 0152 connect(mKeyForCardKeysButton, &QPushButton::clicked, this, &NetKeyWidget::createKeyFromCardKeys); 0153 } 0154 0155 if (!(engineInfo(GpgME::GpgSMEngine).engineVersion() < "2.2.26")) { // see https://dev.gnupg.org/T5184 0156 mCreateCSRButton = new QPushButton(this); 0157 mCreateCSRButton->setText(i18nc("@action:button", "Create CSR")); 0158 mCreateCSRButton->setToolTip(i18nc("@info:tooltip", "Create a certificate signing request for a key stored on the card.")); 0159 mCreateCSRButton->setEnabled(false); 0160 actionLayout->addWidget(mCreateCSRButton); 0161 connect(mCreateCSRButton, &QPushButton::clicked, this, [this]() { 0162 createCSR(); 0163 }); 0164 } 0165 0166 mChangeNKSPINBtn->setText(i18nc("NKS is an identifier for a type of keys on a NetKey card", "Change NKS PIN")); 0167 mChangeSigGPINBtn->setText(i18nc("SigG is an identifier for a type of keys on a NetKey card", "Change SigG PIN")); 0168 0169 connect(mChangeNKSPINBtn, &QPushButton::clicked, this, [this]() { 0170 doChangePin(NetKeyCard::nksPinKeyRef()); 0171 }); 0172 connect(mChangeSigGPINBtn, &QPushButton::clicked, this, [this]() { 0173 doChangePin(NetKeyCard::sigGPinKeyRef()); 0174 }); 0175 0176 actionLayout->addWidget(mChangeNKSPINBtn); 0177 actionLayout->addWidget(mChangeSigGPINBtn); 0178 actionLayout->addStretch(1); 0179 0180 vLay->addLayout(actionLayout); 0181 vLay->addStretch(1); 0182 0183 const KConfigGroup configGroup(KSharedConfig::openConfig(), QStringLiteral("NetKeyCardView")); 0184 mTreeView->restoreLayout(configGroup); 0185 } 0186 0187 NetKeyWidget::~NetKeyWidget() = default; 0188 0189 namespace 0190 { 0191 std::vector<KeyPairInfo> getKeysSuitableForCSRCreation(const NetKeyCard *netKeyCard) 0192 { 0193 if (netKeyCard->hasNKSNullPin()) { 0194 return {}; 0195 } 0196 0197 std::vector<KeyPairInfo> keys; 0198 Kleo::copy_if(netKeyCard->keyInfos(), std::back_inserter(keys), [](const auto &keyInfo) { 0199 if (keyInfo.keyRef.substr(0, 9) == "NKS-SIGG.") { 0200 // SigG certificates for qualified signatures are issued with the physical cards; 0201 // it's not possible to request a certificate for them 0202 return false; 0203 } 0204 return keyInfo.canSign() // 0205 && (keyInfo.keyRef.substr(0, 9) == "NKS-NKS3.") // 0206 && DeVSCompliance::algorithmIsCompliant(keyInfo.algorithm); 0207 }); 0208 0209 return keys; 0210 } 0211 } 0212 0213 void NetKeyWidget::setCard(const NetKeyCard *card) 0214 { 0215 mSerialNumber = card->serialNumber(); 0216 mVersionLabel->setText(i18nc("1 is a Version number", "NetKey v%1 Card", card->appVersion())); 0217 mSerialNumberLabel->setText(card->displaySerialNumber()); 0218 0219 mNullPinWidget->setSerialNumber(mSerialNumber); 0220 /* According to users of NetKey Cards it is fairly uncommon 0221 * to use SigG Certificates at all. So it should be optional to set the pins. */ 0222 mNullPinWidget->setVisible(card->hasNKSNullPin() /*|| card->hasSigGNullPin()*/); 0223 0224 mNullPinWidget->setSigGVisible(false /*card->hasSigGNullPin()*/); 0225 mNullPinWidget->setNKSVisible(card->hasNKSNullPin()); 0226 mChangeNKSPINBtn->setEnabled(!card->hasNKSNullPin()); 0227 0228 if (card->hasSigGNullPin()) { 0229 mChangeSigGPINBtn->setText(i18nc("SigG is an identifier for a type of keys on a NetKey card", "Set SigG PIN")); 0230 } else { 0231 mChangeSigGPINBtn->setText(i18nc("SigG is an identifier for a type of keys on a NetKey card", "Change SigG PIN")); 0232 } 0233 0234 const auto keys = card->keys(); 0235 mLearnKeysBtn->setEnabled(true); 0236 mLearnKeysBtn->setVisible(card->canLearnKeys()); 0237 mTreeView->setVisible(!keys.empty()); 0238 mLearnKeysLabel->setVisible(keys.empty()); 0239 0240 const auto errMsg = card->errorMsg(); 0241 if (!errMsg.isEmpty()) { 0242 mErrorLabel->setText(QStringLiteral("<b>%1:</b> %2").arg(i18n("Error"), errMsg)); 0243 mErrorLabel->setVisible(true); 0244 } else { 0245 mErrorLabel->setVisible(false); 0246 } 0247 0248 mTreeView->setKeys(keys); 0249 0250 if (mKeyForCardKeysButton) { 0251 mKeyForCardKeysButton->setEnabled(!card->hasNKSNullPin() && card->hasSigningKey() && card->hasEncryptionKey() 0252 && DeVSCompliance::algorithmIsCompliant(card->keyInfo(card->signingKeyRef()).algorithm) 0253 && DeVSCompliance::algorithmIsCompliant(card->keyInfo(card->encryptionKeyRef()).algorithm)); 0254 } 0255 if (mCreateCSRButton) { 0256 mCreateCSRButton->setEnabled(!getKeysSuitableForCSRCreation(card).empty()); 0257 } 0258 } 0259 0260 void NetKeyWidget::doChangePin(const std::string &keyRef) 0261 { 0262 const auto netKeyCard = ReaderStatus::instance()->getCard<NetKeyCard>(mSerialNumber); 0263 if (!netKeyCard) { 0264 KMessageBox::error(this, i18n("Failed to find the smartcard with the serial number: %1", QString::fromStdString(mSerialNumber))); 0265 return; 0266 } 0267 0268 auto cmd = new ChangePinCommand(mSerialNumber, NetKeyCard::AppName, this); 0269 this->setEnabled(false); 0270 connect(cmd, &ChangePinCommand::finished, this, [this]() { 0271 this->setEnabled(true); 0272 }); 0273 cmd->setKeyRef(keyRef); 0274 if ((keyRef == NetKeyCard::nksPinKeyRef() && netKeyCard->hasNKSNullPin()) // 0275 || (keyRef == NetKeyCard::sigGPinKeyRef() && netKeyCard->hasSigGNullPin())) { 0276 cmd->setMode(ChangePinCommand::NullPinMode); 0277 } 0278 cmd->start(); 0279 } 0280 0281 void NetKeyWidget::createKeyFromCardKeys() 0282 { 0283 auto cmd = new CreateOpenPGPKeyFromCardKeysCommand(mSerialNumber, NetKeyCard::AppName, this); 0284 this->setEnabled(false); 0285 connect(cmd, &CreateOpenPGPKeyFromCardKeysCommand::finished, this, [this]() { 0286 this->setEnabled(true); 0287 }); 0288 cmd->start(); 0289 } 0290 0291 namespace 0292 { 0293 std::string getKeyRef(const std::vector<KeyPairInfo> &keys, QWidget *parent) 0294 { 0295 QStringList options; 0296 for (const auto &key : keys) { 0297 options << QStringLiteral("%1 - %2").arg(QString::fromStdString(key.keyRef), QString::fromStdString(key.grip)); 0298 } 0299 0300 bool ok; 0301 const QString choice = QInputDialog::getItem(parent, 0302 i18n("Select Key"), 0303 i18n("Please select the key you want to create a certificate signing request for:"), 0304 options, 0305 /* current= */ 0, 0306 /* editable= */ false, 0307 &ok); 0308 return ok ? keys[options.indexOf(choice)].keyRef : std::string(); 0309 } 0310 } 0311 0312 void NetKeyWidget::createCSR() 0313 { 0314 const auto netKeyCard = ReaderStatus::instance()->getCard<NetKeyCard>(mSerialNumber); 0315 if (!netKeyCard) { 0316 KMessageBox::error(this, i18n("Failed to find the smartcard with the serial number: %1", QString::fromStdString(mSerialNumber))); 0317 return; 0318 } 0319 const auto suitableKeys = getKeysSuitableForCSRCreation(netKeyCard.get()); 0320 if (suitableKeys.empty()) { 0321 KMessageBox::error(this, i18n("Sorry! No keys suitable for creating a certificate signing request found on the smartcard.")); 0322 return; 0323 } 0324 const auto keyRef = getKeyRef(suitableKeys, this); 0325 if (keyRef.empty()) { 0326 return; 0327 } 0328 auto cmd = new CreateCSRForCardKeyCommand(keyRef, mSerialNumber, NetKeyCard::AppName, this); 0329 this->setEnabled(false); 0330 connect(cmd, &CreateCSRForCardKeyCommand::finished, this, [this]() { 0331 this->setEnabled(true); 0332 }); 0333 cmd->start(); 0334 } 0335 0336 #include "moc_netkeywidget.cpp"