scd: Use pipe to kick the loop on NetBSD.
[gnupg.git] / scd / iso7816.c
index 4861466..081b080 100644 (file)
@@ -1,11 +1,11 @@
 /* iso7816.c - ISO 7816 commands
 /* iso7816.c - ISO 7816 commands
- *     Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004, 2008, 2009 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
  *
  * This file is part of GnuPG.
  *
  * GnuPG is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
+ * 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,
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * 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, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- *
- * $Id$
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
  */
 
 #include <config.h>
@@ -35,8 +32,8 @@
 #include "options.h"
 #include "errors.h"
 #include "memory.h"
 #include "options.h"
 #include "errors.h"
 #include "memory.h"
-#include "util.h"
-#include "i18n.h"
+#include "../common/util.h"
+#include "../common/i18n.h"
 #else /* GNUPG_MAJOR_VERSION != 1 */
 #include "scdaemon.h"
 #endif /* GNUPG_MAJOR_VERSION != 1 */
 #else /* GNUPG_MAJOR_VERSION != 1 */
 #include "scdaemon.h"
 #endif /* GNUPG_MAJOR_VERSION != 1 */
@@ -46,9 +43,9 @@
 
 
 #define CMD_SELECT_FILE 0xA4
 
 
 #define CMD_SELECT_FILE 0xA4
-#define CMD_VERIFY      0x20
-#define CMD_CHANGE_REFERENCE_DATA 0x24
-#define CMD_RESET_RETRY_COUNTER   0x2C
+#define CMD_VERIFY                ISO7816_VERIFY
+#define CMD_CHANGE_REFERENCE_DATA ISO7816_CHANGE_REFERENCE_DATA
+#define CMD_RESET_RETRY_COUNTER   ISO7816_RESET_RETRY_COUNTER
 #define CMD_GET_DATA    0xCA
 #define CMD_PUT_DATA    0xDA
 #define CMD_MSE         0x22
 #define CMD_GET_DATA    0xCA
 #define CMD_PUT_DATA    0xDA
 #define CMD_MSE         0x22
@@ -67,7 +64,10 @@ map_sw (int sw)
   switch (sw)
     {
     case SW_EEPROM_FAILURE: ec = GPG_ERR_HARDWARE; break;
   switch (sw)
     {
     case SW_EEPROM_FAILURE: ec = GPG_ERR_HARDWARE; break;
+    case SW_TERM_STATE:     ec = GPG_ERR_OBJ_TERM_STATE; break;
     case SW_WRONG_LENGTH:   ec = GPG_ERR_INV_VALUE; break;
     case SW_WRONG_LENGTH:   ec = GPG_ERR_INV_VALUE; break;
+    case SW_SM_NOT_SUP:     ec = GPG_ERR_NOT_SUPPORTED; break;
+    case SW_CC_NOT_SUP:     ec = GPG_ERR_NOT_SUPPORTED; break;
     case SW_CHV_WRONG:      ec = GPG_ERR_BAD_PIN; break;
     case SW_CHV_BLOCKED:    ec = GPG_ERR_PIN_BLOCKED; break;
     case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break;
     case SW_CHV_WRONG:      ec = GPG_ERR_BAD_PIN; break;
     case SW_CHV_BLOCKED:    ec = GPG_ERR_PIN_BLOCKED; break;
     case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break;
@@ -77,6 +77,7 @@ map_sw (int sw)
     case SW_RECORD_NOT_FOUND:ec= GPG_ERR_NOT_FOUND; break;
     case SW_REF_NOT_FOUND:  ec = GPG_ERR_NO_OBJ; break;
     case SW_BAD_P0_P1:      ec = GPG_ERR_INV_VALUE; break;
     case SW_RECORD_NOT_FOUND:ec= GPG_ERR_NOT_FOUND; break;
     case SW_REF_NOT_FOUND:  ec = GPG_ERR_NO_OBJ; break;
     case SW_BAD_P0_P1:      ec = GPG_ERR_INV_VALUE; break;
+    case SW_EXACT_LENGTH:   ec = GPG_ERR_INV_VALUE; break;
     case SW_INS_NOT_SUP:    ec = GPG_ERR_CARD; break;
     case SW_CLA_NOT_SUP:    ec = GPG_ERR_CARD; break;
     case SW_SUCCESS:        ec = 0; break;
     case SW_INS_NOT_SUP:    ec = GPG_ERR_CARD; break;
     case SW_CLA_NOT_SUP:    ec = GPG_ERR_CARD; break;
     case SW_SUCCESS:        ec = 0; break;
@@ -93,6 +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_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_PINPAD:      ec = GPG_ERR_NOT_SUPPORTED; break;
 
     default:
       if ((sw & 0x010000))
 
     default:
       if ((sw & 0x010000))
@@ -122,26 +124,21 @@ iso7816_map_sw (int sw)
    requested application ID.  The function can't be used to enumerate
    AIDs and won't return the AID on success.  The return value is 0
    for okay or a GPG error code.  Note that ISO error codes are
    requested application ID.  The function can't be used to enumerate
    AIDs and won't return the AID on success.  The return value is 0
    for okay or a GPG error code.  Note that ISO error codes are
-   internally mapped. */
+   internally mapped.  Bit 0 of FLAGS should be set if the card does
+   not understand P2=0xC0. */
 gpg_error_t
 gpg_error_t
-iso7816_select_application (int slot, const char *aid, size_t aidlen)
+iso7816_select_application (int slot, const char *aid, size_t aidlen,
+                            unsigned int flags)
 {
 {
-  static char const openpgp_aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
   int sw;
   int sw;
-  int p1 = 0x0C; /* No FCI to be returned. */
-  
-  if (aidlen == sizeof openpgp_aid
-      && !memcmp (aid, openpgp_aid, sizeof openpgp_aid))
-    p1 = 0; /* The current openpgp cards don't allow 0x0c. */
-
-  sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, p1, aidlen, aid);
+  sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE, 4,
+                         (flags&1)? 0 :0x0c, aidlen, aid);
   return map_sw (sw);
 }
 
 
 gpg_error_t
   return map_sw (sw);
 }
 
 
 gpg_error_t
