File indexing completed on 2024-06-16 04:56:16
0001 /* view/openpgpkeycardwidget.cpp 0002 0003 This file is part of Kleopatra, the KDE keymanager 0004 SPDX-FileCopyrightText: 2021, 2022 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 "openpgpkeycardwidget.h" 0011 0012 #include "commands/detailscommand.h" 0013 0014 #include "smartcard/card.h" 0015 #include "smartcard/keypairinfo.h" 0016 #include "smartcard/openpgpcard.h" 0017 0018 #include <Libkleo/Compliance> 0019 #include <Libkleo/Formatting> 0020 #include <Libkleo/KeyCache> 0021 0022 #include <KLocalizedString> 0023 #include <KMessageBox> 0024 0025 #include <QGridLayout> 0026 #include <QLabel> 0027 #include <QPushButton> 0028 0029 #include <gpgme++/key.h> 0030 0031 using namespace Kleo; 0032 using namespace SmartCard; 0033 0034 namespace 0035 { 0036 struct KeyWidgets { 0037 std::string cardKeyRef; 0038 std::string keyFingerprint; 0039 KeyPairInfo keyInfo; 0040 QLabel *keyTitleLabel = nullptr; 0041 QLabel *keyInfoLabel = nullptr; 0042 QPushButton *showCertificateDetailsButton = nullptr; 0043 QPushButton *generateButton = nullptr; 0044 QPushButton *createCSRButton = nullptr; 0045 }; 0046 0047 KeyWidgets createKeyWidgets(const KeyPairInfo &keyInfo, QWidget *parent) 0048 { 0049 KeyWidgets keyWidgets; 0050 keyWidgets.keyTitleLabel = new QLabel{OpenPGPCard::keyDisplayName(keyInfo.keyRef), parent}; 0051 keyWidgets.keyInfoLabel = new QLabel{parent}; 0052 keyWidgets.keyInfoLabel->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::TextSelectableByKeyboard); 0053 keyWidgets.showCertificateDetailsButton = new QPushButton{i18nc("@action:button", "Show Details"), parent}; 0054 keyWidgets.showCertificateDetailsButton->setToolTip(i18nc("@action:tooltip", "Show detailed information about this key")); 0055 keyWidgets.showCertificateDetailsButton->setEnabled(false); 0056 keyWidgets.generateButton = new QPushButton{i18nc("@action:button", "Generate Key"), parent}; 0057 keyWidgets.generateButton->setEnabled(false); 0058 if (keyInfo.canCertify() || keyInfo.canSign() || keyInfo.canAuthenticate()) { 0059 keyWidgets.createCSRButton = new QPushButton{i18nc("@action:button", "Create CSR"), parent}; 0060 keyWidgets.createCSRButton->setToolTip(i18nc("@info:tooltip", "Create a certificate signing request for this key")); 0061 keyWidgets.createCSRButton->setEnabled(false); 0062 } 0063 0064 return keyWidgets; 0065 } 0066 } 0067 0068 class OpenPGPKeyCardWidget::Private 0069 { 0070 public: 0071 explicit Private(OpenPGPKeyCardWidget *q); 0072 ~Private() = default; 0073 0074 void setAllowedActions(Actions actions); 0075 void update(const Card *card = nullptr); 0076 0077 private: 0078 void updateCachedValues(const std::string &openPGPKeyRef, const std::string &cardKeyRef, const Card *card); 0079 void updateKeyWidgets(const std::string &openPGPKeyRef); 0080 0081 void showCertificateDetails(const std::string &openPGPKeyRef); 0082 0083 private: 0084 OpenPGPKeyCardWidget *const q; 0085 Actions mAllowedActions = AllActions; 0086 std::map<std::string, KeyWidgets> mKeyWidgets; 0087 }; 0088 0089 OpenPGPKeyCardWidget::Private::Private(OpenPGPKeyCardWidget *q) 0090 : q{q} 0091 { 0092 auto grid = new QGridLayout{q}; 0093 grid->setContentsMargins(0, 0, 0, 0); 0094 for (const auto &keyInfo : OpenPGPCard::supportedKeys()) { 0095 const KeyWidgets keyWidgets = createKeyWidgets(keyInfo, q); 0096 0097 const std::string keyRef = keyInfo.keyRef; 0098 connect(keyWidgets.showCertificateDetailsButton, &QPushButton::clicked, q, [this, keyRef]() { 0099 showCertificateDetails(keyRef); 0100 }); 0101 connect(keyWidgets.generateButton, &QPushButton::clicked, q, [q, keyRef]() { 0102 Q_EMIT q->generateKeyRequested(keyRef); 0103 }); 0104 if (keyWidgets.createCSRButton) { 0105 connect(keyWidgets.createCSRButton, &QPushButton::clicked, q, [q, keyRef]() { 0106 Q_EMIT q->createCSRRequested(keyRef); 0107 }); 0108 } 0109 0110 const int row = grid->rowCount(); 0111 grid->addWidget(keyWidgets.keyTitleLabel, row, 0, Qt::AlignTop); 0112 grid->addWidget(keyWidgets.keyInfoLabel, row, 1, Qt::AlignTop); 0113 0114 auto buttons = new QHBoxLayout; 0115 buttons->addWidget(keyWidgets.showCertificateDetailsButton); 0116 buttons->addWidget(keyWidgets.generateButton); 0117 if (keyWidgets.createCSRButton) { 0118 buttons->addWidget(keyWidgets.createCSRButton); 0119 } 0120 buttons->addStretch(1); 0121 grid->addLayout(buttons, row, 2, Qt::AlignTop); 0122 0123 mKeyWidgets.insert({keyInfo.keyRef, keyWidgets}); 0124 } 0125 grid->setColumnStretch(grid->columnCount(), 1); 0126 } 0127 0128 void OpenPGPKeyCardWidget::Private::setAllowedActions(Actions actions) 0129 { 0130 mAllowedActions = actions; 0131 update(); 0132 } 0133 0134 void OpenPGPKeyCardWidget::Private::update(const Card *card) 0135 { 0136 if (card) { 0137 updateCachedValues(OpenPGPCard::pgpSigKeyRef(), card->signingKeyRef(), card); 0138 updateCachedValues(OpenPGPCard::pgpEncKeyRef(), card->encryptionKeyRef(), card); 0139 updateCachedValues(OpenPGPCard::pgpAuthKeyRef(), card->authenticationKeyRef(), card); 0140 } 0141 updateKeyWidgets(OpenPGPCard::pgpSigKeyRef()); 0142 updateKeyWidgets(OpenPGPCard::pgpEncKeyRef()); 0143 updateKeyWidgets(OpenPGPCard::pgpAuthKeyRef()); 0144 } 0145 0146 void OpenPGPKeyCardWidget::Private::updateCachedValues(const std::string &openPGPKeyRef, const std::string &cardKeyRef, const Card *card) 0147 { 0148 KeyWidgets &widgets = mKeyWidgets.at(openPGPKeyRef); 0149 widgets.cardKeyRef = cardKeyRef; 0150 widgets.keyFingerprint = card->keyFingerprint(openPGPKeyRef); 0151 widgets.keyInfo = card->keyInfo(cardKeyRef); 0152 } 0153 0154 void OpenPGPKeyCardWidget::Private::updateKeyWidgets(const std::string &openPGPKeyRef) 0155 { 0156 const KeyWidgets &widgets = mKeyWidgets.at(openPGPKeyRef); 0157 0158 const auto cardSupportsKey = !widgets.cardKeyRef.empty(); 0159 widgets.keyTitleLabel->setVisible(cardSupportsKey); 0160 widgets.keyInfoLabel->setVisible(cardSupportsKey); 0161 widgets.showCertificateDetailsButton->setVisible(cardSupportsKey); 0162 widgets.generateButton->setVisible(cardSupportsKey && (mAllowedActions & Action::GenerateKey)); 0163 if (widgets.createCSRButton) { 0164 widgets.createCSRButton->setVisible(cardSupportsKey && (mAllowedActions & Action::CreateCSR)); 0165 } 0166 if (!cardSupportsKey) { 0167 return; 0168 } 0169 0170 widgets.showCertificateDetailsButton->setEnabled(false); 0171 0172 if (widgets.keyFingerprint.empty()) { 0173 widgets.keyInfoLabel->setTextFormat(Qt::RichText); 0174 widgets.keyInfoLabel->setText(i18nc("@info", "<em>No key</em>")); 0175 widgets.generateButton->setText(i18nc("@action:button", "Generate Key")); 0176 widgets.generateButton->setToolTip(i18nc("@info:tooltip", "Generate a key for this card slot")); 0177 if (widgets.createCSRButton) { 0178 widgets.createCSRButton->setEnabled(false); 0179 } 0180 } else { 0181 QStringList lines; 0182 if (widgets.keyFingerprint.size() >= 16) { 0183 const std::string keyid = widgets.keyFingerprint.substr(widgets.keyFingerprint.size() - 16); 0184 const auto subkeys = KeyCache::instance()->findSubkeysByKeyID({keyid}); 0185 if (subkeys.empty() || subkeys[0].isNull()) { 0186 widgets.keyInfoLabel->setTextFormat(Qt::RichText); 0187 lines.push_back(i18nc("@info", "<em>Public key not found locally</em>")); 0188 widgets.keyInfoLabel->setToolTip({}); 0189 } else { 0190 // force interpretation of text as plain text to avoid problems with HTML in user IDs 0191 widgets.keyInfoLabel->setTextFormat(Qt::PlainText); 0192 QStringList toolTips; 0193 toolTips.reserve(subkeys.size()); 0194 for (const auto &sub : subkeys) { 0195 // Yep you can have one subkey associated with multiple primary keys. 0196 const GpgME::Key key = sub.parent(); 0197 toolTips << Formatting::toolTip(key, Formatting::Validity | Formatting::ExpiryDates | Formatting::UserIDs | Formatting::Fingerprint); 0198 const auto uids = key.userIDs(); 0199 for (const auto &uid : uids) { 0200 lines.push_back(Formatting::prettyUserID(uid)); 0201 } 0202 } 0203 widgets.keyInfoLabel->setToolTip(toolTips.join(QLatin1StringView("<br/>"))); 0204 widgets.showCertificateDetailsButton->setEnabled(true); 0205 } 0206 } else { 0207 widgets.keyInfoLabel->setTextFormat(Qt::RichText); 0208 lines.push_back(i18nc("@info", "<em>Invalid fingerprint</em>")); 0209 } 0210 0211 const QString fingerprint = widgets.keyInfoLabel->textFormat() == Qt::RichText 0212 ? Formatting::prettyID(widgets.keyFingerprint.c_str()).replace(QLatin1Char(' '), QLatin1StringView(" ")) 0213 : Formatting::prettyID(widgets.keyFingerprint.c_str()); 0214 lines.insert(0, fingerprint); 0215 const auto lineSeparator = widgets.keyInfoLabel->textFormat() == Qt::PlainText ? QLatin1StringView("\n") : QLatin1String("<br>"); 0216 widgets.keyInfoLabel->setText(lines.join(lineSeparator)); 0217 0218 widgets.generateButton->setText(i18nc("@action:button", "Regenerate Key")); 0219 widgets.generateButton->setToolTip(i18nc("@info:tooltip", "Generate a new key for this card slot replacing the existing key")); 0220 if (widgets.createCSRButton) { 0221 widgets.createCSRButton->setEnabled(DeVSCompliance::algorithmIsCompliant(widgets.keyInfo.algorithm)); 0222 } 0223 } 0224 0225 widgets.generateButton->setEnabled(!widgets.generateButton->isHidden()); 0226 } 0227 0228 void OpenPGPKeyCardWidget::Private::showCertificateDetails(const std::string &openPGPKeyRef) 0229 { 0230 const KeyWidgets &widgets = mKeyWidgets.at(openPGPKeyRef); 0231 0232 if (widgets.keyFingerprint.size() >= 16) { 0233 const std::string keyid = widgets.keyFingerprint.substr(widgets.keyFingerprint.size() - 16); 0234 const auto subkeys = KeyCache::instance()->findSubkeysByKeyID({keyid}); 0235 if (!subkeys.empty() && !subkeys[0].isNull()) { 0236 auto cmd = new Commands::DetailsCommand(subkeys[0].parent()); 0237 cmd->setParentWidget(q); 0238 cmd->start(); 0239 return; 0240 } 0241 } 0242 KMessageBox::error(q, i18nc("@info", "Sorry, I cannot find the key with fingerprint %1.", Formatting::prettyID(widgets.keyFingerprint.c_str()))); 0243 } 0244 0245 OpenPGPKeyCardWidget::OpenPGPKeyCardWidget(QWidget *parent) 0246 : QWidget{parent} 0247 , d{std::make_unique<Private>(this)} 0248 { 0249 connect(KeyCache::instance().get(), &KeyCache::keysMayHaveChanged, this, [this]() { 0250 d->update(); 0251 }); 0252 } 0253 0254 OpenPGPKeyCardWidget::~OpenPGPKeyCardWidget() = default; 0255 0256 void OpenPGPKeyCardWidget::setAllowedActions(Actions actions) 0257 { 0258 d->setAllowedActions(actions); 0259 } 0260 0261 void OpenPGPKeyCardWidget::update(const Card *card) 0262 { 0263 d->update(card); 0264 } 0265 0266 #include "moc_openpgpkeycardwidget.cpp"