Commit 48b58502 authored by Ingo Klöcker's avatar Ingo Klöcker

Add selection of key algorithm to key generation on PIV cards

PIVGenerateCardKeyCommand:
* Use GenCardKeyDialog for selecting algorithm

GenCardKeyDialog:
* Allow customization of key attributes to request from user

PIVCard:
* Add supportedAlgorithms() returning the allowed algorithms for the
  given key

GnuPG-bug-id: 4794
parent 0db0273b
Pipeline #31663 passed with stage
in 13 minutes and 41 seconds
/* commands/pivgeneratecardkeycommand.h
/* commands/pivgeneratecardkeycommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2020 g10 Code GmbH
......@@ -14,10 +14,12 @@
#include "smartcard/pivcard.h"
#include "smartcard/readerstatus.h"
#include <gpgme++/error.h>
#include "dialogs/gencardkeydialog.h"
#include <KLocalizedString>
#include <gpgme++/error.h>
#include "kleopatra_debug.h"
using namespace Kleo;
......@@ -39,16 +41,21 @@ public:
void init();
private:
void slotDialogAccepted();
void slotDialogRejected();
void slotAuthenticateResult(const Error &err);
void slotGenerateKeyResult(const Error &err);
private:
void authenticate();
void generateKey();
void ensureDialogCreated();
private:
std::string keyref;
std::string keyRef;
bool overwriteExistingKey = false;
std::string algorithm;
QPointer<GenCardKeyDialog> dialog;
};
PIVGenerateCardKeyCommand::Private *PIVGenerateCardKeyCommand::d_func()
......@@ -65,6 +72,7 @@ const PIVGenerateCardKeyCommand::Private *PIVGenerateCardKeyCommand::d_func() co
PIVGenerateCardKeyCommand::Private::Private(PIVGenerateCardKeyCommand *qq, const std::string &serialNumber, QWidget *p)
: CardCommand::Private(qq, serialNumber, p)
, dialog()
{
}
......@@ -88,9 +96,9 @@ PIVGenerateCardKeyCommand::~PIVGenerateCardKeyCommand()
qCDebug(KLEOPATRA_LOG) << "PIVGenerateCardKeyCommand::~PIVGenerateCardKeyCommand()";
}
void PIVGenerateCardKeyCommand::setKeyRef(const std::string &keyref)
void PIVGenerateCardKeyCommand::setKeyRef(const std::string &keyRef)
{
d->keyref = keyref;
d->keyRef = keyRef;
}
void PIVGenerateCardKeyCommand::doStart()
......@@ -105,13 +113,13 @@ void PIVGenerateCardKeyCommand::doStart()
return;
}
auto existingKey = pivCard->keyGrip(d->keyref);
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() ?
(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,
......@@ -124,7 +132,9 @@ void PIVGenerateCardKeyCommand::doStart()
d->overwriteExistingKey = true;
}
d->generateKey();
d->ensureDialogCreated();
Q_ASSERT(d->dialog);
d->dialog->show();
}
void PIVGenerateCardKeyCommand::Private::authenticate()
......@@ -163,7 +173,10 @@ void PIVGenerateCardKeyCommand::Private::generateKey()
if (overwriteExistingKey) {
command << "--force";
}
command << "--" << QByteArray::fromStdString(keyref);
if (!algorithm.empty()) {
command << "--algo=" + QByteArray::fromStdString(algorithm);
}
command << "--" << QByteArray::fromStdString(keyRef);
ReaderStatus::mutableInstance()->startSimpleTransaction(command.join(' '), q, "slotGenerateKeyResult");
}
......@@ -186,6 +199,33 @@ void PIVGenerateCardKeyCommand::Private::slotGenerateKeyResult(const GpgME::Erro
finished();
}
void PIVGenerateCardKeyCommand::Private::slotDialogAccepted()
{
algorithm = dialog->getKeyParams().algorithm;
// assume that we are already authenticated to the card
generateKey();
}
void PIVGenerateCardKeyCommand::Private::slotDialogRejected()
{
finished();
}
void PIVGenerateCardKeyCommand::Private::ensureDialogCreated()
{
if (dialog) {
return;
}
dialog = new GenCardKeyDialog(GenCardKeyDialog::KeyAlgorithm, parentWidget());
dialog->setAttribute(Qt::WA_DeleteOnClose);
dialog->setSupportedAlgorithms(PIVCard::supportedAlgorithms(keyRef), "rsa2048");
connect(dialog, SIGNAL(accepted()), q, SLOT(slotDialogAccepted()));
connect(dialog, SIGNAL(rejected()), q, SLOT(slotDialogRejected()));
}
#undef d
#undef q
......
......@@ -37,6 +37,8 @@ private:
class Private;
inline Private *d_func();
inline const Private *d_func() const;
Q_PRIVATE_SLOT(d_func(), void slotDialogAccepted())
Q_PRIVATE_SLOT(d_func(), void slotDialogRejected())
Q_PRIVATE_SLOT(d_func(), void slotGenerateKeyResult(GpgME::Error))
Q_PRIVATE_SLOT(d_func(), void slotAuthenticateResult(GpgME::Error))
};
......
......@@ -23,15 +23,61 @@
#include <KLocalizedString>
#include <KColorScheme>
#include "kleopatra_debug.h"
using namespace Kleo;
class GenCardKeyDialog::Private
{
public:
Private(GenCardKeyDialog *qq): q(qq)
Private(GenCardKeyDialog *qq, KeyAttributes requiredAttributes): q(qq),
mOkButton(nullptr),
mNameEdit(nullptr),
mEmailEdit(nullptr),
mInvalidEmailLabel(nullptr),
mAlgorithmCombo(nullptr),
mBackupCheckBox(nullptr)
{
auto *vBox = new QVBoxLayout(q);
auto *grid = new QGridLayout;
int row = 0;
const KEMailSettings e;
if (requiredAttributes & KeyOwnerName) {
auto nameLabel = new QLabel(i18n("Name:"));
mNameEdit = new QLineEdit(e.getSetting(KEMailSettings::RealName));
grid->addWidget(nameLabel, row, 0);
grid->addWidget(mNameEdit, row++, 1);
}
if (requiredAttributes & KeyOwnerEmail) {
auto mailLabel = new QLabel(i18n("EMail:"));
mEmailEdit = new QLineEdit(e.getSetting(KEMailSettings::EmailAddress));
connect(mEmailEdit, &QLineEdit::textChanged, q, [this]() {checkAcceptable();});
mInvalidEmailLabel = new QLabel(QStringLiteral("<font size='small' color='%1'>%2</font>").arg(
KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::NegativeText).color().name(), i18n("Invalid EMail")));
grid->addWidget(mailLabel, row, 0);
grid->addWidget(mEmailEdit, row++, 1);
grid->addWidget(mInvalidEmailLabel, row++, 1);
}
if (requiredAttributes & KeyAlgorithm) {
auto algorithmLabel = new QLabel(i18n("Algorithm:"));
mAlgorithmCombo = new QComboBox;
grid->addWidget(algorithmLabel, row, 0);
grid->addWidget(mAlgorithmCombo, row++, 1);
}
if (requiredAttributes & LocalKeyBackup) {
mBackupCheckBox = new QCheckBox(i18n("Backup encryption key"));
mBackupCheckBox->setToolTip(i18n("Backup the encryption key in a file.") + QStringLiteral("<br/>") +
i18n("You will be asked for a passphrase to protect that file during key generation."));
mBackupCheckBox->setChecked(true);
grid->addWidget(mBackupCheckBox, row++, 0, 1, 2);
}
vBox->addLayout(grid);
auto bbox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, qq);
......@@ -45,39 +91,6 @@ public:
vBox->addWidget(bbox);
const KEMailSettings e;
mNameEdit = new QLineEdit(e.getSetting(KEMailSettings::RealName));
mEmailEdit = new QLineEdit(e.getSetting(KEMailSettings::EmailAddress));
connect(mEmailEdit, &QLineEdit::textChanged, q, [this]() {checkAcceptable();});
auto nameLabel = new QLabel(i18n("Name:"));
auto mailLabel = new QLabel(i18n("EMail:"));
mInvalidEmailLabel = new QLabel(QStringLiteral("<font size='small' color='%1'>%2</font>").arg(
KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::NegativeText).color().name(), i18n("Invalid EMail")));
int row = 0;
grid->addWidget(nameLabel, row, 0);
grid->addWidget(mNameEdit, row++, 1);
grid->addWidget(mailLabel, row, 0);
grid->addWidget(mEmailEdit, row++, 1);
grid->addWidget(mInvalidEmailLabel, row++, 1);
// In the future GnuPG may support more algos but for now
// (2.1.18) we are stuck with RSA for on card generation.
auto algorithmLabel = new QLabel(i18n("Algorithm:"));
mAlgorithmCombo = new QComboBox;
grid->addWidget(algorithmLabel, row, 0);
grid->addWidget(mAlgorithmCombo, row++, 1);
mBackupCheckBox = new QCheckBox(i18n("Backup encryption key"));
mBackupCheckBox->setToolTip(i18n("Backup the encryption key in a file.") + QStringLiteral("<br/>") +
i18n("You will be asked for a passphrase to protect that file during key generation."));
mBackupCheckBox->setChecked(true);
grid->addWidget(mBackupCheckBox, row++, 0, 1, 2);
q->setMinimumWidth(400);
checkAcceptable();
......@@ -85,15 +98,28 @@ public:
void accept()
{
params.name = mNameEdit->text();
params.email = mEmailEdit->text();
params.algorithm = mAlgorithmCombo->currentData().toByteArray().toStdString();
params.backup = mBackupCheckBox->isChecked();
if (mNameEdit) {
params.name = mNameEdit->text();
}
if (mEmailEdit) {
params.email = mEmailEdit->text();
}
if (mAlgorithmCombo) {
params.algorithm = mAlgorithmCombo->currentData().toByteArray().toStdString();
}
if (mBackupCheckBox) {
params.backup = mBackupCheckBox->isChecked();
}
q->accept();
}
void setSupportedAlgorithms(const std::vector<std::pair<std::string, QString>> &algorithms, const std::string &defaultAlgo)
{
if (!mAlgorithmCombo) {
qCWarning(KLEOPATRA_LOG) << "GenCardKeyDialog::setSupportedAlgorithms() called, but algorithm no required key attribute";
return;
}
mAlgorithmCombo->clear();
for (auto algorithm: algorithms) {
mAlgorithmCombo->addItem(algorithm.second, QByteArray::fromStdString(algorithm.first));
......@@ -104,6 +130,9 @@ public:
void checkAcceptable()
{
// We only require a valid mail address
if (!mEmailEdit) {
return;
}
const QString mail = mEmailEdit->text();
if (!mail.isEmpty() &&
KEmailAddress::isValidSimpleAddress(mail)) {
......@@ -129,8 +158,8 @@ public:
QCheckBox *mBackupCheckBox;
};
GenCardKeyDialog::GenCardKeyDialog(QWidget *parent) : QDialog(parent),
d(new Private(this))
GenCardKeyDialog::GenCardKeyDialog(KeyAttributes requiredAttributes, QWidget *parent) : QDialog(parent),
d(new Private(this, requiredAttributes))
{
}
......
......@@ -31,7 +31,22 @@ public:
std::string algorithm;
bool backup;
};
explicit GenCardKeyDialog(QWidget *parent = nullptr);
enum KeyAttribute {
NoKeyAttributes = 0,
KeyOwnerName = 1,
KeyOwnerEmail = 2,
KeyComment = 4,
KeyAlgorithm = 8,
LocalKeyBackup = 16,
_AllKeyAttributes_Helper,
AllKeyAttributes = 2 * (_AllKeyAttributes_Helper - 1) - 1
};
Q_DECLARE_FLAGS(KeyAttributes, KeyAttribute)
explicit GenCardKeyDialog(KeyAttributes requiredAttributes, QWidget *parent = nullptr);
KeyParams getKeyParams() const;
......
......@@ -9,6 +9,8 @@
#include "pivcard.h"
#include <KLocalizedString>
#include "kleopatra_debug.h"
using namespace Kleo;
......@@ -48,6 +50,30 @@ std::string PIVCard::keyManagementKeyRef()
return std::string("PIV.9D");
}
// static
std::vector< std::pair<std::string, QString> > PIVCard::supportedAlgorithms(const std::string &keyRef)
{
if (keyRef == PIVCard::keyManagementKeyRef()) {
return {
{ "rsa2048", i18n("RSA key transport (2048 bits)") },
{ "nistp256", i18n("ECDH (Curve P-256)") },
{ "nistp384", i18n("ECDH (Curve P-384)") }
};
} else if (keyRef == PIVCard::digitalSignatureKeyRef()) {
return {
{ "rsa2048", i18n("RSA (2048 bits)") },
{ "nistp256", i18n("ECDSA (Curve P-256)") },
{ "nistp384", i18n("ECDSA (Curve P-384)") }
};
}
// NIST SP 800-78-4 does not allow Curve P-384 for PIV Authentication key or Card Authentication key
return {
{ "rsa2048", i18n("RSA (2048 bits)") },
{ "nistp256", i18n("ECDSA (Curve P-256)") },
};
}
std::string PIVCard::keyGrip(const std::string& keyRef) const
{
return mMetaInfo.value("KEYPAIRINFO-" + keyRef);
......
......@@ -29,6 +29,8 @@ public:
static std::string digitalSignatureKeyRef();
static std::string keyManagementKeyRef();
static std::vector< std::pair<std::string, QString> > supportedAlgorithms(const std::string &keyRef);
std::string keyGrip(const std::string &keyRef) const;
void setCardInfo (const std::vector< std::pair<std::string, std::string> > &infos);
......
......@@ -311,7 +311,7 @@ void PGPCardWidget::genkeyRequested()
}
}
GenCardKeyDialog *dlg = new GenCardKeyDialog(this);
GenCardKeyDialog *dlg = new GenCardKeyDialog(GenCardKeyDialog::AllKeyAttributes, this);
std::vector<std::pair<std::string, QString>> algos = {
{ "1024", QStringLiteral("RSA 1024") },
{ "2048", QStringLiteral("RSA 2048") },
......
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