Verified Commit 88ce1d28 authored by Andre Heinecke's avatar Andre Heinecke
Browse files

Add a first impl and widget for PKCS#15 cards

This adds an implementation based on an atos/cardos 5.3 card
with applications from R&S. PKCS#15 cards might still
behave differently.
For now this is read only, just showing some status of the
card to show in the GUI that GnuPG can work with the card
and if we have an OpenPGP certificate for it.

GnuPG-Bug-Id: T4876
parent 5f8313d2
Pipeline #59003 passed with stage
in 14 minutes and 37 seconds
......@@ -118,6 +118,7 @@ set(_kleopatra_SRCS
view/padwidget.cpp
view/pgpcardwidget.cpp
view/pivcardwidget.cpp
view/p15cardwidget.cpp
view/netkeywidget.cpp
view/nullpinwidget.cpp
view/tabwidget.cpp
......@@ -271,6 +272,7 @@ set(_kleopatra_SRCS
smartcard/openpgpcard.cpp
smartcard/netkeycard.cpp
smartcard/pivcard.cpp
smartcard/p15card.cpp
smartcard/keypairinfo.cpp
smartcard/utils.cpp
......
/* smartcard/p15card.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Andre Heinecke <aheinecke@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "p15card.h"
#include "kleopatra_debug.h"
using namespace Kleo;
using namespace Kleo::SmartCard;
// static
const std::string P15Card::AppName = "p15";
P15Card::P15Card(const Card &card)
: Card(card)
{
setAppName(AppName);
}
std::string P15Card::appKeyFingerprint(const std::string &appKeyRef) const
{
return mMetaInfo.value("KLEO-FPR-" + appKeyRef);
}
void P15Card::setCardInfo(const std::vector< std::pair<std::string, std::string> > &infos)
{
// XXX: This is a copy of OpenPGPCard::setCardInfo
qCDebug(KLEOPATRA_LOG) << "Card" << serialNumber().c_str() << "info:";
for (const auto &pair: infos) {
qCDebug(KLEOPATRA_LOG) << pair.first.c_str() << ":" << pair.second.c_str();
if (parseCardInfo(pair.first, pair.second)) {
continue;
}
if (pair.first == "KEY-FPR") {
const auto values = QString::fromStdString(pair.second).split(QLatin1Char(' '));
if (values.size() < 2) {
qCWarning(KLEOPATRA_LOG) << "Invalid entry.";
setStatus(Card::CardError);
continue;
}
const auto keyNumber = values[0];
const std::string keyRef = "OPENPGP." + keyNumber.toStdString();
const auto fpr = values[1].toStdString();
if (keyNumber == QLatin1Char('1') || keyNumber == QLatin1Char('2') || keyNumber == QLatin1Char('3')) {
mMetaInfo.insert("KLEO-FPR-" + keyRef, fpr);
} else {
// Maybe more keyslots in the future?
qCDebug(KLEOPATRA_LOG) << "Unhandled keyslot";
}
} else {
mMetaInfo.insert(pair.first, pair.second);
}
}
}
void P15Card::setManufacturer(const std::string &manufacturer)
{
mManufacturer = manufacturer;
}
std::string P15Card::manufacturer() const
{
return mManufacturer;
}
bool P15Card::operator == (const Card& rhs) const
{
const P15Card *other = dynamic_cast<const P15Card *>(&rhs);
if (!other) {
return false;
}
return Card::operator ==(rhs)
&& mMetaInfo == other->mMetaInfo
&& mManufacturer == other->mManufacturer;
}
/* smartcard/pivcard.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include "card.h"
#include <QMap>
namespace Kleo
{
namespace SmartCard
{
/** Class to work with PKCS#15 smartcards or compatible tokens.
*
* A PKCS#15 card is pretty generic and there is no real standard
* for them. It all depends on the Apps running on the cards. This
* mostly tries to leave it to GnuPG to determine if there are usable
* things on the card. The generic info on all keys on the card is
* accessible through keyInfo from the parent class.
*
* The specialization is required for specific app support.
**/
class P15Card: public Card
{
public:
explicit P15Card(const Card &card);
static const std::string AppName;
void setAppKeyRef(const std::string &appKeyRef,
const std::string &value);
std::string appKeyRef(const std::string &appKeyRef) const;
/* Obtain an application specific fingerprint for a key
* stored on this card.
* e.g. An App Key Ref would be
* OpenPGPCard::pgpSigKeyRef */
std::string appKeyFingerprint(const std::string &appKeyRef) const;
void setCardInfo(const std::vector< std::pair<std::string, std::string> > &infos);
void setManufacturer(const std::string &manufacturer);
std::string manufacturer() const;
bool operator == (const Card& other) const override;
private:
QMap <std::string, std::string> mMetaInfo;
std::string mManufacturer;
};
} // namespace Smartcard
} // namespace Kleopatra
......@@ -42,6 +42,7 @@
#include "openpgpcard.h"
#include "netkeycard.h"
#include "pivcard.h"
#include "p15card.h"
#include <QStringList>
#include <QMutex>
......@@ -509,6 +510,28 @@ static void handle_piv_card(std::shared_ptr<Card> &ci, std::shared_ptr<Context>
ci.reset(pivCard);
}
static void handle_p15_card(std::shared_ptr<Card> &ci, std::shared_ptr<Context> &gpg_agent)
{
Error err;
auto p15Card = new P15Card(*ci);
auto info = gpgagent_statuslines(gpg_agent, "SCD LEARN --force", err);
if (err) {
ci->setStatus(Card::CardError);
return;
}
const auto fprs = gpgagent_statuslines(gpg_agent, "SCD GETATTR KEY-FPR", err);
if (!err) {
info.insert(info.end(), fprs.begin(), fprs.end());
}
p15Card->setCardInfo(info);
p15Card->setManufacturer(get_manufacturer(gpg_agent, err));
setDisplaySerialNumber(p15Card, gpg_agent);
ci.reset(p15Card);
}
static void handle_netkey_card(std::shared_ptr<Card> &ci, std::shared_ptr<Context> &gpg_agent)
{
Error err;
......@@ -630,6 +653,10 @@ static std::shared_ptr<Card> get_card_status(const std::string &serialNumber, co
qCDebug(KLEOPATRA_LOG) << "get_card_status: found PIV card" << ci->serialNumber().c_str() << "end";
handle_piv_card(ci, gpg_agent);
return ci;
} else if (appName == P15Card::AppName) {
qCDebug(KLEOPATRA_LOG) << "get_card_status: found P15 card" << ci->serialNumber().c_str() << "end";
handle_p15_card(ci, gpg_agent);
return ci;
} else {
qCDebug(KLEOPATRA_LOG) << "get_card_status: unhandled application:" << appName;
return ci;
......
/* view/p15cardwiget.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Andre Heinecke <aheinecke@g10code.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "p15cardwidget.h"
#include "smartcard/p15card.h"
#include "smartcard/openpgpcard.h"
#include <QVBoxLayout>
#include <QGridLayout>
#include <QScrollArea>
#include <QStringList>
#include <KLocalizedString>
#include <KSeparator>
#include <Libkleo/KeyCache>
#include <Libkleo/Formatting>
using namespace Kleo;
using namespace Kleo::SmartCard;
P15CardWidget::P15CardWidget(QWidget *parent)
: QWidget(parent)
, mSerialNumber(new QLabel(this))
, mVersionLabel(new QLabel(this))
, mSigFprLabel(new QLabel(this))
, mEncFprLabel(new QLabel(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 cardInfoGrid = new QGridLayout;
{
int row = 0;
// Version and Serialnumber
cardInfoGrid->addWidget(mVersionLabel, row++, 0, 1, 2);
mVersionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
cardInfoGrid->addWidget(new QLabel(i18n("Serial number:")), row, 0);
cardInfoGrid->addWidget(mSerialNumber, row++, 1);
mSerialNumber->setTextInteractionFlags(Qt::TextBrowserInteraction);
cardInfoGrid->setColumnStretch(cardInfoGrid->columnCount(), 1);
}
areaVLay->addLayout(cardInfoGrid);
areaVLay->addWidget(new KSeparator(Qt::Horizontal));
areaVLay->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("OpenPGP keys:"))));
areaVLay->addWidget(mSigFprLabel);
areaVLay->addWidget(mEncFprLabel);
areaVLay->addWidget(new KSeparator(Qt::Horizontal));
areaVLay->addStretch(1);
}
P15CardWidget::~P15CardWidget()
{
}
void P15CardWidget::setCard(const P15Card *card)
{
mCardSerialNumber = card->serialNumber();
mVersionLabel->setText(i18nc("%1 is a smartcard manufacturer", "%1 PKCS#15 card",
QString::fromStdString(card->manufacturer())));
mSerialNumber->setText(card->displaySerialNumber());
mSerialNumber->setToolTip(QString::fromStdString(card->serialNumber()));
std::string keyid = card->appKeyFingerprint(OpenPGPCard::pgpSigKeyRef());
if (!keyid.empty()) {
QString text = i18n("Signing key:") +
QStringLiteral("\t%1 (%2)")
.arg(QString::fromStdString(keyid))
.arg(QString::fromStdString(card->signingKeyRef()));
text += QStringLiteral("<br/><br/>");
keyid.erase(0, keyid.size() - 16);
const auto subkeys = KeyCache::instance()->findSubkeysByKeyID({keyid});
if (subkeys.empty() || subkeys[0].isNull()) {
text += 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::ExpiryDates |
Formatting::UserIDs |
Formatting::Fingerprint);
}
text += toolTips.join(QLatin1String("<br/>"));
}
mSigFprLabel->setText(text);
} else {
mSigFprLabel->setVisible(false);
}
keyid = card->appKeyFingerprint(OpenPGPCard::pgpEncKeyRef());
if (!keyid.empty()) {
mEncFprLabel->setText(i18n("Encryption key:") +
QStringLiteral(" %1 (%2)").arg(QString::fromStdString(keyid))
.arg(QString::fromStdString(card->encryptionKeyRef())));
keyid.erase(0, keyid.size() - 16);
const auto subkeys = KeyCache::instance()->findSubkeysByKeyID({keyid});
if (subkeys.empty() || subkeys[0].isNull()) {
mEncFprLabel->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);
}
mEncFprLabel->setToolTip(toolTips.join(QLatin1String("<br/>")));
}
} else {
mEncFprLabel->setVisible(false);
}
// updateKeyWidgets(OpenPGPCard::pgpSigKeyRef(), card);
// updateKeyWidgets(OpenPGPCard::pgpEncKeyRef(), card);
}
/* view/p15cardwiget.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2020 g10 Code GmbH
SPDX-FileContributor: Andre Heinecke <aheinecke@g10code.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QWidget>
#include <QLabel>
class QLabel;
namespace Kleo
{
namespace SmartCard
{
struct KeyPairInfo;
class P15Card;
} // namespace SmartCard
class P15CardWidget: public QWidget
{
Q_OBJECT
public:
explicit P15CardWidget(QWidget *parent = nullptr);
~P15CardWidget();
void setCard(const SmartCard::P15Card* card);
private:
std::string mCardSerialNumber;
QLabel *mSerialNumber = nullptr;
QLabel *mVersionLabel = nullptr;
QLabel *mSigFprLabel = nullptr;
QLabel *mEncFprLabel = nullptr;
};
}
......@@ -14,12 +14,14 @@
#include "smartcard/readerstatus.h"
#include "smartcard/openpgpcard.h"
#include "smartcard/netkeycard.h"
#include "smartcard/p15card.h"
#include "smartcard/pivcard.h"
#include "smartcard/utils.h"
#include "view/pgpcardwidget.h"
#include "view/netkeywidget.h"
#include "view/pivcardwidget.h"
#include "view/p15cardwidget.h"
#include "kleopatra_debug.h"
......@@ -50,7 +52,8 @@ public:
const QStringList supported = QStringList() << i18nc("OpenPGP refers to a smartcard protocol", "OpenPGP v2.0 - v3.3")
<< i18nc("Gnuk is a cryptographic token for GnuPG", "Gnuk")
<< i18nc("NetKey refers to a smartcard protocol", "NetKey v3")
<< i18nc("PIV refers to a smartcard protocol", "PIV (requires GnuPG 2.3 or later)");
<< i18nc("PIV refers to a smartcard protocol", "PIV (requires GnuPG 2.3 or later)")
<< i18nc("CardOS is a smartcard operating system", "CardOS 5 (various apps)");
lay->addWidget(new QLabel(QStringLiteral("\t\t<h3>") +
i18n("Please insert a compatible smartcard.") + QStringLiteral("</h3>"), this));
lay->addSpacing(10);
......@@ -126,6 +129,8 @@ void SmartCardWidget::Private::cardAddedOrChanged(const std::string &serialNumbe
cardAddedOrChanged<OpenPGPCard, PGPCardWidget>(serialNumber);
} else if (appName == SmartCard::PIVCard::AppName) {
cardAddedOrChanged<PIVCard, PIVCardWidget>(serialNumber);
} else if (appName == SmartCard::P15Card::AppName) {
cardAddedOrChanged<P15Card, P15CardWidget>(serialNumber);
} else {
qCWarning(KLEOPATRA_LOG) << "SmartCardWidget::Private::cardAddedOrChanged:"
<< "App" << appName.c_str() << "is not supported";
......
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