Files
vsmartcard/pcsc-relay/src/vicc.c
2021-04-26 17:15:12 +02:00

172 lines
4.0 KiB
C

/*
* Copyright (C) 2012 Frank Morgner
*
* This file is part of pcsc-relay.
*
* pcsc-relay 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 3 of the License, or (at your option)
* any later version.
*
* pcsc-relay 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
* pcsc-relay. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "pcsc-relay.h"
#include "vpcd.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
unsigned int viccport = VPCDPORT;
char *vicchostname = NULL;
char *viccatr = "3B80800101";
unsigned char atr[256];
size_t atr_len = 0;
static int _vicc_connect(driver_data_t **driver_data)
{
struct vicc_ctx *ctx;
const long secs = 300;
if (!driver_data)
return 0;
if (viccatr) {
size_t i;
char *hex = viccatr;
unsigned char *bin = atr;
atr_len = strlen(viccatr);
if (atr_len % 2 != 0) {
RELAY_ERROR("Length of ATR needs to be even\n");
return 0;
}
atr_len /= 2;
if (atr_len > sizeof atr) {
RELAY_ERROR("ATR too long\n");
return 0;
}
while (*hex) {
if (sscanf(hex, "%2hhX", bin) != 1) {
RELAY_ERROR("bad ATR\n");
return 0;
}
hex += 2;
bin += 1;
}
} else {
atr_len = 0;
}
ctx = vicc_init(vicchostname, viccport);
if (!ctx) {
RELAY_ERROR("Could not initialize connection to VPCD\n");
return 0;
}
*driver_data = ctx;
INFO("Waiting for VPCD on port %hu for %ld seconds\n",
(unsigned short) viccport, secs);
if (vicc_connect(ctx, secs, 0))
return 1;
return 0;
}
static int vicc_disconnect(driver_data_t *driver_data)
{
struct vicc_ctx *ctx = driver_data;
vicc_eject(ctx);
if (vicc_exit(ctx) != 0) {
RELAY_ERROR("Could not close connection to virtual ICC\n");
return 0;
}
return 1;
}
static int vicc_receive_capdu(driver_data_t *driver_data,
unsigned char **capdu, size_t *len)
{
struct vicc_ctx *ctx = driver_data;
int r = 0;
ssize_t size;
if (!len)
goto err;
do {
size = vicc_transmit(ctx, 0, NULL, capdu);
if (size < 0) {
RELAY_ERROR("could not receive request\n");
goto err;
}
if (size == VPCD_CTRL_LEN) {
switch (*capdu[0]) {
case VPCD_CTRL_OFF:
case VPCD_CTRL_ON:
case VPCD_CTRL_RESET:
// ignore reset, power on, power off
break;
case VPCD_CTRL_ATR:
if (vicc_transmit(ctx, atr_len, atr, NULL) < 0) {
RELAY_ERROR("could not send ATR\n");
goto err;
}
break;
default:
RELAY_ERROR("Unknown request: 0x%0X\n", *capdu[0]);
goto err;
}
} else {
// finaly we got the capdu
*len = size;
r = 1;
}
} while (!r);
err:
return r;
}
static int vicc_send_rapdu(driver_data_t *driver_data,
const unsigned char *rapdu, size_t len)
{
struct vicc_ctx *ctx = driver_data;
if (!ctx || !rapdu)
return 0;
if (vicc_transmit(ctx, len, rapdu, NULL) < 0) {
RELAY_ERROR("could not send R-APDU\n");
return 0;
}
return 1;
}
struct rf_driver driver_vicc = {
.connect = _vicc_connect,
.disconnect = vicc_disconnect,
.receive_capdu = vicc_receive_capdu,
.send_rapdu = vicc_send_rapdu,
};