mirror of
https://github.com/adulau/ootp.git
synced 2024-11-21 17:47:10 +00:00
802 lines
19 KiB
C
802 lines
19 KiB
C
/*
|
|
* Copyright (c) 2005 Mark Fullmer
|
|
* Copyright (c) 2009 Mark Fullmer and the Ohio State University
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*
|
|
* $Id: scr.c 73 2009-12-21 05:14:46Z maf $
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
#include <sys/param.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include "scr.h"
|
|
#include "xerr.h"
|
|
|
|
#ifdef SCR_PCSC
|
|
#include <wintypes.h>
|
|
#include <winscard.h>
|
|
#endif /* SCR_PCSC */
|
|
|
|
#if HAVE_STRINGS_H
|
|
#include <strings.h>
|
|
#endif
|
|
#if HAVE_STRING_H
|
|
#include <string.h>
|
|
#endif
|
|
|
|
/*
|
|
*
|
|
* High level abstraction for Smart Card communications. Supports
|
|
* embedded ACR30 driver and PC/SC combatible readers with PCSC-Lite
|
|
*
|
|
* scr_ctx_new() - create context / allocate resources
|
|
* scr_ctx_free() - free context / deallocate resources
|
|
* scr_ctx_valid() - context valid check
|
|
* scr_ctx_connect() - connect to SC reader
|
|
* scr_ctx_reset() - reset SC
|
|
* scr_ctx_cmd() - issue command to SC
|
|
* scr_checksw1sw2_rx() - chek SW1SW2 bytes from SC command response
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* function: scr_ctx_new()
|
|
*
|
|
* Allocate scr context.
|
|
*
|
|
* arguments:
|
|
*
|
|
* valid_readers - SCR_READER_* bitmap to enable reader classes.
|
|
* support EMBEDDED_ACR30S and PCSC
|
|
* verbose - enable verbose output
|
|
*
|
|
* returns: scr_ctx or 0L on failure
|
|
*
|
|
*/
|
|
struct scr_ctx* scr_ctx_new(int valid_readers, int verbose)
|
|
{
|
|
struct scr_ctx *scrctx;
|
|
size_t ralloc;
|
|
int i, r, ret, cur_reader;
|
|
char *buf;
|
|
#ifdef SCR_PCSC
|
|
char *pcsc_rdr_buf, *p;
|
|
DWORD pcsc_rdr_buf_len;
|
|
#endif /* SCR_PCSC */
|
|
|
|
ret = -1; /* fail */
|
|
ralloc = 0;
|
|
#ifdef SCR_PCSC
|
|
pcsc_rdr_buf = (char*)0L;
|
|
#endif /* SCR_PCSC */
|
|
|
|
if (!(scrctx = (struct scr_ctx*)malloc(sizeof *scrctx))) {
|
|
if (verbose)
|
|
xerr_warn("malloc(scrctx)");
|
|
goto scr_ctx_new_out;
|
|
}
|
|
|
|
bzero(scrctx, sizeof *scrctx);
|
|
scrctx->verbose = verbose;
|
|
|
|
#ifdef SCR_PCSC
|
|
|
|
if (valid_readers & SCR_READER_PCSC) {
|
|
|
|
if ((r = SCardEstablishContext(SCARD_SCOPE_SYSTEM, (void*)0L, (void*)0L,
|
|
&scrctx->hContext)) != SCARD_S_SUCCESS) {
|
|
|
|
if (scrctx->verbose)
|
|
xerr_warnx("SCardEstablishContext(): %s.", pcsc_stringify_error(r));
|
|
|
|
/* give up on PCSC readers */
|
|
goto pcsc_done;
|
|
|
|
}
|
|
|
|
/*
|
|
* SCARD_AUTOALLOCATE not portable. Do this in two steps
|
|
* and live with the race condition. first get # readers
|
|
*/
|
|
if ((r = SCardListReaders(scrctx->hContext, (void*)0L, (void*)0L,
|
|
&pcsc_rdr_buf_len)) != SCARD_S_SUCCESS) {
|
|
|
|
if (scrctx->verbose)
|
|
xerr_warnx("SCCardListReaders(): %s.", pcsc_stringify_error(r));
|
|
|
|
/* give up on PCSC readers */
|
|
goto pcsc_done;
|
|
|
|
}
|
|
|
|
if (!(pcsc_rdr_buf = malloc(pcsc_rdr_buf_len))) {
|
|
if (scrctx->verbose)
|
|
xerr_warnx("malloc(pcsc_rdr_buf): failed.");
|
|
goto scr_ctx_new_out;
|
|
}
|
|
|
|
if ((r = SCardListReaders(scrctx->hContext, (void*)0L, pcsc_rdr_buf,
|
|
&pcsc_rdr_buf_len)) != SCARD_S_SUCCESS) {
|
|
if (scrctx->verbose)
|
|
xerr_warnx("SCCardListReaders(): %s", pcsc_stringify_error(ret));
|
|
goto scr_ctx_new_out;
|
|
}
|
|
|
|
/* run through PSCS reader names to get count */
|
|
for (p = pcsc_rdr_buf;*p;++scrctx->pcsc_num_readers)
|
|
p += strlen(p);
|
|
|
|
/* first PCSC reader in the list */
|
|
if (scrctx->pcsc_num_readers)
|
|
scrctx->pcsc_reader_first = scrctx->num_readers;
|
|
|
|
/* add PCSC readers to total available via scr */
|
|
scrctx->num_readers += scrctx->pcsc_num_readers;
|
|
|
|
/* resrve space for reader name + "PCSC:" */
|
|
ralloc += pcsc_rdr_buf_len + (scrctx->pcsc_num_readers * 5);
|
|
|
|
} /* SCR_READER_PCSC */
|
|
|
|
#endif /* SCR_PCSC */
|
|
|
|
pcsc_done:
|
|
|
|
if (valid_readers & SCR_READER_EMBEDDED_ACR30S) {
|
|
|
|
++ scrctx->num_readers;
|
|
|
|
ralloc += strlen(SCR_EMBEDDED_ACR30S_NAME)+1;
|
|
|
|
}
|
|
|
|
/* foreach reader allocate char */
|
|
ralloc += (scrctx->num_readers) * sizeof (char*);
|
|
|
|
if (!(scrctx->readers = (char**)malloc(ralloc))) {
|
|
if (scrctx->verbose)
|
|
xerr_warn("malloc(scrctx->readers)");
|
|
goto scr_ctx_new_out;
|
|
}
|
|
|
|
/* start storing strings after pointers */
|
|
buf = (char*)scrctx->readers + (sizeof (char*))*scrctx->num_readers;
|
|
cur_reader = 0;
|
|
|
|
#ifdef SCR_PCSC
|
|
|
|
if (valid_readers & SCR_READER_PCSC) {
|
|
|
|
p = pcsc_rdr_buf;
|
|
while (p && *p) {
|
|
scrctx->readers[cur_reader++] = buf;
|
|
bcopy("PCSC:", buf, 5);
|
|
buf += 5;
|
|
strcpy(buf, p);
|
|
buf += strlen(p)+1;
|
|
p += strlen(p)+1;
|
|
}
|
|
|
|
} /* SCR_READER_PCSC */
|
|
|
|
#endif /* SCR_PCSC */
|
|
|
|
if (valid_readers & SCR_READER_EMBEDDED_ACR30S) {
|
|
scrctx->readers[cur_reader++] = buf;
|
|
strcpy(buf, SCR_EMBEDDED_ACR30S_NAME);
|
|
buf += strlen(SCR_EMBEDDED_ACR30S_NAME) + 1;
|
|
} /* SCR_READER_PCSC */
|
|
|
|
|
|
scrctx->valid = 1;
|
|
scrctx->valid_readers = valid_readers;
|
|
|
|
ret = 0; /* success */
|
|
|
|
scr_ctx_new_out:
|
|
|
|
/* dump list of readers? */
|
|
if (scrctx->verbose) {
|
|
for (i = 0; i < scrctx->num_readers; ++i)
|
|
xerr_info("reader: %s", scrctx->readers[i]);
|
|
}
|
|
|
|
#ifdef SCR_PCSC
|
|
if (pcsc_rdr_buf)
|
|
free(pcsc_rdr_buf);
|
|
#endif /* SCR_PSCS */
|
|
|
|
if (ret == -1) {
|
|
|
|
if (scrctx && scrctx->readers)
|
|
free(scrctx->readers);
|
|
|
|
if (scrctx)
|
|
free(scrctx);
|
|
|
|
scrctx = (struct scr_ctx*)0L;
|
|
|
|
}
|
|
|
|
return scrctx;
|
|
|
|
} /* scr_ctx_new */
|
|
|
|
/*
|
|
* function: scr_ctx_new()
|
|
*
|
|
* Test context is valid
|
|
*
|
|
* arguments:
|
|
*
|
|
* scrctx - scr context context allocated by scr_ctx_new()
|
|
* who - id representing caller.
|
|
*
|
|
* returns: 0 success, context is valid
|
|
* <0 failure
|
|
*
|
|
*/
|
|
int scr_ctx_valid(struct scr_ctx *scrctx, char *who)
|
|
{
|
|
|
|
if (!scrctx) {
|
|
xerr_warnx("%s(): fatal, no context.", who);
|
|
return -1;
|
|
}
|
|
|
|
if (!scrctx->valid) {
|
|
if (scrctx->verbose)
|
|
xerr_warnx("%s(): fatal, invalid context.", who);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
} /* scr_ctx_valid */
|
|
|
|
/*
|
|
* function: scr_ctx_free()
|
|
*
|
|
* Free context, deallocate resources, disconnect from reader, etc.
|
|
*
|
|
* arguments:
|
|
*
|
|
* scrctx - scr context context allocated by scr_ctx_new()
|
|
*
|
|
* returns: 0 success
|
|
* <0 failure
|
|
*
|
|
*/
|
|
void scr_ctx_free(struct scr_ctx *scrctx)
|
|
{
|
|
int r;
|
|
scr_ctx_valid(scrctx, (char*)__FUNCTION__);
|
|
|
|
if (scrctx->active_reader == SCR_READER_EMBEDDED_ACR30S)
|
|
acr30_close(scrctx->acr30ctx);
|
|
|
|
if (scrctx && scrctx->readers)
|
|
free(scrctx->readers);
|
|
|
|
#ifdef SCR_PCSC
|
|
|
|
if (scrctx->active_reader == SCR_READER_PCSC) {
|
|
if ((r = SCardDisconnect(scrctx->hCard, SCARD_LEAVE_CARD))
|
|
!= SCARD_S_SUCCESS)
|
|
xerr_warnx("SCardDisconnect(): %s.", pcsc_stringify_error(r));
|
|
}
|
|
|
|
if (scrctx->hContext) {
|
|
if ((r = SCardReleaseContext(scrctx->hContext)) != SCARD_S_SUCCESS)
|
|
xerr_warnx("SCardReleaseContext(): %s.", pcsc_stringify_error(r));
|
|
}
|
|
|
|
#endif /* SCR_PCSC */
|
|
|
|
if (scrctx->reader)
|
|
free(scrctx->reader);
|
|
|
|
if (scrctx)
|
|
free(scrctx);
|
|
|
|
|
|
} /* scr_ctx_free() */
|
|
|
|
/*
|
|
* function: scr_ctx_connect()
|
|
*
|
|
* Connect to reader.
|
|
*
|
|
* arguments:
|
|
*
|
|
* scrctx - scr context context allocated by scr_ctx_new()
|
|
* reader - reader string.
|
|
*
|
|
* reader string is of the form
|
|
* PCSC:<pcsc reader name>
|
|
* embedded:acr30s:<serial_device>
|
|
*
|
|
* The reader name and serial device are optional. PCSC will default
|
|
* to the first reader, embedded:acr30s will default to
|
|
* SCR_EMBEDDED_ACR30S_DEVICE
|
|
*
|
|
* An empty reader string will default to the first available reader
|
|
*
|
|
* returns: 0 success, connected to reader
|
|
* <0 failure
|
|
*
|
|
*/
|
|
int scr_ctx_connect(struct scr_ctx *scrctx, char *reader)
|
|
{
|
|
int r, ret;
|
|
char *serialio, n;
|
|
|
|
ret = -1; /* fail */
|
|
|
|
if (scr_ctx_valid(scrctx, (char*)__FUNCTION__) == -1)
|
|
goto scr_ctx_connect_out;
|
|
|
|
|
|
/* empty or no reader string */
|
|
if ((!reader) || (reader[0] == 0)) {
|
|
|
|
if (scrctx->num_readers == 0) {
|
|
xerr_warnx("No readers.");
|
|
goto scr_ctx_connect_out;
|
|
}
|
|
|
|
reader = scrctx->readers[0];
|
|
|
|
}
|
|
|
|
n = strlen(reader);
|
|
|
|
if (!(scrctx->reader = (char*)malloc(n+1))) {
|
|
if (scrctx->verbose)
|
|
xerr_warn("malloc(reader)");
|
|
goto scr_ctx_connect_out;
|
|
}
|
|
|
|
strcpy(scrctx->reader, reader);
|
|
|
|
if (scrctx->valid_readers & SCR_READER_EMBEDDED_ACR30S) {
|
|
|
|
n = strlen(SCR_EMBEDDED_ACR30S_NAME);
|
|
|
|
if (!strncmp(reader, SCR_EMBEDDED_ACR30S_NAME, n)) {
|
|
|
|
serialio = reader + n;
|
|
|
|
/* parse out device name, or use default */
|
|
if ((!*serialio) || ((*serialio == ':') && (!*(serialio+1))))
|
|
serialio = SCR_EMBEDDED_ACR30S_DEVICE;
|
|
else
|
|
serialio += 1;
|
|
|
|
if (!(scrctx->acr30ctx = acr30_open(serialio, scrctx->verbose))) {
|
|
xerr_warnx("acr30_open(%s): failed", serialio);
|
|
goto scr_ctx_connect_out;
|
|
}
|
|
|
|
scrctx->active_reader = SCR_READER_EMBEDDED_ACR30S;
|
|
|
|
}
|
|
|
|
} /* SCR_READER_EMBEDDED_ACR30 */
|
|
|
|
#ifdef SCR_PCSC
|
|
|
|
if (scrctx->valid_readers & SCR_READER_PCSC) {
|
|
|
|
if (!strncmp(scrctx->reader, "PCSC:", 5)) {
|
|
|
|
/* skip PCSC: */
|
|
scrctx->pcsc_active_reader = scrctx->reader + 5;
|
|
|
|
/* PCSC: alone defaults to first PCSC reader if defined */
|
|
if (!*scrctx->pcsc_active_reader) {
|
|
|
|
/* if readers available, then default to first */
|
|
if (scrctx->pcsc_num_readers) {
|
|
scrctx->pcsc_active_reader =\
|
|
scrctx->readers[scrctx->pcsc_reader_first]+5;
|
|
} else {
|
|
xerr_warnx("No PCSC readers.");
|
|
goto scr_ctx_connect_out;
|
|
}
|
|
|
|
} /* PSCS: */
|
|
|
|
if ((r = SCardConnect(scrctx->hContext, scrctx->pcsc_active_reader,
|
|
SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T1, &scrctx->hCard,
|
|
&scrctx->dwActiveProtocol)) != SCARD_S_SUCCESS) {
|
|
if (scrctx->verbose)
|
|
xerr_warnx("ScardConnect(): %s.", pcsc_stringify_error(r));
|
|
goto scr_ctx_connect_out;
|
|
}
|
|
|
|
if (scrctx->dwActiveProtocol != SCARD_PROTOCOL_T1) {
|
|
if (scrctx->verbose)
|
|
xerr_warnx("dwActiveProtocol=0x%2.2x SCARD_PROTOCOL_T1=0x%2.2x",
|
|
(int)scrctx->dwActiveProtocol, SCARD_PROTOCOL_T1);
|
|
goto scr_ctx_connect_out;
|
|
}
|
|
|
|
scrctx->active_reader = SCR_READER_PCSC;
|
|
|
|
}
|
|
|
|
} /* SCR_READER_PCSC */
|
|
|
|
#endif /* SCR_PCSC */
|
|
|
|
if (!scrctx->active_reader) {
|
|
xerr_warnx("No active reader.");
|
|
goto scr_ctx_connect_out;
|
|
}
|
|
|
|
ret = 0; /* success */
|
|
|
|
scr_ctx_connect_out:
|
|
|
|
return ret;
|
|
|
|
} /* scr_ctx_connect */
|
|
|
|
/*
|
|
* function: scr_ctx_reset()
|
|
*
|
|
* Perform reset function on SC. For PCSC this is no more than a
|
|
* disconnect followed by connect. The embedded ACR30S driver
|
|
* will issue a reset command to the SC and wait for a success reply.
|
|
*
|
|
* arguments:
|
|
*
|
|
* scrctx - scr context context allocated by scr_ctx_new()
|
|
*
|
|
* returns: 0 success, SC reset.
|
|
* <0 failure
|
|
*
|
|
*/
|
|
int scr_ctx_reset(struct scr_ctx *scrctx)
|
|
{
|
|
int ret, r;
|
|
|
|
ret = -1; /* fail */
|
|
|
|
if (scrctx->active_reader == SCR_READER_EMBEDDED_ACR30S) {
|
|
|
|
if (acr30_reset(scrctx->acr30ctx) < 0) {
|
|
xerr_warnx("acr30_reset(): failed.");
|
|
goto scr_ctx_reset_out;
|
|
}
|
|
|
|
ret = 0; /* success */
|
|
|
|
}
|
|
|
|
#ifdef SCR_PCSC
|
|
|
|
if (scrctx->active_reader == SCR_READER_PCSC) {
|
|
|
|
if ((r = SCardDisconnect(scrctx->hCard, SCARD_RESET_CARD))
|
|
!= SCARD_S_SUCCESS) {
|
|
xerr_warnx("SCardDisconnect(): %s.", pcsc_stringify_error(r));
|
|
goto scr_ctx_reset_out;
|
|
}
|
|
|
|
if ((r = SCardConnect(scrctx->hContext, scrctx->pcsc_active_reader,
|
|
SCARD_SHARE_EXCLUSIVE, SCARD_PROTOCOL_T1, &scrctx->hCard,
|
|
&scrctx->dwActiveProtocol)) != SCARD_S_SUCCESS) {
|
|
if (scrctx->verbose)
|
|
xerr_warnx("ScardConnect(): %s.", pcsc_stringify_error(r));
|
|
goto scr_ctx_reset_out;
|
|
}
|
|
|
|
if (scrctx->dwActiveProtocol != SCARD_PROTOCOL_T1) {
|
|
if (scrctx->verbose)
|
|
xerr_warnx("dwActiveProtocol=0x%2.2x SCARD_PROTOCOL_T1=0x%2.2x",
|
|
(int)scrctx->dwActiveProtocol, SCARD_PROTOCOL_T1);
|
|
goto scr_ctx_reset_out;
|
|
}
|
|
|
|
ret = 0; /* success */
|
|
|
|
}
|
|
|
|
#endif /* SCR_PCSC */
|
|
|
|
scr_ctx_reset_out:
|
|
|
|
return ret;
|
|
|
|
} /* scr_ctx_reset */
|
|
|
|
/*
|
|
* function: scr_ctx_cmd()
|
|
*
|
|
* Send a command to the SC. Decode the reply.
|
|
*
|
|
* arguments:
|
|
*
|
|
* scrctx - scr context context allocated by scr_ctx_new()
|
|
* scrio - SCR I/O structure.
|
|
*
|
|
* scrio example:
|
|
*
|
|
* bzero(&scrio, sizeof scrio);
|
|
* i = 0;
|
|
*
|
|
* scrio.tx_lc = LC
|
|
* scrio.tx_le = LE
|
|
* scrio.tx_delay = DELAY_AFTER_CMD
|
|
* scrio.rx_buf_len = RECEIVE BUFFER LEN (usually LE)
|
|
*
|
|
* scrio.tx_buf[i++] = CLA
|
|
* scrio.tx_buf[i++] = INS
|
|
* scrio.tx_buf[i++] = P1
|
|
* scrio.tx_buf[i++] = P2
|
|
* scrio.tx_buf[i++] = LC
|
|
* scrio.tx_buf[i++] = <data>
|
|
* scrio.tx_buf[i++] = LE
|
|
*
|
|
* issue command:
|
|
*
|
|
* scr_ctx_cmd(scrctx, &scrio)
|
|
*
|
|
* check reply for normal SW1=90, SW2=00
|
|
*
|
|
* scr_checksw1sw2_rx(&scrio, scrio.tx_le, 0x90, 0x00, 0xFF, 0xFF, 1);
|
|
*
|
|
* copy out response (if necessary)
|
|
*
|
|
* for (j = 0; j < REPLY_DATA_LEN; ++j)
|
|
* mybuf[j] = scrio.rx_buf[j]
|
|
*
|
|
* returns: 0 success, command completed.
|
|
* <0 failure
|
|
*
|
|
*/
|
|
int scr_ctx_cmd(struct scr_ctx *scrctx, struct scr_io *scrio)
|
|
{
|
|
int ret;
|
|
#ifdef SCR_PCSC
|
|
DWORD dwSendLength;
|
|
DWORD dwRecvLength;
|
|
#endif /* SCR_PCSC */
|
|
|
|
ret = -1; /* fail */
|
|
|
|
if (scrctx->active_reader == SCR_READER_EMBEDDED_ACR30S) {
|
|
|
|
bzero(&scrctx->acr30ctx->tx, sizeof (scrctx->acr30ctx->tx));
|
|
|
|
scrctx->acr30ctx->tx.header = ACR30_HEADER_START;
|
|
scrctx->acr30ctx->tx.instruction = ACR30_CMD_MCU;
|
|
bcopy(&scrio->tx_buf, &scrctx->acr30ctx->tx.data, scrio->tx_buf_len);
|
|
scrctx->acr30ctx->tx.data_len = scrio->tx_buf_len;
|
|
|
|
if (acr30_transaction(scrctx->acr30ctx, scrio->tx_delay, 0x90, 0x00,
|
|
scrio->tx_le) < 0) {
|
|
if (scrctx->verbose)
|
|
xerr_warnx("acr30_transaction(): failed.");
|
|
goto scr_ctx_cmd_out;
|
|
}
|
|
|
|
scrio->rx_buf_len = scrctx->acr30ctx->rx.data_len;
|
|
bcopy(&scrctx->acr30ctx->rx.data, &scrio->rx_buf, scrio->rx_buf_len);
|
|
scrio->rx_SW1 = scrctx->acr30ctx->rx.SW1;
|
|
scrio->rx_SW2 = scrctx->acr30ctx->rx.SW2;
|
|
|
|
ret = 0; /* success */
|
|
|
|
} /* SCR_READER_EMBEDDED_ACR30S */
|
|
|
|
#ifdef SCR_PCSC
|
|
if (scrctx->active_reader == SCR_READER_PCSC) {
|
|
|
|
dwSendLength = scrio->tx_buf_len;
|
|
dwRecvLength = scrio->rx_buf_len + 2; /* data + SW1 SW2 */
|
|
|
|
if ((ret = SCardTransmit(scrctx->hCard, SCARD_PCI_T1,
|
|
(BYTE*)&scrio->tx_buf, dwSendLength, 0L, (BYTE*)&scrio->rx_buf,
|
|
&dwRecvLength)) != SCARD_S_SUCCESS) {
|
|
if (scrctx->verbose)
|
|
xerr_warnx("SCardTransmit(): %s.", pcsc_stringify_error(ret));
|
|
goto scr_ctx_cmd_out;
|
|
}
|
|
|
|
scrio->rx_buf_len = dwRecvLength;
|
|
|
|
ret = 0; /* success */
|
|
|
|
} /* SCR_READER_PCSC */
|
|
#endif /* SCR_PCSC */
|
|
|
|
ret = 0; /* success */
|
|
|
|
scr_ctx_cmd_out:
|
|
|
|
return ret;
|
|
|
|
} /* scr_ctx_cmd */
|
|
|
|
/*
|
|
* function: scr_checksw1sw2_rx()
|
|
*
|
|
* Check SW1 SW2 in scrio receive buffer.
|
|
*
|
|
* arguments:
|
|
*
|
|
* scrio - SCR I/O structure.
|
|
* dlen - expected length of data.
|
|
* SW1 - expected value SW1
|
|
* SW2 - expected value SW2
|
|
* SW1_MASK - mask bits for SW1. Use 0xFF for exact match.
|
|
* SW2_MASK - mask bits for SW2. Use 0xFF for exact match.
|
|
*
|
|
* returns: 0 success, SW1, SW2 matched expected values
|
|
* <0 failure
|
|
*
|
|
*/
|
|
int scr_checksw1sw2_rx(struct scr_io *scrio, u_char dlen, u_char SW1,
|
|
u_char SW2, u_char SW1_mask, u_char SW2_mask, int verbose)
|
|
{
|
|
u_char cmd_SW1, cmd_SW2;
|
|
|
|
if (scrio->rx_buf_len < 2) {
|
|
if (verbose)
|
|
xerr_warnx("scr_checksw1sw2_rx(): no data for SW1,SW2");
|
|
return -1; /* fail */
|
|
}
|
|
|
|
cmd_SW1 = scrio->rx_buf[scrio->rx_buf_len-2];
|
|
cmd_SW2 = scrio->rx_buf[scrio->rx_buf_len-1];
|
|
|
|
if ((cmd_SW1 == 0x69) && (cmd_SW2 == 0xC2)) {
|
|
xerr_warnx("Access Denied.");
|
|
return 2; /* fail */
|
|
}
|
|
|
|
if ((cmd_SW1 == 0x66) && (cmd_SW2 == 0xC7)) {
|
|
xerr_warnx("Card Locked. Access Denied.");
|
|
return 2; /* fail */
|
|
}
|
|
|
|
if (((cmd_SW1 & SW1_mask) != SW1) || ((cmd_SW2 & SW2_mask) != SW2)) {
|
|
if (verbose)
|
|
xerr_warnx(
|
|
"response: SW1=%2.2X,SW2=%2.2X expecting: SW1=%2.2X/%2.2X, SW2=%2.2X/%2.2X",
|
|
(int)cmd_SW1, (int)cmd_SW2, (int)SW1, (int)SW1_mask, (int)SW2,
|
|
(int)SW2_mask);
|
|
return 1; /* fail */
|
|
}
|
|
|
|
/* expected recponse length is SW1+SW2+data */
|
|
if (scrio->rx_buf_len != dlen+2) {
|
|
if (verbose)
|
|
xerr_warnx("Expecting %d bytes in response, got %d.", (int)dlen+2,
|
|
scrio->rx_buf_len);
|
|
return -1; /* fail */
|
|
}
|
|
|
|
return 0; /* succes */
|
|
|
|
} /* scr_checksw1sw2_rx */
|
|
|
|
#ifdef SCR_EXAMPLE
|
|
|
|
|
|
#include <stdio.h>
|
|
#include "scr.h"
|
|
#include "otpsc.h"
|
|
#include "xerr.h"
|
|
|
|
main()
|
|
{
|
|
struct scr_ctx *scrctx;
|
|
char sc_hostname[SC_HOSTNAME_LEN+1];
|
|
u_char sc_hotp[SC_HOTP_LEN+1], sc_idx[SC_INDEX_LEN+1];
|
|
u_char sc_version[SC_VERSION_LEN+1], sc_adminkey[SC_ADMINKEY_LEN+1];
|
|
u_char sc_count[SC_COUNT_LEN+1], sc_count32[SC_COUNT32_LEN+1];
|
|
u_char sc_hotpkey[SC_HOTPKEY_LEN+1], sc_adminmode[SC_ADMINMODE_LEN+1];
|
|
u_char sc_firmware, sc_count_len, tmp8u;
|
|
char fmt_buf[1024];
|
|
int i, j, k;
|
|
|
|
xerr_setid("test");
|
|
|
|
if (!(scrctx = scr_ctx_new(SCR_READER_EMBEDDED_ACR30S|SCR_READER_PCSC, 1))) {
|
|
xerr_errx(1, "scr_ctx_new(): failed");
|
|
}
|
|
|
|
char *c = (char*)__FUNCTION__;
|
|
scr_ctx_valid(scrctx, c);
|
|
|
|
for (i = 0; i < scrctx->num_readers; ++i)
|
|
printf("%d:%s\n", i, scrctx->readers[i]);
|
|
|
|
if (scr_ctx_connect(scrctx, "embedded:acr30s") < 0) {
|
|
xerr_errx(1, "scr_ctx_connect(): failed");
|
|
}
|
|
|
|
/*
|
|
if (scr_ctx_connect(scrctx, "PCSC:OmniKey CardMan 1021 00 00") < 0) {
|
|
xerr_errx(1, "scr_ctx_connect(): failed");
|
|
}
|
|
*/
|
|
|
|
/*
|
|
if (scr_ctx_connect(scrctx, "PCSC:") < 0) {
|
|
xerr_errx(1, "scr_ctx_connect(): failed");
|
|
}
|
|
*/
|
|
|
|
if (sccmd_GetVersion(scrctx, &sc_version) < 0) {
|
|
xerr_errx(1, "sccmd_GetVersion(): failed");
|
|
}
|
|
|
|
str_hex_dump(fmt_buf, sc_version, 1);
|
|
printf("Version: 0x%s\n", fmt_buf);
|
|
|
|
sc_count_len = 4;
|
|
|
|
for (j = 0; j < 7; ++j) {
|
|
|
|
sc_idx[0] = j;
|
|
|
|
if (sccmd_GetHost32(scrctx, &sc_idx, sc_count32, sc_hostname,
|
|
sc_hotpkey) < 0) {
|
|
xerr_errx(1, "sccmd_GetVersion(): failed");
|
|
}
|
|
|
|
str_hex_dump(fmt_buf, sc_idx, SC_INDEX_LEN);
|
|
i = (SC_INDEX_LEN<<1); fmt_buf[i] = ':'; i += 1;
|
|
|
|
/* count */
|
|
/* pad to 32 bits */
|
|
for (k = 0; k < (4-sc_count_len)<<1; ++k)
|
|
fmt_buf[i++] = '0';
|
|
str_hex_dump(fmt_buf+i, sc_count32, sc_count_len);
|
|
i += (sc_count_len<<1); fmt_buf[i] = ':'; i += 1;
|
|
|
|
/* hostname */
|
|
str_hex_dump(fmt_buf+i, (u_char*)sc_hostname, SC_HOSTNAME_LEN);
|
|
i += (SC_HOSTNAME_LEN<<1); fmt_buf[i] = ':'; i += 1;
|
|
|
|
/* key */
|
|
str_hex_dump(fmt_buf+i, sc_hotpkey, SC_HOTPKEY_LEN);
|
|
|
|
printf("%s\n", fmt_buf);
|
|
|
|
} /* for */
|
|
|
|
scr_ctx_free(scrctx);
|
|
|
|
} /* main */
|
|
|
|
#endif /* SCR_EXAMPLE */
|