Implement decryption for TCOS 3 cards.
authorWerner Koch <wk@gnupg.org>
Mon, 30 Mar 2009 12:46:06 +0000 (12:46 +0000)
committerWerner Koch <wk@gnupg.org>
Mon, 30 Mar 2009 12:46:06 +0000 (12:46 +0000)
13 files changed:
TODO
agent/ChangeLog
agent/learncard.c
scd/ChangeLog
scd/apdu.c
scd/apdu.h
scd/app-help.c
scd/app-nks.c
scd/app-openpgp.c
scd/ccid-driver.c
scd/command.c
scd/iso7816.c
scd/iso7816.h

diff --git a/TODO b/TODO
index 7806835..45e221b 100644 (file)
--- a/TODO
+++ b/TODO
 ** mark all unimplemented commands and options.
 ** Implement --default-key
 ** support the anyPolicy semantic
-
+** Should we prefer nonRepudiation certs over plain signing certs?
+   Also: Do we need a way to allow the selection of a qualSig cert
+   over a plain one?  The background is that the Telesec cards have 3
+   certs capable of signing all with the same subject name.
 
 * sm/keydb.c
 ** Check file permissions
index c551991..10390da 100644 (file)
@@ -1,3 +1,7 @@
+2009-03-27  Werner Koch  <wk@g10code.com>
+
+       * learncard.c (agent_handle_learn): Add new certtype 111.
+
 2009-03-26  Werner Koch  <wk@g10code.com>
 
        * agent.h (MAX_DIGEST_LEN): Change to 64.
index 3db33ee..b5b5bd7 100644 (file)
@@ -298,10 +298,12 @@ agent_handle_learn (ctrl_t ctrl, void *assuan_context)
   char *p;
   int i;
   static int certtype_list[] = { 
+    111, /* Root CA */
     101, /* trusted */
     102, /* useful */
     100, /* regular */
-    /* We don't include 110 here because gpgsm can't handle it. */
+    /* We don't include 110 here because gpgsm can't handle that
+       special root CA format. */
     -1 /* end of list */
   };
 
index f6de259..efd46fe 100644 (file)
@@ -1,3 +1,16 @@
+2009-03-30  Werner Koch  <wk@g10code.com>
+
+       * app-nks.c (do_decipher): Make it work for TCOS 3.
+       * iso7816.c (iso7816_decipher): Add arg EXTENDED_MODE.
+       * apdu.c (apdu_send): Add arg EXTENDED_MODE and change all callers.
+       (apdu_send_le):  Ditto. 
+       (apdu_send_direct): Ditto, but not yet functional.
+       (send_le): Fix command chaining.  Implement extended length option.
+       * ccid-driver.c (ccid_transceive): Remove restriction on apdu length.
+       (struct ccid_driver_s): Add field IFSC.
+       (ccid_get_atr): Set IFSC.
+       (ccid_transceive): Use negotiated IFSC and support S(IFS) command.
+
 2009-03-26  Werner Koch  <wk@g10code.com>
 
        * command.c (cmd_pksign): Allow more hash algorithms.
index b12b7e9..d63157c 100644 (file)
@@ -414,6 +414,7 @@ apdu_strerror (int rc)
     case SW_FILE_NOT_FOUND : return "file not found";
     case SW_RECORD_NOT_FOUND:return "record not found";
     case SW_REF_NOT_FOUND  : return "reference not found";
+    case SW_BAD_LC         : return "bad Lc";
     case SW_BAD_P0_P1      : return "bad P0 or P1";
     case SW_INS_NOT_SUP    : return "instruction not supported";
     case SW_CLA_NOT_SUP    : return "class not supported";
@@ -2806,14 +2807,18 @@ 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
-   related operations if not NULL.  If EXTENDED_MODE is not NULL
+   related operations if not NULL.  If EXTENDED_MODE is not 0
    command chaining or extended length will be used according to these
    values:
        n < 0 := Use command chaining with the data part limited to -n
                 in each chunk.  If -1 is used a default value is used.
+      n == 0 := No extended mode or command chaining.
       n == 1 := Use extended length for input and output without a
                 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,
