Support the Certifciate DO of the v2 OpenPGP cards.
authorWerner Koch <wk@gnupg.org>
Tue, 23 Sep 2008 09:57:45 +0000 (09:57 +0000)
committerWerner Koch <wk@gnupg.org>
Tue, 23 Sep 2008 09:57:45 +0000 (09:57 +0000)
18 files changed:
agent/pksign.c
doc/gpg.texi
g10/ChangeLog
g10/call-agent.c
g10/call-agent.h
g10/card-util.c
scd/ChangeLog
scd/apdu.c
scd/apdu.h
scd/app-common.h
scd/app-openpgp.c
scd/app.c
scd/atr.c
scd/ccid-driver.c
scd/command.c
scd/iso7816.c
scd/iso7816.h
sm/certdump.c

index 817ce6e..2973fb8 100644 (file)
@@ -75,7 +75,7 @@ do_encode_md (const byte * md, size_t mdlen, int algo, gcry_sexp_t * r_hash,
 }
 
 
-/* Special version of do_encode_md to take care of pckcs#1 padding.
+/* Special version of do_encode_md to take care of pkcs#1 padding.
    For TLS-MD5SHA1 we need to do the padding ourself as Libgrypt does
    not know about this special scheme.  Fixme: We should have a
    pkcs1-only-padding flag for Libgcrypt. */
index 2ea851b..4f347d1 100644 (file)
@@ -2709,6 +2709,9 @@ make a clear text signature
 @item gpg -sb @code{file}
 make a detached signature
 
+@item gpg -u 0x12345678 -sb @code{file}
+make a detached signature with the key 0x12345678
+
 @item gpg --list-keys @code{user_ID}
 show keys
 
index 3478257..1263079 100644 (file)
@@ -1,3 +1,14 @@
+2008-09-16  Werner Koch  <wk@g10code.com>
+
+       * card-util.c (fpr_is_ff): New.
+       (card_status): Do not print general key info for an all-ff fpr.
+       (change_login, change_private_do): Factor common code out to ...
+       (get_data_from_file): .. new.
+       (change_cert): New.
+       (card_edit): Add command "writecert".
+       * call-agent.c (writecert_parm_s): New.
+       (inq_writecert_parms, agent_scd_writecert): New.
+
 2008-09-04  David Shaw  <dshaw@jabberwocky.com>
 
        * keyserver.c (keyserver_import_cert): Allow keyserver URLs in
index fa68e61..9767f04 100644 (file)
@@ -53,6 +53,13 @@ struct cipher_parm_s
   size_t ciphertextlen;
 };
 
+struct writecert_parm_s
+{
+  assuan_context_t ctx;
+  const unsigned char *certdata;
+  size_t certdatalen;
+};
+
 struct writekey_parm_s
 {
   assuan_context_t ctx;
@@ -445,6 +452,56 @@ agent_scd_setattr (const char *name,
 
 
 \f
+/* Handle a CERTDATA inquiry.  Note, we only send the data,
+   assuan_transact takes care of flushing and writing the END
+   command. */
+static int
+inq_writecert_parms (void *opaque, const char *line)
+{
+  int rc;
+  struct writecert_parm_s *parm = opaque; 
+
+  if (!strncmp (line, "CERTDATA", 8) && (line[8]==' '||!line[8]))
+    {
+      rc = assuan_send_data (parm->ctx, parm->certdata, parm->certdatalen);
+    }
+  else
+    rc = default_inq_cb (opaque, line);
+
+  return rc;
+}
+
+
+/* Send a WRITECERT command to the SCdaemon. */
+int 
+agent_scd_writecert (const char *certidstr,
+                     const unsigned char *certdata, size_t certdatalen)
+{
+  int rc;
+  char line[ASSUAN_LINELENGTH];
+  struct writecert_parm_s parms;
+
+  rc = start_agent ();
+  if (rc)
+    return rc;
+
+  memset (&parms, 0, sizeof parms);
+
+  snprintf (line, DIM(line)-1, "SCD WRITECERT %s", certidstr);
+  line[DIM(line)-1] = 0;
+  parms.ctx = agent_ctx;
+  parms.certdata = certdata;
+  parms.certdatalen = certdatalen;
+  
+  rc = assuan_transact (agent_ctx, line, NULL, NULL,
+                        inq_writecert_parms, &parms, NULL, NULL);
+
+  return rc;
+}
+
+
+
+\f
 /* Handle a KEYDATA inquiry.  Note, we only send the data,
    assuan_transact takes care of flushing and writing the end */
 static int
index b3e8ae2..63b460a 100644 (file)
@@ -76,6 +76,10 @@ int agent_scd_setattr (const char *name,
                        const unsigned char *value, size_t valuelen,
                        const char *serialno);
 
+/* Send a WRITECERT command to the SCdaemon. */
+int agent_scd_writecert (const char *certidstr,
+                          const unsigned char *certdata, size_t certdatalen);
+
 /* Send a WRITEKEY command to the SCdaemon. */
 int agent_scd_writekey (int keyno, const char *serialno,
                         const unsigned char *keydata, size_t keydatalen);
index 2778bb8..2d00bb3 100644 (file)
@@ -288,6 +288,18 @@ fpr_is_zero (const char *fpr)
 }
 
 
+/* Return true if the SHA1 fingerprint FPR consists only of 0xFF. */
+static int
+fpr_is_ff (const char *fpr)
+{
+  int i;
+
+  for (i=0; i < 20 && fpr[i] == '\xff'; i++)
+    ;
+  return (i == 20);
+}
+
+
 /* Print all available information about the current card. */
 void
 card_status (FILE *fp, char *serialno, size_t serialnobuflen)
@@ -467,7 +479,10 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen)
 
       thefpr = (info.fpr1valid? info.fpr1 : info.fpr2valid? info.fpr2 : 
                 info.fpr3valid? info.fpr3 : NULL);
-      if ( thefpr && !get_pubkey_byfprint (pk, thefpr, 20))
+      /* If the fingerprint is all 0xff, the key has no asssociated
+         OpenPGP certificate.  */
+      if ( thefpr && !fpr_is_ff (thefpr) 
+           && !get_pubkey_byfprint (pk, thefpr, 20))
         {
           KBNODE keyblock = NULL;
 
@@ -655,6 +670,58 @@ fetch_url(void)
 }
 
 
+/* Read data from file FNAME up to MAXLEN characters.  On error return
+   -1 and store NULl at R_BUFFER; on success return the number of
+   bytes read and store the address of a newly allocated buffer at
+   R_BUFFER. */
+static int
+get_data_from_file (const char *fname, size_t maxlen, char **r_buffer)
+{
+  FILE *fp;
+  char *data;
+  int n;
+  
+  *r_buffer = NULL;
+
+  fp = fopen (fname, "rb");
+#if GNUPG_MAJOR_VERSION == 1
+  if (fp && is_secured_file (fileno (fp)))
+    {
+      fclose (fp);
+      fp = NULL;
+      errno = EPERM;
+    }
+#endif
+  if (!fp)
+    {
+      tty_printf (_("can't open `%s': %s\n"), fname, strerror (errno));
+      return -1;
+    }
+          
+  data = xtrymalloc (maxlen? maxlen:1);
+  if (!data)
+    {
+      tty_printf (_("error allocating enough memory: %s\n"), strerror (errno));
+      fclose (fp);
+      return -1;
+    }
+
+  if (maxlen)
+    n = fread (data, 1, maxlen, fp);
+  else
+    n = 0;
+  fclose (fp);
+  if (n < 0)
+    {
+      tty_printf (_("error reading `%s': %s\n"), fname, strerror (errno));
+      xfree (data);
+      return -1;
+    }
+  *r_buffer = data;
+  return n;
+}
+
+
 static int
 change_login (const char *args)
 {
@@ -664,34 +731,11 @@ change_login (const char *args)
 
   if (args && *args == '<')  /* Read it from a file */
     {
-      FILE *fp;
-
       for (args++; spacep (args); args++)
         ;
-      fp = fopen (args, "rb");
-#if GNUPG_MAJOR_VERSION == 1
-      if (fp && is_secured_file (fileno (fp)))
-        {
-          fclose (fp);
-          fp = NULL;
-          errno = EPERM;
-        }
-#endif
-      if (!fp)
-        {
-          tty_printf (_("can't open `%s': %s\n"), args, strerror (errno));
-          return -1;
-        }
-          
-      data = xmalloc (254);
-      n = fread (data, 1, 254, fp);
-      fclose (fp);
+      n = get_data_from_file (args, 254, &data);
       if (n < 0)
-        {
-          tty_printf (_("error reading `%s': %s\n"), args, strerror (errno));
-          xfree (data);
-          return -1;
-        }
+        return -1;
     }
   else
     {
@@ -732,35 +776,11 @@ change_private_do (const char *args, int nr)
 
   if (args && (args = strchr (args, '<')))  /* Read it from a file */
     {
-      FILE *fp;
-
-      /* Fixme: Factor this duplicated code out. */
       for (args++; spacep (args); args++)
         ;
-      fp = fopen (args, "rb");
-#if GNUPG_MAJOR_VERSION == 1
-      if (fp && is_secured_file (fileno (fp)))
-        {
-          fclose (fp);
-          fp = NULL;
-          errno = EPERM;
-        }
-#endif
-      if (!fp)
-        {
-          tty_printf (_("can't open `%s': %s\n"), args, strerror (errno));
-          return -1;
-        }
-          
-      data = xmalloc (254);
-      n = fread (data, 1, 254, fp);
-      fclose (fp);
+      n = get_data_from_file (args, 254, &data);
       if (n < 0)
-        {
-          tty_printf (_("error reading `%s': %s\n"), args, strerror (errno));
-          xfree (data);
-          return -1;
-        }
+        return -1;
     }
   else
     {
@@ -788,6 +808,36 @@ change_private_do (const char *args, int nr)
   return rc;
 }
 
+
+static int
+change_cert (const char *args)
+{
+  char *data;
+  int n;
+  int rc;
+
+  if (args && *args == '<')  /* Read it from a file */
+    {
+      for (args++; spacep (args); args++)
+        ;
+      n = get_data_from_file (args, 16384, &data);
+      if (n < 0)
+        return -1;
+    }
+  else
+    {
+      tty_printf ("usage error: redirectrion to file required\n");
+      return -1;
+    }
+
+  rc = agent_scd_writecert ("OPENPGP.3", data, n);
+  if (rc)
+    log_error ("error writing certificate to card: %s\n", gpg_strerror (rc));
+  xfree (data);
+  return rc;
+}
+
+
 static int
 change_lang (void)
 {
@@ -1294,7 +1344,7 @@ enum cmdids
     cmdNOP = 0,
     cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
     cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
-    cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO,
+    cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
     cmdINVCMD
   };
 
@@ -1325,8 +1375,9 @@ static struct
     { "generate", cmdGENERATE, 1, N_("generate new keys")},
     { "passwd"  , cmdPASSWD, 0, N_("menu to change or unblock the PIN")},
     { "verify"  , cmdVERIFY, 0, N_("verify the PIN and list all data")},
-    /* Note, that we do not announce this command yet. */
+    /* Note, that we do not announce these command yet. */
     { "privatedo", cmdPRIVATEDO, 0, NULL },
+    { "writecert", cmdWRITECERT, 1, NULL },
     { NULL, cmdINVCMD, 0, NULL } 
   };
 
@@ -1401,6 +1452,7 @@ card_edit (strlist_t commands)
     {
       int arg_number;
       const char *arg_string = "";
+      const char *arg_rest = "";
       char *p;
       int i;
       int cmd_admin_only;
@@ -1469,6 +1521,11 @@ card_edit (strlist_t commands)
               trim_spaces (p);
               arg_number = atoi(p);
               arg_string = p;
+              arg_rest = p;
+              while (digitp (arg_rest))
+                arg_rest++;
+              while (spacep (arg_rest))
+                arg_rest++;
             }
           
           for (i=0; cmds[i].name; i++ )
@@ -1567,6 +1624,13 @@ card_edit (strlist_t commands)
             change_private_do (arg_string, arg_number);
           break;
 
+        case cmdWRITECERT:
+          if ( arg_number != 3 )
+            tty_printf ("usage: writecert 3 < FILE\n");
+          else
+            change_cert (arg_rest);
+          break;
+
         case cmdFORCESIG:
           toggle_forcesig ();
           break;
index ee34054..d9ad2bf 100644 (file)
@@ -1,3 +1,29 @@
+2008-09-23  Werner Koch  <wk@g10code.com>
+
+       * app-openpgp.c (do_setattr): Use command chaining for long
+       values.
+       * iso7816.c (iso7816_put_data): Add arg EXTENDED_MODE. Change all
+       callers.
+       * apdu.c (apdu_send_simple): Add arg EXTENDED_MODE. Change all
+       callers.
+       (send_le): Implement command chaining.
+       * ccid-driver.c (ccid_transceive_apdu_level): Increase allowed
+       APDU size.
+       * apdu.h: Add new SW_ codes.
+
+2008-09-16  Werner Koch  <wk@g10code.com>
+
+       * command.c (cmd_writecert): New.
+       (register_commands): Register it.
+       * app-common.h (app_ctx_s): Add member WRITECERT.
+       * app.c (app_writecert): New.
+       * app-openpgp.c (do_writecert): New.
+       (parse_historical): New.
+       (show_extcap): New.
+       (dump_all_do): Print only the length of longs DOs.
+       * command.c (cmd_writekey, cmd_apdu, cmd_pksign)
+       (cmd_passwd): Replace open coding by skip_options.
+
 2008-08-30  Moritz  <moritz@gnu.org>
 
        * scdaemon.c (main): Use estream_asprintf instead of asprintf.
index 2aa584b..171c7a6 100644 (file)
@@ -1,5 +1,5 @@
 /* apdu.c - ISO 7816 APDU functions and low level I/O
- *     Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004, 2008 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -1182,7 +1182,7 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
        err = SW_HOST_INV_VALUE;
      }
    /* We need to read any rest of the response, to keep the
-      protocol runnng. */
+      protocol running.  */
    while (full_len)
      {
        unsigned char dummybuf[128];
@@ -2587,20 +2587,29 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen,
   if (reader_table[slot].send_apdu_reader)
     return reader_table[slot].send_apdu_reader (slot,
                                                 apdu, apdulen,
-                                                buffer, buflen, pininfo);
+                                                buffer, buflen, 
+                                                pininfo);
   else
     return SW_HOST_NOT_SUPPORTED;
 }
 
 
-/* Core APDU trabceiver function. Parameters are described at
+/* Core APDU tranceiver function. Parameters are described at
    apdu_send_le with the exception of PININFO which indicates keypad
-   related operations if not NULL. */
+   related operations if not NULL.  If EXTENDED_MODE is not NULL
+   command chaining or extended length will be used according to these
+   values:
+       n < 0 := Use command chaining without the data part limited to -n
+                in each chunk.  If -1 is used a default value is used.
+      n == 1 := Use extended length for input and output with out a
+                length limit.
+       n > 1 := Use extended length with up to N bytes.
+*/
 static int
 send_le (int slot, int class, int ins, int p0, int p1,
          int lc, const char *data, int le,
          unsigned char **retbuf, size_t *retbuflen,
-         struct pininfo_s *pininfo)
+         struct pininfo_s *pininfo, int extended_mode)
 {
 #define RESULTLEN 258
   unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
@@ -2611,16 +2620,37 @@ send_le (int slot, int class, int ins, int p0, int p1,
   int sw;
   long rc; /* We need a long here due to PC/SC. */
   int did_exact_length_hack = 0;
+  int use_chaining = 0;
+  int lc_chunk;
 
   if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
     return SW_HOST_NO_DRIVER;
 
   if (DBG_CARD_IO)
-    log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d\n",
-               class, ins, p0, p1, lc, le);
+    log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d em=%d\n",
+               class, ins, p0, p1, lc, le, extended_mode);
 
   if (lc != -1 && (lc > 255 || lc < 0))
-    return SW_WRONG_LENGTH;
+    {
+      /* Data does not fit into an APDU.  What we do now dependes on
+         the EXTENDED_MODE parameter.  */
+      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.  */
+      else if (extended_mode < 0)
+        {
+          /* Send APDU using chaining mode.  */
+          if (lc > 16384)
+            return SW_WRONG_LENGTH;   /* Sanity check.  */
+          if ((class&0xf0) != 0)
+            return SW_HOST_INV_VALUE; /* Upper 4 bits need to be 0.  */ 
+          use_chaining = extended_mode == -1? 255 : -extended_mode;  
+          use_chaining &= 0xff;
+        }
+      else 
+        return SW_HOST_INV_VALUE;
+    }
   if (le != -1 && (le > 256 || le < 0))
     return SW_WRONG_LENGTH;
   if ((!data && lc != -1) || (data && lc == -1))
@@ -2629,43 +2659,61 @@ send_le (int slot, int class, int ins, int p0, int p1,
   if ((sw = lock_slot (slot)))
     return sw;
 
-  apdulen = 0;
-  apdu[apdulen++] = class;
-  apdu[apdulen++] = ins;
-  apdu[apdulen++] = p0;
-  apdu[apdulen++] = p1;
-  if (lc != -1)
-    {
-      apdu[apdulen++] = lc;
-      memcpy (apdu+apdulen, data, lc);
-      apdulen += lc;
-      /* 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)
-    apdu[apdulen++] = le; /* Truncation is okay because 0 means 256. */
-  assert (sizeof (apdu) >= apdulen);
-  /* As safeguard don't pass any garbage from the stack to the driver. */
-  memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
- exact_length_hack:
-  resultlen = RESULTLEN;
-  rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo);
-  if (rc || resultlen < 2)
+  do
     {
-      log_error ("apdu_send_simple(%d) failed: %s\n",
-                 slot, apdu_strerror (rc));
-      unlock_slot (slot);
-      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))
-    {
-      apdu[apdulen-1] = (sw & 0x00ff);
-      did_exact_length_hack = 1;
-      goto exact_length_hack;
+      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)
+        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:
+      resultlen = RESULTLEN;
+      rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo);
+      if (rc || resultlen < 2)
+        {
+          log_error ("apdu_send_simple(%d) failed: %s\n",
+                     slot, apdu_strerror (rc));
+          unlock_slot (slot);
+          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))
+        {
+          apdu[apdulen-1] = (sw & 0x00ff);
+          did_exact_length_hack = 1;
+          goto exact_length_hack;
+        }
     }
+  while (use_chaining && sw == SW_SUCCESS);
 
   /* Store away the returned data but strip the statusword. */
   resultlen -= 2;
@@ -2808,7 +2856,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
   return send_le (slot, class, ins, p0, p1,
                   lc, data, le,
                   retbuf, retbuflen,
-                  NULL);
+                  NULL, 0);
 }
 
 
@@ -2826,7 +2874,7 @@ apdu_send (int slot, 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);
+                  retbuf, retbuflen, NULL, 0);
 }
 
 /* Send an APDU to the card in SLOT.  The APDU is created from all
@@ -2836,10 +2884,12 @@ apdu_send (int slot, int class, int ins, int p0, int p1,
    for an invalid SLOT or other non card related error.  No data will be
    returned. */
 int
-apdu_send_simple (int slot, int class, int ins, int p0, int p1,
+apdu_send_simple (int slot, int extended_mode,
+                  int class, int ins, int p0, int p1,
                   int lc, const char *data)
 {
-  return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL);
+  return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL, 
+                  extended_mode);
 }
 
 
@@ -2857,13 +2907,13 @@ apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
   pininfo.maxlen = pinlen_max;
   pininfo.padlen = pin_padlen;
   return send_le (slot, class, ins, p0, p1, lc, data, -1,
-                  NULL, NULL, &pininfo);
+                  NULL, NULL, &pininfo, 0);
 }
 
 
 /* This is a more generic version of the apdu sending routine.  It
    takes an already formatted APDU in APDUDATA or length APDUDATALEN
-   and returns the with the APDU including the status word.  With
+   and returns the 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
index 92d0738..361dfde 100644 (file)
@@ -1,5 +1,5 @@
 /* apdu.h - ISO 7816 APDU functions and low level I/O
- *     Copyright (C) 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2008 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -29,8 +29,11 @@ enum {
   SW_MORE_DATA      = 0x6100, /* Note: that the low byte must be
                                  masked of.*/
   SW_EOF_REACHED    = 0x6282,
+  SW_TERM_STATE     = 0x6285, /* Selected file is in termination state.  */
   SW_EEPROM_FAILURE = 0x6581,
   SW_WRONG_LENGTH   = 0x6700,
+  SW_SM_NOT_SUP     = 0x6882, /* Secure Messaging is not supported.  */
+  SW_CC_NOT_SUP     = 0x6884, /* Command Chaining is not supported.  */
   SW_CHV_WRONG      = 0x6982,
   SW_CHV_BLOCKED    = 0x6983,
   SW_USE_CONDITIONS = 0x6985,
