Updated card stuff to support T=0 cards.
[gnupg.git] / g10 / iso7816.c
index 9eff9d3..5b98532 100644 (file)
@@ -15,7 +15,8 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
  *
  * $Id$
  */
@@ -77,6 +78,7 @@ map_sw (int sw)
     case SW_RECORD_NOT_FOUND:ec= GPG_ERR_NOT_FOUND; break;
     case SW_REF_NOT_FOUND:  ec = GPG_ERR_NO_OBJ; break;
     case SW_BAD_P0_P1:      ec = GPG_ERR_INV_VALUE; break;
+    case SW_EXACT_LENGTH:   ec = GPG_ERR_INV_VALUE; break;
     case SW_INS_NOT_SUP:    ec = GPG_ERR_CARD; break;
     case SW_CLA_NOT_SUP:    ec = GPG_ERR_CARD; break;
     case SW_SUCCESS:        ec = 0; break;
@@ -153,7 +155,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,
-                             p0, p1, 2, tagbuf );
+                             p0, p1, 2, (char*)tagbuf );
       return map_sw (sw);
     }
 
@@ -161,6 +163,39 @@ iso7816_select_file (int slot, int tag, int is_dir,
 }
 
 
+/* Do a select file command with a direct path. */
+gpg_error_t
+iso7816_select_path (int slot, const unsigned short *path, size_t pathlen,
+                     unsigned char **result, size_t *resultlen)
+{
+  int sw, p0, p1;
+  unsigned char buffer[100];
+  int buflen;
+  
+  if (result || resultlen)
+    {
+      *result = NULL;
+      *resultlen = 0;
+      return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+    }
+  
+  if (pathlen/2 >= sizeof buffer)
+    return gpg_error (GPG_ERR_TOO_LARGE);
+  
+  for (buflen = 0; pathlen; pathlen--, path++)
+    {
+      buffer[buflen++] = (*path >> 8);
+      buffer[buflen++] = *path;
+    }
+
+  p0 = 0x08;
+  p1 = 0x0c; /* No FC return. */
+  sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE,
+                         p0, p1, buflen, (char*)buffer );
+  return map_sw (sw);
+}
+
+
 /* This is a private command currently only working for TCOS cards. */
 gpg_error_t
 iso7816_list_directory (int slot, int list_dirs,
@@ -285,7 +320,7 @@ iso7816_put_data (int slot, int tag,
 
   sw = apdu_send_simple (slot, 0x00, CMD_PUT_DATA,
                          ((tag >> 8) & 0xff), (tag & 0xff),
-                         datalen, data);
+                         datalen, (const char*)data);
   return map_sw (sw);
 }
 
@@ -299,10 +334,11 @@ iso7816_manage_security_env (int slot, int p1, int p2,
 {
   int sw;
 
-  if (p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 || !data || !datalen)
+  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, datalen, data);
+  sw = apdu_send_simple (slot, 0x00, CMD_MSE, p1, p2, 
+                         data? datalen : -1, (const char*)data);
   return map_sw (sw);
 }
 
@@ -322,7 +358,7 @@ 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, data,
+  sw = apdu_send (slot, 0x00, CMD_PSO, 0x9E, 0x9A, datalen, (const char*)data,
                   result, resultlen);
   if (sw != SW_SUCCESS)
     {
@@ -363,13 +399,15 @@ iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
 
       *buf = padind; /* Padding indicator. */
       memcpy (buf+1, data, datalen);
-      sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen+1, buf,
+      sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86,
+                      datalen+1, (char*)buf,
                       result, resultlen);
       xfree (buf);
     }
   else
     {
-      sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen, data,
+      sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86,
+                      datalen, (const char *)data,
                       result, resultlen);
     }
   if (sw != SW_SUCCESS)
@@ -398,7 +436,7 @@ iso7816_internal_authenticate (int slot,
   *resultlen = 0;
 
   sw = apdu_send (slot, 0x00, CMD_INTERNAL_AUTHENTICATE, 0, 0,
-                  datalen, data,  result, resultlen);
+                  datalen, (const char*)data,  result, resultlen);
   if (sw != SW_SUCCESS)
     {
       /* Make sure that pending buffers are released. */
@@ -425,7 +463,7 @@ do_generate_keypair (int slot, int readonly,
   *resultlen = 0;
 
   sw = apdu_send (slot, 0x00, CMD_GENERATE_KEYPAIR, readonly? 0x81:0x80, 0,
-                  datalen, data,  result, resultlen);
+                  datalen, (const char*)data,  result, resultlen);
   if (sw != SW_SUCCESS)
     {
       /* Make sure that pending buffers are released. */
@@ -521,8 +559,10 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax,
     {
       buffer = NULL;
       bufferlen = 0;
-      /* Fixme: Either the ccid driver or the TCOS cards have problems
-         with an Le of 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
@@ -530,6 +570,21 @@ iso7816_read_binary (int slot, size_t offset, size_t nmax,
       sw = apdu_send_le (slot, 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,
+                             ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
+                             n, &buffer, &bufferlen);
+        }
+
+      if (*result && sw == SW_BAD_P0_P1)
+        {
+          /* Bad Parameter means that the offset is outside of the
+             EF. When reading all data we take this as an indication
+             for EOF.  */
+          break;
+        }
 
       if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
         {
@@ -605,7 +660,7 @@ iso7816_read_record (int slot, int recno, int reccount, int short_ef,
 
   buffer = NULL;
   bufferlen = 0;
-  /* Fixme: Either the ccid driver of the TCOS cards have problems
+  /* 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,