File indexing completed on 2024-06-16 04:56:16
0001 /* view/p15cardwiget.cpp 0002 0003 This file is part of Kleopatra, the KDE keymanager 0004 SPDX-FileCopyrightText: 2021 g10 Code GmbH 0005 SPDX-FileContributor: Andre Heinecke <aheinecke@g10code.com> 0006 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 0011 #include "p15cardwidget.h" 0012 0013 #include "openpgpkeycardwidget.h" 0014 0015 #include "settings.h" 0016 0017 #include "smartcard/openpgpcard.h" 0018 #include "smartcard/p15card.h" 0019 #include "smartcard/readerstatus.h" 0020 0021 #include "commands/learncardkeyscommand.h" 0022 0023 #include <QGridLayout> 0024 #include <QLabel> 0025 #include <QPushButton> 0026 #include <QScrollArea> 0027 #include <QStringList> 0028 #include <QVBoxLayout> 0029 0030 #include <KLocalizedString> 0031 #include <KSeparator> 0032 0033 #include <Libkleo/Compat> 0034 #include <Libkleo/Formatting> 0035 #include <Libkleo/GnuPG> 0036 #include <Libkleo/KeyCache> 0037 0038 #include <QGpgME/CryptoConfig> 0039 #include <QGpgME/ImportFromKeyserverJob> 0040 #include <QGpgME/KeyListJob> 0041 #include <QGpgME/Protocol> 0042 #include <gpgme++/importresult.h> 0043 #include <gpgme++/keylistresult.h> 0044 0045 #include "kleopatra_debug.h" 0046 0047 using namespace Kleo; 0048 using namespace Kleo::SmartCard; 0049 using namespace Kleo::Commands; 0050 0051 P15CardWidget::P15CardWidget(QWidget *parent) 0052 : QWidget{parent} 0053 , mVersionLabel{new QLabel{this}} 0054 , mSerialNumber{new QLabel{this}} 0055 , mStatusLabel{new QLabel{this}} 0056 , mOpenPGPKeysSection{new QWidget{this}} 0057 , mOpenPGPKeysWidget{new OpenPGPKeyCardWidget{this}} 0058 { 0059 // Set up the scroll area 0060 auto myLayout = new QVBoxLayout(this); 0061 myLayout->setContentsMargins(0, 0, 0, 0); 0062 0063 auto area = new QScrollArea; 0064 area->setFrameShape(QFrame::NoFrame); 0065 area->setWidgetResizable(true); 0066 myLayout->addWidget(area); 0067 0068 auto areaWidget = new QWidget; 0069 area->setWidget(areaWidget); 0070 0071 auto areaVLay = new QVBoxLayout(areaWidget); 0072 0073 auto cardInfoGrid = new QGridLayout; 0074 { 0075 int row = 0; 0076 0077 // Version and Serialnumber 0078 cardInfoGrid->addWidget(mVersionLabel, row++, 0, 1, 2); 0079 mVersionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::TextSelectableByKeyboard); 0080 0081 cardInfoGrid->addWidget(new QLabel(i18n("Serial number:")), row, 0); 0082 cardInfoGrid->addWidget(mSerialNumber, row++, 1); 0083 mSerialNumber->setTextInteractionFlags(Qt::TextBrowserInteraction | Qt::TextSelectableByKeyboard); 0084 0085 cardInfoGrid->setColumnStretch(cardInfoGrid->columnCount(), 1); 0086 } 0087 areaVLay->addLayout(cardInfoGrid); 0088 areaVLay->addWidget(mStatusLabel); 0089 mStatusLabel->setVisible(false); 0090 0091 areaVLay->addWidget(new KSeparator(Qt::Horizontal)); 0092 0093 { 0094 auto l = new QVBoxLayout{mOpenPGPKeysSection}; 0095 l->setContentsMargins(0, 0, 0, 0); 0096 l->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("OpenPGP keys:")))); 0097 mOpenPGPKeysWidget->setAllowedActions(OpenPGPKeyCardWidget::NoAction); 0098 l->addWidget(mOpenPGPKeysWidget); 0099 l->addWidget(new KSeparator(Qt::Horizontal)); 0100 } 0101 mOpenPGPKeysSection->setVisible(false); 0102 areaVLay->addWidget(mOpenPGPKeysSection); 0103 0104 areaVLay->addStretch(1); 0105 } 0106 0107 P15CardWidget::~P15CardWidget() = default; 0108 0109 void P15CardWidget::searchPGPFpr(const std::string &fpr) 0110 { 0111 /* Only do auto import from LDAP */ 0112 auto conf = QGpgME::cryptoConfig(); 0113 Q_ASSERT(conf); 0114 if (!Settings().alwaysSearchCardOnKeyserver() && !Kleo::keyserver().startsWith(QLatin1StringView{"ldap"})) { 0115 return; 0116 } 0117 mStatusLabel->setText(i18n("Searching in directory service...")); 0118 mStatusLabel->setVisible(true); 0119 qCDebug(KLEOPATRA_LOG) << "Looking for:" << fpr.c_str() << "on ldap server"; 0120 QGpgME::KeyListJob *job = QGpgME::openpgp()->keyListJob(true); 0121 connect(KeyCache::instance().get(), &KeyCache::keysMayHaveChanged, this, [this, fpr]() { 0122 qCDebug(KLEOPATRA_LOG) << "Updating key info after changes"; 0123 ReaderStatus::mutableInstance()->updateStatus(); 0124 mOpenPGPKeysWidget->update(nullptr); 0125 }); 0126 connect(job, &QGpgME::KeyListJob::result, job, [this](GpgME::KeyListResult, std::vector<GpgME::Key> keys, QString, GpgME::Error) { 0127 if (keys.size() == 1) { 0128 auto importJob = QGpgME::openpgp()->importFromKeyserverJob(); 0129 qCDebug(KLEOPATRA_LOG) << "Importing: " << keys[0].primaryFingerprint(); 0130 connect(importJob, &QGpgME::ImportFromKeyserverJob::result, importJob, [this](GpgME::ImportResult, QString, GpgME::Error) { 0131 qCDebug(KLEOPATRA_LOG) << "import job done"; 0132 mStatusLabel->setText(i18n("Automatic import finished.")); 0133 }); 0134 importJob->start(keys); 0135 } else if (keys.size() > 1) { 0136 qCDebug(KLEOPATRA_LOG) << "Multiple keys found on server"; 0137 mStatusLabel->setText(i18n("Error multiple keys found on server.")); 0138 } else { 0139 qCDebug(KLEOPATRA_LOG) << "No key found"; 0140 mStatusLabel->setText(i18n("Key not found in directory service.")); 0141 } 0142 }); 0143 job->start(QStringList() << QString::fromStdString(fpr)); 0144 } 0145 0146 void P15CardWidget::setCard(const P15Card *card) 0147 { 0148 mCardSerialNumber = card->serialNumber(); 0149 mVersionLabel->setText(i18nc("%1 is a smartcard manufacturer", "%1 PKCS#15 card", QString::fromStdString(card->manufacturer()))); 0150 mSerialNumber->setText(card->displaySerialNumber()); 0151 mSerialNumber->setToolTip(QString::fromStdString(card->serialNumber())); 0152 0153 const auto sigInfo = card->keyInfo(card->signingKeyRef()); 0154 if (!sigInfo.grip.empty()) { 0155 const auto key = KeyCache::instance()->findSubkeyByKeyGrip(sigInfo.grip, GpgME::OpenPGP).parent(); 0156 if (key.isNull()) { 0157 qCDebug(KLEOPATRA_LOG) << "Failed to find key for grip:" << sigInfo.grip.c_str(); 0158 const auto pgpSigFpr = card->keyFingerprint(OpenPGPCard::pgpSigKeyRef()); 0159 if (!pgpSigFpr.empty()) { 0160 qCDebug(KLEOPATRA_LOG) << "Should be pgp key:" << pgpSigFpr.c_str(); 0161 searchPGPFpr(pgpSigFpr); 0162 } 0163 } else { 0164 mStatusLabel->setVisible(false); 0165 } 0166 } 0167 0168 const bool cardHasOpenPGPKeys = (!card->keyFingerprint(OpenPGPCard::pgpSigKeyRef()).empty() // 0169 || !card->keyFingerprint(OpenPGPCard::pgpEncKeyRef()).empty()); 0170 mOpenPGPKeysSection->setVisible(cardHasOpenPGPKeys); 0171 if (cardHasOpenPGPKeys) { 0172 mOpenPGPKeysWidget->update(card); 0173 } 0174 0175 /* Check if additional keys could be available */ 0176 if (!Settings().autoLoadP15Certs()) { 0177 return; 0178 } 0179 for (const auto &info : card->keyInfos()) { 0180 const auto key = KeyCache::instance()->findSubkeyByKeyGrip(info.grip); 0181 if (key.isNull()) { 0182 auto cmd = new LearnCardKeysCommand(GpgME::CMS); 0183 cmd->setParentWidget(this); 0184 cmd->setShowsOutputWindow(false); 0185 qCDebug(KLEOPATRA_LOG) << "Did not find:" << info.grip.c_str() << "Starting gpgsm --learn."; 0186 cmd->start(); 0187 connect(cmd, &Command::finished, this, []() { 0188 qCDebug(KLEOPATRA_LOG) << "Learn command finished."; 0189 }); 0190 return; 0191 } 0192 } 0193 qCDebug(KLEOPATRA_LOG) << "All certificates from card cached - Not learning."; 0194 } 0195 0196 #include "moc_p15cardwidget.cpp"