card: Make printing of key information more flexible.
authorWerner Koch <wk@gnupg.org>
Tue, 29 Jan 2019 07:48:53 +0000 (08:48 +0100)
committerWerner Koch <wk@gnupg.org>
Tue, 29 Jan 2019 08:32:20 +0000 (09:32 +0100)
* tools/card-tool-misc.c: New.
* tools/card-tool.h: Rewored data structures for key infos.
* tools/gpg-card-tool.c: Ditto.
* tools/card-call-scd.c: Ditto.
--

Note that this also changes the way the key information is printed.
Formerly we printed it like:

  Signature key ....: <openpgp-fingerprint>
        created ....: <timestamp>
        keygrip ... : <keygrip>

now we do:

  Signature key ....: <keygrip>
        fingerprint : <openpgp-fingerprint>
        created ....: <timestamp>

This is because a keygrip is always available but a fingerprint and
the creation date are properties of an OpenPGP card.  A standard way
of listing keys is better than one depending on the type of card.

Signed-off-by: Werner Koch <wk@gnupg.org>
tools/Makefile.am
tools/card-call-scd.c
tools/card-tool-misc.c [new file with mode: 0644]
tools/card-tool.h
tools/gpg-card-tool.c

index e29e6a2..f74221b 100644 (file)
@@ -123,7 +123,13 @@ gpg_connect_agent_LDADD = ../common/libgpgrl.a $(common_libs) \
                           $(LIBREADLINE) $(LIBINTL) $(NETLIBS) $(LIBICONV) \
                           $(gpg_connect_agent_rc_objs)
 
-gpg_card_tool_SOURCES   = gpg-card-tool.c card-tool.h card-call-scd.c
+
+gpg_card_tool_SOURCES   = \
+       gpg-card-tool.c \
+       card-tool.h     \
+       card-call-scd.c \
+       card-tool-misc.c
+
 gpg_card_tool_LDADD     = ../common/libgpgrl.a $(common_libs) \
                          $(LIBASSUAN_LIBS) $(LIBGCRYPT_LIBS) \
                          $(GPG_ERROR_LIBS) \
index 7df7861..abf35ed 100644 (file)
@@ -132,6 +132,7 @@ release_card_info (card_info_t info)
 {
   int i;
 
+
   if (!info)
     return;
 
@@ -145,12 +146,18 @@ release_card_info (card_info_t info)
   xfree (info->pubkey_url); info->pubkey_url = NULL;
   xfree (info->login_data); info->login_data = NULL;
   info->cafpr1len = info->cafpr2len = info->cafpr3len = 0;
-  info->fpr1len = info->fpr2len = info->fpr3len = 0;
   for (i=0; i < DIM(info->private_do); i++)
     {
       xfree (info->private_do[i]);
       info->private_do[i] = NULL;
     }
+  while (info->kinfo)
+    {
+      key_info_t kinfo = info->kinfo->next;
+      xfree (info->kinfo);
+      info->kinfo = kinfo;
+    }
+
 }
 
 
@@ -534,6 +541,48 @@ get_serialno_cb (void *opaque, const char *line)
 }
 
 
+
+/* For historical reasons OpenPGP cards simply use the numbers 1 to 3
+ * for the <keyref>.  Other cards and future versions of
+ * scd/app-openpgp.c may print the full keyref; i.e. "OpenPGP.1"
+ * instead of "1".  This is a helper to cope with that. */
+static const char *
+parse_keyref_helper (const char *string)
+{
+  if (*string == '1' && spacep (string+1))
+    return "OPENPGP.1";
+  else if (*string == '2' && spacep (string+1))
+    return "OPENPGP.2";
+  else if (*string == '3' && spacep (string+1))
+    return "OPENPGP.3";
+  else
+    return string;
+}
+
+
+/* Create a new key info object with KEYREF.  All fields but the
+ * keyref are zeroed out.  Never returns NULL.  The created object is
+ * appended to the list at INFO. */
+static key_info_t
+create_kinfo (card_info_t info, const char *keyref)
+{
+  key_info_t kinfo, ki;
+
+  kinfo = xcalloc (1, sizeof *kinfo + strlen (keyref));
+  strcpy (kinfo->keyref, keyref);
+
+  if (!info->kinfo)
+    info->kinfo = kinfo;
+  else
+    {
+      for (ki=info->kinfo; ki->next; ki = ki->next)
+        ;
+      ki->next = kinfo;
+    }
+  return kinfo;
+}
+
+
 /* The status callback to handle the LEARN and GETATTR commands.  */
 static gpg_error_t
 learn_status_cb (void *opaque, const char *line)
@@ -541,6 +590,10 @@ learn_status_cb (void *opaque, const char *line)
   struct card_info_s *parm = opaque;
   const char *keyword = line;
   int keywordlen;
+  char *line_buffer = NULL; /* In case we need a copy.  */
+  char *pline;
+  key_info_t kinfo;
+  const char *keyref;
   int i;
 
   for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
@@ -635,18 +688,31 @@ learn_status_cb (void *opaque, const char *line)
         }
       else if (!memcmp (keyword, "KEY-FPR", keywordlen))
         {
-          int no = atoi (line);
-
-          while (*line && !spacep (line))
-            line++;
-          while (spacep (line))
-            line++;
-          if (no == 1)
-            parm->fpr1len = unhexify_fpr (line, parm->fpr1, sizeof parm->fpr1);
-          else if (no == 2)
-            parm->fpr2len = unhexify_fpr (line, parm->fpr2, sizeof parm->fpr2);
-          else if (no == 3)
-            parm->fpr3len = unhexify_fpr (line, parm->fpr3, sizeof parm->fpr3);
+          /* The format of such a line is:
+           *   KEY-FPR <keyref> <fingerprintinhex>
+           */
+          const char *fpr;
+
+          line_buffer = pline = xstrdup (line);
+
+          keyref = parse_keyref_helper (pline);
+          while (*pline && !spacep (pline))
+            pline++;
+          if (*pline)
+            *pline++ = 0; /* Terminate keyref.  */
+          while (spacep (pline)) /* Skip to the fingerprint.  */
+            pline++;
+          fpr = pline;
+
+          /* Check whether we already have an item for the keyref.  */
+          kinfo = find_kinfo (parm, keyref);
+          if (!kinfo)  /* No: new entry.  */
+            kinfo = create_kinfo (parm, keyref);
+          else /* Existing entry - clear the fpr.  */
+            memset (kinfo->fpr, 0, sizeof kinfo->fpr);
+
+          /* Set or update or the fingerprint.  */
+          kinfo->fprlen = unhexify_fpr (fpr, kinfo->fpr, sizeof kinfo->fpr);
         }
       break;
 
@@ -664,17 +730,28 @@ learn_status_cb (void *opaque, const char *line)
         }
       else if (!memcmp (keyword, "KEY-TIME", keywordlen))
         {
-          int no = atoi (line);
-          while (* line && !spacep (line))
-            line++;
-          while (spacep (line))
-            line++;
-          if (no == 1)
-            parm->fpr1time = strtoul (line, NULL, 10);
-          else if (no == 2)
-            parm->fpr2time = strtoul (line, NULL, 10);
-          else if (no == 3)
-            parm->fpr3time = strtoul (line, NULL, 10);
+          /* The format of such a line is:
+           *   KEY-TIME <keyref> <timestamp>
+           */
+          const char *timestamp;
+
+          line_buffer = pline = xstrdup (line);
+
+          keyref = parse_keyref_helper (pline);
+          while (*pline && !spacep (pline))
+            pline++;
+          if (*pline)
+            *pline++ = 0; /* Terminate keyref.  */
+          while (spacep (pline)) /* Skip to the timestamp.  */
+            pline++;
+          timestamp = pline;
+
+          /* Check whether we already have an item for the keyref.  */
+          kinfo = find_kinfo (parm, keyref);
+          if (!kinfo)  /* No: new entry.  */
+            kinfo = create_kinfo (parm, keyref);
+
+          kinfo->created = strtoul (timestamp, NULL, 10);
         }
       else if (!memcmp (keyword, "KEY-ATTR", keywordlen))
         {
@@ -767,21 +844,29 @@ learn_status_cb (void *opaque, const char *line)
         }
       else if (!memcmp (keyword, "KEYPAIRINFO", keywordlen))
         {
+          /* The format of such a line is:
+           *   KEYPARINFO <hexgrip> <keyref>
+           */
           const char *hexgrp = line;
-          int no;
 
           while (*line && !spacep (line))
             line++;
           while (spacep (line))
             line++;
-          if (strncmp (line, "OPENPGP.", 8))
-            ;
-          else if ((no = atoi (line+8)) == 1)
-            unhexify_fpr (hexgrp, parm->grp1, sizeof parm->grp1);
-          else if (no == 2)
-            unhexify_fpr (hexgrp, parm->grp2, sizeof parm->grp2);
-          else if (no == 3)
-            unhexify_fpr (hexgrp, parm->grp3, sizeof parm->grp3);
+
+          keyref = line;
+
+          /* Check whether we already have an item for the keyref.  */
+          kinfo = find_kinfo (parm, keyref);
+          if (!kinfo)  /* New entry.  */
+            kinfo = create_kinfo (parm, keyref);
+          else /* Existing entry - clear the grip.  */
+            memset (kinfo->grip, 0, sizeof kinfo->grip);
+
+          /* Set or update the grip.  Note that due to the
+           * calloc/memset an erroneous too short grip will be nul
+           * padded on the right. */
+          unhexify_fpr (hexgrp, kinfo->grip, sizeof kinfo->grip);
         }
       break;
 
