Added driver for contactless PC/SC readers
- only initiator mode is supported - properties are choosen as they are available via PC/SC, the rest of the defaults are chosen to be compatible with Mifare DESFire - This commit allows reading Mifare DESFire via PC/SC with libfreefare
This commit is contained in:
parent
f8b28523d7
commit
75e5e23c81
6 changed files with 808 additions and 3 deletions
|
@ -7,6 +7,10 @@ libnfcdrivers_la_SOURCES =
|
|||
libnfcdrivers_la_CFLAGS = @DRIVERS_CFLAGS@ -I$(top_srcdir)/libnfc -I$(top_srcdir)/libnfc/buses
|
||||
libnfcdrivers_la_LIBADD =
|
||||
|
||||
if DRIVER_PCSC_ENABLED
|
||||
libnfcdrivers_la_SOURCES += pcsc.c pcsc.h
|
||||
endif
|
||||
|
||||
if DRIVER_ACR122_PCSC_ENABLED
|
||||
libnfcdrivers_la_SOURCES += acr122_pcsc.c acr122_pcsc.h
|
||||
endif
|
||||
|
|
|
@ -93,7 +93,7 @@ const struct pn53x_io acr122_pcsc_io;
|
|||
// Prototypes
|
||||
char *acr122_pcsc_firmware(nfc_device *pnd);
|
||||
|
||||
const char *supported_devices[] = {
|
||||
static const char *supported_devices[] = {
|
||||
"ACS ACR122", // ACR122U & Touchatag, last version
|
||||
"ACS ACR 38U-CCID", // Touchatag, early version
|
||||
"ACS ACR38U-CCID", // Touchatag, early version, under MacOSX
|
||||
|
|
751
libnfc/drivers/pcsc.c
Normal file
751
libnfc/drivers/pcsc.c
Normal file
|
@ -0,0 +1,751 @@
|
|||
/*-
|
||||
* Free/Libre Near Field Communication (NFC) library
|
||||
*
|
||||
* Libnfc historical contributors:
|
||||
* Copyright (C) 2019 Frank Morgner
|
||||
* See AUTHORS file for a more comprehensive list of contributors.
|
||||
* Additional contributors of this file:
|
||||
*
|
||||
* This program 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 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file pcsc.c
|
||||
* @brief Driver for non-ACR122 devices behind PC/SC
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif // HAVE_CONFIG_H
|
||||
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <nfc/nfc.h>
|
||||
|
||||
#include "drivers/pcsc.h"
|
||||
#include "nfc-internal.h"
|
||||
|
||||
// Bus
|
||||
#ifdef __APPLE__
|
||||
#include <PCSC/winscard.h>
|
||||
#include <PCSC/wintypes.h>
|
||||
#else
|
||||
#ifndef _Win32
|
||||
#include <reader.h>
|
||||
#endif
|
||||
#include <winscard.h>
|
||||
#endif
|
||||
|
||||
#define PCSC_DRIVER_NAME "pcsc"
|
||||
|
||||
#include <nfc/nfc.h>
|
||||
|
||||
#define LOG_GROUP NFC_LOG_GROUP_DRIVER
|
||||
#define LOG_CATEGORY "libnfc.driver.pcsc"
|
||||
|
||||
static const char *supported_devices[] = {
|
||||
"ACS ACR122", // ACR122U & Touchatag, last version
|
||||
"ACS ACR 38U-CCID", // Touchatag, early version
|
||||
"ACS ACR38U-CCID", // Touchatag, early version, under MacOSX
|
||||
"ACS AET65", // Touchatag using CCID driver version >= 1.4.6
|
||||
" CCID USB", // ??
|
||||
NULL
|
||||
};
|
||||
|
||||
struct pcsc_data {
|
||||
SCARDHANDLE hCard;
|
||||
SCARD_IO_REQUEST ioCard;
|
||||
DWORD dwShareMode;
|
||||
DWORD last_error;
|
||||
};
|
||||
|
||||
#define DRIVER_DATA(pnd) ((struct pcsc_data*)(pnd->driver_data))
|
||||
|
||||
static SCARDCONTEXT _SCardContext;
|
||||
static int _iSCardContextRefCount = 0;
|
||||
|
||||
const nfc_baud_rate pcsc_supported_brs[] = {NBR_106, NBR_424, 0};
|
||||
const nfc_modulation_type pcsc_supported_mts[] = {NMT_ISO14443A, NMT_ISO14443B, 0};
|
||||
|
||||
SCARDCONTEXT *
|
||||
pcsc_get_scardcontext(void)
|
||||
{
|
||||
if (_iSCardContextRefCount == 0) {
|
||||
if (SCardEstablishContext(SCARD_SCOPE_USER, NULL, NULL, &_SCardContext) != SCARD_S_SUCCESS)
|
||||
return NULL;
|
||||
}
|
||||
_iSCardContextRefCount++;
|
||||
|
||||
return &_SCardContext;
|
||||
}
|
||||
|
||||
void
|
||||
pcsc_free_scardcontext(void)
|
||||
{
|
||||
if (_iSCardContextRefCount) {
|
||||
_iSCardContextRefCount--;
|
||||
if (!_iSCardContextRefCount) {
|
||||
SCardReleaseContext(_SCardContext);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define ICC_TYPE_UNKNOWN 0
|
||||
#define ICC_TYPE_14443A 5
|
||||
#define ICC_TYPE_14443B 6
|
||||
|
||||
uint8_t pcsc_get_icc_type(struct nfc_device *pnd)
|
||||
{
|
||||
uint8_t it = 0;
|
||||
DWORD dwItLen = sizeof it;
|
||||
DRIVER_DATA(pnd)->last_error = SCardGetAttrib(DRIVER_DATA(pnd)->hCard, SCARD_ATTR_ICC_TYPE_PER_ATR, &it, &dwItLen);
|
||||
return it;
|
||||
}
|
||||
|
||||
int pcsc_get_uid(struct nfc_device *pnd, uint8_t *puid, size_t szuid)
|
||||
{
|
||||
uint8_t get_data[] = {0xFF, 0xCA, 0x00, 0x00, 0x00}, data[256 + 2];
|
||||
DWORD dwRxLen = sizeof data;
|
||||
|
||||
LOG_HEX(NFC_LOG_GROUP_COM, "TX", get_data, sizeof get_data);
|
||||
DRIVER_DATA(pnd)->last_error = SCardTransmit(DRIVER_DATA(pnd)->hCard, &(DRIVER_DATA(pnd)->ioCard), get_data, sizeof get_data, NULL, data, &dwRxLen);
|
||||
if ((DRIVER_DATA(pnd)->last_error != SCARD_S_SUCCESS)
|
||||
|| dwRxLen < 2) {
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Reader doesn't support request for UID");
|
||||
pnd->last_error = NFC_EIO;
|
||||
return pnd->last_error;
|
||||
}
|
||||
LOG_HEX(NFC_LOG_GROUP_COM, "RX", data, dwRxLen);
|
||||
if (szuid < (size_t) dwRxLen - 2) {
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Malformed response code");
|
||||
pnd->last_error = NFC_EINVARG;
|
||||
return pnd->last_error;
|
||||
}
|
||||
memcpy(puid, data, dwRxLen - 2);
|
||||
return dwRxLen - 2;
|
||||
}
|
||||
|
||||
int pcsc_props_to_target(uint8_t it, const uint8_t *patr, size_t szatr, const uint8_t *puid, int szuid, const nfc_modulation_type nmt, nfc_target *pnt)
|
||||
{
|
||||
if (NULL != pnt) {
|
||||
switch (nmt) {
|
||||
case NMT_ISO14443A:
|
||||
if ((it == ICC_TYPE_UNKNOWN || it == ICC_TYPE_14443A)
|
||||
&& (szuid <= 0 || szuid == 4 || szuid == 7 || szuid == 10)
|
||||
&& NULL != patr && szatr >= 5
|
||||
&& patr[0] == 0x3B
|
||||
&& patr[1] == (0x80 | ((uint8_t)(szatr - 5)))
|
||||
&& patr[2] == 0x80
|
||||
&& patr[3] == 0x01) {
|
||||
memset(pnt, 0, sizeof * pnt);
|
||||
pnt->nm.nmt = NMT_ISO14443A;
|
||||
pnt->nm.nbr = pcsc_supported_brs[0];
|
||||
if (szuid > 0) {
|
||||
memcpy(pnt->nti.nai.abtUid, puid, szuid);
|
||||
pnt->nti.nai.szUidLen = szuid;
|
||||
}
|
||||
/* SAK_ISO14443_4_COMPLIANT */
|
||||
pnt->nti.nai.btSak = 0x20;
|
||||
/* Choose TL, TA, TB, TC according to Mifare DESFire */
|
||||
memcpy(pnt->nti.nai.abtAts, "\x75\x77\x81\x02", 4);
|
||||
/* copy historical bytes */
|
||||
memcpy(pnt->nti.nai.abtAts + 4, patr + 4, (uint8_t)(szatr - 5));
|
||||
pnt->nti.nai.szAtsLen = 4 + (uint8_t)(szatr - 5);
|
||||
return NFC_SUCCESS;
|
||||
}
|
||||
break;
|
||||
case NMT_ISO14443B:
|
||||
if ((ICC_TYPE_UNKNOWN == 0 || ICC_TYPE_14443B == 6)
|
||||
&& (szuid <= 0 || szuid == 8)
|
||||
&& NULL != patr && szatr == 5 + 8
|
||||
&& patr[0] == 0x3B
|
||||
&& patr[1] == (0x80 | 0x08)
|
||||
&& patr[2] == 0x80
|
||||
&& patr[3] == 0x01) {
|
||||
memset(pnt, 0, sizeof * pnt);
|
||||
pnt->nm.nmt = NMT_ISO14443B;
|
||||
pnt->nm.nbr = pcsc_supported_brs[0];
|
||||
memcpy(pnt->nti.nbi.abtApplicationData, patr + 4, 4);
|
||||
memcpy(pnt->nti.nbi.abtProtocolInfo, patr + 8, 3);
|
||||
/* PI_ISO14443_4_SUPPORTED */
|
||||
pnt->nti.nbi.abtProtocolInfo[1] = 0x01;
|
||||
return NFC_SUCCESS;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return NFC_EINVARG;
|
||||
}
|
||||
|
||||
#define PCSC_MAX_DEVICES 16
|
||||
/**
|
||||
* @brief List opened devices
|
||||
*
|
||||
* Probe PCSC to find any reader but the ACR122 devices (ACR122U and Touchatag/Tikitag).
|
||||
*
|
||||
* @param connstring array of nfc_connstring where found device's connection strings will be stored.
|
||||
* @param connstrings_len size of connstrings array.
|
||||
* @return number of devices found.
|
||||
*/
|
||||
static size_t
|
||||
pcsc_scan(const nfc_context *context, nfc_connstring connstrings[], const size_t connstrings_len)
|
||||
{
|
||||
(void) context;
|
||||
size_t szPos = 0;
|
||||
char acDeviceNames[256 + 64 * PCSC_MAX_DEVICES];
|
||||
size_t szDeviceNamesLen = sizeof(acDeviceNames);
|
||||
SCARDCONTEXT *pscc;
|
||||
int i;
|
||||
|
||||
// Clear the reader list
|
||||
memset(acDeviceNames, '\0', szDeviceNamesLen);
|
||||
|
||||
// Test if context succeeded
|
||||
if (!(pscc = pcsc_get_scardcontext())) {
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_INFO, "Warning: %s", "PCSC context not found (make sure PCSC daemon is running).");
|
||||
return 0;
|
||||
}
|
||||
// Retrieve the string array of all available pcsc readers
|
||||
DWORD dwDeviceNamesLen = szDeviceNamesLen;
|
||||
if (SCardListReaders(*pscc, NULL, acDeviceNames, &dwDeviceNamesLen) != SCARD_S_SUCCESS)
|
||||
return 0;
|
||||
|
||||
size_t device_found = 0;
|
||||
while ((acDeviceNames[szPos] != '\0') && (device_found < connstrings_len)) {
|
||||
bool bSupported = false;
|
||||
for (i = 0; supported_devices[i] && !bSupported; i++) {
|
||||
int l = strlen(supported_devices[i]);
|
||||
bSupported = 0 == !strncmp(supported_devices[i], acDeviceNames + szPos, l);
|
||||
}
|
||||
|
||||
if (bSupported) {
|
||||
// Supported non-ACR122 device found
|
||||
snprintf(connstrings[device_found], sizeof(nfc_connstring), "%s:%s", PCSC_DRIVER_NAME, acDeviceNames + szPos);
|
||||
device_found++;
|
||||
} else {
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Skipping PCSC device [%s] as it is supported by acr122_pcsc driver.", acDeviceNames + szPos);
|
||||
}
|
||||
|
||||
// Find next device name position
|
||||
while (acDeviceNames[szPos++] != '\0');
|
||||
}
|
||||
pcsc_free_scardcontext();
|
||||
|
||||
return device_found;
|
||||
}
|
||||
|
||||
struct pcsc_descriptor {
|
||||
char *pcsc_device_name;
|
||||
};
|
||||
|
||||
static nfc_device *
|
||||
pcsc_open(const nfc_context *context, const nfc_connstring connstring)
|
||||
{
|
||||
struct pcsc_descriptor ndd;
|
||||
int connstring_decode_level = connstring_decode(connstring, PCSC_DRIVER_NAME, "pcsc", &ndd.pcsc_device_name, NULL);
|
||||
|
||||
if (connstring_decode_level < 1) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
nfc_connstring fullconnstring;
|
||||
if (connstring_decode_level == 1) {
|
||||
// Device was not specified, take the first one we can find
|
||||
size_t szDeviceFound = pcsc_scan(context, &fullconnstring, 1);
|
||||
if (szDeviceFound < 1)
|
||||
return NULL;
|
||||
connstring_decode_level = connstring_decode(fullconnstring, PCSC_DRIVER_NAME, "pcsc", &ndd.pcsc_device_name, NULL);
|
||||
if (connstring_decode_level < 2) {
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
memcpy(fullconnstring, connstring, sizeof(nfc_connstring));
|
||||
}
|
||||
if (strlen(ndd.pcsc_device_name) < 5) { // We can assume it's a reader ID as pcsc_name always ends with "NN NN"
|
||||
// Device was not specified, only ID, retrieve it
|
||||
size_t index;
|
||||
if (sscanf(ndd.pcsc_device_name, "%4" SCNuPTR, &index) != 1) {
|
||||
free(ndd.pcsc_device_name);
|
||||
return NULL;
|
||||
}
|
||||
nfc_connstring *ncs = malloc(sizeof(nfc_connstring) * (index + 1));
|
||||
if (!ncs) {
|
||||
perror("malloc");
|
||||
free(ndd.pcsc_device_name);
|
||||
return NULL;
|
||||
}
|
||||
size_t szDeviceFound = pcsc_scan(context, ncs, index + 1);
|
||||
if (szDeviceFound < index + 1) {
|
||||
free(ncs);
|
||||
free(ndd.pcsc_device_name);
|
||||
return NULL;
|
||||
}
|
||||
strncpy(fullconnstring, ncs[index], sizeof(nfc_connstring));
|
||||
fullconnstring[sizeof(nfc_connstring) - 1] = '\0';
|
||||
free(ncs);
|
||||
connstring_decode_level = connstring_decode(fullconnstring, PCSC_DRIVER_NAME, "pcsc", &ndd.pcsc_device_name, NULL);
|
||||
|
||||
if (connstring_decode_level < 2) {
|
||||
free(ndd.pcsc_device_name);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
nfc_device *pnd = nfc_device_new(context, fullconnstring);
|
||||
if (!pnd) {
|
||||
perror("malloc");
|
||||
goto error;
|
||||
}
|
||||
pnd->driver_data = malloc(sizeof(struct pcsc_data));
|
||||
if (!pnd->driver_data) {
|
||||
perror("malloc");
|
||||
goto error;
|
||||
}
|
||||
|
||||
SCARDCONTEXT *pscc;
|
||||
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "Attempt to open %s", ndd.pcsc_device_name);
|
||||
// Test if context succeeded
|
||||
if (!(pscc = pcsc_get_scardcontext()))
|
||||
goto error;
|
||||
DRIVER_DATA(pnd)->last_error = SCardConnect(*pscc, ndd.pcsc_device_name, SCARD_SHARE_DIRECT, 0, &(DRIVER_DATA(pnd)->hCard), (void *) & (DRIVER_DATA(pnd)->ioCard.dwProtocol));
|
||||
if (DRIVER_DATA(pnd)->last_error != SCARD_S_SUCCESS) {
|
||||
// We can not connect to this device.
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "PCSC connect failed");
|
||||
goto error;
|
||||
}
|
||||
// Configure I/O settings for card communication
|
||||
DRIVER_DATA(pnd)->ioCard.cbPciLength = sizeof(SCARD_IO_REQUEST);
|
||||
DRIVER_DATA(pnd)->dwShareMode = SCARD_SHARE_DIRECT;
|
||||
|
||||
// Done, we found the reader we are looking for
|
||||
snprintf(pnd->name, sizeof(pnd->name), "%s", ndd.pcsc_device_name);
|
||||
|
||||
pnd->driver = &pcsc_driver;
|
||||
|
||||
free(ndd.pcsc_device_name);
|
||||
return pnd;
|
||||
|
||||
error:
|
||||
free(ndd.pcsc_device_name);
|
||||
nfc_device_free(pnd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
pcsc_close(nfc_device *pnd)
|
||||
{
|
||||
SCardDisconnect(DRIVER_DATA(pnd)->hCard, SCARD_LEAVE_CARD);
|
||||
pcsc_free_scardcontext();
|
||||
|
||||
nfc_device_free(pnd);
|
||||
}
|
||||
|
||||
static const char *stringify_error(const LONG pcscError)
|
||||
{
|
||||
static char strError[75];
|
||||
const char *msg = NULL;
|
||||
|
||||
switch (pcscError) {
|
||||
case SCARD_S_SUCCESS:
|
||||
msg = "Command successful.";
|
||||
break;
|
||||
case SCARD_F_INTERNAL_ERROR:
|
||||
msg = "Internal error.";
|
||||
break;
|
||||
case SCARD_E_CANCELLED:
|
||||
msg = "Command cancelled.";
|
||||
break;
|
||||
case SCARD_E_INVALID_HANDLE:
|
||||
msg = "Invalid handle.";
|
||||
break;
|
||||
case SCARD_E_INVALID_PARAMETER:
|
||||
msg = "Invalid parameter given.";
|
||||
break;
|
||||
case SCARD_E_INVALID_TARGET:
|
||||
msg = "Invalid target given.";
|
||||
break;
|
||||
case SCARD_E_NO_MEMORY:
|
||||
msg = "Not enough memory.";
|
||||
break;
|
||||
case SCARD_F_WAITED_TOO_LONG:
|
||||
msg = "Waited too long.";
|
||||
break;
|
||||
case SCARD_E_INSUFFICIENT_BUFFER:
|
||||
msg = "Insufficient buffer.";
|
||||
break;
|
||||
case SCARD_E_UNKNOWN_READER:
|
||||
msg = "Unknown reader specified.";
|
||||
break;
|
||||
case SCARD_E_TIMEOUT:
|
||||
msg = "Command timeout.";
|
||||
break;
|
||||
case SCARD_E_SHARING_VIOLATION:
|
||||
msg = "Sharing violation.";
|
||||
break;
|
||||
case SCARD_E_NO_SMARTCARD:
|
||||
msg = "No smart card inserted.";
|
||||
break;
|
||||
case SCARD_E_UNKNOWN_CARD:
|
||||
msg = "Unknown card.";
|
||||
break;
|
||||
case SCARD_E_CANT_DISPOSE:
|
||||
msg = "Cannot dispose handle.";
|
||||
break;
|
||||
case SCARD_E_PROTO_MISMATCH:
|
||||
msg = "Card protocol mismatch.";
|
||||
break;
|
||||
case SCARD_E_NOT_READY:
|
||||
msg = "Subsystem not ready.";
|
||||
break;
|
||||
case SCARD_E_INVALID_VALUE:
|
||||
msg = "Invalid value given.";
|
||||
break;
|
||||
case SCARD_E_SYSTEM_CANCELLED:
|
||||
msg = "System cancelled.";
|
||||
break;
|
||||
case SCARD_F_COMM_ERROR:
|
||||
msg = "RPC transport error.";
|
||||
break;
|
||||
case SCARD_F_UNKNOWN_ERROR:
|
||||
msg = "Unknown error.";
|
||||
break;
|
||||
case SCARD_E_INVALID_ATR:
|
||||
msg = "Invalid ATR.";
|
||||
break;
|
||||
case SCARD_E_NOT_TRANSACTED:
|
||||
msg = "Transaction failed.";
|
||||
break;
|
||||
case SCARD_E_READER_UNAVAILABLE:
|
||||
msg = "Reader is unavailable.";
|
||||
break;
|
||||
/* case SCARD_P_SHUTDOWN: */
|
||||
case SCARD_E_PCI_TOO_SMALL:
|
||||
msg = "PCI struct too small.";
|
||||
break;
|
||||
case SCARD_E_READER_UNSUPPORTED:
|
||||
msg = "Reader is unsupported.";
|
||||
break;
|
||||
case SCARD_E_DUPLICATE_READER:
|
||||
msg = "Reader already exists.";
|
||||
break;
|
||||
case SCARD_E_CARD_UNSUPPORTED:
|
||||
msg = "Card is unsupported.";
|
||||
break;
|
||||
case SCARD_E_NO_SERVICE:
|
||||
msg = "Service not available.";
|
||||
break;
|
||||
case SCARD_E_SERVICE_STOPPED:
|
||||
msg = "Service was stopped.";
|
||||
break;
|
||||
/* case SCARD_E_UNEXPECTED: */
|
||||
/* case SCARD_E_ICC_CREATEORDER: */
|
||||
/* case SCARD_E_UNSUPPORTED_FEATURE: */
|
||||
/* case SCARD_E_DIR_NOT_FOUND: */
|
||||
/* case SCARD_E_NO_DIR: */
|
||||
/* case SCARD_E_NO_FILE: */
|
||||
/* case SCARD_E_NO_ACCESS: */
|
||||
/* case SCARD_E_WRITE_TOO_MANY: */
|
||||
/* case SCARD_E_BAD_SEEK: */
|
||||
/* case SCARD_E_INVALID_CHV: */
|
||||
/* case SCARD_E_UNKNOWN_RES_MNG: */
|
||||
/* case SCARD_E_NO_SUCH_CERTIFICATE: */
|
||||
/* case SCARD_E_CERTIFICATE_UNAVAILABLE: */
|
||||
case SCARD_E_NO_READERS_AVAILABLE:
|
||||
msg = "Cannot find a smart card reader.";
|
||||
break;
|
||||
/* case SCARD_E_COMM_DATA_LOST: */
|
||||
/* case SCARD_E_NO_KEY_CONTAINER: */
|
||||
/* case SCARD_E_SERVER_TOO_BUSY: */
|
||||
case SCARD_W_UNSUPPORTED_CARD:
|
||||
msg = "Card is not supported.";
|
||||
break;
|
||||
case SCARD_W_UNRESPONSIVE_CARD:
|
||||
msg = "Card is unresponsive.";
|
||||
break;
|
||||
case SCARD_W_UNPOWERED_CARD:
|
||||
msg = "Card is unpowered.";
|
||||
break;
|
||||
case SCARD_W_RESET_CARD:
|
||||
msg = "Card was reset.";
|
||||
break;
|
||||
case SCARD_W_REMOVED_CARD:
|
||||
msg = "Card was removed.";
|
||||
break;
|
||||
/* case SCARD_W_SECURITY_VIOLATION: */
|
||||
/* case SCARD_W_WRONG_CHV: */
|
||||
/* case SCARD_W_CHV_BLOCKED: */
|
||||
/* case SCARD_W_EOF: */
|
||||
/* case SCARD_W_CANCELLED_BY_USER: */
|
||||
/* case SCARD_W_CARD_NOT_AUTHENTICATED: */
|
||||
|
||||
case SCARD_E_UNSUPPORTED_FEATURE:
|
||||
msg = "Feature not supported.";
|
||||
break;
|
||||
default:
|
||||
(void)snprintf(strError, sizeof(strError) - 1, "Unknown error: 0x%08lX",
|
||||
pcscError);
|
||||
};
|
||||
|
||||
if (msg)
|
||||
(void)strncpy(strError, msg, sizeof(strError));
|
||||
else
|
||||
(void)snprintf(strError, sizeof(strError) - 1, "Unknown error: 0x%08lX",
|
||||
pcscError);
|
||||
|
||||
/* add a null byte */
|
||||
strError[sizeof(strError) - 1] = '\0';
|
||||
|
||||
return strError;
|
||||
}
|
||||
|
||||
const char *
|
||||
pcsc_strerror(const struct nfc_device *pnd)
|
||||
{
|
||||
return stringify_error(DRIVER_DATA(pnd)->last_error);
|
||||
}
|
||||
|
||||
int pcsc_initiator_init(struct nfc_device *pnd)
|
||||
{
|
||||
(void) pnd;
|
||||
return NFC_SUCCESS;
|
||||
}
|
||||
|
||||
int pcsc_initiator_select_passive_target(struct nfc_device *pnd, const nfc_modulation nm, const uint8_t *pbtInitData, const size_t szInitData, nfc_target *pnt)
|
||||
{
|
||||
uint8_t pbAtr[MAX_ATR_SIZE];
|
||||
uint8_t uid[10];
|
||||
DWORD dwState, dwProtocol, dwReaderLen, dwAtrLen = sizeof pbAtr;
|
||||
|
||||
(void) pbtInitData;
|
||||
(void) szInitData;
|
||||
|
||||
if (nm.nbr != pcsc_supported_brs[0] && nm.nbr != pcsc_supported_brs[1])
|
||||
return NFC_EINVARG;
|
||||
DRIVER_DATA(pnd)->last_error = SCardStatus(DRIVER_DATA(pnd)->hCard, NULL, &dwReaderLen, &dwState, &dwProtocol, pbAtr, &dwAtrLen);
|
||||
if ((DRIVER_DATA(pnd)->last_error != SCARD_S_SUCCESS
|
||||
&& DRIVER_DATA(pnd)->last_error != SCARD_W_RESET_CARD)
|
||||
|| !(dwState & SCARD_PRESENT)) {
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "No target present");
|
||||
return NFC_ENOTSUCHDEV;
|
||||
}
|
||||
uint8_t it = pcsc_get_icc_type(pnd);
|
||||
int szuid = pcsc_get_uid(pnd, uid, sizeof uid);
|
||||
if (pcsc_props_to_target(it, pbAtr, dwAtrLen, uid, szuid, nm.nmt, pnt) != NFC_SUCCESS
|
||||
|| (DRIVER_DATA(pnd)->last_error = SCardReconnect((DRIVER_DATA(pnd)->hCard), SCARD_SHARE_SHARED, SCARD_PROTOCOL_T0 | SCARD_PROTOCOL_T1, SCARD_LEAVE_CARD, (void *) & (DRIVER_DATA(pnd)->ioCard.dwProtocol))) != SCARD_S_SUCCESS) {
|
||||
// We can not reconnect to this device.
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "PCSC reconnect failed");
|
||||
return NFC_EIO;
|
||||
}
|
||||
DRIVER_DATA(pnd)->dwShareMode = SCARD_SHARE_SHARED;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int pcsc_initiator_deselect_target(struct nfc_device *pnd)
|
||||
{
|
||||
DRIVER_DATA(pnd)->last_error = SCardReconnect((DRIVER_DATA(pnd)->hCard), SCARD_SHARE_DIRECT, 0, SCARD_LEAVE_CARD, (void *) & (DRIVER_DATA(pnd)->ioCard.dwProtocol));
|
||||
if (DRIVER_DATA(pnd)->last_error != SCARD_S_SUCCESS
|
||||
&& DRIVER_DATA(pnd)->last_error != SCARD_W_RESET_CARD) {
|
||||
// We can not reconnect to this device.
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "PCSC reconnect failed");
|
||||
return NFC_EIO;
|
||||
}
|
||||
DRIVER_DATA(pnd)->dwShareMode = SCARD_SHARE_DIRECT;
|
||||
return NFC_SUCCESS;
|
||||
}
|
||||
|
||||
int pcsc_initiator_transceive_bytes(struct nfc_device *pnd, const uint8_t *pbtTx, const size_t szTx, uint8_t *pbtRx, const size_t szRx, int timeout)
|
||||
{
|
||||
// FIXME: timeout is not handled
|
||||
(void) timeout;
|
||||
|
||||
DWORD dwRxLen = szRx;
|
||||
|
||||
LOG_HEX(NFC_LOG_GROUP_COM, "TX", pbtTx, szTx);
|
||||
DRIVER_DATA(pnd)->last_error = SCardTransmit(DRIVER_DATA(pnd)->hCard, &(DRIVER_DATA(pnd)->ioCard), pbtTx, szTx, NULL, pbtRx, &dwRxLen);
|
||||
if (DRIVER_DATA(pnd)->last_error != SCARD_S_SUCCESS) {
|
||||
log_put(LOG_GROUP, LOG_CATEGORY, NFC_LOG_PRIORITY_DEBUG, "%s", "PCSC transmit failed");
|
||||
pnd->last_error = NFC_EIO;
|
||||
return pnd->last_error;
|
||||
}
|
||||
LOG_HEX(NFC_LOG_GROUP_COM, "RX", pbtRx, dwRxLen);
|
||||
|
||||
return dwRxLen;
|
||||
}
|
||||
|
||||
int pcsc_initiator_target_is_present(struct nfc_device *pnd, const nfc_target *pnt)
|
||||
{
|
||||
uint8_t pbAtr[MAX_ATR_SIZE];
|
||||
DWORD dwState, dwProtocol, dwReaderLen, dwAtrLen = sizeof pbAtr;
|
||||
nfc_target nt;
|
||||
|
||||
DRIVER_DATA(pnd)->last_error = SCardStatus(DRIVER_DATA(pnd)->hCard, NULL, &dwReaderLen, &dwState, &dwProtocol, pbAtr, &dwAtrLen);
|
||||
if ((DRIVER_DATA(pnd)->last_error != SCARD_S_SUCCESS
|
||||
&& DRIVER_DATA(pnd)->last_error != SCARD_W_RESET_CARD)
|
||||
|| !(dwState & SCARD_PRESENT)) {
|
||||
pnd->last_error = NFC_EIO;
|
||||
return pnd->last_error;
|
||||
}
|
||||
if (pnt) {
|
||||
if (pcsc_props_to_target(ICC_TYPE_UNKNOWN, pbAtr, dwAtrLen, NULL, 0, pnt->nm.nmt, &nt) != NFC_SUCCESS
|
||||
|| pnt->nm.nmt != nt.nm.nmt || pnt->nm.nbr != nt.nm.nbr) {
|
||||
pnd->last_error = NFC_EINVARG;
|
||||
return pnd->last_error;
|
||||
}
|
||||
}
|
||||
return NFC_SUCCESS;
|
||||
}
|
||||
|
||||
int pcsc_device_set_property_bool(struct nfc_device *pnd, const nfc_property property, const bool bEnable)
|
||||
{
|
||||
(void) pnd;
|
||||
switch (property) {
|
||||
case NP_INFINITE_SELECT:
|
||||
// ignore
|
||||
return NFC_SUCCESS;
|
||||
case NP_AUTO_ISO14443_4:
|
||||
case NP_EASY_FRAMING:
|
||||
case NP_FORCE_ISO14443_A:
|
||||
case NP_HANDLE_CRC:
|
||||
case NP_HANDLE_PARITY:
|
||||
case NP_FORCE_SPEED_106:
|
||||
if (bEnable == true)
|
||||
return NFC_SUCCESS;
|
||||
break;
|
||||
case NP_ACCEPT_INVALID_FRAMES:
|
||||
case NP_ACCEPT_MULTIPLE_FRAMES:
|
||||
if (bEnable == false)
|
||||
return NFC_SUCCESS;
|
||||
break;
|
||||
case NP_ACTIVATE_FIELD:
|
||||
if (bEnable == false) {
|
||||
SCardReconnect(DRIVER_DATA(pnd)->hCard, DRIVER_DATA(pnd)->dwShareMode, DRIVER_DATA(pnd)->ioCard.dwProtocol, SCARD_RESET_CARD, (void *) & (DRIVER_DATA(pnd)->ioCard.dwProtocol));
|
||||
}
|
||||
return NFC_SUCCESS;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NFC_EDEVNOTSUPP;
|
||||
}
|
||||
|
||||
int pcsc_get_supported_modulation(struct nfc_device *pnd, const nfc_mode mode, const nfc_modulation_type **const supported_mt)
|
||||
{
|
||||
(void) pnd;
|
||||
if (mode == N_TARGET || NULL == supported_mt)
|
||||
return NFC_EINVARG;
|
||||
*supported_mt = pcsc_supported_mts;
|
||||
return NFC_SUCCESS;
|
||||
}
|
||||
|
||||
int pcsc_get_supported_baud_rate(struct nfc_device *pnd, const nfc_mode mode, const nfc_modulation_type nmt, const nfc_baud_rate **const supported_br)
|
||||
{
|
||||
(void) pnd;
|
||||
(void) nmt;
|
||||
if (mode == N_TARGET || NULL == supported_br)
|
||||
return NFC_EINVARG;
|
||||
*supported_br = pcsc_supported_brs;
|
||||
return NFC_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
pcsc_get_information_about(nfc_device *pnd, char **pbuf)
|
||||
{
|
||||
LPBYTE name = NULL, version = NULL, type = NULL, serial = NULL;
|
||||
DWORD name_len = SCARD_AUTOALLOCATE, version_len = SCARD_AUTOALLOCATE,
|
||||
type_len = SCARD_AUTOALLOCATE, serial_len = SCARD_AUTOALLOCATE;
|
||||
int res = NFC_SUCCESS;
|
||||
SCARDCONTEXT *pscc;
|
||||
|
||||
if (!(pscc = pcsc_get_scardcontext())) {
|
||||
pnd->last_error = NFC_ESOFT;
|
||||
return pnd->last_error;
|
||||
}
|
||||
|
||||
SCardGetAttrib(DRIVER_DATA(pnd)->hCard, SCARD_ATTR_VENDOR_NAME, (LPBYTE)&name, &name_len);
|
||||
SCardGetAttrib(DRIVER_DATA(pnd)->hCard, SCARD_ATTR_VENDOR_IFD_TYPE, (LPBYTE)&type, &type_len);
|
||||
SCardGetAttrib(DRIVER_DATA(pnd)->hCard, SCARD_ATTR_VENDOR_IFD_VERSION, (LPBYTE)&version, &version_len);
|
||||
SCardGetAttrib(DRIVER_DATA(pnd)->hCard, SCARD_ATTR_VENDOR_IFD_SERIAL_NO, (LPBYTE)&serial, &serial_len);
|
||||
|
||||
*pbuf = malloc(name_len + type_len + version_len + serial_len + 30);
|
||||
if (! *pbuf) {
|
||||
res = NFC_ESOFT;
|
||||
goto error;
|
||||
}
|
||||
sprintf((char *) *pbuf,
|
||||
"%s" // model
|
||||
"%s%s" // version
|
||||
" (%s)" // vendor
|
||||
"%s%s\n" // serial
|
||||
,
|
||||
name && name_len > 0 && name[0] != '\0'
|
||||
? (char *)name : "unknown model",
|
||||
version && version_len > 0 && version[0] != '\0'
|
||||
? " " : "", version_len > 0 ? (char *)version : "",
|
||||
type && type_len > 0 && type[0] != '\0'
|
||||
? (char *)type : "unknown vendor",
|
||||
serial && serial_len > 0 && serial[0] != '\0'
|
||||
? "\nserial: " : "", serial_len > 0 ? (char *)serial : "");
|
||||
|
||||
error:
|
||||
SCardFreeMemory(*pscc, name);
|
||||
SCardFreeMemory(*pscc, type);
|
||||
SCardFreeMemory(*pscc, version);
|
||||
SCardFreeMemory(*pscc, serial);
|
||||
|
||||
pnd->last_error = res;
|
||||
return pnd->last_error;
|
||||
}
|
||||
|
||||
const struct nfc_driver pcsc_driver = {
|
||||
.name = PCSC_DRIVER_NAME,
|
||||
.scan = pcsc_scan,
|
||||
.open = pcsc_open,
|
||||
.close = pcsc_close,
|
||||
.strerror = pcsc_strerror,
|
||||
|
||||
.initiator_init = pcsc_initiator_init,
|
||||
.initiator_init_secure_element = NULL, // No secure-element support
|
||||
.initiator_select_passive_target = pcsc_initiator_select_passive_target,
|
||||
.initiator_poll_target = NULL,
|
||||
.initiator_select_dep_target = NULL,
|
||||
.initiator_deselect_target = NULL,
|
||||
.initiator_transceive_bytes = pcsc_initiator_transceive_bytes,
|
||||
.initiator_transceive_bits = NULL,
|
||||
.initiator_transceive_bytes_timed = NULL,
|
||||
.initiator_transceive_bits_timed = NULL,
|
||||
.initiator_target_is_present = pcsc_initiator_target_is_present,
|
||||
|
||||
.target_init = NULL,
|
||||
.target_send_bytes = NULL,
|
||||
.target_receive_bytes = NULL,
|
||||
.target_send_bits = NULL,
|
||||
.target_receive_bits = NULL,
|
||||
|
||||
.device_set_property_bool = pcsc_device_set_property_bool,
|
||||
.device_set_property_int = NULL,
|
||||
.get_supported_modulation = pcsc_get_supported_modulation,
|
||||
.get_supported_baud_rate = pcsc_get_supported_baud_rate,
|
||||
.device_get_information_about = pcsc_get_information_about,
|
||||
|
||||
.abort_command = NULL, // Abort is not supported in this driver
|
||||
.idle = NULL,
|
||||
.powerdown = NULL,
|
||||
};
|
||||
|
35
libnfc/drivers/pcsc.h
Normal file
35
libnfc/drivers/pcsc.h
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*-
|
||||
* Free/Libre Near Field Communication (NFC) library
|
||||
*
|
||||
* Libnfc historical contributors:
|
||||
* Copyright (C) 2019 Frank Morgner
|
||||
* See AUTHORS file for a more comprehensive list of contributors.
|
||||
* Additional contributors of this file:
|
||||
*
|
||||
* This program 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 3 of the License, or (at your
|
||||
* option) any later version.
|
||||
*
|
||||
* This program 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 Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file pcsc.h
|
||||
* @brief Driver for non-ACR122 devices (behind PC/SC daemon)
|
||||
*/
|
||||
|
||||
#ifndef __NFC_DRIVER_PCSC_H__
|
||||
#define __NFC_DRIVER_PCSC_H__
|
||||
|
||||
#include <nfc/nfc-types.h>
|
||||
|
||||
extern const struct nfc_driver pcsc_driver;
|
||||
|
||||
#endif // ! __NFC_DRIVER_PCSC_H__
|
|
@ -87,6 +87,10 @@
|
|||
#include "target-subr.h"
|
||||
#include "drivers.h"
|
||||
|
||||
#if defined (DRIVER_PCSC_ENABLED)
|
||||
# include "drivers/pcsc.h"
|
||||
#endif /* DRIVER_PCSC_ENABLED */
|
||||
|
||||
#if defined (DRIVER_ACR122_PCSC_ENABLED)
|
||||
# include "drivers/acr122_pcsc.h"
|
||||
#endif /* DRIVER_ACR122_PCSC_ENABLED */
|
||||
|
@ -136,6 +140,9 @@ nfc_drivers_init(void)
|
|||
#if defined (DRIVER_PN53X_USB_ENABLED)
|
||||
nfc_register_driver(&pn53x_usb_driver);
|
||||
#endif /* DRIVER_PN53X_USB_ENABLED */
|
||||
#if defined (DRIVER_PCSC_ENABLED)
|
||||
nfc_register_driver(&pcsc_driver);
|
||||
#endif /* DRIVER_ACR122_PCSC_ENABLED */
|
||||
#if defined (DRIVER_ACR122_PCSC_ENABLED)
|
||||
nfc_register_driver(&acr122_pcsc_driver);
|
||||
#endif /* DRIVER_ACR122_PCSC_ENABLED */
|
||||
|
|
|
@ -4,7 +4,7 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
|
|||
[
|
||||
AC_MSG_CHECKING(which drivers to build)
|
||||
AC_ARG_WITH(drivers,
|
||||
AS_HELP_STRING([--with-drivers=DRIVERS], [Use a custom driver set, where DRIVERS is a coma-separated list of drivers to build support for. Available drivers are: 'acr122_pcsc', 'acr122_usb', 'acr122s', 'arygon', 'pn532_i2c', 'pn532_spi', 'pn532_uart' and 'pn53x_usb'. Default drivers set is 'acr122_usb,acr122s,arygon,pn532_i2c,pn532_spi,pn532_uart,pn53x_usb'. The special driver set 'all' compile all available drivers.]),
|
||||
AS_HELP_STRING([--with-drivers=DRIVERS], [Use a custom driver set, where DRIVERS is a coma-separated list of drivers to build support for. Available drivers are: 'pcsc', 'acr122_pcsc', 'acr122_usb', 'acr122s', 'arygon', 'pn532_i2c', 'pn532_spi', 'pn532_uart' and 'pn53x_usb'. Default drivers set is 'acr122_usb,acr122s,arygon,pn532_i2c,pn532_spi,pn532_uart,pn53x_usb'. The special driver set 'all' compile all available drivers.]),
|
||||
[ case "${withval}" in
|
||||
yes | no)
|
||||
dnl ignore calls without any arguments
|
||||
|
@ -36,7 +36,7 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
|
|||
fi
|
||||
;;
|
||||
all)
|
||||
DRIVER_BUILD_LIST="acr122_pcsc acr122_usb acr122s arygon pn53x_usb pn532_uart"
|
||||
DRIVER_BUILD_LIST="pcsc acr122_pcsc acr122_usb acr122s arygon pn53x_usb pn532_uart"
|
||||
if test x"$spi_available" = x"yes"
|
||||
then
|
||||
DRIVER_BUILD_LIST="$DRIVER_BUILD_LIST pn532_spi"
|
||||
|
@ -50,6 +50,7 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
|
|||
|
||||
DRIVERS_CFLAGS=""
|
||||
|
||||
driver_pcsc_enabled="no"
|
||||
driver_acr122_pcsc_enabled="no"
|
||||
driver_acr122_usb_enabled="no"
|
||||
driver_acr122s_enabled="no"
|
||||
|
@ -62,6 +63,11 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
|
|||
for driver in ${DRIVER_BUILD_LIST}
|
||||
do
|
||||
case "${driver}" in
|
||||
pcsc)
|
||||
pcsc_required="yes"
|
||||
driver_pcsc_enabled="yes"
|
||||
DRIVERS_CFLAGS="$DRIVERS_CFLAGS -DDRIVER_PCSC_ENABLED"
|
||||
;;
|
||||
acr122_pcsc)
|
||||
pcsc_required="yes"
|
||||
driver_acr122_pcsc_enabled="yes"
|
||||
|
@ -109,6 +115,7 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
|
|||
done
|
||||
AC_SUBST(DRIVERS_CFLAGS)
|
||||
AM_CONDITIONAL(DRIVER_ACR122_PCSC_ENABLED, [test x"$driver_acr122_pcsc_enabled" = xyes])
|
||||
AM_CONDITIONAL(DRIVER_PCSC_ENABLED, [test x"$driver_pcsc_enabled" = xyes])
|
||||
AM_CONDITIONAL(DRIVER_ACR122_USB_ENABLED, [test x"$driver_acr122_usb_enabled" = xyes])
|
||||
AM_CONDITIONAL(DRIVER_ACR122S_ENABLED, [test x"$driver_acr122s_enabled" = xyes])
|
||||
AM_CONDITIONAL(DRIVER_PN53X_USB_ENABLED, [test x"$driver_pn53x_usb_enabled" = xyes])
|
||||
|
@ -121,6 +128,7 @@ AC_DEFUN([LIBNFC_ARG_WITH_DRIVERS],
|
|||
AC_DEFUN([LIBNFC_DRIVERS_SUMMARY],[
|
||||
echo
|
||||
echo "Selected drivers:"
|
||||
echo " pcsc............. $driver_pcsc_enabled"
|
||||
echo " acr122_pcsc...... $driver_acr122_pcsc_enabled"
|
||||
echo " acr122_usb....... $driver_acr122_usb_enabled"
|
||||
echo " acr122s.......... $driver_acr122s_enabled"
|
||||
|
|
Loading…
Reference in a new issue