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

Add support for replacing existing keys on PIV smartcards

CardCommand:
* Add serialNumber; this allows CardCommand to retrieve the card
  it is operating on

PIVGenerateCardKeyCommand:
* Add possibility to generate keys for non-empty key slots on the card
  overwriting the existing keys; ask the user for approval

PIVCard:
* Replace specific *KeyGrip() methods with keyGrip(keyRef)
* Add *KeyRef() methods for retrieving internal keyRef for the four
  main PIV keys

ReaderStatus:
* Add getCard() for retrieving a specific card by serial number

PIVCardWidget:
* Store card serial number
* Remove unused mCardIsEmpty

GnuPG-bug-id: 4794
parent a7a7d1ca
Pipeline #31631 passed with stage
in 42 minutes and 13 seconds
......@@ -15,9 +15,10 @@
using namespace Kleo;
CardCommand::Private::Private(CardCommand *qq, QWidget *parent)
CardCommand::Private::Private(CardCommand *qq, const std::string &serialNumber, QWidget *parent)
: q(qq),
autoDelete(true),
serialNumber_(serialNumber),
parentWidget_(parent)
{
}
......@@ -26,8 +27,8 @@ CardCommand::Private::~Private()
{
}
CardCommand::CardCommand(QWidget *parent)
: QObject(parent), d(new Private(this, parent))
CardCommand::CardCommand(const std::string &serialNumber, QWidget *parent)
: QObject(parent), d(new Private(this, serialNumber, parent))
{
}
......
......@@ -24,7 +24,7 @@ class CardCommand : public QObject
{
Q_OBJECT
public:
explicit CardCommand(QWidget *parent);
explicit CardCommand(const std::string &serialNumber, QWidget *parent);
~CardCommand() override;
void setAutoDelete(bool on);
......
......@@ -23,9 +23,14 @@ class Kleo::CardCommand::Private
protected:
CardCommand *const q;
public:
explicit Private(CardCommand *qq, QWidget *parent);
explicit Private(CardCommand *qq, const std::string &serialNumber, QWidget *parent);
virtual ~Private();
std::string serialNumber() const
{
return serialNumber_;
}
QWidget *parentWidget() const
{
return parentWidget_;
......@@ -51,6 +56,7 @@ public:
private:
bool autoDelete : 1;
std::string serialNumber_;
QPointer<QWidget> parentWidget_;
};
......
......@@ -11,6 +11,7 @@
#include "cardcommand_p.h"
#include "smartcard/pivcard.h"
#include "smartcard/readerstatus.h"
#include <gpgme++/error.h>
......@@ -32,7 +33,7 @@ class PIVGenerateCardKeyCommand::Private : public CardCommand::Private
return static_cast<PIVGenerateCardKeyCommand *>(q);
}
public:
explicit Private(PIVGenerateCardKeyCommand *qq, QWidget *p);
explicit Private(PIVGenerateCardKeyCommand *qq, const std::string &serialNumber, QWidget *p);
~Private();
void init();
......@@ -46,7 +47,8 @@ private:
void generateKey();
private:
QByteArray keyref;
std::string keyref;
bool overwriteExistingKey = false;
};
PIVGenerateCardKeyCommand::Private *PIVGenerateCardKeyCommand::d_func()
......@@ -61,8 +63,8 @@ const PIVGenerateCardKeyCommand::Private *PIVGenerateCardKeyCommand::d_func() co
#define d d_func()
#define q q_func()
PIVGenerateCardKeyCommand::Private::Private(PIVGenerateCardKeyCommand *qq, QWidget *p)
: CardCommand::Private(qq, p)
PIVGenerateCardKeyCommand::Private::Private(PIVGenerateCardKeyCommand *qq, const std::string &serialNumber, QWidget *p)
: CardCommand::Private(qq, serialNumber, p)
{
}
......@@ -71,8 +73,8 @@ PIVGenerateCardKeyCommand::Private::~Private()
qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::Private::~Private()";
}
PIVGenerateCardKeyCommand::PIVGenerateCardKeyCommand(QWidget *p)
: CardCommand(new Private(this, p))
PIVGenerateCardKeyCommand::PIVGenerateCardKeyCommand(const std::string &serialNumber, QWidget *p)
: CardCommand(new Private(this, serialNumber, p))
{
d->init();
}
......@@ -86,7 +88,7 @@ PIVGenerateCardKeyCommand::~PIVGenerateCardKeyCommand()
qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::~PIVGenerateCardKeyCommand()";
}
void PIVGenerateCardKeyCommand::setKeyRef(const QByteArray &keyref)
void PIVGenerateCardKeyCommand::setKeyRef(const std::string &keyref)
{
d->keyref = keyref;
}
......@@ -95,6 +97,33 @@ void PIVGenerateCardKeyCommand::doStart()
{
qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::doStart()";
// check if key exists
auto pivCard = ReaderStatus::instance()->getCard<PIVCard>(d->serialNumber());
if (!pivCard) {
d->error(i18n("Failed to find the card with the serial number: %1", QString::fromStdString(d->serialNumber())));
d->finished();
return;
}
auto existingKey = pivCard->keyGrip(d->keyref);
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 == PIVCard::keyManagementKeyRef() ?
i18n("It will no longer be possible to decrypt past communication encrypted for the existing key.") :
QString());
const auto choice = KMessageBox::warningContinueCancel(d->parentWidget(), 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->generateKey();
}
......@@ -116,18 +145,26 @@ void PIVGenerateCardKeyCommand::Private::slotAuthenticateResult(const Error &err
error(i18nc("@info", "Authenticating to the card failed: %1", QString::fromLatin1(err.asString())),
i18nc("@title", "Error"));
finished();
} else if (err.isCanceled()) {
return;
}
if (err.isCanceled()) {
finished();
} else {
generateKey();
return;
}
generateKey();
}
void PIVGenerateCardKeyCommand::Private::generateKey()
{
qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::generateKey()";
ReaderStatus::mutableInstance()->startSimpleTransaction("SCD GENKEY -- " + keyref, q, "slotGenerateKeyResult");
QByteArrayList command;
command << "SCD GENKEY";
if (overwriteExistingKey) {
command << "--force";
}
command << "--" << QByteArray::fromStdString(keyref);
ReaderStatus::mutableInstance()->startSimpleTransaction(command.join(' '), q, "slotGenerateKeyResult");
}
void PIVGenerateCardKeyCommand::Private::slotGenerateKeyResult(const GpgME::Error& err)
......
......@@ -25,10 +25,10 @@ class PIVGenerateCardKeyCommand : public CardCommand
{
Q_OBJECT
public:
explicit PIVGenerateCardKeyCommand(QWidget *parent);
explicit PIVGenerateCardKeyCommand(const std::string &serialNumber, QWidget *parent);
~PIVGenerateCardKeyCommand() override;
void setKeyRef(const QByteArray &keyref);
void setKeyRef(const std::string &keyref);
private:
void doStart() override;
......
......@@ -24,24 +24,33 @@ PIVCard::PIVCard(const std::string &serialno): PIVCard()
setSerialNumber(serialno);
}
std::string PIVCard::pivAuthenticationKeyGrip() const
// static
std::string PIVCard::pivAuthenticationKeyRef()
{
return mMetaInfo.value("PIV-AUTH-KEYPAIRINFO");
return std::string("PIV.9A");
}
std::string PIVCard::cardAuthenticationKeyGrip() const
// static
std::string PIVCard::cardAuthenticationKeyRef()
{
return mMetaInfo.value("CARD-AUTH-KEYPAIRINFO");
return std::string("PIV.9E");
}
std::string PIVCard::digitalSignatureKeyGrip() const
// static
std::string PIVCard::digitalSignatureKeyRef()
{
return mMetaInfo.value("SIG-KEYPAIRINFO");
return std::string("PIV.9C");
}
std::string PIVCard::keyManagementKeyGrip() const
// static
std::string PIVCard::keyManagementKeyRef()
{
return mMetaInfo.value("ENC-KEYPAIRINFO");
return std::string("PIV.9D");
}
std::string PIVCard::keyGrip(const std::string& keyRef) const
{
return mMetaInfo.value("KEYPAIRINFO-" + keyRef);
}
namespace {
......@@ -68,19 +77,9 @@ void PIVCard::setCardInfo(const std::vector< std::pair<std::string, std::string>
continue;
}
const auto grip = values[0].toStdString();
const auto slotId = values[1];
const auto usage = values[2];
if (slotId == QLatin1String("PIV.9A")) {
mMetaInfo.insert(std::string("PIV-AUTH-") + pair.first, grip);
} else if (slotId == QLatin1String("PIV.9E")) {
mMetaInfo.insert(std::string("CARD-AUTH-") + pair.first, grip);
} else if (slotId == QLatin1String("PIV.9C")) {
mMetaInfo.insert(std::string("SIG-") + pair.first, grip);
} else if (slotId == QLatin1String("PIV.9D")) {
mMetaInfo.insert(std::string("ENC-") + pair.first, grip);
} else {
qCDebug(KLEOPATRA_LOG) << "Unhandled keyslot";
}
const auto keyRef = values[1].toStdString();
//const auto usage = values[2];
mMetaInfo.insert("KEYPAIRINFO-" + keyRef, grip);
} else {
mMetaInfo.insert(pair.first, pair.second);
}
......@@ -105,8 +104,5 @@ bool PIVCard::operator == (const Card& rhs) const
}
return Card::operator ==(rhs)
&& pivAuthenticationKeyGrip() == other->pivAuthenticationKeyGrip()
&& cardAuthenticationKeyGrip() == other->cardAuthenticationKeyGrip()
&& digitalSignatureKeyGrip() == other->digitalSignatureKeyGrip()
&& keyManagementKeyGrip() == other->keyManagementKeyGrip();
&& mMetaInfo == other->mMetaInfo;
}
......@@ -24,10 +24,12 @@ public:
PIVCard ();
PIVCard (const std::string &serialno);
std::string pivAuthenticationKeyGrip() const;
std::string cardAuthenticationKeyGrip() const;
std::string digitalSignatureKeyGrip() const;
std::string keyManagementKeyGrip() const;
static std::string pivAuthenticationKeyRef();
static std::string cardAuthenticationKeyRef();
static std::string digitalSignatureKeyRef();
static std::string keyManagementKeyRef();
std::string keyGrip(const std::string &keyRef) const;
void setCardInfo (const std::vector< std::pair<std::string, std::string> > &infos);
......
......@@ -18,6 +18,8 @@
#include <vector>
#include <memory>
#include "kleopatra_debug.h"
namespace Kleo
{
namespace SmartCard
......@@ -43,6 +45,19 @@ public:
std::vector<std::shared_ptr<Card> > getCards() const;
template <typename T>
std::shared_ptr<T> getCard(const std::string &serialNumber) const
{
for (const auto &card: getCards()) {
if (card->serialNumber() == serialNumber) {
qCDebug(KLEOPATRA_LOG) << "ReaderStatus::getCard() - Found card with serial number" << QString::fromStdString(serialNumber);
return std::dynamic_pointer_cast<T>(card);
}
}
qCDebug(KLEOPATRA_LOG) << "ReaderStatus::getCard() - Did not find card with serial number" << QString::fromStdString(serialNumber);
return std::shared_ptr<T>();
}
public Q_SLOTS:
void updateStatus();
void startMonitoring();
......
......@@ -48,6 +48,18 @@ static QString formatVersion(int value)
}
return QString::number(d);
}
static QString keyDisplayName(std::string keyRef)
{
static const QMap<std::string, QString> displayNames = {
{ PIVCard::pivAuthenticationKeyRef(), i18n("PIV Authentication Key") },
{ PIVCard::cardAuthenticationKeyRef(), i18n("Card Authentication Key") },
{ PIVCard::digitalSignatureKeyRef(), i18n("Digital Signature Key") },
{ PIVCard::keyManagementKeyRef(), i18n("Key Management Key") },
};
return displayNames.value(keyRef);
}
} // Namespace
PIVCardWidget::PIVCardWidget(QWidget *parent):
......@@ -61,8 +73,7 @@ PIVCardWidget::PIVCardWidget(QWidget *parent):
mGeneratePIVAuthenticationKeyBtn(new QPushButton(this)),
mGenerateCardAuthenticationKeyBtn(new QPushButton(this)),
mGenerateDigitalSignatureKeyBtn(new QPushButton(this)),
mGenerateKeyManagementKeyBtn(new QPushButton(this)),
mCardIsEmpty(false)
mGenerateKeyManagementKeyBtn(new QPushButton(this))
{
auto grid = new QGridLayout;
int row = 0;
......@@ -97,7 +108,7 @@ PIVCardWidget::PIVCardWidget(QWidget *parent):
grid->addWidget(mPIVAuthenticationKey, row, 1);
mPIVAuthenticationKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
mGeneratePIVAuthenticationKeyBtn->setText(i18n("Generate"));
mGeneratePIVAuthenticationKeyBtn->setToolTip(i18n("Generate PIV Authentication Key"));
mGeneratePIVAuthenticationKeyBtn->setEnabled(false);
grid->addWidget(mGeneratePIVAuthenticationKeyBtn, row, 2);
connect(mGeneratePIVAuthenticationKeyBtn, &QPushButton::clicked, this, &PIVCardWidget::generatePIVAuthenticationKey);
row++;
......@@ -106,7 +117,7 @@ PIVCardWidget::PIVCardWidget(QWidget *parent):
grid->addWidget(mCardAuthenticationKey, row, 1);
mCardAuthenticationKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
mGenerateCardAuthenticationKeyBtn->setText(i18n("Generate"));
mGenerateCardAuthenticationKeyBtn->setToolTip(i18n("Generate Card Authentication Key"));
mGeneratePIVAuthenticationKeyBtn->setEnabled(false);
grid->addWidget(mGenerateCardAuthenticationKeyBtn, row, 2);
connect(mGenerateCardAuthenticationKeyBtn, &QPushButton::clicked, this, &PIVCardWidget::generateCardAuthenticationKey);
row++;
......@@ -115,7 +126,7 @@ PIVCardWidget::PIVCardWidget(QWidget *parent):
grid->addWidget(mDigitalSignatureKey, row, 1);
mDigitalSignatureKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
mGenerateDigitalSignatureKeyBtn->setText(i18n("Generate"));
mGenerateDigitalSignatureKeyBtn->setToolTip(i18n("Generate Digital Signature Key"));
mGeneratePIVAuthenticationKeyBtn->setEnabled(false);
grid->addWidget(mGenerateDigitalSignatureKeyBtn, row, 2);
connect(mGenerateDigitalSignatureKeyBtn, &QPushButton::clicked, this, &PIVCardWidget::generateDigitalSignatureKey);
row++;
......@@ -124,7 +135,7 @@ PIVCardWidget::PIVCardWidget(QWidget *parent):
grid->addWidget(mKeyManagementKey, row, 1);
mKeyManagementKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
mGenerateKeyManagementKeyBtn->setText(i18n("Generate"));
mGenerateKeyManagementKeyBtn->setToolTip(i18n("Generate Key Management Key"));
mGeneratePIVAuthenticationKeyBtn->setEnabled(false);
grid->addWidget(mGenerateKeyManagementKeyBtn, row, 2);
connect(mGenerateKeyManagementKeyBtn, &QPushButton::clicked, this, &PIVCardWidget::generateKeyManagementKey);
row++;
......@@ -142,6 +153,7 @@ PIVCardWidget::~PIVCardWidget()
void PIVCardWidget::setCard(const PIVCard *card)
{
mCardSerialNumber = card->serialNumber();
mVersionLabel->setText(i18nc("Placeholder is a version number", "PIV v%1 card", formatVersion(card->appVersion())));
if (card->displaySerialNumber() != card->serialNumber()) {
......@@ -151,25 +163,26 @@ void PIVCardWidget::setCard(const PIVCard *card)
mSerialNumber->setText(QString::fromStdString(card->serialNumber()));
}
updateKey(mPIVAuthenticationKey, mGeneratePIVAuthenticationKeyBtn, card->pivAuthenticationKeyGrip());
updateKey(mCardAuthenticationKey, mGenerateCardAuthenticationKeyBtn, card->cardAuthenticationKeyGrip());
updateKey(mDigitalSignatureKey, mGenerateDigitalSignatureKeyBtn, card->digitalSignatureKeyGrip());
updateKey(mKeyManagementKey, mGenerateKeyManagementKeyBtn, card->keyManagementKeyGrip());
mCardIsEmpty = card->pivAuthenticationKeyGrip().empty()
&& card->cardAuthenticationKeyGrip().empty()
&& card->digitalSignatureKeyGrip().empty()
&& card->keyManagementKeyGrip().empty();
updateKey(PIVCard::pivAuthenticationKeyRef(), card, mPIVAuthenticationKey, mGeneratePIVAuthenticationKeyBtn);
updateKey(PIVCard::cardAuthenticationKeyRef(), card, mCardAuthenticationKey, mGenerateCardAuthenticationKeyBtn);
updateKey(PIVCard::digitalSignatureKeyRef(), card, mDigitalSignatureKey, mGenerateDigitalSignatureKeyBtn);
updateKey(PIVCard::keyManagementKeyRef(), card, mKeyManagementKey, mGenerateKeyManagementKeyBtn);
}
void PIVCardWidget::updateKey(QLabel *label, QPushButton *button, const std::string &grip)
void PIVCardWidget::updateKey(const std::string &keyRef, const PIVCard *card, QLabel *label, QPushButton *button)
{
const std::string grip = card->keyGrip(keyRef);
label->setText(grip.empty() ? i18n("Slot empty") : QString::fromStdString(grip));
button->setEnabled(grip.empty());
button->setText(grip.empty() ? i18n("Generate") : i18n("Replace"));
button->setToolTip(grip.empty() ?
i18nc("Placeholder is the display name of a key", "Generate %1", keyDisplayName(keyRef)) :
i18nc("Placeholder is the display name of a key", "Replace %1 with new key", keyDisplayName(keyRef)));
button->setEnabled(true);
}
void PIVCardWidget::generateKey(const QByteArray &keyref)
void PIVCardWidget::generateKey(const std::string &keyref)
{
auto cmd = new PIVGenerateCardKeyCommand(this);
auto cmd = new PIVGenerateCardKeyCommand(mCardSerialNumber, this);
this->setEnabled(false);
connect(cmd, &PIVGenerateCardKeyCommand::finished,
this, [this]() {
......@@ -181,20 +194,20 @@ void PIVCardWidget::generateKey(const QByteArray &keyref)
void PIVCardWidget::generatePIVAuthenticationKey()
{
generateKey("PIV.9A");
generateKey(PIVCard::pivAuthenticationKeyRef());
}
void PIVCardWidget::generateCardAuthenticationKey()
{
generateKey("PIV.9E");
generateKey(PIVCard::cardAuthenticationKeyRef());
}
void PIVCardWidget::generateDigitalSignatureKey()
{
generateKey("PIV.9C");
generateKey(PIVCard::digitalSignatureKeyRef());
}
void PIVCardWidget::generateKeyManagementKey()
{
generateKey("PIV.9D");
generateKey(PIVCard::keyManagementKeyRef());
}
......@@ -34,8 +34,8 @@ public:
void setCard(const SmartCard::PIVCard* card);
private:
void updateKey(QLabel *label, QPushButton *button, const std::string &fpr);
void generateKey(const QByteArray &keyref);
void updateKey(const std::string &keyRef, const SmartCard::PIVCard *card, QLabel *label, QPushButton *button);
void generateKey(const std::string &keyref);
private Q_SLOTS:
void generatePIVAuthenticationKey();
......@@ -44,6 +44,7 @@ private Q_SLOTS:
void generateKeyManagementKey();
private:
std::string mCardSerialNumber;
QLabel *mSerialNumber = nullptr,
*mVersionLabel = nullptr,
*mPIVAuthenticationKey = nullptr,
......@@ -54,7 +55,6 @@ private:
*mGenerateCardAuthenticationKeyBtn = nullptr,
*mGenerateDigitalSignatureKeyBtn = nullptr,
*mGenerateKeyManagementKeyBtn = nullptr;
bool mCardIsEmpty = false;
};
} // namespace Kleo
......
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