diff --git a/src/smartcard/card.cpp b/src/smartcard/card.cpp index c321c4b3f..dbcda8813 100644 --- a/src/smartcard/card.cpp +++ b/src/smartcard/card.cpp @@ -1,208 +1,246 @@ /* 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" +#include "kleopatra_debug.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::PinState> Card::pinStates() const { return mPinStates; } void Card::setPinStates(const std::vector<PinState> &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; } +const std::vector<KeyPairInfo> & Card::keyInfos() const +{ + return mKeyInfos; +} + +const KeyPairInfo & Card::keyInfo(const std::string &keyRef) const +{ + static const KeyPairInfo nullKey; + for (const KeyPairInfo &k : mKeyInfos) { + if (k.keyRef == keyRef) { + return k; + } + } + return nullKey; +} + 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; + } else if (name == "KEYPAIRINFO") { + const KeyPairInfo info = KeyPairInfo::fromStatusLine(value); + if (info.grip.empty()) { + qCWarning(KLEOPATRA_LOG) << "Invalid KEYPAIRINFO status line" << QString::fromStdString(value); + setStatus(Card::CardError); + } else { + updateKeyInfo(info); + } + return true; } return false; } + +void Card::updateKeyInfo(const KeyPairInfo& keyPairInfo) +{ + for (KeyPairInfo &k : mKeyInfos) { + if (k.keyRef == keyPairInfo.keyRef) { + k.update(keyPairInfo); + return; + } + } + mKeyInfos.push_back(keyPairInfo); +} diff --git a/src/smartcard/card.h b/src/smartcard/card.h index 376deff73..d6bba4a19 100644 --- a/src/smartcard/card.h +++ b/src/smartcard/card.h @@ -1,110 +1,120 @@ #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 "keypairinfo.h" + #include <string> #include <vector> #include <QString> 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<PinState> pinStates() const; void setPinStates(const std::vector<PinState> &pinStates); bool hasNullPin() const; void setHasNullPin(bool value); bool canLearnKeys() const; void setCanLearnKeys(bool value); QString errorMsg() const; void setErrorMsg(const QString &msg); + const std::vector<KeyPairInfo> & keyInfos() const; + const KeyPairInfo & keyInfo(const std::string &keyRef) const; + protected: void setAppName(const std::string &name); bool parseCardInfo(const std::string &name, const std::string &value); +private: + void updateKeyInfo(const KeyPairInfo &keyPairInfo); + 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<PinState> mPinStates; QString mErrMsg; + std::vector<KeyPairInfo> mKeyInfos; }; } // namespace Smartcard } // namespace Kleopatra #endif // SMARTCARD_CARD_H diff --git a/src/smartcard/keypairinfo.cpp b/src/smartcard/keypairinfo.cpp index 64c2ebd76..ab8d18b8d 100644 --- a/src/smartcard/keypairinfo.cpp +++ b/src/smartcard/keypairinfo.cpp @@ -1,39 +1,64 @@ /* smartcard/keypairinfo.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2020 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de> SPDX-License-Identifier: GPL-2.0-or-later */ #include "keypairinfo.h" #include <QString> #include <QStringList> using namespace Kleo::SmartCard; // static KeyPairInfo KeyPairInfo::fromStatusLine(const std::string &s) { // The format of a KEYPAIRINFO line is // KEYPAIRINFO <hexgrip> <keyref> [usage] [keytime] [algostr] // The string s does not contain the leading "KEYPAIRINFO ". KeyPairInfo info; const auto values = QString::fromStdString(s).split(QLatin1Char(' ')); if (values.size() < 2) { return info; } info.grip = values[0].toStdString(); info.keyRef = values[1].toStdString(); if (values.size() >= 3) { info.usage = values[2].toStdString(); } if (values.size() >= 4) { info.keyTime = values[3].toStdString(); } if (values.size() >= 5) { info.algorithm = values[4].toStdString(); } return info; } + +void KeyPairInfo::update(const KeyPairInfo &other) +{ + Q_ASSERT(keyRef == other.keyRef); + if (keyRef != other.keyRef) { + return; + } + if (grip != other.grip) { + // reset all infos if the grip changed + grip = other.grip; + usage = std::string(); + keyTime = std::string(); + algorithm = std::string(); + } + // now update all infos from other's infos unless other's infos are empty or not specified + if (!other.usage.empty() && other.usage != "-") { + usage = other.usage; + } + if (!other.keyTime.empty() && other.keyTime != "-") { + keyTime = other.keyTime; + } + if (!other.algorithm.empty() && other.algorithm != "-") { + algorithm = other.algorithm; + } +} diff --git a/src/smartcard/keypairinfo.h b/src/smartcard/keypairinfo.h index 3e3e8bc09..3b83cf9b8 100644 --- a/src/smartcard/keypairinfo.h +++ b/src/smartcard/keypairinfo.h @@ -1,30 +1,32 @@ /* smartcard/keypairinfo.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2020 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de> SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef SMARTCARD_KEYPAIRINFO_H #define SMARTCARD_KEYPAIRINFO_H #include <string> namespace Kleo { namespace SmartCard { struct KeyPairInfo { static KeyPairInfo fromStatusLine(const std::string &s); + void update(const KeyPairInfo &other); + std::string grip; std::string keyRef; std::string usage; std::string keyTime; std::string algorithm; }; } // namespace Smartcard } // namespace Kleopatra #endif // SMARTCARD_PIVCARD_H diff --git a/src/smartcard/openpgpcard.cpp b/src/smartcard/openpgpcard.cpp index 2472d7ef9..043625752 100644 --- a/src/smartcard/openpgpcard.cpp +++ b/src/smartcard/openpgpcard.cpp @@ -1,156 +1,136 @@ /* 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<std::string, std::string> > &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<const OpenPGPCard *>(&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::pubkeyUrl() const { return mMetaInfo.value("PUBKEY-URL"); } diff --git a/src/smartcard/pivcard.cpp b/src/smartcard/pivcard.cpp index 136b1ea69..f918c05eb 100644 --- a/src/smartcard/pivcard.cpp +++ b/src/smartcard/pivcard.cpp @@ -1,173 +1,162 @@ /* smartcard/pivcard.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2020 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de> SPDX-License-Identifier: GPL-2.0-or-later */ #include "pivcard.h" #include "keypairinfo.h" #include <KLocalizedString> #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::SmartCard; // static const std::string PIVCard::AppName = "piv"; PIVCard::PIVCard(const Card &card) : Card(card) { setAppName(AppName); } // static std::string PIVCard::pivAuthenticationKeyRef() { return std::string("PIV.9A"); } // static std::string PIVCard::cardAuthenticationKeyRef() { return std::string("PIV.9E"); } // static std::string PIVCard::digitalSignatureKeyRef() { return std::string("PIV.9C"); } // static std::string PIVCard::keyManagementKeyRef() { return std::string("PIV.9D"); } // static std::string PIVCard::pinKeyRef() { return std::string("PIV.80"); } // static std::string PIVCard::pukKeyRef() { return std::string("PIV.81"); } // static const std::vector<std::string> & PIVCard::supportedKeys() { static const std::vector<std::string> keyRefs = { PIVCard::pivAuthenticationKeyRef(), PIVCard::cardAuthenticationKeyRef(), PIVCard::digitalSignatureKeyRef(), PIVCard::keyManagementKeyRef() }; return keyRefs; } // static QString PIVCard::keyDisplayName(const 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); } // 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); + return keyInfo(keyRef).grip; } void PIVCard::setCardInfo(const std::vector< std::pair<std::string, std::string> > &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 == "KEYPAIRINFO") { - const KeyPairInfo info = KeyPairInfo::fromStatusLine(pair.second); - if (info.grip.empty()) { - qCWarning(KLEOPATRA_LOG) << "Invalid KEYPAIRINFO status line" - << QString::fromStdString(pair.second); - setStatus(Card::CardError); - continue; - } - mMetaInfo.insert("KEYPAIRINFO-" + info.keyRef, info.grip); - } else { - mMetaInfo.insert(pair.first, pair.second); - } + mMetaInfo.insert(pair.first, pair.second); } } std::string PIVCard::keyAlgorithm(const std::string &keyRef) const { return mMetaInfo.value("KLEO-KEYALGO-" + keyRef); } void PIVCard::setKeyAlgorithm(const std::string &keyRef, const std::string &algorithm) { mMetaInfo.insert("KLEO-KEYALGO-" + keyRef, algorithm); } std::string PIVCard::certificateData(const std::string &keyRef) const { return mMetaInfo.value("KLEO-CERTIFICATE-" + keyRef); } void PIVCard::setCertificateData(const std::string &keyRef, const std::string &data) { mMetaInfo.insert("KLEO-CERTIFICATE-" + keyRef, data); } bool PIVCard::operator == (const Card& rhs) const { const PIVCard *other = dynamic_cast<const PIVCard *>(&rhs); if (!other) { return false; } return Card::operator ==(rhs) && mMetaInfo == other->mMetaInfo; }