@@ -809,6 +894,7 @@ learn_status_cb (void *opaque, const char *line)
       break;
     }
 
+  xfree (line_buffer);
   return 0;
 }
 
diff --git a/tools/card-tool-misc.c b/tools/card-tool-misc.c
new file mode 100644 (file)
index 0000000..0f5fcc0
--- /dev/null
@@ -0,0 +1,44 @@
+/* card-tool-misc.c - Helper functions for gpg-card-tool
+ * Copyright (C) 2019 g10 Code GmbH
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "../common/util.h"
+#include "../common/i18n.h"
+#include "../common/openpgpdefs.h"
+#include "card-tool.h"
+
+/* Return the key info object for the key KEYREF.  If it is not found
+ * NULL is returned.  */
+key_info_t
+find_kinfo (card_info_t info, const char *keyref)
+{
+  key_info_t kinfo;
+
+  for (kinfo = info->kinfo; kinfo; kinfo = kinfo->next)
+    if (!strcmp (kinfo->keyref, keyref))
+      return kinfo;
+  return NULL;
+}
index d51698c..0af6186 100644 (file)
@@ -76,6 +76,26 @@ struct key_attr
   };
 };
 
+/* An object to store information pertaining to a key pair.  This is
+ * commonly used as a linked list with all keys known for the current
+ * card.  */
+struct key_info_s
+{
+  struct key_info_s *next;
+
+  unsigned char grip[20];/* The keygrip.  */
+
+  unsigned char xflag;   /* Temporary flag to help processing a list. */
+
+  /* The three next items are mostly useful for OpenPGP cards.  */
+  unsigned char fprlen;  /* Use length of the next item.  */
+  unsigned char fpr[32]; /* The binary fingerprint of length FPRLEN.  */
+  u32 created;           /* The time the key was created.  */
+
+  char keyref[1];        /* String with the keyref (e.g. OPENPGP.1).  */
+};
+typedef struct key_info_s *key_info_t;
+
 
 /*
  * The object used to store information about a card.
@@ -100,18 +120,7 @@ struct card_info_s
   char cafpr1[20];
   char cafpr2[20];
   char cafpr3[20];
-  unsigned char fpr1len; /* Length of the fingerprint or 0 if invalid.  */
-  unsigned char fpr2len;
-  unsigned char fpr3len;
-  char fpr1[20];
-  char fpr2[20];
-  char fpr3[20];
-  u32  fpr1time;
-  u32  fpr2time;
-  u32  fpr3time;
-  char grp1[20];     /* The keygrip for OPENPGP.1 */
-  char grp2[20];     /* The keygrip for OPENPGP.2 */
-  char grp3[20];     /* The keygrip for OPENPGP.3 */
+  key_info_t kinfo;  /* Linked list with all keypair related data.  */
   unsigned long sig_counter;
   int chv1_cached;   /* True if a PIN is not required for each
                         signing.  Note that the gpg-agent might cache
@@ -133,6 +142,10 @@ struct card_info_s
 typedef struct card_info_s *card_info_t;
 
 
+/*-- card-tool-misc.c --*/
+key_info_t find_kinfo (card_info_t info, const char *keyref);
+
+
 /*-- card-call-scd.c --*/
 void release_card_info (card_info_t info);
 const char *app_type_string (app_type_t app_type);
index b40914a..31d9c22 100644 (file)
@@ -107,10 +107,19 @@ static struct debug_flags_s debug_flags [] =
   };
 
 
