Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F41057503
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
57 KB
Subscribers
None
View Options
diff --git a/src/commands/keytocardcommand.cpp b/src/commands/keytocardcommand.cpp
index eeabf95d9..d1a20aea7 100644
--- a/src/commands/keytocardcommand.cpp
+++ b/src/commands/keytocardcommand.cpp
@@ -1,524 +1,524 @@
/* 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 "smartcard/utils.h"
#include <Libkleo/Dn>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/KeySelectionDialog>
#include <KLocalizedString>
#include <QDateTime>
#include <QInputDialog>
#include <QStringList>
#include <gpg-error.h>
#if GPG_ERROR_VERSION_NUMBER >= 0x12400 // 1.36
# define GPG_ERROR_HAS_NO_AUTH
#endif
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::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);
explicit Private(KeyToCardCommand *qq, const std::string &slot, const std::string &serialNumber, const std::string &appName);
~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:
std::string appName;
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_)
: CardCommand::Private(qq, "", nullptr)
, subkey(subkey_)
{
}
KeyToCardCommand::Private::Private(KeyToCardCommand *qq, const std::string &slot, const std::string &serialNumber, const std::string &appName_)
: CardCommand::Private(qq, serialNumber, nullptr)
, appName(appName_)
, cardSlot(slot)
{
}
KeyToCardCommand::Private::~Private()
{
}
namespace {
static std::shared_ptr<Card> getCardToTransferSubkeyTo(const Subkey &subkey, QWidget *parent)
{
const std::vector<std::shared_ptr<Card> > suitableCards = KeyToCardCommand::getSuitableCards(subkey);
if (suitableCards.empty()) {
return std::shared_ptr<Card>();
} else if (suitableCards.size() == 1) {
return suitableCards[0];
}
QStringList options;
for (const auto &card: suitableCards) {
options.push_back(i18nc("smartcard application - serial number of smartcard", "%1 - %2",
displayAppName(card->appName()), card->displaySerialNumber()));
}
bool ok;
const QString choice = QInputDialog::getItem(parent, i18n("Select Card"),
i18n("Please select the card the key should be written to:"), options, /* current= */ 0, /* editable= */ false, &ok);
if (!ok) {
return std::shared_ptr<Card>();
}
const int index = options.indexOf(choice);
return suitableCards[index];
}
}
void KeyToCardCommand::Private::start()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::Private::start()";
if (!subkey.isNull() && serialNumber().empty()) {
const auto card = getCardToTransferSubkeyTo(subkey, parentWidgetOrView());
if (!card) {
finished();
return;
}
setSerialNumber(card->serialNumber());
appName = card->appName();
}
const auto card = SmartCard::ReaderStatus::instance()->getCard(serialNumber(), appName);
if (!card) {
error(i18n("Failed to find the card with the serial number: %1", QString::fromStdString(serialNumber())));
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();
+ existingKey = pgpCard->keyFingerprint(OpenPGPCard::pgpSigKeyRef());
} else if (slot == 2) {
- existingKey = pgpCard->encFpr();
+ existingKey = pgpCard->keyFingerprint(OpenPGPCard::pgpEncKeyRef());
encKeyWarning = i18n("It will no longer be possible to decrypt past communication "
"encrypted for the existing key.");
} else if (slot == 3) {
- existingKey = pgpCard->authFpr();
+ existingKey = pgpCard->keyFingerprint(OpenPGPCard::pgpAuthKeyRef());
}
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(), Qt::UTC);
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(pgpCard, cmd.toUtf8(), q_func(), "keyToOpenPGPCardDone");
}
namespace {
static std::vector<Key> getSigningCertificates()
{
std::vector<Key> signingCertificates = KeyCache::instance()->secretKeys();
const auto it = std::remove_if(signingCertificates.begin(), signingCertificates.end(),
[](const Key &key) {
return ! (key.protocol() == GpgME::CMS &&
!key.subkey(0).isNull() &&
key.subkey(0).canSign() &&
!key.subkey(0).canEncrypt() &&
key.subkey(0).isSecret() &&
!key.subkey(0).isCardKey());
});
signingCertificates.erase(it, signingCertificates.end());
return signingCertificates;
}
static std::vector<Key> getEncryptionCertificates()
{
std::vector<Key> encryptionCertificates = KeyCache::instance()->secretKeys();
const auto it = std::remove_if(encryptionCertificates.begin(), encryptionCertificates.end(),
[](const Key &key) {
return ! (key.protocol() == GpgME::CMS &&
!key.subkey(0).isNull() &&
key.subkey(0).canEncrypt() &&
key.subkey(0).isSecret() &&
!key.subkey(0).isCardKey());
});
encryptionCertificates.erase(it, encryptionCertificates.end());
return encryptionCertificates;
}
}
Subkey KeyToCardCommand::Private::getSubkeyToTransferToPIVCard(const std::string &cardSlot, const std::shared_ptr<PIVCard> &/*card*/)
{
if (cardSlot != PIVCard::cardAuthenticationKeyRef() && cardSlot != PIVCard::keyManagementKeyRef()) {
return Subkey();
}
const std::vector<Key> certificates = cardSlot == PIVCard::cardAuthenticationKeyRef() ? getSigningCertificates() : getEncryptionCertificates();
if (certificates.empty()) {
error(i18n("Sorry! No suitable certificate to write to this card slot was found."));
return Subkey();
}
auto dialog = new KeySelectionDialog(parentWidgetOrView());
dialog->setWindowTitle(i18nc("@title:window", "Select Certificate"));
dialog->setText(i18n("Please select the certificate whose key pair you want to write to the card:"));
dialog->setKeys(certificates);
if (dialog->exec() == QDialog::Rejected) {
return Subkey();
}
return dialog->selectedKey().subkey(0);
}
void KeyToCardCommand::Private::startKeyToPIVCard()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::Private::startKeyToPIVCard()";
const auto pivCard = SmartCard::ReaderStatus::instance()->getCard<PIVCard>(serialNumber());
if (!pivCard) {
error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(serialNumber())));
finished();
return;
}
if (cardSlot != PIVCard::cardAuthenticationKeyRef() && cardSlot != PIVCard::keyManagementKeyRef()) {
// key to card is only supported for the Card Authentication key and the Key Management key
finished();
return;
}
if (subkey.isNull()) {
subkey = getSubkeyToTransferToPIVCard(cardSlot, pivCard);
}
if (subkey.isNull()) {
finished();
return;
}
if (subkey.parent().protocol() != GpgME::CMS) {
error(i18n("Sorry! This key cannot be transferred to a PIV card."));
finished();
return;
}
if (!subkey.canEncrypt() && !subkey.canSign()) {
error(i18n("Sorry! Only encryption keys and signing keys can be transferred to a PIV card."));
finished();
return;
}
// Check if we need to do the overwrite warning.
if (!overwriteExistingAlreadyApproved) {
const std::string existingKey = pivCard->keyInfo(cardSlot).grip;
if (!existingKey.empty() && (existingKey != subkey.keyGrip())) {
const QString decryptionWarning = (cardSlot == PIVCard::keyManagementKeyRef()) ?
i18n("It will no longer be possible to decrypt past communication encrypted for the existing key.") :
QString();
const QString message = i18nc("@info",
"<p>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(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 &subkey)
: CardCommand(new Private(this, subkey))
{
}
KeyToCardCommand::KeyToCardCommand(const std::string& cardSlot, const std::string &serialNumber, const std::string &appName)
: CardCommand(new Private(this, cardSlot, serialNumber, appName))
{
}
KeyToCardCommand::~KeyToCardCommand()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::~KeyToCardCommand()";
}
// static
std::vector<std::shared_ptr<Card> > KeyToCardCommand::getSuitableCards(const GpgME::Subkey &subkey)
{
std::vector<std::shared_ptr<Card> > suitableCards;
if (subkey.isNull() || subkey.parent().protocol() != GpgME::OpenPGP) {
return suitableCards;
}
for (const auto &card: ReaderStatus::instance()->getCards()) {
if (card->appName() == OpenPGPCard::AppName) {
suitableCards.push_back(card);
}
}
return suitableCards;
}
void KeyToCardCommand::keyToOpenPGPCardDone(const GpgME::Error &err)
{
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(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) {
#ifdef GPG_ERROR_HAS_NO_AUTH
// gpgme 1.13 reports "BAD PIN" instead of "NO AUTH"
if (err.code() == GPG_ERR_NO_AUTH || err.code() == GPG_ERR_BAD_PIN) {
d->authenticate();
return;
}
#endif
d->error(i18nc("@info",
"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/smartcard/openpgpcard.cpp b/src/smartcard/openpgpcard.cpp
index 043625752..42e83d187 100644
--- a/src/smartcard/openpgpcard.cpp
+++ b/src/smartcard/openpgpcard.cpp
@@ -1,136 +1,162 @@
/* smartcard/openpgpcard.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
*/
/* Code in this file is partly based on the GNU Privacy Assistant
* (cm-openpgp.c) git rev. 0a78795146661234070681737b3e08228616441f
*
* Whis is:
* SPDX-FileCopyrightText: 2008, 2009 g 10 Code GmbH
*
* And may be licensed under the GNU General Public License
* as published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*/
#include "openpgpcard.h"
-#include "kleopatra_debug.h"
+#include <KLocalizedString>
+#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::SmartCard;
// static
const std::string OpenPGPCard::AppName = "openpgp";
OpenPGPCard::OpenPGPCard(const Card &card)
: Card(card)
{
setAppName(AppName);
+ setInitialKeyInfos(OpenPGPCard::supportedKeys());
+}
+
+// static
+std::string OpenPGPCard::pgpSigKeyRef()
+{
+ return std::string("OPENPGP.1");
+}
+
+// static
+std::string OpenPGPCard::pgpEncKeyRef()
+{
+ return std::string("OPENPGP.2");
+}
+
+// static
+std::string OpenPGPCard::pgpAuthKeyRef()
+{
+ return std::string("OPENPGP.3");
}
// static
std::string OpenPGPCard::pinKeyRef()
{
return std::string("OPENPGP.1");
}
// static
std::string OpenPGPCard::adminPinKeyRef()
{
return std::string("OPENPGP.3");
}
// static
std::string OpenPGPCard::resetCodeKeyRef()
{
return std::string("OPENPGP.2");
}
-std::string OpenPGPCard::sigFpr() const
+// static
+const std::vector<KeyPairInfo> & OpenPGPCard::supportedKeys()
{
- return mMetaInfo.value("SIGKEY-FPR");
-}
+ static const std::vector<KeyPairInfo> keyInfos = {
+ {OpenPGPCard::pgpSigKeyRef(), "", "sc", "", ""},
+ {OpenPGPCard::pgpEncKeyRef(), "", "e", "", ""},
+ {OpenPGPCard::pgpAuthKeyRef(), "", "a", "", ""}
+ };
-std::string OpenPGPCard::encFpr() const
-{
- return mMetaInfo.value("ENCKEY-FPR");
+ return keyInfos;
}
-std::string OpenPGPCard::authFpr() const
+// static
+QString OpenPGPCard::keyDisplayName(const std::string &keyRef)
{
- return mMetaInfo.value("AUTHKEY-FPR");
+ static const QMap<std::string, QString> displayNames = {
+ { OpenPGPCard::pgpSigKeyRef(), i18n("Signature") },
+ { OpenPGPCard::pgpEncKeyRef(), i18n("Encryption") },
+ { OpenPGPCard::pgpAuthKeyRef(), i18n("Authentication") }
+ };
+
+ return displayNames.value(keyRef);
}
void OpenPGPCard::setCardInfo(const std::vector< std::pair<std::string, std::string> > &infos)
{
qCDebug(KLEOPATRA_LOG) << "Card" << serialNumber().c_str() << "info:";
for (const auto &pair: infos) {
qCDebug(KLEOPATRA_LOG) << pair.first.c_str() << ":" << pair.second.c_str();
if (parseCardInfo(pair.first, pair.second)) {
continue;
}
- if (pair.first == "KEY-FPR" ||
- pair.first == "KEY-TIME") {
- // Key fpr and key time need to be distinguished, the number
- // of the key decides the usage.
+ if (pair.first == "KEY-FPR") {
const auto values = QString::fromStdString(pair.second).split(QLatin1Char(' '));
if (values.size() < 2) {
qCWarning(KLEOPATRA_LOG) << "Invalid entry.";
setStatus(Card::CardError);
continue;
}
- const auto usage = values[0];
+ const auto keyNumber = values[0];
+ const std::string keyRef = "OPENPGP." + keyNumber.toStdString();
const auto fpr = values[1].toStdString();
- if (usage == QLatin1Char('1')) {
- mMetaInfo.insert(std::string("SIG") + pair.first, fpr);
- } else if (usage == QLatin1Char('2')) {
- mMetaInfo.insert(std::string("ENC") + pair.first, fpr);
- } else if (usage == QLatin1Char('3')) {
- mMetaInfo.insert(std::string("AUTH") + pair.first, fpr);
+ if (keyNumber == QLatin1Char('1') || keyNumber == QLatin1Char('2') || keyNumber == QLatin1Char('3')) {
+ mMetaInfo.insert("KLEO-FPR-" + keyRef, fpr);
} else {
// Maybe more keyslots in the future?
qCDebug(KLEOPATRA_LOG) << "Unhandled keyslot";
}
} else {
mMetaInfo.insert(pair.first, pair.second);
}
}
}
+std::string OpenPGPCard::keyFingerprint(const std::string &keyRef) const
+{
+ return mMetaInfo.value("KLEO-FPR-" + keyRef);
+}
+
bool OpenPGPCard::operator == (const Card& rhs) const
{
const OpenPGPCard *other = dynamic_cast<const OpenPGPCard *>(&rhs);
if (!other) {
return false;
}
return Card::operator ==(rhs)
- && sigFpr() == other->sigFpr()
- && encFpr() == other->encFpr()
- && authFpr() == other->authFpr()
- && manufacturer() == other->manufacturer()
- && cardHolder() == other->cardHolder()
- && pubkeyUrl() == other->pubkeyUrl();
+ && mMetaInfo == other->mMetaInfo
+ && mManufacturer == other->mManufacturer;
}
void OpenPGPCard::setManufacturer(const std::string &manufacturer)
{
mManufacturer = manufacturer;
}
std::string OpenPGPCard::manufacturer() const
{
return mManufacturer;
}
std::string OpenPGPCard::pubkeyUrl() const
{
return mMetaInfo.value("PUBKEY-URL");
}
diff --git a/src/smartcard/openpgpcard.h b/src/smartcard/openpgpcard.h
index 42a997395..dd6f1ece9 100644
--- a/src/smartcard/openpgpcard.h
+++ b/src/smartcard/openpgpcard.h
@@ -1,52 +1,62 @@
-#ifndef SMARTCARD_OPENPGPCARD_H
-#define SMARTCARD_OPENPGPCARD_H
/* smartcard/openpgpcard.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
+ SPDX-FileCopyrightText: 2020 g10 Code GmbH
+ SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
-
-#include <QMap>
+#ifndef SMARTCARD_OPENPGPCARD_H
+#define SMARTCARD_OPENPGPCARD_H
#include "card.h"
+#include <QMap>
+
namespace Kleo
{
namespace SmartCard
{
+struct KeyPairInfo;
+
/** Class to work with OpenPGP smartcards or compatible tokens */
class OpenPGPCard: public Card
{
public:
explicit OpenPGPCard(const Card &card);
static const std::string AppName;
+ static std::string pgpSigKeyRef();
+ static std::string pgpEncKeyRef();
+ static std::string pgpAuthKeyRef();
+
static std::string pinKeyRef();
static std::string adminPinKeyRef();
static std::string resetCodeKeyRef();
- std::string encFpr() const;
- std::string sigFpr() const;
- std::string authFpr() const;
+ static const std::vector<KeyPairInfo> & supportedKeys();
+ static QString keyDisplayName(const std::string &keyRef);
void setCardInfo(const std::vector< std::pair<std::string, std::string> > &infos);
+ std::string keyFingerprint(const std::string &keyRef) const;
+
bool operator == (const Card& other) const override;
void setManufacturer(const std::string &manufacturer);
std::string manufacturer() const;
std::string pubkeyUrl() const;
+
private:
QMap <std::string, std::string> mMetaInfo;
std::string mManufacturer;
};
} // namespace Smartcard
} // namespace Kleopatra
#endif // SMARTCARD_CARD_H
diff --git a/src/view/pgpcardwidget.cpp b/src/view/pgpcardwidget.cpp
index a3f7ebad3..a5f191ca0 100644
--- a/src/view/pgpcardwidget.cpp
+++ b/src/view/pgpcardwidget.cpp
@@ -1,512 +1,539 @@
/* 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-FileCopyrightText: 2020 g10 Code GmbH
+ SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "pgpcardwidget.h"
#include "kleopatra_debug.h"
#include "commands/changepincommand.h"
#include "commands/createopenpgpkeyfromcardkeyscommand.h"
#include "smartcard/openpgpcard.h"
#include "smartcard/readerstatus.h"
#include "dialogs/gencardkeydialog.h"
#include <Libkleo/GnuPG>
#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 <KSeparator>
#include <Libkleo/KeyCache>
#include <Libkleo/Formatting>
#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;
};
+
+static void layoutKeyWidgets(QGridLayout *grid, const QString &keyName, const PGPCardWidget::KeyWidgets &keyWidgets)
+{
+ int row = grid->rowCount();
+ grid->addWidget(new QLabel(keyName), row, 0);
+ grid->addWidget(keyWidgets.keyFingerprint, row, 1);
+}
} // 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 area
+ auto myLayout = new QVBoxLayout(this);
+ myLayout->setContentsMargins(0, 0, 0, 0);
- // Set up the scroll are
auto area = new QScrollArea;
area->setFrameShape(QFrame::NoFrame);
area->setWidgetResizable(true);
+ myLayout->addWidget(area);
+
auto areaWidget = new QWidget;
- auto areaVLay = new QVBoxLayout(areaWidget);
- areaVLay->addLayout(grid);
- areaVLay->addStretch(1);
area->setWidget(areaWidget);
- auto myLayout = new QVBoxLayout(this);
- myLayout->setContentsMargins(0, 0, 0, 0);
- 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);
+ auto areaVLay = new QVBoxLayout(areaWidget);
- // 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);
+ auto cardInfoGrid = new QGridLayout;
+ {
+ int row = 0;
+
+ // Version and Serialnumber
+ cardInfoGrid->addWidget(mVersionLabel, row, 0, 1, 2);
+ mVersionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
+ row++;
+
+ cardInfoGrid->addWidget(new QLabel(i18n("Serial number:")), row, 0);
+ cardInfoGrid->addWidget(mSerialNumber, row, 1);
+ mSerialNumber->setTextInteractionFlags(Qt::TextBrowserInteraction);
+ row++;
+
+ // Cardholder Row
+ cardInfoGrid->addWidget(new QLabel(i18nc("The owner of a smartcard. GnuPG refers to this as cardholder.",
+ "Cardholder:")), row, 0);
+ cardInfoGrid->addWidget(mCardHolderLabel, row, 1);
+ mCardHolderLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
+ {
+ auto button = new QPushButton;
+ button->setIcon(QIcon::fromTheme(QStringLiteral("cell_edit")));
+ button->setToolTip(i18n("Change"));
+ cardInfoGrid->addWidget(button, row, 2);
+ connect(button, &QPushButton::clicked, this, &PGPCardWidget::changeNameRequested);
+ }
+ row++;
+
+ // URL Row
+ cardInfoGrid->addWidget(new QLabel(i18nc("The URL under which a public key that "
+ "corresponds to a smartcard can be downloaded",
+ "Pubkey URL:")), row, 0);
+ cardInfoGrid->addWidget(mUrlLabel, row, 1);
+ mUrlLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
+ {
+ auto button = new QPushButton;
+ button->setIcon(QIcon::fromTheme(QStringLiteral("cell_edit")));
+ button->setToolTip(i18n("Change"));
+ cardInfoGrid->addWidget(button, row, 2);
+ connect(button, &QPushButton::clicked, this, &PGPCardWidget::changeUrlRequested);
+ }
- grid->addWidget(new QLabel(i18n("Signature:")), row, 0);
- grid->addWidget(mSigningKey, row++, 1);
- mSigningKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
+ cardInfoGrid->setColumnStretch(cardInfoGrid->columnCount(), 1);
+ }
+ areaVLay->addLayout(cardInfoGrid);
- grid->addWidget(new QLabel(i18n("Encryption:")), row, 0);
- grid->addWidget(mEncryptionKey, row++, 1);
- mEncryptionKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
+ areaVLay->addWidget(new KSeparator(Qt::Horizontal));
- grid->addWidget(new QLabel(i18n("Authentication:")), row, 0);
- grid->addWidget(mAuthKey, row++, 1);
- mAuthKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
+ // The keys
+ areaVLay->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("Keys:"))));
+
+ auto keysGrid = new QGridLayout;
+ for (const auto &keyInfo : OpenPGPCard::supportedKeys()) {
+ KeyWidgets keyWidgets = createKeyWidgets(keyInfo);
+ layoutKeyWidgets(keysGrid, OpenPGPCard::keyDisplayName(keyInfo.keyRef), keyWidgets);
+ }
+ keysGrid->setColumnStretch(keysGrid->columnCount(), 1);
+ areaVLay->addLayout(keysGrid);
- 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);
+ areaVLay->addWidget(new KSeparator(Qt::Horizontal));
+
+ areaVLay->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("Actions:"))));
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()); });
if (CreateOpenPGPKeyFromCardKeysCommand::isSupported()) {
mKeyForCardKeysButton = new QPushButton(this);
mKeyForCardKeysButton->setText(i18n("Create OpenPGP Key"));
mKeyForCardKeysButton->setToolTip(i18n("Create an OpenPGP key for the keys stored on the card."));
actionLayout->addWidget(mKeyForCardKeysButton);
connect(mKeyForCardKeysButton, &QPushButton::clicked, this, &PGPCardWidget::createKeyFromCardKeys);
}
actionLayout->addStretch(-1);
- grid->addLayout(actionLayout, row++, 0, 1, 4);
+ areaVLay->addLayout(actionLayout);
- grid->setColumnStretch(4, -1);
+ areaVLay->addStretch(1);
+}
+
+PGPCardWidget::KeyWidgets PGPCardWidget::createKeyWidgets(const KeyPairInfo &keyInfo)
+{
+ const std::string keyRef = keyInfo.keyRef;
+ KeyWidgets keyWidgets;
+ keyWidgets.keyFingerprint = new QLabel(this);
+ keyWidgets.keyFingerprint->setTextInteractionFlags(Qt::TextBrowserInteraction);
+ mKeyWidgets.insert(keyRef, keyWidgets);
+ return keyWidgets;
}
void PGPCardWidget::setCard(const OpenPGPCard *card)
{
const QString version = card->displayAppVersion();
mIs21 = card->appVersion() >= 0x0201;
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));
mSerialNumber->setText(card->displaySerialNumber());
mRealSerial = card->serialNumber();
const auto holder = 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();
+ updateKeyWidgets(OpenPGPCard::pgpSigKeyRef(), card);
+ updateKeyWidgets(OpenPGPCard::pgpEncKeyRef(), card);
+ updateKeyWidgets(OpenPGPCard::pgpAuthKeyRef(), card);
+ mCardIsEmpty = card->keyFingerprint(OpenPGPCard::pgpSigKeyRef()).empty()
+ && card->keyFingerprint(OpenPGPCard::pgpEncKeyRef()).empty()
+ && card->keyFingerprint(OpenPGPCard::pgpAuthKeyRef()).empty();
if (mKeyForCardKeysButton) {
mKeyForCardKeysButton->setEnabled(card->hasSigningKey() && card->hasEncryptionKey());
}
}
void PGPCardWidget::doChangePin(const std::string &keyRef)
{
auto cmd = new ChangePinCommand(mRealSerial, OpenPGPCard::AppName, this);
this->setEnabled(false);
connect(cmd, &ChangePinCommand::finished,
this, [this]() {
this->setEnabled(true);
});
cmd->setKeyRef(keyRef);
cmd->start();
}
void PGPCardWidget::doGenKey(GenCardKeyDialog *dlg)
{
const GpgME::Error err = ReaderStatus::switchCardAndApp(mRealSerial, OpenPGPCard::AppName);
if (err) {
return;
}
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"));
ReaderStatus::mutableInstance()->updateStatus();
}
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('<'));
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;
}
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::createKeyFromCardKeys()
{
auto cmd = new CreateOpenPGPKeyFromCardKeysCommand(mRealSerial, OpenPGPCard::AppName, this);
this->setEnabled(false);
connect(cmd, &CreateOpenPGPKeyFromCardKeysCommand::finished,
this, [this]() {
this->setEnabled(true);
});
cmd->start();
}
-void PGPCardWidget::updateKey(QLabel *label, const std::string &fpr)
+void PGPCardWidget::updateKeyWidgets(const std::string &keyRef, const OpenPGPCard *card)
{
- 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);
+ KeyWidgets widgets = mKeyWidgets.value(keyRef);
+ const std::string grip = card ? card->keyInfo(keyRef).grip : widgets.keyGrip;
+ widgets.keyGrip = grip;
+ if (grip.empty()) {
+ widgets.keyFingerprint->setText(i18n("Slot empty"));
+ } else {
+ if (card) {
+ // update information if called with card
+ std::string fpr = card->keyFingerprint(keyRef);
+ widgets.keyFingerprint->setText(QString::fromStdString(fpr));
+
+ std::string keyid = fpr;
+ keyid.erase(0, keyid.size() - 16);
+ const auto subkeys = KeyCache::instance()->findSubkeysByKeyID({keyid});
+ if (subkeys.empty() || subkeys[0].isNull()) {
+ widgets.keyFingerprint->setToolTip(i18n("Public key not found."));
+ } else {
+ 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);
+ }
+ widgets.keyFingerprint->setToolTip(toolTips.join(QLatin1String("<br/>")));
+ }
+ }
}
- label->setToolTip(toolTips.join(QLatin1String("<br/>")));
- return;
}
#include "pgpcardwidget.moc"
diff --git a/src/view/pgpcardwidget.h b/src/view/pgpcardwidget.h
index f5d989329..7866f3067 100644
--- a/src/view/pgpcardwidget.h
+++ b/src/view/pgpcardwidget.h
@@ -1,65 +1,76 @@
/* view/pgpcardwiget.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
+ SPDX-FileCopyrightText: 2020 g10 Code GmbH
+ SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef VIEW_PGPCARDWIDGET_H
#define VIEW_PGPCARDWIDGET_H
+#include <QMap>
#include <QWidget>
+
#include <gpgme++/error.h>
#include <string>
class QLabel;
class QPushButton;
namespace Kleo
{
class GenCardKeyDialog;
namespace SmartCard
{
+struct KeyPairInfo;
class OpenPGPCard;
} // namespace SmartCard
class PGPCardWidget: public QWidget
{
Q_OBJECT
public:
explicit PGPCardWidget(QWidget *parent = nullptr);
void setCard(const SmartCard::OpenPGPCard* card);
void doGenKey(GenCardKeyDialog *dlg);
void genKeyDone(const GpgME::Error &err, const std::string &backup);
+ struct KeyWidgets {
+ std::string keyGrip;
+ QLabel *keyFingerprint = nullptr;
+ };
+
public Q_SLOTS:
void genkeyRequested();
void changeNameRequested();
void changeNameResult(const GpgME::Error &err);
void changeUrlRequested();
void changeUrlResult(const GpgME::Error &err);
void createKeyFromCardKeys();
private:
+ KeyWidgets createKeyWidgets(const SmartCard::KeyPairInfo &keyInfo);
+ void updateKeyWidgets(const std::string &keyRef, const SmartCard::OpenPGPCard *card);
void doChangePin(const std::string &keyRef);
- void updateKey(QLabel *label, const std::string &fpr);
+
+private:
QLabel *mSerialNumber = nullptr,
*mCardHolderLabel = nullptr,
*mVersionLabel = nullptr,
- *mSigningKey = nullptr,
- *mEncryptionKey = nullptr,
- *mAuthKey = nullptr,
*mUrlLabel = nullptr;
QPushButton *mKeyForCardKeysButton = nullptr;
+ QMap<std::string, KeyWidgets> mKeyWidgets;
QString mUrl;
bool mCardIsEmpty = false;
bool mIs21 = false;
std::string mRealSerial;
};
} // namespace Kleo
#endif // VIEW_PGPCARDWIDGET_H
diff --git a/src/view/pivcardwidget.h b/src/view/pivcardwidget.h
index b36b41704..c835b029d 100644
--- a/src/view/pivcardwidget.h
+++ b/src/view/pivcardwidget.h
@@ -1,71 +1,71 @@
/* view/pivcardwiget.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef VIEW_PIVCARDWIDGET_H
#define VIEW_PIVCARDWIDGET_H
#include <QMap>
#include <QWidget>
#include <gpgme++/error.h>
class QGridLayout;
class QLabel;
class QPushButton;
namespace Kleo
{
namespace SmartCard
{
-class KeyPairInfo;
+struct KeyPairInfo;
class PIVCard;
} // namespace SmartCard
class PIVCardWidget: public QWidget
{
Q_OBJECT
public:
explicit PIVCardWidget(QWidget *parent = nullptr);
~PIVCardWidget();
void setCard(const SmartCard::PIVCard* card);
struct KeyWidgets {
QLabel *keyGrip = nullptr;
QLabel *keyAlgorithm = nullptr;
QLabel *certificateInfo = nullptr;
QPushButton *generateButton = nullptr;
QPushButton *createCSRButton = nullptr;
QPushButton *writeCertificateButton = nullptr;
QPushButton *importCertificateButton = nullptr;
QPushButton *writeKeyButton = nullptr;
};
private:
KeyWidgets createKeyWidgets(const SmartCard::KeyPairInfo &keyInfo);
void updateKeyWidgets(const std::string &keyRef, const SmartCard::PIVCard *card);
void generateKey(const std::string &keyref);
void createCSR(const std::string &keyref);
void writeCertificateToCard(const std::string &keyref);
void importCertificateFromCard(const std::string &keyref);
void writeKeyToCard(const std::string &keyref);
void createKeyFromCardKeys();
void changePin(const std::string &keyRef);
void setAdminKey();
private:
std::string mCardSerialNumber;
QLabel *mSerialNumber = nullptr;
QLabel *mVersionLabel = nullptr;
QPushButton *mKeyForCardKeysButton = nullptr;
QMap<std::string, KeyWidgets> mKeyWidgets;
};
} // namespace Kleo
#endif // VIEW_PIVCARDWIDGET_H
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, May 5, 6:14 AM (1 d, 23 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
38/af/85bd3a940bad485bd2631db15056
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment