Commit 91afdc61 authored by Ingo Klöcker's avatar Ingo Klöcker
Browse files

Add algorithm to list of keys of PIV cards

* Add helper struct KeyPairInfo
* Add PIVCard::supportedKeys() returning the list of supported keys
  (resp. key slot ids)
* Get information about algorithm for all supported keys on a PIV card
* Show the used algorithms in the key list

GnuPG-bug-id: 4794
parent 71af96a9
Pipeline #35122 passed with stage
in 49 minutes and 8 seconds
......@@ -247,6 +247,7 @@ set(_kleopatra_SRCS
smartcard/openpgpcard.cpp
smartcard/netkeycard.cpp
smartcard/pivcard.cpp
smartcard/keypairinfo.cpp
aboutdata.cpp
systrayicon.cpp
......
/* smartcard/keypairinfo.cpp
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
*/
#include "keypairinfo.h"
#include <QString>
#include <QStringList>
using namespace Kleo::SmartCard;
// static
KeyPairInfo KeyPairInfo::fromStatusLine(const std::string &s) {
// The format of a KEYPAIRINFO line is
// KEYPAIRINFO <hexgrip> <keyref> [usage] [keytime] [algostr]
// The string s does not contain the leading "KEYPAIRINFO ".
KeyPairInfo info;
const auto values = QString::fromStdString(s).split(QLatin1Char(' '));
if (values.size() < 2) {
return info;
}
info.grip = values[0].toStdString();
info.keyRef = values[1].toStdString();
if (values.size() >= 3) {
info.usage = values[2].toStdString();
}
if (values.size() >= 4) {
info.keyTime = values[3].toStdString();
}
if (values.size() >= 5) {
info.algorithm = values[4].toStdString();
}
return info;
}
/* smartcard/keypairinfo.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
*/
#ifndef SMARTCARD_KEYPAIRINFO_H
#define SMARTCARD_KEYPAIRINFO_H
#include <string>
namespace Kleo
{
namespace SmartCard
{
struct KeyPairInfo {
static KeyPairInfo fromStatusLine(const std::string &s);
std::string grip;
std::string keyRef;
std::string usage;
std::string keyTime;
std::string algorithm;
};
} // namespace Smartcard
} // namespace Kleopatra
#endif // SMARTCARD_PIVCARD_H
......@@ -9,6 +9,8 @@
#include "pivcard.h"
#include "keypairinfo.h"
#include <KLocalizedString>
#include "kleopatra_debug.h"
......@@ -62,6 +64,19 @@ std::string PIVCard::pukKeyRef()
return std::string("PIV.81");
}
// static
const std::vector<std::string> & PIVCard::supportedKeys()
{
static const std::vector<std::string> keyRefs = {
PIVCard::pivAuthenticationKeyRef(),
PIVCard::cardAuthenticationKeyRef(),
PIVCard::digitalSignatureKeyRef(),
PIVCard::keyManagementKeyRef()
};
return keyRefs;
}
// static
QString PIVCard::keyDisplayName(const std::string &keyRef)
{
......@@ -121,16 +136,14 @@ void PIVCard::setCardInfo(const std::vector< std::pair<std::string, std::string>
if (pair.first == "APPVERSION") {
setAppVersion(parseAppVersion(pair.second));
} else if (pair.first == "KEYPAIRINFO") {
const auto values = QString::fromStdString(pair.second).split(QLatin1Char(' '));
if (values.size() != 3) {
qCWarning(KLEOPATRA_LOG) << "Invalid KEYPAIRINFO entry" << QString::fromStdString(pair.second);
const KeyPairInfo info = KeyPairInfo::fromStatusLine(pair.second);
if (info.grip.empty()) {
qCWarning(KLEOPATRA_LOG) << "Invalid KEYPAIRINFO status line"
<< QString::fromStdString(pair.second);
setStatus(Card::CardError);
continue;
}
const auto grip = values[0].toStdString();
const auto keyRef = values[1].toStdString();
//const auto usage = values[2];
mMetaInfo.insert("KEYPAIRINFO-" + keyRef, grip);
mMetaInfo.insert("KEYPAIRINFO-" + info.keyRef, info.grip);
} else {
mMetaInfo.insert(pair.first, pair.second);
}
......@@ -147,6 +160,16 @@ void PIVCard::setDisplaySerialNumber(const std::string &serialno)
mDisplaySerialNumber = serialno;
}
std::string PIVCard::keyAlgorithm(const std::string &keyRef) const
{
return mMetaInfo.value("KLEO-KEYALGO-" + keyRef);
}
void PIVCard::setKeyAlgorithm(const std::string &keyRef, const std::string &algorithm)
{
mMetaInfo.insert("KLEO-KEYALGO-" + keyRef, algorithm);
}
bool PIVCard::operator == (const Card& rhs) const
{
const PIVCard *other = dynamic_cast<const PIVCard *>(&rhs);
......
......@@ -32,6 +32,7 @@ public:
static std::string pinKeyRef();
static std::string pukKeyRef();
static const std::vector<std::string> & supportedKeys();
static QString keyDisplayName(const std::string &keyRef);
static std::vector< std::pair<std::string, QString> > supportedAlgorithms(const std::string &keyRef);
......@@ -42,6 +43,9 @@ public:
std::string displaySerialNumber() const;
void setDisplaySerialNumber(const std::string &sn);
std::string keyAlgorithm(const std::string &keyRef) const;
void setKeyAlgorithm(const std::string &keyRef, const std::string &algorithm);
bool operator == (const Card& other) const override;
private:
......
......@@ -13,6 +13,8 @@
#include "readerstatus.h"
#include "keypairinfo.h"
#include <utils/gnupg-helper.h>
#include <Libkleo/FileSystemWatcher>
......@@ -178,7 +180,7 @@ static std::unique_ptr<DefaultAssuanTransaction> gpgagent_default_transact(std::
return gpgagent_transact(gpgAgent, command, std::unique_ptr<DefaultAssuanTransaction>(new DefaultAssuanTransaction), err);
}
const std::vector< std::pair<std::string, std::string> > gpgagent_statuslines(std::shared_ptr<Context> gpgAgent, const char *what, Error &err)
static const std::vector< std::pair<std::string, std::string> > gpgagent_statuslines(std::shared_ptr<Context> gpgAgent, const char *what, Error &err)
{
const std::unique_ptr<DefaultAssuanTransaction> t = gpgagent_default_transact(gpgAgent, what, err);
if (t.get()) {
......@@ -302,18 +304,46 @@ static void handle_piv_card(std::shared_ptr<Card> &ci, std::shared_ptr<Context>
pivCard->setSerialNumber(ci->serialNumber());
const auto displaySerialnumber = scd_getattr_status(gpg_agent, "$DISPSERIALNO", err);
if (err.code()) {
if (err) {
qCWarning(KLEOPATRA_LOG) << "handle_piv_card(): Error on GETATTR $DISPSERIALNO:"
<< "GpgME::Error(" << err.encodedError() << " (" << err.asString() << "))";
}
pivCard->setDisplaySerialNumber(err.code() ? ci->serialNumber() : displaySerialnumber);
pivCard->setDisplaySerialNumber(err ? ci->serialNumber() : displaySerialnumber);
const auto info = gpgagent_statuslines(gpg_agent, "SCD LEARN --force", err);
if (err.code()) {
if (err) {
ci->setStatus(Card::CardError);
return;
}
pivCard->setCardInfo(info);
for (const std::string &keyRef : PIVCard::supportedKeys()) {
if (!pivCard->keyGrip(keyRef).empty()) {
const std::string command = std::string("SCD READKEY --info-only -- ") + keyRef;
const auto keyPairInfoLines = gpgagent_statuslines(gpg_agent, command.c_str(), err);
if (err) {
qCWarning(KLEOPATRA_LOG) << "handle_piv_card(): Error on " << QString::fromStdString(command) << ":"
<< "GpgME::Error(" << err.encodedError() << " (" << err.asString() << "))";
break;
}
for (const auto &pair: keyPairInfoLines) {
if (pair.first == "KEYPAIRINFO") {
const KeyPairInfo info = KeyPairInfo::fromStatusLine(pair.second);
if (info.grip.empty()) {
qCWarning(KLEOPATRA_LOG) << "Invalid KEYPAIRINFO status line"
<< QString::fromStdString(pair.second);
continue;
}
pivCard->setKeyAlgorithm(keyRef, info.algorithm);
} else {
qCWarning(KLEOPATRA_LOG) << "handle_piv_card(): Unexpected status line on "
<< QString::fromStdString(command) << ":" << QString::fromStdString(pair.first)
<< QString::fromStdString(pair.second);
}
}
}
}
ci.reset(pivCard);
}
......
......@@ -85,41 +85,45 @@ PIVCardWidget::PIVCardWidget(QWidget *parent):
// The keys
auto line1 = new QFrame();
line1->setFrameShape(QFrame::HLine);
grid->addWidget(line1, row++, 0, 1, 5);
grid->addWidget(line1, row++, 0, 1, 6);
grid->addWidget(new QLabel(QStringLiteral("<b>%1</b>").arg(i18n("Keys:"))), row++, 0);
mPIVAuthenticationKey = createKeyWidgets(PIVCard::pivAuthenticationKeyRef());
grid->addWidget(new QLabel(i18n("PIV authentication:")), row, 0);
grid->addWidget(mPIVAuthenticationKey.keyGrip, row, 1);
grid->addWidget(mPIVAuthenticationKey.generateButton, row, 2);
grid->addWidget(mPIVAuthenticationKey.writeCertificateButton, row, 3);
grid->addWidget(mPIVAuthenticationKey.keyAlgorithm, row, 2);
grid->addWidget(mPIVAuthenticationKey.generateButton, row, 3);
grid->addWidget(mPIVAuthenticationKey.writeCertificateButton, row, 4);
row++;
mCardAuthenticationKey = createKeyWidgets(PIVCard::cardAuthenticationKeyRef());
grid->addWidget(new QLabel(i18n("Card authentication:")), row, 0);
grid->addWidget(mCardAuthenticationKey.keyGrip, row, 1);
grid->addWidget(mCardAuthenticationKey.generateButton, row, 2);
grid->addWidget(mCardAuthenticationKey.writeCertificateButton, row, 3);
grid->addWidget(mCardAuthenticationKey.keyAlgorithm, row, 2);
grid->addWidget(mCardAuthenticationKey.generateButton, row, 3);
grid->addWidget(mCardAuthenticationKey.writeCertificateButton, row, 4);
row++;
mDigitalSignatureKey = createKeyWidgets(PIVCard::digitalSignatureKeyRef());
grid->addWidget(new QLabel(i18n("Digital signature:")), row, 0);
grid->addWidget(mDigitalSignatureKey.keyGrip, row, 1);
grid->addWidget(mDigitalSignatureKey.generateButton, row, 2);
grid->addWidget(mDigitalSignatureKey.writeCertificateButton, row, 3);
grid->addWidget(mDigitalSignatureKey.keyAlgorithm, row, 2);
grid->addWidget(mDigitalSignatureKey.generateButton, row, 3);
grid->addWidget(mDigitalSignatureKey.writeCertificateButton, row, 4);
row++;
mKeyManagementKey = createKeyWidgets(PIVCard::keyManagementKeyRef());
grid->addWidget(new QLabel(i18n("Key management:")), row, 0);
grid->addWidget(mKeyManagementKey.keyGrip, row, 1);
grid->addWidget(mKeyManagementKey.generateButton, row, 2);
grid->addWidget(mKeyManagementKey.writeCertificateButton, row, 3);
grid->addWidget(mKeyManagementKey.writeKeyButton, row, 4);
grid->addWidget(mKeyManagementKey.keyAlgorithm, row, 2);
grid->addWidget(mKeyManagementKey.generateButton, row, 3);
grid->addWidget(mKeyManagementKey.writeCertificateButton, row, 4);
grid->addWidget(mKeyManagementKey.writeKeyButton, row, 5);
row++;
auto line2 = new QFrame();
line2->setFrameShape(QFrame::HLine);
grid->addWidget(line2, row++, 0, 1, 5);
grid->addWidget(line2, row++, 0, 1, 6);
auto actionLayout = new QHBoxLayout;
......@@ -146,7 +150,7 @@ PIVCardWidget::PIVCardWidget(QWidget *parent):
}
actionLayout->addStretch(-1);
grid->addLayout(actionLayout, row++, 0, 1, 5);
grid->addLayout(actionLayout, row++, 0, 1, 6);
grid->setColumnStretch(4, -1);
}
......@@ -156,6 +160,7 @@ PIVCardWidget::KeyWidgets PIVCardWidget::createKeyWidgets(const std::string &key
KeyWidgets keyWidgets;
keyWidgets.keyGrip = new QLabel(this);
keyWidgets.keyGrip->setTextInteractionFlags(Qt::TextBrowserInteraction);
keyWidgets.keyAlgorithm = new QLabel(this);
keyWidgets.generateButton = new QPushButton(i18nc("@action:button", "Generate"), this);
keyWidgets.generateButton->setEnabled(false);
connect(keyWidgets.generateButton, &QPushButton::clicked, this, [this, keyRef] () { generateKey(keyRef); });
......@@ -197,11 +202,20 @@ void PIVCardWidget::setCard(const PIVCard *card)
void PIVCardWidget::updateKey(const std::string &keyRef, const PIVCard *card, const KeyWidgets &widgets)
{
const std::string grip = card->keyGrip(keyRef);
widgets.keyGrip->setText(grip.empty() ? i18nc("@info", "Slot empty") : QString::fromStdString(grip));
widgets.generateButton->setText(grip.empty() ? i18nc("@action:button", "Generate") : i18nc("@action:button", "Replace"));
widgets.generateButton->setToolTip(grip.empty() ?
i18nc("@info:tooltip %1 display name of a key", "Generate %1", PIVCard::keyDisplayName(keyRef)) :
i18nc("@info:tooltip %1 display name of a key", "Replace %1 with new key", PIVCard::keyDisplayName(keyRef)));
if (grip.empty()) {
widgets.keyGrip->setText(i18nc("@info", "Slot empty"));
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)));
} else {
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.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);
if (widgets.writeCertificateButton) {
widgets.writeCertificateButton->setEnabled(!grip.empty());
......
......@@ -36,6 +36,7 @@ public:
private:
struct KeyWidgets {
QLabel *keyGrip = nullptr;
QLabel *keyAlgorithm = nullptr;
QPushButton *generateButton = nullptr;
QPushButton *writeCertificateButton = nullptr;
QPushButton *writeKeyButton = nullptr;
......
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