GNUPG-1-9-BRANCH gnupg/scd (ChangeLog app-common.h app-openpgp.c app.c command.c)

cvs user wk cvs at cvs.gnupg.org
Tue Feb 22 18:17:53 CET 2005


    Date: Tuesday, February 22, 2005 @ 18:29:07
  Author: wk
    Path: /cvs/gnupg/gnupg/scd
     Tag: GNUPG-1-9-BRANCH

Modified: ChangeLog app-common.h app-openpgp.c app.c command.c

* app-openpgp.c (app_local_s): New field PK.
(do_deinit, do_genkey, app_openpgp_storekey): Clear it.
(get_public_key, send_keypair_info): New.
(do_learn_status): Send KEYPAIR info

* app-common.h (app_ctx_t): Add function pointer READKEY.
* app.c (app_readkey): New.
* command.c (cmd_readkey): Use READKEY function if possible.


---------------+
 ChangeLog     |   16 +++
 app-common.h  |    8 +
 app-openpgp.c |  249 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 app.c         |   26 +++++
 command.c     |   34 ++++++-
 5 files changed, 318 insertions(+), 15 deletions(-)


Index: gnupg/scd/ChangeLog
diff -u gnupg/scd/ChangeLog:1.25.2.66 gnupg/scd/ChangeLog:1.25.2.67
--- gnupg/scd/ChangeLog:1.25.2.66	Thu Feb  3 14:20:57 2005
+++ gnupg/scd/ChangeLog	Tue Feb 22 18:29:07 2005
@@ -1,3 +1,14 @@
+2005-02-22  Werner Koch  <wk at g10code.com>
+
+	* app-openpgp.c (app_local_s): New field PK.
+	(do_deinit, do_genkey, app_openpgp_storekey): Clear it.
+	(get_public_key, send_keypair_info): New.
+	(do_learn_status): Send KEYPAIR info
+
+	* app-common.h (app_ctx_t): Add function pointer READKEY.
+	* app.c (app_readkey): New.
+	* command.c (cmd_readkey): Use READKEY function if possible.
+
 2005-01-26  Werner Koch  <wk at g10code.com>
 
 	* ccid-driver.c (parse_ccid_descriptor): Need the CSM workaround
@@ -18,7 +29,7 @@
 	side effect of the retrieval of the the C4 DO from the 6E DO the
 	cached fingerprint will get updated to the old value and later
 	when signing the generated key the checking of the fingerprint
-	fails becuase it won't match the new one.  Thanks to Moritz for
+	fails because it won't match the new one.  Thanks to Moritz for
 	analyzing this problem.
 	(verify_chv3): Removed the CHV status reread logic because we
 	won't cache the C4 DO anymore.
@@ -934,7 +945,8 @@
 	* scdaemon.c scdaemon.h, command.c: New. Based on the code from
 	the gpg-agent.
 
- Copyright 2002 Free Software Foundation, Inc.
+	
+ Copyright 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
 
  This file is free software; as a special exception the author gives
  unlimited permission to copy and/or distribute it, with or without
Index: gnupg/scd/app-common.h
diff -u gnupg/scd/app-common.h:1.5.2.11 gnupg/scd/app-common.h:1.5.2.12
--- gnupg/scd/app-common.h:1.5.2.11	Fri Oct 22 11:41:56 2004
+++ gnupg/scd/app-common.h	Tue Feb 22 18:29:06 2005
@@ -1,5 +1,5 @@
 /* app-common.h - Common declarations for all card applications
- *	Copyright (C) 2003 Free Software Foundation, Inc.
+ *	Copyright (C) 2003, 2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  *
- * $Id: app-common.h,v 1.5.2.11 2004/10/22 09:41:56 wk Exp $
+ * $Id: app-common.h,v 1.5.2.12 2005/02/22 17:29:06 wk Exp $
  */
 
 #ifndef GNUPG_SCD_APP_COMMON_H
