diff --git a/src/smartcard/card.cpp b/src/smartcard/card.cpp index 36cadb9b9..f0eaf6aaf 100644 --- a/src/smartcard/card.cpp +++ b/src/smartcard/card.cpp @@ -1,351 +1,346 @@ /* smartcard/card.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include "card.h" #include "readerstatus.h" #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::SmartCard; namespace { static QString formatVersion(int value) { if (value < 0) { return QString(); } const unsigned int a = ((value >> 24) & 0xff); const unsigned int b = ((value >> 16) & 0xff); const unsigned int c = ((value >> 8) & 0xff); const unsigned int d = ((value)&0xff); if (a) { return QStringLiteral("%1.%2.%3.%4").arg(QString::number(a), QString::number(b), QString::number(c), QString::number(d)); } else if (b) { return QStringLiteral("%1.%2.%3").arg(QString::number(b), QString::number(c), QString::number(d)); } else if (c) { return QStringLiteral("%1.%2").arg(QString::number(c), QString::number(d)); } return QString::number(d); } } Card::Card() { } Card::~Card() { } void Card::setStatus(Status s) { mStatus = s; } Card::Status Card::status() const { return mStatus; } void Card::setSerialNumber(const std::string &sn) { mSerialNumber = sn; } std::string Card::serialNumber() const { return mSerialNumber; } QString Card::displaySerialNumber() const { return mDisplaySerialNumber; } void Card::setDisplaySerialNumber(const QString &serialNumber) { mDisplaySerialNumber = serialNumber; } std::string Card::appName() const { return mAppName; } void Card::setAppName(const std::string &name) { mAppName = name; } void Card::setAppVersion(int version) { mAppVersion = version; } int Card::appVersion() const { return mAppVersion; } QString Card::displayAppVersion() const { return formatVersion(mAppVersion); } void Card::setManufacturer(const std::string &value) { if (!manufacturer().empty()) { qCDebug(KLEOPATRA_LOG) << "Card manufacturer is already set; overwriting existing value"; mCardInfo.erase("MANUFACTURER"); } mCardInfo.insert({"MANUFACTURER", value}); } std::string Card::manufacturer() const { return cardInfo("MANUFACTURER"); } std::string Card::cardType() const { return mCardType; } int Card::cardVersion() const { return mCardVersion; } QString Card::displayCardVersion() const { return formatVersion(mCardVersion); } QString Card::cardHolder() const { return mCardHolder; } void Card::setSigningKeyRef(const std::string &keyRef) { mSigningKeyRef = keyRef; } std::string Card::signingKeyRef() const { return mSigningKeyRef; } bool Card::hasSigningKey() const { return !keyInfo(mSigningKeyRef).grip.empty(); } void Card::setEncryptionKeyRef(const std::string &keyRef) { mEncryptionKeyRef = keyRef; } std::string Card::encryptionKeyRef() const { return mEncryptionKeyRef; } bool Card::hasEncryptionKey() const { return !keyInfo(mEncryptionKeyRef).grip.empty(); } void Card::setAuthenticationKeyRef(const std::string &keyRef) { mAuthenticationKeyRef = keyRef; } std::string Card::authenticationKeyRef() const { return mAuthenticationKeyRef; } bool Card::hasAuthenticationKey() const { return !keyInfo(mAuthenticationKeyRef).grip.empty(); } std::vector Card::pinStates() const { return mPinStates; } void Card::setPinStates(const std::vector &pinStates) { mPinStates = pinStates; } bool Card::hasNullPin() const { return mHasNullPin; } void Card::setHasNullPin(bool value) { mHasNullPin = value; } bool Card::operator==(const Card &other) const { return mHasNullPin == other.mHasNullPin && mStatus == other.mStatus && mSerialNumber == other.mSerialNumber && mAppName == other.mAppName && mAppVersion == other.mAppVersion && mCardType == other.mCardType && mCardVersion == other.mCardVersion && mCardHolder == other.mCardHolder && mSigningKeyRef == other.mSigningKeyRef && mEncryptionKeyRef == other.mEncryptionKeyRef && mAuthenticationKeyRef == other.mAuthenticationKeyRef && mPinStates == other.mPinStates && mErrMsg == other.mErrMsg && mKeyInfos == other.mKeyInfos && mCardInfo == other.mCardInfo; } bool Card::operator!=(const Card &other) const { return !operator==(other); } void Card::setErrorMsg(const QString &msg) { mErrMsg = msg; } QString Card::errorMsg() const { return mErrMsg; } void Card::setInitialKeyInfos(const std::vector &infos) { mKeyInfos = infos; } const std::vector &Card::keyInfos() const { return mKeyInfos; } const KeyPairInfo &Card::keyInfo(const std::string &keyRef) const { static const KeyPairInfo nullKey; for (const KeyPairInfo &k : mKeyInfos) { if (k.keyRef == keyRef) { return k; } } return nullKey; } void Card::setCardInfo(const std::vector> &infos) { qCDebug(KLEOPATRA_LOG) << "Card" << serialNumber().c_str() << "info:"; for (const auto &pair : infos) { qCDebug(KLEOPATRA_LOG) << pair.first.c_str() << ":" << pair.second.c_str(); parseCardInfo(pair.first, pair.second); } - processCardInfo(); } namespace { static int parseHexEncodedVersionTuple(const std::string &s) { // s is a hex-encoded, unsigned int-packed version tuple, // i.e. each byte represents one part of the version tuple bool ok; const auto version = QByteArray::fromStdString(s).toUInt(&ok, 16); return ok ? version : -1; } } void Card::parseCardInfo(const std::string &name, const std::string &value) { if (name == "APPVERSION") { mAppVersion = parseHexEncodedVersionTuple(value); } else if (name == "CARDTYPE") { mCardType = value; } else if (name == "CARDVERSION") { mCardVersion = parseHexEncodedVersionTuple(value); } else if (name == "DISP-NAME") { auto list = QString::fromUtf8(QByteArray::fromStdString(value)).split(QStringLiteral("<<"), Qt::SkipEmptyParts); std::reverse(list.begin(), list.end()); mCardHolder = list.join(QLatin1Char(' ')).replace(QLatin1Char('<'), QLatin1Char(' ')); } else if (name == "KEYPAIRINFO") { const KeyPairInfo info = KeyPairInfo::fromStatusLine(value); if (info.grip.empty()) { qCWarning(KLEOPATRA_LOG) << "Invalid KEYPAIRINFO status line" << QString::fromStdString(value); setStatus(Card::CardError); } else { updateKeyInfo(info); } } else if (name == "KEY-FPR") { // handle OpenPGP key fingerprints const auto values = QString::fromStdString(value).split(QLatin1Char(' ')); if (values.size() < 2) { qCWarning(KLEOPATRA_LOG) << "Invalid KEY-FPR status line" << QString::fromStdString(value); setStatus(Card::CardError); } const auto keyNumber = values[0]; const std::string keyRef = "OPENPGP." + keyNumber.toStdString(); const auto fpr = values[1].toStdString(); if (keyNumber == QLatin1Char('1') || keyNumber == QLatin1Char('2') || keyNumber == QLatin1Char('3')) { addCardInfo("KLEO-FPR-" + keyRef, fpr); } else { // Maybe more keyslots in the future? qCDebug(KLEOPATRA_LOG) << "Unhandled keyslot" << keyNumber; } } else if (name == "MANUFACTURER") { // the value of MANUFACTURER is the manufacturer ID as unsigned number // optionally followed by the name of the manufacturer, e.g. // 6 Yubico // 65534 unmanaged S/N range // for PKCS#15 cards the manufacturer ID is always 0, e.g. // 0 www.atos.net/cardos [R&S] const auto startOfManufacturerName = value.find(' '); if (startOfManufacturerName != std::string::npos) { addCardInfo(name, value.substr(startOfManufacturerName + 1)); } } else { mCardInfo.insert({name, value}); } } -void Card::processCardInfo() -{ -} - void Card::addCardInfo(const std::string &name, const std::string &value) { mCardInfo.insert({name, value}); } std::string Card::cardInfo(const std::string &name) const { const auto range = mCardInfo.equal_range(name); return range.first != range.second ? range.first->second : std::string(); } void Card::updateKeyInfo(const KeyPairInfo &keyPairInfo) { for (KeyPairInfo &k : mKeyInfos) { if (k.keyRef == keyPairInfo.keyRef) { k.update(keyPairInfo); return; } } mKeyInfos.push_back(keyPairInfo); } std::string Card::keyFingerprint(const std::string &keyRef) const { return cardInfo("KLEO-FPR-" + keyRef); } diff --git a/src/smartcard/card.h b/src/smartcard/card.h index d4fb49c27..86e9a7502 100644 --- a/src/smartcard/card.h +++ b/src/smartcard/card.h @@ -1,143 +1,141 @@ #pragma once /* smartcard/card.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include "keypairinfo.h" #include #include #include #include namespace Kleo { namespace SmartCard { /** Class representing an application on a smartcard or similar hardware token. */ class Card { public: enum PinState { UnknownPinState, NullPin, PinBlocked, NoPin, PinOk, NumPinStates }; enum Status { NoCard, CardPresent, CardActive, CardUsable, _NumScdStates, CardError = _NumScdStates, NumStates }; Card(); virtual ~Card(); virtual bool operator==(const Card &other) const; bool operator!=(const Card &other) const; void setStatus(Status s); Status status() const; void setSerialNumber(const std::string &sn); std::string serialNumber() const; void setCardInfo(const std::vector> &infos); QString displaySerialNumber() const; void setDisplaySerialNumber(const QString &sn); std::string appName() const; void setAppVersion(int version); int appVersion() const; QString displayAppVersion() const; void setManufacturer(const std::string &manufacturer); std::string manufacturer() const; std::string cardType() const; int cardVersion() const; QString displayCardVersion() const; QString cardHolder() const; void setSigningKeyRef(const std::string &keyRef); std::string signingKeyRef() const; bool hasSigningKey() const; void setEncryptionKeyRef(const std::string &keyRef); std::string encryptionKeyRef() const; bool hasEncryptionKey() const; void setAuthenticationKeyRef(const std::string &keyRef); std::string authenticationKeyRef() const; bool hasAuthenticationKey() const; std::vector pinStates() const; void setPinStates(const std::vector &pinStates); bool hasNullPin() const; void setHasNullPin(bool value); QString errorMsg() const; void setErrorMsg(const QString &msg); const std::vector &keyInfos() const; const KeyPairInfo &keyInfo(const std::string &keyRef) const; std::string keyFingerprint(const std::string &keyRef) const; protected: void setAppName(const std::string &name); void setInitialKeyInfos(const std::vector &infos); - virtual void processCardInfo(); - void addCardInfo(const std::string &name, const std::string &value); std::string cardInfo(const std::string &name) const; private: void parseCardInfo(const std::string &name, const std::string &value); void updateKeyInfo(const KeyPairInfo &keyPairInfo); private: bool mHasNullPin = false; Status mStatus = NoCard; std::string mSerialNumber; QString mDisplaySerialNumber; std::string mAppName; int mAppVersion = -1; std::string mCardType; int mCardVersion = -1; QString mCardHolder; std::string mSigningKeyRef; std::string mEncryptionKeyRef; std::string mAuthenticationKeyRef; std::vector mPinStates; QString mErrMsg; std::vector mKeyInfos; std::multimap mCardInfo; }; } // namespace Smartcard } // namespace Kleopatra diff --git a/src/smartcard/netkeycard.cpp b/src/smartcard/netkeycard.cpp index e9eca3cc9..dd3e1b79b 100644 --- a/src/smartcard/netkeycard.cpp +++ b/src/smartcard/netkeycard.cpp @@ -1,152 +1,73 @@ /* smartcard/netkeycard.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2017 Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include "netkeycard.h" #include "keypairinfo.h" #include "kleopatra_debug.h" #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::SmartCard; // static const std::string NetKeyCard::AppName = "nks"; -namespace -{ -static GpgME::Key lookup_key(GpgME::Context *ctx, const std::string &keyGrip) -{ - if (!ctx || keyGrip.empty()) { - return GpgME::Key(); - } - const std::string pattern = '&' + keyGrip; - qCDebug(KLEOPATRA_LOG) << "parse_keypairinfo_and_lookup_key: pattern=" << pattern.c_str(); - if (const auto err = ctx->startKeyListing(pattern.c_str())) { - qCDebug(KLEOPATRA_LOG) << "parse_keypairinfo_and_lookup_key: startKeyListing failed:" << Formatting::errorAsString(err); - return GpgME::Key(); - } - GpgME::Error e; - const auto key = ctx->nextKey(e); - ctx->endKeyListing(); - qCDebug(KLEOPATRA_LOG) << "parse_keypairinfo_and_lookup_key: e=" << e.code() << "; key.isNull()" << key.isNull(); - return key; -} - -} // namespace - NetKeyCard::NetKeyCard(const Card &card) : Card(card) { setAppName(AppName); } // static std::string NetKeyCard::nksPinKeyRef() { return std::string("PW1.CH"); } // static std::string NetKeyCard::sigGPinKeyRef() { return std::string("PW1.CH.SIG"); } -void NetKeyCard::processCardInfo() -{ - setKeyPairInfo(keyInfos()); -} - -void NetKeyCard::setKeyPairInfo(const std::vector &infos) -{ - // check that any of the keys are new - const std::unique_ptr klc(GpgME::Context::createForProtocol(GpgME::CMS)); - if (!klc.get()) { - return; - } - klc->setKeyListMode(GpgME::Ephemeral); - klc->addKeyListMode(GpgME::Validate); - - mKeys.clear(); - for (const auto &info : infos) { - const auto key = lookup_key(klc.get(), info.grip); - if (!key.isNull()) { - mKeys.push_back(key); - } - } -} - // State 0 -> NKS PIN Retry counter // State 1 -> NKS PUK Retry counter // State 2 -> SigG PIN Retry counter // State 3 -> SigG PUK Retry counter bool NetKeyCard::hasNKSNullPin() const { const auto states = pinStates(); if (states.size() < 2) { qCWarning(KLEOPATRA_LOG) << "Invalid size of pin states:" << states.size(); return false; } return states[0] == Card::NullPin; } bool NetKeyCard::hasSigGNullPin() const { const auto states = pinStates(); if (states.size() < 4) { qCWarning(KLEOPATRA_LOG) << "Invalid size of pin states:" << states.size(); return false; } return states[2] == Card::NullPin; } - -std::vector NetKeyCard::keys() const -{ - return mKeys; -} - -bool NetKeyCard::operator==(const Card &other) const -{ - static const _detail::ByFingerprint keysHaveSameFingerprint; - - if (!Card::operator==(other)) { - qCDebug(KLEOPATRA_LOG) << "NetKeyCard" << __func__ << "Card don't match"; - return false; - } - - const auto otherNetKeyCard = dynamic_cast(&other); - if (!otherNetKeyCard) { - qCWarning(KLEOPATRA_LOG) << "Failed to cast other card to NetKeyCard"; - return false; - } - if (mKeys.size() != otherNetKeyCard->mKeys.size()) { - qCDebug(KLEOPATRA_LOG) << "NetKeyCard" << __func__ << "Number of keys doesn't match"; - return false; - } - const auto otherHasKey = [otherNetKeyCard](const GpgME::Key &key) { - return Kleo::any_of(otherNetKeyCard->mKeys, [key](const GpgME::Key &otherKey) { - return keysHaveSameFingerprint(key, otherKey); - }); - }; - const bool result = Kleo::all_of(mKeys, otherHasKey); - qCDebug(KLEOPATRA_LOG) << "NetKeyCard" << __func__ << "Keys match?" << result; - return result; -} diff --git a/src/smartcard/netkeycard.h b/src/smartcard/netkeycard.h index ac28f4846..af84d2adf 100644 --- a/src/smartcard/netkeycard.h +++ b/src/smartcard/netkeycard.h @@ -1,46 +1,35 @@ #pragma once /* smartcard/netkeycard.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2017 Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include "card.h" #include namespace Kleo { namespace SmartCard { struct KeyPairInfo; /** Class to work with NetKey smartcards or compatible tokens */ class NetKeyCard : public Card { public: explicit NetKeyCard(const Card &card); static const std::string AppName; static std::string nksPinKeyRef(); static std::string sigGPinKeyRef(); bool hasSigGNullPin() const; bool hasNKSNullPin() const; - - std::vector keys() const; - -private: - void processCardInfo() override; - void setKeyPairInfo(const std::vector &infos); - - bool operator==(const Card &other) const override; - -private: - std::vector mKeys; }; } // namespace Smartcard } // namespace Kleopatra diff --git a/src/view/netkeywidget.cpp b/src/view/netkeywidget.cpp index 14404ab50..eb9ddbe5e 100644 --- a/src/view/netkeywidget.cpp +++ b/src/view/netkeywidget.cpp @@ -1,317 +1,349 @@ /* 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 "commands/learncardkeyscommand.h" #include #include +#include +#include #include #include +#include +#include #include #include #include #include #include #include #include #include -#include -#include - #include #include 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 QLabel(QStringLiteral("%1").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(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); // The action area auto line2 = new QFrame(); line2->setFrameShape(QFrame::HLine); vLay->addWidget(line2); vLay->addWidget(new QLabel(QStringLiteral("%1").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(), QStringLiteral("NetKeyCardView")); mTreeView->restoreLayout(configGroup); + + connect(KeyCache::instance().get(), &KeyCache::keysMayHaveChanged, this, &NetKeyWidget::loadCertificates); } NetKeyWidget::~NetKeyWidget() = default; namespace { std::vector getKeysSuitableForCSRCreation(const NetKeyCard *netKeyCard) { if (netKeyCard->hasNKSNullPin()) { return {}; } std::vector 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("%1: %2").arg(i18n("Error"), errMsg)); mErrorLabel->setVisible(true); } else { mErrorLabel->setVisible(false); } - const auto keys = card->keys(); - mTreeView->setKeys(keys); - 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()); } - if (card->keyInfos().size() > keys.size()) { + 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(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); +} + void NetKeyWidget::learnCard() { auto cmd = new LearnCardKeysCommand(GpgME::CMS); cmd->setParentWidget(this); cmd->setShowsOutputWindow(false); cmd->start(); } void NetKeyWidget::doChangePin(const std::string &keyRef) { const auto netKeyCard = ReaderStatus::instance()->getCard(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 &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(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(); } #include "moc_netkeywidget.cpp" diff --git a/src/view/netkeywidget.h b/src/view/netkeywidget.h index 3ed887217..2f6a0bf58 100644 --- a/src/view/netkeywidget.h +++ b/src/view/netkeywidget.h @@ -1,57 +1,66 @@ /* 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 #include #include +#include class QLabel; class QPushButton; class QScrollArea; +namespace GpgME +{ +class Key; +} + namespace Kleo { class NullPinWidget; class KeyTreeView; 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 learnCard(); void doChangePin(const std::string &keyRef); void createKeyFromCardKeys(); void createCSR(); private: std::string mSerialNumber; + std::vector mCertificates; + 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; QScrollArea *mArea = nullptr; }; } // namespace Kleo