scd: New options --info and --info-only for READKEY.
authorWerner Koch <wk@gnupg.org>
Wed, 3 Apr 2019 15:31:09 +0000 (17:31 +0200)
committerWerner Koch <wk@gnupg.org>
Wed, 3 Apr 2019 15:31:39 +0000 (17:31 +0200)
* scd/command.c (cmd_readkey): New options --info and --info-only.
* scd/app.c (app_readkey): New arg 'flags'.
* scd/app-common.h (APP_READKEY_FLAG_INFO): New.
(struct app_ctx_s): New args 'ctrl' and 'flags' for member readkey.
Change all implementers.
* scd/app-nks.c (do_readkey): Stub implementation of
APP_READKEY_FLAG_INFO.
* scd/app-openpgp.c (do_readkey): Implement APP_READKEY_FLAG_INFO.
* scd/app-piv.c (do_readkey): Ditto.
--

This feature allows to quickly get the keygrip and in most cases also
the usage flags for one specific keyref.  Example:

 <- readkey --info-only  PIV.9D
 -> S KEYPAIRINFO FC6061FB457224370B85C6F34DD56CD29E669620 PIV.9D e
 -> OK

Signed-off-by: Werner Koch <wk@gnupg.org>
scd/app-common.h
scd/app-help.c
scd/app-nks.c
scd/app-openpgp.c
scd/app-piv.c
scd/app.c
scd/command.c

index c53bf06..6bb8eba 100644 (file)
 /* Flags used with app_writekey.  */
 #define APP_WRITEKEY_FLAG_FORCE  1  /* Force overwriting existing key.  */
 
+/* Flags used with app_readkey.  */
+#define APP_READKEY_FLAG_INFO    1  /* Send also a KEYPAIRINFO line.  */
+
 /* Bit flags set by the decipher function into R_INFO.  */
 #define APP_DECIPHER_INFO_NOPAD  1  /* Padding has been removed.  */
 
 
+
 struct app_local_s;  /* Defined by all app-*.c.  */
 
 struct app_ctx_s {
@@ -75,8 +79,9 @@ struct app_ctx_s {
     gpg_error_t (*learn_status) (app_t app, ctrl_t ctrl, unsigned int flags);
     gpg_error_t (*readcert) (app_t app, const char *certid,
                      unsigned char **cert, size_t *certlen);
-    gpg_error_t (*readkey) (app_t app, const char *certid,
-                    unsigned char **pk, size_t *pklen);
+    gpg_error_t (*readkey) (app_t app, ctrl_t ctrl,
+                            const char *certid, unsigned int flags,
+                            unsigned char **pk, size_t *pklen);
     gpg_error_t (*getattr) (app_t app, ctrl_t ctrl, const char *name);
     gpg_error_t (*setattr) (app_t app, const char *name,
                     gpg_error_t (*pincb)(void*, const char *, char **),
@@ -126,6 +131,8 @@ struct app_ctx_s {
 
 /*-- app-help.c --*/
 unsigned int app_help_count_bits (const unsigned char *a, size_t len);
+gpg_error_t app_help_get_keygrip_string_pk (const void *pk, size_t pklen,
+                                            char *hexkeygrip);
 gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip);
 gpg_error_t app_help_pubkey_from_cert (const void *cert, size_t certlen,
                                        unsigned char **r_pk, size_t *r_pklen);
@@ -152,7 +159,8 @@ gpg_error_t app_write_learn_status (app_t app, ctrl_t ctrl,
 gpg_error_t app_readcert (app_t app, ctrl_t ctrl, const char *certid,
                   unsigned char **cert, size_t *certlen);
 gpg_error_t app_readkey (app_t app, ctrl_t ctrl,
-                 const char *keyid, unsigned char **pk, size_t *pklen);
+                         const char *keyid, unsigned int flags,
+                         unsigned char **pk, size_t *pklen);
 gpg_error_t app_getattr (app_t app, ctrl_t ctrl, const char *name);
 gpg_error_t app_setattr (app_t app, ctrl_t ctrl, const char *name,
                  gpg_error_t (*pincb)(void*, const char *, char **),
index f0f551c..59221ea 100644 (file)
@@ -52,26 +52,17 @@ app_help_count_bits (const unsigned char *a, size_t len)
 }
 
 
-/* Return the KEYGRIP for the certificate CERT as an hex encoded
-   string in the user provided buffer HEXKEYGRIP which must be of at
  least 41 bytes. */
+/* Return the KEYGRIP for the canonical encoded public key (PK,PKLEN)
+ * as an hex encoded string in the user provided buffer HEXKEYGRIP
* which must be of at least 41 bytes. */
 gpg_error_t
-app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip)
+app_help_get_keygrip_string_pk (const void *pk, size_t pklen, char *hexkeygrip)
 {
   gpg_error_t err;
   gcry_sexp_t s_pkey;
-  ksba_sexp_t p;
-  size_t n;
-  unsigned char array[20];
+  unsigned char array[KEYGRIP_LEN];
 
-  p = ksba_cert_get_public_key (cert);
-  if (!p)
-    return gpg_error (GPG_ERR_BUG);
-  n = gcry_sexp_canon_len (p, 0, NULL, NULL);
-  if (!n)
-    return gpg_error (GPG_ERR_INV_SEXP);
-  err = gcry_sexp_sscan (&s_pkey, NULL, (char*)p, n);
-  xfree (p);
+  err = gcry_sexp_sscan (&s_pkey, NULL, pk, pklen);
   if (err)
     return err; /* Can't parse that S-expression. */
   if (!gcry_pk_get_keygrip (s_pkey, array))
@@ -81,12 +72,34 @@ app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip)
     }
   gcry_sexp_release (s_pkey);
 
-  bin2hex (array, 20, hexkeygrip);
+  bin2hex (array, KEYGRIP_LEN, hexkeygrip);
 
   return 0;
 }
 
 
+/* Return the KEYGRIP for the certificate CERT as an hex encoded
+   string in the user provided buffer HEXKEYGRIP which must be of at
+   least 41 bytes. */
+gpg_error_t
+app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip)
+{
+  gpg_error_t err;
+  ksba_sexp_t p;
+  size_t n;
+
+  p = ksba_cert_get_public_key (cert);
+  if (!p)
+    return gpg_error (GPG_ERR_BUG);
+  n = gcry_sexp_canon_len (p, 0, NULL, NULL);
+  if (!n)
+    return gpg_error (GPG_ERR_INV_SEXP);
+  err = app_help_get_keygrip_string_pk ((void*)p, n, hexkeygrip);
+  ksba_free (p);
+  return err;
+}
+
+
 gpg_error_t
 app_help_pubkey_from_cert (const void *cert, size_t certlen,
                            unsigned char **r_pk, size_t *r_pklen)
index 40c9416..2785ad0 100644 (file)
@@ -618,7 +618,8 @@ do_readcert (app_t app, const char *certid,
    certificate parsing code in commands.c:cmd_readkey.  For internal
    use PK and PKLEN may be NULL to just check for an existing key.  */
 static gpg_error_t
-do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
+do_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags,
+            unsigned char **pk, size_t *pklen)
 {
   gpg_error_t err;
   unsigned char *buffer[2];
@@ -653,6 +654,14 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
       return err;
     }
 
