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

Allow writing signing certificate to PIV card from card widget

* Add a button to the PIV card widget for writing the signing certificate
to the card that corresponds to the digitial signature key pair on the
card.

GnuPG-bug-id: 4794
parent dd9137ad
Pipeline #34286 passed with stage
in 31 minutes and 58 seconds
......@@ -22,6 +22,8 @@
#include "utils/writecertassuantransaction.h"
#include <Libkleo/KeyCache>
#include <KLocalizedString>
#include <QInputDialog>
......@@ -49,11 +51,14 @@ class KeyToCardCommand::Private : public Command::Private
public:
explicit Private(KeyToCardCommand *qq, KeyListController *c);
explicit Private(KeyToCardCommand *qq, const GpgME::Subkey &key, const std::string &serialno);
explicit Private(KeyToCardCommand *qq, const std::string &cardSlot, const std::string &serialno);
~Private();
private:
void start();
void startTransferToOpenPGPCard();
void startTransferToPIVCard();
void startKeyToPIVCard();
void startCertificateToPIVCard();
......@@ -96,6 +101,13 @@ KeyToCardCommand::Private::Private(KeyToCardCommand *qq,
{
}
KeyToCardCommand::Private::Private(KeyToCardCommand *qq, const std::string &cardSlot_, const std::string &serialno)
: Command::Private(qq, nullptr)
, mSerial(serialno)
, cardSlot(cardSlot_)
{
}
KeyToCardCommand::Private::~Private()
{
}
......@@ -104,6 +116,17 @@ void KeyToCardCommand::Private::start()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::Private::start()";
// for now we always use the first smart card
if (mSerial.empty()) {
const auto cards = SmartCard::ReaderStatus::instance()->getCards();
if (!cards.size() || cards[0]->serialNumber().empty()) {
error(i18n("Failed to find a smart card."));
finished();
return;
}
mSerial = cards[0]->serialNumber();
}
const auto card = SmartCard::ReaderStatus::instance()->getCard<Card>(mSerial);
if (!card) {
error(i18n("Failed to find the card with the serial number: %1", QString::fromStdString(mSerial)));
......@@ -113,32 +136,30 @@ void KeyToCardCommand::Private::start()
switch (card->appType()) {
case SmartCard::Card::OpenPGPApplication: {
if (mSubkey.parent().protocol() == GpgME::OpenPGP) {
startTransferToOpenPGPCard();
}
else {
error(i18n("Sorry! This key cannot be transferred to an OpenPGP card."));
finished();
}
startTransferToOpenPGPCard();
}
break;
case SmartCard::Card::PivApplication: {
if (mSubkey.parent().protocol() == GpgME::CMS) {
startTransferToPIVCard();
} else {
error(i18n("Sorry! This key cannot be transferred to a PIV card."));
finished();
}
startTransferToPIVCard();
}
break;
default: {
error(i18n("Sorry! Transferring keys to this card is not supported."));
finished();
return;
}
}
}
namespace {
static GpgME::Subkey getSubkeyToTransferToOpenPGPCard(const std::vector<Key> &keys)
{
if (keys.size() == 1 && keys.front().hasSecret()) {
return keys.front().subkey(0);
}
return GpgME::Subkey();
}
static int getOpenPGPCardSlotForKey(const GpgME::Subkey &subKey, QWidget *parent)
{
// Check if we need to ask the user for the slot
......@@ -185,6 +206,19 @@ void KeyToCardCommand::Private::startTransferToOpenPGPCard() {
return;
}
if (mSubkey.isNull()) {
mSubkey = getSubkeyToTransferToOpenPGPCard(keys());
}
if (mSubkey.isNull()) {
finished();
return;
}
if (mSubkey.parent().protocol() != GpgME::OpenPGP) {
error(i18n("Sorry! This key cannot be transferred to an OpenPGP card."));
finished();
return;
}
const auto slot = getOpenPGPCardSlotForKey(mSubkey, parentWidgetOrView());
if (slot < 1) {
finished();
......@@ -230,6 +264,29 @@ void KeyToCardCommand::Private::startTransferToOpenPGPCard() {
}
namespace {
static GpgME::Subkey getSubkeyToTransferToPIVCard(const std::vector<Key> &keys, const std::string &cardSlot, const std::shared_ptr<PIVCard> &card)
{
if (!cardSlot.empty()) {
if (cardSlot == PIVCard::digitalSignatureKeyRef()) {
// get signing certificate matching the key grip
const std::string cardKeygrip = card->keyGrip(cardSlot);
const auto subkey = KeyCache::instance()->findSubkeyByKeyGrip(cardKeygrip);
if (subkey.canSign() && subkey.parent().protocol() == GpgME::CMS) {
return subkey;
}
}
if (cardSlot == PIVCard::keyManagementKeyRef()) {
// get encryption certificate with secret subkey
}
return GpgME::Subkey();
}
if (keys.size() == 1) {
return keys.front().subkey(0);
}
return GpgME::Subkey();
}
static std::string getPIVCardSlotForKey(const GpgME::Subkey &subKey, QWidget *parent)
{
// Check if we need to ask the user for the slot
......@@ -277,6 +334,21 @@ void KeyToCardCommand::Private::startTransferToPIVCard()
return;
}
if (mSubkey.isNull()) {
mSubkey = getSubkeyToTransferToPIVCard(keys(), cardSlot, pivCard);
}
if (mSubkey.isNull()) {
if (!cardSlot.empty()) {
error(i18n("Sorry! No suitable certificate to write to this card slot was found."));
}
finished();
return;
}
if (mSubkey.parent().protocol() != GpgME::CMS) {
error(i18n("Sorry! This key cannot be transferred to a PIV card."));
finished();
return;
}
if (!mSubkey.canEncrypt() && !mSubkey.canSign()) {
error(i18n("Sorry! Only encryption keys and signing keys can be transferred to a PIV card."));
finished();
......@@ -430,6 +502,11 @@ KeyToCardCommand::KeyToCardCommand(const GpgME::Subkey &key, const std::string &
{
}
KeyToCardCommand::KeyToCardCommand(const std::string& cardSlot, const std::string &serialno)
: Command(new Private(this, cardSlot, serialno))
{
}
KeyToCardCommand::~KeyToCardCommand()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::~KeyToCardCommand()";
......@@ -527,27 +604,6 @@ void KeyToCardCommand::doStart()
{
qCDebug(KLEOPATRA_LOG) << "KeyToCardCommand::doStart()";
if (d->mSubkey.isNull()) {
const std::vector<Key> keys = d->keys();
if (keys.size() != 1 ||
!keys.front().hasSecret() ||
keys.front().subkey(0).isNull()) {
d->finished();
return;
}
d->mSubkey = keys.front().subkey(0);
}
if (d->mSerial.empty()) {
const auto cards = SmartCard::ReaderStatus::instance()->getCards();
if (!cards.size() || cards[0]->serialNumber().empty()) {
d->error(i18n("Failed to find a smart card."));
d->finished();
return;
}
d->mSerial = cards[0]->serialNumber();
}
d->start();
}
......
......@@ -28,6 +28,7 @@ public:
explicit KeyToCardCommand(QAbstractItemView *view, KeyListController *parent);
explicit KeyToCardCommand(const GpgME::Key &key);
KeyToCardCommand(const GpgME::Subkey &key, const std::string &serialno);
KeyToCardCommand(const std::string& cardSlot, const std::string &serialno);
~KeyToCardCommand() override;
/* reimp */ static Restrictions restrictions()
......
......@@ -10,6 +10,7 @@
#include "pivcardwidget.h"
#include "commands/changepincommand.h"
#include "commands/keytocardcommand.h"
#include "commands/pivgeneratecardkeycommand.h"
#include "commands/setpivcardapplicationadministrationkeycommand.h"
......@@ -63,6 +64,7 @@ PIVCardWidget::PIVCardWidget(QWidget *parent):
mGeneratePIVAuthenticationKeyBtn(new QPushButton(this)),
mGenerateCardAuthenticationKeyBtn(new QPushButton(this)),
mGenerateDigitalSignatureKeyBtn(new QPushButton(this)),
mWriteDigitalSignatureKeyBtn(new QPushButton(this)),
mGenerateKeyManagementKeyBtn(new QPushButton(this))
{
auto grid = new QGridLayout;
......@@ -107,7 +109,7 @@ PIVCardWidget::PIVCardWidget(QWidget *parent):
grid->addWidget(mCardAuthenticationKey, row, 1);
mCardAuthenticationKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
mGenerateCardAuthenticationKeyBtn->setText(i18n("Generate"));
mGeneratePIVAuthenticationKeyBtn->setEnabled(false);
mGenerateCardAuthenticationKeyBtn->setEnabled(false);
grid->addWidget(mGenerateCardAuthenticationKeyBtn, row, 2);
connect(mGenerateCardAuthenticationKeyBtn, &QPushButton::clicked, this, &PIVCardWidget::generateCardAuthenticationKey);
row++;
......@@ -116,16 +118,21 @@ PIVCardWidget::PIVCardWidget(QWidget *parent):
grid->addWidget(mDigitalSignatureKey, row, 1);
mDigitalSignatureKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
mGenerateDigitalSignatureKeyBtn->setText(i18n("Generate"));
mGeneratePIVAuthenticationKeyBtn->setEnabled(false);
mGenerateDigitalSignatureKeyBtn->setEnabled(false);
grid->addWidget(mGenerateDigitalSignatureKeyBtn, row, 2);
connect(mGenerateDigitalSignatureKeyBtn, &QPushButton::clicked, this, &PIVCardWidget::generateDigitalSignatureKey);
mWriteDigitalSignatureKeyBtn->setText(i18n("Write Certificate"));
mWriteDigitalSignatureKeyBtn->setToolTip(i18n("Write the certificate corresponding to this key to the card"));
mWriteDigitalSignatureKeyBtn->setEnabled(false);
grid->addWidget(mWriteDigitalSignatureKeyBtn, row, 3);
connect(mWriteDigitalSignatureKeyBtn, &QPushButton::clicked, this, [this] () { writeKeyToCard(PIVCard::digitalSignatureKeyRef()); });
row++;
grid->addWidget(new QLabel(i18n("Key management:")), row, 0);
grid->addWidget(mKeyManagementKey, row, 1);
mKeyManagementKey->setTextInteractionFlags(Qt::TextBrowserInteraction);
mGenerateKeyManagementKeyBtn->setText(i18n("Generate"));
mGeneratePIVAuthenticationKeyBtn->setEnabled(false);
mGenerateKeyManagementKeyBtn->setEnabled(false);
grid->addWidget(mGenerateKeyManagementKeyBtn, row, 2);
connect(mGenerateKeyManagementKeyBtn, &QPushButton::clicked, this, &PIVCardWidget::generateKeyManagementKey);
row++;
......@@ -179,21 +186,24 @@ void PIVCardWidget::setCard(const PIVCard *card)
mSerialNumber->setText(QString::fromStdString(card->serialNumber()));
}
updateKey(PIVCard::pivAuthenticationKeyRef(), card, mPIVAuthenticationKey, mGeneratePIVAuthenticationKeyBtn);
updateKey(PIVCard::cardAuthenticationKeyRef(), card, mCardAuthenticationKey, mGenerateCardAuthenticationKeyBtn);
updateKey(PIVCard::digitalSignatureKeyRef(), card, mDigitalSignatureKey, mGenerateDigitalSignatureKeyBtn);
updateKey(PIVCard::keyManagementKeyRef(), card, mKeyManagementKey, mGenerateKeyManagementKeyBtn);
updateKey(PIVCard::pivAuthenticationKeyRef(), card, mPIVAuthenticationKey, mGeneratePIVAuthenticationKeyBtn, nullptr);
updateKey(PIVCard::cardAuthenticationKeyRef(), card, mCardAuthenticationKey, mGenerateCardAuthenticationKeyBtn, nullptr);
updateKey(PIVCard::digitalSignatureKeyRef(), card, mDigitalSignatureKey, mGenerateDigitalSignatureKeyBtn, mWriteDigitalSignatureKeyBtn);
updateKey(PIVCard::keyManagementKeyRef(), card, mKeyManagementKey, mGenerateKeyManagementKeyBtn, nullptr);
}
void PIVCardWidget::updateKey(const std::string &keyRef, const PIVCard *card, QLabel *label, QPushButton *button)
void PIVCardWidget::updateKey(const std::string &keyRef, const PIVCard *card, QLabel *label, QPushButton *generateButton, QPushButton *writeButton)
{
const std::string grip = card->keyGrip(keyRef);
label->setText(grip.empty() ? i18n("Slot empty") : QString::fromStdString(grip));
button->setText(grip.empty() ? i18n("Generate") : i18n("Replace"));
button->setToolTip(grip.empty() ?
generateButton->setText(grip.empty() ? i18n("Generate") : i18n("Replace"));
generateButton->setToolTip(grip.empty() ?
i18nc("Placeholder is the display name of a key", "Generate %1", PIVCard::keyDisplayName(keyRef)) :
i18nc("Placeholder is the display name of a key", "Replace %1 with new key", PIVCard::keyDisplayName(keyRef)));
button->setEnabled(true);
generateButton->setEnabled(true);
if (writeButton) {
writeButton->setEnabled(!grip.empty());
}
}
void PIVCardWidget::generateKey(const std::string &keyref)
......@@ -208,6 +218,18 @@ void PIVCardWidget::generateKey(const std::string &keyref)
cmd->start();
}
void PIVCardWidget::writeKeyToCard(const std::string &keyref)
{
auto cmd = new KeyToCardCommand(keyref, mCardSerialNumber);
this->setEnabled(false);
connect(cmd, &KeyToCardCommand::finished,
this, [this]() {
this->setEnabled(true);
});
cmd->setParentWidget(this);
cmd->start();
}
void PIVCardWidget::generatePIVAuthenticationKey()
{
generateKey(PIVCard::pivAuthenticationKeyRef());
......
......@@ -34,8 +34,9 @@ public:
void setCard(const SmartCard::PIVCard* card);
private:
void updateKey(const std::string &keyRef, const SmartCard::PIVCard *card, QLabel *label, QPushButton *button);
void updateKey(const std::string &keyRef, const SmartCard::PIVCard *card, QLabel *label, QPushButton *generateButton, QPushButton *writeButton);
void generateKey(const std::string &keyref);
void writeKeyToCard(const std::string &keyref);
void changePin(const std::string &keyRef);
void setAdminKey();
......@@ -56,6 +57,7 @@ private:
QPushButton *mGeneratePIVAuthenticationKeyBtn = nullptr,
*mGenerateCardAuthenticationKeyBtn = nullptr,
*mGenerateDigitalSignatureKeyBtn = nullptr,
*mWriteDigitalSignatureKeyBtn = nullptr,
*mGenerateKeyManagementKeyBtn = nullptr;
};
} // 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