@@ -97,7 +100,8 @@ int apdu_get_status (int slot, int hang,
                      unsigned int *status, unsigned int *changed);
 int apdu_check_keypad (int slot, int command, int pin_mode,
                        int pinlen_min, int pinlen_max, int pin_padlen);
-int apdu_send_simple (int slot, int class, int ins, int p0, int p1,
+int apdu_send_simple (int slot, int extended_mode,
+                      int class, int ins, int p0, int p1,
                       int lc, const char *data);
 int apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
                          int lc, const char *data,  
index dc1581e..96fbb92 100644 (file)
@@ -1,5 +1,5 @@
 /* app-common.h - Common declarations for all card applications
- *     Copyright (C) 2003, 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2005, 2008 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -94,8 +94,13 @@ struct app_ctx_s {
                      void *pincb_arg,
                      const void *indata, size_t indatalen,
                      unsigned char **outdata, size_t *outdatalen);
+    gpg_error_t (*writecert) (app_t app, ctrl_t ctrl,
+                              const char *certid,
+                              gpg_error_t (*pincb)(void*,const char *,char **),
+                              void *pincb_arg,
+                              const unsigned char *data, size_t datalen);
     gpg_error_t (*writekey) (app_t app, ctrl_t ctrl,
-                             const char *certid, unsigned int flags,
+                             const char *keyid, unsigned int flags,
                              gpg_error_t (*pincb)(void*,const char *,char **),
                              void *pincb_arg,
                              const unsigned char *pk, size_t pklen);
@@ -165,6 +170,11 @@ gpg_error_t app_decipher (app_t app, const char *keyidstr,
                   void *pincb_arg,
                   const void *indata, size_t indatalen,
                   unsigned char **outdata, size_t *outdatalen );
+gpg_error_t app_writecert (app_t app, ctrl_t ctrl,
+                           const char *certidstr,
+                           gpg_error_t (*pincb)(void*, const char *, char **),
+                           void *pincb_arg,
+                           const unsigned char *keydata, size_t keydatalen);
 gpg_error_t app_writekey (app_t app, ctrl_t ctrl,
                           const char *keyidstr, unsigned int flags,
                           gpg_error_t (*pincb)(void*, const char *, char **),
index 1750668..e2140f2 100644 (file)
@@ -63,6 +63,7 @@ static struct {
 } data_objects[] = {
   { 0x005E, 0,    0, 1, 0, 0, 0, "Login Data" },
   { 0x5F50, 0,    0, 0, 0, 0, 0, "URL" },
+  { 0x5F52, 0,    0, 1, 0, 0, 0, "Historical Bytes" },
   { 0x0065, 1,    0, 1, 0, 0, 0, "Cardholder Related Data"},
   { 0x005B, 0, 0x65, 0, 0, 0, 0, "Name" },
   { 0x5F2D, 0, 0x65, 0, 0, 0, 0, "Language preferences" },
@@ -118,7 +119,16 @@ struct app_local_s {
                            implicitly available.  */
   } pk[3];
 
