Verified Commit 311a8679 authored by Ingo Klöcker's avatar Ingo Klöcker Committed by Ingo Klöcker
Browse files

Allow (re-)generating individual keys of OpenPGP smart cards

This makes it possible to (re-)generate the keys stored in the different
slots of an OpenPGP smart card for one of the algorithms supported by
the smart card.

GnuPG-bug-id: 4429
parent 770f60e9
......@@ -181,6 +181,8 @@ set(_kleopatra_SRCS
commands/newcertificatesigningrequestcommand.h
commands/newopenpgpcertificatecommand.cpp
commands/newopenpgpcertificatecommand.h
commands/openpgpgeneratecardkeycommand.cpp
commands/openpgpgeneratecardkeycommand.h
commands/pivgeneratecardkeycommand.cpp
commands/pivgeneratecardkeycommand.h
commands/refreshcertificatecommand.cpp
......
/* commands/openpgpgeneratecardkeycommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "openpgpgeneratecardkeycommand.h"
#include "cardcommand_p.h"
#include "smartcard/algorithminfo.h"
#include "smartcard/openpgpcard.h"
#include "smartcard/readerstatus.h"
#include "dialogs/gencardkeydialog.h"
#include <KLocalizedString>
#include <QGpgME/Debug>
#include <gpgme++/error.h>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::SmartCard;
using namespace GpgME;
class OpenPGPGenerateCardKeyCommand::Private : public CardCommand::Private
{
friend class ::Kleo::Commands::OpenPGPGenerateCardKeyCommand;
OpenPGPGenerateCardKeyCommand *q_func() const
{
return static_cast<OpenPGPGenerateCardKeyCommand *>(q);
}
public:
explicit Private(OpenPGPGenerateCardKeyCommand *qq, const std::string &keyref, const std::string &serialNumber, QWidget *p);
void init();
private:
void slotDialogAccepted();
void slotDialogRejected();
void slotResult(const Error &err);
private:
void ensureDialogCreated();
void generateKey();
private:
std::string keyRef;
bool overwriteExistingKey = false;
std::string algorithm;
QPointer<GenCardKeyDialog> dialog;
};
OpenPGPGenerateCardKeyCommand::Private *OpenPGPGenerateCardKeyCommand::d_func()
{
return static_cast<Private *>(d.get());
}
const OpenPGPGenerateCardKeyCommand::Private *OpenPGPGenerateCardKeyCommand::d_func() const
{
return static_cast<const Private *>(d.get());
}
#define d d_func()
#define q q_func()
OpenPGPGenerateCardKeyCommand::Private::Private(OpenPGPGenerateCardKeyCommand *qq, const std::string &keyRef_, const std::string &serialNumber, QWidget *p)
: CardCommand::Private(qq, serialNumber, p)
, keyRef{keyRef_}
{
}
void OpenPGPGenerateCardKeyCommand::Private::init()
{
}
void OpenPGPGenerateCardKeyCommand::Private::slotDialogAccepted()
{
algorithm = dialog->getKeyParams().algorithm;
generateKey();
}
void OpenPGPGenerateCardKeyCommand::Private::slotDialogRejected()
{
finished();
}
void OpenPGPGenerateCardKeyCommand::Private::slotResult(const GpgME::Error& err)
{
qCDebug(KLEOPATRA_LOG).nospace() << q << "::Private::" << __func__ << err;
if (err) {
error(i18nc("@info", "Generating key failed: %1", QString::fromLatin1(err.asString())));
} else if (!err.isCanceled()) {
success(i18nc("@info", "Key successfully generated."));
ReaderStatus::mutableInstance()->updateStatus();
}
finished();
}
void OpenPGPGenerateCardKeyCommand::Private::ensureDialogCreated()
{
if (dialog) {
return;
}
dialog = new GenCardKeyDialog(GenCardKeyDialog::KeyAlgorithm, parentWidgetOrView());
dialog->setAttribute(Qt::WA_DeleteOnClose);
connect(dialog, &QDialog::accepted, q, [this]() { slotDialogAccepted(); });
connect(dialog, &QDialog::rejected, q, [this]() { slotDialogRejected(); });
}
void OpenPGPGenerateCardKeyCommand::Private::generateKey()
{
qCDebug(KLEOPATRA_LOG).nospace() << q << "::Private::" << __func__;
auto pgpCard = ReaderStatus::instance()->getCard<OpenPGPCard>(serialNumber());
if (!pgpCard) {
error(i18n("Failed to find the OpenPGP card with the serial number: %1", QString::fromStdString(serialNumber())));
finished();
return;
}
QByteArrayList command;
command << "SCD GENKEY";
if (overwriteExistingKey) {
command << "--force";
}
if (!algorithm.empty()) {
command << "--algo=" + QByteArray::fromStdString(algorithm);
}
command << "--" << QByteArray::fromStdString(keyRef);
ReaderStatus::mutableInstance()->startSimpleTransaction(pgpCard, command.join(' '), q, [this](const GpgME::Error &err) {
slotResult(err);
});
}
OpenPGPGenerateCardKeyCommand::OpenPGPGenerateCardKeyCommand(const std::string &keyref, const std::string &serialNumber, QWidget *p)
: CardCommand(new Private(this, keyref, serialNumber, p))
{
d->init();
}
OpenPGPGenerateCardKeyCommand::~OpenPGPGenerateCardKeyCommand()
{
qCDebug(KLEOPATRA_LOG).nospace() << this << "::" << __func__;
}
void OpenPGPGenerateCardKeyCommand::doStart()
{
qCDebug(KLEOPATRA_LOG).nospace() << this << "::" << __func__;
// check if key exists
auto pgpCard = ReaderStatus::instance()->getCard<OpenPGPCard>(d->serialNumber());
if (!pgpCard) {
d->error(i18n("Failed to find the OpenPGP card with the serial number: %1", QString::fromStdString(d->serialNumber())));
d->finished();
return;
}
auto existingKey = pgpCard->keyInfo(d->keyRef).grip;
if (!existingKey.empty()) {
const QString warningText = i18nc("@info",
"<p>This card already contains a key in this slot. Continuing will <b>overwrite</b> that key.</p>"
"<p>If there is no backup the existing key will be irrecoverably lost.</p>") +
i18n("The existing key has the ID:") + QStringLiteral("<pre>%1</pre>").arg(QString::fromStdString(existingKey)) +
(d->keyRef == OpenPGPCard::pgpEncKeyRef() ?
i18n("It will no longer be possible to decrypt past communication encrypted for the existing key.") :
QString());
const auto choice = KMessageBox::warningContinueCancel(d->parentWidgetOrView(), warningText,
i18nc("@title:window", "Overwrite Existing Key"),
KStandardGuiItem::cont(), KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous);
if (choice != KMessageBox::Continue) {
d->finished();
return;
}
d->overwriteExistingKey = true;
}
d->ensureDialogCreated();
Q_ASSERT(d->dialog);
d->dialog->setSupportedAlgorithms(pgpCard->supportedAlgorithms(d->keyRef), "rsa2048");
d->dialog->show();
}
void OpenPGPGenerateCardKeyCommand::doCancel()
{
}
#undef d
#undef q
#include "moc_openpgpgeneratecardkeycommand.cpp"
/* commands/openpgpgeneratecardkeycommand.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "cardcommand.h"
namespace Kleo
{
namespace Commands
{
class OpenPGPGenerateCardKeyCommand : public CardCommand
{
Q_OBJECT
public:
explicit OpenPGPGenerateCardKeyCommand(const std::string &keyref, const std::string &serialNumber, QWidget *parent);
~OpenPGPGenerateCardKeyCommand() override;
private:
void doStart() override;
void doCancel() override;
private:
class Private;
inline Private *d_func();
inline const Private *d_func() const;
};
} // namespace Commands
} // namespace Kleo
......@@ -110,7 +110,7 @@ QString OpenPGPCard::keyDisplayName(const std::string &keyRef)
void OpenPGPCard::setSupportedAlgorithms(const std::vector<std::string> &algorithms)
{
const static std::vector<std::string> allowedAlgorithms = {
static const std::vector<std::string> allowedAlgorithms = {
"brainpoolP256r1",
"brainpoolP384r1",
"brainpoolP512r1",
......@@ -139,11 +139,11 @@ std::string OpenPGPCard::pubkeyUrl() const
std::vector<AlgorithmInfo> OpenPGPCard::supportedAlgorithms(const std::string &keyRef)
{
const static std::map<std::string, QString> displayNames = {
static const std::map<std::string, QString> displayNames = {
{ "brainpoolP256r1", i18nc("@info", "ECC (Brainpool P-256)") },
{ "brainpoolP384r1", i18nc("@info", "ECC (Brainpool P-384)") },
{ "brainpoolP512r1", i18nc("@info", "ECC (Brainpool P-512)") },
{ "curve25519", i18nc("@info", "ECC (Curve 25519)") },
{ "curve25519", i18nc("@info", "ECC (Curve25519)") },
{ "nistp256", i18nc("@info", "ECC (NIST P-256)") },
{ "nistp384", i18nc("@info", "ECC (NIST P-384)") },
{ "nistp521", i18nc("@info", "ECC (NIST P-521)") },
......
/* view/openpgpkeycardwidget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileCopyrightText: 2021, 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
......@@ -39,6 +39,7 @@ struct KeyWidgets {
QLabel *keyTitleLabel = nullptr;
QLabel *keyInfoLabel = nullptr;
QPushButton *showCertificateDetailsButton = nullptr;
QPushButton *generateButton = nullptr;
QPushButton *createCSRButton = nullptr;
};
......@@ -51,6 +52,8 @@ KeyWidgets createKeyWidgets(const KeyPairInfo &keyInfo, QWidget *parent)
keyWidgets.showCertificateDetailsButton = new QPushButton{i18nc("@action:button", "Show Details"), parent};
keyWidgets.showCertificateDetailsButton->setToolTip(i18nc("@action:tooltip", "Show detailed information about this key"));
keyWidgets.showCertificateDetailsButton->setEnabled(false);
keyWidgets.generateButton = new QPushButton{i18nc("@action:button", "Generate Key"), parent};
keyWidgets.generateButton->setEnabled(false);
if (keyInfo.canCertify() || keyInfo.canSign() || keyInfo.canAuthenticate())
{
keyWidgets.createCSRButton = new QPushButton{i18nc("@action:button", "Create CSR"), parent};
......@@ -94,6 +97,8 @@ OpenPGPKeyCardWidget::Private::Private(OpenPGPKeyCardWidget *q)
const std::string keyRef = keyInfo.keyRef;
connect(keyWidgets.showCertificateDetailsButton, &QPushButton::clicked,
q, [this, keyRef] () { showCertificateDetails(keyRef); });
connect(keyWidgets.generateButton, &QPushButton::clicked,
q, [q, keyRef] () { Q_EMIT q->generateKeyRequested(keyRef); });
if (keyWidgets.createCSRButton) {
connect(keyWidgets.createCSRButton, &QPushButton::clicked,
q, [q, keyRef] () { Q_EMIT q->createCSRRequested(keyRef); });
......@@ -105,6 +110,7 @@ OpenPGPKeyCardWidget::Private::Private(OpenPGPKeyCardWidget *q)
auto buttons = new QHBoxLayout;
buttons->addWidget(keyWidgets.showCertificateDetailsButton);
buttons->addWidget(keyWidgets.generateButton);
if (keyWidgets.createCSRButton) {
buttons->addWidget(keyWidgets.createCSRButton);
}
......@@ -150,6 +156,7 @@ void OpenPGPKeyCardWidget::Private::updateKeyWidgets(const std::string &openPGPK
widgets.keyTitleLabel->setVisible(cardSupportsKey);
widgets.keyInfoLabel->setVisible(cardSupportsKey);
widgets.showCertificateDetailsButton->setVisible(cardSupportsKey);
widgets.generateButton->setVisible(cardSupportsKey && (mAllowedActions & Action::GenerateKey));
if (widgets.createCSRButton) {
widgets.createCSRButton->setVisible(cardSupportsKey && (mAllowedActions & Action::CreateCSR));
}
......@@ -162,6 +169,8 @@ void OpenPGPKeyCardWidget::Private::updateKeyWidgets(const std::string &openPGPK
if (widgets.keyFingerprint.empty()) {
widgets.keyInfoLabel->setTextFormat(Qt::RichText);
widgets.keyInfoLabel->setText(i18nc("@info", "<em>No key</em>"));
widgets.generateButton->setText(i18nc("@action:button", "Generate Key"));
widgets.generateButton->setToolTip(i18nc("@info:tooltip", "Generate a key for this card slot"));
if (widgets.createCSRButton) {
widgets.createCSRButton->setEnabled(false);
}
......@@ -207,10 +216,14 @@ void OpenPGPKeyCardWidget::Private::updateKeyWidgets(const std::string &openPGPK
const auto lineSeparator = widgets.keyInfoLabel->textFormat() == Qt::PlainText ? QLatin1String("\n") : QLatin1String("<br>");
widgets.keyInfoLabel->setText(lines.join(lineSeparator));
widgets.generateButton->setText(i18nc("@action:button", "Regenerate Key"));
widgets.generateButton->setToolTip(i18nc("@info:tooltip", "Generate a new key for this card slot replacing the existing key"));
if (widgets.createCSRButton) {
widgets.createCSRButton->setEnabled(true);
}
}
widgets.generateButton->setEnabled(!widgets.generateButton->isHidden());
}
void OpenPGPKeyCardWidget::Private::showCertificateDetails(const std::string &openPGPKeyRef)
......
/* view/openpgpkeycardwidget.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2021 g10 Code GmbH
SPDX-FileCopyrightText: 2021, 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
......@@ -27,7 +27,8 @@ public:
enum Action {
NoAction = 0x00,
CreateCSR = 0x01,
AllActions = CreateCSR,
GenerateKey = 0x02,
AllActions = CreateCSR | GenerateKey,
};
Q_DECLARE_FLAGS(Actions, Action)
......@@ -41,6 +42,7 @@ public Q_SLOTS:
Q_SIGNALS:
void createCSRRequested(const std::string &keyRef);
void generateKeyRequested(const std::string &keyRef);
private:
class Private;
......
......@@ -3,7 +3,7 @@
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-FileCopyrightText: 2020, 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
......@@ -17,6 +17,7 @@
#include "commands/createcsrforcardkeycommand.h"
#include "commands/createopenpgpkeyfromcardkeyscommand.h"
#include "commands/openpgpgeneratecardkeycommand.h"
#include "smartcard/algorithminfo.h"
#include "smartcard/openpgpcard.h"
......@@ -182,6 +183,7 @@ PGPCardWidget::PGPCardWidget(QWidget *parent):
mKeysWidget = new OpenPGPKeyCardWidget{this};
areaVLay->addWidget(mKeysWidget);
connect(mKeysWidget, &OpenPGPKeyCardWidget::createCSRRequested, this, &PGPCardWidget::createCSR);
connect(mKeysWidget, &OpenPGPKeyCardWidget::generateKeyRequested, this, &PGPCardWidget::generateKey);
areaVLay->addWidget(new KSeparator(Qt::Horizontal));
......@@ -505,4 +507,15 @@ void PGPCardWidget::createCSR(const std::string &keyref)
cmd->start();
}
void PGPCardWidget::generateKey(const std::string &keyref)
{
auto cmd = new OpenPGPGenerateCardKeyCommand(keyref, mRealSerial, this);
this->setEnabled(false);
connect(cmd, &OpenPGPGenerateCardKeyCommand::finished,
this, [this]() {
this->setEnabled(true);
});
cmd->start();
}
#include "pgpcardwidget.moc"
......@@ -3,7 +3,7 @@
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-FileCopyrightText: 2020, 2022 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
......@@ -50,6 +50,7 @@ public Q_SLOTS:
void changeUrlResult(const GpgME::Error &err);
void createKeyFromCardKeys();
void createCSR(const std::string &keyref);
void generateKey(const std::string &keyref);
private:
void doChangePin(const std::string &keyRef, Commands::ChangePinCommand::ChangePinMode mode = Commands::ChangePinCommand::NormalMode);
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment