diff --git a/virtualsmartcard/doc/README.txt b/virtualsmartcard/doc/README.txt index d22aa37..466e523 100644 --- a/virtualsmartcard/doc/README.txt +++ b/virtualsmartcard/doc/README.txt @@ -37,9 +37,10 @@ Currently the Virtual Smart Card supports the following types of smart cards: The |vpcd| is a smart card driver for PCSC-Lite_. It allows smart card applications to access the |vpicc| through the PC/SC API. By -default |vpicc| communicates with |vpcd| through a socket on localhost port -35963. But the |vpicc| does not need to run on the same machine as the |vpcd|, -they can connect over the internet for example. +default |vpcd| opens slots for communication with multiple |vpicc|'s on +localhost from port 35963 to port 35972. But the |vpicc| does not need to run +on the same machine as the |vpcd|, they can connect over the internet for +example. Although the Virtual Smart Card is a software emulator, you can use :ref:`pcsc-relay` to make it accessible to an external contact-less smart card diff --git a/virtualsmartcard/doc/README.txt.in b/virtualsmartcard/doc/README.txt.in index 4acacfd..4e6fb1e 100644 --- a/virtualsmartcard/doc/README.txt.in +++ b/virtualsmartcard/doc/README.txt.in @@ -37,9 +37,10 @@ Currently the @PACKAGE_NAME@ supports the following types of smart cards: The |vpcd| is a smart card driver for PCSC-Lite_. It allows smart card applications to access the |vpicc| through the PC/SC API. By -default |vpicc| communicates with |vpcd| through a socket on localhost port -35963. But the |vpicc| does not need to run on the same machine as the |vpcd|, -they can connect over the internet for example. +default |vpcd| opens slots for communication with multiple |vpicc|'s on +localhost from port 35963 to port 35972. But the |vpicc| does not need to run +on the same machine as the |vpcd|, they can connect over the internet for +example. Although the @PACKAGE_NAME@ is a software emulator, you can use :ref:`pcsc-relay` to make it accessible to an external contact-less smart card diff --git a/virtualsmartcard/src/vpcd/ifd-vpcd.c b/virtualsmartcard/src/vpcd/ifd-vpcd.c index 7589169..507d93b 100644 --- a/virtualsmartcard/src/vpcd/ifd-vpcd.c +++ b/virtualsmartcard/src/vpcd/ifd-vpcd.c @@ -18,14 +18,28 @@ #include "vpcd.h" +/* pcscd allows at most 16 readers. We will use 10. + * See PCSCLITE_MAX_READERS_CONTEXTS in pcsclite.h */ +#define VICC_MAX_SLOTS \ + (PCSCLITE_MAX_READERS_CONTEXTS > 6 ? \ + PCSCLITE_MAX_READERS_CONTEXTS-6 : \ + 1) + +static struct vicc_ctx *ctx[VICC_MAX_SLOTS]; + RESPONSECODE IFDHCreateChannel (DWORD Lun, DWORD Channel) { - if (vicc_init(Channel) < 0) { + size_t slot = Lun & 0xffff; + if (slot >= VICC_MAX_SLOTS) { + return IFD_COMMUNICATION_ERROR; + } + ctx[slot] = vicc_init(Channel+slot); + if (!ctx[slot]) { Log1(PCSC_LOG_ERROR, "Could not initialize connection to virtual ICC"); return IFD_COMMUNICATION_ERROR; } - Log2(PCSC_LOG_INFO, "Waiting for virtual ICC on port %hu", (unsigned short) Channel); + Log2(PCSC_LOG_INFO, "Waiting for virtual ICC on port %hu", (unsigned short) Channel+slot); return IFD_SUCCESS; } @@ -66,13 +80,19 @@ IFDHControl(DWORD Lun, PUCHAR TxBuffer, DWORD TxLength, PUCHAR RxBuffer, RESPONSECODE IFDHCloseChannel (DWORD Lun) { - RESPONSECODE r = vicc_eject(); - if (vicc_exit() < 0) { + size_t slot = Lun & 0xffff; + if (slot >= VICC_MAX_SLOTS) { + return IFD_COMMUNICATION_ERROR; + } + Log2(PCSC_LOG_ERROR, "Slot %u", slot); + if (vicc_exit(ctx[slot]) < 0) { Log1(PCSC_LOG_ERROR, "Could not close connection to virtual ICC"); return IFD_COMMUNICATION_ERROR; } + ctx[slot] = NULL; + Log2(PCSC_LOG_ERROR, "Slot %u", slot); - return r; + return IFD_SUCCESS; } RESPONSECODE @@ -80,6 +100,10 @@ IFDHGetCapabilities (DWORD Lun, DWORD Tag, PDWORD Length, PUCHAR Value) { unsigned char *atr = NULL; ssize_t size; + size_t slot = Lun & 0xffff; + if (slot >= VICC_MAX_SLOTS) { + return IFD_COMMUNICATION_ERROR; + } if (!Length || !Value) return IFD_COMMUNICATION_ERROR; @@ -87,7 +111,7 @@ IFDHGetCapabilities (DWORD Lun, DWORD Tag, PDWORD Length, PUCHAR Value) switch (Tag) { case TAG_IFD_ATR: - size = vicc_getatr(&atr); + size = vicc_getatr(ctx[slot], &atr); if (size < 0) { Log1(PCSC_LOG_ERROR, "could not get ATR"); return IFD_COMMUNICATION_ERROR; @@ -115,6 +139,13 @@ IFDHGetCapabilities (DWORD Lun, DWORD Tag, PDWORD Length, PUCHAR Value) return IFD_COMMUNICATION_ERROR; } + *Value = VICC_MAX_SLOTS; + *Length = 1; + break; + + case TAG_IFD_SLOT_THREAD_SAFE: + /* driver supports access to multiple slots of the same reader at + * the same time */ *Value = 1; *Length = 1; break; @@ -149,9 +180,13 @@ IFDHSetProtocolParameters (DWORD Lun, DWORD Protocol, UCHAR Flags, UCHAR PTS1, RESPONSECODE IFDHPowerICC (DWORD Lun, DWORD Action, PUCHAR Atr, PDWORD AtrLength) { + size_t slot = Lun & 0xffff; + if (slot >= VICC_MAX_SLOTS) { + return IFD_COMMUNICATION_ERROR; + } switch (Action) { case IFD_POWER_DOWN: - if (vicc_poweroff() < 0) { + if (vicc_poweroff(ctx[slot]) < 0) { Log1(PCSC_LOG_ERROR, "could not powerdown"); return IFD_COMMUNICATION_ERROR; } @@ -163,13 +198,13 @@ IFDHPowerICC (DWORD Lun, DWORD Action, PUCHAR Atr, PDWORD AtrLength) #endif return IFD_SUCCESS; case IFD_POWER_UP: - if (vicc_poweron() < 0) { + if (vicc_poweron(ctx[slot]) < 0) { Log1(PCSC_LOG_ERROR, "could not powerup"); return IFD_COMMUNICATION_ERROR; } break; case IFD_RESET: - if (vicc_reset() < 0) { + if (vicc_reset(ctx[slot]) < 0) { Log1(PCSC_LOG_ERROR, "could not reset"); return IFD_COMMUNICATION_ERROR; } @@ -190,13 +225,17 @@ IFDHTransmitToICC (DWORD Lun, SCARD_IO_HEADER SendPci, PUCHAR TxBuffer, unsigned char *rapdu = NULL; ssize_t size; RESPONSECODE r = IFD_COMMUNICATION_ERROR; + size_t slot = Lun & 0xffff; + if (slot >= VICC_MAX_SLOTS) { + return IFD_COMMUNICATION_ERROR; + } if (!RxLength || !RecvPci) { Log1(PCSC_LOG_ERROR, "Invalid input data"); goto err; } - size = vicc_transmit(TxLength, TxBuffer, &rapdu); + size = vicc_transmit(ctx[slot], TxLength, TxBuffer, &rapdu); if (size < 0) { Log1(PCSC_LOG_ERROR, "could not send apdu or receive rapdu"); @@ -226,7 +265,11 @@ err: RESPONSECODE IFDHICCPresence (DWORD Lun) { - switch (vicc_present()) { + size_t slot = Lun & 0xffff; + if (slot >= VICC_MAX_SLOTS) { + return IFD_COMMUNICATION_ERROR; + } + switch (vicc_present(ctx[slot])) { case 0: return IFD_ICC_NOT_PRESENT; case 1: diff --git a/virtualsmartcard/src/vpcd/vpcd.c b/virtualsmartcard/src/vpcd/vpcd.c index e20f758..19794ea 100644 --- a/virtualsmartcard/src/vpcd/vpcd.c +++ b/virtualsmartcard/src/vpcd/vpcd.c @@ -43,11 +43,8 @@ typedef WORD uint16_t; #define VPCD_CTRL_RESET 2 #define VPCD_CTRL_ATR 4 -static int server_sock = -1; -static int client_sock = -1; - -ssize_t sendToVICC(size_t size, const unsigned char *buffer); -ssize_t recvFromVICC(unsigned char **buffer); +ssize_t sendToVICC(struct vicc_ctx *ctx, size_t size, const unsigned char *buffer); +ssize_t recvFromVICC(struct vicc_ctx *ctx, unsigned char **buffer); static ssize_t sendall(int sock, const void *buffer, size_t size); static ssize_t recvall(int sock, void *buffer, size_t size); @@ -135,37 +132,42 @@ int waitforclient(int server, long secs, long usecs) return 0; } -ssize_t sendToVICC(size_t length, const unsigned char* buffer) +ssize_t sendToVICC(struct vicc_ctx *ctx, size_t length, const unsigned char* buffer) { ssize_t r; uint16_t size; + if (!ctx) { + errno = EINVAL; + return -1; + } + /* send size of message on 2 bytes */ size = htons((uint16_t) length); - r = sendall(client_sock, (void *) &size, sizeof size); + r = sendall(ctx->client_sock, (void *) &size, sizeof size); if (r == sizeof size) /* send message */ - r = sendall(client_sock, buffer, length); + r = sendall(ctx->client_sock, buffer, length); if (r < 0) - vicc_eject(); + vicc_eject(ctx); return r; } -ssize_t recvFromVICC(unsigned char **buffer) +ssize_t recvFromVICC(struct vicc_ctx *ctx, unsigned char **buffer) { ssize_t r; uint16_t size; unsigned char *p = NULL; - if (!buffer) { + if (!buffer || !ctx) { errno = EINVAL; return -1; } /* receive size of message on 2 bytes */ - r = recvall(client_sock, &size, sizeof size); + r = recvall(ctx->client_sock, &size, sizeof size); if (r < sizeof size) return r; @@ -179,57 +181,75 @@ ssize_t recvFromVICC(unsigned char **buffer) *buffer = p; /* receive message */ - return recvall(client_sock, *buffer, size); + return recvall(ctx->client_sock, *buffer, size); } -int vicc_eject(void) { - if (client_sock > 0) { - client_sock = close(client_sock); - if (client_sock < 0) { +int vicc_eject(struct vicc_ctx *ctx) +{ + if (ctx && ctx->client_sock > 0) { + ctx->client_sock = close(ctx->client_sock); + if (ctx->client_sock < 0) { return -1; } } return 0; } -int vicc_init(unsigned short port) { - server_sock = opensock(port); - if (server_sock < 0) - return -1; - - return 0; -} - -int vicc_exit(void) { - if (server_sock > 0) { - server_sock = close(server_sock); - if (server_sock < 0) return -1; +struct vicc_ctx * vicc_init(unsigned short port) +{ + struct vicc_ctx *ctx = malloc(sizeof *ctx); + if (!ctx) { + return NULL; } - return 0; + ctx->server_sock = opensock(port); + if (ctx->server_sock < 0) { + free(ctx); + return NULL; + } + + ctx->client_sock = -1; + + return ctx; } -ssize_t vicc_transmit(size_t apdu_len, - const unsigned char *apdu, unsigned char **rapdu) +int vicc_exit(struct vicc_ctx *ctx) { - ssize_t r; - - r = sendToVICC(apdu_len, apdu); - - if (r > 0) - r = recvFromVICC(rapdu); - - if (r <= 0) - vicc_eject(); + int r = vicc_eject(ctx); + if (ctx) { + if (ctx->server_sock > 0) { + ctx->server_sock = close(ctx->server_sock); + if (ctx->server_sock < 0) { + r -= 1; + } + } + } return r; } -int vicc_present(void) { +ssize_t vicc_transmit(struct vicc_ctx *ctx, + size_t apdu_len, const unsigned char *apdu, + unsigned char **rapdu) +{ + ssize_t r; + + r = sendToVICC(ctx, apdu_len, apdu); + + if (r > 0) + r = recvFromVICC(ctx, rapdu); + + if (r <= 0) + vicc_eject(ctx); + + return r; +} + +int vicc_present(struct vicc_ctx *ctx) { unsigned char *atr = NULL; - if (client_sock > 0) { - if (vicc_getatr(&atr) <= 0) + if (ctx->client_sock > 0) { + if (vicc_getatr(ctx, &atr) <= 0) return 0; free(atr); @@ -237,31 +257,31 @@ int vicc_present(void) { return 1; } else { /* Wait up to one microsecond. */ - client_sock = waitforclient(server_sock, 0, 1); + ctx->client_sock = waitforclient(ctx->server_sock, 0, 1); - if (client_sock < 0) + if (ctx->client_sock < 0) return -1; } return 0; } -ssize_t vicc_getatr(unsigned char **atr) { +ssize_t vicc_getatr(struct vicc_ctx *ctx, unsigned char **atr) { unsigned char i = VPCD_CTRL_ATR; - return vicc_transmit(VPCD_CTRL_LEN, &i, atr); + return vicc_transmit(ctx, VPCD_CTRL_LEN, &i, atr); } -int vicc_poweron(void) { +int vicc_poweron(struct vicc_ctx *ctx) { unsigned char i = VPCD_CTRL_ON; - return sendToVICC(VPCD_CTRL_LEN, &i); + return sendToVICC(ctx, VPCD_CTRL_LEN, &i); } -int vicc_poweroff(void) { +int vicc_poweroff(struct vicc_ctx *ctx) { unsigned char i = VPCD_CTRL_OFF; - return sendToVICC(VPCD_CTRL_LEN, &i); + return sendToVICC(ctx, VPCD_CTRL_LEN, &i); } -int vicc_reset(void) { +int vicc_reset(struct vicc_ctx *ctx) { unsigned char i = VPCD_CTRL_RESET; - return sendToVICC(VPCD_CTRL_LEN, &i); + return sendToVICC(ctx, VPCD_CTRL_LEN, &i); } diff --git a/virtualsmartcard/src/vpcd/vpcd.h b/virtualsmartcard/src/vpcd/vpcd.h index b4b0bf5..ea8be80 100644 --- a/virtualsmartcard/src/vpcd/vpcd.h +++ b/virtualsmartcard/src/vpcd/vpcd.h @@ -26,6 +26,11 @@ typedef int ssize_t; #include #endif +struct vicc_ctx { + int server_sock; + int client_sock; +}; + #ifdef __cplusplus extern "C" { #endif @@ -34,14 +39,14 @@ extern "C" { /** Standard port of the virtual smart card reader */ #define VPCDPORT 35963 -int vicc_init(unsigned short port); -int vicc_exit(void); -int vicc_eject(void); +struct vicc_ctx * vicc_init(unsigned short port); +int vicc_exit(struct vicc_ctx *ctx); +int vicc_eject(struct vicc_ctx *ctx); -int vicc_present(void); -int vicc_poweron(void); -int vicc_poweroff(void); -int vicc_reset(void); +int vicc_present(struct vicc_ctx *ctx); +int vicc_poweron(struct vicc_ctx *ctx); +int vicc_poweroff(struct vicc_ctx *ctx); +int vicc_reset(struct vicc_ctx *ctx); /** * @brief Receive ATR from the virtual smart card. @@ -52,7 +57,7 @@ int vicc_reset(void); * @return On success, the call returns the number of bytes received. * On error, -1 is returned, and errno is set appropriately. */ -ssize_t vicc_getatr(unsigned char** atr); +ssize_t vicc_getatr(struct vicc_ctx *ctx, unsigned char** atr); /** * @brief Send an APDU to the virtual smart card. @@ -66,7 +71,8 @@ ssize_t vicc_getatr(unsigned char** atr); * @return On success, the call returns the number of bytes received. * On error, -1 is returned, and errno is set appropriately. */ -ssize_t vicc_transmit(size_t apdu_len, const unsigned char *apdu, +ssize_t vicc_transmit(struct vicc_ctx *ctx, + size_t apdu_len, const unsigned char *apdu, unsigned char **rapdu); #ifdef __cplusplus