@@ -49,6 +49,8 @@
     int (*learn_status) (app_t app, ctrl_t ctrl);
     int (*readcert) (app_t app, const char *certid,
                      unsigned char **cert, size_t *certlen);
+    int (*readkey) (app_t app, const char *certid,
+                    unsigned char **pk, size_t *pklen);
     int (*getattr) (app_t app, ctrl_t ctrl, const char *name);
     int (*setattr) (app_t app, const char *name,
                     int (*pincb)(void*, const char *, char **),
@@ -109,6 +111,8 @@
 int app_write_learn_status (app_t app, ctrl_t ctrl);
 int app_readcert (app_t app, const char *certid,
                   unsigned char **cert, size_t *certlen);
+int app_readkey (app_t app, const char *keyid,
+                 unsigned char **pk, size_t *pklen);
 int app_getattr (app_t app, ctrl_t ctrl, const char *name);
 int app_setattr (app_t app, const char *name,
                  int (*pincb)(void*, const char *, char **),
Index: gnupg/scd/app-openpgp.c
diff -u gnupg/scd/app-openpgp.c:1.9.2.24 gnupg/scd/app-openpgp.c:1.9.2.25
--- gnupg/scd/app-openpgp.c:1.9.2.24	Thu Feb  3 14:20:57 2005
+++ gnupg/scd/app-openpgp.c	Tue Feb 22 18:29:06 2005
@@ -17,7 +17,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
  *
- * $Id: app-openpgp.c,v 1.9.2.24 2005/02/03 13:20:57 wk Exp $
+ * $Id: app-openpgp.c,v 1.9.2.25 2005/02/22 17:29:06 wk Exp $
  */
 
 #include <config.h>
@@ -90,6 +90,7 @@
 };
 
 
+/* One cache item for DOs.  */
 struct cache_s {
   struct cache_s *next;
   int tag;
@@ -97,8 +98,20 @@
   unsigned char data[1];
 };
 
+
+/* Object with application (i.e. OpenPGP card) specific data.  */
 struct app_local_s {
+  /* A linked list with cached DOs.  */
   struct cache_s *cache;
+  
+  /* Keep track of the public keys.  */
+  struct
+  {
+    int read_done;   /* True if we have at least tried to read them.  */
+    gcry_sexp_t key; /* Might be NULL if key is not available.  */
+  } pk[3];
+
+  /* Keep track of card capabilities.  */
   struct 
   {
     unsigned int get_challenge:1;
@@ -106,6 +119,8 @@
     unsigned int change_force_chv:1;
     unsigned int private_dos:1;
   } extcap;
+
+  /* Flags used to control the application.  */
   struct
   {
     unsigned int no_sync:1;   /* Do not sync CHV1 and CHV2 */
@@ -114,10 +129,16 @@
 };
 
 
+
+/***** Local prototypes  *****/
 static unsigned long convert_sig_counter_value (const unsigned char *value,
                                                 size_t valuelen);
-static unsigned long get_sig_counter (APP app);
+static unsigned long get_sig_counter (app_t app);
 
+
+
+
+
 /* Deconstructor. */
 static void
 do_deinit (app_t app)
@@ -125,12 +146,19 @@
   if (app && app->app_local)
     {
       struct cache_s *c, *c2;
+      int i;
 
       for (c = app->app_local->cache; c; c = c2)
         {
           c2 = c->next;
           xfree (c);
         }
+
+      for (i=0; i < DIM (app->app_local->pk); i++)
+        {
+          gcry_sexp_release (app->app_local->pk[i].key);
+          app->app_local->pk[i].read_done = 0;
+        }
       xfree (app->app_local);
       app->app_local = NULL;
     }
@@ -736,6 +764,156 @@
 }
 
 
+/* Get the public key for KEYNO and store it as an S-expresion with
+   the APP handle.  On error that field gets cleared.  If we already
+   know about the public key we will just return.  Note that this does
+   not mean a key is available; this is soley indicated by the
+   presence of the app->app_local->pk[KEYNO-1].key field.
+
+   Note that GnuPG 1.x does not need this and it would be too time
+   consuming to send it just for the fun of it.  */
+#if GNUPG_MAJOR_VERSION > 1
+static gpg_error_t
+get_public_key (app_t app, int keyno)
+{
+  gpg_error_t err = 0;
+  unsigned char *buffer;
+  const unsigned char *keydata, *m, *e;
+  size_t buflen, keydatalen, mlen, elen;
+  gcry_sexp_t sexp;
+
+  if (keyno < 1 || keyno > 3)
+    return gpg_error (GPG_ERR_INV_ID);
+  keyno--;
+
+  /* Already cached? */
+  if (app->app_local->pk[keyno].read_done)
+    return 0;
+
+  gcry_sexp_release (app->app_local->pk[keyno].key);
+  app->app_local->pk[keyno].key = NULL;
+
+  if (app->card_version > 0x0100)
+    {
+      /* We may simply read the public key out of these cards.  */
+      err = iso7816_read_public_key (app->slot, 
+                                    keyno == 0? "\xB6" :
+                                    keyno == 1? "\xB8" : "\xA4",
+                                    2,  
+                                    &buffer, &buflen);
+      if (err)
+        {
+          log_error (_("reading public key failed: %s\n"), gpg_strerror (err));
+          goto leave;
+        }
+
+      keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
+      if (!keydata)
+        {
+          err = gpg_error (GPG_ERR_CARD);
+          log_error (_("response does not contain the public key data\n"));
+          goto leave;
+        }
+ 
+      m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
+      if (!m)
+        {
+          err = gpg_error (GPG_ERR_CARD);
+          log_error (_("response does not contain the RSA modulus\n"));
+          goto leave;
+        }
+
+      e = find_tlv (keydata, keydatalen, 0x0082, &elen);
+      if (!e)
+        {
+          err = gpg_error (GPG_ERR_CARD);
+          log_error (_("response does not contain the RSA public exponent\n"));
+          goto leave;
+        }
+
+      err = gcry_sexp_build (&sexp, NULL,
+                             "(public-key (rsa (n %b) (e %b)))",
+                             (int)mlen, m,(int)elen, e);
+
+      if (err)
+        {
+          log_error ("error formatting the key into an S-expression: %s\n",
+                     gpg_strerror (err));
+          goto leave;
+        }
+      app->app_local->pk[keyno].key = sexp;
+
+    }
+  else
+    {
+      /* Due to a design problem in v1.0 cards we can't get the public
+         key out of these cards without doing a verify on CHV3.
+         Clearly that is not an option and thus we try to locate the
+         key using an external helper.  */
+
+      buffer = NULL;
+      /* FIXME */
+
+    }
+
+ leave:
+  /* Set a flag to indicate that we tried to read the key.  */
+  app->app_local->pk[keyno].read_done = 1;
+
+  xfree (buffer);
+  return 0;
+}
+#endif /* GNUPG_MAJOR_VERSION > 1 */
+
+
+
+/* Send the KEYPAIRINFO back. KEYNO needs to be in the range [1,3].
+   This is used by the LEARN command. */
+static gpg_error_t
+send_keypair_info (app_t app, ctrl_t ctrl, int keyno)
+{
+  gpg_error_t err = 0;
+  /* Note that GnuPG 1.x does not need this and it would be too time
+     consuming to send it just for the fun of it. */
+#if GNUPG_MAJOR_VERSION > 1
+  gcry_sexp_t sexp;
+  unsigned char grip[20];
+  char gripstr[41];
+  char idbuf[50];
+  int i;
+
+  err = get_public_key (app, keyno);
+  if (err)
+    goto leave;
+  
+  assert (keyno >= 1 && keyno <= 3);
+  sexp = app->app_local->pk[keyno-1].key;
+  if (!sexp)
+    goto leave; /* No such key.  */
+
+  if (!gcry_pk_get_keygrip (sexp, grip))
+    {
+      err = gpg_error (GPG_ERR_INTERNAL); 
+      goto leave;  
+    }
+  
+  for (i=0; i < 20; i++)
+    sprintf (gripstr+i*2, "%02X", grip[i]);
+
+  sprintf (idbuf, "OPENPGP.%d", keyno);
+  send_status_info (ctrl, "KEYPAIRINFO", 
+                    gripstr, 40, 
+                    idbuf, strlen (idbuf), 
+                    NULL, (size_t)0);
+
+ leave:
+#endif /* GNUPG_MAJOR_VERSION > 1 */
+
+  return err; 
+}
+
+
+/* Handle the LEARN command for OpenPGP.  */
 static int
 do_learn_status (app_t app, ctrl_t ctrl)
 {
@@ -760,11 +938,63 @@
       if (app->did_chv3)
         do_getattr (app, ctrl, "PRIVATE-DO-4");
     }
+  send_keypair_info (app, ctrl, 1);
+  send_keypair_info (app, ctrl, 2);
+  send_keypair_info (app, ctrl, 3);
+  return 0;
+}
+
+
+/* Handle the READKEY command for OpenPGP.  On success a canonical
+   encoded S-expression with the public key will get stored at PK and
+   its length (for assertions) at PKLEN; the caller must release that
+   buffer. On error PK and PKLEN are not changed and an error code is
+   returned.  */
+static int
+do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
+{
+  gpg_error_t err;
+  int keyno;
+  size_t n;
+  unsigned char *buf;
+  gcry_sexp_t sexp;
+
+  if (!strcmp (keyid, "OPENPGP.1"))
+    keyno = 1;
+  else if (!strcmp (keyid, "OPENPGP.2"))
+    keyno = 2;
+  else if (!strcmp (keyid, "OPENPGP.3"))
+    keyno = 3;
+  else
+    return gpg_error (GPG_ERR_INV_ID);
 
+  err = get_public_key (app, keyno);
+  if (err)
+    return err;
+
+  sexp = app->app_local->pk[keyno-1].key;
+  if (!sexp)
+    return gpg_error (GPG_ERR_NO_PUBKEY);
+
+  n = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, NULL, 0);
+  if (!n)
+    return gpg_error (GPG_ERR_BUG);
+  buf = xtrymalloc (n);
+  if (!buf)
+    return gpg_error_from_errno (errno);
+  n = gcry_sexp_sprint (sexp, GCRYSEXP_FMT_CANON, buf, n);
+  if (!n)
+    {
+      xfree (buf);
+      return gpg_error (GPG_ERR_BUG);
+    }
+  *pk = buf;
+  *pklen = n;
   return 0;
 }
 
 
