Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F23558417
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
18 KB
Subscribers
None
View Options
diff --git a/src/view/netkeywidget.cpp b/src/view/netkeywidget.cpp
index e984850ad..ded3a443c 100644
--- a/src/view/netkeywidget.cpp
+++ b/src/view/netkeywidget.cpp
@@ -1,405 +1,412 @@
/* view/netkeywidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "netkeywidget.h"
#include "keytreeview.h"
#include "kleopatraapplication.h"
#include "nullpinwidget.h"
#include "systrayicon.h"
#include "kleopatra_debug.h"
#include "smartcard/netkeycard.h"
#include "smartcard/readerstatus.h"
#include "commands/changepincommand.h"
#include "commands/createcsrforcardkeycommand.h"
#include "commands/createopenpgpkeyfromcardkeyscommand.h"
#include "commands/detailscommand.h"
#include "utils/qt-cxx20-compat.h"
+#include "view/progressoverlay.h"
#include <Libkleo/Algorithm>
#include <Libkleo/Compliance>
#include <Libkleo/Debug>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyHelpers>
#include <Libkleo/KeyListModel>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KMessageBox>
+#include <KSeparator>
#include <KSharedConfig>
#include <QGpgME/KeyListJob>
#include <QGpgME/Protocol>
#include <QHBoxLayout>
#include <QInputDialog>
#include <QLabel>
#include <QPushButton>
#include <QScrollArea>
#include <QTreeView>
#include <QVBoxLayout>
#include <gpgme++/context.h>
#include <gpgme++/engineinfo.h>
#include <gpgme++/keylistresult.h>
using namespace Kleo;
using namespace Kleo::SmartCard;
using namespace Kleo::Commands;
NetKeyWidget::NetKeyWidget(QWidget *parent)
: QWidget(parent)
, mSerialNumberLabel(new QLabel(this))
, mVersionLabel(new QLabel(this))
, mErrorLabel(new QLabel(this))
, mNullPinWidget(new NullPinWidget(this))
, mChangeNKSPINBtn(new QPushButton(this))
, mChangeSigGPINBtn(new QPushButton(this))
, mTreeView(new KeyTreeView(this))
, mArea(new QScrollArea)
{
auto vLay = new QVBoxLayout;
// Set up the scroll are
mArea->setFrameShape(QFrame::NoFrame);
mArea->setWidgetResizable(true);
auto mAreaWidget = new QWidget;
mAreaWidget->setLayout(vLay);
mArea->setWidget(mAreaWidget);
auto scrollLay = new QVBoxLayout(this);
scrollLay->setContentsMargins(0, 0, 0, 0);
scrollLay->addWidget(mArea);
// Add general widgets
mVersionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
vLay->addWidget(mVersionLabel, 0, Qt::AlignLeft);
mSerialNumberLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
auto hLay1 = new QHBoxLayout;
hLay1->addWidget(new QLabel(i18n("Serial number:")));
hLay1->addWidget(mSerialNumberLabel);
hLay1->addStretch(1);
vLay->addLayout(hLay1);
vLay->addWidget(mNullPinWidget);
- auto line1 = new QFrame();
- line1->setFrameShape(QFrame::HLine);
- vLay->addWidget(line1);
+ vLay->addWidget(new KSeparator(Qt::Horizontal));
vLay->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("Certificates:"))), 0, Qt::AlignLeft);
mErrorLabel->setVisible(false);
vLay->addWidget(mErrorLabel);
// The certificate view
mTreeView->setHierarchicalModel(AbstractKeyListModel::createHierarchicalKeyListModel(mTreeView));
mTreeView->setHierarchicalView(true);
connect(mTreeView->view(), &QAbstractItemView::doubleClicked, this, [this](const QModelIndex &idx) {
const auto klm = dynamic_cast<KeyListModelInterface *>(mTreeView->view()->model());
if (!klm) {
qCDebug(KLEOPATRA_LOG) << "Unhandled Model: " << mTreeView->view()->model()->metaObject()->className();
return;
}
auto cmd = new DetailsCommand(klm->key(idx));
cmd->setParentWidget(this);
cmd->start();
});
vLay->addWidget(mTreeView);
+ mTreeViewOverlay = new ProgressOverlay{mTreeView, this};
+ mTreeViewOverlay->hide();
+
// The action area
- auto line2 = new QFrame();
- line2->setFrameShape(QFrame::HLine);
- vLay->addWidget(line2);
+ vLay->addWidget(new KSeparator(Qt::Horizontal));
vLay->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("Actions:"))), 0, Qt::AlignLeft);
auto actionLayout = new QHBoxLayout();
if (CreateOpenPGPKeyFromCardKeysCommand::isSupported()) {
mKeyForCardKeysButton = new QPushButton(this);
mKeyForCardKeysButton->setText(i18nc("@action:button", "Create OpenPGP Key"));
mKeyForCardKeysButton->setToolTip(i18nc("@info:tooltip", "Create an OpenPGP key for the keys stored on the card."));
actionLayout->addWidget(mKeyForCardKeysButton);
connect(mKeyForCardKeysButton, &QPushButton::clicked, this, &NetKeyWidget::createKeyFromCardKeys);
}
if (!(engineInfo(GpgME::GpgSMEngine).engineVersion() < "2.2.26")) { // see https://dev.gnupg.org/T5184
mCreateCSRButton = new QPushButton(this);
mCreateCSRButton->setText(i18nc("@action:button", "Create CSR"));
mCreateCSRButton->setToolTip(i18nc("@info:tooltip", "Create a certificate signing request for a key stored on the card."));
mCreateCSRButton->setEnabled(false);
actionLayout->addWidget(mCreateCSRButton);
connect(mCreateCSRButton, &QPushButton::clicked, this, [this]() {
createCSR();
});
}
mChangeNKSPINBtn->setText(i18nc("NKS is an identifier for a type of keys on a NetKey card", "Change NKS PIN"));
mChangeSigGPINBtn->setText(i18nc("SigG is an identifier for a type of keys on a NetKey card", "Change SigG PIN"));
connect(mChangeNKSPINBtn, &QPushButton::clicked, this, [this]() {
doChangePin(NetKeyCard::nksPinKeyRef());
});
connect(mChangeSigGPINBtn, &QPushButton::clicked, this, [this]() {
doChangePin(NetKeyCard::sigGPinKeyRef());
});
actionLayout->addWidget(mChangeNKSPINBtn);
actionLayout->addWidget(mChangeSigGPINBtn);
actionLayout->addStretch(1);
vLay->addLayout(actionLayout);
vLay->addStretch(1);
const KConfigGroup configGroup(KSharedConfig::openConfig(), "NetKeyCardView");
mTreeView->restoreLayout(configGroup);
connect(KeyCache::instance().get(), &KeyCache::keysMayHaveChanged, this, &NetKeyWidget::loadCertificates);
}
NetKeyWidget::~NetKeyWidget() = default;
namespace
{
std::vector<KeyPairInfo> getKeysSuitableForCSRCreation(const NetKeyCard *netKeyCard)
{
if (netKeyCard->hasNKSNullPin()) {
return {};
}
std::vector<KeyPairInfo> keys;
Kleo::copy_if(netKeyCard->keyInfos(), std::back_inserter(keys), [](const auto &keyInfo) {
if (keyInfo.keyRef.substr(0, 9) == "NKS-SIGG.") {
// SigG certificates for qualified signatures are issued with the physical cards;
// it's not possible to request a certificate for them
return false;
}
return keyInfo.canSign() //
&& (keyInfo.keyRef.substr(0, 9) == "NKS-NKS3.") //
&& DeVSCompliance::algorithmIsCompliant(keyInfo.algorithm);
});
return keys;
}
}
void NetKeyWidget::setCard(const NetKeyCard *card)
{
mSerialNumber = card->serialNumber();
mVersionLabel->setText(i18nc("1 is a Version number", "NetKey v%1 Card", card->appVersion()));
mSerialNumberLabel->setText(card->displaySerialNumber());
mNullPinWidget->setSerialNumber(mSerialNumber);
/* According to users of NetKey Cards it is fairly uncommon
* to use SigG Certificates at all. So it should be optional to set the pins. */
mNullPinWidget->setVisible(card->hasNKSNullPin() /*|| card->hasSigGNullPin()*/);
mNullPinWidget->setSigGVisible(false /*card->hasSigGNullPin()*/);
mNullPinWidget->setNKSVisible(card->hasNKSNullPin());
mChangeNKSPINBtn->setEnabled(!card->hasNKSNullPin());
if (card->hasSigGNullPin()) {
mChangeSigGPINBtn->setText(i18nc("SigG is an identifier for a type of keys on a NetKey card", "Set SigG PIN"));
} else {
mChangeSigGPINBtn->setText(i18nc("SigG is an identifier for a type of keys on a NetKey card", "Change SigG PIN"));
}
const auto errMsg = card->errorMsg();
if (!errMsg.isEmpty()) {
mErrorLabel->setText(QStringLiteral("<b>%1:</b> %2").arg(i18n("Error"), errMsg));
mErrorLabel->setVisible(true);
} else {
mErrorLabel->setVisible(false);
}
if (mKeyForCardKeysButton) {
mKeyForCardKeysButton->setEnabled(!card->hasNKSNullPin() && card->hasSigningKey() && card->hasEncryptionKey()
&& DeVSCompliance::algorithmIsCompliant(card->keyInfo(card->signingKeyRef()).algorithm)
&& DeVSCompliance::algorithmIsCompliant(card->keyInfo(card->encryptionKeyRef()).algorithm));
}
if (mCreateCSRButton) {
mCreateCSRButton->setEnabled(!getKeysSuitableForCSRCreation(card).empty());
}
loadCertificates();
if (mCertificates.size() != card->keyInfos().size()) {
// the card contains keys we don't know; try to learn them from the card
learnCard();
}
}
void NetKeyWidget::loadCertificates()
{
qCDebug(KLEOPATRA_LOG) << __func__;
if (mSerialNumber.empty()) {
// ignore KeyCache::keysMayHaveChanged signal until the card has been set
return;
}
const auto netKeyCard = ReaderStatus::instance()->getCard<NetKeyCard>(mSerialNumber);
if (!netKeyCard) {
qCDebug(KLEOPATRA_LOG) << "Failed to find the smartcard with the serial number:" << mSerialNumber;
return;
}
const auto cardKeyInfos = netKeyCard->keyInfos();
mCertificates.clear();
mCertificates.reserve(cardKeyInfos.size());
// try to get the certificates from the key cache
for (const auto &cardKeyInfo : cardKeyInfos) {
const auto certificate = KeyCache::instance()->findSubkeyByKeyGrip(cardKeyInfo.grip).parent();
if (!certificate.isNull() && (certificate.protocol() == GpgME::CMS)) {
qCDebug(KLEOPATRA_LOG) << __func__ << "Found certificate for card key" << cardKeyInfo.grip << "in cache:" << certificate;
mCertificates.push_back(certificate);
} else {
qCDebug(KLEOPATRA_LOG) << __func__ << "Did not find certificate for card key" << cardKeyInfo.grip << "in cache";
}
}
mTreeView->setKeys(mCertificates);
ensureCertificatesAreValidated();
}
void NetKeyWidget::ensureCertificatesAreValidated()
{
if (mCertificates.empty()) {
return;
}
std::vector<GpgME::Key> certificatesToValidate;
certificatesToValidate.reserve(mCertificates.size());
Kleo::copy_if(mCertificates, std::back_inserter(certificatesToValidate), [this](const auto &cert) {
// don't bother validating certificates that have expired or are otherwise invalid
return !cert.isBad() && !mValidatedCertificates.contains(cert);
});
if (!certificatesToValidate.empty()) {
startCertificateValidation(certificatesToValidate);
mValidatedCertificates.insert(certificatesToValidate.cbegin(), certificatesToValidate.cend());
}
}
void NetKeyWidget::startCertificateValidation(const std::vector<GpgME::Key> &certificates)
{
qCDebug(KLEOPATRA_LOG) << __func__ << "Validating certificates" << certificates;
auto job = std::unique_ptr<QGpgME::KeyListJob>{QGpgME::smime()->keyListJob(false, true, true)};
auto ctx = QGpgME::Job::context(job.get());
ctx->addKeyListMode(GpgME::WithSecret);
connect(job.get(), &QGpgME::KeyListJob::result, this, &NetKeyWidget::certificateValidationDone);
job->start(Kleo::getFingerprints(certificates));
job.release();
}
void NetKeyWidget::certificateValidationDone(const GpgME::KeyListResult &result, const std::vector<GpgME::Key> &validatedCertificates)
{
qCDebug(KLEOPATRA_LOG) << __func__ << "certificates:" << validatedCertificates;
if (result.error()) {
qCDebug(KLEOPATRA_LOG) << __func__ << "Validating certificates failed:" << result.error();
return;
}
// replace the current certificates with the validated certificates
for (const auto &validatedCert : validatedCertificates) {
const auto fpr = validatedCert.primaryFingerprint();
const auto it = std::find_if(mCertificates.begin(), mCertificates.end(), [fpr](const auto &cert) {
return !qstrcmp(fpr, cert.primaryFingerprint());
});
if (it != mCertificates.end()) {
*it = validatedCert;
} else {
qCDebug(KLEOPATRA_LOG) << __func__ << "Didn't find validated certificate in certificate list:" << validatedCert;
}
}
mTreeView->setKeys(mCertificates);
}
void NetKeyWidget::learnCard()
{
qCDebug(KLEOPATRA_LOG) << __func__;
+ mTreeViewOverlay->setText(i18nc("@info", "Reading certificates from smart card ..."));
+ mTreeViewOverlay->showOverlay();
ReaderStatus::mutableInstance()->learnCards(GpgME::CMS);
+ connect(ReaderStatus::instance(), &ReaderStatus::cardsLearned, this, [this]() {
+ qCDebug(KLEOPATRA_LOG) << "ReaderStatus::cardsLearned";
+ mTreeViewOverlay->hideOverlay();
+ });
}
void NetKeyWidget::doChangePin(const std::string &keyRef)
{
const auto netKeyCard = ReaderStatus::instance()->getCard<NetKeyCard>(mSerialNumber);
if (!netKeyCard) {
KMessageBox::error(this, i18n("Failed to find the smartcard with the serial number: %1", QString::fromStdString(mSerialNumber)));
return;
}
auto cmd = new ChangePinCommand(mSerialNumber, NetKeyCard::AppName, this);
this->setEnabled(false);
connect(cmd, &ChangePinCommand::finished, this, [this]() {
this->setEnabled(true);
});
cmd->setKeyRef(keyRef);
if ((keyRef == NetKeyCard::nksPinKeyRef() && netKeyCard->hasNKSNullPin()) //
|| (keyRef == NetKeyCard::sigGPinKeyRef() && netKeyCard->hasSigGNullPin())) {
cmd->setMode(ChangePinCommand::NullPinMode);
}
cmd->start();
}
void NetKeyWidget::createKeyFromCardKeys()
{
auto cmd = new CreateOpenPGPKeyFromCardKeysCommand(mSerialNumber, NetKeyCard::AppName, this);
this->setEnabled(false);
connect(cmd, &CreateOpenPGPKeyFromCardKeysCommand::finished, this, [this]() {
this->setEnabled(true);
});
cmd->start();
}
namespace
{
std::string getKeyRef(const std::vector<KeyPairInfo> &keys, QWidget *parent)
{
QStringList options;
for (const auto &key : keys) {
options << QStringLiteral("%1 - %2").arg(QString::fromStdString(key.keyRef), QString::fromStdString(key.grip));
}
bool ok;
const QString choice = QInputDialog::getItem(parent,
i18n("Select Key"),
i18n("Please select the key you want to create a certificate signing request for:"),
options,
/* current= */ 0,
/* editable= */ false,
&ok);
return ok ? keys[options.indexOf(choice)].keyRef : std::string();
}
}
void NetKeyWidget::createCSR()
{
const auto netKeyCard = ReaderStatus::instance()->getCard<NetKeyCard>(mSerialNumber);
if (!netKeyCard) {
KMessageBox::error(this, i18n("Failed to find the smartcard with the serial number: %1", QString::fromStdString(mSerialNumber)));
return;
}
const auto suitableKeys = getKeysSuitableForCSRCreation(netKeyCard.get());
if (suitableKeys.empty()) {
KMessageBox::error(this, i18n("Sorry! No keys suitable for creating a certificate signing request found on the smartcard."));
return;
}
const auto keyRef = getKeyRef(suitableKeys, this);
if (keyRef.empty()) {
return;
}
auto cmd = new CreateCSRForCardKeyCommand(keyRef, mSerialNumber, NetKeyCard::AppName, this);
this->setEnabled(false);
connect(cmd, &CreateCSRForCardKeyCommand::finished, this, [this]() {
this->setEnabled(true);
});
cmd->start();
}
diff --git a/src/view/netkeywidget.h b/src/view/netkeywidget.h
index 1a1d4e92b..2d8e44cae 100644
--- a/src/view/netkeywidget.h
+++ b/src/view/netkeywidget.h
@@ -1,77 +1,79 @@
/* view/netkeywidget.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <Libkleo/Predicates>
#include <QWidget>
#include <gpgme++/error.h>
#include <set>
#include <string>
#include <vector>
class QLabel;
class QPushButton;
class QScrollArea;
namespace GpgME
{
class Key;
class KeyListResult;
}
namespace Kleo
{
class NullPinWidget;
class KeyTreeView;
+class ProgressOverlay;
namespace SmartCard
{
class NetKeyCard;
} // namespace SmartCard
class NetKeyWidget : public QWidget
{
Q_OBJECT
public:
explicit NetKeyWidget(QWidget *parent = nullptr);
~NetKeyWidget() override;
void setCard(const SmartCard::NetKeyCard *card);
private:
void loadCertificates();
void ensureCertificatesAreValidated();
void startCertificateValidation(const std::vector<GpgME::Key> &certificates);
void certificateValidationDone(const GpgME::KeyListResult &result, const std::vector<GpgME::Key> &keys);
void learnCard();
void doChangePin(const std::string &keyRef);
void createKeyFromCardKeys();
void createCSR();
private:
std::string mSerialNumber;
std::vector<GpgME::Key> mCertificates;
using KeySet = std::set<GpgME::Key, _detail::ByFingerprint<std::less>>;
KeySet mValidatedCertificates;
QLabel *mSerialNumberLabel = nullptr;
QLabel *mVersionLabel = nullptr;
QLabel *mErrorLabel = nullptr;
NullPinWidget *mNullPinWidget = nullptr;
QPushButton *mKeyForCardKeysButton = nullptr;
QPushButton *mCreateCSRButton = nullptr;
QPushButton *mChangeNKSPINBtn = nullptr;
QPushButton *mChangeSigGPINBtn = nullptr;
KeyTreeView *mTreeView = nullptr;
+ ProgressOverlay *mTreeViewOverlay = nullptr;
QScrollArea *mArea = nullptr;
};
} // namespace Kleo
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, May 31, 7:59 AM (8 h, 59 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
04/0f/051d5b73118a3d9e2e5a6fd8ab17
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment