Commit 6106b1f8 authored by Ingo Klöcker's avatar Ingo Klöcker
Browse files

Refactor OpenPGPCard and PGPCardWidget

Make the two classes a bit more generic (e.g. remove too much usage of
hardcoded key references) as the corresponding PIV-specific classes.

GnuPG-bug-id: 5183
parent fb1ef218
......@@ -236,13 +236,13 @@ void KeyToCardCommand::Private::startKeyToOpenPGPCard() {
std::string existingKey;
QString encKeyWarning;
if (slot == 1) {
existingKey = pgpCard->sigFpr();
existingKey = pgpCard->keyFingerprint(OpenPGPCard::pgpSigKeyRef());
} else if (slot == 2) {
existingKey = pgpCard->encFpr();
existingKey = pgpCard->keyFingerprint(OpenPGPCard::pgpEncKeyRef());
encKeyWarning = i18n("It will no longer be possible to decrypt past communication "
"encrypted for the existing key.");
} else if (slot == 3) {
existingKey = pgpCard->authFpr();
existingKey = pgpCard->keyFingerprint(OpenPGPCard::pgpAuthKeyRef());
}
if (!existingKey.empty()) {
const QString message = i18nc("@info",
......
......@@ -3,6 +3,8 @@
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
......@@ -20,8 +22,9 @@
#include "openpgpcard.h"
#include "kleopatra_debug.h"
#include <KLocalizedString>
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::SmartCard;
......@@ -33,6 +36,25 @@ OpenPGPCard::OpenPGPCard(const Card &card)
: Card(card)
{
setAppName(AppName);
setInitialKeyInfos(OpenPGPCard::supportedKeys());
}
// static
std::string OpenPGPCard::pgpSigKeyRef()
{
return std::string("OPENPGP.1");
}
// static
std::string OpenPGPCard::pgpEncKeyRef()
{
return std::string("OPENPGP.2");
}
// static
std::string OpenPGPCard::pgpAuthKeyRef()
{
return std::string("OPENPGP.3");
}
// static
......@@ -53,19 +75,28 @@ std::string OpenPGPCard::resetCodeKeyRef()
return std::string("OPENPGP.2");
}
std::string OpenPGPCard::sigFpr() const
// static
const std::vector<KeyPairInfo> & OpenPGPCard::supportedKeys()
{
return mMetaInfo.value("SIGKEY-FPR");
}
static const std::vector<KeyPairInfo> keyInfos = {
{OpenPGPCard::pgpSigKeyRef(), "", "sc", "", ""},
{OpenPGPCard::pgpEncKeyRef(), "", "e", "", ""},
{OpenPGPCard::pgpAuthKeyRef(), "", "a", "", ""}
};
std::string OpenPGPCard::encFpr() const
{
return mMetaInfo.value("ENCKEY-FPR");
return keyInfos;
}
std::string OpenPGPCard::authFpr() const
// static
QString OpenPGPCard::keyDisplayName(const std::string &keyRef)
{
return mMetaInfo.value("AUTHKEY-FPR");
static const QMap<std::string, QString> displayNames = {
{ OpenPGPCard::pgpSigKeyRef(), i18n("Signature") },
{ OpenPGPCard::pgpEncKeyRef(), i18n("Encryption") },
{ OpenPGPCard::pgpAuthKeyRef(), i18n("Authentication") }
};
return displayNames.value(keyRef);
}
void OpenPGPCard::setCardInfo(const std::vector< std::pair<std::string, std::string> > &infos)
......@@ -76,24 +107,18 @@ void OpenPGPCard::setCardInfo(const std::vector< std::pair<std::string, std::str
if (parseCardInfo(pair.first, pair.second)) {
continue;
}
if (pair.first == "KEY-FPR" ||
pair.first == "KEY-TIME") {
// Key fpr and key time need to be distinguished, the number
// of the key decides the usage.
if (pair.first == "KEY-FPR") {
const auto values = QString::fromStdString(pair.second).split(QLatin1Char(' '));
if (values.size() < 2) {
qCWarning(KLEOPATRA_LOG) << "Invalid entry.";
setStatus(Card::CardError);
continue;
}
const auto usage = values[0];
const auto keyNumber = values[0];
const std::string keyRef = "OPENPGP." + keyNumber.toStdString();
const auto fpr = values[1].toStdString();
if (usage == QLatin1Char('1')) {
mMetaInfo.insert(std::string("SIG") + pair.first, fpr);
} else if (usage == QLatin1Char('2')) {
mMetaInfo.insert(std::string("ENC") + pair.first, fpr);
} else if (usage == QLatin1Char('3')) {
mMetaInfo.insert(std::string("AUTH") + pair.first, fpr);
if (keyNumber == QLatin1Char('1') || keyNumber == QLatin1Char('2') || keyNumber == QLatin1Char('3')) {
mMetaInfo.insert("KLEO-FPR-" + keyRef, fpr);
} else {
// Maybe more keyslots in the future?
qCDebug(KLEOPATRA_LOG) << "Unhandled keyslot";
......@@ -104,6 +129,11 @@ void OpenPGPCard::setCardInfo(const std::vector< std::pair<std::string, std::str
}
}
std::string OpenPGPCard::keyFingerprint(const std::string &keyRef) const
{
return mMetaInfo.value("KLEO-FPR-" + keyRef);
}
bool OpenPGPCard::operator == (const Card& rhs) const
{
const OpenPGPCard *other = dynamic_cast<const OpenPGPCard *>(&rhs);
......@@ -112,12 +142,8 @@ bool OpenPGPCard::operator == (const Card& rhs) const
}
return Card::operator ==(rhs)
&& sigFpr() == other->sigFpr()
&& encFpr() == other->encFpr()
&& authFpr() == other->authFpr()
&& manufacturer() == other->manufacturer()
&& cardHolder() == other->cardHolder()
&& pubkeyUrl() == other->pubkeyUrl();
&& mMetaInfo == other->mMetaInfo
&& mManufacturer == other->mManufacturer;
}
void OpenPGPCard::setManufacturer(const std::string &manufacturer)
......
#ifndef SMARTCARD_OPENPGPCARD_H
#define SMARTCARD_OPENPGPCARD_H
/* smartcard/openpgpcard.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <QMap>
#ifndef SMARTCARD_OPENPGPCARD_H
#define SMARTCARD_OPENPGPCARD_H
#include "card.h"
#include <QMap>
namespace Kleo
{
namespace SmartCard
{
struct KeyPairInfo;
/** Class to work with OpenPGP smartcards or compatible tokens */
class OpenPGPCard: public Card
{
......@@ -25,22 +29,28 @@ public:
static const std::string AppName;
static std::string pgpSigKeyRef();
static std::string pgpEncKeyRef();
static std::string pgpAuthKeyRef();
static std::string pinKeyRef();
static std::string adminPinKeyRef();
static std::string resetCodeKeyRef();
std::string encFpr() const;
std::string sigFpr() const;
std::string authFpr() const;
static const std::vector<KeyPairInfo> & supportedKeys();
static QString keyDisplayName(const std::string &keyRef);
void setCardInfo(const std::vector< std::pair<std::string, std::string> > &infos);
std::string keyFingerprint(const std::string &keyRef) const;
bool operator == (const Card& other) const override;
void setManufacturer(const std::string &manufacturer);
std::string manufacturer() const;
std::string pubkeyUrl() const;
private:
QMap <std::string, std::string> mMetaInfo;
std::string mManufacturer;
......
......@@ -3,6 +3,8 @@
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
......@@ -34,6 +36,7 @@
#include <KLocalizedString>
#include <KMessageBox>
#include <KSeparator>
#include <Libkleo/KeyCache>
#include <Libkleo/Formatting>
......@@ -94,6 +97,13 @@ class GenKeyThread: public QThread
std::string mBkpFile;
};
static void layoutKeyWidgets(QGridLayout *grid, const QString &keyName, const PGPCardWidget::KeyWidgets &keyWidgets)
{
int row = grid->rowCount();
grid->addWidget(new QLabel(keyName), row, 0);
grid->addWidget(keyWidgets.keyFingerprint, row, 1);
}
} // Namespace
PGPCardWidget::PGPCardWidget(QWidget *parent):
......@@ -101,83 +111,85 @@ PGPCardWidget::PGPCardWidget(QWidget *parent):
mSerialNumber(new QLabel(this)),
mCardHolderLabel(new QLabel(this)),
mVersionLabel(new QLabel(this)),
mSigningKey(new QLabel(this)),
mEncryptionKey(new QLabel(this)),
mAuthKey(new QLabel(this)),
mUrlLabel(new QLabel(this)),
mCardIsEmpty(false)
{
auto grid = new QGridLayout;
int row = 0;
// Set up the scroll area
auto myLayout = new QVBoxLayout(this);
myLayout->setContentsMargins(0, 0, 0, 0);
// Set up the scroll are
auto area = new QScrollArea;
area->setFrameShape(QFrame::NoFrame);
area->setWidgetResizable(true);
myLayout->addWidget(area);
auto areaWidget = new QWidget;
auto areaVLay = new QVBoxLayout(areaWidget);
areaVLay->addLayout(grid);
areaVLay->addStretch(1);
area->setWidget(areaWidget);
auto myLayout = new QVBoxLayout(this);
myLayout->setContentsMargins(0, 0, 0, 0);
myLayout->addWidget(area);
// Version and Serialnumber
grid->addWidget(mVersionLabel, row++, 0, 1, 2);
mVersionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
grid->addWidget(new QLabel(i18n("Serial number:")), row, 0);
grid->addWidget(mSerialNumber, row++, 1);
mSerialNumber->setTextInteractionFlags(Qt::TextBrowserInteraction);
// Cardholder Row
grid->addWidget(new QLabel(i18nc("The owner of a smartcard. GnuPG refers to this as cardholder.",
"Cardholder:")), row, 0);
grid->addWidget(mCardHolderLabel, row, 1);
mCardHolderLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
auto nameButtton = new QPushButton;
nameButtton->setIcon(QIcon::fromTheme(QStringLiteral("cell_edit")));
nameButtton->setToolTip(i18n("Change"));
grid->addWidget(nameButtton, row++, 2);
connect(nameButtton, &QPushButton::clicked, this, &PGPCardWidget::changeNameRequested);
// URL Row
grid->addWidget(new QLabel(i18nc("The URL under which a public key that "
"corresponds to a smartcard can be downloaded",
"Pubkey URL:")), row, 0);
grid->addWidget(mUrlLabel, row, 1);
mUrlLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
auto urlButtton = new QPushButton;
urlButtton->setIcon(QIcon::fromTheme(QStringLiteral("cell_edit")));
urlButtton->setToolTip(i18n("Change"));
grid->addWidget(urlButtton, row++, 2);
connect(urlButtton, &QPushButton::clicked, this, &PGPCardWidget::changeUrlRequested);
auto areaVLay = new QVBoxLayout(areaWidget);
// The keys
auto line1 = new QFrame();
line1->setFrameShape(QFrame::HLine);
grid->addWidget(line1, row++, 0, 1, 4);
grid->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("Keys:"))), row++, 0);
auto cardInfoGrid = new QGridLayout;
{
int row = 0;
// Version and Serialnumber
cardInfoGrid->addWidget(mVersionLabel, row, 0, 1, 2);
mVersionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
row++;
cardInfoGrid->addWidget(new QLabel(i18n("Serial number:")), row, 0);
cardInfoGrid->addWidget(mSerialNumber, row, 1);
mSerialNumber->setTextInteractionFlags(Qt::TextBrowserInteraction);
row++;
// Cardholder Row
cardInfoGrid->addWidget(new QLabel(i18nc("The owner of a smartcard. GnuPG refers to this as cardholder.",
"Cardholder:")), row, 0);
cardInfoGrid->addWidget(mCardHolderLabel, row, 1);
mCardHolderLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
{
auto button = new QPushButton;
button->setIcon(QIcon::fromTheme(QStringLiteral("cell_edit")));
button->setToolTip(i18n("Change"));
cardInfoGrid->addWidget(button, row, 2);
connect(button, &QPushButton::clicked, this, &PGPCardWidget::changeNameRequested);
}
row++;
// URL Row
cardInfoGrid->addWidget(new QLabel(i18nc("The URL under which a public key that "
"corresponds to a smartcard can be downloaded",
"Pubkey URL:")), row, 0);
cardInfoGrid->addWidget(mUrlLabel, row, 1);
mUrlLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
{
auto button = new QPushButton;
button->setIcon(QIcon::fromTheme(QStringLiteral("cell_edit")));
button->setToolTip(i18n("Change"));
cardInfoGrid->addWidget(button, row, 2);
connect(button, &QPushButton::clicked, this, &PGPCardWidget::changeUrlRequested);
}
grid->addWidget(new QLabel(i18n("Signature:")), row, 0);
grid->addWidget(mSigningKey, row++, 1);
mSigningKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
cardInfoGrid->setColumnStretch(cardInfoGrid->columnCount(), 1);
}
areaVLay->addLayout(cardInfoGrid);
grid->addWidget(new QLabel(i18n("Encryption:")), row, 0);
grid->addWidget(mEncryptionKey, row++, 1);
mEncryptionKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
areaVLay->addWidget(new KSeparator(Qt::Horizontal));
grid->addWidget(new QLabel(i18n("Authentication:")), row, 0);
grid->addWidget(mAuthKey, row++, 1);
mAuthKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
// The keys
areaVLay->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("Keys:"))));
auto keysGrid = new QGridLayout;
for (const auto &keyInfo : OpenPGPCard::supportedKeys()) {
KeyWidgets keyWidgets = createKeyWidgets(keyInfo);
layoutKeyWidgets(keysGrid, OpenPGPCard::keyDisplayName(keyInfo.keyRef), keyWidgets);
}
keysGrid->setColumnStretch(keysGrid->columnCount(), 1);
areaVLay->addLayout(keysGrid);
auto line2 = new QFrame();
line2->setFrameShape(QFrame::HLine);
grid->addWidget(line2, row++, 0, 1, 4);
grid->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("Actions:"))), row++, 0);
areaVLay->addWidget(new KSeparator(Qt::Horizontal));
areaVLay->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("Actions:"))));
auto actionLayout = new QHBoxLayout;
......@@ -210,9 +222,19 @@ PGPCardWidget::PGPCardWidget(QWidget *parent):
}
actionLayout->addStretch(-1);
grid->addLayout(actionLayout, row++, 0, 1, 4);
areaVLay->addLayout(actionLayout);
grid->setColumnStretch(4, -1);
areaVLay->addStretch(1);
}
PGPCardWidget::KeyWidgets PGPCardWidget::createKeyWidgets(const KeyPairInfo &keyInfo)
{
const std::string keyRef = keyInfo.keyRef;
KeyWidgets keyWidgets;
keyWidgets.keyFingerprint = new QLabel(this);
keyWidgets.keyFingerprint->setTextInteractionFlags(Qt::TextBrowserInteraction);
mKeyWidgets.insert(keyRef, keyWidgets);
return keyWidgets;
}
void PGPCardWidget::setCard(const OpenPGPCard *card)
......@@ -237,10 +259,12 @@ void PGPCardWidget::setCard(const OpenPGPCard *card)
QStringLiteral("<a href=\"%1\">%1</a>").arg(url.toHtmlEscaped()));
mUrlLabel->setOpenExternalLinks(true);
updateKey(mSigningKey, card->sigFpr());
updateKey(mEncryptionKey, card->encFpr());
updateKey(mAuthKey, card->authFpr());
mCardIsEmpty = card->authFpr().empty() && card->sigFpr().empty() && card->encFpr().empty();
updateKeyWidgets(OpenPGPCard::pgpSigKeyRef(), card);
updateKeyWidgets(OpenPGPCard::pgpEncKeyRef(), card);
updateKeyWidgets(OpenPGPCard::pgpAuthKeyRef(), card);
mCardIsEmpty = card->keyFingerprint(OpenPGPCard::pgpSigKeyRef()).empty()
&& card->keyFingerprint(OpenPGPCard::pgpEncKeyRef()).empty()
&& card->keyFingerprint(OpenPGPCard::pgpAuthKeyRef()).empty();
if (mKeyForCardKeysButton) {
mKeyForCardKeysButton->setEnabled(card->hasSigningKey() && card->hasEncryptionKey());
......@@ -476,37 +500,40 @@ void PGPCardWidget::createKeyFromCardKeys()
cmd->start();
}
void PGPCardWidget::updateKey(QLabel *label, const std::string &fpr)
void PGPCardWidget::updateKeyWidgets(const std::string &keyRef, const OpenPGPCard *card)
{
label->setText(QString::fromStdString(fpr));
if (fpr.empty()) {
label->setText(i18n("Slot empty"));
return;
}
std::vector<std::string> vec;
std::string keyid = fpr;
keyid.erase(0, keyid.size() - 16);
vec.push_back(keyid);
const auto subkeys = KeyCache::instance()->findSubkeysByKeyID(vec);
if (subkeys.empty() || subkeys[0].isNull()) {
label->setToolTip(i18n("Public key not found."));
return;
}
QStringList toolTips;
toolTips.reserve(subkeys.size());
for (const auto &sub: subkeys) {
// Yep you can have one subkey associated with multiple
// primary keys.
toolTips << Formatting::toolTip(sub.parent(), Formatting::Validity |
Formatting::StorageLocation |
Formatting::ExpiryDates |
Formatting::UserIDs |
Formatting::Fingerprint);
KeyWidgets widgets = mKeyWidgets.value(keyRef);
const std::string grip = card ? card->keyInfo(keyRef).grip : widgets.keyGrip;
widgets.keyGrip = grip;
if (grip.empty()) {
widgets.keyFingerprint->setText(i18n("Slot empty"));
} else {
if (card) {
// update information if called with card
std::string fpr = card->keyFingerprint(keyRef);
widgets.keyFingerprint->setText(QString::fromStdString(fpr));
std::string keyid = fpr;
keyid.erase(0, keyid.size() - 16);
const auto subkeys = KeyCache::instance()->findSubkeysByKeyID({keyid});
if (subkeys.empty() || subkeys[0].isNull()) {
widgets.keyFingerprint->setToolTip(i18n("Public key not found."));
} else {
QStringList toolTips;
toolTips.reserve(subkeys.size());
for (const auto &sub: subkeys) {
// Yep you can have one subkey associated with multiple
// primary keys.
toolTips << Formatting::toolTip(sub.parent(), Formatting::Validity |
Formatting::StorageLocation |
Formatting::ExpiryDates |
Formatting::UserIDs |
Formatting::Fingerprint);
}
widgets.keyFingerprint->setToolTip(toolTips.join(QLatin1String("<br/>")));
}
}
}
label->setToolTip(toolTips.join(QLatin1String("<br/>")));
return;
}
#include "pgpcardwidget.moc"
......@@ -3,13 +3,17 @@
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef VIEW_PGPCARDWIDGET_H
#define VIEW_PGPCARDWIDGET_H
#include <QMap>
#include <QWidget>
#include <gpgme++/error.h>
#include <string>
......@@ -23,6 +27,7 @@ class GenCardKeyDialog;
namespace SmartCard
{
struct KeyPairInfo;
class OpenPGPCard;
} // namespace SmartCard
......@@ -36,6 +41,11 @@ public:
void doGenKey(GenCardKeyDialog *dlg);
void genKeyDone(const GpgME::Error &err, const std::string &backup);
struct KeyWidgets {
std::string keyGrip;
QLabel *keyFingerprint = nullptr;
};
public Q_SLOTS:
void genkeyRequested();
void changeNameRequested();
......@@ -45,16 +55,17 @@ public Q_SLOTS:
void createKeyFromCardKeys();
private:
KeyWidgets createKeyWidgets(const SmartCard::KeyPairInfo &keyInfo);
void updateKeyWidgets(const std::string &keyRef, const SmartCard::OpenPGPCard *card);
void doChangePin(const std::string &keyRef);
void updateKey(QLabel *label, const std::string &fpr);
private:
QLabel *mSerialNumber = nullptr,
*mCardHolderLabel = nullptr,
*mVersionLabel = nullptr,
*mSigningKey = nullptr,
*mEncryptionKey = nullptr,
*mAuthKey = nullptr,
*mUrlLabel = nullptr;
QPushButton *mKeyForCardKeysButton = nullptr;
QMap<std::string, KeyWidgets> mKeyWidgets;
QString mUrl;
bool mCardIsEmpty = false;
bool mIs21 = false;
......
......@@ -23,7 +23,7 @@ namespace Kleo
namespace SmartCard
{
class KeyPairInfo;
struct KeyPairInfo;
class PIVCard;
} // namespace SmartCard
......
Markdown is supported
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