diff --git a/src/commands/createopenpgpkeyfromcardkeyscommand.cpp b/src/commands/createopenpgpkeyfromcardkeyscommand.cpp index 306acb16b..724259161 100644 --- a/src/commands/createopenpgpkeyfromcardkeyscommand.cpp +++ b/src/commands/createopenpgpkeyfromcardkeyscommand.cpp @@ -1,231 +1,232 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/createopenpgpkeyfromcardkeyscommand.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2020 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "createopenpgpkeyfromcardkeyscommand.h" #include "cardcommand_p.h" #include "dialogs/adduseriddialog.h" +#include "smartcard/netkeycard.h" #include "smartcard/openpgpcard.h" #include "smartcard/pivcard.h" #include "smartcard/readerstatus.h" #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::Dialogs; using namespace Kleo::SmartCard; using namespace GpgME; using namespace QGpgME; class CreateOpenPGPKeyFromCardKeysCommand::Private : public CardCommand::Private { friend class ::Kleo::Commands::CreateOpenPGPKeyFromCardKeysCommand; CreateOpenPGPKeyFromCardKeysCommand *q_func() const { return static_cast(q); } public: explicit Private(CreateOpenPGPKeyFromCardKeysCommand *qq, const std::string &serialNumber, const std::string &appName, QWidget *parent); ~Private(); private: void start(); void slotDialogAccepted(); void slotDialogRejected(); void slotResult(const Error &err); void ensureDialogCreated(); private: std::string appName; QPointer dialog; }; CreateOpenPGPKeyFromCardKeysCommand::Private *CreateOpenPGPKeyFromCardKeysCommand::d_func() { return static_cast(d.get()); } const CreateOpenPGPKeyFromCardKeysCommand::Private *CreateOpenPGPKeyFromCardKeysCommand::d_func() const { return static_cast(d.get()); } #define d d_func() #define q q_func() CreateOpenPGPKeyFromCardKeysCommand::Private::Private(CreateOpenPGPKeyFromCardKeysCommand *qq, const std::string &serialNumber, const std::string &appName_, QWidget *parent) : CardCommand::Private(qq, serialNumber, parent) , appName(appName_) { } CreateOpenPGPKeyFromCardKeysCommand::Private::~Private() { } void CreateOpenPGPKeyFromCardKeysCommand::Private::start() { - if (appName != OpenPGPCard::AppName && appName != PIVCard::AppName) { + if (appName != NetKeyCard::AppName && appName != OpenPGPCard::AppName && appName != PIVCard::AppName) { qCWarning(KLEOPATRA_LOG) << "CreateOpenPGPKeyFromCardKeysCommand does not support card application" << QString::fromStdString(appName); finished(); return; } const auto card = ReaderStatus::instance()->getCard(serialNumber(), appName); if (!card) { error(i18n("Failed to find the smartcard with the serial number: %1", QString::fromStdString(serialNumber()))); finished(); return; } const auto signingKeyGrip = card->keyInfo(card->signingKeyRef()).grip; const Key signingKey = KeyCache::instance()->findSubkeyByKeyGrip(signingKeyGrip, OpenPGP).parent(); if (!signingKey.isNull()) { const QString message = i18nc("@info", "

There is already an OpenPGP key corresponding to the signing key on this card:

%1

" "

Do you still want to create an OpenPGP key for the card keys?

