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 SPDX-License-Identifier: GPL-2.0-or-later */ #include #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 #include #include #include #include #include #include #include #include #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(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 &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(d.get()); } const KeyToCardCommand::Private *KeyToCardCommand::d_func() const { return static_cast(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 getCardToTransferSubkeyTo(const Subkey &subkey, QWidget *parent) { const std::vector > suitableCards = KeyToCardCommand::getSuitableCards(subkey); if (suitableCards.empty()) { return std::shared_ptr(); } 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(); } 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(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", "

This card already contains a key in this slot. Continuing will overwrite that key.

" "

If there is no backup the existing key will be irrecoverably lost.

") + i18n("The existing key has the fingerprint:") + QStringLiteral("
%1
").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 getSigningCertificates() { std::vector 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 getEncryptionCertificates() { std::vector 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 &/*card*/) { if (cardSlot != PIVCard::cardAuthenticationKeyRef() && cardSlot != PIVCard::keyManagementKeyRef()) { return Subkey(); } const std::vector 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(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", "

This card already contains a key in this slot. Continuing will overwrite that key.

" "

If there is no backup the existing key will be irrecoverably lost.

") + i18n("The existing key has the key grip:") + QStringLiteral("
%1
").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 > KeyToCardCommand::getSuitableCards(const GpgME::Subkey &subkey) { std::vector > 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 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 +#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 & OpenPGPCard::supportedKeys() { - return mMetaInfo.value("SIGKEY-FPR"); -} + static const std::vector 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 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 > &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(&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 SPDX-License-Identifier: GPL-2.0-or-later */ - -#include +#ifndef SMARTCARD_OPENPGPCARD_H +#define SMARTCARD_OPENPGPCARD_H #include "card.h" +#include + 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 & supportedKeys(); + static QString keyDisplayName(const std::string &keyRef); void setCardInfo(const std::vector< std::pair > &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 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 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 #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include 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::createForProtocol(GpgME::OpenPGP)); QGpgME::QByteArrayDataProvider dp; GpgME::Data data(&dp); mErr = ctx->cardEdit(GpgME::Key(), std::unique_ptr (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("%1").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("%1").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("%1").arg(i18n("Actions:"))), row++, 0); + areaVLay->addWidget(new KSeparator(Qt::Horizontal)); + + areaVLay->addWidget(new QLabel(QStringLiteral("%1").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("%1").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 deleted " "and replaced by new keys.") + QStringLiteral("

") + 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> 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(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(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 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("
"))); + } + } } - label->setToolTip(toolTips.join(QLatin1String("
"))); - 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 SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef VIEW_PGPCARDWIDGET_H #define VIEW_PGPCARDWIDGET_H +#include #include + #include #include 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 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 SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef VIEW_PIVCARDWIDGET_H #define VIEW_PIVCARDWIDGET_H #include #include #include 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 mKeyWidgets; }; } // namespace Kleo #endif // VIEW_PIVCARDWIDGET_H