More support for Netkey cards.
authorWerner Koch <wk@gnupg.org>
Fri, 8 May 2009 15:07:45 +0000 (15:07 +0000)
committerWerner Koch <wk@gnupg.org>
Fri, 8 May 2009 15:07:45 +0000 (15:07 +0000)
Small changes to teh CCID driver.
Support 2048 bit OpenPGP cards.

scd/ChangeLog
scd/apdu.c
scd/app-common.h
scd/app-help.c
scd/app-nks.c
scd/app-openpgp.c
scd/ccid-driver.c
scd/iso7816.c
scd/iso7816.h

index d54355d..1ac9585 100644 (file)
@@ -1,3 +1,26 @@
+2009-05-08  Werner Koch  <wk@g10code.com>
+
+       * app-openpgp.c (do_genkey): Allow larger key sizes.
+       (do_decipher): Ditto.
+       * iso7816.c (do_generate_keypair): Add arg EXTENDED_MODE an LE.
+       (iso7816_generate_keypair, iso7816_read_public_key): Ditto.
+       Changed all callers.
+       * apdu.c (send_le): Implement extended length return values.
+       
+       * ccid-driver.c (bulk_in): Retry on EAGAIN.
+       (abort_cmd): Change seqno handling.
+
+2009-04-28  Werner Koch  <wk@g10code.com>
+
+       * app-help.c (app_help_count_bits): New.
+
+       * app-nks.c (switch_application): Detect mass signature cards.
+       Take care of new NEED_APP_SELECT flag.
+       (do_sign): Don't allow mass signature cards.
+       (all_zero_p): New.
+       (do_readkey): New.
+       (app_select_nks): Register do_readkey.
+
 2009-04-01  Werner Koch  <wk@g10code.com>
 
        * app-openpgp.c (do_setattr, do_writekey): Prepare for extended
index d63157c..219dda0 100644 (file)
@@ -2817,8 +2817,6 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen,
                 length limit.
        n > 1 := Use extended length with up to N bytes.
 
