Page MenuHome GnuPG

No OneTemporary

diff --git a/src/commands/authenticatepivcardapplicationcommand.cpp b/src/commands/authenticatepivcardapplicationcommand.cpp
index def549790..c7cd5e4f9 100644
--- a/src/commands/authenticatepivcardapplicationcommand.cpp
+++ b/src/commands/authenticatepivcardapplicationcommand.cpp
@@ -1,177 +1,185 @@
/* commands/authenticatepivcardapplicationcommand.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 "authenticatepivcardapplicationcommand.h"
#include "cardcommand_p.h"
+#include "smartcard/pivcard.h"
#include "smartcard/readerstatus.h"
#include "dialogs/pivcardapplicationadministrationkeyinputdialog.h"
#include <KLocalizedString>
#include <gpgme++/error.h>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::Dialogs;
using namespace Kleo::SmartCard;
using namespace GpgME;
class AuthenticatePIVCardApplicationCommand::Private : public CardCommand::Private
{
friend class ::Kleo::Commands::AuthenticatePIVCardApplicationCommand;
AuthenticatePIVCardApplicationCommand *q_func() const
{
return static_cast<AuthenticatePIVCardApplicationCommand *>(q);
}
public:
explicit Private(AuthenticatePIVCardApplicationCommand *qq, const std::string &serialNumber, QWidget *p);
~Private();
void init();
private:
void slotResult(const Error &err);
void slotDialogAccepted();
void slotDialogRejected();
private:
void authenticate(const QByteArray& adminKey);
void retryAskingForKey();
void ensureDialogCreated();
private:
QString prompt;
QPointer<PIVCardApplicationAdministrationKeyInputDialog> dialog;
};
AuthenticatePIVCardApplicationCommand::Private *AuthenticatePIVCardApplicationCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const AuthenticatePIVCardApplicationCommand::Private *AuthenticatePIVCardApplicationCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
AuthenticatePIVCardApplicationCommand::Private::Private(AuthenticatePIVCardApplicationCommand *qq, const std::string &serialNumber, QWidget *p)
: CardCommand::Private(qq, serialNumber, p)
, dialog()
{
}
AuthenticatePIVCardApplicationCommand::Private::~Private()
{
qCDebug(KLEOPATRA_LOG) << "AuthenticatePIVCardApplicationCommand::Private::~Private()";
}
AuthenticatePIVCardApplicationCommand::AuthenticatePIVCardApplicationCommand(const std::string &serialNumber, QWidget *p)
: CardCommand(new Private(this, serialNumber, p))
{
d->init();
}
void AuthenticatePIVCardApplicationCommand::Private::init()
{
}
AuthenticatePIVCardApplicationCommand::~AuthenticatePIVCardApplicationCommand()
{
qCDebug(KLEOPATRA_LOG) << "AuthenticatePIVCardApplicationCommand::~AuthenticatePIVCardApplicationCommand()";
}
void AuthenticatePIVCardApplicationCommand::setPrompt(const QString& prompt)
{
d->prompt = prompt;
}
void AuthenticatePIVCardApplicationCommand::doStart()
{
qCDebug(KLEOPATRA_LOG) << "AuthenticatePIVCardApplicationCommand::doStart()";
// at first, try to authenticate using the default application administration key
d->authenticate(QByteArray::fromHex("010203040506070801020304050607080102030405060708"));
}
void AuthenticatePIVCardApplicationCommand::doCancel()
{
}
void AuthenticatePIVCardApplicationCommand::Private::authenticate(const QByteArray& adminKey)
{
qCDebug(KLEOPATRA_LOG) << "AuthenticatePIVCardApplicationCommand::authenticate()";
+ 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 plusPercentEncodedAdminKey = adminKey.toPercentEncoding().replace(' ', '+');
const QByteArray command = QByteArray("SCD SETATTR AUTH-ADM-KEY ") + plusPercentEncodedAdminKey;
- ReaderStatus::mutableInstance()->startSimpleTransaction(command, q, "slotResult");
+ ReaderStatus::mutableInstance()->startSimpleTransaction(pivCard, command, q, "slotResult");
}
void AuthenticatePIVCardApplicationCommand::Private::slotResult(const Error &err)
{
qCDebug(KLEOPATRA_LOG) << "AuthenticatePIVCardApplicationCommand::slotResult():"
<< err.asString() << "(" << err.code() << ")";
if (err.isCanceled()) {
canceled();
return;
}
if (err) {
if (err.code() == GPG_ERR_BAD_AUTH) {
retryAskingForKey();
return;
}
error(i18nc("@info", "Authenticating to the card failed: %1", QString::fromLatin1(err.asString())),
i18nc("@title", "Error"));
}
finished();
}
void AuthenticatePIVCardApplicationCommand::Private::retryAskingForKey()
{
ensureDialogCreated();
Q_ASSERT(dialog);
dialog->show();
}
void AuthenticatePIVCardApplicationCommand::Private::ensureDialogCreated()
{
if (dialog) {
return;
}
dialog = new PIVCardApplicationAdministrationKeyInputDialog(parentWidgetOrView());
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setLabelText(prompt.isEmpty() ?
i18n("Please enter the PIV Card Application Administration Key in hex-encoded form.") :
prompt);
connect(dialog, SIGNAL(accepted()), q, SLOT(slotDialogAccepted()));
connect(dialog, SIGNAL(rejected()), q, SLOT(slotDialogRejected()));
}
void AuthenticatePIVCardApplicationCommand::Private::slotDialogAccepted()
{
authenticate(dialog->adminKey());
}
void AuthenticatePIVCardApplicationCommand::Private::slotDialogRejected()
{
canceled();
}
#undef d
#undef q
#include "moc_authenticatepivcardapplicationcommand.cpp"
diff --git a/src/commands/certificatetopivcardcommand.cpp b/src/commands/certificatetopivcardcommand.cpp
index b66548abf..79664dcae 100644
--- a/src/commands/certificatetopivcardcommand.cpp
+++ b/src/commands/certificatetopivcardcommand.cpp
@@ -1,244 +1,250 @@
/* 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 "utils/writecertassuantransaction.h"
#include <Libkleo/Dn>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <KLocalizedString>
#include <qgpgme/dataprovider.h>
#include <gpgme++/context.h>
#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);
~Private();
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)
, 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->keyGrip(cardSlot);
const auto certificate = KeyCache::instance()->findSubkeyByKeyGrip(cardKeygrip).parent();
if (certificate.isNull() || certificate.protocol() != GpgME::CMS) {
return Key();
}
if ((cardSlot == PIVCard::pivAuthenticationKeyRef() && certificate.canSign()) ||
(cardSlot == PIVCard::cardAuthenticationKeyRef() && certificate.canSign()) ||
(cardSlot == PIVCard::digitalSignatureKeyRef() && certificate.canSign()) ||
(cardSlot == PIVCard::keyManagementKeyRef() && certificate.canEncrypt())) {
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 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>",
PIVCard::keyDisplayName(cardSlot), QString::fromStdString(serialNumber()), certificateInfo);
auto confirmButton = KStandardGuiItem::yes();
confirmButton.setText(i18nc("@action:button", "Write certificate"));
confirmButton.setToolTip(QString());
const auto choice = KMessageBox::questionYesNo(
parentWidgetOrView(), message, i18nc("@title:window", "Write certificate to card"),
confirmButton, KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::WindowModal);
if (choice != KMessageBox::Yes) {
finished();
return;
}
startCertificateToPIVCard();
}
void CertificateToPIVCardCommand::Private::startCertificateToPIVCard()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::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", QString::fromUtf8(err.asString())),
i18nc("@title", "Error"));
finished();
return;
}
const QByteArray certificateData = dp.data();
- const QString cmd = QStringLiteral("SCD WRITECERT %1")
- .arg(QString::fromStdString(cardSlot));
+ 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(cmd.toUtf8(), q_func(), "certificateToPIVCardDone", std::move(transaction));
+ ReaderStatus::mutableInstance()->startTransaction(pivCard, command, q_func(), "certificateToPIVCardDone", std::move(transaction));
}
void CertificateToPIVCardCommand::Private::authenticate()
{
qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::authenticate()";
auto cmd = new AuthenticatePIVCardApplicationCommand(serialNumber(), parentWidgetOrView());
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()
{
qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::~CertificateToPIVCardCommand()";
}
void CertificateToPIVCardCommand::certificateToPIVCardDone(const Error &err)
{
qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::certificateToPIVCardDone():"
<< err.asString() << "(" << err.code() << ")";
if (err) {
// 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;
}
d->error(i18nc("@info", "Writing the certificate to the card failed: %1", QString::fromUtf8(err.asString())),
i18nc("@title", "Error"));
} else if (!err.isCanceled()) {
KMessageBox::information(d->parentWidgetOrView(),
i18nc("@info", "Writing the certificate to the card succeeded."),
i18nc("@title", "Success"));
ReaderStatus::mutableInstance()->updateStatus();
}
d->finished();
}
void CertificateToPIVCardCommand::doStart()
{
qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::doStart()";
d->start();
}
void CertificateToPIVCardCommand::doCancel()
{
}
#undef q_func
#undef d_func
diff --git a/src/commands/changepincommand.cpp b/src/commands/changepincommand.cpp
index 98ed08373..fec5ac603 100644
--- a/src/commands/changepincommand.cpp
+++ b/src/commands/changepincommand.cpp
@@ -1,172 +1,185 @@
/* commands/changepincommand.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 "changepincommand.h"
#include "cardcommand_p.h"
#include "smartcard/openpgpcard.h"
#include "smartcard/pivcard.h"
#include "smartcard/readerstatus.h"
#include <KLocalizedString>
#include <gpgme++/error.h>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::SmartCard;
using namespace GpgME;
class ChangePinCommand::Private : public CardCommand::Private
{
friend class ::Kleo::Commands::ChangePinCommand;
ChangePinCommand *q_func() const
{
return static_cast<ChangePinCommand *>(q);
}
public:
explicit Private(ChangePinCommand *qq, const std::string &serialNumber, QWidget *p);
~Private();
void init();
private:
void slotResult(const Error &err);
private:
void changePin();
private:
std::string keyRef;
};
ChangePinCommand::Private *ChangePinCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const ChangePinCommand::Private *ChangePinCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
ChangePinCommand::Private::Private(ChangePinCommand *qq, const std::string &serialNumber, QWidget *p)
: CardCommand::Private(qq, serialNumber, p)
{
}
ChangePinCommand::Private::~Private()
{
qCDebug(KLEOPATRA_LOG) << "ChangePinCommand::Private::~Private()";
}
ChangePinCommand::ChangePinCommand(const std::string &serialNumber, QWidget *p)
: CardCommand(new Private(this, serialNumber, p))
{
d->init();
}
void ChangePinCommand::Private::init()
{
}
ChangePinCommand::~ChangePinCommand()
{
qCDebug(KLEOPATRA_LOG) << "ChangePinCommand::~ChangePinCommand()";
}
void ChangePinCommand::setKeyRef(const std::string &keyRef)
{
d->keyRef = keyRef;
}
void ChangePinCommand::doStart()
{
qCDebug(KLEOPATRA_LOG) << "ChangePinCommand::doStart()";
d->changePin();
}
void ChangePinCommand::doCancel()
{
}
void ChangePinCommand::Private::changePin()
{
qCDebug(KLEOPATRA_LOG) << "ChangePinCommand::changePin()";
+
+ const auto card = SmartCard::ReaderStatus::instance()->getCard<Card>(serialNumber());
+ if (!card) {
+ error(i18n("Failed to find the smartcard with the serial number: %1", QString::fromStdString(serialNumber())));
+ finished();
+ return;
+ }
+
QByteArrayList command;
command << "SCD PASSWD";
- if (keyRef == OpenPGPCard::resetCodeKeyRef()) {
+ if (card->appName() == OpenPGPCard::AppName && keyRef == OpenPGPCard::resetCodeKeyRef()) {
// special handling for setting/changing the Reset Code of OpenPGP v2 cards
- const auto card = ReaderStatus::instance()->getCard<OpenPGPCard>(serialNumber());
- const std::string firstTwoVersionChars = card->cardVersion().substr(0, 2);
+ const auto pgpCard = std::dynamic_pointer_cast<OpenPGPCard>(card);
+ if (!pgpCard) {
+ error(i18n("Failed to find the OpenPGP card with the serial number: %1", QString::fromStdString(serialNumber())));
+ finished();
+ return;
+ }
+ const std::string firstTwoVersionChars = pgpCard->cardVersion().substr(0, 2);
const bool isVersion2 = !(firstTwoVersionChars == "1." || firstTwoVersionChars == "0.");
if (isVersion2) {
command << "--reset";
}
}
command << QByteArray::fromStdString(keyRef);
- ReaderStatus::mutableInstance()->startSimpleTransaction(command.join(' '), q, "slotResult");
+ ReaderStatus::mutableInstance()->startSimpleTransaction(card, command.join(' '), q, "slotResult");
}
namespace {
static QString errorMessage(const std::string &keyRef, const QString &errorText)
{
// see cmd_passwd() in gpg-card.c
if (keyRef == PIVCard::pukKeyRef()) {
return i18nc("@info", "Changing the PUK failed: %1", errorText);
}
if (keyRef == OpenPGPCard::adminPinKeyRef()) {
return i18nc("@info", "Changing the Admin PIN failed: %1", errorText);
}
if (keyRef == OpenPGPCard::resetCodeKeyRef()) {
return i18nc("@info", "Changing the Reset Code failed: %1", errorText);
}
return i18nc("@info", "Changing the PIN failed: %1", errorText);
}
static QString successMessage(const std::string &keyRef)
{
// see cmd_passwd() in gpg-card.c
if (keyRef == PIVCard::pukKeyRef()) {
return i18nc("@info", "PUK successfully changed.");
}
if (keyRef == OpenPGPCard::adminPinKeyRef()) {
return i18nc("@info", "Admin PIN changed successfully.");
}
if (keyRef == OpenPGPCard::resetCodeKeyRef()) {
return i18nc("@info", "Reset Code changed successfully.");
}
return i18nc("@info", "PIN changed successfully.");
}
}
void ChangePinCommand::Private::slotResult(const GpgME::Error& err)
{
qCDebug(KLEOPATRA_LOG) << "ChangePinCommand::slotResult():"
<< err.asString() << "(" << err.code() << ")";
if (err) {
error(errorMessage(keyRef, QString::fromLatin1(err.asString())),
i18nc("@title", "Error"));
} else if (!err.isCanceled()) {
information(successMessage(keyRef), i18nc("@title", "Success"));
ReaderStatus::mutableInstance()->updateStatus();
}
finished();
}
#undef d
#undef q
#include "moc_changepincommand.cpp"
diff --git a/src/commands/keytocardcommand.cpp b/src/commands/keytocardcommand.cpp
index e53f957e4..61f457025 100644
--- a/src/commands/keytocardcommand.cpp
+++ b/src/commands/keytocardcommand.cpp
@@ -1,452 +1,452 @@
/* 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 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 "commands/authenticatepivcardapplicationcommand.h"
#include "dialogs/certificateselectiondialog.h"
#include "smartcard/openpgpcard.h"
#include "smartcard/pivcard.h"
#include "smartcard/readerstatus.h"
#include <Libkleo/Dn>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/KeySelectionDialog>
#include <KLocalizedString>
#include <QDateTime>
#include <QInputDialog>
#include <QStringList>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::Dialogs;
using namespace Kleo::SmartCard;
using namespace GpgME;
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, const std::string &serialno);
explicit Private(KeyToCardCommand *qq, const std::string &slot, const std::string &serialno);
~Private();
private:
void start();
void startKeyToOpenPGPCard();
Subkey getSubkeyToTransferToPIVCard(const std::string &cardSlot, const std::shared_ptr<PIVCard> &card);
void startKeyToPIVCard();
void authenticate();
void authenticationFinished();
void authenticationCanceled();
private:
GpgME::Subkey subkey;
std::string cardSlot;
bool overwriteExistingAlreadyApproved = false;
bool hasBeenCanceled = false;
};
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_,
const std::string &serialno)
: CardCommand::Private(qq, serialno, nullptr),
subkey(subkey_)
{
}
KeyToCardCommand::Private::Private(KeyToCardCommand *qq, const std::string &slot, const std::string &serialno)
: CardCommand::Private(qq, serialno, nullptr)
, cardSlot(slot)
{
}
KeyToCardCommand::Private::~Private()
{
}
void KeyToCardCommand::Private::start()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::Private::start()";
const auto card = SmartCard::ReaderStatus::instance()->getCard<Card>(serialNumber());
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(i18n("Sorry! Transferring keys to this card is not supported."));
finished();
return;
}
}
namespace {
static int 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 1;
}
if (subKey.canEncrypt() && !(subKey.canSign() || subKey.canCertify()) && !subKey.canAuthenticate()) {
// Encrypt only
return 2;
}
if (subKey.canAuthenticate() && !(subKey.canSign() || subKey.canCertify()) && !subKey.canEncrypt()) {
// Auth only
return 3;
}
// Multiple uses, ask user.
QStringList options;
if (subKey.canSign() || subKey.canCertify()) {
options << i18nc("Placeholder is the number of a slot on a smart card", "Signature (%1)", 1);
}
if (subKey.canEncrypt()) {
options << i18nc("Placeholder is the number of a slot on a smart card", "Encryption (%1)", 2);
}
if (subKey.canAuthenticate()) {
options << i18nc("Placeholder is the number of a slot on a smart card", "Authentication (%1)", 3);
}
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 slot = options.indexOf(choice) + 1;
return ok ? slot : -1;
}
}
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;
}
const auto slot = getOpenPGPCardSlotForKey(subkey, parentWidgetOrView());
if (slot < 1) {
finished();
return;
}
// Check if we need to do the overwrite warning.
std::string existingKey;
QString encKeyWarning;
if (slot == 1) {
existingKey = pgpCard->sigFpr();
} else if (slot == 2) {
existingKey = pgpCard->encFpr();
encKeyWarning = i18n("It will no longer be possible to decrypt past communication "
"encrypted for the existing key.");
} else if (slot == 3) {
existingKey = pgpCard->authFpr();
}
if (!existingKey.empty()) {
const QString message = i18nc("@info",
"<p>This card 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>") +
i18n("The existing key has the fingerprint:") +
QStringLiteral("<pre>%1</pre>").arg(QString::fromStdString(existingKey)) +
encKeyWarning;
const auto choice = KMessageBox::warningContinueCancel(parentWidgetOrView(), message,
i18nc("@title:window", "Overwrite existing key"),
KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous);
if (choice != KMessageBox::Continue) {
finished();
return;
}
}
// Now do the deed
const auto time = QDateTime::fromSecsSinceEpoch(subkey.creationTime());
const auto timestamp = time.toString(QStringLiteral("yyyyMMdd'T'HHmmss"));
const QString cmd = QStringLiteral("KEYTOCARD --force %1 %2 OPENPGP.%3 %4")
.arg(QString::fromLatin1(subkey.keyGrip()), QString::fromStdString(serialNumber()))
.arg(slot)
.arg(timestamp);
- ReaderStatus::mutableInstance()->startSimpleTransaction(cmd.toUtf8(), q_func(), "keyToOpenPGPCardDone");
+ ReaderStatus::mutableInstance()->startSimpleTransaction(pgpCard, cmd.toUtf8(), q_func(), "keyToOpenPGPCardDone");
}
namespace {
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());
});
encryptionCertificates.erase(it, encryptionCertificates.end());
return encryptionCertificates;
}
}
Subkey KeyToCardCommand::Private::getSubkeyToTransferToPIVCard(const std::string &cardSlot, const std::shared_ptr<PIVCard> &/*card*/)
{
if (cardSlot != PIVCard::keyManagementKeyRef()) {
return Subkey();
}
const std::vector<Key> encryptionCertificates = getEncryptionCertificates();
if (encryptionCertificates.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(encryptionCertificates);
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::keyManagementKeyRef()) {
// key to card is only supported for encryption keys
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->keyGrip(cardSlot);
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>This card 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>") +
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"),
KStandardGuiItem::cont(), 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(cmd.toUtf8(), q_func(), "keyToPIVCardDone");
+ ReaderStatus::mutableInstance()->startSimpleTransaction(pivCard, cmd.toUtf8(), q_func(), "keyToPIVCardDone");
}
void KeyToCardCommand::Private::authenticate()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::authenticate()";
auto cmd = new AuthenticatePIVCardApplicationCommand(serialNumber(), parentWidgetOrView());
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();
}
KeyToCardCommand::KeyToCardCommand(const GpgME::Subkey &key, const std::string &serialno)
: CardCommand(new Private(this, key, serialno))
{
}
KeyToCardCommand::KeyToCardCommand(const std::string& cardSlot, const std::string &serialno)
: CardCommand(new Private(this, cardSlot, serialno))
{
}
KeyToCardCommand::~KeyToCardCommand()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::~KeyToCardCommand()";
}
bool KeyToCardCommand::supported()
{
return true;
}
void KeyToCardCommand::keyToOpenPGPCardDone(const GpgME::Error &err)
{
if (err) {
d->error(i18nc("@info",
"Moving the key to the card failed: %1", QString::fromUtf8(err.asString())),
i18nc("@title", "Error"));
} else if (!err.isCanceled()) {
/* TODO DELETE_KEY is too strong, because it also deletes the stub
* of the secret key. I could not find out how GnuPG does this. Question
* to GnuPG Developers is pending an answer
if (KMessageBox::questionYesNo(d->parentWidgetOrView(),
i18n("Do you want to delete the key on this computer?"),
i18nc("@title:window",
"Key transferred to card")) == KMessageBox::Yes) {
const QString cmd = QStringLiteral("DELETE_KEY --force %1").arg(d->subkey.keyGrip());
// Using readerstatus is a bit overkill but it's an easy way to talk to the agent.
- ReaderStatus::mutableInstance()->startSimpleTransaction(cmd.toUtf8(), this, "deleteDone");
+ ReaderStatus::mutableInstance()->startSimpleTransaction(card, cmd.toUtf8(), this, "deleteDone");
}
*/
d->information(i18nc("@info", "Successfully copied the key to the card."),
i18nc("@title", "Success"));
ReaderStatus::mutableInstance()->updateStatus();
}
d->finished();
}
void KeyToCardCommand::keyToPIVCardDone(const GpgME::Error &err)
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::keyToPIVCardDone():"
<< err.asString() << "(" << err.code() << ")";
if (err) {
// 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;
}
d->error(i18nc("@info",
"Copying the key pair to the card failed: %1", QString::fromUtf8(err.asString())),
i18nc("@title", "Error"));
} else if (!err.isCanceled()) {
d->information(i18nc("@info", "Successfully copied the key pair to the card."),
i18nc("@title", "Success"));
ReaderStatus::mutableInstance()->updateStatus();
}
d->finished();
}
void KeyToCardCommand::deleteDone(const GpgME::Error &err)
{
if (err) {
d->error(i18nc("@info", "Failed to delete the key: %1", QString::fromUtf8(err.asString())),
i18nc("@title", "Error"));
}
d->finished();
}
void KeyToCardCommand::doStart()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::doStart()";
d->start();
}
void KeyToCardCommand::doCancel()
{
}
#undef q_func
#undef d_func
diff --git a/src/commands/pivgeneratecardkeycommand.cpp b/src/commands/pivgeneratecardkeycommand.cpp
index aaad782f0..edbbce55d 100644
--- a/src/commands/pivgeneratecardkeycommand.cpp
+++ b/src/commands/pivgeneratecardkeycommand.cpp
@@ -1,240 +1,247 @@
/* commands/pivgeneratecardkeycommand.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 "pivgeneratecardkeycommand.h"
#include "cardcommand_p.h"
#include "smartcard/pivcard.h"
#include "smartcard/readerstatus.h"
#include "commands/authenticatepivcardapplicationcommand.h"
#include "dialogs/gencardkeydialog.h"
#include <KLocalizedString>
#include <gpgme++/error.h>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::SmartCard;
using namespace GpgME;
class PIVGenerateCardKeyCommand::Private : public CardCommand::Private
{
friend class ::Kleo::Commands::PIVGenerateCardKeyCommand;
PIVGenerateCardKeyCommand *q_func() const
{
return static_cast<PIVGenerateCardKeyCommand *>(q);
}
public:
explicit Private(PIVGenerateCardKeyCommand *qq, const std::string &serialNumber, QWidget *p);
~Private();
void init();
private:
void slotDialogAccepted();
void slotDialogRejected();
void slotResult(const Error &err);
private:
void authenticate();
void authenticationFinished();
void authenticationCanceled();
void generateKey();
void ensureDialogCreated();
private:
std::string keyRef;
bool overwriteExistingKey = false;
std::string algorithm;
QPointer<GenCardKeyDialog> dialog;
bool hasBeenCanceled = false;
};
PIVGenerateCardKeyCommand::Private *PIVGenerateCardKeyCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const PIVGenerateCardKeyCommand::Private *PIVGenerateCardKeyCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
PIVGenerateCardKeyCommand::Private::Private(PIVGenerateCardKeyCommand *qq, const std::string &serialNumber, QWidget *p)
: CardCommand::Private(qq, serialNumber, p)
, dialog()
{
}
PIVGenerateCardKeyCommand::Private::~Private()
{
qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::Private::~Private()";
}
PIVGenerateCardKeyCommand::PIVGenerateCardKeyCommand(const std::string &serialNumber, QWidget *p)
: CardCommand(new Private(this, serialNumber, p))
{
d->init();
}
void PIVGenerateCardKeyCommand::Private::init()
{
}
PIVGenerateCardKeyCommand::~PIVGenerateCardKeyCommand()
{
qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::~PIVGenerateCardKeyCommand()";
}
void PIVGenerateCardKeyCommand::setKeyRef(const std::string &keyRef)
{
d->keyRef = keyRef;
}
void PIVGenerateCardKeyCommand::doStart()
{
qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::doStart()";
// check if key exists
auto pivCard = ReaderStatus::instance()->getCard<PIVCard>(d->serialNumber());
if (!pivCard) {
- d->error(i18n("Failed to find the card with the serial number: %1", QString::fromStdString(d->serialNumber())));
+ d->error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(d->serialNumber())));
d->finished();
return;
}
auto existingKey = pivCard->keyGrip(d->keyRef);
if (!existingKey.empty()) {
const QString warningText = i18nc("@info",
"<p>This card 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>") +
i18n("The existing key has the ID:") + QStringLiteral("<pre>%1</pre>").arg(QString::fromStdString(existingKey)) +
(d->keyRef == PIVCard::keyManagementKeyRef() ?
i18n("It will no longer be possible to decrypt past communication encrypted for the existing key.") :
QString());
const auto choice = KMessageBox::warningContinueCancel(d->parentWidgetOrView(), warningText,
i18nc("@title:window", "Overwrite existing key"),
KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous);
if (choice != KMessageBox::Continue) {
d->finished();
return;
}
d->overwriteExistingKey = true;
}
d->ensureDialogCreated();
Q_ASSERT(d->dialog);
d->dialog->show();
}
void PIVGenerateCardKeyCommand::doCancel()
{
}
void PIVGenerateCardKeyCommand::Private::authenticate()
{
qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::authenticate()";
auto cmd = new AuthenticatePIVCardApplicationCommand(serialNumber(), parentWidgetOrView());
connect(cmd, &AuthenticatePIVCardApplicationCommand::finished,
q, [this]() { authenticationFinished(); });
connect(cmd, &AuthenticatePIVCardApplicationCommand::canceled,
q, [this]() { authenticationCanceled(); });
cmd->start();
}
void PIVGenerateCardKeyCommand::Private::authenticationFinished()
{
qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::authenticationFinished()";
if (!hasBeenCanceled) {
generateKey();
}
}
void PIVGenerateCardKeyCommand::Private::authenticationCanceled()
{
qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::authenticationCanceled()";
hasBeenCanceled = true;
canceled();
}
void PIVGenerateCardKeyCommand::Private::generateKey()
{
qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::generateKey()";
+ 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;
+ }
+
QByteArrayList command;
command << "SCD GENKEY";
if (overwriteExistingKey) {
command << "--force";
}
if (!algorithm.empty()) {
command << "--algo=" + QByteArray::fromStdString(algorithm);
}
command << "--" << QByteArray::fromStdString(keyRef);
- ReaderStatus::mutableInstance()->startSimpleTransaction(command.join(' '), q, "slotResult");
+ ReaderStatus::mutableInstance()->startSimpleTransaction(pivCard, command.join(' '), q, "slotResult");
}
void PIVGenerateCardKeyCommand::Private::slotResult(const GpgME::Error& err)
{
qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::slotResult():"
<< err.asString() << "(" << err.code() << ")";
if (err) {
if (err.code() == GPG_ERR_NO_AUTH) {
authenticate();
return;
}
error(i18nc("@info", "Generating key failed: %1", QString::fromLatin1(err.asString())),
i18nc("@title", "Error"));
} else if (!err.isCanceled()) {
information(i18nc("@info", "Key successfully generated."), i18nc("@title", "Success"));
ReaderStatus::mutableInstance()->updateStatus();
}
finished();
}
void PIVGenerateCardKeyCommand::Private::slotDialogAccepted()
{
algorithm = dialog->getKeyParams().algorithm;
// assume that we are already authenticated to the card
generateKey();
}
void PIVGenerateCardKeyCommand::Private::slotDialogRejected()
{
finished();
}
void PIVGenerateCardKeyCommand::Private::ensureDialogCreated()
{
if (dialog) {
return;
}
dialog = new GenCardKeyDialog(GenCardKeyDialog::KeyAlgorithm, parentWidgetOrView());
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setSupportedAlgorithms(PIVCard::supportedAlgorithms(keyRef), "rsa2048");
connect(dialog, SIGNAL(accepted()), q, SLOT(slotDialogAccepted()));
connect(dialog, SIGNAL(rejected()), q, SLOT(slotDialogRejected()));
}
#undef d
#undef q
#include "moc_pivgeneratecardkeycommand.cpp"
diff --git a/src/commands/setinitialpincommand.cpp b/src/commands/setinitialpincommand.cpp
index 1ea9376c6..9b54bbd39 100644
--- a/src/commands/setinitialpincommand.cpp
+++ b/src/commands/setinitialpincommand.cpp
@@ -1,170 +1,178 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/setinitialpincommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "setinitialpincommand.h"
#include "cardcommand_p.h"
#include "dialogs/setinitialpindialog.h"
#include "smartcard/netkeycard.h"
#include "smartcard/readerstatus.h"
#include <KLocalizedString>
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::Dialogs;
using namespace Kleo::SmartCard;
using namespace GpgME;
class SetInitialPinCommand::Private : public CardCommand::Private
{
friend class ::Kleo::Commands::SetInitialPinCommand;
SetInitialPinCommand *q_func() const
{
return static_cast<SetInitialPinCommand *>(q);
}
public:
explicit Private(SetInitialPinCommand *qq, const std::string &serialNumber);
~Private();
private:
void init()
{
}
void ensureDialogCreated() const
{
if (dialog) {
return;
}
SetInitialPinDialog *dlg = new SetInitialPinDialog;
applyWindowID(dlg);
dlg->setAttribute(Qt::WA_DeleteOnClose);
dlg->setWindowTitle(i18nc("@title:window", "Set Initial Pin"));
connect(dlg, SIGNAL(nksPinRequested()), q_func(), SLOT(slotNksPinRequested()));
connect(dlg, SIGNAL(sigGPinRequested()), q_func(), SLOT(slotSigGPinRequested()));
connect(dlg, SIGNAL(rejected()), q_func(), SLOT(slotDialogRejected()));
connect(dlg, SIGNAL(accepted()), q_func(), SLOT(slotDialogAccepted()));
dialog = dlg;
}
void ensureDialogVisible()
{
ensureDialogCreated();
if (dialog->isVisible()) {
dialog->raise();
} else {
dialog->show();
}
}
private:
+ void setInitialPin(const char *pinRef, const char *resultSlot)
+ {
+ const auto nksCard = ReaderStatus::instance()->getCard<NetKeyCard>(serialNumber());
+ if (!nksCard) {
+ error(i18n("Failed to find the NetKey card with the serial number: %1", QString::fromStdString(serialNumber())));
+ return;
+ }
+
+ const QByteArray command = QByteArray("SCD PASSWD --nullpin ") + pinRef;
+ ReaderStatus::mutableInstance()->startSimpleTransaction(nksCard, command, dialog, resultSlot);
+ }
+
void slotNksPinRequested()
{
- ReaderStatus::mutableInstance()
- ->startSimpleTransaction("SCD PASSWD --nullpin PW1.CH",
- dialog, "setNksPinSettingResult");
+ setInitialPin("PW1.CH", "setNksPinSettingResult");
}
void slotSigGPinRequested()
{
- ReaderStatus::mutableInstance()
- ->startSimpleTransaction("SCD PASSWD --nullpin PW1.CH.SIG",
- dialog, "setSigGPinSettingResult");
+ setInitialPin("PW1.CH.SIG", "setSigGPinSettingResult");
}
void slotDialogRejected()
{
if (dialog->isComplete()) {
slotDialogAccepted();
} else {
canceled();
}
}
void slotDialogAccepted()
{
ReaderStatus::mutableInstance()->updateStatus();
finished();
}
private:
mutable QPointer<SetInitialPinDialog> dialog;
};
SetInitialPinCommand::Private *SetInitialPinCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const SetInitialPinCommand::Private *SetInitialPinCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define q q_func()
#define d d_func()
SetInitialPinCommand::Private::Private(SetInitialPinCommand *qq, const std::string &serialNumber)
: CardCommand::Private(qq, serialNumber, nullptr),
dialog()
{
}
SetInitialPinCommand::Private::~Private() {}
SetInitialPinCommand::SetInitialPinCommand(const std::string &serialNumber)
: CardCommand(new Private(this, serialNumber))
{
d->init();
}
SetInitialPinCommand::~SetInitialPinCommand() {}
QDialog *SetInitialPinCommand::dialog() const
{
d->ensureDialogCreated();
return d->dialog;
}
void SetInitialPinCommand::doStart()
{
d->ensureDialogCreated();
const auto nksCard = ReaderStatus::instance()->getCard<NetKeyCard>(d->serialNumber());
if (!nksCard) {
d->error(i18n("Failed to find the NetKey card with the serial number: %1", QString::fromStdString(d->serialNumber())));
d->dialog->close();
finished();
return;
}
const std::vector<Card::PinState> pinStates = nksCard->pinStates();
d->dialog->setNksPinPresent(pinStates.size() >= 1 && pinStates[0] != Card::NullPin);
d->dialog->setSigGPinPresent(pinStates.size() >= 3 && pinStates[2] != Card::NullPin);
d->ensureDialogVisible();
}
void SetInitialPinCommand::doCancel()
{
if (d->dialog) {
d->dialog->close();
}
}
#undef q_func
#undef d_func
#include "moc_setinitialpincommand.cpp"
diff --git a/src/commands/setpivcardapplicationadministrationkeycommand.cpp b/src/commands/setpivcardapplicationadministrationkeycommand.cpp
index 0256857cc..b8f07e8a3 100644
--- a/src/commands/setpivcardapplicationadministrationkeycommand.cpp
+++ b/src/commands/setpivcardapplicationadministrationkeycommand.cpp
@@ -1,212 +1,220 @@
/* commands/setpivcardapplicationadministrationkeycommand.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 "setpivcardapplicationadministrationkeycommand.h"
#include "cardcommand_p.h"
+#include "smartcard/pivcard.h"
#include "smartcard/readerstatus.h"
#include "commands/authenticatepivcardapplicationcommand.h"
#include "dialogs/pivcardapplicationadministrationkeyinputdialog.h"
#include <KLocalizedString>
#include <gpgme++/error.h>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::Dialogs;
using namespace Kleo::SmartCard;
using namespace GpgME;
class SetPIVCardApplicationAdministrationKeyCommand::Private : public CardCommand::Private
{
friend class ::Kleo::Commands::SetPIVCardApplicationAdministrationKeyCommand;
SetPIVCardApplicationAdministrationKeyCommand *q_func() const
{
return static_cast<SetPIVCardApplicationAdministrationKeyCommand *>(q);
}
public:
explicit Private(SetPIVCardApplicationAdministrationKeyCommand *qq, const std::string &serialNumber, QWidget *p);
~Private();
void init();
private:
void slotDialogAccepted();
void slotDialogRejected();
void slotResult(const Error &err);
private:
void authenticate();
void authenticationFinished();
void authenticationCanceled();
void setAdminKey();
void ensureDialogCreated();
private:
QByteArray newAdminKey;
QPointer<PIVCardApplicationAdministrationKeyInputDialog> dialog;
bool hasBeenCanceled = false;
};
SetPIVCardApplicationAdministrationKeyCommand::Private *SetPIVCardApplicationAdministrationKeyCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const SetPIVCardApplicationAdministrationKeyCommand::Private *SetPIVCardApplicationAdministrationKeyCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
SetPIVCardApplicationAdministrationKeyCommand::Private::Private(SetPIVCardApplicationAdministrationKeyCommand *qq, const std::string &serialNumber, QWidget *p)
: CardCommand::Private(qq, serialNumber, p)
, dialog()
{
}
SetPIVCardApplicationAdministrationKeyCommand::Private::~Private()
{
qCDebug(KLEOPATRA_LOG) << "SetPIVCardApplicationAdministrationKeyCommand::Private::~Private()";
}
SetPIVCardApplicationAdministrationKeyCommand::SetPIVCardApplicationAdministrationKeyCommand(const std::string &serialNumber, QWidget *p)
: CardCommand(new Private(this, serialNumber, p))
{
d->init();
}
void SetPIVCardApplicationAdministrationKeyCommand::Private::init()
{
}
SetPIVCardApplicationAdministrationKeyCommand::~SetPIVCardApplicationAdministrationKeyCommand()
{
qCDebug(KLEOPATRA_LOG) << "SetPIVCardApplicationAdministrationKeyCommand::~SetPIVCardApplicationAdministrationKeyCommand()";
}
void SetPIVCardApplicationAdministrationKeyCommand::doStart()
{
qCDebug(KLEOPATRA_LOG) << "SetPIVCardApplicationAdministrationKeyCommand::doStart()";
d->authenticate();
}
void SetPIVCardApplicationAdministrationKeyCommand::doCancel()
{
}
void SetPIVCardApplicationAdministrationKeyCommand::Private::authenticate()
{
qCDebug(KLEOPATRA_LOG) << "SetPIVCardApplicationAdministrationKeyCommand::authenticate()";
auto cmd = new AuthenticatePIVCardApplicationCommand(serialNumber(), parentWidgetOrView());
cmd->setPrompt(i18n("Please enter the old PIV Card Application Administration Key in hex-encoded form."));
connect(cmd, &AuthenticatePIVCardApplicationCommand::finished,
q, [this]() { authenticationFinished(); });
connect(cmd, &AuthenticatePIVCardApplicationCommand::canceled,
q, [this]() { authenticationCanceled(); });
cmd->start();
}
void SetPIVCardApplicationAdministrationKeyCommand::Private::authenticationFinished()
{
qCDebug(KLEOPATRA_LOG) << "SetPIVCardApplicationAdministrationKeyCommand::authenticationFinished()";
if (!hasBeenCanceled) {
setAdminKey();
}
}
void SetPIVCardApplicationAdministrationKeyCommand::Private::authenticationCanceled()
{
qCDebug(KLEOPATRA_LOG) << "SetPIVCardApplicationAdministrationKeyCommand::authenticationCanceled()";
hasBeenCanceled = true;
canceled();
}
void SetPIVCardApplicationAdministrationKeyCommand::Private::setAdminKey()
{
qCDebug(KLEOPATRA_LOG) << "SetPIVCardApplicationAdministrationKeyCommand::setAdminKey()";
ensureDialogCreated();
Q_ASSERT(dialog);
dialog->show();
}
void SetPIVCardApplicationAdministrationKeyCommand::Private::ensureDialogCreated()
{
if (dialog) {
return;
}
dialog = new PIVCardApplicationAdministrationKeyInputDialog(parentWidgetOrView());
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setLabelText(newAdminKey.isEmpty() ?
i18n("Please enter the new PIV Card Application Administration Key in hex-encoded form. "
"The key needs to consist of 24 bytes, i.e. 48 hex-characters.") :
i18n("Please enter the new PIV Card Application Administration Key again."));
connect(dialog, SIGNAL(accepted()), q, SLOT(slotDialogAccepted()));
connect(dialog, SIGNAL(rejected()), q, SLOT(slotDialogRejected()));
}
void SetPIVCardApplicationAdministrationKeyCommand::Private::slotDialogAccepted()
{
if (newAdminKey.isEmpty()) {
newAdminKey = dialog->adminKey();
dialog = nullptr;
setAdminKey();
return;
}
const QByteArray newAdminKey2 = dialog->adminKey();
if (newAdminKey != newAdminKey2) {
error(i18nc("@info", "The two keys you have entered do not match. Please retry."),
i18nc("@title", "Error"));
newAdminKey.clear();
dialog = nullptr;
setAdminKey();
return;
}
+ 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 QByteArray plusPercentEncodedAdminKey = newAdminKey.toPercentEncoding().replace(' ', '+');
const QByteArray command = QByteArray("SCD SETATTR SET-ADM-KEY ") + plusPercentEncodedAdminKey;
- ReaderStatus::mutableInstance()->startSimpleTransaction(command, q, "slotResult");
+ ReaderStatus::mutableInstance()->startSimpleTransaction(pivCard, command, q, "slotResult");
}
void SetPIVCardApplicationAdministrationKeyCommand::Private::slotDialogRejected()
{
finished();
}
void SetPIVCardApplicationAdministrationKeyCommand::Private::slotResult(const GpgME::Error& err)
{
qCDebug(KLEOPATRA_LOG) << "SetPIVCardApplicationAdministrationKeyCommand::slotResult():"
<< err.asString() << "(" << err.code() << ")";
if (err) {
error(i18nc("@info", "Setting the PIV Card Application Administration Key failed: %1", QString::fromLatin1(err.asString())),
i18nc("@title", "Error"));
} else if (!err.isCanceled()) {
information(i18nc("@info", "PIV Card Application Administration Key set successfully."), i18nc("@title", "Success"));
ReaderStatus::mutableInstance()->updateStatus();
}
finished();
}
#undef d
#undef q
#include "moc_setpivcardapplicationadministrationkeycommand.cpp"
diff --git a/src/smartcard/readerstatus.cpp b/src/smartcard/readerstatus.cpp
index 0368455e1..b1733f7f4 100644
--- a/src/smartcard/readerstatus.cpp
+++ b/src/smartcard/readerstatus.cpp
@@ -1,813 +1,822 @@
/* -*- mode: c++; c-basic-offset:4 -*-
smartcard/readerstatus.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB
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 "readerstatus.h"
#include "keypairinfo.h"
#include <utils/gnupg-helper.h>
#include <Libkleo/FileSystemWatcher>
#include <Libkleo/Stl_Util>
#include <gpgme++/context.h>
#include <gpgme++/defaultassuantransaction.h>
#include <gpgme++/key.h>
#include <gpg-error.h>
#include "kleopatra_debug.h"
#include "openpgpcard.h"
#include "netkeycard.h"
#include "pivcard.h"
#include <QStringList>
#include <QFileInfo>
#include <QMutex>
#include <QWaitCondition>
#include <QThread>
#include <QPointer>
#include <memory>
#include <vector>
#include <set>
#include <list>
#include <algorithm>
#include <iterator>
#include <utility>
#include <cstdlib>
#include "utils/kdtoolsglobal.h"
using namespace Kleo;
using namespace Kleo::SmartCard;
using namespace GpgME;
static ReaderStatus *self = nullptr;
#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \
*(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10))
#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1))
static const char *flags[] = {
"NOCARD",
"PRESENT",
"ACTIVE",
"USABLE",
};
static_assert(sizeof flags / sizeof * flags == Card::_NumScdStates, "");
static const char *prettyFlags[] = {
"NoCard",
"CardPresent",
"CardActive",
"CardUsable",
"CardError",
};
static_assert(sizeof prettyFlags / sizeof * prettyFlags == Card::NumStates, "");
#if 0
We need this once we have support for multiple readers in scdaemons
interface.
static unsigned int parseFileName(const QString &fileName, bool *ok)
{
QRegExp rx(QLatin1String("reader_(\\d+)\\.status"));
if (ok) {
*ok = false;
}
if (rx.exactMatch(QFileInfo(fileName).fileName())) {
return rx.cap(1).toUInt(ok, 10);
}
return 0;
}
#endif
Q_DECLARE_METATYPE(GpgME::Error)
namespace
{
static QDebug operator<<(QDebug s, const std::vector< std::pair<std::string, std::string> > &v)
{
typedef std::pair<std::string, std::string> pair;
s << '(';
for (const pair &p : v) {
s << "status(" << QString::fromStdString(p.first) << ") =" << QString::fromStdString(p.second) << '\n';
}
return s << ')';
}
+struct CardApp {
+ std::string serialNumber;
+ std::string appName;
+};
+
static int parse_app_version(const std::string &s)
{
return std::atoi(s.c_str());
}
static Card::PinState parse_pin_state(const QString &s)
{
bool ok;
int i = s.toInt(&ok);
if (!ok) {
qCDebug(KLEOPATRA_LOG) << "Failed to parse pin state" << s;
return Card::UnknownPinState;
}
switch (i) {
case -4: return Card::NullPin;
case -3: return Card::PinBlocked;
case -2: return Card::NoPin;
case -1: return Card::UnknownPinState;
default:
if (i < 0) {
return Card::UnknownPinState;
} else {
return Card::PinOk;
}
}
}
template<typename T>
static std::unique_ptr<T> gpgagent_transact(std::shared_ptr<Context> &gpgAgent, const char *command, std::unique_ptr<T> transaction, Error &err)
{
qCDebug(KLEOPATRA_LOG) << "gpgagent_transact(" << command << ")";
err = gpgAgent->assuanTransact(command, std::move(transaction));
if (err.code()) {
qCDebug(KLEOPATRA_LOG) << "gpgagent_transact(" << command << "):" << QString::fromLocal8Bit(err.asString());
if (err.code() >= GPG_ERR_ASS_GENERAL && err.code() <= GPG_ERR_ASS_UNKNOWN_INQUIRE) {
qCDebug(KLEOPATRA_LOG) << "Assuan problem, killing context";
gpgAgent.reset();
}
return std::unique_ptr<T>();
}
std::unique_ptr<AssuanTransaction> t = gpgAgent->takeLastAssuanTransaction();
return std::unique_ptr<T>(dynamic_cast<T*>(t.release()));
}
static std::unique_ptr<DefaultAssuanTransaction> gpgagent_default_transact(std::shared_ptr<Context> &gpgAgent, const char *command, Error &err)
{
return gpgagent_transact(gpgAgent, command, std::unique_ptr<DefaultAssuanTransaction>(new DefaultAssuanTransaction), err);
}
static const std::string gpgagent_data(std::shared_ptr<Context> gpgAgent, const char *command, Error &err)
{
const std::unique_ptr<DefaultAssuanTransaction> t = gpgagent_default_transact(gpgAgent, command, err);
if (t.get()) {
qCDebug(KLEOPATRA_LOG) << "gpgagent_data(" << command << "): got" << QString::fromStdString(t->data());
return t->data();
} else {
qCDebug(KLEOPATRA_LOG) << "gpgagent_data(" << command << "): t == NULL";
return std::string();
}
}
static const std::vector< std::pair<std::string, std::string> > gpgagent_statuslines(std::shared_ptr<Context> gpgAgent, const char *what, Error &err)
{
const std::unique_ptr<DefaultAssuanTransaction> t = gpgagent_default_transact(gpgAgent, what, err);
if (t.get()) {
qCDebug(KLEOPATRA_LOG) << "agent_getattr_status(" << what << "): got" << t->statusLines();
return t->statusLines();
} else {
qCDebug(KLEOPATRA_LOG) << "agent_getattr_status(" << what << "): t == NULL";
return std::vector<std::pair<std::string, std::string> >();
}
}
static const std::string gpgagent_status(const std::shared_ptr<Context> &gpgAgent, const char *what, Error &err)
{
const auto lines = gpgagent_statuslines (gpgAgent, what, err);
// The status is only the last attribute
// e.g. for SCD SERIALNO it would only be "SERIALNO" and for SCD GETATTR FOO
// it would only be FOO
const char *p = strrchr(what, ' ');
const char *needle = (p + 1) ? (p + 1) : what;
for (const auto &pair: lines) {
if (pair.first == needle) {
return pair.second;
}
}
return std::string();
}
static const std::string scd_getattr_status(std::shared_ptr<Context> &gpgAgent, const char *what, Error &err)
{
std::string cmd = "SCD GETATTR ";
cmd += what;
return gpgagent_status(gpgAgent, cmd.c_str(), err);
}
static const char * get_openpgp_card_manufacturer_from_serial_number(const std::string &serialno)
{
qCDebug(KLEOPATRA_LOG) << "get_openpgp_card_manufacturer_from_serial_number(" << serialno.c_str() << ")";
const bool isProperOpenPGPCardSerialNumber =
serialno.size() == 32 && serialno.substr(0, 12) == "D27600012401";
if (isProperOpenPGPCardSerialNumber) {
const char *sn = serialno.c_str();
const int manufacturerId = xtoi_2(sn + 16)*256 + xtoi_2(sn + 18);
switch (manufacturerId) {
case 0x0001: return "PPC Card Systems";
case 0x0002: return "Prism";
case 0x0003: return "OpenFortress";
case 0x0004: return "Wewid";
case 0x0005: return "ZeitControl";
case 0x0006: return "Yubico";
case 0x0007: return "OpenKMS";
case 0x0008: return "LogoEmail";
case 0x002A: return "Magrathea";
case 0x1337: return "Warsaw Hackerspace";
case 0xF517: return "FSIJ";
/* 0x0000 and 0xFFFF are defined as test cards per spec,
0xFF00 to 0xFFFE are assigned for use with randomly created
serial numbers. */
case 0x0000:
case 0xffff: return "test card";
default: return (manufacturerId & 0xff00) == 0xff00 ? "unmanaged S/N range" : "unknown";
}
} else {
return "unknown";
}
}
static const std::string get_manufacturer(std::shared_ptr<Context> &gpgAgent, Error &err)
{
// The result of SCD GETATTR 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
const auto manufacturerIdAndName = scd_getattr_status(gpgAgent, "MANUFACTURER", err);
if (err.code()) {
if (err.code() == GPG_ERR_INV_NAME) {
qCDebug(KLEOPATRA_LOG) << "get_manufacturer(): Querying for attribute MANUFACTURER not yet supported; needs GnuPG 2.2.21+";
} else {
qCDebug(KLEOPATRA_LOG) << "get_manufacturer(): GpgME::Error(" << err.encodedError() << " (" << err.asString() << "))";
}
return std::string();
}
const auto startOfManufacturerName = manufacturerIdAndName.find(' ');
if (startOfManufacturerName == std::string::npos) {
// only ID without manufacturer name
return "unknown";
}
const auto manufacturerName = manufacturerIdAndName.substr(startOfManufacturerName + 1);
return manufacturerName;
}
static void handle_openpgp_card(std::shared_ptr<Card> &ci, std::shared_ptr<Context> &gpg_agent)
{
Error err;
auto ret = new OpenPGPCard();
ret->setSerialNumber(ci->serialNumber());
ret->setManufacturer(get_manufacturer(gpg_agent, err));
if (err.code()) {
// fallback, e.g. if gpg does not yet support querying for the MANUFACTURER attribute
ret->setManufacturer(get_openpgp_card_manufacturer_from_serial_number(ci->serialNumber()));
}
const auto info = gpgagent_statuslines(gpg_agent, "SCD LEARN --keypairinfo", err);
if (err.code()) {
ci->setStatus(Card::CardError);
return;
}
ret->setKeyPairInfo(info);
ci.reset(ret);
}
static void readKeyPairInfoFromPIVCard(const std::string &keyRef, PIVCard *pivCard, const std::shared_ptr<Context> &gpg_agent)
{
Error err;
const std::string command = std::string("SCD READKEY --info-only -- ") + keyRef;
const auto keyPairInfoLines = gpgagent_statuslines(gpg_agent, command.c_str(), err);
if (err) {
qCWarning(KLEOPATRA_LOG) << "readKeyPairInfoFromPIVCard(): Error on " << QString::fromStdString(command) << ":"
<< "GpgME::Error(" << err.encodedError() << " (" << err.asString() << "))";
return;
}
for (const auto &pair: keyPairInfoLines) {
if (pair.first == "KEYPAIRINFO") {
const KeyPairInfo info = KeyPairInfo::fromStatusLine(pair.second);
if (info.grip.empty()) {
qCWarning(KLEOPATRA_LOG) << "Invalid KEYPAIRINFO status line"
<< QString::fromStdString(pair.second);
continue;
}
pivCard->setKeyAlgorithm(keyRef, info.algorithm);
} else {
qCWarning(KLEOPATRA_LOG) << "readKeyPairInfoFromPIVCard(): Unexpected status line on "
<< QString::fromStdString(command) << ":" << QString::fromStdString(pair.first)
<< QString::fromStdString(pair.second);
}
}
}
static void readCertificateFromPIVCard(const std::string &keyRef, PIVCard *pivCard, const std::shared_ptr<Context> &gpg_agent)
{
Error err;
const std::string command = std::string("SCD READCERT ") + keyRef;
const std::string certificateData = gpgagent_data(gpg_agent, command.c_str(), err);
if (err && err.code() != GPG_ERR_NOT_FOUND) {
qCWarning(KLEOPATRA_LOG) << "readCertificateFromPIVCard(" << QString::fromStdString(keyRef) << "): Error on "
<< QString::fromStdString(command) << ":" << "GpgME::Error(" << err.encodedError() << " (" << err.asString() << "))";
return;
}
if (certificateData.empty()) {
qCDebug(KLEOPATRA_LOG) << "readCertificateFromPIVCard(" << QString::fromStdString(keyRef) << "): No certificate stored on card";
return;
}
qCDebug(KLEOPATRA_LOG) << "readCertificateFromPIVCard(" << QString::fromStdString(keyRef) << "): Found certificate stored on card";
pivCard->setCertificateData(keyRef, certificateData);
}
static void handle_piv_card(std::shared_ptr<Card> &ci, std::shared_ptr<Context> &gpg_agent)
{
Error err;
auto pivCard = new PIVCard();
pivCard->setSerialNumber(ci->serialNumber());
const auto displaySerialnumber = scd_getattr_status(gpg_agent, "$DISPSERIALNO", err);
if (err) {
qCWarning(KLEOPATRA_LOG) << "handle_piv_card(): Error on GETATTR $DISPSERIALNO:"
<< "GpgME::Error(" << err.encodedError() << " (" << err.asString() << "))";
}
pivCard->setDisplaySerialNumber(err ? ci->serialNumber() : displaySerialnumber);
const auto info = gpgagent_statuslines(gpg_agent, "SCD LEARN --force", err);
if (err) {
ci->setStatus(Card::CardError);
return;
}
pivCard->setCardInfo(info);
for (const std::string &keyRef : PIVCard::supportedKeys()) {
if (!pivCard->keyGrip(keyRef).empty()) {
readKeyPairInfoFromPIVCard(keyRef, pivCard, gpg_agent);
readCertificateFromPIVCard(keyRef, pivCard, gpg_agent);
}
}
ci.reset(pivCard);
}
static void handle_netkey_card(std::shared_ptr<Card> &ci, std::shared_ptr<Context> &gpg_agent)
{
Error err;
auto nkCard = new NetKeyCard();
nkCard->setSerialNumber(ci->serialNumber());
ci.reset(nkCard);
ci->setAppVersion(parse_app_version(scd_getattr_status(gpg_agent, "NKS-VERSION", err)));
if (err.code()) {
qCDebug(KLEOPATRA_LOG) << "NKS-VERSION resulted in error" << err.asString();
ci->setErrorMsg(QStringLiteral ("NKS-VERSION failed: ") + QString::fromUtf8(err.asString()));
return;
}
if (ci->appVersion() != 3) {
qCDebug(KLEOPATRA_LOG) << "not a NetKey v3 card, giving up. Version:" << ci->appVersion();
ci->setErrorMsg(QStringLiteral("NetKey v%1 cards are not supported.").arg(ci->appVersion()));
return;
}
// the following only works for NKS v3...
const auto chvStatus = QString::fromStdString(
scd_getattr_status(gpg_agent, "CHV-STATUS", err)).split(QLatin1Char(' '));
if (err.code()) {
qCDebug(KLEOPATRA_LOG) << "no CHV-STATUS" << err.asString();
ci->setErrorMsg(QStringLiteral ("CHV-Status failed: ") + QString::fromUtf8(err.asString()));
return;
}
std::vector<Card::PinState> states;
states.reserve(chvStatus.count());
// CHV Status for NKS v3 is
// Pin1 (Normal pin) Pin2 (Normal PUK)
// SigG1 SigG PUK.
int num = 0;
for (const auto &state: chvStatus) {
const auto parsed = parse_pin_state (state);
states.push_back(parsed);
if (parsed == Card::NullPin) {
if (num == 0) {
ci->setHasNullPin(true);
}
}
++num;
}
nkCard->setPinStates(states);
// check for keys to learn:
const std::unique_ptr<DefaultAssuanTransaction> result = gpgagent_default_transact(gpg_agent, "SCD LEARN --keypairinfo", err);
if (err.code() || !result.get()) {
if (err) {
ci->setErrorMsg(QString::fromLatin1(err.asString()));
} else {
ci->setErrorMsg(QStringLiteral("Invalid internal state. No result."));
}
return;
}
const std::vector<std::string> keyPairInfos = result->statusLine("KEYPAIRINFO");
if (keyPairInfos.empty()) {
return;
}
nkCard->setKeyPairInfo(keyPairInfos);
}
static std::shared_ptr<Card> get_card_status(unsigned int slot, std::shared_ptr<Context> &gpg_agent)
{
qCDebug(KLEOPATRA_LOG) << "get_card_status(" << slot << ',' << gpg_agent.get() << ')';
auto ci = std::shared_ptr<Card> (new Card());
if (slot != 0 || !gpg_agent) {
// In the future scdaemon should support multiple slots but
// not yet (2.1.18)
return ci;
}
Error err;
ci->setSerialNumber(gpgagent_status(gpg_agent, "SCD SERIALNO", err));
if (err.code() == GPG_ERR_CARD_NOT_PRESENT || err.code() == GPG_ERR_CARD_REMOVED) {
ci->setStatus(Card::NoCard);
return ci;
}
if (err.code()) {
ci->setStatus(Card::CardError);
return ci;
}
ci->setStatus(Card::CardPresent);
const auto appName = scd_getattr_status(gpg_agent, "APPTYPE", err);
ci->setAppName(appName);
if (err.code()) {
return ci;
}
// Handle different card types
if (ci->appName() == NetKeyCard::AppName) {
qCDebug(KLEOPATRA_LOG) << "get_card_status: found Netkey card" << ci->serialNumber().c_str() << "end";
handle_netkey_card(ci, gpg_agent);
return ci;
} else if (ci->appName() == OpenPGPCard::AppName) {
qCDebug(KLEOPATRA_LOG) << "get_card_status: found OpenPGP card" << ci->serialNumber().c_str() << "end";
handle_openpgp_card(ci, gpg_agent);
return ci;
} else if (ci->appName() == PIVCard::AppName) {
qCDebug(KLEOPATRA_LOG) << "get_card_status: found PIV card" << ci->serialNumber().c_str() << "end";
handle_piv_card(ci, gpg_agent);
return ci;
} else {
qCDebug(KLEOPATRA_LOG) << "get_card_status: unhandled application:" << appName.c_str();
return ci;
}
return ci;
}
static std::vector<std::shared_ptr<Card> > update_cardinfo(std::shared_ptr<Context> &gpgAgent)
{
// Multiple smartcard readers are only supported internally by gnupg
// but not by scdaemon (Status gnupg 2.1.18)
// We still pretend that there can be multiple cards inserted
// at once but we don't handle it yet.
const auto ci = get_card_status(0, gpgAgent);
return std::vector<std::shared_ptr<Card> >(1, ci);
}
} // namespace
struct Transaction {
+ CardApp cardApp;
QByteArray command;
QPointer<QObject> receiver;
const char *slot;
AssuanTransaction* assuanTransaction;
};
-static const Transaction updateTransaction = { "__update__", nullptr, nullptr, nullptr };
-static const Transaction quitTransaction = { "__quit__", nullptr, nullptr, nullptr };
+static const Transaction updateTransaction = { { "__all__", "__all__" }, "__update__", nullptr, nullptr, nullptr };
+static const Transaction quitTransaction = { { "__all__", "__all__" }, "__quit__", nullptr, nullptr, nullptr };
namespace
{
class ReaderStatusThread : public QThread
{
Q_OBJECT
public:
explicit ReaderStatusThread(QObject *parent = nullptr)
: QThread(parent),
m_gnupgHomePath(Kleo::gnupgHomeDirectory()),
m_transactions(1, updateTransaction) // force initial scan
{
connect(this, &ReaderStatusThread::oneTransactionFinished,
this, &ReaderStatusThread::slotOneTransactionFinished);
}
std::vector<std::shared_ptr<Card> > cardInfos() const
{
const QMutexLocker locker(&m_mutex);
return m_cardInfos;
}
Card::Status cardStatus(unsigned int slot) const
{
const QMutexLocker locker(&m_mutex);
if (slot < m_cardInfos.size()) {
return m_cardInfos[slot]->status();
} else {
return Card::NoCard;
}
}
void addTransaction(const Transaction &t)
{
const QMutexLocker locker(&m_mutex);
m_transactions.push_back(t);
m_waitForTransactions.wakeOne();
}
Q_SIGNALS:
void firstCardWithNullPinChanged(const std::string &serialNumber);
void anyCardCanLearnKeysChanged(bool);
void cardChanged(unsigned int);
void oneTransactionFinished(const GpgME::Error &err);
public Q_SLOTS:
void ping()
{
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[GUI]::ping()";
addTransaction(updateTransaction);
}
void stop()
{
const QMutexLocker locker(&m_mutex);
m_transactions.push_front(quitTransaction);
m_waitForTransactions.wakeOne();
}
private Q_SLOTS:
void slotOneTransactionFinished(const GpgME::Error &err)
{
std::list<Transaction> ft;
KDAB_SYNCHRONIZED(m_mutex)
ft.splice(ft.begin(), m_finishedTransactions);
Q_FOREACH (const Transaction &t, ft)
if (t.receiver && t.slot && *t.slot) {
QMetaObject::invokeMethod(t.receiver, t.slot, Qt::DirectConnection, Q_ARG(GpgME::Error, err));
}
}
private:
void run() override {
while (true) {
std::shared_ptr<Context> gpgAgent;
QByteArray command;
bool nullSlot = false;
AssuanTransaction* assuanTransaction = nullptr;
std::list<Transaction> item;
std::vector<std::shared_ptr<Card> > oldCards;
Error err;
std::unique_ptr<Context> c = Context::createForEngine(AssuanEngine, &err);
if (err.code() == GPG_ERR_NOT_SUPPORTED) {
return;
}
gpgAgent = std::shared_ptr<Context>(c.release());
KDAB_SYNCHRONIZED(m_mutex) {
while (m_transactions.empty()) {
// go to sleep waiting for more work:
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[2nd]: waiting for commands";
m_waitForTransactions.wait(&m_mutex);
}
// splice off the first transaction without
// copying, so we own it without really importing
// it into this thread (the QPointer isn't
// thread-safe):
item.splice(item.end(),
m_transactions, m_transactions.begin());
// make local copies of the interesting stuff so
// we can release the mutex again:
command = item.front().command;
nullSlot = !item.front().slot;
// we take ownership of the assuan transaction
std::swap(assuanTransaction, item.front().assuanTransaction);
oldCards = m_cardInfos;
}
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[2nd]: new iteration command=" << command << " ; nullSlot=" << nullSlot;
// now, let's see what we got:
if (nullSlot && command == quitTransaction.command) {
return; // quit
}
if ((nullSlot && command == updateTransaction.command)) {
std::vector<std::shared_ptr<Card> > newCards = update_cardinfo(gpgAgent);
newCards.resize(std::max(newCards.size(), oldCards.size()));
oldCards.resize(std::max(newCards.size(), oldCards.size()));
KDAB_SYNCHRONIZED(m_mutex)
m_cardInfos = newCards;
std::vector<std::shared_ptr<Card> >::const_iterator
nit = newCards.begin(), nend = newCards.end(),
oit = oldCards.begin(), oend = oldCards.end();
unsigned int idx = 0;
bool anyLC = false;
std::string firstCardWithNullPin;
bool anyError = false;
while (nit != nend && oit != oend) {
const auto optr = (*oit).get();
const auto nptr = (*nit).get();
if ((optr && !nptr) || (!optr && nptr) || (optr && nptr && *optr != *nptr)) {
qCDebug(KLEOPATRA_LOG) << "ReaderStatusThread[2nd]: slot" << idx << ": card Changed";
Q_EMIT cardChanged(idx);
}
if ((*nit)->canLearnKeys()) {
anyLC = true;
}
if ((*nit)->hasNullPin() && firstCardWithNullPin.empty()) {
firstCardWithNullPin = (*nit)->serialNumber();
}
if ((*nit)->status() == Card::CardError) {
anyError = true;
}
++nit;
++oit;
++idx;
}
Q_EMIT firstCardWithNullPinChanged(firstCardWithNullPin);
Q_EMIT anyCardCanLearnKeysChanged(anyLC);
if (anyError) {
gpgAgent.reset();
}
} else {
GpgME::Error err;
if (assuanTransaction) {
(void)gpgagent_transact(gpgAgent, command.constData(), std::unique_ptr<AssuanTransaction>(assuanTransaction), err);
} else {
(void)gpgagent_default_transact(gpgAgent, command.constData(), err);
}
KDAB_SYNCHRONIZED(m_mutex)
// splice 'item' into m_finishedTransactions:
m_finishedTransactions.splice(m_finishedTransactions.end(), item);
Q_EMIT oneTransactionFinished(err);
}
}
}
private:
mutable QMutex m_mutex;
QWaitCondition m_waitForTransactions;
const QString m_gnupgHomePath;
// protected by m_mutex:
std::vector<std::shared_ptr<Card> > m_cardInfos;
std::list<Transaction> m_transactions, m_finishedTransactions;
};
}
class ReaderStatus::Private : ReaderStatusThread
{
friend class Kleo::SmartCard::ReaderStatus;
ReaderStatus *const q;
public:
explicit Private(ReaderStatus *qq)
: ReaderStatusThread(qq),
q(qq),
watcher()
{
KDAB_SET_OBJECT_NAME(watcher);
qRegisterMetaType<Card::Status>("Kleo::SmartCard::Card::Status");
qRegisterMetaType<GpgME::Error>("GpgME::Error");
watcher.whitelistFiles(QStringList(QStringLiteral("reader_*.status")));
watcher.addPath(Kleo::gnupgHomeDirectory());
watcher.setDelay(100);
connect(this, &::ReaderStatusThread::cardChanged,
q, &ReaderStatus::cardChanged);
connect(this, &::ReaderStatusThread::firstCardWithNullPinChanged,
q, &ReaderStatus::firstCardWithNullPinChanged);
connect(this, &::ReaderStatusThread::anyCardCanLearnKeysChanged,
q, &ReaderStatus::anyCardCanLearnKeysChanged);
connect(&watcher, &FileSystemWatcher::triggered, this, &::ReaderStatusThread::ping);
}
~Private()
{
stop();
if (!wait(100)) {
terminate();
wait();
}
}
private:
std::string firstCardWithNullPinImpl() const
{
const auto cis = cardInfos();
const auto firstWithNullPin = std::find_if(cis.cbegin(), cis.cend(),
[](const std::shared_ptr<Card> &ci) { return ci->hasNullPin(); });
return firstWithNullPin != cis.cend() ? (*firstWithNullPin)->serialNumber() : std::string();
}
bool anyCardCanLearnKeysImpl() const
{
const auto cis = cardInfos();
return std::any_of(cis.cbegin(), cis.cend(),
[](const std::shared_ptr<Card> &ci) { return ci->canLearnKeys(); });
}
private:
FileSystemWatcher watcher;
};
ReaderStatus::ReaderStatus(QObject *parent)
: QObject(parent), d(new Private(this))
{
self = this;
qRegisterMetaType<std::string>("std::string");
}
ReaderStatus::~ReaderStatus()
{
self = nullptr;
}
// slot
void ReaderStatus::startMonitoring()
{
d->start();
}
// static
ReaderStatus *ReaderStatus::mutableInstance()
{
return self;
}
// static
const ReaderStatus *ReaderStatus::instance()
{
return self;
}
Card::Status ReaderStatus::cardStatus(unsigned int slot) const
{
return d->cardStatus(slot);
}
std::string ReaderStatus::firstCardWithNullPin() const
{
return d->firstCardWithNullPinImpl();
}
bool ReaderStatus::anyCardCanLearnKeys() const
{
return d->anyCardCanLearnKeysImpl();
}
-void ReaderStatus::startSimpleTransaction(const QByteArray &command, QObject *receiver, const char *slot)
+void ReaderStatus::startSimpleTransaction(const std::shared_ptr<Card> &card, const QByteArray &command, QObject *receiver, const char *slot)
{
- const Transaction t = { command, receiver, slot, nullptr };
+ const CardApp cardApp = { card->serialNumber(), card->appName() };
+ const Transaction t = { cardApp, command, receiver, slot, nullptr };
d->addTransaction(t);
}
-void ReaderStatus::startTransaction(const QByteArray &command, QObject *receiver, const char *slot, std::unique_ptr<AssuanTransaction> transaction)
+void ReaderStatus::startTransaction(const std::shared_ptr<Card> &card, const QByteArray &command, QObject *receiver, const char *slot,
+ std::unique_ptr<AssuanTransaction> transaction)
{
- const Transaction t = { command, receiver, slot, transaction.release() };
+ const CardApp cardApp = { card->serialNumber(), card->appName() };
+ const Transaction t = { cardApp, command, receiver, slot, transaction.release() };
d->addTransaction(t);
}
void ReaderStatus::updateStatus()
{
d->ping();
}
std::vector <std::shared_ptr<Card> > ReaderStatus::getCards() const
{
return d->cardInfos();
}
#include "readerstatus.moc"
diff --git a/src/smartcard/readerstatus.h b/src/smartcard/readerstatus.h
index fc18690f0..55c6cff3b 100644
--- a/src/smartcard/readerstatus.h
+++ b/src/smartcard/readerstatus.h
@@ -1,84 +1,85 @@
/* -*- mode: c++; c-basic-offset:4 -*-
smartcard/readerstatus.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef __KLEOPATRA__SMARTCARD__READERSTATUS_H___
#define __KLEOPATRA__SMARTCARD__READERSTATUS_H___
#include <QObject>
#include <QMetaType>
#include "card.h"
#include <vector>
#include <memory>
#include "kleopatra_debug.h"
namespace GpgME
{
class AssuanTransaction;
}
namespace Kleo
{
namespace SmartCard
{
class ReaderStatus : public QObject
{
Q_OBJECT
public:
explicit ReaderStatus(QObject *parent = nullptr);
~ReaderStatus();
static const ReaderStatus *instance();
static ReaderStatus *mutableInstance();
- void startSimpleTransaction(const QByteArray &cmd, QObject *receiver, const char *slot);
- void startTransaction(const QByteArray &cmd, QObject *receiver, const char *slot, std::unique_ptr<GpgME::AssuanTransaction> transaction);
+ void startSimpleTransaction(const std::shared_ptr<Card> &card, const QByteArray &cmd, QObject *receiver, const char *slot);
+ void startTransaction(const std::shared_ptr<Card> &card, const QByteArray &cmd, QObject *receiver, const char *slot,
+ std::unique_ptr<GpgME::AssuanTransaction> transaction);
Card::Status cardStatus(unsigned int slot) const;
std::string firstCardWithNullPin() const;
bool anyCardCanLearnKeys() const;
std::vector<std::shared_ptr<Card> > getCards() const;
template <typename T>
std::shared_ptr<T> getCard(const std::string &serialNumber) const
{
for (const auto &card: getCards()) {
if (card->serialNumber() == serialNumber) {
qCDebug(KLEOPATRA_LOG) << "ReaderStatus::getCard() - Found card with serial number" << QString::fromStdString(serialNumber);
return std::dynamic_pointer_cast<T>(card);
}
}
qCDebug(KLEOPATRA_LOG) << "ReaderStatus::getCard() - Did not find card with serial number" << QString::fromStdString(serialNumber);
return std::shared_ptr<T>();
}
public Q_SLOTS:
void updateStatus();
void startMonitoring();
Q_SIGNALS:
void firstCardWithNullPinChanged(const std::string &serialNumber);
void anyCardCanLearnKeysChanged(bool);
void cardChanged(unsigned int slot);
private:
class Private;
std::shared_ptr<Private> d;
};
} // namespace SmartCard
} // namespace Kleo
Q_DECLARE_METATYPE(Kleo::SmartCard::Card::Status)
#endif /* __KLEOPATRA__SMARTCARD__READERSTATUS_H___ */
diff --git a/src/view/netkeywidget.cpp b/src/view/netkeywidget.cpp
index be61f38de..6be891b39 100644
--- a/src/view/netkeywidget.cpp
+++ b/src/view/netkeywidget.cpp
@@ -1,230 +1,236 @@
/* 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/learncardkeyscommand.h"
#include "commands/detailscommand.h"
#include <Libkleo/KeyListModel>
#include <QLabel>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QScrollArea>
#include <QPushButton>
#include <QTreeView>
#include <KLocalizedString>
#include <KMessageBox>
using namespace Kleo;
using namespace Kleo::SmartCard;
using namespace Kleo::Commands;
NetKeyWidget::NetKeyWidget(QWidget *parent) :
QWidget(parent),
- mSerialNumber(new QLabel(this)),
+ mSerialNumberLabel(new QLabel(this)),
mVersionLabel(new QLabel(this)),
mLearnKeysLabel(new QLabel(this)),
mErrorLabel(new QLabel(this)),
mNullPinWidget(new NullPinWidget()),
mLearnKeysBtn(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->addWidget(mArea);
// Add general widgets
mVersionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
vLay->addWidget(mVersionLabel, 0, Qt::AlignLeft);
- mSerialNumber->setTextInteractionFlags(Qt::TextBrowserInteraction);
+ mSerialNumberLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
auto hLay1 = new QHBoxLayout;
hLay1->addWidget(new QLabel(i18n("Serial number:")));
- hLay1->addWidget(mSerialNumber);
+ 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("<b>%1</b>").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<KeyListModelInterface *> (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("<b>%1</b>").arg(i18n("Actions:"))), 0, Qt::AlignLeft);
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);
vLay->addLayout(hLay3);
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()));
- mSerialNumber->setText(QString::fromStdString(card->serialNumber()));
+ 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("<b>%1:</b> %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<NetKeyCard>(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("SCD PASSWD PW1.CH.SIG",
- this, "setSigGPinSettingResult");
+ ReaderStatus::mutableInstance()->startSimpleTransaction(
+ nksCard, "SCD PASSWD PW1.CH.SIG", this, "setSigGPinSettingResult");
} else {
- ReaderStatus::mutableInstance()
- ->startSimpleTransaction("SCD PASSWD PW1.CH",
- this, "setNksPinSettingResult");
+ ReaderStatus::mutableInstance()->startSimpleTransaction(
+ nksCard, "SCD PASSWD PW1.CH", this, "setNksPinSettingResult");
}
}
diff --git a/src/view/netkeywidget.h b/src/view/netkeywidget.h
index 92fc9980e..0a777c607 100644
--- a/src/view/netkeywidget.h
+++ b/src/view/netkeywidget.h
@@ -1,61 +1,62 @@
/* 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 <QWidget>
#include <gpgme++/error.h>
#include <string>
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);
+ void setCard(const SmartCard::NetKeyCard *card);
private:
void handleResult(const GpgME::Error &err, QPushButton *btn);
void doChangePin(bool sigG);
private Q_SLOTS:
void setSigGPinSettingResult(const GpgME::Error &err);
void setNksPinSettingResult(const GpgME::Error &err);
private:
- QLabel *mSerialNumber,
+ std::string mSerialNumber;
+ QLabel *mSerialNumberLabel,
*mVersionLabel,
*mLearnKeysLabel,
*mErrorLabel;
NullPinWidget *mNullPinWidget;
QPushButton *mLearnKeysBtn,
*mChangeNKSPINBtn,
*mChangeSigGPINBtn;
KeyTreeView *mTreeView;
QScrollArea *mArea;
};
} // namespace Kleo
#endif // VIEW_NETKEYWIDGET_H
diff --git a/src/view/nullpinwidget.cpp b/src/view/nullpinwidget.cpp
index 28fee8a5e..b6e3a7a62 100644
--- a/src/view/nullpinwidget.cpp
+++ b/src/view/nullpinwidget.cpp
@@ -1,127 +1,137 @@
/* view/nullpinwidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "nullpinwidget.h"
#include "kleopatra_debug.h"
+#include "smartcard/netkeycard.h"
#include "smartcard/readerstatus.h"
#include <gpgme++/error.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <KLocalizedString>
#include <KMessageBox>
using namespace Kleo;
using namespace Kleo::SmartCard;
NullPinWidget::NullPinWidget(QWidget *parent)
: QWidget(parent)
{
const auto nullTitle = i18nc("NullPIN is a word that is used all over in the netkey "
"documentation and should be understandable by Netkey cardholders",
"The NullPIN is still active on this card.");
const auto nullDescription = i18n("You need to set a PIN before you can use the certificates.");
const auto descriptionLbl = new QLabel(QStringLiteral("<b>%1</b><br/>%2").arg(nullTitle, nullDescription));
auto vLay = new QVBoxLayout(this);
vLay->addWidget(descriptionLbl, 0, Qt::AlignCenter);
mNKSBtn = new QPushButton(i18nc("NKS is an identifier for a type of keys on a NetKey card", "Set NKS PIN"));
mSigGBtn = new QPushButton(i18nc("SigG is an identifier for a type of keys on a NetKey card", "Set SigG PIN"));
connect(mNKSBtn, &QPushButton::clicked, this, [this] () {
mNKSBtn->setEnabled(false);
doChangePin(false);
});
connect(mSigGBtn, &QPushButton::clicked, this, [this] () {
mSigGBtn->setEnabled(false);
doChangePin(true);
});
auto hLayBtn = new QHBoxLayout;
hLayBtn->addStretch(1);
hLayBtn->addWidget(mNKSBtn);
hLayBtn->addWidget(mSigGBtn);
hLayBtn->addStretch(1);
vLay->addLayout(hLayBtn);
}
+void NullPinWidget::setSerialNumber(const std::string &serialNumber)
+{
+ mSerialNumber = serialNumber;
+}
+
void NullPinWidget::doChangePin(bool sigG)
{
auto ret = KMessageBox::warningContinueCancel(this,
i18n("Setting a PIN is required but <b>can't be reverted</b>.") +
QStringLiteral("<p>%1</p><p>%2</p>").arg(
i18n("If you proceed you will be asked to enter a new PIN "
"and later to repeat that PIN.")).arg(
i18n("It will <b>not be possible</b> to recover the "
"card if the PIN has been entered wrongly more than 2 times.")),
i18n("Set initial PIN"),
KStandardGuiItem::cont(),
KStandardGuiItem::cancel());
if (ret != KMessageBox::Continue) {
return;
}
+ const auto nksCard = ReaderStatus::instance()->getCard<NetKeyCard>(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("SCD PASSWD --nullpin PW1.CH.SIG",
- this, "setSigGPinSettingResult");
+ ReaderStatus::mutableInstance()->startSimpleTransaction(
+ nksCard, "SCD PASSWD --nullpin PW1.CH.SIG", this, "setSigGPinSettingResult");
} else {
- ReaderStatus::mutableInstance()
- ->startSimpleTransaction("SCD PASSWD --nullpin PW1.CH",
- this, "setNksPinSettingResult");
+ ReaderStatus::mutableInstance()->startSimpleTransaction(
+ nksCard, "SCD PASSWD --nullpin PW1.CH", this, "setNksPinSettingResult");
}
}
void NullPinWidget::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;
}
btn->setVisible(false);
if (!mNKSBtn->isVisible() && !mSigGBtn->isVisible()) {
// Both pins are set, we can hide.
setVisible(false);
}
}
void NullPinWidget::setSigGVisible(bool val)
{
mSigGBtn->setVisible(val);
}
void NullPinWidget::setNKSVisible(bool val)
{
mNKSBtn->setVisible(val);
}
void NullPinWidget::setSigGPinSettingResult(const GpgME::Error &err)
{
handleResult(err, mSigGBtn);
}
void NullPinWidget::setNksPinSettingResult(const GpgME::Error &err)
{
handleResult(err, mNKSBtn);
}
diff --git a/src/view/nullpinwidget.h b/src/view/nullpinwidget.h
index 8445a7158..fa681b70d 100644
--- a/src/view/nullpinwidget.h
+++ b/src/view/nullpinwidget.h
@@ -1,47 +1,49 @@
#ifndef VIEW_NULLPINWIDGET_H
#define VIEW_NULLPINWIDGET_H
/* view/nullpinwidget.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <QWidget>
class QPushButton;
namespace GpgME
{
class Error;
} // namespace GpgME
namespace Kleo
{
class NullPinWidget: public QWidget
{
Q_OBJECT
public:
explicit NullPinWidget(QWidget *parent = nullptr);
+ void setSerialNumber(const std::string &serialNumber);
void setSigGVisible(bool val);
void setNKSVisible(bool val);
private:
void doChangePin(bool sigG);
void handleResult(const GpgME::Error &err, QPushButton *btn);
private Q_SLOTS:
void setSigGPinSettingResult(const GpgME::Error &err);
void setNksPinSettingResult(const GpgME::Error &err);
private:
+ std::string mSerialNumber;
QPushButton *mNKSBtn,
*mSigGBtn;
};
} // namespace Kleo
#endif // VIEW_NULLPINWIDGET_H
diff --git a/src/view/pgpcardwidget.cpp b/src/view/pgpcardwidget.cpp
index 9f4b1cf93..57a174b94 100644
--- a/src/view/pgpcardwidget.cpp
+++ b/src/view/pgpcardwidget.cpp
@@ -1,473 +1,483 @@
/* view/pgpcardwiget.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-License-Identifier: GPL-2.0-or-later
*/
#include "pgpcardwidget.h"
#include "kleopatra_debug.h"
#include "commands/changepincommand.h"
#include "smartcard/openpgpcard.h"
#include "smartcard/readerstatus.h"
#include "dialogs/gencardkeydialog.h"
#include "utils/gnupg-helper.h"
#include <QProgressDialog>
#include <QThread>
#include <QScrollArea>
#include <QInputDialog>
#include <QFileDialog>
#include <QFileInfo>
#include <QGridLayout>
#include <QPushButton>
#include <QLabel>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <KLocalizedString>
#include <KMessageBox>
#include <Libkleo/KeyCache>
#include <Libkleo/Formatting>
#include <gpgme++/gpgmepp_version.h>
#include <gpgme++/data.h>
#include <gpgme++/context.h>
#include <QGpgME/DataProvider>
#include <gpgme++/gpggencardkeyinteractor.h>
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::SmartCard;
namespace {
class GenKeyThread: public QThread
{
Q_OBJECT
public:
explicit GenKeyThread(const GenCardKeyDialog::KeyParams &params, const std::string &serial):
mSerial(serial),
mParams(params)
{
}
GpgME::Error error()
{
return mErr;
}
std::string bkpFile()
{
return mBkpFile;
}
protected:
void run() override {
GpgME::GpgGenCardKeyInteractor *ei = new GpgME::GpgGenCardKeyInteractor(mSerial);
ei->setAlgo(GpgME::GpgGenCardKeyInteractor::RSA);
ei->setKeySize(QByteArray::fromStdString(mParams.algorithm).toInt());
ei->setNameUtf8(mParams.name.toStdString());
ei->setEmailUtf8(mParams.email.toStdString());
ei->setDoBackup(mParams.backup);
const auto ctx = std::shared_ptr<GpgME::Context> (GpgME::Context::createForProtocol(GpgME::OpenPGP));
QGpgME::QByteArrayDataProvider dp;
GpgME::Data data(&dp);
mErr = ctx->cardEdit(GpgME::Key(), std::unique_ptr<GpgME::EditInteractor> (ei), data);
mBkpFile = ei->backupFileName();
}
private:
GpgME::Error mErr;
std::string mSerial;
GenCardKeyDialog::KeyParams mParams;
std::string mBkpFile;
};
} // Namespace
PGPCardWidget::PGPCardWidget(QWidget *parent):
QWidget(parent),
mSerialNumber(new QLabel(this)),
mCardHolderLabel(new QLabel(this)),
mVersionLabel(new QLabel(this)),
mSigningKey(new QLabel(this)),
mEncryptionKey(new QLabel(this)),
mAuthKey(new QLabel(this)),
mUrlLabel(new QLabel(this)),
mCardIsEmpty(false)
{
auto grid = new QGridLayout;
int row = 0;
// Set up the scroll are
auto area = new QScrollArea;
area->setFrameShape(QFrame::NoFrame);
area->setWidgetResizable(true);
auto areaWidget = new QWidget;
auto areaVLay = new QVBoxLayout(areaWidget);
areaVLay->addLayout(grid);
areaVLay->addStretch(1);
area->setWidget(areaWidget);
auto myLayout = new QVBoxLayout(this);
myLayout->addWidget(area);
// Version and Serialnumber
grid->addWidget(mVersionLabel, row++, 0, 1, 2);
mVersionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
grid->addWidget(new QLabel(i18n("Serial number:")), row, 0);
grid->addWidget(mSerialNumber, row++, 1);
mSerialNumber->setTextInteractionFlags(Qt::TextBrowserInteraction);
// Cardholder Row
grid->addWidget(new QLabel(i18nc("The owner of a smartcard. GnuPG refers to this as cardholder.",
"Cardholder:")), row, 0);
grid->addWidget(mCardHolderLabel, row, 1);
mCardHolderLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
auto nameButtton = new QPushButton;
nameButtton->setIcon(QIcon::fromTheme(QStringLiteral("cell_edit")));
nameButtton->setToolTip(i18n("Change"));
grid->addWidget(nameButtton, row++, 2);
connect(nameButtton, &QPushButton::clicked, this, &PGPCardWidget::changeNameRequested);
// URL Row
grid->addWidget(new QLabel(i18nc("The URL under which a public key that "
"corresponds to a smartcard can be downloaded",
"Pubkey URL:")), row, 0);
grid->addWidget(mUrlLabel, row, 1);
mUrlLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
auto urlButtton = new QPushButton;
urlButtton->setIcon(QIcon::fromTheme(QStringLiteral("cell_edit")));
urlButtton->setToolTip(i18n("Change"));
grid->addWidget(urlButtton, row++, 2);
connect(urlButtton, &QPushButton::clicked, this, &PGPCardWidget::changeUrlRequested);
// The keys
auto line1 = new QFrame();
line1->setFrameShape(QFrame::HLine);
grid->addWidget(line1, row++, 0, 1, 4);
grid->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("Keys:"))), row++, 0);
grid->addWidget(new QLabel(i18n("Signature:")), row, 0);
grid->addWidget(mSigningKey, row++, 1);
mSigningKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
grid->addWidget(new QLabel(i18n("Encryption:")), row, 0);
grid->addWidget(mEncryptionKey, row++, 1);
mEncryptionKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
grid->addWidget(new QLabel(i18n("Authentication:")), row, 0);
grid->addWidget(mAuthKey, row++, 1);
mAuthKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
auto line2 = new QFrame();
line2->setFrameShape(QFrame::HLine);
grid->addWidget(line2, row++, 0, 1, 4);
grid->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("Actions:"))), row++, 0);
auto actionLayout = new QHBoxLayout;
auto generateButton = new QPushButton(i18n("Generate new Keys"));
generateButton->setToolTip(i18n("Create a new primary key and generate subkeys on the card."));
actionLayout->addWidget(generateButton);
connect(generateButton, &QPushButton::clicked, this, &PGPCardWidget::genkeyRequested);
auto pinButtton = new QPushButton(i18n("Change PIN"));
pinButtton->setToolTip(i18n("Change the PIN required to unblock the smartcard."));
actionLayout->addWidget(pinButtton);
connect(pinButtton, &QPushButton::clicked, this, [this] () { doChangePin(OpenPGPCard::pinKeyRef()); });
auto pukButton = new QPushButton(i18n("Change Admin PIN"));
pukButton->setToolTip(i18n("Change the PIN required to unlock the smartcard."));
actionLayout->addWidget(pukButton);
connect(pukButton, &QPushButton::clicked, this, [this] () { doChangePin(OpenPGPCard::adminPinKeyRef()); });
auto resetCodeButton = new QPushButton(i18n("Change Reset Code"));
pukButton->setToolTip(i18n("Change the PIN required to reset the smartcard to an empty state."));
actionLayout->addWidget(resetCodeButton);
connect(resetCodeButton, &QPushButton::clicked, this, [this] () { doChangePin(OpenPGPCard::resetCodeKeyRef()); });
actionLayout->addStretch(-1);
grid->addLayout(actionLayout, row++, 0, 1, 4);
grid->setColumnStretch(4, -1);
}
void PGPCardWidget::setCard(const OpenPGPCard *card)
{
const QString version = QString::fromStdString(card->cardVersion());
mIs21 = versionIsAtLeast("2.1", card->cardVersion().c_str());
const QString manufacturer = QString::fromStdString(card->manufacturer());
const bool manufacturerIsUnknown = manufacturer.isEmpty() || manufacturer == QLatin1String("unknown");
mVersionLabel->setText(manufacturerIsUnknown ?
i18nc("Placeholder is a version number", "Unknown OpenPGP v%1 card", version) :
i18nc("First placeholder is manufacturer, second placeholder is a version number",
"%1 OpenPGP v%2 card", manufacturer, version));
const QString sn = QString::fromStdString(card->serialNumber()).mid(16, 12);
mSerialNumber->setText(sn);
mRealSerial = card->serialNumber();
const auto holder = QString::fromStdString(card->cardHolder());
const auto url = QString::fromStdString(card->pubkeyUrl());
mCardHolderLabel->setText(holder.isEmpty() ? i18n("not set") : holder);
mUrl = url;
mUrlLabel->setText(url.isEmpty() ? i18n("not set") :
QStringLiteral("<a href=\"%1\">%1</a>").arg(url.toHtmlEscaped()));
mUrlLabel->setOpenExternalLinks(true);
updateKey(mSigningKey, card->sigFpr());
updateKey(mEncryptionKey, card->encFpr());
updateKey(mAuthKey, card->authFpr());
mCardIsEmpty = card->authFpr().empty() && card->sigFpr().empty() && card->encFpr().empty();
}
void PGPCardWidget::doChangePin(const std::string &keyRef)
{
auto cmd = new ChangePinCommand(mRealSerial, this);
this->setEnabled(false);
connect(cmd, &ChangePinCommand::finished,
this, [this]() {
this->setEnabled(true);
});
cmd->setKeyRef(keyRef);
cmd->start();
}
void PGPCardWidget::doGenKey(GenCardKeyDialog *dlg)
{
const auto params = dlg->getKeyParams();
auto progress = new QProgressDialog(this, Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::Dialog);
progress->setAutoClose(true);
progress->setMinimumDuration(0);
progress->setMaximum(0);
progress->setMinimum(0);
progress->setModal(true);
progress->setCancelButton(nullptr);
progress->setWindowTitle(i18nc("@title:window", "Generating Keys"));
progress->setLabel(new QLabel(i18n("This may take several minutes...")));
GenKeyThread *workerThread = new GenKeyThread(params, mRealSerial);
connect(workerThread, &QThread::finished, this, [this, workerThread, progress] {
progress->accept();
progress->deleteLater();
genKeyDone(workerThread->error(), workerThread->bkpFile());
delete workerThread;
});
workerThread->start();
progress->exec();
}
void PGPCardWidget::genKeyDone(const GpgME::Error &err, const std::string &backup)
{
if (err) {
KMessageBox::error(this, i18nc("@info",
"Failed to generate new key: %1", QString::fromLatin1(err.asString())),
i18nc("@title", "Error"));
return;
}
if (err.isCanceled()) {
return;
}
if (!backup.empty()) {
const auto bkpFile = QString::fromStdString(backup);
QFileInfo fi(bkpFile);
const auto target = QFileDialog::getSaveFileName(this, i18n("Save backup of encryption key"),
fi.fileName(),
QStringLiteral("%1 (*.gpg)").arg(i18n("Backup Key")));
if (!target.isEmpty() && !QFile::copy(bkpFile, target)) {
KMessageBox::error(this, i18nc("@info",
"Failed to move backup. The backup key is still stored under: %1", bkpFile),
i18nc("@title", "Error"));
} else if (!target.isEmpty()) {
QFile::remove(bkpFile);
}
}
KMessageBox::information(this, i18nc("@info",
"Successfully generated a new key for this card."),
i18nc("@title", "Success"));
}
void PGPCardWidget::genkeyRequested()
{
if (!mCardIsEmpty) {
auto ret = KMessageBox::warningContinueCancel(this,
i18n("The existing keys on this card will be <b>deleted</b> "
"and replaced by new keys.") + QStringLiteral("<br/><br/>") +
i18n("It will no longer be possible to decrypt past communication "
"encrypted for the existing key."),
i18n("Secret Key Deletion"),
KStandardGuiItem::guiItem(KStandardGuiItem::Delete),
KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous);
if (ret != KMessageBox::Continue) {
return;
}
}
GenCardKeyDialog *dlg = new GenCardKeyDialog(GenCardKeyDialog::AllKeyAttributes, this);
std::vector<std::pair<std::string, QString>> algos = {
{ "1024", QStringLiteral("RSA 1024") },
{ "2048", QStringLiteral("RSA 2048") },
{ "3072", QStringLiteral("RSA 3072") }
};
// There is probably a better way to check for capabilities
if (mIs21) {
algos.push_back({"4096", QStringLiteral("RSA 4096")});
}
dlg->setSupportedAlgorithms(algos, "2048");
connect(dlg, &QDialog::accepted, this, [this, dlg] () {
doGenKey(dlg);
dlg->deleteLater();
});
dlg->setModal(true);
dlg->show();
}
void PGPCardWidget::changeNameRequested()
{
QString text = mCardHolderLabel->text();
while (true) {
bool ok = false;
text = QInputDialog::getText(this, i18n("Change cardholder"),
i18n("New name:"), QLineEdit::Normal,
text, &ok, Qt::WindowFlags(),
Qt::ImhLatinOnly);
if (!ok) {
return;
}
// Some additional restrictions imposed by gnupg
if (text.contains(QLatin1Char('<'))) {
KMessageBox::error(this, i18nc("@info",
"The \"<\" character may not be used."),
i18nc("@title", "Error"));
continue;
}
if (text.contains(QLatin1String(" "))) {
KMessageBox::error(this, i18nc("@info",
"Double spaces are not allowed"),
i18nc("@title", "Error"));
continue;
}
if (text.size() > 38) {
KMessageBox::error(this, i18nc("@info",
"The size of the name may not exceed 38 characters."),
i18nc("@title", "Error"));
}
break;
}
auto parts = text.split(QLatin1Char(' '));
const auto lastName = parts.takeLast();
const QString formatted = lastName + QStringLiteral("<<") + parts.join(QLatin1Char('<'));
- ReaderStatus::mutableInstance()
- ->startSimpleTransaction(QStringLiteral("SCD SETATTR DISP-NAME %1").arg(formatted).toUtf8().constData(),
- this, "changeNameResult");
+ const auto pgpCard = ReaderStatus::instance()->getCard<OpenPGPCard>(mRealSerial);
+ if (!pgpCard) {
+ KMessageBox::error(this, i18n("Failed to find the OpenPGP card with the serial number: %1", QString::fromStdString(mRealSerial)));
+ return;
+ }
+
+ const QByteArray command = QByteArrayLiteral("SCD SETATTR DISP-NAME ") + formatted.toUtf8();
+ ReaderStatus::mutableInstance()->startSimpleTransaction(pgpCard, command, this, "changeNameResult");
}
void PGPCardWidget::changeNameResult(const GpgME::Error &err)
{
if (err) {
KMessageBox::error(this, i18nc("@info",
"Name change failed: %1", QString::fromLatin1(err.asString())),
i18nc("@title", "Error"));
return;
}
if (!err.isCanceled()) {
KMessageBox::information(this, i18nc("@info",
"Name successfully changed."),
i18nc("@title", "Success"));
ReaderStatus::mutableInstance()->updateStatus();
}
}
void PGPCardWidget::changeUrlRequested()
{
QString text = mUrl;
while (true) {
bool ok = false;
text = QInputDialog::getText(this, i18n("Change the URL where the pubkey can be found"),
i18n("New pubkey URL:"), QLineEdit::Normal,
text, &ok, Qt::WindowFlags(),
Qt::ImhLatinOnly);
if (!ok) {
return;
}
// Some additional restrictions imposed by gnupg
if (text.size() > 254) {
KMessageBox::error(this, i18nc("@info",
"The size of the URL may not exceed 254 characters."),
i18nc("@title", "Error"));
}
break;
}
- ReaderStatus::mutableInstance()
- ->startSimpleTransaction(QStringLiteral("SCD SETATTR PUBKEY-URL %1").arg(text).toUtf8().constData(),
- this, "changeUrlResult");
+ const auto pgpCard = ReaderStatus::instance()->getCard<OpenPGPCard>(mRealSerial);
+ if (!pgpCard) {
+ KMessageBox::error(this, i18n("Failed to find the OpenPGP card with the serial number: %1", QString::fromStdString(mRealSerial)));
+ return;
+ }
+
+ const QByteArray command = QByteArrayLiteral("SCD SETATTR PUBKEY-URL ") + text.toUtf8();
+ ReaderStatus::mutableInstance()->startSimpleTransaction(pgpCard, command, this, "changeUrlResult");
}
void PGPCardWidget::changeUrlResult(const GpgME::Error &err)
{
if (err) {
KMessageBox::error(this, i18nc("@info",
"URL change failed: %1", QString::fromLatin1(err.asString())),
i18nc("@title", "Error"));
return;
}
if (!err.isCanceled()) {
KMessageBox::information(this, i18nc("@info",
"URL successfully changed."),
i18nc("@title", "Success"));
ReaderStatus::mutableInstance()->updateStatus();
}
}
void PGPCardWidget::updateKey(QLabel *label, const std::string &fpr)
{
label->setText(QString::fromStdString(fpr));
if (fpr.empty()) {
label->setText(i18n("Slot empty"));
return;
}
std::vector<std::string> vec;
std::string keyid = fpr;
keyid.erase(0, keyid.size() - 16);
vec.push_back(keyid);
const auto subkeys = KeyCache::instance()->findSubkeysByKeyID(vec);
if (subkeys.empty() || subkeys[0].isNull()) {
label->setToolTip(i18n("Public key not found."));
return;
}
QStringList toolTips;
toolTips.reserve(subkeys.size());
for (const auto &sub: subkeys) {
// Yep you can have one subkey associated with multiple
// primary keys.
toolTips << Formatting::toolTip(sub.parent(), Formatting::Validity |
Formatting::StorageLocation |
Formatting::ExpiryDates |
Formatting::UserIDs |
Formatting::Fingerprint);
}
label->setToolTip(toolTips.join(QLatin1String("<br/>")));
return;
}
#include "pgpcardwidget.moc"

File Metadata

Mime Type
text/x-diff
Expires
Thu, Feb 5, 9:41 PM (1 d, 14 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
1b/61/b934b5ab76a5e66408eb19e3db4f

Event Timeline