Commit 81569bb8 authored by Ingo Klöcker's avatar Ingo Klöcker
Browse files

Ask user for target card if multiple cards are suitable for key to card

Previously, always the first card was taken (under the assumption that
it's the only inserted card).

CardCommand::Private:
* Add possibility for subclasses to set the serial number

KeyToCardCommand:
* Remove superfluous supported() (always returned true)
* Add static method for getting the suitable cards for a given subkey
* Ask the user which card to write the key to if multiple suitable cards
  are available

SubKeysWidget:
* Always show the "Transfer to smartcard" option if the secret subkey
  is available; disable the option if no suitable cards are available

smartcard/utils.*, view/smartcardwidget.cpp:
* Make displayAppName() available everywhere

GnuPG-bug-id: 5066
parent e866e470
Pipeline #36949 passed with stage
in 23 minutes and 43 seconds
......@@ -248,6 +248,7 @@ set(_kleopatra_SRCS
smartcard/netkeycard.cpp
smartcard/pivcard.cpp
smartcard/keypairinfo.cpp
smartcard/utils.cpp
aboutdata.cpp
systrayicon.cpp
......
......@@ -30,6 +30,12 @@ public:
return serialNumber_;
}
protected:
void setSerialNumber(const std::string &serialNumber)
{
serialNumber_ = serialNumber;
}
private:
std::string serialNumber_;
};
......
......@@ -22,6 +22,7 @@
#include "smartcard/openpgpcard.h"
#include "smartcard/pivcard.h"
#include "smartcard/readerstatus.h"
#include "smartcard/utils.h"
#include <Libkleo/Dn>
#include <Libkleo/Formatting>
......@@ -50,7 +51,7 @@ class KeyToCardCommand::Private : public CardCommand::Private
return static_cast<KeyToCardCommand *>(q);
}
public:
explicit Private(KeyToCardCommand *qq, const GpgME::Subkey &subkey, const std::string &serialNumber, const std::string &appName);
explicit Private(KeyToCardCommand *qq, const GpgME::Subkey &subkey);
explicit Private(KeyToCardCommand *qq, const std::string &slot, const std::string &serialNumber, const std::string &appName);
~Private();
......@@ -87,13 +88,8 @@ const KeyToCardCommand::Private *KeyToCardCommand::d_func() const
#define d d_func()
KeyToCardCommand::Private::Private(KeyToCardCommand *qq,
const GpgME::Subkey &subkey_,
const std::string &serialNumber,
const std::string &appName_
)
: CardCommand::Private(qq, serialNumber, nullptr)
, appName(appName_)
KeyToCardCommand::Private::Private(KeyToCardCommand *qq, const GpgME::Subkey &subkey_)
: CardCommand::Private(qq, "", nullptr)
, subkey(subkey_)
{
}
......@@ -109,10 +105,47 @@ KeyToCardCommand::Private::~Private()
{
}
namespace {
static std::shared_ptr<Card> getCardToTransferSubkeyTo(const Subkey &subkey, QWidget *parent)
{
const std::vector<std::shared_ptr<Card> > suitableCards = KeyToCardCommand::getSuitableCards(subkey);
if (suitableCards.empty()) {
return std::shared_ptr<Card>();
} else if (suitableCards.size() == 1) {
return suitableCards[0];
}
QStringList options;
for (const auto &card: suitableCards) {
options.push_back(i18nc("smartcard application - serial number of smartcard", "%1 - %2",
displayAppName(card->appName()), card->displaySerialNumber()));
}
bool ok;
const QString choice = QInputDialog::getItem(parent, i18n("Select Card"),
i18n("Please select the card the key should be written to:"), options, /* current= */ 0, /* editable= */ false, &ok);
if (!ok) {
return std::shared_ptr<Card>();
}
const int index = options.indexOf(choice);
return suitableCards[index];
}
}
void KeyToCardCommand::Private::start()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::Private::start()";
if (!subkey.isNull() && serialNumber().empty()) {
const auto card = getCardToTransferSubkeyTo(subkey, parentWidgetOrView());
if (!card) {
finished();
return;
}
setSerialNumber(card->serialNumber());
appName = card->appName();
}
const auto card = SmartCard::ReaderStatus::instance()->getCard(serialNumber(), appName);
if (!card) {
error(i18n("Failed to find the card with the serial number: %1", QString::fromStdString(serialNumber())));
......@@ -364,8 +397,8 @@ void KeyToCardCommand::Private::authenticationCanceled()
canceled();
}
KeyToCardCommand::KeyToCardCommand(const GpgME::Subkey &key, const std::string &serialNumber, const std::string &appName)
: CardCommand(new Private(this, key, serialNumber, appName))
KeyToCardCommand::KeyToCardCommand(const GpgME::Subkey &subkey)
: CardCommand(new Private(this, subkey))
{
}
......@@ -379,9 +412,19 @@ KeyToCardCommand::~KeyToCardCommand()
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::~KeyToCardCommand()";
}
bool KeyToCardCommand::supported()
// static
std::vector<std::shared_ptr<Card> > KeyToCardCommand::getSuitableCards(const GpgME::Subkey &subkey)
{
return true;
std::vector<std::shared_ptr<Card> > suitableCards;
if (subkey.isNull() || subkey.parent().protocol() != GpgME::OpenPGP) {
return suitableCards;
}
for (const auto &card: ReaderStatus::instance()->getCards()) {
if (card->appName() == OpenPGPCard::AppName) {
suitableCards.push_back(card);
}
}
return suitableCards;
}
void KeyToCardCommand::keyToOpenPGPCardDone(const GpgME::Error &err)
......
......@@ -16,6 +16,14 @@
#include <gpgme++/key.h>
namespace Kleo
{
namespace SmartCard
{
class Card;
}
}
namespace Kleo
{
namespace Commands
......@@ -25,11 +33,11 @@ class KeyToCardCommand : public CardCommand
{
Q_OBJECT
public:
KeyToCardCommand(const GpgME::Subkey &key, const std::string &serialNumber, const std::string &appName);
KeyToCardCommand(const GpgME::Subkey &subkey);
KeyToCardCommand(const std::string &cardSlot, const std::string &serialNumber, const std::string &appName);
~KeyToCardCommand() override;
static bool supported();
static std::vector<std::shared_ptr<Kleo::SmartCard::Card> > getSuitableCards(const GpgME::Subkey &subkey);
public Q_SLOTS:
void keyToOpenPGPCardDone(const GpgME::Error &err);
......
......@@ -9,9 +9,6 @@
#include "subkeyswidget.h"
#include "ui_subkeyswidget.h"
#include "smartcard/openpgpcard.h"
#include "smartcard/readerstatus.h"
#include "commands/changeexpirycommand.h"
#include "commands/keytocardcommand.h"
#include "commands/importpaperkeycommand.h"
......@@ -40,6 +37,7 @@
Q_DECLARE_METATYPE(GpgME::Subkey)
using namespace Kleo;
using namespace Kleo::Commands;
class SubKeysWidget::Private
{
......@@ -78,13 +76,13 @@ void SubKeysWidget::Private::tableContextMenuRequested(const QPoint &p)
hasActions = true;
menu->addAction(i18n("Change Expiry Date..."), q,
[this, subkey]() {
auto cmd = new Kleo::Commands::ChangeExpiryCommand(subkey.parent());
auto cmd = new ChangeExpiryCommand(subkey.parent());
if (subkey.keyID() != key.keyID()) {
// do not set the primary key as subkey
cmd->setSubkey(subkey);
}
ui.subkeysTree->setEnabled(false);
connect(cmd, &Kleo::Commands::ChangeExpiryCommand::finished,
connect(cmd, &ChangeExpiryCommand::finished,
q, [this]() {
ui.subkeysTree->setEnabled(true);
key.update();
......@@ -115,34 +113,28 @@ void SubKeysWidget::Private::tableContextMenuRequested(const QPoint &p)
menu->addAction(QIcon::fromTheme(QStringLiteral("view-certificate-import")),
i18n("Restore printed backup"),
q, [this, subkey] () {
auto cmd = new Kleo::Commands::ImportPaperKeyCommand(subkey.parent());
auto cmd = new ImportPaperKeyCommand(subkey.parent());
ui.subkeysTree->setEnabled(false);
connect(cmd, &Kleo::Commands::ImportPaperKeyCommand::finished,
connect(cmd, &ImportPaperKeyCommand::finished,
q, [this]() { ui.subkeysTree->setEnabled(true); });
cmd->setParentWidget(q);
cmd->start();
});
}
if (subkey.isSecret() && Kleo::Commands::KeyToCardCommand::supported()) {
const auto cards = SmartCard::ReaderStatus::instance()->getCards();
if (cards.size() && cards[0]->appName() == SmartCard::OpenPGPCard::AppName) {
const auto card = cards[0];
if (!subkey.cardSerialNumber() || card->serialNumber() != subkey.cardSerialNumber()) {
hasActions = true;
menu->addAction(QIcon::fromTheme(QStringLiteral("send-to-symbolic")),
i18n("Transfer to smartcard"),
q, [this, subkey, card]() {
auto cmd = new Kleo::Commands::KeyToCardCommand(subkey, card->serialNumber(), card->appName());
ui.subkeysTree->setEnabled(false);
connect(cmd, &Kleo::Commands::KeyToCardCommand::finished,
q, [this]() { ui.subkeysTree->setEnabled(true); });
cmd->setParentWidget(q);
cmd->start();
});
}
}
if (subkey.isSecret()) {
hasActions = true;
auto action = menu->addAction(QIcon::fromTheme(QStringLiteral("send-to-symbolic")),
i18n("Transfer to smartcard"),
q, [this, subkey]() {
auto cmd = new KeyToCardCommand(subkey);
ui.subkeysTree->setEnabled(false);
connect(cmd, &KeyToCardCommand::finished,
q, [this]() { ui.subkeysTree->setEnabled(true); });
cmd->setParentWidget(q);
cmd->start();
});
action->setEnabled(!KeyToCardCommand::getSuitableCards(subkey).empty());
}
if (hasActions) {
......
/* smartcard/utils.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "utils.h"
#include "netkeycard.h"
#include "openpgpcard.h"
#include "pivcard.h"
#include <KLocalizedString>
#include <QString>
using namespace Kleo::SmartCard;
QString Kleo::SmartCard::displayAppName(const std::string &appName)
{
if (appName == NetKeyCard::AppName) {
return i18nc("proper name of a type of smartcard", "NetKey");
} else if (appName == OpenPGPCard::AppName) {
return i18nc("proper name of a type of smartcard", "OpenPGP");
} else if (appName == PIVCard::AppName) {
return i18nc("proper name of a type of smartcard", "PIV");
} else {
return QString::fromStdString(appName);
}
}
/* smartcard/utils.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef SMARTCARD_UTILS_H
#define SMARTCARD_UTILS_H
#include <string>
class QString;
namespace Kleo
{
namespace SmartCard
{
QString displayAppName(const std::string &appName);
} // namespace Smartcard
} // namespace Kleopatra
#endif // SMARTCARD_UTILS_H
......@@ -15,6 +15,7 @@
#include "smartcard/openpgpcard.h"
#include "smartcard/netkeycard.h"
#include "smartcard/pivcard.h"
#include "smartcard/utils.h"
#include "view/pgpcardwidget.h"
#include "view/netkeywidget.h"
......@@ -140,22 +141,6 @@ void SmartCardWidget::Private::cardAddedOrChanged(const std::string &serialNumbe
}
}
namespace
{
QString displayAppName(const std::string &appName)
{
if (appName == SmartCard::NetKeyCard::AppName) {
return i18nc("proper name of a type of smartcard", "NetKey");
} else if (appName == SmartCard::OpenPGPCard::AppName) {
return i18nc("proper name of a type of smartcard", "OpenPGP");
} else if (appName == SmartCard::PIVCard::AppName) {
return i18nc("proper name of a type of smartcard", "PIV");
} else {
return QString::fromStdString(appName);
}
}
}
template <typename C, typename W>
void SmartCardWidget::Private::cardAddedOrChanged(const std::string &serialNumber)
{
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment