2003-09-02 19:06:34 +00:00
|
|
|
|
/* ccid-driver.c - USB ChipCardInterfaceDevices driver
|
2008-12-18 16:34:28 +00:00
|
|
|
|
* Copyright (C) 2003, 2004, 2005, 2006, 2007
|
2013-01-10 10:49:27 +09:00
|
|
|
|
* 2008, 2009, 2013 Free Software Foundation, Inc.
|
2007-03-07 20:55:14 +00:00
|
|
|
|
* Written by Werner Koch.
|
2003-09-02 19:06:34 +00:00
|
|
|
|
*
|
|
|
|
|
* This file is part of GnuPG.
|
|
|
|
|
*
|
|
|
|
|
* GnuPG is free software; you can redistribute it and/or modify
|
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
2007-07-04 19:49:40 +00:00
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
2003-09-02 19:06:34 +00:00
|
|
|
|
* (at your option) any later version.
|
|
|
|
|
*
|
|
|
|
|
* GnuPG 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
|
2016-11-05 12:02:19 +01:00
|
|
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
2003-09-05 07:40:41 +00:00
|
|
|
|
*
|
|
|
|
|
* ALTERNATIVELY, this file may be distributed under the terms of the
|
|
|
|
|
* following license, in which case the provisions of this license are
|
|
|
|
|
* required INSTEAD OF the GNU General Public License. If you wish to
|
|
|
|
|
* allow use of your version of this file only under the terms of the
|
|
|
|
|
* GNU General Public License, and not to allow others to use your
|
|
|
|
|
* version of this file under the terms of the following license,
|
|
|
|
|
* indicate your decision by deleting this paragraph and the license
|
|
|
|
|
* below.
|
|
|
|
|
*
|
|
|
|
|
* 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, and the entire permission notice in its entirety,
|
|
|
|
|
* including the disclaimer of warranties.
|
|
|
|
|
* 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.
|
|
|
|
|
* 3. The name of the author may not be used to endorse or promote
|
|
|
|
|
* products derived from this software without specific prior
|
|
|
|
|
* written permission.
|
|
|
|
|
*
|
2012-06-05 19:29:22 +02:00
|
|
|
|
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
|
2003-09-05 07:40:41 +00:00
|
|
|
|
* 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 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.
|
2003-09-02 19:06:34 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* CCID (ChipCardInterfaceDevices) is a specification for accessing
|
2011-02-04 12:57:53 +01:00
|
|
|
|
smartcard via a reader connected to the USB.
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
|
|
|
|
This is a limited driver allowing to use some CCID drivers directly
|
|
|
|
|
without any other specila drivers. This is a fallback driver to be
|
|
|
|
|
used when nothing else works or the system should be kept minimal
|
|
|
|
|
for security reasons. It makes use of the libusb library to gain
|
|
|
|
|
portable access to USB.
|
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
This driver has been tested with the SCM SCR335 and SPR532
|
2006-02-06 16:13:20 +00:00
|
|
|
|
smartcard readers and requires that a reader implements APDU or
|
|
|
|
|
TPDU level exchange and does fully automatic initialization.
|
2003-09-02 19:06:34 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
# include <config.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#if defined(HAVE_LIBUSB) || defined(TEST)
|
2003-09-05 07:40:41 +00:00
|
|
|
|
|
2003-09-02 19:06:34 +00:00
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <assert.h>
|
2006-02-06 16:13:20 +00:00
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <fcntl.h>
|
2009-07-16 15:54:59 +00:00
|
|
|
|
#include <time.h>
|
2016-01-27 12:24:05 +09:00
|
|
|
|
#include <unistd.h>
|
Port to npth.
* configure.ac: Don't check for PTH but for NPTH.
(AH_BOTTOM): Remove PTH_SYSCALL_SOFT.
(have_pth): Rename to ...
(have_npth): ... this.
(USE_GNU_NPTH): Rename to ...
(USE_GNU_PTH): ... this.
* m4/npth.m4: New file.
* agent/Makefile.am, agent/cache.c, agent/call-pinentry.c,
agent/call-scd.c, agent/findkey.c, agent/gpg-agent.c,
agent/trustlist.c, common/Makefile.am, common/estream.c,
common/exechelp-posix.c, common/exechelp-w32.c,
common/exechelp-w32ce.c, common/http.c, common/init.c,
common/sysutils.c, dirmngr/Makefile.am, dirmngr/crlfetch.c,
dirmngr/dirmngr.c, dirmngr/dirmngr_ldap.c, dirmngr/ldap-wrapper-ce.c,
dirmngr/ldap-wrapper.c, dirmngr/ldap.c, g13/Makefile.am,
g13/call-gpg.c, g13/g13.c, g13/runner.c, scd/Makefile.am,
scd/apdu.c, scd/app.c, scd/ccid-driver.c, scd/command.c,
scd/scdaemon.c, tools/Makefile.am: Port to npth.
2012-01-03 22:12:37 +01:00
|
|
|
|
#ifdef HAVE_NPTH
|
|
|
|
|
# include <npth.h>
|
|
|
|
|
#endif /*HAVE_NPTH*/
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
2016-01-27 12:24:05 +09:00
|
|
|
|
#include <libusb.h>
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
SCD: API cleanup for keypad handling.
* scd/iso7816.h (struct pininfo_s): Rename from iso7816_pininfo_s.
Change meaning of MODE.
(pininfo_t): Rename from iso7816_pininfo_t.
* scd/sc-copykeys.c: Include "iso7816.h".
* scd/scdaemon.c, scd/command.c: Likewise.
* scd/ccid-driver.c: Include "scdaemon.h" and "iso7816.h".
(ccid_transceive_secure): Follow the change of PININFO_T.
* scd/app.c: Include "apdu.h" after "iso7816.h".
* scd/iso7816.c (iso7816_check_keypad, iso7816_verify_kp)
(iso7816_change_reference_data_kp): Follow the change of API.
* scd/apdu.c (struct reader_table_s): Change API of CHECK_KEYPAD,
KEYPAD_VERIFY, KEYPAD_MODIFY to have arg of PININFO_T.
(check_pcsc_keypad, check_ccid_keypad): Likewise.
(apdu_check_keypad, apdu_keypad_verify, apdu_keypad_modify): Likewise.
(pcsc_keypad_verify, pcsc_keypad_modify, ct_send_apdu)
(pcsc_send_apdu_direct, pcsc_send_apdu_wrapped, pcsc_send_apdu)
(send_apdu_ccid, ccid_keypad_operation, my_rapdu_send_apdu, send_apdu)
(send_le): Follow the change of API.
* scd/apdu.h (apdu_check_keypad, apdu_keypad_verify)
(apdu_keypad_modify): Change the API.
* scd/app-dinsig.c, scd/app-nks.c, scd/app-openpgp.c: Follow the
change.
2013-01-09 14:10:08 +09:00
|
|
|
|
#include "scdaemon.h"
|
|
|
|
|
#include "iso7816.h"
|
2013-08-30 09:28:17 +02:00
|
|
|
|
#define CCID_DRIVER_INCLUDE_USB_IDS 1
|
2003-09-02 19:06:34 +00:00
|
|
|
|
#include "ccid-driver.h"
|
|
|
|
|
|
2003-09-05 07:40:41 +00:00
|
|
|
|
#define DRVNAME "ccid-driver: "
|
|
|
|
|
|
2015-04-14 14:17:03 +09:00
|
|
|
|
/* Max length of buffer with out CCID message header of 10-byte
|
|
|
|
|
Sending: 547 for RSA-4096 key import
|
|
|
|
|
APDU size = 540 (24+4+256+256)
|
|
|
|
|
commnd + lc + le = 4 + 3 + 0
|
|
|
|
|
Sending: write data object of cardholder certificate
|
|
|
|
|
APDU size = 2048
|
|
|
|
|
commnd + lc + le = 4 + 3 + 0
|
|
|
|
|
Receiving: 2048 for cardholder certificate
|
|
|
|
|
*/
|
|
|
|
|
#define CCID_MAX_BUF (2048+7+10)
|
2003-09-05 07:40:41 +00:00
|
|
|
|
|
2017-03-27 11:25:00 +09:00
|
|
|
|
/* CCID command timeout. */
|
|
|
|
|
#define CCID_CMD_TIMEOUT (5*1000)
|
|
|
|
|
/* OpenPGPcard v2.1 requires huge timeout for key generation. */
|
|
|
|
|
#define CCID_CMD_TIMEOUT_LONGER (60*1000)
|
2015-09-17 11:21:44 +09:00
|
|
|
|
|
2004-03-16 10:49:37 +00:00
|
|
|
|
/* Depending on how this source is used we either define our error
|
2015-04-24 16:10:15 +02:00
|
|
|
|
output to go to stderr or to the GnuPG based logging functions. We
|
|
|
|
|
use the latter when GNUPG_MAJOR_VERSION or GNUPG_SCD_MAIN_HEADER
|
|
|
|
|
are defined. */
|
|
|
|
|
#if defined(GNUPG_MAJOR_VERSION) || defined(GNUPG_SCD_MAIN_HEADER)
|
2003-10-02 10:27:34 +00:00
|
|
|
|
|
2004-03-16 10:49:37 +00:00
|
|
|
|
#if defined(GNUPG_SCD_MAIN_HEADER)
|
|
|
|
|
# include GNUPG_SCD_MAIN_HEADER
|
|
|
|
|
#elif GNUPG_MAJOR_VERSION == 1 /* GnuPG Version is < 1.9. */
|
2003-10-02 10:27:34 +00:00
|
|
|
|
# include "options.h"
|
|
|
|
|
# include "util.h"
|
|
|
|
|
# include "memory.h"
|
|
|
|
|
# include "cardglue.h"
|
|
|
|
|
# else /* This is the modularized GnuPG 1.9 or later. */
|
|
|
|
|
# include "scdaemon.h"
|
2004-03-16 10:49:37 +00:00
|
|
|
|
#endif
|
2003-09-05 07:40:41 +00:00
|
|
|
|
|
2004-01-27 16:40:42 +00:00
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
# define DEBUGOUT(t) do { if (debug_level) \
|
2003-09-05 07:40:41 +00:00
|
|
|
|
log_debug (DRVNAME t); } while (0)
|
2004-09-30 14:34:34 +00:00
|
|
|
|
# define DEBUGOUT_1(t,a) do { if (debug_level) \
|
2003-09-05 07:40:41 +00:00
|
|
|
|
log_debug (DRVNAME t,(a)); } while (0)
|
2004-09-30 14:34:34 +00:00
|
|
|
|
# define DEBUGOUT_2(t,a,b) do { if (debug_level) \
|
2003-09-05 07:40:41 +00:00
|
|
|
|
log_debug (DRVNAME t,(a),(b)); } while (0)
|
2004-09-30 14:34:34 +00:00
|
|
|
|
# define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \
|
2003-09-05 07:40:41 +00:00
|
|
|
|
log_debug (DRVNAME t,(a),(b),(c));} while (0)
|
2005-05-20 20:39:36 +00:00
|
|
|
|
# define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \
|
|
|
|
|
log_debug (DRVNAME t,(a),(b),(c),(d));} while (0)
|
2004-09-30 14:34:34 +00:00
|
|
|
|
# define DEBUGOUT_CONT(t) do { if (debug_level) \
|
2003-09-19 11:17:11 +00:00
|
|
|
|
log_printf (t); } while (0)
|
2004-09-30 14:34:34 +00:00
|
|
|
|
# define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \
|
2003-09-05 07:40:41 +00:00
|
|
|
|
log_printf (t,(a)); } while (0)
|
2004-09-30 14:34:34 +00:00
|
|
|
|
# define DEBUGOUT_CONT_2(t,a,b) do { if (debug_level) \
|
2003-09-19 11:17:11 +00:00
|
|
|
|
log_printf (t,(a),(b)); } while (0)
|
2004-09-30 14:34:34 +00:00
|
|
|
|
# define DEBUGOUT_CONT_3(t,a,b,c) do { if (debug_level) \
|
2003-09-05 07:40:41 +00:00
|
|
|
|
log_printf (t,(a),(b),(c)); } while (0)
|
2004-09-30 14:34:34 +00:00
|
|
|
|
# define DEBUGOUT_LF() do { if (debug_level) \
|
2003-09-05 07:40:41 +00:00
|
|
|
|
log_printf ("\n"); } while (0)
|
|
|
|
|
|
|
|
|
|
#else /* Other usage of this source - don't use gnupg specifics. */
|
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
# define DEBUGOUT(t) do { if (debug_level) \
|
|
|
|
|
fprintf (stderr, DRVNAME t); } while (0)
|
|
|
|
|
# define DEBUGOUT_1(t,a) do { if (debug_level) \
|
|
|
|
|
fprintf (stderr, DRVNAME t, (a)); } while (0)
|
|
|
|
|
# define DEBUGOUT_2(t,a,b) do { if (debug_level) \
|
|
|
|
|
fprintf (stderr, DRVNAME t, (a), (b)); } while (0)
|
|
|
|
|
# define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \
|
|
|
|
|
fprintf (stderr, DRVNAME t, (a), (b), (c)); } while (0)
|
2005-05-20 20:39:36 +00:00
|
|
|
|
# define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \
|
|
|
|
|
fprintf (stderr, DRVNAME t, (a), (b), (c), (d));} while(0)
|
2004-09-30 14:34:34 +00:00
|
|
|
|
# define DEBUGOUT_CONT(t) do { if (debug_level) \
|
|
|
|
|
fprintf (stderr, t); } while (0)
|
|
|
|
|
# define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \
|
|
|
|
|
fprintf (stderr, t, (a)); } while (0)
|
|
|
|
|
# define DEBUGOUT_CONT_2(t,a,b) do { if (debug_level) \
|
|
|
|
|
fprintf (stderr, t, (a), (b)); } while (0)
|
|
|
|
|
# define DEBUGOUT_CONT_3(t,a,b,c) do { if (debug_level) \
|
|
|
|
|
fprintf (stderr, t, (a), (b), (c)); } while (0)
|
|
|
|
|
# define DEBUGOUT_LF() do { if (debug_level) \
|
|
|
|
|
putc ('\n', stderr); } while (0)
|
2003-09-05 07:40:41 +00:00
|
|
|
|
|
|
|
|
|
#endif /* This source not used by scdaemon. */
|
|
|
|
|
|
|
|
|
|
|
2009-05-08 15:07:45 +00:00
|
|
|
|
#ifndef EAGAIN
|
|
|
|
|
#define EAGAIN EWOULDBLOCK
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
2003-10-09 15:18:08 +00:00
|
|
|
|
|
2003-09-02 19:06:34 +00:00
|
|
|
|
enum {
|
|
|
|
|
RDR_to_PC_NotifySlotChange= 0x50,
|
|
|
|
|
RDR_to_PC_HardwareError = 0x51,
|
|
|
|
|
|
|
|
|
|
PC_to_RDR_SetParameters = 0x61,
|
|
|
|
|
PC_to_RDR_IccPowerOn = 0x62,
|
|
|
|
|
PC_to_RDR_IccPowerOff = 0x63,
|
|
|
|
|
PC_to_RDR_GetSlotStatus = 0x65,
|
|
|
|
|
PC_to_RDR_Secure = 0x69,
|
|
|
|
|
PC_to_RDR_T0APDU = 0x6a,
|
|
|
|
|
PC_to_RDR_Escape = 0x6b,
|
|
|
|
|
PC_to_RDR_GetParameters = 0x6c,
|
|
|
|
|
PC_to_RDR_ResetParameters = 0x6d,
|
|
|
|
|
PC_to_RDR_IccClock = 0x6e,
|
|
|
|
|
PC_to_RDR_XfrBlock = 0x6f,
|
|
|
|
|
PC_to_RDR_Mechanical = 0x71,
|
|
|
|
|
PC_to_RDR_Abort = 0x72,
|
|
|
|
|
PC_to_RDR_SetDataRate = 0x73,
|
|
|
|
|
|
|
|
|
|
RDR_to_PC_DataBlock = 0x80,
|
|
|
|
|
RDR_to_PC_SlotStatus = 0x81,
|
|
|
|
|
RDR_to_PC_Parameters = 0x82,
|
|
|
|
|
RDR_to_PC_Escape = 0x83,
|
|
|
|
|
RDR_to_PC_DataRate = 0x84
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2005-05-23 14:17:22 +00:00
|
|
|
|
/* Two macro to detect whether a CCID command has failed and to get
|
|
|
|
|
the error code. These macros assume that we can access the
|
|
|
|
|
mandatory first 10 bytes of a CCID message in BUF. */
|
|
|
|
|
#define CCID_COMMAND_FAILED(buf) ((buf)[7] & 0x40)
|
|
|
|
|
#define CCID_ERROR_CODE(buf) (((unsigned char *)(buf))[8])
|
|
|
|
|
|
|
|
|
|
|
2003-09-02 19:06:34 +00:00
|
|
|
|
/* Store information on the driver's state. A pointer to such a
|
|
|
|
|
structure is used as handle for most functions. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
struct ccid_driver_s
|
2004-09-30 14:34:34 +00:00
|
|
|
|
{
|
2016-01-27 12:24:05 +09:00
|
|
|
|
libusb_device_handle *idev;
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
unsigned int bai;
|
2004-10-06 13:13:51 +00:00
|
|
|
|
unsigned short id_vendor;
|
|
|
|
|
unsigned short id_product;
|
2005-01-13 18:00:46 +00:00
|
|
|
|
int ifc_no;
|
2004-12-28 07:13:24 +00:00
|
|
|
|
int ep_bulk_out;
|
|
|
|
|
int ep_bulk_in;
|
|
|
|
|
int ep_intr;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
int seqno;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
unsigned char t1_ns;
|
|
|
|
|
unsigned char t1_nr;
|
2009-07-16 15:54:59 +00:00
|
|
|
|
unsigned char nonnull_nad;
|
2004-04-20 14:17:10 +00:00
|
|
|
|
int max_ifsd;
|
2015-04-14 14:17:03 +09:00
|
|
|
|
int max_ccid_msglen;
|
2009-03-30 12:46:06 +00:00
|
|
|
|
int ifsc;
|
2009-07-16 15:54:59 +00:00
|
|
|
|
unsigned char apdu_level:2; /* Reader supports short APDU level
|
|
|
|
|
exchange. With a value of 2 short
|
|
|
|
|
and extended level is supported.*/
|
2013-01-08 15:22:31 +09:00
|
|
|
|
unsigned int auto_voltage:1;
|
|
|
|
|
unsigned int auto_param:1;
|
|
|
|
|
unsigned int auto_pps:1;
|
2009-07-16 15:54:59 +00:00
|
|
|
|
unsigned int auto_ifsd:1;
|
|
|
|
|
unsigned int has_pinpad:2;
|
|
|
|
|
unsigned int enodev_seen:1;
|
2017-01-27 20:43:29 +09:00
|
|
|
|
int powered_off;
|
2009-07-16 15:54:59 +00:00
|
|
|
|
|
2009-07-13 09:59:22 +00:00
|
|
|
|
time_t last_progress; /* Last time we sent progress line. */
|
|
|
|
|
|
|
|
|
|
/* The progress callback and its first arg as supplied to
|
|
|
|
|
ccid_set_progress_cb. */
|
|
|
|
|
void (*progress_cb)(void *, const char *, int, int, int);
|
|
|
|
|
void *progress_cb_arg;
|
2017-01-26 16:54:40 +09:00
|
|
|
|
|
2018-10-11 15:41:49 +09:00
|
|
|
|
void (*prompt_cb)(void *, int);
|
|
|
|
|
void *prompt_cb_arg;
|
|
|
|
|
|
2017-01-26 16:54:40 +09:00
|
|
|
|
unsigned char intr_buf[64];
|
2017-01-27 18:01:52 +09:00
|
|
|
|
struct libusb_transfer *transfer;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
static int initialized_usb; /* Tracks whether USB has been initialized. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
static int debug_level; /* Flag to control the debug output.
|
2005-05-20 20:39:36 +00:00
|
|
|
|
0 = No debugging
|
|
|
|
|
1 = USB I/O info
|
2009-02-24 20:41:44 +00:00
|
|
|
|
2 = Level 1 + T=1 protocol tracing
|
|
|
|
|
3 = Level 2 + USB/I/O tracing of SlotStatus.
|
2005-05-20 20:39:36 +00:00
|
|
|
|
*/
|
2017-01-28 00:18:11 +09:00
|
|
|
|
static int ccid_usb_thread_is_alive;
|
2004-09-30 14:34:34 +00:00
|
|
|
|
|
|
|
|
|
|
2004-04-20 14:17:10 +00:00
|
|
|
|
static unsigned int compute_edc (const unsigned char *data, size_t datalen,
|
|
|
|
|
int use_crc);
|
2009-02-24 20:41:44 +00:00
|
|
|
|
static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen,
|
|
|
|
|
int no_debug);
|
2003-10-21 17:12:50 +00:00
|
|
|
|
static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
|
2005-05-23 14:17:22 +00:00
|
|
|
|
size_t *nread, int expected_type, int seqno, int timeout,
|
|
|
|
|
int no_debug);
|
2009-05-13 17:12:00 +00:00
|
|
|
|
static int abort_cmd (ccid_driver_t handle, int seqno);
|
2013-01-28 11:46:40 +09:00
|
|
|
|
static int send_escape_cmd (ccid_driver_t handle, const unsigned char *data,
|
|
|
|
|
size_t datalen, unsigned char *result,
|
|
|
|
|
size_t resultmax, size_t *resultlen);
|
2003-10-21 17:12:50 +00:00
|
|
|
|
|
2003-09-19 11:17:11 +00:00
|
|
|
|
/* Convert a little endian stored 4 byte value into an unsigned
|
|
|
|
|
integer. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
static unsigned int
|
2003-09-19 11:17:11 +00:00
|
|
|
|
convert_le_u32 (const unsigned char *buf)
|
|
|
|
|
{
|
2015-02-11 10:27:57 +01:00
|
|
|
|
return buf[0] | (buf[1] << 8) | (buf[2] << 16) | ((unsigned int)buf[3] << 24);
|
2003-09-19 11:17:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2009-02-24 20:41:44 +00:00
|
|
|
|
|
|
|
|
|
/* Convert a little endian stored 2 byte value into an unsigned
|
|
|
|
|
integer. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
static unsigned int
|
2009-02-24 20:41:44 +00:00
|
|
|
|
convert_le_u16 (const unsigned char *buf)
|
|
|
|
|
{
|
2011-02-04 12:57:53 +01:00
|
|
|
|
return buf[0] | (buf[1] << 8);
|
2009-02-24 20:41:44 +00:00
|
|
|
|
}
|
|
|
|
|
|
2003-10-21 17:12:50 +00:00
|
|
|
|
static void
|
|
|
|
|
set_msg_len (unsigned char *msg, unsigned int length)
|
|
|
|
|
{
|
|
|
|
|
msg[1] = length;
|
|
|
|
|
msg[2] = length >> 8;
|
|
|
|
|
msg[3] = length >> 16;
|
|
|
|
|
msg[4] = length >> 24;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-07-13 09:59:22 +00:00
|
|
|
|
static void
|
|
|
|
|
print_progress (ccid_driver_t handle)
|
|
|
|
|
{
|
|
|
|
|
time_t ct = time (NULL);
|
|
|
|
|
|
|
|
|
|
/* We don't want to print progress lines too often. */
|
|
|
|
|
if (ct == handle->last_progress)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (handle->progress_cb)
|
|
|
|
|
handle->progress_cb (handle->progress_cb_arg, "card_busy", 'w', 0, 0);
|
|
|
|
|
|
|
|
|
|
handle->last_progress = ct;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-05-23 14:17:22 +00:00
|
|
|
|
/* Pint an error message for a failed CCID command including a textual
|
2008-04-18 09:20:25 +00:00
|
|
|
|
error code. MSG shall be the CCID message at a minimum of 10 bytes. */
|
2005-05-23 14:17:22 +00:00
|
|
|
|
static void
|
|
|
|
|
print_command_failed (const unsigned char *msg)
|
|
|
|
|
{
|
|
|
|
|
const char *t;
|
|
|
|
|
char buffer[100];
|
|
|
|
|
int ec;
|
|
|
|
|
|
|
|
|
|
if (!debug_level)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
ec = CCID_ERROR_CODE (msg);
|
|
|
|
|
switch (ec)
|
|
|
|
|
{
|
|
|
|
|
case 0x00: t = "Command not supported"; break;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-05-23 14:17:22 +00:00
|
|
|
|
case 0xE0: t = "Slot busy"; break;
|
|
|
|
|
case 0xEF: t = "PIN cancelled"; break;
|
|
|
|
|
case 0xF0: t = "PIN timeout"; break;
|
|
|
|
|
|
|
|
|
|
case 0xF2: t = "Automatic sequence ongoing"; break;
|
|
|
|
|
case 0xF3: t = "Deactivated Protocol"; break;
|
|
|
|
|
case 0xF4: t = "Procedure byte conflict"; break;
|
|
|
|
|
case 0xF5: t = "ICC class not supported"; break;
|
|
|
|
|
case 0xF6: t = "ICC protocol not supported"; break;
|
|
|
|
|
case 0xF7: t = "Bad checksum in ATR"; break;
|
|
|
|
|
case 0xF8: t = "Bad TS in ATR"; break;
|
|
|
|
|
|
|
|
|
|
case 0xFB: t = "An all inclusive hardware error occurred"; break;
|
|
|
|
|
case 0xFC: t = "Overrun error while talking to the ICC"; break;
|
|
|
|
|
case 0xFD: t = "Parity error while talking to the ICC"; break;
|
|
|
|
|
case 0xFE: t = "CCID timed out while talking to the ICC"; break;
|
|
|
|
|
case 0xFF: t = "Host aborted the current activity"; break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
if (ec > 0 && ec < 128)
|
|
|
|
|
sprintf (buffer, "Parameter error at offset %d", ec);
|
|
|
|
|
else
|
|
|
|
|
sprintf (buffer, "Error code %02X", ec);
|
|
|
|
|
t = buffer;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
DEBUGOUT_1 ("CCID command failed: %s\n", t);
|
|
|
|
|
}
|
2009-02-24 20:41:44 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_pr_data (const unsigned char *data, size_t datalen, size_t off)
|
|
|
|
|
{
|
|
|
|
|
int any = 0;
|
|
|
|
|
|
|
|
|
|
for (; off < datalen; off++)
|
|
|
|
|
{
|
|
|
|
|
if (!any || !(off % 16))
|
|
|
|
|
{
|
|
|
|
|
if (any)
|
|
|
|
|
DEBUGOUT_LF ();
|
2009-07-29 16:19:48 +00:00
|
|
|
|
DEBUGOUT_1 (" [%04lu] ", (unsigned long) off);
|
2009-02-24 20:41:44 +00:00
|
|
|
|
}
|
|
|
|
|
DEBUGOUT_CONT_1 (" %02X", data[off]);
|
|
|
|
|
any = 1;
|
|
|
|
|
}
|
2009-02-25 10:58:56 +00:00
|
|
|
|
if (any && (off % 16))
|
2009-02-24 20:41:44 +00:00
|
|
|
|
DEBUGOUT_LF ();
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2009-02-24 20:41:44 +00:00
|
|
|
|
static void
|
|
|
|
|
print_p2r_header (const char *name, const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 ("%s:\n", name);
|
|
|
|
|
if (msglen < 7)
|
|
|
|
|
return;
|
|
|
|
|
DEBUGOUT_1 (" dwLength ..........: %u\n", convert_le_u32 (msg+1));
|
|
|
|
|
DEBUGOUT_1 (" bSlot .............: %u\n", msg[5]);
|
|
|
|
|
DEBUGOUT_1 (" bSeq ..............: %u\n", msg[6]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_p2r_iccpoweron (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_p2r_header ("PC_to_RDR_IccPowerOn", msg, msglen);
|
|
|
|
|
if (msglen < 10)
|
|
|
|
|
return;
|
|
|
|
|
DEBUGOUT_2 (" bPowerSelect ......: 0x%02x (%s)\n", msg[7],
|
|
|
|
|
msg[7] == 0? "auto":
|
|
|
|
|
msg[7] == 1? "5.0 V":
|
|
|
|
|
msg[7] == 2? "3.0 V":
|
|
|
|
|
msg[7] == 3? "1.8 V":"");
|
|
|
|
|
print_pr_data (msg, msglen, 8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_p2r_iccpoweroff (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_p2r_header ("PC_to_RDR_IccPowerOff", msg, msglen);
|
|
|
|
|
print_pr_data (msg, msglen, 7);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_p2r_getslotstatus (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_p2r_header ("PC_to_RDR_GetSlotStatus", msg, msglen);
|
|
|
|
|
print_pr_data (msg, msglen, 7);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_p2r_xfrblock (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
unsigned int val;
|
|
|
|
|
|
|
|
|
|
print_p2r_header ("PC_to_RDR_XfrBlock", msg, msglen);
|
|
|
|
|
if (msglen < 10)
|
|
|
|
|
return;
|
|
|
|
|
DEBUGOUT_1 (" bBWI ..............: 0x%02x\n", msg[7]);
|
|
|
|
|
val = convert_le_u16 (msg+8);
|
|
|
|
|
DEBUGOUT_2 (" wLevelParameter ...: 0x%04x%s\n", val,
|
|
|
|
|
val == 1? " (continued)":
|
|
|
|
|
val == 2? " (continues+ends)":
|
|
|
|
|
val == 3? " (continues+continued)":
|
|
|
|
|
val == 16? " (DataBlock-expected)":"");
|
|
|
|
|
print_pr_data (msg, msglen, 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_p2r_getparameters (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_p2r_header ("PC_to_RDR_GetParameters", msg, msglen);
|
|
|
|
|
print_pr_data (msg, msglen, 7);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_p2r_resetparameters (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_p2r_header ("PC_to_RDR_ResetParameters", msg, msglen);
|
|
|
|
|
print_pr_data (msg, msglen, 7);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_p2r_setparameters (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_p2r_header ("PC_to_RDR_SetParameters", msg, msglen);
|
|
|
|
|
if (msglen < 10)
|
|
|
|
|
return;
|
|
|
|
|
DEBUGOUT_1 (" bProtocolNum ......: 0x%02x\n", msg[7]);
|
|
|
|
|
print_pr_data (msg, msglen, 8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_p2r_escape (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_p2r_header ("PC_to_RDR_Escape", msg, msglen);
|
|
|
|
|
print_pr_data (msg, msglen, 7);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_p2r_iccclock (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_p2r_header ("PC_to_RDR_IccClock", msg, msglen);
|
|
|
|
|
if (msglen < 10)
|
|
|
|
|
return;
|
|
|
|
|
DEBUGOUT_1 (" bClockCommand .....: 0x%02x\n", msg[7]);
|
|
|
|
|
print_pr_data (msg, msglen, 8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_p2r_to0apdu (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_p2r_header ("PC_to_RDR_T0APDU", msg, msglen);
|
|
|
|
|
if (msglen < 10)
|
|
|
|
|
return;
|
|
|
|
|
DEBUGOUT_1 (" bmChanges .........: 0x%02x\n", msg[7]);
|
|
|
|
|
DEBUGOUT_1 (" bClassGetResponse .: 0x%02x\n", msg[8]);
|
|
|
|
|
DEBUGOUT_1 (" bClassEnvelope ....: 0x%02x\n", msg[9]);
|
|
|
|
|
print_pr_data (msg, msglen, 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_p2r_secure (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
unsigned int val;
|
|
|
|
|
|
|
|
|
|
print_p2r_header ("PC_to_RDR_Secure", msg, msglen);
|
|
|
|
|
if (msglen < 10)
|
|
|
|
|
return;
|
|
|
|
|
DEBUGOUT_1 (" bBMI ..............: 0x%02x\n", msg[7]);
|
|
|
|
|
val = convert_le_u16 (msg+8);
|
|
|
|
|
DEBUGOUT_2 (" wLevelParameter ...: 0x%04x%s\n", val,
|
|
|
|
|
val == 1? " (continued)":
|
|
|
|
|
val == 2? " (continues+ends)":
|
|
|
|
|
val == 3? " (continues+continued)":
|
|
|
|
|
val == 16? " (DataBlock-expected)":"");
|
|
|
|
|
print_pr_data (msg, msglen, 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_p2r_mechanical (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_p2r_header ("PC_to_RDR_Mechanical", msg, msglen);
|
|
|
|
|
if (msglen < 10)
|
|
|
|
|
return;
|
|
|
|
|
DEBUGOUT_1 (" bFunction .........: 0x%02x\n", msg[7]);
|
|
|
|
|
print_pr_data (msg, msglen, 8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_p2r_abort (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_p2r_header ("PC_to_RDR_Abort", msg, msglen);
|
|
|
|
|
print_pr_data (msg, msglen, 7);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_p2r_setdatarate (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_p2r_header ("PC_to_RDR_SetDataRate", msg, msglen);
|
|
|
|
|
if (msglen < 10)
|
|
|
|
|
return;
|
|
|
|
|
print_pr_data (msg, msglen, 7);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_p2r_unknown (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_p2r_header ("Unknown PC_to_RDR command", msg, msglen);
|
|
|
|
|
if (msglen < 10)
|
|
|
|
|
return;
|
|
|
|
|
print_pr_data (msg, msglen, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_r2p_header (const char *name, const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 ("%s:\n", name);
|
|
|
|
|
if (msglen < 9)
|
|
|
|
|
return;
|
|
|
|
|
DEBUGOUT_1 (" dwLength ..........: %u\n", convert_le_u32 (msg+1));
|
|
|
|
|
DEBUGOUT_1 (" bSlot .............: %u\n", msg[5]);
|
|
|
|
|
DEBUGOUT_1 (" bSeq ..............: %u\n", msg[6]);
|
|
|
|
|
DEBUGOUT_1 (" bStatus ...........: %u\n", msg[7]);
|
|
|
|
|
if (msg[8])
|
|
|
|
|
DEBUGOUT_1 (" bError ............: %u\n", msg[8]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_r2p_datablock (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_r2p_header ("RDR_to_PC_DataBlock", msg, msglen);
|
|
|
|
|
if (msglen < 10)
|
|
|
|
|
return;
|
|
|
|
|
if (msg[9])
|
|
|
|
|
DEBUGOUT_2 (" bChainParameter ...: 0x%02x%s\n", msg[9],
|
|
|
|
|
msg[9] == 1? " (continued)":
|
|
|
|
|
msg[9] == 2? " (continues+ends)":
|
|
|
|
|
msg[9] == 3? " (continues+continued)":
|
|
|
|
|
msg[9] == 16? " (XferBlock-expected)":"");
|
|
|
|
|
print_pr_data (msg, msglen, 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_r2p_slotstatus (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_r2p_header ("RDR_to_PC_SlotStatus", msg, msglen);
|
|
|
|
|
if (msglen < 10)
|
|
|
|
|
return;
|
|
|
|
|
DEBUGOUT_2 (" bClockStatus ......: 0x%02x%s\n", msg[9],
|
|
|
|
|
msg[9] == 0? " (running)":
|
|
|
|
|
msg[9] == 1? " (stopped-L)":
|
|
|
|
|
msg[9] == 2? " (stopped-H)":
|
|
|
|
|
msg[9] == 3? " (stopped)":"");
|
|
|
|
|
print_pr_data (msg, msglen, 10);
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-05-23 14:17:22 +00:00
|
|
|
|
|
2009-02-24 20:41:44 +00:00
|
|
|
|
static void
|
|
|
|
|
print_r2p_parameters (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_r2p_header ("RDR_to_PC_Parameters", msg, msglen);
|
|
|
|
|
if (msglen < 10)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
DEBUGOUT_1 (" protocol ..........: T=%d\n", msg[9]);
|
|
|
|
|
if (msglen == 17 && msg[9] == 1)
|
|
|
|
|
{
|
|
|
|
|
/* Protocol T=1. */
|
|
|
|
|
DEBUGOUT_1 (" bmFindexDindex ....: %02X\n", msg[10]);
|
|
|
|
|
DEBUGOUT_1 (" bmTCCKST1 .........: %02X\n", msg[11]);
|
|
|
|
|
DEBUGOUT_1 (" bGuardTimeT1 ......: %02X\n", msg[12]);
|
|
|
|
|
DEBUGOUT_1 (" bmWaitingIntegersT1: %02X\n", msg[13]);
|
|
|
|
|
DEBUGOUT_1 (" bClockStop ........: %02X\n", msg[14]);
|
|
|
|
|
DEBUGOUT_1 (" bIFSC .............: %d\n", msg[15]);
|
|
|
|
|
DEBUGOUT_1 (" bNadValue .........: %d\n", msg[16]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
print_pr_data (msg, msglen, 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_r2p_escape (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_r2p_header ("RDR_to_PC_Escape", msg, msglen);
|
|
|
|
|
if (msglen < 10)
|
|
|
|
|
return;
|
|
|
|
|
DEBUGOUT_1 (" buffer[9] .........: %02X\n", msg[9]);
|
|
|
|
|
print_pr_data (msg, msglen, 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_r2p_datarate (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_r2p_header ("RDR_to_PC_DataRate", msg, msglen);
|
|
|
|
|
if (msglen < 10)
|
|
|
|
|
return;
|
|
|
|
|
if (msglen >= 18)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 (" dwClockFrequency ..: %u\n", convert_le_u32 (msg+10));
|
|
|
|
|
DEBUGOUT_1 (" dwDataRate ..... ..: %u\n", convert_le_u32 (msg+14));
|
|
|
|
|
print_pr_data (msg, msglen, 18);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
print_pr_data (msg, msglen, 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_r2p_unknown (const unsigned char *msg, size_t msglen)
|
|
|
|
|
{
|
|
|
|
|
print_r2p_header ("Unknown RDR_to_PC command", msg, msglen);
|
|
|
|
|
if (msglen < 10)
|
|
|
|
|
return;
|
|
|
|
|
DEBUGOUT_1 (" bMessageType ......: %02X\n", msg[0]);
|
|
|
|
|
DEBUGOUT_1 (" buffer[9] .........: %02X\n", msg[9]);
|
|
|
|
|
print_pr_data (msg, msglen, 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-09-19 11:17:11 +00:00
|
|
|
|
/* Parse a CCID descriptor, optionally print all available features
|
|
|
|
|
and test whether this reader is usable by this driver. Returns 0
|
|
|
|
|
if it is usable.
|
|
|
|
|
|
|
|
|
|
Note, that this code is based on the one in lsusb.c of the
|
|
|
|
|
usb-utils package, I wrote on 2003-09-01. -wk. */
|
|
|
|
|
static int
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
parse_ccid_descriptor (ccid_driver_t handle, unsigned short bcd_device,
|
2004-04-20 14:17:10 +00:00
|
|
|
|
const unsigned char *buf, size_t buflen)
|
2003-09-19 11:17:11 +00:00
|
|
|
|
{
|
|
|
|
|
unsigned int i;
|
|
|
|
|
unsigned int us;
|
2013-01-08 15:22:31 +09:00
|
|
|
|
int have_t1 = 0, have_tpdu=0;
|
2003-09-19 11:17:11 +00:00
|
|
|
|
|
2004-04-20 14:17:10 +00:00
|
|
|
|
handle->nonnull_nad = 0;
|
|
|
|
|
handle->auto_ifsd = 0;
|
|
|
|
|
handle->max_ifsd = 32;
|
2004-09-30 14:34:34 +00:00
|
|
|
|
handle->has_pinpad = 0;
|
2004-12-28 07:13:24 +00:00
|
|
|
|
handle->apdu_level = 0;
|
2013-01-08 15:22:31 +09:00
|
|
|
|
handle->auto_voltage = 0;
|
|
|
|
|
handle->auto_param = 0;
|
|
|
|
|
handle->auto_pps = 0;
|
2004-10-06 13:13:51 +00:00
|
|
|
|
DEBUGOUT_3 ("idVendor: %04X idProduct: %04X bcdDevice: %04X\n",
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
handle->id_vendor, handle->id_product, bcd_device);
|
2003-09-19 11:17:11 +00:00
|
|
|
|
if (buflen < 54 || buf[0] < 54)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT ("CCID device descriptor is too short\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DEBUGOUT ("ChipCard Interface Descriptor:\n");
|
|
|
|
|
DEBUGOUT_1 (" bLength %5u\n", buf[0]);
|
|
|
|
|
DEBUGOUT_1 (" bDescriptorType %5u\n", buf[1]);
|
|
|
|
|
DEBUGOUT_2 (" bcdCCID %2x.%02x", buf[3], buf[2]);
|
2011-02-04 12:57:53 +01:00
|
|
|
|
if (buf[3] != 1 || buf[2] != 0)
|
2003-09-19 11:17:11 +00:00
|
|
|
|
DEBUGOUT_CONT(" (Warning: Only accurate for version 1.0)");
|
|
|
|
|
DEBUGOUT_LF ();
|
|
|
|
|
|
|
|
|
|
DEBUGOUT_1 (" nMaxSlotIndex %5u\n", buf[4]);
|
|
|
|
|
DEBUGOUT_2 (" bVoltageSupport %5u %s\n",
|
|
|
|
|
buf[5], (buf[5] == 1? "5.0V" : buf[5] == 2? "3.0V"
|
|
|
|
|
: buf[5] == 3? "1.8V":"?"));
|
|
|
|
|
|
|
|
|
|
us = convert_le_u32 (buf+6);
|
|
|
|
|
DEBUGOUT_1 (" dwProtocols %5u ", us);
|
|
|
|
|
if ((us & 1))
|
|
|
|
|
DEBUGOUT_CONT (" T=0");
|
|
|
|
|
if ((us & 2))
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_CONT (" T=1");
|
|
|
|
|
have_t1 = 1;
|
|
|
|
|
}
|
|
|
|
|
if ((us & ~3))
|
|
|
|
|
DEBUGOUT_CONT (" (Invalid values detected)");
|
|
|
|
|
DEBUGOUT_LF ();
|
|
|
|
|
|
|
|
|
|
us = convert_le_u32(buf+10);
|
|
|
|
|
DEBUGOUT_1 (" dwDefaultClock %5u\n", us);
|
|
|
|
|
us = convert_le_u32(buf+14);
|
|
|
|
|
DEBUGOUT_1 (" dwMaxiumumClock %5u\n", us);
|
|
|
|
|
DEBUGOUT_1 (" bNumClockSupported %5u\n", buf[18]);
|
|
|
|
|
us = convert_le_u32(buf+19);
|
|
|
|
|
DEBUGOUT_1 (" dwDataRate %7u bps\n", us);
|
|
|
|
|
us = convert_le_u32(buf+23);
|
|
|
|
|
DEBUGOUT_1 (" dwMaxDataRate %7u bps\n", us);
|
|
|
|
|
DEBUGOUT_1 (" bNumDataRatesSupp. %5u\n", buf[27]);
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2003-09-19 11:17:11 +00:00
|
|
|
|
us = convert_le_u32(buf+28);
|
|
|
|
|
DEBUGOUT_1 (" dwMaxIFSD %5u\n", us);
|
2004-04-20 14:17:10 +00:00
|
|
|
|
handle->max_ifsd = us;
|
2003-09-19 11:17:11 +00:00
|
|
|
|
|
|
|
|
|
us = convert_le_u32(buf+32);
|
|
|
|
|
DEBUGOUT_1 (" dwSyncProtocols %08X ", us);
|
|
|
|
|
if ((us&1))
|
|
|
|
|
DEBUGOUT_CONT ( " 2-wire");
|
|
|
|
|
if ((us&2))
|
|
|
|
|
DEBUGOUT_CONT ( " 3-wire");
|
|
|
|
|
if ((us&4))
|
|
|
|
|
DEBUGOUT_CONT ( " I2C");
|
|
|
|
|
DEBUGOUT_LF ();
|
|
|
|
|
|
|
|
|
|
us = convert_le_u32(buf+36);
|
|
|
|
|
DEBUGOUT_1 (" dwMechanical %08X ", us);
|
|
|
|
|
if ((us & 1))
|
|
|
|
|
DEBUGOUT_CONT (" accept");
|
|
|
|
|
if ((us & 2))
|
|
|
|
|
DEBUGOUT_CONT (" eject");
|
|
|
|
|
if ((us & 4))
|
|
|
|
|
DEBUGOUT_CONT (" capture");
|
|
|
|
|
if ((us & 8))
|
|
|
|
|
DEBUGOUT_CONT (" lock");
|
|
|
|
|
DEBUGOUT_LF ();
|
|
|
|
|
|
|
|
|
|
us = convert_le_u32(buf+40);
|
|
|
|
|
DEBUGOUT_1 (" dwFeatures %08X\n", us);
|
|
|
|
|
if ((us & 0x0002))
|
|
|
|
|
{
|
2013-01-08 15:22:31 +09:00
|
|
|
|
DEBUGOUT (" Auto configuration based on ATR (assumes auto voltage)\n");
|
|
|
|
|
handle->auto_voltage = 1;
|
2003-09-19 11:17:11 +00:00
|
|
|
|
}
|
|
|
|
|
if ((us & 0x0004))
|
|
|
|
|
DEBUGOUT (" Auto activation on insert\n");
|
|
|
|
|
if ((us & 0x0008))
|
2013-01-08 15:22:31 +09:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT (" Auto voltage selection\n");
|
|
|
|
|
handle->auto_voltage = 1;
|
|
|
|
|
}
|
2003-09-19 11:17:11 +00:00
|
|
|
|
if ((us & 0x0010))
|
|
|
|
|
DEBUGOUT (" Auto clock change\n");
|
|
|
|
|
if ((us & 0x0020))
|
|
|
|
|
DEBUGOUT (" Auto baud rate change\n");
|
|
|
|
|
if ((us & 0x0040))
|
2013-01-08 15:22:31 +09:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT (" Auto parameter negotiation made by CCID\n");
|
|
|
|
|
handle->auto_param = 1;
|
|
|
|
|
}
|
2003-09-19 11:17:11 +00:00
|
|
|
|
else if ((us & 0x0080))
|
2013-01-08 15:22:31 +09:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT (" Auto PPS made by CCID\n");
|
|
|
|
|
handle->auto_pps = 1;
|
|
|
|
|
}
|
|
|
|
|
if ((us & (0x0040 | 0x0080)) == (0x0040 | 0x0080))
|
2010-01-08 19:18:49 +00:00
|
|
|
|
DEBUGOUT (" WARNING: conflicting negotiation features\n");
|
2003-09-19 11:17:11 +00:00
|
|
|
|
|
|
|
|
|
if ((us & 0x0100))
|
|
|
|
|
DEBUGOUT (" CCID can set ICC in clock stop mode\n");
|
|
|
|
|
if ((us & 0x0200))
|
2004-04-20 14:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT (" NAD value other than 0x00 accepted\n");
|
|
|
|
|
handle->nonnull_nad = 1;
|
|
|
|
|
}
|
2003-09-19 11:17:11 +00:00
|
|
|
|
if ((us & 0x0400))
|
2004-04-20 14:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT (" Auto IFSD exchange\n");
|
|
|
|
|
handle->auto_ifsd = 1;
|
|
|
|
|
}
|
2003-09-19 11:17:11 +00:00
|
|
|
|
|
|
|
|
|
if ((us & 0x00010000))
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT (" TPDU level exchange\n");
|
|
|
|
|
have_tpdu = 1;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
}
|
2003-09-19 11:17:11 +00:00
|
|
|
|
else if ((us & 0x00020000))
|
2004-12-28 07:13:24 +00:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT (" Short APDU level exchange\n");
|
|
|
|
|
handle->apdu_level = 1;
|
|
|
|
|
}
|
2003-09-19 11:17:11 +00:00
|
|
|
|
else if ((us & 0x00040000))
|
2004-12-28 07:13:24 +00:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT (" Short and extended APDU level exchange\n");
|
2009-06-29 20:54:00 +00:00
|
|
|
|
handle->apdu_level = 2;
|
2004-12-28 07:13:24 +00:00
|
|
|
|
}
|
2003-09-19 11:17:11 +00:00
|
|
|
|
else if ((us & 0x00070000))
|
|
|
|
|
DEBUGOUT (" WARNING: conflicting exchange levels\n");
|
|
|
|
|
|
|
|
|
|
us = convert_le_u32(buf+44);
|
|
|
|
|
DEBUGOUT_1 (" dwMaxCCIDMsgLen %5u\n", us);
|
2015-04-14 14:17:03 +09:00
|
|
|
|
handle->max_ccid_msglen = us;
|
2003-09-19 11:17:11 +00:00
|
|
|
|
|
|
|
|
|
DEBUGOUT ( " bClassGetResponse ");
|
|
|
|
|
if (buf[48] == 0xff)
|
|
|
|
|
DEBUGOUT_CONT ("echo\n");
|
|
|
|
|
else
|
|
|
|
|
DEBUGOUT_CONT_1 (" %02X\n", buf[48]);
|
|
|
|
|
|
|
|
|
|
DEBUGOUT ( " bClassEnvelope ");
|
|
|
|
|
if (buf[49] == 0xff)
|
|
|
|
|
DEBUGOUT_CONT ("echo\n");
|
|
|
|
|
else
|
2005-02-03 13:20:57 +00:00
|
|
|
|
DEBUGOUT_CONT_1 (" %02X\n", buf[48]);
|
2003-09-19 11:17:11 +00:00
|
|
|
|
|
|
|
|
|
DEBUGOUT ( " wlcdLayout ");
|
|
|
|
|
if (!buf[50] && !buf[51])
|
|
|
|
|
DEBUGOUT_CONT ("none\n");
|
|
|
|
|
else
|
|
|
|
|
DEBUGOUT_CONT_2 ("%u cols %u lines\n", buf[50], buf[51]);
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2003-09-19 11:17:11 +00:00
|
|
|
|
DEBUGOUT_1 (" bPINSupport %5u ", buf[52]);
|
|
|
|
|
if ((buf[52] & 1))
|
2004-09-30 14:34:34 +00:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_CONT ( " verification");
|
|
|
|
|
handle->has_pinpad |= 1;
|
|
|
|
|
}
|
2003-09-19 11:17:11 +00:00
|
|
|
|
if ((buf[52] & 2))
|
2004-09-30 14:34:34 +00:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_CONT ( " modification");
|
|
|
|
|
handle->has_pinpad |= 2;
|
|
|
|
|
}
|
2003-09-19 11:17:11 +00:00
|
|
|
|
DEBUGOUT_LF ();
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2003-09-19 11:17:11 +00:00
|
|
|
|
DEBUGOUT_1 (" bMaxCCIDBusySlots %5u\n", buf[53]);
|
|
|
|
|
|
2010-10-01 20:33:53 +00:00
|
|
|
|
if (buf[0] > 54)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT (" junk ");
|
|
|
|
|
for (i=54; i < buf[0]-54; i++)
|
|
|
|
|
DEBUGOUT_CONT_1 (" %02X", buf[i]);
|
|
|
|
|
DEBUGOUT_LF ();
|
|
|
|
|
}
|
2003-09-19 11:17:11 +00:00
|
|
|
|
|
2013-01-08 15:22:31 +09:00
|
|
|
|
if (!have_t1 || !(have_tpdu || handle->apdu_level))
|
2003-09-19 11:17:11 +00:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT ("this drivers requires that the reader supports T=1, "
|
2013-01-08 15:22:31 +09:00
|
|
|
|
"TPDU or APDU level exchange - this is not available\n");
|
2003-09-19 11:17:11 +00:00
|
|
|
|
return -1;
|
|
|
|
|
}
|
2004-10-06 13:13:51 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* SCM drivers get stuck in their internal USB stack if they try to
|
|
|
|
|
send a frame of n*wMaxPacketSize back to us. Given that
|
|
|
|
|
wMaxPacketSize is 64 for these readers we set the IFSD to a value
|
|
|
|
|
lower than that:
|
2005-04-11 16:20:10 +00:00
|
|
|
|
64 - 10 CCID header - 4 T1frame - 2 reserved = 48
|
|
|
|
|
Product Ids:
|
2016-12-27 11:58:54 +09:00
|
|
|
|
0xe001 - SCR 331
|
|
|
|
|
0x5111 - SCR 331-DI
|
|
|
|
|
0x5115 - SCR 335
|
|
|
|
|
0xe003 - SPR 532
|
2011-02-04 12:57:53 +01:00
|
|
|
|
The
|
2009-07-24 11:01:17 +00:00
|
|
|
|
0x5117 - SCR 3320 USB ID-000 reader
|
|
|
|
|
seems to be very slow but enabling this workaround boosts the
|
2017-02-20 16:19:50 -05:00
|
|
|
|
performance to a more or less acceptable level (tested by David).
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2005-04-11 16:20:10 +00:00
|
|
|
|
*/
|
2004-10-06 13:13:51 +00:00
|
|
|
|
if (handle->id_vendor == VENDOR_SCM
|
2011-02-04 12:57:53 +01:00
|
|
|
|
&& handle->max_ifsd > 48
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
&& ( (handle->id_product == SCM_SCR331 && bcd_device < 0x0516)
|
|
|
|
|
||(handle->id_product == SCM_SCR331DI && bcd_device < 0x0620)
|
|
|
|
|
||(handle->id_product == SCM_SCR335 && bcd_device < 0x0514)
|
|
|
|
|
||(handle->id_product == SCM_SPR532 && bcd_device < 0x0504)
|
|
|
|
|
||(handle->id_product == SCM_SCR3320 && bcd_device < 0x0522)
|
2005-04-11 16:20:10 +00:00
|
|
|
|
))
|
2004-10-06 13:13:51 +00:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT ("enabling workaround for buggy SCM readers\n");
|
|
|
|
|
handle->max_ifsd = 48;
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-19 11:24:50 +09:00
|
|
|
|
if (handle->id_vendor == VENDOR_GEMPC)
|
2014-06-25 20:25:28 +02:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT ("enabling product quirk: disable non-null NAD\n");
|
|
|
|
|
handle->nonnull_nad = 0;
|
|
|
|
|
}
|
2004-10-06 13:13:51 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
2003-09-19 11:17:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
static char *
|
2016-01-27 12:24:05 +09:00
|
|
|
|
get_escaped_usb_string (libusb_device_handle *idev, int idx,
|
2004-09-30 14:34:34 +00:00
|
|
|
|
const char *prefix, const char *suffix)
|
|
|
|
|
{
|
|
|
|
|
int rc;
|
|
|
|
|
unsigned char buf[280];
|
|
|
|
|
unsigned char *s;
|
|
|
|
|
unsigned int langid;
|
|
|
|
|
size_t i, n, len;
|
|
|
|
|
char *result;
|
|
|
|
|
|
|
|
|
|
if (!idx)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2004-10-06 13:13:51 +00:00
|
|
|
|
/* Fixme: The next line is for the current Valgrid without support
|
2004-09-30 14:34:34 +00:00
|
|
|
|
for USB IOCTLs. */
|
|
|
|
|
memset (buf, 0, sizeof buf);
|
|
|
|
|
|
|
|
|
|
/* First get the list of supported languages and use the first one.
|
|
|
|
|
If we do don't find it we try to use English. Note that this is
|
|
|
|
|
all in a 2 bute Unicode encoding using little endian. */
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
|
|
|
|
npth_unprotect ();
|
|
|
|
|
#endif
|
2016-01-27 12:24:05 +09:00
|
|
|
|
rc = libusb_control_transfer (idev, LIBUSB_ENDPOINT_IN,
|
|
|
|
|
LIBUSB_REQUEST_GET_DESCRIPTOR,
|
|
|
|
|
(LIBUSB_DT_STRING << 8), 0,
|
|
|
|
|
(char*)buf, sizeof buf, 1000 /* ms timeout */);
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
|
|
|
|
npth_protect ();
|
|
|
|
|
#endif
|
2004-09-30 14:34:34 +00:00
|
|
|
|
if (rc < 4)
|
|
|
|
|
langid = 0x0409; /* English. */
|
|
|
|
|
else
|
|
|
|
|
langid = (buf[3] << 8) | buf[2];
|
|
|
|
|
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
|
|
|
|
npth_unprotect ();
|
|
|
|
|
#endif
|
2016-01-27 12:24:05 +09:00
|
|
|
|
rc = libusb_control_transfer (idev, LIBUSB_ENDPOINT_IN,
|
|
|
|
|
LIBUSB_REQUEST_GET_DESCRIPTOR,
|
|
|
|
|
(LIBUSB_DT_STRING << 8) + idx, langid,
|
|
|
|
|
(char*)buf, sizeof buf, 1000 /* ms timeout */);
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
|
|
|
|
npth_protect ();
|
|
|
|
|
#endif
|
2016-01-27 12:24:05 +09:00
|
|
|
|
if (rc < 2 || buf[1] != LIBUSB_DT_STRING)
|
2004-09-30 14:34:34 +00:00
|
|
|
|
return NULL; /* Error or not a string. */
|
|
|
|
|
len = buf[0];
|
|
|
|
|
if (len > rc)
|
|
|
|
|
return NULL; /* Larger than our buffer. */
|
|
|
|
|
|
|
|
|
|
for (s=buf+2, i=2, n=0; i+1 < len; i += 2, s += 2)
|
|
|
|
|
{
|
|
|
|
|
if (s[1])
|
|
|
|
|
n++; /* High byte set. */
|
|
|
|
|
else if (*s <= 0x20 || *s >= 0x7f || *s == '%' || *s == ':')
|
|
|
|
|
n += 3 ;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
else
|
2004-09-30 14:34:34 +00:00
|
|
|
|
n++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = malloc (strlen (prefix) + n + strlen (suffix) + 1);
|
|
|
|
|
if (!result)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
|
|
strcpy (result, prefix);
|
|
|
|
|
n = strlen (prefix);
|
|
|
|
|
for (s=buf+2, i=2; i+1 < len; i += 2, s += 2)
|
|
|
|
|
{
|
|
|
|
|
if (s[1])
|
|
|
|
|
result[n++] = '\xff'; /* High byte set. */
|
|
|
|
|
else if (*s <= 0x20 || *s >= 0x7f || *s == '%' || *s == ':')
|
|
|
|
|
{
|
|
|
|
|
sprintf (result+n, "%%%02X", *s);
|
|
|
|
|
n += 3;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
else
|
2004-09-30 14:34:34 +00:00
|
|
|
|
result[n++] = *s;
|
|
|
|
|
}
|
|
|
|
|
strcpy (result+n, suffix);
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* This function creates an reader id to be used to find the same
|
|
|
|
|
physical reader after a reset. It returns an allocated and possibly
|
|
|
|
|
percent escaped string or NULL if not enough memory is available. */
|
|
|
|
|
static char *
|
2016-01-27 12:24:05 +09:00
|
|
|
|
make_reader_id (libusb_device_handle *idev,
|
2004-09-30 14:34:34 +00:00
|
|
|
|
unsigned int vendor, unsigned int product,
|
|
|
|
|
unsigned char serialno_index)
|
2003-09-19 11:17:11 +00:00
|
|
|
|
{
|
2004-09-30 14:34:34 +00:00
|
|
|
|
char *rid;
|
|
|
|
|
char prefix[20];
|
2003-09-19 11:17:11 +00:00
|
|
|
|
|
2006-02-06 16:13:20 +00:00
|
|
|
|
sprintf (prefix, "%04X:%04X:", (vendor & 0xffff), (product & 0xffff));
|
2004-09-30 14:34:34 +00:00
|
|
|
|
rid = get_escaped_usb_string (idev, serialno_index, prefix, ":0");
|
|
|
|
|
if (!rid)
|
|
|
|
|
{
|
|
|
|
|
rid = malloc (strlen (prefix) + 3 + 1);
|
|
|
|
|
if (!rid)
|
|
|
|
|
return NULL;
|
|
|
|
|
strcpy (rid, prefix);
|
|
|
|
|
strcat (rid, "X:0");
|
|
|
|
|
}
|
|
|
|
|
return rid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2004-12-28 07:13:24 +00:00
|
|
|
|
/* Helper to find the endpoint from an interface descriptor. */
|
|
|
|
|
static int
|
2016-01-27 12:24:05 +09:00
|
|
|
|
find_endpoint (const struct libusb_interface_descriptor *ifcdesc, int mode)
|
2004-12-28 07:13:24 +00:00
|
|
|
|
{
|
|
|
|
|
int no;
|
|
|
|
|
int want_bulk_in = 0;
|
|
|
|
|
|
|
|
|
|
if (mode == 1)
|
|
|
|
|
want_bulk_in = 0x80;
|
|
|
|
|
for (no=0; no < ifcdesc->bNumEndpoints; no++)
|
|
|
|
|
{
|
2016-01-27 12:24:05 +09:00
|
|
|
|
const struct libusb_endpoint_descriptor *ep = ifcdesc->endpoint + no;
|
|
|
|
|
if (ep->bDescriptorType != LIBUSB_DT_ENDPOINT)
|
2004-12-28 07:13:24 +00:00
|
|
|
|
;
|
|
|
|
|
else if (mode == 2
|
2016-01-27 12:24:05 +09:00
|
|
|
|
&& ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK)
|
|
|
|
|
== LIBUSB_TRANSFER_TYPE_INTERRUPT)
|
|
|
|
|
&& (ep->bEndpointAddress & 0x80))
|
|
|
|
|
return ep->bEndpointAddress;
|
2017-01-23 16:12:41 +09:00
|
|
|
|
else if ((mode == 0 || mode == 1)
|
|
|
|
|
&& ((ep->bmAttributes & LIBUSB_TRANSFER_TYPE_MASK)
|
|
|
|
|
== LIBUSB_TRANSFER_TYPE_BULK)
|
2004-12-28 07:13:24 +00:00
|
|
|
|
&& (ep->bEndpointAddress & 0x80) == want_bulk_in)
|
2016-01-27 12:24:05 +09:00
|
|
|
|
return ep->bEndpointAddress;
|
2004-12-28 07:13:24 +00:00
|
|
|
|
}
|
2016-01-27 12:24:05 +09:00
|
|
|
|
|
|
|
|
|
return -1;
|
2004-12-28 07:13:24 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-04-07 13:30:35 +09:00
|
|
|
|
/* Helper for scan_devices. This function returns true if a
|
2006-02-06 16:13:20 +00:00
|
|
|
|
requested device has been found or the caller should stop scanning
|
|
|
|
|
for other reasons. */
|
2017-04-07 13:30:35 +09:00
|
|
|
|
static void
|
|
|
|
|
scan_usb_device (int *count, char **rid_list, struct libusb_device *dev)
|
2006-02-06 16:13:20 +00:00
|
|
|
|
{
|
|
|
|
|
int ifc_no;
|
|
|
|
|
int set_no;
|
2016-01-27 12:24:05 +09:00
|
|
|
|
const struct libusb_interface_descriptor *ifcdesc;
|
2006-02-06 16:13:20 +00:00
|
|
|
|
char *rid;
|
2016-12-27 11:58:54 +09:00
|
|
|
|
libusb_device_handle *idev = NULL;
|
2016-01-27 12:24:05 +09:00
|
|
|
|
int err;
|
2016-12-29 11:31:25 +09:00
|
|
|
|
struct libusb_config_descriptor *config;
|
2017-04-07 13:30:35 +09:00
|
|
|
|
struct libusb_device_descriptor desc;
|
|
|
|
|
char *p;
|
2016-01-27 12:24:05 +09:00
|
|
|
|
|
2017-04-07 13:30:35 +09:00
|
|
|
|
err = libusb_get_device_descriptor (dev, &desc);
|
2016-12-29 11:31:25 +09:00
|
|
|
|
if (err)
|
2017-04-07 13:30:35 +09:00
|
|
|
|
return;
|
2006-02-06 16:13:20 +00:00
|
|
|
|
|
2016-12-29 11:31:25 +09:00
|
|
|
|
err = libusb_get_active_config_descriptor (dev, &config);
|
|
|
|
|
if (err)
|
2017-04-07 13:30:35 +09:00
|
|
|
|
return;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2016-12-29 11:31:25 +09:00
|
|
|
|
for (ifc_no=0; ifc_no < config->bNumInterfaces; ifc_no++)
|
|
|
|
|
for (set_no=0; set_no < config->interface[ifc_no].num_altsetting; set_no++)
|
|
|
|
|
{
|
|
|
|
|
ifcdesc = (config->interface[ifc_no].altsetting + set_no);
|
|
|
|
|
/* The second condition is for older SCM SPR 532 who did
|
|
|
|
|
not know about the assigned CCID class. The third
|
|
|
|
|
condition does the same for a Cherry SmartTerminal
|
|
|
|
|
ST-2000. Instead of trying to interpret the strings
|
|
|
|
|
we simply check the product ID. */
|
|
|
|
|
if (ifcdesc && ifcdesc->extra
|
|
|
|
|
&& ((ifcdesc->bInterfaceClass == 11
|
|
|
|
|
&& ifcdesc->bInterfaceSubClass == 0
|
|
|
|
|
&& ifcdesc->bInterfaceProtocol == 0)
|
|
|
|
|
|| (ifcdesc->bInterfaceClass == 255
|
2017-04-07 13:30:35 +09:00
|
|
|
|
&& desc.idVendor == VENDOR_SCM
|
|
|
|
|
&& desc.idProduct == SCM_SPR532)
|
2016-12-29 11:31:25 +09:00
|
|
|
|
|| (ifcdesc->bInterfaceClass == 255
|
2017-04-07 13:30:35 +09:00
|
|
|
|
&& desc.idVendor == VENDOR_CHERRY
|
|
|
|
|
&& desc.idProduct == CHERRY_ST2000)))
|
2016-12-29 11:31:25 +09:00
|
|
|
|
{
|
|
|
|
|
++*count;
|
|
|
|
|
|
|
|
|
|
err = libusb_open (dev, &idev);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 ("usb_open failed: %s\n", libusb_error_name (err));
|
|
|
|
|
continue; /* with next setting. */
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-07 13:30:35 +09:00
|
|
|
|
rid = make_reader_id (idev, desc.idVendor, desc.idProduct,
|
|
|
|
|
desc.iSerialNumber);
|
2016-12-29 11:31:25 +09:00
|
|
|
|
if (!rid)
|
|
|
|
|
{
|
|
|
|
|
libusb_free_config_descriptor (config);
|
2017-04-07 13:30:35 +09:00
|
|
|
|
return;
|
2016-12-29 11:31:25 +09:00
|
|
|
|
}
|
|
|
|
|
|
2017-04-07 13:30:35 +09:00
|
|
|
|
/* We are collecting infos about all available CCID
|
|
|
|
|
readers. Store them and continue. */
|
|
|
|
|
DEBUGOUT_2 ("found CCID reader %d (ID=%s)\n", *count, rid);
|
|
|
|
|
p = malloc ((*rid_list? strlen (*rid_list):0) + 1
|
|
|
|
|
+ strlen (rid) + 1);
|
|
|
|
|
if (p)
|
2016-12-29 11:31:25 +09:00
|
|
|
|
{
|
2017-04-07 13:30:35 +09:00
|
|
|
|
*p = 0;
|
|
|
|
|
if (*rid_list)
|
2016-12-29 11:31:25 +09:00
|
|
|
|
{
|
2017-04-07 13:30:35 +09:00
|
|
|
|
strcat (p, *rid_list);
|
|
|
|
|
free (*rid_list);
|
2016-12-29 11:31:25 +09:00
|
|
|
|
}
|
2017-04-07 13:30:35 +09:00
|
|
|
|
strcat (p, rid);
|
|
|
|
|
strcat (p, "\n");
|
|
|
|
|
*rid_list = p;
|
2016-12-29 11:31:25 +09:00
|
|
|
|
}
|
2017-04-07 13:30:35 +09:00
|
|
|
|
else /* Out of memory. */
|
2016-12-29 11:31:25 +09:00
|
|
|
|
{
|
|
|
|
|
libusb_free_config_descriptor (config);
|
2017-04-07 13:30:35 +09:00
|
|
|
|
free (rid);
|
|
|
|
|
return;
|
2016-12-29 11:31:25 +09:00
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2016-12-29 11:31:25 +09:00
|
|
|
|
free (rid);
|
|
|
|
|
libusb_close (idev);
|
|
|
|
|
idev = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-04-28 00:08:08 +09:00
|
|
|
|
|
2016-12-29 11:31:25 +09:00
|
|
|
|
libusb_free_config_descriptor (config);
|
2006-02-06 16:13:20 +00:00
|
|
|
|
}
|
2004-12-28 07:13:24 +00:00
|
|
|
|
|
2017-04-07 13:30:35 +09:00
|
|
|
|
/* Scan all CCID devices.
|
2004-09-30 14:34:34 +00:00
|
|
|
|
|
2006-02-06 16:13:20 +00:00
|
|
|
|
The function returns 0 if a reader has been found or when a scan
|
|
|
|
|
returned without error.
|
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
R_RID should be the address where to store the list of reader_ids
|
|
|
|
|
we found. If on return this list is empty, no CCID device has been
|
|
|
|
|
found; otherwise it points to an allocated linked list of reader
|
2017-04-07 13:30:35 +09:00
|
|
|
|
IDs.
|
2004-09-30 14:34:34 +00:00
|
|
|
|
*/
|
2006-02-06 16:13:20 +00:00
|
|
|
|
static int
|
2017-04-07 13:30:35 +09:00
|
|
|
|
scan_devices (char **r_rid)
|
2004-09-30 14:34:34 +00:00
|
|
|
|
{
|
|
|
|
|
char *rid_list = NULL;
|
|
|
|
|
int count = 0;
|
2016-01-27 12:24:05 +09:00
|
|
|
|
libusb_device **dev_list = NULL;
|
|
|
|
|
libusb_device *dev;
|
2006-02-06 16:13:20 +00:00
|
|
|
|
int i;
|
2016-01-27 12:24:05 +09:00
|
|
|
|
ssize_t n;
|
2004-09-30 14:34:34 +00:00
|
|
|
|
|
2006-02-06 16:13:20 +00:00
|
|
|
|
/* Set return values to a default. */
|
2004-09-30 14:34:34 +00:00
|
|
|
|
if (r_rid)
|
|
|
|
|
*r_rid = NULL;
|
|
|
|
|
|
2016-01-27 12:24:05 +09:00
|
|
|
|
n = libusb_get_device_list (NULL, &dev_list);
|
2003-09-19 11:17:11 +00:00
|
|
|
|
|
2016-01-27 12:24:05 +09:00
|
|
|
|
for (i = 0; i < n; i++)
|
2004-09-30 14:34:34 +00:00
|
|
|
|
{
|
2016-01-27 12:24:05 +09:00
|
|
|
|
dev = dev_list[i];
|
2017-04-07 13:30:35 +09:00
|
|
|
|
scan_usb_device (&count, &rid_list, dev);
|
2003-09-19 11:17:11 +00:00
|
|
|
|
}
|
2004-09-30 14:34:34 +00:00
|
|
|
|
|
2016-04-28 00:08:08 +09:00
|
|
|
|
libusb_free_device_list (dev_list, 1);
|
|
|
|
|
|
2017-04-07 13:30:35 +09:00
|
|
|
|
*r_rid = rid_list;
|
|
|
|
|
return 0;
|
2004-09-30 14:34:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-02-06 16:13:20 +00:00
|
|
|
|
/* Set the level of debugging to LEVEL and return the old level. -1
|
2004-09-30 14:34:34 +00:00
|
|
|
|
just returns the old level. A level of 0 disables debugging, 1
|
2005-05-20 20:39:36 +00:00
|
|
|
|
enables debugging, 2 enables additional tracing of the T=1
|
2009-07-16 15:54:59 +00:00
|
|
|
|
protocol, 3 additionally enables debugging for GetSlotStatus, other
|
2009-02-24 20:41:44 +00:00
|
|
|
|
values are not yet defined.
|
2008-12-18 16:34:28 +00:00
|
|
|
|
|
|
|
|
|
Note that libusb may provide its own debugging feature which is
|
|
|
|
|
enabled by setting the envvar USB_DEBUG. */
|
2004-09-30 14:34:34 +00:00
|
|
|
|
int
|
|
|
|
|
ccid_set_debug_level (int level)
|
|
|
|
|
{
|
|
|
|
|
int old = debug_level;
|
|
|
|
|
if (level != -1)
|
|
|
|
|
debug_level = level;
|
|
|
|
|
return old;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
ccid_get_reader_list (void)
|
|
|
|
|
{
|
|
|
|
|
char *reader_list;
|
|
|
|
|
|
|
|
|
|
if (!initialized_usb)
|
|
|
|
|
{
|
2017-05-31 09:49:54 +09:00
|
|
|
|
int rc;
|
|
|
|
|
if ((rc = libusb_init (NULL)))
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 ("usb_init failed: %s.\n", libusb_error_name (rc));
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2004-09-30 14:34:34 +00:00
|
|
|
|
initialized_usb = 1;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-07 13:30:35 +09:00
|
|
|
|
if (scan_devices (&reader_list))
|
2006-02-06 16:13:20 +00:00
|
|
|
|
return NULL; /* Error. */
|
2004-09-30 14:34:34 +00:00
|
|
|
|
return reader_list;
|
2003-09-19 11:17:11 +00:00
|
|
|
|
}
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
|
|
|
|
|
2013-01-28 11:46:40 +09:00
|
|
|
|
/* Vendor specific custom initialization. */
|
|
|
|
|
static int
|
|
|
|
|
ccid_vendor_specific_init (ccid_driver_t handle)
|
|
|
|
|
{
|
|
|
|
|
if (handle->id_vendor == VENDOR_VEGA && handle->id_product == VEGA_ALPHA)
|
|
|
|
|
{
|
2013-08-27 10:15:46 +09:00
|
|
|
|
int r;
|
2013-01-28 11:46:40 +09:00
|
|
|
|
/*
|
|
|
|
|
* Vega alpha has a feature to show retry counter on the pinpad
|
|
|
|
|
* display. But it assumes that the card returns the value of
|
|
|
|
|
* retry counter by VERIFY with empty data (return code of
|
|
|
|
|
* 63Cx). Unfortunately, existing OpenPGP cards don't support
|
|
|
|
|
* VERIFY command with empty data. This vendor specific command
|
|
|
|
|
* sequence is to disable the feature.
|
|
|
|
|
*/
|
2013-08-27 10:15:46 +09:00
|
|
|
|
const unsigned char cmd[] = { '\xb5', '\x01', '\x00', '\x03', '\x00' };
|
2013-01-28 11:46:40 +09:00
|
|
|
|
|
2013-08-27 10:15:46 +09:00
|
|
|
|
r = send_escape_cmd (handle, cmd, sizeof (cmd), NULL, 0, NULL);
|
|
|
|
|
if (r != 0 && r != CCID_DRIVER_ERR_CARD_INACTIVE
|
|
|
|
|
&& r != CCID_DRIVER_ERR_NO_CARD)
|
|
|
|
|
return r;
|
2013-01-28 11:46:40 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
#define MAX_DEVICE 4 /* See MAX_READER in apdu.c. */
|
|
|
|
|
|
|
|
|
|
struct ccid_dev_table {
|
|
|
|
|
int n; /* Index to ccid_usb_dev_list */
|
|
|
|
|
int interface_number;
|
|
|
|
|
int setting_number;
|
|
|
|
|
unsigned char *ifcdesc_extra;
|
|
|
|
|
int ep_bulk_out;
|
|
|
|
|
int ep_bulk_in;
|
|
|
|
|
int ep_intr;
|
2004-09-30 14:34:34 +00:00
|
|
|
|
size_t ifcdesc_extra_len;
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
};
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
static libusb_device **ccid_usb_dev_list;
|
|
|
|
|
static struct ccid_dev_table ccid_dev_table[MAX_DEVICE];
|
|
|
|
|
|
|
|
|
|
gpg_error_t
|
|
|
|
|
ccid_dev_scan (int *idx_max_p, struct ccid_dev_table **t_p)
|
|
|
|
|
{
|
|
|
|
|
ssize_t n;
|
|
|
|
|
libusb_device *dev;
|
|
|
|
|
int i;
|
|
|
|
|
int ifc_no;
|
|
|
|
|
int set_no;
|
|
|
|
|
int idx = 0;
|
|
|
|
|
int err = 0;
|
2004-09-30 14:34:34 +00:00
|
|
|
|
|
2017-05-31 09:49:54 +09:00
|
|
|
|
*idx_max_p = 0;
|
|
|
|
|
*t_p = NULL;
|
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
if (!initialized_usb)
|
2003-09-02 19:06:34 +00:00
|
|
|
|
{
|
2017-05-31 09:49:54 +09:00
|
|
|
|
int rc;
|
|
|
|
|
if ((rc = libusb_init (NULL)))
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 ("usb_init failed: %s.\n", libusb_error_name (rc));
|
2017-05-31 10:05:36 +09:00
|
|
|
|
return gpg_error (GPG_ERR_ENODEV);
|
2017-05-31 09:49:54 +09:00
|
|
|
|
}
|
2004-09-30 14:34:34 +00:00
|
|
|
|
initialized_usb = 1;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
}
|
2004-09-30 14:34:34 +00:00
|
|
|
|
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
n = libusb_get_device_list (NULL, &ccid_usb_dev_list);
|
|
|
|
|
for (i = 0; i < n; i++)
|
2003-09-02 19:06:34 +00:00
|
|
|
|
{
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
struct libusb_config_descriptor *config;
|
|
|
|
|
struct libusb_device_descriptor desc;
|
|
|
|
|
|
|
|
|
|
dev = ccid_usb_dev_list[i];
|
|
|
|
|
|
|
|
|
|
if (libusb_get_device_descriptor (dev, &desc))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
if (libusb_get_active_config_descriptor (dev, &config))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
for (ifc_no=0; ifc_no < config->bNumInterfaces; ifc_no++)
|
|
|
|
|
for (set_no=0; set_no < config->interface[ifc_no].num_altsetting;
|
|
|
|
|
set_no++)
|
|
|
|
|
{
|
|
|
|
|
const struct libusb_interface_descriptor *ifcdesc;
|
|
|
|
|
|
|
|
|
|
ifcdesc = &config->interface[ifc_no].altsetting[set_no];
|
|
|
|
|
/* The second condition is for older SCM SPR 532 who did
|
|
|
|
|
not know about the assigned CCID class. The third
|
|
|
|
|
condition does the same for a Cherry SmartTerminal
|
|
|
|
|
ST-2000. Instead of trying to interpret the strings
|
|
|
|
|
we simply check the product ID. */
|
|
|
|
|
if (ifcdesc && ifcdesc->extra
|
|
|
|
|
&& ((ifcdesc->bInterfaceClass == 11
|
|
|
|
|
&& ifcdesc->bInterfaceSubClass == 0
|
|
|
|
|
&& ifcdesc->bInterfaceProtocol == 0)
|
|
|
|
|
|| (ifcdesc->bInterfaceClass == 255
|
|
|
|
|
&& desc.idVendor == VENDOR_SCM
|
|
|
|
|
&& desc.idProduct == SCM_SPR532)
|
|
|
|
|
|| (ifcdesc->bInterfaceClass == 255
|
|
|
|
|
&& desc.idVendor == VENDOR_CHERRY
|
|
|
|
|
&& desc.idProduct == CHERRY_ST2000)))
|
|
|
|
|
{
|
|
|
|
|
/* Found a reader. */
|
|
|
|
|
unsigned char *ifcdesc_extra;
|
|
|
|
|
|
|
|
|
|
ifcdesc_extra = malloc (ifcdesc->extra_length);
|
|
|
|
|
if (!ifcdesc_extra)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
libusb_free_config_descriptor (config);
|
|
|
|
|
goto scan_finish;
|
|
|
|
|
}
|
|
|
|
|
memcpy (ifcdesc_extra, ifcdesc->extra, ifcdesc->extra_length);
|
|
|
|
|
|
|
|
|
|
ccid_dev_table[idx].n = i;
|
|
|
|
|
ccid_dev_table[idx].interface_number = ifc_no;
|
|
|
|
|
ccid_dev_table[idx].setting_number = set_no;
|
|
|
|
|
ccid_dev_table[idx].ifcdesc_extra = ifcdesc_extra;
|
|
|
|
|
ccid_dev_table[idx].ifcdesc_extra_len = ifcdesc->extra_length;
|
|
|
|
|
ccid_dev_table[idx].ep_bulk_out = find_endpoint (ifcdesc, 0);
|
|
|
|
|
ccid_dev_table[idx].ep_bulk_in = find_endpoint (ifcdesc, 1);
|
|
|
|
|
ccid_dev_table[idx].ep_intr = find_endpoint (ifcdesc, 2);
|
|
|
|
|
|
|
|
|
|
idx++;
|
|
|
|
|
if (idx >= MAX_DEVICE)
|
|
|
|
|
{
|
|
|
|
|
libusb_free_config_descriptor (config);
|
|
|
|
|
err = 0;
|
|
|
|
|
goto scan_finish;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
libusb_free_config_descriptor (config);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
scan_finish:
|
|
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
for (i = 0; i < idx; i++)
|
|
|
|
|
{
|
|
|
|
|
free (ccid_dev_table[idx].ifcdesc_extra);
|
|
|
|
|
ccid_dev_table[idx].n = 0;
|
|
|
|
|
ccid_dev_table[idx].interface_number = 0;
|
|
|
|
|
ccid_dev_table[idx].setting_number = 0;
|
|
|
|
|
ccid_dev_table[idx].ifcdesc_extra = NULL;
|
|
|
|
|
ccid_dev_table[idx].ifcdesc_extra_len = 0;
|
|
|
|
|
ccid_dev_table[idx].ep_bulk_out = 0;
|
|
|
|
|
ccid_dev_table[idx].ep_bulk_in = 0;
|
|
|
|
|
ccid_dev_table[idx].ep_intr = 0;
|
|
|
|
|
}
|
|
|
|
|
libusb_free_device_list (ccid_usb_dev_list, 1);
|
|
|
|
|
ccid_usb_dev_list = NULL;
|
|
|
|
|
}
|
|
|
|
|
else
|
2003-09-02 19:06:34 +00:00
|
|
|
|
{
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
*idx_max_p = idx;
|
|
|
|
|
if (idx)
|
|
|
|
|
*t_p = ccid_dev_table;
|
2004-09-30 14:34:34 +00:00
|
|
|
|
else
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
*t_p = NULL;
|
2004-09-30 14:34:34 +00:00
|
|
|
|
}
|
2004-04-20 14:17:10 +00:00
|
|
|
|
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-27 18:01:52 +09:00
|
|
|
|
void
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
ccid_dev_scan_finish (struct ccid_dev_table *tbl, int max)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < max; i++)
|
2004-09-30 14:34:34 +00:00
|
|
|
|
{
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
free (tbl[i].ifcdesc_extra);
|
|
|
|
|
tbl[i].n = 0;
|
|
|
|
|
tbl[i].interface_number = 0;
|
|
|
|
|
tbl[i].setting_number = 0;
|
|
|
|
|
tbl[i].ifcdesc_extra = NULL;
|
|
|
|
|
tbl[i].ifcdesc_extra_len = 0;
|
|
|
|
|
tbl[i].ep_bulk_out = 0;
|
|
|
|
|
tbl[i].ep_bulk_in = 0;
|
|
|
|
|
tbl[i].ep_intr = 0;
|
2004-09-30 14:34:34 +00:00
|
|
|
|
}
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
libusb_free_device_list (ccid_usb_dev_list, 1);
|
|
|
|
|
ccid_usb_dev_list = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int
|
|
|
|
|
ccid_get_BAI (int idx, struct ccid_dev_table *tbl)
|
|
|
|
|
{
|
|
|
|
|
int n;
|
|
|
|
|
int bus, addr, intf;
|
|
|
|
|
unsigned int bai;
|
2017-04-10 12:25:06 +09:00
|
|
|
|
libusb_device *dev;
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
|
2017-04-10 12:25:06 +09:00
|
|
|
|
n = tbl[idx].n;
|
|
|
|
|
dev = ccid_usb_dev_list[n];
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
|
2017-04-10 12:25:06 +09:00
|
|
|
|
bus = libusb_get_bus_number (dev);
|
|
|
|
|
addr = libusb_get_device_address (dev);
|
|
|
|
|
intf = tbl[idx].interface_number;
|
|
|
|
|
bai = (bus << 16) | (addr << 8) | intf;
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
|
|
|
|
|
return bai;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ccid_compare_BAI (ccid_driver_t handle, unsigned int bai)
|
|
|
|
|
{
|
|
|
|
|
return handle->bai == bai;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-26 16:54:40 +09:00
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
intr_cb (struct libusb_transfer *transfer)
|
|
|
|
|
{
|
|
|
|
|
ccid_driver_t handle = transfer->user_data;
|
|
|
|
|
|
2017-01-27 18:01:52 +09:00
|
|
|
|
DEBUGOUT_1 ("CCID: interrupt callback %d\n", transfer->status);
|
2017-01-26 16:54:40 +09:00
|
|
|
|
|
2019-01-07 14:08:51 +09:00
|
|
|
|
if (transfer->status == LIBUSB_TRANSFER_TIMED_OUT)
|
2017-01-26 16:54:40 +09:00
|
|
|
|
{
|
2017-01-27 20:43:29 +09:00
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
submit_again:
|
|
|
|
|
/* Submit the URB again to keep watching the INTERRUPT transfer. */
|
|
|
|
|
err = libusb_submit_transfer (transfer);
|
|
|
|
|
if (err == LIBUSB_ERROR_NO_DEVICE)
|
|
|
|
|
goto device_removed;
|
|
|
|
|
|
|
|
|
|
DEBUGOUT_1 ("CCID submit transfer again %d\n", err);
|
2017-01-26 16:54:40 +09:00
|
|
|
|
}
|
|
|
|
|
else if (transfer->status == LIBUSB_TRANSFER_COMPLETED)
|
|
|
|
|
{
|
|
|
|
|
if (transfer->actual_length == 2
|
|
|
|
|
&& transfer->buffer[0] == 0x50
|
|
|
|
|
&& (transfer->buffer[1] & 1) == 0)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT ("CCID: card removed\n");
|
|
|
|
|
handle->powered_off = 1;
|
2017-07-21 13:26:53 +09:00
|
|
|
|
scd_kick_the_loop ();
|
2017-01-26 16:54:40 +09:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2017-01-27 20:43:29 +09:00
|
|
|
|
/* Event other than card removal. */
|
|
|
|
|
goto submit_again;
|
2017-01-26 16:54:40 +09:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-27 20:43:29 +09:00
|
|
|
|
else if (transfer->status == LIBUSB_TRANSFER_CANCELLED)
|
|
|
|
|
handle->powered_off = 1;
|
2017-01-26 16:54:40 +09:00
|
|
|
|
else
|
|
|
|
|
{
|
2017-01-27 20:43:29 +09:00
|
|
|
|
device_removed:
|
|
|
|
|
DEBUGOUT ("CCID: device removed\n");
|
|
|
|
|
handle->powered_off = 1;
|
2017-07-21 13:26:53 +09:00
|
|
|
|
scd_kick_the_loop ();
|
2017-01-26 16:54:40 +09:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
ccid_setup_intr (ccid_driver_t handle)
|
|
|
|
|
{
|
|
|
|
|
struct libusb_transfer *transfer;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
transfer = libusb_alloc_transfer (0);
|
2017-01-27 18:01:52 +09:00
|
|
|
|
handle->transfer = transfer;
|
2017-01-26 16:54:40 +09:00
|
|
|
|
libusb_fill_interrupt_transfer (transfer, handle->idev, handle->ep_intr,
|
|
|
|
|
handle->intr_buf, sizeof (handle->intr_buf),
|
|
|
|
|
intr_cb, handle, 0);
|
|
|
|
|
err = libusb_submit_transfer (transfer);
|
|
|
|
|
DEBUGOUT_2 ("CCID submit transfer (%x): %d", handle->ep_intr, err);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-01-28 00:18:11 +09:00
|
|
|
|
static void *
|
|
|
|
|
ccid_usb_thread (void *arg)
|
|
|
|
|
{
|
|
|
|
|
libusb_context *ctx = arg;
|
|
|
|
|
|
|
|
|
|
while (ccid_usb_thread_is_alive)
|
|
|
|
|
{
|
|
|
|
|
#ifdef USE_NPTH
|
|
|
|
|
npth_unprotect ();
|
|
|
|
|
#endif
|
|
|
|
|
libusb_handle_events_completed (ctx, NULL);
|
|
|
|
|
#ifdef USE_NPTH
|
|
|
|
|
npth_protect ();
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
static int
|
|
|
|
|
ccid_open_usb_reader (const char *spec_reader_name,
|
|
|
|
|
int idx, struct ccid_dev_table *ccid_table,
|
|
|
|
|
ccid_driver_t *handle, char **rdrname_p)
|
|
|
|
|
{
|
|
|
|
|
libusb_device *dev;
|
|
|
|
|
libusb_device_handle *idev = NULL;
|
2017-01-28 00:18:11 +09:00
|
|
|
|
char *rid = NULL;
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
int rc = 0;
|
|
|
|
|
int ifc_no, set_no;
|
|
|
|
|
struct libusb_device_descriptor desc;
|
|
|
|
|
int n;
|
|
|
|
|
int bus, addr;
|
|
|
|
|
unsigned int bai;
|
|
|
|
|
|
|
|
|
|
n = ccid_table[idx].n;
|
|
|
|
|
ifc_no = ccid_table[idx].interface_number;
|
|
|
|
|
set_no = ccid_table[idx].setting_number;
|
|
|
|
|
|
|
|
|
|
dev = ccid_usb_dev_list[n];
|
|
|
|
|
bus = libusb_get_bus_number (dev);
|
|
|
|
|
addr = libusb_get_device_address (dev);
|
|
|
|
|
bai = (bus << 16) | (addr << 8) | ifc_no;
|
|
|
|
|
|
|
|
|
|
rc = libusb_open (dev, &idev);
|
|
|
|
|
if (rc)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 ("usb_open failed: %s\n", libusb_error_name (rc));
|
|
|
|
|
free (*handle);
|
|
|
|
|
*handle = NULL;
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-28 00:18:11 +09:00
|
|
|
|
if (ccid_usb_thread_is_alive++ == 0)
|
|
|
|
|
{
|
|
|
|
|
npth_t thread;
|
|
|
|
|
npth_attr_t tattr;
|
|
|
|
|
int err;
|
|
|
|
|
|
|
|
|
|
err = npth_attr_init (&tattr);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 ("npth_attr_init failed: %s\n", strerror (err));
|
|
|
|
|
free (*handle);
|
|
|
|
|
*handle = NULL;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED);
|
|
|
|
|
err = npth_create (&thread, &tattr, ccid_usb_thread, NULL);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 ("npth_create failed: %s\n", strerror (err));
|
|
|
|
|
free (*handle);
|
|
|
|
|
*handle = NULL;
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
npth_attr_destroy (&tattr);
|
|
|
|
|
}
|
|
|
|
|
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
rc = libusb_get_device_descriptor (dev, &desc);
|
|
|
|
|
if (rc)
|
2006-02-06 16:13:20 +00:00
|
|
|
|
{
|
2017-01-28 00:18:11 +09:00
|
|
|
|
DEBUGOUT ("get_device_descripor failed\n");
|
|
|
|
|
goto leave;
|
2006-02-06 16:13:20 +00:00
|
|
|
|
}
|
2003-09-19 11:17:11 +00:00
|
|
|
|
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
rid = make_reader_id (idev, desc.idVendor, desc.idProduct,
|
|
|
|
|
desc.iSerialNumber);
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
/* Check to see if reader name matches the spec. */
|
|
|
|
|
if (spec_reader_name
|
|
|
|
|
&& strncmp (rid, spec_reader_name, strlen (spec_reader_name)))
|
2004-09-30 14:34:34 +00:00
|
|
|
|
{
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
DEBUGOUT ("device not matched\n");
|
|
|
|
|
rc = CCID_DRIVER_ERR_NO_READER;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
(*handle)->id_vendor = desc.idVendor;
|
|
|
|
|
(*handle)->id_product = desc.idProduct;
|
|
|
|
|
(*handle)->idev = idev;
|
|
|
|
|
(*handle)->bai = bai;
|
|
|
|
|
(*handle)->ifc_no = ifc_no;
|
|
|
|
|
(*handle)->ep_bulk_out = ccid_table[idx].ep_bulk_out;
|
|
|
|
|
(*handle)->ep_bulk_in = ccid_table[idx].ep_bulk_in;
|
|
|
|
|
(*handle)->ep_intr = ccid_table[idx].ep_intr;
|
|
|
|
|
|
|
|
|
|
DEBUGOUT_2 ("using CCID reader %d (ID=%s)\n", idx, rid);
|
|
|
|
|
|
|
|
|
|
if (parse_ccid_descriptor (*handle, desc.bcdDevice,
|
|
|
|
|
ccid_table[idx].ifcdesc_extra,
|
|
|
|
|
ccid_table[idx].ifcdesc_extra_len))
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT ("device not supported\n");
|
|
|
|
|
rc = CCID_DRIVER_ERR_NO_READER;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rc = libusb_claim_interface (idev, ifc_no);
|
|
|
|
|
if (rc)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
|
|
|
|
|
rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
if (set_no != 0)
|
|
|
|
|
{
|
|
|
|
|
rc = libusb_set_interface_alt_setting (idev, ifc_no, set_no);
|
2016-12-29 11:31:25 +09:00
|
|
|
|
if (rc)
|
2006-02-06 16:13:20 +00:00
|
|
|
|
{
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
DEBUGOUT_1 ("usb_set_interface_alt_setting failed: %d\n", rc);
|
2006-02-06 16:13:20 +00:00
|
|
|
|
rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2004-09-30 14:34:34 +00:00
|
|
|
|
}
|
2006-02-06 16:13:20 +00:00
|
|
|
|
|
2013-01-28 11:46:40 +09:00
|
|
|
|
rc = ccid_vendor_specific_init (*handle);
|
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
leave:
|
|
|
|
|
if (rc)
|
|
|
|
|
{
|
2017-01-28 00:18:11 +09:00
|
|
|
|
--ccid_usb_thread_is_alive;
|
2004-09-30 14:34:34 +00:00
|
|
|
|
free (rid);
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
libusb_close (idev);
|
2004-09-30 14:34:34 +00:00
|
|
|
|
free (*handle);
|
|
|
|
|
*handle = NULL;
|
|
|
|
|
}
|
2015-11-09 16:15:44 +09:00
|
|
|
|
else
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
{
|
|
|
|
|
if (rdrname_p)
|
|
|
|
|
*rdrname_p = rid;
|
|
|
|
|
else
|
|
|
|
|
free (rid);
|
|
|
|
|
}
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
return rc;
|
|
|
|
|
}
|
2003-09-19 11:17:11 +00:00
|
|
|
|
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
/* Open the reader with the internal number READERNO and return a
|
|
|
|
|
pointer to be used as handle in HANDLE. Returns 0 on success. */
|
|
|
|
|
int
|
2017-01-26 16:54:40 +09:00
|
|
|
|
ccid_open_reader (const char *spec_reader_name, int idx,
|
|
|
|
|
struct ccid_dev_table *ccid_table,
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
ccid_driver_t *handle, char **rdrname_p)
|
|
|
|
|
{
|
|
|
|
|
*handle = calloc (1, sizeof **handle);
|
|
|
|
|
if (!*handle)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT ("out of memory\n");
|
|
|
|
|
return CCID_DRIVER_ERR_OUT_OF_CORE;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-10 12:25:06 +09:00
|
|
|
|
return ccid_open_usb_reader (spec_reader_name, idx, ccid_table,
|
|
|
|
|
handle, rdrname_p);
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
}
|
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
|
2017-01-27 18:01:52 +09:00
|
|
|
|
int
|
|
|
|
|
ccid_require_get_status (ccid_driver_t handle)
|
|
|
|
|
{
|
2017-01-31 12:56:11 +09:00
|
|
|
|
/* When a card reader supports interrupt transfer to check the
|
|
|
|
|
status of card, it is possible to submit only an interrupt
|
|
|
|
|
transfer, and no check is required by application layer. USB can
|
|
|
|
|
detect removal of a card and can detect removal of a reader.
|
|
|
|
|
*/
|
2017-01-27 18:01:52 +09:00
|
|
|
|
if (handle->ep_intr >= 0)
|
|
|
|
|
return 0;
|
|
|
|
|
|
2017-01-31 12:56:11 +09:00
|
|
|
|
/* Libusb actually detects the removal of USB device in use.
|
|
|
|
|
However, there is no good API to handle the removal (yet),
|
|
|
|
|
cleanly and with good portability.
|
|
|
|
|
|
|
|
|
|
There is libusb_set_pollfd_notifiers function, but it doesn't
|
|
|
|
|
offer libusb_device_handle* data to its callback. So, when it
|
|
|
|
|
watches multiple devices, there is no way to know which device is
|
|
|
|
|
removed.
|
2017-01-27 18:01:52 +09:00
|
|
|
|
|
2017-01-31 12:56:11 +09:00
|
|
|
|
Once, we will have a good programming interface of libusb, we can
|
|
|
|
|
list tokens (with no interrupt transfer support, but always with
|
|
|
|
|
card inserted) here to return 0, so that scdaemon can submit
|
|
|
|
|
minimum packet on wire.
|
|
|
|
|
*/
|
2017-01-27 18:01:52 +09:00
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
static void
|
|
|
|
|
do_close_reader (ccid_driver_t handle)
|
|
|
|
|
{
|
|
|
|
|
int rc;
|
|
|
|
|
unsigned char msg[100];
|
|
|
|
|
size_t msglen;
|
|
|
|
|
unsigned char seqno;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
if (!handle->powered_off)
|
|
|
|
|
{
|
|
|
|
|
msg[0] = PC_to_RDR_IccPowerOff;
|
|
|
|
|
msg[5] = 0; /* slot */
|
|
|
|
|
msg[6] = seqno = handle->seqno++;
|
|
|
|
|
msg[7] = 0; /* RFU */
|
|
|
|
|
msg[8] = 0; /* RFU */
|
|
|
|
|
msg[9] = 0; /* RFU */
|
|
|
|
|
set_msg_len (msg, 0);
|
|
|
|
|
msglen = 10;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2009-02-24 20:41:44 +00:00
|
|
|
|
rc = bulk_out (handle, msg, msglen, 0);
|
2004-09-30 14:34:34 +00:00
|
|
|
|
if (!rc)
|
2005-04-11 16:20:10 +00:00
|
|
|
|
bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus,
|
2005-05-23 14:17:22 +00:00
|
|
|
|
seqno, 2000, 0);
|
2004-09-30 14:34:34 +00:00
|
|
|
|
}
|
2017-04-10 12:25:06 +09:00
|
|
|
|
|
|
|
|
|
if (handle->transfer)
|
2004-09-30 14:34:34 +00:00
|
|
|
|
{
|
2017-04-10 12:25:06 +09:00
|
|
|
|
if (!handle->powered_off)
|
2017-01-27 20:43:29 +09:00
|
|
|
|
{
|
2017-04-10 12:25:06 +09:00
|
|
|
|
DEBUGOUT ("libusb_cancel_transfer\n");
|
2017-01-28 00:18:11 +09:00
|
|
|
|
|
2017-04-10 12:25:06 +09:00
|
|
|
|
rc = libusb_cancel_transfer (handle->transfer);
|
|
|
|
|
if (rc != LIBUSB_ERROR_NOT_FOUND)
|
|
|
|
|
while (!handle->powered_off)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT ("libusb_handle_events_completed\n");
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
2017-04-10 12:25:06 +09:00
|
|
|
|
npth_unprotect ();
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#endif
|
2017-04-10 12:25:06 +09:00
|
|
|
|
libusb_handle_events_completed (NULL, &handle->powered_off);
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
2017-04-10 12:25:06 +09:00
|
|
|
|
npth_protect ();
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#endif
|
2017-04-10 12:25:06 +09:00
|
|
|
|
}
|
2017-01-27 20:43:29 +09:00
|
|
|
|
}
|
2017-04-10 12:25:06 +09:00
|
|
|
|
|
|
|
|
|
libusb_free_transfer (handle->transfer);
|
2017-12-27 17:20:03 +09:00
|
|
|
|
handle->transfer = NULL;
|
2006-02-06 16:13:20 +00:00
|
|
|
|
}
|
2017-04-10 12:25:06 +09:00
|
|
|
|
libusb_release_interface (handle->idev, handle->ifc_no);
|
|
|
|
|
--ccid_usb_thread_is_alive;
|
|
|
|
|
libusb_close (handle->idev);
|
|
|
|
|
handle->idev = NULL;
|
2004-09-30 14:34:34 +00:00
|
|
|
|
}
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
|
|
|
|
|
2011-02-04 12:57:53 +01:00
|
|
|
|
int
|
|
|
|
|
ccid_set_progress_cb (ccid_driver_t handle,
|
2009-07-13 09:59:22 +00:00
|
|
|
|
void (*cb)(void *, const char *, int, int, int),
|
|
|
|
|
void *cb_arg)
|
|
|
|
|
{
|
scd: Support multiple readers by CCID driver.
* scd/apdu.c (new_reader_slot): Lock is now in apdu_dev_list_start.
(close_pcsc_reader_direct, close_ccid_reader): RDRNAME is handled...
(apdu_close_reader): ... by this function now.
(apdu_prepare_exit): Likewise.
(open_ccid_reader): Open with dev_list.
(apdu_dev_list_start, apdu_dev_list_finish): New.
(apdu_open_one_reader): New.
(apdu_open_reader): Support multiple readers.
* scd/app.c (select_application): With SCAN, opening all readers
available, and register as new APP.
(app_write_learn_status): app->ref_count == 0 is valid for APP which is
not yet used.
(app_list_start, app_list_finish): New.
* scd/ccid-driver.c (struct ccid_driver_s): Remove RID and BCD_DEVICE.
Add BAI.
(parse_ccid_descriptor): BCD_DEVICE is now on the arguments.
(ccid_dev_scan, ccid_dev_scan_finish): New.
(ccid_get_BAI, ccid_compare_BAI, ccid_open_usb_reader): New.
(ccid_open_reader): Support multiple readers.
(ccid_set_progress_cb, ccid_close_reader): No RID any more.
--
With this change, multiple readers/tokens are supported by the internal
CCID driver of GnuPG. Until the changes of upper layers (scdaemon,
gpg-agent, and gpg front end), only a single reader is used, though.
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
2017-01-06 09:14:13 +09:00
|
|
|
|
if (!handle)
|
2009-07-13 09:59:22 +00:00
|
|
|
|
return CCID_DRIVER_ERR_INV_VALUE;
|
|
|
|
|
|
|
|
|
|
handle->progress_cb = cb;
|
|
|
|
|
handle->progress_cb_arg = cb_arg;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-10-11 15:41:49 +09:00
|
|
|
|
int
|
|
|
|
|
ccid_set_prompt_cb (ccid_driver_t handle,
|
|
|
|
|
void (*cb)(void *, int), void *cb_arg)
|
|
|
|
|
{
|
|
|
|
|
if (!handle)
|
|
|
|
|
return CCID_DRIVER_ERR_INV_VALUE;
|
|
|
|
|
|
|
|
|
|
handle->prompt_cb = cb;
|
|
|
|
|
handle->prompt_cb_arg = cb_arg;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-10-21 17:12:50 +00:00
|
|
|
|
/* Close the reader HANDLE. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
int
|
2003-10-21 17:12:50 +00:00
|
|
|
|
ccid_close_reader (ccid_driver_t handle)
|
2003-09-02 19:06:34 +00:00
|
|
|
|
{
|
2017-04-10 12:25:06 +09:00
|
|
|
|
if (!handle)
|
2003-10-21 17:12:50 +00:00
|
|
|
|
return 0;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
do_close_reader (handle);
|
2003-10-21 17:12:50 +00:00
|
|
|
|
free (handle);
|
|
|
|
|
return 0;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-10-21 17:12:50 +00:00
|
|
|
|
/* Return False if a card is present and powered. */
|
|
|
|
|
int
|
|
|
|
|
ccid_check_card_presence (ccid_driver_t handle)
|
2003-09-02 19:06:34 +00:00
|
|
|
|
{
|
2008-10-20 13:53:23 +00:00
|
|
|
|
(void)handle; /* Not yet implemented. */
|
2003-10-21 17:12:50 +00:00
|
|
|
|
return -1;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Write a MSG of length MSGLEN to the designated bulk out endpoint.
|
|
|
|
|
Returns 0 on success. */
|
|
|
|
|
static int
|
2009-02-24 20:41:44 +00:00
|
|
|
|
bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen,
|
|
|
|
|
int no_debug)
|
2003-09-02 19:06:34 +00:00
|
|
|
|
{
|
|
|
|
|
int rc;
|
2017-04-10 12:25:06 +09:00
|
|
|
|
int transferred;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
2009-07-21 14:21:05 +00:00
|
|
|
|
/* No need to continue and clutter the log with USB write error
|
|
|
|
|
messages after we got the first ENODEV. */
|
2009-07-16 15:54:59 +00:00
|
|
|
|
if (handle->enodev_seen)
|
|
|
|
|
return CCID_DRIVER_ERR_NO_READER;
|
|
|
|
|
|
2009-02-24 20:41:44 +00:00
|
|
|
|
if (debug_level && (!no_debug || debug_level >= 3))
|
|
|
|
|
{
|
|
|
|
|
switch (msglen? msg[0]:0)
|
|
|
|
|
{
|
|
|
|
|
case PC_to_RDR_IccPowerOn:
|
|
|
|
|
print_p2r_iccpoweron (msg, msglen);
|
|
|
|
|
break;
|
|
|
|
|
case PC_to_RDR_IccPowerOff:
|
|
|
|
|
print_p2r_iccpoweroff (msg, msglen);
|
|
|
|
|
break;
|
2016-12-27 11:58:54 +09:00
|
|
|
|
case PC_to_RDR_GetSlotStatus:
|
2009-02-24 20:41:44 +00:00
|
|
|
|
print_p2r_getslotstatus (msg, msglen);
|
|
|
|
|
break;
|
|
|
|
|
case PC_to_RDR_XfrBlock:
|
|
|
|
|
print_p2r_xfrblock (msg, msglen);
|
|
|
|
|
break;
|
2016-12-27 11:58:54 +09:00
|
|
|
|
case PC_to_RDR_GetParameters:
|
2009-02-24 20:41:44 +00:00
|
|
|
|
print_p2r_getparameters (msg, msglen);
|
|
|
|
|
break;
|
|
|
|
|
case PC_to_RDR_ResetParameters:
|
|
|
|
|
print_p2r_resetparameters (msg, msglen);
|
|
|
|
|
break;
|
2016-12-27 11:58:54 +09:00
|
|
|
|
case PC_to_RDR_SetParameters:
|
2009-02-24 20:41:44 +00:00
|
|
|
|
print_p2r_setparameters (msg, msglen);
|
|
|
|
|
break;
|
|
|
|
|
case PC_to_RDR_Escape:
|
|
|
|
|
print_p2r_escape (msg, msglen);
|
|
|
|
|
break;
|
|
|
|
|
case PC_to_RDR_IccClock:
|
|
|
|
|
print_p2r_iccclock (msg, msglen);
|
|
|
|
|
break;
|
2016-12-27 11:58:54 +09:00
|
|
|
|
case PC_to_RDR_T0APDU:
|
2009-02-24 20:41:44 +00:00
|
|
|
|
print_p2r_to0apdu (msg, msglen);
|
|
|
|
|
break;
|
|
|
|
|
case PC_to_RDR_Secure:
|
|
|
|
|
print_p2r_secure (msg, msglen);
|
|
|
|
|
break;
|
|
|
|
|
case PC_to_RDR_Mechanical:
|
|
|
|
|
print_p2r_mechanical (msg, msglen);
|
|
|
|
|
break;
|
|
|
|
|
case PC_to_RDR_Abort:
|
|
|
|
|
print_p2r_abort (msg, msglen);
|
|
|
|
|
break;
|
|
|
|
|
case PC_to_RDR_SetDataRate:
|
|
|
|
|
print_p2r_setdatarate (msg, msglen);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
print_p2r_unknown (msg, msglen);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
2017-04-10 12:25:06 +09:00
|
|
|
|
npth_unprotect ();
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#endif
|
2017-04-10 12:25:06 +09:00
|
|
|
|
rc = libusb_bulk_transfer (handle->idev, handle->ep_bulk_out,
|
|
|
|
|
(char*)msg, msglen, &transferred,
|
|
|
|
|
5000 /* ms timeout */);
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
2017-04-10 12:25:06 +09:00
|
|
|
|
npth_protect ();
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#endif
|
2017-04-10 12:25:06 +09:00
|
|
|
|
if (rc == 0 && transferred == msglen)
|
|
|
|
|
return 0;
|
2009-07-16 15:54:59 +00:00
|
|
|
|
|
2017-04-10 12:25:06 +09:00
|
|
|
|
if (rc)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 ("usb_bulk_write error: %s\n", libusb_error_name (rc));
|
|
|
|
|
if (rc == LIBUSB_ERROR_NO_DEVICE)
|
2009-07-16 15:54:59 +00:00
|
|
|
|
{
|
2017-04-10 12:25:06 +09:00
|
|
|
|
handle->enodev_seen = 1;
|
|
|
|
|
return CCID_DRIVER_ERR_NO_READER;
|
2009-07-16 15:54:59 +00:00
|
|
|
|
}
|
2006-02-06 16:13:20 +00:00
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2017-04-10 12:25:06 +09:00
|
|
|
|
return 0;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Read a maximum of LENGTH bytes from the bulk in endpoint into
|
2003-09-05 07:40:41 +00:00
|
|
|
|
BUFFER and return the actual read number if bytes in NREAD. SEQNO
|
|
|
|
|
is the sequence number used to send the request and EXPECTED_TYPE
|
|
|
|
|
the type of message we expect. Does checks on the ccid
|
2005-05-23 14:17:22 +00:00
|
|
|
|
header. TIMEOUT is the timeout value in ms. NO_DEBUG may be set to
|
2009-02-24 20:41:44 +00:00
|
|
|
|
avoid debug messages in case of no error; this can be overriden
|
|
|
|
|
with a glibal debug level of at least 3. Returns 0 on success. */
|
2003-09-02 19:06:34 +00:00
|
|
|
|
static int
|
|
|
|
|
bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
|
2005-05-23 14:17:22 +00:00
|
|
|
|
size_t *nread, int expected_type, int seqno, int timeout,
|
|
|
|
|
int no_debug)
|
2003-09-02 19:06:34 +00:00
|
|
|
|
{
|
2009-02-24 20:41:44 +00:00
|
|
|
|
int rc;
|
2016-01-27 14:31:13 +01:00
|
|
|
|
int msglen;
|
2018-10-11 15:41:49 +09:00
|
|
|
|
int notified = 0;
|
2019-07-25 09:16:46 +09:00
|
|
|
|
int bwi = 1;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
/* Fixme: The next line for the current Valgrind without support
|
|
|
|
|
for USB IOCTLs. */
|
|
|
|
|
memset (buffer, 0, length);
|
2004-04-20 14:17:10 +00:00
|
|
|
|
retry:
|
2017-04-10 12:25:06 +09:00
|
|
|
|
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
2017-04-10 12:25:06 +09:00
|
|
|
|
npth_unprotect ();
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#endif
|
2017-04-10 12:25:06 +09:00
|
|
|
|
rc = libusb_bulk_transfer (handle->idev, handle->ep_bulk_in,
|
2019-07-25 09:16:46 +09:00
|
|
|
|
buffer, length, &msglen, bwi*timeout);
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
2017-04-10 12:25:06 +09:00
|
|
|
|
npth_protect ();
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#endif
|
2017-04-10 12:25:06 +09:00
|
|
|
|
if (rc)
|
2006-02-06 16:13:20 +00:00
|
|
|
|
{
|
2017-04-10 12:25:06 +09:00
|
|
|
|
DEBUGOUT_1 ("usb_bulk_read error: %s\n", libusb_error_name (rc));
|
|
|
|
|
if (rc == LIBUSB_ERROR_NO_DEVICE)
|
2006-02-06 16:13:20 +00:00
|
|
|
|
{
|
2017-04-10 12:25:06 +09:00
|
|
|
|
handle->enodev_seen = 1;
|
|
|
|
|
return CCID_DRIVER_ERR_NO_READER;
|
2006-02-06 16:13:20 +00:00
|
|
|
|
}
|
2017-04-10 12:25:06 +09:00
|
|
|
|
|
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
}
|
2017-04-10 12:25:06 +09:00
|
|
|
|
if (msglen < 0)
|
|
|
|
|
return CCID_DRIVER_ERR_INV_VALUE; /* Faulty libusb. */
|
|
|
|
|
*nread = msglen;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
|
|
|
|
|
if (msglen < 10)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen);
|
2009-05-13 17:12:00 +00:00
|
|
|
|
abort_cmd (handle, seqno);
|
2004-07-16 15:45:25 +00:00
|
|
|
|
return CCID_DRIVER_ERR_INV_VALUE;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
if (buffer[5] != 0)
|
2003-09-05 07:40:41 +00:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]);
|
2004-07-16 15:45:25 +00:00
|
|
|
|
return CCID_DRIVER_ERR_INV_VALUE;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
if (buffer[6] != seqno)
|
2003-09-05 07:40:41 +00:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n",
|
|
|
|
|
seqno, buffer[6]);
|
2009-05-13 17:12:00 +00:00
|
|
|
|
/* Retry until we are synced again. */
|
|
|
|
|
goto retry;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
2007-03-07 20:55:14 +00:00
|
|
|
|
/* We need to handle the time extension request before we check that
|
2009-02-24 20:41:44 +00:00
|
|
|
|
we got the expected message type. This is in particular required
|
2007-03-07 20:55:14 +00:00
|
|
|
|
for the Cherry keyboard which sends a time extension request for
|
|
|
|
|
each key hit. */
|
2018-10-11 15:41:49 +09:00
|
|
|
|
if (!(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80)
|
2011-02-04 12:57:53 +01:00
|
|
|
|
{
|
2004-04-20 14:17:10 +00:00
|
|
|
|
/* Card present and active, time extension requested. */
|
|
|
|
|
DEBUGOUT_2 ("time extension requested (%02X,%02X)\n",
|
|
|
|
|
buffer[7], buffer[8]);
|
2018-10-11 15:41:49 +09:00
|
|
|
|
|
2019-07-25 09:16:46 +09:00
|
|
|
|
bwi = 1;
|
|
|
|
|
if (buffer[8] != 0 && buffer[8] != 0xff)
|
|
|
|
|
bwi = buffer[8];
|
|
|
|
|
|
2018-10-11 15:41:49 +09:00
|
|
|
|
/* Gnuk enhancement to prompt user input by ack button */
|
|
|
|
|
if (buffer[8] == 0xff && !notified)
|
|
|
|
|
{
|
|
|
|
|
notified = 1;
|
|
|
|
|
handle->prompt_cb (handle->prompt_cb_arg, 1);
|
|
|
|
|
}
|
|
|
|
|
|
2004-04-20 14:17:10 +00:00
|
|
|
|
goto retry;
|
|
|
|
|
}
|
2003-09-05 07:40:41 +00:00
|
|
|
|
|
2018-10-11 15:41:49 +09:00
|
|
|
|
if (notified)
|
|
|
|
|
handle->prompt_cb (handle->prompt_cb_arg, 0);
|
|
|
|
|
|
2017-04-12 11:21:08 +09:00
|
|
|
|
if (buffer[0] != expected_type && buffer[0] != RDR_to_PC_SlotStatus)
|
2007-03-07 20:55:14 +00:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]);
|
2009-05-13 17:12:00 +00:00
|
|
|
|
abort_cmd (handle, seqno);
|
2007-03-07 20:55:14 +00:00
|
|
|
|
return CCID_DRIVER_ERR_INV_VALUE;
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-24 20:41:44 +00:00
|
|
|
|
if (debug_level && (!no_debug || debug_level >= 3))
|
2005-05-23 14:17:22 +00:00
|
|
|
|
{
|
2009-02-24 20:41:44 +00:00
|
|
|
|
switch (buffer[0])
|
|
|
|
|
{
|
|
|
|
|
case RDR_to_PC_DataBlock:
|
|
|
|
|
print_r2p_datablock (buffer, msglen);
|
|
|
|
|
break;
|
|
|
|
|
case RDR_to_PC_SlotStatus:
|
|
|
|
|
print_r2p_slotstatus (buffer, msglen);
|
|
|
|
|
break;
|
|
|
|
|
case RDR_to_PC_Parameters:
|
|
|
|
|
print_r2p_parameters (buffer, msglen);
|
|
|
|
|
break;
|
|
|
|
|
case RDR_to_PC_Escape:
|
|
|
|
|
print_r2p_escape (buffer, msglen);
|
|
|
|
|
break;
|
|
|
|
|
case RDR_to_PC_DataRate:
|
|
|
|
|
print_r2p_datarate (buffer, msglen);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
print_r2p_unknown (buffer, msglen);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2005-05-23 14:17:22 +00:00
|
|
|
|
}
|
|
|
|
|
if (CCID_COMMAND_FAILED (buffer))
|
|
|
|
|
print_command_failed (buffer);
|
|
|
|
|
|
2005-11-28 11:52:25 +00:00
|
|
|
|
/* Check whether a card is at all available. Note: If you add new
|
|
|
|
|
error codes here, check whether they need to be ignored in
|
|
|
|
|
send_escape_cmd. */
|
2004-07-16 15:45:25 +00:00
|
|
|
|
switch ((buffer[7] & 0x03))
|
|
|
|
|
{
|
|
|
|
|
case 0: /* no error */ break;
|
2017-04-12 11:21:08 +09:00
|
|
|
|
case 1: rc = CCID_DRIVER_ERR_CARD_INACTIVE; break;
|
|
|
|
|
case 2: rc = CCID_DRIVER_ERR_NO_CARD; break;
|
2004-07-16 15:45:25 +00:00
|
|
|
|
case 3: /* RFU */ break;
|
|
|
|
|
}
|
2017-04-12 11:21:08 +09:00
|
|
|
|
|
|
|
|
|
if (rc)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Communication failure by device side.
|
|
|
|
|
* Possibly, it was forcibly suspended and resumed.
|
2017-12-27 17:20:03 +09:00
|
|
|
|
*
|
|
|
|
|
* Only detect this kind of failure when interrupt transfer is
|
|
|
|
|
* not supported. For card reader with interrupt transfer
|
|
|
|
|
* support removal is detected by intr_cb.
|
2017-04-12 11:21:08 +09:00
|
|
|
|
*/
|
2017-12-27 17:20:03 +09:00
|
|
|
|
if (handle->ep_intr < 0)
|
2017-07-21 13:26:53 +09:00
|
|
|
|
{
|
2017-12-27 17:20:03 +09:00
|
|
|
|
DEBUGOUT ("CCID: card inactive/removed\n");
|
2017-07-21 13:26:53 +09:00
|
|
|
|
handle->powered_off = 1;
|
|
|
|
|
scd_kick_the_loop ();
|
|
|
|
|
}
|
2017-04-12 11:21:08 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rc;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-12-18 16:34:28 +00:00
|
|
|
|
|
|
|
|
|
/* Send an abort sequence and wait until everything settled. */
|
|
|
|
|
static int
|
2009-05-13 17:12:00 +00:00
|
|
|
|
abort_cmd (ccid_driver_t handle, int seqno)
|
2008-12-18 16:34:28 +00:00
|
|
|
|
{
|
|
|
|
|
int rc;
|
|
|
|
|
char dummybuf[8];
|
|
|
|
|
unsigned char msg[100];
|
2016-01-27 14:31:13 +01:00
|
|
|
|
int msglen;
|
2008-12-18 16:34:28 +00:00
|
|
|
|
|
2009-05-13 17:12:00 +00:00
|
|
|
|
seqno &= 0xff;
|
|
|
|
|
DEBUGOUT_1 ("sending abort sequence for seqno %d\n", seqno);
|
2008-12-18 16:34:28 +00:00
|
|
|
|
/* Send the abort command to the control pipe. Note that we don't
|
|
|
|
|
need to keep track of sent abort commands because there should
|
|
|
|
|
never be another thread using the same slot concurrently. */
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
|
|
|
|
npth_unprotect ();
|
|
|
|
|
#endif
|
2016-01-27 12:24:05 +09:00
|
|
|
|
rc = libusb_control_transfer (handle->idev,
|
|
|
|
|
0x21,/* bmRequestType: host-to-device,
|
|
|
|
|
class specific, to interface. */
|
|
|
|
|
1, /* ABORT */
|
|
|
|
|
(seqno << 8 | 0 /* slot */),
|
|
|
|
|
handle->ifc_no,
|
|
|
|
|
dummybuf, 0,
|
|
|
|
|
1000 /* ms timeout */);
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
|
|
|
|
npth_protect ();
|
|
|
|
|
#endif
|
2016-12-29 11:31:25 +09:00
|
|
|
|
if (rc)
|
2008-12-18 16:34:28 +00:00
|
|
|
|
{
|
2016-01-27 12:24:05 +09:00
|
|
|
|
DEBUGOUT_1 ("usb_control_msg error: %s\n", libusb_error_name (rc));
|
2008-12-18 16:34:28 +00:00
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now send the abort command to the bulk out pipe using the same
|
2009-05-08 15:07:45 +00:00
|
|
|
|
SEQNO and SLOT. Do this in a loop to so that all seqno are
|
|
|
|
|
tried. */
|
|
|
|
|
seqno--; /* Adjust for next increment. */
|
2008-12-18 16:34:28 +00:00
|
|
|
|
do
|
|
|
|
|
{
|
2016-01-27 12:24:05 +09:00
|
|
|
|
int transferred;
|
|
|
|
|
|
2011-02-04 12:57:53 +01:00
|
|
|
|
seqno++;
|
2009-05-08 15:07:45 +00:00
|
|
|
|
msg[0] = PC_to_RDR_Abort;
|
|
|
|
|
msg[5] = 0; /* slot */
|
|
|
|
|
msg[6] = seqno;
|
|
|
|
|
msg[7] = 0; /* RFU */
|
|
|
|
|
msg[8] = 0; /* RFU */
|
|
|
|
|
msg[9] = 0; /* RFU */
|
|
|
|
|
msglen = 10;
|
|
|
|
|
set_msg_len (msg, 0);
|
|
|
|
|
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
|
|
|
|
npth_unprotect ();
|
|
|
|
|
#endif
|
2016-01-27 12:24:05 +09:00
|
|
|
|
rc = libusb_bulk_transfer (handle->idev, handle->ep_bulk_out,
|
|
|
|
|
(char*)msg, msglen, &transferred,
|
|
|
|
|
5000 /* ms timeout */);
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
|
|
|
|
npth_protect ();
|
|
|
|
|
#endif
|
2016-01-27 12:24:05 +09:00
|
|
|
|
if (rc == 0 && transferred == msglen)
|
2009-05-08 15:07:45 +00:00
|
|
|
|
rc = 0;
|
2016-12-29 11:31:25 +09:00
|
|
|
|
else if (rc)
|
2011-02-04 12:57:53 +01:00
|
|
|
|
DEBUGOUT_1 ("usb_bulk_write error in abort_cmd: %s\n",
|
2016-01-27 12:24:05 +09:00
|
|
|
|
libusb_error_name (rc));
|
2009-05-08 15:07:45 +00:00
|
|
|
|
|
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
|
|
|
|
npth_unprotect ();
|
|
|
|
|
#endif
|
2016-01-27 12:24:05 +09:00
|
|
|
|
rc = libusb_bulk_transfer (handle->idev, handle->ep_bulk_in,
|
|
|
|
|
(char*)msg, sizeof msg, &msglen,
|
|
|
|
|
5000 /*ms timeout*/);
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
|
|
|
|
npth_protect ();
|
|
|
|
|
#endif
|
2016-12-29 11:31:25 +09:00
|
|
|
|
if (rc)
|
2008-12-18 16:34:28 +00:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 ("usb_bulk_read error in abort_cmd: %s\n",
|
2016-01-27 12:24:05 +09:00
|
|
|
|
libusb_error_name (rc));
|
2008-12-18 16:34:28 +00:00
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (msglen < 10)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 ("bulk-in msg in abort_cmd too short (%u)\n",
|
|
|
|
|
(unsigned int)msglen);
|
|
|
|
|
return CCID_DRIVER_ERR_INV_VALUE;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
if (msg[5] != 0)
|
2008-12-18 16:34:28 +00:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 ("unexpected bulk-in slot (%d) in abort_cmd\n", msg[5]);
|
|
|
|
|
return CCID_DRIVER_ERR_INV_VALUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DEBUGOUT_3 ("status: %02X error: %02X octet[9]: %02X\n",
|
|
|
|
|
msg[7], msg[8], msg[9]);
|
|
|
|
|
if (CCID_COMMAND_FAILED (msg))
|
|
|
|
|
print_command_failed (msg);
|
|
|
|
|
}
|
|
|
|
|
while (msg[0] != RDR_to_PC_SlotStatus && msg[5] != 0 && msg[6] != seqno);
|
|
|
|
|
|
2009-05-13 17:12:00 +00:00
|
|
|
|
handle->seqno = ((seqno + 1) & 0xff);
|
2008-12-18 16:34:28 +00:00
|
|
|
|
DEBUGOUT ("sending abort sequence succeeded\n");
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-04-11 16:20:10 +00:00
|
|
|
|
/* Note that this function won't return the error codes NO_CARD or
|
2005-11-28 11:52:25 +00:00
|
|
|
|
CARD_INACTIVE. IF RESULT is not NULL, the result from the
|
|
|
|
|
operation will get returned in RESULT and its length in RESULTLEN.
|
|
|
|
|
If the response is larger than RESULTMAX, an error is returned and
|
|
|
|
|
the required buffer length returned in RESULTLEN. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
static int
|
2004-10-06 13:13:51 +00:00
|
|
|
|
send_escape_cmd (ccid_driver_t handle,
|
2005-11-28 11:52:25 +00:00
|
|
|
|
const unsigned char *data, size_t datalen,
|
|
|
|
|
unsigned char *result, size_t resultmax, size_t *resultlen)
|
2004-10-06 13:13:51 +00:00
|
|
|
|
{
|
2009-02-24 20:41:44 +00:00
|
|
|
|
int rc;
|
2004-10-06 13:13:51 +00:00
|
|
|
|
unsigned char msg[100];
|
|
|
|
|
size_t msglen;
|
|
|
|
|
unsigned char seqno;
|
|
|
|
|
|
2005-11-28 11:52:25 +00:00
|
|
|
|
if (resultlen)
|
|
|
|
|
*resultlen = 0;
|
|
|
|
|
|
2004-10-06 13:13:51 +00:00
|
|
|
|
if (datalen > sizeof msg - 10)
|
|
|
|
|
return CCID_DRIVER_ERR_INV_VALUE; /* Escape data too large. */
|
|
|
|
|
|
|
|
|
|
msg[0] = PC_to_RDR_Escape;
|
|
|
|
|
msg[5] = 0; /* slot */
|
|
|
|
|
msg[6] = seqno = handle->seqno++;
|
|
|
|
|
msg[7] = 0; /* RFU */
|
|
|
|
|
msg[8] = 0; /* RFU */
|
|
|
|
|
msg[9] = 0; /* RFU */
|
|
|
|
|
memcpy (msg+10, data, datalen);
|
|
|
|
|
msglen = 10 + datalen;
|
|
|
|
|
set_msg_len (msg, datalen);
|
|
|
|
|
|
2009-02-24 20:41:44 +00:00
|
|
|
|
rc = bulk_out (handle, msg, msglen, 0);
|
2004-10-06 13:13:51 +00:00
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
2005-04-11 16:20:10 +00:00
|
|
|
|
rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Escape,
|
2005-05-23 14:17:22 +00:00
|
|
|
|
seqno, 5000, 0);
|
2005-11-28 11:52:25 +00:00
|
|
|
|
if (result)
|
|
|
|
|
switch (rc)
|
|
|
|
|
{
|
|
|
|
|
/* We need to ignore certain errorcode here. */
|
|
|
|
|
case 0:
|
|
|
|
|
case CCID_DRIVER_ERR_CARD_INACTIVE:
|
|
|
|
|
case CCID_DRIVER_ERR_NO_CARD:
|
|
|
|
|
{
|
|
|
|
|
if (msglen > resultmax)
|
|
|
|
|
rc = CCID_DRIVER_ERR_INV_VALUE; /* Response too large. */
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
memcpy (result, msg, msglen);
|
2017-04-07 13:30:35 +09:00
|
|
|
|
if (resultlen)
|
|
|
|
|
*resultlen = msglen;
|
2015-01-24 03:03:33 +11:00
|
|
|
|
rc = 0;
|
2005-11-28 11:52:25 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2004-10-06 13:13:51 +00:00
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-11-28 11:52:25 +00:00
|
|
|
|
int
|
|
|
|
|
ccid_transceive_escape (ccid_driver_t handle,
|
|
|
|
|
const unsigned char *data, size_t datalen,
|
|
|
|
|
unsigned char *resp, size_t maxresplen, size_t *nresp)
|
|
|
|
|
{
|
|
|
|
|
return send_escape_cmd (handle, data, datalen, resp, maxresplen, nresp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-09-02 19:06:34 +00:00
|
|
|
|
/* experimental */
|
|
|
|
|
int
|
|
|
|
|
ccid_poll (ccid_driver_t handle)
|
|
|
|
|
{
|
|
|
|
|
int rc;
|
|
|
|
|
unsigned char msg[10];
|
2016-01-27 14:31:13 +01:00
|
|
|
|
int msglen;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
int i, j;
|
|
|
|
|
|
2017-04-10 12:25:06 +09:00
|
|
|
|
rc = libusb_interrupt_transfer (handle->idev, handle->ep_intr,
|
|
|
|
|
(char*)msg, sizeof msg, &msglen,
|
|
|
|
|
0 /* ms timeout */ );
|
|
|
|
|
if (rc == LIBUSB_ERROR_TIMEOUT)
|
2003-09-02 19:06:34 +00:00
|
|
|
|
return 0;
|
|
|
|
|
|
2016-12-29 11:31:25 +09:00
|
|
|
|
if (rc)
|
2003-09-02 19:06:34 +00:00
|
|
|
|
{
|
2016-01-27 12:24:05 +09:00
|
|
|
|
DEBUGOUT_1 ("usb_intr_read error: %s\n", libusb_error_name (rc));
|
2004-07-16 15:45:25 +00:00
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (msglen < 1)
|
|
|
|
|
{
|
2003-09-05 07:40:41 +00:00
|
|
|
|
DEBUGOUT ("intr-in msg too short\n");
|
2004-07-16 15:45:25 +00:00
|
|
|
|
return CCID_DRIVER_ERR_INV_VALUE;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (msg[0] == RDR_to_PC_NotifySlotChange)
|
|
|
|
|
{
|
2003-09-05 07:40:41 +00:00
|
|
|
|
DEBUGOUT ("notify slot change:");
|
2003-09-02 19:06:34 +00:00
|
|
|
|
for (i=1; i < msglen; i++)
|
|
|
|
|
for (j=0; j < 4; j++)
|
2003-09-05 07:40:41 +00:00
|
|
|
|
DEBUGOUT_CONT_3 (" %d:%c%c",
|
2011-02-04 12:57:53 +01:00
|
|
|
|
(i-1)*4+j,
|
2003-09-05 07:40:41 +00:00
|
|
|
|
(msg[i] & (1<<(j*2)))? 'p':'-',
|
|
|
|
|
(msg[i] & (2<<(j*2)))? '*':' ');
|
|
|
|
|
DEBUGOUT_LF ();
|
2003-09-02 19:06:34 +00:00
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
else if (msg[0] == RDR_to_PC_HardwareError)
|
2003-09-02 19:06:34 +00:00
|
|
|
|
{
|
2015-11-16 12:41:46 +01:00
|
|
|
|
DEBUGOUT ("hardware error occurred\n");
|
2003-09-02 19:06:34 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2003-09-05 07:40:41 +00:00
|
|
|
|
DEBUGOUT_1 ("unknown intr-in msg of type %02X\n", msg[0]);
|
2003-09-02 19:06:34 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-05-13 17:12:00 +00:00
|
|
|
|
/* Note that this function won't return the error codes NO_CARD or
|
2004-07-16 15:45:25 +00:00
|
|
|
|
CARD_INACTIVE */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
int
|
2017-01-30 09:30:32 +09:00
|
|
|
|
ccid_slot_status (ccid_driver_t handle, int *statusbits, int on_wire)
|
2003-09-02 19:06:34 +00:00
|
|
|
|
{
|
|
|
|
|
int rc;
|
|
|
|
|
unsigned char msg[100];
|
|
|
|
|
size_t msglen;
|
|
|
|
|
unsigned char seqno;
|
2005-04-11 16:20:10 +00:00
|
|
|
|
int retries = 0;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
2017-01-26 16:54:40 +09:00
|
|
|
|
if (handle->powered_off)
|
|
|
|
|
return CCID_DRIVER_ERR_NO_READER;
|
|
|
|
|
|
2017-01-30 09:30:32 +09:00
|
|
|
|
/* If the card (with its lower-level driver) doesn't require
|
|
|
|
|
GET_STATUS on wire (because it supports INTERRUPT transfer for
|
|
|
|
|
status change, or it's a token which has a card always inserted),
|
|
|
|
|
no need to send on wire. */
|
|
|
|
|
if (!on_wire && !ccid_require_get_status (handle))
|
|
|
|
|
{
|
2017-11-21 11:52:54 +09:00
|
|
|
|
/* Setup interrupt transfer at the initial call of slot_status
|
|
|
|
|
with ON_WIRE == 0 */
|
|
|
|
|
if (handle->transfer == NULL && handle->ep_intr >= 0)
|
|
|
|
|
ccid_setup_intr (handle);
|
|
|
|
|
|
2017-01-30 09:30:32 +09:00
|
|
|
|
*statusbits = 0;
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2005-04-11 16:20:10 +00:00
|
|
|
|
retry:
|
2003-09-02 19:06:34 +00:00
|
|
|
|
msg[0] = PC_to_RDR_GetSlotStatus;
|
|
|
|
|
msg[5] = 0; /* slot */
|
|
|
|
|
msg[6] = seqno = handle->seqno++;
|
|
|
|
|
msg[7] = 0; /* RFU */
|
|
|
|
|
msg[8] = 0; /* RFU */
|
|
|
|
|
msg[9] = 0; /* RFU */
|
|
|
|
|
set_msg_len (msg, 0);
|
|
|
|
|
|
2009-02-24 20:41:44 +00:00
|
|
|
|
rc = bulk_out (handle, msg, 10, 1);
|
2003-09-02 19:06:34 +00:00
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
2005-05-23 14:17:22 +00:00
|
|
|
|
/* Note that we set the NO_DEBUG flag here, so that the logs won't
|
|
|
|
|
get cluttered up by a ticker function checking for the slot
|
|
|
|
|
status and debugging enabled. */
|
2005-04-11 16:20:10 +00:00
|
|
|
|
rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus,
|
2005-05-23 14:17:22 +00:00
|
|
|
|
seqno, retries? 1000 : 200, 1);
|
2005-04-11 16:20:10 +00:00
|
|
|
|
if (rc == CCID_DRIVER_ERR_CARD_IO_ERROR && retries < 3)
|
|
|
|
|
{
|
|
|
|
|
if (!retries)
|
|
|
|
|
{
|
2005-05-18 10:48:06 +00:00
|
|
|
|
DEBUGOUT ("USB: CALLING USB_CLEAR_HALT\n");
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
|
|
|
|
npth_unprotect ();
|
|
|
|
|
#endif
|
2016-01-27 12:24:05 +09:00
|
|
|
|
libusb_clear_halt (handle->idev, handle->ep_bulk_in);
|
|
|
|
|
libusb_clear_halt (handle->idev, handle->ep_bulk_out);
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
|
|
|
|
npth_protect ();
|
|
|
|
|
#endif
|
2005-04-11 16:20:10 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2005-05-18 10:48:06 +00:00
|
|
|
|
DEBUGOUT ("USB: RETRYING bulk_in AGAIN\n");
|
2005-04-11 16:20:10 +00:00
|
|
|
|
retries++;
|
|
|
|
|
goto retry;
|
|
|
|
|
}
|
2017-01-26 16:54:40 +09:00
|
|
|
|
if (rc && rc != CCID_DRIVER_ERR_NO_CARD && rc != CCID_DRIVER_ERR_CARD_INACTIVE)
|
2003-09-02 19:06:34 +00:00
|
|
|
|
return rc;
|
2004-04-20 14:17:10 +00:00
|
|
|
|
*statusbits = (msg[7] & 3);
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-01-08 15:22:31 +09:00
|
|
|
|
/* Parse ATR string (of ATRLEN) and update parameters at PARAM.
|
|
|
|
|
Calling this routine, it should prepare default values at PARAM
|
|
|
|
|
beforehand. This routine assumes that card is accessed by T=1
|
|
|
|
|
protocol. It doesn't analyze historical bytes at all.
|
|
|
|
|
|
|
|
|
|
Returns < 0 value on error:
|
|
|
|
|
-1 for parse error or integrity check error
|
|
|
|
|
-2 for card doesn't support T=1 protocol
|
|
|
|
|
-3 for parameters are nod explicitly defined by ATR
|
|
|
|
|
-4 for this driver doesn't support CRC
|
|
|
|
|
|
|
|
|
|
Returns >= 0 on success:
|
|
|
|
|
0 for card is negotiable mode
|
|
|
|
|
1 for card is specific mode (and not negotiable)
|
|
|
|
|
*/
|
|
|
|
|
static int
|
|
|
|
|
update_param_by_atr (unsigned char *param, unsigned char *atr, size_t atrlen)
|
|
|
|
|
{
|
|
|
|
|
int i = -1;
|
|
|
|
|
int t, y, chk;
|
|
|
|
|
int historical_bytes_num, negotiable = 1;
|
|
|
|
|
|
|
|
|
|
#define NEXTBYTE() do { i++; if (atrlen <= i) return -1; } while (0)
|
|
|
|
|
|
|
|
|
|
NEXTBYTE ();
|
|
|
|
|
|
|
|
|
|
if (atr[i] == 0x3F)
|
2013-01-10 10:49:27 +09:00
|
|
|
|
param[1] |= 0x02; /* Convention is inverse. */
|
2013-01-08 15:22:31 +09:00
|
|
|
|
NEXTBYTE ();
|
|
|
|
|
|
|
|
|
|
y = (atr[i] >> 4);
|
|
|
|
|
historical_bytes_num = atr[i] & 0x0f;
|
|
|
|
|
NEXTBYTE ();
|
|
|
|
|
|
|
|
|
|
if ((y & 1))
|
|
|
|
|
{
|
2013-01-10 10:49:27 +09:00
|
|
|
|
param[0] = atr[i]; /* TA1 - Fi & Di */
|
2013-01-08 15:22:31 +09:00
|
|
|
|
NEXTBYTE ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((y & 2))
|
2013-01-10 10:49:27 +09:00
|
|
|
|
NEXTBYTE (); /* TB1 - ignore */
|
2013-01-08 15:22:31 +09:00
|
|
|
|
|
|
|
|
|
if ((y & 4))
|
|
|
|
|
{
|
2013-01-10 10:49:27 +09:00
|
|
|
|
param[2] = atr[i]; /* TC1 - Guard Time */
|
2013-01-08 15:22:31 +09:00
|
|
|
|
NEXTBYTE ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((y & 8))
|
|
|
|
|
{
|
2013-01-10 10:49:27 +09:00
|
|
|
|
y = (atr[i] >> 4); /* TD1 */
|
2013-01-08 15:22:31 +09:00
|
|
|
|
t = atr[i] & 0x0f;
|
|
|
|
|
NEXTBYTE ();
|
|
|
|
|
|
|
|
|
|
if ((y & 1))
|
2013-01-10 10:49:27 +09:00
|
|
|
|
{ /* TA2 - PPS mode */
|
|
|
|
|
if ((atr[i] & 0x0f) != 1)
|
|
|
|
|
return -2; /* Wrong card protocol (!= 1). */
|
2013-01-08 15:22:31 +09:00
|
|
|
|
|
2013-01-10 10:49:27 +09:00
|
|
|
|
if ((atr[i] & 0x10) != 0x10)
|
|
|
|
|
return -3; /* Transmission parameters are implicitly defined. */
|
2013-01-08 15:22:31 +09:00
|
|
|
|
|
2013-01-10 10:49:27 +09:00
|
|
|
|
negotiable = 0; /* TA2 means specific mode. */
|
|
|
|
|
NEXTBYTE ();
|
|
|
|
|
}
|
2013-01-08 15:22:31 +09:00
|
|
|
|
|
|
|
|
|
if ((y & 2))
|
2013-01-10 10:49:27 +09:00
|
|
|
|
NEXTBYTE (); /* TB2 - ignore */
|
2013-01-08 15:22:31 +09:00
|
|
|
|
|
|
|
|
|
if ((y & 4))
|
2013-01-10 10:49:27 +09:00
|
|
|
|
NEXTBYTE (); /* TC2 - ignore */
|
2013-01-08 15:22:31 +09:00
|
|
|
|
|
|
|
|
|
if ((y & 8))
|
2013-01-10 10:49:27 +09:00
|
|
|
|
{
|
|
|
|
|
y = (atr[i] >> 4); /* TD2 */
|
|
|
|
|
t = atr[i] & 0x0f;
|
|
|
|
|
NEXTBYTE ();
|
|
|
|
|
}
|
2013-01-08 15:22:31 +09:00
|
|
|
|
else
|
2013-01-10 10:49:27 +09:00
|
|
|
|
y = 0;
|
2013-01-08 15:22:31 +09:00
|
|
|
|
|
|
|
|
|
while (y)
|
2013-01-10 10:49:27 +09:00
|
|
|
|
{
|
|
|
|
|
if ((y & 1))
|
|
|
|
|
{ /* TAx */
|
|
|
|
|
if (t == 1)
|
|
|
|
|
param[5] = atr[i]; /* IFSC */
|
|
|
|
|
else if (t == 15)
|
|
|
|
|
/* XXX: check voltage? */
|
|
|
|
|
param[4] = (atr[i] >> 6); /* ClockStop */
|
|
|
|
|
|
|
|
|
|
NEXTBYTE ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((y & 2))
|
|
|
|
|
{
|
|
|
|
|
if (t == 1)
|
|
|
|
|
param[3] = atr[i]; /* TBx - BWI & CWI */
|
|
|
|
|
NEXTBYTE ();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((y & 4))
|
|
|
|
|
{
|
|
|
|
|
if (t == 1)
|
|
|
|
|
param[1] |= (atr[i] & 0x01); /* TCx - LRC/CRC */
|
|
|
|
|
NEXTBYTE ();
|
|
|
|
|
|
|
|
|
|
if (param[1] & 0x01)
|
|
|
|
|
return -4; /* CRC not supported yet. */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ((y & 8))
|
|
|
|
|
{
|
|
|
|
|
y = (atr[i] >> 4); /* TDx */
|
|
|
|
|
t = atr[i] & 0x0f;
|
|
|
|
|
NEXTBYTE ();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
y = 0;
|
|
|
|
|
}
|
2013-01-08 15:22:31 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
i += historical_bytes_num - 1;
|
|
|
|
|
NEXTBYTE ();
|
|
|
|
|
if (atrlen != i+1)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
#undef NEXTBYTE
|
|
|
|
|
|
|
|
|
|
chk = 0;
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
chk ^= atr[i];
|
|
|
|
|
i--;
|
|
|
|
|
}
|
|
|
|
|
while (i > 0);
|
|
|
|
|
|
|
|
|
|
if (chk != 0)
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return negotiable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2008-11-03 19:09:34 +00:00
|
|
|
|
/* Return the ATR of the card. This is not a cached value and thus an
|
|
|
|
|
actual reset is done. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
int
|
2003-09-02 19:06:34 +00:00
|
|
|
|
ccid_get_atr (ccid_driver_t handle,
|
|
|
|
|
unsigned char *atr, size_t maxatrlen, size_t *atrlen)
|
|
|
|
|
{
|
|
|
|
|
int rc;
|
2005-04-11 16:20:10 +00:00
|
|
|
|
int statusbits;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
unsigned char msg[100];
|
2004-04-20 14:17:10 +00:00
|
|
|
|
unsigned char *tpdu;
|
|
|
|
|
size_t msglen, tpdulen;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
unsigned char seqno;
|
2004-04-20 14:17:10 +00:00
|
|
|
|
int use_crc = 0;
|
|
|
|
|
unsigned int edc;
|
2005-05-23 14:17:22 +00:00
|
|
|
|
int tried_iso = 0;
|
2006-02-08 17:56:01 +00:00
|
|
|
|
int got_param;
|
2013-01-08 15:22:31 +09:00
|
|
|
|
unsigned char param[7] = { /* For Protocol T=1 */
|
|
|
|
|
0x11, /* bmFindexDindex */
|
|
|
|
|
0x10, /* bmTCCKST1 */
|
|
|
|
|
0x00, /* bGuardTimeT1 */
|
|
|
|
|
0x4d, /* bmWaitingIntegersT1 */
|
|
|
|
|
0x00, /* bClockStop */
|
|
|
|
|
0x20, /* bIFSC */
|
|
|
|
|
0x00 /* bNadValue */
|
|
|
|
|
};
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
2005-04-11 16:20:10 +00:00
|
|
|
|
/* First check whether a card is available. */
|
2017-01-30 09:30:32 +09:00
|
|
|
|
rc = ccid_slot_status (handle, &statusbits, 1);
|
2005-04-11 16:20:10 +00:00
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
|
|
|
|
if (statusbits == 2)
|
|
|
|
|
return CCID_DRIVER_ERR_NO_CARD;
|
|
|
|
|
|
2017-12-27 17:20:03 +09:00
|
|
|
|
/*
|
|
|
|
|
* In the first invocation of ccid_slot_status, card reader may
|
|
|
|
|
* return CCID_DRIVER_ERR_CARD_INACTIVE and handle->powered_off may
|
|
|
|
|
* become 1. Because inactive card is no problem (we are turning it
|
|
|
|
|
* ON here), clear the flag.
|
|
|
|
|
*/
|
|
|
|
|
handle->powered_off = 0;
|
|
|
|
|
|
2005-04-11 16:20:10 +00:00
|
|
|
|
/* For an inactive and also for an active card, issue the PowerOn
|
|
|
|
|
command to get the ATR. */
|
2005-05-23 14:17:22 +00:00
|
|
|
|
again:
|
2003-09-02 19:06:34 +00:00
|
|
|
|
msg[0] = PC_to_RDR_IccPowerOn;
|
|
|
|
|
msg[5] = 0; /* slot */
|
|
|
|
|
msg[6] = seqno = handle->seqno++;
|
2013-01-08 15:22:31 +09:00
|
|
|
|
/* power select (0=auto, 1=5V, 2=3V, 3=1.8V) */
|
|
|
|
|
msg[7] = handle->auto_voltage ? 0 : 1;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
msg[8] = 0; /* RFU */
|
|
|
|
|
msg[9] = 0; /* RFU */
|
|
|
|
|
set_msg_len (msg, 0);
|
|
|
|
|
msglen = 10;
|
|
|
|
|
|
2009-02-24 20:41:44 +00:00
|
|
|
|
rc = bulk_out (handle, msg, msglen, 0);
|
2003-09-02 19:06:34 +00:00
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
2005-04-11 16:20:10 +00:00
|
|
|
|
rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_DataBlock,
|
2005-05-23 14:17:22 +00:00
|
|
|
|
seqno, 5000, 0);
|
2003-09-02 19:06:34 +00:00
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
2005-05-23 14:17:22 +00:00
|
|
|
|
if (!tried_iso && CCID_COMMAND_FAILED (msg) && CCID_ERROR_CODE (msg) == 0xbb
|
|
|
|
|
&& ((handle->id_vendor == VENDOR_CHERRY
|
|
|
|
|
&& handle->id_product == 0x0005)
|
|
|
|
|
|| (handle->id_vendor == VENDOR_GEMPC
|
|
|
|
|
&& handle->id_product == 0x4433)
|
|
|
|
|
))
|
|
|
|
|
{
|
|
|
|
|
tried_iso = 1;
|
|
|
|
|
/* Try switching to ISO mode. */
|
2005-11-28 11:52:25 +00:00
|
|
|
|
if (!send_escape_cmd (handle, (const unsigned char*)"\xF1\x01", 2,
|
|
|
|
|
NULL, 0, NULL))
|
2005-05-23 14:17:22 +00:00
|
|
|
|
goto again;
|
|
|
|
|
}
|
|
|
|
|
else if (CCID_COMMAND_FAILED (msg))
|
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
|
|
|
|
|
handle->powered_off = 0;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2003-09-02 19:06:34 +00:00
|
|
|
|
if (atr)
|
|
|
|
|
{
|
|
|
|
|
size_t n = msglen - 10;
|
|
|
|
|
|
|
|
|
|
if (n > maxatrlen)
|
|
|
|
|
n = maxatrlen;
|
|
|
|
|
memcpy (atr, msg+10, n);
|
|
|
|
|
*atrlen = n;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-08 15:22:31 +09:00
|
|
|
|
param[6] = handle->nonnull_nad? ((1 << 4) | 0): 0;
|
|
|
|
|
rc = update_param_by_atr (param, msg+10, msglen - 10);
|
|
|
|
|
if (rc < 0)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 ("update_param_by_atr failed: %d\n", rc);
|
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
2006-02-08 17:56:01 +00:00
|
|
|
|
got_param = 0;
|
2013-01-08 15:22:31 +09:00
|
|
|
|
|
|
|
|
|
if (handle->auto_param)
|
|
|
|
|
{
|
|
|
|
|
msg[0] = PC_to_RDR_GetParameters;
|
|
|
|
|
msg[5] = 0; /* slot */
|
|
|
|
|
msg[6] = seqno = handle->seqno++;
|
|
|
|
|
msg[7] = 0; /* RFU */
|
|
|
|
|
msg[8] = 0; /* RFU */
|
|
|
|
|
msg[9] = 0; /* RFU */
|
|
|
|
|
set_msg_len (msg, 0);
|
|
|
|
|
msglen = 10;
|
|
|
|
|
rc = bulk_out (handle, msg, msglen, 0);
|
|
|
|
|
if (!rc)
|
2013-01-10 10:49:27 +09:00
|
|
|
|
rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters,
|
|
|
|
|
seqno, 2000, 0);
|
2013-01-08 15:22:31 +09:00
|
|
|
|
if (rc)
|
2013-01-10 10:49:27 +09:00
|
|
|
|
DEBUGOUT ("GetParameters failed\n");
|
2013-01-08 15:22:31 +09:00
|
|
|
|
else if (msglen == 17 && msg[9] == 1)
|
2013-01-10 10:49:27 +09:00
|
|
|
|
got_param = 1;
|
2013-01-08 15:22:31 +09:00
|
|
|
|
}
|
|
|
|
|
else if (handle->auto_pps)
|
|
|
|
|
;
|
2013-01-10 10:49:27 +09:00
|
|
|
|
else if (rc == 1) /* It's negotiable, send PPS. */
|
2013-01-08 15:22:31 +09:00
|
|
|
|
{
|
|
|
|
|
msg[0] = PC_to_RDR_XfrBlock;
|
|
|
|
|
msg[5] = 0; /* slot */
|
|
|
|
|
msg[6] = seqno = handle->seqno++;
|
|
|
|
|
msg[7] = 0;
|
|
|
|
|
msg[8] = 0;
|
|
|
|
|
msg[9] = 0;
|
2013-01-10 10:49:27 +09:00
|
|
|
|
msg[10] = 0xff; /* PPSS */
|
|
|
|
|
msg[11] = 0x11; /* PPS0: PPS1, Protocol T=1 */
|
|
|
|
|
msg[12] = param[0]; /* PPS1: Fi / Di */
|
2013-01-08 15:22:31 +09:00
|
|
|
|
msg[13] = 0xff ^ 0x11 ^ param[0]; /* PCK */
|
|
|
|
|
set_msg_len (msg, 4);
|
|
|
|
|
msglen = 10 + 4;
|
|
|
|
|
|
|
|
|
|
rc = bulk_out (handle, msg, msglen, 0);
|
|
|
|
|
if (rc)
|
2013-01-10 10:49:27 +09:00
|
|
|
|
return rc;
|
2013-01-08 15:22:31 +09:00
|
|
|
|
|
|
|
|
|
rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_DataBlock,
|
2013-01-10 10:49:27 +09:00
|
|
|
|
seqno, 5000, 0);
|
2013-01-08 15:22:31 +09:00
|
|
|
|
if (rc)
|
2013-01-10 10:49:27 +09:00
|
|
|
|
return rc;
|
2013-01-08 15:22:31 +09:00
|
|
|
|
|
|
|
|
|
if (msglen != 10 + 4)
|
2013-01-10 10:49:27 +09:00
|
|
|
|
{
|
2013-08-22 09:35:21 +02:00
|
|
|
|
DEBUGOUT_1 ("Setting PPS failed: %zu\n", msglen);
|
2013-01-10 10:49:27 +09:00
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
|
|
|
|
}
|
2013-01-08 15:22:31 +09:00
|
|
|
|
|
|
|
|
|
if (msg[10] != 0xff || msg[11] != 0x11 || msg[12] != param[0])
|
2013-01-10 10:49:27 +09:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_1 ("Setting PPS failed: 0x%02x\n", param[0]);
|
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
|
|
|
|
}
|
2013-01-08 15:22:31 +09:00
|
|
|
|
}
|
2006-02-08 17:56:01 +00:00
|
|
|
|
|
2004-04-20 14:17:10 +00:00
|
|
|
|
/* Setup parameters to select T=1. */
|
|
|
|
|
msg[0] = PC_to_RDR_SetParameters;
|
|
|
|
|
msg[5] = 0; /* slot */
|
|
|
|
|
msg[6] = seqno = handle->seqno++;
|
|
|
|
|
msg[7] = 1; /* Select T=1. */
|
|
|
|
|
msg[8] = 0; /* RFU */
|
|
|
|
|
msg[9] = 0; /* RFU */
|
|
|
|
|
|
2006-02-08 17:56:01 +00:00
|
|
|
|
if (!got_param)
|
2013-01-08 15:22:31 +09:00
|
|
|
|
memcpy (&msg[10], param, 7);
|
2004-04-20 14:17:10 +00:00
|
|
|
|
set_msg_len (msg, 7);
|
|
|
|
|
msglen = 10 + 7;
|
|
|
|
|
|
2009-02-24 20:41:44 +00:00
|
|
|
|
rc = bulk_out (handle, msg, msglen, 0);
|
2004-04-20 14:17:10 +00:00
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
2006-02-08 17:56:01 +00:00
|
|
|
|
rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters,
|
|
|
|
|
seqno, 5000, 0);
|
|
|
|
|
if (rc)
|
|
|
|
|
DEBUGOUT ("SetParameters failed (ignored)\n");
|
2004-04-20 14:17:10 +00:00
|
|
|
|
|
2009-03-30 12:46:06 +00:00
|
|
|
|
if (!rc && msglen > 15 && msg[15] >= 16 && msg[15] <= 254 )
|
|
|
|
|
handle->ifsc = msg[15];
|
|
|
|
|
else
|
|
|
|
|
handle->ifsc = 128; /* Something went wrong, assume 128 bytes. */
|
|
|
|
|
|
2013-01-08 15:22:31 +09:00
|
|
|
|
if (handle->nonnull_nad && msglen > 16 && msg[16] == 0)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT ("Use Null-NAD, clearing handle->nonnull_nad.\n");
|
|
|
|
|
handle->nonnull_nad = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2004-10-20 08:54:45 +00:00
|
|
|
|
handle->t1_ns = 0;
|
|
|
|
|
handle->t1_nr = 0;
|
2004-04-20 14:17:10 +00:00
|
|
|
|
|
2006-02-08 17:56:01 +00:00
|
|
|
|
/* Send an S-Block with our maximum IFSD to the CCID. */
|
|
|
|
|
if (!handle->apdu_level && !handle->auto_ifsd)
|
2004-04-20 14:17:10 +00:00
|
|
|
|
{
|
|
|
|
|
tpdu = msg+10;
|
|
|
|
|
/* NAD: DAD=1, SAD=0 */
|
|
|
|
|
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
|
|
|
|
|
tpdu[1] = (0xc0 | 0 | 1); /* S-block request: change IFSD */
|
|
|
|
|
tpdu[2] = 1;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
tpdu[3] = handle->max_ifsd? handle->max_ifsd : 32;
|
2004-04-20 14:17:10 +00:00
|
|
|
|
tpdulen = 4;
|
|
|
|
|
edc = compute_edc (tpdu, tpdulen, use_crc);
|
|
|
|
|
if (use_crc)
|
|
|
|
|
tpdu[tpdulen++] = (edc >> 8);
|
|
|
|
|
tpdu[tpdulen++] = edc;
|
|
|
|
|
|
|
|
|
|
msg[0] = PC_to_RDR_XfrBlock;
|
|
|
|
|
msg[5] = 0; /* slot */
|
|
|
|
|
msg[6] = seqno = handle->seqno++;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
msg[7] = 0;
|
2004-04-20 14:17:10 +00:00
|
|
|
|
msg[8] = 0; /* RFU */
|
|
|
|
|
msg[9] = 0; /* RFU */
|
|
|
|
|
set_msg_len (msg, tpdulen);
|
|
|
|
|
msglen = 10 + tpdulen;
|
|
|
|
|
|
2005-05-20 20:39:36 +00:00
|
|
|
|
if (debug_level > 1)
|
|
|
|
|
DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n",
|
|
|
|
|
((msg[11] & 0xc0) == 0x80)? 'R' :
|
|
|
|
|
(msg[11] & 0x80)? 'S' : 'I',
|
|
|
|
|
((msg[11] & 0x80)? !!(msg[11]& 0x10)
|
|
|
|
|
: !!(msg[11] & 0x40)),
|
|
|
|
|
(!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
|
2004-04-20 14:17:10 +00:00
|
|
|
|
|
2009-02-24 20:41:44 +00:00
|
|
|
|
rc = bulk_out (handle, msg, msglen, 0);
|
2004-04-20 14:17:10 +00:00
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rc = bulk_in (handle, msg, sizeof msg, &msglen,
|
2005-05-23 14:17:22 +00:00
|
|
|
|
RDR_to_PC_DataBlock, seqno, 5000, 0);
|
2004-04-20 14:17:10 +00:00
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2004-04-20 14:17:10 +00:00
|
|
|
|
tpdu = msg + 10;
|
|
|
|
|
tpdulen = msglen - 10;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
|
|
|
|
if (tpdulen < 4)
|
|
|
|
|
return CCID_DRIVER_ERR_ABORTED;
|
2004-04-20 14:17:10 +00:00
|
|
|
|
|
2005-05-20 20:39:36 +00:00
|
|
|
|
if (debug_level > 1)
|
|
|
|
|
DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
|
|
|
|
|
((msg[11] & 0xc0) == 0x80)? 'R' :
|
|
|
|
|
(msg[11] & 0x80)? 'S' : 'I',
|
|
|
|
|
((msg[11] & 0x80)? !!(msg[11]& 0x10)
|
|
|
|
|
: !!(msg[11] & 0x40)),
|
|
|
|
|
((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0,
|
|
|
|
|
(!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
|
|
|
|
|
|
2004-04-20 14:17:10 +00:00
|
|
|
|
if ((tpdu[1] & 0xe0) != 0xe0 || tpdu[2] != 1)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT ("invalid response for S-block (Change-IFSD)\n");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
DEBUGOUT_1 ("IFSD has been set to %d\n", tpdu[3]);
|
|
|
|
|
}
|
|
|
|
|
|
2003-09-02 19:06:34 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-04-20 14:17:10 +00:00
|
|
|
|
|
2011-02-04 12:57:53 +01:00
|
|
|
|
static unsigned int
|
2004-04-20 14:17:10 +00:00
|
|
|
|
compute_edc (const unsigned char *data, size_t datalen, int use_crc)
|
|
|
|
|
{
|
|
|
|
|
if (use_crc)
|
|
|
|
|
{
|
|
|
|
|
return 0x42; /* Not yet implemented. */
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
unsigned char crc = 0;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2004-04-20 14:17:10 +00:00
|
|
|
|
for (; datalen; datalen--)
|
|
|
|
|
crc ^= *data++;
|
|
|
|
|
return crc;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2009-06-29 20:54:00 +00:00
|
|
|
|
/* Return true if APDU is an extended length one. */
|
|
|
|
|
static int
|
|
|
|
|
is_exlen_apdu (const unsigned char *apdu, size_t apdulen)
|
|
|
|
|
{
|
|
|
|
|
if (apdulen < 7 || apdu[4])
|
|
|
|
|
return 0; /* Too short or no Z byte. */
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2004-12-28 07:13:24 +00:00
|
|
|
|
/* Helper for ccid_transceive used for APDU level exchanges. */
|
|
|
|
|
static int
|
|
|
|
|
ccid_transceive_apdu_level (ccid_driver_t handle,
|
2015-04-14 14:17:03 +09:00
|
|
|
|
const unsigned char *apdu_buf, size_t apdu_len,
|
2004-12-28 07:13:24 +00:00
|
|
|
|
unsigned char *resp, size_t maxresplen,
|
|
|
|
|
size_t *nresp)
|
|
|
|
|
{
|
|
|
|
|
int rc;
|
2015-04-14 14:17:03 +09:00
|
|
|
|
unsigned char msg[CCID_MAX_BUF];
|
|
|
|
|
const unsigned char *apdu_p;
|
|
|
|
|
size_t apdu_part_len;
|
2004-12-28 07:13:24 +00:00
|
|
|
|
size_t msglen;
|
|
|
|
|
unsigned char seqno;
|
2019-07-25 09:15:12 +09:00
|
|
|
|
int bwi = 0;
|
2015-04-14 14:17:03 +09:00
|
|
|
|
unsigned char chain = 0;
|
2004-12-28 07:13:24 +00:00
|
|
|
|
|
2015-04-14 14:17:03 +09:00
|
|
|
|
if (apdu_len == 0 || apdu_len > sizeof (msg) - 10)
|
2004-12-28 07:13:24 +00:00
|
|
|
|
return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2015-04-14 14:17:03 +09:00
|
|
|
|
apdu_p = apdu_buf;
|
|
|
|
|
while (1)
|
|
|
|
|
{
|
|
|
|
|
apdu_part_len = apdu_len;
|
|
|
|
|
if (apdu_part_len > handle->max_ccid_msglen - 10)
|
|
|
|
|
{
|
|
|
|
|
apdu_part_len = handle->max_ccid_msglen - 10;
|
|
|
|
|
chain |= 0x01;
|
|
|
|
|
}
|
2004-12-28 07:13:24 +00:00
|
|
|
|
|
2015-04-14 14:17:03 +09:00
|
|
|
|
msg[0] = PC_to_RDR_XfrBlock;
|
|
|
|
|
msg[5] = 0; /* slot */
|
|
|
|
|
msg[6] = seqno = handle->seqno++;
|
|
|
|
|
msg[7] = bwi;
|
|
|
|
|
msg[8] = chain;
|
|
|
|
|
msg[9] = 0;
|
|
|
|
|
memcpy (msg+10, apdu_p, apdu_part_len);
|
|
|
|
|
set_msg_len (msg, apdu_part_len);
|
|
|
|
|
msglen = 10 + apdu_part_len;
|
2004-12-28 07:13:24 +00:00
|
|
|
|
|
2015-04-14 14:17:03 +09:00
|
|
|
|
rc = bulk_out (handle, msg, msglen, 0);
|
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2015-04-14 14:17:03 +09:00
|
|
|
|
apdu_p += apdu_part_len;
|
|
|
|
|
apdu_len -= apdu_part_len;
|
2012-01-06 13:50:21 +09:00
|
|
|
|
|
2015-04-14 14:17:03 +09:00
|
|
|
|
rc = bulk_in (handle, msg, sizeof msg, &msglen,
|
2015-09-17 11:21:44 +09:00
|
|
|
|
RDR_to_PC_DataBlock, seqno, CCID_CMD_TIMEOUT, 0);
|
2015-04-14 14:17:03 +09:00
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
2012-01-06 13:50:21 +09:00
|
|
|
|
|
2015-04-14 14:17:03 +09:00
|
|
|
|
if (!(chain & 0x01))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
chain = 0x02;
|
2012-01-06 13:50:21 +09:00
|
|
|
|
}
|
2015-04-14 14:17:03 +09:00
|
|
|
|
|
|
|
|
|
apdu_len = 0;
|
|
|
|
|
while (1)
|
2012-01-06 13:50:21 +09:00
|
|
|
|
{
|
2015-04-14 14:17:03 +09:00
|
|
|
|
apdu_part_len = msglen - 10;
|
|
|
|
|
if (resp && apdu_len + apdu_part_len <= maxresplen)
|
|
|
|
|
memcpy (resp + apdu_len, msg+10, apdu_part_len);
|
|
|
|
|
apdu_len += apdu_part_len;
|
|
|
|
|
|
|
|
|
|
if (!(msg[9] & 0x01))
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
msg[0] = PC_to_RDR_XfrBlock;
|
|
|
|
|
msg[5] = 0; /* slot */
|
|
|
|
|
msg[6] = seqno = handle->seqno++;
|
|
|
|
|
msg[7] = bwi;
|
|
|
|
|
msg[8] = 0x10; /* Request next data block */
|
|
|
|
|
msg[9] = 0;
|
|
|
|
|
set_msg_len (msg, 0);
|
|
|
|
|
msglen = 10;
|
|
|
|
|
|
|
|
|
|
rc = bulk_out (handle, msg, msglen, 0);
|
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
|
|
rc = bulk_in (handle, msg, sizeof msg, &msglen,
|
2015-09-17 11:21:44 +09:00
|
|
|
|
RDR_to_PC_DataBlock, seqno, CCID_CMD_TIMEOUT, 0);
|
2015-04-14 14:17:03 +09:00
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
2012-01-06 13:50:21 +09:00
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2004-12-28 07:13:24 +00:00
|
|
|
|
if (resp)
|
|
|
|
|
{
|
2015-04-14 14:17:03 +09:00
|
|
|
|
if (apdu_len > maxresplen)
|
2004-12-28 07:13:24 +00:00
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_2 ("provided buffer too short for received data "
|
|
|
|
|
"(%u/%u)\n",
|
2015-04-14 14:17:03 +09:00
|
|
|
|
(unsigned int)apdu_len, (unsigned int)maxresplen);
|
2004-12-28 07:13:24 +00:00
|
|
|
|
return CCID_DRIVER_ERR_INV_VALUE;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2015-04-14 14:17:03 +09:00
|
|
|
|
*nresp = apdu_len;
|
2004-12-28 07:13:24 +00:00
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2004-12-28 07:13:24 +00:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2003-09-02 19:06:34 +00:00
|
|
|
|
/*
|
|
|
|
|
Protocol T=1 overview
|
|
|
|
|
|
|
|
|
|
Block Structure:
|
|
|
|
|
Prologue Field:
|
2011-02-04 12:57:53 +01:00
|
|
|
|
1 byte Node Address (NAD)
|
2003-09-02 19:06:34 +00:00
|
|
|
|
1 byte Protocol Control Byte (PCB)
|
2011-02-04 12:57:53 +01:00
|
|
|
|
1 byte Length (LEN)
|
2003-09-02 19:06:34 +00:00
|
|
|
|
Information Field:
|
|
|
|
|
0-254 byte APDU or Control Information (INF)
|
|
|
|
|
Epilogue Field:
|
|
|
|
|
1 byte Error Detection Code (EDC)
|
|
|
|
|
|
2011-02-04 12:57:53 +01:00
|
|
|
|
NAD:
|
2003-09-02 19:06:34 +00:00
|
|
|
|
bit 7 unused
|
|
|
|
|
bit 4..6 Destination Node Address (DAD)
|
|
|
|
|
bit 3 unused
|
|
|
|
|
bit 2..0 Source Node Address (SAD)
|
|
|
|
|
|
|
|
|
|
If node adresses are not used, SAD and DAD should be set to 0 on
|
|
|
|
|
the first block sent to the card. If they are used they should
|
|
|
|
|
have different values (0 for one is okay); that first block sets up
|
2003-09-05 07:40:41 +00:00
|
|
|
|
the addresses of the nodes.
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
|
|
|
|
PCB:
|
|
|
|
|
Information Block (I-Block):
|
|
|
|
|
bit 7 0
|
|
|
|
|
bit 6 Sequence number (yep, that is modulo 2)
|
2011-02-04 12:57:53 +01:00
|
|
|
|
bit 5 Chaining flag
|
2003-09-02 19:06:34 +00:00
|
|
|
|
bit 4..0 reserved
|
|
|
|
|
Received-Ready Block (R-Block):
|
|
|
|
|
bit 7 1
|
|
|
|
|
bit 6 0
|
|
|
|
|
bit 5 0
|
|
|
|
|
bit 4 Sequence number
|
|
|
|
|
bit 3..0 0 = no error
|
|
|
|
|
1 = EDC or parity error
|
|
|
|
|
2 = other error
|
|
|
|
|
other values are reserved
|
|
|
|
|
Supervisory Block (S-Block):
|
|
|
|
|
bit 7 1
|
|
|
|
|
bit 6 1
|
|
|
|
|
bit 5 clear=request,set=response
|
2019-06-23 20:17:05 -04:00
|
|
|
|
bit 4..0 0 = resynchronization request
|
2003-09-02 19:06:34 +00:00
|
|
|
|
1 = information field size request
|
|
|
|
|
2 = abort request
|
|
|
|
|
3 = extension of BWT request
|
|
|
|
|
4 = VPP error
|
|
|
|
|
other values are reserved
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
ccid_transceive (ccid_driver_t handle,
|
2003-10-09 15:18:08 +00:00
|
|
|
|
const unsigned char *apdu_buf, size_t apdu_buflen,
|
2003-09-02 19:06:34 +00:00
|
|
|
|
unsigned char *resp, size_t maxresplen, size_t *nresp)
|
|
|
|
|
{
|
|
|
|
|
int rc;
|
2009-06-29 20:54:00 +00:00
|
|
|
|
/* The size of the buffer used to be 10+259. For the via_escape
|
|
|
|
|
hack we need one extra byte, thus 11+259. */
|
|
|
|
|
unsigned char send_buffer[11+259], recv_buffer[11+259];
|
2003-10-09 15:18:08 +00:00
|
|
|
|
const unsigned char *apdu;
|
|
|
|
|
size_t apdulen;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
unsigned char *msg, *tpdu, *p;
|
2004-04-20 14:17:10 +00:00
|
|
|
|
size_t msglen, tpdulen, last_tpdulen, n;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
unsigned char seqno;
|
2004-04-20 14:17:10 +00:00
|
|
|
|
unsigned int edc;
|
|
|
|
|
int use_crc = 0;
|
2009-06-29 20:54:00 +00:00
|
|
|
|
int hdrlen, pcboff;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
size_t dummy_nresp;
|
2009-06-29 20:54:00 +00:00
|
|
|
|
int via_escape = 0;
|
2003-10-09 15:18:08 +00:00
|
|
|
|
int next_chunk = 1;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
int sending = 1;
|
2004-04-20 14:17:10 +00:00
|
|
|
|
int retries = 0;
|
2009-06-29 20:54:00 +00:00
|
|
|
|
int resyncing = 0;
|
|
|
|
|
int nad_byte;
|
2017-03-27 11:25:00 +09:00
|
|
|
|
int wait_more = 0;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
|
|
|
|
|
if (!nresp)
|
|
|
|
|
nresp = &dummy_nresp;
|
|
|
|
|
*nresp = 0;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
More cleanup of "allow to".
* README, agent/command.c, agent/keyformat.txt, common/i18n.c,
common/iobuf.c, common/keyserver.h, dirmngr/cdblib.c,
dirmngr/ldap-wrapper.c, doc/DETAILS, doc/TRANSLATE,
doc/announce-2.1.txt, doc/gpg.texi, doc/gpgsm.texi,
doc/scdaemon.texi, doc/tools.texi, doc/whats-new-in-2.1.txt,
g10/export.c, g10/getkey.c, g10/import.c, g10/keyedit.c, m4/ksba.m4,
m4/libgcrypt.m4, m4/ntbtls.m4, po/ca.po, po/cs.po, po/da.po,
po/de.po, po/el.po, po/eo.po, po/es.po, po/et.po, po/fi.po,
po/fr.po, po/gl.po, po/hu.po, po/id.po, po/it.po, po/ja.po,
po/nb.po, po/pl.po, po/pt.po, po/ro.po, po/ru.po, po/sk.po,
po/sv.po, po/tr.po, po/uk.po, po/zh_CN.po, po/zh_TW.po,
scd/app-p15.c, scd/ccid-driver.c, scd/command.c, sm/gpgsm.c,
sm/sign.c, tools/gpgconf-comp.c, tools/gpgtar.h: replace "Allow to"
with clearer text.
In standard English, the normal construction is "${XXX} allows ${YYY}
to" -- that is, the subject (${XXX}) of the sentence is allowing the
object (${YYY}) to do something. When the object is missing, the
phrasing sounds awkward, even if the object is implied by context.
There's almost always a better construction that isn't as awkward.
These changes should make the language a bit clearer.
Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2016-08-01 22:19:17 -04:00
|
|
|
|
/* Smarter readers allow sending APDUs directly; divert here. */
|
2004-12-28 07:13:24 +00:00
|
|
|
|
if (handle->apdu_level)
|
2009-06-29 20:54:00 +00:00
|
|
|
|
{
|
|
|
|
|
/* We employ a hack for Omnikey readers which are able to send
|
|
|
|
|
TPDUs using an escape sequence. There is no documentation
|
|
|
|
|
but the Windows driver does it this way. Tested using a
|
2009-07-01 10:53:02 +00:00
|
|
|
|
CM6121. This method works also for the Cherry XX44
|
|
|
|
|
keyboards; however there are problems with the
|
2017-03-27 11:25:00 +09:00
|
|
|
|
ccid_transceive_secure which leads to a loss of sync on the
|
2009-07-01 10:53:02 +00:00
|
|
|
|
CCID level. If Cherry wants to make their keyboard work
|
|
|
|
|
again, they should hand over some docs. */
|
2017-04-10 12:25:06 +09:00
|
|
|
|
if ((handle->id_vendor == VENDOR_OMNIKEY)
|
2009-06-29 20:54:00 +00:00
|
|
|
|
&& handle->apdu_level < 2
|
|
|
|
|
&& is_exlen_apdu (apdu_buf, apdu_buflen))
|
|
|
|
|
via_escape = 1;
|
|
|
|
|
else
|
|
|
|
|
return ccid_transceive_apdu_level (handle, apdu_buf, apdu_buflen,
|
|
|
|
|
resp, maxresplen, nresp);
|
|
|
|
|
}
|
2004-12-28 07:13:24 +00:00
|
|
|
|
|
|
|
|
|
/* The other readers we support require sending TPDUs. */
|
|
|
|
|
|
2003-10-09 15:18:08 +00:00
|
|
|
|
tpdulen = 0; /* Avoid compiler warning about no initialization. */
|
2003-09-05 07:40:41 +00:00
|
|
|
|
msg = send_buffer;
|
2009-06-29 20:54:00 +00:00
|
|
|
|
hdrlen = via_escape? 11 : 10;
|
|
|
|
|
|
|
|
|
|
/* NAD: DAD=1, SAD=0 */
|
|
|
|
|
nad_byte = handle->nonnull_nad? ((1 << 4) | 0): 0;
|
|
|
|
|
if (via_escape)
|
|
|
|
|
nad_byte = 0;
|
|
|
|
|
|
2009-06-30 08:19:28 +00:00
|
|
|
|
last_tpdulen = 0; /* Avoid gcc warning (controlled by RESYNCING). */
|
2003-10-09 15:18:08 +00:00
|
|
|
|
for (;;)
|
|
|
|
|
{
|
|
|
|
|
if (next_chunk)
|
|
|
|
|
{
|
|
|
|
|
next_chunk = 0;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
|
2003-10-09 15:18:08 +00:00
|
|
|
|
apdu = apdu_buf;
|
|
|
|
|
apdulen = apdu_buflen;
|
|
|
|
|
assert (apdulen);
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
2003-10-09 15:18:08 +00:00
|
|
|
|
/* Construct an I-Block. */
|
2009-06-29 20:54:00 +00:00
|
|
|
|
tpdu = msg + hdrlen;
|
|
|
|
|
tpdu[0] = nad_byte;
|
2003-10-09 15:18:08 +00:00
|
|
|
|
tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */
|
2009-03-30 12:46:06 +00:00
|
|
|
|
if (apdulen > handle->ifsc )
|
2003-10-09 15:18:08 +00:00
|
|
|
|
{
|
2009-03-30 12:46:06 +00:00
|
|
|
|
apdulen = handle->ifsc;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
apdu_buf += handle->ifsc;
|
2009-03-30 12:46:06 +00:00
|
|
|
|
apdu_buflen -= handle->ifsc;
|
2003-10-09 15:18:08 +00:00
|
|
|
|
tpdu[1] |= (1 << 5); /* Set more bit. */
|
|
|
|
|
}
|
|
|
|
|
tpdu[2] = apdulen;
|
|
|
|
|
memcpy (tpdu+3, apdu, apdulen);
|
2004-04-20 14:17:10 +00:00
|
|
|
|
tpdulen = 3 + apdulen;
|
|
|
|
|
edc = compute_edc (tpdu, tpdulen, use_crc);
|
|
|
|
|
if (use_crc)
|
|
|
|
|
tpdu[tpdulen++] = (edc >> 8);
|
|
|
|
|
tpdu[tpdulen++] = edc;
|
2003-10-09 15:18:08 +00:00
|
|
|
|
}
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
2009-06-29 20:54:00 +00:00
|
|
|
|
if (via_escape)
|
|
|
|
|
{
|
|
|
|
|
msg[0] = PC_to_RDR_Escape;
|
|
|
|
|
msg[5] = 0; /* slot */
|
|
|
|
|
msg[6] = seqno = handle->seqno++;
|
|
|
|
|
msg[7] = 0; /* RFU */
|
|
|
|
|
msg[8] = 0; /* RFU */
|
|
|
|
|
msg[9] = 0; /* RFU */
|
|
|
|
|
msg[10] = 0x1a; /* Omnikey command to send a TPDU. */
|
|
|
|
|
set_msg_len (msg, 1 + tpdulen);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
msg[0] = PC_to_RDR_XfrBlock;
|
|
|
|
|
msg[5] = 0; /* slot */
|
|
|
|
|
msg[6] = seqno = handle->seqno++;
|
2019-07-25 09:15:12 +09:00
|
|
|
|
msg[7] = wait_more; /* bBWI */
|
2009-06-29 20:54:00 +00:00
|
|
|
|
msg[8] = 0; /* RFU */
|
|
|
|
|
msg[9] = 0; /* RFU */
|
|
|
|
|
set_msg_len (msg, tpdulen);
|
|
|
|
|
}
|
|
|
|
|
msglen = hdrlen + tpdulen;
|
|
|
|
|
if (!resyncing)
|
|
|
|
|
last_tpdulen = tpdulen;
|
|
|
|
|
pcboff = hdrlen+1;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
|
2005-05-20 20:39:36 +00:00
|
|
|
|
if (debug_level > 1)
|
2009-06-29 20:54:00 +00:00
|
|
|
|
DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n",
|
|
|
|
|
((msg[pcboff] & 0xc0) == 0x80)? 'R' :
|
|
|
|
|
(msg[pcboff] & 0x80)? 'S' : 'I',
|
|
|
|
|
((msg[pcboff] & 0x80)? !!(msg[pcboff]& 0x10)
|
|
|
|
|
: !!(msg[pcboff] & 0x40)),
|
|
|
|
|
(!(msg[pcboff] & 0x80) && (msg[pcboff] & 0x20)?
|
|
|
|
|
" [more]":""));
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2009-02-24 20:41:44 +00:00
|
|
|
|
rc = bulk_out (handle, msg, msglen, 0);
|
2003-09-05 07:40:41 +00:00
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
|
|
msg = recv_buffer;
|
|
|
|
|
rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
|
2017-03-27 11:25:00 +09:00
|
|
|
|
via_escape? RDR_to_PC_Escape : RDR_to_PC_DataBlock, seqno,
|
|
|
|
|
wait_more? CCID_CMD_TIMEOUT_LONGER: CCID_CMD_TIMEOUT, 0);
|
2003-09-05 07:40:41 +00:00
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
2009-06-29 20:54:00 +00:00
|
|
|
|
|
|
|
|
|
tpdu = msg + hdrlen;
|
|
|
|
|
tpdulen = msglen - hdrlen;
|
|
|
|
|
resyncing = 0;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
|
|
|
|
if (tpdulen < 4)
|
2003-09-05 07:40:41 +00:00
|
|
|
|
{
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
|
|
|
|
npth_unprotect ();
|
|
|
|
|
#endif
|
2016-01-27 12:24:05 +09:00
|
|
|
|
libusb_clear_halt (handle->idev, handle->ep_bulk_in);
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
|
|
|
|
npth_protect ();
|
|
|
|
|
#endif
|
2011-02-04 12:57:53 +01:00
|
|
|
|
return CCID_DRIVER_ERR_ABORTED;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
}
|
2005-05-20 20:39:36 +00:00
|
|
|
|
|
|
|
|
|
if (debug_level > 1)
|
|
|
|
|
DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
|
2009-06-29 20:54:00 +00:00
|
|
|
|
((msg[pcboff] & 0xc0) == 0x80)? 'R' :
|
|
|
|
|
(msg[pcboff] & 0x80)? 'S' : 'I',
|
|
|
|
|
((msg[pcboff] & 0x80)? !!(msg[pcboff]& 0x10)
|
|
|
|
|
: !!(msg[pcboff] & 0x40)),
|
|
|
|
|
((msg[pcboff] & 0xc0) == 0x80)? (msg[pcboff] & 0x0f) : 0,
|
|
|
|
|
(!(msg[pcboff] & 0x80) && (msg[pcboff] & 0x20)?
|
|
|
|
|
" [more]":""));
|
2003-09-05 07:40:41 +00:00
|
|
|
|
|
|
|
|
|
if (!(tpdu[1] & 0x80))
|
|
|
|
|
{ /* This is an I-block. */
|
2004-04-20 14:17:10 +00:00
|
|
|
|
retries = 0;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
if (sending)
|
|
|
|
|
{ /* last block sent was successful. */
|
|
|
|
|
handle->t1_ns ^= 1;
|
|
|
|
|
sending = 0;
|
|
|
|
|
}
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
2003-09-05 07:40:41 +00:00
|
|
|
|
if (!!(tpdu[1] & 0x40) != handle->t1_nr)
|
2015-11-16 12:41:46 +01:00
|
|
|
|
{ /* Response does not match our sequence number. */
|
2003-09-05 07:40:41 +00:00
|
|
|
|
msg = send_buffer;
|
2009-06-29 20:54:00 +00:00
|
|
|
|
tpdu = msg + hdrlen;
|
|
|
|
|
tpdu[0] = nad_byte;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4 | 2); /* R-block */
|
|
|
|
|
tpdu[2] = 0;
|
|
|
|
|
tpdulen = 3;
|
2004-04-20 14:17:10 +00:00
|
|
|
|
edc = compute_edc (tpdu, tpdulen, use_crc);
|
|
|
|
|
if (use_crc)
|
|
|
|
|
tpdu[tpdulen++] = (edc >> 8);
|
|
|
|
|
tpdu[tpdulen++] = edc;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
2003-09-05 07:40:41 +00:00
|
|
|
|
handle->t1_nr ^= 1;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
2003-09-05 07:40:41 +00:00
|
|
|
|
p = tpdu + 3; /* Skip the prologue field. */
|
|
|
|
|
n = tpdulen - 3 - 1; /* Strip the epilogue field. */
|
|
|
|
|
/* fixme: verify the checksum. */
|
|
|
|
|
if (resp)
|
|
|
|
|
{
|
|
|
|
|
if (n > maxresplen)
|
|
|
|
|
{
|
2004-01-27 16:40:42 +00:00
|
|
|
|
DEBUGOUT_2 ("provided buffer too short for received data "
|
|
|
|
|
"(%u/%u)\n",
|
|
|
|
|
(unsigned int)n, (unsigned int)maxresplen);
|
2004-07-16 15:45:25 +00:00
|
|
|
|
return CCID_DRIVER_ERR_INV_VALUE;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
|
|
|
|
memcpy (resp, p, n);
|
2003-09-05 07:40:41 +00:00
|
|
|
|
resp += n;
|
|
|
|
|
*nresp += n;
|
|
|
|
|
maxresplen -= n;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2003-09-05 07:40:41 +00:00
|
|
|
|
if (!(tpdu[1] & 0x20))
|
|
|
|
|
return 0; /* No chaining requested - ready. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2003-09-05 07:40:41 +00:00
|
|
|
|
msg = send_buffer;
|
2009-06-29 20:54:00 +00:00
|
|
|
|
tpdu = msg + hdrlen;
|
|
|
|
|
tpdu[0] = nad_byte;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4); /* R-block */
|
|
|
|
|
tpdu[2] = 0;
|
|
|
|
|
tpdulen = 3;
|
2004-04-20 14:17:10 +00:00
|
|
|
|
edc = compute_edc (tpdu, tpdulen, use_crc);
|
|
|
|
|
if (use_crc)
|
|
|
|
|
tpdu[tpdulen++] = (edc >> 8);
|
|
|
|
|
tpdu[tpdulen++] = edc;
|
2003-09-02 19:06:34 +00:00
|
|
|
|
}
|
2003-09-05 07:40:41 +00:00
|
|
|
|
else if ((tpdu[1] & 0xc0) == 0x80)
|
|
|
|
|
{ /* This is a R-block. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
if ( (tpdu[1] & 0x0f))
|
|
|
|
|
{
|
2009-06-29 20:54:00 +00:00
|
|
|
|
retries++;
|
|
|
|
|
if (via_escape && retries == 1 && (msg[pcboff] & 0x0f))
|
|
|
|
|
{
|
|
|
|
|
/* Error probably due to switching to TPDU. Send a
|
|
|
|
|
resync request. We use the recv_buffer so that
|
|
|
|
|
we don't corrupt the send_buffer. */
|
|
|
|
|
msg = recv_buffer;
|
|
|
|
|
tpdu = msg + hdrlen;
|
|
|
|
|
tpdu[0] = nad_byte;
|
|
|
|
|
tpdu[1] = 0xc0; /* S-block resync request. */
|
|
|
|
|
tpdu[2] = 0;
|
|
|
|
|
tpdulen = 3;
|
|
|
|
|
edc = compute_edc (tpdu, tpdulen, use_crc);
|
|
|
|
|
if (use_crc)
|
|
|
|
|
tpdu[tpdulen++] = (edc >> 8);
|
|
|
|
|
tpdu[tpdulen++] = edc;
|
2009-06-30 08:22:28 +00:00
|
|
|
|
resyncing = 1;
|
|
|
|
|
DEBUGOUT ("T=1: requesting resync\n");
|
2009-06-29 20:54:00 +00:00
|
|
|
|
}
|
|
|
|
|
else if (retries > 3)
|
2004-04-20 14:17:10 +00:00
|
|
|
|
{
|
2009-06-29 20:54:00 +00:00
|
|
|
|
DEBUGOUT ("T=1: 3 failed retries\n");
|
2004-07-16 15:45:25 +00:00
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
2004-04-20 14:17:10 +00:00
|
|
|
|
}
|
2009-06-29 20:54:00 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* Error: repeat last block */
|
|
|
|
|
msg = send_buffer;
|
|
|
|
|
tpdulen = last_tpdulen;
|
|
|
|
|
}
|
2003-09-05 07:40:41 +00:00
|
|
|
|
}
|
2005-05-20 20:39:36 +00:00
|
|
|
|
else if (sending && !!(tpdu[1] & 0x10) == handle->t1_ns)
|
|
|
|
|
{ /* Response does not match our sequence number. */
|
2003-10-09 15:18:08 +00:00
|
|
|
|
DEBUGOUT ("R-block with wrong seqno received on more bit\n");
|
2004-07-16 15:45:25 +00:00
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
2003-10-09 15:18:08 +00:00
|
|
|
|
}
|
|
|
|
|
else if (sending)
|
|
|
|
|
{ /* Send next chunk. */
|
2004-04-20 14:17:10 +00:00
|
|
|
|
retries = 0;
|
2003-10-09 15:18:08 +00:00
|
|
|
|
msg = send_buffer;
|
|
|
|
|
next_chunk = 1;
|
|
|
|
|
handle->t1_ns ^= 1;
|
|
|
|
|
}
|
2003-09-05 07:40:41 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
2003-10-09 15:18:08 +00:00
|
|
|
|
DEBUGOUT ("unexpected ACK R-block received\n");
|
2004-07-16 15:45:25 +00:00
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
else
|
2003-09-05 07:40:41 +00:00
|
|
|
|
{ /* This is a S-block. */
|
2004-04-20 14:17:10 +00:00
|
|
|
|
retries = 0;
|
2009-06-29 20:54:00 +00:00
|
|
|
|
DEBUGOUT_2 ("T=1: S-block %s received cmd=%d\n",
|
2003-09-05 07:40:41 +00:00
|
|
|
|
(tpdu[1] & 0x20)? "response": "request",
|
|
|
|
|
(tpdu[1] & 0x1f));
|
2009-03-30 12:46:06 +00:00
|
|
|
|
if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 1 && tpdu[2] == 1)
|
|
|
|
|
{
|
|
|
|
|
/* Information field size request. */
|
|
|
|
|
unsigned char ifsc = tpdu[3];
|
|
|
|
|
|
|
|
|
|
if (ifsc < 16 || ifsc > 254)
|
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
|
|
|
|
|
|
|
|
|
msg = send_buffer;
|
2009-06-29 20:54:00 +00:00
|
|
|
|
tpdu = msg + hdrlen;
|
|
|
|
|
tpdu[0] = nad_byte;
|
2009-03-30 12:46:06 +00:00
|
|
|
|
tpdu[1] = (0xc0 | 0x20 | 1); /* S-block response */
|
|
|
|
|
tpdu[2] = 1;
|
|
|
|
|
tpdu[3] = ifsc;
|
|
|
|
|
tpdulen = 4;
|
|
|
|
|
edc = compute_edc (tpdu, tpdulen, use_crc);
|
|
|
|
|
if (use_crc)
|
|
|
|
|
tpdu[tpdulen++] = (edc >> 8);
|
|
|
|
|
tpdu[tpdulen++] = edc;
|
2009-06-29 20:54:00 +00:00
|
|
|
|
DEBUGOUT_1 ("T=1: requesting an ifsc=%d\n", ifsc);
|
2009-03-30 12:46:06 +00:00
|
|
|
|
}
|
|
|
|
|
else if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2])
|
|
|
|
|
{
|
|
|
|
|
/* Wait time extension request. */
|
2003-09-05 07:40:41 +00:00
|
|
|
|
unsigned char bwi = tpdu[3];
|
2017-03-27 11:25:00 +09:00
|
|
|
|
|
|
|
|
|
/* Check if it's unsual value which can't be expressed in ATR. */
|
|
|
|
|
if (bwi > 15)
|
|
|
|
|
wait_more = 1;
|
|
|
|
|
|
2003-09-05 07:40:41 +00:00
|
|
|
|
msg = send_buffer;
|
2009-06-29 20:54:00 +00:00
|
|
|
|
tpdu = msg + hdrlen;
|
|
|
|
|
tpdu[0] = nad_byte;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
tpdu[1] = (0xc0 | 0x20 | 3); /* S-block response */
|
|
|
|
|
tpdu[2] = 1;
|
|
|
|
|
tpdu[3] = bwi;
|
|
|
|
|
tpdulen = 4;
|
2004-04-20 14:17:10 +00:00
|
|
|
|
edc = compute_edc (tpdu, tpdulen, use_crc);
|
|
|
|
|
if (use_crc)
|
|
|
|
|
tpdu[tpdulen++] = (edc >> 8);
|
|
|
|
|
tpdu[tpdulen++] = edc;
|
2009-06-29 20:54:00 +00:00
|
|
|
|
DEBUGOUT_1 ("T=1: waittime extension of bwi=%d\n", bwi);
|
2009-07-13 09:59:22 +00:00
|
|
|
|
print_progress (handle);
|
2009-06-29 20:54:00 +00:00
|
|
|
|
}
|
|
|
|
|
else if ( (tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 0 && !tpdu[2])
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT ("T=1: resync ack from reader\n");
|
|
|
|
|
/* Repeat previous block. */
|
|
|
|
|
msg = send_buffer;
|
|
|
|
|
tpdulen = last_tpdulen;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2004-07-16 15:45:25 +00:00
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
2003-09-05 07:40:41 +00:00
|
|
|
|
}
|
|
|
|
|
} /* end T=1 protocol loop. */
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2005-11-28 11:52:25 +00:00
|
|
|
|
/* Send the CCID Secure command to the reader. APDU_BUF should
|
|
|
|
|
contain the APDU template. PIN_MODE defines how the pin gets
|
|
|
|
|
formatted:
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2004-10-06 13:13:51 +00:00
|
|
|
|
1 := The PIN is ASCII encoded and of variable length. The
|
|
|
|
|
length of the PIN entered will be put into Lc by the reader.
|
|
|
|
|
The APDU should me made up of 4 bytes without Lc.
|
|
|
|
|
|
|
|
|
|
PINLEN_MIN and PINLEN_MAX define the limits for the pin length. 0
|
SCD: Clean up. Remove PADLEN for keypad input.
* scd/apdu.c (struct pininfo_s): Use iso7816_pininfo_s.
(struct reader_table_s): Remove last arg from check_keypad method.
(check_pcsc_keypad, check_pcsc_keypad): Remove PIN_PADLEN.
(pcsc_keypad_verify, pcsc_keypad_modify): Don't check PIN_PADLEN.
(send_apdu_ccid, ccid_keypad_operation): Remove PIN_PADLEN.
(apdu_check_keypad, apdu_keypad_verify, apdu_keypad_modify):
Likewise.
* scd/apdu.h (apdu_check_keypad, apdu_keypad_verify)
(apdu_keypad_modify): Remove PIN_PADLEN.
* scd/ccid-driver.c (ccid_transceive_secure): Remove PIN_PADLEN.
* scd/ccid-driver.h (ccid_transceive_secure): Remove PIN_PADLEN.
* scd/iso7816.c (iso7816_check_keypad, iso7816_verify_kp)
(iso7816_change_reference_data_kp): Remove PADLEN.
* scd/iso7816.h (struct iso7816_pininfo_s): Remove PADLEN, PADCHAR.
--
In the OpenPGPcard specification, password comes with no padding. In
GnuPG, we support keypad input for OpenPGPcard only. Thus, it is
useless to try to support padding for keypad input.
2013-01-07 14:20:55 +09:00
|
|
|
|
may be used t enable reasonable defaults.
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2004-10-06 13:13:51 +00:00
|
|
|
|
When called with RESP and NRESP set to NULL, the function will
|
|
|
|
|
merely check whether the reader supports the secure command for the
|
|
|
|
|
given APDU and PIN_MODE. */
|
|
|
|
|
int
|
|
|
|
|
ccid_transceive_secure (ccid_driver_t handle,
|
|
|
|
|
const unsigned char *apdu_buf, size_t apdu_buflen,
|
2013-01-10 10:49:27 +09:00
|
|
|
|
pininfo_t *pininfo,
|
2004-10-06 13:13:51 +00:00
|
|
|
|
unsigned char *resp, size_t maxresplen, size_t *nresp)
|
|
|
|
|
{
|
|
|
|
|
int rc;
|
|
|
|
|
unsigned char send_buffer[10+259], recv_buffer[10+259];
|
|
|
|
|
unsigned char *msg, *tpdu, *p;
|
|
|
|
|
size_t msglen, tpdulen, n;
|
|
|
|
|
unsigned char seqno;
|
|
|
|
|
size_t dummy_nresp;
|
|
|
|
|
int testmode;
|
2007-03-07 20:55:14 +00:00
|
|
|
|
int cherry_mode = 0;
|
2015-06-22 14:31:25 +09:00
|
|
|
|
int add_zero = 0;
|
SCD: API cleanup for keypad handling.
* scd/iso7816.h (struct pininfo_s): Rename from iso7816_pininfo_s.
Change meaning of MODE.
(pininfo_t): Rename from iso7816_pininfo_t.
* scd/sc-copykeys.c: Include "iso7816.h".
* scd/scdaemon.c, scd/command.c: Likewise.
* scd/ccid-driver.c: Include "scdaemon.h" and "iso7816.h".
(ccid_transceive_secure): Follow the change of PININFO_T.
* scd/app.c: Include "apdu.h" after "iso7816.h".
* scd/iso7816.c (iso7816_check_keypad, iso7816_verify_kp)
(iso7816_change_reference_data_kp): Follow the change of API.
* scd/apdu.c (struct reader_table_s): Change API of CHECK_KEYPAD,
KEYPAD_VERIFY, KEYPAD_MODIFY to have arg of PININFO_T.
(check_pcsc_keypad, check_ccid_keypad): Likewise.
(apdu_check_keypad, apdu_keypad_verify, apdu_keypad_modify): Likewise.
(pcsc_keypad_verify, pcsc_keypad_modify, ct_send_apdu)
(pcsc_send_apdu_direct, pcsc_send_apdu_wrapped, pcsc_send_apdu)
(send_apdu_ccid, ccid_keypad_operation, my_rapdu_send_apdu, send_apdu)
(send_le): Follow the change of API.
* scd/apdu.h (apdu_check_keypad, apdu_keypad_verify)
(apdu_keypad_modify): Change the API.
* scd/app-dinsig.c, scd/app-nks.c, scd/app-openpgp.c: Follow the
change.
2013-01-09 14:10:08 +09:00
|
|
|
|
int enable_varlen = 0;
|
2004-10-06 13:13:51 +00:00
|
|
|
|
|
|
|
|
|
testmode = !resp && !nresp;
|
|
|
|
|
|
|
|
|
|
if (!nresp)
|
|
|
|
|
nresp = &dummy_nresp;
|
|
|
|
|
*nresp = 0;
|
|
|
|
|
|
|
|
|
|
if (apdu_buflen >= 4 && apdu_buf[1] == 0x20 && (handle->has_pinpad & 1))
|
|
|
|
|
;
|
|
|
|
|
else if (apdu_buflen >= 4 && apdu_buf[1] == 0x24 && (handle->has_pinpad & 2))
|
2012-01-06 13:50:21 +09:00
|
|
|
|
;
|
2004-10-06 13:13:51 +00:00
|
|
|
|
else
|
scd: Rename 'keypad' to 'pinpad'.
* NEWS: Mention scd changes.
* agent/divert-scd.c (getpin_cb): Change message.
* agent/call-scd.c (inq_needpin): Change the protocol to
POPUPPINPADPROMPT and DISMISSPINPADPROMPT.
* scd/command.c (pin_cb): Likewise.
* scd/apdu.c (struct reader_table_s): Rename member functions.
(check_pcsc_pinpad, pcsc_pinpad_verify, pcsc_pinpad_modify,
check_ccid_pinpad, ccid_pinpad_operation, apdu_check_pinpad
apdu_pinpad_verify, apdu_pinpad_modify): Rename.
* scd/apdu.h (SW_HOST_NO_PINPAD, apdu_check_pinpad)
(apdu_pinpad_verify, apdu_pinpad_modify): Rename.
* scd/iso7816.h (iso7816_check_pinpad): Rename.
* scd/iso7816.c (map_sw): Use SW_HOST_NO_PINPAD.
(iso7816_check_pinpad): Rename.
(iso7816_verify_kp, iso7816_change_reference_data_kp): Follow
the change.
* scd/ccid-driver.h (CCID_DRIVER_ERR_NO_PINPAD): Rename.
* scd/ccid-driver.c (ccid_transceive_secure): Use it.
* scd/app-dinsig.c (verify_pin): Follow the change.
* scd/app-nks.c (verify_pin): Follow the change.
* scd/app-openpgp.c (check_pinpad_request): Rename.
(parse_login_data, verify_a_chv, verify_chv3, do_change_pin): Follow
the change.
* scd/scdaemon.c (oDisablePinpad, oEnablePinpadVarlen): Rename.
* scd/scdaemon.h (opt): Rename to disable_pinpad,
enable_pinpad_varlen.
* tools/gpgconf-comp.c (gc_options_scdaemon): Rename to
disable-pinpad.
2013-02-07 10:07:51 +09:00
|
|
|
|
return CCID_DRIVER_ERR_NO_PINPAD;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
SCD: API cleanup for keypad handling.
* scd/iso7816.h (struct pininfo_s): Rename from iso7816_pininfo_s.
Change meaning of MODE.
(pininfo_t): Rename from iso7816_pininfo_t.
* scd/sc-copykeys.c: Include "iso7816.h".
* scd/scdaemon.c, scd/command.c: Likewise.
* scd/ccid-driver.c: Include "scdaemon.h" and "iso7816.h".
(ccid_transceive_secure): Follow the change of PININFO_T.
* scd/app.c: Include "apdu.h" after "iso7816.h".
* scd/iso7816.c (iso7816_check_keypad, iso7816_verify_kp)
(iso7816_change_reference_data_kp): Follow the change of API.
* scd/apdu.c (struct reader_table_s): Change API of CHECK_KEYPAD,
KEYPAD_VERIFY, KEYPAD_MODIFY to have arg of PININFO_T.
(check_pcsc_keypad, check_ccid_keypad): Likewise.
(apdu_check_keypad, apdu_keypad_verify, apdu_keypad_modify): Likewise.
(pcsc_keypad_verify, pcsc_keypad_modify, ct_send_apdu)
(pcsc_send_apdu_direct, pcsc_send_apdu_wrapped, pcsc_send_apdu)
(send_apdu_ccid, ccid_keypad_operation, my_rapdu_send_apdu, send_apdu)
(send_le): Follow the change of API.
* scd/apdu.h (apdu_check_keypad, apdu_keypad_verify)
(apdu_keypad_modify): Change the API.
* scd/app-dinsig.c, scd/app-nks.c, scd/app-openpgp.c: Follow the
change.
2013-01-09 14:10:08 +09:00
|
|
|
|
if (!pininfo->minlen)
|
|
|
|
|
pininfo->minlen = 1;
|
|
|
|
|
if (!pininfo->maxlen)
|
2013-03-21 09:04:13 +09:00
|
|
|
|
pininfo->maxlen = 15;
|
2004-10-06 13:13:51 +00:00
|
|
|
|
|
|
|
|
|
/* Note that the 25 is the maximum value the SPR532 allows. */
|
SCD: API cleanup for keypad handling.
* scd/iso7816.h (struct pininfo_s): Rename from iso7816_pininfo_s.
Change meaning of MODE.
(pininfo_t): Rename from iso7816_pininfo_t.
* scd/sc-copykeys.c: Include "iso7816.h".
* scd/scdaemon.c, scd/command.c: Likewise.
* scd/ccid-driver.c: Include "scdaemon.h" and "iso7816.h".
(ccid_transceive_secure): Follow the change of PININFO_T.
* scd/app.c: Include "apdu.h" after "iso7816.h".
* scd/iso7816.c (iso7816_check_keypad, iso7816_verify_kp)
(iso7816_change_reference_data_kp): Follow the change of API.
* scd/apdu.c (struct reader_table_s): Change API of CHECK_KEYPAD,
KEYPAD_VERIFY, KEYPAD_MODIFY to have arg of PININFO_T.
(check_pcsc_keypad, check_ccid_keypad): Likewise.
(apdu_check_keypad, apdu_keypad_verify, apdu_keypad_modify): Likewise.
(pcsc_keypad_verify, pcsc_keypad_modify, ct_send_apdu)
(pcsc_send_apdu_direct, pcsc_send_apdu_wrapped, pcsc_send_apdu)
(send_apdu_ccid, ccid_keypad_operation, my_rapdu_send_apdu, send_apdu)
(send_le): Follow the change of API.
* scd/apdu.h (apdu_check_keypad, apdu_keypad_verify)
(apdu_keypad_modify): Change the API.
* scd/app-dinsig.c, scd/app-nks.c, scd/app-openpgp.c: Follow the
change.
2013-01-09 14:10:08 +09:00
|
|
|
|
if (pininfo->minlen < 1 || pininfo->minlen > 25
|
|
|
|
|
|| pininfo->maxlen < 1 || pininfo->maxlen > 25
|
|
|
|
|
|| pininfo->minlen > pininfo->maxlen)
|
2004-10-06 13:13:51 +00:00
|
|
|
|
return CCID_DRIVER_ERR_INV_VALUE;
|
|
|
|
|
|
2006-11-20 16:49:41 +00:00
|
|
|
|
/* We have only tested a few readers so better don't risk anything
|
|
|
|
|
and do not allow the use with other readers. */
|
|
|
|
|
switch (handle->id_vendor)
|
|
|
|
|
{
|
|
|
|
|
case VENDOR_SCM: /* Tested with SPR 532. */
|
|
|
|
|
case VENDOR_KAAN: /* Tested with KAAN Advanced (1.02). */
|
2013-03-21 09:04:13 +09:00
|
|
|
|
case VENDOR_FSIJ: /* Tested with Gnuk (0.21). */
|
|
|
|
|
pininfo->maxlen = 25;
|
2013-01-09 13:24:57 +09:00
|
|
|
|
enable_varlen = 1;
|
2006-11-20 16:49:41 +00:00
|
|
|
|
break;
|
2013-04-24 08:36:31 +09:00
|
|
|
|
case VENDOR_REINER:/* Tested with cyberJack go */
|
2012-01-06 13:50:21 +09:00
|
|
|
|
case VENDOR_VASCO: /* Tested with DIGIPASS 920 */
|
2013-01-09 13:24:57 +09:00
|
|
|
|
enable_varlen = 1;
|
2012-01-06 13:50:21 +09:00
|
|
|
|
break;
|
2007-03-07 20:55:14 +00:00
|
|
|
|
case VENDOR_CHERRY:
|
2015-06-22 14:31:25 +09:00
|
|
|
|
pininfo->maxlen = 15;
|
2013-01-09 13:24:57 +09:00
|
|
|
|
enable_varlen = 1;
|
2007-03-07 20:55:14 +00:00
|
|
|
|
/* The CHERRY XX44 keyboard echos an asterisk for each entered
|
|
|
|
|
character on the keyboard channel. We use a special variant
|
|
|
|
|
of PC_to_RDR_Secure which directs these characters to the
|
|
|
|
|
smart card's bulk-in channel. We also need to append a zero
|
|
|
|
|
Lc byte to the APDU. It seems that it will be replaced with
|
|
|
|
|
the actual length instead of being appended before the APDU
|
|
|
|
|
is send to the card. */
|
2015-06-22 14:31:25 +09:00
|
|
|
|
add_zero = 1;
|
2011-12-02 18:09:58 +01:00
|
|
|
|
if (handle->id_product != CHERRY_ST2000)
|
|
|
|
|
cherry_mode = 1;
|
2007-03-07 20:55:14 +00:00
|
|
|
|
break;
|
2018-07-17 17:11:42 +02:00
|
|
|
|
case VENDOR_NXP:
|
|
|
|
|
if (handle->id_product == CRYPTOUCAN){
|
|
|
|
|
pininfo->maxlen = 25;
|
|
|
|
|
enable_varlen = 1;
|
2019-11-15 15:49:46 +09:00
|
|
|
|
break;
|
2018-07-17 17:11:42 +02:00
|
|
|
|
}
|
2019-11-15 15:49:46 +09:00
|
|
|
|
return CCID_DRIVER_ERR_NOT_SUPPORTED;
|
|
|
|
|
case VENDOR_GEMPC:
|
|
|
|
|
if (handle->id_product == GEMPC_PINPAD)
|
|
|
|
|
{
|
|
|
|
|
enable_varlen = 0;
|
|
|
|
|
pininfo->minlen = 4;
|
|
|
|
|
pininfo->maxlen = 8;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (handle->id_product == GEMPC_EZIO)
|
|
|
|
|
{
|
|
|
|
|
pininfo->maxlen = 25;
|
|
|
|
|
enable_varlen = 1;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return CCID_DRIVER_ERR_NOT_SUPPORTED;
|
2006-11-20 16:49:41 +00:00
|
|
|
|
default:
|
2019-11-15 15:49:46 +09:00
|
|
|
|
if ((handle->id_vendor == VENDOR_VEGA &&
|
|
|
|
|
handle->id_product == VEGA_ALPHA))
|
2013-01-10 10:49:27 +09:00
|
|
|
|
{
|
|
|
|
|
enable_varlen = 0;
|
|
|
|
|
pininfo->minlen = 4;
|
|
|
|
|
pininfo->maxlen = 8;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2006-11-20 16:49:41 +00:00
|
|
|
|
return CCID_DRIVER_ERR_NOT_SUPPORTED;
|
|
|
|
|
}
|
2004-10-06 13:13:51 +00:00
|
|
|
|
|
SCD: API cleanup for keypad handling.
* scd/iso7816.h (struct pininfo_s): Rename from iso7816_pininfo_s.
Change meaning of MODE.
(pininfo_t): Rename from iso7816_pininfo_t.
* scd/sc-copykeys.c: Include "iso7816.h".
* scd/scdaemon.c, scd/command.c: Likewise.
* scd/ccid-driver.c: Include "scdaemon.h" and "iso7816.h".
(ccid_transceive_secure): Follow the change of PININFO_T.
* scd/app.c: Include "apdu.h" after "iso7816.h".
* scd/iso7816.c (iso7816_check_keypad, iso7816_verify_kp)
(iso7816_change_reference_data_kp): Follow the change of API.
* scd/apdu.c (struct reader_table_s): Change API of CHECK_KEYPAD,
KEYPAD_VERIFY, KEYPAD_MODIFY to have arg of PININFO_T.
(check_pcsc_keypad, check_ccid_keypad): Likewise.
(apdu_check_keypad, apdu_keypad_verify, apdu_keypad_modify): Likewise.
(pcsc_keypad_verify, pcsc_keypad_modify, ct_send_apdu)
(pcsc_send_apdu_direct, pcsc_send_apdu_wrapped, pcsc_send_apdu)
(send_apdu_ccid, ccid_keypad_operation, my_rapdu_send_apdu, send_apdu)
(send_le): Follow the change of API.
* scd/apdu.h (apdu_check_keypad, apdu_keypad_verify)
(apdu_keypad_modify): Change the API.
* scd/app-dinsig.c, scd/app-nks.c, scd/app-openpgp.c: Follow the
change.
2013-01-09 14:10:08 +09:00
|
|
|
|
if (enable_varlen)
|
2013-01-09 16:23:55 +09:00
|
|
|
|
pininfo->fixedlen = 0;
|
SCD: API cleanup for keypad handling.
* scd/iso7816.h (struct pininfo_s): Rename from iso7816_pininfo_s.
Change meaning of MODE.
(pininfo_t): Rename from iso7816_pininfo_t.
* scd/sc-copykeys.c: Include "iso7816.h".
* scd/scdaemon.c, scd/command.c: Likewise.
* scd/ccid-driver.c: Include "scdaemon.h" and "iso7816.h".
(ccid_transceive_secure): Follow the change of PININFO_T.
* scd/app.c: Include "apdu.h" after "iso7816.h".
* scd/iso7816.c (iso7816_check_keypad, iso7816_verify_kp)
(iso7816_change_reference_data_kp): Follow the change of API.
* scd/apdu.c (struct reader_table_s): Change API of CHECK_KEYPAD,
KEYPAD_VERIFY, KEYPAD_MODIFY to have arg of PININFO_T.
(check_pcsc_keypad, check_ccid_keypad): Likewise.
(apdu_check_keypad, apdu_keypad_verify, apdu_keypad_modify): Likewise.
(pcsc_keypad_verify, pcsc_keypad_modify, ct_send_apdu)
(pcsc_send_apdu_direct, pcsc_send_apdu_wrapped, pcsc_send_apdu)
(send_apdu_ccid, ccid_keypad_operation, my_rapdu_send_apdu, send_apdu)
(send_le): Follow the change of API.
* scd/apdu.h (apdu_check_keypad, apdu_keypad_verify)
(apdu_keypad_modify): Change the API.
* scd/app-dinsig.c, scd/app-nks.c, scd/app-openpgp.c: Follow the
change.
2013-01-09 14:10:08 +09:00
|
|
|
|
|
2004-10-06 13:13:51 +00:00
|
|
|
|
if (testmode)
|
|
|
|
|
return 0; /* Success */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2013-01-09 16:23:55 +09:00
|
|
|
|
if (pininfo->fixedlen < 0 || pininfo->fixedlen >= 16)
|
|
|
|
|
return CCID_DRIVER_ERR_NOT_SUPPORTED;
|
|
|
|
|
|
2004-10-06 13:13:51 +00:00
|
|
|
|
msg = send_buffer;
|
|
|
|
|
if (handle->id_vendor == VENDOR_SCM)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT ("sending escape sequence to switch to a case 1 APDU\n");
|
2005-11-28 11:52:25 +00:00
|
|
|
|
rc = send_escape_cmd (handle, (const unsigned char*)"\x80\x02\x00", 3,
|
|
|
|
|
NULL, 0, NULL);
|
2004-10-06 13:13:51 +00:00
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2007-03-07 20:55:14 +00:00
|
|
|
|
msg[0] = cherry_mode? 0x89 : PC_to_RDR_Secure;
|
2004-10-06 13:13:51 +00:00
|
|
|
|
msg[5] = 0; /* slot */
|
|
|
|
|
msg[6] = seqno = handle->seqno++;
|
2006-11-20 16:49:41 +00:00
|
|
|
|
msg[7] = 0; /* bBWI */
|
2004-10-06 13:13:51 +00:00
|
|
|
|
msg[8] = 0; /* RFU */
|
|
|
|
|
msg[9] = 0; /* RFU */
|
2012-01-06 13:50:21 +09:00
|
|
|
|
msg[10] = apdu_buf[1] == 0x20 ? 0 : 1;
|
|
|
|
|
/* Perform PIN verification or PIN modification. */
|
2004-10-06 13:13:51 +00:00
|
|
|
|
msg[11] = 0; /* Timeout in seconds. */
|
|
|
|
|
msg[12] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */
|
|
|
|
|
if (handle->id_vendor == VENDOR_SCM)
|
|
|
|
|
{
|
|
|
|
|
/* For the SPR532 the next 2 bytes need to be zero. We do this
|
2007-03-07 20:55:14 +00:00
|
|
|
|
for all SCM products. Kudos to Martin Paljak for this
|
2004-10-06 13:13:51 +00:00
|
|
|
|
hint. */
|
|
|
|
|
msg[13] = msg[14] = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2013-01-09 16:40:41 +09:00
|
|
|
|
msg[13] = pininfo->fixedlen; /* bmPINBlockString:
|
2013-01-10 10:49:27 +09:00
|
|
|
|
0 bits of pin length to insert.
|
|
|
|
|
PIN block size by fixedlen. */
|
2004-10-06 13:13:51 +00:00
|
|
|
|
msg[14] = 0x00; /* bmPINLengthFormat:
|
|
|
|
|
Units are bytes, position is 0. */
|
|
|
|
|
}
|
2007-01-31 14:24:41 +00:00
|
|
|
|
|
2012-01-06 13:50:21 +09:00
|
|
|
|
msglen = 15;
|
|
|
|
|
if (apdu_buf[1] == 0x24)
|
|
|
|
|
{
|
|
|
|
|
msg[msglen++] = 0; /* bInsertionOffsetOld */
|
2013-01-09 16:40:41 +09:00
|
|
|
|
msg[msglen++] = pininfo->fixedlen; /* bInsertionOffsetNew */
|
2012-01-06 13:50:21 +09:00
|
|
|
|
}
|
2007-01-31 14:24:41 +00:00
|
|
|
|
|
2012-01-06 13:50:21 +09:00
|
|
|
|
/* The following is a little endian word. */
|
SCD: API cleanup for keypad handling.
* scd/iso7816.h (struct pininfo_s): Rename from iso7816_pininfo_s.
Change meaning of MODE.
(pininfo_t): Rename from iso7816_pininfo_t.
* scd/sc-copykeys.c: Include "iso7816.h".
* scd/scdaemon.c, scd/command.c: Likewise.
* scd/ccid-driver.c: Include "scdaemon.h" and "iso7816.h".
(ccid_transceive_secure): Follow the change of PININFO_T.
* scd/app.c: Include "apdu.h" after "iso7816.h".
* scd/iso7816.c (iso7816_check_keypad, iso7816_verify_kp)
(iso7816_change_reference_data_kp): Follow the change of API.
* scd/apdu.c (struct reader_table_s): Change API of CHECK_KEYPAD,
KEYPAD_VERIFY, KEYPAD_MODIFY to have arg of PININFO_T.
(check_pcsc_keypad, check_ccid_keypad): Likewise.
(apdu_check_keypad, apdu_keypad_verify, apdu_keypad_modify): Likewise.
(pcsc_keypad_verify, pcsc_keypad_modify, ct_send_apdu)
(pcsc_send_apdu_direct, pcsc_send_apdu_wrapped, pcsc_send_apdu)
(send_apdu_ccid, ccid_keypad_operation, my_rapdu_send_apdu, send_apdu)
(send_le): Follow the change of API.
* scd/apdu.h (apdu_check_keypad, apdu_keypad_verify)
(apdu_keypad_modify): Change the API.
* scd/app-dinsig.c, scd/app-nks.c, scd/app-openpgp.c: Follow the
change.
2013-01-09 14:10:08 +09:00
|
|
|
|
msg[msglen++] = pininfo->maxlen; /* wPINMaxExtraDigit-Maximum. */
|
|
|
|
|
msg[msglen++] = pininfo->minlen; /* wPINMaxExtraDigit-Minimum. */
|
2012-01-06 13:50:21 +09:00
|
|
|
|
|
|
|
|
|
if (apdu_buf[1] == 0x24)
|
|
|
|
|
msg[msglen++] = apdu_buf[2] == 0 ? 0x03 : 0x01;
|
|
|
|
|
/* bConfirmPIN
|
|
|
|
|
* 0x00: new PIN once
|
|
|
|
|
* 0x01: new PIN twice (confirmation)
|
|
|
|
|
* 0x02: old PIN and new PIN once
|
|
|
|
|
* 0x03: old PIN and new PIN twice (confirmation)
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
msg[msglen] = 0x02; /* bEntryValidationCondition:
|
|
|
|
|
Validation key pressed */
|
SCD: API cleanup for keypad handling.
* scd/iso7816.h (struct pininfo_s): Rename from iso7816_pininfo_s.
Change meaning of MODE.
(pininfo_t): Rename from iso7816_pininfo_t.
* scd/sc-copykeys.c: Include "iso7816.h".
* scd/scdaemon.c, scd/command.c: Likewise.
* scd/ccid-driver.c: Include "scdaemon.h" and "iso7816.h".
(ccid_transceive_secure): Follow the change of PININFO_T.
* scd/app.c: Include "apdu.h" after "iso7816.h".
* scd/iso7816.c (iso7816_check_keypad, iso7816_verify_kp)
(iso7816_change_reference_data_kp): Follow the change of API.
* scd/apdu.c (struct reader_table_s): Change API of CHECK_KEYPAD,
KEYPAD_VERIFY, KEYPAD_MODIFY to have arg of PININFO_T.
(check_pcsc_keypad, check_ccid_keypad): Likewise.
(apdu_check_keypad, apdu_keypad_verify, apdu_keypad_modify): Likewise.
(pcsc_keypad_verify, pcsc_keypad_modify, ct_send_apdu)
(pcsc_send_apdu_direct, pcsc_send_apdu_wrapped, pcsc_send_apdu)
(send_apdu_ccid, ccid_keypad_operation, my_rapdu_send_apdu, send_apdu)
(send_le): Follow the change of API.
* scd/apdu.h (apdu_check_keypad, apdu_keypad_verify)
(apdu_keypad_modify): Change the API.
* scd/app-dinsig.c, scd/app-nks.c, scd/app-openpgp.c: Follow the
change.
2013-01-09 14:10:08 +09:00
|
|
|
|
if (pininfo->minlen && pininfo->maxlen && pininfo->minlen == pininfo->maxlen)
|
2012-01-06 13:50:21 +09:00
|
|
|
|
msg[msglen] |= 0x01; /* Max size reached. */
|
|
|
|
|
msglen++;
|
|
|
|
|
|
|
|
|
|
if (apdu_buf[1] == 0x20)
|
2013-01-10 10:49:27 +09:00
|
|
|
|
msg[msglen++] = 0x01; /* bNumberMessage. */
|
2012-01-06 13:50:21 +09:00
|
|
|
|
else
|
|
|
|
|
msg[msglen++] = 0x03; /* bNumberMessage. */
|
|
|
|
|
|
|
|
|
|
msg[msglen++] = 0x09; /* wLangId-Low: English FIXME: use the first entry. */
|
|
|
|
|
msg[msglen++] = 0x04; /* wLangId-High. */
|
|
|
|
|
|
|
|
|
|
if (apdu_buf[1] == 0x20)
|
|
|
|
|
msg[msglen++] = 0; /* bMsgIndex. */
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
msg[msglen++] = 0; /* bMsgIndex1. */
|
|
|
|
|
msg[msglen++] = 1; /* bMsgIndex2. */
|
|
|
|
|
msg[msglen++] = 2; /* bMsgIndex3. */
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-09 16:23:55 +09:00
|
|
|
|
/* Calculate Lc. */
|
|
|
|
|
n = pininfo->fixedlen;
|
|
|
|
|
if (apdu_buf[1] == 0x24)
|
|
|
|
|
n += pininfo->fixedlen;
|
|
|
|
|
|
2004-10-06 13:13:51 +00:00
|
|
|
|
/* bTeoProlog follows: */
|
2012-01-06 13:50:21 +09:00
|
|
|
|
msg[msglen++] = handle->nonnull_nad? ((1 << 4) | 0): 0;
|
|
|
|
|
msg[msglen++] = ((handle->t1_ns & 1) << 6); /* I-block */
|
2013-01-09 16:23:55 +09:00
|
|
|
|
if (n)
|
|
|
|
|
msg[msglen++] = n + 5; /* apdulen should be filled for fixed length. */
|
|
|
|
|
else
|
|
|
|
|
msg[msglen++] = 0; /* The apdulen will be filled in by the reader. */
|
2004-10-06 13:13:51 +00:00
|
|
|
|
/* APDU follows: */
|
2012-01-06 13:50:21 +09:00
|
|
|
|
msg[msglen++] = apdu_buf[0]; /* CLA */
|
|
|
|
|
msg[msglen++] = apdu_buf[1]; /* INS */
|
|
|
|
|
msg[msglen++] = apdu_buf[2]; /* P1 */
|
|
|
|
|
msg[msglen++] = apdu_buf[3]; /* P2 */
|
2015-06-22 14:31:25 +09:00
|
|
|
|
if (add_zero)
|
2007-03-07 20:55:14 +00:00
|
|
|
|
msg[msglen++] = 0;
|
2013-01-09 16:23:55 +09:00
|
|
|
|
else if (pininfo->fixedlen != 0)
|
|
|
|
|
{
|
|
|
|
|
msg[msglen++] = n;
|
|
|
|
|
memset (&msg[msglen], 0xff, n);
|
|
|
|
|
msglen += n;
|
|
|
|
|
}
|
2006-11-20 16:49:41 +00:00
|
|
|
|
/* An EDC is not required. */
|
2004-10-06 13:13:51 +00:00
|
|
|
|
set_msg_len (msg, msglen - 10);
|
|
|
|
|
|
2009-02-24 20:41:44 +00:00
|
|
|
|
rc = bulk_out (handle, msg, msglen, 0);
|
2004-10-06 13:13:51 +00:00
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2004-10-06 13:13:51 +00:00
|
|
|
|
msg = recv_buffer;
|
|
|
|
|
rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
|
2006-11-20 16:49:41 +00:00
|
|
|
|
RDR_to_PC_DataBlock, seqno, 30000, 0);
|
2004-10-06 13:13:51 +00:00
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2004-10-06 13:13:51 +00:00
|
|
|
|
tpdu = msg + 10;
|
|
|
|
|
tpdulen = msglen - 10;
|
2006-11-20 16:49:41 +00:00
|
|
|
|
|
|
|
|
|
if (handle->apdu_level)
|
|
|
|
|
{
|
|
|
|
|
if (resp)
|
|
|
|
|
{
|
|
|
|
|
if (tpdulen > maxresplen)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_2 ("provided buffer too short for received data "
|
|
|
|
|
"(%u/%u)\n",
|
|
|
|
|
(unsigned int)tpdulen, (unsigned int)maxresplen);
|
|
|
|
|
return CCID_DRIVER_ERR_INV_VALUE;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
|
|
|
|
memcpy (resp, tpdu, tpdulen);
|
2006-11-20 16:49:41 +00:00
|
|
|
|
*nresp = tpdulen;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
|
|
|
|
if (tpdulen < 4)
|
2004-10-06 13:13:51 +00:00
|
|
|
|
{
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
|
|
|
|
npth_unprotect ();
|
|
|
|
|
#endif
|
2016-01-27 12:24:05 +09:00
|
|
|
|
libusb_clear_halt (handle->idev, handle->ep_bulk_in);
|
2017-01-28 00:18:11 +09:00
|
|
|
|
#ifdef USE_NPTH
|
|
|
|
|
npth_protect ();
|
|
|
|
|
#endif
|
2011-02-04 12:57:53 +01:00
|
|
|
|
return CCID_DRIVER_ERR_ABORTED;
|
2004-10-06 13:13:51 +00:00
|
|
|
|
}
|
2005-05-20 20:39:36 +00:00
|
|
|
|
if (debug_level > 1)
|
|
|
|
|
DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
|
|
|
|
|
((msg[11] & 0xc0) == 0x80)? 'R' :
|
|
|
|
|
(msg[11] & 0x80)? 'S' : 'I',
|
|
|
|
|
((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
|
|
|
|
|
((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0,
|
|
|
|
|
(!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
|
2004-10-06 13:13:51 +00:00
|
|
|
|
|
|
|
|
|
if (!(tpdu[1] & 0x80))
|
|
|
|
|
{ /* This is an I-block. */
|
|
|
|
|
/* Last block sent was successful. */
|
|
|
|
|
handle->t1_ns ^= 1;
|
|
|
|
|
|
|
|
|
|
if (!!(tpdu[1] & 0x40) != handle->t1_nr)
|
2015-11-16 12:41:46 +01:00
|
|
|
|
{ /* Response does not match our sequence number. */
|
2004-10-06 13:13:51 +00:00
|
|
|
|
DEBUGOUT ("I-block with wrong seqno received\n");
|
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
handle->t1_nr ^= 1;
|
|
|
|
|
|
|
|
|
|
p = tpdu + 3; /* Skip the prologue field. */
|
|
|
|
|
n = tpdulen - 3 - 1; /* Strip the epilogue field. */
|
|
|
|
|
/* fixme: verify the checksum. */
|
|
|
|
|
if (resp)
|
|
|
|
|
{
|
|
|
|
|
if (n > maxresplen)
|
|
|
|
|
{
|
|
|
|
|
DEBUGOUT_2 ("provided buffer too short for received data "
|
|
|
|
|
"(%u/%u)\n",
|
|
|
|
|
(unsigned int)n, (unsigned int)maxresplen);
|
|
|
|
|
return CCID_DRIVER_ERR_INV_VALUE;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
|
|
|
|
memcpy (resp, p, n);
|
2004-10-06 13:13:51 +00:00
|
|
|
|
*nresp += n;
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2004-10-06 13:13:51 +00:00
|
|
|
|
if (!(tpdu[1] & 0x20))
|
|
|
|
|
return 0; /* No chaining requested - ready. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2004-10-06 13:13:51 +00:00
|
|
|
|
DEBUGOUT ("chaining requested but not supported for Secure operation\n");
|
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
|
|
|
|
}
|
|
|
|
|
else if ((tpdu[1] & 0xc0) == 0x80)
|
|
|
|
|
{ /* This is a R-block. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
if ( (tpdu[1] & 0x0f))
|
2004-10-06 13:13:51 +00:00
|
|
|
|
{ /* Error: repeat last block */
|
|
|
|
|
DEBUGOUT ("No retries supported for Secure operation\n");
|
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
|
|
|
|
}
|
2005-05-20 20:39:36 +00:00
|
|
|
|
else if (!!(tpdu[1] & 0x10) == handle->t1_ns)
|
2015-11-16 12:41:46 +01:00
|
|
|
|
{ /* Response does not match our sequence number. */
|
2004-10-06 13:13:51 +00:00
|
|
|
|
DEBUGOUT ("R-block with wrong seqno received on more bit\n");
|
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{ /* Send next chunk. */
|
|
|
|
|
DEBUGOUT ("chaining not supported on Secure operation\n");
|
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
else
|
2009-07-01 10:53:02 +00:00
|
|
|
|
{ /* This is a S-block. */
|
2009-06-29 20:54:00 +00:00
|
|
|
|
DEBUGOUT_2 ("T=1: S-block %s received cmd=%d for Secure operation\n",
|
2004-10-06 13:13:51 +00:00
|
|
|
|
(tpdu[1] & 0x20)? "response": "request",
|
|
|
|
|
(tpdu[1] & 0x1f));
|
|
|
|
|
return CCID_DRIVER_ERR_CARD_IO_ERROR;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
}
|
2004-10-06 13:13:51 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef TEST
|
2004-09-30 14:34:34 +00:00
|
|
|
|
|
2004-10-06 13:13:51 +00:00
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
static void
|
|
|
|
|
print_error (int err)
|
|
|
|
|
{
|
|
|
|
|
const char *p;
|
|
|
|
|
char buf[50];
|
|
|
|
|
|
|
|
|
|
switch (err)
|
|
|
|
|
{
|
|
|
|
|
case 0: p = "success";
|
|
|
|
|
case CCID_DRIVER_ERR_OUT_OF_CORE: p = "out of core"; break;
|
|
|
|
|
case CCID_DRIVER_ERR_INV_VALUE: p = "invalid value"; break;
|
|
|
|
|
case CCID_DRIVER_ERR_NO_DRIVER: p = "no driver"; break;
|
|
|
|
|
case CCID_DRIVER_ERR_NOT_SUPPORTED: p = "not supported"; break;
|
|
|
|
|
case CCID_DRIVER_ERR_LOCKING_FAILED: p = "locking failed"; break;
|
|
|
|
|
case CCID_DRIVER_ERR_BUSY: p = "busy"; break;
|
|
|
|
|
case CCID_DRIVER_ERR_NO_CARD: p = "no card"; break;
|
|
|
|
|
case CCID_DRIVER_ERR_CARD_INACTIVE: p = "card inactive"; break;
|
|
|
|
|
case CCID_DRIVER_ERR_CARD_IO_ERROR: p = "card I/O error"; break;
|
|
|
|
|
case CCID_DRIVER_ERR_GENERAL_ERROR: p = "general error"; break;
|
|
|
|
|
case CCID_DRIVER_ERR_NO_READER: p = "no reader"; break;
|
|
|
|
|
case CCID_DRIVER_ERR_ABORTED: p = "aborted"; break;
|
|
|
|
|
default: sprintf (buf, "0x%05x", err); p = buf; break;
|
|
|
|
|
}
|
|
|
|
|
fprintf (stderr, "operation failed: %s\n", p);
|
|
|
|
|
}
|
|
|
|
|
|
2009-02-24 20:41:44 +00:00
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
static void
|
|
|
|
|
print_data (const unsigned char *data, size_t length)
|
|
|
|
|
{
|
|
|
|
|
if (length >= 2)
|
|
|
|
|
{
|
|
|
|
|
fprintf (stderr, "operation status: %02X%02X\n",
|
|
|
|
|
data[length-2], data[length-1]);
|
|
|
|
|
length -= 2;
|
|
|
|
|
}
|
|
|
|
|
if (length)
|
|
|
|
|
{
|
|
|
|
|
fputs (" returned data:", stderr);
|
|
|
|
|
for (; length; length--, data++)
|
|
|
|
|
fprintf (stderr, " %02X", *data);
|
|
|
|
|
putc ('\n', stderr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
|
print_result (int rc, const unsigned char *data, size_t length)
|
|
|
|
|
{
|
|
|
|
|
if (rc)
|
|
|
|
|
print_error (rc);
|
|
|
|
|
else if (data)
|
|
|
|
|
print_data (data, length);
|
|
|
|
|
}
|
|
|
|
|
|
2003-09-02 19:06:34 +00:00
|
|
|
|
int
|
|
|
|
|
main (int argc, char **argv)
|
|
|
|
|
{
|
|
|
|
|
int rc;
|
|
|
|
|
ccid_driver_t ccid;
|
2006-11-20 16:49:41 +00:00
|
|
|
|
int slotstat;
|
2004-09-30 14:34:34 +00:00
|
|
|
|
unsigned char result[512];
|
|
|
|
|
size_t resultlen;
|
2004-10-06 13:13:51 +00:00
|
|
|
|
int no_pinpad = 0;
|
|
|
|
|
int verify_123456 = 0;
|
|
|
|
|
int did_verify = 0;
|
2005-01-13 18:00:46 +00:00
|
|
|
|
int no_poll = 0;
|
2004-09-30 14:34:34 +00:00
|
|
|
|
|
|
|
|
|
if (argc)
|
|
|
|
|
{
|
|
|
|
|
argc--;
|
|
|
|
|
argv++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (argc)
|
|
|
|
|
{
|
|
|
|
|
if ( !strcmp (*argv, "--list"))
|
|
|
|
|
{
|
|
|
|
|
char *p;
|
|
|
|
|
p = ccid_get_reader_list ();
|
|
|
|
|
if (!p)
|
|
|
|
|
return 1;
|
|
|
|
|
fputs (p, stderr);
|
|
|
|
|
free (p);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
else if ( !strcmp (*argv, "--debug"))
|
|
|
|
|
{
|
2006-11-20 16:49:41 +00:00
|
|
|
|
ccid_set_debug_level (ccid_set_debug_level (-1)+1);
|
2004-09-30 14:34:34 +00:00
|
|
|
|
argc--; argv++;
|
|
|
|
|
}
|
2005-01-13 18:00:46 +00:00
|
|
|
|
else if ( !strcmp (*argv, "--no-poll"))
|
|
|
|
|
{
|
|
|
|
|
no_poll = 1;
|
|
|
|
|
argc--; argv++;
|
|
|
|
|
}
|
2004-10-06 13:13:51 +00:00
|
|
|
|
else if ( !strcmp (*argv, "--no-pinpad"))
|
|
|
|
|
{
|
|
|
|
|
no_pinpad = 1;
|
|
|
|
|
argc--; argv++;
|
|
|
|
|
}
|
|
|
|
|
else if ( !strcmp (*argv, "--verify-123456"))
|
|
|
|
|
{
|
|
|
|
|
verify_123456 = 1;
|
|
|
|
|
argc--; argv++;
|
|
|
|
|
}
|
2004-09-30 14:34:34 +00:00
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
2015-11-09 16:15:44 +09:00
|
|
|
|
rc = ccid_open_reader (&ccid, argc? *argv:NULL, NULL);
|
2003-09-02 19:06:34 +00:00
|
|
|
|
if (rc)
|
|
|
|
|
return 1;
|
|
|
|
|
|
2005-01-13 18:00:46 +00:00
|
|
|
|
if (!no_poll)
|
|
|
|
|
ccid_poll (ccid);
|
2003-09-02 19:06:34 +00:00
|
|
|
|
fputs ("getting ATR ...\n", stderr);
|
|
|
|
|
rc = ccid_get_atr (ccid, NULL, 0, NULL);
|
|
|
|
|
if (rc)
|
2004-09-30 14:34:34 +00:00
|
|
|
|
{
|
|
|
|
|
print_error (rc);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
2005-01-13 18:00:46 +00:00
|
|
|
|
if (!no_poll)
|
|
|
|
|
ccid_poll (ccid);
|
2003-09-02 19:06:34 +00:00
|
|
|
|
fputs ("getting slot status ...\n", stderr);
|
2017-11-21 11:52:54 +09:00
|
|
|
|
rc = ccid_slot_status (ccid, &slotstat, 1);
|
2003-09-02 19:06:34 +00:00
|
|
|
|
if (rc)
|
2004-09-30 14:34:34 +00:00
|
|
|
|
{
|
|
|
|
|
print_error (rc);
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
2005-01-13 18:00:46 +00:00
|
|
|
|
if (!no_poll)
|
|
|
|
|
ccid_poll (ccid);
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
fputs ("selecting application OpenPGP ....\n", stderr);
|
2003-09-02 19:06:34 +00:00
|
|
|
|
{
|
|
|
|
|
static unsigned char apdu[] = {
|
|
|
|
|
0, 0xA4, 4, 0, 6, 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01};
|
2004-09-30 14:34:34 +00:00
|
|
|
|
rc = ccid_transceive (ccid,
|
|
|
|
|
apdu, sizeof apdu,
|
|
|
|
|
result, sizeof result, &resultlen);
|
|
|
|
|
print_result (rc, result, resultlen);
|
2003-09-02 19:06:34 +00:00
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
|
2005-01-13 18:00:46 +00:00
|
|
|
|
if (!no_poll)
|
|
|
|
|
ccid_poll (ccid);
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
2004-09-30 14:34:34 +00:00
|
|
|
|
fputs ("getting OpenPGP DO 0x65 ....\n", stderr);
|
2003-09-02 19:06:34 +00:00
|
|
|
|
{
|
2004-09-30 14:34:34 +00:00
|
|
|
|
static unsigned char apdu[] = { 0, 0xCA, 0, 0x65, 254 };
|
|
|
|
|
rc = ccid_transceive (ccid, apdu, sizeof apdu,
|
|
|
|
|
result, sizeof result, &resultlen);
|
|
|
|
|
print_result (rc, result, resultlen);
|
2003-09-02 19:06:34 +00:00
|
|
|
|
}
|
2004-09-30 14:34:34 +00:00
|
|
|
|
|
2004-10-06 13:13:51 +00:00
|
|
|
|
if (!no_pinpad)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!no_pinpad)
|
|
|
|
|
{
|
|
|
|
|
static unsigned char apdu[] = { 0, 0x20, 0, 0x81 };
|
|
|
|
|
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2004-10-06 13:13:51 +00:00
|
|
|
|
if (ccid_transceive_secure (ccid,
|
|
|
|
|
apdu, sizeof apdu,
|
|
|
|
|
1, 0, 0, 0,
|
|
|
|
|
NULL, 0, NULL))
|
|
|
|
|
fputs ("can't verify using a PIN-Pad reader\n", stderr);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
fputs ("verifying CHV1 using the PINPad ....\n", stderr);
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2004-10-06 13:13:51 +00:00
|
|
|
|
rc = ccid_transceive_secure (ccid,
|
|
|
|
|
apdu, sizeof apdu,
|
|
|
|
|
1, 0, 0, 0,
|
|
|
|
|
result, sizeof result, &resultlen);
|
|
|
|
|
print_result (rc, result, resultlen);
|
|
|
|
|
did_verify = 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2004-10-06 13:13:51 +00:00
|
|
|
|
if (verify_123456 && !did_verify)
|
|
|
|
|
{
|
|
|
|
|
fputs ("verifying that CHV1 is 123456....\n", stderr);
|
|
|
|
|
{
|
|
|
|
|
static unsigned char apdu[] = {0, 0x20, 0, 0x81,
|
|
|
|
|
6, '1','2','3','4','5','6'};
|
|
|
|
|
rc = ccid_transceive (ccid, apdu, sizeof apdu,
|
|
|
|
|
result, sizeof result, &resultlen);
|
|
|
|
|
print_result (rc, result, resultlen);
|
|
|
|
|
}
|
|
|
|
|
}
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
2004-10-06 13:13:51 +00:00
|
|
|
|
if (!rc)
|
|
|
|
|
{
|
|
|
|
|
fputs ("getting OpenPGP DO 0x5E ....\n", stderr);
|
|
|
|
|
{
|
|
|
|
|
static unsigned char apdu[] = { 0, 0xCA, 0, 0x5E, 254 };
|
|
|
|
|
rc = ccid_transceive (ccid, apdu, sizeof apdu,
|
|
|
|
|
result, sizeof result, &resultlen);
|
|
|
|
|
print_result (rc, result, resultlen);
|
|
|
|
|
}
|
|
|
|
|
}
|
2004-09-30 14:34:34 +00:00
|
|
|
|
|
|
|
|
|
ccid_close_reader (ccid);
|
2003-09-02 19:06:34 +00:00
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Local Variables:
|
|
|
|
|
* compile-command: "gcc -DTEST -Wall -I/usr/local/include -lusb -g ccid-driver.c"
|
|
|
|
|
* End:
|
|
|
|
|
*/
|
|
|
|
|
#endif /*TEST*/
|
|
|
|
|
#endif /*HAVE_LIBUSB*/
|