diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5e3aa3d5c..5ef61c820 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,348 +1,349 @@ add_subdirectory(icons) include_directories(${CMAKE_CURRENT_BINARY_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}) if (NOT DISABLE_KWATCHGNUPG) add_subdirectory(kwatchgnupg) endif() add_subdirectory(libkleopatraclient) add_subdirectory(conf) add_subdirectory(kconf_update) if(WIN32) set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_win.cpp) set(_kleopatra_extra_SRCS utils/gnupg-registry.c selftest/registrycheck.cpp utils/windowsprocessdevice.cpp ) else() set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_unix.cpp) set(_kleopatra_extra_SRCS) endif() set(_kleopatra_uiserver_SRCS uiserver/sessiondata.cpp uiserver/uiserver.cpp ${_kleopatra_extra_uiserver_SRCS} uiserver/assuanserverconnection.cpp uiserver/echocommand.cpp uiserver/decryptverifycommandemailbase.cpp uiserver/decryptverifycommandfilesbase.cpp uiserver/signcommand.cpp uiserver/signencryptfilescommand.cpp uiserver/prepencryptcommand.cpp uiserver/prepsigncommand.cpp uiserver/encryptcommand.cpp uiserver/selectcertificatecommand.cpp uiserver/importfilescommand.cpp uiserver/createchecksumscommand.cpp uiserver/verifychecksumscommand.cpp selftest/uiservercheck.cpp ) if(ASSUAN2_FOUND) include_directories(${ASSUAN2_INCLUDES}) set(_kleopatra_uiserver_extra_libs ${ASSUAN2_LIBRARIES}) else() include_directories(${ASSUAN_INCLUDES}) if(WIN32) set(_kleopatra_uiserver_extra_libs ${ASSUAN_VANILLA_LIBRARIES}) else() set(_kleopatra_uiserver_extra_libs ${ASSUAN_PTHREAD_LIBRARIES}) endif() endif() if(HAVE_GPG_ERR_SOURCE_KLEO) add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO) else() add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1) endif() ki18n_wrap_ui(_kleopatra_uiserver_SRCS crypto/gui/signingcertificateselectionwidget.ui) set(_kleopatra_SRCS utils/gnupg-helper.cpp utils/gui-helper.cpp utils/filedialog.cpp utils/kdpipeiodevice.cpp utils/headerview.cpp utils/scrollarea.cpp utils/dragqueen.cpp utils/multivalidator.cpp utils/systemtrayicon.cpp utils/hex.cpp utils/path-helper.cpp utils/input.cpp utils/output.cpp utils/validation.cpp utils/wsastarter.cpp utils/iodevicelogger.cpp utils/log.cpp utils/action_data.cpp utils/types.cpp utils/archivedefinition.cpp utils/auditlog.cpp utils/clipboardmenu.cpp utils/kuniqueservice.cpp utils/remarks.cpp utils/writecertassuantransaction.cpp selftest/selftest.cpp selftest/enginecheck.cpp selftest/gpgconfcheck.cpp selftest/gpgagentcheck.cpp selftest/libkleopatrarccheck.cpp ${_kleopatra_extra_SRCS} view/keylistcontroller.cpp view/keytreeview.cpp view/searchbar.cpp view/smartcardwidget.cpp view/padwidget.cpp view/pgpcardwidget.cpp view/pivcardwidget.cpp view/netkeywidget.cpp view/nullpinwidget.cpp view/tabwidget.cpp view/keycacheoverlay.cpp view/waitwidget.cpp view/welcomewidget.cpp dialogs/certificateselectiondialog.cpp dialogs/certifywidget.cpp dialogs/expirydialog.cpp dialogs/lookupcertificatesdialog.cpp dialogs/ownertrustdialog.cpp dialogs/selftestdialog.cpp dialogs/certifycertificatedialog.cpp dialogs/adduseriddialog.cpp dialogs/addemaildialog.cpp dialogs/exportcertificatesdialog.cpp dialogs/deletecertificatesdialog.cpp dialogs/setinitialpindialog.cpp dialogs/certificatedetailswidget.cpp dialogs/trustchainwidget.cpp dialogs/weboftrustwidget.cpp dialogs/weboftrustdialog.cpp dialogs/exportdialog.cpp dialogs/subkeyswidget.cpp dialogs/gencardkeydialog.cpp dialogs/updatenotification.cpp dialogs/pivcardapplicationadministrationkeyinputdialog.cpp crypto/controller.cpp crypto/certificateresolver.cpp crypto/sender.cpp crypto/recipient.cpp crypto/task.cpp crypto/taskcollection.cpp crypto/decryptverifytask.cpp crypto/decryptverifyemailcontroller.cpp crypto/decryptverifyfilescontroller.cpp crypto/autodecryptverifyfilescontroller.cpp crypto/encryptemailtask.cpp crypto/encryptemailcontroller.cpp crypto/newsignencryptemailcontroller.cpp crypto/signencrypttask.cpp crypto/signencryptfilescontroller.cpp crypto/signemailtask.cpp crypto/signemailcontroller.cpp crypto/createchecksumscontroller.cpp crypto/verifychecksumscontroller.cpp crypto/gui/wizard.cpp crypto/gui/wizardpage.cpp crypto/gui/certificateselectionline.cpp crypto/gui/certificatelineedit.cpp crypto/gui/signingcertificateselectionwidget.cpp crypto/gui/signingcertificateselectiondialog.cpp crypto/gui/resultitemwidget.cpp crypto/gui/resultlistwidget.cpp crypto/gui/resultpage.cpp crypto/gui/newresultpage.cpp crypto/gui/signencryptfileswizard.cpp crypto/gui/signencryptemailconflictdialog.cpp crypto/gui/decryptverifyoperationwidget.cpp crypto/gui/decryptverifyfileswizard.cpp crypto/gui/decryptverifyfilesdialog.cpp crypto/gui/objectspage.cpp crypto/gui/resolverecipientspage.cpp crypto/gui/signerresolvepage.cpp crypto/gui/encryptemailwizard.cpp crypto/gui/signemailwizard.cpp crypto/gui/signencryptwidget.cpp crypto/gui/signencryptwizard.cpp crypto/gui/unknownrecipientwidget.cpp crypto/gui/verifychecksumsdialog.cpp commands/command.cpp commands/gnupgprocesscommand.cpp commands/detailscommand.cpp commands/exportcertificatecommand.cpp commands/importcertificatescommand.cpp commands/importcertificatefromfilecommand.cpp commands/importcertificatefromclipboardcommand.cpp commands/importcertificatefromdatacommand.cpp commands/lookupcertificatescommand.cpp commands/reloadkeyscommand.cpp commands/refreshx509certscommand.cpp commands/refreshopenpgpcertscommand.cpp commands/deletecertificatescommand.cpp commands/decryptverifyfilescommand.cpp commands/signencryptfilescommand.cpp commands/signencryptfoldercommand.cpp commands/encryptclipboardcommand.cpp commands/signclipboardcommand.cpp commands/decryptverifyclipboardcommand.cpp commands/clearcrlcachecommand.cpp commands/dumpcrlcachecommand.cpp commands/dumpcertificatecommand.cpp commands/importcrlcommand.cpp commands/changeexpirycommand.cpp commands/changeownertrustcommand.cpp commands/changeroottrustcommand.cpp commands/changepassphrasecommand.cpp commands/certifycertificatecommand.cpp commands/selftestcommand.cpp commands/exportsecretkeycommand.cpp commands/exportopenpgpcertstoservercommand.cpp commands/adduseridcommand.cpp commands/newcertificatecommand.cpp commands/setinitialpincommand.cpp commands/learncardkeyscommand.cpp commands/checksumcreatefilescommand.cpp commands/checksumverifyfilescommand.cpp commands/exportpaperkeycommand.cpp commands/importpaperkeycommand.cpp commands/genrevokecommand.cpp commands/keytocardcommand.cpp commands/cardcommand.cpp commands/pivgeneratecardkeycommand.cpp commands/changepincommand.cpp commands/authenticatepivcardapplicationcommand.cpp commands/setpivcardapplicationadministrationkeycommand.cpp + commands/certificatetopivcardcommand.cpp ${_kleopatra_uiserver_files} conf/configuredialog.cpp newcertificatewizard/listwidget.cpp newcertificatewizard/newcertificatewizard.cpp smartcard/readerstatus.cpp smartcard/card.cpp smartcard/openpgpcard.cpp smartcard/netkeycard.cpp smartcard/pivcard.cpp aboutdata.cpp systrayicon.cpp kleopatraapplication.cpp mainwindow.cpp main.cpp ) if(WIN32) configure_file (versioninfo.rc.in versioninfo.rc) set(_kleopatra_SRCS ${CMAKE_CURRENT_BINARY_DIR}/versioninfo.rc ${_kleopatra_SRCS}) endif() if(HAVE_KCMUTILS) set (_kleopatra_extra_libs KF5::KCMUtils) else() set (_kleopatra_SRCS conf/kleopageconfigdialog.cpp ${_kleopatra_SRCS}) endif() ecm_qt_declare_logging_category(_kleopatra_SRCS HEADER kleopatra_debug.h IDENTIFIER KLEOPATRA_LOG CATEGORY_NAME org.kde.pim.kleopatra DESCRIPTION "kleopatra (kleopatra)" OLD_CATEGORY_NAMES log_kleopatra EXPORT KLEOPATRA ) if(KLEO_MODEL_TEST) add_definitions(-DKLEO_MODEL_TEST) set(_kleopatra_SRCS ${_kleopatra_SRCS} models/modeltest.cpp) endif() ki18n_wrap_ui(_kleopatra_SRCS dialogs/expirydialog.ui dialogs/lookupcertificatesdialog.ui dialogs/ownertrustdialog.ui dialogs/selectchecklevelwidget.ui dialogs/selftestdialog.ui dialogs/adduseriddialog.ui dialogs/setinitialpindialog.ui dialogs/certificatedetailswidget.ui dialogs/trustchainwidget.ui dialogs/subkeyswidget.ui newcertificatewizard/listwidget.ui newcertificatewizard/chooseprotocolpage.ui newcertificatewizard/enterdetailspage.ui newcertificatewizard/overviewpage.ui newcertificatewizard/keycreationpage.ui newcertificatewizard/resultpage.ui newcertificatewizard/advancedsettingsdialog.ui ) kconfig_add_kcfg_files(_kleopatra_SRCS kcfg/tooltippreferences.kcfgc kcfg/emailoperationspreferences.kcfgc kcfg/fileoperationspreferences.kcfgc kcfg/smimevalidationpreferences.kcfgc ) file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/icons/*-apps-kleopatra.png") ecm_add_app_icon(_kleopatra_SRCS ICONS ${ICONS_SRCS}) qt5_add_resources(_kleopatra_SRCS kleopatra.qrc) add_executable(kleopatra_bin ${_kleopatra_SRCS} ${_kleopatra_uiserver_SRCS}) set_target_properties(kleopatra_bin PROPERTIES OUTPUT_NAME kleopatra) target_link_libraries(kleopatra_bin Gpgmepp QGpgme ${_kleopatra_extra_libs} KF5::Libkleo KF5::Mime KF5::I18n KF5::XmlGui KF5::IconThemes KF5::WindowSystem KF5::CoreAddons KF5::ItemModels KF5::Crash Qt5::Network Qt5::PrintSupport # Printing secret keys ${_kleopatra_uiserver_extra_libs} ${_kleopatra_dbusaddons_libs} kleopatraclientcore ) install(TARGETS kleopatra_bin ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install( PROGRAMS data/org.kde.kleopatra.desktop data/kleopatra_import.desktop DESTINATION ${KDE_INSTALL_APPDIR} ) install(FILES data/org.kde.kleopatra.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) install( FILES data/kleopatra_signencryptfiles.desktop data/kleopatra_signencryptfolders.desktop data/kleopatra_decryptverifyfiles.desktop data/kleopatra_decryptverifyfolders.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR} ) diff --git a/src/commands/certificatetopivcardcommand.cpp b/src/commands/certificatetopivcardcommand.cpp new file mode 100644 index 000000000..e5f1cdbb3 --- /dev/null +++ b/src/commands/certificatetopivcardcommand.cpp @@ -0,0 +1,237 @@ +/* commands/certificatetopivcardcommand.cpp + + 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 +*/ + +#include + +#include "certificatetopivcardcommand.h" + +#include "command_p.h" + +#include "smartcard/readerstatus.h" +#include "smartcard/pivcard.h" + +#include "commands/authenticatepivcardapplicationcommand.h" + +#include "utils/writecertassuantransaction.h" + +#include + +#include + +#include + +#include + +#include "kleopatra_debug.h" + +using namespace Kleo; +using namespace Kleo::Commands; +using namespace Kleo::SmartCard; +using namespace GpgME; + +class CertificateToPIVCardCommand::Private : public Command::Private +{ + friend class ::Kleo::Commands::CertificateToPIVCardCommand; + CertificateToPIVCardCommand *q_func() const + { + return static_cast(q); + } +public: + explicit Private(CertificateToPIVCardCommand *qq, const GpgME::Subkey &key, const std::string &serialno); + explicit Private(CertificateToPIVCardCommand *qq, const std::string &cardSlot, const std::string &serialno); + ~Private(); + +private: + void start(); + void startCertificateToPIVCard(); + + void authenticate(); + void authenticationFinished(); + void authenticationCanceled(); + +private: + std::string mSerial; + GpgME::Subkey mSubkey; + std::string cardSlot; + bool overwriteExistingAlreadyApproved = false; + bool hasBeenCanceled = false; +}; + +CertificateToPIVCardCommand::Private *CertificateToPIVCardCommand::d_func() +{ + return static_cast(d.get()); +} +const CertificateToPIVCardCommand::Private *CertificateToPIVCardCommand::d_func() const +{ + return static_cast(d.get()); +} + +#define q q_func() +#define d d_func() + + +CertificateToPIVCardCommand::Private::Private(CertificateToPIVCardCommand *qq, + const GpgME::Subkey &key, + const std::string &serialno) + : Command::Private(qq, nullptr), + mSerial(serialno), + mSubkey(key) +{ +} + +CertificateToPIVCardCommand::Private::Private(CertificateToPIVCardCommand *qq, const std::string &cardSlot_, const std::string &serialno) + : Command::Private(qq, nullptr) + , mSerial(serialno) + , cardSlot(cardSlot_) +{ +} + +CertificateToPIVCardCommand::Private::~Private() +{ +} + +namespace { +static GpgME::Subkey getSubkeyToTransferToPIVCard(const std::string &cardSlot, const std::shared_ptr &card) +{ + if (!cardSlot.empty()) { + if (cardSlot == PIVCard::digitalSignatureKeyRef()) { + // get signing certificate matching the key grip + const std::string cardKeygrip = card->keyGrip(cardSlot); + const auto subkey = KeyCache::instance()->findSubkeyByKeyGrip(cardKeygrip); + if (subkey.canSign() && subkey.parent().protocol() == GpgME::CMS) { + return subkey; + } + } + if (cardSlot == PIVCard::keyManagementKeyRef()) { + // get encryption certificate with secret subkey + } + return GpgME::Subkey(); + } + + return GpgME::Subkey(); +} +} + +void CertificateToPIVCardCommand::Private::start() +{ + qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::Private::start()"; + + const auto pivCard = SmartCard::ReaderStatus::instance()->getCard(mSerial); + if (!pivCard) { + error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(mSerial))); + finished(); + return; + } + + mSubkey = getSubkeyToTransferToPIVCard(cardSlot, pivCard); + if (mSubkey.isNull()) { + error(i18n("Sorry! No suitable certificate to write to this card slot was found.")); + finished(); + return; + } + + startCertificateToPIVCard(); +} + +void CertificateToPIVCardCommand::Private::startCertificateToPIVCard() +{ + qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::Private::startCertificateToPIVCard()"; + + auto ctx = Context::createForProtocol(GpgME::CMS); + QGpgME::QByteArrayDataProvider dp; + Data data(&dp); + const Error err = ctx->exportPublicKeys(mSubkey.parent().primaryFingerprint(), data); + if (err) { + error(i18nc("@info", "Exporting the certificate failed: %1", QString::fromUtf8(err.asString())), + i18nc("@title", "Error")); + finished(); + return; + } + const QByteArray certificateData = dp.data(); + + const QString cmd = QStringLiteral("SCD WRITECERT %1") + .arg(QString::fromStdString(cardSlot)); + auto transaction = std::unique_ptr(new WriteCertAssuanTransaction(certificateData)); + ReaderStatus::mutableInstance()->startTransaction(cmd.toUtf8(), q_func(), "certificateToPIVCardDone", std::move(transaction)); +} + +void CertificateToPIVCardCommand::Private::authenticate() +{ + qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::authenticate()"; + + auto cmd = new AuthenticatePIVCardApplicationCommand(mSerial, parentWidgetOrView()); + connect(cmd, &AuthenticatePIVCardApplicationCommand::finished, + q, [this]() { authenticationFinished(); }); + connect(cmd, &AuthenticatePIVCardApplicationCommand::canceled, + q, [this]() { authenticationCanceled(); }); + cmd->start(); +} + +void CertificateToPIVCardCommand::Private::authenticationFinished() +{ + qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::authenticationFinished()"; + if (!hasBeenCanceled) { + startCertificateToPIVCard(); + } +} + +void CertificateToPIVCardCommand::Private::authenticationCanceled() +{ + qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::authenticationCanceled()"; + hasBeenCanceled = true; + canceled(); +} + +CertificateToPIVCardCommand::CertificateToPIVCardCommand(const std::string& cardSlot, const std::string &serialno) + : Command(new Private(this, cardSlot, serialno)) +{ +} + +CertificateToPIVCardCommand::~CertificateToPIVCardCommand() +{ + qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::~CertificateToPIVCardCommand()"; +} + +void CertificateToPIVCardCommand::certificateToPIVCardDone(const Error &err) +{ + qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::certificateToPIVCardDone():" + << err.asString() << "(" << err.code() << ")"; + if (err) { + // gpgme 1.13 reports "BAD PIN" instead of "NO AUTH" + if (err.code() == GPG_ERR_NO_AUTH || err.code() == GPG_ERR_BAD_PIN) { + d->authenticate(); + return; + } + + d->error(i18nc("@info", + "Writing the certificate to the card failed: %1", QString::fromUtf8(err.asString())), + i18nc("@title", "Error")); + } else if (!err.isCanceled()) { + KMessageBox::information(d->parentWidgetOrView(), + i18n("Successfully copied the certificate to the card."), + i18nc("@title", "Success")); + ReaderStatus::mutableInstance()->updateStatus(); + } + + d->finished(); +} + +void CertificateToPIVCardCommand::doStart() +{ + qCDebug(KLEOPATRA_LOG) << "CertificateToPIVCardCommand::doStart()"; + + d->start(); +} + +void CertificateToPIVCardCommand::doCancel() +{ +} + +#undef q_func +#undef d_func diff --git a/src/commands/certificatetopivcardcommand.h b/src/commands/certificatetopivcardcommand.h new file mode 100644 index 000000000..eb4f820d8 --- /dev/null +++ b/src/commands/certificatetopivcardcommand.h @@ -0,0 +1,48 @@ +/* commands/certificatetopivcardcommand.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 __KLEOPATRA_COMMANDS_CERTIFICATETOPIVCARDCOMMAND_H__ +#define __KLEOPATRA_COMMANDS_CERTIFICATETOPIVCARDCOMMAND_H__ + +#include + +namespace GpgME +{ +class Error; +} + +namespace Kleo +{ +namespace Commands +{ + +class CertificateToPIVCardCommand : public Command +{ + Q_OBJECT +public: + CertificateToPIVCardCommand(const std::string& cardSlot, const std::string &serialno); + ~CertificateToPIVCardCommand() override; + +public Q_SLOTS: + void certificateToPIVCardDone(const GpgME::Error &err); + +private: + void doStart() override; + void doCancel() override; + +private: + class Private; + inline Private *d_func(); + inline const Private *d_func() const; +}; + +} +} + +#endif /* __KLEOPATRA_COMMANDS_CERTIFICATETOPIVCARDCOMMAND_H__ */ diff --git a/src/commands/keytocardcommand.cpp b/src/commands/keytocardcommand.cpp index 75921754d..fb6ea8e6f 100644 --- a/src/commands/keytocardcommand.cpp +++ b/src/commands/keytocardcommand.cpp @@ -1,580 +1,424 @@ /* 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 "kleopatra_debug.h" #include "command_p.h" #include "smartcard/readerstatus.h" #include "smartcard/openpgpcard.h" #include "smartcard/pivcard.h" #include "commands/authenticatepivcardapplicationcommand.h" -#include "utils/writecertassuantransaction.h" - -#include - #include #include #include #include -#include - -#include - #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::SmartCard; using namespace GpgME; class KeyToCardCommand::Private : public Command::Private { friend class ::Kleo::Commands::KeyToCardCommand; KeyToCardCommand *q_func() const { return static_cast(q); } public: explicit Private(KeyToCardCommand *qq, const GpgME::Subkey &key, const std::string &serialno); explicit Private(KeyToCardCommand *qq, const std::string &cardSlot, const std::string &serialno); ~Private(); private: void start(); - void startTransferToOpenPGPCard(); + void startKeyToOpenPGPCard(); - void startTransferToPIVCard(); void startKeyToPIVCard(); - void startCertificateToPIVCard(); + void authenticate(); void authenticationFinished(); void authenticationCanceled(); private: std::string mSerial; GpgME::Subkey mSubkey; 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 &key, const std::string &serialno) : Command::Private(qq, nullptr), mSerial(serialno), mSubkey(key) { } KeyToCardCommand::Private::Private(KeyToCardCommand *qq, const std::string &cardSlot_, const std::string &serialno) : Command::Private(qq, nullptr) , mSerial(serialno) , cardSlot(cardSlot_) { } KeyToCardCommand::Private::~Private() { } void KeyToCardCommand::Private::start() { qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::Private::start()"; - // for now we always use the first smart card - if (mSerial.empty()) { - const auto cards = SmartCard::ReaderStatus::instance()->getCards(); - if (!cards.size() || cards[0]->serialNumber().empty()) { - error(i18n("Failed to find a smart card.")); - finished(); - return; - } - mSerial = cards[0]->serialNumber(); - } - const auto card = SmartCard::ReaderStatus::instance()->getCard(mSerial); if (!card) { error(i18n("Failed to find the card with the serial number: %1", QString::fromStdString(mSerial))); finished(); return; } switch (card->appType()) { case SmartCard::Card::OpenPGPApplication: { - startTransferToOpenPGPCard(); + startKeyToOpenPGPCard(); } break; case SmartCard::Card::PivApplication: { - startTransferToPIVCard(); + startKeyToPIVCard(); } break; default: { 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::startTransferToOpenPGPCard() { - qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::Private::startTransferToOpenPGPCard()"; +void KeyToCardCommand::Private::startKeyToOpenPGPCard() { + qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::Private::startKeyToOpenPGPCard()"; const auto pgpCard = SmartCard::ReaderStatus::instance()->getCard(mSerial); if (!pgpCard) { error(i18n("Failed to find the OpenPGP card with the serial number: %1", QString::fromStdString(mSerial))); finished(); return; } if (mSubkey.isNull()) { finished(); return; } if (mSubkey.parent().protocol() != GpgME::OpenPGP) { error(i18n("Sorry! This key cannot be transferred to an OpenPGP card.")); finished(); return; } const auto slot = getOpenPGPCardSlotForKey(mSubkey, parentWidgetOrView()); if (slot < 1) { finished(); return; } // Check if we need to do the overwrite warning. std::string existingKey; QString encKeyWarning; if (slot == 1) { existingKey = pgpCard->sigFpr(); } else if (slot == 2) { existingKey = pgpCard->encFpr(); encKeyWarning = i18n("It will no longer be possible to decrypt past communication " "encrypted for the existing key."); } else if (slot == 3) { existingKey = pgpCard->authFpr(); } if (!existingKey.empty()) { const QString message = i18nc("@info", "

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(mSubkey.creationTime()); const auto timestamp = time.toString(QStringLiteral("yyyyMMdd'T'HHmmss")); const QString cmd = QStringLiteral("KEYTOCARD --force %1 %2 OPENPGP.%3 %4") .arg(QString::fromLatin1(mSubkey.keyGrip()), QString::fromStdString(mSerial)) .arg(slot) .arg(timestamp); ReaderStatus::mutableInstance()->startSimpleTransaction(cmd.toUtf8(), q_func(), "keyToOpenPGPCardDone"); } namespace { -static GpgME::Subkey getSubkeyToTransferToPIVCard(const std::string &cardSlot, const std::shared_ptr &card) +static GpgME::Subkey getSubkeyToTransferToPIVCard(const std::string &cardSlot, const std::shared_ptr &/*card*/) { if (!cardSlot.empty()) { - if (cardSlot == PIVCard::digitalSignatureKeyRef()) { - // get signing certificate matching the key grip - const std::string cardKeygrip = card->keyGrip(cardSlot); - const auto subkey = KeyCache::instance()->findSubkeyByKeyGrip(cardKeygrip); - if (subkey.canSign() && subkey.parent().protocol() == GpgME::CMS) { - return subkey; - } - } if (cardSlot == PIVCard::keyManagementKeyRef()) { // get encryption certificate with secret subkey } return GpgME::Subkey(); } return GpgME::Subkey(); } - -static std::string getPIVCardSlotForKey(const GpgME::Subkey &subKey, QWidget *parent) -{ - // Check if we need to ask the user for the slot - if (subKey.canSign() && !subKey.canEncrypt()) { - // Signing only - return PIVCard::digitalSignatureKeyRef(); - } - if (subKey.canEncrypt() && !subKey.canSign()) { - // Encrypt only - return PIVCard::keyManagementKeyRef(); - } - - // Multiple uses, ask user. - QMap options; - - if (subKey.canSign()) { - options.insert(i18nc("Placeholder 1 is the name of a key, e.g. 'Digital Signature Key'; " - "placeholder 2 is the identifier of a slot on a smart card", "%1 (%2)", - PIVCard::keyDisplayName(PIVCard::digitalSignatureKeyRef()), QString::fromStdString(PIVCard::digitalSignatureKeyRef())), - PIVCard::digitalSignatureKeyRef()); - } - if (subKey.canEncrypt()) { - options.insert(i18nc("Placeholder 1 is the name of a key, e.g. 'Digital Signature Key'; " - "placeholder 2 is the identifier of a slot on a smart card", "%1 (%2)", - PIVCard::keyDisplayName(PIVCard::keyManagementKeyRef()), QString::fromStdString(PIVCard::keyManagementKeyRef())), - PIVCard::keyManagementKeyRef()); - } - - bool ok; - const QString choice = QInputDialog::getItem(parent, i18n("Select Card Slot"), - i18n("Please select the card slot the certificate should be written to:"), options.keys(), /* current= */ 0, /* editable= */ false, &ok); - const std::string slot = options.value(choice); - return ok ? slot : std::string(); -} } -void KeyToCardCommand::Private::startTransferToPIVCard() +void KeyToCardCommand::Private::startKeyToPIVCard() { - qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::Private::startTransferToPIVCard()"; + qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::Private::startKeyToPIVCard()"; const auto pivCard = SmartCard::ReaderStatus::instance()->getCard(mSerial); if (!pivCard) { error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(mSerial))); finished(); return; } + if (cardSlot != PIVCard::keyManagementKeyRef()) { + // key to card is only supported for encryption keys + finished(); + return; + } + if (mSubkey.isNull()) { mSubkey = getSubkeyToTransferToPIVCard(cardSlot, pivCard); } if (mSubkey.isNull()) { - if (!cardSlot.empty()) { - error(i18n("Sorry! No suitable certificate to write to this card slot was found.")); - } + error(i18n("Sorry! No suitable certificate to write to this card slot was found.")); finished(); return; } if (mSubkey.parent().protocol() != GpgME::CMS) { error(i18n("Sorry! This key cannot be transferred to a PIV card.")); finished(); return; } if (!mSubkey.canEncrypt() && !mSubkey.canSign()) { error(i18n("Sorry! Only encryption keys and signing keys can be transferred to a PIV card.")); finished(); return; } - // get card slot unless it was already selected before authentication was performed - if (cardSlot.empty()) { - cardSlot = getPIVCardSlotForKey(mSubkey, parentWidgetOrView()); - if (cardSlot.empty()) { - finished(); - return; - } - } - - if (cardSlot == PIVCard::keyManagementKeyRef()) { - startKeyToPIVCard(); - } else { - // skip key to card because it's only supported for encryption keys - startCertificateToPIVCard(); - } -} - -void KeyToCardCommand::Private::startKeyToPIVCard() -{ - qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::Private::startKeyToPIVCard()"; - - const auto pivCard = SmartCard::ReaderStatus::instance()->getCard(mSerial); - if (!pivCard) { - error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(mSerial))); - finished(); - return; - } - // Check if we need to do the overwrite warning. if (!overwriteExistingAlreadyApproved) { const std::string existingKey = pivCard->keyGrip(cardSlot); if (!existingKey.empty() && (existingKey != mSubkey.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(mSubkey.keyGrip()), QString::fromStdString(mSerial)) .arg(QString::fromStdString(cardSlot)); ReaderStatus::mutableInstance()->startSimpleTransaction(cmd.toUtf8(), q_func(), "keyToPIVCardDone"); } -void KeyToCardCommand::Private::startCertificateToPIVCard() -{ - qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::Private::startCertificateToPIVCard()"; - - const auto pivCard = SmartCard::ReaderStatus::instance()->getCard(mSerial); - if (!pivCard) { - error(i18n("Failed to find the PIV card with the serial number: %1", QString::fromStdString(mSerial))); - finished(); - return; - } - - // verify that public keys on the card and in the certificate match - const std::string cardKeygrip = pivCard->keyGrip(cardSlot); - const std::string certificateKeygrip = mSubkey.keyGrip(); - if (cardKeygrip != certificateKeygrip) { - error(i18n("

The certificate does not seem to correspond to the key on the card.

" - "

Public key on card: %1
" - "Public key of certificate: %2

", - QString::fromStdString(cardKeygrip), - QString::fromStdString(certificateKeygrip))); - finished(); - return; - } - - auto ctx = Context::createForProtocol(GpgME::CMS); - QGpgME::QByteArrayDataProvider dp; - Data data(&dp); - const Error err = ctx->exportPublicKeys(mSubkey.parent().primaryFingerprint(), data); - if (err) { - error(i18nc("@info", "Exporting the certificate failed: %1", QString::fromUtf8(err.asString())), - i18nc("@title", "Error")); - finished(); - return; - } - const QByteArray certificateData = dp.data(); - - const QString cmd = QStringLiteral("SCD WRITECERT %1") - .arg(QString::fromStdString(cardSlot)); - auto transaction = std::unique_ptr(new WriteCertAssuanTransaction(certificateData)); - ReaderStatus::mutableInstance()->startTransaction(cmd.toUtf8(), q_func(), "certificateToPIVCardDone", std::move(transaction)); -} - void KeyToCardCommand::Private::authenticate() { qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::authenticate()"; auto cmd = new AuthenticatePIVCardApplicationCommand(mSerial, 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) { - startTransferToPIVCard(); + startKeyToPIVCard(); } } void KeyToCardCommand::Private::authenticationCanceled() { qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::authenticationCanceled()"; hasBeenCanceled = true; canceled(); } KeyToCardCommand::KeyToCardCommand(const GpgME::Subkey &key, const std::string &serialno) : Command(new Private(this, key, serialno)) { } KeyToCardCommand::KeyToCardCommand(const std::string& cardSlot, const std::string &serialno) : Command(new Private(this, cardSlot, serialno)) { } KeyToCardCommand::~KeyToCardCommand() { qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::~KeyToCardCommand()"; } bool KeyToCardCommand::supported() { return true; } void KeyToCardCommand::keyToOpenPGPCardDone(const GpgME::Error &err) { if (err) { d->error(i18nc("@info", "Moving the key to the card failed: %1", QString::fromUtf8(err.asString())), i18nc("@title", "Error")); } else if (!err.isCanceled()) { /* TODO DELETE_KEY is too strong, because it also deletes the stub * of the secret key. I could not find out how GnuPG does this. Question * to GnuPG Developers is pending an answer if (KMessageBox::questionYesNo(d->parentWidgetOrView(), i18n("Do you want to delete the key on this computer?"), i18nc("@title:window", "Key transferred to card")) == KMessageBox::Yes) { const QString cmd = QStringLiteral("DELETE_KEY --force %1").arg(d->mSubkey.keyGrip()); // Using readerstatus is a bit overkill but it's an easy way to talk to the agent. ReaderStatus::mutableInstance()->startSimpleTransaction(cmd.toUtf8(), this, "deleteDone"); } */ KMessageBox::information(d->parentWidgetOrView(), i18n("Successfully copied the key to the card."), i18nc("@title", "Success")); } d->finished(); } void KeyToCardCommand::keyToPIVCardDone(const GpgME::Error &err) { qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::keyToPIVCardDone():" << err.asString() << "(" << err.code() << ")"; - if (!err && !err.isCanceled()) { - d->startCertificateToPIVCard(); - return; - } - if (err) { // gpgme 1.13 reports "BAD PIN" instead of "NO AUTH" if (err.code() == GPG_ERR_NO_AUTH || err.code() == GPG_ERR_BAD_PIN) { d->authenticate(); return; } d->error(i18nc("@info", "Moving the key to the card failed: %1", QString::fromUtf8(err.asString())), i18nc("@title", "Error")); } d->finished(); } -void KeyToCardCommand::certificateToPIVCardDone(const GpgME::Error &err) -{ - qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::certificateToPIVCardDone():" - << err.asString() << "(" << err.code() << ")"; - if (err) { - // gpgme 1.13 reports "BAD PIN" instead of "NO AUTH" - if (err.code() == GPG_ERR_NO_AUTH || err.code() == GPG_ERR_BAD_PIN) { - d->authenticate(); - return; - } - - d->error(i18nc("@info", - "Writing the certificate to the card failed: %1", QString::fromUtf8(err.asString())), - i18nc("@title", "Error")); - } else if (!err.isCanceled()) { - KMessageBox::information(d->parentWidgetOrView(), - i18n("Successfully copied the certificate to the card."), - i18nc("@title", "Success")); - ReaderStatus::mutableInstance()->updateStatus(); - } - - d->finished(); -} - void KeyToCardCommand::deleteDone(const GpgME::Error &err) { if (err) { d->error(i18nc("@info", "Failed to delete the key: %1", QString::fromUtf8(err.asString())), i18nc("@title", "Error")); } d->finished(); } void KeyToCardCommand::doStart() { qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::doStart()"; d->start(); } void KeyToCardCommand::doCancel() { } #undef q_func #undef d_func diff --git a/src/commands/keytocardcommand.h b/src/commands/keytocardcommand.h index 959d56ce6..6bcb7c1eb 100644 --- a/src/commands/keytocardcommand.h +++ b/src/commands/keytocardcommand.h @@ -1,52 +1,52 @@ /* commands/keytocardcommand.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 __KLEOPATRA_COMMANDS_KEYTOCARDCOMMAND_H__ #define __KLEOPATRA_COMMANDS_KEYTOCARDCOMMAND_H__ #include -#include #include namespace Kleo { namespace Commands { class KeyToCardCommand : public Command { Q_OBJECT public: KeyToCardCommand(const GpgME::Subkey &key, const std::string &serialno); KeyToCardCommand(const std::string& cardSlot, const std::string &serialno); ~KeyToCardCommand() override; static bool supported(); public Q_SLOTS: void keyToOpenPGPCardDone(const GpgME::Error &err); void keyToPIVCardDone(const GpgME::Error &err); - void certificateToPIVCardDone(const GpgME::Error &err); void deleteDone(const GpgME::Error &err); private: void doStart() override; void doCancel() override; private: class Private; inline Private *d_func(); inline const Private *d_func() const; }; } } #endif /* __KLEOPATRA_COMMANDS_KEYTOCARDCOMMAND_H__ */ diff --git a/src/view/pivcardwidget.cpp b/src/view/pivcardwidget.cpp index 262784920..e314aeee5 100644 --- a/src/view/pivcardwidget.cpp +++ b/src/view/pivcardwidget.cpp @@ -1,274 +1,274 @@ /* view/pivcardwiget.cpp 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 */ #include "pivcardwidget.h" +#include "commands/certificatetopivcardcommand.h" #include "commands/changepincommand.h" -#include "commands/keytocardcommand.h" #include "commands/pivgeneratecardkeycommand.h" #include "commands/setpivcardapplicationadministrationkeycommand.h" #include "smartcard/pivcard.h" #include "smartcard/readerstatus.h" #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::SmartCard; namespace { static QString formatVersion(int value) { if (value < 0) { return QLatin1String("n/a"); } const unsigned int a = ((value >> 24) & 0xff); const unsigned int b = ((value >> 16) & 0xff); const unsigned int c = ((value >> 8) & 0xff); const unsigned int d = ((value ) & 0xff); if (a) { return QStringLiteral("%1.%2.%3.%4").arg(QString::number(a), QString::number(b), QString::number(c), QString::number(d)); } else if (b) { return QStringLiteral("%1.%2.%3").arg(QString::number(b), QString::number(c), QString::number(d)); } else if (c) { return QStringLiteral("%1.%2").arg(QString::number(c), QString::number(d)); } return QString::number(d); } } // Namespace PIVCardWidget::PIVCardWidget(QWidget *parent): QWidget(parent), mSerialNumber(new QLabel(this)), mVersionLabel(new QLabel(this)), mPIVAuthenticationKey(new QLabel(this)), mCardAuthenticationKey(new QLabel(this)), mDigitalSignatureKey(new QLabel(this)), mKeyManagementKey(new QLabel(this)), mGeneratePIVAuthenticationKeyBtn(new QPushButton(this)), mGenerateCardAuthenticationKeyBtn(new QPushButton(this)), mGenerateDigitalSignatureKeyBtn(new QPushButton(this)), mWriteDigitalSignatureKeyBtn(new QPushButton(this)), mGenerateKeyManagementKeyBtn(new QPushButton(this)) { auto grid = new QGridLayout; int row = 0; // Set up the scroll are auto area = new QScrollArea; area->setFrameShape(QFrame::NoFrame); area->setWidgetResizable(true); auto areaWidget = new QWidget; auto areaVLay = new QVBoxLayout(areaWidget); areaVLay->addLayout(grid); areaVLay->addStretch(1); area->setWidget(areaWidget); auto myLayout = new QVBoxLayout(this); myLayout->addWidget(area); // Version and Serialnumber grid->addWidget(mVersionLabel, row++, 0, 1, 2); mVersionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); grid->addWidget(new QLabel(i18n("Serial number:")), row, 0); grid->addWidget(mSerialNumber, row++, 1); mSerialNumber->setTextInteractionFlags(Qt::TextBrowserInteraction); // 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); grid->addWidget(new QLabel(i18n("PIV authentication:")), row, 0); grid->addWidget(mPIVAuthenticationKey, row, 1); mPIVAuthenticationKey->setTextInteractionFlags(Qt::TextBrowserInteraction); mGeneratePIVAuthenticationKeyBtn->setText(i18n("Generate")); mGeneratePIVAuthenticationKeyBtn->setEnabled(false); grid->addWidget(mGeneratePIVAuthenticationKeyBtn, row, 2); connect(mGeneratePIVAuthenticationKeyBtn, &QPushButton::clicked, this, &PIVCardWidget::generatePIVAuthenticationKey); row++; grid->addWidget(new QLabel(i18n("Card authentication:")), row, 0); grid->addWidget(mCardAuthenticationKey, row, 1); mCardAuthenticationKey->setTextInteractionFlags(Qt::TextBrowserInteraction); mGenerateCardAuthenticationKeyBtn->setText(i18n("Generate")); mGenerateCardAuthenticationKeyBtn->setEnabled(false); grid->addWidget(mGenerateCardAuthenticationKeyBtn, row, 2); connect(mGenerateCardAuthenticationKeyBtn, &QPushButton::clicked, this, &PIVCardWidget::generateCardAuthenticationKey); row++; grid->addWidget(new QLabel(i18n("Digital signature:")), row, 0); grid->addWidget(mDigitalSignatureKey, row, 1); mDigitalSignatureKey->setTextInteractionFlags(Qt::TextBrowserInteraction); mGenerateDigitalSignatureKeyBtn->setText(i18n("Generate")); mGenerateDigitalSignatureKeyBtn->setEnabled(false); grid->addWidget(mGenerateDigitalSignatureKeyBtn, row, 2); connect(mGenerateDigitalSignatureKeyBtn, &QPushButton::clicked, this, &PIVCardWidget::generateDigitalSignatureKey); mWriteDigitalSignatureKeyBtn->setText(i18n("Write Certificate")); mWriteDigitalSignatureKeyBtn->setToolTip(i18n("Write the certificate corresponding to this key to the card")); mWriteDigitalSignatureKeyBtn->setEnabled(false); grid->addWidget(mWriteDigitalSignatureKeyBtn, row, 3); - connect(mWriteDigitalSignatureKeyBtn, &QPushButton::clicked, this, [this] () { writeKeyToCard(PIVCard::digitalSignatureKeyRef()); }); + connect(mWriteDigitalSignatureKeyBtn, &QPushButton::clicked, this, [this] () { writeCertificateToCard(PIVCard::digitalSignatureKeyRef()); }); row++; grid->addWidget(new QLabel(i18n("Key management:")), row, 0); grid->addWidget(mKeyManagementKey, row, 1); mKeyManagementKey->setTextInteractionFlags(Qt::TextBrowserInteraction); mGenerateKeyManagementKeyBtn->setText(i18n("Generate")); mGenerateKeyManagementKeyBtn->setEnabled(false); grid->addWidget(mGenerateKeyManagementKeyBtn, row, 2); connect(mGenerateKeyManagementKeyBtn, &QPushButton::clicked, this, &PIVCardWidget::generateKeyManagementKey); row++; auto line2 = new QFrame(); line2->setFrameShape(QFrame::HLine); grid->addWidget(line2, row++, 0, 1, 4); auto actionLayout = new QHBoxLayout; { auto button = new QPushButton(i18n("Change PIN")); button->setToolTip(i18n("Change the PIV Card Application PIN that activates the PIV Card and enables private key operations using the stored keys.")); actionLayout->addWidget(button); connect(button, &QPushButton::clicked, this, [this] () { changePin(PIVCard::pinKeyRef()); }); } { auto button = new QPushButton(i18n("Change PUK")); button->setToolTip(i18n("Change the PIN Unblocking Key that enables a reset of the PIN.")); actionLayout->addWidget(button); connect(button, &QPushButton::clicked, this, [this] () { changePin(PIVCard::pukKeyRef()); }); } { auto button = new QPushButton(i18n("Change Admin Key")); button->setToolTip(i18n("Change the PIV Card Application Administration Key that is used by the " "PIV Card Application to authenticate the PIV Card Application Administrator and by the " "administrator (resp. Kleopatra) to authenticate the PIV Card Application.")); actionLayout->addWidget(button); connect(button, &QPushButton::clicked, this, [this] () { setAdminKey(); }); } actionLayout->addStretch(-1); grid->addLayout(actionLayout, row++, 0, 1, 4); grid->setColumnStretch(4, -1); } PIVCardWidget::~PIVCardWidget() { } void PIVCardWidget::setCard(const PIVCard *card) { mCardSerialNumber = card->serialNumber(); mVersionLabel->setText(i18nc("Placeholder is a version number", "PIV v%1 card", formatVersion(card->appVersion()))); if (card->displaySerialNumber() != card->serialNumber()) { mSerialNumber->setText(QStringLiteral("%1 (%2)").arg(QString::fromStdString(card->displaySerialNumber()), QString::fromStdString(card->serialNumber()))); } else { mSerialNumber->setText(QString::fromStdString(card->serialNumber())); } updateKey(PIVCard::pivAuthenticationKeyRef(), card, mPIVAuthenticationKey, mGeneratePIVAuthenticationKeyBtn, nullptr); updateKey(PIVCard::cardAuthenticationKeyRef(), card, mCardAuthenticationKey, mGenerateCardAuthenticationKeyBtn, nullptr); updateKey(PIVCard::digitalSignatureKeyRef(), card, mDigitalSignatureKey, mGenerateDigitalSignatureKeyBtn, mWriteDigitalSignatureKeyBtn); updateKey(PIVCard::keyManagementKeyRef(), card, mKeyManagementKey, mGenerateKeyManagementKeyBtn, nullptr); } void PIVCardWidget::updateKey(const std::string &keyRef, const PIVCard *card, QLabel *label, QPushButton *generateButton, QPushButton *writeButton) { const std::string grip = card->keyGrip(keyRef); label->setText(grip.empty() ? i18n("Slot empty") : QString::fromStdString(grip)); generateButton->setText(grip.empty() ? i18n("Generate") : i18n("Replace")); generateButton->setToolTip(grip.empty() ? i18nc("Placeholder is the display name of a key", "Generate %1", PIVCard::keyDisplayName(keyRef)) : i18nc("Placeholder is the display name of a key", "Replace %1 with new key", PIVCard::keyDisplayName(keyRef))); generateButton->setEnabled(true); if (writeButton) { writeButton->setEnabled(!grip.empty()); } } void PIVCardWidget::generateKey(const std::string &keyref) { auto cmd = new PIVGenerateCardKeyCommand(mCardSerialNumber, this); this->setEnabled(false); connect(cmd, &PIVGenerateCardKeyCommand::finished, this, [this]() { this->setEnabled(true); }); cmd->setKeyRef(keyref); cmd->start(); } -void PIVCardWidget::writeKeyToCard(const std::string &keyref) +void PIVCardWidget::writeCertificateToCard(const std::string &keyref) { - auto cmd = new KeyToCardCommand(keyref, mCardSerialNumber); + auto cmd = new CertificateToPIVCardCommand(keyref, mCardSerialNumber); this->setEnabled(false); - connect(cmd, &KeyToCardCommand::finished, + connect(cmd, &CertificateToPIVCardCommand::finished, this, [this]() { this->setEnabled(true); }); cmd->setParentWidget(this); cmd->start(); } void PIVCardWidget::generatePIVAuthenticationKey() { generateKey(PIVCard::pivAuthenticationKeyRef()); } void PIVCardWidget::generateCardAuthenticationKey() { generateKey(PIVCard::cardAuthenticationKeyRef()); } void PIVCardWidget::generateDigitalSignatureKey() { generateKey(PIVCard::digitalSignatureKeyRef()); } void PIVCardWidget::generateKeyManagementKey() { generateKey(PIVCard::keyManagementKeyRef()); } void PIVCardWidget::changePin(const std::string &keyRef) { auto cmd = new ChangePinCommand(mCardSerialNumber, this); this->setEnabled(false); connect(cmd, &ChangePinCommand::finished, this, [this]() { this->setEnabled(true); }); cmd->setKeyRef(keyRef); cmd->start(); } void PIVCardWidget::setAdminKey() { auto cmd = new SetPIVCardApplicationAdministrationKeyCommand(mCardSerialNumber, this); this->setEnabled(false); connect(cmd, &SetPIVCardApplicationAdministrationKeyCommand::finished, this, [this]() { this->setEnabled(true); }); cmd->start(); } diff --git a/src/view/pivcardwidget.h b/src/view/pivcardwidget.h index 2305ec42a..2e554acc0 100644 --- a/src/view/pivcardwidget.h +++ b/src/view/pivcardwidget.h @@ -1,65 +1,65 @@ /* 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 class QLabel; class QPushButton; namespace Kleo { namespace SmartCard { class PIVCard; } // namespace SmartCard class PIVCardWidget: public QWidget { Q_OBJECT public: explicit PIVCardWidget(QWidget *parent = nullptr); ~PIVCardWidget(); void setCard(const SmartCard::PIVCard* card); private: void updateKey(const std::string &keyRef, const SmartCard::PIVCard *card, QLabel *label, QPushButton *generateButton, QPushButton *writeButton); void generateKey(const std::string &keyref); - void writeKeyToCard(const std::string &keyref); + void writeCertificateToCard(const std::string &keyref); void changePin(const std::string &keyRef); void setAdminKey(); private Q_SLOTS: void generatePIVAuthenticationKey(); void generateCardAuthenticationKey(); void generateDigitalSignatureKey(); void generateKeyManagementKey(); private: std::string mCardSerialNumber; QLabel *mSerialNumber = nullptr, *mVersionLabel = nullptr, *mPIVAuthenticationKey = nullptr, *mCardAuthenticationKey = nullptr, *mDigitalSignatureKey = nullptr, *mKeyManagementKey = nullptr; QPushButton *mGeneratePIVAuthenticationKeyBtn = nullptr, *mGenerateCardAuthenticationKeyBtn = nullptr, *mGenerateDigitalSignatureKeyBtn = nullptr, *mWriteDigitalSignatureKeyBtn = nullptr, *mGenerateKeyManagementKeyBtn = nullptr; }; } // namespace Kleo #endif // VIEW_PIVCARDWIDGET_H