-iso7816_select_file (int slot, int tag, int is_dir,
-                     unsigned char **result, size_t *resultlen)
+iso7816_select_file (int slot, int tag, int is_dir)
 {
   int sw, p0, p1;
   unsigned char tagbuf[2];
 {
   int sw, p0, p1;
   unsigned char tagbuf[2];
@@ -149,22 +146,36 @@ iso7816_select_file (int slot, int tag, int is_dir,
   tagbuf[0] = (tag >> 8) & 0xff;
   tagbuf[1] = tag & 0xff;
 
   tagbuf[0] = (tag >> 8) & 0xff;
   tagbuf[1] = tag & 0xff;
 
-  if (result || resultlen)
-    {
-      *result = NULL;
-      *resultlen = 0;
-      return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
-    }
-  else
+  p0 = (tag == 0x3F00)? 0: is_dir? 1:2;
+  p1 = 0x0c; /* No FC return. */
+  sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE,
+                         p0, p1, 2, (char*)tagbuf );
+  return map_sw (sw);
+}
+
+
+/* Do a select file command with a direct path. */
+gpg_error_t
+iso7816_select_path (int slot, const unsigned short *path, size_t pathlen)
+{
+  int sw, p0, p1;
+  unsigned char buffer[100];
+  int buflen;
+
+  if (pathlen/2 >= sizeof buffer)
+    return gpg_error (GPG_ERR_TOO_LARGE);
+
+  for (buflen = 0; pathlen; pathlen--, path++)
     {
     {
-      p0 = (tag == 0x3F00)? 0: is_dir? 1:2;
-      p1 = 0x0c; /* No FC return. */
-      sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE,
-                             p0, p1, 2, tagbuf );
-      return map_sw (sw);
+      buffer[buflen++] = (*path >> 8);
+      buffer[buflen++] = *path;
     }
 
     }
 
-  return 0;
+  p0 = 0x08;
+  p1 = 0x0c; /* No FC return. */
+  sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE,
+                         p0, p1, buflen, (char*)buffer );
+  return map_sw (sw);
 }
 
 
 }
 
 
@@ -180,7 +191,7 @@ iso7816_list_directory (int slot, int list_dirs,
   *result = NULL;
   *resultlen = 0;
 
   *result = NULL;
   *resultlen = 0;
 
-  sw = apdu_send (slot, 0x80, 0xAA, list_dirs? 1:2, 0, -1, NULL,
+  sw = apdu_send (slot, 0, 0x80, 0xAA, list_dirs? 1:2, 0, -1, NULL,
                   result, resultlen);
   if (sw != SW_SUCCESS)
     {
                   result, resultlen);
   if (sw != SW_SUCCESS)
     {
@@ -193,15 +204,95 @@ iso7816_list_directory (int slot, int list_dirs,
 }
 
 
 }
 
 
+/* This function sends an already formatted APDU to the card.  With
+   HANDLE_MORE set to true a MORE DATA status will be handled
+   internally.  The return value is a gpg error code (i.e. a mapped
+   status word).  This is basically the same as apdu_send_direct but
+   it maps the status word and does not return it in the result
+   buffer.  */
+gpg_error_t
+iso7816_apdu_direct (int slot, const void *apdudata, size_t apdudatalen,
+                     int handle_more,
+                     unsigned char **result, size_t *resultlen)
+{
+  int sw;
+
+  if (!result || !resultlen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  *result = NULL;
+  *resultlen = 0;
+
+  sw = apdu_send_direct (slot, 0, apdudata, apdudatalen, handle_more,
+                         result, resultlen);
+  if (!sw)
+    {
+      if (*resultlen < 2)
+        sw = SW_HOST_GENERAL_ERROR;
+      else
+        {
+          sw = ((*result)[*resultlen-2] << 8) | (*result)[*resultlen-1];
+          (*resultlen)--;
+          (*resultlen)--;
+        }
+    }
+  if (sw != SW_SUCCESS)
+    {
+      /* Make sure that pending buffers are released. */
+      xfree (*result);
+      *result = NULL;
+      *resultlen = 0;
+    }
+  return map_sw (sw);
+}
+
+
+/* Check whether the reader supports the ISO command code COMMAND on
+   the pinpad.  Returns 0 on success.  */
+gpg_error_t
+iso7816_check_pinpad (int slot, int command, pininfo_t *pininfo)
+{
+  int sw;
+
+  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 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, pininfo_t *pininfo)
+{
+  int sw;
+
+  sw = apdu_pinpad_verify (slot, 0x00, CMD_VERIFY, 0, chvno, pininfo);
+  return map_sw (sw);
+}
 
 /* Perform a VERIFY command on SLOT using the card holder verification
 
 /* Perform a VERIFY command on SLOT using the card holder verification
-   vector CHVNO with a CHV of lenght CHVLEN.  Returns 0 on success. */
+   vector CHVNO with a CHV of length CHVLEN.  Returns 0 on success. */
 gpg_error_t
 iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen)
 {
   int sw;
 
 gpg_error_t
 iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen)
 {
   int sw;
 
-  sw = apdu_send_simple (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv);
+  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);
 }
 
   return map_sw (sw);
 }
 
@@ -230,45 +321,67 @@ iso7816_change_reference_data (int slot, int chvno,
     memcpy (buf, oldchv, oldchvlen);
   memcpy (buf+oldchvlen, newchv, newchvlen);
 
     memcpy (buf, oldchv, oldchvlen);
   memcpy (buf+oldchvlen, newchv, newchvlen);
 
-  sw = apdu_send_simple (slot, 0x00, CMD_CHANGE_REFERENCE_DATA,
+  sw = apdu_send_simple (slot, 0, 0x00, CMD_CHANGE_REFERENCE_DATA,
                          oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf);
   xfree (buf);
   return map_sw (sw);
 
 }
 
                          oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf);
   xfree (buf);
   return map_sw (sw);
 
 }
 
+
+gpg_error_t
+iso7816_reset_retry_counter_with_rc (int slot, int chvno,
+                                     const char *data, size_t datalen)
+{
+  int sw;
+
+  if (!data || !datalen )
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  sw = apdu_send_simple (slot, 0, 0x00, CMD_RESET_RETRY_COUNTER,
+                         0, chvno, datalen, data);
+  return map_sw (sw);
+}
+
+
 gpg_error_t
 iso7816_reset_retry_counter (int slot, int chvno,
                              const char *newchv, size_t newchvlen)
 {
   int sw;
 
 gpg_error_t
 iso7816_reset_retry_counter (int slot, int chvno,
                              const char *newchv, size_t newchvlen)
 {
   int sw;
 
-  if (!newchv || !newchvlen )
-    return gpg_error (GPG_ERR_INV_VALUE);
-
-  sw = apdu_send_simple (slot, 0x00, CMD_RESET_RETRY_COUNTER,
+  sw = apdu_send_simple (slot, 0, 0x00, CMD_RESET_RETRY_COUNTER,
                          2, chvno, newchvlen, newchv);
   return map_sw (sw);
 }
 
 
                          2, chvno, newchvlen, newchv);
   return map_sw (sw);
 }
 
 
