Add limited support for NetKey 3.0 cards.
authorWerner Koch <wk@gnupg.org>
Thu, 8 Jan 2009 19:56:30 +0000 (19:56 +0000)
committerWerner Koch <wk@gnupg.org>
Thu, 8 Jan 2009 19:56:30 +0000 (19:56 +0000)
g10/misc.c
scd/ChangeLog
scd/apdu.c
scd/app-nks.c
scd/command.c
scd/iso7816.c
scd/iso7816.h

index 5325faa..b0e5e2c 100644 (file)
@@ -1248,17 +1248,16 @@ unescape_percent_string (const unsigned char *s)
 }
 
 
-/* Check whether the string has characters not valid in an RFC822
+/* Check whether the string has characters not valid in an RFC-822
    address.  To cope with OpenPGP we ignore allow non-ascii characters
    so that for example umlauts are legal in an email address.  An
-   OpenPGP user ID must be utf-8 encoded and tehre is no strict
+   OpenPGP user ID must be utf-8 encoded but there is no strict
    requirement for RFC-822.  Thus to avoid IDNA encoding we put the
-   address verbatim as utf-8 into the user ID under the assumtiopn
-   that mail programs etc handle IDNA at a lower level and take
-   OpenPGP user IDS as utf-8.  Note that we can't do an utf-8 encoding
-   checking here becuase in keygen.c this function is called with the
-   native encoding and native to utf-8 encoding is done only after
-   checking. */
+   address verbatim as utf-8 into the user ID under the assumption
+   that mail programs handle IDNA at a lower level and take OpenPGP
+   user IDs as utf-8.  Note that we can't do an utf-8 encoding
+   checking here because in keygen.c this function is called with the
+   native encoding and native to utf-8 encoding is only done  later.  */
 int
 has_invalid_email_chars (const char *s)
 {
index c93a17a..937010d 100644 (file)
@@ -1,3 +1,18 @@
+2009-01-08  Werner Koch  <wk@g10code.com>
+
+       * iso7816.c (iso7816_read_record, iso7816_read_binary): Pass 0 for
+       L_e because the problem with the CCID driver has gone.
+       (iso7816_apdu_direct): New.
+
+       * app-nks.c (filelist): Add NKS_VER field.  Add NKS 3 specific
+       entries.
+       (app_local_s, do_deinit): New.
+       (get_nks_version): New.
+       (app_select_nks): Setup local data.
+       (keygripstr_from_pk_file): Replace SLOT by APP and take care of
+       NKS version > 2.
+       (do_learn_status): Take care of NKS version.
+
 2009-01-05  Werner Koch  <wk@g10code.com>
 
        * apdu.c (apdu_get_status): Save the last status.
index f92565d..b3c0f66 100644 (file)
@@ -2793,9 +2793,9 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen,
    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
+       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 == 1 := Use extended length for input and output with out a
+      n == 1 := Use extended length for input and output without a
                 length limit.
        n > 1 := Use extended length with up to N bytes.
 */
@@ -3107,7 +3107,7 @@ apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
 
 /* 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 an APDU including the status word.  With
+   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
@@ -3237,7 +3237,7 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
                 }
             }
           else
-            log_info ("apdu_send_sdirect(%d) "
+            log_info ("apdu_send_direct(%d) "
                       "got unexpected status %04X from get response\n",
                       slot, sw);
         }
@@ -3268,8 +3268,8 @@ apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
 
   unlock_slot (slot);
 
-  /* Append the status word - we reseved the two extra bytes while
-     allocating the buffer. */
+  /* Append the status word.  Note that we reserved the two extra
+     bytes while allocating the buffer.  */
   if (retbuf)
     {
       (*retbuf)[(*retbuflen)++] = (sw >> 8);
index 31ddae0..edc179b 100644 (file)
@@ -1,5 +1,5 @@
-/* app-nks.c - The Telesec NKS 2.0 card application.
- * Copyright (C) 2004, 2007, 2008 Free Software Foundation, Inc.
+/* app-nks.c - The Telesec NKS card application.
+ * Copyright (C) 2004, 2007, 2008, 2009 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
 static struct
 {
   int fid;       /* File ID. */
+  int nks_ver;   /* 0 for NKS version 2, 3 for version 3. */
   int certtype;  /* Type of certificate or 0 if it is not a certificate. */
   int iskeypair; /* If true has the FID of the correspoding certificate. */
   int issignkey; /* True if file is a key usable for signing. */
   int isenckey;  /* True if file is a key usable for decryption. */
 } filelist[] = {
-  { 0x4531, 0,  0xC000, 1, 0 }, 
-  { 0xC000, 101 },
-  { 0x4331, 100 },
-  { 0x4332, 100 },
-  { 0xB000, 110 },
-  { 0x45B1, 0,  0xC200, 0, 1 },
-  { 0xC200, 101 },
-  { 0x43B1, 100 },
-  { 0x43B2, 100 },
-  { 0, 0 }
+  { 0x4531, 0, 0,  0xC000, 1, 0 }, /* EF_PK.NKS.SIG */
+  { 0xC000, 0, 101 },              /* EF_C.NKS.SIG  */
+  { 0x4331, 0, 100 },
+  { 0x4332, 0, 100 },
+  { 0xB000, 0, 110 },              /* EF_PK.RCA.NKS */
+  { 0x45B1, 0, 0,  0xC200, 0, 1 }, /* EF_PK.NKS.ENC */
+  { 0xC200, 0, 101 },              /* EF_C.NKS.ENC  */
+  { 0x43B1, 0, 100 },
+  { 0x43B2, 0, 100 },
+  { 0x4571, 3, 0,  0xc500, 0, 0 }, /* EF_PK.NKS.AUT */
+  { 0xC500, 3, 101 },              /* EF_C.NKS.AUT  */
+  { 0x45B2, 3, 0,  0xC201, 0, 1 }, /* EF_PK.NKS.ENC1024 */
+  { 0xC201, 3, 101 },              /* EF_C.NKS.ENC1024  */
+  { 0 }
 };
 
 
 
+/* Object with application (i.e. NKS) specific data.  */
+struct app_local_s {
+  int nks_version;  /* NKS version.  */
+
+};
+
+
+
+\f
+/* Release local data. */
+static void
+do_deinit (app_t app)
+{
+  if (app && app->app_local)
+    {
+      xfree (app->app_local);
+      app->app_local = NULL;
+    }
+}
+
+
 /* 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
-keygripstr_from_pk_file (int slot, int fid, char *r_gripstr)
+keygripstr_from_pk_file (app_t app, int fid, char *r_gripstr)
 {
   gpg_error_t err;
   unsigned char grip[20];
@@ -65,29 +91,34 @@ keygripstr_from_pk_file (int slot, int fid, char *r_gripstr)
   gcry_sexp_t sexp;
   int i;
   
-  err = iso7816_select_file (slot, fid, 0, NULL, NULL);
+  err = iso7816_select_file (app->slot, fid, 0, NULL, NULL);
   if (err)
     return err;
-  err = iso7816_read_record (slot, 1, 1, 0, &buffer[0], &buflen[0]);
+  err = iso7816_read_record (app->slot, 1, 1, 0, &buffer[0], &buflen[0]);
   if (err)
     return err;
-  err = iso7816_read_record (slot, 2, 1, 0, &buffer[1], &buflen[1]);
+  err = iso7816_read_record (app->slot, 2, 1, 0, &buffer[1], &buflen[1]);
   if (err)
     {
       xfree (buffer[0]);
       return err;
     }
   
-  for (i=0; i < 2; i++)
+  if (app->app_local->nks_version < 3)
     {
-      /* Check that the value appears like an integer encoded as
-         Simple-TLV.  We don't check the tag because the tests cards I
-         have use 1 for both, the modulus and the exponent - the
-         example in the documentation gives 2 for the exponent. */
-      if (buflen[i] < 3)
-        err = gpg_error (GPG_ERR_TOO_SHORT);
-      else if (buffer[i][1] != buflen[i]-2 )
-        err = gpg_error (GPG_ERR_INV_OBJ);
+      /* Old versions of NKS store the values in a TLV encoded format.
+         We need to do some checks.  */
+      for (i=0; i < 2; i++)
+        {
+          /* Check that the value appears like an integer encoded as
+             Simple-TLV.  We don't check the tag because the tests cards I
+             have use 1 for both, the modulus and the exponent - the
+             example in the documentation gives 2 for the exponent. */
+          if (buflen[i] < 3)
+            err = gpg_error (GPG_ERR_TOO_SHORT);
+          else if (buffer[i][1] != buflen[i]-2 )
+            err = gpg_error (GPG_ERR_INV_OBJ);
+        }
     }
 
   if (!err)
