diff --git a/CMakeLists.txt b/CMakeLists.txt index 2883d24a9..f81f0baf0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,167 +1,167 @@ set(kleopatra_version 3.1.12) # The following is for Windows. Keep in line with kleopatra_version. set(kleopatra_fileversion 3,1,12,0) cmake_minimum_required(VERSION 3.5) project(kleopatra VERSION ${kleopatra_version}) # The RELEASE_SERVICE_VERSION is used by Gpg4win to add the Gpg4win version if (NOT RELEASE_SERVICE_VERSION) set (RELEASE_SERVICE_VERSION "21.03.80") endif() option(FORCE_DISABLE_KCMUTILS "Force building Kleopatra without KCMUtils. Doing this will disable configuration KCM Plugins. [default=OFF]" OFF) option(DISABLE_KWATCHGNUPG "Don't build the kwatchgnupg tool [default=OFF]" OFF) # Standalone build. Find / include everything necessary. set(KF5_MIN_VERSION "5.76.0") set(KMIME_VERSION "5.15.80") -set(LIBKLEO_VERSION "5.15.82") +set(LIBKLEO_VERSION "5.16.41") set(QT_REQUIRED_VERSION "5.13.0") set(GPGME_REQUIRED_VERSION "1.11.1") if (WIN32) set(KF5_WANT_VERSION "5.70.0") set(KMIME_WANT_VERSION "5.12.0") else () set(KF5_WANT_VERSION ${KF5_MIN_VERSION}) set(KMIME_WANT_VERSION ${KMIME_VERSION}) endif () find_package(ECM ${KF5_WANT_VERSION} CONFIG REQUIRED) set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH}) include(ECMInstallIcons) include(ECMSetupVersion) include(ECMAddTests) include(GenerateExportHeader) include(ECMGenerateHeaders) include(FeatureSummary) include(CheckFunctionExists) include(ECMGeneratePriFile) include(KDEInstallDirs) include(KDECMakeSettings) include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE) include(ECMAddAppIcon) include(ECMQtDeclareLoggingCategory) # Find KF5 packages if (NOT FORCE_DISABLE_KCMUTILS) find_package(KF5KCMUtils ${KF5_WANT_VERSION} CONFIG REQUIRED) endif() find_package(KF5WidgetsAddons ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5ConfigWidgets ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5CoreAddons ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5Codecs ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5Config ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5I18n ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5IconThemes ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5ItemModels ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5XmlGui ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5WindowSystem ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF5DocTools ${KF5_WANT_VERSION} CONFIG) find_package(KF5Crash ${KF5_WANT_VERSION} REQUIRED) set_package_properties(KF5DocTools PROPERTIES DESCRIPTION "Documentation tools" TYPE OPTIONAL PURPOSE "Required to generate Kleopatra documentation.") # Optional packages if (WIN32) # Only a replacement available for Windows so this # is required on other platforms. find_package(KF5DBusAddons ${KF5_WANT_VERSION} CONFIG) set_package_properties(KF5DBusAddons PROPERTIES DESCRIPTION "Support library to work with DBus" PURPOSE "DBus session integration" URL "https://inqlude.org/libraries/kdbusaddons.html" TYPE OPTIONAL) else() find_package(KF5DBusAddons ${KF5_WANT_VERSION} CONFIG REQUIRED) set(_kleopatra_dbusaddons_libs KF5::DBusAddons) endif() set(HAVE_QDBUS ${Qt5DBus_FOUND}) find_package(Gpgmepp ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED) find_package(QGpgme ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED) # Kdepimlibs packages find_package(KF5Libkleo ${LIBKLEO_VERSION} CONFIG REQUIRED) find_package(KF5Mime ${KMIME_WANT_VERSION} CONFIG REQUIRED) find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets Test Network PrintSupport) find_package(Assuan2 REQUIRED) set(HAVE_KCMUTILS ${KF5KCMUtils_FOUND}) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Boost 1.34.0 REQUIRED) find_path(Boost_TOPOLOGICAL_SORT_DIR NAMES boost/graph/topological_sort.hpp PATHS ${Boost_INCLUDE_DIRS}) if(NOT Boost_TOPOLOGICAL_SORT_DIR) message(FATAL_ERROR "The Boost Topological_sort header was NOT found. Should be part of Boost graph module.") endif() set(kleopatra_release FALSE) if(NOT kleopatra_release) if(GIT_FOUND) execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --oneline --format=%h ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${kdepim_SOURCE_DIR}/kleopatra OUTPUT_VARIABLE Kleopatra_WC_REVISION) string(REGEX REPLACE "\n" "" Kleopatra_WC_REVISION "${Kleopatra_WC_REVISION}") execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --oneline --format=%ci ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${kdepim_SOURCE_DIR}/kleopatra OUTPUT_VARIABLE Kleopatra_WC_LAST_CHANGED_DATE) string(REGEX REPLACE " [-0-9:+ ]*\n" "" Kleopatra_WC_LAST_CHANGED_DATE "${Kleopatra_WC_LAST_CHANGED_DATE}") set(kleopatra_version "${kleopatra_version}-git${Kleopatra_WC_REVISION} (${Kleopatra_WC_LAST_CHANGED_DATE})") endif() endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version-kleopatra.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/version-kleopatra.h) include (ConfigureChecks.cmake) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-kleopatra.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kleopatra.h) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${Boost_INCLUDE_DIR} ${ASSUAN2_INCLUDES} ) add_definitions(-D_ASSUAN_ONLY_GPG_ERRORS) #add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x050f00) add_definitions(-DKF_DISABLE_DEPRECATED_BEFORE_AND_AT=0x054D00) if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-braces -Wno-parentheses -Wno-ignored-qualifiers") endif() add_definitions(-DQT_NO_EMIT) kde_enable_exceptions() add_subdirectory(pics) add_subdirectory(src) if(BUILD_TESTING) add_subdirectory(tests) add_subdirectory(autotests) endif() ecm_qt_install_logging_categories( EXPORT KLEOPATRA FILE kleopatra.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR} ) if(KF5DocTools_FOUND) add_subdirectory(doc) endif() diff --git a/src/commands/createopenpgpkeyfromcardkeyscommand.cpp b/src/commands/createopenpgpkeyfromcardkeyscommand.cpp index e9de35773..4a19999be 100644 --- a/src/commands/createopenpgpkeyfromcardkeyscommand.cpp +++ b/src/commands/createopenpgpkeyfromcardkeyscommand.cpp @@ -1,223 +1,236 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/createopenpgpkeyfromcardkeyscommand.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 "createopenpgpkeyfromcardkeyscommand.h" #include "cardcommand_p.h" #include "dialogs/adduseriddialog.h" #include "smartcard/openpgpcard.h" +#include "smartcard/pivcard.h" #include "smartcard/readerstatus.h" #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::Dialogs; using namespace Kleo::SmartCard; using namespace GpgME; using namespace QGpgME; class CreateOpenPGPKeyFromCardKeysCommand::Private : public CardCommand::Private { friend class ::Kleo::Commands::CreateOpenPGPKeyFromCardKeysCommand; CreateOpenPGPKeyFromCardKeysCommand *q_func() const { return static_cast(q); } public: explicit Private(CreateOpenPGPKeyFromCardKeysCommand *qq, const std::string &serialNumber, const std::string &appName, QWidget *parent); ~Private(); private: void start(); void slotDialogAccepted(); void slotDialogRejected(); void slotResult(const Error &err); void ensureDialogCreated(); private: std::string appName; QPointer dialog; }; CreateOpenPGPKeyFromCardKeysCommand::Private *CreateOpenPGPKeyFromCardKeysCommand::d_func() { return static_cast(d.get()); } const CreateOpenPGPKeyFromCardKeysCommand::Private *CreateOpenPGPKeyFromCardKeysCommand::d_func() const { return static_cast(d.get()); } #define d d_func() #define q q_func() CreateOpenPGPKeyFromCardKeysCommand::Private::Private(CreateOpenPGPKeyFromCardKeysCommand *qq, const std::string &serialNumber, const std::string &appName_, QWidget *parent) : CardCommand::Private(qq, serialNumber, parent) , appName(appName_) { } CreateOpenPGPKeyFromCardKeysCommand::Private::~Private() { } void CreateOpenPGPKeyFromCardKeysCommand::Private::start() { - const auto pgpCard = ReaderStatus::instance()->getCard(serialNumber()); - if (!pgpCard) { - error(i18n("Failed to find the OpenPGP card with the serial number: %1", QString::fromStdString(serialNumber()))); + const auto card = ReaderStatus::instance()->getCard(serialNumber(), appName); + if (!card) { + error(i18n("Failed to find the smartcard with the serial number: %1", QString::fromStdString(serialNumber()))); + finished(); + return; + } + + Key signingKey; + if (card->appName() == OpenPGPCard::AppName) { + const auto pgpCard = std::dynamic_pointer_cast(card); + signingKey = KeyCache::instance()->findByKeyIDOrFingerprint(pgpCard->sigFpr()); + } else if (appName == PIVCard::AppName) { + const auto pivCard = std::dynamic_pointer_cast(card); + signingKey = KeyCache::instance()->findSubkeyByKeyGrip(pivCard->keyGrip(PIVCard::digitalSignatureKeyRef()), OpenPGP).parent(); + } else { + qCWarning(KLEOPATRA_LOG) << "CreateOpenPGPKeyFromCardKeysCommand does not support card application" << QString::fromStdString(appName); finished(); return; } - const auto signingKey = KeyCache::instance()->findByKeyIDOrFingerprint(pgpCard->sigFpr()); if (!signingKey.isNull()) { const QString message = i18nc("@info", "

