Add readcert command.
authorWerner Koch <wk@gnupg.org>
Wed, 17 Jun 2009 09:45:50 +0000 (09:45 +0000)
committerWerner Koch <wk@gnupg.org>
Wed, 17 Jun 2009 09:45:50 +0000 (09:45 +0000)
fix reading large certificates.

common/exechelp.c
g10/ChangeLog
g10/call-agent.c
g10/call-agent.h
g10/card-util.c
g10/keyserver.c
scd/ChangeLog
scd/app-openpgp.c
scd/iso7816.c
scd/iso7816.h

index 7019ae7..a5e25fd 100644 (file)
@@ -575,7 +575,7 @@ gnupg_spawn_process (const char *pgmname, const char *argv[],
 /*              (int) pi.dwProcessId, (int) pi.dwThreadId); */
   
   /* Fixme: For unknown reasons AllowSetForegroundWindow returns an
-     invalid argument error if we pass the the correct processID to
+     invalid argument error if we pass the correct processID to
      it.  As a workaround we use -1 (ASFW_ANY).  */
   if ( (flags & 64) )
     gnupg_allow_set_foregound_window ((pid_t)(-1)/*pi.dwProcessId*/);
index 688ff14..be083a6 100644 (file)
@@ -1,3 +1,15 @@
+2009-06-17  Werner Koch  <wk@g10code.com>
+
+       * card-util.c (put_data_to_file, read_cert): New. 
+       (card_edit): Add command "readcert".
+       (fetch_url): Allow code also for this gnupg major version 2.
+       * call-agent.c (agent_scd_readcert): New.
+
+2009-06-15  Werner Koch  <wk@g10code.com>
+
+       * keyserver.c (keyserver_search_prompt): No prompt in batch+colons
+       mode.
+
 2009-06-09  Werner Koch  <wk@g10code.com>
 
        * card-util.c (write_sc_op_status): New.
index 63919dd..cd58b90 100644 (file)
@@ -488,7 +488,6 @@ agent_scd_writecert (const char *certidstr,
 }
 
 
-
 \f
 /* Handle a KEYDATA inquiry.  Note, we only send the data,
    assuan_transact takes care of flushing and writing the end */
@@ -539,7 +538,6 @@ agent_scd_writekey (int keyno, const char *serialno,
 }
 
 
-
 \f
 /* Status callback for the SCD GENKEY command. */
 static int
@@ -765,6 +763,43 @@ agent_scd_pkdecrypt (const char *serialno,
 }
 
 
+\f
+/* Send a READCERT command to the SCdaemon. */
+int 
+agent_scd_readcert (const char *certidstr,
+                    void **r_buf, size_t *r_buflen)
+{
+  int rc;
+  char line[ASSUAN_LINELENGTH];
+  membuf_t data;
+  size_t len;
+
+  *r_buf = NULL;
+  rc = start_agent ();
+  if (rc)
+    return rc;
+
+  init_membuf (&data, 2048);
+
+  snprintf (line, DIM(line)-1, "SCD READCERT %s", certidstr);
+  line[DIM(line)-1] = 0;
+  rc = assuan_transact (agent_ctx, line,
+                        membuf_data_cb, &data,
+                        default_inq_cb, NULL, NULL, NULL);
+  if (rc)
+    {
+      xfree (get_membuf (&data, &len));
+      return rc;
+    }
+  *r_buf = get_membuf (&data, r_buflen);
+  if (!*r_buf)
+    return gpg_error (GPG_ERR_ENOMEM);
+
+  return 0;
+}
+
+
+\f
 /* Change the PIN of an OpenPGP card or reset the retry counter.
    CHVNO 1: Change the PIN
          2: For v1 cards: Same as 1.
index 577926e..6d46f54 100644 (file)
@@ -104,6 +104,10 @@ int agent_scd_pkdecrypt (const char *serialno,
                          const unsigned char *indata, size_t indatalen,
                          unsigned char **r_buf, size_t *r_buflen);
 
+/* Send a READKEY command to the SCdaemon. */
+int agent_scd_readcert (const char *certidstr,
+                        void **r_buf, size_t *r_buflen);
+
 /* Change the PIN of an OpenPGP card or reset the retry counter. */
 int agent_scd_change_pin (int chvno, const char *serialno);
 
index 290e3b7..9295a17 100644 (file)
@@ -715,7 +715,6 @@ change_url (void)
 static int
 fetch_url(void)
 {
-#if GNUPG_MAJOR_VERSION == 1
   int rc;
   struct agent_card_info_s info;
 
@@ -755,15 +754,11 @@ fetch_url(void)
     }
 
   return rc;
-#else
-  #warning need to implemented fucntion
-  return 0;
-#endif
 }
 
 
 /* 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
+   -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
@@ -814,6 +809,39 @@ get_data_from_file (const char *fname, size_t maxlen, char **r_buffer)
 }
 
 
+/* Write LENGTH bytes from BUFFER to file FNAME.  Return 0 on
+   success.  */
+static int
+put_data_to_file (const char *fname, const void *buffer, size_t length)
+{
+  FILE *fp;
+  
+  fp = fopen (fname, "wb");
+#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 create `%s': %s\n"), fname, strerror (errno));
+      return -1;
+    }
+          
+  if (length && fwrite (buffer, length, 1, fp) != 1)
+    {
+      tty_printf (_("error writing `%s': %s\n"), fname, strerror (errno));
+      fclose (fp);
+      return -1;
+    }
+  fclose (fp);
+  return 0;
+}
+
+
 static int
 change_login (const char *args)
 {
@@ -934,6 +962,37 @@ change_cert (const char *args)
 
 
 static int
+read_cert (const char *args)
+{
+  const char *fname;
+  void *buffer;
+  size_t length;
+  int rc;
+
+  if (args && *args == '>')  /* Write it to a file */
+    {
+      for (args++; spacep (args); args++)
+        ;
+      fname = args;
+    }
+  else
+    {
+      tty_printf ("usage error: redirectrion to file required\n");
+      return -1;
+    }
+
+  rc = agent_scd_readcert ("OPENPGP.3", &buffer, &length);
+  if (rc)
+    log_error ("error reading certificate from card: %s\n", gpg_strerror (rc));
+  else
+    rc = put_data_to_file (fname, buffer, length);
+  xfree (buffer);
+  write_sc_op_status (rc);
+  return rc;
+}
+
+
+static int
 change_lang (void)
 {
   char *data, *p;
@@ -1447,7 +1506,7 @@ enum cmdids
     cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
     cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
     cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
-    cmdUNBLOCK,
+    cmdREADCERT, cmdUNBLOCK,
     cmdINVCMD
   };
 
@@ -1481,6 +1540,7 @@ static struct
     { "unblock" , cmdUNBLOCK,0, N_("unblock the PIN using a Reset Code") },
     /* Note, that we do not announce these command yet. */
     { "privatedo", cmdPRIVATEDO, 0, NULL },
+    { "readcert", cmdREADCERT, 0, NULL },
     { "writecert", cmdWRITECERT, 1, NULL },
     { NULL, cmdINVCMD, 0, NULL } 
   };