+/* An object to create lists of labels and keyrefs.  */
+struct keyinfolabel_s
+{
+  const char *label;
+  const char *keyref;
+};
+typedef struct keyinfolabel_s *keyinfolabel_t;
+
+
 /* Limit of size of data we read from a file for certain commands.  */
 #define MAX_GET_DATA_FROM_FILE 16384
 
-/* Constats for OpenPGP cards.  */
+/* Constants for OpenPGP cards.  */
 #define OPENPGP_USER_PIN_DEFAULT  "123456"
 #define OPENPGP_ADMIN_PIN_DEFAULT "12345678"
 #define OPENPGP_KDF_DATA_LENGTH_MIN  90
@@ -544,35 +553,101 @@ print_isoname (estream_t fp, const char *name)
 }
 
 
-/* Return true if the SHA1 fingerprint FPR consists only of zeroes. */
+/* Return true if the buffer MEM of length memlen consists only of zeroes. */
 static int
-fpr_is_zero (const char *fpr, unsigned int fprlen)
+mem_is_zero (const char *mem, unsigned int memlen)
 {
   int i;
 
-  for (i=0; i < fprlen && !fpr[i]; i++)
+  for (i=0; i < memlen && !mem[i]; i++)
     ;
-  return (i == fprlen);
+  return (i == memlen);
 }
 
 
-/* Return true if the fingerprint FPR consists only of 0xFF. */
+/* Return true if the buffer MEM or length MEMLEN consists only of 0xFF. */
 static int
-fpr_is_ff (const char *fpr, unsigned int fprlen)
+mem_is_ff (const char *mem, unsigned int memlen)
 {
   int i;
 
-  for (i=0; i < fprlen && fpr[i] == '\xff'; i++)
+  for (i=0; i < memlen && mem[i] == '\xff'; i++)
     ;
-  return (i == fprlen);
+  return (i == memlen);
 }
 
 
 \f
+/* Helper to list a single keyref.  */
+static void
+list_one_kinfo (key_info_t kinfo, estream_t fp)
+{
+  if (kinfo)
+    {
+      tty_fprintf (fp, " ");
+      if (mem_is_zero (kinfo->grip, sizeof kinfo->grip))
+        tty_fprintf (fp, "[none]\n");
+      else
+        print_keygrip (fp, kinfo->grip);
+
+      if (kinfo->fprlen && kinfo->created)
+        {
+          tty_fprintf (fp, "      fingerprint :");
+          print_shax_fpr (fp, kinfo->fpr, kinfo->fprlen);
+          tty_fprintf (fp, "      created ....: %s\n",
+                       isotimestamp (kinfo->created));
+        }
+    }
+  else
+    tty_fprintf (fp, " [none]\n");
+}
+
+
+/* List all keyinfo in INFO using the list of LABELS.  */
+static void
+list_all_kinfo (card_info_t info, keyinfolabel_t labels, estream_t fp)
+{
+  key_info_t kinfo;
+  int idx, i;
+
+  /* Print the keyinfo.  We first print those we known and then all
+   * remaining item.  */
+  for (kinfo = info->kinfo; kinfo; kinfo = kinfo->next)
+    kinfo->xflag = 0;
+  if (labels)
+    {
+      for (idx=0; labels[idx].label; idx++)
+        {
+          tty_fprintf (fp, "%s", labels[idx].label);
+          kinfo = find_kinfo (info, labels[idx].keyref);
+          list_one_kinfo (kinfo, fp);
+          if (kinfo)
+            kinfo->xflag = 1;
+        }
+    }
+  for (kinfo = info->kinfo; kinfo; kinfo = kinfo->next)
+    {
+      if (kinfo->xflag)
+        continue;
+      tty_fprintf (fp, "Key %s ", kinfo->keyref);
+      for (i=5+strlen (kinfo->keyref); i < 18; i++)
+        tty_fprintf (fp, ".");
+      tty_fprintf (fp, ":");
+      list_one_kinfo (kinfo, fp);
+    }
+}
+
+
 /* List OpenPGP card specific data.  */
 static void
 list_openpgp (card_info_t info, estream_t fp)
 {
+  static struct keyinfolabel_s keyinfolabels[] = {
+    { "Signature key ....:", "OPENPGP.1" },
+    { "Encryption key....:", "OPENPGP.2" },
+    { "Authentication key:", "OPENPGP.3" },
+    { NULL, NULL }
+  };
   int i;
 
   if (!info->serialno
@@ -661,33 +736,8 @@ list_openpgp (card_info_t info, estream_t fp)
                    info->uif[0] ? "on" : "off", info->uif[1] ? "on" : "off",
                    info->uif[2] ? "on" : "off");
     }
