diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index dc30079c6..2a6dc3399 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,211 +1,214 @@ # target_include_directories does not handle empty include paths include_directories(${GPGME_INCLUDES}) add_definitions(-DTRANSLATION_DOMAIN=\"libkleopatra\") #add_definitions( -DQT_NO_CAST_FROM_ASCII ) #add_definitions( -DQT_NO_CAST_TO_ASCII ) kde_enable_exceptions() add_definitions( -DGPGMEPP_ERR_SOURCE_DEFAULT=13 ) # 13 is GPG_ERR_SOURCE_KLEO, even if gpg-error's too old to know about add_subdirectory( pics ) if (BUILD_TESTING) add_subdirectory( tests ) endif() ########### next target ############### set(libkleo_core_SRCS kleo/checksumdefinition.cpp kleo/defaultkeyfilter.cpp kleo/defaultkeygenerationjob.cpp kleo/dn.cpp kleo/enum.cpp kleo/exception.cpp kleo/kconfigbasedkeyfilter.cpp kleo/keyfiltermanager.cpp kleo/keyresolver.cpp models/keycache.cpp models/keylistmodel.cpp models/keylistsortfilterproxymodel.cpp models/keyrearrangecolumnsproxymodel.cpp models/subkeylistmodel.cpp models/useridlistmodel.cpp utils/filesystemwatcher.cpp utils/formatting.cpp utils/classify.cpp utils/gnupg.cpp utils/hex.cpp + smartcard/card.cpp + smartcard/openpgpcard.cpp + smartcard/netkeycard.cpp ) ecm_qt_declare_logging_category(libkleo_core_SRCS HEADER libkleo_debug.h IDENTIFIER LIBKLEO_LOG CATEGORY_NAME org.kde.pim.libkleo) set(libkleo_ui_common_SRCS ui/dnattributeorderconfigwidget.cpp ui/kdhorizontalline.cpp ui/filenamerequester.cpp ui/messagebox.cpp ui/cryptoconfigmodule.cpp ui/cryptoconfigdialog.cpp ui/directoryserviceswidget.cpp ui/progressbar.cpp ui/progressdialog.cpp ui/auditlogviewer.cpp ) ecm_qt_declare_logging_category(libkleo_ui_common_SRCS HEADER kleo_ui_debug.h IDENTIFIER KLEO_UI_LOG CATEGORY_NAME org.kde.pim.kleo_ui) set(libkleo_ui_SRCS # make this a separate lib. ui/keylistview.cpp ui/keyselectiondialog.cpp ui/keyrequester.cpp ui/keyapprovaldialog.cpp ui/newkeyapprovaldialog.cpp ui/keyselectioncombo.cpp ) ki18n_wrap_ui(libkleo_ui_common_SRCS ui/directoryserviceswidget.ui ) set(kleo_LIB_SRCS ${libkleo_core_SRCS} ${libkleo_ui_SRCS} ${libkleo_ui_common_SRCS}) set(kleo_LIB_LIBS PUBLIC QGpgme Gpgmepp PRIVATE Qt5::Widgets KF5::I18n KF5::Completion KF5::ConfigCore KF5::CoreAddons KF5::WidgetsAddons KF5::ItemModels KF5::Codecs) if (KF5PimTextEdit_FOUND) add_definitions(-DHAVE_PIMTEXTEDIT) set(kleo_LIB_LIBS ${kleo_LIB_LIBS} PRIVATE KF5::PimTextEdit) endif() add_library(KF5Libkleo ${kleo_LIB_SRCS}) generate_export_header(KF5Libkleo BASE_NAME kleo) add_library(KF5::Libkleo ALIAS KF5Libkleo) if(WIN32) target_link_libraries(KF5Libkleo ${kleo_LIB_LIBS} ${GPGME_VANILLA_LIBRARIES} ) else() target_link_libraries(KF5Libkleo ${kleo_LIB_LIBS} ) endif() set_target_properties(KF5Libkleo PROPERTIES VERSION ${LIBKLEO_VERSION_STRING} SOVERSION ${LIBKLEO_SOVERSION} EXPORT_NAME Libkleo ) install(TARGETS KF5Libkleo EXPORT KF5LibkleoTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS} ${LIBRARY_NAMELINK} ) target_include_directories(KF5Libkleo PUBLIC "$") target_include_directories(KF5Libkleo INTERFACE "$") ecm_generate_headers(libkleo_CamelCase_HEADERS HEADER_NAMES ChecksumDefinition DefaultKeyFilter DefaultKeyGenerationJob Dn Enum Exception KConfigBasedKeyFilter KeyFilter KeyFilterManager KeyResolver OidMap Predicates Stl_Util REQUIRED_HEADERS libkleo_HEADERS PREFIX Libkleo RELATIVE kleo ) ecm_generate_headers(libkleo_CamelCase_models_HEADERS HEADER_NAMES KeyCache KeyListModel KeyListModelInterface KeyListSortFilterProxyModel KeyRearrangeColumnsProxyModel SubkeyListModel UserIDListModel REQUIRED_HEADERS libkleo_models_HEADERS PREFIX Libkleo RELATIVE models ) ecm_generate_headers(libkleo_CamelCase_utils_HEADERS HEADER_NAMES Classify FileSystemWatcher Formatting GnuPG REQUIRED_HEADERS libkleo_utils_HEADERS PREFIX Libkleo RELATIVE utils ) ecm_generate_headers(libkleo_CamelCase_ui_HEADERS HEADER_NAMES CryptoConfigDialog CryptoConfigModule DNAttributeOrderConfigWidget DirectoryServicesWidget FileNameRequester KDHorizontalLine KeyApprovalDialog NewKeyApprovalDialog KeyRequester KeySelectionCombo KeySelectionDialog MessageBox ProgressDialog REQUIRED_HEADERS libkleo_ui_HEADERS PREFIX Libkleo RELATIVE ui ) ecm_generate_pri_file(BASE_NAME Libkleo LIB_NAME KF5Libkleo DEPS "QGpgme" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/Libkleo ) install(FILES ${libkleo_CamelCase_HEADERS} ${libkleo_CamelCase_models_HEADERS} ${libkleo_CamelCase_ui_HEADERS} ${libkleo_CamelCase_utils_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/Libkleo COMPONENT Devel ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/kleo_export.h ${libkleo_HEADERS} ${libkleo_models_HEADERS} ${libkleo_ui_HEADERS} ${libkleo_utils_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/libkleo COMPONENT Devel ) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) if ( WIN32 ) install ( FILES libkleopatrarc-win32.desktop DESTINATION ${KDE_INSTALL_CONFDIR} RENAME libkleopatrarc ) else () install ( FILES libkleopatrarc.desktop DESTINATION ${KDE_INSTALL_CONFDIR} RENAME libkleopatrarc ) endif () diff --git a/src/smartcard/card.cpp b/src/smartcard/card.cpp new file mode 100644 index 000000000..32eeca901 --- /dev/null +++ b/src/smartcard/card.cpp @@ -0,0 +1,150 @@ +/* smartcard/card.h + + This file is part of Kleopatra, the KDE keymanager + Copyright (c) 2017 by Bundesamt für Sicherheit in der Informationstechnik + Software engineering by Intevation GmbH + + Kleopatra is free software; you can redistribute it and/or modify + it under the terms of 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. + + Kleopatra is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "card.h" + +using namespace Kleo; +using namespace Kleo::SmartCard; + +Card::Card(): mCanLearn(false), + mHasNullPin(false), + mStatus(Status::NoCard), + mAppType(UnknownApplication), + mAppVersion(-1) { +} + +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; +} + +Card::AppType Card::appType() const +{ + return mAppType; +} + +void Card::setAppType(AppType t) +{ + mAppType = t; +} + +void Card::setAppVersion(int version) +{ + mAppVersion = version; +} + +int Card::appVersion() const +{ + return mAppVersion; +} + +std::vector Card::pinStates() const +{ + return mPinStates; +} + +void Card::setPinStates(const std::vector &pinStates) +{ + mPinStates = pinStates; +} + +void Card::setSlot(int slot) +{ + mSlot = slot; +} + +int Card::slot() const +{ + return mSlot; +} + +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() + && mAppType == other.appType() + && mAppVersion == other.appVersion() + && mPinStates == other.pinStates() + && mSlot == other.slot() + && 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; +} diff --git a/src/smartcard/card.h b/src/smartcard/card.h new file mode 100644 index 000000000..a82ec3697 --- /dev/null +++ b/src/smartcard/card.h @@ -0,0 +1,130 @@ +#ifndef SMARTCARD_CARD_H +#define SMARTCARD_CARD_H +/* smartcard/card.h + + This file is part of Kleopatra, the KDE keymanager + Copyright (c) 2017 by Bundesamt für Sicherheit in der Informationstechnik + Software engineering by Intevation GmbH + + Kleopatra is free software; you can redistribute it and/or modify + it under the terms of 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. + + Kleopatra is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include +#include + +#include + +namespace Kleo +{ +namespace SmartCard +{ +class ReaderStatus; +/** Class to work with Smartcards or other Hardware tokens. */ +class Card +{ +public: + enum AppType { + UnknownApplication, + OpenPGPApplication, + NksApplication, + P15Application, + DinSigApplication, + GeldkarteApplication, + + NumAppTypes + }; + + 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; + + virtual void setSerialNumber(const std::string &sn); + std::string serialNumber() const; + + AppType appType() const; + void setAppType(AppType type); + + void setAppVersion(int version); + int appVersion() const; + + std::vector pinStates() const; + void setPinStates(const std::vector &pinStates); + + void setSlot(int slot); + int slot() const; + + bool hasNullPin() const; + void setHasNullPin(bool value); + + bool canLearnKeys() const; + void setCanLearnKeys(bool value); + + QString errorMsg() const; + void setErrorMsg(const QString &msg); + +private: + bool mCanLearn; + bool mHasNullPin; + Status mStatus; + std::string mSerialNumber; + AppType mAppType; + int mAppVersion; + std::vector mPinStates; + int mSlot; + QString mErrMsg; +}; +} // namespace Smartcard +} // namespace Kleopatra + +#endif // SMARTCARD_CARD_H diff --git a/src/smartcard/netkeycard.cpp b/src/smartcard/netkeycard.cpp new file mode 100644 index 000000000..dff70c575 --- /dev/null +++ b/src/smartcard/netkeycard.cpp @@ -0,0 +1,129 @@ +/* smartcard/netkeycard.cpp + + This file is part of Kleopatra, the KDE keymanager + Copyright (c) 2017 Intevation GmbH + + Kleopatra is free software; you can redistribute it and/or modify + it under the terms of 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. + + Kleopatra is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include "netkeycard.h" + +#include "libkleo_debug.h" + +#include +#include +#include + +#include +#include + +using namespace Kleo; +using namespace Kleo::SmartCard; + +namespace +{ +static std::string parse_keypairinfo(const std::string &kpi) +{ + static const char hexchars[] = "0123456789abcdefABCDEF"; + return '&' + kpi.substr(0, kpi.find_first_not_of(hexchars)); +} + +static GpgME::Key parse_keypairinfo_and_lookup_key(GpgME::Context *ctx, const std::string &kpi) +{ + if (!ctx) { + return GpgME::Key(); + } + const std::string pattern = parse_keypairinfo(kpi); + qCDebug(LIBKLEO_LOG) << "parse_keypairinfo_and_lookup_key: pattern=" << pattern.c_str(); + if (const auto err = ctx->startKeyListing(pattern.c_str())) { + qCDebug(LIBKLEO_LOG) << "parse_keypairinfo_and_lookup_key: startKeyListing failed:" << err.asString(); + return GpgME::Key(); + } + GpgME::Error e; + const auto key = ctx->nextKey(e); + ctx->endKeyListing(); + qCDebug(LIBKLEO_LOG) << "parse_keypairinfo_and_lookup_key: e=" << e.code() << "; key.isNull()" << key.isNull(); + return key; +} + +} // namespace + +NetKeyCard::NetKeyCard() +{ + setAppType(Card::NksApplication); +} + +void NetKeyCard::setKeyPairInfo(const std::vector &infos) +{ + // check that any of the keys are new + const std::unique_ptr klc(GpgME::Context::createForProtocol(GpgME::CMS)); + if (!klc.get()) { + return; + } + klc->setKeyListMode(GpgME::Ephemeral); + klc->addKeyListMode(GpgME::Validate); + + setCanLearnKeys(false); + mKeys.clear(); + for (const auto &info: infos) { + const auto key = parse_keypairinfo_and_lookup_key(klc.get(), info); + if (key.isNull()) { + setCanLearnKeys(true); + } + mKeys.push_back(key); + } +} + + +// State 0 -> NKS PIN Retry counter +// State 1 -> NKS PUK Retry counter +// State 2 -> SigG PIN Retry counter +// State 3 -> SigG PUK Retry counter + +bool NetKeyCard::hasNKSNullPin() const +{ + const auto states = pinStates(); + if (states.size() < 2) { + qCWarning(LIBKLEO_LOG) << "Invalid size of pin states:" << states.size(); + return false; + } + return states[0] == Card::NullPin; +} + +bool NetKeyCard::hasSigGNullPin() const +{ + const auto states = pinStates(); + if (states.size() < 4) { + qCWarning(LIBKLEO_LOG) << "Invalid size of pin states:" << states.size(); + return false; + } + return states[2] == Card::NullPin; +} + +std::vector NetKeyCard::keys() const +{ + return mKeys; +} diff --git a/src/smartcard/netkeycard.h b/src/smartcard/netkeycard.h new file mode 100644 index 000000000..c4e2c9900 --- /dev/null +++ b/src/smartcard/netkeycard.h @@ -0,0 +1,65 @@ +#ifndef SMARTCARD_NETKEYCARD_H +#define SMARTCARD_NETKEYCARD_H +/* smartcard/openpgpcard.h + + This file is part of Kleopatra, the KDE keymanager + Copyright (c) 2017 Intevation GmbH + + Kleopatra is free software; you can redistribute it and/or modify + it under the terms of 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. + + Kleopatra is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + + +#include "card.h" + +#include + +namespace Kleo +{ +namespace SmartCard +{ +/** Class to work with OpenPGP Smartcards or compatible tokens */ +class NetKeyCard: public Card +{ +public: + NetKeyCard (); + + void setKeyPairInfo (const std::vector &infos); + + bool hasSigGNullPin() const; + bool hasNKSNullPin() const; + + std::vector keys() const; + +private: + class Private; + std::shared_ptr d; + std::vector mKeys; +}; +} // namespace Smartcard +} // namespace Kleopatra + +#endif // SMARTCARD_CARD_H + diff --git a/src/smartcard/openpgpcard.cpp b/src/smartcard/openpgpcard.cpp new file mode 100644 index 000000000..be7fc046b --- /dev/null +++ b/src/smartcard/openpgpcard.cpp @@ -0,0 +1,236 @@ +/* smartcard/openpgpcard.cpp + + This file is part of Kleopatra, the KDE keymanager + Copyright (c) 2017 by Bundesamt für Sicherheit in der Informationstechnik + Software engineering by Intevation GmbH + + Kleopatra is free software; you can redistribute it and/or modify + it under the terms of 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. + + Kleopatra is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +/* Code in this file is partly based on the GNU Privacy Assistant + * (cm-openpgp.c) git rev. 0a78795146661234070681737b3e08228616441f + * + * Whis is: + * Copyright (C) 2008, 2009 g10 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 "libkleo_debug.h" + + +using namespace Kleo; +using namespace Kleo::SmartCard; + +#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ + *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) +#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) + +namespace +{ +static const char * get_manufacturer (unsigned int no) +{ + switch (no) { + case 0x0001: return "PPC Card Systems"; + case 0x0002: return "Prism"; + case 0x0003: return "OpenFortress"; + case 0x0004: return "Wewid"; + case 0x0005: return "ZeitControl"; + case 0x0006: return "Yubico"; + case 0x0007: return "OpenKMS"; + case 0x0008: return "LogoEmail"; + + case 0x002A: return "Magrathea"; + + case 0x1337: return "Warsaw Hackerspace"; + + case 0xF517: return "FSIJ"; + + /* 0x0000 and 0xFFFF are defined as test cards per spec, + 0xFF00 to 0xFFFE are assigned for use with randomly created + serial numbers. */ + case 0x0000: + case 0xffff: return "test card"; + default: return (no & 0xff00) == 0xff00? "unmanaged S/N range":"unknown"; + } +} + +} // namespace + +OpenPGPCard::OpenPGPCard() +{ + setAppType(Card::OpenPGPApplication); +} + +OpenPGPCard::OpenPGPCard(const std::string &serialno): OpenPGPCard() +{ + setSerialNumber(serialno); +} + +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::setKeyPairInfo(const std::vector< std::pair > &infos) +{ + qCDebug(LIBKLEO_LOG) << "Card" << serialNumber().c_str() << "info:"; + for (const auto &pair: infos) { + qCDebug(LIBKLEO_LOG) << pair.first.c_str() << ":" << pair.second.c_str(); + 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(LIBKLEO_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(LIBKLEO_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(LIBKLEO_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(LIBKLEO_LOG) << "Unhandled keyslot"; + } + } else { + mMetaInfo.insert(pair.first, pair.second); + } + } +} + +void OpenPGPCard::setSerialNumber(const std::string &serialno) +{ + char version_buffer[6]; + const char *version = ""; + const char *string = serialno.c_str(); + + Card::setSerialNumber(serialno); + if (strncmp(string, "D27600012401", 12) || strlen(string) != 32 ) { + /* Not a proper OpenPGP card serialnumber. Display the full + serialnumber. */ + mManufacturer = "unknown"; + } else { + /* Reformat the version number to be better human readable. */ + char *p = version_buffer; + if (string[12] != '0') { + *p++ = string[12]; + } + *p++ = string[13]; + *p++ = '.'; + if (string[14] != '0') { + *p++ = string[14]; + } + *p++ = string[15]; + *p++ = '\0'; + version = version_buffer; + + /* Get the manufacturer. */ + mManufacturer = get_manufacturer(xtoi_2(string + 16)*256 + xtoi_2(string + 18)); + } + + mIsV2 = !((*version == '1' || *version == '0') && version[1] == '.'); + mCardVersion = version; +} + +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() + && cardVersion() == other->cardVersion() + && cardHolder() == other->cardHolder() + && pubkeyUrl() == other->pubkeyUrl(); +} + +std::string OpenPGPCard::manufacturer() const +{ + return mManufacturer; +} + +std::string OpenPGPCard::cardVersion() const +{ + return mCardVersion; +} + +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 new file mode 100644 index 000000000..65c3e0e68 --- /dev/null +++ b/src/smartcard/openpgpcard.h @@ -0,0 +1,73 @@ +#ifndef SMARTCARD_OPENPGPCARD_H +#define SMARTCARD_OPENPGPCARD_H +/* smartcard/openpgpcard.h + + This file is part of Kleopatra, the KDE keymanager + Copyright (c) 2017 by Bundesamt für Sicherheit in der Informationstechnik + Software engineering by Intevation GmbH + + Kleopatra is free software; you can redistribute it and/or modify + it under the terms of 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. + + Kleopatra is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + In addition, as a special exception, the copyright holders give + permission to link the code of this program with any edition of + the Qt library by Trolltech AS, Norway (or with modified versions + of Qt that use the same license as Qt), and distribute linked + combinations including the two. You must obey the GNU General + Public License in all respects for all of the code used other than + Qt. If you modify this file, you may extend this exception to + your version of the file, but you are not obligated to do so. If + you do not wish to do so, delete this exception statement from + your version. +*/ + +#include + +#include "card.h" + +namespace Kleo +{ +namespace SmartCard +{ +/** Class to work with OpenPGP Smartcards or compatible tokens */ +class OpenPGPCard: public Card +{ +public: + OpenPGPCard (); + OpenPGPCard (const std::string &serialno); + void setSerialNumber(const std::string &sn) override; + + std::string encFpr() const; + std::string sigFpr() const; + std::string authFpr() const; + + void setKeyPairInfo (const std::vector< std::pair > &infos); + + bool operator == (const Card& other) const override; + + std::string manufacturer() const; + std::string cardVersion() const; + std::string cardHolder() const; + std::string pubkeyUrl() const; +private: + bool mIsV2 = false; + std::string mCardVersion; + QMap mMetaInfo; + std::string mManufacturer; +}; +} // namespace Smartcard +} // namespace Kleopatra + +#endif // SMARTCARD_CARD_H +