summaryrefslogtreecommitdiff
path: root/scd
diff options
context:
space:
mode:
Diffstat (limited to 'scd')
-rw-r--r--scd/Makefile.am19
-rw-r--r--scd/Makefile.in158
-rw-r--r--scd/apdu.c976
-rw-r--r--scd/apdu.h15
-rw-r--r--scd/app-dinsig.c14
-rw-r--r--scd/app-nks.c14
-rw-r--r--scd/app-openpgp.c664
-rw-r--r--scd/app.c153
-rw-r--r--scd/ccid-driver.c838
-rw-r--r--scd/ccid-driver.h5
-rw-r--r--scd/command.c295
-rw-r--r--scd/iso7816.c113
-rw-r--r--scd/iso7816.h34
-rw-r--r--scd/pcsc-wrapper.c164
-rw-r--r--scd/scdaemon-w32info.rc50
-rw-r--r--scd/scdaemon.c36
-rw-r--r--scd/scdaemon.h3
17 files changed, 2458 insertions, 1093 deletions
diff --git a/scd/Makefile.am b/scd/Makefile.am
index 58e2f9b..e883180 100644
--- a/scd/Makefile.am
+++ b/scd/Makefile.am
@@ -17,19 +17,24 @@
## Process this file with automake to produce Makefile.in
-bin_PROGRAMS = scdaemon
-if ! HAVE_W32_SYSTEM
-libexec_PROGRAMS = gnupg-pcsc-wrapper
+if HAVE_W32_SYSTEM
+libexec_PROGRAMS = scdaemon
+else
+libexec_PROGRAMS = scdaemon gnupg-pcsc-wrapper
endif
-EXTRA_DIST = ChangeLog-2011
+EXTRA_DIST = ChangeLog-2011 scdaemon-w32info.rc
AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/intl -I$(top_srcdir)/common
include $(top_srcdir)/am/cmacros.am
-AM_CFLAGS = $(LIBGCRYPT_CFLAGS) \
- $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) $(PTH_CFLAGS)
+if HAVE_W32_SYSTEM
+resource_objs += scdaemon-w32info.o
+endif
+
+AM_CFLAGS = $(GPG_ERROR_CFLAGS) $(LIBGCRYPT_CFLAGS) \
+ $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) $(PTH_CFLAGS)
card_apps = app-openpgp.c app-nks.c app-dinsig.c app-p15.c app-geldkarte.c
@@ -46,7 +51,7 @@ scdaemon_SOURCES = \
scdaemon_LDADD = $(libcommonpth) ../jnlib/libjnlib.a ../gl/libgnu.a \
$(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) $(PTH_LIBS) \
$(LIBUSB_LIBS) $(GPG_ERROR_LIBS) \
- $(LIBINTL) $(DL_LIBS) $(NETLIBS) $(LIBICONV)
+ $(LIBINTL) $(DL_LIBS) $(NETLIBS) $(LIBICONV) $(resource_objs)
# Removed for now: We need to decide whether it makes sense to
# continue it at all, given that gpg has now all required
diff --git a/scd/Makefile.in b/scd/Makefile.in
index d3a924c..71865f6 100644
--- a/scd/Makefile.in
+++ b/scd/Makefile.in
@@ -1,9 +1,9 @@
-# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# Makefile.in generated by automake 1.11.6 from Makefile.am.
# @configure_input@
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
-# Inc.
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software
+# Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
@@ -41,16 +41,33 @@
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
-#
+#
# 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
# along with this program; if not, see <http://www.gnu.org/licenses/>.
VPATH = @srcdir@
+am__make_dryrun = \
+ { \
+ am__dry=no; \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ echo 'am--echo: ; @echo "AM" OK' | $(MAKE) -f - 2>/dev/null \
+ | grep '^AM OK$$' >/dev/null || am__dry=yes;; \
+ *) \
+ for am__flg in $$MAKEFLAGS; do \
+ case $$am__flg in \
+ *=*|--*) ;; \
+ *n*) am__dry=yes; break;; \
+ esac; \
+ done;; \
+ esac; \
+ test $$am__dry = yes; \
+ }
pkgdatadir = $(datadir)/@PACKAGE@
pkgincludedir = $(includedir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
@@ -69,8 +86,9 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
-bin_PROGRAMS = scdaemon$(EXEEXT)
-@HAVE_W32_SYSTEM_FALSE@libexec_PROGRAMS = gnupg-pcsc-wrapper$(EXEEXT)
+@HAVE_W32_SYSTEM_FALSE@libexec_PROGRAMS = scdaemon$(EXEEXT) \
+@HAVE_W32_SYSTEM_FALSE@ gnupg-pcsc-wrapper$(EXEEXT)
+@HAVE_W32_SYSTEM_TRUE@libexec_PROGRAMS = scdaemon$(EXEEXT)
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
$(top_srcdir)/am/cmacros.am
@HAVE_DOSISH_SYSTEM_FALSE@am__append_1 = -DGNUPG_BINDIR="\"$(bindir)\"" \
@@ -88,6 +106,7 @@ DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in \
@GNUPG_SCDAEMON_PGM_TRUE@am__append_4 = -DGNUPG_DEFAULT_SCDAEMON="\"@GNUPG_SCDAEMON_PGM@\""
@GNUPG_DIRMNGR_PGM_TRUE@am__append_5 = -DGNUPG_DEFAULT_DIRMNGR="\"@GNUPG_DIRMNGR_PGM@\""
@GNUPG_PROTECT_TOOL_PGM_TRUE@am__append_6 = -DGNUPG_DEFAULT_PROTECT_TOOL="\"@GNUPG_PROTECT_TOOL_PGM@\""
+@HAVE_W32_SYSTEM_TRUE@am__append_7 = scdaemon-w32info.o
subdir = scd
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/gl/m4/absolute-header.m4 \
@@ -118,8 +137,8 @@ mkinstalldirs = $(SHELL) $(top_srcdir)/scripts/mkinstalldirs
CONFIG_HEADER = $(top_builddir)/config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
-am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)"
-PROGRAMS = $(bin_PROGRAMS) $(libexec_PROGRAMS)
+am__installdirs = "$(DESTDIR)$(libexecdir)"
+PROGRAMS = $(libexec_PROGRAMS)
am_gnupg_pcsc_wrapper_OBJECTS = \
gnupg_pcsc_wrapper-pcsc-wrapper.$(OBJEXT)
gnupg_pcsc_wrapper_OBJECTS = $(am_gnupg_pcsc_wrapper_OBJECTS)
@@ -138,7 +157,7 @@ scdaemon_DEPENDENCIES = $(libcommonpth) ../jnlib/libjnlib.a \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
- $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) $(resource_objs)
DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
depcomp = $(SHELL) $(top_srcdir)/scripts/depcomp
am__depfiles_maybe = depfiles
@@ -149,6 +168,11 @@ CCLD = $(CC)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
SOURCES = $(gnupg_pcsc_wrapper_SOURCES) $(scdaemon_SOURCES)
DIST_SOURCES = $(gnupg_pcsc_wrapper_SOURCES) $(scdaemon_SOURCES)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
ETAGS = etags
CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
@@ -168,7 +192,11 @@ BITSIZEOF_SIG_ATOMIC_T = @BITSIZEOF_SIG_ATOMIC_T@
BITSIZEOF_SIZE_T = @BITSIZEOF_SIZE_T@
BITSIZEOF_WCHAR_T = @BITSIZEOF_WCHAR_T@
BITSIZEOF_WINT_T = @BITSIZEOF_WINT_T@
+BUILD_FILEVERSION = @BUILD_FILEVERSION@
+BUILD_HOSTNAME = @BUILD_HOSTNAME@
BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@
+BUILD_REVISION = @BUILD_REVISION@
+BUILD_TIMESTAMP = @BUILD_TIMESTAMP@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CC_FOR_BUILD = @CC_FOR_BUILD@
@@ -346,17 +374,18 @@ target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
-EXTRA_DIST = ChangeLog-2011
+EXTRA_DIST = ChangeLog-2011 scdaemon-w32info.rc
AM_CPPFLAGS = -I$(top_srcdir)/gl -I$(top_srcdir)/intl \
-I$(top_srcdir)/common -DLOCALEDIR=\"$(localedir)\" \
$(am__append_1) $(am__append_2) $(am__append_3) \
$(am__append_4) $(am__append_5) $(am__append_6)
+resource_objs = $(am__append_7)
# Convenience macros
libcommon = ../common/libcommon.a
libcommonpth = ../common/libcommonpth.a
-AM_CFLAGS = $(LIBGCRYPT_CFLAGS) \
- $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) $(PTH_CFLAGS)
+AM_CFLAGS = $(GPG_ERROR_CFLAGS) $(LIBGCRYPT_CFLAGS) \
+ $(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) $(PTH_CFLAGS)
card_apps = app-openpgp.c app-nks.c app-dinsig.c app-p15.c app-geldkarte.c
scdaemon_SOURCES = \
@@ -370,7 +399,7 @@ scdaemon_SOURCES = \
scdaemon_LDADD = $(libcommonpth) ../jnlib/libjnlib.a ../gl/libgnu.a \
$(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) $(PTH_LIBS) \
$(LIBUSB_LIBS) $(GPG_ERROR_LIBS) \
- $(LIBINTL) $(DL_LIBS) $(NETLIBS) $(LIBICONV)
+ $(LIBINTL) $(DL_LIBS) $(NETLIBS) $(LIBICONV) $(resource_objs)
# Removed for now: We need to decide whether it makes sense to
@@ -397,7 +426,7 @@ gnupg_pcsc_wrapper_CFLAGS =
all: all-am
.SUFFIXES:
-.SUFFIXES: .c .o .obj
+.SUFFIXES: .c .o .obj .rc
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(top_srcdir)/am/cmacros.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
@@ -419,6 +448,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
+$(top_srcdir)/am/cmacros.am:
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
@@ -428,47 +458,13 @@ $(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
-install-binPROGRAMS: $(bin_PROGRAMS)
- @$(NORMAL_INSTALL)
- test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
- @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
- for p in $$list; do echo "$$p $$p"; done | \
- sed 's/$(EXEEXT)$$//' | \
- while read p p1; do if test -f $$p; \
- then echo "$$p"; echo "$$p"; else :; fi; \
- done | \
- sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
- -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
- sed 'N;N;N;s,\n, ,g' | \
- $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
- { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
- if ($$2 == $$4) files[d] = files[d] " " $$1; \
- else { print "f", $$3 "/" $$4, $$1; } } \
- END { for (d in files) print "f", d, files[d] }' | \
- while read type dir files; do \
- if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
- test -z "$$files" || { \
- echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
- $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
- } \
- ; done
-
-uninstall-binPROGRAMS:
- @$(NORMAL_UNINSTALL)
- @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
- files=`for p in $$list; do echo "$$p"; done | \
- sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
- -e 's/$$/$(EXEEXT)/' `; \
- test -n "$$list" || exit 0; \
- echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
- cd "$(DESTDIR)$(bindir)" && rm -f $$files
-
-clean-binPROGRAMS:
- -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
install-libexecPROGRAMS: $(libexec_PROGRAMS)
@$(NORMAL_INSTALL)
- test -z "$(libexecdir)" || $(MKDIR_P) "$(DESTDIR)$(libexecdir)"
@list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \
+ fi; \
for p in $$list; do echo "$$p $$p"; done | \
sed 's/$(EXEEXT)$$//' | \
while read p p1; do if test -f $$p; \
@@ -502,10 +498,10 @@ uninstall-libexecPROGRAMS:
clean-libexecPROGRAMS:
-test -z "$(libexec_PROGRAMS)" || rm -f $(libexec_PROGRAMS)
-gnupg-pcsc-wrapper$(EXEEXT): $(gnupg_pcsc_wrapper_OBJECTS) $(gnupg_pcsc_wrapper_DEPENDENCIES)
+gnupg-pcsc-wrapper$(EXEEXT): $(gnupg_pcsc_wrapper_OBJECTS) $(gnupg_pcsc_wrapper_DEPENDENCIES) $(EXTRA_gnupg_pcsc_wrapper_DEPENDENCIES)
@rm -f gnupg-pcsc-wrapper$(EXEEXT)
$(gnupg_pcsc_wrapper_LINK) $(gnupg_pcsc_wrapper_OBJECTS) $(gnupg_pcsc_wrapper_LDADD) $(LIBS)
-scdaemon$(EXEEXT): $(scdaemon_OBJECTS) $(scdaemon_DEPENDENCIES)
+scdaemon$(EXEEXT): $(scdaemon_OBJECTS) $(scdaemon_DEPENDENCIES) $(EXTRA_scdaemon_DEPENDENCIES)
@rm -f scdaemon$(EXEEXT)
$(LINK) $(scdaemon_OBJECTS) $(scdaemon_LDADD) $(LIBS)
@@ -643,7 +639,7 @@ check-am: all-am
check: check-am
all-am: Makefile $(PROGRAMS)
installdirs:
- for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)"; do \
+ for dir in "$(DESTDIR)$(libexecdir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-am
@@ -656,10 +652,15 @@ install-am: all-am
installcheck: installcheck-am
install-strip:
- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
- `test -z '$(STRIP)' || \
- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
mostlyclean-generic:
clean-generic:
@@ -673,8 +674,7 @@ maintainer-clean-generic:
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
-clean-am: clean-binPROGRAMS clean-generic clean-libexecPROGRAMS \
- mostlyclean-am
+clean-am: clean-generic clean-libexecPROGRAMS mostlyclean-am
distclean: distclean-am
-rm -rf ./$(DEPDIR)
@@ -700,7 +700,7 @@ install-dvi: install-dvi-am
install-dvi-am:
-install-exec-am: install-binPROGRAMS install-libexecPROGRAMS
+install-exec-am: install-libexecPROGRAMS
install-html: install-html-am
@@ -739,24 +739,26 @@ ps: ps-am
ps-am:
-uninstall-am: uninstall-binPROGRAMS uninstall-libexecPROGRAMS
+uninstall-am: uninstall-libexecPROGRAMS
.MAKE: install-am install-strip
-.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
- clean-generic clean-libexecPROGRAMS ctags distclean \
- distclean-compile distclean-generic distclean-tags distdir dvi \
- dvi-am html html-am info info-am install install-am \
- install-binPROGRAMS install-data install-data-am install-dvi \
- install-dvi-am install-exec install-exec-am install-html \
- install-html-am install-info install-info-am \
- install-libexecPROGRAMS install-man install-pdf install-pdf-am \
- install-ps install-ps-am install-strip installcheck \
- installcheck-am installdirs maintainer-clean \
- maintainer-clean-generic mostlyclean mostlyclean-compile \
- mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
- uninstall-am uninstall-binPROGRAMS uninstall-libexecPROGRAMS
-
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-generic \
+ clean-libexecPROGRAMS ctags distclean distclean-compile \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-html install-html-am install-info \
+ install-info-am install-libexecPROGRAMS install-man \
+ install-pdf install-pdf-am install-ps install-ps-am \
+ install-strip installcheck installcheck-am installdirs \
+ maintainer-clean maintainer-clean-generic mostlyclean \
+ mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
+ tags uninstall uninstall-am uninstall-libexecPROGRAMS
+
+
+@HAVE_W32_SYSTEM_TRUE@.rc.o:
+@HAVE_W32_SYSTEM_TRUE@ $(WINDRES) $(DEFAULT_INCLUDES) $(INCLUDES) "$<" "$@"
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
diff --git a/scd/apdu.c b/scd/apdu.c
index b68cd71..9028120 100644
--- a/scd/apdu.c
+++ b/scd/apdu.c
@@ -59,10 +59,10 @@
#include "exechelp.h"
#endif /* GNUPG_MAJOR_VERSION != 1 */
+#include "iso7816.h"
#include "apdu.h"
#include "ccid-driver.h"
-
/* Due to conflicting use of threading libraries we usually can't link
against libpcsclite. Instead we use a wrapper program. */
#ifdef USE_GNU_PTH
@@ -81,15 +81,11 @@
#define DLSTDCALL
#endif
-
-/* Helper to pass parameters related to keypad based operations. */
-struct pininfo_s
-{
- int mode;
- int minlen;
- int maxlen;
- int padlen;
-};
+#if defined(__APPLE__) || defined(_WIN32) || defined(__CYGWIN__)
+typedef unsigned int pcsc_dword_t;
+#else
+typedef unsigned long pcsc_dword_t;
+#endif
/* A structure to collect information pertaining to one reader
slot. */
@@ -105,18 +101,24 @@ struct reader_table_s {
int (*reset_reader)(int);
int (*get_status_reader)(int, unsigned int *);
int (*send_apdu_reader)(int,unsigned char *,size_t,
- unsigned char *, size_t *, struct pininfo_s *);
- int (*check_keypad)(int, int, int, int, int, int);
+ unsigned char *, size_t *, pininfo_t *);
+ int (*check_pinpad)(int, int, pininfo_t *);
void (*dump_status_reader)(int);
int (*set_progress_cb)(int, gcry_handler_progress_t, void*);
+ int (*pinpad_verify)(int, int, int, int, int, pininfo_t *);
+ int (*pinpad_modify)(int, int, int, int, int, pininfo_t *);
struct {
ccid_driver_t handle;
} ccid;
struct {
- unsigned long context;
- unsigned long card;
- unsigned long protocol;
+ long context;
+ long card;
+ pcsc_dword_t protocol;
+ pcsc_dword_t verify_ioctl;
+ pcsc_dword_t modify_ioctl;
+ int pinmin;
+ int pinmax;
#ifdef NEED_PCSC_WRAPPER
int req_fd;
int rsp_fd;
@@ -133,6 +135,10 @@ struct reader_table_s {
int last_status;
int status;
int is_t0; /* True if we know that we are running T=0. */
+ int is_spr532; /* True if we know that the reader is a SPR532. */
+ int pinpad_varlen_supported; /* True if we know that the reader
+ supports variable length pinpad
+ input. */
unsigned char atr[33];
size_t atrlen; /* A zero length indicates that the ATR has
not yet been read; i.e. the card is not
@@ -216,8 +222,34 @@ static char (* DLSTDCALL CT_close) (unsigned short ctn);
#define PCSC_E_SYSTEM_CANCELLED 0x80100012
#define PCSC_E_NOT_TRANSACTED 0x80100016
#define PCSC_E_READER_UNAVAILABLE 0x80100017
+#define PCSC_E_NO_SERVICE 0x8010001D
#define PCSC_W_REMOVED_CARD 0x80100069
+/* Fix pcsc-lite ABI incompatibilty. */
+#ifndef SCARD_CTL_CODE
+#ifdef _WIN32
+#include <winioctl.h>
+#define SCARD_CTL_CODE(code) CTL_CODE(FILE_DEVICE_SMARTCARD, (code), \
+ METHOD_BUFFERED, FILE_ANY_ACCESS)
+#else
+#define SCARD_CTL_CODE(code) (0x42000000 + (code))
+#endif
+#endif
+
+#define CM_IOCTL_GET_FEATURE_REQUEST SCARD_CTL_CODE(3400)
+#define CM_IOCTL_VENDOR_IFD_EXCHANGE SCARD_CTL_CODE(1)
+#define FEATURE_VERIFY_PIN_DIRECT 0x06
+#define FEATURE_MODIFY_PIN_DIRECT 0x07
+#define FEATURE_GET_TLV_PROPERTIES 0x12
+
+#define PCSCv2_PART10_PROPERTY_bEntryValidationCondition 2
+#define PCSCv2_PART10_PROPERTY_bTimeOut2 3
+#define PCSCv2_PART10_PROPERTY_bMinPINSize 6
+#define PCSCv2_PART10_PROPERTY_bMaxPINSize 7
+#define PCSCv2_PART10_PROPERTY_wIdVendor 11
+#define PCSCv2_PART10_PROPERTY_wIdProduct 12
+
+
/* The PC/SC error is defined as a long as per specs. Due to left
shifts bit 31 will get sign extended. We use this mask to fix
it. */
@@ -232,71 +264,89 @@ struct pcsc_io_request_s
typedef struct pcsc_io_request_s *pcsc_io_request_t;
+#ifdef __APPLE__
+#pragma pack(1)
+#endif
+
struct pcsc_readerstate_s
{
const char *reader;
void *user_data;
- unsigned long current_state;
- unsigned long event_state;
- unsigned long atrlen;
+ pcsc_dword_t current_state;
+ pcsc_dword_t event_state;
+ pcsc_dword_t atrlen;
unsigned char atr[33];
};
+#ifdef __APPLE__
+#pragma pack()
+#endif
+
typedef struct pcsc_readerstate_s *pcsc_readerstate_t;
-long (* DLSTDCALL pcsc_establish_context) (unsigned long scope,
+long (* DLSTDCALL pcsc_establish_context) (pcsc_dword_t scope,
const void *reserved1,
const void *reserved2,
- unsigned long *r_context);
-long (* DLSTDCALL pcsc_release_context) (unsigned long context);
-long (* DLSTDCALL pcsc_list_readers) (unsigned long context,
+ long *r_context);
+long (* DLSTDCALL pcsc_release_context) (long context);
+long (* DLSTDCALL pcsc_list_readers) (long context,
const char *groups,
- char *readers, unsigned long*readerslen);
-long (* DLSTDCALL pcsc_get_status_change) (unsigned long context,
- unsigned long timeout,
+ char *readers, pcsc_dword_t*readerslen);
+long (* DLSTDCALL pcsc_get_status_change) (long context,
+ pcsc_dword_t timeout,
pcsc_readerstate_t readerstates,
- unsigned long nreaderstates);
-long (* DLSTDCALL pcsc_connect) (unsigned long context,
+ pcsc_dword_t nreaderstates);
+long (* DLSTDCALL pcsc_connect) (long context,
const char *reader,
- unsigned long share_mode,
- unsigned long preferred_protocols,
- unsigned long *r_card,
- unsigned long *r_active_protocol);
-long (* DLSTDCALL pcsc_reconnect) (unsigned long card,
- unsigned long share_mode,
- unsigned long preferred_protocols,
- unsigned long initialization,
- unsigned long *r_active_protocol);
-long (* DLSTDCALL pcsc_disconnect) (unsigned long card,
- unsigned long disposition);
-long (* DLSTDCALL pcsc_status) (unsigned long card,
- char *reader, unsigned long *readerlen,
- unsigned long *r_state,
- unsigned long *r_protocol,
- unsigned char *atr, unsigned long *atrlen);
-long (* DLSTDCALL pcsc_begin_transaction) (unsigned long card);
-long (* DLSTDCALL pcsc_end_transaction) (unsigned long card,
- unsigned long disposition);
-long (* DLSTDCALL pcsc_transmit) (unsigned long card,
+ pcsc_dword_t share_mode,
+ pcsc_dword_t preferred_protocols,
+ long *r_card,
+ pcsc_dword_t *r_active_protocol);
+long (* DLSTDCALL pcsc_reconnect) (long card,
+ pcsc_dword_t share_mode,
+ pcsc_dword_t preferred_protocols,
+ pcsc_dword_t initialization,
+ pcsc_dword_t *r_active_protocol);
+long (* DLSTDCALL pcsc_disconnect) (long card,
+ pcsc_dword_t disposition);
+long (* DLSTDCALL pcsc_status) (long card,
+ char *reader, pcsc_dword_t *readerlen,
+ pcsc_dword_t *r_state,
+ pcsc_dword_t *r_protocol,
+ unsigned char *atr, pcsc_dword_t *atrlen);
+long (* DLSTDCALL pcsc_begin_transaction) (long card);
+long (* DLSTDCALL pcsc_end_transaction) (long card,
+ pcsc_dword_t disposition);
+long (* DLSTDCALL pcsc_transmit) (long card,
const pcsc_io_request_t send_pci,
const unsigned char *send_buffer,
- unsigned long send_len,
+ pcsc_dword_t send_len,
pcsc_io_request_t recv_pci,
unsigned char *recv_buffer,
- unsigned long *recv_len);
-long (* DLSTDCALL pcsc_set_timeout) (unsigned long context,
- unsigned long timeout);
-
-/* Flag set if PC/SC returned the no-service error. */
-static int pcsc_no_service;
+ pcsc_dword_t *recv_len);
+long (* DLSTDCALL pcsc_set_timeout) (long context,
+ pcsc_dword_t timeout);
+long (* DLSTDCALL pcsc_control) (long card,
+ pcsc_dword_t control_code,
+ const void *send_buffer,
+ pcsc_dword_t send_len,
+ void *recv_buffer,
+ pcsc_dword_t recv_len,
+ pcsc_dword_t *bytes_returned);
/* Prototypes. */
+static int pcsc_vendor_specific_init (int slot);
static int pcsc_get_status (int slot, unsigned int *status);
static int reset_pcsc_reader (int slot);
static int apdu_get_status_internal (int slot, int hang, int no_atr_reset,
unsigned int *status,
unsigned int *changed);
+static int check_pcsc_pinpad (int slot, int command, pininfo_t *pininfo);
+static int pcsc_pinpad_verify (int slot, int class, int ins, int p0, int p1,
+ pininfo_t *pininfo);
+static int pcsc_pinpad_modify (int slot, int class, int ins, int p0, int p1,
+ pininfo_t *pininfo);
@@ -305,8 +355,47 @@ static int apdu_get_status_internal (int slot, int hang, int no_atr_reset,
*/
+static int
+lock_slot (int slot)
+{
+#ifdef USE_GNU_PTH
+ if (!pth_mutex_acquire (&reader_table[slot].lock, 0, NULL))
+ {
+ log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
+ return SW_HOST_LOCKING_FAILED;
+ }
+#endif /*USE_GNU_PTH*/
+ return 0;
+}
+
+static int
+trylock_slot (int slot)
+{
+#ifdef USE_GNU_PTH
+ if (!pth_mutex_acquire (&reader_table[slot].lock, TRUE, NULL))
+ {
+ if (errno == EBUSY)
+ return SW_HOST_BUSY;
+ log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
+ return SW_HOST_LOCKING_FAILED;
+ }
+#endif /*USE_GNU_PTH*/
+ return 0;
+}
+
+static void
+unlock_slot (int slot)
+{
+#ifdef USE_GNU_PTH
+ if (!pth_mutex_release (&reader_table[slot].lock))
+ log_error ("failed to release apdu lock: %s\n", strerror (errno));
+#endif /*USE_GNU_PTH*/
+}
+
+
/* Find an unused reader slot for PORTSTR and put it into the reader
- table. Return -1 on error or the index into the reader table. */
+ table. Return -1 on error or the index into the reader table.
+ Acquire slot's lock on successful return. Caller needs to unlock it. */
static int
new_reader_slot (void)
{
@@ -333,6 +422,11 @@ new_reader_slot (void)
reader_table[reader].lock_initialized = 1;
}
#endif /*USE_GNU_PTH*/
+ if (lock_slot (reader))
+ {
+ log_error ("error locking mutex: %s\n", strerror (errno));
+ return -1;
+ }
reader_table[reader].connect_card = NULL;
reader_table[reader].disconnect_card = NULL;
reader_table[reader].close_reader = NULL;
@@ -340,19 +434,27 @@ new_reader_slot (void)
reader_table[reader].reset_reader = NULL;
reader_table[reader].get_status_reader = NULL;
reader_table[reader].send_apdu_reader = NULL;
- reader_table[reader].check_keypad = NULL;
+ reader_table[reader].check_pinpad = check_pcsc_pinpad;
reader_table[reader].dump_status_reader = NULL;
reader_table[reader].set_progress_cb = NULL;
+ reader_table[reader].pinpad_verify = pcsc_pinpad_verify;
+ reader_table[reader].pinpad_modify = pcsc_pinpad_modify;
reader_table[reader].used = 1;
reader_table[reader].any_status = 0;
reader_table[reader].last_status = 0;
reader_table[reader].is_t0 = 1;
+ reader_table[reader].is_spr532 = 0;
+ reader_table[reader].pinpad_varlen_supported = 0;
#ifdef NEED_PCSC_WRAPPER
reader_table[reader].pcsc.req_fd = -1;
reader_table[reader].pcsc.rsp_fd = -1;
reader_table[reader].pcsc.pid = (pid_t)(-1);
#endif
+ reader_table[reader].pcsc.verify_ioctl = 0;
+ reader_table[reader].pcsc.modify_ioctl = 0;
+ reader_table[reader].pcsc.pinmin = -1;
+ reader_table[reader].pcsc.pinmax = -1;
return reader;
}
@@ -395,7 +497,7 @@ host_sw_string (long err)
case SW_HOST_GENERAL_ERROR: return "general error";
case SW_HOST_NO_READER: return "no reader";
case SW_HOST_ABORTED: return "aborted";
- case SW_HOST_NO_KEYPAD: return "no keypad";
+ case SW_HOST_NO_PINPAD: return "no pinpad";
case SW_HOST_ALREADY_CONNECTED: return "already connected";
default: return "unknown host status error";
}
@@ -560,7 +662,7 @@ ct_get_status (int slot, unsigned int *status)
set to BUFLEN. Returns: CT API error code. */
static int
ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
- unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo)
+ unsigned char *buffer, size_t *buflen, pininfo_t *pininfo)
{
int rc;
unsigned char dad[1], sad[1];
@@ -610,6 +712,7 @@ open_ct_reader (int port)
log_error ("apdu_open_ct_reader failed on port %d: %s\n",
port, ct_error_string (rc));
reader_table[reader].used = 0;
+ unlock_slot (reader);
return -1;
}
@@ -625,10 +728,13 @@ open_ct_reader (int port)
reader_table[reader].reset_reader = reset_ct_reader;
reader_table[reader].get_status_reader = ct_get_status;
reader_table[reader].send_apdu_reader = ct_send_apdu;
- reader_table[reader].check_keypad = NULL;
+ reader_table[reader].check_pinpad = NULL;
reader_table[reader].dump_status_reader = ct_dump_reader_status;
+ reader_table[reader].pinpad_verify = NULL;
+ reader_table[reader].pinpad_modify = NULL;
dump_reader_status (reader);
+ unlock_slot (reader);
return reader;
}
@@ -766,6 +872,7 @@ pcsc_error_to_sw (long ec)
case PCSC_E_CANCELLED: rc = SW_HOST_ABORTED; break;
case PCSC_E_NO_MEMORY: rc = SW_HOST_OUT_OF_CORE; break;
case PCSC_E_TIMEOUT: rc = SW_HOST_CARD_IO_ERROR; break;
+ case PCSC_E_UNKNOWN_READER: rc = SW_HOST_NO_READER; break;
case PCSC_E_SHARING_VIOLATION: rc = SW_HOST_LOCKING_FAILED; break;
case PCSC_E_NO_SMARTCARD: rc = SW_HOST_NO_CARD; break;
case PCSC_W_REMOVED_CARD: rc = SW_HOST_NO_CARD; break;
@@ -838,9 +945,11 @@ pcsc_get_status_direct (int slot, unsigned int *status)
*status = 0;
if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
- *status |= APDU_CARD_PRESENT;
- if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
- *status |= APDU_CARD_ACTIVE;
+ {
+ *status |= APDU_CARD_PRESENT;
+ if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
+ *status |= APDU_CARD_ACTIVE;
+ }
#ifndef HAVE_W32_SYSTEM
/* We indicate a useful card if it is not in use by another
application. This is because we only use exclusive access
@@ -987,11 +1096,11 @@ pcsc_get_status (int slot, unsigned int *status)
static int
pcsc_send_apdu_direct (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen,
- struct pininfo_s *pininfo)
+ pininfo_t *pininfo)
{
long err;
struct pcsc_io_request_s send_pci;
- unsigned long recv_len;
+ pcsc_dword_t recv_len;
if (!reader_table[slot].atrlen
&& (err = reset_pcsc_reader (slot)))
@@ -1023,7 +1132,7 @@ pcsc_send_apdu_direct (int slot, unsigned char *apdu, size_t apdulen,
static int
pcsc_send_apdu_wrapped (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen,
- struct pininfo_s *pininfo)
+ pininfo_t *pininfo)
{
long err;
reader_table_t slotp;
@@ -1142,7 +1251,7 @@ pcsc_send_apdu_wrapped (int slot, unsigned char *apdu, size_t apdulen,
static int
pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen,
- struct pininfo_s *pininfo)
+ pininfo_t *pininfo)
{
#ifdef NEED_PCSC_WRAPPER
return pcsc_send_apdu_wrapped (slot, apdu, apdulen, buffer, buflen, pininfo);
@@ -1154,6 +1263,150 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
#ifndef NEED_PCSC_WRAPPER
static int
+control_pcsc_direct (int slot, pcsc_dword_t ioctl_code,
+ const unsigned char *cntlbuf, size_t len,
+ unsigned char *buffer, pcsc_dword_t *buflen)
+{
+ long err;
+
+ err = pcsc_control (reader_table[slot].pcsc.card, ioctl_code,
+ cntlbuf, len, buffer, *buflen, buflen);
+ if (err)
+ {
+ log_error ("pcsc_control failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ return pcsc_error_to_sw (err);
+ }
+
+ return 0;
+}
+#endif /*!NEED_PCSC_WRAPPER*/
+
+
+#ifdef NEED_PCSC_WRAPPER
+static int
+control_pcsc_wrapped (int slot, pcsc_dword_t ioctl_code,
+ const unsigned char *cntlbuf, size_t len,
+ unsigned char *buffer, pcsc_dword_t *buflen)
+{
+ long err = PCSC_E_NOT_TRANSACTED;
+ reader_table_t slotp;
+ unsigned char msgbuf[9];
+ int i, n;
+ size_t full_len;
+
+ slotp = reader_table + slot;
+
+ msgbuf[0] = 0x06; /* CONTROL command. */
+ msgbuf[1] = ((len + 4) >> 24);
+ msgbuf[2] = ((len + 4) >> 16);
+ msgbuf[3] = ((len + 4) >> 8);
+ msgbuf[4] = ((len + 4) );
+ msgbuf[5] = (ioctl_code >> 24);
+ msgbuf[6] = (ioctl_code >> 16);
+ msgbuf[7] = (ioctl_code >> 8);
+ msgbuf[8] = (ioctl_code );
+ if ( writen (slotp->pcsc.req_fd, msgbuf, 9)
+ || writen (slotp->pcsc.req_fd, cntlbuf, len))
+ {
+ log_error ("error sending PC/SC CONTROL request: %s\n",
+ strerror (errno));
+ goto command_failed;
+ }
+
+ /* Read the response. */
+ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
+ {
+ log_error ("error receiving PC/SC CONTROL response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
+ if (msgbuf[0] != 0x81 || len < 4)
+ {
+ log_error ("invalid response header from PC/SC received\n");
+ goto command_failed;
+ }
+ len -= 4; /* Already read the error code. */
+ err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
+ | (msgbuf[7] << 8 ) | msgbuf[8]);
+ if (err)
+ {
+ log_error ("pcsc_control failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ return pcsc_error_to_sw (err);
+ }
+
+ full_len = len;
+
+ n = *buflen < len ? *buflen : len;
+ if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n)
+ {
+ log_error ("error receiving PC/SC CONTROL response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ *buflen = n;
+
+ full_len -= len;
+ if (full_len)
+ {
+ log_error ("pcsc_send_apdu: provided buffer too short - truncated\n");
+ err = PCSC_E_INVALID_VALUE;
+ }
+ /* We need to read any rest of the response, to keep the
+ protocol running. */
+ while (full_len)
+ {
+ unsigned char dummybuf[128];
+
+ n = full_len < DIM (dummybuf) ? full_len : DIM (dummybuf);
+ if ((i=readn (slotp->pcsc.rsp_fd, dummybuf, n, &len)) || len != n)
+ {
+ log_error ("error receiving PC/SC CONTROL response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ full_len -= n;
+ }
+
+ if (!err)
+ return 0;
+
+ command_failed:
+ close (slotp->pcsc.req_fd);
+ close (slotp->pcsc.rsp_fd);
+ slotp->pcsc.req_fd = -1;
+ slotp->pcsc.rsp_fd = -1;
+ kill (slotp->pcsc.pid, SIGTERM);
+ slotp->pcsc.pid = (pid_t)(-1);
+ slotp->used = 0;
+ return pcsc_error_to_sw (err);
+}
+#endif /*NEED_PCSC_WRAPPER*/
+
+
+
+/* Do some control with the value of IOCTL_CODE to the card inserted
+ to SLOT. Input buffer is specified by CNTLBUF of length LEN.
+ Output buffer is specified by BUFFER of length *BUFLEN, and the
+ actual output size will be stored at BUFLEN. Returns: A status word.
+ This routine is used for PIN pad input support. */
+static int
+control_pcsc (int slot, pcsc_dword_t ioctl_code,
+ const unsigned char *cntlbuf, size_t len,
+ unsigned char *buffer, pcsc_dword_t *buflen)
+{
+#ifdef NEED_PCSC_WRAPPER
+ return control_pcsc_wrapped (slot, ioctl_code, cntlbuf, len, buffer, buflen);
+#else
+ return control_pcsc_direct (slot, ioctl_code, cntlbuf, len, buffer, buflen);
+#endif
+}
+
+
+#ifndef NEED_PCSC_WRAPPER
+static int
close_pcsc_reader_direct (int slot)
{
pcsc_release_context (reader_table[slot].pcsc.context);
@@ -1277,8 +1530,10 @@ connect_pcsc_card (int slot)
else
{
char reader[250];
- unsigned long readerlen, atrlen;
- unsigned long card_state, card_protocol;
+ pcsc_dword_t readerlen, atrlen;
+ long card_state, card_protocol;
+
+ pcsc_vendor_specific_init (slot);
atrlen = DIM (reader_table[0].atr);
readerlen = sizeof reader -1 ;
@@ -1465,6 +1720,147 @@ reset_pcsc_reader (int slot)
}
+/* Examine reader specific parameters and initialize. This is mostly
+ for pinpad input. Called at opening the connection to the reader. */
+static int
+pcsc_vendor_specific_init (int slot)
+{
+ unsigned char buf[256];
+ pcsc_dword_t len;
+ int sw;
+ int vendor = 0;
+ int product = 0;
+ pcsc_dword_t get_tlv_ioctl = (pcsc_dword_t)-1;
+ unsigned char *p;
+
+ len = sizeof (buf);
+ sw = control_pcsc (slot, CM_IOCTL_GET_FEATURE_REQUEST, NULL, 0, buf, &len);
+ if (sw)
+ {
+ log_error ("pcsc_vendor_specific_init: GET_FEATURE_REQUEST failed: %d\n",
+ sw);
+ return SW_NOT_SUPPORTED;
+ }
+ else
+ {
+ p = buf;
+ while (p < buf + len)
+ {
+ unsigned char code = *p++;
+ int l = *p++;
+ unsigned int v = 0;
+
+ if (l == 1)
+ v = p[0];
+ else if (l == 2)
+ v = ((p[0] << 8) | p[1]);
+ else if (l == 4)
+ v = ((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
+
+ if (code == FEATURE_VERIFY_PIN_DIRECT)
+ reader_table[slot].pcsc.verify_ioctl = v;
+ else if (code == FEATURE_MODIFY_PIN_DIRECT)
+ reader_table[slot].pcsc.modify_ioctl = v;
+ else if (code == FEATURE_GET_TLV_PROPERTIES)
+ get_tlv_ioctl = v;
+
+ if (DBG_CARD_IO)
+ log_debug ("feature: code=%02X, len=%d, v=%02X\n", code, l, v);
+
+ p += l;
+ }
+ }
+
+ if (get_tlv_ioctl == (pcsc_dword_t)-1)
+ {
+ /*
+ * For system which doesn't support GET_TLV_PROPERTIES,
+ * we put some heuristics here.
+ */
+ if (reader_table[slot].rdrname)
+ {
+ if (strstr (reader_table[slot].rdrname, "SPRx32"))
+ {
+ reader_table[slot].is_spr532 = 1;
+ reader_table[slot].pinpad_varlen_supported = 1;
+ }
+ else if (strstr (reader_table[slot].rdrname, "ST-2xxx")
+ || strstr (reader_table[slot].rdrname, "cyberJack")
+ || strstr (reader_table[slot].rdrname, "DIGIPASS")
+ || strstr (reader_table[slot].rdrname, "Gnuk")
+ || strstr (reader_table[slot].rdrname, "KAAN"))
+ reader_table[slot].pinpad_varlen_supported = 1;
+ }
+
+ return 0;
+ }
+
+ len = sizeof (buf);
+ sw = control_pcsc (slot, get_tlv_ioctl, NULL, 0, buf, &len);
+ if (sw)
+ {
+ log_error ("pcsc_vendor_specific_init: GET_TLV_IOCTL failed: %d\n", sw);
+ return SW_NOT_SUPPORTED;
+ }
+
+ p = buf;
+ while (p < buf + len)
+ {
+ unsigned char tag = *p++;
+ int l = *p++;
+ unsigned int v = 0;
+
+ /* Umm... here is little endian, while the encoding above is big. */
+ if (l == 1)
+ v = p[0];
+ else if (l == 2)
+ v = ((p[1] << 8) | p[0]);
+ else if (l == 4)
+ v = ((p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]);
+
+ if (tag == PCSCv2_PART10_PROPERTY_bMinPINSize)
+ reader_table[slot].pcsc.pinmin = v;
+ else if (tag == PCSCv2_PART10_PROPERTY_bMaxPINSize)
+ reader_table[slot].pcsc.pinmax = v;
+ else if (tag == PCSCv2_PART10_PROPERTY_wIdVendor)
+ vendor = v;
+ else if (tag == PCSCv2_PART10_PROPERTY_wIdProduct)
+ product = v;
+
+ if (DBG_CARD_IO)
+ log_debug ("TLV properties: tag=%02X, len=%d, v=%08X\n", tag, l, v);
+
+ p += l;
+ }
+
+ if (vendor == 0x0982 && product == 0x0008) /* Vega Alpha */
+ {
+ /*
+ * Please read the comment of ccid_vendor_specific_init in
+ * ccid-driver.c.
+ */
+ const unsigned char cmd[] = { '\xb5', '\x01', '\x00', '\x03', '\x00' };
+ sw = control_pcsc (slot, CM_IOCTL_VENDOR_IFD_EXCHANGE,
+ cmd, sizeof (cmd), NULL, 0);
+ if (sw)
+ return SW_NOT_SUPPORTED;
+ }
+ else if (vendor == 0x04e6 && product == 0xe003) /* SCM SPR532 */
+ {
+ reader_table[slot].is_spr532 = 1;
+ reader_table[slot].pinpad_varlen_supported = 1;
+ }
+ else if ((vendor == 0x046a && product == 0x003e) /* Cherry ST-2xxx */
+ || vendor == 0x0c4b /* Tested with Reiner cyberJack GO */
+ || vendor == 0x1a44 /* Tested with Vasco DIGIPASS 920 */
+ || vendor == 0x234b /* Tested with FSIJ Gnuk Token */
+ || vendor == 0x0d46 /* Tested with KAAN Advanced??? */)
+ reader_table[slot].pinpad_varlen_supported = 1;
+
+ return 0;
+}
+
+
/* Open the PC/SC reader without using the wrapper. Returns -1 on
error or a slot number for the reader. */
#ifndef NEED_PCSC_WRAPPER
@@ -1474,7 +1870,7 @@ open_pcsc_reader_direct (const char *portstr)
long err;
int slot;
char *list = NULL;
- unsigned long nreader, listlen;
+ pcsc_dword_t nreader, listlen;
char *p;
slot = new_reader_slot ();
@@ -1490,11 +1886,9 @@ open_pcsc_reader_direct (const char *portstr)
log_error ("pcsc_establish_context failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
reader_table[slot].used = 0;
- if (err == 0x8010001d)
- pcsc_no_service = 1;
+ unlock_slot (slot);
return -1;
}
- pcsc_no_service = 0;
err = pcsc_list_readers (reader_table[slot].pcsc.context,
NULL, NULL, &nreader);
@@ -1506,6 +1900,7 @@ open_pcsc_reader_direct (const char *portstr)
log_error ("error allocating memory for reader list\n");
pcsc_release_context (reader_table[slot].pcsc.context);
reader_table[slot].used = 0;
+ unlock_slot (slot);
return -1 /*SW_HOST_OUT_OF_CORE*/;
}
err = pcsc_list_readers (reader_table[slot].pcsc.context,
@@ -1518,6 +1913,7 @@ open_pcsc_reader_direct (const char *portstr)
pcsc_release_context (reader_table[slot].pcsc.context);
reader_table[slot].used = 0;
xfree (list);
+ unlock_slot (slot);
return -1;
}
@@ -1544,6 +1940,7 @@ open_pcsc_reader_direct (const char *portstr)
log_error ("error allocating memory for reader name\n");
pcsc_release_context (reader_table[slot].pcsc.context);
reader_table[slot].used = 0;
+ unlock_slot (slot);
return -1;
}
strcpy (reader_table[slot].rdrname, portstr? portstr : list);
@@ -1563,6 +1960,7 @@ open_pcsc_reader_direct (const char *portstr)
reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
dump_reader_status (slot);
+ unlock_slot (slot);
return slot;
}
#endif /*!NEED_PCSC_WRAPPER */
@@ -1610,6 +2008,7 @@ open_pcsc_reader_wrapped (const char *portstr)
{
log_error ("error creating a pipe: %s\n", strerror (errno));
slotp->used = 0;
+ unlock_slot (slot);
return -1;
}
if (pipe (wp) == -1)
@@ -1618,6 +2017,7 @@ open_pcsc_reader_wrapped (const char *portstr)
close (rp[0]);
close (rp[1]);
slotp->used = 0;
+ unlock_slot (slot);
return -1;
}
@@ -1630,6 +2030,7 @@ open_pcsc_reader_wrapped (const char *portstr)
close (wp[0]);
close (wp[1]);
slotp->used = 0;
+ unlock_slot (slot);
return -1;
}
slotp->pcsc.pid = pid;
@@ -1726,9 +2127,11 @@ open_pcsc_reader_wrapped (const char *portstr)
}
err = PCSC_ERR_MASK ((msgbuf[5] << 24) | (msgbuf[6] << 16)
| (msgbuf[7] << 8 ) | msgbuf[8]);
+
if (err)
{
- log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err));
+ log_error ("PC/SC OPEN failed: %s (0x%08x)\n",
+ pcsc_error_string (err), err);
/*sw = pcsc_error_to_sw (err);*/
goto command_failed;
}
@@ -1760,10 +2163,13 @@ open_pcsc_reader_wrapped (const char *portstr)
reader_table[slot].send_apdu_reader = pcsc_send_apdu;
reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
+ pcsc_vendor_specific_init (slot);
+
/* Read the status so that IS_T0 will be set. */
pcsc_get_status (slot, &dummy_status);
dump_reader_status (slot);
+ unlock_slot (slot);
return slot;
command_failed:
@@ -1774,6 +2180,7 @@ open_pcsc_reader_wrapped (const char *portstr)
kill (slotp->pcsc.pid, SIGTERM);
slotp->pcsc.pid = (pid_t)(-1);
slotp->used = 0;
+ unlock_slot (slot);
/* There is no way to return SW. */
return -1;
@@ -1792,6 +2199,200 @@ open_pcsc_reader (const char *portstr)
}
+/* Check whether the reader supports the ISO command code COMMAND
+ on the pinpad. Return 0 on success. */
+static int
+check_pcsc_pinpad (int slot, int command, pininfo_t *pininfo)
+{
+ int r;
+
+ if (reader_table[slot].pcsc.pinmin >= 0)
+ pininfo->minlen = reader_table[slot].pcsc.pinmin;
+
+ if (reader_table[slot].pcsc.pinmax >= 0)
+ pininfo->maxlen = reader_table[slot].pcsc.pinmax;
+
+ if (!pininfo->minlen)
+ pininfo->minlen = 1;
+ if (!pininfo->maxlen)
+ pininfo->maxlen = 15;
+
+ if ((command == ISO7816_VERIFY && reader_table[slot].pcsc.verify_ioctl != 0)
+ || (command == ISO7816_CHANGE_REFERENCE_DATA
+ && reader_table[slot].pcsc.modify_ioctl != 0))
+ r = 0; /* Success */
+ else
+ r = SW_NOT_SUPPORTED;
+
+ if (DBG_CARD_IO)
+ log_debug ("check_pcsc_pinpad: command=%02X, r=%d\n",
+ (unsigned int)command, r);
+
+ if (reader_table[slot].pinpad_varlen_supported)
+ pininfo->fixedlen = 0;
+
+ return r;
+}
+
+#define PIN_VERIFY_STRUCTURE_SIZE 24
+static int
+pcsc_pinpad_verify (int slot, int class, int ins, int p0, int p1,
+ pininfo_t *pininfo)
+{
+ int sw;
+ unsigned char *pin_verify;
+ int len = PIN_VERIFY_STRUCTURE_SIZE + pininfo->fixedlen;
+ unsigned char result[2];
+ pcsc_dword_t resultlen = 2;
+ int no_lc;
+
+ if (!reader_table[slot].atrlen
+ && (sw = reset_pcsc_reader (slot)))
+ return sw;
+
+ if (pininfo->fixedlen < 0 || pininfo->fixedlen >= 16)
+ return SW_NOT_SUPPORTED;
+
+ pin_verify = xtrymalloc (len);
+ if (!pin_verify)
+ return SW_HOST_OUT_OF_CORE;
+
+ no_lc = (!pininfo->fixedlen && reader_table[slot].is_spr532);
+
+ pin_verify[0] = 0x00; /* bTimeOut */
+ pin_verify[1] = 0x00; /* bTimeOut2 */
+ pin_verify[2] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */
+ pin_verify[3] = pininfo->fixedlen; /* bmPINBlockString */
+ pin_verify[4] = 0x00; /* bmPINLengthFormat */
+ pin_verify[5] = pininfo->maxlen; /* wPINMaxExtraDigit */
+ pin_verify[6] = pininfo->minlen; /* wPINMaxExtraDigit */
+ pin_verify[7] = 0x02; /* bEntryValidationCondition: Validation key pressed */
+ if (pininfo->minlen && pininfo->maxlen && pininfo->minlen == pininfo->maxlen)
+ pin_verify[7] |= 0x01; /* Max size reached. */
+ pin_verify[8] = 0x01; /* bNumberMessage: One message */
+ pin_verify[9] = 0x09; /* wLangId: 0x0409: US English */
+ pin_verify[10] = 0x04; /* wLangId: 0x0409: US English */
+ pin_verify[11] = 0x00; /* bMsgIndex */
+ pin_verify[12] = 0x00; /* bTeoPrologue[0] */
+ pin_verify[13] = 0x00; /* bTeoPrologue[1] */
+ pin_verify[14] = pininfo->fixedlen + 0x05 - no_lc; /* bTeoPrologue[2] */
+ pin_verify[15] = pininfo->fixedlen + 0x05 - no_lc; /* ulDataLength */
+ pin_verify[16] = 0x00; /* ulDataLength */
+ pin_verify[17] = 0x00; /* ulDataLength */
+ pin_verify[18] = 0x00; /* ulDataLength */
+ pin_verify[19] = class; /* abData[0] */
+ pin_verify[20] = ins; /* abData[1] */
+ pin_verify[21] = p0; /* abData[2] */
+ pin_verify[22] = p1; /* abData[3] */
+ pin_verify[23] = pininfo->fixedlen; /* abData[4] */
+ if (pininfo->fixedlen)
+ memset (&pin_verify[24], 0xff, pininfo->fixedlen);
+ else if (no_lc)
+ len--;
+
+ if (DBG_CARD_IO)
+ log_debug ("send secure: c=%02X i=%02X p1=%02X p2=%02X len=%d pinmax=%d\n",
+ class, ins, p0, p1, len, pininfo->maxlen);
+
+ sw = control_pcsc (slot, reader_table[slot].pcsc.verify_ioctl,
+ pin_verify, len, result, &resultlen);
+ xfree (pin_verify);
+ if (sw || resultlen < 2)
+ {
+ log_error ("control_pcsc failed: %d\n", sw);
+ return sw? sw: SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ }
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ if (DBG_CARD_IO)
+ log_debug (" response: sw=%04X datalen=%d\n", sw, (unsigned int)resultlen);
+ return sw;
+}
+
+
+#define PIN_MODIFY_STRUCTURE_SIZE 29
+static int
+pcsc_pinpad_modify (int slot, int class, int ins, int p0, int p1,
+ pininfo_t *pininfo)
+{
+ int sw;
+ unsigned char *pin_modify;
+ int len = PIN_MODIFY_STRUCTURE_SIZE + 2 * pininfo->fixedlen;
+ unsigned char result[2];
+ pcsc_dword_t resultlen = 2;
+ int no_lc;
+
+ if (!reader_table[slot].atrlen
+ && (sw = reset_pcsc_reader (slot)))
+ return sw;
+
+ if (pininfo->fixedlen < 0 || pininfo->fixedlen >= 16)
+ return SW_NOT_SUPPORTED;
+
+ pin_modify = xtrymalloc (len);
+ if (!pin_modify)
+ return SW_HOST_OUT_OF_CORE;
+
+ no_lc = (!pininfo->fixedlen && reader_table[slot].is_spr532);
+
+ pin_modify[0] = 0x00; /* bTimeOut */
+ pin_modify[1] = 0x00; /* bTimeOut2 */
+ pin_modify[2] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */
+ pin_modify[3] = pininfo->fixedlen; /* bmPINBlockString */
+ pin_modify[4] = 0x00; /* bmPINLengthFormat */
+ pin_modify[5] = 0x00; /* bInsertionOffsetOld */
+ pin_modify[6] = pininfo->fixedlen; /* bInsertionOffsetNew */
+ pin_modify[7] = pininfo->maxlen; /* wPINMaxExtraDigit */
+ pin_modify[8] = pininfo->minlen; /* wPINMaxExtraDigit */
+ pin_modify[9] = (p0 == 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)
+ */
+ pin_modify[10] = 0x02; /* bEntryValidationCondition: Validation key pressed */
+ if (pininfo->minlen && pininfo->maxlen && pininfo->minlen == pininfo->maxlen)
+ pin_modify[10] |= 0x01; /* Max size reached. */
+ pin_modify[11] = 0x03; /* bNumberMessage: Three messages */
+ pin_modify[12] = 0x09; /* wLangId: 0x0409: US English */
+ pin_modify[13] = 0x04; /* wLangId: 0x0409: US English */
+ pin_modify[14] = 0x00; /* bMsgIndex1 */
+ pin_modify[15] = 0x01; /* bMsgIndex2 */
+ pin_modify[16] = 0x02; /* bMsgIndex3 */
+ pin_modify[17] = 0x00; /* bTeoPrologue[0] */
+ pin_modify[18] = 0x00; /* bTeoPrologue[1] */
+ pin_modify[19] = 2 * pininfo->fixedlen + 0x05 - no_lc; /* bTeoPrologue[2] */
+ pin_modify[20] = 2 * pininfo->fixedlen + 0x05 - no_lc; /* ulDataLength */
+ pin_modify[21] = 0x00; /* ulDataLength */
+ pin_modify[22] = 0x00; /* ulDataLength */
+ pin_modify[23] = 0x00; /* ulDataLength */
+ pin_modify[24] = class; /* abData[0] */
+ pin_modify[25] = ins; /* abData[1] */
+ pin_modify[26] = p0; /* abData[2] */
+ pin_modify[27] = p1; /* abData[3] */
+ pin_modify[28] = 2 * pininfo->fixedlen; /* abData[4] */
+ if (pininfo->fixedlen)
+ memset (&pin_modify[29], 0xff, 2 * pininfo->fixedlen);
+ else if (no_lc)
+ len--;
+
+ if (DBG_CARD_IO)
+ log_debug ("send secure: c=%02X i=%02X p1=%02X p2=%02X len=%d pinmax=%d\n",
+ class, ins, p0, p1, len, (int)pininfo->maxlen);
+
+ sw = control_pcsc (slot, reader_table[slot].pcsc.modify_ioctl,
+ pin_modify, len, result, &resultlen);
+ xfree (pin_modify);
+ if (sw || resultlen < 2)
+ {
+ log_error ("control_pcsc failed: %d\n", sw);
+ return sw? sw : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ }
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ if (DBG_CARD_IO)
+ log_debug (" response: sw=%04X datalen=%d\n", sw, (unsigned int)resultlen);
+ return sw;
+}
#ifdef HAVE_LIBUSB
/*
@@ -1878,7 +2479,7 @@ get_status_ccid (int slot, unsigned int *status)
static int
send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen,
- struct pininfo_s *pininfo)
+ pininfo_t *pininfo)
{
long err;
size_t maxbuflen;
@@ -1894,11 +2495,7 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
maxbuflen = *buflen;
if (pininfo)
err = ccid_transceive_secure (reader_table[slot].ccid.handle,
- apdu, apdulen,
- pininfo->mode,
- pininfo->minlen,
- pininfo->maxlen,
- pininfo->padlen,
+ apdu, apdulen, pininfo,
buffer, maxbuflen, buflen);
else
err = ccid_transceive (reader_table[slot].ccid.handle,
@@ -1913,19 +2510,43 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
/* Check whether the CCID reader supports the ISO command code COMMAND
- on the keypad. Return 0 on success. For a description of the pin
+ on the pinpad. Return 0 on success. For a description of the pin
parameters, see ccid-driver.c */
static int
-check_ccid_keypad (int slot, int command, int pin_mode,
- int pinlen_min, int pinlen_max, int pin_padlen)
+check_ccid_pinpad (int slot, int command, pininfo_t *pininfo)
{
unsigned char apdu[] = { 0, 0, 0, 0x81 };
apdu[1] = command;
- return ccid_transceive_secure (reader_table[slot].ccid.handle,
- apdu, sizeof apdu,
- pin_mode, pinlen_min, pinlen_max, pin_padlen,
- NULL, 0, NULL);
+ return ccid_transceive_secure (reader_table[slot].ccid.handle, apdu,
+ sizeof apdu, pininfo, NULL, 0, NULL);
+}
+
+
+static int
+ccid_pinpad_operation (int slot, int class, int ins, int p0, int p1,
+ pininfo_t *pininfo)
+{
+ unsigned char apdu[4];
+ int err, sw;
+ unsigned char result[2];
+ size_t resultlen = 2;
+
+ apdu[0] = class;
+ apdu[1] = ins;
+ apdu[2] = p0;
+ apdu[3] = p1;
+ err = ccid_transceive_secure (reader_table[slot].ccid.handle,
+ apdu, sizeof apdu, pininfo,
+ result, 2, &resultlen);
+ if (err)
+ return err;
+
+ if (resultlen < 2)
+ return SW_HOST_INCOMPLETE_CARD_RESPONSE;
+
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ return sw;
}
@@ -1946,6 +2567,7 @@ open_ccid_reader (const char *portstr)
if (err)
{
slotp->used = 0;
+ unlock_slot (slot);
return -1;
}
@@ -1970,14 +2592,17 @@ open_ccid_reader (const char *portstr)
reader_table[slot].reset_reader = reset_ccid_reader;
reader_table[slot].get_status_reader = get_status_ccid;
reader_table[slot].send_apdu_reader = send_apdu_ccid;
- reader_table[slot].check_keypad = check_ccid_keypad;
+ reader_table[slot].check_pinpad = check_ccid_pinpad;
reader_table[slot].dump_status_reader = dump_ccid_reader_status;
reader_table[slot].set_progress_cb = set_progress_cb_ccid_reader;
+ reader_table[slot].pinpad_verify = ccid_pinpad_operation;
+ reader_table[slot].pinpad_modify = ccid_pinpad_operation;
/* Our CCID reader code does not support T=0 at all, thus reset the
flag. */
reader_table[slot].is_t0 = 0;
dump_reader_status (slot);
+ unlock_slot (slot);
return slot;
}
@@ -2132,7 +2757,7 @@ my_rapdu_get_status (int slot, unsigned int *status)
static int
my_rapdu_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen,
- struct pininfo_s *pininfo)
+ pininfo_t *pininfo)
{
int err;
reader_table_t slotp;
@@ -2216,6 +2841,7 @@ open_rapdu_reader (int portno,
if (!slotp->rapdu.handle)
{
slotp->used = 0;
+ unlock_slot (slot);
return -1;
}
@@ -2263,17 +2889,21 @@ open_rapdu_reader (int portno,
reader_table[slot].reset_reader = reset_rapdu_reader;
reader_table[slot].get_status_reader = my_rapdu_get_status;
reader_table[slot].send_apdu_reader = my_rapdu_send_apdu;
- reader_table[slot].check_keypad = NULL;
+ reader_table[slot].check_pinpad = NULL;
reader_table[slot].dump_status_reader = NULL;
+ reader_table[slot].pinpad_verify = NULL;
+ reader_table[slot].pinpad_modify = NULL;
dump_reader_status (slot);
rapdu_msg_release (msg);
+ unlock_slot (slot);
return slot;
failure:
rapdu_msg_release (msg);
rapdu_release (slotp->rapdu.handle);
slotp->used = 0;
+ unlock_slot (slot);
return -1;
}
@@ -2286,60 +2916,18 @@ open_rapdu_reader (int portno,
*/
-static int
-lock_slot (int slot)
-{
-#ifdef USE_GNU_PTH
- if (!pth_mutex_acquire (&reader_table[slot].lock, 0, NULL))
- {
- log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
- return SW_HOST_LOCKING_FAILED;
- }
-#endif /*USE_GNU_PTH*/
- return 0;
-}
-
-static int
-trylock_slot (int slot)
-{
-#ifdef USE_GNU_PTH
- if (!pth_mutex_acquire (&reader_table[slot].lock, TRUE, NULL))
- {
- if (errno == EBUSY)
- return SW_HOST_BUSY;
- log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
- return SW_HOST_LOCKING_FAILED;
- }
-#endif /*USE_GNU_PTH*/
- return 0;
-}
-
-static void
-unlock_slot (int slot)
-{
-#ifdef USE_GNU_PTH
- if (!pth_mutex_release (&reader_table[slot].lock))
- log_error ("failed to release apdu lock: %s\n", strerror (errno));
-#endif /*USE_GNU_PTH*/
-}
-
-
/* Open the reader and return an internal slot number or -1 on
error. If PORTSTR is NULL we default to a suitable port (for ctAPI:
the first USB reader. For PC/SC the first listed reader). */
int
-apdu_open_reader (const char *portstr, int *r_no_service)
+apdu_open_reader (const char *portstr)
{
static int pcsc_api_loaded, ct_api_loaded;
- int slot;
-
- if (r_no_service)
- *r_no_service = 0;
#ifdef HAVE_LIBUSB
if (!opt.disable_ccid)
{
- int i;
+ int slot, i;
const char *s;
slot = open_ccid_reader (portstr);
@@ -2432,6 +3020,7 @@ apdu_open_reader (const char *portstr, int *r_no_service)
pcsc_end_transaction = dlsym (handle, "SCardEndTransaction");
pcsc_transmit = dlsym (handle, "SCardTransmit");
pcsc_set_timeout = dlsym (handle, "SCardSetTimeout");
+ pcsc_control = dlsym (handle, "SCardControl");
if (!pcsc_establish_context
|| !pcsc_release_context
@@ -2444,12 +3033,13 @@ apdu_open_reader (const char *portstr, int *r_no_service)
|| !pcsc_begin_transaction
|| !pcsc_end_transaction
|| !pcsc_transmit
+ || !pcsc_control
/* || !pcsc_set_timeout */)
{
/* Note that set_timeout is currently not used and also not
available under Windows. */
log_error ("apdu_open_reader: invalid PC/SC driver "
- "(%d%d%d%d%d%d%d%d%d%d%d%d)\n",
+ "(%d%d%d%d%d%d%d%d%d%d%d%d%d)\n",
!!pcsc_establish_context,
!!pcsc_release_context,
!!pcsc_list_readers,
@@ -2461,7 +3051,8 @@ apdu_open_reader (const char *portstr, int *r_no_service)
!!pcsc_begin_transaction,
!!pcsc_end_transaction,
!!pcsc_transmit,
- !!pcsc_set_timeout );
+ !!pcsc_set_timeout,
+ !!pcsc_control );
dlclose (handle);
return -1;
}
@@ -2469,11 +3060,7 @@ apdu_open_reader (const char *portstr, int *r_no_service)
pcsc_api_loaded = 1;
}
- slot = open_pcsc_reader (portstr);
- if (slot == -1 && r_no_service && pcsc_no_service)
- *r_no_service = 1;
-
- return slot;
+ return open_pcsc_reader (portstr);
}
@@ -2596,11 +3183,14 @@ apdu_enum_reader (int slot, int *used)
/* Connect a card. This is used to power up the card and make sure
- that an ATR is available. */
+ that an ATR is available. Depending on the reader backend it may
+ return an error for an inactive card or if no card is
+ available. */
int
apdu_connect (int slot)
{
int sw;
+ unsigned int status;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
@@ -2625,7 +3215,14 @@ apdu_connect (int slot)
scdaemon is fired up and apdu_get_status has not yet been called.
Without that we would force a reset of the card with the next
call to apdu_get_status. */
- apdu_get_status_internal (slot, 1, 1, NULL, NULL);
+ apdu_get_status_internal (slot, 1, 1, &status, NULL);
+ if (sw)
+ ;
+ else if (!(status & APDU_CARD_PRESENT))
+ sw = SW_HOST_NO_CARD;
+ else if ((status & APDU_CARD_PRESENT) && !(status & APDU_CARD_ACTIVE))
+ sw = SW_HOST_CARD_INACTIVE;
+
return sw;
}
@@ -2847,19 +3444,76 @@ apdu_get_status (int slot, int hang,
/* Check whether the reader supports the ISO command code COMMAND on
- the keypad. Return 0 on success. For a description of the pin
+ the pinpad. Return 0 on success. For a description of the pin
parameters, see ccid-driver.c */
int
-apdu_check_keypad (int slot, int command, int pin_mode,
- int pinlen_min, int pinlen_max, int pin_padlen)
+apdu_check_pinpad (int slot, int command, pininfo_t *pininfo)
{
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
- if (reader_table[slot].check_keypad)
- return reader_table[slot].check_keypad (slot, command,
- pin_mode, pinlen_min, pinlen_max,
- pin_padlen);
+ if (opt.enable_pinpad_varlen)
+ pininfo->fixedlen = 0;
+
+ if (reader_table[slot].check_pinpad)
+ {
+ int sw;
+
+ if ((sw = lock_slot (slot)))
+ return sw;
+
+ sw = reader_table[slot].check_pinpad (slot, command, pininfo);
+ unlock_slot (slot);
+ return sw;
+ }
+ else
+ return SW_HOST_NOT_SUPPORTED;
+}
+
+
+int
+apdu_pinpad_verify (int slot, int class, int ins, int p0, int p1,
+ pininfo_t *pininfo)
+{
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if (reader_table[slot].pinpad_verify)
+ {
+ int sw;
+
+ if ((sw = lock_slot (slot)))
+ return sw;
+
+ sw = reader_table[slot].pinpad_verify (slot, class, ins, p0, p1,
+ pininfo);
+ unlock_slot (slot);
+ return sw;
+ }
+ else
+ return SW_HOST_NOT_SUPPORTED;
+}
+
+
+int
+apdu_pinpad_modify (int slot, int class, int ins, int p0, int p1,
+ pininfo_t *pininfo)
+{
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if (reader_table[slot].pinpad_modify)
+ {
+ int sw;
+
+ if ((sw = lock_slot (slot)))
+ return sw;
+
+ sw = reader_table[slot].pinpad_modify (slot, class, ins, p0, p1,
+ pininfo);
+ unlock_slot (slot);
+ return sw;
+ }
else
return SW_HOST_NOT_SUPPORTED;
}
@@ -2869,7 +3523,7 @@ apdu_check_keypad (int slot, int command, int pin_mode,
function should be called in locked state. */
static int
send_apdu (int slot, unsigned char *apdu, size_t apdulen,
- unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo)
+ unsigned char *buffer, size_t *buflen, pininfo_t *pininfo)
{
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
@@ -2885,7 +3539,7 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen,
/* Core APDU tranceiver function. Parameters are described at
- apdu_send_le with the exception of PININFO which indicates keypad
+ apdu_send_le with the exception of PININFO which indicates pinpad
related operations if not NULL. If EXTENDED_MODE is not 0
command chaining or extended length will be used according to these
values:
@@ -2901,7 +3555,7 @@ static int
send_le (int slot, int class, int ins, int p0, int p1,
int lc, const char *data, int le,
unsigned char **retbuf, size_t *retbuflen,
- struct pininfo_s *pininfo, int extended_mode)
+ pininfo_t *pininfo, int extended_mode)
{
#define SHORT_RESULT_BUFFER_SIZE 258
/* We allocate 8 extra bytes as a safety margin towards a driver bug. */
@@ -3298,24 +3952,6 @@ apdu_send_simple (int slot, int extended_mode,
}
-/* Same as apdu_send_simple but uses the keypad of the reader. */
-int
-apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
- int lc, const char *data,
- int pin_mode,
- int pinlen_min, int pinlen_max, int pin_padlen)
-{
- struct pininfo_s pininfo;
-
- pininfo.mode = pin_mode;
- pininfo.minlen = pinlen_min;
- pininfo.maxlen = pinlen_max;
- pininfo.padlen = pin_padlen;
- return send_le (slot, class, ins, p0, p1, lc, data, -1,
- NULL, NULL, &pininfo, 0);
-}
-
-
/* This is a more generic version of the apdu sending routine. It
takes an already formatted APDU in APDUDATA or length APDUDATALEN
and returns with an APDU including the status word. With
diff --git a/scd/apdu.h b/scd/apdu.h
index d79f8b4..1524f99 100644
--- a/scd/apdu.h
+++ b/scd/apdu.h
@@ -66,7 +66,7 @@ enum {
SW_HOST_GENERAL_ERROR = 0x1000b,
SW_HOST_NO_READER = 0x1000c,
SW_HOST_ABORTED = 0x1000d,
- SW_HOST_NO_KEYPAD = 0x1000e,
+ SW_HOST_NO_PINPAD = 0x1000e,
SW_HOST_ALREADY_CONNECTED = 0x1000f
};
@@ -81,7 +81,7 @@ enum {
/* Note, that apdu_open_reader returns no status word but -1 on error. */
-int apdu_open_reader (const char *portstr, int *r_no_service);
+int apdu_open_reader (const char *portstr);
int apdu_open_remote_reader (const char *portstr,
const unsigned char *cookie, size_t length,
int (*readfnc) (void *opaque,
@@ -112,15 +112,14 @@ int apdu_activate (int slot);
int apdu_reset (int slot);
int apdu_get_status (int slot, int hang,
unsigned int *status, unsigned int *changed);
-int apdu_check_keypad (int slot, int command, int pin_mode,
- int pinlen_min, int pinlen_max, int pin_padlen);
+int apdu_check_pinpad (int slot, int command, pininfo_t *pininfo);
+int apdu_pinpad_verify (int slot, int class, int ins, int p0, int p1,
+ pininfo_t *pininfo);
+int apdu_pinpad_modify (int slot, int class, int ins, int p0, int p1,
+ pininfo_t *pininfo);
int apdu_send_simple (int slot, int extended_mode,
int class, int ins, int p0, int p1,
int lc, const char *data);
-int apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
- int lc, const char *data,
- int pin_mode,
- int pinlen_min, int pinlen_max, int pin_padlen);
int apdu_send (int slot, int extended_mode,
int class, int ins, int p0, int p1, int lc, const char *data,
unsigned char **retbuf, size_t *retbuflen);
diff --git a/scd/app-dinsig.c b/scd/app-dinsig.c
index 46e9a6a..1a0cb60 100644
--- a/scd/app-dinsig.c
+++ b/scd/app-dinsig.c
@@ -282,21 +282,21 @@ verify_pin (app_t app,
{
const char *s;
int rc;
- iso7816_pininfo_t pininfo;
+ pininfo_t pininfo;
if ( app->did_chv1 && !app->force_chv1 )
return 0; /* No need to verify it again. */
memset (&pininfo, 0, sizeof pininfo);
- pininfo.mode = 1;
+ pininfo.fixedlen = -1;
pininfo.minlen = 6;
pininfo.maxlen = 8;
- if (!opt.disable_keypad
- && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
+ if (!opt.disable_pinpad
+ && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) )
{
rc = pincb (pincb_arg,
- _("||Please enter your PIN at the reader's keypad"),
+ _("||Please enter your PIN at the reader's pinpad"),
NULL);
if (rc)
{
@@ -304,11 +304,11 @@ verify_pin (app_t app,
gpg_strerror (rc));
return rc;
}
- rc = iso7816_verify_kp (app->slot, 0x81, "", 0, &pininfo);
+ rc = iso7816_verify_kp (app->slot, 0x81, &pininfo);
/* Dismiss the prompt. */
pincb (pincb_arg, NULL, NULL);
}
- else /* No Keypad. */
+ else /* No Pinpad. */
{
char *pinvalue;
diff --git a/scd/app-nks.c b/scd/app-nks.c
index 076b913..f117445 100644
--- a/scd/app-nks.c
+++ b/scd/app-nks.c
@@ -781,19 +781,19 @@ verify_pin (app_t app, int pwid, const char *desc,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
- iso7816_pininfo_t pininfo;
+ pininfo_t pininfo;
int rc;
if (!desc)
desc = "PIN";
memset (&pininfo, 0, sizeof pininfo);
- pininfo.mode = 1;
+ pininfo.fixedlen = -1;
pininfo.minlen = 6;
pininfo.maxlen = 16;
- if (!opt.disable_keypad
- && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
+ if (!opt.disable_pinpad
+ && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) )
{
rc = pincb (pincb_arg, desc, NULL);
if (rc)
@@ -802,8 +802,8 @@ verify_pin (app_t app, int pwid, const char *desc,
gpg_strerror (rc));
return rc;
}
-
- rc = iso7816_verify_kp (app->slot, pwid, "", 0, &pininfo);
+
+ rc = iso7816_verify_kp (app->slot, pwid, &pininfo);
pincb (pincb_arg, NULL, NULL); /* Dismiss the prompt. */
}
else
@@ -1144,7 +1144,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr,
int is_sigg;
const char *newdesc;
int pwid;
- iso7816_pininfo_t pininfo;
+ pininfo_t pininfo;
(void)ctrl;
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index 8a71caf..0d4ce90 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -1,6 +1,6 @@
/* app-openpgp.c - The OpenPGP card application.
* Copyright (C) 2003, 2004, 2005, 2007, 2008,
- * 2009 Free Software Foundation, Inc.
+ * 2009, 2013 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -118,13 +118,13 @@ static struct {
/* The format of RSA private keys. */
typedef enum
- {
+ {
RSA_UNKNOWN_FMT,
RSA_STD,
RSA_STD_N,
RSA_CRT,
RSA_CRT_N
- }
+ }
rsa_key_format_t;
@@ -141,7 +141,7 @@ struct cache_s {
struct app_local_s {
/* A linked list with cached DOs. */
struct cache_s *cache;
-
+
/* Keep track of the public keys. */
struct
{
@@ -158,6 +158,8 @@ struct app_local_s {
unsigned char status_indicator; /* The card status indicator. */
+ unsigned int manufacturer:16; /* Manufacturer ID from the s/n. */
+
/* Keep track of the ISO card capabilities. */
struct
{
@@ -166,7 +168,7 @@ struct app_local_s {
} cardcap;
/* Keep track of extended card capabilities. */
- struct
+ struct
{
unsigned int is_v2:1; /* This is a v2.0 compatible card. */
unsigned int get_challenge:1;
@@ -189,13 +191,21 @@ struct app_local_s {
unsigned int def_chv2:1; /* Use 123456 for CHV2. */
} flags;
+ /* Pinpad request specified on card. */
+ struct
+ {
+ unsigned int specified:1;
+ int fixedlen_user;
+ int fixedlen_admin;
+ } pinpad;
+
struct
{
unsigned int n_bits; /* Size of the modulus in bits. The rest
of this strucuire is only valid if
this is not 0. */
unsigned int e_bits; /* Size of the public exponent in bits. */
- rsa_key_format_t format;
+ rsa_key_format_t format;
} keyattr[3];
};
@@ -213,7 +223,7 @@ static gpg_error_t do_auth (app_t app, const char *keyidstr,
unsigned char **outdata, size_t *outdatalen);
static void parse_algorithm_attribute (app_t app, int keyno);
static gpg_error_t change_keyattr_from_string
- (app_t app,
+ (app_t app,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const void *value, size_t valuelen);
@@ -253,7 +263,7 @@ do_deinit (app_t app)
bypassed. With TRY_EXTLEN extended lengths APDUs are use if
supported by the card. */
static gpg_error_t
-get_cached_data (app_t app, int tag,
+get_cached_data (app_t app, int tag,
unsigned char **result, size_t *resultlen,
int get_immediate, int try_extlen)
{
@@ -280,13 +290,13 @@ get_cached_data (app_t app, int tag,
memcpy (p, c->data, c->length);
*result = p;
}
-
+
*resultlen = c->length;
-
+
return 0;
}
}
-
+
if (try_extlen && app->app_local->cardcap.ext_lc_le)
exmode = app->app_local->extcap.max_rsp_data;
else
@@ -313,7 +323,7 @@ get_cached_data (app_t app, int tag,
/* Okay, cache it. */
for (c=app->app_local->cache; c; c = c->next)
assert (c->tag != tag);
-
+
c = xtrymalloc (sizeof *c + len);
if (c)
{
@@ -439,7 +449,7 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
{
rc = get_cached_data (app, data_objects[i].get_from,
&buffer, &buflen,
- (data_objects[i].dont_cache
+ (data_objects[i].dont_cache
|| data_objects[i].get_immediate_in_v11),
data_objects[i].try_extlen);
if (!rc)
@@ -463,7 +473,7 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
if (!value) /* Not in a constructed DO, try simple. */
{
rc = get_cached_data (app, tag, &buffer, &buflen,
- (data_objects[i].dont_cache
+ (data_objects[i].dont_cache
|| data_objects[i].get_immediate_in_v11),
data_objects[i].try_extlen);
if (!rc)
@@ -490,7 +500,7 @@ dump_all_do (int slot)
int rc, i, j;
unsigned char *buffer;
size_t buflen;
-
+
for (i=0; data_objects[i].tag; i++)
{
if (data_objects[i].get_from)
@@ -501,7 +511,7 @@ dump_all_do (int slot)
rc = iso7816_get_data (slot, 0, data_objects[i].tag, &buffer, &buflen);
if (gpg_err_code (rc) == GPG_ERR_NO_OBJ)
;
- else if (rc)
+ else if (rc)
log_info ("DO `%s' not available: %s\n",
data_objects[i].desc, gpg_strerror (rc));
else
@@ -522,7 +532,7 @@ dump_all_do (int slot)
{
const unsigned char *value;
size_t valuelen;
-
+
if (j==i || data_objects[i].tag != data_objects[j].get_from)
continue;
value = find_tlv_unchecked (buffer, buflen,
@@ -579,17 +589,23 @@ count_bits (const unsigned char *a, size_t len)
Everything up to a LF is considered a mailbox or account name. If
the first LF is followed by DC4 (0x14) control sequence are
expected up to the next LF. Control sequences are separated by FS
- (0x18) and consist of key=value pairs. There is one key defined:
+ (0x18) and consist of key=value pairs. There are two keys defined:
F=<flags>
- Were FLAGS is a plain hexadecimal number representing flag values.
+ Where FLAGS is a plain hexadecimal number representing flag values.
The lsb is here the rightmost bit. Defined flags bits are:
Bit 0 = CHV1 and CHV2 are not syncronized
Bit 1 = CHV2 has been been set to the default PIN of "123456"
(this implies that bit 0 is also set).
+ P=<pinpad-request>
+
+ Where PINPAD_REQUEST is in the format of: <n> or <n>,<m>.
+ N for user PIN, M for admin PIN. If M is missing it means M=N.
+ 0 means to force not to use pinpad.
+
*/
static void
parse_login_data (app_t app)
@@ -601,6 +617,9 @@ parse_login_data (app_t app)
/* Set defaults. */
app->app_local->flags.no_sync = 0;
app->app_local->flags.def_chv2 = 0;
+ app->app_local->pinpad.specified = 0;
+ app->app_local->pinpad.fixedlen_user = -1;
+ app->app_local->pinpad.fixedlen_admin = -1;
/* Read the DO. */
relptr = get_one_do (app, 0x005E, &buffer, &buflen, NULL);
@@ -610,7 +629,11 @@ parse_login_data (app_t app)
if (*buffer == '\n')
break;
if (buflen < 2 || buffer[1] != '\x14')
- return; /* No control sequences. */
+ {
+ xfree (relptr);
+ return; /* No control sequences. */
+ }
+
buflen--;
buffer++;
do
@@ -626,33 +649,73 @@ parse_login_data (app_t app)
any leading digits but bail out on invalid characters. */
for (p=buffer+2, len = buflen-2; len && hexdigitp (p); p++, len--)
lastdig = xtoi_1 (p);
+ buffer = p;
+ buflen = len;
if (len && !(*p == '\n' || *p == '\x18'))
goto next; /* Invalid characters in field. */
app->app_local->flags.no_sync = !!(lastdig & 1);
app->app_local->flags.def_chv2 = (lastdig & 3) == 3;
}
+ else if (buflen > 1 && *buffer == 'P' && buffer[1] == '=')
+ {
+ /* Pinpad request control sequence found. */
+ buffer += 2;
+ buflen -= 2;
+
+ if (buflen)
+ {
+ if (digitp (buffer))
+ {
+ char *q;
+ int n, m;
+
+ n = strtol (buffer, &q, 10);
+ if (q >= (char *)buffer + buflen
+ || *q == '\x18' || *q == '\n')
+ m = n;
+ else
+ {
+ if (*q++ != ',' || !digitp (q))
+ goto next;
+ m = strtol (q, &q, 10);
+ }
+
+ if (buflen < ((unsigned char *)q - buffer))
+ break;
+
+ buflen -= ((unsigned char *)q - buffer);
+ buffer = q;
+
+ if (buflen && !(*buffer == '\n' || *buffer == '\x18'))
+ goto next;
+ app->app_local->pinpad.specified = 1;
+ app->app_local->pinpad.fixedlen_user = n;
+ app->app_local->pinpad.fixedlen_admin = m;
+ }
+ }
+ }
next:
- for (; buflen && *buffer != '\x18'; buflen--, buffer++)
- if (*buffer == '\n')
- buflen = 1;
+ /* Skip to FS (0x18) or LF (\n). */
+ for (; buflen && *buffer != '\x18' && *buffer != '\n'; buflen--)
+ buffer++;
}
- while (buflen);
+ while (buflen && *buffer != '\n');
xfree (relptr);
}
/* Note, that FPR must be at least 20 bytes. */
-static gpg_error_t
+static gpg_error_t
store_fpr (app_t app, int keynumber, u32 timestamp,
const unsigned char *m, size_t mlen,
- const unsigned char *e, size_t elen,
+ const unsigned char *e, size_t elen,
unsigned char *fpr, unsigned int card_version)
{
unsigned int n, nbits;
unsigned char *buffer, *p;
int tag, tag2;
int rc;
-
+
for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */
;
for (; elen && !*e; elen--, e++) /* strip leading zeroes */
@@ -662,7 +725,7 @@ store_fpr (app_t app, int keynumber, u32 timestamp,
p = buffer = xtrymalloc (3 + n);
if (!buffer)
return gpg_error_from_syserror ();
-
+
*p++ = 0x99; /* ctb */
*p++ = n >> 8; /* 2 byte length header */
*p++ = n;
@@ -680,15 +743,15 @@ store_fpr (app_t app, int keynumber, u32 timestamp,
*p++ = nbits >> 8;
*p++ = nbits;
memcpy (p, e, elen); p += elen;
-
+
gcry_md_hash_buffer (GCRY_MD_SHA1, fpr, buffer, n+3);
xfree (buffer);
tag = (card_version > 0x0007? 0xC7 : 0xC6) + keynumber;
- flush_cache_item (app, tag);
+ flush_cache_item (app, 0xC5);
tag2 = 0xCE + keynumber;
- flush_cache_item (app, tag2);
+ flush_cache_item (app, 0xCD);
rc = iso7816_put_data (app->slot, 0, tag, fpr, 20);
if (rc)
@@ -712,11 +775,11 @@ store_fpr (app_t app, int keynumber, u32 timestamp,
return rc;
}
-
+
static void
send_fpr_if_not_null (ctrl_t ctrl, const char *keyword,
int number, const unsigned char *fpr)
-{
+{
int i;
char buf[41];
char numbuf[25];
@@ -738,7 +801,7 @@ send_fpr_if_not_null (ctrl_t ctrl, const char *keyword,
static void
send_fprtime_if_not_null (ctrl_t ctrl, const char *keyword,
int number, const unsigned char *stamp)
-{
+{
char numbuf1[50], numbuf2[50];
unsigned long value;
@@ -753,7 +816,7 @@ send_fprtime_if_not_null (ctrl_t ctrl, const char *keyword,
}
static void
-send_key_data (ctrl_t ctrl, const char *name,
+send_key_data (ctrl_t ctrl, const char *name,
const unsigned char *a, size_t alen)
{
char *buffer, *buf;
@@ -784,7 +847,7 @@ send_key_data (ctrl_t ctrl, const char *name,
static void
send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int number)
-{
+{
char buffer[200];
assert (number >=0 && number < DIM(app->app_local->keyattr));
@@ -801,7 +864,7 @@ send_key_attr (ctrl_t ctrl, app_t app, const char *keyword, int number)
/* Implement the GETATTR command. This is similar to the LEARN
command but returns just one value via the status interface. */
-static gpg_error_t
+static gpg_error_t
do_getattr (app_t app, ctrl_t ctrl, const char *name)
{
static struct {
@@ -818,7 +881,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
{ "KEY-TIME", 0x00CD, 4 },
{ "KEY-ATTR", 0x0000, -5 },
{ "CA-FPR", 0x00C6, 3 },
- { "CHV-STATUS", 0x00C4, 1 },
+ { "CHV-STATUS", 0x00C4, 1 },
{ "SIG-COUNTER", 0x0093, 2 },
{ "SERIALNO", 0x004F, -1 },
{ "AID", 0x004F },
@@ -839,8 +902,8 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++)
;
if (!table[idx].name)
- return gpg_error (GPG_ERR_INV_NAME);
-
+ return gpg_error (GPG_ERR_INV_NAME);
+
if (table[idx].special == -1)
{
/* The serial number is very special. We could have used the
@@ -868,7 +931,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
char tmp[100];
snprintf (tmp, sizeof tmp,
- "gc=%d ki=%d fc=%d pd=%d mcl3=%u aac=%d sm=%d",
+ "gc=%d ki=%d fc=%d pd=%d mcl3=%u aac=%d sm=%d",
app->app_local->extcap.get_challenge,
app->app_local->extcap.key_import,
app->app_local->extcap.change_force_chv,
@@ -891,7 +954,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
{
char *serial;
time_t stamp;
-
+
if (!app_get_serial_and_stamp (app, &serial, &stamp))
{
if (strlen (serial) > 16+12)
@@ -902,7 +965,7 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
}
xfree (serial);
}
- return gpg_error (GPG_ERR_INV_NAME);
+ return gpg_error (GPG_ERR_INV_NAME);
}
if (table[idx].special == -5)
{
@@ -917,9 +980,9 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
if (table[idx].special == 1)
{
char numbuf[7*23];
-
+
for (i=0,*numbuf=0; i < valuelen && i < 7; i++)
- sprintf (numbuf+strlen (numbuf), " %d", value[i]);
+ sprintf (numbuf+strlen (numbuf), " %d", value[i]);
send_status_info (ctrl, table[idx].name,
numbuf, strlen (numbuf), NULL, 0);
}
@@ -1045,7 +1108,7 @@ retrieve_key_material (FILE *fp, const char *hexkeyid,
found_key = 1;
continue;
}
-
+
if ( !strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
break; /* Next key - stop. */
@@ -1058,7 +1121,7 @@ retrieve_key_material (FILE *fp, const char *hexkeyid,
err = gpg_error (GPG_ERR_GENERAL);
goto leave; /* Error: Invalid key data record or not an RSA key. */
}
-
+
err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_HEX, fields[3], 0, NULL);
if (err)
mpi = NULL;
@@ -1070,7 +1133,7 @@ retrieve_key_material (FILE *fp, const char *hexkeyid,
if (err)
goto leave;
}
-
+
if (m_new && e_new)
{
*m = m_new;
@@ -1145,10 +1208,10 @@ get_public_key (app_t app, int keyno)
le_value = 256; /* Use legacy value. */
}
- err = iso7816_read_public_key
+ err = iso7816_read_public_key
(app->slot, exmode,
(const unsigned char*)(keyno == 0? "\xB6" :
- keyno == 1? "\xB8" : "\xA4"), 2,
+ keyno == 1? "\xB8" : "\xA4"), 2,
le_value,
&buffer, &buflen);
if (err)
@@ -1164,7 +1227,7 @@ get_public_key (app_t app, int keyno)
log_error (_("response does not contain the public key data\n"));
goto leave;
}
-
+
m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
if (!m)
{
@@ -1172,7 +1235,7 @@ get_public_key (app_t app, int keyno)
log_error (_("response does not contain the RSA modulus\n"));
goto leave;
}
-
+
e = find_tlv (keydata, keydatalen, 0x0082, &elen);
if (!e)
@@ -1275,7 +1338,7 @@ get_public_key (app_t app, int keyno)
err = gpg_error_from_syserror ();
goto leave;
}
-
+
sprintf (keybuf, "(10:public-key(3:rsa(1:n%u:", (unsigned int) mlen);
keybuf_p = keybuf + strlen (keybuf);
memcpy (keybuf_p, m, mlen);
@@ -1286,7 +1349,7 @@ get_public_key (app_t app, int keyno)
keybuf_p += elen;
strcpy (keybuf_p, ")))");
keybuf_p += strlen (keybuf_p);
-
+
app->app_local->pk[keyno].key = (unsigned char*)keybuf;
app->app_local->pk[keyno].keylen = (keybuf_p - keybuf);
@@ -1319,7 +1382,7 @@ send_keypair_info (app_t app, ctrl_t ctrl, int keyno)
err = get_public_key (app, keyno);
if (err)
goto leave;
-
+
assert (keyno >= 1 && keyno <= 3);
if (!app->app_local->pk[keyno-1].key)
goto leave; /* No such key - ignore. */
@@ -1329,19 +1392,19 @@ send_keypair_info (app_t app, ctrl_t ctrl, int keyno)
grip);
if (err)
goto leave;
-
+
bin2hex (grip, 20, gripstr);
sprintf (idbuf, "OPENPGP.%d", keyno);
- send_status_info (ctrl, "KEYPAIRINFO",
- gripstr, 40,
- idbuf, strlen (idbuf),
+ send_status_info (ctrl, "KEYPAIRINFO",
+ gripstr, 40,
+ idbuf, strlen (idbuf),
NULL, (size_t)0);
leave:
#endif /* GNUPG_MAJOR_VERSION > 1 */
- return err;
+ return err;
}
@@ -1350,7 +1413,7 @@ static gpg_error_t
do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
{
(void)flags;
-
+
do_getattr (app, ctrl, "EXTCAP");
do_getattr (app, ctrl, "DISP-NAME");
do_getattr (app, ctrl, "DISP-LANG");
@@ -1468,15 +1531,50 @@ do_readcert (app_t app, const char *certid,
}
+/* Decide if we use the pinpad of the reader for PIN input according
+ to the user preference on the card, and the capability of the
+ reader. This routine is only called when the reader has pinpad.
+ Returns 0 if we use pinpad, 1 otherwise. */
+static int
+check_pinpad_request (app_t app, pininfo_t *pininfo, int admin_pin)
+{
+ if (app->app_local->pinpad.specified == 0) /* No preference on card. */
+ {
+ if (pininfo->fixedlen == 0) /* Reader has varlen capability. */
+ return 0; /* Then, use pinpad. */
+ else
+ /*
+ * Reader has limited capability, and it may not match PIN of
+ * the card.
+ */
+ return 1;
+ }
+
+ if (admin_pin)
+ pininfo->fixedlen = app->app_local->pinpad.fixedlen_admin;
+ else
+ pininfo->fixedlen = app->app_local->pinpad.fixedlen_user;
+
+ if (pininfo->fixedlen == 0 /* User requests disable pinpad. */
+ || pininfo->fixedlen < pininfo->minlen
+ || pininfo->fixedlen > pininfo->maxlen
+ /* Reader doesn't have the capability to input a PIN which
+ * length is FIXEDLEN. */)
+ return 1;
+
+ return 0;
+}
+
+
/* Verify a CHV either using using the pinentry or if possibile by
- using a keypad. PINCB and PINCB_ARG describe the usual callback
+ using a pinpad. PINCB and PINCB_ARG describe the usual callback
for the pinentry. CHVNO must be either 1 or 2. SIGCOUNT is only
used with CHV1. PINVALUE is the address of a pointer which will
receive a newly allocated block with the actual PIN (this is useful
in case that PIN shall be used for another verify operation). The
caller needs to free this value. If the function returns with
success and NULL is stored at PINVALUE, the caller should take this
- as an indication that the keypad has been used.
+ as an indication that the pinpad has been used.
*/
static gpg_error_t
verify_a_chv (app_t app,
@@ -1487,7 +1585,7 @@ verify_a_chv (app_t app,
int rc = 0;
char *prompt_buffer = NULL;
const char *prompt;
- iso7816_pininfo_t pininfo;
+ pininfo_t pininfo;
int minlen = 6;
assert (chvno == 1 || chvno == 2);
@@ -1514,7 +1612,7 @@ verify_a_chv (app_t app,
}
memset (&pininfo, 0, sizeof pininfo);
- pininfo.mode = 1;
+ pininfo.fixedlen = -1;
pininfo.minlen = minlen;
@@ -1533,16 +1631,17 @@ verify_a_chv (app_t app,
else
prompt = _("||Please enter the PIN");
-
- if (!opt.disable_keypad
- && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
+
+ if (!opt.disable_pinpad
+ && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo)
+ && !check_pinpad_request (app, &pininfo, 0))
{
- /* The reader supports the verify command through the keypad.
+ /* The reader supports the verify command through the pinpad.
Note that the pincb appends a text to the prompt telling the
- user to use the keypad. */
- rc = pincb (pincb_arg, prompt, NULL);
+ user to use the pinpad. */
+ rc = pincb (pincb_arg, prompt, NULL);
prompt = NULL;
- xfree (prompt_buffer);
+ xfree (prompt_buffer);
prompt_buffer = NULL;
if (rc)
{
@@ -1550,7 +1649,7 @@ verify_a_chv (app_t app,
gpg_strerror (rc));
return rc;
}
- rc = iso7816_verify_kp (app->slot, 0x80+chvno, "", 0, &pininfo);
+ rc = iso7816_verify_kp (app->slot, 0x80+chvno, &pininfo);
/* Dismiss the prompt. */
pincb (pincb_arg, NULL, NULL);
@@ -1558,10 +1657,10 @@ verify_a_chv (app_t app,
}
else
{
- /* The reader has no keypad or we don't want to use it. */
- rc = pincb (pincb_arg, prompt, pinvalue);
+ /* The reader has no pinpad or we don't want to use it. */
+ rc = pincb (pincb_arg, prompt, pinvalue);
prompt = NULL;
- xfree (prompt_buffer);
+ xfree (prompt_buffer);
prompt_buffer = NULL;
if (rc)
{
@@ -1569,7 +1668,7 @@ verify_a_chv (app_t app,
gpg_strerror (rc));
return rc;
}
-
+
if (strlen (*pinvalue) < minlen)
{
log_error (_("PIN for CHV%d is too short;"
@@ -1582,7 +1681,7 @@ verify_a_chv (app_t app,
rc = iso7816_verify (app->slot, 0x80+chvno,
*pinvalue, strlen (*pinvalue));
}
-
+
if (rc)
{
log_error (_("verify CHV%d failed: %s\n"), chvno, gpg_strerror (rc));
@@ -1605,20 +1704,20 @@ verify_chv2 (app_t app,
int rc;
char *pinvalue;
- if (app->did_chv2)
+ if (app->did_chv2)
return 0; /* We already verified CHV2. */
rc = verify_a_chv (app, pincb, pincb_arg, 2, 0, &pinvalue);
if (rc)
return rc;
app->did_chv2 = 1;
-
+
if (!app->did_chv1 && !app->force_chv1 && pinvalue)
{
/* For convenience we verify CHV1 here too. We do this only if
the card is not configured to require a verification before
each CHV1 controlled operation (force_chv1) and if we are not
- using the keypad (PINVALUE == NULL). */
+ using the pinpad (PINVALUE == NULL). */
rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
if (gpg_err_code (rc) == GPG_ERR_BAD_PIN)
rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
@@ -1639,7 +1738,7 @@ verify_chv2 (app_t app,
/* Build the prompt to enter the Admin PIN. The prompt depends on the
current sdtate of the card. */
-static gpg_error_t
+static gpg_error_t
build_enter_admin_pin_prompt (app_t app, char **r_prompt)
{
void *relptr;
@@ -1665,7 +1764,7 @@ build_enter_admin_pin_prompt (app_t app, char **r_prompt)
}
remaining = value[6];
xfree (relptr);
-
+
log_info(_("%d Admin PIN attempts remaining before card"
" is permanently locked\n"), remaining);
@@ -1678,10 +1777,10 @@ build_enter_admin_pin_prompt (app_t app, char **r_prompt)
}
else
prompt = xtrystrdup (_("|A|Please enter the Admin PIN"));
-
+
if (!prompt)
return gpg_error_from_syserror ();
-
+
*r_prompt = prompt;
return 0;
}
@@ -1702,26 +1801,27 @@ verify_chv3 (app_t app,
return gpg_error (GPG_ERR_EACCES);
}
#endif
-
- if (!app->did_chv3)
+
+ if (!app->did_chv3)
{
- iso7816_pininfo_t pininfo;
+ pininfo_t pininfo;
int minlen = 8;
char *prompt;
memset (&pininfo, 0, sizeof pininfo);
- pininfo.mode = 1;
+ pininfo.fixedlen = -1;
pininfo.minlen = minlen;
rc = build_enter_admin_pin_prompt (app, &prompt);
if (rc)
return rc;
- if (!opt.disable_keypad
- && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
+ if (!opt.disable_pinpad
+ && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo)
+ && !check_pinpad_request (app, &pininfo, 1))
{
- /* The reader supports the verify command through the keypad. */
- rc = pincb (pincb_arg, prompt, NULL);
+ /* The reader supports the verify command through the pinpad. */
+ rc = pincb (pincb_arg, prompt, NULL);
xfree (prompt);
prompt = NULL;
if (rc)
@@ -1730,7 +1830,7 @@ verify_chv3 (app_t app,
gpg_strerror (rc));
return rc;
}
- rc = iso7816_verify_kp (app->slot, 0x83, "", 0, &pininfo);
+ rc = iso7816_verify_kp (app->slot, 0x83, &pininfo);
/* Dismiss the prompt. */
pincb (pincb_arg, NULL, NULL);
}
@@ -1738,7 +1838,7 @@ verify_chv3 (app_t app,
{
char *pinvalue;
- rc = pincb (pincb_arg, prompt, &pinvalue);
+ rc = pincb (pincb_arg, prompt, &pinvalue);
xfree (prompt);
prompt = NULL;
if (rc)
@@ -1747,7 +1847,7 @@ verify_chv3 (app_t app,
gpg_strerror (rc));
return rc;
}
-
+
if (strlen (pinvalue) < minlen)
{
log_error (_("PIN for CHV%d is too short;"
@@ -1755,11 +1855,11 @@ verify_chv3 (app_t app,
xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN);
}
-
+
rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue));
xfree (pinvalue);
}
-
+
if (rc)
{
log_error (_("verify CHV%d failed: %s\n"), 3, gpg_strerror (rc));
@@ -1774,7 +1874,7 @@ verify_chv3 (app_t app,
/* Handle the SETATTR operation. All arguments are already basically
checked. */
-static gpg_error_t
+static gpg_error_t
do_setattr (app_t app, const char *name,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
@@ -1813,7 +1913,7 @@ do_setattr (app_t app, const char *name,
for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++)
;
if (!table[idx].name)
- return gpg_error (GPG_ERR_INV_NAME);
+ return gpg_error (GPG_ERR_INV_NAME);
if (table[idx].need_v2 && !app->app_local->extcap.is_v2)
return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Not yet supported. */
@@ -1864,7 +1964,7 @@ do_setattr (app_t app, const char *name,
callback. */
static gpg_error_t
do_writecert (app_t app, ctrl_t ctrl,
- const char *certidstr,
+ const char *certidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const unsigned char *certdata, size_t certdatalen)
@@ -1902,8 +2002,8 @@ do_writecert (app_t app, ctrl_t ctrl,
- 2 2 Verify Reset Code and set a new PW1.
- 3 any Verify CHV3/PW3 and set a new CHV3/PW3.
*/
-static gpg_error_t
-do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
+static gpg_error_t
+do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
unsigned int flags,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg)
@@ -1912,11 +2012,17 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
int chvno = atoi (chvnostr);
char *resetcode = NULL;
char *oldpinvalue = NULL;
- char *pinvalue;
+ char *pinvalue = NULL;
int reset_mode = !!(flags & APP_CHANGE_FLAG_RESET);
int set_resetcode = 0;
+ pininfo_t pininfo;
+ int use_pinpad = 0;
+ int minlen = 6;
(void)ctrl;
+ memset (&pininfo, 0, sizeof pininfo);
+ pininfo.fixedlen = -1;
+ pininfo.minlen = minlen;
if (reset_mode && chvno == 3)
{
@@ -1941,7 +2047,7 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
/* On a v1.x card CHV1 and CVH2 should always have the same
value, thus we enforce it here. */
int save_force = app->force_chv1;
-
+
app->force_chv1 = 0;
app->did_chv1 = 0;
app->did_chv2 = 0;
@@ -1960,48 +2066,58 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
{
/* Version 2 cards. */
+ if (!opt.disable_pinpad
+ && !iso7816_check_pinpad (app->slot,
+ ISO7816_CHANGE_REFERENCE_DATA, &pininfo)
+ && !check_pinpad_request (app, &pininfo, chvno == 3))
+ use_pinpad = 1;
+
if (reset_mode)
{
/* To reset a PIN the Admin PIN is required. */
+ use_pinpad = 0;
app->did_chv3 = 0;
rc = verify_chv3 (app, pincb, pincb_arg);
if (rc)
goto leave;
-
+
if (chvno == 2)
set_resetcode = 1;
}
else if (chvno == 1 || chvno == 3)
{
- int minlen = (chvno ==3)? 8 : 6;
- char *promptbuf = NULL;
- const char *prompt;
-
- if (chvno == 3)
+ if (!use_pinpad)
{
- rc = build_enter_admin_pin_prompt (app, &promptbuf);
+ char *promptbuf = NULL;
+ const char *prompt;
+
+ if (chvno == 3)
+ {
+ minlen = 8;
+ rc = build_enter_admin_pin_prompt (app, &promptbuf);
+ if (rc)
+ goto leave;
+ prompt = promptbuf;
+ }
+ else
+ prompt = _("||Please enter the PIN");
+ rc = pincb (pincb_arg, prompt, &oldpinvalue);
+ xfree (promptbuf);
+ promptbuf = NULL;
if (rc)
- goto leave;
- prompt = promptbuf;
- }
- else
- prompt = _("||Please enter the PIN");
- rc = pincb (pincb_arg, prompt, &oldpinvalue);
- xfree (promptbuf);
- promptbuf = NULL;
- if (rc)
- {
- log_info (_("PIN callback returned error: %s\n"),
- gpg_strerror (rc));
- goto leave;
- }
+ {
+ log_info (_("PIN callback returned error: %s\n"),
+ gpg_strerror (rc));
+ goto leave;
+ }
- if (strlen (oldpinvalue) < minlen)
- {
- log_info (_("PIN for CHV%d is too short;"
- " minimum length is %d\n"), chvno, minlen);
- rc = gpg_error (GPG_ERR_BAD_PIN);
- goto leave;
+ if (strlen (oldpinvalue) < minlen)
+ {
+ log_info (_("PIN for CHV%d is too short;"
+ " minimum length is %d\n"), chvno, minlen);
+ rc = gpg_error (GPG_ERR_BAD_PIN);
+ goto leave;
+ }
}
}
else if (chvno == 2)
@@ -2012,8 +2128,9 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
unsigned char *value;
size_t valuelen;
int remaining;
- int minlen = 8;
+ use_pinpad = 0;
+ minlen = 8;
relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
if (!relptr || valuelen < 7)
{
@@ -2029,14 +2146,14 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
log_error (_("Reset Code not or not anymore available\n"));
rc = gpg_error (GPG_ERR_BAD_PIN);
goto leave;
- }
-
+ }
+
rc = pincb (pincb_arg,
_("||Please enter the Reset Code for the card"),
- &resetcode);
+ &resetcode);
if (rc)
{
- log_info (_("PIN callback returned error: %s\n"),
+ log_info (_("PIN callback returned error: %s\n"),
gpg_strerror (rc));
goto leave;
}
@@ -2060,17 +2177,19 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
else
app->did_chv1 = app->did_chv2 = 0;
- /* TRANSLATORS: Do not translate the "|*|" prefixes but
- keep it at the start of the string. We need this elsewhere
- to get some infos on the string. */
- rc = pincb (pincb_arg,
- set_resetcode? _("|RN|New Reset Code") :
- chvno == 3? _("|AN|New Admin PIN") : _("|N|New PIN"),
- &pinvalue);
- if (rc)
+ if (!use_pinpad)
{
- log_error (_("error getting new PIN: %s\n"), gpg_strerror (rc));
- goto leave;
+ /* TRANSLATORS: Do not translate the "|*|" prefixes but
+ keep it at the start of the string. We need this elsewhere
+ to get some infos on the string. */
+ rc = pincb (pincb_arg, set_resetcode? _("|RN|New Reset Code") :
+ chvno == 3? _("|AN|New Admin PIN") : _("|N|New PIN"),
+ &pinvalue);
+ if (rc)
+ {
+ log_error (_("error getting new PIN: %s\n"), gpg_strerror (rc));
+ goto leave;
+ }
}
@@ -2130,10 +2249,28 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr,
{
/* Version 2 cards. */
assert (chvno == 1 || chvno == 3);
-
- rc = iso7816_change_reference_data (app->slot, 0x80 + chvno,
- oldpinvalue, strlen (oldpinvalue),
- pinvalue, strlen (pinvalue));
+
+ if (use_pinpad)
+ {
+ rc = pincb (pincb_arg,
+ chvno == 3 ?
+ _("||Please enter the Admin PIN and New Admin PIN") :
+ _("||Please enter the PIN and New PIN"), NULL);
+ if (rc)
+ {
+ log_info (_("PIN callback returned error: %s\n"),
+ gpg_strerror (rc));
+ goto leave;
+ }
+ rc = iso7816_change_reference_data_kp (app->slot, 0x80 + chvno, 0,
+ &pininfo);
+ /* Dismiss the prompt. */
+ pincb (pincb_arg, NULL, NULL);
+ }
+ else
+ rc = iso7816_change_reference_data (app->slot, 0x80 + chvno,
+ oldpinvalue, strlen (oldpinvalue),
+ pinvalue, strlen (pinvalue));
}
if (pinvalue)
@@ -2208,7 +2345,7 @@ does_key_exist (app_t app, int keyidx, int generating, int force)
of tag and length. A LENGTH greater than 65535 is truncated. */
static size_t
add_tlv (unsigned char *buffer, unsigned int tag, size_t length)
-{
+{
unsigned char *p = buffer;
assert (tag <= 0xffff);
@@ -2243,10 +2380,13 @@ build_privkey_template (app_t app, int keyno,
const unsigned char *rsa_e, size_t rsa_e_len,
const unsigned char *rsa_p, size_t rsa_p_len,
const unsigned char *rsa_q, size_t rsa_q_len,
+ const unsigned char *rsa_u, size_t rsa_u_len,
+ const unsigned char *rsa_dp, size_t rsa_dp_len,
+ const unsigned char *rsa_dq, size_t rsa_dq_len,
unsigned char **result, size_t *resultlen)
{
size_t rsa_e_reqlen;
- unsigned char privkey[7*(1+3)];
+ unsigned char privkey[7*(1+3+3)];
size_t privkey_len;
unsigned char exthdr[2+2+3];
size_t exthdr_len;
@@ -2264,17 +2404,16 @@ build_privkey_template (app_t app, int keyno,
{
case RSA_STD:
case RSA_STD_N:
- break;
case RSA_CRT:
case RSA_CRT_N:
- return gpg_error (GPG_ERR_NOT_SUPPORTED);
+ break;
default:
return gpg_error (GPG_ERR_INV_VALUE);
}
- /* Get the required length for E. */
- rsa_e_reqlen = app->app_local->keyattr[keyno].e_bits/8;
+ /* Get the required length for E. Rounded up to the nearest byte */
+ rsa_e_reqlen = (app->app_local->keyattr[keyno].e_bits + 7) / 8;
assert (rsa_e_len <= rsa_e_reqlen);
/* Build the 7f48 cardholder private key template. */
@@ -2290,6 +2429,17 @@ build_privkey_template (app_t app, int keyno,
tp += add_tlv (tp, 0x93, rsa_q_len);
datalen += rsa_q_len;
+ if (app->app_local->keyattr[keyno].format == RSA_CRT
+ || app->app_local->keyattr[keyno].format == RSA_CRT_N)
+ {
+ tp += add_tlv (tp, 0x94, rsa_u_len);
+ datalen += rsa_u_len;
+ tp += add_tlv (tp, 0x95, rsa_dp_len);
+ datalen += rsa_dp_len;
+ tp += add_tlv (tp, 0x96, rsa_dq_len);
+ datalen += rsa_dq_len;
+ }
+
if (app->app_local->keyattr[keyno].format == RSA_STD_N
|| app->app_local->keyattr[keyno].format == RSA_CRT_N)
{
@@ -2334,15 +2484,26 @@ build_privkey_template (app_t app, int keyno,
/* Right justify E. */
memmove (tp + rsa_e_reqlen - rsa_e_len, tp, rsa_e_len);
memset (tp, 0, rsa_e_reqlen - rsa_e_len);
- }
+ }
tp += rsa_e_reqlen;
-
+
memcpy (tp, rsa_p, rsa_p_len);
tp += rsa_p_len;
-
+
memcpy (tp, rsa_q, rsa_q_len);
tp += rsa_q_len;
-
+
+ if (app->app_local->keyattr[keyno].format == RSA_CRT
+ || app->app_local->keyattr[keyno].format == RSA_CRT_N)
+ {
+ memcpy (tp, rsa_u, rsa_u_len);
+ tp += rsa_u_len;
+ memcpy (tp, rsa_dp, rsa_dp_len);
+ tp += rsa_dp_len;
+ memcpy (tp, rsa_dq, rsa_dq_len);
+ tp += rsa_dq_len;
+ }
+
if (app->app_local->keyattr[keyno].format == RSA_STD_N
|| app->app_local->keyattr[keyno].format == RSA_CRT_N)
{
@@ -2387,7 +2548,7 @@ change_keyattr (app_t app, int keyno, unsigned int nbits,
xfree (relptr);
return gpg_error (GPG_ERR_CARD);
}
-
+
/* We only change n_bits and don't touch anything else. Before we
do so, we round up NBITS to a sensible way in the same way as
gpg's key generation does it. This may help to sort out problems
@@ -2422,8 +2583,8 @@ change_keyattr (app_t app, int keyno, unsigned int nbits,
/* Helper to process an setattr command for name KEY-ATTR. It expects
a string "--force <keyno> <algo> <nbits>" in (VALUE,VALUELEN). */
-static gpg_error_t
-change_keyattr_from_string (app_t app,
+static gpg_error_t
+change_keyattr_from_string (app_t app,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
const void *value, size_t valuelen)
@@ -2503,13 +2664,13 @@ do_writekey (app_t app, ctrl_t ctrl,
keyno = 2;
else
return gpg_error (GPG_ERR_INV_ID);
-
+
err = does_key_exist (app, keyno, 0, force);
if (err)
return err;
- /*
+ /*
Parse the S-expression
*/
buf = keydata;
@@ -2557,10 +2718,10 @@ do_writekey (app_t app, ctrl_t ctrl,
switch (*tok)
{
- case 'n': mpi = &rsa_n; mpi_len = &rsa_n_len; break;
- case 'e': mpi = &rsa_e; mpi_len = &rsa_e_len; break;
- case 'p': mpi = &rsa_p; mpi_len = &rsa_p_len; break;
- case 'q': mpi = &rsa_q; mpi_len = &rsa_q_len;break;
+ case 'n': mpi = &rsa_n; mpi_len = &rsa_n_len; break;
+ case 'e': mpi = &rsa_e; mpi_len = &rsa_e_len; break;
+ case 'p': mpi = &rsa_p; mpi_len = &rsa_p_len; break;
+ case 'q': mpi = &rsa_q; mpi_len = &rsa_q_len;break;
default: mpi = NULL; mpi_len = NULL; break;
}
if (mpi && *mpi)
@@ -2632,7 +2793,7 @@ do_writekey (app_t app, ctrl_t ctrl,
maxbits = app->app_local->keyattr[keyno].n_bits;
nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0;
if (opt.verbose)
- log_info ("RSA modulus size is %u bits (%u bytes)\n",
+ log_info ("RSA modulus size is %u bits (%u bytes)\n",
nbits, (unsigned int)rsa_n_len);
if (nbits && nbits != maxbits
&& app->app_local->extcap.algo_attr_change)
@@ -2644,7 +2805,7 @@ do_writekey (app_t app, ctrl_t ctrl,
}
if (nbits != maxbits)
{
- log_error (_("RSA modulus missing or not of size %d bits\n"),
+ log_error (_("RSA modulus missing or not of size %d bits\n"),
(int)maxbits);
err = gpg_error (GPG_ERR_BAD_SECKEY);
goto leave;
@@ -2666,7 +2827,7 @@ do_writekey (app_t app, ctrl_t ctrl,
nbits = rsa_p? count_bits (rsa_p, rsa_p_len) : 0;
if (nbits != maxbits)
{
- log_error (_("RSA prime %s missing or not of size %d bits\n"),
+ log_error (_("RSA prime %s missing or not of size %d bits\n"),
"P", (int)maxbits);
err = gpg_error (GPG_ERR_BAD_SECKEY);
goto leave;
@@ -2674,12 +2835,12 @@ do_writekey (app_t app, ctrl_t ctrl,
nbits = rsa_q? count_bits (rsa_q, rsa_q_len) : 0;
if (nbits != maxbits)
{
- log_error (_("RSA prime %s missing or not of size %d bits\n"),
+ log_error (_("RSA prime %s missing or not of size %d bits\n"),
"Q", (int)maxbits);
err = gpg_error (GPG_ERR_BAD_SECKEY);
goto leave;
}
-
+
/* We need to remove the cached public key. */
xfree (app->app_local->pk[keyno].key);
app->app_local->pk[keyno].key = NULL;
@@ -2689,16 +2850,53 @@ do_writekey (app_t app, ctrl_t ctrl,
if (app->app_local->extcap.is_v2)
{
+ unsigned char *rsa_u, *rsa_dp, *rsa_dq;
+ size_t rsa_u_len, rsa_dp_len, rsa_dq_len;
+ gcry_mpi_t mpi_e, mpi_p, mpi_q;
+ gcry_mpi_t mpi_u = gcry_mpi_snew (0);
+ gcry_mpi_t mpi_dp = gcry_mpi_snew (0);
+ gcry_mpi_t mpi_dq = gcry_mpi_snew (0);
+ gcry_mpi_t mpi_tmp = gcry_mpi_snew (0);
+ int exmode;
+
+ /* Calculate the u, dp and dq components needed by RSA_CRT cards */
+ gcry_mpi_scan (&mpi_e, GCRYMPI_FMT_USG, rsa_e, rsa_e_len, NULL);
+ gcry_mpi_scan (&mpi_p, GCRYMPI_FMT_USG, rsa_p, rsa_p_len, NULL);
+ gcry_mpi_scan (&mpi_q, GCRYMPI_FMT_USG, rsa_q, rsa_q_len, NULL);
+
+ gcry_mpi_invm (mpi_u, mpi_q, mpi_p);
+ gcry_mpi_sub_ui (mpi_tmp, mpi_p, 1);
+ gcry_mpi_invm (mpi_dp, mpi_e, mpi_tmp);
+ gcry_mpi_sub_ui (mpi_tmp, mpi_q, 1);
+ gcry_mpi_invm (mpi_dq, mpi_e, mpi_tmp);
+
+ gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_u, &rsa_u_len, mpi_u);
+ gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_dp, &rsa_dp_len, mpi_dp);
+ gcry_mpi_aprint (GCRYMPI_FMT_USG, &rsa_dq, &rsa_dq_len, mpi_dq);
+
+ gcry_mpi_release (mpi_e);
+ gcry_mpi_release (mpi_p);
+ gcry_mpi_release (mpi_q);
+ gcry_mpi_release (mpi_u);
+ gcry_mpi_release (mpi_dp);
+ gcry_mpi_release (mpi_dq);
+ gcry_mpi_release (mpi_tmp);
+
/* Build the private key template as described in section 4.3.3.7 of
the OpenPGP card specs version 2.0. */
- int exmode;
-
err = build_privkey_template (app, keyno,
rsa_n, rsa_n_len,
rsa_e, rsa_e_len,
rsa_p, rsa_p_len,
rsa_q, rsa_q_len,
+ rsa_u, rsa_u_len,
+ rsa_dp, rsa_dp_len,
+ rsa_dq, rsa_dq_len,
&template, &template_len);
+ xfree(rsa_u);
+ xfree(rsa_dp);
+ xfree(rsa_dq);
+
if (err)
goto leave;
@@ -2722,8 +2920,8 @@ do_writekey (app_t app, ctrl_t ctrl,
/* Build the private key template as described in section 4.3.3.6 of
the OpenPGP card specs version 1.1:
0xC0 <length> public exponent
- 0xC1 <length> prime p
- 0xC2 <length> prime q
+ 0xC1 <length> prime p
+ 0xC2 <length> prime q
*/
assert (rsa_e_len <= 4);
template_len = (1 + 1 + 4
@@ -2743,21 +2941,21 @@ do_writekey (app_t app, ctrl_t ctrl,
/* Right justify E. */
memmove (tp+4-rsa_e_len, tp, rsa_e_len);
memset (tp, 0, 4-rsa_e_len);
- }
+ }
tp += 4;
-
+
*tp++ = 0xC1;
*tp++ = rsa_p_len;
memcpy (tp, rsa_p, rsa_p_len);
tp += rsa_p_len;
-
+
*tp++ = 0xC2;
*tp++ = rsa_q_len;
memcpy (tp, rsa_q, rsa_q_len);
tp += rsa_q_len;
-
+
assert (tp - template == template_len);
-
+
/* Prepare for storing the key. */
err = verify_chv3 (app, pincb, pincb_arg);
if (err)
@@ -2773,7 +2971,7 @@ do_writekey (app_t app, ctrl_t ctrl,
log_error (_("failed to store the key: %s\n"), gpg_strerror (err));
goto leave;
}
-
+
err = store_fpr (app, keyno, created_at,
rsa_n, rsa_n_len, rsa_e, rsa_e_len,
fprbuf, app->card_version);
@@ -2788,7 +2986,7 @@ do_writekey (app_t app, ctrl_t ctrl,
/* Handle the GENKEY command. */
-static gpg_error_t
+static gpg_error_t
do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
time_t createtime,
gpg_error_t (*pincb)(void*, const char *, char **),
@@ -2806,7 +3004,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
time_t start_at;
int exmode;
int le_value;
- unsigned int keybits;
+ unsigned int keybits;
if (keyno < 1 || keyno > 3)
return gpg_error (GPG_ERR_INV_ID);
@@ -2830,7 +3028,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
/* Because we send the key parameter back via status lines we need
to put a limit on the max. allowed keysize. 2048 bit will
already lead to a 527 byte long status line and thus a 4096 bit
- key would exceed the Assuan line length limit. */
+ key would exceed the Assuan line length limit. */
keybits = app->app_local->keyattr[keyno].n_bits;
if (keybits > 4096)
return gpg_error (GPG_ERR_TOO_LARGE);
@@ -2848,7 +3046,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
le_value = app->app_local->extcap.max_rsp_data;
/* No need to check le_value because it comes from a 16 bit
value and thus can't create an overflow on a 32 bit
- system. */
+ system. */
}
else
{
@@ -2858,10 +3056,10 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
log_info (_("please wait while key is being generated ...\n"));
start_at = time (NULL);
- rc = iso7816_generate_keypair
+ rc = iso7816_generate_keypair
/* # warning key generation temporary replaced by reading an existing key. */
/* rc = iso7816_read_public_key */
- (app->slot, exmode,
+ (app->slot, exmode,
(const unsigned char*)(keyno == 0? "\xB6" :
keyno == 1? "\xB8" : "\xA4"), 2,
le_value,
@@ -2882,7 +3080,7 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
log_error (_("response does not contain the public key data\n"));
goto leave;
}
-
+
m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
if (!m)
{
@@ -2959,7 +3157,7 @@ compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr)
unsigned char *buffer;
size_t buflen, n;
int rc, i;
-
+
assert (keyno >= 1 && keyno <= 3);
rc = get_cached_data (app, 0x006E, &buffer, &buflen, 0, 0);
@@ -3023,12 +3221,12 @@ check_against_given_fingerprint (app_t app, const char *fpr, int keyno)
Note that this function may return the error code
GPG_ERR_WRONG_CARD to indicate that the card currently present does
not match the one required for the requested action (e.g. the
- serial number does not match).
-
+ serial number does not match).
+
As a special feature a KEYIDSTR of "OPENPGP.3" redirects the
operation to the auth command.
*/
-static gpg_error_t
+static gpg_error_t
do_sign (app_t app, const char *keyidstr, int hashalgo,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
@@ -3080,7 +3278,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
{ \
indata = (const char*)indata + sizeof b ## _prefix; \
indatalen -= sizeof b ## _prefix; \
- }
+ }
if (indatalen == 20)
; /* Assume a plain SHA-1 or RMD160 digest has been given. */
@@ -3090,7 +3288,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
else X(SHA256, sha256, 32, app->app_local->extcap.is_v2)
else X(SHA384, sha384, 48, app->app_local->extcap.is_v2)
else X(SHA512, sha512, 64, app->app_local->extcap.is_v2)
- else if ((indatalen == 28 || indatalen == 32
+ else if ((indatalen == 28 || indatalen == 32
|| indatalen == 48 || indatalen ==64)
&& app->app_local->extcap.is_v2)
; /* Assume a plain SHA-3 digest has been given. */
@@ -3119,7 +3317,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
else if (!*s)
; /* no fingerprint given: we allow this for now. */
else if (*s == '/')
- fpr = s + 1;
+ fpr = s + 1;
else
return gpg_error (GPG_ERR_INV_ID);
@@ -3150,7 +3348,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
assert (datalen <= sizeof data); \
memcpy (data, b ## _prefix, sizeof b ## _prefix); \
memcpy (data + sizeof b ## _prefix, indata, indatalen); \
- }
+ }
X(SHA1, sha1, 1)
else X(RMD160, rmd160, 1)
@@ -3158,7 +3356,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
else X(SHA256, sha256, app->app_local->extcap.is_v2)
else X(SHA384, sha384, app->app_local->extcap.is_v2)
else X(SHA512, sha512, app->app_local->extcap.is_v2)
- else
+ else
return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
#undef X
@@ -3175,7 +3373,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
log_info (_("signatures created so far: %lu\n"), sigcount);
/* Check CHV if needed. */
- if (!app->did_chv1 || app->force_chv1 )
+ if (!app->did_chv1 || app->force_chv1 )
{
char *pinvalue;
@@ -3189,7 +3387,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
sync, thus we verify CHV2 here using the given PIN. Cards
with version2 to not have the need for a separate CHV2 and
internally use just one. Obviously we can't do that if the
- keypad has been used. */
+ pinpad has been used. */
if (!app->did_chv2 && pinvalue && !app->app_local->extcap.is_v2)
{
rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
@@ -3216,7 +3414,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
else
{
exmode = 0;
- le_value = 0;
+ le_value = 0;
}
rc = iso7816_compute_ds (app->slot, exmode, data, datalen, le_value,
outdata, outdatalen);
@@ -3233,7 +3431,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
GPG_ERR_WRONG_CARD to indicate that the card currently present does
not match the one required for the requested action (e.g. the
serial number does not match). */
-static gpg_error_t
+static gpg_error_t
do_auth (app_t app, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
@@ -3265,13 +3463,13 @@ do_auth (app_t app, const char *keyidstr,
else if (!*s)
; /* no fingerprint given: we allow this for now. */
else if (*s == '/')
- fpr = s + 1;
+ fpr = s + 1;
else
return gpg_error (GPG_ERR_INV_ID);
for (s=keyidstr, n=0; n < 16; s += 2, n++)
tmp_sn[n] = xtoi_2 (s);
-
+
if (app->serialnolen != 16)
return gpg_error (GPG_ERR_INV_CARD);
if (memcmp (app->serialno, tmp_sn, 16))
@@ -3301,7 +3499,7 @@ do_auth (app_t app, const char *keyidstr,
else
{
exmode = 0;
- le_value = 0;
+ le_value = 0;
}
rc = iso7816_internal_authenticate (app->slot, exmode,
indata, indatalen, le_value,
@@ -3311,7 +3509,7 @@ do_auth (app_t app, const char *keyidstr,
}
-static gpg_error_t
+static gpg_error_t
do_decipher (app_t app, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
@@ -3342,13 +3540,13 @@ do_decipher (app_t app, const char *keyidstr,
else if (!*s)
; /* no fingerprint given: we allow this for now. */
else if (*s == '/')
- fpr = s + 1;
+ fpr = s + 1;
else
return gpg_error (GPG_ERR_INV_ID);
-
+
for (s=keyidstr, n=0; n < 16; s += 2, n++)
tmp_sn[n] = xtoi_2 (s);
-
+
if (app->serialnolen != 16)
return gpg_error (GPG_ERR_INV_CARD);
if (memcmp (app->serialno, tmp_sn, 16))
@@ -3401,7 +3599,7 @@ do_decipher (app_t app, const char *keyidstr,
fixbuf = xtrymalloc (fixuplen + indatalen);
if (!fixbuf)
return gpg_error_from_syserror ();
-
+
memset (fixbuf, 0, fixuplen);
memcpy (fixbuf+fixuplen, indata, indatalen);
indata = fixbuf;
@@ -3420,12 +3618,18 @@ do_decipher (app_t app, const char *keyidstr,
le_value = 0;
}
else
- exmode = le_value = 0;
+ exmode = le_value = 0;
- rc = iso7816_decipher (app->slot, exmode,
+ rc = iso7816_decipher (app->slot, exmode,
indata, indatalen, le_value, padind,
outdata, outdatalen);
xfree (fixbuf);
+
+ if (gpg_err_code (rc) == GPG_ERR_CARD /* actual SW is 0x640a */
+ && app->app_local->manufacturer == 5
+ && app->card_version == 0x0200)
+ log_info ("NOTE: Cards with manufacturer id 5 and s/n <= 346 (0x15a)"
+ " do not work with encryption keys > 2048 bits\n");
}
return rc;
@@ -3442,12 +3646,12 @@ do_decipher (app_t app, const char *keyidstr,
There is a special mode if the keyidstr is "<serialno>[CHV3]" with
the "[CHV3]" being a literal string: The Admin Pin is checked if
and only if the retry counter is still at 3. */
-static gpg_error_t
+static gpg_error_t
do_check_pin (app_t app, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
- unsigned char tmp_sn[20];
+ unsigned char tmp_sn[20];
const char *s;
int n;
int admin_pin = 0;
@@ -3458,7 +3662,7 @@ do_check_pin (app_t app, const char *keyidstr,
/* Check whether an OpenPGP card of any version has been requested. */
if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
return gpg_error (GPG_ERR_INV_ID);
-
+
for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
;
if (n != 32)
@@ -3491,7 +3695,7 @@ do_check_pin (app_t app, const char *keyidstr,
unsigned char *value;
size_t valuelen;
int count;
-
+
relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
if (!relptr || valuelen < 7)
{
@@ -3552,7 +3756,7 @@ show_caps (struct app_local_s *s)
/* Parse the historical bytes in BUFFER of BUFLEN and store them in
APPLOC. */
static void
-parse_historical (struct app_local_s *apploc,
+parse_historical (struct app_local_s *apploc,
const unsigned char * buffer, size_t buflen)
{
/* Example buffer: 00 31 C5 73 C0 01 80 00 90 00 */
@@ -3564,9 +3768,9 @@ parse_historical (struct app_local_s *apploc,
if (*buffer)
{
log_error ("warning: bad category indicator in historical bytes\n");
- return;
+ return;
}
-
+
/* Skip category indicator. */
buffer++;
buflen--;
@@ -3601,9 +3805,9 @@ parse_historical (struct app_local_s *apploc,
/* Parse and optionally show the algorithm attributes for KEYNO.
KEYNO must be in the range 0..2. */
-static void
+static void
parse_algorithm_attribute (app_t app, int keyno)
-{
+{
unsigned char *buffer;
size_t buflen;
void *relptr;
@@ -3612,7 +3816,7 @@ parse_algorithm_attribute (app_t app, int keyno)
assert (keyno >=0 && keyno <= 2);
app->app_local->keyattr[keyno].n_bits = 0;
-
+
relptr = get_one_do (app, 0xC1+keyno, &buffer, &buflen, NULL);
if (!relptr)
{
@@ -3639,7 +3843,7 @@ parse_algorithm_attribute (app_t app, int keyno)
app->app_local->keyattr[keyno].format = (buffer[5] == 0? RSA_STD :
buffer[5] == 1? RSA_STD_N :
buffer[5] == 2? RSA_CRT :
- buffer[5] == 3? RSA_CRT_N :
+ buffer[5] == 3? RSA_CRT_N :
RSA_UNKNOWN_FMT);
if (opt.verbose)
@@ -3669,7 +3873,7 @@ app_select_openpgp (app_t app)
unsigned char *buffer;
size_t buflen;
void *relptr;
-
+
/* Note that the card can't cope with P2=0xCO, thus we need to pass a
special flag value. */
rc = iso7816_select_application (slot, aid, sizeof aid, 0x0001);
@@ -3713,6 +3917,8 @@ app_select_openpgp (app_t app)
goto leave;
}
+ app->app_local->manufacturer = manufacturer;
+
if (app->card_version >= 0x0200)
app->app_local->extcap.is_v2 = 1;
@@ -3762,7 +3968,7 @@ app_select_openpgp (app_t app)
{
/* Available with v2 cards. */
app->app_local->extcap.sm_aes128 = (buffer[1] == 1);
- app->app_local->extcap.max_get_challenge
+ app->app_local->extcap.max_get_challenge
= (buffer[2] << 8 | buffer[3]);
app->app_local->extcap.max_certlen_3 = (buffer[4] << 8 | buffer[5]);
app->app_local->extcap.max_cmd_data = (buffer[6] << 8 | buffer[7]);
@@ -3783,7 +3989,7 @@ app_select_openpgp (app_t app)
parse_algorithm_attribute (app, 0);
parse_algorithm_attribute (app, 1);
parse_algorithm_attribute (app, 2);
-
+
if (opt.verbose > 1)
dump_all_do (slot);
diff --git a/scd/app.c b/scd/app.c
index a23c4a5..742f937 100644
--- a/scd/app.c
+++ b/scd/app.c
@@ -26,8 +26,8 @@
#include "scdaemon.h"
#include "app-common.h"
-#include "apdu.h"
#include "iso7816.h"
+#include "apdu.h"
#include "tlv.h"
/* This table is used to keep track of locks on a per reader base.
@@ -69,7 +69,7 @@ print_progress_line (void *opaque, const char *what, int pc, int cur, int tot)
never shares a reader (while performing one command). Returns 0 on
success; only then the unlock_reader function must be called after
returning from the handler. */
-static gpg_error_t
+static gpg_error_t
lock_reader (int slot, ctrl_t ctrl)
{
gpg_error_t err;
@@ -89,7 +89,7 @@ lock_reader (int slot, ctrl_t ctrl)
lock_table[slot].app = NULL;
lock_table[slot].last_app = NULL;
}
-
+
if (!pth_mutex_acquire (&lock_table[slot].lock, 0, NULL))
{
err = gpg_error_from_syserror ();
@@ -188,7 +188,7 @@ application_notify_card_reset (int slot)
return;
/* FIXME: We are ignoring any error value here. */
- lock_reader (slot, NULL);
+ lock_reader (slot, NULL);
/* Mark application as non-reusable. */
if (lock_table[slot].app)
@@ -204,10 +204,10 @@ application_notify_card_reset (int slot)
lock_table[slot].last_app = NULL;
deallocate_app (app);
}
- unlock_reader (slot);
+ unlock_reader (slot);
}
-
+
/* This function is used by the serialno command to check for an
application conflict which may appear if the serialno command is
used to request a specific application and the connection has
@@ -241,11 +241,14 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
app_t app = NULL;
unsigned char *result = NULL;
size_t resultlen;
+ int want_undefined;
(void)ctrl;
*r_app = NULL;
+ want_undefined = (name && !strcmp (name, "undefined"));
+
err = lock_reader (slot, ctrl);
if (err)
return err;
@@ -287,7 +290,7 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
lock_table[slot].app = app;
lock_table[slot].last_app = NULL;
}
- else
+ else
{
/* No, this saved application can't be used - deallocate it. */
lock_table[slot].last_app = NULL;
@@ -309,7 +312,7 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
unlock_reader (slot);
return 0; /* Okay: We share that one. */
}
-
+
/* Need to allocate a new one. */
app = xtrycalloc (1, sizeof *app);
if (!app)
@@ -325,54 +328,66 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
/* Fixme: We should now first check whether a card is at all
present. */
- /* Try to read the GDO file first to get a default serial number. */
- err = iso7816_select_file (slot, 0x3F00, 1, NULL, NULL);
- if (!err)
- err = iso7816_select_file (slot, 0x2F02, 0, NULL, NULL);
- if (!err)
- err = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
- if (!err)
+ /* Try to read the GDO file first to get a default serial number.
+ We skip this if the undefined application has been requested. */
+ if (!want_undefined)
{
- size_t n;
- const unsigned char *p;
-
- p = find_tlv_unchecked (result, resultlen, 0x5A, &n);
- if (p)
- resultlen -= (p-result);
- if (p && n > resultlen && n == 0x0d && resultlen+1 == n)
+ err = iso7816_select_file (slot, 0x3F00, 1, NULL, NULL);
+ if (!err)
+ err = iso7816_select_file (slot, 0x2F02, 0, NULL, NULL);
+ if (!err)
+ err = iso7816_read_binary (slot, 0, 0, &result, &resultlen);
+ if (!err)
{
- /* The object it does not fit into the buffer. This is an
- invalid encoding (or the buffer is too short. However, I
- have some test cards with such an invalid encoding and
- therefore I use this ugly workaround to return something
- I can further experiment with. */
- log_info ("enabling BMI testcard workaround\n");
- n--;
+ size_t n;
+ const unsigned char *p;
+
+ p = find_tlv_unchecked (result, resultlen, 0x5A, &n);
+ if (p)
+ resultlen -= (p-result);
+ if (p && n > resultlen && n == 0x0d && resultlen+1 == n)
+ {
+ /* The object it does not fit into the buffer. This is an
+ invalid encoding (or the buffer is too short. However, I
+ have some test cards with such an invalid encoding and
+ therefore I use this ugly workaround to return something
+ I can further experiment with. */
+ log_info ("enabling BMI testcard workaround\n");
+ n--;
+ }
+
+ if (p && n <= resultlen)
+ {
+ /* The GDO file is pretty short, thus we simply reuse it for
+ storing the serial number. */
+ memmove (result, p, n);
+ app->serialno = result;
+ app->serialnolen = n;
+ err = app_munge_serialno (app);
+ if (err)
+ goto leave;
+ }
+ else
+ xfree (result);
+ result = NULL;
}
-
- if (p && n <= resultlen)
- {
- /* The GDO file is pretty short, thus we simply reuse it for
- storing the serial number. */
- memmove (result, p, n);
- app->serialno = result;
- app->serialnolen = n;
- err = app_munge_serialno (app);
- if (err)
- goto leave;
- }
- else
- xfree (result);
- result = NULL;
}
/* For certain error codes, there is no need to try more. */
if (gpg_err_code (err) == GPG_ERR_CARD_NOT_PRESENT
|| gpg_err_code (err) == GPG_ERR_ENODEV)
goto leave;
-
+
/* Figure out the application to use. */
- err = gpg_error (GPG_ERR_NOT_FOUND);
+ if (want_undefined)
+ {
+ /* We switch to the "undefined" application only if explicitly
+ requested. */
+ app->apptype = "UNDEFINED";
+ err = 0;
+ }
+ else
+ err = gpg_error (GPG_ERR_NOT_FOUND);
if (err && is_app_allowed ("openpgp")
&& (!name || !strcmp (name, "openpgp")))
@@ -381,11 +396,11 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
err = app_select_nks (app);
if (err && is_app_allowed ("p15") && (!name || !strcmp (name, "p15")))
err = app_select_p15 (app);
- if (err && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig")))
- err = app_select_dinsig (app);
if (err && is_app_allowed ("geldkarte")
&& (!name || !strcmp (name, "geldkarte")))
err = app_select_geldkarte (app);
+ if (err && is_app_allowed ("dinsig") && (!name || !strcmp (name, "dinsig")))
+ err = app_select_dinsig (app);
if (err && name)
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
@@ -419,17 +434,19 @@ get_supported_applications (void)
"openpgp",
"nks",
"p15",
- "dinsig",
"geldkarte",
+ "dinsig",
+ /* Note: "undefined" is not listed here because it needs special
+ treatment by the client. */
NULL
};
int idx;
size_t nbytes;
char *buffer, *p;
-
+
for (nbytes=1, idx=0; list[idx]; idx++)
nbytes += strlen (list[idx]) + 1 + 1;
-
+
buffer = xtrymalloc (nbytes);
if (!buffer)
return NULL;
@@ -506,22 +523,22 @@ release_application (app_t app)
/* The serial number may need some cosmetics. Do it here. This
function shall only be called once after a new serial number has
- been put into APP->serialno.
+ been put into APP->serialno.
Prefixes we use:
-
+
FF 00 00 = For serial numbers starting with an FF
FF 01 00 = Some german p15 cards return an empty serial number so the
serial number from the EF(TokenInfo) is used instead.
FF 7F 00 = No serialno.
-
+
All other serial number not starting with FF are used as they are.
*/
gpg_error_t
app_munge_serialno (app_t app)
{
if (app->serialnolen && app->serialno[0] == 0xff)
- {
+ {
/* The serial number starts with our special prefix. This
requires that we put our default prefix "FF0000" in front. */
unsigned char *p = xtrymalloc (app->serialnolen + 3);
@@ -534,7 +551,7 @@ app_munge_serialno (app_t app)
app->serialno = p;
}
else if (!app->serialnolen)
- {
+ {
unsigned char *p = xtrymalloc (3);
if (!p)
return gpg_error_from_syserror ();
@@ -554,7 +571,7 @@ app_munge_serialno (app_t app)
no update time is available the returned value is 0. Caller must
free SERIAL unless the function returns an error. If STAMP is not
of interest, NULL may be passed. */
-gpg_error_t
+gpg_error_t
app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp)
{
char *buf;
@@ -637,7 +654,7 @@ app_readcert (app_t app, const char *certid,
code returned.
This function might not be supported by all applications. */
-gpg_error_t
+gpg_error_t
app_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
{
gpg_error_t err;
@@ -663,7 +680,7 @@ app_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
/* Perform a GETATTR operation. */
-gpg_error_t
+gpg_error_t
app_getattr (app_t app, ctrl_t ctrl, const char *name)
{
gpg_error_t err;
@@ -684,7 +701,7 @@ app_getattr (app_t app, ctrl_t ctrl, const char *name)
char *serial;
time_t stamp;
int rc;
-
+
rc = app_get_serial_and_stamp (app, &serial, &stamp);
if (rc)
return rc;
@@ -704,7 +721,7 @@ app_getattr (app_t app, ctrl_t ctrl, const char *name)
}
/* Perform a SETATTR operation. */
-gpg_error_t
+gpg_error_t
app_setattr (app_t app, const char *name,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
@@ -729,7 +746,7 @@ app_setattr (app_t app, const char *name,
/* Create the signature and return the allocated result in OUTDATA.
If a PIN is required the PINCB will be used to ask for the PIN; it
should return the PIN in an allocated buffer and put it into PIN. */
-gpg_error_t
+gpg_error_t
app_sign (app_t app, const char *keyidstr, int hashalgo,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
@@ -761,7 +778,7 @@ app_sign (app_t app, const char *keyidstr, int hashalgo,
return the allocated result in OUTDATA. If a PIN is required the
PINCB will be used to ask for the PIN; it should return the PIN in
an allocated buffer and put it into PIN. */
-gpg_error_t
+gpg_error_t
app_auth (app_t app, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
@@ -793,7 +810,7 @@ app_auth (app_t app, const char *keyidstr,
/* Decrypt the data in INDATA and return the allocated result in OUTDATA.
If a PIN is required the PINCB will be used to ask for the PIN; it
should return the PIN in an allocated buffer and put it into PIN. */
-gpg_error_t
+gpg_error_t
app_decipher (app_t app, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg,
@@ -879,7 +896,7 @@ app_writekey (app_t app, ctrl_t ctrl,
/* Perform a SETATTR operation. */
-gpg_error_t
+gpg_error_t
app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
time_t createtime,
gpg_error_t (*pincb)(void*, const char *, char **),
@@ -896,7 +913,7 @@ app_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
err = lock_reader (app->slot, ctrl);
if (err)
return err;
- err = app->fnc.genkey (app, ctrl, keynostr, flags,
+ err = app->fnc.genkey (app, ctrl, keynostr, flags,
createtime, pincb, pincb_arg);
unlock_reader (app->slot);
if (opt.verbose)
@@ -928,7 +945,7 @@ app_get_challenge (app_t app, size_t nbytes, unsigned char *buffer)
/* Perform a CHANGE REFERENCE DATA or RESET RETRY COUNTER operation. */
-gpg_error_t
+gpg_error_t
app_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg)
@@ -956,7 +973,7 @@ app_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode,
/* Perform a VERIFY operation without doing anything lese. This may
be used to initialze a the PIN cache for long lasting other
operations. Its use is highly application dependent. */
-gpg_error_t
+gpg_error_t
app_check_pin (app_t app, const char *keyidstr,
gpg_error_t (*pincb)(void*, const char *, char **),
void *pincb_arg)
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c
index 8c362d7..6e0bc55 100644
--- a/scd/ccid-driver.c
+++ b/scd/ccid-driver.c
@@ -1,6 +1,6 @@
/* ccid-driver.c - USB ChipCardInterfaceDevices driver
* Copyright (C) 2003, 2004, 2005, 2006, 2007
- * 2008, 2009 Free Software Foundation, Inc.
+ * 2008, 2009, 2013 Free Software Foundation, Inc.
* Written by Werner Koch.
*
* This file is part of GnuPG.
@@ -57,7 +57,7 @@
/* CCID (ChipCardInterfaceDevices) is a specification for accessing
- smartcard via a reader connected to the USB.
+ smartcard via a reader connected to the USB.
This is a limited driver allowing to use some CCID drivers directly
without any other specila drivers. This is a fallback driver to be
@@ -91,6 +91,8 @@
#include <usb.h>
+#include "scdaemon.h"
+#include "iso7816.h"
#include "ccid-driver.h"
#define DRVNAME "ccid-driver: "
@@ -209,9 +211,26 @@ enum {
VENDOR_SCM = 0x04e6,
VENDOR_OMNIKEY= 0x076b,
VENDOR_GEMPC = 0x08e6,
- VENDOR_KAAN = 0x0d46
+ VENDOR_VEGA = 0x0982,
+ VENDOR_REINER = 0x0c4b,
+ VENDOR_KAAN = 0x0d46,
+ VENDOR_VASCO = 0x1a44,
+ VENDOR_FSIJ = 0x234b,
};
+/* Some product ids. */
+#define SCM_SCR331 0xe001
+#define SCM_SCR331DI 0x5111
+#define SCM_SCR335 0x5115
+#define SCM_SCR3320 0x5117
+#define SCM_SPR532 0xe003
+#define CHERRY_ST2000 0x003e
+#define VASCO_920 0x0920
+#define GEMPC_PINPAD 0x3478
+#define GEMPC_CT30 0x3437
+#define VEGA_ALPHA 0x0008
+#define CYBERJACK_GO 0x0504
+
/* A list and a table with special transport descriptions. */
enum {
TRANSPORT_USB = 0, /* Standard USB transport. */
@@ -219,20 +238,20 @@ enum {
};
static struct
-{
+{
char *name; /* Device name. */
int type;
} transports[] = {
- { "/dev/cmx0", TRANSPORT_CM4040 },
- { "/dev/cmx1", TRANSPORT_CM4040 },
+ { "/dev/cmx0", TRANSPORT_CM4040 },
+ { "/dev/cmx1", TRANSPORT_CM4040 },
{ NULL },
};
/* Store information on the driver's state. A pointer to such a
structure is used as handle for most functions. */
-struct ccid_driver_s
+struct ccid_driver_s
{
usb_dev_handle *idev;
char *rid;
@@ -255,6 +274,9 @@ struct ccid_driver_s
unsigned char apdu_level:2; /* Reader supports short APDU level
exchange. With a value of 2 short
and extended level is supported.*/
+ unsigned int auto_voltage:1;
+ unsigned int auto_param:1;
+ unsigned int auto_pps:1;
unsigned int auto_ifsd:1;
unsigned int powered_off:1;
unsigned int has_pinpad:2;
@@ -270,7 +292,7 @@ struct ccid_driver_s
static int initialized_usb; /* Tracks whether USB has been initialized. */
-static int debug_level; /* Flag to control the debug output.
+static int debug_level; /* Flag to control the debug output.
0 = No debugging
1 = USB I/O info
2 = Level 1 + T=1 protocol tracing
@@ -286,22 +308,25 @@ static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
size_t *nread, int expected_type, int seqno, int timeout,
int no_debug);
static int abort_cmd (ccid_driver_t handle, int seqno);
+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);
/* Convert a little endian stored 4 byte value into an unsigned
integer. */
-static unsigned int
+static unsigned int
convert_le_u32 (const unsigned char *buf)
{
- return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
}
/* Convert a little endian stored 2 byte value into an unsigned
integer. */
-static unsigned int
+static unsigned int
convert_le_u16 (const unsigned char *buf)
{
- return buf[0] | (buf[1] << 8);
+ return buf[0] | (buf[1] << 8);
}
static void
@@ -322,7 +347,7 @@ my_sleep (int seconds)
may give up its timeslot. */
if (!seconds)
{
-# ifdef HAVE_W32_SYSTEM
+# ifdef HAVE_W32_SYSTEM
Sleep (0);
# else
sleep (0);
@@ -330,7 +355,7 @@ my_sleep (int seconds)
}
pth_sleep (seconds);
#else
-# ifdef HAVE_W32_SYSTEM
+# ifdef HAVE_W32_SYSTEM
Sleep (seconds*1000);
# else
sleep (seconds);
@@ -371,7 +396,7 @@ print_command_failed (const unsigned char *msg)
switch (ec)
{
case 0x00: t = "Command not supported"; break;
-
+
case 0xE0: t = "Slot busy"; break;
case 0xEF: t = "PIN cancelled"; break;
case 0xF0: t = "PIN timeout"; break;
@@ -422,7 +447,7 @@ print_pr_data (const unsigned char *data, size_t datalen, size_t off)
DEBUGOUT_LF ();
}
-
+
static void
print_p2r_header (const char *name, const unsigned char *msg, size_t msglen)
{
@@ -646,7 +671,7 @@ print_r2p_slotstatus (const unsigned char *msg, size_t msglen)
msg[9] == 3? " (stopped)":"");
print_pr_data (msg, msglen, 10);
}
-
+
static void
print_r2p_parameters (const unsigned char *msg, size_t msglen)
@@ -749,7 +774,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
{
unsigned int i;
unsigned int us;
- int have_t1 = 0, have_tpdu=0, have_auto_conf = 0;
+ int have_t1 = 0, have_tpdu=0;
handle->nonnull_nad = 0;
@@ -758,6 +783,9 @@ parse_ccid_descriptor (ccid_driver_t handle,
handle->ifsd = 0;
handle->has_pinpad = 0;
handle->apdu_level = 0;
+ handle->auto_voltage = 0;
+ handle->auto_param = 0;
+ handle->auto_pps = 0;
DEBUGOUT_3 ("idVendor: %04X idProduct: %04X bcdDevice: %04X\n",
handle->id_vendor, handle->id_product, handle->bcd_device);
if (buflen < 54 || buf[0] < 54)
@@ -770,7 +798,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
DEBUGOUT_1 (" bLength %5u\n", buf[0]);
DEBUGOUT_1 (" bDescriptorType %5u\n", buf[1]);
DEBUGOUT_2 (" bcdCCID %2x.%02x", buf[3], buf[2]);
- if (buf[3] != 1 || buf[2] != 0)
+ if (buf[3] != 1 || buf[2] != 0)
DEBUGOUT_CONT(" (Warning: Only accurate for version 1.0)");
DEBUGOUT_LF ();
@@ -802,7 +830,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
us = convert_le_u32(buf+23);
DEBUGOUT_1 (" dwMaxDataRate %7u bps\n", us);
DEBUGOUT_1 (" bNumDataRatesSupp. %5u\n", buf[27]);
-
+
us = convert_le_u32(buf+28);
DEBUGOUT_1 (" dwMaxIFSD %5u\n", us);
handle->max_ifsd = us;
@@ -833,22 +861,31 @@ parse_ccid_descriptor (ccid_driver_t handle,
DEBUGOUT_1 (" dwFeatures %08X\n", us);
if ((us & 0x0002))
{
- DEBUGOUT (" Auto configuration based on ATR\n");
- have_auto_conf = 1;
+ DEBUGOUT (" Auto configuration based on ATR (assumes auto voltage)\n");
+ handle->auto_voltage = 1;
}
if ((us & 0x0004))
DEBUGOUT (" Auto activation on insert\n");
if ((us & 0x0008))
- DEBUGOUT (" Auto voltage selection\n");
+ {
+ DEBUGOUT (" Auto voltage selection\n");
+ handle->auto_voltage = 1;
+ }
if ((us & 0x0010))
DEBUGOUT (" Auto clock change\n");
if ((us & 0x0020))
DEBUGOUT (" Auto baud rate change\n");
if ((us & 0x0040))
- DEBUGOUT (" Auto parameter negotiation made by CCID\n");
+ {
+ DEBUGOUT (" Auto parameter negotiation made by CCID\n");
+ handle->auto_param = 1;
+ }
else if ((us & 0x0080))
- DEBUGOUT (" Auto PPS made by CCID\n");
- else if ((us & (0x0040 | 0x0080)))
+ {
+ DEBUGOUT (" Auto PPS made by CCID\n");
+ handle->auto_pps = 1;
+ }
+ if ((us & (0x0040 | 0x0080)) == (0x0040 | 0x0080))
DEBUGOUT (" WARNING: conflicting negotiation features\n");
if ((us & 0x0100))
@@ -868,7 +905,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
{
DEBUGOUT (" TPDU level exchange\n");
have_tpdu = 1;
- }
+ }
else if ((us & 0x00020000))
{
DEBUGOUT (" Short APDU level exchange\n");
@@ -902,7 +939,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
DEBUGOUT_CONT ("none\n");
else
DEBUGOUT_CONT_2 ("%u cols %u lines\n", buf[50], buf[51]);
-
+
DEBUGOUT_1 (" bPINSupport %5u ", buf[52]);
if ((buf[52] & 1))
{
@@ -915,7 +952,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
handle->has_pinpad |= 2;
}
DEBUGOUT_LF ();
-
+
DEBUGOUT_1 (" bMaxCCIDBusySlots %5u\n", buf[53]);
if (buf[0] > 54) {
@@ -925,11 +962,10 @@ parse_ccid_descriptor (ccid_driver_t handle,
DEBUGOUT_LF ();
}
- if (!have_t1 || !(have_tpdu || handle->apdu_level) || !have_auto_conf)
+ if (!have_t1 || !(have_tpdu || handle->apdu_level))
{
DEBUGOUT ("this drivers requires that the reader supports T=1, "
- "TPDU or APDU level exchange and auto configuration - "
- "this is not available\n");
+ "TPDU or APDU level exchange - this is not available\n");
return -1;
}
@@ -940,29 +976,34 @@ parse_ccid_descriptor (ccid_driver_t handle,
lower than that:
64 - 10 CCID header - 4 T1frame - 2 reserved = 48
Product Ids:
- 0xe001 - SCR 331
- 0x5111 - SCR 331-DI
- 0x5115 - SCR 335
- 0xe003 - SPR 532
- The
+ 0xe001 - SCR 331
+ 0x5111 - SCR 331-DI
+ 0x5115 - SCR 335
+ 0xe003 - SPR 532
+ The
0x5117 - SCR 3320 USB ID-000 reader
seems to be very slow but enabling this workaround boosts the
- performance to a a more or less acceptable level (tested by David).
-
+ performance to a a more or less acceptable level (tested by David).
+
*/
if (handle->id_vendor == VENDOR_SCM
- && handle->max_ifsd > 48
- && ( (handle->id_product == 0xe001 && handle->bcd_device < 0x0516)
- ||(handle->id_product == 0x5111 && handle->bcd_device < 0x0620)
- ||(handle->id_product == 0x5115 && handle->bcd_device < 0x0514)
- ||(handle->id_product == 0xe003 && handle->bcd_device < 0x0504)
- ||(handle->id_product == 0x5117 && handle->bcd_device < 0x0522)
+ && handle->max_ifsd > 48
+ && ( (handle->id_product == SCM_SCR331 && handle->bcd_device < 0x0516)
+ ||(handle->id_product == SCM_SCR331DI && handle->bcd_device < 0x0620)
+ ||(handle->id_product == SCM_SCR335 && handle->bcd_device < 0x0514)
+ ||(handle->id_product == SCM_SPR532 && handle->bcd_device < 0x0504)
+ ||(handle->id_product == SCM_SCR3320 && handle->bcd_device < 0x0522)
))
{
DEBUGOUT ("enabling workaround for buggy SCM readers\n");
handle->max_ifsd = 48;
}
+ if (handle->id_vendor == VENDOR_GEMPC && handle->id_product == GEMPC_CT30)
+ {
+ DEBUGOUT ("enabling product quirk: disable non-null NAD\n");
+ handle->nonnull_nad = 0;
+ }
return 0;
}
@@ -990,7 +1031,7 @@ get_escaped_usb_string (usb_dev_handle *idev, int idx,
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. */
rc = usb_control_msg (idev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR,
- (USB_DT_STRING << 8), 0,
+ (USB_DT_STRING << 8), 0,
(char*)buf, sizeof buf, 1000 /* ms timeout */);
if (rc < 4)
langid = 0x0409; /* English. */
@@ -1012,7 +1053,7 @@ get_escaped_usb_string (usb_dev_handle *idev, int idx,
n++; /* High byte set. */
else if (*s <= 0x20 || *s >= 0x7f || *s == '%' || *s == ':')
n += 3 ;
- else
+ else
n++;
}
@@ -1031,7 +1072,7 @@ get_escaped_usb_string (usb_dev_handle *idev, int idx,
sprintf (result+n, "%%%02X", *s);
n += 3;
}
- else
+ else
result[n++] = *s;
}
strcpy (result+n, suffix);
@@ -1113,7 +1154,7 @@ scan_or_find_usb_device (int scan_mode,
int ifc_no;
int set_no;
struct usb_config_descriptor *config;
- struct usb_interface *interface;
+ struct usb_interface *interface;
struct usb_interface_descriptor *ifcdesc;
char *rid;
usb_dev_handle *idev;
@@ -1131,21 +1172,25 @@ scan_or_find_usb_device (int scan_mode,
interface = config->interface + ifc_no;
if (!interface)
continue;
-
+
for (set_no=0; set_no < interface->num_altsetting; set_no++)
{
ifcdesc = (interface->altsetting + set_no);
/* The second condition is for older SCM SPR 532 who did
- not know about the assigned CCID class. Instead of
- trying to interpret the strings we simply check the
- product ID. */
+ 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
&& dev->descriptor.idVendor == VENDOR_SCM
- && dev->descriptor.idProduct == 0xe003)))
+ && dev->descriptor.idProduct == SCM_SPR532)
+ || (ifcdesc->bInterfaceClass == 255
+ && dev->descriptor.idVendor == VENDOR_CHERRY
+ && dev->descriptor.idProduct == CHERRY_ST2000)))
{
idev = usb_open (dev);
if (!idev)
@@ -1154,7 +1199,7 @@ scan_or_find_usb_device (int scan_mode,
strerror (errno));
continue; /* with next setting. */
}
-
+
rid = make_reader_id (idev,
dev->descriptor.idVendor,
dev->descriptor.idProduct,
@@ -1164,7 +1209,7 @@ scan_or_find_usb_device (int scan_mode,
if (scan_mode)
{
char *p;
-
+
/* We are collecting infos about all
available CCID readers. Store them and
continue. */
@@ -1186,7 +1231,7 @@ scan_or_find_usb_device (int scan_mode,
}
else /* Out of memory. */
free (rid);
-
+
rid = NULL;
++*count;
}
@@ -1230,7 +1275,7 @@ scan_or_find_usb_device (int scan_mode,
}
else
free (rid);
-
+
*r_idev = idev;
return 1; /* Found requested device. */
}
@@ -1244,7 +1289,7 @@ scan_or_find_usb_device (int scan_mode,
}
free (rid);
}
-
+
usb_close (idev);
idev = NULL;
return 0;
@@ -1257,7 +1302,7 @@ scan_or_find_usb_device (int scan_mode,
}
/* Combination function to either scan all CCID devices or to find and
- open one specific device.
+ open one specific device.
The function returns 0 if a reader has been found or when a scan
returned without error.
@@ -1316,7 +1361,7 @@ scan_or_find_devices (int readerno, const char *readerid,
if (r_rid)
*r_rid = NULL;
if (r_dev)
- *r_dev = NULL;
+ *r_dev = NULL;
if (ifcdesc_extra)
*ifcdesc_extra = NULL;
if (ifcdesc_extra_len)
@@ -1329,7 +1374,7 @@ scan_or_find_devices (int readerno, const char *readerid,
*r_fd = -1;
/* See whether we want scan or find mode. */
- if (scan_mode)
+ if (scan_mode)
{
assert (r_rid);
}
@@ -1343,7 +1388,7 @@ scan_or_find_devices (int readerno, const char *readerid,
busses = usb_busses;
#endif
- for (bus = busses; bus; bus = bus->next)
+ for (bus = busses; bus; bus = bus->next)
{
for (dev = bus->devices; dev; dev = dev->next)
{
@@ -1365,7 +1410,7 @@ scan_or_find_devices (int readerno, const char *readerid,
return -1; /* error */
}
*r_idev = idev;
- return 0;
+ return 0;
}
}
}
@@ -1492,9 +1537,36 @@ ccid_get_reader_list (void)
}
-/* Open the reader with the internal number READERNO and return a
+/* 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)
+ {
+ int r;
+ /*
+ * 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.
+ */
+ const unsigned char cmd[] = { '\xb5', '\x01', '\x00', '\x03', '\x00' };
+
+ 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;
+ }
+
+ return 0;
+}
+
+
+/* Open the reader with the internal number READERNO and return a
pointer to be used as handle in HANDLE. Returns 0 on success. */
-int
+int
ccid_open_reader (ccid_driver_t *handle, const char *readerid)
{
int rc = 0;
@@ -1590,7 +1662,7 @@ ccid_open_reader (ccid_driver_t *handle, const char *readerid)
rc = CCID_DRIVER_ERR_NO_READER;
goto leave;
}
-
+
rc = usb_claim_interface (idev, ifc_no);
if (rc)
{
@@ -1600,6 +1672,8 @@ ccid_open_reader (ccid_driver_t *handle, const char *readerid)
}
}
+ rc = ccid_vendor_specific_init (*handle);
+
leave:
free (ifcdesc_extra);
if (rc)
@@ -1624,7 +1698,7 @@ do_close_reader (ccid_driver_t handle)
unsigned char msg[100];
size_t msglen;
unsigned char seqno;
-
+
if (!handle->powered_off)
{
msg[0] = PC_to_RDR_IccPowerOff;
@@ -1635,7 +1709,7 @@ do_close_reader (ccid_driver_t handle)
msg[9] = 0; /* RFU */
set_msg_len (msg, 0);
msglen = 10;
-
+
rc = bulk_out (handle, msg, msglen, 0);
if (!rc)
bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus,
@@ -1659,12 +1733,12 @@ do_close_reader (ccid_driver_t handle)
/* Reset a reader on HANDLE. This is useful in case a reader has been
plugged of and inserted at a different port. By resetting the
handle, the same reader will be get used. Note, that on error the
- handle won't get released.
+ handle won't get released.
This does not return an ATR, so ccid_get_atr should be called right
after this one.
*/
-int
+int
ccid_shutdown_reader (ccid_driver_t handle)
{
int rc = 0;
@@ -1702,7 +1776,7 @@ ccid_shutdown_reader (ccid_driver_t handle)
rc = CCID_DRIVER_ERR_NO_READER;
goto leave;
}
-
+
rc = usb_claim_interface (idev, ifc_no);
if (rc)
{
@@ -1729,8 +1803,8 @@ ccid_shutdown_reader (ccid_driver_t handle)
}
-int
-ccid_set_progress_cb (ccid_driver_t handle,
+int
+ccid_set_progress_cb (ccid_driver_t handle,
void (*cb)(void *, const char *, int, int, int),
void *cb_arg)
{
@@ -1744,7 +1818,7 @@ ccid_set_progress_cb (ccid_driver_t handle,
/* Close the reader HANDLE. */
-int
+int
ccid_close_reader (ccid_driver_t handle)
{
if (!handle || (!handle->idev && handle->dev_fd == -1))
@@ -1772,7 +1846,7 @@ writen (int fd, const void *buf, size_t nbytes)
{
size_t nleft = nbytes;
int nwritten;
-
+
while (nleft > 0)
{
nwritten = write (fd, buf, nleft);
@@ -1786,7 +1860,7 @@ writen (int fd, const void *buf, size_t nbytes)
nleft -= nwritten;
buf = (const char*)buf + nwritten;
}
-
+
return 0;
}
@@ -1855,10 +1929,10 @@ bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen,
break;
}
}
-
+
if (handle->idev)
{
- rc = usb_bulk_write (handle->idev,
+ rc = usb_bulk_write (handle->idev,
handle->ep_bulk_out,
(char*)msg, msglen,
5000 /* ms timeout */);
@@ -1895,7 +1969,7 @@ bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen,
return 0;
DEBUGOUT_2 ("writen to %d failed: %s\n",
handle->dev_fd, strerror (errno));
-
+
}
return CCID_DRIVER_ERR_CARD_IO_ERROR;
}
@@ -1923,7 +1997,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
retry:
if (handle->idev)
{
- rc = usb_bulk_read (handle->idev,
+ rc = usb_bulk_read (handle->idev,
handle->ep_bulk_in,
(char*)buffer, length,
timeout);
@@ -1965,12 +2039,12 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
abort_cmd (handle, seqno);
return CCID_DRIVER_ERR_INV_VALUE;
}
- if (buffer[5] != 0)
+ if (buffer[5] != 0)
{
DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]);
return CCID_DRIVER_ERR_INV_VALUE;
}
- if (buffer[6] != seqno)
+ if (buffer[6] != seqno)
{
DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n",
seqno, buffer[6]);
@@ -1983,7 +2057,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
for the Cherry keyboard which sends a time extension request for
each key hit. */
if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80)
- {
+ {
/* Card present and active, time extension requested. */
DEBUGOUT_2 ("time extension requested (%02X,%02X)\n",
buffer[7], buffer[8]);
@@ -2053,13 +2127,13 @@ abort_cmd (ccid_driver_t handle, int seqno)
/* I don't know how to send an abort to non-USB devices. */
rc = CCID_DRIVER_ERR_NOT_SUPPORTED;
}
-
+
seqno &= 0xff;
DEBUGOUT_1 ("sending abort sequence for seqno %d\n", seqno);
/* 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. */
- rc = usb_control_msg (handle->idev,
+ rc = usb_control_msg (handle->idev,
0x21,/* bmRequestType: host-to-device,
class specific, to interface. */
1, /* ABORT */
@@ -2079,7 +2153,7 @@ abort_cmd (ccid_driver_t handle, int seqno)
seqno--; /* Adjust for next increment. */
do
{
- seqno++;
+ seqno++;
msg[0] = PC_to_RDR_Abort;
msg[5] = 0; /* slot */
msg[6] = seqno;
@@ -2089,24 +2163,24 @@ abort_cmd (ccid_driver_t handle, int seqno)
msglen = 10;
set_msg_len (msg, 0);
- rc = usb_bulk_write (handle->idev,
+ rc = usb_bulk_write (handle->idev,
handle->ep_bulk_out,
(char*)msg, msglen,
5000 /* ms timeout */);
if (rc == msglen)
rc = 0;
else if (rc == -1)
- DEBUGOUT_1 ("usb_bulk_write error in abort_cmd: %s\n",
+ DEBUGOUT_1 ("usb_bulk_write error in abort_cmd: %s\n",
strerror (errno));
else
DEBUGOUT_1 ("usb_bulk_write failed in abort_cmd: %d\n", rc);
if (rc)
return rc;
-
- rc = usb_bulk_read (handle->idev,
+
+ rc = usb_bulk_read (handle->idev,
handle->ep_bulk_in,
- (char*)msg, sizeof msg,
+ (char*)msg, sizeof msg,
5000 /*ms timeout*/);
if (rc < 0)
{
@@ -2122,7 +2196,7 @@ abort_cmd (ccid_driver_t handle, int seqno)
(unsigned int)msglen);
return CCID_DRIVER_ERR_INV_VALUE;
}
- if (msg[5] != 0)
+ if (msg[5] != 0)
{
DEBUGOUT_1 ("unexpected bulk-in slot (%d) in abort_cmd\n", msg[5]);
return CCID_DRIVER_ERR_INV_VALUE;
@@ -2147,7 +2221,7 @@ abort_cmd (ccid_driver_t handle, int seqno)
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. */
-static int
+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)
@@ -2199,7 +2273,7 @@ send_escape_cmd (ccid_driver_t handle,
default:
break;
}
-
+
return rc;
}
@@ -2225,14 +2299,14 @@ ccid_poll (ccid_driver_t handle)
if (handle->idev)
{
- rc = usb_bulk_read (handle->idev,
+ rc = usb_bulk_read (handle->idev,
handle->ep_intr,
(char*)msg, sizeof msg,
0 /* ms timeout */ );
if (rc < 0 && errno == ETIMEDOUT)
return 0;
}
- else
+ else
return 0;
if (rc < 0)
@@ -2256,12 +2330,12 @@ ccid_poll (ccid_driver_t handle)
for (i=1; i < msglen; i++)
for (j=0; j < 4; j++)
DEBUGOUT_CONT_3 (" %d:%c%c",
- (i-1)*4+j,
+ (i-1)*4+j,
(msg[i] & (1<<(j*2)))? 'p':'-',
(msg[i] & (2<<(j*2)))? '*':' ');
DEBUGOUT_LF ();
}
- else if (msg[0] == RDR_to_PC_HardwareError)
+ else if (msg[0] == RDR_to_PC_HardwareError)
{
DEBUGOUT ("hardware error occured\n");
}
@@ -2276,7 +2350,7 @@ ccid_poll (ccid_driver_t handle)
/* Note that this function won't return the error codes NO_CARD or
CARD_INACTIVE */
-int
+int
ccid_slot_status (ccid_driver_t handle, int *statusbits)
{
int rc;
@@ -2324,9 +2398,154 @@ ccid_slot_status (ccid_driver_t handle, int *statusbits)
}
+/* 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)
+ param[1] |= 0x02; /* Convention is inverse. */
+ NEXTBYTE ();
+
+ y = (atr[i] >> 4);
+ historical_bytes_num = atr[i] & 0x0f;
+ NEXTBYTE ();
+
+ if ((y & 1))
+ {
+ param[0] = atr[i]; /* TA1 - Fi & Di */
+ NEXTBYTE ();
+ }
+
+ if ((y & 2))
+ NEXTBYTE (); /* TB1 - ignore */
+
+ if ((y & 4))
+ {
+ param[2] = atr[i]; /* TC1 - Guard Time */
+ NEXTBYTE ();
+ }
+
+ if ((y & 8))
+ {
+ y = (atr[i] >> 4); /* TD1 */
+ t = atr[i] & 0x0f;
+ NEXTBYTE ();
+
+ if ((y & 1))
+ { /* TA2 - PPS mode */
+ if ((atr[i] & 0x0f) != 1)
+ return -2; /* Wrong card protocol (!= 1). */
+
+ if ((atr[i] & 0x10) != 0x10)
+ return -3; /* Transmission parameters are implicitly defined. */
+
+ negotiable = 0; /* TA2 means specific mode. */
+ NEXTBYTE ();
+ }
+
+ if ((y & 2))
+ NEXTBYTE (); /* TB2 - ignore */
+
+ if ((y & 4))
+ NEXTBYTE (); /* TC2 - ignore */
+
+ if ((y & 8))
+ {
+ y = (atr[i] >> 4); /* TD2 */
+ t = atr[i] & 0x0f;
+ NEXTBYTE ();
+ }
+ else
+ y = 0;
+
+ while (y)
+ {
+ 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;
+ }
+ }
+
+ 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;
+}
+
+
/* Return the ATR of the card. This is not a cached value and thus an
actual reset is done. */
-int
+int
ccid_get_atr (ccid_driver_t handle,
unsigned char *atr, size_t maxatrlen, size_t *atrlen)
{
@@ -2340,6 +2559,15 @@ ccid_get_atr (ccid_driver_t handle,
unsigned int edc;
int tried_iso = 0;
int got_param;
+ unsigned char param[7] = { /* For Protocol T=1 */
+ 0x11, /* bmFindexDindex */
+ 0x10, /* bmTCCKST1 */
+ 0x00, /* bGuardTimeT1 */
+ 0x4d, /* bmWaitingIntegersT1 */
+ 0x00, /* bClockStop */
+ 0x20, /* bIFSC */
+ 0x00 /* bNadValue */
+ };
/* First check whether a card is available. */
rc = ccid_slot_status (handle, &statusbits);
@@ -2354,7 +2582,8 @@ ccid_get_atr (ccid_driver_t handle,
msg[0] = PC_to_RDR_IccPowerOn;
msg[5] = 0; /* slot */
msg[6] = seqno = handle->seqno++;
- msg[7] = 0; /* power select (0=auto, 1=5V, 2=3V, 3=1.8V) */
+ /* power select (0=auto, 1=5V, 2=3V, 3=1.8V) */
+ msg[7] = handle->auto_voltage ? 0 : 1;
msg[8] = 0; /* RFU */
msg[9] = 0; /* RFU */
set_msg_len (msg, 0);
@@ -2385,7 +2614,7 @@ ccid_get_atr (ccid_driver_t handle,
handle->powered_off = 0;
-
+
if (atr)
{
size_t n = msglen - 10;
@@ -2396,23 +2625,73 @@ ccid_get_atr (ccid_driver_t handle,
*atrlen = n;
}
+ 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;
+ }
+
got_param = 0;
- 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)
- rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters,
- seqno, 2000, 0);
- if (rc)
- DEBUGOUT ("GetParameters failed\n");
- else if (msglen == 17 && msg[9] == 1)
- got_param = 1;
+
+ 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)
+ rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters,
+ seqno, 2000, 0);
+ if (rc)
+ DEBUGOUT ("GetParameters failed\n");
+ else if (msglen == 17 && msg[9] == 1)
+ got_param = 1;
+ }
+ else if (handle->auto_pps)
+ ;
+ else if (rc == 1) /* It's negotiable, send PPS. */
+ {
+ msg[0] = PC_to_RDR_XfrBlock;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 0;
+ msg[8] = 0;
+ msg[9] = 0;
+ msg[10] = 0xff; /* PPSS */
+ msg[11] = 0x11; /* PPS0: PPS1, Protocol T=1 */
+ msg[12] = param[0]; /* PPS1: Fi / Di */
+ msg[13] = 0xff ^ 0x11 ^ param[0]; /* PCK */
+ set_msg_len (msg, 4);
+ msglen = 10 + 4;
+
+ rc = bulk_out (handle, msg, msglen, 0);
+ if (rc)
+ return rc;
+
+ rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_DataBlock,
+ seqno, 5000, 0);
+ if (rc)
+ return rc;
+
+ if (msglen != 10 + 4)
+ {
+ DEBUGOUT_1 ("Setting PPS failed: %d\n", (int)msglen);
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+
+ if (msg[10] != 0xff || msg[11] != 0x11 || msg[12] != param[0])
+ {
+ DEBUGOUT_1 ("Setting PPS failed: 0x%02x\n", param[0]);
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ }
/* Setup parameters to select T=1. */
msg[0] = PC_to_RDR_SetParameters;
@@ -2423,16 +2702,7 @@ ccid_get_atr (ccid_driver_t handle,
msg[9] = 0; /* RFU */
if (!got_param)
- {
- /* FIXME: Get those values from the ATR. */
- msg[10]= 0x01; /* Fi/Di */
- msg[11]= 0x10; /* LRC, direct convention. */
- msg[12]= 0; /* Extra guardtime. */
- msg[13]= 0x41; /* BWI/CWI */
- msg[14]= 0; /* No clock stoppping. */
- msg[15]= 254; /* IFSC */
- msg[16]= 0; /* Does not support non default NAD values. */
- }
+ memcpy (&msg[10], param, 7);
set_msg_len (msg, 7);
msglen = 10 + 7;
@@ -2449,6 +2719,12 @@ ccid_get_atr (ccid_driver_t handle,
else
handle->ifsc = 128; /* Something went wrong, assume 128 bytes. */
+ if (handle->nonnull_nad && msglen > 16 && msg[16] == 0)
+ {
+ DEBUGOUT ("Use Null-NAD, clearing handle->nonnull_nad.\n");
+ handle->nonnull_nad = 0;
+ }
+
handle->t1_ns = 0;
handle->t1_nr = 0;
@@ -2460,7 +2736,7 @@ ccid_get_atr (ccid_driver_t handle,
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = (0xc0 | 0 | 1); /* S-block request: change IFSD */
tpdu[2] = 1;
- tpdu[3] = handle->max_ifsd? handle->max_ifsd : 32;
+ tpdu[3] = handle->max_ifsd? handle->max_ifsd : 32;
tpdulen = 4;
edc = compute_edc (tpdu, tpdulen, use_crc);
if (use_crc)
@@ -2470,7 +2746,7 @@ ccid_get_atr (ccid_driver_t handle,
msg[0] = PC_to_RDR_XfrBlock;
msg[5] = 0; /* slot */
msg[6] = seqno = handle->seqno++;
- msg[7] = 0;
+ msg[7] = 0;
msg[8] = 0; /* RFU */
msg[9] = 0; /* RFU */
set_msg_len (msg, tpdulen);
@@ -2493,12 +2769,12 @@ ccid_get_atr (ccid_driver_t handle,
RDR_to_PC_DataBlock, seqno, 5000, 0);
if (rc)
return rc;
-
+
tpdu = msg + 10;
tpdulen = msglen - 10;
-
- if (tpdulen < 4)
- return CCID_DRIVER_ERR_ABORTED;
+
+ if (tpdulen < 4)
+ return CCID_DRIVER_ERR_ABORTED;
if (debug_level > 1)
DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
@@ -2523,7 +2799,7 @@ ccid_get_atr (ccid_driver_t handle,
-static unsigned int
+static unsigned int
compute_edc (const unsigned char *data, size_t datalen, int use_crc)
{
if (use_crc)
@@ -2533,7 +2809,7 @@ compute_edc (const unsigned char *data, size_t datalen, int use_crc)
else
{
unsigned char crc = 0;
-
+
for (; datalen; datalen--)
crc ^= *data++;
return crc;
@@ -2575,10 +2851,10 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
/* The maximum length for a short APDU T=1 block is 261. For an
extended APDU T=1 block the maximum length 65544; however
- extended APDU exchange level is not yet supported. */
- if (apdulen > 261)
+ extended APDU exchange level is not fully supported yet. */
+ if (apdulen > sizeof (send_buffer) - 10)
return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
-
+
msg[0] = PC_to_RDR_XfrBlock;
msg[5] = 0; /* slot */
msg[6] = seqno = handle->seqno++;
@@ -2598,10 +2874,53 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
RDR_to_PC_DataBlock, seqno, 5000, 0);
if (rc)
return rc;
-
- apdu = msg + 10;
- apdulen = msglen - 10;
-
+
+ if (msg[9] == 1)
+ {
+ size_t total_msglen = msglen;
+
+ while (1)
+ {
+ unsigned char status;
+
+ msg = recv_buffer + total_msglen;
+
+ msg[0] = PC_to_RDR_XfrBlock;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = bwi; /* bBWI */
+ 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 recv_buffer - total_msglen, &msglen,
+ RDR_to_PC_DataBlock, seqno, 5000, 0);
+ if (rc)
+ return rc;
+ status = msg[9];
+ memmove (msg, msg+10, msglen - 10);
+ total_msglen += msglen - 10;
+ if (total_msglen >= sizeof recv_buffer)
+ return CCID_DRIVER_ERR_OUT_OF_CORE;
+
+ if (status == 0x02)
+ break;
+ }
+
+ apdu = recv_buffer + 10;
+ apdulen = total_msglen - 10;
+ }
+ else
+ {
+ apdu = msg + 10;
+ apdulen = msglen - 10;
+ }
+
if (resp)
{
if (apdulen > maxresplen)
@@ -2611,11 +2930,11 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
(unsigned int)apdulen, (unsigned int)maxresplen);
return CCID_DRIVER_ERR_INV_VALUE;
}
-
- memcpy (resp, apdu, apdulen);
+
+ memcpy (resp, apdu, apdulen);
*nresp = apdulen;
}
-
+
return 0;
}
@@ -2626,15 +2945,15 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
Block Structure:
Prologue Field:
- 1 byte Node Address (NAD)
+ 1 byte Node Address (NAD)
1 byte Protocol Control Byte (PCB)
- 1 byte Length (LEN)
+ 1 byte Length (LEN)
Information Field:
0-254 byte APDU or Control Information (INF)
Epilogue Field:
1 byte Error Detection Code (EDC)
- NAD:
+ NAD:
bit 7 unused
bit 4..6 Destination Node Address (DAD)
bit 3 unused
@@ -2649,7 +2968,7 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
Information Block (I-Block):
bit 7 0
bit 6 Sequence number (yep, that is modulo 2)
- bit 5 Chaining flag
+ bit 5 Chaining flag
bit 4..0 reserved
Received-Ready Block (R-Block):
bit 7 1
@@ -2752,7 +3071,7 @@ ccid_transceive (ccid_driver_t handle,
if (apdulen > handle->ifsc )
{
apdulen = handle->ifsc;
- apdu_buf += handle->ifsc;
+ apdu_buf += handle->ifsc;
apdu_buflen -= handle->ifsc;
tpdu[1] |= (1 << 5); /* Set more bit. */
}
@@ -2799,14 +3118,14 @@ ccid_transceive (ccid_driver_t handle,
: !!(msg[pcboff] & 0x40)),
(!(msg[pcboff] & 0x80) && (msg[pcboff] & 0x20)?
" [more]":""));
-
+
rc = bulk_out (handle, msg, msglen, 0);
if (rc)
return rc;
msg = recv_buffer;
rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
- via_escape? RDR_to_PC_Escape : RDR_to_PC_DataBlock,
+ via_escape? RDR_to_PC_Escape : RDR_to_PC_DataBlock,
seqno, 5000, 0);
if (rc)
return rc;
@@ -2814,11 +3133,11 @@ ccid_transceive (ccid_driver_t handle,
tpdu = msg + hdrlen;
tpdulen = msglen - hdrlen;
resyncing = 0;
-
- if (tpdulen < 4)
+
+ if (tpdulen < 4)
{
usb_clear_halt (handle->idev, handle->ep_bulk_in);
- return CCID_DRIVER_ERR_ABORTED;
+ return CCID_DRIVER_ERR_ABORTED;
}
if (debug_level > 1)
@@ -2870,16 +3189,16 @@ ccid_transceive (ccid_driver_t handle,
(unsigned int)n, (unsigned int)maxresplen);
return CCID_DRIVER_ERR_INV_VALUE;
}
-
- memcpy (resp, p, n);
+
+ memcpy (resp, p, n);
resp += n;
*nresp += n;
maxresplen -= n;
}
-
+
if (!(tpdu[1] & 0x20))
return 0; /* No chaining requested - ready. */
-
+
msg = send_buffer;
tpdu = msg + hdrlen;
tpdu[0] = nad_byte;
@@ -2893,8 +3212,8 @@ ccid_transceive (ccid_driver_t handle,
}
else if ((tpdu[1] & 0xc0) == 0x80)
{ /* This is a R-block. */
- if ( (tpdu[1] & 0x0f))
- {
+ if ( (tpdu[1] & 0x0f))
+ {
retries++;
if (via_escape && retries == 1 && (msg[pcboff] & 0x0f))
{
@@ -2944,7 +3263,7 @@ ccid_transceive (ccid_driver_t handle,
return CCID_DRIVER_ERR_CARD_IO_ERROR;
}
}
- else
+ else
{ /* This is a S-block. */
retries = 0;
DEBUGOUT_2 ("T=1: S-block %s received cmd=%d\n",
@@ -3008,22 +3327,21 @@ ccid_transceive (ccid_driver_t handle,
/* Send the CCID Secure command to the reader. APDU_BUF should
contain the APDU template. PIN_MODE defines how the pin gets
formatted:
-
+
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
- may be used t enable reasonable defaults. PIN_PADLEN should be 0.
-
+ may be used t enable reasonable defaults.
+
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,
- int pin_mode, int pinlen_min, int pinlen_max,
- int pin_padlen,
+ pininfo_t *pininfo,
unsigned char *resp, size_t maxresplen, size_t *nresp)
{
int rc;
@@ -3034,6 +3352,7 @@ ccid_transceive_secure (ccid_driver_t handle,
size_t dummy_nresp;
int testmode;
int cherry_mode = 0;
+ int enable_varlen = 0;
testmode = !resp && !nresp;
@@ -3044,25 +3363,19 @@ ccid_transceive_secure (ccid_driver_t handle,
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))
- return CCID_DRIVER_ERR_NOT_SUPPORTED; /* Not yet by our code. */
+ ;
else
- return CCID_DRIVER_ERR_NO_KEYPAD;
-
- if (pin_mode != 1)
- return CCID_DRIVER_ERR_NOT_SUPPORTED;
+ return CCID_DRIVER_ERR_NO_PINPAD;
- if (pin_padlen != 0)
- return CCID_DRIVER_ERR_NOT_SUPPORTED;
-
- if (!pinlen_min)
- pinlen_min = 1;
- if (!pinlen_max)
- pinlen_max = 25;
+ if (!pininfo->minlen)
+ pininfo->minlen = 1;
+ if (!pininfo->maxlen)
+ pininfo->maxlen = 15;
/* Note that the 25 is the maximum value the SPR532 allows. */
- if (pinlen_min < 1 || pinlen_min > 25
- || pinlen_max < 1 || pinlen_max > 25
- || pinlen_min > pinlen_max)
+ if (pininfo->minlen < 1 || pininfo->minlen > 25
+ || pininfo->maxlen < 1 || pininfo->maxlen > 25
+ || pininfo->minlen > pininfo->maxlen)
return CCID_DRIVER_ERR_INV_VALUE;
/* We have only tested a few readers so better don't risk anything
@@ -3071,8 +3384,17 @@ ccid_transceive_secure (ccid_driver_t handle,
{
case VENDOR_SCM: /* Tested with SPR 532. */
case VENDOR_KAAN: /* Tested with KAAN Advanced (1.02). */
+ case VENDOR_FSIJ: /* Tested with Gnuk (0.21). */
+ pininfo->maxlen = 25;
+ enable_varlen = 1;
+ break;
+ case VENDOR_REINER: /* Tested with cyberJack go */
+ case VENDOR_VASCO: /* Tested with DIGIPASS 920 */
+ enable_varlen = 1;
break;
case VENDOR_CHERRY:
+ pininfo->maxlen = 25;
+ enable_varlen = 1;
/* 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
@@ -3080,15 +3402,32 @@ ccid_transceive_secure (ccid_driver_t handle,
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. */
- cherry_mode = 1;
+ if (handle->id_product != CHERRY_ST2000)
+ cherry_mode = 1;
break;
default:
+ if ((handle->id_vendor == VENDOR_GEMPC &&
+ handle->id_product == GEMPC_PINPAD)
+ || (handle->id_vendor == VENDOR_VEGA &&
+ handle->id_product == VEGA_ALPHA))
+ {
+ enable_varlen = 0;
+ pininfo->minlen = 4;
+ pininfo->maxlen = 8;
+ break;
+ }
return CCID_DRIVER_ERR_NOT_SUPPORTED;
}
+ if (enable_varlen)
+ pininfo->fixedlen = 0;
+
if (testmode)
return 0; /* Success */
-
+
+ if (pininfo->fixedlen < 0 || pininfo->fixedlen >= 16)
+ return CCID_DRIVER_ERR_NOT_SUPPORTED;
+
msg = send_buffer;
if (handle->id_vendor == VENDOR_SCM)
{
@@ -3105,7 +3444,8 @@ ccid_transceive_secure (ccid_driver_t handle,
msg[7] = 0; /* bBWI */
msg[8] = 0; /* RFU */
msg[9] = 0; /* RFU */
- msg[10] = 0; /* Perform PIN verification. */
+ msg[10] = apdu_buf[1] == 0x20 ? 0 : 1;
+ /* Perform PIN verification or PIN modification. */
msg[11] = 0; /* Timeout in seconds. */
msg[12] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */
if (handle->id_vendor == VENDOR_SCM)
@@ -3117,50 +3457,94 @@ ccid_transceive_secure (ccid_driver_t handle,
}
else
{
- msg[13] = 0x00; /* bmPINBlockString:
- 0 bits of pin length to insert.
- 0 bytes of PIN block size. */
+ msg[13] = pininfo->fixedlen; /* bmPINBlockString:
+ 0 bits of pin length to insert.
+ PIN block size by fixedlen. */
msg[14] = 0x00; /* bmPINLengthFormat:
Units are bytes, position is 0. */
}
+ msglen = 15;
+ if (apdu_buf[1] == 0x24)
+ {
+ msg[msglen++] = 0; /* bInsertionOffsetOld */
+ msg[msglen++] = pininfo->fixedlen; /* bInsertionOffsetNew */
+ }
+
/* The following is a little endian word. */
- msg[15] = pinlen_max; /* wPINMaxExtraDigit-Maximum. */
- msg[16] = pinlen_min; /* wPINMaxExtraDigit-Minimum. */
-
- msg[17] = 0x02; /* bEntryValidationCondition:
- Validation key pressed */
- if (pinlen_min && pinlen_max && pinlen_min == pinlen_max)
- msg[17] |= 0x01; /* Max size reached. */
- msg[18] = 0xff; /* bNumberMessage: Default. */
- msg[19] = 0x04; /* wLangId-High. */
- msg[20] = 0x09; /* wLangId-Low: English FIXME: use the first entry. */
- msg[21] = 0; /* bMsgIndex. */
+ msg[msglen++] = pininfo->maxlen; /* wPINMaxExtraDigit-Maximum. */
+ msg[msglen++] = pininfo->minlen; /* wPINMaxExtraDigit-Minimum. */
+
+ 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 */
+ if (pininfo->minlen && pininfo->maxlen && pininfo->minlen == pininfo->maxlen)
+ msg[msglen] |= 0x01; /* Max size reached. */
+ msglen++;
+
+ if (apdu_buf[1] == 0x20)
+ msg[msglen++] = 0x01; /* bNumberMessage. */
+ 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. */
+ }
+
+ /* Calculate Lc. */
+ n = pininfo->fixedlen;
+ if (apdu_buf[1] == 0x24)
+ n += pininfo->fixedlen;
+
/* bTeoProlog follows: */
- msg[22] = handle->nonnull_nad? ((1 << 4) | 0): 0;
- msg[23] = ((handle->t1_ns & 1) << 6); /* I-block */
- msg[24] = 0; /* The apdulen will be filled in by the reader. */
+ msg[msglen++] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+ msg[msglen++] = ((handle->t1_ns & 1) << 6); /* I-block */
+ 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. */
/* APDU follows: */
- msg[25] = apdu_buf[0]; /* CLA */
- msg[26] = apdu_buf[1]; /* INS */
- msg[27] = apdu_buf[2]; /* P1 */
- msg[28] = apdu_buf[3]; /* P2 */
- msglen = 29;
+ msg[msglen++] = apdu_buf[0]; /* CLA */
+ msg[msglen++] = apdu_buf[1]; /* INS */
+ msg[msglen++] = apdu_buf[2]; /* P1 */
+ msg[msglen++] = apdu_buf[3]; /* P2 */
if (cherry_mode)
msg[msglen++] = 0;
+ else if (pininfo->fixedlen != 0)
+ {
+ msg[msglen++] = n;
+ memset (&msg[msglen], 0xff, n);
+ msglen += n;
+ }
/* An EDC is not required. */
set_msg_len (msg, msglen - 10);
rc = bulk_out (handle, msg, msglen, 0);
if (rc)
return rc;
-
+
msg = recv_buffer;
rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
RDR_to_PC_DataBlock, seqno, 30000, 0);
if (rc)
return rc;
-
+
tpdu = msg + 10;
tpdulen = msglen - 10;
@@ -3175,17 +3559,17 @@ ccid_transceive_secure (ccid_driver_t handle,
(unsigned int)tpdulen, (unsigned int)maxresplen);
return CCID_DRIVER_ERR_INV_VALUE;
}
-
- memcpy (resp, tpdu, tpdulen);
+
+ memcpy (resp, tpdu, tpdulen);
*nresp = tpdulen;
}
return 0;
}
-
- if (tpdulen < 4)
+
+ if (tpdulen < 4)
{
usb_clear_halt (handle->idev, handle->ep_bulk_in);
- return CCID_DRIVER_ERR_ABORTED;
+ return CCID_DRIVER_ERR_ABORTED;
}
if (debug_level > 1)
DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
@@ -3220,22 +3604,22 @@ ccid_transceive_secure (ccid_driver_t handle,
(unsigned int)n, (unsigned int)maxresplen);
return CCID_DRIVER_ERR_INV_VALUE;
}
-
- memcpy (resp, p, n);
+
+ memcpy (resp, p, n);
resp += n;
*nresp += n;
maxresplen -= n;
}
-
+
if (!(tpdu[1] & 0x20))
return 0; /* No chaining requested - ready. */
-
+
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. */
- if ( (tpdu[1] & 0x0f))
+ if ( (tpdu[1] & 0x0f))
{ /* Error: repeat last block */
DEBUGOUT ("No retries supported for Secure operation\n");
return CCID_DRIVER_ERR_CARD_IO_ERROR;
@@ -3251,13 +3635,13 @@ ccid_transceive_secure (ccid_driver_t handle,
return CCID_DRIVER_ERR_CARD_IO_ERROR;
}
}
- else
+ else
{ /* This is a S-block. */
DEBUGOUT_2 ("T=1: S-block %s received cmd=%d for Secure operation\n",
(tpdu[1] & 0x20)? "response": "request",
(tpdu[1] & 0x1f));
return CCID_DRIVER_ERR_CARD_IO_ERROR;
- }
+ }
return 0;
}
@@ -3413,7 +3797,7 @@ main (int argc, char **argv)
result, sizeof result, &resultlen);
print_result (rc, result, resultlen);
}
-
+
if (!no_poll)
ccid_poll (ccid);
@@ -3434,7 +3818,7 @@ main (int argc, char **argv)
{
static unsigned char apdu[] = { 0, 0x20, 0, 0x81 };
-
+
if (ccid_transceive_secure (ccid,
apdu, sizeof apdu,
1, 0, 0, 0,
@@ -3443,7 +3827,7 @@ main (int argc, char **argv)
else
{
fputs ("verifying CHV1 using the PINPad ....\n", stderr);
-
+
rc = ccid_transceive_secure (ccid,
apdu, sizeof apdu,
1, 0, 0, 0,
@@ -3452,7 +3836,7 @@ main (int argc, char **argv)
did_verify = 1;
}
}
-
+
if (verify_123456 && !did_verify)
{
fputs ("verifying that CHV1 is 123456....\n", stderr);
diff --git a/scd/ccid-driver.h b/scd/ccid-driver.h
index 6bb1913..2af1a18 100644
--- a/scd/ccid-driver.h
+++ b/scd/ccid-driver.h
@@ -72,7 +72,7 @@
#define CCID_DRIVER_ERR_GENERAL_ERROR 0x1000b
#define CCID_DRIVER_ERR_NO_READER 0x1000c
#define CCID_DRIVER_ERR_ABORTED 0x1000d
-#define CCID_DRIVER_ERR_NO_KEYPAD 0x1000e
+#define CCID_DRIVER_ERR_NO_PINPAD 0x1000e
struct ccid_driver_s;
typedef struct ccid_driver_s *ccid_driver_t;
@@ -93,8 +93,7 @@ int ccid_transceive (ccid_driver_t handle,
unsigned char *resp, size_t maxresplen, size_t *nresp);
int ccid_transceive_secure (ccid_driver_t handle,
const unsigned char *apdu, size_t apdulen,
- int pin_mode,
- int pinlen_min, int pinlen_max, int pin_padlen,
+ pininfo_t *pininfo,
unsigned char *resp, size_t maxresplen, size_t *nresp);
int ccid_transceive_escape (ccid_driver_t handle,
const unsigned char *data, size_t datalen,
diff --git a/scd/command.c b/scd/command.c
index 52b22c6..fc1f5a2 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -34,6 +34,7 @@
#include <assuan.h>
#include <ksba.h>
#include "app-common.h"
+#include "iso7816.h"
#include "apdu.h" /* Required for apdu_*_reader (). */
#include "exechelp.h"
#ifdef HAVE_LIBUSB
@@ -46,6 +47,9 @@
/* Maximum allowed size of key data as used in inquiries. */
#define MAXLEN_KEYDATA 4096
+/* Maximum allowed total data size for SETDATA. */
+#define MAXLEN_SETDATA 4096
+
/* Maximum allowed size of certificate data as used in inquiries. */
#define MAXLEN_CERTDATA 16384
@@ -60,6 +64,7 @@
int _r = (r); \
if (gpg_err_code (_r) == GPG_ERR_CARD_NOT_PRESENT \
|| gpg_err_code (_r) == GPG_ERR_CARD_REMOVED \
+ || gpg_err_code (_r) == GPG_ERR_CARD_RESET \
|| gpg_err_code (_r) == GPG_ERR_ENODEV ) \
update_card_removed ((c)->reader_slot, 1); \
} while (0)
@@ -70,12 +75,8 @@
&& (c)->reader_slot == locked_session->ctrl_backlink->reader_slot)
-/* Flag indicating that the reader has been disabled. */
-static int reader_disabled;
-
-
/* This structure is used to keep track of open readers (slots). */
-struct slot_status_s
+struct slot_status_s
{
int valid; /* True if the other objects are valid. */
int slot; /* Slot number of the reader or -1 if not open. */
@@ -92,11 +93,11 @@ struct slot_status_s
/* Data used to associate an Assuan context with local server data.
This object describes the local properties of one session. */
-struct server_local_s
+struct server_local_s
{
/* We keep a list of all active sessions with the anchor at
SESSION_LIST (see below). This field is used for linking. */
- struct server_local_s *next_session;
+ struct server_local_s *next_session;
/* This object is usually assigned to a CTRL object (which is
globally visible). While enumerating all sessions we sometimes
@@ -112,10 +113,10 @@ struct server_local_s
#else
int event_signal; /* Or 0 if not used. */
#endif
-
+
/* True if the card has been removed and a reset is required to
continue operation. */
- int card_removed;
+ int card_removed;
/* Flag indicating that the application context needs to be released
at the next opportunity. */
@@ -126,7 +127,7 @@ struct server_local_s
/* If set to true we will be terminate ourself at the end of the
this session. */
- int stopme;
+ int stopme;
};
@@ -180,6 +181,9 @@ update_card_removed (int slot, int value)
{
struct server_local_s *sl;
+ if (slot == -1)
+ return;
+
for (sl=session_list; sl; sl = sl->next_session)
if (sl->ctrl_backlink
&& sl->ctrl_backlink->reader_slot == slot)
@@ -256,7 +260,7 @@ hex_to_buffer (const char *string, size_t *r_length)
return NULL;
for (s=string, n=0; *s; s++)
{
- if (spacep (s) || *s == ':')
+ if (spacep (s) || *s == ':')
continue;
if (hexdigitp (s) && hexdigitp (s+1))
{
@@ -293,7 +297,7 @@ do_reset (ctrl_t ctrl, int send_reset)
if (send_reset)
{
struct server_local_s *sl;
-
+
for (sl=session_list; sl; sl = sl->next_session)
if (sl->ctrl_backlink
&& sl->ctrl_backlink->reader_slot == slot)
@@ -307,11 +311,19 @@ do_reset (ctrl_t ctrl, int send_reset)
tell the application layer about it. */
if (slot != -1 && send_reset && !IS_LOCKED (ctrl) )
{
- if (apdu_reset (slot))
- {
- slot_table[slot].valid = 0;
- }
application_notify_card_reset (slot);
+ switch (apdu_reset (slot))
+ {
+ case 0:
+ break;
+ case SW_HOST_NO_CARD:
+ case SW_HOST_CARD_INACTIVE:
+ break;
+ default:
+ apdu_close_reader (slot);
+ slot_table[slot].slot = -1;
+ break;
+ }
}
/* If we hold a lock, unlock now. */
@@ -345,7 +357,7 @@ do_reset (ctrl_t ctrl, int send_reset)
static gpg_error_t
reset_notify (assuan_context_t ctx, char *line)
{
- ctrl_t ctrl = assuan_get_pointer (ctx);
+ ctrl_t ctrl = assuan_get_pointer (ctx);
(void) line;
@@ -399,39 +411,29 @@ get_reader_slot (void)
/* Try to open the reader. */
if (ss->slot == -1)
{
- int no_service_flag;
- ss->slot = apdu_open_reader (opt.reader_port, &no_service_flag);
+ ss->slot = apdu_open_reader (opt.reader_port);
/* If we still don't have a slot, we have no readers.
Invalidate for now until a reader is attached. */
if(ss->slot == -1)
{
ss->valid = 0;
+ return -1;
}
-
- if (no_service_flag)
- {
- log_info ("no card services - disabling scdaemon\n");
- reader_disabled = 1;
- }
}
/* Return the slot_table index. */
return 0;
}
-/* If the card has not yet been opened, do it. Note that this
- function returns an Assuan error, so don't map the error a second
- time. */
+
+/* If the card has not yet been opened, do it. */
static gpg_error_t
open_card (ctrl_t ctrl, const char *apptype)
{
gpg_error_t err;
int slot;
- if (reader_disabled)
- return gpg_error (GPG_ERR_NOT_OPERATIONAL);
-
/* If we ever got a card not present error code, return that. Only
the SERIALNO command and a reset are able to clear from that
state. */
@@ -464,7 +466,7 @@ open_card (ctrl_t ctrl, const char *apptype)
slot = get_reader_slot ();
ctrl->reader_slot = slot;
if (slot == -1)
- err = gpg_error (reader_disabled? GPG_ERR_NOT_OPERATIONAL: GPG_ERR_CARD);
+ err = gpg_error (GPG_ERR_CARD);
else
{
/* Fixme: We should move the apdu_connect call to
@@ -477,6 +479,8 @@ open_card (ctrl_t ctrl, const char *apptype)
{
if (sw == SW_HOST_NO_CARD)
err = gpg_error (GPG_ERR_CARD_NOT_PRESENT);
+ else if (sw == SW_HOST_CARD_INACTIVE)
+ err = gpg_error (GPG_ERR_CARD_RESET);
else
err = gpg_error (GPG_ERR_CARD);
}
@@ -489,7 +493,7 @@ open_card (ctrl_t ctrl, const char *apptype)
}
-static const char hlp_serialno[] =
+static const char hlp_serialno[] =
"SERIALNO [<apptype>]\n"
"\n"
"Return the serial number of the card using a status reponse. This\n"
@@ -516,9 +520,11 @@ cmd_serialno (assuan_context_t ctx, char *line)
char *serial_and_stamp;
char *serial;
time_t stamp;
+ int retries = 0;
/* Clear the remove flag so that the open_card is able to reread it. */
- if (!reader_disabled && ctrl->server_local->card_removed)
+ retry:
+ if (ctrl->server_local->card_removed)
{
if ( IS_LOCKED (ctrl) )
return gpg_error (GPG_ERR_LOCKED);
@@ -526,7 +532,12 @@ cmd_serialno (assuan_context_t ctx, char *line)
}
if ((rc = open_card (ctrl, *line? line:NULL)))
- return rc;
+ {
+ /* In case of an inactive card, retry once. */
+ if (gpg_err_code (rc) == GPG_ERR_CARD_RESET && retries++ < 1)
+ goto retry;
+ return rc;
+ }
rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp);
if (rc)
@@ -544,7 +555,7 @@ cmd_serialno (assuan_context_t ctx, char *line)
}
-static const char hlp_learn[] =
+static const char hlp_learn[] =
"LEARN [--force] [--keypairinfo]\n"
"\n"
"Learn all useful information of the currently inserted card. When\n"
@@ -632,7 +643,7 @@ cmd_learn (assuan_context_t ctx, char *line)
char *serial_and_stamp;
char *serial;
time_t stamp;
-
+
rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp);
if (rc)
return rc;
@@ -643,11 +654,11 @@ cmd_learn (assuan_context_t ctx, char *line)
return out_of_core ();
rc = 0;
assuan_write_status (ctx, "SERIALNO", serial_and_stamp);
-
+
if (!has_option (line, "--force"))
{
char *command;
-
+
rc = estream_asprintf (&command, "KNOWNCARDP %s", serial_and_stamp);
if (rc < 0)
{
@@ -655,7 +666,7 @@ cmd_learn (assuan_context_t ctx, char *line)
return out_of_core ();
}
rc = 0;
- rc = assuan_inquire (ctx, command, NULL, NULL, 0);
+ rc = assuan_inquire (ctx, command, NULL, NULL, 0);
xfree (command);
if (rc)
{
@@ -663,13 +674,13 @@ cmd_learn (assuan_context_t ctx, char *line)
log_error ("inquire KNOWNCARDP failed: %s\n",
gpg_strerror (rc));
xfree (serial_and_stamp);
- return rc;
+ return rc;
}
/* Not canceled, so we have to proceeed. */
}
xfree (serial_and_stamp);
}
-
+
/* Let the application print out its collection of useful status
information. */
if (!rc)
@@ -715,7 +726,7 @@ cmd_readcert (assuan_context_t ctx, char *line)
}
-static const char hlp_readkey[] =
+static const char hlp_readkey[] =
"READKEY <keyid>\n"
"\n"
"Return the public key for the given cert or key ID as a standard\n"
@@ -753,7 +764,7 @@ cmd_readkey (assuan_context_t ctx, char *line)
if (gpg_err_code (rc) != GPG_ERR_UNSUPPORTED_OPERATION)
log_error ("app_readkey failed: %s\n", gpg_strerror (rc));
- else
+ else
{
rc = app_readcert (ctrl->app_ctx, line, &cert, &ncert);
if (rc)
@@ -763,7 +774,7 @@ cmd_readkey (assuan_context_t ctx, char *line)
line = NULL;
if (rc)
goto leave;
-
+
rc = ksba_cert_new (&kc);
if (rc)
{
@@ -798,18 +809,25 @@ cmd_readkey (assuan_context_t ctx, char *line)
-static const char hlp_setdata[] =
- "SETDATA <hexstring> \n"
+static const char hlp_setdata[] =
+ "SETDATA [--append] <hexstring>\n"
"\n"
- "The client should use this command to tell us the data he want to sign.";
+ "The client should use this command to tell us the data he want to sign.\n"
+ "With the option --append, the data is appended to the data set by a\n"
+ "previous SETDATA command.";
static gpg_error_t
cmd_setdata (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
- int n;
+ int append;
+ int n, i, off;
char *p;
unsigned char *buf;
+ append = (ctrl->in_data.value && has_option (line, "--append"));
+
+ line = skip_options (line);
+
if (locked_session && locked_session != ctrl->server_local)
return gpg_error (GPG_ERR_LOCKED);
@@ -823,20 +841,36 @@ cmd_setdata (assuan_context_t ctx, char *line)
if ((n&1))
return set_error (GPG_ERR_ASS_PARAMETER, "odd number of digits");
n /= 2;
+ if (append)
+ {
+ if (ctrl->in_data.valuelen + n > MAXLEN_SETDATA)
+ return set_error (GPG_ERR_TOO_LARGE,
+ "limit on total size of data reached");
+ buf = xtrymalloc (ctrl->in_data.valuelen + n);
+ }
+ else
buf = xtrymalloc (n);
if (!buf)
return out_of_core ();
+ if (append)
+ {
+ memcpy (buf, ctrl->in_data.value, ctrl->in_data.valuelen);
+ off = ctrl->in_data.valuelen;
+ }
+ else
+ off = 0;
+ for (p=line, i=0; i < n; p += 2, i++)
+ buf[off+i] = xtoi_2 (p);
+
ctrl->in_data.value = buf;
- ctrl->in_data.valuelen = n;
- for (p=line, n=0; n < ctrl->in_data.valuelen; p += 2, n++)
- buf[n] = xtoi_2 (p);
+ ctrl->in_data.valuelen = off + n;
return 0;
}
-static gpg_error_t
+static gpg_error_t
pin_cb (void *opaque, const char *info, char **retstr)
{
assuan_context_t ctx = opaque;
@@ -847,23 +881,23 @@ pin_cb (void *opaque, const char *info, char **retstr)
if (!retstr)
{
- /* We prompt for keypad entry. To make sure that the popup has
+ /* We prompt for pinpad entry. To make sure that the popup has
been show we use an inquire and not just a status message.
We ignore any value returned. */
if (info)
{
- log_debug ("prompting for keypad entry '%s'\n", info);
- rc = estream_asprintf (&command, "POPUPKEYPADPROMPT %s", info);
+ log_debug ("prompting for pinpad entry '%s'\n", info);
+ rc = estream_asprintf (&command, "POPUPPINPADPROMPT %s", info);
if (rc < 0)
return gpg_error (gpg_err_code_from_errno (errno));
- rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN);
- xfree (command);
+ rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN);
+ xfree (command);
}
else
{
- log_debug ("dismiss keypad entry prompt\n");
- rc = assuan_inquire (ctx, "DISMISSKEYPADPROMPT",
- &value, &valuelen, MAXLEN_PIN);
+ log_debug ("dismiss pinpad entry prompt\n");
+ rc = assuan_inquire (ctx, "DISMISSPINPADPROMPT",
+ &value, &valuelen, MAXLEN_PIN);
}
if (!rc)
xfree (value);
@@ -879,8 +913,8 @@ pin_cb (void *opaque, const char *info, char **retstr)
/* Fixme: Write an inquire function which returns the result in
secure memory and check all further handling of the PIN. */
- rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN);
- xfree (command);
+ rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN);
+ xfree (command);
if (rc)
return rc;
@@ -895,7 +929,7 @@ pin_cb (void *opaque, const char *info, char **retstr)
}
-static const char hlp_pksign[] =
+static const char hlp_pksign[] =
"PKSIGN [--hash=[rmd160|sha{1,224,256,384,512}|md5]] <hexified_id>\n"
"\n"
"The --hash option is optional; the default is SHA1.";
@@ -924,7 +958,7 @@ cmd_pksign (assuan_context_t ctx, char *line)
else if (has_option (line, "--hash=md5"))
hash_algo = GCRY_MD_MD5;
else if (!strstr (line, "--"))
- hash_algo = GCRY_MD_SHA1;
+ hash_algo = GCRY_MD_SHA1;
else
return set_error (GPG_ERR_ASS_PARAMETER, "invalid hash algorithm");
@@ -942,7 +976,7 @@ cmd_pksign (assuan_context_t ctx, char *line)
keyidstr = xtrystrdup (line);
if (!keyidstr)
return out_of_core ();
-
+
rc = app_sign (ctrl->app_ctx,
keyidstr, hash_algo,
pin_cb, ctx,
@@ -967,7 +1001,7 @@ cmd_pksign (assuan_context_t ctx, char *line)
}
-static const char hlp_pkauth[] =
+static const char hlp_pkauth[] =
"PKAUTH <hexified_id>";
static gpg_error_t
cmd_pkauth (assuan_context_t ctx, char *line)
@@ -993,7 +1027,7 @@ cmd_pkauth (assuan_context_t ctx, char *line)
keyidstr = xtrystrdup (line);
if (!keyidstr)
return out_of_core ();
-
+
rc = app_auth (ctrl->app_ctx,
keyidstr,
pin_cb, ctx,
@@ -1017,7 +1051,7 @@ cmd_pkauth (assuan_context_t ctx, char *line)
}
-static const char hlp_pkdecrypt[] =
+static const char hlp_pkdecrypt[] =
"PKDECRYPT <hexified_id>";
static gpg_error_t
cmd_pkdecrypt (assuan_context_t ctx, char *line)
@@ -1038,7 +1072,7 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
if (!keyidstr)
return out_of_core ();
rc = app_decipher (ctrl->app_ctx,
- keyidstr,
+ keyidstr,
pin_cb, ctx,
ctrl->in_data.value, ctrl->in_data.valuelen,
&outdata, &outdatalen);
@@ -1061,7 +1095,7 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
}
-static const char hlp_getattr[] =
+static const char hlp_getattr[] =
"GETATTR <name>\n"
"\n"
"This command is used to retrieve data from a smartcard. The\n"
@@ -1100,7 +1134,7 @@ cmd_getattr (assuan_context_t ctx, char *line)
}
-static const char hlp_setattr[] =
+static const char hlp_setattr[] =
"SETATTR <name> <value> \n"
"\n"
"This command is used to store data on a a smartcard. The allowed\n"
@@ -1153,7 +1187,7 @@ cmd_setattr (assuan_context_t ctx, char *orig_line)
}
-static const char hlp_writecert[] =
+static const char hlp_writecert[] =
"WRITECERT <hexified_certid>\n"
"\n"
"This command is used to store a certifciate on a smartcard. The\n"
@@ -1205,7 +1239,7 @@ cmd_writecert (assuan_context_t ctx, char *line)
}
/* Write the certificate to the card. */
- rc = app_writecert (ctrl->app_ctx, ctrl, certid,
+ rc = app_writecert (ctrl->app_ctx, ctrl, certid,
pin_cb, ctx, certdata, certdatalen);
xfree (certid);
xfree (certdata);
@@ -1215,7 +1249,7 @@ cmd_writecert (assuan_context_t ctx, char *line)
}
-static const char hlp_writekey[] =
+static const char hlp_writekey[] =
"WRITEKEY [--force] <keyid> \n"
"\n"
"This command is used to store a secret key on a a smartcard. The\n"
@@ -1282,7 +1316,7 @@ cmd_writekey (assuan_context_t ctx, char *line)
}
-static const char hlp_genkey[] =
+static const char hlp_genkey[] =
"GENKEY [--force] [--timestamp=<isodate>] <no>\n"
"\n"
"Generate a key on-card identified by NO, which is application\n"
@@ -1360,7 +1394,7 @@ cmd_genkey (assuan_context_t ctx, char *line)
}
-static const char hlp_random[] =
+static const char hlp_random[] =
"RANDOM <nbytes>\n"
"\n"
"Get NBYTES of random from the card and send them back as data.\n"
@@ -1377,7 +1411,7 @@ cmd_random (assuan_context_t ctx, char *line)
unsigned char *buffer;
if (!*line)
- return set_error (GPG_ERR_ASS_PARAMETER,
+ return set_error (GPG_ERR_ASS_PARAMETER,
"number of requested bytes missing");
nbytes = strtoul (line, NULL, 0);
@@ -1443,7 +1477,7 @@ cmd_passwd (assuan_context_t ctx, char *line)
if (!ctrl->app_ctx)
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
-
+
chvnostr = xtrystrdup (chvnostr);
if (!chvnostr)
return out_of_core ();
@@ -1457,7 +1491,7 @@ cmd_passwd (assuan_context_t ctx, char *line)
}
-static const char hlp_checkpin[] =
+static const char hlp_checkpin[] =
"CHECKPIN <idstr>\n"
"\n"
"Perform a VERIFY operation without doing anything else. This may\n"
@@ -1511,7 +1545,7 @@ cmd_checkpin (assuan_context_t ctx, char *line)
idstr = xtrystrdup (line);
if (!idstr)
return out_of_core ();
-
+
rc = app_check_pin (ctrl->app_ctx, idstr, pin_cb, ctx);
xfree (idstr);
if (rc)
@@ -1522,7 +1556,7 @@ cmd_checkpin (assuan_context_t ctx, char *line)
}
-static const char hlp_lock[] =
+static const char hlp_lock[] =
"LOCK [--wait]\n"
"\n"
"Grant exclusive card access to this session. Note that there is\n"
@@ -1559,14 +1593,14 @@ cmd_lock (assuan_context_t ctx, char *line)
goto retry;
}
#endif /*USE_GNU_PTH*/
-
+
if (rc)
log_error ("cmd_lock failed: %s\n", gpg_strerror (rc));
return rc;
}
-static const char hlp_unlock[] =
+static const char hlp_unlock[] =
"UNLOCK\n"
"\n"
"Release exclusive card access.";
@@ -1594,7 +1628,7 @@ cmd_unlock (assuan_context_t ctx, char *line)
}
-static const char hlp_getinfo[] =
+static const char hlp_getinfo[] =
"GETINFO <what>\n"
"\n"
"Multi purpose command to return certain information. \n"
@@ -1656,16 +1690,13 @@ cmd_getinfo (assuan_context_t ctx, char *line)
if (!ctrl->server_local->card_removed && slot != -1)
{
struct slot_status_s *ss;
-
+
if (!(slot >= 0 && slot < DIM(slot_table)))
BUG ();
ss = &slot_table[slot];
- if (!ss->valid)
- BUG ();
-
- if (ss->any && (ss->status & 1))
+ if (ss->valid && ss->any && (ss->status & 1))
flag = 'u';
}
rc = assuan_send_data (ctx, &flag, 1);
@@ -1677,7 +1708,7 @@ cmd_getinfo (assuan_context_t ctx, char *line)
#else
char *s = NULL;
#endif
-
+
if (s)
rc = assuan_send_data (ctx, s, strlen (s));
else
@@ -1701,7 +1732,7 @@ cmd_getinfo (assuan_context_t ctx, char *line)
}
-static const char hlp_restart[] =
+static const char hlp_restart[] =
"RESTART\n"
"\n"
"Restart the current connection; this is a kind of warm reset. It\n"
@@ -1732,7 +1763,7 @@ cmd_restart (assuan_context_t ctx, char *line)
}
-static const char hlp_disconnect[] =
+static const char hlp_disconnect[] =
"DISCONNECT\n"
"\n"
"Disconnect the card if it is not any longer used by other\n"
@@ -1743,14 +1774,14 @@ cmd_disconnect (assuan_context_t ctx, char *line)
ctrl_t ctrl = assuan_get_pointer (ctx);
(void)line;
-
+
ctrl->server_local->disconnect_allowed = 1;
return 0;
}
-static const char hlp_apdu[] =
+static const char hlp_apdu[] =
"APDU [--atr] [--more] [--exlen[=N]] [hexstring]\n"
"\n"
"Send an APDU to the current reader. This command bypasses the high\n"
@@ -1807,7 +1838,7 @@ cmd_apdu (assuan_context_t ctx, char *line)
unsigned char *atr;
size_t atrlen;
char hexbuf[400];
-
+
atr = apdu_get_atr (ctrl->reader_slot, &atrlen);
if (!atr || atrlen > sizeof hexbuf - 2 )
{
@@ -1849,7 +1880,7 @@ cmd_apdu (assuan_context_t ctx, char *line)
}
-static const char hlp_killscd[] =
+static const char hlp_killscd[] =
"KILLSCD\n"
"\n"
"Commit suicide.";
@@ -1883,8 +1914,8 @@ register_commands (assuan_context_t ctx)
{ "PKSIGN", cmd_pksign, hlp_pksign },
{ "PKAUTH", cmd_pkauth, hlp_pkauth },
{ "PKDECRYPT", cmd_pkdecrypt,hlp_pkdecrypt },
- { "INPUT", NULL },
- { "OUTPUT", NULL },
+ { "INPUT", NULL },
+ { "OUTPUT", NULL },
{ "GETATTR", cmd_getattr, hlp_getattr },
{ "SETATTR", cmd_setattr, hlp_setattr },
{ "WRITECERT", cmd_writecert,hlp_writecert },
@@ -1910,7 +1941,7 @@ register_commands (assuan_context_t ctx)
table[i].help);
if (rc)
return rc;
- }
+ }
assuan_set_hello_line (ctx, "GNU Privacy Guard's Smartcard server ready");
assuan_register_reset_notify (ctx, reset_notify);
@@ -1928,7 +1959,7 @@ scd_command_handler (ctrl_t ctrl, int fd)
int rc;
assuan_context_t ctx = NULL;
int stopme;
-
+
rc = assuan_new (&ctx);
if (rc)
{
@@ -1996,7 +2027,7 @@ scd_command_handler (ctrl_t ctrl, int fd)
log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
break;
}
-
+
rc = assuan_process (ctx);
if (rc)
{
@@ -2006,7 +2037,7 @@ scd_command_handler (ctrl_t ctrl, int fd)
}
/* Cleanup. We don't send an explicit reset to the card. */
- do_reset (ctrl, 0);
+ do_reset (ctrl, 0);
/* Release the server object. */
if (session_list == ctrl->server_local)
@@ -2014,7 +2045,7 @@ scd_command_handler (ctrl_t ctrl, int fd)
else
{
struct server_local_s *sl;
-
+
for (sl=session_list; sl->next_session; sl = sl->next_session)
if (sl->next_session == ctrl->server_local)
break;
@@ -2022,7 +2053,7 @@ scd_command_handler (ctrl_t ctrl, int fd)
BUG ();
sl->next_session = ctrl->server_local->next_session;
}
- stopme = ctrl->server_local->stopme || reader_disabled;
+ stopme = ctrl->server_local->stopme;
xfree (ctrl->server_local);
ctrl->server_local = NULL;
@@ -2049,10 +2080,10 @@ send_status_info (ctrl_t ctrl, const char *keyword, ...)
char buf[950], *p;
size_t n;
assuan_context_t ctx = ctrl->server_local->assuan_ctx;
-
+
va_start (arg_ptr, keyword);
- p = buf;
+ p = buf;
n = 0;
while ( (value = va_arg (arg_ptr, const unsigned char *)) )
{
@@ -2102,17 +2133,17 @@ static void
send_client_notifications (void)
{
struct {
- pid_t pid;
+ pid_t pid;
#ifdef HAVE_W32_SYSTEM
HANDLE handle;
#else
- int signo;
+ int signo;
#endif
} killed[50];
int killidx = 0;
int kidx;
struct server_local_s *sl;
-
+
for (sl=session_list; sl; sl = sl->next_session)
{
if (sl->event_signal && sl->assuan_ctx)
@@ -2120,9 +2151,9 @@ send_client_notifications (void)
pid_t pid = assuan_get_pid (sl->assuan_ctx);
#ifdef HAVE_W32_SYSTEM
HANDLE handle = (void *)sl->event_signal;
-
+
for (kidx=0; kidx < killidx; kidx++)
- if (killed[kidx].pid == pid
+ if (killed[kidx].pid == pid
&& killed[kidx].handle == handle)
break;
if (kidx < killidx)
@@ -2144,11 +2175,11 @@ send_client_notifications (void)
}
#else /*!HAVE_W32_SYSTEM*/
int signo = sl->event_signal;
-
+
if (pid != (pid_t)(-1) && pid && signo > 0)
{
for (kidx=0; kidx < killidx; kidx++)
- if (killed[kidx].pid == pid
+ if (killed[kidx].pid == pid
&& killed[kidx].signo == signo)
break;
if (kidx < killidx)
@@ -2182,11 +2213,6 @@ update_reader_status_file (int set_card_removed_flag)
int idx;
unsigned int status, changed;
- /* Make sure that the reader has been opened. Like get_reader_slot,
- this part of the code assumes that there is only one reader. */
- if (!slot_table[0].valid)
- (void)get_reader_slot ();
-
/* Note, that we only try to get the status, because it does not
make sense to wait here for a operation to complete. If we are
busy working with a card, delays in the status file update should
@@ -2199,11 +2225,12 @@ update_reader_status_file (int set_card_removed_flag)
if (!ss->valid || ss->slot == -1)
continue; /* Not valid or reader not yet open. */
-
+
sw_apdu = apdu_get_status (ss->slot, 0, &status, &changed);
if (sw_apdu == SW_HOST_NO_READER)
{
/* Most likely the _reader_ has been unplugged. */
+ application_notify_card_reset (ss->slot);
apdu_close_reader (ss->slot);
ss->valid = 0;
status = 0;
@@ -2212,7 +2239,7 @@ update_reader_status_file (int set_card_removed_flag)
else if (sw_apdu)
{
/* Get status failed. Ignore that. */
- continue;
+ continue;
}
if (!ss->any || ss->status != status || ss->changed != changed )
@@ -2241,14 +2268,14 @@ update_reader_status_file (int set_card_removed_flag)
fclose (fp);
}
xfree (fname);
-
+
/* If a status script is executable, run it. */
{
const char *args[9], *envs[2];
char numbuf1[30], numbuf2[30], numbuf3[30];
char *homestr, *envstr;
gpg_error_t err;
-
+
homestr = make_filename (opt.homedir, NULL);
if (estream_asprintf (&envstr, "GNUPGHOME=%s", homestr) < 0)
log_error ("out of core while building environment\n");
@@ -2261,16 +2288,16 @@ update_reader_status_file (int set_card_removed_flag)
sprintf (numbuf2, "0x%04X", ss->status);
sprintf (numbuf3, "0x%04X", status);
args[0] = "--reader-port";
- args[1] = numbuf1;
+ args[1] = numbuf1;
args[2] = "--old-code";
- args[3] = numbuf2;
+ args[3] = numbuf2;
args[4] = "--new-code";
- args[5] = numbuf3;
+ args[5] = numbuf3;
args[6] = "--status";
args[7] = ((status & 1)? "USABLE":
(status & 4)? "ACTIVE":
(status & 2)? "PRESENT": "NOCARD");
- args[8] = NULL;
+ args[8] = NULL;
fname = make_filename (opt.homedir, "scd-event", NULL);
err = gnupg_spawn_process_detached (fname, args, envs);
@@ -2283,24 +2310,22 @@ update_reader_status_file (int set_card_removed_flag)
xfree (homestr);
}
- /* Set the card removed flag for all current sessions. We
- will set this on any card change because a reset or
- SERIALNO request must be done in any case. */
- if (ss->any && set_card_removed_flag)
+ /* Set the card removed flag for all current sessions. */
+ if (ss->any && ss->status == 0 && set_card_removed_flag)
update_card_removed (idx, 1);
-
+
ss->any = 1;
/* Send a signal to all clients who applied for it. */
send_client_notifications ();
}
-
+
/* Check whether a disconnect is pending. */
if (opt.card_timeout)
{
for (sl=session_list; sl; sl = sl->next_session)
if (!sl->disconnect_allowed)
- break;
+ break;
if (session_list && !sl)
{
/* FIXME: Use a real timeout. */
@@ -2309,7 +2334,7 @@ update_reader_status_file (int set_card_removed_flag)
apdu_disconnect (ss->slot);
}
}
-
+
}
}
diff --git a/scd/iso7816.c b/scd/iso7816.c
index e3f2c1b..2a9aa53 100644
--- a/scd/iso7816.c
+++ b/scd/iso7816.c
@@ -94,7 +94,7 @@ map_sw (int sw)
case SW_HOST_GENERAL_ERROR: ec = GPG_ERR_GENERAL; break;
case SW_HOST_NO_READER: ec = GPG_ERR_ENODEV; break;
case SW_HOST_ABORTED: ec = GPG_ERR_CANCELED; break;
- case SW_HOST_NO_KEYPAD: ec = GPG_ERR_NOT_SUPPORTED; break;
+ case SW_HOST_NO_PINPAD: ec = GPG_ERR_NOT_SUPPORTED; break;
default:
if ((sw & 0x010000))
@@ -267,36 +267,26 @@ iso7816_apdu_direct (int slot, const void *apdudata, size_t apdudatalen,
/* Check whether the reader supports the ISO command code COMMAND on
- the keypad. Returns 0 on success. */
+ the pinpad. Returns 0 on success. */
gpg_error_t
-iso7816_check_keypad (int slot, int command, iso7816_pininfo_t *pininfo)
+iso7816_check_pinpad (int slot, int command, pininfo_t *pininfo)
{
int sw;
- sw = apdu_check_keypad (slot, command,
- pininfo->mode, pininfo->minlen, pininfo->maxlen,
- pininfo->padlen);
+ sw = apdu_check_pinpad (slot, command, pininfo);
return iso7816_map_sw (sw);
}
/* Perform a VERIFY command on SLOT using the card holder verification
- vector CHVNO with a CHV of lenght CHVLEN. With PININFO non-NULL
- the keypad of the reader will be used. Returns 0 on success. */
+ vector CHVNO. With PININFO non-NULL the pinpad of the reader will
+ be used. Returns 0 on success. */
gpg_error_t
-iso7816_verify_kp (int slot, int chvno, const char *chv, size_t chvlen,
- iso7816_pininfo_t *pininfo)
+iso7816_verify_kp (int slot, int chvno, pininfo_t *pininfo)
{
int sw;
- if (pininfo && pininfo->mode)
- sw = apdu_send_simple_kp (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv,
- pininfo->mode,
- pininfo->minlen,
- pininfo->maxlen,
- pininfo->padlen);
- else
- sw = apdu_send_simple (slot, 0, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv);
+ sw = apdu_pinpad_verify (slot, 0x00, CMD_VERIFY, 0, chvno, pininfo);
return map_sw (sw);
}
@@ -305,20 +295,36 @@ iso7816_verify_kp (int slot, int chvno, const char *chv, size_t chvlen,
gpg_error_t
iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen)
{
- return iso7816_verify_kp (slot, chvno, chv, chvlen, NULL);
+ int sw;
+
+ sw = apdu_send_simple (slot, 0, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv);
+ return map_sw (sw);
+}
+
+/* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder
+ verification vector CHVNO. With PININFO non-NULL the pinpad of the
+ reader will be used. If IS_EXCHANGE is 0, a "change reference
+ data" is done, otherwise an "exchange reference data". */
+gpg_error_t
+iso7816_change_reference_data_kp (int slot, int chvno, int is_exchange,
+ pininfo_t *pininfo)
+{
+ int sw;
+
+ sw = apdu_pinpad_modify (slot, 0x00, CMD_CHANGE_REFERENCE_DATA,
+ is_exchange ? 1 : 0, chvno, pininfo);
+ return map_sw (sw);
}
/* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder
verification vector CHVNO. If the OLDCHV is NULL (and OLDCHVLEN
0), a "change reference data" is done, otherwise an "exchange
reference data". The new reference data is expected in NEWCHV of
- length NEWCHVLEN. With PININFO non-NULL the keypad of the reader
- will be used. */
+ length NEWCHVLEN. */
gpg_error_t
-iso7816_change_reference_data_kp (int slot, int chvno,
- const char *oldchv, size_t oldchvlen,
- const char *newchv, size_t newchvlen,
- iso7816_pininfo_t *pininfo)
+iso7816_change_reference_data (int slot, int chvno,
+ const char *oldchv, size_t oldchvlen,
+ const char *newchv, size_t newchvlen)
{
int sw;
char *buf;
@@ -335,60 +341,13 @@ iso7816_change_reference_data_kp (int slot, int chvno,
memcpy (buf, oldchv, oldchvlen);
memcpy (buf+oldchvlen, newchv, newchvlen);
- if (pininfo && pininfo->mode)
- sw = apdu_send_simple_kp (slot, 0x00, CMD_CHANGE_REFERENCE_DATA,
- oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf,
- pininfo->mode,
- pininfo->minlen,
- pininfo->maxlen,
- pininfo->padlen);
- else
- sw = apdu_send_simple (slot, 0, 0x00, CMD_CHANGE_REFERENCE_DATA,
- oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf);
+ sw = apdu_send_simple (slot, 0, 0x00, CMD_CHANGE_REFERENCE_DATA,
+ oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf);
xfree (buf);
return map_sw (sw);
}
-/* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder
- verification vector CHVNO. If the OLDCHV is NULL (and OLDCHVLEN
- 0), a "change reference data" is done, otherwise an "exchange
- reference data". The new reference data is expected in NEWCHV of
- length NEWCHVLEN. */
-gpg_error_t
-iso7816_change_reference_data (int slot, int chvno,
- const char *oldchv, size_t oldchvlen,
- const char *newchv, size_t newchvlen)
-{
- return iso7816_change_reference_data_kp (slot, chvno, oldchv, oldchvlen,
- newchv, newchvlen, NULL);
-}
-
-
-gpg_error_t
-iso7816_reset_retry_counter_kp (int slot, int chvno,
- const char *newchv, size_t newchvlen,
- iso7816_pininfo_t *pininfo)
-{
- int sw;
-
- if (!newchv || !newchvlen )
- return gpg_error (GPG_ERR_INV_VALUE);
-
- /* FIXME: The keypad mode has not yet been tested. */
- if (pininfo && pininfo->mode)
- sw = apdu_send_simple_kp (slot, 0x00, CMD_RESET_RETRY_COUNTER,
- 2, chvno, newchvlen, newchv,
- pininfo->mode,
- pininfo->minlen,
- pininfo->maxlen,
- pininfo->padlen);
- else
- sw = apdu_send_simple (slot, 0, 0x00, CMD_RESET_RETRY_COUNTER,
- 2, chvno, newchvlen, newchv);
- return map_sw (sw);
-}
-
gpg_error_t
iso7816_reset_retry_counter_with_rc (int slot, int chvno,
@@ -409,7 +368,11 @@ gpg_error_t
iso7816_reset_retry_counter (int slot, int chvno,
const char *newchv, size_t newchvlen)
{
- return iso7816_reset_retry_counter_kp (slot, chvno, newchv, newchvlen, NULL);
+ int sw;
+
+ sw = apdu_send_simple (slot, 0, 0x00, CMD_RESET_RETRY_COUNTER,
+ 2, chvno, newchvlen, newchv);
+ return map_sw (sw);
}
diff --git a/scd/iso7816.h b/scd/iso7816.h
index 8519712..4354c72 100644
--- a/scd/iso7816.h
+++ b/scd/iso7816.h
@@ -24,23 +24,26 @@
#include "cardglue.h"
#endif
-/* Command codes used by iso7816_check_keypad. */
+/* Command codes used by iso7816_check_pinpad. */
#define ISO7816_VERIFY 0x20
#define ISO7816_CHANGE_REFERENCE_DATA 0x24
#define ISO7816_RESET_RETRY_COUNTER 0x2C
-/* Information to be passed to keypad equipped readers. See
+/* Information to be passed to pinpad equipped readers. See
ccid-driver.c for details. */
-struct iso7816_pininfo_s
+struct pininfo_s
{
- int mode; /* A mode of 0 means: Do not use the keypad. */
+ int fixedlen; /*
+ * -1: Variable length input is not supported,
+ * no information of fixed length yet.
+ * 0: Use variable length input.
+ * >0: Fixed length of PIN.
+ */
int minlen;
int maxlen;
- int padlen;
- int padchar;
};
-typedef struct iso7816_pininfo_s iso7816_pininfo_t;
+typedef struct pininfo_s pininfo_t;
gpg_error_t iso7816_map_sw (int sw);
@@ -59,26 +62,19 @@ gpg_error_t iso7816_apdu_direct (int slot,
const void *apdudata, size_t apdudatalen,
int handle_more,
unsigned char **result, size_t *resultlen);
-gpg_error_t iso7816_check_keypad (int slot, int command,
- iso7816_pininfo_t *pininfo);
+gpg_error_t iso7816_check_pinpad (int slot, int command,
+ pininfo_t *pininfo);
gpg_error_t iso7816_verify (int slot,
int chvno, const char *chv, size_t chvlen);
-gpg_error_t iso7816_verify_kp (int slot,
- int chvno, const char *chv, size_t chvlen,
- iso7816_pininfo_t *pininfo);
+gpg_error_t iso7816_verify_kp (int slot, int chvno, pininfo_t *pininfo);
gpg_error_t iso7816_change_reference_data (int slot, int chvno,
const char *oldchv, size_t oldchvlen,
const char *newchv, size_t newchvlen);
gpg_error_t iso7816_change_reference_data_kp (int slot, int chvno,
- const char *oldchv, size_t oldchvlen,
- const char *newchv, size_t newchvlen,
- iso7816_pininfo_t *pininfo);
+ int is_exchange,
+ pininfo_t *pininfo);
gpg_error_t iso7816_reset_retry_counter (int slot, int chvno,
const char *newchv, size_t newchvlen);
-gpg_error_t iso7816_reset_retry_counter_kp (int slot, int chvno,
- const char *newchv,
- size_t newchvlen,
- iso7816_pininfo_t *pininfo);
gpg_error_t iso7816_reset_retry_counter_with_rc (int slot, int chvno,
const char *data,
size_t datalen);
diff --git a/scd/pcsc-wrapper.c b/scd/pcsc-wrapper.c
index ee974ac..0d572d2 100644
--- a/scd/pcsc-wrapper.c
+++ b/scd/pcsc-wrapper.c
@@ -65,6 +65,12 @@
static int verbose;
+#if defined(__APPLE__) || defined(_WIN32) || defined(__CYGWIN__)
+typedef unsigned int pcsc_dword_t;
+#else
+typedef unsigned long pcsc_dword_t;
+#endif
+
/* PC/SC constants and function pointer. */
#define PCSC_SCOPE_USER 0
@@ -112,16 +118,24 @@ struct pcsc_io_request_s {
typedef struct pcsc_io_request_s *pcsc_io_request_t;
+#ifdef __APPLE__
+#pragma pack(1)
+#endif
+
struct pcsc_readerstate_s
{
const char *reader;
void *user_data;
- unsigned long current_state;
- unsigned long event_state;
- unsigned long atrlen;
+ pcsc_dword_t current_state;
+ pcsc_dword_t event_state;
+ pcsc_dword_t atrlen;
unsigned char atr[33];
};
+#ifdef __APPLE__
+#pragma pack()
+#endif
+
typedef struct pcsc_readerstate_s *pcsc_readerstate_t;
@@ -129,55 +143,62 @@ static int driver_is_open; /* True if the PC/SC driver has been
initialzied and is ready for
operations. The following variables
are then valid. */
-static unsigned long pcsc_context; /* The current PC/CS context. */
+static long pcsc_context; /* The current PC/CS context. */
static char *current_rdrname;
-static unsigned long pcsc_card;
-static unsigned long pcsc_protocol;
+static long pcsc_card;
+static pcsc_dword_t pcsc_protocol;
static unsigned char current_atr[33];
static size_t current_atrlen;
-long (* pcsc_establish_context) (unsigned long scope,
+long (* pcsc_establish_context) (pcsc_dword_t scope,
const void *reserved1,
const void *reserved2,
- unsigned long *r_context);
-long (* pcsc_release_context) (unsigned long context);
-long (* pcsc_list_readers) (unsigned long context,
+ long *r_context);
+long (* pcsc_release_context) (long context);
+long (* pcsc_list_readers) (long context,
const char *groups,
- char *readers, unsigned long*readerslen);
-long (* pcsc_get_status_change) (unsigned long context,
- unsigned long timeout,
+ char *readers, pcsc_dword_t *readerslen);
+long (* pcsc_get_status_change) (long context,
+ pcsc_dword_t timeout,
pcsc_readerstate_t readerstates,
- unsigned long nreaderstates);
-long (* pcsc_connect) (unsigned long context,
+ pcsc_dword_t nreaderstates);
+long (* pcsc_connect) (long context,
const char *reader,
- unsigned long share_mode,
- unsigned long preferred_protocols,
- unsigned long *r_card,
- unsigned long *r_active_protocol);
-long (* pcsc_reconnect) (unsigned long card,
- unsigned long share_mode,
- unsigned long preferred_protocols,
- unsigned long initialization,
- unsigned long *r_active_protocol);
-long (* pcsc_disconnect) (unsigned long card,
- unsigned long disposition);
-long (* pcsc_status) (unsigned long card,
- char *reader, unsigned long *readerlen,
- unsigned long *r_state,
- unsigned long *r_protocol,
- unsigned char *atr, unsigned long *atrlen);
-long (* pcsc_begin_transaction) (unsigned long card);
-long (* pcsc_end_transaction) (unsigned long card,
- unsigned long disposition);
-long (* pcsc_transmit) (unsigned long card,
+ pcsc_dword_t share_mode,
+ pcsc_dword_t preferred_protocols,
+ long *r_card,
+ pcsc_dword_t *r_active_protocol);
+long (* pcsc_reconnect) (long card,
+ pcsc_dword_t share_mode,
+ pcsc_dword_t preferred_protocols,
+ pcsc_dword_t initialization,
+ pcsc_dword_t *r_active_protocol);
+long (* pcsc_disconnect) (long card,
+ pcsc_dword_t disposition);
+long (* pcsc_status) (long card,
+ char *reader, pcsc_dword_t *readerlen,
+ pcsc_dword_t *r_state,
+ pcsc_dword_t *r_protocol,
+ unsigned char *atr, pcsc_dword_t *atrlen);
+long (* pcsc_begin_transaction) (long card);
+long (* pcsc_end_transaction) (long card,
+ pcsc_dword_t disposition);
+long (* pcsc_transmit) (long card,
const pcsc_io_request_t send_pci,
const unsigned char *send_buffer,
- unsigned long send_len,
+ pcsc_dword_t send_len,
pcsc_io_request_t recv_pci,
unsigned char *recv_buffer,
- unsigned long *recv_len);
-long (* pcsc_set_timeout) (unsigned long context,
- unsigned long timeout);
+ pcsc_dword_t *recv_len);
+long (* pcsc_set_timeout) (long context,
+ pcsc_dword_t timeout);
+long (* pcsc_control) (long card,
+ pcsc_dword_t control_code,
+ const void *send_buffer,
+ pcsc_dword_t send_len,
+ void *recv_buffer,
+ pcsc_dword_t recv_len,
+ pcsc_dword_t *bytes_returned);
@@ -335,6 +356,7 @@ load_pcsc_driver (const char *libname)
pcsc_end_transaction = dlsym (handle, "SCardEndTransaction");
pcsc_transmit = dlsym (handle, "SCardTransmit");
pcsc_set_timeout = dlsym (handle, "SCardSetTimeout");
+ pcsc_control = dlsym (handle, "SCardControl");
if (!pcsc_establish_context
|| !pcsc_release_context
@@ -347,13 +369,14 @@ load_pcsc_driver (const char *libname)
|| !pcsc_begin_transaction
|| !pcsc_end_transaction
|| !pcsc_transmit
+ || !pcsc_control
/* || !pcsc_set_timeout */)
{
/* Note that set_timeout is currently not used and also not
available under Windows. */
fprintf (stderr,
"apdu_open_reader: invalid PC/SC driver "
- "(%d%d%d%d%d%d%d%d%d%d%d%d)\n",
+ "(%d%d%d%d%d%d%d%d%d%d%d%d%d)\n",
!!pcsc_establish_context,
!!pcsc_release_context,
!!pcsc_list_readers,
@@ -365,7 +388,8 @@ load_pcsc_driver (const char *libname)
!!pcsc_begin_transaction,
!!pcsc_end_transaction,
!!pcsc_transmit,
- !!pcsc_set_timeout );
+ !!pcsc_set_timeout,
+ !!pcsc_control );
dlclose (handle);
exit (1);
}
@@ -384,9 +408,9 @@ handle_open (unsigned char *argbuf, size_t arglen)
long err;
const char * portstr;
char *list = NULL;
- unsigned long nreader, atrlen;
+ pcsc_dword_t nreader, atrlen;
char *p;
- unsigned long card_state, card_protocol;
+ pcsc_dword_t card_state, card_protocol;
unsigned char atr[33];
/* Make sure there is only the port string */
@@ -482,7 +506,7 @@ handle_open (unsigned char *argbuf, size_t arglen)
if (!err)
{
char reader[250];
- unsigned long readerlen;
+ pcsc_dword_t readerlen;
atrlen = 33;
readerlen = sizeof reader -1;
@@ -578,9 +602,11 @@ handle_status (unsigned char *argbuf, size_t arglen)
if ( !(rdrstates[0].event_state & PCSC_STATE_UNKNOWN) )
{
if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
- status |= 2;
- if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
- status |= 4;
+ {
+ status |= 2;
+ if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
+ status |= 4;
+ }
/* We indicate a useful card if it is not in use by another
application. This is because we only use exclusive access
mode. */
@@ -616,8 +642,8 @@ handle_reset (unsigned char *argbuf, size_t arglen)
{
long err;
char reader[250];
- unsigned long nreader, atrlen;
- unsigned long card_state, card_protocol;
+ pcsc_dword_t nreader, atrlen;
+ pcsc_dword_t card_state, card_protocol;
(void)argbuf;
(void)arglen;
@@ -687,8 +713,8 @@ handle_transmit (unsigned char *argbuf, size_t arglen)
{
long err;
struct pcsc_io_request_s send_pci;
- unsigned long recv_len;
- unsigned char buffer[1024];
+ pcsc_dword_t recv_len;
+ unsigned char buffer[4096];
/* The apdu should at least be one byte. */
if (!arglen)
@@ -720,6 +746,38 @@ handle_transmit (unsigned char *argbuf, size_t arglen)
}
+/* Handle a control request. The argument is expected to be a buffer
+ which contains CONTROL_CODE (4-byte) and INPUT_BYTES.
+ */
+static void
+handle_control (unsigned char *argbuf, size_t arglen)
+{
+ long err;
+ pcsc_dword_t ioctl_code;
+ pcsc_dword_t recv_len = 1024;
+ unsigned char buffer[1024];
+
+ if (arglen < 4)
+ bad_request ("CONTROL");
+
+ ioctl_code = (argbuf[0] << 24) | (argbuf[1] << 16) | (argbuf[2] << 8) | argbuf[3];
+ argbuf += 4;
+ arglen -= 4;
+
+ recv_len = sizeof (buffer);
+ err = pcsc_control (pcsc_card, ioctl_code, argbuf, arglen,
+ buffer, recv_len, &recv_len);
+ if (err)
+ {
+ if (verbose)
+ fprintf (stderr, PGM": pcsc_control failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ request_failed (err);
+ return;
+ }
+ request_succeeded (buffer, recv_len);
+}
+
static void
print_version (int with_help)
@@ -831,6 +889,10 @@ main (int argc, char **argv)
handle_reset (argbuffer, arglen);
break;
+ case 6:
+ handle_control (argbuffer, arglen);
+ break;
+
default:
fprintf (stderr, PGM ": invalid request 0x%02X\n", c);
exit (1);
diff --git a/scd/scdaemon-w32info.rc b/scd/scdaemon-w32info.rc
new file mode 100644
index 0000000..aa0eba4
--- /dev/null
+++ b/scd/scdaemon-w32info.rc
@@ -0,0 +1,50 @@
+/* scdaemon-w32info.rc -*- c -*-
+ * Copyright (C) 2013 g10 Code GmbH
+ *
+ * This file is free software; as a special exception the author gives
+ * unlimited permission to copy and/or distribute it, with or without
+ * modifications, as long as this notice is preserved.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+ * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "afxres.h"
+#include "../common/w32info-rc.h"
+
+1 ICON "../common/gnupg.ico"
+
+1 VERSIONINFO
+ FILEVERSION W32INFO_VI_FILEVERSION
+ PRODUCTVERSION W32INFO_VI_PRODUCTVERSION
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x01L /* VS_FF_DEBUG (0x1)*/
+#else
+ FILEFLAGS 0x00L
+#endif
+ FILEOS 0x40004L /* VOS_NT (0x40000) | VOS__WINDOWS32 (0x4) */
+ FILETYPE 0x1L /* VFT_APP (0x1) */
+ FILESUBTYPE 0x0L /* VFT2_UNKNOWN */
+ BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" /* US English (0409), Unicode (04b0) */
+ BEGIN
+ VALUE "FileDescription", L"GnuPG\x2019s smartcard daemon\0"
+ VALUE "InternalName", "scdaemon\0"
+ VALUE "OriginalFilename", "scdaemon.exe\0"
+ VALUE "ProductName", W32INFO_PRODUCTNAME
+ VALUE "ProductVersion", W32INFO_PRODUCTVERSION
+ VALUE "CompanyName", W32INFO_COMPANYNAME
+ VALUE "FileVersion", W32INFO_FILEVERSION
+ VALUE "LegalCopyright", W32INFO_LEGALCOPYRIGHT
+ VALUE "Comments", W32INFO_COMMENTS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 0x4b0
+ END
+ END
diff --git a/scd/scdaemon.c b/scd/scdaemon.c
index 064d342..e133ddc 100644
--- a/scd/scdaemon.c
+++ b/scd/scdaemon.c
@@ -48,6 +48,7 @@
#include "i18n.h"
#include "sysutils.h"
#include "app-common.h"
+#include "iso7816.h"
#include "apdu.h"
#include "ccid-driver.h"
#include "mkdtemp.h"
@@ -87,10 +88,11 @@ enum cmd_and_opt_values
opcscDriver,
oDisableCCID,
oDisableOpenSC,
- oDisableKeypad,
+ oDisablePinpad,
oAllowAdmin,
oDenyAdmin,
oDisableApplication,
+ oEnablePinpadVarlen,
oDebugDisableTicker
};
@@ -137,12 +139,17 @@ static ARGPARSE_OPTS opts[] = {
/* end --disable-ccid */),
ARGPARSE_s_u (oCardTimeout, "card-timeout",
N_("|N|disconnect the card after N seconds of inactivity")),
- ARGPARSE_s_n (oDisableKeypad, "disable-keypad",
- N_("do not use a reader's keypad")),
+
+ ARGPARSE_s_n (oDisablePinpad, "disable-pinpad",
+ N_("do not use a reader's pinpad")),
+ ARGPARSE_ignore (300, "disable-keypad"),
+
ARGPARSE_s_n (oAllowAdmin, "allow-admin", "@"),
ARGPARSE_s_n (oDenyAdmin, "deny-admin",
N_("deny the use of admin card commands")),
ARGPARSE_s_s (oDisableApplication, "disable-application", "@"),
+ ARGPARSE_s_n (oEnablePinpadVarlen, "enable-pinpad-varlen",
+ N_("use variable length input for pinpad")),
ARGPARSE_end ()
};
@@ -205,12 +212,15 @@ static void handle_connections (int listen_fd);
/* Pth wrapper function definitions. */
ASSUAN_SYSTEM_PTH_IMPL;
+#if GCRYPT_VERSION_NUMBER < 0x010600
GCRY_THREAD_OPTION_PTH_IMPL;
+#if GCRY_THREAD_OPTION_VERSION < 1
static int fixed_gcry_pth_init (void)
{
return pth_self ()? 0 : (pth_init () == FALSE) ? errno : 0;
}
-
+#endif
+#endif /*GCRYPT_VERSION_NUMBER < 0x010600*/
static char *
@@ -372,7 +382,6 @@ main (int argc, char **argv )
{
ARGPARSE_ARGS pargs;
int orig_argc;
- gpg_error_t err;
char **orig_argv;
FILE *configfp = NULL;
char *configname = NULL;
@@ -407,15 +416,23 @@ main (int argc, char **argv )
init_common_subsystems ();
- /* Libgcrypt requires us to register the threading model first.
+#if GCRYPT_VERSION_NUMBER < 0x010600
+ /* Libgcrypt < 1.6 requires us to register the threading model first.
Note that this will also do the pth_init. */
+ {
+ gpg_error_t err;
+#if GCRY_THREAD_OPTION_VERSION < 1
gcry_threads_pth.init = fixed_gcry_pth_init;
+#endif
+
err = gcry_control (GCRYCTL_SET_THREAD_CBS, &gcry_threads_pth);
if (err)
{
log_fatal ("can't register GNU Pth with Libgcrypt: %s\n",
gpg_strerror (err));
}
+ }
+#endif /*GCRYPT_VERSION_NUMBER < 0x010600*/
/* Check that the libraries are suitable. Do it here because
the option parsing may need services of the library */
@@ -579,7 +596,7 @@ main (int argc, char **argv )
case oDisableCCID: opt.disable_ccid = 1; break;
case oDisableOpenSC: break;
- case oDisableKeypad: opt.disable_keypad = 1; break;
+ case oDisablePinpad: opt.disable_pinpad = 1; break;
case oAllowAdmin: /* Dummy because allow is now the default. */
break;
@@ -591,6 +608,8 @@ main (int argc, char **argv )
add_to_strlist (&opt.disabled_applications, pargs.r.ret_str);
break;
+ case oEnablePinpadVarlen: opt.enable_pinpad_varlen = 1; break;
+
default:
pargs.err = configfp? ARGPARSE_PRINT_WARNING:ARGPARSE_PRINT_ERROR;
break;
@@ -670,8 +689,9 @@ main (int argc, char **argv )
printf ("disable-ccid:%lu:\n", GC_OPT_FLAG_NONE );
#endif
printf ("deny-admin:%lu:\n", GC_OPT_FLAG_NONE );
- printf ("disable-keypad:%lu:\n", GC_OPT_FLAG_NONE );
+ printf ("disable-pinpad:%lu:\n", GC_OPT_FLAG_NONE );
printf ("card-timeout:%lu:%d:\n", GC_OPT_FLAG_DEFAULT, 0);
+ printf ("enable-pinpad-varlen:%lu:\n", GC_OPT_FLAG_NONE );
scd_exit (0);
}
diff --git a/scd/scdaemon.h b/scd/scdaemon.h
index c429396..5ead4aa 100644
--- a/scd/scdaemon.h
+++ b/scd/scdaemon.h
@@ -56,7 +56,8 @@ struct
const char *pcsc_driver; /* Library to access the PC/SC system. */
const char *reader_port; /* NULL or reder port to use. */
int disable_ccid; /* Disable the use of the internal CCID driver. */
- int disable_keypad; /* Do not use a keypad. */
+ int disable_pinpad; /* Do not use a pinpad. */
+ int enable_pinpad_varlen; /* Use variable length input for pinpad. */
int allow_admin; /* Allow the use of admin commands for certain
cards. */
strlist_t disabled_applications; /* Card applications we do not