Page MenuHome GnuPG

No OneTemporary

diff --git a/src/commands/certificatetopivcardcommand.cpp b/src/commands/certificatetopivcardcommand.cpp
index 6f12f5a2f..d625248d9 100644
--- a/src/commands/certificatetopivcardcommand.cpp
+++ b/src/commands/certificatetopivcardcommand.cpp
@@ -1,274 +1,274 @@
/* commands/certificatetopivcardcommand.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 <config-kleopatra.h>
#include "certificatetopivcardcommand.h"
#include "cardcommand_p.h"
#include "commands/authenticatepivcardapplicationcommand.h"
#include "smartcard/pivcard.h"
#include "smartcard/readerstatus.h"
#include "smartcard/utils.h"
#include "utils/writecertassuantransaction.h"
#include <Libkleo/Compat>
#include <Libkleo/Dn>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <KLocalizedString>
#include <qgpgme/dataprovider.h>
#include <gpgme++/context.h>
#include <gpg-error.h>
#if GPG_ERROR_VERSION_NUMBER >= 0x12400 // 1.36
#define GPG_ERROR_HAS_NO_AUTH
#endif
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::SmartCard;
using namespace GpgME;
class CertificateToPIVCardCommand::Private : public CardCommand::Private
{
friend class ::Kleo::Commands::CertificateToPIVCardCommand;
CertificateToPIVCardCommand *q_func() const
{
return static_cast<CertificateToPIVCardCommand *>(q);
}
public:
- explicit Private(CertificateToPIVCardCommand *qq, const std::string &slot, const std::string &serialno);
+ explicit Private(CertificateToPIVCardCommand *qq, const std::string &slot, const std::string &serialno, QWidget *parent);
~Private() override;
private:
void start();
void startCertificateToPIVCard();
void authenticate();
void authenticationFinished();
void authenticationCanceled();
private:
std::string cardSlot;
Key certificate;
bool hasBeenCanceled = false;
};
CertificateToPIVCardCommand::Private *CertificateToPIVCardCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const CertificateToPIVCardCommand::Private *CertificateToPIVCardCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define q q_func()
#define d d_func()
-CertificateToPIVCardCommand::Private::Private(CertificateToPIVCardCommand *qq, const std::string &slot, const std::string &serialno)
- : CardCommand::Private(qq, serialno, nullptr)
+CertificateToPIVCardCommand::Private::Private(CertificateToPIVCardCommand *qq, const std::string &slot, const std::string &serialno, QWidget *parent)
+ : CardCommand::Private(qq, serialno, parent)
, cardSlot(slot)
{
}
CertificateToPIVCardCommand::Private::~Private()
{
}
namespace
{
static Key getCertificateToWriteToPIVCard(const std::string &cardSlot, const std::shared_ptr<PIVCard> &card)
{
if (!cardSlot.empty()) {
const std::string cardKeygrip = card->keyInfo(cardSlot).grip;
const auto certificate = KeyCache::instance()->findSubkeyByKeyGrip(cardKeygrip).parent();
if (certificate.isNull() || certificate.protocol() != GpgME::CMS) {
return Key();
}
if ((cardSlot == PIVCard::pivAuthenticationKeyRef() && Kleo::keyHasSign(certificate))
|| (cardSlot == PIVCard::cardAuthenticationKeyRef() && Kleo::keyHasSign(certificate))
|| (cardSlot == PIVCard::digitalSignatureKeyRef() && Kleo::keyHasSign(certificate))
|| (cardSlot == PIVCard::keyManagementKeyRef() && Kleo::keyHasEncrypt(certificate))) {
return certificate;
}
}
return Key();
}
}
void CertificateToPIVCardCommand::Private::start()
{
qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::Private::start()";
const auto pivCard = SmartCard::ReaderStatus::instance()->getCard<PIVCard>(serialNumber());
if (!pivCard) {
error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(serialNumber())));
finished();
return;
}
certificate = getCertificateToWriteToPIVCard(cardSlot, pivCard);
if (certificate.isNull()) {
error(i18n("Sorry! No suitable certificate to write to this card slot was found."));
finished();
return;
}
const QString certificateInfo = i18nc("X.509 certificate DN (validity, created: date)",
"%1 (%2, created: %3)",
DN(certificate.userID(0).id()).prettyDN(),
Formatting::complianceStringShort(certificate),
Formatting::creationDateString(certificate));
const QString slotName = cardKeyDisplayName(cardSlot);
const QString message = i18nc("@info %1 name of card slot, %2 serial number of card",
"<p>Please confirm that you want to write the following certificate to the %1 slot of card %2:</p>"
"<center>%3</center>",
!slotName.isEmpty() ? slotName : QString::fromStdString(cardSlot),
QString::fromStdString(serialNumber()),
certificateInfo);
auto confirmButton = KStandardGuiItem::ok();
confirmButton.setText(i18nc("@action:button", "Write certificate"));
confirmButton.setToolTip(QString());
const auto choice = KMessageBox::questionTwoActions(parentWidgetOrView(),
message,
i18nc("@title:window", "Write certificate to card"),
confirmButton,
KStandardGuiItem::cancel(),
QString(),
KMessageBox::Notify | KMessageBox::WindowModal);
if (choice != KMessageBox::ButtonCode::PrimaryAction) {
finished();
return;
}
startCertificateToPIVCard();
}
void CertificateToPIVCardCommand::Private::startCertificateToPIVCard()
{
qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::Private::startCertificateToPIVCard()";
auto ctx = Context::createForProtocol(GpgME::CMS);
QGpgME::QByteArrayDataProvider dp;
Data data(&dp);
const Error err = ctx->exportPublicKeys(certificate.primaryFingerprint(), data);
if (err) {
error(i18nc("@info", "Exporting the certificate failed: %1", Formatting::errorAsString(err)));
finished();
return;
}
const QByteArray certificateData = dp.data();
const auto pivCard = SmartCard::ReaderStatus::instance()->getCard<PIVCard>(serialNumber());
if (!pivCard) {
error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(serialNumber())));
finished();
return;
}
const QByteArray command = QByteArrayLiteral("SCD WRITECERT ") + QByteArray::fromStdString(cardSlot);
auto transaction = std::unique_ptr<AssuanTransaction>(new WriteCertAssuanTransaction(certificateData));
ReaderStatus::mutableInstance()->startTransaction(
pivCard,
command,
q_func(),
[this](const GpgME::Error &err) {
q->certificateToPIVCardDone(err);
},
std::move(transaction));
}
void CertificateToPIVCardCommand::Private::authenticate()
{
qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::authenticate()";
auto cmd = new AuthenticatePIVCardApplicationCommand(serialNumber(), parentWidgetOrView());
cmd->setAutoResetCardToOpenPGP(false);
connect(cmd, &AuthenticatePIVCardApplicationCommand::finished, q, [this]() {
authenticationFinished();
});
connect(cmd, &AuthenticatePIVCardApplicationCommand::canceled, q, [this]() {
authenticationCanceled();
});
cmd->start();
}
void CertificateToPIVCardCommand::Private::authenticationFinished()
{
qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::authenticationFinished()";
if (!hasBeenCanceled) {
startCertificateToPIVCard();
}
}
void CertificateToPIVCardCommand::Private::authenticationCanceled()
{
qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::authenticationCanceled()";
hasBeenCanceled = true;
canceled();
}
-CertificateToPIVCardCommand::CertificateToPIVCardCommand(const std::string &cardSlot, const std::string &serialno)
- : CardCommand(new Private(this, cardSlot, serialno))
+CertificateToPIVCardCommand::CertificateToPIVCardCommand(const std::string &cardSlot, const std::string &serialno, QWidget *parent)
+ : CardCommand(new Private(this, cardSlot, serialno, parent))
{
}
CertificateToPIVCardCommand::~CertificateToPIVCardCommand()
{
qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::~CertificateToPIVCardCommand()";
}
void CertificateToPIVCardCommand::certificateToPIVCardDone(const Error &err)
{
qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::certificateToPIVCardDone():" << Formatting::errorAsString(err) << "(" << err.code() << ")";
if (err) {
#ifdef GPG_ERROR_HAS_NO_AUTH
// gpgme 1.13 reports "BAD PIN" instead of "NO AUTH"
if (err.code() == GPG_ERR_NO_AUTH || err.code() == GPG_ERR_BAD_PIN) {
d->authenticate();
return;
}
#endif
d->error(i18nc("@info", "Writing the certificate to the card failed: %1", Formatting::errorAsString(err)));
} else if (!err.isCanceled()) {
d->success(i18nc("@info", "Writing the certificate to the card succeeded."));
}
ReaderStatus::mutableInstance()->updateStatus();
d->finished();
}
void CertificateToPIVCardCommand::doStart()
{
qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::doStart()";
d->start();
}
void CertificateToPIVCardCommand::doCancel()
{
}
#undef q_func
#undef d_func
#include "moc_certificatetopivcardcommand.cpp"
diff --git a/src/commands/certificatetopivcardcommand.h b/src/commands/certificatetopivcardcommand.h
index bb92e524e..579845108 100644
--- a/src/commands/certificatetopivcardcommand.h
+++ b/src/commands/certificatetopivcardcommand.h
@@ -1,45 +1,45 @@
/* commands/certificatetopivcardcommand.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
*/
#pragma once
#include <commands/cardcommand.h>
namespace GpgME
{
class Error;
}
namespace Kleo
{
namespace Commands
{
class CertificateToPIVCardCommand : public CardCommand
{
Q_OBJECT
public:
- CertificateToPIVCardCommand(const std::string &cardSlot, const std::string &serialno);
+ CertificateToPIVCardCommand(const std::string &cardSlot, const std::string &serialno, QWidget *parent = nullptr);
~CertificateToPIVCardCommand() override;
public Q_SLOTS:
void certificateToPIVCardDone(const GpgME::Error &err);
private:
void doStart() override;
void doCancel() override;
private:
class Private;
inline Private *d_func();
inline const Private *d_func() const;
};
}
}
diff --git a/src/commands/importcertificatefrompivcardcommand.cpp b/src/commands/importcertificatefrompivcardcommand.cpp
index 39fa0f187..51ab2db77 100644
--- a/src/commands/importcertificatefrompivcardcommand.cpp
+++ b/src/commands/importcertificatefrompivcardcommand.cpp
@@ -1,138 +1,141 @@
/* commands/importcertificatefrompivcardcommand.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 "importcertificatefrompivcardcommand.h"
#include "cardcommand_p.h"
#include "smartcard/pivcard.h"
#include "smartcard/readerstatus.h"
#include "commands/importcertificatefromdatacommand.h"
#include <KLocalizedString>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::SmartCard;
class ImportCertificateFromPIVCardCommand::Private : public CardCommand::Private
{
friend class ::Kleo::Commands::ImportCertificateFromPIVCardCommand;
ImportCertificateFromPIVCardCommand *q_func() const
{
return static_cast<ImportCertificateFromPIVCardCommand *>(q);
}
public:
- explicit Private(ImportCertificateFromPIVCardCommand *qq, const std::string &slot, const std::string &serialno);
+ explicit Private(ImportCertificateFromPIVCardCommand *qq, const std::string &slot, const std::string &serialno, QWidget *parent);
~Private() override;
private:
void start();
void importFinished();
void importCanceled();
private:
std::string cardSlot;
bool hasBeenCanceled = false;
};
ImportCertificateFromPIVCardCommand::Private *ImportCertificateFromPIVCardCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const ImportCertificateFromPIVCardCommand::Private *ImportCertificateFromPIVCardCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define q q_func()
#define d d_func()
-ImportCertificateFromPIVCardCommand::Private::Private(ImportCertificateFromPIVCardCommand *qq, const std::string &slot, const std::string &serialno)
- : CardCommand::Private(qq, serialno, nullptr)
+ImportCertificateFromPIVCardCommand::Private::Private(ImportCertificateFromPIVCardCommand *qq,
+ const std::string &slot,
+ const std::string &serialno,
+ QWidget *parent)
+ : CardCommand::Private(qq, serialno, parent)
, cardSlot(slot)
{
}
ImportCertificateFromPIVCardCommand::Private::~Private()
{
}
void ImportCertificateFromPIVCardCommand::Private::start()
{
qCDebug(KLEOPATRA_LOG) << "ImportCertificateFromPIVCardCommand::Private::start()";
const auto pivCard = ReaderStatus::instance()->getCard<PIVCard>(serialNumber());
if (!pivCard) {
error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(serialNumber())));
finished();
return;
}
const std::string certificateData = pivCard->certificateData(cardSlot);
if (certificateData.empty()) {
error(i18n("Sorry! No certificate to import from this card slot was found."));
finished();
return;
}
auto cmd = new ImportCertificateFromDataCommand(QByteArray::fromStdString(certificateData), GpgME::CMS, i18n("Card Certificate"));
connect(cmd, &ImportCertificateFromDataCommand::finished, q, [this]() {
importFinished();
});
connect(cmd, &ImportCertificateFromDataCommand::canceled, q, [this]() {
importCanceled();
});
cmd->start();
}
void ImportCertificateFromPIVCardCommand::Private::importFinished()
{
qCDebug(KLEOPATRA_LOG) << "ImportCertificateFromPIVCardCommand::importFinished()";
if (!hasBeenCanceled) {
finished();
}
}
void ImportCertificateFromPIVCardCommand::Private::importCanceled()
{
qCDebug(KLEOPATRA_LOG) << "ImportCertificateFromPIVCardCommand::importCanceled()";
hasBeenCanceled = true;
canceled();
}
-ImportCertificateFromPIVCardCommand::ImportCertificateFromPIVCardCommand(const std::string &cardSlot, const std::string &serialno)
- : CardCommand(new Private(this, cardSlot, serialno))
+ImportCertificateFromPIVCardCommand::ImportCertificateFromPIVCardCommand(const std::string &cardSlot, const std::string &serialno, QWidget *parent)
+ : CardCommand(new Private(this, cardSlot, serialno, parent))
{
}
ImportCertificateFromPIVCardCommand::~ImportCertificateFromPIVCardCommand()
{
qCDebug(KLEOPATRA_LOG) << "ImportCertificateFromPIVCardCommand::~ImportCertificateFromPIVCardCommand()";
}
void ImportCertificateFromPIVCardCommand::doStart()
{
qCDebug(KLEOPATRA_LOG) << "ImportCertificateFromPIVCardCommand::doStart()";
d->start();
}
void ImportCertificateFromPIVCardCommand::doCancel()
{
}
#undef q_func
#undef d_func
#include "moc_importcertificatefrompivcardcommand.cpp"
diff --git a/src/commands/importcertificatefrompivcardcommand.h b/src/commands/importcertificatefrompivcardcommand.h
index 0c2e70d60..a82f3f416 100644
--- a/src/commands/importcertificatefrompivcardcommand.h
+++ b/src/commands/importcertificatefrompivcardcommand.h
@@ -1,37 +1,37 @@
/* commands/importcertificatefrompivcardcommand.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
*/
#pragma once
#include "cardcommand.h"
namespace Kleo
{
namespace Commands
{
class ImportCertificateFromPIVCardCommand : public CardCommand
{
Q_OBJECT
public:
- ImportCertificateFromPIVCardCommand(const std::string &cardSlot, const std::string &serialno);
+ ImportCertificateFromPIVCardCommand(const std::string &cardSlot, const std::string &serialno, QWidget *parent = nullptr);
~ImportCertificateFromPIVCardCommand() override;
private:
void doStart() override;
void doCancel() override;
private:
class Private;
inline Private *d_func();
inline const Private *d_func() const;
};
}
}
diff --git a/src/commands/keytocardcommand.cpp b/src/commands/keytocardcommand.cpp
index 779c95f19..88ad65fdc 100644
--- a/src/commands/keytocardcommand.cpp
+++ b/src/commands/keytocardcommand.cpp
@@ -1,783 +1,783 @@
/* commands/keytocardcommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-FileCopyrightText: 2020,2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "keytocardcommand.h"
#include "cardcommand_p.h"
#include "authenticatepivcardapplicationcommand.h"
#include "smartcard/algorithminfo.h"
#include "smartcard/openpgpcard.h"
#include "smartcard/pivcard.h"
#include "smartcard/readerstatus.h"
#include "smartcard/utils.h"
#include <utils/applicationstate.h>
#include <utils/filedialog.h>
#include <Libkleo/Algorithm>
#include <Libkleo/Dn>
#include <Libkleo/Formatting>
#include <Libkleo/GnuPG>
#include <Libkleo/KeyCache>
#include <Libkleo/KeySelectionDialog>
#include <KLocalizedString>
#include <QDateTime>
#include <QDir>
#include <QInputDialog>
#include <QSaveFile>
#include <QStringList>
#include <gpg-error.h>
#if GPG_ERROR_VERSION_NUMBER >= 0x12400 // 1.36
#define GPG_ERROR_HAS_NO_AUTH
#endif
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::SmartCard;
using namespace GpgME;
namespace
{
QString cardDisplayName(const std::shared_ptr<const Card> &card)
{
return i18nc("smartcard application - serial number of smartcard", "%1 - %2", displayAppName(card->appName()), card->displaySerialNumber());
}
}
class KeyToCardCommand::Private : public CardCommand::Private
{
friend class ::Kleo::Commands::KeyToCardCommand;
KeyToCardCommand *q_func() const
{
return static_cast<KeyToCardCommand *>(q);
}
public:
explicit Private(KeyToCardCommand *qq, const GpgME::Subkey &subkey);
- explicit Private(KeyToCardCommand *qq, const std::string &slot, const std::string &serialNumber, const std::string &appName);
+ explicit Private(KeyToCardCommand *qq, const std::string &slot, const std::string &serialNumber, const std::string &appName, QWidget *parent);
private:
enum Confirmation {
AskForConfirmation,
SkipConfirmation,
};
void start();
void startKeyToOpenPGPCard();
Subkey getSubkeyToTransferToPIVCard(const std::string &cardSlot, const std::shared_ptr<PIVCard> &card);
void startKeyToPIVCard();
void authenticate();
void authenticationFinished();
void authenticationCanceled();
void keyToCardDone(const GpgME::Error &err);
void keyToPIVCardDone(const GpgME::Error &err);
void updateDone();
void keyHasBeenCopiedToCard();
void backupHasBeenCreated(const QString &backupFilename);
QString backupKey();
std::vector<QByteArray> readSecretKeyFile();
bool writeSecretKeyBackup(const QString &filename, const std::vector<QByteArray> &keydata);
void startDeleteSecretKeyLocally(Confirmation confirmation);
void deleteSecretKeyLocallyFinished(const GpgME::Error &err);
private:
std::string appName;
GpgME::Subkey subkey;
std::string cardSlot;
bool overwriteExistingAlreadyApproved = false;
bool hasBeenCanceled = false;
QMetaObject::Connection updateConnection;
};
KeyToCardCommand::Private *KeyToCardCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const KeyToCardCommand::Private *KeyToCardCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define q q_func()
#define d d_func()
KeyToCardCommand::Private::Private(KeyToCardCommand *qq, const GpgME::Subkey &subkey_)
: CardCommand::Private(qq, "", nullptr)
, subkey(subkey_)
{
}
-KeyToCardCommand::Private::Private(KeyToCardCommand *qq, const std::string &slot, const std::string &serialNumber, const std::string &appName_)
- : CardCommand::Private(qq, serialNumber, nullptr)
+KeyToCardCommand::Private::Private(KeyToCardCommand *qq, const std::string &slot, const std::string &serialNumber, const std::string &appName_, QWidget *parent)
+ : CardCommand::Private(qq, serialNumber, parent)
, appName(appName_)
, cardSlot(slot)
{
}
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(cardDisplayName(card));
}
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())));
finished();
return;
}
if (card->appName() == SmartCard::OpenPGPCard::AppName) {
startKeyToOpenPGPCard();
} else if (card->appName() == SmartCard::PIVCard::AppName) {
startKeyToPIVCard();
} else {
error(xi18nc("@info", "Sorry! Writing keys to the card <emphasis>%1</emphasis> is not supported.", cardDisplayName(card)));
finished();
return;
}
}
namespace
{
static std::string getOpenPGPCardSlotForKey(const GpgME::Subkey &subKey, QWidget *parent)
{
// Check if we need to ask the user for the slot
if ((subKey.canSign() || subKey.canCertify()) && !subKey.canEncrypt() && !subKey.canAuthenticate()) {
// Signing only
return OpenPGPCard::pgpSigKeyRef();
}
if (subKey.canEncrypt() && !(subKey.canSign() || subKey.canCertify()) && !subKey.canAuthenticate()) {
// Encrypt only
return OpenPGPCard::pgpEncKeyRef();
}
if (subKey.canAuthenticate() && !(subKey.canSign() || subKey.canCertify()) && !subKey.canEncrypt()) {
// Auth only
return OpenPGPCard::pgpAuthKeyRef();
}
// Multiple uses, ask user.
QStringList options;
std::vector<std::string> cardSlots;
if (subKey.canSign() || subKey.canCertify()) {
options.push_back(i18nc("@item:inlistbox", "Signature"));
cardSlots.push_back(OpenPGPCard::pgpSigKeyRef());
}
if (subKey.canEncrypt()) {
options.push_back(i18nc("@item:inlistbox", "Encryption"));
cardSlots.push_back(OpenPGPCard::pgpEncKeyRef());
}
if (subKey.canAuthenticate()) {
options.push_back(i18nc("@item:inlistbox", "Authentication"));
cardSlots.push_back(OpenPGPCard::pgpAuthKeyRef());
}
bool ok;
const QString choice = QInputDialog::getItem(parent,
i18n("Select Card Slot"),
i18n("Please select the card slot the key should be written to:"),
options,
/* current= */ 0,
/* editable= */ false,
&ok);
const int choiceIndex = options.indexOf(choice);
if (ok && choiceIndex >= 0) {
return cardSlots[choiceIndex];
} else {
return {};
}
}
}
void KeyToCardCommand::Private::startKeyToOpenPGPCard()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::Private::startKeyToOpenPGPCard()";
const auto pgpCard = SmartCard::ReaderStatus::instance()->getCard<OpenPGPCard>(serialNumber());
if (!pgpCard) {
error(i18n("Failed to find the OpenPGP card with the serial number: %1", QString::fromStdString(serialNumber())));
finished();
return;
}
if (subkey.isNull()) {
finished();
return;
}
if (subkey.parent().protocol() != GpgME::OpenPGP) {
error(i18n("Sorry! This key cannot be transferred to an OpenPGP card."));
finished();
return;
}
cardSlot = getOpenPGPCardSlotForKey(subkey, parentWidgetOrView());
if (cardSlot.empty()) {
finished();
return;
}
// Check if we need to do the overwrite warning.
const std::string existingKey = pgpCard->keyFingerprint(cardSlot);
if (!existingKey.empty()) {
const auto encKeyWarning = (cardSlot == OpenPGPCard::pgpEncKeyRef())
? i18n("It will no longer be possible to decrypt past communication encrypted for the existing key.")
: QString{};
const QString message = i18nc("@info",
"<p>The card <em>%1</em> already contains a key in this slot. Continuing will <b>overwrite</b> that key.</p>"
"<p>If there is no backup the existing key will be irrecoverably lost.</p>",
cardDisplayName(pgpCard))
+ i18n("The existing key has the fingerprint:") + QStringLiteral("<pre>%1</pre>").arg(Formatting::prettyID(existingKey.c_str())) + encKeyWarning;
const auto choice = KMessageBox::warningContinueCancel(parentWidgetOrView(),
message,
i18nc("@title:window", "Overwrite existing key"),
KGuiItem{i18nc("@action:button", "Overwrite Existing Key")},
KStandardGuiItem::cancel(),
QString(),
KMessageBox::Notify | KMessageBox::Dangerous);
if (choice != KMessageBox::Continue) {
finished();
return;
}
}
// Now do the deed
const auto time = QDateTime::fromSecsSinceEpoch(quint32(subkey.creationTime()), QTimeZone::utc());
const auto timestamp = time.toString(QStringLiteral("yyyyMMdd'T'HHmmss"));
const QString cmd = QStringLiteral("KEYTOCARD --force %1 %2 %3 %4")
.arg(QString::fromLatin1(subkey.keyGrip()), QString::fromStdString(serialNumber()), QString::fromStdString(cardSlot), timestamp);
ReaderStatus::mutableInstance()->startSimpleTransaction(pgpCard, cmd.toUtf8(), q_func(), [this](const GpgME::Error &err) {
keyToCardDone(err);
});
}
namespace
{
static std::vector<Key> getSigningCertificates()
{
std::vector<Key> signingCertificates = KeyCache::instance()->secretKeys();
const auto it = std::remove_if(signingCertificates.begin(), signingCertificates.end(), [](const Key &key) {
return !(key.protocol() == GpgME::CMS && !key.subkey(0).isNull() && key.subkey(0).canSign() && !key.subkey(0).canEncrypt() && key.subkey(0).isSecret()
&& !key.subkey(0).isCardKey());
});
signingCertificates.erase(it, signingCertificates.end());
return signingCertificates;
}
static std::vector<Key> getEncryptionCertificates()
{
std::vector<Key> encryptionCertificates = KeyCache::instance()->secretKeys();
const auto it = std::remove_if(encryptionCertificates.begin(), encryptionCertificates.end(), [](const Key &key) {
return !(key.protocol() == GpgME::CMS && !key.subkey(0).isNull() && key.subkey(0).canEncrypt() && key.subkey(0).isSecret()
&& !key.subkey(0).isCardKey());
});
encryptionCertificates.erase(it, encryptionCertificates.end());
return encryptionCertificates;
}
}
Subkey KeyToCardCommand::Private::getSubkeyToTransferToPIVCard(const std::string &cardSlot, const std::shared_ptr<PIVCard> & /*card*/)
{
if (cardSlot != PIVCard::cardAuthenticationKeyRef() && cardSlot != PIVCard::keyManagementKeyRef()) {
return Subkey();
}
const std::vector<Key> certificates = cardSlot == PIVCard::cardAuthenticationKeyRef() ? getSigningCertificates() : getEncryptionCertificates();
if (certificates.empty()) {
error(i18n("Sorry! No suitable certificate to write to this card slot was found."));
return Subkey();
}
auto dialog = new KeySelectionDialog(parentWidgetOrView());
dialog->setWindowTitle(i18nc("@title:window", "Select Certificate"));
dialog->setText(i18n("Please select the certificate whose key pair you want to write to the card:"));
dialog->setKeys(certificates);
if (dialog->exec() == QDialog::Rejected) {
return Subkey();
}
return dialog->selectedKey().subkey(0);
}
void KeyToCardCommand::Private::startKeyToPIVCard()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::Private::startKeyToPIVCard()";
const auto pivCard = SmartCard::ReaderStatus::instance()->getCard<PIVCard>(serialNumber());
if (!pivCard) {
error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(serialNumber())));
finished();
return;
}
if (cardSlot != PIVCard::cardAuthenticationKeyRef() && cardSlot != PIVCard::keyManagementKeyRef()) {
// key to card is only supported for the Card Authentication key and the Key Management key
finished();
return;
}
if (subkey.isNull()) {
subkey = getSubkeyToTransferToPIVCard(cardSlot, pivCard);
}
if (subkey.isNull()) {
finished();
return;
}
if (subkey.parent().protocol() != GpgME::CMS) {
error(i18n("Sorry! This key cannot be transferred to a PIV card."));
finished();
return;
}
if (!subkey.canEncrypt() && !subkey.canSign()) {
error(i18n("Sorry! Only encryption keys and signing keys can be transferred to a PIV card."));
finished();
return;
}
// Check if we need to do the overwrite warning.
if (!overwriteExistingAlreadyApproved) {
const std::string existingKey = pivCard->keyInfo(cardSlot).grip;
if (!existingKey.empty() && (existingKey != subkey.keyGrip())) {
const QString decryptionWarning = (cardSlot == PIVCard::keyManagementKeyRef())
? i18n("It will no longer be possible to decrypt past communication encrypted for the existing key.")
: QString();
const QString message = i18nc("@info",
"<p>The card <em>%1</em> already contains a key in this slot. Continuing will <b>overwrite</b> that key.</p>"
"<p>If there is no backup the existing key will be irrecoverably lost.</p>",
cardDisplayName(pivCard))
+ i18n("The existing key has the key grip:") + QStringLiteral("<pre>%1</pre>").arg(QString::fromStdString(existingKey)) + decryptionWarning;
const auto choice = KMessageBox::warningContinueCancel(parentWidgetOrView(),
message,
i18nc("@title:window", "Overwrite existing key"),
KGuiItem{i18nc("@action:button", "Overwrite Existing Key")},
KStandardGuiItem::cancel(),
QString(),
KMessageBox::Notify | KMessageBox::Dangerous);
if (choice != KMessageBox::Continue) {
finished();
return;
}
overwriteExistingAlreadyApproved = true;
}
}
const QString cmd = QStringLiteral("KEYTOCARD --force %1 %2 %3")
.arg(QString::fromLatin1(subkey.keyGrip()), QString::fromStdString(serialNumber()))
.arg(QString::fromStdString(cardSlot));
ReaderStatus::mutableInstance()->startSimpleTransaction(pivCard, cmd.toUtf8(), q_func(), [this](const GpgME::Error &err) {
keyToPIVCardDone(err);
});
}
void KeyToCardCommand::Private::authenticate()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::authenticate()";
auto cmd = new AuthenticatePIVCardApplicationCommand(serialNumber(), parentWidgetOrView());
cmd->setAutoResetCardToOpenPGP(false);
connect(cmd, &AuthenticatePIVCardApplicationCommand::finished, q, [this]() {
authenticationFinished();
});
connect(cmd, &AuthenticatePIVCardApplicationCommand::canceled, q, [this]() {
authenticationCanceled();
});
cmd->start();
}
void KeyToCardCommand::Private::authenticationFinished()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::authenticationFinished()";
if (!hasBeenCanceled) {
startKeyToPIVCard();
}
}
void KeyToCardCommand::Private::authenticationCanceled()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::authenticationCanceled()";
hasBeenCanceled = true;
canceled();
}
void KeyToCardCommand::Private::updateDone()
{
disconnect(updateConnection);
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())));
finished();
return;
}
const std::string keyGripOnCard = card->keyInfo(cardSlot).grip;
if (keyGripOnCard != subkey.keyGrip()) {
qCWarning(KLEOPATRA_LOG) << q << __func__ << "KEYTOCARD succeeded, but key on card doesn't match copied key";
error(i18nc("@info", "Copying the key to the card failed."));
finished();
return;
}
keyHasBeenCopiedToCard();
}
void KeyToCardCommand::Private::keyHasBeenCopiedToCard()
{
const auto answer = KMessageBox::questionTwoActionsCancel(parentWidgetOrView(),
xi18nc("@info",
"<para>The key has been copied to the card.</para>"
"<para>You can now delete the copy of the key stored on this computer. "
"Optionally, you can first create a backup of the key.</para>"),
i18nc("@title:window", "Success"),
KGuiItem{i18nc("@action:button", "Create backup")},
KGuiItem{i18nc("@action:button", "Delete copy on disk")},
KGuiItem{i18nc("@action:button", "Keep copy on disk")});
if (answer == KMessageBox::ButtonCode::PrimaryAction) {
const QString backupFilename = backupKey();
if (backupFilename.isEmpty()) {
// user canceled the backup or there was an error; repeat above question
QMetaObject::invokeMethod(q, [this]() {
keyHasBeenCopiedToCard();
});
}
backupHasBeenCreated(backupFilename);
} else if (answer == KMessageBox::ButtonCode::SecondaryAction) {
startDeleteSecretKeyLocally(AskForConfirmation);
} else {
finished();
}
}
void KeyToCardCommand::Private::backupHasBeenCreated(const QString &backupFilename)
{
const auto answer =
KMessageBox::questionTwoActions(parentWidgetOrView(),
xi18nc("@info",
"<para>The key has been copied to the card and a backup has been written to <filename>%1</filename>.</para>"
"<para>Do you want to delete the copy of the key stored on this computer?</para>",
backupFilename),
i18nc("@title:window", "Success"),
KGuiItem{i18nc("@action:button", "Delete copy on disk")},
KGuiItem{i18nc("@action:button", "Keep copy on disk")});
if (answer == KMessageBox::ButtonCode::PrimaryAction) {
// the user has created a backup; don't ask again for confirmation before deleting the copy on disk
startDeleteSecretKeyLocally(SkipConfirmation);
} else {
finished();
}
}
namespace
{
QString gnupgPrivateKeyBackupExtension()
{
return QStringLiteral(".gpgsk");
}
QString proposeFilename(const Subkey &subkey)
{
QString filename;
const auto key = subkey.parent();
auto name = Formatting::prettyName(key);
if (name.isEmpty()) {
name = Formatting::prettyEMail(key);
}
const auto shortKeyID = Formatting::prettyKeyID(key.shortKeyID());
const auto shortSubkeyID = Formatting::prettyKeyID(QByteArray{subkey.keyID()}.right(8).constData());
const auto usage = Formatting::usageString(subkey).replace(QLatin1StringView{", "}, QLatin1String{"_"});
/* Not translated so it's better to use in tutorials etc. */
filename = ((shortKeyID == shortSubkeyID) //
? QStringView{u"%1_%2_SECRET_KEY_BACKUP_%3"}.arg(name, shortKeyID, usage)
: QStringView{u"%1_%2_SECRET_KEY_BACKUP_%3_%4"}.arg(name, shortKeyID, shortSubkeyID, usage));
filename.replace(u'/', u'_');
return QDir{ApplicationState::lastUsedExportDirectory()}.filePath(filename + gnupgPrivateKeyBackupExtension());
}
QString requestPrivateKeyBackupFilename(const QString &proposedFilename, QWidget *parent)
{
auto filename = FileDialog::getSaveFileNameEx(parent,
i18nc("@title:window", "Backup Secret Key"),
QStringLiteral("imp"),
proposedFilename,
i18nc("description of filename filter", "Secret Key Backup Files") + QLatin1StringView{" (*.gpgsk)"});
if (!filename.isEmpty()) {
const QFileInfo fi{filename};
if (fi.suffix().isEmpty()) {
filename += gnupgPrivateKeyBackupExtension();
}
ApplicationState::setLastUsedExportDirectory(filename);
}
return filename;
}
}
QString KeyToCardCommand::Private::backupKey()
{
static const QByteArray backupInfoName = "Backup-info:";
auto keydata = readSecretKeyFile();
if (keydata.empty()) {
return {};
}
const auto filename = requestPrivateKeyBackupFilename(proposeFilename(subkey), parentWidgetOrView());
if (filename.isEmpty()) {
return {};
}
// remove old backup info
Kleo::erase_if(keydata, [](const auto &line) {
return line.startsWith(backupInfoName);
});
// prepend new backup info
const QByteArrayList backupInfo = {
backupInfoName,
subkey.keyGrip(),
QDateTime::currentDateTimeUtc().toString(Qt::ISODate).toUtf8(),
"Kleopatra",
Formatting::prettyNameAndEMail(subkey.parent()).toUtf8(),
};
keydata.insert(keydata.begin(), backupInfo.join(' ') + '\n');
if (writeSecretKeyBackup(filename, keydata)) {
return filename;
} else {
return {};
}
}
std::vector<QByteArray> KeyToCardCommand::Private::readSecretKeyFile()
{
const auto filename = QString::fromLatin1(subkey.keyGrip()) + QLatin1StringView{".key"};
const auto path = QDir{Kleo::gnupgPrivateKeysDirectory()}.filePath(filename);
QFile file{path};
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
error(xi18n("Cannot open the private key file <filename>%1</filename> for reading.", path));
return {};
}
std::vector<QByteArray> lines;
while (!file.atEnd()) {
lines.push_back(file.readLine());
}
if (lines.empty()) {
error(xi18n("The private key file <filename>%1</filename> is empty.", path));
}
return lines;
}
bool KeyToCardCommand::Private::writeSecretKeyBackup(const QString &filename, const std::vector<QByteArray> &keydata)
{
QSaveFile file{filename};
// open the file in binary format because we want to write Unix line endings
if (!file.open(QIODevice::WriteOnly)) {
error(xi18n("Cannot open the file <filename>%1</filename> for writing.", filename));
return false;
}
for (const auto &line : keydata) {
file.write(line);
}
if (!file.commit()) {
error(xi18n("Writing the backup of the secret key to <filename>%1</filename> failed.", filename));
return false;
};
return true;
}
void KeyToCardCommand::Private::startDeleteSecretKeyLocally(Confirmation confirmation)
{
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())));
finished();
return;
}
if (confirmation == AskForConfirmation) {
const auto answer = KMessageBox::questionTwoActions(parentWidgetOrView(),
xi18nc("@info", "Do you really want to delete the copy of the key stored on this computer?"),
i18nc("@title:window", "Confirm Deletion"),
KStandardGuiItem::del(),
KStandardGuiItem::cancel(),
{},
KMessageBox::Notify | KMessageBox::Dangerous);
if (answer != KMessageBox::ButtonCode::PrimaryAction) {
finished();
return;
}
}
const auto cmd = QByteArray{"DELETE_KEY --force "} + subkey.keyGrip();
ReaderStatus::mutableInstance()->startSimpleTransaction(card, cmd, q, [this](const GpgME::Error &err) {
deleteSecretKeyLocallyFinished(err);
});
}
void KeyToCardCommand::Private::deleteSecretKeyLocallyFinished(const GpgME::Error &err)
{
ReaderStatus::mutableInstance()->updateStatus();
if (err) {
error(xi18nc("@info",
"<para>Failed to delete the copy of the key stored on this computer:</para><para><message>%1</message></para>",
Formatting::errorAsString(err)));
} else if (!err.isCanceled()) {
success(i18nc("@info", "Successfully deleted the copy of the key stored on this computer."));
}
finished();
}
KeyToCardCommand::KeyToCardCommand(const GpgME::Subkey &subkey)
: CardCommand(new Private(this, subkey))
{
}
-KeyToCardCommand::KeyToCardCommand(const std::string &cardSlot, const std::string &serialNumber, const std::string &appName)
- : CardCommand(new Private(this, cardSlot, serialNumber, appName))
+KeyToCardCommand::KeyToCardCommand(const std::string &cardSlot, const std::string &serialNumber, const std::string &appName, QWidget *parent)
+ : CardCommand(new Private(this, cardSlot, serialNumber, appName, parent))
{
}
KeyToCardCommand::~KeyToCardCommand()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::~KeyToCardCommand()";
}
namespace
{
bool cardSupportsKeyAlgorithm(const std::shared_ptr<const Card> &card, const std::string &keyAlgo)
{
if (card->appName() == OpenPGPCard::AppName) {
const auto pgpCard = static_cast<const OpenPGPCard *>(card.get());
const auto cardAlgos = pgpCard->supportedAlgorithms();
return std::ranges::any_of(cardAlgos, [keyAlgo](const auto &algo) {
return (keyAlgo == algo.id) //
|| (keyAlgo == OpenPGPCard::getAlgorithmName(algo.id, OpenPGPCard::pgpEncKeyRef()))
|| (keyAlgo == OpenPGPCard::getAlgorithmName(algo.id, OpenPGPCard::pgpSigKeyRef()));
});
}
return false;
}
}
// static
std::vector<std::shared_ptr<Card>> KeyToCardCommand::getSuitableCards(const GpgME::Subkey &subkey)
{
std::vector<std::shared_ptr<Card>> suitableCards;
if (subkey.isNull() || subkey.parent().protocol() != GpgME::OpenPGP) {
return suitableCards;
}
const auto keyAlgo = subkey.algoName();
Kleo::copy_if(ReaderStatus::instance()->getCards(), std::back_inserter(suitableCards), [keyAlgo](const auto &card) {
return cardSupportsKeyAlgorithm(card, keyAlgo);
});
return suitableCards;
}
void KeyToCardCommand::Private::keyToCardDone(const GpgME::Error &err)
{
if (!err && !err.isCanceled()) {
updateConnection = connect(ReaderStatus::instance(), &ReaderStatus::updateFinished, q, [this]() {
updateDone();
});
ReaderStatus::mutableInstance()->updateCard(serialNumber(), appName);
return;
}
if (err) {
error(xi18nc("@info", "<para>Copying the key to the card failed:</para><para><message>%1</message></para>", Formatting::errorAsString(err)));
}
finished();
}
void KeyToCardCommand::Private::keyToPIVCardDone(const GpgME::Error &err)
{
qCDebug(KLEOPATRA_LOG) << q << __func__ << Formatting::errorAsString(err) << "(" << err.code() << ")";
#ifdef GPG_ERROR_HAS_NO_AUTH
// gpgme 1.13 reports "BAD PIN" instead of "NO AUTH"
if (err.code() == GPG_ERR_NO_AUTH || err.code() == GPG_ERR_BAD_PIN) {
authenticate();
return;
}
#endif
keyToCardDone(err);
}
void KeyToCardCommand::doStart()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::doStart()";
d->start();
}
void KeyToCardCommand::doCancel()
{
}
#undef q_func
#undef d_func
#include "moc_keytocardcommand.cpp"
diff --git a/src/commands/keytocardcommand.h b/src/commands/keytocardcommand.h
index a93f5aae9..a24e79623 100644
--- a/src/commands/keytocardcommand.h
+++ b/src/commands/keytocardcommand.h
@@ -1,54 +1,54 @@
/* commands/keytocardcommand.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-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <commands/cardcommand.h>
#include <gpgme++/key.h>
#include <memory>
namespace Kleo
{
namespace SmartCard
{
class Card;
}
}
namespace Kleo
{
namespace Commands
{
class KeyToCardCommand : public CardCommand
{
Q_OBJECT
public:
KeyToCardCommand(const GpgME::Subkey &subkey);
- KeyToCardCommand(const std::string &cardSlot, const std::string &serialNumber, const std::string &appName);
+ KeyToCardCommand(const std::string &cardSlot, const std::string &serialNumber, const std::string &appName, QWidget *parent = nullptr);
~KeyToCardCommand() override;
static std::vector<std::shared_ptr<Kleo::SmartCard::Card>> getSuitableCards(const GpgME::Subkey &subkey);
private:
void doStart() override;
void doCancel() override;
private:
class Private;
inline Private *d_func();
inline const Private *d_func() const;
};
}
}
diff --git a/src/view/smartcardswidget.cpp b/src/view/smartcardswidget.cpp
index 83f9fedbb..6022516a1 100644
--- a/src/view/smartcardswidget.cpp
+++ b/src/view/smartcardswidget.cpp
@@ -1,567 +1,564 @@
/* view/smartcardswidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "smartcardswidget.h"
#include "netkeywidget.h"
#include "p15cardwidget.h"
#include "pgpcardwidget.h"
#include "pivcardwidget.h"
#include "smartcardactions.h"
#include "smartcardwidget.h"
#include <commands/certificatetopivcardcommand.h>
#include <commands/changepincommand.h>
#include <commands/createcsrforcardkeycommand.h>
#include <commands/createopenpgpkeyfromcardkeyscommand.h>
#include <commands/detailscommand.h>
#include <commands/generateopenpgpcardkeysandcertificatecommand.h>
#include <commands/importcertificatefrompivcardcommand.h>
#include <commands/keytocardcommand.h>
#include <commands/openpgpgeneratecardkeycommand.h>
#include <commands/pivgeneratecardkeycommand.h>
#include <commands/setpivcardapplicationadministrationkeycommand.h>
#include "smartcard/netkeycard.h"
#include "smartcard/openpgpcard.h"
#include "smartcard/p15card.h"
#include "smartcard/pivcard.h"
#include "smartcard/readerstatus.h"
#include "smartcard/utils.h"
#include "kleopatra_debug.h"
#include <KActionCollection>
#include <KLocalizedString>
#include <QHBoxLayout>
#include <QLabel>
#include <QPointer>
#include <QStackedWidget>
#include <QTabWidget>
#include <QToolButton>
#include <QVBoxLayout>
#include <gpgme++/key.h>
using namespace GpgME;
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::SmartCard;
using namespace Qt::Literals::StringLiterals;
namespace
{
class PlaceHolderWidget : public QWidget
{
Q_OBJECT
public:
explicit PlaceHolderWidget(QWidget *parent = nullptr)
: QWidget{parent}
{
auto lay = new QVBoxLayout;
lay->addStretch(-1);
const QStringList supported{
i18nc("OpenPGP refers to a smartcard protocol", "OpenPGP v2.0 or later"),
i18nc("Gnuk is a cryptographic token for GnuPG", "Gnuk"),
i18nc("NetKey refers to a smartcard protocol", "NetKey v3 or later"),
i18nc("PIV refers to a smartcard protocol", "PIV (requires GnuPG 2.3 or later)"),
i18nc("CardOS is a smartcard operating system", "CardOS 5 (various apps)"),
};
lay->addWidget(new QLabel(QStringLiteral("\t\t<h3>") + i18n("Please insert a compatible smartcard.") + QStringLiteral("</h3>"), this));
lay->addSpacing(10);
lay->addWidget(new QLabel(QStringLiteral("\t\t") + i18n("Kleopatra currently supports the following card types:") + QStringLiteral("<ul><li>")
+ supported.join(QLatin1StringView("</li><li>")) + QStringLiteral("</li></ul>"),
this));
lay->addSpacing(10);
{
auto hbox = new QHBoxLayout;
hbox->addStretch(1);
mReloadButton = new QToolButton{this};
mReloadButton->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
mReloadButton->setDefaultAction(SmartCardActions::instance()->action(u"reload"_s));
hbox->addWidget(mReloadButton);
hbox->addStretch(1);
lay->addLayout(hbox);
}
lay->addStretch(-1);
auto hLay = new QHBoxLayout(this);
hLay->addStretch(-1);
hLay->addLayout(lay);
hLay->addStretch(-1);
lay->addStretch(-1);
connect(ReaderStatus::instance(), &ReaderStatus::currentActionChanged, this, &PlaceHolderWidget::updateReloadButton);
updateReloadButton();
}
void updateReloadButton()
{
mReloadButton->setEnabled(ReaderStatus::instance()->currentAction() != ReaderStatus::UpdateCards);
}
private:
QToolButton *mReloadButton = nullptr;
};
} // namespace
class SmartCardsWidget::Private
{
friend class ::Kleo::SmartCardsWidget;
public:
Private(SmartCardsWidget *qq);
const SmartCardWidget *currentCardWidget() const;
AppType currentCardType() const;
std::string currentSerialNumber() const;
std::string currentCardSlot() const;
GpgME::Key currentCertificate() const;
void cardAddedOrChanged(const std::string &serialNumber, const std::string &appName);
void cardRemoved(const std::string &serialNumber, const std::string &appName);
void enableCurrentWidget();
void disableCurrentWidget();
// card actions
void generateCardKeysAndOpenPGPCertificate();
void createOpenPGPCertificate();
void changePin(const std::string &keyRef, ChangePinCommand::ChangePinMode mode = ChangePinCommand::NormalMode);
void unblockOpenPGPCard();
void setPIVAdminKey();
// card slot actions
void showCertificateDetails();
void generateKey();
void createCSR();
void writeCertificateToCard();
void readCertificateFromCard();
void writeKeyToCard();
private:
template<typename C, typename W>
void cardAddedOrChanged(const std::string &serialNumber);
private:
SmartCardsWidget *const q;
QMap<std::pair<std::string, std::string>, QPointer<SmartCardWidget>> mCardWidgets;
PlaceHolderWidget *mPlaceHolderWidget;
QStackedWidget *mStack;
QTabWidget *mTabWidget;
QToolButton *mReloadButton;
};
SmartCardsWidget::Private::Private(SmartCardsWidget *qq)
: q{qq}
{
auto vLay = new QVBoxLayout(q);
mStack = new QStackedWidget{q};
vLay->addWidget(mStack);
mPlaceHolderWidget = new PlaceHolderWidget{q};
mStack->addWidget(mPlaceHolderWidget);
mTabWidget = new QTabWidget{q};
// create "Reload" button after tab widget to ensure correct tab order
mReloadButton = new QToolButton{q};
mTabWidget->setCornerWidget(mReloadButton, Qt::TopRightCorner);
mStack->addWidget(mTabWidget);
mStack->setCurrentWidget(mPlaceHolderWidget);
connect(ReaderStatus::instance(), &ReaderStatus::cardAdded, q, [this](const std::string &serialNumber, const std::string &appName) {
cardAddedOrChanged(serialNumber, appName);
});
connect(ReaderStatus::instance(), &ReaderStatus::cardChanged, q, [this](const std::string &serialNumber, const std::string &appName) {
cardAddedOrChanged(serialNumber, appName);
});
connect(ReaderStatus::instance(), &ReaderStatus::cardRemoved, q, [this](const std::string &serialNumber, const std::string &appName) {
cardRemoved(serialNumber, appName);
});
const auto actions = SmartCardActions::instance();
actions->connectAction(u"reload"_s, q, &SmartCardsWidget::reload);
mReloadButton->setDefaultAction(actions->action(u"reload"_s));
// connect card actions
actions->connectAction(u"card_all_create_openpgp_certificate"_s, q, [this]() {
createOpenPGPCertificate();
});
actions->connectAction(u"card_netkey_set_nks_pin"_s, q, [this]() {
changePin(NetKeyCard::nksPinKeyRef());
});
actions->connectAction(u"card_netkey_set_sigg_pin"_s, q, [this]() {
changePin(NetKeyCard::sigGPinKeyRef());
});
actions->connectAction(u"card_pgp_generate_keys_and_certificate"_s, q, [this]() {
generateCardKeysAndOpenPGPCertificate();
});
actions->connectAction(u"card_pgp_change_pin"_s, q, [this]() {
changePin(OpenPGPCard::pinKeyRef());
});
actions->connectAction(u"card_pgp_unblock_card"_s, q, [this]() {
unblockOpenPGPCard();
});
actions->connectAction(u"card_pgp_change_admin_pin"_s, q, [this]() {
changePin(OpenPGPCard::adminPinKeyRef());
});
actions->connectAction(u"card_pgp_change_puk"_s, q, [this]() {
changePin(OpenPGPCard::resetCodeKeyRef(), ChangePinCommand::ResetMode);
});
actions->connectAction(u"card_piv_change_pin"_s, q, [this]() {
changePin(PIVCard::pinKeyRef());
});
actions->connectAction(u"card_piv_change_puk"_s, q, [this]() {
changePin(PIVCard::pukKeyRef());
});
actions->connectAction(u"card_piv_change_admin_key"_s, q, [this]() {
setPIVAdminKey();
});
// connect card slot actions
actions->connectAction(u"card_slot_show_certificate_details"_s, q, [this]() {
showCertificateDetails();
});
actions->connectAction(u"card_slot_generate_key"_s, q, [this]() {
generateKey();
});
actions->connectAction(u"card_slot_write_key"_s, q, [this]() {
writeKeyToCard();
});
actions->connectAction(u"card_slot_write_certificate"_s, q, [this]() {
writeCertificateToCard();
});
actions->connectAction(u"card_slot_read_certificate"_s, q, [this]() {
readCertificateFromCard();
});
actions->connectAction(u"card_slot_create_csr"_s, q, [this]() {
createCSR();
});
}
const SmartCardWidget *SmartCardsWidget::Private::currentCardWidget() const
{
return qobject_cast<const SmartCardWidget *>(mTabWidget->currentWidget());
}
AppType SmartCardsWidget::Private::currentCardType() const
{
if (const SmartCardWidget *widget = currentCardWidget()) {
return widget->cardType();
}
return AppType::NoApp;
}
std::string SmartCardsWidget::Private::currentSerialNumber() const
{
if (const SmartCardWidget *widget = currentCardWidget()) {
return widget->serialNumber();
}
return {};
}
std::string SmartCardsWidget::Private::currentCardSlot() const
{
if (const SmartCardWidget *widget = currentCardWidget()) {
return widget->currentCardSlot();
}
return {};
}
GpgME::Key SmartCardsWidget::Private::currentCertificate() const
{
if (const SmartCardWidget *widget = currentCardWidget()) {
return widget->currentCertificate();
}
return {};
}
void SmartCardsWidget::Private::cardAddedOrChanged(const std::string &serialNumber, const std::string &appName)
{
if (appName == SmartCard::NetKeyCard::AppName) {
cardAddedOrChanged<NetKeyCard, NetKeyWidget>(serialNumber);
} else if (appName == SmartCard::OpenPGPCard::AppName) {
cardAddedOrChanged<OpenPGPCard, PGPCardWidget>(serialNumber);
} else if (appName == SmartCard::PIVCard::AppName) {
cardAddedOrChanged<PIVCard, PIVCardWidget>(serialNumber);
} else if (appName == SmartCard::P15Card::AppName) {
cardAddedOrChanged<P15Card, P15CardWidget>(serialNumber);
} else {
qCWarning(KLEOPATRA_LOG) << "SmartCardsWidget::Private::cardAddedOrChanged:"
<< "App" << appName.c_str() << "is not supported";
}
}
namespace
{
static QString getCardLabel(const std::shared_ptr<Card> &card)
{
if (!card->cardHolder().isEmpty()) {
return i18nc("@title:tab smartcard application - name of card holder - serial number of smartcard",
"%1 - %2 - %3",
displayAppName(card->appName()),
card->cardHolder(),
card->displaySerialNumber());
} else {
return i18nc("@title:tab smartcard application - serial number of smartcard", "%1 - %2", displayAppName(card->appName()), card->displaySerialNumber());
}
}
}
template<typename C, typename W>
void SmartCardsWidget::Private::cardAddedOrChanged(const std::string &serialNumber)
{
const auto card = ReaderStatus::instance()->getCard<C>(serialNumber);
if (!card) {
qCWarning(KLEOPATRA_LOG) << "SmartCardsWidget::Private::cardAddedOrChanged:"
<< "New or changed card" << serialNumber.c_str() << "with app" << C::AppName.c_str() << "not found";
return;
}
W *cardWidget = dynamic_cast<W *>(mCardWidgets.value({serialNumber, C::AppName}).data());
if (!cardWidget) {
cardWidget = new W;
mCardWidgets.insert({serialNumber, C::AppName}, cardWidget);
mTabWidget->addTab(cardWidget, getCardLabel(card));
if (mCardWidgets.size() == 1) {
mStack->setCurrentWidget(mTabWidget);
}
}
cardWidget->setCard(card.get());
}
void SmartCardsWidget::Private::cardRemoved(const std::string &serialNumber, const std::string &appName)
{
QWidget *cardWidget = mCardWidgets.take({serialNumber, appName});
if (cardWidget) {
const int index = mTabWidget->indexOf(cardWidget);
if (index != -1) {
mTabWidget->removeTab(index);
}
delete cardWidget;
}
if (mCardWidgets.empty()) {
mStack->setCurrentWidget(mPlaceHolderWidget);
}
}
void SmartCardsWidget::Private::enableCurrentWidget()
{
mTabWidget->currentWidget()->setEnabled(true);
}
void SmartCardsWidget::Private::disableCurrentWidget()
{
mTabWidget->currentWidget()->setEnabled(false);
}
void SmartCardsWidget::Private::generateCardKeysAndOpenPGPCertificate()
{
Q_ASSERT(currentCardType() == AppType::OpenPGPApp);
auto cmd = new GenerateOpenPGPCardKeysAndCertificateCommand(currentSerialNumber(), q->window());
disableCurrentWidget();
connect(cmd, &Command::finished, q, [this]() {
enableCurrentWidget();
});
cmd->start();
}
void SmartCardsWidget::Private::createOpenPGPCertificate()
{
const auto app = currentCardType();
Q_ASSERT(app == AppType::NetKeyApp || app == AppType::PIVApp);
const std::string serialNumber = currentSerialNumber();
Q_ASSERT(!serialNumber.empty());
auto cmd = new CreateOpenPGPKeyFromCardKeysCommand(serialNumber, appName(app), q->window());
disableCurrentWidget();
connect(cmd, &CreateOpenPGPKeyFromCardKeysCommand::finished, q, [this]() {
enableCurrentWidget();
});
cmd->start();
}
void SmartCardsWidget::Private::changePin(const std::string &keyRef, ChangePinCommand::ChangePinMode mode)
{
const auto app = currentCardType();
Q_ASSERT(app == AppType::NetKeyApp || app == AppType::OpenPGPApp || app == AppType::PIVApp);
const std::string serialNumber = currentSerialNumber();
Q_ASSERT(!serialNumber.empty());
auto cmd = new ChangePinCommand(serialNumber, appName(app), q->window());
cmd->setKeyRef(keyRef);
cmd->setMode(mode);
if (app == AppType::NetKeyApp) {
auto netKeyCard = static_cast<const NetKeyCard *>(currentCardWidget()->card());
Q_ASSERT(netKeyCard);
if ((keyRef == NetKeyCard::nksPinKeyRef() && netKeyCard->hasNKSNullPin()) //
|| (keyRef == NetKeyCard::sigGPinKeyRef() && netKeyCard->hasSigGNullPin())) {
cmd->setMode(ChangePinCommand::NullPinMode);
}
}
disableCurrentWidget();
connect(cmd, &ChangePinCommand::finished, q, [this]() {
enableCurrentWidget();
});
cmd->start();
}
void SmartCardsWidget::Private::unblockOpenPGPCard()
{
Q_ASSERT(currentCardType() == AppType::OpenPGPApp);
const auto pinCounters = currentCardWidget()->card()->pinCounters();
const bool pukIsAvailable = (pinCounters.size() == 3) && (pinCounters[1] > 0);
if (pukIsAvailable) {
// unblock card with the PUK
changePin(OpenPGPCard::resetCodeKeyRef());
} else {
// unblock card with the Admin PIN
changePin(OpenPGPCard::pinKeyRef(), ChangePinCommand::ResetMode);
}
}
void SmartCardsWidget::Private::setPIVAdminKey()
{
Q_ASSERT(currentCardType() == AppType::PIVApp);
auto cmd = new SetPIVCardApplicationAdministrationKeyCommand(currentSerialNumber(), q->window());
disableCurrentWidget();
connect(cmd, &Command::finished, q, [this]() {
enableCurrentWidget();
});
cmd->start();
}
void SmartCardsWidget::Private::showCertificateDetails()
{
const Key certificate = currentCertificate();
if (!certificate.isNull()) {
auto cmd = new DetailsCommand(certificate);
cmd->setParentWidget(q->window());
cmd->start();
}
}
static Command *createGenerateKeyCommand(AppType app, const std::string &serialNumber, const std::string &keyRef, QWidget *parent)
{
Q_ASSERT(app == AppType::OpenPGPApp || app == AppType::PIVApp);
Q_ASSERT(!serialNumber.empty());
if (app == AppType::OpenPGPApp) {
return new OpenPGPGenerateCardKeyCommand(keyRef, serialNumber, parent);
}
auto cmd = new PIVGenerateCardKeyCommand(serialNumber, parent);
cmd->setKeyRef(keyRef);
return cmd;
}
void SmartCardsWidget::Private::generateKey()
{
auto cmd = createGenerateKeyCommand(currentCardType(), currentSerialNumber(), currentCardSlot(), q->window());
disableCurrentWidget();
connect(cmd, &Command::finished, q, [this]() {
enableCurrentWidget();
});
cmd->start();
}
void SmartCardsWidget::Private::createCSR()
{
const auto app = currentCardType();
Q_ASSERT(app == AppType::NetKeyApp || app == AppType::OpenPGPApp || app == AppType::PIVApp);
const std::string serialNumber = currentSerialNumber();
Q_ASSERT(!serialNumber.empty());
const std::string keyRef = currentCardSlot();
auto cmd = new CreateCSRForCardKeyCommand(keyRef, serialNumber, appName(app), q->window());
disableCurrentWidget();
connect(cmd, &CreateCSRForCardKeyCommand::finished, q, [this]() {
enableCurrentWidget();
});
cmd->start();
}
void SmartCardsWidget::Private::writeCertificateToCard()
{
Q_ASSERT(currentCardType() == AppType::PIVApp);
const std::string serialNumber = currentSerialNumber();
Q_ASSERT(!serialNumber.empty());
const std::string keyRef = currentCardSlot();
- auto cmd = new CertificateToPIVCardCommand(keyRef, serialNumber);
+ auto cmd = new CertificateToPIVCardCommand(keyRef, serialNumber, q->window());
disableCurrentWidget();
connect(cmd, &CertificateToPIVCardCommand::finished, q, [this]() {
enableCurrentWidget();
});
- cmd->setParentWidget(q->window());
cmd->start();
}
void SmartCardsWidget::Private::readCertificateFromCard()
{
Q_ASSERT(currentCardType() == AppType::PIVApp);
const std::string serialNumber = currentSerialNumber();
Q_ASSERT(!serialNumber.empty());
const std::string keyRef = currentCardSlot();
- auto cmd = new ImportCertificateFromPIVCardCommand(keyRef, serialNumber);
+ auto cmd = new ImportCertificateFromPIVCardCommand(keyRef, serialNumber, q->window());
disableCurrentWidget();
connect(cmd, &ImportCertificateFromPIVCardCommand::finished, q, [this, keyRef]() {
// this->updateKeyWidgets(keyRef); // this should happen automatically
enableCurrentWidget();
});
- cmd->setParentWidget(q->window());
cmd->start();
}
void SmartCardsWidget::Private::writeKeyToCard()
{
Q_ASSERT(currentCardType() == AppType::PIVApp);
const std::string serialNumber = currentSerialNumber();
Q_ASSERT(!serialNumber.empty());
const std::string keyRef = currentCardSlot();
- auto cmd = new KeyToCardCommand(keyRef, serialNumber, PIVCard::AppName);
+ auto cmd = new KeyToCardCommand(keyRef, serialNumber, PIVCard::AppName, q->window());
disableCurrentWidget();
connect(cmd, &KeyToCardCommand::finished, q, [this]() {
enableCurrentWidget();
});
- cmd->setParentWidget(q->window());
cmd->start();
}
SmartCardsWidget::SmartCardsWidget(QWidget *parent)
: QWidget{parent}
, d{std::make_unique<Private>(this)}
{
connect(ReaderStatus::instance(), &ReaderStatus::currentActionChanged, this, &SmartCardsWidget::updateReloadButton);
updateReloadButton();
}
SmartCardsWidget::~SmartCardsWidget() = default;
void SmartCardsWidget::showCards(const std::vector<std::shared_ptr<Kleo::SmartCard::Card>> &cards)
{
for (const auto &card : cards) {
d->cardAddedOrChanged(card->serialNumber(), card->appName());
}
}
void SmartCardsWidget::reload()
{
ReaderStatus::mutableInstance()->updateStatus();
}
void SmartCardsWidget::updateReloadButton()
{
d->mReloadButton->setEnabled(ReaderStatus::instance()->currentAction() != ReaderStatus::UpdateCards);
}
#include "smartcardswidget.moc"
#include "moc_smartcardswidget.cpp"

File Metadata

Mime Type
text/x-diff
Expires
Thu, Feb 26, 6:26 PM (11 h, 43 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
a6/19/8d112bc1feb1936a4ad59f91fc6f

Event Timeline