@@ -126,6 +157,9 @@ do_learn_status (app_t app, ctrl_t ctrl)
   /* Output information about all useful objects. */
   for (i=0; filelist[i].fid; i++)
     {
+      if (filelist[i].nks_ver > app->app_local->nks_version)
+        continue;
+
       if (filelist[i].certtype)
         {
           size_t len;
@@ -149,7 +183,7 @@ do_learn_status (app_t app, ctrl_t ctrl)
         {
           char gripstr[40+1];
 
-          err = keygripstr_from_pk_file (app->slot, filelist[i].fid, gripstr);
+          err = keygripstr_from_pk_file (app, filelist[i].fid, gripstr);
           if (err)
             log_error ("can't get keygrip from FID 0x%04X: %s\n",
                        filelist[i].fid, gpg_strerror (err));
@@ -580,7 +614,40 @@ do_check_pin (app_t app, const char *keyidstr,
 }
 
 
-/* Select the NKS 2.0 application.  */
+/* Return the version of the NKS application.  */
+static int
+get_nks_version (int slot)
+{
+  unsigned char *result = NULL;
+  size_t resultlen;
+  int type;
+
+  if (iso7816_apdu_direct (slot, "\x80\xaa\x06\x00\x00", 5, 0, 
+                           &result, &resultlen))
+    return 2; /* NKS 2 does not support this command.  */
+  
+  /* Example value:    04 11 19 22 21 6A 20 80 03 03 01 01 01 00 00 00
+                       vv tt ccccccccccccccccc aa bb cc vvvvvvvvvvv xx
+     vendor (Philips) -+  |  |                 |  |  |  |           |
+     chip type -----------+  |                 |  |  |  |           |
+     chip id ----------------+                 |  |  |  |           |
+     card type (3 - tcos 3) -------------------+  |  |  |           |
+     OS version of card type ---------------------+  |  |           |
+     OS release of card type ------------------------+  |           |
+     OS vendor internal version ------------------------+           |
+     RFU -----------------------------------------------------------+
+  */
+  if (resultlen < 16)
+    type = 0;  /* Invalid data returned.  */
+  else
+    type = result[8];
+  xfree (result);
+
+  return type;
+}
+
+
+/* Select the NKS application.  */
 gpg_error_t
 app_select_nks (app_t app)
 {
@@ -593,6 +660,18 @@ app_select_nks (app_t app)
     {
       app->apptype = "NKS";
 
+      app->app_local = xtrycalloc (1, sizeof *app->app_local);
+      if (!app->app_local)
+        {
+          rc = gpg_error (gpg_err_code_from_errno (errno));
+          goto leave;
+        }
+
+      app->app_local->nks_version = get_nks_version (slot);
+      if (opt.verbose)
+        log_info ("Detected NKS version: %d\n", app->app_local->nks_version);
+
+      app->fnc.deinit = do_deinit;
       app->fnc.learn_status = do_learn_status;
       app->fnc.readcert = do_readcert;
       app->fnc.getattr = NULL;
@@ -605,6 +684,9 @@ app_select_nks (app_t app)
       app->fnc.check_pin = do_check_pin;
    }
 
+ leave:
+  if (rc)
+    do_deinit (app);
   return rc;
 }
 
index 1037f16..c535acb 100644 (file)
@@ -559,7 +559,7 @@ 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 (DINSIG)
+      110 := Root CA cert (e.g. DINSIG)
 
    For certain cards, more information will be returned:
 
index 2286090..ecb6dc1 100644 (file)
@@ -1,5 +1,5 @@
 /* iso7816.c - ISO 7816 commands
- * Copyright (C) 2003, 2004, 2008 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004, 2008, 2009 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -226,6 +226,48 @@ iso7816_list_directory (int slot, int list_dirs,
 }
 
 
+/* This funcion sends an already formatted APDU to the card.  With
+   HANDLE_MORE set to true a MORE DATA status will be handled
+   internally.  The return value is a gpg error code (i.e. a mapped
+   status word).  This is basically the same as apdu_send_direct but
+   it maps the status word and does not return it in the result
+   buffer.  */
+gpg_error_t
+iso7816_apdu_direct (int slot, const void *apdudata, size_t apdudatalen, 
+                     int handle_more,
+                     unsigned char **result, size_t *resultlen)
+{
+  int sw;
+
+  if (!result || !resultlen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  *result = NULL;
+  *resultlen = 0;
+
+  sw = apdu_send_direct (slot, apdudata, apdudatalen, handle_more,
+                         result, resultlen);
+  if (!sw)
+    {
+      if (*resultlen < 2)
+        sw = SW_HOST_GENERAL_ERROR;
+      else
+        {
+          sw = ((*result)[*resultlen-2] << 8) | (*result)[*resultlen-1];
+          (*resultlen)--;
+          (*resultlen)--;
+        }
+    }
+  if (sw != SW_SUCCESS)
+    {
+      /* Make sure that pending buffers are released. */
+      xfree (*result);
+      *result = NULL;
+      *resultlen = 0;
+    }
+  return map_sw (sw);
+}
+
+
 /* Check whether the reader supports the ISO command code COMMAND on
    the keypad.  Returns 0 on success.  */
 gpg_error_t
@@ -668,14 +710,7 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax,
     {
       buffer = NULL;
       bufferlen = 0;
-      /* Note, that we to set N to 254 due to problems either with the
-         ccid driver or some TCOS cards.  It actually should be 0
-         which is the official ISO value to read a variable length
-         object. */
-      if (read_all || nmax > 254)
-        n = 254;
-      else
-        n = nmax;
+      n = read_all? 0 : nmax;
       sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY,
                          ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
                          n, &buffer, &bufferlen);
@@ -769,13 +804,11 @@ iso7816_read_record (int slot, int recno, int reccount, int short_ef,
 
   buffer = NULL;
   bufferlen = 0;
-  /* Fixme: Either the ccid driver or the TCOS cards have problems
-     with an Le of 0. */
   sw = apdu_send_le (slot, 0x00, CMD_READ_RECORD,
                      recno, 
                      short_ef? short_ef : 0x04,
                      -1, NULL,
-                     254, &buffer, &bufferlen);
+                     0, &buffer, &bufferlen);
 
   if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
     {
index 2847511..6c0485d 100644 (file)
@@ -57,6 +57,10 @@ gpg_error_t iso7816_select_path (int slot,
                                  unsigned char **result, size_t *resultlen);
 gpg_error_t iso7816_list_directory (int slot, int list_dirs,
                                     unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_apdu_direct (int slot,
+                                 const void *apdudata, size_t apdudatalen, 
+                                 int handle_more,
+                                 unsigned char **result, size_t *resultlen);
 gpg_error_t iso7816_check_keypad (int slot, int command,
                                   iso7816_pininfo_t *pininfo);
 gpg_error_t iso7816_verify (int slot,