Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F35313472
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
125 KB
Subscribers
None
View Options
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 ¶ms, 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
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Feb 5, 9:41 PM (1 d, 11 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
1b/61/b934b5ab76a5e66408eb19e3db4f
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment