[svn] GnuPG - r4997 - trunk/scd

svn author wk cvs at cvs.gnupg.org
Fri May 8 17:07:46 CEST 2009


Author: wk
Date: 2009-05-08 17:07:45 +0200 (Fri, 08 May 2009)
New Revision: 4997

Modified:
   trunk/scd/ChangeLog
   trunk/scd/apdu.c
   trunk/scd/app-common.h
   trunk/scd/app-help.c
   trunk/scd/app-nks.c
   trunk/scd/app-openpgp.c
   trunk/scd/ccid-driver.c
   trunk/scd/iso7816.c
   trunk/scd/iso7816.h
Log:
More support for Netkey cards.
Small changes to teh CCID driver.
Support 2048 bit OpenPGP cards.


Modified: trunk/scd/ChangeLog
===================================================================
--- trunk/scd/ChangeLog	2009-05-07 15:01:47 UTC (rev 4996)
+++ trunk/scd/ChangeLog	2009-05-08 15:07:45 UTC (rev 4997)
@@ -1,3 +1,26 @@
+2009-05-08  Werner Koch  <wk at 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 at 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 at g10code.com>
 
 	* app-openpgp.c (do_setattr, do_writekey): Prepare for extended

Modified: trunk/scd/apdu.c
===================================================================
--- trunk/scd/apdu.c	2009-05-07 15:01:47 UTC (rev 4996)
+++ trunk/scd/apdu.c	2009-05-08 15:07:45 UTC (rev 4997)
@@ -2817,8 +2817,6 @@
                 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 @@
          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 @@
   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 @@
 
       /* 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 @@
       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 @@
       /* 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 @@
           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 @@
     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 @@
                   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

Modified: trunk/scd/app-common.h
===================================================================
--- trunk/scd/app-common.h	2009-05-07 15:01:47 UTC (rev 4996)
+++ trunk/scd/app-common.h	2009-05-08 15:07:45 UTC (rev 4997)
@@ -135,6 +135,7 @@
                           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);
 

Modified: trunk/scd/app-help.c
===================================================================
--- trunk/scd/app-help.c	2009-05-07 15:01:47 UTC (rev 4996)
+++ trunk/scd/app-help.c	2009-05-08 15:07:45 UTC (rev 4997)
@@ -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.
  *
@@ -28,6 +28,30 @@
 #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. */

Modified: trunk/scd/app-nks.c
===================================================================
--- trunk/scd/app-nks.c	2009-05-07 15:01:47 UTC (rev 4996)
+++ trunk/scd/app-nks.c	2009-05-08 15:07:45 UTC (rev 4997)
@@ -100,6 +100,11 @@
   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 @@
 }
 
 
+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,7 +607,66 @@
 }
 
 
+/* 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)
 {
   if (strlen (pinvalue) < minlen)
@@ -673,7 +749,6 @@
 }
 
 
-
 /* 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 @@
   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 @@
 {
   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 @@
     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->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;

Modified: trunk/scd/app-openpgp.c
===================================================================
--- trunk/scd/app-openpgp.c	2009-05-07 15:01:47 UTC (rev 4996)
+++ trunk/scd/app-openpgp.c	2009-05-08 15:07:45 UTC (rev 4997)
@@ -1095,9 +1095,9 @@
     {
       /* 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 @@
   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 @@
   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 @@
     }
   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 @@
       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 @@
       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 @@
   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 @@
      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 @@
   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 @@
          probability anyway broken.  */
       if (indatalen >= (128-16) && indatalen < 128)      /* 1024 bit key.  */
         fixuplen = 128 - 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 >= (192-16) && indatalen < 192) /* 1536 bit key.  */
-        fixuplen = 192 - 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;
 }
 

Modified: trunk/scd/ccid-driver.c
===================================================================
--- trunk/scd/ccid-driver.c	2009-05-07 15:01:47 UTC (rev 4996)
+++ trunk/scd/ccid-driver.c	2009-05-08 15:07:45 UTC (rev 4997)
@@ -159,7 +159,12 @@
 #endif /* This source not used by scdaemon. */
 
 
+#ifndef EAGAIN
+#define EAGAIN  EWOULDBLOCK
+#endif
 
+
+
 enum {
   RDR_to_PC_NotifySlotChange= 0x50,
   RDR_to_PC_HardwareError   = 0x51,
@@ -1811,6 +1816,7 @@
 {
   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 @@
                           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 @@
       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 @@
   /* 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 @@
     }
 
   /* 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. */
+     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);
+      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.  */
-  do
-    {
+      if (rc)
+        return rc;
+      
       rc = usb_bulk_read (handle->idev, 
                           handle->ep_bulk_in,
                           (char*)msg, sizeof msg, 
@@ -2017,6 +2039,7 @@
     }
   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 @@
                             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 @@
      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 @@
   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"

Modified: trunk/scd/iso7816.c
===================================================================
--- trunk/scd/iso7816.c	2009-05-07 15:01:47 UTC (rev 4996)
+++ trunk/scd/iso7816.c	2009-05-08 15:07:45 UTC (rev 4997)
@@ -605,10 +605,15 @@
 }
 
 
+/* 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 @@
   *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 @@
 
 
 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);
 }
 
 

Modified: trunk/scd/iso7816.h
===================================================================
--- trunk/scd/iso7816.h	2009-05-07 15:01:47 UTC (rev 4996)
+++ trunk/scd/iso7816.h	2009-05-08 15:07:45 UTC (rev 4997)
@@ -103,12 +103,14 @@
 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);
 




More information about the Gnupg-commits mailing list