-  tty_fprintf (fp, "Signature key ....:");
-  print_shax_fpr (fp, info->fpr1len? info->fpr1:NULL, info->fpr1len);
-  if (info->fpr1len && info->fpr1time)
-    {
-      tty_fprintf (fp, "      created ....: %s\n",
-                   isotimestamp (info->fpr1time));
-      tty_fprintf (fp, "      keygrip ....: ");
-      print_keygrip (fp, info->grp1);
-    }
-  tty_fprintf (fp, "Encryption key....:");
-  print_shax_fpr (fp, info->fpr2len? info->fpr2:NULL, info->fpr2len);
-  if (info->fpr2len && info->fpr2time)
-    {
-      tty_fprintf (fp, "      created ....: %s\n",
-                   isotimestamp (info->fpr2time));
-      tty_fprintf (fp, "      keygrip ....: ");
-      print_keygrip (fp, info->grp2);
-    }
-  tty_fprintf (fp, "Authentication key:");
-  print_shax_fpr (fp, info->fpr3len? info->fpr3:NULL, info->fpr3len);
-  if (info->fpr3len && info->fpr3time)
-    {
-      tty_fprintf (fp, "      created ....: %s\n",
-                   isotimestamp (info->fpr3time));
-      tty_fprintf (fp, "      keygrip ....: ");
-      print_keygrip (fp, info->grp3);
-    }
+
+  list_all_kinfo (info, keyinfolabels, fp);
 
   /* tty_fprintf (fp, "General key info->.: "); */
   /* thefpr = (info->fpr1len? info->fpr1 : info->fpr2len? info->fpr2 : */
@@ -696,7 +746,7 @@ list_openpgp (card_info_t info, estream_t fp)
   /*              info->fpr3len? info->fpr3len : 0); */
   /* If the fingerprint is all 0xff, the key has no associated
      OpenPGP certificate.  */
-  /* if ( thefpr && !fpr_is_ff (thefpr, thefprlen) */
+  /* if ( thefpr && !mem_is_ff (thefpr, thefprlen) */
   /*      && !get_pubkey_byfprint (ctrl, pk, &keyblock, thefpr, thefprlen)) */
   /*   { */
       /* print_pubkey_info (ctrl, fp, pk); */
@@ -900,6 +950,7 @@ static gpg_error_t
 cmd_fetch (card_info_t info)
 {
   gpg_error_t err;
+  key_info_t kinfo;
 
   if (!info)
     return print_help
@@ -916,7 +967,7 @@ cmd_fetch (card_info_t info)
       /* free_strlist (sl); */
       err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);  /* FIXME */
     }
-  else if (info->fpr1len)
+  else if ((kinfo = find_kinfo (info, "OPENPGP.1")) && kinfo->fprlen)
     {
       /* rc = keyserver_import_fprint (ctrl, info.fpr1, info.fpr1len, */
       /*                               opt.keyserver, 0); */
@@ -1479,6 +1530,7 @@ cmd_generate (card_info_t info)
   int forced_chv1 = -1;
   int want_backup;
   char *answer = NULL;
+  key_info_t kinfo1, kinfo2, kinfo3;
 
   if (!info)
     return print_help
@@ -1507,9 +1559,15 @@ cmd_generate (card_info_t info)
   else
     want_backup = 0;
 
-  if ( (info->fpr1len && !fpr_is_zero (info->fpr1, info->fpr1len))
-       || (info->fpr2len && !fpr_is_zero (info->fpr2, info->fpr2len))
-       || (info->fpr3len && !fpr_is_zero (info->fpr3, info->fpr3len)))
+
+  kinfo1 = find_kinfo (info, "OPENPGP.1");
+  kinfo2 = find_kinfo (info, "OPENPGP.2");
+  kinfo3 = find_kinfo (info, "OPENPGP.3");
+
+  if ((kinfo1 && kinfo1->fprlen && !mem_is_zero (kinfo1->fpr,kinfo1->fprlen))
+      || (kinfo2 && kinfo2->fprlen && !mem_is_zero (kinfo2->fpr,kinfo2->fprlen))
+      || (kinfo3 && kinfo3->fprlen && !mem_is_zero (kinfo3->fpr,kinfo3->fprlen))
+      )
     {
       tty_printf ("\n");
       log_info (_("Note: keys are already stored on the card!\n"));