-  /* Keep track of card capabilities.  */
+  unsigned char status_indicator; /* The card status indicator.  */
+
+  /* Keep track of the ISO card capabilities.  */
+  struct
+  {
+    unsigned int cmd_chaining:1;  /* Command chaining is supported.  */
+    unsigned int ext_lc_le:1;     /* Extended Lc and Le are supported.  */
+  } cardcap;
+
+  /* Keep track of extended card capabilities.  */
   struct 
   {
     unsigned int is_v2:1;  /* This is a v2.0 compatible card.  */
@@ -126,7 +136,12 @@ struct app_local_s {
     unsigned int key_import:1;
     unsigned int change_force_chv:1;
     unsigned int private_dos:1;
+    unsigned int sm_supported:1;  /* Secure Messaging is supported.  */
+    unsigned int sm_aes128:1;     /* Use AES-128 for SM.  */
     unsigned int max_certlen_3:16;
+    unsigned int max_get_challenge:16; /* Maximum size for get_challenge.  */
+    unsigned int max_cmd_data:16;      /* Maximum data size for a command.  */
+    unsigned int max_rsp_data:16;      /* Maximum size of a response.  */
   } extcap;
 
   /* Flags used to control the application.  */
@@ -451,7 +466,10 @@ dump_all_do (int slot)
                       if (data_objects[j].binary)
                         {
                           log_info ("DO `%s': ", data_objects[j].desc);
-                          log_printhex ("", value, valuelen);
+                          if (valuelen > 200)
+                            log_info ("[%u]\n", (unsigned int)valuelen);
+                          else
+                            log_printhex ("", value, valuelen);
                         }
                       else
                         log_info ("DO `%s': `%.*s'\n",
@@ -596,8 +614,9 @@ store_fpr (int slot, int keynumber, u32 timestamp,
 
   xfree (buffer);
 
-  rc = iso7816_put_data (slot, (card_version > 0x0007? 0xC7 : 0xC6)
-                               + keynumber, fpr, 20);
+  rc = iso7816_put_data (slot, 0, 
+                         (card_version > 0x0007? 0xC7 : 0xC6)
+                         + keynumber, fpr, 20);
   if (rc)
     log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc));
 
@@ -610,7 +629,7 @@ store_fpr (int slot, int keynumber, u32 timestamp,
       buf[2] = timestamp >>  8;
       buf[3] = timestamp;
 
-      rc = iso7816_put_data (slot, 0xCE + keynumber, buf, 4);
+      rc = iso7816_put_data (slot, 0, 0xCE + keynumber, buf, 4);
       if (rc)
         log_error (_("failed to store the creation date: %s\n"),
                    gpg_strerror (rc));
@@ -1278,10 +1297,10 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
 #endif
 }
 
-/* Read the statdard certificate of an OpenPGP v2 card.  It is
+/* Read the standard certificate of an OpenPGP v2 card.  It is
    returned in a freshly allocated buffer with that address stored at
    CERT and the length of the certificate stored at CERTLEN.  CERTID
-   needs to be set to "OpenPGP.3".  */
+   needs to be set to "OPENPGP.3".  */
 static gpg_error_t
 do_readcert (app_t app, const char *certid,
              unsigned char **cert, size_t *certlen)
@@ -1296,10 +1315,10 @@ do_readcert (app_t app, const char *certid,
   *certlen = 0;
   if (strcmp (certid, "OPENPGP.3"))
     return gpg_error (GPG_ERR_INV_ID);
-  if (app->app_local->extcap.is_v2)
+  if (!app->app_local->extcap.is_v2)
     return gpg_error (GPG_ERR_NOT_FOUND);
 
-  relptr = get_one_do (app, 0x00C4, &buffer, &buflen, NULL);
+  relptr = get_one_do (app, 0x7F21, &buffer, &buflen, NULL);
   if (!relptr)
     return gpg_error (GPG_ERR_NOT_FOUND);
 
@@ -1649,15 +1668,18 @@ do_setattr (app_t app, const char *name,
     { "PRIVATE-DO-3", 0x0103, 2 },
     { "PRIVATE-DO-4", 0x0104, 3 },
     { "CERT-3",       0x7F21, 3, 0, 1 },
+    { "SM-KEY-ENC",   0x00D1, 3, 0, 1 },
+    { "SM-KEY-MAC",   0x00D2, 3, 0, 1 },
+    { "PW-RESET-CODE",0x00D3, 3, 0, 1 },
     { NULL, 0 }
   };
-
+  int exmode;
 
   for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++)
     ;
   if (!table[idx].name)
     return gpg_error (GPG_ERR_INV_NAME); 
-  if (table[idx].need_v2)
+  if (table[idx].need_v2 && !app->app_local->extcap.is_v2)
     return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Not yet supported.  */
 
   switch (table[idx].need_chv)
@@ -1678,7 +1700,12 @@ do_setattr (app_t app, const char *name,
      will reread the data from the card and thus get synced in case of
      errors (e.g. data truncated by the card). */
   flush_cache_item (app, table[idx].tag);
-  rc = iso7816_put_data (app->slot, table[idx].tag, value, valuelen);
+  /* For command chaining we use a value of 254 for this card.  */
+  if (app->app_local->cardcap.cmd_chaining && valuelen > 254)
+    exmode = -254;
+  else
+    exmode = 0;
+  rc = iso7816_put_data (app->slot, exmode, table[idx].tag, value, valuelen);
   if (rc)
     log_error ("failed to set `%s': %s\n", table[idx].name, gpg_strerror (rc));
 
@@ -1691,6 +1718,34 @@ do_setattr (app_t app, const char *name,
 }
 
 
+/* Handle the WRITECERT command for OpenPGP.  This rites the standard
+   certifciate to the card; CERTID needs to be set to "OPENPGP.3".
+   PINCB and PINCB_ARG are the usual arguments for the pinentry
+   callback.  */
+static gpg_error_t
+do_writecert (app_t app, ctrl_t ctrl,
+              const char *certidstr, 
+              gpg_error_t (*pincb)(void*, const char *, char **),
+              void *pincb_arg,
+              const unsigned char *certdata, size_t certdatalen)
+{
+#if GNUPG_MAJOR_VERSION > 1
+  if (strcmp (certidstr, "OPENPGP.3"))
+    return gpg_error (GPG_ERR_INV_ID);
+  if (!certdata || !certdatalen)
+    return gpg_error (GPG_ERR_INV_ARG);
+  if (!app->app_local->extcap.is_v2)
+    return gpg_error (GPG_ERR_NOT_SUPPORTED);
+  if (certdatalen > app->app_local->extcap.max_certlen_3)
+    return gpg_error (GPG_ERR_TOO_LARGE);
+  return do_setattr (app, "CERT-3", pincb, pincb_arg, certdata, certdatalen);
+#else
+  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+#endif
+}
+
+
+
 /* Handle the PASSWD command. */
 static gpg_error_t 
 do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr, 
@@ -2074,7 +2129,7 @@ do_writekey (app_t app, ctrl_t ctrl,
     goto leave;
 
   /* Store the key. */
-  err = iso7816_put_data (app->slot,
+  err = iso7816_put_data (app->slot, 0,
                          (app->card_version > 0x0007? 0xE0 : 0xE9) + keyno,
                          template, template_len);
   if (err)
@@ -2711,6 +2766,80 @@ do_check_pin (app_t app, const char *keyidstr,
 }
 
 
+/* Show information about card capabilities.  */
+static void
+show_caps (struct app_local_s *s)
+{
+  log_info ("Version-2 ......: %s\n", s->extcap.is_v2? "yes":"no");
+  log_info ("Get-Challenge ..: %s", s->extcap.get_challenge? "yes":"no");
+  if (s->extcap.get_challenge)
+    log_printf (" (%u bytes max)", s->extcap.max_get_challenge);
+  log_info ("Key-Import .....: %s\n", s->extcap.key_import? "yes":"no");
+  log_info ("Change-Force-PW1: %s\n", s->extcap.change_force_chv? "yes":"no");
+  log_info ("Private-DOs ....: %s\n", s->extcap.private_dos? "yes":"no");
+  log_info ("SM-Support .....: %s", s->extcap.sm_supported? "yes":"no");
+  if (s->extcap.sm_supported)
+    log_printf (" (%s)", s->extcap.sm_aes128? "AES-128":"3DES");
+  log_info ("Max-Cert3-Len ..: %u\n", s->extcap.max_certlen_3);
+  log_info ("Max-Cmd-Data ...: %u\n", s->extcap.max_cmd_data);
+  log_info ("Max-Rsp-Data ...: %u\n", s->extcap.max_rsp_data);
+  log_info ("Cmd-Chaining ...: %s\n", s->cardcap.cmd_chaining?"yes":"no");
+  log_info ("Ext-Lc-Le ......: %s\n", s->cardcap.ext_lc_le?"yes":"no");
+  log_info ("Status Indicator: %02X\n", s->status_indicator);
+
+  log_info ("GnuPG-No-Sync ..: %s\n",  s->flags.no_sync? "yes":"no");
+  log_info ("GnuPG-Def-PW2 ..: %s\n",  s->flags.def_chv2? "yes":"no");
+}
+
+
+/* Parse the historical bytes in BUFFER of BUFLEN and store them in
+   APPLOC.  */
+static void
+parse_historical (struct app_local_s *apploc, 
+                  const unsigned char * buffer, size_t buflen)
+{
+  /* Example buffer: 00 31 C5 73 C0 01 80 00 90 00  */
+  if (buflen < 4)
+    {
+      log_error ("warning: historical bytes are too short\n");
+      return; /* Too short.  */
+    }
+  if (*buffer)
+    {
+      log_error ("warning: bad category indicator in historical bytes\n");
+      return; 
+    }
+  
+  /* Skip category indicator.  */
+  buffer++;
+  buflen--;
+
+  /* Get the status indicator.  */
+  apploc->status_indicator = buffer[buflen-3];
+  buflen -= 3;
+
+  /* Parse the compact TLV.  */
+  while (buflen)
+    {
+      unsigned int tag = (*buffer & 0xf0) >> 4;
+      unsigned int len = (*buffer & 0x0f);
+      if (len+1 > buflen)
+        {
+          log_error ("warning: bad Compact-TLV in historical bytes\n");
+          return; /* Error.  */
+        }
+      buffer++;
+      buflen--;
+      if (tag == 7 && len == 3)
+        {
+          /* Card capabilities.  */
+          apploc->cardcap.cmd_chaining = !!(buffer[2] & 0x80);
+          apploc->cardcap.ext_lc_le    = !!(buffer[2] & 0x40);
+        }
+      buffer += len;
+      buflen -= len;
+    }
+}
 
 
 /* Select the OpenPGP application on the card in SLOT.  This function
@@ -2771,6 +2900,22 @@ app_select_openpgp (app_t app)
       if (app->card_version >= 0x0200)
         app->app_local->extcap.is_v2 = 1;
 
+
+      /* Read the historical bytes.  */
+      relptr = get_one_do (app, 0x5f52, &buffer, &buflen, NULL);
+      if (relptr)
+        {
+          if (opt.verbose)
+            {
+              log_info ("Historical Bytes: ");
+              log_printhex ("", buffer, buflen);
+            }
+          parse_historical (app->app_local, buffer, buflen);
+          xfree (relptr);
+        }
+
+
+      /* Read the force-chv1 flag.  */
       relptr = get_one_do (app, 0x00C4, &buffer, &buflen, NULL);
       if (!relptr)
         {
@@ -2781,6 +2926,7 @@ app_select_openpgp (app_t app)
       app->force_chv1 = (buflen && *buffer == 0);
       xfree (relptr);
 
+      /* Read the extended capabilities.  */
       relptr = get_one_do (app, 0x00C0, &buffer, &buflen, NULL);
       if (!relptr)
         {
@@ -2790,6 +2936,7 @@ app_select_openpgp (app_t app)
         }
       if (buflen)
         {
+          app->app_local->extcap.sm_supported     = !!(*buffer & 0x80);
           app->app_local->extcap.get_challenge    = !!(*buffer & 0x40);
           app->app_local->extcap.key_import       = !!(*buffer & 0x20);
           app->app_local->extcap.change_force_chv = !!(*buffer & 0x10);
@@ -2798,7 +2945,12 @@ app_select_openpgp (app_t app)
       if (buflen >= 10)
         {
           /* Available with v2 cards.  */
+          app->app_local->extcap.sm_aes128     = (buffer[1] == 1);
+          app->app_local->extcap.max_get_challenge 
+                                               = (buffer[2] << 8 | buffer[3]);
           app->app_local->extcap.max_certlen_3 = (buffer[4] << 8 | buffer[5]);
+          app->app_local->extcap.max_cmd_data  = (buffer[6] << 8 | buffer[7]);
+          app->app_local->extcap.max_rsp_data  = (buffer[8] << 8 | buffer[9]);
         }
       xfree (relptr);
       
@@ -2809,6 +2961,9 @@ app_select_openpgp (app_t app)
 
       parse_login_data (app);
 
+      if (opt.verbose)
+        show_caps (app->app_local);
+
       if (opt.verbose > 1)
         dump_all_do (slot);
 
@@ -2818,6 +2973,7 @@ app_select_openpgp (app_t app)
       app->fnc.readkey = do_readkey;
       app->fnc.getattr = do_getattr;
       app->fnc.setattr = do_setattr;
+      app->fnc.writecert = do_writecert;
       app->fnc.writekey = do_writekey;
       app->fnc.genkey = do_genkey;
       app->fnc.sign = do_sign;
index 8cb066f..d2732be 100644 (file)
--- a/scd/app.c
+++ b/scd/app.c
@@ -240,8 +240,8 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app)
 
   /* If we don't have an app, check whether we have a saved
      application for that slot.  This is useful so that a card does
-     not get reset even if only one session is using the card - so the
-     PIN cache and other cached data are preserved. */
+     not get reset even if only one session is using the card - this
+     way the PIN cache and other cached data are preserved.  */
   if (!app && lock_table[slot].initialized && lock_table[slot].last_app)
     {
       app = lock_table[slot].last_app;
@@ -734,6 +734,34 @@ app_decipher (app_t app, const char *keyidstr,
 }
 
 
+/* Perform the WRITECERT operation.  */
+gpg_error_t
+app_writecert (app_t app, ctrl_t ctrl,
+              const char *certidstr,
+              gpg_error_t (*pincb)(void*, const char *, char **),
+              void *pincb_arg,
+              const unsigned char *data, size_t datalen)
+{
+  gpg_error_t err;
+
+  if (!app || !certidstr || !*certidstr || !pincb)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  if (!app->initialized)
+    return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+  if (!app->fnc.writecert)
+    return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+  err = lock_reader (app->slot);
+  if (err)
+    return err;
+  err = app->fnc.writecert (app, ctrl, certidstr,
+                            pincb, pincb_arg, data, datalen);
+  unlock_reader (app->slot);
+  if (opt.verbose)
+    log_info ("operation writecert result: %s\n", gpg_strerror (err));
+  return err;
+}
+
+
 /* Perform the WRITEKEY operation.  */
 gpg_error_t
 app_writekey (app_t app, ctrl_t ctrl,
@@ -759,7 +787,6 @@ app_writekey (app_t app, ctrl_t ctrl,
   if (opt.verbose)
     log_info ("operation writekey result: %s\n", gpg_strerror (err));
   return err;
-
 }
 
 
index cb6b05a..67a86d0 100644 (file)
--- a/scd/atr.c
+++ b/scd/atr.c
@@ -277,10 +277,121 @@ atr_dump (int slot, FILE *fp)
 }
 
 
+/* Note: This code has not yet been tested!  It shall return -1 on
+   error or the nu,ber of hiostroical bytes and store them at
+   HISTORICAL.  */
+int
+atr_get_historical (int slot, unsigned char historical[])
+{
+  int result = -1;
+  unsigned char *atrbuffer = NULL;
+  unsigned char *atr;
+  size_t atrlen;
+  int have_ta, have_tb, have_tc, have_td;
+  int n_historical;
+  int idx;
+  unsigned char chksum;
+
+  atr = atrbuffer = apdu_get_atr (slot, &atrlen);
+  if (!atr || atrlen < 2)
+    goto leave;
+  atrlen--;
+  atr++;
+
+  chksum = *atr;
+  for (idx=1; idx < atrlen-1; idx++)
+    chksum ^= atr[idx];
+
+  have_ta = !!(*atr & 0x10);
+  have_tb = !!(*atr & 0x20);
+  have_tc = !!(*atr & 0x40);
+  have_td = !!(*atr & 0x80);
+  n_historical = (*atr & 0x0f);
+
+  if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen)
+    goto leave; /* ATR shorter than indicated by format character.  */
+  atrlen--;
+  atr++;
+
+  if (have_ta + have_tb + have_tc >= atrlen)
+    goto leave;
+  atrlen -= have_ta + have_tb + have_tc;
+  atr    += have_ta + have_tb + have_tc;
+
+  if (have_td)
+    {
+      have_ta = !!(*atr & 0x10);
+      have_tb = !!(*atr & 0x20);
+      have_tc = !!(*atr & 0x40);
+      have_td = !!(*atr & 0x80);
+      if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen)
+        goto leave; /* ATR shorter than indicated by format character.  */
+      atrlen--;
+      atr++;
+    }
+  else
+    have_ta = have_tb = have_tc = have_td = 0;
+
+  if (have_ta + have_tb + have_tc >= atrlen)
+    goto leave;
+  atrlen -= have_ta + have_tb + have_tc;
+  atr    += have_ta + have_tb + have_tc;
+
+  if (have_td)
+    {
+      have_ta = !!(*atr & 0x10);
+      have_tb = !!(*atr & 0x20);
+      have_tc = !!(*atr & 0x40);
+      have_td = !!(*atr & 0x80);
+      if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen)
+        goto leave; /* ATR shorter than indicated by format character.  */
+      atrlen--;
+      atr++;
+    }
+  else
+    have_ta = have_tb = have_tc = have_td = 0;
+
+  for (idx = 3; have_ta || have_tb || have_tc || have_td; idx++)
+    {
+      if (have_ta + have_tb + have_tc >= atrlen)
+        goto leave;
+      atrlen -= have_ta + have_tb + have_tc;
+      atr    += have_ta + have_tb + have_tc;
+
+      if (have_td)
+        {
+          have_ta = !!(*atr & 0x10);
+          have_tb = !!(*atr & 0x20);
+          have_tc = !!(*atr & 0x40);
+          have_td = !!(*atr & 0x80);
+          if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen)
+            goto leave; /* ATR shorter than indicated by format character.  */
+          atrlen--;
+          atr++;
+        }
+      else
+        have_ta = have_tb = have_tc = have_td = 0;
+    }
 
+  if (n_historical >= atrlen)
+    goto leave; /* ATR shorter than required for historical bytes. */
+  
+  if (n_historical)
+    {
+      for (idx=0; n_historical && atrlen; n_historical--, atrlen--, atr++)
+        historical[idx] = *atr;
+    }
 
+  if (!atrlen || *atr != chksum)
+    goto leave;
 
+  /* Don't care about garbage at the end of the ATR.  */
 
+  result = n_historical;
 
+ leave:
+  xfree (atrbuffer);
 
+  return result;
+}
 
index 79a42a4..899f7ae 100644 (file)
@@ -1957,7 +1957,7 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
                             size_t *nresp)
 {
   int rc;
-  unsigned char send_buffer[10+259], recv_buffer[10+259];
+  unsigned char send_buffer[10+261], recv_buffer[10+261];
   const unsigned char *apdu;
   size_t apdulen;
   unsigned char *msg;
@@ -1971,7 +1971,9 @@ ccid_transceive_apdu_level (ccid_driver_t handle,
   apdulen = apdu_buflen;
   assert (apdulen);
 
-  if (apdulen > 254)
+  /* The maximum length for a short APDU T=1 block is 261, for an
+     extra APDU T=1 block is 65544.  */
+  if (apdulen > 261)
     return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
 
   msg[0] = PC_to_RDR_XfrBlock;
@@ -2117,6 +2119,7 @@ ccid_transceive (ccid_driver_t handle,
           assert (apdulen);
 
           /* Construct an I-Block. */
+#warning fixme: APDULEN may be larger
           if (apdulen > 254)
             return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
 
index 418ad00..696fa67 100644 (file)
@@ -47,6 +47,9 @@
 /* Maximum allowed size of key data as used in inquiries. */
 #define MAXLEN_KEYDATA 4096
 
+/* Maximum allowed size of certificate data as used in inquiries. */
+#define MAXLEN_CERTDATA 16384
+
 
 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
 
@@ -851,14 +854,8 @@ cmd_pksign (assuan_context_t ctx, char *line)
     hash_algo = GCRY_MD_SHA1; 
   else
     return set_error (GPG_ERR_ASS_PARAMETER, "invalid hash algorithm");
-  /* Skip over options. */
-  while ( *line == '-' && line[1] == '-' )
-    {
-      while (*line && !spacep (line))
-        line++;
-      while (spacep (line))
-        line++;
-    }
+
+  line = skip_options (line);
 
   if ( IS_LOCKED (ctrl) )
     return gpg_error (GPG_ERR_LOCKED);
@@ -1036,8 +1033,8 @@ cmd_getattr (assuan_context_t ctx, char *line)
    names and values are depend on the currently selected smartcard
    application.  NAME and VALUE must be percent and '+' escaped.
 
-   However, the curent implementation assumes that Name is not escaped;
-   this works as long as noone uses arbitrary escaping. 
+   However, the current implementation assumes that NAME is not
+   escaped; this works as long as noone uses arbitrary escaping.
  
    A PIN will be requested for most NAMEs.  See the corresponding
    setattr function of the actually used application (app-*.c) for
@@ -1083,12 +1080,74 @@ cmd_setattr (assuan_context_t ctx, char *orig_line)
 
 
 
+/* WRITECERT <hexified_certid>
+
+   This command is used to store a certifciate on a smartcard.  The
+   allowed certids depend on the currently selected smartcard
+   application. The actual certifciate is requested using the inquiry
+   "CERTDATA" and needs to be provided in its raw (e.g. DER) form.
+
+   In almost all cases a a PIN will be requested.  See the related
+   writecert function of the actually used application (app-*.c) for
+   details.  */
+static int
+cmd_writecert (assuan_context_t ctx, char *line)
+{
+  ctrl_t ctrl = assuan_get_pointer (ctx);
+  int rc;
+  char *certid;
+  unsigned char *certdata;
+  size_t certdatalen;
+
+  if ( IS_LOCKED (ctrl) )
+    return gpg_error (GPG_ERR_LOCKED);
+
+  line = skip_options (line);
+
+  if (!*line)
+    return set_error (GPG_ERR_ASS_PARAMETER, "no certid given");
+  certid = line;
+  while (*line && !spacep (line))
+    line++;
+  *line = 0;
+
+  if ((rc = open_card (ctrl, NULL)))
+    return rc;
+
+  if (!ctrl->app_ctx)
+    return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+  certid = xtrystrdup (certid);
+  if (!certid)
+    return out_of_core ();
+
+  /* Now get the actual keydata. */
+  rc = assuan_inquire (ctx, "CERTDATA",
+                       &certdata, &certdatalen, MAXLEN_CERTDATA);
+  if (rc)
+    {
+      xfree (certid);
+      return rc;
+    }
+
+  /* Write the certificate to the card. */
+  rc = app_writecert (ctrl->app_ctx, ctrl, certid, 
+                      pin_cb, ctx, certdata, certdatalen);
+  xfree (certid);
+  xfree (certdata);
+
+  TEST_CARD_REMOVAL (ctrl, rc);
+  return rc;
+}
+
+
+
 /* WRITEKEY [--force] <keyid> 
 
    This command is used to store a secret key on a a smartcard.  The
    allowed keyids depend on the currently selected smartcard
    application. The actual keydata is requested using the inquiry
-   "KETDATA" and need to be provided without any protection.  With
+   "KEYDATA" and need to be provided without any protection.  With
    --force set an existing key under this KEYID will get overwritten.
    The keydata is expected to be the usual canonical encoded
    S-expression.
@@ -1109,14 +1168,8 @@ cmd_writekey (assuan_context_t ctx, char *line)
   if ( IS_LOCKED (ctrl) )
     return gpg_error (GPG_ERR_LOCKED);
 
-  /* Skip over options. */
-  while ( *line == '-' && line[1] == '-' )
-    {
-      while (*line && !spacep (line))
-        line++;
-      while (spacep (line))
-        line++;
-    }
+  line = skip_options (line);
+
   if (!*line)
     return set_error (GPG_ERR_ASS_PARAMETER, "no keyid given");
   keyid = line;
@@ -1294,14 +1347,8 @@ cmd_passwd (assuan_context_t ctx, char *line)
   if ( IS_LOCKED (ctrl) )
     return gpg_error (GPG_ERR_LOCKED);
 
-  /* Skip over options. */
-  while (*line == '-' && line[1] == '-')
-    {
-      while (*line && !spacep (line))
-        line++;
-      while (spacep (line))
-        line++;
-    }
+  line = skip_options (line);
+
   if (!*line)
     return set_error (GPG_ERR_ASS_PARAMETER, "no CHV number given");
   chvnostr = line;
@@ -1601,14 +1648,7 @@ cmd_apdu (assuan_context_t ctx, char *line)
   with_atr = has_option (line, "--atr");
   handle_more = has_option (line, "--more");
 
-  /* Skip over options. */
-  while ( *line == '-' && line[1] == '-' )
-    {
-      while (*line && !spacep (line))
-        line++;
-      while (spacep (line))
-        line++;
-    }
+  line = skip_options (line);
 
   if ( IS_LOCKED (ctrl) )
     return gpg_error (GPG_ERR_LOCKED);
@@ -1687,6 +1727,7 @@ register_commands (assuan_context_t ctx)
     { "OUTPUT",       NULL }, 
     { "GETATTR",      cmd_getattr },
     { "SETATTR",      cmd_setattr },
+    { "WRITECERT",    cmd_writecert },
     { "WRITEKEY",     cmd_writekey },
     { "GENKEY",       cmd_genkey },
     { "RANDOM",       cmd_random },
index b38ae4f..75a8f4a 100644 (file)
@@ -66,7 +66,10 @@ map_sw (int sw)
   switch (sw)
     {
     case SW_EEPROM_FAILURE: ec = GPG_ERR_HARDWARE; break;
+    case SW_TERM_STATE:     ec = GPG_ERR_CARD; break;
     case SW_WRONG_LENGTH:   ec = GPG_ERR_INV_VALUE; break;
+    case SW_SM_NOT_SUP:     ec = GPG_ERR_NOT_SUPPORTED; break;
+    case SW_CC_NOT_SUP:     ec = GPG_ERR_NOT_SUPPORTED; break;
     case SW_CHV_WRONG:      ec = GPG_ERR_BAD_PIN; break;
     case SW_CHV_BLOCKED:    ec = GPG_ERR_PIN_BLOCKED; break;
     case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break;
@@ -130,7 +133,7 @@ iso7816_select_application (int slot, const char *aid, size_t aidlen,
                             unsigned int flags)
 {
   int sw;
-  sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4,
+  sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE, 4,
                          (flags&1)? 0 :0x0c, aidlen, aid);
   return map_sw (sw);
 }
@@ -156,7 +159,7 @@ iso7816_select_file (int slot, int tag, int is_dir,
     {
       p0 = (tag == 0x3F00)? 0: is_dir? 1:2;
       p1 = 0x0c; /* No FC return. */
-      sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE,
+      sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE,
                              p0, p1, 2, (char*)tagbuf );
       return map_sw (sw);
     }
@@ -192,7 +195,7 @@ iso7816_select_path (int slot, const unsigned short *path, size_t pathlen,
 
   p0 = 0x08;
   p1 = 0x0c; /* No FC return. */
-  sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE,
+  sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE,
                          p0, p1, buflen, (char*)buffer );
   return map_sw (sw);
 }
@@ -253,7 +256,7 @@ iso7816_verify_kp (int slot, int chvno, const char *chv, size_t chvlen,
                               pininfo->maxlen,
                               pininfo->padlen);
   else
-    sw = apdu_send_simple (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv);
+    sw = apdu_send_simple (slot, 0, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv);
   return map_sw (sw);
 }
 
@@ -300,7 +303,7 @@ iso7816_change_reference_data_kp (int slot, int chvno,
                            pininfo->maxlen,
                            pininfo->padlen);
   else
-    sw = apdu_send_simple (slot, 0x00, CMD_CHANGE_REFERENCE_DATA,
+    sw = apdu_send_simple (slot, 0, 0x00, CMD_CHANGE_REFERENCE_DATA,
                            oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf);
   xfree (buf);
   return map_sw (sw);
@@ -340,7 +343,7 @@ iso7816_reset_retry_counter_kp (int slot, int chvno,
                            pininfo->maxlen,
                            pininfo->padlen);
   else
-    sw = apdu_send_simple (slot, 0x00, CMD_RESET_RETRY_COUNTER,
+    sw = apdu_send_simple (slot, 0, 0x00, CMD_RESET_RETRY_COUNTER,
                            2, chvno, newchvlen, newchv);
   return map_sw (sw);
 }
@@ -386,14 +389,16 @@ iso7816_get_data (int slot, int tag,
 
 
 /* Perform a PUT DATA command on card in SLOT.  Write DATA of length
-   DATALEN to TAG. */
+   DATALEN to TAG.  EXTENDED_MODE controls whether extended length
+   headers or command chaining is used instead of single length
+   bytes. */
 gpg_error_t
-iso7816_put_data (int slot, int tag,
+iso7816_put_data (int slot, int extended_mode, int tag,
                   const unsigned char *data, size_t datalen)
 {
   int sw;
 
-  sw = apdu_send_simple (slot, 0x00, CMD_PUT_DATA,
+  sw = apdu_send_simple (slot, extended_mode, 0x00, CMD_PUT_DATA,
                          ((tag >> 8) & 0xff), (tag & 0xff),
                          datalen, (const char*)data);
   return map_sw (sw);
@@ -412,7 +417,7 @@ iso7816_manage_security_env (int slot, int p1, int p2,
   if (p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 )
     return gpg_error (GPG_ERR_INV_VALUE);
 
-  sw = apdu_send_simple (slot, 0x00, CMD_MSE, p1, p2, 
+  sw = apdu_send_simple (slot, 0, 0x00, CMD_MSE, p1, p2, 
                          data? datalen : -1, (const char*)data);
   return map_sw (sw);
 }
index ebc121a..1956818 100644 (file)
@@ -79,7 +79,7 @@ gpg_error_t iso7816_reset_retry_counter_kp (int slot, int chvno,
                                             iso7816_pininfo_t *pininfo);
 gpg_error_t iso7816_get_data (int slot, int tag,
                               unsigned char **result, size_t *resultlen);
-gpg_error_t iso7816_put_data (int slot, int tag,
+gpg_error_t iso7816_put_data (int slot, int extended_mode, int tag,
                               const unsigned char *data, size_t datalen);
 gpg_error_t iso7816_manage_security_env (int slot, int p1, int p2,
                                          const unsigned char *data,
index 9dbd245..2dfbce1 100644 (file)
@@ -982,7 +982,7 @@ gpgsm_format_keydesc (ksba_cert_t cert)
       /* We also escape the quote character to work around a bug in
          the mingw32 runtime which does not correcty handle command
          line quoting.  We correctly double the quote mark when
-         calling a program (i.e. gpg-protec-tool), but the pre-main
+         calling a program (i.e. gpg-protect-tool), but the pre-main
          code does not notice the double quote as an escaped
          quote.  */
       if (*s < ' ' || *s == '+' || *s == '\"')