+
 /* Verify CHV2 if required.  Depending on the configuration of the
    card CHV1 will also be verified. */
 static int
@@ -1082,6 +1312,11 @@
      generation.  This _might_ help a card to gather more entropy. */
   flush_cache (app);
 
+  /* Obviously we need to remove the cached public key.  */
+  gcry_sexp_release (app->app_local->pk[keyno].key);
+  app->app_local->pk[keyno].read_done = 0;
+
+  /* Check whether a key already exists.  */
   rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen);
   if (rc)
     {
@@ -1109,11 +1344,12 @@
   else
     log_info (_("generating new key\n"));
 
-
+  
+  /* Prepare for key generation by verifying the ADmin PIN.  */
   rc = verify_chv3 (app, pincb, pincb_arg);
   if (rc)
     goto leave;
-
+   
   xfree (buffer); buffer = NULL;
 
 #if 1
@@ -1682,7 +1918,7 @@
 
       app->fnc.deinit = do_deinit;
       app->fnc.learn_status = do_learn_status;
-      app->fnc.readcert = NULL;
+      app->fnc.readkey = do_readkey;
       app->fnc.getattr = do_getattr;
       app->fnc.setattr = do_setattr;
       app->fnc.genkey = do_genkey;
@@ -1818,6 +2054,9 @@
 
   flush_cache (app);
 
+  gcry_sexp_release (app->app_local->pk[keyno].key);
+  app->app_local->pk[keyno].read_done = 0;
+
   rc = iso7816_put_data (app->slot,
                          (app->card_version > 0x0007? 0xE0 : 0xE9) + keyno,
                          template, template_len);
Index: gnupg/scd/app.c
diff -u gnupg/scd/app.c:1.3.2.9 gnupg/scd/app.c:1.3.2.10
--- gnupg/scd/app.c:1.3.2.9	Thu Sep  9 09:28:47 2004
+++ gnupg/scd/app.c	Tue Feb 22 18:29:06 2005
@@ -263,6 +263,32 @@
 }
 
 