@@ -1735,6 +1795,13 @@ card_edit (strlist_t commands)
             change_cert (arg_rest);
           break;
 
+        case cmdREADCERT:
+          if ( arg_number != 3 )
+            tty_printf ("usage: readcert 3 > FILE\n");
+          else
+            read_cert (arg_rest);
+          break;
+
         case cmdFORCESIG:
           toggle_forcesig ();
           break;
index 249a62f..a7c67c0 100644 (file)
@@ -862,6 +862,9 @@ keyserver_search_prompt(IOBUF buffer,const char *searchstr)
              if(i!=count)
                validcount=0;
 
+              if (opt.with_colons && opt.batch)
+                break;
+                
              for(;;)
                {
                  if(show_prompt(desc,i,validcount?count:0,localstr))
index 7fc8fe4..b7dfda6 100644 (file)
@@ -1,3 +1,17 @@
+2009-06-17  Werner Koch  <wk@g10code.com>
+
+       * iso7816.c (iso7816_get_data): Add arg EXTENDED_MODE.  Change all
+       callers.
+       * app-openpgp.c (data_objects): Use bit flags.  Add flag
+       TRY_EXTLENGTH.
+       (get_cached_data): Add arg TRY_EXTLEN and use it for iso7816_get_data.
+       (get_one_do): Use extended length APDU if necessary.
+
+2009-06-10  Werner Koch  <wk@g10code.com>
+
+       * app-openpgp.c (store_fpr): Change first arg to app_t; adjust
+       callers.  Flush the cache.
+
 2009-06-09  Werner Koch  <wk@g10code.com>
 
        * app-openpgp.c (do_readcert): Return NOT_FOUND if the retrieved
index 658459e..f9ada25 100644 (file)
@@ -75,43 +75,45 @@ static struct {
   int tag;
   int constructed;
   int get_from;  /* Constructed DO with this DO or 0 for direct access. */
-  int binary;
-  int dont_cache;
-  int flush_on_error;
-  int get_immediate_in_v11; /* Enable a hack to bypass the cache of
-                               this data object if it is used in 1.1
-                               and later versions of the card.  This
-                               does not work with composite DO and is
-                               currently only useful for the CHV
-                               status bytes. */
+  int binary:1;
+  int dont_cache:1;
+  int flush_on_error:1;
+  int get_immediate_in_v11:1; /* Enable a hack to bypass the cache of
+                                 this data object if it is used in 1.1
+                                 and later versions of the card.  This
+                                 does not work with composite DO and
+                                 is currently only useful for the CHV
+                                 status bytes. */
+  int try_extlen:1;           /* Large object; try to use an extended
+                                 length APDU.  */
   char *desc;
 } 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" },
-  { 0x5F35, 0, 0x65, 0, 0, 0, 0, "Sex" },
-  { 0x006E, 1,    0, 1, 0, 0, 0, "Application Related Data" },
-  { 0x004F, 0, 0x6E, 1, 0, 0, 0, "AID" },
-  { 0x0073, 1,    0, 1, 0, 0, 0, "Discretionary Data Objects" },
-  { 0x0047, 0, 0x6E, 1, 1, 0, 0, "Card Capabilities" },
-  { 0x00C0, 0, 0x6E, 1, 1, 0, 0, "Extended Card Capabilities" },
-  { 0x00C1, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Signature" },
-  { 0x00C2, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Decryption" },
-  { 0x00C3, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Authentication" },
-  { 0x00C4, 0, 0x6E, 1, 0, 1, 1, "CHV Status Bytes" },
-  { 0x00C5, 0, 0x6E, 1, 0, 0, 0, "Fingerprints" },
-  { 0x00C6, 0, 0x6E, 1, 0, 0, 0, "CA Fingerprints" },
-  { 0x00CD, 0, 0x6E, 1, 0, 0, 0, "Generation time" },
-  { 0x007A, 1,    0, 1, 0, 0, 0, "Security Support Template" },
-  { 0x0093, 0, 0x7A, 1, 1, 0, 0, "Digital Signature Counter" },
-  { 0x0101, 0,    0, 0, 0, 0, 0, "Private DO 1"},
-  { 0x0102, 0,    0, 0, 0, 0, 0, "Private DO 2"},
-  { 0x0103, 0,    0, 0, 0, 0, 0, "Private DO 3"},
-  { 0x0104, 0,    0, 0, 0, 0, 0, "Private DO 4"},
-  { 0x7F21, 1,    0, 1, 0, 0, 0, "Cardholder certificate"},
+  { 0x005E, 0,    0, 1, 0, 0, 0, 0, "Login Data" },
+  { 0x5F50, 0,    0, 0, 0, 0, 0, 0, "URL" },
+  { 0x5F52, 0,    0, 1, 0, 0, 0, 0, "Historical Bytes" },
+  { 0x0065, 1,    0, 1, 0, 0, 0, 0, "Cardholder Related Data"},
+  { 0x005B, 0, 0x65, 0, 0, 0, 0, 0, "Name" },
+  { 0x5F2D, 0, 0x65, 0, 0, 0, 0, 0, "Language preferences" },
+  { 0x5F35, 0, 0x65, 0, 0, 0, 0, 0, "Sex" },
+  { 0x006E, 1,    0, 1, 0, 0, 0, 0, "Application Related Data" },
+  { 0x004F, 0, 0x6E, 1, 0, 0, 0, 0, "AID" },
+  { 0x0073, 1,    0, 1, 0, 0, 0, 0, "Discretionary Data Objects" },
+  { 0x0047, 0, 0x6E, 1, 1, 0, 0, 0, "Card Capabilities" },
+  { 0x00C0, 0, 0x6E, 1, 1, 0, 0, 0, "Extended Card Capabilities" },
+  { 0x00C1, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Signature" },
+  { 0x00C2, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Decryption" },
+  { 0x00C3, 0, 0x6E, 1, 1, 0, 0, 0, "Algorithm Attributes Authentication" },
+  { 0x00C4, 0, 0x6E, 1, 0, 1, 1, 0, "CHV Status Bytes" },
+  { 0x00C5, 0, 0x6E, 1, 0, 0, 0, 0, "Fingerprints" },
+  { 0x00C6, 0, 0x6E, 1, 0, 0, 0, 0, "CA Fingerprints" },
+  { 0x00CD, 0, 0x6E, 1, 0, 0, 0, 0, "Generation time" },
+  { 0x007A, 1,    0, 1, 0, 0, 0, 0, "Security Support Template" },
+  { 0x0093, 0, 0x7A, 1, 1, 0, 0, 0, "Digital Signature Counter" },
+  { 0x0101, 0,    0, 0, 0, 0, 0, 0, "Private DO 1"},
+  { 0x0102, 0,    0, 0, 0, 0, 0, 0, "Private DO 2"},
+  { 0x0103, 0,    0, 0, 0, 0, 0, 0, "Private DO 3"},
+  { 0x0104, 0,    0, 0, 0, 0, 0, 0, "Private DO 4"},
+  { 0x7F21, 1,    0, 1, 0, 0, 0, 1, "Cardholder certificate"},
   { 0 }
 };
 
@@ -244,17 +246,19 @@ do_deinit (app_t app)
 
 /* Wrapper around iso7816_get_data which first tries to get the data
    from the cache.  With GET_IMMEDIATE passed as true, the cache is
-   bypassed. */
+   bypassed.  With TRY_EXTLEN extended lengths APDUs are use if
+   supported by the card.  */
 static gpg_error_t
 get_cached_data (app_t app, int tag, 
                  unsigned char **result, size_t *resultlen,
-                 int get_immediate)
+                 int get_immediate, int try_extlen)
 {
   gpg_error_t err;
   int i;
   unsigned char *p;
   size_t len;
   struct cache_s *c;
+  int exmode;
 
   *result = NULL;
   *resultlen = 0;
@@ -279,7 +283,12 @@ get_cached_data (app_t app, int tag,
           }
     }
   
-  err = iso7816_get_data (app->slot, tag, &p, &len);
+  if (try_extlen && app->app_local->cardcap.ext_lc_le)
+    exmode = app->app_local->extcap.max_rsp_data;
+  else
+    exmode = 0;
+
+  err = iso7816_get_data (app->slot, exmode, tag, &p, &len);
   if (err)
     return err;
   *result = p;
@@ -392,6 +401,7 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
   unsigned char *value;
   size_t valuelen;
   int dummyrc;
+  int exmode;
 
   if (!r_rc)
     r_rc = &dummyrc;
@@ -404,7 +414,11 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
 
   if (app->card_version > 0x0100 && data_objects[i].get_immediate_in_v11)
     {
-      rc = iso7816_get_data (app->slot, tag, &buffer, &buflen);
+      if (data_objects[i].try_extlen && app->app_local->cardcap.ext_lc_le)
+        exmode = app->app_local->extcap.max_rsp_data;
+      else
+        exmode = 0;
+      rc = iso7816_get_data (app->slot, exmode, tag, &buffer, &buflen);
       if (rc)
         {
           *r_rc = rc;
@@ -422,7 +436,8 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
       rc = get_cached_data (app, data_objects[i].get_from,
                             &buffer, &buflen,
                             (data_objects[i].dont_cache 
-                             || data_objects[i].get_immediate_in_v11));
+                             || data_objects[i].get_immediate_in_v11),
+                            data_objects[i].try_extlen);
       if (!rc)
         {
           const unsigned char *s;
@@ -445,7 +460,8 @@ get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
     {
       rc = get_cached_data (app, tag, &buffer, &buflen,
                             (data_objects[i].dont_cache 
-                             || data_objects[i].get_immediate_in_v11));
+                             || data_objects[i].get_immediate_in_v11),
+                            data_objects[i].try_extlen);
       if (!rc)
         {
           value = buffer;
@@ -476,7 +492,9 @@ dump_all_do (int slot)
       if (data_objects[i].get_from)
         continue;
 
-      rc = iso7816_get_data (slot, data_objects[i].tag, &buffer, &buflen);
+      /* We don't try extended length APDU because such large DO would
+         be pretty useless in a log file.  */
+      rc = iso7816_get_data (slot, 0, data_objects[i].tag, &buffer, &buflen);
       if (gpg_err_code (rc) == GPG_ERR_NO_OBJ)
         ;
       else if (rc) 
@@ -621,13 +639,14 @@ parse_login_data (app_t app)
 
 /* Note, that FPR must be at least 20 bytes. */
 static gpg_error_t 
-store_fpr (int slot, int keynumber, u32 timestamp,
+store_fpr (app_t app, int keynumber, u32 timestamp,
            const unsigned char *m, size_t mlen,
            const unsigned char *e, size_t elen, 
            unsigned char *fpr, unsigned int card_version)
 {
   unsigned int n, nbits;
   unsigned char *buffer, *p;
+  int tag, tag2;
   int rc;
   
   for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */
@@ -662,9 +681,12 @@ store_fpr (int slot, int keynumber, u32 timestamp,
 
   xfree (buffer);
 
-  rc = iso7816_put_data (slot, 0, 
-                         (card_version > 0x0007? 0xC7 : 0xC6)
-                         + keynumber, fpr, 20);
+  tag = (card_version > 0x0007? 0xC7 : 0xC6) + keynumber;
+  flush_cache_item (app, tag);
+  tag2 = 0xCE + keynumber;
+  flush_cache_item (app, tag2);
+
+  rc = iso7816_put_data (app->slot, 0, tag, fpr, 20);
   if (rc)
     log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc));
 
@@ -677,7 +699,7 @@ store_fpr (int slot, int keynumber, u32 timestamp,
       buf[2] = timestamp >>  8;
       buf[3] = timestamp;
 
-      rc = iso7816_put_data (slot, 0, 0xCE + keynumber, buf, 4);
+      rc = iso7816_put_data (app->slot, 0, tag2, buf, 4);
       if (rc)
         log_error (_("failed to store the creation date: %s\n"),
                    gpg_strerror (rc));
@@ -2131,7 +2153,7 @@ does_key_exist (app_t app, int keyidx, int force)
 
   assert (keyidx >=0 && keyidx <= 2);
 
-  if (iso7816_get_data (app->slot, 0x006E, &buffer, &buflen))
+  if (iso7816_get_data (app->slot, 0, 0x006E, &buffer, &buflen))
     {
       log_error (_("error reading application data\n"));
       return gpg_error (GPG_ERR_GENERAL);
@@ -2623,7 +2645,7 @@ do_writekey (app_t app, ctrl_t ctrl,
       goto leave;
     }
  
-  err = store_fpr (app->slot, keyno, created_at,
+  err = store_fpr (app, keyno, created_at,
                   rsa_n, rsa_n_len, rsa_e, rsa_e_len,
                   fprbuf, app->card_version);
   if (err)
@@ -2757,7 +2779,7 @@ do_genkey (app_t app, ctrl_t ctrl,  const char *keynostr, unsigned int flags,
   send_status_info (ctrl, "KEY-CREATED-AT",
                     numbuf, (size_t)strlen(numbuf), NULL, 0);
 
-  rc = store_fpr (app->slot, keyno, (u32)created_at,
+  rc = store_fpr (app, keyno, (u32)created_at,
                   m, mlen, e, elen, fprbuf, app->card_version);
   if (rc)
     goto leave;
@@ -2811,7 +2833,7 @@ compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr)
   
   assert (keyno >= 1 && keyno <= 3);
 
-  rc = get_cached_data (app, 0x006E, &buffer, &buflen, 0);
+  rc = get_cached_data (app, 0x006E, &buffer, &buflen, 0, 0);
   if (rc)
     {
       log_error (_("error reading application data\n"));
@@ -3502,7 +3524,7 @@ app_select_openpgp (app_t app)
          replace a possibly already set one from a EF.GDO with this
          one.  Note, that for current OpenPGP cards, no EF.GDO exists
          and thus it won't matter at all. */
-      rc = iso7816_get_data (slot, 0x004F, &buffer, &buflen);
+      rc = iso7816_get_data (slot, 0, 0x004F, &buffer, &buflen);
       if (rc)
         goto leave;
       if (opt.verbose)
index 492e125..3fea6c7 100644 (file)
@@ -420,19 +420,27 @@ iso7816_reset_retry_counter (int slot, int chvno,
    a newly allocated buffer at the address passed by RESULT.  Return
    the length of this data at the address of RESULTLEN. */
 gpg_error_t
-iso7816_get_data (int slot, int tag,
+iso7816_get_data (int slot, int extended_mode, int tag,
                   unsigned char **result, size_t *resultlen)
 {
   int sw;
+  int le;
 
   if (!result || !resultlen)
     return gpg_error (GPG_ERR_INV_VALUE);
   *result = NULL;
   *resultlen = 0;
 
-  sw = apdu_send (slot, 0, 0x00, CMD_GET_DATA,
-                  ((tag >> 8) & 0xff), (tag & 0xff), -1, NULL,
-                  result, resultlen);
+  if (extended_mode > 0 && extended_mode < 256)
+    le = 65534; /* Not 65535 in case it is used as some special flag.  */
+  else if (extended_mode > 0)
+    le = extended_mode;
+  else
+    le = 256;
+
+  sw = apdu_send_le (slot, extended_mode, 0x00, CMD_GET_DATA,
+                     ((tag >> 8) & 0xff), (tag & 0xff), -1, NULL, le,
+                     result, resultlen);
   if (sw != SW_SUCCESS)
     {
       /* Make sure that pending buffers are released. */
index 3c6dd89..d3deda1 100644 (file)
@@ -84,7 +84,7 @@ gpg_error_t iso7816_reset_retry_counter_kp (int slot, int chvno,
 gpg_error_t iso7816_reset_retry_counter_with_rc (int slot, int chvno,
                                                  const char *data,
                                                  size_t datalen);
-gpg_error_t iso7816_get_data (int slot, int tag,
+gpg_error_t iso7816_get_data (int slot, int extended_mode, int tag,
                               unsigned char **result, size_t *resultlen);
 gpg_error_t iso7816_put_data (int slot, int extended_mode, int tag,
                               const unsigned char *data, size_t datalen);