diff --git a/src/smartcard/card.cpp b/src/smartcard/card.cpp index 9a8ec52e7..c321c4b3f 100644 --- a/src/smartcard/card.cpp +++ b/src/smartcard/card.cpp @@ -1,197 +1,208 @@ /* smartcard/card.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-License-Identifier: GPL-2.0-or-later */ #include "card.h" #include "readerstatus.h" using namespace Kleo; using namespace Kleo::SmartCard; namespace { static QString formatVersion(int value) { if (value < 0) { return QString(); } const unsigned int a = ((value >> 24) & 0xff); const unsigned int b = ((value >> 16) & 0xff); const unsigned int c = ((value >> 8) & 0xff); const unsigned int d = ((value ) & 0xff); if (a) { return QStringLiteral("%1.%2.%3.%4").arg(QString::number(a), QString::number(b), QString::number(c), QString::number(d)); } else if (b) { return QStringLiteral("%1.%2.%3").arg(QString::number(b), QString::number(c), QString::number(d)); } else if (c) { return QStringLiteral("%1.%2").arg(QString::number(c), QString::number(d)); } return QString::number(d); } } Card::Card() { } Card::~Card() { } void Card::setStatus(Status s) { mStatus = s; } Card::Status Card::status() const { return mStatus; } void Card::setSerialNumber(const std::string &sn) { mSerialNumber = sn; } std::string Card::serialNumber() const { return mSerialNumber; } QString Card::displaySerialNumber() const { return mDisplaySerialNumber; } void Card::setDisplaySerialNumber(const QString &serialNumber) { mDisplaySerialNumber = serialNumber; } std::string Card::appName() const { return mAppName; } void Card::setAppName(const std::string &name) { mAppName = name; } void Card::setAppVersion(int version) { mAppVersion = version; } int Card::appVersion() const { return mAppVersion; } QString Card::displayAppVersion() const { return formatVersion(mAppVersion); } std::string Card::cardType() const { return mCardType; } int Card::cardVersion() const { return mCardVersion; } QString Card::displayCardVersion() const { return formatVersion(mCardVersion); } +QString Card::cardHolder() const +{ + return mCardHolder; +} + std::vector Card::pinStates() const { return mPinStates; } void Card::setPinStates(const std::vector &pinStates) { mPinStates = pinStates; } bool Card::hasNullPin() const { return mHasNullPin; } void Card::setHasNullPin(bool value) { mHasNullPin = value; } bool Card::canLearnKeys() const { return mCanLearn; } void Card::setCanLearnKeys(bool value) { mCanLearn = value; } bool Card::operator == (const Card &other) const { return mStatus == other.status() && mSerialNumber == other.serialNumber() && mAppName == other.appName() && mAppVersion == other.appVersion() && mPinStates == other.pinStates() && mCanLearn == other.canLearnKeys() && mHasNullPin == other.hasNullPin(); } bool Card::operator != (const Card &other) const { return !operator==(other); } void Card::setErrorMsg(const QString &msg) { mErrMsg = msg; } QString Card::errorMsg() const { return mErrMsg; } namespace { static int parseHexEncodedVersionTuple(const std::string &s) { // s is a hex-encoded, unsigned int-packed version tuple, // i.e. each byte represents one part of the version tuple bool ok; const auto version = QByteArray::fromStdString(s).toUInt(&ok, 16); return ok ? version : -1; } } bool Card::parseCardInfo(const std::string &name, const std::string &value) { if (name == "APPVERSION") { mAppVersion = parseHexEncodedVersionTuple(value); return true; } else if (name == "CARDTYPE") { mCardType = value; return true; } else if (name == "CARDVERSION") { mCardVersion = parseHexEncodedVersionTuple(value); return true; + } else if (name == "DISP-NAME") { + auto list = QString::fromUtf8(QByteArray::fromStdString(value)). + split(QStringLiteral("<<"), Qt::SkipEmptyParts); + std::reverse(list.begin(), list.end()); + mCardHolder = list.join(QLatin1Char(' ')); + return true; } return false; } diff --git a/src/smartcard/card.h b/src/smartcard/card.h index d3c92e75f..376deff73 100644 --- a/src/smartcard/card.h +++ b/src/smartcard/card.h @@ -1,107 +1,110 @@ #ifndef SMARTCARD_CARD_H #define SMARTCARD_CARD_H /* smartcard/card.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-License-Identifier: GPL-2.0-or-later */ #include #include #include namespace Kleo { namespace SmartCard { /** Class representing an application on a smartcard or similar hardware token. */ class Card { public: enum PinState { UnknownPinState, NullPin, PinBlocked, NoPin, PinOk, NumPinStates }; enum Status { NoCard, CardPresent, CardActive, CardUsable, _NumScdStates, CardError = _NumScdStates, NumStates }; Card(); virtual ~Card(); virtual bool operator == (const Card &other) const; bool operator != (const Card &other) const; void setStatus(Status s); Status status() const; void setSerialNumber(const std::string &sn); std::string serialNumber() const; QString displaySerialNumber() const; void setDisplaySerialNumber(const QString &sn); std::string appName() const; void setAppVersion(int version); int appVersion() const; QString displayAppVersion() const; std::string cardType() const; int cardVersion() const; QString displayCardVersion() const; + QString cardHolder() const; + std::vector pinStates() const; void setPinStates(const std::vector &pinStates); bool hasNullPin() const; void setHasNullPin(bool value); bool canLearnKeys() const; void setCanLearnKeys(bool value); QString errorMsg() const; void setErrorMsg(const QString &msg); protected: void setAppName(const std::string &name); bool parseCardInfo(const std::string &name, const std::string &value); private: bool mCanLearn = false; bool mHasNullPin = false; Status mStatus = NoCard; std::string mSerialNumber; QString mDisplaySerialNumber; std::string mAppName; int mAppVersion = -1; std::string mCardType; int mCardVersion = -1; + QString mCardHolder; std::vector mPinStates; QString mErrMsg; }; } // namespace Smartcard } // namespace Kleopatra #endif // SMARTCARD_CARD_H diff --git a/src/smartcard/openpgpcard.cpp b/src/smartcard/openpgpcard.cpp index ec7dd4eb6..2472d7ef9 100644 --- a/src/smartcard/openpgpcard.cpp +++ b/src/smartcard/openpgpcard.cpp @@ -1,163 +1,156 @@ /* 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-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 "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::SmartCard; // static const std::string OpenPGPCard::AppName = "openpgp"; OpenPGPCard::OpenPGPCard(const Card &card) : Card(card) { setAppName(AppName); } // 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"); } std::string OpenPGPCard::sigFpr() const { return mMetaInfo.value("SIGKEY-FPR"); } std::string OpenPGPCard::encFpr() const { return mMetaInfo.value("ENCKEY-FPR"); } std::string OpenPGPCard::authFpr() const { return mMetaInfo.value("AUTHKEY-FPR"); } void OpenPGPCard::setCardInfo(const std::vector< std::pair > &infos) { qCDebug(KLEOPATRA_LOG) << "Card" << serialNumber().c_str() << "info:"; for (const auto &pair: infos) { qCDebug(KLEOPATRA_LOG) << pair.first.c_str() << ":" << pair.second.c_str(); if (parseCardInfo(pair.first, pair.second)) { continue; } if (pair.first == "KEY-FPR" || pair.first == "KEY-TIME") { // Key fpr and key time need to be distinguished, the number // of the key decides the usage. const auto values = QString::fromStdString(pair.second).split(QLatin1Char(' ')); if (values.size() < 2) { qCWarning(KLEOPATRA_LOG) << "Invalid entry."; setStatus(Card::CardError); continue; } const auto usage = values[0]; const auto fpr = values[1].toStdString(); if (usage == QLatin1Char('1')) { mMetaInfo.insert(std::string("SIG") + pair.first, fpr); } else if (usage == QLatin1Char('2')) { mMetaInfo.insert(std::string("ENC") + pair.first, fpr); } else if (usage == QLatin1Char('3')) { mMetaInfo.insert(std::string("AUTH") + pair.first, fpr); } else { // Maybe more keyslots in the future? qCDebug(KLEOPATRA_LOG) << "Unhandled keyslot"; } } else if (pair.first == "KEYPAIRINFO") { // Fun, same as above but the other way around. const auto values = QString::fromStdString(pair.second).split(QLatin1Char(' ')); if (values.size() < 2) { qCWarning(KLEOPATRA_LOG) << "Invalid entry."; setStatus(Card::CardError); continue; } const auto usage = values[1]; const auto grip = values[0].toStdString(); if (usage == QLatin1String("OPENPGP.1")) { mMetaInfo.insert(std::string("SIG") + pair.first, grip); } else if (usage == QLatin1String("OPENPGP.2")) { mMetaInfo.insert(std::string("ENC") + pair.first, grip); } else if (usage == QLatin1String("OPENPGP.3")) { mMetaInfo.insert(std::string("AUTH") + pair.first, grip); } else { // Maybe more keyslots in the future? qCDebug(KLEOPATRA_LOG) << "Unhandled keyslot"; } } else { mMetaInfo.insert(pair.first, pair.second); } } } bool OpenPGPCard::operator == (const Card& rhs) const { const OpenPGPCard *other = dynamic_cast(&rhs); if (!other) { return false; } return Card::operator ==(rhs) && sigFpr() == other->sigFpr() && encFpr() == other->encFpr() && authFpr() == other->authFpr() && manufacturer() == other->manufacturer() && cardHolder() == other->cardHolder() && pubkeyUrl() == other->pubkeyUrl(); } void OpenPGPCard::setManufacturer(const std::string &manufacturer) { mManufacturer = manufacturer; } std::string OpenPGPCard::manufacturer() const { return mManufacturer; } -std::string OpenPGPCard::cardHolder() const -{ - auto list = QString::fromStdString(mMetaInfo.value("DISP-NAME")).split(QStringLiteral("<<")); - std::reverse(list.begin(), list.end()); - return list.join(QLatin1Char(' ')).toStdString(); -} - std::string OpenPGPCard::pubkeyUrl() const { return mMetaInfo.value("PUBKEY-URL"); } diff --git a/src/smartcard/openpgpcard.h b/src/smartcard/openpgpcard.h index 085275a43..42a997395 100644 --- a/src/smartcard/openpgpcard.h +++ b/src/smartcard/openpgpcard.h @@ -1,53 +1,52 @@ #ifndef SMARTCARD_OPENPGPCARD_H #define SMARTCARD_OPENPGPCARD_H /* 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-License-Identifier: GPL-2.0-or-later */ #include #include "card.h" namespace Kleo { namespace SmartCard { /** 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 pinKeyRef(); static std::string adminPinKeyRef(); static std::string resetCodeKeyRef(); std::string encFpr() const; std::string sigFpr() const; std::string authFpr() const; void setCardInfo(const std::vector< std::pair > &infos); bool operator == (const Card& other) const override; void setManufacturer(const std::string &manufacturer); std::string manufacturer() const; - std::string cardHolder() const; std::string pubkeyUrl() const; private: QMap mMetaInfo; std::string mManufacturer; }; } // namespace Smartcard } // namespace Kleopatra #endif // SMARTCARD_CARD_H diff --git a/src/view/pgpcardwidget.cpp b/src/view/pgpcardwidget.cpp index 078d53fc6..e86a05288 100644 --- a/src/view/pgpcardwidget.cpp +++ b/src/view/pgpcardwidget.cpp @@ -1,483 +1,483 @@ /* view/pgpcardwiget.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-License-Identifier: GPL-2.0-or-later */ #include "pgpcardwidget.h" #include "kleopatra_debug.h" #include "commands/changepincommand.h" #include "smartcard/openpgpcard.h" #include "smartcard/readerstatus.h" #include "dialogs/gencardkeydialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::SmartCard; namespace { class GenKeyThread: public QThread { Q_OBJECT public: explicit GenKeyThread(const GenCardKeyDialog::KeyParams ¶ms, const std::string &serial): mSerial(serial), mParams(params) { } GpgME::Error error() { return mErr; } std::string bkpFile() { return mBkpFile; } protected: void run() override { GpgME::GpgGenCardKeyInteractor *ei = new GpgME::GpgGenCardKeyInteractor(mSerial); ei->setAlgo(GpgME::GpgGenCardKeyInteractor::RSA); ei->setKeySize(QByteArray::fromStdString(mParams.algorithm).toInt()); ei->setNameUtf8(mParams.name.toStdString()); ei->setEmailUtf8(mParams.email.toStdString()); ei->setDoBackup(mParams.backup); const auto ctx = std::shared_ptr (GpgME::Context::createForProtocol(GpgME::OpenPGP)); QGpgME::QByteArrayDataProvider dp; GpgME::Data data(&dp); mErr = ctx->cardEdit(GpgME::Key(), std::unique_ptr (ei), data); mBkpFile = ei->backupFileName(); } private: GpgME::Error mErr; std::string mSerial; GenCardKeyDialog::KeyParams mParams; std::string mBkpFile; }; } // Namespace PGPCardWidget::PGPCardWidget(QWidget *parent): QWidget(parent), mSerialNumber(new QLabel(this)), mCardHolderLabel(new QLabel(this)), mVersionLabel(new QLabel(this)), mSigningKey(new QLabel(this)), mEncryptionKey(new QLabel(this)), mAuthKey(new QLabel(this)), mUrlLabel(new QLabel(this)), mCardIsEmpty(false) { auto grid = new QGridLayout; int row = 0; // Set up the scroll are auto area = new QScrollArea; area->setFrameShape(QFrame::NoFrame); area->setWidgetResizable(true); auto areaWidget = new QWidget; auto areaVLay = new QVBoxLayout(areaWidget); areaVLay->addLayout(grid); areaVLay->addStretch(1); area->setWidget(areaWidget); auto myLayout = new QVBoxLayout(this); myLayout->setContentsMargins(0, 0, 0, 0); myLayout->addWidget(area); // Version and Serialnumber grid->addWidget(mVersionLabel, row++, 0, 1, 2); mVersionLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); grid->addWidget(new QLabel(i18n("Serial number:")), row, 0); grid->addWidget(mSerialNumber, row++, 1); mSerialNumber->setTextInteractionFlags(Qt::TextBrowserInteraction); // Cardholder Row grid->addWidget(new QLabel(i18nc("The owner of a smartcard. GnuPG refers to this as cardholder.", "Cardholder:")), row, 0); grid->addWidget(mCardHolderLabel, row, 1); mCardHolderLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); auto nameButtton = new QPushButton; nameButtton->setIcon(QIcon::fromTheme(QStringLiteral("cell_edit"))); nameButtton->setToolTip(i18n("Change")); grid->addWidget(nameButtton, row++, 2); connect(nameButtton, &QPushButton::clicked, this, &PGPCardWidget::changeNameRequested); // URL Row grid->addWidget(new QLabel(i18nc("The URL under which a public key that " "corresponds to a smartcard can be downloaded", "Pubkey URL:")), row, 0); grid->addWidget(mUrlLabel, row, 1); mUrlLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); auto urlButtton = new QPushButton; urlButtton->setIcon(QIcon::fromTheme(QStringLiteral("cell_edit"))); urlButtton->setToolTip(i18n("Change")); grid->addWidget(urlButtton, row++, 2); connect(urlButtton, &QPushButton::clicked, this, &PGPCardWidget::changeUrlRequested); // The keys auto line1 = new QFrame(); line1->setFrameShape(QFrame::HLine); grid->addWidget(line1, row++, 0, 1, 4); grid->addWidget(new QLabel(QStringLiteral("%1").arg(i18n("Keys:"))), row++, 0); grid->addWidget(new QLabel(i18n("Signature:")), row, 0); grid->addWidget(mSigningKey, row++, 1); mSigningKey->setTextInteractionFlags(Qt::TextBrowserInteraction); grid->addWidget(new QLabel(i18n("Encryption:")), row, 0); grid->addWidget(mEncryptionKey, row++, 1); mEncryptionKey->setTextInteractionFlags(Qt::TextBrowserInteraction); grid->addWidget(new QLabel(i18n("Authentication:")), row, 0); grid->addWidget(mAuthKey, row++, 1); mAuthKey->setTextInteractionFlags(Qt::TextBrowserInteraction); auto line2 = new QFrame(); line2->setFrameShape(QFrame::HLine); grid->addWidget(line2, row++, 0, 1, 4); grid->addWidget(new QLabel(QStringLiteral("%1").arg(i18n("Actions:"))), row++, 0); auto actionLayout = new QHBoxLayout; auto generateButton = new QPushButton(i18n("Generate new Keys")); generateButton->setToolTip(i18n("Create a new primary key and generate subkeys on the card.")); actionLayout->addWidget(generateButton); connect(generateButton, &QPushButton::clicked, this, &PGPCardWidget::genkeyRequested); auto pinButtton = new QPushButton(i18n("Change PIN")); pinButtton->setToolTip(i18n("Change the PIN required to unblock the smartcard.")); actionLayout->addWidget(pinButtton); connect(pinButtton, &QPushButton::clicked, this, [this] () { doChangePin(OpenPGPCard::pinKeyRef()); }); auto pukButton = new QPushButton(i18n("Change Admin PIN")); pukButton->setToolTip(i18n("Change the PIN required to unlock the smartcard.")); actionLayout->addWidget(pukButton); connect(pukButton, &QPushButton::clicked, this, [this] () { doChangePin(OpenPGPCard::adminPinKeyRef()); }); auto resetCodeButton = new QPushButton(i18n("Change Reset Code")); pukButton->setToolTip(i18n("Change the PIN required to reset the smartcard to an empty state.")); actionLayout->addWidget(resetCodeButton); connect(resetCodeButton, &QPushButton::clicked, this, [this] () { doChangePin(OpenPGPCard::resetCodeKeyRef()); }); actionLayout->addStretch(-1); grid->addLayout(actionLayout, row++, 0, 1, 4); grid->setColumnStretch(4, -1); } void PGPCardWidget::setCard(const OpenPGPCard *card) { const QString version = card->displayAppVersion(); mIs21 = card->appVersion() >= 0x0201; const QString manufacturer = QString::fromStdString(card->manufacturer()); const bool manufacturerIsUnknown = manufacturer.isEmpty() || manufacturer == QLatin1String("unknown"); mVersionLabel->setText(manufacturerIsUnknown ? i18nc("Placeholder is a version number", "Unknown OpenPGP v%1 card", version) : i18nc("First placeholder is manufacturer, second placeholder is a version number", "%1 OpenPGP v%2 card", manufacturer, version)); mSerialNumber->setText(card->displaySerialNumber()); mRealSerial = card->serialNumber(); - const auto holder = QString::fromStdString(card->cardHolder()); + const auto holder = card->cardHolder(); const auto url = QString::fromStdString(card->pubkeyUrl()); mCardHolderLabel->setText(holder.isEmpty() ? i18n("not set") : holder); mUrl = url; mUrlLabel->setText(url.isEmpty() ? i18n("not set") : QStringLiteral("%1").arg(url.toHtmlEscaped())); mUrlLabel->setOpenExternalLinks(true); updateKey(mSigningKey, card->sigFpr()); updateKey(mEncryptionKey, card->encFpr()); updateKey(mAuthKey, card->authFpr()); mCardIsEmpty = card->authFpr().empty() && card->sigFpr().empty() && card->encFpr().empty(); } void PGPCardWidget::doChangePin(const std::string &keyRef) { auto cmd = new ChangePinCommand(mRealSerial, OpenPGPCard::AppName, this); this->setEnabled(false); connect(cmd, &ChangePinCommand::finished, this, [this]() { this->setEnabled(true); }); cmd->setKeyRef(keyRef); cmd->start(); } void PGPCardWidget::doGenKey(GenCardKeyDialog *dlg) { const auto params = dlg->getKeyParams(); auto progress = new QProgressDialog(this, Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::Dialog); progress->setAutoClose(true); progress->setMinimumDuration(0); progress->setMaximum(0); progress->setMinimum(0); progress->setModal(true); progress->setCancelButton(nullptr); progress->setWindowTitle(i18nc("@title:window", "Generating Keys")); progress->setLabel(new QLabel(i18n("This may take several minutes..."))); GenKeyThread *workerThread = new GenKeyThread(params, mRealSerial); connect(workerThread, &QThread::finished, this, [this, workerThread, progress] { progress->accept(); progress->deleteLater(); genKeyDone(workerThread->error(), workerThread->bkpFile()); delete workerThread; }); workerThread->start(); progress->exec(); } void PGPCardWidget::genKeyDone(const GpgME::Error &err, const std::string &backup) { if (err) { KMessageBox::error(this, i18nc("@info", "Failed to generate new key: %1", QString::fromLatin1(err.asString())), i18nc("@title", "Error")); return; } if (err.isCanceled()) { return; } if (!backup.empty()) { const auto bkpFile = QString::fromStdString(backup); QFileInfo fi(bkpFile); const auto target = QFileDialog::getSaveFileName(this, i18n("Save backup of encryption key"), fi.fileName(), QStringLiteral("%1 (*.gpg)").arg(i18n("Backup Key"))); if (!target.isEmpty() && !QFile::copy(bkpFile, target)) { KMessageBox::error(this, i18nc("@info", "Failed to move backup. The backup key is still stored under: %1", bkpFile), i18nc("@title", "Error")); } else if (!target.isEmpty()) { QFile::remove(bkpFile); } } KMessageBox::information(this, i18nc("@info", "Successfully generated a new key for this card."), i18nc("@title", "Success")); } void PGPCardWidget::genkeyRequested() { if (!mCardIsEmpty) { auto ret = KMessageBox::warningContinueCancel(this, i18n("The existing keys on this card will be deleted " "and replaced by new keys.") + QStringLiteral("

