[pkg-opensc-commit] [opensc] 190/295: Added support for PIN commands via escape commands

Eric Dorland eric at moszumanska.debian.org
Sat Jun 24 21:11:30 UTC 2017


This is an automated email from the git hooks/post-receive script.

eric pushed a commit to branch master
in repository opensc.

commit 40acedcc218c7770243a94eb5bc9d43078dd2209
Author: Frank Morgner <morgner at informatik.hu-berlin.de>
Date:   Wed Nov 11 00:28:16 2015 +0100

    Added support for PIN commands via escape commands
    
    As defined in BSI TR-03119 to issue SCardTransmit (with Uses
    Pseudo-APDU) instead of SCardControl (with FEATURE_VERIFY_PIN_DIRECT).
    It allows using a very basic PC/SC reader driver without special support
    for PIN verification or modification (such as the default CCID driver on
    Windows).
    
    Also gets IFD vendor information via escape commands.
    
    PC/SC's Get Uid command is now only triggered if enable_escape = true;
    was set by the user to allow disabling wrapped commands on broken
    readers (see https://github.com/OpenSC/OpenSC/issues/810)
---
 etc/opensc.conf.in              |    6 +
 src/libopensc/Makefile.am       |    4 +-
 src/libopensc/Makefile.mak      |    2 +-
 src/libopensc/card.c            |    4 +
 src/libopensc/ccid-types.h      |  281 +++++++++++
 src/libopensc/libopensc.exports |    6 +
 src/libopensc/opensc.h          |    1 +
 src/libopensc/reader-ctapi.c    |    2 +
 src/libopensc/reader-openct.c   |    2 +
 src/libopensc/reader-pcsc.c     |   52 +-
 src/libopensc/reader-tr03119.c  | 1012 +++++++++++++++++++++++++++++++++++++++
 src/libopensc/reader-tr03119.h  |   57 +++
 src/pkcs11/slot.c               |   33 +-
 13 files changed, 1429 insertions(+), 33 deletions(-)

diff --git a/etc/opensc.conf.in b/etc/opensc.conf.in
index bf74b63..5419b44 100644
--- a/etc/opensc.conf.in
+++ b/etc/opensc.conf.in
@@ -103,6 +103,12 @@ app default {
 		# Default: true
 		# enable_pinpad = false;
 		#
+		# Detect reader capabilities with escape commands (wrapped APDUs with
+		# CLA=0xFF as defined by PC/SC pt. 3 and BSI TR-03119, e.g. for getting
+		# the UID, escaped PIN commands and the reader's firmware version)
+		# Default: false
+		# enable_escape = true;
+		#
 		# Use specific pcsc provider.
 		# Default: @DEFAULT_PCSC_PROVIDER@
 		# provider_library = @DEFAULT_PCSC_PROVIDER@
diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am
index d836636..9cd44da 100644
--- a/src/libopensc/Makefile.am
+++ b/src/libopensc/Makefile.am
@@ -12,7 +12,7 @@ noinst_HEADERS = cards.h ctbcs.h internal.h esteid.h muscle.h muscle-filesystem.
 	errors.h types.h compression.h itacns.h iso7816.h \
 	authentic.h iasecc.h iasecc-sdo.h sm.h card-sc-hsm.h \
 	pace.h cwa14890.h cwa-dnie.h card-gids.h aux-data.h \
-	jpki.h sc-ossl-compat.h card-npa.h
+	jpki.h sc-ossl-compat.h card-npa.h ccid-types.h reader-tr03119.h
 
 AM_CPPFLAGS = -DOPENSC_CONF_PATH=\"$(sysconfdir)/opensc.conf\" \
 	-I$(top_srcdir)/src
@@ -30,7 +30,7 @@ libopensc_la_SOURCES = \
 	\
 	muscle.c muscle-filesystem.c \
 	\
-	ctbcs.c reader-ctapi.c reader-pcsc.c reader-openct.c \
+	ctbcs.c reader-ctapi.c reader-pcsc.c reader-openct.c reader-tr03119.c \
 	\
 	card-setcos.c card-miocos.c card-flex.c card-gpk.c \
 	card-cardos.c card-tcos.c card-default.c \
diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak
index 1a3eb2a..a2431d3 100644
--- a/src/libopensc/Makefile.mak
+++ b/src/libopensc/Makefile.mak
@@ -12,7 +12,7 @@ OBJECTS			= \
 	\
 	muscle.obj muscle-filesystem.obj \
 	\
-	ctbcs.obj reader-ctapi.obj reader-pcsc.obj reader-openct.obj \
+	ctbcs.obj reader-ctapi.obj reader-pcsc.obj reader-openct.obj reader-tr03119.obj \
 	\
 	card-setcos.obj card-miocos.obj card-flex.obj card-gpk.obj \
 	card-cardos.obj card-tcos.obj card-default.obj \
diff --git a/src/libopensc/card.c b/src/libopensc/card.c
index dd96e17..73f8ede 100644
--- a/src/libopensc/card.c
+++ b/src/libopensc/card.c
@@ -29,6 +29,7 @@
 #endif
 #include <string.h>
 
+#include "reader-tr03119.h"
 #include "internal.h"
 #include "asn1.h"
 #include "common/compat_strlcpy.h"
@@ -204,6 +205,9 @@ int sc_connect_card(sc_reader_t *reader, sc_card_t **card_out)
 	card->reader = reader;
 	card->ctx = ctx;
 
+	if (reader->flags & SC_READER_ENABLE_ESCAPE)
+		sc_detect_escape_cmds(reader);
+
 	memcpy(&card->atr, &reader->atr, sizeof(card->atr));
 	memcpy(&card->uid, &reader->uid, sizeof(card->uid));
 
diff --git a/src/libopensc/ccid-types.h b/src/libopensc/ccid-types.h
new file mode 100644
index 0000000..59d3cf3
--- /dev/null
+++ b/src/libopensc/ccid-types.h
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2009-2015 Frank Morgner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+/**
+ * @file
+ */
+#ifndef _CCID_TYPES_H
+#define _CCID_TYPES_H
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef _MSC_VER
+#define PACKED
+#pragma pack(push,1)
+#elif defined(__GNUC__)
+#define PACKED __attribute__ ((__packed__))
+#endif
+
+#define USB_REQ_CCID        0xA1
+
+#define CCID_CONTROL_ABORT                  0x01
+#define CCID_CONTROL_GET_CLOCK_FREQUENCIES  0x02
+#define CCID_CONTROL_GET_DATA_RATES 0x03
+
+#define CCID_OPERATION_VERIFY   0x00;
+#define CCID_OPERATION_MODIFY   0x01;
+#define CCID_ENTRY_VALIDATE     0x02
+
+#define CCID_BERROR_CMD_ABORTED 0xff /** Host aborted the current activity */
+#define CCID_BERROR_ICC_MUTE 0xfe /** CCID timed out while talking to the ICC */
+#define CCID_BERROR_XFR_PARITY_ERROR 0xfd /** Parity error while talking to the ICC */
+#define CCID_BERROR_XFR_OVERRUN 0xfc /** Overrun error while talking to the ICC */
+#define CCID_BERROR_HW_ERROR 0xfb /** An all inclusive hardware error occurred */
+#define CCID_BERROR_BAD_ATR_TS 0xf
+#define CCID_BERROR_BAD_ATR_TCK 0xf
+#define CCID_BERROR_ICC_PROTOCOL_NOT_SUPPORTED 0xf6
+#define CCID_BERROR_ICC_CLASS_NOT_SUPPORTED 0xf5
+#define CCID_BERROR_PROCEDURE_BYTE_CONFLICT 0xf4
+#define CCID_BERROR_DEACTIVATED_PROTOCOL 0xf3
+#define CCID_BERROR_BUSY_WITH_AUTO_SEQUENCE 0xf2 /** Automatic Sequence Ongoing */
+#define CCID_BERROR_PIN_TIMEOUT 0xf0
+#define CCID_BERROR_PIN_CANCELLED 0xef
+#define CCID_BERROR_CMD_SLOT_BUSY 0xe0 /** A second command was sent to a slot which was already processing a command. */
+#define CCID_BERROR_CMD_NOT_SUPPORTED 0x00
+#define CCID_BERROR_OK 0x00
+
+#define CCID_BSTATUS_OK_ACTIVE 0x00 /** No error. An ICC is present and active */
+#define CCID_BSTATUS_OK_INACTIVE 0x01 /** No error. ICC is present and inactive */
+#define CCID_BSTATUS_OK_NOICC 0x02 /** No error. No ICC is present */
+#define CCID_BSTATUS_ERROR_ACTIVE 0x40 /** Failed. An ICC is present and active */
+#define CCID_BSTATUS_ERROR_INACTIVE 0x41 /** Failed. ICC is present and inactive */
+#define CCID_BSTATUS_ERROR_NOICC 0x42 /** Failed. No ICC is present */
+
+#define CCID_WLEVEL_DIRECT __constant_cpu_to_le16(0) /** APDU begins and ends with this command */
+#define CCID_WLEVEL_CHAIN_NEXT_XFRBLOCK __constant_cpu_to_le16(1) /** APDU begins with this command, and continue in the next PC_to_RDR_XfrBlock */
+#define CCID_WLEVEL_CHAIN_END __constant_cpu_to_le16(2) /** abData field continues a command APDU and ends the APDU command */
+#define CCID_WLEVEL_CHAIN_CONTINUE __constant_cpu_to_le16(3) /** abData field continues a command APDU and another block is to follow */
+#define CCID_WLEVEL_RESPONSE_IN_DATABLOCK __constant_cpu_to_le16(0x10) /** empty abData field, continuation of response APDU is expected in the next RDR_to_PC_DataBlock */
+
+#define CCID_PIN_ENCODING_BIN   0x00
+#define CCID_PIN_ENCODING_BCD   0x01
+#define CCID_PIN_ENCODING_ASCII 0x02
+#define CCID_PIN_UNITS_BYTES    0x80
+#define CCID_PIN_JUSTIFY_RIGHT  0x04
+#define CCID_PIN_CONFIRM_NEW    0x01
+#define CCID_PIN_INSERT_OLD     0x02
+#define CCID_PIN_NO_MSG         0x00
+#define CCID_PIN_MSG1           0x01
+#define CCID_PIN_MSG2           0x02
+#define CCID_PIN_MSG_REF        0x03
+#define CCID_PIN_MSG_DEFAULT    0xff
+
+#define CCID_SLOTS_UNCHANGED    0x00
+#define CCID_SLOT1_CARD_PRESENT 0x01
+#define CCID_SLOT1_CHANGED      0x02
+#define CCID_SLOT2_CARD_PRESENT 0x04
+#define CCID_SLOT2_CHANGED      0x08
+#define CCID_SLOT3_CARD_PRESENT 0x10
+#define CCID_SLOT3_CHANGED      0x20
+#define CCID_SLOT4_CARD_PRESENT 0x40
+#define CCID_SLOT4_CHANGED      0x80
+
+#define CCID_EXT_APDU_MAX       (4 + 3 + 0xffff + 3)
+#define CCID_SHORT_APDU_MAX     (4 + 1 + 0xff + 1)
+
+struct ccid_class_descriptor {
+	uint8_t  bLength;
+	uint8_t  bDescriptorType;
+	uint16_t bcdCCID;
+	uint8_t  bMaxSlotIndex;
+	uint8_t  bVoltageSupport;
+	uint32_t dwProtocols;
+	uint32_t dwDefaultClock;
+	uint32_t dwMaximumClock;
+	uint8_t  bNumClockSupport;
+	uint32_t dwDataRate;
+	uint32_t dwMaxDataRate;
+	uint8_t  bNumDataRatesSupported;
+	uint32_t dwMaxIFSD;
+	uint32_t dwSynchProtocols;
+	uint32_t dwMechanical;
+	uint32_t dwFeatures;
+	uint32_t dwMaxCCIDMessageLength;
+	uint8_t  bClassGetResponse;
+	uint8_t  bclassEnvelope;
+	uint16_t wLcdLayout;
+	uint8_t  bPINSupport;
+	uint8_t  bMaxCCIDBusySlots;
+} PACKED;
+
+typedef struct {
+	uint8_t  bmFindexDindex;
+	uint8_t  bmTCCKST0;
+	uint8_t  bGuardTimeT0;
+	uint8_t  bWaitingIntegerT0;
+	uint8_t  bClockStop;
+} PACKED abProtocolDataStructure_T0_t;
+typedef struct {
+	uint8_t  bmFindexDindex;
+	uint8_t  bmTCCKST1;
+	uint8_t  bGuardTimeT1;
+	uint8_t  bWaitingIntegersT1;
+	uint8_t  bClockStop;
+	uint8_t  bIFSC;
+	uint8_t  bNadValue;
+} PACKED abProtocolDataStructure_T1_t;
+
+typedef struct {
+	uint8_t  bTimeOut;
+	uint8_t  bmFormatString;
+	uint8_t  bmPINBlockString;
+	uint8_t  bmPINLengthFormat;
+	uint16_t wPINMaxExtraDigit;
+	uint8_t  bEntryValidationCondition;
+	uint8_t  bNumberMessage;
+	uint16_t wLangId;
+	uint8_t  bMsgIndex;
+	uint8_t  bTeoPrologue1;
+	uint16_t bTeoPrologue2;
+} PACKED abPINDataStucture_Verification_t;
+typedef struct {
+	uint8_t  bTimeOut;
+	uint8_t  bmFormatString;
+	uint8_t  bmPINBlockString;
+	uint8_t  bmPINLengthFormat;
+	uint8_t  bInsertionOffsetOld;
+	uint8_t  bInsertionOffsetNew;
+	uint16_t wPINMaxExtraDigit;
+	uint8_t  bConfirmPIN;
+	uint8_t  bEntryValidationCondition;
+	uint8_t  bNumberMessage;
+	uint16_t wLangId;
+	uint8_t  bMsgIndex1;
+} PACKED abPINDataStucture_Modification_t;
+
+typedef struct {
+	uint8_t  bMessageType;
+	uint32_t dwLength;
+	uint8_t  bSlot;
+	uint8_t  bSeq;
+	uint8_t  bBWI;
+	uint16_t wLevelParameter;
+} PACKED PC_to_RDR_XfrBlock_t;
+typedef struct {
+	uint8_t  bMessageType;
+	uint32_t dwLength;
+	uint8_t  bSlot;
+	uint8_t  bSeq;
+	uint8_t  abRFU1;
+	uint16_t abRFU2;
+} PACKED PC_to_RDR_IccPowerOff_t;
+typedef struct {
+	uint8_t  bMessageType;
+	uint32_t dwLength;
+	uint8_t  bSlot;
+	uint8_t  bSeq;
+	uint8_t  abRFU1;
+	uint16_t abRFU2;
+} PACKED PC_to_RDR_GetSlotStatus_t;
+typedef struct {
+	uint8_t  bMessageType;
+	uint32_t dwLength;
+	uint8_t  bSlot;
+	uint8_t  bSeq;
+	uint8_t  abRFU1;
+	uint16_t abRFU2;
+} PACKED PC_to_RDR_GetParameters_t;
+typedef struct {
+	uint8_t  bMessageType;
+	uint32_t dwLength;
+	uint8_t  bSlot;
+	uint8_t  bSeq;
+	uint8_t  abRFU1;
+	uint16_t abRFU2;
+} PACKED PC_to_RDR_ResetParameters_t;
+typedef struct {
+	uint8_t  bMessageType;
+	uint32_t dwLength;
+	uint8_t  bSlot;
+	uint8_t  bSeq;
+	uint8_t  bProtocolNum;
+	uint16_t abRFU;
+} PACKED PC_to_RDR_SetParameters_t;
+typedef struct {
+	uint8_t  bMessageType;
+	uint32_t dwLength;
+	uint8_t  bSlot;
+	uint8_t  bSeq;
+	uint8_t  bBWI;
+	uint16_t wLevelParameter;
+} PACKED PC_to_RDR_Secure_t;
+typedef struct {
+	uint8_t  bMessageType;
+	uint32_t dwLength;
+	uint8_t  bSlot;
+	uint8_t  bSeq;
+	uint8_t  bPowerSelect;
+	uint16_t abRFU;
+} PACKED PC_to_RDR_IccPowerOn_t;
+
+typedef struct {
+	uint8_t  bMessageType;
+	uint32_t dwLength;
+	uint8_t  bSlot;
+	uint8_t  bSeq;
+	uint8_t  bStatus;
+	uint8_t  bError;
+	uint8_t  bClockStatus;
+} PACKED RDR_to_PC_SlotStatus_t;
+typedef struct {
+	uint8_t  bMessageType;
+	uint32_t dwLength;
+	uint8_t  bSlot;
+	uint8_t  bSeq;
+	uint8_t  bStatus;
+	uint8_t  bError;
+	uint8_t  bChainParameter;
+} PACKED RDR_to_PC_DataBlock_t;
+typedef struct {
+	uint8_t  bMessageType;
+	uint32_t dwLength;
+	uint8_t  bSlot;
+	uint8_t  bSeq;
+	uint8_t  bStatus;
+	uint8_t  bError;
+	uint8_t  bProtocolNum;
+} PACKED RDR_to_PC_Parameters_t;
+typedef struct {
+	uint8_t  bMessageType;
+	uint8_t  bmSlotICCState; /* we support 1 slots, so we need 2*1 bits = 1 byte */
+} PACKED RDR_to_PC_NotifySlotChange_t;
+
+#ifdef _MSC_VER
+#undef PACKED
+#pragma pack(pop)
+#elif defined(__GNUC__)
+#undef PACKED
+#endif
+
+#ifdef  __cplusplus
+}
+#endif
+#endif
diff --git a/src/libopensc/libopensc.exports b/src/libopensc/libopensc.exports
index a7027b1..18f8037 100644
--- a/src/libopensc/libopensc.exports
+++ b/src/libopensc/libopensc.exports
@@ -353,3 +353,9 @@ perform_chip_authentication
 npa_default_flags
 npa_reset_retry_counter
 npa_pace_get_tries_left
+escape_pace_input_to_buf
+escape_buf_to_pace_input
+escape_pace_output_to_buf
+escape_buf_to_pace_output
+escape_pace_capabilities_to_buf
+escape_buf_to_pace_capabilities
diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h
index fcd2843..01cbbc3 100644
--- a/src/libopensc/opensc.h
+++ b/src/libopensc/opensc.h
@@ -287,6 +287,7 @@ struct sc_reader_driver {
 #define SC_READER_CARD_EXCLUSIVE	0x00000008
 #define SC_READER_HAS_WAITING_AREA	0x00000010
 #define SC_READER_REMOVED			0x00000020
+#define SC_READER_ENABLE_ESCAPE		0x00000040
 
 /* reader capabilities */
 #define SC_READER_CAP_DISPLAY	0x00000001
diff --git a/src/libopensc/reader-ctapi.c b/src/libopensc/reader-ctapi.c
index 32f9104..0fc01db 100644
--- a/src/libopensc/reader-ctapi.c
+++ b/src/libopensc/reader-ctapi.c
@@ -397,6 +397,8 @@ static int ctapi_load_module(sc_context_t *ctx,
 		if (conf_block) {
 			reader->max_send_size = scconf_get_int(conf_block, "max_send_size", reader->max_send_size);
 			reader->max_recv_size = scconf_get_int(conf_block, "max_recv_size", reader->max_recv_size);
+			if (scconf_get_bool(conf_block, "enable_escape", 0))
+				reader->flags |= SC_READER_ENABLE_ESCAPE;
 		}
 
 		r = _sc_add_reader(ctx, reader);
diff --git a/src/libopensc/reader-openct.c b/src/libopensc/reader-openct.c
index d66d391..9e3bef6 100644
--- a/src/libopensc/reader-openct.c
+++ b/src/libopensc/reader-openct.c
@@ -138,6 +138,8 @@ openct_add_reader(sc_context_t *ctx, unsigned int num, ct_info_t *info)
 	if (conf_block) {
 		reader->max_send_size = scconf_get_int(conf_block, "max_send_size", reader->max_send_size);
 		reader->max_recv_size = scconf_get_int(conf_block, "max_recv_size", reader->max_recv_size);
+		if (scconf_get_bool(conf_block, "enable_escape", 0))
+			reader->flags |= SC_READER_ENABLE_ESCAPE;
 	}
 
 	if ((rc = _sc_add_reader(ctx, reader)) < 0) {
diff --git a/src/libopensc/reader-pcsc.c b/src/libopensc/reader-pcsc.c
index 93bfbbe..bc33cfc 100644
--- a/src/libopensc/reader-pcsc.c
+++ b/src/libopensc/reader-pcsc.c
@@ -472,29 +472,31 @@ static int pcsc_reconnect(sc_reader_t * reader, DWORD action)
 
 static void initialize_uid(sc_reader_t *reader)
 {
-	sc_apdu_t apdu;
-	/* though we only expect 10 bytes max, we want to set the Le to 0x00 to not
-	 * get 0x6282 as SW in case of a UID variant shorter than 10 bytes */
-	u8 rbuf[256];
-
-	memset(&apdu, 0, sizeof(apdu));
-	apdu.cse = SC_APDU_CASE_2_SHORT;
-	apdu.cla = 0xFF;
-	apdu.ins = 0xCA;
-	apdu.p1 = 0x00;
-	apdu.p2 = 0x00;
-	apdu.le = 0x00;
-	apdu.resp = rbuf;
-	apdu.resplen = sizeof rbuf;
-
-	if (SC_SUCCESS == pcsc_transmit(reader, &apdu)
-			&& apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
-		reader->uid.len = apdu.resplen;
-		memcpy(reader->uid.value, apdu.resp, reader->uid.len);
-		sc_debug_hex(reader->ctx, SC_LOG_DEBUG_NORMAL, "UID",
-				reader->uid.value, reader->uid.len);
-	} else {
-		sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "unable to get UID");
+	if (reader->flags & SC_READER_ENABLE_ESCAPE) {
+		sc_apdu_t apdu;
+		/* though we only expect 10 bytes max, we want to set the Le to 0x00 to not
+		 * get 0x6282 as SW in case of a UID variant shorter than 10 bytes */
+		u8 rbuf[256];
+
+		memset(&apdu, 0, sizeof(apdu));
+		apdu.cse = SC_APDU_CASE_2_SHORT;
+		apdu.cla = 0xFF;
+		apdu.ins = 0xCA;
+		apdu.p1 = 0x00;
+		apdu.p2 = 0x00;
+		apdu.le = 0x00;
+		apdu.resp = rbuf;
+		apdu.resplen = sizeof rbuf;
+
+		if (SC_SUCCESS == pcsc_transmit(reader, &apdu)
+				&& apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
+			reader->uid.len = apdu.resplen;
+			memcpy(reader->uid.value, apdu.resp, reader->uid.len);
+			sc_debug_hex(reader->ctx, SC_LOG_DEBUG_NORMAL, "UID",
+					reader->uid.value, reader->uid.len);
+		} else {
+			sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL, "unable to get UID");
+		}
 	}
 }
 
@@ -1306,6 +1308,8 @@ static int pcsc_detect_readers(sc_context_t *ctx)
 			if (conf_block) {
 				reader->max_send_size = scconf_get_int(conf_block, "max_send_size", reader->max_send_size);
 				reader->max_recv_size = scconf_get_int(conf_block, "max_recv_size", reader->max_recv_size);
+				if (scconf_get_bool(conf_block, "enable_escape", 0))
+					reader->flags |= SC_READER_ENABLE_ESCAPE;
 			}
 
 			sc_log(ctx, "reader's max-send-size: %i, max-recv-size: %i", reader->max_send_size, reader->max_recv_size);
@@ -2393,6 +2397,8 @@ int cardmod_use_reader(sc_context_t *ctx, void * pcsc_context_handle, void * pcs
 		if (conf_block) {
 			reader->max_send_size = scconf_get_int(conf_block, "max_send_size", reader->max_send_size);
 			reader->max_recv_size = scconf_get_int(conf_block, "max_recv_size", reader->max_recv_size);
+			if (scconf_get_bool(conf_block, "enable_escape", 0))
+				reader->flags |= SC_READER_ENABLE_ESCAPE;
 		}
 
 		/* attempt to detect protocol in use T0/T1/RAW */
diff --git a/src/libopensc/reader-tr03119.c b/src/libopensc/reader-tr03119.c
new file mode 100644
index 0000000..2aada6e
--- /dev/null
+++ b/src/libopensc/reader-tr03119.c
@@ -0,0 +1,1012 @@
+/*
+ * reader-escape.c: implementation related to escape commands with pseudo APDUs
+ *
+ * Copyright (C) 2013-2015  Frank Morgner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "reader-tr03119.h"
+#include "ccid-types.h"
+#include "internal.h"
+#include "libopensc/asn1.h"
+#include "libopensc/log.h"
+#include "libopensc/opensc.h"
+#include "libopensc/pace.h"
+#include <stdlib.h>
+#include <string.h>
+
+#if _WIN32
+/* FIXME might not always work */
+#define htole16(x) (x)
+#define htole32(x) (x)
+#elif __APPLE__
+#include <libkern/OSByteOrder.h>
+#define htole16(x) OSSwapHostToLittleInt16(x)
+#define htole32(x) OSSwapHostToLittleInt32(x)
+#else
+#ifndef _BSD_SOURCE
+#define _BSD_SOURCE             /* See feature_test_macros(7) */
+#endif
+#include <endian.h>
+#endif
+
+static const u8 escape_cla                          = 0xff;
+static const u8 escape_ins                          = 0x9a;
+
+static const u8 escape_p1_PIN                       = 0x04;
+static const u8 escape_p2_GetReaderPACECapabilities = 0x01;
+static const u8 escape_p2_EstablishPACEChannel      = 0x02;
+/*static const u8 escape_p2_DestroyPACEChannel        = 0x03;*/
+static const u8 escape_p2_PC_to_RDR_Secure          = 0x10;
+
+static const u8 escape_p1_IFD                       = 0x01;
+static const u8 escape_p2_vendor                    = 0x01;
+/*static const u8 escape_p2_product                   = 0x03;*/
+static const u8 escape_p2_version_firmware          = 0x06;
+/*static const u8 escape_p2_version_driver            = 0x07;*/
+
+struct sc_asn1_entry g_boolean[] = {
+	{ "boolean",
+		SC_ASN1_BOOLEAN, SC_ASN1_TAG_BOOLEAN, 0, NULL, NULL },
+	{ NULL , 0 , 0 , 0 , NULL , NULL }
+};
+struct sc_asn1_entry g_int_as_octet_string[] = {
+	{ "int as octet string",
+		SC_ASN1_OCTET_STRING, SC_ASN1_TAG_INTEGER, 0, NULL, NULL },
+	{ NULL , 0 , 0 , 0 , NULL , NULL }
+};
+struct sc_asn1_entry g_octet_string[] = {
+	{ "octet string",
+		SC_ASN1_OCTET_STRING, SC_ASN1_TAG_OCTET_STRING, SC_ASN1_ALLOC, NULL, NULL },
+	{ NULL , 0 , 0 , 0 , NULL , NULL }
+};
+struct sc_asn1_entry g_numeric_string_as_octet_string[] = {
+	{ "utf8string",
+		SC_ASN1_OCTET_STRING, SC_ASN1_TAG_NUMERICSTRING, SC_ASN1_ALLOC, NULL, NULL },
+	{ NULL , 0 , 0 , 0 , NULL , NULL }
+};
+
+static const struct sc_asn1_entry g_EstablishPACEChannelInput_data[] = {
+	{ "passwordID",
+		/* use an OCTET STRING to avoid a conversion to int */
+		SC_ASN1_STRUCT, SC_ASN1_CTX|0x01|SC_ASN1_CONS, 0, NULL, NULL },
+	{ "transmittedPassword",
+		SC_ASN1_STRUCT, SC_ASN1_CTX|0x02|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
+	{ "cHAT",
+		SC_ASN1_STRUCT, SC_ASN1_CTX|0x03|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
+	{ "certificateDescription",
+		SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x04|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
+	{ "hashOID",
+		/* use an OCTET STRING to avoid a conversion to struct sc_object_id */
+		SC_ASN1_STRUCT, SC_ASN1_CTX|0x05|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
+	{ NULL , 0 , 0 , 0 , NULL , NULL }
+};
+static const struct sc_asn1_entry g_EstablishPACEChannelOutput_data[] = {
+	{ "errorCode",
+		SC_ASN1_STRUCT, SC_ASN1_CTX|0x01|SC_ASN1_CONS, 0, NULL, NULL },
+	{ "statusMSESetAT",
+		SC_ASN1_STRUCT, SC_ASN1_CTX|0x02|SC_ASN1_CONS, 0, NULL, NULL },
+	{ "efCardAccess",
+		SC_ASN1_OCTET_STRING, SC_ASN1_CTX|0x03|SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL },
+	{ "idPICC",
+		SC_ASN1_STRUCT, SC_ASN1_CTX|0x04|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
+	{ "curCAR",
+		SC_ASN1_STRUCT, SC_ASN1_CTX|0x05|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
+	{ "prevCAR",
+		SC_ASN1_STRUCT, SC_ASN1_CTX|0x06|SC_ASN1_CONS, SC_ASN1_OPTIONAL|SC_ASN1_ALLOC, NULL, NULL },
+	{ NULL , 0 , 0 , 0 , NULL , NULL }
+};
+static const struct sc_asn1_entry g_EstablishPACEChannel[] = {
+	{ "EstablishPACEChannel",
+		SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE|SC_ASN1_CONS, 0, NULL, NULL },
+	{ NULL , 0 , 0 , 0 , NULL , NULL }
+};
+
+int escape_pace_input_to_buf(sc_context_t *ctx,
+		const struct establish_pace_channel_input *input,
+		unsigned char **asn1, size_t *asn1_len)
+{
+	size_t pin_id_len = sizeof input->pin_id;
+	struct sc_asn1_entry EstablishPACEChannelInput_data[
+		sizeof g_EstablishPACEChannelInput_data/
+		sizeof *g_EstablishPACEChannelInput_data];
+	struct sc_asn1_entry EstablishPACEChannel[
+		sizeof g_EstablishPACEChannel/
+		sizeof *g_EstablishPACEChannel];
+	struct sc_asn1_entry passwordID[
+		sizeof g_int_as_octet_string/
+		sizeof *g_int_as_octet_string];
+	struct sc_asn1_entry transmittedPassword[
+		sizeof g_numeric_string_as_octet_string/
+		sizeof *g_numeric_string_as_octet_string];
+	struct sc_asn1_entry cHAT[
+		sizeof g_octet_string/
+		sizeof *g_octet_string];
+
+	sc_copy_asn1_entry(g_EstablishPACEChannel,
+			EstablishPACEChannel);
+	sc_format_asn1_entry(EstablishPACEChannel,
+			EstablishPACEChannelInput_data, 0, 1);
+
+	sc_copy_asn1_entry(g_EstablishPACEChannelInput_data,
+			EstablishPACEChannelInput_data);
+
+	sc_format_asn1_entry(EstablishPACEChannelInput_data+0,
+			passwordID, 0, 1);
+	sc_copy_asn1_entry(g_int_as_octet_string,
+			passwordID);
+	sc_format_asn1_entry(passwordID,
+			(unsigned char *) &input->pin_id, &pin_id_len, 1);
+
+	if (input->pin) {
+		sc_format_asn1_entry(EstablishPACEChannelInput_data+1,
+				transmittedPassword,
+				0, 1);
+		sc_copy_asn1_entry(g_numeric_string_as_octet_string,
+				transmittedPassword);
+		sc_format_asn1_entry(transmittedPassword,
+				(unsigned char *) input->pin,
+				(size_t *) &input->pin_length, 1);
+	}
+
+	if (input->chat) {
+		sc_format_asn1_entry(EstablishPACEChannelInput_data+2,
+				cHAT,
+				0, 1);
+		sc_copy_asn1_entry(g_octet_string,
+				cHAT);
+		sc_format_asn1_entry(cHAT,
+				(unsigned char *) input->chat,
+				(size_t *) &input->chat_length, 1);
+	}
+
+	if (input->certificate_description) {
+		sc_format_asn1_entry(EstablishPACEChannelInput_data+3,
+				(unsigned char *) input->certificate_description,
+				(size_t *) &input->certificate_description_length, 1);
+	}
+
+	return sc_asn1_encode(ctx, EstablishPACEChannel, asn1, asn1_len);
+}
+
+int escape_buf_to_pace_input(sc_context_t *ctx,
+		const unsigned char *asn1, size_t asn1_len,
+		struct establish_pace_channel_input *input)
+{
+	size_t pin_id_len = sizeof input->pin_id;
+	struct sc_asn1_entry EstablishPACEChannelInput_data[
+		sizeof g_EstablishPACEChannelInput_data/
+		sizeof *g_EstablishPACEChannelInput_data];
+	struct sc_asn1_entry EstablishPACEChannel[
+		sizeof g_EstablishPACEChannel/
+		sizeof *g_EstablishPACEChannel];
+	struct sc_asn1_entry passwordID[
+		sizeof g_int_as_octet_string/
+		sizeof *g_int_as_octet_string];
+	struct sc_asn1_entry transmittedPassword[
+		sizeof g_numeric_string_as_octet_string/
+		sizeof *g_numeric_string_as_octet_string];
+	struct sc_asn1_entry cHAT[
+		sizeof g_octet_string/
+		sizeof *g_octet_string];
+	/* FIXME handle hashOID */
+
+	sc_copy_asn1_entry(g_EstablishPACEChannel,
+			EstablishPACEChannel);
+	sc_format_asn1_entry(EstablishPACEChannel,
+			EstablishPACEChannelInput_data, 0, 0);
+
+	sc_copy_asn1_entry(g_EstablishPACEChannelInput_data,
+			EstablishPACEChannelInput_data);
+
+	sc_format_asn1_entry(EstablishPACEChannelInput_data+0,
+			passwordID, 0, 0);
+	sc_copy_asn1_entry(g_int_as_octet_string,
+			passwordID);
+	sc_format_asn1_entry(passwordID,
+			&input->pin_id, &pin_id_len, 0);
+
+	if (input->pin) {
+		sc_format_asn1_entry(EstablishPACEChannelInput_data+1,
+				transmittedPassword, 0, 0);
+		sc_copy_asn1_entry(g_numeric_string_as_octet_string,
+				transmittedPassword);
+		sc_format_asn1_entry(transmittedPassword,
+				(unsigned char *) &input->pin, &input->pin_length, 0);
+	}
+
+	if (input->chat) {
+		sc_format_asn1_entry(EstablishPACEChannelInput_data+2,
+				cHAT, 0, 0);
+		sc_copy_asn1_entry(g_octet_string,
+				cHAT);
+		sc_format_asn1_entry(cHAT,
+				(unsigned char *) &input->chat, &input->chat_length, 0);
+	}
+
+	if (input->certificate_description) {
+		sc_format_asn1_entry(EstablishPACEChannelInput_data+3,
+				(unsigned char *) &input->certificate_description,
+				&input->certificate_description_length, 0);
+	}
+
+	LOG_TEST_RET(ctx,
+			sc_asn1_decode(ctx, EstablishPACEChannel, asn1, asn1_len, NULL, NULL),
+			"Error decoding EstablishPACEChannel");
+
+	if (pin_id_len != sizeof input->pin_id)
+		return SC_ERROR_UNKNOWN_DATA_RECEIVED;
+
+	return SC_SUCCESS;
+}
+
+int escape_pace_output_to_buf(sc_context_t *ctx,
+		const struct establish_pace_channel_output *output,
+		unsigned char **asn1, size_t *asn1_len)
+{
+	uint16_t status_mse_set_at = ((output->mse_set_at_sw1 & 0xff) << 8) | output->mse_set_at_sw2;
+	size_t result_len = sizeof output->result,
+		   status_mse_set_at_len = sizeof status_mse_set_at;
+	struct sc_asn1_entry EstablishPACEChannelOutput_data[
+		sizeof g_EstablishPACEChannelOutput_data/
+		sizeof *g_EstablishPACEChannelOutput_data];
+	struct sc_asn1_entry EstablishPACEChannel[
+		sizeof g_EstablishPACEChannel/
+		sizeof *g_EstablishPACEChannel];
+	struct sc_asn1_entry errorCode[
+		sizeof g_octet_string/
+		sizeof *g_octet_string];
+	struct sc_asn1_entry statusMSESetAT[
+		sizeof g_octet_string/
+		sizeof *g_octet_string];
+	struct sc_asn1_entry idPICC[
+		sizeof g_octet_string/
+		sizeof *g_octet_string];
+	struct sc_asn1_entry curCAR[
+		sizeof g_octet_string/
+		sizeof *g_octet_string];
+	struct sc_asn1_entry prevCAR[
+		sizeof g_octet_string/
+		sizeof *g_octet_string];
+
+	sc_copy_asn1_entry(g_EstablishPACEChannel,
+			EstablishPACEChannel);
+	sc_format_asn1_entry(EstablishPACEChannel,
+			EstablishPACEChannelOutput_data, 0, 1);
+
+	sc_copy_asn1_entry(g_EstablishPACEChannelOutput_data,
+			EstablishPACEChannelOutput_data);
+
+	sc_format_asn1_entry(EstablishPACEChannelOutput_data+0,
+			errorCode, 0, 1);
+	sc_copy_asn1_entry(g_octet_string,
+			errorCode);
+	sc_format_asn1_entry(errorCode,
+			(unsigned char *) &output->result, &result_len, 1);
+
+	sc_format_asn1_entry(EstablishPACEChannelOutput_data+1,
+			statusMSESetAT, 0, 1);
+	sc_copy_asn1_entry(g_octet_string,
+			statusMSESetAT);
+	sc_format_asn1_entry(statusMSESetAT,
+			&status_mse_set_at, &status_mse_set_at_len, 1);
+
+	if (output->ef_cardaccess) {
+		sc_format_asn1_entry(EstablishPACEChannelOutput_data+2,
+				output->ef_cardaccess, (size_t *) &output->ef_cardaccess_length, 1);
+	}
+
+	if (output->id_icc) {
+		sc_format_asn1_entry(EstablishPACEChannelOutput_data+3,
+				idPICC, 0, 1);
+		sc_copy_asn1_entry(g_octet_string,
+				idPICC);
+		sc_format_asn1_entry(idPICC,
+				output->id_icc, (size_t *) &output->id_icc_length, 1);
+	}
+
+	if (output->recent_car) {
+		sc_format_asn1_entry(EstablishPACEChannelOutput_data+4,
+				curCAR, 0, 1);
+		sc_copy_asn1_entry(g_octet_string,
+				curCAR);
+		sc_format_asn1_entry(curCAR,
+				output->recent_car, (size_t *) &output->recent_car_length, 1);
+	}
+
+	if (output->previous_car) {
+		sc_format_asn1_entry(EstablishPACEChannelOutput_data+5,
+			prevCAR, 0, 1);
+		sc_copy_asn1_entry(g_octet_string,
+				prevCAR);
+		sc_format_asn1_entry(prevCAR,
+			output->previous_car, (size_t *) &output->previous_car_length, 1);
+	}
+
+	return sc_asn1_encode(ctx, EstablishPACEChannel, asn1, asn1_len);
+}
+
+int escape_buf_to_pace_output(sc_context_t *ctx,
+		const unsigned char *asn1, size_t asn1_len,
+		struct establish_pace_channel_output *output)
+{
+	uint16_t status_mse_set_at;
+	size_t result_len = sizeof output->result,
+		   status_mse_set_at_len = sizeof status_mse_set_at;
+	struct sc_asn1_entry EstablishPACEChannelOutput_data[
+		sizeof g_EstablishPACEChannelOutput_data/
+		sizeof *g_EstablishPACEChannelOutput_data];
+	struct sc_asn1_entry EstablishPACEChannel[
+		sizeof g_EstablishPACEChannel/
+		sizeof *g_EstablishPACEChannel];
+	struct sc_asn1_entry errorCode[
+		sizeof g_octet_string/
+		sizeof *g_octet_string];
+	struct sc_asn1_entry statusMSESetAT[
+		sizeof g_octet_string/
+		sizeof *g_octet_string];
+	struct sc_asn1_entry idPICC[
+		sizeof g_octet_string/
+		sizeof *g_octet_string];
+	struct sc_asn1_entry curCAR[
+		sizeof g_octet_string/
+		sizeof *g_octet_string];
+	struct sc_asn1_entry prevCAR[
+		sizeof g_octet_string/
+		sizeof *g_octet_string];
+
+	sc_copy_asn1_entry(g_EstablishPACEChannel,
+			EstablishPACEChannel);
+	sc_format_asn1_entry(EstablishPACEChannel,
+			EstablishPACEChannelOutput_data, 0, 0);
+
+	sc_copy_asn1_entry(g_EstablishPACEChannelOutput_data,
+			EstablishPACEChannelOutput_data);
+	sc_format_asn1_entry(EstablishPACEChannelOutput_data+0,
+			errorCode, 0, 0);
+	sc_format_asn1_entry(EstablishPACEChannelOutput_data+1,
+			statusMSESetAT, 0, 0);
+	sc_format_asn1_entry(EstablishPACEChannelOutput_data+2,
+			&output->ef_cardaccess, &output->ef_cardaccess_length, 0);
+	sc_format_asn1_entry(EstablishPACEChannelOutput_data+3,
+			idPICC, 0, 0);
+	sc_format_asn1_entry(EstablishPACEChannelOutput_data+4,
+			curCAR, 0, 0);
+	sc_format_asn1_entry(EstablishPACEChannelOutput_data+5,
+			prevCAR, 0, 0);
+
+	sc_copy_asn1_entry(g_octet_string,
+			errorCode);
+	sc_format_asn1_entry(errorCode,
+			&output->result, &result_len, 0);
+	/* we already allocated memory for the result */
+	errorCode->flags = 0;
+
+	sc_copy_asn1_entry(g_octet_string,
+			statusMSESetAT);
+	sc_format_asn1_entry(statusMSESetAT,
+			&status_mse_set_at, &status_mse_set_at_len, 0);
+	/* we already allocated memory for the result */
+	statusMSESetAT->flags = 0;
+
+	sc_copy_asn1_entry(g_octet_string,
+			idPICC);
+	sc_format_asn1_entry(idPICC,
+			&output->id_icc, &output->id_icc_length, 0);
+
+	sc_copy_asn1_entry(g_octet_string,
+			curCAR);
+	sc_format_asn1_entry(curCAR,
+			&output->recent_car, &output->recent_car_length, 0);
+
+	sc_copy_asn1_entry(g_octet_string,
+			prevCAR);
+	sc_format_asn1_entry(prevCAR,
+			&output->previous_car, &output->previous_car_length, 0);
+
+	LOG_TEST_RET(ctx,
+			sc_asn1_decode(ctx, EstablishPACEChannel,
+				asn1, asn1_len, NULL, NULL),
+			"Error decoding EstablishPACEChannel");
+
+	if (status_mse_set_at_len != sizeof status_mse_set_at
+			|| result_len != sizeof output->result)
+		return SC_ERROR_UNKNOWN_DATA_RECEIVED;
+
+	output->mse_set_at_sw1 = (status_mse_set_at >> 8) & 0xff;
+	output->mse_set_at_sw2 =  status_mse_set_at       & 0xff;
+
+	return SC_SUCCESS;
+}
+
+#define CCID_PIN_TIMEOUT	30
+#define CCID_DISPLAY_DEFAULT    0xff
+static int escape_pin_cmd_to_buf(sc_context_t *ctx,
+		const struct sc_pin_cmd_data *data,
+		unsigned char **pc_to_rdr_secure, size_t *pc_to_rdr_secure_len)
+{
+	PC_to_RDR_Secure_t *secure;
+	abPINDataStucture_Modification_t *modify;
+	abPINDataStucture_Verification_t *verify;
+	uint16_t wLangId = 0,
+			 bTeoPrologue2 = 0,
+			 wPINMaxExtraDigit;
+	uint8_t bTimeOut = CCID_PIN_TIMEOUT,
+			bNumberMessage = CCID_DISPLAY_DEFAULT,
+			bTeoPrologue1 = 0,
+			bMsgIndex = 0,
+			bMessageType = 0x69,
+			bSlot = 0,
+			bSeq = 0,
+			bBWI = 0xff,
+			wLevelParameter = 0,
+			bEntryValidationCondition = CCID_ENTRY_VALIDATE,
+			bmFormatString, bmPINLengthFormat, bmPINBlockString;
+	const struct sc_pin_cmd_pin *pin_ref;
+	int r;
+	unsigned char *pinapdu = NULL;
+	size_t pinapdu_len = 0;
+
+	if (!data || !pc_to_rdr_secure || !pc_to_rdr_secure_len) {
+		r = SC_ERROR_INVALID_ARGUMENTS;
+		goto err;
+	}
+
+	pin_ref = data->flags & SC_PIN_CMD_IMPLICIT_CHANGE ?
+		&data->pin2 : &data->pin1;
+
+	wPINMaxExtraDigit = htole16(
+			(0xff & pin_ref->min_length) << 8)
+			| (pin_ref->max_length & 0xff);
+
+	bmFormatString = CCID_PIN_UNITS_BYTES
+		| ((pin_ref->offset & 0xf) << 3);
+	switch (pin_ref->encoding) {
+		case SC_PIN_ENCODING_ASCII:
+			bmFormatString |= CCID_PIN_ENCODING_ASCII;
+			break;
+		case SC_PIN_ENCODING_BCD:
+			bmFormatString |= CCID_PIN_ENCODING_BCD;
+			break;
+		default:
+			r = SC_ERROR_INVALID_ARGUMENTS;
+			goto err;
+	}
+
+	/* GLP PINs expect the effective PIN length from bit 4 */
+	bmPINLengthFormat = pin_ref->encoding == SC_PIN_ENCODING_GLP ?
+		0x04 : 0x00;
+
+	if (pin_ref->encoding == SC_PIN_ENCODING_GLP) {
+		/* GLP PIN length is encoded in 4 bits and block size is always 8 bytes */
+		bmPINBlockString = 0x40 | 0x08;
+	} else if (pin_ref->encoding == SC_PIN_ENCODING_ASCII && data->flags & SC_PIN_CMD_NEED_PADDING) {
+		bmPINBlockString = pin_ref->pad_length;
+	} else {
+		bmPINBlockString = 0x00;
+	}
+
+	r = sc_apdu_get_octets(ctx, data->apdu, &pinapdu, &pinapdu_len,
+			SC_PROTO_T1);
+	if (r < 0)
+		goto err;
+
+	switch (data->cmd) {
+		case SC_PIN_CMD_VERIFY:
+			*pc_to_rdr_secure_len = sizeof *secure + 1
+				+ sizeof *verify + pinapdu_len;
+			break;
+
+		case SC_PIN_CMD_CHANGE:
+			*pc_to_rdr_secure_len = sizeof *secure + 1
+				+ sizeof *modify + 3 + pinapdu_len;
+			break;
+
+		default:
+			r = SC_ERROR_INVALID_ARGUMENTS;
+			goto err;
+	}
+
+	*pc_to_rdr_secure = malloc(*pc_to_rdr_secure_len);
+	if (!*pc_to_rdr_secure) {
+		r = SC_ERROR_OUT_OF_MEMORY;
+		goto err;
+	}
+	secure = (PC_to_RDR_Secure_t *) *pc_to_rdr_secure;
+	secure->bMessageType = bMessageType;
+	secure->dwLength = htole32((*pc_to_rdr_secure_len) - sizeof *secure);
+	secure->bSlot = bSlot;
+	secure->bSeq = bSeq;
+	secure->bBWI = bBWI;
+	secure->wLevelParameter = wLevelParameter;
+
+	switch (data->cmd) {
+		case SC_PIN_CMD_VERIFY:
+			/* bPINOperation */
+			*((*pc_to_rdr_secure) + sizeof *secure) = CCID_OPERATION_VERIFY;
+			verify = (abPINDataStucture_Verification_t *)
+				((*pc_to_rdr_secure) + sizeof *secure + 1);
+			verify->bTimeOut = bTimeOut;
+			verify->bmFormatString = bmFormatString;
+			verify->bmPINBlockString = bmPINBlockString;
+			verify->bmPINLengthFormat = bmPINLengthFormat;
+			verify->wPINMaxExtraDigit = wPINMaxExtraDigit;
+			verify->bEntryValidationCondition = bEntryValidationCondition;
+			verify->bNumberMessage = bNumberMessage;
+			verify->wLangId = wLangId;
+			verify->bMsgIndex = bMsgIndex;
+			verify->bTeoPrologue1 = bTeoPrologue1;
+			verify->bTeoPrologue2 = bTeoPrologue2;
+
+			memcpy((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *verify,
+					pinapdu, pinapdu_len);
+			break;
+
+		case SC_PIN_CMD_CHANGE:
+			/* bPINOperation */
+			*((*pc_to_rdr_secure) + sizeof *secure) = CCID_OPERATION_MODIFY;
+			modify = (abPINDataStucture_Modification_t *)
+				((*pc_to_rdr_secure) + sizeof *secure + 1);
+			modify->bTimeOut = bTimeOut;
+			modify->bmFormatString = bmFormatString;
+			modify->bmPINBlockString = bmPINBlockString;
+			modify->bmPINLengthFormat = bmPINLengthFormat;
+			if (!(data->flags & SC_PIN_CMD_IMPLICIT_CHANGE)
+					&& data->pin1.offset) {
+				modify->bInsertionOffsetOld = data->pin1.offset - 5;
+			} else {
+				modify->bInsertionOffsetOld = 0;
+			}
+			modify->bInsertionOffsetNew = data->pin2.offset ? data->pin2.offset - 5 : 0;
+			modify->wPINMaxExtraDigit = wPINMaxExtraDigit;
+			modify->bConfirmPIN = CCID_PIN_CONFIRM_NEW
+				| (data->flags & SC_PIN_CMD_IMPLICIT_CHANGE ? 0 : CCID_PIN_INSERT_OLD);
+			modify->bEntryValidationCondition = bEntryValidationCondition;
+			modify->bNumberMessage = bNumberMessage;
+			modify->wLangId = wLangId;
+			modify->bMsgIndex1 = bMsgIndex;
+			*((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *modify + 0) =
+				bTeoPrologue1;
+			*((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *modify + 1) =
+				bTeoPrologue1;
+			*((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *modify + 2) =
+				bTeoPrologue1;
+
+			memcpy((*pc_to_rdr_secure) + sizeof *secure + 1 + sizeof *modify + 3,
+					pinapdu, pinapdu_len);
+			break;
+
+		default:
+			r = SC_ERROR_INVALID_ARGUMENTS;
+			goto err;
+	}
+
+	r = SC_SUCCESS;
+
+err:
+	free(pinapdu);
+	if (r < 0 && pc_to_rdr_secure && *pc_to_rdr_secure) {
+		free(*pc_to_rdr_secure);
+		*pc_to_rdr_secure = NULL;
+	}
+
+	return r;
+}
+
+#define CCID_BSTATUS_OK_ACTIVE 0x00 /** No error. An ICC is present and active */
+static int escape_buf_to_verify_result(sc_context_t *ctx,
+		const unsigned char *rdr_to_pc_datablock,
+		size_t rdr_to_pc_datablock_len,
+		sc_apdu_t *apdu)
+{
+	RDR_to_PC_DataBlock_t *datablock =
+		(RDR_to_PC_DataBlock_t *) rdr_to_pc_datablock;
+
+	if (!rdr_to_pc_datablock
+			|| rdr_to_pc_datablock_len < sizeof *datablock
+			|| datablock->bMessageType != 0x80)
+		return SC_ERROR_UNKNOWN_DATA_RECEIVED;
+
+	if (datablock->bStatus != CCID_BSTATUS_OK_ACTIVE)
+		return SC_ERROR_TRANSMIT_FAILED;
+
+	return sc_apdu_set_resp(ctx, apdu,
+			rdr_to_pc_datablock + sizeof *datablock,
+			htole32(datablock->dwLength));
+}
+
+static int escape_perform_verify(struct sc_reader *reader,
+		struct sc_pin_cmd_data *data)
+{
+	u8 rbuf[0xff];
+	sc_apdu_t apdu;
+	int r;
+
+	memset(&apdu, 0, sizeof(apdu));
+	apdu.cse     = SC_APDU_CASE_4_SHORT;
+	apdu.cla     = escape_cla;
+	apdu.ins     = escape_ins;
+	apdu.p1      = escape_p1_PIN;
+	apdu.p2      = escape_p2_PC_to_RDR_Secure;
+	apdu.resp    = rbuf;
+	apdu.resplen = sizeof rbuf;
+	apdu.le      = sizeof rbuf;
+
+	if (!reader || !reader->ops || !reader->ops->transmit) {
+		r = SC_ERROR_NOT_SUPPORTED;
+		goto err;
+	}
+
+	r = escape_pin_cmd_to_buf(reader->ctx, data,
+			(unsigned char **) &apdu.data, &apdu.datalen);
+	if (r < 0) {
+		sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
+				"Error encoding PC_to_RDR_Secure");
+		goto err;
+	}
+	apdu.lc = apdu.datalen;
+
+	r = SC_SUCCESS;
+
+	r = reader->ops->transmit(reader, &apdu);
+	if (r < 0) {
+		sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
+				"Error performing PC_to_RDR_Secure");
+		goto err;
+	}
+
+	if (apdu.sw1 != 0x90 && apdu.sw2 != 0x00) {
+		sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
+				"Error decoding PC_to_RDR_Secure");
+		r = SC_ERROR_NOT_SUPPORTED;
+		goto err;
+	}
+
+	r = escape_buf_to_verify_result(reader->ctx, apdu.resp, apdu.resplen,
+			data->apdu);
+
+err:
+	free((unsigned char *) apdu.data);
+
+	return r;
+}
+
+static int escape_perform_pace(struct sc_reader *reader,
+		void *establish_pace_channel_input,
+		void *establish_pace_channel_output)
+{
+	u8 rbuf[0xffff];
+	sc_apdu_t apdu;
+	int r;
+	struct establish_pace_channel_input  *input  =
+		establish_pace_channel_input;
+	struct establish_pace_channel_output *output =
+		establish_pace_channel_output;
+
+	memset(&apdu, 0, sizeof(apdu));
+	apdu.cse     = SC_APDU_CASE_4_EXT;
+	apdu.cla     = escape_cla;
+	apdu.ins     = escape_ins;
+	apdu.p1      = escape_p1_PIN;
+	apdu.p2      = escape_p2_EstablishPACEChannel;
+	apdu.resp    = rbuf;
+	apdu.resplen = sizeof rbuf;
+	apdu.le      = sizeof rbuf;
+
+	if (!reader || !reader->ops || !reader->ops->transmit) {
+		r = SC_ERROR_NOT_SUPPORTED;
+		goto err;
+	}
+
+	r = escape_pace_input_to_buf(reader->ctx, input,
+			(unsigned char **) &apdu.data, &apdu.datalen);
+	if (r < 0) {
+		sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
+				"Error encoding EstablishPACEChannel");
+		goto err;
+	}
+	apdu.lc = apdu.datalen;
+
+	r = reader->ops->transmit(reader, &apdu);
+	if (r < 0) {
+		sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
+				"Error performing EstablishPACEChannel");
+		goto err;
+	}
+
+	if (apdu.sw1 != 0x90 && apdu.sw2 != 0x00) {
+		sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
+				"Error decoding EstablishPACEChannel");
+		r = SC_ERROR_NOT_SUPPORTED;
+		goto err;
+	}
+
+	r = escape_buf_to_pace_output(reader->ctx, apdu.resp, apdu.resplen,
+			output);
+
+err:
+	free((unsigned char *) apdu.data);
+
+	return r;
+}
+
+struct sc_asn1_entry g_PACECapabilities_data[] = {
+	{ "capabilityPACE",
+		SC_ASN1_STRUCT, SC_ASN1_CTX|0x01|SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL },
+	{ "capabilityEID",
+		SC_ASN1_STRUCT, SC_ASN1_CTX|0x02|SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL },
+	{ "capabilityESign",
+		SC_ASN1_STRUCT, SC_ASN1_CTX|0x03|SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL },
+	{ "capabilityDestroy",
+		SC_ASN1_STRUCT, SC_ASN1_CTX|0x04|SC_ASN1_CONS, SC_ASN1_ALLOC, NULL, NULL },
+	{ NULL , 0 , 0 , 0 , NULL , NULL }
+};
+struct sc_asn1_entry g_PACECapabilities[] = {
+	{ "PACECapabilities",
+		SC_ASN1_STRUCT, SC_ASN1_TAG_SEQUENCE|SC_ASN1_CONS, 0, NULL, NULL },
+	{ NULL , 0 , 0 , 0 , NULL , NULL }
+};
+
+int escape_buf_to_pace_capabilities(sc_context_t *ctx,
+		const unsigned char *asn1, size_t asn1_len,
+		unsigned long *sc_reader_t_capabilities)
+{
+	int pace = 0, eid = 0, esign = 0, destroy = 0;
+	struct sc_asn1_entry PACECapabilities_data[
+		sizeof g_PACECapabilities_data/
+		sizeof *g_PACECapabilities_data];
+	struct sc_asn1_entry PACECapabilities[
+		sizeof g_PACECapabilities/
+		sizeof *g_PACECapabilities];
+	struct sc_asn1_entry capabilityPACE[
+		sizeof g_boolean/
+		sizeof *g_boolean];
+	struct sc_asn1_entry capabilityEID[
+		sizeof g_boolean/
+		sizeof *g_boolean];
+	struct sc_asn1_entry capabilityESign[
+		sizeof g_boolean/
+		sizeof *g_boolean];
+	struct sc_asn1_entry capabilityDestroy[
+		sizeof g_boolean/
+		sizeof *g_boolean];
+
+	sc_copy_asn1_entry(g_PACECapabilities,
+			PACECapabilities);
+	sc_format_asn1_entry(PACECapabilities,
+			PACECapabilities_data, 0, 1);
+
+	sc_copy_asn1_entry(g_PACECapabilities_data,
+			PACECapabilities_data);
+	sc_format_asn1_entry(PACECapabilities_data+0,
+			&capabilityPACE, NULL, 1);
+	sc_format_asn1_entry(PACECapabilities_data+1,
+			&capabilityEID, NULL, 1);
+	sc_format_asn1_entry(PACECapabilities_data+2,
+			&capabilityESign, NULL, 1);
+	sc_format_asn1_entry(PACECapabilities_data+3,
+			&capabilityDestroy, NULL, 1);
+
+	sc_copy_asn1_entry(g_boolean,
+			capabilityPACE);
+	sc_format_asn1_entry(capabilityPACE+0,
+			&pace, NULL, 0);
+
+	sc_copy_asn1_entry(g_boolean,
+			capabilityEID);
+	sc_format_asn1_entry(capabilityEID+0,
+			&eid, NULL, 0);
+
+	sc_copy_asn1_entry(g_boolean,
+			capabilityESign);
+	sc_format_asn1_entry(capabilityESign+0,
+			&esign, NULL, 0);
+
+	sc_copy_asn1_entry(g_boolean,
+			capabilityDestroy);
+	sc_format_asn1_entry(capabilityDestroy+0,
+			&destroy, NULL, 0);
+
+	LOG_TEST_RET(ctx,
+			sc_asn1_decode(ctx, PACECapabilities,
+				asn1, asn1_len, NULL, NULL),
+			"Error decoding PACECapabilities");
+
+	/* We got a valid PACE Capabilities reply. There is currently no mechanism
+	 * to determine support PIN verification/modification with a escape
+	 * command. Since the reader implements this mechanism it is reasonable to
+	 * assume that PIN verification/modification is available. */
+	*sc_reader_t_capabilities = SC_READER_CAP_PIN_PAD;
+
+	if (pace)
+		*sc_reader_t_capabilities |= SC_READER_CAP_PACE_GENERIC;
+	if (eid)
+		*sc_reader_t_capabilities |= SC_READER_CAP_PACE_EID;
+	if (esign)
+		*sc_reader_t_capabilities |= SC_READER_CAP_PACE_ESIGN;
+	if (destroy)
+		*sc_reader_t_capabilities |= SC_READER_CAP_PACE_DESTROY_CHANNEL;
+
+	return SC_SUCCESS;
+}
+
+int escape_pace_capabilities_to_buf(sc_context_t *ctx,
+		const unsigned long sc_reader_t_capabilities,
+		unsigned char **asn1, size_t *asn1_len)
+{
+	int yes = 1, no = 0;
+	struct sc_asn1_entry PACECapabilities_data[
+		sizeof g_PACECapabilities_data/
+		sizeof *g_PACECapabilities_data];
+	struct sc_asn1_entry PACECapabilities[
+		sizeof g_PACECapabilities/
+		sizeof *g_PACECapabilities];
+	struct sc_asn1_entry capabilityPACE[
+		sizeof g_boolean/
+		sizeof *g_boolean];
+	struct sc_asn1_entry capabilityEID[
+		sizeof g_boolean/
+		sizeof *g_boolean];
+	struct sc_asn1_entry capabilityESign[
+		sizeof g_boolean/
+		sizeof *g_boolean];
+	struct sc_asn1_entry capabilityDestroy[
+		sizeof g_boolean/
+		sizeof *g_boolean];
+
+	sc_copy_asn1_entry(g_EstablishPACEChannel,
+			PACECapabilities);
+	sc_format_asn1_entry(PACECapabilities,
+			PACECapabilities_data, 0, 1);
+
+	sc_copy_asn1_entry(g_PACECapabilities_data,
+			PACECapabilities_data);
+	sc_format_asn1_entry(PACECapabilities_data+0,
+			&capabilityPACE, NULL, 1);
+	sc_format_asn1_entry(PACECapabilities_data+1,
+			&capabilityEID, NULL, 1);
+	sc_format_asn1_entry(PACECapabilities_data+2,
+			&capabilityESign, NULL, 1);
+	sc_format_asn1_entry(PACECapabilities_data+3,
+			&capabilityDestroy, NULL, 1);
+
+	sc_copy_asn1_entry(g_boolean,
+			capabilityPACE);
+	sc_format_asn1_entry(capabilityPACE,
+			sc_reader_t_capabilities & SC_READER_CAP_PACE_GENERIC
+			? &yes : &no, NULL, 1);
+
+	sc_copy_asn1_entry(g_boolean,
+			capabilityEID);
+	sc_format_asn1_entry(capabilityEID,
+			sc_reader_t_capabilities & SC_READER_CAP_PACE_EID
+			? &yes : &no, NULL, 1);
+
+	sc_copy_asn1_entry(g_boolean,
+			capabilityESign);
+	sc_format_asn1_entry(capabilityESign,
+			sc_reader_t_capabilities & SC_READER_CAP_PACE_ESIGN
+			? &yes : &no, NULL, 1);
+
+	sc_copy_asn1_entry(g_boolean,
+			capabilityDestroy);
+	sc_format_asn1_entry(capabilityDestroy,
+			sc_reader_t_capabilities & SC_READER_CAP_PACE_DESTROY_CHANNEL
+			? &yes : &no, NULL, 1);
+
+	return sc_asn1_encode(ctx, PACECapabilities, asn1, asn1_len);
+}
+
+void sc_detect_escape_cmds(sc_reader_t *reader)
+{
+	int error = 0;
+	u8 rbuf[0xff+1];
+	sc_apdu_t apdu;
+	unsigned long capabilities;
+
+	if (reader && reader->ops && reader->ops->transmit) {
+		memset(&apdu, 0, sizeof(apdu));
+		apdu.cse     = SC_APDU_CASE_2_SHORT;
+		apdu.cla     = escape_cla;
+		apdu.ins     = escape_ins;
+		apdu.p1      = escape_p1_PIN;
+		apdu.p2      = escape_p2_GetReaderPACECapabilities;
+		apdu.resp    = rbuf;
+		apdu.resplen = sizeof rbuf;
+		apdu.le      = sizeof rbuf;
+
+		if (reader->ops->transmit(reader, &apdu) == SC_SUCCESS
+				&& apdu.sw1 == 0x90 && apdu.sw2 == 0x00
+				&& escape_buf_to_pace_capabilities(reader->ctx,
+					apdu.resp, apdu.resplen, &capabilities) == SC_SUCCESS) {
+			if (capabilities & SC_READER_CAP_PIN_PAD
+					&& !(reader->capabilities & SC_READER_CAP_PIN_PAD)) {
+				((struct sc_reader_operations *) reader->ops)->perform_verify =
+					escape_perform_verify;
+				sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
+						"Added escape command wrappers for PIN verification/modification to '%s'", reader->name);
+			}
+
+			if (capabilities & SC_READER_CAP_PACE_GENERIC
+					&& !(reader->capabilities & SC_READER_CAP_PACE_GENERIC)) {
+				((struct sc_reader_operations *) reader->ops)->perform_pace =
+					escape_perform_pace;
+				sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
+						"Added escape command wrappers for PACE to '%s'", reader->name);
+			}
+
+			reader->capabilities |= capabilities;
+		} else {
+			error++;
+			sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
+					"%s does not support escape commands", reader->name);
+		}
+
+		apdu.p1      = escape_p1_IFD;
+		apdu.p2      = escape_p2_vendor;
+		apdu.resplen = sizeof rbuf;
+		if (reader->ops->transmit(reader, &apdu) == SC_SUCCESS
+				&& apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
+			if (!reader->vendor) {
+				/* add NUL termination, just in case... */
+				rbuf[apdu.resplen] = '\0';
+				reader->vendor = strdup((const char *) rbuf);
+			}
+		} else {
+			error++;
+		}
+
+		apdu.p1      = escape_p1_IFD;
+		apdu.p2      = escape_p2_version_firmware;
+		apdu.resplen = sizeof rbuf;
+		if (reader->ops->transmit(reader, &apdu) == SC_SUCCESS
+				&& apdu.sw1 == 0x90 && apdu.sw2 == 0x00) {
+			if (!reader->version_major && !reader->version_minor) {
+				unsigned int major = 0, minor = 0;
+				/* add NUL termination, just in case... */
+				rbuf[apdu.resplen] = '\0';
+				sscanf((const char *) rbuf, "%u.%u", &major, &minor);
+				reader->version_major = major>0xff ? 0xff : major;
+				reader->version_minor = minor>0xff ? 0xff : minor;
+			}
+		} else {
+			error++;
+		}
+	}
+
+	if (error) {
+		sc_debug(reader->ctx, SC_LOG_DEBUG_NORMAL,
+				"%d escape command%s failed, need to reset the card",
+				error, error == 1 ? "" : "s");
+		if (reader && reader->ops && reader->ops->transmit) {
+			memset(&apdu, 0, sizeof(apdu));
+			apdu.cse     = SC_APDU_CASE_3_SHORT;
+			apdu.cla     = 0x00;
+			apdu.ins     = 0xA4;
+			apdu.p1      = 8;
+			apdu.p2      = 0x0C;
+			apdu.data    = rbuf;
+			rbuf[0] = 0x3F;
+			rbuf[1] = 0x00;
+			apdu.datalen = 2;
+			apdu.lc      = 2;
+			apdu.resp    = NULL;
+			apdu.resplen = 0;
+			apdu.le      = 0;
+			reader->ops->transmit(reader, &apdu);
+		}
+	}
+}
diff --git a/src/libopensc/reader-tr03119.h b/src/libopensc/reader-tr03119.h
new file mode 100644
index 0000000..089074f
--- /dev/null
+++ b/src/libopensc/reader-tr03119.h
@@ -0,0 +1,57 @@
+/*
+ * reader-tr03119.h: interface related to escape commands with pseudo APDUs
+ *
+ * Copyright (C) 2013-2015  Frank Morgner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _READER_TR03119_H
+#define _READER_TR03119_H
+
+#include "libopensc/opensc.h"
+#include "libopensc/pace.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void sc_detect_escape_cmds(sc_reader_t *reader);
+
+int escape_pace_input_to_buf(sc_context_t *ctx,
+		const struct establish_pace_channel_input *input,
+		unsigned char **asn1, size_t *asn1_len);
+int escape_buf_to_pace_input(sc_context_t *ctx,
+		const unsigned char *asn1, size_t asn1_len,
+		struct establish_pace_channel_input *input);
+int escape_pace_output_to_buf(sc_context_t *ctx,
+		const struct establish_pace_channel_output *output,
+		unsigned char **asn1, size_t *asn1_len);
+int escape_buf_to_pace_output(sc_context_t *ctx,
+		const unsigned char *asn1, size_t asn1_len,
+		struct establish_pace_channel_output *output);
+int escape_pace_capabilities_to_buf(sc_context_t *ctx,
+		const unsigned long sc_reader_t_capabilities,
+		unsigned char **asn1, size_t *asn1_len);
+int escape_buf_to_pace_capabilities(sc_context_t *ctx,
+		const unsigned char *asn1, size_t asn1_len,
+		unsigned long *sc_reader_t_capabilities);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/src/pkcs11/slot.c b/src/pkcs11/slot.c
index ba5e4f4..24891ef 100644
--- a/src/pkcs11/slot.c
+++ b/src/pkcs11/slot.c
@@ -50,13 +50,20 @@ static struct sc_pkcs11_slot * reader_get_slot(sc_reader_t *reader)
 	return NULL;
 }
 
-static void init_slot_info(CK_SLOT_INFO_PTR pInfo)
+static void init_slot_info(CK_SLOT_INFO_PTR pInfo, sc_reader_t *reader)
 {
-	strcpy_bp(pInfo->slotDescription, "Virtual hotplug slot", 64);
-	strcpy_bp(pInfo->manufacturerID, OPENSC_VS_FF_COMPANY_NAME, 32);
+	if (reader) {
+		strcpy_bp(pInfo->slotDescription, reader->name, 64);
+		strcpy_bp(pInfo->manufacturerID, reader->vendor, 32);
+		pInfo->hardwareVersion.major = reader->version_major;
+		pInfo->hardwareVersion.minor = reader->version_minor;
+	} else {
+		strcpy_bp(pInfo->slotDescription, "Virtual hotplug slot", 64);
+		strcpy_bp(pInfo->manufacturerID, OPENSC_VS_FF_COMPANY_NAME, 32);
+		pInfo->hardwareVersion.major = OPENSC_VERSION_MAJOR;
+		pInfo->hardwareVersion.minor = OPENSC_VERSION_MINOR;
+	}
 	pInfo->flags = CKF_REMOVABLE_DEVICE | CKF_HW_SLOT;
-	pInfo->hardwareVersion.major = OPENSC_VERSION_MAJOR;
-	pInfo->hardwareVersion.minor = OPENSC_VERSION_MINOR;
 	pInfo->firmwareVersion.major = 0;
 	pInfo->firmwareVersion.minor = 0;
 }
@@ -105,7 +112,7 @@ CK_RV create_slot(sc_reader_t *reader)
 
 	slot->login_user = -1;
 	slot->id = (CK_SLOT_ID) list_locate(&virtual_slots, slot);
-	init_slot_info(&slot->slot_info);
+	init_slot_info(&slot->slot_info, reader);
 	sc_log(context, "Initializing slot with id 0x%lx", slot->id);
 
 	if (reader != NULL) {
@@ -127,7 +134,7 @@ void empty_slot(struct sc_pkcs11_slot *slot)
 			 * already been reset by `slot_token_removed()`, lists have been
 			 * emptied. We replace the reader with a virtual hotplug slot. */
 			slot->reader = NULL;
-			init_slot_info(&slot->slot_info);
+			init_slot_info(&slot->slot_info, NULL);
 		} else {
 			list_destroy(&slot->objects);
 			list_destroy(&slot->logins);
@@ -273,6 +280,18 @@ again:
 			return sc_to_cryptoki_error(rc, NULL);
 		}
 
+		/* escape commands are only guaranteed to be working with a card
+		 * inserted. That's why by now, after sc_connect_card() the reader's
+		 * metadata may have changed. We re-initialize the metadata for every
+		 * slot of this reader here. */
+		if (reader->flags & SC_READER_ENABLE_ESCAPE) {
+			for (i = 0; i<list_size(&virtual_slots); i++) {
+				sc_pkcs11_slot_t *slot = (sc_pkcs11_slot_t *) list_get_at(&virtual_slots, i);
+				if (slot->reader == reader)
+					init_slot_info(&slot->slot_info, reader);
+			}
+		}
+
 		sc_log(context, "%s: Connected SC card %p", reader->name, p11card->card);
 	}
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-opensc/opensc.git



More information about the pkg-opensc-commit mailing list