Page MenuHome GnuPG

No OneTemporary

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 &params, const std::string &serial):
mSerial(serial),
mParams(params)
{
}
GpgME::Error error()
{
return mErr;
}
std::string bkpFile()
{
return mBkpFile;
}
protected:
void run() override {
GpgME::GpgGenCardKeyInteractor *ei = new GpgME::GpgGenCardKeyInteractor(mSerial);
ei->setAlgo(GpgME::GpgGenCardKeyInteractor::RSA);
ei->setKeySize(QByteArray::fromStdString(mParams.algorithm).toInt());
ei->setNameUtf8(mParams.name.toStdString());
ei->setEmailUtf8(mParams.email.toStdString());
ei->setDoBackup(mParams.backup);
const auto ctx = std::shared_ptr<GpgME::Context> (GpgME::Context::createForProtocol(GpgME::OpenPGP));
QGpgME::QByteArrayDataProvider dp;
GpgME::Data data(&dp);
mErr = ctx->cardEdit(GpgME::Key(), std::unique_ptr<GpgME::EditInteractor> (ei), data);
mBkpFile = ei->backupFileName();
}
private:
GpgME::Error mErr;
std::string mSerial;
GenCardKeyDialog::KeyParams mParams;
std::string mBkpFile;
};
+
+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

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

Event Timeline