") + i18n("It will no longer be possible to decrypt past communication " "encrypted for the existing key."), i18n("Secret Key Deletion"), KStandardGuiItem::guiItem(KStandardGuiItem::Delete), KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous); if (ret != KMessageBox::Continue) { return; } } GenCardKeyDialog *dlg = new GenCardKeyDialog(GenCardKeyDialog::AllKeyAttributes, this); std::vector> algos = { { "1024", QStringLiteral("RSA 1024") }, { "2048", QStringLiteral("RSA 2048") }, { "3072", QStringLiteral("RSA 3072") } }; // There is probably a better way to check for capabilities if (mIs21) { algos.push_back({"4096", QStringLiteral("RSA 4096")}); } dlg->setSupportedAlgorithms(algos, "2048"); connect(dlg, &QDialog::accepted, this, [this, dlg] () { doGenKey(dlg); dlg->deleteLater(); }); dlg->setModal(true); dlg->show(); } void PGPCardWidget::changeNameRequested() { QString text = mCardHolderLabel->text(); while (true) { bool ok = false; text = QInputDialog::getText(this, i18n("Change cardholder"), i18n("New name:"), QLineEdit::Normal, text, &ok, Qt::WindowFlags(), Qt::ImhLatinOnly); if (!ok) { return; } // Some additional restrictions imposed by gnupg if (text.contains(QLatin1Char('<'))) { KMessageBox::error(this, i18nc("@info", "The \"<\" character may not be used."), i18nc("@title", "Error")); continue; } if (text.contains(QLatin1String(" "))) { KMessageBox::error(this, i18nc("@info", "Double spaces are not allowed"), i18nc("@title", "Error")); continue; } if (text.size() > 38) { KMessageBox::error(this, i18nc("@info", "The size of the name may not exceed 38 characters."), i18nc("@title", "Error")); } break; } auto parts = text.split(QLatin1Char(' ')); const auto lastName = parts.takeLast(); const QString formatted = lastName + QStringLiteral("<<") + parts.join(QLatin1Char('<')); const auto pgpCard = ReaderStatus::instance()->getCard(mRealSerial); if (!pgpCard) { KMessageBox::error(this, i18n("Failed to find the OpenPGP card with the serial number: %1", QString::fromStdString(mRealSerial))); return; } const QByteArray command = QByteArrayLiteral("SCD SETATTR DISP-NAME ") + formatted.toUtf8(); ReaderStatus::mutableInstance()->startSimpleTransaction(pgpCard, command, this, "changeNameResult"); } void PGPCardWidget::changeNameResult(const GpgME::Error &err) { if (err) { KMessageBox::error(this, i18nc("@info", "Name change failed: %1", QString::fromLatin1(err.asString())), i18nc("@title", "Error")); return; } if (!err.isCanceled()) { KMessageBox::information(this, i18nc("@info", "Name successfully changed."), i18nc("@title", "Success")); ReaderStatus::mutableInstance()->updateStatus(); } } void PGPCardWidget::changeUrlRequested() { QString text = mUrl; while (true) { bool ok = false; text = QInputDialog::getText(this, i18n("Change the URL where the pubkey can be found"), i18n("New pubkey URL:"), QLineEdit::Normal, text, &ok, Qt::WindowFlags(), Qt::ImhLatinOnly); if (!ok) { return; } // Some additional restrictions imposed by gnupg if (text.size() > 254) { KMessageBox::error(this, i18nc("@info", "The size of the URL may not exceed 254 characters."), i18nc("@title", "Error")); } break; } const auto pgpCard = ReaderStatus::instance()->getCard(mRealSerial); if (!pgpCard) { KMessageBox::error(this, i18n("Failed to find the OpenPGP card with the serial number: %1", QString::fromStdString(mRealSerial))); return; } const QByteArray command = QByteArrayLiteral("SCD SETATTR PUBKEY-URL ") + text.toUtf8(); ReaderStatus::mutableInstance()->startSimpleTransaction(pgpCard, command, this, "changeUrlResult"); } void PGPCardWidget::changeUrlResult(const GpgME::Error &err) { if (err) { KMessageBox::error(this, i18nc("@info", "URL change failed: %1", QString::fromLatin1(err.asString())), i18nc("@title", "Error")); return; } if (!err.isCanceled()) { KMessageBox::information(this, i18nc("@info", "URL successfully changed."), i18nc("@title", "Success")); ReaderStatus::mutableInstance()->updateStatus(); } } void PGPCardWidget::updateKey(QLabel *label, const std::string &fpr) { label->setText(QString::fromStdString(fpr)); if (fpr.empty()) { label->setText(i18n("Slot empty")); return; } std::vector vec; std::string keyid = fpr; keyid.erase(0, keyid.size() - 16); vec.push_back(keyid); const auto subkeys = KeyCache::instance()->findSubkeysByKeyID(vec); if (subkeys.empty() || subkeys[0].isNull()) { label->setToolTip(i18n("Public key not found.")); return; } QStringList toolTips; toolTips.reserve(subkeys.size()); for (const auto &sub: subkeys) { // Yep you can have one subkey associated with multiple // primary keys. toolTips << Formatting::toolTip(sub.parent(), Formatting::Validity | Formatting::StorageLocation | Formatting::ExpiryDates | Formatting::UserIDs | Formatting::Fingerprint); } label->setToolTip(toolTips.join(QLatin1String("
"))); return; } #include "pgpcardwidget.moc" diff --git a/src/view/smartcardwidget.cpp b/src/view/smartcardwidget.cpp index 5bc39bc9e..c31509569 100644 --- a/src/view/smartcardwidget.cpp +++ b/src/view/smartcardwidget.cpp @@ -1,193 +1,203 @@ /* view/smartcardwidget.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 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include "smartcardwidget.h" #include "smartcard/readerstatus.h" #include "smartcard/openpgpcard.h" #include "smartcard/netkeycard.h" #include "smartcard/pivcard.h" #include "smartcard/utils.h" #include "view/pgpcardwidget.h" #include "view/netkeywidget.h" #include "view/pivcardwidget.h" #include "kleopatra_debug.h" #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::SmartCard; namespace { class PlaceHolderWidget: public QWidget { Q_OBJECT public: explicit PlaceHolderWidget(QWidget *parent = nullptr) : QWidget(parent) { auto lay = new QVBoxLayout; lay->addStretch(-1); const QStringList supported = QStringList() << QStringLiteral("OpenPGP v2.0 - v3.3") << QStringLiteral("Gnuk") << QStringLiteral("NetKey v3") << QStringLiteral("PIV"); lay->addWidget(new QLabel(QStringLiteral("\t\t

") + i18n("Please insert a compatible smartcard.") + QStringLiteral("

"), this)); lay->addSpacing(10); lay->addWidget(new QLabel(QStringLiteral("\t\t") + i18n("Kleopatra currently supports the following card types:") + QStringLiteral("
  • ") + supported.join(QLatin1String("
  • ")) + QStringLiteral("
"), this)); lay->addSpacing(10); lay->addWidget(new QLabel(i18n("Refresh the view (F5) to update the smartcard status."), this)); lay->addStretch(-1); auto hLay = new QHBoxLayout(this); hLay->addStretch(-1); hLay->addLayout(lay); hLay->addStretch(-1); lay->addStretch(-1); } }; } // namespace class SmartCardWidget::Private { public: Private(SmartCardWidget *qq); void cardAddedOrChanged(const std::string &serialNumber, const std::string &appName); void cardRemoved(const std::string &serialNumber, const std::string &appName); private: template void cardAddedOrChanged(const std::string &serialNumber); private: SmartCardWidget *const q; QMap, QPointer > mCardWidgets; PlaceHolderWidget *mPlaceHolderWidget; QStackedWidget *mStack; QTabWidget *mTabWidget; }; SmartCardWidget::Private::Private(SmartCardWidget *qq) : q(qq) { QPushButton *backBtn = new QPushButton(QIcon::fromTheme(QStringLiteral("arrow-left")), i18n("Back")); QHBoxLayout *backH = new QHBoxLayout; backH->addWidget(backBtn); backH->addWidget(new QLabel(QStringLiteral("

") + i18n("Smartcard Management") + QStringLiteral("

"))); backH->addStretch(-1); QVBoxLayout *vLay = new QVBoxLayout(q); connect(backBtn, &QPushButton::clicked, q, [this] () {Q_EMIT q->backRequested();}); vLay->addLayout(backH); mStack = new QStackedWidget; vLay->addWidget(mStack); mPlaceHolderWidget = new PlaceHolderWidget; mStack->addWidget(mPlaceHolderWidget); mTabWidget = new QTabWidget; mStack->addWidget(mTabWidget); mStack->setCurrentWidget(mPlaceHolderWidget); connect(ReaderStatus::instance(), &ReaderStatus::cardAdded, q, [this] (const std::string &serialNumber, const std::string &appName) { cardAddedOrChanged(serialNumber, appName); }); connect(ReaderStatus::instance(), &ReaderStatus::cardChanged, q, [this] (const std::string &serialNumber, const std::string &appName) { cardAddedOrChanged(serialNumber, appName); }); connect(ReaderStatus::instance(), &ReaderStatus::cardRemoved, q, [this] (const std::string &serialNumber, const std::string &appName) { cardRemoved(serialNumber, appName); }); } void SmartCardWidget::Private::cardAddedOrChanged(const std::string &serialNumber, const std::string &appName) { if (appName == SmartCard::NetKeyCard::AppName) { cardAddedOrChanged(serialNumber); } else if (appName == SmartCard::OpenPGPCard::AppName) { cardAddedOrChanged(serialNumber); } else if (appName == SmartCard::PIVCard::AppName) { cardAddedOrChanged(serialNumber); } else { qCWarning(KLEOPATRA_LOG) << "SmartCardWidget::Private::cardAddedOrChanged:" << "App" << appName.c_str() << "is not supported"; } } +namespace { +static QString getCardLabel(const std::shared_ptr &card) { + if (!card->cardHolder().isEmpty()) { + return i18nc("@title:tab smartcard application - name of card holder - serial number of smartcard", "%1 - %2 - %3", + displayAppName(card->appName()), card->cardHolder(), card->displaySerialNumber()); + } else { + return i18nc("@title:tab smartcard application - serial number of smartcard", "%1 - %2", + displayAppName(card->appName()), card->displaySerialNumber()); + } +} +} + template void SmartCardWidget::Private::cardAddedOrChanged(const std::string &serialNumber) { const auto card = ReaderStatus::instance()->getCard(serialNumber); if (!card) { qCWarning(KLEOPATRA_LOG) << "SmartCardWidget::Private::cardAddedOrChanged:" << "New or changed card" << serialNumber.c_str() << "with app" << C::AppName.c_str() << "not found"; return; } W *cardWidget = dynamic_cast(mCardWidgets.value({serialNumber, C::AppName}).data()); if (!cardWidget) { cardWidget = new W; mCardWidgets.insert({serialNumber, C::AppName}, cardWidget); - const QString cardLabel = i18nc("@title:tab smartcard application - serial number of smartcard", "%1 - %2", - displayAppName(C::AppName), card->displaySerialNumber()); - mTabWidget->addTab(cardWidget, cardLabel); + mTabWidget->addTab(cardWidget, getCardLabel(card)); if (mCardWidgets.size() == 1) { mStack->setCurrentWidget(mTabWidget); } } cardWidget->setCard(card.get()); } void SmartCardWidget::Private::cardRemoved(const std::string &serialNumber, const std::string &appName) { QWidget * cardWidget = mCardWidgets.take({serialNumber, appName}); if (cardWidget) { const int index = mTabWidget->indexOf(cardWidget); if (index != -1) { mTabWidget->removeTab(index); } delete cardWidget; } if (mCardWidgets.empty()) { mStack->setCurrentWidget(mPlaceHolderWidget); } } SmartCardWidget::SmartCardWidget(QWidget *parent): QWidget(parent), d(new Private(this)) { } void SmartCardWidget::reload() { ReaderStatus::mutableInstance()->updateStatus(); } #include "smartcardwidget.moc"