", Formatting::summaryLine(signingKey)); const auto choice = KMessageBox::warningContinueCancel(parentWidgetOrView(), message, i18nc("@title:window", "Create OpenPGP Key"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Notify); if (choice != KMessageBox::Continue) { finished(); return; } } ensureDialogCreated(); dialog->setWindowTitle(i18n("Enter User ID")); dialog->setName(card->cardHolder()); dialog->show(); } void CreateOpenPGPKeyFromCardKeysCommand::Private::slotDialogAccepted() { if (!(engineInfo(GpgEngine).engineVersion() < "2.3.0")) { Error err; std::unique_ptr c = Context::createForEngine(AssuanEngine, &err); if (err.code() == GPG_ERR_NOT_SUPPORTED) { finished(); return; } auto assuanContext = std::shared_ptr(c.release()); const auto resultSerialNumber = ReaderStatus::switchCard(assuanContext, serialNumber(), err); if (err || resultSerialNumber != serialNumber()) { qCWarning(KLEOPATRA_LOG) << "Switching to card" << QString::fromStdString(serialNumber()) << "failed"; finished(); return; } const auto resultAppName = ReaderStatus::switchApp(assuanContext, serialNumber(), appName, err); if (err || resultAppName != appName) { qCWarning(KLEOPATRA_LOG) << "Switching card to" << QString::fromStdString(appName) << "app failed"; finished(); return; } } const auto backend = openpgp(); if (!backend) { finished(); return; } QuickJob *const job = backend->quickJob(); if (!job) { finished(); return; } connect(job, SIGNAL(result(GpgME::Error)), q, SLOT(slotResult(GpgME::Error))); const QString userID = Formatting::prettyNameAndEMail(OpenPGP, QString(), dialog->name(), dialog->email(), dialog->comment()); const QDateTime expires = QDateTime(); const unsigned int flags = GPGME_CREATE_FORCE; job->startCreate(userID, "card", expires, Key(), flags); } void CreateOpenPGPKeyFromCardKeysCommand::Private::slotDialogRejected() { canceled(); } void CreateOpenPGPKeyFromCardKeysCommand::Private::slotResult(const Error &err) { if (err.isCanceled()) { // do nothing } else if (err) { error(i18nc("@info", "Creating an OpenPGP key from the card keys failed: %1", QString::fromUtf8(err.asString())), i18nc("@title", "Error")); } else { information(i18nc("@info", "Successfully generated an OpenPGP key from the card keys."), i18nc("@title", "Success")); } finished(); } void CreateOpenPGPKeyFromCardKeysCommand::Private::ensureDialogCreated() { if (dialog) { return; } dialog = new AddUserIDDialog; applyWindowID(dialog); dialog->setAttribute(Qt::WA_DeleteOnClose); connect(dialog, SIGNAL(accepted()), q, SLOT(slotDialogAccepted())); connect(dialog, SIGNAL(rejected()), q, SLOT(slotDialogRejected())); } CreateOpenPGPKeyFromCardKeysCommand::CreateOpenPGPKeyFromCardKeysCommand(const std::string &serialNumber, const std::string &appName, QWidget *parent) : CardCommand(new Private(this, serialNumber, appName, parent)) { } CreateOpenPGPKeyFromCardKeysCommand::~CreateOpenPGPKeyFromCardKeysCommand() { } void CreateOpenPGPKeyFromCardKeysCommand::doStart() { d->start(); } void CreateOpenPGPKeyFromCardKeysCommand::doCancel() { } #undef d #undef q #include "moc_createopenpgpkeyfromcardkeyscommand.cpp" diff --git a/src/view/netkeywidget.cpp b/src/view/netkeywidget.cpp index 71d2db93e..5e59f3d55 100644 --- a/src/view/netkeywidget.cpp +++ b/src/view/netkeywidget.cpp @@ -1,237 +1,257 @@ /* 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 "nullpinwidget.h" #include "keytreeview.h" #include "kleopatraapplication.h" #include "systrayicon.h" #include "kleopatra_debug.h" #include "smartcard/netkeycard.h" #include "smartcard/readerstatus.h" + +#include "commands/createopenpgpkeyfromcardkeyscommand.h" #include "commands/learncardkeyscommand.h" #include "commands/detailscommand.h" #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)), mLearnKeysLabel(new QLabel(this)), mErrorLabel(new QLabel(this)), mNullPinWidget(new NullPinWidget()), mLearnKeysBtn(new QPushButton(this)), + mKeyForCardKeysButton(new QPushButton(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); mLearnKeysLabel = new QLabel(i18n("There are unknown certificates on this card.")); mLearnKeysBtn->setText(i18nc("@action", "Load Certificates")); connect(mLearnKeysBtn, &QPushButton::clicked, this, [this] () { mLearnKeysBtn->setEnabled(false); auto cmd = new LearnCardKeysCommand(GpgME::CMS); cmd->setParentWidget(this); cmd->start(); auto icon = KleopatraApplication::instance()->sysTrayIcon(); if (icon) { icon->setLearningInProgress(true); } connect(cmd, &Command::finished, this, [icon] () { ReaderStatus::mutableInstance()->updateStatus(); icon->setLearningInProgress(false); }); }); auto hLay2 = new QHBoxLayout; hLay2->addWidget(mLearnKeysLabel); hLay2->addWidget(mLearnKeysBtn); hLay2->addStretch(1); vLay->addLayout(hLay2); 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), nullptr); 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(); + + 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); + 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] () { mChangeNKSPINBtn->setEnabled(false); doChangePin(false); }); connect(mChangeSigGPINBtn, &QPushButton::clicked, this, [this] () { mChangeSigGPINBtn->setEnabled(false); doChangePin(true); }); - auto hLay3 = new QHBoxLayout(); - hLay3->addWidget(mChangeNKSPINBtn); - hLay3->addWidget(mChangeSigGPINBtn); - hLay3->addStretch(1); + actionLayout->addWidget(mChangeNKSPINBtn); + actionLayout->addWidget(mChangeSigGPINBtn); + actionLayout->addStretch(1); - vLay->addLayout(hLay3); + vLay->addLayout(actionLayout); vLay->addStretch(1); } NetKeyWidget::~NetKeyWidget() { } 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(QString::fromStdString(mSerialNumber)); 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")); } mLearnKeysBtn->setEnabled(true); mLearnKeysBtn->setVisible(card->canLearnKeys()); mTreeView->setVisible(!card->canLearnKeys()); mLearnKeysLabel->setVisible(card->canLearnKeys()); 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); } void NetKeyWidget::handleResult(const GpgME::Error &err, QPushButton *btn) { btn->setEnabled(true); if (err.isCanceled()) { return; } if (err) { KMessageBox::error(this, i18nc("@info", "Failed to set PIN: %1", QString::fromLatin1(err.asString())), i18nc("@title", "Error")); return; } } void NetKeyWidget::setSigGPinSettingResult(const GpgME::Error &err) { handleResult(err, mChangeSigGPINBtn); } void NetKeyWidget::setNksPinSettingResult(const GpgME::Error &err) { handleResult(err, mChangeNKSPINBtn); } void NetKeyWidget::doChangePin(bool sigG) { const auto nksCard = ReaderStatus::instance()->getCard(mSerialNumber); if (!nksCard) { KMessageBox::error(this, i18n("Failed to find the NetKey card with the serial number: %1", QString::fromStdString(mSerialNumber))); return; } if (sigG) { ReaderStatus::mutableInstance()->startSimpleTransaction( nksCard, "SCD PASSWD PW1.CH.SIG", this, "setSigGPinSettingResult"); } else { ReaderStatus::mutableInstance()->startSimpleTransaction( nksCard, "SCD PASSWD PW1.CH", this, "setNksPinSettingResult"); } } + +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(); +} diff --git a/src/view/netkeywidget.h b/src/view/netkeywidget.h index 0a777c607..cecc71b4c 100644 --- a/src/view/netkeywidget.h +++ b/src/view/netkeywidget.h @@ -1,62 +1,64 @@ /* 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 */ #ifndef VIEW_NETKEYWIDGET_H #define VIEW_NETKEYWIDGET_H #include #include #include class QLabel; class QPushButton; class QScrollArea; 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(); void setCard(const SmartCard::NetKeyCard *card); private: void handleResult(const GpgME::Error &err, QPushButton *btn); void doChangePin(bool sigG); + void createKeyFromCardKeys(); private Q_SLOTS: void setSigGPinSettingResult(const GpgME::Error &err); void setNksPinSettingResult(const GpgME::Error &err); private: std::string mSerialNumber; QLabel *mSerialNumberLabel, *mVersionLabel, *mLearnKeysLabel, *mErrorLabel; NullPinWidget *mNullPinWidget; QPushButton *mLearnKeysBtn, + *mKeyForCardKeysButton, *mChangeNKSPINBtn, *mChangeSigGPINBtn; KeyTreeView *mTreeView; QScrollArea *mArea; }; } // namespace Kleo #endif // VIEW_NETKEYWIDGET_H