From 5e1254a2981b58c044b2ea1c8a84da4472b82e06 Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Thu, 17 Oct 2024 09:09:50 +0300 Subject: [PATCH] Move QPCSC to digidoc IB-7927 Signed-off-by: Raul Metsma --- .gitmodules | 3 - CMakeLists.txt | 13 +- Common.cpp | 110 +++++------ Common.h | 22 +-- Configuration.cpp | 15 +- QPCSC.cpp | 486 ---------------------------------------------- QPCSC.h | 131 ------------- QPCSC_p.h | 207 -------------------- cmake | 1 - 9 files changed, 72 insertions(+), 916 deletions(-) delete mode 100644 QPCSC.cpp delete mode 100644 QPCSC.h delete mode 100644 QPCSC_p.h delete mode 160000 cmake diff --git a/.gitmodules b/.gitmodules index 188b557..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "cmake"] - path = cmake - url = ../cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 2739a59..7fe2efc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,15 @@ cmake_minimum_required(VERSION 3.10) project(qdigidoccommon VERSION 1.0.0) if(NOT VERSION) - set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules) - include(VersionInfo) + macro(set_env) + set(${ARGN}) + endmacro() + add_compile_definitions( + MAJOR_VER=${PROJECT_VERSION_MAJOR} + MINOR_VER=${PROJECT_VERSION_MINOR} + RELEASE_VER=${PROJECT_VERSION_PATCH} + BUILD_VER=$,$ENV{BUILD_NUMBER},0> + ) endif() set_env( CONFIG_URL "https://id.eesti.ee/config.json" CACHE STRING "Set Config URL" ) find_package(OpenSSL 1.1.1 REQUIRED) @@ -22,7 +29,7 @@ configure_file( translations/common_tr.qrc common_tr.qrc COPYONLY ) qt_add_translation(SOURCES translations/qtbase_et.ts translations/qtbase_ru.ts translations/common_en.ts translations/common_et.ts translations/common_ru.ts) qt_add_resources(SOURCES ${CMAKE_CURRENT_BINARY_DIR}/common_tr.qrc) -add_library(qdigidoccommon STATIC ${SOURCES} Common.cpp QPCSC.cpp) +add_library(qdigidoccommon STATIC ${SOURCES} Common.cpp) target_include_directories(qdigidoccommon PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_compile_definitions(qdigidoccommon PUBLIC QT_DEPRECATED_WARNINGS_SINCE=051200) target_link_libraries(qdigidoccommon PUBLIC Qt${QT_VERSION_MAJOR}::Network Qt${QT_VERSION_MAJOR}::Widgets OpenSSL::SSL) diff --git a/Common.cpp b/Common.cpp index 807f714..b0127e4 100644 --- a/Common.cpp +++ b/Common.cpp @@ -19,48 +19,20 @@ #include "Common.h" -#include -#include -#include -#include #include -#include -#include #ifdef Q_OS_WIN #include -#endif - -#ifndef COMMON_STATIC -Common::Common( int &argc, char **argv, const QString &app, const QString &icon ) - : BaseApplication( argc, argv ) -{ - setApplicationName( app ); - setApplicationVersion(QStringLiteral("%1.%2.%3.%4") - .arg( MAJOR_VER ).arg( MINOR_VER ).arg( RELEASE_VER ).arg( BUILD_VER ) ); - setOrganizationDomain(QStringLiteral("ria.ee")); - setOrganizationName(QStringLiteral("RIA")); - setWindowIcon( QIcon( icon ) ); - if(QFile::exists(QStringLiteral("%1/%2.log").arg(QDir::tempPath(), app))) - qInstallMessageHandler(msgHandler); - - Q_INIT_RESOURCE(common_tr); -#if defined(Q_OS_WIN) - AllowSetForegroundWindow( ASFW_ANY ); -#ifdef NDEBUG - setLibraryPaths({ applicationDirPath() }); -#endif +#include +#include #elif defined(Q_OS_MAC) - qputenv("OPENSSL_CONF", applicationDirPath().toUtf8() + "../Resources/openssl.cnf"); -#ifdef NDEBUG - setLibraryPaths({ applicationDirPath() + "/../PlugIns" }); -#endif -#endif - setStyleSheet(QStringLiteral( - "QDialogButtonBox { dialogbuttonbox-buttons-have-icons: 0; }\n")); - - QNetworkProxyFactory::setUseSystemConfiguration(true); -} +#include +#include +#include +#else +#include +#include +#include #endif QString Common::applicationOs() @@ -93,28 +65,50 @@ QString Common::applicationOs() #endif } -void Common::msgHandler(QtMsgType type, const QMessageLogContext &ctx, const QString &msg) +QStringList Common::drivers() { - QFile f(QStringLiteral("%1/%2.log").arg(QDir::tempPath(), applicationName())); - if(!f.open( QFile::Append )) - return; - f.write(QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd hh:mm:ss ")).toUtf8()); - switch(type) - { - case QtDebugMsg: f.write("D"); break; - case QtWarningMsg: f.write("W"); break; - case QtCriticalMsg: f.write("C"); break; - case QtFatalMsg: f.write("F"); break; - default: f.write("I"); break; - } - f.write(QStringLiteral(" %1 ").arg(QLatin1String(ctx.category)).toUtf8()); - if(ctx.line > 0) + QStringList list; +#ifdef Q_OS_WIN + GUID guid {0x50dd5230L, 0xba8a, 0x11d1, 0xbf, 0x5d, 0x00, 0x00, 0xf8, 0x05, 0xf5, 0x30}; // SmartCardReader + HDEVINFO h = SetupDiGetClassDevs(&guid, nullptr, 0, DIGCF_PRESENT); + if(!h) + return list; + + SP_DEVINFO_DATA info { sizeof(SP_DEVINFO_DATA) }; + DWORD size = 0; + WCHAR data[1024]; + for(DWORD i = 0; SetupDiEnumDeviceInfo(h, i, &info); ++i) { - f.write(QStringLiteral("%1:%2 \"%3\" ") - .arg(QFileInfo(QString::fromLatin1(ctx.file)).fileName()) - .arg(ctx.line) - .arg(QLatin1String(ctx.function)).toUtf8()); + DWORD conf = 0; + SetupDiGetDeviceRegistryPropertyW(h, &info, + SPDRP_CONFIGFLAGS, 0, LPBYTE(&conf), sizeof(conf), &size); + if(conf & CONFIGFLAG_DISABLED) + continue; + + SetupDiGetDeviceRegistryPropertyW(h, &info, + SPDRP_DEVICEDESC, 0, LPBYTE(data), sizeof(data), &size); + QString name = QString::fromWCharArray(data); + + SetupDiGetDeviceRegistryPropertyW(h, &info, + SPDRP_HARDWAREID, 0, LPBYTE(data), sizeof(data), &size); + + list.append(QStringLiteral("%1 (%2)").arg(name, QString::fromWCharArray(data))); } - f.write(msg.toUtf8()); - f.write("\n"); + SetupDiDestroyDeviceInfoList(h); +#else + SCARDCONTEXT context{}; + SCardEstablishContext(DWORD(SCARD_SCOPE_USER), nullptr, nullptr, &context); + if(!context) + return list; + DWORD size{}; + if(SCardListReaders(context, nullptr, nullptr, &size) != SCARD_S_SUCCESS || !size) + return list; + QByteArray data(int(size), 0); + if(SCardListReaders(context, nullptr, data.data(), &size) != SCARD_S_SUCCESS) + data.clear(); + SCardReleaseContext(context); + list = QString::fromLatin1(data).split('\0'); + list.removeAll({}); +#endif + return list; } diff --git a/Common.h b/Common.h index e07b5fd..57cf5aa 100644 --- a/Common.h +++ b/Common.h @@ -19,28 +19,12 @@ #pragma once -#include +#include -#ifdef Q_OS_MAC -#include -typedef QApplication BaseApplication; -#else -#include "qtsingleapplication/src/QtSingleApplication" -typedef QtSingleApplication BaseApplication; -#endif - -class QLabel; - -class Common: public BaseApplication +class Common { - Q_OBJECT public: -#ifndef COMMON_STATIC - Common( int &argc, char **argv, const QString &app, const QString &icon ); -#endif - static QString applicationOs(); + static QStringList drivers(); -private: - static void msgHandler(QtMsgType type, const QMessageLogContext &ctx, const QString &msg); }; diff --git a/Configuration.cpp b/Configuration.cpp index 8a4fbc5..ec4d7ff 100644 --- a/Configuration.cpp +++ b/Configuration.cpp @@ -20,8 +20,7 @@ #include "Configuration.h" #include "Common.h" -#include "QPCSC.h" - +#include #include #include #include @@ -175,8 +174,8 @@ Configuration::Configuration(QObject *parent) d->url.adjusted(QUrl::RemoveFilename).toString(), QFileInfo(d->url.fileName()).baseName()); d->req.setRawHeader("User-Agent", QStringLiteral("%1/%2 (%3) Lang: %4 Devices: %5") - .arg(QApplication::applicationName(), QApplication::applicationVersion(), - Common::applicationOs(), QLocale().uiLanguages().first(), QPCSC::instance().drivers().join('/')).toUtf8()); + .arg(QCoreApplication::applicationName(), QCoreApplication::applicationVersion(), + Common::applicationOs(), QLocale().uiLanguages().first(), Common::drivers().join('/')).toUtf8()); #if QT_VERSION >= QT_VERSION_CHECK(5, 15, 0) d->req.setTransferTimeout(); #endif @@ -185,7 +184,7 @@ Configuration::Configuration(QObject *parent) [](QNetworkReply *reply, const QList &errors){ reply->ignoreSslErrors(errors); }); - connect(d->net, &QNetworkAccessManager::finished, this, [=](QNetworkReply *reply){ + connect(d->net, &QNetworkAccessManager::finished, this, [this](QNetworkReply *reply){ QScopedPointer replyScoped(reply); if(reply->error() != QNetworkReply::NoError) { @@ -244,7 +243,7 @@ Configuration::Configuration(QObject *parent) }); QByteArray key = readFile(QStringLiteral(":/config.pub")); - BIO *bio = BIO_new_mem_buf(key.constData(), key.size()); + BIO *bio = BIO_new_mem_buf(key.constData(), int(key.size())); if(!bio) { qWarning() << "Failed to parse public key"; @@ -295,7 +294,7 @@ Configuration::Configuration(QObject *parent) // Scheduled update or DigiDoc4 updated else if(lastCheck < QDate::currentDate().addDays(-LAST_CHECK_DAYS) || QVersionNumber::fromString(QSettings().value(QStringLiteral("LastVersion")).toString()) < - QVersionNumber::fromString(QApplication::applicationVersion())) + QVersionNumber::fromString(QCoreApplication::applicationVersion())) update(); #endif } @@ -340,5 +339,5 @@ void Configuration::update(bool force) { d->initCache(force); sendRequest(d->rsaurl); - QSettings().setValue(QStringLiteral("LastVersion"), QApplication::applicationVersion()); + QSettings().setValue(QStringLiteral("LastVersion"), QCoreApplication::applicationVersion()); } diff --git a/QPCSC.cpp b/QPCSC.cpp deleted file mode 100644 index 57f20e5..0000000 --- a/QPCSC.cpp +++ /dev/null @@ -1,486 +0,0 @@ -/* - * QEstEidCommon - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#include "QPCSC_p.h" - -#include -#include -#include -#include - -#include -#include - -#ifdef Q_OS_WIN -#include -#include -#endif - -#if QT_VERSION < QT_VERSION_CHECK(5, 15, 0) -namespace Qt { - using ::hex; -} -#endif - -Q_LOGGING_CATEGORY(APDU,"QPCSC.APDU") -Q_LOGGING_CATEGORY(SCard,"QPCSC.SCard") - -static QStringList stateToString(DWORD state) -{ - QStringList result; - #define STATE(X) if(state & SCARD_STATE_##X) result.append(QStringLiteral(#X)) - STATE(IGNORE); - STATE(CHANGED); - STATE(UNKNOWN); - STATE(UNAVAILABLE); - STATE(EMPTY); - STATE(PRESENT); - STATE(ATRMATCH); - STATE(EXCLUSIVE); - STATE(INUSE); - STATE(MUTE); - return result; -} - -template < typename Func, typename... Args> -LONG SCCall( const char *file, int line, const char *function, Func func, Args... args) -{ - LONG err = func(args...); - if(SCard().isDebugEnabled()) - QMessageLogger(file, line, function, SCard().categoryName()).debug() - << function << Qt::hex << (unsigned long)err; - return err; -} -#define SC(API, ...) SCCall(__FILE__, __LINE__, "SCard"#API, SCard##API, __VA_ARGS__) - -QHash QPCSCReader::Private::features() -{ - if(!featuresList.isEmpty()) - return featuresList; - DWORD size = 0; - std::array feature{}; - if(SC(Control, card, DWORD(CM_IOCTL_GET_FEATURE_REQUEST), nullptr, 0U, feature.data(), DWORD(feature.size()), &size)) - return featuresList; - for(auto p = feature.cbegin(); std::distance(feature.cbegin(), p) < size; ) - { - unsigned int tag = *p++, len = *p++, value = 0; - for(unsigned int i = 0; i < len; ++i) - value |= *p++ << 8 * i; - featuresList[DRIVER_FEATURES(tag)] = qFromBigEndian(value); - } - return featuresList; -} - - - -QPCSC::QPCSC() - : d(new Private) -{ - const_cast(SCard()).setEnabled(QtDebugMsg, qEnvironmentVariableIsSet("PCSC_DEBUG")); - const_cast(APDU()).setEnabled(QtDebugMsg, qEnvironmentVariableIsSet("APDU_DEBUG")); - Q_UNUSED(serviceRunning()) -} - -QPCSC::~QPCSC() -{ - requestInterruption(); - wait(); - if( d->context ) - SC(ReleaseContext, d->context); - qDeleteAll(d->lock); - delete d; -} - -QStringList QPCSC::drivers() const -{ -#ifdef Q_OS_WIN - HDEVINFO h = SetupDiGetClassDevs( 0, 0, 0, DIGCF_ALLCLASSES | DIGCF_PRESENT ); - if( !h ) - return {}; - - SP_DEVINFO_DATA info { sizeof(SP_DEVINFO_DATA) }; - QStringList list; - for( DWORD i = 0; SetupDiEnumDeviceInfo( h, i, &info ); i++ ) - { - DWORD size = 0; - WCHAR data[1024]; - - SetupDiGetDeviceRegistryPropertyW( h, &info, - SPDRP_CLASS, 0, LPBYTE(data), sizeof(data), &size ); - if( _wcsicmp( data, L"SmartCardReader" ) != 0 ) - continue; - - DWORD conf = 0; - SetupDiGetDeviceRegistryPropertyW( h, &info, - SPDRP_CONFIGFLAGS, 0, LPBYTE(&conf), sizeof(conf), &size ); - if( conf & CONFIGFLAG_DISABLED ) - continue; - - SetupDiGetDeviceRegistryPropertyW( h, &info, - SPDRP_DEVICEDESC, 0, LPBYTE(data), sizeof(data), &size ); - QString name = QString::fromWCharArray(data); - - SetupDiGetDeviceRegistryPropertyW( h, &info, - SPDRP_HARDWAREID, 0, LPBYTE(data), sizeof(data), &size ); - - list.append(QStringLiteral("%1 (%2)").arg(name, QString::fromWCharArray(data))); - } - SetupDiDestroyDeviceInfoList( h ); - - return list; -#else - return readers(); -#endif -} - -QPCSC& QPCSC::instance() -{ - static QPCSC pcsc; - return pcsc; -} - -QByteArray QPCSC::rawReaders() const -{ - if( !serviceRunning() ) - return {}; - - DWORD size = 0; - LONG err = SC(ListReaders, d->context, nullptr, nullptr, &size); - if(err != SCARD_S_SUCCESS || !size) - return {}; - - QByteArray data(int(size), 0); - err = SC(ListReaders, d->context, nullptr, data.data(), &size); - if(err != SCARD_S_SUCCESS) - data.clear(); - return data; -} - -QStringList QPCSC::readers() const -{ - QByteArray tmp = rawReaders(); - QStringList readers = QString::fromLocal8Bit(tmp.data(), tmp.size()).split(QChar(0)); - readers.removeAll({}); - return readers; -} - -void QPCSC::run() -{ - QPCSC pcsc; - std::vector list; - while(!isInterruptionRequested()) - { - if(!pcsc.serviceRunning()) - { - sleep(5); - continue; - } - // "\\?PnP?\Notification" does not work on macOS - QByteArray data = pcsc.rawReaders(); - if(data.isEmpty()) - { - sleep(5); - continue; - } - for(const char *name = data.constData(); *name; name += strlen(name) + 1) - { - if(std::none_of(list.cbegin(), list.cend(), [&name](const SCARD_READERSTATE &state) { return strcmp(state.szReader, name) == 0; })) - list.push_back({ strdup(name), nullptr, 0, 0, 0, {} }); - } - if(SC(GetStatusChange, pcsc.d->context, 5*1000U, list.data(), DWORD(list.size())) != SCARD_S_SUCCESS) - continue; - for(auto i = list.begin(); i != list.end(); ) - { - if((i->dwEventState & SCARD_STATE_CHANGED) == 0) - { - ++i; - continue; - } - i->dwCurrentState = i->dwEventState; - qCDebug(SCard) << "New state: " << QString::fromLocal8Bit(i->szReader) << stateToString(i->dwCurrentState); - Q_EMIT statusChanged(QString::fromLocal8Bit(i->szReader), stateToString(i->dwCurrentState)); - if((i->dwCurrentState & (SCARD_STATE_UNKNOWN|SCARD_STATE_IGNORE)) > 0) - { - free((void*)i->szReader); - i = list.erase(i); - } - else - ++i; - } - } -} - -bool QPCSC::serviceRunning() const -{ - if(d->context && SC(IsValidContext, d->context) == SCARD_S_SUCCESS) - return true; - SC(EstablishContext, DWORD(SCARD_SCOPE_USER), nullptr, nullptr, &d->context); - return d->context; -} - - - -QPCSCReader::QPCSCReader( const QString &reader, QPCSC *parent ) - : d(new Private) -{ - if(!parent->d->lock.contains(reader)) - parent->d->lock[reader] = new QMutex(); - parent->d->lock[reader]->lock(); - d->d = parent->d; - d->reader = reader.toUtf8(); - d->state.szReader = d->reader.constData(); - updateState(); -} - -QPCSCReader::~QPCSCReader() -{ - disconnect(); - d->d->lock[d->reader]->unlock(); - delete d; -} - -QByteArray QPCSCReader::atr() const -{ - return QByteArray::fromRawData((const char*)d->state.rgbAtr, int(d->state.cbAtr)).toHex().toUpper(); -} - -bool QPCSCReader::beginTransaction() -{ - return d->isTransacted = SC(BeginTransaction, d->card) == SCARD_S_SUCCESS; -} - -bool QPCSCReader::connect(Connect connect, Mode mode) -{ - return connectEx(connect, mode) == SCARD_S_SUCCESS; -} - -quint32 QPCSCReader::connectEx(Connect connect, Mode mode) -{ - LONG err = SC(Connect, d->d->context, d->state.szReader, connect, mode, &d->card, &d->io.dwProtocol); - updateState(); - return quint32(err); -} - -void QPCSCReader::disconnect( Reset reset ) -{ - if(d->isTransacted) - endTransaction(); - if( d->card ) - SC(Disconnect, d->card, reset); - d->io.dwProtocol = SCARD_PROTOCOL_UNDEFINED; - d->card = 0; - d->featuresList.clear(); - updateState(); -} - -bool QPCSCReader::endTransaction( Reset reset ) -{ - bool result = SC(EndTransaction, d->card, reset) == SCARD_S_SUCCESS; - if(result) - d->isTransacted = false; - return result; -} - -bool QPCSCReader::isPinPad() const -{ - if(d->reader.contains("HID Global OMNIKEY 3x21 Smart Card Reader") || - d->reader.contains("HID Global OMNIKEY 6121 Smart Card Reader")) - return false; - if(qEnvironmentVariableIsSet("SMARTCARDPP_NOPINPAD")) - return false; - QHash features = d->features(); - return features.contains(FEATURE_VERIFY_PIN_DIRECT) || features.contains(FEATURE_VERIFY_PIN_START); -} - -bool QPCSCReader::isPresent() const -{ - return d->state.dwEventState & SCARD_STATE_PRESENT; -} - -QString QPCSCReader::name() const -{ - return QString::fromLocal8Bit( d->reader ); -} - -QHash QPCSCReader::properties() const -{ - QHash properties; - if( DWORD ioctl = d->features().value(FEATURE_GET_TLV_PROPERTIES) ) - { - DWORD size = 0; - std::array recv{}; - if(SC(Control, d->card, ioctl, nullptr, 0U, recv.data(), DWORD(recv.size()), &size)) - return properties; - for(auto p = recv.cbegin(); std::distance(recv.cbegin(), p) < size; ) - { - int tag = *p++, len = *p++, value = 0; - for(int i = 0; i < len; ++i) - value |= *p++ << 8 * i; - properties[Properties(tag)] = value; - } - } - return properties; -} - -int QPCSCReader::protocol() const -{ - return int(d->io.dwProtocol); -} - -bool QPCSCReader::reconnect( Reset reset, Mode mode ) -{ - if( !d->card ) - return false; - LONG err = SC(Reconnect, d->card, DWORD(SCARD_SHARE_SHARED), mode, reset, &d->io.dwProtocol); - updateState(); - return err == SCARD_S_SUCCESS; -} - -QStringList QPCSCReader::state() const -{ - return stateToString(d->state.dwEventState); -} - -QPCSCReader::Result QPCSCReader::transfer( const QByteArray &apdu ) const -{ - QByteArray data( 1024, 0 ); - auto size = DWORD(data.size()); - - qCDebug(APDU).nospace() << 'T' << d->io.dwProtocol - 1 << "> " << apdu.toHex().constData(); - LONG ret = SC(Transmit, d->card, &d->io, - LPCBYTE(apdu.constData()), DWORD(apdu.size()), nullptr, LPBYTE(data.data()), &size); - if( ret != SCARD_S_SUCCESS ) - return { {}, {}, quint32(ret) }; - - Result result = { data.mid(int(size - 2), 2), data.left(int(size - 2)), quint32(ret) }; - qCDebug(APDU).nospace() << 'T' << d->io.dwProtocol - 1 << "< " << result.SW.toHex().constData(); - if(!result.data.isEmpty()) qCDebug(APDU).nospace() << data.left(int(size)).toHex().constData(); - - switch(result.SW.at(0)) - { - case 0x61: // Read more - { - QByteArray cmd( "\x00\xC0\x00\x00\x00", 5 ); - cmd[4] = data.at(int(size - 1)); - Result result2 = transfer( cmd ); - result2.data.prepend(result.data); - return result2; - } - case 0x6C: // Excpected lenght - { - QByteArray cmd = apdu; - cmd[4] = result.SW.at(1); - return transfer(cmd); - } - default: return result; - } -} - -QPCSCReader::Result QPCSCReader::transferCTL(const QByteArray &apdu, bool verify, - quint16 lang, quint8 minlen, quint8 newPINOffset, bool requestCurrentPIN) const -{ - bool display = false; - QHash features = d->features(); - if( DWORD ioctl = features.value(FEATURE_IFD_PIN_PROPERTIES) ) - { - PIN_PROPERTIES_STRUCTURE caps{}; - DWORD size = sizeof(caps); - if(!SC(Control, d->card, ioctl, nullptr, 0U, &caps, size, &size)) - display = caps.wLcdLayout > 0; - } - - quint8 PINFrameOffset = 0, PINLengthOffset = 0; - auto toByteArray = [&](auto &data) { - data.bTimerOut = 30; - data.bTimerOut2 = 30; - data.bmFormatString = FormatASCII|AlignLeft|quint8(PINFrameOffset << 4)|PINFrameOffsetUnitBits; - data.bmPINBlockString = PINLengthNone << 5|PINFrameSizeAuto; - data.bmPINLengthFormat = PINLengthOffsetUnitBits|PINLengthOffset; - data.wPINMaxExtraDigit = quint16(minlen << 8) | 12; - data.bEntryValidationCondition = ValidOnKeyPressed; - data.wLangId = lang; - data.ulDataLength = quint32(apdu.size()); - return QByteArray((const char*)&data, sizeof(data) - 1) + apdu; - }; - - QByteArray cmd; - if( verify ) - { - PIN_VERIFY_STRUCTURE data{}; - data.bNumberMessage = display ? CCIDDefaultInvitationMessage : NoInvitationMessage; - data.bMsgIndex = NoInvitationMessage; - cmd = toByteArray(data); - } - else - { - PIN_MODIFY_STRUCTURE data{}; - data.bNumberMessage = display ? ThreeInvitationMessage : NoInvitationMessage; - data.bInsertionOffsetOld = 0x00; - data.bInsertionOffsetNew = newPINOffset; - data.bConfirmPIN = ConfirmNewPin; - if(requestCurrentPIN) - { - data.bConfirmPIN |= RequestCurrentPin; - data.bMsgIndex1 = NoInvitationMessage; - data.bMsgIndex2 = OneInvitationMessage; - data.bMsgIndex3 = TwoInvitationMessage; - } - else - { - data.bMsgIndex1 = OneInvitationMessage; - data.bMsgIndex2 = TwoInvitationMessage; - data.bMsgIndex3 = ThreeInvitationMessage; - } - cmd = toByteArray(data); - } - - DWORD ioctl = features.value( verify ? FEATURE_VERIFY_PIN_START : FEATURE_MODIFY_PIN_START ); - if( !ioctl ) - ioctl = features.value( verify ? FEATURE_VERIFY_PIN_DIRECT : FEATURE_MODIFY_PIN_DIRECT ); - - qCDebug(APDU).nospace() << 'T' << d->io.dwProtocol - 1 << "> " << apdu.toHex().constData(); - qCDebug(APDU).nospace() << "CTL" << "> " << cmd.toHex().constData(); - QByteArray data( 255 + 3, 0 ); - auto size = DWORD(data.size()); - LONG err = SC(Control, d->card, ioctl, cmd.constData(), DWORD(cmd.size()), LPVOID(data.data()), DWORD(data.size()), &size); - - if( DWORD finish = features.value( verify ? FEATURE_VERIFY_PIN_FINISH : FEATURE_MODIFY_PIN_FINISH ) ) - { - size = DWORD(data.size()); - err = SC(Control, d->card, finish, nullptr, 0U, LPVOID(data.data()), DWORD(data.size()), &size); - } - - Result result { data.mid(int(size - 2), 2), data.left(int(size - 2)), quint32(err) }; - qCDebug(APDU).nospace() << 'T' << d->io.dwProtocol - 1 << "< " << result.SW.toHex().constData(); - if(!result.data.isEmpty()) qCDebug(APDU).nospace() << data.left(int(size)).toHex().constData(); - return result; -} - -bool QPCSCReader::updateState( quint32 msec ) -{ - if(!d->d->context) - return false; - d->state.dwCurrentState = d->state.dwEventState; - switch(SC(GetStatusChange, d->d->context, msec, &d->state, 1U)) - { - case LONG(SCARD_S_SUCCESS): return true; - case LONG(SCARD_E_TIMEOUT): return msec == 0; - default: return false; - } -} diff --git a/QPCSC.h b/QPCSC.h deleted file mode 100644 index dd7ed07..0000000 --- a/QPCSC.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * QEstEidCommon - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#pragma once - -#include - -template class QHash; - -class QPCSCReader; -class QPCSC final: public QThread -{ - Q_OBJECT -public: - ~QPCSC() final; - - static QPCSC& instance(); - QStringList drivers() const; - QStringList readers() const; - bool serviceRunning() const; - -Q_SIGNALS: - void statusChanged(const QString &reader, const QStringList &state); - -private: - QPCSC(); - Q_DISABLE_COPY(QPCSC); - - QByteArray rawReaders() const; - void run() final; - - class Private; - Private *d; - - friend class QPCSCReader; -}; - -class QPCSCReader final: public QObject -{ - Q_OBJECT -public: - struct Result { - QByteArray SW; - QByteArray data; - quint32 err = 0; - inline operator bool() const { return resultOk(); } - inline bool resultOk() const - { - static const QByteArray OK("\x90\x00", 2); - return SW == OK; - } - }; - - enum Properties { - wLcdLayout = 0x01, - bEntryValidationCondition = 0x02, - bTimeOut2 = 0x03, - wLcdMaxCharacters = 0x04, - wLcdMaxLines = 0x05, - bMinPINSize = 0x06, - bMaxPINSize = 0x07, - sFirmwareID = 0x08, - bPPDUSupport = 0x09, - dwMaxAPDUDataSize = 0x0A, - wIdVendor = 0x0B, - wIdProduct = 0x0C - }; - - enum Connect { - Exclusive = 1, - Shared = 2, - Direct = 3 - }; - - enum Reset - { - LeaveCard = 0, - ResetCard = 1, - UnpowerCard = 2, - EjectCard = 3 - }; - - enum Mode { - Undefined = 0, - T0 = 1, - T1 = 2 - }; - - explicit QPCSCReader( const QString &reader, QPCSC *parent ); - ~QPCSCReader() final; - - QByteArray atr() const; - bool isPinPad() const; - bool isPresent() const; - QString name() const; - QHash properties() const; - int protocol() const; - QStringList state() const; - bool updateState( quint32 msec = 0 ); - - bool connect( Connect connect = Shared, Mode mode = Mode(T0|T1) ); - quint32 connectEx( Connect connect = Shared, Mode mode = Mode(T0|T1) ); - void disconnect( Reset reset = LeaveCard ); - bool reconnect( Reset reset = LeaveCard, Mode mode = Mode(T0|T1) ); - bool beginTransaction(); - bool endTransaction( Reset reset = LeaveCard ); - Result transfer( const QByteArray &apdu ) const; - Result transferCTL(const QByteArray &apdu, bool verify, quint16 lang = 0, - quint8 minlen = 4, quint8 newPINOffset = 0, bool requestCurrentPIN = true) const; - -private: - Q_DISABLE_COPY(QPCSCReader) - class Private; - Private *d; -}; diff --git a/QPCSC_p.h b/QPCSC_p.h deleted file mode 100644 index 19fab0b..0000000 --- a/QPCSC_p.h +++ /dev/null @@ -1,207 +0,0 @@ -/* - * QEstEidCommon - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library 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 - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - * - */ - -#pragma once - -#include "QPCSC.h" - -#include -#include - -#ifdef Q_OS_WIN -#undef UNICODE -#define NOMINMAX -#include -#include -#elif defined(Q_OS_MAC) -#include -#include -#include -#else -#include -#include -#include -#endif - -#ifndef SCARD_CTL_CODE -#define SCARD_CTL_CODE(code) (0x42000000 + (code)) -#endif - -// http://pcscworkgroup.com/Download/Specifications/pcsc10_v2.02.09.pdf -// http://ludovic.rousseau.free.fr/softwares/pcsc-lite/SecurePIN%20discussion%20v5.pdf -#define CM_IOCTL_GET_FEATURE_REQUEST SCARD_CTL_CODE(3400) - -enum DRIVER_FEATURES { -FEATURE_VERIFY_PIN_START = 0x01, -FEATURE_VERIFY_PIN_FINISH = 0x02, -FEATURE_MODIFY_PIN_START = 0x03, -FEATURE_MODIFY_PIN_FINISH = 0x04, -FEATURE_GET_KEY_PRESSED = 0x05, -FEATURE_VERIFY_PIN_DIRECT = 0x06, -FEATURE_MODIFY_PIN_DIRECT = 0x07, -FEATURE_MCT_READER_DIRECT = 0x08, -FEATURE_MCT_UNIVERSAL = 0x09, -FEATURE_IFD_PIN_PROPERTIES = 0x0A, -FEATURE_ABORT = 0x0B, -FEATURE_SET_SPE_MESSAGE = 0x0C, -FEATURE_VERIFY_PIN_DIRECT_APP_ID = 0x0D, -FEATURE_MODIFY_PIN_DIRECT_APP_ID = 0x0E, -FEATURE_WRITE_DISPLAY = 0x0F, -FEATURE_GET_KEY = 0x10, -FEATURE_IFD_DISPLAY_PROPERTIES = 0x11, -FEATURE_GET_TLV_PROPERTIES = 0x12, -FEATURE_CCID_ESC_COMMAND = 0x13 -}; - -#ifdef Q_OS_MAC -#pragma pack(1) -#else -#pragma pack(push, 1) -#endif - -using PCSC_TLV_STRUCTURE = struct -{ - quint8 tag; - quint8 length; - quint32 value; -}; - -enum bmFormatString : quint8 -{ - FormatBinary = 0, // (1234 => 01h 02h 03h 04h) - FormatBCD = 1 << 0, // (1234 => 12h 34h) - FormatASCII = 1 << 1, // (1234 => 31h 32h 33h 34h) - AlignLeft = 0, - AlignRight = 1 << 2, - PINFrameOffsetUnitBits = 0, - PINFrameOffsetUnitBytes = 1 << 7, -}; - -enum bmPINBlockString : quint8 -{ - PINLengthNone = 0, - PINFrameSizeAuto = 0, -}; - -enum bmPINLengthFormat : quint8 -{ - PINLengthOffsetUnitBits = 0, - PINLengthOffsetUnitBytes = 1 << 5, -}; - -enum bEntryValidationCondition : quint8 -{ - ValidOnMaxSizeReached = 1 << 0, - ValidOnKeyPressed = 1 << 1, - ValidOnTimeout = 1 << 2, -}; - -enum bNumberMessage : quint8 -{ - NoInvitationMessage = 0, - OneInvitationMessage = 1, - TwoInvitationMessage = 2, // MODIFY - ThreeInvitationMessage = 3, // MODIFY - CCIDDefaultInvitationMessage = 0xFF, -}; - -enum bConfirmPIN : quint8 -{ - ConfirmNewPin = 1 << 0, - RequestCurrentPin = 1 << 1, - AdvancedModify = 1 << 2, -}; - -using PIN_VERIFY_STRUCTURE = struct -{ - quint8 bTimerOut; // timeout in seconds (00 means use default timeout) - quint8 bTimerOut2; // timeout in seconds after first key stroke - quint8 bmFormatString; // formatting options - quint8 bmPINBlockString; // PIN block definition - quint8 bmPINLengthFormat; // PIN length definition - quint16 wPINMaxExtraDigit; // 0xXXYY where XX is minimum PIN size in digits, and YY is maximum PIN size in digits - quint8 bEntryValidationCondition; // Conditions under which PIN entry should be considered complete - quint8 bNumberMessage; // Number of messages to display for PIN verification - quint16 wLangId; // Language for messages (http://www.usb.org/developers/docs/USB_LANGIDs.pdf) - quint8 bMsgIndex; // Message index (should be 00) - quint8 bTeoPrologue[3]; // T=1 I-block prologue field to use (fill with 00) - quint32 ulDataLength; // length of Data to be sent to the ICC - quint8 abData[1]; // Data to send to the ICC -}; - -using PIN_MODIFY_STRUCTURE = struct -{ - quint8 bTimerOut; // timeout in seconds (00 means use default timeout) - quint8 bTimerOut2; // timeout in seconds after first key stroke - quint8 bmFormatString; // formatting options - quint8 bmPINBlockString; // PIN block definition - quint8 bmPINLengthFormat; // PIN length definition - quint8 bInsertionOffsetOld; // Insertion position offset in bytes for the current PIN - quint8 bInsertionOffsetNew; // Insertion position offset in bytes for the new PIN - quint16 wPINMaxExtraDigit; // 0xXXYY where XX is minimum PIN size in digits, and YY is maximum PIN size in digits - quint8 bConfirmPIN; // Flags governing need for confirmation of new PIN - quint8 bEntryValidationCondition; // Conditions under which PIN entry should be considered complete - quint8 bNumberMessage; // Number of messages to display for PIN verification - quint16 wLangId; // Language for messages (http://www.usb.org/developers/docs/USB_LANGIDs.pdf) - quint8 bMsgIndex1; // Index of the invitation message - quint8 bMsgIndex2; // Index of the invitation message - quint8 bMsgIndex3; // Index of the invitation message - quint8 bTeoPrologue[3]; // T=1 I-block prologue field to use (fill with 00) - quint32 ulDataLength; // length of Data to be sent to the ICC - quint8 abData[1]; // Data to send to the ICC -}; - -using PIN_PROPERTIES_STRUCTURE = struct { - quint16 wLcdLayout; - quint8 bEntryValidationCondition; - quint8 bTimeOut2; -}; - -using DISPLAY_PROPERTIES_STRUCTURE = struct { - quint16 wLcdMaxCharacters; - quint16 wLcdMaxLines; -}; - -#ifdef Q_OS_MAC -#pragma pack() -#else -#pragma pack(pop) -#endif - -class QPCSC::Private -{ -public: - SCARDCONTEXT context {}; - QHash lock; -}; - -class QPCSCReader::Private -{ -public: - QHash features(); - - QPCSC::Private *d {}; - SCARDHANDLE card {}; - SCARD_IO_REQUEST io {SCARD_PROTOCOL_UNDEFINED, sizeof(SCARD_IO_REQUEST)}; - SCARD_READERSTATE state {}; - QByteArray reader; - bool isTransacted {}; - - QHash featuresList; -}; diff --git a/cmake b/cmake deleted file mode 160000 index 7b34cc0..0000000 --- a/cmake +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7b34cc036c350e2ac13e4f3f529361003e36394a