There is already an OpenPGP key corresponding to the signing key on this card:

%1

" "

Do you still want to create an OpenPGP key for the card keys?

", Formatting::summaryLine(signingKey)); const auto choice = KMessageBox::warningContinueCancel(parentWidgetOrView(), message, i18nc("@title:window", "Create OpenPGP Key"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Notify); if (choice != KMessageBox::Continue) { finished(); return; } } ensureDialogCreated(); dialog->setWindowTitle(i18n("Enter User ID")); - dialog->setName(pgpCard->cardHolder()); + dialog->setName(card->cardHolder()); dialog->show(); } void CreateOpenPGPKeyFromCardKeysCommand::Private::slotDialogAccepted() { if (!(engineInfo(GpgEngine).engineVersion() < "2.3.0")) { Error err; std::unique_ptr c = Context::createForEngine(AssuanEngine, &err); if (err.code() == GPG_ERR_NOT_SUPPORTED) { finished(); return; } auto assuanContext = std::shared_ptr(c.release()); const auto resultSerialNumber = ReaderStatus::switchCard(assuanContext, serialNumber(), err); if (err || resultSerialNumber != serialNumber()) { qCWarning(KLEOPATRA_LOG) << "Switching to card" << QString::fromStdString(serialNumber()) << "failed"; finished(); return; } const auto resultAppName = ReaderStatus::switchApp(assuanContext, serialNumber(), appName, err); if (err || resultAppName != appName) { qCWarning(KLEOPATRA_LOG) << "Switching card to" << QString::fromStdString(appName) << "app failed"; finished(); return; } } const auto backend = openpgp(); if (!backend) { finished(); return; } QuickJob *const job = backend->quickJob(); if (!job) { finished(); return; } connect(job, SIGNAL(result(GpgME::Error)), q, SLOT(slotResult(GpgME::Error))); const QString userID = Formatting::prettyNameAndEMail(OpenPGP, QString(), dialog->name(), dialog->email(), dialog->comment()); const QDateTime expires = QDateTime(); const unsigned int flags = GPGME_CREATE_FORCE; job->startCreate(userID, "card", expires, Key(), flags); } void CreateOpenPGPKeyFromCardKeysCommand::Private::slotDialogRejected() { canceled(); } void CreateOpenPGPKeyFromCardKeysCommand::Private::slotResult(const Error &err) { if (err.isCanceled()) { // do nothing } else if (err) { error(i18nc("@info", "Creating an OpenPGP key from the card keys failed: %1", QString::fromUtf8(err.asString())), i18nc("@title", "Error")); } else { information(i18nc("@info", "Successfully generated an OpenPGP key from the card keys."), i18nc("@title", "Success")); } finished(); } void CreateOpenPGPKeyFromCardKeysCommand::Private::ensureDialogCreated() { if (dialog) { return; } dialog = new AddUserIDDialog; applyWindowID(dialog); dialog->setAttribute(Qt::WA_DeleteOnClose); connect(dialog, SIGNAL(accepted()), q, SLOT(slotDialogAccepted())); connect(dialog, SIGNAL(rejected()), q, SLOT(slotDialogRejected())); } CreateOpenPGPKeyFromCardKeysCommand::CreateOpenPGPKeyFromCardKeysCommand(const std::string &serialNumber, const std::string &appName, QWidget *parent) : CardCommand(new Private(this, serialNumber, appName, parent)) { } CreateOpenPGPKeyFromCardKeysCommand::~CreateOpenPGPKeyFromCardKeysCommand() { } void CreateOpenPGPKeyFromCardKeysCommand::doStart() { d->start(); } void CreateOpenPGPKeyFromCardKeysCommand::doCancel() { } #undef d #undef q #include "moc_createopenpgpkeyfromcardkeyscommand.cpp" diff --git a/src/view/pgpcardwidget.cpp b/src/view/pgpcardwidget.cpp index 2e7fd5db5..1d15eaf00 100644 --- a/src/view/pgpcardwidget.cpp +++ b/src/view/pgpcardwidget.cpp @@ -1,523 +1,523 @@ /* view/pgpcardwiget.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include "pgpcardwidget.h" #include "kleopatra_debug.h" #include "commands/changepincommand.h" #include "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; }; } // 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)), mKeyForCardKeysButton(new QPushButton(this)), mCardIsEmpty(false) { auto grid = new QGridLayout; int row = 0; // Set up the scroll are auto area = new QScrollArea; area->setFrameShape(QFrame::NoFrame); area->setWidgetResizable(true); auto areaWidget = new QWidget; auto areaVLay = new QVBoxLayout(areaWidget); areaVLay->addLayout(grid); areaVLay->addStretch(1); area->setWidget(areaWidget); auto myLayout = new QVBoxLayout(this); myLayout->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); // 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("Signature:")), row, 0); grid->addWidget(mSigningKey, row++, 1); mSigningKey->setTextInteractionFlags(Qt::TextBrowserInteraction); grid->addWidget(new QLabel(i18n("Encryption:")), row, 0); grid->addWidget(mEncryptionKey, row++, 1); mEncryptionKey->setTextInteractionFlags(Qt::TextBrowserInteraction); grid->addWidget(new QLabel(i18n("Authentication:")), row, 0); grid->addWidget(mAuthKey, row++, 1); mAuthKey->setTextInteractionFlags(Qt::TextBrowserInteraction); auto line2 = new QFrame(); line2->setFrameShape(QFrame::HLine); grid->addWidget(line2, row++, 0, 1, 4); grid->addWidget(new QLabel(QStringLiteral("%1").arg(i18n("Actions:"))), row++, 0); auto actionLayout = new QHBoxLayout; auto generateButton = new QPushButton(i18n("Generate new Keys")); generateButton->setToolTip(i18n("Create a new primary key and generate subkeys on the card.")); actionLayout->addWidget(generateButton); connect(generateButton, &QPushButton::clicked, this, &PGPCardWidget::genkeyRequested); auto pinButtton = new QPushButton(i18n("Change PIN")); pinButtton->setToolTip(i18n("Change the PIN required to unblock the smartcard.")); actionLayout->addWidget(pinButtton); connect(pinButtton, &QPushButton::clicked, this, [this] () { doChangePin(OpenPGPCard::pinKeyRef()); }); auto pukButton = new QPushButton(i18n("Change Admin PIN")); pukButton->setToolTip(i18n("Change the PIN required to unlock the smartcard.")); actionLayout->addWidget(pukButton); connect(pukButton, &QPushButton::clicked, this, [this] () { doChangePin(OpenPGPCard::adminPinKeyRef()); }); auto resetCodeButton = new QPushButton(i18n("Change Reset Code")); pukButton->setToolTip(i18n("Change the PIN required to reset the smartcard to an empty state.")); actionLayout->addWidget(resetCodeButton); connect(resetCodeButton, &QPushButton::clicked, this, [this] () { doChangePin(OpenPGPCard::resetCodeKeyRef()); }); - mKeyForCardKeysButton->setText(i18n("Create Key for Card Keys")); + 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); grid->setColumnStretch(4, -1); } 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(); mKeyForCardKeysButton->setEnabled(!mCardIsEmpty); } 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) { if (!(engineInfo(GpgME::GpgEngine).engineVersion() < "2.3.0")) { GpgME::Error err; std::unique_ptr c = GpgME::Context::createForEngine(GpgME::AssuanEngine, &err); if (err.code() == GPG_ERR_NOT_SUPPORTED) { return; } auto assuanContext = std::shared_ptr(c.release()); const auto serialNumber = ReaderStatus::switchCard(assuanContext, mRealSerial, err); if (err || serialNumber != mRealSerial) { qCWarning(KLEOPATRA_LOG) << "Switching to card" << QString::fromStdString(mRealSerial) << "failed"; return; } const auto appName = ReaderStatus::switchApp(assuanContext, mRealSerial, OpenPGPCard::AppName, err); if (err || appName != OpenPGPCard::AppName) { qCWarning(KLEOPATRA_LOG) << "Switching card to" << QString::fromStdString(OpenPGPCard::AppName) << "app failed"; 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) { 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); } label->setToolTip(toolTips.join(QLatin1String("
"))); return; } #include "pgpcardwidget.moc" diff --git a/src/view/pivcardwidget.cpp b/src/view/pivcardwidget.cpp index 64dda2fa7..7bbb8318f 100644 --- a/src/view/pivcardwidget.cpp +++ b/src/view/pivcardwidget.cpp @@ -1,340 +1,362 @@ /* 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 "tooltippreferences.h" #include "commands/certificatetopivcardcommand.h" #include "commands/changepincommand.h" +#include "commands/createopenpgpkeyfromcardkeyscommand.h" #include "commands/importcertificatefrompivcardcommand.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 #include #include #include using namespace GpgME; using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::SmartCard; namespace { static void layoutKeyWidgets(QGridLayout *grid, const QString &keyName, const PIVCardWidget::KeyWidgets &keyWidgets) { int row = grid->rowCount(); grid->addWidget(new QLabel(keyName), row, 0); grid->addWidget(keyWidgets.certificateInfo, row, 1, 1, 2); grid->addWidget(keyWidgets.generateButton, row, 3); if (keyWidgets.writeKeyButton) { grid->addWidget(keyWidgets.writeKeyButton, row, 4); } row++; grid->addWidget(keyWidgets.keyGrip, row, 1); grid->addWidget(keyWidgets.keyAlgorithm, row, 2); grid->addWidget(keyWidgets.writeCertificateButton, row, 3); grid->addWidget(keyWidgets.importCertificateButton, row, 4); } static int toolTipOptions() { using namespace Kleo::Formatting; static const int validityFlags = Validity | Issuer | ExpiryDates | CertificateUsage; static const int ownerFlags = Subject | UserIDs | OwnerTrust; static const int detailsFlags = StorageLocation | CertificateType | SerialNumber | Fingerprint; const TooltipPreferences prefs; int flags = KeyID; flags |= prefs.showValidity() ? validityFlags : 0; flags |= prefs.showOwnerInformation() ? ownerFlags : 0; flags |= prefs.showCertificateDetails() ? detailsFlags : 0; return flags; } } -PIVCardWidget::PIVCardWidget(QWidget *parent): - QWidget(parent), - mSerialNumber(new QLabel(this)), - mVersionLabel(new QLabel(this)) +PIVCardWidget::PIVCardWidget(QWidget *parent) + : QWidget(parent) + , mSerialNumber(new QLabel(this)) + , mVersionLabel(new QLabel(this)) + , mKeyForCardKeysButton(new QPushButton(this)) { // Set up the scroll area auto myLayout = new QVBoxLayout(this); myLayout->setContentsMargins(0, 0, 0, 0); auto area = new QScrollArea; area->setFrameShape(QFrame::NoFrame); area->setWidgetResizable(true); myLayout->addWidget(area); auto areaWidget = new QWidget; area->setWidget(areaWidget); auto areaVLay = new QVBoxLayout(areaWidget); auto grid = new QGridLayout; areaVLay->addLayout(grid); areaVLay->addStretch(1); const int columnCount = 5; int row = 0; // 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); { auto line = new QFrame(); line->setFrameShape(QFrame::HLine); grid->addWidget(line, row++, 0, 1, columnCount); } // The keys grid->addWidget(new QLabel(QStringLiteral("%1").arg(i18n("Keys:"))), row++, 0); { KeyWidgets keyWidgets = createKeyWidgets(PIVCard::pivAuthenticationKeyRef()); layoutKeyWidgets(grid, i18n("PIV authentication:"), keyWidgets); } { KeyWidgets keyWidgets = createKeyWidgets(PIVCard::cardAuthenticationKeyRef()); layoutKeyWidgets(grid, i18n("Card authentication:"), keyWidgets); } { KeyWidgets keyWidgets = createKeyWidgets(PIVCard::digitalSignatureKeyRef()); layoutKeyWidgets(grid, i18n("Digital signature:"), keyWidgets); } { KeyWidgets keyWidgets = createKeyWidgets(PIVCard::keyManagementKeyRef()); layoutKeyWidgets(grid, i18n("Key management:"), keyWidgets); } row = grid->rowCount(); { auto line = new QFrame(); line->setFrameShape(QFrame::HLine); grid->addWidget(line, row++, 0, 1, columnCount); } auto actionLayout = new QHBoxLayout; + mKeyForCardKeysButton->setText(i18nc("@action:button", "Create OpenPGP Key")); + mKeyForCardKeysButton->setToolTip(i18nc("@info:tooltip", "Create an OpenPGP key for the keys stored on the card.")); + actionLayout->addWidget(mKeyForCardKeysButton); + connect(mKeyForCardKeysButton, &QPushButton::clicked, this, &PIVCardWidget::createKeyFromCardKeys); + { auto button = new QPushButton(i18nc("@action:button", "Change PIN")); button->setToolTip(i18nc("@info:tooltip", "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(i18nc("@action:button", "Change PUK")); button->setToolTip(i18nc("@info:tooltip", "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(i18nc("@action:button", "Change Admin Key")); button->setToolTip(i18nc("@info:tooltip", "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, columnCount); grid->setColumnStretch(4, -1); } PIVCardWidget::KeyWidgets PIVCardWidget::createKeyWidgets(const std::string &keyRef) { KeyWidgets keyWidgets; keyWidgets.keyGrip = new QLabel(this); keyWidgets.keyGrip->setTextInteractionFlags(Qt::TextBrowserInteraction); keyWidgets.keyAlgorithm = new QLabel(this); keyWidgets.keyAlgorithm->setTextInteractionFlags(Qt::TextSelectableByMouse); keyWidgets.certificateInfo = new QLabel(this); keyWidgets.certificateInfo->setTextInteractionFlags(Qt::TextBrowserInteraction); keyWidgets.generateButton = new QPushButton(i18nc("@action:button", "Generate"), this); keyWidgets.generateButton->setEnabled(false); connect(keyWidgets.generateButton, &QPushButton::clicked, this, [this, keyRef] () { generateKey(keyRef); }); keyWidgets.writeCertificateButton = new QPushButton(i18nc("@action:button", "Write Certificate")); keyWidgets.writeCertificateButton->setToolTip(i18nc("@info:tooltip", "Write the certificate corresponding to this key to the card")); keyWidgets.writeCertificateButton->setEnabled(false); connect(keyWidgets.writeCertificateButton, &QPushButton::clicked, this, [this, keyRef] () { writeCertificateToCard(keyRef); }); keyWidgets.importCertificateButton = new QPushButton(i18nc("@action:button", "Import Certificate")); keyWidgets.importCertificateButton->setToolTip(i18nc("@info:tooltip", "Import the certificate stored on the card")); keyWidgets.importCertificateButton->setEnabled(false); connect(keyWidgets.importCertificateButton, &QPushButton::clicked, this, [this, keyRef] () { importCertificateFromCard(keyRef); }); if (keyRef == PIVCard::cardAuthenticationKeyRef() || keyRef == PIVCard::keyManagementKeyRef()) { keyWidgets.writeKeyButton = new QPushButton(i18nc("@action:button", "Write Key")); keyWidgets.writeKeyButton->setToolTip(i18nc("@info:tooltip", "Write the key pair of a certificate to the card")); keyWidgets.writeKeyButton->setEnabled(true); connect(keyWidgets.writeKeyButton, &QPushButton::clicked, this, [this, keyRef] () { writeKeyToCard(keyRef); }); } mKeyWidgets.insert(keyRef, keyWidgets); return keyWidgets; } PIVCardWidget::~PIVCardWidget() { } void PIVCardWidget::setCard(const PIVCard *card) { mCardSerialNumber = card->serialNumber(); mVersionLabel->setText(i18nc("%1 version number", "PIV v%1 card", card->displayAppVersion())); mSerialNumber->setText(card->displaySerialNumber()); mSerialNumber->setToolTip(QString::fromStdString(card->serialNumber())); updateKeyWidgets(PIVCard::pivAuthenticationKeyRef(), card); updateKeyWidgets(PIVCard::cardAuthenticationKeyRef(), card); updateKeyWidgets(PIVCard::digitalSignatureKeyRef(), card); updateKeyWidgets(PIVCard::keyManagementKeyRef(), card); + + const bool signingKeyAvailable = !card->keyGrip(PIVCard::digitalSignatureKeyRef()).empty(); + const bool encryptionKeyAvailable = !card->keyGrip(PIVCard::keyManagementKeyRef()).empty(); + mKeyForCardKeysButton->setEnabled(signingKeyAvailable && encryptionKeyAvailable); } void PIVCardWidget::updateKeyWidgets(const std::string &keyRef, const PIVCard *card) { KeyWidgets widgets = mKeyWidgets.value(keyRef); const std::string grip = card ? card->keyGrip(keyRef) : widgets.keyGrip->text().toStdString(); if (grip.empty()) { widgets.certificateInfo->setText(i18nc("@info", "slot empty")); widgets.certificateInfo->setToolTip(QString()); widgets.keyGrip->setText(QString()); widgets.keyAlgorithm->setText(QString()); widgets.generateButton->setText(i18nc("@action:button", "Generate")); widgets.generateButton->setToolTip( i18nc("@info:tooltip %1 display name of a key", "Generate %1", PIVCard::keyDisplayName(keyRef))); widgets.writeCertificateButton->setEnabled(false); widgets.importCertificateButton->setEnabled(false); } else { - const Key certificate = KeyCache::instance()->findSubkeyByKeyGrip(grip).parent(); - if (!certificate.isNull() && certificate.protocol() == GpgME::CMS) { + const Key certificate = KeyCache::instance()->findSubkeyByKeyGrip(grip, GpgME::CMS).parent(); + if (!certificate.isNull()) { widgets.certificateInfo->setText( i18nc("X.509 certificate DN (validity, created: date)", "%1 (%2, created: %3)", DN(certificate.userID(0).id()).prettyDN(), Formatting::complianceStringShort(certificate), Formatting::creationDateString(certificate))); widgets.certificateInfo->setToolTip(Formatting::toolTip(certificate, toolTipOptions())); widgets.writeCertificateButton->setEnabled(true); } else { widgets.certificateInfo->setText(i18nc("@info", "no matching certificate")); widgets.certificateInfo->setToolTip(QString()); widgets.writeCertificateButton->setEnabled(false); } if (card) { // update information if called with card widgets.keyGrip->setText(QString::fromStdString(grip)); const std::string algo = card->keyAlgorithm(keyRef); widgets.keyAlgorithm->setText(algo.empty() ? i18nc("@info unknown key algorithm", "unknown") : QString::fromStdString(algo)); widgets.importCertificateButton->setEnabled(!card->certificateData(keyRef).empty()); } widgets.generateButton->setText(i18nc("@action:button", "Replace")); widgets.generateButton->setToolTip( i18nc("@info:tooltip %1 display name of a key", "Replace %1 with new key", PIVCard::keyDisplayName(keyRef))); } widgets.generateButton->setEnabled(true); } 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::writeCertificateToCard(const std::string &keyref) { auto cmd = new CertificateToPIVCardCommand(keyref, mCardSerialNumber); this->setEnabled(false); connect(cmd, &CertificateToPIVCardCommand::finished, this, [this]() { this->setEnabled(true); }); cmd->setParentWidget(this); cmd->start(); } void PIVCardWidget::importCertificateFromCard(const std::string &keyref) { auto cmd = new ImportCertificateFromPIVCardCommand(keyref, mCardSerialNumber); this->setEnabled(false); connect(cmd, &ImportCertificateFromPIVCardCommand::finished, this, [this, keyref] () { this->updateKeyWidgets(keyref, nullptr); this->setEnabled(true); }); cmd->setParentWidget(this); cmd->start(); } void PIVCardWidget::writeKeyToCard(const std::string &keyref) { auto cmd = new KeyToCardCommand(keyref, mCardSerialNumber, PIVCard::AppName); this->setEnabled(false); connect(cmd, &KeyToCardCommand::finished, this, [this]() { this->setEnabled(true); }); cmd->setParentWidget(this); cmd->start(); } +void PIVCardWidget::createKeyFromCardKeys() +{ + auto cmd = new CreateOpenPGPKeyFromCardKeysCommand(mCardSerialNumber, PIVCard::AppName, this); + this->setEnabled(false); + connect(cmd, &CreateOpenPGPKeyFromCardKeysCommand::finished, + this, [this]() { + this->setEnabled(true); + }); + cmd->start(); +} + void PIVCardWidget::changePin(const std::string &keyRef) { auto cmd = new ChangePinCommand(mCardSerialNumber, PIVCard::AppName, 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 865f32e4d..cc790526a 100644 --- a/src/view/pivcardwidget.h +++ b/src/view/pivcardwidget.h @@ -1,66 +1,68 @@ /* 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 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 *writeCertificateButton = nullptr; QPushButton *importCertificateButton = nullptr; QPushButton *writeKeyButton = nullptr; }; private: KeyWidgets createKeyWidgets(const std::string &keyRef); void updateKeyWidgets(const std::string &keyRef, const SmartCard::PIVCard *card); void generateKey(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