@@ -2825,12 +2830,16 @@ send_le (int slot, int class, int ins, int p0, int p1,
   unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
                                          the driver. */
   size_t resultlen;
-  unsigned char apdu[5+256+1];
+  unsigned char short_apdu_buffer[5+256+1];
+  unsigned char *apdu_buffer = NULL;
+  size_t apdu_buffer_size;
+  unsigned char *apdu;
   size_t apdulen;
   int sw;
   long rc; /* We need a long here due to PC/SC. */
   int did_exact_length_hack = 0;
   int use_chaining = 0;
+  int use_extended_length = 0;
   int lc_chunk;
 
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
@@ -2847,7 +2856,7 @@ send_le (int slot, int class, int ins, int p0, int p1,
       if (!extended_mode)
         return SW_WRONG_LENGTH; /* No way to send such an APDU.  */
       else if (extended_mode > 0)
-        return SW_HOST_NOT_SUPPORTED; /* FIXME.  */
+        use_extended_length = 1;
       else if (extended_mode < 0)
         {
           /* Send APDU using chaining mode.  */
@@ -2861,51 +2870,99 @@ send_le (int slot, int class, int ins, int p0, int p1,
       else 
         return SW_HOST_INV_VALUE;
     }
+  else if (lc == -1 && extended_mode > 0)
+    use_extended_length = 1;
+    
   if (le != -1 && (le > 256 || le < 0))
     return SW_WRONG_LENGTH;
   if ((!data && lc != -1) || (data && lc == -1))
     return SW_HOST_INV_VALUE;
 
+  if (use_extended_length)
+    {
+      if (reader_table[slot].is_t0)
+        return SW_HOST_NOT_SUPPORTED;
+
+      /* 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);
+      if (!apdu_buffer)
+        return SW_HOST_OUT_OF_CORE;
+      apdu = apdu_buffer;
+    }
+  else
+    {
+      apdu_buffer_size = sizeof short_apdu_buffer;
+      apdu = short_apdu_buffer;
+    }
+
   if ((sw = lock_slot (slot)))
     return sw;
 
   do
     {
-      apdulen = 0;
-      apdu[apdulen] = class;
-      if (use_chaining && lc > 255)
-        {
-          apdu[apdulen] |= 0x10;
-          assert (use_chaining < 256);
-          lc_chunk = use_chaining;
-          lc -= use_chaining;
-        }
-      else
+      if (use_extended_length)
         {
           use_chaining = 0;
-          lc_chunk = lc;
+          apdulen = 0;
+          apdu[apdulen++] = class;
+          apdu[apdulen++] = ins;
+          apdu[apdulen++] = p0;
+          apdu[apdulen++] = p1;
+          apdu[apdulen++] = 0;  /* Z byte: Extended length marker.  */
+          if (lc >= 0)
+            {
+              apdu[apdulen++] = ((lc >> 8) & 0xff);
+              apdu[apdulen++] = (lc & 0xff);
+              memcpy (apdu+apdulen, data, lc);
+              data += lc;
+              apdulen += lc;
+            }
+          if (le != -1)
+            {
+              apdu[apdulen++] = ((le >> 8) & 0xff); 
+              apdu[apdulen++] = (le & 0xff); 
+            }
         }
-      apdulen++;
-      apdu[apdulen++] = ins;
-      apdu[apdulen++] = p0;
-      apdu[apdulen++] = p1;
-      if (lc_chunk != -1)
+      else
         {
-          apdu[apdulen++] = lc_chunk;
-          memcpy (apdu+apdulen, data, lc_chunk);
-          data += lc_chunk;
-          apdulen += lc_chunk;
-          /* T=0 does not allow the use of Lc together with Le; thus
-             disable Le in this case.  */
-          if (reader_table[slot].is_t0)
-            le = -1;
+          apdulen = 0;
+          apdu[apdulen] = class;
+          if (use_chaining && lc > 255)
+            {
+              apdu[apdulen] |= 0x10;
+              assert (use_chaining < 256);
+              lc_chunk = use_chaining;
+              lc -= use_chaining;
+            }
+          else
+            {
+              use_chaining = 0;
+              lc_chunk = lc;
+            }
+          apdulen++;
+          apdu[apdulen++] = ins;
+          apdu[apdulen++] = p0;
+          apdu[apdulen++] = p1;
+          if (lc_chunk != -1)
+            {
+              apdu[apdulen++] = lc_chunk;
+              memcpy (apdu+apdulen, data, lc_chunk);
+              data += lc_chunk;
+              apdulen += lc_chunk;
+              /* T=0 does not allow the use of Lc together with Le;
+                 thus disable Le in this case.  */
+              if (reader_table[slot].is_t0)
+                le = -1;
+            }
+          if (le != -1 && !use_chaining)
+            apdu[apdulen++] = le; /* Truncation is okay (0 means 256). */
         }
-      if (le != -1)
-        apdu[apdulen++] = le; /* Truncation is okay because 0 means 256. */
-      /* As safeguard don't pass any garbage from the stack to the driver. */
-      assert (sizeof (apdu) >= apdulen);
-      memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
+
     exact_length_hack:
+      /* 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;
       rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo);
       if (rc || resultlen < 2)
@@ -2916,7 +2973,8 @@ send_le (int slot, int class, int ins, int p0, int p1,
           return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
         }
       sw = (result[resultlen-2] << 8) | result[resultlen-1];
-      if (!did_exact_length_hack && SW_EXACT_LENGTH_P (sw))
+      if (!use_extended_length 
+          && !did_exact_length_hack && SW_EXACT_LENGTH_P (sw))
         {
           apdu[apdulen-1] = (sw & 0x00ff);
           did_exact_length_hack = 1;
@@ -2925,6 +2983,13 @@ send_le (int slot, int class, int ins, int p0, int p1,
     }
   while (use_chaining && sw == SW_SUCCESS);
 
+  if (apdu_buffer)
+    {
+      xfree (apdu_buffer);
+      apdu_buffer = NULL;
+      apdu_buffer_size = 0;
+    }
+  
   /* Store away the returned data but strip the statusword. */
   resultlen -= 2;
   if (DBG_CARD_IO)
@@ -2976,13 +3041,16 @@ send_le (int slot, int class, int ins, int p0, int p1,
           if (DBG_CARD_IO)
             log_debug ("apdu_send_simple(%d): %d more bytes available\n",
                        slot, len);
+          apdu_buffer_size = sizeof short_apdu_buffer;
+          apdu = short_apdu_buffer;
           apdulen = 0;
           apdu[apdulen++] = class;
           apdu[apdulen++] = 0xC0;
           apdu[apdulen++] = 0;
           apdu[apdulen++] = 0;
           apdu[apdulen++] = len;
-          memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
+          assert (apdulen <= apdu_buffer_size);
+          memset (apdu+apdulen, 0, apdu_buffer_size - apdulen);
           resultlen = RESULTLEN;
           rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
           if (rc || resultlen < 2)
@@ -3052,47 +3120,52 @@ send_le (int slot, int class, int ins, int p0, int p1,
 /* Send an APDU to the card in SLOT.  The APDU is created from all
    given parameters: CLASS, INS, P0, P1, LC, DATA, LE.  A value of -1
    for LC won't sent this field and the data field; in this case DATA
-   must also be passed as NULL.  The return value is the status word
-   or -1 for an invalid SLOT or other non card related error.  If
-   RETBUF is not NULL, it will receive an allocated buffer with the
-   returned data.  The length of that data will be put into
-   *RETBUFLEN.  The caller is reponsible for releasing the buffer even
-   in case of errors.  */
+   must also be passed as NULL.  If EXTENDED_MODE is not 0 command
+   chaining or extended length will be used; see send_le for details.
+   The return value is the status word or -1 for an invalid SLOT or
+   other non card related error.  If RETBUF is not NULL, it will
+   receive an allocated buffer with the returned data.  The length of
+   that data will be put into *RETBUFLEN.  The caller is reponsible
+   for releasing the buffer even in case of errors.  */
 int
-apdu_send_le(int slot, int class, int ins, int p0, int p1,
+apdu_send_le(int slot, int extended_mode, 
+             int class, int ins, int p0, int p1,
              int lc, const char *data, int le,
              unsigned char **retbuf, size_t *retbuflen)
 {
   return send_le (slot, class, ins, p0, p1,
                   lc, data, le,
                   retbuf, retbuflen,
-                  NULL, 0);
+                  NULL, extended_mode);
 }
 
 
 /* Send an APDU to the card in SLOT.  The APDU is created from all
    given parameters: CLASS, INS, P0, P1, LC, DATA.  A value of -1 for
    LC won't sent this field and the data field; in this case DATA must
-   also be passed as NULL. The return value is the status word or -1
-   for an invalid SLOT or other non card related error.  If RETBUF is
-   not NULL, it will receive an allocated buffer with the returned
-   data.  The length of that data will be put into *RETBUFLEN.  The
-   caller is reponsible for releasing the buffer even in case of
-   errors.  */
+   also be passed as NULL.  If EXTENDED_MODE is not 0 command chaining
+   or extended length will be used; see send_le for details.  The
+   return value is the status word or -1 for an invalid SLOT or other
+   non card related error.  If RETBUF is not NULL, it will receive an
+   allocated buffer with the returned data.  The length of that data
+   will be put into *RETBUFLEN.  The caller is reponsible for
+   releasing the buffer even in case of errors.  */
 int
-apdu_send (int slot, int class, int ins, int p0, int p1,
+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)
 {
   return send_le (slot, class, ins, p0, p1, lc, data, 256,
-                  retbuf, retbuflen, NULL, 0);
+                  retbuf, retbuflen, NULL, extended_mode);
 }
 
 /* Send an APDU to the card in SLOT.  The APDU is created from all
    given parameters: CLASS, INS, P0, P1, LC, DATA.  A value of -1 for
    LC won't sent this field and the data field; in this case DATA must
-   also be passed as NULL. The return value is the status word or -1
-   for an invalid SLOT or other non card related error.  No data will be
-   returned. */
+   also be passed as NULL.  If EXTENDED_MODE is not 0 command chaining
+   or extended length will be used; see send_le for details.  The
+   return value is the status word or -1 for an invalid SLOT or other
+   non card related error.  No data will be returned.  */
 int
 apdu_send_simple (int slot, int extended_mode,
                   int class, int ins, int p0, int p1,
@@ -3126,11 +3199,13 @@ apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
    and returns with an APDU including the status word.  With
    HANDLE_MORE set to true this function will handle the MORE DATA
    status and return all APDUs concatenated with one status word at
-   the end.  The function does not return a regular status word but 0
-   on success.  If the slot is locked, the function returns
-   immediately with an error.  */
+   the end.  If EXTENDED_MODE is not 0 command chaining or extended
+   length will be used; see send_le for details.  The function does
+   not return a regular status word but 0 on success.  If the slot is
+   locked, the function returns immediately with an error.  */
 int
-apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
+apdu_send_direct (int slot, int extended_mode,
+                  const unsigned char *apdudata, size_t apdudatalen,
                   int handle_more,
                   unsigned char **retbuf, size_t *retbuflen)
 {
@@ -3147,6 +3222,9 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
 
+  if (extended_mode)
+    return SW_HOST_NOT_SUPPORTED; /* FIXME. */
+
   if ((sw = trylock_slot (slot)))
     return sw;
 
index 007bda7..2a932df 100644 (file)
@@ -41,6 +41,7 @@ enum {
   SW_NOT_SUPPORTED  = 0x6a81,
   SW_FILE_NOT_FOUND = 0x6a82,
   SW_RECORD_NOT_FOUND = 0x6a83,
+  SW_BAD_LC         = 0x6a87, /* Lc does not match command or p1/p2.  */
   SW_REF_NOT_FOUND  = 0x6a88,
   SW_BAD_P0_P1      = 0x6b00,
   SW_EXACT_LENGTH   = 0x6c00,
@@ -117,13 +118,14 @@ 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 class, int ins, int p0, int p1,
-               int lc, const char *data,
+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);
-int apdu_send_le (int slot, int class, int ins, int p0, int p1,
+int apdu_send_le (int slot, int extended_mode, 
+                  int class, int ins, int p0, int p1,
                   int lc, const char *data, int le,
                   unsigned char **retbuf, size_t *retbuflen);
-int apdu_send_direct (int slot,
+int apdu_send_direct (int slot, int extended_mode,
                       const unsigned char *apdudata, size_t apdudatalen,
                       int handle_more,
                       unsigned char **retbuf, size_t *retbuflen);
index 7b9ce99..83b34c6 100644 (file)
@@ -121,8 +121,7 @@ app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff)
   if ( !(class == CLASS_UNIVERSAL &&  constructed
          && (tag == TAG_SEQUENCE || tag == TAG_SET)))
     {
-      log_info ("contents of FID 0x%04X does not look like a certificate\n",
-                fid);
+      log_info ("data at FID 0x%04X does not look like a certificate\n", fid);
       return 0;
     }
 
index d2ba978..4656b23 100644 (file)
   - We are now targeting TCOS 3 cards and it may happen that there is
     a regression towards TCOS 2 cards.  Please report.
 
-  - The TKS3 AUT key is not used by our authentication command but
-    accessible via the decrypt command.
+  - The TKS3 AUT key is not used.  It seems that it is only useful for
+    the internal authentication command and not accessible by other
+    applications.  The key itself is in the encryption class but the
+    corresponding certificate has only the digitalSignature
+    capability.
 
   - If required, we automagically switch between the NKS application
     and the SigG application.  This avoids to use the DINSIG
@@ -70,9 +73,7 @@ static struct
   unsigned char kid;  /* Corresponding key references.  */
 } filelist[] = {
   { 0, 0x4531, 0, 0,  0xC000, 1, 0, 0x80 }, /* EF_PK.NKS.SIG */
-  { 1, 0x4531, 3, 0,  0x0000, 1, 1, 0x84 }, /* EF_PK.CH.SIG  */
   { 0, 0xC000, 0, 101 },                    /* EF_C.NKS.SIG  */
-  { 1, 0xC000, 0, 101 },                    /* EF_C.CH.SIG  */
   { 0, 0x4331, 0, 100 },
   { 0, 0x4332, 0, 100 },
   { 0, 0xB000, 0, 110 },                    /* EF_PK.RCA.NKS */
@@ -80,11 +81,15 @@ static struct
   { 0, 0xC200, 0, 101 },                    /* EF_C.NKS.ENC  */
   { 0, 0x43B1, 0, 100 },
   { 0, 0x43B2, 0, 100 },
-  { 0, 0x4571, 3, 0,  0xc500, 0, 0, 0x82 }, /* EF_PK.NKS.AUT */
-  { 0, 0xC500, 3, 101 },                    /* EF_C.NKS.AUT  */
+/* The authentication key is not used.  */
+/*   { 0, 0x4571, 3, 0,  0xC500, 0, 0, 0x82 }, /\* EF_PK.NKS.AUT *\/ */
+/*   { 0, 0xC500, 3, 101 },                    /\* EF_C.NKS.AUT  *\/ */
   { 0, 0x45B2, 3, 0,  0xC201, 0, 1, 0x83 }, /* EF_PK.NKS.ENC1024 */
   { 0, 0xC201, 3, 101 },                    /* EF_C.NKS.ENC1024  */
-/*   { 1, 0xB000, 3, ...  */
+  { 1, 0x4531, 3, 0,  0xC000, 1, 1, 0x84 }, /* EF_PK.CH.SIG  */
+  { 1, 0xC000, 0, 101 },                    /* EF_C.CH.SIG  */
+  { 1, 0xC008, 3, 101 },                    /* EF_C.CA.SIG  */
+  { 1, 0xC00E, 3, 111 },                    /* EF_C.RCA.SIG  */
   { 0, 0 }
 };
 
@@ -249,7 +254,7 @@ get_chv_status (app_t app, int sigg, int pwid)
   command[2] = 0x00;
   command[3] = pwid;
 
-  if (apdu_send_direct (app->slot, command, 4, 0, &result, &resultlen))
+  if (apdu_send_direct (app->slot, 0, command, 4, 0, &result, &resultlen))
     rc = -1; /* Error. */
   else if (resultlen < 2)
     rc = -1; /* Error. */
@@ -808,13 +813,10 @@ do_decipher (app_t app, const char *keyidstr,
              const void *indata, size_t indatalen,
              unsigned char **outdata, size_t *outdatalen )
 {
-  static const unsigned char mse_parm[] = {
-    0x80, 1, 0x10, /* Select algorithm RSA. */
-    0x84, 1, 0x81  /* Select local secret key 1 for decryption. */
-  };
   int rc, i;
   int is_sigg = 0;
   int fid;
+  int kid;
 
   if (!keyidstr || !*keyidstr || !indatalen)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -847,15 +849,40 @@ do_decipher (app_t app, const char *keyidstr,
     return gpg_error (GPG_ERR_NOT_FOUND);
   if (!filelist[i].isenckey)
     return gpg_error (GPG_ERR_INV_ID);
+  kid = filelist[i].kid;
+
+  if (app->app_local->nks_version > 2)
+    {
+      unsigned char mse[6];
+      mse[0] = 0x80; /* Algorithm reference.  */
+      mse[1] = 1;
+      mse[2] = 0x0a; /* RSA no padding.  (0x1A is pkcs#1.5 padding.)  */
+      mse[3] = 0x84; /* Private key reference.  */
+      mse[4] = 1;
+      mse[5] = kid;
+      rc = iso7816_manage_security_env (app->slot, 0x41, 0xB8,
+                                        mse, sizeof mse);
+    }
+  else
+    {
+      static const unsigned char mse[] = 
+        {
+          0x80, 1, 0x10, /* Select algorithm RSA. */
+          0x84, 1, 0x81  /* Select local secret key 1 for decryption. */
+        };
+      rc = iso7816_manage_security_env (app->slot, 0xC1, 0xB8,
+                                        mse, sizeof mse);
+
+    }
 
-  /* Do the TCOS specific MSE. */
-  rc = iso7816_manage_security_env (app->slot, 
-                                    0xC1, 0xB8,
-                                    mse_parm, sizeof mse_parm);
   if (!rc)
     rc = verify_pin (app, 0, NULL, pincb, pincb_arg);
+
+  /* Note that we need to use extended length APDUs for TCOS 3 cards.
+     Command chaining does not work.  */
   if (!rc)
-    rc = iso7816_decipher (app->slot, indata, indatalen, 0x81,
+    rc = iso7816_decipher (app->slot, app->app_local->nks_version > 2? 1:0,
+                           indata, indatalen, 0x81,
                            outdata, outdatalen);
   return rc;
 }
index ce09a51..2c89e8c 100644 (file)
@@ -3066,14 +3066,16 @@ do_decipher (app_t app, const char *keyidstr,
             {
               memset (fixbuf, 0, fixuplen);
               memcpy (fixbuf+fixuplen, indata, indatalen);
-              rc = iso7816_decipher (app->slot, fixbuf, fixuplen+indatalen, -1,
+              rc = iso7816_decipher (app->slot, 0,
+                                     fixbuf, fixuplen+indatalen, -1,
                                      outdata, outdatalen);
               xfree (fixbuf);
             }
 
         }
       else
-        rc = iso7816_decipher (app->slot, indata, indatalen, 0,
+        rc = iso7816_decipher (app->slot, 0, 
+                               indata, indatalen, 0,
                                outdata, outdatalen);
     }
   return rc;
index 854aca1..c159b5c 100644 (file)
@@ -243,6 +243,7 @@ struct ccid_driver_s
   int auto_ifsd;
   int max_ifsd;
   int ifsd;
+  int ifsc;
   int powered_off;
   int has_pinpad;
   int apdu_level;     /* Reader supports short APDU level exchange.  */
@@ -1840,7 +1841,6 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
       *nread = msglen = rc;
     }
 
-
   if (msglen < 10)
     {
       DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen);
@@ -1880,7 +1880,6 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
       return CCID_DRIVER_ERR_INV_VALUE;
     }
 
-
   if (debug_level && (!no_debug || debug_level >= 3))
     {
       switch (buffer[0])
@@ -2326,6 +2325,11 @@ ccid_get_atr (ccid_driver_t handle,
   if (rc)
     DEBUGOUT ("SetParameters failed (ignored)\n");
 
+  if (!rc && msglen > 15 && msg[15] >= 16 && msg[15] <= 254 )
+    handle->ifsc = msg[15];
+  else
+    handle->ifsc = 128; /* Something went wrong, assume 128 bytes.  */
+
   handle->t1_ns = 0;
   handle->t1_nr = 0;
 
@@ -2582,22 +2586,15 @@ ccid_transceive (ccid_driver_t handle,
           assert (apdulen);
 
           /* Construct an I-Block. */
-          /* Fixme: I am not sure whether limiting the length to 259
-             as per CCID spec is required.  The code blow chops the
-             APDU anyway into 128 byte blocks.  Needs to be addressed
-             when supporting extended length APDUs. */
-          if (apdulen > 259)
-            return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
-
           tpdu = msg+10;
           /* NAD: DAD=1, SAD=0 */
           tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
           tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */
-          if (apdulen > 128 /* fixme: replace by ifsc */)
+          if (apdulen > handle->ifsc )
             {
-              apdulen = 128;
-              apdu_buf += 128;  
-              apdu_buflen -= 128;
+              apdulen = handle->ifsc;
+              apdu_buf += handle->ifsc;  
+              apdu_buflen -= handle->ifsc;
               tpdu[1] |= (1 << 5); /* Set more bit. */
             }
           tpdu[2] = apdulen;
@@ -2752,8 +2749,31 @@ ccid_transceive (ccid_driver_t handle,
           DEBUGOUT_2 ("T=1 S-block %s received cmd=%d\n",
                       (tpdu[1] & 0x20)? "response": "request",
                       (tpdu[1] & 0x1f));
-          if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2])
-            { /* Wait time extension request. */
+          if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 1 && tpdu[2] == 1)
+            {
+              /* Information field size request.  */
+              unsigned char ifsc = tpdu[3];
+
+              if (ifsc < 16 || ifsc > 254)
+                return CCID_DRIVER_ERR_CARD_IO_ERROR;
+
+              msg = send_buffer;
+              tpdu = msg+10;
+              /* NAD: DAD=1, SAD=0 */
+              tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+              tpdu[1] = (0xc0 | 0x20 | 1); /* S-block response */
+              tpdu[2] = 1;
+              tpdu[3] = ifsc;
+              tpdulen = 4;
+              edc = compute_edc (tpdu, tpdulen, use_crc);
+              if (use_crc)
+                tpdu[tpdulen++] = (edc >> 8);
+              tpdu[tpdulen++] = edc;
+              DEBUGOUT_1 ("T=1 requesting an ifsc=%d\n", ifsc);
+            }
+          else if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2])
+            {
+              /* Wait time extension request. */
               unsigned char bwi = tpdu[3];
               msg = send_buffer;
               tpdu = msg+10;
index 07aed17..71081b2 100644 (file)
@@ -596,7 +596,8 @@ cmd_serialno (assuan_context_t ctx, char *line)
       100 := Regular X.509 cert
       101 := Trusted X.509 cert
       102 := Useful X.509 cert
-      110 := Root CA cert (e.g. DINSIG)
+      110 := Root CA cert in a special format (e.g. DINSIG)
+      111 := Root CA cert as standard X509 cert.
 
    For certain cards, more information will be returned:
 
@@ -963,7 +964,7 @@ cmd_pksign (assuan_context_t ctx, char *line)
   xfree (keyidstr);
   if (rc)
     {
-      log_error ("card_sign failed: %s\n", gpg_strerror (rc));
+      log_error ("app_sign failed: %s\n", gpg_strerror (rc));
     }
   else
     {
@@ -1013,7 +1014,7 @@ cmd_pkauth (assuan_context_t ctx, char *line)
   xfree (keyidstr);
   if (rc)
     {
-      log_error ("app_auth_sign failed: %s\n", gpg_strerror (rc));
+      log_error ("app_auth failed: %s\n", gpg_strerror (rc));
     }
   else
     {
@@ -1057,7 +1058,7 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
   xfree (keyidstr);
   if (rc)
     {
-      log_error ("card_create_signature failed: %s\n", gpg_strerror (rc));
+      log_error ("app_decipher failed: %s\n", gpg_strerror (rc));
     }
   else
     {
@@ -1821,7 +1822,7 @@ cmd_apdu (assuan_context_t ctx, char *line)
       unsigned char *result = NULL;
       size_t resultlen;
 
-      rc = apdu_send_direct (ctrl->reader_slot, apdu, apdulen, handle_more,
+      rc = apdu_send_direct (ctrl->reader_slot, 0, apdu, apdulen, handle_more,
                              &result, &resultlen);
       if (rc)
         log_error ("apdu_send_direct failed: %s\n", gpg_strerror (rc));
index ecb6dc1..d12f918 100644 (file)
@@ -213,7 +213,7 @@ iso7816_list_directory (int slot, int list_dirs,
   *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)
     {
@@ -244,7 +244,7 @@ iso7816_apdu_direct (int slot, const void *apdudata, size_t apdudatalen,
   *result = NULL;
   *resultlen = 0;
 
-  sw = apdu_send_direct (slot, apdudata, apdudatalen, handle_more,
+  sw = apdu_send_direct (slot, 0, apdudata, apdudatalen, handle_more,
                          result, resultlen);
   if (!sw)
     {
@@ -430,7 +430,7 @@ iso7816_get_data (int slot, int tag,
   *result = NULL;
   *resultlen = 0;
 
-  sw = apdu_send (slot, 0x00, CMD_GET_DATA,
+  sw = apdu_send (slot, 0, 0x00, CMD_GET_DATA,
                   ((tag >> 8) & 0xff), (tag & 0xff), -1, NULL,
                   result, resultlen);
   if (sw != SW_SUCCESS)
@@ -462,7 +462,7 @@ iso7816_put_data (int slot, int extended_mode, int tag,
   return map_sw (sw);
 }
 
-/* Same as iso7816_put_data but uses an odd instrcution byte.  */
+/* Same as iso7816_put_data but uses an odd instruction byte.  */
 gpg_error_t
 iso7816_put_data_odd (int slot, int extended_mode, int tag,
                       const unsigned char *data, size_t datalen)
@@ -509,7 +509,8 @@ iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
   *result = NULL;
   *resultlen = 0;
 
-  sw = apdu_send (slot, 0x00, CMD_PSO, 0x9E, 0x9A, datalen, (const char*)data,
+  sw = apdu_send (slot, 0, 
+                  0x00, CMD_PSO, 0x9E, 0x9A, datalen, (const char*)data,
                   result, resultlen);
   if (sw != SW_SUCCESS)
     {
@@ -530,7 +531,8 @@ iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
    and the plaintext is available in a newly allocated buffer stored
    at RESULT with its length stored at RESULTLEN. */
 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 padind, unsigned char **result, size_t *resultlen)
 {
   int sw;
@@ -547,17 +549,19 @@ iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
       buf = xtrymalloc (datalen + 1);
       if (!buf)
         return gpg_error (gpg_err_code_from_errno (errno));
-
+      
       *buf = padind; /* Padding indicator. */
       memcpy (buf+1, data, datalen);
-      sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86,
+      sw = apdu_send (slot, extended_mode, 
+                      0x00, CMD_PSO, 0x80, 0x86,
                       datalen+1, (char*)buf,
                       result, resultlen);
       xfree (buf);
     }
   else
     {
-      sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86,
+      sw = apdu_send (slot, extended_mode,
+                      0x00, CMD_PSO, 0x80, 0x86,
                       datalen, (const char *)data,
                       result, resultlen);
     }
@@ -586,7 +590,7 @@ iso7816_internal_authenticate (int slot,
   *result = NULL;
   *resultlen = 0;
 
-  sw = apdu_send (slot, 0x00, CMD_INTERNAL_AUTHENTICATE, 0, 0,
+  sw = apdu_send (slot, 0, 0x00, CMD_INTERNAL_AUTHENTICATE, 0, 0,
                   datalen, (const char*)data,  result, resultlen);
   if (sw != SW_SUCCESS)
     {
@@ -613,7 +617,8 @@ do_generate_keypair (int slot, int readonly,
   *result = NULL;
   *resultlen = 0;
 
-  sw = apdu_send (slot, 0x00, CMD_GENERATE_KEYPAIR, readonly? 0x81:0x80, 0,
+  sw = apdu_send (slot, 0,
+                  0x00, CMD_GENERATE_KEYPAIR, readonly? 0x81:0x80, 0,
                   datalen, (const char*)data,  result, resultlen);
   if (sw != SW_SUCCESS)
     {
@@ -661,8 +666,8 @@ iso7816_get_challenge (int slot, int length, unsigned char *buffer)
     {
       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)
         {
@@ -711,13 +716,13 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax,
       buffer = NULL;
       bufferlen = 0;
       n = read_all? 0 : nmax;
-      sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY,
+      sw = apdu_send_le (slot, 0, 0x00, CMD_READ_BINARY,
                          ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
                          n, &buffer, &bufferlen);
       if ( SW_EXACT_LENGTH_P(sw) )
         {
           n = (sw & 0x00ff);
-          sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY,
+          sw = apdu_send_le (slot, 0, 0x00, CMD_READ_BINARY,
                              ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
                              n, &buffer, &bufferlen);
         }
@@ -804,7 +809,7 @@ iso7816_read_record (int slot, int recno, int reccount, int short_ef,
 
   buffer = NULL;
   bufferlen = 0;
-  sw = apdu_send_le (slot, 0x00, CMD_READ_RECORD,
+  sw = apdu_send_le (slot, 0, 0x00, CMD_READ_RECORD,
                      recno, 
                      short_ef? short_ef : 0x04,
                      -1, NULL,
index 6c0485d..44e50d7 100644 (file)
@@ -96,7 +96,7 @@ gpg_error_t iso7816_manage_security_env (int slot, int p1, int p2,
 gpg_error_t iso7816_compute_ds (int slot,
                                 const unsigned char *data, size_t datalen,
                                 unsigned char **result, size_t *resultlen);
-gpg_error_t iso7816_decipher (int slot,
+gpg_error_t iso7816_decipher (int slot, int extended_mode,
                               const unsigned char *data, size_t datalen,
                               int padind,
                               unsigned char **result, size_t *resultlen);