Данный проект реализует мост между протоколами PC/SC (протокол для общения c smart-картами) и протоколом CEN/XFS, используемом банкоматным ПО для доступа к устройствам, в том числе чиповым считывателям карт.
Лицензия MIT. Вкратце, возможно бесплатное использование в коммерческих и открытых проектах. Текст лицензии на русском и английском языках в файле LICENSE.md. Юридическую силу имеет только английский вариант.
- Boost (должна быть прописана переменная окружения
%BOOST_ROOT%, указывающая на корневой каталог boost-а, т.е. каталог, содержащий папки libs, doc, stage и т.п. предполагается, что boost собран в каталог по умолчанию, коим является stage)boost.chronoboost.threadboost.date_time(зависимость отboost.thread)
- XFS SDK
- взять можно у проекта freexfs. Там же содержится и документация.
- Также можно взять на официальном FTP-сайте, но его достаточно трудно отыскать. На официальном сайте группы CEN/XFS найти ссылки на документацию не удалось, к счастью, пользователь winner13 в форума bankomatchik.ru каким-то чудом отыскал FTP-ссылку.
- Подсистема PC/SC являтется частью SDK Windows.
Подготовить зависимости: скачать буст и собрать необходимые библиотеки. Сборку можно выполнить следующим образом:
- Запустить командную строку Visual Studio
- Перейти в
%BOOST_ROOT% - Выполнить команду
bootstrap.bat msvcдля сборки инструмента сборки буста - Выполнить команду
build-boost.batиз корня проекта для сбора необходимых библиотек в нужном варианте
Запустить командную строку Visual Studio и выполнить в ней команду
make
Либо открыть обычную командную строку в выполнить команды, предварительно заменив %VC_INSTALL_DIR%
на путь к установленной MSVC. Так как Kalignite собран под x86, то и библиотеку будем собирать под
эту архитектуру. Естественно, когда появится 64-битная версия, необходимо будет использовать x86_amd64:
"%VC_INSTALL_DIR%\VC\vcvarsall.bat" x86
make
Используется C++03. Проверена сборка следующими компиляторами:
- MSVC 2005
- MSVC 2008
- MSVC 2013
При загрузке динамической библиотеки создается глобальный объект Manager, в конструкторе которого
инициализируется подсистема PC/SC и запускается поток опроса изменений в устройствах и подключения
новых устройств. В деструкторе глобального объекта соединение с подсистемой PC/SC закрывается, это
происходит автоматически, когда менеджер XFS выгружает библиотеку.
Хотя может показаться, что можно было напрямую мапить хендл сервис-провайдера (HSERVICE), на хендл
контекста PC/SC (SCARDCONTEXT), этого не делается потому, что функция SCardListReaders блокирующая,
а она требует хендл контекста. Таким образом, если бы на каждый сервис-провайдер был заведен свой PC/SC
контекст, потребовалось бы на каждый создавать по своему потоку для опроса изменений в устройствах.
Класс Manager содержит список задач на чтение карты, которые создаются при вызове метода WFPExecute,
и список сервисов, представляющих открытые XFS-менеджером сервисы (через WFPOpen).
Когда происходит событие PC/SC, отдельный поток сначала уведомляет все подписавшиеся окна (через
WFPRegister) на изменения (естественно, выполняется трансляция события из PC/SC в XFS форму), а
затем уведомляет все задачи обо всех произошедших изменениях. Таким образом реализуется требование
XFS, что все события должны быть испущены до того, как произойдет WFS_xxx_COMPLETE-событие.
Если задача считает, что изменение ей интересно, она генерирует событие WFS_xxx_COMPLETE и ее метод
match возвращает true, в результате чего она удаляется из списка задач.
События о завершении отправляются Windows функцией PostMessage, которая укладывает ее в очередь сообщений
потока, который обрабатывает события завершения. На совести конечного приложения, что оно предоставляет
хендлы окон, которые существуют в одном потоке, иначе WFS_xxx_COMPLETE-событие может прийти раньше,
чем прочие виды событий. Кроме того, если события приложения обрабатывает в другом потоке, чем асинхронные
вызовы сервис-провайдера, то события могут прийти раньше, чем завершится асинхронный вызов, их инициирующий.
На совести приложения работать правильно в таком случае и не терять уведомления. Это особенность XFS API,
оно предъявляет очень жесткие требования к приложению.
Большинство настроек предназначены для обхода проблем, обнаруженных в процессе тестирования, но некоторые
управляют штатным функционалом сервис-провайдера. Все настройки выполняются в ветке реестра, под веткой
с провайдером логического сервиса (HKEY_LOCAL_MACHINE\SOFTWARE\XFS\SERVICE_PROVIDERS\<провайдер>).
Все DWORD значения в таблице являются логическими флагами, со значением 0 -- сброшены, любое другое --
выставлен:
| Название | Тип | Назначение |
|---|---|---|
| ReaderName | REG_SZ |
PC/SC название считывателя, с которым должен работать данный провайдер. Если параметр пустой или отсутствует, то слушаются все подключенные считыватели и используется первый, в который будет вставлена карточка (это делается каждый раз, т.е. если карточку вытащили из первого считывателя и вставили во второй, то работа будет происходить со вторым считывателем). Если не пустой, то событие вставки карты будет обрабатываться только от указанного считывателя |
| Exclusive | DWORD |
Если флаг установлен, то считыватель будет использовать карту в монопольном режиме (SCARD_SHARE_EXCLUSIVE), т.е. никто, кроме сервис-провайдера, не сможет общаться с картой одновременно. Если сброшен или отсутсвует, то карта открывается в совместном режиме (SCARD_SHARE_SHARED) |
| Workarounds | Подраздел -- обходы багов | |
| CorrectChipIO | DWORD |
Анализировать длину передаваемых чипу команд и корректировать ее в соответствии с тем, что передается в заголовке команды. Kalignite может передавать лишние байты в команде чтения, а это вызывает ошибку у функции SCardTransmit. Если сброшен или отсутствует, то анализ не производится |
| CanEject | DWORD |
Сообщать, что устройство умеет извлекать карты в возможностях устройства и принимать команду извлечения карты (WFS_CMD_IDC_EJECT_CARD). При этом ничего не делается. Если сброшен или отсутствует, то в возможностях сообщать, что команда не поддерживается, а при получении этой команды возвращать ошибку неподдерживаемая команда (WFS_ERR_UNSUPP_COMMAND). Kalignite пытается выдавать карту, даже если эта возможность не поддерживается, и не ожидает, что команда не будет выполнена, падая с Fatal Error в случае кода ответа, отличного от успеха |
| Track2 | Подраздел Workarounds -- настройки второй дорожки | |
| (по умолчанию) | REG_SZ |
Значение второй дорожки, сообщаемое провайдером, без начального и конечного разделителей, как будет отдано приложению. Значение сообщается, только если флаг Report взведен |
| Report | DWORD |
Сообщать о возможности чтения второй магнитной дорожки. Если флаг взведен, а значение трека пустое, то при чтении возвращается код ошибки данные отсутствуют (WFS_IDC_DATAMISSING). Если флаг сброшен, то в возможностях устройства сообщается, что чтение второй дорожки не поддерживается. Kalignite требует, чтобы вторая дорожка была прочитана, даже если в условиях чтения указать не читать вторую дорожку (на момент чтения все в порядке, но потом при работе сценария он падает с Fatal Error из-за отсутствия второй дорожки) |
Для работы с Kaliginte-ом были активированы все обходы багов.
- OMNIKEY 3121 (pcsc_scan знает его, как OMNIKEY AG Smart Card Reader USB 0) -- успешно работает
- ACR38u -- успешно работает