diff --git a/src/commands/openpgpgeneratecardkeycommand.cpp b/src/commands/openpgpgeneratecardkeycommand.cpp index 6fabed17c..6bd756fbf 100644 --- a/src/commands/openpgpgeneratecardkeycommand.cpp +++ b/src/commands/openpgpgeneratecardkeycommand.cpp @@ -1,202 +1,202 @@ /* commands/openpgpgeneratecardkeycommand.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "openpgpgeneratecardkeycommand.h" #include "cardcommand_p.h" #include "smartcard/algorithminfo.h" #include "smartcard/openpgpcard.h" #include "smartcard/readerstatus.h" #include "dialogs/gencardkeydialog.h" #include #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::SmartCard; using namespace GpgME; class OpenPGPGenerateCardKeyCommand::Private : public CardCommand::Private { friend class ::Kleo::Commands::OpenPGPGenerateCardKeyCommand; OpenPGPGenerateCardKeyCommand *q_func() const { return static_cast(q); } public: explicit Private(OpenPGPGenerateCardKeyCommand *qq, const std::string &keyref, const std::string &serialNumber, QWidget *p); void init(); private: void slotDialogAccepted(); void slotDialogRejected(); void slotResult(const Error &err); private: void ensureDialogCreated(); void generateKey(); private: std::string keyRef; bool overwriteExistingKey = false; std::string algorithm; QPointer dialog; }; OpenPGPGenerateCardKeyCommand::Private *OpenPGPGenerateCardKeyCommand::d_func() { return static_cast(d.get()); } const OpenPGPGenerateCardKeyCommand::Private *OpenPGPGenerateCardKeyCommand::d_func() const { return static_cast(d.get()); } #define d d_func() #define q q_func() OpenPGPGenerateCardKeyCommand::Private::Private(OpenPGPGenerateCardKeyCommand *qq, const std::string &keyRef_, const std::string &serialNumber, QWidget *p) : CardCommand::Private(qq, serialNumber, p) , keyRef{keyRef_} { } void OpenPGPGenerateCardKeyCommand::Private::init() { } void OpenPGPGenerateCardKeyCommand::Private::slotDialogAccepted() { algorithm = dialog->getKeyParams().algorithm; generateKey(); } void OpenPGPGenerateCardKeyCommand::Private::slotDialogRejected() { finished(); } void OpenPGPGenerateCardKeyCommand::Private::slotResult(const GpgME::Error& err) { qCDebug(KLEOPATRA_LOG).nospace() << q << "::Private::" << __func__ << err; if (err) { error(i18nc("@info", "Generating key failed: %1", QString::fromLatin1(err.asString()))); } else if (!err.isCanceled()) { success(i18nc("@info", "Key successfully generated.")); ReaderStatus::mutableInstance()->updateStatus(); } finished(); } void OpenPGPGenerateCardKeyCommand::Private::ensureDialogCreated() { if (dialog) { return; } dialog = new GenCardKeyDialog(GenCardKeyDialog::KeyAlgorithm, parentWidgetOrView()); dialog->setAttribute(Qt::WA_DeleteOnClose); connect(dialog, &QDialog::accepted, q, [this]() { slotDialogAccepted(); }); connect(dialog, &QDialog::rejected, q, [this]() { slotDialogRejected(); }); } void OpenPGPGenerateCardKeyCommand::Private::generateKey() { qCDebug(KLEOPATRA_LOG).nospace() << q << "::Private::" << __func__; auto pgpCard = ReaderStatus::instance()->getCard(serialNumber()); if (!pgpCard) { error(i18n("Failed to find the OpenPGP card with the serial number: %1", QString::fromStdString(serialNumber()))); finished(); return; } QByteArrayList command; command << "SCD GENKEY"; if (overwriteExistingKey) { command << "--force"; } if (!algorithm.empty()) { - command << "--algo=" + QByteArray::fromStdString(algorithm); + command << "--algo=" + QByteArray::fromStdString(OpenPGPCard::getAlgorithmName(algorithm, keyRef)); } command << "--" << QByteArray::fromStdString(keyRef); ReaderStatus::mutableInstance()->startSimpleTransaction(pgpCard, command.join(' '), q, [this](const GpgME::Error &err) { slotResult(err); }); } OpenPGPGenerateCardKeyCommand::OpenPGPGenerateCardKeyCommand(const std::string &keyref, const std::string &serialNumber, QWidget *p) : CardCommand(new Private(this, keyref, serialNumber, p)) { d->init(); } OpenPGPGenerateCardKeyCommand::~OpenPGPGenerateCardKeyCommand() { qCDebug(KLEOPATRA_LOG).nospace() << this << "::" << __func__; } void OpenPGPGenerateCardKeyCommand::doStart() { qCDebug(KLEOPATRA_LOG).nospace() << this << "::" << __func__; // check if key exists auto pgpCard = ReaderStatus::instance()->getCard(d->serialNumber()); if (!pgpCard) { d->error(i18n("Failed to find the OpenPGP card with the serial number: %1", QString::fromStdString(d->serialNumber()))); d->finished(); return; } auto existingKey = pgpCard->keyInfo(d->keyRef).grip; if (!existingKey.empty()) { const QString warningText = i18nc("@info", "

This card already contains a key in this slot. Continuing will overwrite that key.

" "

If there is no backup the existing key will be irrecoverably lost.

") + i18n("The existing key has the ID:") + QStringLiteral("
%1
").arg(QString::fromStdString(existingKey)) + (d->keyRef == OpenPGPCard::pgpEncKeyRef() ? i18n("It will no longer be possible to decrypt past communication encrypted for the existing key.") : QString()); const auto choice = KMessageBox::warningContinueCancel(d->parentWidgetOrView(), 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->ensureDialogCreated(); Q_ASSERT(d->dialog); - d->dialog->setSupportedAlgorithms(pgpCard->supportedAlgorithms(d->keyRef), "rsa2048"); + d->dialog->setSupportedAlgorithms(pgpCard->supportedAlgorithms(), "rsa2048"); d->dialog->show(); } void OpenPGPGenerateCardKeyCommand::doCancel() { } #undef d #undef q #include "moc_openpgpgeneratecardkeycommand.cpp" diff --git a/src/smartcard/openpgpcard.cpp b/src/smartcard/openpgpcard.cpp index 7dbfdb4e0..8cdb18a9c 100644 --- a/src/smartcard/openpgpcard.cpp +++ b/src/smartcard/openpgpcard.cpp @@ -1,163 +1,188 @@ /* smartcard/openpgpcard.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-FileCopyrightText: 2020, 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ /* Code in this file is partly based on the GNU Privacy Assistant * (cm-openpgp.c) git rev. 0a78795146661234070681737b3e08228616441f * * Whis is: * SPDX-FileCopyrightText: 2008, 2009 g 10 Code GmbH * * And may be licensed under the GNU General Public License * as published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. */ #include "openpgpcard.h" #include "algorithminfo.h" #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::SmartCard; static QDebug operator<<(QDebug s, const std::string &string) { return s << QString::fromStdString(string); } // static const std::string OpenPGPCard::AppName = "openpgp"; OpenPGPCard::OpenPGPCard(const Card &card) : Card(card) { setAppName(AppName); setInitialKeyInfos(OpenPGPCard::supportedKeys()); } // static std::string OpenPGPCard::pgpSigKeyRef() { return std::string("OPENPGP.1"); } // static std::string OpenPGPCard::pgpEncKeyRef() { return std::string("OPENPGP.2"); } // static std::string OpenPGPCard::pgpAuthKeyRef() { return std::string("OPENPGP.3"); } // static std::string OpenPGPCard::pinKeyRef() { return std::string("OPENPGP.1"); } // static std::string OpenPGPCard::adminPinKeyRef() { return std::string("OPENPGP.3"); } // static std::string OpenPGPCard::resetCodeKeyRef() { return std::string("OPENPGP.2"); } // static const std::vector & OpenPGPCard::supportedKeys() { static const std::vector keyInfos = { {OpenPGPCard::pgpSigKeyRef(), "", "sc", "", ""}, {OpenPGPCard::pgpEncKeyRef(), "", "e", "", ""}, {OpenPGPCard::pgpAuthKeyRef(), "", "a", "", ""} }; return keyInfos; } // static QString OpenPGPCard::keyDisplayName(const std::string &keyRef) { static const QMap displayNames = { { OpenPGPCard::pgpSigKeyRef(), i18n("Signature") }, { OpenPGPCard::pgpEncKeyRef(), i18n("Encryption") }, { OpenPGPCard::pgpAuthKeyRef(), i18n("Authentication") } }; return displayNames.value(keyRef); } +// static +std::string OpenPGPCard::getAlgorithmName(const std::string &algorithm, const std::string &keyRef) +{ + static const std::map ecdhAlgorithmMapping = { + { "curve25519", "cv25519" }, + { "curve448", "cv448" }, + }; + static const std::map eddsaAlgorithmMapping = { + { "curve25519", "ed25519" }, + { "curve448", "ed448" }, + }; + if (keyRef == OpenPGPCard::pgpEncKeyRef()) { + const auto it = ecdhAlgorithmMapping.find(algorithm); + if (it != ecdhAlgorithmMapping.end()) { + return it->second; + } + } else { + const auto it = eddsaAlgorithmMapping.find(algorithm); + if (it != eddsaAlgorithmMapping.end()) { + return it->second; + } + } + return algorithm; +} + void OpenPGPCard::setSupportedAlgorithms(const std::vector &algorithms) { static const std::vector allowedAlgorithms = { "brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1", "curve25519", + "curve448", "nistp256", "nistp384", "nistp521", "rsa2048", "rsa3072", "rsa4096", + "secp256k1", }; mAlgorithms.clear(); std::copy_if(algorithms.begin(), algorithms.end(), std::back_inserter(mAlgorithms), [](const auto &algo) { return Kleo::contains(allowedAlgorithms, algo); }); if (mAlgorithms.size() < algorithms.size()) { qWarning(KLEOPATRA_LOG).nospace() << "OpenPGPCard::" << __func__ << " Invalid algorithm in " << algorithms << " (allowed algorithms: " << allowedAlgorithms << ")"; } } std::string OpenPGPCard::pubkeyUrl() const { return cardInfo("PUBKEY-URL"); } -std::vector OpenPGPCard::supportedAlgorithms(const std::string &keyRef) +std::vector OpenPGPCard::supportedAlgorithms() { static const std::map displayNames = { { "brainpoolP256r1", i18nc("@info", "ECC (Brainpool P-256)") }, { "brainpoolP384r1", i18nc("@info", "ECC (Brainpool P-384)") }, { "brainpoolP512r1", i18nc("@info", "ECC (Brainpool P-512)") }, { "curve25519", i18nc("@info", "ECC (Curve25519)") }, + { "curve448", i18nc("@info", "ECC (Curve448)") }, { "nistp256", i18nc("@info", "ECC (NIST P-256)") }, { "nistp384", i18nc("@info", "ECC (NIST P-384)") }, { "nistp521", i18nc("@info", "ECC (NIST P-521)") }, { "rsa2048", i18nc("@info", "RSA 2048") }, { "rsa3072", i18nc("@info", "RSA 3072") }, { "rsa4096", i18nc("@info", "RSA 4096") }, + { "secp256k1", i18nc("@info", "secp256k1") }, }; - const std::string curve25519Algo = keyRef == OpenPGPCard::pgpEncKeyRef() ? "cv25519" : "ed25519"; std::vector algos; - std::transform(mAlgorithms.cbegin(), mAlgorithms.cend(), std::back_inserter(algos), [curve25519Algo](const auto &algo) { - if (algo == "curve25519") { - return AlgorithmInfo{curve25519Algo, displayNames.at(algo)}; - } + std::transform(mAlgorithms.cbegin(), mAlgorithms.cend(), std::back_inserter(algos), [](const auto &algo) { return AlgorithmInfo{algo, displayNames.at(algo)}; }); return algos; } diff --git a/src/smartcard/openpgpcard.h b/src/smartcard/openpgpcard.h index c5ceca4c2..39c4befe5 100644 --- a/src/smartcard/openpgpcard.h +++ b/src/smartcard/openpgpcard.h @@ -1,68 +1,75 @@ /* smartcard/openpgpcard.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-FileCopyrightText: 2020, 2022 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "card.h" namespace Kleo { namespace SmartCard { struct AlgorithmInfo; struct KeyPairInfo; /** Class to work with OpenPGP smartcards or compatible tokens */ class OpenPGPCard: public Card { public: explicit OpenPGPCard(const Card &card); static const std::string AppName; static std::string pgpSigKeyRef(); static std::string pgpEncKeyRef(); static std::string pgpAuthKeyRef(); static std::string pinKeyRef(); static std::string adminPinKeyRef(); static std::string resetCodeKeyRef(); static const std::vector & supportedKeys(); static QString keyDisplayName(const std::string &keyRef); + /** + * Returns an algorithm name for the algorithm \p algorithm that is suitable + * for passing to scdaemon for the card slot specified by \p keyRef. + * + * For example, it maps "curve25519" to either "ed25519" or "cv25519". + */ + static std::string getAlgorithmName(const std::string &algorithm, const std::string &keyRef); + /** * Sets the algorithms supported by this smart card to \p algorithms. * The following values for algorithms are allowed: * brainpoolP256r1, brainpoolP384r1, brainpoolP512r1, * curve25519, * nistp256, nistp384, nistp521, * rsa2048, rsa3072, rsa4096. */ void setSupportedAlgorithms(const std::vector &algorithms); std::string pubkeyUrl() const; /** - * Returns a list of algorithm names and corresponding display names suitable - * for the card slot specified by \p keyRef. + * Returns a list of algorithm IDs and corresponding display names. * - * \note For Curve25519, depending on the given card slot, either "ed25519" - * or "cv25519" is returned as algorithm ID. + * \note: You have to use getAlgorithmName to map the algorithm ID to + * an algorithm name suitable for a certain card slot. */ - std::vector supportedAlgorithms(const std::string &keyRef); + std::vector supportedAlgorithms(); private: std::vector mAlgorithms; }; } // namespace Smartcard } // namespace Kleopatra