+  if ((flags & APP_READKEY_FLAG_INFO))
+    {
+      /* Not yet implemented but we won't get here for any regular
+       * keyrefs anyway, thus the top layer will provide the
+       * keypairinfo from the certificate.  */
+      (void)ctrl;
+    }
+
   if (pk && pklen)
     {
       *pk = make_canon_sexp_from_rsa_pk (buffer[0], buflen[0],
@@ -698,7 +707,7 @@ do_writekey (app_t app, ctrl_t ctrl,
   else
     return gpg_error (GPG_ERR_INV_ID);
 
-  if (!force && !do_readkey (app, keyid, NULL, NULL))
+  if (!force && !do_readkey (app, ctrl, keyid, 0, NULL, NULL))
     return gpg_error (GPG_ERR_EEXIST);
 
   /* Parse the S-expression.  */
index c5ca063..1a53712 100644 (file)
@@ -1889,7 +1889,8 @@ do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
    buffer. On error PK and PKLEN are not changed and an error code is
    returned.  */
 static gpg_error_t
-do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
+do_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags,
+            unsigned char **pk, size_t *pklen)
 {
   gpg_error_t err;
   int keyno;
@@ -1912,15 +1913,25 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
   if (!buf)
     return gpg_error (GPG_ERR_NO_PUBKEY);
 
-  *pklen = app->app_local->pk[keyno].keylen;
-  *pk = xtrymalloc (*pklen);
-  if (!*pk)
+  if ((flags & APP_READKEY_FLAG_INFO))
     {
-      err = gpg_error_from_syserror ();
-      *pklen = 0;
-      return err;
+      err = send_keypair_info (app, ctrl, keyno+1);
+      if (err)
+        return err;
+    }
+
+  if (pk && pklen)
+    {
+      *pklen = app->app_local->pk[keyno].keylen;
+      *pk = xtrymalloc (*pklen);
+      if (!*pk)
+        {
+          err = gpg_error_from_syserror ();
+          *pklen = 0;
+          return err;
+        }
+      memcpy (*pk, buf, *pklen);
     }
-  memcpy (*pk, buf, *pklen);
 
   return 0;
 }
index addc22c..3dd3fb1 100644 (file)
@@ -1465,7 +1465,7 @@ do_readcert (app_t app, const char *certid,
  * returned.
  */
 static gpg_error_t
-do_readkey (app_t app, const char *keyrefstr,
+do_readkey (app_t app, ctrl_t ctrl, const char *keyrefstr, unsigned int flags,
             unsigned char **r_pk, size_t *r_pklen)
 {
   gpg_error_t err;
@@ -1517,9 +1517,35 @@ do_readkey (app_t app, const char *keyrefstr,
         goto leave;
     }
 
-  *r_pk = pk;
-  pk = NULL;
-  *r_pklen = pklen;
+  if ((flags & APP_READKEY_FLAG_INFO))
+    {
+      char keygripstr[KEYGRIP_LEN*2+1];
+      char idbuf[50];
+      const char *usage;
+
+      err = app_help_get_keygrip_string_pk (pk, pklen, keygripstr);
+      if (err)
+        {
+          log_error ("app_help_get_keygrip_string_pk failed: %s\n",
+                     gpg_strerror (err));
+          goto leave;
+        }
+      usage = dobj->usage? dobj->usage : "";
+
+      snprintf (idbuf, sizeof idbuf, "PIV.%s", dobj->keyref);
+      send_status_info (ctrl, "KEYPAIRINFO",
+                        keygripstr, strlen (keygripstr),
+                        idbuf, strlen (idbuf),
+                        usage, strlen (usage),
+                        NULL, (size_t)0);
+    }
+
+  if (r_pk && r_pklen)
+    {
+      *r_pk = pk;
+      pk = NULL;
+      *r_pklen = pklen;
+    }
 
  leave:
   gcry_sexp_release (s_pkey);
@@ -3218,7 +3244,7 @@ do_writecert (app_t app, ctrl_t ctrl,
    * GPG_ERR_NO_PUBKEY).  We enforce this because otherwise the only
    * way to detect whether a key exists is by trying to use that
    * key. */
-  err = do_readkey (app, certrefstr, &orig_pk, &orig_pklen);
+  err = do_readkey (app, ctrl, certrefstr, 0, &orig_pk, &orig_pklen);
   if (err)
     {
       if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
index 59a8880..1f3808f 100644 (file)
--- a/scd/app.c
+++ b/scd/app.c
@@ -772,14 +772,15 @@ app_readcert (app_t app, ctrl_t ctrl, const char *certid,
 
 
 /* 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.  */
+ * 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.  If the key is not required NULL may be passed for
+ * PK; this makse send if the APP_READKEY_FLAG_INFO has also been set.
+ *
+ * This function might not be supported by all applications.  */
 gpg_error_t
-app_readkey (app_t app, ctrl_t ctrl, const char *keyid,
+app_readkey (app_t app, ctrl_t ctrl, const char *keyid, unsigned int flags,
              unsigned char **pk, size_t *pklen)
 {
   gpg_error_t err;
@@ -789,7 +790,7 @@ app_readkey (app_t app, ctrl_t ctrl, const char *keyid,
   if (pklen)
     *pklen = 0;
 
-  if (!app || !keyid || !pk || !pklen)
+  if (!app || !keyid)
     return gpg_error (GPG_ERR_INV_VALUE);
   if (!app->ref_count)
     return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
@@ -798,7 +799,7 @@ app_readkey (app_t app, ctrl_t ctrl, const char *keyid,
   err = lock_app (app, ctrl);
   if (err)
     return err;
-  err= app->fnc.readkey (app, keyid, pk, pklen);
+  err= app->fnc.readkey (app, ctrl, keyid, flags, pk, pklen);
   unlock_app (app);
   return err;
 }
index 0d1a5cd..28c739d 100644 (file)
@@ -502,19 +502,20 @@ cmd_readcert (assuan_context_t ctx, char *line)
 
 
 static const char hlp_readkey[] =
-  "READKEY [--advanced] <keyid>|<oid>\n"
+  "READKEY [--advanced] [--info[-only]] <keyid>|<oid>\n"
   "\n"
   "Return the public key for the given cert or key ID as a standard\n"
-  "S-expression.\n"
-  "In --advanced mode it returns the S-expression in advanced format.\n"
-  "\n"
-  "Note that this function may even be used on a locked card.";
+  "S-expression.  With --advanced  the S-expression is returned in\n"
+  "advanced format.  With --info a KEYPAIRINFO status line is also\n"
+  "emitted; with --info-only the regular output is suppressed.";
 static gpg_error_t
 cmd_readkey (assuan_context_t ctx, char *line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
   int rc;
   int advanced = 0;
+  int opt_info = 0;
+  int opt_nokey = 0;
   unsigned char *cert = NULL;
   unsigned char *pk = NULL;
   size_t ncert, pklen;
@@ -524,6 +525,10 @@ cmd_readkey (assuan_context_t ctx, char *line)
 
   if (has_option (line, "--advanced"))
     advanced = 1;
+  if (has_option (line, "--info"))
+    opt_info = 1;
+  if (has_option (line, "--info-only"))
+    opt_info = opt_nokey = 1;
 
   line = skip_options (line);
   line = xstrdup (line); /* Need a copy of the line. */
@@ -531,7 +536,9 @@ cmd_readkey (assuan_context_t ctx, char *line)
   /* 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, ctrl, line, &pk, &pklen);
+  rc = app_readkey (ctrl->app_ctx, ctrl, line,
+                    opt_info? APP_READKEY_FLAG_INFO : 0,
+                    opt_nokey? NULL : &pk, &pklen);
   if (!rc)
     ; /* Okay, got that key.  */
   else if (gpg_err_code (rc) == GPG_ERR_UNSUPPORTED_OPERATION
@@ -551,6 +558,26 @@ cmd_readkey (assuan_context_t ctx, char *line)
                      gpg_strerror (rc));
           goto leave;
         }
+
+      if (opt_info)
+        {
+          char keygripstr[KEYGRIP_LEN*2+1];
+
+          rc = app_help_get_keygrip_string_pk (pk, pklen, keygripstr);
+          if (rc)
+            {
+              log_error ("app_help_get_keygrip_string failed: %s\n",
+                         gpg_strerror (rc));
+              goto leave;
+            }
+
+          /* FIXME: Using LINE is not correct because it might be an
+           * OID and has not been canonicalized (i.e. uppercased).  */
+          send_status_info (ctrl, "KEYPAIRINFO",
+                            keygripstr, strlen (keygripstr),
+                            line, strlen (line),
+                            NULL, (size_t)0);
+        }
     }
   else
     {
@@ -558,7 +585,9 @@ cmd_readkey (assuan_context_t ctx, char *line)
       goto leave;
     }
 
-  if (advanced)
+  if (opt_nokey)
+    ;
+  else if (advanced)
     {
       gcry_sexp_t s_key;
       unsigned char *pkadv;