+/* Read the key with ID KEYID.  On success a canonical encoded
+   S-expression with the public key will get stored at PK and its
+   length (for assertions) at PKLEN; the caller must release that
+   buffer. On error NULL will be stored at PK and PKLEN and an error
+   code returned.
+
+   This function might not be supported by all applications.  */
+int
+app_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
+{
+  if (pk)
+    *pk = NULL;
+  if (pklen)
+    *pklen = 0;
+
+  if (!app || !keyid || !pk || !pklen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  if (!app->initialized)
+    return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+  if (!app->fnc.readkey)
+    return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+  return app->fnc.readkey (app, keyid, pk, pklen);
+}
+
+
 /* Perform a GETATTR operation.  */
 int 
 app_getattr (APP app, CTRL ctrl, const char *name)
Index: gnupg/scd/command.c
diff -u gnupg/scd/command.c:1.19.2.15 gnupg/scd/command.c:1.19.2.16
--- gnupg/scd/command.c:1.19.2.15	Tue Dec 21 11:02:59 2004
+++ gnupg/scd/command.c	Tue Feb 22 18:29:06 2005
@@ -1,5 +1,5 @@
 /* command.c - SCdaemon command handler
- *	Copyright (C) 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -494,9 +494,9 @@
    Return the public key for the given cert or key ID as an standard
    S-Expression.  */
 static int
-cmd_readkey (ASSUAN_CONTEXT ctx, char *line)
+cmd_readkey (assuan_context_t ctx, char *line)
 {
-  CTRL ctrl = assuan_get_pointer (ctx);
+  ctrl_t ctrl = assuan_get_pointer (ctx);
   int rc;
   unsigned char *cert = NULL;
   size_t ncert, n;
@@ -509,9 +509,31 @@
   line = xstrdup (line); /* Need a copy of the line. */
   if (ctrl->app_ctx)
     {
-      rc = app_readcert (ctrl->app_ctx, line, &cert, &ncert);
-      if (rc)
-        log_error ("app_readcert failed: %s\n", gpg_strerror (rc));
+      unsigned char *pk;
+      size_t pklen;
+
+      /* If the application supports the READKEY function we use that.
+         Otherwise we use the old way by extracting it from the
+         certificate.  */
+      rc = app_readkey (ctrl->app_ctx, line, &pk, &pklen);
+      if (!rc)
+        { /* Yeah, got that key - send it back.  */
+          rc = assuan_send_data (ctx, pk, pklen);
+          xfree (pk);
+          rc = map_assuan_err (rc);
+          xfree (line);
+          line = NULL;
+          goto leave;
+        }
+
+      if (gpg_err_code (rc) != GPG_ERR_UNSUPPORTED_OPERATION)
+        log_error ("app_readkey failed: %s\n", gpg_strerror (rc));
+      else  
+        {
+          rc = app_readcert (ctrl->app_ctx, line, &cert, &ncert);
+          if (rc)
+            log_error ("app_readcert failed: %s\n", gpg_strerror (rc));
+        }
     }
   else
     {




More information about the Gnupg-commits mailing list