-       FIXME: We don't support extended length return values larger
-       than 256 bytes due to a static buffer. 
 */
 static int
 send_le (int slot, int class, int ins, int p0, int p1,
@@ -2826,9 +2824,12 @@ send_le (int slot, int class, int ins, int p0, int p1,
          unsigned char **retbuf, size_t *retbuflen,
          struct pininfo_s *pininfo, int extended_mode)
 {
-#define RESULTLEN 258
-  unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
-                                         the driver. */
+#define SHORT_RESULT_BUFFER_SIZE 258
+  /* We allocate 8 extra bytes as a safety margin towards a driver bug.  */
+  unsigned char short_result_buffer[SHORT_RESULT_BUFFER_SIZE+10]; 
+  unsigned char *result_buffer = NULL;
+  size_t result_buffer_size;
+  unsigned char *result;
   size_t resultlen;
   unsigned char short_apdu_buffer[5+256+1];
   unsigned char *apdu_buffer = NULL;
@@ -2873,8 +2874,22 @@ send_le (int slot, int class, int ins, int p0, int p1,
   else if (lc == -1 && extended_mode > 0)
     use_extended_length = 1;
     
-  if (le != -1 && (le > 256 || le < 0))
-    return SW_WRONG_LENGTH;
+  if (le != -1 && (le > (extended_mode > 0? 255:256) || le < 0))
+    {
+      /* Expected Data does not fit into an APDU.  What we do now
+         depends on the EXTENDED_MODE parameter.  Note that a check
+         for command chaining does not make sense because we are
+         looking at Le.  */
+      if (!extended_mode)
+        return SW_WRONG_LENGTH; /* No way to send such an APDU.  */
+      else if (use_extended_length)
+        ; /* We are already using extended length.  */
+      else if (extended_mode > 0)
+        use_extended_length = 1;
+      else 
+        return SW_HOST_INV_VALUE;
+    }
+
   if ((!data && lc != -1) || (data && lc == -1))
     return SW_HOST_INV_VALUE;
 
@@ -2885,7 +2900,7 @@ send_le (int slot, int class, int ins, int p0, int p1,
 
       /* Space for: cls/ins/p1/p2+Z+2_byte_Lc+Lc+2_byte_Le.  */
       apdu_buffer_size = 4 + 1 + (lc >= 0? (2+lc):0) + 2;
-      apdu_buffer = xtrymalloc (apdu_buffer_size);
+      apdu_buffer = xtrymalloc (apdu_buffer_size + 10);
       if (!apdu_buffer)
         return SW_HOST_OUT_OF_CORE;
       apdu = apdu_buffer;
@@ -2896,6 +2911,24 @@ send_le (int slot, int class, int ins, int p0, int p1,
       apdu = short_apdu_buffer;
     }
 
+  if (use_extended_length && (le > 256 || le < 0))
+    {
+      result_buffer_size = le < 0? 4096 : le;
+      result_buffer = xtrymalloc (result_buffer_size + 10);
+      if (!result_buffer)
+        {
+          xfree (apdu_buffer);
+          return SW_HOST_OUT_OF_CORE;
+        }
+      result = result_buffer;
+    }
+  else
+    {
+      result_buffer_size = SHORT_RESULT_BUFFER_SIZE;
+      result = short_result_buffer;
+    }
+#undef SHORT_RESULT_BUFFER_SIZE
+
   if ((sw = lock_slot (slot)))
     return sw;
 
@@ -2963,7 +2996,7 @@ send_le (int slot, int class, int ins, int p0, int p1,
       /* As a safeguard don't pass any garbage to the driver.  */
       assert (apdulen <= apdu_buffer_size);
       memset (apdu+apdulen, 0, apdu_buffer_size - apdulen);
-      resultlen = RESULTLEN;
+      resultlen = result_buffer_size;
       rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo);
       if (rc || resultlen < 2)
         {
@@ -3051,7 +3084,7 @@ send_le (int slot, int class, int ins, int p0, int p1,
           apdu[apdulen++] = len;
           assert (apdulen <= apdu_buffer_size);
           memset (apdu+apdulen, 0, apdu_buffer_size - apdulen);
-          resultlen = RESULTLEN;
+          resultlen = result_buffer_size;
           rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
           if (rc || resultlen < 2)
             {
@@ -3114,7 +3147,6 @@ send_le (int slot, int class, int ins, int p0, int p1,
     log_printhex ("      dump: ", *retbuf, *retbuflen);
 
   return sw;
-#undef RESULTLEN
 }
 
 /* Send an APDU to the card in SLOT.  The APDU is created from all
@@ -3210,6 +3242,7 @@ apdu_send_direct (int slot, int extended_mode,
                   unsigned char **retbuf, size_t *retbuflen)
 {
 #define RESULTLEN 258
+  /* FIXME:  Implement dynamic result buffer and extended Le.  */
   unsigned char apdu[5+256+1];
   size_t apdulen;
   unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
index e5a0dc5..4b2e13e 100644 (file)
@@ -135,6 +135,7 @@ gpg_error_t app_openpgp_storekey (app_t app, int keyno,
                           void *pincb_arg);
 #else
 /*-- app-help.c --*/
+unsigned int app_help_count_bits (const unsigned char *a, size_t len);
 gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip);
 size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff);
 
index 83b34c6..3d9c605 100644 (file)
@@ -1,5 +1,5 @@
 /* app-help.c - Application helper functions
- *     Copyright (C) 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2004, 2009 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 #include "iso7816.h"
 #include "tlv.h"
 
+
+/* Count the number of bits, assuming the A represents an unsigned big
+   integer of length LEN bytes.  If A is NULL a length of 0 is
+   returned. */
+unsigned int
+app_help_count_bits (const unsigned char *a, size_t len)
+{
+  unsigned int n = len * 8;
+  int i;
+
+  if (!a)
+    return 0;
+
+  for (; len && !*a; len--, a++, n -=8)
+    ;
+  if (len)
+    {
+      for (i=7; i && !(*a & (1<<i)); i--)
+        n--;
+    }
+  return n;
+}
+
+
 /* Return the KEYGRIP for the certificate CERT as an hex encoded
    string in the user provided buffer HEXKEYGRIP which must be of at
    least 41 bytes. */
index 4656b23..7e6c7f9 100644 (file)
@@ -100,6 +100,11 @@ struct app_local_s {
   int nks_version;  /* NKS version.  */
 
   int sigg_active;  /* True if switched to the SigG application.  */
+  int sigg_msig_checked;/*  True if we checked for a mass signature card.  */
+  int sigg_is_msig; /* True if this is a mass signature card.  */
+
+  int need_app_select; /* Need to re-select the application.  */
+
 };
 
 
@@ -120,6 +125,18 @@ do_deinit (app_t app)
 }
 
 
+static int
+all_zero_p (void *buffer, size_t length)
+{
+  char *p;
+
+  for (p=buffer; length; length--, p++)
+    if (*p)
+      return 0;
+  return 1;
+}
+
+
 /* Read the file with FID, assume it contains a public key and return
    its keygrip in the caller provided 41 byte buffer R_GRIPSTR. */
 static gpg_error_t
@@ -590,6 +607,65 @@ do_readcert (app_t app, const char *certid,
 }
 
 
+/* Handle the READKEY command. On success a canonical encoded
+   S-expression with the public key will get stored at PK and its
+   length at PKLEN; the caller must release that buffer.  On error PK
+   and PKLEN are not changed and an error code is returned.  As of now
+   this function is only useful for the internal authentication key.
+   Other keys are automagically retrieved via by means of the
+   certificate parsing code in commands.c:cmd_readkey.  For internal
+   use PK and PKLEN may be NULL to just check for an existing key.  */
+static gpg_error_t
+do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
+{
+  gpg_error_t err;
+  unsigned char *buffer[2];
+  size_t buflen[2];
+  unsigned short path[1] = { 0x4500 };
+
+  /* We use a generic name to retrieve PK.AUT.IFD-SPK.  */
+  if (!strcmp (keyid, "$IFDAUTHKEY") && app->app_local->nks_version >= 3)
+    ;
+  else /* Return the error code expected by cmd_readkey.  */
+    return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); 
+
+  /* Access the KEYD file which is always in the master directory.  */
+  err = iso7816_select_path (app->slot, path, DIM (path), NULL, NULL);
+  if (err)
+    return err;
+  /* Due to the above select we need to re-select our application.  */
+  app->app_local->need_app_select = 1;
+  /* Get the two records.  */
+  err = iso7816_read_record (app->slot, 5, 1, 0, &buffer[0], &buflen[0]);
+  if (err)
+    return err;
+  if (all_zero_p (buffer[0], buflen[0]))
+    {
+      xfree (buffer[0]);
+      return gpg_error (GPG_ERR_NOT_FOUND);
+    }
+  err = iso7816_read_record (app->slot, 6, 1, 0, &buffer[1], &buflen[1]);
+  if (err)
+    {
+      xfree (buffer[0]);
+      return err;
+    }
+
+  if (pk && pklen)
+    {
+      *pk = make_canon_sexp_from_rsa_pk (buffer[0], buflen[0],
+                                         buffer[1], buflen[1],
+                                         pklen);
+      if (!*pk)
+        err = gpg_error_from_syserror ();
+    }
+
+  xfree (buffer[0]);
+  xfree (buffer[1]);
+  return err;
+}
+
+
 static gpg_error_t
 basic_pin_checks (const char *pinvalue, int minlen, int maxlen)
 {
@@ -673,7 +749,6 @@ verify_pin (app_t app, int pwid, const char *desc,
 }
 
 
-
 /* 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;
    that callback should return the PIN in an allocated buffer and
@@ -723,6 +798,12 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
   if (rc)
     return rc;
 
+  if (is_sigg && app->app_local->sigg_is_msig)
+    {
+      log_info ("mass signature cards are not allowed\n");
+      return gpg_error (GPG_ERR_NOT_SUPPORTED);
+    }
+
   if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
       || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3) 
       || keyidstr[4])
@@ -1147,8 +1228,9 @@ switch_application (app_t app, int enable_sigg)
 {
   gpg_error_t err;
 
-  if ((app->app_local->sigg_active && enable_sigg)
-      || (!app->app_local->sigg_active && !enable_sigg) )
+  if (((app->app_local->sigg_active && enable_sigg)
+       || (!app->app_local->sigg_active && !enable_sigg))
+      && !app->app_local->need_app_select)
     return 0;  /* Already switched.  */
 
   log_info ("app-nks: switching to %s\n", enable_sigg? "SigG":"NKS");
@@ -1156,9 +1238,40 @@ switch_application (app_t app, int enable_sigg)
     err = iso7816_select_application (app->slot, aid_sigg, sizeof aid_sigg, 0);
   else
     err = iso7816_select_application (app->slot, aid_nks, sizeof aid_nks, 0);
+
+  if (!err && enable_sigg && app->app_local->nks_version >= 3 
+      && !app->app_local->sigg_msig_checked)
+    {
+      /* Check whether this card is a mass signature card.  */
+      unsigned char *buffer;
+      size_t buflen;
+      const unsigned char *tmpl;
+      size_t tmpllen;
+      
+      app->app_local->sigg_msig_checked = 1;
+      app->app_local->sigg_is_msig = 1;
+      err = iso7816_select_file (app->slot, 0x5349, 0, NULL, NULL);
+      if (!err)
+        err = iso7816_read_record (app->slot, 1, 1, 0, &buffer, &buflen);
+      if (!err)
+        {
+          tmpl = find_tlv (buffer, buflen, 0x7a, &tmpllen);
+          if (tmpl && tmpllen == 12 
+              && !memcmp (tmpl,
+                          "\x93\x02\x00\x01\xA4\x06\x83\x01\x81\x83\x01\x83",
+                          12))
+            app->app_local->sigg_is_msig = 0;
+          xfree (buffer);
+        }
+      if (app->app_local->sigg_is_msig)
+        log_info ("This is a mass signature card\n");
+    }
   
   if (!err)
-    app->app_local->sigg_active = enable_sigg;
+    {
+      app->app_local->need_app_select = 0;
+      app->app_local->sigg_active = enable_sigg;
+    }
   else
     log_error ("app-nks: error switching to %s: %s\n",
                enable_sigg? "SigG":"NKS", gpg_strerror (err));
@@ -1193,8 +1306,10 @@ app_select_nks (app_t app)
       app->fnc.deinit = do_deinit;
       app->fnc.learn_status = do_learn_status;
       app->fnc.readcert = do_readcert;
+      app->fnc.readkey = do_readkey;
       app->fnc.getattr = do_getattr;
       app->fnc.setattr = NULL;
+      app->fnc.writekey = NULL;
       app->fnc.genkey = NULL;
       app->fnc.sign = do_sign;
       app->fnc.auth = NULL;
index 6f48cb8..a633fea 100644 (file)
@@ -1095,9 +1095,9 @@ get_public_key (app_t app, int keyno)
     {
       /* We may simply read the public key out of these cards.  */
       err = iso7816_read_public_key 
-        (app->slot, (const unsigned char*)(keyno == 0? "\xB6" :
-                                           keyno == 1? "\xB8" : "\xA4"),
-         2,  
+        (app->slot, 0, (const unsigned char*)(keyno == 0? "\xB6" :
+                                              keyno == 1? "\xB8" : "\xA4"), 2,  
+         0,
          &buffer, &buflen);
       if (err)
         {
@@ -2530,6 +2530,9 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
   int keyno = atoi (keynostr);
   int force = (flags & 1);
   time_t start_at;
+  int exmode;
+  int le_value;
+  unsigned int keybits; 
 
   if (keyno < 1 || keyno > 3)
     return gpg_error (GPG_ERR_INV_ID);
@@ -2550,22 +2553,44 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
   if (rc)
     return rc;
 
+  /* 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.  */ 
+  keybits = app->app_local->keyattr[keyno].n_bits;
+  if (keybits > 3072)
+    return gpg_error (GPG_ERR_TOO_LARGE);
+
   /* Prepare for key generation by verifying the Admin PIN.  */
   rc = verify_chv3 (app, pincb, pincb_arg);
   if (rc)
     goto leave;
-   
-#if 1
+
+  /* Test whether we will need extended length mode.  (1900 is an
+     arbitrary length which for sure fits into a short apdu.)  */
+  if (app->app_local->cardcap.ext_lc_le && keybits > 1900)
+    {
+      exmode = 1;    /* Use extended length w/o a limit.  */
+      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.  */ 
+    }
+  else
+    {
+      exmode = 0;
+      le_value = 256; /* Use legacy value. */
+    }
+
   log_info (_("please wait while key is being generated ...\n"));
   start_at = time (NULL);
   rc = iso7816_generate_keypair 
-#else
-# warning key generation temporary replaced by reading an existing key.
-  rc = iso7816_read_public_key
-#endif
-    (app->slot, (const unsigned char*)(keyno == 0? "\xB6" :
-                                       keyno == 1? "\xB8" : "\xA4"),
-     2,
+/* # warning key generation temporary replaced by reading an existing key. */
+/*   rc = iso7816_read_public_key */
+    (app->slot, exmode, 
+     (const unsigned char*)(keyno == 0? "\xB6" :
+                            keyno == 1? "\xB8" : "\xA4"), 2,
+     le_value,
      &buffer, &buflen);
   if (rc)
     {
@@ -2575,6 +2600,7 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
     }
   log_info (_("key generation completed (%d seconds)\n"),
             (int)(time (NULL) - start_at));
+
   keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
   if (!keydata)
     {
@@ -2590,7 +2616,7 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
       log_error (_("response does not contain the RSA modulus\n"));
       goto leave;
     }
-/*    log_printhex ("RSA n:", m, mlen); */
+  /* log_printhex ("RSA n:", m, mlen); */
   send_key_data (ctrl, "n", m, mlen);
 
   e = find_tlv (keydata, keydatalen, 0x0082, &elen);
@@ -2600,7 +2626,7 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
       log_error (_("response does not contain the RSA public exponent\n"));
       goto leave;
     }
-/*    log_printhex ("RSA e:", e, elen); */
+  /* log_printhex ("RSA e:", e, elen); */
   send_key_data (ctrl, "e", e, elen);
 
   created_at = createtime? createtime : gnupg_get_time ();
@@ -2995,6 +3021,7 @@ do_decipher (app_t app, const char *keyidstr,
   const char *s;
   int n;
   const char *fpr = NULL;
+  int exmode;
 
   if (!keyidstr || !*keyidstr || !indatalen)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -3030,7 +3057,7 @@ do_decipher (app_t app, const char *keyidstr,
      the card.  This is allows for a meaningful error message in case
      the key on the card has been replaced but the shadow information
      known to gpg was not updated.  If there is no fingerprint, the
-     decryption will won't produce the right plaintext anyway. */
+     decryption won't produce the right plaintext anyway. */
   rc = fpr? check_against_given_fingerprint (app, fpr, 2) : 0;
   if (rc)
     return rc;
@@ -3039,6 +3066,8 @@ do_decipher (app_t app, const char *keyidstr,
   if (!rc)
     {
       size_t fixuplen;
+      unsigned char *fixbuf = NULL;
+      int padind = 0;
 
       /* We might encounter a couple of leading zeroes in the
          cryptogram.  Due to internal use of MPIs thease leading
@@ -3049,39 +3078,46 @@ do_decipher (app_t app, const char *keyidstr,
          probability anyway broken.  */
       if (indatalen >= (128-16) && indatalen < 128)      /* 1024 bit key.  */
         fixuplen = 128 - indatalen;
-      else if (indatalen >= (256-16) && indatalen < 256) /* 2048 bit key.  */
-        fixuplen = 256 - indatalen;
       else if (indatalen >= (192-16) && indatalen < 192) /* 1536 bit key.  */
         fixuplen = 192 - indatalen;
+      else if (indatalen >= (256-16) && indatalen < 256) /* 2048 bit key.  */
+        fixuplen = 256 - indatalen;
+      else if (indatalen >= (384-16) && indatalen < 384) /* 3072 bit key.  */
+        fixuplen = 384 - indatalen;
       else
         fixuplen = 0;
+
       if (fixuplen)
         {
-          unsigned char *fixbuf;
-
           /* While we have to prepend stuff anyway, we can also
              include the padding byte here so that iso1816_decipher
-             does not need to do yet another data mangling.  */
+             does not need to do another data mangling.  */
           fixuplen++;
+
           fixbuf = xtrymalloc (fixuplen + indatalen);
           if (!fixbuf)
-            rc = gpg_error_from_syserror ();
-          else
-            {
-              memset (fixbuf, 0, fixuplen);
-              memcpy (fixbuf+fixuplen, indata, indatalen);
-              rc = iso7816_decipher (app->slot, 0,
-                                     fixbuf, fixuplen+indatalen, -1,
-                                     outdata, outdatalen);
-              xfree (fixbuf);
-            }
-
+            return gpg_error_from_syserror ();
+          
+          memset (fixbuf, 0, fixuplen);
+          memcpy (fixbuf+fixuplen, indata, indatalen);
+          indata = fixbuf;
+          indatalen = fixuplen + indatalen;
+          padind = -1; /* Already padded.  */
         }
+      
+      if (app->app_local->cardcap.ext_lc_le && indatalen > 254 )
+        exmode = 1;    /* Extended length w/o a limit.  */
+      else if (app->app_local->cardcap.cmd_chaining && indatalen > 254)
+        exmode = -254; /* Command chaining with max. 254 bytes.  */
       else
-        rc = iso7816_decipher (app->slot, 0, 
-                               indata, indatalen, 0,
-                               outdata, outdatalen);
+        exmode = 0;    
+
+      rc = iso7816_decipher (app->slot, exmode, 
+                             indata, indatalen, padind,
+                             outdata, outdatalen);
+      xfree (fixbuf);
     }
+
   return rc;
 }
 
index c159b5c..75a38ca 100644 (file)
 #endif /* This source not used by scdaemon. */
 
 
+#ifndef EAGAIN
+#define EAGAIN  EWOULDBLOCK
+#endif
+
+
 
 enum {
   RDR_to_PC_NotifySlotChange= 0x50,
@@ -1811,6 +1816,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
 {
   int rc;
   size_t msglen;
+  int eagain_retries = 0;
 
   /* Fixme: The next line for the current Valgrind without support
      for USB IOCTLs. */
@@ -1824,7 +1830,13 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
                           timeout);
       if (rc < 0)
         {
-          DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (errno));
+          rc = errno;
+          DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (rc));
+          if (rc == EAGAIN && eagain_retries++ < 5)
+            {
+              gnupg_sleep (1);
+              goto retry;
+            }
           return CCID_DRIVER_ERR_CARD_IO_ERROR;
         }
       *nread = msglen = rc;
@@ -1834,12 +1846,19 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
       rc = read (handle->dev_fd, buffer, length);
       if (rc < 0)
         {
+          rc = errno;
           DEBUGOUT_2 ("read from %d failed: %s\n",
-                      handle->dev_fd, strerror (errno));
+                      handle->dev_fd, strerror (rc));
+          if (rc == EAGAIN && eagain_retries++ < 5)
+            {
+              gnupg_sleep (1);
+              goto retry;
+            }
           return CCID_DRIVER_ERR_CARD_IO_ERROR;
         }
       *nread = msglen = rc;
     }
+  eagain_retries = 0;
 
   if (msglen < 10)
     {
@@ -1942,6 +1961,7 @@ abort_cmd (ccid_driver_t handle)
   /* 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.  */
+  handle->seqno--;  /* Restore the last one sent.  */
   seqno = (handle->seqno & 0xff);
   rc = usb_control_msg (handle->idev, 
                         0x21,/* bmRequestType: host-to-device,
@@ -1958,34 +1978,36 @@ abort_cmd (ccid_driver_t handle)
     }
 
   /* Now send the abort command to the bulk out pipe using the same
-     SEQNO and SLOT. */
-  msg[0] = PC_to_RDR_Abort;
-  msg[5] = 0; /* slot */
-  msg[6] = seqno;
-  msg[7] = 0; /* RFU */
-  msg[8] = 0; /* RFU */
-  msg[9] = 0; /* RFU */
-  msglen = 10;
-  set_msg_len (msg, 0);
-  handle->seqno++;  /* Bumb up for the next use. */
-
-  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", strerror (errno));
-  else
-    DEBUGOUT_1 ("usb_bulk_write failed in abort_cmd: %d\n", rc);
-
-  if (rc)
-    return rc;
-
-  /* Wait for the expected response.  */
+     SEQNO and SLOT.  Do this in a loop to so that all seqno are
+     tried.  */
+  seqno--;  /* Adjust for next increment.  */
   do
     {
+      seqno++; 
+      msg[0] = PC_to_RDR_Abort;
+      msg[5] = 0; /* slot */
+      msg[6] = seqno;
+      msg[7] = 0; /* RFU */
+      msg[8] = 0; /* RFU */
+      msg[9] = 0; /* RFU */
+      msglen = 10;
+      set_msg_len (msg, 0);
+
+      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", 
+                    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, 
                           handle->ep_bulk_in,
                           (char*)msg, sizeof msg, 
@@ -2017,6 +2039,7 @@ abort_cmd (ccid_driver_t handle)
     }
   while (msg[0] != RDR_to_PC_SlotStatus && msg[5] != 0 && msg[6] != seqno);
 
+  handle->seqno = seqno;
   DEBUGOUT ("sending abort sequence succeeded\n");
 
   return 0;
@@ -2430,12 +2453,13 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
                             size_t *nresp)
 {
   int rc;
-  unsigned char send_buffer[10+261], recv_buffer[10+261];
+  unsigned char send_buffer[10+261+300], recv_buffer[10+261+300];
   const unsigned char *apdu;
   size_t apdulen;
   unsigned char *msg;
   size_t msglen;
   unsigned char seqno;
+  int bwi = 4;
 
   msg = send_buffer;
 
@@ -2448,11 +2472,11 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
      extended APDU exchange level is not yet supported.  */
   if (apdulen > 261)
     return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
-
+  
   msg[0] = PC_to_RDR_XfrBlock;
   msg[5] = 0; /* slot */
   msg[6] = seqno = handle->seqno++;
-  msg[7] = 4; /* bBWI */
+  msg[7] = bwi; /* bBWI */
   msg[8] = 0; /* RFU */
   msg[9] = 0; /* RFU */
   memcpy (msg+10, apdu, apdulen);
@@ -3274,6 +3298,13 @@ main (int argc, char **argv)
   return 0;
 }
 
+static coid
+gnupg_sleep (int seconds)
+{
+  sleep (seconds);
+}
+
+
 /*
  * Local Variables:
  *  compile-command: "gcc -DTEST -Wall -I/usr/local/include -lusb -g ccid-driver.c"
index d12f918..492e125 100644 (file)
@@ -605,10 +605,15 @@ 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
-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 readonly,
+                     const unsigned char *data, size_t datalen,
+                     int le, 
+                     unsigned char **result, size_t *resultlen)
 {
   int sw;
 
@@ -617,9 +622,11 @@ do_generate_keypair (int slot, int readonly,
   *result = NULL;
   *resultlen = 0;
 
-  sw = apdu_send (slot, 0,
-                  0x00, CMD_GENERATE_KEYPAIR, readonly? 0x81:0x80, 0,
-                  datalen, (const char*)data,  result, resultlen);
+  sw = apdu_send_le (slot, extended_mode,
+                     0x00, CMD_GENERATE_KEYPAIR, readonly? 0x81:0x80, 0,
+                     datalen, (const char*)data,
+                     le >= 0 && le < 256? 256:le,
+                     result, resultlen);
   if (sw != SW_SUCCESS)
     {
       /* Make sure that pending buffers are released. */
@@ -634,20 +641,24 @@ do_generate_keypair (int slot, int readonly,
 
 
 gpg_error_t
-iso7816_generate_keypair (int slot,
+iso7816_generate_keypair (int slot, int extended_mode,
                           const unsigned char *data, size_t datalen,
+                          int le, 
                           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
-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 unsigned 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);
 }
 
 
index 44e50d7..3c6dd89 100644 (file)
@@ -103,12 +103,14 @@ gpg_error_t iso7816_decipher (int slot, int extended_mode,
 gpg_error_t iso7816_internal_authenticate (int slot,
                                    const unsigned char *data, size_t datalen,
                                    unsigned char **result, size_t *resultlen);
-gpg_error_t iso7816_generate_keypair (int slot,
-                                   const unsigned char *data, size_t datalen,
-                                   unsigned char **result, size_t *resultlen);
-gpg_error_t iso7816_read_public_key (int slot,
-                                   const unsigned char *data, size_t datalen,
-                                   unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_generate_keypair (int slot, int extended_mode,
+                                    const unsigned char *data, size_t datalen,
+                                    int le,
+                                    unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_read_public_key (int slot, int extended_mode,
+                                    const unsigned char *data, size_t datalen,
+                                    int le,
+                                    unsigned char **result, size_t *resultlen);
 gpg_error_t iso7816_get_challenge (int slot,
                                    int length, unsigned char *buffer);