+
 /* Perform a GET DATA command requesting TAG and storing the result in
    a newly allocated buffer at the address passed by RESULT.  Return
    the length of this data at the address of RESULTLEN. */
 gpg_error_t
 /* Perform a GET DATA command requesting TAG and storing the result in
    a newly allocated buffer at the address passed by RESULT.  Return
    the length of this data at the address of RESULTLEN. */
 gpg_error_t
-iso7816_get_data (int slot, int tag,
+iso7816_get_data (int slot, int extended_mode, int tag,
                   unsigned char **result, size_t *resultlen)
 {
   int sw;
                   unsigned char **result, size_t *resultlen)
 {
   int sw;
+  int le;
 
   if (!result || !resultlen)
     return gpg_error (GPG_ERR_INV_VALUE);
   *result = NULL;
   *resultlen = 0;
 
 
   if (!result || !resultlen)
     return gpg_error (GPG_ERR_INV_VALUE);
   *result = NULL;
   *resultlen = 0;
 
-  sw = apdu_send (slot, 0x00, CMD_GET_DATA,
-                  ((tag >> 8) & 0xff), (tag & 0xff), -1, NULL,
-                  result, resultlen);
+  if (extended_mode > 0 && extended_mode < 256)
+    le = 65534; /* Not 65535 in case it is used as some special flag.  */
+  else if (extended_mode > 0)
+    le = extended_mode;
+  else
+    le = 256;
+
+  sw = apdu_send_le (slot, extended_mode, 0x00, CMD_GET_DATA,
+                     ((tag >> 8) & 0xff), (tag & 0xff), -1, NULL, le,
+                     result, resultlen);
   if (sw != SW_SUCCESS)
     {
       /* Make sure that pending buffers are released. */
   if (sw != SW_SUCCESS)
     {
       /* Make sure that pending buffers are released. */
@@ -283,16 +396,31 @@ iso7816_get_data (int slot, int tag,
 
 
 /* Perform a PUT DATA command on card in SLOT.  Write DATA of length
 
 
 /* Perform a PUT DATA command on card in SLOT.  Write DATA of length
-   DATALEN to TAG. */
+   DATALEN to TAG.  EXTENDED_MODE controls whether extended length
+   headers or command chaining is used instead of single length
+   bytes. */
+gpg_error_t
+iso7816_put_data (int slot, int extended_mode, int tag,
+                  const void *data, size_t datalen)
+{
+  int sw;
+
+  sw = apdu_send_simple (slot, extended_mode, 0x00, CMD_PUT_DATA,
+                         ((tag >> 8) & 0xff), (tag & 0xff),
+                         datalen, (const char*)data);
+  return map_sw (sw);
+}
+
+/* Same as iso7816_put_data but uses an odd instruction byte.  */
 gpg_error_t
 gpg_error_t
-iso7816_put_data (int slot, int tag,
-                  const unsigned char *data, size_t datalen)
+iso7816_put_data_odd (int slot, int extended_mode, int tag,
+                      const void *data, size_t datalen)
 {
   int sw;
 
 {
   int sw;
 
-  sw = apdu_send_simple (slot, 0x00, CMD_PUT_DATA,
+  sw = apdu_send_simple (slot, extended_mode, 0x00, CMD_PUT_DATA+1,
                          ((tag >> 8) & 0xff), (tag & 0xff),
                          ((tag >> 8) & 0xff), (tag & 0xff),
-                         datalen, data);
+                         datalen, (const char*)data);
   return map_sw (sw);
 }
 
   return map_sw (sw);
 }
 
@@ -306,10 +434,11 @@ iso7816_manage_security_env (int slot, int p1, int p2,
 {
   int sw;
 
 {
   int sw;
 
-  if (p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 || !data || !datalen)
+  if (p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 )
     return gpg_error (GPG_ERR_INV_VALUE);
 
     return gpg_error (GPG_ERR_INV_VALUE);
 
-  sw = apdu_send_simple (slot, 0x00, CMD_MSE, p1, p2, datalen, data);
+  sw = apdu_send_simple (slot, 0, 0x00, CMD_MSE, p1, p2,
+                         data? datalen : -1, (const char*)data);
   return map_sw (sw);
 }
 
   return map_sw (sw);
 }
 
@@ -317,9 +446,10 @@ iso7816_manage_security_env (int slot, int p1, int p2,
 /* Perform the security operation COMPUTE DIGITAL SIGANTURE.  On
    success 0 is returned and the data is availavle in a newly
    allocated buffer stored at RESULT with its length stored at
 /* Perform the security operation COMPUTE DIGITAL SIGANTURE.  On
    success 0 is returned and the data is availavle in a newly
    allocated buffer stored at RESULT with its length stored at
-   RESULTLEN. */
+   RESULTLEN.  For LE see do_generate_keypair. */
 gpg_error_t
 gpg_error_t
-iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
+iso7816_compute_ds (int slot, int extended_mode,
+                    const unsigned char *data, size_t datalen, int le,
                     unsigned char **result, size_t *resultlen)
 {
   int sw;
                     unsigned char **result, size_t *resultlen)
 {
   int sw;
@@ -329,8 +459,16 @@ iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
   *result = NULL;
   *resultlen = 0;
 
   *result = NULL;
   *resultlen = 0;
 
-  sw = apdu_send (slot, 0x00, CMD_PSO, 0x9E, 0x9A, datalen, data,
-                  result, resultlen);
+  if (!extended_mode)
+    le = 256;  /* Ignore provided Le and use what apdu_send uses. */
+  else if (le >= 0 && le < 256)
+    le = 256;
+
+  sw = apdu_send_le (slot, extended_mode,
+                     0x00, CMD_PSO, 0x9E, 0x9A,
+                     datalen, (const char*)data,
+                     le,
+                     result, resultlen);
   if (sw != SW_SUCCESS)
     {
       /* Make sure that pending buffers are released. */
   if (sw != SW_SUCCESS)
     {
       /* Make sure that pending buffers are released. */
@@ -348,9 +486,11 @@ iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
    indicator to be used.  It should be 0 if no padding is required, a
    value of -1 suppresses the padding byte.  On success 0 is returned
    and the plaintext is available in a newly allocated buffer stored
    indicator to be used.  It should be 0 if no padding is required, a
    value of -1 suppresses the padding byte.  On success 0 is returned
    and the plaintext is available in a newly allocated buffer stored
-   at RESULT with its length stored at RESULTLEN. */
+   at RESULT with its length stored at RESULTLEN.  For LE see
+   do_generate_keypair. */
 gpg_error_t
 gpg_error_t
-iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
+iso7816_decipher (int slot, int extended_mode,
+                  const unsigned char *data, size_t datalen, int le,
                   int padind, unsigned char **result, size_t *resultlen)
 {
   int sw;
                   int padind, unsigned char **result, size_t *resultlen)
 {
   int sw;
@@ -361,6 +501,11 @@ iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
   *result = NULL;
   *resultlen = 0;
 
   *result = NULL;
   *resultlen = 0;
 
+  if (!extended_mode)
+    le = 256;  /* Ignore provided Le and use what apdu_send uses. */
+  else if (le >= 0 && le < 256)
+    le = 256;
+
   if (padind >= 0)
     {
       /* We need to prepend the padding indicator. */
   if (padind >= 0)
     {
       /* We need to prepend the padding indicator. */
@@ -370,14 +515,18 @@ iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
 
       *buf = padind; /* Padding indicator. */
       memcpy (buf+1, data, datalen);
 
       *buf = padind; /* Padding indicator. */
       memcpy (buf+1, data, datalen);
-      sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen+1, buf,
-                      result, resultlen);
+      sw = apdu_send_le (slot, extended_mode,
+                         0x00, CMD_PSO, 0x80, 0x86,
+                         datalen+1, (char*)buf, le,
+                         result, resultlen);
       xfree (buf);
     }
   else
     {
       xfree (buf);
     }
   else
     {
-      sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen, data,
-                      result, resultlen);
+      sw = apdu_send_le (slot, extended_mode,
+                         0x00, CMD_PSO, 0x80, 0x86,
+                         datalen, (const char *)data, le,
+                         result, resultlen);
     }
   if (sw != SW_SUCCESS)
     {
     }
   if (sw != SW_SUCCESS)
     {
@@ -392,9 +541,11 @@ iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
 }
 
 
 }
 
 
+/*  For LE see do_generate_keypair.  */
 gpg_error_t
 gpg_error_t
-iso7816_internal_authenticate (int slot,
+iso7816_internal_authenticate (int slot, int extended_mode,
                                const unsigned char *data, size_t datalen,
                                const unsigned char *data, size_t datalen,
+                               int le,
                                unsigned char **result, size_t *resultlen)
 {
   int sw;
                                unsigned char **result, size_t *resultlen)
 {
   int sw;
@@ -404,8 +555,16 @@ iso7816_internal_authenticate (int slot,
   *result = NULL;
   *resultlen = 0;
 
   *result = NULL;
   *resultlen = 0;
 
-  sw = apdu_send (slot, 0x00, CMD_INTERNAL_AUTHENTICATE, 0, 0,
-                  datalen, data,  result, resultlen);
+  if (!extended_mode)
+    le = 256;  /* Ignore provided Le and use what apdu_send uses. */
+  else if (le >= 0 && le < 256)
+    le = 256;
+
+  sw = apdu_send_le (slot, extended_mode,
+                     0x00, CMD_INTERNAL_AUTHENTICATE, 0, 0,
+                     datalen, (const char*)data,
+                     le,
+                     result, resultlen);
   if (sw != SW_SUCCESS)
     {
       /* Make sure that pending buffers are released. */
   if (sw != SW_SUCCESS)
     {
       /* Make sure that pending buffers are released. */
@@ -419,10 +578,14 @@ iso7816_internal_authenticate (int slot,
 }
 
 
 }
 
 
+/* LE is the expected return length.  This is usually 0 except if
+   extended length mode is used and more than 256 byte will be
+   returned.  In that case a value of -1 uses a large default
+   (e.g. 4096 bytes), a value larger 256 used that value.  */
 static gpg_error_t
 static gpg_error_t
-do_generate_keypair (int slot, int readonly,
-                  const unsigned char *data, size_t datalen,
-                  unsigned char **result, size_t *resultlen)
+do_generate_keypair (int slot, int extended_mode, int read_only,
+                     const char *data, size_t datalen, int le,
+                     unsigned char **result, size_t *resultlen)
 {
   int sw;
 
 {
   int sw;
 
@@ -431,8 +594,11 @@ do_generate_keypair (int slot, int readonly,
   *result = NULL;
   *resultlen = 0;
 
   *result = NULL;
   *resultlen = 0;
 
-  sw = apdu_send (slot, 0x00, CMD_GENERATE_KEYPAIR, readonly? 0x81:0x80, 0,
-                  datalen, data,  result, resultlen);
+  sw = apdu_send_le (slot, extended_mode,
+                     0x00, CMD_GENERATE_KEYPAIR, read_only? 0x81:0x80, 0,
+                     datalen, data,
+                     le >= 0 && le < 256? 256:le,
+                     result, resultlen);
   if (sw != SW_SUCCESS)
     {
       /* Make sure that pending buffers are released. */
   if (sw != SW_SUCCESS)
     {
       /* Make sure that pending buffers are released. */
@@ -447,20 +613,24 @@ do_generate_keypair (int slot, int readonly,
 
 
 gpg_error_t
 
 
 gpg_error_t
-iso7816_generate_keypair (int slot,
-                          const unsigned char *data, size_t datalen,
+iso7816_generate_keypair (int slot, int extended_mode,
+                          const char *data, size_t datalen,
+                          int le,
                           unsigned char **result, size_t *resultlen)
 {
                           unsigned char **result, size_t *resultlen)
 {
-  return do_generate_keypair (slot, 0, data, datalen, result, resultlen);
+  return do_generate_keypair (slot, extended_mode, 0,
+                              data, datalen, le, result, resultlen);
 }
 
 
 gpg_error_t
 }
 
 
 gpg_error_t
-iso7816_read_public_key (int slot,
-                          const unsigned char *data, size_t datalen,
-                          unsigned char **result, size_t *resultlen)
+iso7816_read_public_key (int slot, int extended_mode,
+                         const char *data, size_t datalen,
+                         int le,
+                         unsigned char **result, size_t *resultlen)
 {
 {
-  return do_generate_keypair (slot, 1, data, datalen, result, resultlen);
+  return do_generate_keypair (slot, extended_mode, 1,
+                              data, datalen, le, result, resultlen);
 }
 
 
 }
 
 
@@ -479,8 +649,8 @@ iso7816_get_challenge (int slot, int length, unsigned char *buffer)
     {
       result = NULL;
       n = length > 254? 254 : length;
     {
       result = NULL;
       n = length > 254? 254 : length;
-      sw = apdu_send_le (slot, 0x00, CMD_GET_CHALLENGE, 0, 0, -1, NULL,
-                         n,
+      sw = apdu_send_le (slot, 0,
+                         0x00, CMD_GET_CHALLENGE, 0, 0, -1, NULL, n,
                          &result, &resultlen);
       if (sw != SW_SUCCESS)
         {
                          &result, &resultlen);
       if (sw != SW_SUCCESS)
         {
@@ -528,15 +698,25 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax,
     {
       buffer = NULL;
       bufferlen = 0;
     {
       buffer = NULL;
       bufferlen = 0;
-      /* Fixme: Either the ccid driver or the TCOS cards have problems
-         with an Le of 0. */
-      if (read_all || nmax > 254)
-        n = 254;
-      else
-        n = nmax;
-      sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY,
+      n = read_all? 0 : nmax;
+      sw = apdu_send_le (slot, 0, 0x00, CMD_READ_BINARY,
                          ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
                          n, &buffer, &bufferlen);
                          ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
                          n, &buffer, &bufferlen);
+      if ( SW_EXACT_LENGTH_P(sw) )
+        {
+          n = (sw & 0x00ff);
+          sw = apdu_send_le (slot, 0, 0x00, CMD_READ_BINARY,
+                             ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
+                             n, &buffer, &bufferlen);
+        }
+
+      if (*result && sw == SW_BAD_P0_P1)
+        {
+          /* Bad Parameter means that the offset is outside of the
+             EF. When reading all data we take this as an indication
+             for EOF.  */
+          break;
+        }
 
       if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
         {
 
       if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
         {
@@ -552,7 +732,7 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax,
           unsigned char *p = xtryrealloc (*result, *resultlen + bufferlen);
           if (!p)
             {
           unsigned char *p = xtryrealloc (*result, *resultlen + bufferlen);
           if (!p)
             {
-              gpg_error_t err = gpg_error_from_errno (errno);
+              gpg_error_t err = gpg_error_from_syserror ();
               xfree (buffer);
               xfree (*result);
               *result = NULL;
               xfree (buffer);
               xfree (*result);
               *result = NULL;
@@ -580,7 +760,7 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax,
         nmax = 0;
     }
   while ((read_all && sw != SW_EOF_REACHED) || (!read_all && nmax));
         nmax = 0;
     }
   while ((read_all && sw != SW_EOF_REACHED) || (!read_all && nmax));
-  
+
   return 0;
 }
 
   return 0;
 }
 
@@ -612,13 +792,11 @@ iso7816_read_record (int slot, int recno, int reccount, int short_ef,
 
   buffer = NULL;
   bufferlen = 0;
 
   buffer = NULL;
   bufferlen = 0;
-  /* Fixme: Either the ccid driver of the TCOS cards have problems
-     with an Le of 0. */
-  sw = apdu_send_le (slot, 0x00, CMD_READ_RECORD,
-                     recno, 
+  sw = apdu_send_le (slot, 0, 0x00, CMD_READ_RECORD,
+                     recno,
                      short_ef? short_ef : 0x04,
                      -1, NULL,
                      short_ef? short_ef : 0x04,
                      -1, NULL,
-                     254, &buffer, &bufferlen);
+                     0, &buffer, &bufferlen);
 
   if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
     {
 
   if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
     {
@@ -631,7 +809,6 @@ iso7816_read_record (int slot, int recno, int reccount, int short_ef,
     }
   *result = buffer;
   *resultlen = bufferlen;
     }
   *result = buffer;
   *resultlen = bufferlen;
-  
+
   return 0;
 }
   return 0;
 }
-